diff options
Diffstat (limited to 'usr/src')
46 files changed, 3568 insertions, 4517 deletions
diff --git a/usr/src/lib/fm/topo/modules/common/hostbridge/hostbridge.h b/usr/src/lib/fm/topo/modules/common/hostbridge/hostbridge.h index ee76315446..0854175722 100644 --- a/usr/src/lib/fm/topo/modules/common/hostbridge/hostbridge.h +++ b/usr/src/lib/fm/topo/modules/common/hostbridge/hostbridge.h @@ -20,15 +20,13 @@ */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _HOSTBRIDGE_H #define _HOSTBRIDGE_H -#pragma ident "%Z%%M% %I% %E% SMI" - #include <libdevinfo.h> #ifdef __cplusplus @@ -45,7 +43,7 @@ extern "C" { #define SCHIZO "pcisch" #define PSYCHO "pcipsy" #define NPE "npe" -#define PCIE_PCI "pcie_pci" +#define PCIEB "pcieb" #define PCI_PCI "pci_pci" #define PCI "pci" #define PX "px" diff --git a/usr/src/lib/fm/topo/modules/i86pc/hostbridge/hb_i86pc.c b/usr/src/lib/fm/topo/modules/i86pc/hostbridge/hb_i86pc.c index b56d00935c..b8d89db32d 100644 --- a/usr/src/lib/fm/topo/modules/i86pc/hostbridge/hb_i86pc.c +++ b/usr/src/lib/fm/topo/modules/i86pc/hostbridge/hb_i86pc.c @@ -20,12 +20,10 @@ */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <fm/topo_mod.h> #include <fm/topo_hc.h> #include <libdevinfo.h> @@ -120,7 +118,7 @@ pci_hostbridges_find(topo_mod_t *mod, tnode_t *ptn) } hbcnt++; } - if (strcmp(di_driver_name(cnode), PCIE_PCI) == 0) { + if (strcmp(di_driver_name(cnode), PCIEB) == 0) { if (rc_process(mod, ptn, hbcnt, cnode) < 0) { if (hbcnt == 0) topo_node_range_destroy(ptn, diff --git a/usr/src/pkgdefs/SUNWckr/prototype_i386 b/usr/src/pkgdefs/SUNWckr/prototype_i386 index c36ca4dd08..8a0cdd82c4 100644 --- a/usr/src/pkgdefs/SUNWckr/prototype_i386 +++ b/usr/src/pkgdefs/SUNWckr/prototype_i386 @@ -119,8 +119,8 @@ f none kernel/drv/nulldriver 755 root sys f none kernel/drv/openeepr 755 root sys f none kernel/drv/options 755 root sys f none kernel/drv/pci_pci 755 root sys -f none kernel/drv/pcie_pci 755 root sys -f none kernel/drv/pcie_pci.conf 644 root sys +f none kernel/drv/pcieb 755 root sys +f none kernel/drv/pcieb.conf 644 root sys f none kernel/drv/physmem 755 root sys f none kernel/drv/poll 755 root sys f none kernel/drv/power 755 root sys @@ -338,7 +338,7 @@ f none kernel/drv/amd64/nulldriver 755 root sys f none kernel/drv/amd64/openeepr 755 root sys f none kernel/drv/amd64/options 755 root sys f none kernel/drv/amd64/pci_pci 755 root sys -f none kernel/drv/amd64/pcie_pci 755 root sys +f none kernel/drv/amd64/pcieb 755 root sys f none kernel/drv/amd64/physmem 755 root sys f none kernel/drv/amd64/poll 755 root sys f none kernel/drv/amd64/power 755 root sys diff --git a/usr/src/pkgdefs/SUNWckr/prototype_sparc b/usr/src/pkgdefs/SUNWckr/prototype_sparc index b87c24d06e..334dda12f0 100644 --- a/usr/src/pkgdefs/SUNWckr/prototype_sparc +++ b/usr/src/pkgdefs/SUNWckr/prototype_sparc @@ -63,8 +63,7 @@ d none kernel/dacf/sparcv9 755 root sys f none kernel/dacf/sparcv9/consconfig_dacf 755 root sys f none kernel/dacf/sparcv9/net_dacf 755 root sys f none kernel/drv/dad.conf 644 root sys -f none kernel/drv/px_pci.conf 644 root sys -f none kernel/drv/pxb_plx.conf 644 root sys +f none kernel/drv/pcieb.conf 644 root sys e sdconf kernel/drv/sd.conf 644 root sys e preserve kernel/drv/uata.conf 644 root sys d none kernel/drv/sparcv9 755 root sys @@ -113,9 +112,8 @@ f none kernel/drv/sparcv9/poll 755 root sys f none kernel/drv/sparcv9/pseudo 755 root sys f none kernel/drv/sparcv9/ptc 755 root sys f none kernel/drv/sparcv9/ptsl 755 root sys -f none kernel/drv/sparcv9/px_pci 755 root sys -f none kernel/drv/sparcv9/pxb_bcm 755 root sys -f none kernel/drv/sparcv9/pxb_plx 755 root sys +f none kernel/drv/sparcv9/pcieb 755 root sys +f none kernel/drv/sparcv9/pcieb_bcm 755 root sys f none kernel/drv/sparcv9/ramdisk 755 root sys f none kernel/drv/sparcv9/random 755 root sys f none kernel/drv/sparcv9/rts 755 root sys diff --git a/usr/src/pkgdefs/common_files/i.drvalias b/usr/src/pkgdefs/common_files/i.drvalias index 0dd7c5c391..060454af42 100644 --- a/usr/src/pkgdefs/common_files/i.drvalias +++ b/usr/src/pkgdefs/common_files/i.drvalias @@ -21,10 +21,9 @@ # # -# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -#ident "%Z%%M% %I% %E% SMI" PATH=/usr/bin:/usr/sbin:$PATH; export PATH @@ -89,6 +88,28 @@ obsolete_sparc() -e '/^[ ]*px_pci[ ][ ]*"pci10b5,8532"[ #].*$/d' \ -e '/^[ ]*px_pci[ ][ ]*"pci10b5,8516"$/d' \ -e '/^[ ]*px_pci[ ][ ]*"pci10b5,8516"[ #].*$/d' \ + -e '/^[ ]*px_pci[ ][ ]*"pciexclass,060400"$/d' \ + -e '/^[ ]*px_pci[ ][ ]*"pciexclass,060400"[ #].*$/d' \ + -e '/^[ ]*pxb_bcm[ ][ ]*"pciex1166,103"$/d' \ + -e '/^[ ]*pxb_bcm[ ][ ]*"pciex1166,103"[ #].*$/d' \ + -e '/^[ ]*pxb_plx[ ][ ]*"pciex10b5,8114"$/d' \ + -e '/^[ ]*pxb_plx[ ][ ]*"pciex10b5,8114"[ #].*$/d' \ + -e '/^[ ]*pxb_plx[ ][ ]*"pciex10b5,8532"$/d' \ + -e '/^[ ]*pxb_plx[ ][ ]*"pciex10b5,8532"[ #].*$/d' \ + -e '/^[ ]*pxb_plx[ ][ ]*"pciex10b5,8516"$/d' \ + -e '/^[ ]*pxb_plx[ ][ ]*"pciex10b5,8516"[ #].*$/d' \ + -e '/^[ ]*pxb_plx[ ][ ]*"pciex10b5,8548"$/d' \ + -e '/^[ ]*pxb_plx[ ][ ]*"pciex10b5,8548"[ #].*$/d' \ + -e '/^[ ]*pxb_plx[ ][ ]*"pciex10b5,8533"$/d' \ + -e '/^[ ]*pxb_plx[ ][ ]*"pciex10b5,8533"[ #].*$/d' \ + -e '/^[ ]*pxb_plx[ ][ ]*"pciex10b5,8517"$/d' \ + -e '/^[ ]*pxb_plx[ ][ ]*"pciex10b5,8517"[ #].*$/d' \ + -e '/^[ ]*pxb_plx[ ][ ]*"pciex10b5,8518"$/d' \ + -e '/^[ ]*pxb_plx[ ][ ]*"pciex10b5,8518"[ #].*$/d' \ + -e '/^[ ]*pxb_plx[ ][ ]*"pciex108e,9010"$/d' \ + -e '/^[ ]*pxb_plx[ ][ ]*"pciex108e,9010"[ #].*$/d' \ + -e '/^[ ]*pxb_plx[ ][ ]*"pciex108e,9020"$/d' \ + -e '/^[ ]*pxb_plx[ ][ ]*"pciex108e,9020"[ #].*$/d' \ -e '/^[ ]*sx[ ][ ]*SUNW,sx$/d' \ -e '/^[ ]*sx[ ][ ]*SUNW,sx[ #].*$/d' \ -e '/^[ ]*sx[ ][ ]*"SUNW,sx"$/d' \ @@ -150,6 +171,10 @@ obsolete_i386() { -e '/^[ ]*pcie_pci[ ][ ]*"pciex1011,21"[ #].*$/d' \ -e '/^[ ]*pcie_pci[ ][ ]*"pciex1014,22"$/d' \ -e '/^[ ]*pcie_pci[ ][ ]*"pciex1014,22"[ #].*$/d' \ + -e '/^[ ]*pcie_pci[ ][ ]*"pciexclass,060400"$/d' \ + -e '/^[ ]*pcie_pci[ ][ ]*"pciexclass,060400"[ #].*$/d' \ + -e '/^[ ]*pcie_pci[ ][ ]*"pciexclass,060401"$/d' \ + -e '/^[ ]*pcie_pci[ ][ ]*"pciexclass,060401"[ #].*$/d' \ -e '/^[ ]*spwr[ ][ ]*"pci10b8,0005"$/d' \ -e '/^[ ]*spwr[ ][ ]*"pci10b8,0005"[ #].*$/d' \ -e '/^[ ]*chs[ ][ ]*"pci1014,2e"$/d' \ diff --git a/usr/src/pkgdefs/common_files/i.nametomajor b/usr/src/pkgdefs/common_files/i.nametomajor index 12cb51d1aa..cdf1db4e90 100644 --- a/usr/src/pkgdefs/common_files/i.nametomajor +++ b/usr/src/pkgdefs/common_files/i.nametomajor @@ -21,10 +21,9 @@ # # -# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -#ident "%Z%%M% %I% %E% SMI" PATH=/usr/bin:/usr/sbin:$PATH; export PATH @@ -104,6 +103,9 @@ obsolete_sparc() obs["soc"] = 1; obs["sc_nct"] = 1; obs["tomtppm"] = 1; + obs["px_pci"] = 1; + obs["pxb_bcm"] = 1; + obs["pxb_plx"] = 1; } !($1 in obs) { print $0 }' } @@ -179,6 +181,7 @@ obsolete_i386() obs["pci_to_i2o"] = 1; obs["i2o_scsi"] = 1; obs["i2o_bs"] = 1; + obs["pcie_pci"] = 1; } !($1 in obs) { print $0 }' } diff --git a/usr/src/tools/scripts/bfu.sh b/usr/src/tools/scripts/bfu.sh index 383fe21cfe..f63bc624de 100644 --- a/usr/src/tools/scripts/bfu.sh +++ b/usr/src/tools/scripts/bfu.sh @@ -7759,6 +7759,18 @@ mondo_loop() { rmdir $root/usr/include/sys/i2o/ 2>/dev/null # + # Remove px_pci, pxb_plx, pxb_bcm, pcie_pci + # + rm -f $root/kernel/drv/px_pci.conf + rm -f $root/kernel/drv/sparcv9/px_pci + rm -f $root/kernel/drv/pxb_plx.conf + rm -f $root/kernel/drv/sparcv9/pxb_plx + rm -f $root/kernel/drv/sparcv9/pxb_bcm + rm -f $root/kernel/drv/pcie_pci.conf + rm -f $root/kernel/drv/pcie_pci + rm -f $root/kernel/drv/amd64/pcie_pci + + # # Remove /usr/ccs/bin dependency files that now live in # /usr/share/lib/ccs # diff --git a/usr/src/uts/common/Makefile.files b/usr/src/uts/common/Makefile.files index bea36dc659..d06ac373ba 100644 --- a/usr/src/uts/common/Makefile.files +++ b/usr/src/uts/common/Makefile.files @@ -1771,6 +1771,7 @@ INCLUDE_PATH += $(INC_PATH) $(CCYFLAG)$(UTSBASE)/common # PCIE_MISC_OBJS += pcie.o pcie_fault.o +PCIEB_OBJS += pcieb.o pcie_pwr.o # Chelsio N110 10G NIC driver module # diff --git a/usr/src/uts/common/Makefile.rules b/usr/src/uts/common/Makefile.rules index 6bde281698..0049e42f49 100644 --- a/usr/src/uts/common/Makefile.rules +++ b/usr/src/uts/common/Makefile.rules @@ -656,6 +656,10 @@ $(OBJS_DIR)/%.o: $(UTSBASE)/common/io/hme/%.c $(COMPILE.c) -o $@ $< $(CTFCONVERT_O) +$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/pciex/%.c + $(COMPILE.c) -o $@ $< + $(CTFCONVERT_O) + $(OBJS_DIR)/%.o: $(UTSBASE)/common/io/hotplug/hpcsvc/%.c $(COMPILE.c) -o $@ $< $(CTFCONVERT_O) @@ -1875,6 +1879,9 @@ $(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/fcoe/%.c $(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/hme/%.c @($(LHEAD) $(LINT.c) $< $(LTAIL)) +$(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/pciex/%.c + @($(LHEAD) $(LINT.c) $< $(LTAIL)) + $(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/hotplug/hpcsvc/%.c @($(LHEAD) $(LINT.c) $< $(LTAIL)) diff --git a/usr/src/uts/common/io/pcie.c b/usr/src/uts/common/io/pciex/pcie.c index f14d4cb1ea..4f39719d6b 100644 --- a/usr/src/uts/common/io/pcie.c +++ b/usr/src/uts/common/io/pciex/pcie.c @@ -577,8 +577,6 @@ pcie_init_bus(dev_info_t *cdip) /* Save the Header Type */ bus_p->bus_hdr_type = PCIE_GET(8, bus_p, PCI_CONF_HEADER); bus_p->bus_hdr_type &= PCI_HEADER_TYPE_M; - bus_p->bus_pcie2pci_secbus = ddi_prop_get_int(DDI_DEV_T_ANY, cdip, 0, - "pcie2pci-sec-bus", 0); /* Figure out the device type and all the relavant capability offsets */ if ((PCI_CAP_LOCATE(eh, PCI_CAP_ID_PCI_E, &bus_p->bus_pcie_off)) @@ -1027,7 +1025,7 @@ pcie_get_bdf_for_dma_xfer(dev_info_t *dip, dev_info_t *rdip) /* * As part of the probing, the PCI fcode interpreter may setup a DMA * request if a given card has a fcode on it using dip and rdip of the - * AP (attachment point) i.e, dip and rdip of px/px_pci driver. In this + * AP (attachment point) i.e, dip and rdip of px/pcieb driver. In this * case, return a invalid value for the bdf since we cannot get to the * bdf value of the actual device which will be initiating this DMA. */ @@ -1037,7 +1035,7 @@ pcie_get_bdf_for_dma_xfer(dev_info_t *dip, dev_info_t *rdip) cdip = pcie_get_my_childs_dip(dip, rdip); /* - * For a given rdip, return the bdf value of dip's (px or px_pci) + * For a given rdip, return the bdf value of dip's (px or pcieb) * immediate child or secondary bus-id if dip is a PCIe2PCI bridge. * * XXX - For now, return a invalid bdf value for all PCI and PCI-X diff --git a/usr/src/uts/common/io/pcie_fault.c b/usr/src/uts/common/io/pciex/pcie_fault.c index 2dd656a06e..ae540274c3 100644 --- a/usr/src/uts/common/io/pcie_fault.c +++ b/usr/src/uts/common/io/pciex/pcie_fault.c @@ -1850,7 +1850,7 @@ pf_get_pcie_bridge(pf_data_t *pfd_p, pcie_req_id_t secbus) for (bdg_pfd_p = pfd_p->pe_next; bdg_pfd_p; bdg_pfd_p = bdg_pfd_p->pe_next) { if (PCIE_IS_PCIE_BDG(PCIE_PFD2BUS(bdg_pfd_p)) && - PCIE_PFD2BUS(bdg_pfd_p)->bus_pcie2pci_secbus == secbus) + PCIE_PFD2BUS(bdg_pfd_p)->bus_bdg_secbus == secbus) return (bdg_pfd_p); } diff --git a/usr/src/uts/sun4/io/px/pcie_pwr.c b/usr/src/uts/common/io/pciex/pcie_pwr.c index fe96966b3a..bc352cfc71 100644 --- a/usr/src/uts/sun4/io/px/pcie_pwr.c +++ b/usr/src/uts/common/io/pciex/pcie_pwr.c @@ -19,16 +19,13 @@ * CDDL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <sys/types.h> #include <sys/ddi.h> #include <sys/kmem.h> -#include <sys/async.h> #include <sys/sysmacros.h> #include <sys/sunddi.h> #include <sys/sunpm.h> @@ -39,7 +36,7 @@ #include <sys/pcie.h> #include <sys/pcie_impl.h> #include <sys/promif.h> /* prom_printf */ -#include "pcie_pwr.h" +#include <sys/pcie_pwr.h> #if defined(DEBUG) @@ -126,6 +123,11 @@ pcie_power(dev_info_t *dip, int component, int level) int pmcaps = pwr_p->pwr_pmcaps; int ret = DDI_FAILURE; +#if defined(__i386) || defined(__amd64) + if (dip) + return (DDI_SUCCESS); +#endif /* defined(__i386) || defined(__amd64) */ + ASSERT(level != PM_LEVEL_UNKNOWN); /* PM should not asking for a level, which is unsupported */ ASSERT(level == PM_LEVEL_D0 || level == PM_LEVEL_D3 || @@ -276,6 +278,11 @@ pcie_bus_power(dev_info_t *dip, void *impl_arg, pm_bus_power_op_t op, int rv = DDI_SUCCESS; int level_allowed, comp; +#if defined(__i386) || defined(__amd64) + if (dip) + return (DDI_SUCCESS); +#endif /* defined(__i386) || defined(__amd64) */ + switch (op) { case BUS_POWER_PRE_NOTIFICATION: case BUS_POWER_POST_NOTIFICATION: @@ -520,7 +527,7 @@ pcie_add_comps(dev_info_t *dip, dev_info_t *cdip, pcie_pwr_t *pwr_p) * Allocate counters per child. This is a part of pcie * pm info. If there is no pcie pm info, allocate it here. * pcie pm info might already be there for pci express nexus - * driver e.g. px_pci. For all leaf nodes, it is allocated here. + * driver e.g. pcieb. For all leaf nodes, it is allocated here. */ if ((pcie_pm_p = PCIE_PMINFO(cdip)) == NULL) { pcie_pm_p = (pcie_pm_t *)kmem_zalloc( @@ -576,7 +583,7 @@ pcie_remove_comps(dev_info_t *dip, dev_info_t *cdip, pcie_pwr_t *pwr_p) } /* - * Power management related initialization common to px and px_pci + * Power management related initialization common to px and pcieb */ int pwr_common_setup(dev_info_t *dip) @@ -822,7 +829,7 @@ pcie_is_pcie(dev_info_t *dip) } /* - * Called by px_attach or pxb_attach:: DDI_RESUME + * Called by px_attach or pcieb_attach:: DDI_RESUME */ int pcie_pwr_resume(dev_info_t *dip) @@ -830,6 +837,11 @@ pcie_pwr_resume(dev_info_t *dip) dev_info_t *cdip; pcie_pwr_t *pwr_p = NULL; +#if defined(__i386) || defined(__amd64) + if (dip) + return (DDI_SUCCESS); +#endif /* defined(__i386) || defined(__amd64) */ + if (PCIE_PMINFO(dip)) pwr_p = PCIE_NEXUS_PMINFO(dip); @@ -908,6 +920,11 @@ pcie_pwr_suspend(dev_info_t *dip) int *child_counters = NULL; /* per child dip counters */ pcie_pwr_t *pwr_p = NULL; +#if defined(__i386) || defined(__amd64) + if (dip) + return (DDI_SUCCESS); +#endif /* defined(__i386) || defined(__amd64) */ + if (PCIE_PMINFO(dip)) pwr_p = PCIE_NEXUS_PMINFO(dip); diff --git a/usr/src/uts/common/io/pciex/pcieb.c b/usr/src/uts/common/io/pciex/pcieb.c new file mode 100644 index 0000000000..fa2a9d8f85 --- /dev/null +++ b/usr/src/uts/common/io/pciex/pcieb.c @@ -0,0 +1,2016 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * Common x86 and SPARC PCI-E to PCI bus bridge nexus driver + */ + +#include <sys/sysmacros.h> +#include <sys/conf.h> +#include <sys/kmem.h> +#include <sys/debug.h> +#include <sys/modctl.h> +#include <sys/autoconf.h> +#include <sys/ddi_impldefs.h> +#include <sys/pci.h> +#include <sys/ddi.h> +#include <sys/sunddi.h> +#include <sys/sunndi.h> +#include <sys/fm/util.h> +#include <sys/pcie.h> +#include <sys/pci_cap.h> +#include <sys/pcie_impl.h> +#include <sys/hotplug/pci/pcihp.h> +#include <sys/hotplug/pci/pciehpc.h> +#include <sys/hotplug/pci/pcishpc.h> +#include <sys/open.h> +#include <sys/stat.h> +#include <sys/file.h> +#include <sys/promif.h> /* prom_printf */ +#include <sys/disp.h> +#include <sys/pcie_pwr.h> +#include "pcieb.h" +#ifdef PX_PLX +#include <io/pciex/pcieb_plx.h> +#endif /* PX_PLX */ + +/*LINTLIBRARY*/ + +/* panic flag */ +int pcieb_die = PF_ERR_FATAL_FLAGS; + +/* flag to turn on MSI support */ +int pcieb_enable_msi = 1; + +#if defined(DEBUG) +uint_t pcieb_dbg_print = 0; + +static char *pcieb_debug_sym [] = { /* same sequence as pcieb_debug_bit */ + /* 0 */ "attach", + /* 1 */ "pwr", + /* 2 */ "intr" +}; +#endif /* DEBUG */ + +static int pcieb_bus_map(dev_info_t *, dev_info_t *, ddi_map_req_t *, off_t, + off_t, caddr_t *); +static int pcieb_ctlops(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *, + void *); +static int pcieb_fm_init(pcieb_devstate_t *pcieb_p); +static void pcieb_fm_fini(pcieb_devstate_t *pcieb_p); +static int pcieb_fm_init_child(dev_info_t *dip, dev_info_t *cdip, int cap, + ddi_iblock_cookie_t *ibc_p); +static int pcieb_dma_allochdl(dev_info_t *dip, dev_info_t *rdip, + ddi_dma_attr_t *attr_p, int (*waitfp)(caddr_t), caddr_t arg, + ddi_dma_handle_t *handlep); +static int pcieb_dma_mctl(dev_info_t *dip, dev_info_t *rdip, + ddi_dma_handle_t handle, enum ddi_dma_ctlops cmd, off_t *offp, + size_t *lenp, caddr_t *objp, uint_t cache_flags); +static int pcieb_intr_ops(dev_info_t *dip, dev_info_t *rdip, + ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, void *result); + +static struct bus_ops pcieb_bus_ops = { + BUSO_REV, + pcieb_bus_map, + 0, + 0, + 0, + i_ddi_map_fault, + ddi_dma_map, + pcieb_dma_allochdl, + ddi_dma_freehdl, + ddi_dma_bindhdl, + ddi_dma_unbindhdl, + ddi_dma_flush, + ddi_dma_win, + pcieb_dma_mctl, + pcieb_ctlops, + ddi_bus_prop_op, + ndi_busop_get_eventcookie, /* (*bus_get_eventcookie)(); */ + ndi_busop_add_eventcall, /* (*bus_add_eventcall)(); */ + ndi_busop_remove_eventcall, /* (*bus_remove_eventcall)(); */ + ndi_post_event, /* (*bus_post_event)(); */ + NULL, /* (*bus_intr_ctl)(); */ + NULL, /* (*bus_config)(); */ + NULL, /* (*bus_unconfig)(); */ + pcieb_fm_init_child, /* (*bus_fm_init)(); */ + NULL, /* (*bus_fm_fini)(); */ + i_ndi_busop_access_enter, /* (*bus_fm_access_enter)(); */ + i_ndi_busop_access_exit, /* (*bus_fm_access_exit)(); */ + pcie_bus_power, /* (*bus_power)(); */ + pcieb_intr_ops /* (*bus_intr_op)(); */ +}; + +static int pcieb_open(dev_t *, int, int, cred_t *); +static int pcieb_close(dev_t, int, int, cred_t *); +static int pcieb_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); +static int pcieb_prop_op(dev_t, dev_info_t *, ddi_prop_op_t, int, char *, + caddr_t, int *); +static int pcieb_info(dev_info_t *, ddi_info_cmd_t, void *, void **); +static uint_t pcieb_intr_handler(caddr_t arg1, caddr_t arg2); + +/* PM related functions */ +static int pcieb_pwr_setup(dev_info_t *dip); +static int pcieb_pwr_init_and_raise(dev_info_t *dip, pcie_pwr_t *pwr_p); +static void pcieb_pwr_teardown(dev_info_t *dip); +static int pcieb_pwr_disable(dev_info_t *dip); + +/* Hotplug related functions */ +static int pcieb_pciehpc_probe(dev_info_t *dip, ddi_acc_handle_t config_handle); +static int pcieb_pcishpc_probe(dev_info_t *dip, ddi_acc_handle_t config_handle); +static int pcieb_init_hotplug(pcieb_devstate_t *pcieb); +static void pcieb_id_props(pcieb_devstate_t *pcieb); + +/* + * soft state pointer + */ +void *pcieb_state; + +static struct cb_ops pcieb_cb_ops = { + pcieb_open, /* open */ + pcieb_close, /* close */ + nodev, /* strategy */ + nodev, /* print */ + nodev, /* dump */ + nodev, /* read */ + nodev, /* write */ + pcieb_ioctl, /* ioctl */ + nodev, /* devmap */ + nodev, /* mmap */ + nodev, /* segmap */ + nochpoll, /* poll */ + pcieb_prop_op, /* cb_prop_op */ + NULL, /* streamtab */ + D_NEW | D_MP | D_HOTPLUG, /* Driver compatibility flag */ + CB_REV, /* rev */ + nodev, /* int (*cb_aread)() */ + nodev /* int (*cb_awrite)() */ +}; + +static int pcieb_probe(dev_info_t *); +static int pcieb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd); +static int pcieb_detach(dev_info_t *devi, ddi_detach_cmd_t cmd); + +static struct dev_ops pcieb_ops = { + DEVO_REV, /* devo_rev */ + 0, /* refcnt */ + pcieb_info, /* info */ + nulldev, /* identify */ + pcieb_probe, /* probe */ + pcieb_attach, /* attach */ + pcieb_detach, /* detach */ + nulldev, /* reset */ + &pcieb_cb_ops, /* driver operations */ + &pcieb_bus_ops, /* bus operations */ + pcie_power, /* power */ + ddi_quiesce_not_needed, /* quiesce */ +}; + +/* + * Module linkage information for the kernel. + */ + +static struct modldrv modldrv = { + &mod_driverops, /* Type of module */ + "PCIe to PCI nexus driver", + &pcieb_ops, /* driver ops */ +}; + +static struct modlinkage modlinkage = { + MODREV_1, + (void *)&modldrv, + NULL +}; + +/* + * forward function declarations: + */ +static void pcieb_uninitchild(dev_info_t *); +static int pcieb_initchild(dev_info_t *child); +static void pcieb_create_ranges_prop(dev_info_t *, ddi_acc_handle_t); +static boolean_t pcieb_is_pcie_device_type(dev_info_t *dip); + +/* interrupt related declarations */ +static int pcieb_msi_supported(dev_info_t *); +static int pcieb_intr_attach(pcieb_devstate_t *pcieb); +static int pcieb_intr_init(pcieb_devstate_t *pcieb_p, int intr_type); +static void pcieb_intr_fini(pcieb_devstate_t *pcieb_p); + +int +_init(void) +{ + int e; + + if ((e = ddi_soft_state_init(&pcieb_state, sizeof (pcieb_devstate_t), + 1)) == 0 && (e = mod_install(&modlinkage)) != 0) + ddi_soft_state_fini(&pcieb_state); + return (e); +} + +int +_fini(void) +{ + int e; + + if ((e = mod_remove(&modlinkage)) == 0) { + ddi_soft_state_fini(&pcieb_state); + } + return (e); +} + +int +_info(struct modinfo *modinfop) +{ + return (mod_info(&modlinkage, modinfop)); +} + +/*ARGSUSED*/ +static int +pcieb_probe(dev_info_t *devi) +{ + return (DDI_PROBE_SUCCESS); +} + +static int +pcieb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) +{ + int instance; + char device_type[8]; + pcieb_devstate_t *pcieb; + pcie_bus_t *bus_p = PCIE_DIP2UPBUS(devi); + ddi_acc_handle_t config_handle = bus_p->bus_cfg_hdl; + uint8_t dev_type = bus_p->bus_dev_type; + + switch (cmd) { + case DDI_RESUME: + (void) pcie_pwr_resume(devi); + return (DDI_SUCCESS); + + default: + return (DDI_FAILURE); + + case DDI_ATTACH: + break; + } + + if (!(PCIE_IS_BDG(bus_p))) { + PCIEB_DEBUG(DBG_ATTACH, devi, "This is not a switch or" + " bridge\n"); + return (DDI_FAILURE); + } + + /* + * If PCIE_LINKCTL_LINK_DISABLE bit in the PCIe Config + * Space (PCIe Capability Link Control Register) is set, + * then do not bind the driver. + */ + if (PCIE_CAP_GET(16, bus_p, PCIE_LINKCTL) & PCIE_LINKCTL_LINK_DISABLE) + return (DDI_FAILURE); + + /* + * Allocate and get soft state structure. + */ + instance = ddi_get_instance(devi); + if (ddi_soft_state_zalloc(pcieb_state, instance) != DDI_SUCCESS) + return (DDI_FAILURE); + pcieb = ddi_get_soft_state(pcieb_state, instance); + pcieb->pcieb_dip = devi; + pcieb->pcieb_soft_state = PCIEB_SOFT_STATE_CLOSED; + + if ((pcieb_fm_init(pcieb)) != DDI_SUCCESS) { + PCIEB_DEBUG(DBG_ATTACH, devi, "Failed in pcieb_fm_init\n"); + goto fail; + } + pcieb->pcieb_init_flags |= PCIEB_INIT_FM; + + mutex_init(&pcieb->pcieb_mutex, NULL, MUTEX_DRIVER, NULL); + mutex_init(&pcieb->pcieb_err_mutex, NULL, MUTEX_DRIVER, + (void *)pcieb->pcieb_fm_ibc); + mutex_init(&pcieb->pcieb_peek_poke_mutex, NULL, MUTEX_DRIVER, + (void *)pcieb->pcieb_fm_ibc); + + /* create special properties for device identification */ + pcieb_id_props(pcieb); + + /* + * Power management setup. This also makes sure that switch/bridge + * is at D0 during attach. + */ + if (pwr_common_setup(devi) != DDI_SUCCESS) { + PCIEB_DEBUG(DBG_PWR, devi, "pwr_common_setup failed\n"); + goto fail; + } + + if (pcieb_pwr_setup(devi) != DDI_SUCCESS) { + PCIEB_DEBUG(DBG_PWR, devi, "pxb_pwr_setup failed \n"); + goto fail; + } + + /* + * Make sure the "device_type" property exists. + */ + if (pcieb_is_pcie_device_type(devi)) + (void) strcpy(device_type, "pciex"); + else + (void) strcpy(device_type, "pci"); + + (void) ddi_prop_update_string(DDI_DEV_T_NONE, devi, + "device_type", device_type); + + /* + * Check whether the "ranges" property is present. + * Otherwise create the ranges property by reading + * the configuration registers + */ + if (ddi_prop_exists(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS, + "ranges") == 0) { + pcieb_create_ranges_prop(devi, config_handle); + } + + if (PCIE_IS_PCI_BDG(bus_p)) + pcieb_set_pci_perf_parameters(devi, config_handle); + +#ifdef PX_PLX + pcieb_attach_plx_workarounds(pcieb); +#endif /* PX_PLX */ + + /* Initialize hotplug */ + pcieb->pcieb_hotplug_capable = B_FALSE; + + if ((dev_type == PCIE_PCIECAP_DEV_TYPE_DOWN) || + (dev_type == PCIE_PCIECAP_DEV_TYPE_ROOT) || + (dev_type == PCIE_PCIECAP_DEV_TYPE_PCIE2PCI) || + (dev_type == PCIE_PCIECAP_DEV_TYPE_PCI2PCIE)) { + (void) pcieb_init_hotplug(pcieb); + } + + /* + * Initialize interrupt handlers. Ignore return value. + */ + (void) pcieb_intr_attach(pcieb); + + if (pcieb->pcieb_hotplug_capable == B_FALSE) { + /* + * (for non hotplug bus) this would create ":devctl" minor + * node to support DEVCTL_DEVICE_* and DEVCTL_BUS_* ioctls + * to this bus. + */ + if (ddi_create_minor_node(devi, "devctl", S_IFCHR, + PCIHP_AP_MINOR_NUM(instance, PCIHP_DEVCTL_MINOR), + DDI_NT_NEXUS, 0) != DDI_SUCCESS) + goto fail; + } + + PCIEB_DEBUG(DBG_ATTACH, devi, + "pcieb_attach: this nexus %s hotplug slots\n", + pcieb->pcieb_hotplug_capable == B_TRUE ? "has":"has no"); + + /* Do any platform specific workarounds needed at this time */ + pcieb_plat_attach_workaround(devi); + + /* + * If this is a root port, determine and set the max payload size. + * Since this will involve scanning the fabric, all error enabling + * and sw workarounds should be in place before doing this. + */ + if (PCIE_IS_RP(bus_p)) + pcie_init_root_port_mps(devi); + + ddi_report_dev(devi); + return (DDI_SUCCESS); + +fail: + (void) pcieb_detach(devi, DDI_DETACH); + return (DDI_FAILURE); +} + +static int +pcieb_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) +{ + pcieb_devstate_t *pcieb; + int error = DDI_SUCCESS; + + switch (cmd) { + case DDI_SUSPEND: + error = pcie_pwr_suspend(devi); + return (error); + + case DDI_DETACH: + break; + + default: + return (DDI_FAILURE); + } + + pcieb = ddi_get_soft_state(pcieb_state, ddi_get_instance(devi)); + + /* remove interrupt handlers */ + pcieb_intr_fini(pcieb); + + if (pcieb->pcieb_hotplug_capable == B_TRUE) { + if (pcihp_uninit(devi) == DDI_FAILURE) + error = DDI_FAILURE; + + if (pcieb->pcieb_hpc_type == HPC_PCIE) + (void) pciehpc_uninit(devi); + else if (pcieb->pcieb_hpc_type == HPC_SHPC) + (void) pcishpc_uninit(devi); + } else { + ddi_remove_minor_node(devi, "devctl"); + } + + (void) ddi_prop_remove(DDI_DEV_T_NONE, devi, "device_type"); + + (void) ndi_prop_remove(DDI_DEV_T_NONE, pcieb->pcieb_dip, + "pcie_ce_mask"); + + if (pcieb->pcieb_init_flags & PCIEB_INIT_FM) + pcieb_fm_fini(pcieb); + + pcieb_pwr_teardown(devi); + pwr_common_teardown(devi); + + mutex_destroy(&pcieb->pcieb_peek_poke_mutex); + mutex_destroy(&pcieb->pcieb_err_mutex); + mutex_destroy(&pcieb->pcieb_mutex); + + /* + * And finally free the per-pci soft state. + */ + ddi_soft_state_free(pcieb_state, ddi_get_instance(devi)); + + return (DDI_SUCCESS); +} + +static int +pcieb_bus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp, + off_t offset, off_t len, caddr_t *vaddrp) +{ + dev_info_t *pdip; + + pdip = (dev_info_t *)DEVI(dip)->devi_parent; + return ((DEVI(pdip)->devi_ops->devo_bus_ops->bus_map)(pdip, rdip, mp, + offset, len, vaddrp)); +} + +static int +pcieb_ctlops(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t ctlop, + void *arg, void *result) +{ + pci_regspec_t *drv_regp; + int reglen; + int rn; + int totreg; + pcieb_devstate_t *pcieb = ddi_get_soft_state(pcieb_state, + ddi_get_instance(dip)); + struct detachspec *ds; + struct attachspec *as; + + switch (ctlop) { + case DDI_CTLOPS_REPORTDEV: + if (rdip == (dev_info_t *)0) + return (DDI_FAILURE); + cmn_err(CE_CONT, "?PCIE-device: %s@%s, %s%d\n", + ddi_node_name(rdip), ddi_get_name_addr(rdip), + ddi_driver_name(rdip), + ddi_get_instance(rdip)); + return (DDI_SUCCESS); + + case DDI_CTLOPS_INITCHILD: + return (pcieb_initchild((dev_info_t *)arg)); + + case DDI_CTLOPS_UNINITCHILD: + pcieb_uninitchild((dev_info_t *)arg); + return (DDI_SUCCESS); + + case DDI_CTLOPS_SIDDEV: + return (DDI_SUCCESS); + + case DDI_CTLOPS_REGSIZE: + case DDI_CTLOPS_NREGS: + if (rdip == (dev_info_t *)0) + return (DDI_FAILURE); + break; + + case DDI_CTLOPS_PEEK: + case DDI_CTLOPS_POKE: + return (pcieb_plat_peekpoke(dip, rdip, ctlop, arg, result)); + case DDI_CTLOPS_ATTACH: + if (!pcie_is_child(dip, rdip)) + return (DDI_SUCCESS); + + as = (struct attachspec *)arg; + switch (as->when) { + case DDI_PRE: + if (as->cmd == DDI_RESUME) { + pcie_clear_errors(rdip); + if (pcieb_plat_ctlops(rdip, ctlop, arg) != + DDI_SUCCESS) + return (DDI_FAILURE); + } + + if (as->cmd == DDI_ATTACH) + return (pcie_pm_hold(dip)); + + return (DDI_SUCCESS); + + case DDI_POST: + if (as->cmd == DDI_ATTACH && as->result != DDI_SUCCESS) + pcie_pm_release(dip); + + if (as->result == DDI_SUCCESS) { + pf_init(rdip, (void *)pcieb->pcieb_fm_ibc, + as->cmd); + + (void) pcieb_plat_ctlops(rdip, ctlop, arg); + } + + /* + * For empty hotplug-capable slots, we should explicitly + * disable the errors, so that we won't panic upon + * unsupported hotplug messages. + */ + if ((!ddi_prop_exists(DDI_DEV_T_ANY, rdip, + DDI_PROP_DONTPASS, "hotplug-capable")) || + ddi_get_child(rdip)) { + (void) pcie_postattach_child(rdip); + return (DDI_SUCCESS); + } + + pcie_disable_errors(rdip); + + return (DDI_SUCCESS); + default: + break; + } + return (DDI_SUCCESS); + + case DDI_CTLOPS_DETACH: + if (!pcie_is_child(dip, rdip)) + return (DDI_SUCCESS); + + ds = (struct detachspec *)arg; + switch (ds->when) { + case DDI_PRE: + pf_fini(rdip, ds->cmd); + return (DDI_SUCCESS); + + case DDI_POST: + if (pcieb_plat_ctlops(rdip, ctlop, arg) != DDI_SUCCESS) + return (DDI_FAILURE); + if (ds->cmd == DDI_DETACH && + ds->result == DDI_SUCCESS) { + return (pcie_pm_remove_child(dip, rdip)); + } + return (DDI_SUCCESS); + default: + break; + } + return (DDI_SUCCESS); + default: + return (ddi_ctlops(dip, rdip, ctlop, arg, result)); + } + + *(int *)result = 0; + if (ddi_getlongprop(DDI_DEV_T_ANY, rdip, + DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP, "reg", (caddr_t)&drv_regp, + ®len) != DDI_SUCCESS) + return (DDI_FAILURE); + + totreg = reglen / sizeof (pci_regspec_t); + if (ctlop == DDI_CTLOPS_NREGS) + *(int *)result = totreg; + else if (ctlop == DDI_CTLOPS_REGSIZE) { + rn = *(int *)arg; + if (rn >= totreg) { + kmem_free(drv_regp, reglen); + return (DDI_FAILURE); + } + + *(off_t *)result = drv_regp[rn].pci_size_low | + ((uint64_t)drv_regp[rn].pci_size_hi << 32); + } + + kmem_free(drv_regp, reglen); + return (DDI_SUCCESS); +} + +/* + * name_child + * + * This function is called from init_child to name a node. It is + * also passed as a callback for node merging functions. + * + * return value: DDI_SUCCESS, DDI_FAILURE + */ +static int +pcieb_name_child(dev_info_t *child, char *name, int namelen) +{ + pci_regspec_t *pci_rp; + uint_t slot, func; + char **unit_addr; + uint_t n; + + /* + * For .conf nodes, use unit-address property as name + */ + if (ndi_dev_is_persistent_node(child) == 0) { + if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, child, + DDI_PROP_DONTPASS, "unit-address", &unit_addr, &n) != + DDI_PROP_SUCCESS) { + cmn_err(CE_WARN, + "cannot find unit-address in %s.conf", + ddi_driver_name(child)); + return (DDI_FAILURE); + } + if (n != 1 || *unit_addr == NULL || **unit_addr == 0) { + cmn_err(CE_WARN, "unit-address property in %s.conf" + " not well-formed", ddi_driver_name(child)); + ddi_prop_free(unit_addr); + return (DDI_FAILURE); + } + (void) snprintf(name, namelen, "%s", *unit_addr); + ddi_prop_free(unit_addr); + return (DDI_SUCCESS); + } + + /* + * Get the address portion of the node name based on + * the function and device number. + */ + if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child, + DDI_PROP_DONTPASS, "reg", (int **)&pci_rp, &n) != DDI_SUCCESS) { + return (DDI_FAILURE); + } + + /* copy the device identifications */ + slot = PCI_REG_DEV_G(pci_rp[0].pci_phys_hi); + func = PCI_REG_FUNC_G(pci_rp[0].pci_phys_hi); + + if (func != 0) + (void) snprintf(name, namelen, "%x,%x", slot, func); + else + (void) snprintf(name, namelen, "%x", slot); + + ddi_prop_free(pci_rp); + return (DDI_SUCCESS); +} + +static int +pcieb_initchild(dev_info_t *child) +{ + char name[MAXNAMELEN]; + int result = DDI_FAILURE; + pcieb_devstate_t *pcieb = + (pcieb_devstate_t *)ddi_get_soft_state(pcieb_state, + ddi_get_instance(ddi_get_parent(child))); + + /* + * Name the child + */ + if (pcieb_name_child(child, name, MAXNAMELEN) != DDI_SUCCESS) { + result = DDI_FAILURE; + goto done; + } + ddi_set_name_addr(child, name); + + /* + * Pseudo nodes indicate a prototype node with per-instance + * properties to be merged into the real h/w device node. + * The interpretation of the unit-address is DD[,F] + * where DD is the device id and F is the function. + */ + if (ndi_dev_is_persistent_node(child) == 0) { + extern int pci_allow_pseudo_children; + + /* + * Try to merge the properties from this prototype + * node into real h/w nodes. + */ + if (ndi_merge_node(child, pcieb_name_child) != DDI_SUCCESS) { + /* + * Merged ok - return failure to remove the node. + */ + ddi_set_name_addr(child, NULL); + result = DDI_FAILURE; + goto done; + } + + /* workaround for ddivs to run under PCI-E */ + if (pci_allow_pseudo_children) { + result = DDI_SUCCESS; + goto done; + } + + /* + * The child was not merged into a h/w node, + * but there's not much we can do with it other + * than return failure to cause the node to be removed. + */ + cmn_err(CE_WARN, "!%s@%s: %s.conf properties not merged", + ddi_driver_name(child), ddi_get_name_addr(child), + ddi_driver_name(child)); + ddi_set_name_addr(child, NULL); + result = DDI_NOT_WELL_FORMED; + goto done; + } + + /* platform specific initchild */ + pcieb_plat_initchild(child); + + if (pcie_pm_hold(pcieb->pcieb_dip) != DDI_SUCCESS) { + PCIEB_DEBUG(DBG_PWR, pcieb->pcieb_dip, + "INITCHILD: px_pm_hold failed\n"); + result = DDI_FAILURE; + goto done; + } + /* Any return from here must call pcie_pm_release */ + + /* + * If configuration registers were previously saved by + * child (before it entered D3), then let the child do the + * restore to set up the config regs as it'll first need to + * power the device out of D3. + */ + if (ddi_prop_exists(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, + "config-regs-saved-by-child") == 1) { + PCIEB_DEBUG(DBG_PWR, ddi_get_parent(child), + "INITCHILD: config regs to be restored by child" + " for %s@%s\n", ddi_node_name(child), + ddi_get_name_addr(child)); + + result = DDI_SUCCESS; + goto cleanup; + } + + PCIEB_DEBUG(DBG_PWR, ddi_get_parent(child), + "INITCHILD: config regs setup for %s@%s\n", + ddi_node_name(child), ddi_get_name_addr(child)); + + if (!pcie_init_bus(child) || pcie_initchild(child) != DDI_SUCCESS) { + result = DDI_FAILURE; + goto cleanup; + } + +#ifdef PX_PLX + if (pcieb_init_plx_workarounds(pcieb, child) == DDI_FAILURE) { + result = DDI_FAILURE; + goto cleanup; + } +#endif /* PX_PLX */ + + result = DDI_SUCCESS; +cleanup: + pcie_pm_release(pcieb->pcieb_dip); +done: + return (result); +} + +static void +pcieb_uninitchild(dev_info_t *dip) +{ + + pcie_uninitchild(dip); + + pcieb_plat_uninitchild(dip); + + ddi_set_name_addr(dip, NULL); + + /* + * Strip the node to properly convert it back to prototype form + */ + ddi_remove_minor_node(dip, NULL); + + ddi_prop_remove_all(dip); +} + +static boolean_t +pcieb_is_pcie_device_type(dev_info_t *dip) +{ + pcie_bus_t *bus_p = PCIE_DIP2BUS(dip); + + if (PCIE_IS_SW(bus_p) || PCIE_IS_RP(bus_p) || PCIE_IS_PCI2PCIE(bus_p)) + return (B_TRUE); + + return (B_FALSE); +} + +static int +pcieb_intr_attach(pcieb_devstate_t *pcieb) +{ + int intr_types; + dev_info_t *dip = pcieb->pcieb_dip; + + /* Allow platform specific code to do any initialization first */ + pcieb_plat_intr_attach(pcieb); + + /* + * 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(dip, &intr_types) != DDI_SUCCESS) { + PCIEB_DEBUG(DBG_ATTACH, dip, "ddi_intr_get_supported_types" + " failed\n"); + goto FAIL; + } + + if ((intr_types & DDI_INTR_TYPE_MSI) && + (pcieb_msi_supported(dip) == DDI_SUCCESS)) { + if (pcieb_intr_init(pcieb, DDI_INTR_TYPE_MSI) == DDI_SUCCESS) + intr_types = DDI_INTR_TYPE_MSI; + else { + PCIEB_DEBUG(DBG_ATTACH, dip, "Unable to attach MSI" + " handler\n"); + } + } + + if (intr_types != DDI_INTR_TYPE_MSI) { + /* + * MSIs are not supported or MSI initialization failed. For Root + * Ports mark this so error handling might try to fallback to + * some other mechanism if available (machinecheck etc.). + */ + if (PCIE_IS_RP(PCIE_DIP2UPBUS(dip))) + pcieb->pcieb_no_aer_msi = B_TRUE; + } + + if (intr_types & DDI_INTR_TYPE_FIXED) { + if (pcieb_intr_init(pcieb, DDI_INTR_TYPE_FIXED) != + DDI_SUCCESS) { + PCIEB_DEBUG(DBG_ATTACH, dip, + "Unable to attach INTx handler\n"); + goto FAIL; + } + } + return (DDI_SUCCESS); + +FAIL: + return (DDI_FAILURE); +} + +/* + * This function initializes internally generated interrupts only. + * It does not affect any interrupts generated by downstream devices + * or the forwarding of them. + * + * Enable Device Specific Interrupts or Hotplug features here. + * Enabling features may change how many interrupts are requested + * by the device. If features are not enabled first, the + * device might not ask for any interrupts. + */ +static int +pcieb_intr_init(pcieb_devstate_t *pcieb, int intr_type) +{ + dev_info_t *dip = pcieb->pcieb_dip; + int nintrs, request, count, x; + int intr_cap = 0; + int inum = 0; + int ret, hp_msi_off; + pcie_bus_t *bus_p = PCIE_DIP2UPBUS(dip); + uint16_t vendorid = bus_p->bus_dev_ven_id & 0xFFFF; + boolean_t is_hp = B_FALSE; + boolean_t is_pme = B_FALSE; + + PCIEB_DEBUG(DBG_ATTACH, dip, "pcieb_intr_init: Attaching %s handler\n", + (intr_type == DDI_INTR_TYPE_MSI) ? "MSI" : "INTx"); + + request = 0; + if (pcieb->pcieb_hotplug_capable) { + request++; + is_hp = B_TRUE; + } + + /* + * Hotplug and PME share the same MSI vector. If hotplug is not + * supported check if MSI is needed for PME. + */ + if ((intr_type == DDI_INTR_TYPE_MSI) && PCIE_IS_RP(bus_p) && + (vendorid == NVIDIA_VENDOR_ID)) { + is_pme = B_TRUE; + if (!is_hp) + request++; + } + + /* + * Setup MSI if this device is a Rootport and has AER. Currently no + * SPARC Root Port supports fabric errors being reported through it. + */ + if (intr_type == DDI_INTR_TYPE_MSI) { + if (PCIE_IS_RP(bus_p) && PCIE_HAS_AER(bus_p)) + request++; + } + + if (request == 0) + return (DDI_SUCCESS); + + /* + * Get number of supported interrupts. + * + * Several Bridges/Switches will not have this property set, resulting + * in a FAILURE, if the device is not configured in a way that + * interrupts are needed. (eg. hotplugging) + */ + ret = ddi_intr_get_nintrs(dip, intr_type, &nintrs); + if ((ret != DDI_SUCCESS) || (nintrs == 0)) { + PCIEB_DEBUG(DBG_ATTACH, dip, "ddi_intr_get_nintrs ret:%d" + " req:%d\n", ret, nintrs); + return (DDI_FAILURE); + } + + PCIEB_DEBUG(DBG_ATTACH, dip, "bdf 0x%x: ddi_intr_get_nintrs: nintrs %d", + " request %d\n", bus_p->bus_bdf, nintrs, request); + + if (request > nintrs) + request = nintrs; + + /* Allocate an array of interrupt handlers */ + pcieb->pcieb_htable_size = sizeof (ddi_intr_handle_t) * request; + pcieb->pcieb_htable = kmem_zalloc(pcieb->pcieb_htable_size, + KM_SLEEP); + pcieb->pcieb_init_flags |= PCIEB_INIT_HTABLE; + + ret = ddi_intr_alloc(dip, pcieb->pcieb_htable, intr_type, inum, + request, &count, DDI_INTR_ALLOC_NORMAL); + if ((ret != DDI_SUCCESS) || (count == 0)) { + PCIEB_DEBUG(DBG_ATTACH, dip, "ddi_intr_alloc() ret: %d ask: %d" + " actual: %d\n", ret, request, count); + goto FAIL; + } + pcieb->pcieb_init_flags |= PCIEB_INIT_ALLOC; + + /* Save the actual number of interrupts allocated */ + pcieb->pcieb_intr_count = count; + if (count < request) { + PCIEB_DEBUG(DBG_ATTACH, dip, "bdf 0%x: Requested Intr: %d" + " Received: %d\n", bus_p->bus_bdf, request, count); + } + + /* + * NVidia (MCP55 and other) chipsets have a errata that if the number + * of requested MSI intrs is not allocated we have to fall back to INTx. + */ + if (intr_type == DDI_INTR_TYPE_MSI) { + if (PCIE_IS_RP(bus_p) && (vendorid == NVIDIA_VENDOR_ID)) { + if (request != count) + goto FAIL; + } + } + + /* Get interrupt priority */ + ret = ddi_intr_get_pri(pcieb->pcieb_htable[0], + &pcieb->pcieb_intr_priority); + if (ret != DDI_SUCCESS) { + PCIEB_DEBUG(DBG_ATTACH, dip, "ddi_intr_get_pri() ret: %d\n", + ret); + goto FAIL; + } + + if (pcieb->pcieb_intr_priority >= LOCK_LEVEL) { + pcieb->pcieb_intr_priority = LOCK_LEVEL - 1; + ret = ddi_intr_set_pri(pcieb->pcieb_htable[0], + pcieb->pcieb_intr_priority); + if (ret != DDI_SUCCESS) { + PCIEB_DEBUG(DBG_ATTACH, dip, "ddi_intr_set_pri() ret:" + " %d\n", ret); + + goto FAIL; + } + } + + mutex_init(&pcieb->pcieb_intr_mutex, NULL, MUTEX_DRIVER, NULL); + + pcieb->pcieb_init_flags |= PCIEB_INIT_MUTEX; + + for (count = 0; count < pcieb->pcieb_intr_count; count++) { + ret = ddi_intr_add_handler(pcieb->pcieb_htable[count], + pcieb_intr_handler, (caddr_t)pcieb, + (caddr_t)(uintptr_t)(inum + count)); + + if (ret != DDI_SUCCESS) { + PCIEB_DEBUG(DBG_ATTACH, dip, "Cannot add " + "interrupt(%d)\n", ret); + break; + } + } + + /* If unsucessful, remove the added handlers */ + if (ret != DDI_SUCCESS) { + for (x = 0; x < count; x++) { + (void) ddi_intr_remove_handler(pcieb->pcieb_htable[x]); + } + goto FAIL; + } + + pcieb->pcieb_init_flags |= PCIEB_INIT_HANDLER; + + (void) ddi_intr_get_cap(pcieb->pcieb_htable[0], &intr_cap); + + /* + * Get this intr lock because we are not quite ready to handle + * interrupts immediately after enabling it. The MSI multi register + * gets programmed in ddi_intr_enable after which we need to get the + * MSI offsets for Hotplug/AER. + */ + mutex_enter(&pcieb->pcieb_intr_mutex); + + if (intr_cap & DDI_INTR_FLAG_BLOCK) { + (void) ddi_intr_block_enable(pcieb->pcieb_htable, + pcieb->pcieb_intr_count); + pcieb->pcieb_init_flags |= PCIEB_INIT_BLOCK; + } else { + for (count = 0; count < pcieb->pcieb_intr_count; count++) { + (void) ddi_intr_enable(pcieb->pcieb_htable[count]); + } + } + pcieb->pcieb_init_flags |= PCIEB_INIT_ENABLE; + + /* Save the interrupt type */ + pcieb->pcieb_intr_type = intr_type; + + /* Get the MSI offset for hotplug/PME from the PCIe cap reg */ + if (intr_type == DDI_INTR_TYPE_MSI) { + hp_msi_off = PCI_CAP_GET16(bus_p->bus_cfg_hdl, NULL, + bus_p->bus_pcie_off, PCIE_PCIECAP) & + PCIE_PCIECAP_INT_MSG_NUM; + + if (hp_msi_off >= count) { + PCIEB_DEBUG(DBG_ATTACH, dip, "MSI number %d in PCIe " + "cap > max allocated %d\n", hp_msi_off, count); + mutex_exit(&pcieb->pcieb_intr_mutex); + goto FAIL; + } + + if (is_hp) + pcieb->pcieb_isr_tab[hp_msi_off] |= PCIEB_INTR_SRC_HP; + + if (is_pme) + pcieb->pcieb_isr_tab[hp_msi_off] |= PCIEB_INTR_SRC_PME; + } else { + /* INTx handles only Hotplug interrupts */ + if (is_hp) + pcieb->pcieb_isr_tab[0] |= PCIEB_INTR_SRC_HP; + } + + + /* + * Get the MSI offset for errors from the AER Root Error status + * register. + */ + if ((intr_type == DDI_INTR_TYPE_MSI) && PCIE_IS_RP(bus_p)) { + if (PCIE_HAS_AER(bus_p)) { + int aer_msi_off; + aer_msi_off = (PCI_XCAP_GET32(bus_p->bus_cfg_hdl, NULL, + bus_p->bus_aer_off, PCIE_AER_RE_STS) >> + PCIE_AER_RE_STS_MSG_NUM_SHIFT) & + PCIE_AER_RE_STS_MSG_NUM_MASK; + + if (aer_msi_off >= count) { + PCIEB_DEBUG(DBG_ATTACH, dip, "MSI number %d in" + " AER cap > max allocated %d\n", + aer_msi_off, count); + mutex_exit(&pcieb->pcieb_intr_mutex); + goto FAIL; + } + pcieb->pcieb_isr_tab[aer_msi_off] |= PCIEB_INTR_SRC_AER; + } else { + /* + * This RP does not have AER. Fallback to the + * SERR+Machinecheck approach if available. + */ + pcieb->pcieb_no_aer_msi = B_TRUE; + } + } + + mutex_exit(&pcieb->pcieb_intr_mutex); + + return (DDI_SUCCESS); + +FAIL: + return (DDI_FAILURE); +} + +static void +pcieb_intr_fini(pcieb_devstate_t *pcieb) +{ + int x; + int count = pcieb->pcieb_intr_count; + int flags = pcieb->pcieb_init_flags; + + if ((flags & PCIEB_INIT_ENABLE) && + (flags & PCIEB_INIT_BLOCK)) { + (void) ddi_intr_block_disable(pcieb->pcieb_htable, count); + flags &= ~(PCIEB_INIT_ENABLE | + PCIEB_INIT_BLOCK); + } + + if (flags & PCIEB_INIT_MUTEX) + mutex_destroy(&pcieb->pcieb_intr_mutex); + + for (x = 0; x < count; x++) { + if (flags & PCIEB_INIT_ENABLE) + (void) ddi_intr_disable(pcieb->pcieb_htable[x]); + + if (flags & PCIEB_INIT_HANDLER) + (void) ddi_intr_remove_handler(pcieb->pcieb_htable[x]); + + if (flags & PCIEB_INIT_ALLOC) + (void) ddi_intr_free(pcieb->pcieb_htable[x]); + } + + flags &= ~(PCIEB_INIT_ENABLE | PCIEB_INIT_HANDLER | PCIEB_INIT_ALLOC | + PCIEB_INIT_MUTEX); + + if (flags & PCIEB_INIT_HTABLE) + kmem_free(pcieb->pcieb_htable, pcieb->pcieb_htable_size); + + flags &= ~PCIEB_INIT_HTABLE; + + pcieb->pcieb_init_flags &= flags; +} + +/* + * Checks if this device needs MSIs enabled or not. + */ +/*ARGSUSED*/ +static int +pcieb_msi_supported(dev_info_t *dip) +{ + return ((pcieb_enable_msi && pcieb_plat_msi_supported(dip)) ? + DDI_SUCCESS: DDI_FAILURE); +} + +/*ARGSUSED*/ +int +pcieb_fm_init_child(dev_info_t *dip, dev_info_t *tdip, int cap, + ddi_iblock_cookie_t *ibc) +{ + pcieb_devstate_t *pcieb = ddi_get_soft_state(pcieb_state, + ddi_get_instance(dip)); + + ASSERT(ibc != NULL); + *ibc = pcieb->pcieb_fm_ibc; + + return (DEVI(dip)->devi_fmhdl->fh_cap | DDI_FM_ACCCHK_CAPABLE | + DDI_FM_DMACHK_CAPABLE); +} + +static int +pcieb_fm_init(pcieb_devstate_t *pcieb_p) +{ + dev_info_t *dip = pcieb_p->pcieb_dip; + int fm_cap = DDI_FM_EREPORT_CAPABLE; + + /* + * Request our capability level and get our parents capability + * and ibc. + */ + ddi_fm_init(dip, &fm_cap, &pcieb_p->pcieb_fm_ibc); + + return (DDI_SUCCESS); +} + +/* + * Breakdown our FMA resources + */ +static void +pcieb_fm_fini(pcieb_devstate_t *pcieb_p) +{ + /* + * Clean up allocated fm structures + */ + ddi_fm_fini(pcieb_p->pcieb_dip); +} + +static int +pcieb_open(dev_t *devp, int flags, int otyp, cred_t *credp) +{ + pcieb_devstate_t *pcieb_p; + minor_t minor = getminor(*devp); + int instance = PCIHP_AP_MINOR_NUM_TO_INSTANCE(minor); + + /* + * Make sure the open is for the right file type. + */ + if (otyp != OTYP_CHR) + return (EINVAL); + + /* + * Get the soft state structure for the device. + */ + pcieb_p = (pcieb_devstate_t *)ddi_get_soft_state(pcieb_state, + instance); + + if (pcieb_p == NULL) + return (ENXIO); + + if (pcieb_p->pcieb_hotplug_capable == B_TRUE) + return ((pcihp_get_cb_ops())->cb_open(devp, flags, + otyp, credp)); + + /* + * Handle the open by tracking the device state. + */ + mutex_enter(&pcieb_p->pcieb_mutex); + if (flags & FEXCL) { + if (pcieb_p->pcieb_soft_state != PCIEB_SOFT_STATE_CLOSED) { + mutex_exit(&pcieb_p->pcieb_mutex); + return (EBUSY); + } + pcieb_p->pcieb_soft_state = PCIEB_SOFT_STATE_OPEN_EXCL; + } else { + if (pcieb_p->pcieb_soft_state == PCIEB_SOFT_STATE_OPEN_EXCL) { + mutex_exit(&pcieb_p->pcieb_mutex); + return (EBUSY); + } + pcieb_p->pcieb_soft_state = PCIEB_SOFT_STATE_OPEN; + } + mutex_exit(&pcieb_p->pcieb_mutex); + return (0); +} + +static int +pcieb_close(dev_t dev, int flags, int otyp, cred_t *credp) +{ + pcieb_devstate_t *pcieb_p; + minor_t minor = getminor(dev); + int instance = PCIHP_AP_MINOR_NUM_TO_INSTANCE(minor); + + if (otyp != OTYP_CHR) + return (EINVAL); + + pcieb_p = (pcieb_devstate_t *)ddi_get_soft_state(pcieb_state, + instance); + + if (pcieb_p == NULL) + return (ENXIO); + + if (pcieb_p->pcieb_hotplug_capable == B_TRUE) + return ((pcihp_get_cb_ops())->cb_close(dev, flags, + otyp, credp)); + + mutex_enter(&pcieb_p->pcieb_mutex); + pcieb_p->pcieb_soft_state = PCIEB_SOFT_STATE_CLOSED; + mutex_exit(&pcieb_p->pcieb_mutex); + return (0); +} + +static int +pcieb_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, + int *rvalp) +{ + pcieb_devstate_t *pcieb_p; + dev_info_t *self; + struct devctl_iocdata *dcp; + uint_t bus_state; + int rv = 0; + minor_t minor = getminor(dev); + int instance = PCIHP_AP_MINOR_NUM_TO_INSTANCE(minor); + + pcieb_p = (pcieb_devstate_t *)ddi_get_soft_state(pcieb_state, + instance); + + if (pcieb_p == NULL) + return (ENXIO); + + self = pcieb_p->pcieb_dip; + if (pcieb_p->pcieb_hotplug_capable == B_TRUE) { + rv = ((pcihp_get_cb_ops())->cb_ioctl(dev, cmd, + arg, mode, credp, rvalp)); + + pcieb_plat_ioctl_hotplug(self, rv, cmd); + return (rv); + } + + /* + * We can use the generic implementation for these ioctls + */ + switch (cmd) { + case DEVCTL_DEVICE_GETSTATE: + case DEVCTL_DEVICE_ONLINE: + case DEVCTL_DEVICE_OFFLINE: + case DEVCTL_BUS_GETSTATE: + return (ndi_devctl_ioctl(self, cmd, arg, mode, 0)); + } + + /* + * read devctl ioctl data + */ + if (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS) + return (EFAULT); + + switch (cmd) { + + case DEVCTL_DEVICE_RESET: + rv = ENOTSUP; + break; + + case DEVCTL_BUS_QUIESCE: + if (ndi_get_bus_state(self, &bus_state) == NDI_SUCCESS) + if (bus_state == BUS_QUIESCED) + break; + (void) ndi_set_bus_state(self, BUS_QUIESCED); + break; + + case DEVCTL_BUS_UNQUIESCE: + if (ndi_get_bus_state(self, &bus_state) == NDI_SUCCESS) + if (bus_state == BUS_ACTIVE) + break; + (void) ndi_set_bus_state(self, BUS_ACTIVE); + break; + + case DEVCTL_BUS_RESET: + rv = ENOTSUP; + break; + + case DEVCTL_BUS_RESETALL: + rv = ENOTSUP; + break; + + default: + rv = ENOTTY; + } + + ndi_dc_freehdl(dcp); + return (rv); +} + +static int +pcieb_prop_op(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op, + int flags, char *name, caddr_t valuep, int *lengthp) +{ + pcieb_devstate_t *pcieb_p; + minor_t minor = getminor(dev); + int instance = PCIHP_AP_MINOR_NUM_TO_INSTANCE(minor); + + pcieb_p = (pcieb_devstate_t *)ddi_get_soft_state(pcieb_state, + instance); + + if (pcieb_p == NULL) + return (ENXIO); + + if (pcieb_p->pcieb_hotplug_capable == B_TRUE) + return ((pcihp_get_cb_ops())->cb_prop_op(dev, dip, prop_op, + flags, name, valuep, lengthp)); + + return (ddi_prop_op(dev, dip, prop_op, flags, name, valuep, lengthp)); +} + +/*ARGSUSED*/ +static int +pcieb_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) +{ + pcieb_devstate_t *pcieb_p; /* per pcieb state pointer */ + minor_t minor = getminor((dev_t)arg); + int instance = PCIHP_AP_MINOR_NUM_TO_INSTANCE(minor); + + pcieb_p = (pcieb_devstate_t *)ddi_get_soft_state(pcieb_state, + instance); + + switch (infocmd) { + default: + return (DDI_FAILURE); + + case DDI_INFO_DEVT2INSTANCE: + *result = (void *)(intptr_t)instance; + return (DDI_SUCCESS); + + case DDI_INFO_DEVT2DEVINFO: + if (pcieb_p == NULL) + return (DDI_FAILURE); + *result = (void *)pcieb_p->pcieb_dip; + return (DDI_SUCCESS); + } +} + +/* + * Common interrupt handler for hotplug, PME and errors. + */ +static uint_t +pcieb_intr_handler(caddr_t arg1, caddr_t arg2) +{ + pcieb_devstate_t *pcieb_p = (pcieb_devstate_t *)arg1; + dev_info_t *dip = pcieb_p->pcieb_dip; + ddi_fm_error_t derr; + int sts = 0; + int ret = DDI_INTR_UNCLAIMED; + int isrc; + + if (!(pcieb_p->pcieb_init_flags & PCIEB_INIT_ENABLE)) + goto FAIL; + + mutex_enter(&pcieb_p->pcieb_intr_mutex); + isrc = pcieb_p->pcieb_isr_tab[(int)(uintptr_t)arg2]; + mutex_exit(&pcieb_p->pcieb_intr_mutex); + + PCIEB_DEBUG(DBG_INTR, dip, "Received intr number %d\n", + (int)(uintptr_t)arg2); + + if (isrc == PCIEB_INTR_SRC_UNKNOWN) + goto FAIL; + + if (isrc & PCIEB_INTR_SRC_HP) { + if (pcieb_p->pcieb_hpc_type == HPC_PCIE) + ret = pciehpc_intr(dip); + else if (pcieb_p->pcieb_hpc_type == HPC_SHPC) + ret = pcishpc_intr(dip); + } + + if (isrc & PCIEB_INTR_SRC_PME) + ret = DDI_INTR_CLAIMED; + + /* AER Error */ + if (isrc & PCIEB_INTR_SRC_AER) { + /* + * If MSI is shared with PME/hotplug then check Root Error + * Status Reg before claiming it. For now it's ok since + * we know we get 2 MSIs. + */ + ret = DDI_INTR_CLAIMED; + bzero(&derr, sizeof (ddi_fm_error_t)); + derr.fme_version = DDI_FME_VERSION; + mutex_enter(&pcieb_p->pcieb_peek_poke_mutex); + mutex_enter(&pcieb_p->pcieb_err_mutex); + + if ((DEVI(dip)->devi_fmhdl->fh_cap) & DDI_FM_EREPORT_CAPABLE) + sts = pf_scan_fabric(dip, &derr, NULL); + + mutex_exit(&pcieb_p->pcieb_err_mutex); + mutex_exit(&pcieb_p->pcieb_peek_poke_mutex); + if (pcieb_die & sts) + fm_panic("%s-%d: PCI(-X) Express Fatal Error. (0x%x)", + ddi_driver_name(dip), ddi_get_instance(dip), sts); + } +FAIL: + return (ret); +} + +/* + * Some PCI-X to PCI-E bridges do not support full 64-bit addressing on the + * PCI-X side of the bridge. We build a special version of this driver for + * those bridges, which uses PCIEB_ADDR_LIMIT_LO and/or PCIEB_ADDR_LIMIT_HI + * to define the range of values which the chip can handle. The code below + * then clamps the DMA address range supplied by the driver, preventing the + * PCI-E nexus driver from allocating any memory the bridge can't deal + * with. + */ +static int +pcieb_dma_allochdl(dev_info_t *dip, dev_info_t *rdip, + ddi_dma_attr_t *attr_p, int (*waitfp)(caddr_t), caddr_t arg, + ddi_dma_handle_t *handlep) +{ + int ret; +#ifdef BCM_SW_WORKAROUNDS + uint64_t lim; + + /* + * If the leaf device's limits are outside than what the Broadcom + * bridge can handle, we need to clip the values passed up the chain. + */ + lim = attr_p->dma_attr_addr_lo; + attr_p->dma_attr_addr_lo = MAX(lim, PCIEB_ADDR_LIMIT_LO); + + lim = attr_p->dma_attr_addr_hi; + attr_p->dma_attr_addr_hi = MIN(lim, PCIEB_ADDR_LIMIT_HI); + +#endif /* BCM_SW_WORKAROUNDS */ + + /* + * This is a software workaround to fix the Broadcom 5714/5715 PCIe-PCI + * bridge prefetch bug. Intercept the DMA alloc handle request and set + * PX_DMAI_FLAGS_MAP_BUFZONE flag in the handle. If this flag is set, + * the px nexus driver will allocate an extra page & make it valid one, + * for any DVMA request that comes from any of the Broadcom bridge child + * devices. + */ + if ((ret = ddi_dma_allochdl(dip, rdip, attr_p, waitfp, arg, + handlep)) == DDI_SUCCESS) { + ddi_dma_impl_t *mp = (ddi_dma_impl_t *)*handlep; +#ifdef BCM_SW_WORKAROUNDS + mp->dmai_inuse |= PX_DMAI_FLAGS_MAP_BUFZONE; +#endif /* BCM_SW_WORKAROUNDS */ + /* + * For a given rdip, update mp->dmai_bdf with the bdf value + * of pcieb's immediate child or secondary bus-id of the + * PCIe2PCI bridge. + */ + mp->dmai_minxfer = pcie_get_bdf_for_dma_xfer(dip, rdip); + } + + return (ret); +} + +/* + * FDVMA feature is not supported for any child device of Broadcom 5714/5715 + * PCIe-PCI bridge due to prefetch bug. Return failure immediately, so that + * these drivers will switch to regular DVMA path. + */ +/*ARGSUSED*/ +static int +pcieb_dma_mctl(dev_info_t *dip, dev_info_t *rdip, ddi_dma_handle_t handle, + enum ddi_dma_ctlops cmd, off_t *offp, size_t *lenp, caddr_t *objp, + uint_t cache_flags) +{ + int ret; + +#ifdef BCM_SW_WORKAROUNDS + if (cmd == DDI_DMA_RESERVE) + return (DDI_FAILURE); +#endif /* BCM_SW_WORKAROUNDS */ + + if (((ret = ddi_dma_mctl(dip, rdip, handle, cmd, offp, lenp, objp, + cache_flags)) == DDI_SUCCESS) && (cmd == DDI_DMA_RESERVE)) { + ddi_dma_impl_t *mp = (ddi_dma_impl_t *)*objp; + + /* + * For a given rdip, update mp->dmai_bdf with the bdf value + * of pcieb's immediate child or secondary bus-id of the + * PCIe2PCI bridge. + */ + mp->dmai_minxfer = pcie_get_bdf_for_dma_xfer(dip, rdip); + } + + return (ret); +} + +static int +pcieb_intr_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op, + ddi_intr_handle_impl_t *hdlp, void *result) +{ + return (pcieb_plat_intr_ops(dip, rdip, intr_op, hdlp, result)); + +} + +/*ARGSUSED*/ +static int pcieb_pciehpc_probe(dev_info_t *dip, ddi_acc_handle_t config_handle) +{ + uint16_t cap_ptr; + + if ((PCI_CAP_LOCATE(config_handle, PCI_CAP_ID_PCI_E, &cap_ptr)) != + DDI_FAILURE) { + uint16_t slotimpl = PCI_CAP_GET16(config_handle, NULL, cap_ptr, + PCIE_PCIECAP) & PCIE_PCIECAP_SLOT_IMPL; + if (slotimpl) + if (PCI_CAP_GET32(config_handle, NULL, cap_ptr, + PCIE_SLOTCAP) & PCIE_SLOTCAP_HP_CAPABLE) + return (DDI_SUCCESS); + } + + return (DDI_FAILURE); +} + +static int pcieb_pcishpc_probe(dev_info_t *dip, ddi_acc_handle_t config_handle) +{ + return (pcieb_plat_pcishpc_probe(dip, config_handle)); +} + +/* + * Initialize hotplug framework if we are hotpluggable. + * Sets flag in the soft state if Hot Plug is supported and initialized + * properly. + */ +/*ARGSUSED*/ +static int +pcieb_init_hotplug(pcieb_devstate_t *pcieb) +{ + int rv = DDI_FAILURE; + pcie_bus_t *bus_p = PCIE_DIP2BUS(pcieb->pcieb_dip); + ddi_acc_handle_t config_handle = bus_p->bus_cfg_hdl; + uint8_t dev_type = bus_p->bus_dev_type; + +#ifdef PX_PLX + uint16_t vid = bus_p->bus_dev_ven_id & 0xFFFF; + uint16_t did = bus_p->bus_dev_ven_id >> 16; + if ((vid == PXB_VENDOR_PLX) && (did == PXB_DEVICE_PLX_8532) && + (bus_p->bus_rev_id <= PXB_DEVICE_PLX_AA_REV)) + return (DDI_SUCCESS); +#endif /* PX_PLX */ + + if (((dev_type == PCIE_PCIECAP_DEV_TYPE_DOWN) || + (dev_type == PCIE_PCIECAP_DEV_TYPE_PCI2PCIE) || + (dev_type == PCIE_PCIECAP_DEV_TYPE_ROOT)) && + (pcieb_pciehpc_probe(pcieb->pcieb_dip, + config_handle) == DDI_SUCCESS)) { + pcieb->pcieb_hpc_type = HPC_PCIE; + } else if ((dev_type == PCIE_PCIECAP_DEV_TYPE_PCIE2PCI) && + (pcieb_pcishpc_probe(pcieb->pcieb_dip, + config_handle) == DDI_SUCCESS)) { + pcieb->pcieb_hpc_type = HPC_SHPC; + } else { + pcieb->pcieb_hpc_type = HPC_NONE; + return (DDI_SUCCESS); + } + + pcieb->pcieb_hotplug_capable = B_TRUE; + + if (pcieb->pcieb_hpc_type == HPC_PCIE) + rv = pciehpc_init(pcieb->pcieb_dip, NULL); + else if (pcieb->pcieb_hpc_type == HPC_SHPC) + rv = pcishpc_init(pcieb->pcieb_dip); + + if (rv != DDI_SUCCESS) + goto fail; + + if (pcihp_init(pcieb->pcieb_dip) != DDI_SUCCESS) { + if (pcieb->pcieb_hpc_type == HPC_PCIE) + (void) pciehpc_uninit(pcieb->pcieb_dip); + else if (pcieb->pcieb_hpc_type == HPC_SHPC) + (void) pcishpc_uninit(pcieb->pcieb_dip); + + goto fail; + } + + (void) ndi_prop_create_boolean(DDI_DEV_T_NONE, pcieb->pcieb_dip, + "hotplug-capable"); + + return (DDI_SUCCESS); + +fail: + pcieb->pcieb_hpc_type = HPC_NONE; + pcieb->pcieb_hotplug_capable = B_FALSE; + cmn_err(CE_WARN, "%s%d: Failed setting hotplug framework", + ddi_driver_name(pcieb->pcieb_dip), + ddi_get_instance(pcieb->pcieb_dip)); + + return (DDI_FAILURE); +} + +/* + * Power management related initialization specific to pcieb. + * Called by pcieb_attach() + */ +static int +pcieb_pwr_setup(dev_info_t *dip) +{ + char *comp_array[5]; + int i; + ddi_acc_handle_t conf_hdl; + uint16_t pmcap, cap_ptr; + pcie_pwr_t *pwr_p; + + /* Some platforms/devices may choose to disable PM */ + if (pcieb_plat_pwr_disable(dip)) { + (void) pcieb_pwr_disable(dip); + return (DDI_SUCCESS); + } + + ASSERT(PCIE_PMINFO(dip)); + pwr_p = PCIE_NEXUS_PMINFO(dip); + ASSERT(pwr_p); + + /* Code taken from pci_pci driver */ + if (pci_config_setup(dip, &pwr_p->pwr_conf_hdl) != DDI_SUCCESS) { + PCIEB_DEBUG(DBG_PWR, dip, "pcieb_pwr_setup: pci_config_setup " + "failed\n"); + return (DDI_FAILURE); + } + conf_hdl = pwr_p->pwr_conf_hdl; + + /* + * Walk the capabilities searching for a PM entry. + */ + if ((PCI_CAP_LOCATE(conf_hdl, PCI_CAP_ID_PM, &cap_ptr)) == + DDI_FAILURE) { + PCIEB_DEBUG(DBG_PWR, dip, "switch/bridge does not support PM. " + " PCI PM data structure not found in config header\n"); + pci_config_teardown(&conf_hdl); + return (DDI_SUCCESS); + } + /* + * Save offset to pmcsr for future references. + */ + pwr_p->pwr_pmcsr_offset = cap_ptr + PCI_PMCSR; + pmcap = PCI_CAP_GET16(conf_hdl, NULL, cap_ptr, PCI_PMCAP); + if (pmcap & PCI_PMCAP_D1) { + PCIEB_DEBUG(DBG_PWR, dip, "D1 state supported\n"); + pwr_p->pwr_pmcaps |= PCIE_SUPPORTS_D1; + } + if (pmcap & PCI_PMCAP_D2) { + PCIEB_DEBUG(DBG_PWR, dip, "D2 state supported\n"); + pwr_p->pwr_pmcaps |= PCIE_SUPPORTS_D2; + } + + i = 0; + comp_array[i++] = "NAME=PCIe switch/bridge PM"; + comp_array[i++] = "0=Power Off (D3)"; + if (pwr_p->pwr_pmcaps & PCIE_SUPPORTS_D2) + comp_array[i++] = "1=D2"; + if (pwr_p->pwr_pmcaps & PCIE_SUPPORTS_D1) + comp_array[i++] = "2=D1"; + comp_array[i++] = "3=Full Power D0"; + + /* + * Create pm-components property, if it does not exist already. + */ + if (ddi_prop_update_string_array(DDI_DEV_T_NONE, dip, + "pm-components", comp_array, i) != DDI_PROP_SUCCESS) { + PCIEB_DEBUG(DBG_PWR, dip, "could not create pm-components " + " prop\n"); + pci_config_teardown(&conf_hdl); + return (DDI_FAILURE); + } + return (pcieb_pwr_init_and_raise(dip, pwr_p)); +} + +/* + * undo whatever is done in pcieb_pwr_setup. called by pcieb_detach() + */ +static void +pcieb_pwr_teardown(dev_info_t *dip) +{ + pcie_pwr_t *pwr_p; + + if (!PCIE_PMINFO(dip) || !(pwr_p = PCIE_NEXUS_PMINFO(dip))) + return; + + (void) ddi_prop_remove(DDI_DEV_T_NONE, dip, "pm-components"); + if (pwr_p->pwr_conf_hdl) + pci_config_teardown(&pwr_p->pwr_conf_hdl); +} + +/* + * Initializes the power level and raise the power to D0, if it is + * not at D0. + */ +static int +pcieb_pwr_init_and_raise(dev_info_t *dip, pcie_pwr_t *pwr_p) +{ + uint16_t pmcsr; + int ret = DDI_SUCCESS; + + /* + * Intialize our power level from PMCSR. The common code initializes + * this to UNKNOWN. There is no guarantee that we will be at full + * power at attach. If we are not at D0, raise the power. + */ + pmcsr = pci_config_get16(pwr_p->pwr_conf_hdl, pwr_p->pwr_pmcsr_offset); + pmcsr &= PCI_PMCSR_STATE_MASK; + switch (pmcsr) { + case PCI_PMCSR_D0: + pwr_p->pwr_func_lvl = PM_LEVEL_D0; + break; + + case PCI_PMCSR_D1: + pwr_p->pwr_func_lvl = PM_LEVEL_D1; + break; + + case PCI_PMCSR_D2: + pwr_p->pwr_func_lvl = PM_LEVEL_D2; + break; + + case PCI_PMCSR_D3HOT: + pwr_p->pwr_func_lvl = PM_LEVEL_D3; + break; + + default: + break; + } + + /* Raise the power to D0. */ + if (pwr_p->pwr_func_lvl != PM_LEVEL_D0 && + ((ret = pm_raise_power(dip, 0, PM_LEVEL_D0)) != DDI_SUCCESS)) { + /* + * Read PMCSR again. If it is at D0, ignore the return + * value from pm_raise_power. + */ + pmcsr = pci_config_get16(pwr_p->pwr_conf_hdl, + pwr_p->pwr_pmcsr_offset); + if ((pmcsr & PCI_PMCSR_STATE_MASK) == PCI_PMCSR_D0) + ret = DDI_SUCCESS; + else { + PCIEB_DEBUG(DBG_PWR, dip, "pcieb_pwr_setup: could not " + "raise power to D0 \n"); + } + } + if (ret == DDI_SUCCESS) + pwr_p->pwr_func_lvl = PM_LEVEL_D0; + return (ret); +} + +/* + * Disable PM for x86 and PLX 8532 switch. + * For PLX Transitioning one port on this switch to low power causes links + * on other ports on the same station to die. Due to PLX erratum #34, we + * can't allow the downstream device go to non-D0 state. + */ +static int +pcieb_pwr_disable(dev_info_t *dip) +{ + pcie_pwr_t *pwr_p; + + ASSERT(PCIE_PMINFO(dip)); + pwr_p = PCIE_NEXUS_PMINFO(dip); + ASSERT(pwr_p); + PCIEB_DEBUG(DBG_PWR, dip, "pcieb_pwr_disable: disabling PM\n"); + pwr_p->pwr_func_lvl = PM_LEVEL_D0; + pwr_p->pwr_flags = PCIE_NO_CHILD_PM; + return (DDI_SUCCESS); +} + +#ifdef DEBUG +int pcieb_dbg_intr_print = 0; +void +pcieb_dbg(uint_t bit, dev_info_t *dip, char *fmt, ...) +{ + va_list ap; + + if (!pcieb_dbg_print) + return; + + if (dip) + prom_printf("%s(%d): %s", ddi_driver_name(dip), + ddi_get_instance(dip), pcieb_debug_sym[bit]); + + va_start(ap, fmt); + if (servicing_interrupt()) { + if (pcieb_dbg_intr_print) + prom_vprintf(fmt, ap); + } else { + prom_vprintf(fmt, ap); + } + + va_end(ap); +} +#endif + +static void +pcieb_id_props(pcieb_devstate_t *pcieb) +{ + uint64_t serialid = 0; /* 40b field of EUI-64 serial no. register */ + uint16_t cap_ptr; + uint8_t fic = 0; /* 1 = first in chassis device */ + pcie_bus_t *bus_p = PCIE_DIP2BUS(pcieb->pcieb_dip); + ddi_acc_handle_t config_handle = bus_p->bus_cfg_hdl; + + /* + * Identify first in chassis. In the special case of a Sun branded + * PLX device, it obviously is first in chassis. Otherwise, in the + * general case, look for an Expansion Slot Register and check its + * first-in-chassis bit. + */ +#ifdef PX_PLX + uint16_t vendor_id = bus_p->bus_dev_ven_id & 0xFFFF; + uint16_t device_id = bus_p->bus_dev_ven_id >> 16; + if ((vendor_id == PXB_VENDOR_SUN) && + ((device_id == PXB_DEVICE_PLX_PCIX) || + (device_id == PXB_DEVICE_PLX_PCIE))) { + fic = 1; + } +#endif /* PX_PLX */ + if ((fic == 0) && ((PCI_CAP_LOCATE(config_handle, + PCI_CAP_ID_SLOT_ID, &cap_ptr)) != DDI_FAILURE)) { + uint8_t esr = PCI_CAP_GET8(config_handle, NULL, + cap_ptr, PCI_CAP_ID_REGS_OFF); + if (PCI_CAPSLOT_FIC(esr)) + fic = 1; + } + + if ((PCI_CAP_LOCATE(config_handle, + PCI_CAP_XCFG_SPC(PCIE_EXT_CAP_ID_SER), &cap_ptr)) != DDI_FAILURE) { + /* Serialid can be 0 thru a full 40b number */ + serialid = PCI_XCAP_GET32(config_handle, NULL, + cap_ptr, PCIE_SER_SID_UPPER_DW); + serialid <<= 32; + serialid |= PCI_XCAP_GET32(config_handle, NULL, + cap_ptr, PCIE_SER_SID_LOWER_DW); + } + + if (fic) + (void) ndi_prop_create_boolean(DDI_DEV_T_NONE, pcieb->pcieb_dip, + "first-in-chassis"); + if (serialid) + (void) ddi_prop_update_int64(DDI_DEV_T_NONE, pcieb->pcieb_dip, + "serialid#", serialid); +} + +static void +pcieb_create_ranges_prop(dev_info_t *dip, + ddi_acc_handle_t config_handle) +{ + uint32_t base, limit; + pcieb_ranges_t ranges[PCIEB_RANGE_LEN]; + uint8_t io_base_lo, io_limit_lo; + uint16_t io_base_hi, io_limit_hi, mem_base, mem_limit; + int i = 0, rangelen = sizeof (pcieb_ranges_t)/sizeof (int); + + io_base_lo = pci_config_get8(config_handle, PCI_BCNF_IO_BASE_LOW); + io_limit_lo = pci_config_get8(config_handle, PCI_BCNF_IO_LIMIT_LOW); + io_base_hi = pci_config_get16(config_handle, PCI_BCNF_IO_BASE_HI); + io_limit_hi = pci_config_get16(config_handle, PCI_BCNF_IO_LIMIT_HI); + mem_base = pci_config_get16(config_handle, PCI_BCNF_MEM_BASE); + mem_limit = pci_config_get16(config_handle, PCI_BCNF_MEM_LIMIT); + + /* + * Create ranges for IO space + */ + ranges[i].size_low = ranges[i].size_high = 0; + ranges[i].parent_mid = ranges[i].child_mid = ranges[i].parent_high = 0; + ranges[i].child_high = ranges[i].parent_high |= + (PCI_REG_REL_M | PCI_ADDR_IO); + base = PCIEB_16bit_IOADDR(io_base_lo); + limit = PCIEB_16bit_IOADDR(io_limit_lo); + + if ((io_base_lo & 0xf) == PCIEB_32BIT_IO) { + base = PCIEB_LADDR(base, io_base_hi); + } + if ((io_limit_lo & 0xf) == PCIEB_32BIT_IO) { + limit = PCIEB_LADDR(limit, io_limit_hi); + } + + if ((io_base_lo & PCIEB_32BIT_IO) && (io_limit_hi > 0)) { + base = PCIEB_LADDR(base, io_base_hi); + limit = PCIEB_LADDR(limit, io_limit_hi); + } + + /* + * Create ranges for 32bit memory space + */ + base = PCIEB_32bit_MEMADDR(mem_base); + limit = PCIEB_32bit_MEMADDR(mem_limit); + ranges[i].size_low = ranges[i].size_high = 0; + ranges[i].parent_mid = ranges[i].child_mid = ranges[i].parent_high = 0; + ranges[i].child_high = ranges[i].parent_high |= + (PCI_REG_REL_M | PCI_ADDR_MEM32); + ranges[i].child_low = ranges[i].parent_low = base; + if (limit >= base) { + ranges[i].size_low = limit - base + PCIEB_MEMGRAIN; + i++; + } + + if (i) { + (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, dip, "ranges", + (int *)ranges, i * rangelen); + } +} + +/* + * For PCI and PCI-X devices including PCIe2PCI bridge, initialize + * cache-line-size and latency timer configuration registers. + */ +void +pcieb_set_pci_perf_parameters(dev_info_t *dip, ddi_acc_handle_t cfg_hdl) +{ + uint_t n; + + /* Initialize cache-line-size configuration register if needed */ + if (ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, + "cache-line-size", 0) == 0) { + pci_config_put8(cfg_hdl, PCI_CONF_CACHE_LINESZ, + PCIEB_CACHE_LINE_SIZE); + n = pci_config_get8(cfg_hdl, PCI_CONF_CACHE_LINESZ); + if (n != 0) { + (void) ndi_prop_update_int(DDI_DEV_T_NONE, dip, + "cache-line-size", n); + } + } + + /* Initialize latency timer configuration registers if needed */ + if (ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, + "latency-timer", 0) == 0) { + uchar_t min_gnt, latency_timer; + uchar_t header_type; + + /* Determine the configuration header type */ + header_type = pci_config_get8(cfg_hdl, PCI_CONF_HEADER); + + if ((header_type & PCI_HEADER_TYPE_M) == PCI_HEADER_ONE) { + latency_timer = PCIEB_LATENCY_TIMER; + pci_config_put8(cfg_hdl, PCI_BCNF_LATENCY_TIMER, + latency_timer); + } else { + min_gnt = pci_config_get8(cfg_hdl, PCI_CONF_MIN_G); + latency_timer = min_gnt * 8; + } + + pci_config_put8(cfg_hdl, PCI_CONF_LATENCY_TIMER, + latency_timer); + n = pci_config_get8(cfg_hdl, PCI_CONF_LATENCY_TIMER); + if (n != 0) { + (void) ndi_prop_update_int(DDI_DEV_T_NONE, dip, + "latency-timer", n); + } + } +} diff --git a/usr/src/uts/sun4/io/px/px_pci.conf b/usr/src/uts/common/io/pciex/pcieb.conf index c0172bcd95..7af98706b2 100644 --- a/usr/src/uts/sun4/io/px/px_pci.conf +++ b/usr/src/uts/common/io/pciex/pcieb.conf @@ -19,11 +19,9 @@ # CDDL HEADER END # # -# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -#ident "%Z%%M% %I% %E% SMI" -# # Force load driver to support hotplug activity # ddi-forceattach=1; diff --git a/usr/src/uts/common/io/pciex/pcieb.h b/usr/src/uts/common/io/pciex/pcieb.h new file mode 100644 index 0000000000..17a9052172 --- /dev/null +++ b/usr/src/uts/common/io/pciex/pcieb.h @@ -0,0 +1,215 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SYS_PCIEB_H +#define _SYS_PCIEB_H + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(DEBUG) +#define PCIEB_DEBUG pcieb_dbg +extern void pcieb_dbg(uint_t bit, dev_info_t *dip, char *fmt, ...); +#else /* DEBUG */ +#define PCIEB_DEBUG 0 && +#endif /* DEBUG */ + +typedef enum { /* same sequence as pcieb_debug_sym[] */ + /* 0 */ DBG_ATTACH, + /* 1 */ DBG_PWR, + /* 2 */ DBG_INTR +} pcieb_debug_bit_t; + +/* + * Intel specific register offsets with bit definitions. + */ +#define PCIEB_PX_CAPABILITY_ID 0x44 +#define PCIEB_BRIDGE_CONF 0x40 + +/* + * PCI/PCI-E Configuration register specific values. + */ +#define PX_PMODE 0x4000 /* PCI/PCIX Mode */ +#define PX_PFREQ_66 0x200 /* PCI clock frequency */ +#define PX_PFREQ_100 0x400 +#define PX_PFREQ_133 0x600 +#define PX_PMRE 0x80 /* Peer memory read enable */ + +/* + * Downstream delayed transaction resource partitioning. + */ +#define PX_ODTP 0x40 /* Max. of two entries PX and PCI */ + +/* + * Maximum upstream delayed transaction. + */ +#define PX_MDT_44 0x00 +#define PX_MDT_11 0x01 +#define PX_MDT_22 0x10 + + +#define NUM_LOGICAL_SLOTS 32 +#define PCIEB_RANGE_LEN 2 +#define PCIEB_32BIT_IO 1 +#define PCIEB_32bit_MEM 1 +#define PCIEB_MEMGRAIN 0x100000 +#define PCIEB_IOGRAIN 0x1000 + +#define PCIEB_16bit_IOADDR(addr) ((uint16_t)(((uint8_t)(addr) & 0xF0) << 8)) +#define PCIEB_LADDR(lo, hi) (((uint16_t)(hi) << 16) | (uint16_t)(lo)) +#define PCIEB_32bit_MEMADDR(addr) (PCIEB_LADDR(0, ((uint16_t)(addr) & 0xFFF0))) + +/* + * The following typedef is used to represent an entry in the "ranges" + * property of a device node. + */ +typedef struct { + uint32_t child_high; + uint32_t child_mid; + uint32_t child_low; + uint32_t parent_high; + uint32_t parent_mid; + uint32_t parent_low; + uint32_t size_high; + uint32_t size_low; +} pcieb_ranges_t; + +typedef enum { HPC_NONE, HPC_PCIE, HPC_SHPC, HPC_OUTBAND } pcieb_hpc_type_t; + +typedef struct { + dev_info_t *pcieb_dip; + + /* Hotplug support */ + boolean_t pcieb_hotplug_capable; + pcieb_hpc_type_t pcieb_hpc_type; + + /* Interrupt support */ + ddi_intr_handle_t *pcieb_htable; /* Intr Handlers */ + int pcieb_htable_size; /* htable size */ + int pcieb_intr_count; /* Num of Intr */ + uint_t pcieb_intr_priority; /* Intr Priority */ + int pcieb_intr_type; /* (MSI | FIXED) */ + int pcieb_isr_tab[4]; /* MSI source offset */ + + uint_t pcieb_soft_state; + int pcieb_init_flags; + kmutex_t pcieb_mutex; /* Soft state mutex */ + kmutex_t pcieb_intr_mutex; /* Intr handler mutex */ + kmutex_t pcieb_err_mutex; /* Error mutex */ + kmutex_t pcieb_peek_poke_mutex; /* Peekpoke mutex */ + + /* FMA */ + boolean_t pcieb_no_aer_msi; + ddi_iblock_cookie_t pcieb_fm_ibc; +} pcieb_devstate_t; + +/* + * soft state pointer + */ +extern void *pcieb_state; + +/* soft state flags */ +#define PCIEB_SOFT_STATE_CLOSED 0x00 +#define PCIEB_SOFT_STATE_OPEN 0x01 +#define PCIEB_SOFT_STATE_OPEN_EXCL 0x02 + +/* init flags */ +#define PCIEB_INIT_MUTEX 0x01 +#define PCIEB_INIT_HTABLE 0x02 +#define PCIEB_INIT_ALLOC 0x04 +#define PCIEB_INIT_HANDLER 0x08 +#define PCIEB_INIT_ENABLE 0x10 +#define PCIEB_INIT_BLOCK 0x20 +#define PCIEB_INIT_FM 0x40 + +#define PCIEB_INTR_SRC_UNKNOWN 0x0 /* must be 0 */ +#define PCIEB_INTR_SRC_HP 0x1 +#define PCIEB_INTR_SRC_PME 0x2 +#define PCIEB_INTR_SRC_AER 0x4 + +/* + * Need to put vendor ids in a common file and not platform specific files + * as is done today. Until then putting this vendor id define here. + */ +#define NVIDIA_VENDOR_ID 0x10de /* Nvidia Vendor Id */ + +#ifdef BCM_SW_WORKAROUNDS + +/* Workaround for address space limitation in Broadcom 5714/5715 */ +#define PCIEB_ADDR_LIMIT_LO 0ull +#define PCIEB_ADDR_LIMIT_HI ((1ull << 40) - 1) + +#endif /* BCM_SW_WORKAROUNDS */ + +/* + * The following values are used to initialize the cache line size + * and latency timer registers for PCI, PCI-X and PCIe2PCI devices. + */ +#define PCIEB_CACHE_LINE_SIZE 0x10 /* 64 bytes in # of DWORDs */ +#define PCIEB_LATENCY_TIMER 0x40 /* 64 PCI cycles */ + +extern void pcieb_set_pci_perf_parameters(dev_info_t *dip, + ddi_acc_handle_t config_handle); +extern void pcieb_plat_attach_workaround(dev_info_t *dip); +extern void pcieb_plat_intr_attach(pcieb_devstate_t *pcieb); +extern void pcieb_plat_initchild(dev_info_t *child); +extern void pcieb_plat_uninitchild(dev_info_t *child); +extern void pcieb_plat_ioctl_hotplug(dev_info_t *dip, int rv, int cmd); +extern int pcieb_plat_ctlops(dev_info_t *rdip, ddi_ctl_enum_t ctlop, + void *arg); +extern int pcieb_plat_pcishpc_probe(dev_info_t *dip, + ddi_acc_handle_t config_handle); +extern int pcieb_plat_peekpoke(dev_info_t *dip, dev_info_t *rdip, + ddi_ctl_enum_t ctlop, void *arg, void *result); +extern int pcieb_plat_intr_ops(dev_info_t *dip, dev_info_t *rdip, + ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, void *result); +extern boolean_t pcieb_plat_msi_supported(dev_info_t *dip); +extern boolean_t pcieb_plat_pwr_disable(dev_info_t *dip); + +#if defined(__i386) || defined(__amd64) +extern void pcieb_intel_error_workaround(dev_info_t *dip); +extern void pcieb_intel_serr_workaround(dev_info_t *dip, boolean_t mcheck); +extern void pcieb_intel_rber_workaround(dev_info_t *dip); +extern void pcieb_intel_sw_workaround(dev_info_t *dip); +extern void pcieb_intel_mps_workaround(dev_info_t *dip); +extern void pcieb_init_osc(dev_info_t *dip); +extern void pcieb_peekpoke_cb(dev_info_t *, ddi_fm_error_t *); +extern int pcishpc_init(dev_info_t *dip); +extern int pcishpc_uninit(dev_info_t *dip); +extern int pcishpc_intr(dev_info_t *dip); +#endif /* defined(__i386) || defined(__amd64) */ + +#ifdef PX_PLX +extern void pcieb_attach_plx_workarounds(pcieb_devstate_t *pcieb); +extern int pcieb_init_plx_workarounds(pcieb_devstate_t *pcieb, + dev_info_t *child); +#endif /* PX_PLX */ + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_PCIEB_H */ diff --git a/usr/src/uts/common/sys/pcie_impl.h b/usr/src/uts/common/sys/pcie_impl.h index 817211192f..1e88407b1c 100644 --- a/usr/src/uts/common/sys/pcie_impl.h +++ b/usr/src/uts/common/sys/pcie_impl.h @@ -37,7 +37,7 @@ extern "C" { #define PCI_GET_SEC_BUS(dip) \ PCIE_DIP2BUS(dip)->bus_bdg_secbus #define PCI_GET_PCIE2PCI_SECBUS(dip) \ - PCIE_DIP2BUS(dip)->bus_pcie2pci_secbus + PCIE_DIP2BUS(dip)->bus_bdg_secbus #define DEVI_PORT_TYPE_PCI \ ((PCI_CLASS_BRIDGE << 16) | (PCI_BRIDGE_PCI << 8) | \ @@ -124,7 +124,7 @@ extern "C" { /* * The following flag is used for Broadcom 5714/5715 bridge prefetch issue. - * This flag will be used both by px and px_pci nexus drivers. + * This flag will be used both by px and pcieb nexus drivers. */ #define PX_DMAI_FLAGS_MAP_BUFZONE 0x40000 @@ -253,7 +253,6 @@ typedef struct pcie_bus { uint32_t bus_dev_ven_id; /* device/vendor ID */ uint8_t bus_rev_id; /* revision ID */ uint8_t bus_hdr_type; /* pci header type, see pci.h */ - pcie_req_id_t bus_pcie2pci_secbus; /* PCIe2PCI Bridge secbus num */ uint16_t bus_dev_type; /* PCI-E dev type, see pcie.h */ uint8_t bus_bdg_secbus; /* Bridge secondary bus num */ uint16_t bus_pcie_off; /* PCIe Capability Offset */ diff --git a/usr/src/uts/sun4/io/px/pcie_pwr.h b/usr/src/uts/common/sys/pcie_pwr.h index ec08e1634b..4cb32af372 100644 --- a/usr/src/uts/sun4/io/px/pcie_pwr.h +++ b/usr/src/uts/common/sys/pcie_pwr.h @@ -19,15 +19,13 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _SYS_PCIE_PWR_H #define _SYS_PCIE_PWR_H -#pragma ident "%Z%%M% %I% %E% SMI" - #ifdef __cplusplus extern "C" { #endif @@ -147,8 +145,8 @@ typedef struct pcie_pm { */ /* - * We link pcie_pwr.o into several drivers (px, px_pci, pxb_bcm, pxb_plx), which - * causes the symbols below to be duplicated. This isn't an issue in + * We link pcie_pwr.o into several drivers (px, pcieb, pcieb_bcm, pcieb_plx), + * which causes the symbols below to be duplicated. This isn't an issue in * practice, since they aren't used from outside the module that they're * part of. However, lint does not know this, and when it does global * crosschecks for the kernel, it complains. To prevent this, we rename the diff --git a/usr/src/uts/i86pc/io/pci/pci_common.c b/usr/src/uts/i86pc/io/pci/pci_common.c index cde3e67cfb..e9ddc98526 100644 --- a/usr/src/uts/i86pc/io/pci/pci_common.c +++ b/usr/src/uts/i86pc/io/pci/pci_common.c @@ -178,7 +178,7 @@ pci_get_priority(dev_info_t *dip, ddi_intr_handle_impl_t *hdlp, int *pri) -static int pcie_pci_intr_pri_counter = 0; +static int pcieb_intr_pri_counter = 0; /* * pci_common_intr_ops: bus_intr_op() function for interrupt support @@ -311,13 +311,13 @@ SUPPORTED_TYPES_OUT: (psm_intr_ops != NULL) && (pci_get_priority(rdip, hdlp, &priority) == DDI_SUCCESS)) { /* - * Following check is a special case for 'pcie_pci'. + * Following check is a special case for 'pcieb'. * This makes sure vectors with the right priority - * are allocated for pcie_pci during ALLOC time. + * are allocated for pcieb during ALLOC time. */ - if (strcmp(ddi_driver_name(rdip), "pcie_pci") == 0) { + if (strcmp(ddi_driver_name(rdip), "pcieb") == 0) { hdlp->ih_pri = - (pcie_pci_intr_pri_counter % 2) ? 4 : 7; + (pcieb_intr_pri_counter % 2) ? 4 : 7; pciepci = 1; } else hdlp->ih_pri = priority; @@ -394,7 +394,7 @@ SUPPORTED_TYPES_OUT: ispec = (struct intrspec *)isp; if (ispec) ispec->intrspec_pri = hdlp->ih_pri; - ++pcie_pci_intr_pri_counter; + ++pcieb_intr_pri_counter; } } else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) { diff --git a/usr/src/uts/i86pc/io/pciex/inc.flg b/usr/src/uts/i86pc/io/pciex/inc.flg index 3757e5750e..42c94632c8 100644 --- a/usr/src/uts/i86pc/io/pciex/inc.flg +++ b/usr/src/uts/i86pc/io/pciex/inc.flg @@ -21,12 +21,10 @@ # # -# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -#ident "%Z%%M% %I% %E% SMI" - # This file brings down all that is needed to build the # x86 PCI Express code. # @@ -58,7 +56,7 @@ find_files "s.*" \ usr/src/uts/i86pc/npe \ usr/src/uts/i86xpv/npe \ usr/src/uts/intel/pci_autoconfig \ - usr/src/uts/intel/pcie_pci \ + usr/src/uts/intel/pcieb \ usr/src/uts/intel/pciehpc \ usr/src/uts/intel/pcicfg diff --git a/usr/src/uts/i86pc/io/pciex/npe_misc.c b/usr/src/uts/i86pc/io/pciex/npe_misc.c index 676a5b9b4a..59dfbce86e 100644 --- a/usr/src/uts/i86pc/io/pciex/npe_misc.c +++ b/usr/src/uts/i86pc/io/pciex/npe_misc.c @@ -191,7 +191,7 @@ npe_disable_empty_bridges_workaround(dev_info_t *child) * Do not bind drivers to empty bridges. * Fail above, if the bridge is found to be hotplug capable */ - if (ddi_driver_major(child) == ddi_name_to_major("pcie_pci") && + if (ddi_driver_major(child) == ddi_name_to_major("pcieb") && ddi_get_child(child) == NULL && ddi_prop_get_int(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, "pci-hotplug-type", INBAND_HPC_NONE) == INBAND_HPC_NONE) diff --git a/usr/src/uts/intel/Makefile.files b/usr/src/uts/intel/Makefile.files index 98b40700ab..b00253f54e 100644 --- a/usr/src/uts/intel/Makefile.files +++ b/usr/src/uts/intel/Makefile.files @@ -148,9 +148,9 @@ GHD_OBJS += ghd.o ghd_debug.o ghd_dma.o ghd_queue.o ghd_scsa.o \ I915_OBJS += i915_dma.o i915_drv.o i915_irq.o i915_mem.o NSKERN_OBJS += nsc_asm.o PCICFG_OBJS += pcicfg.o -PCI_E_PCINEXUS_OBJS += pcie_pci.o PCI_PCINEXUS_OBJS += pci_pci.o PCIEHPCNEXUS_OBJS += pciehpc_acpi.o +PCIEB_OBJS += pcieb_x86.o PIT_BEEP_OBJS += pit_beep.o POWER_OBJS += power.o PCI_AUTOCONFIG_OBJS += pci_autoconfig.o pci_boot.o pcie_nvidia.o \ diff --git a/usr/src/uts/intel/Makefile.intel.shared b/usr/src/uts/intel/Makefile.intel.shared index ac16b73280..85075459e0 100644 --- a/usr/src/uts/intel/Makefile.intel.shared +++ b/usr/src/uts/intel/Makefile.intel.shared @@ -279,7 +279,7 @@ DRV_KMODS += nxge DRV_KMODS += openeepr DRV_KMODS += pci_pci DRV_KMODS += pcic -DRV_KMODS += pcie_pci +DRV_KMODS += pcieb DRV_KMODS += physmem DRV_KMODS += pcan DRV_KMODS += pcwl diff --git a/usr/src/uts/intel/io/pciex/pcie_pci.c b/usr/src/uts/intel/io/pciex/pcie_pci.c deleted file mode 100644 index 6d8f6d9ef4..0000000000 --- a/usr/src/uts/intel/io/pciex/pcie_pci.c +++ /dev/null @@ -1,1781 +0,0 @@ -/* - * CDDL HEADER START - * - * The contents of this file are subject to the terms of the - * Common Development and Distribution License (the "License"). - * You may not use this file except in compliance with the License. - * - * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE - * or http://www.opensolaris.org/os/licensing. - * See the License for the specific language governing permissions - * and limitations under the License. - * - * When distributing Covered Code, include this CDDL HEADER in each - * file and include the License file at usr/src/OPENSOLARIS.LICENSE. - * If applicable, add the following below this CDDL HEADER, with the - * fields enclosed by brackets "[]" replaced with your own identifying - * information: Portions Copyright [yyyy] [name of copyright owner] - * - * CDDL HEADER END - */ -/* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. - */ - -/* - * PCI-E to PCI bus bridge nexus driver - */ - -#include <sys/conf.h> -#include <sys/kmem.h> -#include <sys/debug.h> -#include <sys/modctl.h> -#include <sys/autoconf.h> -#include <sys/ddi_impldefs.h> -#include <sys/pci.h> -#include <sys/ddi.h> -#include <sys/sunddi.h> -#include <sys/sunndi.h> -#include <sys/ddifm.h> -#include <sys/ndifm.h> -#include <sys/fm/util.h> -#include <sys/fm/protocol.h> -#include <sys/pcie.h> -#include <sys/pci_cap.h> -#include <sys/pcie_impl.h> -#include <sys/pcie_acpi.h> -#include <sys/hotplug/pci/pcihp.h> -#include <sys/hotplug/pci/pciehpc.h> -#include <sys/hotplug/hpctrl.h> -#include <io/pciex/pcie_nvidia.h> -#include <io/pciex/pcie_nb5000.h> - -#ifdef DEBUG -static int pepb_debug = 0; -#define PEPB_DEBUG(args) if (pepb_debug) cmn_err args -#else -#define PEPB_DEBUG(args) -#endif - -/* - * interfaces from misc/pcie - */ -static int pepb_bus_map(dev_info_t *, dev_info_t *, ddi_map_req_t *, off_t, - off_t, caddr_t *); -static int pepb_ctlops(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *, - void *); -static int pepb_fm_init(dev_info_t *, dev_info_t *, int, - ddi_iblock_cookie_t *); - -struct bus_ops pepb_bus_ops = { - BUSO_REV, - pepb_bus_map, - 0, - 0, - 0, - i_ddi_map_fault, - ddi_dma_map, - ddi_dma_allochdl, - ddi_dma_freehdl, - ddi_dma_bindhdl, - ddi_dma_unbindhdl, - ddi_dma_flush, - ddi_dma_win, - ddi_dma_mctl, - pepb_ctlops, - ddi_bus_prop_op, - 0, /* (*bus_get_eventcookie)(); */ - 0, /* (*bus_add_eventcall)(); */ - 0, /* (*bus_remove_eventcall)(); */ - 0, /* (*bus_post_event)(); */ - 0, /* (*bus_intr_ctl)(); */ - 0, /* (*bus_config)(); */ - 0, /* (*bus_unconfig)(); */ - pepb_fm_init, /* (*bus_fm_init)(); */ - NULL, /* (*bus_fm_fini)(); */ - NULL, /* (*bus_fm_access_enter)(); */ - NULL, /* (*bus_fm_access_exit)(); */ - NULL, /* (*bus_power)(); */ - i_ddi_intr_ops /* (*bus_intr_op)(); */ -}; - -/* - * The goal here is to leverage off of the pcihp.c source without making - * changes to it. Call into it's cb_ops directly if needed. - */ -static int pepb_open(dev_t *, int, int, cred_t *); -static int pepb_close(dev_t, int, int, cred_t *); -static int pepb_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); -static int pepb_prop_op(dev_t, dev_info_t *, ddi_prop_op_t, int, char *, - caddr_t, int *); -static int pepb_info(dev_info_t *, ddi_info_cmd_t, void *, void **); -static void pepb_peekpoke_cb(dev_info_t *, ddi_fm_error_t *); -static uint_t pepb_intr_handler(caddr_t arg1, caddr_t arg2); - -struct cb_ops pepb_cb_ops = { - pepb_open, /* open */ - pepb_close, /* close */ - nodev, /* strategy */ - nodev, /* print */ - nodev, /* dump */ - nodev, /* read */ - nodev, /* write */ - pepb_ioctl, /* ioctl */ - nodev, /* devmap */ - nodev, /* mmap */ - nodev, /* segmap */ - nochpoll, /* poll */ - pepb_prop_op, /* cb_prop_op */ - NULL, /* streamtab */ - D_NEW | D_MP | D_HOTPLUG, /* Driver compatibility flag */ - CB_REV, /* rev */ - nodev, /* int (*cb_aread)() */ - nodev /* int (*cb_awrite)() */ -}; - -static int pepb_probe(dev_info_t *); -static int pepb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd); -static int pepb_detach(dev_info_t *devi, ddi_detach_cmd_t cmd); -static int pepb_check_slot_disabled(dev_info_t *dip); - -struct dev_ops pepb_ops = { - DEVO_REV, /* devo_rev */ - 0, /* refcnt */ - pepb_info, /* info */ - nulldev, /* identify */ - pepb_probe, /* probe */ - pepb_attach, /* attach */ - pepb_detach, /* detach */ - nulldev, /* reset */ - &pepb_cb_ops, /* driver operations */ - &pepb_bus_ops, /* bus operations */ - NULL, /* power */ - ddi_quiesce_not_needed, /* quiesce */ -}; - -/* - * Module linkage information for the kernel. - */ - -static struct modldrv modldrv = { - &mod_driverops, /* Type of module */ - "PCIe to PCI nexus driver", - &pepb_ops, /* driver ops */ -}; - -static struct modlinkage modlinkage = { - MODREV_1, - (void *)&modldrv, - NULL -}; - -/* - * soft state pointer and structure template: - */ -static void *pepb_state; - -typedef struct { - dev_info_t *dip; - - /* - * cpr support: - */ - uint_t config_state_index; - struct { - dev_info_t *dip; - ushort_t command; - uchar_t cache_line_size; - uchar_t latency_timer; - uchar_t header_type; - uchar_t sec_latency_timer; - ushort_t bridge_control; - } config_state[PCI_MAX_CHILDREN]; - - /* - * hot plug support - */ - int inband_hpc; /* inband HPC type */ - - /* - * interrupt support - */ - ddi_intr_handle_t *htable; /* interrupt handles */ - int htable_size; /* htable size */ - int intr_count; /* Num of Intr */ - uint_t intr_priority; /* Intr Priority */ - int intr_type; /* (MSI | FIXED) */ - int isr_tab[4]; /* MSI source offset */ - uint32_t soft_state; /* soft state flags */ - kmutex_t pepb_mutex; /* Mutex for this ctrl */ - kmutex_t pepb_err_mutex; /* Error handling mutex */ - kmutex_t pepb_peek_poke_mutex; - boolean_t pepb_no_aer_msi; - ddi_iblock_cookie_t pepb_fm_ibc; -} pepb_devstate_t; - -/* soft state flags */ -#define PEPB_SOFT_STATE_INIT_HTABLE 0x01 /* htable kmem_alloced */ -#define PEPB_SOFT_STATE_INIT_ALLOC 0x02 /* ddi_intr_alloc called */ -#define PEPB_SOFT_STATE_INIT_HANDLER 0x04 /* ddi_intr_add_handler done */ -#define PEPB_SOFT_STATE_INIT_ENABLE 0x08 /* ddi_intr_enable called */ -#define PEPB_SOFT_STATE_INIT_BLOCK 0x10 /* ddi_intr_block_enable done */ -#define PEPB_SOFT_STATE_INIT_MUTEX 0x20 /* mutex initialized */ - -/* default interrupt priority for all interrupts (hotplug or non-hotplug */ -#define PEPB_INTR_PRI 1 - -#define PEPB_INTR_SRC_UNKNOWN 0x0 /* must be 0 */ -#define PEPB_INTR_SRC_HP 0x1 -#define PEPB_INTR_SRC_PME 0x2 -#define PEPB_INTR_SRC_AER 0x4 - -/* flag to turn on MSI support */ -int pepb_enable_msi = 1; - -/* panic on PF_PANIC flag */ -int pepb_die = PF_ERR_FATAL_FLAGS; - -extern errorq_t *pci_target_queue; - -/* - * forward function declarations: - */ -static void pepb_uninitchild(dev_info_t *); -static int pepb_initchild(dev_info_t *child); -static void pepb_save_config_regs(pepb_devstate_t *pepb_p); -static void pepb_restore_config_regs(pepb_devstate_t *pepb_p); -static boolean_t pepb_is_pcie_device_type(dev_info_t *dip); - -/* interrupt related declarations */ -static int pepb_msi_intr_supported(dev_info_t *, int intr_type); -static int pepb_intr_init(pepb_devstate_t *pepb_p, int intr_type); -static void pepb_intr_fini(pepb_devstate_t *pepb_p); - -/* Intel Workarounds */ -static void pepb_intel_serr_workaround(dev_info_t *dip, boolean_t mcheck); -static void pepb_intel_rber_workaround(dev_info_t *dip); -static void pepb_intel_mps_workaround(dev_info_t *dip); -static void pepb_intel_sw_workaround(dev_info_t *dip); -int pepb_intel_workaround_disable = 0; - -int -_init(void) -{ - int e; - - if ((e = ddi_soft_state_init(&pepb_state, sizeof (pepb_devstate_t), - 1)) == 0 && (e = mod_install(&modlinkage)) != 0) - ddi_soft_state_fini(&pepb_state); - return (e); -} - -int -_fini(void) -{ - int e; - - if ((e = mod_remove(&modlinkage)) == 0) { - /* - * Destroy pci_target_queue, and set it to NULL. - */ - if (pci_target_queue) - errorq_destroy(pci_target_queue); - pci_target_queue = NULL; - ddi_soft_state_fini(&pepb_state); - } - return (e); -} - -int -_info(struct modinfo *modinfop) -{ - return (mod_info(&modlinkage, modinfop)); -} - -/*ARGSUSED*/ -static int -pepb_probe(dev_info_t *devi) -{ - return (DDI_PROBE_SUCCESS); -} - -static int -pepb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) -{ - int instance, intr_types, fmcap; - char device_type[8]; - pepb_devstate_t *pepb; - pcie_bus_t *bus_p = PCIE_DIP2UPBUS(devi); - ddi_acc_handle_t config_handle = bus_p->bus_cfg_hdl; - - switch (cmd) { - case DDI_RESUME: - - /* - * Get the soft state structure for the bridge. - */ - pepb = ddi_get_soft_state(pepb_state, ddi_get_instance(devi)); - pepb_restore_config_regs(pepb); - return (DDI_SUCCESS); - - default: - return (DDI_FAILURE); - - case DDI_ATTACH: - break; - } - - /* - * If PCIE_LINKCTL_LINK_DISABLE bit in the PCIe Config - * Space (PCIe Capability Link Control Register) is set, - * then do not bind the driver. - */ - if (pepb_check_slot_disabled(devi) == 1) - return (DDI_FAILURE); - - /* - * Allocate and get soft state structure. - */ - instance = ddi_get_instance(devi); - if (ddi_soft_state_zalloc(pepb_state, instance) != DDI_SUCCESS) - return (DDI_FAILURE); - pepb = ddi_get_soft_state(pepb_state, instance); - pepb->dip = devi; - - /* - * initialize fma support before we start accessing config space - */ - pci_targetq_init(); - fmcap = DDI_FM_EREPORT_CAPABLE | DDI_FM_ACCCHK_CAPABLE | - DDI_FM_DMACHK_CAPABLE; - ddi_fm_init(devi, &fmcap, &pepb->pepb_fm_ibc); - - mutex_init(&pepb->pepb_err_mutex, NULL, MUTEX_DRIVER, - (void *)pepb->pepb_fm_ibc); - mutex_init(&pepb->pepb_peek_poke_mutex, NULL, MUTEX_DRIVER, - (void *)pepb->pepb_fm_ibc); - - /* - * Make sure the "device_type" property exists. - */ - if (pepb_is_pcie_device_type(devi)) - (void) strcpy(device_type, "pciex"); - else - (void) strcpy(device_type, "pci"); - (void) ddi_prop_update_string(DDI_DEV_T_NONE, devi, - "device_type", device_type); - - /* probe for inband HPC */ - pepb->inband_hpc = ddi_prop_get_int(DDI_DEV_T_ANY, devi, - DDI_PROP_DONTPASS, "pci-hotplug-type", INBAND_HPC_NONE); - - /* - * 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 - * to this bus. - */ - if (pcihp_init(devi) != DDI_SUCCESS) - cmn_err(CE_WARN, "Failed to setup hotplug framework"); - else { - /* - * If there is an inband PCI-E HPC then initialize it. - * The failure is not considered fatal for the system - * so log the message and ignore the failure. - */ - if (pepb->inband_hpc == INBAND_HPC_PCIE && - pciehpc_init(devi, NULL) != DDI_SUCCESS) { - pepb->inband_hpc = INBAND_HPC_NONE; - cmn_err(CE_CONT, "!Failed to initialize inband hotplug " - "controller"); - } - } - - /* - * Call _OSC method for 2 reasons: - * 1. Hotplug: To determine if it is native or ACPI mode. - * - * 2. Error handling: Inform firmware that OS can support AER error - * handling. Currently we don't care for what the BIOS response was - * and instead setup interrupts for error handling as if it were - * supported. - * - * For hotpluggable slots the _OSC method has already been called as - * part of the hotplug initialization above. - * For non-hotpluggable slots we need to call the _OSC method only for - * Root Ports (for AER support). - */ - if (!pcie_is_osc(devi) && PCIE_IS_RP(bus_p) && PCIE_HAS_AER(bus_p)) { - uint32_t osc_flags = OSC_CONTROL_PCIE_ADV_ERR; - (void) pcie_acpi_osc(devi, &osc_flags); - } - - /* - * Initialize interrupt handlers. - */ - if (ddi_intr_get_supported_types(devi, &intr_types) != DDI_SUCCESS) - goto next_step; - - PEPB_DEBUG((CE_NOTE, "%s#%d: intr_types = 0x%x\n", - ddi_driver_name(devi), ddi_get_instance(devi), intr_types)); - - if (pepb_msi_intr_supported(devi, intr_types) == DDI_SUCCESS) { - if (pepb_intr_init(pepb, DDI_INTR_TYPE_MSI) == DDI_SUCCESS) - goto next_step; - else - PEPB_DEBUG((CE_WARN, - "%s#%d: Unable to attach MSI handler", - ddi_driver_name(devi), ddi_get_instance(devi))); - } - - /* - * If we are here that means MSIs were not enabled. For errors fall back - * to the SERR+Machinecheck approach on certain Intel chipsets. - */ - if (PCIE_IS_RP(bus_p)) - pepb->pepb_no_aer_msi = B_TRUE; - - /* - * Only register hotplug interrupts for now. - * Check if device supports PCIe hotplug or not? - * If yes, register fixed interrupts if ILINE is valid. - * Fix error handling for INTx. - */ - if (pepb->inband_hpc == INBAND_HPC_PCIE) { - uint8_t iline; - - iline = pci_config_get8(config_handle, PCI_CONF_ILINE); - - if (iline == 0 || iline > 15) - goto next_step; - - if (pepb_intr_init(pepb, DDI_INTR_TYPE_FIXED) != DDI_SUCCESS) - PEPB_DEBUG((CE_WARN, - "%s#%d: Unable to attach INTx handler", - ddi_driver_name(devi), ddi_get_instance(devi))); - } - -next_step: - /* Must apply workaround only after all initialization is done */ - pepb_intel_serr_workaround(devi, pepb->pepb_no_aer_msi); - pepb_intel_rber_workaround(devi); - pepb_intel_sw_workaround(devi); - pepb_intel_mps_workaround(devi); - - /* - * If this is a root port, determine and set the max payload size. - * Since this will involve scanning the fabric, all error enabling - * and sw workarounds should be in place before doing this. - */ - if (PCIE_IS_RP(bus_p)) - pcie_init_root_port_mps(devi); - - ddi_report_dev(devi); - return (DDI_SUCCESS); -} - -static int -pepb_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) -{ - pepb_devstate_t *pepb; - - switch (cmd) { - case DDI_SUSPEND: - pepb = ddi_get_soft_state(pepb_state, ddi_get_instance(devi)); - pepb_save_config_regs(pepb); - return (DDI_SUCCESS); - - case DDI_DETACH: - break; - - default: - return (DDI_FAILURE); - } - - (void) ddi_prop_remove(DDI_DEV_T_NONE, devi, "device_type"); - pepb = ddi_get_soft_state(pepb_state, ddi_get_instance(devi)); - - /* remove interrupt handlers */ - pepb_intr_fini(pepb); - - /* uninitialize inband PCI-E HPC if present */ - if (pepb->inband_hpc == INBAND_HPC_PCIE) - (void) pciehpc_uninit(devi); - - /* - * Uninitialize hotplug support on this bus. - */ - (void) pcihp_uninit(devi); - - mutex_destroy(&pepb->pepb_err_mutex); - mutex_destroy(&pepb->pepb_peek_poke_mutex); - ddi_fm_fini(devi); - - /* - * And finally free the per-pci soft state. - */ - ddi_soft_state_free(pepb_state, ddi_get_instance(devi)); - - return (DDI_SUCCESS); -} - -static int -pepb_bus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp, - off_t offset, off_t len, caddr_t *vaddrp) -{ - dev_info_t *pdip; - - pdip = (dev_info_t *)DEVI(dip)->devi_parent; - return ((DEVI(pdip)->devi_ops->devo_bus_ops->bus_map)(pdip, rdip, mp, - offset, len, vaddrp)); -} - -static int -pepb_ctlops(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t ctlop, - void *arg, void *result) -{ - pci_regspec_t *drv_regp; - int reglen; - int rn; - int totreg; - pepb_devstate_t *pepb = ddi_get_soft_state(pepb_state, - ddi_get_instance(dip)); - struct detachspec *ds; - struct attachspec *as; - - switch (ctlop) { - case DDI_CTLOPS_REPORTDEV: - if (rdip == (dev_info_t *)0) - return (DDI_FAILURE); - cmn_err(CE_CONT, "?PCIE-device: %s@%s, %s%d\n", - ddi_node_name(rdip), ddi_get_name_addr(rdip), - ddi_driver_name(rdip), - ddi_get_instance(rdip)); - return (DDI_SUCCESS); - - case DDI_CTLOPS_INITCHILD: - return (pepb_initchild((dev_info_t *)arg)); - - case DDI_CTLOPS_UNINITCHILD: - pepb_uninitchild((dev_info_t *)arg); - return (DDI_SUCCESS); - - case DDI_CTLOPS_SIDDEV: - return (DDI_SUCCESS); - - case DDI_CTLOPS_REGSIZE: - case DDI_CTLOPS_NREGS: - if (rdip == (dev_info_t *)0) - return (DDI_FAILURE); - break; - - case DDI_CTLOPS_PEEK: - case DDI_CTLOPS_POKE: - if (!PCIE_IS_RP(PCIE_DIP2BUS(dip))) - return (ddi_ctlops(dip, rdip, ctlop, arg, result)); - return (pci_peekpoke_check(dip, rdip, ctlop, arg, result, - ddi_ctlops, &pepb->pepb_err_mutex, - &pepb->pepb_peek_poke_mutex, - pepb_peekpoke_cb)); - - case DDI_CTLOPS_ATTACH: - if (!pcie_is_child(dip, rdip)) - return (DDI_SUCCESS); - - as = (struct attachspec *)arg; - if ((as->when == DDI_POST) && (as->result == DDI_SUCCESS)) { - pf_init(rdip, (void *)pepb->pepb_fm_ibc, as->cmd); - (void) pcie_postattach_child(rdip); - - /* - * For leaf devices supporting RBER and AER, we need - * to apply this workaround on them after attach to be - * notified of UEs that would otherwise be ignored - * as CEs on Intel chipsets currently - */ - pepb_intel_rber_workaround(rdip); - } - - if (as->cmd == DDI_RESUME && as->when == DDI_PRE) - if (pci_pre_resume(rdip) != DDI_SUCCESS) - return (DDI_FAILURE); - - return (DDI_SUCCESS); - - case DDI_CTLOPS_DETACH: - if (!pcie_is_child(dip, rdip)) - return (DDI_SUCCESS); - - ds = (struct detachspec *)arg; - if (ds->when == DDI_PRE) - pf_fini(rdip, ds->cmd); - - if (ds->cmd == DDI_SUSPEND && ds->when == DDI_POST) - if (pci_post_suspend(rdip) != DDI_SUCCESS) - return (DDI_FAILURE); - - return (DDI_SUCCESS); - default: - return (ddi_ctlops(dip, rdip, ctlop, arg, result)); - } - - *(int *)result = 0; - if (ddi_getlongprop(DDI_DEV_T_ANY, rdip, - DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP, "reg", (caddr_t)&drv_regp, - ®len) != DDI_SUCCESS) - return (DDI_FAILURE); - - totreg = reglen / sizeof (pci_regspec_t); - if (ctlop == DDI_CTLOPS_NREGS) - *(int *)result = totreg; - else if (ctlop == DDI_CTLOPS_REGSIZE) { - rn = *(int *)arg; - if (rn >= totreg) { - kmem_free(drv_regp, reglen); - return (DDI_FAILURE); - } - *(off_t *)result = drv_regp[rn].pci_size_low; - } - - kmem_free(drv_regp, reglen); - return (DDI_SUCCESS); -} - -static int -pepb_name_child(dev_info_t *child, char *name, int namelen) -{ - pci_regspec_t *pci_rp; - uint_t slot, func; - char **unit_addr; - uint_t n; - - /* - * For .conf nodes, use unit-address property as name - */ - if (ndi_dev_is_persistent_node(child) == 0) { - if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, child, - DDI_PROP_DONTPASS, "unit-address", &unit_addr, &n) != - DDI_PROP_SUCCESS) { - cmn_err(CE_WARN, - "cannot find unit-address in %s.conf", - ddi_driver_name(child)); - return (DDI_FAILURE); - } - if (n != 1 || *unit_addr == NULL || **unit_addr == 0) { - cmn_err(CE_WARN, "unit-address property in %s.conf" - " not well-formed", ddi_driver_name(child)); - ddi_prop_free(unit_addr); - return (DDI_SUCCESS); - } - (void) snprintf(name, namelen, "%s", *unit_addr); - ddi_prop_free(unit_addr); - return (DDI_SUCCESS); - } - - /* get child "reg" property */ - if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child, - DDI_PROP_DONTPASS, "reg", (int **)&pci_rp, &n) != DDI_SUCCESS) { - return (DDI_FAILURE); - } - - /* copy the device identifications */ - slot = PCI_REG_DEV_G(pci_rp->pci_phys_hi); - func = PCI_REG_FUNC_G(pci_rp->pci_phys_hi); - - if (func != 0) - (void) snprintf(name, namelen, "%x,%x", slot, func); - else - (void) snprintf(name, namelen, "%x", slot); - - ddi_prop_free(pci_rp); - return (DDI_SUCCESS); -} - -static int -pepb_initchild(dev_info_t *child) -{ - struct ddi_parent_private_data *pdptr; - struct pcie_bus *bus_p; - char name[MAXNAMELEN]; - - if (pepb_name_child(child, name, MAXNAMELEN) != DDI_SUCCESS) - return (DDI_FAILURE); - ddi_set_name_addr(child, name); - - /* - * Pseudo nodes indicate a prototype node with per-instance - * properties to be merged into the real h/w device node. - * The interpretation of the unit-address is DD[,F] - * where DD is the device id and F is the function. - */ - if (ndi_dev_is_persistent_node(child) == 0) { - extern int pci_allow_pseudo_children; - - ddi_set_parent_data(child, NULL); - - /* - * Try to merge the properties from this prototype - * node into real h/w nodes. - */ - if (ndi_merge_node(child, pepb_name_child) != DDI_SUCCESS) { - /* - * Merged ok - return failure to remove the node. - */ - ddi_set_name_addr(child, NULL); - return (DDI_FAILURE); - } - - /* workaround for ddivs to run under PCI-E */ - if (pci_allow_pseudo_children) - return (DDI_SUCCESS); - - /* - * The child was not merged into a h/w node, - * but there's not much we can do with it other - * than return failure to cause the node to be removed. - */ - cmn_err(CE_WARN, "!%s@%s: %s.conf properties not merged", - ddi_driver_name(child), ddi_get_name_addr(child), - ddi_driver_name(child)); - ddi_set_name_addr(child, NULL); - return (DDI_NOT_WELL_FORMED); - } - - if (ddi_getprop(DDI_DEV_T_NONE, child, DDI_PROP_DONTPASS, "interrupts", - -1) != -1) { - pdptr = kmem_zalloc((sizeof (struct ddi_parent_private_data) + - sizeof (struct intrspec)), KM_SLEEP); - pdptr->par_intr = (struct intrspec *)(pdptr + 1); - pdptr->par_nintr = 1; - ddi_set_parent_data(child, pdptr); - } else - ddi_set_parent_data(child, NULL); - - bus_p = pcie_init_bus(child); - if (!bus_p || pcie_initchild(child) != DDI_SUCCESS) - return (DDI_FAILURE); - - return (DDI_SUCCESS); -} - -static void -pepb_uninitchild(dev_info_t *dip) -{ - struct ddi_parent_private_data *pdptr; - - pcie_uninitchild(dip); - - if ((pdptr = ddi_get_parent_data(dip)) != NULL) { - kmem_free(pdptr, (sizeof (*pdptr) + sizeof (struct intrspec))); - ddi_set_parent_data(dip, NULL); - } - - ddi_set_name_addr(dip, NULL); - - /* - * Strip the node to properly convert it back to prototype form - */ - ddi_remove_minor_node(dip, NULL); - - ddi_prop_remove_all(dip); -} - -/* - * pepb_save_config_regs - * - * This routine saves the state of the configuration registers of all - * the child nodes of each PBM. - * - * used by: pepb_detach() on suspends - * - * return value: none - * - * XXX: Need to save PCI-E config registers including MSI - */ -static void -pepb_save_config_regs(pepb_devstate_t *pepb_p) -{ - int i; - dev_info_t *dip; - ddi_acc_handle_t config_handle; - - for (i = 0, dip = ddi_get_child(pepb_p->dip); dip != NULL; - i++, dip = ddi_get_next_sibling(dip)) { - - if (pci_config_setup(dip, &config_handle) != DDI_SUCCESS) { - cmn_err(CE_WARN, "%s%d: can't config space for %s%d\n", - ddi_driver_name(pepb_p->dip), - ddi_get_instance(pepb_p->dip), - ddi_driver_name(dip), - ddi_get_instance(dip)); - continue; - } - - pepb_p->config_state[i].dip = dip; - pepb_p->config_state[i].command = - pci_config_get16(config_handle, PCI_CONF_COMM); - pepb_p->config_state[i].header_type = - pci_config_get8(config_handle, PCI_CONF_HEADER); - - if ((pepb_p->config_state[i].header_type & PCI_HEADER_TYPE_M) == - PCI_HEADER_ONE) - pepb_p->config_state[i].bridge_control = - pci_config_get16(config_handle, PCI_BCNF_BCNTRL); - - pepb_p->config_state[i].cache_line_size = - pci_config_get8(config_handle, PCI_CONF_CACHE_LINESZ); - pepb_p->config_state[i].latency_timer = - pci_config_get8(config_handle, PCI_CONF_LATENCY_TIMER); - - if ((pepb_p->config_state[i].header_type & - PCI_HEADER_TYPE_M) == PCI_HEADER_ONE) - pepb_p->config_state[i].sec_latency_timer = - pci_config_get8(config_handle, - PCI_BCNF_LATENCY_TIMER); - - pci_config_teardown(&config_handle); - } - pepb_p->config_state_index = i; -} - - -/* - * pepb_restore_config_regs - * - * This routine restores the state of the configuration registers of all - * the child nodes of each PBM. - * - * used by: pepb_attach() on resume - * - * return value: none - * - * XXX: Need to restore PCI-E config registers including MSI - */ -static void -pepb_restore_config_regs(pepb_devstate_t *pepb_p) -{ - int i; - dev_info_t *dip; - ddi_acc_handle_t config_handle; - - for (i = 0; i < pepb_p->config_state_index; i++) { - dip = pepb_p->config_state[i].dip; - if (pci_config_setup(dip, &config_handle) != DDI_SUCCESS) { - cmn_err(CE_WARN, "%s%d: can't config space for %s%d\n", - ddi_driver_name(pepb_p->dip), - ddi_get_instance(pepb_p->dip), - ddi_driver_name(dip), - ddi_get_instance(dip)); - continue; - } - pci_config_put16(config_handle, PCI_CONF_COMM, - pepb_p->config_state[i].command); - if ((pepb_p->config_state[i].header_type & PCI_HEADER_TYPE_M) == - PCI_HEADER_ONE) - pci_config_put16(config_handle, PCI_BCNF_BCNTRL, - pepb_p->config_state[i].bridge_control); - - pci_config_put8(config_handle, PCI_CONF_CACHE_LINESZ, - pepb_p->config_state[i].cache_line_size); - pci_config_put8(config_handle, PCI_CONF_LATENCY_TIMER, - pepb_p->config_state[i].latency_timer); - - if ((pepb_p->config_state[i].header_type & - PCI_HEADER_TYPE_M) == PCI_HEADER_ONE) - pci_config_put8(config_handle, PCI_BCNF_LATENCY_TIMER, - pepb_p->config_state[i].sec_latency_timer); - - pci_config_teardown(&config_handle); - } -} - -static boolean_t -pepb_is_pcie_device_type(dev_info_t *dip) -{ - pcie_bus_t *bus_p = PCIE_DIP2BUS(dip); - - if (PCIE_IS_SW(bus_p) || PCIE_IS_RP(bus_p) || PCIE_IS_PCI2PCIE(bus_p)) - return (B_TRUE); - - return (B_FALSE); -} - -/* - * This function initializes internally generated interrupts only. - * It does not affect any interrupts generated by downstream devices - * or the forwarding of them. - * - * Enable Device Specific Interrupts or Hotplug features here. - * Enabling features may change how many interrupts are requested - * by the device. If features are not enabled first, the - * device might not ask for any interrupts. - */ -static int -pepb_intr_init(pepb_devstate_t *pepb_p, int intr_type) -{ - dev_info_t *dip = pepb_p->dip; - int nintrs, request, count, x; - int intr_cap = 0; - int inum = 0; - int ret, hp_msi_off, aer_msi_off; - pcie_bus_t *bus_p = PCIE_DIP2UPBUS(dip); - uint16_t vendorid = bus_p->bus_dev_ven_id & 0xFFFF; - boolean_t is_hp = B_FALSE; - boolean_t is_pme = B_FALSE; - - PEPB_DEBUG((CE_NOTE, "pepb_intr_init: Attaching %s handler\n", - (intr_type == DDI_INTR_TYPE_MSI) ? "MSI" : "INTx")); - - request = 0; - if (pepb_p->inband_hpc == INBAND_HPC_PCIE) { - request++; - is_hp = B_TRUE; - } - - /* - * Hotplug and PME share the same MSI vector. If hotplug is not - * supported check if MSI is needed for PME. - */ - if ((intr_type == DDI_INTR_TYPE_MSI) && PCIE_IS_RP(bus_p) && - (vendorid == NVIDIA_VENDOR_ID)) { - is_pme = B_TRUE; - if (!is_hp) - request++; - } - - /* Setup MSI if this device is a Rootport and has AER. */ - if (intr_type == DDI_INTR_TYPE_MSI) { - if (PCIE_IS_RP(bus_p) && PCIE_HAS_AER(bus_p)) - request++; - } - - if (request == 0) - return (DDI_FAILURE); - - /* - * Get number of supported interrupts. - * - * Several Bridges/Switches will not have this property set, resulting - * in a FAILURE, if the device is not configured in a way that - * interrupts are needed. (eg. hotplugging) - */ - ret = ddi_intr_get_nintrs(dip, intr_type, &nintrs); - if ((ret != DDI_SUCCESS) || (nintrs == 0)) { - PEPB_DEBUG((CE_NOTE, "ddi_intr_get_nintrs ret:%d req:%d\n", - ret, nintrs)); - return (DDI_FAILURE); - } - - PEPB_DEBUG((CE_NOTE, "bdf 0x%x: ddi_intr_get_nintrs: nintrs %d, request" - " %d\n", bus_p->bus_bdf, nintrs, request)); - - if (request > nintrs) - request = nintrs; - - /* Allocate an array of interrupt handlers */ - pepb_p->htable_size = sizeof (ddi_intr_handle_t) * request; - pepb_p->htable = kmem_zalloc(pepb_p->htable_size, KM_SLEEP); - pepb_p->soft_state |= PEPB_SOFT_STATE_INIT_HTABLE; - - ret = ddi_intr_alloc(dip, pepb_p->htable, intr_type, inum, request, - &count, DDI_INTR_ALLOC_NORMAL); - if ((ret != DDI_SUCCESS) || (count == 0)) { - PEPB_DEBUG((CE_WARN, "ddi_intr_alloc() ret: %d ask: %d" - " actual: %d\n", ret, request, count)); - goto FAIL; - } - pepb_p->soft_state |= PEPB_SOFT_STATE_INIT_ALLOC; - - /* Save the actual number of interrupts allocated */ - pepb_p->intr_count = count; - if (count < request) { - PEPB_DEBUG((CE_WARN, "bdf 0%x: Requested Intr: %d Received:" - " %d\n", bus_p->bus_bdf, request, count)); - } - - /* - * NVidia (MCP55 and other) chipsets have a errata that if the number - * of requested MSI intrs is not allocated we have to fall back to INTx. - */ - if (intr_type == DDI_INTR_TYPE_MSI) { - if (PCIE_IS_RP(bus_p) && (vendorid == NVIDIA_VENDOR_ID)) { - if (request != count) - goto FAIL; - } - } - - /* Get interrupt priority */ - ret = ddi_intr_get_pri(pepb_p->htable[0], &pepb_p->intr_priority); - if (ret != DDI_SUCCESS) { - PEPB_DEBUG((CE_WARN, "ddi_intr_get_pri() ret: %d\n", ret)); - goto FAIL; - } - - /* initialize the interrupt mutex */ - mutex_init(&pepb_p->pepb_mutex, NULL, MUTEX_DRIVER, - DDI_INTR_PRI(pepb_p->intr_priority)); - pepb_p->soft_state |= PEPB_SOFT_STATE_INIT_MUTEX; - - for (count = 0; count < pepb_p->intr_count; count++) { - ret = ddi_intr_add_handler(pepb_p->htable[count], - pepb_intr_handler, (caddr_t)pepb_p, - (caddr_t)(uintptr_t)(inum + count)); - - if (ret != DDI_SUCCESS) { - PEPB_DEBUG((CE_WARN, "Cannot add interrupt(%d)\n", - ret)); - break; - } - } - - /* If unsucessful, remove the added handlers */ - if (ret != DDI_SUCCESS) { - for (x = 0; x < count; x++) { - (void) ddi_intr_remove_handler(pepb_p->htable[x]); - } - goto FAIL; - } - - pepb_p->soft_state |= PEPB_SOFT_STATE_INIT_HANDLER; - - (void) ddi_intr_get_cap(pepb_p->htable[0], &intr_cap); - - /* - * Get this intr lock because we are not quite ready to handle - * interrupts immediately after enabling it. The MSI multi register - * gets programmed in ddi_intr_enable after which we need to get the - * MSI offsets for Hotplug/AER. - */ - mutex_enter(&pepb_p->pepb_mutex); - - if (intr_cap & DDI_INTR_FLAG_BLOCK) { - (void) ddi_intr_block_enable(pepb_p->htable, - pepb_p->intr_count); - pepb_p->soft_state |= PEPB_SOFT_STATE_INIT_BLOCK; - } else { - for (count = 0; count < pepb_p->intr_count; count++) { - (void) ddi_intr_enable(pepb_p->htable[count]); - } - } - pepb_p->soft_state |= PEPB_SOFT_STATE_INIT_ENABLE; - - /* Save the interrupt type */ - pepb_p->intr_type = intr_type; - - /* Get the MSI offset for hotplug/PME from the PCIe cap reg */ - if (intr_type == DDI_INTR_TYPE_MSI) { - hp_msi_off = PCI_CAP_GET16(bus_p->bus_cfg_hdl, NULL, - bus_p->bus_pcie_off, PCIE_PCIECAP) & - PCIE_PCIECAP_INT_MSG_NUM; - - if (hp_msi_off >= count) { - PEPB_DEBUG((CE_NOTE, "%s%d: MSI number %d in PCIe cap >" - " max allocated %d\n", ddi_driver_name(dip), - ddi_get_instance(dip), hp_msi_off, count)); - mutex_exit(&pepb_p->pepb_mutex); - goto FAIL; - } - - if (is_hp) - pepb_p->isr_tab[hp_msi_off] |= PEPB_INTR_SRC_HP; - - if (is_pme) - pepb_p->isr_tab[hp_msi_off] |= PEPB_INTR_SRC_PME; - } else { - /* INTx handles only Hotplug interrupts */ - if (is_hp) - pepb_p->isr_tab[0] |= PEPB_INTR_SRC_HP; - } - - /* - * Get the MSI offset for errors from the AER Root Error status - * register. - */ - if ((intr_type == DDI_INTR_TYPE_MSI) && PCIE_IS_RP(bus_p)) { - if (PCIE_HAS_AER(bus_p)) { - aer_msi_off = (PCI_XCAP_GET32(bus_p->bus_cfg_hdl, NULL, - bus_p->bus_aer_off, PCIE_AER_RE_STS) >> - PCIE_AER_RE_STS_MSG_NUM_SHIFT) & - PCIE_AER_RE_STS_MSG_NUM_MASK; - - if (aer_msi_off >= count) { - PEPB_DEBUG((CE_NOTE, "%s%d: MSI number %d in" - " AER cap > max allocated %d\n", - ddi_driver_name(dip), ddi_get_instance(dip), - aer_msi_off, count)); - mutex_exit(&pepb_p->pepb_mutex); - goto FAIL; - } - pepb_p->isr_tab[aer_msi_off] |= PEPB_INTR_SRC_AER; - } else { - /* - * This RP does not have AER. Fallback to the - * SERR+Machinecheck approach. - */ - pepb_p->pepb_no_aer_msi = B_TRUE; - } - } - - mutex_exit(&pepb_p->pepb_mutex); - - return (DDI_SUCCESS); - -FAIL: - pepb_intr_fini(pepb_p); - - return (DDI_FAILURE); -} - -static void -pepb_intr_fini(pepb_devstate_t *pepb_p) -{ - int x; - int count = pepb_p->intr_count; - int flags = pepb_p->soft_state; - - if ((flags & PEPB_SOFT_STATE_INIT_ENABLE) && - (flags & PEPB_SOFT_STATE_INIT_BLOCK)) { - (void) ddi_intr_block_disable(pepb_p->htable, count); - flags &= ~(PEPB_SOFT_STATE_INIT_ENABLE | - PEPB_SOFT_STATE_INIT_BLOCK); - } - - if (flags & PEPB_SOFT_STATE_INIT_MUTEX) { - /* destroy the mutex */ - mutex_destroy(&pepb_p->pepb_mutex); - } - - for (x = 0; x < count; x++) { - if (flags & PEPB_SOFT_STATE_INIT_ENABLE) - (void) ddi_intr_disable(pepb_p->htable[x]); - - if (flags & PEPB_SOFT_STATE_INIT_HANDLER) - (void) ddi_intr_remove_handler(pepb_p->htable[x]); - - if (flags & PEPB_SOFT_STATE_INIT_ALLOC) - (void) ddi_intr_free(pepb_p->htable[x]); - } - - flags &= ~(PEPB_SOFT_STATE_INIT_ENABLE | - PEPB_SOFT_STATE_INIT_HANDLER | - PEPB_SOFT_STATE_INIT_ALLOC | PEPB_SOFT_STATE_INIT_MUTEX); - - if (flags & PEPB_SOFT_STATE_INIT_HTABLE) - kmem_free(pepb_p->htable, pepb_p->htable_size); - - flags &= ~PEPB_SOFT_STATE_INIT_HTABLE; - - pepb_p->soft_state &= flags; -} - -/* - * Checks if this device needs MSIs enabled or not. - */ -static int -pepb_msi_intr_supported(dev_info_t *dip, int intr_type) -{ - uint16_t vendor_id, device_id; - pcie_bus_t *bus_p = PCIE_DIP2UPBUS(dip); - - if (!(intr_type & DDI_INTR_TYPE_MSI) || !pepb_enable_msi) - return (DDI_FAILURE); - - vendor_id = bus_p->bus_dev_ven_id & 0xFFFF; - device_id = bus_p->bus_dev_ven_id >> 16; - /* - * Intel ESB2 switches have a errata which prevents using MSIs - * for hotplug. - */ - if ((vendor_id == INTEL_VENDOR_ID) && - INTEL_ESB2_SW_PCIE_DEV_ID(device_id)) - return (DDI_FAILURE); - - return (DDI_SUCCESS); -} - -/*ARGSUSED*/ -int -pepb_fm_init(dev_info_t *dip, dev_info_t *tdip, int cap, - ddi_iblock_cookie_t *ibc) -{ - pepb_devstate_t *pepb = ddi_get_soft_state(pepb_state, - ddi_get_instance(dip)); - - ASSERT(ibc != NULL); - *ibc = pepb->pepb_fm_ibc; - - return (DEVI(dip)->devi_fmhdl->fh_cap); -} - -static int -pepb_check_slot_disabled(dev_info_t *dip) -{ - return ((PCIE_CAP_GET(16, PCIE_DIP2BUS(dip), PCIE_LINKCTL) & - PCIE_LINKCTL_LINK_DISABLE) ? 1 : 0); -} - -static int -pepb_open(dev_t *devp, int flags, int otyp, cred_t *credp) -{ - return ((pcihp_get_cb_ops())->cb_open(devp, flags, otyp, credp)); -} - -static int -pepb_close(dev_t dev, int flags, int otyp, cred_t *credp) -{ - return ((pcihp_get_cb_ops())->cb_close(dev, flags, otyp, credp)); -} - -static int -pepb_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, - int *rvalp) -{ - int rv, inst; - pepb_devstate_t *pepb; - dev_info_t *dip; - - rv = (pcihp_get_cb_ops())->cb_ioctl(dev, cmd, arg, mode, credp, - rvalp); - - /* - * like in attach, since hotplugging can change error registers, - * we need to ensure that the proper bits are set on this port - * after a configure operation - */ - if (rv == HPC_SUCCESS && cmd == DEVCTL_AP_CONFIGURE) { - inst = PCIHP_AP_MINOR_NUM_TO_INSTANCE(getminor(dev)); - pepb = ddi_get_soft_state(pepb_state, inst); - dip = pepb->dip; - - pepb_intel_serr_workaround(dip, pepb->pepb_no_aer_msi); - pepb_intel_rber_workaround(dip); - pepb_intel_sw_workaround(dip); - } - - return (rv); -} - -static int -pepb_prop_op(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op, - int flags, char *name, caddr_t valuep, int *lengthp) -{ - return ((pcihp_get_cb_ops())->cb_prop_op(dev, dip, prop_op, flags, - name, valuep, lengthp)); -} - -static int -pepb_info(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result) -{ - return (pcihp_info(dip, cmd, arg, result)); -} - -void -pepb_peekpoke_cb(dev_info_t *dip, ddi_fm_error_t *derr) { - (void) pf_scan_fabric(dip, derr, NULL); -} - -typedef struct x86_error_reg { - uint32_t offset; - uint_t size; - uint32_t mask; - uint32_t value1; /* Value for MSI case */ - uint32_t value2; /* Value for machinecheck case */ -} x86_error_reg_t; - -typedef struct x86_error_tbl { - uint16_t vendor_id; - uint16_t device_id_low; - uint16_t device_id_high; - uint8_t rev_id_low; - uint8_t rev_id_high; - x86_error_reg_t *error_regs; - int error_regs_len; -} x86_error_tbl_t; - -/* - * Chipset and device specific settings that are required for error handling - * (reporting, fowarding, and response at the RC) beyond the standard - * registers in the PCIE and AER caps. - * - * The Northbridge Root Port settings also apply to the ESI port. The ESI - * port is a special leaf device but functions like a root port connected - * to the Southbridge and receives all the onboard Southbridge errors - * including those from Southbridge Root Ports. However, this does not - * include the Southbridge Switch Ports which act like normal switch ports - * and is connected to the Northbridge through a separate link. - * - * PCIE errors from the ESB2 Southbridge RPs are simply fowarded to the ESI - * port on the Northbridge. - * - * Currently without FMA support, we want UEs (Fatal and Non-Fatal) to panic - * the system, except for URs. We do this by having the Root Ports respond - * with a System Error and having that trigger a Machine Check (MCE). - */ - -/* - * 7300 Northbridge Root Ports - */ -static x86_error_reg_t intel_7300_rp_regs[] = { - /* Command Register - Enable SERR */ - {0x4, 16, 0xFFFF, 0x0, PCI_COMM_SERR_ENABLE}, - - /* Root Control Register - SERR on NFE/FE */ - {0x88, 16, 0x0, 0x0, PCIE_ROOTCTL_SYS_ERR_ON_NFE_EN | - PCIE_ROOTCTL_SYS_ERR_ON_FE_EN}, - - /* AER UE Mask - Mask UR */ - {0x108, 32, 0x0, PCIE_AER_UCE_UR, PCIE_AER_UCE_UR}, - - /* PEXCTRL[21] check for certain malformed TLP types and MSI enable */ - {0x48, 32, 0xFFFFFFFF, 0xC0200000, 0x200000}, - /* PEXCTRL3[7]. MSI RAS error enable */ - {0x4D, 32, 0xFFFFFFFF, 0x1, 0x0}, - - /* PEX_ERR_DOCMD[7:0] */ - {0x144, 8, 0x0, 0x0, 0xF0}, - - /* EMASK_UNCOR_PEX[21:0] UE mask */ - {0x148, 32, 0x0, PCIE_AER_UCE_UR, PCIE_AER_UCE_UR}, - - /* EMASK_RP_PEX[2:0] FE, UE, CE message detect mask */ - {0x150, 8, 0x0, 0x0, 0x1}, -}; -#define INTEL_7300_RP_REGS_LEN \ - (sizeof (intel_7300_rp_regs) / sizeof (x86_error_reg_t)) - -/* - * 5000 Northbridge Root Ports - */ -static x86_error_reg_t intel_5000_rp_regs[] = { - /* Command Register - Enable SERR */ - {0x4, 16, 0xFFFF, PCI_COMM_SERR_ENABLE, PCI_COMM_SERR_ENABLE}, - - /* Root Control Register - SERR on NFE/FE/CE */ - {0x88, 16, 0x0, PCIE_ROOTCTL_SYS_ERR_ON_NFE_EN | - PCIE_ROOTCTL_SYS_ERR_ON_FE_EN | - PCIE_ROOTCTL_SYS_ERR_ON_CE_EN, - PCIE_ROOTCTL_SYS_ERR_ON_NFE_EN | - PCIE_ROOTCTL_SYS_ERR_ON_FE_EN}, - - /* AER UE Mask - Mask UR */ - {0x108, 32, 0x0, PCIE_AER_UCE_UR, PCIE_AER_UCE_UR}, - - /* PEXCTRL[21] check for certain malformed TLP type */ - {0x48, 32, 0xFFFFFFFF, 0xC0200000, 0x200000}, - /* PEXCTRL3[7]. MSI RAS error enable. */ - {0x4D, 32, 0xFFFFFFFF, 0x1, 0x0}, - - /* PEX_ERR_DOCMD[7:0] */ - {0x144, 8, 0x0, 0x0, 0xF0}, - - /* EMASK_UNCOR_PEX[21:0] UE mask */ - {0x148, 32, 0x0, PCIE_AER_UCE_UR, PCIE_AER_UCE_UR}, - - /* EMASK_RP_PEX[2:0] FE, UE, CE message detect mask */ - {0x150, 8, 0x0, 0x0, 0x1}, -}; -#define INTEL_5000_RP_REGS_LEN \ - (sizeof (intel_5000_rp_regs) / sizeof (x86_error_reg_t)) - -/* - * 5400 Northbridge Root Ports. - */ -static x86_error_reg_t intel_5400_rp_regs[] = { - /* Command Register - Enable SERR */ - {0x4, 16, 0xFFFF, PCI_COMM_SERR_ENABLE, PCI_COMM_SERR_ENABLE}, - - /* Root Control Register - SERR on NFE/FE */ - {0x88, 16, 0x0, PCIE_ROOTCTL_SYS_ERR_ON_NFE_EN | - PCIE_ROOTCTL_SYS_ERR_ON_FE_EN | - PCIE_ROOTCTL_SYS_ERR_ON_CE_EN, - PCIE_ROOTCTL_SYS_ERR_ON_NFE_EN | - PCIE_ROOTCTL_SYS_ERR_ON_FE_EN}, - - /* AER UE Mask - Mask UR */ - {0x108, 32, 0x0, PCIE_AER_UCE_UR, PCIE_AER_UCE_UR}, - - /* PEXCTRL[21] check for certain malformed TLP types */ - {0x48, 32, 0xFFFFFFFF, 0xC0200000, 0x200000}, - /* PEXCTRL3. MSI RAS error enable. */ - {0x4E, 8, 0x0, 0x1, 0x0}, - - /* PEX_ERR_DOCMD[11:0] */ - {0x144, 16, 0x0, 0x0, 0xFF0}, - - /* PEX_ERR_PIN_MASK[4:0] do not mask ERR[2:0] pins used by DOCMD */ - {0x146, 16, 0x0, 0x10, 0x10}, - - /* EMASK_UNCOR_PEX[21:0] UE mask */ - {0x148, 32, 0x0, PCIE_AER_UCE_UR, PCIE_AER_UCE_UR}, - - /* EMASK_RP_PEX[2:0] FE, UE, CE message detect mask */ - {0x150, 8, 0x0, 0x0, 0x1}, -}; -#define INTEL_5400_RP_REGS_LEN \ - (sizeof (intel_5400_rp_regs) / sizeof (x86_error_reg_t)) - - -/* - * ESB2 Southbridge Root Ports - */ -static x86_error_reg_t intel_esb2_rp_regs[] = { - /* Command Register - Enable SERR */ - {0x4, 16, 0xFFFF, PCI_COMM_SERR_ENABLE, PCI_COMM_SERR_ENABLE}, - - /* Root Control Register - SERR on NFE/FE */ - {0x5c, 16, 0x0, PCIE_ROOTCTL_SYS_ERR_ON_NFE_EN | - PCIE_ROOTCTL_SYS_ERR_ON_FE_EN | - PCIE_ROOTCTL_SYS_ERR_ON_CE_EN, - PCIE_ROOTCTL_SYS_ERR_ON_NFE_EN | - PCIE_ROOTCTL_SYS_ERR_ON_FE_EN}, - - /* UEM[20:0] UE mask (write-once) */ - {0x148, 32, 0x0, PCIE_AER_UCE_UR, PCIE_AER_UCE_UR}, -}; -#define INTEL_ESB2_RP_REGS_LEN \ - (sizeof (intel_esb2_rp_regs) / sizeof (x86_error_reg_t)) - - -/* - * ESB2 Southbridge Switch Ports - */ -static x86_error_reg_t intel_esb2_sw_regs[] = { - /* Command Register - Enable SERR */ - {0x4, 16, 0xFFFF, PCI_COMM_SERR_ENABLE, PCI_COMM_SERR_ENABLE}, - - /* AER UE Mask - Mask UR */ - {0x108, 32, 0x0, PCIE_AER_UCE_UR, PCIE_AER_UCE_UR}, -}; -#define INTEL_ESB2_SW_REGS_LEN \ - (sizeof (intel_esb2_sw_regs) / sizeof (x86_error_reg_t)) - - -x86_error_tbl_t x86_error_init_tbl[] = { - /* Intel 7300: 3600 = ESI, 3604-360A = NB root ports */ - {0x8086, 0x3600, 0x3600, 0x0, 0xFF, - intel_7300_rp_regs, INTEL_7300_RP_REGS_LEN}, - {0x8086, 0x3604, 0x360A, 0x0, 0xFF, - intel_7300_rp_regs, INTEL_7300_RP_REGS_LEN}, - - /* Intel 5000: 25C0, 25D0, 25D4, 25D8 = ESI */ - {0x8086, 0x25C0, 0x25C0, 0x0, 0xFF, - intel_5000_rp_regs, INTEL_5000_RP_REGS_LEN}, - {0x8086, 0x25D0, 0x25D0, 0x0, 0xFF, - intel_5000_rp_regs, INTEL_5000_RP_REGS_LEN}, - {0x8086, 0x25D4, 0x25D4, 0x0, 0xFF, - intel_5000_rp_regs, INTEL_5000_RP_REGS_LEN}, - {0x8086, 0x25D8, 0x25D8, 0x0, 0xFF, - intel_5000_rp_regs, INTEL_5000_RP_REGS_LEN}, - - /* Intel 5000: 25E2-25E7 and 25F7-25FA = NB root ports */ - {0x8086, 0x25E2, 0x25E7, 0x0, 0xFF, - intel_5000_rp_regs, INTEL_5000_RP_REGS_LEN}, - {0x8086, 0x25F7, 0x25FA, 0x0, 0xFF, - intel_5000_rp_regs, INTEL_5000_RP_REGS_LEN}, - - /* Intel 5400: 4000-4001, 4003 = ESI and 4021-4029 = NB root ports */ - {0x8086, 0x4000, 0x4001, 0x0, 0xFF, - intel_5400_rp_regs, INTEL_5400_RP_REGS_LEN}, - {0x8086, 0x4003, 0x4003, 0x0, 0xFF, - intel_5400_rp_regs, INTEL_5400_RP_REGS_LEN}, - {0x8086, 0x4021, 0x4029, 0x0, 0xFF, - intel_5400_rp_regs, INTEL_5400_RP_REGS_LEN}, - - /* Intel 631xESB/632xESB aka ESB2: 2690-2697 = SB root ports */ - {0x8086, 0x2690, 0x2697, 0x0, 0xFF, - intel_esb2_rp_regs, INTEL_ESB2_RP_REGS_LEN}, - - /* Intel Switches on esb2: 3500-3503, 3510-351B */ - {0x8086, 0x3500, 0x3503, 0x0, 0xFF, - intel_esb2_sw_regs, INTEL_ESB2_SW_REGS_LEN}, - {0x8086, 0x3510, 0x351B, 0x0, 0xFF, - intel_esb2_sw_regs, INTEL_ESB2_SW_REGS_LEN}, - - /* XXX Intel PCIe-PCIx on esb2: 350C */ -}; -static int x86_error_init_tbl_len = - sizeof (x86_error_init_tbl) / sizeof (x86_error_tbl_t); - - -static int -pepb_get_bdf(dev_info_t *dip, int *busp, int *devp, int *funcp) -{ - pci_regspec_t *regspec; - int reglen; - int rv; - - rv = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, - "reg", (int **)®spec, (uint_t *)®len); - if (rv != DDI_SUCCESS) - return (rv); - - if (reglen < (sizeof (pci_regspec_t) / sizeof (int))) { - ddi_prop_free(regspec); - return (DDI_FAILURE); - } - - /* Get phys_hi from first element. All have same bdf. */ - *busp = PCI_REG_BUS_G(regspec->pci_phys_hi); - *devp = PCI_REG_DEV_G(regspec->pci_phys_hi); - *funcp = PCI_REG_FUNC_G(regspec->pci_phys_hi); - - ddi_prop_free(regspec); - return (DDI_SUCCESS); -} - -/* - * The main goal of this workaround is to set chipset specific settings if - * MSIs happen to be enabled on this device. Otherwise make the system - * Machine Check/Panic if an UE is detected in the fabric. - */ -static void -pepb_intel_serr_workaround(dev_info_t *dip, boolean_t mcheck) -{ - uint16_t vid, did; - uint8_t rid; - int bus, dev, func; - int i, j; - x86_error_tbl_t *tbl; - x86_error_reg_t *reg; - ddi_acc_handle_t cfg_hdl; - pcie_bus_t *bus_p = PCIE_DIP2UPBUS(dip); - uint16_t bdf = bus_p->bus_bdf; - - if (pepb_intel_workaround_disable) - return; - - (void) pci_config_setup(dip, &cfg_hdl); - vid = pci_config_get16(cfg_hdl, PCI_CONF_VENID); - did = pci_config_get16(cfg_hdl, PCI_CONF_DEVID); - rid = pci_config_get8(cfg_hdl, PCI_CONF_REVID); - - if (pepb_get_bdf(dip, &bus, &dev, &func) != DDI_SUCCESS) { - PEPB_DEBUG((CE_WARN, "%s#%d: pepb_get_bdf() failed", - ddi_driver_name(dip), ddi_get_instance(dip))); - return; - } - - PEPB_DEBUG((CE_NOTE, "VID:0x%x DID:0x%x RID:0x%x bdf=%x.%x.%x, " - "dip:0x%p", vid, did, rid, bus, dev, func, (void *)dip)); - - tbl = x86_error_init_tbl; - for (i = 0; i < x86_error_init_tbl_len; i++, tbl++) { - if (!((vid == tbl->vendor_id) && - (did >= tbl->device_id_low) && - (did <= tbl->device_id_high) && - (rid >= tbl->rev_id_low) && - (rid <= tbl->rev_id_high))) - continue; - - if (mcheck && PCIE_IS_RP(bus_p)) - pcie_set_rber_fatal(dip, B_TRUE); - - reg = tbl->error_regs; - for (j = 0; j < tbl->error_regs_len; j++, reg++) { - uint32_t data = 0xDEADBEEF; - uint32_t value = 0xDEADBEEF; - switch (reg->size) { - case 32: - data = (uint32_t)pci_config_get32(cfg_hdl, - reg->offset); - value = (mcheck ? - ((data & reg->mask) | reg->value2) : - ((data & reg->mask) | reg->value1)); - pci_config_put32(cfg_hdl, reg->offset, value); - value = (uint32_t)pci_config_get32(cfg_hdl, - reg->offset); - break; - case 16: - data = (uint32_t)pci_config_get16(cfg_hdl, - reg->offset); - value = (mcheck ? - ((data & reg->mask) | reg->value2) : - ((data & reg->mask) | reg->value1)); - pci_config_put16(cfg_hdl, reg->offset, - (uint16_t)value); - value = (uint32_t)pci_config_get16(cfg_hdl, - reg->offset); - break; - case 8: - data = (uint32_t)pci_config_get8(cfg_hdl, - reg->offset); - value = (mcheck ? - ((data & reg->mask) | reg->value2) : - ((data & reg->mask) | reg->value1)); - pci_config_put8(cfg_hdl, reg->offset, - (uint8_t)value); - value = (uint32_t)pci_config_get8(cfg_hdl, - reg->offset); - break; - } - - PEPB_DEBUG((CE_NOTE, "bdf:%x mcheck:%d size:%d off:0x%x" - " mask:0x%x value:0x%x + orig:0x%x -> 0x%x", bdf, - mcheck, reg->size, reg->offset, reg->mask, - (mcheck ? reg->value2 : reg->value1), - data, value)); - } - } - - pci_config_teardown(&cfg_hdl); -} - -/* - * For devices that support Role Base Errors, make several UE have a FATAL - * severity. That way a Fatal Message will be sent instead of a Correctable - * Message. Without full FMA support, CEs will be ignored. - */ -uint32_t pepb_rber_sev = (PCIE_AER_UCE_TRAINING | PCIE_AER_UCE_DLP | - PCIE_AER_UCE_SD | PCIE_AER_UCE_PTLP | PCIE_AER_UCE_FCP | PCIE_AER_UCE_TO | - PCIE_AER_UCE_CA | PCIE_AER_UCE_RO | PCIE_AER_UCE_MTLP | PCIE_AER_UCE_ECRC); - -static void -pepb_intel_rber_workaround(dev_info_t *dip) -{ - uint32_t rber; - pcie_bus_t *bus_p = PCIE_DIP2UPBUS(dip); - - if (pepb_intel_workaround_disable) - return; - - /* - * Check Root Port's machinecheck setting to determine if this - * workaround is needed or not. - */ - if (!pcie_get_rber_fatal(dip)) - return; - - if (!PCIE_IS_PCIE(bus_p) || !PCIE_HAS_AER(bus_p)) - return; - - rber = PCIE_CAP_GET(16, bus_p, PCIE_DEVCAP) & - PCIE_DEVCAP_ROLE_BASED_ERR_REP; - if (!rber) - return; - - PCIE_AER_PUT(32, bus_p, PCIE_AER_UCE_SERV, pepb_rber_sev); -} - -/* - * Workaround for certain switches regardless of platform - */ -static void -pepb_intel_sw_workaround(dev_info_t *dip) -{ - uint16_t vid, regw; - ddi_acc_handle_t cfg_hdl; - - if (pepb_intel_workaround_disable) - return; - - if (!PCIE_IS_SW(PCIE_DIP2BUS(dip))) - return; - - (void) pci_config_setup(dip, &cfg_hdl); - vid = pci_config_get16(cfg_hdl, PCI_CONF_VENID); - - /* - * Intel and PLX switches require SERR in CMD reg to foward error - * messages, though this is not PCIE spec-compliant behavior. - * To prevent the switches themselves from reporting errors on URs - * when the CMD reg has SERR enabled (which is expected according to - * the PCIE spec) we rely on masking URs in the AER cap. - */ - if (vid == 0x8086 || vid == 0x10B5) { - regw = pci_config_get16(cfg_hdl, PCI_CONF_COMM); - pci_config_put16(cfg_hdl, PCI_CONF_COMM, - regw | PCI_COMM_SERR_ENABLE); - } - - pci_config_teardown(&cfg_hdl); -} - -/* - * The Intel 5000 Chipset has an errata that requires read completion - * coalescing to be disabled if the Max Payload Size is set to 256 bytes. - */ -static void -pepb_intel_mps_workaround(dev_info_t *dip) -{ - uint16_t vid, did; - uint32_t pexctrl; - pcie_bus_t *bus_p = PCIE_DIP2UPBUS(dip); - - vid = bus_p->bus_dev_ven_id & 0xFFFF; - did = bus_p->bus_dev_ven_id >> 16; - - if ((vid == INTEL_VENDOR_ID) && INTEL_NB5000_PCIE_DEV_ID(did)) { - pexctrl = pci_config_get32(bus_p->bus_cfg_hdl, - INTEL_NB5000_PEXCTRL_OFFSET); - /* - * Turn off coalescing (bit 10) - */ - pexctrl &= ~INTEL_NB5000_PEXCTRL_COALESCE_EN; - - pci_config_put32(bus_p->bus_cfg_hdl, - INTEL_NB5000_PEXCTRL_OFFSET, pexctrl); - } -} - -/* - * Common interrupt handler for hotplug, PME and errors. - */ -static uint_t -pepb_intr_handler(caddr_t arg1, caddr_t arg2) -{ - pepb_devstate_t *pepb_p = (pepb_devstate_t *)arg1; - dev_info_t *dip = pepb_p->dip; - ddi_fm_error_t derr; - int sts = 0; - int ret = DDI_INTR_UNCLAIMED; - int isrc; - - mutex_enter(&pepb_p->pepb_mutex); - if (!(pepb_p->soft_state & PEPB_SOFT_STATE_INIT_ENABLE)) - goto FAIL; - - isrc = pepb_p->isr_tab[(int)(uintptr_t)arg2]; - - PEPB_DEBUG((CE_NOTE, "pepb_intr_handler: received intr number %d\n", - (int)(uintptr_t)arg2)); - - if (isrc == PEPB_INTR_SRC_UNKNOWN) - goto FAIL; - - if (isrc & PEPB_INTR_SRC_HP) - ret = pciehpc_intr(dip); - - if (isrc & PEPB_INTR_SRC_PME) { - PEPB_DEBUG((CE_NOTE, "pepb_pwr_msi_intr: received intr number" - "%d\n", (int)(uintptr_t)arg2)); - ret = DDI_INTR_CLAIMED; - } - - /* AER Error */ - if (isrc & PEPB_INTR_SRC_AER) { - /* - * If MSI is shared with PME/hotplug then check Root Error - * Status Reg before claiming it. For now it's ok since - * we know we get 2 MSIs. - */ - ret = DDI_INTR_CLAIMED; - bzero(&derr, sizeof (ddi_fm_error_t)); - derr.fme_version = DDI_FME_VERSION; - mutex_enter(&pepb_p->pepb_peek_poke_mutex); - mutex_enter(&pepb_p->pepb_err_mutex); - - if ((DEVI(dip)->devi_fmhdl->fh_cap) & DDI_FM_EREPORT_CAPABLE) - sts = pf_scan_fabric(dip, &derr, NULL); - - mutex_exit(&pepb_p->pepb_err_mutex); - mutex_exit(&pepb_p->pepb_peek_poke_mutex); - - if (pepb_die & sts) - fm_panic("%s-%d: PCI(-X) Express Fatal Error", - ddi_driver_name(dip), ddi_get_instance(dip)); - } -FAIL: - mutex_exit(&pepb_p->pepb_mutex); - return (ret); -} diff --git a/usr/src/uts/intel/io/pciex/pcie_pci.conf b/usr/src/uts/intel/io/pciex/pcie_pci.conf deleted file mode 100644 index b8d0d8b148..0000000000 --- a/usr/src/uts/intel/io/pciex/pcie_pci.conf +++ /dev/null @@ -1,36 +0,0 @@ -# -# 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 2005 Sun Microsystems, Inc. All rights reserved. -# Use is subject to license terms. -# -#ident "%Z%%M% %I% %E% SMI" -# -# Force load driver to support hotplug activity -# -ddi-forceattach=1; - -# -# force interrupt priorities to be one -# otherwise this driver binds as bridge device with priority 12 -# -interrupt-priorities=1; diff --git a/usr/src/uts/intel/io/pciex/pcieb_x86.c b/usr/src/uts/intel/io/pciex/pcieb_x86.c new file mode 100644 index 0000000000..605b7aa509 --- /dev/null +++ b/usr/src/uts/intel/io/pciex/pcieb_x86.c @@ -0,0 +1,679 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* x86 specific code used by the pcieb driver */ + +#include <sys/types.h> +#include <sys/ddi.h> +#include <sys/kmem.h> +#include <sys/sysmacros.h> +#include <sys/sunddi.h> +#include <sys/sunndi.h> +#include <sys/pcie.h> +#include <sys/pci_cap.h> +#include <sys/pcie_impl.h> +#include <sys/pcie_acpi.h> +#include <sys/hotplug/hpctrl.h> +#include <io/pciex/pcieb.h> +#include <io/pciex/pcie_nb5000.h> + +/* Flag to turn off intel error handling workarounds */ +int pcieb_intel_workaround_disable = 0; + +void +pcieb_peekpoke_cb(dev_info_t *dip, ddi_fm_error_t *derr) { + (void) pf_scan_fabric(dip, derr, NULL); +} + +int +pcieb_plat_peekpoke(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t ctlop, + void *arg, void *result) +{ + pcieb_devstate_t *pcieb = ddi_get_soft_state(pcieb_state, + ddi_get_instance(dip)); + + if (!PCIE_IS_RP(PCIE_DIP2BUS(dip))) + return (ddi_ctlops(dip, rdip, ctlop, arg, result)); + + return (pci_peekpoke_check(dip, rdip, ctlop, arg, result, + ddi_ctlops, &pcieb->pcieb_err_mutex, + &pcieb->pcieb_peek_poke_mutex, + pcieb_peekpoke_cb)); +} + +/* x86 specific workarounds needed at the end of pcieb attach */ +void +pcieb_plat_attach_workaround(dev_info_t *dip) +{ + /* Must apply workaround only after all initialization is done */ + pcieb_intel_error_workaround(dip); + pcieb_intel_mps_workaround(dip); + +} + +/* Workarounds to enable error handling on certain Intel chipsets */ +void +pcieb_intel_error_workaround(dev_info_t *dip) +{ + pcieb_devstate_t *pcieb = ddi_get_soft_state(pcieb_state, + ddi_get_instance(dip)); + + pcieb_intel_serr_workaround(dip, pcieb->pcieb_no_aer_msi); + pcieb_intel_rber_workaround(dip); + pcieb_intel_sw_workaround(dip); +} + +int +pcieb_plat_intr_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op, + ddi_intr_handle_impl_t *hdlp, void *result) +{ + return (i_ddi_intr_ops(dip, rdip, intr_op, hdlp, result)); +} + +/* shpc is not supported on x86 */ +/*ARGSUSED*/ +int +pcieb_plat_pcishpc_probe(dev_info_t *dip, ddi_acc_handle_t config_handle) +{ + return (DDI_FAILURE); +} + +/* + * Dummy functions to get around the fact that there's no shpc module on x86 + * today + */ +/*ARGSUSED*/ +int +pcishpc_init(dev_info_t *dip) +{ + return (DDI_FAILURE); +} + +/*ARGSUSED*/ +int +pcishpc_uninit(dev_info_t *dip) +{ + return (DDI_FAILURE); +} + +/*ARGSUSED*/ +int +pcishpc_intr(dev_info_t *dip) +{ + return (DDI_INTR_UNCLAIMED); +} + +/*ARGSUSED*/ +boolean_t +pcieb_plat_pwr_disable(dev_info_t *dip) +{ + /* Always disable on x86 */ + return (B_TRUE); +} + +boolean_t +pcieb_plat_msi_supported(dev_info_t *dip) +{ + pcie_bus_t *bus_p = PCIE_DIP2UPBUS(dip); + uint16_t vendor_id, device_id; + vendor_id = bus_p->bus_dev_ven_id & 0xFFFF; + device_id = bus_p->bus_dev_ven_id >> 16; + + /* + * Intel ESB2 switches have a errata which prevents using MSIs + * for hotplug. + */ + return (((vendor_id == INTEL_VENDOR_ID) && + INTEL_ESB2_SW_PCIE_DEV_ID(device_id)) ? B_FALSE : B_TRUE); +} + +void +pcieb_plat_intr_attach(pcieb_devstate_t *pcieb) +{ + /* + * _OSC initialization needs to be done before interrupts are + * initialized. + */ + pcieb_init_osc(pcieb->pcieb_dip); +} + +void +pcieb_plat_initchild(dev_info_t *child) +{ + struct ddi_parent_private_data *pdptr; + if (ddi_getprop(DDI_DEV_T_NONE, child, DDI_PROP_DONTPASS, "interrupts", + -1) != -1) { + pdptr = kmem_zalloc((sizeof (struct ddi_parent_private_data) + + sizeof (struct intrspec)), KM_SLEEP); + pdptr->par_intr = (struct intrspec *)(pdptr + 1); + pdptr->par_nintr = 1; + ddi_set_parent_data(child, pdptr); + } else + ddi_set_parent_data(child, NULL); +} + +void +pcieb_plat_uninitchild(dev_info_t *child) +{ + struct ddi_parent_private_data *pdptr; + + if ((pdptr = ddi_get_parent_data(child)) != NULL) + kmem_free(pdptr, (sizeof (*pdptr) + sizeof (struct intrspec))); + + ddi_set_parent_data(child, NULL); +} + +/* _OSC related */ +void +pcieb_init_osc(dev_info_t *devi) { + pcie_bus_t *bus_p = PCIE_DIP2UPBUS(devi); + uint32_t osc_flags = OSC_CONTROL_PCIE_ADV_ERR; + + /* + * Call _OSC method for 2 reasons: + * 1. Hotplug: To determine if it is native or ACPI mode. + * + * 2. Error handling: Inform firmware that OS can support AER error + * handling. Currently we don't care for what the BIOS response was + * and instead setup interrupts for error handling as if it were + * supported. + * + * For hotpluggable slots the _OSC method has already been called as + * part of the hotplug initialization. + * For non-hotpluggable slots we need to call the _OSC method only for + * Root Ports (for AER support). + */ + if (!pcie_is_osc(devi) && PCIE_IS_RP(bus_p) && PCIE_HAS_AER(bus_p)) + (void) pcie_acpi_osc(devi, &osc_flags); +} + +/* + * Intel chip specific workarounds. Right now they're limited to the 5000, 5400 + * and 7300 series chipsets. + */ +typedef struct x86_error_reg { + uint32_t offset; + uint_t size; + uint32_t mask; + uint32_t value1; /* Value for MSI case */ + uint32_t value2; /* Value for machinecheck case */ +} x86_error_reg_t; + +typedef struct x86_error_tbl { + uint16_t vendor_id; + uint16_t device_id_low; + uint16_t device_id_high; + uint8_t rev_id_low; + uint8_t rev_id_high; + x86_error_reg_t *error_regs; + int error_regs_len; +} x86_error_tbl_t; + +/* + * Chipset and device specific settings that are required for error handling + * (reporting, fowarding, and response at the RC) beyond the standard + * registers in the PCIE and AER caps. + * + * The Northbridge Root Port settings also apply to the ESI port. The ESI + * port is a special leaf device but functions like a root port connected + * to the Southbridge and receives all the onboard Southbridge errors + * including those from Southbridge Root Ports. However, this does not + * include the Southbridge Switch Ports which act like normal switch ports + * and is connected to the Northbridge through a separate link. + * + * PCIE errors from the ESB2 Southbridge RPs are simply fowarded to the ESI + * port on the Northbridge. + * + * If MSIs don't work we want UEs (Fatal and Non-Fatal) to panic the system, + * except for URs. We do this by having the Root Ports respond with a System + * Error and having that trigger a Machine Check (MCE). + */ + +/* + * 7300 Northbridge Root Ports + */ +static x86_error_reg_t intel_7300_rp_regs[] = { + /* Command Register - Enable SERR */ + {0x4, 16, 0xFFFF, 0x0, PCI_COMM_SERR_ENABLE}, + + /* Root Control Register - SERR on NFE/FE */ + {0x88, 16, 0x0, 0x0, PCIE_ROOTCTL_SYS_ERR_ON_NFE_EN | + PCIE_ROOTCTL_SYS_ERR_ON_FE_EN}, + + /* AER UE Mask - Mask UR */ + {0x108, 32, 0x0, PCIE_AER_UCE_UR, PCIE_AER_UCE_UR}, + + /* PEXCTRL[21] check for certain malformed TLP types and MSI enable */ + {0x48, 32, 0xFFFFFFFF, 0xC0200000, 0x200000}, + /* PEXCTRL3[7]. MSI RAS error enable */ + {0x4D, 32, 0xFFFFFFFF, 0x1, 0x0}, + + /* PEX_ERR_DOCMD[7:0] */ + {0x144, 8, 0x0, 0x0, 0xF0}, + + /* EMASK_UNCOR_PEX[21:0] UE mask */ + {0x148, 32, 0x0, PCIE_AER_UCE_UR, PCIE_AER_UCE_UR}, + + /* EMASK_RP_PEX[2:0] FE, UE, CE message detect mask */ + {0x150, 8, 0x0, 0x0, 0x1}, +}; +#define INTEL_7300_RP_REGS_LEN \ + (sizeof (intel_7300_rp_regs) / sizeof (x86_error_reg_t)) + +/* + * 5000 Northbridge Root Ports + */ +static x86_error_reg_t intel_5000_rp_regs[] = { + /* Command Register - Enable SERR */ + {0x4, 16, 0xFFFF, PCI_COMM_SERR_ENABLE, PCI_COMM_SERR_ENABLE}, + + /* Root Control Register - SERR on NFE/FE/CE */ + {0x88, 16, 0x0, PCIE_ROOTCTL_SYS_ERR_ON_NFE_EN | + PCIE_ROOTCTL_SYS_ERR_ON_FE_EN | + PCIE_ROOTCTL_SYS_ERR_ON_CE_EN, + PCIE_ROOTCTL_SYS_ERR_ON_NFE_EN | + PCIE_ROOTCTL_SYS_ERR_ON_FE_EN}, + + /* AER UE Mask - Mask UR */ + {0x108, 32, 0x0, PCIE_AER_UCE_UR, PCIE_AER_UCE_UR}, + + /* PEXCTRL[21] check for certain malformed TLP type */ + {0x48, 32, 0xFFFFFFFF, 0xC0200000, 0x200000}, + /* PEXCTRL3[7]. MSI RAS error enable. */ + {0x4D, 32, 0xFFFFFFFF, 0x1, 0x0}, + + /* PEX_ERR_DOCMD[7:0] */ + {0x144, 8, 0x0, 0x0, 0xF0}, + + /* EMASK_UNCOR_PEX[21:0] UE mask */ + {0x148, 32, 0x0, PCIE_AER_UCE_UR, PCIE_AER_UCE_UR}, + + /* EMASK_RP_PEX[2:0] FE, UE, CE message detect mask */ + {0x150, 8, 0x0, 0x0, 0x1}, +}; +#define INTEL_5000_RP_REGS_LEN \ + (sizeof (intel_5000_rp_regs) / sizeof (x86_error_reg_t)) + +/* + * 5400 Northbridge Root Ports. + */ +static x86_error_reg_t intel_5400_rp_regs[] = { + /* Command Register - Enable SERR */ + {0x4, 16, 0xFFFF, PCI_COMM_SERR_ENABLE, PCI_COMM_SERR_ENABLE}, + + /* Root Control Register - SERR on NFE/FE */ + {0x88, 16, 0x0, PCIE_ROOTCTL_SYS_ERR_ON_NFE_EN | + PCIE_ROOTCTL_SYS_ERR_ON_FE_EN | + PCIE_ROOTCTL_SYS_ERR_ON_CE_EN, + PCIE_ROOTCTL_SYS_ERR_ON_NFE_EN | + PCIE_ROOTCTL_SYS_ERR_ON_FE_EN}, + + /* AER UE Mask - Mask UR */ + {0x108, 32, 0x0, PCIE_AER_UCE_UR, PCIE_AER_UCE_UR}, + + /* PEXCTRL[21] check for certain malformed TLP types */ + {0x48, 32, 0xFFFFFFFF, 0xC0200000, 0x200000}, + /* PEXCTRL3. MSI RAS error enable. */ + {0x4E, 8, 0x0, 0x1, 0x0}, + + /* PEX_ERR_DOCMD[11:0] */ + {0x144, 16, 0x0, 0x0, 0xFF0}, + + /* PEX_ERR_PIN_MASK[4:0] do not mask ERR[2:0] pins used by DOCMD */ + {0x146, 16, 0x0, 0x10, 0x10}, + + /* EMASK_UNCOR_PEX[21:0] UE mask */ + {0x148, 32, 0x0, PCIE_AER_UCE_UR, PCIE_AER_UCE_UR}, + + /* EMASK_RP_PEX[2:0] FE, UE, CE message detect mask */ + {0x150, 8, 0x0, 0x0, 0x1}, +}; +#define INTEL_5400_RP_REGS_LEN \ + (sizeof (intel_5400_rp_regs) / sizeof (x86_error_reg_t)) + + +/* + * ESB2 Southbridge Root Ports + */ +static x86_error_reg_t intel_esb2_rp_regs[] = { + /* Command Register - Enable SERR */ + {0x4, 16, 0xFFFF, PCI_COMM_SERR_ENABLE, PCI_COMM_SERR_ENABLE}, + + /* Root Control Register - SERR on NFE/FE */ + {0x5c, 16, 0x0, PCIE_ROOTCTL_SYS_ERR_ON_NFE_EN | + PCIE_ROOTCTL_SYS_ERR_ON_FE_EN | + PCIE_ROOTCTL_SYS_ERR_ON_CE_EN, + PCIE_ROOTCTL_SYS_ERR_ON_NFE_EN | + PCIE_ROOTCTL_SYS_ERR_ON_FE_EN}, + + /* UEM[20:0] UE mask (write-once) */ + {0x148, 32, 0x0, PCIE_AER_UCE_UR, PCIE_AER_UCE_UR}, +}; +#define INTEL_ESB2_RP_REGS_LEN \ + (sizeof (intel_esb2_rp_regs) / sizeof (x86_error_reg_t)) + + +/* + * ESB2 Southbridge Switch Ports + */ +static x86_error_reg_t intel_esb2_sw_regs[] = { + /* Command Register - Enable SERR */ + {0x4, 16, 0xFFFF, PCI_COMM_SERR_ENABLE, PCI_COMM_SERR_ENABLE}, + + /* AER UE Mask - Mask UR */ + {0x108, 32, 0x0, PCIE_AER_UCE_UR, PCIE_AER_UCE_UR}, +}; +#define INTEL_ESB2_SW_REGS_LEN \ + (sizeof (intel_esb2_sw_regs) / sizeof (x86_error_reg_t)) + + +x86_error_tbl_t x86_error_init_tbl[] = { + /* Intel 7300: 3600 = ESI, 3604-360A = NB root ports */ + {0x8086, 0x3600, 0x3600, 0x0, 0xFF, + intel_7300_rp_regs, INTEL_7300_RP_REGS_LEN}, + {0x8086, 0x3604, 0x360A, 0x0, 0xFF, + intel_7300_rp_regs, INTEL_7300_RP_REGS_LEN}, + + /* Intel 5000: 25C0, 25D0, 25D4, 25D8 = ESI */ + {0x8086, 0x25C0, 0x25C0, 0x0, 0xFF, + intel_5000_rp_regs, INTEL_5000_RP_REGS_LEN}, + {0x8086, 0x25D0, 0x25D0, 0x0, 0xFF, + intel_5000_rp_regs, INTEL_5000_RP_REGS_LEN}, + {0x8086, 0x25D4, 0x25D4, 0x0, 0xFF, + intel_5000_rp_regs, INTEL_5000_RP_REGS_LEN}, + {0x8086, 0x25D8, 0x25D8, 0x0, 0xFF, + intel_5000_rp_regs, INTEL_5000_RP_REGS_LEN}, + + /* Intel 5000: 25E2-25E7 and 25F7-25FA = NB root ports */ + {0x8086, 0x25E2, 0x25E7, 0x0, 0xFF, + intel_5000_rp_regs, INTEL_5000_RP_REGS_LEN}, + {0x8086, 0x25F7, 0x25FA, 0x0, 0xFF, + intel_5000_rp_regs, INTEL_5000_RP_REGS_LEN}, + + /* Intel 5400: 4000-4001, 4003 = ESI and 4021-4029 = NB root ports */ + {0x8086, 0x4000, 0x4001, 0x0, 0xFF, + intel_5400_rp_regs, INTEL_5400_RP_REGS_LEN}, + {0x8086, 0x4003, 0x4003, 0x0, 0xFF, + intel_5400_rp_regs, INTEL_5400_RP_REGS_LEN}, + {0x8086, 0x4021, 0x4029, 0x0, 0xFF, + intel_5400_rp_regs, INTEL_5400_RP_REGS_LEN}, + + /* Intel 631xESB/632xESB aka ESB2: 2690-2697 = SB root ports */ + {0x8086, 0x2690, 0x2697, 0x0, 0xFF, + intel_esb2_rp_regs, INTEL_ESB2_RP_REGS_LEN}, + + /* Intel Switches on esb2: 3500-3503, 3510-351B */ + {0x8086, 0x3500, 0x3503, 0x0, 0xFF, + intel_esb2_sw_regs, INTEL_ESB2_SW_REGS_LEN}, + {0x8086, 0x3510, 0x351B, 0x0, 0xFF, + intel_esb2_sw_regs, INTEL_ESB2_SW_REGS_LEN}, + + /* XXX Intel PCIe-PCIx on esb2: 350C */ +}; +static int x86_error_init_tbl_len = + sizeof (x86_error_init_tbl) / sizeof (x86_error_tbl_t); + +/* + * The main goal of this workaround is to set chipset specific settings if + * MSIs happen to be enabled on this device. Otherwise make the system + * Machine Check/Panic if an UE is detected in the fabric. + */ +void +pcieb_intel_serr_workaround(dev_info_t *dip, boolean_t mcheck) +{ + uint16_t vid, did; + uint8_t rid; + int i, j; + x86_error_tbl_t *tbl; + x86_error_reg_t *reg; + pcie_bus_t *bus_p = PCIE_DIP2UPBUS(dip); + ddi_acc_handle_t cfg_hdl = bus_p->bus_cfg_hdl; + uint16_t bdf = bus_p->bus_bdf; + + if (pcieb_intel_workaround_disable) + return; + + vid = bus_p->bus_dev_ven_id & 0xFFFF; + did = bus_p->bus_dev_ven_id >> 16; + rid = bus_p->bus_rev_id; + + PCIEB_DEBUG(DBG_ATTACH, dip, "VID:0x%x DID:0x%x RID:0x%x bdf=0x%x\n", + vid, did, rid, bdf); + + tbl = x86_error_init_tbl; + for (i = 0; i < x86_error_init_tbl_len; i++, tbl++) { + if (!((vid == tbl->vendor_id) && + (did >= tbl->device_id_low) && + (did <= tbl->device_id_high) && + (rid >= tbl->rev_id_low) && + (rid <= tbl->rev_id_high))) + continue; + + if (mcheck && PCIE_IS_RP(bus_p)) + pcie_set_rber_fatal(dip, B_TRUE); + + reg = tbl->error_regs; + for (j = 0; j < tbl->error_regs_len; j++, reg++) { + uint32_t data = 0xDEADBEEF; + uint32_t value = 0xDEADBEEF; + switch (reg->size) { + case 32: + data = (uint32_t)pci_config_get32(cfg_hdl, + reg->offset); + value = (mcheck ? + ((data & reg->mask) | reg->value2) : + ((data & reg->mask) | reg->value1)); + pci_config_put32(cfg_hdl, reg->offset, value); + value = (uint32_t)pci_config_get32(cfg_hdl, + reg->offset); + break; + case 16: + data = (uint32_t)pci_config_get16(cfg_hdl, + reg->offset); + value = (mcheck ? + ((data & reg->mask) | reg->value2) : + ((data & reg->mask) | reg->value1)); + pci_config_put16(cfg_hdl, reg->offset, + (uint16_t)value); + value = (uint32_t)pci_config_get16(cfg_hdl, + reg->offset); + break; + case 8: + data = (uint32_t)pci_config_get8(cfg_hdl, + reg->offset); + value = (mcheck ? + ((data & reg->mask) | reg->value2) : + ((data & reg->mask) | reg->value1)); + pci_config_put8(cfg_hdl, reg->offset, + (uint8_t)value); + value = (uint32_t)pci_config_get8(cfg_hdl, + reg->offset); + break; + } + + PCIEB_DEBUG(DBG_ATTACH, dip, "bdf:%x mcheck:%d size:%d " + "off:0x%x mask:0x%x value:0x%x + orig:0x%x -> " + "0x%x\n", bdf, mcheck, reg->size, reg->offset, + reg->mask, (mcheck ? reg->value2 : reg->value1), + data, value); + } + } +} + +/* + * For devices that support Role Base Errors, make several UE have a FATAL + * severity. That way a Fatal Message will be sent instead of a Correctable + * Message. Without full FMA support, CEs will be ignored. + */ +uint32_t pcieb_rber_sev = (PCIE_AER_UCE_TRAINING | PCIE_AER_UCE_DLP | + PCIE_AER_UCE_SD | PCIE_AER_UCE_PTLP | PCIE_AER_UCE_FCP | PCIE_AER_UCE_TO | + PCIE_AER_UCE_CA | PCIE_AER_UCE_RO | PCIE_AER_UCE_MTLP | PCIE_AER_UCE_ECRC); + +void +pcieb_intel_rber_workaround(dev_info_t *dip) +{ + uint32_t rber; + pcie_bus_t *bus_p = PCIE_DIP2UPBUS(dip); + + if (pcieb_intel_workaround_disable) + return; + + /* + * Check Root Port's machinecheck setting to determine if this + * workaround is needed or not. + */ + if (!pcie_get_rber_fatal(dip)) + return; + + if (!PCIE_IS_PCIE(bus_p) || !PCIE_HAS_AER(bus_p)) + return; + + rber = PCIE_CAP_GET(16, bus_p, PCIE_DEVCAP) & + PCIE_DEVCAP_ROLE_BASED_ERR_REP; + if (!rber) + return; + + PCIE_AER_PUT(32, bus_p, PCIE_AER_UCE_SERV, pcieb_rber_sev); +} + +/* + * The Intel 5000 Chipset has an errata that requires read completion + * coalescing to be disabled if the Max Payload Size is set to 256 bytes. + */ +void +pcieb_intel_mps_workaround(dev_info_t *dip) +{ + uint16_t vid, did; + uint32_t pexctrl; + pcie_bus_t *bus_p = PCIE_DIP2UPBUS(dip); + + vid = bus_p->bus_dev_ven_id & 0xFFFF; + did = bus_p->bus_dev_ven_id >> 16; + + if ((vid == INTEL_VENDOR_ID) && INTEL_NB5000_PCIE_DEV_ID(did)) { + pexctrl = pci_config_get32(bus_p->bus_cfg_hdl, + INTEL_NB5000_PEXCTRL_OFFSET); + /* + * Turn off coalescing (bit 10) + */ + pexctrl &= ~INTEL_NB5000_PEXCTRL_COALESCE_EN; + + pci_config_put32(bus_p->bus_cfg_hdl, + INTEL_NB5000_PEXCTRL_OFFSET, pexctrl); + } +} + +/* + * Workaround for certain switches regardless of platform + */ +void +pcieb_intel_sw_workaround(dev_info_t *dip) +{ + uint16_t vid, regw; + pcie_bus_t *bus_p = PCIE_DIP2UPBUS(dip); + ddi_acc_handle_t cfg_hdl = bus_p->bus_cfg_hdl; + + if (pcieb_intel_workaround_disable) + return; + + if (!PCIE_IS_SW(PCIE_DIP2BUS(dip))) + return; + + vid = bus_p->bus_dev_ven_id & 0xFFFF; + /* + * Intel and PLX switches require SERR in CMD reg to foward error + * messages, though this is not PCIE spec-compliant behavior. + * To prevent the switches themselves from reporting errors on URs + * when the CMD reg has SERR enabled (which is expected according to + * the PCIE spec) we rely on masking URs in the AER cap. + */ + if (vid == 0x8086 || vid == 0x10B5) { + regw = pci_config_get16(cfg_hdl, PCI_CONF_COMM); + pci_config_put16(cfg_hdl, PCI_CONF_COMM, + regw | PCI_COMM_SERR_ENABLE); + } +} + +int +pcieb_plat_ctlops(dev_info_t *rdip, ddi_ctl_enum_t ctlop, void *arg) +{ + struct detachspec *ds; + struct attachspec *as; + + switch (ctlop) { + case DDI_CTLOPS_DETACH: + ds = (struct detachspec *)arg; + switch (ds->when) { + case DDI_POST: + if (ds->cmd == DDI_SUSPEND) { + if (pci_post_suspend(rdip) != DDI_SUCCESS) + return (DDI_FAILURE); + } + break; + default: + break; + } + break; + case DDI_CTLOPS_ATTACH: + as = (struct attachspec *)arg; + switch (as->when) { + case DDI_PRE: + if (as->cmd == DDI_RESUME) { + if (pci_pre_resume(rdip) != DDI_SUCCESS) + return (DDI_FAILURE); + } + break; + case DDI_POST: + /* + * For leaf devices supporting RBER and AER, we + * need to apply this workaround on them after + * attach to be notified of UEs that would + * otherwise be ignored as CEs on Intel chipsets + * currently + */ + pcieb_intel_rber_workaround(rdip); + break; + default: + break; + } + break; + default: + break; + } + + return (DDI_SUCCESS); +} + +void +pcieb_plat_ioctl_hotplug(dev_info_t *dip, int rv, int cmd) +{ + /* + * like in attach, since hotplugging can change error registers, + * we need to ensure that the proper bits are set on this port + * after a configure operation + */ + if ((rv == HPC_SUCCESS) && (cmd == DEVCTL_AP_CONFIGURE)) + pcieb_intel_error_workaround(dip); +} diff --git a/usr/src/uts/intel/os/driver_aliases b/usr/src/uts/intel/os/driver_aliases index 420404560d..fe9bb3e226 100644 --- a/usr/src/uts/intel/os/driver_aliases +++ b/usr/src/uts/intel/os/driver_aliases @@ -1,6 +1,6 @@ npe "pciex_root_complex" -pcie_pci "pciexclass,060400" -pcie_pci "pciexclass,060401" +pcieb "pciexclass,060400" +pcieb "pciexclass,060401" pci_pci "pciclass,060400" pci_pci "pciclass,060401" pci_pci "pci1011,1" diff --git a/usr/src/uts/intel/os/name_to_major b/usr/src/uts/intel/os/name_to_major index fe4850d74c..df3e1ce7cc 100644 --- a/usr/src/uts/intel/os/name_to_major +++ b/usr/src/uts/intel/os/name_to_major @@ -109,7 +109,7 @@ smbios 180 power 181 zfs 182 npe 183 -pcie_pci 184 +pcieb 184 kssl 185 mc-amd 186 tzmon 187 diff --git a/usr/src/uts/intel/pcie_pci/Makefile b/usr/src/uts/intel/pcieb/Makefile index 51a4ba8a6d..420476a8cb 100644 --- a/usr/src/uts/intel/pcie_pci/Makefile +++ b/usr/src/uts/intel/pcieb/Makefile @@ -19,14 +19,12 @@ # CDDL HEADER END # # -# uts/intel/pcie_pci/Makefile +# uts/intel/pcieb/Makefile # -# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Copyright 2009 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 pcie_pci driver kernel +# This makefile drives the production of the pcieb driver kernel # module. # @@ -38,11 +36,11 @@ UTSBASE = ../.. # # Define the module and object file sets. # -MODULE = pcie_pci -OBJECTS = $(PCI_E_PCINEXUS_OBJS:%=$(OBJS_DIR)/%) -LINTS = $(PCI_E_PCINEXUS_OBJS:%.o=$(LINTS_DIR)/%.ln) +MODULE = pcieb +OBJECTS = $(PCIEB_OBJS:%=$(OBJS_DIR)/%) +LINTS = $(PCIEB_OBJS:%.o=$(LINTS_DIR)/%.ln) ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE) -CONF_SRCDIR = $(UTSBASE)/intel/io/pciex +CONF_SRCDIR = $(UTSBASE)/common/io/pciex # # Include common rules. diff --git a/usr/src/uts/sparc/Makefile.files b/usr/src/uts/sparc/Makefile.files index 22e344f15f..9839926d80 100644 --- a/usr/src/uts/sparc/Makefile.files +++ b/usr/src/uts/sparc/Makefile.files @@ -64,8 +64,8 @@ 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 pcie_pwr.o PCIE_MISC_OBJS += pcie_plat.o +PCIEB_OBJS += pcieb_sparc.o FCODE_OBJS += fcode.o NSKERN_OBJS += nsc_asm.o diff --git a/usr/src/uts/sparc/Makefile.sparc.shared b/usr/src/uts/sparc/Makefile.sparc.shared index 8d3f3d09e9..5ff4d10821 100644 --- a/usr/src/uts/sparc/Makefile.sparc.shared +++ b/usr/src/uts/sparc/Makefile.sparc.shared @@ -278,7 +278,7 @@ DRV_KMODS += wusb_df hwahc hwarc wusb_ca DRV_KMODS += hci1394 av1394 scsa1394 dcam1394 DRV_KMODS += sbp2 DRV_KMODS += ib ibd rdsib sdp iser daplt hermon tavor -DRV_KMODS += pci_pci px_pci pxb_plx pxb_bcm pcie +DRV_KMODS += pci_pci pcieb pcieb_bcm pcie DRV_KMODS += i8042 kb8042 mouse8042 DRV_KMODS += fcode DRV_KMODS += mpt_sas diff --git a/usr/src/uts/sun4/io/px/pxb_plx.h b/usr/src/uts/sparc/io/pciex/pcieb_plx.h index f2f407a01d..be0f15f1d1 100644 --- a/usr/src/uts/sun4/io/px/pxb_plx.h +++ b/usr/src/uts/sparc/io/pciex/pcieb_plx.h @@ -23,8 +23,8 @@ * Use is subject to license terms. */ -#ifndef _SYS_PXB_PLX_H -#define _SYS_PXB_PLX_H +#ifndef _SYS_PCIEB_PLX_H +#define _SYS_PCIEB_PLX_H #ifdef __cplusplus extern "C" { @@ -51,8 +51,12 @@ extern "C" { #define PLX_CAM_PORT_12 0x2f8 #define PLX_RO_MODE_BIT 0x20 +#define IS_PLX_VENDORID(x) (x == PXB_VENDOR_PLX) + +static int pxb_tlp_count = 64; + #ifdef __cplusplus } #endif -#endif /* _SYS_PXB_PLX_H */ +#endif /* _SYS_PCIEB_PLX_H */ diff --git a/usr/src/uts/sparc/io/pciex/pcieb_sparc.c b/usr/src/uts/sparc/io/pciex/pcieb_sparc.c new file mode 100644 index 0000000000..385c4a3aa3 --- /dev/null +++ b/usr/src/uts/sparc/io/pciex/pcieb_sparc.c @@ -0,0 +1,508 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* SPARC specific code used by the pcieb driver */ + +#include <sys/types.h> +#include <sys/ddi.h> +#include <sys/kmem.h> +#include <sys/sysmacros.h> +#include <sys/sunddi.h> +#include <sys/sunndi.h> +#include <sys/pcie.h> +#include <sys/pci_cap.h> +#include <sys/pcie_impl.h> +#include <io/pciex/pcieb.h> +#include "pcieb_plx.h" + +/*LINTLIBRARY*/ + +/* PLX specific functions */ +#ifdef PX_PLX +static void plx_ro_disable(pcieb_devstate_t *pcieb); +#ifdef PRINT_PLX_SEEPROM_CRC +static void pcieb_print_plx_seeprom_crc_data(pcieb_devstate_t *pcieb_p); +#endif /* PRINT_PLX_SEEPROM_CRC */ +#endif /* PX_PLX */ + +int +pcieb_plat_peekpoke(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t ctlop, + void *arg, void *result) +{ + return (ddi_ctlops(dip, rdip, ctlop, arg, result)); +} + +/*ARGSUSED*/ +void +pcieb_plat_attach_workaround(dev_info_t *dip) +{ +} + +int +pcieb_plat_intr_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op, + ddi_intr_handle_impl_t *hdlp, void *result) +{ + dev_info_t *cdip = rdip; + pci_regspec_t *pci_rp; + int reglen, len; + uint32_t d, intr; + + if ((intr_op == DDI_INTROP_SUPPORTED_TYPES) || + (hdlp->ih_type != DDI_INTR_TYPE_FIXED)) + goto done; + + /* + * If the interrupt-map property is defined at this + * node, it will have performed the interrupt + * translation as part of the property, so no + * rotation needs to be done. + */ + if (ddi_getproplen(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, + "interrupt-map", &len) == DDI_PROP_SUCCESS) + goto done; + + cdip = pcie_get_my_childs_dip(dip, rdip); + + /* + * Use the devices reg property to determine its + * PCI bus number and device number. + */ + if (ddi_getlongprop(DDI_DEV_T_ANY, cdip, DDI_PROP_DONTPASS, + "reg", (caddr_t)&pci_rp, ®len) != DDI_SUCCESS) + return (DDI_FAILURE); + + intr = hdlp->ih_vector; + + /* spin the interrupt */ + d = PCI_REG_DEV_G(pci_rp[0].pci_phys_hi); + if ((intr >= PCI_INTA) && (intr <= PCI_INTD)) + hdlp->ih_vector = ((intr - 1 + (d % 4)) % 4 + 1); + else + cmn_err(CE_WARN, "%s%d: %s: PCI intr=%x out of range", + ddi_driver_name(rdip), ddi_get_instance(rdip), + ddi_driver_name(dip), intr); + + kmem_free(pci_rp, reglen); + +done: + /* Pass up the request to our parent. */ + return (i_ddi_intr_ops(dip, rdip, intr_op, hdlp, result)); +} + +int +pcieb_plat_pcishpc_probe(dev_info_t *dip, ddi_acc_handle_t config_handle) +{ + uint16_t cap_ptr; + if ((PCI_CAP_LOCATE(config_handle, PCI_CAP_ID_PCI_HOTPLUG, &cap_ptr)) != + DDI_FAILURE) { + return (DDI_SUCCESS); + } + + return (DDI_FAILURE); +} + +/* + * Disable PM on PLX. For PLX Transitioning one port on this switch to + * low power causes links on other ports on the same station to die. + * Due to PLX erratum #34, we can't allow the downstream device go to + * non-D0 state. + */ +boolean_t +pcieb_plat_pwr_disable(dev_info_t *dip) +{ + uint16_t vendor_id = (PCIE_DIP2UPBUS(dip)->bus_dev_ven_id) & 0xFFFF; + return (IS_PLX_VENDORID(vendor_id) ? B_TRUE : B_FALSE); +} + +/*ARGSUSED*/ +boolean_t +pcieb_plat_msi_supported(dev_info_t *dip) +{ + return (B_TRUE); +} + +/*ARGSUSED*/ +void +pcieb_plat_intr_attach(pcieb_devstate_t *pcieb) +{ +} + +/*ARGSUSED*/ +int +pcieb_plat_ctlops(dev_info_t *rdip, ddi_ctl_enum_t ctlop, void *arg) +{ + return (DDI_SUCCESS); +} + +/*ARGSUSED*/ +void +pcieb_plat_ioctl_hotplug(dev_info_t *dip, int rv, int cmd) +{ +} + +void +pcieb_plat_initchild(dev_info_t *child) +{ + intptr_t ppd = NULL; + /* + * XXX set ppd to 1 to disable iommu BDF protection on SPARC. + * It relies on unused parent private data for PCI devices. + */ + if (ddi_prop_exists(DDI_DEV_T_NONE, child, DDI_PROP_DONTPASS, + "dvma-share")) + ppd = 1; + + ddi_set_parent_data(child, (void *)ppd); +} + +void +pcieb_plat_uninitchild(dev_info_t *child) +{ + /* + * XXX Clear parent private data used as a flag to disable + * iommu BDF protection + */ + if ((intptr_t)ddi_get_parent_data(child) == 1) + ddi_set_parent_data(child, NULL); +} + +#ifdef PX_PLX +/* + * These are PLX specific workarounds needed during attach. + */ +void +pcieb_attach_plx_workarounds(pcieb_devstate_t *pcieb) +{ + dev_info_t *dip = pcieb->pcieb_dip; + pcie_bus_t *bus_p = PCIE_DIP2BUS(dip); + ddi_acc_handle_t config_handle = bus_p->bus_cfg_hdl; + uint_t bus_num, primary, secondary; + uint8_t dev_type = bus_p->bus_dev_type; + uint16_t vendor_id = bus_p->bus_dev_ven_id & 0xFFFF; + + if (!IS_PLX_VENDORID(vendor_id)) + return; + + /* + * Due to a PLX HW bug we need to disable the receiver error CE on all + * ports. To this end we create a property "pcie_ce_mask" with value + * set to PCIE_AER_CE_RECEIVER_ERR. The pcie module will check for this + * property before setting the AER CE mask. + */ + (void) ddi_prop_update_int(DDI_DEV_T_NONE, dip, + "pcie_ce_mask", PCIE_AER_CE_RECEIVER_ERR); + + /* + * There is a bug in the PLX 8114 bridge, such that an 8-bit + * write to the secondary bus number register will corrupt an + * internal shadow copy of the primary bus number. Reading + * out the registers and writing the same values back as + * 16-bits resolves the problem. This bug was reported by + * PLX as errata #19. + */ + primary = pci_config_get8(config_handle, PCI_BCNF_PRIBUS); + secondary = pci_config_get8(config_handle, PCI_BCNF_SECBUS); + bus_num = (secondary << 8) | primary; + pci_config_put16(config_handle, PCI_BCNF_PRIBUS, bus_num); + + /* + * Workaround for a race condition between hotplug + * initialization and actual MSI interrupt registration + * for hotplug functionality. The hotplug initialization + * generates an INTx interrupt for hotplug events and this + * INTx interrupt may interfere with shared leaf drivers + * using same INTx interrupt, which may eventually block + * the leaf drivers. + */ + if ((dev_type == PCIE_PCIECAP_DEV_TYPE_DOWN) || + (dev_type == PCIE_PCIECAP_DEV_TYPE_ROOT) || + (dev_type == PCIE_PCIECAP_DEV_TYPE_PCIE2PCI) || + (dev_type == PCIE_PCIECAP_DEV_TYPE_PCI2PCIE)) { + pci_config_put16(config_handle, PCI_CONF_COMM, + pci_config_get16(config_handle, PCI_CONF_COMM) | + PCI_COMM_INTX_DISABLE); + } + + /* + * Disable PLX Special Relaxed Ordering + */ + plx_ro_disable(pcieb); + +#ifdef PRINT_PLX_SEEPROM_CRC + /* check seeprom CRC to ensure the platform config is right */ + (void) pcieb_print_plx_seeprom_crc_data(pcieb); +#endif /* PRINT_PLX_SEEPROM_CRC */ +} + +/* + * These are PLX specific workarounds called during child's initchild. + */ +int +pcieb_init_plx_workarounds(pcieb_devstate_t *pcieb, dev_info_t *child) +{ + int i; + int result = DDI_FAILURE; + uint16_t reg = 0; + ddi_acc_handle_t config_handle; + uint16_t vendor_id = + (PCIE_DIP2UPBUS(pcieb->pcieb_dip))->bus_dev_ven_id & 0xFFFF; + + if (!IS_PLX_VENDORID(vendor_id)) + return (DDI_SUCCESS); + + /* + * Due to a PLX HW bug, a SW workaround to prevent the chip from + * wedging is needed. SW just needs to tranfer 64 TLPs from + * the downstream port to the child device. + * The most benign way of doing this is to read the ID register + * 64 times. This SW workaround should have minimum performance + * impact and shouldn't cause a problem for all other bridges + * and switches. + * + * The code needs to be written in a way to make sure it isn't + * optimized out. + */ + if (!pxb_tlp_count) { + result = DDI_SUCCESS; + goto done; + } + + if (pci_config_setup(child, &config_handle) != DDI_SUCCESS) { + result = DDI_FAILURE; + goto done; + } + + for (i = 0; i < pxb_tlp_count; i += 1) + reg |= pci_config_get16(config_handle, PCI_CONF_VENID); + + if (PCIE_IS_PCIE_BDG(PCIE_DIP2BUS(pcieb->pcieb_dip))) + pcieb_set_pci_perf_parameters(child, config_handle); + + pci_config_teardown(&config_handle); + result = DDI_SUCCESS; +done: + return (result); +} + +/* + * Disable PLX specific relaxed ordering mode. Due to PLX + * erratum #6, use of this mode with Cut-Through Cancellation + * can result in dropped Completion type packets. + * + * Clear the Relaxed Ordering Mode on 8533 and 8548 switches. + * To disable RO, clear bit 5 in offset 0x664, an undocumented + * bit in the PLX spec, on Ports 0, 8 and 12. Proprietary PLX + * registers are normally accessible only via memspace from Port + * 0. If port 0 is attached go ahead and disable RO on Port 0, + * 8 and 12, if they exist. + */ +static void +plx_ro_disable(pcieb_devstate_t *pcieb) +{ + pcie_bus_t *bus_p = PCIE_DIP2BUS(pcieb->pcieb_dip); + dev_info_t *dip = pcieb->pcieb_dip; + uint16_t device_id = bus_p->bus_dev_ven_id >> 16; + pci_regspec_t *reg_spec, *addr_spec; + int rlen, alen; + int orig_rsize, new_rsize; + uint_t rnum, anum; + ddi_device_acc_attr_t attr; + ddi_acc_handle_t hdl; + caddr_t regsp; + uint32_t val, port_enable; + char *offset; + char *port_offset; + + if (!((device_id == PXB_DEVICE_PLX_8533) || + (device_id == PXB_DEVICE_PLX_8548))) + return; + + /* You can also only do this on Port 0 */ + val = PCIE_CAP_GET(32, bus_p, PCIE_LINKCAP); + val = (val >> PCIE_LINKCAP_PORT_NUMBER_SHIFT) & + PCIE_LINKCAP_PORT_NUMBER_MASK; + + PCIEB_DEBUG(DBG_ATTACH, dip, "PLX RO Disable : bdf=0x%x port=%d\n", + bus_p->bus_bdf, val); + + if (val != 0) + return; + + /* + * Read the reg property, but allocate extra space incase we need to add + * a new entry later. + */ + if (ddi_getproplen(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "reg", + &orig_rsize) != DDI_SUCCESS) + return; + + new_rsize = orig_rsize + sizeof (pci_regspec_t); + reg_spec = kmem_alloc(new_rsize, KM_SLEEP); + + if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "reg", + (caddr_t)reg_spec, &orig_rsize) != DDI_SUCCESS) + goto fail; + + /* Find the mem32 reg property */ + rlen = orig_rsize / sizeof (pci_regspec_t); + for (rnum = 0; rnum < rlen; rnum++) { + if ((reg_spec[rnum].pci_phys_hi & PCI_ADDR_MASK) == + PCI_ADDR_MEM32) + goto fix; + } + + /* + * Mem32 reg property was not found. + * Look for it in assign-address property. + */ + addr_spec = bus_p->bus_assigned_addr; + alen = bus_p->bus_assigned_entries; + for (anum = 0; anum < alen; anum++) { + if ((addr_spec[anum].pci_phys_hi & PCI_ADDR_MASK) == + PCI_ADDR_MEM32) + goto update; + } + + /* Unable to find mem space assigned address, give up. */ + goto fail; + +update: + /* + * Add the mem32 access to the reg spec. + * Use the last entry which was previously allocated. + */ + reg_spec[rnum].pci_phys_hi = (addr_spec[anum].pci_phys_hi & + ~PCI_REG_REL_M); + reg_spec[rnum].pci_phys_mid = 0; + reg_spec[rnum].pci_phys_low = 0; + reg_spec[rnum].pci_size_hi = addr_spec[anum].pci_size_hi; + reg_spec[rnum].pci_size_low = addr_spec[anum].pci_size_low; + + /* Create the new reg_spec data and update the property */ + if (ddi_prop_update_int_array(DDI_DEV_T_NONE, dip, "reg", + (int *)reg_spec, (new_rsize / sizeof (int))) != DDI_SUCCESS) + goto fail; + +fix: + attr.devacc_attr_version = DDI_DEVICE_ATTR_V0; + attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC; + attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC; + + if (ddi_regs_map_setup(dip, rnum, ®sp, 0, 0, &attr, + &hdl) != DDI_SUCCESS) + goto fail; + + /* Grab register which shows which ports are enabled */ + offset = (char *)regsp + PLX_INGRESS_PORT_ENABLE; + port_enable = ddi_get32(hdl, (uint32_t *)offset); + + if ((port_enable == 0xFFFFFFFF) || (port_enable == 0)) + goto done; + + offset = (char *)regsp + PLX_INGRESS_CONTROL_SHADOW; + + /* Disable RO on Port 0 */ + port_offset = 0x0 + offset; + val = ddi_get32(hdl, (uint32_t *)port_offset); + if (val & PLX_RO_MODE_BIT) + val ^= PLX_RO_MODE_BIT; + ddi_put32(hdl, (uint32_t *)port_offset, val); + + /* Disable RO on Port 8, but make sure its enabled */ + if (!(port_enable & (1 << 8))) + goto port12; + + port_offset = (8 * 0x1000) + offset; + val = ddi_get32(hdl, (uint32_t *)port_offset); + if (val & PLX_RO_MODE_BIT) + val ^= PLX_RO_MODE_BIT; + ddi_put32(hdl, (uint32_t *)port_offset, val); + +port12: + /* Disable RO on Port 12, but make sure it exists */ + if (!(port_enable & (1 << 12))) + goto done; + + port_offset = (12 * 0x1000) + offset; + val = ddi_get32(hdl, (uint32_t *)port_offset); + if (val & PLX_RO_MODE_BIT) + val ^= PLX_RO_MODE_BIT; + ddi_put32(hdl, (uint32_t *)port_offset, val); + + goto done; + +done: + ddi_regs_map_free(&hdl); +fail: + kmem_free(reg_spec, new_rsize); +} + +#ifdef PRINT_PLX_SEEPROM_CRC +static void +pcieb_print_plx_seeprom_crc_data(pcieb_devstate_t *pcieb_p) +{ + ddi_acc_handle_t h; + dev_info_t *dip = pcieb_p->pcieb_dip; + uint16_t vendorid = (PCIE_DIP2BUS(dip)->bus_dev_ven_id) & 0xFFFF; + 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 (vendorid != 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 /* PLX_HOT_RESET_DISABLE */ + ddi_regs_map_free(&h); +} +#endif /* PRINT_PLX_SEEPROM_CRC */ +#endif /* PX_PLX */ diff --git a/usr/src/uts/sparc/os/driver_aliases b/usr/src/uts/sparc/os/driver_aliases index 4c95e31eb4..8add121d44 100644 --- a/usr/src/uts/sparc/os/driver_aliases +++ b/usr/src/uts/sparc/os/driver_aliases @@ -145,17 +145,8 @@ ibd "ib.ipib" px "SUNW,sun4v-pci" px "pciex108e,80f0" px "pciex108e,80f8" -px_pci "pciexclass,060400" -pxb_bcm "pciex1166,103" -pxb_plx "pciex10b5,8114" -pxb_plx "pciex10b5,8532" -pxb_plx "pciex10b5,8516" -pxb_plx "pciex10b5,8548" -pxb_plx "pciex10b5,8533" -pxb_plx "pciex10b5,8517" -pxb_plx "pciex10b5,8518" -pxb_plx "pciex108e,9010" -pxb_plx "pciex108e,9020" +pcieb "pciexclass,060400" +pcieb_bcm "pciex1166,103" vnex "SUNW,sun4v-virtual-devices" vnex "SUNW,virtual-devices" mi2cv "fire-i2c" diff --git a/usr/src/uts/sparc/os/name_to_major b/usr/src/uts/sparc/os/name_to_major index ee5c8793df..f384fa995d 100644 --- a/usr/src/uts/sparc/os/name_to_major +++ b/usr/src/uts/sparc/os/name_to_major @@ -189,7 +189,7 @@ ntwdt 240 dld 241 aggr 242 px 243 -px_pci 244 +pcieb 244 qcn 245 vnex 246 glvc 247 @@ -212,8 +212,7 @@ scfd 263 mc-opl 264 dm2s 265 oplkmdrv 266 -pxb_bcm 267 -pxb_plx 268 +pcieb_bcm 267 n2rng 269 physmem 270 ds_snmp 271 diff --git a/usr/src/uts/sparc/pxb_plx/Makefile b/usr/src/uts/sparc/pcieb/Makefile index 5c1e21036c..f6690fec79 100644 --- a/usr/src/uts/sparc/pxb_plx/Makefile +++ b/usr/src/uts/sparc/pcieb/Makefile @@ -19,14 +19,12 @@ # CDDL HEADER END # # -# uts/sparc/pxb_plx/Makefile +# uts/sparc/pcieb/Makefile # -# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Copyright 2009 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 pxb_plx driver kernel module +# This makefile drives the production of the pcieb driver kernel module # # sparc implementation architecture dependent # @@ -39,11 +37,11 @@ UTSBASE = ../.. # # Define the module and object file sets. # -MODULE = pxb_plx -OBJECTS = $(PX_PCI_OBJS:%=$(OBJS_DIR)/%) -LINTS = $(PX_PCI_OBJS:%.o=$(LINTS_DIR)/%.ln) +MODULE = pcieb +OBJECTS = $(PCIEB_OBJS:%=$(OBJS_DIR)/%) +LINTS = $(PCIEB_OBJS:%.o=$(LINTS_DIR)/%.ln) ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE) -CONF_SRCDIR = $(UTSBASE)/sun4/io/px/ +CONF_SRCDIR = $(UTSBASE)/common/io/pciex/ # # Include common rules. @@ -58,11 +56,6 @@ LINT_TARGET = $(MODULE).lint INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE) # -# Include sun4 specific headers files -# -INC_PATH += -I$(UTSBASE)/sun4 - -# # lint pass one enforcement # CFLAGS += $(CCVERBOSE) diff --git a/usr/src/uts/sparc/pxb_bcm/Makefile b/usr/src/uts/sparc/pcieb_bcm/Makefile index 750867e7eb..a6eee4a8ce 100644 --- a/usr/src/uts/sparc/pxb_bcm/Makefile +++ b/usr/src/uts/sparc/pcieb_bcm/Makefile @@ -19,14 +19,13 @@ # CDDL HEADER END # # -# uts/sparc/pxb_bcm/Makefile +# uts/sparc/pcieb_bcm/Makefile # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2009 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 pxb_bcm driver kernel module +# This makefile drives the production of the pcieb_bcm driver kernel module # # sparc implementation architecture dependent # @@ -39,9 +38,9 @@ UTSBASE = ../.. # # Define the module and object file sets. # -MODULE = pxb_bcm -OBJECTS = $(PX_PCI_OBJS:%=$(OBJS_DIR)/%) -LINTS = $(PX_PCI_OBJS:%.o=$(LINTS_DIR)/%.ln) +MODULE = pcieb_bcm +OBJECTS = $(PCIEB_OBJS:%=$(OBJS_DIR)/%) +LINTS = $(PCIEB_OBJS:%.o=$(LINTS_DIR)/%.ln) ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE) # @@ -57,11 +56,6 @@ LINT_TARGET = $(MODULE).lint INSTALL_TARGET = $(BINARY) $(ROOTMODULE) # -# Include sun4 specific headers files -# -INC_PATH += -I$(UTSBASE)/sun4 - -# # lint pass one enforcement # CFLAGS += $(CCVERBOSE) @@ -75,7 +69,7 @@ CFLAGS += -dalign # Enable Broadcom 5714/5715 workaround code and lint duplicate symbol # avoidance hack # -CPPFLAGS += -DBCM_SW_WORKAROUNDS -DPX_MOD_NAME=pxb_bcm +CPPFLAGS += -DBCM_SW_WORKAROUNDS -DPX_MOD_NAME=pcieb_bcm # # Dependency diff --git a/usr/src/uts/sparc/px_pci/Makefile b/usr/src/uts/sparc/px_pci/Makefile deleted file mode 100644 index 7bd7e1c8b6..0000000000 --- a/usr/src/uts/sparc/px_pci/Makefile +++ /dev/null @@ -1,112 +0,0 @@ -# -# CDDL HEADER START -# -# The contents of this file are subject to the terms of the -# Common Development and Distribution License (the "License"). -# You may not use this file except in compliance with the License. -# -# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE -# or http://www.opensolaris.org/os/licensing. -# See the License for the specific language governing permissions -# and limitations under the License. -# -# When distributing Covered Code, include this CDDL HEADER in each -# file and include the License file at usr/src/OPENSOLARIS.LICENSE. -# If applicable, add the following below this CDDL HEADER, with the -# fields enclosed by brackets "[]" replaced with your own identifying -# information: Portions Copyright [yyyy] [name of copyright owner] -# -# CDDL HEADER END -# -# -# uts/sparc/px_pci/Makefile -# -# Copyright 2007 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 px_pci driver kernel module -# -# sparc implementation architecture dependent -# - -# -# Path to the base of the uts directory tree (usually /usr/src/uts). -# -UTSBASE = ../.. - -# -# Define the module and object file sets. -# -MODULE = px_pci -OBJECTS = $(PX_PCI_OBJS:%=$(OBJS_DIR)/%) -LINTS = $(PX_PCI_OBJS:%.o=$(LINTS_DIR)/%.ln) -ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE) -CONF_SRCDIR = $(UTSBASE)/sun4/io/px/ - -# -# Include common rules. -# -include $(UTSBASE)/sparc/Makefile.sparc - -# -# Define targets -# -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 - -# -# lint pass one enforcement -# -CFLAGS += $(CCVERBOSE) - -# -# Turn on doubleword alignment for 64 bit registers -# -CFLAGS += -dalign - -# -# Dependency -# -LDFLAGS += -dy -Nmisc/pcie -Nmisc/pcishpc -Nmisc/pcihp -Nmisc/pciehpc - -# -# For now, disable these lint checks; maintainers should endeavor -# to investigate and remove these for maximum lint coverage. -# Please do not carry these forward to new Makefiles. -# -LINTTAGS += -erroff=E_BAD_PTR_CAST_ALIGN -LINTTAGS += -erroff=E_STATIC_UNUSED - -# -# 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/sun4/io/px/px.c b/usr/src/uts/sun4/io/px/px.c index c2de28bc8a..22eff51642 100644 --- a/usr/src/uts/sun4/io/px/px.c +++ b/usr/src/uts/sun4/io/px/px.c @@ -43,7 +43,7 @@ #include "px_obj.h" #include <sys/pci_tools.h> #include "px_tools_ext.h" -#include "pcie_pwr.h" +#include <sys/pcie_pwr.h> /*LINTLIBRARY*/ @@ -63,8 +63,6 @@ static void px_set_mps(px_t *px_p); extern int pcie_max_mps; -extern errorq_t *pci_target_queue; - /* * function prototypes for hotplug routines: */ @@ -174,13 +172,6 @@ _fini(void) e = mod_remove(&modlinkage); if (e != DDI_SUCCESS) return (e); - /* - * Destroy pci_target_queue, and set it to NULL. - */ - if (pci_target_queue) - errorq_destroy(pci_target_queue); - - pci_target_queue = NULL; /* Free px soft state */ ddi_soft_state_fini(&px_state_p); @@ -720,7 +711,7 @@ px_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp, * There may be a need to differentiate between PCI * and PCI-Ex devices so the following range check is * done correctly, depending on the implementation of - * px_pci bridge nexus driver. + * pcieb bridge nexus driver. */ if ((off >= PCIE_CONF_HDR_SIZE) || (len > PCIE_CONF_HDR_SIZE) || diff --git a/usr/src/uts/sun4/io/px/px_devctl.c b/usr/src/uts/sun4/io/px/px_devctl.c index b140cdd89f..253dfd6503 100644 --- a/usr/src/uts/sun4/io/px/px_devctl.c +++ b/usr/src/uts/sun4/io/px/px_devctl.c @@ -42,7 +42,7 @@ #include "px_obj.h" #include <sys/pci_tools.h> #include "px_tools_ext.h" -#include "pcie_pwr.h" +#include <sys/pcie_pwr.h> /*LINTLIBRARY*/ diff --git a/usr/src/uts/sun4/io/px/px_fm.c b/usr/src/uts/sun4/io/px/px_fm.c index 987957ce96..958c18f42b 100644 --- a/usr/src/uts/sun4/io/px/px_fm.c +++ b/usr/src/uts/sun4/io/px/px_fm.c @@ -77,12 +77,6 @@ px_fm_attach(px_t *px_p) DDI_FM_ACCCHK_CAPABLE | DDI_FM_DMACHK_CAPABLE; /* - * Initialize pci_target_queue for FMA handling of - * pci errors. - */ - pci_targetq_init(); - - /* * check parents' capability */ ddi_fm_init(dip, &px_p->px_fm_cap, &px_p->px_fm_ibc); diff --git a/usr/src/uts/sun4/io/px/px_pci.c b/usr/src/uts/sun4/io/px/px_pci.c deleted file mode 100644 index 18671c5a7b..0000000000 --- a/usr/src/uts/sun4/io/px/px_pci.c +++ /dev/null @@ -1,2273 +0,0 @@ -/* - * CDDL HEADER START - * - * The contents of this file are subject to the terms of the - * Common Development and Distribution License (the "License"). - * You may not use this file except in compliance with the License. - * - * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE - * or http://www.opensolaris.org/os/licensing. - * See the License for the specific language governing permissions - * and limitations under the License. - * - * When distributing Covered Code, include this CDDL HEADER in each - * file and include the License file at usr/src/OPENSOLARIS.LICENSE. - * If applicable, add the following below this CDDL HEADER, with the - * fields enclosed by brackets "[]" replaced with your own identifying - * information: Portions Copyright [yyyy] [name of copyright owner] - * - * CDDL HEADER END - */ -/* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. - */ - - -/* - * Sun4 PCI Express to PCI bus bridge nexus driver - */ - -#include <sys/sysmacros.h> -#include <sys/conf.h> -#include <sys/kmem.h> -#include <sys/debug.h> -#include <sys/modctl.h> -#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/pci_impl.h> -#include <sys/ddi.h> -#include <sys/sunndi.h> -#include <sys/pci_cap.h> -#include <sys/hotplug/pci/pcihp.h> -#include <sys/hotplug/pci/pciehpc.h> -#include <sys/hotplug/pci/pcishpc.h> -#include <sys/open.h> -#include <sys/stat.h> -#include <sys/file.h> -#include <sys/promif.h> /* prom_printf */ -#include "pcie_pwr.h" -#include "px_pci.h" -#ifdef PX_PLX -#include "pxb_plx.h" -#endif /* PX_PLX */ - -#if defined(DEBUG) -#define DBG pxb_dbg -static void pxb_dbg(uint_t bit, dev_info_t *dip, char *fmt, ...); -static uint_t pxb_dbg_print = 0; - -#else /* DEBUG */ - -#define DBG 0 && - -#endif /* DEBUG */ - -typedef enum { /* same sequence as px_debug_sym[] */ - /* 0 */ DBG_ATTACH, - /* 1 */ DBG_PWR -} pxb_debug_bit_t; - -static char *pxb_debug_sym [] = { /* same sequence as px_debug_bit */ - /* 0 */ "attach", - /* 1 */ "pwr" -}; - -/* Tunables. Beware: Some are for debug purpose only. */ -/* - * PXB MSI tunable: - * - * By default MSI is enabled on all supported platforms. - */ -static boolean_t pxb_enable_msi = B_TRUE; /* MSI enabled if TRUE, else INTX */ - -static int pxb_bus_map(dev_info_t *, dev_info_t *, ddi_map_req_t *, - off_t, off_t, caddr_t *); -static int pxb_dma_allochdl(dev_info_t *dip, dev_info_t *rdip, - ddi_dma_attr_t *attr_p, int (*waitfp)(caddr_t), caddr_t arg, - ddi_dma_handle_t *handlep); -static int pxb_dma_mctl(dev_info_t *dip, dev_info_t *rdip, - ddi_dma_handle_t handle, enum ddi_dma_ctlops cmd, off_t *offp, - size_t *lenp, caddr_t *objp, uint_t cache_flags); -static int pxb_ctlops(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, - void *, void *); -static int pxb_intr_ops(dev_info_t *dip, dev_info_t *rdip, - ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, void *result); - -/* - * FMA functions - */ -static int pxb_fm_init(pxb_devstate_t *pxb_p); -static void pxb_fm_fini(pxb_devstate_t *pxb_p); -static int pxb_fm_init_child(dev_info_t *dip, dev_info_t *cdip, int cap, - ddi_iblock_cookie_t *ibc_p); - -static void pxb_set_pci_perf_parameters(dev_info_t *dip, - ddi_acc_handle_t config_handle); -#ifdef PRINT_PLX_SEEPROM_CRC -static void pxb_print_plx_seeprom_crc_data(pxb_devstate_t *pxb_p); -#endif - -static struct bus_ops pxb_bus_ops = { - BUSO_REV, - pxb_bus_map, - 0, - 0, - 0, - i_ddi_map_fault, - ddi_dma_map, - pxb_dma_allochdl, - ddi_dma_freehdl, - ddi_dma_bindhdl, - ddi_dma_unbindhdl, - ddi_dma_flush, - ddi_dma_win, - pxb_dma_mctl, - pxb_ctlops, - ddi_bus_prop_op, - ndi_busop_get_eventcookie, /* (*bus_get_eventcookie)(); */ - ndi_busop_add_eventcall, /* (*bus_add_eventcall)(); */ - ndi_busop_remove_eventcall, /* (*bus_remove_eventcall)(); */ - ndi_post_event, /* (*bus_post_event)(); */ - NULL, /* (*bus_intr_ctl)(); */ - NULL, /* (*bus_config)(); */ - NULL, /* (*bus_unconfig)(); */ - pxb_fm_init_child, /* (*bus_fm_init)(); */ - NULL, /* (*bus_fm_fini)(); */ - i_ndi_busop_access_enter, /* (*bus_fm_access_enter)(); */ - i_ndi_busop_access_exit, /* (*bus_fm_access_fini)(); */ - pcie_bus_power, /* (*bus_power)(); */ - pxb_intr_ops /* (*bus_intr_op)(); */ -}; - -static int pxb_open(dev_t *devp, int flags, int otyp, cred_t *credp); -static int pxb_close(dev_t dev, int flags, int otyp, cred_t *credp); -static int pxb_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, - cred_t *credp, int *rvalp); -static int pxb_prop_op(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op, - int flags, char *name, caddr_t valuep, int *lengthp); - -static struct cb_ops pxb_cb_ops = { - pxb_open, /* open */ - pxb_close, /* close */ - nulldev, /* strategy */ - nulldev, /* print */ - nulldev, /* dump */ - nulldev, /* read */ - nulldev, /* write */ - pxb_ioctl, /* ioctl */ - nodev, /* devmap */ - nodev, /* mmap */ - nodev, /* segmap */ - nochpoll, /* poll */ - pxb_prop_op, /* cb_prop_op */ - NULL, /* streamtab */ - D_NEW | D_MP | D_HOTPLUG, /* Driver compatibility flag */ - CB_REV, /* rev */ - nodev, /* int (*cb_aread)() */ - nodev /* int (*cb_awrite)() */ -}; - -static int pxb_probe(dev_info_t *); -static int pxb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd); -static int pxb_detach(dev_info_t *devi, ddi_detach_cmd_t cmd); -static int pxb_info(dev_info_t *dip, ddi_info_cmd_t infocmd, - void *arg, void **result); -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); - -/* PLX specific functions */ -#ifdef PX_PLX -static int plx_pwr_disable(dev_info_t *dip); -static void plx_ro_disable(pxb_devstate_t *pxb); -#endif /* PX_PLX */ - - -/* 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 int pxb_init_hotplug(pxb_devstate_t *pxb); -static void pxb_id_props(pxb_devstate_t *pxb); - -static struct dev_ops pxb_ops = { - DEVO_REV, /* devo_rev */ - 0, /* refcnt */ - pxb_info, /* info */ - nulldev, /* identify */ - pxb_probe, /* probe */ - pxb_attach, /* attach */ - pxb_detach, /* detach */ - nulldev, /* reset */ - &pxb_cb_ops, /* driver operations */ - &pxb_bus_ops, /* bus operations */ - pcie_power, /* power entry */ - ddi_quiesce_not_needed, /* quiesce */ -}; - -/* - * Module linkage information for the kernel. - */ - -static struct modldrv modldrv = { - &mod_driverops, /* Type of module */ - "PCIe/PCI nexus driver", - &pxb_ops, /* driver ops */ -}; - -static struct modlinkage modlinkage = { - MODREV_1, - (void *)&modldrv, - NULL -}; - -/* - * soft state pointer and structure template: - */ -void *pxb_state; - -/* - * SW workaround for PLX HW bug Flag - */ -static int pxb_tlp_count = 64; - -/* - * forward function declarations: - */ -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 void pxb_removechild(dev_info_t *); -static int pxb_initchild(dev_info_t *child); -static void pxb_create_ranges_prop(dev_info_t *, ddi_acc_handle_t); - -int -_init(void) -{ - int e; - if ((e = ddi_soft_state_init(&pxb_state, sizeof (pxb_devstate_t), - 1)) == 0 && (e = mod_install(&modlinkage)) != 0) - ddi_soft_state_fini(&pxb_state); - return (e); -} - -int -_fini(void) -{ - int e; - - if ((e = mod_remove(&modlinkage)) == 0) - ddi_soft_state_fini(&pxb_state); - return (e); -} - -int -_info(struct modinfo *modinfop) -{ - return (mod_info(&modlinkage, modinfop)); -} - -/*ARGSUSED*/ -static int -pxb_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) -{ - pxb_devstate_t *pxb_p; /* per pxb state pointer */ - minor_t minor = getminor((dev_t)arg); - int instance = PCIHP_AP_MINOR_NUM_TO_INSTANCE(minor); - - pxb_p = (pxb_devstate_t *)ddi_get_soft_state(pxb_state, - instance); - - switch (infocmd) { - default: - return (DDI_FAILURE); - - case DDI_INFO_DEVT2INSTANCE: - *result = (void *)(intptr_t)instance; - return (DDI_SUCCESS); - - case DDI_INFO_DEVT2DEVINFO: - if (pxb_p == NULL) - return (DDI_FAILURE); - *result = (void *)pxb_p->pxb_dip; - return (DDI_SUCCESS); - } -} - -/*ARGSUSED*/ -static int -pxb_probe(register dev_info_t *devi) -{ - return (DDI_PROBE_SUCCESS); -} - -static boolean_t -pxb_is_pcie_device_type(dev_info_t *dip) -{ - pcie_bus_t *bus_p = PCIE_DIP2BUS(dip); - - if (PCIE_IS_SW(bus_p) || PCIE_IS_RP(bus_p) || PCIE_IS_PCI2PCIE(bus_p)) - return (B_TRUE); - - return (B_FALSE); -} - -/*ARGSUSED*/ -static int -pxb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) -{ - pcie_bus_t *bus_p = PCIE_DIP2BUS(devi); - int instance; - pxb_devstate_t *pxb; - ddi_acc_handle_t config_handle; - char device_type[8]; - uint8_t dev_type = bus_p->bus_dev_type; -#ifdef PX_PLX - uint_t bus_num, primary, secondary; -#endif /* PX_PLX */ - - instance = ddi_get_instance(devi); - - switch (cmd) { - case DDI_RESUME: - DBG(DBG_ATTACH, devi, "DDI_RESUME\n"); - /* - * Get the soft state structure for the bridge. - */ - pxb = (pxb_devstate_t *)ddi_get_soft_state(pxb_state, instance); - (void) pcie_pwr_resume(devi); - - return (DDI_SUCCESS); - - case DDI_ATTACH: - DBG(DBG_ATTACH, devi, "DDI_ATTACH\n"); - - /* Follow through to below the switch statement */ - break; - default: - return (DDI_FAILURE); - } - - /* - * Allocate and get soft state structure. - */ - if (ddi_soft_state_zalloc(pxb_state, instance) != DDI_SUCCESS) { - DBG(DBG_ATTACH, devi, "Unable to allocate soft state.\n"); - return (DDI_FAILURE); - } - - pxb = (pxb_devstate_t *)ddi_get_soft_state(pxb_state, instance); - pxb->pxb_dip = devi; - pxb->pxb_soft_state = PXB_SOFT_STATE_CLOSED; - - /* Create Mutex */ - mutex_init(&pxb->pxb_mutex, NULL, MUTEX_DRIVER, NULL); - pxb->pxb_init_flags = PXB_INIT_MUTEX; - - /* Setup and save the config space pointer */ - if (pci_config_setup(devi, &config_handle) != DDI_SUCCESS) { - DBG(DBG_ATTACH, devi, "Failed in pci_config_setup call\n"); - goto fail; - } - pxb->pxb_config_handle = config_handle; - pxb->pxb_init_flags |= PXB_INIT_CONFIG_HANDLE; - - /* Save the vendor 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); - - /* create special properties for device identification */ - pxb_id_props(pxb); - - /* - * Power management setup. This also makes sure that switch/bridge - * is at D0 during attach. - */ - if (pwr_common_setup(devi) != DDI_SUCCESS) { - DBG(DBG_PWR, devi, "pwr_common_setup failed\n"); - goto fail; - } - -#ifdef PX_PLX - if (plx_pwr_disable(devi) != DDI_SUCCESS) { - DBG(DBG_PWR, devi, "plx_pwr_disable failed \n"); -#else - if (pxb_pwr_setup(devi) != DDI_SUCCESS) { - DBG(DBG_PWR, devi, "pxb_pwr_setup failed \n"); -#endif /* PX_PLX */ - goto fail; - } - - if (!(PCIE_IS_BDG(bus_p))) { - DBG(DBG_ATTACH, devi, "This is not a switch or bridge\n"); - goto fail; - } - - /* - * Make sure the "device_type" property exists. - */ - if (pxb_is_pcie_device_type(devi)) - (void) strcpy(device_type, "pciex"); - else - (void) strcpy(device_type, "pci"); - - (void) ddi_prop_update_string(DDI_DEV_T_NONE, devi, - "device_type", device_type); - - /* - * Check whether the "ranges" property is present. - * Otherwise create the ranges property by reading - * the configuration registers - */ - if (ddi_prop_exists(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS, - "ranges") == 0) { - pxb_create_ranges_prop(devi, config_handle); - } - - /* - * Create an integer property with PCIE2PCI bridge's secondary - * PCI bus number. This property will be read and saved in all - * PCI and PCI-X device driver's parent private data structure - * as part of their init child function. - */ - if (PCIE_IS_PCI_BDG(bus_p)) { - if (ndi_prop_update_int(DDI_DEV_T_NONE, pxb->pxb_dip, - "pcie2pci-sec-bus", pci_config_get8(config_handle, - PCI_BCNF_SECBUS)) != DDI_PROP_SUCCESS) { - DBG(DBG_ATTACH, pxb->pxb_dip, - "ndi_prop_update_int() failed\n"); - goto fail; - } - - pxb_set_pci_perf_parameters(devi, config_handle); - } - - /* - * Initialize hotplug support on this bus except for the PLX 8532 - * revision AA. At a minimum (for non hotplug bus) this would create - * ":devctl" minor node to support DEVCTL_DEVICE_* and DEVCTL_BUS_* - * ioctls to this bus. This all takes place if this nexus has hot-plug - * slots and successfully initializes Hot Plug Framework. - */ - pxb->pxb_hotplug_capable = B_FALSE; - -#ifdef PX_PLX - /* - * Due to a PLX HW bug we need to disable the receiver error CE on all - * ports. To this end we create a property "pcie_ce_mask" with value - * set to PCIE_AER_CE_RECEIVER_ERR. The pcie module will check for this - * property before setting the AER CE mask. - */ - (void) ddi_prop_update_int(DDI_DEV_T_NONE, pxb->pxb_dip, - "pcie_ce_mask", PCIE_AER_CE_RECEIVER_ERR); - - /* - * There is a bug in the PLX 8114 bridge, such that an 8-bit - * write to the secondary bus number register will corrupt an - * internal shadow copy of the primary bus number. Reading - * out the registers and writing the same values back as - * 16-bits resolves the problem. This bug was reported by - * PLX as errata #19. - */ - primary = pci_config_get8(config_handle, PCI_BCNF_PRIBUS); - secondary = pci_config_get8(config_handle, PCI_BCNF_SECBUS); - bus_num = (secondary << 8) | primary; - pci_config_put16(config_handle, PCI_BCNF_PRIBUS, bus_num); - - /* - * Disable PLX Special Relaxed Ordering - */ - plx_ro_disable(pxb); - - if ((pxb->pxb_device_id == PXB_DEVICE_PLX_8532) && - (pxb->pxb_rev_id <= PXB_DEVICE_PLX_AA_REV)) - goto hotplug_done; -#endif /* PX_PLX */ - - if ((dev_type == PCIE_PCIECAP_DEV_TYPE_DOWN) || - (dev_type == PCIE_PCIECAP_DEV_TYPE_ROOT) || - (dev_type == PCIE_PCIECAP_DEV_TYPE_PCIE2PCI) || - (dev_type == PCIE_PCIECAP_DEV_TYPE_PCI2PCIE)) { -#ifdef PX_PLX - /* - * Workaround for a race condition between hotplug - * initialization and actual MSI interrupt registration - * for hotplug functionality. The hotplug initialization - * generates an INTx interrupt for hotplug events and this - * INTx interrupt may interfere with shared leaf drivers - * using same INTx interrupt, which may eventually block - * the leaf drivers. - */ - pci_config_put16(config_handle, PCI_CONF_COMM, - pci_config_get16(config_handle, PCI_CONF_COMM) | - PCI_COMM_INTX_DISABLE); -#endif /* PX_PLX */ - - if (pxb_init_hotplug(pxb) != DDI_SUCCESS) - goto fail; - } - -hotplug_done: -#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 - */ - if (ddi_create_minor_node(devi, "devctl", S_IFCHR, - PCIHP_AP_MINOR_NUM(instance, PCIHP_DEVCTL_MINOR), - DDI_NT_NEXUS, 0) != DDI_SUCCESS) - goto fail; - } - - DBG(DBG_ATTACH, devi, - "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; - - /* - * If this is a root port, determine and set the max payload size. - */ - if (PCIE_IS_RP(bus_p)) - pcie_init_root_port_mps(devi); - - ddi_report_dev(devi); - - return (DDI_SUCCESS); - -fail: - (void) pxb_detach(devi, DDI_DETACH); - - return (DDI_FAILURE); -} - -/*ARGSUSED*/ -static int -pxb_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) -{ - pxb_devstate_t *pxb; - int error = DDI_SUCCESS; - - switch (cmd) { - case DDI_DETACH: - /* - * And finally free the per-pci soft state after - * uninitializing hotplug support for this bus in - * opposite order of attach. - */ - pxb = (pxb_devstate_t *) - ddi_get_soft_state(pxb_state, ddi_get_instance(devi)); - -#ifdef PX_PLX - (void) ndi_prop_remove(DDI_DEV_T_NONE, pxb->pxb_dip, - "pcie_ce_mask"); -#endif /* PX_PLX */ - - 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); - - pxb_intr_fini(pxb); - } - else - ddi_remove_minor_node(devi, "devctl"); - - (void) ddi_prop_remove(DDI_DEV_T_NONE, devi, "device_type"); - - if (pxb->pxb_init_flags & PXB_INIT_FM) - pxb_fm_fini(pxb); - - (void) ndi_prop_remove(DDI_DEV_T_NONE, pxb->pxb_dip, - "pcie2pci-sec-bus"); - - if (pxb->pxb_init_flags & PXB_INIT_CONFIG_HANDLE) - pci_config_teardown(&pxb->pxb_config_handle); - - pxb_pwr_teardown(devi); - pwr_common_teardown(devi); - if (pxb->pxb_init_flags & PXB_INIT_MUTEX) - mutex_destroy(&pxb->pxb_mutex); - - ddi_soft_state_free(pxb_state, ddi_get_instance(devi)); - - return (error); - - case DDI_SUSPEND: - pxb = (pxb_devstate_t *) - ddi_get_soft_state(pxb_state, ddi_get_instance(devi)); - - error = pcie_pwr_suspend(devi); - - return (error); - } - return (DDI_FAILURE); -} - -/*ARGSUSED*/ -static int -pxb_bus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp, - off_t offset, off_t len, caddr_t *vaddrp) -{ - register dev_info_t *pdip; - - pdip = (dev_info_t *)DEVI(dip)->devi_parent; - return ((DEVI(pdip)->devi_ops->devo_bus_ops->bus_map) - (pdip, rdip, mp, offset, len, vaddrp)); -} - -/*ARGSUSED*/ -static int -pxb_ctlops(dev_info_t *dip, dev_info_t *rdip, - ddi_ctl_enum_t ctlop, void *arg, void *result) -{ - pci_regspec_t *drv_regp; - int reglen; - int rn; - int totreg; - struct detachspec *ds; - struct attachspec *as; - pxb_devstate_t *pxb_p; - - pxb_p = (pxb_devstate_t *)ddi_get_soft_state(pxb_state, - ddi_get_instance(dip)); - - switch (ctlop) { - case DDI_CTLOPS_REPORTDEV: - if (rdip == (dev_info_t *)0) - return (DDI_FAILURE); - cmn_err(CE_CONT, "?PCI-device: %s@%s, %s%d\n", - ddi_node_name(rdip), ddi_get_name_addr(rdip), - ddi_driver_name(rdip), - ddi_get_instance(rdip)); - return (DDI_SUCCESS); - - case DDI_CTLOPS_INITCHILD: - return (pxb_initchild((dev_info_t *)arg)); - - case DDI_CTLOPS_UNINITCHILD: - pxb_removechild((dev_info_t *)arg); - return (DDI_SUCCESS); - - case DDI_CTLOPS_SIDDEV: - return (DDI_SUCCESS); - - case DDI_CTLOPS_REGSIZE: - case DDI_CTLOPS_NREGS: - if (rdip == (dev_info_t *)0) - return (DDI_FAILURE); - break; - - case DDI_CTLOPS_ATTACH: - if (!pcie_is_child(dip, rdip)) - return (DDI_SUCCESS); - - as = (struct attachspec *)arg; - switch (as->when) { - case DDI_PRE: - if (as->cmd == DDI_ATTACH) { - DBG(DBG_PWR, dip, "PRE_ATTACH for %s@%d\n", - ddi_driver_name(rdip), - ddi_get_instance(rdip)); - return (pcie_pm_hold(dip)); - } - if (as->cmd == DDI_RESUME) { - DBG(DBG_PWR, dip, "PRE_RESUME for %s@%d\n", - ddi_driver_name(rdip), - ddi_get_instance(rdip)); - - pcie_clear_errors(rdip); - } - return (DDI_SUCCESS); - - case DDI_POST: { - DBG(DBG_PWR, dip, "POST_ATTACH for %s@%d\n", - ddi_driver_name(rdip), ddi_get_instance(rdip)); - if (as->cmd == DDI_ATTACH && as->result != DDI_SUCCESS) - pcie_pm_release(dip); - - if (as->result == DDI_SUCCESS) - pf_init(rdip, (void *)pxb_p->pxb_fm_ibc, - as->cmd); - - /* - * For empty hotplug-capable slots, we should explicitly - * disable the errors, so that we won't panic upon - * unsupported hotplug messages. - */ - if ((!ddi_prop_exists(DDI_DEV_T_ANY, rdip, - DDI_PROP_DONTPASS, "hotplug-capable")) || - ddi_get_child(rdip)) { - (void) pcie_postattach_child(rdip); - return (DDI_SUCCESS); - } - - pcie_disable_errors(rdip); - - return (DDI_SUCCESS); - } - default: - break; - } - break; - - case DDI_CTLOPS_DETACH: - if (!pcie_is_child(dip, rdip)) - return (DDI_SUCCESS); - - ds = (struct detachspec *)arg; - switch (ds->when) { - case DDI_PRE: - pf_fini(rdip, ds->cmd); - return (DDI_SUCCESS); - - case DDI_POST: - if (ds->cmd == DDI_DETACH && - ds->result == DDI_SUCCESS) { - DBG(DBG_PWR, dip, "POST_DETACH for %s@%d\n", - ddi_driver_name(rdip), - ddi_get_instance(rdip)); - return (pcie_pm_remove_child(dip, rdip)); - } - return (DDI_SUCCESS); - default: - break; - } - break; - - default: - return (ddi_ctlops(dip, rdip, ctlop, arg, result)); - } - - *(int *)result = 0; - if (ddi_getlongprop(DDI_DEV_T_ANY, rdip, - DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP, "reg", - (caddr_t)&drv_regp, ®len) != DDI_SUCCESS) - return (DDI_FAILURE); - - totreg = reglen / sizeof (pci_regspec_t); - if (ctlop == DDI_CTLOPS_NREGS) - *(int *)result = totreg; - else if (ctlop == DDI_CTLOPS_REGSIZE) { - rn = *(int *)arg; - if (rn >= totreg) { - kmem_free(drv_regp, reglen); - return (DDI_FAILURE); - } - *(off_t *)result = drv_regp[rn].pci_size_low | - ((uint64_t)drv_regp[rn].pci_size_hi << 32); - } - - kmem_free(drv_regp, reglen); - return (DDI_SUCCESS); -} - - -static int -pxb_intr_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op, - ddi_intr_handle_impl_t *hdlp, void *result) -{ - dev_info_t *cdip = rdip; - pci_regspec_t *pci_rp; - int reglen, len; - uint32_t d, intr; - - if ((intr_op == DDI_INTROP_SUPPORTED_TYPES) || - (hdlp->ih_type != DDI_INTR_TYPE_FIXED)) - goto done; - - /* - * If the interrupt-map property is defined at this - * node, it will have performed the interrupt - * translation as part of the property, so no - * rotation needs to be done. - */ - if (ddi_getproplen(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, - "interrupt-map", &len) == DDI_PROP_SUCCESS) - goto done; - - cdip = pcie_get_my_childs_dip(dip, rdip); - - /* - * Use the devices reg property to determine its - * PCI bus number and device number. - */ - if (ddi_getlongprop(DDI_DEV_T_ANY, cdip, DDI_PROP_DONTPASS, - "reg", (caddr_t)&pci_rp, ®len) != DDI_SUCCESS) - return (DDI_FAILURE); - - intr = hdlp->ih_vector; - - /* spin the interrupt */ - d = PCI_REG_DEV_G(pci_rp[0].pci_phys_hi); - if ((intr >= PCI_INTA) && (intr <= PCI_INTD)) - hdlp->ih_vector = ((intr - 1 + (d % 4)) % 4 + 1); - else - cmn_err(CE_WARN, "%s%d: %s: PCI intr=%x out of range", - ddi_driver_name(rdip), ddi_get_instance(rdip), - ddi_driver_name(dip), intr); - - kmem_free(pci_rp, reglen); - -done: - /* Pass up the request to our parent. */ - return (i_ddi_intr_ops(dip, rdip, intr_op, hdlp, result)); -} - -/* - * name_child - * - * This function is called from init_child to name a node. It is - * also passed as a callback for node merging functions. - * - * return value: DDI_SUCCESS, DDI_FAILURE - */ -static int -pxb_name_child(dev_info_t *child, char *name, int namelen) -{ - pci_regspec_t *pci_rp; - uint_t slot, func; - char **unit_addr; - uint_t n; - - /* - * Pseudo nodes indicate a prototype node with per-instance - * properties to be merged into the real h/w device node. - * The interpretation of the unit-address is DD[,F] - * where DD is the device id and F is the function. - */ - if (ndi_dev_is_persistent_node(child) == 0) { - if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, child, - DDI_PROP_DONTPASS, "unit-address", &unit_addr, &n) != - DDI_PROP_SUCCESS) { - cmn_err(CE_WARN, "cannot name node from %s.conf", - ddi_driver_name(child)); - return (DDI_FAILURE); - } - if (n != 1 || *unit_addr == NULL || **unit_addr == 0) { - cmn_err(CE_WARN, "unit-address property in %s.conf" - " not well-formed", ddi_driver_name(child)); - ddi_prop_free(unit_addr); - return (DDI_FAILURE); - } - (void) snprintf(name, namelen, "%s", *unit_addr); - ddi_prop_free(unit_addr); - return (DDI_SUCCESS); - } - - /* - * Get the address portion of the node name based on - * the function and device number. - */ - if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, - "reg", (int **)&pci_rp, &n) != DDI_SUCCESS) { - return (DDI_FAILURE); - } - - slot = PCI_REG_DEV_G(pci_rp[0].pci_phys_hi); - func = PCI_REG_FUNC_G(pci_rp[0].pci_phys_hi); - - if (func != 0) - (void) snprintf(name, namelen, "%x,%x", slot, func); - else - (void) snprintf(name, namelen, "%x", slot); - - ddi_prop_free(pci_rp); - return (DDI_SUCCESS); -} - -static int -pxb_initchild(dev_info_t *child) -{ - char name[MAXNAMELEN]; - pxb_devstate_t *pxb; - int result = DDI_FAILURE; - intptr_t ppd = NULL; -#ifdef PX_PLX - int i; - uint16_t reg = 0; - ddi_acc_handle_t config_handle; -#endif /* PX_PLX */ - - /* - * Name the child - */ - if (pxb_name_child(child, name, MAXNAMELEN) != DDI_SUCCESS) { - result = DDI_FAILURE; - goto done; - } - - /* - * XXX set ppd to 1 to disable iommu BDF protection - * It relies on unused parent private data for PCI devices. - */ - if (ddi_prop_exists(DDI_DEV_T_NONE, child, DDI_PROP_DONTPASS, - "dvma-share")) - ppd = 1; - - ddi_set_name_addr(child, name); - ddi_set_parent_data(child, (void *)ppd); - - /* - * Pseudo nodes indicate a prototype node with per-instance - * properties to be merged into the real h/w device node. - * The interpretation of the unit-address is DD[,F] - * where DD is the device id and F is the function. - */ - if (ndi_dev_is_persistent_node(child) == 0) { - extern int pci_allow_pseudo_children; - - /* - * Try to merge the properties from this prototype - * node into real h/w nodes. - */ - if (ndi_merge_node(child, pxb_name_child) == DDI_SUCCESS) { - /* - * Merged ok - return failure to remove the node. - */ - ddi_set_name_addr(child, NULL); - ddi_remove_minor_node(child, NULL); - result = DDI_FAILURE; - goto done; - } - - /* workaround for ddivs to run under PCI */ - if (pci_allow_pseudo_children) { - result = DDI_SUCCESS; - goto done; - } - - /* - * The child was not merged into a h/w node, - * but there's not much we can do with it other - * than return failure to cause the node to be removed. - */ - cmn_err(CE_WARN, "!%s@%s: %s.conf properties not merged", - ddi_driver_name(child), ddi_get_name_addr(child), - ddi_driver_name(child)); - ddi_set_name_addr(child, NULL); - ddi_remove_minor_node(child, NULL); - result = DDI_NOT_WELL_FORMED; - goto done; - } - - ddi_set_parent_data(child, (void *)ppd); - - pxb = (pxb_devstate_t *)ddi_get_soft_state(pxb_state, - ddi_get_instance(ddi_get_parent(child))); - - if (pcie_pm_hold(pxb->pxb_dip) != DDI_SUCCESS) { - DBG(DBG_PWR, pxb->pxb_dip, - "INITCHILD: px_pm_hold failed\n"); - result = DDI_FAILURE; - goto done; - } - /* Any return from here must call pcie_pm_release */ - - /* - * If configuration registers were previously saved by - * child (before it entered D3), then let the child do the - * restore to set up the config regs as it'll first need to - * power the device out of D3. - */ - if (ddi_prop_exists(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, - "config-regs-saved-by-child") == 1) { - DBG(DBG_PWR, ddi_get_parent(child), - "INITCHILD: config regs to be restored by child" - " for %s@%s\n", ddi_node_name(child), - ddi_get_name_addr(child)); - - result = DDI_SUCCESS; - goto cleanup; - } - - DBG(DBG_PWR, ddi_get_parent(child), - "INITCHILD: config regs setup for %s@%s\n", - ddi_node_name(child), ddi_get_name_addr(child)); - - if (!pcie_init_bus(child) || pcie_initchild(child) != DDI_SUCCESS) { - result = DDI_FAILURE; - goto cleanup; - } - -#ifdef PX_PLX - /* - * Due to a PLX HW bug, a SW workaround to prevent the chip from - * wedging is needed. SW just needs to tranfer 64 TLPs from - * the downstream port to the child device. - * The most benign way of doing this is to read the ID register - * 64 times. This SW workaround should have minimum performance - * impact and shouldn't cause a problem for all other bridges - * and switches. - * - * The code needs to be written in a way to make sure it isn't - * optimized out. - */ - if (!pxb_tlp_count) { - result = DDI_SUCCESS; - goto cleanup; - } - - if (pci_config_setup(child, &config_handle) != DDI_SUCCESS) { - result = DDI_FAILURE; - goto cleanup; - } - - for (i = 0; i < pxb_tlp_count; i += 1) - reg |= pci_config_get16(config_handle, PCI_CONF_VENID); - - if (PCIE_IS_PCIE_BDG(PCIE_DIP2BUS(pxb->pxb_dip))) - pxb_set_pci_perf_parameters(child, config_handle); - - pci_config_teardown(&config_handle); -#endif /* PX_PLX */ - - result = DDI_SUCCESS; -cleanup: - pcie_pm_release(pxb->pxb_dip); -done: - return (result); -} - -static int -pxb_intr_attach(pxb_devstate_t *pxb) -{ - int intr_types; - dev_info_t *devi; - - 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 ((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"); - return (DDI_FAILURE); - } - } - return (DDI_SUCCESS); -} - -/* - * This function initializes internally generated interrupts only. - * It does not affect any interrupts generated by downstream devices - * or the forwarding of them. - * - * Enable Device Specific Interrupts or Hotplug features here. - * Enabling features may change how many interrupts are requested - * by the device. If features are not enabled first, the - * device might not ask for any interrupts. - */ -static int -pxb_intr_init(pxb_devstate_t *pxb, int intr_type) -{ - dev_info_t *dip = pxb->pxb_dip; - int request, count, x; - int ret; - int intr_cap = 0; - - DBG(DBG_ATTACH, dip, - "Attaching %s handler\n", - (intr_type == DDI_INTR_TYPE_MSI) ? "MSI" : "INTx"); - - /* - * Get number of requested interrupts. If none requested or DDI_FAILURE - * just return DDI_SUCCESS. - * - * Several Bridges/Switches will not have this property set, resulting - * in a FAILURE, if the device is not configured in a way that - * interrupts are needed. (eg. hotplugging) - */ - ret = ddi_intr_get_nintrs(dip, intr_type, &request); - if (ret != DDI_SUCCESS || request == 0) { - DBG(DBG_ATTACH, dip, - "ddi_intr_get_nintrs() ret: %d req %d\n", ret, request); - - return (DDI_SUCCESS); - } - - /* Find out how many MSI's are available. */ - if (intr_type == DDI_INTR_TYPE_MSI) { - ret = ddi_intr_get_navail(dip, intr_type, &count); - if ((ret != DDI_SUCCESS) || (count == 0)) { - DBG(DBG_ATTACH, dip, - "ddi_intr_get_navail() ret: %d available: %d\n", - ret, count); - - goto fail; - } - - if (request < count) { - DBG(DBG_ATTACH, dip, - "Requested Intr: %d Available: %d\n", - request, count); - - request = count; - } - } - - /* Allocate an array of interrupt handlers */ - pxb->pxb_htable_size = sizeof (ddi_intr_handle_t) * request; - pxb->pxb_htable = kmem_zalloc(pxb->pxb_htable_size, KM_SLEEP); - pxb->pxb_init_flags |= PXB_INIT_HTABLE; - - ret = ddi_intr_alloc(dip, pxb->pxb_htable, intr_type, - 0, request, &count, DDI_INTR_ALLOC_NORMAL); - if ((ret != DDI_SUCCESS) || (count == 0)) { - DBG(DBG_ATTACH, dip, - "ddi_intr_alloc() ret: %d ask: %d actual: %d\n", - ret, request, count); - - goto fail; - } - - /* Save the actually number of interrupts allocated */ - pxb->pxb_intr_count = count; - if (count < request) { - DBG(DBG_ATTACH, dip, - "Requested Intr: %d Received: %d\n", - request, count); - } - pxb->pxb_init_flags |= PXB_INIT_ALLOC; - - - /* Get interrupt priority */ - ret = ddi_intr_get_pri(pxb->pxb_htable[0], &pxb->pxb_intr_priority); - if (ret != DDI_SUCCESS) { - DBG(DBG_ATTACH, dip, "ddi_intr_get_pri() ret: %d\n", ret); - - goto fail; - } - - if (pxb->pxb_intr_priority >= LOCK_LEVEL) { - pxb->pxb_intr_priority = LOCK_LEVEL - 1; - ret = ddi_intr_set_pri(pxb->pxb_htable[0], - pxb->pxb_intr_priority); - if (ret != DDI_SUCCESS) { - DBG(DBG_ATTACH, dip, "ddi_intr_set_pri() ret: %d\n", - ret); - - goto fail; - } - } - - for (count = 0; count < pxb->pxb_intr_count; count++) { - ret = ddi_intr_add_handler(pxb->pxb_htable[count], - pxb_intr, (caddr_t)pxb, NULL); - - if (ret != DDI_SUCCESS) { - DBG(DBG_ATTACH, dip, - "ddi_intr_add_handler() ret: %d\n", - ret); - - break; - } - } - - /* If unsucessful remove the added handlers */ - if (ret != DDI_SUCCESS) { - for (x = 0; x < count; x++) { - (void) ddi_intr_remove_handler(pxb->pxb_htable[x]); - } - goto fail; - } - - pxb->pxb_init_flags |= PXB_INIT_HANDLER; - - (void) ddi_intr_get_cap(pxb->pxb_htable[0], &intr_cap); - - if (intr_cap & DDI_INTR_FLAG_BLOCK) { - (void) ddi_intr_block_enable(pxb->pxb_htable, - pxb->pxb_intr_count); - pxb->pxb_init_flags |= PXB_INIT_BLOCK; - } else { - for (count = 0; count < pxb->pxb_intr_count; count++) { - (void) ddi_intr_enable(pxb->pxb_htable[count]); - } - } - pxb->pxb_init_flags |= PXB_INIT_ENABLE; - - /* Save the interrupt type */ - pxb->pxb_intr_type = intr_type; - - return (DDI_SUCCESS); - -fail: - pxb_intr_fini(pxb); - - return (DDI_FAILURE); -} - -static void -pxb_intr_fini(pxb_devstate_t *pxb) -{ - int x; - int count = pxb->pxb_intr_count; - int flags = pxb->pxb_init_flags; - - if ((flags & PXB_INIT_ENABLE) && (flags & PXB_INIT_BLOCK)) { - (void) ddi_intr_block_disable(pxb->pxb_htable, count); - flags &= ~(PXB_INIT_ENABLE | PXB_INIT_BLOCK); - } - - for (x = 0; x < count; x++) { - if (flags & PXB_INIT_ENABLE) - (void) ddi_intr_disable(pxb->pxb_htable[x]); - - if (flags & PXB_INIT_HANDLER) - (void) ddi_intr_remove_handler(pxb->pxb_htable[x]); - - if (flags & PXB_INIT_ALLOC) - (void) ddi_intr_free(pxb->pxb_htable[x]); - } - - flags &= ~(PXB_INIT_ENABLE | PXB_INIT_HANDLER | PXB_INIT_ALLOC); - - if (flags & PXB_INIT_HTABLE) - kmem_free(pxb->pxb_htable, pxb->pxb_htable_size); - - flags &= ~PXB_INIT_HTABLE; - - pxb->pxb_init_flags &= flags; -} - -/* - * This only handles internal errors, not bus errors. - * Currently the only known interrupt would be from hotplugging. - */ -/*ARGSUSED*/ -static uint_t -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; - - 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 (rval); -} - -static void -pxb_removechild(dev_info_t *dip) -{ - ddi_set_name_addr(dip, NULL); - - /* - * Strip the node to properly convert it back to prototype form - */ - ddi_remove_minor_node(dip, NULL); - - /* - * XXX Clear parent private data used as a flag to disable - * iommu BDF protection - */ - if ((intptr_t)ddi_get_parent_data(dip) == 1) - ddi_set_parent_data(dip, NULL); - - impl_rem_dev_props(dip); - - pcie_uninitchild(dip); -} - -/* - * Initialize hotplug framework if we are hotpluggable. - * Sets flag in the soft state if Hot Plug is supported and initialized - * properly. - */ -/*ARGSUSED*/ -static int -pxb_init_hotplug(pxb_devstate_t *pxb) -{ - int rv = DDI_FAILURE; - uint8_t dev_type = PCIE_DIP2BUS(pxb->pxb_dip)->bus_dev_type; - - if (((dev_type == PCIE_PCIECAP_DEV_TYPE_DOWN) || - (dev_type == PCIE_PCIECAP_DEV_TYPE_PCI2PCIE) || - (dev_type == PCIE_PCIECAP_DEV_TYPE_ROOT)) && - (pxb_pciehpc_probe(pxb->pxb_dip, - pxb->pxb_config_handle) == DDI_SUCCESS)) { - pxb->pxb_hpc_type = HPC_PCIE; - } else if ((dev_type == PCIE_PCIECAP_DEV_TYPE_PCIE2PCI) && - (pxb_pcishpc_probe(pxb->pxb_dip, - pxb->pxb_config_handle) == DDI_SUCCESS)) { - pxb->pxb_hpc_type = HPC_SHPC; - } else { - pxb->pxb_hpc_type = HPC_NONE; - return (DDI_SUCCESS); - } - - pxb->pxb_hotplug_capable = B_TRUE; - if (pxb_intr_attach(pxb) != DDI_SUCCESS) - goto fail; - - if (pxb->pxb_hpc_type == HPC_PCIE) - rv = pciehpc_init(pxb->pxb_dip, NULL); - else if (pxb->pxb_hpc_type == HPC_SHPC) - rv = pcishpc_init(pxb->pxb_dip); - - if (rv != DDI_SUCCESS) - goto fail; - - 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); - - goto fail; - } - - (void) ndi_prop_create_boolean(DDI_DEV_T_NONE, pxb->pxb_dip, - "hotplug-capable"); - - return (DDI_SUCCESS); - -fail: - pxb->pxb_hpc_type = HPC_NONE; - pxb->pxb_hotplug_capable = B_FALSE; - cmn_err(CE_WARN, "%s%d: Failed setting hotplug framework", - ddi_driver_name(pxb->pxb_dip), ddi_get_instance(pxb->pxb_dip)); - - return (DDI_FAILURE); -} - -static void -pxb_create_ranges_prop(dev_info_t *dip, - ddi_acc_handle_t config_handle) -{ - uint32_t base, limit; - pxb_ranges_t ranges[PXB_RANGE_LEN]; - uint8_t io_base_lo, io_limit_lo; - uint16_t io_base_hi, io_limit_hi, mem_base, mem_limit; - int i = 0, rangelen = sizeof (pxb_ranges_t)/sizeof (int); - - io_base_lo = pci_config_get8(config_handle, PCI_BCNF_IO_BASE_LOW); - io_limit_lo = pci_config_get8(config_handle, PCI_BCNF_IO_LIMIT_LOW); - io_base_hi = pci_config_get16(config_handle, PCI_BCNF_IO_BASE_HI); - io_limit_hi = pci_config_get16(config_handle, PCI_BCNF_IO_LIMIT_HI); - mem_base = pci_config_get16(config_handle, PCI_BCNF_MEM_BASE); - mem_limit = pci_config_get16(config_handle, PCI_BCNF_MEM_LIMIT); - - /* - * Create ranges for IO space - */ - ranges[i].size_low = ranges[i].size_high = 0; - ranges[i].parent_mid = ranges[i].child_mid = ranges[i].parent_high = 0; - ranges[i].child_high = ranges[i].parent_high |= - (PCI_REG_REL_M | PCI_ADDR_IO); - base = PXB_16bit_IOADDR(io_base_lo); - limit = PXB_16bit_IOADDR(io_limit_lo); - - if ((io_base_lo & 0xf) == PXB_32BIT_IO) { - base = PXB_LADDR(base, io_base_hi); - } - if ((io_limit_lo & 0xf) == PXB_32BIT_IO) { - limit = PXB_LADDR(limit, io_limit_hi); - } - - if ((io_base_lo & PXB_32BIT_IO) && (io_limit_hi > 0)) { - base = PXB_LADDR(base, io_base_hi); - limit = PXB_LADDR(limit, io_limit_hi); - } - - /* - * Create ranges for 32bit memory space - */ - base = PXB_32bit_MEMADDR(mem_base); - limit = PXB_32bit_MEMADDR(mem_limit); - ranges[i].size_low = ranges[i].size_high = 0; - ranges[i].parent_mid = ranges[i].child_mid = ranges[i].parent_high = 0; - ranges[i].child_high = ranges[i].parent_high |= - (PCI_REG_REL_M | PCI_ADDR_MEM32); - ranges[i].child_low = ranges[i].parent_low = base; - if (limit >= base) { - ranges[i].size_low = limit - base + PXB_MEMGRAIN; - i++; - } - - if (i) { - (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, dip, "ranges", - (int *)ranges, i * rangelen); - } -} - -/*ARGSUSED*/ -static int -pxb_open(dev_t *devp, int flags, int otyp, cred_t *credp) -{ - pxb_devstate_t *pxb_p; - minor_t minor = getminor(*devp); - int instance = PCIHP_AP_MINOR_NUM_TO_INSTANCE(minor); - - /* - * Make sure the open is for the right file type. - */ - if (otyp != OTYP_CHR) - return (EINVAL); - - /* - * Get the soft state structure for the device. - */ - pxb_p = (pxb_devstate_t *)ddi_get_soft_state(pxb_state, - instance); - - if (pxb_p == NULL) - return (ENXIO); - - if (pxb_p->pxb_hotplug_capable == B_TRUE) - return ((pcihp_get_cb_ops())->cb_open(devp, flags, - otyp, credp)); - - /* - * Handle the open by tracking the device state. - */ - mutex_enter(&pxb_p->pxb_mutex); - if (flags & FEXCL) { - if (pxb_p->pxb_soft_state != PXB_SOFT_STATE_CLOSED) { - mutex_exit(&pxb_p->pxb_mutex); - return (EBUSY); - } - pxb_p->pxb_soft_state = PXB_SOFT_STATE_OPEN_EXCL; - } else { - if (pxb_p->pxb_soft_state == PXB_SOFT_STATE_OPEN_EXCL) { - mutex_exit(&pxb_p->pxb_mutex); - return (EBUSY); - } - pxb_p->pxb_soft_state = PXB_SOFT_STATE_OPEN; - } - mutex_exit(&pxb_p->pxb_mutex); - return (0); -} - - -/*ARGSUSED*/ -static int -pxb_close(dev_t dev, int flags, int otyp, cred_t *credp) -{ - pxb_devstate_t *pxb_p; - minor_t minor = getminor(dev); - int instance = PCIHP_AP_MINOR_NUM_TO_INSTANCE(minor); - - if (otyp != OTYP_CHR) - return (EINVAL); - - pxb_p = (pxb_devstate_t *)ddi_get_soft_state(pxb_state, - instance); - - if (pxb_p == NULL) - return (ENXIO); - - if (pxb_p->pxb_hotplug_capable == B_TRUE) - return ((pcihp_get_cb_ops())->cb_close(dev, flags, - otyp, credp)); - - mutex_enter(&pxb_p->pxb_mutex); - pxb_p->pxb_soft_state = PXB_SOFT_STATE_CLOSED; - mutex_exit(&pxb_p->pxb_mutex); - return (0); -} - - -/* - * pxb_ioctl: devctl hotplug controls - */ -/*ARGSUSED*/ -static int -pxb_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, - int *rvalp) -{ - pxb_devstate_t *pxb_p; - dev_info_t *self; - struct devctl_iocdata *dcp; - uint_t bus_state; - int rv = 0; - minor_t minor = getminor(dev); - int instance = PCIHP_AP_MINOR_NUM_TO_INSTANCE(minor); - - pxb_p = (pxb_devstate_t *)ddi_get_soft_state(pxb_state, - instance); - - if (pxb_p == NULL) - return (ENXIO); - - if (pxb_p->pxb_hotplug_capable == B_TRUE) - return ((pcihp_get_cb_ops())->cb_ioctl(dev, cmd, - arg, mode, credp, rvalp)); - - self = pxb_p->pxb_dip; - - /* - * We can use the generic implementation for these ioctls - */ - switch (cmd) { - case DEVCTL_DEVICE_GETSTATE: - case DEVCTL_DEVICE_ONLINE: - case DEVCTL_DEVICE_OFFLINE: - case DEVCTL_BUS_GETSTATE: - return (ndi_devctl_ioctl(self, cmd, arg, mode, 0)); - } - - /* - * read devctl ioctl data - */ - if (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS) - return (EFAULT); - - switch (cmd) { - - case DEVCTL_DEVICE_RESET: - rv = ENOTSUP; - break; - - case DEVCTL_BUS_QUIESCE: - if (ndi_get_bus_state(self, &bus_state) == NDI_SUCCESS) - if (bus_state == BUS_QUIESCED) - break; - (void) ndi_set_bus_state(self, BUS_QUIESCED); - break; - - case DEVCTL_BUS_UNQUIESCE: - if (ndi_get_bus_state(self, &bus_state) == NDI_SUCCESS) - if (bus_state == BUS_ACTIVE) - break; - (void) ndi_set_bus_state(self, BUS_ACTIVE); - break; - - case DEVCTL_BUS_RESET: - rv = ENOTSUP; - break; - - case DEVCTL_BUS_RESETALL: - rv = ENOTSUP; - break; - - default: - rv = ENOTTY; - } - - ndi_dc_freehdl(dcp); - return (rv); -} - -static int pxb_prop_op(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op, - int flags, char *name, caddr_t valuep, int *lengthp) -{ - pxb_devstate_t *pxb_p; - minor_t minor = getminor(dev); - int instance = PCIHP_AP_MINOR_NUM_TO_INSTANCE(minor); - - pxb_p = (pxb_devstate_t *)ddi_get_soft_state(pxb_state, - instance); - - if (pxb_p == NULL) - return (ENXIO); - - if (pxb_p->pxb_hotplug_capable == B_TRUE) - return ((pcihp_get_cb_ops())->cb_prop_op(dev, dip, prop_op, - flags, name, valuep, lengthp)); - - return (ddi_prop_op(dev, dip, prop_op, flags, name, valuep, lengthp)); -} - -#ifdef PX_PLX -/* - * Disable PM for PLX 8532 switch. Transitioning one port on - * this switch to low power causes links on other ports on the - * same station to die. - * Due to PLX erratum #34, we can't allow the downstream device - * go to non-D0 state. - */ -static int -plx_pwr_disable(dev_info_t *dip) -{ - pcie_pwr_t *pwr_p; - - ASSERT(PCIE_PMINFO(dip)); - pwr_p = PCIE_NEXUS_PMINFO(dip); - ASSERT(pwr_p); - DBG(DBG_PWR, dip, "plx_pwr_disable: PLX8532/PLX8516 found " - "disabling PM\n"); - pwr_p->pwr_func_lvl = PM_LEVEL_D0; - pwr_p->pwr_flags = PCIE_NO_CHILD_PM; - return (DDI_SUCCESS); -} -#endif /* PX_PLX */ - -/* - * Power management related initialization specific to px_pci. - * Called by pxb_attach() - */ -static int -pxb_pwr_setup(dev_info_t *dip) -{ - char *comp_array[5]; - int i; - ddi_acc_handle_t conf_hdl; - uint16_t pmcap, cap_ptr; - pcie_pwr_t *pwr_p; - - ASSERT(PCIE_PMINFO(dip)); - pwr_p = PCIE_NEXUS_PMINFO(dip); - ASSERT(pwr_p); - - /* Code taken from pci_pci driver */ - if (pci_config_setup(dip, &pwr_p->pwr_conf_hdl) != DDI_SUCCESS) { - DBG(DBG_PWR, dip, "pxb_pwr_setup: pci_config_setup failed\n"); - return (DDI_FAILURE); - } - conf_hdl = pwr_p->pwr_conf_hdl; - - /* - * Walk the capabilities searching for a PM entry. - */ - if ((PCI_CAP_LOCATE(conf_hdl, PCI_CAP_ID_PM, &cap_ptr)) == - DDI_FAILURE) { - DBG(DBG_PWR, dip, "switch/bridge does not support PM. PCI" - " PM data structure not found in config header\n"); - pci_config_teardown(&conf_hdl); - return (DDI_SUCCESS); - } - /* - * Save offset to pmcsr for future references. - */ - pwr_p->pwr_pmcsr_offset = cap_ptr + PCI_PMCSR; - pmcap = PCI_CAP_GET16(conf_hdl, NULL, cap_ptr, PCI_PMCAP); - if (pmcap & PCI_PMCAP_D1) { - DBG(DBG_PWR, dip, "D1 state supported\n"); - pwr_p->pwr_pmcaps |= PCIE_SUPPORTS_D1; - } - if (pmcap & PCI_PMCAP_D2) { - DBG(DBG_PWR, dip, "D2 state supported\n"); - pwr_p->pwr_pmcaps |= PCIE_SUPPORTS_D2; - } - - i = 0; - comp_array[i++] = "NAME=PCIe switch/bridge PM"; - comp_array[i++] = "0=Power Off (D3)"; - if (pwr_p->pwr_pmcaps & PCIE_SUPPORTS_D2) - comp_array[i++] = "1=D2"; - if (pwr_p->pwr_pmcaps & PCIE_SUPPORTS_D1) - comp_array[i++] = "2=D1"; - comp_array[i++] = "3=Full Power D0"; - - /* - * Create pm-components property, if it does not exist already. - */ - if (ddi_prop_update_string_array(DDI_DEV_T_NONE, dip, - "pm-components", comp_array, i) != DDI_PROP_SUCCESS) { - DBG(DBG_PWR, dip, "could not create pm-components prop\n"); - pci_config_teardown(&conf_hdl); - return (DDI_FAILURE); - } - return (pxb_pwr_init_and_raise(dip, pwr_p)); -} - -/* - * Initializes the power level and raise the power to D0, if it is - * not at D0. - */ -static int -pxb_pwr_init_and_raise(dev_info_t *dip, pcie_pwr_t *pwr_p) -{ - uint16_t pmcsr; - int ret = DDI_SUCCESS; - - /* - * Intialize our power level from PMCSR. The common code initializes - * this to UNKNOWN. There is no guarantee that we will be at full - * power at attach. If we are not at D0, raise the power. - */ - pmcsr = pci_config_get16(pwr_p->pwr_conf_hdl, pwr_p->pwr_pmcsr_offset); - pmcsr &= PCI_PMCSR_STATE_MASK; - switch (pmcsr) { - case PCI_PMCSR_D0: - pwr_p->pwr_func_lvl = PM_LEVEL_D0; - break; - - case PCI_PMCSR_D1: - pwr_p->pwr_func_lvl = PM_LEVEL_D1; - break; - - case PCI_PMCSR_D2: - pwr_p->pwr_func_lvl = PM_LEVEL_D2; - break; - - case PCI_PMCSR_D3HOT: - pwr_p->pwr_func_lvl = PM_LEVEL_D3; - break; - - default: - break; - } - - /* Raise the power to D0. */ - if (pwr_p->pwr_func_lvl != PM_LEVEL_D0 && - ((ret = pm_raise_power(dip, 0, PM_LEVEL_D0)) != DDI_SUCCESS)) { - /* - * Read PMCSR again. If it is at D0, ignore the return - * value from pm_raise_power. - */ - pmcsr = pci_config_get16(pwr_p->pwr_conf_hdl, - pwr_p->pwr_pmcsr_offset); - if ((pmcsr & PCI_PMCSR_STATE_MASK) == PCI_PMCSR_D0) - ret = DDI_SUCCESS; - else { - DBG(DBG_PWR, dip, "pxb_pwr_setup: could not raise " - "power to D0 \n"); - } - } - if (ret == DDI_SUCCESS) - pwr_p->pwr_func_lvl = PM_LEVEL_D0; - return (ret); -} - -static int -pxb_fm_init(pxb_devstate_t *pxb_p) -{ - dev_info_t *dip = pxb_p->pxb_dip; - int fm_cap = DDI_FM_EREPORT_CAPABLE | - DDI_FM_ACCCHK_CAPABLE | DDI_FM_DMACHK_CAPABLE; - - /* - * Request our capability level and get our parents capability - * and ibc. - */ - ddi_fm_init(dip, &fm_cap, &pxb_p->pxb_fm_ibc); - - pci_ereport_setup(dip); - - return (DDI_SUCCESS); -} - -/* - * Breakdown our FMA resources - */ -static void -pxb_fm_fini(pxb_devstate_t *pxb_p) -{ - dev_info_t *dip = pxb_p->pxb_dip; - /* - * Clean up allocated fm structures - */ - ddi_fm_fini(dip); -} - -/* - * Function used to initialize FMA for our children nodes. Called - * through pci busops when child node calls ddi_fm_init. - */ -/*ARGSUSED*/ -int -pxb_fm_init_child(dev_info_t *dip, dev_info_t *cdip, int cap, - ddi_iblock_cookie_t *ibc_p) -{ - pxb_devstate_t *pxb_p = (pxb_devstate_t *) - ddi_get_soft_state(pxb_state, ddi_get_instance(dip)); - *ibc_p = pxb_p->pxb_fm_ibc; - return (DEVI(dip)->devi_fmhdl->fh_cap | DDI_FM_DMACHK_CAPABLE); -} - -/* - * undo whatever is done in pxb_pwr_setup. called by pxb_detach() - */ -static void -pxb_pwr_teardown(dev_info_t *dip) -{ - pcie_pwr_t *pwr_p; - - if (!PCIE_PMINFO(dip) || !(pwr_p = PCIE_NEXUS_PMINFO(dip))) - return; - - (void) ddi_prop_remove(DDI_DEV_T_NONE, dip, "pm-components"); - 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 cap_ptr; - - if ((PCI_CAP_LOCATE(config_handle, PCI_CAP_ID_PCI_E, &cap_ptr)) != - DDI_FAILURE) { - uint16_t slotimpl = PCI_CAP_GET16(config_handle, NULL, cap_ptr, - PCIE_PCIECAP) & PCIE_PCIECAP_SLOT_IMPL; - if (slotimpl) - if (PCI_CAP_GET32(config_handle, NULL, cap_ptr, - PCIE_SLOTCAP) & PCIE_SLOTCAP_HP_CAPABLE) - return (DDI_SUCCESS); - } - - return (DDI_FAILURE); - -} - -/*ARGSUSED*/ -static int pxb_pcishpc_probe(dev_info_t *dip, ddi_acc_handle_t config_handle) -{ - uint16_t cap_ptr; - - if ((PCI_CAP_LOCATE(config_handle, PCI_CAP_ID_PCI_HOTPLUG, &cap_ptr)) != - DDI_FAILURE) { - return (DDI_SUCCESS); - } - - return (DDI_FAILURE); - -} - -/* - * For PCI and PCI-X devices including PCIe2PCI bridge, initialize - * cache-line-size and latency timer configuration registers. - */ -static void -pxb_set_pci_perf_parameters(dev_info_t *dip, ddi_acc_handle_t cfg_hdl) -{ - uint_t n; - - /* Initialize cache-line-size configuration register if needed */ - if (ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, - "cache-line-size", 0) == 0) { - pci_config_put8(cfg_hdl, PCI_CONF_CACHE_LINESZ, - PXB_CACHE_LINE_SIZE); - n = pci_config_get8(cfg_hdl, PCI_CONF_CACHE_LINESZ); - if (n != 0) { - (void) ndi_prop_update_int(DDI_DEV_T_NONE, dip, - "cache-line-size", n); - } - } - - /* Initialize latency timer configuration registers if needed */ - if (ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, - "latency-timer", 0) == 0) { - uchar_t min_gnt, latency_timer; - uchar_t header_type; - - /* Determine the configuration header type */ - header_type = pci_config_get8(cfg_hdl, PCI_CONF_HEADER); - - if ((header_type & PCI_HEADER_TYPE_M) == PCI_HEADER_ONE) { - latency_timer = PXB_LATENCY_TIMER; - pci_config_put8(cfg_hdl, PCI_BCNF_LATENCY_TIMER, - latency_timer); - } else { - min_gnt = pci_config_get8(cfg_hdl, PCI_CONF_MIN_G); - latency_timer = min_gnt * 8; - } - - pci_config_put8(cfg_hdl, PCI_CONF_LATENCY_TIMER, - latency_timer); - n = pci_config_get8(cfg_hdl, PCI_CONF_LATENCY_TIMER); - if (n != 0) { - (void) ndi_prop_update_int(DDI_DEV_T_NONE, dip, - "latency-timer", n); - } - } -} - -#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 - -static void -pxb_id_props(pxb_devstate_t *pxb) -{ - uint64_t serialid = 0; /* 40b field of EUI-64 serial no. register */ - uint16_t cap_ptr; - uint8_t fic = 0; /* 1 = first in chassis device */ - - /* - * Identify first in chassis. In the special case of a Sun branded - * PLX device, it obviously is first in chassis. Otherwise, in the - * general case, look for an Expansion Slot Register and check its - * first-in-chassis bit. - */ -#ifdef PX_PLX - if ((pxb->pxb_vendor_id == PXB_VENDOR_SUN) && - ((pxb->pxb_device_id == PXB_DEVICE_PLX_PCIX) || - (pxb->pxb_device_id == PXB_DEVICE_PLX_PCIE))) { - fic = 1; - } -#endif /* PX_PLX */ - if ((fic == 0) && ((PCI_CAP_LOCATE(pxb->pxb_config_handle, - PCI_CAP_ID_SLOT_ID, &cap_ptr)) != DDI_FAILURE)) { - uint8_t esr = PCI_CAP_GET8(pxb->pxb_config_handle, NULL, - cap_ptr, PCI_CAP_ID_REGS_OFF); - if (PCI_CAPSLOT_FIC(esr)) - fic = 1; - } - - if ((PCI_CAP_LOCATE(pxb->pxb_config_handle, - PCI_CAP_XCFG_SPC(PCIE_EXT_CAP_ID_SER), &cap_ptr)) != DDI_FAILURE) { - /* Serialid can be 0 thru a full 40b number */ - serialid = PCI_XCAP_GET32(pxb->pxb_config_handle, NULL, - cap_ptr, PCIE_SER_SID_UPPER_DW); - serialid <<= 32; - serialid |= PCI_XCAP_GET32(pxb->pxb_config_handle, NULL, - cap_ptr, PCIE_SER_SID_LOWER_DW); - } - - if (fic) - (void) ndi_prop_create_boolean(DDI_DEV_T_NONE, pxb->pxb_dip, - "first-in-chassis"); - if (serialid) - (void) ddi_prop_update_int64(DDI_DEV_T_NONE, pxb->pxb_dip, - "serialid#", serialid); -} - -/* - * Some PCI-X to PCI-E bridges do not support full 64-bit addressing on the - * PCI-X side of the bridge. We build a special version of this driver for - * those bridges, which uses PXB_ADDR_LIMIT_LO and/or PXB_ADDR_LIMIT_HI - * to define the range of values which the chip can handle. The code below - * then clamps the DMA address range supplied by the driver, preventing the - * PCI-E nexus driver from allocating any memory the bridge can't deal - * with. - */ -static int -pxb_dma_allochdl(dev_info_t *dip, dev_info_t *rdip, - ddi_dma_attr_t *attr_p, int (*waitfp)(caddr_t), caddr_t arg, - ddi_dma_handle_t *handlep) -{ - int ret; -#ifdef BCM_SW_WORKAROUNDS - uint64_t lim; - - /* - * If the leaf device's limits are outside than what the Broadcom - * bridge can handle, we need to clip the values passed up the chain. - */ - lim = attr_p->dma_attr_addr_lo; - attr_p->dma_attr_addr_lo = MAX(lim, PXB_ADDR_LIMIT_LO); - - lim = attr_p->dma_attr_addr_hi; - attr_p->dma_attr_addr_hi = MIN(lim, PXB_ADDR_LIMIT_HI); - -#endif /* BCM_SW_WORKAROUNDS */ - - /* - * This is a software workaround to fix the Broadcom 5714/5715 PCIe-PCI - * bridge prefetch bug. Intercept the DMA alloc handle request and set - * PX_DMAI_FLAGS_MAP_BUFZONE flag in the handle. If this flag is set, - * the px nexus driver will allocate an extra page & make it valid one, - * for any DVMA request that comes from any of the Broadcom bridge child - * devices. - */ - if ((ret = ddi_dma_allochdl(dip, rdip, attr_p, waitfp, arg, - handlep)) == DDI_SUCCESS) { - ddi_dma_impl_t *mp = (ddi_dma_impl_t *)*handlep; -#ifdef BCM_SW_WORKAROUNDS - mp->dmai_inuse |= PX_DMAI_FLAGS_MAP_BUFZONE; -#endif /* BCM_SW_WORKAROUNDS */ - /* - * For a given rdip, update mp->dmai_bdf with the bdf value - * of px_pci's immediate child or secondary bus-id of the - * PCIe2PCI bridge. - */ - mp->dmai_minxfer = pcie_get_bdf_for_dma_xfer(dip, rdip); - } - - return (ret); -} - -/* - * FDVMA feature is not supported for any child device of Broadcom 5714/5715 - * PCIe-PCI bridge due to prefetch bug. Return failure immediately, so that - * these drivers will switch to regular DVMA path. - */ -/*ARGSUSED*/ -static int -pxb_dma_mctl(dev_info_t *dip, dev_info_t *rdip, ddi_dma_handle_t handle, - enum ddi_dma_ctlops cmd, off_t *offp, size_t *lenp, caddr_t *objp, - uint_t cache_flags) -{ - int ret; - -#ifdef BCM_SW_WORKAROUNDS - if (cmd == DDI_DMA_RESERVE) - return (DDI_FAILURE); -#endif /* BCM_SW_WORKAROUNDS */ - - if (((ret = ddi_dma_mctl(dip, rdip, handle, cmd, offp, lenp, objp, - cache_flags)) == DDI_SUCCESS) && (cmd == DDI_DMA_RESERVE)) { - ddi_dma_impl_t *mp = (ddi_dma_impl_t *)*objp; - - /* - * For a given rdip, update mp->dmai_bdf with the bdf value - * of px_pci's immediate child or secondary bus-id of the - * PCIe2PCI bridge. - */ - mp->dmai_minxfer = pcie_get_bdf_for_dma_xfer(dip, rdip); - } - - return (ret); -} - -#ifdef DEBUG -static void -pxb_dbg(uint_t bit, dev_info_t *dip, char *fmt, ...) -{ - va_list ap; - - if (!(bit & pxb_dbg_print)) - return; - - if (dip) - prom_printf("%s(%d): %s", ddi_driver_name(dip), - ddi_get_instance(dip), pxb_debug_sym[bit]); -body: - va_start(ap, fmt); - if (ap) - prom_vprintf(fmt, ap); - else - prom_printf(fmt); - - va_end(ap); -} -#endif - - -#ifdef PX_PLX -/* - * Disable PLX specific relaxed ordering mode. Due to PLX - * erratum #6, use of this mode with Cut-Through Cancellation - * can result in dropped Completion type packets. - * - * Clear the Relaxed Ordering Mode on 8533 and 8548 switches. - * To disable RO, clear bit 5 in offset 0x664, an undocumented - * bit in the PLX spec, on Ports 0, 8 and 12. Proprietary PLX - * registers are normally accessible only via memspace from Port - * 0. If port 0 is attached go ahead and disable RO on Port 0, - * 8 and 12, if they exist. - */ -static void -plx_ro_disable(pxb_devstate_t *pxb) -{ - pcie_bus_t *bus_p = PCIE_DIP2BUS(pxb->pxb_dip); - dev_info_t *dip = pxb->pxb_dip; - pci_regspec_t *reg_spec, *addr_spec; - int rlen, alen; - int orig_rsize, new_rsize; - uint_t rnum, anum; - ddi_device_acc_attr_t attr; - ddi_acc_handle_t hdl; - caddr_t regsp; - uint32_t val, port_enable; - char *offset; - char *port_offset; - - if (!((pxb->pxb_device_id == PXB_DEVICE_PLX_8533) || - (pxb->pxb_device_id == PXB_DEVICE_PLX_8548))) - return; - - /* You can also only do this on Port 0 */ - val = PCIE_CAP_GET(32, bus_p, PCIE_LINKCAP); - val = (val >> PCIE_LINKCAP_PORT_NUMBER_SHIFT) & - PCIE_LINKCAP_PORT_NUMBER_MASK; - - DBG(DBG_ATTACH, dip, "PLX RO Disable : bdf=0x%x port=%d\n", - bus_p->bus_bdf, val); - - if (val != 0) - return; - - /* - * Read the reg property, but allocate extra space incase we need to add - * a new entry later. - */ - if (ddi_getproplen(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "reg", - &orig_rsize) != DDI_SUCCESS) - return; - - new_rsize = orig_rsize + sizeof (pci_regspec_t); - reg_spec = kmem_alloc(new_rsize, KM_SLEEP); - - if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "reg", - (caddr_t)reg_spec, &orig_rsize) != DDI_SUCCESS) - goto fail; - - /* Find the mem32 reg property */ - rlen = orig_rsize / sizeof (pci_regspec_t); - for (rnum = 0; rnum < rlen; rnum++) { - if ((reg_spec[rnum].pci_phys_hi & PCI_ADDR_MASK) == - PCI_ADDR_MEM32) - goto fix; - } - - /* - * Mem32 reg property was not found. - * Look for it in assign-address property. - */ - addr_spec = bus_p->bus_assigned_addr; - alen = bus_p->bus_assigned_entries; - for (anum = 0; anum < alen; anum++) { - if ((addr_spec[anum].pci_phys_hi & PCI_ADDR_MASK) == - PCI_ADDR_MEM32) - goto update; - } - - /* Unable to find mem space assigned address, give up. */ - goto fail; - -update: - /* - * Add the mem32 access to the reg spec. - * Use the last entry which was previously allocated. - */ - reg_spec[rnum].pci_phys_hi = (addr_spec[anum].pci_phys_hi & - ~PCI_REG_REL_M); - reg_spec[rnum].pci_phys_mid = 0; - reg_spec[rnum].pci_phys_low = 0; - reg_spec[rnum].pci_size_hi = addr_spec[anum].pci_size_hi; - reg_spec[rnum].pci_size_low = addr_spec[anum].pci_size_low; - - /* Create the new reg_spec data and update the property */ - if (ddi_prop_update_int_array(DDI_DEV_T_NONE, dip, "reg", - (int *)reg_spec, (new_rsize / sizeof (int))) != DDI_SUCCESS) - goto fail; - -fix: - attr.devacc_attr_version = DDI_DEVICE_ATTR_V0; - attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC; - attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC; - - if (ddi_regs_map_setup(dip, rnum, ®sp, 0, 0, &attr, - &hdl) != DDI_SUCCESS) - goto fail; - - /* Grab register which shows which ports are enabled */ - offset = (char *)regsp + PLX_INGRESS_PORT_ENABLE; - port_enable = ddi_get32(hdl, (uint32_t *)offset); - - if ((port_enable == 0xFFFFFFFF) || (port_enable == 0)) - goto done; - - offset = (char *)regsp + PLX_INGRESS_CONTROL_SHADOW; - - /* Disable RO on Port 0 */ - port_offset = 0x0 + offset; - val = ddi_get32(hdl, (uint32_t *)port_offset); - if (val & PLX_RO_MODE_BIT) - val ^= PLX_RO_MODE_BIT; - ddi_put32(hdl, (uint32_t *)port_offset, val); - - /* Disable RO on Port 8, but make sure its enabled */ - if (!(port_enable & (1 << 8))) - goto port12; - - port_offset = (8 * 0x1000) + offset; - val = ddi_get32(hdl, (uint32_t *)port_offset); - if (val & PLX_RO_MODE_BIT) - val ^= PLX_RO_MODE_BIT; - ddi_put32(hdl, (uint32_t *)port_offset, val); - -port12: - /* Disable RO on Port 12, but make sure it exists */ - if (!(port_enable & (1 << 12))) - goto done; - - port_offset = (12 * 0x1000) + offset; - val = ddi_get32(hdl, (uint32_t *)port_offset); - if (val & PLX_RO_MODE_BIT) - val ^= PLX_RO_MODE_BIT; - ddi_put32(hdl, (uint32_t *)port_offset, val); - -done: - ddi_regs_map_free(&hdl); -fail: - kmem_free(reg_spec, new_rsize); -} -#endif /* PX_PLX */ diff --git a/usr/src/uts/sun4/io/px/px_pci.h b/usr/src/uts/sun4/io/px/px_pci.h deleted file mode 100644 index 1303de878b..0000000000 --- a/usr/src/uts/sun4/io/px/px_pci.h +++ /dev/null @@ -1,177 +0,0 @@ -/* - * CDDL HEADER START - * - * The contents of this file are subject to the terms of the - * Common Development and Distribution License (the "License"). - * You may not use this file except in compliance with the License. - * - * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE - * or http://www.opensolaris.org/os/licensing. - * See the License for the specific language governing permissions - * and limitations under the License. - * - * When distributing Covered Code, include this CDDL HEADER in each - * file and include the License file at usr/src/OPENSOLARIS.LICENSE. - * If applicable, add the following below this CDDL HEADER, with the - * fields enclosed by brackets "[]" replaced with your own identifying - * information: Portions Copyright [yyyy] [name of copyright owner] - * - * CDDL HEADER END - */ -/* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. - */ - -#ifndef _SYS_PX_PCI_H -#define _SYS_PX_PCI_H - -#pragma ident "%Z%%M% %I% %E% SMI" - -#ifdef __cplusplus -extern "C" { -#endif - -/* - * Intel specific register offsets with bit definitions. - */ -#define PXB_PX_CAPABILITY_ID 0x44 -#define PXB_BRIDGE_CONF 0x40 - -/* - * PCI/PCI-E Configuration register specific values. - */ -#define PX_PMODE 0x4000 /* PCI/PCIX Mode */ -#define PX_PFREQ_66 0x200 /* PCI clock frequency */ -#define PX_PFREQ_100 0x400 -#define PX_PFREQ_133 0x600 -#define PX_PMRE 0x80 /* Peer memory read enable */ - -/* - * Downstream delayed transaction resource partitioning. - */ -#define PX_ODTP 0x40 /* Max. of two entries PX and PCI */ - -/* - * Maximum upstream delayed transaction. - */ -#define PX_MDT_44 0x00 -#define PX_MDT_11 0x01 -#define PX_MDT_22 0x10 - - -#define NUM_LOGICAL_SLOTS 32 -#define PXB_RANGE_LEN 2 -#define PXB_32BIT_IO 1 -#define PXB_32bit_MEM 1 -#define PXB_MEMGRAIN 0x100000 -#define PXB_IOGRAIN 0x1000 - -#define PXB_16bit_IOADDR(addr) ((uint16_t)(((uint8_t)(addr) & 0xF0) << 8)) -#define PXB_LADDR(lo, hi) (((uint16_t)(hi) << 16) | (uint16_t)(lo)) -#define PXB_32bit_MEMADDR(addr) (PXB_LADDR(0, ((uint16_t)(addr) & 0xFFF0))) - -typedef struct slot_table { - uchar_t bus_id[128]; - uchar_t slot_name[32]; - uint8_t device_no; - uint8_t phys_slot_num; -} slot_table_t; - -/* - * The following typedef is used to represent an entry in the "ranges" - * property of a device node. - */ -typedef struct { - uint32_t child_high; - uint32_t child_mid; - uint32_t child_low; - uint32_t parent_high; - uint32_t parent_mid; - uint32_t parent_low; - uint32_t size_high; - uint32_t size_low; -} pxb_ranges_t; - -typedef enum { HPC_NONE, HPC_PCIE, HPC_SHPC, HPC_OUTBAND } pxb_hpc_type_t; - -typedef struct { - dev_info_t *pxb_dip; - - ddi_acc_handle_t pxb_config_handle; - - /* Interrupt */ - ddi_intr_handle_t *pxb_htable; /* Intr Handlers */ - int pxb_htable_size; /* htable size */ - int pxb_intr_count; /* Num of Intr */ - uint_t pxb_intr_priority; /* Intr Priority */ - int pxb_intr_type; /* (MSI | FIXED) */ - - /* - * HP support - */ - boolean_t pxb_hotplug_capable; - pxb_hpc_type_t pxb_hpc_type; - - kmutex_t pxb_mutex; - uint_t pxb_soft_state; - - /* Initialization flags */ - int pxb_init_flags; - - /* FMA */ - ddi_iblock_cookie_t pxb_fm_ibc; - - /* Vendor Device Id */ - uint16_t pxb_vendor_id; - uint16_t pxb_device_id; - uint8_t pxb_rev_id; -} pxb_devstate_t; - -/* - * soft state pointer and structure template: - */ -extern void *pxb_state; - -/* pxb soft states */ -#define PXB_SOFT_STATE_CLOSED 0x00 -#define PXB_SOFT_STATE_OPEN 0x01 -#define PXB_SOFT_STATE_OPEN_EXCL 0x02 - -/* pxb init flags */ -#define PXB_INIT_MUTEX 0x01 -#define PXB_INIT_CONFIG_HANDLE 0x02 -#define PXB_INIT_HTABLE 0x04 -#define PXB_INIT_ALLOC 0x08 -#define PXB_INIT_HANDLER 0x10 -#define PXB_INIT_ENABLE 0x20 -#define PXB_INIT_BLOCK 0x40 -#define PXB_INIT_FM 0x80 - -#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 BCM_SW_WORKAROUNDS - -/* Workaround for address space limitation in Broadcom 5714/5715 */ -#define PXB_ADDR_LIMIT_LO 0ull -#define PXB_ADDR_LIMIT_HI ((1ull << 40) - 1) - -#endif /* BCM_SW_WORKAROUNDS */ - -/* - * The following values are used to initialize the cache line size - * and latency timer registers for PCI, PCI-X and PCIe2PCI devices. - */ -#define PXB_CACHE_LINE_SIZE 0x10 /* 64 bytes in # of DWORDs */ -#define PXB_LATENCY_TIMER 0x40 /* 64 PCI cycles */ - -#ifdef __cplusplus -} -#endif - -#endif /* _SYS_PX_PCI_H */ diff --git a/usr/src/uts/sun4/io/px/px_util.c b/usr/src/uts/sun4/io/px/px_util.c index 0d4cf63bfe..7b951d2c5d 100644 --- a/usr/src/uts/sun4/io/px/px_util.c +++ b/usr/src/uts/sun4/io/px/px_util.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -39,7 +39,7 @@ #include <sys/sunndi.h> #include <sys/ddi_impldefs.h> #include "px_obj.h" -#include "pcie_pwr.h" +#include <sys/pcie_pwr.h> /*LINTLIBRARY*/ diff --git a/usr/src/uts/sun4u/io/px/px_err.c b/usr/src/uts/sun4u/io/px/px_err.c index b5d753a0f5..c1c8d49a0b 100644 --- a/usr/src/uts/sun4u/io/px/px_err.c +++ b/usr/src/uts/sun4u/io/px/px_err.c @@ -41,7 +41,6 @@ #include <sys/membar.h> #include <sys/machcpuvar.h> #include <sys/platform_module.h> -#include "pcie_pwr.h" #include "px_lib4u.h" #include "px_err.h" #include "px_err_impl.h" diff --git a/usr/src/uts/sun4u/io/px/px_hlib.c b/usr/src/uts/sun4u/io/px/px_hlib.c index 6a292fc299..a98657031c 100644 --- a/usr/src/uts/sun4u/io/px/px_hlib.c +++ b/usr/src/uts/sun4u/io/px/px_hlib.c @@ -31,7 +31,6 @@ #include <sys/iommutsb.h> #include <sys/pci.h> #include <sys/hotplug/pci/pciehpc.h> -#include <pcie_pwr.h> #include <px_obj.h> #include "px_regs.h" #include "oberon_regs.h" diff --git a/usr/src/uts/sun4u/io/px/px_lib4u.c b/usr/src/uts/sun4u/io/px/px_lib4u.c index f9b98235d4..a8c655d8e2 100644 --- a/usr/src/uts/sun4u/io/px/px_lib4u.c +++ b/usr/src/uts/sun4u/io/px/px_lib4u.c @@ -43,7 +43,7 @@ #include <sys/hotplug/pci/pciehpc.h> #include <sys/spl.h> #include <px_obj.h> -#include <pcie_pwr.h> +#include <sys/pcie_pwr.h> #include "px_tools_var.h" #include <px_regs.h> #include <px_csr.h> |