diff options
| author | kini <none@none> | 2006-03-02 17:22:20 -0800 |
|---|---|---|
| committer | kini <none@none> | 2006-03-02 17:22:20 -0800 |
| commit | b65731f1f612238279eb4d997f43589b535c5646 (patch) | |
| tree | 2e13f2163e4c89fcce481bb6f1d09eb2ec55fc5e /usr/src | |
| parent | f18fc2780f13712904943c9253c24cab2e41e92b (diff) | |
| download | illumos-joyent-b65731f1f612238279eb4d997f43589b535c5646.tar.gz | |
PSARC/2005/375 PCI Hotplug Extensions for PCIe
PSARC/2006/037 PCI Express Hotplug Framework Interrupt Interfaces
6379464 Integrate PSARC/2005/375 for SPARC PCI Express Hotplug Support
5049969 Make efcode' PCI configurator as the default configurator for SPARC platforms
--HG--
rename : usr/src/uts/sun4u/fcode/Makefile => usr/src/uts/sparc/fcode/Makefile
rename : usr/src/uts/sun4u/fcodem/Makefile => usr/src/uts/sparc/fcodem/Makefile
rename : usr/src/uts/sun4u/fcpci/Makefile => usr/src/uts/sparc/fcpci/Makefile
rename : usr/src/uts/sun4u/pcicfg.e/Makefile => usr/src/uts/sparc/pcicfg.e/Makefile
rename : usr/src/uts/sun4u/io/pcicfg.e.c => usr/src/uts/sun4/io/pcicfg.e.c
rename : usr/src/uts/sun4u/sys/fc_plat.h => usr/src/uts/sun4/sys/fc_plat.h
Diffstat (limited to 'usr/src')
44 files changed, 4133 insertions, 500 deletions
diff --git a/usr/src/cmd/pcidr/etc/SUNW,EC_dr,ESC_dr_req,sysevent.conf b/usr/src/cmd/pcidr/etc/SUNW,EC_dr,ESC_dr_req,sysevent.conf index cb151ebf2a..15fb798ce5 100644 --- a/usr/src/cmd/pcidr/etc/SUNW,EC_dr,ESC_dr_req,sysevent.conf +++ b/usr/src/cmd/pcidr/etc/SUNW,EC_dr,ESC_dr_req,sysevent.conf @@ -22,8 +22,9 @@ # # ident "%Z%%M% %I% %E% SMI" # -# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # EC_dr ESC_dr_req SUNW pcie_pci - - - /usr/lib/pci/pcidr class=$class subclass=$subclass publisher=$publisher dr_request_type=$dr_request_type dr_ap_id=$dr_ap_id +EC_dr ESC_dr_req SUNW px_pci - - - /usr/lib/pci/pcidr class=$class subclass=$subclass publisher=$publisher dr_request_type=$dr_request_type dr_ap_id=$dr_ap_id diff --git a/usr/src/lib/efcode/pci/Makefile.com b/usr/src/lib/efcode/pci/Makefile.com index 759b03e7e9..9d9db0a6c0 100644 --- a/usr/src/lib/efcode/pci/Makefile.com +++ b/usr/src/lib/efcode/pci/Makefile.com @@ -20,7 +20,7 @@ # CDDL HEADER END # # -# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "%Z%%M% %I% %E% SMI" @@ -29,6 +29,7 @@ OBJECTS = pci.o LIBRARY = lfc_upa_pci.a -SYMLINKS = lfc_pci_pci.so lfc_gptwo_pci.so +SYMLINKS = lfc_pci_pci.so lfc_gptwo_pci.so lfc_pciex_pciex.so \ + lfc_pciex_pci.so lfc_pci_pciex.so include ../../Makefile.efcode diff --git a/usr/src/pkgdefs/SUNWckr/prototype_sparc b/usr/src/pkgdefs/SUNWckr/prototype_sparc index 352dd57adc..731701cf21 100644 --- a/usr/src/pkgdefs/SUNWckr/prototype_sparc +++ b/usr/src/pkgdefs/SUNWckr/prototype_sparc @@ -167,9 +167,10 @@ f none kernel/misc/sparcv9/kcf 755 root sys f none kernel/misc/sparcv9/krtld 755 root sys f none kernel/misc/sparcv9/mac 755 root sys l none kernel/misc/sparcv9/md5=../../../kernel/crypto/sparcv9/md5 -f none kernel/misc/sparcv9/pcicfg 755 root sys f none kernel/misc/sparcv9/pcie 755 root sys f none kernel/misc/sparcv9/pcihp 755 root sys +f none kernel/misc/sparcv9/pciehpc 755 root sys +f none kernel/misc/sparcv9/pcishpc 755 root sys f none kernel/misc/sparcv9/pcmcia 755 root sys f none kernel/misc/sparcv9/rpcsec 755 root sys f none kernel/misc/sparcv9/scsi 755 root sys diff --git a/usr/src/pkgdefs/SUNWefc.u/prototype_com b/usr/src/pkgdefs/SUNWefc.u/prototype_com index 6a7e03e59c..b7fef18ce7 100644 --- a/usr/src/pkgdefs/SUNWefc.u/prototype_com +++ b/usr/src/pkgdefs/SUNWefc.u/prototype_com @@ -22,7 +22,7 @@ # #pragma ident "%Z%%M% %I% %E% SMI" # -# Copyright 2000-2001,2003 Sun Microsystems, Inc. All rights reserved. +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # This required package information file contains a list of package contents. @@ -46,52 +46,20 @@ i depend # # SUNWefc.u # +d none kernel 755 root sys +d none kernel/drv 755 root sys +f none kernel/drv/fcode.conf 644 root sys +d none kernel/drv/sparcv9 755 root sys +f none kernel/drv/sparcv9/fcode 755 root sys +d none kernel/misc 755 root sys +d none kernel/misc/sparcv9 755 root sys +f none kernel/misc/sparcv9/fcodem 755 root sys +f none kernel/misc/sparcv9/fcpci 755 root sys +f none kernel/misc/sparcv9/pcicfg.e 755 root sys d none platform 755 root sys d none platform/sun4u 755 root sys d none platform/sun4u/kernel 755 root sys -d none platform/sun4u/kernel/drv 755 root sys -f none platform/sun4u/kernel/drv/fcode.conf 644 root sys -d none platform/sun4u/kernel/drv/sparcv9 755 root sys -f none platform/sun4u/kernel/drv/sparcv9/fcode 755 root sys d none platform/sun4u/kernel/misc 755 root sys d none platform/sun4u/kernel/misc/sparcv9 755 root sys -f none platform/sun4u/kernel/misc/sparcv9/fcodem 755 root sys -f none platform/sun4u/kernel/misc/sparcv9/fcpci 755 root sys -f none platform/sun4u/kernel/misc/sparcv9/pcicfg.e 755 root sys f none platform/sun4u/kernel/misc/sparcv9/gptwocfg 755 root sys f none platform/sun4u/kernel/misc/sparcv9/gptwo_cpu 755 root sys -d none platform/SUNW,Sun-Fire 755 root sys -d none platform/SUNW,Sun-Fire/kernel 755 root sys -d none platform/SUNW,Sun-Fire/kernel/misc 755 root sys -d none platform/SUNW,Sun-Fire/kernel/misc/sparcv9 755 root sys -s none platform/SUNW,Sun-Fire/kernel/misc/sparcv9/pcicfg=../../../../sun4u/kernel/misc/sparcv9/pcicfg.e -d none platform/SUNW,Sun-Fire-880 755 root sys -d none platform/SUNW,Sun-Fire-880/kernel 755 root sys -d none platform/SUNW,Sun-Fire-880/kernel/misc 755 root sys -d none platform/SUNW,Sun-Fire-880/kernel/misc/sparcv9 755 root sys -s none platform/SUNW,Sun-Fire-880/kernel/misc/sparcv9/pcicfg=../../../../sun4u/kernel/misc/sparcv9/pcicfg.e -d none platform/SUNW,Sun-Fire-15000 755 root sys -d none platform/SUNW,Sun-Fire-15000/kernel 755 root sys -d none platform/SUNW,Sun-Fire-15000/kernel/misc 755 root sys -d none platform/SUNW,Sun-Fire-15000/kernel/misc/sparcv9 755 root sys -s none platform/SUNW,Sun-Fire-15000/kernel/misc/sparcv9/pcicfg=../../../../sun4u/kernel/misc/sparcv9/pcicfg.e -d none platform/SUNW,UltraSPARC-IIi-Netract 755 root sys -d none platform/SUNW,UltraSPARC-IIi-Netract/kernel 755 root sys -d none platform/SUNW,UltraSPARC-IIi-Netract/kernel/misc 755 root sys -d none platform/SUNW,UltraSPARC-IIi-Netract/kernel/misc/sparcv9 755 root sys -s none platform/SUNW,UltraSPARC-IIi-Netract/kernel/misc/sparcv9/pcicfg=../../../../sun4u/kernel/misc/sparcv9/pcicfg.e -d none platform/SUNW,Netra-T12 755 root sys -d none platform/SUNW,Netra-T12/kernel 755 root sys -d none platform/SUNW,Netra-T12/kernel/misc 755 root sys -d none platform/SUNW,Netra-T12/kernel/misc/sparcv9 755 root sys -s none platform/SUNW,Netra-T12/kernel/misc/sparcv9/pcicfg=../../../../sun4u/kernel/misc/sparcv9/pcicfg.e -d none platform/SUNW,UltraSPARC-IIe-NetraCT-40 755 root sys -d none platform/SUNW,UltraSPARC-IIe-NetraCT-40/kernel 755 root sys -d none platform/SUNW,UltraSPARC-IIe-NetraCT-40/kernel/misc 755 root sys -d none platform/SUNW,UltraSPARC-IIe-NetraCT-40/kernel/misc/sparcv9 755 root sys -s none platform/SUNW,UltraSPARC-IIe-NetraCT-40/kernel/misc/sparcv9/pcicfg=../../../../sun4u/kernel/misc/sparcv9/pcicfg.e -d none platform/SUNW,UltraSPARC-IIe-NetraCT-60 755 root sys -d none platform/SUNW,UltraSPARC-IIe-NetraCT-60/kernel 755 root sys -d none platform/SUNW,UltraSPARC-IIe-NetraCT-60/kernel/misc 755 root sys -d none platform/SUNW,UltraSPARC-IIe-NetraCT-60/kernel/misc/sparcv9 755 root sys -s none platform/SUNW,UltraSPARC-IIe-NetraCT-60/kernel/misc/sparcv9/pcicfg=../../../../sun4u/kernel/misc/sparcv9/pcicfg.e diff --git a/usr/src/pkgdefs/SUNWefcl/prototype_sparc b/usr/src/pkgdefs/SUNWefcl/prototype_sparc index 0dca26522c..a20f5ac105 100644 --- a/usr/src/pkgdefs/SUNWefcl/prototype_sparc +++ b/usr/src/pkgdefs/SUNWefcl/prototype_sparc @@ -22,7 +22,7 @@ # # ident "%Z%%M% %I% %E% SMI" # -# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # This required package information file contains a list of package contents. @@ -64,3 +64,6 @@ f none usr/lib/efcode/sparcv9/lfc_upa_pci.so 755 root bin f none usr/lib/efcode/sparcv9/lfc_gptwo.so 755 root bin s none usr/lib/efcode/sparcv9/lfc_gptwo_gptwo.so=lfc_gptwo.so s none usr/lib/efcode/sparcv9/lfc_gptwo_pci.so=lfc_upa_pci.so +s none usr/lib/efcode/sparcv9/lfc_pciex_pciex.so=lfc_upa_pci.so +s none usr/lib/efcode/sparcv9/lfc_pciex_pci.so=lfc_upa_pci.so +s none usr/lib/efcode/sparcv9/lfc_pci_pciex.so=lfc_upa_pci.so diff --git a/usr/src/uts/common/Makefile.files b/usr/src/uts/common/Makefile.files index f347dc71a7..f2d155fd25 100644 --- a/usr/src/uts/common/Makefile.files +++ b/usr/src/uts/common/Makefile.files @@ -684,6 +684,8 @@ PCIHPNEXUS_OBJS += pcihp.o PCIEHPCNEXUS_OBJS += pciehpc.o +PCISHPC_OBJS += pcishpc.o + PCICFG_OBJS += pcicfg.o OPENEEPR_OBJS += openprom.o diff --git a/usr/src/uts/common/Makefile.rules b/usr/src/uts/common/Makefile.rules index 2e6586e4e2..3a214afab7 100644 --- a/usr/src/uts/common/Makefile.rules +++ b/usr/src/uts/common/Makefile.rules @@ -476,6 +476,10 @@ $(OBJS_DIR)/%.o: $(UTSBASE)/common/io/hotplug/pciehpc/%.c $(COMPILE.c) -o $@ $< $(CTFCONVERT_O) +$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/hotplug/pcishpc/%.c + $(COMPILE.c) -o $@ $< + $(CTFCONVERT_O) + $(OBJS_DIR)/%.o: $(UTSBASE)/common/io/hotplug/pcihp/%.c $(COMPILE.c) -o $@ $< $(CTFCONVERT_O) @@ -1196,6 +1200,9 @@ $(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/hotplug/pcicfg/%.c $(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/hotplug/pciehpc/%.c @($(LHEAD) $(LINT.c) $< $(LTAIL)) +$(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/hotplug/pcishpc/%.c + @($(LHEAD) $(LINT.c) $< $(LTAIL)) + $(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/hotplug/pcihp/%.c @($(LHEAD) $(LINT.c) $< $(LTAIL)) diff --git a/usr/src/uts/common/io/hotplug/pciehpc/pciehpc.c b/usr/src/uts/common/io/hotplug/pciehpc/pciehpc.c index 329f053017..41435779fa 100644 --- a/usr/src/uts/common/io/hotplug/pciehpc/pciehpc.c +++ b/usr/src/uts/common/io/hotplug/pciehpc/pciehpc.c @@ -47,6 +47,9 @@ #include <sys/callb.h> #include <sys/ddi.h> #include <sys/sunddi.h> +#if defined(__sparc) +#include <sys/pcie_impl.h> +#endif #include <sys/hotplug/pci/pciehpc_impl.h> /* @@ -70,6 +73,12 @@ 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); +#if defined(__sparc) +static void pciehpc_disable_errors(pciehpc_t *ctrl_p); +static void pciehpc_enable_errors(pciehpc_t *ctrl_p); +#endif + #ifdef DEBUG int pciehpc_debug = 0; static void pciehpc_dump_hpregs(pciehpc_t *ctrl_p); @@ -186,6 +195,8 @@ pciehpc_init(dev_info_t *dip, pciehpc_regops_t *regops) goto cleanup; } + PCIEHPC_DISABLE_ERRORS(ctrl_p); + /* * Set the platform specific hot plug mode. */ @@ -245,6 +256,7 @@ cleanup2: (void) (ctrl_p->ops.uninit_hpc_hw)(ctrl_p); cleanup1: + PCIEHPC_ENABLE_ERRORS(ctrl_p); pciehpc_regs_teardown(&ctrl_p->cfghdl); cleanup: @@ -285,6 +297,8 @@ pciehpc_uninit(dev_info_t *dip) /* 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 */ pciehpc_regs_teardown(&ctrl_p->cfghdl); @@ -1257,6 +1271,7 @@ pciehpc_slot_control(caddr_t ops_arg, hpc_slot_t slot_hdl, /* 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: @@ -1270,11 +1285,16 @@ pciehpc_slot_control(caddr_t ops_arg, hpc_slot_t slot_hdl, 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) { + /*EMPTY*/ + PCIEHPC_ENABLE_ERRORS(ctrl_p); + } break; default: PCIEHPC_DEBUG((CE_WARN, @@ -1683,6 +1703,15 @@ pciehpc_dev_info(pciehpc_t *ctrl_p) 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)®spec, ®len) != DDI_SUCCESS) { @@ -1757,3 +1786,52 @@ pciehpc_set_slot_name(pciehpc_t *ctrl_p) 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); +} + +#if defined(__sparc) +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, ctrl_p->cfghdl); + 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) { + pcie_enable_errors(ctrl_p->dip, ctrl_p->cfghdl); + PCIEHPC_DEBUG3((CE_NOTE, "%s%d: pciehpc_enable_errors\n", + ddi_driver_name(ctrl_p->dip), + ddi_get_instance(ctrl_p->dip))); + } +} +#endif diff --git a/usr/src/uts/common/io/hotplug/pcishpc/pcishpc.c b/usr/src/uts/common/io/hotplug/pcishpc/pcishpc.c new file mode 100644 index 0000000000..e5a19c8cf4 --- /dev/null +++ b/usr/src/uts/common/io/hotplug/pcishpc/pcishpc.c @@ -0,0 +1,2379 @@ +/* + * 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. + * + * 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. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * 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/hpcsvc.h> + +/* + * SHPC controller registers accessed via the SHPC DWORD select and DATA + * registers in PCI configuration space relative to the SHPC capibility + * pointer. + */ +#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 + + +/* 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 + + +/* 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_MRL_STATE_MASK REG_BIT8 +#define SHPC_SLOT_CARD_EMPTY_MASK (REG_BIT10 | REG_BIT11) +#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_BIT29|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 + +/* reset delay is 2^25 clock cycles as per PCI spec @33MHz. */ +static int pcishpc_reset_delay = 0x1000000; + +/* 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 numSlots; /* # of HotPlug Slots */ + 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_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_t *pcishpc_p); +static int pcishpc_issue_command(pcishpc_t *pcishpc_p, uint8_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 v1.12", +}; + +/* 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->numSlots; 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->numSlots; 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)®spec, ®len) + != 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->numSlots; 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->numSlots; slot++) { + ctrl_p->slots[slot]->slot_events = 0; + + reg = pcishpc_read_reg(ctrl_p, SHPC_LOGICAL_SLOT_REGS+slot); + + /* Enable/Unmask all slot interrupts. */ + reg &= (~SHPC_SLOT_MASK_ALL); + + pcishpc_write_reg(ctrl_p, SHPC_LOGICAL_SLOT_REGS+slot, reg); + } + + 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->numSlots; 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); + } + + 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() faled to Register " + "slot"); + 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. */ + ctrl_p->numSlots = ((config)&31); + + /* 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->numSlots > 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_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, *prev_pcishpc_p; + pcishpc_t **pcishpc_pp; + + pcishpc_debug("pcishpc_destroy_slots() called(ctrl_p=%p)", ctrl_p); + + pcishpc_pp = &pcishpc_head; + prev_pcishpc_p = NULL; + + while ((pcishpc_p = *pcishpc_pp) != NULL) { + + pcishpc_pp = &(pcishpc_p->nextp); + + 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); + } + + if (prev_pcishpc_p == NULL) + pcishpc_head = pcishpc_p->nextp; + else + prev_pcishpc_p->nextp = pcishpc_p->nextp; + + pcishpc_debug("pcishpc_destroy_slots() (shpc_p=%p) " + "destroyed", pcishpc_p); + kmem_free(pcishpc_p, sizeof (pcishpc_t)); + } else + prev_pcishpc_p = pcishpc_p; + } + + 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; + + /* 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 */ + drv_usecwait(pcishpc_reset_delay); + + 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; + (void) pcishpc_setled(pcishpc_p, hpc_led_info->led, + hpc_led_info->state); + 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; + uint8_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 = 0; + + /* 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, cmd_code)); +} + + +/* + * pcishpc_wait_busy() + * + * Wait until the SHPC controller is not busy. + */ +static int +pcishpc_wait_busy(pcishpc_t *pcishpc_p) +{ + uint32_t status; + + /* Wait until SHPC controller is NOT busy. */ + /*CONSTCOND*/ + while (1) { + status = pcishpc_read_reg(pcishpc_p->ctrl, + SHPC_COMMAND_STATUS_REG); + + /* Is there an MRL Sensor error? */ + if (((status>>17) & 1)) { + pcishpc_debug("pcishpc_wait_busy() ERROR: MRL Sensor " + "error"); + break; + } + + /* Is there an Invalid command error? */ + if (((status>>18) & 1)) { + pcishpc_debug("pcishpc_wait_busy() ERROR: Invalid " + "command error"); + break; + } + + /* Is there an Invalid Speed/Mode error? */ + if (((status>>19) & 1)) { + pcishpc_debug("pcishpc_wait_busy() ERROR: Invalid " + "Speed/Mode error"); + break; + } + + /* Is the SHPC controller not BUSY? */ + if (((status>>16)&1) == 0) { + /* 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_t *pcishpc_p, uint8_t cmd_code) +{ + uint32_t command; + int retCode; + + pcishpc_debug("pcishpc_issue_command() cmd_code=%02x", cmd_code); + + /* Setup the slot for this command. */ + command = ((1+pcishpc_p->slotNum)<<8) | (uint32_t)cmd_code; + + mutex_enter(&pcishpc_p->ctrl->shpc_intr_mutex); + + pcishpc_p->ctrl->command_complete = B_FALSE; + + /* Write the command to the SHPC controller. */ + pcishpc_write_reg(pcishpc_p->ctrl, SHPC_COMMAND_STATUS_REG, command); + + while (pcishpc_p->ctrl->command_complete == B_FALSE) + cv_wait(&pcishpc_p->ctrl->cmd_comp_cv, + &pcishpc_p->ctrl->shpc_intr_mutex); + + /* Wait until the SHPC controller processes the command. */ + retCode = pcishpc_wait_busy(pcishpc_p); + + /* Make sure the command completed. */ + if (retCode == DDI_SUCCESS) { + + /* Did the command fail to generate the command complete IRQ? */ + if (pcishpc_p->ctrl->command_complete != B_TRUE) { + pcishpc_debug("pcishpc_issue_command() Failed on " + "generate cmd complete IRQ"); + retCode = DDI_FAILURE; + } + + } + + mutex_exit(&pcishpc_p->ctrl->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; + uint32_t config; + + 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 { + config = pcishpc_read_reg(ctrl_p, SHPC_SLOT_CONFIGURATION_REG); + p->phy_slot_num = SHPC_SLOT_CONFIG_PHY_SLOT_NUM(config); + } + + if (!p->phy_slot_num) { /* platform may not have initialized it */ + cmn_err(CE_WARN, "%s#%d: Invalid slot number! ", + ddi_driver_name(ctrl_p->shpc_dip), + ddi_get_instance(ctrl_p->shpc_dip)); + 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%ddev%d", + p->phy_slot_num, p->deviceNum); + else + (void) sprintf(p->slot_info.pci_slot_name, "pci%d", + p->phy_slot_num); +} diff --git a/usr/src/uts/common/sys/hotplug/pci/pciehpc_impl.h b/usr/src/uts/common/sys/hotplug/pci/pciehpc_impl.h index 6afbf1e432..e11e57109e 100644 --- a/usr/src/uts/common/sys/hotplug/pci/pciehpc_impl.h +++ b/usr/src/uts/common/sys/hotplug/pci/pciehpc_impl.h @@ -21,7 +21,7 @@ */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -80,6 +80,7 @@ typedef uint32_t pciehpc_soft_state_t; #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 @@ -210,6 +211,14 @@ extern int pciehpc_debug; /* default interrupt priority for Hot Plug interrupts */ #define PCIEHPC_INTR_PRI 1 +#if defined(__sparc) +#define PCIEHPC_ENABLE_ERRORS(arg) pciehpc_enable_errors(arg) +#define PCIEHPC_DISABLE_ERRORS(arg) pciehpc_disable_errors(arg) +#else +#define PCIEHPC_ENABLE_ERRORS(arg) +#define PCIEHPC_DISABLE_ERRORS(arg) +#endif + #ifdef __cplusplus } #endif diff --git a/usr/src/uts/common/sys/hotplug/pci/pcishpc.h b/usr/src/uts/common/sys/hotplug/pci/pcishpc.h new file mode 100644 index 0000000000..f8c1fab1c0 --- /dev/null +++ b/usr/src/uts/common/sys/hotplug/pci/pcishpc.h @@ -0,0 +1,50 @@ +/* + * 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. + * + * 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_PCISHPC_H +#define _SYS_PCISHPC_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#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 *); + +#define PCISHPC_INTR_PRI (LOCK_LEVEL - 1) +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_PCISHPC_H */ diff --git a/usr/src/uts/sparc/Makefile.files b/usr/src/uts/sparc/Makefile.files index c582f2a42b..a831f5a4ed 100644 --- a/usr/src/uts/sparc/Makefile.files +++ b/usr/src/uts/sparc/Makefile.files @@ -20,7 +20,7 @@ # CDDL HEADER END # # -# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" @@ -68,6 +68,7 @@ FD_OBJS += fd_asm.o CPR_SPARC_OBJS += cpr_sparc.o PCI_PCI_OBJS += pci_pci.o pci_debug.o pci_pwr.o pcix.o PX_PCI_OBJS += px_pci.o px_debug.o pcie_pwr.o +FCODE_OBJS += fcode.o # # file system modules @@ -90,6 +91,9 @@ KRTLD_OBJS += \ kobj_reloc.o SWAPGENERIC_OBJS += swapgeneric.o +PCICFG_E_OBJS += pcicfg.e.o +FCPCI_OBJS += fcpci.o +FCODEM_OBJS += fc_ddi.o fc_physio.o fc_ops.o fc_subr.o # # special files diff --git a/usr/src/uts/sparc/Makefile.sparc.shared b/usr/src/uts/sparc/Makefile.sparc.shared index 5bdb8df6b2..7933df65ed 100644 --- a/usr/src/uts/sparc/Makefile.sparc.shared +++ b/usr/src/uts/sparc/Makefile.sparc.shared @@ -264,6 +264,7 @@ DRV_KMODS += sbp2 DRV_KMODS += ib ibd DRV_KMODS += pci_pci px_pci pcie DRV_KMODS += i8042 kb8042 mouse8042 +DRV_KMODS += fcode $(CLOSED_BUILD)CLOSED_DRV_KMODS += audioens $(CLOSED_BUILD)CLOSED_DRV_KMODS += audiovia823x @@ -352,7 +353,7 @@ MISC_KMODS += kmech_krb5 MISC_KMODS += fssnap_if MISC_KMODS += hidparser kbtrans usba usba10 usbs49_fw MISC_KMODS += s1394 -MISC_KMODS += hpcsvc pcicfg pcihp +MISC_KMODS += hpcsvc pcihp pciehpc pcishpc MISC_KMODS += rsmops MISC_KMODS += kcf MISC_KMODS += ibcm @@ -364,6 +365,7 @@ MISC_KMODS += zmod MISC_KMODS += mac dls MISC_KMODS += cmlb MISC_KMODS += tem +MISC_KMODS += pcicfg.e fcodem fcpci $(CLOSED_BUILD)CLOSED_MISC_KMODS += amsrc1 $(CLOSED_BUILD)CLOSED_MISC_KMODS += klmmod klmops diff --git a/usr/src/uts/sun4u/fcode/Makefile b/usr/src/uts/sparc/fcode/Makefile index 3e980a4cae..5f5eac7595 100644 --- a/usr/src/uts/sun4u/fcode/Makefile +++ b/usr/src/uts/sparc/fcode/Makefile @@ -20,15 +20,16 @@ # CDDL HEADER END # # -# uts/sun4u/fcode/Makefile -# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# uts/sparc/fcode/Makefile +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" # -# This makefile drives the production of the ebus driver kernel module +# This makefile drives the production of the fcode driver kernel module # -# sun4u implementation architecture dependent +# sparc implementation architecture dependent # # @@ -42,14 +43,13 @@ UTSBASE = ../.. MODULE = fcode OBJECTS = $(FCODE_OBJS:%=$(OBJS_DIR)/%) LINTS = $(FCODE_OBJS:%.o=$(LINTS_DIR)/%.ln) -ROOTMODULE = $(ROOT_PSM_DRV_DIR)/$(MODULE) +ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE) CONF_SRCDIR = $(UTSBASE)/sun4/io/efcode - # # Include common rules. # -include $(UTSBASE)/sun4u/Makefile.sun4u +include $(UTSBASE)/sparc/Makefile.sparc # # Define targets @@ -58,6 +58,11 @@ ALL_TARGET = $(BINARY) $(SRC_CONFFILE) LINT_TARGET = $(MODULE).lint INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE) +# +# Include sun4 specific headers files +# +INC_PATH += -I$(UTSBASE)/sun4 + # Turn this on once compiler understands v9 in it's backend #INLINES += $(UTSBASE)/sun4/io/fcode.il @@ -95,4 +100,4 @@ install: $(INSTALL_DEPS) # # Include common targets. # -include $(UTSBASE)/sun4u/Makefile.targ +include $(UTSBASE)/sparc/Makefile.targ diff --git a/usr/src/uts/sun4u/fcodem/Makefile b/usr/src/uts/sparc/fcodem/Makefile index adcdd91b6c..94141b4ba7 100644 --- a/usr/src/uts/sun4u/fcodem/Makefile +++ b/usr/src/uts/sparc/fcodem/Makefile @@ -20,15 +20,16 @@ # CDDL HEADER END # # -# uts/sun4u/fcodem/Makefile -# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# uts/sparc/fcodem/Makefile +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" # -# This makefile drives the production of the ebus driver kernel module +# This makefile drives the production of the fcodem misc kernel module # -# sun4u implementation architecture dependent +# sparc implementation architecture dependent # # @@ -42,12 +43,12 @@ UTSBASE = ../.. MODULE = fcodem OBJECTS = $(FCODEM_OBJS:%=$(OBJS_DIR)/%) LINTS = $(FCODEM_OBJS:%.o=$(LINTS_DIR)/%.ln) -ROOTMODULE = $(ROOT_PSM_MISC_DIR)/$(MODULE) +ROOTMODULE = $(ROOT_MISC_DIR)/$(MODULE) # # Include common rules. # -include $(UTSBASE)/sun4u/Makefile.sun4u +include $(UTSBASE)/sparc/Makefile.sparc # # Define targets @@ -56,6 +57,11 @@ ALL_TARGET = $(BINARY) LINT_TARGET = $(MODULE).lint INSTALL_TARGET = $(BINARY) $(ROOTMODULE) +# +# Include sun4 specific headers files +# +INC_PATH += -I$(UTSBASE)/sun4 + # Turn this on once compiler understands v9 in it's backend #INLINES += $(UTSBASE)/sun4/io/fcode.il @@ -93,4 +99,4 @@ install: $(INSTALL_DEPS) # # Include common targets. # -include $(UTSBASE)/sun4u/Makefile.targ +include $(UTSBASE)/sparc/Makefile.targ diff --git a/usr/src/uts/sun4u/fcpci/Makefile b/usr/src/uts/sparc/fcpci/Makefile index 101284b7bd..c11db6ad71 100644 --- a/usr/src/uts/sun4u/fcpci/Makefile +++ b/usr/src/uts/sparc/fcpci/Makefile @@ -20,17 +20,17 @@ # CDDL HEADER END # # -# uts/sun4u/fcpci/Makefile -# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# uts/sparc/fcpci/Makefile +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" # -# This makefile drives the production of the ebus driver kernel module +# This makefile drives the production of the fcpci kernel misc module # -# sun4u implementation architecture dependent +# sparc implementation architecture dependent # - # # Path to the base of the uts directory tree (usually /usr/src/uts). # @@ -42,12 +42,12 @@ UTSBASE = ../.. MODULE = fcpci OBJECTS = $(FCPCI_OBJS:%=$(OBJS_DIR)/%) LINTS = $(FCPCI_OBJS:%.o=$(LINTS_DIR)/%.ln) -ROOTMODULE = $(ROOT_PSM_MISC_DIR)/$(MODULE) +ROOTMODULE = $(ROOT_MISC_DIR)/$(MODULE) # # Include common rules. # -include $(UTSBASE)/sun4u/Makefile.sun4u +include $(UTSBASE)/sparc/Makefile.sparc # # Define targets @@ -56,6 +56,11 @@ ALL_TARGET = $(BINARY) LINT_TARGET = $(MODULE).lint INSTALL_TARGET = $(BINARY) $(ROOTMODULE) +# +# Include sun4 specific headers files +# +INC_PATH += -I$(UTSBASE)/sun4 + # Turn this on once compiler understands v9 in it's backend #INLINES += $(UTSBASE)/sun4/io/fcode.il @@ -93,4 +98,4 @@ install: $(INSTALL_DEPS) # # Include common targets. # -include $(UTSBASE)/sun4u/Makefile.targ +include $(UTSBASE)/sparc/Makefile.targ diff --git a/usr/src/uts/sparc/ml/modstubs.s b/usr/src/uts/sparc/ml/modstubs.s index 2f2bdb9508..97bff00380 100644 --- a/usr/src/uts/sparc/ml/modstubs.s +++ b/usr/src/uts/sparc/ml/modstubs.s @@ -20,7 +20,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -982,13 +982,35 @@ stubs_base: #endif /* - * Stubs for PCI configurator module (misc/pcicfg). + * Stubs for PCI configurator module (misc/pcicfg.e). */ -#ifndef PCICFG_MODULE - MODULE(pcicfg,misc); - STUB(pcicfg, pcicfg_configure, 0); - STUB(pcicfg, pcicfg_unconfigure, 0); - END_MODULE(pcicfg); +#ifndef PCICFG_E_MODULE + MODULE(pcicfg.e,misc); + STUB(pcicfg.e, pcicfg_configure, 0); + STUB(pcicfg.e, pcicfg_unconfigure, 0); + END_MODULE(pcicfg.e); +#endif + +/* + * Stubs for PCIEHPC (pci-ex hot plug support) module (misc/pciehpc). + */ +#ifndef PCIEHPC_MODULE + MODULE(pciehpc,misc); + STUB(pciehpc, pciehpc_init, 0); + STUB(pciehpc, pciehpc_uninit, 0); + WSTUB(pciehpc, pciehpc_intr, 0); + END_MODULE(pciehpc); +#endif + +/* + * Stubs for PCISHPC (pci/pci-x shpc hot plug support) module (misc/pcishpc). + */ +#ifndef PCISHPC_MODULE + MODULE(pcishpc,misc); + STUB(pcishpc, pcishpc_init, 0); + STUB(pcishpc, pcishpc_uninit, 0); + WSTUB(pcishpc, pcishpc_intr, 0); + END_MODULE(pcishpc); #endif #ifndef PCIHP_MODULE diff --git a/usr/src/uts/sun4u/pcicfg.e/Makefile b/usr/src/uts/sparc/pcicfg.e/Makefile index c57597056a..9c48775d98 100644 --- a/usr/src/uts/sun4u/pcicfg.e/Makefile +++ b/usr/src/uts/sparc/pcicfg.e/Makefile @@ -20,8 +20,8 @@ # CDDL HEADER END # # -# uts/sun4u/pcicfg.e/Makefile -# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# uts/sparc/pcicfg.e/Makefile +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" @@ -29,7 +29,7 @@ # This makefile drives the production of the EFCode Enabled # PCI Configurator. # -# sun4u implementation architecture dependent +# sun4 implementation architecture dependent # # @@ -43,12 +43,12 @@ UTSBASE = ../.. MODULE = pcicfg.e OBJECTS = $(PCICFG_E_OBJS:%=$(OBJS_DIR)/%) LINTS = $(PCICFG_E_OBJS:%.o=$(LINTS_DIR)/%.ln) -ROOTMODULE = $(ROOT_PSM_MISC_DIR)/$(MODULE) +ROOTMODULE = $(ROOT_MISC_DIR)/$(MODULE) # # Include common rules. # -include $(UTSBASE)/sun4u/Makefile.sun4u +include $(UTSBASE)/sparc/Makefile.sparc # # Define targets @@ -57,6 +57,11 @@ ALL_TARGET = $(BINARY) LINT_TARGET = $(MODULE).lint INSTALL_TARGET = $(BINARY) $(ROOTMODULE) +# +# Include sun4 specific headers files +# +INC_PATH += -I$(UTSBASE)/sun4 + # Turn this on once compiler understands v9 in it's backend #INLINES += $(UTSBASE)/sun4u/io/pcicfg.il @@ -73,8 +78,7 @@ CFLAGS += -dalign -DPCICFG_INTERPRET_FCODE # # Dependency -LDFLAGS += -dy -Nmisc/busra -Nmisc/hpcsvc -Nmisc/fcpci -Nmisc/fcodem - +LDFLAGS += -dy -Nmisc/busra -Nmisc/fcpci -Nmisc/fcodem -Nmisc/pcie # # Default build targets. @@ -100,4 +104,4 @@ install: $(INSTALL_DEPS) # # Include common targets. # -include $(UTSBASE)/sun4u/Makefile.targ +include $(UTSBASE)/sparc/Makefile.targ diff --git a/usr/src/uts/sparc/pciehpc/Makefile b/usr/src/uts/sparc/pciehpc/Makefile new file mode 100644 index 0000000000..1f9d29c436 --- /dev/null +++ b/usr/src/uts/sparc/pciehpc/Makefile @@ -0,0 +1,87 @@ +# +# 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. +# +# 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 +# +# +# uts/sparc/pciehpc/Makefile +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +# This makefile drives the production of the kernel/misc/pciehpc module +# (PCIe hotplug controller module) for PCIe hotplug support in PCIe nexus +# drivers. +# + +# +# Path to the base of the uts directory tree (usually /usr/src/uts). +# +UTSBASE = ../.. + +# +# Define the module and object file sets. +# +MODULE = pciehpc +OBJECTS = $(PCIEHPCNEXUS_OBJS:%=$(OBJS_DIR)/%) +LINTS = $(PCIEHPCNEXUS_OBJS:%.o=$(LINTS_DIR)/%.ln) +ROOTMODULE = $(ROOT_MISC_DIR)/$(MODULE) + +# +# Include common rules. + +include $(UTSBASE)/sparc/Makefile.sparc + +# +# Define targets +# +ALL_TARGET = $(BINARY) +LINT_TARGET = $(MODULE).lint +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) + +# +# Dependency +LDFLAGS += -dy -Nmisc/hpcsvc -Nmisc/pcie + +# +# Default build targets. +# +.KEEP_STATE: + +def: $(DEF_DEPS) + +all: $(ALL_DEPS) + +clean: $(CLEAN_DEPS) + +clobber: $(CLOBBER_DEPS) + +lint: $(LINT_DEPS) + +modlintlib: $(MODLINTLIB_DEPS) + +clean.lint: $(CLEAN_LINT_DEPS) + +install: $(INSTALL_DEPS) + +# +# Include common targets. +# +include $(UTSBASE)/sparc/Makefile.targ diff --git a/usr/src/uts/sparc/pcihp/Makefile b/usr/src/uts/sparc/pcihp/Makefile index b410885379..8e7076c31e 100644 --- a/usr/src/uts/sparc/pcihp/Makefile +++ b/usr/src/uts/sparc/pcihp/Makefile @@ -21,7 +21,7 @@ # # # uts/sparc/pcihp/Makefile -# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #pragma ident "%Z%%M% %I% %E% SMI" @@ -71,7 +71,7 @@ CLEANFILES += $(MODSTUBS_O) # # Dependency -LDFLAGS += -dy -Nmisc/busra -Nmisc/hpcsvc -Nmisc/pcicfg +LDFLAGS += -dy -Nmisc/busra -Nmisc/hpcsvc -Nmisc/pcicfg.e # # Default build targets. diff --git a/usr/src/uts/sparc/pcishpc/Makefile b/usr/src/uts/sparc/pcishpc/Makefile new file mode 100644 index 0000000000..4052e0338f --- /dev/null +++ b/usr/src/uts/sparc/pcishpc/Makefile @@ -0,0 +1,87 @@ +# +# 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. +# +# 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 +# +# +# uts/sparc/pcishpc/Makefile +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +# This makefile drives the production of the kernel/misc/pcishpc module +# (PCI SHPC hotplug controller module) for PCI hotplug support in PCI +# nexus drivers. +# + +# +# Path to the base of the uts directory tree (usually /usr/src/uts). +# +UTSBASE = ../.. + +# +# Define the module and object file sets. +# +MODULE = pcishpc +OBJECTS = $(PCISHPC_OBJS:%=$(OBJS_DIR)/%) +LINTS = $(PCISHPC_OBJS:%.o=$(LINTS_DIR)/%.ln) +ROOTMODULE = $(ROOT_MISC_DIR)/$(MODULE) + +# +# Include common rules. + +include $(UTSBASE)/sparc/Makefile.sparc + +# +# Define targets +# +ALL_TARGET = $(BINARY) +LINT_TARGET = $(MODULE).lint +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) + +# +# Dependency +LDFLAGS += -dy -Nmisc/hpcsvc + +# +# Default build targets. +# +.KEEP_STATE: + +def: $(DEF_DEPS) + +all: $(ALL_DEPS) + +clean: $(CLEAN_DEPS) + +clobber: $(CLOBBER_DEPS) + +lint: $(LINT_DEPS) + +modlintlib: $(MODLINTLIB_DEPS) + +clean.lint: $(CLEAN_LINT_DEPS) + +install: $(INSTALL_DEPS) + +# +# Include common targets. +# +include $(UTSBASE)/sparc/Makefile.targ diff --git a/usr/src/uts/sparc/px_pci/Makefile b/usr/src/uts/sparc/px_pci/Makefile index fd148993ee..3d6b919e2f 100644 --- a/usr/src/uts/sparc/px_pci/Makefile +++ b/usr/src/uts/sparc/px_pci/Makefile @@ -22,7 +22,7 @@ # # uts/sparc/px_pci/Makefile # -# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" @@ -75,7 +75,7 @@ CFLAGS += -dalign # # Dependency # -LDFLAGS += -dy -Nmisc/pcie +LDFLAGS += -dy -Nmisc/pcie -Nmisc/pcishpc -Nmisc/pcihp -Nmisc/pciehpc # # Default build targets. @@ -102,4 +102,3 @@ install: $(INSTALL_DEPS) # Include common targets. # include $(UTSBASE)/sparc/Makefile.targ -include $(UTSBASE)/sun4/Makefile.rules diff --git a/usr/src/uts/sun4/Makefile.files b/usr/src/uts/sun4/Makefile.files index 8ad679100e..2be0e9093f 100644 --- a/usr/src/uts/sun4/Makefile.files +++ b/usr/src/uts/sun4/Makefile.files @@ -80,9 +80,6 @@ PX_OBJS += px.o px_cb.o px_debug.o px_devctl.o px_dma.o \ FPC_OBJS += fpc.o fpc-impl.o fpc-kstats.o VIS_OBJS += visinstr.o TOD_OBJS += tod.o -FCODE_OBJS += fcode.o -FCODEM_OBJS += fc_ddi.o fc_physio.o fc_ops.o fc_subr.o -FCPCI_OBJS += fcpci.o EBUS_OBJS += ebus.o SU_OBJS += su_driver.o diff --git a/usr/src/uts/sun4u/io/pcicfg.e.c b/usr/src/uts/sun4/io/pcicfg.e.c index 3fb3f38717..aa5fef244e 100644 --- a/usr/src/uts/sun4u/io/pcicfg.e.c +++ b/usr/src/uts/sun4/io/pcicfg.e.c @@ -20,7 +20,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -37,21 +37,20 @@ #include <sys/debug.h> #include <sys/modctl.h> #include <sys/autoconf.h> - #include <sys/hwconf.h> #include <sys/ddi_impldefs.h> - #include <sys/fcode.h> - -#include <sys/pci.h> - +#include <sys/pcie.h> +#include <sys/pcie_impl.h> #include <sys/ddi.h> #include <sys/sunddi.h> #include <sys/sunndi.h> #include <sys/hotplug/pci/pcicfg.h> - #include <sys/ndi_impldefs.h> +#define PCICFG_DEVICE_TYPE_PCI 1 +#define PCICFG_DEVICE_TYPE_PCIE 2 + #define EFCODE21554 /* changes for supporting 21554 */ static int pcicfg_alloc_resource(dev_info_t *, pci_regspec_t); @@ -100,6 +99,12 @@ static int pcicfg_start_devno = 0; /* for Debug only */ #define PCICFG_IO_MULT 4 #define PCICFG_RANGE_LEN 2 /* Number of range entries */ +static int pcicfg_slot_busnums = 8; +static int pcicfg_slot_memsize = 32 * PCICFG_MEMGRAN; /* 32MB per slot */ +static int pcicfg_slot_iosize = 64 * PCICFG_IOGRAN; /* 64K per slot */ +static int pcicfg_chassis_per_tree = 1; +static int pcicfg_sec_reset_delay = 1000000; + /* * The following typedef is used to represent a * 1275 "bus-range" property of a PCI Bus node. @@ -174,6 +179,14 @@ struct pcicfg_find_ctrl { dev_info_t *dip; }; +typedef struct pcicfg_err_regs { + uint16_t cmd; + uint16_t bcntl; + uint16_t pcie_dev; + uint16_t devctl; + int pcie_cap_off; +} pcicfg_err_regs_t; + /* * List of Indirect Config Map Devices. At least the intent of the * design is to look for a device in this list during the configure @@ -249,7 +262,8 @@ int pcicfg_dont_interpret = 1; static int pcicfg_add_config_reg(dev_info_t *, uint_t, uint_t, uint_t); -static int pcicfg_probe_children(dev_info_t *, uint_t, uint_t, uint_t); +static int pcicfg_probe_children(dev_info_t *, uint_t, uint_t, uint_t, + uint_t *); #ifdef PCICFG_INTERPRET_FCODE static int pcicfg_load_fcode(dev_info_t *, uint_t, uint_t, uint_t, @@ -275,10 +289,14 @@ static int pcicfg_bridge_assign(dev_info_t *, void *); static int pcicfg_free_resources(dev_info_t *); static void pcicfg_setup_bridge(pcicfg_phdl_t *, ddi_acc_handle_t); static void pcicfg_update_bridge(pcicfg_phdl_t *, ddi_acc_handle_t); +static void pcicfg_enable_bridge_probe_err(dev_info_t *dip, + ddi_acc_handle_t h, pcicfg_err_regs_t *regs); +static void pcicfg_disable_bridge_probe_err(dev_info_t *dip, + ddi_acc_handle_t h, pcicfg_err_regs_t *regs); static int pcicfg_update_assigned_prop(dev_info_t *, pci_regspec_t *); static void pcicfg_device_on(ddi_acc_handle_t); static void pcicfg_device_off(ddi_acc_handle_t); -static int pcicfg_set_busnode_props(dev_info_t *); +static int pcicfg_set_busnode_props(dev_info_t *, uint8_t); static int pcicfg_free_bridge_resources(dev_info_t *); static int pcicfg_free_device_resources(dev_info_t *); static int pcicfg_teardown_device(dev_info_t *); @@ -380,7 +398,7 @@ extern struct mod_ops mod_miscops; static struct modlmisc modlmisc = { &mod_miscops, /* Type of module */ - "PCI Config (EFCode Enabled) v%I%" + "PCIe/PCI Config (EFCode Enabled) v%I%" }; static struct modlinkage modlinkage = { @@ -539,6 +557,152 @@ struct modinfo *modinfop; } /* + * given a cap_id, return its cap_id location in config space + */ +static int +pcicfg_get_cap(ddi_acc_handle_t config_handle, uint8_t cap_id) +{ + uint8_t curcap; + uint_t cap_id_loc; + uint16_t status; + int location = -1; + + /* + * Need to check the Status register for ECP support first. + * Also please note that for type 1 devices, the + * offset could change. Should support type 1 next. + */ + status = pci_config_get16(config_handle, PCI_CONF_STAT); + if (!(status & PCI_STAT_CAP)) { + return (-1); + } + cap_id_loc = pci_config_get8(config_handle, PCI_CONF_CAP_PTR); + + /* Walk the list of capabilities */ + while (cap_id_loc) { + + curcap = pci_config_get8(config_handle, cap_id_loc); + + if (curcap == cap_id) { + location = cap_id_loc; + break; + } + cap_id_loc = pci_config_get8(config_handle, + cap_id_loc + 1); + } + return (location); +} + +/*ARGSUSED*/ +static uint8_t +pcicfg_get_nslots(dev_info_t *dip, ddi_acc_handle_t handle) +{ + int cap_id_loc; + uint8_t num_slots = 0; + + /* just depend on the pcie_cap for now. */ + if ((cap_id_loc = pcicfg_get_cap(handle, PCI_CAP_ID_PCI_E)) + > 0) { + if (pci_config_get16(handle, cap_id_loc + + PCIE_PCIECAP) & + PCIE_PCIECAP_SLOT_IMPL) + num_slots = 1; + } else /* not a PCIe switch/bridge. Must be a PCI-PCI[-X] bridge */ + if ((cap_id_loc = pcicfg_get_cap(handle, PCI_CAP_ID_SLOT_ID)) + > 0) { + uint8_t esr_reg = pci_config_get8(handle, cap_id_loc + + PCI_CAP_ID_REGS_OFF); + num_slots = PCI_CAPSLOT_NSLOTS(esr_reg); + } + /* XXX - need to cover PCI-PCIe bridge with n slots */ + return (num_slots); +} + +/*ARGSUSED*/ +static uint8_t +pcicfg_is_chassis(dev_info_t *dip, ddi_acc_handle_t handle) +{ + int cap_id_loc; + + if ((cap_id_loc = pcicfg_get_cap(handle, PCI_CAP_ID_SLOT_ID)) + > 0) { + uint8_t esr_reg = pci_config_get8(handle, cap_id_loc + 2); + if (PCI_CAPSLOT_FIC(esr_reg)) + return (B_TRUE); + } + return (B_FALSE); +} + +/*ARGSUSED*/ +static int +pcicfg_pcie_dev(dev_info_t *dip, int bus_type, pcicfg_err_regs_t *regs) +{ + /* get parent device's device_type property */ + char *device_type; + int rc = DDI_FAILURE; + dev_info_t *pdip = ddi_get_parent(dip); + + regs->pcie_dev = 0; + if (ddi_prop_lookup_string(DDI_DEV_T_ANY, pdip, + DDI_PROP_DONTPASS, "device_type", &device_type) + != DDI_PROP_SUCCESS) { + DEBUG2("device_type property missing for %s#%d", + ddi_get_name(pdip), ddi_get_instance(pdip)); + return (DDI_FAILURE); + } + switch (bus_type) { + case PCICFG_DEVICE_TYPE_PCIE: + if (strcmp(device_type, "pciex") == 0) { + rc = DDI_SUCCESS; + regs->pcie_dev = 1; + } + break; + case PCICFG_DEVICE_TYPE_PCI: + if (strcmp(device_type, "pci") == 0) + rc = DDI_SUCCESS; + break; + default: + break; + } + ddi_prop_free(device_type); + return (rc); +} + +/*ARGSUSED*/ +static int +pcicfg_pcie_port_type(dev_info_t *dip, ddi_acc_handle_t handle) +{ + int port_type = -1; + int cap_loc; + + if ((cap_loc = pcicfg_get_cap(handle, PCI_CAP_ID_PCI_E)) > 0) + port_type = pci_config_get16(handle, + cap_loc + PCIE_PCIECAP) & PCIE_PCIECAP_DEV_TYPE_MASK; + + return (port_type); +} + +static int +pcicfg_pcie_device_type(dev_info_t *dip, ddi_acc_handle_t handle) +{ + int port_type = pcicfg_pcie_port_type(dip, handle); + + DEBUG1("device port_type = %x\n", port_type); + /* No PCIe CAP regs, we are not PCIe device_type */ + if (port_type < 0) + return (DDI_FAILURE); + + /* check for all PCIe device_types */ + if ((port_type == PCIE_PCIECAP_DEV_TYPE_UP) || + (port_type == PCIE_PCIECAP_DEV_TYPE_DOWN) || + (port_type == PCIE_PCIECAP_DEV_TYPE_PCI2PCIE)) + return (DDI_SUCCESS); + + return (DDI_FAILURE); + +} + +/* * In the following functions ndi_devi_enter() without holding the * parent dip is sufficient. This is because pci dr is driven through * opens on the nexus which is in the device tree path above the node @@ -597,6 +761,8 @@ pcicfg_configure(dev_info_t *devi, uint_t device) DEBUG3("no device : bus " "[0x%x] slot [0x%x] func [0x%x]\n", bus, device, func); + if (func) + continue; break; default: DEBUG3("configure: bus => [%d] " @@ -632,7 +798,7 @@ cleanup: if ((new_device = pcicfg_devi_find(devi, device, func)) == NULL) { DEBUG0("No more devices to clean up\n"); - break; + continue; } DEBUG2("Cleaning up device [0x%x] function [0x%x]\n", @@ -667,6 +833,7 @@ pcicfg_configure_ntbridge(dev_info_t *new_device, uint_t bus, uint_t device) uint64_t next_bus; uint64_t blen; ndi_ra_request_t req; + uint8_t pcie_device_type = 0; /* * If we need to do indirect config, lets create a property here @@ -675,15 +842,22 @@ pcicfg_configure_ntbridge(dev_info_t *new_device, uint_t bus, uint_t device) * mapped directly under the host. */ if ((rc = ndi_prop_update_int(DDI_DEV_T_NONE, new_device, - PCICFG_DEV_CONF_MAP_PROP, (int)DDI_SUCCESS)) + PCI_DEV_CONF_MAP_PROP, (int)DDI_SUCCESS)) != DDI_SUCCESS) { DEBUG0("Cannot create indirect conf map property.\n"); return ((int)PCICFG_FAILURE); } + if (pci_config_setup(new_device, &config_handle) != DDI_SUCCESS) + return (PCICFG_FAILURE); + /* check if we are PCIe device */ + if (pcicfg_pcie_device_type(new_device, config_handle) == DDI_SUCCESS) + pcie_device_type = 1; + pci_config_teardown(&config_handle); /* create Bus node properties for ntbridge. */ - if (pcicfg_set_busnode_props(new_device) != PCICFG_SUCCESS) { + if (pcicfg_set_busnode_props(new_device, pcie_device_type) != + PCICFG_SUCCESS) { DEBUG0("Failed to set busnode props\n"); return (rc); } @@ -1217,15 +1391,15 @@ pcicfg_indirect_map(dev_info_t *dip) #if defined(__sparc) int rc = DDI_FAILURE; - if (ddi_getprop(DDI_DEV_T_ANY, ddi_get_parent(dip), DDI_PROP_DONTPASS, - PCICFG_DEV_CONF_MAP_PROP, DDI_FAILURE) != DDI_FAILURE) + if (ddi_prop_get_int(DDI_DEV_T_ANY, ddi_get_parent(dip), 0, + PCI_DEV_CONF_MAP_PROP, DDI_FAILURE) != DDI_FAILURE) rc = DDI_SUCCESS; else - if (ddi_getprop(DDI_DEV_T_ANY, ddi_get_parent(dip), - DDI_PROP_DONTPASS, PCICFG_BUS_CONF_MAP_PROP, - DDI_FAILURE) != DDI_FAILURE) + if (ddi_prop_get_int(DDI_DEV_T_ANY, ddi_get_parent(dip), + 0, PCI_BUS_CONF_MAP_PROP, + DDI_FAILURE) != DDI_FAILURE) rc = DDI_SUCCESS; - + DEBUG1("pci conf map = %d", rc); return (rc); #else return (DDI_SUCCESS); @@ -1299,7 +1473,7 @@ pcicfg_unconfigure(dev_info_t *devi, uint_t device) */ for (func = 0; func < PCICFG_MAX_FUNCTION; func++) { if ((child_dip = pcicfg_devi_find(devi, device, func)) == NULL) - break; + continue; if (ndi_devi_offline(child_dip, NDI_UNCONFIG) == NDI_SUCCESS) continue; @@ -1311,12 +1485,12 @@ pcicfg_unconfigure(dev_info_t *devi, uint_t device) DEBUG2("Device [0x%x] function [%x] is busy\n", device, func); for (i = 0; i < func; i++) { if ((child_dip = pcicfg_devi_find(devi, device, i)) - == NULL) { + == NULL) { DEBUG0("No more devices to put back on line!!\n"); /* * Made it through all functions */ - break; + continue; } if (ndi_devi_online(child_dip, NDI_CONFIG) != NDI_SUCCESS) { DEBUG0("Failed to put back devices state\n"); @@ -1333,7 +1507,7 @@ pcicfg_unconfigure(dev_info_t *devi, uint_t device) if ((child_dip = pcicfg_devi_find(devi, device, func)) == NULL) { DEBUG0("No more devices to tear down!\n"); - break; + continue; } DEBUG2("Tearing down device [0x%x] function [0x%x]\n", @@ -1561,17 +1735,15 @@ pcicfg_bridge_assign(dev_info_t *dip, void *hdl) pcicfg_phdl_t *entry = (pcicfg_phdl_t *)hdl; - DEBUG1("bridge assign: assigning addresses to %s\n", - ddi_get_name(dip)); - - entry->error = PCICFG_SUCCESS; + DEBUG1("bridge assign: assigning addresses to %s\n", ddi_get_name(dip)); if (entry == NULL) { DEBUG0("Failed to get entry\n"); - entry->error = PCICFG_FAILURE; return (DDI_WALK_TERMINATE); } + entry->error = PCICFG_SUCCESS; + if (pcicfg_config_setup(dip, &handle) != DDI_SUCCESS) { DEBUG0("Failed to map config space!\n"); entry->error = PCICFG_FAILURE; @@ -1841,6 +2013,12 @@ pcicfg_device_assign(dev_info_t *dip) reg[i].pci_phys_low = PCICFG_LOADDR(answer); reg[i].pci_phys_mid = PCICFG_HIADDR(answer); + /* + * currently support 32b address space + * assignments only. + */ + reg[i].pci_phys_hi ^= PCI_ADDR_MEM64 ^ + PCI_ADDR_MEM32; offset += 8; break; @@ -2345,11 +2523,22 @@ pcicfg_free_bridge_resources(dev_info_t *dip) DDI_PROP_DONTPASS, "ranges", (caddr_t)&ranges, &length) != DDI_PROP_SUCCESS) { DEBUG0("Failed to read ranges property\n"); + if (ddi_get_child(dip)) { + cmn_err(CE_WARN, "No ranges property found for %s", + ddi_get_name(dip)); + /* + * strictly speaking, we can check for children with + * assigned-addresses but for now it is better to + * be conservative and assume that if there are child + * nodes, then they do consume PCI memory or IO + * resources, Hence return failure. + */ + return (PCICFG_FAILURE); + } + length = 0; + + } -#if 0 - return (PCICFG_FAILURE); -#endif - } else { for (i = 0; i < length / sizeof (pcicfg_range_t); i++) { if (ranges[i].size_lo != 0 || ranges[i].size_hi != 0) { @@ -2396,8 +2585,8 @@ pcicfg_free_bridge_resources(dev_info_t *dip) } } - kmem_free(ranges, length); - } + if (length) + kmem_free(ranges, length); if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "bus-range", (caddr_t)&bus, @@ -2412,6 +2601,7 @@ pcicfg_free_bridge_resources(dev_info_t *dip) if (ndi_ra_free(ddi_get_parent(dip), (uint64_t)bus[0], (uint64_t)(bus[1] - bus[0] + 1), NDI_RA_TYPE_PCI_BUSNUM, NDI_RA_PASS) != NDI_SUCCESS) { + /*EMPTY*/ DEBUG0("Failed to free a bus number\n"); } /* @@ -2785,16 +2975,17 @@ pcicfg_device_off(ddi_acc_handle_t config_handle) * header of the PCI device */ static int -pcicfg_set_standard_props(dev_info_t *dip, ddi_acc_handle_t config_handle) +pcicfg_set_standard_props(dev_info_t *dip, ddi_acc_handle_t config_handle, + uint8_t pcie_dev) { - int ret; + int ret, cap_id_loc; uint16_t val; uint32_t wordval; uint8_t byteval; /* These two exists only for non-bridges */ - if ((pci_config_get8(config_handle, - PCI_CONF_HEADER) & PCI_HEADER_TYPE_M) == PCI_HEADER_ZERO) { + if (((pci_config_get8(config_handle, PCI_CONF_HEADER) + & PCI_HEADER_TYPE_M) == PCI_HEADER_ZERO) && !pcie_dev) { byteval = pci_config_get8(config_handle, PCI_CONF_MIN_G); if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, "min-grant", byteval)) != DDI_SUCCESS) { @@ -2848,13 +3039,17 @@ pcicfg_set_standard_props(dev_info_t *dip, ddi_acc_handle_t config_handle) * present (but with no value other than its own existence) if the bit * is set, non-existent otherwise */ - if (pci_config_get16(config_handle, PCI_CONF_STAT) & PCI_STAT_FBBC) { + if ((!pcie_dev) && + (pci_config_get16(config_handle, PCI_CONF_STAT) & + PCI_STAT_FBBC)) { if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, "fast-back-to-back", 0)) != DDI_SUCCESS) { return (ret); } } - if (pci_config_get16(config_handle, PCI_CONF_STAT) & PCI_STAT_66MHZ) { + if ((!pcie_dev) && + (pci_config_get16(config_handle, PCI_CONF_STAT) & + PCI_STAT_66MHZ)) { if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, "66mhz-capable", 0)) != DDI_SUCCESS) { return (ret); @@ -2909,15 +3104,38 @@ pcicfg_set_standard_props(dev_info_t *dip, ddi_acc_handle_t config_handle) return (ret); } } + if (pcie_dev && (cap_id_loc = pcicfg_get_cap(config_handle, + PCI_CAP_ID_PCI_E)) > 0) { + val = pci_config_get16(config_handle, cap_id_loc + + PCIE_PCIECAP) & PCIE_PCIECAP_SLOT_IMPL; + /* if slot implemented, get physical slot number */ + if (val) { + wordval = (pci_config_get32(config_handle, cap_id_loc + + PCIE_SLOTCAP) >> + PCIE_SLOTCAP_PHY_SLOT_NUM_SHIFT) & + PCIE_SLOTCAP_PHY_SLOT_NUM_MASK; + if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, + dip, "physical-slot#", wordval)) + != DDI_SUCCESS) { + return (ret); + } + } + } return (PCICFG_SUCCESS); } static int -pcicfg_set_busnode_props(dev_info_t *dip) +pcicfg_set_busnode_props(dev_info_t *dip, uint8_t pcie_device_type) { int ret; + char device_type[8]; + + if (pcie_device_type) + (void) strcpy(device_type, "pciex"); + else + (void) strcpy(device_type, "pci"); if ((ret = ndi_prop_update_string(DDI_DEV_T_NONE, dip, - "device_type", "pci")) != DDI_SUCCESS) { + "device_type", device_type)) != DDI_SUCCESS) { return (ret); } if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, @@ -2932,27 +3150,38 @@ pcicfg_set_busnode_props(dev_info_t *dip) } static int -pcicfg_set_childnode_props(dev_info_t *dip, ddi_acc_handle_t config_handle) +pcicfg_set_childnode_props(dev_info_t *dip, ddi_acc_handle_t config_handle, + uint8_t pcie_dev) { int ret; -#ifndef _DONT_USE_1275_GENERIC_NAMES - uint32_t wordval; -#endif char *name; - char buffer[64]; - uint32_t classcode; - char *compat[8]; + char buffer[64], pprefix[8]; + uint16_t classcode; + uint8_t revid, pif, pclass, psubclass; + char *compat[24]; int i; int n; - uint16_t sub_vid, sub_sid; + uint16_t sub_vid, sub_sid, vid, did; + + /* set the property prefix based on the device type */ + if (pcie_dev) + (void) sprintf(pprefix, "pciex"); + else + (void) sprintf(pprefix, "pci"); + sub_vid = pci_config_get16(config_handle, PCI_CONF_SUBVENID), + sub_sid = pci_config_get16(config_handle, PCI_CONF_SUBSYSID); + vid = pci_config_get16(config_handle, PCI_CONF_VENID), + did = pci_config_get16(config_handle, PCI_CONF_DEVID); + revid = pci_config_get8(config_handle, PCI_CONF_REVID); + pif = pci_config_get8(config_handle, PCI_CONF_PROGCLASS); + classcode = pci_config_get16(config_handle, PCI_CONF_SUBCLASS); + pclass = pci_config_get8(config_handle, PCI_CONF_BASCLASS); + psubclass = pci_config_get8(config_handle, PCI_CONF_SUBCLASS); + /* * NOTE: These are for both a child and PCI-PCI bridge node */ -#ifndef _DONT_USE_1275_GENERIC_NAMES - wordval = (pci_config_get16(config_handle, PCI_CONF_SUBCLASS)<< 8) | - (pci_config_get8(config_handle, PCI_CONF_PROGCLASS)); -#endif /* * "name" property rule @@ -2980,15 +3209,10 @@ pcicfg_set_childnode_props(dev_info_t *dip, ddi_acc_handle_t config_handle) * ssid = subsystem id */ - sub_vid = pci_config_get16(config_handle, PCI_CONF_SUBVENID), - sub_sid = pci_config_get16(config_handle, PCI_CONF_SUBSYSID); - if ((pci_config_get16(config_handle, PCI_CONF_SUBSYSID) != 0) || - (pci_config_get16(config_handle, PCI_CONF_SUBVENID) != 0)) { - (void) sprintf(buffer, "pci%x,%x", sub_vid, sub_sid); + if ((sub_sid != 0) || (sub_vid != 0)) { + (void) sprintf(buffer, "%s%x,%x", pprefix, sub_vid, sub_sid); } else { - (void) sprintf(buffer, "pci%x,%x", - pci_config_get16(config_handle, PCI_CONF_VENID), - pci_config_get16(config_handle, PCI_CONF_DEVID)); + (void) sprintf(buffer, "%s%x,%x", pprefix, vid, did); } /* @@ -3000,7 +3224,7 @@ pcicfg_set_childnode_props(dev_info_t *dip, ddi_acc_handle_t config_handle) #ifdef _DONT_USE_1275_GENERIC_NAMES name = buffer; #else - if ((name = pcicfg_get_class_name(wordval>>8)) == NULL) { + if ((name = pcicfg_get_class_name(classcode)) == NULL) { /* * Set name to the above fabricated name */ @@ -3025,23 +3249,53 @@ pcicfg_set_childnode_props(dev_info_t *dip, ddi_acc_handle_t config_handle) (void) strcpy(compat[n++], buffer); /* - * Add in the VendorID/DeviceID compatible name. + * Setup 'compatible' as per the PCI2.1 bindings document. + * pci[ex]VVVV,DDDD.SSSS.ssss.RR + * pci[ex]VVVV,DDDD.SSSS.ssss + * pciSSSS.ssss -> not created for PCIe as per PCIe bindings + * pci[ex]VVVV,DDDD.RR + * pci[ex]VVVV,DDDD + * pci[ex]class,CCSSPP + * pci[ex]class,CCSS */ - (void) sprintf(buffer, "pci%x,%x", - pci_config_get16(config_handle, PCI_CONF_VENID), - pci_config_get16(config_handle, PCI_CONF_DEVID)); + /* pci[ex]VVVV,DDDD.SSSS.ssss.RR */ + (void) sprintf(buffer, "%s%x,%x.%x.%x.%x", pprefix, vid, did, + sub_vid, sub_sid, revid); compat[n] = kmem_alloc(strlen(buffer) + 1, KM_SLEEP); (void) strcpy(compat[n++], buffer); - classcode = (pci_config_get16(config_handle, PCI_CONF_SUBCLASS)<< 8) | - (pci_config_get8(config_handle, PCI_CONF_PROGCLASS)); + /* pci[ex]VVVV,DDDD.SSSS.ssss */ + (void) sprintf(buffer, "%s%x,%x.%x.%x", pprefix, vid, did, + sub_vid, sub_sid); + compat[n] = kmem_alloc(strlen(buffer) + 1, KM_SLEEP); + (void) strcpy(compat[n++], buffer); - /* - * Add in the Classcode - */ - (void) sprintf(buffer, "pciclass,%06x", classcode); + /* pciSSSS.ssss -> not created for PCIe as per PCIe bindings */ + if (!pcie_dev) { + (void) sprintf(buffer, "pci%x,%x", sub_vid, sub_sid); + compat[n] = kmem_alloc(strlen(buffer) + 1, KM_SLEEP); + (void) strcpy(compat[n++], buffer); + } + /* pci[ex]VVVV,DDDD.RR */ + (void) sprintf(buffer, "%s%x,%x.%x", pprefix, vid, did, revid); + compat[n] = kmem_alloc(strlen(buffer) + 1, KM_SLEEP); + (void) strcpy(compat[n++], buffer); + + /* pci[ex]VVVV,DDDD */ + (void) sprintf(buffer, "%s%x,%x", pprefix, vid, did); + compat[n] = kmem_alloc(strlen(buffer) + 1, KM_SLEEP); + (void) strcpy(compat[n++], buffer); + + /* pci[ex]class,CCSSPP */ + (void) sprintf(buffer, "%sclass,%02x%02x%02x", pprefix, + pclass, psubclass, pif); + compat[n] = kmem_alloc(strlen(buffer) + 1, KM_SLEEP); + (void) strcpy(compat[n++], buffer); + + /* pci[ex]class,CCSS */ + (void) sprintf(buffer, "%sclass,%04x", pprefix, classcode); compat[n] = kmem_alloc(strlen(buffer) + 1, KM_SLEEP); (void) strcpy(compat[n++], buffer); @@ -3075,6 +3329,8 @@ static void pcicfg_set_bus_numbers(ddi_acc_handle_t config_handle, uint_t primary, uint_t secondary, uint_t subordinate) { + DEBUG3("Setting bridge bus-range %d,%d,%d\n", primary, secondary, + subordinate); /* * Primary bus# */ @@ -3151,7 +3407,7 @@ pcicfg_setup_bridge(pcicfg_phdl_t *entry, * first Configuration access. The worst case is 33MHz, which * is a 1 second wait. */ - drv_usecwait(1000000); + drv_usecwait(pcicfg_sec_reset_delay); } @@ -3211,30 +3467,80 @@ pcicfg_update_bridge(pcicfg_phdl_t *entry, } } +/*ARGSUSED*/ +static void +pcicfg_disable_bridge_probe_err(dev_info_t *dip, ddi_acc_handle_t h, + pcicfg_err_regs_t *regs) +{ + uint16_t val; + + /* disable SERR generated in the context of Master Aborts. */ + regs->cmd = val = pci_config_get16(h, PCI_CONF_COMM); + val &= ~PCI_COMM_SERR_ENABLE; + pci_config_put16(h, PCI_CONF_COMM, val); + regs->bcntl = val = pci_config_get16(h, PCI_BCNF_BCNTRL); + val &= ~PCI_BCNF_BCNTRL_SERR_ENABLE; + pci_config_put16(h, PCI_BCNF_BCNTRL, val); + /* clear any current pending errors */ + pci_config_put16(h, PCI_CONF_STAT, PCI_STAT_S_TARG_AB| + PCI_STAT_R_TARG_AB | PCI_STAT_R_MAST_AB | PCI_STAT_S_SYSERR); + pci_config_put16(h, PCI_BCNF_SEC_STATUS, PCI_STAT_S_TARG_AB| + PCI_STAT_R_TARG_AB | PCI_STAT_R_MAST_AB | PCI_STAT_S_SYSERR); + /* if we are a PCIe device, disable the generation of UR, CE and NFE */ + if (regs->pcie_dev) { + uint16_t devctl; + int off = pcicfg_get_cap(h, PCI_CAP_ID_PCI_E); + + regs->pcie_cap_off = off; + regs->devctl = devctl = pci_config_get16(h, off + PCIE_DEVCTL); + devctl &= ~(PCIE_DEVCTL_UR_REPORTING_EN | + PCIE_DEVCTL_CE_REPORTING_EN | + PCIE_DEVCTL_NFE_REPORTING_EN | + PCIE_DEVCTL_FE_REPORTING_EN); + pci_config_put16(h, off + PCIE_DEVCTL, devctl); + } +} + +/*ARGSUSED*/ +static void +pcicfg_enable_bridge_probe_err(dev_info_t *dip, ddi_acc_handle_t h, + pcicfg_err_regs_t *regs) +{ + /* clear any pending errors */ + pci_config_put16(h, PCI_CONF_STAT, PCI_STAT_S_TARG_AB| + PCI_STAT_R_TARG_AB | PCI_STAT_R_MAST_AB | PCI_STAT_S_SYSERR); + pci_config_put16(h, PCI_BCNF_SEC_STATUS, PCI_STAT_S_TARG_AB| + PCI_STAT_R_TARG_AB | PCI_STAT_R_MAST_AB | PCI_STAT_S_SYSERR); + + /* restore original settings */ + if (regs->pcie_dev) { + pcie_clear_errors(dip, h); + pci_config_put16(h, regs->pcie_cap_off + PCIE_DEVCTL, + regs->devctl); + } + + pci_config_put16(h, PCI_BCNF_BCNTRL, regs->bcntl); + pci_config_put16(h, PCI_CONF_COMM, regs->cmd); + +} + static int pcicfg_probe_children(dev_info_t *parent, uint_t bus, - uint_t device, uint_t func) + uint_t device, uint_t func, uint_t *highest_bus) { dev_info_t *new_child; ddi_acc_handle_t config_handle; - uint8_t header_type; + uint8_t header_type, pcie_dev = 0; - int i, j; - ndi_ra_request_t req; - uint64_t next_bus; - uint64_t blen; + int i; uint32_t request; - uint_t new_bus; int ret; - int circ; - + pcicfg_err_regs_t regs; /* * This node will be put immediately below * "parent". Allocate a blank device node. It will either * be filled in or freed up based on further probing. */ - - ndi_devi_enter(parent, &circ); /* * Note: in usr/src/uts/common/io/hotplug/pcicfg/pcicfg.c * ndi_devi_alloc() is called as ndi_devi_alloc_sleep() @@ -3243,7 +3549,6 @@ pcicfg_probe_children(dev_info_t *parent, uint_t bus, (pnode_t)DEVI_SID_NODEID, &new_child) != NDI_SUCCESS) { DEBUG0("pcicfg_probe_children(): Failed to alloc child node\n"); - ndi_devi_exit(parent, circ); return (PCICFG_FAILURE); } @@ -3258,7 +3563,6 @@ pcicfg_probe_children(dev_info_t *parent, uint_t bus, != PCICFG_SUCCESS) { if (ret == PCICFG_NODEVICE) { (void) ndi_devi_free(new_child); - ndi_devi_exit(parent, circ); return (ret); } DEBUG0("pcicfg_probe_children():" @@ -3273,12 +3577,18 @@ pcicfg_probe_children(dev_info_t *parent, uint_t bus, */ (void) pcicfg_device_off(config_handle); + /* check if we are PCIe device */ + if (pcicfg_pcie_dev(new_child, PCICFG_DEVICE_TYPE_PCIE, ®s) + == DDI_SUCCESS) { + DEBUG0("PCIe device detected\n"); + pcie_dev = 1; + } /* * Set 1275 properties common to all devices */ - if (pcicfg_set_standard_props(new_child, - config_handle) != PCICFG_SUCCESS) { + if (pcicfg_set_standard_props(new_child, config_handle, + pcie_dev) != PCICFG_SUCCESS) { DEBUG0("Failed to set standard properties\n"); goto failedchild; } @@ -3286,8 +3596,8 @@ pcicfg_probe_children(dev_info_t *parent, uint_t bus, /* * Child node properties NOTE: Both for PCI-PCI bridge and child node */ - if (pcicfg_set_childnode_props(new_child, - config_handle) != PCICFG_SUCCESS) { + if (pcicfg_set_childnode_props(new_child, config_handle, + pcie_dev) != PCICFG_SUCCESS) { goto failedchild; } @@ -3296,11 +3606,10 @@ pcicfg_probe_children(dev_info_t *parent, uint_t bus, /* * If this is not a multi-function card only probe function zero. */ - if (!(header_type & PCI_HEADER_MULTI) && (func != 0)) { + if ((!(header_type & PCI_HEADER_MULTI)) && (func != 0)) { (void) pcicfg_config_teardown(&config_handle); (void) ndi_devi_free(new_child); - ndi_devi_exit(parent, circ); return (PCICFG_NODEVICE); } @@ -3309,54 +3618,22 @@ pcicfg_probe_children(dev_info_t *parent, uint_t bus, DEBUG1("---Device ID = [0x%x]\n", pci_config_get16(config_handle, PCI_CONF_DEVID)); + /* + * Attach the child to its parent + */ + (void) i_ndi_config_node(new_child, DS_LINKED, 0); + if ((header_type & PCI_HEADER_TYPE_M) == PCI_HEADER_PPB) { DEBUG3("--Bridge found bus [0x%x] device" "[0x%x] func [0x%x]\n", bus, device, func); - /* - * Get next bus in sequence and program device. - * XXX There might have to be slot specific - * ranges taken care of here. - */ - bzero((caddr_t)&req, sizeof (ndi_ra_request_t)); - req.ra_len = 1; - if (ndi_ra_alloc(ddi_get_parent(new_child), &req, - &next_bus, &blen, NDI_RA_TYPE_PCI_BUSNUM, - NDI_RA_PASS) != NDI_SUCCESS) { - DEBUG0("Failed to get a bus number\n"); - goto failedchild; - } - new_bus = next_bus; - - DEBUG1("NEW bus found ->[%d]\n", new_bus); - - (void) pcicfg_set_bus_numbers(config_handle, - bus, new_bus, new_bus); - /* - * Set bus properties - */ - if (pcicfg_set_busnode_props(new_child) != PCICFG_SUCCESS) { - DEBUG0("Failed to set busnode props\n"); + if (pcicfg_probe_bridge(new_child, config_handle, + bus, highest_bus) != PCICFG_SUCCESS) { + (void) pcicfg_free_bridge_resources(new_child); goto failedchild; } - /* - * Probe all children devices - */ - for (i = 0; i < PCICFG_MAX_DEVICE; i++) { - for (j = 0; j < PCICFG_MAX_FUNCTION; j++) { - if (pcicfg_probe_children(new_child, - new_bus, i, j) == - PCICFG_FAILURE) { - DEBUG3("Failed to configure bus " - "[0x%x] device [0x%x] func [0x%x]\n", - new_bus, i, j); - goto failedchild; - } - } - } - } else { DEBUG3("--Leaf device found bus [0x%x] device" @@ -3430,16 +3707,16 @@ pcicfg_probe_children(dev_info_t *parent, uint_t bus, goto failedchild; } } - } - /* - * Attach the child to its parent - */ - (void) ndi_devi_bind_driver(new_child, 0); + /* now allocate & program the resources */ + if (pcicfg_device_assign(new_child) != PCICFG_SUCCESS) { + (void) pcicfg_free_device_resources(new_child); + goto failedchild; + } + (void) ndi_devi_bind_driver(new_child, 0); + } (void) pcicfg_config_teardown(&config_handle); - ndi_devi_exit(parent, circ); - return (PCICFG_SUCCESS); failedchild: @@ -3449,8 +3726,6 @@ failedchild: failedconfig: (void) ndi_devi_free(new_child); - ndi_devi_exit(parent, circ); - return (PCICFG_FAILURE); } @@ -3461,10 +3736,10 @@ pcicfg_fcode_probe(dev_info_t *parent, uint_t bus, dev_info_t *new_child; int8_t header_type; int ret; - ddi_acc_handle_t h; + ddi_acc_handle_t h, ph; int error = 0; - int rval; extern int pcicfg_dont_interpret; + pcicfg_err_regs_t parent_regs, regs; #ifdef PCICFG_INTERPRET_FCODE struct pci_ops_bus_args po; fco_handle_t c; @@ -3479,6 +3754,22 @@ pcicfg_fcode_probe(dev_info_t *parent, uint_t bus, #endif /* + * check if our parent is of type pciex. + * if so, program config space to disable error msgs during probe. + */ + if (pcicfg_pcie_dev(parent, PCICFG_DEVICE_TYPE_PCIE, &parent_regs) + == DDI_SUCCESS) { + DEBUG0("PCI/PCIe parent detected. Disable errors.\n"); + /* + * disable parent generating URs or SERR#s during probing + * alone. + */ + if (pci_config_setup(parent, &ph) != DDI_SUCCESS) + return (DDI_FAILURE); + pcicfg_disable_bridge_probe_err(parent, ph, &parent_regs); + } + + /* * This node will be put immediately below * "parent". Allocate a blank device node. It will either * be filled in or freed up based on further probing. @@ -3488,7 +3779,9 @@ pcicfg_fcode_probe(dev_info_t *parent, uint_t bus, (pnode_t)DEVI_SID_NODEID, &new_child) != NDI_SUCCESS) { DEBUG0("pcicfg_fcode_probe(): Failed to alloc child node\n"); - return (PCICFG_FAILURE); + /* return (PCICFG_FAILURE); */ + ret = PCICFG_FAILURE; + goto failed2; } /* @@ -3497,21 +3790,19 @@ pcicfg_fcode_probe(dev_info_t *parent, uint_t bus, * produce one by hand. */ if (pcicfg_add_config_reg(new_child, bus, - device, func) != DDI_SUCCESS) { - - (void) ndi_devi_free(new_child); - return (PCICFG_FAILURE); + device, func) != DDI_SUCCESS) { + ret = PCICFG_FAILURE; + goto failed3; } #ifdef EFCODE21554 if ((ret = pcicfg_config_setup(new_child, &h)) != PCICFG_SUCCESS) { DEBUG0("pcicfg_fcode_probe():" "Failed to setup config space\n"); - (void) ndi_devi_free(new_child); - return (ret); + ret = PCICFG_NODEVICE; + goto failed3; } - DEBUG0("fcode_probe: conf space mapped.\n"); #else p.pci_phys_hi = PCICFG_MAKE_REG_HIGH(bus, device, func, 0); p.pci_phys_mid = p.pci_phys_low = 0; @@ -3526,11 +3817,9 @@ pcicfg_fcode_probe(dev_info_t *parent, uint_t bus, if (pcicfg_map_phys(new_child, &p, &virt, &acc, &h)) { DEBUG0("pcicfg_fcode_probe():" - "Failed to setup config space\n"); - - (void) ndi_devi_free(new_child); - - return (PCICFG_FAILURE); + "Failed to setup config space\n"); + ret = PCICFG_NODEVICE; + goto failed3; } /* @@ -3541,10 +3830,11 @@ pcicfg_fcode_probe(dev_info_t *parent, uint_t bus, if (ddi_peek16(new_child, (int16_t *)v, &vendor_id)) { DEBUG0("Can not read Vendor ID"); pcicfg_unmap_phys(&h, &p); - (void) ndi_devi_free(new_child); - return (PCICFG_NODEVICE); + ret = PCICFG_NODEVICE; + goto failed3; } #endif + DEBUG0("fcode_probe: conf space mapped.\n"); /* * As soon as we have access to config space, * turn off device. It will get turned on @@ -3552,11 +3842,18 @@ pcicfg_fcode_probe(dev_info_t *parent, uint_t bus, */ (void) pcicfg_device_off(h); + /* check if we are PCIe device */ + if (pcicfg_pcie_dev(new_child, PCICFG_DEVICE_TYPE_PCIE, ®s) + == DDI_SUCCESS) { + /*EMPTY*/ + DEBUG0("PCI/PCIe device detected\n"); + } + /* * Set 1275 properties common to all devices */ if (pcicfg_set_standard_props(new_child, - h) != PCICFG_SUCCESS) { + h, regs.pcie_dev) != PCICFG_SUCCESS) { DEBUG0("Failed to set standard properties\n"); goto failed; } @@ -3565,7 +3862,8 @@ pcicfg_fcode_probe(dev_info_t *parent, uint_t bus, * Child node properties NOTE: Both for PCI-PCI bridge and child node */ if (pcicfg_set_childnode_props(new_child, - h) != PCICFG_SUCCESS) { + h, regs.pcie_dev) != PCICFG_SUCCESS) { + ret = PCICFG_FAILURE; goto failed; } @@ -3576,13 +3874,8 @@ pcicfg_fcode_probe(dev_info_t *parent, uint_t bus, */ if (!(header_type & PCI_HEADER_MULTI) && (func > 0)) { -#ifdef EFCODE21554 - pcicfg_config_teardown(&h); -#else - pcicfg_unmap_phys(&h, &p); -#endif - (void) ndi_devi_free(new_child); - return (PCICFG_NODEVICE); + ret = PCICFG_NODEVICE; + goto failed; } if ((header_type & PCI_HEADER_TYPE_M) == PCI_HEADER_PPB) { @@ -3600,17 +3893,10 @@ pcicfg_fcode_probe(dev_info_t *parent, uint_t bus, DEBUG3("--Bridge found bus [0x%x] device" "[0x%x] func [0x%x]\n", bus, device, func); - rval = pcicfg_probe_bridge(new_child, h, bus, highest_bus); - -#ifdef EFCODE21554 - pcicfg_config_teardown(&h); -#else - pcicfg_unmap_phys(&h, &p); -#endif - if (rval != PCICFG_SUCCESS) - (void) ndi_devi_free(new_child); - - return (rval); + if ((ret = pcicfg_probe_bridge(new_child, h, + bus, highest_bus)) != PCICFG_SUCCESS) + (void) pcicfg_free_bridge_resources(new_child); + goto done; } else { DEBUG3("--Leaf device found bus [0x%x] device" @@ -3660,6 +3946,7 @@ pcicfg_fcode_probe(dev_info_t *parent, uint_t bus, != PCICFG_SUCCESS) { DEBUG0("Failed to assign addresses to " "implemented BARs"); + ret = PCICFG_FAILURE; goto failed; } @@ -3695,8 +3982,8 @@ pcicfg_fcode_probe(dev_info_t *parent, uint_t bus, != DDI_SUCCESS) { DEBUG0("Failed to create " "fcode-rom-offset property\n"); - (void) ndi_devi_free(new_child); - return (PCICFG_FAILURE); + ret = PCICFG_FAILURE; + goto failed; } } else { DEBUG0("There is no Expansion ROM\n"); @@ -3769,32 +4056,24 @@ pcicfg_fcode_probe(dev_info_t *parent, uint_t bus, */ if (pcicfg_alloc_new_resources(new_child) == PCICFG_FAILURE) { -#ifdef EFCODE21554 - pcicfg_config_teardown(&h); -#else - pcicfg_unmap_phys(&h, &p); -#endif - (void) ndi_devi_free(new_child); - return (PCICFG_FAILURE); + ret = PCICFG_FAILURE; + goto failed; } -#ifdef EFCODE21554 - pcicfg_config_teardown(&h); -#else - pcicfg_unmap_phys(&h, &p); -#endif + ret = PCICFG_SUCCESS; /* no fcode, bind driver now */ (void) ndi_devi_bind_driver(new_child, 0); - return (PCICFG_SUCCESS); + goto done; } else if ((error != FC_NO_FCODE) && - (pcicfg_dont_interpret == 0)) { + (pcicfg_dont_interpret == 0)) { /* * The interpreter located fcode, but had an error in * processing. Cleanup and fail the operation. */ DEBUG0("Interpreter detected fcode failure\n"); (void) pcicfg_free_resources(new_child); + ret = PCICFG_FAILURE; goto failed; } else { /* @@ -3822,11 +4101,12 @@ pcicfg_fcode_probe(dev_info_t *parent, uint_t bus, DEBUG0("No Drop-in Probe device ourself\n"); - ret = pcicfg_probe_children(parent, bus, device, func); + ret = pcicfg_probe_children(parent, bus, device, func, + highest_bus); if (ret != PCICFG_SUCCESS) { DEBUG0("Could not self probe child\n"); - return (ret); + goto failed2; } /* @@ -3836,13 +4116,8 @@ pcicfg_fcode_probe(dev_info_t *parent, uint_t bus, parent, device, func)) == NULL) { DEBUG0("Did'nt find device node " "just created\n"); - return (PCICFG_FAILURE); - } - if (pcicfg_program_ap(new_child) - == PCICFG_FAILURE) { - DEBUG0("Failed to program devices\n"); - (void) ndi_devi_free(new_child); - return (PCICFG_FAILURE); + ret = PCICFG_FAILURE; + goto failed2; } #ifdef EFCODE21554 /* @@ -3883,19 +4158,26 @@ pcicfg_fcode_probe(dev_info_t *parent, uint_t bus, (void) pcicfg_teardown_device(new_child); } #endif - - return (ret); + goto done2; } } +done: failed: #ifdef EFCODE21554 pcicfg_config_teardown(&h); #else pcicfg_unmap_phys(&h, &p); #endif - (void) ndi_devi_free(new_child); - - return (PCICFG_FAILURE); +failed3: + if (ret != PCICFG_SUCCESS) + (void) ndi_devi_free(new_child); +done2: +failed2: + if (parent_regs.pcie_dev) { + pcicfg_enable_bridge_probe_err(parent, ph, &parent_regs); + pci_config_teardown(&ph); + } + return (ret); } static int @@ -3903,12 +4185,11 @@ pcicfg_probe_bridge(dev_info_t *new_child, ddi_acc_handle_t h, uint_t bus, uint_t *highest_bus) { uint64_t next_bus; - uint_t new_bus; + uint_t new_bus, num_slots; ndi_ra_request_t req; int rval, i, j; - uint64_t mem_answer, io_answer, mem_base, io_base, mem_alen, io_alen; - uint64_t mem_size, io_size; - uint64_t mem_end, io_end; + uint64_t mem_answer, mem_base, mem_alen, mem_size, mem_end; + uint64_t io_answer, io_base, io_alen, io_size, io_end; uint64_t round_answer, round_len; pcicfg_range_t range[PCICFG_RANGE_LEN]; int bus_range[2]; @@ -3916,6 +4197,7 @@ pcicfg_probe_bridge(dev_info_t *new_child, ddi_acc_handle_t h, uint_t bus, int count; uint64_t pcibus_base, pcibus_alen; uint64_t max_bus; + uint8_t pcie_device_type = 0; bzero((caddr_t)&req, sizeof (ndi_ra_request_t)); req.ra_flags = (NDI_RA_ALLOC_BOUNDED | NDI_RA_ALLOC_PARTIAL_OK); @@ -3929,6 +4211,7 @@ pcicfg_probe_bridge(dev_info_t *new_child, ddi_acc_handle_t h, uint_t bus, if (rval != NDI_SUCCESS) { if (rval == NDI_RA_PARTIAL_REQ) { + /*EMPTY*/ DEBUG0("NDI_RA_PARTIAL_REQ returned for bus range\n"); } else { DEBUG0( @@ -3970,6 +4253,10 @@ pcicfg_probe_bridge(dev_info_t *new_child, ddi_acc_handle_t h, uint_t bus, bzero((caddr_t)&req, sizeof (ndi_ra_request_t)); req.ra_flags = (NDI_RA_ALLOC_BOUNDED | NDI_RA_ALLOC_PARTIAL_OK); req.ra_boundbase = 0; + /* + * Note: To support a 32b system, boundlen and len need to be + * 32b quantities + */ req.ra_boundlen = PCICFG_4GIG_LIMIT + 1; req.ra_len = PCICFG_4GIG_LIMIT + 1; /* Get as big as possible */ req.ra_align_mask = @@ -3980,6 +4267,7 @@ pcicfg_probe_bridge(dev_info_t *new_child, ddi_acc_handle_t h, uint_t bus, if (rval != NDI_SUCCESS) { if (rval == NDI_RA_PARTIAL_REQ) { + /*EMPTY*/ DEBUG0("NDI_RA_PARTIAL_REQ returned\n"); } else { DEBUG0( @@ -4021,28 +4309,33 @@ pcicfg_probe_bridge(dev_info_t *new_child, ddi_acc_handle_t h, uint_t bus, if (rval != NDI_SUCCESS) { if (rval == NDI_RA_PARTIAL_REQ) { + /*EMPTY*/ DEBUG0("NDI_RA_PARTIAL_REQ returned\n"); } else { DEBUG0("Failed to allocate io space for bridge\n"); - return (PCICFG_FAILURE); + io_base = io_answer = io_alen = 0; + /* return (PCICFG_FAILURE); */ } } - DEBUG3("Bridge IO Space Allocated [0x%x.%x] len [0x%x]\n", - PCICFG_HIADDR(io_answer), PCICFG_LOADDR(io_answer), io_alen); + if (io_alen) { + DEBUG3("Bridge IO Space Allocated [0x%x.%x] len [0x%x]\n", + PCICFG_HIADDR(io_answer), PCICFG_LOADDR(io_answer), + io_alen); - if (ndi_ra_map_setup(new_child, NDI_RA_TYPE_IO) == NDI_FAILURE) { - DEBUG0("Can not setup resource map - NDI_RA_TYPE_IO\n"); - return (PCICFG_FAILURE); - } - - /* - * Put available I/O into the pool. - */ - (void) ndi_ra_free(new_child, io_answer, io_alen, NDI_RA_TYPE_IO, - NDI_RA_PASS); + if (ndi_ra_map_setup(new_child, NDI_RA_TYPE_IO) == + NDI_FAILURE) { + DEBUG0("Can not setup resource map - NDI_RA_TYPE_IO\n"); + return (PCICFG_FAILURE); + } - io_base = io_answer; + /* + * Put available I/O into the pool. + */ + (void) ndi_ra_free(new_child, io_answer, io_alen, + NDI_RA_TYPE_IO, NDI_RA_PASS); + io_base = io_answer; + } (void) pcicfg_set_bus_numbers(h, bus, new_bus, max_bus); @@ -4151,10 +4444,15 @@ pcicfg_probe_bridge(dev_info_t *new_child, ddi_acc_handle_t h, uint_t bus, */ pci_config_put8(h, PCI_CONF_ILINE, 0xf); + /* check our device_type as defined by Open Firmware */ + if (pcicfg_pcie_device_type(new_child, h) == DDI_SUCCESS) + pcie_device_type = 1; + /* * Set bus properties */ - if (pcicfg_set_busnode_props(new_child) != PCICFG_SUCCESS) { + if (pcicfg_set_busnode_props(new_child, pcie_device_type) + != PCICFG_SUCCESS) { DEBUG0("Failed to set busnode props\n"); return (PCICFG_FAILURE); } @@ -4174,7 +4472,7 @@ pcicfg_probe_bridge(dev_info_t *new_child, ddi_acc_handle_t h, uint_t bus, * first Configuration access. The worst case is 33MHz, which * is a 1 second wait. */ - drv_usecwait(1000000); + drv_usecwait(pcicfg_sec_reset_delay); /* * Probe all children devices @@ -4183,25 +4481,45 @@ pcicfg_probe_bridge(dev_info_t *new_child, ddi_acc_handle_t h, uint_t bus, ndi_devi_enter(new_child, &count); for (i = 0; i < PCICFG_MAX_DEVICE; i++) { for (j = 0; j < PCICFG_MAX_FUNCTION; j++) { - if (pcicfg_fcode_probe(new_child, - new_bus, i, j, highest_bus) != PCICFG_SUCCESS) { - DEBUG3("Failed to configure bus " - "[0x%x] device [0x%x] func [0x%x]\n", - new_bus, i, j); + if ((rval = pcicfg_fcode_probe(new_child, + new_bus, i, j, highest_bus)) + != PCICFG_SUCCESS) { + if (rval == PCICFG_NODEVICE) { + DEBUG3("No Device at bus [0x%x]" + "device [0x%x] " + "func [0x%x]\n", new_bus, i, j); + if (j) + continue; + } else { + DEBUG3("Failed to configure bus " + "[0x%x] device [0x%x] " + "func [0x%x]\n", new_bus, i, j); + rval = PCICFG_FAILURE; + } + break; } } + /* if any function fails to be configured, no need to proceed */ + if (rval != PCICFG_NODEVICE) { + break; + } } ndi_devi_exit(new_child, count); + /* if empty topology underneath, it is still a success. */ + if (rval != PCICFG_FAILURE) + rval = PCICFG_SUCCESS; + /* * Offline the bridge to allow reprogramming of resources. */ (void) ndi_devi_offline(new_child, NDI_NO_EVENT|NDI_UNCONFIG); phdl.dip = new_child; - phdl.memory_base = 0; - phdl.io_base = 0; + phdl.memory_base = mem_answer; + phdl.io_base = (uint32_t)io_answer; + phdl.error = PCICFG_SUCCESS; /* in case of empty child tree */ ndi_devi_enter(ddi_get_parent(new_child), &count); ddi_walk_devs(new_child, pcicfg_find_resource_end, (void *)&phdl); @@ -4212,22 +4530,71 @@ pcicfg_probe_bridge(dev_info_t *new_child, ddi_acc_handle_t h, uint_t bus, return (PCICFG_FAILURE); } + num_slots = pcicfg_get_nslots(new_child, h); mem_end = PCICFG_ROUND_UP(phdl.memory_base, PCICFG_MEMGRAN); io_end = PCICFG_ROUND_UP(phdl.io_base, PCICFG_IOGRAN); - DEBUG2("Start of Unallocated Bridge Resources Mem=0x%lx I/O=0x%lx\n", - mem_end, io_end); - - - if (mem_end == 0) { - /* - * Give back all memory space back to parent. - */ - (void) ndi_ra_free(ddi_get_parent(new_child), - mem_answer, mem_alen, NDI_RA_TYPE_MEM, NDI_RA_PASS); - + DEBUG3("Start of Unallocated Bridge(%d slots) Resources " + "Mem=0x%lx I/O=0x%lx\n", num_slots, mem_end, io_end); + + /* + * if the bridge a slots, then preallocate. If not, assume static + * configuration. Also check for preallocation limits and spit + * warning messages appropriately (perhaps some can be in debug mode). + */ + if (num_slots) { + uint64_t mem_reqd = mem_answer + (num_slots * + pcicfg_slot_memsize); + uint64_t io_reqd = io_answer + (num_slots * + pcicfg_slot_iosize); + uint8_t highest_bus_reqd = new_bus + (num_slots * + pcicfg_slot_busnums); + + if (mem_end > mem_reqd) + cmn_err(CE_WARN, "Memory space consumed by bridge" + " more than planned for %d slot(s)(%lx, %lx)", + num_slots, mem_answer, mem_end); + if (io_end > io_reqd) + cmn_err(CE_WARN, "IO space consumed by bridge" + " more than planned for %d slot(s)(%lx, %lx)", + num_slots, io_answer, io_end); + if (*highest_bus > highest_bus_reqd) + cmn_err(CE_WARN, "Buses consumed by bridge" + " more than planned for %d slot(s)(%x, %x)", + num_slots, new_bus, *highest_bus); + + if (mem_reqd > (mem_answer + mem_alen)) + cmn_err(CE_WARN, "Memory space required by bridge" + " more than available for %d slot(s)(%lx, %lx)", + num_slots, mem_answer, mem_end); + + if (io_reqd > (io_answer + io_alen)) + cmn_err(CE_WARN, "IO space required by bridge" + " more than available for %d slot(s)(%lx, %lx)", + num_slots, io_answer, io_end); + if (highest_bus_reqd > max_bus) + cmn_err(CE_WARN, "Bus numbers required by bridge" + " more than available for %d slot(s)(%x, %x)", + num_slots, new_bus, *highest_bus); + + mem_end = MAX((MIN(mem_reqd, (mem_answer + mem_alen))), + mem_end); + io_end = MAX((MIN(io_reqd, (io_answer + io_alen))), io_end); + *highest_bus = MAX((MIN(highest_bus_reqd, max_bus)), + *highest_bus); + DEBUG3("mem_end %lx, io_end %lx, highest_bus %x\n", + mem_end, io_end, *highest_bus); + } + + /* + * Give back unused memory space to parent. + */ + (void) ndi_ra_free(ddi_get_parent(new_child), + mem_end, (mem_answer + mem_alen) - mem_end, NDI_RA_TYPE_MEM, + NDI_RA_PASS); + + if (mem_end == mem_answer) { DEBUG0("No memory resources used\n"); - /* * To prevent the bridge from forwarding any Memory * transactions, the Memory Limit will be programmed @@ -4239,12 +4606,6 @@ pcicfg_probe_bridge(dev_info_t *new_child, ddi_acc_handle_t h, uint_t bus, mem_size = 0; } else { /* - * Give back the unused memory space to the parent. - */ - (void) ndi_ra_free(ddi_get_parent(new_child), - mem_end, (mem_answer + mem_alen) - mem_end, NDI_RA_TYPE_MEM, - NDI_RA_PASS); - /* * Reprogram the end of the memory. */ pci_config_put16(h, PCI_BCNF_MEM_LIMIT, @@ -4252,14 +4613,14 @@ pcicfg_probe_bridge(dev_info_t *new_child, ddi_acc_handle_t h, uint_t bus, mem_size = mem_end - mem_base; } + /* + * Give back unused io space to parent. + */ + (void) ndi_ra_free(ddi_get_parent(new_child), + io_end, (io_answer + io_alen) - io_end, + NDI_RA_TYPE_IO, NDI_RA_PASS); - if (io_end == 0) { - /* - * Give back all io space back to parent. - */ - (void) ndi_ra_free(ddi_get_parent(new_child), - io_answer, io_alen, NDI_RA_TYPE_IO, NDI_RA_PASS); - + if (io_end == io_answer) { DEBUG0("No IO Space resources used\n"); /* @@ -4275,13 +4636,6 @@ pcicfg_probe_bridge(dev_info_t *new_child, ddi_acc_handle_t h, uint_t bus, io_size = 0; } else { /* - * Give back the unused io space to the parent. - */ - (void) ndi_ra_free(ddi_get_parent(new_child), - io_end, (io_answer + io_alen) - io_end, - NDI_RA_TYPE_IO, NDI_RA_PASS); - - /* * Reprogram the end of the io space. */ pci_config_put8(h, PCI_BCNF_IO_LIMIT_LOW, @@ -4354,25 +4708,29 @@ pcicfg_probe_bridge(dev_info_t *new_child, ddi_acc_handle_t h, uint_t bus, } /* * Remove the resource maps for the bridge since we no longer - * need them. + * need them. Note that the failure is ignored since the + * ndi_devi_offline above may have already taken care of it via + * driver detach. + * It has been checked that there are no other reasons for + * failure other than map itself being non-existent. So we are Ok. */ if (ndi_ra_map_destroy(new_child, NDI_RA_TYPE_MEM) == NDI_FAILURE) { + /*EMPTY*/ DEBUG0("Can not destroy resource map - NDI_RA_TYPE_MEM\n"); - return (PCICFG_FAILURE); } if (ndi_ra_map_destroy(new_child, NDI_RA_TYPE_IO) == NDI_FAILURE) { + /*EMPTY*/ DEBUG0("Can not destroy resource map - NDI_RA_TYPE_IO\n"); - return (PCICFG_FAILURE); } if (ndi_ra_map_destroy(new_child, NDI_RA_TYPE_PCI_BUSNUM) == NDI_FAILURE) { + /*EMPTY*/ DEBUG0("Can't destroy resource map - NDI_RA_TYPE_PCI_BUSNUM\n"); - return (PCICFG_FAILURE); } - return (PCICFG_SUCCESS); + return (rval); } /* @@ -4395,6 +4753,7 @@ pcicfg_config_setup(dev_info_t *dip, ddi_acc_handle_t *handle) * mapped, otherwise it is 0. "flags" is introduced in support of any * non transparent bridges, where configuration space is indirectly * mapped. + * Indirect mapping is always true on sun4v systems. */ int flags = 0; @@ -4425,6 +4784,7 @@ pcicfg_config_setup(dev_info_t *dip, ddi_acc_handle_t *handle) attr.devacc_attr_version = DDI_DEVICE_ATTR_V0; attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC; attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC; + attr.devacc_attr_access = DDI_CAUTIOUS_ACC; #ifdef EFCODE21554 if (ddi_regs_map_setup(dip, 0, &virt, @@ -4452,12 +4812,18 @@ pcicfg_config_setup(dev_info_t *dip, ddi_acc_handle_t *handle) } if (ret == DDI_SUCCESS) { - if ((tmp == (int16_t)0xffff) || (tmp == -1)) { + if (tmp == -1) { DEBUG1("NO DEVICEFOUND, read %x\n", tmp); ret = PCICFG_NODEVICE; } else { - DEBUG1("DEVICEFOUND, read %x\n", tmp); - ret = PCICFG_SUCCESS; + /* XXX - Need to check why HV is returning 0 */ + if (tmp == 0) { + DEBUG0("Device Not Ready yet ?"); + ret = PCICFG_NODEVICE; + } else { + DEBUG1("DEVICEFOUND, read %x\n", tmp); + ret = PCICFG_SUCCESS; + } } } else { DEBUG0("ddi_peek failed, must be NODEVICE\n"); diff --git a/usr/src/uts/sun4/io/px/px.c b/usr/src/uts/sun4/io/px/px.c index d8337a931d..4ce07e4620 100644 --- a/usr/src/uts/sun4/io/px/px.c +++ b/usr/src/uts/sun4/io/px/px.c @@ -20,7 +20,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -42,6 +42,8 @@ #include <sys/spl.h> #include <sys/epm.h> #include <sys/iommutsb.h> +#include <sys/hotplug/pci/pcihp.h> +#include <sys/hotplug/pci/pciehpc.h> #include "px_obj.h" #include <sys/pci_tools.h> #include "px_tools_ext.h" @@ -60,6 +62,12 @@ static int px_pwr_setup(dev_info_t *dip); static void px_pwr_teardown(dev_info_t *dip); /* + * function prototypes for hotplug routines: + */ +static uint_t px_init_hotplug(px_t *px_p); +static uint_t px_uninit_hotplug(dev_info_t *dip); + +/* * bus ops and dev ops structures: */ static struct bus_ops px_bus_ops = { @@ -181,14 +189,12 @@ px_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) int instance = getminor((dev_t)arg); px_t *px_p = INST_TO_STATE(instance); -#ifdef HOTPLUG /* * Allow hotplug to deal with ones it manages * Hot Plug will be done later. */ - if (px_p && (px_p->hotplug_capable == B_TRUE)) + if (px_p && (px_p->px_dev_caps & PX_HOTPLUG_CAPABLE)) return (pcihp_info(dip, infocmd, arg, result)); -#endif /* HOTPLUG */ /* non-hotplug or not attached */ switch (infocmd) { @@ -239,6 +245,8 @@ px_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) px_p->px_soft_state = PX_SOFT_STATE_CLOSED; px_p->px_open_count = 0; + (void) ddi_prop_update_string(DDI_DEV_T_NONE, dip, + "device_type", "pciex"); /* * Get key properties of the pci bridge node and * determine it's type (psycho, schizo, etc ...). @@ -293,6 +301,8 @@ px_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) if ((ret = px_err_add_intr(&px_p->px_fault)) != DDI_SUCCESS) goto err_bad_pec_add_intr; + (void) px_init_hotplug(px_p); + /* * Create the "devctl" node for hotplug and pcitool support. * For non-hotplug bus, we still need ":devctl" to @@ -420,17 +430,11 @@ px_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) */ px_cpr_rem_callb(px_p); -#ifdef HOTPLUG - /* - * Hot plug will be done later. - */ - if (px_p->hotplug_capable == B_TRUE) { - if (pxhp_uninit(dip) == DDI_FAILURE) { + if (px_p->px_dev_caps & PX_HOTPLUG_CAPABLE) + if (px_uninit_hotplug(dip) != DDI_SUCCESS) { mutex_exit(&px_p->px_mutex); return (DDI_FAILURE); } - } -#endif /* HOTPLUG */ /* * things which used to be done in obj_destroy @@ -530,6 +534,8 @@ px_pwr_setup(dev_info_t *dip) DDI_INTR_PRI(px_pwr_pil)); cv_init(&px_p->px_l23ready_cv, NULL, CV_DRIVER, NULL); + + /* Initilize handle */ hdl.ih_cb_arg1 = px_p; hdl.ih_cb_arg2 = NULL; @@ -1304,3 +1310,78 @@ px_intr_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op, return (ret); } + +static uint_t +px_init_hotplug(px_t *px_p) +{ + px_bus_range_t bus_range; + dev_info_t *dip; + pciehpc_regops_t regops; + + dip = px_p->px_dip; + + if (ddi_prop_exists(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, + "hotplug-capable") == 0) + return (DDI_FAILURE); + + /* + * Before initializing hotplug - open up bus range. The busra + * module will initialize its pool of bus numbers from this. + * "busra" will be the agent that keeps track of them during + * hotplug. Also, note, that busra will remove any bus numbers + * already in use from boot time. + */ + if (ddi_prop_exists(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, + "bus-range") == 0) { + cmn_err(CE_WARN, "%s%d: bus-range not found\n", + ddi_driver_name(dip), ddi_get_instance(dip)); +#ifdef DEBUG + bus_range.lo = 0x0; + bus_range.hi = 0xff; + + if (ndi_prop_update_int_array(DDI_DEV_T_NONE, + dip, "bus-range", (int *)&bus_range, 2) + != DDI_PROP_SUCCESS) { + return (DDI_FAILURE); + } +#else + return (DDI_FAILURE); +#endif + } + + if (px_lib_hotplug_init(dip, (void *)®ops) != DDI_SUCCESS) + return (DDI_FAILURE); + + if (pciehpc_init(dip, ®ops) != DDI_SUCCESS) { + px_lib_hotplug_uninit(dip); + return (DDI_FAILURE); + } + + if (pcihp_init(dip) != DDI_SUCCESS) { + (void) pciehpc_uninit(dip); + px_lib_hotplug_uninit(dip); + return (DDI_FAILURE); + } + + if (pcihp_get_cb_ops() != NULL) { + DBG(DBG_ATTACH, dip, "%s%d hotplug enabled", + ddi_driver_name(dip), ddi_get_instance(dip)); + px_p->px_dev_caps |= PX_HOTPLUG_CAPABLE; + } + + return (DDI_SUCCESS); +} + +static uint_t +px_uninit_hotplug(dev_info_t *dip) +{ + if (pcihp_uninit(dip) != DDI_SUCCESS) + return (DDI_FAILURE); + + if (pciehpc_uninit(dip) != DDI_SUCCESS) + return (DDI_FAILURE); + + px_lib_hotplug_uninit(dip); + + return (DDI_SUCCESS); +} diff --git a/usr/src/uts/sun4/io/px/px_devctl.c b/usr/src/uts/sun4/io/px/px_devctl.c index 1dbc054c8a..196d8d6e3a 100644 --- a/usr/src/uts/sun4/io/px/px_devctl.c +++ b/usr/src/uts/sun4/io/px/px_devctl.c @@ -20,7 +20,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -82,6 +82,8 @@ static int px_open(dev_t *devp, int flags, int otyp, cred_t *credp) { px_t *px_p; + int rval; + uint_t orig_px_soft_state; /* * Make sure the open is for the right file type. @@ -101,6 +103,7 @@ px_open(dev_t *devp, int flags, int otyp, cred_t *credp) */ DBG(DBG_OPEN, px_p->px_dip, "devp=%x: flags=%x\n", devp, flags); mutex_enter(&px_p->px_mutex); + orig_px_soft_state = px_p->px_soft_state; if (flags & FEXCL) { if (px_p->px_soft_state != PX_SOFT_STATE_CLOSED) { mutex_exit(&px_p->px_mutex); @@ -116,6 +119,15 @@ px_open(dev_t *devp, int flags, int otyp, cred_t *credp) } px_p->px_soft_state = PX_SOFT_STATE_OPEN; } + + if (px_p->px_dev_caps & PX_HOTPLUG_CAPABLE) + if (rval = (pcihp_get_cb_ops())->cb_open(devp, flags, + otyp, credp)) { + px_p->px_soft_state = orig_px_soft_state; + mutex_exit(&px_p->px_mutex); + return (rval); + } + px_p->px_open_count++; mutex_exit(&px_p->px_mutex); return (0); @@ -127,6 +139,7 @@ static int px_close(dev_t dev, int flags, int otyp, cred_t *credp) { px_t *px_p; + int rval; if (otyp != OTYP_CHR) return (EINVAL); @@ -137,6 +150,14 @@ px_close(dev_t dev, int flags, int otyp, cred_t *credp) DBG(DBG_CLOSE, px_p->px_dip, "dev=%x: flags=%x\n", dev, flags); mutex_enter(&px_p->px_mutex); + + if (px_p->px_dev_caps & PX_HOTPLUG_CAPABLE) + if (rval = (pcihp_get_cb_ops())->cb_close(dev, flags, + otyp, credp)) { + mutex_exit(&px_p->px_mutex); + return (rval); + } + px_p->px_soft_state = PX_SOFT_STATE_CLOSED; px_p->px_open_count = 0; mutex_exit(&px_p->px_mutex); @@ -230,6 +251,9 @@ px_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp) return (rv); default: + if (px_p->px_dev_caps & PX_HOTPLUG_CAPABLE) + return ((pcihp_get_cb_ops())->cb_ioctl(dev, cmd, + arg, mode, credp, rvalp)); break; } diff --git a/usr/src/uts/sun4/io/px/px_dma.c b/usr/src/uts/sun4/io/px/px_dma.c index 743b71c41c..58731d3277 100644 --- a/usr/src/uts/sun4/io/px/px_dma.c +++ b/usr/src/uts/sun4/io/px/px_dma.c @@ -228,7 +228,7 @@ px_dma_attach(px_t *px_p) PCI_MAP_ATTR_WRITE|PCI_MAP_ATTR_READ, &baddr) != DDI_ENOTSUP) /* ignore all other errors */ - px_p->px_soft_state |= PX_BYPASS_DMA_ALLOWED; + px_p->px_dev_caps |= PX_BYPASS_DMA_ALLOWED; return (DDI_SUCCESS); } @@ -276,7 +276,7 @@ px_dma_attr2hdl(px_t *px_p, ddi_dma_impl_t *mp) * If Bypass DMA is not supported, return error so that * target driver can fall back to dvma mode of operation */ - if (!(px_p->px_soft_state & PX_BYPASS_DMA_ALLOWED)) + if (!(px_p->px_dev_caps & PX_BYPASS_DMA_ALLOWED)) return (DDI_DMA_BADATTR); mp->dmai_flags |= PX_DMAI_FLAGS_BYPASSREQ; if (nocross != UINT64_MAX) diff --git a/usr/src/uts/sun4/io/px/px_fm.c b/usr/src/uts/sun4/io/px/px_fm.c index 987649b781..3270dfc2d9 100644 --- a/usr/src/uts/sun4/io/px/px_fm.c +++ b/usr/src/uts/sun4/io/px/px_fm.c @@ -20,7 +20,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -804,7 +804,9 @@ px_err_fabric_intr(px_t *px_p, msgcode_t msg_code, */ if ((err & (PX_FATAL_GOS | PX_FATAL_SW)) || (ret == DDI_FM_FATAL) || (fab_err == DDI_FM_FATAL)) - PX_FM_PANIC("Fatal PCIe Fabric Error has occurred\n"); + PX_FM_PANIC("%s#%d: Fatal PCIe Fabric Error has occurred" + "(%x,%x,%x)\n", ddi_driver_name(rpdip), + ddi_get_instance(rpdip), err, fab_err, ret); return (DDI_INTR_CLAIMED); } diff --git a/usr/src/uts/sun4/io/px/px_lib.h b/usr/src/uts/sun4/io/px/px_lib.h index d4edf2702f..a32b084c63 100644 --- a/usr/src/uts/sun4/io/px/px_lib.h +++ b/usr/src/uts/sun4/io/px/px_lib.h @@ -20,7 +20,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -210,6 +210,12 @@ extern void px_fab_set(px_t *px_p, pcie_req_id_t bdf, uint16_t offset, extern void px_cpr_add_callb(px_t *); extern void px_cpr_rem_callb(px_t *); +/* + * Hotplug functions + */ +extern int px_lib_hotplug_init(dev_info_t *dip, void *regops); +extern void px_lib_hotplug_uninit(dev_info_t *dip); + #ifdef __cplusplus } #endif diff --git a/usr/src/uts/sun4/io/px/px_pci.c b/usr/src/uts/sun4/io/px/px_pci.c index d0761e6950..90da208323 100644 --- a/usr/src/uts/sun4/io/px/px_pci.c +++ b/usr/src/uts/sun4/io/px/px_pci.c @@ -37,11 +37,19 @@ #include <sys/autoconf.h> #include <sys/ddi_impldefs.h> #include <sys/ddi_subrdefs.h> +#include <sys/time.h> +#include <sys/proc.h> +#include <sys/thread.h> +#include <sys/disp.h> +#include <sys/condvar.h> +#include <sys/callb.h> #include <sys/pcie.h> #include <sys/pcie_impl.h> #include <sys/ddi.h> #include <sys/sunndi.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> @@ -49,12 +57,13 @@ #include "px_pci.h" #include "px_debug.h" +/* Tunables. Beware: Some are for debug purpose only. */ /* * PXB MSI tunable: * * By default MSI is enabled on all supported platforms. */ -boolean_t pxb_enable_msi = B_TRUE; +boolean_t pxb_enable_msi = B_TRUE; /* MSI enabled by default, otherwise INTX */ static int pxb_bus_map(dev_info_t *, dev_info_t *, ddi_map_req_t *, off_t, off_t, caddr_t *); @@ -73,6 +82,11 @@ static int pxb_fm_init_child(dev_info_t *dip, dev_info_t *cdip, int cap, static int pxb_fm_err_callback(dev_info_t *dip, ddi_fm_error_t *derr, const void *impl_data); +static int ppb_pcie_device_type(pxb_devstate_t *pxb_p); +#ifdef PRINT_PLX_SEEPROM_CRC +static void pxb_print_plx_seeprom_crc_data(pxb_devstate_t *pxb_p); +#endif + struct bus_ops pxb_bus_ops = { BUSO_REV, pxb_bus_map, @@ -142,6 +156,17 @@ static int pxb_pwr_setup(dev_info_t *dip); static int pxb_pwr_init_and_raise(dev_info_t *dip, pcie_pwr_t *pwr_p); static void pxb_pwr_teardown(dev_info_t *dip); +static int pxb_get_cap(ddi_acc_handle_t config_handle, uint8_t cap_id); +static uint16_t pxb_find_ext_cap_reg(ddi_acc_handle_t config_handle, + uint16_t cap_id); +static int pxb_bad_func(pxb_devstate_t *pxb, int func); +static int pxb_check_bad_devs(pxb_devstate_t *pxb, int vend); + +/* Hotplug related functions */ +static int pxb_pciehpc_probe(dev_info_t *dip, ddi_acc_handle_t config_handle); +static int pxb_pcishpc_probe(dev_info_t *dip, ddi_acc_handle_t config_handle); +static void pxb_init_hotplug(pxb_devstate_t *pxb); + struct dev_ops pxb_ops = { DEVO_REV, /* devo_rev */ 0, /* refcnt */ @@ -162,7 +187,7 @@ struct dev_ops pxb_ops = { static struct modldrv modldrv = { &mod_driverops, /* Type of module */ - "PCIe/PCI nexus driver %I%", + "PCIe/PCI nexus driver 1.29", &pxb_ops, /* driver ops */ }; @@ -188,11 +213,12 @@ int pxb_tlp_count = 64; static int pxb_intr_init(pxb_devstate_t *pxb, int intr_type); static void pxb_intr_fini(pxb_devstate_t *pxb); static uint_t pxb_intr(caddr_t arg1, caddr_t arg2); +static int pxb_intr_attach(pxb_devstate_t *pxb); + static int pxb_get_port_type(dev_info_t *dip, ddi_acc_handle_t config_handle); static void pxb_removechild(dev_info_t *); static int pxb_initchild(dev_info_t *child); static dev_info_t *get_my_childs_dip(dev_info_t *dip, dev_info_t *rdip); -static void pxb_init_hotplug(pxb_devstate_t *pxb); static void pxb_create_ranges_prop(dev_info_t *, ddi_acc_handle_t); int @@ -262,7 +288,7 @@ pxb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) int instance; pxb_devstate_t *pxb; ddi_acc_handle_t config_handle; - int intr_types; + char device_type[8]; instance = ddi_get_instance(devi); @@ -326,6 +352,7 @@ pxb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) /* Save the vnedor id and device id */ pxb->pxb_vendor_id = pci_config_get16(config_handle, PCI_CONF_VENID); pxb->pxb_device_id = pci_config_get16(config_handle, PCI_CONF_DEVID); + pxb->pxb_rev_id = pci_config_get8(config_handle, PCI_CONF_REVID); /* * This is a software workaround to fix the Broadcom PCIe-PCI bridge @@ -356,13 +383,8 @@ pxb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) goto fail; } - if ((pxb_fm_init(pxb)) != DDI_SUCCESS) { - DBG(DBG_ATTACH, devi, "Failed in px_pci_fm_attach\n"); - goto fail; - } - pxb->pxb_init_flags |= PXB_INIT_FM; - pxb->pxb_port_type = pxb_get_port_type(devi, config_handle); + if ((pxb->pxb_port_type != PX_CAP_REG_DEV_TYPE_UP) && (pxb->pxb_port_type != PX_CAP_REG_DEV_TYPE_DOWN) && (pxb->pxb_port_type != PX_CAP_REG_DEV_TYPE_PCIE2PCI) && @@ -374,8 +396,12 @@ pxb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) /* * Make sure the "device_type" property exists. */ + if (ppb_pcie_device_type(pxb) == DDI_SUCCESS) + (void) strcpy(device_type, "pciex"); + else + (void) strcpy(device_type, "pci"); (void) ddi_prop_update_string(DDI_DEV_T_NONE, devi, - "device_type", "pciex"); + "device_type", device_type); /* * Check whether the "ranges" property is present. @@ -388,32 +414,6 @@ pxb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) } /* - * Initialize interrupt handlers. - * If both MSI and FIXED are supported, try to attach MSI first. - * If MSI fails for any reason, then try FIXED, but only allow one - * type to be attached. - */ - if (ddi_intr_get_supported_types(devi, &intr_types) != DDI_SUCCESS) { - DBG(DBG_ATTACH, devi, "ddi_intr_get_supported_types failed\n"); - goto fail; - } - - if ((intr_types & DDI_INTR_TYPE_MSI) && pxb_enable_msi) { - if (pxb_intr_init(pxb, DDI_INTR_TYPE_MSI) == DDI_SUCCESS) - intr_types = DDI_INTR_TYPE_MSI; - else - DBG(DBG_ATTACH, devi, "Unable to attach MSI handler\n"); - } - - if (intr_types & DDI_INTR_TYPE_FIXED) { - if (pxb_intr_init(pxb, DDI_INTR_TYPE_FIXED) != DDI_SUCCESS) { - DBG(DBG_ATTACH, devi, - "Unable to attach INTx handler\n"); - goto fail; - } - } - - /* * Initialize hotplug support on this bus. At minimum * (for non hotplug bus) this would create ":devctl" minor * node to support DEVCTL_DEVICE_* and DEVCTL_BUS_* ioctls @@ -421,7 +421,23 @@ pxb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) * slots and successfully initializes Hot Plug Framework. */ pxb->pxb_hotplug_capable = B_FALSE; - pxb_init_hotplug(pxb); + + if ((pxb->pxb_port_type == PX_CAP_REG_DEV_TYPE_DOWN) || + (pxb->pxb_port_type == PX_CAP_REG_DEV_TYPE_PCIE2PCI) || + (pxb->pxb_port_type == PX_CAP_REG_DEV_TYPE_PCI2PCIE)) { + pxb_init_hotplug(pxb); + } + + /* attach interrupt only if hotplug functionality is required */ + if (pxb->pxb_hotplug_capable != B_FALSE) { + if (pxb_intr_attach(pxb) != DDI_SUCCESS) + goto fail; + } +#ifdef PRINT_PLX_SEEPROM_CRC + /* check seeprom CRC to ensure the platform config is right */ + (void) pxb_print_plx_seeprom_crc_data(pxb); +#endif + if (pxb->pxb_hotplug_capable == B_FALSE) { /* * create minor node for devctl interfaces @@ -436,6 +452,12 @@ pxb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) "pxb_attach(): this nexus %s hotplug slots\n", pxb->pxb_hotplug_capable == B_TRUE ? "has":"has no"); + if ((pxb_fm_init(pxb)) != DDI_SUCCESS) { + DBG(DBG_ATTACH, devi, "Failed in px_pci_fm_attach\n"); + goto fail; + } + pxb->pxb_init_flags |= PXB_INIT_FM; + ddi_report_dev(devi); return (DDI_SUCCESS); @@ -463,16 +485,22 @@ pxb_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) pxb = (pxb_devstate_t *) ddi_get_soft_state(pxb_state, ddi_get_instance(devi)); - if (pxb->pxb_hotplug_capable == B_TRUE) + if (pxb->pxb_hotplug_capable == B_TRUE) { if (pcihp_uninit(devi) == DDI_FAILURE) error = DDI_FAILURE; + + if (pxb->pxb_hpc_type == HPC_PCIE) + (void) pciehpc_uninit(devi); + else if (pxb->pxb_hpc_type == HPC_SHPC) + (void) pcishpc_uninit(devi); + } else ddi_remove_minor_node(devi, "devctl"); (void) ddi_prop_remove(DDI_DEV_T_NONE, devi, "device_type"); - pxb_intr_fini(pxb); - + if (pxb->pxb_hotplug_capable != B_FALSE) + pxb_intr_fini(pxb); if (pxb->pxb_init_flags & PXB_INIT_FM) pxb_fm_fini(pxb); @@ -881,8 +909,13 @@ pxb_initchild(dev_info_t *child) * optimized out. */ if ((!pxb_tlp_count) || - (pxb->pxb_vendor_id != PXB_VENDOR_PLX) || - ((pxb->pxb_device_id != PXB_DEVICE_PLX_8532) && + ((pxb->pxb_vendor_id != PXB_VENDOR_SUN) && + (pxb->pxb_vendor_id != PXB_VENDOR_PLX)) || + ((pxb->pxb_vendor_id == PXB_VENDOR_SUN) && + (pxb->pxb_device_id != PXB_DEVICE_PLX_PCIX) && + (pxb->pxb_device_id != PXB_DEVICE_PLX_PCIE)) || + ((pxb->pxb_vendor_id == PXB_VENDOR_PLX) && + (pxb->pxb_device_id != PXB_DEVICE_PLX_8532) && (pxb->pxb_device_id != PXB_DEVICE_PLX_8516))) { /* Workaround not needed return success */ result = DDI_SUCCESS; @@ -906,6 +939,46 @@ done: return (result); } +static int +pxb_intr_attach(pxb_devstate_t *pxb) +{ + int intr_types; + dev_info_t *devi; + uint8_t bad_msi_dev = 0; + + devi = pxb->pxb_dip; + /* + * Initialize interrupt handlers. + * If both MSI and FIXED are supported, try to attach MSI first. + * If MSI fails for any reason, then try FIXED, but only allow one + * type to be attached. + */ + if (ddi_intr_get_supported_types(devi, &intr_types) != DDI_SUCCESS) { + DBG(DBG_ATTACH, devi, "ddi_intr_get_supported_types failed\n"); + return (DDI_FAILURE); + } + + if (pxb_bad_func(pxb, PXB_MSI)) + bad_msi_dev = 1; + + if ((intr_types & DDI_INTR_TYPE_MSI) && pxb_enable_msi && + !bad_msi_dev) { + if (pxb_intr_init(pxb, DDI_INTR_TYPE_MSI) == DDI_SUCCESS) + intr_types = DDI_INTR_TYPE_MSI; + else + DBG(DBG_ATTACH, devi, "Unable to attach MSI handler\n"); + } + + if (intr_types & DDI_INTR_TYPE_FIXED) { + if (pxb_intr_init(pxb, DDI_INTR_TYPE_FIXED) != DDI_SUCCESS) { + DBG(DBG_ATTACH, devi, + "Unable to attach INTx handler\n"); + return (DDI_FAILURE); + } + } + return (DDI_SUCCESS); +} + /* * This function initializes internally generated interrupts only. * It does not affect any interrupts generated by downstream devices @@ -917,7 +990,8 @@ done: * device might not ask for any interrupts. */ static int -pxb_intr_init(pxb_devstate_t *pxb, int intr_type) { +pxb_intr_init(pxb_devstate_t *pxb, int intr_type) +{ dev_info_t *dip = pxb->pxb_dip; int request, count, x; int ret; @@ -1055,8 +1129,9 @@ fail: return (DDI_FAILURE); } -static -void pxb_intr_fini(pxb_devstate_t *pxb) { +static void +pxb_intr_fini(pxb_devstate_t *pxb) +{ int x; int count = pxb->pxb_intr_count; int flags = pxb->pxb_init_flags; @@ -1093,21 +1168,30 @@ void pxb_intr_fini(pxb_devstate_t *pxb) { */ /*ARGSUSED*/ static uint_t -pxb_intr(caddr_t arg1, caddr_t arg2) { +pxb_intr(caddr_t arg1, caddr_t arg2) +{ pxb_devstate_t *pxb = (pxb_devstate_t *)arg1; dev_info_t *dip = pxb->pxb_dip; + int rval = DDI_INTR_UNCLAIMED; - cmn_err(CE_WARN, - "%s%d: Received %s Interrupt\n", - ddi_driver_name(dip), - ddi_get_instance(dip), - (pxb->pxb_intr_type == DDI_INTR_TYPE_MSI) ? "MSI" : "INTx"); + if (pxb->pxb_hotplug_capable == B_TRUE) { + if (pxb->pxb_hpc_type == HPC_PCIE) + rval = pciehpc_intr(pxb->pxb_dip); + else + if (pxb->pxb_hpc_type == HPC_SHPC) + rval = pcishpc_intr(pxb->pxb_dip); + } + if ((rval == DDI_INTR_UNCLAIMED) && (pxb->pxb_intr_type == + DDI_INTR_TYPE_MSI)) + cmn_err(CE_WARN, "%s%d: Cannot handle interrupt", + ddi_driver_name(dip), ddi_get_instance(dip)); - return (DDI_INTR_UNCLAIMED); + return (rval); } -static -int pxb_get_port_type(dev_info_t *dip, ddi_acc_handle_t config_handle) { +static int +pxb_get_port_type(dev_info_t *dip, ddi_acc_handle_t config_handle) +{ ushort_t caps_ptr, cap; int port_type = PX_CAP_REG_DEV_TYPE_PCIE_DEV; @@ -1168,10 +1252,41 @@ pxb_removechild(dev_info_t *dip) static void pxb_init_hotplug(pxb_devstate_t *pxb) { - /* - * Hot plug support to be decided. - */ + int rv; + + pxb->pxb_hpc_type = HPC_NONE; + + if (((pxb->pxb_port_type == PX_CAP_REG_DEV_TYPE_DOWN) || + (pxb->pxb_port_type == PX_CAP_REG_DEV_TYPE_PCI2PCIE)) && + (pxb_pciehpc_probe(pxb->pxb_dip, + pxb->pxb_config_handle) == DDI_SUCCESS)) { + rv = pciehpc_init(pxb->pxb_dip, NULL); + if (rv == DDI_SUCCESS) + pxb->pxb_hpc_type = HPC_PCIE; + } else if ((pxb->pxb_port_type == PX_CAP_REG_DEV_TYPE_PCIE2PCI) && + (pxb_pcishpc_probe(pxb->pxb_dip, + pxb->pxb_config_handle) == DDI_SUCCESS)) { + rv = pcishpc_init(pxb->pxb_dip); + if (rv == DDI_SUCCESS) + pxb->pxb_hpc_type = HPC_SHPC; + } + if (pxb->pxb_hpc_type != HPC_NONE) { + if (pcihp_init(pxb->pxb_dip) != DDI_SUCCESS) { + if (pxb->pxb_hpc_type == HPC_PCIE) + (void) pciehpc_uninit(pxb->pxb_dip); + else if (pxb->pxb_hpc_type == HPC_SHPC) + (void) pcishpc_uninit(pxb->pxb_dip); + + pxb->pxb_hpc_type = HPC_NONE; + cmn_err(CE_WARN, + "%s%d: Failed setting hotplug framework", + ddi_driver_name(pxb->pxb_dip), + ddi_get_instance(pxb->pxb_dip)); + } + } + + pxb->pxb_hotplug_capable = (pxb->pxb_hpc_type != HPC_NONE); } static void @@ -1442,9 +1557,12 @@ pxb_pwr_setup(dev_info_t *dip) * Due to PLX erratum #34, we can't allow the downstream device * go to non-D0 state. */ - if ((pxb->pxb_vendor_id == PXB_VENDOR_PLX) && - ((pxb->pxb_device_id == PXB_DEVICE_PLX_8516) || - (pxb->pxb_device_id == PXB_DEVICE_PLX_8532))) { + if (((pxb->pxb_vendor_id == PXB_VENDOR_SUN) && + ((pxb->pxb_device_id == PXB_DEVICE_PLX_PCIX) || + (pxb->pxb_device_id == PXB_DEVICE_PLX_PCIE))) || + ((pxb->pxb_vendor_id == PXB_VENDOR_PLX) && + ((pxb->pxb_device_id == PXB_DEVICE_PLX_8516) || + (pxb->pxb_device_id == PXB_DEVICE_PLX_8532)))) { DBG(DBG_PWR, dip, "pxb_pwr_setup: PLX8532/PLX8516 found " "disabling PM\n"); pwr_p->pwr_func_lvl = PM_LEVEL_D0; @@ -1601,7 +1719,8 @@ pxb_fm_init(pxb_devstate_t *pxb_p) /* * Register error callback with our parent. */ - ddi_fm_handler_register(pxb_p->pxb_dip, pxb_fm_err_callback, NULL); + ddi_fm_handler_register(pxb_p->pxb_dip, pxb_fm_err_callback, + (void *)&pxb_p->pxb_config_handle); return (DDI_SUCCESS); } @@ -1636,20 +1755,24 @@ pxb_fm_init_child(dev_info_t *dip, dev_info_t *cdip, int cap, } /* - * Error callback handler. + * FMA Error callback handler. + * Need to revisit when pcie fm is supported. */ /*ARGSUSED*/ static int pxb_fm_err_callback(dev_info_t *dip, ddi_fm_error_t *derr, const void *impl_data) { - /* Need to revisit when pcie fm is supported */ uint16_t pci_cfg_stat, pci_cfg_sec_stat; + int ret; + ddi_acc_handle_t hdl = *(ddi_acc_handle_t *)impl_data; pci_ereport_post(dip, derr, &pci_cfg_stat); pci_bdg_ereport_post(dip, derr, &pci_cfg_sec_stat); - return (pci_bdg_check_status(dip, derr, pci_cfg_stat, - pci_cfg_sec_stat)); + ret = pci_bdg_check_status(dip, derr, pci_cfg_stat, pci_cfg_sec_stat); + /* do this till Fabric FMA is in place. */ + pcie_clear_errors(dip, hdl); + return (ret); } /* @@ -1667,3 +1790,277 @@ pxb_pwr_teardown(dev_info_t *dip) if (pwr_p->pwr_conf_hdl) pci_config_teardown(&pwr_p->pwr_conf_hdl); } + +/*ARGSUSED*/ +static int pxb_pciehpc_probe(dev_info_t *dip, ddi_acc_handle_t config_handle) +{ + uint16_t status; + uint8_t cap_ptr, cap_id; + + status = pci_config_get16(config_handle, PCI_CONF_STAT); + + if (!(status & PCI_STAT_CAP)) + cap_ptr = PCI_CAP_NEXT_PTR_NULL; + else { + cap_ptr = pci_config_get8(config_handle, PCI_CONF_CAP_PTR); + cap_ptr &= 0xFC; + } + + while (cap_ptr != PCI_CAP_NEXT_PTR_NULL) { + cap_id = pci_config_get8(config_handle, cap_ptr); + + if (cap_id == PCI_CAP_ID_PCI_E) { + uint16_t slotimpl; + + slotimpl = pci_config_get16(config_handle, cap_ptr + + PCIE_PCIECAP) & PCIE_PCIECAP_SLOT_IMPL; + if (slotimpl) + if (pci_config_get32(config_handle, cap_ptr + + PCIE_SLOTCAP) & + PCIE_SLOTCAP_HP_CAPABLE) + break; + } + + cap_ptr = pci_config_get8(config_handle, cap_ptr + + PCI_CAP_NEXT_PTR); + cap_ptr &= 0xFC; + } + + return ((cap_ptr != PCI_CAP_NEXT_PTR_NULL) ? DDI_SUCCESS : DDI_FAILURE); +} + +/*ARGSUSED*/ +static int pxb_pcishpc_probe(dev_info_t *dip, ddi_acc_handle_t config_handle) +{ + uint16_t status; + uint8_t cap_ptr, cap_id; + + status = pci_config_get16(config_handle, PCI_CONF_STAT); + + if (!(status & PCI_STAT_CAP)) + cap_ptr = PCI_CAP_NEXT_PTR_NULL; + else { + cap_ptr = pci_config_get8(config_handle, PCI_CONF_CAP_PTR); + cap_ptr &= 0xFC; + } + + while (cap_ptr != PCI_CAP_NEXT_PTR_NULL) { + cap_id = pci_config_get8(config_handle, cap_ptr); + + if (cap_id == PCI_CAP_ID_PCI_HOTPLUG) + break; + + cap_ptr = pci_config_get8(config_handle, cap_ptr + + PCI_CAP_NEXT_PTR); + cap_ptr &= 0xFC; + } + + return ((cap_ptr != PCI_CAP_NEXT_PTR_NULL) ? DDI_SUCCESS : DDI_FAILURE); +} + +/* check if this device has PCIe link underneath. */ +static int +ppb_pcie_device_type(pxb_devstate_t *pxb_p) +{ + int port_type = pxb_p->pxb_port_type; + + /* No PCIe CAP regs, we are not PCIe device_type */ + if (port_type < 0) + return (DDI_FAILURE); + + /* check for all PCIe device_types */ + if ((port_type == PCIE_PCIECAP_DEV_TYPE_UP) || + (port_type == PCIE_PCIECAP_DEV_TYPE_DOWN) || + (port_type == PCIE_PCIECAP_DEV_TYPE_PCI2PCIE)) + return (DDI_SUCCESS); + + return (DDI_FAILURE); +} + +#ifdef PRINT_PLX_SEEPROM_CRC +static void +pxb_print_plx_seeprom_crc_data(pxb_devstate_t *pxb_p) +{ + ddi_acc_handle_t h; + dev_info_t *dip = pxb_p->pxb_dip; + int nregs; + caddr_t mp; + off_t bar_size; + ddi_device_acc_attr_t mattr = { + DDI_DEVICE_ATTR_V0, + DDI_STRUCTURE_LE_ACC, + DDI_STRICTORDER_ACC + }; + uint32_t addr_reg_off = 0x260, data_reg_off = 0x264, data = 0x6BE4; + + if (pxb_p->pxb_vendor_id != PXB_VENDOR_PLX) + return; + if (ddi_dev_nregs(dip, &nregs) != DDI_SUCCESS) + return; + if (nregs < 2) /* check for CONF entry only, no BARs */ + return; + if (ddi_dev_regsize(dip, 1, &bar_size) != DDI_SUCCESS) + return; + if (ddi_regs_map_setup(dip, 1, (caddr_t *)&mp, 0, bar_size, + &mattr, &h) != DDI_SUCCESS) + return; + ddi_put32(h, (uint32_t *)((uchar_t *)mp + addr_reg_off), data); + delay(drv_usectohz(1000000)); + printf("%s#%d: EEPROM StatusReg = %x, CRC = %x\n", + ddi_driver_name(dip), ddi_get_instance(dip), + ddi_get32(h, (uint32_t *)((uchar_t *)mp + addr_reg_off)), + ddi_get32(h, (uint32_t *)((uchar_t *)mp + data_reg_off))); +#ifdef PLX_HOT_RESET_DISABLE + /* prevent hot reset from propogating downstream. */ + data = ddi_get32(h, (uint32_t *)((uchar_t *)mp + 0x1DC)); + ddi_put32(h, (uint32_t *)((uchar_t *)mp + 0x1DC), data | 0x80000); + delay(drv_usectohz(1000000)); + printf("%s#%d: EEPROM 0x1DC prewrite=%x postwrite=%x\n", + ddi_driver_name(dip), ddi_get_instance(dip), data, + ddi_get32(h, (uint32_t *)((uchar_t *)mp + 0x1DC))); +#endif + ddi_regs_map_free(&h); +} +#endif + +/* + * given a cap_id, return its cap_id location in config space + */ +static int +pxb_get_cap(ddi_acc_handle_t config_handle, uint8_t cap_id) +{ + uint8_t curcap; + uint_t cap_id_loc; + uint16_t status; + int location = -1; + + /* + * Need to check the Status register for ECP support first. + * Also please note that for type 1 devices, the + * offset could change. Should support type 1 next. + */ + status = pci_config_get16(config_handle, PCI_CONF_STAT); + if (!(status & PCI_STAT_CAP)) { + return (-1); + } + cap_id_loc = pci_config_get8(config_handle, PCI_CONF_CAP_PTR); + + /* Walk the list of capabilities */ + while (cap_id_loc) { + + curcap = pci_config_get8(config_handle, cap_id_loc); + + if (curcap == cap_id) { + location = cap_id_loc; + break; + } + cap_id_loc = pci_config_get8(config_handle, + cap_id_loc + 1); + } + return (location); +} + +static uint16_t +pxb_find_ext_cap_reg(ddi_acc_handle_t config_handle, uint16_t cap_id) +{ + uint32_t hdr, hdr_next_ptr, hdr_cap_id; + uint16_t offset = P2ALIGN(PCIE_EXT_CAP, 4); + + hdr = pci_config_get32(config_handle, offset); + hdr_next_ptr = (hdr >> PCIE_EXT_CAP_NEXT_PTR_SHIFT) & + PCIE_EXT_CAP_NEXT_PTR_MASK; + hdr_cap_id = (hdr >> PCIE_EXT_CAP_ID_SHIFT) & + PCIE_EXT_CAP_ID_MASK; + + while ((hdr_next_ptr != PCIE_EXT_CAP_NEXT_PTR_NULL) && + (hdr_cap_id != cap_id)) { + offset = P2ALIGN(hdr_next_ptr, 4); + hdr = pci_config_get32(config_handle, offset); + hdr_next_ptr = (hdr >> PCIE_EXT_CAP_NEXT_PTR_SHIFT) & + PCIE_EXT_CAP_NEXT_PTR_MASK; + hdr_cap_id = (hdr >> PCIE_EXT_CAP_ID_SHIFT) & + PCIE_EXT_CAP_ID_MASK; + } + + if (hdr_cap_id == cap_id) + return (P2ALIGN(offset, 4)); + + return (PCIE_EXT_CAP_NEXT_PTR_NULL); +} + +/* check for known bad functionality in devices */ +static int +pxb_bad_func(pxb_devstate_t *pxb, int func) +{ + int ret = B_FALSE; + + switch (func) { + /* some devices do not render MSI well. */ + case PXB_MSI: + if (pxb_check_bad_devs(pxb, PXB_VENDOR_SUN) || + pxb_check_bad_devs(pxb, PXB_VENDOR_PLX)) { + + if (pxb->pxb_rev_id <= + PXB_DEVICE_PLX_BAD_MSI_REV) + ret = B_TRUE; + } + break; + + case PXB_LINK_INIT: + /* + * some devices require certain initialization sequence so + * as to not get wedged. + */ + + case PXB_HOTPLUG_MSGS: + /* + * disable UR for 1.0a implementations where + * hotplug messages sent downstream cause URs to be + * detected causing error messages to be sent to the fabric. + */ + if (pxb_check_bad_devs(pxb, PXB_VENDOR_SUN) || + pxb_check_bad_devs(pxb, PXB_VENDOR_PLX)) { + + ret = B_TRUE; + } + if (func == PXB_HOTPLUG_MSGS) + ret = B_TRUE; + break; + + + default: + break; + } + return (ret); +} + +/* check for known bad devices from a vendor */ +static int +pxb_check_bad_devs(pxb_devstate_t *pxb, int vend) +{ + int ret = B_FALSE; + + switch (vend) { + case PXB_VENDOR_SUN: + if ((pxb->pxb_vendor_id == PXB_VENDOR_SUN) && + ((pxb->pxb_device_id == + PXB_DEVICE_PLX_PCIX) || + (pxb->pxb_device_id == + PXB_DEVICE_PLX_PCIE))) { + ret = B_TRUE; + } + break; + case PXB_VENDOR_PLX: + if ((pxb->pxb_vendor_id == PXB_VENDOR_PLX) && + ((pxb->pxb_device_id == + PXB_DEVICE_PLX_8532) || + (pxb->pxb_device_id == + PXB_DEVICE_PLX_8516))) { + ret = B_TRUE; + } + break; + default: + break; + } + return (ret); +} diff --git a/usr/src/uts/sun4/io/px/px_pci.h b/usr/src/uts/sun4/io/px/px_pci.h index 283c635b39..d3fbe16c7e 100644 --- a/usr/src/uts/sun4/io/px/px_pci.h +++ b/usr/src/uts/sun4/io/px/px_pci.h @@ -114,6 +114,8 @@ typedef struct { uint32_t size_low; } pxb_ranges_t; +typedef enum { HPC_NONE, HPC_PCIE, HPC_SHPC, HPC_OUTBAND } pxb_hpc_type_t; + typedef struct { dev_info_t *pxb_dip; @@ -133,6 +135,7 @@ typedef struct { * HP support */ boolean_t pxb_hotplug_capable; + pxb_hpc_type_t pxb_hpc_type; kmutex_t pxb_mutex; uint_t pxb_soft_state; @@ -147,7 +150,7 @@ typedef struct { /* Vendor Device Id */ uint16_t pxb_vendor_id; uint16_t pxb_device_id; - + uint8_t pxb_rev_id; } pxb_devstate_t; /* @@ -184,6 +187,19 @@ extern void *pxb_state; ((pxb->pxb_vendor_id == PXB_VENDOR_BCM) && \ (pxb->pxb_device_id == PXB_DEVICE_BCM5714)) +#define PXB_DEVICE_PLX_BAD_MSI_REV 0xAA /* last known bad rev for MSI */ + +#define PXB_VENDOR_SUN 0x108E +#define PXB_DEVICE_PLX_PCIX 0x9010 +#define PXB_DEVICE_PLX_PCIE 0x9020 + +#define PXB_HOTPLUG_INTR_PRI (LOCK_LEVEL - 1) + +/* functionality checks */ +#define PXB_MSI 1 +#define PXB_LINK_INIT 2 +#define PXB_HOTPLUG_MSGS 3 + #ifdef __cplusplus } #endif diff --git a/usr/src/uts/sun4/io/px/px_var.h b/usr/src/uts/sun4/io/px/px_var.h index 23d3355530..5b1fd8d898 100644 --- a/usr/src/uts/sun4/io/px/px_var.h +++ b/usr/src/uts/sun4/io/px/px_var.h @@ -20,7 +20,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -132,6 +132,8 @@ struct px { int px_fm_cap; ddi_iblock_cookie_t px_fm_ibc; + uint32_t px_dev_caps; + /* Platform specific information */ void *px_plat_p; @@ -152,7 +154,10 @@ struct px { #define PX_SOFT_STATE_OPEN 0x01 #define PX_SOFT_STATE_OPEN_EXCL 0x02 #define PX_SOFT_STATE_CLOSED 0x04 -#define PX_BYPASS_DMA_ALLOWED 0x10 + +/* px_dev_caps definition */ +#define PX_BYPASS_DMA_ALLOWED 0x00000001 +#define PX_HOTPLUG_CAPABLE 0x00000002 /* px_pm_flags definitions used with interrupts and FMA code */ #define PX_PMETOACK_RECVD 0x01 /* With PME_To_ACK interrupt */ diff --git a/usr/src/uts/sun4u/sys/fc_plat.h b/usr/src/uts/sun4/sys/fc_plat.h index 77211e30f3..77211e30f3 100644 --- a/usr/src/uts/sun4u/sys/fc_plat.h +++ b/usr/src/uts/sun4/sys/fc_plat.h diff --git a/usr/src/uts/sun4u/Makefile.files b/usr/src/uts/sun4u/Makefile.files index a35156f264..88169fb6b1 100644 --- a/usr/src/uts/sun4u/Makefile.files +++ b/usr/src/uts/sun4u/Makefile.files @@ -103,7 +103,6 @@ GRBEEP_OBJS += grbeep.o ADM1031_OBJS += adm1031.o ICS951601_OBJS += ics951601.o PPM_OBJS += ppm_subr.o ppm.o -PCICFG_E_OBJS += pcicfg.e.o PCF8584_OBJS += pcf8584.o PCA9556_OBJS += pca9556.o ADM1026_OBJS += adm1026.o diff --git a/usr/src/uts/sun4u/Makefile.sun4u.shared b/usr/src/uts/sun4u/Makefile.sun4u.shared index 196e611b4b..b2b4b80914 100644 --- a/usr/src/uts/sun4u/Makefile.sun4u.shared +++ b/usr/src/uts/sun4u/Makefile.sun4u.shared @@ -367,7 +367,6 @@ DRV_KMODS += su DRV_KMODS += tod DRV_KMODS += power DRV_KMODS += epic -DRV_KMODS += fcode DRV_KMODS += grbeep DRV_KMODS += pcf8584 max1617 seeprom tda8444 pca9556 DRV_KMODS += ics951601 adm1031 @@ -434,7 +433,6 @@ SYS_KMODS += MISC_KMODS += obpsym bootdev vis cpr platmod md5 sha1 i2c_svc MISC_KMODS += sbd -MISC_KMODS += fcodem fcpci pcicfg.e MISC_KMODS += kmech_krb5 MISC_KMODS += zuluvm diff --git a/usr/src/uts/sun4u/daktari/Makefile b/usr/src/uts/sun4u/daktari/Makefile index 73c5ce61b3..0febbbe020 100644 --- a/usr/src/uts/sun4u/daktari/Makefile +++ b/usr/src/uts/sun4u/daktari/Makefile @@ -21,9 +21,10 @@ # # # uts/sun4u/daktari/Makefile -# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # +# #ident "%Z%%M% %I% %E% SMI" # # This makefile drives the production of all Daktari system @@ -85,13 +86,7 @@ install: $(ROOT_DAKTARI_DIR) $(USR_DAKTARI_DIR) \ $(LINKED_PLATFORMS:%=$(USR_PLAT_DIR)/%/include) \ $(LINKED_PLATFORMS:%=$(USR_PLAT_DIR)/%/sbin) \ $(DAKTARI_CRYPTO_LINKS) \ - .WAIT $(DAKTARI_KMODS) \ - pcicfg - -pcicfg: $(ROOT_PSM_MISC_DIR_64)/pcicfg.e - -@$(RM) $(ROOT_DAKTARI_MISC_DIR_64)/$@ - $(SYMLINK) ../../../../sun4u/kernel/misc/sparcv9/pcicfg.e \ - $(ROOT_DAKTARI_MISC_DIR_64)/$@ + .WAIT $(DAKTARI_KMODS) $(DAKTARI_CRYPTO_LINKS): $(ROOT_DAKTARI_CRYPTO_DIR_64) -$(RM) $(ROOT_DAKTARI_CRYPTO_DIR_64)/$@; diff --git a/usr/src/uts/sun4u/io/px/px_lib4u.c b/usr/src/uts/sun4u/io/px/px_lib4u.c index e4d84fc9df..2ee179b796 100644 --- a/usr/src/uts/sun4u/io/px/px_lib4u.c +++ b/usr/src/uts/sun4u/io/px/px_lib4u.c @@ -20,7 +20,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -42,6 +42,7 @@ #include <sys/cpuvar.h> #include <sys/ivintr.h> #include <sys/byteorder.h> +#include <sys/hotplug/pci/pciehpc.h> #include <px_obj.h> #include <pcie_pwr.h> #include <px_regs.h> @@ -2025,3 +2026,16 @@ px_cpr_rem_callb(px_t *px_p) { (void) callb_delete(px_p->px_cprcb_id); } + +/*ARGSUSED*/ +int +px_lib_hotplug_init(dev_info_t *dip, void *arg) +{ + return (DDI_ENOTSUP); +} + +/*ARGSUSED*/ +void +px_lib_hotplug_uninit(dev_info_t *dip) +{ +} diff --git a/usr/src/uts/sun4u/montecarlo/Makefile b/usr/src/uts/sun4u/montecarlo/Makefile index fca5358f84..cf62a50216 100644 --- a/usr/src/uts/sun4u/montecarlo/Makefile +++ b/usr/src/uts/sun4u/montecarlo/Makefile @@ -21,9 +21,10 @@ # # # uts/sun4u/montecarlo/Makefile -# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # +# #ident "%Z%%M% %I% %E% SMI" # # This makefile drives the production of all MonteCarlo system @@ -71,12 +72,7 @@ install: $(ROOT_MONTECARLO_DIR) $(USR_MONTECARLO_DIR) \ $(USR_MONTECARLO_SBIN_DIR) \ $(USR_MONTECARLO_LIB_DIR) \ .WAIT $(MONTECARLO_KMODS) $(CLOSED_MONTECARLO_KMODS) \ - pcicfg se.conf ttymux.conf - -pcicfg: $(ROOT_MONTECARLO_MISC_DIR_64) - -@$(RM) $(ROOT_MONTECARLO_MISC_DIR_64)/$@ - $(SYMLINK) ../../../../sun4u/kernel/misc/sparcv9/pcicfg.e \ - $(ROOT_MONTECARLO_MISC_DIR_64)/$@ + se.conf ttymux.conf se.conf: $(ROOT_MONTECARLO_DRV_DIR) -@$(RM) $(ROOT_MONTECARLO_DRV_DIR)/$@ diff --git a/usr/src/uts/sun4u/px/Makefile b/usr/src/uts/sun4u/px/Makefile index 972b5a41fb..8364572ae6 100644 --- a/usr/src/uts/sun4u/px/Makefile +++ b/usr/src/uts/sun4u/px/Makefile @@ -22,7 +22,7 @@ # # uts/sun4u/px/Makefile # -# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" @@ -76,7 +76,7 @@ CFLAGS += -dalign # # Dependency # -LDFLAGS += -dy -Nmisc/busra -Nmisc/pcie +LDFLAGS += -dy -Nmisc/busra -Nmisc/pcie -Nmisc/pcihp -Nmisc/pciehpc # # Default build targets. diff --git a/usr/src/uts/sun4u/serengeti/Makefile b/usr/src/uts/sun4u/serengeti/Makefile index a6ccb23ab5..7f532317bc 100644 --- a/usr/src/uts/sun4u/serengeti/Makefile +++ b/usr/src/uts/sun4u/serengeti/Makefile @@ -21,9 +21,10 @@ # # # uts/sun4u/serengeti/Makefile -# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # +# #ident "%Z%%M% %I% %E% SMI" # # This makefile drives the production of the sun4u serengeti platform @@ -86,13 +87,7 @@ install: $(ROOT_SERENGETI_DIR) $(USR_SERENGETI_DIR) \ $(USR_SERENGETI_LIB_DIR) \ $(SERENGETI_CRYPTO_LINKS) \ genassym unix .WAIT \ - $(SERENGETI_KMODS) $(CLOSED_SERENGETI_KMODS) \ - pcicfg - -pcicfg: $(ROOT_PSM_MISC_DIR_64)/pcicfg.e $(ROOT_SERENGETI_MISC_DIR_64) - -@$(RM) $(ROOT_SERENGETI_MISC_DIR_64)/$@ - $(SYMLINK) ../../../../sun4u/kernel/misc/sparcv9/pcicfg.e \ - $(ROOT_SERENGETI_MISC_DIR_64)/$@ + $(SERENGETI_KMODS) $(CLOSED_SERENGETI_KMODS) genassym unix $(SERENGETI_KMODS): FRC @cd $@; pwd; $(MAKE) $(NO_STATE) $(TARGET) diff --git a/usr/src/uts/sun4u/starcat/Makefile b/usr/src/uts/sun4u/starcat/Makefile index a22852614f..afdb3d7244 100644 --- a/usr/src/uts/sun4u/starcat/Makefile +++ b/usr/src/uts/sun4u/starcat/Makefile @@ -22,9 +22,10 @@ # #pragma ident "%Z%%M% %I% %E% SMI" # -# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # +# # This makefile drives the production of the sun4u starcat platform # module. # @@ -86,12 +87,7 @@ install: $(ROOT_STARCAT_DIR) $(USR_STARCAT_DIR) \ $(USR_STARCAT_LIB_DIR) \ $(STARCAT_CRYPTO_LINKS) \ genassym unix .WAIT $(STARCAT_CPU_KMODS) \ - $(STARCAT_KMODS) $(CLOSED_STARCAT_KMODS) pcicfg - -pcicfg: $(ROOT_PSM_MISC_DIR_64)/pcicfg.e $(ROOT_STARCAT_MISC_DIR_64) - -@$(RM) $(ROOT_STARCAT_MISC_DIR_64)/$@ - $(SYMLINK) ../../../../sun4u/kernel/misc/sparcv9/pcicfg.e \ - $(ROOT_STARCAT_MISC_DIR_64)/$@ + $(STARCAT_KMODS) $(CLOSED_STARCAT_KMODS) genassym unix $(STARCAT_KMODS) $(STARCAT_CPU_KMODS): FRC @cd $@; pwd; $(MAKE) $(NO_STATE) $(TARGET) diff --git a/usr/src/uts/sun4u/sys/Makefile b/usr/src/uts/sun4u/sys/Makefile index 21c7f3eca7..0de14065cc 100644 --- a/usr/src/uts/sun4u/sys/Makefile +++ b/usr/src/uts/sun4u/sys/Makefile @@ -50,6 +50,7 @@ SUN4_HDRS= \ eeprom.h \ errclassify.h \ fcode.h \ + fc_plat.h \ idprom.h \ intr.h \ intreg.h \ @@ -80,7 +81,6 @@ HDRS= \ envctrl_ue250.h \ envctrl_ue450.h \ gpio_87317.h \ - fc_plat.h \ iocache.h \ iommu.h \ machasi.h \ diff --git a/usr/src/uts/sun4v/io/px/px_lib4v.c b/usr/src/uts/sun4v/io/px/px_lib4v.c index 298e832aed..ed5b463c84 100644 --- a/usr/src/uts/sun4v/io/px/px_lib4v.c +++ b/usr/src/uts/sun4v/io/px/px_lib4v.c @@ -20,7 +20,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -40,6 +40,7 @@ #include <sys/hypervisor_api.h> #include <px_obj.h> #include <sys/machsystm.h> +#include <sys/hotplug/pci/pcihp.h> #include "px_lib4v.h" #include "px_err.h" @@ -72,6 +73,14 @@ px_lib_dev_init(dev_info_t *dip, devhandle_t *dev_hdl) ddi_prop_free(rp); + /* + * hotplug implementation requires this property to be associated with + * any indirect PCI config access services + */ + (void) ddi_prop_update_int(makedevice(ddi_driver_major(dip), + PCIHP_AP_MINOR_NUM(ddi_get_instance(dip), PCIHP_DEVCTL_MINOR)), dip, + PCI_BUS_CONF_MAP_PROP, 1); + DBG(DBG_ATTACH, dip, "px_lib_dev_init: dev_hdl 0x%llx\n", *dev_hdl); return (DDI_SUCCESS); @@ -83,6 +92,10 @@ px_lib_dev_fini(dev_info_t *dip) { DBG(DBG_DETACH, dip, "px_lib_dev_fini: dip 0x%p\n", dip); + (void) ddi_prop_remove(makedevice(ddi_driver_major(dip), + PCIHP_AP_MINOR_NUM(ddi_get_instance(dip), PCIHP_DEVCTL_MINOR)), dip, + PCI_BUS_CONF_MAP_PROP); + return (DDI_SUCCESS); } @@ -1824,6 +1837,19 @@ px_fab_set(px_t *px_p, pcie_req_id_t bdf, uint16_t offset, (bdf << PX_RA_BDF_SHIFT), offset, 4, wdata); } +/*ARGSUSED*/ +int +px_lib_hotplug_init(dev_info_t *dip, void *arg) +{ + return (DDI_ENOTSUP); +} + +/*ARGSUSED*/ +void +px_lib_hotplug_uninit(dev_info_t *dip) +{ +} + /* Dummy cpr add callback */ /*ARGSUSED*/ void diff --git a/usr/src/uts/sun4v/px/Makefile b/usr/src/uts/sun4v/px/Makefile index ee1eb57e5d..0ae8987576 100644 --- a/usr/src/uts/sun4v/px/Makefile +++ b/usr/src/uts/sun4v/px/Makefile @@ -22,7 +22,7 @@ # # uts/sun4v/px/Makefile # -# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" @@ -78,7 +78,7 @@ CFLAGS += -dalign # Dependency # -LDFLAGS += -dy -Nmisc/busra -Nmisc/pcie +LDFLAGS += -dy -Nmisc/busra -Nmisc/pcie -Nmisc/pciehpc -Nmisc/pcihp # Default build targets. # |
