summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
authorEvan Yan <Evan.Yan@Sun.COM>2009-11-02 15:58:28 +0800
committerEvan Yan <Evan.Yan@Sun.COM>2009-11-02 15:58:28 +0800
commit269473047d747f7815af570197e4ef7322d3632c (patch)
treee93761fa235a8ba4c1b5c637a8f3a429be21a508 /usr/src
parentb24ab6762772a3f6a89393947930c7fa61306783 (diff)
downloadillumos-joyent-269473047d747f7815af570197e4ef7322d3632c.tar.gz
PSARC/2008/181 Solaris Hotplug Framework
6837240 Solaris Hotplug Framework 6783012 Add support for PCIe Alternate Routing-ID Interpretation 6638136 remove obsolete ndi_ra_xxx logic from px_msi code 6695081 Race condition between pciehpc_intr() and pciehpc_init() --HG-- rename : usr/src/uts/common/io/hotplug/pciehpc/pciehpc.c => usr/src/uts/common/io/pciex/hotplug/pciehpc.c rename : usr/src/uts/common/io/hotplug/pcishpc/pcishpc.c => usr/src/uts/common/io/pciex/hotplug/pcishpc.c rename : usr/src/uts/intel/io/hotplug/pciehpc/pciehpc_acpi.c => usr/src/uts/intel/io/pciex/hotplug/pciehpc_acpi.c rename : usr/src/uts/intel/io/hotplug/pciehpc/pciehpc_acpi.h => usr/src/uts/intel/sys/hotplug/pci/pciehpc_acpi.h rename : usr/src/uts/sparc/pcicfg.e/Makefile => usr/src/uts/sparc/pcicfg/Makefile rename : usr/src/uts/sun4/io/pcicfg.e.c => usr/src/uts/sun4/io/pcicfg.c
Diffstat (limited to 'usr/src')
-rw-r--r--usr/src/Makefile.lint3
-rw-r--r--usr/src/cmd/Makefile3
-rw-r--r--usr/src/cmd/cfgadm/cfgadm.c139
-rw-r--r--usr/src/cmd/hotplug/Makefile57
-rw-r--r--usr/src/cmd/hotplug/hotplug.c1286
-rw-r--r--usr/src/cmd/hotplugd/Makefile68
-rw-r--r--usr/src/cmd/hotplugd/hotplug.xml96
-rw-r--r--usr/src/cmd/hotplugd/hotplugd.c405
-rw-r--r--usr/src/cmd/hotplugd/hotplugd_door.c760
-rw-r--r--usr/src/cmd/hotplugd/hotplugd_impl.c435
-rw-r--r--usr/src/cmd/hotplugd/hotplugd_impl.h81
-rw-r--r--usr/src/cmd/hotplugd/hotplugd_info.c513
-rw-r--r--usr/src/cmd/hotplugd/hotplugd_rcm.c703
-rw-r--r--usr/src/cmd/hotplugd/svc-hotplug54
-rw-r--r--usr/src/cmd/mdb/common/modules/genunix/Makefile.files1
-rw-r--r--usr/src/cmd/mdb/common/modules/genunix/devinfo.c16
-rw-r--r--usr/src/cmd/mdb/common/modules/genunix/devinfo.h19
-rw-r--r--usr/src/cmd/mdb/common/modules/genunix/genunix.c5
-rw-r--r--usr/src/cmd/mdb/common/modules/genunix/hotplug.c164
-rw-r--r--[-rwxr-xr-x]usr/src/cmd/mdb/common/modules/genunix/hotplug.h (renamed from usr/src/uts/common/sys/hotplug/pci/pcishpc_regs.h)33
-rw-r--r--usr/src/cmd/pcitool/pcitool.c125
-rw-r--r--usr/src/cmd/truss/print.c1
-rw-r--r--usr/src/head/auth_list.h3
-rw-r--r--usr/src/lib/Makefile3
-rw-r--r--usr/src/lib/cfgadm_plugins/Makefile4
-rw-r--r--usr/src/lib/cfgadm_plugins/shp/Makefile70
-rw-r--r--usr/src/lib/cfgadm_plugins/shp/Makefile.com86
-rw-r--r--usr/src/lib/cfgadm_plugins/shp/amd64/Makefile31
-rw-r--r--usr/src/lib/cfgadm_plugins/shp/common/mapfile-vers50
-rw-r--r--usr/src/lib/cfgadm_plugins/shp/common/shp.c1698
-rw-r--r--usr/src/lib/cfgadm_plugins/shp/i386/Makefile30
-rw-r--r--usr/src/lib/cfgadm_plugins/shp/shp.xcl154
-rw-r--r--usr/src/lib/cfgadm_plugins/shp/sparc/Makefile30
-rw-r--r--usr/src/lib/cfgadm_plugins/shp/sparcv9/Makefile31
-rw-r--r--usr/src/lib/libbsm/audit_event.txt5
-rw-r--r--usr/src/lib/libbsm/common/adt.xml77
-rw-r--r--usr/src/lib/libcfgadm/common/config_admin.c698
-rw-r--r--usr/src/lib/libdevinfo/devinfo.c279
-rw-r--r--usr/src/lib/libdevinfo/libdevinfo.h25
-rw-r--r--usr/src/lib/libdevinfo/mapfile-vers10
-rw-r--r--usr/src/lib/libhotplug/Makefile49
-rw-r--r--usr/src/lib/libhotplug/Makefile.com45
-rw-r--r--usr/src/lib/libhotplug/amd64/Makefile29
-rw-r--r--usr/src/lib/libhotplug/common/libhotplug.c1367
-rw-r--r--usr/src/lib/libhotplug/common/libhotplug.h104
-rw-r--r--usr/src/lib/libhotplug/common/libhotplug_impl.h105
-rw-r--r--usr/src/lib/libhotplug/common/llib-lhotplug29
-rw-r--r--usr/src/lib/libhotplug/common/mapfile-vers63
-rw-r--r--usr/src/lib/libhotplug/i386/Makefile28
-rw-r--r--usr/src/lib/libhotplug/sparc/Makefile28
-rw-r--r--usr/src/lib/libhotplug/sparcv9/Makefile29
-rw-r--r--usr/src/lib/libsecdb/auth_attr.txt4
-rw-r--r--usr/src/lib/libsecdb/help/auths/HotplugHeader.html34
-rw-r--r--usr/src/lib/libsecdb/help/auths/HotplugModify.html39
-rw-r--r--usr/src/lib/libsecdb/help/auths/Makefile3
-rw-r--r--usr/src/lib/libsecdb/help/auths/SmfManageHotplug.html37
-rw-r--r--usr/src/lib/libsecdb/help/profiles/Makefile1
-rw-r--r--usr/src/lib/libsecdb/help/profiles/RtHotplugMngmnt.html37
-rw-r--r--usr/src/lib/libsecdb/prof_attr.txt3
-rw-r--r--usr/src/pkgdefs/SUNW0on/prototype_com4
-rw-r--r--usr/src/pkgdefs/SUNWarc/prototype_com2
-rw-r--r--usr/src/pkgdefs/SUNWarc/prototype_i3861
-rw-r--r--usr/src/pkgdefs/SUNWarc/prototype_sparc1
-rw-r--r--usr/src/pkgdefs/SUNWckr/prototype_i3862
-rw-r--r--usr/src/pkgdefs/SUNWckr/prototype_sparc2
-rw-r--r--usr/src/pkgdefs/SUNWcsl/prototype_com4
-rw-r--r--usr/src/pkgdefs/SUNWcsl/prototype_i3864
-rw-r--r--usr/src/pkgdefs/SUNWcsl/prototype_sparc4
-rw-r--r--usr/src/pkgdefs/SUNWcsr/prototype_com2
-rw-r--r--usr/src/pkgdefs/SUNWcsu/prototype_com6
-rw-r--r--usr/src/pkgdefs/SUNWefck/prototype_com6
-rw-r--r--usr/src/pkgdefs/SUNWhea/prototype_com2
-rw-r--r--usr/src/tools/scripts/bfu.sh40
-rw-r--r--usr/src/uts/common/Makefile.files10
-rw-r--r--usr/src/uts/common/Makefile.rules11
-rw-r--r--usr/src/uts/common/io/busra.c644
-rw-r--r--usr/src/uts/common/io/devinfo.c144
-rw-r--r--usr/src/uts/common/io/hotplug/pciehpc/pciehpc.c1982
-rw-r--r--usr/src/uts/common/io/hotplug/pcihp/pcihp.c22
-rw-r--r--usr/src/uts/common/io/hotplug/pcishpc/pcishpc.c2656
-rw-r--r--usr/src/uts/common/io/pciex/hotplug/pcie_hp.c1300
-rw-r--r--usr/src/uts/common/io/pciex/hotplug/pciehpc.c2285
-rw-r--r--usr/src/uts/common/io/pciex/hotplug/pcishpc.c2645
-rw-r--r--usr/src/uts/common/io/pciex/pcie.c463
-rw-r--r--usr/src/uts/common/io/pciex/pcieb.c409
-rw-r--r--usr/src/uts/common/io/pciex/pcieb.h28
-rw-r--r--usr/src/uts/common/os/ddi_hp_impl.c1139
-rw-r--r--usr/src/uts/common/os/ddi_hp_ndi.c405
-rw-r--r--usr/src/uts/common/os/devcfg.c25
-rw-r--r--usr/src/uts/common/os/modctl.c80
-rw-r--r--usr/src/uts/common/sys/Makefile2
-rw-r--r--usr/src/uts/common/sys/autoconf.h10
-rw-r--r--usr/src/uts/common/sys/ddi_hp.h126
-rw-r--r--usr/src/uts/common/sys/ddi_hp_impl.h160
-rw-r--r--usr/src/uts/common/sys/ddi_impldefs.h16
-rw-r--r--usr/src/uts/common/sys/devinfo_impl.h25
-rw-r--r--usr/src/uts/common/sys/devops.h15
-rw-r--r--usr/src/uts/common/sys/hotplug/pci/pcicfg.h17
-rw-r--r--usr/src/uts/common/sys/hotplug/pci/pcie_hp.h332
-rw-r--r--usr/src/uts/common/sys/hotplug/pci/pciehpc.h37
-rw-r--r--usr/src/uts/common/sys/hotplug/pci/pciehpc_impl.h233
-rw-r--r--usr/src/uts/common/sys/hotplug/pci/pcihp.h4
-rw-r--r--usr/src/uts/common/sys/hotplug/pci/pcishpc.h24
-rw-r--r--usr/src/uts/common/sys/modctl.h10
-rw-r--r--usr/src/uts/common/sys/pci.h100
-rw-r--r--usr/src/uts/common/sys/pci_impl.h19
-rw-r--r--usr/src/uts/common/sys/pcie.h75
-rw-r--r--usr/src/uts/common/sys/pcie_impl.h87
-rw-r--r--usr/src/uts/common/sys/sunndi.h35
-rw-r--r--usr/src/uts/i86pc/Makefile.files2
-rw-r--r--usr/src/uts/i86pc/Makefile.rules7
-rw-r--r--usr/src/uts/i86pc/io/pci/pci.c31
-rw-r--r--usr/src/uts/i86pc/io/pci/pci_common.c19
-rw-r--r--usr/src/uts/i86pc/io/pci/pci_common.h4
-rw-r--r--usr/src/uts/i86pc/io/pci/pci_tools.c6
-rw-r--r--usr/src/uts/i86pc/io/pci/pci_tools_ext.h16
-rw-r--r--usr/src/uts/i86pc/io/pciex/inc.flg1
-rw-r--r--usr/src/uts/i86pc/io/pciex/npe.c233
-rw-r--r--usr/src/uts/i86pc/npe/Makefile11
-rw-r--r--usr/src/uts/i86pc/pcie/Makefile11
-rw-r--r--usr/src/uts/i86xpv/Makefile.files2
-rw-r--r--usr/src/uts/i86xpv/Makefile.rules9
-rw-r--r--usr/src/uts/i86xpv/npe/Makefile13
-rw-r--r--usr/src/uts/i86xpv/pcie/Makefile11
-rw-r--r--usr/src/uts/intel/Makefile.files1
-rw-r--r--usr/src/uts/intel/Makefile.intel.shared1
-rw-r--r--usr/src/uts/intel/Makefile.rules6
-rw-r--r--usr/src/uts/intel/ia32/ml/modstubs.s14
-rw-r--r--usr/src/uts/intel/io/hotplug/pcicfg/pcicfg.c2083
-rw-r--r--usr/src/uts/intel/io/pci/pci_boot.c2
-rw-r--r--usr/src/uts/intel/io/pci/pci_pci.c163
-rw-r--r--usr/src/uts/intel/io/pciex/hotplug/pciehpc_acpi.c (renamed from usr/src/uts/intel/io/hotplug/pciehpc/pciehpc_acpi.c)425
-rw-r--r--usr/src/uts/intel/io/pciex/pcieb_x86.c12
-rw-r--r--usr/src/uts/intel/pci_autoconfig/Makefile12
-rw-r--r--usr/src/uts/intel/pci_pci/Makefile8
-rw-r--r--usr/src/uts/intel/pcicfg/Makefile6
-rw-r--r--usr/src/uts/intel/pcieb/Makefile9
-rw-r--r--usr/src/uts/intel/pciehpc/Makefile104
-rw-r--r--usr/src/uts/intel/sys/hotplug/pci/pciehpc_acpi.h (renamed from usr/src/uts/intel/io/hotplug/pciehpc/pciehpc_acpi.h)3
-rw-r--r--usr/src/uts/sparc/Makefile.files2
-rw-r--r--usr/src/uts/sparc/Makefile.sparc.shared4
-rw-r--r--usr/src/uts/sparc/io/pciex/pcieb_sparc.c16
-rw-r--r--usr/src/uts/sparc/ml/modstubs.s34
-rw-r--r--usr/src/uts/sparc/pci_pci/Makefile6
-rw-r--r--usr/src/uts/sparc/pcicfg/Makefile (renamed from usr/src/uts/sparc/pcicfg.e/Makefile)12
-rw-r--r--usr/src/uts/sparc/pcie/Makefile12
-rw-r--r--usr/src/uts/sparc/pcieb/Makefile2
-rw-r--r--usr/src/uts/sparc/pcieb_bcm/Makefile4
-rw-r--r--usr/src/uts/sparc/pciehpc/Makefile93
-rw-r--r--usr/src/uts/sparc/pcihp/Makefile6
-rw-r--r--usr/src/uts/sparc/pcishpc/Makefile94
-rw-r--r--usr/src/uts/sun4/io/pcicfg.c (renamed from usr/src/uts/sun4/io/pcicfg.e.c)892
-rw-r--r--usr/src/uts/sun4/io/px/px.c191
-rw-r--r--usr/src/uts/sun4/io/px/px_devctl.c158
-rw-r--r--usr/src/uts/sun4/io/px/px_fm.c14
-rw-r--r--usr/src/uts/sun4/io/px/px_msi.c194
-rw-r--r--usr/src/uts/sun4/io/px/px_msi.h11
-rw-r--r--usr/src/uts/sun4/io/px/px_msiq.c67
-rw-r--r--usr/src/uts/sun4/io/px/px_msiq.h8
-rw-r--r--usr/src/uts/sun4/io/px/px_obj.h5
-rw-r--r--usr/src/uts/sun4/io/px/px_pec.c4
-rw-r--r--usr/src/uts/sun4/io/px/px_space.c1
-rw-r--r--usr/src/uts/sun4/io/px/px_tools.c5
-rw-r--r--usr/src/uts/sun4/io/px/px_tools_ext.h17
-rw-r--r--usr/src/uts/sun4/io/px/px_util.c9
-rw-r--r--usr/src/uts/sun4/io/px/px_util.h6
-rw-r--r--usr/src/uts/sun4/io/px/px_var.h40
-rw-r--r--usr/src/uts/sun4u/io/pci/pci.c8
-rw-r--r--usr/src/uts/sun4u/io/pci/pci_devctl.c6
-rw-r--r--usr/src/uts/sun4u/io/pci/pci_pci.c238
-rw-r--r--usr/src/uts/sun4u/io/px/oberon_regs.h5
-rw-r--r--usr/src/uts/sun4u/io/px/px_hlib.c35
-rw-r--r--usr/src/uts/sun4u/io/px/px_lib4u.c32
-rw-r--r--usr/src/uts/sun4u/io/px/px_lib4u.h11
-rw-r--r--usr/src/uts/sun4u/px/Makefile6
-rw-r--r--usr/src/uts/sun4u/sys/pci/pci_obj.h10
-rw-r--r--usr/src/uts/sun4u/sys/pci/pci_tools_ext.h16
-rw-r--r--usr/src/uts/sun4u/sys/pci/pci_var.h8
-rw-r--r--usr/src/uts/sun4v/io/px/px_lib4v.c9
-rw-r--r--usr/src/uts/sun4v/niumx/Makefile12
-rw-r--r--usr/src/uts/sun4v/px/Makefile6
181 files changed, 23333 insertions, 8141 deletions
diff --git a/usr/src/Makefile.lint b/usr/src/Makefile.lint
index e65728b327..715ab9cf9a 100644
--- a/usr/src/Makefile.lint
+++ b/usr/src/Makefile.lint
@@ -156,6 +156,8 @@ COMMON_SUBDIRS = \
cmd/head \
cmd/hostid \
cmd/hostname \
+ cmd/hotplug \
+ cmd/hotplugd \
cmd/idmap \
cmd/init \
cmd/intrstat \
@@ -361,6 +363,7 @@ COMMON_SUBDIRS = \
lib/libgen \
lib/libgrubmgmt \
lib/libgss \
+ lib/libhotplug \
lib/libidmap \
lib/libinetcfg \
lib/libinetsvc \
diff --git a/usr/src/cmd/Makefile b/usr/src/cmd/Makefile
index e9423852de..c664306fd2 100644
--- a/usr/src/cmd/Makefile
+++ b/usr/src/cmd/Makefile
@@ -192,6 +192,8 @@ COMMON_SUBDIRS= \
head \
hostid \
hostname \
+ hotplug \
+ hotplugd \
hwdata \
id \
idmap \
@@ -593,6 +595,7 @@ MSGSUBDIRS= \
halt \
head \
hostname \
+ hotplug \
id \
idmap \
isaexec \
diff --git a/usr/src/cmd/cfgadm/cfgadm.c b/usr/src/cmd/cfgadm/cfgadm.c
index 38673335a4..1ef642871d 100644
--- a/usr/src/cmd/cfgadm/cfgadm.c
+++ b/usr/src/cmd/cfgadm/cfgadm.c
@@ -20,7 +20,7 @@
*/
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -405,86 +405,86 @@ main(
switch (action) {
case CFGA_OP_CHANGE_STATE:
- /* Sanity check - requires an argument */
- if ((argc - optind) <= 0) {
- usage();
- break;
- }
- /* Sanity check - args cannot be ap_types */
- for (i = 0; i < (argc - optind); i++) {
- if (find_arg_type(ap_args[i]) == AP_TYPE) {
+ /* Sanity check - requires an argument */
+ if ((argc - optind) <= 0) {
usage();
- exit(EXIT_ARGERROR);
- /*NOTREACHED*/
- }
- }
- ret = config_change_state(sc_opt, argc - optind, ap_args, plat_opts,
- &confirm, &message, &estrp, flags);
- if (ret != CFGA_OK)
- cfgadm_error(ret, estrp);
- break;
+ break;
+ }
+ /* Sanity check - args cannot be ap_types */
+ for (i = 0; i < (argc - optind); i++) {
+ if (find_arg_type(ap_args[i]) == AP_TYPE) {
+ usage();
+ exit(EXIT_ARGERROR);
+ /*NOTREACHED*/
+ }
+ }
+ ret = config_change_state(sc_opt, argc - optind, ap_args,
+ plat_opts, &confirm, &message, &estrp, flags);
+ if (ret != CFGA_OK)
+ cfgadm_error(ret, estrp);
+ break;
case CFGA_OP_PRIVATE:
- /* Sanity check - requires an argument */
- if ((argc - optind) <= 0) {
- usage();
+ /* Sanity check - requires an argument */
+ if ((argc - optind) <= 0) {
+ usage();
+ break;
+ }
+ /* Sanity check - args cannot be ap_types */
+ for (i = 0; i < (argc - optind); i++) {
+ if (find_arg_type(ap_args[i]) == AP_TYPE) {
+ usage();
+ exit(EXIT_ARGERROR);
+ /*NOTREACHED*/
+ }
+ }
+
+ ret = config_private_func(act_arg, argc - optind, ap_args,
+ plat_opts, &confirm, &message, &estrp, flags);
+
+ if (ret != CFGA_OK)
+ cfgadm_error(ret, estrp);
break;
- }
- /* Sanity check - args cannot be ap_types */
- for (i = 0; i < (argc - optind); i++) {
- if (find_arg_type(ap_args[i]) == AP_TYPE) {
+ case CFGA_OP_TEST:
+ /* Sanity check - requires an argument */
+ if ((argc - optind) <= 0) {
+ usage();
+ break;
+ }
+
+ if ((flags & ~CFGA_FLAG_VERBOSE) != 0) {
usage();
exit(EXIT_ARGERROR);
/*NOTREACHED*/
- }
- }
-
- ret = config_private_func(act_arg, argc - optind, ap_args,
- plat_opts, &confirm, &message, &estrp, flags);
+ }
- if (ret != CFGA_OK)
- cfgadm_error(ret, estrp);
- break;
- case CFGA_OP_TEST:
- /* Sanity check - requires an argument */
- if ((argc - optind) <= 0) {
- usage();
+ /* Sanity check - args cannot be ap_types */
+ for (i = 0; i < (argc - optind); i++) {
+ if (find_arg_type(ap_args[i]) == AP_TYPE) {
+ usage();
+ exit(EXIT_ARGERROR);
+ /*NOTREACHED*/
+ }
+ }
+ ret = config_test(argc - optind, ap_args, plat_opts, &message,
+ &estrp, flags);
+ if (ret != CFGA_OK)
+ cfgadm_error(ret, estrp);
break;
- }
-
- if ((flags & ~CFGA_FLAG_VERBOSE) != 0) {
- usage();
- exit(EXIT_ARGERROR);
- /*NOTREACHED*/
- }
+ case CFGA_OP_HELP:
- /* Sanity check - args cannot be ap_types */
- for (i = 0; i < (argc - optind); i++) {
- if (find_arg_type(ap_args[i]) == AP_TYPE) {
+ if ((flags & ~CFGA_FLAG_VERBOSE) != 0) {
usage();
exit(EXIT_ARGERROR);
/*NOTREACHED*/
- }
- }
- ret = config_test(argc - optind, ap_args, plat_opts, &message,
- &estrp, flags);
- if (ret != CFGA_OK)
- cfgadm_error(ret, estrp);
- break;
- case CFGA_OP_HELP:
+ }
- if ((flags & ~CFGA_FLAG_VERBOSE) != 0) {
+ /* always do usage? */
usage();
- exit(EXIT_ARGERROR);
- /*NOTREACHED*/
- }
-
- /* always do usage? */
- usage();
- ret = config_help(argc - optind, ap_args, &message, plat_opts,
- flags);
- if (ret != CFGA_OK)
- cfgadm_error(ret, estrp);
- break;
+ ret = config_help(argc - optind, ap_args, &message, plat_opts,
+ flags);
+ if (ret != CFGA_OK)
+ cfgadm_error(ret, estrp);
+ break;
case CFGA_OP_LIST: {
/*
@@ -604,6 +604,8 @@ main(
S_FREE(list_array);
S_FREE(post_filtp);
+ if (estrp != NULL && *estrp != '\0')
+ cfgadm_error(CFGA_NOTSUPP, estrp);
if (exitcode != EXIT_OK) {
exit(exitcode);
/*NOTREACHED*/
@@ -634,8 +636,7 @@ main(
* usage - outputs the usage help message.
*/
static void
-usage(
- void)
+usage(void)
{
int i;
diff --git a/usr/src/cmd/hotplug/Makefile b/usr/src/cmd/hotplug/Makefile
new file mode 100644
index 0000000000..fe62d9acc6
--- /dev/null
+++ b/usr/src/cmd/hotplug/Makefile
@@ -0,0 +1,57 @@
+#
+# 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.
+#
+# cmd/hotplug/Makefile
+#
+
+PROG= hotplug
+
+include ../Makefile.cmd
+
+OBJS= hotplug.o
+SRCS= $(OBJS:.o=.c)
+
+CPPFLAGS += -I$(SRC)/lib/libhotplug/common
+LDLIBS += -lhotplug
+
+XGETFLAGS += -s
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+$(PROG): $(OBJS)
+ $(LINK.c) -o $@ $(OBJS) $(LDLIBS)
+ $(POST_PROCESS)
+
+install: all $(ROOTUSRSBINPROG)
+
+check: $(PROG).c
+ $(CSTYLE) -pP $(SRCS:%=%)
+
+clean:
+
+lint: lint_SRCS
+
+include ../Makefile.targ
diff --git a/usr/src/cmd/hotplug/hotplug.c b/usr/src/cmd/hotplug/hotplug.c
new file mode 100644
index 0000000000..8ffb8d48dd
--- /dev/null
+++ b/usr/src/cmd/hotplug/hotplug.c
@@ -0,0 +1,1286 @@
+/*
+ * 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <locale.h>
+#include <libintl.h>
+#include <alloca.h>
+#include <getopt.h>
+#include <libhotplug.h>
+#include <sys/types.h>
+#include <sys/sunddi.h>
+#include <sys/ddi_hp.h>
+
+#if !defined(TEXT_DOMAIN) /* should be defined by cc -D */
+#define TEXT_DOMAIN "SYS_TEST" /* Use this only if it wasn't */
+#endif
+
+/*
+ * Function prototypes.
+ */
+static int cmd_list(int, char **, const char *);
+static int cmd_online(int, char **, const char *);
+static int cmd_offline(int, char **, const char *);
+static int cmd_enable(int, char **, const char *);
+static int cmd_disable(int, char **, const char *);
+static int cmd_poweron(int, char **, const char *);
+static int cmd_poweroff(int, char **, const char *);
+static int cmd_getpriv(int, char **, const char *);
+static int cmd_setpriv(int, char **, const char *);
+static int cmd_changestate(int, char **, const char *);
+static void parse_common(int, char **, const char *);
+static void parse_flags(int, char **, int *, const char *);
+static void parse_target(int, char **, char **, char **, const char *);
+static void parse_options(int, char **, char **, const char *);
+static void bad_option(int, int, const char *);
+static void usage(const char *);
+static int list_cb(hp_node_t, void *);
+static int list_long_cb(hp_node_t, void *);
+static int error_cb(hp_node_t, void *);
+static void print_options(const char *);
+static void print_error(int);
+static int state_atoi(char *);
+static char *state_itoa(int);
+static short valid_target(int);
+
+/*
+ * Define a conversion table for hotplug states.
+ */
+typedef struct {
+ int state;
+ char *state_str;
+ short valid_target;
+} hpstate_t;
+
+static hpstate_t hpstates[] = {
+ { DDI_HP_CN_STATE_EMPTY, "EMPTY", 0 },
+ { DDI_HP_CN_STATE_PRESENT, "PRESENT", 1 },
+ { DDI_HP_CN_STATE_POWERED, "POWERED", 1 },
+ { DDI_HP_CN_STATE_ENABLED, "ENABLED", 1 },
+ { DDI_HP_CN_STATE_PORT_EMPTY, "PORT-EMPTY", 0 },
+ { DDI_HP_CN_STATE_PORT_PRESENT, "PORT-PRESENT", 1 },
+ { DDI_HP_CN_STATE_OFFLINE, "OFFLINE", 1 },
+ { DDI_HP_CN_STATE_ATTACHED, "ATTACHED", 0 },
+ { DDI_HP_CN_STATE_MAINTENANCE, "MAINTENANCE", 0 },
+ { DDI_HP_CN_STATE_ONLINE, "ONLINE", 1 },
+ { 0, 0, 0 }
+};
+
+/*
+ * Define tables of supported subcommands.
+ */
+typedef struct {
+ char *usage_str;
+ char *cmd_str;
+ int (*func)(int argc, char *argv[], const char *usage_str);
+} subcmd_t;
+
+static subcmd_t subcmds[] = {
+ { "list [-l] [-v] [<path> [<connection>]]", "list", cmd_list },
+ { "online <path> <port>", "online", cmd_online },
+ { "offline [-f] [-q] <path> <port>", "offline", cmd_offline },
+ { "enable <path> <connector>", "enable", cmd_enable },
+ { "disable [-f] [-q] <path> <connector>", "disable", cmd_disable },
+ { "poweron <path> <connector>", "poweron", cmd_poweron },
+ { "poweroff [-f] [-q] <path> <connector>", "poweroff", cmd_poweroff },
+ { "get -o <options> <path> <connector>", "get", cmd_getpriv },
+ { "set -o <options> <path> <connector>", "set", cmd_setpriv }
+};
+
+static subcmd_t hidden_subcmds[] = {
+ { "changestate [-f] [-q] -s <state> <path> <connection>",
+ "changestate", cmd_changestate }
+};
+
+/*
+ * Define tables of command line options.
+ */
+static const struct option common_opts[] = {
+ { "help", no_argument, 0, '?' },
+ { "version", no_argument, 0, 'V' },
+ { 0, 0, 0, 0 }
+};
+
+static const struct option list_opts[] = {
+ { "list-path", no_argument, 0, 'l' },
+ { "verbose", no_argument, 0, 'v' },
+ { 0, 0, 0, 0 }
+};
+
+static const struct option flag_opts[] = {
+ { "force", no_argument, 0, 'f' },
+ { "query", no_argument, 0, 'q' },
+ { 0, 0, 0, 0 }
+};
+
+static const struct option private_opts[] = {
+ { "options", required_argument, 0, 'o' },
+ { 0, 0, 0, 0 }
+};
+
+static const struct option changestate_opts[] = {
+ { "force", no_argument, 0, 'f' },
+ { "query", no_argument, 0, 'q' },
+ { "state", required_argument, 0, 's' },
+ { 0, 0, 0, 0 }
+};
+
+/*
+ * Define exit codes.
+ */
+#define EXIT_OK 0
+#define EXIT_EINVAL 1 /* invalid arguments */
+#define EXIT_ENOENT 2 /* path or connection doesn't exist */
+#define EXIT_FAILED 3 /* operation failed */
+#define EXIT_UNAVAIL 4 /* service not available */
+
+/*
+ * Global variables.
+ */
+static char *prog;
+static char version[] = "1.0";
+extern int errno;
+
+/*
+ * main()
+ *
+ * The main routine determines which subcommand is used,
+ * and dispatches control to the corresponding function.
+ */
+int
+main(int argc, char *argv[])
+{
+ int i, rv;
+
+ (void) setlocale(LC_ALL, "");
+ (void) textdomain(TEXT_DOMAIN);
+
+ if ((prog = strrchr(argv[0], '/')) == NULL)
+ prog = argv[0];
+ else
+ prog++;
+
+ if (argc < 2) {
+ usage(NULL);
+ return (EXIT_EINVAL);
+ }
+
+ parse_common(argc, argv, NULL);
+
+ /* Check the list of defined subcommands. */
+ for (i = 0; i < (sizeof (subcmds) / sizeof (subcmd_t)); i++) {
+ if (strcmp(argv[1], subcmds[i].cmd_str) == 0) {
+ rv = subcmds[i].func(argc - 1, &argv[1],
+ subcmds[i].usage_str);
+ goto finished;
+ }
+ }
+
+ /* Check the list of hidden subcommands. */
+ for (i = 0; i < (sizeof (hidden_subcmds) / sizeof (subcmd_t)); i++) {
+ if (strcmp(argv[1], hidden_subcmds[i].cmd_str) == 0) {
+ rv = hidden_subcmds[i].func(argc - 1, &argv[1],
+ hidden_subcmds[i].usage_str);
+ goto finished;
+ }
+ }
+
+ /* No matching subcommand found. */
+ (void) fprintf(stderr, gettext("ERROR: %s: unknown subcommand '%s'\n"),
+ prog, argv[1]);
+ usage(NULL);
+ exit(EXIT_EINVAL);
+
+finished:
+ /* Determine exit code */
+ switch (rv) {
+ case 0:
+ break;
+ case EINVAL:
+ return (EXIT_EINVAL);
+ case ENXIO:
+ case ENOENT:
+ return (EXIT_ENOENT);
+ case EBADF:
+ return (EXIT_UNAVAIL);
+ default:
+ return (EXIT_FAILED);
+ }
+
+ return (EXIT_OK);
+}
+
+/*
+ * cmd_list()
+ *
+ * Subcommand to list hotplug information.
+ */
+static int
+cmd_list(int argc, char *argv[], const char *usage_str)
+{
+ hp_node_t root;
+ char *path = NULL;
+ char *connection = NULL;
+ boolean_t long_flag = B_FALSE;
+ int flags = 0;
+ int opt;
+
+ /* Parse command line options */
+ parse_common(argc, argv, usage_str);
+ while ((opt = getopt_clip(argc, argv, "lv", list_opts, NULL)) != -1) {
+ switch (opt) {
+ case 'l':
+ long_flag = B_TRUE;
+ break;
+ case 'v':
+ flags |= HPINFOUSAGE;
+ break;
+ default:
+ bad_option(opt, optopt, usage_str);
+ break;
+ }
+ }
+ parse_target(argc, argv, &path, &connection, usage_str);
+
+ /* Default path is "/" */
+ if (path == NULL)
+ path = "/";
+
+ /* Get hotplug information snapshot */
+ if ((root = hp_init(path, connection, flags)) == NULL) {
+ print_error(errno);
+ return (errno);
+ }
+
+ /* Display hotplug information */
+ (void) hp_traverse(root, NULL, long_flag ? list_long_cb : list_cb);
+
+ /* Discard hotplug information snapshot */
+ hp_fini(root);
+
+ return (0);
+}
+
+/*
+ * cmd_online()
+ *
+ * Subcommand to online a hotplug port.
+ */
+static int
+cmd_online(int argc, char *argv[], const char *usage_str)
+{
+ hp_node_t root;
+ hp_node_t results = NULL;
+ char *path = NULL;
+ char *connection = NULL;
+ int rv;
+
+ /* Parse command line options */
+ parse_common(argc, argv, usage_str);
+ parse_target(argc, argv, &path, &connection, usage_str);
+
+ /* Path and connection are required */
+ if ((path == NULL) || (connection == NULL)) {
+ (void) fprintf(stderr, gettext("ERROR: too few arguments.\n"));
+ usage(usage_str);
+ return (EINVAL);
+ }
+
+ /* Get hotplug information snapshot */
+ if ((root = hp_init(path, connection, 0)) == NULL) {
+ print_error(errno);
+ return (errno);
+ }
+
+ /* Verify target is a port */
+ if (hp_type(root) != HP_NODE_PORT) {
+ (void) fprintf(stderr,
+ gettext("ERROR: invalid target (must be a port).\n"));
+ hp_fini(root);
+ return (EINVAL);
+ }
+
+ /* Do state change */
+ rv = hp_set_state(root, 0, DDI_HP_CN_STATE_ONLINE, &results);
+
+ /* Display results */
+ if (rv == EIO) {
+ (void) fprintf(stderr, gettext("ERROR: failed to attach device "
+ "drivers or other internal errors.\n"));
+ } else if (rv != 0) {
+ print_error(rv);
+ }
+ if (results != NULL) {
+ (void) hp_traverse(results, NULL, error_cb);
+ hp_fini(results);
+ }
+
+ /* Discard hotplug information snapshot */
+ hp_fini(root);
+
+ return (rv);
+}
+
+/*
+ * cmd_offline()
+ *
+ * Subcommand to offline a hotplug port.
+ */
+static int
+cmd_offline(int argc, char *argv[], const char *usage_str)
+{
+ hp_node_t root;
+ hp_node_t results = NULL;
+ char *path = NULL;
+ char *connection = NULL;
+ int flags = 0;
+ int rv;
+
+ /* Parse command line options */
+ parse_common(argc, argv, usage_str);
+ parse_flags(argc, argv, &flags, usage_str);
+ parse_target(argc, argv, &path, &connection, usage_str);
+
+ /* Path and connection are required */
+ if ((path == NULL) || (connection == NULL)) {
+ (void) fprintf(stderr, gettext("ERROR: too few arguments.\n"));
+ usage(usage_str);
+ return (EINVAL);
+ }
+
+ /* Get hotplug information snapshot */
+ if ((root = hp_init(path, connection, 0)) == NULL) {
+ print_error(errno);
+ return (errno);
+ }
+
+ /* Verify target is a port */
+ if (hp_type(root) != HP_NODE_PORT) {
+ (void) fprintf(stderr,
+ gettext("ERROR: invalid target (must be a port).\n"));
+ hp_fini(root);
+ return (EINVAL);
+ }
+
+ /* Do state change */
+ rv = hp_set_state(root, flags, DDI_HP_CN_STATE_OFFLINE, &results);
+
+ /* Display results */
+ print_error(rv);
+ if (results != NULL) {
+ (void) hp_traverse(results, NULL, error_cb);
+ hp_fini(results);
+ }
+
+ /* Discard hotplug information snapshot */
+ hp_fini(root);
+
+ return (rv);
+}
+
+/*
+ * cmd_enable()
+ *
+ * Subcommand to enable a hotplug connector.
+ */
+static int
+cmd_enable(int argc, char *argv[], const char *usage_str)
+{
+ hp_node_t root;
+ hp_node_t results = NULL;
+ char *path = NULL;
+ char *connection = NULL;
+ int rv;
+
+ /* Parse command line options */
+ parse_common(argc, argv, usage_str);
+ parse_target(argc, argv, &path, &connection, usage_str);
+
+ /* Path and connection are required */
+ if ((path == NULL) || (connection == NULL)) {
+ (void) fprintf(stderr, gettext("ERROR: too few arguments.\n"));
+ usage(usage_str);
+ return (EINVAL);
+ }
+
+ /* Get hotplug information snapshot */
+ if ((root = hp_init(path, connection, 0)) == NULL) {
+ print_error(errno);
+ return (errno);
+ }
+
+ /* Verify target is a connector */
+ if (hp_type(root) != HP_NODE_CONNECTOR) {
+ (void) fprintf(stderr,
+ gettext("ERROR: invalid target (must be a connector).\n"));
+ hp_fini(root);
+ return (EINVAL);
+ }
+
+ /* Do state change */
+ rv = hp_set_state(root, 0, DDI_HP_CN_STATE_ENABLED, &results);
+
+ /* Display results */
+ print_error(rv);
+ if (results != NULL) {
+ (void) hp_traverse(results, NULL, error_cb);
+ hp_fini(results);
+ }
+
+ /* Discard hotplug information snapshot */
+ hp_fini(root);
+
+ return (rv);
+}
+
+/*
+ * cmd_disable()
+ *
+ * Subcommand to disable a hotplug connector.
+ */
+static int
+cmd_disable(int argc, char *argv[], const char *usage_str)
+{
+ hp_node_t root;
+ hp_node_t results = NULL;
+ char *path = NULL;
+ char *connection = NULL;
+ int flags = 0;
+ int rv;
+
+ /* Parse command line options */
+ parse_common(argc, argv, usage_str);
+ parse_flags(argc, argv, &flags, usage_str);
+ parse_target(argc, argv, &path, &connection, usage_str);
+
+ /* Path and connection are required */
+ if ((path == NULL) || (connection == NULL)) {
+ (void) fprintf(stderr, gettext("ERROR: too few arguments.\n"));
+ usage(usage_str);
+ return (EINVAL);
+ }
+
+ /* Get hotplug information snapshot */
+ if ((root = hp_init(path, connection, 0)) == NULL) {
+ print_error(errno);
+ return (errno);
+ }
+
+ /* Verify target is a connector */
+ if (hp_type(root) != HP_NODE_CONNECTOR) {
+ (void) fprintf(stderr,
+ gettext("ERROR: invalid target (must be a connector).\n"));
+ hp_fini(root);
+ return (EINVAL);
+ }
+
+ /*
+ * Do nothing unless the connector is in the ENABLED state.
+ * Otherwise this subcommand becomes an alias for 'poweron.'
+ */
+ if (hp_state(root) != DDI_HP_CN_STATE_ENABLED) {
+ hp_fini(root);
+ return (0);
+ }
+
+ /* Do state change */
+ rv = hp_set_state(root, flags, DDI_HP_CN_STATE_POWERED, &results);
+
+ /* Display results */
+ print_error(rv);
+ if (results != NULL) {
+ (void) hp_traverse(results, NULL, error_cb);
+ hp_fini(results);
+ }
+
+ /* Discard hotplug information snapshot */
+ hp_fini(root);
+
+ return (rv);
+}
+
+/*
+ * cmd_poweron()
+ *
+ * Subcommand to power on a hotplug connector.
+ */
+static int
+cmd_poweron(int argc, char *argv[], const char *usage_str)
+{
+ hp_node_t root;
+ hp_node_t results = NULL;
+ char *path = NULL;
+ char *connection = NULL;
+ int rv;
+
+ /* Parse command line options */
+ parse_common(argc, argv, usage_str);
+ parse_target(argc, argv, &path, &connection, usage_str);
+
+ /* Path and connection are required */
+ if ((path == NULL) || (connection == NULL)) {
+ (void) fprintf(stderr, gettext("ERROR: too few arguments.\n"));
+ usage(usage_str);
+ return (EINVAL);
+ }
+
+ /* Get hotplug information snapshot */
+ if ((root = hp_init(path, connection, 0)) == NULL) {
+ print_error(errno);
+ return (errno);
+ }
+
+ /* Verify target is a connector */
+ if (hp_type(root) != HP_NODE_CONNECTOR) {
+ (void) fprintf(stderr,
+ gettext("ERROR: invalid target (must be a connector).\n"));
+ hp_fini(root);
+ return (EINVAL);
+ }
+
+ /*
+ * Do nothing if the connector is already powered.
+ * Otherwise this subcommand becomes an alias for 'disable.'
+ */
+ if (hp_state(root) >= DDI_HP_CN_STATE_POWERED) {
+ hp_fini(root);
+ return (0);
+ }
+
+ /* Do state change */
+ rv = hp_set_state(root, 0, DDI_HP_CN_STATE_POWERED, &results);
+
+ /* Display results */
+ print_error(rv);
+ if (results != NULL) {
+ (void) hp_traverse(results, NULL, error_cb);
+ hp_fini(results);
+ }
+
+ /* Discard hotplug information snapshot */
+ hp_fini(root);
+
+ return (rv);
+}
+
+/*
+ * cmd_poweroff()
+ *
+ * Subcommand to power off a hotplug connector.
+ */
+static int
+cmd_poweroff(int argc, char *argv[], const char *usage_str)
+{
+ hp_node_t root;
+ hp_node_t results = NULL;
+ char *path = NULL;
+ char *connection = NULL;
+ int flags = 0;
+ int rv;
+
+ /* Parse command line options */
+ parse_common(argc, argv, usage_str);
+ parse_flags(argc, argv, &flags, usage_str);
+ parse_target(argc, argv, &path, &connection, usage_str);
+
+ /* Path and connection are required */
+ if ((path == NULL) || (connection == NULL)) {
+ (void) fprintf(stderr, gettext("ERROR: too few arguments.\n"));
+ usage(usage_str);
+ return (EINVAL);
+ }
+
+ /* Get hotplug information snapshot */
+ if ((root = hp_init(path, connection, 0)) == NULL) {
+ print_error(errno);
+ return (errno);
+ }
+
+ /* Verify target is a connector */
+ if (hp_type(root) != HP_NODE_CONNECTOR) {
+ (void) fprintf(stderr,
+ gettext("ERROR: invalid target (must be a connector).\n"));
+ hp_fini(root);
+ return (EINVAL);
+ }
+
+ /* Do state change */
+ rv = hp_set_state(root, flags, DDI_HP_CN_STATE_PRESENT, &results);
+
+ /* Display results */
+ print_error(rv);
+ if (results != NULL) {
+ (void) hp_traverse(results, NULL, error_cb);
+ hp_fini(results);
+ }
+
+ /* Discard hotplug information snapshot */
+ hp_fini(root);
+
+ return (rv);
+}
+
+/*
+ * cmd_getpriv()
+ *
+ * Subcommand to get and display bus private options.
+ */
+static int
+cmd_getpriv(int argc, char *argv[], const char *usage_str)
+{
+ hp_node_t root;
+ char *path = NULL;
+ char *connection = NULL;
+ char *options = NULL;
+ char *results = NULL;
+ int rv;
+
+ /* Parse command line options */
+ parse_common(argc, argv, usage_str);
+ parse_options(argc, argv, &options, usage_str);
+ parse_target(argc, argv, &path, &connection, usage_str);
+
+ /* Options, path, and connection are all required */
+ if ((options == NULL) || (path == NULL) || (connection == NULL)) {
+ (void) fprintf(stderr, gettext("ERROR: too few arguments.\n"));
+ usage(usage_str);
+ return (EINVAL);
+ }
+
+ /* Get hotplug information snapshot */
+ if ((root = hp_init(path, connection, 0)) == NULL) {
+ print_error(errno);
+ return (errno);
+ }
+
+ /* Verify target is a connector */
+ if (hp_type(root) != HP_NODE_CONNECTOR) {
+ (void) fprintf(stderr,
+ gettext("ERROR: invalid target (must be a connector).\n"));
+ hp_fini(root);
+ return (EINVAL);
+ }
+
+ /* Do the operation */
+ rv = hp_get_private(root, options, &results);
+
+ /* Display results */
+ if (rv == ENOTSUP) {
+ (void) fprintf(stderr,
+ gettext("ERROR: unsupported property name or value.\n"));
+ (void) fprintf(stderr,
+ gettext("(Properties may depend upon connector state.)\n"));
+ } else if (rv != 0) {
+ print_error(rv);
+ }
+ if (results != NULL) {
+ print_options(results);
+ free(results);
+ }
+
+ /* Discard hotplug information snapshot */
+ hp_fini(root);
+
+ return (rv);
+}
+
+/*
+ * cmd_setpriv()
+ *
+ * Subcommand to set bus private options.
+ */
+static int
+cmd_setpriv(int argc, char *argv[], const char *usage_str)
+{
+ hp_node_t root;
+ char *path = NULL;
+ char *connection = NULL;
+ char *options = NULL;
+ char *results = NULL;
+ int rv;
+
+ /* Parse command line options */
+ parse_common(argc, argv, usage_str);
+ parse_options(argc, argv, &options, usage_str);
+ parse_target(argc, argv, &path, &connection, usage_str);
+
+ /* Options, path, and connection are all required */
+ if ((options == NULL) || (path == NULL) || (connection == NULL)) {
+ (void) fprintf(stderr, gettext("ERROR: too few arguments.\n"));
+ usage(usage_str);
+ return (EINVAL);
+ }
+
+ /* Get hotplug information snapshot */
+ if ((root = hp_init(path, connection, 0)) == NULL) {
+ print_error(errno);
+ return (errno);
+ }
+
+ /* Verify target is a connector */
+ if (hp_type(root) != HP_NODE_CONNECTOR) {
+ (void) fprintf(stderr,
+ gettext("ERROR: invalid target (must be a connector).\n"));
+ hp_fini(root);
+ return (EINVAL);
+ }
+
+ /* Do the operation */
+ rv = hp_set_private(root, options, &results);
+
+ /* Display results */
+ if (rv == ENOTSUP) {
+ (void) fprintf(stderr,
+ gettext("ERROR: unsupported property name or value.\n"));
+ (void) fprintf(stderr,
+ gettext("(Properties may depend upon connector state.)\n"));
+ } else if (rv != 0) {
+ print_error(rv);
+ }
+ if (results != NULL) {
+ print_options(results);
+ free(results);
+ }
+
+ /* Discard hotplug information snapshot */
+ hp_fini(root);
+
+ return (rv);
+}
+
+/*
+ * cmd_changestate()
+ *
+ * Subcommand to initiate a state change operation. This is
+ * a hidden subcommand to directly set a connector or port to
+ * a specific target state.
+ */
+static int
+cmd_changestate(int argc, char *argv[], const char *usage_str)
+{
+ hp_node_t root;
+ hp_node_t results = NULL;
+ char *path = NULL;
+ char *connection = NULL;
+ int state = -1;
+ int flags = 0;
+ int opt, rv;
+
+ /* Parse command line options */
+ parse_common(argc, argv, usage_str);
+ while ((opt = getopt_clip(argc, argv, "fqs:", changestate_opts,
+ NULL)) != -1) {
+ switch (opt) {
+ case 'f':
+ flags |= HPFORCE;
+ break;
+ case 'q':
+ flags |= HPQUERY;
+ break;
+ case 's':
+ if ((state = state_atoi(optarg)) == -1) {
+ (void) printf("ERROR: invalid target state\n");
+ return (EINVAL);
+ }
+ break;
+ default:
+ bad_option(opt, optopt, usage_str);
+ break;
+ }
+ }
+ parse_target(argc, argv, &path, &connection, usage_str);
+
+ /* State, path, and connection are all required */
+ if ((state == -1) || (path == NULL) || (connection == NULL)) {
+ (void) fprintf(stderr, gettext("ERROR: too few arguments.\n"));
+ usage(usage_str);
+ return (EINVAL);
+ }
+
+ /* Check that target state is valid */
+ if (valid_target(state) == 0) {
+ (void) fprintf(stderr,
+ gettext("ERROR: invalid target state\n"));
+ return (EINVAL);
+ }
+
+ /* Get hotplug information snapshot */
+ if ((root = hp_init(path, connection, 0)) == NULL) {
+ print_error(errno);
+ return (errno);
+ }
+
+ /* Initiate state change operation on root of snapshot */
+ rv = hp_set_state(root, flags, state, &results);
+
+ /* Display results */
+ print_error(rv);
+ if (results) {
+ (void) hp_traverse(results, NULL, error_cb);
+ hp_fini(results);
+ }
+
+ /* Discard hotplug information snapshot */
+ hp_fini(root);
+
+ return (rv);
+}
+
+/*
+ * parse_common()
+ *
+ * Parse command line options that are common to the
+ * entire program, and to each of its subcommands.
+ */
+static void
+parse_common(int argc, char *argv[], const char *usage_str)
+{
+ int opt;
+ extern int opterr;
+ extern int optind;
+
+ /* Turn off error reporting */
+ opterr = 0;
+
+ while ((opt = getopt_clip(argc, argv, "?V", common_opts, NULL)) != -1) {
+ switch (opt) {
+ case '?':
+ if (optopt == '?') {
+ usage(usage_str);
+ exit(0);
+ }
+ break;
+ case 'V':
+ (void) printf(gettext("%s: Version %s\n"),
+ prog, version);
+ exit(0);
+ default:
+ break;
+ }
+ }
+
+ /* Reset option index */
+ optind = 1;
+}
+
+/*
+ * parse_flags()
+ *
+ * Parse command line flags common to all downward state
+ * change operations (offline, disable, poweoff).
+ */
+static void
+parse_flags(int argc, char *argv[], int *flagsp, const char *usage_str)
+{
+ int opt;
+ int flags = 0;
+
+ while ((opt = getopt_clip(argc, argv, "fq", flag_opts, NULL)) != -1) {
+ switch (opt) {
+ case 'f':
+ flags |= HPFORCE;
+ break;
+ case 'q':
+ flags |= HPQUERY;
+ break;
+ default:
+ bad_option(opt, optopt, usage_str);
+ break;
+ }
+ }
+
+ *flagsp = flags;
+}
+
+/*
+ * parse_options()
+ *
+ * Parse command line options common to the bus private set and
+ * get subcommands.
+ */
+static void
+parse_options(int argc, char *argv[], char **optionsp, const char *usage_str)
+{
+ int opt;
+
+ while ((opt = getopt_clip(argc, argv, "o:", private_opts,
+ NULL)) != -1) {
+ switch (opt) {
+ case 'o':
+ *optionsp = optarg;
+ break;
+ default:
+ bad_option(opt, optopt, usage_str);
+ break;
+ }
+ }
+}
+
+/*
+ * parse_target()
+ *
+ * Parse the target path and connection name from the command line.
+ */
+static void
+parse_target(int argc, char *argv[], char **pathp, char **connectionp,
+ const char *usage_str)
+{
+ extern int optind;
+
+ if (optind < argc)
+ *pathp = argv[optind++];
+
+ if (optind < argc)
+ *connectionp = argv[optind++];
+
+ if (optind < argc) {
+ (void) fprintf(stderr, gettext("ERROR: too many arguments.\n"));
+ usage(usage_str);
+ exit(EINVAL);
+ }
+}
+
+/*
+ * bad_option()
+ *
+ * Routine to handle bad command line options.
+ */
+static void
+bad_option(int opt, int optopt, const char *usage_str)
+{
+ switch (opt) {
+ case ':':
+ (void) fprintf(stderr,
+ gettext("ERROR: option '%c' requires an argument.\n"),
+ optopt);
+ break;
+ default:
+ if (optopt == '?') {
+ usage(usage_str);
+ exit(EXIT_OK);
+ }
+ (void) fprintf(stderr,
+ gettext("ERROR: unrecognized option '%c'.\n"), optopt);
+ break;
+ }
+
+ usage(usage_str);
+
+ exit(EXIT_EINVAL);
+}
+
+/*
+ * usage()
+ *
+ * Display general usage of the command. Including
+ * the usage synopsis of each defined subcommand.
+ */
+static void
+usage(const char *usage_str)
+{
+ int i;
+
+ if (usage_str != NULL) {
+ (void) fprintf(stderr, gettext("Usage: %s %s\n\n"),
+ prog, usage_str);
+ return;
+ }
+
+ (void) fprintf(stderr, gettext("Usage: %s <subcommand> [<args>]\n\n"),
+ prog);
+
+ (void) fprintf(stderr, gettext("Subcommands:\n\n"));
+
+ for (i = 0; i < (sizeof (subcmds) / sizeof (subcmd_t)); i++)
+ (void) fprintf(stderr, " %s\n\n", subcmds[i].usage_str);
+}
+
+/*
+ * list_cb()
+ *
+ * Callback function for hp_traverse(), to display nodes
+ * of a hotplug information snapshot. (Short version.)
+ */
+/*ARGSUSED*/
+static int
+list_cb(hp_node_t node, void *arg)
+{
+ hp_node_t parent;
+
+ /* Indent */
+ for (parent = hp_parent(node); parent; parent = hp_parent(parent))
+ if (hp_type(parent) == HP_NODE_DEVICE)
+ (void) printf(" ");
+
+ switch (hp_type(node)) {
+ case HP_NODE_DEVICE:
+ (void) printf("%s\n", hp_name(node));
+ break;
+
+ case HP_NODE_CONNECTOR:
+ (void) printf("[%s]", hp_name(node));
+ (void) printf(" (%s)", state_itoa(hp_state(node)));
+ (void) printf("\n");
+ break;
+
+ case HP_NODE_PORT:
+ (void) printf("<%s>", hp_name(node));
+ (void) printf(" (%s)", state_itoa(hp_state(node)));
+ (void) printf("\n");
+ break;
+
+ case HP_NODE_USAGE:
+ (void) printf("{ %s }\n", hp_usage(node));
+ break;
+ }
+
+ return (HP_WALK_CONTINUE);
+}
+
+/*
+ * list_long_cb()
+ *
+ * Callback function for hp_traverse(), to display nodes
+ * of a hotplug information snapshot. (Long version.)
+ */
+/*ARGSUSED*/
+static int
+list_long_cb(hp_node_t node, void *arg)
+{
+ char path[MAXPATHLEN];
+ char connection[MAXPATHLEN];
+
+ if (hp_type(node) != HP_NODE_USAGE) {
+ if (hp_path(node, path, connection) != 0)
+ return (HP_WALK_CONTINUE);
+ (void) printf("%s", path);
+ }
+
+ switch (hp_type(node)) {
+ case HP_NODE_CONNECTOR:
+ (void) printf(" [%s]", connection);
+ (void) printf(" (%s)", state_itoa(hp_state(node)));
+ break;
+
+ case HP_NODE_PORT:
+ (void) printf(" <%s>", connection);
+ (void) printf(" (%s)", state_itoa(hp_state(node)));
+ break;
+
+ case HP_NODE_USAGE:
+ (void) printf(" { %s }", hp_usage(node));
+ break;
+ }
+
+ (void) printf("\n");
+
+ return (HP_WALK_CONTINUE);
+}
+
+/*
+ * error_cb()
+ *
+ * Callback function for hp_traverse(), to display
+ * error results from a state change operation.
+ */
+/*ARGSUSED*/
+static int
+error_cb(hp_node_t node, void *arg)
+{
+ hp_node_t child;
+ char *usage_str;
+ static char path[MAXPATHLEN];
+ static char connection[MAXPATHLEN];
+
+ if (((child = hp_child(node)) != NULL) &&
+ (hp_type(child) == HP_NODE_USAGE)) {
+ if (hp_path(node, path, connection) == 0)
+ (void) printf("%s:\n", path);
+ return (HP_WALK_CONTINUE);
+ }
+
+ if ((hp_type(node) == HP_NODE_USAGE) &&
+ ((usage_str = hp_usage(node)) != NULL))
+ (void) printf(" { %s }\n", usage_str);
+
+ return (HP_WALK_CONTINUE);
+}
+
+/*
+ * print_options()
+ *
+ * Parse and display bus private options. The options are
+ * formatted as a string which conforms to the getsubopt(3C)
+ * format. This routine only splits the string elements as
+ * separated by commas, and displays each portion on its own
+ * separate line of output.
+ */
+static void
+print_options(const char *options)
+{
+ char *buf, *curr, *next;
+ size_t len;
+
+ /* Do nothing if options string is empty */
+ if ((len = strlen(options)) == 0)
+ return;
+
+ /* To avoid modifying the input string, make a copy on the stack */
+ if ((buf = (char *)alloca(len + 1)) == NULL) {
+ (void) printf("%s\n", options);
+ return;
+ }
+ (void) strlcpy(buf, options, len + 1);
+
+ /* Iterate through each comma-separated name/value pair */
+ curr = buf;
+ do {
+ if ((next = strchr(curr, ',')) != NULL) {
+ *next = '\0';
+ next++;
+ }
+ (void) printf("%s\n", curr);
+ } while ((curr = next) != NULL);
+}
+
+/*
+ * print_error()
+ *
+ * Common routine to print error numbers in an appropriate way.
+ * Prints nothing if error code is 0.
+ */
+static void
+print_error(int error)
+{
+ switch (error) {
+ case 0:
+ /* No error */
+ return;
+ case EACCES:
+ (void) fprintf(stderr,
+ gettext("ERROR: operation not authorized.\n"));
+ break;
+ case EBADF:
+ (void) fprintf(stderr,
+ gettext("ERROR: hotplug service is not available.\n"));
+ break;
+ case EBUSY:
+ (void) fprintf(stderr,
+ gettext("ERROR: devices or resources are busy.\n"));
+ break;
+ case EEXIST:
+ (void) fprintf(stderr,
+ gettext("ERROR: resource already exists.\n"));
+ break;
+ case EFAULT:
+ (void) fprintf(stderr,
+ gettext("ERROR: internal failure in hotplug service.\n"));
+ break;
+ case EINVAL:
+ (void) fprintf(stderr,
+ gettext("ERROR: invalid arguments.\n"));
+ break;
+ case ENOENT:
+ (void) fprintf(stderr,
+ gettext("ERROR: there are no connections to display.\n"));
+ (void) fprintf(stderr,
+ gettext("(See hotplug(1m) for more information.)\n"));
+ break;
+ case ENXIO:
+ (void) fprintf(stderr,
+ gettext("ERROR: no such path or connection.\n"));
+ break;
+ case ENOMEM:
+ (void) fprintf(stderr,
+ gettext("ERROR: not enough memory.\n"));
+ break;
+ case ENOTSUP:
+ (void) fprintf(stderr,
+ gettext("ERROR: operation not supported.\n"));
+ break;
+ case EIO:
+ (void) fprintf(stderr,
+ gettext("ERROR: hardware or driver specific failure.\n"));
+ break;
+ default:
+ (void) fprintf(stderr, gettext("ERROR: operation failed: %s\n"),
+ strerror(error));
+ break;
+ }
+}
+
+/*
+ * state_atoi()
+ *
+ * Convert a hotplug state from a string to an integer.
+ */
+static int
+state_atoi(char *state)
+{
+ int i;
+
+ for (i = 0; hpstates[i].state_str != NULL; i++)
+ if (strcasecmp(state, hpstates[i].state_str) == 0)
+ return (hpstates[i].state);
+
+ return (-1);
+}
+
+/*
+ * state_itoa()
+ *
+ * Convert a hotplug state from an integer to a string.
+ */
+static char *
+state_itoa(int state)
+{
+ static char unknown[] = "UNKNOWN";
+ int i;
+
+ for (i = 0; hpstates[i].state_str != NULL; i++)
+ if (state == hpstates[i].state)
+ return (hpstates[i].state_str);
+
+ return (unknown);
+}
+
+/*
+ * valid_target()
+ *
+ * Check if a state is a valid target for a changestate command.
+ */
+static short
+valid_target(int state)
+{
+ int i;
+
+ for (i = 0; hpstates[i].state_str != NULL; i++)
+ if (state == hpstates[i].state)
+ return (hpstates[i].valid_target);
+
+ return (0);
+}
diff --git a/usr/src/cmd/hotplugd/Makefile b/usr/src/cmd/hotplugd/Makefile
new file mode 100644
index 0000000000..405d183d21
--- /dev/null
+++ b/usr/src/cmd/hotplugd/Makefile
@@ -0,0 +1,68 @@
+#
+# 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.
+#
+#
+# cmd/hotplugd/Makefile
+#
+
+PROG= hotplugd
+OBJS= hotplugd.o \
+ hotplugd_impl.o \
+ hotplugd_door.o \
+ hotplugd_info.o \
+ hotplugd_rcm.o
+SRCS= $(OBJS:.o=.c)
+SVCMETHOD= svc-hotplug
+MANIFEST= hotplug.xml
+
+include ../Makefile.cmd
+
+ROOTCMDDIR= $(ROOTLIB)
+ROOTMANIFESTDIR= $(ROOTSVCSYSTEM)
+$(ROOTMANIFEST) := FILEMODE= 444
+
+CPPFLAGS += -I$(SRC)/lib/libhotplug/common
+LDLIBS += -ldevinfo -lhotplug -lnvpair -lsecdb -lrcm -lbsm
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+$(PROG): $(OBJS)
+ $(LINK.c) -o $@ $(OBJS) $(LDLIBS)
+ $(POST_PROCESS)
+
+.PARALLEL: $(OBJS)
+
+install: all $(ROOTCMD) $(ROOTMANIFEST) $(ROOTSVCMETHOD)
+
+clean:
+ $(RM) $(PROG) $(OBJS) $(LLOBJS)
+
+check: $(CHKMANIFEST)
+ $(CSTYLE) -pP $(SRCS:%=%)
+
+lint: lint_SRCS
+
+include ../Makefile.targ
diff --git a/usr/src/cmd/hotplugd/hotplug.xml b/usr/src/cmd/hotplugd/hotplug.xml
new file mode 100644
index 0000000000..19b0ad5572
--- /dev/null
+++ b/usr/src/cmd/hotplugd/hotplug.xml
@@ -0,0 +1,96 @@
+<?xml version="1.0"?>
+<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
+<!--
+ 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.
+
+ NOTE: This service manifest is not editable; its contents will
+ be overwritten by package or patch operations, including
+ operating system upgrade. Make customizations in a different
+ file.
+-->
+
+<service_bundle type='manifest' name='SUNWcsr:hotplug'>
+
+<service
+ name='system/hotplug'
+ type='service'
+ version='1'>
+
+ <create_default_instance enabled='false' />
+
+ <single_instance/>
+
+ <dependency name='device'
+ type='service'
+ grouping='require_all'
+ restart_on='none'>
+ <service_fmri value='svc:/system/device/local' />
+ </dependency>
+
+ <dependency name='usr'
+ type='service'
+ grouping='require_all'
+ restart_on='none'>
+ <service_fmri value='svc:/system/filesystem/local' />
+ </dependency>
+
+ <exec_method
+ type='method'
+ name='start'
+ exec='/lib/svc/method/svc-hotplug'
+ timeout_seconds='30'>
+ <method_context>
+ <method_credential user='root' group='root' />
+ </method_context>
+ </exec_method>
+
+ <exec_method
+ type='method'
+ name='stop'
+ exec=':kill'
+ timeout_seconds='30'>
+ </exec_method>
+
+ <property_group name='general' type='framework'>
+ <propval name='action_authorization' type='astring'
+ value='solaris.smf.manage.hotplug' />
+ <propval name='value_authorization' type='astring'
+ value='solaris.smf.manage.hotplug' />
+ </property_group>
+
+ <stability value='Unstable' />
+
+ <template>
+ <common_name>
+ <loctext xml:lang='C'>
+ hotplug daemon
+ </loctext>
+ </common_name>
+ <documentation>
+ <manpage title='hotplugd' section='1M'
+ manpath='/usr/share/man' />
+ </documentation>
+ </template>
+</service>
+
+</service_bundle>
diff --git a/usr/src/cmd/hotplugd/hotplugd.c b/usr/src/cmd/hotplugd/hotplugd.c
new file mode 100644
index 0000000000..decd10e7b5
--- /dev/null
+++ b/usr/src/cmd/hotplugd/hotplugd.c
@@ -0,0 +1,405 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <strings.h>
+#include <syslog.h>
+#include <priv.h>
+#include <wait.h>
+#include <getopt.h>
+#include <synch.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <libhotplug.h>
+#include <libhotplug_impl.h>
+#include "hotplugd_impl.h"
+
+/*
+ * Define long options for command line.
+ */
+static const struct option lopts[] = {
+ { "help", no_argument, 0, '?' },
+ { "version", no_argument, 0, 'V' },
+ { "debug", no_argument, 0, 'd' },
+ { 0, 0, 0, 0 }
+};
+
+/*
+ * Local functions.
+ */
+static void usage(void);
+static boolean_t check_privileges(void);
+static int daemonize(void);
+static void init_signals(void);
+static void signal_handler(int signum);
+static void shutdown_daemon(void);
+
+/*
+ * Global variables.
+ */
+static char *prog;
+static char version[] = "1.0";
+static boolean_t log_flag = B_FALSE;
+static boolean_t debug_flag = B_FALSE;
+static boolean_t exit_flag = B_FALSE;
+static sema_t signal_sem;
+
+/*
+ * main()
+ *
+ * The hotplug daemon is designed to be a background daemon
+ * controlled by SMF. So by default it will daemonize and
+ * do some coordination with its parent process in order to
+ * indicate proper success or failure back to SMF. And all
+ * output will be sent to syslog.
+ *
+ * But if given the '-d' command line option, it will instead
+ * run in the foreground in a standalone, debug mode. Errors
+ * and additional debug messages will be printed to the controlling
+ * terminal instead of to syslog.
+ */
+int
+main(int argc, char *argv[])
+{
+ int opt;
+ int pfd;
+ int status;
+
+ if ((prog = strrchr(argv[0], '/')) == NULL)
+ prog = argv[0];
+ else
+ prog++;
+
+ /* Check privileges */
+ if (!check_privileges()) {
+ (void) fprintf(stderr, "Insufficient privileges. "
+ "(All privileges are required.)\n");
+ return (-1);
+ }
+
+ /* Process options */
+ while ((opt = getopt_clip(argc, argv, "dV?", lopts, NULL)) != -1) {
+ switch (opt) {
+ case 'd':
+ debug_flag = B_TRUE;
+ break;
+ case 'V':
+ (void) printf("%s: Version %s\n", prog, version);
+ return (0);
+ default:
+ if (optopt == '?') {
+ usage();
+ return (0);
+ }
+ (void) fprintf(stderr, "Unrecognized option '%c'.\n",
+ optopt);
+ usage();
+ return (-1);
+ }
+ }
+
+ /* Initialize semaphore for daemon shutdown */
+ if (sema_init(&signal_sem, 1, USYNC_THREAD, NULL) != 0)
+ exit(EXIT_FAILURE);
+
+ /* Initialize signal handling */
+ init_signals();
+
+ /* Daemonize, if not in DEBUG mode */
+ if (!debug_flag)
+ pfd = daemonize();
+
+ /* Initialize door service */
+ if (!door_server_init()) {
+ if (!debug_flag) {
+ status = EXIT_FAILURE;
+ (void) write(pfd, &status, sizeof (status));
+ (void) close(pfd);
+ }
+ exit(EXIT_FAILURE);
+ }
+
+ /* Daemon initialized */
+ if (!debug_flag) {
+ status = 0;
+ (void) write(pfd, &status, sizeof (status));
+ (void) close(pfd);
+ }
+
+ /* Note that daemon is running */
+ log_info("hotplug daemon started.\n");
+
+ /* Wait for shutdown signal */
+ while (!exit_flag)
+ (void) sema_wait(&signal_sem);
+
+ shutdown_daemon();
+ return (0);
+}
+
+/*
+ * usage()
+ *
+ * Print a brief usage synopsis for the command line options.
+ */
+static void
+usage(void)
+{
+ (void) printf("Usage: %s [-d]\n", prog);
+}
+
+/*
+ * check_privileges()
+ *
+ * Check if the current process has enough privileges
+ * to run the daemon. Note that all privileges are
+ * required in order for RCM interactions to work.
+ */
+static boolean_t
+check_privileges(void)
+{
+ priv_set_t *privset;
+ boolean_t rv = B_FALSE;
+
+ if ((privset = priv_allocset()) != NULL) {
+ if (getppriv(PRIV_EFFECTIVE, privset) == 0) {
+ rv = priv_isfullset(privset);
+ }
+ priv_freeset(privset);
+ }
+
+ return (rv);
+}
+
+/*
+ * daemonize()
+ *
+ * Fork the daemon process into the background, and detach from
+ * the controlling terminal. Setup a shared pipe that will later
+ * be used to report startup status to the parent process.
+ */
+static int
+daemonize(void)
+{
+ int status;
+ int pfds[2];
+ pid_t pid;
+ sigset_t set;
+ sigset_t oset;
+
+ /*
+ * Temporarily block all signals. They will remain blocked in
+ * the parent, but will be unblocked in the child once it has
+ * notified the parent of its startup status.
+ */
+ (void) sigfillset(&set);
+ (void) sigdelset(&set, SIGABRT);
+ (void) sigprocmask(SIG_BLOCK, &set, &oset);
+
+ /* Create the shared pipe */
+ if (pipe(pfds) == -1) {
+ log_err("Cannot create pipe (%s)\n", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ /* Fork the daemon process */
+ if ((pid = fork()) == -1) {
+ log_err("Cannot fork daemon process (%s)\n", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ /* Parent: waits for exit status from child. */
+ if (pid > 0) {
+ (void) close(pfds[1]);
+ if (read(pfds[0], &status, sizeof (status)) == sizeof (status))
+ _exit(status);
+ if ((waitpid(pid, &status, 0) == pid) && WIFEXITED(status))
+ _exit(WEXITSTATUS(status));
+ log_err("Failed to spawn daemon process.\n");
+ _exit(EXIT_FAILURE);
+ }
+
+ /* Child continues... */
+
+ (void) setsid();
+ (void) chdir("/");
+ (void) umask(CMASK);
+ (void) sigprocmask(SIG_SETMASK, &oset, NULL);
+ (void) close(pfds[0]);
+
+ /* Detach from controlling terminal */
+ (void) close(0);
+ (void) close(1);
+ (void) close(2);
+ (void) open("/dev/null", O_RDONLY);
+ (void) open("/dev/null", O_WRONLY);
+ (void) open("/dev/null", O_WRONLY);
+
+ /* Use syslog for future messages */
+ log_flag = B_TRUE;
+ openlog(prog, LOG_PID, LOG_DAEMON);
+
+ return (pfds[1]);
+}
+
+/*
+ * init_signals()
+ *
+ * Initialize signal handling.
+ */
+static void
+init_signals(void)
+{
+ struct sigaction act;
+ sigset_t set;
+
+ (void) sigfillset(&set);
+ (void) sigdelset(&set, SIGABRT);
+
+ (void) sigfillset(&act.sa_mask);
+ act.sa_handler = signal_handler;
+ act.sa_flags = 0;
+
+ (void) sigaction(SIGTERM, &act, NULL);
+ (void) sigaction(SIGHUP, &act, NULL);
+ (void) sigaction(SIGINT, &act, NULL);
+ (void) sigaction(SIGPIPE, &act, NULL);
+
+ (void) sigdelset(&set, SIGTERM);
+ (void) sigdelset(&set, SIGHUP);
+ (void) sigdelset(&set, SIGINT);
+ (void) sigdelset(&set, SIGPIPE);
+}
+
+/*
+ * signal_handler()
+ *
+ * Most signals cause the hotplug daemon to shut down.
+ * Shutdown is triggered using a semaphore to wake up
+ * the main thread for a clean exit.
+ *
+ * Except SIGPIPE is used to coordinate between the parent
+ * and child processes when the daemon first starts.
+ */
+static void
+signal_handler(int signum)
+{
+ log_info("Received signal %d.\n", signum);
+
+ switch (signum) {
+ case 0:
+ case SIGPIPE:
+ break;
+ default:
+ exit_flag = B_TRUE;
+ (void) sema_post(&signal_sem);
+ break;
+ }
+}
+
+/*
+ * shutdown_daemon()
+ *
+ * Perform a clean shutdown of the daemon.
+ */
+static void
+shutdown_daemon(void)
+{
+ log_info("Hotplug daemon shutting down.\n");
+
+ door_server_fini();
+
+ if (log_flag)
+ closelog();
+
+ (void) sema_destroy(&signal_sem);
+}
+
+/*
+ * log_err()
+ *
+ * Display an error message. Use syslog if in daemon
+ * mode, otherwise print to stderr when in debug mode.
+ */
+/*PRINTFLIKE1*/
+void
+log_err(char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ if (debug_flag || !log_flag)
+ (void) vfprintf(stderr, fmt, ap);
+ else
+ vsyslog(LOG_ERR, fmt, ap);
+ va_end(ap);
+}
+
+/*
+ * log_info()
+ *
+ * Display an information message. Use syslog if in daemon
+ * mode, otherwise print to stdout when in debug mode.
+ */
+/*PRINTFLIKE1*/
+void
+log_info(char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ if (debug_flag || !log_flag)
+ (void) vfprintf(stdout, fmt, ap);
+ else
+ vsyslog(LOG_INFO, fmt, ap);
+ va_end(ap);
+}
+
+/*
+ * dprintf()
+ *
+ * Print a debug tracing statement. Only works in debug
+ * mode, and always prints to stdout.
+ */
+/*PRINTFLIKE1*/
+void
+dprintf(char *fmt, ...)
+{
+ va_list ap;
+
+ if (debug_flag) {
+ va_start(ap, fmt);
+ (void) vprintf(fmt, ap);
+ va_end(ap);
+ }
+}
diff --git a/usr/src/cmd/hotplugd/hotplugd_door.c b/usr/src/cmd/hotplugd/hotplugd_door.c
new file mode 100644
index 0000000000..6d0ec7f26a
--- /dev/null
+++ b/usr/src/cmd/hotplugd/hotplugd_door.c
@@ -0,0 +1,760 @@
+/*
+ * 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <strings.h>
+#include <alloca.h>
+#include <door.h>
+#include <pthread.h>
+#include <synch.h>
+#include <pwd.h>
+#include <auth_list.h>
+#include <auth_attr.h>
+#include <bsm/adt.h>
+#include <bsm/adt_event.h>
+#include <sys/sunddi.h>
+#include <sys/ddi_hp.h>
+#include <libnvpair.h>
+#include <libhotplug.h>
+#include <libhotplug_impl.h>
+#include "hotplugd_impl.h"
+
+/*
+ * Buffer management for results.
+ */
+typedef struct i_buffer {
+ uint64_t seqnum;
+ char *buffer;
+ struct i_buffer *next;
+} i_buffer_t;
+
+static uint64_t buffer_seqnum = 1;
+static i_buffer_t *buffer_list = NULL;
+static pthread_mutex_t buffer_lock = PTHREAD_MUTEX_INITIALIZER;
+
+/*
+ * Door file descriptor.
+ */
+static int door_fd = -1;
+
+/*
+ * Function prototypes.
+ */
+static void door_server(void *, char *, size_t, door_desc_t *, uint_t);
+static int check_auth(ucred_t *, const char *);
+static int cmd_getinfo(nvlist_t *, nvlist_t **);
+static int cmd_changestate(nvlist_t *, nvlist_t **);
+static int cmd_private(hp_cmd_t, nvlist_t *, nvlist_t **);
+static void add_buffer(uint64_t, char *);
+static void free_buffer(uint64_t);
+static uint64_t get_seqnum(void);
+static char *state_str(int);
+static int audit_session(ucred_t *, adt_session_data_t **);
+static void audit_changestate(ucred_t *, char *, char *, char *, int, int,
+ int);
+static void audit_setprivate(ucred_t *, char *, char *, char *, char *,
+ int);
+
+/*
+ * door_server_init()
+ *
+ * Create the door file, and initialize the door server.
+ */
+boolean_t
+door_server_init(void)
+{
+ int fd;
+
+ /* Create the door file */
+ if ((fd = open(HOTPLUGD_DOOR, O_CREAT|O_EXCL|O_RDONLY, 0644)) == -1) {
+ if (errno == EEXIST) {
+ log_err("Door service is already running.\n");
+ } else {
+ log_err("Cannot open door file '%s': %s\n",
+ HOTPLUGD_DOOR, strerror(errno));
+ }
+ return (B_FALSE);
+ }
+ (void) close(fd);
+
+ /* Initialize the door service */
+ if ((door_fd = door_create(door_server, NULL,
+ DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) {
+ log_err("Cannot create door service: %s\n", strerror(errno));
+ return (B_FALSE);
+ }
+
+ /* Cleanup stale door associations */
+ (void) fdetach(HOTPLUGD_DOOR);
+
+ /* Associate door service with door file */
+ if (fattach(door_fd, HOTPLUGD_DOOR) != 0) {
+ log_err("Cannot attach to door file '%s': %s\n", HOTPLUGD_DOOR,
+ strerror(errno));
+ (void) door_revoke(door_fd);
+ (void) fdetach(HOTPLUGD_DOOR);
+ door_fd = -1;
+ return (B_FALSE);
+ }
+
+ return (B_TRUE);
+}
+
+/*
+ * door_server_fini()
+ *
+ * Terminate and cleanup the door server.
+ */
+void
+door_server_fini(void)
+{
+ if (door_fd != -1) {
+ (void) door_revoke(door_fd);
+ (void) fdetach(HOTPLUGD_DOOR);
+ }
+
+ (void) unlink(HOTPLUGD_DOOR);
+}
+
+/*
+ * door_server()
+ *
+ * This routine is the handler which responds to each door call.
+ * Each incoming door call is expected to send a packed nvlist
+ * of arguments which describe the requested action. And each
+ * response is sent back as a packed nvlist of results.
+ *
+ * Results are always allocated on the heap. A global list of
+ * allocated result buffers is managed, and each one is tracked
+ * by a unique sequence number. The final step in the protocol
+ * is for the caller to send a short response using the sequence
+ * number when the buffer can be released.
+ */
+/*ARGSUSED*/
+static void
+door_server(void *cookie, char *argp, size_t sz, door_desc_t *dp, uint_t ndesc)
+{
+ nvlist_t *args = NULL;
+ nvlist_t *results = NULL;
+ hp_cmd_t cmd;
+ int rv;
+
+ dprintf("Door call: cookie=%p, argp=%p, sz=%d\n", cookie, (void *)argp,
+ sz);
+
+ /* Special case to free a results buffer */
+ if (sz == sizeof (uint64_t)) {
+ free_buffer(*(uint64_t *)(uintptr_t)argp);
+ (void) door_return(NULL, 0, NULL, 0);
+ return;
+ }
+
+ /* Unpack the arguments nvlist */
+ if (nvlist_unpack(argp, sz, &args, 0) != 0) {
+ log_err("Cannot unpack door arguments.\n");
+ rv = EINVAL;
+ goto fail;
+ }
+
+ /* Extract the requested command */
+ if (nvlist_lookup_int32(args, HPD_CMD, (int32_t *)&cmd) != 0) {
+ log_err("Cannot decode door command.\n");
+ rv = EINVAL;
+ goto fail;
+ }
+
+ /* Implement the command */
+ switch (cmd) {
+ case HP_CMD_GETINFO:
+ rv = cmd_getinfo(args, &results);
+ break;
+ case HP_CMD_CHANGESTATE:
+ rv = cmd_changestate(args, &results);
+ break;
+ case HP_CMD_SETPRIVATE:
+ case HP_CMD_GETPRIVATE:
+ rv = cmd_private(cmd, args, &results);
+ break;
+ default:
+ rv = EINVAL;
+ break;
+ }
+
+ /* The arguments nvlist is no longer needed */
+ nvlist_free(args);
+ args = NULL;
+
+ /*
+ * If an nvlist was constructed for the results,
+ * then pack the results nvlist and return it.
+ */
+ if (results != NULL) {
+ uint64_t seqnum;
+ char *buf = NULL;
+ size_t len = 0;
+
+ /* Add a sequence number to the results */
+ seqnum = get_seqnum();
+ if (nvlist_add_uint64(results, HPD_SEQNUM, seqnum) != 0) {
+ log_err("Cannot add sequence number.\n");
+ rv = EFAULT;
+ goto fail;
+ }
+
+ /* Pack the results nvlist */
+ if (nvlist_pack(results, &buf, &len,
+ NV_ENCODE_NATIVE, 0) != 0) {
+ log_err("Cannot pack door results.\n");
+ rv = EFAULT;
+ goto fail;
+ }
+
+ /* Link results buffer into list */
+ add_buffer(seqnum, buf);
+
+ /* The results nvlist is no longer needed */
+ nvlist_free(results);
+
+ /* Return the results */
+ (void) door_return(buf, len, NULL, 0);
+ return;
+ }
+
+ /* Return result code (when no nvlist) */
+ (void) door_return((char *)&rv, sizeof (int), NULL, 0);
+ return;
+
+fail:
+ log_err("Door call failed (%s)\n", strerror(rv));
+ if (args != NULL)
+ nvlist_free(args);
+ if (results != NULL)
+ nvlist_free(results);
+ (void) door_return((char *)&rv, sizeof (int), NULL, 0);
+}
+
+/*
+ * check_auth()
+ *
+ * Perform an RBAC authorization check.
+ */
+static int
+check_auth(ucred_t *ucred, const char *auth)
+{
+ struct passwd pwd;
+ uid_t euid;
+ char buf[MAXPATHLEN];
+
+ euid = ucred_geteuid(ucred);
+
+ if ((getpwuid_r(euid, &pwd, buf, sizeof (buf)) == NULL) ||
+ (chkauthattr(auth, pwd.pw_name) == 0)) {
+ log_info("Unauthorized door call.\n");
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ * cmd_getinfo()
+ *
+ * Implements the door command to get a hotplug information snapshot.
+ */
+static int
+cmd_getinfo(nvlist_t *args, nvlist_t **resultsp)
+{
+ hp_node_t root;
+ nvlist_t *results;
+ char *path;
+ char *connection;
+ char *buf = NULL;
+ size_t len = 0;
+ uint_t flags;
+ int rv;
+
+ dprintf("cmd_getinfo:\n");
+
+ /* Get arguments */
+ if (nvlist_lookup_string(args, HPD_PATH, &path) != 0) {
+ dprintf("cmd_getinfo: invalid arguments.\n");
+ return (EINVAL);
+ }
+ if (nvlist_lookup_string(args, HPD_CONNECTION, &connection) != 0)
+ connection = NULL;
+ if (nvlist_lookup_uint32(args, HPD_FLAGS, (uint32_t *)&flags) != 0)
+ flags = 0;
+
+ /* Get and pack the requested snapshot */
+ if ((rv = getinfo(path, connection, flags, &root)) == 0) {
+ rv = hp_pack(root, &buf, &len);
+ hp_fini(root);
+ }
+ dprintf("cmd_getinfo: getinfo(): rv = %d, buf = %p.\n", rv,
+ (void *)buf);
+
+ /*
+ * If the above failed or there is no snapshot,
+ * then only return a status code.
+ */
+ if (rv != 0)
+ return (rv);
+ if (buf == NULL)
+ return (EFAULT);
+
+ /* Allocate nvlist for results */
+ if (nvlist_alloc(&results, NV_UNIQUE_NAME_TYPE, 0) != 0) {
+ dprintf("cmd_getinfo: nvlist_alloc() failed.\n");
+ free(buf);
+ return (ENOMEM);
+ }
+
+ /* Add snapshot and successful status to results */
+ if ((nvlist_add_int32(results, HPD_STATUS, 0) != 0) ||
+ (nvlist_add_byte_array(results, HPD_INFO,
+ (uchar_t *)buf, len) != 0)) {
+ dprintf("cmd_getinfo: nvlist add failure.\n");
+ nvlist_free(results);
+ free(buf);
+ return (ENOMEM);
+ }
+
+ /* Packed snapshot no longer needed */
+ free(buf);
+
+ /* Success */
+ *resultsp = results;
+ return (0);
+}
+
+/*
+ * cmd_changestate()
+ *
+ * Implements the door command to initate a state change operation.
+ *
+ * NOTE: requires 'modify' authorization.
+ */
+static int
+cmd_changestate(nvlist_t *args, nvlist_t **resultsp)
+{
+ hp_node_t root = NULL;
+ nvlist_t *results = NULL;
+ char *path, *connection;
+ ucred_t *uc = NULL;
+ uint_t flags;
+ int rv, state, old_state, status;
+
+ dprintf("cmd_changestate:\n");
+
+ /* Get arguments */
+ if ((nvlist_lookup_string(args, HPD_PATH, &path) != 0) ||
+ (nvlist_lookup_string(args, HPD_CONNECTION, &connection) != 0) ||
+ (nvlist_lookup_int32(args, HPD_STATE, &state) != 0)) {
+ dprintf("cmd_changestate: invalid arguments.\n");
+ return (EINVAL);
+ }
+ if (nvlist_lookup_uint32(args, HPD_FLAGS, (uint32_t *)&flags) != 0)
+ flags = 0;
+
+ /* Get caller's credentials */
+ if (door_ucred(&uc) != 0) {
+ log_err("Cannot get door credentials (%s)\n", strerror(errno));
+ return (EACCES);
+ }
+
+ /* Check authorization */
+ if (check_auth(uc, HP_MODIFY_AUTH) != 0) {
+ dprintf("cmd_changestate: access denied.\n");
+ audit_changestate(uc, HP_MODIFY_AUTH, path, connection,
+ state, -1, ADT_FAIL_VALUE_AUTH);
+ ucred_free(uc);
+ return (EACCES);
+ }
+
+ /* Perform the state change operation */
+ status = changestate(path, connection, state, flags, &old_state, &root);
+ dprintf("cmd_changestate: changestate() == %d\n", status);
+
+ /* Audit the operation */
+ audit_changestate(uc, HP_MODIFY_AUTH, path, connection, state,
+ old_state, status);
+
+ /* Caller's credentials no longer needed */
+ ucred_free(uc);
+
+ /*
+ * Pack the results into an nvlist if there is an error snapshot.
+ *
+ * If any error occurs while packing the results, the original
+ * error code from changestate() above is still returned.
+ */
+ if (root != NULL) {
+ char *buf = NULL;
+ size_t len = 0;
+
+ dprintf("cmd_changestate: results nvlist required.\n");
+
+ /* Pack and discard the error snapshot */
+ rv = hp_pack(root, &buf, &len);
+ hp_fini(root);
+ if (rv != 0) {
+ dprintf("cmd_changestate: hp_pack() failed (%s).\n",
+ strerror(rv));
+ return (status);
+ }
+
+ /* Allocate nvlist for results */
+ if (nvlist_alloc(&results, NV_UNIQUE_NAME_TYPE, 0) != 0) {
+ dprintf("cmd_changestate: nvlist_alloc() failed.\n");
+ free(buf);
+ return (status);
+ }
+
+ /* Add the results into the nvlist */
+ if ((nvlist_add_int32(results, HPD_STATUS, status) != 0) ||
+ (nvlist_add_byte_array(results, HPD_INFO, (uchar_t *)buf,
+ len) != 0)) {
+ dprintf("cmd_changestate: nvlist add failed.\n");
+ nvlist_free(results);
+ free(buf);
+ return (status);
+ }
+
+ *resultsp = results;
+ }
+
+ return (status);
+}
+
+/*
+ * cmd_private()
+ *
+ * Implementation of the door command to set or get bus private options.
+ *
+ * NOTE: requires 'modify' authorization for the 'set' command.
+ */
+static int
+cmd_private(hp_cmd_t cmd, nvlist_t *args, nvlist_t **resultsp)
+{
+ nvlist_t *results = NULL;
+ ucred_t *uc = NULL;
+ char *path, *connection, *options;
+ char *values = NULL;
+ int status;
+
+ dprintf("cmd_private:\n");
+
+ /* Get caller's credentials */
+ if ((cmd == HP_CMD_SETPRIVATE) && (door_ucred(&uc) != 0)) {
+ log_err("Cannot get door credentials (%s)\n", strerror(errno));
+ return (EACCES);
+ }
+
+ /* Get arguments */
+ if ((nvlist_lookup_string(args, HPD_PATH, &path) != 0) ||
+ (nvlist_lookup_string(args, HPD_CONNECTION, &connection) != 0) ||
+ (nvlist_lookup_string(args, HPD_OPTIONS, &options) != 0)) {
+ dprintf("cmd_private: invalid arguments.\n");
+ return (EINVAL);
+ }
+
+ /* Check authorization */
+ if ((cmd == HP_CMD_SETPRIVATE) &&
+ (check_auth(uc, HP_MODIFY_AUTH) != 0)) {
+ dprintf("cmd_private: access denied.\n");
+ audit_setprivate(uc, HP_MODIFY_AUTH, path, connection, options,
+ ADT_FAIL_VALUE_AUTH);
+ ucred_free(uc);
+ return (EACCES);
+ }
+
+ /* Perform the operation */
+ status = private_options(path, connection, cmd, options, &values);
+ dprintf("cmd_private: private_options() == %d\n", status);
+
+ /* Audit the operation */
+ if (cmd == HP_CMD_SETPRIVATE) {
+ audit_setprivate(uc, HP_MODIFY_AUTH, path, connection, options,
+ status);
+ ucred_free(uc);
+ }
+
+ /* Construct an nvlist if values were returned */
+ if (values != NULL) {
+
+ /* Allocate nvlist for results */
+ if (nvlist_alloc(&results, NV_UNIQUE_NAME_TYPE, 0) != 0) {
+ dprintf("cmd_private: nvlist_alloc() failed.\n");
+ free(values);
+ return (ENOMEM);
+ }
+
+ /* Add values and status to the results */
+ if ((nvlist_add_int32(results, HPD_STATUS, status) != 0) ||
+ (nvlist_add_string(results, HPD_OPTIONS, values) != 0)) {
+ dprintf("cmd_private: nvlist add failed.\n");
+ nvlist_free(results);
+ free(values);
+ return (ENOMEM);
+ }
+
+ /* The values string is no longer needed */
+ free(values);
+
+ *resultsp = results;
+ }
+
+ return (status);
+}
+
+/*
+ * get_seqnum()
+ *
+ * Allocate the next unique sequence number for a results buffer.
+ */
+static uint64_t
+get_seqnum(void)
+{
+ uint64_t seqnum;
+
+ (void) pthread_mutex_lock(&buffer_lock);
+
+ seqnum = buffer_seqnum++;
+
+ (void) pthread_mutex_unlock(&buffer_lock);
+
+ return (seqnum);
+}
+
+/*
+ * add_buffer()
+ *
+ * Link a results buffer into the list containing all buffers.
+ */
+static void
+add_buffer(uint64_t seqnum, char *buf)
+{
+ i_buffer_t *node;
+
+ if ((node = (i_buffer_t *)malloc(sizeof (i_buffer_t))) == NULL) {
+ /* The consequence is a memory leak. */
+ log_err("Cannot allocate results buffer: %s\n",
+ strerror(errno));
+ return;
+ }
+
+ node->seqnum = seqnum;
+ node->buffer = buf;
+
+ (void) pthread_mutex_lock(&buffer_lock);
+
+ node->next = buffer_list;
+ buffer_list = node;
+
+ (void) pthread_mutex_unlock(&buffer_lock);
+}
+
+/*
+ * free_buffer()
+ *
+ * Remove a results buffer from the list containing all buffers.
+ */
+static void
+free_buffer(uint64_t seqnum)
+{
+ i_buffer_t *node, *prev;
+
+ (void) pthread_mutex_lock(&buffer_lock);
+
+ prev = NULL;
+ node = buffer_list;
+
+ while (node) {
+ if (node->seqnum == seqnum) {
+ dprintf("Free buffer %lld\n", seqnum);
+ if (prev) {
+ prev->next = node->next;
+ } else {
+ buffer_list = node->next;
+ }
+ free(node->buffer);
+ free(node);
+ break;
+ }
+ prev = node;
+ node = node->next;
+ }
+
+ (void) pthread_mutex_unlock(&buffer_lock);
+}
+
+/*
+ * audit_session()
+ *
+ * Initialize an audit session.
+ */
+static int
+audit_session(ucred_t *ucred, adt_session_data_t **sessionp)
+{
+ adt_session_data_t *session;
+
+ if (adt_start_session(&session, NULL, 0) != 0) {
+ log_err("Cannot start audit session.\n");
+ return (-1);
+ }
+
+ if (adt_set_from_ucred(session, ucred, ADT_NEW) != 0) {
+ log_err("Cannot set audit session from ucred.\n");
+ (void) adt_end_session(session);
+ return (-1);
+ }
+
+ *sessionp = session;
+ return (0);
+}
+
+/*
+ * audit_changestate()
+ *
+ * Audit a 'changestate' door command.
+ */
+static void
+audit_changestate(ucred_t *ucred, char *auth, char *path, char *connection,
+ int new_state, int old_state, int result)
+{
+ adt_session_data_t *session;
+ adt_event_data_t *event;
+ int pass_fail, fail_reason;
+
+ if (audit_session(ucred, &session) != 0)
+ return;
+
+ if ((event = adt_alloc_event(session, ADT_hotplug_state)) == NULL) {
+ (void) adt_end_session(session);
+ return;
+ }
+
+ if (result == 0) {
+ pass_fail = ADT_SUCCESS;
+ fail_reason = ADT_SUCCESS;
+ } else {
+ pass_fail = ADT_FAILURE;
+ fail_reason = result;
+ }
+
+ event->adt_hotplug_state.auth_used = auth;
+ event->adt_hotplug_state.device_path = path;
+ event->adt_hotplug_state.connection = connection;
+ event->adt_hotplug_state.new_state = state_str(new_state);
+ event->adt_hotplug_state.old_state = state_str(old_state);
+
+ /* Put the event */
+ if (adt_put_event(event, pass_fail, fail_reason) != 0)
+ log_err("Cannot put audit event.\n");
+
+ adt_free_event(event);
+ (void) adt_end_session(session);
+}
+
+/*
+ * audit_setprivate()
+ *
+ * Audit a 'set private' door command.
+ */
+static void
+audit_setprivate(ucred_t *ucred, char *auth, char *path, char *connection,
+ char *options, int result)
+{
+ adt_session_data_t *session;
+ adt_event_data_t *event;
+ int pass_fail, fail_reason;
+
+ if (audit_session(ucred, &session) != 0)
+ return;
+
+ if ((event = adt_alloc_event(session, ADT_hotplug_set)) == NULL) {
+ (void) adt_end_session(session);
+ return;
+ }
+
+ if (result == 0) {
+ pass_fail = ADT_SUCCESS;
+ fail_reason = ADT_SUCCESS;
+ } else {
+ pass_fail = ADT_FAILURE;
+ fail_reason = result;
+ }
+
+ event->adt_hotplug_set.auth_used = auth;
+ event->adt_hotplug_set.device_path = path;
+ event->adt_hotplug_set.connection = connection;
+ event->adt_hotplug_set.options = options;
+
+ /* Put the event */
+ if (adt_put_event(event, pass_fail, fail_reason) != 0)
+ log_err("Cannot put audit event.\n");
+
+ adt_free_event(event);
+ (void) adt_end_session(session);
+}
+
+/*
+ * state_str()
+ *
+ * Convert a state from integer to string.
+ */
+static char *
+state_str(int state)
+{
+ switch (state) {
+ case DDI_HP_CN_STATE_EMPTY:
+ return ("EMPTY");
+ case DDI_HP_CN_STATE_PRESENT:
+ return ("PRESENT");
+ case DDI_HP_CN_STATE_POWERED:
+ return ("POWERED");
+ case DDI_HP_CN_STATE_ENABLED:
+ return ("ENABLED");
+ case DDI_HP_CN_STATE_PORT_EMPTY:
+ return ("PORT-EMPTY");
+ case DDI_HP_CN_STATE_PORT_PRESENT:
+ return ("PORT-PRESENT");
+ case DDI_HP_CN_STATE_OFFLINE:
+ return ("OFFLINE");
+ case DDI_HP_CN_STATE_ATTACHED:
+ return ("ATTACHED");
+ case DDI_HP_CN_STATE_MAINTENANCE:
+ return ("MAINTENANCE");
+ case DDI_HP_CN_STATE_ONLINE:
+ return ("ONLINE");
+ default:
+ return ("UNKNOWN");
+ }
+}
diff --git a/usr/src/cmd/hotplugd/hotplugd_impl.c b/usr/src/cmd/hotplugd/hotplugd_impl.c
new file mode 100644
index 0000000000..245314d6fe
--- /dev/null
+++ b/usr/src/cmd/hotplugd/hotplugd_impl.c
@@ -0,0 +1,435 @@
+/*
+ * 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <pthread.h>
+#include <alloca.h>
+#include <libnvpair.h>
+#include <libhotplug.h>
+#include <libhotplug_impl.h>
+#include <sys/types.h>
+#include <sys/sunddi.h>
+#include <sys/ddi_hp.h>
+#include <sys/modctl.h>
+#include "hotplugd_impl.h"
+
+/*
+ * All operations affecting kernel state are serialized.
+ */
+static pthread_mutex_t hotplug_lock = PTHREAD_MUTEX_INITIALIZER;
+
+/*
+ * Local functions.
+ */
+static boolean_t check_rcm_required(hp_node_t, int);
+static int pack_properties(const char *, ddi_hp_property_t *);
+static void unpack_properties(ddi_hp_property_t *, char **);
+static void free_properties(ddi_hp_property_t *);
+
+/*
+ * changestate()
+ *
+ * Perform a state change operation.
+ *
+ * NOTE: all operations are serialized, using a global lock.
+ */
+int
+changestate(const char *path, const char *connection, int state, uint_t flags,
+ int *old_statep, hp_node_t *resultsp)
+{
+ hp_node_t root = NULL;
+ char **rsrcs = NULL;
+ boolean_t use_rcm = B_FALSE;
+ int rv;
+
+ dprintf("changestate(path=%s, connection=%s, state=0x%x, flags=0x%x)\n",
+ path, connection, state, flags);
+
+ /* Initialize results */
+ *resultsp = NULL;
+ *old_statep = -1;
+
+ (void) pthread_mutex_lock(&hotplug_lock);
+
+ /* Get an information snapshot, without usage details */
+ if ((rv = getinfo(path, connection, 0, &root)) != 0) {
+ (void) pthread_mutex_unlock(&hotplug_lock);
+ dprintf("changestate: getinfo() failed (%s)\n", strerror(rv));
+ return (rv);
+ }
+
+ /* Record current state (used in hotplugd_door.c for auditing) */
+ *old_statep = hp_state(root);
+
+ /* Check if RCM interactions are required */
+ use_rcm = check_rcm_required(root, state);
+
+ /* If RCM is required, perform RCM offline */
+ if (use_rcm) {
+
+ dprintf("changestate: RCM offline is required.\n");
+
+ /* Get RCM resources */
+ if ((rv = rcm_resources(root, &rsrcs)) != 0) {
+ dprintf("changestate: rcm_resources() failed.\n");
+ (void) pthread_mutex_unlock(&hotplug_lock);
+ hp_fini(root);
+ return (rv);
+ }
+
+ /* Request RCM offline */
+ if ((rsrcs != NULL) &&
+ ((rv = rcm_offline(rsrcs, flags, root)) != 0)) {
+ dprintf("changestate: rcm_offline() failed.\n");
+ rcm_online(rsrcs);
+ (void) pthread_mutex_unlock(&hotplug_lock);
+ free_rcm_resources(rsrcs);
+ *resultsp = root;
+ return (rv);
+ }
+ }
+
+ /* The information snapshot is no longer needed */
+ hp_fini(root);
+
+ /* Stop now if QUERY flag was specified */
+ if (flags & HPQUERY) {
+ dprintf("changestate: operation was QUERY only.\n");
+ rcm_online(rsrcs);
+ (void) pthread_mutex_unlock(&hotplug_lock);
+ free_rcm_resources(rsrcs);
+ return (0);
+ }
+
+ /* Do state change in kernel */
+ rv = 0;
+ if (modctl(MODHPOPS, MODHPOPS_CHANGE_STATE, path, connection, state))
+ rv = errno;
+ dprintf("changestate: modctl(MODHPOPS_CHANGE_STATE) = %d.\n", rv);
+
+ /*
+ * If RCM is required, then perform an RCM online or RCM remove
+ * operation. Which depends upon if modctl succeeded or failed.
+ */
+ if (use_rcm && (rsrcs != NULL)) {
+
+ /* RCM online if failure, or RCM remove if successful */
+ if (rv == 0)
+ rcm_remove(rsrcs);
+ else
+ rcm_online(rsrcs);
+
+ /* RCM resources no longer required */
+ free_rcm_resources(rsrcs);
+ }
+
+ (void) pthread_mutex_unlock(&hotplug_lock);
+
+ *resultsp = NULL;
+ return (rv);
+}
+
+/*
+ * private_options()
+ *
+ * Implement set/get of bus private options.
+ */
+int
+private_options(const char *path, const char *connection, hp_cmd_t cmd,
+ const char *options, char **resultsp)
+{
+ ddi_hp_property_t prop;
+ ddi_hp_property_t results;
+ char *values = NULL;
+ int rv;
+
+ dprintf("private_options(path=%s, connection=%s, options='%s')\n",
+ path, connection, options);
+
+ /* Initialize property arguments */
+ if ((rv = pack_properties(options, &prop)) != 0) {
+ dprintf("private_options: failed to pack properties.\n");
+ return (rv);
+ }
+
+ /* Initialize results */
+ (void) memset(&results, 0, sizeof (ddi_hp_property_t));
+ results.buf_size = HP_PRIVATE_BUF_SZ;
+ results.nvlist_buf = (char *)calloc(1, HP_PRIVATE_BUF_SZ);
+ if (results.nvlist_buf == NULL) {
+ dprintf("private_options: failed to allocate buffer.\n");
+ free_properties(&prop);
+ return (ENOMEM);
+ }
+
+ /* Lock hotplug */
+ (void) pthread_mutex_lock(&hotplug_lock);
+
+ /* Perform the command */
+ rv = 0;
+ if (cmd == HP_CMD_GETPRIVATE) {
+ if (modctl(MODHPOPS, MODHPOPS_BUS_GET, path, connection,
+ &prop, &results))
+ rv = errno;
+ dprintf("private_options: modctl(MODHPOPS_BUS_GET) = %d\n", rv);
+ } else {
+ if (modctl(MODHPOPS, MODHPOPS_BUS_SET, path, connection,
+ &prop, &results))
+ rv = errno;
+ dprintf("private_options: modctl(MODHPOPS_BUS_SET) = %d\n", rv);
+ }
+
+ /* Unlock hotplug */
+ (void) pthread_mutex_unlock(&hotplug_lock);
+
+ /* Parse results */
+ if (rv == 0) {
+ unpack_properties(&results, &values);
+ *resultsp = values;
+ }
+
+ /* Cleanup */
+ free_properties(&prop);
+ free_properties(&results);
+
+ return (rv);
+}
+
+/*
+ * check_rcm_required()
+ *
+ * Given the root of a changestate operation and the target
+ * state, determine if RCM interactions will be required.
+ */
+static boolean_t
+check_rcm_required(hp_node_t root, int target_state)
+{
+ /*
+ * RCM is required when transitioning an ENABLED
+ * connector to a non-ENABLED state.
+ */
+ if ((root->hp_type == HP_NODE_CONNECTOR) &&
+ HP_IS_ENABLED(root->hp_state) && !HP_IS_ENABLED(target_state))
+ return (B_TRUE);
+
+ /*
+ * RCM is required when transitioning an OPERATIONAL
+ * port to a non-OPERATIONAL state.
+ */
+ if ((root->hp_type == HP_NODE_PORT) &&
+ HP_IS_ONLINE(root->hp_state) && HP_IS_OFFLINE(target_state))
+ return (B_TRUE);
+
+ /* RCM is not required in other cases */
+ return (B_FALSE);
+}
+
+/*
+ * pack_properties()
+ *
+ * Given a specified set/get command and an options string,
+ * construct the structure containing a packed nvlist that
+ * contains the specified options.
+ */
+static int
+pack_properties(const char *options, ddi_hp_property_t *prop)
+{
+ nvlist_t *nvl;
+ char *buf, *tmp, *name, *value, *next;
+ size_t len;
+
+ /* Initialize results */
+ (void) memset(prop, 0, sizeof (ddi_hp_property_t));
+
+ /* Do nothing if options string is empty */
+ if ((len = strlen(options)) == 0) {
+ dprintf("pack_properties: options string is empty.\n");
+ return (ENOENT);
+ }
+
+ /* Avoid modifying the input string by using a copy on the stack */
+ if ((tmp = (char *)alloca(len + 1)) == NULL) {
+ log_err("Failed to allocate buffer for private options.\n");
+ return (ENOMEM);
+ }
+ (void) strlcpy(tmp, options, len + 1);
+
+ /* Allocate the nvlist */
+ if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) {
+ log_err("Failed to allocate private options nvlist.\n");
+ return (ENOMEM);
+ }
+
+ /* Add each option from the string */
+ for (name = tmp; name != NULL; name = next) {
+
+ /* Isolate current name/value, and locate the next */
+ if ((next = strchr(name, ',')) != NULL) {
+ *next = '\0';
+ next++;
+ }
+
+ /* Split current name/value pair */
+ if ((value = strchr(name, '=')) != NULL) {
+ *value = '\0';
+ value++;
+ } else {
+ value = "";
+ }
+
+ /* Add the option to the nvlist */
+ if (nvlist_add_string(nvl, name, value) != 0) {
+ log_err("Failed to add private option to nvlist.\n");
+ nvlist_free(nvl);
+ return (EFAULT);
+ }
+ }
+
+ /* Pack the nvlist */
+ len = 0;
+ buf = NULL;
+ if (nvlist_pack(nvl, &buf, &len, NV_ENCODE_NATIVE, 0) != 0) {
+ log_err("Failed to pack private options nvlist.\n");
+ nvlist_free(nvl);
+ return (EFAULT);
+ }
+
+ /* Save results */
+ prop->nvlist_buf = buf;
+ prop->buf_size = len;
+
+ /* The nvlist is no longer needed */
+ nvlist_free(nvl);
+
+ return (0);
+}
+
+/*
+ * unpack_properties()
+ *
+ * Given a structure possibly containing a packed nvlist of
+ * bus private options, unpack the nvlist and expand its
+ * contents into an options string.
+ */
+static void
+unpack_properties(ddi_hp_property_t *prop, char **optionsp)
+{
+ nvlist_t *nvl = NULL;
+ nvpair_t *nvp;
+ boolean_t first_flag;
+ char *name, *value, *options;
+ size_t len;
+
+ /* Initialize results */
+ *optionsp = NULL;
+
+ /* Do nothing if properties do not exist */
+ if ((prop->nvlist_buf == NULL) || (prop->buf_size == 0)) {
+ dprintf("unpack_properties: no properties exist.\n");
+ return;
+ }
+
+ /* Unpack the nvlist */
+ if (nvlist_unpack(prop->nvlist_buf, prop->buf_size, &nvl, 0) != 0) {
+ log_err("Failed to unpack private options nvlist.\n");
+ return;
+ }
+
+ /* Compute the size of the options string */
+ for (len = 0, nvp = NULL; nvp = nvlist_next_nvpair(nvl, nvp); ) {
+
+ name = nvpair_name(nvp);
+
+ /* Skip the command, and anything not a string */
+ if ((strcmp(name, "cmd") == 0) ||
+ (nvpair_type(nvp) != DATA_TYPE_STRING))
+ continue;
+
+ (void) nvpair_value_string(nvp, &value);
+
+ /* Account for '=' signs, commas, and terminating NULL */
+ len += (strlen(name) + strlen(value) + 2);
+ }
+
+ /* Allocate the resulting options string */
+ if ((options = (char *)calloc(len, sizeof (char))) == NULL) {
+ log_err("Failed to allocate private options string.\n");
+ nvlist_free(nvl);
+ return;
+ }
+
+ /* Copy name/value pairs into the options string */
+ first_flag = B_TRUE;
+ for (nvp = NULL; nvp = nvlist_next_nvpair(nvl, nvp); ) {
+
+ name = nvpair_name(nvp);
+
+ /* Skip the command, and anything not a string */
+ if ((strcmp(name, "cmd") == 0) ||
+ (nvpair_type(nvp) != DATA_TYPE_STRING))
+ continue;
+
+ if (!first_flag)
+ (void) strlcat(options, ",", len);
+
+ (void) strlcat(options, name, len);
+
+ (void) nvpair_value_string(nvp, &value);
+
+ if (strlen(value) > 0) {
+ (void) strlcat(options, "=", len);
+ (void) strlcat(options, value, len);
+ }
+
+ first_flag = B_FALSE;
+ }
+
+ /* The unpacked nvlist is no longer needed */
+ nvlist_free(nvl);
+
+ /* Save results */
+ *optionsp = options;
+}
+
+/*
+ * free_properties()
+ *
+ * Destroy a structure containing a packed nvlist of bus
+ * private properties.
+ */
+static void
+free_properties(ddi_hp_property_t *prop)
+{
+ if (prop) {
+ if (prop->nvlist_buf)
+ free(prop->nvlist_buf);
+ (void) memset(prop, 0, sizeof (ddi_hp_property_t));
+ }
+}
diff --git a/usr/src/cmd/hotplugd/hotplugd_impl.h b/usr/src/cmd/hotplugd/hotplugd_impl.h
new file mode 100644
index 0000000000..5446a4dc13
--- /dev/null
+++ b/usr/src/cmd/hotplugd/hotplugd_impl.h
@@ -0,0 +1,81 @@
+/*
+ * 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 _HOTPLUGD_IMPL_H
+#define _HOTPLUGD_IMPL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Define macros to test connection states.
+ */
+#define HP_IS_ENABLED(s) (s == DDI_HP_CN_STATE_ENABLED)
+
+#define HP_IS_ONLINE(s) ((s == DDI_HP_CN_STATE_ONLINE) || \
+ (s == DDI_HP_CN_STATE_MAINTENANCE))
+
+#define HP_IS_OFFLINE(s) ((s == DDI_HP_CN_STATE_PORT_EMPTY) || \
+ (s == DDI_HP_CN_STATE_PORT_PRESENT) || \
+ (s == DDI_HP_CN_STATE_OFFLINE))
+
+/*
+ * Define size of nvlist buffer for set/get commands.
+ */
+#define HP_PRIVATE_BUF_SZ 4096
+
+/*
+ * Define a string for parsing /devices paths.
+ */
+#define S_DEVICES "/devices"
+
+/*
+ * Global functions.
+ */
+void log_err(char *fmt, ...);
+void log_info(char *fmt, ...);
+void dprintf(char *fmt, ...);
+boolean_t door_server_init(void);
+void door_server_fini(void);
+int getinfo(const char *path, const char *connection, uint_t flags,
+ hp_node_t *rootp);
+int changestate(const char *path, const char *connection, int state,
+ uint_t flags, int *old_statep, hp_node_t *resultsp);
+int private_options(const char *path, const char *connection,
+ hp_cmd_t cmd, const char *options, char **resultsp);
+int copy_usage(hp_node_t root);
+int rcm_resources(hp_node_t root, char ***rsrcsp);
+void free_rcm_resources(char **rsrcs);
+int rcm_offline(char **rsrcs, uint_t flags, hp_node_t root);
+void rcm_online(char **rsrcs);
+void rcm_remove(char **rsrcs);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _HOTPLUGD_IMPL_H */
diff --git a/usr/src/cmd/hotplugd/hotplugd_info.c b/usr/src/cmd/hotplugd/hotplugd_info.c
new file mode 100644
index 0000000000..2daaad6e91
--- /dev/null
+++ b/usr/src/cmd/hotplugd/hotplugd_info.c
@@ -0,0 +1,513 @@
+/*
+ * 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libdevinfo.h>
+#include <libhotplug.h>
+#include <libhotplug_impl.h>
+#include <sys/sunddi.h>
+#include <sys/ddi_hp.h>
+#include "hotplugd_impl.h"
+
+/*
+ * Define a list of hotplug nodes.
+ * (Only used within this module.)
+ */
+typedef struct {
+ hp_node_t head;
+ hp_node_t prev;
+} hp_node_list_t;
+
+/*
+ * Local functions.
+ */
+static int copy_devinfo(const char *, const char *, uint_t,
+ hp_node_t *);
+static int copy_devices(hp_node_t, di_node_t, uint_t, hp_node_t *);
+static int copy_hotplug(hp_node_t, di_node_t, const char *, uint_t,
+ hp_node_t *);
+static char *base_path(const char *);
+static int search_cb(di_node_t, void *);
+static int check_search(di_node_t, uint_t);
+static hp_node_t new_device_node(hp_node_t, di_node_t);
+static hp_node_t new_hotplug_node(hp_node_t, di_hp_t);
+static void node_list_add(hp_node_list_t *, hp_node_t);
+
+/*
+ * getinfo()
+ *
+ * Build a hotplug information snapshot. The path, connection,
+ * and flags indicate what information should be included.
+ */
+int
+getinfo(const char *path, const char *connection, uint_t flags, hp_node_t *retp)
+{
+ hp_node_t root = NULL;
+ hp_node_t child;
+ char *basepath;
+ int rv;
+
+ if ((path == NULL) || (retp == NULL))
+ return (EINVAL);
+
+ dprintf("getinfo: path=%s, connection=%s, flags=0x%x\n", path,
+ (connection == NULL) ? "NULL" : connection, flags);
+
+ /* Allocate the base path */
+ if ((basepath = base_path(path)) == NULL)
+ return (ENOMEM);
+
+ /* Copy in device and hotplug nodes from libdevinfo */
+ if ((rv = copy_devinfo(basepath, connection, flags, &root)) != 0) {
+ hp_fini(root);
+ free(basepath);
+ return (rv);
+ }
+
+ /* Check if there were no connections */
+ if (root == NULL) {
+ dprintf("getinfo: no hotplug connections.\n");
+ free(basepath);
+ return (ENOENT);
+ }
+
+ /* Special case: exclude root nexus from snapshot */
+ if (strcmp(basepath, "/") == 0) {
+ child = root->hp_child;
+ if (root->hp_name != NULL)
+ free(root->hp_name);
+ free(root);
+ root = child;
+ for (child = root; child; child = child->hp_sibling)
+ child->hp_parent = NULL;
+ }
+
+ /* Store a pointer to the base path in each root node */
+ for (child = root; child != NULL; child = child->hp_sibling)
+ child->hp_basepath = basepath;
+
+ /* Copy in usage information from RCM */
+ if (flags & HPINFOUSAGE) {
+ if ((rv = copy_usage(root)) != 0) {
+ (void) hp_fini(root);
+ return (rv);
+ }
+ }
+
+ *retp = root;
+ return (0);
+}
+
+/*
+ * copy_devinfo()
+ *
+ * Copy information about device and hotplug nodes from libdevinfo.
+ *
+ * When path is set to "/", the results need to be limited only to
+ * branches that contain hotplug information. An initial search
+ * is performed to mark which branches contain hotplug nodes.
+ */
+static int
+copy_devinfo(const char *path, const char *connection, uint_t flags,
+ hp_node_t *rootp)
+{
+ hp_node_t hp_root = NULL;
+ di_node_t di_root;
+ int rv;
+
+ /* Get libdevinfo snapshot */
+ if ((di_root = di_init(path, DINFOSUBTREE | DINFOHP)) == DI_NODE_NIL)
+ return (errno);
+
+ /* Do initial search pass, if required */
+ if (strcmp(path, "/") == 0) {
+ flags |= HPINFOSEARCH;
+ (void) di_walk_node(di_root, DI_WALK_CLDFIRST, NULL, search_cb);
+ }
+
+ /*
+ * If a connection is specified, just copy immediate hotplug info.
+ * Else, copy the device tree normally.
+ */
+ if (connection != NULL)
+ rv = copy_hotplug(NULL, di_root, connection, flags, &hp_root);
+ else
+ rv = copy_devices(NULL, di_root, flags, &hp_root);
+
+ /* Destroy devinfo snapshot */
+ di_fini(di_root);
+
+ *rootp = (rv == 0) ? hp_root : NULL;
+ return (rv);
+}
+
+/*
+ * copy_devices()
+ *
+ * Copy a full branch of device nodes. Used by copy_devinfo() and
+ * copy_hotplug().
+ */
+static int
+copy_devices(hp_node_t parent, di_node_t dev, uint_t flags, hp_node_t *rootp)
+{
+ hp_node_list_t children;
+ hp_node_t self, branch;
+ di_node_t child;
+ int rv = 0;
+
+ /* Initialize results */
+ *rootp = NULL;
+
+ /* Enforce search semantics */
+ if (check_search(dev, flags) == 0)
+ return (0);
+
+ /* Allocate new node for current device */
+ if ((self = new_device_node(parent, dev)) == NULL)
+ return (ENOMEM);
+
+ /*
+ * If the device has hotplug nodes, then use copy_hotplug()
+ * instead to build the branch associated with current device.
+ */
+ if (di_hp_next(dev, DI_HP_NIL) != DI_HP_NIL) {
+ if ((rv = copy_hotplug(self, dev, NULL, flags,
+ &self->hp_child)) != 0) {
+ free(self);
+ return (rv);
+ }
+ *rootp = self;
+ return (0);
+ }
+
+ /*
+ * The device does not have hotplug nodes. Use normal
+ * approach of iterating through its child device nodes.
+ */
+ (void) memset(&children, 0, sizeof (hp_node_list_t));
+ for (child = di_child_node(dev); child != DI_NODE_NIL;
+ child = di_sibling_node(child)) {
+ branch = NULL;
+ if ((rv = copy_devices(self, child, flags, &branch)) != 0) {
+ (void) hp_fini(children.head);
+ free(self);
+ return (rv);
+ }
+ if (branch != NULL)
+ node_list_add(&children, branch);
+ }
+ self->hp_child = children.head;
+
+ /* Done */
+ *rootp = self;
+ return (0);
+}
+
+/*
+ * copy_hotplug()
+ *
+ * Copy a full branch of hotplug nodes. Used by copy_devinfo()
+ * and copy_devices().
+ *
+ * If a connection is specified, the results are limited only
+ * to the branch associated with that specific connection.
+ */
+static int
+copy_hotplug(hp_node_t parent, di_node_t dev, const char *connection,
+ uint_t flags, hp_node_t *retp)
+{
+ hp_node_list_t connections, ports;
+ hp_node_t node, port_node;
+ di_node_t child_dev;
+ di_hp_t hp, port_hp;
+ uint_t child_flags;
+ int rv, physnum;
+
+ /* Stop implementing the HPINFOSEARCH flag */
+ child_flags = flags & ~(HPINFOSEARCH);
+
+ /* Clear lists of discovered ports and connections */
+ (void) memset(&ports, 0, sizeof (hp_node_list_t));
+ (void) memset(&connections, 0, sizeof (hp_node_list_t));
+
+ /*
+ * Scan virtual ports.
+ *
+ * If a connection is specified and it matches a virtual port,
+ * this will build the branch associated with that connection.
+ * Else, this will only build branches for virtual ports that
+ * are not associated with a physical connector.
+ */
+ for (hp = DI_HP_NIL; (hp = di_hp_next(dev, hp)) != DI_HP_NIL; ) {
+
+ /* Ignore connectors */
+ if (di_hp_type(hp) != DDI_HP_CN_TYPE_VIRTUAL_PORT)
+ continue;
+
+ /*
+ * Ignore ports associated with connectors, unless
+ * a specific connection is being sought.
+ */
+ if ((connection == NULL) && (di_hp_depends_on(hp) != -1))
+ continue;
+
+ /* If a connection is specified, ignore non-matching ports */
+ if ((connection != NULL) &&
+ (strcmp(di_hp_name(hp), connection) != 0))
+ continue;
+
+ /* Create a new port node */
+ if ((node = new_hotplug_node(parent, hp)) == NULL) {
+ rv = ENOMEM;
+ goto fail;
+ }
+
+ /* Add port node to connection list */
+ node_list_add(&connections, node);
+
+ /* Add branch of child devices to port node */
+ if ((child_dev = di_hp_child(hp)) != DI_NODE_NIL)
+ if ((rv = copy_devices(node, child_dev, child_flags,
+ &node->hp_child)) != 0)
+ goto fail;
+ }
+
+ /*
+ * Scan physical connectors.
+ *
+ * If a connection is specified, the results will be limited
+ * only to the branch associated with that connection.
+ */
+ for (hp = DI_HP_NIL; (hp = di_hp_next(dev, hp)) != DI_HP_NIL; ) {
+
+ /* Ignore ports */
+ if (di_hp_type(hp) == DDI_HP_CN_TYPE_VIRTUAL_PORT)
+ continue;
+
+ /* If a connection is specified, ignore non-matching ports */
+ if ((connection != NULL) &&
+ (strcmp(di_hp_name(hp), connection) != 0))
+ continue;
+
+ /* Create a new connector node */
+ if ((node = new_hotplug_node(parent, hp)) == NULL) {
+ rv = ENOMEM;
+ goto fail;
+ }
+
+ /* Add connector node to connection list */
+ node_list_add(&connections, node);
+
+ /* Add branches of associated port nodes */
+ physnum = di_hp_connection(hp);
+ port_hp = DI_HP_NIL;
+ while ((port_hp = di_hp_next(dev, port_hp)) != DI_HP_NIL) {
+
+ /* Ignore irrelevant connections */
+ if (di_hp_depends_on(port_hp) != physnum)
+ continue;
+
+ /* Add new port node to port list */
+ if ((port_node = new_hotplug_node(node,
+ port_hp)) == NULL) {
+ rv = ENOMEM;
+ goto fail;
+ }
+ node_list_add(&ports, port_node);
+
+ /* Add branch of child devices */
+ if ((child_dev = di_hp_child(port_hp)) != DI_NODE_NIL) {
+ if ((rv = copy_devices(port_node, child_dev,
+ child_flags, &port_node->hp_child)) != 0)
+ goto fail;
+ }
+ }
+ node->hp_child = ports.head;
+ (void) memset(&ports, 0, sizeof (hp_node_list_t));
+ }
+
+ if (connections.head == NULL)
+ return (ENXIO);
+ *retp = connections.head;
+ return (0);
+
+fail:
+ (void) hp_fini(ports.head);
+ (void) hp_fini(connections.head);
+ return (rv);
+}
+
+/*
+ * base_path()
+ *
+ * Normalize the base path of a hotplug information snapshot.
+ * The caller must free the string that is allocated.
+ */
+static char *
+base_path(const char *path)
+{
+ char *base_path;
+ size_t devices_len;
+
+ devices_len = strlen(S_DEVICES);
+
+ if (strncmp(path, S_DEVICES, devices_len) == 0)
+ base_path = strdup(&path[devices_len]);
+ else
+ base_path = strdup(path);
+
+ return (base_path);
+}
+
+/*
+ * search_cb()
+ *
+ * Callback function used by di_walk_node() to search for branches
+ * of the libdevinfo snapshot that contain hotplug nodes.
+ */
+/*ARGSUSED*/
+static int
+search_cb(di_node_t node, void *arg)
+{
+ di_node_t parent;
+ uint_t flags;
+
+ (void) di_node_private_set(node, (void *)(uintptr_t)0);
+
+ if (di_hp_next(node, DI_HP_NIL) == DI_HP_NIL)
+ return (DI_WALK_CONTINUE);
+
+ for (parent = node; parent != DI_NODE_NIL;
+ parent = di_parent_node(parent)) {
+ flags = (uint_t)(uintptr_t)di_node_private_get(parent);
+ flags |= HPINFOSEARCH;
+ (void) di_node_private_set(parent, (void *)(uintptr_t)flags);
+ }
+
+ return (DI_WALK_CONTINUE);
+}
+
+/*
+ * check_search()
+ *
+ * Check if a device node was marked by an initial search pass.
+ */
+static int
+check_search(di_node_t dev, uint_t flags)
+{
+ uint_t dev_flags;
+
+ if (flags & HPINFOSEARCH) {
+ dev_flags = (uint_t)(uintptr_t)di_node_private_get(dev);
+ if ((dev_flags & HPINFOSEARCH) == 0)
+ return (0);
+ }
+
+ return (1);
+}
+
+/*
+ * node_list_add()
+ *
+ * Utility function to append one node to a list of hotplug nodes.
+ */
+static void
+node_list_add(hp_node_list_t *listp, hp_node_t node)
+{
+ if (listp->prev != NULL)
+ listp->prev->hp_sibling = node;
+ else
+ listp->head = node;
+
+ listp->prev = node;
+}
+
+/*
+ * new_device_node()
+ *
+ * Build a new hotplug node based on a specified devinfo node.
+ */
+static hp_node_t
+new_device_node(hp_node_t parent, di_node_t dev)
+{
+ hp_node_t node;
+ char *node_name, *bus_addr;
+ char name[MAXPATHLEN];
+
+ node = (hp_node_t)calloc(1, sizeof (struct hp_node));
+
+ if (node != NULL) {
+ node->hp_parent = parent;
+ node->hp_type = HP_NODE_DEVICE;
+
+ node_name = di_node_name(dev);
+ bus_addr = di_bus_addr(dev);
+ if (bus_addr && (strlen(bus_addr) > 0)) {
+ if (snprintf(name, sizeof (name), "%s@%s", node_name,
+ bus_addr) >= sizeof (name)) {
+ log_err("Path too long for device node.\n");
+ free(node);
+ return (NULL);
+ }
+ node->hp_name = strdup(name);
+ } else
+ node->hp_name = strdup(node_name);
+ }
+
+ return (node);
+}
+
+/*
+ * new_hotplug_node()
+ *
+ * Build a new hotplug node based on a specified devinfo hotplug node.
+ */
+static hp_node_t
+new_hotplug_node(hp_node_t parent, di_hp_t hp)
+{
+ hp_node_t node;
+ char *s;
+
+ node = (hp_node_t)calloc(1, sizeof (struct hp_node));
+
+ if (node != NULL) {
+ node->hp_parent = parent;
+ node->hp_state = di_hp_state(hp);
+ node->hp_last_change = di_hp_last_change(hp);
+ if ((s = di_hp_name(hp)) != NULL)
+ node->hp_name = strdup(s);
+ if ((s = di_hp_description(hp)) != NULL)
+ node->hp_description = strdup(s);
+ if (di_hp_type(hp) == DDI_HP_CN_TYPE_VIRTUAL_PORT)
+ node->hp_type = HP_NODE_PORT;
+ else
+ node->hp_type = HP_NODE_CONNECTOR;
+ }
+
+ return (node);
+}
diff --git a/usr/src/cmd/hotplugd/hotplugd_rcm.c b/usr/src/cmd/hotplugd/hotplugd_rcm.c
new file mode 100644
index 0000000000..5be134b32b
--- /dev/null
+++ b/usr/src/cmd/hotplugd/hotplugd_rcm.c
@@ -0,0 +1,703 @@
+/*
+ * 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <librcm.h>
+#include <libhotplug.h>
+#include <libhotplug_impl.h>
+#include <sys/sunddi.h>
+#include <sys/ddi_hp.h>
+#include "hotplugd_impl.h"
+
+/*
+ * Define structures for a path-to-usage lookup table.
+ */
+typedef struct info_entry {
+ char *rsrc;
+ char *usage;
+ struct info_entry *next;
+} info_entry_t;
+
+typedef struct {
+ char *path;
+ info_entry_t *entries;
+} info_table_t;
+
+/*
+ * Define callback argument used when getting resources.
+ */
+typedef struct {
+ int error;
+ int n_rsrcs;
+ char **rsrcs;
+ char path[MAXPATHLEN];
+ char connection[MAXPATHLEN];
+ char dev_path[MAXPATHLEN];
+} resource_cb_arg_t;
+
+/*
+ * Define callback argument used when merging info.
+ */
+typedef struct {
+ int error;
+ info_table_t *table;
+ size_t table_len;
+ char path[MAXPATHLEN];
+ char connection[MAXPATHLEN];
+} merge_cb_arg_t;
+
+/*
+ * Local functions.
+ */
+static int merge_rcm_info(hp_node_t root, rcm_info_t *info);
+static int get_rcm_usage(char **rsrcs, rcm_info_t **info_p);
+static int build_table(rcm_info_t *info, info_table_t **tablep,
+ size_t *table_lenp);
+static void free_table(info_table_t *table, size_t table_len);
+static int resource_callback(hp_node_t node, void *argp);
+static int merge_callback(hp_node_t node, void *argp);
+static int rsrc2path(const char *rsrc, char *path);
+static int compare_info(const void *a, const void *b);
+
+/*
+ * copy_usage()
+ *
+ * Given an information snapshot, get the corresponding
+ * RCM usage information and merge it into the snapshot.
+ */
+int
+copy_usage(hp_node_t root)
+{
+ rcm_info_t *info = NULL;
+ char **rsrcs = NULL;
+ int rv;
+
+ /* Get resource names */
+ if ((rv = rcm_resources(root, &rsrcs)) != 0) {
+ log_err("Cannot get RCM resources (%s)\n", strerror(rv));
+ return (rv);
+ }
+
+ /* Do nothing if no resources */
+ if (rsrcs == NULL)
+ return (0);
+
+ /* Get RCM usage information */
+ if ((rv = get_rcm_usage(rsrcs, &info)) != 0) {
+ log_err("Cannot get RCM information (%s)\n", strerror(rv));
+ free_rcm_resources(rsrcs);
+ return (rv);
+ }
+
+ /* Done with resource names */
+ free_rcm_resources(rsrcs);
+
+ /* If there is RCM usage information, merge it in */
+ if (info != NULL) {
+ rv = merge_rcm_info(root, info);
+ rcm_free_info(info);
+ return (rv);
+ }
+
+ return (0);
+}
+
+/*
+ * rcm_resources()
+ *
+ * Given the root of a hotplug information snapshot,
+ * construct a list of RCM compatible resource names.
+ */
+int
+rcm_resources(hp_node_t root, char ***rsrcsp)
+{
+ resource_cb_arg_t arg;
+
+ /* Initialize results */
+ *rsrcsp = NULL;
+
+ /* Traverse snapshot to get resources */
+ (void) memset(&arg, 0, sizeof (resource_cb_arg_t));
+ (void) hp_traverse(root, &arg, resource_callback);
+
+ /* Check for errors */
+ if (arg.error != 0) {
+ free_rcm_resources(arg.rsrcs);
+ return (arg.error);
+ }
+
+ /* Success */
+ *rsrcsp = arg.rsrcs;
+ return (0);
+}
+
+/*
+ * free_rcm_resources()
+ *
+ * Free a table of RCM resource names.
+ */
+void
+free_rcm_resources(char **rsrcs)
+{
+ int i;
+
+ if (rsrcs != NULL) {
+ for (i = 0; rsrcs[i] != NULL; i++)
+ free(rsrcs[i]);
+ free(rsrcs);
+ }
+}
+
+/*
+ * rcm_offline()
+ *
+ * Implement an RCM offline request.
+ *
+ * NOTE: errors from RCM will be merged into the snapshot.
+ */
+int
+rcm_offline(char **rsrcs, uint_t flags, hp_node_t root)
+{
+ rcm_handle_t *handle;
+ rcm_info_t *info = NULL;
+ uint_t rcm_flags = 0;
+ int rv = 0;
+
+ dprintf("rcm_offline()\n");
+
+ /* Set flags */
+ if (flags & HPFORCE)
+ rcm_flags |= RCM_FORCE;
+ if (flags & HPQUERY)
+ rcm_flags |= RCM_QUERY;
+
+ /* Allocate RCM handle */
+ if (rcm_alloc_handle(NULL, 0, NULL, &handle) != RCM_SUCCESS) {
+ log_err("Cannot allocate RCM handle (%s)\n", strerror(errno));
+ return (EFAULT);
+ }
+
+ /* Request RCM offline */
+ if (rcm_request_offline_list(handle, rsrcs, rcm_flags,
+ &info) != RCM_SUCCESS)
+ rv = EBUSY;
+
+ /* RCM handle is no longer needed */
+ (void) rcm_free_handle(handle);
+
+ /*
+ * Check if RCM returned any information tuples. If so,
+ * then also check if the RCM operation failed, and possibly
+ * merge the RCM info into the caller's hotplug snapshot.
+ */
+ if (info != NULL) {
+ if (rv != 0)
+ (void) merge_rcm_info(root, info);
+ rcm_free_info(info);
+ }
+
+ return (rv);
+}
+
+/*
+ * rcm_online()
+ *
+ * Implement an RCM online notification.
+ */
+void
+rcm_online(char **rsrcs)
+{
+ rcm_handle_t *handle;
+ rcm_info_t *info = NULL;
+
+ dprintf("rcm_online()\n");
+
+ if (rcm_alloc_handle(NULL, 0, NULL, &handle) != RCM_SUCCESS) {
+ log_err("Cannot allocate RCM handle (%s)\n", strerror(errno));
+ return;
+ }
+
+ (void) rcm_notify_online_list(handle, rsrcs, 0, &info);
+
+ (void) rcm_free_handle(handle);
+
+ if (info != NULL)
+ rcm_free_info(info);
+}
+
+/*
+ * rcm_remove()
+ *
+ * Implement an RCM remove notification.
+ */
+void
+rcm_remove(char **rsrcs)
+{
+ rcm_handle_t *handle;
+ rcm_info_t *info = NULL;
+
+ dprintf("rcm_remove()\n");
+
+ if (rcm_alloc_handle(NULL, 0, NULL, &handle) != RCM_SUCCESS) {
+ log_err("Cannot allocate RCM handle (%s)\n", strerror(errno));
+ return;
+ }
+
+ (void) rcm_notify_remove_list(handle, rsrcs, 0, &info);
+
+ (void) rcm_free_handle(handle);
+
+ if (info != NULL)
+ rcm_free_info(info);
+}
+
+/*
+ * get_rcm_usage()
+ *
+ * Lookup usage information for a set of resources from RCM.
+ */
+static int
+get_rcm_usage(char **rsrcs, rcm_info_t **info_p)
+{
+ rcm_handle_t *handle;
+ rcm_info_t *info = NULL;
+ int rv = 0;
+
+ /* No-op if no RCM resources */
+ if (rsrcs == NULL)
+ return (0);
+
+ /* Allocate RCM handle */
+ if (rcm_alloc_handle(NULL, RCM_NOPID, NULL, &handle) != RCM_SUCCESS) {
+ log_err("Cannot allocate RCM handle (%s)\n", strerror(errno));
+ return (EFAULT);
+ }
+
+ /* Get usage information from RCM */
+ if (rcm_get_info_list(handle, rsrcs,
+ RCM_INCLUDE_DEPENDENT | RCM_INCLUDE_SUBTREE,
+ &info) != RCM_SUCCESS) {
+ log_err("Failed to get RCM information (%s)\n",
+ strerror(errno));
+ rv = EFAULT;
+ }
+
+ /* RCM handle is no longer needed */
+ (void) rcm_free_handle(handle);
+
+ *info_p = info;
+ return (rv);
+}
+
+/*
+ * merge_rcm_info()
+ *
+ * Merge RCM information into a hotplug information snapshot.
+ * First a lookup table is built to map lists of RCM usage to
+ * pathnames. Then during a full traversal of the snapshot,
+ * the lookup table is used for each node to find matching
+ * RCM info tuples for each path in the snapshot.
+ */
+static int
+merge_rcm_info(hp_node_t root, rcm_info_t *info)
+{
+ merge_cb_arg_t arg;
+ info_table_t *table;
+ size_t table_len;
+ int rv;
+
+ /* Build a lookup table, mapping paths to usage information */
+ if ((rv = build_table(info, &table, &table_len)) != 0) {
+ log_err("Cannot build RCM lookup table (%s)\n", strerror(rv));
+ return (rv);
+ }
+
+ /* Stop if no valid entries were inserted in table */
+ if ((table == NULL) || (table_len == 0)) {
+ log_err("Unable to gather RCM usage.\n");
+ return (0);
+ }
+
+ /* Initialize callback argument */
+ (void) memset(&arg, 0, sizeof (merge_cb_arg_t));
+ arg.table = table;
+ arg.table_len = table_len;
+
+ /* Perform a merge traversal */
+ (void) hp_traverse(root, (void *)&arg, merge_callback);
+
+ /* Done with the table */
+ free_table(table, table_len);
+
+ /* Check for errors */
+ if (arg.error != 0) {
+ log_err("Cannot merge RCM information (%s)\n", strerror(rv));
+ return (rv);
+ }
+
+ return (0);
+}
+
+/*
+ * resource_callback()
+ *
+ * A callback function for hp_traverse() that builds an RCM
+ * compatible list of resource path names. The array has
+ * been pre-allocated based on results from the related
+ * callback resource_count_callback().
+ */
+static int
+resource_callback(hp_node_t node, void *argp)
+{
+ resource_cb_arg_t *arg = (resource_cb_arg_t *)argp;
+ char **new_rsrcs;
+ size_t new_size;
+ int type;
+
+ /* Get node type */
+ type = hp_type(node);
+
+ /* Prune OFFLINE ports */
+ if ((type == HP_NODE_PORT) && HP_IS_OFFLINE(hp_state(node)))
+ return (HP_WALK_PRUNECHILD);
+
+ /* Skip past non-devices */
+ if (type != HP_NODE_DEVICE)
+ return (HP_WALK_CONTINUE);
+
+ /* Lookup resource path */
+ if (hp_path(node, arg->path, arg->connection) != 0) {
+ log_err("Cannot get RCM resource path.\n");
+ arg->error = EFAULT;
+ return (HP_WALK_TERMINATE);
+ }
+
+ /* Insert "/devices" to path name */
+ (void) snprintf(arg->dev_path, MAXPATHLEN, "/devices%s", arg->path);
+
+ /*
+ * Grow resource array to accomodate new /devices path.
+ * NOTE: include an extra NULL pointer at end of array.
+ */
+ new_size = (arg->n_rsrcs + 2) * sizeof (char *);
+ if (arg->rsrcs == NULL)
+ new_rsrcs = (char **)malloc(new_size);
+ else
+ new_rsrcs = (char **)realloc(arg->rsrcs, new_size);
+ if (new_rsrcs != NULL) {
+ arg->rsrcs = new_rsrcs;
+ } else {
+ log_err("Cannot allocate RCM resource array.\n");
+ arg->error = ENOMEM;
+ return (HP_WALK_TERMINATE);
+ }
+
+ /* Initialize new entries */
+ arg->rsrcs[arg->n_rsrcs] = strdup(arg->dev_path);
+ arg->rsrcs[arg->n_rsrcs + 1] = NULL;
+
+ /* Check for errors */
+ if (arg->rsrcs[arg->n_rsrcs] == NULL) {
+ log_err("Cannot allocate RCM resource path.\n");
+ arg->error = ENOMEM;
+ return (HP_WALK_TERMINATE);
+ }
+
+ /* Increment resource count */
+ arg->n_rsrcs += 1;
+
+ /* Do not visit children */
+ return (HP_WALK_PRUNECHILD);
+}
+
+/*
+ * merge_callback()
+ *
+ * A callback function for hp_traverse() that merges RCM information
+ * tuples into an existing hotplug information snapshot. The RCM
+ * information will be turned into HP_NODE_USAGE nodes.
+ */
+static int
+merge_callback(hp_node_t node, void *argp)
+{
+ merge_cb_arg_t *arg = (merge_cb_arg_t *)argp;
+ hp_node_t usage;
+ info_table_t lookup;
+ info_table_t *slot;
+ info_entry_t *entry;
+ int rv;
+
+ /* Only process device nodes (other nodes cannot have usage) */
+ if (hp_type(node) != HP_NODE_DEVICE)
+ return (HP_WALK_CONTINUE);
+
+ /* Get path of current node, using buffer provided in 'arg' */
+ if ((rv = hp_path(node, arg->path, arg->connection)) != 0) {
+ log_err("Cannot lookup hotplug path (%s)\n", strerror(rv));
+ arg->error = rv;
+ return (HP_WALK_TERMINATE);
+ }
+
+ /* Check the lookup table for associated usage */
+ lookup.path = arg->path;
+ if ((slot = bsearch(&lookup, arg->table, arg->table_len,
+ sizeof (info_table_t), compare_info)) == NULL)
+ return (HP_WALK_CONTINUE);
+
+ /* Usage information was found. Append HP_NODE_USAGE nodes. */
+ for (entry = slot->entries; entry != NULL; entry = entry->next) {
+
+ /* Allocate a new usage node */
+ usage = (hp_node_t)calloc(1, sizeof (struct hp_node));
+ if (usage == NULL) {
+ log_err("Cannot allocate hotplug usage node.\n");
+ arg->error = ENOMEM;
+ return (HP_WALK_TERMINATE);
+ }
+
+ /* Initialize the usage node's contents */
+ usage->hp_type = HP_NODE_USAGE;
+ if ((usage->hp_name = strdup(entry->rsrc)) == NULL) {
+ log_err("Cannot allocate hotplug usage node name.\n");
+ free(usage);
+ arg->error = ENOMEM;
+ return (HP_WALK_TERMINATE);
+ }
+ if ((usage->hp_usage = strdup(entry->usage)) == NULL) {
+ log_err("Cannot allocate hotplug usage node info.\n");
+ free(usage->hp_name);
+ free(usage);
+ arg->error = ENOMEM;
+ return (HP_WALK_TERMINATE);
+ }
+
+ /* Link the usage node as a child of the device node */
+ usage->hp_parent = node;
+ usage->hp_sibling = node->hp_child;
+ node->hp_child = usage;
+ }
+
+ return (HP_WALK_CONTINUE);
+}
+
+/*
+ * build_table()
+ *
+ * Build a lookup table that will be used to map paths to their
+ * corresponding RCM information tuples.
+ */
+static int
+build_table(rcm_info_t *info, info_table_t **tablep, size_t *table_lenp)
+{
+ rcm_info_tuple_t *tuple;
+ info_entry_t *entry;
+ info_table_t *slot;
+ info_table_t *table;
+ size_t table_len;
+ const char *rsrc;
+ const char *usage;
+ char path[MAXPATHLEN];
+
+ /* Initialize results */
+ *tablep = NULL;
+ *table_lenp = 0;
+
+ /* Count the RCM info tuples to determine the table's size */
+ table_len = 0;
+ for (tuple = NULL; (tuple = rcm_info_next(info, tuple)) != NULL; )
+ table_len++;
+
+ /* If the table would be empty, then do nothing */
+ if (table_len == 0)
+ return (ENOENT);
+
+ /* Allocate the lookup table */
+ table = (info_table_t *)calloc(table_len, sizeof (info_table_t));
+ if (table == NULL)
+ return (ENOMEM);
+
+ /*
+ * Fill in the lookup table. Fill one slot in the table
+ * for each device path that has a set of associated RCM
+ * information tuples. In some cases multiple tuples will
+ * be joined together within the same slot.
+ */
+ slot = NULL;
+ table_len = 0;
+ for (tuple = NULL; (tuple = rcm_info_next(info, tuple)) != NULL; ) {
+
+ /*
+ * Extract RCM resource name and usage description.
+ *
+ * NOTE: skip invalid tuples to return as much as possible.
+ */
+ if (((rsrc = rcm_info_rsrc(tuple)) == NULL) ||
+ ((usage = rcm_info_info(tuple)) == NULL)) {
+ log_err("RCM returned invalid resource or usage.\n");
+ continue;
+ }
+
+ /*
+ * Try to convert the RCM resource name to a hotplug path.
+ * If conversion succeeds and this path differs from the
+ * current slot in the table, then initialize the next
+ * slot in the table.
+ */
+ if ((rsrc2path(rsrc, path) == 0) &&
+ ((slot == NULL) || (strcmp(slot->path, path) != 0))) {
+ slot = &table[table_len];
+ if ((slot->path = strdup(path)) == NULL) {
+ log_err("Cannot build info table slot.\n");
+ free_table(table, table_len);
+ return (ENOMEM);
+ }
+ table_len++;
+ }
+
+ /* Append current usage to entry list in the current slot */
+ if (slot != NULL) {
+
+ /* Allocate new entry */
+ entry = (info_entry_t *)malloc(sizeof (info_entry_t));
+ if (entry == NULL) {
+ log_err("Cannot allocate info table entry.\n");
+ free_table(table, table_len);
+ return (ENOMEM);
+ }
+
+ /* Link entry into current slot list */
+ entry->next = slot->entries;
+ slot->entries = entry;
+
+ /* Initialize entry values */
+ if (((entry->rsrc = strdup(rsrc)) == NULL) ||
+ ((entry->usage = strdup(usage)) == NULL)) {
+ log_err("Cannot build info table entry.\n");
+ free_table(table, table_len);
+ return (ENOMEM);
+ }
+ }
+ }
+
+ /* Check if valid entries were inserted in table */
+ if (table_len == 0) {
+ free(table);
+ return (0);
+ }
+
+ /* Sort the lookup table by hotplug path */
+ qsort(table, table_len, sizeof (info_table_t), compare_info);
+
+ /* Done */
+ *tablep = table;
+ *table_lenp = table_len;
+ return (0);
+}
+
+/*
+ * free_table()
+ *
+ * Destroy a lookup table.
+ */
+static void
+free_table(info_table_t *table, size_t table_len)
+{
+ info_entry_t *entry;
+ int index;
+
+ if (table != NULL) {
+ for (index = 0; index < table_len; index++) {
+ if (table[index].path != NULL)
+ free(table[index].path);
+ while (table[index].entries != NULL) {
+ entry = table[index].entries;
+ table[index].entries = entry->next;
+ if (entry->rsrc != NULL)
+ free(entry->rsrc);
+ if (entry->usage != NULL)
+ free(entry->usage);
+ free(entry);
+ }
+ }
+ free(table);
+ }
+}
+
+/*
+ * rsrc2path()
+ *
+ * Convert from an RCM resource name to a hotplug device path.
+ */
+static int
+rsrc2path(const char *rsrc, char *path)
+{
+ char *s;
+ char tmp[MAXPATHLEN];
+
+ /* Only convert /dev and /devices paths */
+ if (strncmp(rsrc, "/dev", 4) == 0) {
+
+ /* Follow symbolic links for /dev paths */
+ if (realpath(rsrc, tmp) == NULL) {
+ log_err("Cannot resolve RCM resource (%s)\n",
+ strerror(errno));
+ return (-1);
+ }
+
+ /* Remove the leading "/devices" part */
+ (void) strlcpy(path, &tmp[strlen(S_DEVICES)], MAXPATHLEN);
+
+ /* Remove any trailing minor node part */
+ if ((s = strrchr(path, ':')) != NULL)
+ *s = '\0';
+
+ /* Successfully converted */
+ return (0);
+ }
+
+ /* Not converted */
+ return (-1);
+}
+
+/*
+ * compare_info()
+ *
+ * Compare two slots in the lookup table that maps paths to usage.
+ *
+ * NOTE: for use with qsort() and bsearch().
+ */
+static int
+compare_info(const void *a, const void *b)
+{
+ info_table_t *slot_a = (info_table_t *)a;
+ info_table_t *slot_b = (info_table_t *)b;
+
+ return (strcmp(slot_a->path, slot_b->path));
+}
diff --git a/usr/src/cmd/hotplugd/svc-hotplug b/usr/src/cmd/hotplugd/svc-hotplug
new file mode 100644
index 0000000000..ad140b6b39
--- /dev/null
+++ b/usr/src/cmd/hotplugd/svc-hotplug
@@ -0,0 +1,54 @@
+#!/sbin/sh
+#
+# 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.
+#
+
+#
+# Startup script for the hotplugd(1M) daemon.
+#
+
+. /lib/svc/share/smf_include.sh
+
+HOTPLUGD_DOOR=/var/run/hotplugd_door
+
+# The hotplug service is only supported in the global zone.
+if smf_is_nonglobalzone; then
+ /usr/sbin/svcadm disable $SMF_FMRI
+ echo "$SMF_FMRI is not supported in a local zone"
+ sleep 5 &
+ exit $SMF_EXIT_OK
+fi
+
+# If a hotplug door exists, check for a hotplugd process and exit
+# if the daemon is already running.
+if [ -f $HOTPLUGD_DOOR ]; then
+ if /usr/bin/pgrep -x -u 0 hotplugd >/dev/null 2>&1; then
+ echo "$0: hotplugd is already running"
+ exit 1
+ fi
+fi
+
+rm -f $HOTPLUGD_DOOR
+exec /usr/lib/hotplugd
diff --git a/usr/src/cmd/mdb/common/modules/genunix/Makefile.files b/usr/src/cmd/mdb/common/modules/genunix/Makefile.files
index b52992c8d0..c07bd9cb70 100644
--- a/usr/src/cmd/mdb/common/modules/genunix/Makefile.files
+++ b/usr/src/cmd/mdb/common/modules/genunix/Makefile.files
@@ -44,6 +44,7 @@ GENUNIX_SRCS = \
fm.c \
genunix.c \
group.c \
+ hotplug.c \
irm.c \
kgrep.c \
kmem.c \
diff --git a/usr/src/cmd/mdb/common/modules/genunix/devinfo.c b/usr/src/cmd/mdb/common/modules/genunix/devinfo.c
index 79988e2417..5d05dbbf9b 100644
--- a/usr/src/cmd/mdb/common/modules/genunix/devinfo.c
+++ b/usr/src/cmd/mdb/common/modules/genunix/devinfo.c
@@ -41,21 +41,12 @@
#include <mdb/mdb_ks.h>
#include "nvpair.h"
+#include "devinfo.h"
#define DEVINFO_TREE_INDENT 4 /* Indent for devs one down in tree */
#define DEVINFO_PROP_INDENT 4 /* Indent for properties */
#define DEVINFO_PROPLIST_INDENT 8 /* Indent for properties lists */
-
-/*
- * Options for prtconf/devinfo dcmd.
- */
-#define DEVINFO_VERBOSE 0x1
-#define DEVINFO_PARENT 0x2
-#define DEVINFO_CHILD 0x4
-#define DEVINFO_ALLBOLD 0x8
-#define DEVINFO_SUMMARY 0x10
-
/*
* devinfo node state map. Used by devinfo() and devinfo_audit().
* Long words are deliberately truncated so that output
@@ -954,11 +945,6 @@ exit:
mdb_dec_indent(DEVINFO_PROP_INDENT);
}
-typedef struct devinfo_cb_data {
- uintptr_t di_base;
- uint_t di_flags;
-} devinfo_cb_data_t;
-
static int
devinfo_print(uintptr_t addr, struct dev_info *dev, devinfo_cb_data_t *data)
{
diff --git a/usr/src/cmd/mdb/common/modules/genunix/devinfo.h b/usr/src/cmd/mdb/common/modules/genunix/devinfo.h
index d17638b185..a279faf215 100644
--- a/usr/src/cmd/mdb/common/modules/genunix/devinfo.h
+++ b/usr/src/cmd/mdb/common/modules/genunix/devinfo.h
@@ -20,21 +20,34 @@
*/
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#ifndef _DEVINFO_H
#define _DEVINFO_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#ifdef __cplusplus
extern "C" {
#endif
#include <mdb/mdb_modapi.h>
+/*
+ * Options for prtconf/devinfo/hotplug dcmd.
+ */
+#define DEVINFO_VERBOSE 0x1
+#define DEVINFO_PARENT 0x2
+#define DEVINFO_CHILD 0x4
+#define DEVINFO_ALLBOLD 0x8
+#define DEVINFO_SUMMARY 0x10
+#define DEVINFO_HP_PHYSICAL 0x20
+
+typedef struct devinfo_cb_data {
+ uintptr_t di_base;
+ uint_t di_flags;
+} devinfo_cb_data_t;
+
extern int devinfo_walk_init(mdb_walk_state_t *);
extern int devinfo_walk_step(mdb_walk_state_t *);
extern void devinfo_walk_fini(mdb_walk_state_t *);
diff --git a/usr/src/cmd/mdb/common/modules/genunix/genunix.c b/usr/src/cmd/mdb/common/modules/genunix/genunix.c
index f27684baf6..3e49d9a99c 100644
--- a/usr/src/cmd/mdb/common/modules/genunix/genunix.c
+++ b/usr/src/cmd/mdb/common/modules/genunix/genunix.c
@@ -106,6 +106,7 @@
#include "typegraph.h"
#include "vfs.h"
#include "zone.h"
+#include "hotplug.h"
/*
* Surely this is defined somewhere...
@@ -4503,6 +4504,10 @@ static const mdb_dcmd_t dcmds[] = {
{ "zsd", ":[-v] [zsd_key]", "display zone-specific-data entries for "
"selected zones", zsd },
+ /* from hotplug.c */
+ { "hotplug", "?[-p]", "display a registered hotplug attachment",
+ hotplug, hotplug_help },
+
{ NULL }
};
diff --git a/usr/src/cmd/mdb/common/modules/genunix/hotplug.c b/usr/src/cmd/mdb/common/modules/genunix/hotplug.c
new file mode 100644
index 0000000000..12b70b3669
--- /dev/null
+++ b/usr/src/cmd/mdb/common/modules/genunix/hotplug.c
@@ -0,0 +1,164 @@
+/*
+ * 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.
+ */
+
+#include <sys/mdb_modapi.h>
+#include <sys/proc.h>
+#include <sys/types.h>
+#include <sys/sunddi.h>
+#include <sys/ddi_hp.h>
+#include "devinfo.h"
+
+static char *
+ddihp_get_cn_state(ddi_hp_cn_state_t state)
+{
+ switch (state) {
+ case DDI_HP_CN_STATE_EMPTY:
+ return ("Empty");
+ case DDI_HP_CN_STATE_PRESENT:
+ return ("Present");
+ case DDI_HP_CN_STATE_POWERED:
+ return ("Powered");
+ case DDI_HP_CN_STATE_ENABLED:
+ return ("Enabled");
+ case DDI_HP_CN_STATE_PORT_EMPTY:
+ return ("Port_Empty");
+ case DDI_HP_CN_STATE_PORT_PRESENT:
+ return ("Port_Present");
+ case DDI_HP_CN_STATE_OFFLINE:
+ return ("Offline");
+ case DDI_HP_CN_STATE_ATTACHED:
+ return ("Attached");
+ case DDI_HP_CN_STATE_MAINTENANCE:
+ return ("Maintenance");
+ case DDI_HP_CN_STATE_ONLINE:
+ return ("Online");
+ default:
+ return ("Unknown");
+ }
+}
+
+/*ARGSUSED*/
+static int
+hotplug_print(uintptr_t addr, struct dev_info *dev, devinfo_cb_data_t *data)
+{
+ ddi_hp_cn_handle_t hdl;
+ uintptr_t hdlp = (uintptr_t)dev->devi_hp_hdlp;
+ char cn_type[15];
+ char cn_name[15];
+
+ while (hdlp) {
+ if (mdb_vread(&hdl, sizeof (ddi_hp_cn_handle_t), hdlp) == -1) {
+ mdb_warn("Failed to read hdlp!\n");
+ return (DCMD_ERR);
+ }
+
+ if (!(data->di_flags & DEVINFO_HP_PHYSICAL) ||
+ hdl.cn_info.cn_type != DDI_HP_CN_TYPE_VIRTUAL_PORT) {
+ if (mdb_readstr(cn_type, sizeof (cn_type),
+ (uintptr_t)hdl.cn_info.cn_type_str) == -1) {
+ mdb_warn("Failed to read cn_type!\n");
+ return (DCMD_ERR);
+ }
+ if (mdb_readstr(cn_name, sizeof (cn_name),
+ (uintptr_t)hdl.cn_info.cn_name) == -1) {
+ mdb_warn("Failed to read cn_name!\n");
+ return (DCMD_ERR);
+ }
+ mdb_printf("%?p %?p %-12s %-15s %-15s\n", hdl.cn_dip,
+ hdlp, ddihp_get_cn_state(hdl.cn_info.cn_state),
+ cn_type, cn_name);
+ }
+ hdlp = (uintptr_t)hdl.next;
+ };
+
+ return (WALK_NEXT);
+}
+
+void
+hotplug_help(void)
+{
+ mdb_printf("Switches:\n"
+ " -p only print the physical hotplug connectors\n");
+}
+
+int
+hotplug(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ devinfo_cb_data_t data;
+ uintptr_t devinfo_root; /* Address of root of devinfo tree */
+ ddi_hp_cn_handle_t hdl;
+ char cn_type[15];
+ char cn_name[15];
+ int status;
+
+ data.di_flags = 0;
+ if (mdb_getopts(argc, argv,
+ 'p', MDB_OPT_SETBITS, DEVINFO_HP_PHYSICAL, &data.di_flags, NULL)
+ != argc)
+ return (DCMD_USAGE);
+
+ if (DCMD_HDRSPEC(flags)) {
+ mdb_printf("%<u>%?s %?s %-12s %-15s %-15s%</u>\n",
+ "PARENT_DEVINFO", "HANDLE", "STATE", "TYPE", "CN_NAME");
+ }
+
+ if ((flags & DCMD_ADDRSPEC) == 0) {
+ data.di_flags |= DEVINFO_PARENT | DEVINFO_CHILD;
+
+ if (mdb_readvar(&devinfo_root, "top_devinfo") == -1) {
+ mdb_warn("failed to read 'top_devinfo'");
+ return (NULL);
+ }
+
+ data.di_base = devinfo_root;
+ status = mdb_pwalk("devinfo", (mdb_walk_cb_t)hotplug_print,
+ &data, devinfo_root);
+ if (status == -1) {
+ mdb_warn("couldn't walk devinfo tree");
+ return (DCMD_ERR);
+ }
+ return (DCMD_OK);
+ }
+
+ if (mdb_vread(&hdl, sizeof (ddi_hp_cn_handle_t), (uintptr_t)addr)
+ == -1) {
+ mdb_warn("Failed to read hdlp!\n");
+ return (DCMD_ERR);
+ }
+ if (mdb_readstr(cn_type, sizeof (cn_type),
+ (uintptr_t)hdl.cn_info.cn_type_str) == -1) {
+ mdb_warn("Failed to read cn_type!\n");
+ return (DCMD_ERR);
+ }
+ if (mdb_readstr(cn_name, sizeof (cn_name),
+ (uintptr_t)hdl.cn_info.cn_name) == -1) {
+ mdb_warn("Failed to read cn_name!\n");
+ return (DCMD_ERR);
+ }
+ mdb_printf("%?p %?p %-12s %-15s %-15s\n", hdl.cn_dip, addr,
+ ddihp_get_cn_state(hdl.cn_info.cn_state), cn_type, cn_name);
+
+ return (DCMD_OK);
+}
diff --git a/usr/src/uts/common/sys/hotplug/pci/pcishpc_regs.h b/usr/src/cmd/mdb/common/modules/genunix/hotplug.h
index d487067f59..ccc8ed0733 100755..100644
--- a/usr/src/uts/common/sys/hotplug/pci/pcishpc_regs.h
+++ b/usr/src/cmd/mdb/common/modules/genunix/hotplug.h
@@ -18,43 +18,24 @@
*
* CDDL HEADER END
*/
-
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#ifndef _SYS_HOTPLUG_PCI_PCISHPC_REGS_H
-#define _SYS_HOTPLUG_PCI_PCISHPC_REGS_H
-
-#pragma ident "%Z%%M% %I% %E% SMI"
+#ifndef _HOTPLUG_H
+#define _HOTPLUG_H
#ifdef __cplusplus
extern "C" {
#endif
-/*
- * SHPC controller registers accessed via the SHPC DWORD select and DATA
- * registers in PCI configuration space relative to the SHPC capibility
- * pointer.
- */
-#define SHPC_DWORD_SELECT_OFF 0x2
-#define SHPC_DWORD_DATA_OFF 0x4
-
-#define SHPC_BASE_OFFSET_REG 0x00
-#define SHPC_SLOTS_AVAIL_I_REG 0x01
-#define SHPC_SLOTS_AVAIL_II_REG 0x02
-#define SHPC_SLOT_CONFIGURATION_REG 0x03
-#define SHPC_PROF_IF_SBCR_REG 0x04
-#define SHPC_COMMAND_STATUS_REG 0x05
-#define SHPC_IRQ_LOCATOR_REG 0x06
-#define SHPC_SERR_LOCATOR_REG 0x07
-#define SHPC_CTRL_SERR_INT_REG 0x08
-#define SHPC_LOGICAL_SLOT_REGS 0x09
-#define SHPC_VENDOR_SPECIFIC 0x28
+extern int hotplug(uintptr_t addr, uint_t flags, int argc,
+ const mdb_arg_t *argv);
+extern void hotplug_help(void);
#ifdef __cplusplus
}
#endif
-#endif /* _SYS_HOTPLUG_PCI_PCISHPC_REGS_H */
+#endif /* _HOTPLUG_H */
diff --git a/usr/src/cmd/pcitool/pcitool.c b/usr/src/cmd/pcitool/pcitool.c
index 028712dbc1..a6ef09f6d7 100644
--- a/usr/src/cmd/pcitool/pcitool.c
+++ b/usr/src/cmd/pcitool/pcitool.c
@@ -136,6 +136,7 @@ static void print_probe_info(pci_conf_hdr_t *config_hdr_p,
pcitool_reg_t *info_p, boolean_t verbose);
static int get_config_header(int fd, uint8_t bus_no, uint8_t dev_no,
uint8_t func_no, pci_conf_hdr_t *config_hdr_p);
+static int supports_ari(int fd, uint8_t bus_no);
static int probe_dev(int fd, pcitool_reg_t *prg_p,
pcitool_uiargs_t *input_args_p);
static int do_probe(int fd, di_node_t di_node, di_prom_handle_t di_phdl,
@@ -457,10 +458,104 @@ get_config_header(int fd, uint8_t bus_no, uint8_t dev_no, uint8_t func_no,
}
config_hdr_p->dwords[i] = (uint32_t)cfg_prg.data;
}
-
return (rval);
}
+static int
+supports_ari(int fd, uint8_t bus_no)
+{
+ pcitool_reg_t cfg_prg;
+ int deadcount = 0;
+ uint32_t data, hdr_next_ptr, hdr_cap_id;
+ uint8_t dev_no = 0;
+ uint8_t func_no = 0;
+
+ /* Prepare a local pcitool_reg_t so as to not disturb the caller's. */
+ cfg_prg.bus_no = bus_no;
+ cfg_prg.dev_no = dev_no;
+ cfg_prg.func_no = func_no;
+ cfg_prg.barnum = 0;
+ cfg_prg.user_version = PCITOOL_VERSION;
+ cfg_prg.offset = PCI_CONF_COMM;
+ cfg_prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_4 + PCITOOL_ACC_ATTR_ENDN_LTL;
+
+ if (ioctl(fd, PCITOOL_DEVICE_GET_REG, &cfg_prg) != SUCCESS) {
+ return (FAILURE);
+ }
+
+ data = (uint32_t)cfg_prg.data;
+
+ if (!((data >> 16) & PCI_STAT_CAP))
+ return (FAILURE);
+
+ cfg_prg.offset = PCI_CONF_CAP_PTR;
+ if (ioctl(fd, PCITOOL_DEVICE_GET_REG, &cfg_prg) != SUCCESS) {
+ return (FAILURE);
+ }
+ data = (uint32_t)cfg_prg.data;
+ hdr_next_ptr = data & 0xff;
+ hdr_cap_id = 0;
+
+ /*
+ * Find the PCIe capability.
+ */
+ while ((hdr_next_ptr != PCI_CAP_NEXT_PTR_NULL) &&
+ (hdr_cap_id != PCI_CAP_ID_PCI_E)) {
+
+ if (hdr_next_ptr < 0x40)
+ break;
+
+ cfg_prg.offset = hdr_next_ptr;
+
+ if (ioctl(fd, PCITOOL_DEVICE_GET_REG, &cfg_prg) != SUCCESS)
+ return (FAILURE);
+
+ data = (uint32_t)cfg_prg.data;
+
+ hdr_next_ptr = (data >> 8) & 0xFF;
+ hdr_cap_id = data & 0xFF;
+
+ if (deadcount++ > 100)
+ return (FAILURE);
+ }
+
+ if (hdr_cap_id != PCI_CAP_ID_PCI_E)
+ return (FAILURE);
+
+ /* Found a PCIe Capability */
+
+ hdr_next_ptr = 0x100;
+ hdr_cap_id = 0;
+
+ /*
+ * Now find the ARI Capability.
+ */
+ while ((hdr_next_ptr != PCI_CAP_NEXT_PTR_NULL) &&
+ (hdr_cap_id != 0xe)) {
+
+ if (hdr_next_ptr < 0x40)
+ break;
+
+ cfg_prg.offset = hdr_next_ptr;
+
+ if (ioctl(fd, PCITOOL_DEVICE_GET_REG, &cfg_prg) != SUCCESS) {
+ return (FAILURE);
+ }
+ data = (uint32_t)cfg_prg.data;
+
+ hdr_next_ptr = (data >> 20) & 0xFFF;
+ hdr_cap_id = data & 0xFFFF;
+
+ if (deadcount++ > 100)
+ return (FAILURE);
+ }
+
+ if (hdr_cap_id != 0xe)
+ return (FAILURE);
+
+ return (SUCCESS);
+}
+
/*
* Identify problematic southbridges. These have device id 0x5249 and
* vendor id 0x10b9. Check for revision ID 0 and class code 060400 as well.
@@ -488,7 +583,7 @@ static int
probe_dev(int fd, pcitool_reg_t *prg_p, pcitool_uiargs_t *input_args_p)
{
pci_conf_hdr_t config_hdr;
- boolean_t multi_function_device;
+ boolean_t multi_function_device = B_FALSE;
int func;
int first_func = 0;
int last_func = PCI_REG_FUNC_M >> PCI_REG_FUNC_SHIFT;
@@ -496,6 +591,10 @@ probe_dev(int fd, pcitool_reg_t *prg_p, pcitool_uiargs_t *input_args_p)
if (input_args_p->flags & FUNC_SPEC_FLAG) {
first_func = last_func = input_args_p->function;
+ } else if (supports_ari(fd, prg_p->bus_no) == SUCCESS) {
+ multi_function_device = B_TRUE;
+ if (!(input_args_p->flags & DEV_SPEC_FLAG))
+ last_func = 255;
}
/*
@@ -508,11 +607,14 @@ probe_dev(int fd, pcitool_reg_t *prg_p, pcitool_uiargs_t *input_args_p)
* will force the loop as the user wants a specific function to be
* checked.
*/
- for (func = first_func, multi_function_device = B_FALSE;
- ((func <= last_func) &&
+ for (func = first_func; ((func <= last_func) &&
((func == first_func) || (multi_function_device)));
func++) {
- prg_p->func_no = func;
+ if (last_func > 7) {
+ prg_p->func_no = func & 0x7;
+ prg_p->dev_no = (func >> 3) & 0x1f;
+ } else
+ prg_p->func_no = func;
/*
* Four things can happen here:
@@ -587,8 +689,6 @@ probe_dev(int fd, pcitool_reg_t *prg_p, pcitool_uiargs_t *input_args_p)
*/
} else if (prg_p->data == 0) {
rval = SUCCESS;
- break; /* Func loop. */
-
/* Found something. */
} else {
config_hdr.dwords[0] = (uint32_t)prg_p->data;
@@ -718,6 +818,17 @@ do_probe(int fd, di_node_t di_node, di_prom_handle_t di_phdl,
*/
for (bus = first_bus; ((bus <= last_bus) && (rval == SUCCESS)); bus++) {
prg.bus_no = bus;
+
+ /* Device number explicitly specified. */
+ if (input_args_p->flags & DEV_SPEC_FLAG) {
+ first_dev = last_dev = input_args_p->device;
+ } else if (supports_ari(fd, bus) == SUCCESS) {
+ last_dev = 0;
+ first_dev = 0;
+ } else {
+ last_dev = PCI_REG_DEV_M >> PCI_REG_DEV_SHIFT;
+ }
+
for (dev = first_dev;
((dev <= last_dev) && (rval == SUCCESS)); dev++) {
prg.dev_no = dev;
diff --git a/usr/src/cmd/truss/print.c b/usr/src/cmd/truss/print.c
index bf77638b05..fa5e43a2ae 100644
--- a/usr/src/cmd/truss/print.c
+++ b/usr/src/cmd/truss/print.c
@@ -1140,6 +1140,7 @@ prt_mod(private_t *pri, int raw, long val) /* print modctl() code */
case MODGETDEVFSPATH_MI:
s = "MODGETDEVFSPATH_MI"; break;
case MODREMDRVALIAS: s = "MODREMDRVALIAS"; break;
+ case MODHPOPS: s = "MODHPOPS"; break;
}
}
diff --git a/usr/src/head/auth_list.h b/usr/src/head/auth_list.h
index 76f0ceb47a..d1ac5bc55e 100644
--- a/usr/src/head/auth_list.h
+++ b/usr/src/head/auth_list.h
@@ -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.
*
* This is an internal header file. Not to be shipped.
@@ -48,6 +48,7 @@ extern "C" {
#define SET_DATE_AUTH "solaris.system.date"
#define WIFI_CONFIG_AUTH "solaris.network.wifi.config"
#define WIFI_WEP_AUTH "solaris.network.wifi.wep"
+#define HP_MODIFY_AUTH "solaris.hotplug.modify"
/*
* Authorizations used by Trusted Solaris.
diff --git a/usr/src/lib/Makefile b/usr/src/lib/Makefile
index b535355b13..0fac076985 100644
--- a/usr/src/lib/Makefile
+++ b/usr/src/lib/Makefile
@@ -262,6 +262,7 @@ SUBDIRS += \
mpapi \
librstp \
libreparse \
+ libhotplug \
$($(MACH)_SUBDIRS)
i386_SUBDIRS= \
@@ -641,6 +642,8 @@ mpapi: libpthread libdevinfo libsysevent libnvpair
libgrubmgmt: libdevinfo libzfs libfstyp
pyzfs: libnvpair libsec libidmap libzfs
libreparse: libnvpair
+libhotplug: libnvpair
+cfgadm_plugins: libhotplug
#
# The reason this rule checks for the existence of the
diff --git a/usr/src/lib/cfgadm_plugins/Makefile b/usr/src/lib/cfgadm_plugins/Makefile
index 39543eabd3..bb37297b11 100644
--- a/usr/src/lib/cfgadm_plugins/Makefile
+++ b/usr/src/lib/cfgadm_plugins/Makefile
@@ -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.
#
# lib/cfgadm_plugins/Makefile
@@ -27,7 +27,7 @@
include $(SRC)/Makefile.master
-COMMON_SUBDIRS= scsi sdcard pci usb ib fp
+COMMON_SUBDIRS= scsi sdcard pci usb ib fp shp
sparc_SUBDIRS= sbd ac sysctrl
i386_SUBDIRS= sata
diff --git a/usr/src/lib/cfgadm_plugins/shp/Makefile b/usr/src/lib/cfgadm_plugins/shp/Makefile
new file mode 100644
index 0000000000..dfb0481783
--- /dev/null
+++ b/usr/src/lib/cfgadm_plugins/shp/Makefile
@@ -0,0 +1,70 @@
+#
+# 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.
+#
+#
+
+include ../../../Makefile.master
+
+SUBDIRS = $(MACH)
+$(BUILD64)SUBDIRS += $(MACH64)
+
+all := TARGET= all
+clean := TARGET= clean
+clobber := TARGET= clobber
+delete := TARGET= delete
+install := TARGET= install
+lint := TARGET= lint
+_msg := TARGET= _msg
+package := TARGET= package
+
+TEXT_DOMAIN= SUNW_OST_OSLIB
+XGETFLAGS= -a -x shp.xcl
+POFILE= shp.po
+POFILES= generic.po
+
+SED= sed
+GREP= grep
+
+.KEEP_STATE:
+
+all clean clobber delete install lint package: $(SUBDIRS)
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+_msg: $(MSGDOMAIN) $(POFILE)
+ $(RM) $(MSGDOMAIN)/$(POFILE)
+ $(CP) $(POFILE) $(MSGDOMAIN)
+
+$(POFILE): $(POFILES)
+ $(RM) $@
+ $(CAT) $(POFILES) > $@
+
+$(POFILES):
+ $(RM) messages.po
+ $(XGETTEXT) $(XGETFLAGS) `$(GREP) -l gettext *.[ch]* */*.[ch]*`
+ $(SED) -e '/^# msg/d' -e '/^domain/d' messages.po > $@
+ $(RM) messages.po
+
+FRC:
diff --git a/usr/src/lib/cfgadm_plugins/shp/Makefile.com b/usr/src/lib/cfgadm_plugins/shp/Makefile.com
new file mode 100644
index 0000000000..9a1751803b
--- /dev/null
+++ b/usr/src/lib/cfgadm_plugins/shp/Makefile.com
@@ -0,0 +1,86 @@
+#
+# 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.
+#
+
+LIBRARY= shp.a
+VERS= .1
+OBJECTS= shp.o pci_strings.o
+
+# include library definitions
+include ../../../Makefile.lib
+
+SRCDIR = ../common
+INS.dir.root.sys= $(INS) -s -d -m $(DIRMODE) $@
+$(CH)INS.dir.root.sys= $(INS) -s -d -m $(DIRMODE) -u root -g sys $@
+INS.dir.bin.bin= $(INS) -s -d -m $(DIRMODE) $@
+$(CH)INS.dir.bin.bin= $(INS) -s -d -m $(DIRMODE) -u bin -g bin $@
+
+USR_LIB_DIR = $(ROOT)/usr/lib
+USR_LIB_DIR_CFGADM = $(USR_LIB_DIR)/cfgadm
+USR_LIB_DIR_CFGADM_64 = $(USR_LIB_DIR_CFGADM)/$(MACH64)
+
+ROOTLIBDIR= $(USR_LIB_DIR_CFGADM)
+ROOTLIBDIR64= $(USR_LIB_DIR_CFGADM_64)
+
+SRCS= ../common/shp.c $(SRC)/common/pci/pci_strings.c
+
+LIBS = $(DYNLIB)
+
+CPPFLAGS += -I$(SRC)/lib/libhotplug/common
+CPPFLAGS += -D_POSIX_PTHREAD_SEMANTICS
+CFLAGS += $(CCVERBOSE)
+LDLIBS += -lc -ldevinfo -lhotplug
+
+.KEEP_STATE:
+
+all: $(LIBS)
+
+lint: lintcheck
+
+# Create target directories
+$(USR_LIB_DIR):
+ -$(INS.dir.root.sys)
+
+$(USR_LIB_DIR_CFGADM): $(USR_LIB_DIR)
+ -$(INS.dir.bin.bin)
+
+$(USR_LIB_DIR_CFGADM_64): $(USR_LIB_DIR_CFGADM)
+ -$(INS.dir.bin.bin)
+
+$(USR_LIB_DIR_CFGADM)/%: % $(USR_LIB_DIR_CFGADM)
+ -$(INS.file)
+
+$(USR_LIB_DIR_CFGADM_64)/%: % $(USR_LIB_DIR_CFGADM_64)
+ -$(INS.file)
+
+# include library targets
+include ../../../Makefile.targ
+
+pics/shp.o: ../common/shp.c
+ $(COMPILE.c) -o $@ ../common/shp.c
+ $(POST_PROCESS_O)
+
+pics/pci_strings.o: $(SRC)/common/pci/pci_strings.c
+ $(COMPILE.c) -o $@ $(SRC)/common/pci/pci_strings.c
+ $(POST_PROCESS_O)
diff --git a/usr/src/lib/cfgadm_plugins/shp/amd64/Makefile b/usr/src/lib/cfgadm_plugins/shp/amd64/Makefile
new file mode 100644
index 0000000000..ad23eac706
--- /dev/null
+++ b/usr/src/lib/cfgadm_plugins/shp/amd64/Makefile
@@ -0,0 +1,31 @@
+#
+# 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.
+#
+
+include ../Makefile.com
+include ../../../Makefile.lib.64
+
+.KEEP_STATE:
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64)
diff --git a/usr/src/lib/cfgadm_plugins/shp/common/mapfile-vers b/usr/src/lib/cfgadm_plugins/shp/common/mapfile-vers
new file mode 100644
index 0000000000..3f6e58d469
--- /dev/null
+++ b/usr/src/lib/cfgadm_plugins/shp/common/mapfile-vers
@@ -0,0 +1,50 @@
+#
+# 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.
+#
+
+#
+# MAPFILE HEADER START
+#
+# WARNING: STOP NOW. DO NOT MODIFY THIS FILE.
+# Object versioning must comply with the rules detailed in
+#
+# usr/src/lib/README.mapfiles
+#
+# You should not be making modifications here until you've read the most current
+# copy of that file. If you need help, contact a gatekeeper for guidance.
+#
+# MAPFILE HEADER END
+#
+
+SUNWprivate_1.1 {
+ global:
+ cfga_change_state;
+ cfga_help;
+ cfga_list_ext;
+ cfga_private_func;
+ cfga_test;
+ cfga_version;
+ local:
+ *;
+};
diff --git a/usr/src/lib/cfgadm_plugins/shp/common/shp.c b/usr/src/lib/cfgadm_plugins/shp/common/shp.c
new file mode 100644
index 0000000000..9a5b36aae7
--- /dev/null
+++ b/usr/src/lib/cfgadm_plugins/shp/common/shp.c
@@ -0,0 +1,1698 @@
+/*
+ * 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.
+ */
+
+/*
+ * Plugin library for PCI Express and PCI (SHPC) hotplug controller
+ */
+
+#include <stddef.h>
+#include <locale.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <locale.h>
+#include <langinfo.h>
+#include <time.h>
+#include <sys/param.h>
+#include <stdarg.h>
+#include <libdevinfo.h>
+#include <libdevice.h>
+
+#define CFGA_PLUGIN_LIB
+
+#include <config_admin.h>
+
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/dditypes.h>
+#include <sys/pci.h>
+#include <libintl.h>
+
+#include <dirent.h>
+#include <limits.h>
+#include <sys/mkdev.h>
+#include "../../../../uts/common/sys/hotplug/pci/pcie_hp.h"
+#include "../../../../common/pci/pci_strings.h"
+#include <libhotplug.h>
+
+extern const struct pci_class_strings_s class_pci[];
+extern int class_pci_items;
+
+#define MSG_HOTPLUG_DISABLED \
+ "Error: hotplug service is probably not running, " \
+ "please use 'svcadm enable hotplug' to enable the service. " \
+ "See cfgadm_shp(1M) for more details."
+
+#define DEVICES_DIR "/devices"
+#define SLASH "/"
+#define GET_DYN(a) (strstr((a), CFGA_DYN_SEP))
+
+/*
+ * Set the version number
+ */
+int cfga_version = CFGA_HSL_V2;
+
+#ifdef DEBUG
+#define SHP_DBG 1
+#endif
+
+#if !defined(TEXT_DOMAIN)
+#define TEXT_DOMAIN "SYS_TEST"
+#endif
+
+/*
+ * DEBUGING LEVEL
+ *
+ * External routines: 1 - 2
+ * Internal routines: 3 - 4
+ */
+#ifdef SHP_DBG
+int shp_debug = 1;
+#define DBG(level, args) \
+ { if (shp_debug >= (level)) printf args; }
+#define DBG_F(level, args) \
+ { if (shp_debug >= (level)) fprintf args; }
+#else
+#define DBG(level, args) /* nothing */
+#define DBG_F(level, args) /* nothing */
+#endif
+
+#define CMD_ACQUIRE 0
+#define CMD_GETSTAT 1
+#define CMD_LIST 2
+#define CMD_SLOT_CONNECT 3
+#define CMD_SLOT_DISCONNECT 4
+#define CMD_SLOT_CONFIGURE 5
+#define CMD_SLOT_UNCONFIGURE 6
+#define CMD_SLOT_INSERT 7
+#define CMD_SLOT_REMOVE 8
+#define CMD_OPEN 9
+#define CMD_FSTAT 10
+#define ERR_CMD_INVAL 11
+#define ERR_AP_INVAL 12
+#define ERR_AP_ERR 13
+#define ERR_OPT_INVAL 14
+
+static char *
+cfga_errstrs[] = {
+ /* n */ "acquire ",
+ /* n */ "get-status ",
+ /* n */ "list ",
+ /* n */ "connect ",
+ /* n */ "disconnect ",
+ /* n */ "configure ",
+ /* n */ "unconfigure ",
+ /* n */ "insert ",
+ /* n */ "remove ",
+ /* n */ "open ",
+ /* n */ "fstat ",
+ /* y */ "invalid command ",
+ /* y */ "invalid attachment point ",
+ /* y */ "invalid transition ",
+ /* y */ "invalid option ",
+ NULL
+};
+
+#define HELP_HEADER 1
+#define HELP_CONFIG 2
+#define HELP_ENABLE_SLOT 3
+#define HELP_DISABLE_SLOT 4
+#define HELP_ENABLE_AUTOCONF 5
+#define HELP_DISABLE_AUTOCONF 6
+#define HELP_LED_CNTRL 7
+#define HELP_UNKNOWN 8
+#define SUCCESS 9
+#define FAILED 10
+#define UNKNOWN 11
+
+#define MAXLINE 256
+
+extern int errno;
+
+static void cfga_err(char **errstring, ...);
+static cfga_err_t fix_ap_name(char *ap_log_id, const char *ap_id,
+ char *slot_name, char **errstring);
+static cfga_err_t check_options(const char *options);
+static void cfga_msg(struct cfga_msg *msgp, const char *str);
+static char *findlink(char *ap_phys_id);
+
+static char *
+cfga_strs[] = {
+NULL,
+"\nPCI hotplug specific commands:",
+"\t-c [connect|disconnect|configure|unconfigure|insert|remove] "
+"ap_id [ap_id...]",
+"\t-x enable_slot ap_id [ap_id...]",
+"\t-x disable_slot ap_id [ap_id...]",
+"\t-x enable_autoconfig ap_id [ap_id...]",
+"\t-x disable_autoconfig ap_id [ap_id...]",
+"\t-x led[=[fault|power|active|attn],mode=[on|off|blink]] ap_id [ap_id...]",
+"\tunknown command or option: ",
+"success ",
+"failed ",
+"unknown",
+NULL
+};
+
+#define MAX_FORMAT 80
+
+#define ENABLE_SLOT 0
+#define DISABLE_SLOT 1
+#define ENABLE_AUTOCNF 2
+#define DISABLE_AUTOCNF 3
+#define LED 4
+#define MODE 5
+
+typedef enum { PCIEHPC_FAULT_LED, PCIEHPC_POWER_LED, PCIEHPC_ATTN_LED,
+ PCIEHPC_ACTIVE_LED} pciehpc_led_t;
+
+typedef enum { PCIEHPC_BOARD_UNKNOWN, PCIEHPC_BOARD_PCI_HOTPLUG }
+ pciehpc_board_type_t;
+
+/*
+ * Board Type
+ */
+static char *
+board_strs[] = {
+ /* n */ "???", /* PCIEHPC_BOARD_UNKNOWN */
+ /* n */ "hp", /* PCIEHPC_BOARD_PCI_HOTPLUG */
+ /* n */ NULL
+};
+
+/*
+ * HW functions
+ */
+static char *
+func_strs[] = {
+ /* n */ "enable_slot",
+ /* n */ "disable_slot",
+ /* n */ "enable_autoconfig",
+ /* n */ "disable_autoconfig",
+ /* n */ "led",
+ /* n */ "mode",
+ /* n */ NULL
+};
+
+/*
+ * LED strings
+ */
+static char *
+led_strs[] = {
+ /* n */ "fault", /* PCIEHPC_FAULT_LED */
+ /* n */ "power", /* PCIEHPC_POWER_LED */
+ /* n */ "attn", /* PCIEHPC_ATTN_LED */
+ /* n */ "active", /* PCIEHPC_ACTIVE_LED */
+ /* n */ NULL
+};
+
+static char *
+led_strs2[] = {
+ /* n */ PCIEHPC_PROP_LED_FAULT, /* PCIEHPC_FAULT_LED */
+ /* n */ PCIEHPC_PROP_LED_POWER, /* PCIEHPC_POWER_LED */
+ /* n */ PCIEHPC_PROP_LED_ATTN, /* PCIEHPC_ATTN_LED */
+ /* n */ PCIEHPC_PROP_LED_ACTIVE, /* PCIEHPC_ACTIVE_LED */
+ /* n */ NULL
+};
+
+#define FAULT 0
+#define POWER 1
+#define ATTN 2
+#define ACTIVE 3
+
+static char *
+mode_strs[] = {
+ /* n */ "off", /* OFF */
+ /* n */ "on", /* ON */
+ /* n */ "blink", /* BLINK */
+ /* n */ NULL
+};
+
+#define OFF 0
+#define ON 1
+#define BLINK 2
+
+#define cfga_errstrs(i) cfga_errstrs[(i)]
+
+#define cfga_eid(a, b) (((a) << 8) + (b))
+#define MAXDEVS 32
+
+typedef enum {
+ SOLARIS_SLT_NAME,
+ PROM_SLT_NAME
+} slt_name_src_t;
+
+struct searcharg {
+ char *devpath;
+ char slotnames[MAXDEVS][MAXNAMELEN];
+ int minor;
+ di_prom_handle_t promp;
+ slt_name_src_t slt_name_src;
+};
+
+static void *private_check;
+
+/*
+ * Return the corresponding hp node for a given ap_id, it is the caller's
+ * responsibility to call hp_fini() to free the snapshot.
+ */
+static cfga_err_t
+physpath2node(const char *physpath, char **errstring, hp_node_t *nodep)
+{
+ char *rpath;
+ char *cp;
+ hp_node_t node;
+ size_t len;
+ char *errmsg;
+
+ if (getuid() != 0 && geteuid() != 0)
+ return (CFGA_ERROR);
+
+ if ((rpath = malloc(strlen(physpath) + 1)) == NULL)
+ return (CFGA_ERROR);
+
+ (void) strcpy(rpath, physpath);
+
+ /* Remove devices prefix (if any) */
+ len = strlen(DEVICES_DIR);
+ if (strncmp(rpath, DEVICES_DIR SLASH, len + strlen(SLASH)) == 0) {
+ (void) memmove(rpath, rpath + len,
+ strlen(rpath + len) + 1);
+ }
+
+ /* Remove dynamic component if any */
+ if ((cp = GET_DYN(rpath)) != NULL) {
+ *cp = '\0';
+ }
+
+ /* Remove minor name (if any) */
+ if ((cp = strrchr(rpath, ':')) == NULL) {
+ free(rpath);
+ return (CFGA_INVAL);
+ }
+
+ *cp = '\0';
+ cp++;
+
+ DBG(1, ("rpath=%s,cp=%s\n", rpath, cp));
+ if ((node = hp_init(rpath, cp, 0)) == NULL) {
+ if (errno == EBADF) {
+ /* No reponse to operations on the door file. */
+ assert(errstring != NULL);
+ *errstring = strdup(MSG_HOTPLUG_DISABLED);
+ free(rpath);
+ return (CFGA_NOTSUPP);
+ }
+ free(rpath);
+ return (CFGA_ERROR);
+ }
+
+ free(rpath);
+
+ *nodep = node;
+ return (CFGA_OK);
+}
+
+typedef struct error_size_cb_arg {
+ size_t rsrc_width;
+ size_t info_width;
+ int cnt;
+} error_size_cb_arg_t;
+
+/*
+ * Callback function for hp_traverse(), to sum up the
+ * maximum length for error message display.
+ */
+static int
+error_sizeup_cb(hp_node_t node, void *arg)
+{
+ error_size_cb_arg_t *sizearg = (error_size_cb_arg_t *)arg;
+ size_t len;
+
+ /* Only process USAGE nodes */
+ if (hp_type(node) != HP_NODE_USAGE)
+ return (HP_WALK_CONTINUE);
+
+ sizearg->cnt++;
+
+ /* size up resource name */
+ len = strlen(hp_name(node));
+ if (sizearg->rsrc_width < len)
+ sizearg->rsrc_width = len;
+
+ /* size up usage description */
+ len = strlen(hp_usage(node));
+ if (sizearg->info_width < len)
+ sizearg->info_width = len;
+
+ return (HP_WALK_CONTINUE);
+}
+
+typedef struct error_sum_cb_arg {
+ char **table;
+ char *format;
+} error_sum_cb_arg_t;
+
+/*
+ * Callback function for hp_traverse(), to add the error
+ * message to he table.
+ */
+static int
+error_sumup_cb(hp_node_t node, void *arg)
+{
+ error_sum_cb_arg_t *sumarg = (error_sum_cb_arg_t *)arg;
+ char **table = sumarg->table;
+ char *format = sumarg->format;
+
+ /* Only process USAGE nodes */
+ if (hp_type(node) != HP_NODE_USAGE)
+ return (HP_WALK_CONTINUE);
+
+ (void) strcat(*table, "\n");
+ (void) sprintf(&((*table)[strlen(*table)]),
+ format, hp_name(node), hp_usage(node));
+
+ return (HP_WALK_CONTINUE);
+}
+
+/*
+ * Takes an opaque rcm_info_t pointer and a character pointer, and appends
+ * the rcm_info_t data in the form of a table to the given character pointer.
+ */
+static void
+pci_rcm_info_table(hp_node_t node, char **table)
+{
+ int i;
+ size_t w;
+ size_t width = 0;
+ size_t w_rsrc = 0;
+ size_t w_info = 0;
+ size_t table_size = 0;
+ uint_t tuples = 0;
+ char *rsrc;
+ char *info;
+ char *newtable;
+ static char format[MAX_FORMAT];
+ const char *infostr;
+ error_size_cb_arg_t sizearg;
+ error_sum_cb_arg_t sumarg;
+
+ /* Protect against invalid arguments */
+ if (table == NULL)
+ return;
+
+ /* Set localized table header strings */
+ rsrc = dgettext(TEXT_DOMAIN, "Resource");
+ info = dgettext(TEXT_DOMAIN, "Information");
+
+ /* A first pass, to size up the RCM information */
+ sizearg.rsrc_width = strlen(rsrc);
+ sizearg.info_width = strlen(info);
+ sizearg.cnt = 0;
+ (void) hp_traverse(node, &sizearg, error_sizeup_cb);
+
+ /* If nothing was sized up above, stop early */
+ if (sizearg.cnt == 0)
+ return;
+
+ w_rsrc = sizearg.rsrc_width;
+ w_info = sizearg.info_width;
+ tuples = sizearg.cnt;
+
+ /* Adjust column widths for column headings */
+ if ((w = strlen(rsrc)) > w_rsrc)
+ w_rsrc = w;
+ else if ((w_rsrc - w) % 2)
+ w_rsrc++;
+ if ((w = strlen(info)) > w_info)
+ w_info = w;
+ else if ((w_info - w) % 2)
+ w_info++;
+
+ /*
+ * Compute the total line width of each line,
+ * accounting for intercolumn spacing.
+ */
+ width = w_info + w_rsrc + 4;
+
+ /* Allocate space for the table */
+ table_size = (2 + tuples) * (width + 1) + 2;
+ if (*table == NULL) {
+ /* zero fill for the strcat() call below */
+ *table = calloc(table_size, sizeof (char));
+ if (*table == NULL)
+ return;
+ } else {
+ newtable = realloc(*table, strlen(*table) + table_size);
+ if (newtable == NULL)
+ return;
+ else
+ *table = newtable;
+ }
+
+ /* Place a table header into the string */
+
+ /* The resource header */
+ (void) strcat(*table, "\n");
+ w = strlen(rsrc);
+ for (i = 0; i < ((w_rsrc - w) / 2); i++)
+ (void) strcat(*table, " ");
+ (void) strcat(*table, rsrc);
+ for (i = 0; i < ((w_rsrc - w) / 2); i++)
+ (void) strcat(*table, " ");
+
+ /* The information header */
+ (void) strcat(*table, " ");
+ w = strlen(info);
+ for (i = 0; i < ((w_info - w) / 2); i++)
+ (void) strcat(*table, " ");
+ (void) strcat(*table, info);
+ for (i = 0; i < ((w_info - w) / 2); i++)
+ (void) strcat(*table, " ");
+ /* Underline the headers */
+ (void) strcat(*table, "\n");
+ for (i = 0; i < w_rsrc; i++)
+ (void) strcat(*table, "-");
+ (void) strcat(*table, " ");
+ for (i = 0; i < w_info; i++)
+ (void) strcat(*table, "-");
+
+ /* Construct the format string */
+ (void) snprintf(format, MAX_FORMAT, "%%-%ds %%-%ds",
+ (int)w_rsrc, (int)w_info);
+
+ /* Add the tuples to the table string */
+ sumarg.table = table;
+ sumarg.format = format;
+ (void) hp_traverse(node, &sumarg, error_sumup_cb);
+}
+
+/*
+ * Figure out the target kernel state for a given cfgadm
+ * change-state operation.
+ */
+static cfga_err_t
+cfga_target_state(cfga_cmd_t state_change_cmd, int *state)
+{
+ switch (state_change_cmd) {
+ case CFGA_CMD_CONNECT:
+ *state = DDI_HP_CN_STATE_POWERED;
+ break;
+ case CFGA_CMD_DISCONNECT:
+ *state = DDI_HP_CN_STATE_PRESENT;
+ break;
+ case CFGA_CMD_CONFIGURE:
+ *state = DDI_HP_CN_STATE_ENABLED;
+ break;
+ case CFGA_CMD_UNCONFIGURE:
+ *state = DDI_HP_CN_STATE_POWERED;
+ break;
+ default:
+ return (CFGA_ERROR);
+ }
+
+ return (CFGA_OK);
+}
+
+/*
+ * Translate kernel state to cfgadm receptacle state and occupant state.
+ */
+static cfga_err_t
+cfga_get_state(hp_node_t connector, ap_rstate_t *rs, ap_ostate_t *os)
+{
+ int state;
+ hp_node_t port;
+
+ state = hp_state(connector);
+
+ /* Receptacle state */
+ switch (state) {
+ case DDI_HP_CN_STATE_EMPTY:
+ *rs = AP_RSTATE_EMPTY;
+ break;
+ case DDI_HP_CN_STATE_PRESENT:
+ *rs = AP_RSTATE_DISCONNECTED;
+ break;
+ case DDI_HP_CN_STATE_POWERED:
+ case DDI_HP_CN_STATE_ENABLED:
+ *rs = AP_RSTATE_CONNECTED;
+ break;
+ /*
+ * Connector state can only be one of
+ * Empty, Present, Powered, Enabled.
+ */
+ default:
+ return (CFGA_ERROR);
+ }
+
+ /*
+ * Occupant state
+ */
+ port = hp_child(connector);
+ while (port != NULL) {
+ DBG(1, ("cfga_get_state:(%x)\n", hp_state(port)));
+
+ /*
+ * Mark occupant state as "configured" if at least one of the
+ * associated ports is at state "offline" or above. Driver
+ * attach ("online" state) is not necessary here.
+ */
+ if (hp_state(port) >= DDI_HP_CN_STATE_OFFLINE)
+ break;
+
+ port = hp_sibling(port);
+ }
+
+ if (port != NULL)
+ *os = AP_OSTATE_CONFIGURED;
+ else
+ *os = AP_OSTATE_UNCONFIGURED;
+
+ return (CFGA_OK);
+}
+
+/*
+ * Transitional Diagram:
+ *
+ * empty unconfigure
+ * (remove) ^| (physically insert card)
+ * |V
+ * disconnect configure
+ * "-c DISCONNECT" ^| "-c CONNECT"
+ * |V "-c CONFIGURE"
+ * connect unconfigure -> connect configure
+ * <-
+ * "-c UNCONFIGURE"
+ *
+ */
+/*ARGSUSED*/
+cfga_err_t
+cfga_change_state(cfga_cmd_t state_change_cmd, const char *ap_id,
+ const char *options, struct cfga_confirm *confp,
+ struct cfga_msg *msgp, char **errstring, cfga_flags_t flags)
+{
+ int rv, state, new_state;
+ hp_node_t node;
+ hp_node_t results = NULL;
+
+ if ((rv = check_options(options)) != CFGA_OK) {
+ return (rv);
+ }
+
+ if (errstring != NULL)
+ *errstring = NULL;
+
+ rv = CFGA_OK;
+ DBG(1, ("cfga_change_state:(%s)\n", ap_id));
+
+ rv = physpath2node(ap_id, errstring, &node);
+ if (rv != CFGA_OK)
+ return (rv);
+
+ state = hp_state(node);
+
+ /*
+ * Which state should we drive to ?
+ */
+ if ((state_change_cmd != CFGA_CMD_LOAD) &&
+ (state_change_cmd != CFGA_CMD_UNLOAD)) {
+ if (cfga_target_state(state_change_cmd,
+ &new_state) != CFGA_OK) {
+ hp_fini(node);
+ return (CFGA_ERROR);
+ }
+ }
+
+ DBG(1, ("cfga_change_state: state is %d\n", state));
+ switch (state_change_cmd) {
+ case CFGA_CMD_CONNECT:
+ if ((state == DDI_HP_CN_STATE_EMPTY) ||
+ (state >= DDI_HP_CN_STATE_POWERED)) {
+ cfga_err(errstring, ERR_AP_ERR, 0);
+ rv = CFGA_INVAL;
+ } else {
+ /* Lets connect the slot */
+ if (hp_set_state(node, 0, new_state, &results) != 0) {
+ rv = CFGA_ERROR;
+ cfga_err(errstring, CMD_SLOT_CONNECT, 0);
+ }
+ }
+
+ break;
+
+ case CFGA_CMD_DISCONNECT:
+ DBG(1, ("disconnect\n"));
+
+ if (state < DDI_HP_CN_STATE_POWERED) {
+ cfga_err(errstring, ERR_AP_ERR, 0);
+ rv = CFGA_INVAL;
+ } else {
+ if ((rv = hp_set_state(node, 0, new_state, &results))
+ != 0) {
+ if (rv == EBUSY)
+ rv = CFGA_BUSY;
+ else
+ rv = CFGA_ERROR;
+
+ if (results) {
+ pci_rcm_info_table(results, errstring);
+ hp_fini(results);
+ } else {
+ cfga_err(errstring,
+ CMD_SLOT_DISCONNECT, 0);
+ }
+ }
+ }
+
+ break;
+
+ case CFGA_CMD_CONFIGURE:
+ /*
+ * for multi-func device we allow multiple
+ * configure on the same slot because one
+ * func can be configured and other one won't
+ */
+ if (hp_set_state(node, 0, new_state, &results) != 0) {
+ rv = CFGA_ERROR;
+ cfga_err(errstring, CMD_SLOT_CONFIGURE, 0);
+ }
+
+ break;
+
+ case CFGA_CMD_UNCONFIGURE:
+ DBG(1, ("unconfigure\n"));
+
+ if (state >= DDI_HP_CN_STATE_ENABLED) {
+ if ((rv = hp_set_state(node, 0, new_state, &results))
+ != 0) {
+ if (rv == EBUSY)
+ rv = CFGA_BUSY;
+ else
+ rv = CFGA_ERROR;
+
+ if (results) {
+ pci_rcm_info_table(results, errstring);
+ hp_fini(results);
+ } else {
+ cfga_err(errstring,
+ CMD_SLOT_UNCONFIGURE, 0);
+ }
+ }
+ } else {
+ cfga_err(errstring, ERR_AP_ERR, 0);
+ rv = CFGA_INVAL;
+ }
+
+ DBG(1, ("unconfigure rv:(%i)\n", rv));
+ break;
+
+ case CFGA_CMD_LOAD:
+ /* do nothing, just produce error msg as is */
+ if (state < DDI_HP_CN_STATE_POWERED) {
+ rv = CFGA_ERROR;
+ cfga_err(errstring, CMD_SLOT_INSERT, 0);
+ } else {
+ cfga_err(errstring, ERR_AP_ERR, 0);
+ rv = CFGA_INVAL;
+ }
+ break;
+
+ case CFGA_CMD_UNLOAD:
+ /* do nothing, just produce error msg as is */
+ if (state < DDI_HP_CN_STATE_POWERED) {
+ rv = CFGA_ERROR;
+ cfga_err(errstring, CMD_SLOT_REMOVE, 0);
+ } else {
+ cfga_err(errstring, ERR_AP_ERR, 0);
+ rv = CFGA_INVAL;
+ }
+ break;
+
+ default:
+ rv = CFGA_OPNOTSUPP;
+ break;
+ }
+
+ hp_fini(node);
+ return (rv);
+}
+
+char *
+get_val_from_result(char *result)
+{
+ char *tmp;
+
+ tmp = strchr(result, '=');
+ if (tmp == NULL)
+ return (NULL);
+
+ tmp++;
+ return (tmp);
+}
+
+static cfga_err_t
+prt_led_mode(const char *ap_id, int repeat, char **errstring,
+ struct cfga_msg *msgp)
+{
+ pciehpc_led_t led;
+ hp_node_t node;
+ char *buff;
+ char *buf;
+ char *cp, line[MAXLINE];
+ char *tmp;
+ char *format;
+ char *result;
+ int i, n, rv;
+ int len = MAXLINE;
+
+ pciehpc_led_t states[] = {
+ PCIEHPC_POWER_LED,
+ PCIEHPC_FAULT_LED,
+ PCIEHPC_ATTN_LED,
+ PCIEHPC_ACTIVE_LED
+ };
+
+ DBG(1, ("prt_led_mod function\n"));
+ if (!repeat)
+ cfga_msg(msgp, "Ap_Id\t\t\tLed");
+
+ rv = physpath2node(ap_id, errstring, &node);
+ if (rv != CFGA_OK)
+ return (rv);
+
+ if ((buff = malloc(MAXPATHLEN)) == NULL) {
+ hp_fini(node);
+ cfga_err(errstring, "malloc ", 0);
+ return (CFGA_ERROR);
+ }
+
+ (void) memset(buff, 0, MAXPATHLEN);
+
+ if (fix_ap_name(buff, ap_id, hp_name(node),
+ errstring) != CFGA_OK) {
+ hp_fini(node);
+ free(buff);
+ return (CFGA_ERROR);
+ }
+
+ cp = line;
+ (void) snprintf(cp, len, "%s\t\t", buff);
+ len -= strlen(cp);
+ cp += strlen(cp);
+
+ free(buff);
+
+ n = sizeof (states)/sizeof (pciehpc_led_t);
+ for (i = 0; i < n; i++) {
+ led = states[i];
+
+ format = (i == n - 1) ? "%s=%s" : "%s=%s,";
+ if (hp_get_private(node, led_strs2[led], &result) != 0) {
+ (void) snprintf(cp, len, format,
+ led_strs[led], cfga_strs[UNKNOWN]);
+ len -= strlen(cp);
+ cp += strlen(cp);
+ DBG(1, ("%s:%s\n", led_strs[led], cfga_strs[UNKNOWN]));
+ } else {
+ /*
+ * hp_get_private() will return back things like
+ * "led_fault=off", transform it to cfgadm desired
+ * format.
+ */
+ tmp = get_val_from_result(result);
+ if (tmp == NULL) {
+ free(result);
+ hp_fini(node);
+ return (CFGA_ERROR);
+ }
+
+ (void) snprintf(cp, len, format,
+ led_strs[led], tmp);
+ len -= strlen(cp);
+ cp += strlen(cp);
+ DBG(1, ("%s:%s\n", led_strs[led], tmp));
+ free(result);
+ }
+ }
+
+ cfga_msg(msgp, line); /* print the message */
+
+ hp_fini(node);
+
+ return (CFGA_OK);
+}
+
+/*ARGSUSED*/
+cfga_err_t
+cfga_private_func(const char *function, const char *ap_id,
+ const char *options, struct cfga_confirm *confp,
+ struct cfga_msg *msgp, char **errstring, cfga_flags_t flags)
+{
+ char *str;
+ int len, fd, i = 0, repeat = 0;
+ char buf[MAXNAMELEN];
+ char ptr;
+ cfga_err_t rv;
+ char *led, *mode;
+ hp_node_t node;
+ char *result;
+
+ DBG(1, ("cfgadm_private_func: ap_id:%s\n", ap_id));
+ DBG(2, (" options: %s\n", (options == NULL)?"null":options));
+ DBG(2, (" confp: %x\n", confp));
+ DBG(2, (" cfga_msg: %x\n", cfga_msg));
+ DBG(2, (" flag: %d\n", flags));
+
+ if ((rv = check_options(options)) != CFGA_OK) {
+ return (rv);
+ }
+
+ if (private_check == confp)
+ repeat = 1;
+ else
+ private_check = (void*)confp;
+
+ for (i = 0, str = func_strs[i], len = strlen(str);
+ func_strs[i] != NULL; i++) {
+ str = func_strs[i];
+ len = strlen(str);
+ if (strncmp(function, str, len) == 0)
+ break;
+ }
+
+ switch (i) {
+ case ENABLE_SLOT:
+ case DISABLE_SLOT:
+ /* pass through */
+ case ENABLE_AUTOCNF:
+ case DISABLE_AUTOCNF:
+ /* no action needed */
+ return (CFGA_OK);
+ break;
+ case LED:
+ /* set mode */
+ ptr = function[len++];
+ if (ptr == '=') {
+ str = (char *)function;
+ for (str = (str+len++), i = 0; *str != ',';
+ i++, str++) {
+ if (i == (MAXNAMELEN - 1))
+ break;
+
+ buf[i] = *str;
+ DBG_F(2, (stdout, "%c\n", buf[i]));
+ }
+ buf[i] = '\0'; str++;
+ DBG(2, ("buf = %s\n", buf));
+
+ /* ACTIVE=3,ATTN=2,POWER=1,FAULT=0 */
+ if (strcmp(buf, led_strs[POWER]) == 0)
+ led = PCIEHPC_PROP_LED_POWER;
+ else if (strcmp(buf, led_strs[FAULT]) == 0)
+ led = PCIEHPC_PROP_LED_FAULT;
+ else if (strcmp(buf, led_strs[ATTN]) == 0)
+ led = PCIEHPC_PROP_LED_ATTN;
+ else if (strcmp(buf, led_strs[ACTIVE]) == 0)
+ led = PCIEHPC_PROP_LED_ACTIVE;
+ else return (CFGA_INVAL);
+
+ len = strlen(func_strs[MODE]);
+ if ((strncmp(str, func_strs[MODE], len) == 0) &&
+ (*(str+(len)) == '=')) {
+ for (str = (str+(++len)), i = 0;
+ *str != NULL; i++, str++) {
+ buf[i] = *str;
+ }
+ }
+ buf[i] = '\0';
+ DBG(2, ("buf_mode= %s\n", buf));
+
+ /* ON = 1, OFF = 0 */
+ if (strcmp(buf, mode_strs[ON]) == 0)
+ mode = PCIEHPC_PROP_VALUE_ON;
+ else if (strcmp(buf, mode_strs[OFF]) == 0)
+ mode = PCIEHPC_PROP_VALUE_OFF;
+ else if (strcmp(buf, mode_strs[BLINK]) == 0)
+ mode = PCIEHPC_PROP_VALUE_BLINK;
+ else return (CFGA_INVAL);
+
+ /* sendin */
+ memset(buf, 0, sizeof (buf));
+ snprintf(buf, sizeof (buf), "%s=%s",
+ led, mode);
+ buf[MAXNAMELEN - 1] = '\0';
+
+ break;
+ } else if (ptr == '\0') {
+ /* print mode */
+ DBG(1, ("Print mode\n"));
+ return (prt_led_mode(ap_id, repeat, errstring,
+ msgp));
+ }
+ default:
+ DBG(1, ("default\n"));
+ errno = EINVAL;
+ return (CFGA_INVAL);
+ }
+
+ rv = physpath2node(ap_id, errstring, &node);
+ if (rv != CFGA_OK)
+ return (rv);
+
+ if (hp_set_private(node, buf, &result) != 0) {
+ hp_fini(node);
+ return (CFGA_ERROR);
+ }
+
+ hp_fini(node);
+ return (CFGA_OK);
+}
+
+/*ARGSUSED*/
+cfga_err_t cfga_test(const char *ap_id, const char *options,
+ struct cfga_msg *msgp, char **errstring, cfga_flags_t flags)
+{
+ cfga_err_t rv;
+ if (errstring != NULL)
+ *errstring = NULL;
+
+ if ((rv = check_options(options)) != CFGA_OK) {
+ return (rv);
+ }
+
+ DBG(1, ("cfga_test:(%s)\n", ap_id));
+ /* will need to implement pci CTRL command */
+ return (CFGA_NOTSUPP);
+}
+
+/*
+ * The slot-names property describes the external labeling of add-in slots.
+ * This property is an encoded array, an integer followed by a list of
+ * strings. The return value from di_prop_lookup_ints for slot-names is -1.
+ * The expected return value should be the number of elements.
+ * Di_prop_decode_common does not decode encoded data from software,
+ * such as the solaris device tree, unlike from the prom.
+ * Di_prop_decode_common takes the size of the encoded data and mods
+ * it with the size of int. The size of the encoded data for slot-names is 9
+ * and the size of int is 4, yielding a non zero result. A value of -1 is used
+ * to indicate that the number of elements can not be determined.
+ * Di_prop_decode_common can be modified to decode encoded data from the solaris
+ * device tree.
+ */
+static int
+fixup_slotname(int rval, int *intp, struct searcharg *slotarg)
+{
+ if ((slotarg->slt_name_src == PROM_SLT_NAME) && (rval == -1)) {
+ return (DI_WALK_TERMINATE);
+ } else {
+ int i;
+ char *tmptr = (char *)(intp+1);
+ DBG(1, ("slot-bitmask: %x \n", *intp));
+
+ rval = (rval -1) * 4;
+
+ for (i = 0; i <= slotarg->minor; i++) {
+ DBG(2, ("curr slot-name: %s \n", tmptr));
+
+ if (i >= MAXDEVS)
+ return (DI_WALK_TERMINATE);
+
+ if ((*intp >> i) & 1) {
+ /* assign tmptr */
+ DBG(2, ("slot-name: %s \n", tmptr));
+ if (i == slotarg->minor)
+ (void) strcpy(slotarg->slotnames[i],
+ tmptr);
+ /* wind tmptr to next \0 */
+ while (*tmptr != '\0') {
+ tmptr++;
+ }
+ tmptr++;
+ } else {
+ /* point at unknown string */
+ if (i == slotarg->minor)
+ (void) strcpy(slotarg->slotnames[i],
+ "unknown");
+ }
+ }
+ }
+ return (DI_WALK_TERMINATE);
+}
+
+static int
+find_slotname(di_node_t din, di_minor_t dim, void *arg)
+{
+ struct searcharg *slotarg = (struct searcharg *)arg;
+ di_prom_handle_t ph = (di_prom_handle_t)slotarg->promp;
+ di_prom_prop_t prom_prop;
+ di_prop_t solaris_prop;
+ int *intp, rval;
+ char *devname;
+ char fulldevname[MAXNAMELEN];
+
+ slotarg->minor = dim->dev_minor % 256;
+
+ DBG(2, ("minor number:(%i)\n", slotarg->minor));
+ DBG(2, ("hot plug slots found so far:(%i)\n", 0));
+
+ if ((devname = di_devfs_path(din)) != NULL) {
+ (void) snprintf(fulldevname, MAXNAMELEN,
+ "/devices%s:%s", devname, di_minor_name(dim));
+ di_devfs_path_free(devname);
+ }
+
+ if (strcmp(fulldevname, slotarg->devpath) == 0) {
+
+ /*
+ * Check the Solaris device tree first
+ * in the case of a DR operation
+ */
+ solaris_prop = di_prop_hw_next(din, DI_PROP_NIL);
+ while (solaris_prop != DI_PROP_NIL) {
+ if (strcmp("slot-names", di_prop_name(solaris_prop))
+ == 0) {
+ rval = di_prop_lookup_ints(DDI_DEV_T_ANY,
+ din, di_prop_name(solaris_prop), &intp);
+ slotarg->slt_name_src = SOLARIS_SLT_NAME;
+
+ return (fixup_slotname(rval, intp, slotarg));
+ }
+ solaris_prop = di_prop_hw_next(din, solaris_prop);
+ }
+
+ /*
+ * Check the prom device tree which is populated at boot.
+ * If this fails, give up and set the slot name to null.
+ */
+ prom_prop = di_prom_prop_next(ph, din, DI_PROM_PROP_NIL);
+ while (prom_prop != DI_PROM_PROP_NIL) {
+ if (strcmp("slot-names", di_prom_prop_name(prom_prop))
+ == 0) {
+ rval = di_prom_prop_lookup_ints(ph,
+ din, di_prom_prop_name(prom_prop), &intp);
+ slotarg->slt_name_src = PROM_SLT_NAME;
+
+ return (fixup_slotname(rval, intp, slotarg));
+ }
+ prom_prop = di_prom_prop_next(ph, din, prom_prop);
+ }
+ *slotarg->slotnames[slotarg->minor] = '\0';
+ return (DI_WALK_TERMINATE);
+ } else
+ return (DI_WALK_CONTINUE);
+}
+
+static int
+find_physical_slot_names(const char *devcomp, struct searcharg *slotarg)
+{
+ di_node_t root_node;
+
+ DBG(1, ("find_physical_slot_names\n"));
+
+ if ((root_node = di_init("/", DINFOCPYALL|DINFOPATH))
+ == DI_NODE_NIL) {
+ DBG(1, ("di_init() failed\n"));
+ return (-1);
+ }
+
+ slotarg->devpath = (char *)devcomp;
+
+ if ((slotarg->promp = di_prom_init()) == DI_PROM_HANDLE_NIL) {
+ DBG(1, ("di_prom_init() failed\n"));
+ di_fini(root_node);
+ return (-1);
+ }
+
+ (void) di_walk_minor(root_node, "ddi_ctl:attachment_point:pci",
+ 0, (void *)slotarg, find_slotname);
+
+ di_prom_fini(slotarg->promp);
+ di_fini(root_node);
+ if (slotarg->slotnames[0] != NULL)
+ return (0);
+ else
+ return (-1);
+}
+
+static void
+get_type(const char *boardtype, const char *cardtype, char *buf)
+{
+/* for type string assembly in get_type() */
+#define TPCT(s) (void) strlcat(buf, (s), CFGA_TYPE_LEN)
+
+ int i;
+
+ if (strcmp(cardtype, "unknown") == 0) {
+ TPCT("unknown");
+ return;
+ }
+
+ TPCT(cardtype);
+ TPCT("/");
+
+ if (strcmp(boardtype, PCIEHPC_PROP_VALUE_PCIHOTPLUG) == 0)
+ TPCT(board_strs[PCIEHPC_BOARD_PCI_HOTPLUG]);
+ else
+ TPCT(board_strs[PCIEHPC_BOARD_UNKNOWN]);
+}
+
+/*
+ * call-back function for di_devlink_walk
+ * if the link lives in /dev/cfg copy its name
+ */
+static int
+found_devlink(di_devlink_t link, void *ap_log_id)
+{
+ if (strncmp("/dev/cfg/", di_devlink_path(link), 9) == 0) {
+ /* copy everything but /dev/cfg/ */
+ (void) strcpy((char *)ap_log_id, di_devlink_path(link) + 9);
+ DBG(1, ("found_devlink: %s\n", (char *)ap_log_id));
+ return (DI_WALK_TERMINATE);
+ }
+ return (DI_WALK_CONTINUE);
+}
+
+/*
+ * Walk throught the cached /dev link tree looking for links to the ap
+ * if none are found return an error
+ */
+static cfga_err_t
+check_devlinks(char *ap_log_id, const char *ap_id)
+{
+ di_devlink_handle_t hdl;
+
+ DBG(1, ("check_devlinks: %s\n", ap_id));
+
+ hdl = di_devlink_init(NULL, 0);
+
+ if (strncmp("/devices/", ap_id, 9) == 0) {
+ /* ap_id is a valid minor_path with /devices prepended */
+ (void) di_devlink_walk(hdl, NULL, ap_id + 8, DI_PRIMARY_LINK,
+ (void *)ap_log_id, found_devlink);
+ } else {
+ DBG(1, ("check_devlinks: invalid ap_id: %s\n", ap_id));
+ return (CFGA_ERROR);
+ }
+
+ (void) di_devlink_fini(&hdl);
+
+ if (ap_log_id[0] != '\0')
+ return (CFGA_OK);
+ else
+ return (CFGA_ERROR);
+}
+
+/*
+ * most of this is needed to compensate for
+ * differences between various platforms
+ */
+static cfga_err_t
+fix_ap_name(char *ap_log_id, const char *ap_id, char *slot_name,
+ char **errstring)
+{
+ char *buf;
+ char *tmp;
+ char *ptr;
+
+ di_node_t ap_node;
+
+ ap_log_id[0] = '\0';
+
+ if (check_devlinks(ap_log_id, ap_id) == CFGA_OK)
+ return (CFGA_OK);
+
+ DBG(1, ("fix_ap_name: %s\n", ap_id));
+
+ if ((buf = malloc(strlen(ap_id) + 1)) == NULL) {
+ DBG(1, ("malloc failed\n"));
+ return (CFGA_ERROR);
+ }
+ (void) strcpy(buf, ap_id);
+ tmp = buf + sizeof ("/devices") - 1;
+
+ ptr = strchr(tmp, ':');
+ ptr[0] = '\0';
+
+ DBG(1, ("fix_ap_name: %s\n", tmp));
+
+ ap_node = di_init(tmp, DINFOMINOR);
+ if (ap_node == DI_NODE_NIL) {
+ cfga_err(errstring, "di_init ", 0);
+ DBG(1, ("fix_ap_name: failed to snapshot node\n"));
+ return (CFGA_ERROR);
+ }
+
+ (void) snprintf(ap_log_id, strlen(ap_id) + 1, "%s%i:%s",
+ di_driver_name(ap_node), di_instance(ap_node), slot_name);
+
+ DBG(1, ("fix_ap_name: %s\n", ap_log_id));
+
+ di_fini(ap_node);
+
+ free(buf);
+ return (CFGA_OK);
+}
+
+
+static int
+findlink_cb(di_devlink_t devlink, void *arg)
+{
+ (*(char **)arg) = strdup(di_devlink_path(devlink));
+
+ return (DI_WALK_TERMINATE);
+}
+
+/*
+ * returns an allocated string containing the full path to the devlink for
+ * <ap_phys_id> in the devlink database; we expect only one devlink per
+ * <ap_phys_id> so we return the first encountered
+ */
+static char *
+findlink(char *ap_phys_id)
+{
+ di_devlink_handle_t hdl;
+ char *path = NULL;
+
+ hdl = di_devlink_init(NULL, 0);
+
+ if (strncmp("/devices/", ap_phys_id, 9) == 0)
+ ap_phys_id += 8;
+
+ (void) di_devlink_walk(hdl, "^cfg/.+$", ap_phys_id, DI_PRIMARY_LINK,
+ (void *)&path, findlink_cb);
+
+ (void) di_devlink_fini(&hdl);
+ return (path);
+}
+
+
+/*
+ * returns CFGA_OK if it can succesfully retrieve the devlink info associated
+ * with devlink for <ap_phys_id> which will be returned through <ap_info>
+ */
+cfga_err_t
+get_dli(char *dlpath, char *ap_info, int ap_info_sz)
+{
+ int fd;
+
+ fd = di_dli_openr(dlpath);
+ if (fd < 0)
+ return (CFGA_ERROR);
+
+ (void) read(fd, ap_info, ap_info_sz);
+ ap_info[ap_info_sz - 1] = '\0';
+
+ di_dli_close(fd);
+ return (CFGA_OK);
+}
+
+static cfga_err_t
+cfga_get_condition(hp_node_t node, ap_condition_t *cond)
+{
+ char *condition;
+
+ /* "condition" bus specific commands */
+ if (hp_get_private(node, PCIEHPC_PROP_SLOT_CONDITION,
+ &condition) != 0) {
+ *cond = AP_COND_UNKNOWN;
+ return (CFGA_ERROR);
+ }
+
+ condition = get_val_from_result(condition);
+
+ if (strcmp(condition, PCIEHPC_PROP_COND_OK) == 0)
+ *cond = AP_COND_OK;
+ else if (strcmp(condition, PCIEHPC_PROP_COND_FAILING) == 0)
+ *cond = AP_COND_FAILING;
+ else if (strcmp(condition, PCIEHPC_PROP_COND_FAILED) == 0)
+ *cond = AP_COND_FAILED;
+ else if (strcmp(condition, PCIEHPC_PROP_COND_UNUSABLE) == 0)
+ *cond = AP_COND_UNUSABLE;
+ else if (strcmp(condition, PCIEHPC_PROP_COND_UNKNOWN) == 0)
+ *cond = AP_COND_UNKNOWN;
+ else
+ return (CFGA_ERROR);
+
+ return (CFGA_OK);
+}
+
+/*ARGSUSED*/
+cfga_err_t
+cfga_list_ext(const char *ap_id, cfga_list_data_t **cs,
+ int *nlist, const char *options, const char *listopts, char **errstring,
+ cfga_flags_t flags)
+{
+ char *boardtype;
+ char *cardtype;
+ struct searcharg slotname_arg;
+ int fd;
+ int rv = CFGA_OK;
+ char *dlpath = NULL;
+ hp_node_t node;
+ ap_rstate_t rs;
+ ap_ostate_t os;
+ ap_condition_t cond;
+
+ if ((rv = check_options(options)) != CFGA_OK) {
+ return (rv);
+ }
+
+ if (errstring != NULL)
+ *errstring = NULL;
+
+ DBG(1, ("cfga_list_ext:(%s)\n", ap_id));
+
+ if (cs == NULL || nlist == NULL) {
+ rv = CFGA_ERROR;
+ return (rv);
+ }
+
+ *nlist = 1;
+
+ if ((*cs = malloc(sizeof (cfga_list_data_t))) == NULL) {
+ cfga_err(errstring, "malloc ", 0);
+ DBG(1, ("malloc failed\n"));
+ rv = CFGA_ERROR;
+ return (rv);
+ }
+ (void) memset(*cs, 0, sizeof (cfga_list_data_t));
+
+ rv = physpath2node(ap_id, errstring, &node);
+ if (rv != CFGA_OK) {
+ DBG(1, ("physpath2node failed\n"));
+ return (rv);
+ }
+
+ if (cfga_get_state(node, &rs, &os) != CFGA_OK) {
+ DBG(1, ("cfga_get_state failed\n"));
+ hp_fini(node);
+ return (CFGA_ERROR);
+ }
+
+ switch (rs) {
+ case AP_RSTATE_EMPTY:
+ (*cs)->ap_r_state = CFGA_STAT_EMPTY;
+ DBG(2, ("ap_rstate = CFGA_STAT_EMPTY\n"));
+ break;
+ case AP_RSTATE_DISCONNECTED:
+ (*cs)->ap_r_state = CFGA_STAT_DISCONNECTED;
+ DBG(2, ("ap_rstate = CFGA_STAT_DISCONNECTED\n"));
+ break;
+ case AP_RSTATE_CONNECTED:
+ (*cs)->ap_r_state = CFGA_STAT_CONNECTED;
+ DBG(2, ("ap_rstate = CFGA_STAT_CONNECTED\n"));
+ break;
+ default:
+ cfga_err(errstring, CMD_GETSTAT, ap_id, 0);
+ rv = CFGA_ERROR;
+ hp_fini(node);
+ return (rv);
+ }
+
+ switch (os) {
+ case AP_OSTATE_CONFIGURED:
+ (*cs)->ap_o_state = CFGA_STAT_CONFIGURED;
+ DBG(2, ("ap_ostate = CFGA_STAT_CONFIGURED\n"));
+ break;
+ case AP_OSTATE_UNCONFIGURED:
+ (*cs)->ap_o_state = CFGA_STAT_UNCONFIGURED;
+ DBG(2, ("ap_ostate = CFGA_STAT_UNCONFIGURED\n"));
+ break;
+ default:
+ cfga_err(errstring, CMD_GETSTAT, ap_id, 0);
+ rv = CFGA_ERROR;
+ hp_fini(node);
+ return (rv);
+ }
+
+ (void) cfga_get_condition(node, &cond);
+
+ switch (cond) {
+ case AP_COND_OK:
+ (*cs)->ap_cond = CFGA_COND_OK;
+ DBG(2, ("ap_cond = CFGA_COND_OK\n"));
+ break;
+ case AP_COND_FAILING:
+ (*cs)->ap_cond = CFGA_COND_FAILING;
+ DBG(2, ("ap_cond = CFGA_COND_FAILING\n"));
+ break;
+ case AP_COND_FAILED:
+ (*cs)->ap_cond = CFGA_COND_FAILED;
+ DBG(2, ("ap_cond = CFGA_COND_FAILED\n"));
+ break;
+ case AP_COND_UNUSABLE:
+ (*cs)->ap_cond = CFGA_COND_UNUSABLE;
+ DBG(2, ("ap_cond = CFGA_COND_UNUSABLE\n"));
+ break;
+ case AP_COND_UNKNOWN:
+ (*cs)->ap_cond = CFGA_COND_UNKNOWN;
+ DBG(2, ("ap_cond = CFGA_COND_UNKNOW\n"));
+ break;
+ default:
+ cfga_err(errstring, CMD_GETSTAT, ap_id, 0);
+ rv = CFGA_ERROR;
+ hp_fini(node);
+ return (rv);
+ }
+ /*
+ * We're not busy since the entrance into the kernel has been
+ * sync'ed via libhotplug.
+ */
+ (*cs)->ap_busy = 0;
+
+ /* last change */
+ (*cs)->ap_status_time = hp_last_change(node);
+
+ /* board type */
+ if (hp_get_private(node, PCIEHPC_PROP_BOARD_TYPE, &boardtype) != 0)
+ boardtype = PCIEHPC_PROP_VALUE_UNKNOWN;
+ else
+ boardtype = get_val_from_result(boardtype);
+
+ /* card type */
+ if (hp_get_private(node, PCIEHPC_PROP_CARD_TYPE, &cardtype) != 0)
+ cardtype = PCIEHPC_PROP_VALUE_UNKNOWN;
+ else
+ cardtype = get_val_from_result(cardtype);
+
+ /* logical ap_id */
+ rv = fix_ap_name((*cs)->ap_log_id, ap_id,
+ hp_name(node), errstring);
+ DBG(1, ("logical id: %s\n", (*cs)->ap_log_id));
+ /* physical ap_id */
+ (void) strcpy((*cs)->ap_phys_id, ap_id); /* physical path of AP */
+
+ /* information */
+ dlpath = findlink((*cs)->ap_phys_id);
+ if (dlpath != NULL) {
+ if (get_dli(dlpath, (*cs)->ap_info,
+ sizeof ((*cs)->ap_info)) != CFGA_OK)
+ (*cs)->ap_info[0] = '\0';
+ free(dlpath);
+ }
+
+ if ((*cs)->ap_log_id[0] == '\0')
+ (void) strcpy((*cs)->ap_log_id, hp_name(node));
+
+ if ((*cs)->ap_info[0] == '\0') {
+ /* slot_names of bus node */
+ if (find_physical_slot_names(ap_id, &slotname_arg) != -1)
+ (void) strcpy((*cs)->ap_info,
+ slotname_arg.slotnames[slotname_arg.minor]);
+ }
+
+ /* class_code/subclass/boardtype */
+ get_type(boardtype, cardtype, (*cs)->ap_type);
+
+ DBG(1, ("cfga_list_ext return success\n"));
+ rv = CFGA_OK;
+
+ hp_fini(node);
+ return (rv);
+}
+
+/*
+ * This routine prints a single line of help message
+ */
+static void
+cfga_msg(struct cfga_msg *msgp, const char *str)
+{
+ DBG(2, ("<%s>", str));
+
+ if (msgp == NULL || msgp->message_routine == NULL)
+ return;
+
+ (*msgp->message_routine)(msgp->appdata_ptr, str);
+ (*msgp->message_routine)(msgp->appdata_ptr, "\n");
+}
+
+static cfga_err_t
+check_options(const char *options)
+{
+ struct cfga_msg *msgp = NULL;
+
+ if (options) {
+ cfga_msg(msgp, dgettext(TEXT_DOMAIN, cfga_strs[HELP_UNKNOWN]));
+ cfga_msg(msgp, options);
+ return (CFGA_INVAL);
+ }
+ return (CFGA_OK);
+}
+
+/*ARGSUSED*/
+cfga_err_t
+cfga_help(struct cfga_msg *msgp, const char *options, cfga_flags_t flags)
+{
+ if (options) {
+ cfga_msg(msgp, dgettext(TEXT_DOMAIN, cfga_strs[HELP_UNKNOWN]));
+ cfga_msg(msgp, options);
+ }
+ DBG(1, ("cfga_help\n"));
+
+ cfga_msg(msgp, dgettext(TEXT_DOMAIN, cfga_strs[HELP_HEADER]));
+ cfga_msg(msgp, cfga_strs[HELP_CONFIG]);
+ cfga_msg(msgp, cfga_strs[HELP_ENABLE_SLOT]);
+ cfga_msg(msgp, cfga_strs[HELP_DISABLE_SLOT]);
+ cfga_msg(msgp, cfga_strs[HELP_ENABLE_AUTOCONF]);
+ cfga_msg(msgp, cfga_strs[HELP_DISABLE_AUTOCONF]);
+ cfga_msg(msgp, cfga_strs[HELP_LED_CNTRL]);
+ return (CFGA_OK);
+}
+
+/*
+ * cfga_err() accepts a variable number of message IDs and constructs
+ * a corresponding error string which is returned via the errstring argument.
+ * cfga_err() calls gettext() to internationalize proper messages.
+ */
+static void
+cfga_err(char **errstring, ...)
+{
+ int a;
+ int i;
+ int n;
+ int len;
+ int flen;
+ char *p;
+ char *q;
+ char *s[32];
+ char *failed;
+ va_list ap;
+
+ /*
+ * If errstring is null it means user is not interested in getting
+ * error status. So we don't do all the work
+ */
+ if (errstring == NULL) {
+ return;
+ }
+ va_start(ap, errstring);
+
+ failed = dgettext(TEXT_DOMAIN, cfga_strs[FAILED]);
+ flen = strlen(failed);
+
+ for (n = len = 0; (a = va_arg(ap, int)) != 0; n++) {
+ switch (a) {
+ case CMD_GETSTAT:
+ case CMD_LIST:
+ case CMD_SLOT_CONNECT:
+ case CMD_SLOT_DISCONNECT:
+ case CMD_SLOT_CONFIGURE:
+ case CMD_SLOT_UNCONFIGURE:
+ p = cfga_errstrs(a);
+ len += (strlen(p) + flen);
+ s[n] = p;
+ s[++n] = cfga_strs[FAILED];
+
+ DBG(2, ("<%s>", p));
+ DBG(2, (cfga_strs[FAILED]));
+ break;
+
+ case ERR_CMD_INVAL:
+ case ERR_AP_INVAL:
+ case ERR_OPT_INVAL:
+ case ERR_AP_ERR:
+ switch (a) {
+ case ERR_CMD_INVAL:
+ p = dgettext(TEXT_DOMAIN,
+ cfga_errstrs[ERR_CMD_INVAL]);
+ break;
+ case ERR_AP_INVAL:
+ p = dgettext(TEXT_DOMAIN,
+ cfga_errstrs[ERR_AP_INVAL]);
+ break;
+ case ERR_OPT_INVAL:
+ p = dgettext(TEXT_DOMAIN,
+ cfga_errstrs[ERR_OPT_INVAL]);
+ break;
+ case ERR_AP_ERR:
+ p = dgettext(TEXT_DOMAIN,
+ cfga_errstrs[ERR_AP_ERR]);
+ break;
+ }
+
+ if ((q = va_arg(ap, char *)) != NULL) {
+ len += (strlen(p) + strlen(q));
+ s[n] = p;
+ s[++n] = q;
+ DBG(2, ("<%s>", p));
+ DBG(2, ("<%s>", q));
+ break;
+ } else {
+ len += strlen(p);
+ s[n] = p;
+
+ }
+ DBG(2, ("<%s>", p));
+ break;
+
+ default:
+ n--;
+ break;
+ }
+ }
+
+ DBG(2, ("\n"));
+ va_end(ap);
+
+ if ((p = calloc(len + 1, 1)) == NULL)
+ return;
+
+ for (i = 0; i < n; i++) {
+ (void) strlcat(p, s[i], len + 1);
+ DBG(2, ("i:%d, %s\n", i, s[i]));
+ }
+
+ *errstring = p;
+ DBG(2, ("%s\n", *errstring));
+}
+
+/*
+ * cfga_ap_id_cmp -- use default_ap_id_cmp() in libcfgadm
+ */
diff --git a/usr/src/lib/cfgadm_plugins/shp/i386/Makefile b/usr/src/lib/cfgadm_plugins/shp/i386/Makefile
new file mode 100644
index 0000000000..7879e9544a
--- /dev/null
+++ b/usr/src/lib/cfgadm_plugins/shp/i386/Makefile
@@ -0,0 +1,30 @@
+#
+# 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.
+#
+
+include ../Makefile.com
+
+.KEEP_STATE:
+
+install: all $(ROOTLIBS) $(ROOTLINKS)
diff --git a/usr/src/lib/cfgadm_plugins/shp/shp.xcl b/usr/src/lib/cfgadm_plugins/shp/shp.xcl
new file mode 100644
index 0000000000..cec2835a23
--- /dev/null
+++ b/usr/src/lib/cfgadm_plugins/shp/shp.xcl
@@ -0,0 +1,154 @@
+#
+# 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.
+#
+# lib/cfgadm_plugins/pci/pci.xcl
+#
+msgid "acquire "
+msgid "get-status "
+msgid "list "
+msgid "connect "
+msgid "disconnect "
+msgid "configure "
+msgid "unconfigure "
+msgid "insert "
+msgid "remove "
+msgid "open "
+msgid "fstat "
+msgid "\t-c [connect|disconnect|configure|unconfigure|insert|remove] "
+ "ap_id [ap_id...]"
+msgid "\t-x enable_slot ap_id [ap_id...]"
+msgid "\t-x disable_slot ap_id [ap_id...]"
+msgid "\t-x enable_autoconfig ap_id [ap_id...]"
+msgid "\t-x disable_autoconfig ap_id [ap_id...]"
+msgid "\t-x led[=[fault|power|active|attn],mode=[on|off|blink]] ap_id [ap_id...]"
+msgid "unknown"
+msgid "SCSI"
+msgid "IDE"
+msgid "FlpyDisk"
+msgid "IPI"
+msgid "RAID"
+msgid "MassStrg"
+msgid "Ethernet"
+msgid "TokenRg"
+msgid "FDDI"
+msgid "ATM"
+msgid "Network"
+msgid "VGA8514"
+msgid "xGA"
+msgid "Display"
+msgid "Vedio"
+msgid "Audio"
+msgid "MultiMda"
+msgid "Ram"
+msgid "Flash"
+msgid "Memory"
+msgid "Host-PCI"
+msgid "PCI-ISA"
+msgid "PCI-EISA"
+msgid "PCI-MC"
+msgid "PCI-PCI"
+msgid "PCI-MCIA"
+msgid "PCI-NuBs"
+msgid "PCI-Card"
+msgid "Bridge"
+msgid "???"
+msgid "HP"
+msgid "NHS"
+msgid "BHS"
+msgid "FHS"
+msgid "HS"
+msgid "enable_slot"
+msgid "disable_slot"
+msgid "enable_autoconfig"
+msgid "disable_autoconfig"
+msgid "led"
+msgid "mode"
+msgid "fault"
+msgid "power"
+msgid "attn"
+msgid "active"
+msgid "off"
+msgid "on"
+msgid "blink"
+msgid "slot"
+msgid "ap_idx(%s)\n"
+msgid "<%d>"
+msgid "%s"
+msgid "cfga_change_state:(%s)\n"
+msgid "cfga_change_state: state is %d\n"
+msgid "cfga_get_condition failed\n"
+msgid "cfga_get_state failed\n"
+msgid "cfga_get_state:(%x)\n"
+msgid "malloc failed\n"
+msgid "prt_led_mod function\n"
+msgid "Ap_Id\t\t\tLed\n"
+msgid "physpath2node failed\n"
+msgid "malloc "
+msgid "%s\t\t"
+msgid "%s=%s,"
+msgid "%s:%s\n"
+msgid "%s=%s\n"
+msgid "cfgadm_private_func: ap_id:%s\n"
+msgid " options: %s\n"
+msgid "null"
+msgid " confp: %x\n"
+msgid " cfga_msg: %x\n"
+msgid " flag: %d\n"
+msgid "%c\n"
+msgid "buf = %s\n"
+msgid "buf_mode= %s\n"
+msgid "Print mode\n"
+msgid "default\n"
+msgid "open"
+msgid "cfga_test:(%s)\n"
+msgid "/devices%s:%s"
+msgid "slot-names"
+msgid "di_prom_prop_lookup_ints"
+msgid "slot-names:"
+ " %s \n"
+msgid "/"
+msgid "di_init() failed\n"
+msgid "di_prom_init() failed\n"
+msgid "ddi_ctl:attachment_point:pci"
+msgid "MULT"
+msgid "cfga_stat:(%d)\n"
+msgid "ap_rstate = CFGA_STAT_EMPTY\n"
+msgid "ap_rstate = CFGA_STAT_DISCONNECTED\n"
+msgid "ap_rstate = CFGA_STAT_CONNECTED\n"
+msgid "ap_ostate = CFGA_STAT_CONFIGURED\n"
+msgid "ap_ostate = CFGA_STAT_UNCONFIGURED\n"
+msgid "ap_cond = CFGA_COND_OK\n"
+msgid "ap_cond = CFGA_COND_FAILING\n"
+msgid "ap_cond = CFGA_COND_FAILED\n"
+msgid "ap_cond = CFGA_COND_UNUSABLE\n"
+msgid "ap_cond = CFGA_COND_UNKNOW\n"
+msgid "cfga_stat return success\n"
+msgid "cfga_list:(%s)\n"
+msgid "cfga_help\n"
+msgid "<%s>"
+msgid "\n"
+msgid "i:%d, %s\n"
+msgid "%s\n"
+msgid "rpath=%s,cp=%s\n"
+
diff --git a/usr/src/lib/cfgadm_plugins/shp/sparc/Makefile b/usr/src/lib/cfgadm_plugins/shp/sparc/Makefile
new file mode 100644
index 0000000000..7879e9544a
--- /dev/null
+++ b/usr/src/lib/cfgadm_plugins/shp/sparc/Makefile
@@ -0,0 +1,30 @@
+#
+# 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.
+#
+
+include ../Makefile.com
+
+.KEEP_STATE:
+
+install: all $(ROOTLIBS) $(ROOTLINKS)
diff --git a/usr/src/lib/cfgadm_plugins/shp/sparcv9/Makefile b/usr/src/lib/cfgadm_plugins/shp/sparcv9/Makefile
new file mode 100644
index 0000000000..ad23eac706
--- /dev/null
+++ b/usr/src/lib/cfgadm_plugins/shp/sparcv9/Makefile
@@ -0,0 +1,31 @@
+#
+# 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.
+#
+
+include ../Makefile.com
+include ../../../Makefile.lib.64
+
+.KEEP_STATE:
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64)
diff --git a/usr/src/lib/libbsm/audit_event.txt b/usr/src/lib/libbsm/audit_event.txt
index c632d11771..219ca21a69 100644
--- a/usr/src/lib/libbsm/audit_event.txt
+++ b/usr/src/lib/libbsm/audit_event.txt
@@ -533,6 +533,11 @@
6417:AUE_tpm_physicalpresence:set the TPM physical presence flag:as
6418:AUE_tpm_fieldupgrade:update TPM protected capabilities:as
6419:AUE_tpm_resetlockvalue:reset TPM failed authorization attempt lock:as
+#
+# hotplugd(1m) events
+#
+6500:AUE_hotplug_state:change hotplug connection state:ss
+6501:AUE_hotplug_set:set hotplug bus private options:ss
#
# Trusted Extensions events:
diff --git a/usr/src/lib/libbsm/common/adt.xml b/usr/src/lib/libbsm/common/adt.xml
index c58ad56398..8ab6e24422 100644
--- a/usr/src/lib/libbsm/common/adt.xml
+++ b/usr/src/lib/libbsm/common/adt.xml
@@ -2016,8 +2016,83 @@ Use is subject to license terms.
<see>tcsd(8)</see>
</event>
+<!-- hotplug events recorded by hotplugd(1m) -->
+
+ <event id="AUE_hotplug_state" header="0" idNo="117" omit="JNI">
+ <title>change hotplug connection state</title>
+ <program>/usr/lib/hotplugd</program>
+ <see>hotplugd(1M)</see>
+ <entry id="subject">
+ <internal token="subject"/>
+ <external opt="none"/>
+ </entry>
+ <entry id="auth_used">
+ <internal token="uauth"/>
+ <external opt="required" type="char *"/>
+ <comment>authorization used</comment>
+ </entry>
+ <entry id="device_path">
+ <internal token="path"/>
+ <external opt="required" type="char *"/>
+ <comment>device path</comment>
+ </entry>
+ <entry id="connection">
+ <internal token="text"/>
+ <external opt="required" type="char *"/>
+ <comment>connector or port</comment>
+ </entry>
+ <entry id="new_state">
+ <internal token="text"/>
+ <external opt="required" type="char *"/>
+ <comment>new connection state</comment>
+ </entry>
+ <entry id="old_state">
+ <internal token="text"/>
+ <external opt="required" type="char *"/>
+ <comment>old connection state</comment>
+ </entry>
+ <entry id="return">
+ <internal token="return"/>
+ <external opt="none"/>
+ </entry>
+ </event>
+
+ <event id="AUE_hotplug_set" header="0" idNo="118" omit="JNI">
+ <title>set hotplug bus private options</title>
+ <program>/usr/lib/hotplugd</program>
+ <see>hotplugd(1M)</see>
+ <entry id="subject">
+ <internal token="subject"/>
+ <external opt="none"/>
+ </entry>
+ <entry id="auth_used">
+ <internal token="uauth"/>
+ <external opt="required" type="char *"/>
+ <comment>authorization used</comment>
+ </entry>
+ <entry id="device_path">
+ <internal token="path"/>
+ <external opt="required" type="char *"/>
+ <comment>device path</comment>
+ </entry>
+ <entry id="connection">
+ <internal token="text"/>
+ <external opt="required" type="char *"/>
+ <comment>connector or port</comment>
+ </entry>
+ <entry id="options">
+ <internal token="text"/>
+ <external opt="required" type="char *"/>
+ <comment>bus private options</comment>
+ </entry>
+ <entry id="return">
+ <internal token="return"/>
+ <external opt="none"/>
+ </entry>
+ </event>
+
<!-- add new events here with the next higher idNo -->
-<!-- Highest idNo is 116, so next is 117, then fix this comment -->
+<!-- Highest idNo is 119, so next is 120, then fix this comment -->
<!-- end of C Only events -->
<!--
diff --git a/usr/src/lib/libcfgadm/common/config_admin.c b/usr/src/lib/libcfgadm/common/config_admin.c
index dc4632e202..d642fa820b 100644
--- a/usr/src/lib/libcfgadm/common/config_admin.c
+++ b/usr/src/lib/libcfgadm/common/config_admin.c
@@ -22,12 +22,10 @@
/* Portions Copyright 2005 Cyril Plisko */
/*
- * 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 <errno.h>
#include <stdio.h>
#include <stdlib.h>
@@ -177,6 +175,7 @@ typedef struct list_stat {
stat_data_list_t *sdl; /* Linked list of stat structures */
array_list_t *al; /* Linked list of arrays of list structures */
vers_req_t use_vers; /* plugin versions to be stat'ed */
+ char *shp_errstr; /* only for shp plugin */
} list_stat_t;
/*
@@ -205,13 +204,24 @@ static void *config_calloc_check(size_t, size_t, char **);
static cfga_err_t resolve_lib_ref(plugin_lib_t *, lib_loc_t *);
static cfga_err_t config_get_lib(const char *, lib_loc_t *, char **);
static int check_ap(di_node_t, di_minor_t, void *);
+static int check_ap_hp(di_node_t, di_hp_t, void *);
+static int check_ap_impl(di_node_t, di_minor_t, di_hp_t, void *);
static int check_ap_phys(di_node_t, di_minor_t, void *);
+static int check_ap_phys_hp(di_node_t, di_hp_t, void *);
+static int check_ap_phys_impl(di_node_t, di_minor_t, di_hp_t, void *);
static cfga_err_t find_ap_common(lib_loc_t *libloc_p, const char *rootpath,
- int (*fcn)(di_node_t node, di_minor_t minor, void *arg), char **errstring);
+ int (*fcn)(di_node_t node, di_minor_t minor, void *arg),
+ int (*fcn_hp)(di_node_t node, di_hp_t hp, void *arg),
+ char **errstring);
static plugin_lib_t *lib_in_list(char *);
+static cfga_err_t find_lib(di_node_t, di_minor_t, lib_loc_t *);
+static cfga_err_t find_lib_hp(di_node_t, di_hp_t, lib_loc_t *);
+static cfga_err_t find_lib_impl(char *, lib_loc_t *);
static cfga_err_t load_lib(di_node_t, di_minor_t, lib_loc_t *);
+static cfga_err_t load_lib_hp(di_node_t, di_hp_t, lib_loc_t *);
+static cfga_err_t load_lib_impl(di_node_t, di_minor_t, di_hp_t, lib_loc_t *);
extern void bcopy(const void *, void *, size_t);
static void config_err(int, int, char **);
static void hold_lib(plugin_lib_t *);
@@ -222,6 +232,9 @@ static cfga_err_t parse_listopt(char *listopts, char **classpp,
static cfga_err_t list_common(list_stat_t *lstatp, const char *class);
static int do_list_common(di_node_t node, di_minor_t minor, void *arg);
+static int do_list_common_hp(di_node_t node, di_hp_t hp, void *arg);
+static int do_list_common_impl(di_node_t node, di_minor_t minor,
+ di_hp_t hp, void *arg);
static cfga_err_t stat_common(int num_ap_ids, char *const *ap_ids,
const char *class, list_stat_t *lstatp);
@@ -575,6 +588,7 @@ config_stat(
lstat.countp = &nstat;
lstat.opts = options;
lstat.errstr = errstring;
+ lstat.shp_errstr = NULL;
/*
* This is a V1 interface which can use only V1 plugins
*/
@@ -611,6 +625,7 @@ config_list(
lstat.countp = &nstat;
lstat.opts = options;
lstat.errstr = errstring;
+ lstat.shp_errstr = NULL;
/*
* This is a V1 interface which can use only V1 plugins
*/
@@ -683,6 +698,7 @@ config_list_ext(
lstat.countp = &nstat;
lstat.opts = options;
lstat.errstr = errstring;
+ lstat.shp_errstr = NULL;
lstat.flags = flags;
/*
* We support both V1 and V2 plugins through this entry
@@ -1127,6 +1143,35 @@ mklog_common(
}
/*
+ * mklog_common - make a logical name from the driver and instance
+ */
+/*ARGSUSED*/
+static cfga_err_t
+mklog_hp(
+ di_node_t node,
+ di_hp_t hp,
+ plugin_lib_t *libp,
+ lib_loc_t *liblocp)
+{
+ const size_t len = CFGA_LOG_EXT_LEN;
+ int inst;
+ char *drv, *hp_name;
+
+ drv = di_driver_name(node);
+ inst = di_instance(node);
+ hp_name = di_hp_name(hp);
+
+ errno = 0;
+ if (drv != NULL && inst != -1 && hp_name != NULL &&
+ snprintf(liblocp->ap_logical, len, "%s%d:%s", drv, inst,
+ hp_name) < len) { /* snprintf returns strlen */
+ return (CFGA_OK);
+ }
+
+ return (CFGA_LIB_ERROR);
+}
+
+/*
* resolve_lib_ref - relocate to use plugin lib
*/
static cfga_err_t
@@ -1352,6 +1397,9 @@ config_get_lib(
/*
* find and load the library
* The base component of the ap_id is used to locate the plug-in
+ *
+ * NOTE that PCIE/PCISHPC connectors also have minor nodes &
+ * dev links created for now.
*/
if ((type = find_arg_type(lib_loc_p->ap_base)) == PHYSICAL_AP) {
/*
@@ -1360,14 +1408,15 @@ config_get_lib(
* ap_id.
*/
ret = find_ap_common(lib_loc_p, lib_loc_p->ap_base,
- check_ap_phys, errstring);
+ check_ap_phys, check_ap_phys_hp, errstring);
} else if ((type == LOGICAL_DRV_AP) ||
(type == AP_TYPE && dyncomp == NULL)) {
/*
* logical ap_id or ap_type: Use "/" as root for tree walk
* Note: an aptype cannot have a dynamic component
*/
- ret = find_ap_common(lib_loc_p, "/", check_ap, errstring);
+ ret = find_ap_common(lib_loc_p, "/", check_ap,
+ check_ap_hp, errstring);
} else {
ret = CFGA_APID_NOEXIST;
}
@@ -1418,20 +1467,49 @@ out:
return (ret);
}
+/* load_lib - load library for non-SHP attachment point node */
+static cfga_err_t
+load_lib(
+ di_node_t node,
+ di_minor_t minor,
+ lib_loc_t *libloc_p)
+{
+ return (load_lib_impl(node, minor, NULL, libloc_p));
+}
+
+/* load_lib_hp - load library for SHP attachment point node */
+static cfga_err_t
+load_lib_hp(
+ di_node_t node,
+ di_hp_t hp,
+ lib_loc_t *libloc_p)
+{
+ return (load_lib_impl(node, NULL, hp, libloc_p));
+}
/*
- * load_lib - Given a library pathname, create a entry for it
- * in the library list, if one does not already exist, and read
+ * load_lib_impl - Given a library pathname, create a entry for it
+ * in the library list, * if one does not already exist, and read
* lock it to keep it there.
*/
static cfga_err_t
-load_lib(
+load_lib_impl(
di_node_t node,
di_minor_t minor,
+ di_hp_t hp,
lib_loc_t *libloc_p)
{
plugin_lib_t *libp, *list_libp;
char *devfs_path;
+ char *name;
+
+ if (minor != DI_MINOR_NIL && hp != DI_HP_NIL)
+ return (CFGA_LIB_ERROR);
+
+ if (minor != DI_MINOR_NIL)
+ name = di_minor_name(minor);
+ else
+ name = di_hp_name(hp);
/*
* lock the library list
@@ -1448,15 +1526,22 @@ load_lib(
/* fill in logical and physical name in libloc_p */
libloc_p->libp = libp = list_libp;
- if (libp->vers_ops->mklog(node, minor, libp, libloc_p)
- != CFGA_OK) {
- rele_lib(list_libp);
- return (CFGA_LIB_ERROR);
+ if (minor != DI_MINOR_NIL) {
+ if (libp->vers_ops->mklog(node, minor, libp, libloc_p)
+ != CFGA_OK) {
+ rele_lib(list_libp);
+ return (CFGA_LIB_ERROR);
+ }
+ } else {
+ if (mklog_hp(node, hp, libp, libloc_p) != CFGA_OK) {
+ rele_lib(list_libp);
+ return (CFGA_LIB_ERROR);
+ }
}
devfs_path = di_devfs_path(node);
(void) snprintf(libloc_p->ap_physical, MAXPATHLEN, "%s%s:%s",
- DEVICES_DIR, devfs_path, di_minor_name(minor));
+ DEVICES_DIR, devfs_path, name);
di_devfs_path_free(devfs_path);
return (CFGA_OK);
@@ -1482,12 +1567,23 @@ load_lib(
return (CFGA_NO_LIB);
}
- if (resolve_lib_ref(libp, libloc_p) != CFGA_OK ||
- libp->vers_ops->mklog(node, minor, libp, libloc_p) != CFGA_OK) {
- (void) mutex_unlock(&plugin_list_lock);
- (void) dlclose(libp->handle);
- free(libp);
- return (CFGA_NO_LIB);
+ if (minor != DI_MINOR_NIL) {
+ if (resolve_lib_ref(libp, libloc_p) != CFGA_OK ||
+ libp->vers_ops->mklog(node, minor, libp, libloc_p)
+ != CFGA_OK) {
+ (void) mutex_unlock(&plugin_list_lock);
+ (void) dlclose(libp->handle);
+ free(libp);
+ return (CFGA_NO_LIB);
+ }
+ } else {
+ if (resolve_lib_ref(libp, libloc_p) != CFGA_OK ||
+ mklog_hp(node, hp, libp, libloc_p) != CFGA_OK) {
+ (void) mutex_unlock(&plugin_list_lock);
+ (void) dlclose(libp->handle);
+ free(libp);
+ return (CFGA_NO_LIB);
+ }
}
/*
@@ -1511,7 +1607,7 @@ load_lib(
libloc_p->libp = libp;
devfs_path = di_devfs_path(node);
(void) snprintf(libloc_p->ap_physical, MAXPATHLEN, "%s%s:%s",
- DEVICES_DIR, devfs_path, di_minor_name(minor));
+ DEVICES_DIR, devfs_path, name);
di_devfs_path_free(devfs_path);
return (CFGA_OK);
@@ -1521,8 +1617,7 @@ load_lib(
#define NUM_LIB_NAMES 2
/*
- * find_lib - Given an attachment point node find it's library
- *
+ * find_lib - find library for non-SHP attachment point node
*/
static cfga_err_t
find_lib(
@@ -1530,34 +1625,13 @@ find_lib(
di_minor_t minor,
lib_loc_t *libloc_p)
{
- char lib[MAXPATHLEN];
char name[NUM_LIB_NAMES][MAXPATHLEN];
- struct stat lib_stat;
- void *dlhandle = NULL;
- static char plat_name[SYSINFO_LENGTH];
- static char machine_name[SYSINFO_LENGTH];
- static char arch_name[SYSINFO_LENGTH];
- int i = 0;
char *class = NULL, *drv = NULL;
+ int i;
/* Make sure pathname and class is null if we fail */
- *libloc_p->ap_class = *libloc_p->pathname = *lib = '\0';
-
- /*
- * Initialize machine name and arch name
- */
- if (strncmp("", machine_name, MAXPATHLEN) == 0) {
- if (sysinfo(SI_PLATFORM, plat_name, SYSINFO_LENGTH) == -1) {
- return (CFGA_ERROR);
- }
- if (sysinfo(SI_ARCHITECTURE, arch_name, SYSINFO_LENGTH) == -1) {
- return (CFGA_ERROR);
- }
- if (sysinfo(SI_MACHINE, machine_name, SYSINFO_LENGTH) == -1) {
- return (CFGA_ERROR);
- }
- }
+ *libloc_p->ap_class = *libloc_p->pathname = '\0';
/*
* Initialize possible library tags.
@@ -1584,72 +1658,174 @@ find_lib(
continue;
}
- /*
- * Try path based upon platform name
- */
- (void) snprintf(lib, sizeof (lib), "%s%s%s%s%s",
- LIB_PATH_BASE1, plat_name, LIB_PATH_MIDDLE,
- name[i], LIB_PATH_TAIL);
-
- if (stat(lib, &lib_stat) == 0) {
- /* file exists, is it a lib */
- dlhandle = dlopen(lib, RTLD_LAZY);
- if (dlhandle != NULL) {
- goto found;
- }
- }
+ if (find_lib_impl(name[i], libloc_p) == CFGA_OK)
+ goto found;
+ }
- /*
- * Try path based upon machine name
- */
- (void) snprintf(lib, sizeof (lib), "%s%s%s%s%s",
- LIB_PATH_BASE1, machine_name, LIB_PATH_MIDDLE,
- name[i], LIB_PATH_TAIL);
+ return (CFGA_NO_LIB);
+found:
- if (stat(lib, &lib_stat) == 0) {
- /* file exists, is it a lib */
- dlhandle = dlopen(lib, RTLD_LAZY);
- if (dlhandle != NULL) {
- goto found;
- }
- }
+ /* Record class name (if any) */
+ (void) snprintf(libloc_p->ap_class, sizeof (libloc_p->ap_class), "%s",
+ class);
+
+ return (CFGA_OK);
+}
+
+/*
+ * find_lib_hp - find library for SHP attachment point
+ */
+/*ARGSUSED*/
+static cfga_err_t
+find_lib_hp(
+ di_node_t node,
+ di_hp_t hp,
+ lib_loc_t *libloc_p)
+{
+ char name[MAXPATHLEN];
+ char *class = NULL;
- /*
- * Try path based upon arch name
- */
- (void) snprintf(lib, sizeof (lib), "%s%s%s%s%s",
- LIB_PATH_BASE1, arch_name, LIB_PATH_MIDDLE,
- name[i], LIB_PATH_TAIL);
-
- if (stat(lib, &lib_stat) == 0) {
- /* file exists, is it a lib */
- dlhandle = dlopen(lib, RTLD_LAZY);
- if (dlhandle != NULL) {
- goto found;
- }
+ /* Make sure pathname and class is null if we fail */
+ *libloc_p->ap_class = *libloc_p->pathname = '\0';
+
+ /*
+ * Initialize possible library tags.
+ *
+ * Only support PCI class for now, this will need to be
+ * changed as other plugins are migrated to SHP plugin.
+ */
+ class = "pci";
+#if 0
+ /*
+ * No type check for now as PCI is the only class SHP plugin
+ * supports. In the future we'll need to enable the type check
+ * and set class accordingly, when non PCI plugins are migrated
+ * to SHP. In that case we'll probably need to add an additional
+ * interface between libcfgadm and the plugins, and SHP plugin will
+ * implement this interface which will translate the bus specific
+ * strings to standard classes that libcfgadm can recognize, for
+ * all the buses it supports, e.g. for pci/pcie it will translate
+ * PCIE_NATIVE_HP_TYPE to string "pci". We'll also need to bump up
+ * SHP plugin version to 3 to use the new interface.
+ */
+ class = di_hp_type(hp);
+ if ((strcmp(class, PCIE_NATIVE_HP_TYPE) == 0) ||
+ (strcmp(class, PCIE_ACPI_HP_TYPE) == 0) ||
+ (strcmp(class, PCIE_PCI_HP_TYPE) == 0)) {
+ class = "pci";
+ } else {
+ goto fail;
+ }
+#endif
+ (void) snprintf(&name[0], sizeof (name), "%s", "shp");
+
+ if (find_lib_impl(name, libloc_p) == CFGA_OK)
+ goto found;
+fail:
+ return (CFGA_NO_LIB);
+
+found:
+
+ /* Record class name (if any) */
+ (void) snprintf(libloc_p->ap_class, sizeof (libloc_p->ap_class), "%s",
+ class);
+
+ return (CFGA_OK);
+}
+
+/*
+ * find_lib_impl - Given an attachment point node find it's library
+ */
+static cfga_err_t
+find_lib_impl(
+ char *name,
+ lib_loc_t *libloc_p)
+{
+ char lib[MAXPATHLEN];
+ struct stat lib_stat;
+ void *dlhandle = NULL;
+ static char plat_name[SYSINFO_LENGTH];
+ static char machine_name[SYSINFO_LENGTH];
+ static char arch_name[SYSINFO_LENGTH];
+
+ /*
+ * Initialize machine name and arch name
+ */
+ if (strncmp("", machine_name, MAXPATHLEN) == 0) {
+ if (sysinfo(SI_PLATFORM, plat_name, SYSINFO_LENGTH) == -1) {
+ return (CFGA_ERROR);
+ }
+ if (sysinfo(SI_ARCHITECTURE, arch_name, SYSINFO_LENGTH) == -1) {
+ return (CFGA_ERROR);
}
+ if (sysinfo(SI_MACHINE, machine_name, SYSINFO_LENGTH) == -1) {
+ return (CFGA_ERROR);
+ }
+ }
- /*
- * Try generic location
- */
- (void) snprintf(lib, sizeof (lib), "%s%s%s%s",
- LIB_PATH_BASE2, LIB_PATH_MIDDLE, name[i], LIB_PATH_TAIL);
+ /*
+ * Try path based upon platform name
+ */
+ (void) snprintf(lib, sizeof (lib), "%s%s%s%s%s",
+ LIB_PATH_BASE1, plat_name, LIB_PATH_MIDDLE,
+ name, LIB_PATH_TAIL);
+
+ if (stat(lib, &lib_stat) == 0) {
+ /* file exists, is it a lib */
+ dlhandle = dlopen(lib, RTLD_LAZY);
+ if (dlhandle != NULL) {
+ goto found;
+ }
+ }
+ /*
+ * Try path based upon machine name
+ */
+ (void) snprintf(lib, sizeof (lib), "%s%s%s%s%s",
+ LIB_PATH_BASE1, machine_name, LIB_PATH_MIDDLE,
+ name, LIB_PATH_TAIL);
- if (stat(lib, &lib_stat) == 0) {
- /* file exists, is it a lib */
- dlhandle = dlopen(lib, RTLD_LAZY);
- if (dlhandle != NULL) {
- goto found;
- }
+ if (stat(lib, &lib_stat) == 0) {
+ /* file exists, is it a lib */
+ dlhandle = dlopen(lib, RTLD_LAZY);
+ if (dlhandle != NULL) {
+ goto found;
+ }
+ }
+ /*
+ * Try path based upon arch name
+ */
+ (void) snprintf(lib, sizeof (lib), "%s%s%s%s%s",
+ LIB_PATH_BASE1, arch_name, LIB_PATH_MIDDLE,
+ name, LIB_PATH_TAIL);
+
+ if (stat(lib, &lib_stat) == 0) {
+ /* file exists, is it a lib */
+ dlhandle = dlopen(lib, RTLD_LAZY);
+ if (dlhandle != NULL) {
+ goto found;
}
+
}
+ /*
+ * Try generic location
+ */
+ (void) snprintf(lib, sizeof (lib), "%s%s%s%s",
+ LIB_PATH_BASE2, LIB_PATH_MIDDLE, name, LIB_PATH_TAIL);
+
+ if (stat(lib, &lib_stat) == 0) {
+ /* file exists, is it a lib */
+ dlhandle = dlopen(lib, RTLD_LAZY);
+ if (dlhandle != NULL) {
+ goto found;
+ }
+ }
return (CFGA_NO_LIB);
found:
@@ -1659,10 +1835,6 @@ found:
(void) dlclose(dlhandle);
- /* Record class name (if any) */
- (void) snprintf(libloc_p->ap_class, sizeof (libloc_p->ap_class), "%s",
- class);
-
return (CFGA_OK);
}
@@ -1746,6 +1918,7 @@ find_ap_common(
lib_loc_t *libloc_p,
const char *physpath,
int (*fcn)(di_node_t node, di_minor_t minor, void *arg),
+ int (*fcn_hp)(di_node_t node, di_hp_t hp, void *arg),
char **errstring)
{
di_node_t rnode, wnode;
@@ -1781,37 +1954,84 @@ find_ap_common(
/*
* begin walk of device tree
+ *
+ * Since we create minor nodes & dev links for both all PCI/PCIE
+ * connectors, but only create hp nodes for PCIE/PCISHPC connectors
+ * of the new framework, we should first match with hp nodes. If
+ * the ap_id refers to a PCIE/PCISHPC connector, we'll be able to
+ * find it here.
*/
- rnode = di_init("/", DINFOCACHE);
+ rnode = di_init("/", DINFOSUBTREE | DINFOHP);
if (rnode)
wnode = di_lookup_node(rnode, rpath);
else
wnode = DI_NODE_NIL;
- S_FREE(rpath);
if (wnode == DI_NODE_NIL) {
if (rnode == DI_NODE_NIL) {
+ S_FREE(rpath);
config_err(errno, DI_INIT_FAILED, errstring);
return (CFGA_LIB_ERROR);
} else {
/*
- * di_lookup_node() may fail because the ap_id
- * does not exist
+ * di_lookup_node() may fail, either because the
+ * ap_id does not exist, or because the ap_id refers
+ * to a legacy PCI slot, thus we'll not able to
+ * find node using DINFOHP, try to see if we can
+ * find one using DINFOCACHE.
*/
di_fini(rnode);
- return (CFGA_APID_NOEXIST);
+ goto find_minor;
}
}
libloc_p->libp = NULL;
libloc_p->status = CFGA_APID_NOEXIST;
- (void) di_walk_minor(wnode, "ddi_ctl:attachment_point",
- DI_CHECK_ALIAS|DI_CHECK_INTERNAL_PATH,
- libloc_p, fcn);
+ (void) di_walk_hp(wnode, NULL, DI_HP_CONNECTOR,
+ libloc_p, fcn_hp);
di_fini(rnode);
+ /*
+ * Failed to find a matching hp node, try minor node.
+ */
+ if (libloc_p->libp == NULL) {
+find_minor:
+ rnode = di_init("/", DINFOCACHE);
+ if (rnode)
+ wnode = di_lookup_node(rnode, rpath);
+ else
+ wnode = DI_NODE_NIL;
+
+ if (wnode == DI_NODE_NIL) {
+ if (rnode == DI_NODE_NIL) {
+ S_FREE(rpath);
+ config_err(errno, DI_INIT_FAILED, errstring);
+ return (CFGA_LIB_ERROR);
+ } else {
+ /*
+ * di_lookup_node() may fail, because the
+ * ap_id does not exist.
+ */
+ S_FREE(rpath);
+ di_fini(rnode);
+ return (CFGA_APID_NOEXIST);
+ }
+ }
+
+ libloc_p->libp = NULL;
+ libloc_p->status = CFGA_APID_NOEXIST;
+
+ (void) di_walk_minor(wnode, "ddi_ctl:attachment_point",
+ DI_CHECK_ALIAS|DI_CHECK_INTERNAL_PATH,
+ libloc_p, fcn);
+
+ di_fini(rnode);
+ }
+
+ S_FREE(rpath);
+
if (libloc_p->libp != NULL) {
update_cache(libloc_p);
return (CFGA_OK);
@@ -1821,18 +2041,42 @@ find_ap_common(
}
/*
- * check_ap - called for each attachment point found
+ * check_ap - called for each non-SHP attachment point found
+ */
+static int
+check_ap(
+ di_node_t node,
+ di_minor_t minor,
+ void *arg)
+{
+ return (check_ap_impl(node, minor, NULL, arg));
+}
+
+/*
+ * check_ap_hp - called for each SHP attachment point found
+ */
+static int
+check_ap_hp(
+ di_node_t node,
+ di_hp_t hp,
+ void *arg)
+{
+ return (check_ap_impl(node, NULL, hp, arg));
+}
+
+/*
+ * check_ap_impl - called for each attachment point found
*
* This is used in cases where a particular attachment point
* or type of attachment point is specified via a logical name or ap_type.
* Not used for physical names or in the list case with no
* ap's specified.
*/
-
static int
-check_ap(
+check_ap_impl(
di_node_t node,
di_minor_t minor,
+ di_hp_t hp,
void *arg)
{
char *cp = NULL;
@@ -1847,6 +2091,8 @@ check_ap(
int instance;
cfga_ap_types_t type;
+ if (minor != DI_MINOR_NIL && hp != DI_HP_NIL)
+ return (DI_WALK_CONTINUE);
libloc_p = (lib_loc_t *)arg;
@@ -1873,7 +2119,11 @@ check_ap(
return (DI_WALK_CONTINUE);
}
- node_minor = di_minor_name(minor);
+ if (minor != DI_MINOR_NIL)
+ node_minor = di_minor_name(minor);
+ else
+ node_minor = di_hp_name(hp);
+
drv_name = di_driver_name(node);
instance = di_instance(node);
@@ -1910,13 +2160,24 @@ check_ap(
/*
* save the correct type of error so user does not get confused
*/
- if (find_lib(node, minor, libloc_p) != CFGA_OK) {
- libloc_p->status = CFGA_NO_LIB;
- return (DI_WALK_CONTINUE);
- }
- if (load_lib(node, minor, libloc_p) != CFGA_OK) {
- libloc_p->status = CFGA_LIB_ERROR;
- return (DI_WALK_CONTINUE);
+ if (minor != DI_MINOR_NIL) {
+ if (find_lib(node, minor, libloc_p) != CFGA_OK) {
+ libloc_p->status = CFGA_NO_LIB;
+ return (DI_WALK_CONTINUE);
+ }
+ if (load_lib(node, minor, libloc_p) != CFGA_OK) {
+ libloc_p->status = CFGA_LIB_ERROR;
+ return (DI_WALK_CONTINUE);
+ }
+ } else {
+ if (find_lib_hp(node, hp, libloc_p) != CFGA_OK) {
+ libloc_p->status = CFGA_NO_LIB;
+ return (DI_WALK_CONTINUE);
+ }
+ if (load_lib_hp(node, hp, libloc_p) != CFGA_OK) {
+ libloc_p->status = CFGA_LIB_ERROR;
+ return (DI_WALK_CONTINUE);
+ }
}
libloc_p->status = CFGA_OK;
return (DI_WALK_TERMINATE);
@@ -1928,17 +2189,41 @@ check_ap(
/*
- * check_ap_phys - called for each attachment point found
+ * check_ap_phys - called for each non-SHP attachment point found
+ */
+static int
+check_ap_phys(
+ di_node_t node,
+ di_minor_t minor,
+ void *arg)
+{
+ return (check_ap_phys_impl(node, minor, DI_HP_NIL, arg));
+}
+
+/*
+ * check_ap_phys_hp - called for each SHP attachment point found
+ */
+static int
+check_ap_phys_hp(
+ di_node_t node,
+ di_hp_t hp,
+ void *arg)
+{
+ return (check_ap_phys_impl(node, DI_HP_NIL, hp, arg));
+}
+
+/*
+ * check_ap_phys_impl - called for each attachment point found
*
* This is used in cases where a particular attachment point
* is specified via a physical name. If the name matches then
* we try and find and load the library for it.
*/
-
static int
-check_ap_phys(
+check_ap_phys_impl(
di_node_t node,
di_minor_t minor,
+ di_hp_t hp,
void *arg)
{
lib_loc_t *libloc_p;
@@ -1946,9 +2231,15 @@ check_ap_phys(
char *devfs_path;
char *minor_name;
+ if (minor != DI_MINOR_NIL && hp != DI_HP_NIL)
+ return (DI_WALK_CONTINUE);
+
libloc_p = (lib_loc_t *)arg;
devfs_path = di_devfs_path(node);
- minor_name = di_minor_name(minor);
+ if (minor != DI_MINOR_NIL)
+ minor_name = di_minor_name(minor);
+ else
+ minor_name = di_hp_name(hp);
if (devfs_path == NULL || minor_name == NULL) {
libloc_p->status = CFGA_APID_NOEXIST;
@@ -1961,14 +2252,26 @@ check_ap_phys(
di_devfs_path_free(devfs_path);
if (strcmp(phys_name, libloc_p->ap_base) == 0) {
- if (find_lib(node, minor, libloc_p) != CFGA_OK) {
- libloc_p->status = CFGA_NO_LIB;
- return (DI_WALK_CONTINUE);
- }
- if (load_lib(node, minor, libloc_p) != CFGA_OK) {
- libloc_p->status = CFGA_LIB_ERROR;
- return (DI_WALK_CONTINUE);
+ if (minor != DI_MINOR_NIL) {
+ if (find_lib(node, minor, libloc_p) != CFGA_OK) {
+ libloc_p->status = CFGA_NO_LIB;
+ return (DI_WALK_CONTINUE);
+ }
+ if (load_lib(node, minor, libloc_p) != CFGA_OK) {
+ libloc_p->status = CFGA_LIB_ERROR;
+ return (DI_WALK_CONTINUE);
+ }
+ } else {
+ if (find_lib_hp(node, hp, libloc_p) != CFGA_OK) {
+ libloc_p->status = CFGA_NO_LIB;
+ return (DI_WALK_CONTINUE);
+ }
+ if (load_lib_hp(node, hp, libloc_p) != CFGA_OK) {
+ libloc_p->status = CFGA_LIB_ERROR;
+ return (DI_WALK_CONTINUE);
+ }
}
+
libloc_p->status = CFGA_OK;
return (DI_WALK_TERMINATE);
} else {
@@ -2167,15 +2470,6 @@ list_common(list_stat_t *lstatp, const char *class)
char nodetype[MAXPATHLEN];
const char *l_class, *l_sep;
-
- /*
- * begin walk of device tree
- */
- if ((rnode = di_init("/", DINFOCACHE)) == DI_NODE_NIL) {
- config_err(errno, DI_INIT_FAILED, lstatp->errstr);
- return (CFGA_LIB_ERROR);
- }
-
/*
* May walk a subset of all attachment points in the device tree if
* a class is specified
@@ -2190,10 +2484,38 @@ list_common(list_stat_t *lstatp, const char *class)
(void) snprintf(nodetype, sizeof (nodetype), "%s%s%s",
DDI_NT_ATTACHMENT_POINT, l_sep, l_class);
+ /*
+ * Walk all hp nodes
+ */
+ if ((rnode = di_init("/", DINFOSUBTREE | DINFOHP)) == DI_NODE_NIL) {
+ config_err(errno, DI_INIT_FAILED, lstatp->errstr);
+ return (CFGA_LIB_ERROR);
+ }
+ /* No need to filter on class for now */
+ (void) di_walk_hp(rnode, NULL, DI_HP_CONNECTOR,
+ lstatp, do_list_common_hp);
+
+ di_fini(rnode);
+
+ /*
+ * Walk all minor nodes
+ * but exclude PCIE/PCIESHPC connectors which have been walked above.
+ */
+ if ((rnode = di_init("/", DINFOCACHE)) == DI_NODE_NIL) {
+ config_err(errno, DI_INIT_FAILED, lstatp->errstr);
+ return (CFGA_LIB_ERROR);
+ }
(void) di_walk_minor(rnode, nodetype,
DI_CHECK_ALIAS|DI_CHECK_INTERNAL_PATH, lstatp, do_list_common);
+
di_fini(rnode);
+ if (lstatp->shp_errstr != NULL) {
+ *(lstatp->errstr) = strdup(lstatp->shp_errstr);
+ free(lstatp->shp_errstr);
+ lstatp->shp_errstr = NULL;
+ }
+
return (CFGA_OK);
}
@@ -2245,16 +2567,66 @@ config_err(int errnum, int err_type, char **errstring)
}
/*
- * do_list_common - Routine to list attachment point as part of
+ * do_list_common - list non-SHP attachment point
+ */
+static int
+do_list_common(
+ di_node_t node,
+ di_minor_t minor,
+ void *arg)
+{
+ di_node_t rnode;
+ di_hp_t hp;
+ char *minor_name;
+
+ minor_name = di_minor_name(minor);
+
+ /*
+ * since PCIE/PCIHSHPC connectors have both hp nodes and minor nodes
+ * created for now, we need to specifically exclude these connectors
+ * during walking minor nodes.
+ */
+ if ((rnode = di_init(di_devfs_path(node), DINFOSUBTREE | DINFOHP))
+ == DI_NODE_NIL) {
+ return (DI_WALK_CONTINUE);
+ }
+
+ for (hp = DI_HP_NIL; (hp = di_hp_next(rnode, hp)) != DI_HP_NIL; ) {
+ if (strcmp(di_hp_name(hp), minor_name) == 0) {
+ di_fini(rnode);
+ return (DI_WALK_CONTINUE);
+ }
+ }
+
+ di_fini(rnode);
+
+ return (do_list_common_impl(node, minor, NULL, arg));
+}
+
+/*
+ * do_list_common_hp - list SHP attachment point
+ */
+static int
+do_list_common_hp(
+ di_node_t node,
+ di_hp_t hp,
+ void *arg)
+{
+ return (do_list_common_impl(node, NULL, hp, arg));
+}
+
+/*
+ * do_list_common_impl - Routine to list attachment point as part of
* a config_list opertion. Used by both v1 and v2 interfaces.
* This is somewhat similar to config_get_lib() and its helper routines
* except that the ap_ids are always physical and don't have dynamic
* components.
*/
static int
-do_list_common(
+do_list_common_impl(
di_node_t node,
di_minor_t minor,
+ di_hp_t hp,
void *arg)
{
lib_loc_t lib_loc;
@@ -2262,6 +2634,8 @@ do_list_common(
list_stat_t *lstatp = NULL;
cfga_err_t ret = CFGA_ERROR;
+ if (minor != DI_MINOR_NIL && hp != DI_HP_NIL)
+ return (DI_WALK_CONTINUE);
lstatp = (list_stat_t *)arg;
@@ -2269,7 +2643,12 @@ do_list_common(
/*
* try and find a lib for this node
*/
- if (find_lib(node, minor, &lib_loc) != CFGA_OK) {
+ if (minor != DI_MINOR_NIL) {
+ ret = find_lib(node, minor, &lib_loc);
+ } else {
+ ret = find_lib_hp(node, hp, &lib_loc);
+ }
+ if (ret != CFGA_OK) {
return (DI_WALK_CONTINUE);
}
@@ -2280,7 +2659,11 @@ do_list_common(
lib_loc.vers_req.v_min = CFGA_HSL_V1;
lib_loc.vers_req.v_max = CFGA_HSL_VERS;
- ret = load_lib(node, minor, &lib_loc);
+ if (minor != DI_MINOR_NIL) {
+ ret = load_lib(node, minor, &lib_loc);
+ } else {
+ ret = load_lib_hp(node, hp, &lib_loc);
+ }
if (ret != CFGA_OK) {
return (DI_WALK_CONTINUE);
}
@@ -2294,7 +2677,32 @@ do_list_common(
* stop the walk if an error occurs in the plugin.
*/
if (compat_plugin(&lstatp->use_vers, libp->plugin_vers)) {
- (void) libp->vers_ops->stat_plugin(lstatp, &lib_loc, NULL);
+ if (minor != DI_MINOR_NIL) {
+ (void) libp->vers_ops->stat_plugin(lstatp,
+ &lib_loc, NULL);
+ } else {
+ /*
+ * If the underlying hotplug daemon is not enabled,
+ * the SHP attach points will not be shown, this
+ * could confuse the uesrs. We specifically pass the
+ * errstring to SHP plugin so that it can set the
+ * errstring accordingly in this case, giving users
+ * a hint.
+ */
+ ret = libp->vers_ops->stat_plugin(lstatp,
+ &lib_loc, lstatp->errstr);
+ if (ret == CFGA_NOTSUPP && *(lstatp->errstr) != NULL) {
+ if (lstatp->shp_errstr == NULL) {
+ lstatp->shp_errstr =
+ strdup(*(lstatp->errstr));
+ }
+ }
+
+ if (*(lstatp->errstr) != NULL) {
+ free(*(lstatp->errstr));
+ *(lstatp->errstr) = NULL;
+ }
+ }
}
rele_lib(libp);
diff --git a/usr/src/lib/libdevinfo/devinfo.c b/usr/src/lib/libdevinfo/devinfo.c
index 2431c87a96..e9c3e18375 100644
--- a/usr/src/lib/libdevinfo/devinfo.c
+++ b/usr/src/lib/libdevinfo/devinfo.c
@@ -44,6 +44,7 @@
#include <sys/time.h>
#include <sys/autoconf.h>
#include <stdarg.h>
+#include <sys/ddi_hp.h>
#define NDEBUG 1
#include <assert.h>
@@ -2522,6 +2523,284 @@ di_driver_private_data(di_node_t node)
}
/*
+ * Hotplug information access
+ */
+
+typedef struct {
+ void *arg;
+ const char *type;
+ uint_t flag;
+ int (*hp_callback)(di_node_t, di_hp_t, void *);
+} di_walk_hp_arg_t;
+
+static int
+di_walk_hp_callback(di_node_t node, void *argp)
+{
+ di_walk_hp_arg_t *arg = (di_walk_hp_arg_t *)argp;
+ di_hp_t hp;
+ char *type_str;
+
+ for (hp = DI_HP_NIL; (hp = di_hp_next(node, hp)) != DI_HP_NIL; ) {
+
+ /* Exclude non-matching types if a type filter is specified */
+ if (arg->type != NULL) {
+ type_str = di_hp_description(hp);
+ if (type_str && (strcmp(arg->type, type_str) != 0))
+ continue;
+ }
+
+ /* Exclude ports if DI_HP_PORT flag not specified */
+ if (!(arg->flag & DI_HP_PORT) &&
+ (di_hp_type(hp) == DDI_HP_CN_TYPE_VIRTUAL_PORT))
+ continue;
+
+ /* Exclude connectors if DI_HP_CONNECTOR flag not specified */
+ if (!(arg->flag & DI_HP_CONNECTOR) &&
+ !(di_hp_type(hp) == DDI_HP_CN_TYPE_VIRTUAL_PORT))
+ continue;
+
+ /* Perform callback */
+ if (arg->hp_callback(node, hp, arg->arg) != DI_WALK_CONTINUE)
+ return (DI_WALK_TERMINATE);
+ }
+
+ return (DI_WALK_CONTINUE);
+}
+
+int
+di_walk_hp(di_node_t node, const char *type, uint_t flag, void *arg,
+ int (*hp_callback)(di_node_t node, di_hp_t hp, void *arg))
+{
+ di_walk_hp_arg_t walk_arg;
+ caddr_t pa;
+
+#ifdef DEBUG
+ char *devfspath = di_devfs_path(node);
+ DPRINTF((DI_INFO, "walking hotplug nodes under %s\n", devfspath));
+ di_devfs_path_free(devfspath);
+#endif
+ /*
+ * paranoid error checking
+ */
+ if ((node == DI_NODE_NIL) || (hp_callback == NULL)) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ /* check if hotplug data is included in snapshot */
+ pa = (caddr_t)node - DI_NODE(node)->self;
+ if (!(DI_ALL(pa)->command & DINFOHP)) {
+ errno = ENOTSUP;
+ return (-1);
+ }
+
+ walk_arg.arg = arg;
+ walk_arg.type = type;
+ walk_arg.flag = flag;
+ walk_arg.hp_callback = hp_callback;
+ return (di_walk_node(node, DI_WALK_CLDFIRST, &walk_arg,
+ di_walk_hp_callback));
+}
+
+di_hp_t
+di_hp_next(di_node_t node, di_hp_t hp)
+{
+ caddr_t pa;
+
+ /*
+ * paranoid error checking
+ */
+ if (node == DI_NODE_NIL) {
+ errno = EINVAL;
+ return (DI_HP_NIL);
+ }
+
+ /*
+ * hotplug node is not NIL
+ */
+ if (hp != DI_HP_NIL) {
+ if (DI_HP(hp)->next != 0)
+ return (DI_HP((caddr_t)hp - hp->self + hp->next));
+ else {
+ errno = ENXIO;
+ return (DI_HP_NIL);
+ }
+ }
+
+ /*
+ * hotplug node is NIL-->caller asks for first hotplug node
+ */
+ if (DI_NODE(node)->hp_data != 0) {
+ return (DI_HP((caddr_t)node - DI_NODE(node)->self +
+ DI_NODE(node)->hp_data));
+ }
+
+ /*
+ * no hotplug data-->check if snapshot includes hotplug data
+ * in order to set the correct errno
+ */
+ pa = (caddr_t)node - DI_NODE(node)->self;
+ if (DINFOHP & DI_ALL(pa)->command)
+ errno = ENXIO;
+ else
+ errno = ENOTSUP;
+
+ return (DI_HP_NIL);
+}
+
+char *
+di_hp_name(di_hp_t hp)
+{
+ caddr_t pa;
+
+ /*
+ * paranoid error checking
+ */
+ if (hp == DI_HP_NIL) {
+ errno = EINVAL;
+ return (NULL);
+ }
+
+ pa = (caddr_t)hp - DI_HP(hp)->self;
+
+ if (DI_HP(hp)->hp_name == 0) {
+ errno = ENXIO;
+ return (NULL);
+ }
+
+ return ((char *)(pa + DI_HP(hp)->hp_name));
+}
+
+int
+di_hp_connection(di_hp_t hp)
+{
+ /*
+ * paranoid error checking
+ */
+ if (hp == DI_HP_NIL) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ if (DI_HP(hp)->hp_connection == -1)
+ errno = ENOENT;
+
+ return (DI_HP(hp)->hp_connection);
+}
+
+int
+di_hp_depends_on(di_hp_t hp)
+{
+ /*
+ * paranoid error checking
+ */
+ if (hp == DI_HP_NIL) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ if (DI_HP(hp)->hp_depends_on == -1)
+ errno = ENOENT;
+
+ return (DI_HP(hp)->hp_depends_on);
+}
+
+int
+di_hp_state(di_hp_t hp)
+{
+ /*
+ * paranoid error checking
+ */
+ if (hp == DI_HP_NIL) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ return (DI_HP(hp)->hp_state);
+}
+
+int
+di_hp_type(di_hp_t hp)
+{
+ /*
+ * paranoid error checking
+ */
+ if (hp == DI_HP_NIL) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ return (DI_HP(hp)->hp_type);
+}
+
+char *
+di_hp_description(di_hp_t hp)
+{
+ caddr_t pa;
+
+ /*
+ * paranoid error checking
+ */
+ if (hp == DI_HP_NIL) {
+ errno = EINVAL;
+ return (NULL);
+ }
+
+ pa = (caddr_t)hp - DI_HP(hp)->self;
+
+ if (DI_HP(hp)->hp_type_str == 0)
+ return (NULL);
+
+ return ((char *)(pa + DI_HP(hp)->hp_type_str));
+}
+
+di_node_t
+di_hp_child(di_hp_t hp)
+{
+ caddr_t pa; /* starting address of map */
+
+ /*
+ * paranoid error checking
+ */
+ if (hp == DI_HP_NIL) {
+ errno = EINVAL;
+ return (DI_NODE_NIL);
+ }
+
+ pa = (caddr_t)hp - DI_HP(hp)->self;
+
+ if (DI_HP(hp)->hp_child > 0) {
+ return (DI_NODE(pa + DI_HP(hp)->hp_child));
+ }
+
+ /*
+ * Deal with error condition:
+ * Child doesn't exist, figure out if DINFOSUBTREE is set.
+ * If it isn't, set errno to ENOTSUP.
+ */
+ if (!(DINFOSUBTREE & DI_ALL(pa)->command))
+ errno = ENOTSUP;
+ else
+ errno = ENXIO;
+
+ return (DI_NODE_NIL);
+}
+
+time_t
+di_hp_last_change(di_hp_t hp)
+{
+ /*
+ * paranoid error checking
+ */
+ if (hp == DI_HP_NIL) {
+ errno = EINVAL;
+ return ((time_t)0);
+ }
+
+ return ((time_t)DI_HP(hp)->hp_last_change);
+}
+
+/*
* PROM property access
*/
diff --git a/usr/src/lib/libdevinfo/libdevinfo.h b/usr/src/lib/libdevinfo/libdevinfo.h
index 520e5fb7a1..7789edd60a 100644
--- a/usr/src/lib/libdevinfo/libdevinfo.h
+++ b/usr/src/lib/libdevinfo/libdevinfo.h
@@ -71,6 +71,12 @@ extern "C" {
#define DI_CHECK_MASK 0xf0
+/*
+ * flags for di_walk_hp
+ */
+#define DI_HP_CONNECTOR 0x1
+#define DI_HP_PORT 0x2
+
/* nodeid types */
#define DI_PSEUDO_NODEID -1
#define DI_SID_NODEID -2
@@ -106,6 +112,7 @@ typedef struct di_path *di_path_t; /* path_node */
typedef struct di_link *di_link_t; /* link */
typedef struct di_lnode *di_lnode_t; /* endpoint */
typedef struct di_devlink *di_devlink_t; /* devlink */
+typedef struct di_hp *di_hp_t; /* hotplug */
typedef struct di_prop *di_prop_t; /* node property */
typedef struct di_path_prop *di_path_prop_t; /* path property */
@@ -126,6 +133,7 @@ typedef struct di_devlink_handle *di_devlink_handle_t; /* devlink snapshot */
#define DI_PROP_NIL NULL
#define DI_PROM_PROP_NIL NULL
#define DI_PROM_HANDLE_NIL NULL
+#define DI_HP_NIL NULL
/*
* IEEE 1275 properties and other standardized property names
@@ -320,6 +328,23 @@ extern void *di_link_private_get(di_link_t link);
extern void di_lnode_private_set(di_lnode_t lnode, void *data);
extern void *di_lnode_private_get(di_lnode_t lnode);
+/*
+ * hp_node: traversal, data access, and parameters
+ */
+extern int di_walk_hp(di_node_t node, const char *type,
+ uint_t flag, void *arg,
+ int (*hp_callback)(di_node_t node, di_hp_t hp,
+ void *arg));
+extern di_hp_t di_hp_next(di_node_t node, di_hp_t hp);
+
+extern char *di_hp_name(di_hp_t hp);
+extern int di_hp_connection(di_hp_t hp);
+extern int di_hp_depends_on(di_hp_t hp);
+extern int di_hp_state(di_hp_t hp);
+extern int di_hp_type(di_hp_t hp);
+extern char *di_hp_description(di_hp_t hp);
+extern time_t di_hp_last_change(di_hp_t hp);
+extern di_node_t di_hp_child(di_hp_t hp);
/*
* Private interfaces
diff --git a/usr/src/lib/libdevinfo/mapfile-vers b/usr/src/lib/libdevinfo/mapfile-vers
index 2f3527c3f0..bf0c6caf21 100644
--- a/usr/src/lib/libdevinfo/mapfile-vers
+++ b/usr/src/lib/libdevinfo/mapfile-vers
@@ -199,6 +199,15 @@ SUNWprivate_1.1 {
di_dli_openw;
di_driver_private_data;
di_flags;
+ di_hp_child;
+ di_hp_connection;
+ di_hp_depends_on;
+ di_hp_description;
+ di_hp_last_change;
+ di_hp_name;
+ di_hp_next;
+ di_hp_state;
+ di_hp_type;
di_init_driver;
di_init_impl;
di_lookup_node;
@@ -238,6 +247,7 @@ SUNWprivate_1.1 {
di_unretire_device;
di_vhci_first_node;
di_vhci_next_node;
+ di_walk_hp;
finddev_close;
finddev_emptydir;
finddev_next;
diff --git a/usr/src/lib/libhotplug/Makefile b/usr/src/lib/libhotplug/Makefile
new file mode 100644
index 0000000000..eaa5d492bd
--- /dev/null
+++ b/usr/src/lib/libhotplug/Makefile
@@ -0,0 +1,49 @@
+#
+# 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.
+#
+
+include ../Makefile.lib
+
+SUBDIRS= $(MACH)
+$(BUILD64)SUBDIRS += $(MACH64)
+
+all := TARGET = all
+clean := TARGET = clean
+clobber := TARGET = clobber
+install := TARGET = install
+lint := TARGET = lint
+
+HDRS= libhotplug.h libhotplug_impl.h
+HDRDIR= common
+
+.KEEP_STATE:
+
+all clean clobber install lint: $(SUBDIRS)
+
+check: $(CHECKHDRS)
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
diff --git a/usr/src/lib/libhotplug/Makefile.com b/usr/src/lib/libhotplug/Makefile.com
new file mode 100644
index 0000000000..e463eca54d
--- /dev/null
+++ b/usr/src/lib/libhotplug/Makefile.com
@@ -0,0 +1,45 @@
+#
+# 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.
+#
+
+LIBRARY= libhotplug.a
+VERS= .1
+OBJECTS= libhotplug.o
+
+include ../../Makefile.lib
+
+LIBS = $(DYNLIB) $(LINTLIB)
+LDLIBS += -lc -lnvpair
+
+SRCDIR = ../common
+CPPFLAGS += -I$(SRCDIR) -D_REENTRANT
+$(LINTLIB) := SRCS= $(SRCDIR)/$(LINTSRC)
+
+.KEEP_STATE:
+
+all: $(LIBS)
+
+lint: lintcheck
+
+include ../../Makefile.targ
diff --git a/usr/src/lib/libhotplug/amd64/Makefile b/usr/src/lib/libhotplug/amd64/Makefile
new file mode 100644
index 0000000000..036c13779e
--- /dev/null
+++ b/usr/src/lib/libhotplug/amd64/Makefile
@@ -0,0 +1,29 @@
+#
+# 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.
+#
+
+include ../Makefile.com
+include ../../Makefile.lib.64
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64)
diff --git a/usr/src/lib/libhotplug/common/libhotplug.c b/usr/src/lib/libhotplug/common/libhotplug.c
new file mode 100644
index 0000000000..e87acb425e
--- /dev/null
+++ b/usr/src/lib/libhotplug/common/libhotplug.c
@@ -0,0 +1,1367 @@
+/*
+ * 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <door.h>
+#include <libnvpair.h>
+#include <libhotplug.h>
+#include <libhotplug_impl.h>
+#include <sys/sunddi.h>
+#include <sys/ddi_hp.h>
+
+static void i_hp_dprintf(const char *fmt, ...);
+static int i_hp_pack_branch(hp_node_t, char **, size_t *);
+static int i_hp_pack_node(hp_node_t, char **, size_t *);
+static int i_hp_unpack_node(char *, size_t, hp_node_t, hp_node_t *);
+static int i_hp_unpack_branch(char *, size_t, hp_node_t, hp_node_t *);
+static int i_hp_call_hotplugd(nvlist_t *, nvlist_t **);
+static nvlist_t *i_hp_set_args(hp_cmd_t, const char *, const char *, uint_t,
+ const char *, int);
+static int i_hp_parse_results(nvlist_t *, hp_node_t *, char **);
+
+/*
+ * Global flag to enable debug features.
+ */
+int libhotplug_debug = 0;
+
+/*
+ * hp_init()
+ *
+ * Initialize a hotplug information snapshot.
+ */
+hp_node_t
+hp_init(const char *path, const char *connection, uint_t flags)
+{
+ nvlist_t *args;
+ nvlist_t *results;
+ hp_node_t root = NULL;
+ int rv;
+
+ i_hp_dprintf("hp_init: path=%p, connection=%p, flags=0x%x\n",
+ (void *)path, (void *)connection, flags);
+
+ /* Check arguments */
+ if ((path == NULL) || !HP_INIT_FLAGS_VALID(flags)) {
+ i_hp_dprintf("hp_init: invalid arguments.\n");
+ errno = EINVAL;
+ return (NULL);
+ }
+
+ /* Build arguments for door call */
+ if ((args = i_hp_set_args(HP_CMD_GETINFO, path, connection, flags,
+ NULL, 0)) == NULL) {
+ i_hp_dprintf("hp_init: cannot build arguments nvlist.\n");
+ errno = ENOMEM;
+ return (NULL);
+ }
+
+ /* Make the door call to hotplugd */
+ rv = i_hp_call_hotplugd(args, &results);
+
+ /* Arguments no longer needed */
+ nvlist_free(args);
+
+ /* Parse additional results, if any */
+ if ((rv == 0) && (results != NULL)) {
+ rv = i_hp_parse_results(results, &root, NULL);
+ nvlist_free(results);
+ }
+
+ /* Check for errors */
+ if (rv != 0) {
+ i_hp_dprintf("hp_init: failure (%s).\n", strerror(rv));
+ if (root)
+ hp_fini(root);
+ errno = rv;
+ return (NULL);
+ }
+
+ /* Success requires an info snapshot */
+ if (root == NULL) {
+ i_hp_dprintf("hp_init: missing info snapshot.\n");
+ errno = EFAULT;
+ return (NULL);
+ }
+
+ /* Success */
+ return (root);
+}
+
+/*
+ * hp_fini()
+ *
+ * Terminate and clean-up a hotplug information snapshot.
+ */
+void
+hp_fini(hp_node_t root)
+{
+ hp_node_t node;
+ hp_node_t sibling;
+ char *basepath;
+
+ i_hp_dprintf("hp_fini: root=%p\n", (void *)root);
+
+ if (root == NULL) {
+ i_hp_dprintf("hp_fini: invalid arguments.\n");
+ return;
+ }
+
+ /* Extract and free base path */
+ if (root->hp_basepath) {
+ basepath = root->hp_basepath;
+ for (node = root; node != NULL; node = node->hp_sibling)
+ node->hp_basepath = NULL;
+ free(basepath);
+ }
+
+ /* Destroy the nodes */
+ node = root;
+ while (node) {
+ sibling = node->hp_sibling;
+ if (node->hp_child)
+ hp_fini(node->hp_child);
+ if (node->hp_name)
+ free(node->hp_name);
+ if (node->hp_usage)
+ free(node->hp_usage);
+ if (node->hp_description)
+ free(node->hp_description);
+ free(node);
+ node = sibling;
+ }
+}
+
+/*
+ * hp_traverse()
+ *
+ * Walk a graph of hotplug nodes, executing a callback on each node.
+ */
+int
+hp_traverse(hp_node_t root, void *arg, int (*hp_callback)(hp_node_t, void *arg))
+{
+ int rv;
+ hp_node_t node;
+
+ i_hp_dprintf("hp_traverse: root=%p, arg=%p, hp_callback=%p\n",
+ (void *)root, arg, (void *)hp_callback);
+
+ /* Check arguments */
+ if ((root == NULL) || (hp_callback == NULL)) {
+ i_hp_dprintf("hp_traverse: invalid arguments.\n");
+ errno = EINVAL;
+ return (-1);
+ }
+
+ for (node = root; node; node = node->hp_sibling) {
+ rv = hp_callback(node, arg);
+
+ if (rv == HP_WALK_TERMINATE) {
+ i_hp_dprintf("hp_traverse: walk terminated.\n");
+ return (HP_WALK_TERMINATE);
+ }
+
+ if (node->hp_child && (rv != HP_WALK_PRUNECHILD))
+ if (hp_traverse(node->hp_child, arg, hp_callback) ==
+ HP_WALK_TERMINATE) {
+ i_hp_dprintf("hp_traverse: walk terminated.\n");
+ return (HP_WALK_TERMINATE);
+ }
+
+ if (rv == HP_WALK_PRUNESIBLING)
+ break;
+ }
+
+ return (0);
+}
+
+/*
+ * hp_type()
+ *
+ * Return a node's type.
+ */
+int
+hp_type(hp_node_t node)
+{
+ i_hp_dprintf("hp_type: node=%p\n", (void *)node);
+
+ if (node == NULL) {
+ i_hp_dprintf("hp_type: invalid arguments.\n");
+ errno = EINVAL;
+ return (-1);
+ }
+
+ return (node->hp_type);
+}
+
+/*
+ * hp_name()
+ *
+ * Return a node's name.
+ */
+char *
+hp_name(hp_node_t node)
+{
+ i_hp_dprintf("hp_name: node=%p\n", (void *)node);
+
+ if (node == NULL) {
+ i_hp_dprintf("hp_name: invalid arguments.\n");
+ errno = EINVAL;
+ return (NULL);
+ }
+
+ if (node->hp_name == NULL) {
+ i_hp_dprintf("hp_name: missing name value.\n");
+ errno = EFAULT;
+ }
+
+ return (node->hp_name);
+}
+
+/*
+ * hp_state()
+ *
+ * Return a node's current state.
+ */
+int
+hp_state(hp_node_t node)
+{
+ i_hp_dprintf("hp_state: node=%p\n", (void *)node);
+
+ if (node == NULL) {
+ i_hp_dprintf("hp_state: invalid arguments.\n");
+ errno = EINVAL;
+ return (-1);
+ }
+
+ if ((node->hp_type != HP_NODE_CONNECTOR) &&
+ (node->hp_type != HP_NODE_PORT)) {
+ i_hp_dprintf("hp_state: operation not supported.\n");
+ errno = ENOTSUP;
+ return (-1);
+ }
+
+ return (node->hp_state);
+}
+
+/*
+ * hp_usage()
+ *
+ * Return a usage description for usage nodes.
+ */
+char *
+hp_usage(hp_node_t node)
+{
+ i_hp_dprintf("hp_usage: node=%p\n", (void *)node);
+
+ if (node == NULL) {
+ i_hp_dprintf("hp_usage: invalid arguments.\n");
+ errno = EINVAL;
+ return (NULL);
+ }
+
+ if (node->hp_type != HP_NODE_USAGE) {
+ i_hp_dprintf("hp_usage: operation not supported.\n");
+ errno = ENOTSUP;
+ return (NULL);
+ }
+
+ if (node->hp_usage == NULL) {
+ i_hp_dprintf("hp_usage: missing usage value.\n");
+ errno = EFAULT;
+ }
+
+ return (node->hp_usage);
+}
+
+/*
+ * hp_description()
+ *
+ * Return a type description (e.g. "PCI slot") for connection nodes.
+ */
+char *
+hp_description(hp_node_t node)
+{
+ i_hp_dprintf("hp_description: node=%p\n", (void *)node);
+
+ if (node == NULL) {
+ i_hp_dprintf("hp_description: invalid arguments.\n");
+ errno = EINVAL;
+ return (NULL);
+ }
+
+ if ((node->hp_type != HP_NODE_CONNECTOR) &&
+ (node->hp_type != HP_NODE_PORT)) {
+ i_hp_dprintf("hp_description: operation not supported.\n");
+ errno = ENOTSUP;
+ return (NULL);
+ }
+
+ if (node->hp_description == NULL) {
+ i_hp_dprintf("hp_description: missing description value.\n");
+ errno = EFAULT;
+ }
+
+ return (node->hp_description);
+}
+
+/*
+ * hp_last_change()
+ *
+ * Return when the state of a connection was last changed.
+ */
+time_t
+hp_last_change(hp_node_t node)
+{
+ i_hp_dprintf("hp_last_change: node=%p\n", (void *)node);
+
+ if (node == NULL) {
+ i_hp_dprintf("hp_last_change: invalid arguments.\n");
+ errno = EINVAL;
+ return (NULL);
+ }
+
+ if ((node->hp_type != HP_NODE_CONNECTOR) &&
+ (node->hp_type != HP_NODE_PORT)) {
+ i_hp_dprintf("hp_last_change: operation not supported.\n");
+ errno = ENOTSUP;
+ return (NULL);
+ }
+
+ return (node->hp_last_change);
+}
+
+/*
+ * hp_parent()
+ *
+ * Return a node's parent node.
+ */
+hp_node_t
+hp_parent(hp_node_t node)
+{
+ i_hp_dprintf("hp_parent: node=%p\n", (void *)node);
+
+ if (node == NULL) {
+ i_hp_dprintf("hp_parent: invalid arguments.\n");
+ errno = EINVAL;
+ return (NULL);
+ }
+
+ if (node->hp_parent == NULL) {
+ i_hp_dprintf("hp_parent: node has no parent.\n");
+ errno = ENXIO;
+ }
+
+ return (node->hp_parent);
+}
+
+/*
+ * hp_child()
+ *
+ * Return a node's first child node.
+ */
+hp_node_t
+hp_child(hp_node_t node)
+{
+ i_hp_dprintf("hp_child: node=%p\n", (void *)node);
+
+ if (node == NULL) {
+ i_hp_dprintf("hp_child: invalid arguments.\n");
+ errno = EINVAL;
+ return (NULL);
+ }
+
+ if (node->hp_child == NULL) {
+ i_hp_dprintf("hp_child: node has no child.\n");
+ errno = ENXIO;
+ }
+
+ return (node->hp_child);
+}
+
+/*
+ * hp_sibling()
+ *
+ * Return a node's next sibling node.
+ */
+hp_node_t
+hp_sibling(hp_node_t node)
+{
+ i_hp_dprintf("hp_sibling: node=%p\n", (void *)node);
+
+ if (node == NULL) {
+ i_hp_dprintf("hp_sibling: invalid arguments.\n");
+ errno = EINVAL;
+ return (NULL);
+ }
+
+ if (node->hp_sibling == NULL) {
+ i_hp_dprintf("hp_sibling: node has no sibling.\n");
+ errno = ENXIO;
+ }
+
+ return (node->hp_sibling);
+}
+
+/*
+ * hp_path()
+ *
+ * Return the path (and maybe connection name) of a node.
+ * The caller must supply two buffers, each MAXPATHLEN size.
+ */
+int
+hp_path(hp_node_t node, char *path, char *connection)
+{
+ hp_node_t root;
+ hp_node_t parent;
+ int i;
+ char *s;
+ char components[MAXPATHLEN];
+
+ i_hp_dprintf("hp_path: node=%p, path=%p, connection=%p\n", (void *)node,
+ (void *)path, (void *)connection);
+
+ if ((node == NULL) || (path == NULL) || (connection == NULL)) {
+ i_hp_dprintf("hp_path: invalid arguments.\n");
+ return (EINVAL);
+ }
+
+ (void) memset(path, 0, MAXPATHLEN);
+ (void) memset(connection, 0, MAXPATHLEN);
+ (void) memset(components, 0, MAXPATHLEN);
+
+ /* Set 'connection' only for connectors and ports */
+ if ((node->hp_type == HP_NODE_CONNECTOR) ||
+ (node->hp_type == HP_NODE_PORT))
+ (void) strlcpy(connection, node->hp_name, MAXPATHLEN);
+
+ /* Trace back to the root node, accumulating components */
+ for (parent = node; parent != NULL; parent = parent->hp_parent) {
+ if (parent->hp_type == HP_NODE_DEVICE) {
+ (void) strlcat(components, "/", MAXPATHLEN);
+ (void) strlcat(components, parent->hp_name, MAXPATHLEN);
+ }
+ if (parent->hp_parent == NULL)
+ root = parent;
+ }
+
+ /* Ensure the snapshot actually contains a base path */
+ if (root->hp_basepath == NULL) {
+ i_hp_dprintf("hp_path: missing base pathname.\n");
+ return (EFAULT);
+ }
+
+ /*
+ * Construct the path. Start with the base path from the root
+ * node, then append the accumulated components in reverse order.
+ */
+ if (strcmp(root->hp_basepath, "/") != 0) {
+ (void) strlcat(path, root->hp_basepath, MAXPATHLEN);
+ if ((root->hp_type == HP_NODE_DEVICE) &&
+ ((s = strrchr(path, '/')) != NULL))
+ *s = '\0';
+ }
+ for (i = strlen(components) - 1; i >= 0; i--) {
+ if (components[i] == '/') {
+ (void) strlcat(path, &components[i], MAXPATHLEN);
+ components[i] = '\0';
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * hp_set_state()
+ *
+ * Initiate a state change operation on a node.
+ */
+int
+hp_set_state(hp_node_t node, uint_t flags, int state, hp_node_t *resultsp)
+{
+ hp_node_t root = NULL;
+ nvlist_t *args;
+ nvlist_t *results;
+ int rv;
+ char path[MAXPATHLEN];
+ char connection[MAXPATHLEN];
+
+ i_hp_dprintf("hp_set_state: node=%p, flags=0x%x, state=0x%x, "
+ "resultsp=%p\n", (void *)node, flags, state, (void *)resultsp);
+
+ /* Check arguments */
+ if ((node == NULL) || (resultsp == NULL) ||
+ !HP_SET_STATE_FLAGS_VALID(flags)) {
+ i_hp_dprintf("hp_set_state: invalid arguments.\n");
+ return (EINVAL);
+ }
+
+ /* Check node type */
+ if ((node->hp_type != HP_NODE_CONNECTOR) &&
+ (node->hp_type != HP_NODE_PORT)) {
+ i_hp_dprintf("hp_set_state: operation not supported.\n");
+ return (ENOTSUP);
+ }
+
+ /* Check that target state is valid */
+ switch (state) {
+ case DDI_HP_CN_STATE_PRESENT:
+ case DDI_HP_CN_STATE_POWERED:
+ case DDI_HP_CN_STATE_ENABLED:
+ if (node->hp_type != HP_NODE_CONNECTOR) {
+ i_hp_dprintf("hp_set_state: mismatched target.\n");
+ return (ENOTSUP);
+ }
+ break;
+ case DDI_HP_CN_STATE_PORT_PRESENT:
+ case DDI_HP_CN_STATE_OFFLINE:
+ case DDI_HP_CN_STATE_ONLINE:
+ if (node->hp_type != HP_NODE_PORT) {
+ i_hp_dprintf("hp_set_state: mismatched target.\n");
+ return (ENOTSUP);
+ }
+ break;
+ default:
+ i_hp_dprintf("hp_set_state: invalid target state.\n");
+ return (EINVAL);
+ }
+
+ /* Get path and connection of specified node */
+ if ((rv = hp_path(node, path, connection)) != 0)
+ return (rv);
+
+ /* Build arguments for door call */
+ if ((args = i_hp_set_args(HP_CMD_CHANGESTATE, path, connection, flags,
+ NULL, state)) == NULL)
+ return (ENOMEM);
+
+ /* Make the door call to hotplugd */
+ rv = i_hp_call_hotplugd(args, &results);
+
+ /* Arguments no longer needed */
+ nvlist_free(args);
+
+ /* Parse additional results, if any */
+ if ((rv == 0) && (results != NULL)) {
+ rv = i_hp_parse_results(results, &root, NULL);
+ nvlist_free(results);
+ *resultsp = root;
+ }
+
+ /* Done */
+ return (rv);
+}
+
+/*
+ * hp_set_private()
+ *
+ * Set bus private options on the hotplug connection
+ * indicated by the given hotplug information node.
+ */
+int
+hp_set_private(hp_node_t node, const char *options, char **resultsp)
+{
+ int rv;
+ nvlist_t *args;
+ nvlist_t *results;
+ char *values = NULL;
+ char path[MAXPATHLEN];
+ char connection[MAXPATHLEN];
+
+ i_hp_dprintf("hp_set_private: node=%p, options=%p, resultsp=%p\n",
+ (void *)node, (void *)options, (void *)resultsp);
+
+ /* Check arguments */
+ if ((node == NULL) || (options == NULL) || (resultsp == NULL)) {
+ i_hp_dprintf("hp_set_private: invalid arguments.\n");
+ return (EINVAL);
+ }
+
+ /* Check node type */
+ if (node->hp_type != HP_NODE_CONNECTOR) {
+ i_hp_dprintf("hp_set_private: operation not supported.\n");
+ return (ENOTSUP);
+ }
+
+ /* Initialize results */
+ *resultsp = NULL;
+
+ /* Get path and connection of specified node */
+ if ((rv = hp_path(node, path, connection)) != 0)
+ return (rv);
+
+ /* Build arguments for door call */
+ if ((args = i_hp_set_args(HP_CMD_SETPRIVATE, path, connection, 0,
+ options, 0)) == NULL)
+ return (ENOMEM);
+
+ /* Make the door call to hotplugd */
+ rv = i_hp_call_hotplugd(args, &results);
+
+ /* Arguments no longer needed */
+ nvlist_free(args);
+
+ /* Parse additional results, if any */
+ if ((rv == 0) && (results != NULL)) {
+ rv = i_hp_parse_results(results, NULL, &values);
+ nvlist_free(results);
+ *resultsp = values;
+ }
+
+ /* Done */
+ return (rv);
+}
+
+/*
+ * hp_get_private()
+ *
+ * Get bus private options on the hotplug connection
+ * indicated by the given hotplug information node.
+ */
+int
+hp_get_private(hp_node_t node, const char *options, char **resultsp)
+{
+ int rv;
+ nvlist_t *args;
+ nvlist_t *results;
+ char *values = NULL;
+ char path[MAXPATHLEN];
+ char connection[MAXPATHLEN];
+
+ i_hp_dprintf("hp_get_private: node=%p, options=%p, resultsp=%p\n",
+ (void *)node, (void *)options, (void *)resultsp);
+
+ /* Check arguments */
+ if ((node == NULL) || (options == NULL) || (resultsp == NULL)) {
+ i_hp_dprintf("hp_get_private: invalid arguments.\n");
+ return (EINVAL);
+ }
+
+ /* Check node type */
+ if (node->hp_type != HP_NODE_CONNECTOR) {
+ i_hp_dprintf("hp_get_private: operation not supported.\n");
+ return (ENOTSUP);
+ }
+
+ /* Initialize results */
+ *resultsp = NULL;
+
+ /* Get path and connection of specified node */
+ if ((rv = hp_path(node, path, connection)) != 0)
+ return (rv);
+
+ /* Build arguments for door call */
+ if ((args = i_hp_set_args(HP_CMD_GETPRIVATE, path, connection, 0,
+ options, 0)) == NULL)
+ return (ENOMEM);
+
+ /* Make the door call to hotplugd */
+ rv = i_hp_call_hotplugd(args, &results);
+
+ /* Arguments no longer needed */
+ nvlist_free(args);
+
+ /* Parse additional results, if any */
+ if ((rv == 0) && (results != NULL)) {
+ rv = i_hp_parse_results(results, NULL, &values);
+ nvlist_free(results);
+ *resultsp = values;
+ }
+
+ /* Done */
+ return (rv);
+}
+
+/*
+ * hp_pack()
+ *
+ * Given the root of a hotplug information snapshot, pack
+ * it into a contiguous byte array so that it is suitable
+ * for network transport.
+ */
+int
+hp_pack(hp_node_t root, char **bufp, size_t *lenp)
+{
+ hp_node_t node;
+ nvlist_t *nvl;
+ char *buf;
+ size_t len;
+ int rv;
+
+ i_hp_dprintf("hp_pack: root=%p, bufp=%p, lenp=%p\n", (void *)root,
+ (void *)bufp, (void *)lenp);
+
+ if ((root == NULL) || (bufp == NULL) || (lenp == NULL)) {
+ i_hp_dprintf("hp_pack: invalid arguments.\n");
+ return (EINVAL);
+ }
+
+ *lenp = 0;
+ *bufp = NULL;
+
+ if (nvlist_alloc(&nvl, 0, 0) != 0) {
+ i_hp_dprintf("hp_pack: nvlist_alloc() failed (%s).\n",
+ strerror(errno));
+ return (ENOMEM);
+ }
+
+ if (root->hp_basepath != NULL) {
+ rv = nvlist_add_string(nvl, HP_INFO_BASE, root->hp_basepath);
+ if (rv != 0) {
+ nvlist_free(nvl);
+ return (rv);
+ }
+ }
+
+ for (node = root; node != NULL; node = node->hp_sibling) {
+ if ((rv = i_hp_pack_branch(node, &buf, &len)) == 0) {
+ rv = nvlist_add_byte_array(nvl, HP_INFO_BRANCH,
+ (uchar_t *)buf, len);
+ free(buf);
+ }
+ if (rv != 0) {
+ nvlist_free(nvl);
+ return (rv);
+ }
+ }
+
+ len = 0;
+ buf = NULL;
+ if ((rv = nvlist_pack(nvl, &buf, &len, NV_ENCODE_NATIVE, 0)) == 0) {
+ *lenp = len;
+ *bufp = buf;
+ }
+
+ nvlist_free(nvl);
+
+ return (rv);
+}
+
+/*
+ * hp_unpack()
+ *
+ * Unpack a hotplug information snapshot for normal usage.
+ */
+int
+hp_unpack(char *packed_buf, size_t packed_len, hp_node_t *retp)
+{
+ hp_node_t root;
+ hp_node_t root_list = NULL;
+ hp_node_t prev_root = NULL;
+ nvlist_t *nvl = NULL;
+ nvpair_t *nvp;
+ char *basepath = NULL;
+ int rv;
+
+ i_hp_dprintf("hp_unpack: packed_buf=%p, packed_len=%u, retp=%p\n",
+ (void *)packed_buf, (uint32_t)packed_len, (void *)retp);
+
+ if ((packed_buf == NULL) || (packed_len == 0) || (retp == NULL)) {
+ i_hp_dprintf("hp_unpack: invalid arguments.\n");
+ return (EINVAL);
+ }
+
+ if ((rv = nvlist_unpack(packed_buf, packed_len, &nvl, 0)) != 0)
+ return (rv);
+
+ if (nvlist_next_nvpair(nvl, NULL) == NULL) {
+ nvlist_free(nvl);
+ errno = EINVAL;
+ return (NULL);
+ }
+
+ for (nvp = NULL; nvp = nvlist_next_nvpair(nvl, nvp); ) {
+
+ rv = EINVAL;
+
+ if (strcmp(nvpair_name(nvp), HP_INFO_BASE) == 0) {
+ char *val_string;
+
+ if ((rv = nvpair_value_string(nvp, &val_string)) == 0) {
+ if ((basepath = strdup(val_string)) == NULL)
+ rv = ENOMEM;
+ }
+
+ } else if (strcmp(nvpair_name(nvp), HP_INFO_BRANCH) == 0) {
+ size_t len = 0;
+ char *buf = NULL;
+
+ if ((rv = nvpair_value_byte_array(nvp,
+ (uchar_t **)&buf, (uint_t *)&len)) == 0) {
+ rv = i_hp_unpack_branch(buf, len, NULL, &root);
+ }
+
+ if (rv == 0) {
+ if (prev_root) {
+ prev_root->hp_sibling = root;
+ } else {
+ root_list = root;
+ }
+ prev_root = root;
+ }
+ }
+
+ if (rv != 0) {
+ if (basepath)
+ free(basepath);
+ nvlist_free(nvl);
+ hp_fini(root_list);
+ *retp = NULL;
+ return (rv);
+ }
+ }
+
+ /* Store the base path in each root node */
+ if (basepath) {
+ for (root = root_list; root; root = root->hp_sibling)
+ root->hp_basepath = basepath;
+ }
+
+ nvlist_free(nvl);
+ *retp = root_list;
+ return (0);
+}
+
+/*
+ * i_hp_dprintf()
+ *
+ * Print debug messages to stderr, but only when the debug flag
+ * (libhotplug_debug) is set.
+ */
+/*PRINTFLIKE1*/
+static void
+i_hp_dprintf(const char *fmt, ...)
+{
+ va_list ap;
+
+ if (libhotplug_debug) {
+ va_start(ap, fmt);
+ (void) vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ }
+}
+
+/*
+ * i_hp_pack_branch()
+ *
+ * Pack an individual branch of a hotplug information snapshot.
+ */
+static int
+i_hp_pack_branch(hp_node_t root, char **bufp, size_t *lenp)
+{
+ hp_node_t child;
+ nvlist_t *nvl;
+ char *buf;
+ size_t len;
+ int rv;
+
+ *lenp = 0;
+ *bufp = NULL;
+
+ /* Allocate an nvlist for this branch */
+ if (nvlist_alloc(&nvl, 0, 0) != 0)
+ return (ENOMEM);
+
+ /* Pack the root of the branch and add it to the nvlist */
+ if ((rv = i_hp_pack_node(root, &buf, &len)) == 0) {
+ rv = nvlist_add_byte_array(nvl, HP_INFO_NODE,
+ (uchar_t *)buf, len);
+ free(buf);
+ }
+ if (rv != 0) {
+ nvlist_free(nvl);
+ return (rv);
+ }
+
+ /* Pack each subordinate branch, and add it to the nvlist */
+ for (child = root->hp_child; child != NULL; child = child->hp_sibling) {
+ if ((rv = i_hp_pack_branch(child, &buf, &len)) == 0) {
+ rv = nvlist_add_byte_array(nvl, HP_INFO_BRANCH,
+ (uchar_t *)buf, len);
+ free(buf);
+ }
+ if (rv != 0) {
+ nvlist_free(nvl);
+ return (rv);
+ }
+ }
+
+ /* Pack the resulting nvlist into a single buffer */
+ len = 0;
+ buf = NULL;
+ if ((rv = nvlist_pack(nvl, &buf, &len, NV_ENCODE_NATIVE, 0)) == 0) {
+ *lenp = len;
+ *bufp = buf;
+ }
+
+ /* Free the nvlist */
+ nvlist_free(nvl);
+
+ return (rv);
+}
+
+/*
+ * i_hp_pack_node()
+ *
+ * Pack an individual node of a hotplug information snapshot.
+ */
+static int
+i_hp_pack_node(hp_node_t node, char **bufp, size_t *lenp)
+{
+ nvlist_t *nvl;
+ char *buf = NULL;
+ size_t len = 0;
+ int rv;
+
+ if (nvlist_alloc(&nvl, 0, 0) != 0)
+ return (ENOMEM);
+
+ if ((rv = nvlist_add_uint32(nvl, HP_INFO_TYPE,
+ (uint32_t)node->hp_type)) != 0)
+ goto fail;
+
+ if ((node->hp_name) &&
+ ((rv = nvlist_add_string(nvl, HP_INFO_NAME, node->hp_name)) != 0))
+ goto fail;
+
+ if ((node->hp_usage) &&
+ ((rv = nvlist_add_string(nvl, HP_INFO_USAGE, node->hp_usage)) != 0))
+ goto fail;
+
+ if ((node->hp_description) &&
+ ((rv = nvlist_add_string(nvl, HP_INFO_DESC,
+ node->hp_description)) != 0))
+ goto fail;
+
+ if ((rv = nvlist_add_uint32(nvl, HP_INFO_STATE, node->hp_state)) != 0)
+ goto fail;
+
+ if ((node->hp_last_change != 0) &&
+ ((rv = nvlist_add_uint32(nvl, HP_INFO_TIME,
+ node->hp_last_change)) != 0))
+ goto fail;
+
+ if ((rv = nvlist_pack(nvl, &buf, &len, NV_ENCODE_NATIVE, 0)) != 0)
+ goto fail;
+
+ *bufp = buf;
+ *lenp = len;
+ nvlist_free(nvl);
+ return (0);
+
+fail:
+ *bufp = NULL;
+ *lenp = 0;
+ nvlist_free(nvl);
+ return (rv);
+}
+
+/*
+ * i_hp_unpack_branch()
+ *
+ * Unpack a branch of hotplug information nodes.
+ */
+static int
+i_hp_unpack_branch(char *packed_buf, size_t packed_len, hp_node_t parent,
+ hp_node_t *retp)
+{
+ hp_node_t node = NULL;
+ hp_node_t child;
+ hp_node_t prev_child = NULL;
+ nvlist_t *nvl = NULL;
+ nvpair_t *nvp;
+ char *buf;
+ size_t len;
+ int rv;
+
+ /* Initialize results */
+ *retp = NULL;
+
+ /* Unpack the nvlist for this branch */
+ if ((rv = nvlist_unpack(packed_buf, packed_len, &nvl, 0)) != 0)
+ return (rv);
+
+ /*
+ * Unpack the branch. The first item in the nvlist is
+ * always the root node. And zero or more subordinate
+ * branches may be packed afterward.
+ */
+ for (nvp = NULL; nvp = nvlist_next_nvpair(nvl, nvp); ) {
+
+ len = 0;
+ buf = NULL;
+
+ if (strcmp(nvpair_name(nvp), HP_INFO_NODE) == 0) {
+
+ /* Check that there is only one root node */
+ if (node != NULL) {
+ hp_fini(node);
+ nvlist_free(nvl);
+ return (EFAULT);
+ }
+
+ if ((rv = nvpair_value_byte_array(nvp, (uchar_t **)&buf,
+ (uint_t *)&len)) == 0)
+ rv = i_hp_unpack_node(buf, len, parent, &node);
+
+ if (rv != 0) {
+ nvlist_free(nvl);
+ return (rv);
+ }
+
+ } else if (strcmp(nvpair_name(nvp), HP_INFO_BRANCH) == 0) {
+
+ if ((rv = nvpair_value_byte_array(nvp, (uchar_t **)&buf,
+ (uint_t *)&len)) == 0)
+ rv = i_hp_unpack_branch(buf, len, node, &child);
+
+ if (rv != 0) {
+ hp_fini(node);
+ nvlist_free(nvl);
+ return (rv);
+ }
+
+ if (prev_child) {
+ prev_child->hp_sibling = child;
+ } else {
+ node->hp_child = child;
+ }
+ prev_child = child;
+ }
+ }
+
+ nvlist_free(nvl);
+ *retp = node;
+ return (0);
+}
+
+/*
+ * i_hp_unpack_node()
+ *
+ * Unpack an individual hotplug information node.
+ */
+static int
+i_hp_unpack_node(char *buf, size_t len, hp_node_t parent, hp_node_t *retp)
+{
+ hp_node_t node;
+ nvlist_t *nvl;
+ nvpair_t *nvp;
+ uint32_t val_uint32;
+ char *val_string;
+ int rv = 0;
+
+ /* Initialize results */
+ *retp = NULL;
+
+ /* Unpack node into an nvlist */
+ if ((nvlist_unpack(buf, len, &nvl, 0) != 0))
+ return (EINVAL);
+
+ /* Allocate the new node */
+ if ((node = (hp_node_t)calloc(1, sizeof (struct hp_node))) == NULL) {
+ nvlist_free(nvl);
+ return (ENOMEM);
+ }
+
+ /* Iterate through nvlist, unpacking each field */
+ for (nvp = NULL; nvp = nvlist_next_nvpair(nvl, nvp); ) {
+
+ if ((strcmp(nvpair_name(nvp), HP_INFO_TYPE) == 0) &&
+ (nvpair_type(nvp) == DATA_TYPE_UINT32)) {
+
+ (void) nvpair_value_uint32(nvp, &val_uint32);
+ node->hp_type = val_uint32;
+
+ } else if ((strcmp(nvpair_name(nvp), HP_INFO_NAME) == 0) &&
+ (nvpair_type(nvp) == DATA_TYPE_STRING)) {
+
+ (void) nvpair_value_string(nvp, &val_string);
+ if ((node->hp_name = strdup(val_string)) == NULL) {
+ rv = ENOMEM;
+ break;
+ }
+
+ } else if ((strcmp(nvpair_name(nvp), HP_INFO_STATE) == 0) &&
+ (nvpair_type(nvp) == DATA_TYPE_UINT32)) {
+
+ (void) nvpair_value_uint32(nvp, &val_uint32);
+ node->hp_state = val_uint32;
+
+ } else if ((strcmp(nvpair_name(nvp), HP_INFO_USAGE) == 0) &&
+ (nvpair_type(nvp) == DATA_TYPE_STRING)) {
+
+ (void) nvpair_value_string(nvp, &val_string);
+ if ((node->hp_usage = strdup(val_string)) == NULL) {
+ rv = ENOMEM;
+ break;
+ }
+
+ } else if ((strcmp(nvpair_name(nvp), HP_INFO_DESC) == 0) &&
+ (nvpair_type(nvp) == DATA_TYPE_STRING)) {
+
+ (void) nvpair_value_string(nvp, &val_string);
+ if ((node->hp_description = strdup(val_string))
+ == NULL) {
+ rv = ENOMEM;
+ break;
+ }
+
+ } else if ((strcmp(nvpair_name(nvp), HP_INFO_TIME) == 0) &&
+ (nvpair_type(nvp) == DATA_TYPE_UINT32)) {
+
+ (void) nvpair_value_uint32(nvp, &val_uint32);
+ node->hp_last_change = (time_t)val_uint32;
+
+ } else {
+ i_hp_dprintf("i_hp_unpack_node: unrecognized: '%s'\n",
+ nvpair_name(nvp));
+ }
+ }
+
+ /* Unpacked nvlist no longer needed */
+ nvlist_free(nvl);
+
+ /* Check for errors */
+ if (rv != 0) {
+ hp_fini(node);
+ return (rv);
+ }
+
+ /* Success */
+ node->hp_parent = parent;
+ *retp = node;
+ return (0);
+}
+
+/*
+ * i_hp_call_hotplugd()
+ *
+ * Perform a door call to the hotplug daemon.
+ */
+static int
+i_hp_call_hotplugd(nvlist_t *args, nvlist_t **resultsp)
+{
+ door_arg_t door_arg;
+ nvlist_t *results = NULL;
+ char *buf = NULL;
+ size_t len = 0;
+ uint64_t seqnum;
+ int door_fd;
+ int rv;
+
+ /* Initialize results */
+ *resultsp = NULL;
+
+ /* Open door */
+ if ((door_fd = open(HOTPLUGD_DOOR, O_RDONLY)) < 0) {
+ i_hp_dprintf("i_hp_call_hotplugd: cannot open door (%s)\n",
+ strerror(errno));
+ return (EBADF);
+ }
+
+ /* Pack the nvlist of arguments */
+ if ((rv = nvlist_pack(args, &buf, &len, NV_ENCODE_NATIVE, 0)) != 0) {
+ i_hp_dprintf("i_hp_call_hotplugd: cannot pack arguments (%s)\n",
+ strerror(rv));
+ return (rv);
+ }
+
+ /* Set the door argument using the packed arguments */
+ door_arg.data_ptr = buf;
+ door_arg.data_size = len;
+ door_arg.desc_ptr = NULL;
+ door_arg.desc_num = 0;
+ door_arg.rbuf = (char *)(uintptr_t)&rv;
+ door_arg.rsize = sizeof (rv);
+
+ /* Attempt the door call */
+ if (door_call(door_fd, &door_arg) != 0) {
+ rv = errno;
+ i_hp_dprintf("i_hp_call_hotplugd: door call failed (%s)\n",
+ strerror(rv));
+ (void) close(door_fd);
+ free(buf);
+ return (rv);
+ }
+
+ /* The arguments are no longer needed */
+ free(buf);
+
+ /*
+ * If results are not in the original buffer provided,
+ * then check and process the new results buffer.
+ */
+ if (door_arg.rbuf != (char *)(uintptr_t)&rv) {
+
+ /*
+ * First check that the buffer is valid. Then check for
+ * the simple case where a short result code was sent.
+ * The last case is a packed nvlist was returned, which
+ * needs to be unpacked.
+ */
+ if ((door_arg.rbuf == NULL) ||
+ (door_arg.data_size < sizeof (rv))) {
+ i_hp_dprintf("i_hp_call_hotplugd: invalid results.\n");
+ rv = EFAULT;
+
+ } else if (door_arg.data_size == sizeof (rv)) {
+ rv = *(int *)(uintptr_t)door_arg.rbuf;
+
+ } else if ((rv = nvlist_unpack(door_arg.rbuf,
+ door_arg.data_size, &results, 0)) != 0) {
+ i_hp_dprintf("i_hp_call_hotplugd: "
+ "cannot unpack results (%s).\n", strerror(rv));
+ results = NULL;
+ rv = EFAULT;
+ }
+
+ /* Unmap the results buffer */
+ if (door_arg.rbuf != NULL)
+ (void) munmap(door_arg.rbuf, door_arg.rsize);
+
+ /*
+ * In the case of a packed nvlist, notify the daemon
+ * that it can free the result buffer from its heap.
+ */
+ if ((results != NULL) &&
+ (nvlist_lookup_uint64(results, HPD_SEQNUM, &seqnum) == 0)) {
+ door_arg.data_ptr = (char *)(uintptr_t)&seqnum;
+ door_arg.data_size = sizeof (seqnum);
+ door_arg.desc_ptr = NULL;
+ door_arg.desc_num = 0;
+ door_arg.rbuf = NULL;
+ door_arg.rsize = 0;
+ (void) door_call(door_fd, &door_arg);
+ if (door_arg.rbuf != NULL)
+ (void) munmap(door_arg.rbuf, door_arg.rsize);
+ }
+
+ *resultsp = results;
+ }
+
+ (void) close(door_fd);
+ return (rv);
+}
+
+/*
+ * i_hp_set_args()
+ *
+ * Construct an nvlist of arguments for a hotplugd door call.
+ */
+static nvlist_t *
+i_hp_set_args(hp_cmd_t cmd, const char *path, const char *connection,
+ uint_t flags, const char *options, int state)
+{
+ nvlist_t *args;
+
+ /* Allocate a new nvlist */
+ if (nvlist_alloc(&args, NV_UNIQUE_NAME_TYPE, 0) != 0)
+ return (NULL);
+
+ /* Add common arguments */
+ if ((nvlist_add_int32(args, HPD_CMD, cmd) != 0) ||
+ (nvlist_add_string(args, HPD_PATH, path) != 0)) {
+ nvlist_free(args);
+ return (NULL);
+ }
+
+ /* Add connection, but only if defined */
+ if ((connection != NULL) && (connection[0] != '\0') &&
+ (nvlist_add_string(args, HPD_CONNECTION, connection) != 0)) {
+ nvlist_free(args);
+ return (NULL);
+ }
+
+ /* Add flags, but only if defined */
+ if ((flags != 0) && (nvlist_add_uint32(args, HPD_FLAGS, flags) != 0)) {
+ nvlist_free(args);
+ return (NULL);
+ }
+
+ /* Add options, but only if defined */
+ if ((options != NULL) &&
+ (nvlist_add_string(args, HPD_OPTIONS, options) != 0)) {
+ nvlist_free(args);
+ return (NULL);
+ }
+
+ /* Add state, but only for CHANGESTATE command */
+ if ((cmd == HP_CMD_CHANGESTATE) &&
+ (nvlist_add_int32(args, HPD_STATE, state) != 0)) {
+ nvlist_free(args);
+ return (NULL);
+ }
+
+ return (args);
+}
+
+/*
+ * i_hp_parse_results()
+ *
+ * Parse out individual fields of an nvlist of results from
+ * a hotplugd door call.
+ */
+static int
+i_hp_parse_results(nvlist_t *results, hp_node_t *rootp, char **optionsp)
+{
+ int rv;
+
+ /* Parse an information snapshot */
+ if (rootp) {
+ char *buf = NULL;
+ size_t len = 0;
+
+ *rootp = NULL;
+ if (nvlist_lookup_byte_array(results, HPD_INFO,
+ (uchar_t **)&buf, (uint_t *)&len) == 0) {
+ if ((rv = hp_unpack(buf, len, rootp)) != 0)
+ return (rv);
+ }
+ }
+
+ /* Parse a bus private option string */
+ if (optionsp) {
+ char *str;
+
+ *optionsp = NULL;
+ if ((nvlist_lookup_string(results, HPD_OPTIONS, &str) == 0) &&
+ ((*optionsp = strdup(str)) == NULL)) {
+ return (ENOMEM);
+ }
+ }
+
+ /* Parse result code of the operation */
+ if (nvlist_lookup_int32(results, HPD_STATUS, &rv) != 0) {
+ i_hp_dprintf("i_hp_call_hotplugd: missing status.\n");
+ return (EFAULT);
+ }
+
+ return (rv);
+}
diff --git a/usr/src/lib/libhotplug/common/libhotplug.h b/usr/src/lib/libhotplug/common/libhotplug.h
new file mode 100644
index 0000000000..c13fd66b51
--- /dev/null
+++ b/usr/src/lib/libhotplug/common/libhotplug.h
@@ -0,0 +1,104 @@
+/*
+ * 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 _LIBHOTPLUG_H
+#define _LIBHOTPLUG_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+
+/*
+ * Define node types in hotplug snapshot.
+ */
+#define HP_NODE_NONE 0
+#define HP_NODE_DEVICE 1
+#define HP_NODE_CONNECTOR 2
+#define HP_NODE_PORT 3
+#define HP_NODE_USAGE 4
+
+/*
+ * Define flags for hp_init().
+ */
+#define HPINFOUSAGE 0x1
+#define HPINFOSEARCH 0x2 /* private flag */
+
+/*
+ * Define flags for hp_set_state().
+ */
+#define HPFORCE 0x1
+#define HPQUERY 0x2
+
+/*
+ * Define private flags.
+ */
+
+/*
+ * Define return values for hp_traverse() callbacks.
+ */
+#define HP_WALK_CONTINUE 0
+#define HP_WALK_PRUNECHILD 1
+#define HP_WALK_PRUNESIBLING 2
+#define HP_WALK_TERMINATE 3
+
+/*
+ * Define opaque handle to hotplug nodes.
+ */
+typedef struct hp_node *hp_node_t;
+
+/*
+ * Interface prototypes.
+ */
+hp_node_t hp_init(const char *path, const char *connection, uint_t flags);
+void hp_fini(hp_node_t root);
+int hp_traverse(hp_node_t root, void *arg,
+ int (*hp_callback)(hp_node_t, void *arg));
+int hp_type(hp_node_t node);
+char *hp_name(hp_node_t node);
+char *hp_usage(hp_node_t node);
+int hp_state(hp_node_t node);
+char *hp_description(hp_node_t node);
+time_t hp_last_change(hp_node_t node);
+hp_node_t hp_parent(hp_node_t node);
+hp_node_t hp_child(hp_node_t node);
+hp_node_t hp_sibling(hp_node_t node);
+int hp_path(hp_node_t node, char *path, char *connection);
+int hp_set_state(hp_node_t node, uint_t flags, int state,
+ hp_node_t *resultsp);
+int hp_set_private(hp_node_t node, const char *options,
+ char **resultsp);
+int hp_get_private(hp_node_t node, const char *options,
+ char **resultsp);
+int hp_pack(hp_node_t root, char **bufp, size_t *lenp);
+int hp_unpack(char *packed_buf, size_t packed_len, hp_node_t *retp);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBHOTPLUG_H */
diff --git a/usr/src/lib/libhotplug/common/libhotplug_impl.h b/usr/src/lib/libhotplug/common/libhotplug_impl.h
new file mode 100644
index 0000000000..e75170bcb2
--- /dev/null
+++ b/usr/src/lib/libhotplug/common/libhotplug_impl.h
@@ -0,0 +1,105 @@
+/*
+ * 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 _LIBHOTPLUG_IMPL_H
+#define _LIBHOTPLUG_IMPL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/param.h>
+#include <libhotplug.h>
+
+/*
+ * Definition of a node in a hotplug information snapshot.
+ */
+struct hp_node {
+ int hp_type;
+ char *hp_name;
+ char *hp_usage;
+ char *hp_description;
+ char *hp_basepath;
+ int hp_state;
+ time_t hp_last_change;
+ hp_node_t hp_parent;
+ hp_node_t hp_child;
+ hp_node_t hp_sibling;
+};
+
+/*
+ * Definitions used for packing/unpacking snapshots.
+ */
+#define HP_INFO_BASE "hp_info.basepath"
+#define HP_INFO_NODE "hp_info.node"
+#define HP_INFO_BRANCH "hp_info.branch"
+#define HP_INFO_TYPE "hp_info.type"
+#define HP_INFO_NAME "hp_info.name"
+#define HP_INFO_USAGE "hp_info.usage"
+#define HP_INFO_STATE "hp_info.state"
+#define HP_INFO_DESC "hp_info.description"
+#define HP_INFO_TIME "hp_info.last_change"
+
+/*
+ * Definitions for the door interface to hotplugd(1m).
+ */
+#define HOTPLUGD_PID "/var/run/hotplugd.pid"
+#define HOTPLUGD_DOOR "/var/run/hotplugd_door"
+
+typedef enum {
+ HP_CMD_NONE = 0,
+ HP_CMD_GETINFO,
+ HP_CMD_CHANGESTATE,
+ HP_CMD_SETPRIVATE,
+ HP_CMD_GETPRIVATE
+} hp_cmd_t;
+
+#define HPD_CMD "hp_door.cmd"
+#define HPD_PATH "hp_door.path"
+#define HPD_CONNECTION "hp_door.connection"
+#define HPD_FLAGS "hp_door.flags"
+#define HPD_STATE "hp_door.state"
+#define HPD_OPTIONS "hp_door.options"
+#define HPD_INFO "hp_door.info"
+#define HPD_STATUS "hp_door.status"
+#define HPD_SEQNUM "hp_door.seqnum"
+
+/*
+ * Definition of macros to validate flags.
+ */
+#define HP_INIT_FLAGS_VALID(f) ((f & ~(HPINFOUSAGE)) == 0)
+#define HP_SET_STATE_FLAGS_VALID(f) ((f & ~(HPFORCE | HPQUERY)) == 0)
+
+/*
+ * Definition of global flag to enable debug.
+ */
+extern int libhotplug_debug;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBHOTPLUG_IMPL_H */
diff --git a/usr/src/lib/libhotplug/common/llib-lhotplug b/usr/src/lib/libhotplug/common/llib-lhotplug
new file mode 100644
index 0000000000..6773e83b80
--- /dev/null
+++ b/usr/src/lib/libhotplug/common/llib-lhotplug
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+/* LINTLIBRARY */
+/* PROTOLIB1 */
+
+#include <libhotplug.h>
diff --git a/usr/src/lib/libhotplug/common/mapfile-vers b/usr/src/lib/libhotplug/common/mapfile-vers
new file mode 100644
index 0000000000..22e24bf62a
--- /dev/null
+++ b/usr/src/lib/libhotplug/common/mapfile-vers
@@ -0,0 +1,63 @@
+#
+# 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.
+#
+
+#
+# MAPFILE HEADER START
+#
+# WARNING: STOP NOW. DO NOT MODIFY THIS FILE.
+# Object versioning must comply with the rules detailed in
+#
+# usr/src/lib/README.mapfiles
+#
+# You should not be making modifications here until you've read the most current
+# copy of that file. If you need help, contact a gatekeeper for guidance.
+#
+# MAPFILE HEADER END
+#
+
+SUNWprivate_1.1 {
+ global:
+ hp_init;
+ hp_fini;
+ hp_traverse;
+ hp_name;
+ hp_type;
+ hp_description;
+ hp_state;
+ hp_usage;
+ hp_parent;
+ hp_child;
+ hp_sibling;
+ hp_path;
+ hp_set_state;
+ hp_set_private;
+ hp_get_private;
+ hp_pack;
+ hp_unpack;
+ hp_last_change;
+ libhotplug_debug;
+ local:
+ *;
+};
diff --git a/usr/src/lib/libhotplug/i386/Makefile b/usr/src/lib/libhotplug/i386/Makefile
new file mode 100644
index 0000000000..c86be4377c
--- /dev/null
+++ b/usr/src/lib/libhotplug/i386/Makefile
@@ -0,0 +1,28 @@
+#
+# 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.
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
diff --git a/usr/src/lib/libhotplug/sparc/Makefile b/usr/src/lib/libhotplug/sparc/Makefile
new file mode 100644
index 0000000000..c86be4377c
--- /dev/null
+++ b/usr/src/lib/libhotplug/sparc/Makefile
@@ -0,0 +1,28 @@
+#
+# 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.
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
diff --git a/usr/src/lib/libhotplug/sparcv9/Makefile b/usr/src/lib/libhotplug/sparcv9/Makefile
new file mode 100644
index 0000000000..036c13779e
--- /dev/null
+++ b/usr/src/lib/libhotplug/sparcv9/Makefile
@@ -0,0 +1,29 @@
+#
+# 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.
+#
+
+include ../Makefile.com
+include ../../Makefile.lib.64
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64)
diff --git a/usr/src/lib/libsecdb/auth_attr.txt b/usr/src/lib/libsecdb/auth_attr.txt
index b09d1f7a1e..12123ba3de 100644
--- a/usr/src/lib/libsecdb/auth_attr.txt
+++ b/usr/src/lib/libsecdb/auth_attr.txt
@@ -58,6 +58,9 @@ solaris.file.:::File Operations::help=FileHeader.html
solaris.file.chown:::Change File Owner::help=FileChown.html
solaris.file.owner:::Act as File Owner::help=FileOwner.html
#
+solaris.hotplug.:::Hotplug::help=HotplugHeader.html
+solaris.hotplug.modify:::Modify Hotplug Connections::help=HotplugModify.html
+#
solaris.jobs.:::Job Scheduler::help=JobHeader.html
solaris.jobs.admin:::Manage All Jobs::help=AuthJobsAdmin.html
solaris.jobs.grant:::Delegate Cron & At Administration::help=JobsGrant.html
@@ -129,6 +132,7 @@ solaris.smf.manage.extended-accounting.flow:::Manage Flow Extended Accounting Se
solaris.smf.manage.extended-accounting.process:::Manage Process Extended Accounting Service States::help=SmfExAcctProcessStates.html
solaris.smf.manage.extended-accounting.flow:::Manage Task Extended Accounting Service States::help=SmfExAcctTaskStates.html
solaris.smf.manage.hal:::Manage HAL Service States::help=SmfHALStates.html
+solaris.smf.manage.hotplug:::Manage Hotplug Service::help=SmfManageHotplug.html
solaris.smf.manage.idmap:::Manage Identity Mapping Service States::help=SmfIdmapStates.html
solaris.smf.manage.inetd:::Manage inetd and inetd managed services States::help=SmfIntedStates.html
solaris.smf.manage.ipsec:::Manage IPsec Service States::help=SmfIPsecStates.html
diff --git a/usr/src/lib/libsecdb/help/auths/HotplugHeader.html b/usr/src/lib/libsecdb/help/auths/HotplugHeader.html
new file mode 100644
index 0000000000..240aaf670e
--- /dev/null
+++ b/usr/src/lib/libsecdb/help/auths/HotplugHeader.html
@@ -0,0 +1,34 @@
+<HTML>
+<!--
+ 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.
+-->
+<HEAD>
+ <TITLE> </TITLE>
+
+
+</HEAD>
+<BODY>
+
+The authorizations allow users access and the ability to display and manage hotplug connections.
+</BODY>
+</HTML>
diff --git a/usr/src/lib/libsecdb/help/auths/HotplugModify.html b/usr/src/lib/libsecdb/help/auths/HotplugModify.html
new file mode 100644
index 0000000000..062995ce1f
--- /dev/null
+++ b/usr/src/lib/libsecdb/help/auths/HotplugModify.html
@@ -0,0 +1,39 @@
+<HTML>
+<!--
+ Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ Use is subject to license terms.
+
+ 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
+-->
+<HEAD>
+<!--
+META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1"
+-->
+<!--
+META NAME="GENERATOR" CONTENT="Mozilla/4.02 [en] (X11; U; SunOS 5.6 sun4u) [Netscape]"
+-->
+</HEAD>
+<BODY>
+When Modify Hotplug Connections is in the Authorizations Included column, it grants the authorization to perform hotplug operations on devices in the system.
+<p>
+If Modify Hotplug Connections is grayed, then you are not entitled to Add or Remove this authorization.
+<BR>&nbsp;
+</BODY>
+</HTML>
diff --git a/usr/src/lib/libsecdb/help/auths/Makefile b/usr/src/lib/libsecdb/help/auths/Makefile
index e5611950f5..1d1f131d1d 100644
--- a/usr/src/lib/libsecdb/help/auths/Makefile
+++ b/usr/src/lib/libsecdb/help/auths/Makefile
@@ -36,6 +36,8 @@ HTMLENTS = \
DevCDRW.html \
DevGrant.html \
DevRevoke.html \
+ HotplugHeader.html \
+ HotplugModify.html \
JobHeader.html \
AuthJobsAdmin.html \
JobsGrant.html \
@@ -75,6 +77,7 @@ HTMLENTS = \
SmfInetdStates.html \
SmfIPsecStates.html \
SmfManageHeader.html \
+ SmfManageHotplug.html \
SmfMDNSStates.html \
SmfModifyAppl.html \
SmfModifyDepend.html \
diff --git a/usr/src/lib/libsecdb/help/auths/SmfManageHotplug.html b/usr/src/lib/libsecdb/help/auths/SmfManageHotplug.html
new file mode 100644
index 0000000000..3f207f207e
--- /dev/null
+++ b/usr/src/lib/libsecdb/help/auths/SmfManageHotplug.html
@@ -0,0 +1,37 @@
+<HTML>
+<!--
+ 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.
+-->
+<HEAD>
+ <TITLE> </TITLE>
+
+
+</HEAD>
+<BODY>
+
+When Manage Hotplug Service is in the Authorizations Include column, it grants the authorization to enable, disable, or restart the hotplug service.
+<p>
+If Manage Hotplug Service is grayed, then you are not entitled to Add or Remove this authorization.
+<p>
+</BODY>
+</HTML>
diff --git a/usr/src/lib/libsecdb/help/profiles/Makefile b/usr/src/lib/libsecdb/help/profiles/Makefile
index cb36914f91..fab13a3051 100644
--- a/usr/src/lib/libsecdb/help/profiles/Makefile
+++ b/usr/src/lib/libsecdb/help/profiles/Makefile
@@ -46,6 +46,7 @@ HTMLENTS = \
RtFileSysMngmnt.html \
RtFileSysSecurity.html \
RtFTPMngmnt.html \
+ RtHotplugMngmnt.html \
RtInetdMngmnt.html \
RtIPFilterMngmnt.html \
RtKerberosClntMngmnt.html \
diff --git a/usr/src/lib/libsecdb/help/profiles/RtHotplugMngmnt.html b/usr/src/lib/libsecdb/help/profiles/RtHotplugMngmnt.html
new file mode 100644
index 0000000000..d8576fb08a
--- /dev/null
+++ b/usr/src/lib/libsecdb/help/profiles/RtHotplugMngmnt.html
@@ -0,0 +1,37 @@
+<HTML>
+<!--
+ 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.
+-->
+<HEAD>
+ <TITLE> </TITLE>
+
+
+</HEAD>
+<BODY>
+
+When Hotplug Management is in the Rights Included column, it grants the right to manage hotplug connections for devices in the system.
+<p>
+If Hotplug Management is grayed, then you are not entitled to Add or Remove this right.
+<p>
+</BODY>
+</HTML>
diff --git a/usr/src/lib/libsecdb/prof_attr.txt b/usr/src/lib/libsecdb/prof_attr.txt
index 96bfbfc5c9..ad497ee369 100644
--- a/usr/src/lib/libsecdb/prof_attr.txt
+++ b/usr/src/lib/libsecdb/prof_attr.txt
@@ -48,11 +48,12 @@ Extended Accounting Net Management:::Manage the Net Extended Accounting service:
File System Management:::Manage, mount, share file systems:profiles=SMB Management,VSCAN Management,SMBFS Management;auths=solaris.smf.manage.autofs,solaris.smf.manage.shares.*,solaris.smf.value.shares.*;help=RtFileSysMngmnt.html
File System Security:::Manage file system security attributes:help=RtFileSysSecurity.html
HAL Management:::Manage HAL SMF service:auths=solaris.smf.manage.hal;help=RtHALMngmnt.html
+Hotplug Management:::Manage Hotplug Connections:auths=solaris.smf.manage.hotplug,solaris.hotplug.*;help=RtHotplugMgmt.html
Idmap Name Mapping Management:::Manage Name-based Mapping Rules of Identity Mapping Service:auths=solaris.admin.idmap.rules;help=RtIdmapNameRulesMngmnt.html
Idmap Service Management:::Manage Identity Mapping Service:auths=solaris.smf.manage.idmap,solaris.smf.value.idmap;help=RtIdmapMngmnt.html
Inetd Management:::Manage inetd configuration parameters:auths=solaris.smf.manage.inetd,solaris.smf.value.inetd;help=RtInetdMngmnt.html
Mail Management:::Manage sendmail & queues:auths=solaris.smf.manage.sendmail;help=RtMailMngmnt.html
-Maintenance and Repair:::Maintain and repair a system:auths=solaris.smf.manage.system-log,solaris.label.range,solaris.smf.manage.coreadm,solaris.smf.value.coreadm;help=RtMaintAndRepair.html
+Maintenance and Repair:::Maintain and repair a system:auths=solaris.smf.manage.system-log,solaris.label.range,solaris.smf.manage.coreadm,solaris.smf.value.coreadm;profiles=Hotplug Management;help=RtMaintAndRepair.html
Media Backup:::Backup files and file systems:profiles=NDMP Management;help=RtMediaBkup.html
Media Catalog:::Catalog files and file systems:help=RtMediaCtlg.html
Media Restore:::Restore files and file systems from backups:profiles=NDMP Management;help=RtMediaRestore.html
diff --git a/usr/src/pkgdefs/SUNW0on/prototype_com b/usr/src/pkgdefs/SUNW0on/prototype_com
index 59d362abea..6aa1273d9e 100644
--- a/usr/src/pkgdefs/SUNW0on/prototype_com
+++ b/usr/src/pkgdefs/SUNW0on/prototype_com
@@ -212,6 +212,8 @@ f none usr/lib/help/auths/locale/DevAllocate.html 444 root bin
f none usr/lib/help/auths/locale/DevConfig.html 444 root bin
f none usr/lib/help/auths/locale/DevGrant.html 444 root bin
f none usr/lib/help/auths/locale/DevRevoke.html 444 root bin
+f none usr/lib/help/auths/locale/HotplugHeader.html 444 root bin
+f none usr/lib/help/auths/locale/HotplugModify.html 444 root bin
f none usr/lib/help/auths/locale/JobHeader.html 444 root bin
f none usr/lib/help/auths/locale/AuthJobsAdmin.html 444 root bin
f none usr/lib/help/auths/locale/JobsGrant.html 444 root bin
@@ -248,6 +250,7 @@ f none usr/lib/help/auths/locale/SmfExAcctNetStates.html 444 root bin
f none usr/lib/help/auths/locale/SmfHeader.html 444 root bin
f none usr/lib/help/auths/locale/SmfInetdStates.html 444 root bin
f none usr/lib/help/auths/locale/SmfManageHeader.html 444 root bin
+f none usr/lib/help/auths/locale/SmfManageHotplug.html 444 root bin
f none usr/lib/help/auths/locale/SmfMDNSStates.html 444 root bin
f none usr/lib/help/auths/locale/SmfModifyAppl.html 444 root bin
f none usr/lib/help/auths/locale/SmfModifyDepend.html 444 root bin
@@ -360,6 +363,7 @@ f none usr/lib/help/profiles/locale/RtLogMngmnt.html 444 root bin
f none usr/lib/help/profiles/locale/RtDeviceSecurity.html 444 root bin
f none usr/lib/help/profiles/locale/RtFileSysMngmnt.html 444 root bin
f none usr/lib/help/profiles/locale/RtFTPMngmnt.html 444 root bin
+f none usr/lib/help/profiles/locale/RtHotplugMngmnt.html 444 root bin
f none usr/lib/help/profiles/locale/RtInetdMngmnt.html 444 root bin
f none usr/lib/help/profiles/locale/RtKerberosClntMngmnt.html 444 root bin
f none usr/lib/help/profiles/locale/RtKerberosSrvrMngmnt.html 444 root bin
diff --git a/usr/src/pkgdefs/SUNWarc/prototype_com b/usr/src/pkgdefs/SUNWarc/prototype_com
index 75cd755017..1ef4e5a179 100644
--- a/usr/src/pkgdefs/SUNWarc/prototype_com
+++ b/usr/src/pkgdefs/SUNWarc/prototype_com
@@ -113,6 +113,8 @@ f none usr/lib/llib-lfstyp 644 root bin
f none usr/lib/llib-lfstyp.ln 644 root bin
f none usr/lib/llib-lexacct 644 root bin
f none usr/lib/llib-lexacct.ln 644 root bin
+f none usr/lib/llib-lhotplug 644 root bin
+f none usr/lib/llib-lhotplug.ln 644 root bin
f none usr/lib/llib-lidmap 644 root bin
f none usr/lib/llib-lidmap.ln 644 root bin
s none usr/lib/llib-lintl=../../lib/llib-lintl
diff --git a/usr/src/pkgdefs/SUNWarc/prototype_i386 b/usr/src/pkgdefs/SUNWarc/prototype_i386
index ac0c40de89..d8c8af1d66 100644
--- a/usr/src/pkgdefs/SUNWarc/prototype_i386
+++ b/usr/src/pkgdefs/SUNWarc/prototype_i386
@@ -89,6 +89,7 @@ f none usr/lib/amd64/llib-lidmap.ln 644 root bin
f none usr/lib/amd64/llib-lform.ln 644 root bin
s none usr/lib/amd64/llib-lgen.ln=../../../lib/amd64/llib-lgen.ln
f none usr/lib/amd64/llib-lgss.ln 644 root bin
+f none usr/lib/amd64/llib-lhotplug.ln 644 root bin
s none usr/lib/amd64/llib-lintl.ln=../../../lib/amd64/llib-lintl.ln
f none usr/lib/amd64/llib-lipmi.ln 644 root bin
f none usr/lib/amd64/llib-lipp.ln 644 root bin
diff --git a/usr/src/pkgdefs/SUNWarc/prototype_sparc b/usr/src/pkgdefs/SUNWarc/prototype_sparc
index 9315cb47a8..5c5c2b7ae3 100644
--- a/usr/src/pkgdefs/SUNWarc/prototype_sparc
+++ b/usr/src/pkgdefs/SUNWarc/prototype_sparc
@@ -85,6 +85,7 @@ f none usr/lib/sparcv9/llib-lidmap.ln 644 root bin
f none usr/lib/sparcv9/llib-lform.ln 644 root bin
s none usr/lib/sparcv9/llib-lgen.ln=../../../lib/sparcv9/llib-lgen.ln
f none usr/lib/sparcv9/llib-lgss.ln 644 root bin
+f none usr/lib/sparcv9/llib-lhotplug.ln 644 root bin
s none usr/lib/sparcv9/llib-lintl.ln=../../../lib/sparcv9/llib-lintl.ln
f none usr/lib/sparcv9/llib-lipmi.ln 644 root bin
f none usr/lib/sparcv9/llib-lipp.ln 644 root bin
diff --git a/usr/src/pkgdefs/SUNWckr/prototype_i386 b/usr/src/pkgdefs/SUNWckr/prototype_i386
index bae6c08944..2a6676197e 100644
--- a/usr/src/pkgdefs/SUNWckr/prototype_i386
+++ b/usr/src/pkgdefs/SUNWckr/prototype_i386
@@ -229,7 +229,6 @@ f none kernel/misc/mii 755 root sys
f none kernel/misc/net80211 755 root sys
f none kernel/misc/neti 755 root sys
f none kernel/misc/pcicfg 755 root sys
-f none kernel/misc/pciehpc 755 root sys
f none kernel/misc/pcihp 755 root sys
f none kernel/misc/pci_autoconfig 755 root sys
f none kernel/misc/pcmcia 755 root sys
@@ -450,7 +449,6 @@ f none kernel/misc/amd64/mii 755 root sys
f none kernel/misc/amd64/net80211 755 root sys
f none kernel/misc/amd64/neti 755 root sys
f none kernel/misc/amd64/pcicfg 755 root sys
-f none kernel/misc/amd64/pciehpc 755 root sys
f none kernel/misc/amd64/pcihp 755 root sys
f none kernel/misc/amd64/pci_autoconfig 755 root sys
f none kernel/misc/amd64/pcmcia 755 root sys
diff --git a/usr/src/pkgdefs/SUNWckr/prototype_sparc b/usr/src/pkgdefs/SUNWckr/prototype_sparc
index ae54f53a4a..e086c94862 100644
--- a/usr/src/pkgdefs/SUNWckr/prototype_sparc
+++ b/usr/src/pkgdefs/SUNWckr/prototype_sparc
@@ -211,8 +211,6 @@ f none kernel/misc/sparcv9/mii 755 root sys
f none kernel/misc/sparcv9/neti 755 root sys
f none kernel/misc/sparcv9/pcie 755 root sys
f none kernel/misc/sparcv9/pcihp 755 root sys
-f none kernel/misc/sparcv9/pciehpc 755 root sys
-f none kernel/misc/sparcv9/pcishpc 755 root sys
f none kernel/misc/sparcv9/pcmcia 755 root sys
f none kernel/misc/sparcv9/rpcsec 755 root sys
f none kernel/misc/sparcv9/scsi 755 root sys
diff --git a/usr/src/pkgdefs/SUNWcsl/prototype_com b/usr/src/pkgdefs/SUNWcsl/prototype_com
index 13001f6c96..dcbac34a9e 100644
--- a/usr/src/pkgdefs/SUNWcsl/prototype_com
+++ b/usr/src/pkgdefs/SUNWcsl/prototype_com
@@ -54,6 +54,8 @@ f none usr/lib/cfgadm/sdcard.so.1 755 root bin
s none usr/lib/cfgadm/sdcard.so=./sdcard.so.1
f none usr/lib/cfgadm/pci.so.1 755 root bin
s none usr/lib/cfgadm/pci.so=./pci.so.1
+f none usr/lib/cfgadm/shp.so.1 755 root bin
+s none usr/lib/cfgadm/shp.so=./shp.so.1
f none usr/lib/cfgadm/usb.so.1 755 root bin
s none usr/lib/cfgadm/usb.so=./usb.so.1
f none usr/lib/cfgadm/ib.so.1 755 root bin
@@ -134,6 +136,8 @@ f none usr/lib/libfstyp.so.1 755 root bin
s none usr/lib/libgen.so.1=../../lib/libgen.so.1
s none usr/lib/libgen.so=../../lib/libgen.so.1
s none usr/ccs/lib/libgen.so=../../../lib/libgen.so.1
+s none usr/lib/libhotplug.so=./libhotplug.so.1
+f none usr/lib/libhotplug.so.1 755 root bin
f none usr/lib/libidmap.so.1 755 root bin
s none usr/lib/libidmap.so=./libidmap.so.1
f none usr/lib/libinetsvc.so.1 755 root bin
diff --git a/usr/src/pkgdefs/SUNWcsl/prototype_i386 b/usr/src/pkgdefs/SUNWcsl/prototype_i386
index d7a759507d..a7a4de5692 100644
--- a/usr/src/pkgdefs/SUNWcsl/prototype_i386
+++ b/usr/src/pkgdefs/SUNWcsl/prototype_i386
@@ -67,6 +67,8 @@ f none usr/lib/cfgadm/amd64/sdcard.so.1 755 root bin
s none usr/lib/cfgadm/amd64/sdcard.so=./sdcard.so.1
f none usr/lib/cfgadm/amd64/pci.so.1 755 root bin
s none usr/lib/cfgadm/amd64/pci.so=./pci.so.1
+f none usr/lib/cfgadm/amd64/shp.so.1 755 root bin
+s none usr/lib/cfgadm/amd64/shp.so=./shp.so.1
f none usr/lib/cfgadm/amd64/usb.so.1 755 root bin
s none usr/lib/cfgadm/amd64/usb.so=./usb.so.1
f none usr/lib/cfgadm/amd64/ib.so.1 755 root bin
@@ -184,6 +186,8 @@ f none usr/lib/amd64/libform.so.1 755 root bin
s none usr/lib/amd64/libform.so=libform.so.1
s none usr/lib/amd64/libgen.so.1=../../../lib/amd64/libgen.so.1
s none usr/lib/amd64/libgen.so=../../../lib/amd64/libgen.so.1
+f none usr/lib/amd64/libhotplug.so.1 755 root bin
+s none usr/lib/amd64/libhotplug.so=libhotplug.so.1
f none usr/lib/amd64/libidmap.so.1 755 root bin
s none usr/lib/amd64/libidmap.so=./libidmap.so.1
f none usr/lib/amd64/libike.so.1 755 root bin
diff --git a/usr/src/pkgdefs/SUNWcsl/prototype_sparc b/usr/src/pkgdefs/SUNWcsl/prototype_sparc
index 2eb58e5d50..0bc57576ba 100644
--- a/usr/src/pkgdefs/SUNWcsl/prototype_sparc
+++ b/usr/src/pkgdefs/SUNWcsl/prototype_sparc
@@ -64,6 +64,8 @@ f none usr/lib/cfgadm/sparcv9/sdcard.so.1 755 root bin
s none usr/lib/cfgadm/sparcv9/sdcard.so=./sdcard.so.1
f none usr/lib/cfgadm/sparcv9/pci.so.1 755 root bin
s none usr/lib/cfgadm/sparcv9/pci.so=./pci.so.1
+f none usr/lib/cfgadm/sparcv9/shp.so.1 755 root bin
+s none usr/lib/cfgadm/sparcv9/shp.so=./shp.so.1
f none usr/lib/cfgadm/sparcv9/usb.so.1 755 root bin
s none usr/lib/cfgadm/sparcv9/usb.so=./usb.so.1
f none usr/lib/cfgadm/sparcv9/ib.so.1 755 root bin
@@ -178,6 +180,8 @@ f none usr/lib/sparcv9/libform.so.1 755 root bin
s none usr/lib/sparcv9/libform.so=libform.so.1
s none usr/lib/sparcv9/libgen.so.1=../../../lib/sparcv9/libgen.so.1
s none usr/lib/sparcv9/libgen.so=../../../lib/sparcv9/libgen.so.1
+f none usr/lib/sparcv9/libhotplug.so.1 755 root bin
+s none usr/lib/sparcv9/libhotplug.so=libhotplug.so.1
f none usr/lib/sparcv9/libidmap.so.1 755 root bin
s none usr/lib/sparcv9/libidmap.so=./libidmap.so.1
f none usr/lib/sparcv9/libike.so.1 755 root bin
diff --git a/usr/src/pkgdefs/SUNWcsr/prototype_com b/usr/src/pkgdefs/SUNWcsr/prototype_com
index 168304edec..eea39d6222 100644
--- a/usr/src/pkgdefs/SUNWcsr/prototype_com
+++ b/usr/src/pkgdefs/SUNWcsr/prototype_com
@@ -375,6 +375,7 @@ f none lib/svc/method/svc-auditd 0555 root bin
f none lib/svc/method/svc-consadm 0555 root bin
f none lib/svc/method/svc-cron 0555 root bin
f none lib/svc/method/svc-forwarding 0555 root bin
+f none lib/svc/method/svc-hotplug 0555 root bin
f none lib/svc/method/svc-legacy-routing 0555 root bin
f none lib/svc/method/svc-dlmgmtd 0555 root bin
f none lib/svc/method/svc-nscd 0555 root bin
@@ -546,6 +547,7 @@ f manifest var/svc/manifest/system/filesystem/local-fs.xml 0444 root sys
f manifest var/svc/manifest/system/filesystem/minimal-fs.xml 0444 root sys
f manifest var/svc/manifest/system/filesystem/root-fs.xml 0444 root sys
f manifest var/svc/manifest/system/filesystem/usr-fs.xml 0444 root sys
+f manifest var/svc/manifest/system/hotplug.xml 0444 root sys
d none var/svc/manifest/system/security 755 root sys
f manifest var/svc/manifest/system/auditd.xml 0444 root sys
f manifest var/svc/manifest/system/consadm.xml 0444 root sys
diff --git a/usr/src/pkgdefs/SUNWcsu/prototype_com b/usr/src/pkgdefs/SUNWcsu/prototype_com
index 6d7b3919c7..1e0a68fe8d 100644
--- a/usr/src/pkgdefs/SUNWcsu/prototype_com
+++ b/usr/src/pkgdefs/SUNWcsu/prototype_com
@@ -475,6 +475,8 @@ f none usr/lib/help/auths/locale/C/DevGrant.html 444 root bin
f none usr/lib/help/auths/locale/C/DevRevoke.html 444 root bin
f none usr/lib/help/auths/locale/C/DhcpmgrHeader.html 444 root bin
f none usr/lib/help/auths/locale/C/DhcpmgrWrite.html 444 root bin
+f none usr/lib/help/auths/locale/C/HotplugHeader.html 444 root bin
+f none usr/lib/help/auths/locale/C/HotplugModify.html 444 root bin
f none usr/lib/help/auths/locale/C/JobHeader.html 444 root bin
f none usr/lib/help/auths/locale/C/JobsGrant.html 444 root bin
f none usr/lib/help/auths/locale/C/LoginEnable.html 444 root bin
@@ -494,6 +496,7 @@ f none usr/lib/help/auths/locale/C/SmfExAcctTaskStates.html 444 root bin
f none usr/lib/help/auths/locale/C/SmfExAcctNetStates.html 444 root bin
f none usr/lib/help/auths/locale/C/SmfHeader.html 444 root bin
f none usr/lib/help/auths/locale/C/SmfManageHeader.html 444 root bin
+f none usr/lib/help/auths/locale/C/SmfManageHotplug.html 444 root bin
f none usr/lib/help/auths/locale/C/SmfMDNSStates.html 444 root bin
f none usr/lib/help/auths/locale/C/SmfModifyAppl.html 444 root bin
f none usr/lib/help/auths/locale/C/SmfModifyDepend.html 444 root bin
@@ -586,6 +589,7 @@ f none usr/lib/help/profiles/locale/C/RtDeviceSecurity.html 444 root bin
f none usr/lib/help/profiles/locale/C/RtFTPMngmnt.html 444 root bin
f none usr/lib/help/profiles/locale/C/RtFileSysMngmnt.html 444 root bin
f none usr/lib/help/profiles/locale/C/RtFileSysSecurity.html 444 root bin
+f none usr/lib/help/profiles/locale/C/RtHotplugMngmnt.html 444 root bin
f none usr/lib/help/profiles/locale/C/RtInetdMngmnt.html 444 root bin
f none usr/lib/help/profiles/locale/C/RtIPFilterMngmnt.html 444 root bin
f none usr/lib/help/profiles/locale/C/RtKerberosClntMngmnt.html 444 root bin
@@ -633,6 +637,7 @@ f none usr/lib/help/profiles/locale/C/RtCPUPowerManagement.html 444 root bin
f none usr/lib/help/profiles/locale/C/RtMMSAdmin.html 444 root bin
f none usr/lib/help/profiles/locale/C/RtMMSOper.html 444 root bin
f none usr/lib/help/profiles/locale/C/RtMMSUser.html 444 root bin
+f none usr/lib/hotplugd 555 root bin
d none usr/lib/iconv 755 root bin
f none usr/lib/iconv/646da.8859.t 444 root bin
f none usr/lib/iconv/646de.8859.t 444 root bin
@@ -864,6 +869,7 @@ f none usr/sbin/groupmod 555 root sys
f none usr/sbin/grpck 555 root bin
f none usr/sbin/halt 755 root bin
s none usr/sbin/hostconfig=../../sbin/hostconfig
+f none usr/sbin/hotplug 555 root bin
f none usr/sbin/idmap 555 root bin
f none usr/sbin/if_mpadm 555 root bin
s none usr/sbin/ifconfig=../../sbin/ifconfig
diff --git a/usr/src/pkgdefs/SUNWefck/prototype_com b/usr/src/pkgdefs/SUNWefck/prototype_com
index 67e4df86ab..d9c2d0bcea 100644
--- a/usr/src/pkgdefs/SUNWefck/prototype_com
+++ b/usr/src/pkgdefs/SUNWefck/prototype_com
@@ -19,9 +19,7 @@
# CDDL HEADER END
#
#
-#pragma ident "%Z%%M% %I% %E% SMI"
-#
-# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# This required package information file contains a list of package contents.
@@ -54,5 +52,5 @@ d none kernel/misc 755 root sys
d none kernel/misc/sparcv9 755 root sys
f none kernel/misc/sparcv9/fcodem 755 root sys
f none kernel/misc/sparcv9/fcpci 755 root sys
-f none kernel/misc/sparcv9/pcicfg.e 755 root sys
+f none kernel/misc/sparcv9/pcicfg 755 root sys
diff --git a/usr/src/pkgdefs/SUNWhea/prototype_com b/usr/src/pkgdefs/SUNWhea/prototype_com
index 03cfc436a0..3129ef6be5 100644
--- a/usr/src/pkgdefs/SUNWhea/prototype_com
+++ b/usr/src/pkgdefs/SUNWhea/prototype_com
@@ -751,6 +751,8 @@ f none usr/include/sys/dc_ki.h 644 root bin
f none usr/include/sys/ddi.h 644 root bin
f none usr/include/sys/ddifm.h 644 root bin
f none usr/include/sys/ddifm_impl.h 644 root bin
+f none usr/include/sys/ddi_hp.h 644 root bin
+f none usr/include/sys/ddi_hp_impl.h 644 root bin
f none usr/include/sys/ddi_impldefs.h 644 root bin
f none usr/include/sys/ddi_implfuncs.h 644 root bin
f none usr/include/sys/ddi_intr.h 644 root bin
diff --git a/usr/src/tools/scripts/bfu.sh b/usr/src/tools/scripts/bfu.sh
index 53045ea126..67acb35a90 100644
--- a/usr/src/tools/scripts/bfu.sh
+++ b/usr/src/tools/scripts/bfu.sh
@@ -7180,6 +7180,46 @@ mondo_loop() {
rm -f $usr/kernel/pcbe/sparcv9/pcbe.SUNW,UltraSPARC-T1
#
+ # Remove hotplug modules
+ #
+ rm -f $root/kernel/misc/pciehpc
+ rm -f $root/kernel/misc/amd64/pciehpc
+ rm -f $root/kernel/misc/sparcv9/pciehpc
+ rm -f $root/kernel/misc/sparcv9/pcishpc
+ rm -f $root/kernel/misc/sparcv9/pcicfg.e
+ rm -f $root/kernel/misc/sparcv9/pcicfg
+ rm -f $root/lib/svc/method/svc-hotplug
+ rm -f $root/var/svc/manifest/system/hotplug.xml
+ rm -f $usr/include/sys/ddi_hp.h
+ rm -f $usr/include/sys/ddi_hp_impl.h
+ rm -f $usr/lib/cfgadm/shp.so.1
+ rm -f $usr/lib/cfgadm/shp.so
+ rm -f $usr/lib/cfgadm/amd64/shp.so.1
+ rm -f $usr/lib/cfgadm/amd64/shp.so
+ rm -f $usr/lib/cfgadm/sparcv9/shp.so.1
+ rm -f $usr/lib/cfgadm/sparcv9/shp.so
+ rm -f $usr/lib/help/auths/locale/HotplugHeader.html
+ rm -f $usr/lib/help/auths/locale/HotplugModify.html
+ rm -f $usr/lib/help/auths/locale/SmfManageHotplug.html
+ rm -f $usr/lib/help/auths/locale/C/HotplugHeader.html
+ rm -f $usr/lib/help/auths/locale/C/HotplugModify.html
+ rm -f $usr/lib/help/auths/locale/C/SmfManageHotplug.html
+ rm -f $usr/lib/help/profiles/locale/RtHotplugMngmnt.html
+ rm -f $usr/lib/help/profiles/locale/C/RtHotplugMngmnt.html
+ rm -f $usr/lib/hotplugd
+ rm -f $usr/lib/libhotplug.so
+ rm -f $usr/lib/libhotplug.so.1
+ rm -f $usr/lib/amd64/libhotplug.so.1
+ rm -f $usr/lib/amd64/libhotplug.so
+ rm -f $usr/lib/sparcv9/libhotplug.so.1
+ rm -f $usr/lib/sparcv9/libhotplug.so
+ rm -f $usr/lib/llib-lhotplug
+ rm -f $usr/lib/llib-lhotplug.ln
+ rm -f $usr/lib/amd64/llib-lhotplug.ln
+ rm -f $usr/lib/sparcv9/llib-lhotplug.ln
+ rm -f $usr/sbin/hotplug
+
+ #
# Remove the IPsec encryption and authentication modules.
# IPsec now uses the Kernel Crypto Framework for crypto.
#
diff --git a/usr/src/uts/common/Makefile.files b/usr/src/uts/common/Makefile.files
index 8ded90867e..3aa180f583 100644
--- a/usr/src/uts/common/Makefile.files
+++ b/usr/src/uts/common/Makefile.files
@@ -132,6 +132,8 @@ GENUNIX_OBJS += \
cyclic.o \
ddi.o \
ddifm.o \
+ ddi_hp_impl.o \
+ ddi_hp_ndi.o \
ddi_intr.o \
ddi_intr_impl.o \
ddi_intr_irm.o \
@@ -978,11 +980,9 @@ VUIDPS2_OBJS += vuidmice.o vuidps2.o
HPCSVC_OBJS += hpcsvc.o
-PCIHPNEXUS_OBJS += pcihp.o
-
-PCIEHPCNEXUS_OBJS += pciehpc.o
+PCIE_MISC_OBJS += pcie.o pcie_fault.o pcie_hp.o pciehpc.o pcishpc.o pcie_pwr.o
-PCISHPC_OBJS += pcishpc.o
+PCIHPNEXUS_OBJS += pcihp.o
OPENEEPR_OBJS += openprom.o
@@ -1813,8 +1813,6 @@ NATIVE_INC_PATH += $(INC_PATH) $(CCYFLAG)$(UTSBASE)/common
AS_INC_PATH += $(INC_PATH) -I$(UTSBASE)/common
INCLUDE_PATH += $(INC_PATH) $(CCYFLAG)$(UTSBASE)/common
-#
-PCIE_MISC_OBJS += pcie.o pcie_fault.o pcie_pwr.o
PCIEB_OBJS += pcieb.o
# Chelsio N110 10G NIC driver module
diff --git a/usr/src/uts/common/Makefile.rules b/usr/src/uts/common/Makefile.rules
index e45169a005..ebb286f385 100644
--- a/usr/src/uts/common/Makefile.rules
+++ b/usr/src/uts/common/Makefile.rules
@@ -716,11 +716,7 @@ $(OBJS_DIR)/%.o: $(UTSBASE)/common/io/hotplug/hpcsvc/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
-$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/hotplug/pciehpc/%.c
- $(COMPILE.c) -o $@ $<
- $(CTFCONVERT_O)
-
-$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/hotplug/pcishpc/%.c
+$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/pciex/hotplug/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
@@ -1993,10 +1989,7 @@ $(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/pciex/%.c
$(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/hotplug/hpcsvc/%.c
@($(LHEAD) $(LINT.c) $< $(LTAIL))
-$(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/hotplug/pciehpc/%.c
- @($(LHEAD) $(LINT.c) $< $(LTAIL))
-
-$(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/hotplug/pcishpc/%.c
+$(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/pciex/hotplug/%.c
@($(LHEAD) $(LINT.c) $< $(LTAIL))
$(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/hotplug/pcihp/%.c
diff --git a/usr/src/uts/common/io/busra.c b/usr/src/uts/common/io/busra.c
index 4a059560f7..8a72954676 100644
--- a/usr/src/uts/common/io/busra.c
+++ b/usr/src/uts/common/io/busra.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -127,6 +127,15 @@ static int isnot_pow2(uint64_t value);
static int claim_pci_busnum(dev_info_t *dip, void *arg);
static int ra_map_exist(dev_info_t *dip, char *type);
+static int pci_get_available_prop(dev_info_t *dip, uint64_t base,
+ uint64_t len, char *busra_type);
+static int pci_put_available_prop(dev_info_t *dip, uint64_t base,
+ uint64_t len, char *busra_type);
+static uint32_t pci_type_ra2pci(char *type);
+static boolean_t is_pcie_fabric(dev_info_t *dip);
+
+#define PCI_ADDR_TYPE_MASK (PCI_REG_ADDR_M | PCI_REG_PF_M)
+#define PCI_ADDR_TYPE_INVAL 0xffffffff
#define RA_INSERT(prev, el) \
el->ra_next = *prev; \
@@ -151,7 +160,7 @@ _init()
int ret;
mutex_init(&ra_lock, NULL, MUTEX_DRIVER,
- (void *)(intptr_t)__ipltospl(SPL7 - 1));
+ (void *)(intptr_t)__ipltospl(SPL7 - 1));
if ((ret = mod_install(&modlinkage)) != 0) {
mutex_destroy(&ra_lock);
}
@@ -206,9 +215,9 @@ ndi_ra_map_setup(dev_info_t *dip, char *type)
if (dipmap == NULL) {
if (backtype == NULL) {
typemapp = (struct ra_type_map *)
- kmem_zalloc(sizeof (*typemapp), KM_SLEEP);
+ kmem_zalloc(sizeof (*typemapp), KM_SLEEP);
typemapp->type = (char *)kmem_zalloc(strlen(type) + 1,
- KM_SLEEP);
+ KM_SLEEP);
(void) strcpy(typemapp->type, type);
RA_INSERT(&ra_map_list_head, typemapp);
} else {
@@ -217,7 +226,7 @@ ndi_ra_map_setup(dev_info_t *dip, char *type)
if (backdip == NULL) {
/* allocate and insert in list of dips for this type */
dipmap = (struct ra_dip_type *)
- kmem_zalloc(sizeof (*dipmap), KM_SLEEP);
+ kmem_zalloc(sizeof (*dipmap), KM_SLEEP);
dipmap->ra_dip = dip;
RA_INSERT(&typemapp->ra_dip_list, dipmap);
}
@@ -413,7 +422,7 @@ ndi_ra_free(dev_info_t *dip, uint64_t base, uint64_t len, char *type,
} else if (base < mapp->ra_base) {
/* somewhere in between so just an insert */
newmap = (struct ra_resource *)
- kmem_zalloc(sizeof (*newmap), KM_SLEEP);
+ kmem_zalloc(sizeof (*newmap), KM_SLEEP);
newmap->ra_base = base;
newmap->ra_len = len;
RA_INSERT(backp, newmap);
@@ -423,13 +432,20 @@ ndi_ra_free(dev_info_t *dip, uint64_t base, uint64_t len, char *type,
if (mapp == NULL) {
/* stick on end */
newmap = (struct ra_resource *)
- kmem_zalloc(sizeof (*newmap), KM_SLEEP);
+ kmem_zalloc(sizeof (*newmap), KM_SLEEP);
newmap->ra_base = base;
newmap->ra_len = len;
RA_INSERT(backp, newmap);
}
mutex_exit(&ra_lock);
+
+ /*
+ * Update dip's "available" property, adding this piece of
+ * resource to the pool.
+ */
+ (void) pci_put_available_prop(dip, base, len, type);
+done:
return (NDI_SUCCESS);
overlap:
@@ -485,10 +501,9 @@ adjust_link(struct ra_resource **backp, struct ra_resource *mapp,
} else {
/* in the middle */
newmap = (struct ra_resource *)
- kmem_zalloc(sizeof (*newmap), KM_SLEEP);
+ kmem_zalloc(sizeof (*newmap), KM_SLEEP);
newmap->ra_base = base + len;
- newmap->ra_len = mapp->ra_len -
- (len + newlen);
+ newmap->ra_len = mapp->ra_len - (len + newlen);
mapp->ra_len = newlen;
RA_INSERT(&(mapp->ra_next), newmap);
}
@@ -523,7 +538,7 @@ ndi_ra_alloc(dev_info_t *dip, ndi_ra_request_t *req, uint64_t *retbasep,
if (req->ra_flags & NDI_RA_ALIGN_SIZE) {
if (isnot_pow2(req->ra_len)) {
DEBUGPRT(CE_WARN, "ndi_ra_alloc: bad length(pow2) 0x%"
- PRIx64, req->ra_len);
+ PRIx64, req->ra_len);
*retbasep = 0;
*retlenp = 0;
return (NDI_FAILURE);
@@ -543,7 +558,7 @@ ndi_ra_alloc(dev_info_t *dip, ndi_ra_request_t *req, uint64_t *retbasep,
}
DEBUGPRT(CE_CONT, "ndi_ra_alloc: mapp = %p len=%" PRIx64 ", mask=%"
- PRIx64 "\n", (void *)mapp, len, mask);
+ PRIx64 "\n", (void *)mapp, len, mask);
backp = &(dipmap->ra_rangeset);
backlargestp = NULL;
@@ -560,11 +575,10 @@ ndi_ra_alloc(dev_info_t *dip, ndi_ra_request_t *req, uint64_t *retbasep,
if ((upper == 0) || (upper < req->ra_boundlen))
upper = ~(uint64_t)0;
DEBUGPRT(CE_CONT, "ndi_ra_alloc: ra_len = %" PRIx64 ", len = %"
- PRIx64 " ra_base=%" PRIx64 ", mask=%" PRIx64
- "\n", mapp->ra_len, len, mapp->ra_base, mask);
- for (; mapp != NULL &&
- (mapp->ra_base + mapp->ra_len) < lower;
- backp = &(mapp->ra_next), mapp = mapp->ra_next) {
+ PRIx64 " ra_base=%" PRIx64 ", mask=%" PRIx64
+ "\n", mapp->ra_len, len, mapp->ra_base, mask);
+ for (; mapp != NULL && (mapp->ra_base + mapp->ra_len) < lower;
+ backp = &(mapp->ra_next), mapp = mapp->ra_next) {
if (((mapp->ra_len + mapp->ra_base) == 0) ||
((mapp->ra_len + mapp->ra_base) < mapp->ra_len))
/*
@@ -583,9 +597,9 @@ ndi_ra_alloc(dev_info_t *dip, ndi_ra_request_t *req, uint64_t *retbasep,
if (!(req->ra_flags & NDI_RA_ALLOC_SPECIFIED)) {
/* first fit - not user specified */
DEBUGPRT(CE_CONT, "ndi_ra_alloc(unspecified request)"
- "lower=%" PRIx64 ", upper=%" PRIx64 "\n", lower, upper);
+ "lower=%" PRIx64 ", upper=%" PRIx64 "\n", lower, upper);
for (; mapp != NULL && mapp->ra_base <= upper;
- backp = &(mapp->ra_next), mapp = mapp->ra_next) {
+ backp = &(mapp->ra_next), mapp = mapp->ra_next) {
DEBUGPRT(CE_CONT, "ndi_ra_alloc: ra_len = %" PRIx64
", len = %" PRIx64 "", mapp->ra_len, len);
@@ -606,7 +620,7 @@ ndi_ra_alloc(dev_info_t *dip, ndi_ra_request_t *req, uint64_t *retbasep,
base = base & ~mask;
base += (mask + 1);
DEBUGPRT(CE_CONT, "\tnew base=%" PRIx64 "\n",
- base);
+ base);
/*
* Check to see if the new base is past
@@ -622,7 +636,7 @@ ndi_ra_alloc(dev_info_t *dip, ndi_ra_request_t *req, uint64_t *retbasep,
remlen = upper - base;
else
remlen = mapp->ra_len -
- (base - mapp->ra_base);
+ (base - mapp->ra_base);
if ((backlargestp == NULL) ||
(largestlen < remlen)) {
@@ -656,7 +670,7 @@ ndi_ra_alloc(dev_info_t *dip, ndi_ra_request_t *req, uint64_t *retbasep,
base = req->ra_addr;
len = req->ra_len;
for (; mapp != NULL && mapp->ra_base <= upper;
- backp = &(mapp->ra_next), mapp = mapp->ra_next) {
+ backp = &(mapp->ra_next), mapp = mapp->ra_next) {
if (base >= mapp->ra_base &&
((base - mapp->ra_base) < mapp->ra_len)) {
/*
@@ -696,7 +710,7 @@ ndi_ra_alloc(dev_info_t *dip, ndi_ra_request_t *req, uint64_t *retbasep,
(req->ra_flags & NDI_RA_ALLOC_PARTIAL_OK) &&
(backlargestp != NULL)) {
adjust_link(backlargestp, *backlargestp, largestbase,
- largestlen);
+ largestlen);
base = largestbase;
len = largestlen;
@@ -712,6 +726,14 @@ ndi_ra_alloc(dev_info_t *dip, ndi_ra_request_t *req, uint64_t *retbasep,
*retbasep = base;
*retlenp = len;
}
+
+ /*
+ * Update dip's "available" property, substract this piece of
+ * resource from the pool.
+ */
+ if ((rval == NDI_SUCCESS) || (rval == NDI_RA_PARTIAL_REQ))
+ (void) pci_get_available_prop(dip, *retbasep, *retlenp, type);
+
return (rval);
}
@@ -748,7 +770,7 @@ isa_resource_setup()
used = ddi_find_devinfo("used-resources", -1, 0);
if (used == NULL) {
DEBUGPRT(CE_CONT,
- "isa_resource_setup: used-resources not found");
+ "isa_resource_setup: used-resources not found");
return (NDI_FAILURE);
}
@@ -869,7 +891,7 @@ ra_dump_all(char *type, dev_info_t *dip)
}
cmn_err(CE_CONT, "type is %s\n", typemap->type);
for (dipmap = typemap->ra_dip_list; dipmap != NULL;
- dipmap = dipmap->ra_next) {
+ dipmap = dipmap->ra_next) {
if (dip != NULL) {
if ((dipmap->ra_dip) != dip)
continue;
@@ -877,7 +899,7 @@ ra_dump_all(char *type, dev_info_t *dip)
cmn_err(CE_CONT, " dip is %p\n",
(void *)dipmap->ra_dip);
for (res = dipmap->ra_rangeset; res != NULL;
- res = res->ra_next) {
+ res = res->ra_next) {
cmn_err(CE_CONT, "\t range is %" PRIx64
" %" PRIx64 "\n", res->ra_base,
res->ra_len);
@@ -942,6 +964,26 @@ pci_resource_setup(dev_info_t *dip)
* code would really become an assert to make sure this
* function is not called for the same dip twice.
*/
+ /*
+ * Another user for the check below is hotplug PCI/PCIe bridges.
+ *
+ * For PCI/PCIE devices under a PCIE hierarchy, ndi_ra_alloc/free
+ * will update the devinfo node's "available" property, to reflect
+ * the fact that a piece of resource has been removed/added to
+ * a devinfo node.
+ * During probe of a new PCI bridge in the hotplug case, PCI
+ * configurator firstly allocates maximum MEM/IO from its parent,
+ * then calls ndi_ra_free() to use these resources to setup busra
+ * pool for the new bridge, as well as adding these resources to
+ * the "available" property of the new devinfo node. Then configu-
+ * rator will attach driver for the bridge before probing its
+ * children, and the bridge driver will then initialize its hotplug
+ * contollers (if it supports hotplug) and HPC driver will call
+ * this function to setup the busra pool, but the resource pool
+ * has already been setup at the first of pcicfg_probe_bridge(),
+ * thus we need the check below to return directly in this case.
+ * Otherwise the ndi_ra_free() below will see overlapping resources.
+ */
{
if (ra_map_exist(dip, NDI_RA_TYPE_MEM) == NDI_SUCCESS) {
return (NDI_FAILURE);
@@ -979,45 +1021,54 @@ pci_resource_setup(dev_info_t *dip)
if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
"available", (caddr_t)&regs, &rlen) == DDI_SUCCESS) {
/*
+ * Remove "available" property as the entries will be
+ * re-created in ndi_ra_free() below, note prom based
+ * property will not be removed. But in ndi_ra_free()
+ * we'll be creating non prom based property entries.
+ */
+ (void) ndi_prop_remove(DDI_DEV_T_NONE, dip, "available");
+ /*
* create the available resource list for both memory and
* io space
*/
rcount = rlen / sizeof (pci_regspec_t);
for (i = 0; i < rcount; i++) {
- switch (PCI_REG_ADDR_G(regs[i].pci_phys_hi)) {
- case PCI_REG_ADDR_G(PCI_ADDR_MEM32):
- (void) ndi_ra_free(dip,
- (uint64_t)regs[i].pci_phys_low,
- (uint64_t)regs[i].pci_size_low,
- (regs[i].pci_phys_hi & PCI_REG_PF_M) ?
- NDI_RA_TYPE_PCI_PREFETCH_MEM : NDI_RA_TYPE_MEM,
- 0);
- break;
- case PCI_REG_ADDR_G(PCI_ADDR_MEM64):
- (void) ndi_ra_free(dip,
- ((uint64_t)(regs[i].pci_phys_mid) << 32) |
- ((uint64_t)(regs[i].pci_phys_low)),
- ((uint64_t)(regs[i].pci_size_hi) << 32) |
- ((uint64_t)(regs[i].pci_size_low)),
- (regs[i].pci_phys_hi & PCI_REG_PF_M) ?
- NDI_RA_TYPE_PCI_PREFETCH_MEM : NDI_RA_TYPE_MEM,
- 0);
- break;
- case PCI_REG_ADDR_G(PCI_ADDR_IO):
- (void) ndi_ra_free(dip,
- (uint64_t)regs[i].pci_phys_low,
- (uint64_t)regs[i].pci_size_low,
- NDI_RA_TYPE_IO,
- 0);
- break;
- case PCI_REG_ADDR_G(PCI_ADDR_CONFIG):
- break;
- default:
- cmn_err(CE_WARN,
- "pci_resource_setup: bad addr type: %x\n",
- PCI_REG_ADDR_G(regs[i].pci_phys_hi));
- break;
- }
+ switch (PCI_REG_ADDR_G(regs[i].pci_phys_hi)) {
+ case PCI_REG_ADDR_G(PCI_ADDR_MEM32):
+ (void) ndi_ra_free(dip,
+ (uint64_t)regs[i].pci_phys_low,
+ (uint64_t)regs[i].pci_size_low,
+ (regs[i].pci_phys_hi & PCI_REG_PF_M) ?
+ NDI_RA_TYPE_PCI_PREFETCH_MEM :
+ NDI_RA_TYPE_MEM,
+ 0);
+ break;
+ case PCI_REG_ADDR_G(PCI_ADDR_MEM64):
+ (void) ndi_ra_free(dip,
+ ((uint64_t)(regs[i].pci_phys_mid) << 32) |
+ ((uint64_t)(regs[i].pci_phys_low)),
+ ((uint64_t)(regs[i].pci_size_hi) << 32) |
+ ((uint64_t)(regs[i].pci_size_low)),
+ (regs[i].pci_phys_hi & PCI_REG_PF_M) ?
+ NDI_RA_TYPE_PCI_PREFETCH_MEM :
+ NDI_RA_TYPE_MEM,
+ 0);
+ break;
+ case PCI_REG_ADDR_G(PCI_ADDR_IO):
+ (void) ndi_ra_free(dip,
+ (uint64_t)regs[i].pci_phys_low,
+ (uint64_t)regs[i].pci_size_low,
+ NDI_RA_TYPE_IO,
+ 0);
+ break;
+ case PCI_REG_ADDR_G(PCI_ADDR_CONFIG):
+ break;
+ default:
+ cmn_err(CE_WARN,
+ "pci_resource_setup: bad addr type: %x\n",
+ PCI_REG_ADDR_G(regs[i].pci_phys_hi));
+ break;
+ }
}
kmem_free(regs, rlen);
}
@@ -1174,22 +1225,16 @@ pci_resource_setup_avail(dev_info_t *dip, pci_regspec_t *avail_p, int entries)
switch (PCI_REG_ADDR_G(avail_p->pci_phys_hi)) {
case PCI_REG_ADDR_G(PCI_ADDR_MEM32): {
- (void) ndi_ra_free(dip,
- (uint64_t)avail_p->pci_phys_low,
- (uint64_t)avail_p->pci_size_low,
- (avail_p->pci_phys_hi &
- PCI_REG_PF_M) ?
- NDI_RA_TYPE_PCI_PREFETCH_MEM :
- NDI_RA_TYPE_MEM,
- 0);
+ (void) ndi_ra_free(dip, (uint64_t)avail_p->pci_phys_low,
+ (uint64_t)avail_p->pci_size_low,
+ (avail_p->pci_phys_hi & PCI_REG_PF_M) ?
+ NDI_RA_TYPE_PCI_PREFETCH_MEM : NDI_RA_TYPE_MEM,
+ 0);
}
break;
case PCI_REG_ADDR_G(PCI_ADDR_IO):
- (void) ndi_ra_free(dip,
- (uint64_t)avail_p->pci_phys_low,
- (uint64_t)avail_p->pci_size_low,
- NDI_RA_TYPE_IO,
- 0);
+ (void) ndi_ra_free(dip, (uint64_t)avail_p->pci_phys_low,
+ (uint64_t)avail_p->pci_size_low, NDI_RA_TYPE_IO, 0);
break;
default:
goto err;
@@ -1204,6 +1249,457 @@ pci_resource_setup_avail(dev_info_t *dip, pci_regspec_t *avail_p, int entries)
err:
cmn_err(CE_WARN, "pci_resource_setup_avail: bad entry[%d]=%x\n",
- i, avail_p->pci_phys_hi);
+ i, avail_p->pci_phys_hi);
return (NDI_FAILURE);
}
+
+/*
+ * Return true if the devinfo node resides on PCI or PCI Express bus,
+ * sitting in a PCI Express hierarchy.
+ */
+static boolean_t
+is_pcie_fabric(dev_info_t *dip)
+{
+ dev_info_t *root = ddi_root_node();
+ dev_info_t *pdip;
+ boolean_t found = B_FALSE;
+ char *bus;
+
+ /*
+ * Is this pci/pcie ?
+ */
+ if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip,
+ DDI_PROP_DONTPASS, "device_type", &bus) !=
+ DDI_PROP_SUCCESS) {
+ DEBUGPRT(CE_WARN, "is_pcie_fabric: cannot find "
+ "\"device_type\" property for dip %p\n", (void *)dip);
+ return (B_FALSE);
+ }
+
+ if (strcmp(bus, "pciex") == 0) {
+ /* pcie bus, done */
+ ddi_prop_free(bus);
+ return (B_TRUE);
+ } else if (strcmp(bus, "pci") == 0) {
+ /*
+ * pci bus, fall through to check if it resides in
+ * a pcie hierarchy.
+ */
+ ddi_prop_free(bus);
+ } else {
+ /* other bus, return failure */
+ ddi_prop_free(bus);
+ return (B_FALSE);
+ }
+
+ /*
+ * Does this device reside in a pcie fabric ?
+ */
+ for (pdip = ddi_get_parent(dip); pdip && (pdip != root) &&
+ !found; pdip = ddi_get_parent(pdip)) {
+ if (ddi_prop_lookup_string(DDI_DEV_T_ANY, pdip,
+ DDI_PROP_DONTPASS, "device_type", &bus) !=
+ DDI_PROP_SUCCESS)
+ break;
+
+ if (strcmp(bus, "pciex") == 0)
+ found = B_TRUE;
+
+ ddi_prop_free(bus);
+ }
+
+ return (found);
+}
+
+/*
+ * Remove a piece of IO/MEM resource from "available" property of 'dip'.
+ */
+static int
+pci_get_available_prop(dev_info_t *dip, uint64_t base, uint64_t len,
+ char *busra_type)
+{
+ pci_regspec_t *regs, *newregs;
+ uint_t status;
+ int rlen, rcount;
+ int i, j, k;
+ uint64_t dlen;
+ boolean_t found = B_FALSE;
+ uint32_t type;
+
+ /* check if we're manipulating MEM/IO resource */
+ if ((type = pci_type_ra2pci(busra_type)) == PCI_ADDR_TYPE_INVAL)
+ return (DDI_SUCCESS);
+
+ /* check if dip is a pci/pcie device resides in a pcie fabric */
+ if (!is_pcie_fabric(dip))
+ return (DDI_SUCCESS);
+
+ status = ddi_getlongprop(DDI_DEV_T_ANY, dip,
+ DDI_PROP_DONTPASS | DDI_PROP_NOTPROM,
+ "available", (caddr_t)&regs, &rlen);
+
+ ASSERT(status == DDI_SUCCESS);
+ if (status != DDI_SUCCESS)
+ return (status);
+
+ /*
+ * The updated "available" property will at most have one more entry
+ * than existing one (when the requested range is in the middle of
+ * the matched property entry)
+ */
+ newregs = kmem_alloc(rlen + sizeof (pci_regspec_t), KM_SLEEP);
+
+ rcount = rlen / sizeof (pci_regspec_t);
+ for (i = 0, j = 0; i < rcount; i++) {
+ if (type == (regs[i].pci_phys_hi & PCI_ADDR_TYPE_MASK)) {
+ uint64_t range_base, range_len;
+
+ range_base = ((uint64_t)(regs[i].pci_phys_mid) << 32) |
+ ((uint64_t)(regs[i].pci_phys_low));
+ range_len = ((uint64_t)(regs[i].pci_size_hi) << 32) |
+ ((uint64_t)(regs[i].pci_size_low));
+
+ if ((base < range_base) ||
+ (base + len > range_base + range_len)) {
+ /*
+ * not a match, copy the entry
+ */
+ goto copy_entry;
+ }
+
+ /*
+ * range_base base base+len range_base
+ * +range_len
+ * +------------+-----------+----------+
+ * | |///////////| |
+ * +------------+-----------+----------+
+ */
+ /*
+ * Found a match, remove the range out of this entry.
+ */
+ found = B_TRUE;
+
+ dlen = base - range_base;
+ if (dlen != 0) {
+ newregs[j].pci_phys_hi = regs[i].pci_phys_hi;
+ newregs[j].pci_phys_mid =
+ (uint32_t)(range_base >> 32);
+ newregs[j].pci_phys_low =
+ (uint32_t)(range_base);
+ newregs[j].pci_size_hi = (uint32_t)(dlen >> 32);
+ newregs[j].pci_size_low = (uint32_t)dlen;
+ j++;
+ }
+
+ dlen = (range_base + range_len) - (base + len);
+ if (dlen != 0) {
+ newregs[j].pci_phys_hi = regs[i].pci_phys_hi;
+ newregs[j].pci_phys_mid =
+ (uint32_t)((base + len)>> 32);
+ newregs[j].pci_phys_low =
+ (uint32_t)(base + len);
+ newregs[j].pci_size_hi = (uint32_t)(dlen >> 32);
+ newregs[j].pci_size_low = (uint32_t)dlen;
+ j++;
+ }
+
+ /*
+ * We've allocated the resource from the matched
+ * entry, almost finished but still need to copy
+ * the rest entries from the original property
+ * array.
+ */
+ for (k = i + 1; k < rcount; k++) {
+ newregs[j] = regs[k];
+ j++;
+ }
+
+ goto done;
+
+ } else {
+copy_entry:
+ newregs[j] = regs[i];
+ j++;
+ }
+ }
+
+done:
+ /*
+ * This should not fail so assert it. For non-debug kernel we don't
+ * want to panic thus only logging a warning message.
+ */
+ ASSERT(found == B_TRUE);
+ if (!found) {
+ cmn_err(CE_WARN, "pci_get_available_prop: failed to remove "
+ "resource from dip %p : base 0x%" PRIx64 ", len 0x%" PRIX64
+ ", type 0x%x\n", (void *)dip, base, len, type);
+ kmem_free(newregs, rlen + sizeof (pci_regspec_t));
+ kmem_free(regs, rlen);
+
+ return (DDI_FAILURE);
+ }
+
+ /*
+ * Found the resources from parent, update the "available"
+ * property.
+ */
+ if (j == 0) {
+ /* all the resources are consumed, remove the property */
+ (void) ndi_prop_remove(DDI_DEV_T_NONE, dip, "available");
+ } else {
+ /*
+ * There are still resource available in the parent dip,
+ * update with the remaining resources.
+ */
+ (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, dip,
+ "available", (int *)newregs,
+ (j * sizeof (pci_regspec_t)) / sizeof (int));
+ }
+
+ kmem_free(newregs, rlen + sizeof (pci_regspec_t));
+ kmem_free(regs, rlen);
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * Add a piece of IO/MEM resource to "available" property of 'dip'.
+ */
+static int
+pci_put_available_prop(dev_info_t *dip, uint64_t base, uint64_t len,
+ char *busra_type)
+{
+ pci_regspec_t *regs, *newregs;
+ uint_t status;
+ int rlen, rcount;
+ int i, j, k;
+ int matched = 0;
+ uint64_t orig_base = base;
+ uint64_t orig_len = len;
+ uint32_t type;
+
+ /* check if we're manipulating MEM/IO resource */
+ if ((type = pci_type_ra2pci(busra_type)) == PCI_ADDR_TYPE_INVAL)
+ return (DDI_SUCCESS);
+
+ /* check if dip is a pci/pcie device resides in a pcie fabric */
+ if (!is_pcie_fabric(dip))
+ return (DDI_SUCCESS);
+
+ status = ddi_getlongprop(DDI_DEV_T_ANY, dip,
+ DDI_PROP_DONTPASS | DDI_PROP_NOTPROM,
+ "available", (caddr_t)&regs, &rlen);
+
+ switch (status) {
+ case DDI_PROP_NOT_FOUND:
+ goto not_found;
+
+ case DDI_PROP_SUCCESS:
+ break;
+
+ default:
+ return (status);
+ }
+
+ /*
+ * The "available" property exist on the node, try to put this
+ * resource back, merge if there are adjacent resources.
+ *
+ * The updated "available" property will at most have one more entry
+ * than existing one (when there is no adjacent entries thus the new
+ * resource is appended at the end)
+ */
+ newregs = kmem_alloc(rlen + sizeof (pci_regspec_t), KM_SLEEP);
+
+ rcount = rlen / sizeof (pci_regspec_t);
+ for (i = 0, j = 0; i < rcount; i++) {
+ if (type == (regs[i].pci_phys_hi & PCI_ADDR_TYPE_MASK)) {
+ uint64_t range_base, range_len;
+
+ range_base = ((uint64_t)(regs[i].pci_phys_mid) << 32) |
+ ((uint64_t)(regs[i].pci_phys_low));
+ range_len = ((uint64_t)(regs[i].pci_size_hi) << 32) |
+ ((uint64_t)(regs[i].pci_size_low));
+
+ if ((base + len < range_base) ||
+ (base > range_base + range_len)) {
+ /*
+ * Not adjacent, copy the entry and contiue
+ */
+ goto copy_entry;
+ }
+
+ /*
+ * Adjacent or overlap?
+ *
+ * Should not have overlapping resources so assert it.
+ * For non-debug kernel we don't want to panic thus
+ * only logging a warning message.
+ */
+#if 0
+ ASSERT((base + len == range_base) ||
+ (base == range_base + range_len));
+#endif
+ if ((base + len != range_base) &&
+ (base != range_base + range_len)) {
+ cmn_err(CE_WARN, "pci_put_available_prop: "
+ "failed to add resource to dip %p : "
+ "base 0x%" PRIx64 ", len 0x%" PRIx64 " "
+ "overlaps with existing resource "
+ "base 0x%" PRIx64 ", len 0x%" PRIx64 "\n",
+ (void *)dip, orig_base, orig_len,
+ range_base, range_len);
+
+ goto failure;
+ }
+
+ /*
+ * On the left:
+ *
+ * base range_base
+ * +-------------+-------------+
+ * |/////////////| |
+ * +-------------+-------------+
+ * len range_len
+ *
+ * On the right:
+ *
+ * range_base base
+ * +-------------+-------------+
+ * | |/////////////|
+ * +-------------+-------------+
+ * range_len len
+ */
+ /*
+ * There are at most two piece of resources adjacent
+ * with this resource, assert it.
+ */
+ ASSERT(matched < 2);
+
+ if (!(matched < 2)) {
+ cmn_err(CE_WARN, "pci_put_available_prop: "
+ "failed to add resource to dip %p : "
+ "base 0x%" PRIx64 ", len 0x%" PRIx64 " "
+ "found overlaps in existing resources\n",
+ (void *)dip, orig_base, orig_len);
+
+ goto failure;
+ }
+
+ /* setup base & len to refer to the merged range */
+ len += range_len;
+ if (base == range_base + range_len)
+ base = range_base;
+
+ if (matched == 0) {
+ /*
+ * One adjacent entry, add this resource in
+ */
+ newregs[j].pci_phys_hi = regs[i].pci_phys_hi;
+ newregs[j].pci_phys_mid =
+ (uint32_t)(base >> 32);
+ newregs[j].pci_phys_low = (uint32_t)(base);
+ newregs[j].pci_size_hi = (uint32_t)(len >> 32);
+ newregs[j].pci_size_low = (uint32_t)len;
+
+ matched = 1;
+ k = j;
+ j++;
+ } else { /* matched == 1 */
+ /*
+ * Two adjacent entries, merge them together
+ */
+ newregs[k].pci_phys_hi = regs[i].pci_phys_hi;
+ newregs[k].pci_phys_mid =
+ (uint32_t)(base >> 32);
+ newregs[k].pci_phys_low = (uint32_t)(base);
+ newregs[k].pci_size_hi = (uint32_t)(len >> 32);
+ newregs[k].pci_size_low = (uint32_t)len;
+
+ matched = 2;
+ }
+ } else {
+copy_entry:
+ newregs[j] = regs[i];
+ j++;
+ }
+ }
+
+ if (matched == 0) {
+ /* No adjacent entries, append at end */
+ ASSERT(j == rcount);
+
+ /*
+ * According to page 15 of 1275 spec, bit "n" of "available"
+ * should be set to 1.
+ */
+ newregs[j].pci_phys_hi = type;
+ newregs[j].pci_phys_hi |= PCI_REG_REL_M;
+
+ newregs[j].pci_phys_mid = (uint32_t)(base >> 32);
+ newregs[j].pci_phys_low = (uint32_t)base;
+ newregs[j].pci_size_hi = (uint32_t)(len >> 32);
+ newregs[j].pci_size_low = (uint32_t)len;
+
+ j++;
+ }
+
+ (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, dip,
+ "available", (int *)newregs,
+ (j * sizeof (pci_regspec_t)) / sizeof (int));
+
+ kmem_free(newregs, rlen + sizeof (pci_regspec_t));
+ kmem_free(regs, rlen);
+ return (DDI_SUCCESS);
+
+not_found:
+ /*
+ * There is no "available" property on the parent node, create it.
+ */
+ newregs = kmem_alloc(sizeof (pci_regspec_t), KM_SLEEP);
+
+ /*
+ * According to page 15 of 1275 spec, bit "n" of "available" should
+ * be set to 1.
+ */
+ newregs[0].pci_phys_hi = type;
+ newregs[0].pci_phys_hi |= PCI_REG_REL_M;
+
+ newregs[0].pci_phys_mid = (uint32_t)(base >> 32);
+ newregs[0].pci_phys_low = (uint32_t)base;
+ newregs[0].pci_size_hi = (uint32_t)(len >> 32);
+ newregs[0].pci_size_low = (uint32_t)len;
+
+ (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, dip,
+ "available", (int *)newregs,
+ sizeof (pci_regspec_t) / sizeof (int));
+ kmem_free(newregs, sizeof (pci_regspec_t));
+ return (DDI_SUCCESS);
+
+failure:
+ kmem_free(newregs, rlen + sizeof (pci_regspec_t));
+ kmem_free(regs, rlen);
+ return (DDI_FAILURE);
+}
+
+static uint32_t
+pci_type_ra2pci(char *type)
+{
+ uint32_t pci_type = PCI_ADDR_TYPE_INVAL;
+
+ /*
+ * No 64 bit mem support for now
+ */
+ if (strcmp(type, NDI_RA_TYPE_IO) == 0) {
+ pci_type = PCI_ADDR_IO;
+
+ } else if (strcmp(type, NDI_RA_TYPE_MEM) == 0) {
+ pci_type = PCI_ADDR_MEM32;
+
+ } else if (strcmp(type, NDI_RA_TYPE_PCI_PREFETCH_MEM) == 0) {
+ pci_type = PCI_ADDR_MEM32;
+ pci_type |= PCI_REG_PF_M;
+ }
+
+ return (pci_type);
+}
diff --git a/usr/src/uts/common/io/devinfo.c b/usr/src/uts/common/io/devinfo.c
index d1d02ce378..490d7fc28e 100644
--- a/usr/src/uts/common/io/devinfo.c
+++ b/usr/src/uts/common/io/devinfo.c
@@ -54,6 +54,10 @@
#include <sys/disp.h>
#include <sys/kobj.h>
#include <sys/crc32.h>
+#include <sys/ddi_hp.h>
+#include <sys/ddi_hp_impl.h>
+#include <sys/sysmacros.h>
+#include <sys/list.h>
#ifdef DEBUG
@@ -231,6 +235,12 @@ typedef struct i_lnode {
i_link_t *link_out;
} i_lnode_t;
+typedef struct i_hp {
+ di_off_t hp_off; /* Offset of di_hp_t in snapshot */
+ dev_info_t *hp_child; /* Child devinfo node of the di_hp_t */
+ list_node_t hp_link; /* List linkage */
+} i_hp_t;
+
/*
* Soft state associated with each instance of driver open.
*/
@@ -246,6 +256,8 @@ static struct di_state {
mod_hash_t *lnode_hash;
mod_hash_t *link_hash;
+
+ list_t hp_list;
} **di_states;
static kmutex_t di_lock; /* serialize instance assignment */
@@ -298,6 +310,8 @@ static di_off_t di_getmdata(struct ddi_minor_data *, di_off_t *, di_off_t,
struct di_state *);
static di_off_t di_getppdata(struct dev_info *, di_off_t *, struct di_state *);
static di_off_t di_getdpdata(struct dev_info *, di_off_t *, struct di_state *);
+static di_off_t di_gethpdata(ddi_hp_cn_handle_t *, di_off_t *,
+ struct di_state *);
static di_off_t di_getprop(int, struct ddi_prop **, di_off_t *,
struct di_state *, struct dev_info *);
static void di_allocmem(struct di_state *, size_t);
@@ -320,6 +334,7 @@ static int di_cache_update(struct di_state *st);
static void di_cache_print(di_cache_debug_t msglevel, char *fmt, ...);
static int build_vhci_list(dev_info_t *vh_devinfo, void *arg);
static int build_phci_list(dev_info_t *ph_devinfo, void *arg);
+static void di_hotplug_children(struct di_state *st);
extern int modrootloaded;
extern void mdi_walk_vhcis(int (*)(dev_info_t *, void *), void *);
@@ -1341,6 +1356,11 @@ di_snapshot(struct di_state *st)
di_key_dtor, mod_hash_null_valdtor, di_hash_byptr,
NULL, di_key_cmp, KM_SLEEP);
+ if (DINFOHP & st->command) {
+ list_create(&st->hp_list, sizeof (i_hp_t),
+ offsetof(i_hp_t, hp_link));
+ }
+
/*
* copy the device tree
*/
@@ -1350,6 +1370,10 @@ di_snapshot(struct di_state *st)
mdi_walk_vhcis(build_vhci_list, st);
}
+ if (DINFOHP & st->command) {
+ di_hotplug_children(st);
+ }
+
ddi_release_devi(rootnode);
/*
@@ -1696,7 +1720,8 @@ di_copynode(struct dev_info *node, struct di_stack *dsp, struct di_state *st)
{
di_off_t off;
struct di_node *me;
- size_t size; struct dev_info *n;
+ size_t size;
+ struct dev_info *n;
dcmn_err2((CE_CONT, "di_copynode: depth = %x\n", dsp->depth));
ASSERT((node != NULL) && (node == TOP_NODE(dsp)));
@@ -1820,7 +1845,7 @@ di_copynode(struct dev_info *node, struct di_stack *dsp, struct di_state *st)
/*
* An optimization to skip mutex_enter when not needed.
*/
- if (!((DINFOMINOR | DINFOPROP | DINFOPATH) & st->command)) {
+ if (!((DINFOMINOR | DINFOPROP | DINFOPATH | DINFOHP) & st->command)) {
goto priv_data;
}
@@ -1873,7 +1898,7 @@ path:
property:
if (!(DINFOPROP & st->command)) {
- goto priv_data;
+ goto hotplug_data;
}
if (node->devi_drv_prop_ptr) { /* driver property list */
@@ -1913,6 +1938,16 @@ property:
}
}
+hotplug_data:
+ if (!(DINFOHP & st->command)) {
+ goto priv_data;
+ }
+
+ if (node->devi_hp_hdlp) { /* hotplug data */
+ me->hp_data = off;
+ off = di_gethpdata(node->devi_hp_hdlp, &me->hp_data, st);
+ }
+
priv_data:
if (!(DINFOPRIVDATA & st->command)) {
goto pm_info;
@@ -3447,6 +3482,88 @@ di_getdpdata(struct dev_info *node, di_off_t *off_p, struct di_state *st)
}
/*
+ * Copy hotplug data associated with a devinfo node into the snapshot.
+ */
+static di_off_t
+di_gethpdata(ddi_hp_cn_handle_t *hp_hdl, di_off_t *off_p,
+ struct di_state *st)
+{
+ struct i_hp *hp;
+ struct di_hp *me;
+ size_t size;
+ di_off_t off;
+
+ dcmn_err2((CE_CONT, "di_gethpdata:\n"));
+
+ /*
+ * check memory first
+ */
+ off = di_checkmem(st, *off_p, sizeof (struct di_hp));
+ *off_p = off;
+
+ do {
+ me = DI_HP(di_mem_addr(st, off));
+ me->self = off;
+ me->hp_name = 0;
+ me->hp_connection = (int)hp_hdl->cn_info.cn_num;
+ me->hp_depends_on = (int)hp_hdl->cn_info.cn_num_dpd_on;
+ (void) ddihp_cn_getstate(hp_hdl);
+ me->hp_state = (int)hp_hdl->cn_info.cn_state;
+ me->hp_type = (int)hp_hdl->cn_info.cn_type;
+ me->hp_type_str = 0;
+ me->hp_last_change = (uint32_t)hp_hdl->cn_info.cn_last_change;
+ me->hp_child = 0;
+
+ /*
+ * Child links are resolved later by di_hotplug_children().
+ * Store a reference to this di_hp_t in the list used later
+ * by di_hotplug_children().
+ */
+ hp = kmem_zalloc(sizeof (i_hp_t), KM_SLEEP);
+ hp->hp_off = off;
+ hp->hp_child = hp_hdl->cn_info.cn_child;
+ list_insert_tail(&st->hp_list, hp);
+
+ off += sizeof (struct di_hp);
+
+ /* Add name of this di_hp_t to the snapshot */
+ if (hp_hdl->cn_info.cn_name) {
+ size = strlen(hp_hdl->cn_info.cn_name) + 1;
+ me->hp_name = off = di_checkmem(st, off, size);
+ (void) strcpy(di_mem_addr(st, off),
+ hp_hdl->cn_info.cn_name);
+ off += size;
+ }
+
+ /* Add type description of this di_hp_t to the snapshot */
+ if (hp_hdl->cn_info.cn_type_str) {
+ size = strlen(hp_hdl->cn_info.cn_type_str) + 1;
+ me->hp_type_str = off = di_checkmem(st, off, size);
+ (void) strcpy(di_mem_addr(st, off),
+ hp_hdl->cn_info.cn_type_str);
+ off += size;
+ }
+
+ /*
+ * Set link to next in the chain of di_hp_t nodes,
+ * or terminate the chain when processing the last node.
+ */
+ if (hp_hdl->next != NULL) {
+ off = di_checkmem(st, off, sizeof (struct di_hp));
+ me->next = off;
+ } else {
+ me->next = 0;
+ }
+
+ /* Update pointer to next in the chain */
+ hp_hdl = hp_hdl->next;
+
+ } while (hp_hdl);
+
+ return (off);
+}
+
+/*
* The driver is stateful across DINFOCPYALL and DINFOUSRLD.
* This function encapsulates the state machine:
*
@@ -4075,3 +4192,24 @@ di_cache_print(di_cache_debug_t msglevel, char *fmt, ...)
vcmn_err(msglevel, fmt, ap);
va_end(ap);
}
+
+static void
+di_hotplug_children(struct di_state *st)
+{
+ di_off_t off;
+ struct di_hp *hp;
+ struct i_hp *hp_list_node;
+
+ while (hp_list_node = (struct i_hp *)list_remove_head(&st->hp_list)) {
+
+ if ((hp_list_node->hp_child != NULL) &&
+ (di_dip_find(st, hp_list_node->hp_child, &off) == 0)) {
+ hp = DI_HP(di_mem_addr(st, hp_list_node->hp_off));
+ hp->hp_child = off;
+ }
+
+ kmem_free(hp_list_node, sizeof (i_hp_t));
+ }
+
+ list_destroy(&st->hp_list);
+}
diff --git a/usr/src/uts/common/io/hotplug/pciehpc/pciehpc.c b/usr/src/uts/common/io/hotplug/pciehpc/pciehpc.c
deleted file mode 100644
index 735e600c02..0000000000
--- a/usr/src/uts/common/io/hotplug/pciehpc/pciehpc.c
+++ /dev/null
@@ -1,1982 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License (the "License").
- * You may not use this file except in compliance with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information: Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- */
-
-/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
- */
-
-/*
- * PCIEHPC - The Standard PCI Express HotPlug Controller driver module. This
- * driver can be used with PCI Express HotPlug controllers that
- * are compatible with the PCI Express ver 1.0a specification.
- */
-
-#include <sys/types.h>
-#include <sys/note.h>
-#include <sys/conf.h>
-#include <sys/kmem.h>
-#include <sys/debug.h>
-#include <sys/vtrace.h>
-#include <sys/modctl.h>
-#include <sys/autoconf.h>
-#include <sys/varargs.h>
-#include <sys/ddi_impldefs.h>
-#include <sys/pci.h>
-#include <sys/time.h>
-#include <sys/callb.h>
-#include <sys/ddi.h>
-#include <sys/sunddi.h>
-#include <sys/pcie_impl.h>
-#include <sys/hotplug/pci/pciehpc_impl.h>
-
-/*
- * Local data/functions
- */
-
-/* mutex to protect pciehpc_head list */
-static kmutex_t pciehpc_list_mutex;
-
-/* pointer to linked list of pciehpc structures */
-static pciehpc_t *pciehpc_head = NULL;
-
-/* mutex to protect init/uninit controllers */
-static kmutex_t pciehpc_init_mutex;
-static int pciehpc_init_count = 0; /* count of pciehpc instances in use */
-
-static pciehpc_t *pciehpc_create_soft_state(dev_info_t *dip);
-static pciehpc_t *pciehpc_get_soft_state(dev_info_t *dip);
-static void pciehpc_destroy_soft_state(dev_info_t *dip);
-static char *pciehpc_led_state_text(hpc_led_state_t state);
-static void pciehpc_attn_btn_handler(pciehpc_t *ctrl_p);
-static void pciehpc_dev_info(pciehpc_t *ctrl_p);
-
-static int pciehpc_pcie_dev(dev_info_t *dip, ddi_acc_handle_t handle);
-static void pciehpc_disable_errors(pciehpc_t *ctrl_p);
-static void pciehpc_enable_errors(pciehpc_t *ctrl_p);
-
-#ifdef DEBUG
-int pciehpc_debug = 0;
-static void pciehpc_dump_hpregs(pciehpc_t *ctrl_p);
-#endif
-
-/*
- * Module linkage information for the kernel.
- */
-extern struct mod_ops mod_miscops;
-static struct modlmisc modlmisc =
-{
- &mod_miscops,
- "PCIe hotplug driver",
-};
-
-static struct modlinkage modlinkage =
-{
- MODREV_1,
- &modlmisc,
- NULL
-};
-
-
-int
-_init(void)
-{
- int error;
-
- PCIEHPC_DEBUG3((CE_NOTE, "pciehpc: _init() called\n"));
- mutex_init(&pciehpc_list_mutex, NULL, MUTEX_DRIVER, NULL);
- mutex_init(&pciehpc_init_mutex, NULL, MUTEX_DRIVER, NULL);
- if ((error = mod_install(&modlinkage)) != 0) {
- mutex_destroy(&pciehpc_init_mutex);
- mutex_destroy(&pciehpc_list_mutex);
- }
-
- return (error);
-}
-
-int
-_fini(void)
-{
- int error;
-
- PCIEHPC_DEBUG3((CE_NOTE, "pciehpc: _fini() called\n"));
-
- mutex_enter(&pciehpc_init_mutex);
- if (pciehpc_init_count != 0) {
- mutex_exit(&pciehpc_init_mutex);
- return (EBUSY);
- }
- error = mod_remove(&modlinkage);
- if (error != 0) {
- mutex_exit(&pciehpc_init_mutex);
- return (error);
- }
- mutex_destroy(&pciehpc_list_mutex);
- mutex_destroy(&pciehpc_init_mutex);
- return (0);
-}
-
-int
-_info(struct modinfo *modinfop)
-{
- PCIEHPC_DEBUG3((CE_NOTE, "pciehpc: _info() called\n"));
- return (mod_info(&modlinkage, modinfop));
-}
-
-/*
- * pciehpc_init()
- *
- * Initialize Hot Plug Controller if present. The arguments are:
- * dip - Devinfo node pointer to the hot plug bus node
- * regops - register ops to access HPC registers for non-standard
- * HPC hw implementations (e.g: HPC in host PCI-E brdiges)
- * This is NULL for standard HPC in PCIe bridges.
- * Returns:
- * DDI_SUCCESS for successful HPC initialization
- * DDI_FAILURE for errors or if HPC hw not found
- */
-int
-pciehpc_init(dev_info_t *dip, pciehpc_regops_t *regops)
-{
- pciehpc_t *ctrl_p;
-
- PCIEHPC_DEBUG3((CE_NOTE, "pciehpc_init() called (dip=%p)",
- (void *)dip));
-
- mutex_enter(&pciehpc_init_mutex);
-
- /* Make sure that it is not already initialized */
- if (pciehpc_get_soft_state(dip) != NULL) {
- PCIEHPC_DEBUG((CE_WARN,
- "%s%d: pciehpc instance already initialized!",
- ddi_driver_name(dip), ddi_get_instance(dip)));
- mutex_exit(&pciehpc_init_mutex);
- return (DDI_SUCCESS);
- }
-
- /* allocate a new soft state structure */
- ctrl_p = pciehpc_create_soft_state(dip);
-
- /* get PCI device info */
- pciehpc_dev_info(ctrl_p);
-
- /* setup access handle for HPC regs */
- if (regops != NULL) {
- /* HPC access is non-standard; use the supplied reg ops */
- ctrl_p->regops = *regops;
- } else {
- /* standard HPC in a PCIe bridge */
- if (pciehpc_regs_setup(dip, 0, 0, &ctrl_p->regs_base,
- &ctrl_p->cfghdl) != DDI_SUCCESS)
- goto cleanup;
- }
-
- pciehpc_disable_errors(ctrl_p);
-
- /*
- * Set the platform specific hot plug mode.
- */
- ctrl_p->hp_mode = PCIEHPC_NATIVE_HP_MODE; /* default is Native mode */
- ctrl_p->ops.init_hpc_hw = pciehpc_hpc_init;
- ctrl_p->ops.init_hpc_slotinfo = pciehpc_slotinfo_init;
- ctrl_p->ops.disable_hpc_intr = pciehpc_disable_intr;
- ctrl_p->ops.enable_hpc_intr = pciehpc_enable_intr;
- ctrl_p->ops.uninit_hpc_hw = pciehpc_hpc_uninit;
- ctrl_p->ops.uninit_hpc_slotinfo = pciehpc_slotinfo_uninit;
- ctrl_p->ops.probe_hpc = pciehpc_probe_hpc;
-
-#if defined(__i386) || defined(__amd64)
- pciehpc_update_ops(ctrl_p);
-#endif
- if (regops == NULL) { /* it is a standard HPC in a PCIe bridge */
- /* make sure we really have a hot plug controller */
- if ((ctrl_p->ops.probe_hpc)(ctrl_p) != DDI_SUCCESS)
- goto cleanup1;
- }
-
- /* initialize hot plug controller hw */
- if ((ctrl_p->ops.init_hpc_hw)(ctrl_p) != DDI_SUCCESS)
- goto cleanup1;
-
- /* initialize slot information soft state structure */
- if ((ctrl_p->ops.init_hpc_slotinfo)(ctrl_p) != DDI_SUCCESS)
- goto cleanup2;
-
- /* register the hot plug slot with HPS framework */
- if (pciehpc_register_slot(ctrl_p) != DDI_SUCCESS)
- goto cleanup3;
-
- /* HPC initialization is complete now */
- ctrl_p->soft_state |= PCIEHPC_SOFT_STATE_INITIALIZED;
- ctrl_p->soft_state &= ~PCIEHPC_SOFT_STATE_UNINITIALIZED;
-
-#ifdef DEBUG
- /* For debug, dump the HPC registers */
- if (pciehpc_debug > 2)
- pciehpc_dump_hpregs(ctrl_p);
-#endif
-
- /* enable hot plug interrupts/event */
- (void) (ctrl_p->ops.enable_hpc_intr)(ctrl_p);
-
- pciehpc_init_count++;
-
- mutex_exit(&pciehpc_init_mutex);
-
- return (DDI_SUCCESS);
-
-cleanup3:
- (void) (ctrl_p->ops.uninit_hpc_slotinfo)(ctrl_p);
-
-cleanup2:
- (void) (ctrl_p->ops.uninit_hpc_hw)(ctrl_p);
-
-cleanup1:
- pciehpc_enable_errors(ctrl_p);
- /* free up the HPC register mapping if applicable */
- if (ctrl_p->cfghdl)
- pciehpc_regs_teardown(&ctrl_p->cfghdl);
-
-cleanup:
- pciehpc_destroy_soft_state(dip);
- mutex_exit(&pciehpc_init_mutex);
- return (DDI_FAILURE);
-}
-
-/*
- * Uninitialize HPC soft state structure and free up any resources
- * used for the HPC instance.
- */
-int
-pciehpc_uninit(dev_info_t *dip)
-{
- pciehpc_t *ctrl_p;
-
- PCIEHPC_DEBUG3((CE_NOTE, "pciehpc_uninit() called (dip=%p)\n",
- (void *)dip));
-
- mutex_enter(&pciehpc_init_mutex);
-
- /* get the soft state structure for this dip */
- if ((ctrl_p = pciehpc_get_soft_state(dip)) == NULL) {
- mutex_exit(&pciehpc_init_mutex);
- return (DDI_FAILURE);
- }
-
- /* disable interrupts */
- (void) (ctrl_p->ops.disable_hpc_intr)(ctrl_p);
-
- /* unregister the slot */
- (void) pciehpc_unregister_slot(ctrl_p);
-
- /* uninit any slot info data structures */
- (void) (ctrl_p->ops.uninit_hpc_slotinfo)(ctrl_p);
-
- /* uninitialize hpc, remove interrupt handler, etc. */
- (void) (ctrl_p->ops.uninit_hpc_hw)(ctrl_p);
-
- pciehpc_enable_errors(ctrl_p);
-
- /* free up the HPC register mapping if applicable */
- if (ctrl_p->cfghdl)
- pciehpc_regs_teardown(&ctrl_p->cfghdl);
-
- /* destroy the soft state structure */
- pciehpc_destroy_soft_state(dip);
-
- ASSERT(pciehpc_init_count != 0);
-
- pciehpc_init_count--;
-
- mutex_exit(&pciehpc_init_mutex);
-
- return (DDI_SUCCESS);
-}
-
-/*
- * Probe for the inband PCI-E hot plug controller. Returns DDI_SUCCESS
- * if found. This function works only for the standard PCI-E bridge
- * that has inband hot plug controller.
- *
- * NOTE: This won't work for Host-PCIE bridges.
- */
-int
-pciehpc_probe_hpc(pciehpc_t *ctrl_p)
-{
- uint8_t cap_ptr;
- uint8_t cap_id;
- uint16_t status;
-
- /* Read the PCI configuration status register. */
- status = pciehpc_reg_get16(ctrl_p, PCI_CONF_STAT);
-
- /* check for capabilities list */
- if (!(status & PCI_STAT_CAP)) {
- /* no capabilities list */
- return (DDI_FAILURE);
- }
-
- /* Get a pointer to the PCI capabilities list. */
- cap_ptr = pciehpc_reg_get8(ctrl_p, PCI_BCNF_CAP_PTR);
- cap_ptr &= 0xFC; /* mask off reserved bits */
-
- /*
- * Walk thru the capabilities list looking for PCI Express capability
- * structure.
- */
- while (cap_ptr != PCI_CAP_NEXT_PTR_NULL) {
- cap_id = pciehpc_reg_get8(ctrl_p, (uint_t)cap_ptr);
-
- PCIEHPC_DEBUG3((CE_NOTE, "pciehpc_probe_hpc() capability @"
- " pointer=%02x (id=%02x)\n", cap_ptr, cap_id));
-
- if (cap_id == PCI_CAP_ID_PCI_E) {
- uint32_t slot_cap;
-
- /* Read the PCI Express Slot Capabilities Register */
- slot_cap = pciehpc_reg_get32(ctrl_p,
- (uint_t)cap_ptr + PCIE_SLOTCAP);
-
- /* Does it have PCI Express HotPlug capability? */
- if (slot_cap & PCIE_SLOTCAP_HP_CAPABLE) {
- /* Save the offset to PCI Express Capabilities structure */
- ctrl_p->pcie_caps_reg_offset = cap_ptr;
- return (DDI_SUCCESS);
- }
- }
-
- /* Get the pointer to the next capability */
- cap_ptr = pciehpc_reg_get8(ctrl_p, (uint_t)cap_ptr + 1);
- cap_ptr &= 0xFC;
- }
-
- return (DDI_FAILURE);
-}
-
-/*
- * Setup slot information for use with HPS framework.
- */
-int
-pciehpc_slotinfo_init(pciehpc_t *ctrl_p)
-{
- uint32_t slot_capabilities, link_capabilities;
- pciehpc_slot_t *p = &ctrl_p->slot;
-
- /*
- * setup HPS framework slot ops structure
- */
- p->slot_ops.hpc_version = HPC_SLOT_OPS_VERSION;
- p->slot_ops.hpc_op_connect = pciehpc_slot_connect;
- p->slot_ops.hpc_op_disconnect = pciehpc_slot_disconnect;
- p->slot_ops.hpc_op_insert = NULL;
- p->slot_ops.hpc_op_remove = NULL;
- p->slot_ops.hpc_op_control = pciehpc_slot_control;
-
- /*
- * setup HPS framework slot information structure
- */
- p->slot_info.version = HPC_SLOT_OPS_VERSION;
- p->slot_info.slot_type = HPC_SLOT_TYPE_PCIE;
- p->slot_info.slot_flags =
- HPC_SLOT_CREATE_DEVLINK | HPC_SLOT_NO_AUTO_ENABLE;
- p->slot_info.pci_slot_capabilities = HPC_SLOT_64BITS;
- /* the device number is fixed as 0 as per the spec */
- p->slot_info.pci_dev_num = 0;
-
- /* read Slot Capabilities Register */
- slot_capabilities = pciehpc_reg_get32(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCAP);
-
- /* set slot-name/slot-number info */
- pciehpc_set_slot_name(ctrl_p);
-
- /* check if Attn Button present */
- ctrl_p->has_attn = (slot_capabilities & PCIE_SLOTCAP_ATTN_BUTTON) ?
- B_TRUE : B_FALSE;
-
- /* check if Manual Retention Latch sensor present */
- ctrl_p->has_mrl = (slot_capabilities & PCIE_SLOTCAP_MRL_SENSOR) ?
- B_TRUE : B_FALSE;
-
- /*
- * PCI-E version 1.1 defines EMI Lock Present bit
- * in Slot Capabilities register. Check for it.
- */
- ctrl_p->has_emi_lock = (slot_capabilities &
- PCIE_SLOTCAP_EMI_LOCK_PRESENT) ? B_TRUE : B_FALSE;
-
- link_capabilities = pciehpc_reg_get32(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_LINKCAP);
- ctrl_p->dll_active_rep = (link_capabilities &
- PCIE_LINKCAP_DLL_ACTIVE_REP_CAPABLE) ? B_TRUE : B_FALSE;
- if (ctrl_p->dll_active_rep)
- cv_init(&ctrl_p->slot.dll_active_cv, NULL, CV_DRIVER, NULL);
-
- /* initialize synchronization conditional variable */
- cv_init(&ctrl_p->slot.cmd_comp_cv, NULL, CV_DRIVER, NULL);
- ctrl_p->slot.command_pending = B_FALSE;
-
- /* setup thread for handling ATTN button events */
- if (ctrl_p->has_attn) {
- PCIEHPC_DEBUG3((CE_NOTE,
- "pciehpc_slotinfo_init: setting up ATTN button event "
- "handler thread for slot %d\n", ctrl_p->slot.slotNum));
- cv_init(&ctrl_p->slot.attn_btn_cv, NULL, CV_DRIVER, NULL);
- ctrl_p->slot.attn_btn_pending = B_FALSE;
- ctrl_p->slot.attn_btn_threadp = thread_create(NULL, 0,
- pciehpc_attn_btn_handler,
- (void *)ctrl_p, 0, &p0, TS_RUN, minclsyspri);
- ctrl_p->slot.attn_btn_thread_exit = B_FALSE;
- }
-
- /* get current slot state from the hw */
- pciehpc_get_slot_state(ctrl_p);
-
- return (DDI_SUCCESS);
-}
-
-/*ARGSUSED*/
-int
-pciehpc_slotinfo_uninit(pciehpc_t *ctrl_p)
-{
- cv_destroy(&ctrl_p->slot.cmd_comp_cv);
-
- if (ctrl_p->slot.attn_btn_threadp != NULL) {
- mutex_enter(&ctrl_p->pciehpc_mutex);
- ctrl_p->slot.attn_btn_thread_exit = B_TRUE;
- cv_signal(&ctrl_p->slot.attn_btn_cv);
- PCIEHPC_DEBUG3((CE_NOTE,
- "pciehpc_slotinfo_uninit: waiting for ATTN thread exit\n"));
- cv_wait(&ctrl_p->slot.attn_btn_cv, &ctrl_p->pciehpc_mutex);
- PCIEHPC_DEBUG3((CE_NOTE,
- "pciehpc_slotinfo_uninit: ATTN thread exit\n"));
- cv_destroy(&ctrl_p->slot.attn_btn_cv);
- ctrl_p->slot.attn_btn_threadp = NULL;
- mutex_exit(&ctrl_p->pciehpc_mutex);
- }
-
- if (ctrl_p->dll_active_rep)
- cv_destroy(&ctrl_p->slot.dll_active_cv);
-
- return (DDI_SUCCESS);
-}
-
-/*
- * Get the current state of the slot from the hw.
- */
-void
-pciehpc_get_slot_state(pciehpc_t *ctrl_p)
-{
- pciehpc_slot_t *p = &ctrl_p->slot;
- uint16_t control, status;
-
- /* read the Slot Control Register */
- control = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL);
-
- p->fault_led_state = HPC_LED_OFF; /* no fault led */
- p->active_led_state = HPC_LED_OFF; /* no active led */
-
- /* read the current Slot Status Register */
- status = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTSTS);
-
- /* get POWER led state */
- p->power_led_state =
- pciehpc_led_state_to_hpc(pcie_slotctl_pwr_indicator_get(control));
-
- /* get ATTN led state */
- p->attn_led_state =
- pciehpc_led_state_to_hpc(pcie_slotctl_attn_indicator_get(control));
-
- if (!(status & PCIE_SLOTSTS_PRESENCE_DETECTED))
- /* no device present; slot is empty */
- p->slot_state = HPC_SLOT_EMPTY;
-
- else if (!(control & PCIE_SLOTCTL_PWR_CONTROL))
- /* device is present and powered up */
- p->slot_state = HPC_SLOT_CONNECTED;
- else
- /* device is present and powered down */
- p->slot_state = HPC_SLOT_DISCONNECTED;
-}
-
-
-/*
- * pciehpc_regs_setup()
- *
- * Setup PCI-E config registers for DDI access functions.
- *
- * Note: This is same as pci_config_setup() except that this may be
- * used to map specific reg set with an offset in the case of host
- * PCI-E bridges.
- */
-int
-pciehpc_regs_setup(dev_info_t *dip, uint_t rnum, offset_t off,
- caddr_t *addrp, ddi_acc_handle_t *handle)
-{
- ddi_device_acc_attr_t attr;
-
- attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
- attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
- attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
-
- /* Check for fault management capabilities */
- if (DDI_FM_ACC_ERR_CAP(ddi_fm_capable(dip)))
- attr.devacc_attr_access = DDI_FLAGERR_ACC;
-
- return (ddi_regs_map_setup(dip, rnum, addrp, off, 0, &attr, handle));
-}
-
-/*
- * pciehpc_regs_teardown()
- *
- * Unmap config register set.
- *
- * Note: This is same as pci_config_teardown() function.
- */
-void
-pciehpc_regs_teardown(ddi_acc_handle_t *handle)
-{
- ddi_regs_map_free(handle);
-}
-
-/*
- * Find the soft state structure for the HPC associated with the dip.
- */
-static pciehpc_t *
-pciehpc_get_soft_state(dev_info_t *dip)
-{
- pciehpc_t *ctrl_p;
-
- mutex_enter(&pciehpc_list_mutex);
-
- ctrl_p = pciehpc_head;
-
- while (ctrl_p) {
- if (ctrl_p->dip == dip) {
- mutex_exit(&pciehpc_list_mutex);
- return (ctrl_p);
- }
- ctrl_p = ctrl_p->nextp;
- }
-
- mutex_exit(&pciehpc_list_mutex);
-
- return (NULL);
-}
-
-/*
- * Allocate a soft state structure for the HPC associated with this dip.
- */
-static pciehpc_t *
-pciehpc_create_soft_state(dev_info_t *dip)
-{
- pciehpc_t *ctrl_p;
-
- ctrl_p = kmem_zalloc(sizeof (pciehpc_t), KM_SLEEP);
-
- ctrl_p->dip = dip;
-
- mutex_enter(&pciehpc_list_mutex);
- ctrl_p->nextp = pciehpc_head;
- pciehpc_head = ctrl_p;
- ctrl_p->soft_state = PCIEHPC_SOFT_STATE_UNINITIALIZED;
- mutex_exit(&pciehpc_list_mutex);
-
- return (ctrl_p);
-}
-
-/*
- * Remove the HPC soft state structure from the linked list.
- */
-static void
-pciehpc_destroy_soft_state(dev_info_t *dip)
-{
- pciehpc_t **pp;
- pciehpc_t *p;
-
- mutex_enter(&pciehpc_list_mutex);
- pp = &pciehpc_head;
- while ((p = *pp) != NULL) {
- if (p->dip == dip) {
- *pp = p->nextp;
- kmem_free(p, sizeof (pciehpc_t));
- break;
- }
- pp = &(p->nextp);
- }
- mutex_exit(&pciehpc_list_mutex);
-}
-
-/*
- * convert LED state from PCIE HPC definition to hpc_led_state_t
- * definition.
- */
-hpc_led_state_t
-pciehpc_led_state_to_hpc(uint16_t state)
-{
- switch (state) {
- case PCIE_SLOTCTL_INDICATOR_STATE_ON:
- return (HPC_LED_ON);
- case PCIE_SLOTCTL_INDICATOR_STATE_BLINK:
- return (HPC_LED_BLINK);
- case PCIE_SLOTCTL_INDICATOR_STATE_OFF:
- default:
- return (HPC_LED_OFF);
- }
-}
-
-/*
- * convert LED state from hpc_led_state_t definition to PCIE HPC
- * definition.
- */
-uint16_t
-pciehpc_led_state_to_pciehpc(hpc_led_state_t state)
-{
- switch (state) {
- case HPC_LED_ON:
- return (PCIE_SLOTCTL_INDICATOR_STATE_ON);
- case HPC_LED_BLINK:
- return (PCIE_SLOTCTL_INDICATOR_STATE_BLINK);
- case HPC_LED_OFF:
- default:
- return (PCIE_SLOTCTL_INDICATOR_STATE_OFF);
- }
-}
-
-/*
- * Initialize HPC hardware, install interrupt handler, etc. It doesn't
- * enable hot plug interrupts.
- *
- * (Note: It is called only from pciehpc_init().)
- */
-int
-pciehpc_hpc_init(pciehpc_t *ctrl_p)
-{
- uint16_t reg;
-
- /* read the Slot Control Register */
- reg = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL);
-
- /* disable all interrupts */
- reg &= ~(SLOTCTL_SUPPORTED_INTRS_MASK);
- pciehpc_reg_put16(ctrl_p, ctrl_p->pcie_caps_reg_offset +
- PCIE_SLOTCTL, reg);
-
- /* clear any interrupt status bits */
- reg = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTSTS);
- pciehpc_reg_put16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTSTS, reg);
-
- /* initialize the interrupt mutex */
- mutex_init(&ctrl_p->pciehpc_mutex, NULL, MUTEX_DRIVER,
- (void *)PCIEHPC_INTR_PRI);
-
- return (DDI_SUCCESS);
-}
-
-/*
- * Uninitialize HPC hardware, uninstall interrupt handler, etc.
- *
- * (Note: It is called only from pciehpc_uninit().)
- */
-int
-pciehpc_hpc_uninit(pciehpc_t *ctrl_p)
-{
- /* disable interrupts */
- (void) pciehpc_disable_intr(ctrl_p);
-
- /* destroy the mutex */
- mutex_destroy(&ctrl_p->pciehpc_mutex);
-
- return (DDI_SUCCESS);
-}
-
-/*
- * Disable hot plug interrupts.
- * Note: this is only for Native hot plug mode.
- */
-int
-pciehpc_disable_intr(pciehpc_t *ctrl_p)
-{
- uint16_t reg;
-
- /* read the Slot Control Register */
- reg = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL);
-
- /* disable all interrupts */
- reg &= ~(SLOTCTL_SUPPORTED_INTRS_MASK);
- pciehpc_reg_put16(ctrl_p, ctrl_p->pcie_caps_reg_offset +
- PCIE_SLOTCTL, reg);
-
- /* clear any interrupt status bits */
- reg = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTSTS);
- pciehpc_reg_put16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTSTS, reg);
-
- return (DDI_SUCCESS);
-}
-
-/*
- * Enable hot plug interrupts.
- * Note: this is only for Native hot plug mode.
- */
-int
-pciehpc_enable_intr(pciehpc_t *ctrl_p)
-{
- uint16_t reg;
-
- /* clear any interrupt status bits */
- reg = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTSTS);
- pciehpc_reg_put16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTSTS, reg);
-
- /* read the Slot Control Register */
- reg = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL);
-
- /*
- * enable interrupts: power fault detection interrupt is enabled
- * only when the slot is 'connected', i.e. power is ON
- */
- if (ctrl_p->slot.slot_state == HPC_SLOT_CONNECTED)
- pciehpc_reg_put16(ctrl_p, ctrl_p->pcie_caps_reg_offset +
- PCIE_SLOTCTL, reg | SLOTCTL_SUPPORTED_INTRS_MASK);
- else
- pciehpc_reg_put16(ctrl_p, ctrl_p->pcie_caps_reg_offset +
- PCIE_SLOTCTL, reg | (SLOTCTL_SUPPORTED_INTRS_MASK &
- ~PCIE_SLOTCTL_PWR_FAULT_EN));
-
- return (DDI_SUCCESS);
-}
-
-/*
- * Register the PCI-E hot plug slot with HPS framework.
- */
-int
-pciehpc_register_slot(pciehpc_t *ctrl_p)
-{
- char nexus_path[MAXNAMELEN];
- pciehpc_slot_t *p = &ctrl_p->slot;
-
- /* get nexus path name */
- (void) ddi_pathname(ctrl_p->dip, nexus_path);
-
- /* register the slot with HPS framework */
- if (hpc_slot_register(ctrl_p->dip, nexus_path,
- &p->slot_info, &p->slot_handle,
- &p->slot_ops, (caddr_t)ctrl_p, 0) != 0) {
- PCIEHPC_DEBUG((CE_WARN,
- "pciehpc_register_slot() failed to register slot %d\n",
- p->slotNum));
- return (DDI_FAILURE);
- }
-
- PCIEHPC_DEBUG3((CE_NOTE,
- "pciehpc_register_slot(): registered slot %d\n", p->slotNum));
- return (DDI_SUCCESS);
-}
-
-/*
- * Unregister the PCI-E hot plug slot from the HPS framework.
- */
-int
-pciehpc_unregister_slot(pciehpc_t *ctrl_p)
-{
- pciehpc_slot_t *p = &ctrl_p->slot;
-
- if (hpc_slot_unregister(&p->slot_handle) != 0) {
- PCIEHPC_DEBUG((CE_WARN,
- "pciehpc_unregister_slot() failed to unregister slot %d\n",
- p->slotNum));
- return (DDI_FAILURE);
- }
- PCIEHPC_DEBUG3((CE_NOTE,
- "pciehpc_unregister_slot(): unregistered slot %d\n", p->slotNum));
- return (DDI_SUCCESS);
-}
-
-/*
- * pciehpc_intr()
- *
- * Interrupt handler for PCI-E Hot plug controller interrupts.
- *
- * Note: This is only for native mode hot plug. This is called
- * by the nexus driver at interrupt context. Interrupt Service Routine
- * registration is done by the nexus driver for both hot plug and
- * non-hot plug interrupts. This function is called from the ISR
- * of the nexus driver to handle hot-plug interrupts.
- */
-int
-pciehpc_intr(dev_info_t *dip)
-{
- pciehpc_t *ctrl_p;
- uint16_t status, control;
-
- /* get the soft state structure for this dip */
- if ((ctrl_p = pciehpc_get_soft_state(dip)) == NULL)
- return (DDI_INTR_UNCLAIMED);
-
- mutex_enter(&ctrl_p->pciehpc_mutex);
-
- /* make sure the controller soft state is initialized */
- if (ctrl_p->soft_state & PCIEHPC_SOFT_STATE_UNINITIALIZED) {
- mutex_exit(&ctrl_p->pciehpc_mutex);
- return (DDI_INTR_UNCLAIMED);
- }
-
- /* if it is not NATIVE hot plug mode then return */
- if (ctrl_p->hp_mode != PCIEHPC_NATIVE_HP_MODE) {
- mutex_exit(&ctrl_p->pciehpc_mutex);
- return (DDI_INTR_UNCLAIMED);
- }
-
- /* read the current slot status register */
- status = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTSTS);
-
- /* check if there are any hot plug interrupts occurred */
- if (!(status & SLOT_STATUS_EVENTS)) {
- /* no hot plug events occurred */
- mutex_exit(&ctrl_p->pciehpc_mutex);
- return (DDI_INTR_UNCLAIMED);
- }
-
- /* clear the interrupt status bits */
- pciehpc_reg_put16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTSTS, status);
-
- /* check for CMD COMPLETE interrupt */
- if (status & PCIE_SLOTSTS_COMMAND_COMPLETED) {
- PCIEHPC_DEBUG3((CE_NOTE,
- "pciehpc_intr(): CMD COMPLETED interrupt received\n"));
- /* wake up any one waiting for Command Completion event */
- cv_signal(&ctrl_p->slot.cmd_comp_cv);
- }
-
- /* check for ATTN button interrupt */
- if (status & PCIE_SLOTSTS_ATTN_BTN_PRESSED) {
- PCIEHPC_DEBUG3((CE_NOTE,
- "pciehpc_intr(): ATTN BUTTON interrupt received\n"));
- /* if ATTN button event is still pending then cancel it */
- if (ctrl_p->slot.attn_btn_pending == B_TRUE)
- ctrl_p->slot.attn_btn_pending = B_FALSE;
- else
- ctrl_p->slot.attn_btn_pending = B_TRUE;
- /* wake up the ATTN event handler */
- cv_signal(&ctrl_p->slot.attn_btn_cv);
- }
-
- /* check for power fault interrupt */
- if (status & PCIE_SLOTSTS_PWR_FAULT_DETECTED) {
- PCIEHPC_DEBUG3((CE_NOTE,
- "pciehpc_intr(): POWER FAULT interrupt received"
- " on slot %d\n", ctrl_p->slot.slotNum));
- control = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL);
- if (control & PCIE_SLOTCTL_PWR_FAULT_EN) {
- /* disable power fault detction interrupt */
- pciehpc_reg_put16(ctrl_p, ctrl_p->pcie_caps_reg_offset +
- PCIE_SLOTCTL, control & ~PCIE_SLOTCTL_PWR_FAULT_EN);
-
- /* send the event to HPS framework */
- (void) hpc_slot_event_notify(ctrl_p->slot.slot_handle,
- HPC_EVENT_SLOT_POWER_FAULT, HPC_EVENT_NORMAL);
- }
- }
-
- /* check for MRL SENSOR CHANGED interrupt */
- if (status & PCIE_SLOTSTS_MRL_SENSOR_CHANGED) {
- PCIEHPC_DEBUG3((CE_NOTE,
- "pciehpc_intr(): MRL SENSOR CHANGED interrupt received"
- " on slot %d\n", ctrl_p->slot.slotNum));
- /* For now (phase-I), no action is taken on this event */
- }
-
- /* check for PRESENCE CHANGED interrupt */
- if (status & PCIE_SLOTSTS_PRESENCE_CHANGED) {
- PCIEHPC_DEBUG3((CE_NOTE,
- "pciehpc_intr(): PRESENCE CHANGED interrupt received"
- " on slot %d\n", ctrl_p->slot.slotNum));
-
- if (status & PCIE_SLOTSTS_PRESENCE_DETECTED) {
- /* card is inserted into the slot */
-
- /* send the event to HPS framework */
- (void) hpc_slot_event_notify(ctrl_p->slot.slot_handle,
- HPC_EVENT_SLOT_INSERTION, HPC_EVENT_NORMAL);
- } else {
- /* card is removed from the slot */
-
- /* make sure to disable power fault detction interrupt */
- control = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL);
- if (control & PCIE_SLOTCTL_PWR_FAULT_EN)
- pciehpc_reg_put16(ctrl_p, ctrl_p->pcie_caps_reg_offset +
- PCIE_SLOTCTL, control & ~PCIE_SLOTCTL_PWR_FAULT_EN);
-
- /* send the event to HPS framework */
- (void) hpc_slot_event_notify(ctrl_p->slot.slot_handle,
- HPC_EVENT_SLOT_REMOVAL, HPC_EVENT_NORMAL);
- }
- }
-
- /* check for DLL state changed interrupt */
- if (ctrl_p->dll_active_rep &&
- (status & PCIE_SLOTSTS_DLL_STATE_CHANGED)) {
- PCIEHPC_DEBUG3((CE_NOTE,
- "pciehpc_intr(): DLL STATE CHANGED interrupt received"
- " on slot %d\n", ctrl_p->slot.slotNum));
-
- cv_signal(&ctrl_p->slot.dll_active_cv);
- }
-
- mutex_exit(&ctrl_p->pciehpc_mutex);
-
- return (DDI_INTR_CLAIMED);
-}
-
-#ifdef DEBUG
-/*
- * Dump PCI-E Hot Plug registers.
- */
-static void
-pciehpc_dump_hpregs(pciehpc_t *ctrl_p)
-{
- uint16_t control;
- uint32_t capabilities;
-
- capabilities = pciehpc_reg_get32(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCAP);
-
- control = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL);
-
- cmn_err(CE_NOTE, "pciehpc_dump_hpregs: Found PCI-E hot plug slot %d\n",
- ctrl_p->slot.slotNum);
- cmn_err(CE_NOTE, "Attention Button Present = %s",
- capabilities & PCIE_SLOTCAP_ATTN_BUTTON ? "Yes":"No");
-
- cmn_err(CE_NOTE, "Power controller Present = %s",
- capabilities & PCIE_SLOTCAP_POWER_CONTROLLER ? "Yes":"No");
-
- cmn_err(CE_NOTE, "MRL Sensor Present = %s",
- capabilities & PCIE_SLOTCAP_MRL_SENSOR ? "Yes":"No");
-
- cmn_err(CE_NOTE, "Attn Indicator Present = %s",
- capabilities & PCIE_SLOTCAP_ATTN_INDICATOR ? "Yes":"No");
-
- cmn_err(CE_NOTE, "Power Indicator Present = %s",
- capabilities & PCIE_SLOTCAP_PWR_INDICATOR ? "Yes":"No");
-
- cmn_err(CE_NOTE, "HotPlug Surprise = %s",
- capabilities & PCIE_SLOTCAP_HP_SURPRISE ? "Yes":"No");
-
- cmn_err(CE_NOTE, "HotPlug Capable = %s",
- capabilities & PCIE_SLOTCAP_HP_CAPABLE ? "Yes":"No");
-
- cmn_err(CE_NOTE, "Physical Slot Number = %d",
- PCIE_SLOTCAP_PHY_SLOT_NUM(capabilities));
-
- cmn_err(CE_NOTE, "Attn Button interrupt Enabled = %s",
- control & PCIE_SLOTCTL_ATTN_BTN_EN ? "Yes":"No");
-
- cmn_err(CE_NOTE, "Power Fault interrupt Enabled = %s",
- control & PCIE_SLOTCTL_PWR_FAULT_EN ? "Yes":"No");
-
- cmn_err(CE_NOTE, "MRL Sensor INTR Enabled = %s",
- control & PCIE_SLOTCTL_MRL_SENSOR_EN ? "Yes":"No");
-
- cmn_err(CE_NOTE, "Presence interrupt Enabled = %s",
- control & PCIE_SLOTCTL_PRESENCE_CHANGE_EN ? "Yes":"No");
-
- cmn_err(CE_NOTE, "Cmd Complete interrupt Enabled = %s",
- control & PCIE_SLOTCTL_CMD_INTR_EN ? "Yes":"No");
-
- cmn_err(CE_NOTE, "HotPlug interrupt Enabled = %s",
- control & PCIE_SLOTCTL_HP_INTR_EN ? "Yes":"No");
-
- cmn_err(CE_NOTE, "Power Indicator LED = %s", pciehpc_led_state_text(
- pciehpc_led_state_to_hpc(pcie_slotctl_pwr_indicator_get(control))));
-
- cmn_err(CE_NOTE, "Attn Indicator LED = %s",
- pciehpc_led_state_text(pciehpc_led_state_to_hpc(
- pcie_slotctl_attn_indicator_get(control))));
-}
-
-static char *
-pciehpc_led_state_text(hpc_led_state_t state)
-{
- switch (state) {
- case HPC_LED_ON:
- return ("on");
- case HPC_LED_OFF:
- return ("off");
- case HPC_LED_BLINK:
- default:
- return ("blink");
- }
-}
-#endif /* DEBUG */
-
-/*
- * pciehpc_slot_connect()
- *
- * Connect power to the PCI-E slot.
- *
- * Returns: HPC_SUCCESS if the slot is powered up and enabled.
- * HPC_ERR_FAILED if the slot can't be enabled.
- *
- * (Note: This function is called by HPS framework at kernel context only.)
- */
-/*ARGSUSED*/
-int
-pciehpc_slot_connect(caddr_t ops_arg, hpc_slot_t slot_hdl,
- void *data, uint_t flags)
-{
- uint16_t status, control;
-
- pciehpc_t *ctrl_p = (pciehpc_t *)ops_arg;
-
- ASSERT(slot_hdl == ctrl_p->slot.slot_handle);
-
- mutex_enter(&ctrl_p->pciehpc_mutex);
-
- /* get the current state of the slot */
- pciehpc_get_slot_state(ctrl_p);
-
- /* check if the slot is already in the 'connected' state */
- if (ctrl_p->slot.slot_state == HPC_SLOT_CONNECTED) {
- /* slot is already in the 'connected' state */
- PCIEHPC_DEBUG3((CE_NOTE,
- "pciehpc_slot_connect() slot %d already connected\n",
- ctrl_p->slot.slotNum));
- mutex_exit(&ctrl_p->pciehpc_mutex);
- return (HPC_SUCCESS);
- }
-
- /* read the Slot Status Register */
- status = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTSTS);
-
- /* make sure the MRL switch is closed if present */
- if ((ctrl_p->has_mrl) && (status & PCIE_SLOTSTS_MRL_SENSOR_OPEN)) {
- /* MRL switch is open */
- cmn_err(CE_WARN, "MRL switch is open on slot %d\n",
- ctrl_p->slot.slotNum);
- goto cleanup;
- }
-
- /* make sure the slot has a device present */
- if (!(status & PCIE_SLOTSTS_PRESENCE_DETECTED)) {
- /* slot is empty */
- PCIEHPC_DEBUG((CE_NOTE,
- "slot %d is empty\n", ctrl_p->slot.slotNum));
- goto cleanup;
- }
-
- /* get the current state of Slot Control Register */
- control = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL);
-
- /* check if the slot's power state is ON */
- if (!(control & PCIE_SLOTCTL_PWR_CONTROL)) {
- /* slot is already powered up */
- PCIEHPC_DEBUG3((CE_NOTE,
- "pciehpc_slot_connect() slot %d already connected\n",
- ctrl_p->slot.slotNum));
- ctrl_p->slot.slot_state = HPC_SLOT_CONNECTED;
- mutex_exit(&ctrl_p->pciehpc_mutex);
- return (HPC_SUCCESS);
- }
-
- /*
- * Enable power to the slot involves:
- * 1. Set power LED to blink and ATTN led to OFF.
- * 2. Set power control ON in Slot Control Reigster and
- * wait for Command Completed Interrupt or 1 sec timeout.
- * 3. If Data Link Layer State Changed events are supported
- * then wait for the event to indicate Data Layer Link
- * is active. The time out value for this event is 1 second.
- * This is specified in PCI-E version 1.1.
- * 4. Set power LED to be ON.
- */
-
- /* 1. set power LED to blink & ATTN led to OFF */
- pciehpc_set_led_state(ctrl_p, HPC_POWER_LED, HPC_LED_BLINK);
- pciehpc_set_led_state(ctrl_p, HPC_ATTN_LED, HPC_LED_OFF);
-
- /* 2. set power control to ON */
- control = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL);
- control &= ~PCIE_SLOTCTL_PWR_CONTROL;
- pciehpc_issue_hpc_command(ctrl_p, control);
-
- /* 3. wait for DLL State Change event, if it's supported */
- if (ctrl_p->dll_active_rep) {
- status = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_LINKSTS);
-
- if (!(status & PCIE_LINKSTS_DLL_LINK_ACTIVE)) {
- /* wait 1 sec for the DLL State Changed event */
- (void) cv_timedwait(&ctrl_p->slot.dll_active_cv,
- &ctrl_p->pciehpc_mutex,
- ddi_get_lbolt() +
- SEC_TO_TICK(PCIEHPC_DLL_STATE_CHANGE_TIMEOUT));
-
- /* check Link status */
- status = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset +
- PCIE_LINKSTS);
- if (!(status & PCIE_LINKSTS_DLL_LINK_ACTIVE))
- goto cleanup2;
- }
- }
-
- /* wait 1 sec for link to come up */
- delay(drv_usectohz(1000000));
-
- /* check power is really turned ON */
- control = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL);
- if (control & PCIE_SLOTCTL_PWR_CONTROL) {
- PCIEHPC_DEBUG((CE_NOTE,
- "slot %d fails to turn on power on connect\n",
- ctrl_p->slot.slotNum));
-
- goto cleanup1;
- }
-
- /* clear power fault status */
- status = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTSTS);
- status |= PCIE_SLOTSTS_PWR_FAULT_DETECTED;
- pciehpc_reg_put16(ctrl_p, ctrl_p->pcie_caps_reg_offset + PCIE_SLOTSTS,
- status);
-
- /* enable power fault detection interrupt */
- control |= PCIE_SLOTCTL_PWR_FAULT_EN;
- pciehpc_issue_hpc_command(ctrl_p, control);
-
- /* 4. Set power LED to be ON */
- pciehpc_set_led_state(ctrl_p, HPC_POWER_LED, HPC_LED_ON);
-
- /* if EMI is present, turn it ON */
- if (ctrl_p->has_emi_lock) {
- status = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTSTS);
-
- if (!(status & PCIE_SLOTSTS_EMI_LOCK_SET)) {
- control = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL);
- control |= PCIE_SLOTCTL_EMI_LOCK_CONTROL;
- pciehpc_issue_hpc_command(ctrl_p, control);
-
- /* wait 1 sec after toggling the state of EMI lock */
- delay(drv_usectohz(1000000));
- }
- }
-
- ctrl_p->slot.slot_state = HPC_SLOT_CONNECTED;
- mutex_exit(&ctrl_p->pciehpc_mutex);
- return (HPC_SUCCESS);
-
-cleanup2:
- control = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL);
- /* if power is ON, set power control to OFF */
- if (!(control & PCIE_SLOTCTL_PWR_CONTROL)) {
- control |= PCIE_SLOTCTL_PWR_CONTROL;
- pciehpc_issue_hpc_command(ctrl_p, control);
- }
-
-cleanup1:
- /* set power led to OFF */
- pciehpc_set_led_state(ctrl_p, HPC_POWER_LED, HPC_LED_OFF);
-
-cleanup:
- mutex_exit(&ctrl_p->pciehpc_mutex);
- return (HPC_ERR_FAILED);
-}
-
-/*
- * pciehpc_slot_disconnect()
- *
- * Disconnect power to the slot.
- *
- * Returns: HPC_SUCCESS if the slot is powered up and enabled.
- * HPC_ERR_FAILED if the slot can't be enabled.
- *
- * (Note: This function is called by HPS framework at kernel context only.)
- */
-/*ARGSUSED*/
-int
-pciehpc_slot_disconnect(caddr_t ops_arg, hpc_slot_t slot_hdl,
- void *data, uint_t flags)
-{
- uint16_t status;
- uint16_t control;
-
- pciehpc_t *ctrl_p = (pciehpc_t *)ops_arg;
-
- ASSERT(slot_hdl == ctrl_p->slot.slot_handle);
-
- mutex_enter(&ctrl_p->pciehpc_mutex);
-
- /* get the current state of the slot */
- pciehpc_get_slot_state(ctrl_p);
-
- /* check if the slot is already in the 'disconnected' state */
- if (ctrl_p->slot.slot_state == HPC_SLOT_DISCONNECTED) {
- /* slot is in the 'disconnected' state */
- PCIEHPC_DEBUG3((CE_NOTE,
- "pciehpc_slot_disconnect(): slot %d already disconnected\n",
- ctrl_p->slot.slotNum));
- ASSERT(ctrl_p->slot.power_led_state == HPC_LED_OFF);
- mutex_exit(&ctrl_p->pciehpc_mutex);
- return (HPC_SUCCESS);
- }
-
- /* read the Slot Status Register */
- status = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTSTS);
-
- /* make sure the slot has a device present */
- if (!(status & PCIE_SLOTSTS_PRESENCE_DETECTED)) {
- /* slot is empty */
- PCIEHPC_DEBUG((CE_NOTE,
- "pciehpc_slot_disconnect(): slot %d is empty\n",
- ctrl_p->slot.slotNum));
- goto cleanup;
- }
-
- /*
- * Disable power to the slot involves:
- * 1. Set power LED to blink.
- * 2. Set power control OFF in Slot Control Reigster and
- * wait for Command Completed Interrupt or 1 sec timeout.
- * 3. Set POWER led and ATTN led to be OFF.
- */
-
- /* 1. set power LED to blink */
- pciehpc_set_led_state(ctrl_p, HPC_POWER_LED, HPC_LED_BLINK);
-
- /* disable power fault detection interrupt */
- control = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL);
- control &= ~PCIE_SLOTCTL_PWR_FAULT_EN;
- pciehpc_issue_hpc_command(ctrl_p, control);
-
- /* 2. set power control to OFF */
- control = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL);
- control |= PCIE_SLOTCTL_PWR_CONTROL;
- pciehpc_issue_hpc_command(ctrl_p, control);
-
-#ifdef DEBUG
- /* check for power control bit to be OFF */
- control = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL);
- ASSERT(control & PCIE_SLOTCTL_PWR_CONTROL);
-#endif
-
- /* 3. Set power LED to be OFF */
- pciehpc_set_led_state(ctrl_p, HPC_POWER_LED, HPC_LED_OFF);
- pciehpc_set_led_state(ctrl_p, HPC_ATTN_LED, HPC_LED_OFF);
-
- /* if EMI is present, turn it OFF */
- if (ctrl_p->has_emi_lock) {
- status = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTSTS);
-
- if (status & PCIE_SLOTSTS_EMI_LOCK_SET) {
- control = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL);
- control |= PCIE_SLOTCTL_EMI_LOCK_CONTROL;
- pciehpc_issue_hpc_command(ctrl_p, control);
-
- /* wait 1 sec after toggling the state of EMI lock */
- delay(drv_usectohz(1000000));
- }
- }
-
- ctrl_p->slot.slot_state = HPC_SLOT_DISCONNECTED;
- mutex_exit(&ctrl_p->pciehpc_mutex);
- return (HPC_SUCCESS);
-
-cleanup:
- mutex_exit(&ctrl_p->pciehpc_mutex);
- return (HPC_ERR_FAILED);
-}
-
-/*ARGSUSED*/
-int
-pciehpc_slot_control(caddr_t ops_arg, hpc_slot_t slot_hdl,
- int request, caddr_t arg)
-{
- pciehpc_t *ctrl_p;
- hpc_led_info_t *led_info;
- int ret = HPC_SUCCESS;
-
- ctrl_p = (pciehpc_t *)ops_arg;
-
- ASSERT(ctrl_p != NULL);
-
- mutex_enter(&ctrl_p->pciehpc_mutex);
-
- /* get the current slot state */
- pciehpc_get_slot_state(ctrl_p);
-
- switch (request) {
-
- case HPC_CTRL_GET_SLOT_STATE:
- *(hpc_slot_state_t *)arg = ctrl_p->slot.slot_state;
- break;
-
- case HPC_CTRL_GET_BOARD_TYPE:
- if (ctrl_p->slot.slot_state == HPC_SLOT_EMPTY)
- *(hpc_board_type_t *)arg = HPC_BOARD_UNKNOWN;
- else
- *(hpc_board_type_t *)arg = HPC_BOARD_PCI_HOTPLUG;
- break;
-
- case HPC_CTRL_GET_LED_STATE:
- led_info = (hpc_led_info_t *)arg;
- switch (led_info->led) {
- case HPC_ATTN_LED:
- led_info->state = ctrl_p->slot.attn_led_state;
- break;
- case HPC_POWER_LED:
- led_info->state = ctrl_p->slot.power_led_state;
- break;
- case HPC_FAULT_LED:
- case HPC_ACTIVE_LED:
- led_info->state = HPC_LED_OFF;
- break;
- default:
- PCIEHPC_DEBUG((CE_WARN, "pciehpc_slot_control:"
- " unknown led state\n"));
- ret = HPC_ERR_NOTSUPPORTED;
- break;
- }
- break;
- case HPC_CTRL_SET_LED_STATE:
- led_info = (hpc_led_info_t *)arg;
- switch (led_info->led) {
- case HPC_ATTN_LED:
- pciehpc_set_led_state(ctrl_p, led_info->led,
- led_info->state);
- break;
- case HPC_POWER_LED:
- PCIEHPC_DEBUG((CE_WARN, "pciehpc_slot_control: power"
- " LED control is not allowed on slot #%d\n",
- ctrl_p->slot.slotNum));
- ret = HPC_ERR_NOTSUPPORTED;
- break;
- case HPC_FAULT_LED:
- case HPC_ACTIVE_LED:
- break;
- default:
- PCIEHPC_DEBUG((CE_WARN, "pciehpc_slot_control:"
- " unknown led type %d\n", led_info->led));
- ret = HPC_ERR_NOTSUPPORTED;
- break;
- }
- break;
- case HPC_CTRL_DEV_CONFIG_FAILURE:
- /* turn the ATTN led ON for configure failure */
- pciehpc_set_led_state(ctrl_p, HPC_ATTN_LED, HPC_LED_ON);
- /* if power to the slot is still on then set Power led to ON */
- if (ctrl_p->slot.slot_state == HPC_SLOT_CONNECTED)
- pciehpc_set_led_state(ctrl_p, HPC_POWER_LED, HPC_LED_ON);
- break;
- case HPC_CTRL_DEV_UNCONFIG_FAILURE:
- /* if power to the slot is still on then set Power led to ON */
- if (ctrl_p->slot.slot_state == HPC_SLOT_CONNECTED)
- pciehpc_set_led_state(ctrl_p, HPC_POWER_LED, HPC_LED_ON);
- pciehpc_enable_errors(ctrl_p);
- break;
- case HPC_CTRL_ENABLE_AUTOCFG:
- case HPC_CTRL_DISABLE_AUTOCFG:
- /* no action is needed here */
- break;
-
- case HPC_CTRL_DISABLE_SLOT:
- case HPC_CTRL_ENABLE_SLOT:
- /* no action is needed here */
- break;
-
- case HPC_CTRL_DEV_CONFIG_START:
- case HPC_CTRL_DEV_UNCONFIG_START:
- pciehpc_disable_errors(ctrl_p);
- /* no action is needed here */
- break;
- case HPC_CTRL_DEV_CONFIGURED:
- case HPC_CTRL_DEV_UNCONFIGURED:
- /* no action is needed here */
- if (request == HPC_CTRL_DEV_CONFIGURED) {
- pciehpc_enable_errors(ctrl_p);
- }
- break;
- default:
- PCIEHPC_DEBUG((CE_WARN,
- "pciehpc_slot_control: unsupported operation\n"));
- ret = HPC_ERR_NOTSUPPORTED;
- }
-
- mutex_exit(&ctrl_p->pciehpc_mutex);
-
- return (ret);
-}
-
-/*
- * Get the state of an LED.
- */
-hpc_led_state_t
-pciehpc_get_led_state(pciehpc_t *ctrl_p, hpc_led_t led)
-{
- uint16_t control;
- uint16_t state;
-
- /* get the current state of Slot Control register */
- control = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL);
-
- switch (led) {
- case HPC_POWER_LED:
- state = pcie_slotctl_pwr_indicator_get(control);
- break;
- case HPC_ATTN_LED:
- state = pcie_slotctl_attn_indicator_get(control);
- break;
- default:
- PCIEHPC_DEBUG((CE_WARN,
- "pciehpc_get_led_state() invalid LED %d\n", led));
- return (HPC_LED_OFF);
- }
-
- switch (state) {
- case PCIE_SLOTCTL_INDICATOR_STATE_ON:
- return (HPC_LED_ON);
-
- case PCIE_SLOTCTL_INDICATOR_STATE_BLINK:
- return (HPC_LED_BLINK);
-
- case PCIE_SLOTCTL_INDICATOR_STATE_OFF:
- default:
- return (HPC_LED_OFF);
- }
-}
-
-/*
- * Set the state of an LED. It updates both hw and sw state.
- */
-void
-pciehpc_set_led_state(pciehpc_t *ctrl_p, hpc_led_t led, hpc_led_state_t state)
-{
- uint16_t control;
-
- /* get the current state of Slot Control register */
- control = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL);
-
- switch (led) {
- case HPC_POWER_LED:
- /* clear led mask */
- control &= ~PCIE_SLOTCTL_PWR_INDICATOR_MASK;
- ctrl_p->slot.power_led_state = state;
- break;
- case HPC_ATTN_LED:
- /* clear led mask */
- control &= ~PCIE_SLOTCTL_ATTN_INDICATOR_MASK;
- ctrl_p->slot.attn_led_state = state;
- break;
- default:
- PCIEHPC_DEBUG((CE_WARN,
- "pciehpc_set_led_state() invalid LED %d\n", led));
- return;
- }
-
- switch (state) {
- case HPC_LED_ON:
- if (led == HPC_POWER_LED)
- control = pcie_slotctl_pwr_indicator_set(control,
- PCIE_SLOTCTL_INDICATOR_STATE_ON);
- else if (led == HPC_ATTN_LED)
- control = pcie_slotctl_attn_indicator_set(control,
- PCIE_SLOTCTL_INDICATOR_STATE_ON);
- break;
- case HPC_LED_OFF:
- if (led == HPC_POWER_LED)
- control = pcie_slotctl_pwr_indicator_set(control,
- PCIE_SLOTCTL_INDICATOR_STATE_OFF);
- else if (led == HPC_ATTN_LED)
- control = pcie_slotctl_attn_indicator_set(control,
- PCIE_SLOTCTL_INDICATOR_STATE_OFF);
- break;
- case HPC_LED_BLINK:
- if (led == HPC_POWER_LED)
- control = pcie_slotctl_pwr_indicator_set(control,
- PCIE_SLOTCTL_INDICATOR_STATE_BLINK);
- else if (led == HPC_ATTN_LED)
- control = pcie_slotctl_attn_indicator_set(control,
- PCIE_SLOTCTL_INDICATOR_STATE_BLINK);
- break;
-
- default:
- PCIEHPC_DEBUG((CE_WARN,
- "pciehpc_set_led_state() invalid LED state %d\n", state));
- return;
- }
-
- /* update the Slot Control Register */
- pciehpc_issue_hpc_command(ctrl_p, control);
-
-#ifdef DEBUG
- /* get the current state of Slot Control register */
- control = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL);
-
- PCIEHPC_DEBUG3((CE_NOTE, "pciehpc_set_led_state: "
- "slot %d power-led %s attn-led %s\n",
- ctrl_p->slot.slotNum,
- pciehpc_led_state_text(
- pciehpc_led_state_to_hpc(
- pcie_slotctl_pwr_indicator_get(control))),
- pciehpc_led_state_text(
- pciehpc_led_state_to_hpc(
- pcie_slotctl_attn_indicator_get(control)))));
-#endif
-}
-
-/*
- * Send a command to the PCI-E Hot Plug Controller.
- *
- * NOTES: The PCI-E spec defines the following semantics for issuing hot plug
- * commands.
- * 1) If Command Complete events/interrupts are supported then software
- * waits for Command Complete event after issuing a command (i.e writing
- * to the Slot Control register). The command completion could take as
- * long as 1 second so software should be prepared to wait for 1 second
- * before issuing another command.
- *
- * 2) If Command Complete events/interrupts are not supported then
- * software could issue multiple Slot Control writes without any delay
- * between writes.
- */
-void
-pciehpc_issue_hpc_command(pciehpc_t *ctrl_p, uint16_t control)
-{
-
- uint16_t status;
- uint32_t slot_cap;
-
- /*
- * PCI-E version 1.1 spec defines No Command Completed
- * Support bit (bit#18) in Slot Capabilities register. If this
- * bit is set then slot doesn't support notification of command
- * completion events.
- */
- slot_cap = pciehpc_reg_get32(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCAP);
- /*
- * If no Command Completion event is supported or it is ACPI
- * hot plug mode then just issue the command and return.
- */
- if ((slot_cap & PCIE_SLOTCAP_NO_CMD_COMP_SUPP) ||
- (ctrl_p->hp_mode == PCIEHPC_ACPI_HP_MODE)) {
- pciehpc_reg_put16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL, control);
- return;
- }
-
- /*
- * **************************************
- * Command Complete events are supported.
- * **************************************
- */
-
- /*
- * If HPC is not yet initialized then just poll for the Command
- * Completion interrupt.
- */
- if (!(ctrl_p->soft_state & PCIEHPC_SOFT_STATE_INITIALIZED)) {
- int retry = PCIEHPC_CMD_WAIT_RETRY;
-
- /* write the command to the HPC */
- pciehpc_reg_put16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL,
- control);
-
- /* poll for status completion */
- while (retry--) {
- /* wait for 10 msec before checking the status */
- delay(drv_usectohz(PCIEHPC_CMD_WAIT_TIME));
-
- status = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTSTS);
-
- if (status & PCIE_SLOTSTS_COMMAND_COMPLETED) {
- /* clear the status bits */
- pciehpc_reg_put16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTSTS,
- status);
- break;
- }
- }
- return;
- }
-
- /* HPC is already initialized */
-
- ASSERT(MUTEX_HELD(&ctrl_p->pciehpc_mutex));
-
- /*
- * If previous command is still pending then wait for its
- * completion. i.e cv_wait()
- */
-
- while (ctrl_p->slot.command_pending == B_TRUE)
- cv_wait(&ctrl_p->slot.cmd_comp_cv, &ctrl_p->pciehpc_mutex);
-
- /*
- * Issue the command and wait for Command Completion or
- * the 1 sec timeout.
- */
- pciehpc_reg_put16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL, control);
-
- ctrl_p->slot.command_pending = B_TRUE;
-
- if (cv_timedwait(&ctrl_p->slot.cmd_comp_cv, &ctrl_p->pciehpc_mutex,
- ddi_get_lbolt() + SEC_TO_TICK(1)) == -1) {
- /* it is a timeout */
- PCIEHPC_DEBUG2((CE_NOTE,
- "pciehpc_issue_hpc_command: Command Complete"
- " interrupt is not received for slot %d\n",
- ctrl_p->slot.slotNum));
-
- /* clear the status info in case interrupts are disabled? */
- status = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTSTS);
-
- if (status & PCIE_SLOTSTS_COMMAND_COMPLETED) {
- /* clear the status bits */
- pciehpc_reg_put16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTSTS,
- status);
- }
- }
-
- ctrl_p->slot.command_pending = B_FALSE;
-
- /* wake up any one waiting for issuing another command to HPC */
- cv_signal(&ctrl_p->slot.cmd_comp_cv);
-}
-
-/*
- * pciehcp_attn_btn_handler()
- *
- * This handles ATTN button pressed event as per the PCI-E 1.1 spec.
- */
-static void
-pciehpc_attn_btn_handler(pciehpc_t *ctrl_p)
-{
- hpc_led_state_t power_led_state;
- callb_cpr_t cprinfo;
-
- PCIEHPC_DEBUG3((CE_NOTE, "pciehpc_attn_btn_handler: thread started\n"));
-
- CALLB_CPR_INIT(&cprinfo, &ctrl_p->pciehpc_mutex, callb_generic_cpr,
- "pciehpc_attn_btn_handler");
-
- mutex_enter(&ctrl_p->pciehpc_mutex);
-
- /* wait for ATTN button event */
- cv_wait(&ctrl_p->slot.attn_btn_cv, &ctrl_p->pciehpc_mutex);
-
- while (ctrl_p->slot.attn_btn_thread_exit == B_FALSE) {
-
- if (ctrl_p->slot.attn_btn_pending == B_TRUE) {
-
- /* get the current state of power LED */
- power_led_state = pciehpc_get_led_state(ctrl_p, HPC_POWER_LED);
-
- /* Blink the Power LED while we wait for 5 seconds */
- pciehpc_set_led_state(ctrl_p, HPC_POWER_LED, HPC_LED_BLINK);
-
- /* wait for 5 seconds before taking any action */
- if (cv_timedwait(&ctrl_p->slot.attn_btn_cv,
- &ctrl_p->pciehpc_mutex,
- ddi_get_lbolt() + SEC_TO_TICK(5)) == -1) {
- /*
- * It is a time out; make sure the ATTN pending flag is
- * still ON before sending the event to HPS framework.
- */
- if (ctrl_p->slot.attn_btn_pending == B_TRUE) {
- /* send the ATTN button event to HPS framework */
- ctrl_p->slot.attn_btn_pending = B_FALSE;
- (void) hpc_slot_event_notify(
- ctrl_p->slot.slot_handle,
- HPC_EVENT_SLOT_ATTN, HPC_EVENT_NORMAL);
- }
- }
- /* restore the power LED state */
- pciehpc_set_led_state(ctrl_p, HPC_POWER_LED, power_led_state);
- continue;
- }
-
- /* wait for another ATTN button event */
- cv_wait(&ctrl_p->slot.attn_btn_cv, &ctrl_p->pciehpc_mutex);
- }
-
- PCIEHPC_DEBUG3((CE_NOTE, "pciehpc_attn_btn_handler: thread exit\n"));
- cv_signal(&ctrl_p->slot.attn_btn_cv);
- CALLB_CPR_EXIT(&cprinfo);
- thread_exit();
-}
-
-/*
- * Read/Write access to HPC registers. If platform nexus has non-standard
- * HPC access mechanism then regops functions are used to do reads/writes.
- */
-uint8_t
-pciehpc_reg_get8(pciehpc_t *ctrl_p, uint_t off)
-{
- PCIEHPC_DEBUG3((CE_NOTE, "read reg8 (offset %x)", off));
-
- if (ctrl_p->regops.get != NULL)
- return ((uint8_t)ctrl_p->regops.get(ctrl_p->regops.cookie,
- (off_t)off));
- else
- return (ddi_get8(ctrl_p->cfghdl,
- (uint8_t *)(ctrl_p->regs_base + off)));
-}
-
-uint16_t
-pciehpc_reg_get16(pciehpc_t *ctrl_p, uint_t off)
-{
- PCIEHPC_DEBUG3((CE_NOTE, "read reg16 (offset %x)", off));
-
- if (ctrl_p->regops.get != NULL)
- return ((uint16_t)ctrl_p->regops.get(ctrl_p->regops.cookie,
- (off_t)off));
- else
- return (ddi_get16(ctrl_p->cfghdl,
- (uint16_t *)(ctrl_p->regs_base + off)));
-}
-
-uint32_t
-pciehpc_reg_get32(pciehpc_t *ctrl_p, uint_t off)
-{
- PCIEHPC_DEBUG3((CE_NOTE, "read reg32 (offset %x)", off));
-
- if (ctrl_p->regops.get != NULL)
- return ((uint32_t)ctrl_p->regops.get(ctrl_p->regops.cookie,
- (off_t)off));
- else
- return (ddi_get32(ctrl_p->cfghdl,
- (uint32_t *)(ctrl_p->regs_base + off)));
-}
-
-void
-pciehpc_reg_put8(pciehpc_t *ctrl_p, uint_t off, uint8_t val)
-{
- PCIEHPC_DEBUG3((CE_NOTE, "write reg8 (offset %x, val %x)",
- off, val));
-
- if (ctrl_p->regops.put != NULL)
- ctrl_p->regops.put(ctrl_p->regops.cookie, (off_t)off, (uint_t)val);
- else
- ddi_put8(ctrl_p->cfghdl,
- (uint8_t *)(ctrl_p->regs_base + off), val);
-}
-
-void
-pciehpc_reg_put16(pciehpc_t *ctrl_p, uint_t off, uint16_t val)
-{
- PCIEHPC_DEBUG3((CE_NOTE, "write reg16 (offset %x, val %x)",
- off, val));
-
- if (ctrl_p->regops.put != NULL)
- ctrl_p->regops.put(ctrl_p->regops.cookie, (off_t)off, (uint_t)val);
- else
- ddi_put16(ctrl_p->cfghdl,
- (uint16_t *)(ctrl_p->regs_base + off), val);
-}
-
-void
-pciehpc_reg_put32(pciehpc_t *ctrl_p, uint_t off, uint32_t val)
-{
- PCIEHPC_DEBUG3((CE_NOTE, "write reg32 (offset %x, val %x)",
- off, val));
-
- if (ctrl_p->regops.put != NULL)
- ctrl_p->regops.put(ctrl_p->regops.cookie, (off_t)off, (uint_t)val);
- else
- ddi_put32(ctrl_p->cfghdl,
- (uint32_t *)(ctrl_p->regs_base + off), val);
-}
-
-static void
-pciehpc_dev_info(pciehpc_t *ctrl_p)
-{
- pci_regspec_t *regspec;
- int reglen;
- dev_info_t *dip = ctrl_p->dip;
-
- /*
- * Check if it is a PCIe fabric hotplug nexus. This is specially
- * not so for Rootcomplex nodes supporting PCIe hotplug.
- * We save this information so as to implement hardening for
- * fabric nodes only via pcie services.
- */
- if (pciehpc_pcie_dev(dip, ctrl_p->cfghdl) == DDI_SUCCESS)
- ctrl_p->soft_state |= PCIEHPC_SOFT_STATE_PCIE_DEV;
-
- /* Get the device number. */
- if (ddi_getlongprop(DDI_DEV_T_NONE, dip, DDI_PROP_DONTPASS,
- "reg", (caddr_t)&regspec, &reglen) != DDI_SUCCESS) {
- return;
- }
-
- ctrl_p->bus = PCI_REG_BUS_G(regspec[0].pci_phys_hi);
- ctrl_p->dev = PCI_REG_DEV_G(regspec[0].pci_phys_hi);
- ctrl_p->func = PCI_REG_FUNC_G(regspec[0].pci_phys_hi);
-
- kmem_free(regspec, reglen);
-
- PCIEHPC_DEBUG3((CE_NOTE, "pciehpc_dev_info: bus=%x, dev=%x, func=%x",
- ctrl_p->bus, ctrl_p->dev, ctrl_p->func));
-}
-
-/*
- * setup slot name/slot-number info.
- */
-void
-pciehpc_set_slot_name(pciehpc_t *ctrl_p)
-{
- pciehpc_slot_t *p = &ctrl_p->slot;
- uchar_t *slotname_data;
- int *slotnum;
- uint_t count;
- int len;
- int invalid_slotnum = 0;
- uint32_t slot_capabilities;
-
- if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, ctrl_p->dip,
- DDI_PROP_DONTPASS, "physical-slot#", &slotnum, &count) ==
- DDI_PROP_SUCCESS) {
- p->slotNum = slotnum[0];
- ddi_prop_free(slotnum);
- } else {
- slot_capabilities = pciehpc_reg_get32(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCAP);
- p->slotNum = PCIE_SLOTCAP_PHY_SLOT_NUM(slot_capabilities);
- }
-
- if (!p->slotNum) { /* platform may not have initialized it */
- PCIEHPC_DEBUG((CE_WARN, "%s#%d: Invalid slot number! ",
- ddi_driver_name(ctrl_p->dip),
- ddi_get_instance(ctrl_p->dip)));
- p->slotNum = pciehpc_reg_get8(ctrl_p, PCI_BCNF_SECBUS);
- invalid_slotnum = 1;
- }
-
- /*
- * construct the slot_name:
- * if "slot-names" property exists then use that name
- * else if valid slot number exists then it is "pcie<slot-num>".
- * else it will be "pcie<sec-bus-number>dev0"
- */
- if (ddi_getlongprop(DDI_DEV_T_ANY, ctrl_p->dip, DDI_PROP_DONTPASS,
- "slot-names", (caddr_t)&slotname_data,
- &len) == DDI_PROP_SUCCESS) {
- /*
- * Note: for PCI-E slots, the device number is always 0 so the
- * first (and only) string is the slot name for this slot.
- */
- (void) sprintf(p->slot_info.pci_slot_name,
- (char *)slotname_data + 4);
- kmem_free(slotname_data, len);
- } else {
- if (invalid_slotnum) /* use device number ie. 0 */
- (void) snprintf(p->slot_info.pci_slot_name,
- sizeof (p->slot_info.pci_slot_name), "pcie0");
- else
- (void) snprintf(p->slot_info.pci_slot_name,
- sizeof (p->slot_info.pci_slot_name), "pcie%d",
- p->slotNum);
- }
-}
-
-/*ARGSUSED*/
-static int
-pciehpc_pcie_dev(dev_info_t *dip, ddi_acc_handle_t handle)
-{
- /* get parent device's device_type property */
- char *device_type;
- int rc;
- dev_info_t *pdip = ddi_get_parent(dip);
-
- if (ddi_prop_lookup_string(DDI_DEV_T_ANY, pdip,
- DDI_PROP_DONTPASS, "device_type", &device_type)
- != DDI_PROP_SUCCESS) {
- PCIEHPC_DEBUG2((CE_NOTE, "device_type property missing for "
- "%s#%d", ddi_get_name(pdip), ddi_get_instance(pdip)));
- return (DDI_FAILURE);
- }
-
- PCIEHPC_DEBUG((CE_NOTE, "device_type=<%s>\n", device_type));
- rc = DDI_FAILURE;
- if (strcmp(device_type, "pciex") == 0)
- rc = DDI_SUCCESS;
- ddi_prop_free(device_type);
- return (rc);
-}
-
-static void
-pciehpc_disable_errors(pciehpc_t *ctrl_p)
-{
- if (ctrl_p->soft_state & PCIEHPC_SOFT_STATE_PCIE_DEV) {
- PCIE_DISABLE_ERRORS(ctrl_p->dip);
- PCIEHPC_DEBUG3((CE_NOTE, "%s%d: pciehpc_disable_errors\n",
- ddi_driver_name(ctrl_p->dip),
- ddi_get_instance(ctrl_p->dip)));
- }
-}
-
-static void
-pciehpc_enable_errors(pciehpc_t *ctrl_p)
-{
- if (ctrl_p->soft_state & PCIEHPC_SOFT_STATE_PCIE_DEV) {
- (void) PCIE_ENABLE_ERRORS(ctrl_p->dip);
- PCIEHPC_DEBUG3((CE_NOTE, "%s%d: pciehpc_enable_errors\n",
- ddi_driver_name(ctrl_p->dip),
- ddi_get_instance(ctrl_p->dip)));
- }
-}
diff --git a/usr/src/uts/common/io/hotplug/pcihp/pcihp.c b/usr/src/uts/common/io/hotplug/pcihp/pcihp.c
index 0faa5b8bdf..d84b72c7cb 100644
--- a/usr/src/uts/common/io/hotplug/pcihp/pcihp.c
+++ b/usr/src/uts/common/io/hotplug/pcihp/pcihp.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -258,9 +258,6 @@ static int pcihp_get_board_type(struct pcihp_slotinfo *);
/* sysevent function */
static void pcihp_gen_sysevent(char *, int, int, dev_info_t *, int);
-extern int pcicfg_configure(dev_info_t *, uint_t);
-extern int pcicfg_unconfigure(dev_info_t *, uint_t);
-
static int pcihp_list_occupants(dev_info_t *, void *);
static int pcihp_indirect_map(dev_info_t *dip);
@@ -1558,7 +1555,8 @@ pcihp_configure_ap(pcihp_t *pcihp_p, int pci_dev)
/*
* Call the configurator to configure the card.
*/
- if (pcicfg_configure(self, pci_dev) != PCICFG_SUCCESS) {
+ if (pcicfg_configure(self, pci_dev, PCICFG_ALL_FUNC, 0)
+ != PCICFG_SUCCESS) {
if (slotinfop->slot_type & HPC_SLOT_TYPE_CPCI) {
if (pcihp_cpci_blue_led)
pcihp_hs_csr_op(pcihp_p, pci_dev,
@@ -1717,8 +1715,8 @@ pcihp_unconfigure_ap(pcihp_t *pcihp_p, int pci_dev)
(void) hpc_nexus_control(slotinfop->slot_hdl,
HPC_CTRL_DEV_UNCONFIG_START, NULL);
- if (pcicfg_unconfigure(self,
- pci_dev) == PCICFG_SUCCESS) {
+ if (pcicfg_unconfigure(self, pci_dev,
+ PCICFG_ALL_FUNC, 0) == PCICFG_SUCCESS) {
/*
* Now that resources are freed,
* clear EXT and Turn LED ON.
@@ -2155,7 +2153,8 @@ pcihp_new_slot_state(dev_info_t *dip, hpc_slot_t hdl,
/*
* Call the configurator to configure the card.
*/
- if (pcicfg_configure(dip, pci_dev) != PCICFG_SUCCESS) {
+ if (pcicfg_configure(dip, pci_dev, PCICFG_ALL_FUNC, 0)
+ != PCICFG_SUCCESS) {
if (slotinfop->slot_type & HPC_SLOT_TYPE_CPCI) {
if (pcihp_cpci_blue_led)
pcihp_hs_csr_op(pcihp_p,
@@ -2465,7 +2464,8 @@ pcihp_event_handler(caddr_t slot_arg, uint_t event_mask)
/*
* Call the configurator to configure the card.
*/
- if (pcicfg_configure(pcihp_p->dip, pci_dev) != PCICFG_SUCCESS) {
+ if (pcicfg_configure(pcihp_p->dip, pci_dev, PCICFG_ALL_FUNC, 0)
+ != PCICFG_SUCCESS) {
if (slotinfop->slot_type & HPC_SLOT_TYPE_CPCI) {
if (pcihp_cpci_blue_led)
pcihp_hs_csr_op(pcihp_p, pci_dev,
@@ -2611,8 +2611,8 @@ pcihp_event_handler(caddr_t slot_arg, uint_t event_mask)
(void) hpc_nexus_control(slotinfop->slot_hdl,
HPC_CTRL_DEV_UNCONFIG_START, NULL);
- if (pcicfg_unconfigure(pcihp_p->dip,
- pci_dev) == PCICFG_SUCCESS) {
+ if (pcicfg_unconfigure(pcihp_p->dip, pci_dev,
+ PCICFG_ALL_FUNC, 0) == PCICFG_SUCCESS) {
/* Resources freed. Turn LED on. Clear EXT. */
if (slotinfop->slot_type & HPC_SLOT_TYPE_CPCI) {
diff --git a/usr/src/uts/common/io/hotplug/pcishpc/pcishpc.c b/usr/src/uts/common/io/hotplug/pcishpc/pcishpc.c
deleted file mode 100644
index 0ceac54b34..0000000000
--- a/usr/src/uts/common/io/hotplug/pcishpc/pcishpc.c
+++ /dev/null
@@ -1,2656 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License (the "License").
- * You may not use this file except in compliance with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information: Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- */
-/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
- */
-
-/*
- * PCISHPC - The Standard PCI HotPlug Controller driver module. This driver
- * can be used with PCI HotPlug controllers that are compatible
- * with the PCI SHPC specification 1.x.
- */
-
-#include <sys/note.h>
-#include <sys/conf.h>
-#include <sys/kmem.h>
-#include <sys/kstat.h>
-#include <sys/debug.h>
-#include <sys/vtrace.h>
-#include <sys/modctl.h>
-#include <sys/autoconf.h>
-#include <sys/varargs.h>
-#include <sys/hwconf.h>
-#include <sys/ddi_impldefs.h>
-#include <sys/pci.h>
-#include <sys/callb.h>
-#include <sys/ddi.h>
-#include <sys/sunddi.h>
-#include <sys/sunndi.h>
-#include <sys/ndi_impldefs.h>
-#include <sys/hotplug/pci/pcishpc.h>
-#include <sys/hotplug/pci/pcishpc_regs.h>
-#include <sys/hotplug/hpcsvc.h>
-
-
-/* General Register bit weights for the 32-bit SHPC registers */
-#define REG_BIT0 0x00000001
-#define REG_BIT1 0x00000002
-#define REG_BIT2 0x00000004
-#define REG_BIT3 0x00000008
-#define REG_BIT4 0x00000010
-#define REG_BIT5 0x00000020
-#define REG_BIT6 0x00000040
-#define REG_BIT7 0x00000080
-#define REG_BIT8 0x00000100
-#define REG_BIT9 0x00000200
-#define REG_BIT10 0x00000400
-#define REG_BIT11 0x00000800
-#define REG_BIT12 0x00001000
-#define REG_BIT13 0x00002000
-#define REG_BIT14 0x00004000
-#define REG_BIT15 0x00008000
-#define REG_BIT16 0x00010000
-#define REG_BIT17 0x00020000
-#define REG_BIT18 0x00040000
-#define REG_BIT19 0x00080000
-#define REG_BIT20 0x00100000
-#define REG_BIT21 0x00200000
-#define REG_BIT22 0x00400000
-#define REG_BIT23 0x00800000
-#define REG_BIT24 0x01000000
-#define REG_BIT25 0x02000000
-#define REG_BIT26 0x04000000
-#define REG_BIT27 0x08000000
-#define REG_BIT28 0x10000000
-#define REG_BIT29 0x20000000
-#define REG_BIT30 0x40000000
-#define REG_BIT31 0x80000000
-
-/* Definitions used with the SHPC SHPC_SLOTS_AVAIL_I_REG register */
-#define SHPC_AVAIL_33MHZ_CONV_SPEED_SHIFT 0
-#define SHPC_AVAIL_66MHZ_PCIX_SPEED_SHIFT 8
-#define SHPC_AVAIL_100MHZ_PCIX_SPEED_SHIFT 16
-#define SHPC_AVAIL_133MHZ_PCIX_SPEED_SHIFT 24
-#define SHPC_AVAIL_SPEED_MASK 0x1F
-
-/* Definitions used with the SHPC SHPC_SLOTS_AVAIL_II_REG register */
-#define SHPC_AVAIL_66MHZ_CONV_SPEED_SHIFT 0
-
-/* Register bits used with the SHPC SHPC_PROF_IF_SBCR_REG register */
-#define SHPC_SBCR_33MHZ_CONV_SPEED 0
-#define SHPC_SBCR_66MHZ_CONV_SPEED REG_BIT0
-#define SHPC_SBCR_66MHZ_PCIX_SPEED REG_BIT1
-#define SHPC_SBCR_100MHZ_PCIX_SPEED (REG_BIT0|REG_BIT1)
-#define SHPC_SBCR_133MHZ_PCIX_SPEED REG_BIT2
-#define SHPC_SBCR_SPEED_MASK (REG_BIT0|REG_BIT1|REG_BIT2)
-
-/* Register bits used with the SHPC SHPC_COMMAND_STATUS_REG register */
-#define SHPC_COMM_STS_ERR_INVALID_SPEED REG_BIT19
-#define SHPC_COMM_STS_ERR_INVALID_COMMAND REG_BIT18
-#define SHPC_COMM_STS_ERR_MRL_OPEN REG_BIT17
-#define SHPC_COMM_STS_ERR_MASK (REG_BIT17|REG_BIT18|REG_BIT19)
-#define SHPC_COMM_STS_CTRL_BUSY REG_BIT16
-#define SHPC_COMM_STS_SET_SPEED REG_BIT6
-
-/* Register bits used with the SHPC SHPC_CTRL_SERR_INT_REG register */
-#define SHPC_SERR_INT_GLOBAL_IRQ_MASK REG_BIT0
-#define SHPC_SERR_INT_GLOBAL_SERR_MASK REG_BIT1
-#define SHPC_SERR_INT_CMD_COMPLETE_MASK REG_BIT2
-#define SHPC_SERR_INT_ARBITER_SERR_MASK REG_BIT3
-#define SHPC_SERR_INT_CMD_COMPLETE_IRQ REG_BIT16
-#define SHPC_SERR_INT_ARBITER_IRQ REG_BIT17
-#define SHPC_SERR_INT_MASK_ALL (REG_BIT0|REG_BIT1|REG_BIT2|REG_BIT3)
-
-/* Register bits used with the SHPC SHPC_LOGICAL_SLOT_REGS register */
-#define SHPC_SLOT_POWER_ONLY REG_BIT0
-#define SHPC_SLOT_ENABLED REG_BIT1
-#define SHPC_SLOT_DISABLED (REG_BIT0 | REG_BIT1)
-#define SHPC_SLOT_STATE_MASK (REG_BIT0 | REG_BIT1)
-#define SHPC_SLOT_MRL_STATE_MASK REG_BIT8
-#define SHPC_SLOT_66MHZ_CONV_CAPABLE REG_BIT9
-#define SHPC_SLOT_CARD_EMPTY_MASK (REG_BIT10 | REG_BIT11)
-#define SHPC_SLOT_66MHZ_PCIX_CAPABLE REG_BIT12
-#define SHPC_SLOT_100MHZ_PCIX_CAPABLE REG_BIT13
-#define SHPC_SLOT_133MHZ_PCIX_CAPABLE (REG_BIT12 | REG_BIT13)
-#define SHPC_SLOT_PCIX_CAPABLE_MASK (REG_BIT12 | REG_BIT13)
-#define SHPC_SLOT_PCIX_CAPABLE_SHIFT 12
-#define SHPC_SLOT_PRESENCE_DETECTED REG_BIT16
-#define SHPC_SLOT_ISO_PWR_DETECTED REG_BIT17
-#define SHPC_SLOT_ATTN_DETECTED REG_BIT18
-#define SHPC_SLOT_MRL_DETECTED REG_BIT19
-#define SHPC_SLOT_POWER_DETECTED REG_BIT20
-#define SHPC_SLOT_PRESENCE_MASK REG_BIT24
-#define SHPC_SLOT_ISO_PWR_MASK REG_BIT25
-#define SHPC_SLOT_ATTN_MASK REG_BIT26
-#define SHPC_SLOT_MRL_MASK REG_BIT27
-#define SHPC_SLOT_POWER_MASK REG_BIT28
-#define SHPC_SLOT_MRL_SERR_MASK REG_BIT29
-#define SHPC_SLOT_POWER_SERR_MASK REG_BIT30
-#define SHPC_SLOT_MASK_ALL (REG_BIT24|REG_BIT25|REG_BIT26|\
- REG_BIT27|REG_BIT28|REG_BIT30)
-
-/* Register bits used with the SHPC SHPC_IRQ_LOCATOR_REG register. */
-#define SHPC_IRQ_CMD_COMPLETE REG_BIT0
-#define SHPC_IRQ_SLOT_N_PENDING REG_BIT1
-
-/* Register bits used with the SHPC SHPC_SERR_LOCATOR_REG register. */
-#define SHPC_IRQ_SERR_ARBITER_PENDING REG_BIT0
-#define SHPC_IRQ_SERR_SLOT_N_PENDING REG_BIT1
-
-/* Register bits used with the SHPC SHPC_SLOT_CONFIGURATION_REG register */
-#define SHPC_SLOT_CONFIG_MRL_SENSOR REG_BIT30
-#define SHPC_SLOT_CONFIG_ATTN_BUTTON REG_BIT31
-#define SHPC_SLOT_CONFIG_PHY_SLOT_NUM_SHIFT 16
-#define SHPC_SLOT_CONFIG_PHY_SLOT_NUM_MASK 0x3FF
-#define SHPC_SLOT_CONFIG_PHY_SLOT_NUM(reg) (((reg) >> 16) & 0x3FF)
-
-/* Max PCISHPC controller slots */
-#define MAX_SHPC_SLOTS 31
-
-/* PCISHPC controller command complete delay in microseconds. */
-#define SHPC_COMMAND_WAIT_TIME 10000
-
-/*
- * Power good wait time after issuing a command to change the slot state
- * to power only state.
- */
-#define SHPC_POWER_GOOD_WAIT_TIME 220000
-
-/* reset delay to 1 sec. */
-static int pcishpc_reset_delay = 1000000;
-
-/* PCISHPC controller softstate structure */
-typedef struct pcishpc_ctrl {
- dev_info_t *shpc_dip; /* DIP for SHPC Nexus */
- ddi_acc_handle_t shpc_config_hdl; /* SHPC DDI cfg handle */
- kmutex_t shpc_intr_mutex; /* Interrupt mutex lock */
- boolean_t interrupt_installed; /* Interrupt installed */
- boolean_t command_complete; /* Got a cmd complete IRQ */
- kcondvar_t cmd_comp_cv;
- boolean_t arbiter_timeout; /* Got a Arb timeout IRQ */
- kmutex_t shpc_mutex; /* Mutex for this SHPC */
- char nexus_path[MAXNAMELEN]; /* Pathname of Nexus */
- uint32_t shpc_bus; /* SHPC bus */
- uint32_t shpc_dev; /* SHPC device */
- uint32_t shpc_func; /* SHPC function */
- uint8_t shpc_dword_select; /* SHPC register offset */
- uint8_t shpc_dword_data_reg; /* SHPC data register */
- uint32_t shpc_slots_avail1_reg; /* SHPC Slots Available1 Reg */
- uint32_t shpc_slots_avail2_reg; /* SHPC Slots Available2 Reg */
- uint32_t numSlotsImpl; /* # of HP Slots Implemented */
- uint32_t numSlotsConn; /* # of HP Slots Connected */
- int currBusSpeed; /* Current Bus Speed */
- uint32_t deviceStart; /* 1st PCI Device # */
- uint32_t physStart; /* 1st Phys Device # */
- uint32_t deviceIncreases; /* Device # Increases */
- struct pcishpc *slots[MAX_SHPC_SLOTS]; /* Slot pointers */
- boolean_t has_attn; /* Do we have attn btn? */
- boolean_t has_mrl; /* Do we have MRL? */
- struct pcishpc_ctrl *nextp; /* Linked list pointer */
-} pcishpc_ctrl_t;
-
-/* PCISHPC slot softstate structure */
-typedef struct pcishpc {
- pcishpc_ctrl_t *ctrl; /* SHPC ctrl for this slot */
- hpc_slot_info_t slot_info; /* HPS framework slot info */
- hpc_slot_t slot_handle; /* HPS framework handle */
- hpc_slot_ops_t *slot_ops; /* HPS framework callbacks */
- uint32_t fault_led_state; /* Fault LED state */
- uint32_t power_led_state; /* Power LED state */
- uint32_t attn_led_state; /* Attn LED state */
- uint32_t active_led_state; /* Active LED state */
- hpc_slot_state_t slot_state; /* Slot State */
- uint32_t deviceNum; /* PCI device num for slot */
- uint32_t slotNum; /* SHPC slot number */
- uint32_t phy_slot_num; /* physical slot number */
- uint32_t slot_events; /* Slot event(s) IRQ */
- kcondvar_t attn_btn_cv; /* ATTN button pressed intr */
- boolean_t attn_btn_pending;
- kthread_t *attn_btn_threadp; /* ATTN button event thread */
- boolean_t attn_btn_thread_exit;
- struct pcishpc *nextp; /* Linked list pointer */
-} pcishpc_t;
-/* mutex to protect the shpc_head and shpc_ctrl_head linked lists */
-static kmutex_t pcishpc_list_mutex;
-
-/* Pointer to a linked list of shpc slot softstate structures */
-static pcishpc_t *pcishpc_head = NULL;
-
-/* Pointer to a linked list of shpc controller softstate structures */
-static pcishpc_ctrl_t *pcishpc_ctrl_head = NULL;
-
-/* mutex to protect access to the controller */
-static kmutex_t pcishpc_control_mutex;
-
-/* SHPC static function prototypes */
-static pcishpc_ctrl_t *pcishpc_create_controller(dev_info_t *dip);
-static int pcishpc_destroy_controller(dev_info_t *dip);
-static pcishpc_ctrl_t *pcishpc_get_controller(dev_info_t *dip);
-static pcishpc_t *pcishpc_create_slot(pcishpc_ctrl_t *ctrl_p);
-static int pcishpc_destroy_slots(pcishpc_ctrl_t *ctrl_p);
-static pcishpc_t *pcishpc_hpc_get_slot_state(hpc_slot_t slot);
-static int pcishpc_setup_controller(pcishpc_ctrl_t *ctrl_p);
-static int pcishpc_register_slot(pcishpc_ctrl_t *ctrl_p, int slot);
-static int pcishpc_connect(caddr_t ops_arg,
- hpc_slot_t slot_hdl, void *data,
- uint_t flags);
-static int pcishpc_disconnect(caddr_t ops_arg,
- hpc_slot_t slot_hdl, void *data,
- uint_t flags);
-static int pcishpc_pci_control(caddr_t ops_arg, hpc_slot_t slot_hdl,
- int request, caddr_t arg);
-static int pcishpc_setled(pcishpc_t *pcishpc_p, hpc_led_t led,
- hpc_led_state_t state);
-static int pcishpc_set_power_state(pcishpc_t *pcishpc_p,
- hpc_slot_state_t state);
-static int pcishpc_set_bus_speed(pcishpc_t *pcishpc_p);
-static int pcishpc_probe_controller(pcishpc_ctrl_t *pcishpc_p);
-static int pcishpc_get_pci_info(pcishpc_ctrl_t *pcishpc_p);
-static void pcishpc_get_slot_state(pcishpc_t *pcishpc_p);
-static int pcishpc_process_intr(pcishpc_ctrl_t *ctrl_p);
-static int pcishpc_enable_irqs(pcishpc_ctrl_t *ctrl_p);
-static int pcishpc_disable_irqs(pcishpc_ctrl_t *ctrl_p);
-static void pcishpc_set_soft_int(pcishpc_ctrl_t *ctrl_p);
-static int pcishpc_wait_busy(pcishpc_ctrl_t *ctrl_p);
-static int pcishpc_issue_command(pcishpc_ctrl_t *ctrl_p,
- uint32_t cmd_code);
-static int pcishpc_led_shpc_to_hpc(int state);
-static int pcishpc_led_hpc_to_shpc(int state);
-static int pcishpc_slot_shpc_to_hpc(int state);
-static int pcishpc_slot_hpc_to_shpc(int state);
-static char *pcishpc_textledstate(hpc_led_state_t state);
-static char *pcishpc_textslotstate(hpc_slot_state_t state);
-static char *pcishpc_textrequest(int request);
-static int pcishpc_set_slot_state(pcishpc_t *pcishpc_p);
-static void pcishpc_dump_regs(pcishpc_ctrl_t *ctrl_p);
-static void pcishpc_write_reg(pcishpc_ctrl_t *ctrl_p, int reg,
- uint32_t data);
-static uint32_t pcishpc_read_reg(pcishpc_ctrl_t *ctrl_p, int reg);
-static void pcishpc_debug(char *fmt, ...);
-
-static void pcishpc_attn_btn_handler(pcishpc_t *pcishpc_p);
-static void pcishpc_set_slot_name(pcishpc_ctrl_t *ctrl_p, int slot);
-
-static int pcishpc_debug_enabled = 0;
-
-/* Module operations information for the kernel */
-extern struct mod_ops mod_miscops;
-static struct modlmisc modlmisc = {
- &mod_miscops,
- "PCI SHPC hotplug module",
-};
-
-/* Module linkage information for the kernel */
-static struct modlinkage modlinkage = {
- MODREV_1,
- &modlmisc,
- NULL
-};
-
-int
-_init(void)
-{
- int rc;
-
- if ((rc = mod_install(&modlinkage)) != 0) {
- pcishpc_debug("pcishpc: install error=%d", rc);
- return (rc);
- }
-
- /* Init the shpc driver list mutex. */
- mutex_init(&pcishpc_list_mutex, NULL, MUTEX_DRIVER, NULL);
- /* Init the shpc control mutex. */
- mutex_init(&pcishpc_control_mutex, NULL, MUTEX_DRIVER, NULL);
-
- pcishpc_debug("pcishpc: installed");
- return (rc);
-}
-
-int
-_fini(void)
-{
- pcishpc_debug("pcishpc: _fini called()");
- /* XXX - to be fixed later */
- return (EBUSY);
-}
-
-int
-_info(struct modinfo *modinfop)
-{
- pcishpc_debug("pcishpc: _info called()");
- return (mod_info(&modlinkage, modinfop));
-}
-
-
-/*
- * pcishpc_create_controller()
- *
- * This function allocates and creates an SHPC controller state structure
- * and adds it to the linked list of controllers.
- */
-static pcishpc_ctrl_t *
-pcishpc_create_controller(dev_info_t *dip)
-{
- pcishpc_ctrl_t *ctrl_p;
-
- pcishpc_debug("pcishpc: create controller for %s#%d",
- ddi_driver_name(dip), ddi_get_instance(dip));
-
- ctrl_p = kmem_zalloc(sizeof (pcishpc_ctrl_t), KM_SLEEP);
-
- ctrl_p->interrupt_installed = B_FALSE;
- ctrl_p->shpc_dip = dip;
-
- (void) ddi_pathname(dip, ctrl_p->nexus_path);
-
- /* Get the PCI BUS,DEVICE,FUNCTION for this SHPC controller. */
- if (pcishpc_get_pci_info(ctrl_p) != DDI_SUCCESS) {
-
- pcishpc_debug("pcishpc_create_controller() "
- "Error: pcishpc_get_pci_info() failed");
- kmem_free(ctrl_p, sizeof (pcishpc_ctrl_t));
- return (NULL);
- }
-
- if (pci_config_setup(dip, &ctrl_p->shpc_config_hdl) != DDI_SUCCESS) {
- pcishpc_debug("pcishpc_create_controller() "
- "Error: Unable to map SHPC PCI Config registers");
- kmem_free(ctrl_p, sizeof (pcishpc_ctrl_t));
- return (NULL);
- }
-
- /* Make sure the SHPC is listed in the PCI capibilities list. */
- if (pcishpc_probe_controller(ctrl_p) != DDI_SUCCESS) {
- pcishpc_debug("pcishpc_create_controller() "
- "Error: Unable to find SHPC controller");
- pci_config_teardown(&ctrl_p->shpc_config_hdl);
- kmem_free(ctrl_p, sizeof (pcishpc_ctrl_t));
- return (NULL);
- }
-
- /* Init the interrupt mutex */
- mutex_init(&ctrl_p->shpc_intr_mutex, NULL, MUTEX_DRIVER,
- (void *)PCISHPC_INTR_PRI);
-
- /* Interrupts are now enabled. */
- ctrl_p->interrupt_installed = B_TRUE;
-
- /* Init the shpc controller's mutex. */
- mutex_init(&ctrl_p->shpc_mutex, NULL, MUTEX_DRIVER, NULL);
-
- mutex_enter(&pcishpc_list_mutex);
-
- /* Insert new softstate into linked list of current soft states. */
- ctrl_p->nextp = pcishpc_ctrl_head;
- pcishpc_ctrl_head = ctrl_p;
-
- mutex_exit(&pcishpc_list_mutex);
-
- pcishpc_debug("pcishpc_create_controller() success");
-
- return (ctrl_p);
-}
-
-
-/*
- * pcishpc_probe_controller()
- *
- * This function probes to make sure there is indeed an SHPC controller.
- */
-static int
-pcishpc_probe_controller(pcishpc_ctrl_t *ctrl_p)
-{
- uint8_t cap_ptr;
- uint8_t cap_id;
- uint16_t status;
-
- status = pci_config_get16(ctrl_p->shpc_config_hdl, PCI_CONF_STAT);
- if (!(status & PCI_STAT_CAP)) {
- return (DDI_FAILURE);
- }
-
- /* Get a pointer to the PCI capabilities list. */
- cap_ptr = pci_config_get8(ctrl_p->shpc_config_hdl, PCI_BCNF_CAP_PTR);
-
- cap_ptr &= 0xFC;
-
- /* Walk PCI capabilities list searching for the SHPC capability. */
- while (cap_ptr != PCI_CAP_NEXT_PTR_NULL) {
- cap_id = pci_config_get8(ctrl_p->shpc_config_hdl, cap_ptr);
-
- pcishpc_debug("pcishpc_probe_controller() capability @ "
- "pointer=%02x (id=%02x)", cap_ptr, cap_id);
-
- if (cap_id == PCI_CAP_ID_PCI_HOTPLUG) {
- /* Save the SHPC register offset. */
- ctrl_p->shpc_dword_select = cap_ptr+2;
- /* Save the SHPC data register. */
- ctrl_p->shpc_dword_data_reg = cap_ptr+4;
- break;
- }
-
- /* Get the pointer to the next capability. */
- cap_ptr = pci_config_get8(ctrl_p->shpc_config_hdl,
- cap_ptr+1);
-
- cap_ptr &= 0xFC;
- }
-
- if (cap_ptr == PCI_CAP_NEXT_PTR_NULL) {
- return (DDI_FAILURE);
- }
-
- pcishpc_debug("pcishpc_probe_controller() Found SHPC capibility");
-
- return (DDI_SUCCESS);
-}
-
-
-/*
- * pcishpc_destroy_controller()
- *
- * This function deallocates all of the SHPC controller resources.
- */
-static int
-pcishpc_destroy_controller(dev_info_t *dip)
-{
- pcishpc_ctrl_t *ctrl_p;
- pcishpc_ctrl_t **ctrl_pp;
-
- pcishpc_debug("pcishpc_destroy_controller() called(dip=%p)", dip);
-
- mutex_enter(&pcishpc_list_mutex);
-
- ctrl_pp = &pcishpc_ctrl_head;
-
- /* Walk the linked list of softstates. */
- while ((ctrl_p = *ctrl_pp) != NULL) {
- if (ctrl_p->shpc_dip == dip) {
- /*
- * Deallocate the slot state structures for
- * this controller.
- */
- (void) pcishpc_destroy_slots(ctrl_p);
-
- *ctrl_pp = ctrl_p->nextp;
-
- pci_config_teardown(&ctrl_p->shpc_config_hdl);
-
- cv_destroy(&ctrl_p->cmd_comp_cv);
-
- mutex_destroy(&ctrl_p->shpc_mutex);
- mutex_destroy(&ctrl_p->shpc_intr_mutex);
- kmem_free(ctrl_p, sizeof (pcishpc_ctrl_t));
- mutex_exit(&pcishpc_list_mutex);
-
- pcishpc_debug("pcishpc_destroy_controller() success");
- return (DDI_SUCCESS);
- }
- ctrl_pp = &(ctrl_p->nextp);
- }
-
- mutex_exit(&pcishpc_list_mutex);
-
- pcishpc_debug("pcishpc_destroy_controller() not found");
-
- return (DDI_FAILURE);
-}
-
-
-/*
- * pcishpc_intr()
- *
- * This is the SHPC controller interrupt handler.
- */
-int
-pcishpc_intr(dev_info_t *dip)
-{
- pcishpc_ctrl_t *ctrl_p = pcishpc_get_controller(dip);
- int slot;
- uint32_t irq_locator, irq_serr_locator, reg;
- boolean_t slot_event = B_FALSE;
-
- pcishpc_debug("pcishpc_intr() called");
-
- if (ctrl_p->interrupt_installed == B_TRUE) {
- mutex_enter(&ctrl_p->shpc_intr_mutex);
-
- pcishpc_debug("pcishpc_intr() interrupt received");
-
- reg = pcishpc_read_reg(ctrl_p, SHPC_CTRL_SERR_INT_REG);
-
- if (reg & SHPC_SERR_INT_CMD_COMPLETE_IRQ) {
- pcishpc_debug("pcishpc_intr() "
- "SHPC_SERR_INT_CMD_COMPLETE_IRQ detected");
- ctrl_p->command_complete = B_TRUE;
- cv_signal(&ctrl_p->cmd_comp_cv);
- }
-
- if (reg & SHPC_SERR_INT_ARBITER_IRQ) {
- pcishpc_debug("pcishpc_intr() SHPC_SERR_INT_ARBITER_IRQ"
- " detected");
- ctrl_p->arbiter_timeout = B_TRUE;
- }
-
- /* Write back the SERR INT register to acknowledge the IRQs. */
- pcishpc_write_reg(ctrl_p, SHPC_CTRL_SERR_INT_REG, reg);
-
- irq_locator = pcishpc_read_reg(ctrl_p, SHPC_IRQ_LOCATOR_REG);
-
- irq_serr_locator = pcishpc_read_reg(ctrl_p,
- SHPC_SERR_LOCATOR_REG);
-
- /* Check for slot events that might have occured. */
- for (slot = 0; slot < ctrl_p->numSlotsImpl; slot++) {
- if ((irq_locator & (SHPC_IRQ_SLOT_N_PENDING<<slot)) ||
- (irq_serr_locator &
- (SHPC_IRQ_SERR_SLOT_N_PENDING<<slot))) {
- pcishpc_debug("pcishpc_intr() slot %d and "
- "pending IRQ", slot+1);
-
- /*
- * Note that we will need to generate a
- * slot event interrupt.
- */
- slot_event = B_TRUE;
-
- reg = pcishpc_read_reg(ctrl_p,
- SHPC_LOGICAL_SLOT_REGS+slot);
-
- /* Record any pending slot interrupts/events. */
- ctrl_p->slots[slot]->slot_events |= reg;
-
- /* Acknoledge any slot interrupts */
- pcishpc_write_reg(ctrl_p,
- SHPC_LOGICAL_SLOT_REGS+slot, reg);
- }
- }
-
- if (slot_event == B_TRUE) {
- pcishpc_debug("pcishpc_intr() slot(s) have event(s)");
- (void) pcishpc_process_intr(ctrl_p);
- } else {
- pcishpc_debug("pcishpc_intr() No slot event(s)");
- }
-
- mutex_exit(&ctrl_p->shpc_intr_mutex);
-
- pcishpc_debug("pcishpc_intr() claimed");
-
- return (DDI_INTR_CLAIMED);
- }
-
- pcishpc_debug("pcishpc_intr() unclaimed");
-
- return (DDI_INTR_UNCLAIMED);
-}
-
-/*
- * pcishpc_process_intr()
- *
- * This is the SHPC soft interrupt handler.
- */
-static int
-pcishpc_process_intr(pcishpc_ctrl_t *ctrl_p)
-{
- int slot;
-
- mutex_enter(&ctrl_p->shpc_mutex);
-
- pcishpc_debug("pcishpc_process_intr() called");
-
- /* XXX - add event handling code here */
- for (slot = 0; slot < ctrl_p->numSlotsImpl; slot++) {
- if (ctrl_p->slots[slot]->slot_events &
- SHPC_SLOT_PRESENCE_DETECTED)
- pcishpc_debug("slot %d: SHPC_SLOT_PRESENCE_DETECTED",
- slot+1);
-
- if (ctrl_p->slots[slot]->slot_events &
- SHPC_SLOT_ISO_PWR_DETECTED)
- pcishpc_debug("slot %d: SHPC_SLOT_ISO_PWR_DETECTED",
- slot+1);
-
- if (ctrl_p->slots[slot]->slot_events &
- SHPC_SLOT_ATTN_DETECTED) {
- pcishpc_debug("slot %d: SHPC_SLOT_ATTN_DETECTED",
- slot+1);
- /*
- * if ATTN button event is still pending
- * then cancel it
- */
- if (ctrl_p->slots[slot]->attn_btn_pending == B_TRUE)
- ctrl_p->slots[slot]->attn_btn_pending = B_FALSE;
-
- /* wake up the ATTN event handler */
- cv_signal(&ctrl_p->slots[slot]->attn_btn_cv);
- }
-
- if (ctrl_p->slots[slot]->slot_events & SHPC_SLOT_MRL_DETECTED)
- pcishpc_debug("slot %d: SHPC_SLOT_MRL_DETECTED",
- slot+1);
-
- if (ctrl_p->slots[slot]->slot_events & SHPC_SLOT_POWER_DETECTED)
- pcishpc_debug("slot %d: SHPC_SLOT_POWER_DETECTED",
- slot+1);
-
- /* Clear the events now that we've processed all of them. */
- ctrl_p->slots[slot]->slot_events = 0;
- }
-
- mutex_exit(&ctrl_p->shpc_mutex);
-
- return (DDI_INTR_CLAIMED);
-}
-
-
-/*
- * pcishpc_get_controller()
- *
- * This function retrieves the hot plug SHPC controller soft state.
- */
-static pcishpc_ctrl_t *
-pcishpc_get_controller(dev_info_t *dip)
-{
- pcishpc_ctrl_t *ctrl_p;
-
- pcishpc_debug("pcishpc_get_controller() called (dip=%p)", dip);
-
- mutex_enter(&pcishpc_list_mutex);
-
- ctrl_p = pcishpc_ctrl_head;
-
- while (ctrl_p) {
- if (ctrl_p->shpc_dip == dip)
- break;
- ctrl_p = ctrl_p->nextp;
- }
-
- mutex_exit(&pcishpc_list_mutex);
-
- pcishpc_debug("pcishpc_get_controller() (ctrl_p=%llx)", ctrl_p);
-
- return (ctrl_p);
-}
-
-
-/*
- * pcishpc_hpc_get_slot_state()
- *
- * This function retrieves the hot plug SHPC soft state from the
- * the HPS framework slot handle.
- */
-static pcishpc_t *
-pcishpc_hpc_get_slot_state(hpc_slot_t slot)
-{
- pcishpc_t *pcishpc_p;
-
- pcishpc_debug("pcishpc_hpc_get_slot_state() called (hpc_slot=%x)",
- slot);
-
- mutex_enter(&pcishpc_list_mutex);
-
- pcishpc_p = pcishpc_head;
-
- while (pcishpc_p) {
- if (pcishpc_p->slot_handle == slot) {
- pcishpc_debug("pcishpc_hpc_get_slot_state() found "
- "(pcishpc=%x)", pcishpc_p);
- mutex_exit(&pcishpc_list_mutex);
- return (pcishpc_p);
- }
- pcishpc_p = pcishpc_p->nextp;
- }
-
- mutex_exit(&pcishpc_list_mutex);
-
- pcishpc_debug("pcishpc_hpc_get_slot_state() failed (slot=%x)", slot);
-
- return (NULL);
-}
-
-
-/*
- * pcishpc_get_pci_info()
- *
- * Read the PCI Bus, PCI Device, and PCI function for the SHPC controller.
- */
-static int
-pcishpc_get_pci_info(pcishpc_ctrl_t *pcishpc_p)
-{
- pci_regspec_t *regspec;
- int reglen;
-
- pcishpc_debug("pcishpc_get_pci_info() called");
-
- if (ddi_getlongprop(DDI_DEV_T_NONE, pcishpc_p->shpc_dip,
- DDI_PROP_DONTPASS, "reg", (caddr_t)&regspec, &reglen)
- != DDI_SUCCESS) {
- pcishpc_debug("pcishpc_get_pci_info() failed to get regspec.");
- return (DDI_FAILURE);
- }
-
- pcishpc_p->shpc_bus = PCI_REG_BUS_G(regspec[0].pci_phys_hi);
- pcishpc_p->shpc_dev = PCI_REG_DEV_G(regspec[0].pci_phys_hi);
- pcishpc_p->shpc_func = PCI_REG_FUNC_G(regspec[0].pci_phys_hi);
-
- kmem_free(regspec, reglen);
-
- pcishpc_debug("pcishpc_get_pci_info() %s%d: bus=%d, dev=%d, func=%d",
- ddi_driver_name(pcishpc_p->shpc_dip),
- ddi_get_instance(pcishpc_p->shpc_dip),
- pcishpc_p->shpc_bus, pcishpc_p->shpc_dev,
- pcishpc_p->shpc_func);
-
- return (DDI_SUCCESS);
-}
-
-
-/*
- * pcishpc_init()
- *
- * Install and configure an SHPC controller and register the HotPlug slots
- * with the Solaris HotPlug framework. This function is usually called by
- * a PCI bridge Nexus driver that has a built in SHPC controller.
- */
-int
-pcishpc_init(dev_info_t *dip)
-{
- pcishpc_ctrl_t *ctrl_p;
- int i;
-
- pcishpc_debug("pcishpc_init() called from %s#%d",
- ddi_driver_name(dip), ddi_get_instance(dip));
-
- mutex_enter(&pcishpc_control_mutex);
-
- if (pcishpc_get_controller(dip) != NULL) {
- pcishpc_debug("pcishpc_init() shpc instance already "
- "initialized!");
- mutex_exit(&pcishpc_control_mutex);
- return (DDI_SUCCESS);
- }
-
- /* Initialize soft state structure for the SHPC instance. */
- ctrl_p = pcishpc_create_controller(dip);
-
- if (ctrl_p == NULL) {
- pcishpc_debug("pcishpc_init() failed to create shpc softstate");
- mutex_exit(&pcishpc_control_mutex);
- return (DDI_FAILURE);
- }
-
- if (pcishpc_setup_controller(ctrl_p) != DDI_SUCCESS) {
- pcishpc_debug("pcishpc_init() failed to setup controller");
- (void) pcishpc_destroy_controller(dip);
- mutex_exit(&pcishpc_control_mutex);
- return (DDI_FAILURE);
- }
-
-#if 0
- pcishpc_debug("%s%d: P2P bridge register dump:",
- ddi_driver_name(dip), ddi_get_instance(dip));
-
- for (i = 0; i < 0x100; i += 4) {
- pcishpc_debug("SHPC Cfg reg 0x%02x: %08x", i,
- pci_config_get32(ctrl_p->shpc_config_hdl, i));
- }
-#endif
-
- /* Setup each HotPlug slot on this SHPC controller. */
- for (i = 0; i < ctrl_p->numSlotsImpl; i++) {
- if (pcishpc_register_slot(ctrl_p, i) != DDI_SUCCESS) {
- pcishpc_debug("pcishpc_init() failed to register "
- "slot %d", i);
- (void) pcishpc_destroy_controller(dip);
- mutex_exit(&pcishpc_control_mutex);
- return (DDI_FAILURE);
- }
- }
-
- (void) pcishpc_enable_irqs(ctrl_p);
-
- if (pcishpc_debug_enabled) {
- /* Dump out the SHPC registers. */
- pcishpc_dump_regs(ctrl_p);
- }
-
- mutex_exit(&pcishpc_control_mutex);
-
- pcishpc_debug("pcishpc_init() success(dip=%p)", dip);
- return (DDI_SUCCESS);
-}
-
-
-/*
- * pcishpc_enable_irqs()
- *
- * Enable/unmask the different IRQ's we support from the SHPC controller.
- */
-static int
-pcishpc_enable_irqs(pcishpc_ctrl_t *ctrl_p)
-{
- uint32_t reg;
- int slot;
-
- reg = pcishpc_read_reg(ctrl_p, SHPC_CTRL_SERR_INT_REG);
-
- /* Enable all interrupts. */
- reg &= ~SHPC_SERR_INT_MASK_ALL;
-
- pcishpc_write_reg(ctrl_p, SHPC_CTRL_SERR_INT_REG, reg);
-
- /* Unmask the interrupts for each slot. */
- for (slot = 0; slot < ctrl_p->numSlotsImpl; slot++) {
- ctrl_p->slots[slot]->slot_events = 0;
-
- reg = pcishpc_read_reg(ctrl_p, SHPC_LOGICAL_SLOT_REGS+slot);
- if ((reg & SHPC_SLOT_STATE_MASK) == SHPC_SLOT_ENABLED) {
- reg &= ~(SHPC_SLOT_MASK_ALL | SHPC_SLOT_MRL_SERR_MASK);
- ctrl_p->numSlotsConn++;
- if (ctrl_p->currBusSpeed == -1)
- ctrl_p->currBusSpeed = pcishpc_read_reg(ctrl_p,
- SHPC_PROF_IF_SBCR_REG) &
- SHPC_SBCR_SPEED_MASK;
- } else {
- reg &= ~(SHPC_SLOT_MASK_ALL);
- }
-
- /* Enable/Unmask all slot interrupts. */
- pcishpc_write_reg(ctrl_p, SHPC_LOGICAL_SLOT_REGS+slot, reg);
- }
-
- pcishpc_debug("pcishpc_enable_irqs: ctrl_p 0x%p, "
- "current bus speed 0x%x, slots connected 0x%x\n", ctrl_p,
- ctrl_p->currBusSpeed, ctrl_p->numSlotsConn);
-
- return (DDI_SUCCESS);
-}
-
-
-/*
- * pcishpc_disable_irqs()
- *
- * Disable/Mask the different IRQ's we support from the SHPC controller.
- */
-static int
-pcishpc_disable_irqs(pcishpc_ctrl_t *ctrl_p)
-{
- uint32_t reg;
- int slot;
-
- reg = pcishpc_read_reg(ctrl_p, SHPC_CTRL_SERR_INT_REG);
-
- /* Mask all interrupts. */
- reg |= SHPC_SERR_INT_MASK_ALL;
-
- pcishpc_write_reg(ctrl_p, SHPC_CTRL_SERR_INT_REG, reg);
-
- /* Unmask the interrupts for each slot. */
- for (slot = 0; slot < ctrl_p->numSlotsImpl; slot++) {
- reg = pcishpc_read_reg(ctrl_p, SHPC_LOGICAL_SLOT_REGS+slot);
-
- /* Disable/Mask all slot interrupts. */
- reg |= SHPC_SLOT_MASK_ALL;
-
- pcishpc_write_reg(ctrl_p, SHPC_LOGICAL_SLOT_REGS+slot, reg);
- }
-
- pcishpc_debug("pcishpc_disable_irqs: ctrl_p 0x%p, "
- "current bus speed 0x%x, slots connected 0x%x\n", ctrl_p,
- ctrl_p->currBusSpeed, ctrl_p->numSlotsConn);
-
- return (DDI_SUCCESS);
-}
-
-
-/*
- * pcishpc_register_slot()
- *
- * Create and register a slot with the Solaris HotPlug framework.
- */
-static int
-pcishpc_register_slot(pcishpc_ctrl_t *ctrl_p, int slot)
-{
- pcishpc_t *pcishpc_p;
-
- pcishpc_p = pcishpc_create_slot(ctrl_p);
-
- ctrl_p->slots[slot] = pcishpc_p;
-
- pcishpc_p->slot_ops = hpc_alloc_slot_ops(KM_SLEEP);
-
- pcishpc_p->slot_ops->hpc_version = HPC_SLOT_OPS_VERSION;
-
- pcishpc_p->slotNum = slot;
-
- /* Setup the PCI device # for this SHPC slot. */
- if (ctrl_p->deviceIncreases)
- pcishpc_p->deviceNum = ctrl_p->deviceStart + pcishpc_p->slotNum;
- else
- pcishpc_p->deviceNum = ctrl_p->deviceStart - pcishpc_p->slotNum;
-
- /* Setup the HPS framework slot ops callbacks for the SHPC driver. */
- pcishpc_p->slot_ops->hpc_op_connect = pcishpc_connect;
- pcishpc_p->slot_ops->hpc_op_disconnect = pcishpc_disconnect;
- pcishpc_p->slot_ops->hpc_op_control = pcishpc_pci_control;
- /* PCI HPC drivers do not support the insert/remove callbacks. */
- pcishpc_p->slot_ops->hpc_op_insert = NULL;
- pcishpc_p->slot_ops->hpc_op_remove = NULL;
-
- /* Setup the HPS framework slot information. */
- pcishpc_p->slot_info.version = HPC_SLOT_OPS_VERSION;
- pcishpc_p->slot_info.slot_type = HPC_SLOT_TYPE_PCI;
- /* Do not auto enable the deivce in this slot. */
- pcishpc_p->slot_info.slot_flags = HPC_SLOT_NO_AUTO_ENABLE |
- HPC_SLOT_CREATE_DEVLINK;
-
- pcishpc_p->slot_info.slot.pci.device_number = pcishpc_p->deviceNum;
- pcishpc_p->slot_info.slot.pci.slot_capabilities = HPC_SLOT_64BITS;
-
- /* setup thread for handling ATTN button events */
- if (ctrl_p->has_attn) {
- pcishpc_debug("pcishpc_register_slot: "
- "setting up ATTN button event "
- "handler thread for slot %d\n", slot);
- cv_init(&pcishpc_p->attn_btn_cv, NULL, CV_DRIVER, NULL);
- pcishpc_p->attn_btn_pending = B_FALSE;
- pcishpc_p->attn_btn_threadp = thread_create(NULL, 0,
- pcishpc_attn_btn_handler,
- (void *)pcishpc_p, 0, &p0, TS_RUN, minclsyspri);
- pcishpc_p->attn_btn_thread_exit = B_FALSE;
- }
-
- /* setup the slot name (used for ap-id) */
- pcishpc_set_slot_name(ctrl_p, slot);
-
- pcishpc_get_slot_state(pcishpc_p);
-
- /* Register this SHPC slot with the HPS framework. */
- if (hpc_slot_register(ctrl_p->shpc_dip, ctrl_p->nexus_path,
- &pcishpc_p->slot_info, &pcishpc_p->slot_handle,
- pcishpc_p->slot_ops, (caddr_t)pcishpc_p, 0) != 0) {
-
- pcishpc_debug("pcishpc_register_slot() failed to Register "
- "slot");
-
- hpc_free_slot_ops(pcishpc_p->slot_ops);
- pcishpc_p->slot_ops = NULL;
-
- return (DDI_FAILURE);
- }
-
- pcishpc_debug("pcishpc_register_slot() success for slot %d", slot);
-
- return (DDI_SUCCESS);
-}
-
-
-/*
- * pcishpc_create_slot()
- *
- * Allocate and add a new HotPlug slot state structure to the linked list.
- */
-static pcishpc_t *
-pcishpc_create_slot(pcishpc_ctrl_t *ctrl_p)
-{
- pcishpc_t *pcishpc_p;
-
- pcishpc_debug("pcishpc_create_slot() called(ctrl_p=%x)", ctrl_p);
-
- /* Allocate a new slot structure. */
- pcishpc_p = kmem_zalloc(sizeof (pcishpc_t), KM_SLEEP);
-
- pcishpc_p->ctrl = ctrl_p;
-
- mutex_enter(&pcishpc_list_mutex);
-
- /* Insert new slot into linked list of current slots. */
- pcishpc_p->nextp = pcishpc_head;
- pcishpc_head = pcishpc_p;
-
- mutex_exit(&pcishpc_list_mutex);
-
- pcishpc_debug("pcishpc_create_slot() success");
- return (pcishpc_p);
-}
-
-/*
- * pcishpc_setup_controller()
- *
- * Get the number of HotPlug Slots, and the PCI device information
- * for this HotPlug controller.
- */
-static int
-pcishpc_setup_controller(pcishpc_ctrl_t *ctrl_p)
-{
- uint32_t config;
- dev_info_t *ppdip;
-
- config = pcishpc_read_reg(ctrl_p, SHPC_SLOT_CONFIGURATION_REG);
-
- /* Get the number of HotPlug slots implemented */
- ctrl_p->numSlotsImpl = ((config)&31);
-
- /*
- * Initilize the current bus speed and number of hotplug slots
- * currently connected.
- */
- ctrl_p->currBusSpeed = -1;
- ctrl_p->numSlotsConn = 0;
-
- /* Save the value of Slots Available 1 and 2 registers */
- ctrl_p->shpc_slots_avail1_reg = pcishpc_read_reg(ctrl_p,
- SHPC_SLOTS_AVAIL_I_REG);
- ctrl_p->shpc_slots_avail2_reg = pcishpc_read_reg(ctrl_p,
- SHPC_SLOTS_AVAIL_II_REG);
-
- /* Get the first PCI device Number used. */
- /*
- * PCI-X I/O boat workaround.
- * The register doesn't set up the correct value.
- */
- ppdip = ddi_get_parent(ddi_get_parent(ctrl_p->shpc_dip));
- if ((ddi_prop_get_int(DDI_DEV_T_ANY, ppdip, DDI_PROP_DONTPASS,
- "vendor-id", -1) == 0x108e) &&
- (ddi_prop_get_int(DDI_DEV_T_ANY, ppdip, DDI_PROP_DONTPASS,
- "device-id", -1) == 0x9010))
- ctrl_p->deviceStart = 4;
- else
- ctrl_p->deviceStart = ((config>>8)&31);
-
- /* Get the first Physical device number. */
- ctrl_p->physStart = ((config>>16)&0x7ff);
- /* Check if the device numbers increase or decrease. */
- ctrl_p->deviceIncreases = ((config>>29)&0x1);
-
- ctrl_p->has_attn =
- (config & SHPC_SLOT_CONFIG_ATTN_BUTTON) ? B_TRUE : B_FALSE;
- ctrl_p->has_mrl =
- (config & SHPC_SLOT_CONFIG_MRL_SENSOR) ? B_TRUE : B_FALSE;
-
- cv_init(&ctrl_p->cmd_comp_cv, NULL, CV_DRIVER, NULL);
- ctrl_p->command_complete = B_FALSE;
- ctrl_p->arbiter_timeout = B_FALSE;
-
- if (ctrl_p->numSlotsImpl > MAX_SHPC_SLOTS) {
- pcishpc_debug("pcishpc_setup_controller() too many SHPC "
- "slots error");
- return (DDI_FAILURE);
- }
-
- return (DDI_SUCCESS);
-}
-
-
-/*
- * pcishpc_uninit()
- * Unload the HogPlug controller driver and deallocate all resources.
- */
-int
-pcishpc_uninit(dev_info_t *dip)
-{
- pcishpc_ctrl_t *ctrl_p;
-
- pcishpc_debug("pcishpc_uninit() called(dip=%p)", dip);
-
- mutex_enter(&pcishpc_control_mutex);
-
- ctrl_p = pcishpc_get_controller(dip);
-
- if (!ctrl_p) {
- pcishpc_debug("pcishpc_uninit() Unable to find softstate");
- mutex_exit(&pcishpc_control_mutex);
- return (DDI_FAILURE);
- }
-
- (void) pcishpc_disable_irqs(ctrl_p);
- ctrl_p->interrupt_installed = B_FALSE;
-
- (void) pcishpc_destroy_controller(dip);
-
- mutex_exit(&pcishpc_control_mutex);
-
- pcishpc_debug("pcishpc_uninit() success(dip=%p)", dip);
-
- return (DDI_SUCCESS);
-}
-
-/*
- * pcishpc_destroy_slots()
- *
- * Free up all of the slot resources for this controller.
- */
-static int
-pcishpc_destroy_slots(pcishpc_ctrl_t *ctrl_p)
-{
- pcishpc_t *pcishpc_p;
- pcishpc_t **pcishpc_pp;
-
- pcishpc_debug("pcishpc_destroy_slots() called(ctrl_p=%p)", ctrl_p);
-
- pcishpc_pp = &pcishpc_head;
-
- while ((pcishpc_p = *pcishpc_pp) != NULL) {
- if (pcishpc_p->ctrl == ctrl_p) {
- if (pcishpc_p->attn_btn_threadp != NULL) {
- mutex_enter(&ctrl_p->shpc_mutex);
- pcishpc_p->attn_btn_thread_exit = B_TRUE;
- cv_signal(&pcishpc_p->attn_btn_cv);
- pcishpc_debug("pcishpc_destroy_slots: "
- "waiting for ATTN thread exit\n");
- cv_wait(&pcishpc_p->attn_btn_cv,
- &ctrl_p->shpc_mutex);
- pcishpc_debug("pcishpc_destroy_slots: "
- "ATTN thread exit\n");
- cv_destroy(&pcishpc_p->attn_btn_cv);
- pcishpc_p->attn_btn_threadp = NULL;
- mutex_exit(&ctrl_p->shpc_mutex);
- }
-
- *pcishpc_pp = pcishpc_p->nextp;
-
- pcishpc_debug("pcishpc_destroy_slots() (shpc_p=%p) "
- "destroyed", pcishpc_p);
- if (pcishpc_p->slot_ops)
- if (hpc_slot_unregister(
- &pcishpc_p->slot_handle) != 0) {
- pcishpc_debug("pcishpc_destroy_slots() "
- "failed to unregister slot");
- return (DDI_FAILURE);
- } else {
- hpc_free_slot_ops(pcishpc_p->slot_ops);
- pcishpc_p->slot_ops = NULL;
- }
- kmem_free(pcishpc_p, sizeof (pcishpc_t));
- } else
- pcishpc_pp = &(pcishpc_p->nextp);
- }
-
- return (DDI_SUCCESS);
-}
-
-
-/*
- * pcishpc_connect()
- *
- * Called by Hot Plug Services to connect a slot on the bus.
- */
-/*ARGSUSED*/
-static int
-pcishpc_connect(caddr_t ops_arg, hpc_slot_t slot_hdl, void *data, uint_t flags)
-{
- pcishpc_t *pcishpc_p;
- uint32_t status;
-
- pcishpc_debug("pcishpc_connect called()");
-
- pcishpc_p = pcishpc_hpc_get_slot_state(slot_hdl);
-
- if (!pcishpc_p) {
- pcishpc_debug("pcishpc_connect() "
- "Failed to find soft state for slot_hdl %x", slot_hdl);
- return (HPC_ERR_FAILED);
- }
-
- mutex_enter(&pcishpc_p->ctrl->shpc_mutex);
-
- /* make sure the MRL sensor is closed */
- status = pcishpc_read_reg(pcishpc_p->ctrl,
- SHPC_LOGICAL_SLOT_REGS+pcishpc_p->slotNum);
-
- if (status & SHPC_SLOT_MRL_STATE_MASK) {
- pcishpc_debug("pcishpc_connect() failed: MRL open");
- goto cleanup;
- }
-
- if (pcishpc_set_power_state(pcishpc_p, HPC_SLOT_CONNECTED) !=
- DDI_SUCCESS) {
- pcishpc_debug("pcishpc_connect() failed: set power state");
- goto cleanup;
- }
-
- mutex_exit(&pcishpc_p->ctrl->shpc_mutex);
-
- pcishpc_debug("pcishpc_connect() success!");
-
- return (HPC_SUCCESS);
-
-cleanup:
- mutex_exit(&pcishpc_p->ctrl->shpc_mutex);
- return (HPC_ERR_FAILED);
-}
-
-
-/*
- * pcishpc_set_power_state()
- *
- * Changed a slot's power state.
- */
-static int
-pcishpc_set_power_state(pcishpc_t *pcishpc_p, hpc_slot_state_t state)
-{
- pcishpc_get_slot_state(pcishpc_p);
-
- /* Check to see if the slot is already in this state. */
- if (pcishpc_p->slot_state == state) {
- pcishpc_debug("pcishpc_set_power_state() slot already in "
- "this state");
- return (DDI_SUCCESS);
- }
-
- if ((pcishpc_p->slot_state == HPC_SLOT_EMPTY) &&
- ((state == HPC_SLOT_CONNECTED) ||
- (state == HPC_SLOT_DISCONNECTED))) {
- pcishpc_debug("pcishpc_set_power_state() slot in "
- "empty state");
- return (DDI_FAILURE);
- }
-
- /* Set the Power LED to blink. */
- (void) pcishpc_setled(pcishpc_p, HPC_POWER_LED, HPC_LED_BLINK);
-
- /* Turn all other LEDS off. */
- (void) pcishpc_setled(pcishpc_p, HPC_FAULT_LED, HPC_LED_OFF);
- (void) pcishpc_setled(pcishpc_p, HPC_ATTN_LED, HPC_LED_OFF);
- (void) pcishpc_setled(pcishpc_p, HPC_ACTIVE_LED, HPC_LED_OFF);
-
- /* Set the slot state to the new slot state. */
- pcishpc_p->slot_state = state;
-
- /* Set the bus speed only if the bus segment is not running */
- if (state == HPC_SLOT_CONNECTED) {
- if (pcishpc_set_bus_speed(pcishpc_p) != DDI_SUCCESS)
- return (DDI_FAILURE);
-
- pcishpc_p->ctrl->numSlotsConn++;
- } else {
- if (--pcishpc_p->ctrl->numSlotsConn == 0)
- pcishpc_p->ctrl->currBusSpeed = -1;
- }
-
- pcishpc_debug("pcishpc_set_power_state(): ctrl_p 0x%p, "
- "pcishpc_p 0x%p, slot state 0x%x, current bus speed 0x%x, "
- "slots connected 0x%x\n", pcishpc_p->ctrl, pcishpc_p, state,
- pcishpc_p->ctrl->currBusSpeed, pcishpc_p->ctrl->numSlotsConn);
-
- /* Mask or Unmask MRL Sensor SEER bit based on new slot state */
- if (pcishpc_p->ctrl->has_mrl == B_TRUE) {
- uint32_t reg;
-
- reg = pcishpc_read_reg(pcishpc_p->ctrl,
- SHPC_LOGICAL_SLOT_REGS+pcishpc_p->slotNum);
- reg = (state == HPC_SLOT_CONNECTED) ?
- (reg & ~SHPC_SLOT_MRL_SERR_MASK) :
- (reg | SHPC_SLOT_MRL_SERR_MASK);
-
- pcishpc_write_reg(pcishpc_p->ctrl,
- SHPC_LOGICAL_SLOT_REGS+pcishpc_p->slotNum, reg);
- }
-
- /* Update the hardweare slot state. */
- if (pcishpc_set_slot_state(pcishpc_p) != DDI_SUCCESS) {
- pcishpc_debug("pcishpc_set_power_state() failed");
- (void) pcishpc_setled(pcishpc_p, HPC_POWER_LED, HPC_LED_OFF);
- pcishpc_get_slot_state(pcishpc_p);
- return (DDI_FAILURE);
- }
-
- /* Turn the Power LED ON for a connected slot. */
- if (state == HPC_SLOT_CONNECTED) {
- (void) pcishpc_setled(pcishpc_p, HPC_POWER_LED, HPC_LED_ON);
- }
-
- /* Turn the Power LED OFF for a disconnected slot. */
- if (state == HPC_SLOT_DISCONNECTED) {
- (void) pcishpc_setled(pcishpc_p, HPC_POWER_LED, HPC_LED_OFF);
- }
-
- /* Turn all other LEDS off. */
- (void) pcishpc_setled(pcishpc_p, HPC_FAULT_LED, HPC_LED_OFF);
- (void) pcishpc_setled(pcishpc_p, HPC_ATTN_LED, HPC_LED_OFF);
- (void) pcishpc_setled(pcishpc_p, HPC_ACTIVE_LED, HPC_LED_OFF);
-
- pcishpc_debug("pcishpc_set_power_state() success!");
-
- pcishpc_get_slot_state(pcishpc_p);
-
- /* delay after powerON to let the device initialize itself */
- delay(drv_usectohz(pcishpc_reset_delay));
-
- return (DDI_SUCCESS);
-}
-
-/*
- * pcishpc_set_bus_speed()
- *
- * Set the bus speed and mode.
- */
-static int
-pcishpc_set_bus_speed(pcishpc_t *pcishpc_p)
-{
- pcishpc_ctrl_t *ctrl_p = pcishpc_p->ctrl;
- int curr_speed = ctrl_p->currBusSpeed;
- int speed = -1;
- int avail_slots;
- uint32_t status;
-
- /* Make sure that the slot is in a correct state */
- status = pcishpc_read_reg(ctrl_p,
- SHPC_LOGICAL_SLOT_REGS+pcishpc_p->slotNum);
-
- /* Return failure if the slot is empty */
- if ((status & SHPC_SLOT_CARD_EMPTY_MASK) ==
- SHPC_SLOT_CARD_EMPTY_MASK) {
- pcishpc_debug("pcishpc_set_bus_speed() failed: "
- "the slot is empty.");
- return (DDI_FAILURE);
- }
-
- /* Return failure if the slot is not in disabled state */
- if ((status & SHPC_SLOT_STATE_MASK) != SHPC_SLOT_DISABLED) {
- pcishpc_debug("pcishpc_set_bus_speed() failed: "
- "incorrect slot state.");
- return (DDI_FAILURE);
- }
-
- /* Set the "power-only" mode for the slot */
- if (pcishpc_issue_command(ctrl_p, ((1+pcishpc_p->slotNum)<<8) |
- SHPC_SLOT_POWER_ONLY) != DDI_SUCCESS) {
- pcishpc_debug("pcishpc_set_bus_speed() failed to set "
- "the slot %d in the power-only mode", pcishpc_p->slotNum);
- return (DDI_FAILURE);
- }
-
- /* Wait for power good */
- delay(drv_usectohz(SHPC_POWER_GOOD_WAIT_TIME));
-
- /* Make sure that the slot is in "power-only" state */
- status = pcishpc_read_reg(ctrl_p,
- SHPC_LOGICAL_SLOT_REGS+pcishpc_p->slotNum);
-
- if ((status & SHPC_SLOT_STATE_MASK) != SHPC_SLOT_POWER_ONLY) {
- pcishpc_debug("pcishpc_set_bus_speed() "
- "power-only failed: incorrect slot state.");
- return (DDI_FAILURE);
- }
-
- /*
- * Check if SHPC has available slots and select the highest
- * available bus speed for the slot.
- *
- * The bus speed codes are:
- * 100 - 133Mhz; <--+
- * 011 - 100Mhz; <--+ PCI-X
- * 010 - 66Mhz; <--+
- *
- * 001 - 66Mhz; <--+
- * 000 - 33Mhz <--+ Conv PCI
- */
- switch (status & SHPC_SLOT_PCIX_CAPABLE_MASK) {
- case SHPC_SLOT_133MHZ_PCIX_CAPABLE:
- avail_slots = (ctrl_p->shpc_slots_avail1_reg >>
- SHPC_AVAIL_133MHZ_PCIX_SPEED_SHIFT) & SHPC_AVAIL_SPEED_MASK;
-
- if (((curr_speed == -1) && avail_slots) ||
- (curr_speed == SHPC_SBCR_133MHZ_PCIX_SPEED)) {
- speed = SHPC_SBCR_133MHZ_PCIX_SPEED;
- break;
- }
- /* FALLTHROUGH */
- case SHPC_SLOT_100MHZ_PCIX_CAPABLE:
- avail_slots = (ctrl_p->shpc_slots_avail1_reg >>
- SHPC_AVAIL_100MHZ_PCIX_SPEED_SHIFT) & SHPC_AVAIL_SPEED_MASK;
-
- if (((curr_speed == -1) && avail_slots) ||
- (curr_speed == SHPC_SBCR_100MHZ_PCIX_SPEED)) {
- speed = SHPC_SBCR_100MHZ_PCIX_SPEED;
- break;
- }
- /* FALLTHROUGH */
- case SHPC_SLOT_66MHZ_PCIX_CAPABLE:
- avail_slots = (ctrl_p->shpc_slots_avail1_reg >>
- SHPC_AVAIL_66MHZ_PCIX_SPEED_SHIFT) & SHPC_AVAIL_SPEED_MASK;
-
- if (((curr_speed == -1) && avail_slots) ||
- (curr_speed == SHPC_SBCR_66MHZ_PCIX_SPEED)) {
- speed = SHPC_SBCR_66MHZ_PCIX_SPEED;
- break;
- }
- /* FALLTHROUGH */
- default:
- avail_slots = (ctrl_p->shpc_slots_avail2_reg >>
- SHPC_AVAIL_66MHZ_CONV_SPEED_SHIFT) & SHPC_AVAIL_SPEED_MASK;
-
- if ((status & SHPC_SLOT_66MHZ_CONV_CAPABLE) &&
- (((curr_speed == -1) && avail_slots) ||
- (curr_speed == SHPC_SBCR_66MHZ_CONV_SPEED))) {
- speed = SHPC_SBCR_66MHZ_CONV_SPEED;
- } else {
- avail_slots = (ctrl_p->shpc_slots_avail1_reg >>
- SHPC_AVAIL_33MHZ_CONV_SPEED_SHIFT) &
- SHPC_AVAIL_SPEED_MASK;
-
- if (((curr_speed == -1) && (avail_slots)) ||
- (curr_speed == SHPC_SBCR_33MHZ_CONV_SPEED)) {
- speed = SHPC_SBCR_33MHZ_CONV_SPEED;
- } else {
- pcishpc_debug("pcishpc_set_bus_speed() "
- " failed to set the bus speed, slot# %d",
- pcishpc_p->slotNum);
- return (DDI_FAILURE);
- }
- }
- break;
- }
-
- /*
- * If the bus segment is already running, check to see the card
- * in the slot can support the current bus speed.
- */
- if (curr_speed == speed) {
- /*
- * Check to see there is any slot available for the current
- * bus speed. Otherwise, we need fail the current slot connect
- * request.
- */
- return ((avail_slots <= ctrl_p->numSlotsConn) ?
- DDI_FAILURE : DDI_SUCCESS);
- }
-
- /* Set the bus speed */
- if (pcishpc_issue_command(ctrl_p, SHPC_COMM_STS_SET_SPEED |
- speed) == DDI_FAILURE) {
- pcishpc_debug("pcishpc_set_bus_speed() failed "
- "to set bus %d speed", pcishpc_p->slotNum);
- return (DDI_FAILURE);
- }
-
- /* Check the current bus speed */
- status = pcishpc_read_reg(ctrl_p, SHPC_PROF_IF_SBCR_REG) &
- SHPC_SBCR_SPEED_MASK;
- if ((status & SHPC_SBCR_SPEED_MASK) != speed) {
- pcishpc_debug("pcishpc_set_bus_speed() an incorrect "
- "bus speed, slot = 0x%x, speed = 0x%x",
- pcishpc_p->slotNum, status & SHPC_SBCR_SPEED_MASK);
- return (DDI_FAILURE);
- }
-
-
- /* Save the current bus speed */
- ctrl_p->currBusSpeed = speed;
-
- return (DDI_SUCCESS);
-}
-
-/*
- * pcishpc_disconnect()
- *
- * Called by Hot Plug Services to disconnect a slot on the bus.
- */
-/*ARGSUSED*/
-static int
-pcishpc_disconnect(caddr_t ops_arg, hpc_slot_t slot_hdl, void *data,
- uint_t flags)
-{
- pcishpc_t *pcishpc_p;
-
- pcishpc_debug("pcishpc_disconnect called()");
-
- pcishpc_p = pcishpc_hpc_get_slot_state(slot_hdl);
-
- if (!pcishpc_p) {
- pcishpc_debug("pcishpc_disconnect() "
- "Failed to find soft state for slot_hdl %x", slot_hdl);
- return (HPC_ERR_FAILED);
- }
-
- mutex_enter(&pcishpc_p->ctrl->shpc_mutex);
-
- if (pcishpc_set_power_state(pcishpc_p, HPC_SLOT_DISCONNECTED)
- != DDI_SUCCESS) {
- pcishpc_debug("pcishpc_disconnect() failed");
- goto cleanup;
- }
-
- mutex_exit(&pcishpc_p->ctrl->shpc_mutex);
-
- pcishpc_debug("pcishpc_disconnect() success!");
-
- return (HPC_SUCCESS);
-
-cleanup:
- mutex_exit(&pcishpc_p->ctrl->shpc_mutex);
- return (HPC_ERR_FAILED);
-}
-
-
-/*
- * pcishpc_pci_control()
- *
- * Called by Hot Plug Services to perform a attachment point specific
- * operation on a Hot Pluggable Standard PCI Slot.
- */
-/*ARGSUSED*/
-static int
-pcishpc_pci_control(caddr_t ops_arg, hpc_slot_t slot_hdl, int request,
- caddr_t arg)
-{
- hpc_slot_state_t *hpc_slot_state;
- hpc_board_type_t *hpc_board_type;
- hpc_led_info_t *hpc_led_info;
- pcishpc_t *pcishpc_p;
- int ret = HPC_SUCCESS;
-
- pcishpc_debug("pcishpc_pci_control called(Request %s)",
- pcishpc_textrequest(request));
-
- pcishpc_p = pcishpc_hpc_get_slot_state(slot_hdl);
-
- if (!pcishpc_p) {
- pcishpc_debug("pcishpc_pci_control() Error: "
- "Failed to find soft state for slot_hdl %x", slot_hdl);
- return (HPC_ERR_FAILED);
- }
-
- mutex_enter(&pcishpc_p->ctrl->shpc_mutex);
-
- switch (request) {
- case HPC_CTRL_GET_SLOT_STATE:
- hpc_slot_state = (hpc_slot_state_t *)arg;
- pcishpc_get_slot_state(pcishpc_p);
- *hpc_slot_state = pcishpc_p->slot_state;
- pcishpc_debug("pcishpc_pci_control() - "
- "HPC_CTRL_GET_SLOT_STATE (state=%s)",
- pcishpc_textslotstate(pcishpc_p->slot_state));
- break;
-
- case HPC_CTRL_GET_BOARD_TYPE:
- hpc_board_type = (hpc_board_type_t *)arg;
- pcishpc_debug("pcishpc_pci_control() - "
- "HPC_CTRL_GET_BOARD_TYPE");
- pcishpc_get_slot_state(pcishpc_p);
- /*
- * The HPS framework does not know what board
- * type is plugged in.
- */
- if (pcishpc_p->slot_state == HPC_SLOT_EMPTY)
- *hpc_board_type = HPC_BOARD_UNKNOWN;
- else
- *hpc_board_type = HPC_BOARD_PCI_HOTPLUG;
- break;
-
- case HPC_CTRL_GET_LED_STATE:
- hpc_led_info = (hpc_led_info_t *)arg;
-
- pcishpc_get_slot_state(pcishpc_p);
-
- switch (hpc_led_info->led) {
- case HPC_FAULT_LED:
- hpc_led_info->state =
- pcishpc_p->fault_led_state;
- pcishpc_debug("pcishpc_pci_control() - "
- "GET_LED FAULT (state=%s)",
- pcishpc_textledstate(
- hpc_led_info->state));
- break;
-
- case HPC_POWER_LED:
- hpc_led_info->state =
- pcishpc_p->power_led_state;
- pcishpc_debug("pcishpc_pci_control() - "
- "GET_LED POWER (state=%s)",
- pcishpc_textledstate(
- hpc_led_info->state));
- break;
-
- case HPC_ATTN_LED:
- hpc_led_info->state =
- pcishpc_p->attn_led_state;
- pcishpc_debug("pcishpc_pci_control() - "
- "GET_LED ATTN(state = %s)",
- pcishpc_textledstate(
- hpc_led_info->state));
- break;
-
- case HPC_ACTIVE_LED:
- hpc_led_info->state =
- pcishpc_p->active_led_state;
- pcishpc_debug("pcishpc_pci_control() - "
- "GET_LED ACTIVE(state = %s)",
- pcishpc_textledstate(
- hpc_led_info->state));
- break;
-
- default:
- pcishpc_debug("pcishpc_pci_control() "
- "Error: GET_LED - "
- "Invalid LED %x",
- hpc_led_info->led);
- ret = HPC_ERR_NOTSUPPORTED;
- break;
- }
- break;
-
- case HPC_CTRL_SET_LED_STATE:
- hpc_led_info = (hpc_led_info_t *)arg;
- switch (hpc_led_info->led) {
- case HPC_ATTN_LED:
- (void) pcishpc_setled(pcishpc_p,
- hpc_led_info->led,
- hpc_led_info->state);
- break;
- case HPC_POWER_LED:
- pcishpc_debug("pcishpc_pci_control() "
- "Error: SET_LED - power LED");
- ret = HPC_ERR_NOTSUPPORTED;
- break;
- case HPC_FAULT_LED:
- case HPC_ACTIVE_LED:
- break;
- default:
- pcishpc_debug("pcishpc_pci_control() "
- "Error: SET_LED - Unknown LED %x",
- hpc_led_info->led);
- ret = HPC_ERR_NOTSUPPORTED;
- break;
- }
- break;
-
- case HPC_CTRL_DEV_UNCONFIG_FAILURE:
- case HPC_CTRL_DEV_CONFIG_FAILURE:
- pcishpc_debug("pcishpc_pci_control() Config/Unconfig "
- "failed.");
- (void) pcishpc_setled(pcishpc_p, HPC_ATTN_LED,
- HPC_LED_BLINK);
- break;
-
- case HPC_CTRL_ENABLE_AUTOCFG:
- case HPC_CTRL_DISABLE_AUTOCFG:
- case HPC_CTRL_DISABLE_SLOT:
- case HPC_CTRL_DEV_UNCONFIGURED:
- case HPC_CTRL_ENABLE_SLOT:
- case HPC_CTRL_DISABLE_ENUM:
- case HPC_CTRL_DEV_UNCONFIG_START:
- case HPC_CTRL_DEV_CONFIG_START:
- case HPC_CTRL_DEV_CONFIGURED:
- pcishpc_debug("pcishpc_pci_control() - %s",
- pcishpc_textrequest(request));
- break;
-
- case HPC_CTRL_ENABLE_ENUM:
- default:
- pcishpc_debug("pcishpc_pci_control() - Error: "
- "request (%d) NOT SUPPORTED", request);
- ret = HPC_ERR_NOTSUPPORTED;
- break;
- }
-
- mutex_exit(&pcishpc_p->ctrl->shpc_mutex);
- return (ret);
-}
-
-
-/*
- * pcishpc_setled()
- *
- * Change the state of a slot's LED.
- */
-static int
-pcishpc_setled(pcishpc_t *pcishpc_p, hpc_led_t led, hpc_led_state_t state)
-{
- switch (led) {
- case HPC_FAULT_LED:
- pcishpc_debug("pcishpc_setled() - HPC_FAULT_LED "
- "(set %s)", pcishpc_textledstate(state));
- pcishpc_p->fault_led_state = state;
- break;
-
- case HPC_POWER_LED:
- pcishpc_debug("pcishpc_setled() - HPC_POWER_LED "
- "(set %s)", pcishpc_textledstate(state));
- pcishpc_p->power_led_state = state;
- break;
-
- case HPC_ATTN_LED:
- pcishpc_debug("pcishpc_setled() - HPC_ATTN_LED "
- "(set %s)", pcishpc_textledstate(state));
- pcishpc_p->attn_led_state = state;
- break;
-
- case HPC_ACTIVE_LED:
- pcishpc_debug("pcishpc_setled() - HPC_ACTIVE_LED "
- "(set %s)", pcishpc_textledstate(state));
- pcishpc_p->active_led_state = state;
- break;
- }
-
- return (pcishpc_set_slot_state(pcishpc_p));
-}
-
-
-/*
- * pcishpc_set_slot_state()
- *
- * Updates the slot's state and leds.
- */
-static int
-pcishpc_set_slot_state(pcishpc_t *pcishpc_p)
-{
- uint32_t reg;
- uint32_t cmd_code;
- hpc_slot_state_t slot_state;
-
- reg = pcishpc_read_reg(pcishpc_p->ctrl,
- SHPC_LOGICAL_SLOT_REGS+pcishpc_p->slotNum);
-
- /* Default all states to unchanged. */
- cmd_code = ((1+pcishpc_p->slotNum)<<8);
-
- /* Has the slot state changed? */
- if ((reg & SHPC_SLOT_CARD_EMPTY_MASK) == SHPC_SLOT_CARD_EMPTY_MASK)
- slot_state = HPC_SLOT_EMPTY;
- else
- slot_state = pcishpc_slot_shpc_to_hpc(reg & 3);
- if (pcishpc_p->slot_state != slot_state) {
- pcishpc_debug("pcishpc_set_slot_state() Slot State changed");
- /* Set the new slot state in the Slot operation command. */
- cmd_code |= pcishpc_slot_hpc_to_shpc(pcishpc_p->slot_state);
- }
-
- /* Has the Power LED state changed? */
- if (pcishpc_p->power_led_state != pcishpc_led_shpc_to_hpc((reg>>2)&3)) {
- pcishpc_debug("pcishpc_set_slot_state() Power LED State "
- "changed");
- /* Set the new power led state in the Slot operation command. */
- cmd_code |=
- (pcishpc_led_hpc_to_shpc(pcishpc_p->power_led_state)
- << 2);
- }
-
- /* Has the Attn LED state changed? */
- if (pcishpc_p->attn_led_state != pcishpc_led_shpc_to_hpc((reg>>4)&3)) {
- pcishpc_debug("pcishpc_set_slot_state() Attn LED State "
- "changed");
- /* Set the new attn led state in the Slot operation command. */
- cmd_code |= (pcishpc_led_hpc_to_shpc(pcishpc_p->attn_led_state)
- << 4);
- }
-
- return (pcishpc_issue_command(pcishpc_p->ctrl, cmd_code));
-}
-
-
-/*
- * pcishpc_wait_busy()
- *
- * Wait until the SHPC controller is not busy.
- */
-static int
-pcishpc_wait_busy(pcishpc_ctrl_t *ctrl_p)
-{
- uint32_t status;
-
- /* Wait until SHPC controller is NOT busy */
- /*CONSTCOND*/
- while (1) {
- status = pcishpc_read_reg(ctrl_p, SHPC_COMMAND_STATUS_REG);
-
- /* Is there an MRL Sensor error? */
- if ((status & SHPC_COMM_STS_ERR_MASK) ==
- SHPC_COMM_STS_ERR_MRL_OPEN) {
- pcishpc_debug("pcishpc_wait_busy() ERROR: MRL Sensor "
- "error");
- break;
- }
-
- /* Is there an Invalid command error? */
- if ((status & SHPC_COMM_STS_ERR_MASK) ==
- SHPC_COMM_STS_ERR_INVALID_COMMAND) {
- pcishpc_debug("pcishpc_wait_busy() ERROR: Invalid "
- "command error");
- break;
- }
-
- /* Is there an Invalid Speed/Mode error? */
- if ((status & SHPC_COMM_STS_ERR_MASK) ==
- SHPC_COMM_STS_ERR_INVALID_SPEED) {
- pcishpc_debug("pcishpc_wait_busy() ERROR: Invalid "
- "Speed/Mode error");
- break;
- }
-
- /* Is the SHPC controller not BUSY? */
- if (!(status & SHPC_COMM_STS_CTRL_BUSY)) {
- /* Return Success. */
- return (DDI_SUCCESS);
- }
-
- pcishpc_debug("pcishpc_wait_busy() SHPC controller busy. "
- "Waiting");
-
- /* Wait before polling the status register again. */
- delay(drv_usectohz(SHPC_COMMAND_WAIT_TIME));
- }
-
- return (DDI_FAILURE);
-}
-
-
-/*
- * pcishpc_issue_command()
- *
- * Sends a command to the SHPC controller.
- */
-static int
-pcishpc_issue_command(pcishpc_ctrl_t *ctrl_p, uint32_t cmd_code)
-{
- int retCode;
-
- pcishpc_debug("pcishpc_issue_command() cmd_code=%02x", cmd_code);
-
- mutex_enter(&ctrl_p->shpc_intr_mutex);
-
- ctrl_p->command_complete = B_FALSE;
-
- /* Write the command to the SHPC controller. */
- pcishpc_write_reg(ctrl_p, SHPC_COMMAND_STATUS_REG, cmd_code);
-
- while (ctrl_p->command_complete == B_FALSE)
- cv_wait(&ctrl_p->cmd_comp_cv, &ctrl_p->shpc_intr_mutex);
-
- /* Wait until the SHPC controller processes the command. */
- retCode = pcishpc_wait_busy(ctrl_p);
-
- /* Make sure the command completed. */
- if (retCode == DDI_SUCCESS) {
- /* Did the command fail to generate the command complete IRQ? */
- if (ctrl_p->command_complete != B_TRUE) {
- pcishpc_debug("pcishpc_issue_command() Failed on "
- "generate cmd complete IRQ");
- retCode = DDI_FAILURE;
- }
- }
-
- mutex_exit(&ctrl_p->shpc_intr_mutex);
-
- if (retCode == DDI_FAILURE)
- pcishpc_debug("pcishpc_issue_command() Failed on cmd_code=%02x",
- cmd_code);
- else
- pcishpc_debug("pcishpc_issue_command() Success on "
- "cmd_code=%02x", cmd_code);
-
- return (retCode);
-}
-
-/*
- * pcishpc_led_shpc_to_hpc()
- *
- * Convert from SHPC indicator status to HPC indicator status.
- */
-static int
-pcishpc_led_shpc_to_hpc(int state)
-{
- switch (state) {
- case 1: /* SHPC On bits b01 */
- return (HPC_LED_ON);
- case 2: /* SHPC Blink bits b10 */
- return (HPC_LED_BLINK);
- case 3: /* SHPC Off bits b11 */
- return (HPC_LED_OFF);
- }
-
- return (HPC_LED_OFF);
-}
-
-
-/*
- * pcishpc_led_hpc_to_shpc()
- *
- * Convert from HPC indicator status to SHPC indicator status.
- */
-static int
-pcishpc_led_hpc_to_shpc(int state)
-{
- switch (state) {
- case HPC_LED_ON:
- return (1); /* SHPC On bits b01 */
- case HPC_LED_BLINK:
- return (2); /* SHPC Blink bits b10 */
- case HPC_LED_OFF:
- return (3); /* SHPC Off bits b11 */
- }
-
- return (3); /* SHPC Off bits b11 */
-}
-
-/*
- * pcishpc_slot_shpc_to_hpc()
- *
- * Convert from SHPC slot state to HPC slot state.
- */
-static int
-pcishpc_slot_shpc_to_hpc(int state)
-{
- switch (state) {
- case 0: /* SHPC Reserved */
- return (HPC_SLOT_EMPTY);
-
- case 1: /* SHPC Powered Only */
- return (HPC_SLOT_UNKNOWN);
-
- case 2: /* SHPC Enabled */
- return (HPC_SLOT_CONNECTED);
-
- case 3: /* SHPC Disabled */
- return (HPC_SLOT_DISCONNECTED);
- }
-
- /* Unknown slot state. */
- return (HPC_SLOT_UNKNOWN);
-}
-
-
-/*
- * pcishpc_slot_hpc_to_shpc()
- *
- * Convert from HPC slot state to SHPC slot state.
- */
-static int
-pcishpc_slot_hpc_to_shpc(int state)
-{
- switch (state) {
- case HPC_SLOT_EMPTY:
- return (0); /* SHPC Reserved */
-
- case HPC_SLOT_UNKNOWN:
- return (1); /* SHPC Powered Only */
-
- case HPC_SLOT_CONNECTED:
- return (2); /* SHPC Enabled */
-
- case HPC_SLOT_DISCONNECTED:
- return (3); /* SHPC Disabled */
- }
-
- /* Known slot state is reserved. */
- return (0);
-}
-
-
-/*
- * pcishpc_get_slot_state()
- *
- * Get the state of the slot.
- */
-static void
-pcishpc_get_slot_state(pcishpc_t *pcishpc_p)
-{
- uint32_t reg;
-
- /* Read the logical slot register for this Slot. */
- reg = pcishpc_read_reg(pcishpc_p->ctrl,
- SHPC_LOGICAL_SLOT_REGS+pcishpc_p->slotNum);
-
- /* Convert from the SHPC slot state to the HPC slot state. */
- if ((reg & SHPC_SLOT_CARD_EMPTY_MASK) == SHPC_SLOT_CARD_EMPTY_MASK)
- pcishpc_p->slot_state = HPC_SLOT_EMPTY;
- else
- pcishpc_p->slot_state = pcishpc_slot_shpc_to_hpc(reg & 3);
-
- /* Convert from the SHPC Power LED state to the HPC Power LED state. */
- pcishpc_p->power_led_state = pcishpc_led_shpc_to_hpc((reg>>2)&3);
-
- /* Convert from the SHPC Attn LED state to the HPC Attn LED state. */
- pcishpc_p->attn_led_state = pcishpc_led_shpc_to_hpc((reg>>4)&3);
-
- /* We don't have a fault LED so just default it to OFF. */
- pcishpc_p->fault_led_state = HPC_LED_OFF;
-
- /* We don't have an active LED so just default it to OFF. */
- pcishpc_p->active_led_state = HPC_LED_OFF;
-}
-
-/*
- * pcishpc_textledstate()
- *
- * Convert the led state into a text message.
- */
-static char *
-pcishpc_textledstate(hpc_led_state_t state)
-{
- /* Convert an HPC led state into a textual string. */
- switch (state) {
- case HPC_LED_OFF:
- return ("off");
-
- case HPC_LED_ON:
- return ("on");
-
- case HPC_LED_BLINK:
- return ("blink");
- }
- return ("unknown");
-}
-
-/*
- * pcishpc_textrequest()
- *
- * Convert the request into a text message.
- */
-static char *
-pcishpc_textrequest(int request)
-{
- /* Convert an HPC request into a textual string. */
- switch (request) {
- case HPC_CTRL_GET_LED_STATE:
- return ("HPC_CTRL_GET_LED_STATE");
- case HPC_CTRL_SET_LED_STATE:
- return ("HPC_CTRL_SET_LED_STATE");
- case HPC_CTRL_GET_SLOT_STATE:
- return ("HPC_CTRL_GET_SLOT_STATE");
- case HPC_CTRL_DEV_CONFIGURED:
- return ("HPC_CTRL_DEV_CONFIGURED");
- case HPC_CTRL_DEV_UNCONFIGURED:
- return ("HPC_CTRL_DEV_UNCONFIGURED");
- case HPC_CTRL_GET_BOARD_TYPE:
- return ("HPC_CTRL_GET_BOARD_TYPE");
- case HPC_CTRL_DISABLE_AUTOCFG:
- return ("HPC_CTRL_DISABLE_AUTOCFG");
- case HPC_CTRL_ENABLE_AUTOCFG:
- return ("HPC_CTRL_ENABLE_AUTOCFG");
- case HPC_CTRL_DISABLE_SLOT:
- return ("HPC_CTRL_DISABLE_SLOT");
- case HPC_CTRL_ENABLE_SLOT:
- return ("HPC_CTRL_ENABLE_SLOT");
- case HPC_CTRL_DISABLE_ENUM:
- return ("HPC_CTRL_DISABLE_ENUM");
- case HPC_CTRL_ENABLE_ENUM:
- return ("HPC_CTRL_ENABLE_ENUM");
- case HPC_CTRL_DEV_CONFIG_FAILURE:
- return ("HPC_CTRL_DEV_CONFIG_FAILURE");
- case HPC_CTRL_DEV_UNCONFIG_FAILURE:
- return ("HPC_CTRL_DEV_UNCONFIG_FAILURE");
- case HPC_CTRL_DEV_CONFIG_START:
- return ("HPC_CTRL_DEV_CONFIG_START");
- case HPC_CTRL_DEV_UNCONFIG_START:
- return ("HPC_CTRL_DEV_UNCONFIG_START");
- }
- return ("Unknown");
-}
-
-/*
- * pcishpc_textslotstate()
- *
- * Convert the request into a text message.
- */
-static char *
-pcishpc_textslotstate(hpc_slot_state_t state)
-{
- /* Convert an HPC slot state into a textual string. */
- switch (state) {
- case HPC_SLOT_EMPTY:
- return ("HPC_SLOT_EMPTY");
- case HPC_SLOT_DISCONNECTED:
- return ("HPC_SLOT_DISCONNECTED");
- case HPC_SLOT_CONNECTED:
- return ("HPC_SLOT_CONNECTED");
- case HPC_SLOT_UNKNOWN:
- return ("HPC_SLOT_UNKNOWN");
- }
- return ("Unknown");
-}
-
-
-/*
- * pcishpc_write_reg()
- *
- * Write to a SHPC controller register.
- */
-static void
-pcishpc_write_reg(pcishpc_ctrl_t *ctrl_p, int reg, uint32_t data)
-{
- /* Setup the SHPC dword select register. */
- pci_config_put8(ctrl_p->shpc_config_hdl,
- ctrl_p->shpc_dword_select, (uint8_t)reg);
-
- /* Read back the SHPC dword select register and verify. */
- if (pci_config_get8(ctrl_p->shpc_config_hdl,
- ctrl_p->shpc_dword_select) != (uint8_t)reg) {
- pcishpc_debug("pcishpc_write_reg() - Failed writing "
- "DWORD select reg");
- return;
- }
-
- /* Write to the SHPC dword data register. */
- pci_config_put32(ctrl_p->shpc_config_hdl,
- ctrl_p->shpc_dword_data_reg, data);
-
- /*
- * Issue a read of the VendorID/DeviceID just to force the previous
- * write to complete. This is probably not necessary, but it does
- * help enforce ordering if there is an issue.
- */
- (void) pci_config_get16(ctrl_p->shpc_config_hdl, PCI_CONF_VENID);
-}
-
-
-/*
- * pcishpc_read_reg()
- *
- * Read from a SHPC controller register.
- */
-static uint32_t
-pcishpc_read_reg(pcishpc_ctrl_t *ctrl_p, int reg)
-{
- /* Setup the SHPC dword select register. */
- pci_config_put8(ctrl_p->shpc_config_hdl,
- ctrl_p->shpc_dword_select, (uint8_t)reg);
-
- /* Read back the SHPC dword select register and verify. */
- if (pci_config_get8(ctrl_p->shpc_config_hdl,
- ctrl_p->shpc_dword_select) != (uint8_t)reg) {
- pcishpc_debug("pcishpc_read_reg() - Failed writing DWORD "
- "select reg");
- return (0xFFFFFFFF);
- }
-
- /* Read from the SHPC dword data register. */
- return (pci_config_get32(ctrl_p->shpc_config_hdl,
- ctrl_p->shpc_dword_data_reg));
-}
-
-
-/*
- * pcishpc_debug()
- *
- * Controls debug output if enabled.
- */
-static void
-pcishpc_debug(char *fmt, ...)
-{
- va_list ap;
-
- if (pcishpc_debug_enabled) {
- va_start(ap, fmt);
- vcmn_err(CE_WARN, fmt, ap);
- va_end(ap);
- }
-}
-
-
-/*
- * pcishpc_dump_regs()
- *
- * Dumps all of the SHPC controller registers.
- */
-static void
-pcishpc_dump_regs(pcishpc_ctrl_t *ctrl_p)
-{
- int slot, numSlots;
- uint32_t reg;
- char *state;
-
- cmn_err(CE_WARN, "pcishpc_dump_regs() called:");
- cmn_err(CE_WARN, "================================================"
- "==========");
-
- cmn_err(CE_WARN, "SHPC Base Offset "
- ": 0x%08x", pcishpc_read_reg(ctrl_p, SHPC_BASE_OFFSET_REG));
-
- reg = pcishpc_read_reg(ctrl_p, SHPC_SLOTS_AVAIL_I_REG);
-
- cmn_err(CE_WARN, "Number of PCIX slots avail (33 Mhz) : %d",
- (reg & 31));
-
- cmn_err(CE_WARN, "Number of PCIX slots avail (66 Mhz) : %d",
- ((reg>>8) & 31));
-
- cmn_err(CE_WARN, "Number of PCIX slots avail (100 Mhz) : %d",
- ((reg>>16) & 31));
-
- cmn_err(CE_WARN, "Number of PCIX slots avail (133 Mhz) : %d",
- ((reg>>24) & 31));
-
- reg = pcishpc_read_reg(ctrl_p, SHPC_SLOTS_AVAIL_II_REG);
-
- cmn_err(CE_WARN, "Number of conventional PCI slots (66 Mhz) : %d",
- (reg & 31));
-
- reg = pcishpc_read_reg(ctrl_p, SHPC_SLOT_CONFIGURATION_REG);
-
- numSlots = (reg & 31);
-
- cmn_err(CE_WARN, "Number of Slots connected to this port : %d",
- numSlots);
-
- cmn_err(CE_WARN, "PCI Device # for First HotPlug Slot : %d",
- ((reg>>8) & 31));
-
- cmn_err(CE_WARN, "Physical Slot # for First PCI Device # : %d",
- ((reg>>16) & 0x7ff));
-
- cmn_err(CE_WARN, "Physical Slot Number Up/Down "
- ": %d", ((reg>>29) & 0x1));
-
- cmn_err(CE_WARN, "MRL Sensor Implemented "
- ": %s", (reg & SHPC_SLOT_CONFIG_MRL_SENSOR) ? "Yes" :
- "No");
-
- cmn_err(CE_WARN, "Attention Button Implemented "
- ": %s", (reg & SHPC_SLOT_CONFIG_ATTN_BUTTON) ? "Yes" :
- "No");
-
- reg = pcishpc_read_reg(ctrl_p, SHPC_PROF_IF_SBCR_REG);
-
- switch (reg & 7) {
- case 0:
- state = "33Mhz Conventional PCI";
- break;
- case 1:
- state = "66Mhz Conventional PCI";
- break;
- case 2:
- state = "66Mhz PCI-X";
- break;
- case 3:
- state = "100Mhz PCI-X";
- break;
- case 4:
- state = "133Mhz PCI-X";
- break;
- default:
- state = "Reserved (Error)";
- break;
- }
-
- cmn_err(CE_WARN, "Current Port Operation Mode "
- ": %s", state);
-
- cmn_err(CE_WARN, "SHPC Interrupt Message Number "
- ": %d", ((reg>>16) &31));
-
- cmn_err(CE_WARN, "SHPC Programming Interface "
- ": %d", ((reg>>24) & 0xff));
-
- reg = pcishpc_read_reg(ctrl_p, SHPC_COMMAND_STATUS_REG);
-
- cmn_err(CE_WARN, "SHPC Command Code "
- ": %d", (reg & 0xff));
-
- cmn_err(CE_WARN, "SHPC Target Slot "
- ": %d", ((reg>>8) & 31));
-
- cmn_err(CE_WARN, "SHPC Controller Busy "
- ": %s", ((reg>>16) & 1) ? "Yes" : "No");
-
- cmn_err(CE_WARN, "SHPC Controller Err: MRL Sensor "
- ": %s", ((reg>>17) & 1) ? "Yes" : "No");
-
- cmn_err(CE_WARN, "SHPC Controller Err: Invalid Command : %s",
- ((reg>>18) & 1) ? "Yes" : "No");
-
- cmn_err(CE_WARN, "SHPC Controller Err: Invalid Speed/Mode : %s",
- ((reg>>19) & 1) ? "Yes" : "No");
-
- reg = pcishpc_read_reg(ctrl_p, SHPC_IRQ_LOCATOR_REG);
-
- cmn_err(CE_WARN, "Command Completion Interrupt Pending : %s",
- (reg & SHPC_IRQ_CMD_COMPLETE) ? "Yes" : "No");
-
- for (slot = 0; slot < numSlots; slot++) {
- cmn_err(CE_WARN, "Slot %d Interrupt Pending "
- ": %s", slot+1,
- (reg & (SHPC_IRQ_SLOT_N_PENDING<<slot)) ? "Yes" : "No");
- }
-
- reg = pcishpc_read_reg(ctrl_p, SHPC_SERR_LOCATOR_REG);
-
- cmn_err(CE_WARN, "Arbiter SERR Pending "
- ": %s", (reg & SHPC_IRQ_SERR_ARBITER_PENDING) ?
- "Yes" : "No");
-
- for (slot = 0; slot < numSlots; slot++) {
- cmn_err(CE_WARN, "Slot %d SERR Pending "
- ": %s", slot+1, (reg &
- (SHPC_IRQ_SERR_SLOT_N_PENDING<<slot)) ?
- "Yes" : "No");
- }
-
- reg = pcishpc_read_reg(ctrl_p, SHPC_CTRL_SERR_INT_REG);
-
- cmn_err(CE_WARN, "Global Interrupt Mask "
- ": %s", (reg & SHPC_SERR_INT_GLOBAL_IRQ_MASK) ?
- "Yes" : "No");
-
- cmn_err(CE_WARN, "Global SERR Mask "
- ": %s", (reg & SHPC_SERR_INT_GLOBAL_SERR_MASK) ?
- "Yes" : "No");
-
- cmn_err(CE_WARN, "Command Completion Interrupt Mask "
- ": %s", (reg & SHPC_SERR_INT_CMD_COMPLETE_MASK) ?
- "Yes" : "No");
-
- cmn_err(CE_WARN, "Arbiter SERR Mask "
- ": %s", (reg & SHPC_SERR_INT_ARBITER_SERR_MASK) ?
- "Yes" : "No");
-
- cmn_err(CE_WARN, "Command Completion Detected "
- ": %s", (reg & SHPC_SERR_INT_CMD_COMPLETE_IRQ) ?
- "Yes" : "No");
-
- cmn_err(CE_WARN, "Arbiter Timeout Detected "
- ": %s", (reg & SHPC_SERR_INT_ARBITER_IRQ) ?
- "Yes" : "No");
-
-
- for (slot = 0; slot < numSlots; slot++) {
- cmn_err(CE_WARN, "Logical Slot %d Registers:", slot+1);
- cmn_err(CE_WARN, "------------------------------------");
-
- reg = pcishpc_read_reg(ctrl_p, SHPC_LOGICAL_SLOT_REGS+slot);
-
- cmn_err(CE_WARN, "Slot %d state "
- ": %s", slot+1,
- pcishpc_textslotstate(pcishpc_slot_shpc_to_hpc(
- (reg & 3))));
-
- cmn_err(CE_WARN, "Slot %d Power Indicator State "
- ": %s", slot+1,
- pcishpc_textledstate(pcishpc_led_shpc_to_hpc(
- (reg>>2) &3)));
-
- cmn_err(CE_WARN, "Slot %d Attention Indicator State "
- ": %s", slot+1,
- pcishpc_textledstate(pcishpc_led_shpc_to_hpc(
- (reg>>4)&3)));
-
- cmn_err(CE_WARN, "Slot %d Power Fault "
- ": %s", slot+1, ((reg>>6)&1) ? "Fault Detected" :
- "No Fault");
- cmn_err(CE_WARN, "Slot %d Attention Button "
- ": %s", slot+1, ((reg>>7)&1) ? "Depressed" :
- "Not Depressed");
- cmn_err(CE_WARN, "Slot %d MRL Sensor "
- ": %s", slot+1, ((reg>>8)&1) ? "Not Closed" :
- "Closed");
- cmn_err(CE_WARN, "Slot %d 66mhz Capable "
- ": %s", slot+1, ((reg>>9)&1) ? "66mhz" : "33mgz");
-
- switch ((reg>>10)&3) {
- case 0:
- state = "Card Present 7.5W";
- break;
- case 1:
- state = "Card Present 15W";
- break;
- case 2:
- state = "Card Present 25W";
- break;
- case 3:
- state = "Slot Empty";
- break;
- }
-
- cmn_err(CE_WARN, "Slot %d PRSNT1#/PRSNT2# "
- ": %s", slot+1, state);
-
- switch ((reg>>12)&3) {
- case 0:
- state = "Non PCI-X";
- break;
- case 1:
- state = "66mhz PCI-X";
- break;
- case 2:
- state = "Reserved";
- break;
- case 3:
- state = "133mhz PCI-X";
- break;
- }
-
- cmn_err(CE_WARN, "Slot %d Card Presence Change Detected : %s",
- slot+1, (reg & SHPC_SLOT_PRESENCE_DETECTED) ? "Yes" :
- "No");
- cmn_err(CE_WARN, "Slot %d Isolated Power Fault Detected : %s",
- slot+1, (reg & SHPC_SLOT_ISO_PWR_DETECTED) ? "Yes" :
- "No");
- cmn_err(CE_WARN, "Slot %d Attention Button Press Detected"
- ": %s", slot+1,
- (reg & SHPC_SLOT_ATTN_DETECTED) ? "Yes" : "No");
- cmn_err(CE_WARN, "Slot %d MRL Sensor Change Detected "
- ": %s", slot+1,
- (reg & SHPC_SLOT_MRL_DETECTED) ? "Yes" : "No");
- cmn_err(CE_WARN, "Slot %d Connected Power Fault Detected"
- ": %s", slot+1,
- (reg & SHPC_SLOT_POWER_DETECTED) ? "Yes" : "No");
-
- cmn_err(CE_WARN, "Slot %d Card Presence IRQ Masked "
- ": %s", slot+1,
- (reg & SHPC_SLOT_PRESENCE_MASK) ? "Yes" : "No");
- cmn_err(CE_WARN, "Slot %d Isolated Power Fault IRQ Masked"
- ": %s", slot+1,
- (reg & SHPC_SLOT_ISO_PWR_MASK) ? "Yes" : "No");
- cmn_err(CE_WARN, "Slot %d Attention Button IRQ Masked "
- ": %s", slot+1, (reg & SHPC_SLOT_ATTN_MASK) ? "Yes" :
- "No");
- cmn_err(CE_WARN, "Slot %d MRL Sensor IRQ Masked "
- ": %s", slot+1,
- (reg & SHPC_SLOT_MRL_MASK) ? "Yes" : "No");
- cmn_err(CE_WARN, "Slot %d Connected Power Fault IRQ Masked"
- " : %s", slot+1,
- (reg & SHPC_SLOT_POWER_MASK) ? "Yes" : "No");
- cmn_err(CE_WARN, "Slot %d MRL Sensor SERR Masked "
- ": %s", slot+1,
- (reg & SHPC_SLOT_MRL_SERR_MASK) ? "Yes" : "No");
- cmn_err(CE_WARN, "Slot %d Connected Power Fault SERR Masked :"
- "%s", slot+1,
- (reg & SHPC_SLOT_POWER_SERR_MASK) ? "Yes" : "No");
- }
-}
-
-static void
-pcishpc_attn_btn_handler(pcishpc_t *pcishpc_p)
-{
- hpc_led_state_t power_led_state;
- callb_cpr_t cprinfo;
-
- pcishpc_debug("pcishpc_attn_btn_handler: thread started\n");
-
- CALLB_CPR_INIT(&cprinfo, &pcishpc_p->ctrl->shpc_mutex,
- callb_generic_cpr, "pcishpc_attn_btn_handler");
-
- mutex_enter(&pcishpc_p->ctrl->shpc_mutex);
-
- /* wait for ATTN button event */
- cv_wait(&pcishpc_p->attn_btn_cv, &pcishpc_p->ctrl->shpc_mutex);
-
- while (pcishpc_p->attn_btn_thread_exit == B_FALSE) {
- if (pcishpc_p->attn_btn_pending == B_TRUE) {
- /* get the current state of power LED */
- power_led_state = pcishpc_p->power_led_state;
-
- /* Blink the Power LED while we wait for 5 seconds */
- (void) pcishpc_setled(pcishpc_p, HPC_POWER_LED,
- HPC_LED_BLINK);
-
- /* wait for 5 seconds before taking any action */
- if (cv_timedwait(&pcishpc_p->attn_btn_cv,
- &pcishpc_p->ctrl->shpc_mutex,
- ddi_get_lbolt() + SEC_TO_TICK(5)) == -1) {
- /*
- * It is a time out;
- * make sure the ATTN pending flag is
- * still ON before sending the event
- * to HPS framework.
- */
- if (pcishpc_p->attn_btn_pending == B_TRUE) {
- /*
- * send the ATTN button event
- * to HPS framework
- */
- pcishpc_p->attn_btn_pending = B_FALSE;
- (void) hpc_slot_event_notify(
- pcishpc_p->slot_handle,
- HPC_EVENT_SLOT_ATTN,
- HPC_EVENT_NORMAL);
- }
- }
-
- /* restore the power LED state ??? XXX */
- (void) pcishpc_setled(pcishpc_p, HPC_POWER_LED,
- power_led_state);
- continue;
- }
-
- /* wait for another ATTN button event */
- cv_wait(&pcishpc_p->attn_btn_cv, &pcishpc_p->ctrl->shpc_mutex);
- }
-
- pcishpc_debug("pcishpc_attn_btn_handler: thread exit\n");
- cv_signal(&pcishpc_p->attn_btn_cv);
- CALLB_CPR_EXIT(&cprinfo);
- thread_exit();
-}
-
-/*
- * setup slot name/slot-number info.
- */
-static void
-pcishpc_set_slot_name(pcishpc_ctrl_t *ctrl_p, int slot)
-{
- pcishpc_t *p = ctrl_p->slots[slot];
- uchar_t *slotname_data;
- int *slotnum;
- uint_t count;
- int len;
- uchar_t *s;
- uint32_t bit_mask;
- int pci_id_cnt, pci_id_bit;
- int slots_before, found;
- int invalid_slotnum = 0;
-
- if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, ctrl_p->shpc_dip,
- DDI_PROP_DONTPASS, "physical-slot#", &slotnum, &count) ==
- DDI_PROP_SUCCESS) {
- p->phy_slot_num = slotnum[0];
- ddi_prop_free(slotnum);
- } else {
- if (ctrl_p->deviceIncreases)
- p->phy_slot_num = ctrl_p->physStart + slot;
- else
- p->phy_slot_num = ctrl_p->physStart - slot;
-
- if ((ndi_prop_update_int(DDI_DEV_T_NONE, ctrl_p->shpc_dip,
- "physical-slot#", p->phy_slot_num)) != DDI_SUCCESS) {
- pcishpc_debug("pcishpc_set_slot_name(): failed to "
- "create phyical-slot#%d", p->phy_slot_num);
- }
- }
-
- if (!p->phy_slot_num) { /* platform may not have initialized it */
- p->phy_slot_num = pci_config_get8(ctrl_p->shpc_config_hdl,
- PCI_BCNF_SECBUS);
- invalid_slotnum = 1;
- }
-
- /*
- * construct the slot_name:
- * if "slot-names" property exists then use that name
- * else if valid slot number exists then it is "pci<slot-num>".
- * else it will be "pci<sec-bus-number>dev<dev-number>"
- */
- if (ddi_getlongprop(DDI_DEV_T_ANY, ctrl_p->shpc_dip, DDI_PROP_DONTPASS,
- "slot-names", (caddr_t)&slotname_data,
- &len) == DDI_PROP_SUCCESS) {
-
- bit_mask = slotname_data[3] | (slotname_data[2] << 8) |
- (slotname_data[1] << 16) | (slotname_data[0] << 24);
-
- pci_id_bit = 1;
- pci_id_cnt = slots_before = found = 0;
-
- /*
- * Walk the bit mask until we find the bit that corresponds
- * to our slots device number. We count how many bits
- * we find before we find our slot's bit.
- */
- while (!found && (pci_id_cnt < 32)) {
-
- while (p->deviceNum != pci_id_cnt) {
-
- /*
- * Find the next bit set.
- */
- while (!(bit_mask & pci_id_bit) &&
- (pci_id_cnt < 32)) {
- pci_id_bit = pci_id_bit << 1;
- pci_id_cnt++;
- }
-
- if (p->deviceNum != pci_id_cnt)
- slots_before++;
- else
- found = 1;
- }
- }
-
- if (pci_id_cnt < 32) {
-
- /*
- * Set ptr to first string.
- */
- s = slotname_data + 4;
-
- /*
- * Increment past all the strings for the slots
- * before ours.
- */
- while (slots_before) {
- while (*s != NULL)
- s++;
- s++;
- slots_before--;
- }
-
- (void) sprintf(p->slot_info.pci_slot_name, (char *)s);
-
- kmem_free(slotname_data, len);
- return;
- }
-
- /* slot-names entry not found */
- pcishpc_debug("pcishpc_set_slot_name(): "
- "No slot-names entry found for slot #%d",
- p->phy_slot_num);
- kmem_free(slotname_data, len);
- }
-
- if (invalid_slotnum)
- (void) sprintf(p->slot_info.pci_slot_name, "pci%d",
- p->deviceNum);
- else
- (void) sprintf(p->slot_info.pci_slot_name, "pci%d",
- p->phy_slot_num);
-}
diff --git a/usr/src/uts/common/io/pciex/hotplug/pcie_hp.c b/usr/src/uts/common/io/pciex/hotplug/pcie_hp.c
new file mode 100644
index 0000000000..55cc077a5b
--- /dev/null
+++ b/usr/src/uts/common/io/pciex/hotplug/pcie_hp.c
@@ -0,0 +1,1300 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * This file contains the common hotplug code that is used by Standard
+ * PCIe and PCI HotPlug Controller code.
+ *
+ * NOTE: This file is compiled and delivered through misc/pcie module.
+ */
+
+#include <sys/types.h>
+#include <sys/conf.h>
+#include <sys/kmem.h>
+#include <sys/debug.h>
+#include <sys/vtrace.h>
+#include <sys/autoconf.h>
+#include <sys/varargs.h>
+#include <sys/ddi_impldefs.h>
+#include <sys/time.h>
+#include <sys/note.h>
+#include <sys/callb.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/sunndi.h>
+#include <sys/sysevent.h>
+#include <sys/sysevent/eventdefs.h>
+#include <sys/sysevent/dr.h>
+#include <sys/pci_impl.h>
+#include <sys/pci_cap.h>
+#include <sys/hotplug/pci/pcicfg.h>
+#include <sys/hotplug/pci/pcie_hp.h>
+#include <sys/hotplug/pci/pciehpc.h>
+#include <sys/hotplug/pci/pcishpc.h>
+#include <io/pciex/pcieb.h>
+
+/* Local functions prototype */
+static int pcie_hp_list_occupants(dev_info_t *dip, void *arg);
+static int pcie_hp_register_port(dev_info_t *dip, dev_info_t *pdip,
+ char *cn_name);
+static int pcie_hp_register_ports_for_dev(dev_info_t *dip, int device_num);
+static int pcie_hp_unregister_ports_cb(ddi_hp_cn_info_t *info, void *arg);
+static int pcie_hp_get_port_state(ddi_hp_cn_info_t *info, void *arg);
+static int pcie_hp_match_dev_func(dev_info_t *dip, void *hdl);
+static boolean_t pcie_hp_match_dev(dev_info_t *dip, int dev_num);
+static int pcie_hp_get_df_from_port_name(char *cn_name, int *dev_num,
+ int *func_num);
+static int pcie_hp_create_port_name_num(dev_info_t *dip,
+ ddi_hp_cn_info_t *cn_info);
+static int pcie_hp_check_hardware_existence(dev_info_t *dip, int dev_num,
+ int func_num);
+
+/*
+ * Global functions (called by other drivers/modules)
+ */
+
+/*
+ * return description text for led state
+ */
+char *
+pcie_led_state_text(pcie_hp_led_state_t state)
+{
+ switch (state) {
+ case PCIE_HP_LED_ON:
+ return (PCIEHPC_PROP_VALUE_ON);
+ case PCIE_HP_LED_OFF:
+ return (PCIEHPC_PROP_VALUE_OFF);
+ case PCIE_HP_LED_BLINK:
+ default:
+ return (PCIEHPC_PROP_VALUE_BLINK);
+ }
+}
+
+/*
+ * return description text for slot condition
+ */
+char *
+pcie_slot_condition_text(ap_condition_t condition)
+{
+ switch (condition) {
+ case AP_COND_UNKNOWN:
+ return (PCIEHPC_PROP_VALUE_UNKNOWN);
+ case AP_COND_OK:
+ return (PCIEHPC_PROP_VALUE_OK);
+ case AP_COND_FAILING:
+ return (PCIEHPC_PROP_VALUE_FAILING);
+ case AP_COND_FAILED:
+ return (PCIEHPC_PROP_VALUE_FAILED);
+ case AP_COND_UNUSABLE:
+ return (PCIEHPC_PROP_VALUE_UNUSABLE);
+ default:
+ return (PCIEHPC_PROP_VALUE_UNKNOWN);
+ }
+}
+
+/*
+ * routine to copy in a nvlist from userland
+ */
+int
+pcie_copyin_nvlist(char *packed_buf, size_t packed_sz, nvlist_t **nvlp)
+{
+ int ret = DDI_SUCCESS;
+ char *packed;
+ nvlist_t *dest = NULL;
+
+ if (packed_buf == NULL || packed_sz == 0)
+ return (DDI_EINVAL);
+
+ /* copyin packed nvlist */
+ if ((packed = kmem_alloc(packed_sz, KM_SLEEP)) == NULL)
+ return (DDI_ENOMEM);
+
+ if (copyin(packed_buf, packed, packed_sz) != 0) {
+ cmn_err(CE_WARN, "pcie_copyin_nvlist: copyin failed.\n");
+ ret = DDI_FAILURE;
+ goto copyin_cleanup;
+ }
+
+ /* unpack packed nvlist */
+ if ((ret = nvlist_unpack(packed, packed_sz, &dest, KM_SLEEP)) != 0) {
+ cmn_err(CE_WARN, "pcie_copyin_nvlist: nvlist_unpack "
+ "failed with err %d\n", ret);
+ switch (ret) {
+ case EINVAL:
+ case ENOTSUP:
+ ret = DDI_EINVAL;
+ goto copyin_cleanup;
+ case ENOMEM:
+ ret = DDI_ENOMEM;
+ goto copyin_cleanup;
+ default:
+ ret = DDI_FAILURE;
+ goto copyin_cleanup;
+ }
+ }
+ *nvlp = dest;
+copyin_cleanup:
+ kmem_free(packed, packed_sz);
+ return (ret);
+}
+
+/*
+ * routine to copy out a nvlist to userland
+ */
+int
+pcie_copyout_nvlist(nvlist_t *nvl, char *packed_buf, size_t *buf_sz)
+{
+ int err = 0;
+ char *buf = NULL;
+ size_t packed_sz;
+
+ if (nvl == NULL || packed_buf == NULL || buf_sz == NULL)
+ return (DDI_EINVAL);
+
+ /* pack nvlist, the library will allocate memory */
+ if ((err = nvlist_pack(nvl, &buf, &packed_sz, NV_ENCODE_NATIVE, 0))
+ != 0) {
+ cmn_err(CE_WARN, "pcie_copyout_nvlist: nvlist_pack "
+ "failed with err %d\n", err);
+ switch (err) {
+ case EINVAL:
+ case ENOTSUP:
+ return (DDI_EINVAL);
+ case ENOMEM:
+ return (DDI_ENOMEM);
+ default:
+ return (DDI_FAILURE);
+ }
+ }
+ if (packed_sz > *buf_sz) {
+ return (DDI_EINVAL);
+ }
+
+ /* copyout packed nvlist */
+ if (copyout(buf, packed_buf, packed_sz) != 0) {
+ cmn_err(CE_WARN, "pcie_copyout_nvlist: copyout " "failed.\n");
+ kmem_free(buf, packed_sz);
+ return (DDI_FAILURE);
+ }
+
+ *buf_sz = packed_sz;
+ kmem_free(buf, packed_sz);
+ return (DDI_SUCCESS);
+}
+
+/*
+ * init bus_hp_op entry and init hotpluggable slots & virtual ports
+ */
+int
+pcie_hp_init(dev_info_t *dip, caddr_t arg)
+{
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
+ int ret = DDI_SUCCESS, count;
+ dev_info_t *cdip;
+
+ if (PCIE_IS_PCIE_HOTPLUG_CAPABLE(bus_p)) {
+ /* Init hotplug controller */
+ ret = pciehpc_init(dip, arg);
+ } else if (PCIE_IS_PCI_HOTPLUG_CAPABLE(bus_p)) {
+ ret = pcishpc_init(dip);
+ }
+
+ if (ret != DDI_SUCCESS) {
+ cmn_err(CE_WARN, "pcie_hp_init: initialize hotplug "
+ "controller failed with %d\n", ret);
+ return (ret);
+ }
+
+ ndi_devi_enter(dip, &count);
+
+ /* Create port for the first level children */
+ cdip = ddi_get_child(dip);
+ while (cdip != NULL) {
+ if ((ret = pcie_hp_register_port(cdip, dip, NULL))
+ != DDI_SUCCESS) {
+ /* stop and cleanup */
+ break;
+ }
+ cdip = ddi_get_next_sibling(cdip);
+ }
+ ndi_devi_exit(dip, count);
+ if (ret != DDI_SUCCESS) {
+ cmn_err(CE_WARN, "pcie_hp_init: initialize virtual "
+ "hotplug port failed with %d\n", ret);
+ (void) pcie_hp_uninit(dip);
+
+ return (ret);
+ }
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * uninit the hotpluggable slots and virtual ports
+ */
+int
+pcie_hp_uninit(dev_info_t *dip)
+{
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
+ pcie_hp_unreg_port_t arg;
+
+ /*
+ * Must set arg.rv to NDI_SUCCESS so that if there's no port
+ * under this dip, we still return success thus the bridge
+ * driver can be successfully detached.
+ *
+ * Note that during the probe PCI configurator calls
+ * ndi_devi_offline() to detach driver for a new probed bridge,
+ * so that it can reprogram the resources for the bridge,
+ * ndi_devi_offline() calls into pcieb_detach() which in turn
+ * calls into this function. In this case there are no ports
+ * created under a new probe bridge dip, as ports are only
+ * created after the configurator finishing probing, thus the
+ * ndi_hp_walk_cn() will see no ports when this is called
+ * from the PCI configurtor.
+ */
+ arg.nexus_dip = dip;
+ arg.connector_num = DDI_HP_CN_NUM_NONE;
+ arg.rv = NDI_SUCCESS;
+
+ /* tear down all virtual hotplug handles */
+ ndi_hp_walk_cn(dip, pcie_hp_unregister_ports_cb, &arg);
+
+ if (arg.rv != NDI_SUCCESS)
+ return (DDI_FAILURE);
+
+ if (PCIE_IS_PCIE_HOTPLUG_ENABLED(bus_p))
+ (void) pciehpc_uninit(dip);
+ else if (PCIE_IS_PCI_HOTPLUG_ENABLED(bus_p))
+ (void) pcishpc_uninit(dip);
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * interrupt handler
+ */
+int
+pcie_hp_intr(dev_info_t *dip)
+{
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
+ int ret = DDI_INTR_UNCLAIMED;
+
+ if (PCIE_IS_PCIE_HOTPLUG_ENABLED(bus_p))
+ ret = pciehpc_intr(dip);
+ else if (PCIE_IS_PCI_HOTPLUG_ENABLED(bus_p))
+ ret = pcishpc_intr(dip);
+
+ return (ret);
+}
+
+/*
+ * Probe the given PCIe/PCI Hotplug Connection (CN).
+ */
+/*ARGSUSED*/
+int
+pcie_hp_probe(pcie_hp_slot_t *slot_p)
+{
+ pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl;
+ dev_info_t *dip = ctrl_p->hc_dip;
+
+ /*
+ * Call the configurator to probe a given PCI hotplug
+ * Hotplug Connection (CN).
+ */
+ if (pcicfg_configure(dip, slot_p->hs_device_num, PCICFG_ALL_FUNC, 0)
+ != PCICFG_SUCCESS) {
+ PCIE_DBG("pcie_hp_probe() failed\n");
+ return (DDI_FAILURE);
+ }
+ slot_p->hs_condition = AP_COND_OK;
+ pcie_hp_create_occupant_props(dip, makedevice(ddi_driver_major(dip),
+ slot_p->hs_minor), slot_p->hs_device_num);
+
+ /*
+ * Create ports for the newly probed devices.
+ * Note, this is only for the first level children because the
+ * descendants' ports will be created during bridge driver attach.
+ */
+ return (pcie_hp_register_ports_for_dev(dip, slot_p->hs_device_num));
+}
+
+/*
+ * Unprobe the given PCIe/PCI Hotplug Connection (CN):
+ * 1. remove all child device nodes
+ * 2. unregister all dependent ports
+ */
+/*ARGSUSED*/
+int
+pcie_hp_unprobe(pcie_hp_slot_t *slot_p)
+{
+ pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl;
+ dev_info_t *dip = ctrl_p->hc_dip;
+ pcie_hp_unreg_port_t arg;
+
+ /*
+ * Call the configurator to unprobe a given PCI hotplug
+ * Hotplug Connection (CN).
+ */
+ if (pcicfg_unconfigure(dip, slot_p->hs_device_num, PCICFG_ALL_FUNC, 0)
+ != PCICFG_SUCCESS) {
+ PCIE_DBG("pcie_hp_unprobe() failed\n");
+ return (DDI_FAILURE);
+ }
+ slot_p->hs_condition = AP_COND_UNKNOWN;
+ pcie_hp_delete_occupant_props(dip, makedevice(ddi_driver_major(dip),
+ slot_p->hs_minor));
+
+ /*
+ * Remove ports for the unprobed devices.
+ * Note, this is only for the first level children because the
+ * descendants' ports were already removed during bridge driver dettach.
+ */
+ arg.nexus_dip = dip;
+ arg.connector_num = slot_p->hs_info.cn_num;
+ arg.rv = NDI_SUCCESS;
+ ndi_hp_walk_cn(dip, pcie_hp_unregister_ports_cb, &arg);
+
+ return (arg.rv == NDI_SUCCESS) ? (DDI_SUCCESS) : (DDI_FAILURE);
+}
+
+/* Read-only probe: no hardware register programming. */
+int
+pcie_read_only_probe(dev_info_t *dip, char *cn_name, dev_info_t **pcdip)
+{
+ long dev, func;
+ int ret;
+ char *sp;
+ dev_info_t *cdip;
+
+ *pcdip = NULL;
+ /*
+ * Parse the string of a pci Port name and get the device number
+ * and function number.
+ */
+ if (ddi_strtol(cn_name + 4, &sp, 10, &dev) != 0)
+ return (DDI_EINVAL);
+ if (ddi_strtol(sp + 1, NULL, 10, &func) != 0)
+ return (DDI_EINVAL);
+
+ ret = pcicfg_configure(dip, (int)dev, (int)func,
+ PCICFG_FLAG_READ_ONLY);
+ if (ret == PCICFG_SUCCESS) {
+ cdip = pcie_hp_devi_find(dip, (int)dev, (int)func);
+ *pcdip = cdip;
+ }
+ return (ret);
+}
+
+/* Read-only unprobe: no hardware register programming. */
+int
+pcie_read_only_unprobe(dev_info_t *dip, char *cn_name)
+{
+ long dev, func;
+ int ret;
+ char *sp;
+
+ /*
+ * Parse the string of a pci Port name and get the device number
+ * and function number.
+ */
+ if (ddi_strtol(cn_name + 4, &sp, 10, &dev) != 0)
+ return (DDI_EINVAL);
+ if (ddi_strtol(sp + 1, NULL, 10, &func) != 0)
+ return (DDI_EINVAL);
+
+ ret = pcicfg_unconfigure(dip, (int)dev, (int)func,
+ PCICFG_FLAG_READ_ONLY);
+
+ return (ret);
+}
+
+/* Control structure used to find a device in the devinfo tree */
+struct pcie_hp_find_ctrl {
+ uint_t device;
+ uint_t function;
+ dev_info_t *dip;
+};
+
+/*
+ * find a devinfo node with specified device and function number
+ * in the device tree under 'dip'
+ */
+dev_info_t *
+pcie_hp_devi_find(dev_info_t *dip, uint_t device, uint_t function)
+{
+ struct pcie_hp_find_ctrl ctrl;
+ int count;
+
+ ctrl.device = device;
+ ctrl.function = function;
+ ctrl.dip = NULL;
+
+ ndi_devi_enter(dip, &count);
+ ddi_walk_devs(ddi_get_child(dip), pcie_hp_match_dev_func,
+ (void *)&ctrl);
+ ndi_devi_exit(dip, count);
+
+ return (ctrl.dip);
+}
+
+/*
+ * routine to create 'pci-occupant' property for a hotplug slot
+ */
+void
+pcie_hp_create_occupant_props(dev_info_t *dip, dev_t dev, int pci_dev)
+{
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
+ pcie_hp_ctrl_t *ctrl_p = (pcie_hp_ctrl_t *)bus_p->bus_hp_ctrl;
+ pcie_hp_slot_t *slotp;
+ pcie_hp_cn_cfg_t cn_cfg;
+ pcie_hp_occupant_info_t *occupant;
+ int circular, i;
+
+ ndi_devi_enter(dip, &circular);
+
+ if (PCIE_IS_PCIE_HOTPLUG_ENABLED(bus_p)) {
+ slotp = (ctrl_p && (pci_dev == 0)) ?
+ ctrl_p->hc_slots[pci_dev] : NULL;
+ } else if (PCIE_IS_PCI_HOTPLUG_ENABLED(bus_p)) {
+ if (ctrl_p) {
+ int slot_num;
+
+ slot_num = (ctrl_p->hc_device_increases) ?
+ (pci_dev - ctrl_p->hc_device_start) :
+ (pci_dev + ctrl_p->hc_device_start);
+
+ slotp = ctrl_p->hc_slots[slot_num];
+ } else {
+ slotp = NULL;
+ }
+ }
+
+ if (slotp == NULL)
+ return;
+
+ occupant = kmem_alloc(sizeof (pcie_hp_occupant_info_t), KM_SLEEP);
+ occupant->i = 0;
+
+ cn_cfg.flag = B_FALSE;
+ cn_cfg.rv = NDI_SUCCESS;
+ cn_cfg.dip = NULL;
+ cn_cfg.slotp = (void *)slotp;
+ cn_cfg.cn_private = (void *)occupant;
+
+ ddi_walk_devs(ddi_get_child(dip), pcie_hp_list_occupants,
+ (void *)&cn_cfg);
+
+ if (occupant->i == 0) {
+ /* no occupants right now, need to create stub property */
+ char *c[] = { "" };
+ (void) ddi_prop_update_string_array(dev, dip, "pci-occupant",
+ c, 1);
+ } else {
+ (void) ddi_prop_update_string_array(dev, dip, "pci-occupant",
+ occupant->id, occupant->i);
+ }
+
+ for (i = 0; i < occupant->i; i++)
+ kmem_free(occupant->id[i], sizeof (char[MAXPATHLEN]));
+
+ kmem_free(occupant, sizeof (pcie_hp_occupant_info_t));
+
+ ndi_devi_exit(dip, circular);
+}
+
+/*
+ * routine to remove 'pci-occupant' property for a hotplug slot
+ */
+void
+pcie_hp_delete_occupant_props(dev_info_t *dip, dev_t dev)
+{
+ (void) ddi_prop_remove(dev, dip, "pci-occupant");
+}
+
+/*
+ * general code to create a minor node, called from hotplug controller
+ * drivers.
+ */
+int
+pcie_create_minor_node(pcie_hp_ctrl_t *ctrl_p, int slot)
+{
+ dev_info_t *dip = ctrl_p->hc_dip;
+ pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[slot];
+ ddi_hp_cn_info_t *info_p = &slot_p->hs_info;
+
+ if (ddi_create_minor_node(dip, info_p->cn_name,
+ S_IFCHR, slot_p->hs_minor,
+ DDI_NT_PCI_ATTACHMENT_POINT, 0) != DDI_SUCCESS) {
+ return (DDI_FAILURE);
+ }
+
+ (void) ddi_prop_update_int(DDI_DEV_T_NONE,
+ dip, "ap-names", 1 << slot_p->hs_device_num);
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * general code to remove a minor node, called from hotplug controller
+ * drivers.
+ */
+void
+pcie_remove_minor_node(pcie_hp_ctrl_t *ctrl_p, int slot)
+{
+ ddi_remove_minor_node(ctrl_p->hc_dip,
+ ctrl_p->hc_slots[slot]->hs_info.cn_name);
+}
+
+/*
+ * Local functions (called within this file)
+ */
+
+/*
+ * Register ports for all the children with device number device_num
+ */
+static int
+pcie_hp_register_ports_for_dev(dev_info_t *dip, int device_num)
+{
+ dev_info_t *cdip;
+ int rv;
+
+ for (cdip = ddi_get_child(dip); cdip;
+ cdip = ddi_get_next_sibling(cdip)) {
+ if (pcie_hp_match_dev(cdip, device_num)) {
+ /*
+ * Found the newly probed device under the
+ * current slot. Register a port for it.
+ */
+ if ((rv = pcie_hp_register_port(cdip, dip, NULL))
+ != DDI_SUCCESS)
+ return (rv);
+ } else {
+ continue;
+ }
+ }
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * Unregister ports of a pci bridge dip, get called from ndi_hp_walk_cn()
+ *
+ * If connector_num is specified, then unregister the slot's dependent ports
+ * only; Otherwise, unregister all ports of a pci bridge dip.
+ */
+static int
+pcie_hp_unregister_ports_cb(ddi_hp_cn_info_t *info, void *arg)
+{
+ pcie_hp_unreg_port_t *unreg_arg = (pcie_hp_unreg_port_t *)arg;
+ dev_info_t *dip = unreg_arg->nexus_dip;
+ int rv = NDI_SUCCESS;
+
+ if (info->cn_type != DDI_HP_CN_TYPE_VIRTUAL_PORT) {
+ unreg_arg->rv = rv;
+ return (DDI_WALK_CONTINUE);
+ }
+
+ if (unreg_arg->connector_num != DDI_HP_CN_NUM_NONE) {
+ /* Unregister ports for all unprobed devices under a slot. */
+ if (unreg_arg->connector_num == info->cn_num_dpd_on) {
+
+ rv = ndi_hp_unregister(dip, info->cn_name);
+ }
+ } else {
+
+ /* Unregister all ports of a pci bridge dip. */
+ rv = ndi_hp_unregister(dip, info->cn_name);
+ }
+
+ unreg_arg->rv = rv;
+ if (rv == NDI_SUCCESS)
+ return (DDI_WALK_CONTINUE);
+ else
+ return (DDI_WALK_TERMINATE);
+}
+
+/*
+ * Find a port according to cn_name and get the port's state.
+ */
+static int
+pcie_hp_get_port_state(ddi_hp_cn_info_t *info, void *arg)
+{
+ pcie_hp_port_state_t *port = (pcie_hp_port_state_t *)arg;
+
+ if (info->cn_type != DDI_HP_CN_TYPE_VIRTUAL_PORT)
+ return (DDI_WALK_CONTINUE);
+
+ if (strcmp(info->cn_name, port->cn_name) == 0) {
+ /* Matched. */
+ port->cn_state = info->cn_state;
+ port->rv = DDI_SUCCESS;
+
+ return (DDI_WALK_TERMINATE);
+ }
+
+ return (DDI_WALK_CONTINUE);
+}
+
+/*
+ * Find the physical slot with the given device number;
+ * return the slot if found.
+ */
+static pcie_hp_slot_t *
+pcie_find_physical_slot(dev_info_t *dip, int dev_num)
+{
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
+ pcie_hp_ctrl_t *ctrl = PCIE_GET_HP_CTRL(dip);
+
+ if (PCIE_IS_PCIE_HOTPLUG_CAPABLE(bus_p)) {
+ /* PCIe has only one slot */
+ return (dev_num == 0) ? (ctrl->hc_slots[0]) : (NULL);
+ } else if (PCIE_IS_PCI_HOTPLUG_CAPABLE(bus_p)) {
+ for (int slot = 0; slot < ctrl->hc_num_slots_impl; slot++) {
+ if (ctrl->hc_slots[slot]->hs_device_num == dev_num) {
+ /* found */
+ return (ctrl->hc_slots[slot]);
+ }
+ }
+ }
+
+ return (NULL);
+}
+
+/*
+ * setup slot name/slot-number info for the port which is being registered.
+ */
+static int
+pcie_hp_create_port_name_num(dev_info_t *dip, ddi_hp_cn_info_t *cn_info)
+{
+ int ret, dev_num, func_num, name_len;
+ dev_info_t *pdip = ddi_get_parent(dip);
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(pdip);
+ pcie_hp_slot_t *slot;
+ pcie_req_id_t bdf;
+ char tmp[PCIE_HP_DEV_FUNC_NUM_STRING_LEN];
+
+ ret = pcie_get_bdf_from_dip(dip, &bdf);
+ if (ret != DDI_SUCCESS) {
+ return (ret);
+ }
+ if (PCIE_IS_RP(bus_p) || PCIE_IS_SWD(bus_p) ||
+ PCIE_IS_PCI2PCIE(bus_p)) {
+ /*
+ * It is under a PCIe device, devcie number is always 0;
+ * function number might > 8 in ARI supported case.
+ */
+ dev_num = 0;
+ func_num = (bdf & ((~PCI_REG_BUS_M) >> 8));
+ } else {
+ dev_num = (bdf & (PCI_REG_DEV_M >> 8)) >> 3;
+ func_num = bdf & (PCI_REG_FUNC_M >> 8);
+ }
+ /*
+ * The string length of dev_num and func_num must be no longer than 4
+ * including the string end mark. (With ARI case considered, e.g.,
+ * dev_num=0x0, func_num=0xff.)
+ */
+ (void) snprintf(tmp, PCIE_HP_DEV_FUNC_NUM_STRING_LEN, "%x%x",
+ dev_num, func_num);
+ /*
+ * Calculate the length of cn_name.
+ * The format of pci port name is: pci.d,f
+ * d stands for dev_num, f stands for func_num. So the length of the
+ * name string can be calculated as following.
+ */
+ name_len = strlen(tmp) + PCIE_HP_PORT_NAME_STRING_LEN + 1;
+
+ cn_info->cn_name = (char *)kmem_zalloc(name_len, KM_SLEEP);
+ (void) snprintf(cn_info->cn_name, name_len, "pci.%x,%x",
+ dev_num, func_num);
+ cn_info->cn_num = (dev_num << 8) | func_num;
+ slot = pcie_find_physical_slot(pdip, dev_num);
+
+ cn_info->cn_num_dpd_on = slot ?
+ slot->hs_info.cn_num : DDI_HP_CN_NUM_NONE;
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * Extract device and function number from port name, whose format is
+ * something like 'pci.1,0'
+ */
+static int
+pcie_hp_get_df_from_port_name(char *cn_name, int *dev_num, int *func_num)
+{
+ int name_len, ret;
+ long d, f;
+ char *sp;
+
+ /* some checks for the input name */
+ name_len = strlen(cn_name);
+ if ((name_len <= PCIE_HP_PORT_NAME_STRING_LEN) ||
+ (name_len > (PCIE_HP_PORT_NAME_STRING_LEN +
+ PCIE_HP_DEV_FUNC_NUM_STRING_LEN - 1)) ||
+ (strncmp("pci.", cn_name, 4) != 0)) {
+ return (DDI_EINVAL);
+ }
+ ret = ddi_strtol(cn_name + 4, &sp, 10, &d);
+ if (ret != DDI_SUCCESS)
+ return (ret);
+
+ if (strncmp(",", sp, 1) != 0)
+ return (DDI_EINVAL);
+
+ ret = ddi_strtol(sp + 1, NULL, 10, &f);
+ if (ret != DDI_SUCCESS)
+ return (ret);
+ *dev_num = (int)d;
+ *func_num = (int)f;
+
+ return (ret);
+}
+
+/*
+ * Check/copy cn_name and set connection numbers.
+ * If it is a valid name, then setup cn_info for the newly created port.
+ */
+static int
+pcie_hp_setup_port_name_num(dev_info_t *pdip, char *cn_name,
+ ddi_hp_cn_info_t *cn_info)
+{
+ int dev_num, func_num, ret;
+ pcie_hp_slot_t *slot;
+
+ if ((ret = pcie_hp_get_df_from_port_name(cn_name, &dev_num, &func_num))
+ != DDI_SUCCESS)
+ return (ret);
+
+ if (pcie_hp_check_hardware_existence(pdip, dev_num, func_num) ==
+ DDI_SUCCESS) {
+ cn_info->cn_state = DDI_HP_CN_STATE_PRESENT;
+ } else {
+ cn_info->cn_state = DDI_HP_CN_STATE_EMPTY;
+ }
+
+ cn_info->cn_name = ddi_strdup(cn_name, KM_SLEEP);
+ cn_info->cn_num = (dev_num << 8) | func_num;
+
+ slot = pcie_find_physical_slot(pdip, dev_num);
+ if (slot) {
+ cn_info->cn_num_dpd_on = slot->hs_info.cn_num;
+ } else {
+ cn_info->cn_num_dpd_on = DDI_HP_CN_NUM_NONE;
+ }
+ return (DDI_SUCCESS);
+}
+
+static int
+ndi2ddi(int n)
+{
+ int ret;
+
+ switch (n) {
+ case NDI_SUCCESS:
+ ret = DDI_SUCCESS;
+ break;
+ case NDI_NOMEM:
+ ret = DDI_ENOMEM;
+ break;
+ case NDI_BUSY:
+ ret = DDI_EBUSY;
+ break;
+ case NDI_EINVAL:
+ ret = DDI_EINVAL;
+ break;
+ case NDI_ENOTSUP:
+ ret = DDI_ENOTSUP;
+ break;
+ case NDI_FAILURE:
+ default:
+ ret = DDI_FAILURE;
+ break;
+ }
+ return (ret);
+}
+
+/*
+ * Common routine to create and register a new port
+ *
+ * Create an empty port if dip is NULL, and cn_name needs to be specified in
+ * this case. Otherwise, create a port mapping to the specified dip, and cn_name
+ * is not needed in this case.
+ */
+static int
+pcie_hp_register_port(dev_info_t *dip, dev_info_t *pdip, char *cn_name)
+{
+ ddi_hp_cn_info_t *cn_info;
+ int ret;
+
+ ASSERT((dip == NULL) != (cn_name == NULL));
+ cn_info = kmem_zalloc(sizeof (ddi_hp_cn_info_t), KM_SLEEP);
+ if (dip != NULL)
+ ret = pcie_hp_create_port_name_num(dip, cn_info);
+ else
+ ret = pcie_hp_setup_port_name_num(pdip, cn_name, cn_info);
+
+ if (ret != DDI_SUCCESS) {
+ kmem_free(cn_info, sizeof (ddi_hp_cn_info_t));
+ return (ret);
+ }
+
+ cn_info->cn_child = dip;
+ cn_info->cn_type = DDI_HP_CN_TYPE_VIRTUAL_PORT;
+ cn_info->cn_type_str = DDI_HP_CN_TYPE_STR_PORT;
+
+ ret = ndi_hp_register(pdip, cn_info);
+
+ kmem_free(cn_info->cn_name, strlen(cn_info->cn_name) + 1);
+ kmem_free(cn_info, sizeof (ddi_hp_cn_info_t));
+
+ return (ndi2ddi(ret));
+}
+
+/* Check if there is a piece of hardware exist corresponding to the cn_name */
+static int
+pcie_hp_check_hardware_existence(dev_info_t *dip, int dev_num, int func_num)
+{
+
+ /*
+ * VHPTODO:
+ * According to device and function number, check if there is a hardware
+ * device exists. Currently, this function can not be reached before
+ * we enable state transition to or from "Port-Empty" or "Port-Present"
+ * states. When the pci device type project is integrated, we are going
+ * to call the pci config space access interfaces introduced by it.
+ */
+ _NOTE(ARGUNUSED(dip, dev_num, func_num));
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * Dispatch hotplug commands to different hotplug controller drivers, including
+ * physical and virtual hotplug operations.
+ */
+/* ARGSUSED */
+int
+pcie_hp_common_ops(dev_info_t *dip, char *cn_name, ddi_hp_op_t op,
+ void *arg, void *result)
+{
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
+ int ret = DDI_SUCCESS;
+
+ PCIE_DBG("pcie_hp_common_ops: dip=%p cn_name=%s op=%x arg=%p\n",
+ dip, cn_name, op, arg);
+
+ switch (op) {
+ case DDI_HPOP_CN_CREATE_PORT:
+ {
+ /* create an empty port */
+ return (pcie_hp_register_port(NULL, dip, cn_name));
+ }
+ case DDI_HPOP_CN_CHANGE_STATE:
+ {
+ ddi_hp_cn_state_t curr_state;
+ ddi_hp_cn_state_t target_state = *(ddi_hp_cn_state_t *)arg;
+ pcie_hp_port_state_t state_arg;
+
+ if (target_state < DDI_HP_CN_STATE_PORT_EMPTY) {
+ /* this is for physical slot state change */
+ break;
+ }
+ PCIE_DBG("pcie_hp_common_ops: change port state"
+ " dip=%p cn_name=%s"
+ " op=%x arg=%p\n", (void *)dip, cn_name, op, arg);
+
+ state_arg.rv = DDI_FAILURE;
+ state_arg.cn_name = cn_name;
+ ndi_hp_walk_cn(dip, pcie_hp_get_port_state, &state_arg);
+ if (state_arg.rv != DDI_SUCCESS) {
+ /* can not find the port */
+ return (DDI_EINVAL);
+ }
+ curr_state = state_arg.cn_state;
+ /*
+ * Check if this is for changing port's state: change to/from
+ * PORT_EMPTY/PRESENT states.
+ */
+ if (curr_state < target_state) {
+ /* Upgrade state */
+ switch (curr_state) {
+ case DDI_HP_CN_STATE_PORT_EMPTY:
+ if (target_state ==
+ DDI_HP_CN_STATE_PORT_PRESENT) {
+ int dev_num, func_num;
+
+ ret = pcie_hp_get_df_from_port_name(
+ cn_name, &dev_num, &func_num);
+ if (ret != DDI_SUCCESS)
+ goto port_state_done;
+
+ ret = pcie_hp_check_hardware_existence(
+ dip, dev_num, func_num);
+ } else if (target_state ==
+ DDI_HP_CN_STATE_OFFLINE) {
+ ret = pcie_read_only_probe(dip,
+ cn_name, (dev_info_t **)result);
+ } else
+ ret = DDI_EINVAL;
+
+ goto port_state_done;
+ case DDI_HP_CN_STATE_PORT_PRESENT:
+ if (target_state ==
+ DDI_HP_CN_STATE_OFFLINE)
+ ret = pcie_read_only_probe(dip,
+ cn_name, (dev_info_t **)result);
+ else
+ ret = DDI_EINVAL;
+
+ goto port_state_done;
+ default:
+ ASSERT("unexpected state");
+ }
+ } else {
+ /* Downgrade state */
+ switch (curr_state) {
+ case DDI_HP_CN_STATE_PORT_PRESENT:
+ {
+ int dev_num, func_num;
+
+ ret = pcie_hp_get_df_from_port_name(cn_name,
+ &dev_num, &func_num);
+ if (ret != DDI_SUCCESS)
+ goto port_state_done;
+
+ ret = pcie_hp_check_hardware_existence(dip,
+ dev_num, func_num);
+
+ goto port_state_done;
+ }
+ case DDI_HP_CN_STATE_OFFLINE:
+ ret = pcie_read_only_unprobe(dip, cn_name);
+
+ goto port_state_done;
+ default:
+ ASSERT("unexpected state");
+ }
+ }
+port_state_done:
+ *(ddi_hp_cn_state_t *)result = curr_state;
+ return (ret);
+ }
+ default:
+ break;
+ }
+
+ if (PCIE_IS_PCIE_HOTPLUG_CAPABLE(bus_p)) {
+ /* PCIe hotplug */
+ ret = pciehpc_hp_ops(dip, cn_name, op, arg, result);
+ } else if (PCIE_IS_PCI_HOTPLUG_CAPABLE(bus_p)) {
+ /* PCI SHPC hotplug */
+ ret = pcishpc_hp_ops(dip, cn_name, op, arg, result);
+ } else {
+ cmn_err(CE_WARN, "pcie_hp_common_ops: op is not supported."
+ " dip=%p cn_name=%s"
+ " op=%x arg=%p\n", (void *)dip, cn_name, op, arg);
+ ret = DDI_ENOTSUP;
+ }
+
+#if defined(__i386) || defined(__amd64)
+ /*
+ * like in attach, since hotplugging can change error registers,
+ * we need to ensure that the proper bits are set on this port
+ * after a configure operation
+ */
+ if ((ret == DDI_SUCCESS) && (op == DDI_HPOP_CN_CHANGE_STATE) &&
+ (*(ddi_hp_cn_state_t *)arg == DDI_HP_CN_STATE_ENABLED))
+ pcieb_intel_error_workaround(dip);
+#endif
+
+ return (ret);
+}
+
+/*
+ * pcie_hp_match_dev_func:
+ * Match dip's PCI device number and function number with input ones.
+ */
+static int
+pcie_hp_match_dev_func(dev_info_t *dip, void *hdl)
+{
+ struct pcie_hp_find_ctrl *ctrl = (struct pcie_hp_find_ctrl *)hdl;
+ pci_regspec_t *pci_rp;
+ int length;
+ int pci_dev, pci_func;
+
+ if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
+ "reg", (int **)&pci_rp, (uint_t *)&length) != DDI_PROP_SUCCESS) {
+ ctrl->dip = NULL;
+ return (DDI_WALK_TERMINATE);
+ }
+
+ /* get the PCI device address info */
+ pci_dev = PCI_REG_DEV_G(pci_rp->pci_phys_hi);
+ pci_func = PCI_REG_FUNC_G(pci_rp->pci_phys_hi);
+
+ /*
+ * free the memory allocated by ddi_prop_lookup_int_array
+ */
+ ddi_prop_free(pci_rp);
+
+ if ((pci_dev == ctrl->device) && (pci_func == ctrl->function)) {
+ /* found the match for the specified device address */
+ ctrl->dip = dip;
+ return (DDI_WALK_TERMINATE);
+ }
+
+ /*
+ * continue the walk to the next sibling to look for a match.
+ */
+ return (DDI_WALK_PRUNECHILD);
+}
+
+/*
+ * pcie_hp_match_dev:
+ * Match the dip's pci device number with the input dev_num
+ */
+static boolean_t
+pcie_hp_match_dev(dev_info_t *dip, int dev_num)
+{
+ pci_regspec_t *pci_rp;
+ int length;
+ int pci_dev;
+
+ if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
+ "reg", (int **)&pci_rp, (uint_t *)&length) != DDI_PROP_SUCCESS) {
+ return (B_FALSE);
+ }
+
+ /* get the PCI device address info */
+ pci_dev = PCI_REG_DEV_G(pci_rp->pci_phys_hi);
+
+ /*
+ * free the memory allocated by ddi_prop_lookup_int_array
+ */
+ ddi_prop_free(pci_rp);
+
+ if (pci_dev == dev_num) {
+ /* found the match for the specified device address */
+ return (B_TRUE);
+ }
+
+ return (B_FALSE);
+}
+
+/*
+ * Callback function to match with device number in order to list
+ * occupants under a specific slot
+ */
+static int
+pcie_hp_list_occupants(dev_info_t *dip, void *arg)
+{
+ pcie_hp_cn_cfg_t *cn_cfg_p = (pcie_hp_cn_cfg_t *)arg;
+ pcie_hp_occupant_info_t *occupant =
+ (pcie_hp_occupant_info_t *)cn_cfg_p->cn_private;
+ pcie_hp_slot_t *slot_p =
+ (pcie_hp_slot_t *)cn_cfg_p->slotp;
+ int pci_dev;
+ pci_regspec_t *pci_rp;
+ int length;
+ major_t major;
+
+ /*
+ * Get the PCI device number information from the devinfo
+ * node. Since the node may not have the address field
+ * setup (this is done in the DDI_INITCHILD of the parent)
+ * we look up the 'reg' property to decode that information.
+ */
+ if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip,
+ DDI_PROP_DONTPASS, "reg", (int **)&pci_rp,
+ (uint_t *)&length) != DDI_PROP_SUCCESS) {
+ cn_cfg_p->rv = DDI_FAILURE;
+ cn_cfg_p->dip = dip;
+ return (DDI_WALK_TERMINATE);
+ }
+
+ /* get the pci device id information */
+ pci_dev = PCI_REG_DEV_G(pci_rp->pci_phys_hi);
+
+ /*
+ * free the memory allocated by ddi_prop_lookup_int_array
+ */
+ ddi_prop_free(pci_rp);
+
+ /*
+ * Match the node for the device number of the slot.
+ */
+ if (pci_dev == slot_p->hs_device_num) {
+
+ major = ddi_driver_major(dip);
+
+ /*
+ * If the node is not yet attached, then don't list it
+ * as an occupant. This is valid, since nothing can be
+ * consuming it until it is attached, and cfgadm will
+ * ask for the property explicitly which will cause it
+ * to be re-freshed right before checking with rcm.
+ */
+ if ((major == DDI_MAJOR_T_NONE) || !i_ddi_devi_attached(dip))
+ return (DDI_WALK_PRUNECHILD);
+
+ /*
+ * If we have used all our occupants then print mesage
+ * and terminate walk.
+ */
+ if (occupant->i >= PCIE_HP_MAX_OCCUPANTS) {
+ cmn_err(CE_WARN,
+ "pcie (%s%d): unable to list all occupants",
+ ddi_driver_name(ddi_get_parent(dip)),
+ ddi_get_instance(ddi_get_parent(dip)));
+ return (DDI_WALK_TERMINATE);
+ }
+
+ /*
+ * No need to hold the dip as ddi_walk_devs
+ * has already arranged that for us.
+ */
+ occupant->id[occupant->i] =
+ kmem_alloc(sizeof (char[MAXPATHLEN]), KM_SLEEP);
+ (void) ddi_pathname(dip, (char *)occupant->id[occupant->i]);
+ occupant->i++;
+ }
+
+ /*
+ * continue the walk to the next sibling to look for a match
+ * or to find other nodes if this card is a multi-function card.
+ */
+ return (DDI_WALK_PRUNECHILD);
+}
+
+/*
+ * Generate the System Event for ESC_DR_REQ.
+ * One of the consumers is pcidr, it calls to libcfgadm to perform a
+ * configure or unconfigure operation to the AP.
+ */
+void
+pcie_hp_gen_sysevent_req(char *slot_name, int hint,
+ dev_info_t *self, int kmflag)
+{
+ sysevent_id_t eid;
+ nvlist_t *ev_attr_list = NULL;
+ char cn_path[MAXPATHLEN];
+ char *ap_id;
+ int err, ap_id_len;
+
+ /*
+ * Minor device name (AP) will be bus path
+ * concatenated with slot name
+ */
+ (void) strcpy(cn_path, "/devices");
+ (void) ddi_pathname(self, cn_path + strlen("/devices"));
+
+ ap_id_len = strlen(cn_path) + strlen(":") +
+ strlen(slot_name) + 1;
+ ap_id = kmem_zalloc(ap_id_len, kmflag);
+ if (ap_id == NULL) {
+ cmn_err(CE_WARN,
+ "%s%d: Failed to allocate memory for AP ID: %s:%s",
+ ddi_driver_name(self), ddi_get_instance(self),
+ cn_path, slot_name);
+
+ return;
+ }
+
+ (void) strcpy(ap_id, cn_path);
+ (void) strcat(ap_id, ":");
+ (void) strcat(ap_id, slot_name);
+
+ err = nvlist_alloc(&ev_attr_list, NV_UNIQUE_NAME_TYPE, kmflag);
+ if (err != 0) {
+ cmn_err(CE_WARN,
+ "%s%d: Failed to allocate memory "
+ "for event attributes%s", ddi_driver_name(self),
+ ddi_get_instance(self), ESC_DR_REQ);
+
+ kmem_free(ap_id, ap_id_len);
+ return;
+ }
+
+ switch (hint) {
+
+ case SE_INVESTIGATE_RES: /* fall through */
+ case SE_INCOMING_RES: /* fall through */
+ case SE_OUTGOING_RES: /* fall through */
+
+ err = nvlist_add_string(ev_attr_list, DR_REQ_TYPE,
+ SE_REQ2STR(hint));
+
+ if (err != 0) {
+ cmn_err(CE_WARN,
+ "%s%d: Failed to add attr [%s] "
+ "for %s event", ddi_driver_name(self),
+ ddi_get_instance(self),
+ DR_REQ_TYPE, ESC_DR_REQ);
+
+ goto done;
+ }
+ break;
+
+ default:
+ cmn_err(CE_WARN, "%s%d: Unknown hint on sysevent",
+ ddi_driver_name(self), ddi_get_instance(self));
+
+ goto done;
+ }
+
+ /*
+ * Add attachment point as attribute (common attribute)
+ */
+
+ err = nvlist_add_string(ev_attr_list, DR_AP_ID, ap_id);
+
+ if (err != 0) {
+ cmn_err(CE_WARN, "%s%d: Failed to add attr [%s] for %s event",
+ ddi_driver_name(self), ddi_get_instance(self),
+ DR_AP_ID, EC_DR);
+
+ goto done;
+ }
+
+
+ /*
+ * Log this event with sysevent framework.
+ */
+
+ err = ddi_log_sysevent(self, DDI_VENDOR_SUNW, EC_DR,
+ ESC_DR_REQ, ev_attr_list, &eid,
+ ((kmflag == KM_SLEEP) ? DDI_SLEEP : DDI_NOSLEEP));
+ if (err != 0) {
+ cmn_err(CE_WARN, "%s%d: Failed to log %s event",
+ ddi_driver_name(self), ddi_get_instance(self), EC_DR);
+ }
+
+done:
+ nvlist_free(ev_attr_list);
+ kmem_free(ap_id, ap_id_len);
+}
diff --git a/usr/src/uts/common/io/pciex/hotplug/pciehpc.c b/usr/src/uts/common/io/pciex/hotplug/pciehpc.c
new file mode 100644
index 0000000000..900e36b383
--- /dev/null
+++ b/usr/src/uts/common/io/pciex/hotplug/pciehpc.c
@@ -0,0 +1,2285 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * This file contains Standard PCI Express HotPlug functionality that is
+ * compatible with the PCI Express ver 1.1 specification.
+ *
+ * NOTE: This file is compiled and delivered through misc/pcie module.
+ */
+
+#include <sys/types.h>
+#include <sys/note.h>
+#include <sys/conf.h>
+#include <sys/kmem.h>
+#include <sys/debug.h>
+#include <sys/vtrace.h>
+#include <sys/autoconf.h>
+#include <sys/varargs.h>
+#include <sys/ddi_impldefs.h>
+#include <sys/time.h>
+#include <sys/callb.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/sunndi.h>
+#include <sys/sysevent/dr.h>
+#include <sys/pci_impl.h>
+#include <sys/hotplug/pci/pcie_hp.h>
+#include <sys/hotplug/pci/pciehpc.h>
+
+typedef struct pciehpc_prop {
+ char *prop_name;
+ char *prop_value;
+} pciehpc_prop_t;
+
+static pciehpc_prop_t pciehpc_props[] = {
+ { PCIEHPC_PROP_LED_FAULT, PCIEHPC_PROP_VALUE_LED },
+ { PCIEHPC_PROP_LED_POWER, PCIEHPC_PROP_VALUE_LED },
+ { PCIEHPC_PROP_LED_ATTN, PCIEHPC_PROP_VALUE_LED },
+ { PCIEHPC_PROP_LED_ACTIVE, PCIEHPC_PROP_VALUE_LED },
+ { PCIEHPC_PROP_CARD_TYPE, PCIEHPC_PROP_VALUE_TYPE },
+ { PCIEHPC_PROP_BOARD_TYPE, PCIEHPC_PROP_VALUE_TYPE },
+ { PCIEHPC_PROP_SLOT_CONDITION, PCIEHPC_PROP_VALUE_TYPE }
+};
+
+/* Local functions prototype */
+static int pciehpc_hpc_init(pcie_hp_ctrl_t *ctrl_p);
+static int pciehpc_hpc_uninit(pcie_hp_ctrl_t *ctrl_p);
+static int pciehpc_slotinfo_init(pcie_hp_ctrl_t *ctrl_p);
+static int pciehpc_slotinfo_uninit(pcie_hp_ctrl_t *ctrl_p);
+static int pciehpc_enable_intr(pcie_hp_ctrl_t *ctrl_p);
+static int pciehpc_disable_intr(pcie_hp_ctrl_t *ctrl_p);
+static pcie_hp_ctrl_t *pciehpc_create_controller(dev_info_t *dip);
+static void pciehpc_destroy_controller(dev_info_t *dip);
+static int pciehpc_register_slot(pcie_hp_ctrl_t *ctrl_p);
+static int pciehpc_unregister_slot(pcie_hp_ctrl_t *ctrl_p);
+static int pciehpc_slot_get_property(pcie_hp_slot_t *slot_p,
+ ddi_hp_property_t *arg, ddi_hp_property_t *rval);
+static int pciehpc_slot_set_property(pcie_hp_slot_t *slot_p,
+ ddi_hp_property_t *arg, ddi_hp_property_t *rval);
+static void pciehpc_issue_hpc_command(pcie_hp_ctrl_t *ctrl_p, uint16_t control);
+static void pciehpc_attn_btn_handler(pcie_hp_ctrl_t *ctrl_p);
+static pcie_hp_led_state_t pciehpc_led_state_to_hpc(uint16_t state);
+static pcie_hp_led_state_t pciehpc_get_led_state(pcie_hp_ctrl_t *ctrl_p,
+ pcie_hp_led_t led);
+static void pciehpc_set_led_state(pcie_hp_ctrl_t *ctrl_p, pcie_hp_led_t led,
+ pcie_hp_led_state_t state);
+
+static int pciehpc_upgrade_slot_state(pcie_hp_slot_t *slot_p,
+ ddi_hp_cn_state_t target_state);
+static int pciehpc_downgrade_slot_state(pcie_hp_slot_t *slot_p,
+ ddi_hp_cn_state_t target_state);
+static int pciehpc_change_slot_state(pcie_hp_slot_t *slot_p,
+ ddi_hp_cn_state_t target_state);
+static int
+ pciehpc_slot_poweron(pcie_hp_slot_t *slot_p, ddi_hp_cn_state_t *result);
+static int
+ pciehpc_slot_poweroff(pcie_hp_slot_t *slot_p, ddi_hp_cn_state_t *result);
+static int pciehpc_slot_probe(pcie_hp_slot_t *slot_p);
+static int pciehpc_slot_unprobe(pcie_hp_slot_t *slot_p);
+
+#ifdef DEBUG
+static void pciehpc_dump_hpregs(pcie_hp_ctrl_t *ctrl_p);
+#endif /* DEBUG */
+
+/*
+ * Global functions (called by other drivers/modules)
+ */
+
+/*
+ * Initialize Hot Plug Controller if present. The arguments are:
+ * dip - Devinfo node pointer to the hot plug bus node
+ * regops - register ops to access HPC registers for non-standard
+ * HPC hw implementations (e.g: HPC in host PCI-E brdiges)
+ * This is NULL for standard HPC in PCIe bridges.
+ * Returns:
+ * DDI_SUCCESS for successful HPC initialization
+ * DDI_FAILURE for errors or if HPC hw not found
+ */
+int
+pciehpc_init(dev_info_t *dip, caddr_t arg)
+{
+ pcie_hp_regops_t *regops = (pcie_hp_regops_t *)(void *)arg;
+ pcie_hp_ctrl_t *ctrl_p;
+
+ PCIE_DBG("pciehpc_init() called (dip=%p)\n", (void *)dip);
+
+ /* Make sure that it is not already initialized */
+ if ((ctrl_p = PCIE_GET_HP_CTRL(dip)) != NULL) {
+ PCIE_DBG("%s%d: pciehpc instance already initialized!\n",
+ ddi_driver_name(dip), ddi_get_instance(dip));
+ return (DDI_SUCCESS);
+ }
+
+ /* Allocate a new hotplug controller and slot structures */
+ ctrl_p = pciehpc_create_controller(dip);
+
+ /* setup access handle for HPC regs */
+ if (regops != NULL) {
+ /* HPC access is non-standard; use the supplied reg ops */
+ ctrl_p->hc_regops = *regops;
+ }
+
+ /*
+ * Setup resource maps for this bus node.
+ */
+ (void) pci_resource_setup(dip);
+
+ PCIE_DISABLE_ERRORS(dip);
+
+ /*
+ * Set the platform specific hot plug mode.
+ */
+ ctrl_p->hc_ops.init_hpc_hw = pciehpc_hpc_init;
+ ctrl_p->hc_ops.uninit_hpc_hw = pciehpc_hpc_uninit;
+ ctrl_p->hc_ops.init_hpc_slotinfo = pciehpc_slotinfo_init;
+ ctrl_p->hc_ops.uninit_hpc_slotinfo = pciehpc_slotinfo_uninit;
+ ctrl_p->hc_ops.poweron_hpc_slot = pciehpc_slot_poweron;
+ ctrl_p->hc_ops.poweroff_hpc_slot = pciehpc_slot_poweroff;
+
+ ctrl_p->hc_ops.enable_hpc_intr = pciehpc_enable_intr;
+ ctrl_p->hc_ops.disable_hpc_intr = pciehpc_disable_intr;
+
+#if defined(__i386) || defined(__amd64)
+ pciehpc_update_ops(ctrl_p);
+#endif
+
+ /* initialize hot plug controller hw */
+ if ((ctrl_p->hc_ops.init_hpc_hw)(ctrl_p) != DDI_SUCCESS)
+ goto cleanup1;
+
+ /* initialize slot information soft state structure */
+ if ((ctrl_p->hc_ops.init_hpc_slotinfo)(ctrl_p) != DDI_SUCCESS)
+ goto cleanup2;
+
+ /* register the hot plug slot with DDI HP framework */
+ if (pciehpc_register_slot(ctrl_p) != DDI_SUCCESS)
+ goto cleanup3;
+
+ /* create minor node for this slot */
+ if (pcie_create_minor_node(ctrl_p, 0) != DDI_SUCCESS)
+ goto cleanup4;
+
+ /* HPC initialization is complete now */
+ ctrl_p->hc_flags = PCIE_HP_INITIALIZED_FLAG;
+
+#ifdef DEBUG
+ /* For debug, dump the HPC registers */
+ pciehpc_dump_hpregs(ctrl_p);
+#endif /* DEBUG */
+
+ /* enable hot plug interrupts/event */
+ (void) (ctrl_p->hc_ops.enable_hpc_intr)(ctrl_p);
+
+ return (DDI_SUCCESS);
+cleanup4:
+ (void) pciehpc_unregister_slot(ctrl_p);
+cleanup3:
+ (void) (ctrl_p->hc_ops.uninit_hpc_slotinfo)(ctrl_p);
+
+cleanup2:
+ (void) (ctrl_p->hc_ops.uninit_hpc_hw)(ctrl_p);
+
+cleanup1:
+ PCIE_ENABLE_ERRORS(dip);
+ (void) pci_resource_destroy(dip);
+
+ pciehpc_destroy_controller(dip);
+ return (DDI_FAILURE);
+}
+
+/*
+ * Uninitialize HPC soft state structure and free up any resources
+ * used for the HPC instance.
+ */
+int
+pciehpc_uninit(dev_info_t *dip)
+{
+ pcie_hp_ctrl_t *ctrl_p;
+
+ PCIE_DBG("pciehpc_uninit() called (dip=%p)\n", (void *)dip);
+
+ /* get the soft state structure for this dip */
+ if ((ctrl_p = PCIE_GET_HP_CTRL(dip)) == NULL) {
+ return (DDI_FAILURE);
+ }
+
+ pcie_remove_minor_node(ctrl_p, 0);
+
+ /* disable interrupts */
+ (void) (ctrl_p->hc_ops.disable_hpc_intr)(ctrl_p);
+
+ /* unregister the slot */
+ (void) pciehpc_unregister_slot(ctrl_p);
+
+ /* uninit any slot info data structures */
+ (void) (ctrl_p->hc_ops.uninit_hpc_slotinfo)(ctrl_p);
+
+ /* uninitialize hpc, remove interrupt handler, etc. */
+ (void) (ctrl_p->hc_ops.uninit_hpc_hw)(ctrl_p);
+
+ PCIE_ENABLE_ERRORS(dip);
+
+ /*
+ * Destroy resource maps for this bus node.
+ */
+ (void) pci_resource_destroy(dip);
+
+ /* destroy the soft state structure */
+ pciehpc_destroy_controller(dip);
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * pciehpc_intr()
+ *
+ * Interrupt handler for PCI-E Hot plug controller interrupts.
+ *
+ * Note: This is only for native mode hot plug. This is called
+ * by the nexus driver at interrupt context. Interrupt Service Routine
+ * registration is done by the nexus driver for both hot plug and
+ * non-hot plug interrupts. This function is called from the ISR
+ * of the nexus driver to handle hot-plug interrupts.
+ */
+int
+pciehpc_intr(dev_info_t *dip)
+{
+ pcie_hp_ctrl_t *ctrl_p;
+ pcie_hp_slot_t *slot_p;
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
+ uint16_t status, control;
+
+ /* get the soft state structure for this dip */
+ if ((ctrl_p = PCIE_GET_HP_CTRL(dip)) == NULL)
+ return (DDI_INTR_UNCLAIMED);
+
+ mutex_enter(&ctrl_p->hc_mutex);
+
+ /* make sure the controller soft state is initialized */
+ if (!(ctrl_p->hc_flags & PCIE_HP_INITIALIZED_FLAG)) {
+ mutex_exit(&ctrl_p->hc_mutex);
+ return (DDI_INTR_UNCLAIMED);
+ }
+
+ /* if it is not NATIVE hot plug mode then return */
+ if (bus_p->bus_hp_curr_mode != PCIE_NATIVE_HP_MODE) {
+ mutex_exit(&ctrl_p->hc_mutex);
+ return (DDI_INTR_UNCLAIMED);
+ }
+
+ slot_p = ctrl_p->hc_slots[0];
+
+ /* read the current slot status register */
+ status = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTSTS);
+
+ /* check if there are any hot plug interrupts occurred */
+ if (!(status & PCIE_SLOTSTS_STATUS_EVENTS)) {
+ /* no hot plug events occurred */
+ mutex_exit(&ctrl_p->hc_mutex);
+ return (DDI_INTR_UNCLAIMED);
+ }
+
+ /* clear the interrupt status bits */
+ pciehpc_reg_put16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTSTS, status);
+
+ /* check for CMD COMPLETE interrupt */
+ if (status & PCIE_SLOTSTS_COMMAND_COMPLETED) {
+ PCIE_DBG("pciehpc_intr(): CMD COMPLETED interrupt received\n");
+ /* wake up any one waiting for Command Completion event */
+ cv_signal(&ctrl_p->hc_cmd_comp_cv);
+ }
+
+ /* check for ATTN button interrupt */
+ if (status & PCIE_SLOTSTS_ATTN_BTN_PRESSED) {
+ PCIE_DBG("pciehpc_intr(): ATTN BUTTON interrupt received\n");
+
+ /* if ATTN button event is still pending then cancel it */
+ if (slot_p->hs_attn_btn_pending == B_TRUE)
+ slot_p->hs_attn_btn_pending = B_FALSE;
+ else
+ slot_p->hs_attn_btn_pending = B_TRUE;
+
+ /* wake up the ATTN event handler */
+ cv_signal(&slot_p->hs_attn_btn_cv);
+ }
+
+ /* check for power fault interrupt */
+ if (status & PCIE_SLOTSTS_PWR_FAULT_DETECTED) {
+
+ PCIE_DBG("pciehpc_intr(): POWER FAULT interrupt received"
+ " on slot %d\n", slot_p->hs_phy_slot_num);
+ control = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTCTL);
+
+ if (control & PCIE_SLOTCTL_PWR_FAULT_EN) {
+ slot_p->hs_condition = AP_COND_FAILED;
+
+ /* disable power fault detction interrupt */
+ pciehpc_reg_put16(ctrl_p, bus_p->bus_pcie_off +
+ PCIE_SLOTCTL, control & ~PCIE_SLOTCTL_PWR_FAULT_EN);
+
+ /*
+ * Send the event to DDI Hotplug framework, power off
+ * the slot
+ */
+ (void) ndi_hp_state_change_req(dip,
+ slot_p->hs_info.cn_name,
+ DDI_HP_CN_STATE_EMPTY, DDI_HP_REQ_ASYNC);
+
+ pciehpc_set_led_state(ctrl_p, PCIE_HP_ATTN_LED,
+ PCIE_HP_LED_ON);
+ }
+ }
+
+ /* check for MRL SENSOR CHANGED interrupt */
+ if (status & PCIE_SLOTSTS_MRL_SENSOR_CHANGED) {
+ /* For now (phase-I), no action is taken on this event */
+ PCIE_DBG("pciehpc_intr(): MRL SENSOR CHANGED interrupt received"
+ " on slot %d\n", slot_p->hs_phy_slot_num);
+ }
+
+ /* check for PRESENCE CHANGED interrupt */
+ if (status & PCIE_SLOTSTS_PRESENCE_CHANGED) {
+
+ PCIE_DBG("pciehpc_intr(): PRESENCE CHANGED interrupt received"
+ " on slot %d\n", slot_p->hs_phy_slot_num);
+
+ if (status & PCIE_SLOTSTS_PRESENCE_DETECTED) {
+ /*
+ * card is inserted into the slot, ask DDI Hotplug
+ * framework to change state to Present.
+ */
+ (void) ndi_hp_state_change_req(dip,
+ slot_p->hs_info.cn_name,
+ DDI_HP_CN_STATE_PRESENT,
+ DDI_HP_REQ_ASYNC);
+ } else { /* card is removed from the slot */
+ cmn_err(CE_NOTE, "pciehpc (%s%d): card is removed"
+ " from the slot %s",
+ ddi_driver_name(dip),
+ ddi_get_instance(dip),
+ slot_p->hs_info.cn_name);
+
+ if (slot_p->hs_info.cn_state ==
+ DDI_HP_CN_STATE_ENABLED) {
+ /* Card is removed when slot is enabled */
+ slot_p->hs_condition = AP_COND_FAILED;
+ } else {
+ slot_p->hs_condition = AP_COND_UNKNOWN;
+ }
+ /* make sure to disable power fault detction intr */
+ control = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTCTL);
+
+ if (control & PCIE_SLOTCTL_PWR_FAULT_EN)
+ pciehpc_reg_put16(ctrl_p, bus_p->bus_pcie_off +
+ PCIE_SLOTCTL,
+ control & ~PCIE_SLOTCTL_PWR_FAULT_EN);
+
+ /*
+ * Ask DDI Hotplug framework to change state to Empty
+ */
+ (void) ndi_hp_state_change_req(dip,
+ slot_p->hs_info.cn_name,
+ DDI_HP_CN_STATE_EMPTY,
+ DDI_HP_REQ_ASYNC);
+ }
+ }
+
+ /* check for DLL state changed interrupt */
+ if (ctrl_p->hc_dll_active_rep &&
+ (status & PCIE_SLOTSTS_DLL_STATE_CHANGED)) {
+ PCIE_DBG("pciehpc_intr(): DLL STATE CHANGED interrupt received"
+ " on slot %d\n", slot_p->hs_phy_slot_num);
+
+ cv_signal(&slot_p->hs_dll_active_cv);
+ }
+
+ mutex_exit(&ctrl_p->hc_mutex);
+
+ return (DDI_INTR_CLAIMED);
+}
+
+/*
+ * Handle hotplug commands
+ *
+ * Note: This function is called by DDI HP framework at kernel context only
+ */
+/* ARGSUSED */
+int
+pciehpc_hp_ops(dev_info_t *dip, char *cn_name, ddi_hp_op_t op,
+ void *arg, void *result)
+{
+ pcie_hp_ctrl_t *ctrl_p;
+ pcie_hp_slot_t *slot_p;
+ int ret = DDI_SUCCESS;
+
+ PCIE_DBG("pciehpc_hp_ops: dip=%p cn_name=%s op=%x arg=%p\n",
+ dip, cn_name, op, arg);
+
+ if ((ctrl_p = PCIE_GET_HP_CTRL(dip)) == NULL)
+ return (DDI_FAILURE);
+
+ slot_p = ctrl_p->hc_slots[0];
+
+ if (strcmp(cn_name, slot_p->hs_info.cn_name) != 0)
+ return (DDI_EINVAL);
+
+ switch (op) {
+ case DDI_HPOP_CN_GET_STATE:
+ {
+ mutex_enter(&slot_p->hs_ctrl->hc_mutex);
+
+ /* get the current slot state */
+ pciehpc_get_slot_state(slot_p);
+
+ *((ddi_hp_cn_state_t *)result) = slot_p->hs_info.cn_state;
+
+ mutex_exit(&slot_p->hs_ctrl->hc_mutex);
+ break;
+ }
+ case DDI_HPOP_CN_CHANGE_STATE:
+ {
+ ddi_hp_cn_state_t target_state = *(ddi_hp_cn_state_t *)arg;
+
+ mutex_enter(&slot_p->hs_ctrl->hc_mutex);
+
+ ret = pciehpc_change_slot_state(slot_p, target_state);
+ *(ddi_hp_cn_state_t *)result = slot_p->hs_info.cn_state;
+
+ mutex_exit(&slot_p->hs_ctrl->hc_mutex);
+ break;
+ }
+ case DDI_HPOP_CN_PROBE:
+
+ ret = pciehpc_slot_probe(slot_p);
+
+ break;
+ case DDI_HPOP_CN_UNPROBE:
+ ret = pciehpc_slot_unprobe(slot_p);
+
+ break;
+ case DDI_HPOP_CN_GET_PROPERTY:
+ ret = pciehpc_slot_get_property(slot_p,
+ (ddi_hp_property_t *)arg, (ddi_hp_property_t *)result);
+ break;
+ case DDI_HPOP_CN_SET_PROPERTY:
+ ret = pciehpc_slot_set_property(slot_p,
+ (ddi_hp_property_t *)arg, (ddi_hp_property_t *)result);
+ break;
+ default:
+ ret = DDI_ENOTSUP;
+ break;
+ }
+
+ return (ret);
+}
+
+/*
+ * Get the current state of the slot from the hw.
+ *
+ * The slot state should have been initialized before this function gets called.
+ */
+void
+pciehpc_get_slot_state(pcie_hp_slot_t *slot_p)
+{
+ pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl;
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
+ uint16_t control, status;
+ ddi_hp_cn_state_t curr_state = slot_p->hs_info.cn_state;
+
+ /* read the Slot Control Register */
+ control = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTCTL);
+
+ slot_p->hs_fault_led_state = PCIE_HP_LED_OFF; /* no fault led */
+ slot_p->hs_active_led_state = PCIE_HP_LED_OFF; /* no active led */
+
+ /* read the current Slot Status Register */
+ status = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTSTS);
+
+ /* get POWER led state */
+ slot_p->hs_power_led_state =
+ pciehpc_led_state_to_hpc(pcie_slotctl_pwr_indicator_get(control));
+
+ /* get ATTN led state */
+ slot_p->hs_attn_led_state =
+ pciehpc_led_state_to_hpc(pcie_slotctl_attn_indicator_get(control));
+
+ if (!(status & PCIE_SLOTSTS_PRESENCE_DETECTED)) {
+ /* no device present; slot is empty */
+ slot_p->hs_info.cn_state = DDI_HP_CN_STATE_EMPTY;
+
+ return;
+ }
+
+ /* device is present */
+ slot_p->hs_info.cn_state = DDI_HP_CN_STATE_PRESENT;
+
+ if (!(control & PCIE_SLOTCTL_PWR_CONTROL)) {
+ /*
+ * Device is powered on. Set to "ENABLED" state (skip
+ * POWERED state) because there is not a explicit "enable"
+ * action exists for PCIe.
+ * If it is already in "POWERED" state, then keep it until
+ * user explicitly change it to other states.
+ */
+ if (curr_state == DDI_HP_CN_STATE_POWERED) {
+ slot_p->hs_info.cn_state = curr_state;
+ } else {
+ slot_p->hs_info.cn_state = DDI_HP_CN_STATE_ENABLED;
+ }
+ }
+}
+
+/*
+ * setup slot name/slot-number info.
+ */
+void
+pciehpc_set_slot_name(pcie_hp_ctrl_t *ctrl_p)
+{
+ pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0];
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
+ uchar_t *slotname_data;
+ int *slotnum;
+ uint_t count;
+ int len;
+ int invalid_slotnum = 0;
+ uint32_t slot_capabilities;
+
+ if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, ctrl_p->hc_dip,
+ DDI_PROP_DONTPASS, "physical-slot#", &slotnum, &count) ==
+ DDI_PROP_SUCCESS) {
+ slot_p->hs_phy_slot_num = slotnum[0];
+ ddi_prop_free(slotnum);
+ } else {
+ slot_capabilities = pciehpc_reg_get32(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTCAP);
+ slot_p->hs_phy_slot_num =
+ PCIE_SLOTCAP_PHY_SLOT_NUM(slot_capabilities);
+ }
+
+ /* platform may not have initialized it */
+ if (!slot_p->hs_phy_slot_num) {
+ PCIE_DBG("%s#%d: Invalid slot number!\n",
+ ddi_driver_name(ctrl_p->hc_dip),
+ ddi_get_instance(ctrl_p->hc_dip));
+ slot_p->hs_phy_slot_num = pciehpc_reg_get8(ctrl_p,
+ PCI_BCNF_SECBUS);
+ invalid_slotnum = 1;
+ }
+ slot_p->hs_info.cn_num = slot_p->hs_phy_slot_num;
+ slot_p->hs_info.cn_num_dpd_on = DDI_HP_CN_NUM_NONE;
+
+ /*
+ * construct the slot_name:
+ * if "slot-names" property exists then use that name
+ * else if valid slot number exists then it is "pcie<slot-num>".
+ * else it will be "pcie<sec-bus-number>dev0"
+ */
+ if (ddi_getlongprop(DDI_DEV_T_ANY, ctrl_p->hc_dip, DDI_PROP_DONTPASS,
+ "slot-names", (caddr_t)&slotname_data, &len) == DDI_PROP_SUCCESS) {
+ char tmp_name[256];
+
+ /*
+ * Note: for PCI-E slots, the device number is always 0 so the
+ * first (and only) string is the slot name for this slot.
+ */
+ (void) snprintf(tmp_name, sizeof (tmp_name),
+ (char *)slotname_data + 4);
+ slot_p->hs_info.cn_name = ddi_strdup(tmp_name, KM_SLEEP);
+ kmem_free(slotname_data, len);
+ } else {
+ if (invalid_slotnum) {
+ /* use device number ie. 0 */
+ slot_p->hs_info.cn_name = ddi_strdup("pcie0",
+ KM_SLEEP);
+ } else {
+ char tmp_name[256];
+
+ (void) snprintf(tmp_name, sizeof (tmp_name), "pcie%d",
+ slot_p->hs_phy_slot_num);
+ slot_p->hs_info.cn_name = ddi_strdup(tmp_name,
+ KM_SLEEP);
+ }
+ }
+}
+
+/*
+ * Read/Write access to HPC registers. If platform nexus has non-standard
+ * HPC access mechanism then regops functions are used to do reads/writes.
+ */
+uint8_t
+pciehpc_reg_get8(pcie_hp_ctrl_t *ctrl_p, uint_t off)
+{
+ if (ctrl_p->hc_regops.get != NULL) {
+ return ((uint8_t)ctrl_p->hc_regops.get(
+ ctrl_p->hc_regops.cookie, (off_t)off));
+ } else {
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
+
+ return (pci_config_get8(bus_p->bus_cfg_hdl, off));
+ }
+}
+
+uint16_t
+pciehpc_reg_get16(pcie_hp_ctrl_t *ctrl_p, uint_t off)
+{
+ if (ctrl_p->hc_regops.get != NULL) {
+ return ((uint16_t)ctrl_p->hc_regops.get(
+ ctrl_p->hc_regops.cookie, (off_t)off));
+ } else {
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
+
+ return (pci_config_get16(bus_p->bus_cfg_hdl, off));
+ }
+}
+
+uint32_t
+pciehpc_reg_get32(pcie_hp_ctrl_t *ctrl_p, uint_t off)
+{
+ if (ctrl_p->hc_regops.get != NULL) {
+ return ((uint32_t)ctrl_p->hc_regops.get(
+ ctrl_p->hc_regops.cookie, (off_t)off));
+ } else {
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
+
+ return (pci_config_get32(bus_p->bus_cfg_hdl, off));
+ }
+}
+
+void
+pciehpc_reg_put8(pcie_hp_ctrl_t *ctrl_p, uint_t off, uint8_t val)
+{
+ if (ctrl_p->hc_regops.put != NULL) {
+ ctrl_p->hc_regops.put(ctrl_p->hc_regops.cookie,
+ (off_t)off, (uint_t)val);
+ } else {
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
+
+ pci_config_put8(bus_p->bus_cfg_hdl, off, val);
+ }
+}
+
+void
+pciehpc_reg_put16(pcie_hp_ctrl_t *ctrl_p, uint_t off, uint16_t val)
+{
+ if (ctrl_p->hc_regops.put != NULL) {
+ ctrl_p->hc_regops.put(ctrl_p->hc_regops.cookie,
+ (off_t)off, (uint_t)val);
+ } else {
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
+
+ pci_config_put16(bus_p->bus_cfg_hdl, off, val);
+ }
+}
+
+void
+pciehpc_reg_put32(pcie_hp_ctrl_t *ctrl_p, uint_t off, uint32_t val)
+{
+ if (ctrl_p->hc_regops.put != NULL) {
+ ctrl_p->hc_regops.put(ctrl_p->hc_regops.cookie,
+ (off_t)off, (uint_t)val);
+ } else {
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
+
+ pci_config_put32(bus_p->bus_cfg_hdl, off, val);
+ }
+}
+
+/*
+ * ************************************************************************
+ * *** Local functions (called within this file)
+ * *** PCIe Native Hotplug mode specific functions
+ * ************************************************************************
+ */
+
+/*
+ * Initialize HPC hardware, install interrupt handler, etc. It doesn't
+ * enable hot plug interrupts.
+ *
+ * (Note: It is called only from pciehpc_init().)
+ */
+static int
+pciehpc_hpc_init(pcie_hp_ctrl_t *ctrl_p)
+{
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
+ uint16_t reg;
+
+ /* read the Slot Control Register */
+ reg = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTCTL);
+
+ /* disable all interrupts */
+ reg &= ~(PCIE_SLOTCTL_INTR_MASK);
+ pciehpc_reg_put16(ctrl_p, bus_p->bus_pcie_off +
+ PCIE_SLOTCTL, reg);
+
+ /* clear any interrupt status bits */
+ reg = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTSTS);
+ pciehpc_reg_put16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTSTS, reg);
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * Uninitialize HPC hardware, uninstall interrupt handler, etc.
+ *
+ * (Note: It is called only from pciehpc_uninit().)
+ */
+static int
+pciehpc_hpc_uninit(pcie_hp_ctrl_t *ctrl_p)
+{
+ /* disable interrupts */
+ (void) pciehpc_disable_intr(ctrl_p);
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * Setup slot information for use with DDI HP framework.
+ */
+static int
+pciehpc_slotinfo_init(pcie_hp_ctrl_t *ctrl_p)
+{
+ uint32_t slot_capabilities, link_capabilities;
+ pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0];
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
+
+ mutex_enter(&ctrl_p->hc_mutex);
+ /*
+ * setup DDI HP framework slot information structure
+ */
+ slot_p->hs_device_num = 0;
+
+ slot_p->hs_info.cn_type = DDI_HP_CN_TYPE_PCIE;
+ slot_p->hs_info.cn_type_str = (ctrl_p->hc_regops.get == NULL) ?
+ PCIE_NATIVE_HP_TYPE : PCIE_PROP_HP_TYPE;
+ slot_p->hs_info.cn_child = NULL;
+
+ slot_p->hs_minor =
+ PCI_MINOR_NUM(ddi_get_instance(ctrl_p->hc_dip),
+ slot_p->hs_device_num);
+ slot_p->hs_condition = AP_COND_UNKNOWN;
+
+ /* read Slot Capabilities Register */
+ slot_capabilities = pciehpc_reg_get32(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTCAP);
+
+ /* set slot-name/slot-number info */
+ pciehpc_set_slot_name(ctrl_p);
+
+ /* check if Attn Button present */
+ ctrl_p->hc_has_attn = (slot_capabilities & PCIE_SLOTCAP_ATTN_BUTTON) ?
+ B_TRUE : B_FALSE;
+
+ /* check if Manual Retention Latch sensor present */
+ ctrl_p->hc_has_mrl = (slot_capabilities & PCIE_SLOTCAP_MRL_SENSOR) ?
+ B_TRUE : B_FALSE;
+
+ /*
+ * PCI-E version 1.1 defines EMI Lock Present bit
+ * in Slot Capabilities register. Check for it.
+ */
+ ctrl_p->hc_has_emi_lock = (slot_capabilities &
+ PCIE_SLOTCAP_EMI_LOCK_PRESENT) ? B_TRUE : B_FALSE;
+
+ link_capabilities = pciehpc_reg_get32(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_LINKCAP);
+ ctrl_p->hc_dll_active_rep = (link_capabilities &
+ PCIE_LINKCAP_DLL_ACTIVE_REP_CAPABLE) ? B_TRUE : B_FALSE;
+ if (ctrl_p->hc_dll_active_rep)
+ cv_init(&slot_p->hs_dll_active_cv, NULL, CV_DRIVER, NULL);
+
+ /* setup thread for handling ATTN button events */
+ if (ctrl_p->hc_has_attn) {
+ PCIE_DBG("pciehpc_slotinfo_init: setting up ATTN button event "
+ "handler thread for slot %d\n", slot_p->hs_phy_slot_num);
+
+ cv_init(&slot_p->hs_attn_btn_cv, NULL, CV_DRIVER, NULL);
+ slot_p->hs_attn_btn_pending = B_FALSE;
+ slot_p->hs_attn_btn_threadp = thread_create(NULL, 0,
+ pciehpc_attn_btn_handler,
+ (void *)ctrl_p, 0, &p0, TS_RUN, minclsyspri);
+ slot_p->hs_attn_btn_thread_exit = B_FALSE;
+ }
+
+ /* get current slot state from the hw */
+ slot_p->hs_info.cn_state = DDI_HP_CN_STATE_EMPTY;
+ pciehpc_get_slot_state(slot_p);
+ if (slot_p->hs_info.cn_state >= DDI_HP_CN_STATE_ENABLED)
+ slot_p->hs_condition = AP_COND_OK;
+
+ mutex_exit(&ctrl_p->hc_mutex);
+
+ return (DDI_SUCCESS);
+}
+
+/*ARGSUSED*/
+static int
+pciehpc_slotinfo_uninit(pcie_hp_ctrl_t *ctrl_p)
+{
+ pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0];
+
+ if (slot_p->hs_attn_btn_threadp != NULL) {
+ mutex_enter(&ctrl_p->hc_mutex);
+ slot_p->hs_attn_btn_thread_exit = B_TRUE;
+ cv_signal(&slot_p->hs_attn_btn_cv);
+ PCIE_DBG("pciehpc_slotinfo_uninit: "
+ "waiting for ATTN thread exit\n");
+ cv_wait(&slot_p->hs_attn_btn_cv, &ctrl_p->hc_mutex);
+ PCIE_DBG("pciehpc_slotinfo_uninit: ATTN thread exit\n");
+ cv_destroy(&slot_p->hs_attn_btn_cv);
+ slot_p->hs_attn_btn_threadp = NULL;
+ mutex_exit(&ctrl_p->hc_mutex);
+ }
+
+ if (ctrl_p->hc_dll_active_rep)
+ cv_destroy(&slot_p->hs_dll_active_cv);
+ if (slot_p->hs_info.cn_name)
+ kmem_free(slot_p->hs_info.cn_name,
+ strlen(slot_p->hs_info.cn_name) + 1);
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * Enable hot plug interrupts.
+ * Note: this is only for Native hot plug mode.
+ */
+static int
+pciehpc_enable_intr(pcie_hp_ctrl_t *ctrl_p)
+{
+ pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0];
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
+ uint16_t reg;
+
+ /* clear any interrupt status bits */
+ reg = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTSTS);
+ pciehpc_reg_put16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTSTS, reg);
+
+ /* read the Slot Control Register */
+ reg = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTCTL);
+
+ /*
+ * enable interrupts: power fault detection interrupt is enabled
+ * only when the slot is powered ON
+ */
+ if (slot_p->hs_info.cn_state >= DDI_HP_CN_STATE_POWERED)
+ pciehpc_reg_put16(ctrl_p, bus_p->bus_pcie_off +
+ PCIE_SLOTCTL, reg | PCIE_SLOTCTL_INTR_MASK);
+ else
+ pciehpc_reg_put16(ctrl_p, bus_p->bus_pcie_off +
+ PCIE_SLOTCTL, reg | (PCIE_SLOTCTL_INTR_MASK &
+ ~PCIE_SLOTCTL_PWR_FAULT_EN));
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * Disable hot plug interrupts.
+ * Note: this is only for Native hot plug mode.
+ */
+static int
+pciehpc_disable_intr(pcie_hp_ctrl_t *ctrl_p)
+{
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
+ uint16_t reg;
+
+ /* read the Slot Control Register */
+ reg = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTCTL);
+
+ /* disable all interrupts */
+ reg &= ~(PCIE_SLOTCTL_INTR_MASK);
+ pciehpc_reg_put16(ctrl_p, bus_p->bus_pcie_off + PCIE_SLOTCTL, reg);
+
+ /* clear any interrupt status bits */
+ reg = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTSTS);
+ pciehpc_reg_put16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTSTS, reg);
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * Allocate a new hotplug controller and slot structures for HPC
+ * associated with this dip.
+ */
+static pcie_hp_ctrl_t *
+pciehpc_create_controller(dev_info_t *dip)
+{
+ pcie_hp_ctrl_t *ctrl_p;
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
+
+ ctrl_p = kmem_zalloc(sizeof (pcie_hp_ctrl_t), KM_SLEEP);
+ ctrl_p->hc_dip = dip;
+
+ /* Allocate a new slot structure. */
+ ctrl_p->hc_slots[0] = kmem_zalloc(sizeof (pcie_hp_slot_t), KM_SLEEP);
+ ctrl_p->hc_slots[0]->hs_num = 0;
+ ctrl_p->hc_slots[0]->hs_ctrl = ctrl_p;
+
+ /* Initialize the interrupt mutex */
+ mutex_init(&ctrl_p->hc_mutex, NULL, MUTEX_DRIVER,
+ (void *)PCIE_INTR_PRI);
+
+ /* Initialize synchronization conditional variable */
+ cv_init(&ctrl_p->hc_cmd_comp_cv, NULL, CV_DRIVER, NULL);
+ ctrl_p->hc_cmd_pending = B_FALSE;
+
+ bus_p->bus_hp_curr_mode = PCIE_NATIVE_HP_MODE;
+ PCIE_SET_HP_CTRL(dip, ctrl_p);
+
+ return (ctrl_p);
+}
+
+/*
+ * Remove the HPC controller and slot structures
+ */
+static void
+pciehpc_destroy_controller(dev_info_t *dip)
+{
+ pcie_hp_ctrl_t *ctrl_p;
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
+
+ /* get the soft state structure for this dip */
+ if ((ctrl_p = PCIE_GET_HP_CTRL(dip)) == NULL)
+ return;
+
+ PCIE_SET_HP_CTRL(dip, NULL);
+ bus_p->bus_hp_curr_mode = PCIE_NONE_HP_MODE;
+
+ mutex_destroy(&ctrl_p->hc_mutex);
+ cv_destroy(&ctrl_p->hc_cmd_comp_cv);
+ kmem_free(ctrl_p->hc_slots[0], sizeof (pcie_hp_slot_t));
+ kmem_free(ctrl_p, sizeof (pcie_hp_ctrl_t));
+}
+
+/*
+ * Register the PCI-E hot plug slot with DDI HP framework.
+ */
+static int
+pciehpc_register_slot(pcie_hp_ctrl_t *ctrl_p)
+{
+ pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0];
+ dev_info_t *dip = ctrl_p->hc_dip;
+
+ /* register the slot with DDI HP framework */
+ if (ndi_hp_register(dip, &slot_p->hs_info) != NDI_SUCCESS) {
+ PCIE_DBG("pciehpc_register_slot() failed to register slot %d\n",
+ slot_p->hs_phy_slot_num);
+ return (DDI_FAILURE);
+ }
+
+ pcie_hp_create_occupant_props(dip, makedevice(ddi_driver_major(dip),
+ slot_p->hs_minor), slot_p->hs_device_num);
+
+ PCIE_DBG("pciehpc_register_slot(): registered slot %d\n",
+ slot_p->hs_phy_slot_num);
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * Unregister the PCI-E hot plug slot from DDI HP framework.
+ */
+static int
+pciehpc_unregister_slot(pcie_hp_ctrl_t *ctrl_p)
+{
+ pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0];
+ dev_info_t *dip = ctrl_p->hc_dip;
+
+ pcie_hp_delete_occupant_props(dip, makedevice(ddi_driver_major(dip),
+ slot_p->hs_minor));
+
+ /* unregister the slot with DDI HP framework */
+ if (ndi_hp_unregister(dip, slot_p->hs_info.cn_name) != NDI_SUCCESS) {
+ PCIE_DBG("pciehpc_unregister_slot() "
+ "failed to unregister slot %d\n", slot_p->hs_phy_slot_num);
+ return (DDI_FAILURE);
+ }
+
+ PCIE_DBG("pciehpc_unregister_slot(): unregistered slot %d\n",
+ slot_p->hs_phy_slot_num);
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * pciehpc_slot_poweron()
+ *
+ * Poweron/Enable the slot.
+ *
+ * Note: This function is called by DDI HP framework at kernel context only
+ */
+/*ARGSUSED*/
+static int
+pciehpc_slot_poweron(pcie_hp_slot_t *slot_p, ddi_hp_cn_state_t *result)
+{
+ pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl;
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
+ uint16_t status, control;
+
+ ASSERT(MUTEX_HELD(&ctrl_p->hc_mutex));
+
+ /* get the current state of the slot */
+ pciehpc_get_slot_state(slot_p);
+
+ /* check if the slot is already in the 'enabled' state */
+ if (slot_p->hs_info.cn_state >= DDI_HP_CN_STATE_POWERED) {
+ /* slot is already in the 'enabled' state */
+ PCIE_DBG("pciehpc_slot_poweron() slot %d already enabled\n",
+ slot_p->hs_phy_slot_num);
+
+ *result = slot_p->hs_info.cn_state;
+ return (DDI_SUCCESS);
+ }
+
+ /* read the Slot Status Register */
+ status = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTSTS);
+
+ /* make sure the MRL switch is closed if present */
+ if ((ctrl_p->hc_has_mrl) && (status & PCIE_SLOTSTS_MRL_SENSOR_OPEN)) {
+ /* MRL switch is open */
+ cmn_err(CE_WARN, "MRL switch is open on slot %d\n",
+ slot_p->hs_phy_slot_num);
+ goto cleanup;
+ }
+
+ /* make sure the slot has a device present */
+ if (!(status & PCIE_SLOTSTS_PRESENCE_DETECTED)) {
+ /* slot is empty */
+ PCIE_DBG("slot %d is empty\n", slot_p->hs_phy_slot_num);
+ goto cleanup;
+ }
+
+ /* get the current state of Slot Control Register */
+ control = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTCTL);
+
+ /*
+ * Enable power to the slot involves:
+ * 1. Set power LED to blink and ATTN led to OFF.
+ * 2. Set power control ON in Slot Control Reigster and
+ * wait for Command Completed Interrupt or 1 sec timeout.
+ * 3. If Data Link Layer State Changed events are supported
+ * then wait for the event to indicate Data Layer Link
+ * is active. The time out value for this event is 1 second.
+ * This is specified in PCI-E version 1.1.
+ * 4. Set power LED to be ON.
+ */
+
+ /* 1. set power LED to blink & ATTN led to OFF */
+ pciehpc_set_led_state(ctrl_p, PCIE_HP_POWER_LED, PCIE_HP_LED_BLINK);
+ pciehpc_set_led_state(ctrl_p, PCIE_HP_ATTN_LED, PCIE_HP_LED_OFF);
+
+ /* 2. set power control to ON */
+ control = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTCTL);
+ control &= ~PCIE_SLOTCTL_PWR_CONTROL;
+ pciehpc_issue_hpc_command(ctrl_p, control);
+
+ /* 3. wait for DLL State Change event, if it's supported */
+ if (ctrl_p->hc_dll_active_rep) {
+ status = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_LINKSTS);
+
+ if (!(status & PCIE_LINKSTS_DLL_LINK_ACTIVE)) {
+ /* wait 1 sec for the DLL State Changed event */
+ (void) cv_timedwait(&slot_p->hs_dll_active_cv,
+ &ctrl_p->hc_mutex,
+ ddi_get_lbolt() +
+ SEC_TO_TICK(PCIE_HP_DLL_STATE_CHANGE_TIMEOUT));
+
+ /* check Link status */
+ status = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off +
+ PCIE_LINKSTS);
+ if (!(status & PCIE_LINKSTS_DLL_LINK_ACTIVE))
+ goto cleanup2;
+ }
+ }
+
+ /* wait 1 sec for link to come up */
+ delay(drv_usectohz(1000000));
+
+ /* check power is really turned ON */
+ control = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTCTL);
+
+ if (control & PCIE_SLOTCTL_PWR_CONTROL) {
+ PCIE_DBG("slot %d fails to turn on power on connect\n",
+ slot_p->hs_phy_slot_num);
+
+ goto cleanup1;
+ }
+
+ /* clear power fault status */
+ status = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTSTS);
+ status |= PCIE_SLOTSTS_PWR_FAULT_DETECTED;
+ pciehpc_reg_put16(ctrl_p, bus_p->bus_pcie_off + PCIE_SLOTSTS,
+ status);
+
+ /* enable power fault detection interrupt */
+ control |= PCIE_SLOTCTL_PWR_FAULT_EN;
+ pciehpc_issue_hpc_command(ctrl_p, control);
+
+ /* 4. Set power LED to be ON */
+ pciehpc_set_led_state(ctrl_p, PCIE_HP_POWER_LED, PCIE_HP_LED_ON);
+
+ /* if EMI is present, turn it ON */
+ if (ctrl_p->hc_has_emi_lock) {
+ status = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTSTS);
+
+ if (!(status & PCIE_SLOTSTS_EMI_LOCK_SET)) {
+ control = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTCTL);
+ control |= PCIE_SLOTCTL_EMI_LOCK_CONTROL;
+ pciehpc_issue_hpc_command(ctrl_p, control);
+
+ /* wait 1 sec after toggling the state of EMI lock */
+ delay(drv_usectohz(1000000));
+ }
+ }
+
+ *result = slot_p->hs_info.cn_state =
+ DDI_HP_CN_STATE_POWERED;
+
+ return (DDI_SUCCESS);
+
+cleanup2:
+ control = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTCTL);
+
+ /* if power is ON, set power control to OFF */
+ if (!(control & PCIE_SLOTCTL_PWR_CONTROL)) {
+ control |= PCIE_SLOTCTL_PWR_CONTROL;
+ pciehpc_issue_hpc_command(ctrl_p, control);
+ }
+
+cleanup1:
+ /* set power led to OFF */
+ pciehpc_set_led_state(ctrl_p, PCIE_HP_POWER_LED, PCIE_HP_LED_OFF);
+
+cleanup:
+ return (DDI_FAILURE);
+}
+
+/*ARGSUSED*/
+static int
+pciehpc_slot_poweroff(pcie_hp_slot_t *slot_p, ddi_hp_cn_state_t *result)
+{
+ pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl;
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
+ uint16_t status, control;
+
+ ASSERT(MUTEX_HELD(&ctrl_p->hc_mutex));
+
+ /* get the current state of the slot */
+ pciehpc_get_slot_state(slot_p);
+
+ /* check if the slot is not in the "enabled' state */
+ if (slot_p->hs_info.cn_state < DDI_HP_CN_STATE_POWERED) {
+ /* slot is in the 'disabled' state */
+ PCIE_DBG("pciehpc_slot_poweroff(): "
+ "slot %d already disabled\n", slot_p->hs_phy_slot_num);
+ ASSERT(slot_p->hs_power_led_state == PCIE_HP_LED_OFF);
+
+ *result = slot_p->hs_info.cn_state;
+ return (DDI_SUCCESS);
+ }
+
+ /* read the Slot Status Register */
+ status = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTSTS);
+
+ /* make sure the slot has a device present */
+ if (!(status & PCIE_SLOTSTS_PRESENCE_DETECTED)) {
+ /* slot is empty */
+ PCIE_DBG("pciehpc_slot_poweroff(): slot %d is empty\n",
+ slot_p->hs_phy_slot_num);
+ goto cleanup;
+ }
+
+ /*
+ * Disable power to the slot involves:
+ * 1. Set power LED to blink.
+ * 2. Set power control OFF in Slot Control Reigster and
+ * wait for Command Completed Interrupt or 1 sec timeout.
+ * 3. Set POWER led and ATTN led to be OFF.
+ */
+
+ /* 1. set power LED to blink */
+ pciehpc_set_led_state(ctrl_p, PCIE_HP_POWER_LED, PCIE_HP_LED_BLINK);
+
+ /* disable power fault detection interrupt */
+ control = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTCTL);
+ control &= ~PCIE_SLOTCTL_PWR_FAULT_EN;
+ pciehpc_issue_hpc_command(ctrl_p, control);
+
+ /* 2. set power control to OFF */
+ control = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTCTL);
+ control |= PCIE_SLOTCTL_PWR_CONTROL;
+ pciehpc_issue_hpc_command(ctrl_p, control);
+
+#ifdef DEBUG
+ /* check for power control bit to be OFF */
+ control = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTCTL);
+ ASSERT(control & PCIE_SLOTCTL_PWR_CONTROL);
+#endif
+
+ /* 3. Set power LED to be OFF */
+ pciehpc_set_led_state(ctrl_p, PCIE_HP_POWER_LED, PCIE_HP_LED_OFF);
+ pciehpc_set_led_state(ctrl_p, PCIE_HP_ATTN_LED, PCIE_HP_LED_OFF);
+
+ /* if EMI is present, turn it OFF */
+ if (ctrl_p->hc_has_emi_lock) {
+ status = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTSTS);
+
+ if (status & PCIE_SLOTSTS_EMI_LOCK_SET) {
+ control = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTCTL);
+ control |= PCIE_SLOTCTL_EMI_LOCK_CONTROL;
+ pciehpc_issue_hpc_command(ctrl_p, control);
+
+ /* wait 1 sec after toggling the state of EMI lock */
+ delay(drv_usectohz(1000000));
+ }
+ }
+
+ /* get the current state of the slot */
+ pciehpc_get_slot_state(slot_p);
+
+ *result = slot_p->hs_info.cn_state;
+
+ return (DDI_SUCCESS);
+
+cleanup:
+ return (DDI_FAILURE);
+}
+
+/*
+ * pciehpc_slot_probe()
+ *
+ * Probe the slot.
+ *
+ * Note: This function is called by DDI HP framework at kernel context only
+ */
+/*ARGSUSED*/
+static int
+pciehpc_slot_probe(pcie_hp_slot_t *slot_p)
+{
+ pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl;
+ int ret = DDI_SUCCESS;
+
+ mutex_enter(&ctrl_p->hc_mutex);
+
+ /* get the current state of the slot */
+ pciehpc_get_slot_state(slot_p);
+
+ /*
+ * Probe a given PCIe Hotplug Connection (CN).
+ */
+ PCIE_DISABLE_ERRORS(ctrl_p->hc_dip);
+ ret = pcie_hp_probe(slot_p);
+
+ if (ret != DDI_SUCCESS) {
+ PCIE_DBG("pciehpc_slot_probe() failed\n");
+
+ /* turn the ATTN led ON for configure failure */
+ pciehpc_set_led_state(ctrl_p, PCIE_HP_ATTN_LED, PCIE_HP_LED_ON);
+
+ /* if power to the slot is still on then set Power led to ON */
+ if (slot_p->hs_info.cn_state >= DDI_HP_CN_STATE_POWERED)
+ pciehpc_set_led_state(ctrl_p, PCIE_HP_POWER_LED,
+ PCIE_HP_LED_ON);
+
+ mutex_exit(&ctrl_p->hc_mutex);
+ return (DDI_FAILURE);
+ }
+
+ PCIE_ENABLE_ERRORS(ctrl_p->hc_dip);
+
+ /* get the current state of the slot */
+ pciehpc_get_slot_state(slot_p);
+
+ mutex_exit(&ctrl_p->hc_mutex);
+ return (DDI_SUCCESS);
+}
+
+/*
+ * pciehpc_slot_unprobe()
+ *
+ * Unprobe the slot.
+ *
+ * Note: This function is called by DDI HP framework at kernel context only
+ */
+/*ARGSUSED*/
+static int
+pciehpc_slot_unprobe(pcie_hp_slot_t *slot_p)
+{
+ pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl;
+ int ret;
+
+ mutex_enter(&ctrl_p->hc_mutex);
+
+ /* get the current state of the slot */
+ pciehpc_get_slot_state(slot_p);
+
+ /*
+ * Unprobe a given PCIe Hotplug Connection (CN).
+ */
+ PCIE_DISABLE_ERRORS(ctrl_p->hc_dip);
+ ret = pcie_hp_unprobe(slot_p);
+
+ if (ret != DDI_SUCCESS) {
+ PCIE_DBG("pciehpc_slot_unprobe() failed\n");
+
+ /* if power to the slot is still on then set Power led to ON */
+ if (slot_p->hs_info.cn_state >= DDI_HP_CN_STATE_POWERED)
+ pciehpc_set_led_state(ctrl_p, PCIE_HP_POWER_LED,
+ PCIE_HP_LED_ON);
+
+ PCIE_ENABLE_ERRORS(ctrl_p->hc_dip);
+
+ mutex_exit(&ctrl_p->hc_mutex);
+ return (DDI_FAILURE);
+ }
+
+ /* get the current state of the slot */
+ pciehpc_get_slot_state(slot_p);
+
+ mutex_exit(&ctrl_p->hc_mutex);
+ return (DDI_SUCCESS);
+}
+
+static int
+pciehpc_upgrade_slot_state(pcie_hp_slot_t *slot_p,
+ ddi_hp_cn_state_t target_state)
+{
+ ddi_hp_cn_state_t curr_state;
+ int rv = DDI_SUCCESS;
+
+ if (target_state > DDI_HP_CN_STATE_ENABLED) {
+ return (DDI_EINVAL);
+ }
+
+ curr_state = slot_p->hs_info.cn_state;
+ while ((curr_state < target_state) && (rv == DDI_SUCCESS)) {
+
+ switch (curr_state) {
+ case DDI_HP_CN_STATE_EMPTY:
+ /*
+ * From EMPTY to PRESENT, just check the hardware
+ * slot state.
+ */
+ pciehpc_get_slot_state(slot_p);
+ curr_state = slot_p->hs_info.cn_state;
+ if (curr_state < DDI_HP_CN_STATE_PRESENT)
+ rv = DDI_FAILURE;
+ break;
+ case DDI_HP_CN_STATE_PRESENT:
+ rv = (slot_p->hs_ctrl->hc_ops.poweron_hpc_slot)(slot_p,
+ &curr_state);
+
+ break;
+ case DDI_HP_CN_STATE_POWERED:
+ curr_state = slot_p->hs_info.cn_state =
+ DDI_HP_CN_STATE_ENABLED;
+ break;
+ default:
+ /* should never reach here */
+ ASSERT("unknown devinfo state");
+ }
+ }
+
+ return (rv);
+}
+
+static int
+pciehpc_downgrade_slot_state(pcie_hp_slot_t *slot_p,
+ ddi_hp_cn_state_t target_state)
+{
+ ddi_hp_cn_state_t curr_state;
+ int rv = DDI_SUCCESS;
+
+
+ curr_state = slot_p->hs_info.cn_state;
+ while ((curr_state > target_state) && (rv == DDI_SUCCESS)) {
+
+ switch (curr_state) {
+ case DDI_HP_CN_STATE_PRESENT:
+ /*
+ * From PRESENT to EMPTY, just check hardware slot
+ * state.
+ */
+ pciehpc_get_slot_state(slot_p);
+ curr_state = slot_p->hs_info.cn_state;
+ if (curr_state >= DDI_HP_CN_STATE_PRESENT)
+ rv = DDI_FAILURE;
+ break;
+ case DDI_HP_CN_STATE_POWERED:
+ rv = (slot_p->hs_ctrl->hc_ops.poweroff_hpc_slot)(
+ slot_p, &curr_state);
+
+ break;
+ case DDI_HP_CN_STATE_ENABLED:
+ curr_state = slot_p->hs_info.cn_state =
+ DDI_HP_CN_STATE_POWERED;
+
+ break;
+ default:
+ /* should never reach here */
+ ASSERT("unknown devinfo state");
+ }
+ }
+
+ return (rv);
+}
+
+/* Change slot state to a target state */
+static int
+pciehpc_change_slot_state(pcie_hp_slot_t *slot_p,
+ ddi_hp_cn_state_t target_state)
+{
+ ddi_hp_cn_state_t curr_state;
+ int rv;
+
+ pciehpc_get_slot_state(slot_p);
+ curr_state = slot_p->hs_info.cn_state;
+
+ if (curr_state == target_state) {
+ return (DDI_SUCCESS);
+ }
+ if (curr_state < target_state) {
+
+ rv = pciehpc_upgrade_slot_state(slot_p, target_state);
+ } else {
+ rv = pciehpc_downgrade_slot_state(slot_p, target_state);
+ }
+
+ return (rv);
+}
+
+int
+pciehpc_slot_get_property(pcie_hp_slot_t *slot_p, ddi_hp_property_t *arg,
+ ddi_hp_property_t *rval)
+{
+ ddi_hp_property_t request, result;
+#ifdef _SYSCALL32_IMPL
+ ddi_hp_property32_t request32, result32;
+#endif
+ pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl;
+ nvlist_t *prop_list;
+ nvlist_t *prop_rlist; /* nvlist for return values */
+ nvpair_t *prop_pair;
+ char *name, *value;
+ int ret = DDI_SUCCESS;
+ int i, n;
+ boolean_t get_all_prop = B_FALSE;
+
+ if (get_udatamodel() == DATAMODEL_NATIVE) {
+ if (copyin(arg, &request, sizeof (ddi_hp_property_t)) ||
+ copyin(rval, &result, sizeof (ddi_hp_property_t)))
+ return (DDI_FAILURE);
+ }
+#ifdef _SYSCALL32_IMPL
+ else {
+ bzero(&request, sizeof (request));
+ bzero(&result, sizeof (result));
+ if (copyin(arg, &request32, sizeof (ddi_hp_property32_t)) ||
+ copyin(rval, &result32, sizeof (ddi_hp_property32_t)))
+ return (DDI_FAILURE);
+ request.nvlist_buf = (char *)(uintptr_t)request32.nvlist_buf;
+ request.buf_size = request32.buf_size;
+ result.nvlist_buf = (char *)(uintptr_t)result32.nvlist_buf;
+ result.buf_size = result32.buf_size;
+ }
+#endif
+
+ if ((ret = pcie_copyin_nvlist(request.nvlist_buf, request.buf_size,
+ &prop_list)) != DDI_SUCCESS)
+ return (ret);
+
+ if (nvlist_alloc(&prop_rlist, NV_UNIQUE_NAME, 0)) {
+ ret = DDI_ENOMEM;
+ goto get_prop_cleanup;
+ }
+
+ /* check whether the requested property is "all" or "help" */
+ prop_pair = nvlist_next_nvpair(prop_list, NULL);
+ if (prop_pair && !nvlist_next_nvpair(prop_list, prop_pair)) {
+ name = nvpair_name(prop_pair);
+ n = sizeof (pciehpc_props) / sizeof (pciehpc_prop_t);
+
+ if (strcmp(name, PCIEHPC_PROP_ALL) == 0) {
+ (void) nvlist_remove_all(prop_list, PCIEHPC_PROP_ALL);
+
+ /*
+ * Add all properties into the request list, so that we
+ * will get the values in the following for loop.
+ */
+ for (i = 0; i < n; i++) {
+ if (nvlist_add_string(prop_list,
+ pciehpc_props[i].prop_name, "") != 0) {
+ ret = DDI_FAILURE;
+ goto get_prop_cleanup1;
+ }
+ }
+ get_all_prop = B_TRUE;
+ } else if (strcmp(name, PCIEHPC_PROP_HELP) == 0) {
+ /*
+ * Empty the request list, and add help strings into the
+ * return list. We will pass the following for loop.
+ */
+ (void) nvlist_remove_all(prop_list, PCIEHPC_PROP_HELP);
+
+ for (i = 0; i < n; i++) {
+ if (nvlist_add_string(prop_rlist,
+ pciehpc_props[i].prop_name,
+ pciehpc_props[i].prop_value) != 0) {
+ ret = DDI_FAILURE;
+ goto get_prop_cleanup1;
+ }
+ }
+ }
+ }
+
+ mutex_enter(&ctrl_p->hc_mutex);
+
+ /* get the current slot state */
+ pciehpc_get_slot_state(slot_p);
+
+ /* for each requested property, get the value and add it to nvlist */
+ prop_pair = NULL;
+ while (prop_pair = nvlist_next_nvpair(prop_list, prop_pair)) {
+ name = nvpair_name(prop_pair);
+
+ if (strcmp(name, PCIEHPC_PROP_LED_FAULT) == 0) {
+ value = pcie_led_state_text(
+ slot_p->hs_fault_led_state);
+ } else if (strcmp(name, PCIEHPC_PROP_LED_POWER) == 0) {
+ value = pcie_led_state_text(
+ slot_p->hs_power_led_state);
+ } else if (strcmp(name, PCIEHPC_PROP_LED_ATTN) == 0) {
+ value = pcie_led_state_text(
+ slot_p->hs_attn_led_state);
+ } else if (strcmp(name, PCIEHPC_PROP_LED_ACTIVE) == 0) {
+ value = pcie_led_state_text(
+ slot_p->hs_active_led_state);
+ } else if (strcmp(name, PCIEHPC_PROP_CARD_TYPE) == 0) {
+ ddi_acc_handle_t handle;
+ dev_info_t *cdip;
+ uint8_t prog_class, base_class, sub_class;
+ int i;
+
+ mutex_exit(&ctrl_p->hc_mutex);
+ cdip = pcie_hp_devi_find(
+ ctrl_p->hc_dip, slot_p->hs_device_num, 0);
+ mutex_enter(&ctrl_p->hc_mutex);
+
+ if ((slot_p->hs_info.cn_state
+ != DDI_HP_CN_STATE_ENABLED) || (cdip == NULL)) {
+ /*
+ * When getting all properties, just ignore the
+ * one that's not available under certain state.
+ */
+ if (get_all_prop)
+ continue;
+
+ ret = DDI_ENOTSUP;
+ goto get_prop_cleanup2;
+ }
+
+ if (pci_config_setup(cdip, &handle) != DDI_SUCCESS) {
+ ret = DDI_FAILURE;
+ goto get_prop_cleanup2;
+ }
+
+ prog_class = pci_config_get8(handle,
+ PCI_CONF_PROGCLASS);
+ base_class = pci_config_get8(handle, PCI_CONF_BASCLASS);
+ sub_class = pci_config_get8(handle, PCI_CONF_SUBCLASS);
+ pci_config_teardown(&handle);
+
+ for (i = 0; i < class_pci_items; i++) {
+ if ((base_class == class_pci[i].base_class) &&
+ (sub_class == class_pci[i].sub_class) &&
+ (prog_class == class_pci[i].prog_class)) {
+ value = class_pci[i].short_desc;
+ break;
+ }
+ }
+ if (i == class_pci_items)
+ value = PCIEHPC_PROP_VALUE_UNKNOWN;
+ } else if (strcmp(name, PCIEHPC_PROP_BOARD_TYPE) == 0) {
+ if (slot_p->hs_info.cn_state <= DDI_HP_CN_STATE_EMPTY)
+ value = PCIEHPC_PROP_VALUE_UNKNOWN;
+ else
+ value = PCIEHPC_PROP_VALUE_PCIHOTPLUG;
+ } else if (strcmp(name, PCIEHPC_PROP_SLOT_CONDITION) == 0) {
+ value = pcie_slot_condition_text(slot_p->hs_condition);
+ } else {
+ /* unsupported property */
+ cmn_err(CE_WARN, "Unsupported property: %s\n", name);
+
+ ret = DDI_ENOTSUP;
+ goto get_prop_cleanup2;
+ }
+ if (nvlist_add_string(prop_rlist, name, value) != 0) {
+ ret = DDI_FAILURE;
+ goto get_prop_cleanup2;
+ }
+ }
+
+ /* pack nvlist and copyout */
+ if ((ret = pcie_copyout_nvlist(prop_rlist, result.nvlist_buf,
+ &result.buf_size)) != DDI_SUCCESS) {
+ goto get_prop_cleanup2;
+ }
+ if (get_udatamodel() == DATAMODEL_NATIVE) {
+ if (copyout(&result, rval, sizeof (ddi_hp_property_t)))
+ ret = DDI_FAILURE;
+ }
+#ifdef _SYSCALL32_IMPL
+ else {
+ if (result.buf_size > UINT32_MAX) {
+ ret = DDI_FAILURE;
+ } else {
+ result32.buf_size = (uint32_t)result.buf_size;
+ if (copyout(&result32, rval,
+ sizeof (ddi_hp_property32_t)))
+ ret = DDI_FAILURE;
+ }
+ }
+#endif
+
+get_prop_cleanup2:
+ mutex_exit(&ctrl_p->hc_mutex);
+get_prop_cleanup1:
+ nvlist_free(prop_rlist);
+get_prop_cleanup:
+ nvlist_free(prop_list);
+ return (ret);
+}
+
+int
+pciehpc_slot_set_property(pcie_hp_slot_t *slot_p, ddi_hp_property_t *arg,
+ ddi_hp_property_t *rval)
+{
+ ddi_hp_property_t request, result;
+#ifdef _SYSCALL32_IMPL
+ ddi_hp_property32_t request32, result32;
+#endif
+ pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl;
+ nvlist_t *prop_list;
+ nvlist_t *prop_rlist;
+ nvpair_t *prop_pair;
+ char *name, *value;
+ pcie_hp_led_state_t led_state;
+ int ret = DDI_SUCCESS;
+
+ if (get_udatamodel() == DATAMODEL_NATIVE) {
+ if (copyin(arg, &request, sizeof (ddi_hp_property_t)))
+ return (DDI_FAILURE);
+ if (rval &&
+ copyin(rval, &result, sizeof (ddi_hp_property_t)))
+ return (DDI_FAILURE);
+ }
+#ifdef _SYSCALL32_IMPL
+ else {
+ bzero(&request, sizeof (request));
+ bzero(&result, sizeof (result));
+ if (copyin(arg, &request32, sizeof (ddi_hp_property32_t)))
+ return (DDI_FAILURE);
+ if (rval &&
+ copyin(rval, &result32, sizeof (ddi_hp_property32_t)))
+ return (DDI_FAILURE);
+ request.nvlist_buf = (char *)(uintptr_t)request32.nvlist_buf;
+ request.buf_size = request32.buf_size;
+ if (rval) {
+ result.nvlist_buf =
+ (char *)(uintptr_t)result32.nvlist_buf;
+ result.buf_size = result32.buf_size;
+ }
+ }
+#endif
+
+ if ((ret = pcie_copyin_nvlist(request.nvlist_buf, request.buf_size,
+ &prop_list)) != DDI_SUCCESS)
+ return (ret);
+
+ /* check whether the requested property is "help" */
+ prop_pair = nvlist_next_nvpair(prop_list, NULL);
+ if (prop_pair && !nvlist_next_nvpair(prop_list, prop_pair) &&
+ (strcmp(nvpair_name(prop_pair), PCIEHPC_PROP_HELP) == 0)) {
+ if (!rval) {
+ ret = DDI_ENOTSUP;
+ goto set_prop_cleanup;
+ }
+
+ if (nvlist_alloc(&prop_rlist, NV_UNIQUE_NAME, 0)) {
+ ret = DDI_ENOMEM;
+ goto set_prop_cleanup;
+ }
+ if (nvlist_add_string(prop_rlist, PCIEHPC_PROP_LED_ATTN,
+ PCIEHPC_PROP_VALUE_LED) != 0) {
+ ret = DDI_FAILURE;
+ goto set_prop_cleanup1;
+ }
+
+ if ((ret = pcie_copyout_nvlist(prop_rlist, result.nvlist_buf,
+ &result.buf_size)) != DDI_SUCCESS) {
+ goto set_prop_cleanup1;
+ }
+ if (get_udatamodel() == DATAMODEL_NATIVE) {
+ if (copyout(&result, rval,
+ sizeof (ddi_hp_property_t))) {
+ ret = DDI_FAILURE;
+ goto set_prop_cleanup1;
+ }
+ }
+#ifdef _SYSCALL32_IMPL
+ else {
+ if (result.buf_size > UINT32_MAX) {
+ ret = DDI_FAILURE;
+ goto set_prop_cleanup1;
+ } else {
+ result32.buf_size = (uint32_t)result.buf_size;
+ if (copyout(&result32, rval,
+ sizeof (ddi_hp_property32_t))) {
+ ret = DDI_FAILURE;
+ goto set_prop_cleanup1;
+ }
+ }
+ }
+#endif
+set_prop_cleanup1:
+ nvlist_free(prop_rlist);
+ nvlist_free(prop_list);
+ return (ret);
+ }
+
+ /* Validate the request */
+ prop_pair = NULL;
+ while (prop_pair = nvlist_next_nvpair(prop_list, prop_pair)) {
+ name = nvpair_name(prop_pair);
+ if (nvpair_type(prop_pair) != DATA_TYPE_STRING) {
+ cmn_err(CE_WARN, "Unexpected data type of setting "
+ "property %s.\n", name);
+ ret = DDI_EINVAL;
+ goto set_prop_cleanup;
+ }
+ if (nvpair_value_string(prop_pair, &value)) {
+ cmn_err(CE_WARN, "Get string value failed for property "
+ "%s.\n", name);
+ ret = DDI_FAILURE;
+ goto set_prop_cleanup;
+ }
+
+ if (strcmp(name, PCIEHPC_PROP_LED_ATTN) == 0) {
+ if ((strcmp(value, PCIEHPC_PROP_VALUE_ON) != 0) &&
+ (strcmp(value, PCIEHPC_PROP_VALUE_OFF) != 0) &&
+ (strcmp(value, PCIEHPC_PROP_VALUE_BLINK) != 0)) {
+ cmn_err(CE_WARN, "Unsupported value of setting "
+ "property %s\n", name);
+ ret = DDI_ENOTSUP;
+ goto set_prop_cleanup;
+ }
+ } else {
+ cmn_err(CE_WARN, "Unsupported property: %s\n", name);
+ ret = DDI_ENOTSUP;
+ goto set_prop_cleanup;
+ }
+ }
+ mutex_enter(&ctrl_p->hc_mutex);
+
+ /* get the current slot state */
+ pciehpc_get_slot_state(slot_p);
+
+ /* set each property */
+ prop_pair = NULL;
+ while (prop_pair = nvlist_next_nvpair(prop_list, prop_pair)) {
+ name = nvpair_name(prop_pair);
+
+ if (strcmp(name, PCIEHPC_PROP_LED_ATTN) == 0) {
+ if (strcmp(value, PCIEHPC_PROP_VALUE_ON) == 0)
+ led_state = PCIE_HP_LED_ON;
+ else if (strcmp(value, PCIEHPC_PROP_VALUE_OFF) == 0)
+ led_state = PCIE_HP_LED_OFF;
+ else if (strcmp(value, PCIEHPC_PROP_VALUE_BLINK) == 0)
+ led_state = PCIE_HP_LED_BLINK;
+
+ pciehpc_set_led_state(ctrl_p, PCIE_HP_ATTN_LED,
+ led_state);
+ }
+ }
+
+ mutex_exit(&ctrl_p->hc_mutex);
+set_prop_cleanup:
+ nvlist_free(prop_list);
+ return (ret);
+}
+
+/*
+ * Send a command to the PCI-E Hot Plug Controller.
+ *
+ * NOTES: The PCI-E spec defines the following semantics for issuing hot plug
+ * commands.
+ * 1) If Command Complete events/interrupts are supported then software
+ * waits for Command Complete event after issuing a command (i.e writing
+ * to the Slot Control register). The command completion could take as
+ * long as 1 second so software should be prepared to wait for 1 second
+ * before issuing another command.
+ *
+ * 2) If Command Complete events/interrupts are not supported then
+ * software could issue multiple Slot Control writes without any delay
+ * between writes.
+ */
+static void
+pciehpc_issue_hpc_command(pcie_hp_ctrl_t *ctrl_p, uint16_t control)
+{
+ pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0];
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
+ uint16_t status;
+ uint32_t slot_cap;
+
+ /*
+ * PCI-E version 1.1 spec defines No Command Completed
+ * Support bit (bit#18) in Slot Capabilities register. If this
+ * bit is set then slot doesn't support notification of command
+ * completion events.
+ */
+ slot_cap = pciehpc_reg_get32(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTCAP);
+
+ /*
+ * If no Command Completion event is supported or it is ACPI
+ * hot plug mode then just issue the command and return.
+ */
+ if ((slot_cap & PCIE_SLOTCAP_NO_CMD_COMP_SUPP) ||
+ (bus_p->bus_hp_curr_mode == PCIE_ACPI_HP_MODE)) {
+ pciehpc_reg_put16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTCTL, control);
+ return;
+ }
+
+ /*
+ * **************************************
+ * Command Complete events are supported.
+ * **************************************
+ */
+
+ /*
+ * If HPC is not yet initialized then just poll for the Command
+ * Completion interrupt.
+ */
+ if (!(ctrl_p->hc_flags & PCIE_HP_INITIALIZED_FLAG)) {
+ int retry = PCIE_HP_CMD_WAIT_RETRY;
+
+ /* write the command to the HPC */
+ pciehpc_reg_put16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTCTL, control);
+
+ /* poll for status completion */
+ while (retry--) {
+ /* wait for 10 msec before checking the status */
+ delay(drv_usectohz(PCIE_HP_CMD_WAIT_TIME));
+
+ status = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTSTS);
+
+ if (status & PCIE_SLOTSTS_COMMAND_COMPLETED) {
+ /* clear the status bits */
+ pciehpc_reg_put16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTSTS, status);
+ break;
+ }
+ }
+ return;
+ }
+
+ /* HPC is already initialized */
+
+ ASSERT(MUTEX_HELD(&ctrl_p->hc_mutex));
+
+ /*
+ * If previous command is still pending then wait for its
+ * completion. i.e cv_wait()
+ */
+
+ while (ctrl_p->hc_cmd_pending == B_TRUE)
+ cv_wait(&ctrl_p->hc_cmd_comp_cv, &ctrl_p->hc_mutex);
+
+ /*
+ * Issue the command and wait for Command Completion or
+ * the 1 sec timeout.
+ */
+ pciehpc_reg_put16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTCTL, control);
+
+ ctrl_p->hc_cmd_pending = B_TRUE;
+
+ if (cv_timedwait(&ctrl_p->hc_cmd_comp_cv, &ctrl_p->hc_mutex,
+ ddi_get_lbolt() + SEC_TO_TICK(1)) == -1) {
+
+ /* it is a timeout */
+ PCIE_DBG("pciehpc_issue_hpc_command: Command Complete"
+ " interrupt is not received for slot %d\n",
+ slot_p->hs_phy_slot_num);
+
+ /* clear the status info in case interrupts are disabled? */
+ status = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTSTS);
+
+ if (status & PCIE_SLOTSTS_COMMAND_COMPLETED) {
+ /* clear the status bits */
+ pciehpc_reg_put16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTSTS, status);
+ }
+ }
+
+ ctrl_p->hc_cmd_pending = B_FALSE;
+
+ /* wake up any one waiting for issuing another command to HPC */
+ cv_signal(&ctrl_p->hc_cmd_comp_cv);
+}
+
+/*
+ * pciehcp_attn_btn_handler()
+ *
+ * This handles ATTN button pressed event as per the PCI-E 1.1 spec.
+ */
+static void
+pciehpc_attn_btn_handler(pcie_hp_ctrl_t *ctrl_p)
+{
+ pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0];
+ pcie_hp_led_state_t power_led_state;
+ callb_cpr_t cprinfo;
+
+ PCIE_DBG("pciehpc_attn_btn_handler: thread started\n");
+
+ CALLB_CPR_INIT(&cprinfo, &ctrl_p->hc_mutex, callb_generic_cpr,
+ "pciehpc_attn_btn_handler");
+
+ mutex_enter(&ctrl_p->hc_mutex);
+
+ /* wait for ATTN button event */
+ cv_wait(&slot_p->hs_attn_btn_cv, &ctrl_p->hc_mutex);
+
+ while (slot_p->hs_attn_btn_thread_exit == B_FALSE) {
+ if (slot_p->hs_attn_btn_pending == B_TRUE) {
+ /* get the current state of power LED */
+ power_led_state = pciehpc_get_led_state(ctrl_p,
+ PCIE_HP_POWER_LED);
+
+ /* Blink the Power LED while we wait for 5 seconds */
+ pciehpc_set_led_state(ctrl_p, PCIE_HP_POWER_LED,
+ PCIE_HP_LED_BLINK);
+
+ /* wait for 5 seconds before taking any action */
+ if (cv_timedwait(&slot_p->hs_attn_btn_cv,
+ &ctrl_p->hc_mutex,
+ ddi_get_lbolt() + SEC_TO_TICK(5)) == -1) {
+ /*
+ * It is a time out; make sure the ATTN pending
+ * flag is still ON before sending the event to
+ * DDI HP framework.
+ */
+ if (slot_p->hs_attn_btn_pending == B_TRUE) {
+ int hint;
+
+ slot_p->hs_attn_btn_pending = B_FALSE;
+ pciehpc_get_slot_state(slot_p);
+
+ if (slot_p->hs_info.cn_state <=
+ DDI_HP_CN_STATE_PRESENT) {
+ /*
+ * Insertion.
+ */
+ hint = SE_INCOMING_RES;
+ } else {
+ /*
+ * Want to remove;
+ */
+ hint = SE_OUTGOING_RES;
+ }
+
+ /*
+ * We can't call ddihp_cn_gen_sysevent
+ * here since it's not a DDI interface.
+ */
+ pcie_hp_gen_sysevent_req(
+ slot_p->hs_info.cn_name,
+ hint,
+ ctrl_p->hc_dip,
+ KM_SLEEP);
+ }
+ }
+
+ /* restore the power LED state */
+ pciehpc_set_led_state(ctrl_p, PCIE_HP_POWER_LED,
+ power_led_state);
+ continue;
+ }
+
+ /* wait for another ATTN button event */
+ cv_wait(&slot_p->hs_attn_btn_cv, &ctrl_p->hc_mutex);
+ }
+
+ PCIE_DBG("pciehpc_attn_btn_handler: thread exit\n");
+ cv_signal(&slot_p->hs_attn_btn_cv);
+ CALLB_CPR_EXIT(&cprinfo);
+ thread_exit();
+}
+
+/*
+ * convert LED state from PCIE HPC definition to pcie_hp_led_state_t
+ * definition.
+ */
+static pcie_hp_led_state_t
+pciehpc_led_state_to_hpc(uint16_t state)
+{
+ switch (state) {
+ case PCIE_SLOTCTL_INDICATOR_STATE_ON:
+ return (PCIE_HP_LED_ON);
+ case PCIE_SLOTCTL_INDICATOR_STATE_BLINK:
+ return (PCIE_HP_LED_BLINK);
+ case PCIE_SLOTCTL_INDICATOR_STATE_OFF:
+ default:
+ return (PCIE_HP_LED_OFF);
+ }
+}
+
+/*
+ * Get the state of an LED.
+ */
+static pcie_hp_led_state_t
+pciehpc_get_led_state(pcie_hp_ctrl_t *ctrl_p, pcie_hp_led_t led)
+{
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
+ uint16_t control, state;
+
+ /* get the current state of Slot Control register */
+ control = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTCTL);
+
+ switch (led) {
+ case PCIE_HP_POWER_LED:
+ state = pcie_slotctl_pwr_indicator_get(control);
+ break;
+ case PCIE_HP_ATTN_LED:
+ state = pcie_slotctl_attn_indicator_get(control);
+ break;
+ default:
+ PCIE_DBG("pciehpc_get_led_state() invalid LED %d\n", led);
+ return (PCIE_HP_LED_OFF);
+ }
+
+ switch (state) {
+ case PCIE_SLOTCTL_INDICATOR_STATE_ON:
+ return (PCIE_HP_LED_ON);
+
+ case PCIE_SLOTCTL_INDICATOR_STATE_BLINK:
+ return (PCIE_HP_LED_BLINK);
+
+ case PCIE_SLOTCTL_INDICATOR_STATE_OFF:
+ default:
+ return (PCIE_HP_LED_OFF);
+ }
+}
+
+/*
+ * Set the state of an LED. It updates both hw and sw state.
+ */
+static void
+pciehpc_set_led_state(pcie_hp_ctrl_t *ctrl_p, pcie_hp_led_t led,
+ pcie_hp_led_state_t state)
+{
+ pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0];
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
+ uint16_t control;
+
+ /* get the current state of Slot Control register */
+ control = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTCTL);
+
+ switch (led) {
+ case PCIE_HP_POWER_LED:
+ /* clear led mask */
+ control &= ~PCIE_SLOTCTL_PWR_INDICATOR_MASK;
+ slot_p->hs_power_led_state = state;
+ break;
+ case PCIE_HP_ATTN_LED:
+ /* clear led mask */
+ control &= ~PCIE_SLOTCTL_ATTN_INDICATOR_MASK;
+ slot_p->hs_attn_led_state = state;
+ break;
+ default:
+ PCIE_DBG("pciehpc_set_led_state() invalid LED %d\n", led);
+ return;
+ }
+
+ switch (state) {
+ case PCIE_HP_LED_ON:
+ if (led == PCIE_HP_POWER_LED)
+ control = pcie_slotctl_pwr_indicator_set(control,
+ PCIE_SLOTCTL_INDICATOR_STATE_ON);
+ else if (led == PCIE_HP_ATTN_LED)
+ control = pcie_slotctl_attn_indicator_set(control,
+ PCIE_SLOTCTL_INDICATOR_STATE_ON);
+ break;
+ case PCIE_HP_LED_OFF:
+ if (led == PCIE_HP_POWER_LED)
+ control = pcie_slotctl_pwr_indicator_set(control,
+ PCIE_SLOTCTL_INDICATOR_STATE_OFF);
+ else if (led == PCIE_HP_ATTN_LED)
+ control = pcie_slotctl_attn_indicator_set(control,
+ PCIE_SLOTCTL_INDICATOR_STATE_OFF);
+ break;
+ case PCIE_HP_LED_BLINK:
+ if (led == PCIE_HP_POWER_LED)
+ control = pcie_slotctl_pwr_indicator_set(control,
+ PCIE_SLOTCTL_INDICATOR_STATE_BLINK);
+ else if (led == PCIE_HP_ATTN_LED)
+ control = pcie_slotctl_attn_indicator_set(control,
+ PCIE_SLOTCTL_INDICATOR_STATE_BLINK);
+ break;
+
+ default:
+ PCIE_DBG("pciehpc_set_led_state() invalid LED state %d\n",
+ state);
+ return;
+ }
+
+ /* update the Slot Control Register */
+ pciehpc_issue_hpc_command(ctrl_p, control);
+
+#ifdef DEBUG
+ /* get the current state of Slot Control register */
+ control = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTCTL);
+
+ PCIE_DBG("pciehpc_set_led_state: slot %d power-led %s attn-led %s\n",
+ slot_p->hs_phy_slot_num, pcie_led_state_text(
+ pciehpc_led_state_to_hpc(pcie_slotctl_pwr_indicator_get(control))),
+ pcie_led_state_text(pciehpc_led_state_to_hpc(
+ pcie_slotctl_attn_indicator_get(control))));
+#endif
+}
+
+#ifdef DEBUG
+/*
+ * Dump PCI-E Hot Plug registers.
+ */
+static void
+pciehpc_dump_hpregs(pcie_hp_ctrl_t *ctrl_p)
+{
+ pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0];
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
+ uint16_t control;
+ uint32_t capabilities;
+
+ if (!pcie_debug_flags)
+ return;
+
+ capabilities = pciehpc_reg_get32(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTCAP);
+
+ control = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTCTL);
+
+ PCIE_DBG("pciehpc_dump_hpregs: Found PCI-E hot plug slot %d\n",
+ slot_p->hs_phy_slot_num);
+
+ PCIE_DBG("Attention Button Present = %s\n",
+ capabilities & PCIE_SLOTCAP_ATTN_BUTTON ? "Yes":"No");
+
+ PCIE_DBG("Power controller Present = %s\n",
+ capabilities & PCIE_SLOTCAP_POWER_CONTROLLER ? "Yes":"No");
+
+ PCIE_DBG("MRL Sensor Present = %s\n",
+ capabilities & PCIE_SLOTCAP_MRL_SENSOR ? "Yes":"No");
+
+ PCIE_DBG("Attn Indicator Present = %s\n",
+ capabilities & PCIE_SLOTCAP_ATTN_INDICATOR ? "Yes":"No");
+
+ PCIE_DBG("Power Indicator Present = %s\n",
+ capabilities & PCIE_SLOTCAP_PWR_INDICATOR ? "Yes":"No");
+
+ PCIE_DBG("HotPlug Surprise = %s\n",
+ capabilities & PCIE_SLOTCAP_HP_SURPRISE ? "Yes":"No");
+
+ PCIE_DBG("HotPlug Capable = %s\n",
+ capabilities & PCIE_SLOTCAP_HP_CAPABLE ? "Yes":"No");
+
+ PCIE_DBG("Physical Slot Number = %d\n",
+ PCIE_SLOTCAP_PHY_SLOT_NUM(capabilities));
+
+ PCIE_DBG("Attn Button interrupt Enabled = %s\n",
+ control & PCIE_SLOTCTL_ATTN_BTN_EN ? "Yes":"No");
+
+ PCIE_DBG("Power Fault interrupt Enabled = %s\n",
+ control & PCIE_SLOTCTL_PWR_FAULT_EN ? "Yes":"No");
+
+ PCIE_DBG("MRL Sensor INTR Enabled = %s\n",
+ control & PCIE_SLOTCTL_MRL_SENSOR_EN ? "Yes":"No");
+
+ PCIE_DBG("Presence interrupt Enabled = %s\n",
+ control & PCIE_SLOTCTL_PRESENCE_CHANGE_EN ? "Yes":"No");
+
+ PCIE_DBG("Cmd Complete interrupt Enabled = %s\n",
+ control & PCIE_SLOTCTL_CMD_INTR_EN ? "Yes":"No");
+
+ PCIE_DBG("HotPlug interrupt Enabled = %s\n",
+ control & PCIE_SLOTCTL_HP_INTR_EN ? "Yes":"No");
+
+ PCIE_DBG("Power Indicator LED = %s", pcie_led_state_text(
+ pciehpc_led_state_to_hpc(pcie_slotctl_pwr_indicator_get(control))));
+
+ PCIE_DBG("Attn Indicator LED = %s\n",
+ pcie_led_state_text(pciehpc_led_state_to_hpc(
+ pcie_slotctl_attn_indicator_get(control))));
+}
+#endif /* DEBUG */
diff --git a/usr/src/uts/common/io/pciex/hotplug/pcishpc.c b/usr/src/uts/common/io/pciex/hotplug/pcishpc.c
new file mode 100644
index 0000000000..40c6f71a46
--- /dev/null
+++ b/usr/src/uts/common/io/pciex/hotplug/pcishpc.c
@@ -0,0 +1,2645 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * This file contains PCI HotPlug functionality that is compatible with the
+ * PCI SHPC specification 1.x.
+ *
+ * NOTE: This file is compiled and delivered through misc/pcie module.
+ */
+
+#include <sys/note.h>
+#include <sys/conf.h>
+#include <sys/kmem.h>
+#include <sys/kstat.h>
+#include <sys/debug.h>
+#include <sys/vtrace.h>
+#include <sys/autoconf.h>
+#include <sys/varargs.h>
+#include <sys/hwconf.h>
+#include <sys/ddi_impldefs.h>
+#include <sys/callb.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/sunndi.h>
+#include <sys/sysevent/dr.h>
+#include <sys/ndi_impldefs.h>
+#include <sys/pci_impl.h>
+#include <sys/hotplug/pci/pcie_hp.h>
+#include <sys/hotplug/pci/pcishpc.h>
+
+typedef struct pcishpc_prop {
+ char *prop_name;
+ char *prop_value;
+} pcishpc_prop_t;
+
+static pcishpc_prop_t pcishpc_props[] = {
+ { PCIEHPC_PROP_LED_FAULT, PCIEHPC_PROP_VALUE_LED },
+ { PCIEHPC_PROP_LED_POWER, PCIEHPC_PROP_VALUE_LED },
+ { PCIEHPC_PROP_LED_ATTN, PCIEHPC_PROP_VALUE_LED },
+ { PCIEHPC_PROP_LED_ACTIVE, PCIEHPC_PROP_VALUE_LED },
+ { PCIEHPC_PROP_CARD_TYPE, PCIEHPC_PROP_VALUE_TYPE },
+ { PCIEHPC_PROP_BOARD_TYPE, PCIEHPC_PROP_VALUE_TYPE },
+ { PCIEHPC_PROP_SLOT_CONDITION, PCIEHPC_PROP_VALUE_TYPE }
+};
+
+/* reset delay to 1 sec. */
+static int pcishpc_reset_delay = 1000000;
+
+/* Local function prototype */
+static pcie_hp_ctrl_t *pcishpc_create_controller(dev_info_t *dip);
+static int pcishpc_setup_controller(pcie_hp_ctrl_t *ctrl_p);
+static int pcishpc_destroy_controller(dev_info_t *dip);
+static pcie_hp_slot_t *pcishpc_create_slot(pcie_hp_ctrl_t *ctrl_p);
+static int pcishpc_register_slot(pcie_hp_ctrl_t *ctrl_p, int slot);
+static int pcishpc_destroy_slots(pcie_hp_ctrl_t *ctrl_p);
+static int pcishpc_enable_irqs(pcie_hp_ctrl_t *ctrl_p);
+static int pcishpc_disable_irqs(pcie_hp_ctrl_t *ctrl_p);
+static int pcishpc_slot_get_property(pcie_hp_slot_t *slot_p,
+ ddi_hp_property_t *arg, ddi_hp_property_t *rval);
+static int pcishpc_slot_set_property(pcie_hp_slot_t *slot_p,
+ ddi_hp_property_t *arg, ddi_hp_property_t *rval);
+static int pcishpc_issue_command(pcie_hp_ctrl_t *ctrl_p,
+ uint32_t cmd_code);
+static int pcishpc_wait_busy(pcie_hp_ctrl_t *ctrl_p);
+static void pcishpc_attn_btn_handler(pcie_hp_slot_t *slot_p);
+static void pcishpc_get_slot_state(pcie_hp_slot_t *slot_p);
+static int pcishpc_set_slot_state(pcie_hp_slot_t *slot_p,
+ ddi_hp_cn_state_t new_slot_state);
+static void pcishpc_set_slot_name(pcie_hp_ctrl_t *ctrl_p, int slot);
+static int pcishpc_set_bus_speed(pcie_hp_slot_t *slot_p);
+static int pcishpc_setled(pcie_hp_slot_t *slot_p, pcie_hp_led_t led,
+ pcie_hp_led_state_t state);
+static int pcishpc_led_shpc_to_hpc(int state);
+static int pcishpc_led_hpc_to_shpc(int state);
+static int pcishpc_slot_shpc_to_hpc(int shpc_state);
+static int pcishpc_slot_hpc_to_shpc(int state);
+static char *pcishpc_slot_textslotstate(ddi_hp_cn_state_t state);
+static char *pcishpc_slot_textledstate(pcie_hp_led_state_t state);
+
+static uint32_t pcishpc_read_reg(pcie_hp_ctrl_t *ctrl_p, int reg);
+static void pcishpc_write_reg(pcie_hp_ctrl_t *ctrl_p, int reg,
+ uint32_t data);
+
+static int pcishpc_upgrade_slot_state(pcie_hp_slot_t *slot_p,
+ ddi_hp_cn_state_t target_state);
+static int pcishpc_downgrade_slot_state(pcie_hp_slot_t *slot_p,
+ ddi_hp_cn_state_t target_state);
+static int pcishpc_change_slot_state(pcie_hp_slot_t *slot_p,
+ ddi_hp_cn_state_t target_state);
+
+static int pcishpc_slot_poweron(pcie_hp_slot_t *slot_p,
+ ddi_hp_cn_state_t *result_state);
+static int pcishpc_slot_poweroff(pcie_hp_slot_t *slot_p,
+ ddi_hp_cn_state_t *result_state);
+static int pcishpc_slot_probe(pcie_hp_slot_t *slot_p);
+static int pcishpc_slot_unprobe(pcie_hp_slot_t *slot_p);
+#ifdef DEBUG
+static void pcishpc_dump_regs(pcie_hp_ctrl_t *ctrl_p);
+#endif /* DEBUG */
+
+
+/*
+ * Global functions (called by other drivers/modules)
+ */
+
+/*
+ * pcishpc_init()
+ *
+ * Install and configure an SHPC controller and register the HotPlug slots
+ * with the Solaris HotPlug framework. This function is usually called by
+ * a PCI bridge Nexus driver that has a built in SHPC controller.
+ */
+int
+pcishpc_init(dev_info_t *dip)
+{
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
+ pcie_hp_ctrl_t *ctrl_p;
+ int i;
+
+ PCIE_DBG("pcishpc_init() called from %s#%d\n",
+ ddi_driver_name(dip), ddi_get_instance(dip));
+
+ if ((ctrl_p = PCIE_GET_HP_CTRL(dip)) != NULL) {
+ PCIE_DBG("pcishpc_init() shpc instance already "
+ "initialized!\n");
+ return (DDI_SUCCESS);
+ }
+
+ /* Initialize soft state structure for the SHPC instance. */
+ ctrl_p = pcishpc_create_controller(dip);
+
+ if (ctrl_p == NULL) {
+ PCIE_DBG("pcishpc_init() failed to create shpc softstate\n");
+ return (DDI_FAILURE);
+ }
+
+ if (pcishpc_setup_controller(ctrl_p) != DDI_SUCCESS) {
+ PCIE_DBG("pcishpc_init() failed to setup controller\n");
+ goto cleanup;
+ }
+
+ /*
+ * Setup resource maps for this bus node.
+ */
+ (void) pci_resource_setup(dip);
+
+#ifdef DEBUG
+ PCIE_DBG("%s%d: P2P bridge register dump:\n",
+ ddi_driver_name(dip), ddi_get_instance(dip));
+
+ for (i = 0; i < 0x100; i += 4) {
+ PCIE_DBG("SHPC Cfg reg 0x%02x: %08x\n", i,
+ pci_config_get32(bus_p->bus_cfg_hdl, i));
+ }
+#endif /* DEBUG */
+
+ /* Setup each HotPlug slot on this SHPC controller. */
+ for (i = 0; i < ctrl_p->hc_num_slots_impl; i++) {
+ if (pcishpc_register_slot(ctrl_p, i) != DDI_SUCCESS) {
+ PCIE_DBG("pcishpc_init() failed to register "
+ "slot %d\n", i);
+ goto cleanup1;
+ }
+ if (pcie_create_minor_node(ctrl_p, i) != DDI_SUCCESS) {
+ PCIE_DBG("pcishpc_init() failed to create "
+ "minor node for slot %d\n", i);
+ goto cleanup1;
+ }
+ }
+
+ (void) pcishpc_enable_irqs(ctrl_p);
+
+#ifdef DEBUG
+ /* Dump out the SHPC registers. */
+ pcishpc_dump_regs(ctrl_p);
+#endif /* DEBUG */
+
+ PCIE_DBG("pcishpc_init() success(dip=%p)\n", dip);
+ return (DDI_SUCCESS);
+
+cleanup1:
+ for (i = 0; i < ctrl_p->hc_num_slots_impl; i++) {
+ if (ctrl_p->hc_slots[i] == NULL)
+ continue;
+
+ pcie_remove_minor_node(ctrl_p, i);
+ }
+ (void) pci_resource_destroy(dip);
+cleanup:
+ (void) pcishpc_destroy_controller(dip);
+ return (DDI_FAILURE);
+}
+
+/*
+ * pcishpc_uninit()
+ * Unload the HogPlug controller driver and deallocate all resources.
+ */
+int
+pcishpc_uninit(dev_info_t *dip)
+{
+ pcie_hp_ctrl_t *ctrl_p;
+ int i;
+
+ PCIE_DBG("pcishpc_uninit() called(dip=%p)\n", dip);
+
+ ctrl_p = PCIE_GET_HP_CTRL(dip);
+
+ if (!ctrl_p) {
+ PCIE_DBG("pcishpc_uninit() Unable to find softstate\n");
+ return (DDI_FAILURE);
+ }
+
+ for (i = 0; i < PCIE_HP_MAX_SLOTS; i++) {
+ if (ctrl_p->hc_slots[i] == NULL)
+ continue;
+
+ pcie_remove_minor_node(ctrl_p, i);
+ }
+
+ (void) pcishpc_disable_irqs(ctrl_p);
+ ctrl_p->hc_flags = 0;
+
+ /*
+ * Destroy resource maps for this bus node.
+ */
+ (void) pci_resource_destroy(dip);
+
+ (void) pcishpc_destroy_controller(dip);
+
+ PCIE_DBG("pcishpc_uninit() success(dip=%p)\n", dip);
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * pcishpc_intr()
+ *
+ * This is the SHPC controller interrupt handler.
+ */
+int
+pcishpc_intr(dev_info_t *dip)
+{
+ pcie_hp_ctrl_t *ctrl_p;
+ uint32_t irq_locator, irq_serr_locator, reg;
+ int slot;
+
+ PCIE_DBG("pcishpc_intr() called\n");
+
+ /* get the soft state structure for this dip */
+ if ((ctrl_p = PCIE_GET_HP_CTRL(dip)) == NULL)
+ return (DDI_INTR_UNCLAIMED);
+
+ mutex_enter(&ctrl_p->hc_mutex);
+
+ if (!(ctrl_p->hc_flags & PCIE_HP_INITIALIZED_FLAG)) {
+ PCIE_DBG("pcishpc_intr() unclaimed\n");
+ mutex_exit(&ctrl_p->hc_mutex);
+ return (DDI_INTR_UNCLAIMED);
+ }
+
+ PCIE_DBG("pcishpc_intr() interrupt received\n");
+
+ reg = pcishpc_read_reg(ctrl_p, PCI_HP_CTRL_SERR_INT_REG);
+
+ if (reg & PCI_HP_SERR_INT_CMD_COMPLETE_IRQ) {
+ PCIE_DBG("pcishpc_intr() "
+ "PCI_HP_SERR_INT_CMD_COMPLETE_IRQ detected\n");
+ ctrl_p->hc_cmd_pending = B_FALSE;
+ cv_signal(&ctrl_p->hc_cmd_comp_cv);
+ }
+
+ if (reg & PCI_HP_SERR_INT_ARBITER_IRQ) {
+ PCIE_DBG("pcishpc_intr() PCI_HP_SERR_INT_ARBITER_IRQ "
+ "detected\n");
+ ctrl_p->hc_arbiter_timeout = B_TRUE;
+ }
+
+ /* Write back the SERR INT register to acknowledge the IRQs. */
+ pcishpc_write_reg(ctrl_p, PCI_HP_CTRL_SERR_INT_REG, reg);
+
+ irq_locator = pcishpc_read_reg(ctrl_p, PCI_HP_IRQ_LOCATOR_REG);
+ irq_serr_locator = pcishpc_read_reg(ctrl_p, PCI_HP_SERR_LOCATOR_REG);
+
+ /* Check for slot events that might have occured. */
+ for (slot = 0; slot < ctrl_p->hc_num_slots_impl; slot++) {
+ if ((irq_locator & (PCI_HP_IRQ_SLOT_N_PENDING<<slot)) ||
+ (irq_serr_locator &
+ (PCI_HP_IRQ_SERR_SLOT_N_PENDING<<slot))) {
+ PCIE_DBG("pcishpc_intr() slot %d and "
+ "pending IRQ\n", slot+1);
+
+ reg = pcishpc_read_reg(ctrl_p,
+ PCI_HP_LOGICAL_SLOT_REGS+slot);
+
+ if (reg & PCI_HP_SLOT_PRESENCE_DETECTED)
+ PCIE_DBG("slot %d: "
+ "PCI_HP_SLOT_PRESENCE_DETECTED\n",
+ slot+1);
+
+ if (reg & PCI_HP_SLOT_ISO_PWR_DETECTED)
+ PCIE_DBG("slot %d: "
+ "PCI_HP_SLOT_ISO_PWR_DETECTED\n",
+ slot+1);
+
+ if (reg & PCI_HP_SLOT_ATTN_DETECTED) {
+ PCIE_DBG("slot %d: "
+ "PCI_HP_SLOT_ATTN_DETECTED\n", slot+1);
+
+ /*
+ * if ATTN button event is still pending
+ * then cancel it
+ */
+ if (ctrl_p->hc_slots[slot]->
+ hs_attn_btn_pending == B_TRUE)
+ ctrl_p->hc_slots[slot]->
+ hs_attn_btn_pending = B_FALSE;
+
+ /* wake up the ATTN event handler */
+ cv_signal(&ctrl_p->hc_slots[slot]->
+ hs_attn_btn_cv);
+ }
+
+ if (reg & PCI_HP_SLOT_MRL_DETECTED)
+ PCIE_DBG("slot %d: "
+ "PCI_HP_SLOT_MRL_DETECTED\n", slot+1);
+
+ if (reg & PCI_HP_SLOT_POWER_DETECTED)
+ PCIE_DBG("slot %d: "
+ "PCI_HP_SLOT_POWER_DETECTED\n", slot+1);
+
+ /* Acknoledge any slot interrupts */
+ pcishpc_write_reg(ctrl_p, PCI_HP_LOGICAL_SLOT_REGS+slot,
+ reg);
+ }
+ }
+
+ mutex_exit(&ctrl_p->hc_mutex);
+
+ PCIE_DBG("pcishpc_intr() claimed\n");
+
+ return (DDI_INTR_CLAIMED);
+}
+
+int
+pcishpc_slot_get_property(pcie_hp_slot_t *slot_p, ddi_hp_property_t *arg,
+ ddi_hp_property_t *rval)
+{
+ ddi_hp_property_t request, result;
+#ifdef _SYSCALL32_IMPL
+ ddi_hp_property32_t request32, result32;
+#endif
+ pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl;
+ nvlist_t *prop_list;
+ nvlist_t *prop_rlist; /* nvlist for return values */
+ nvpair_t *prop_pair;
+ char *name, *value;
+ int ret = DDI_SUCCESS;
+ int i, n;
+ boolean_t get_all_prop = B_FALSE;
+
+ if (get_udatamodel() == DATAMODEL_NATIVE) {
+ if (copyin(arg, &request, sizeof (ddi_hp_property_t)) ||
+ copyin(rval, &result, sizeof (ddi_hp_property_t)))
+ return (DDI_FAILURE);
+ }
+#ifdef _SYSCALL32_IMPL
+ else {
+ bzero(&request, sizeof (request));
+ bzero(&result, sizeof (result));
+ if (copyin(arg, &request32, sizeof (ddi_hp_property32_t)) ||
+ copyin(rval, &result32, sizeof (ddi_hp_property32_t)))
+ return (DDI_FAILURE);
+ request.nvlist_buf = (char *)(uintptr_t)request32.nvlist_buf;
+ request.buf_size = request32.buf_size;
+ result.nvlist_buf = (char *)(uintptr_t)result32.nvlist_buf;
+ result.buf_size = result32.buf_size;
+ }
+#endif
+
+ if ((ret = pcie_copyin_nvlist(request.nvlist_buf, request.buf_size,
+ &prop_list)) != DDI_SUCCESS)
+ return (ret);
+
+ if (nvlist_alloc(&prop_rlist, NV_UNIQUE_NAME, 0)) {
+ ret = DDI_ENOMEM;
+ goto get_prop_cleanup;
+ }
+
+ /* check whether the requested property is "all" or "help" */
+ prop_pair = nvlist_next_nvpair(prop_list, NULL);
+ if (prop_pair && !nvlist_next_nvpair(prop_list, prop_pair)) {
+ name = nvpair_name(prop_pair);
+ n = sizeof (pcishpc_props) / sizeof (pcishpc_prop_t);
+
+ if (strcmp(name, PCIEHPC_PROP_ALL) == 0) {
+ (void) nvlist_remove_all(prop_list, PCIEHPC_PROP_ALL);
+
+ /*
+ * Add all properties into the request list, so that we
+ * will get the values in the following for loop.
+ */
+ for (i = 0; i < n; i++) {
+ if (nvlist_add_string(prop_list,
+ pcishpc_props[i].prop_name, "") != 0) {
+ ret = DDI_FAILURE;
+ goto get_prop_cleanup1;
+ }
+ }
+ get_all_prop = B_TRUE;
+ } else if (strcmp(name, PCIEHPC_PROP_HELP) == 0) {
+ /*
+ * Empty the request list, and add help strings into the
+ * return list. We will pass the following for loop.
+ */
+ (void) nvlist_remove_all(prop_list, PCIEHPC_PROP_HELP);
+
+ for (i = 0; i < n; i++) {
+ if (nvlist_add_string(prop_rlist,
+ pcishpc_props[i].prop_name,
+ pcishpc_props[i].prop_value) != 0) {
+ ret = DDI_FAILURE;
+ goto get_prop_cleanup1;
+ }
+ }
+ }
+ }
+
+ mutex_enter(&ctrl_p->hc_mutex);
+
+ /* get the current slot state */
+ pcishpc_get_slot_state(slot_p);
+
+ /* for each requested property, get the value and add it to nvlist */
+ prop_pair = NULL;
+ while (prop_pair = nvlist_next_nvpair(prop_list, prop_pair)) {
+ name = nvpair_name(prop_pair);
+
+ if (strcmp(name, PCIEHPC_PROP_LED_FAULT) == 0) {
+ value = pcie_led_state_text(
+ slot_p->hs_fault_led_state);
+ } else if (strcmp(name, PCIEHPC_PROP_LED_POWER) == 0) {
+ value = pcie_led_state_text(
+ slot_p->hs_power_led_state);
+ } else if (strcmp(name, PCIEHPC_PROP_LED_ATTN) == 0) {
+ value = pcie_led_state_text(
+ slot_p->hs_attn_led_state);
+ } else if (strcmp(name, PCIEHPC_PROP_LED_ACTIVE) == 0) {
+ value = pcie_led_state_text(
+ slot_p->hs_active_led_state);
+ } else if (strcmp(name, PCIEHPC_PROP_CARD_TYPE) == 0) {
+ ddi_acc_handle_t handle;
+ dev_info_t *cdip;
+ uint8_t prog_class, base_class, sub_class;
+ int i;
+
+ mutex_exit(&ctrl_p->hc_mutex);
+ cdip = pcie_hp_devi_find(
+ ctrl_p->hc_dip, slot_p->hs_device_num, 0);
+ mutex_enter(&ctrl_p->hc_mutex);
+
+ if ((slot_p->hs_info.cn_state !=
+ DDI_HP_CN_STATE_ENABLED) || (cdip == NULL)) {
+ /*
+ * When getting all properties, just ignore the
+ * one that's not available under certain state.
+ */
+ if (get_all_prop)
+ continue;
+
+ ret = DDI_ENOTSUP;
+ goto get_prop_cleanup2;
+ }
+
+ if (pci_config_setup(cdip, &handle) != DDI_SUCCESS) {
+ ret = DDI_FAILURE;
+ goto get_prop_cleanup2;
+ }
+
+ prog_class = pci_config_get8(handle,
+ PCI_CONF_PROGCLASS);
+ base_class = pci_config_get8(handle, PCI_CONF_BASCLASS);
+ sub_class = pci_config_get8(handle, PCI_CONF_SUBCLASS);
+ pci_config_teardown(&handle);
+
+ for (i = 0; i < class_pci_items; i++) {
+ if ((base_class == class_pci[i].base_class) &&
+ (sub_class == class_pci[i].sub_class) &&
+ (prog_class == class_pci[i].prog_class)) {
+ value = class_pci[i].short_desc;
+ break;
+ }
+ }
+ if (i == class_pci_items)
+ value = PCIEHPC_PROP_VALUE_UNKNOWN;
+ } else if (strcmp(name, PCIEHPC_PROP_BOARD_TYPE) == 0) {
+ if (slot_p->hs_info.cn_state <= DDI_HP_CN_STATE_EMPTY)
+ value = PCIEHPC_PROP_VALUE_UNKNOWN;
+ else
+ value = PCIEHPC_PROP_VALUE_PCIHOTPLUG;
+ } else if (strcmp(name, PCIEHPC_PROP_SLOT_CONDITION) == 0) {
+ value = pcie_slot_condition_text(slot_p->hs_condition);
+ } else {
+ /* unsupported property */
+ cmn_err(CE_WARN, "Unsupported property: %s\n", name);
+
+ ret = DDI_ENOTSUP;
+ goto get_prop_cleanup2;
+ }
+ if (nvlist_add_string(prop_rlist, name, value) != 0) {
+ ret = DDI_FAILURE;
+ goto get_prop_cleanup2;
+ }
+ }
+
+ // pack nvlist and copyout
+ if ((ret = pcie_copyout_nvlist(prop_rlist, result.nvlist_buf,
+ &result.buf_size)) != DDI_SUCCESS) {
+ goto get_prop_cleanup2;
+ }
+ if (get_udatamodel() == DATAMODEL_NATIVE) {
+ if (copyout(&result, rval, sizeof (ddi_hp_property_t))) {
+ ret = DDI_FAILURE;
+ goto get_prop_cleanup2;
+ }
+ }
+#ifdef _SYSCALL32_IMPL
+ else {
+ if (result.buf_size > UINT32_MAX) {
+ ret = DDI_FAILURE;
+ } else {
+ result32.buf_size = (uint32_t)result.buf_size;
+ if (copyout(&result32, rval,
+ sizeof (ddi_hp_property32_t)))
+ ret = DDI_FAILURE;
+ }
+ }
+#endif
+
+get_prop_cleanup2:
+ mutex_exit(&ctrl_p->hc_mutex);
+get_prop_cleanup1:
+ nvlist_free(prop_rlist);
+get_prop_cleanup:
+ nvlist_free(prop_list);
+ return (ret);
+}
+
+int
+pcishpc_slot_set_property(pcie_hp_slot_t *slot_p, ddi_hp_property_t *arg,
+ ddi_hp_property_t *rval)
+{
+ ddi_hp_property_t request, result;
+#ifdef _SYSCALL32_IMPL
+ ddi_hp_property32_t request32, result32;
+#endif
+ pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl;
+ nvlist_t *prop_list;
+ nvlist_t *prop_rlist;
+ nvpair_t *prop_pair;
+ char *name, *value;
+ pcie_hp_led_state_t led_state;
+ int ret = DDI_SUCCESS;
+
+ if (get_udatamodel() == DATAMODEL_NATIVE) {
+ if (copyin(arg, &request, sizeof (ddi_hp_property_t)))
+ return (DDI_FAILURE);
+ if (rval &&
+ copyin(rval, &result, sizeof (ddi_hp_property_t)))
+ return (DDI_FAILURE);
+ }
+#ifdef _SYSCALL32_IMPL
+ else {
+ bzero(&request, sizeof (request));
+ bzero(&result, sizeof (result));
+ if (copyin(arg, &request32, sizeof (ddi_hp_property32_t)))
+ return (DDI_FAILURE);
+ if (rval &&
+ copyin(rval, &result32, sizeof (ddi_hp_property32_t)))
+ return (DDI_FAILURE);
+ request.nvlist_buf = (char *)(uintptr_t)request32.nvlist_buf;
+ request.buf_size = request32.buf_size;
+ if (rval) {
+ result.nvlist_buf =
+ (char *)(uintptr_t)result32.nvlist_buf;
+ result.buf_size = result32.buf_size;
+ }
+ }
+#endif
+
+ if ((ret = pcie_copyin_nvlist(request.nvlist_buf, request.buf_size,
+ &prop_list)) != DDI_SUCCESS)
+ return (ret);
+
+ /* check whether the requested property is "help" */
+ prop_pair = nvlist_next_nvpair(prop_list, NULL);
+ if (prop_pair && !nvlist_next_nvpair(prop_list, prop_pair) &&
+ (strcmp(nvpair_name(prop_pair), PCIEHPC_PROP_HELP) == 0)) {
+ if (!rval) {
+ ret = DDI_ENOTSUP;
+ goto set_prop_cleanup;
+ }
+
+ if (nvlist_alloc(&prop_rlist, NV_UNIQUE_NAME, 0)) {
+ ret = DDI_ENOMEM;
+ goto set_prop_cleanup;
+ }
+ if (nvlist_add_string(prop_rlist, PCIEHPC_PROP_LED_ATTN,
+ PCIEHPC_PROP_VALUE_LED) != 0) {
+ ret = DDI_FAILURE;
+ goto set_prop_cleanup1;
+ }
+
+ if ((ret = pcie_copyout_nvlist(prop_rlist, result.nvlist_buf,
+ &result.buf_size)) != DDI_SUCCESS) {
+ goto set_prop_cleanup1;
+ }
+ if (get_udatamodel() == DATAMODEL_NATIVE) {
+ if (copyout(&result, rval,
+ sizeof (ddi_hp_property_t))) {
+ ret = DDI_FAILURE;
+ goto set_prop_cleanup1;
+ }
+ }
+#ifdef _SYSCALL32_IMPL
+ else {
+ if (result.buf_size > UINT32_MAX) {
+ ret = DDI_FAILURE;
+ goto set_prop_cleanup1;
+ } else {
+ result32.buf_size = (uint32_t)result.buf_size;
+ if (copyout(&result32, rval,
+ sizeof (ddi_hp_property32_t))) {
+ ret = DDI_FAILURE;
+ goto set_prop_cleanup1;
+ }
+ }
+ }
+#endif
+set_prop_cleanup1:
+ nvlist_free(prop_rlist);
+ nvlist_free(prop_list);
+ return (ret);
+ }
+
+ /* Validate the request */
+ prop_pair = NULL;
+ while (prop_pair = nvlist_next_nvpair(prop_list, prop_pair)) {
+ name = nvpair_name(prop_pair);
+ if (nvpair_type(prop_pair) != DATA_TYPE_STRING) {
+ cmn_err(CE_WARN, "Unexpected data type of setting "
+ "property %s.\n", name);
+ ret = DDI_EINVAL;
+ goto set_prop_cleanup;
+ }
+ if (nvpair_value_string(prop_pair, &value)) {
+ cmn_err(CE_WARN, "Get string value failed for property "
+ "%s.\n", name);
+ ret = DDI_FAILURE;
+ goto set_prop_cleanup;
+ }
+
+ if (strcmp(name, PCIEHPC_PROP_LED_ATTN) == 0) {
+ if ((strcmp(value, PCIEHPC_PROP_VALUE_ON) != 0) &&
+ (strcmp(value, PCIEHPC_PROP_VALUE_OFF) != 0) &&
+ (strcmp(value, PCIEHPC_PROP_VALUE_BLINK) != 0)) {
+ cmn_err(CE_WARN, "Unsupported value of setting "
+ "property %s\n", name);
+ ret = DDI_ENOTSUP;
+ goto set_prop_cleanup;
+ }
+ } else {
+ cmn_err(CE_WARN, "Unsupported property: %s\n", name);
+ ret = DDI_ENOTSUP;
+ goto set_prop_cleanup;
+ }
+ }
+
+ mutex_enter(&ctrl_p->hc_mutex);
+
+ /* get the current slot state */
+ pcishpc_get_slot_state(slot_p);
+
+ // set each property
+ prop_pair = NULL;
+ while (prop_pair = nvlist_next_nvpair(prop_list, prop_pair)) {
+ name = nvpair_name(prop_pair);
+
+ if (strcmp(name, PCIEHPC_PROP_LED_ATTN) == 0) {
+ if (strcmp(value, PCIEHPC_PROP_VALUE_ON) == 0)
+ led_state = PCIE_HP_LED_ON;
+ else if (strcmp(value, PCIEHPC_PROP_VALUE_OFF) == 0)
+ led_state = PCIE_HP_LED_OFF;
+ else if (strcmp(value, PCIEHPC_PROP_VALUE_BLINK) == 0)
+ led_state = PCIE_HP_LED_BLINK;
+
+ (void) pcishpc_setled(slot_p, PCIE_HP_ATTN_LED,
+ led_state);
+ }
+ }
+
+ mutex_exit(&ctrl_p->hc_mutex);
+set_prop_cleanup:
+ nvlist_free(prop_list);
+ return (ret);
+}
+
+/*
+ * pcishpc_hp_ops()
+ *
+ * Handle hotplug commands
+ *
+ * Note: This function is called by DDI HP framework at kernel context only
+ */
+/* ARGSUSED */
+int
+pcishpc_hp_ops(dev_info_t *dip, char *cn_name, ddi_hp_op_t op,
+ void *arg, void *result)
+{
+ pcie_hp_slot_t *slot_p = NULL;
+ pcie_hp_ctrl_t *ctrl_p;
+ int ret = DDI_SUCCESS, i;
+
+ PCIE_DBG("pcishpc_hp_ops: dip=%p cn_name=%s op=%x arg=%p\n",
+ dip, cn_name, op, arg);
+
+ if ((ctrl_p = PCIE_GET_HP_CTRL(dip)) == NULL)
+ return (DDI_FAILURE);
+
+ for (i = 0; i < PCIE_HP_MAX_SLOTS && ctrl_p->hc_slots[i]; i++) {
+ if (strcmp(ctrl_p->hc_slots[i]->hs_info.cn_name, cn_name)
+ == 0) {
+ /* Match with a physical slot, found */
+ slot_p = ctrl_p->hc_slots[i];
+ break;
+ }
+ }
+ if (!slot_p) {
+ PCIE_DBG("pcishpc_hp_ops: Failed to find the slot under"
+ "dip %p with name: %s; op=%x arg=%p\n",
+ dip, cn_name, op, arg);
+ return (DDI_EINVAL);
+ }
+ switch (op) {
+ case DDI_HPOP_CN_GET_STATE:
+ {
+ mutex_enter(&ctrl_p->hc_mutex);
+
+ /* get the current slot state */
+ pcishpc_get_slot_state(slot_p);
+
+ *((ddi_hp_cn_state_t *)result) = slot_p->hs_info.cn_state;
+
+ mutex_exit(&ctrl_p->hc_mutex);
+ break;
+ }
+ case DDI_HPOP_CN_CHANGE_STATE:
+ {
+ ddi_hp_cn_state_t target_state = *(ddi_hp_cn_state_t *)arg;
+
+ mutex_enter(&slot_p->hs_ctrl->hc_mutex);
+
+ ret = pcishpc_change_slot_state(slot_p, target_state);
+ *((ddi_hp_cn_state_t *)result) = slot_p->hs_info.cn_state;
+
+ mutex_exit(&slot_p->hs_ctrl->hc_mutex);
+ break;
+ }
+ case DDI_HPOP_CN_PROBE:
+ ret = pcishpc_slot_probe(slot_p);
+
+ break;
+ case DDI_HPOP_CN_UNPROBE:
+ ret = pcishpc_slot_unprobe(slot_p);
+
+ break;
+ case DDI_HPOP_CN_GET_PROPERTY:
+ ret = pcishpc_slot_get_property(slot_p,
+ (ddi_hp_property_t *)arg, (ddi_hp_property_t *)result);
+ break;
+ case DDI_HPOP_CN_SET_PROPERTY:
+ ret = pcishpc_slot_set_property(slot_p,
+ (ddi_hp_property_t *)arg, (ddi_hp_property_t *)result);
+ break;
+ default:
+ ret = DDI_ENOTSUP;
+ break;
+ }
+
+ return (ret);
+}
+
+/*
+ * Local functions (called within this file)
+ */
+
+/*
+ * pcishpc_create_controller()
+ *
+ * This function allocates and creates an SHPC controller state structure
+ * and adds it to the linked list of controllers.
+ */
+static pcie_hp_ctrl_t *
+pcishpc_create_controller(dev_info_t *dip)
+{
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
+ pcie_hp_ctrl_t *ctrl_p;
+
+ PCIE_DBG("pcishpc: create controller for %s#%d\n",
+ ddi_driver_name(dip), ddi_get_instance(dip));
+
+ ctrl_p = kmem_zalloc(sizeof (pcie_hp_ctrl_t), KM_SLEEP);
+ ctrl_p->hc_dip = dip;
+
+ cv_init(&ctrl_p->hc_cmd_comp_cv, NULL, CV_DRIVER, NULL);
+
+ /* Init the shpc controller's mutex. */
+ mutex_init(&ctrl_p->hc_mutex, NULL, MUTEX_DRIVER, NULL);
+
+ /* HPC initialization is complete now */
+ ctrl_p->hc_flags = PCIE_HP_INITIALIZED_FLAG;
+ bus_p->bus_hp_curr_mode = PCIE_PCI_HP_MODE;
+
+ PCIE_SET_HP_CTRL(dip, ctrl_p);
+
+ PCIE_DBG("pcishpc_create_controller() success\n");
+
+ return (ctrl_p);
+}
+
+
+/*
+ * pcishpc_setup_controller()
+ *
+ * Get the number of HotPlug Slots, and the PCI device information
+ * for this HotPlug controller.
+ */
+static int
+pcishpc_setup_controller(pcie_hp_ctrl_t *ctrl_p)
+{
+ uint32_t config;
+ dev_info_t *ppdip;
+
+ config = pcishpc_read_reg(ctrl_p, PCI_HP_SLOT_CONFIGURATION_REG);
+
+ /* Get the number of HotPlug slots implemented */
+ ctrl_p->hc_num_slots_impl = ((config)&31);
+
+ /*
+ * Initilize the current bus speed and number of hotplug slots
+ * currently connected.
+ */
+ ctrl_p->hc_curr_bus_speed = -1;
+ ctrl_p->hc_num_slots_connected = 0;
+
+ /*
+ * Get the first PCI device Number used.
+ *
+ * PCI-X I/O boat workaround.
+ * The register doesn't set up the correct value.
+ */
+ ppdip = ddi_get_parent(ddi_get_parent(ctrl_p->hc_dip));
+ if ((ddi_prop_get_int(DDI_DEV_T_ANY, ppdip, DDI_PROP_DONTPASS,
+ "vendor-id", -1) == 0x108e) &&
+ (ddi_prop_get_int(DDI_DEV_T_ANY, ppdip, DDI_PROP_DONTPASS,
+ "device-id", -1) == 0x9010))
+ ctrl_p->hc_device_start = 4;
+ else
+ ctrl_p->hc_device_start = ((config>>8)&31);
+
+ /* Get the first Physical device number. */
+ ctrl_p->hc_phys_start = ((config>>16)&0x7ff);
+
+ /* Check if the device numbers increase or decrease. */
+ ctrl_p->hc_device_increases = ((config>>29)&0x1);
+
+ ctrl_p->hc_has_attn =
+ (config & PCI_HP_SLOT_CONFIG_ATTN_BUTTON) ? B_TRUE : B_FALSE;
+ ctrl_p->hc_has_mrl =
+ (config & PCI_HP_SLOT_CONFIG_MRL_SENSOR) ? B_TRUE : B_FALSE;
+
+ ctrl_p->hc_cmd_pending = B_FALSE;
+ ctrl_p->hc_arbiter_timeout = B_FALSE;
+
+ if (ctrl_p->hc_num_slots_impl > PCIE_HP_MAX_SLOTS) {
+ PCIE_DBG("pcishpc_setup_controller() too many SHPC "
+ "slots error\n");
+ return (DDI_FAILURE);
+ }
+
+ return (DDI_SUCCESS);
+}
+
+
+/*
+ * pcishpc_destroy_controller()
+ *
+ * This function deallocates all of the SHPC controller resources.
+ */
+static int
+pcishpc_destroy_controller(dev_info_t *dip)
+{
+ pcie_hp_ctrl_t *ctrl_p;
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
+
+ PCIE_DBG("pcishpc_destroy_controller() called(dip=%p)\n", dip);
+
+ /* get the soft state structure for this dip */
+ if ((ctrl_p = PCIE_GET_HP_CTRL(dip)) == NULL) {
+ PCIE_DBG("pcishpc_destroy_controller() not found\n");
+ return (DDI_FAILURE);
+ }
+
+ /*
+ * Deallocate the slot state structures for this controller.
+ */
+ (void) pcishpc_destroy_slots(ctrl_p);
+ cv_destroy(&ctrl_p->hc_cmd_comp_cv);
+ mutex_destroy(&ctrl_p->hc_mutex);
+ kmem_free(ctrl_p, sizeof (pcie_hp_ctrl_t));
+ bus_p->bus_hp_curr_mode = PCIE_NONE_HP_MODE;
+
+ PCIE_DBG("pcishpc_destroy_controller() success\n");
+ return (DDI_SUCCESS);
+}
+
+/*
+ * pcishpc_create_slot()
+ *
+ * Allocate and add a new HotPlug slot state structure to the linked list.
+ */
+static pcie_hp_slot_t *
+pcishpc_create_slot(pcie_hp_ctrl_t *ctrl_p)
+{
+ pcie_hp_slot_t *slot_p;
+
+ PCIE_DBG("pcishpc_create_slot() called(ctrl_p=%x)\n", ctrl_p);
+
+ /* Allocate a new slot structure. */
+ slot_p = kmem_zalloc(sizeof (pcie_hp_slot_t), KM_SLEEP);
+ slot_p->hs_ctrl = ctrl_p;
+
+ /* Assign an initial value */
+ slot_p->hs_info.cn_state = DDI_HP_CN_STATE_EMPTY;
+
+ PCIE_DBG("pcishpc_create_slot() success\n");
+ return (slot_p);
+}
+
+/*
+ * pcishpc_register_slot()
+ *
+ * Create and register a slot with the Solaris HotPlug framework.
+ */
+static int
+pcishpc_register_slot(pcie_hp_ctrl_t *ctrl_p, int slot)
+{
+ dev_info_t *dip = ctrl_p->hc_dip;
+ pcie_hp_slot_t *slot_p;
+
+ slot_p = pcishpc_create_slot(ctrl_p);
+ ctrl_p->hc_slots[slot] = slot_p;
+ slot_p->hs_num = slot;
+
+ /* Setup the PCI device # for this SHPC slot. */
+ if (ctrl_p->hc_device_increases)
+ slot_p->hs_device_num = ctrl_p->hc_device_start +
+ slot_p->hs_num;
+ else
+ slot_p->hs_device_num = ctrl_p->hc_device_start -
+ slot_p->hs_num;
+
+ /* Setup the DDI HP framework slot information. */
+ slot_p->hs_info.cn_type = DDI_HP_CN_TYPE_PCI;
+ slot_p->hs_info.cn_type_str = PCIE_PCI_HP_TYPE;
+ slot_p->hs_info.cn_child = NULL;
+
+ slot_p->hs_minor = PCI_MINOR_NUM(
+ ddi_get_instance(dip), slot_p->hs_device_num);
+ slot_p->hs_condition = AP_COND_UNKNOWN;
+
+ /* setup thread for handling ATTN button events */
+ if (ctrl_p->hc_has_attn) {
+ PCIE_DBG("pcishpc_register_slot: "
+ "setting up ATTN button event "
+ "handler thread for slot %d\n", slot);
+
+ cv_init(&slot_p->hs_attn_btn_cv, NULL, CV_DRIVER, NULL);
+ slot_p->hs_attn_btn_pending = B_FALSE;
+ slot_p->hs_attn_btn_threadp = thread_create(NULL, 0,
+ pcishpc_attn_btn_handler,
+ (void *)slot_p, 0, &p0, TS_RUN, minclsyspri);
+ slot_p->hs_attn_btn_thread_exit = B_FALSE;
+ }
+
+ /* setup the slot name (used for ap-id) */
+ pcishpc_set_slot_name(ctrl_p, slot);
+
+ pcishpc_get_slot_state(slot_p);
+ if (slot_p->hs_info.cn_state >= DDI_HP_CN_STATE_ENABLED)
+ slot_p->hs_condition = AP_COND_OK;
+
+ /* register the slot with DDI HP framework */
+ if (ndi_hp_register(dip, &slot_p->hs_info) != NDI_SUCCESS) {
+ PCIE_DBG("pciehpc_register_slot() failed to register slot %d\n",
+ slot_p->hs_phy_slot_num);
+ return (DDI_FAILURE);
+ }
+
+ pcie_hp_create_occupant_props(dip, makedevice(ddi_driver_major(dip),
+ slot_p->hs_minor), slot_p->hs_device_num);
+
+ PCIE_DBG("pcishpc_register_slot() success for slot %d\n", slot);
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * pcishpc_destroy_slots()
+ *
+ * Free up all of the slot resources for this controller.
+ */
+static int
+pcishpc_destroy_slots(pcie_hp_ctrl_t *ctrl_p)
+{
+ dev_info_t *dip = ctrl_p->hc_dip;
+ pcie_hp_slot_t *slot_p;
+ int i;
+
+ PCIE_DBG("pcishpc_destroy_slots() called(ctrl_p=%p)\n", ctrl_p);
+
+ for (i = 0; i < PCIE_HP_MAX_SLOTS; i++) {
+ if ((slot_p = ctrl_p->hc_slots[i]) == NULL)
+ continue;
+
+ if (slot_p->hs_attn_btn_threadp != NULL) {
+ mutex_enter(&ctrl_p->hc_mutex);
+ slot_p->hs_attn_btn_thread_exit = B_TRUE;
+ cv_signal(&slot_p->hs_attn_btn_cv);
+ PCIE_DBG("pcishpc_destroy_slots: "
+ "waiting for ATTN thread exit\n");
+ cv_wait(&slot_p->hs_attn_btn_cv, &ctrl_p->hc_mutex);
+ PCIE_DBG("pcishpc_destroy_slots: "
+ "ATTN thread exit\n");
+ cv_destroy(&slot_p->hs_attn_btn_cv);
+ slot_p->hs_attn_btn_threadp = NULL;
+ mutex_exit(&ctrl_p->hc_mutex);
+ }
+
+ PCIE_DBG("pcishpc_destroy_slots() (shpc_p=%p)\n"
+ "destroyed", slot_p);
+
+ pcie_hp_delete_occupant_props(dip,
+ makedevice(ddi_driver_major(dip),
+ slot_p->hs_minor));
+
+ /* unregister the slot with DDI HP framework */
+ if (ndi_hp_unregister(dip, slot_p->hs_info.cn_name) !=
+ NDI_SUCCESS) {
+ PCIE_DBG("pcishpc_destroy_slots() "
+ "failed to unregister slot %d\n",
+ slot_p->hs_phy_slot_num);
+ return (DDI_FAILURE);
+ }
+ kmem_free(slot_p->hs_info.cn_name,
+ strlen(slot_p->hs_info.cn_name) + 1);
+ kmem_free(slot_p, sizeof (pcie_hp_slot_t));
+ }
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * pcishpc_enable_irqs()
+ *
+ * Enable/unmask the different IRQ's we support from the SHPC controller.
+ */
+static int
+pcishpc_enable_irqs(pcie_hp_ctrl_t *ctrl_p)
+{
+ uint32_t reg;
+ int slot;
+
+ reg = pcishpc_read_reg(ctrl_p, PCI_HP_CTRL_SERR_INT_REG);
+
+ /* Enable all interrupts. */
+ reg &= ~PCI_HP_SERR_INT_MASK_ALL;
+
+ pcishpc_write_reg(ctrl_p, PCI_HP_CTRL_SERR_INT_REG, reg);
+
+ /* Unmask the interrupts for each slot. */
+ for (slot = 0; slot < ctrl_p->hc_num_slots_impl; slot++) {
+ reg = pcishpc_read_reg(ctrl_p, PCI_HP_LOGICAL_SLOT_REGS+slot);
+ if ((reg & PCI_HP_SLOT_STATE_MASK) == PCI_HP_SLOT_ENABLED) {
+ reg &= ~(PCI_HP_SLOT_MASK_ALL |
+ PCI_HP_SLOT_MRL_SERR_MASK);
+ ctrl_p->hc_num_slots_connected++;
+ if (ctrl_p->hc_curr_bus_speed == -1)
+ ctrl_p->hc_curr_bus_speed =
+ pcishpc_read_reg(ctrl_p,
+ PCI_HP_PROF_IF_SBCR_REG) &
+ PCI_HP_SBCR_SPEED_MASK;
+ } else {
+ reg &= ~(PCI_HP_SLOT_MASK_ALL);
+ }
+
+ /* Enable/Unmask all slot interrupts. */
+ pcishpc_write_reg(ctrl_p, PCI_HP_LOGICAL_SLOT_REGS+slot, reg);
+ }
+
+ PCIE_DBG("pcishpc_enable_irqs: ctrl_p 0x%p, "
+ "current bus speed 0x%x, slots connected 0x%x\n", ctrl_p,
+ ctrl_p->hc_curr_bus_speed, ctrl_p->hc_num_slots_connected);
+
+ return (DDI_SUCCESS);
+}
+
+
+/*
+ * pcishpc_disable_irqs()
+ *
+ * Disable/Mask the different IRQ's we support from the SHPC controller.
+ */
+static int
+pcishpc_disable_irqs(pcie_hp_ctrl_t *ctrl_p)
+{
+ uint32_t reg;
+ int slot;
+
+ reg = pcishpc_read_reg(ctrl_p, PCI_HP_CTRL_SERR_INT_REG);
+
+ /* Mask all interrupts. */
+ reg |= PCI_HP_SERR_INT_MASK_ALL;
+
+ pcishpc_write_reg(ctrl_p, PCI_HP_CTRL_SERR_INT_REG, reg);
+
+ /* Unmask the interrupts for each slot. */
+ for (slot = 0; slot < ctrl_p->hc_num_slots_impl; slot++) {
+ reg = pcishpc_read_reg(ctrl_p, PCI_HP_LOGICAL_SLOT_REGS+slot);
+
+ /* Disable/Mask all slot interrupts. */
+ reg |= PCI_HP_SLOT_MASK_ALL;
+
+ pcishpc_write_reg(ctrl_p, PCI_HP_LOGICAL_SLOT_REGS+slot, reg);
+ }
+
+ PCIE_DBG("pcishpc_disable_irqs: ctrl_p 0x%p, "
+ "current bus speed 0x%x, slots connected 0x%x\n", ctrl_p,
+ ctrl_p->hc_curr_bus_speed, ctrl_p->hc_num_slots_connected);
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * pcishpc_slot_poweron()
+ *
+ * Poweron/Enable the slot.
+ *
+ * Note: This function is called by DDI HP framework at kernel context only
+ */
+/*ARGSUSED*/
+static int
+pcishpc_slot_poweron(pcie_hp_slot_t *slot_p, ddi_hp_cn_state_t *result_state)
+{
+ uint32_t status;
+
+ PCIE_DBG("pcishpc_slot_poweron called()\n");
+
+ ASSERT(MUTEX_HELD(&slot_p->hs_ctrl->hc_mutex));
+
+ /* get the current slot state */
+ pcishpc_get_slot_state(slot_p);
+
+ /* check if the slot is already in the 'enabled' state */
+ if (slot_p->hs_info.cn_state >= DDI_HP_CN_STATE_POWERED) {
+ /* slot is already in the 'enabled' state */
+ PCIE_DBG("pcishpc_slot_poweron() slot %d already enabled\n",
+ slot_p->hs_phy_slot_num);
+
+ *result_state = slot_p->hs_info.cn_state;
+ return (DDI_SUCCESS);
+ }
+
+ if (slot_p->hs_info.cn_state == DDI_HP_CN_STATE_EMPTY) {
+ PCIE_DBG("pcishpc_slot_poweron() slot in empty state\n");
+ goto cleanup;
+ }
+
+ /* make sure the MRL sensor is closed */
+ status = pcishpc_read_reg(slot_p->hs_ctrl,
+ PCI_HP_LOGICAL_SLOT_REGS+slot_p->hs_num);
+
+ if (status & PCI_HP_SLOT_MRL_STATE_MASK) {
+ PCIE_DBG("pcishpc_slot_poweron() failed: MRL open\n");
+ goto cleanup;
+ }
+
+ /* Set the Power LED to blink */
+ (void) pcishpc_setled(slot_p, PCIE_HP_POWER_LED, PCIE_HP_LED_BLINK);
+
+ /* Turn all other LEDS off */
+ (void) pcishpc_setled(slot_p, PCIE_HP_FAULT_LED, PCIE_HP_LED_OFF);
+ (void) pcishpc_setled(slot_p, PCIE_HP_ATTN_LED, PCIE_HP_LED_OFF);
+ (void) pcishpc_setled(slot_p, PCIE_HP_ACTIVE_LED, PCIE_HP_LED_OFF);
+
+ /* Set the bus speed only if the bus segment is not running */
+ if (pcishpc_set_bus_speed(slot_p) != DDI_SUCCESS) {
+ PCIE_DBG("pcishpc_slot_poweron() setting speed failed\n");
+ goto cleanup;
+ }
+
+ slot_p->hs_ctrl->hc_num_slots_connected++;
+
+ PCIE_DBG("pcishpc_slot_poweron(): slot_p 0x%p, slot state 0x%x, "
+ "current bus speed 0x%x, slots connected 0x%x\n", slot_p,
+ slot_p->hs_info.cn_state, slot_p->hs_ctrl->hc_curr_bus_speed,
+ slot_p->hs_ctrl->hc_num_slots_connected);
+
+ /* Mask or Unmask MRL Sensor SEER bit based on new slot state */
+ if (slot_p->hs_ctrl->hc_has_mrl == B_TRUE) {
+ uint32_t reg;
+
+ reg = pcishpc_read_reg(slot_p->hs_ctrl,
+ PCI_HP_LOGICAL_SLOT_REGS+slot_p->hs_num);
+
+ pcishpc_write_reg(slot_p->hs_ctrl,
+ PCI_HP_LOGICAL_SLOT_REGS+slot_p->hs_num,
+ reg & ~PCI_HP_SLOT_MRL_SERR_MASK);
+ }
+
+ /* Update the hardware slot state. */
+ if (pcishpc_set_slot_state(slot_p,
+ DDI_HP_CN_STATE_ENABLED) != DDI_SUCCESS) {
+ PCIE_DBG("pcishpc_slot_poweron() failed\n");
+
+ pcishpc_get_slot_state(slot_p);
+ goto cleanup;
+ }
+ /* Update the current state. It will be used in pcishpc_setled() */
+ slot_p->hs_info.cn_state = DDI_HP_CN_STATE_ENABLED;
+
+ /* Turn the Power LED ON for a enabled slot. */
+ (void) pcishpc_setled(slot_p, PCIE_HP_POWER_LED, PCIE_HP_LED_ON);
+
+ /* Turn all other LEDS off. */
+ (void) pcishpc_setled(slot_p, PCIE_HP_FAULT_LED, PCIE_HP_LED_OFF);
+ (void) pcishpc_setled(slot_p, PCIE_HP_ATTN_LED, PCIE_HP_LED_OFF);
+ (void) pcishpc_setled(slot_p, PCIE_HP_ACTIVE_LED, PCIE_HP_LED_OFF);
+
+ /* delay after powerON to let the device initialize itself */
+ delay(drv_usectohz(pcishpc_reset_delay));
+
+ PCIE_DBG("pcishpc_slot_poweron() success!\n");
+
+ /*
+ * Want to show up as POWERED state for now. It will be updated to
+ * ENABLED state when user explicitly enable the slot.
+ */
+ slot_p->hs_info.cn_state = DDI_HP_CN_STATE_POWERED;
+
+ /* get the current slot state */
+ pcishpc_get_slot_state(slot_p);
+ /*
+ * It should be poweron'ed now. Have a check here in case any
+ * hardware problems.
+ */
+ if (slot_p->hs_info.cn_state < DDI_HP_CN_STATE_POWERED) {
+ PCIE_DBG("pcishpc_slot_poweron() failed after hardware"
+ " registers all programmed.\n");
+
+ goto cleanup;
+ }
+
+ *result_state = slot_p->hs_info.cn_state;
+
+ return (DDI_SUCCESS);
+
+cleanup:
+ (void) pcishpc_setled(slot_p, PCIE_HP_POWER_LED, PCIE_HP_LED_OFF);
+ return (DDI_FAILURE);
+}
+
+/*ARGSUSED*/
+static int
+pcishpc_slot_poweroff(pcie_hp_slot_t *slot_p, ddi_hp_cn_state_t *result_state)
+{
+ PCIE_DBG("pcishpc_slot_poweroff called()\n");
+
+ ASSERT(MUTEX_HELD(&slot_p->hs_ctrl->hc_mutex));
+
+ /* get the current slot state */
+ pcishpc_get_slot_state(slot_p);
+
+ /* check if the slot is not in the "enabled" or "powered" state */
+ if (slot_p->hs_info.cn_state < DDI_HP_CN_STATE_POWERED) {
+ /* slot is in the 'disabled' state */
+ PCIE_DBG("pcishpc_slot_poweroff(): "
+ "slot %d already disabled\n", slot_p->hs_phy_slot_num);
+
+ *result_state = slot_p->hs_info.cn_state;
+ return (DDI_SUCCESS);
+ }
+
+ /* Set the Power LED to blink */
+ (void) pcishpc_setled(slot_p, PCIE_HP_POWER_LED, PCIE_HP_LED_BLINK);
+
+ /* Turn all other LEDS off */
+ (void) pcishpc_setled(slot_p, PCIE_HP_FAULT_LED, PCIE_HP_LED_OFF);
+ (void) pcishpc_setled(slot_p, PCIE_HP_ATTN_LED, PCIE_HP_LED_OFF);
+ (void) pcishpc_setled(slot_p, PCIE_HP_ACTIVE_LED, PCIE_HP_LED_OFF);
+
+ if (--slot_p->hs_ctrl->hc_num_slots_connected == 0)
+ slot_p->hs_ctrl->hc_curr_bus_speed = -1;
+
+ PCIE_DBG("pcishpc_slot_poweroff(): slot_p 0x%p, slot state 0x%x, "
+ "current bus speed 0x%x, slots connected 0x%x\n", slot_p,
+ slot_p->hs_info.cn_state, slot_p->hs_ctrl->hc_curr_bus_speed,
+ slot_p->hs_ctrl->hc_num_slots_connected);
+
+ /* Mask or Unmask MRL Sensor SEER bit based on new slot state */
+ if (slot_p->hs_ctrl->hc_has_mrl == B_TRUE) {
+ uint32_t reg;
+
+ reg = pcishpc_read_reg(slot_p->hs_ctrl,
+ PCI_HP_LOGICAL_SLOT_REGS+slot_p->hs_num);
+
+ pcishpc_write_reg(slot_p->hs_ctrl,
+ PCI_HP_LOGICAL_SLOT_REGS+slot_p->hs_num,
+ reg | PCI_HP_SLOT_MRL_SERR_MASK);
+ }
+
+ /* Update the hardware slot state. */
+ if (pcishpc_set_slot_state(slot_p, DDI_HP_CN_STATE_PRESENT) !=
+ DDI_SUCCESS) {
+ PCIE_DBG("pcishpc_slot_poweroff() failed\n");
+
+ pcishpc_get_slot_state(slot_p);
+ goto cleanup;
+ }
+
+ /* Update the current state. It will be used in pcishpc_setled() */
+ slot_p->hs_info.cn_state = DDI_HP_CN_STATE_PRESENT;
+
+ /* Turn the Power LED OFF for a disabled slot. */
+ (void) pcishpc_setled(slot_p, PCIE_HP_POWER_LED, PCIE_HP_LED_OFF);
+
+ /* Turn all other LEDS off. */
+ (void) pcishpc_setled(slot_p, PCIE_HP_FAULT_LED, PCIE_HP_LED_OFF);
+ (void) pcishpc_setled(slot_p, PCIE_HP_ATTN_LED, PCIE_HP_LED_OFF);
+ (void) pcishpc_setled(slot_p, PCIE_HP_ACTIVE_LED, PCIE_HP_LED_OFF);
+
+ /* delay after powerON to let the device initialize itself */
+ delay(drv_usectohz(pcishpc_reset_delay));
+
+ pcishpc_get_slot_state(slot_p);
+ /*
+ * It should be poweroff'ed now. Have a check here in case any
+ * hardware problems.
+ */
+ if (slot_p->hs_info.cn_state > DDI_HP_CN_STATE_PRESENT) {
+ PCIE_DBG("pcishpc_slot_poweroff() failed after hardware"
+ " registers all programmed.\n");
+
+ goto cleanup;
+ }
+
+ PCIE_DBG("pcishpc_slot_poweroff() success!\n");
+
+ *result_state = slot_p->hs_info.cn_state;
+ return (DDI_SUCCESS);
+
+cleanup:
+ (void) pcishpc_setled(slot_p, PCIE_HP_POWER_LED, PCIE_HP_LED_OFF);
+ return (DDI_FAILURE);
+}
+
+/*
+ * pcishpc_slot_probe()
+ *
+ * Probe the slot.
+ *
+ * Note: This function is called by DDI HP framework at kernel context only
+ */
+/*ARGSUSED*/
+static int
+pcishpc_slot_probe(pcie_hp_slot_t *slot_p)
+{
+ mutex_enter(&slot_p->hs_ctrl->hc_mutex);
+
+ PCIE_DBG("pcishpc_slot_probe called()\n");
+
+ /* get the current slot state */
+ pcishpc_get_slot_state(slot_p);
+
+ /*
+ * Probe a given PCI Hotplug Connection (CN).
+ */
+ if (pcie_hp_probe(slot_p) != DDI_SUCCESS) {
+ (void) pcishpc_setled(slot_p, PCIE_HP_ATTN_LED,
+ PCIE_HP_LED_BLINK);
+
+ PCIE_DBG("pcishpc_slot_probe() failed\n");
+
+ mutex_exit(&slot_p->hs_ctrl->hc_mutex);
+ return (DDI_FAILURE);
+ }
+
+ PCIE_DBG("pcishpc_slot_probe() success!\n");
+
+ /* get the current slot state */
+ pcishpc_get_slot_state(slot_p);
+
+ mutex_exit(&slot_p->hs_ctrl->hc_mutex);
+ return (DDI_SUCCESS);
+}
+
+/*
+ * pcishpc_slot_unprobe()
+ *
+ * Unprobe the slot.
+ *
+ * Note: This function is called by DDI HP framework at kernel context only
+ */
+/*ARGSUSED*/
+static int
+pcishpc_slot_unprobe(pcie_hp_slot_t *slot_p)
+{
+ mutex_enter(&slot_p->hs_ctrl->hc_mutex);
+
+ PCIE_DBG("pcishpc_slot_unprobe called()\n");
+
+ /* get the current slot state */
+ pcishpc_get_slot_state(slot_p);
+
+ /*
+ * Unprobe a given PCI Hotplug Connection (CN).
+ */
+ if (pcie_hp_unprobe(slot_p) != DDI_SUCCESS) {
+ (void) pcishpc_setled(slot_p, PCIE_HP_ATTN_LED,
+ PCIE_HP_LED_BLINK);
+
+ PCIE_DBG("pcishpc_slot_unprobe() failed\n");
+
+ mutex_exit(&slot_p->hs_ctrl->hc_mutex);
+ return (DDI_FAILURE);
+ }
+
+ PCIE_DBG("pcishpc_slot_unprobe() success!\n");
+
+ /* get the current slot state */
+ pcishpc_get_slot_state(slot_p);
+
+ mutex_exit(&slot_p->hs_ctrl->hc_mutex);
+ return (DDI_SUCCESS);
+}
+
+static int
+pcishpc_upgrade_slot_state(pcie_hp_slot_t *slot_p,
+ ddi_hp_cn_state_t target_state)
+{
+ ddi_hp_cn_state_t curr_state;
+ int rv = DDI_SUCCESS;
+
+ if (target_state > DDI_HP_CN_STATE_ENABLED) {
+ return (DDI_EINVAL);
+ }
+
+ curr_state = slot_p->hs_info.cn_state;
+ while ((curr_state < target_state) && (rv == DDI_SUCCESS)) {
+
+ switch (curr_state) {
+ case DDI_HP_CN_STATE_EMPTY:
+ /*
+ * From EMPTY to PRESENT, just check the hardware
+ * slot state.
+ */
+ pcishpc_get_slot_state(slot_p);
+ curr_state = slot_p->hs_info.cn_state;
+ if (curr_state < DDI_HP_CN_STATE_PRESENT)
+ rv = DDI_FAILURE;
+ break;
+ case DDI_HP_CN_STATE_PRESENT:
+ rv = pcishpc_slot_poweron(slot_p, &curr_state);
+ break;
+ case DDI_HP_CN_STATE_POWERED:
+ curr_state = slot_p->hs_info.cn_state =
+ DDI_HP_CN_STATE_ENABLED;
+ break;
+ default:
+ /* should never reach here */
+ ASSERT("unknown devinfo state");
+ }
+ }
+
+ return (rv);
+}
+
+static int
+pcishpc_downgrade_slot_state(pcie_hp_slot_t *slot_p,
+ ddi_hp_cn_state_t target_state)
+{
+ ddi_hp_cn_state_t curr_state;
+ int rv = DDI_SUCCESS;
+
+
+ curr_state = slot_p->hs_info.cn_state;
+ while ((curr_state > target_state) && (rv == DDI_SUCCESS)) {
+
+ switch (curr_state) {
+ case DDI_HP_CN_STATE_PRESENT:
+ /*
+ * From PRESENT to EMPTY, just check hardware
+ * slot state.
+ */
+ pcishpc_get_slot_state(slot_p);
+ curr_state = slot_p->hs_info.cn_state;
+ if (curr_state >= DDI_HP_CN_STATE_PRESENT)
+ rv = DDI_FAILURE;
+ break;
+ case DDI_HP_CN_STATE_POWERED:
+ rv = pcishpc_slot_poweroff(slot_p, &curr_state);
+
+ break;
+ case DDI_HP_CN_STATE_ENABLED:
+ curr_state = slot_p->hs_info.cn_state =
+ DDI_HP_CN_STATE_POWERED;
+
+ break;
+ default:
+ /* should never reach here */
+ ASSERT("unknown devinfo state");
+ }
+ }
+
+ return (rv);
+}
+
+/* Change slot state to a target state */
+static int
+pcishpc_change_slot_state(pcie_hp_slot_t *slot_p,
+ ddi_hp_cn_state_t target_state)
+{
+ ddi_hp_cn_state_t curr_state;
+ int rv;
+
+ pcishpc_get_slot_state(slot_p);
+ curr_state = slot_p->hs_info.cn_state;
+
+ if (curr_state == target_state) {
+ return (DDI_SUCCESS);
+ }
+ if (curr_state < target_state) {
+
+ rv = pcishpc_upgrade_slot_state(slot_p, target_state);
+ } else {
+ rv = pcishpc_downgrade_slot_state(slot_p, target_state);
+ }
+
+ return (rv);
+}
+
+/*
+ * pcishpc_issue_command()
+ *
+ * Sends a command to the SHPC controller.
+ */
+static int
+pcishpc_issue_command(pcie_hp_ctrl_t *ctrl_p, uint32_t cmd_code)
+{
+ int retCode;
+
+ ASSERT(MUTEX_HELD(&ctrl_p->hc_mutex));
+
+ PCIE_DBG("pcishpc_issue_command() cmd_code=%02x\n", cmd_code);
+
+ ctrl_p->hc_cmd_pending = B_TRUE;
+
+ /* Write the command to the SHPC controller. */
+ pcishpc_write_reg(ctrl_p, PCI_HP_COMMAND_STATUS_REG, cmd_code);
+
+ while (ctrl_p->hc_cmd_pending == B_TRUE)
+ cv_wait(&ctrl_p->hc_cmd_comp_cv, &ctrl_p->hc_mutex);
+
+ /* Wait until the SHPC controller processes the command. */
+ retCode = pcishpc_wait_busy(ctrl_p);
+
+ /* Make sure the command completed. */
+ if (retCode == DDI_SUCCESS) {
+ /* Did the command fail to generate the command complete IRQ? */
+ if (ctrl_p->hc_cmd_pending != B_FALSE) {
+ PCIE_DBG("pcishpc_issue_command() Failed on "
+ "generate cmd complete IRQ\n");
+ retCode = DDI_FAILURE;
+ }
+ }
+
+ if (retCode == DDI_FAILURE)
+ PCIE_DBG("pcishpc_issue_command() Failed on cmd_code=%02x\n",
+ cmd_code);
+ else
+ PCIE_DBG("pcishpc_issue_command() Success on "
+ "cmd_code=%02x\n", cmd_code);
+
+ return (retCode);
+}
+
+/*
+ * pcishpc_wait_busy()
+ *
+ * Wait until the SHPC controller is not busy.
+ */
+static int
+pcishpc_wait_busy(pcie_hp_ctrl_t *ctrl_p)
+{
+ uint32_t status;
+
+ /* Wait until SHPC controller is NOT busy */
+ for (;;) {
+ status = pcishpc_read_reg(ctrl_p, PCI_HP_COMMAND_STATUS_REG);
+
+ /* Is there an MRL Sensor error? */
+ if ((status & PCI_HP_COMM_STS_ERR_MASK) ==
+ PCI_HP_COMM_STS_ERR_MRL_OPEN) {
+ PCIE_DBG("pcishpc_wait_busy() ERROR: "
+ "MRL Sensor error\n");
+ break;
+ }
+
+ /* Is there an Invalid command error? */
+ if ((status & PCI_HP_COMM_STS_ERR_MASK) ==
+ PCI_HP_COMM_STS_ERR_INVALID_COMMAND) {
+ PCIE_DBG("pcishpc_wait_busy() ERROR: Invalid "
+ "command error\n");
+ break;
+ }
+
+ /* Is there an Invalid Speed/Mode error? */
+ if ((status & PCI_HP_COMM_STS_ERR_MASK) ==
+ PCI_HP_COMM_STS_ERR_INVALID_SPEED) {
+ PCIE_DBG("pcishpc_wait_busy() ERROR: Invalid "
+ "Speed/Mode error\n");
+ break;
+ }
+
+ /* Is the SHPC controller not BUSY? */
+ if (!(status & PCI_HP_COMM_STS_CTRL_BUSY)) {
+ /* Return Success. */
+ return (DDI_SUCCESS);
+ }
+
+ PCIE_DBG("pcishpc_wait_busy() SHPC controller busy. Waiting\n");
+
+ /* Wait before polling the status register again. */
+ delay(drv_usectohz(PCIE_HP_CMD_WAIT_TIME));
+ }
+
+ return (DDI_FAILURE);
+}
+
+static void
+pcishpc_attn_btn_handler(pcie_hp_slot_t *slot_p)
+{
+ pcie_hp_led_state_t hs_power_led_state;
+ callb_cpr_t cprinfo;
+
+ PCIE_DBG("pcishpc_attn_btn_handler: thread started\n");
+
+ CALLB_CPR_INIT(&cprinfo, &slot_p->hs_ctrl->hc_mutex,
+ callb_generic_cpr, "pcishpc_attn_btn_handler");
+
+ mutex_enter(&slot_p->hs_ctrl->hc_mutex);
+
+ /* wait for ATTN button event */
+ cv_wait(&slot_p->hs_attn_btn_cv, &slot_p->hs_ctrl->hc_mutex);
+
+ while (slot_p->hs_attn_btn_thread_exit == B_FALSE) {
+ if (slot_p->hs_attn_btn_pending == B_TRUE) {
+ /* get the current state of power LED */
+ hs_power_led_state = slot_p->hs_power_led_state;
+
+ /* Blink the Power LED while we wait for 5 seconds */
+ (void) pcishpc_setled(slot_p, PCIE_HP_POWER_LED,
+ PCIE_HP_LED_BLINK);
+
+ /* wait for 5 seconds before taking any action */
+ if (cv_timedwait(&slot_p->hs_attn_btn_cv,
+ &slot_p->hs_ctrl->hc_mutex,
+ ddi_get_lbolt() + SEC_TO_TICK(5)) == -1) {
+ /*
+ * It is a time out;
+ * make sure the ATTN pending flag is
+ * still ON before sending the event
+ * to DDI HP framework.
+ */
+ if (slot_p->hs_attn_btn_pending == B_TRUE) {
+ int hint;
+
+ /* restore the power LED state */
+ (void) pcishpc_setled(slot_p,
+ PCIE_HP_POWER_LED,
+ hs_power_led_state);
+ /*
+ * send the ATTN button event
+ * to DDI HP framework
+ */
+ slot_p->hs_attn_btn_pending = B_FALSE;
+
+ pcishpc_get_slot_state(slot_p);
+
+ if (slot_p->hs_info.cn_state <=
+ DDI_HP_CN_STATE_PRESENT) {
+ /*
+ * Insertion.
+ */
+ hint = SE_INCOMING_RES;
+ } else {
+ /*
+ * Want to remove;
+ */
+ hint = SE_OUTGOING_RES;
+ }
+ pcie_hp_gen_sysevent_req(
+ slot_p->hs_info.cn_name,
+ hint,
+ slot_p->hs_ctrl->hc_dip,
+ KM_SLEEP);
+
+ continue;
+ }
+ }
+
+ /* restore the power LED state */
+ (void) pcishpc_setled(slot_p, PCIE_HP_POWER_LED,
+ hs_power_led_state);
+ continue;
+ }
+
+ /* wait for another ATTN button event */
+ cv_wait(&slot_p->hs_attn_btn_cv, &slot_p->hs_ctrl->hc_mutex);
+ }
+
+ PCIE_DBG("pcishpc_attn_btn_handler: thread exit\n");
+ cv_signal(&slot_p->hs_attn_btn_cv);
+ CALLB_CPR_EXIT(&cprinfo);
+ thread_exit();
+}
+
+/*
+ * pcishpc_get_slot_state()
+ *
+ * Get the state of the slot.
+ * The slot state should have been initialized before this function gets called.
+ */
+static void
+pcishpc_get_slot_state(pcie_hp_slot_t *slot_p)
+{
+ uint32_t reg;
+ ddi_hp_cn_state_t curr_state = slot_p->hs_info.cn_state;
+
+ /* Read the logical slot register for this Slot. */
+ reg = pcishpc_read_reg(slot_p->hs_ctrl,
+ PCI_HP_LOGICAL_SLOT_REGS+slot_p->hs_num);
+
+ /* Convert from the SHPC slot state to the HPC slot state. */
+ slot_p->hs_info.cn_state = pcishpc_slot_shpc_to_hpc(reg);
+ if (curr_state == DDI_HP_CN_STATE_POWERED &&
+ slot_p->hs_info.cn_state > DDI_HP_CN_STATE_POWERED) {
+ /*
+ * Keep POWERED state if it is currently POWERED state because
+ * this driver does not really implement enable/disable
+ * slot operations. That is, when poweron, it actually enables
+ * the slot also.
+ * So, from hardware view, POWERED == ENABLED.
+ * But, when user explicitly change to POWERED state, it should
+ * be kept until user explicitly change to other states later.
+ */
+ slot_p->hs_info.cn_state = DDI_HP_CN_STATE_POWERED;
+ }
+
+ /* Convert from the SHPC Power LED state to the HPC Power LED state. */
+ slot_p->hs_power_led_state = pcishpc_led_shpc_to_hpc((reg>>2)&3);
+
+ /* Convert from the SHPC Attn LED state to the HPC Attn LED state. */
+ slot_p->hs_attn_led_state = pcishpc_led_shpc_to_hpc((reg>>4)&3);
+
+ /* We don't have a fault LED so just default it to OFF. */
+ slot_p->hs_fault_led_state = PCIE_HP_LED_OFF;
+
+ /* We don't have an active LED so just default it to OFF. */
+ slot_p->hs_active_led_state = PCIE_HP_LED_OFF;
+}
+
+/*
+ * pcishpc_set_slot_state()
+ *
+ * Updates the slot's state and leds.
+ */
+static int
+pcishpc_set_slot_state(pcie_hp_slot_t *slot_p,
+ ddi_hp_cn_state_t new_slot_state)
+{
+ uint32_t reg, cmd_code;
+ ddi_hp_cn_state_t curr_state;
+
+ ASSERT(MUTEX_HELD(&slot_p->hs_ctrl->hc_mutex));
+
+ reg = pcishpc_read_reg(slot_p->hs_ctrl,
+ PCI_HP_LOGICAL_SLOT_REGS+slot_p->hs_num);
+
+ /* Default all states to unchanged. */
+ cmd_code = ((1 + slot_p->hs_num) << 8);
+
+ /* Has the slot state changed? */
+ curr_state = pcishpc_slot_shpc_to_hpc(reg);
+ if (curr_state != new_slot_state) {
+ PCIE_DBG("pcishpc_set_slot_state() Slot State changed");
+
+ /* Set the new slot state in the Slot operation command. */
+ cmd_code |= pcishpc_slot_hpc_to_shpc(new_slot_state);
+ }
+
+ /* Has the Power LED state changed? */
+ if (slot_p->hs_power_led_state != pcishpc_led_shpc_to_hpc((reg>>2)&3)) {
+ PCIE_DBG("pcishpc_set_slot_state() Power LED State changed\n");
+
+ /* Set the new power led state in the Slot operation command. */
+ cmd_code |=
+ (pcishpc_led_hpc_to_shpc(slot_p->hs_power_led_state) << 2);
+ }
+
+ /* Has the Attn LED state changed? */
+ if (slot_p->hs_attn_led_state != pcishpc_led_shpc_to_hpc((reg>>4)&3)) {
+ PCIE_DBG("pcishpc_set_slot_state() Attn LED State changed\n");
+
+ /* Set the new attn led state in the Slot operation command. */
+ cmd_code |=
+ (pcishpc_led_hpc_to_shpc(slot_p->hs_attn_led_state) << 4);
+ }
+
+ return (pcishpc_issue_command(slot_p->hs_ctrl, cmd_code));
+}
+
+/*
+ * setup slot name/slot-number info.
+ */
+static void
+pcishpc_set_slot_name(pcie_hp_ctrl_t *ctrl_p, int slot)
+{
+ pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[slot];
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
+ uchar_t *slotname_data;
+ int *slotnum;
+ uint_t count;
+ int len;
+ uchar_t *s;
+ uint32_t bit_mask;
+ int pci_id_cnt, pci_id_bit;
+ int slots_before, found;
+ int invalid_slotnum = 0;
+
+ if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, ctrl_p->hc_dip,
+ DDI_PROP_DONTPASS, "physical-slot#", &slotnum, &count) ==
+ DDI_PROP_SUCCESS) {
+ slot_p->hs_phy_slot_num = slotnum[0];
+ ddi_prop_free(slotnum);
+ } else {
+ if (ctrl_p->hc_device_increases)
+ slot_p->hs_phy_slot_num = ctrl_p->hc_phys_start + slot;
+ else
+ slot_p->hs_phy_slot_num = ctrl_p->hc_phys_start - slot;
+
+ if ((ndi_prop_update_int(DDI_DEV_T_NONE, ctrl_p->hc_dip,
+ "physical-slot#", slot_p->hs_phy_slot_num)) != DDI_SUCCESS)
+ PCIE_DBG("pcishpc_set_slot_name(): failed to "
+ "create phyical-slot#%d\n",
+ slot_p->hs_phy_slot_num);
+ }
+
+ /* Platform may not have initialized it */
+ if (!slot_p->hs_phy_slot_num) {
+ slot_p->hs_phy_slot_num = pci_config_get8(bus_p->bus_cfg_hdl,
+ PCI_BCNF_SECBUS);
+ invalid_slotnum = 1;
+ }
+ slot_p->hs_info.cn_num = slot_p->hs_phy_slot_num;
+ slot_p->hs_info.cn_num_dpd_on = DDI_HP_CN_NUM_NONE;
+
+ /*
+ * construct the slot_name:
+ * if "slot-names" property exists then use that name
+ * else if valid slot number exists then it is "pci<slot-num>".
+ * else it will be "pci<sec-bus-number>dev<dev-number>"
+ */
+ if (ddi_getlongprop(DDI_DEV_T_ANY, ctrl_p->hc_dip, DDI_PROP_DONTPASS,
+ "slot-names", (caddr_t)&slotname_data, &len) == DDI_PROP_SUCCESS) {
+ bit_mask = slotname_data[3] | (slotname_data[2] << 8) |
+ (slotname_data[1] << 16) | (slotname_data[0] << 24);
+
+ pci_id_bit = 1;
+ pci_id_cnt = slots_before = found = 0;
+
+ /*
+ * Walk the bit mask until we find the bit that corresponds
+ * to our slots device number. We count how many bits
+ * we find before we find our slot's bit.
+ */
+ while (!found && (pci_id_cnt < 32)) {
+ while (slot_p->hs_device_num != pci_id_cnt) {
+
+ /*
+ * Find the next bit set.
+ */
+ while (!(bit_mask & pci_id_bit) &&
+ (pci_id_cnt < 32)) {
+ pci_id_bit = pci_id_bit << 1;
+ pci_id_cnt++;
+ }
+
+ if (slot_p->hs_device_num != pci_id_cnt)
+ slots_before++;
+ else
+ found = 1;
+ }
+ }
+
+ if (pci_id_cnt < 32) {
+
+ /*
+ * Set ptr to first string.
+ */
+ s = slotname_data + 4;
+
+ /*
+ * Increment past all the strings for the slots
+ * before ours.
+ */
+ while (slots_before) {
+ while (*s != NULL)
+ s++;
+ s++;
+ slots_before--;
+ }
+
+ slot_p->hs_info.cn_name = i_ddi_strdup((char *)s,
+ KM_SLEEP);
+ kmem_free(slotname_data, len);
+ return;
+ }
+
+ /* slot-names entry not found */
+ PCIE_DBG("pcishpc_set_slot_name(): "
+ "No slot-names entry found for slot #%d\n",
+ slot_p->hs_phy_slot_num);
+ kmem_free(slotname_data, len);
+ }
+
+ if (invalid_slotnum) {
+ char tmp_name[256];
+
+ (void) snprintf(tmp_name, sizeof (tmp_name), "pci%d",
+ slot_p->hs_device_num);
+ slot_p->hs_info.cn_name = i_ddi_strdup(tmp_name, KM_SLEEP);
+ } else {
+ char tmp_name[256];
+
+ (void) snprintf(tmp_name, sizeof (tmp_name), "pci%d",
+ slot_p->hs_phy_slot_num);
+ slot_p->hs_info.cn_name = i_ddi_strdup(tmp_name, KM_SLEEP);
+ }
+}
+
+/*
+ * pcishpc_set_bus_speed()
+ *
+ * Set the bus speed and mode.
+ */
+static int
+pcishpc_set_bus_speed(pcie_hp_slot_t *slot_p)
+{
+ pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl;
+ int curr_speed = ctrl_p->hc_curr_bus_speed;
+ int speed = -1;
+ int avail_slots;
+ uint32_t status, slots_avail1_reg, slots_avail2_reg;
+
+ ASSERT(MUTEX_HELD(&slot_p->hs_ctrl->hc_mutex));
+
+ /* Make sure that the slot is in a correct state */
+ status = pcishpc_read_reg(ctrl_p,
+ PCI_HP_LOGICAL_SLOT_REGS+slot_p->hs_num);
+
+ /* Return failure if the slot is empty */
+ if ((status & PCI_HP_SLOT_CARD_EMPTY_MASK) ==
+ PCI_HP_SLOT_CARD_EMPTY_MASK) {
+ PCIE_DBG("pcishpc_set_bus_speed() failed: "
+ "the slot is empty\n");
+ return (DDI_FAILURE);
+ }
+
+ /* Return failure if the slot is not in disabled state */
+ if ((status & PCI_HP_SLOT_STATE_MASK) != PCI_HP_SLOT_DISABLED) {
+ PCIE_DBG("pcishpc_set_bus_speed() failed: "
+ "incorrect slot state\n");
+ return (DDI_FAILURE);
+ }
+
+ /* Set the "power-only" mode for the slot */
+ if (pcishpc_issue_command(ctrl_p, ((1+slot_p->hs_num)<<8) |
+ PCI_HP_SLOT_POWER_ONLY) != DDI_SUCCESS) {
+ PCIE_DBG("pcishpc_set_bus_speed() failed to set "
+ "the slot %d in the power-only mode\n", slot_p->hs_num);
+ return (DDI_FAILURE);
+ }
+
+ /* Wait for power good */
+ delay(drv_usectohz(PCIE_HP_POWER_GOOD_WAIT_TIME));
+
+ /* Make sure that the slot is in "power-only" state */
+ status = pcishpc_read_reg(ctrl_p,
+ PCI_HP_LOGICAL_SLOT_REGS+slot_p->hs_num);
+
+ if ((status & PCI_HP_SLOT_STATE_MASK) != PCI_HP_SLOT_POWER_ONLY) {
+ PCIE_DBG("pcishpc_set_bus_speed() "
+ "power-only failed: incorrect slot state\n");
+ return (DDI_FAILURE);
+ }
+
+ slots_avail1_reg = pcishpc_read_reg(ctrl_p,
+ PCI_HP_SLOTS_AVAIL_I_REG);
+ slots_avail2_reg = pcishpc_read_reg(ctrl_p,
+ PCI_HP_SLOTS_AVAIL_II_REG);
+
+ /*
+ * Check if SHPC has available slots and select the highest
+ * available bus speed for the slot.
+ *
+ * The bus speed codes are:
+ * 100 - 133Mhz; <--+
+ * 011 - 100Mhz; <--+ PCI-X
+ * 010 - 66Mhz; <--+
+ *
+ * 001 - 66Mhz; <--+
+ * 000 - 33Mhz <--+ Conv PCI
+ */
+ switch (status & PCI_HP_SLOT_PCIX_CAPABLE_MASK) {
+ case PCI_HP_SLOT_133MHZ_PCIX_CAPABLE:
+ avail_slots = (slots_avail1_reg >>
+ PCI_HP_AVAIL_133MHZ_PCIX_SPEED_SHIFT) &
+ PCI_HP_AVAIL_SPEED_MASK;
+
+ if (((curr_speed == -1) && avail_slots) ||
+ (curr_speed == PCI_HP_SBCR_133MHZ_PCIX_SPEED)) {
+ speed = PCI_HP_SBCR_133MHZ_PCIX_SPEED;
+ break;
+ }
+ /* FALLTHROUGH */
+ case PCI_HP_SLOT_100MHZ_PCIX_CAPABLE:
+ avail_slots = (slots_avail1_reg >>
+ PCI_HP_AVAIL_100MHZ_PCIX_SPEED_SHIFT) &
+ PCI_HP_AVAIL_SPEED_MASK;
+
+ if (((curr_speed == -1) && avail_slots) ||
+ (curr_speed == PCI_HP_SBCR_100MHZ_PCIX_SPEED)) {
+ speed = PCI_HP_SBCR_100MHZ_PCIX_SPEED;
+ break;
+ }
+ /* FALLTHROUGH */
+ case PCI_HP_SLOT_66MHZ_PCIX_CAPABLE:
+ avail_slots = (slots_avail1_reg >>
+ PCI_HP_AVAIL_66MHZ_PCIX_SPEED_SHIFT) &
+ PCI_HP_AVAIL_SPEED_MASK;
+
+ if (((curr_speed == -1) && avail_slots) ||
+ (curr_speed == PCI_HP_SBCR_66MHZ_PCIX_SPEED)) {
+ speed = PCI_HP_SBCR_66MHZ_PCIX_SPEED;
+ break;
+ }
+ /* FALLTHROUGH */
+ default:
+ avail_slots = (slots_avail2_reg >>
+ PCI_HP_AVAIL_66MHZ_CONV_SPEED_SHIFT) &
+ PCI_HP_AVAIL_SPEED_MASK;
+
+ if ((status & PCI_HP_SLOT_66MHZ_CONV_CAPABLE) &&
+ (((curr_speed == -1) && avail_slots) ||
+ (curr_speed == PCI_HP_SBCR_66MHZ_CONV_SPEED))) {
+ speed = PCI_HP_SBCR_66MHZ_CONV_SPEED;
+ } else {
+ avail_slots = (slots_avail1_reg >>
+ PCI_HP_AVAIL_33MHZ_CONV_SPEED_SHIFT) &
+ PCI_HP_AVAIL_SPEED_MASK;
+
+ if (((curr_speed == -1) && (avail_slots)) ||
+ (curr_speed == PCI_HP_SBCR_33MHZ_CONV_SPEED)) {
+ speed = PCI_HP_SBCR_33MHZ_CONV_SPEED;
+ } else {
+ PCIE_DBG("pcishpc_set_bus_speed() "
+ " failed to set the bus speed, slot# %d\n",
+ slot_p->hs_num);
+ return (DDI_FAILURE);
+ }
+ }
+ break;
+ }
+
+ /*
+ * If the bus segment is already running, check to see the card
+ * in the slot can support the current bus speed.
+ */
+ if (curr_speed == speed) {
+ /*
+ * Check to see there is any slot available for the current
+ * bus speed. Otherwise, we need fail the current slot connect
+ * request.
+ */
+ return ((avail_slots <= ctrl_p->hc_num_slots_connected) ?
+ DDI_FAILURE : DDI_SUCCESS);
+ }
+
+ /* Set the bus speed */
+ if (pcishpc_issue_command(ctrl_p, PCI_HP_COMM_STS_SET_SPEED |
+ speed) == DDI_FAILURE) {
+ PCIE_DBG("pcishpc_set_bus_speed() failed "
+ "to set bus %d speed\n", slot_p->hs_num);
+ return (DDI_FAILURE);
+ }
+
+ /* Check the current bus speed */
+ status = pcishpc_read_reg(ctrl_p, PCI_HP_PROF_IF_SBCR_REG) &
+ PCI_HP_SBCR_SPEED_MASK;
+ if ((status & PCI_HP_SBCR_SPEED_MASK) != speed) {
+ PCIE_DBG("pcishpc_set_bus_speed() an incorrect "
+ "bus speed, slot = 0x%x, speed = 0x%x\n",
+ slot_p->hs_num, status & PCI_HP_SBCR_SPEED_MASK);
+ return (DDI_FAILURE);
+ }
+
+
+ /* Save the current bus speed */
+ ctrl_p->hc_curr_bus_speed = speed;
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * pcishpc_setled()
+ *
+ * Change the state of a slot's LED.
+ */
+static int
+pcishpc_setled(pcie_hp_slot_t *slot_p, pcie_hp_led_t led,
+ pcie_hp_led_state_t state)
+{
+ ASSERT(MUTEX_HELD(&slot_p->hs_ctrl->hc_mutex));
+
+ switch (led) {
+ case PCIE_HP_FAULT_LED:
+ PCIE_DBG("pcishpc_setled() - PCIE_HP_FAULT_LED "
+ "(set %s)\n", pcishpc_slot_textledstate(state));
+ slot_p->hs_fault_led_state = state;
+ break;
+
+ case PCIE_HP_POWER_LED:
+ PCIE_DBG("pcishpc_setled() - PCIE_HP_POWER_LED "
+ "(set %s)\n", pcishpc_slot_textledstate(state));
+ slot_p->hs_power_led_state = state;
+ break;
+
+ case PCIE_HP_ATTN_LED:
+ PCIE_DBG("pcishpc_setled() - PCIE_HP_ATTN_LED "
+ "(set %s)\n", pcishpc_slot_textledstate(state));
+ slot_p->hs_attn_led_state = state;
+ break;
+
+ case PCIE_HP_ACTIVE_LED:
+ PCIE_DBG("pcishpc_setled() - PCIE_HP_ACTIVE_LED "
+ "(set %s)\n", pcishpc_slot_textledstate(state));
+ slot_p->hs_active_led_state = state;
+ break;
+ }
+
+ return (pcishpc_set_slot_state(slot_p, slot_p->hs_info.cn_state));
+}
+
+/*
+ * pcishpc_led_shpc_to_hpc()
+ *
+ * Convert from SHPC indicator status to HPC indicator status.
+ */
+static int
+pcishpc_led_shpc_to_hpc(int state)
+{
+ switch (state) {
+ case 1: /* SHPC On bits b01 */
+ return (PCIE_HP_LED_ON);
+ case 2: /* SHPC Blink bits b10 */
+ return (PCIE_HP_LED_BLINK);
+ case 3: /* SHPC Off bits b11 */
+ return (PCIE_HP_LED_OFF);
+ }
+
+ return (PCIE_HP_LED_OFF);
+}
+
+
+/*
+ * pcishpc_led_hpc_to_shpc()
+ *
+ * Convert from HPC indicator status to SHPC indicator status.
+ */
+static int
+pcishpc_led_hpc_to_shpc(int state)
+{
+ switch (state) {
+ case PCIE_HP_LED_ON:
+ return (1); /* SHPC On bits b01 */
+ case PCIE_HP_LED_BLINK:
+ return (2); /* SHPC Blink bits b10 */
+ case PCIE_HP_LED_OFF:
+ return (3); /* SHPC Off bits b11 */
+ }
+
+ return (3); /* SHPC Off bits b11 */
+}
+
+/*
+ * pcishpc_slot_shpc_to_hpc()
+ *
+ * Convert from SHPC slot state to HPC slot state.
+ * The argument shpc_state is expected to be read from the slot register.
+ */
+static int
+pcishpc_slot_shpc_to_hpc(int shpc_state)
+{
+ if ((shpc_state & PCI_HP_SLOT_CARD_EMPTY_MASK) ==
+ PCI_HP_SLOT_CARD_EMPTY_MASK)
+ return (DDI_HP_CN_STATE_EMPTY);
+
+ switch (shpc_state & PCI_HP_SLOT_STATE_MASK) {
+ case PCI_HP_SLOT_POWER_ONLY: /* SHPC Powered Only */
+ return (DDI_HP_CN_STATE_POWERED);
+
+ case PCI_HP_SLOT_ENABLED: /* SHPC Enabled */
+ return (DDI_HP_CN_STATE_ENABLED);
+
+ case PCI_HP_SLOT_DISABLED: /* SHPC Disabled */
+ default : /* SHPC Reserved */
+ return (DDI_HP_CN_STATE_PRESENT);
+ }
+}
+
+/*
+ * pcishpc_slot_hpc_to_shpc()
+ *
+ * Convert from HPC slot state to SHPC slot state.
+ */
+static int
+pcishpc_slot_hpc_to_shpc(int state)
+{
+ switch (state) {
+ case DDI_HP_CN_STATE_EMPTY:
+ return (0);
+
+ case DDI_HP_CN_STATE_POWERED:
+ return (PCI_HP_SLOT_POWER_ONLY);
+
+ case DDI_HP_CN_STATE_ENABLED:
+ return (PCI_HP_SLOT_ENABLED);
+
+ default:
+ return (PCI_HP_SLOT_DISABLED);
+ }
+}
+
+/*
+ * pcishpc_slot_textslotstate()
+ *
+ * Convert the request into a text message.
+ */
+static char *
+pcishpc_slot_textslotstate(ddi_hp_cn_state_t state)
+{
+ /* Convert an HPC slot state into a textual string. */
+ if (state == DDI_HP_CN_STATE_EMPTY)
+ return ("HPC_SLOT_EMPTY");
+ else if (state == DDI_HP_CN_STATE_ENABLED)
+ return ("HPC_SLOT_ENABLED");
+ else if (state == DDI_HP_CN_STATE_POWERED)
+ return ("HPC_SLOT_POWERED_ONLY");
+ else
+ return ("HPC_SLOT_DISABLED");
+}
+
+
+/*
+ * pcishpc_slot_textledstate()
+ *
+ * Convert the led state into a text message.
+ */
+static char *
+pcishpc_slot_textledstate(pcie_hp_led_state_t state)
+{
+ /* Convert an HPC led state into a textual string. */
+ switch (state) {
+ case PCIE_HP_LED_OFF:
+ return ("off");
+
+ case PCIE_HP_LED_ON:
+ return ("on");
+
+ case PCIE_HP_LED_BLINK:
+ return ("blink");
+ }
+ return ("unknown");
+}
+
+
+/*
+ * pcishpc_read_reg()
+ *
+ * Read from a SHPC controller register.
+ */
+static uint32_t
+pcishpc_read_reg(pcie_hp_ctrl_t *ctrl_p, int reg)
+{
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
+
+ /* Setup the SHPC dword select register. */
+ pci_config_put8(bus_p->bus_cfg_hdl,
+ bus_p->bus_pci_hp_off + PCI_HP_DWORD_SELECT_OFF, (uint8_t)reg);
+
+ /* Read back the SHPC dword select register and verify. */
+ if (pci_config_get8(bus_p->bus_cfg_hdl, bus_p->bus_pci_hp_off +
+ PCI_HP_DWORD_SELECT_OFF) != (uint8_t)reg) {
+ PCIE_DBG("pcishpc_read_reg() - Failed writing DWORD "
+ "select reg\n");
+ return (0xFFFFFFFF);
+ }
+
+ /* Read from the SHPC dword data register. */
+ return (pci_config_get32(bus_p->bus_cfg_hdl,
+ bus_p->bus_pci_hp_off + PCI_HP_DWORD_DATA_OFF));
+}
+
+
+/*
+ * pcishpc_write_reg()
+ *
+ * Write to a SHPC controller register.
+ */
+static void
+pcishpc_write_reg(pcie_hp_ctrl_t *ctrl_p, int reg, uint32_t data)
+{
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
+
+ /* Setup the SHPC dword select register. */
+ pci_config_put8(bus_p->bus_cfg_hdl,
+ bus_p->bus_pci_hp_off + PCI_HP_DWORD_SELECT_OFF, (uint8_t)reg);
+
+ /* Read back the SHPC dword select register and verify. */
+ if (pci_config_get8(bus_p->bus_cfg_hdl, bus_p->bus_pci_hp_off +
+ PCI_HP_DWORD_SELECT_OFF) != (uint8_t)reg) {
+ PCIE_DBG("pcishpc_write_reg() - Failed writing "
+ "DWORD select reg\n");
+ return;
+ }
+
+ /* Write to the SHPC dword data register. */
+ pci_config_put32(bus_p->bus_cfg_hdl,
+ bus_p->bus_pci_hp_off + PCI_HP_DWORD_DATA_OFF, data);
+
+ /*
+ * Issue a read of the VendorID/DeviceID just to force the previous
+ * write to complete. This is probably not necessary, but it does
+ * help enforce ordering if there is an issue.
+ */
+ (void) pci_config_get16(bus_p->bus_cfg_hdl, PCI_CONF_VENID);
+}
+
+
+#ifdef DEBUG
+/*
+ * pcishpc_dump_regs()
+ *
+ * Dumps all of the SHPC controller registers.
+ */
+static void
+pcishpc_dump_regs(pcie_hp_ctrl_t *ctrl_p)
+{
+ int slot, numSlots;
+ uint32_t reg;
+ char *state;
+
+ if (!pcie_debug_flags)
+ return;
+
+ PCIE_DBG("pcishpc_dump_regs() called:\n");
+ PCIE_DBG("==========================================================");
+
+ PCIE_DBG("SHPC Base Offset "
+ ": 0x%08x\n", pcishpc_read_reg(ctrl_p, PCI_HP_BASE_OFFSET_REG));
+
+ reg = pcishpc_read_reg(ctrl_p, PCI_HP_SLOTS_AVAIL_I_REG);
+
+ PCIE_DBG("Number of PCIX slots avail (33 Mhz) : %d\n",
+ (reg & 31));
+
+ PCIE_DBG("Number of PCIX slots avail (66 Mhz) : %d\n",
+ ((reg>>8) & 31));
+
+ PCIE_DBG("Number of PCIX slots avail (100 Mhz) : %d\n",
+ ((reg>>16) & 31));
+
+ PCIE_DBG("Number of PCIX slots avail (133 Mhz) : %d\n",
+ ((reg>>24) & 31));
+
+ reg = pcishpc_read_reg(ctrl_p, PCI_HP_SLOTS_AVAIL_II_REG);
+
+ PCIE_DBG("Number of conventional PCI slots (66 Mhz) : %d\n",
+ (reg & 31));
+
+ reg = pcishpc_read_reg(ctrl_p, PCI_HP_SLOT_CONFIGURATION_REG);
+
+ numSlots = (reg & 31);
+
+ PCIE_DBG("Number of Slots connected to this port : %d\n",
+ numSlots);
+
+ PCIE_DBG("PCI Device # for First HotPlug Slot : %d\n",
+ ((reg>>8) & 31));
+
+ PCIE_DBG("Physical Slot # for First PCI Device # : %d\n",
+ ((reg>>16) & 0x7ff));
+
+ PCIE_DBG("Physical Slot Number Up/Down : %d\n",
+ ((reg>>29) & 0x1));
+
+ PCIE_DBG("MRL Sensor Implemented : %s\n",
+ (reg & PCI_HP_SLOT_CONFIG_MRL_SENSOR) ? "Yes" : "No");
+
+ PCIE_DBG("Attention Button Implemented : %s\n",
+ (reg & PCI_HP_SLOT_CONFIG_ATTN_BUTTON) ? "Yes" : "No");
+
+ reg = pcishpc_read_reg(ctrl_p, PCI_HP_PROF_IF_SBCR_REG);
+
+ switch (reg & 7) {
+ case 0:
+ state = "33Mhz Conventional PCI";
+ break;
+ case 1:
+ state = "66Mhz Conventional PCI";
+ break;
+ case 2:
+ state = "66Mhz PCI-X";
+ break;
+ case 3:
+ state = "100Mhz PCI-X";
+ break;
+ case 4:
+ state = "133Mhz PCI-X";
+ break;
+ default:
+ state = "Reserved (Error)";
+ break;
+ }
+
+ PCIE_DBG("Current Port Operation Mode : %s\n", state);
+
+ PCIE_DBG("SHPC Interrupt Message Number : %d\n",
+ ((reg>>16) &31));
+
+ PCIE_DBG("SHPC Programming Interface : %d\n",
+ ((reg>>24) & 0xff));
+
+ reg = pcishpc_read_reg(ctrl_p, PCI_HP_COMMAND_STATUS_REG);
+
+ PCIE_DBG("SHPC Command Code : %d\n",
+ (reg & 0xff));
+
+ PCIE_DBG("SHPC Target Slot : %d\n",
+ ((reg>>8) & 31));
+
+ PCIE_DBG("SHPC Controller Busy : %s\n",
+ ((reg>>16) & 1) ? "Yes" : "No");
+
+ PCIE_DBG("SHPC Controller Err: MRL Sensor : %s\n",
+ ((reg>>17) & 1) ? "Yes" : "No");
+
+ PCIE_DBG("SHPC Controller Err: Invalid Command : %s\n",
+ ((reg>>18) & 1) ? "Yes" : "No");
+
+ PCIE_DBG("SHPC Controller Err: Invalid Speed/Mode : %s\n",
+ ((reg>>19) & 1) ? "Yes" : "No");
+
+ reg = pcishpc_read_reg(ctrl_p, PCI_HP_IRQ_LOCATOR_REG);
+
+ PCIE_DBG("Command Completion Interrupt Pending : %s\n",
+ (reg & PCI_HP_IRQ_CMD_COMPLETE) ? "Yes" : "No");
+
+ for (slot = 0; slot < numSlots; slot++) {
+ PCIE_DBG("Slot %d Interrupt Pending : %s\n", slot+1,
+ (reg & (PCI_HP_IRQ_SLOT_N_PENDING<<slot)) ? "Yes" : "No");
+ }
+
+ reg = pcishpc_read_reg(ctrl_p, PCI_HP_SERR_LOCATOR_REG);
+
+ PCIE_DBG("Arbiter SERR Pending : %s\n",
+ (reg & PCI_HP_IRQ_SERR_ARBITER_PENDING) ? "Yes" : "No");
+
+ for (slot = 0; slot < numSlots; slot++) {
+ PCIE_DBG("Slot %d SERR Pending : %s\n",
+ slot+1, (reg &
+ (PCI_HP_IRQ_SERR_SLOT_N_PENDING<<slot)) ? "Yes" : "No");
+ }
+
+ reg = pcishpc_read_reg(ctrl_p, PCI_HP_CTRL_SERR_INT_REG);
+
+ PCIE_DBG("Global Interrupt Mask : %s\n",
+ (reg & PCI_HP_SERR_INT_GLOBAL_IRQ_MASK) ? "Yes" : "No");
+
+ PCIE_DBG("Global SERR Mask : %s\n",
+ (reg & PCI_HP_SERR_INT_GLOBAL_SERR_MASK) ? "Yes" : "No");
+
+ PCIE_DBG("Command Completion Interrupt Mask : %s\n",
+ (reg & PCI_HP_SERR_INT_CMD_COMPLETE_MASK) ? "Yes" : "No");
+
+ PCIE_DBG("Arbiter SERR Mask : %s\n",
+ (reg & PCI_HP_SERR_INT_ARBITER_SERR_MASK) ? "Yes" : "No");
+
+ PCIE_DBG("Command Completion Detected : %s\n",
+ (reg & PCI_HP_SERR_INT_CMD_COMPLETE_IRQ) ? "Yes" : "No");
+
+ PCIE_DBG("Arbiter Timeout Detected : %s\n",
+ (reg & PCI_HP_SERR_INT_ARBITER_IRQ) ? "Yes" : "No");
+
+ for (slot = 0; slot < numSlots; slot++) {
+ PCIE_DBG("Logical Slot %d Registers:\n", slot+1);
+ PCIE_DBG("------------------------------------\n");
+
+ reg = pcishpc_read_reg(ctrl_p, PCI_HP_LOGICAL_SLOT_REGS+slot);
+
+ PCIE_DBG("Slot %d state : %s\n", slot+1,
+ pcishpc_slot_textslotstate(pcishpc_slot_shpc_to_hpc(reg)));
+
+ PCIE_DBG("Slot %d Power Indicator State : %s\n", slot+1,
+ pcishpc_slot_textledstate(pcishpc_led_shpc_to_hpc(
+ (reg>>2) &3)));
+
+ PCIE_DBG("Slot %d Attention Indicator State : %s\n", slot+1,
+ pcishpc_slot_textledstate(pcishpc_led_shpc_to_hpc(
+ (reg>>4)&3)));
+
+ PCIE_DBG("Slot %d Power Fault : %s\n", slot+1,
+ ((reg>>6)&1) ? "Fault Detected" : "No Fault");
+ PCIE_DBG("Slot %d Attention Button : %s\n", slot+1,
+ ((reg>>7)&1) ? "Depressed" : "Not Depressed");
+ PCIE_DBG("Slot %d MRL Sensor : %s\n", slot+1,
+ ((reg>>8)&1) ? "Not Closed" : "Closed");
+ PCIE_DBG("Slot %d 66mhz Capable : %s\n", slot+1,
+ ((reg>>9)&1) ? "66mhz" : "33mgz");
+
+ switch ((reg>>10)&3) {
+ case 0:
+ state = "Card Present 7.5W";
+ break;
+ case 1:
+ state = "Card Present 15W";
+ break;
+ case 2:
+ state = "Card Present 25W";
+ break;
+ case 3:
+ state = "Slot Empty";
+ break;
+ }
+
+ PCIE_DBG("Slot %d PRSNT1#/PRSNT2# : %s\n", slot+1,
+ state);
+
+ switch ((reg>>12)&3) {
+ case 0:
+ state = "Non PCI-X";
+ break;
+ case 1:
+ state = "66mhz PCI-X";
+ break;
+ case 2:
+ state = "Reserved";
+ break;
+ case 3:
+ state = "133mhz PCI-X";
+ break;
+ }
+
+ PCIE_DBG("Slot %d Card Presence Change Detected : %s\n",
+ slot+1, (reg & PCI_HP_SLOT_PRESENCE_DETECTED) ? "Yes" :
+ "No");
+ PCIE_DBG("Slot %d Isolated Power Fault Detected : %s\n",
+ slot+1, (reg & PCI_HP_SLOT_ISO_PWR_DETECTED) ? "Yes" :
+ "No");
+ PCIE_DBG("Slot %d Attention Button Press Detected : %s\n",
+ slot+1, (reg & PCI_HP_SLOT_ATTN_DETECTED) ? "Yes" : "No");
+ PCIE_DBG("Slot %d MRL Sensor Change Detected : %s\n",
+ slot+1, (reg & PCI_HP_SLOT_MRL_DETECTED) ? "Yes" : "No");
+ PCIE_DBG("Slot %d Connected Power Fault Detected : %s\n",
+ slot+1, (reg & PCI_HP_SLOT_POWER_DETECTED) ? "Yes" : "No");
+
+ PCIE_DBG("Slot %d Card Presence IRQ Masked : %s\n",
+ slot+1, (reg & PCI_HP_SLOT_PRESENCE_MASK) ? "Yes" : "No");
+ PCIE_DBG("Slot %d Isolated Power Fault IRQ Masked : %s\n",
+ slot+1, (reg & PCI_HP_SLOT_ISO_PWR_MASK) ? "Yes" : "No");
+ PCIE_DBG("Slot %d Attention Button IRQ Masked : %s\n",
+ slot+1, (reg & PCI_HP_SLOT_ATTN_MASK) ? "Yes" : "No");
+ PCIE_DBG("Slot %d MRL Sensor IRQ Masked : %s\n",
+ slot+1, (reg & PCI_HP_SLOT_MRL_MASK) ? "Yes" : "No");
+ PCIE_DBG("Slot %d Connected Power Fault IRQ Masked : %s\n",
+ slot+1, (reg & PCI_HP_SLOT_POWER_MASK) ? "Yes" : "No");
+ PCIE_DBG("Slot %d MRL Sensor SERR Masked : %s\n",
+ slot+1, (reg & PCI_HP_SLOT_MRL_SERR_MASK) ? "Yes" : "No");
+ PCIE_DBG("Slot %d Connected Power Fault SERR Masked : %s\n",
+ slot+1, (reg & PCI_HP_SLOT_POWER_SERR_MASK) ? "Yes" : "No");
+ }
+}
+#endif /* DEBUG */
diff --git a/usr/src/uts/common/io/pciex/pcie.c b/usr/src/uts/common/io/pciex/pcie.c
index 4f39719d6b..bf08608fa4 100644
--- a/usr/src/uts/common/io/pciex/pcie.c
+++ b/usr/src/uts/common/io/pciex/pcie.c
@@ -34,10 +34,15 @@
#include <sys/fm/util.h>
#include <sys/promif.h>
#include <sys/disp.h>
-#include <sys/pcie.h>
+#include <sys/stat.h>
+#include <sys/file.h>
#include <sys/pci_cap.h>
+#include <sys/pci_impl.h>
#include <sys/pcie_impl.h>
+#include <sys/hotplug/pci/pcie_hp.h>
+#include <sys/hotplug/pci/pcicfg.h>
+/* Local functions prototypes */
static void pcie_init_pfd(dev_info_t *);
static void pcie_fini_pfd(dev_info_t *);
@@ -48,6 +53,7 @@ static void pcie_check_io_mem_range(ddi_acc_handle_t, boolean_t *, boolean_t *);
#ifdef DEBUG
uint_t pcie_debug_flags = 0;
static void pcie_print_bus(pcie_bus_t *bus_p);
+void pcie_dbg(char *fmt, ...);
#endif /* DEBUG */
/* Variable to control default PCI-Express config settings */
@@ -114,7 +120,7 @@ uint32_t pcie_ecrc_value =
* If a particular platform wants to disable certain errors such as UR/MA,
* instead of using #defines have the platform's PCIe Root Complex driver set
* these masks using the pcie_get_XXX_mask and pcie_set_XXX_mask functions. For
- * x86 the closest thing to a PCIe root complex driver is NPE. For SPARC the
+ * x86 the closest thing to a PCIe root complex driver is NPE. For SPARC the
* closest PCIe root complex driver is PX.
*
* pcie_serr_disable_flag : disable SERR only (in RCR and command reg) x86
@@ -135,13 +141,14 @@ uint32_t pcie_aer_suce_severity = PCIE_AER_SUCE_SERR_ASSERT | \
PCIE_AER_SUCE_USC_MSG_DATA_ERR;
int pcie_max_mps = PCIE_DEVCTL_MAX_PAYLOAD_4096 >> 5;
+int pcie_disable_ari = 0;
static void pcie_scan_mps(dev_info_t *rc_dip, dev_info_t *dip,
int *max_supported);
static int pcie_get_max_supported(dev_info_t *dip, void *arg);
static int pcie_map_phys(dev_info_t *dip, pci_regspec_t *phys_spec,
caddr_t *addrp, ddi_acc_handle_t *handlep);
-static void pcie_unmap_phys(ddi_acc_handle_t *handlep, pci_regspec_t *ph);
+static void pcie_unmap_phys(ddi_acc_handle_t *handlep, pci_regspec_t *ph);
/*
* modload support
@@ -149,7 +156,7 @@ static void pcie_unmap_phys(ddi_acc_handle_t *handlep, pci_regspec_t *ph);
static struct modlmisc modlmisc = {
&mod_miscops, /* Type of module */
- "PCIE: PCI framework"
+ "PCI Express Framework Module"
};
static struct modlinkage modlinkage = {
@@ -199,6 +206,195 @@ _info(struct modinfo *modinfop)
return (mod_info(&modlinkage, modinfop));
}
+/* ARGSUSED */
+int
+pcie_init(dev_info_t *dip, caddr_t arg)
+{
+ int ret = DDI_SUCCESS;
+
+ /*
+ * Create a "devctl" minor node to support DEVCTL_DEVICE_*
+ * and DEVCTL_BUS_* ioctls to this bus.
+ */
+ if ((ret = ddi_create_minor_node(dip, "devctl", S_IFCHR,
+ PCI_MINOR_NUM(ddi_get_instance(dip), PCI_DEVCTL_MINOR),
+ DDI_NT_NEXUS, 0)) != DDI_SUCCESS) {
+ PCIE_DBG("Failed to create devctl minor node for %s%d\n",
+ ddi_driver_name(dip), ddi_get_instance(dip));
+
+ return (ret);
+ }
+
+ if ((ret = pcie_hp_init(dip, arg)) != DDI_SUCCESS) {
+ /*
+ * On a few x86 platforms, we observed unexpected hotplug
+ * initialization failures in recent years. Continue with
+ * a message printed because we don't want to stop PCI
+ * driver attach and system boot because of this hotplug
+ * initialization failure before we address all those issues.
+ */
+ cmn_err(CE_WARN, "%s%d: Failed setting hotplug framework\n",
+ ddi_driver_name(dip), ddi_get_instance(dip));
+
+#if defined(__sparc)
+ ddi_remove_minor_node(dip, "devctl");
+
+ return (ret);
+#endif /* defined(__sparc) */
+ }
+
+ if ((pcie_ari_supported(dip) == PCIE_ARI_FORW_SUPPORTED) &&
+ (pcie_ari_is_enabled(dip) == PCIE_ARI_FORW_DISABLED))
+ (void) pcicfg_configure(dip, 0, PCICFG_ALL_FUNC,
+ PCICFG_FLAG_ENABLE_ARI);
+
+ return (DDI_SUCCESS);
+}
+
+/* ARGSUSED */
+int
+pcie_uninit(dev_info_t *dip)
+{
+ int ret = DDI_SUCCESS;
+
+ if (pcie_ari_is_enabled(dip) == PCIE_ARI_FORW_ENABLED)
+ (void) pcie_ari_disable(dip);
+
+ if ((ret = pcie_hp_uninit(dip)) != DDI_SUCCESS) {
+ PCIE_DBG("Failed to uninitialize hotplug for %s%d\n",
+ ddi_driver_name(dip), ddi_get_instance(dip));
+
+ return (ret);
+ }
+
+ ddi_remove_minor_node(dip, "devctl");
+
+ return (ret);
+}
+
+/* ARGSUSED */
+int
+pcie_intr(dev_info_t *dip)
+{
+ return (pcie_hp_intr(dip));
+}
+
+/* ARGSUSED */
+int
+pcie_open(dev_info_t *dip, dev_t *devp, int flags, int otyp, cred_t *credp)
+{
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
+
+ /*
+ * Make sure the open is for the right file type.
+ */
+ if (otyp != OTYP_CHR)
+ return (EINVAL);
+
+ /*
+ * Handle the open by tracking the device state.
+ */
+ if ((bus_p->bus_soft_state == PCI_SOFT_STATE_OPEN_EXCL) ||
+ ((flags & FEXCL) &&
+ (bus_p->bus_soft_state != PCI_SOFT_STATE_CLOSED))) {
+ return (EBUSY);
+ }
+
+ if (flags & FEXCL)
+ bus_p->bus_soft_state = PCI_SOFT_STATE_OPEN_EXCL;
+ else
+ bus_p->bus_soft_state = PCI_SOFT_STATE_OPEN;
+
+ return (0);
+}
+
+/* ARGSUSED */
+int
+pcie_close(dev_info_t *dip, dev_t dev, int flags, int otyp, cred_t *credp)
+{
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
+
+ if (otyp != OTYP_CHR)
+ return (EINVAL);
+
+ bus_p->bus_soft_state = PCI_SOFT_STATE_CLOSED;
+
+ return (0);
+}
+
+/* ARGSUSED */
+int
+pcie_ioctl(dev_info_t *dip, dev_t dev, int cmd, intptr_t arg, int mode,
+ cred_t *credp, int *rvalp)
+{
+ struct devctl_iocdata *dcp;
+ uint_t bus_state;
+ int rv = DDI_SUCCESS;
+
+ /*
+ * We can use the generic implementation for devctl ioctl
+ */
+ switch (cmd) {
+ case DEVCTL_DEVICE_GETSTATE:
+ case DEVCTL_DEVICE_ONLINE:
+ case DEVCTL_DEVICE_OFFLINE:
+ case DEVCTL_BUS_GETSTATE:
+ return (ndi_devctl_ioctl(dip, cmd, arg, mode, 0));
+ default:
+ break;
+ }
+
+ /*
+ * read devctl ioctl data
+ */
+ if (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS)
+ return (EFAULT);
+
+ switch (cmd) {
+ case DEVCTL_BUS_QUIESCE:
+ if (ndi_get_bus_state(dip, &bus_state) == NDI_SUCCESS)
+ if (bus_state == BUS_QUIESCED)
+ break;
+ (void) ndi_set_bus_state(dip, BUS_QUIESCED);
+ break;
+ case DEVCTL_BUS_UNQUIESCE:
+ if (ndi_get_bus_state(dip, &bus_state) == NDI_SUCCESS)
+ if (bus_state == BUS_ACTIVE)
+ break;
+ (void) ndi_set_bus_state(dip, BUS_ACTIVE);
+ break;
+ case DEVCTL_BUS_RESET:
+ case DEVCTL_BUS_RESETALL:
+ case DEVCTL_DEVICE_RESET:
+ rv = ENOTSUP;
+ break;
+ default:
+ rv = ENOTTY;
+ }
+
+ ndi_dc_freehdl(dcp);
+ return (rv);
+}
+
+/* ARGSUSED */
+int
+pcie_prop_op(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op,
+ int flags, char *name, caddr_t valuep, int *lengthp)
+{
+ if (dev == DDI_DEV_T_ANY)
+ goto skip;
+
+ if (PCIE_IS_HOTPLUG_CAPABLE(dip) &&
+ strcmp(name, "pci-occupant") == 0) {
+ int pci_dev = PCI_MINOR_NUM_TO_PCI_DEVNUM(getminor(dev));
+
+ pcie_hp_create_occupant_props(dip, dev, pci_dev);
+ }
+
+skip:
+ return (ddi_prop_op(dev, dip, prop_op, flags, name, valuep, lengthp));
+}
+
/*
* PCI-Express child device initialization.
* This function enables generic pci-express interrupts and error
@@ -312,6 +508,13 @@ pcie_initchild(dev_info_t *cdip)
pcie_enable_errors(cdip);
}
+ bus_p->bus_ari = B_FALSE;
+ if ((pcie_ari_is_enabled(ddi_get_parent(cdip))
+ == PCIE_ARI_FORW_ENABLED) && (pcie_ari_device(cdip)
+ == PCIE_ARI_DEVICE)) {
+ bus_p->bus_ari = B_TRUE;
+ }
+
if (pcie_initchild_mps(cdip) == DDI_FAILURE)
return (DDI_FAILURE);
@@ -528,6 +731,7 @@ void
pcie_rc_fini_bus(dev_info_t *dip)
{
pcie_bus_t *bus_p = (pcie_bus_t *)ndi_get_bus_private(dip, B_FALSE);
+
ndi_set_bus_private(dip, B_FALSE, NULL, NULL);
kmem_free(bus_p, sizeof (pcie_bus_t));
}
@@ -552,7 +756,6 @@ pcie_init_bus(dev_info_t *cdip)
/* allocate memory for pcie bus data */
bus_p = kmem_zalloc(sizeof (pcie_bus_t), KM_SLEEP);
-
/* Set back pointer to dip */
bus_p->bus_dip = cdip;
@@ -561,8 +764,10 @@ pcie_init_bus(dev_info_t *cdip)
errstr = "Cannot setup config access";
goto fail;
}
+
bus_p->bus_cfg_hdl = eh;
bus_p->bus_fm_flags = 0;
+ bus_p->bus_soft_state = PCI_SOFT_STATE_CLOSED;
/* get device's bus/dev/function number */
if (pcie_get_bdf_from_dip(cdip, &bus_p->bus_bdf) != DDI_SUCCESS) {
@@ -588,6 +793,14 @@ pcie_init_bus(dev_info_t *cdip)
if (PCI_CAP_LOCATE(eh, PCI_CAP_XCFG_SPC(PCIE_EXT_CAP_ID_AER),
&bus_p->bus_aer_off) != DDI_SUCCESS)
bus_p->bus_aer_off = NULL;
+
+ /* Check and save PCIe hotplug capability information */
+ if ((PCIE_IS_RP(bus_p) || PCIE_IS_SWD(bus_p)) &&
+ (PCI_CAP_GET16(eh, NULL, bus_p->bus_pcie_off, PCIE_PCIECAP)
+ & PCIE_PCIECAP_SLOT_IMPL) &&
+ (PCI_CAP_GET32(eh, NULL, bus_p->bus_pcie_off, PCIE_SLOTCAP)
+ & PCIE_SLOTCAP_HP_CAPABLE))
+ bus_p->bus_hp_sup_modes |= PCIE_NATIVE_HP_MODE;
} else {
bus_p->bus_pcie_off = NULL;
bus_p->bus_dev_type = PCIE_PCIECAP_DEV_TYPE_PCI_PSEUDO;
@@ -608,6 +821,11 @@ pcie_init_bus(dev_info_t *cdip)
/* Save the Range information if device is a switch/bridge */
if (PCIE_IS_BDG(bus_p)) {
+ /* Check and save PCI hotplug (SHPC) capability information */
+ if ((PCI_CAP_LOCATE(eh, PCI_CAP_ID_PCI_HOTPLUG,
+ &bus_p->bus_pci_hp_off)) == DDI_SUCCESS)
+ bus_p->bus_hp_sup_modes |= PCIE_PCI_HP_MODE;
+
/* get "bus_range" property */
range_size = sizeof (pci_bus_range_t);
if (ddi_getlongprop_buf(DDI_DEV_T_ANY, cdip, DDI_PROP_DONTPASS,
@@ -661,6 +879,10 @@ pcie_init_bus(dev_info_t *cdip)
ndi_set_bus_private(cdip, B_TRUE, DEVI_PORT_TYPE_PCI, (void *)bus_p);
+ if (PCIE_IS_HOTPLUG_CAPABLE(cdip))
+ (void) ndi_prop_create_boolean(DDI_DEV_T_NONE, cdip,
+ "hotplug-capable");
+
pcie_init_pfd(cdip);
bus_p->bus_mps = 0;
@@ -717,6 +939,10 @@ pcie_fini_bus(dev_info_t *cdip)
bus_p = PCIE_DIP2UPBUS(cdip);
ASSERT(bus_p);
+
+ if (PCIE_IS_HOTPLUG_CAPABLE(cdip))
+ (void) ndi_prop_remove(DDI_DEV_T_NONE, cdip, "hotplug-capable");
+
pci_config_teardown(&bus_p->bus_cfg_hdl);
ndi_set_bus_private(cdip, B_TRUE, NULL, NULL);
kmem_free(bus_p->bus_assigned_addr,
@@ -1025,7 +1251,7 @@ pcie_get_bdf_for_dma_xfer(dev_info_t *dip, dev_info_t *rdip)
/*
* As part of the probing, the PCI fcode interpreter may setup a DMA
* request if a given card has a fcode on it using dip and rdip of the
- * AP (attachment point) i.e, dip and rdip of px/pcieb driver. In this
+ * hotplug connector i.e, dip and rdip of px/pcieb driver. In this
* case, return a invalid value for the bdf since we cannot get to the
* bdf value of the actual device which will be initiating this DMA.
*/
@@ -1152,6 +1378,7 @@ pcie_initchild_mps(dev_info_t *cdip)
int max_payload_size;
pcie_bus_t *bus_p;
dev_info_t *pdip = ddi_get_parent(cdip);
+ uint8_t dev_type;
bus_p = PCIE_DIP2BUS(cdip);
if (bus_p == NULL) {
@@ -1160,6 +1387,21 @@ pcie_initchild_mps(dev_info_t *cdip)
return (DDI_FAILURE);
}
+ dev_type = bus_p->bus_dev_type;
+
+ /*
+ * For ARI Devices, only function zero's MPS needs to be set.
+ */
+ if ((dev_type == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) &&
+ (pcie_ari_is_enabled(pdip) == PCIE_ARI_FORW_ENABLED)) {
+ pcie_req_id_t child_bdf;
+
+ if (pcie_get_bdf_from_dip(cdip, &child_bdf) == DDI_FAILURE)
+ return (DDI_FAILURE);
+ if ((child_bdf & PCIE_REQ_ID_ARI_FUNC_MASK) != 0)
+ return (DDI_SUCCESS);
+ }
+
if (PCIE_IS_RP(bus_p)) {
/*
* If this device is a root port, then the mps scan
@@ -1202,6 +1444,7 @@ pcie_initchild_mps(dev_info_t *cdip)
bus_p->bus_mps = mps;
}
+
return (DDI_SUCCESS);
}
@@ -1244,6 +1487,7 @@ pcie_scan_mps(dev_info_t *rc_dip, dev_info_t *dip, int *max_supported)
ddi_walk_devs(dip, pcie_get_max_supported,
(void *)&max_pay_load_supported);
ndi_devi_exit(ddi_get_parent(dip), circular_count);
+
*max_supported = max_pay_load_supported.highest_common_mps;
}
@@ -1313,7 +1557,7 @@ fail1:
* dip - dip of root complex
*
* Returns - DDI_SUCCESS if there is at least one root port otherwise
- * DDI_FAILURE.
+ * DDI_FAILURE.
*/
int
pcie_root_port(dev_info_t *dip)
@@ -1473,6 +1717,211 @@ pcie_get_rber_fatal(dev_info_t *dip)
return (rp_bus_p->bus_pfd->pe_rber_fatal);
}
+int
+pcie_ari_supported(dev_info_t *dip)
+{
+ uint32_t devcap2;
+ uint16_t pciecap;
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
+ uint8_t dev_type;
+
+ PCIE_DBG("pcie_ari_supported: dip=%p\n", dip);
+
+ if (bus_p == NULL)
+ return (PCIE_ARI_FORW_NOT_SUPPORTED);
+
+ dev_type = bus_p->bus_dev_type;
+
+ if ((dev_type != PCIE_PCIECAP_DEV_TYPE_DOWN) &&
+ (dev_type != PCIE_PCIECAP_DEV_TYPE_ROOT))
+ return (PCIE_ARI_FORW_NOT_SUPPORTED);
+
+ if (pcie_disable_ari) {
+ PCIE_DBG("pcie_ari_supported: dip=%p: ARI Disabled\n", dip);
+ return (PCIE_ARI_FORW_NOT_SUPPORTED);
+ }
+
+ pciecap = PCIE_CAP_GET(16, bus_p, PCIE_PCIECAP);
+
+ if ((pciecap & PCIE_PCIECAP_VER_MASK) < PCIE_PCIECAP_VER_2_0) {
+ PCIE_DBG("pcie_ari_supported: dip=%p: Not 2.0\n", dip);
+ return (PCIE_ARI_FORW_NOT_SUPPORTED);
+ }
+
+ devcap2 = PCIE_CAP_GET(32, bus_p, PCIE_DEVCAP2);
+
+ PCIE_DBG("pcie_ari_supported: dip=%p: DevCap2=0x%x\n",
+ dip, devcap2);
+
+ if (devcap2 & PCIE_DEVCAP2_ARI_FORWARD) {
+ PCIE_DBG("pcie_ari_supported: "
+ "dip=%p: ARI Forwarding is supported\n", dip);
+ return (PCIE_ARI_FORW_SUPPORTED);
+ }
+ return (PCIE_ARI_FORW_NOT_SUPPORTED);
+}
+
+int
+pcie_ari_enable(dev_info_t *dip)
+{
+ uint16_t devctl2;
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
+
+ PCIE_DBG("pcie_ari_enable: dip=%p\n", dip);
+
+ if (pcie_ari_supported(dip) == PCIE_ARI_FORW_NOT_SUPPORTED)
+ return (DDI_FAILURE);
+
+ devctl2 = PCIE_CAP_GET(16, bus_p, PCIE_DEVCTL2);
+ devctl2 |= PCIE_DEVCTL2_ARI_FORWARD_EN;
+ PCIE_CAP_PUT(16, bus_p, PCIE_DEVCTL2, devctl2);
+
+ PCIE_DBG("pcie_ari_enable: dip=%p: writing 0x%x to DevCtl2\n",
+ dip, devctl2);
+
+ return (DDI_SUCCESS);
+}
+
+int
+pcie_ari_disable(dev_info_t *dip)
+{
+ uint16_t devctl2;
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
+
+ PCIE_DBG("pcie_ari_disable: dip=%p\n", dip);
+
+ if (pcie_ari_supported(dip) == PCIE_ARI_FORW_NOT_SUPPORTED)
+ return (DDI_FAILURE);
+
+ devctl2 = PCIE_CAP_GET(16, bus_p, PCIE_DEVCTL2);
+ devctl2 &= ~PCIE_DEVCTL2_ARI_FORWARD_EN;
+ PCIE_CAP_PUT(16, bus_p, PCIE_DEVCTL2, devctl2);
+
+ PCIE_DBG("pcie_ari_disable: dip=%p: writing 0x%x to DevCtl2\n",
+ dip, devctl2);
+
+ return (DDI_SUCCESS);
+}
+
+int
+pcie_ari_is_enabled(dev_info_t *dip)
+{
+ uint16_t devctl2;
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
+
+ PCIE_DBG("pcie_ari_is_enabled: dip=%p\n", dip);
+
+ if (pcie_ari_supported(dip) == PCIE_ARI_FORW_NOT_SUPPORTED)
+ return (PCIE_ARI_FORW_DISABLED);
+
+ devctl2 = PCIE_CAP_GET(32, bus_p, PCIE_DEVCTL2);
+
+ PCIE_DBG("pcie_ari_is_enabled: dip=%p: DevCtl2=0x%x\n",
+ dip, devctl2);
+
+ if (devctl2 & PCIE_DEVCTL2_ARI_FORWARD_EN) {
+ PCIE_DBG("pcie_ari_is_enabled: "
+ "dip=%p: ARI Forwarding is enabled\n", dip);
+ return (PCIE_ARI_FORW_ENABLED);
+ }
+
+ return (PCIE_ARI_FORW_DISABLED);
+}
+
+int
+pcie_ari_device(dev_info_t *dip)
+{
+ ddi_acc_handle_t handle;
+ uint16_t cap_ptr;
+
+ PCIE_DBG("pcie_ari_device: dip=%p\n", dip);
+
+ /*
+ * XXX - This function may be called before the bus_p structure
+ * has been populated. This code can be changed to remove
+ * pci_config_setup()/pci_config_teardown() when the RFE
+ * to populate the bus_p structures early in boot is putback.
+ */
+
+ /* First make sure it is a PCIe device */
+
+ if (pci_config_setup(dip, &handle) != DDI_SUCCESS)
+ return (PCIE_NOT_ARI_DEVICE);
+
+ if ((PCI_CAP_LOCATE(handle, PCI_CAP_ID_PCI_E, &cap_ptr))
+ != DDI_SUCCESS) {
+ pci_config_teardown(&handle);
+ return (PCIE_NOT_ARI_DEVICE);
+ }
+
+ /* Locate the ARI Capability */
+
+ if ((PCI_CAP_LOCATE(handle, PCI_CAP_XCFG_SPC(PCIE_EXT_CAP_ID_ARI),
+ &cap_ptr)) == DDI_FAILURE) {
+ pci_config_teardown(&handle);
+ return (PCIE_NOT_ARI_DEVICE);
+ }
+
+ /* ARI Capability was found so it must be a ARI device */
+ PCIE_DBG("pcie_ari_device: ARI Device dip=%p\n", dip);
+
+ pci_config_teardown(&handle);
+ return (PCIE_ARI_DEVICE);
+}
+
+int
+pcie_ari_get_next_function(dev_info_t *dip, int *func)
+{
+ uint32_t val;
+ uint16_t cap_ptr, next_function;
+ ddi_acc_handle_t handle;
+
+ /*
+ * XXX - This function may be called before the bus_p structure
+ * has been populated. This code can be changed to remove
+ * pci_config_setup()/pci_config_teardown() when the RFE
+ * to populate the bus_p structures early in boot is putback.
+ */
+
+ if (pci_config_setup(dip, &handle) != DDI_SUCCESS)
+ return (DDI_FAILURE);
+
+ if ((PCI_CAP_LOCATE(handle,
+ PCI_CAP_XCFG_SPC(PCIE_EXT_CAP_ID_ARI), &cap_ptr)) == DDI_FAILURE) {
+ pci_config_teardown(&handle);
+ return (DDI_FAILURE);
+ }
+
+ val = PCI_CAP_GET32(handle, NULL, cap_ptr, PCIE_ARI_CAP);
+
+ next_function = (val >> PCIE_ARI_CAP_NEXT_FUNC_SHIFT) &
+ PCIE_ARI_CAP_NEXT_FUNC_MASK;
+
+ pci_config_teardown(&handle);
+
+ *func = next_function;
+
+ return (DDI_SUCCESS);
+}
+
+dev_info_t *
+pcie_func_to_dip(dev_info_t *dip, pcie_req_id_t function)
+{
+ pcie_req_id_t child_bdf;
+ dev_info_t *cdip;
+
+ for (cdip = ddi_get_child(dip); cdip;
+ cdip = ddi_get_next_sibling(cdip)) {
+
+ if (pcie_get_bdf_from_dip(cdip, &child_bdf) == DDI_FAILURE)
+ return (NULL);
+
+ if ((child_bdf & PCIE_REQ_ID_ARI_FUNC_MASK) == function)
+ return (cdip);
+ }
+ return (NULL);
+}
+
#ifdef DEBUG
static void
diff --git a/usr/src/uts/common/io/pciex/pcieb.c b/usr/src/uts/common/io/pciex/pcieb.c
index 298e895044..24d8ebc03d 100644
--- a/usr/src/uts/common/io/pciex/pcieb.c
+++ b/usr/src/uts/common/io/pciex/pcieb.c
@@ -39,18 +39,16 @@
#include <sys/sunddi.h>
#include <sys/sunndi.h>
#include <sys/fm/util.h>
-#include <sys/pcie.h>
#include <sys/pci_cap.h>
+#include <sys/pci_impl.h>
#include <sys/pcie_impl.h>
-#include <sys/hotplug/pci/pcihp.h>
-#include <sys/hotplug/pci/pciehpc.h>
-#include <sys/hotplug/pci/pcishpc.h>
#include <sys/open.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <sys/promif.h> /* prom_printf */
#include <sys/disp.h>
#include <sys/pcie_pwr.h>
+#include <sys/hotplug/pci/pcie_hp.h>
#include "pcieb.h"
#ifdef PX_PLX
#include <io/pciex/pcieb_plx.h>
@@ -120,14 +118,13 @@ static struct bus_ops pcieb_bus_ops = {
i_ndi_busop_access_enter, /* (*bus_fm_access_enter)(); */
i_ndi_busop_access_exit, /* (*bus_fm_access_exit)(); */
pcie_bus_power, /* (*bus_power)(); */
- pcieb_intr_ops /* (*bus_intr_op)(); */
+ pcieb_intr_ops, /* (*bus_intr_op)(); */
+ pcie_hp_common_ops /* (*bus_hp_op)(); */
};
static int pcieb_open(dev_t *, int, int, cred_t *);
static int pcieb_close(dev_t, int, int, cred_t *);
static int pcieb_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
-static int pcieb_prop_op(dev_t, dev_info_t *, ddi_prop_op_t, int, char *,
- caddr_t, int *);
static int pcieb_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
static uint_t pcieb_intr_handler(caddr_t arg1, caddr_t arg2);
@@ -138,9 +135,6 @@ static void pcieb_pwr_teardown(dev_info_t *dip);
static int pcieb_pwr_disable(dev_info_t *dip);
/* Hotplug related functions */
-static int pcieb_pciehpc_probe(dev_info_t *dip, ddi_acc_handle_t config_handle);
-static int pcieb_pcishpc_probe(dev_info_t *dip, ddi_acc_handle_t config_handle);
-static int pcieb_init_hotplug(pcieb_devstate_t *pcieb);
static void pcieb_id_props(pcieb_devstate_t *pcieb);
/*
@@ -161,7 +155,7 @@ static struct cb_ops pcieb_cb_ops = {
nodev, /* mmap */
nodev, /* segmap */
nochpoll, /* poll */
- pcieb_prop_op, /* cb_prop_op */
+ pcie_prop_op, /* cb_prop_op */
NULL, /* streamtab */
D_NEW | D_MP | D_HOTPLUG, /* Driver compatibility flag */
CB_REV, /* rev */
@@ -194,7 +188,7 @@ static struct dev_ops pcieb_ops = {
static struct modldrv modldrv = {
&mod_driverops, /* Type of module */
- "PCIe to PCI nexus driver",
+ "PCIe bridge/switch driver",
&pcieb_ops, /* driver ops */
};
@@ -246,6 +240,36 @@ _info(struct modinfo *modinfop)
return (mod_info(&modlinkage, modinfop));
}
+/* ARGSUSED */
+static int
+pcieb_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
+{
+ minor_t minor = getminor((dev_t)arg);
+ int instance = PCI_MINOR_NUM_TO_INSTANCE(minor);
+ pcieb_devstate_t *pcieb = ddi_get_soft_state(pcieb_state, instance);
+ int ret = DDI_SUCCESS;
+
+ switch (infocmd) {
+ case DDI_INFO_DEVT2INSTANCE:
+ *result = (void *)(intptr_t)instance;
+ break;
+ case DDI_INFO_DEVT2DEVINFO:
+ if (pcieb == NULL) {
+ ret = DDI_FAILURE;
+ break;
+ }
+
+ *result = (void *)pcieb->pcieb_dip;
+ break;
+ default:
+ ret = DDI_FAILURE;
+ break;
+ }
+
+ return (ret);
+}
+
+
/*ARGSUSED*/
static int
pcieb_probe(dev_info_t *devi)
@@ -261,7 +285,6 @@ pcieb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
pcieb_devstate_t *pcieb;
pcie_bus_t *bus_p = PCIE_DIP2UPBUS(devi);
ddi_acc_handle_t config_handle = bus_p->bus_cfg_hdl;
- uint8_t dev_type = bus_p->bus_dev_type;
switch (cmd) {
case DDI_RESUME:
@@ -297,7 +320,6 @@ pcieb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
return (DDI_FAILURE);
pcieb = ddi_get_soft_state(pcieb_state, instance);
pcieb->pcieb_dip = devi;
- pcieb->pcieb_soft_state = PCIEB_SOFT_STATE_CLOSED;
if ((pcieb_fm_init(pcieb)) != DDI_SUCCESS) {
PCIEB_DEBUG(DBG_ATTACH, devi, "Failed in pcieb_fm_init\n");
@@ -356,37 +378,14 @@ pcieb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
pcieb_attach_plx_workarounds(pcieb);
#endif /* PX_PLX */
- /* Initialize hotplug */
- pcieb->pcieb_hotplug_capable = B_FALSE;
-
- if ((dev_type == PCIE_PCIECAP_DEV_TYPE_DOWN) ||
- (dev_type == PCIE_PCIECAP_DEV_TYPE_ROOT) ||
- (dev_type == PCIE_PCIECAP_DEV_TYPE_PCIE2PCI) ||
- (dev_type == PCIE_PCIECAP_DEV_TYPE_PCI2PCIE)) {
- (void) pcieb_init_hotplug(pcieb);
- }
+ if (pcie_init(devi, NULL) != DDI_SUCCESS)
+ goto fail;
/*
* Initialize interrupt handlers. Ignore return value.
*/
(void) pcieb_intr_attach(pcieb);
- if (pcieb->pcieb_hotplug_capable == B_FALSE) {
- /*
- * (for non hotplug bus) this would create ":devctl" minor
- * node to support DEVCTL_DEVICE_* and DEVCTL_BUS_* ioctls
- * to this bus.
- */
- if (ddi_create_minor_node(devi, "devctl", S_IFCHR,
- PCIHP_AP_MINOR_NUM(instance, PCIHP_DEVCTL_MINOR),
- DDI_NT_NEXUS, 0) != DDI_SUCCESS)
- goto fail;
- }
-
- PCIEB_DEBUG(DBG_ATTACH, devi,
- "pcieb_attach: this nexus %s hotplug slots\n",
- pcieb->pcieb_hotplug_capable == B_TRUE ? "has":"has no");
-
/* Do any platform specific workarounds needed at this time */
pcieb_plat_attach_workaround(devi);
@@ -429,19 +428,8 @@ pcieb_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
/* remove interrupt handlers */
pcieb_intr_fini(pcieb);
- if (pcieb->pcieb_hotplug_capable == B_TRUE) {
- if (pcihp_uninit(devi) == DDI_FAILURE)
- error = DDI_FAILURE;
-
- if (pcieb->pcieb_hpc_type == HPC_PCIE)
- (void) pciehpc_uninit(devi);
- else if (pcieb->pcieb_hpc_type == HPC_SHPC)
- (void) pcishpc_uninit(devi);
-
- (void) ndi_prop_remove(DDI_DEV_T_NONE, devi, "hotplug-capable");
- } else {
- ddi_remove_minor_node(devi, "devctl");
- }
+ /* uninitialize inband PCI-E HPC if present */
+ (void) pcie_uninit(devi);
(void) ddi_prop_remove(DDI_DEV_T_NONE, devi, "device_type");
@@ -640,7 +628,7 @@ static int
pcieb_name_child(dev_info_t *child, char *name, int namelen)
{
pci_regspec_t *pci_rp;
- uint_t slot, func;
+ uint_t device, func;
char **unit_addr;
uint_t n;
@@ -677,13 +665,19 @@ pcieb_name_child(dev_info_t *child, char *name, int namelen)
}
/* copy the device identifications */
- slot = PCI_REG_DEV_G(pci_rp[0].pci_phys_hi);
+ device = PCI_REG_DEV_G(pci_rp[0].pci_phys_hi);
func = PCI_REG_FUNC_G(pci_rp[0].pci_phys_hi);
+ if (pcie_ari_is_enabled(ddi_get_parent(child))
+ == PCIE_ARI_FORW_ENABLED) {
+ func = (device << 3) | func;
+ device = 0;
+ }
+
if (func != 0)
- (void) snprintf(name, namelen, "%x,%x", slot, func);
+ (void) snprintf(name, namelen, "%x,%x", device, func);
else
- (void) snprintf(name, namelen, "%x", slot);
+ (void) snprintf(name, namelen, "%x", device);
ddi_prop_free(pci_rp);
return (DDI_SUCCESS);
@@ -911,7 +905,7 @@ pcieb_intr_init(pcieb_devstate_t *pcieb, int intr_type)
(intr_type == DDI_INTR_TYPE_MSI) ? "MSI" : "INTx");
request = 0;
- if (pcieb->pcieb_hotplug_capable) {
+ if (PCIE_IS_HOTPLUG_ENABLED(dip)) {
request++;
is_hp = B_TRUE;
}
@@ -1220,202 +1214,52 @@ pcieb_fm_fini(pcieb_devstate_t *pcieb_p)
static int
pcieb_open(dev_t *devp, int flags, int otyp, cred_t *credp)
{
- pcieb_devstate_t *pcieb_p;
- minor_t minor = getminor(*devp);
- int instance = PCIHP_AP_MINOR_NUM_TO_INSTANCE(minor);
-
- /*
- * Make sure the open is for the right file type.
- */
- if (otyp != OTYP_CHR)
- return (EINVAL);
+ int inst = PCI_MINOR_NUM_TO_INSTANCE(getminor(*devp));
+ pcieb_devstate_t *pcieb = ddi_get_soft_state(pcieb_state, inst);
+ int rv;
- /*
- * Get the soft state structure for the device.
- */
- pcieb_p = (pcieb_devstate_t *)ddi_get_soft_state(pcieb_state,
- instance);
-
- if (pcieb_p == NULL)
+ if (pcieb == NULL)
return (ENXIO);
- if (pcieb_p->pcieb_hotplug_capable == B_TRUE)
- return ((pcihp_get_cb_ops())->cb_open(devp, flags,
- otyp, credp));
+ mutex_enter(&pcieb->pcieb_mutex);
+ rv = pcie_open(pcieb->pcieb_dip, devp, flags, otyp, credp);
+ mutex_exit(&pcieb->pcieb_mutex);
- /*
- * Handle the open by tracking the device state.
- */
- mutex_enter(&pcieb_p->pcieb_mutex);
- if (flags & FEXCL) {
- if (pcieb_p->pcieb_soft_state != PCIEB_SOFT_STATE_CLOSED) {
- mutex_exit(&pcieb_p->pcieb_mutex);
- return (EBUSY);
- }
- pcieb_p->pcieb_soft_state = PCIEB_SOFT_STATE_OPEN_EXCL;
- } else {
- if (pcieb_p->pcieb_soft_state == PCIEB_SOFT_STATE_OPEN_EXCL) {
- mutex_exit(&pcieb_p->pcieb_mutex);
- return (EBUSY);
- }
- pcieb_p->pcieb_soft_state = PCIEB_SOFT_STATE_OPEN;
- }
- mutex_exit(&pcieb_p->pcieb_mutex);
- return (0);
+ return (rv);
}
static int
pcieb_close(dev_t dev, int flags, int otyp, cred_t *credp)
{
- pcieb_devstate_t *pcieb_p;
- minor_t minor = getminor(dev);
- int instance = PCIHP_AP_MINOR_NUM_TO_INSTANCE(minor);
+ int inst = PCI_MINOR_NUM_TO_INSTANCE(getminor(dev));
+ pcieb_devstate_t *pcieb = ddi_get_soft_state(pcieb_state, inst);
+ int rv;
- if (otyp != OTYP_CHR)
- return (EINVAL);
-
- pcieb_p = (pcieb_devstate_t *)ddi_get_soft_state(pcieb_state,
- instance);
-
- if (pcieb_p == NULL)
+ if (pcieb == NULL)
return (ENXIO);
- if (pcieb_p->pcieb_hotplug_capable == B_TRUE)
- return ((pcihp_get_cb_ops())->cb_close(dev, flags,
- otyp, credp));
+ mutex_enter(&pcieb->pcieb_mutex);
+ rv = pcie_close(pcieb->pcieb_dip, dev, flags, otyp, credp);
+ mutex_exit(&pcieb->pcieb_mutex);
- mutex_enter(&pcieb_p->pcieb_mutex);
- pcieb_p->pcieb_soft_state = PCIEB_SOFT_STATE_CLOSED;
- mutex_exit(&pcieb_p->pcieb_mutex);
- return (0);
+ return (rv);
}
static int
pcieb_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
int *rvalp)
{
- pcieb_devstate_t *pcieb_p;
- dev_info_t *self;
- struct devctl_iocdata *dcp;
- uint_t bus_state;
- int rv = 0;
- minor_t minor = getminor(dev);
- int instance = PCIHP_AP_MINOR_NUM_TO_INSTANCE(minor);
-
- pcieb_p = (pcieb_devstate_t *)ddi_get_soft_state(pcieb_state,
- instance);
-
- if (pcieb_p == NULL)
- return (ENXIO);
-
- self = pcieb_p->pcieb_dip;
- if (pcieb_p->pcieb_hotplug_capable == B_TRUE) {
- rv = ((pcihp_get_cb_ops())->cb_ioctl(dev, cmd,
- arg, mode, credp, rvalp));
+ int inst = PCI_MINOR_NUM_TO_INSTANCE(getminor(dev));
+ pcieb_devstate_t *pcieb = ddi_get_soft_state(pcieb_state, inst);
+ int rv;
- pcieb_plat_ioctl_hotplug(self, rv, cmd);
- return (rv);
- }
-
- /*
- * We can use the generic implementation for these ioctls
- */
- switch (cmd) {
- case DEVCTL_DEVICE_GETSTATE:
- case DEVCTL_DEVICE_ONLINE:
- case DEVCTL_DEVICE_OFFLINE:
- case DEVCTL_BUS_GETSTATE:
- return (ndi_devctl_ioctl(self, cmd, arg, mode, 0));
- }
-
- /*
- * read devctl ioctl data
- */
- if (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS)
- return (EFAULT);
-
- switch (cmd) {
-
- case DEVCTL_DEVICE_RESET:
- rv = ENOTSUP;
- break;
-
- case DEVCTL_BUS_QUIESCE:
- if (ndi_get_bus_state(self, &bus_state) == NDI_SUCCESS)
- if (bus_state == BUS_QUIESCED)
- break;
- (void) ndi_set_bus_state(self, BUS_QUIESCED);
- break;
-
- case DEVCTL_BUS_UNQUIESCE:
- if (ndi_get_bus_state(self, &bus_state) == NDI_SUCCESS)
- if (bus_state == BUS_ACTIVE)
- break;
- (void) ndi_set_bus_state(self, BUS_ACTIVE);
- break;
-
- case DEVCTL_BUS_RESET:
- rv = ENOTSUP;
- break;
-
- case DEVCTL_BUS_RESETALL:
- rv = ENOTSUP;
- break;
-
- default:
- rv = ENOTTY;
- }
-
- ndi_dc_freehdl(dcp);
- return (rv);
-}
-
-static int
-pcieb_prop_op(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op,
- int flags, char *name, caddr_t valuep, int *lengthp)
-{
- pcieb_devstate_t *pcieb_p;
- minor_t minor = getminor(dev);
- int instance = PCIHP_AP_MINOR_NUM_TO_INSTANCE(minor);
-
- pcieb_p = (pcieb_devstate_t *)ddi_get_soft_state(pcieb_state,
- instance);
-
- if (pcieb_p == NULL)
+ if (pcieb == NULL)
return (ENXIO);
- if (pcieb_p->pcieb_hotplug_capable == B_TRUE)
- return ((pcihp_get_cb_ops())->cb_prop_op(dev, dip, prop_op,
- flags, name, valuep, lengthp));
-
- return (ddi_prop_op(dev, dip, prop_op, flags, name, valuep, lengthp));
-}
-
-/*ARGSUSED*/
-static int
-pcieb_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
-{
- pcieb_devstate_t *pcieb_p; /* per pcieb state pointer */
- minor_t minor = getminor((dev_t)arg);
- int instance = PCIHP_AP_MINOR_NUM_TO_INSTANCE(minor);
-
- pcieb_p = (pcieb_devstate_t *)ddi_get_soft_state(pcieb_state,
- instance);
+ /* To handle devctl and hotplug related ioctls */
+ rv = pcie_ioctl(pcieb->pcieb_dip, dev, cmd, arg, mode, credp, rvalp);
- switch (infocmd) {
- default:
- return (DDI_FAILURE);
-
- case DDI_INFO_DEVT2INSTANCE:
- *result = (void *)(intptr_t)instance;
- return (DDI_SUCCESS);
-
- case DDI_INFO_DEVT2DEVINFO:
- if (pcieb_p == NULL)
- return (DDI_FAILURE);
- *result = (void *)pcieb_p->pcieb_dip;
- return (DDI_SUCCESS);
- }
+ return (rv);
}
/*
@@ -1444,12 +1288,8 @@ pcieb_intr_handler(caddr_t arg1, caddr_t arg2)
if (isrc == PCIEB_INTR_SRC_UNKNOWN)
goto FAIL;
- if (isrc & PCIEB_INTR_SRC_HP) {
- if (pcieb_p->pcieb_hpc_type == HPC_PCIE)
- ret = pciehpc_intr(dip);
- else if (pcieb_p->pcieb_hpc_type == HPC_SHPC)
- ret = pcishpc_intr(dip);
- }
+ if (isrc & PCIEB_INTR_SRC_HP)
+ ret = pcie_intr(dip);
if (isrc & PCIEB_INTR_SRC_PME)
ret = DDI_INTR_CLAIMED;
@@ -1576,99 +1416,6 @@ pcieb_intr_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op,
}
-/*ARGSUSED*/
-static int pcieb_pciehpc_probe(dev_info_t *dip, ddi_acc_handle_t config_handle)
-{
- uint16_t cap_ptr;
-
- if ((PCI_CAP_LOCATE(config_handle, PCI_CAP_ID_PCI_E, &cap_ptr)) !=
- DDI_FAILURE) {
- uint16_t slotimpl = PCI_CAP_GET16(config_handle, NULL, cap_ptr,
- PCIE_PCIECAP) & PCIE_PCIECAP_SLOT_IMPL;
- if (slotimpl)
- if (PCI_CAP_GET32(config_handle, NULL, cap_ptr,
- PCIE_SLOTCAP) & PCIE_SLOTCAP_HP_CAPABLE)
- return (DDI_SUCCESS);
- }
-
- return (DDI_FAILURE);
-}
-
-static int pcieb_pcishpc_probe(dev_info_t *dip, ddi_acc_handle_t config_handle)
-{
- return (pcieb_plat_pcishpc_probe(dip, config_handle));
-}
-
-/*
- * Initialize hotplug framework if we are hotpluggable.
- * Sets flag in the soft state if Hot Plug is supported and initialized
- * properly.
- */
-/*ARGSUSED*/
-static int
-pcieb_init_hotplug(pcieb_devstate_t *pcieb)
-{
- int rv = DDI_FAILURE;
- pcie_bus_t *bus_p = PCIE_DIP2BUS(pcieb->pcieb_dip);
- ddi_acc_handle_t config_handle = bus_p->bus_cfg_hdl;
- uint8_t dev_type = bus_p->bus_dev_type;
-
-#ifdef PX_PLX
- uint16_t vid = bus_p->bus_dev_ven_id & 0xFFFF;
- uint16_t did = bus_p->bus_dev_ven_id >> 16;
- if ((vid == PXB_VENDOR_PLX) && (did == PXB_DEVICE_PLX_8532) &&
- (bus_p->bus_rev_id <= PXB_DEVICE_PLX_AA_REV))
- return (DDI_SUCCESS);
-#endif /* PX_PLX */
-
- if (((dev_type == PCIE_PCIECAP_DEV_TYPE_DOWN) ||
- (dev_type == PCIE_PCIECAP_DEV_TYPE_PCI2PCIE) ||
- (dev_type == PCIE_PCIECAP_DEV_TYPE_ROOT)) &&
- (pcieb_pciehpc_probe(pcieb->pcieb_dip,
- config_handle) == DDI_SUCCESS)) {
- pcieb->pcieb_hpc_type = HPC_PCIE;
- } else if ((dev_type == PCIE_PCIECAP_DEV_TYPE_PCIE2PCI) &&
- (pcieb_pcishpc_probe(pcieb->pcieb_dip,
- config_handle) == DDI_SUCCESS)) {
- pcieb->pcieb_hpc_type = HPC_SHPC;
- } else {
- pcieb->pcieb_hpc_type = HPC_NONE;
- return (DDI_SUCCESS);
- }
-
- pcieb->pcieb_hotplug_capable = B_TRUE;
-
- if (pcieb->pcieb_hpc_type == HPC_PCIE)
- rv = pciehpc_init(pcieb->pcieb_dip, NULL);
- else if (pcieb->pcieb_hpc_type == HPC_SHPC)
- rv = pcishpc_init(pcieb->pcieb_dip);
-
- if (rv != DDI_SUCCESS)
- goto fail;
-
- if (pcihp_init(pcieb->pcieb_dip) != DDI_SUCCESS) {
- if (pcieb->pcieb_hpc_type == HPC_PCIE)
- (void) pciehpc_uninit(pcieb->pcieb_dip);
- else if (pcieb->pcieb_hpc_type == HPC_SHPC)
- (void) pcishpc_uninit(pcieb->pcieb_dip);
-
- goto fail;
- }
-
- (void) ndi_prop_create_boolean(DDI_DEV_T_NONE, pcieb->pcieb_dip,
- "hotplug-capable");
-
- return (DDI_SUCCESS);
-
-fail:
- pcieb->pcieb_hpc_type = HPC_NONE;
- pcieb->pcieb_hotplug_capable = B_FALSE;
- PCIEB_DEBUG(DBG_ATTACH, pcieb->pcieb_dip, "Failed setting hotplug"
- " framework\n");
-
- return (DDI_FAILURE);
-}
-
/*
* Power management related initialization specific to pcieb.
* Called by pcieb_attach()
@@ -1922,10 +1669,10 @@ pcieb_create_ranges_prop(dev_info_t *dip,
ddi_acc_handle_t config_handle)
{
uint32_t base, limit;
- pcieb_ranges_t ranges[PCIEB_RANGE_LEN];
+ ppb_ranges_t ranges[PCIEB_RANGE_LEN];
uint8_t io_base_lo, io_limit_lo;
uint16_t io_base_hi, io_limit_hi, mem_base, mem_limit;
- int i = 0, rangelen = sizeof (pcieb_ranges_t)/sizeof (int);
+ int i = 0, rangelen = sizeof (ppb_ranges_t)/sizeof (int);
io_base_lo = pci_config_get8(config_handle, PCI_BCNF_IO_BASE_LOW);
io_limit_lo = pci_config_get8(config_handle, PCI_BCNF_IO_LIMIT_LOW);
diff --git a/usr/src/uts/common/io/pciex/pcieb.h b/usr/src/uts/common/io/pciex/pcieb.h
index 17a9052172..7d8556cfc1 100644
--- a/usr/src/uts/common/io/pciex/pcieb.h
+++ b/usr/src/uts/common/io/pciex/pcieb.h
@@ -70,7 +70,6 @@ typedef enum { /* same sequence as pcieb_debug_sym[] */
#define PX_MDT_11 0x01
#define PX_MDT_22 0x10
-
#define NUM_LOGICAL_SLOTS 32
#define PCIEB_RANGE_LEN 2
#define PCIEB_32BIT_IO 1
@@ -82,30 +81,9 @@ typedef enum { /* same sequence as pcieb_debug_sym[] */
#define PCIEB_LADDR(lo, hi) (((uint16_t)(hi) << 16) | (uint16_t)(lo))
#define PCIEB_32bit_MEMADDR(addr) (PCIEB_LADDR(0, ((uint16_t)(addr) & 0xFFF0)))
-/*
- * The following typedef is used to represent an entry in the "ranges"
- * property of a device node.
- */
-typedef struct {
- uint32_t child_high;
- uint32_t child_mid;
- uint32_t child_low;
- uint32_t parent_high;
- uint32_t parent_mid;
- uint32_t parent_low;
- uint32_t size_high;
- uint32_t size_low;
-} pcieb_ranges_t;
-
-typedef enum { HPC_NONE, HPC_PCIE, HPC_SHPC, HPC_OUTBAND } pcieb_hpc_type_t;
-
typedef struct {
dev_info_t *pcieb_dip;
- /* Hotplug support */
- boolean_t pcieb_hotplug_capable;
- pcieb_hpc_type_t pcieb_hpc_type;
-
/* Interrupt support */
ddi_intr_handle_t *pcieb_htable; /* Intr Handlers */
int pcieb_htable_size; /* htable size */
@@ -114,7 +92,6 @@ typedef struct {
int pcieb_intr_type; /* (MSI | FIXED) */
int pcieb_isr_tab[4]; /* MSI source offset */
- uint_t pcieb_soft_state;
int pcieb_init_flags;
kmutex_t pcieb_mutex; /* Soft state mutex */
kmutex_t pcieb_intr_mutex; /* Intr handler mutex */
@@ -156,13 +133,13 @@ extern void *pcieb_state;
*/
#define NVIDIA_VENDOR_ID 0x10de /* Nvidia Vendor Id */
-#ifdef BCM_SW_WORKAROUNDS
+#ifdef PCIEB_BCM
/* Workaround for address space limitation in Broadcom 5714/5715 */
#define PCIEB_ADDR_LIMIT_LO 0ull
#define PCIEB_ADDR_LIMIT_HI ((1ull << 40) - 1)
-#endif /* BCM_SW_WORKAROUNDS */
+#endif /* PCIEB_BCM */
/*
* The following values are used to initialize the cache line size
@@ -177,7 +154,6 @@ extern void pcieb_plat_attach_workaround(dev_info_t *dip);
extern void pcieb_plat_intr_attach(pcieb_devstate_t *pcieb);
extern void pcieb_plat_initchild(dev_info_t *child);
extern void pcieb_plat_uninitchild(dev_info_t *child);
-extern void pcieb_plat_ioctl_hotplug(dev_info_t *dip, int rv, int cmd);
extern int pcieb_plat_ctlops(dev_info_t *rdip, ddi_ctl_enum_t ctlop,
void *arg);
extern int pcieb_plat_pcishpc_probe(dev_info_t *dip,
diff --git a/usr/src/uts/common/os/ddi_hp_impl.c b/usr/src/uts/common/os/ddi_hp_impl.c
new file mode 100644
index 0000000000..6aeb01aac4
--- /dev/null
+++ b/usr/src/uts/common/os/ddi_hp_impl.c
@@ -0,0 +1,1139 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Sun DDI hotplug implementation specific functions
+ */
+
+#include <sys/sysmacros.h>
+#include <sys/types.h>
+#include <sys/file.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kmem.h>
+#include <sys/cmn_err.h>
+#include <sys/debug.h>
+#include <sys/avintr.h>
+#include <sys/autoconf.h>
+#include <sys/ddi.h>
+#include <sys/sunndi.h>
+#include <sys/ndi_impldefs.h>
+#include <sys/sysevent.h>
+#include <sys/sysevent/eventdefs.h>
+#include <sys/sysevent/dr.h>
+#include <sys/fs/dv_node.h>
+
+/*
+ * Local function prototypes
+ */
+/* Connector operations */
+static int ddihp_cn_pre_change_state(ddi_hp_cn_handle_t *hdlp,
+ ddi_hp_cn_state_t target_state);
+static int ddihp_cn_post_change_state(ddi_hp_cn_handle_t *hdlp,
+ ddi_hp_cn_state_t new_state);
+static int ddihp_cn_handle_state_change(ddi_hp_cn_handle_t *hdlp);
+static int ddihp_cn_change_children_state(ddi_hp_cn_handle_t *hdlp,
+ boolean_t online);
+static int ddihp_change_node_state(dev_info_t *dip, void *arg);
+/* Port operations */
+static int ddihp_port_change_state(ddi_hp_cn_handle_t *hdlp,
+ ddi_hp_cn_state_t target_state);
+static int ddihp_port_upgrade_state(ddi_hp_cn_handle_t *hdlp,
+ ddi_hp_cn_state_t target_state);
+static int ddihp_port_downgrade_state(ddi_hp_cn_handle_t *hdlp,
+ ddi_hp_cn_state_t target_state);
+/* Misc routines */
+static void ddihp_update_last_change(ddi_hp_cn_handle_t *hdlp);
+static boolean_t ddihp_check_status_prop(dev_info_t *dip);
+
+/*
+ * Global functions (called within hotplug framework)
+ */
+
+/*
+ * Implement modctl() commands for hotplug.
+ * Called by modctl_hp() in modctl.c
+ */
+int
+ddihp_modctl(int hp_op, char *path, char *cn_name, uintptr_t arg,
+ uintptr_t rval)
+{
+ dev_info_t *dip;
+ ddi_hp_cn_handle_t *hdlp;
+ ddi_hp_op_t op = (ddi_hp_op_t)hp_op;
+ int count, rv, error;
+
+ /* Get the dip of nexus node */
+ dip = e_ddi_hold_devi_by_path(path, 0);
+
+ if (dip == NULL)
+ return (ENXIO);
+
+ DDI_HP_IMPLDBG((CE_CONT, "ddihp_modctl: dip %p op %x path %s "
+ "cn_name %s arg %p rval %p\n", (void *)dip, hp_op, path, cn_name,
+ (void *)arg, (void *)rval));
+
+ if (!NEXUS_HAS_HP_OP(dip)) {
+ ddi_release_devi(dip);
+ return (ENOTSUP);
+ }
+
+ /* Lock before access */
+ ndi_devi_enter(dip, &count);
+
+ hdlp = ddihp_cn_name_to_handle(dip, cn_name);
+
+ if (hp_op == DDI_HPOP_CN_CREATE_PORT) {
+ if (hdlp != NULL) {
+ /* this port already exists. */
+ error = EEXIST;
+
+ goto done;
+ }
+ rv = (*(DEVI(dip)->devi_ops->devo_bus_ops->bus_hp_op))(
+ dip, cn_name, op, NULL, NULL);
+ } else {
+ if (hdlp == NULL) {
+ /* Invalid Connection name */
+ error = ENXIO;
+
+ goto done;
+ }
+ if (hp_op == DDI_HPOP_CN_CHANGE_STATE) {
+ ddi_hp_cn_state_t target_state = (ddi_hp_cn_state_t)arg;
+ ddi_hp_cn_state_t result_state = 0;
+
+ DDIHP_CN_OPS(hdlp, op, (void *)&target_state,
+ (void *)&result_state, rv);
+
+ DDI_HP_IMPLDBG((CE_CONT, "ddihp_modctl: target_state="
+ "%x, result_state=%x, rv=%x \n",
+ target_state, result_state, rv));
+ } else {
+ DDIHP_CN_OPS(hdlp, op, (void *)arg, (void *)rval, rv);
+ }
+ }
+ switch (rv) {
+ case DDI_SUCCESS:
+ error = 0;
+ break;
+ case DDI_EINVAL:
+ error = EINVAL;
+ break;
+ case DDI_EBUSY:
+ error = EBUSY;
+ break;
+ case DDI_ENOTSUP:
+ error = ENOTSUP;
+ break;
+ case DDI_ENOMEM:
+ error = ENOMEM;
+ break;
+ default:
+ error = EIO;
+ }
+
+done:
+ ndi_devi_exit(dip, count);
+
+ ddi_release_devi(dip);
+
+ return (error);
+}
+
+/*
+ * Return the state of Hotplug Connection (CN)
+ */
+int
+ddihp_cn_getstate(ddi_hp_cn_handle_t *hdlp)
+{
+ ddi_hp_cn_state_t new_state;
+ int ret;
+
+ DDI_HP_IMPLDBG((CE_CONT, "ddihp_cn_getstate: pdip %p hdlp %p\n",
+ (void *)hdlp->cn_dip, (void *)hdlp));
+
+ ASSERT(DEVI_BUSY_OWNED(hdlp->cn_dip));
+
+ DDIHP_CN_OPS(hdlp, DDI_HPOP_CN_GET_STATE,
+ NULL, (void *)&new_state, ret);
+ if (ret != DDI_SUCCESS) {
+ DDI_HP_IMPLDBG((CE_CONT, "ddihp_cn_getstate: "
+ "CN %p getstate command failed\n", (void *)hdlp));
+
+ return (ret);
+ }
+
+ DDI_HP_IMPLDBG((CE_CONT, "ddihp_cn_getstate: hdlp %p "
+ "current Connection state %x new Connection state %x\n",
+ (void *)hdlp, hdlp->cn_info.cn_state, new_state));
+
+ if (new_state != hdlp->cn_info.cn_state) {
+ hdlp->cn_info.cn_state = new_state;
+ ddihp_update_last_change(hdlp);
+ }
+
+ return (ret);
+}
+
+/*
+ * Implementation function for unregistering the Hotplug Connection (CN)
+ */
+int
+ddihp_cn_unregister(ddi_hp_cn_handle_t *hdlp)
+{
+ dev_info_t *dip = hdlp->cn_dip;
+
+ DDI_HP_NEXDBG((CE_CONT, "ddihp_cn_unregister: hdlp %p\n",
+ (void *)hdlp));
+
+ ASSERT(DEVI_BUSY_OWNED(dip));
+
+ (void) ddihp_cn_getstate(hdlp);
+
+ if (hdlp->cn_info.cn_state > DDI_HP_CN_STATE_OFFLINE) {
+ DDI_HP_NEXDBG((CE_CONT, "ddihp_cn_unregister: dip %p, hdlp %p "
+ "state %x. Device busy, failed to unregister connection!\n",
+ (void *)dip, (void *)hdlp, hdlp->cn_info.cn_state));
+
+ return (DDI_EBUSY);
+ }
+
+ /* unlink the handle */
+ DDIHP_LIST_REMOVE(ddi_hp_cn_handle_t, (DEVI(dip)->devi_hp_hdlp), hdlp);
+
+ kmem_free(hdlp->cn_info.cn_name, strlen(hdlp->cn_info.cn_name) + 1);
+ kmem_free(hdlp, sizeof (ddi_hp_cn_handle_t));
+ return (DDI_SUCCESS);
+}
+
+/*
+ * For a given Connection name and the dip node where the Connection is
+ * supposed to be, find the corresponding hotplug handle.
+ */
+ddi_hp_cn_handle_t *
+ddihp_cn_name_to_handle(dev_info_t *dip, char *cn_name)
+{
+ ddi_hp_cn_handle_t *hdlp;
+
+ ASSERT(DEVI_BUSY_OWNED(dip));
+
+ DDI_HP_IMPLDBG((CE_CONT, "ddihp_cn_name_to_handle: "
+ "dip %p cn_name to find: %s", (void *)dip, cn_name));
+ for (hdlp = DEVI(dip)->devi_hp_hdlp; hdlp; hdlp = hdlp->next) {
+ DDI_HP_IMPLDBG((CE_CONT, "ddihp_cn_name_to_handle: "
+ "current cn_name: %s", hdlp->cn_info.cn_name));
+
+ if (strcmp(cn_name, hdlp->cn_info.cn_name) == 0) {
+ /* found */
+ return (hdlp);
+ }
+ }
+ DDI_HP_IMPLDBG((CE_CONT, "ddihp_cn_name_to_handle: "
+ "failed to find cn_name"));
+ return (NULL);
+}
+
+/*
+ * Process the hotplug operations for Connector and also create Port
+ * upon user command.
+ */
+int
+ddihp_connector_ops(ddi_hp_cn_handle_t *hdlp, ddi_hp_op_t op,
+ void *arg, void *result)
+{
+ int rv = DDI_SUCCESS;
+ dev_info_t *dip = hdlp->cn_dip;
+
+ ASSERT(DEVI_BUSY_OWNED(dip));
+
+ DDI_HP_IMPLDBG((CE_CONT, "ddihp_connector_ops: pdip=%p op=%x "
+ "hdlp=%p arg=%p\n", (void *)dip, op, (void *)hdlp, arg));
+
+ if (op == DDI_HPOP_CN_CHANGE_STATE) {
+ ddi_hp_cn_state_t target_state = *(ddi_hp_cn_state_t *)arg;
+
+ rv = ddihp_cn_pre_change_state(hdlp, target_state);
+ if (rv != DDI_SUCCESS) {
+ /* the state is not changed */
+ *((ddi_hp_cn_state_t *)result) =
+ hdlp->cn_info.cn_state;
+ return (rv);
+ }
+ }
+ ASSERT(NEXUS_HAS_HP_OP(dip));
+ rv = (*(DEVI(dip)->devi_ops->devo_bus_ops->bus_hp_op))(
+ dip, hdlp->cn_info.cn_name, op, arg, result);
+
+ if (rv != DDI_SUCCESS) {
+ DDI_HP_IMPLDBG((CE_CONT, "ddihp_connector_ops: "
+ "bus_hp_op failed: pdip=%p cn_name:%s op=%x "
+ "hdlp=%p arg=%p\n", (void *)dip, hdlp->cn_info.cn_name,
+ op, (void *)hdlp, arg));
+ }
+ if (op == DDI_HPOP_CN_CHANGE_STATE) {
+ int rv_post;
+
+ DDI_HP_IMPLDBG((CE_CONT, "ddihp_connector_ops: "
+ "old_state=%x, new_state=%x, rv=%x\n",
+ hdlp->cn_info.cn_state, *(ddi_hp_cn_state_t *)result, rv));
+
+ /*
+ * After state change op is successfully done or
+ * failed at some stages, continue to do some jobs.
+ */
+ rv_post = ddihp_cn_post_change_state(hdlp,
+ *(ddi_hp_cn_state_t *)result);
+
+ if (rv_post != DDI_SUCCESS)
+ rv = rv_post;
+ }
+
+ return (rv);
+}
+
+/*
+ * Process the hotplug op for Port
+ */
+int
+ddihp_port_ops(ddi_hp_cn_handle_t *hdlp, ddi_hp_op_t op,
+ void *arg, void *result)
+{
+ int ret = DDI_SUCCESS;
+
+ ASSERT(DEVI_BUSY_OWNED(hdlp->cn_dip));
+
+ DDI_HP_IMPLDBG((CE_CONT, "ddihp_port_ops: pdip=%p op=%x hdlp=%p "
+ "arg=%p\n", (void *)hdlp->cn_dip, op, (void *)hdlp, arg));
+
+ switch (op) {
+ case DDI_HPOP_CN_GET_STATE:
+ {
+ int state;
+
+ state = hdlp->cn_info.cn_state;
+
+ if (hdlp->cn_info.cn_child == NULL) {
+ /* No child. Either present or empty. */
+ if (state >= DDI_HP_CN_STATE_PORT_PRESENT)
+ state = DDI_HP_CN_STATE_PORT_PRESENT;
+ else
+ state = DDI_HP_CN_STATE_PORT_EMPTY;
+
+ } else { /* There is a child of this Port */
+
+ /* Check DEVI(dip)->devi_node_state */
+ switch (i_ddi_node_state(hdlp->cn_info.cn_child)) {
+ case DS_INVAL:
+ case DS_PROTO:
+ case DS_LINKED:
+ case DS_BOUND:
+ case DS_INITIALIZED:
+ case DS_PROBED:
+ state = DDI_HP_CN_STATE_OFFLINE;
+ break;
+ case DS_ATTACHED:
+ state = DDI_HP_CN_STATE_MAINTENANCE;
+ break;
+ case DS_READY:
+ state = DDI_HP_CN_STATE_ONLINE;
+ break;
+ default:
+ /* should never reach here */
+ ASSERT("unknown devinfo state");
+ }
+ /*
+ * Check DEVI(dip)->devi_state in case the node is
+ * downgraded or quiesced.
+ */
+ if (state == DDI_HP_CN_STATE_ONLINE &&
+ ddi_get_devstate(hdlp->cn_info.cn_child) !=
+ DDI_DEVSTATE_UP)
+ state = DDI_HP_CN_STATE_MAINTENANCE;
+ }
+
+ *((ddi_hp_cn_state_t *)result) = state;
+
+ break;
+ }
+ case DDI_HPOP_CN_CHANGE_STATE:
+ {
+ ddi_hp_cn_state_t target_state = *(ddi_hp_cn_state_t *)arg;
+ ddi_hp_cn_state_t curr_state = hdlp->cn_info.cn_state;
+
+ ret = ddihp_port_change_state(hdlp, target_state);
+ if (curr_state != hdlp->cn_info.cn_state) {
+ ddihp_update_last_change(hdlp);
+ }
+ *((ddi_hp_cn_state_t *)result) = hdlp->cn_info.cn_state;
+
+ break;
+ }
+ case DDI_HPOP_CN_REMOVE_PORT:
+ {
+ (void) ddihp_cn_getstate(hdlp);
+
+ if (hdlp->cn_info.cn_state != DDI_HP_CN_STATE_PORT_EMPTY) {
+ /* Only empty PORT can be removed by commands */
+ ret = DDI_EBUSY;
+
+ break;
+ }
+
+ ret = ddihp_cn_unregister(hdlp);
+ break;
+ }
+ default:
+ ret = DDI_ENOTSUP;
+ break;
+ }
+
+ return (ret);
+}
+
+/*
+ * Generate the system event with a possible hint
+ */
+/* ARGSUSED */
+void
+ddihp_cn_gen_sysevent(ddi_hp_cn_handle_t *hdlp,
+ ddi_hp_cn_sysevent_t event_sub_class, int hint, int kmflag)
+{
+ dev_info_t *dip = hdlp->cn_dip;
+ char *cn_path, *ap_id;
+ char *ev_subclass = NULL;
+ nvlist_t *ev_attr_list = NULL;
+ sysevent_id_t eid;
+ int ap_id_len, err;
+
+ cn_path = kmem_zalloc(MAXPATHLEN, kmflag);
+ if (cn_path == NULL) {
+ cmn_err(CE_WARN,
+ "%s%d: Failed to allocate memory for hotplug"
+ " connection: %s\n",
+ ddi_driver_name(dip), ddi_get_instance(dip),
+ hdlp->cn_info.cn_name);
+
+ return;
+ }
+
+ /*
+ * Minor device name will be bus path
+ * concatenated with connection name.
+ * One of consumers of the sysevent will pass it
+ * to cfgadm as AP ID.
+ */
+ (void) strcpy(cn_path, "/devices");
+ (void) ddi_pathname(dip, cn_path + strlen("/devices"));
+
+ ap_id_len = strlen(cn_path) + strlen(":") +
+ strlen(hdlp->cn_info.cn_name) + 1;
+ ap_id = kmem_zalloc(ap_id_len, kmflag);
+ if (ap_id == NULL) {
+ cmn_err(CE_WARN,
+ "%s%d: Failed to allocate memory for AP ID: %s:%s\n",
+ ddi_driver_name(dip), ddi_get_instance(dip),
+ cn_path, hdlp->cn_info.cn_name);
+ kmem_free(cn_path, MAXPATHLEN);
+
+ return;
+ }
+
+ (void) strcpy(ap_id, cn_path);
+ (void) strcat(ap_id, ":");
+ (void) strcat(ap_id, hdlp->cn_info.cn_name);
+ kmem_free(cn_path, MAXPATHLEN);
+
+ err = nvlist_alloc(&ev_attr_list, NV_UNIQUE_NAME_TYPE, kmflag);
+
+ if (err != 0) {
+ cmn_err(CE_WARN,
+ "%s%d: Failed to allocate memory for event subclass %d\n",
+ ddi_driver_name(dip), ddi_get_instance(dip),
+ event_sub_class);
+ kmem_free(ap_id, ap_id_len);
+
+ return;
+ }
+
+ switch (event_sub_class) {
+ case DDI_HP_CN_STATE_CHANGE:
+ ev_subclass = ESC_DR_AP_STATE_CHANGE;
+
+ switch (hint) {
+ case SE_NO_HINT: /* fall through */
+ case SE_HINT_INSERT: /* fall through */
+ case SE_HINT_REMOVE:
+ err = nvlist_add_string(ev_attr_list, DR_HINT,
+ SE_HINT2STR(hint));
+
+ if (err != 0) {
+ cmn_err(CE_WARN, "%s%d: Failed to add attr [%s]"
+ " for %s event\n", ddi_driver_name(dip),
+ ddi_get_instance(dip), DR_HINT,
+ ESC_DR_AP_STATE_CHANGE);
+
+ goto done;
+ }
+ break;
+
+ default:
+ cmn_err(CE_WARN, "%s%d: Unknown hint on sysevent\n",
+ ddi_driver_name(dip), ddi_get_instance(dip));
+
+ goto done;
+ }
+
+ break;
+
+ /* event sub class: DDI_HP_CN_REQ */
+ case DDI_HP_CN_REQ:
+ ev_subclass = ESC_DR_REQ;
+
+ switch (hint) {
+ case SE_INVESTIGATE_RES: /* fall through */
+ case SE_INCOMING_RES: /* fall through */
+ case SE_OUTGOING_RES: /* fall through */
+ err = nvlist_add_string(ev_attr_list, DR_REQ_TYPE,
+ SE_REQ2STR(hint));
+
+ if (err != 0) {
+ cmn_err(CE_WARN,
+ "%s%d: Failed to add attr [%s] for %s \n"
+ "event", ddi_driver_name(dip),
+ ddi_get_instance(dip),
+ DR_REQ_TYPE, ESC_DR_REQ);
+
+ goto done;
+ }
+ break;
+
+ default:
+ cmn_err(CE_WARN, "%s%d: Unknown hint on sysevent\n",
+ ddi_driver_name(dip), ddi_get_instance(dip));
+
+ goto done;
+ }
+
+ break;
+
+ default:
+ cmn_err(CE_WARN, "%s%d: Unknown Event subclass\n",
+ ddi_driver_name(dip), ddi_get_instance(dip));
+
+ goto done;
+ }
+
+ /*
+ * Add Hotplug Connection (CN) as attribute (common attribute)
+ */
+ err = nvlist_add_string(ev_attr_list, DR_AP_ID, ap_id);
+ if (err != 0) {
+ cmn_err(CE_WARN, "%s%d: Failed to add attr [%s] for %s event\n",
+ ddi_driver_name(dip), ddi_get_instance(dip),
+ DR_AP_ID, EC_DR);
+
+ goto done;
+ }
+
+ /*
+ * Log this event with sysevent framework.
+ */
+ err = ddi_log_sysevent(dip, DDI_VENDOR_SUNW, EC_DR,
+ ev_subclass, ev_attr_list, &eid,
+ ((kmflag == KM_SLEEP) ? DDI_SLEEP : DDI_NOSLEEP));
+
+ if (err != 0) {
+ cmn_err(CE_WARN, "%s%d: Failed to log %s event\n",
+ ddi_driver_name(dip), ddi_get_instance(dip), EC_DR);
+ }
+
+done:
+ nvlist_free(ev_attr_list);
+ kmem_free(ap_id, ap_id_len);
+}
+
+/*
+ * Local functions (called within this file)
+ */
+
+/*
+ * Connector operations
+ */
+
+/*
+ * Prepare to change state for a Connector: offline, unprobe, etc.
+ */
+static int
+ddihp_cn_pre_change_state(ddi_hp_cn_handle_t *hdlp,
+ ddi_hp_cn_state_t target_state)
+{
+ ddi_hp_cn_state_t curr_state = hdlp->cn_info.cn_state;
+ dev_info_t *dip = hdlp->cn_dip;
+ int rv = DDI_SUCCESS;
+
+ if (curr_state > target_state &&
+ curr_state == DDI_HP_CN_STATE_ENABLED) {
+ /*
+ * If the Connection goes to a lower state from ENABLED,
+ * then offline all children under it.
+ */
+ rv = ddihp_cn_change_children_state(hdlp, B_FALSE);
+ if (rv != DDI_SUCCESS) {
+ cmn_err(CE_WARN,
+ "(%s%d): "
+ "failed to unconfigure the device in the"
+ " Connection %s\n", ddi_driver_name(dip),
+ ddi_get_instance(dip),
+ hdlp->cn_info.cn_name);
+
+ return (rv);
+ }
+ ASSERT(NEXUS_HAS_HP_OP(dip));
+ /*
+ * Remove all the children and their ports
+ * after they are offlined.
+ */
+ rv = (*(DEVI(dip)->devi_ops->devo_bus_ops->bus_hp_op))(
+ dip, hdlp->cn_info.cn_name, DDI_HPOP_CN_UNPROBE,
+ NULL, NULL);
+ if (rv != DDI_SUCCESS) {
+ cmn_err(CE_WARN,
+ "(%s%d): failed"
+ " to unprobe the device in the Connector"
+ " %s\n", ddi_driver_name(dip),
+ ddi_get_instance(dip),
+ hdlp->cn_info.cn_name);
+
+ return (rv);
+ }
+
+ DDI_HP_NEXDBG((CE_CONT,
+ "ddihp_connector_ops (%s%d): device"
+ " is unconfigured and unprobed in Connector %s\n",
+ ddi_driver_name(dip), ddi_get_instance(dip),
+ hdlp->cn_info.cn_name));
+ }
+
+ return (rv);
+}
+
+/*
+ * Jobs after change state of a Connector: update last change time,
+ * probe, online, sysevent, etc.
+ */
+static int
+ddihp_cn_post_change_state(ddi_hp_cn_handle_t *hdlp,
+ ddi_hp_cn_state_t new_state)
+{
+ int rv = DDI_SUCCESS;
+ ddi_hp_cn_state_t curr_state = hdlp->cn_info.cn_state;
+
+ /* Update the state in handle */
+ if (new_state != curr_state) {
+ hdlp->cn_info.cn_state = new_state;
+ ddihp_update_last_change(hdlp);
+ }
+
+ if (curr_state < new_state &&
+ new_state == DDI_HP_CN_STATE_ENABLED) {
+ /*
+ * Probe and online devices if state is
+ * upgraded to ENABLED.
+ */
+ rv = ddihp_cn_handle_state_change(hdlp);
+ }
+ if (curr_state != hdlp->cn_info.cn_state) {
+ /*
+ * For Connector, generate a sysevent on
+ * state change.
+ */
+ ddihp_cn_gen_sysevent(hdlp, DDI_HP_CN_STATE_CHANGE,
+ SE_NO_HINT, KM_SLEEP);
+ }
+
+ return (rv);
+}
+
+/*
+ * Handle Connector state change.
+ *
+ * This function is called after connector is upgraded to ENABLED sate.
+ * It probes the device plugged in the connector to setup devinfo nodes
+ * and then online the nodes.
+ */
+static int
+ddihp_cn_handle_state_change(ddi_hp_cn_handle_t *hdlp)
+{
+ dev_info_t *dip = hdlp->cn_dip;
+ int rv = DDI_SUCCESS;
+
+ ASSERT(DEVI_BUSY_OWNED(dip));
+ ASSERT(NEXUS_HAS_HP_OP(dip));
+ /*
+ * If the Connection went to state ENABLED from a lower state,
+ * probe it.
+ */
+ rv = (*(DEVI(dip)->devi_ops->devo_bus_ops->bus_hp_op))(
+ dip, hdlp->cn_info.cn_name, DDI_HPOP_CN_PROBE, NULL, NULL);
+
+ if (rv != DDI_SUCCESS) {
+ ddi_hp_cn_state_t target_state = DDI_HP_CN_STATE_POWERED;
+ ddi_hp_cn_state_t result_state = 0;
+
+ /*
+ * Probe failed. Disable the connector so that it can
+ * be enabled again by a later try from userland.
+ */
+ (void) (*(DEVI(dip)->devi_ops->devo_bus_ops->bus_hp_op))(
+ dip, hdlp->cn_info.cn_name, DDI_HPOP_CN_CHANGE_STATE,
+ (void *)&target_state, (void *)&result_state);
+
+ if (result_state && result_state != hdlp->cn_info.cn_state) {
+ hdlp->cn_info.cn_state = result_state;
+ ddihp_update_last_change(hdlp);
+ }
+
+ cmn_err(CE_WARN,
+ "(%s%d): failed to probe the Connection %s\n",
+ ddi_driver_name(dip), ddi_get_instance(dip),
+ hdlp->cn_info.cn_name);
+
+ return (rv);
+ }
+ /*
+ * Try to online all the children of CN.
+ */
+ (void) ddihp_cn_change_children_state(hdlp, B_TRUE);
+
+ DDI_HP_NEXDBG((CE_CONT, "ddihp_cn_event_handler (%s%d): "
+ "device is configured in the Connection %s\n",
+ ddi_driver_name(dip), ddi_get_instance(dip),
+ hdlp->cn_info.cn_name));
+ return (rv);
+}
+
+/*
+ * Online/Offline all the children under the Hotplug Connection (CN)
+ *
+ * Do online operation when the online parameter is true; otherwise do offline.
+ */
+static int
+ddihp_cn_change_children_state(ddi_hp_cn_handle_t *hdlp, boolean_t online)
+{
+ ddi_hp_cn_cfg_t cn_cfg;
+ dev_info_t *dip = hdlp->cn_dip;
+ dev_info_t *cdip;
+ ddi_hp_cn_handle_t *h;
+ int rv = DDI_SUCCESS;
+
+ DDI_HP_IMPLDBG((CE_CONT, "ddihp_cn_change_children_state:"
+ " dip %p hdlp %p, online %x\n",
+ (void *)dip, (void *)hdlp, online));
+
+ cn_cfg.online = online;
+ ASSERT(DEVI_BUSY_OWNED(dip));
+
+ /*
+ * Return invalid if Connection state is < DDI_HP_CN_STATE_ENABLED
+ * when try to online children.
+ */
+ if (online && hdlp->cn_info.cn_state < DDI_HP_CN_STATE_ENABLED) {
+ DDI_HP_IMPLDBG((CE_CONT, "ddihp_cn_change_children_state: "
+ "Connector %p is not in probed state\n", (void *)hdlp));
+
+ return (DDI_EINVAL);
+ }
+
+ /* Now, online/offline all the devices depending on the Connector */
+
+ if (!online) {
+ /*
+ * For offline operation we need to firstly clean up devfs
+ * so as not to prevent driver detach.
+ */
+ (void) devfs_clean(dip, NULL, DV_CLEAN_FORCE);
+ }
+ for (h = DEVI(dip)->devi_hp_hdlp; h; h = h->next) {
+ if (h->cn_info.cn_type != DDI_HP_CN_TYPE_VIRTUAL_PORT)
+ continue;
+
+ if (h->cn_info.cn_num_dpd_on !=
+ hdlp->cn_info.cn_num)
+ continue;
+
+ cdip = h->cn_info.cn_child;
+ ASSERT(cdip);
+ if (online) {
+ /* online children */
+ if (!ddihp_check_status_prop(dip))
+ continue;
+
+ if (ndi_devi_online(cdip,
+ NDI_ONLINE_ATTACH | NDI_CONFIG) != NDI_SUCCESS) {
+ cmn_err(CE_WARN,
+ "(%s%d):"
+ " failed to attach driver for a device"
+ " (%s%d) under the Connection %s\n",
+ ddi_driver_name(dip), ddi_get_instance(dip),
+ ddi_driver_name(cdip),
+ ddi_get_instance(cdip),
+ hdlp->cn_info.cn_name);
+ /*
+ * One of the devices failed to online, but we
+ * want to continue to online the rest siblings
+ * after mark the failure here.
+ */
+ rv = DDI_FAILURE;
+
+ continue;
+ }
+ cn_cfg.rv = NDI_SUCCESS;
+ if (ddi_get_child(cdip)) {
+ /* Continue to online grand children */
+ int c;
+
+ ndi_devi_enter(cdip, &c);
+ ddi_walk_devs(ddi_get_child(cdip),
+ ddihp_change_node_state,
+ (void *)&cn_cfg);
+ ndi_devi_exit(cdip, c);
+ }
+ if (cn_cfg.rv != NDI_SUCCESS) {
+ /*
+ * one of the grand children is not ONLINE'd.
+ */
+ cmn_err(CE_WARN,
+ "(%s%d):"
+ " failed to attach driver for a grandchild"
+ "device (%s%d) in the Connection %s\n",
+ ddi_driver_name(dip), ddi_get_instance(dip),
+ ddi_driver_name(cdip),
+ ddi_get_instance(cdip),
+ hdlp->cn_info.cn_name);
+
+ rv = DDI_FAILURE;
+ }
+
+ } else {
+ /* offline children */
+ if (ndi_devi_offline(cdip, NDI_UNCONFIG) !=
+ NDI_SUCCESS) {
+ cmn_err(CE_WARN,
+ "(%s%d):"
+ " failed to dettach driver for the device"
+ " (%s%d) in the Connection %s\n",
+ ddi_driver_name(dip), ddi_get_instance(dip),
+ ddi_driver_name(cdip),
+ ddi_get_instance(cdip),
+ hdlp->cn_info.cn_name);
+
+ return (DDI_EBUSY);
+ }
+ }
+ }
+
+ return (rv);
+}
+
+/*
+ * This function is called to online or offline the dev_info nodes for an
+ * Hotplug Connection (CN).
+ */
+static int
+ddihp_change_node_state(dev_info_t *dip, void *arg)
+{
+ ddi_hp_cn_cfg_t *cn_cfg_p = (ddi_hp_cn_cfg_t *)arg;
+ int rv;
+
+ if (cn_cfg_p->online) {
+ /* It is online operation */
+ if (!ddihp_check_status_prop(dip))
+ return (DDI_WALK_PRUNECHILD);
+
+ rv = ndi_devi_online(dip, NDI_ONLINE_ATTACH | NDI_CONFIG);
+ } else {
+ /* It is offline operation */
+ (void) devfs_clean(ddi_get_parent(dip), NULL, DV_CLEAN_FORCE);
+ rv = ndi_devi_offline(dip, NDI_UNCONFIG);
+ }
+ if (rv != NDI_SUCCESS) {
+ DDI_HP_IMPLDBG((CE_CONT, "ddihp_change_devinfo_node_state:"
+ " failed op %x rv %d\n", cn_cfg_p->online, rv));
+ cn_cfg_p->rv = rv;
+
+ /* Failed to attach/detach the driver(s) */
+ return (DDI_WALK_PRUNECHILD);
+ }
+
+ /* Continue the walk */
+ return (DDI_WALK_CONTINUE);
+}
+
+/*
+ * Port operations
+ */
+
+/*
+ * Change Port state to target_state.
+ */
+static int
+ddihp_port_change_state(ddi_hp_cn_handle_t *hdlp,
+ ddi_hp_cn_state_t target_state)
+{
+ ddi_hp_cn_state_t curr_state = hdlp->cn_info.cn_state;
+
+ if (target_state < DDI_HP_CN_STATE_PORT_EMPTY ||
+ target_state > DDI_HP_CN_STATE_ONLINE) {
+
+ return (DDI_EINVAL);
+ }
+
+ if (curr_state < target_state)
+ return (ddihp_port_upgrade_state(hdlp, target_state));
+ else if (curr_state > target_state)
+ return (ddihp_port_downgrade_state(hdlp, target_state));
+ else
+ return (DDI_SUCCESS);
+}
+
+/*
+ * Upgrade port state to target_state.
+ */
+static int
+ddihp_port_upgrade_state(ddi_hp_cn_handle_t *hdlp,
+ ddi_hp_cn_state_t target_state)
+{
+ ddi_hp_cn_state_t curr_state, new_state, result_state;
+ dev_info_t *cdip;
+ int rv = DDI_SUCCESS;
+
+ curr_state = hdlp->cn_info.cn_state;
+ while (curr_state < target_state) {
+ switch (curr_state) {
+ case DDI_HP_CN_STATE_PORT_EMPTY:
+ /* Check the existence of the corresponding hardware */
+ new_state = DDI_HP_CN_STATE_PORT_PRESENT;
+ rv = ddihp_connector_ops(hdlp,
+ DDI_HPOP_CN_CHANGE_STATE,
+ (void *)&new_state, (void *)&result_state);
+ if (rv == DDI_SUCCESS) {
+ hdlp->cn_info.cn_state =
+ result_state;
+ }
+ break;
+ case DDI_HP_CN_STATE_PORT_PRESENT:
+ /* Read-only probe the corresponding hardware. */
+ new_state = DDI_HP_CN_STATE_OFFLINE;
+ rv = ddihp_connector_ops(hdlp,
+ DDI_HPOP_CN_CHANGE_STATE,
+ (void *)&new_state, &cdip);
+ if (rv == DDI_SUCCESS) {
+ hdlp->cn_info.cn_state =
+ DDI_HP_CN_STATE_OFFLINE;
+
+ ASSERT(hdlp->cn_info.cn_child == NULL);
+ hdlp->cn_info.cn_child = cdip;
+ }
+ break;
+ case DDI_HP_CN_STATE_OFFLINE:
+ /* fall through */
+ case DDI_HP_CN_STATE_MAINTENANCE:
+
+ cdip = hdlp->cn_info.cn_child;
+
+ rv = ndi_devi_online(cdip,
+ NDI_ONLINE_ATTACH | NDI_CONFIG);
+ if (rv == NDI_SUCCESS) {
+ hdlp->cn_info.cn_state =
+ DDI_HP_CN_STATE_ONLINE;
+ rv = DDI_SUCCESS;
+ } else {
+ rv = DDI_FAILURE;
+ DDI_HP_IMPLDBG((CE_CONT,
+ "ddihp_port_upgrade_state: "
+ "failed to online device %p at port: %s\n",
+ (void *)cdip, hdlp->cn_info.cn_name));
+ }
+ break;
+ case DDI_HP_CN_STATE_ONLINE:
+
+ break;
+ default:
+ /* should never reach here */
+ ASSERT("unknown devinfo state");
+ }
+ curr_state = hdlp->cn_info.cn_state;
+ if (rv != DDI_SUCCESS) {
+ DDI_HP_IMPLDBG((CE_CONT, "ddihp_port_upgrade_state: "
+ "failed curr_state=%x, target_state=%x \n",
+ curr_state, target_state));
+ return (rv);
+ }
+ }
+
+ return (rv);
+}
+
+/*
+ * Downgrade state to target_state
+ */
+static int
+ddihp_port_downgrade_state(ddi_hp_cn_handle_t *hdlp,
+ ddi_hp_cn_state_t target_state)
+{
+ ddi_hp_cn_state_t curr_state, new_state, result_state;
+ dev_info_t *dip = hdlp->cn_dip;
+ dev_info_t *cdip;
+ int rv = DDI_SUCCESS;
+
+ curr_state = hdlp->cn_info.cn_state;
+ while (curr_state > target_state) {
+
+ switch (curr_state) {
+ case DDI_HP_CN_STATE_PORT_EMPTY:
+
+ break;
+ case DDI_HP_CN_STATE_PORT_PRESENT:
+ /* Check the existence of the corresponding hardware */
+ new_state = DDI_HP_CN_STATE_PORT_EMPTY;
+ rv = ddihp_connector_ops(hdlp,
+ DDI_HPOP_CN_CHANGE_STATE,
+ (void *)&new_state, (void *)&result_state);
+ if (rv == DDI_SUCCESS)
+ hdlp->cn_info.cn_state =
+ result_state;
+
+ break;
+ case DDI_HP_CN_STATE_OFFLINE:
+ /*
+ * Read-only unprobe the corresponding hardware:
+ * 1. release the assigned resource;
+ * 2. remove the node pointed by the port's cn_child
+ */
+ new_state = DDI_HP_CN_STATE_PORT_PRESENT;
+ rv = ddihp_connector_ops(hdlp,
+ DDI_HPOP_CN_CHANGE_STATE,
+ (void *)&new_state, (void *)&result_state);
+ if (rv == DDI_SUCCESS)
+ hdlp->cn_info.cn_state =
+ DDI_HP_CN_STATE_PORT_PRESENT;
+ break;
+ case DDI_HP_CN_STATE_MAINTENANCE:
+ /* fall through. */
+ case DDI_HP_CN_STATE_ONLINE:
+ cdip = hdlp->cn_info.cn_child;
+
+ (void) devfs_clean(dip, NULL, DV_CLEAN_FORCE);
+ rv = ndi_devi_offline(cdip, NDI_UNCONFIG);
+ if (rv == NDI_SUCCESS) {
+ hdlp->cn_info.cn_state =
+ DDI_HP_CN_STATE_OFFLINE;
+ rv = DDI_SUCCESS;
+ } else {
+ rv = DDI_EBUSY;
+ DDI_HP_IMPLDBG((CE_CONT,
+ "ddihp_port_downgrade_state: failed "
+ "to offline node, rv=%x, cdip=%p \n",
+ rv, (void *)cdip));
+ }
+
+ break;
+ default:
+ /* should never reach here */
+ ASSERT("unknown devinfo state");
+ }
+ curr_state = hdlp->cn_info.cn_state;
+ if (rv != DDI_SUCCESS) {
+ DDI_HP_IMPLDBG((CE_CONT,
+ "ddihp_port_downgrade_state: failed "
+ "curr_state=%x, target_state=%x \n",
+ curr_state, target_state));
+ return (rv);
+ }
+ }
+
+ return (rv);
+}
+
+/*
+ * Misc routines
+ */
+
+/* Update the last state change time */
+static void
+ddihp_update_last_change(ddi_hp_cn_handle_t *hdlp)
+{
+ time_t time;
+
+ if (drv_getparm(TIME, (void *)&time) != DDI_SUCCESS)
+ hdlp->cn_info.cn_last_change = (time_t)-1;
+ else
+ hdlp->cn_info.cn_last_change = (time32_t)time;
+}
+
+/*
+ * Check the device for a 'status' property. A conforming device
+ * should have a status of "okay", "disabled", "fail", or "fail-xxx".
+ *
+ * Return FALSE for a conforming device that is disabled or faulted.
+ * Return TRUE in every other case.
+ *
+ * 'status' property is NOT a bus specific property. It is defined in page 184,
+ * IEEE 1275 spec. The full name of the spec is "IEEE Standard for
+ * Boot (Initialization Configuration) Firmware: Core Requirements and
+ * Practices".
+ */
+static boolean_t
+ddihp_check_status_prop(dev_info_t *dip)
+{
+ char *status_prop;
+ boolean_t rv = B_TRUE;
+
+ /* try to get the 'status' property */
+ if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
+ "status", &status_prop) == DDI_PROP_SUCCESS) {
+ /*
+ * test if the status is "disabled", "fail", or
+ * "fail-xxx".
+ */
+ if (strcmp(status_prop, "disabled") == 0) {
+ rv = B_FALSE;
+ DDI_HP_IMPLDBG((CE_CONT, "ddihp_check_status_prop "
+ "(%s%d): device is in disabled state",
+ ddi_driver_name(dip), ddi_get_instance(dip)));
+ } else if (strncmp(status_prop, "fail", 4) == 0) {
+ rv = B_FALSE;
+ cmn_err(CE_WARN,
+ "hotplug (%s%d): device is in fault state (%s)\n",
+ ddi_driver_name(dip), ddi_get_instance(dip),
+ status_prop);
+ }
+
+ ddi_prop_free(status_prop);
+ }
+
+ return (rv);
+}
diff --git a/usr/src/uts/common/os/ddi_hp_ndi.c b/usr/src/uts/common/os/ddi_hp_ndi.c
new file mode 100644
index 0000000000..d8cb0de3a2
--- /dev/null
+++ b/usr/src/uts/common/os/ddi_hp_ndi.c
@@ -0,0 +1,405 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Sun NDI hotplug interfaces
+ */
+
+#include <sys/note.h>
+#include <sys/sysmacros.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kmem.h>
+#include <sys/cmn_err.h>
+#include <sys/debug.h>
+#include <sys/avintr.h>
+#include <sys/autoconf.h>
+#include <sys/sunndi.h>
+#include <sys/ndi_impldefs.h>
+#include <sys/ddi.h>
+#include <sys/disp.h>
+#include <sys/stat.h>
+#include <sys/callb.h>
+#include <sys/sysevent.h>
+#include <sys/sysevent/eventdefs.h>
+#include <sys/sysevent/dr.h>
+#include <sys/taskq.h>
+
+/* Local functions prototype */
+static void ddihp_cn_run_event(void *arg);
+static int ddihp_cn_req_handler(ddi_hp_cn_handle_t *hdlp,
+ ddi_hp_cn_state_t target_state);
+
+/*
+ * Global functions (called by hotplug controller or nexus drivers)
+ */
+
+/*
+ * Register the Hotplug Connection (CN)
+ */
+int
+ndi_hp_register(dev_info_t *dip, ddi_hp_cn_info_t *info_p)
+{
+ ddi_hp_cn_handle_t *hdlp;
+ int count;
+
+ DDI_HP_NEXDBG((CE_CONT, "ndi_hp_register: dip %p, info_p %p\n",
+ (void *)dip, (void *)info_p));
+
+ ASSERT(!servicing_interrupt());
+ if (servicing_interrupt())
+ return (NDI_FAILURE);
+
+ /* Validate the arguments */
+ if ((dip == NULL) || (info_p == NULL))
+ return (NDI_EINVAL);
+
+ if (!NEXUS_HAS_HP_OP(dip)) {
+ return (NDI_ENOTSUP);
+ }
+ /* Lock before access */
+ ndi_devi_enter(dip, &count);
+
+ hdlp = ddihp_cn_name_to_handle(dip, info_p->cn_name);
+ if (hdlp) {
+ /* This cn_name is already registered. */
+ ndi_devi_exit(dip, count);
+
+ return (NDI_SUCCESS);
+ }
+ /*
+ * Create and initialize hotplug Connection handle
+ */
+ hdlp = (ddi_hp_cn_handle_t *)kmem_zalloc(
+ (sizeof (ddi_hp_cn_handle_t)), KM_SLEEP);
+
+ /* Copy the Connection information */
+ hdlp->cn_dip = dip;
+ bcopy(info_p, &(hdlp->cn_info), sizeof (*info_p));
+
+ /* Copy cn_name */
+ hdlp->cn_info.cn_name = ddi_strdup(info_p->cn_name, KM_SLEEP);
+
+ if (ddihp_cn_getstate(hdlp) != DDI_SUCCESS) {
+ DDI_HP_NEXDBG((CE_CONT, "ndi_hp_register: dip %p, hdlp %p"
+ "ddi_cn_getstate failed\n", (void *)dip, (void *)hdlp));
+
+ goto fail;
+ }
+
+ /*
+ * Append the handle to the list
+ */
+ DDIHP_LIST_APPEND(ddi_hp_cn_handle_t, (DEVI(dip)->devi_hp_hdlp),
+ hdlp);
+
+ ndi_devi_exit(dip, count);
+
+ return (NDI_SUCCESS);
+
+fail:
+ kmem_free(hdlp->cn_info.cn_name, strlen(hdlp->cn_info.cn_name) + 1);
+ kmem_free(hdlp, sizeof (ddi_hp_cn_handle_t));
+ ndi_devi_exit(dip, count);
+
+ return (NDI_FAILURE);
+}
+
+/*
+ * Unregister a Hotplug Connection (CN)
+ */
+int
+ndi_hp_unregister(dev_info_t *dip, char *cn_name)
+{
+ ddi_hp_cn_handle_t *hdlp;
+ int count;
+ int ret;
+
+ DDI_HP_NEXDBG((CE_CONT, "ndi_hp_unregister: dip %p, cn name %s\n",
+ (void *)dip, cn_name));
+
+ ASSERT(!servicing_interrupt());
+ if (servicing_interrupt())
+ return (NDI_FAILURE);
+
+ /* Validate the arguments */
+ if ((dip == NULL) || (cn_name == NULL))
+ return (NDI_EINVAL);
+
+ ndi_devi_enter(dip, &count);
+
+ hdlp = ddihp_cn_name_to_handle(dip, cn_name);
+ if (hdlp == NULL) {
+ ndi_devi_exit(dip, count);
+ return (NDI_EINVAL);
+ }
+
+ switch (ddihp_cn_unregister(hdlp)) {
+ case DDI_SUCCESS:
+ ret = NDI_SUCCESS;
+ break;
+ case DDI_EINVAL:
+ ret = NDI_EINVAL;
+ break;
+ case DDI_EBUSY:
+ ret = NDI_BUSY;
+ break;
+ default:
+ ret = NDI_FAILURE;
+ break;
+ }
+
+ ndi_devi_exit(dip, count);
+
+ return (ret);
+}
+
+/*
+ * Notify the Hotplug Connection (CN) to change state.
+ * Flag:
+ * DDI_HP_REQ_SYNC Return after the change is finished.
+ * DDI_HP_REQ_ASYNC Return after the request is dispatched.
+ */
+int
+ndi_hp_state_change_req(dev_info_t *dip, char *cn_name,
+ ddi_hp_cn_state_t state, uint_t flag)
+{
+ ddi_hp_cn_async_event_entry_t *eventp;
+
+ DDI_HP_NEXDBG((CE_CONT, "ndi_hp_state_change_req: dip %p "
+ "cn_name: %s, state %x, flag %x\n",
+ (void *)dip, cn_name, state, flag));
+
+ /* Validate the arguments */
+ if (dip == NULL || cn_name == NULL)
+ return (NDI_EINVAL);
+
+ if (!NEXUS_HAS_HP_OP(dip)) {
+ return (NDI_ENOTSUP);
+ }
+ /*
+ * If the request is to handle the event synchronously, then call
+ * the event handler without queuing the event.
+ */
+ if (flag & DDI_HP_REQ_SYNC) {
+ ddi_hp_cn_handle_t *hdlp;
+ int count;
+ int ret;
+
+ ASSERT(!servicing_interrupt());
+ if (servicing_interrupt())
+ return (NDI_FAILURE);
+
+ ndi_devi_enter(dip, &count);
+
+ hdlp = ddihp_cn_name_to_handle(dip, cn_name);
+ if (hdlp == NULL) {
+ ndi_devi_exit(dip, count);
+
+ return (NDI_EINVAL);
+ }
+
+ DDI_HP_NEXDBG((CE_CONT, "ndi_hp_state_change_req: hdlp %p "
+ "calling ddihp_cn_req_handler() directly to handle "
+ "target_state %x\n", (void *)hdlp, state));
+
+ ret = ddihp_cn_req_handler(hdlp, state);
+
+ ndi_devi_exit(dip, count);
+
+ return (ret);
+ }
+
+ eventp = kmem_zalloc(sizeof (ddi_hp_cn_async_event_entry_t),
+ KM_NOSLEEP);
+ if (eventp == NULL)
+ return (NDI_NOMEM);
+
+ eventp->cn_name = ddi_strdup(cn_name, KM_NOSLEEP);
+ if (eventp->cn_name == NULL) {
+ kmem_free(eventp, sizeof (ddi_hp_cn_async_event_entry_t));
+ return (NDI_NOMEM);
+ }
+ eventp->dip = dip;
+ eventp->target_state = state;
+
+ /*
+ * Hold the parent's ref so that it won't disappear when the taskq is
+ * scheduled to run.
+ */
+ ndi_hold_devi(dip);
+
+ if (!taskq_dispatch(system_taskq, ddihp_cn_run_event, eventp,
+ TQ_NOSLEEP)) {
+ ndi_rele_devi(dip);
+ DDI_HP_NEXDBG((CE_CONT, "ndi_hp_state_change_req: "
+ "taskq_dispatch failed! dip %p "
+ "target_state %x\n", (void *)dip, state));
+ return (NDI_NOMEM);
+ }
+
+ return (NDI_CLAIMED);
+}
+
+/*
+ * Walk the link of Hotplug Connection handles of a dip:
+ * DEVI(dip)->devi_hp_hdlp->[link of connections]
+ */
+void
+ndi_hp_walk_cn(dev_info_t *dip, int (*f)(ddi_hp_cn_info_t *,
+ void *), void *arg)
+{
+ int count;
+ ddi_hp_cn_handle_t *head, *curr, *prev;
+
+ DDI_HP_NEXDBG((CE_CONT, "ndi_hp_walk_cn: dip %p arg %p\n",
+ (void *)dip, arg));
+
+ ASSERT(!servicing_interrupt());
+ if (servicing_interrupt())
+ return;
+
+ /* Validate the arguments */
+ if (dip == NULL)
+ return;
+
+ ndi_devi_enter(dip, &count);
+
+ head = DEVI(dip)->devi_hp_hdlp;
+ curr = head;
+ prev = NULL;
+ while (curr != NULL) {
+ DDI_HP_NEXDBG((CE_CONT, "ndi_hp_walk_cn: dip %p "
+ "current cn_name: %s\n",
+ (void *)dip, curr->cn_info.cn_name));
+ switch ((*f)(&(curr->cn_info), arg)) {
+ case DDI_WALK_TERMINATE:
+ ndi_devi_exit(dip, count);
+
+ return;
+ case DDI_WALK_CONTINUE:
+ default:
+ if (DEVI(dip)->devi_hp_hdlp != head) {
+ /*
+ * The current node is head and it is removed
+ * by last call to (*f)()
+ */
+ head = DEVI(dip)->devi_hp_hdlp;
+ curr = head;
+ prev = NULL;
+ } else if (prev && prev->next != curr) {
+ /*
+ * The current node is a middle node or tail
+ * node and it is removed by last call to
+ * (*f)()
+ */
+ curr = prev->next;
+ } else {
+ /* no removal accurred on curr node */
+ prev = curr;
+ curr = curr->next;
+ }
+ }
+ }
+ ndi_devi_exit(dip, count);
+}
+
+/*
+ * Local functions (called within this file)
+ */
+
+/*
+ * Wrapper function for ddihp_cn_req_handler() called from taskq
+ */
+static void
+ddihp_cn_run_event(void *arg)
+{
+ ddi_hp_cn_async_event_entry_t *eventp =
+ (ddi_hp_cn_async_event_entry_t *)arg;
+ dev_info_t *dip = eventp->dip;
+ ddi_hp_cn_handle_t *hdlp;
+ int count;
+
+ /* Lock before access */
+ ndi_devi_enter(dip, &count);
+
+ hdlp = ddihp_cn_name_to_handle(dip, eventp->cn_name);
+ if (hdlp) {
+ (void) ddihp_cn_req_handler(hdlp, eventp->target_state);
+ } else {
+ DDI_HP_NEXDBG((CE_CONT, "ddihp_cn_run_event: no handle for "
+ "cn_name: %s dip %p. Request for target_state %x is"
+ " dropped. \n",
+ eventp->cn_name, (void *)dip, eventp->target_state));
+ }
+
+ ndi_devi_exit(dip, count);
+
+ /* Release the devi's ref that is held from interrupt context. */
+ ndi_rele_devi((dev_info_t *)DEVI(dip));
+ kmem_free(eventp->cn_name, strlen(eventp->cn_name) + 1);
+ kmem_free(eventp, sizeof (ddi_hp_cn_async_event_entry_t));
+}
+
+/*
+ * Handle state change request of a Hotplug Connection (CN)
+ */
+static int
+ddihp_cn_req_handler(ddi_hp_cn_handle_t *hdlp,
+ ddi_hp_cn_state_t target_state)
+{
+ dev_info_t *dip = hdlp->cn_dip;
+ int ret = DDI_SUCCESS;
+
+ DDI_HP_NEXDBG((CE_CONT, "ddihp_cn_req_handler:"
+ " hdlp %p, target_state %x\n",
+ (void *)hdlp, target_state));
+
+ ASSERT(DEVI_BUSY_OWNED(dip));
+
+ if (ddihp_cn_getstate(hdlp) != DDI_SUCCESS) {
+ DDI_HP_NEXDBG((CE_CONT, "ddihp_cn_req_handler: dip %p, "
+ "hdlp %p ddi_cn_getstate failed\n", (void *)dip,
+ (void *)hdlp));
+
+ return (NDI_UNCLAIMED);
+ }
+ if (hdlp->cn_info.cn_state != target_state) {
+ ddi_hp_cn_state_t result_state = 0;
+
+ DDIHP_CN_OPS(hdlp, DDI_HPOP_CN_CHANGE_STATE,
+ (void *)&target_state, (void *)&result_state, ret);
+
+ DDI_HP_NEXDBG((CE_CONT, "ddihp_cn_req_handler: dip %p, "
+ "hdlp %p changed state to %x, ret=%x\n",
+ (void *)dip, (void *)hdlp, result_state, ret));
+ }
+
+ if (ret == DDI_SUCCESS)
+ return (NDI_CLAIMED);
+ else
+ return (NDI_UNCLAIMED);
+}
diff --git a/usr/src/uts/common/os/devcfg.c b/usr/src/uts/common/os/devcfg.c
index 871b1c4c91..e36f63bd4c 100644
--- a/usr/src/uts/common/os/devcfg.c
+++ b/usr/src/uts/common/os/devcfg.c
@@ -407,6 +407,7 @@ i_ddi_free_node(dev_info_t *dip)
ASSERT(devi->devi_addr == NULL);
ASSERT(devi->devi_node_state == DS_PROTO);
ASSERT(devi->devi_child == NULL);
+ ASSERT(devi->devi_hp_hdlp == NULL);
#if defined(__x86) && !defined(__xpv)
for (gfxp = gfx_devinfo_list; gfxp; gfxp = gfxp->g_next) {
@@ -652,7 +653,7 @@ link_node(dev_info_t *dip)
* This is a temporary workaround for Bug 4618861.
* We keep the scsi_vhci nexus node on the left side of the devinfo
* tree (under the root nexus driver), so that virtual nodes under
- * scsi_vhci will be SUSPENDed first and RESUMEd last. This ensures
+ * scsi_vhci will be SUSPENDed first and RESUMEd last. This ensures
* that the pHCI nodes are active during times when their clients
* may be depending on them. This workaround embodies the knowledge
* that system PM and CPR both traverse the tree left-to-right during
@@ -695,6 +696,7 @@ unlink_node(dev_info_t *dip)
struct dev_info *devi = DEVI(dip);
struct dev_info *parent = devi->devi_parent;
dev_info_t **dipp;
+ ddi_hp_cn_handle_t *hdlp;
ASSERT(parent != NULL);
ASSERT(devi->devi_node_state == DS_LINKED);
@@ -737,6 +739,11 @@ unlink_node(dev_info_t *dip)
remove_from_dn_list(&orphanlist, dip);
}
+ /* Update parent's hotplug handle list */
+ for (hdlp = DEVI(parent)->devi_hp_hdlp; hdlp; hdlp = hdlp->next) {
+ if (hdlp->cn_info.cn_child == dip)
+ hdlp->cn_info.cn_child = NULL;
+ }
return (DDI_SUCCESS);
}
@@ -2453,7 +2460,7 @@ i_ddi_get_exported_classes(dev_info_t *dip, char ***classes)
n += get_class(ddi_driver_name(dip), buf);
unlock_hw_class_list();
- ASSERT(n == nclass); /* make sure buf wasn't overrun */
+ ASSERT(n == nclass); /* make sure buf wasn't overrun */
return (nclass);
}
@@ -3094,7 +3101,7 @@ debug_dtree(dev_info_t *devi, struct dev_info *adevi, char *service)
}
#else /* DEBUG */
#define debug_dtree(a1, a2, a3) /* nothing */
-#endif /* DEBUG */
+#endif /* DEBUG */
static void
ddi_optimize_dtree(dev_info_t *devi)
@@ -3288,7 +3295,7 @@ i_ddi_forceattach_drivers()
* when uhci/ohci reset themselves, it induces a port change on
* the ehci companion controller. Since there's no interrupt handler
* installed at the time, the moment that interrupt is unmasked, an
- * interrupt storm will occur. All this is averted when ehci is
+ * interrupt storm will occur. All this is averted when ehci is
* loaded first. And now you know..... the REST of the story.
*
* Regardless of platform, ehci needs to initialize first to avoid
@@ -4226,7 +4233,7 @@ reset_leaves(void)
* outstanding attach or detach operations in progress when quiesce_devices() or
* reset_leaves()is invoked. It must be called before the system becomes
* single-threaded because device attach and detach are multi-threaded
- * operations. (note that during system shutdown the system doesn't actually
+ * operations. (note that during system shutdown the system doesn't actually
* become single-thread since other threads still exist, but the shutdown thread
* will disable preemption for itself, raise it's pil, and stop all the other
* cpus in the system there by effectively making the system single-threaded.)
@@ -4420,7 +4427,7 @@ unbind_children_by_driver(dev_info_t *dip, void *arg)
* We are called either from rem_drv or update_drv when reloading
* a driver.conf file. In either case, we unbind persistent nodes
* and destroy .conf nodes. In the case of rem_drv, this will be
- * the final state. In the case of update_drv, i_ddi_bind_devs()
+ * the final state. In the case of update_drv, i_ddi_bind_devs()
* may be invoked later to re-enumerate (new) driver.conf rebind
* persistent nodes.
*/
@@ -5371,7 +5378,7 @@ devi_config_one(dev_info_t *pdip, char *devnm, dev_info_t **cdipp,
* We may have a genericname on a system that creates drivername
* nodes (from .conf files). Find the drivername by nodeid. If we
* can't find a node with devnm as the node name then we search by
- * drivername. This allows an implementation to supply a genericly
+ * drivername. This allows an implementation to supply a genericly
* named boot path (disk) and locate drivename nodes (sd). The
* NDI_PROMNAME flag does not apply to /devices/pseudo paths.
*/
@@ -8480,7 +8487,7 @@ e_ddi_unretire_device(char *path)
static int
mark_and_fence(dev_info_t *dip, void *arg)
{
- char *fencepath = (char *)arg;
+ char *fencepath = (char *)arg;
/*
* We have already decided to retire this device. The various
@@ -8541,7 +8548,7 @@ i_ddi_check_retire(dev_info_t *dip)
(void *)dip, path));
/*
- * Check if this device is in the "retired" store i.e. should
+ * Check if this device is in the "retired" store i.e. should
* be retired. If not, we have nothing to do.
*/
if (e_ddi_device_retired(path) == 0) {
diff --git a/usr/src/uts/common/os/modctl.c b/usr/src/uts/common/os/modctl.c
index 8ec4bc3f7d..1ddec74d87 100644
--- a/usr/src/uts/common/os/modctl.c
+++ b/usr/src/uts/common/os/modctl.c
@@ -174,7 +174,7 @@ mod_setup(void)
num_devs = read_binding_file(majbind, mb_hashtab, make_mbind);
/*
* Since read_binding_file is common code, it doesn't enforce that all
- * of the binding file entries have major numbers <= MAXMAJ32. Thus,
+ * of the binding file entries have major numbers <= MAXMAJ32. Thus,
* ensure that we don't allocate some massive amount of space due to a
* bad entry. We can't have major numbers bigger than MAXMAJ32
* until file system support for larger major numbers exists.
@@ -1232,7 +1232,7 @@ modctl_devid2paths(ddi_devid_t udevid, char *uminor_name, uint_t flag,
char *minor_name = NULL;
dev_info_t *dip = NULL;
int circ;
- struct ddi_minor_data *dmdp;
+ struct ddi_minor_data *dmdp;
char *path = NULL;
int ulens;
int lens;
@@ -1998,7 +1998,7 @@ modctl_remdrv_cleanup(const char *u_drvname)
* instance of a device bound to the driver being
* removed, remove any underlying devfs attribute nodes.
*
- * This is a two-step process. First we go through
+ * This is a two-step process. First we go through
* the instance data itself, constructing a list of
* the nodes discovered. The second step is then
* to find and remove any devfs attribute nodes
@@ -2261,6 +2261,67 @@ err:
return (ret);
}
+static int
+modctl_hp(int subcmd, const char *path, char *cn_name, uintptr_t arg,
+ uintptr_t rval)
+{
+ int error = 0;
+ size_t pathsz, namesz;
+ char *devpath, *cn_name_str;
+
+ if (path == NULL)
+ return (EINVAL);
+
+ devpath = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
+ error = copyinstr(path, devpath, MAXPATHLEN, &pathsz);
+ if (error != 0) {
+ kmem_free(devpath, MAXPATHLEN);
+ return (EFAULT);
+ }
+
+ cn_name_str = kmem_zalloc(MAXNAMELEN, KM_SLEEP);
+ error = copyinstr(cn_name, cn_name_str, MAXNAMELEN, &namesz);
+ if (error != 0) {
+ kmem_free(devpath, MAXPATHLEN);
+ kmem_free(cn_name_str, MAXNAMELEN);
+
+ return (EFAULT);
+ }
+
+ switch (subcmd) {
+ case MODHPOPS_CHANGE_STATE:
+ error = ddihp_modctl(DDI_HPOP_CN_CHANGE_STATE, devpath,
+ cn_name_str, arg, NULL);
+ break;
+ case MODHPOPS_CREATE_PORT:
+ /* Create an empty PORT */
+ error = ddihp_modctl(DDI_HPOP_CN_CREATE_PORT, devpath,
+ cn_name_str, NULL, NULL);
+ break;
+ case MODHPOPS_REMOVE_PORT:
+ /* Remove an empty PORT */
+ error = ddihp_modctl(DDI_HPOP_CN_REMOVE_PORT, devpath,
+ cn_name_str, NULL, NULL);
+ break;
+ case MODHPOPS_BUS_GET:
+ error = ddihp_modctl(DDI_HPOP_CN_GET_PROPERTY, devpath,
+ cn_name_str, arg, rval);
+ break;
+ case MODHPOPS_BUS_SET:
+ error = ddihp_modctl(DDI_HPOP_CN_SET_PROPERTY, devpath,
+ cn_name_str, arg, rval);
+ break;
+ default:
+ error = ENOTSUP;
+ break;
+ }
+
+ kmem_free(devpath, MAXPATHLEN);
+ kmem_free(cn_name_str, MAXNAMELEN);
+
+ return (error);
+}
+
int
modctl_moddevname(int subcmd, uintptr_t a1, uintptr_t a2)
{
@@ -2421,7 +2482,7 @@ modctl(int cmd, uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4,
#endif
break;
- case MODGETDEVFSPATH: /* get path name of (dev_t,spec) type */
+ case MODGETDEVFSPATH: /* get path name of (dev_t,spec) type */
if (get_udatamodel() == DATAMODEL_NATIVE) {
error = modctl_devfspath((dev_t)a1, (int)a2,
(uint_t)a3, (char *)a4);
@@ -2439,7 +2500,7 @@ modctl(int cmd, uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4,
(uint_t *)a3);
break;
- case MODGETDEVFSPATH_MI: /* get path name of (major,instance) */
+ case MODGETDEVFSPATH_MI: /* get path name of (major,instance) */
error = modctl_devfspath_mi((major_t)a1, (int)a2,
(uint_t)a3, (char *)a4);
break;
@@ -2536,6 +2597,11 @@ modctl(int cmd, uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4,
error = modctl_unretire((char *)a1);
break;
+ case MODHPOPS: /* hotplug operations */
+ /* device named by physpath a2 and Connection name a3 */
+ error = modctl_hp((int)a1, (char *)a2, (char *)a3, a4, a5);
+ break;
+
default:
error = EINVAL;
break;
@@ -3235,7 +3301,7 @@ modgetsymname(uintptr_t value, ulong_t *offset)
/*
* Lookup a symbol in a specified module. These are wrapper routines that
- * call kobj_lookup(). kobj_lookup() may go away but these wrappers will
+ * call kobj_lookup(). kobj_lookup() may go away but these wrappers will
* prevent callers from noticing.
*/
uintptr_t
@@ -4171,7 +4237,7 @@ mod_make_requisite(struct modctl *dependent, struct modctl *on_mod)
* which are dependent on it from being uninstalled and
* unloaded. "on_mod"'s mod_ref count decremented in
* mod_release_requisites when the "dependent" module
- * unload is complete. "on_mod" must be loaded, but may not
+ * unload is complete. "on_mod" must be loaded, but may not
* yet be installed.
*/
on_mod->mod_ref++;
diff --git a/usr/src/uts/common/sys/Makefile b/usr/src/uts/common/sys/Makefile
index 83a852b35b..c8be2f37c4 100644
--- a/usr/src/uts/common/sys/Makefile
+++ b/usr/src/uts/common/sys/Makefile
@@ -148,6 +148,8 @@ CHKHDRS= \
ddi.h \
ddifm.h \
ddifm_impl.h \
+ ddi_hp.h \
+ ddi_hp_impl.h \
ddi_intr.h \
ddi_intr_impl.h \
ddi_impldefs.h \
diff --git a/usr/src/uts/common/sys/autoconf.h b/usr/src/uts/common/sys/autoconf.h
index 0f83c24f0b..acf41f6155 100644
--- a/usr/src/uts/common/sys/autoconf.h
+++ b/usr/src/uts/common/sys/autoconf.h
@@ -26,7 +26,6 @@
#ifndef _SYS_AUTOCONF_H
#define _SYS_AUTOCONF_H
-
/* Derived from autoconf.h, SunOS 4.1.1 1.15 */
#ifdef __cplusplus
@@ -113,6 +112,9 @@ struct devnames {
#define LDI_EV_DEBUG 0x8000 /* LDI events debug messages */
#define LDI_EV_TRACE 0x10000 /* LDI events trace messages */
#define DDI_INTR_IRM 0x20000 /* interrupt resource management */
+#define DDI_HP_API 0x40000 /* Hotplug interface messages */
+#define DDI_HP_IMPL 0x80000 /* Hotplug implementation msgs */
+#define DDI_HP_NEXUS 0x100000 /* Hotplug messages from nexuses */
extern int ddidebug;
@@ -133,6 +135,9 @@ extern int ddidebug;
#define LDI_EVDBG(args) if (ddidebug & LDI_EV_DEBUG) cmn_err args
#define LDI_EVTRC(args) if (ddidebug & LDI_EV_TRACE) cmn_err args
#define DDI_INTR_IRMDBG(args) if (ddidebug & DDI_INTR_IRM) cmn_err args
+#define DDI_HP_APIDBG(args) if (ddidebug & DDI_HP_API) cmn_err args
+#define DDI_HP_IMPLDBG(args) if (ddidebug & DDI_HP_IMPL) cmn_err args
+#define DDI_HP_NEXDBG(args) if (ddidebug & DDI_HP_NEXUS) cmn_err args
#else
#define NDI_CONFIG_DEBUG(args)
#define BMDPRINTF(args)
@@ -150,6 +155,9 @@ extern int ddidebug;
#define LDI_EVDBG(args) if (ddidebug & LDI_EV_DEBUG) cmn_err args
#define LDI_EVTRC(args) if (ddidebug & LDI_EV_TRACE) cmn_err args
#define DDI_INTR_IRMDBG(args)
+#define DDI_HP_APIDBG(args)
+#define DDI_HP_IMPLDBG(args)
+#define DDI_HP_NEXDBG(args)
#endif
diff --git a/usr/src/uts/common/sys/ddi_hp.h b/usr/src/uts/common/sys/ddi_hp.h
new file mode 100644
index 0000000000..eadb88ed49
--- /dev/null
+++ b/usr/src/uts/common/sys/ddi_hp.h
@@ -0,0 +1,126 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _SYS_DDI_HP_H
+#define _SYS_DDI_HP_H
+
+/*
+ * Sun DDI hotplug support definitions
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * ddi_hp_cn_state_t
+ *
+ * Typedef of generic hotplug state machine for Hotplug Connection (CN)
+ */
+typedef enum {
+ DDI_HP_CN_STATE_EMPTY = 0x1000, /* Empty */
+ DDI_HP_CN_STATE_PRESENT = 0x2000, /* A Device Present */
+ DDI_HP_CN_STATE_POWERED = 0x3000, /* Powered */
+ DDI_HP_CN_STATE_ENABLED = 0x4000, /* Enabled */
+ DDI_HP_CN_STATE_PORT_EMPTY = 0x5000, /* PORT Empty */
+ DDI_HP_CN_STATE_PORT_PRESENT = 0x6000, /* A Device Node Present */
+ DDI_HP_CN_STATE_OFFLINE = 0x7000, /* Driver not attached */
+ DDI_HP_CN_STATE_ATTACHED = 0x8000, /* Device driver attached */
+ DDI_HP_CN_STATE_MAINTENANCE = 0x9000, /* Device in maintenance */
+ DDI_HP_CN_STATE_ONLINE = 0xa000 /* Device is ready */
+} ddi_hp_cn_state_t;
+
+/*
+ * ddi_hp_cn_type_t
+ *
+ * Typedef for Hotplug Connection (CN) types.
+ */
+typedef enum {
+ DDI_HP_CN_TYPE_VIRTUAL_PORT = 0x1, /* Virtual Hotplug Port */
+ DDI_HP_CN_TYPE_PCI = 0x2, /* PCI bus slot */
+ DDI_HP_CN_TYPE_PCIE = 0x3 /* PCI Express slot */
+} ddi_hp_cn_type_t;
+
+#define DDI_HP_CN_TYPE_STR_PORT "Virtual-Port"
+/*
+ * The value set to ddi_hp_cn_info_t->cn_num_dpd_on in the case of the
+ * connection does not depend on any other connections.
+ */
+#define DDI_HP_CN_NUM_NONE -1
+
+/*
+ * ddi_hp_cn_info_t
+ *
+ * Hotplug Connection (CN) information structure
+ */
+typedef struct ddi_hp_cn_info {
+ char *cn_name; /* Name of the Connection */
+ /*
+ * Connection number.
+ */
+ int cn_num;
+ /*
+ * Depend-on connection number;
+ * The connection number on which this connection is depending on.
+ * If this connection does not depend on any other connections
+ * under the same parent node, then it's cn_num_dpd_on is set to
+ * DDI_HP_CN_NUM_NONE.
+ */
+ int cn_num_dpd_on;
+
+ ddi_hp_cn_type_t cn_type; /* Type: Port, PCI, PCIE, ... */
+
+ /*
+ * Description string for types of Connection. Set by bus software
+ * and read by users only.
+ */
+ char *cn_type_str;
+ /*
+ * The child device of this Port.
+ * It is NULL if this is a Connector.
+ */
+ dev_info_t *cn_child;
+
+ ddi_hp_cn_state_t cn_state; /* Hotplug Connection state */
+ time32_t cn_last_change; /* Last time state changed. */
+} ddi_hp_cn_info_t;
+
+typedef struct ddi_hp_property {
+ char *nvlist_buf;
+ size_t buf_size;
+} ddi_hp_property_t;
+
+#if defined(_SYSCALL32)
+typedef struct ddi_hp_property32 {
+ caddr32_t nvlist_buf;
+ uint32_t buf_size;
+} ddi_hp_property32_t;
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_DDI_HP_H */
diff --git a/usr/src/uts/common/sys/ddi_hp_impl.h b/usr/src/uts/common/sys/ddi_hp_impl.h
new file mode 100644
index 0000000000..9b5a193313
--- /dev/null
+++ b/usr/src/uts/common/sys/ddi_hp_impl.h
@@ -0,0 +1,160 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _SYS_DDI_HP_IMPL_H
+#define _SYS_DDI_HP_IMPL_H
+
+/*
+ * Sun DDI hotplug implementation specific definitions
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef _KERNEL
+
+/* Flags for sync request and async hotplug request */
+#define DDI_HP_REQ_SYNC 0x0001
+#define DDI_HP_REQ_ASYNC 0x0002
+
+/* Check if a handle represents a port or a connector */
+#define DDI_HP_IS_VIRTUAL_PORT(hdlp) \
+ (hdlp->cn_info.cn_type == DDI_HP_CN_TYPE_VIRTUAL_PORT)
+
+/*
+ * ddi_hp_cn_handle_t
+ *
+ * DDI handle for a registered Hotplug Connection (CN)
+ */
+typedef struct ddi_hp_cn_handle {
+ dev_info_t *cn_dip; /* The dip that the handle is linked */
+ ddi_hp_cn_info_t cn_info; /* Connection info */
+ struct ddi_hp_cn_handle *next; /* Next Connector/Port. */
+} ddi_hp_cn_handle_t;
+
+typedef struct ddi_hp_cn_async_event_entry {
+ dev_info_t *dip;
+ char *cn_name;
+ ddi_hp_cn_state_t target_state;
+} ddi_hp_cn_async_event_entry_t;
+
+/*
+ * ddi_hp_op_t
+ *
+ * Typedef for Hotplug OPS commands used with bus_hp_op()
+ */
+typedef enum {
+ DDI_HPOP_CN_GET_STATE = 1, /* Get Connection state */
+ DDI_HPOP_CN_CHANGE_STATE, /* Change Connection state */
+ DDI_HPOP_CN_PROBE, /* Probe Connection */
+ DDI_HPOP_CN_UNPROBE, /* Unprobe Connection */
+ DDI_HPOP_CN_GET_PROPERTY, /* Get bus specific property */
+ DDI_HPOP_CN_SET_PROPERTY, /* Set bus specific property */
+ DDI_HPOP_CN_CREATE_PORT, /* Create a port for virtual hotplug */
+ DDI_HPOP_CN_REMOVE_PORT /* Remove an empty port */
+} ddi_hp_op_t;
+
+#define DDIHP_CN_OPS(hdlp, op, arg, result, ret) \
+ if (DDI_HP_IS_VIRTUAL_PORT(hdlp)) \
+ ret = ddihp_port_ops(hdlp, op, arg, result); \
+ else \
+ ret = ddihp_connector_ops(hdlp, op, arg, result);
+
+#define NEXUS_HAS_HP_OP(dip) \
+ ((DEVI(dip)->devi_ops->devo_bus_ops) && \
+ (DEVI(dip)->devi_ops->devo_bus_ops->busops_rev >= BUSO_REV_10) && \
+ (DEVI(dip)->devi_ops->devo_bus_ops->bus_hp_op))
+
+/*
+ * ddi_hp_cn_sysevent_t
+ *
+ * The following correspond to sysevent defined subclasses
+ */
+typedef enum {
+ DDI_HP_CN_STATE_CHANGE,
+ DDI_HP_CN_REQ
+} ddi_hp_cn_sysevent_t;
+
+/*
+ * Control structure for tree walk during configure/unconfigure operation.
+ */
+typedef struct ddi_hp_cn_cfg {
+ boolean_t online; /* TRUE for online children; */
+ /* FALSE for offline children. */
+ int rv; /* Return error code */
+} ddi_hp_cn_cfg_t;
+
+/*
+ * Misc
+ */
+
+/* Append a node to list */
+#define DDIHP_LIST_APPEND(type, head, node) \
+if (node) { \
+ type *curr, *prev = NULL; \
+ (node)->next = NULL; \
+ for (curr = (head); curr; prev = curr, curr = curr->next); \
+ if (prev == NULL) \
+ (head) = (node); \
+ else \
+ prev->next = (node); \
+}
+
+/* Remove a node from a list */
+#define DDIHP_LIST_REMOVE(type, head, node) \
+if (node) { \
+ type *curr, *prev = NULL; \
+ for (curr = (head); curr; prev = curr, curr = curr->next) { \
+ if (curr == (node)) \
+ break; \
+ } \
+ if (curr) { \
+ if (prev == NULL) \
+ (head) = (head)->next; \
+ else \
+ prev->next = curr->next; \
+ } \
+}
+
+int ddihp_modctl(int hp_op, char *path, char *cn_name, uintptr_t arg,
+ uintptr_t rval);
+ddi_hp_cn_handle_t *ddihp_cn_name_to_handle(dev_info_t *dip, char *cn_name);
+int ddihp_cn_getstate(ddi_hp_cn_handle_t *hdlp);
+int ddihp_port_ops(ddi_hp_cn_handle_t *hdlp, ddi_hp_op_t op,
+ void *arg, void *result);
+int ddihp_connector_ops(ddi_hp_cn_handle_t *hdlp,
+ ddi_hp_op_t op, void *arg, void *result);
+void ddihp_cn_gen_sysevent(ddi_hp_cn_handle_t *hdlp,
+ ddi_hp_cn_sysevent_t event_sub_class, int hint, int kmflag);
+int ddihp_cn_unregister(ddi_hp_cn_handle_t *hdlp);
+
+#endif /* _KERNEL */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_DDI_HP_IMPL_H */
diff --git a/usr/src/uts/common/sys/ddi_impldefs.h b/usr/src/uts/common/sys/ddi_impldefs.h
index 5b5d103e08..388ecde590 100644
--- a/usr/src/uts/common/sys/ddi_impldefs.h
+++ b/usr/src/uts/common/sys/ddi_impldefs.h
@@ -39,6 +39,8 @@
#include <sys/epm.h>
#include <sys/ddidmareq.h>
#include <sys/ddi_intr.h>
+#include <sys/ddi_hp.h>
+#include <sys/ddi_hp_impl.h>
#include <sys/ddi_isa.h>
#include <sys/id_space.h>
#include <sys/modhash.h>
@@ -69,12 +71,12 @@ typedef enum {
* Definitions for generic callback mechanism.
*/
typedef enum {
- DDI_CB_INTR_ADD,
- DDI_CB_INTR_REMOVE
+ DDI_CB_INTR_ADD, /* More available interrupts */
+ DDI_CB_INTR_REMOVE /* Fewer available interrupts */
} ddi_cb_action_t;
typedef enum {
- DDI_CB_FLAG_INTR = 0x1
+ DDI_CB_FLAG_INTR = 0x1 /* Driver is IRM aware */
} ddi_cb_flags_t;
#define DDI_CB_FLAG_VALID(f) ((f) & DDI_CB_FLAG_INTR)
@@ -119,6 +121,7 @@ typedef struct devi_bus_priv {
struct iommulib_unit;
typedef struct iommulib_unit *iommulib_handle_t;
typedef uint8_t ndi_flavor_t;
+struct ddi_hp_cn_handle;
struct dev_info {
@@ -231,8 +234,8 @@ struct dev_info {
uint_t devi_cpr_flags;
- /* For interrupt support */
- devinfo_intr_t *devi_intr_p;
+ /* Owned by DDI interrupt framework */
+ devinfo_intr_t *devi_intr_p;
void *devi_nex_pm; /* nexus PM private */
@@ -267,6 +270,9 @@ struct dev_info {
ndi_flavor_t devi_flavor; /* flavor assigned by parent */
ndi_flavor_t devi_flavorv_n; /* number of child-flavors */
void **devi_flavorv; /* child-flavor specific data */
+
+ /* Owned by hotplug framework */
+ struct ddi_hp_cn_handle *devi_hp_hdlp; /* hotplug handle list */
};
#define DEVI(dev_info_type) ((struct dev_info *)(dev_info_type))
diff --git a/usr/src/uts/common/sys/devinfo_impl.h b/usr/src/uts/common/sys/devinfo_impl.h
index 4f874ea1e3..411ec3f5e2 100644
--- a/usr/src/uts/common/sys/devinfo_impl.h
+++ b/usr/src/uts/common/sys/devinfo_impl.h
@@ -64,6 +64,9 @@ extern "C" {
/* new public flag for the layered drivers framework */
#define DINFOLYR (DIIOC | 0x40) /* get device layering information */
+/* new public flag for the hotplug framework */
+#define DINFOHP (DIIOC | 0x400000) /* include hotplug information */
+
/*
* Straight ioctl commands, not bitwise operation
*/
@@ -119,6 +122,7 @@ extern "C" {
#define DI_LINK(addr) ((struct di_link *)((void *)(addr)))
#define DI_LNODE(addr) ((struct di_lnode *)((void *)(addr)))
#define DI_PRIV_FORMAT(addr) ((struct di_priv_format *)((void *)(addr)))
+#define DI_HP(addr) ((struct di_hp *)((void *)(addr)))
/*
* multipath component definitions: Follows the registered component of
@@ -269,12 +273,15 @@ struct di_node { /* useful info to export for each tree node */
di_off_t top_phci;
di_off_t next_phci;
uint32_t multipath_component; /* stores MDI_COMPONENT_* value. */
-
/*
* devi_flags field
*/
uint32_t flags;
uint32_t di_pad2; /* 4 byte padding for 32bit x86 app. */
+ /*
+ * offset to hotplug nodes.
+ */
+ di_off_t hp_data;
};
/*
@@ -321,6 +328,22 @@ struct di_path {
};
/*
+ * chain of hotplug information structures
+ */
+struct di_hp {
+ di_off_t self; /* make it self addressable */
+ di_off_t next; /* next one in chain */
+ di_off_t hp_name; /* name of hotplug connection */
+ int hp_connection; /* connection number */
+ int hp_depends_on; /* connection number depended upon */
+ int hp_state; /* current hotplug state */
+ int hp_type; /* connection type: PCI, ... */
+ di_off_t hp_type_str; /* description of connection type */
+ uint32_t hp_last_change; /* timestamp of last change */
+ di_off_t hp_child; /* child device node */
+};
+
+/*
* Flags for snap_state
*/
#define DI_PATH_SNAP_NOCLIENT 0x01 /* client endpt not in snapshot */
diff --git a/usr/src/uts/common/sys/devops.h b/usr/src/uts/common/sys/devops.h
index 705a8dba02..6efcdcc257 100644
--- a/usr/src/uts/common/sys/devops.h
+++ b/usr/src/uts/common/sys/devops.h
@@ -19,14 +19,13 @@
* CDDL HEADER END
*/
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#ifndef _SYS_DEVOPS_H
#define _SYS_DEVOPS_H
-
#include <sys/types.h>
#include <sys/cred.h>
#include <sys/uio.h>
@@ -42,6 +41,8 @@
#include <sys/ddifm.h>
#include <sys/nexusdefs.h>
#include <sys/ddi_intr.h>
+#include <sys/ddi_hp.h>
+#include <sys/ddi_hp_impl.h>
#include <sys/aio_req.h>
#include <vm/page.h>
@@ -145,7 +146,8 @@ struct cb_ops {
#define BUSO_REV_7 7
#define BUSO_REV_8 8
#define BUSO_REV_9 9
-#define BUSO_REV BUSO_REV_9
+#define BUSO_REV_10 10
+#define BUSO_REV BUSO_REV_10
struct bus_ops {
@@ -268,6 +270,13 @@ struct bus_ops {
int (*bus_intr_op)(dev_info_t *dip, dev_info_t *rdip,
ddi_intr_op_t op, ddi_intr_handle_impl_t *hdlp,
void *result);
+
+ /*
+ * NOTE: the following busop entrypoint is available with version
+ * 10 or greater.
+ */
+ int (*bus_hp_op)(dev_info_t *dip, char *cn_name,
+ ddi_hp_op_t op, void *arg, void *result);
};
/*
diff --git a/usr/src/uts/common/sys/hotplug/pci/pcicfg.h b/usr/src/uts/common/sys/hotplug/pci/pcicfg.h
index c800f90e5a..943ece4875 100644
--- a/usr/src/uts/common/sys/hotplug/pci/pcicfg.h
+++ b/usr/src/uts/common/sys/hotplug/pci/pcicfg.h
@@ -20,28 +20,35 @@
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#ifndef _SYS_HOTPLUG_PCI_PCICFG_H
#define _SYS_HOTPLUG_PCI_PCICFG_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#ifdef __cplusplus
extern "C" {
#endif
+typedef enum pcicfg_flags {
+ /* No probing; used in case of virtual hotplug */
+ PCICFG_FLAG_READ_ONLY = 0x1,
+ /* Enable ARI; used in case of boot case */
+ PCICFG_FLAG_ENABLE_ARI = 0x2
+} pcicfg_flags_t;
+
/*
* Interfaces exported by PCI configurator module, kernel/misc/pcicfg.
*/
-int pcicfg_configure(dev_info_t *, uint_t);
-int pcicfg_unconfigure(dev_info_t *, uint_t);
+int pcicfg_configure(dev_info_t *, uint_t, uint_t, pcicfg_flags_t);
+int pcicfg_unconfigure(dev_info_t *, uint_t, uint_t, pcicfg_flags_t);
#define PCICFG_SUCCESS DDI_SUCCESS
#define PCICFG_FAILURE DDI_FAILURE
+#define PCICFG_ALL_FUNC 0xffffffff
+
/*
* The following subclass definition for Non Transparent bridge should
* be moved to pci.h.
diff --git a/usr/src/uts/common/sys/hotplug/pci/pcie_hp.h b/usr/src/uts/common/sys/hotplug/pci/pcie_hp.h
new file mode 100644
index 0000000000..8810c5203c
--- /dev/null
+++ b/usr/src/uts/common/sys/hotplug/pci/pcie_hp.h
@@ -0,0 +1,332 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _SYS_PCIE_HP_H
+#define _SYS_PCIE_HP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef _KERNEL
+#include <sys/ddi_hp.h>
+#include <sys/pcie_impl.h>
+#endif /* _KERNEL */
+#include "../../../../../common/pci/pci_strings.h"
+#include <sys/hotplug/pci/pcihp.h>
+
+#define PCIEHPC_PROP_HELP "help"
+#define PCIEHPC_PROP_ALL "all"
+#define PCIEHPC_PROP_LED_FAULT "fault_led"
+#define PCIEHPC_PROP_LED_POWER "power_led"
+#define PCIEHPC_PROP_LED_ATTN "attn_led"
+#define PCIEHPC_PROP_LED_ACTIVE "active_led"
+#define PCIEHPC_PROP_CARD_TYPE "card_type"
+#define PCIEHPC_PROP_BOARD_TYPE "board_type"
+#define PCIEHPC_PROP_SLOT_CONDITION "slot_condition"
+
+#define PCIEHPC_PROP_VALUE_UNKNOWN "unknown"
+#define PCIEHPC_PROP_VALUE_ON "on"
+#define PCIEHPC_PROP_VALUE_OFF "off"
+#define PCIEHPC_PROP_VALUE_BLINK "blink"
+#define PCIEHPC_PROP_VALUE_PCIHOTPLUG "pci hotplug"
+#define PCIEHPC_PROP_VALUE_OK "ok"
+#define PCIEHPC_PROP_VALUE_FAILING "failing"
+#define PCIEHPC_PROP_VALUE_FAILED "failed"
+#define PCIEHPC_PROP_VALUE_UNUSABLE "unusable"
+#define PCIEHPC_PROP_VALUE_LED "<on|off|blink>"
+#define PCIEHPC_PROP_VALUE_TYPE "<type description>"
+#define PCIEHPC_PROP_VALUE_CONDITION "<unknown|ok|failing|failed|unusable>"
+
+/* condition */
+#define PCIEHPC_PROP_COND_OK "ok"
+#define PCIEHPC_PROP_COND_FAILING "failing"
+#define PCIEHPC_PROP_COND_FAILED "failed"
+#define PCIEHPC_PROP_COND_UNUSABLE "unusable"
+#define PCIEHPC_PROP_COND_UNKNOWN "unknown"
+
+#ifdef _KERNEL
+
+#define PCIE_HP_MAX_SLOTS 31 /* Max # of slots */
+#define PCIE_HP_CMD_WAIT_TIME 10000 /* Delay in microseconds */
+#define PCIE_HP_CMD_WAIT_RETRY 100 /* Max retry count */
+#define PCIE_HP_DLL_STATE_CHANGE_TIMEOUT 1 /* Timeout in seconds */
+#define PCIE_HP_POWER_GOOD_WAIT_TIME 220000 /* Wait time after issuing a */
+ /* cmd to change slot state */
+
+/* definations for PCIEHPC/PCISHPC */
+#define PCIE_NATIVE_HP_TYPE "PCIe-Native" /* PCIe Native type */
+#define PCIE_ACPI_HP_TYPE "PCIe-ACPI" /* PCIe ACPI type */
+#define PCIE_PROP_HP_TYPE "PCIe-Proprietary" /* PCIe Prop type */
+#define PCIE_PCI_HP_TYPE "PCI-SHPC" /* PCI (SHPC) type */
+
+#define PCIE_GET_HP_CTRL(dip) \
+ (pcie_hp_ctrl_t *)PCIE_DIP2BUS(dip)->bus_hp_ctrl
+
+#define PCIE_SET_HP_CTRL(dip, ctrl_p) \
+ (PCIE_DIP2BUS(dip)->bus_hp_ctrl) = (pcie_hp_ctrl_t *)ctrl_p
+
+#define PCIE_IS_PCIE_HOTPLUG_CAPABLE(bus_p) \
+ ((bus_p->bus_hp_sup_modes & PCIE_ACPI_HP_MODE) || \
+ (bus_p->bus_hp_sup_modes & PCIE_NATIVE_HP_MODE))
+
+#define PCIE_IS_PCI_HOTPLUG_CAPABLE(bus_p) \
+ (bus_p->bus_hp_sup_modes & PCIE_PCI_HP_MODE)
+
+#define PCIE_IS_PCIE_HOTPLUG_ENABLED(bus_p) \
+ ((bus_p->bus_hp_curr_mode == PCIE_ACPI_HP_MODE) || \
+ (bus_p->bus_hp_curr_mode == PCIE_NATIVE_HP_MODE))
+
+#define PCIE_IS_PCI_HOTPLUG_ENABLED(bus_p) \
+ (bus_p->bus_hp_curr_mode & PCIE_PCI_HP_MODE)
+
+typedef struct pcie_hp_ctrl pcie_hp_ctrl_t;
+typedef struct pcie_hp_slot pcie_hp_slot_t;
+
+/*
+ * Maximum length of the string converted from the digital number of pci device
+ * number and function number, including the string's end mark. For example,
+ * device number 0 and function number 255 (ARI case), then the length is
+ * (1 + 3 + 1).
+ */
+#define PCIE_HP_DEV_FUNC_NUM_STRING_LEN 5
+
+/*
+ * Length of the characters in a PCI port name.
+ * The format of the PCI port name is: pci.d,f where d is device number, f is
+ * function number. The constant string and characters are "pci." and ",".
+ */
+#define PCIE_HP_PORT_NAME_STRING_LEN 5
+
+/* Platform specific ops (Native HP, ACPI, etc.) */
+typedef struct pcie_hp_ops {
+ /* initialize/setup hot plug controller hw */
+ int (*init_hpc_hw)(pcie_hp_ctrl_t *ctrl_p);
+
+ /* uninitialize hot plug controller hw */
+ int (*uninit_hpc_hw)(pcie_hp_ctrl_t *ctrl_p);
+
+ /* initialize slot information structure */
+ int (*init_hpc_slotinfo)(pcie_hp_ctrl_t *ctrl_p);
+
+ /* uninitialize slot information structure */
+ int (*uninit_hpc_slotinfo)(pcie_hp_ctrl_t *ctrl_p);
+
+ /* slot poweron */
+ int (*poweron_hpc_slot)(pcie_hp_slot_t *slot_p,
+ ddi_hp_cn_state_t *result);
+
+ /* slot poweroff */
+ /* uninitialize hot plug controller hw */
+ int (*poweroff_hpc_slot)(pcie_hp_slot_t *slot_p,
+ ddi_hp_cn_state_t *result);
+
+ /* enable hot plug interrupts/events */
+ int (*enable_hpc_intr)(pcie_hp_ctrl_t *ctrl_p);
+
+ /* disable hot plug interrupts/events */
+ int (*disable_hpc_intr)(pcie_hp_ctrl_t *ctrl_p);
+} pcie_hp_ops_t;
+
+/* Slot occupant information structure */
+#define PCIE_HP_MAX_OCCUPANTS 128
+typedef struct pcie_hp_occupant_info {
+ int i;
+ char *id[PCIE_HP_MAX_OCCUPANTS];
+} pcie_hp_occupant_info_t;
+
+/*
+ * pcie_hp_led_t
+ *
+ * Type definitions for LED type
+ */
+typedef enum {
+ PCIE_HP_FAULT_LED,
+ PCIE_HP_POWER_LED,
+ PCIE_HP_ATTN_LED,
+ PCIE_HP_ACTIVE_LED
+} pcie_hp_led_t;
+
+/*
+ * pcie_hp_led_state_t
+ *
+ * Type definitions for LED state
+ */
+typedef enum {
+ PCIE_HP_LED_OFF,
+ PCIE_HP_LED_ON,
+ PCIE_HP_LED_BLINK
+} pcie_hp_led_state_t;
+
+/*
+ * PCI and PCI Express Hotplug slot structure
+ */
+struct pcie_hp_slot {
+ uint32_t hs_num; /* Logical slot number */
+ uint32_t hs_phy_slot_num; /* Physical slot number */
+ uint32_t hs_device_num; /* PCI device num for slot */
+ uint16_t hs_minor; /* Minor num for this slot */
+ ddi_hp_cn_info_t hs_info; /* Slot information */
+ ddi_hp_cn_state_t hs_state; /* Slot state */
+
+ pcie_hp_led_state_t hs_power_led_state; /* Power LED state */
+ pcie_hp_led_state_t hs_attn_led_state; /* Attn LED state */
+ pcie_hp_led_state_t hs_active_led_state; /* Active LED state */
+ pcie_hp_led_state_t hs_fault_led_state; /* Fault LED state */
+
+ ap_condition_t hs_condition; /* Condition of the slot. */
+ /* For cfgadm condition. */
+
+ /* Synchronization variable(s) for hot plug events */
+ kcondvar_t hs_attn_btn_cv; /* ATTN button pressed intr */
+ boolean_t hs_attn_btn_pending;
+ kthread_t *hs_attn_btn_threadp; /* ATTN button event thread */
+ boolean_t hs_attn_btn_thread_exit;
+ kcondvar_t hs_dll_active_cv; /* DLL State Changed intr */
+
+ pcie_hp_ctrl_t *hs_ctrl; /* Hotplug ctrl for this slot */
+};
+
+/*
+ * Register ops for read/write of non-standard HPC (e.g: OPL platform).
+ */
+typedef struct pcie_hp_regops {
+ uint_t (*get)(void *cookie, off_t offset);
+ uint_t (*put)(void *cookie, off_t offset, uint_t val);
+ void *cookie;
+} pcie_hp_regops_t;
+
+/*
+ * PCI and PCI Express Hotplug controller structure
+ */
+struct pcie_hp_ctrl {
+ dev_info_t *hc_dip; /* DIP for HP controller */
+ kmutex_t hc_mutex; /* Mutex for this ctrl */
+ uint_t hc_flags; /* Misc flags */
+
+ /* Slot information */
+ pcie_hp_slot_t *hc_slots[PCIE_HP_MAX_SLOTS]; /* Slot pointers */
+ boolean_t hc_has_attn; /* Do we have attn btn? */
+ boolean_t hc_has_mrl; /* Do we have MRL? */
+ kcondvar_t hc_cmd_comp_cv; /* Command Completion intr */
+ boolean_t hc_cmd_pending; /* Command completion pending */
+
+ /* PCI Express Hotplug specific fields */
+ boolean_t hc_has_emi_lock; /* Do we have EMI Lock? */
+ boolean_t hc_dll_active_rep; /* Report DLL DL_Active state */
+ pcie_hp_ops_t hc_ops; /* Platform specific ops */
+ /* (Native, ACPI) */
+
+ /* PCI Hotplug (SHPC) specific fields */
+ uint32_t hc_num_slots_impl; /* # of HP Slots Implemented */
+ uint32_t hc_num_slots_connected; /* # of HP Slots Connected */
+ int hc_curr_bus_speed; /* Current Bus Speed */
+ uint32_t hc_device_start; /* 1st PCI Device # */
+ uint32_t hc_phys_start; /* 1st Phys Device # */
+ uint32_t hc_device_increases; /* Device # Increases */
+ boolean_t hc_arbiter_timeout; /* Got a Arb timeout IRQ */
+
+ /* Register read/write ops for non-standard HPC (e.g: OPL) */
+ pcie_hp_regops_t hc_regops;
+
+ /* Platform implementation specific data if any: ACPI, CK804,... */
+ void *hc_misc_data;
+};
+
+/*
+ * Control structure for tree walk during configure/unconfigure operation.
+ */
+typedef struct pcie_hp_cn_cfg_t {
+ void *slotp;
+ boolean_t flag; /* Flag to ignore errors */
+ int rv; /* Return error code */
+ dev_info_t *dip; /* dip at which the (first) */
+ /* error occurred */
+ void *cn_private; /* Connection specific data */
+} pcie_hp_cn_cfg_t;
+
+/*
+ * arg for unregistering port of a pci bridge
+ */
+typedef struct pcie_hp_unreg_port {
+ /* pci bridge dip to which the port is associated */
+ dev_info_t *nexus_dip;
+ /*
+ * Connector number of the physical slot whose dependent ports will be
+ * unregistered. If NULL, then all the ports of the pci bridge dip will
+ * be unregistered.
+ */
+ int connector_num;
+ int rv;
+} pcie_hp_unreg_port_t;
+
+/*
+ * arg for getting a port's state
+ */
+typedef struct pcie_hp_port_state {
+ char *cn_name;
+ ddi_hp_cn_state_t cn_state;
+ int rv;
+} pcie_hp_port_state_t;
+
+/* hc_flags */
+#define PCIE_HP_INITIALIZED_FLAG (1 << 0) /* HPC initialized */
+
+/* PCIe hotplug friendly functions */
+extern int pcie_hp_init(dev_info_t *dip, caddr_t arg);
+extern int pcie_hp_uninit(dev_info_t *dip);
+extern int pcie_hp_intr(dev_info_t *dip);
+extern int pcie_hp_probe(pcie_hp_slot_t *slot_p);
+extern int pcie_hp_unprobe(pcie_hp_slot_t *slot_p);
+extern int pcie_hp_common_ops(dev_info_t *dip, char *cn_name, ddi_hp_op_t op,
+ void *arg, void *result);
+extern dev_info_t *pcie_hp_devi_find(dev_info_t *dip, uint_t device,
+ uint_t function);
+extern void pcie_hp_create_occupant_props(dev_info_t *self, dev_t dev,
+ int pci_dev);
+extern void pcie_hp_create_occupant_props(dev_info_t *self, dev_t dev,
+ int pci_dev);
+extern void pcie_hp_delete_occupant_props(dev_info_t *dip, dev_t dev);
+extern int pcie_copyin_nvlist(char *packed_buf, size_t packed_sz,
+ nvlist_t **nvlp);
+extern int pcie_copyout_nvlist(nvlist_t *nvl, char *packed_buf,
+ size_t *packed_sz);
+extern char *pcie_led_state_text(pcie_hp_led_state_t state);
+extern char *pcie_slot_condition_text(ap_condition_t condition);
+extern int pcie_create_minor_node(pcie_hp_ctrl_t *, int);
+extern void pcie_remove_minor_node(pcie_hp_ctrl_t *, int);
+extern void pcie_hp_gen_sysevent_req(char *slot_name, int hint,
+ dev_info_t *self, int kmflag);
+
+extern const struct pci_class_strings_s class_pci[];
+extern int class_pci_items;
+
+#endif /* _KERNEL */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_PCIE_HP_H */
diff --git a/usr/src/uts/common/sys/hotplug/pci/pciehpc.h b/usr/src/uts/common/sys/hotplug/pci/pciehpc.h
index dd1ad7c416..1d57720559 100644
--- a/usr/src/uts/common/sys/hotplug/pci/pciehpc.h
+++ b/usr/src/uts/common/sys/hotplug/pci/pciehpc.h
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -19,17 +18,14 @@
*
* CDDL HEADER END
*/
-
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#ifndef _SYS_HOTPLUG_PCI_PCIEHPC_H
#define _SYS_HOTPLUG_PCI_PCIEHPC_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#ifdef __cplusplus
extern "C" {
#endif
@@ -37,17 +33,22 @@ extern "C" {
/*
* Interfaces exported by PCI-E nexus Hot Plug Controller extension module
*/
-
-/* register ops for read/write of non-standard HPC (e.g: OPL platform) */
-typedef struct pciehpc_regops {
- uint_t (*get)(void *cookie, off_t offset);
- uint_t (*put)(void *cookie, off_t offset, uint_t val);
- void *cookie;
-} pciehpc_regops_t;
-
-int pciehpc_init(dev_info_t *, pciehpc_regops_t *);
-int pciehpc_uninit(dev_info_t *);
-int pciehpc_intr(dev_info_t *);
+int pciehpc_init(dev_info_t *dip, caddr_t arg);
+int pciehpc_uninit(dev_info_t *dip);
+int pciehpc_intr(dev_info_t *dip);
+int pciehpc_hp_ops(dev_info_t *dip, char *cn_name, ddi_hp_op_t op, void *arg,
+ void *result);
+void pciehpc_get_slot_state(pcie_hp_slot_t *slot_p);
+void pciehpc_set_slot_name(pcie_hp_ctrl_t *ctrl_p);
+uint8_t pciehpc_reg_get8(pcie_hp_ctrl_t *ctrl_p, uint_t off);
+uint16_t pciehpc_reg_get16(pcie_hp_ctrl_t *ctrl_p, uint_t off);
+uint32_t pciehpc_reg_get32(pcie_hp_ctrl_t *ctrl_p, uint_t off);
+void pciehpc_reg_put8(pcie_hp_ctrl_t *ctrl_p, uint_t off, uint8_t val);
+void pciehpc_reg_put16(pcie_hp_ctrl_t *ctrl_p, uint_t off, uint16_t val);
+void pciehpc_reg_put32(pcie_hp_ctrl_t *ctrl_p, uint_t off, uint32_t val);
+#if defined(__i386) || defined(__amd64)
+extern void pciehpc_update_ops(pcie_hp_ctrl_t *ctrl_p);
+#endif /* defined(__i386) || defined(__amd64) */
#ifdef __cplusplus
}
diff --git a/usr/src/uts/common/sys/hotplug/pci/pciehpc_impl.h b/usr/src/uts/common/sys/hotplug/pci/pciehpc_impl.h
deleted file mode 100644
index b2915d15e9..0000000000
--- a/usr/src/uts/common/sys/hotplug/pci/pciehpc_impl.h
+++ /dev/null
@@ -1,233 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License (the "License").
- * You may not use this file except in compliance with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information: Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- */
-
-/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
- */
-
-#ifndef _SYS_HOTPLUG_PCI_PCIEHPC_IMPL_H
-#define _SYS_HOTPLUG_PCI_PCIEHPC_IMPL_H
-
-#pragma ident "%Z%%M% %I% %E% SMI"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <sys/disp.h>
-#include <sys/stat.h>
-#include <sys/condvar.h>
-#include <sys/pcie.h>
-#include <sys/hotplug/hpcsvc.h>
-#include <sys/hotplug/pci/pciehpc.h>
-
-/*
- * PCI Express Hot Plug slot softstate structure
- *
- */
-typedef struct pciehpc_slot
-{
- hpc_slot_info_t slot_info; /* HPS framework slot info */
- hpc_slot_t slot_handle; /* HPS framework handle */
- hpc_slot_ops_t slot_ops; /* HPS framework callbacks */
- uint32_t fault_led_state; /* Fault LED state */
- uint32_t power_led_state; /* Power LED state */
- uint32_t attn_led_state; /* Attn LED state */
- uint32_t active_led_state; /* Active LED state */
- hpc_slot_state_t slot_state; /* Slot State */
- uint32_t slotNum; /* slot number */
- /* synchronization variable(s) for hot plug events */
- kcondvar_t cmd_comp_cv; /* Command Completion intr. */
- boolean_t command_pending;
- kcondvar_t attn_btn_cv; /* ATTN button pressed intr */
- boolean_t attn_btn_pending;
- kthread_t *attn_btn_threadp; /* ATTN button event thread */
- boolean_t attn_btn_thread_exit;
- kcondvar_t dll_active_cv; /* DLL State Changed intr */
-} pciehpc_slot_t;
-
-typedef enum {
- PCIEHPC_NATIVE_HP_MODE, PCIEHPC_ACPI_HP_MODE
-} pciehpc_hp_mode_t;
-
-typedef uint32_t pciehpc_soft_state_t;
-
-/* init_flags */
-#define PCIEHPC_SOFT_STATE_UNINITIALIZED 0x01
-#define PCIEHPC_SOFT_STATE_INITIALIZED 0x02
-#define PCIEHPC_SOFT_STATE_INIT_HTABLE 0x04
-#define PCIEHPC_SOFT_STATE_INIT_ALLOC 0x08
-#define PCIEHPC_SOFT_STATE_INIT_HANDLER 0x10
-#define PCIEHPC_SOFT_STATE_INIT_ENABLE 0x20
-#define PCIEHPC_SOFT_STATE_INIT_BLOCK 0x40
-#define PCIEHPC_SOFT_STATE_INIT_FM 0x80
-#define PCIEHPC_SOFT_STATE_PCIE_DEV 0x10000
-
-/*
- * PCI Express Hotplug controller soft state structure
- */
-typedef struct pciehpc
-{
- dev_info_t *dip; /* DIP for the Nexus */
- uint8_t bus; /* primary bus number */
- uint8_t dev; /* device number */
- uint8_t func; /* function number */
- kmutex_t pciehpc_mutex; /* Mutex for this ctrl */
- pciehpc_soft_state_t soft_state; /* soft state flags */
- pciehpc_hp_mode_t hp_mode; /* HP mode (Native, ACPI) */
- struct pciehpc *nextp; /* Linked list pointer */
-
- /* PCIE Hot Plug Controller register access */
- ddi_acc_handle_t cfghdl; /* PCI cfg access handle */
- caddr_t regs_base; /* config regs base */
- uint_t pcie_caps_reg_offset; /* offset to PCIE Cap regs */
-
- /* slot information */
- pciehpc_slot_t slot; /* Slot info */
- boolean_t has_attn; /* Do we have attn btn? */
- boolean_t has_mrl; /* Do we have MRL? */
- boolean_t has_emi_lock; /* Do we have EMI Lock? */
-
- /* link capablities */
- boolean_t dll_active_rep; /* Do we report DLL DL_Active state? */
-
- /* register read/write ops for non-standard HPC (e.g: OPL) */
- pciehpc_regops_t regops;
-
- /* platform specific ops (Native HP, ACPI, etc.) */
- struct pciehpc_ops {
- /* initialize/setup hot plug controller hw */
- int (*init_hpc_hw)(struct pciehpc *ctrl_p);
- /* initialize slot information structure */
- int (*init_hpc_slotinfo)(struct pciehpc *ctrl_p);
- /* disable hot plug interrupts/events */
- int (*disable_hpc_intr)(struct pciehpc *ctrl_p);
- /* enable hot plug interrupts/events */
- int (*enable_hpc_intr)(struct pciehpc *ctrl_p);
- /* uninitialize hot plug controller hw */
- int (*uninit_hpc_hw)(struct pciehpc *ctrl_p);
- /* uninitialize slot information structure */
- int (*uninit_hpc_slotinfo)(struct pciehpc *ctrl_p);
- /* probe for HPC */
- int (*probe_hpc)(struct pciehpc *ctrl_p);
- } ops;
-
- /* platform implementation specific data if any: ACPI, CK804,... */
- void *misc_data;
-} pciehpc_t;
-
-typedef struct pciehpc_ops pciehpc_ops_t;
-
-/*
- * PCI-E HPC Command Completion delay in microseconds and the max retry
- * count.
- */
-#define PCIEHPC_CMD_WAIT_TIME 10000
-#define PCIEHPC_CMD_WAIT_RETRY 100
-
-/*
- * PCI-E HPC Dll State Change time out in seconds
- */
-#define PCIEHPC_DLL_STATE_CHANGE_TIMEOUT 1
-
-#define SLOTCTL_SUPPORTED_INTRS_MASK \
- (PCIE_SLOTCTL_ATTN_BTN_EN \
- | PCIE_SLOTCTL_PWR_FAULT_EN \
- | PCIE_SLOTCTL_MRL_SENSOR_EN \
- | PCIE_SLOTCTL_PRESENCE_CHANGE_EN \
- | PCIE_SLOTCTL_CMD_INTR_EN \
- | PCIE_SLOTCTL_HP_INTR_EN \
- | PCIE_SLOTCTL_DLL_STATE_EN)
-
-#define SLOT_STATUS_EVENTS \
- (PCIE_SLOTSTS_ATTN_BTN_PRESSED \
- | PCIE_SLOTSTS_PWR_FAULT_DETECTED \
- | PCIE_SLOTSTS_MRL_SENSOR_CHANGED \
- | PCIE_SLOTSTS_COMMAND_COMPLETED \
- | PCIE_SLOTSTS_PRESENCE_CHANGED \
- | PCIE_SLOTSTS_DLL_STATE_CHANGED)
-
-/*
- * function prototype defintions for common native mode functions in
- * PCIEHPC module.
- */
-int pciehpc_hpc_init(pciehpc_t *ctrl_p);
-int pciehpc_hpc_uninit(pciehpc_t *ctrl_p);
-int pciehpc_slotinfo_init(pciehpc_t *ctrl_p);
-int pciehpc_enable_intr(pciehpc_t *ctrl_p);
-int pciehpc_disable_intr(pciehpc_t *ctrl_p);
-int pciehpc_slotinfo_uninit(pciehpc_t *ctrl_p);
-int pciehpc_probe_hpc(pciehpc_t *ctrl_p);
-hpc_led_state_t pciehpc_led_state_to_hpc(uint16_t state);
-uint16_t pciehpc_led_state_to_pciehpc(hpc_led_state_t state);
-hpc_led_state_t pciehpc_get_led_state(pciehpc_t *ctrl_p, hpc_led_t led);
-void pciehpc_set_led_state(pciehpc_t *ctrl_p, hpc_led_t led,
- hpc_led_state_t state);
-int pciehpc_slot_connect(caddr_t ops_arg, hpc_slot_t slot_hdl,
- void *data, uint_t flags);
-int pciehpc_slot_disconnect(caddr_t ops_arg, hpc_slot_t slot_hdl,
- void *data, uint_t flags);
-int pciehpc_slot_control(caddr_t ops_arg, hpc_slot_t slot_hdl,
- int request, caddr_t arg);
-void pciehpc_get_slot_state(pciehpc_t *ctrl_p);
-void pciehpc_issue_hpc_command(pciehpc_t *ctrl_p, uint16_t control);
-int pciehpc_regs_setup(dev_info_t *dip, uint_t rnum, offset_t off,
- caddr_t *addrp, ddi_acc_handle_t *handle);
-void pciehpc_regs_teardown(ddi_acc_handle_t *handle);
-int pciehpc_register_slot(pciehpc_t *ctrl_p);
-int pciehpc_unregister_slot(pciehpc_t *ctrl_p);
-uint8_t pciehpc_reg_get8(pciehpc_t *ctrl_p, uint_t off);
-uint16_t pciehpc_reg_get16(pciehpc_t *ctrl_p, uint_t off);
-uint32_t pciehpc_reg_get32(pciehpc_t *ctrl_p, uint_t off);
-void pciehpc_reg_put8(pciehpc_t *ctrl_p, uint_t off, uint8_t val);
-void pciehpc_reg_put16(pciehpc_t *ctrl_p, uint_t off, uint16_t val);
-void pciehpc_reg_put32(pciehpc_t *ctrl_p, uint_t off, uint32_t val);
-void pciehpc_set_slot_name(pciehpc_t *ctrl_p);
-
-#if defined(__i386) || defined(__amd64)
-void pciehpc_update_ops(pciehpc_t *ctrl_p);
-#endif /* defined(__i386) || defined(__amd64) */
-
-#ifdef DEBUG
-extern int pciehpc_debug;
-#define PCIEHPC_DEBUG(args) if (pciehpc_debug >= 1) cmn_err args
-#define PCIEHPC_DEBUG2(args) if (pciehpc_debug >= 2) cmn_err args
-#define PCIEHPC_DEBUG3(args) if (pciehpc_debug >= 3) cmn_err args
-#else
-#define PCIEHPC_DEBUG(args)
-#define PCIEHPC_DEBUG2(args)
-#define PCIEHPC_DEBUG3(args)
-#endif
-
-/* default interrupt priority for Hot Plug interrupts */
-#define PCIEHPC_INTR_PRI 1
-
-#define PCIE_ENABLE_ERRORS(arg1) \
- pcie_enable_errors(arg1); \
- (void) pcie_enable_ce(arg1)
-#define PCIE_DISABLE_ERRORS(arg1) pcie_disable_errors(arg1)
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _SYS_HOTPLUG_PCI_PCIEHPC_IMPL_H */
diff --git a/usr/src/uts/common/sys/hotplug/pci/pcihp.h b/usr/src/uts/common/sys/hotplug/pci/pcihp.h
index 75a4ee0c2d..eb96e924bd 100644
--- a/usr/src/uts/common/sys/hotplug/pci/pcihp.h
+++ b/usr/src/uts/common/sys/hotplug/pci/pcihp.h
@@ -19,15 +19,13 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#ifndef _SYS_HOTPLUG_PCI_PCIHP_H
#define _SYS_HOTPLUG_PCI_PCIHP_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#ifdef __cplusplus
extern "C" {
#endif
diff --git a/usr/src/uts/common/sys/hotplug/pci/pcishpc.h b/usr/src/uts/common/sys/hotplug/pci/pcishpc.h
index 81443bbaa4..12b9afd30a 100644
--- a/usr/src/uts/common/sys/hotplug/pci/pcishpc.h
+++ b/usr/src/uts/common/sys/hotplug/pci/pcishpc.h
@@ -18,32 +18,26 @@
*
* CDDL HEADER END
*/
-
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#ifndef _SYS_PCISHPC_H
-#define _SYS_PCISHPC_H
-
-#pragma ident "%Z%%M% %I% %E% SMI"
+#ifndef _SYS_HOTPLUG_PCI_PCISHPC_H
+#define _SYS_HOTPLUG_PCI_PCISHPC_H
#ifdef __cplusplus
extern "C" {
#endif
-/*
- * Interfaces exported by SHPC Nexus extension module
- */
-
-int pcishpc_init(dev_info_t *);
-int pcishpc_uninit(dev_info_t *);
-int pcishpc_intr(dev_info_t *);
+int pcishpc_init(dev_info_t *dip);
+int pcishpc_uninit(dev_info_t *dip);
+int pcishpc_intr(dev_info_t *dip);
+int pcishpc_hp_ops(dev_info_t *dip, char *cn_name, ddi_hp_op_t op, void *arg,
+ void *result);
-#define PCISHPC_INTR_PRI (LOCK_LEVEL - 1)
#ifdef __cplusplus
}
#endif
-#endif /* _SYS_PCISHPC_H */
+#endif /* _SYS_HOTPLUG_PCI_PCISHPC_H */
diff --git a/usr/src/uts/common/sys/modctl.h b/usr/src/uts/common/sys/modctl.h
index 7ccd1d6dd4..3ac19b948e 100644
--- a/usr/src/uts/common/sys/modctl.h
+++ b/usr/src/uts/common/sys/modctl.h
@@ -269,6 +269,7 @@ struct modlinkage {
#define MODISRETIRED 42
#define MODDEVEMPTYDIR 43
#define MODREMDRVALIAS 44
+#define MODHPOPS 45
/*
* sub cmds for MODEVENTS
@@ -289,6 +290,15 @@ struct modlinkage {
#define MODDEVNAME_RECONFIG 4
#define MODDEVNAME_SYSAVAIL 5
+/*
+ * subcmds for MODHPOPS
+ */
+#define MODHPOPS_CHANGE_STATE 0
+#define MODHPOPS_CREATE_PORT 1
+#define MODHPOPS_REMOVE_PORT 2
+#define MODHPOPS_BUS_GET 3
+#define MODHPOPS_BUS_SET 4
+
/*
* Data structure passed to modconfig command in kernel to build devfs tree
diff --git a/usr/src/uts/common/sys/pci.h b/usr/src/uts/common/sys/pci.h
index efb19097e9..9d38b5bb37 100644
--- a/usr/src/uts/common/sys/pci.h
+++ b/usr/src/uts/common/sys/pci.h
@@ -603,7 +603,7 @@ extern "C" {
#define PCI_CAP_ID_VS 0x9 /* Vendor Specific */
#define PCI_CAP_ID_DEBUG_PORT 0xA /* Debug Port supported */
#define PCI_CAP_ID_cPCI_CRC 0xB /* CompactPCI central resource ctrl */
-#define PCI_CAP_ID_PCI_HOTPLUG 0xC /* PCI Hot Plug supported */
+#define PCI_CAP_ID_PCI_HOTPLUG 0xC /* PCI Hot Plug (SHPC) supported */
#define PCI_CAP_ID_P2P_SUBSYS 0xD /* PCI bridge Sub-system ID */
#define PCI_CAP_ID_AGP_8X 0xE /* AGP 8X supported */
#define PCI_CAP_ID_SECURE_DEV 0xF /* Secure Device supported */
@@ -796,6 +796,104 @@ typedef struct pcix_attr {
#define PCI_PCIX_BSS_SPL_DLY 0x20 /* Secondary split comp delayed */
/*
+ * PCI Hotplug capability entry offsets
+ *
+ * SHPC based PCI hotplug controller registers accessed via the DWORD
+ * select and DATA registers in PCI configuration space relative to the
+ * PCI HP capibility pointer.
+ */
+#define PCI_HP_DWORD_SELECT_OFF 0x2
+#define PCI_HP_DWORD_DATA_OFF 0x4
+
+#define PCI_HP_BASE_OFFSET_REG 0x00
+#define PCI_HP_SLOTS_AVAIL_I_REG 0x01
+#define PCI_HP_SLOTS_AVAIL_II_REG 0x02
+#define PCI_HP_SLOT_CONFIGURATION_REG 0x03
+#define PCI_HP_PROF_IF_SBCR_REG 0x04
+#define PCI_HP_COMMAND_STATUS_REG 0x05
+#define PCI_HP_IRQ_LOCATOR_REG 0x06
+#define PCI_HP_SERR_LOCATOR_REG 0x07
+#define PCI_HP_CTRL_SERR_INT_REG 0x08
+#define PCI_HP_LOGICAL_SLOT_REGS 0x09
+#define PCI_HP_VENDOR_SPECIFIC 0x28
+
+/* Definitions used with the PCI_HP_SLOTS_AVAIL_I_REG register */
+#define PCI_HP_AVAIL_33MHZ_CONV_SPEED_SHIFT 0
+#define PCI_HP_AVAIL_66MHZ_PCIX_SPEED_SHIFT 8
+#define PCI_HP_AVAIL_100MHZ_PCIX_SPEED_SHIFT 16
+#define PCI_HP_AVAIL_133MHZ_PCIX_SPEED_SHIFT 24
+#define PCI_HP_AVAIL_SPEED_MASK 0x1F
+
+/* Definitions used with the PCI_HP_SLOTS_AVAIL_II_REG register */
+#define PCI_HP_AVAIL_66MHZ_CONV_SPEED_SHIFT 0
+
+/* Register bits used with the PCI_HP_PROF_IF_SBCR_REG register */
+#define PCI_HP_SBCR_33MHZ_CONV_SPEED 0x0
+#define PCI_HP_SBCR_66MHZ_CONV_SPEED 0x1
+#define PCI_HP_SBCR_66MHZ_PCIX_SPEED 0x2
+#define PCI_HP_SBCR_100MHZ_PCIX_SPEED 0x3
+#define PCI_HP_SBCR_133MHZ_PCIX_SPEED 0x4
+#define PCI_HP_SBCR_SPEED_MASK 0x7
+
+/* Register bits used with the PCI_HP_COMMAND_STATUS_REG register */
+#define PCI_HP_COMM_STS_ERR_INVALID_SPEED 0x80000
+#define PCI_HP_COMM_STS_ERR_INVALID_COMMAND 0x40000
+#define PCI_HP_COMM_STS_ERR_MRL_OPEN 0x20000
+#define PCI_HP_COMM_STS_ERR_MASK 0xe0000
+#define PCI_HP_COMM_STS_CTRL_BUSY 0x10000
+#define PCI_HP_COMM_STS_SET_SPEED 0x40
+
+/* Register bits used with the PCI_HP_CTRL_SERR_INT_REG register */
+#define PCI_HP_SERR_INT_GLOBAL_IRQ_MASK 0x1
+#define PCI_HP_SERR_INT_GLOBAL_SERR_MASK 0x2
+#define PCI_HP_SERR_INT_CMD_COMPLETE_MASK 0x4
+#define PCI_HP_SERR_INT_ARBITER_SERR_MASK 0x8
+#define PCI_HP_SERR_INT_CMD_COMPLETE_IRQ 0x10000
+#define PCI_HP_SERR_INT_ARBITER_IRQ 0x20000
+#define PCI_HP_SERR_INT_MASK_ALL 0xf
+
+/* Register bits used with the PCI_HP_LOGICAL_SLOT_REGS register */
+#define PCI_HP_SLOT_POWER_ONLY 0x1
+#define PCI_HP_SLOT_ENABLED 0x2
+#define PCI_HP_SLOT_DISABLED 0x3
+#define PCI_HP_SLOT_STATE_MASK 0x3
+#define PCI_HP_SLOT_MRL_STATE_MASK 0x100
+#define PCI_HP_SLOT_66MHZ_CONV_CAPABLE 0x200
+#define PCI_HP_SLOT_CARD_EMPTY_MASK 0xc00
+#define PCI_HP_SLOT_66MHZ_PCIX_CAPABLE 0x1000
+#define PCI_HP_SLOT_100MHZ_PCIX_CAPABLE 0x2000
+#define PCI_HP_SLOT_133MHZ_PCIX_CAPABLE 0x3000
+#define PCI_HP_SLOT_PCIX_CAPABLE_MASK 0x3000
+#define PCI_HP_SLOT_PCIX_CAPABLE_SHIFT 12
+#define PCI_HP_SLOT_PRESENCE_DETECTED 0x10000
+#define PCI_HP_SLOT_ISO_PWR_DETECTED 0x20000
+#define PCI_HP_SLOT_ATTN_DETECTED 0x40000
+#define PCI_HP_SLOT_MRL_DETECTED 0x80000
+#define PCI_HP_SLOT_POWER_DETECTED 0x100000
+#define PCI_HP_SLOT_PRESENCE_MASK 0x1000000
+#define PCI_HP_SLOT_ISO_PWR_MASK 0x2000000
+#define PCI_HP_SLOT_ATTN_MASK 0x4000000
+#define PCI_HP_SLOT_MRL_MASK 0x8000000
+#define PCI_HP_SLOT_POWER_MASK 0x10000000
+#define PCI_HP_SLOT_MRL_SERR_MASK 0x20000000
+#define PCI_HP_SLOT_POWER_SERR_MASK 0x40000000
+#define PCI_HP_SLOT_MASK_ALL 0x5f000000
+
+/* Register bits used with the PCI_HP_IRQ_LOCATOR_REG register */
+#define PCI_HP_IRQ_CMD_COMPLETE 0x1
+#define PCI_HP_IRQ_SLOT_N_PENDING 0x2
+
+/* Register bits used with the PCI_HP_SERR_LOCATOR_REG register */
+#define PCI_HP_IRQ_SERR_ARBITER_PENDING 0x1
+#define PCI_HP_IRQ_SERR_SLOT_N_PENDING 0x2
+
+/* Register bits used with the PCI_HP_SLOT_CONFIGURATION_REG register */
+#define PCI_HP_SLOT_CONFIG_MRL_SENSOR 0x40000000
+#define PCI_HP_SLOT_CONFIG_ATTN_BUTTON 0x80000000
+#define PCI_HP_SLOT_CONFIG_PHY_SLOT_NUM_SHIFT 16
+#define PCI_HP_SLOT_CONFIG_PHY_SLOT_NUM_MASK 0x3FF
+
+/*
* PCI Message Signalled Interrupts (MSI) capability entry offsets for 32-bit
*/
#define PCI_MSI_CTRL 0x02 /* MSI control register, 2 bytes */
diff --git a/usr/src/uts/common/sys/pci_impl.h b/usr/src/uts/common/sys/pci_impl.h
index 0dc0679bd4..d485800b96 100644
--- a/usr/src/uts/common/sys/pci_impl.h
+++ b/usr/src/uts/common/sys/pci_impl.h
@@ -26,7 +26,6 @@
#ifndef _SYS_PCI_IMPL_H
#define _SYS_PCI_IMPL_H
-
#include <sys/dditypes.h>
#include <sys/memlist.h>
@@ -133,6 +132,24 @@ extern int memlist_count(struct memlist *);
#endif /* __i386 || __amd64 */
+/* Definitions for minor numbers */
+#define PCI_MINOR_NUM(x, y) (((uint_t)(x) << 8) | ((y) & 0xFF))
+#define PCI_MINOR_NUM_TO_PCI_DEVNUM(x) ((x) & 0xFF)
+#define PCI_MINOR_NUM_TO_INSTANCE(x) ((x) >> 8)
+#define PCI_DEVCTL_MINOR 0xFF
+
+/*
+ * Minor numbers for dedicated pcitool nodes.
+ * Note that FF and FE minor numbers are used for other minor nodes.
+ */
+#define PCI_TOOL_REG_MINOR_NUM 0xFD
+#define PCI_TOOL_INTR_MINOR_NUM 0xFC
+
+/* pci devctl soft state flag */
+#define PCI_SOFT_STATE_CLOSED 0x0
+#define PCI_SOFT_STATE_OPEN 0x1
+#define PCI_SOFT_STATE_OPEN_EXCL 0x2
+
/*
* PCI capability related definitions.
*/
diff --git a/usr/src/uts/common/sys/pcie.h b/usr/src/uts/common/sys/pcie.h
index 6ffa4ac033..05b70a56fa 100644
--- a/usr/src/uts/common/sys/pcie.h
+++ b/usr/src/uts/common/sys/pcie.h
@@ -50,6 +50,15 @@ extern "C" {
#define PCIE_SLOTSTS 0x1A /* Slot Status */
#define PCIE_ROOTCTL 0x1C /* Root Control */
#define PCIE_ROOTSTS 0x20 /* Root Status */
+#define PCIE_DEVCAP2 0x24 /* Device Capability 2 */
+#define PCIE_DEVCTL2 0x28 /* Device Control 2 */
+#define PCIE_DEVSTS2 0x2A /* Device Status 2 */
+#define PCIE_LINKCAP2 0x2C /* Link Capability 2 */
+#define PCIE_LINKCTL2 0x30 /* Link Control 2 */
+#define PCIE_LINKSTS2 0x32 /* Link Status 2 */
+#define PCIE_SLOTCAP2 0x34 /* Slot Capability 2 */
+#define PCIE_SLOTCTL2 0x38 /* Slot Control 2 */
+#define PCIE_SLOTSTS2 0x3A /* Slot Status 2 */
/*
* PCI-Express Config Space size
@@ -60,6 +69,7 @@ extern "C" {
* PCI-Express Capabilities Register (2 bytes)
*/
#define PCIE_PCIECAP_VER_1_0 0x1 /* PCI-E spec 1.0 */
+#define PCIE_PCIECAP_VER_2_0 0x2 /* PCI-E spec 2.0 */
#define PCIE_PCIECAP_VER_MASK 0xF /* Version Mask */
#define PCIE_PCIECAP_DEV_TYPE_PCIE_DEV 0x00 /* PCI-E Endpont Device */
#define PCIE_PCIECAP_DEV_TYPE_PCI_DEV 0x10 /* "Leg PCI" Endpont Device */
@@ -303,6 +313,7 @@ extern "C" {
#define PCIE_SLOTCTL_DLL_STATE_EN 0x1000 /* DLL State Changed En */
#define PCIE_SLOTCTL_ATTN_INDICATOR_MASK 0x00C0 /* Attn Indicator mask */
#define PCIE_SLOTCTL_PWR_INDICATOR_MASK 0x0300 /* Power Indicator mask */
+#define PCIE_SLOTCTL_INTR_MASK 0x103f /* Supported intr mask */
/* State values for the Power and Attention Indicators */
#define PCIE_SLOTCTL_INDICATOR_STATE_ON 0x1 /* indicator ON */
@@ -334,6 +345,7 @@ extern "C" {
#define PCIE_SLOTSTS_PRESENCE_DETECTED 0x40 /* Card Present in slot */
#define PCIE_SLOTSTS_EMI_LOCK_SET 0x0080 /* EMI Lock set */
#define PCIE_SLOTSTS_DLL_STATE_CHANGED 0x0100 /* DLL State Changed */
+#define PCIE_SLOTSTS_STATUS_EVENTS 0x11f /* Supported events */
/*
* Root Control Register (2 bytes)
@@ -352,6 +364,49 @@ extern "C" {
#define PCIE_ROOTSTS_PME_STATUS 0x10000 /* PME Status */
#define PCIE_ROOTSTS_PME_PENDING 0x20000 /* PME Pending */
+/*
+ * Device Capabilities 2 Register (4 bytes)
+ */
+#define PCIE_DEVCAP2_COM_TO_RANGE_MASK 0xF
+#define PCIE_DEVCAP2_COM_TO_DISABLE 0x10
+#define PCIE_DEVCAP2_ARI_FORWARD 0x20
+#define PCIE_DEVCAP2_ATOMICOP_ROUTING 0x40
+#define PCIE_DEVCAP2_32_ATOMICOP_COMPL 0x80
+#define PCIE_DEVCAP2_64_ATOMICOP_COMPL 0x100
+#define PCIE_DEVCAP2_128_CAS_COMPL 0x200
+#define PCIE_DEVCAP2_NO_RO_PR_PR_PASS 0x400
+#define PCIE_DEVCAP2_LTR_MECH 0x800
+#define PCIE_DEVCAP2_TPH_COMP_SHIFT 12
+#define PCIE_DEVCAP2_TPH_COMP_MASK 0x3
+#define PCIE_DEVCAP2_EXT_FMT_FIELD 0x100000
+#define PCIE_DEVCAP2_END_END_TLP_PREFIX 0x200000
+#define PCIE_DEVCAP2_MAX_END_END_SHIFT 22
+#define PCIE_DEVCAP2_MAX_END_END_MASK 0x3
+
+/*
+ * Device Control 2 Register (2 bytes)
+ */
+#define PCIE_DEVCTL2_COM_TO_RANGE_0 0x0
+#define PCIE_DEVCTL2_COM_TO_RANGE_1 0x1
+#define PCIE_DEVCTL2_COM_TO_RANGE_2 0x2
+#define PCIE_DEVCTL2_COM_TO_RANGE_3 0x5
+#define PCIE_DEVCTL2_COM_TO_RANGE_4 0x6
+#define PCIE_DEVCTL2_COM_TO_RANGE_5 0x9
+#define PCIE_DEVCTL2_COM_TO_RANGE_6 0xa
+#define PCIE_DEVCTL2_COM_TO_RANGE_7 0xd
+#define PCIE_DEVCTL2_COM_TO_RANGE_8 0xe
+#define PCIE_DEVCTL2_COM_TO_DISABLE 0x10
+#define PCIE_DEVCTL2_ARI_FORWARD_EN 0x20
+#define PCIE_DEVCTL2_ATOMICOP_REQ_EN 0x40
+#define PCIE_DEVCTL2_ATOMICOP_EGRS_BLK 0x80
+#define PCIE_DEVCTL2_IDO_REQ_EN 0x100
+#define PCIE_DEVCTL2_IDO_COMPL_EN 0x200
+#define PCIE_DEVCTL2_LTR_MECH_EN 0x400
+#define PCIE_DEVCTL2_END_END_TLP_PREFIX 0x8000
+
+
+
+
/*
* PCI-Express Enhanced Capabilities Link Entry Bit Offsets
@@ -549,6 +604,25 @@ extern "C" {
#define PCIE_SER_SID_UPPER_DW 0x8 /* Upper 32-bit Serial Number */
/*
+ * ARI Capability Offsets
+ */
+#define PCIE_ARI_HDR 0x0 /* Enhanced Capability Header */
+#define PCIE_ARI_CAP 0x4 /* ARI Capability Register */
+#define PCIE_ARI_CTL 0x6 /* ARI Control Register */
+
+#define PCIE_ARI_CAP_MFVC_FUNC_GRP 0x01
+#define PCIE_ARI_CAP_ASC_FUNC_GRP 0x02
+
+#define PCIE_ARI_CAP_NEXT_FUNC_SHIFT 8
+#define PCIE_ARI_CAP_NEXT_FUNC_MASK 0xffff
+
+#define PCIE_ARI_CTRL_MFVC_FUNC_GRP 0x01
+#define PCIE_ARI_CTRL_ASC_FUNC_GRP 0x02
+
+#define PCIE_ARI_CTRL_FUNC_GRP_SHIFT 4
+#define PCIE_ARI_CTRL_FUNC_GRP_MASK 0x7
+
+/*
* PCI-E Common TLP Header Fields
*/
#define PCIE_TLP_FMT_3DW 0x00
@@ -595,6 +669,7 @@ typedef uint16_t pcie_req_id_t;
#define PCIE_REQ_ID_DEV_MASK 0x00F8
#define PCIE_REQ_ID_FUNC_SHIFT 0
#define PCIE_REQ_ID_FUNC_MASK 0x0007
+#define PCIE_REQ_ID_ARI_FUNC_MASK 0x00FF
#define PCIE_CPL_STS_SUCCESS 0
#define PCIE_CPL_STS_UR 1
diff --git a/usr/src/uts/common/sys/pcie_impl.h b/usr/src/uts/common/sys/pcie_impl.h
index 1e88407b1c..0943da2c92 100644
--- a/usr/src/uts/common/sys/pcie_impl.h
+++ b/usr/src/uts/common/sys/pcie_impl.h
@@ -65,6 +65,14 @@ extern "C" {
#define PCIE_HAS_AER(bus_p) (bus_p->bus_aer_off)
/* IS_ROOT = is RC or RP */
#define PCIE_IS_ROOT(bus_p) (PCIE_IS_RC(bus_p) || PCIE_IS_RP(bus_p))
+
+#define PCIE_IS_HOTPLUG_CAPABLE(dip) \
+ (PCIE_DIP2BUS(dip)->bus_hp_sup_modes)
+
+#define PCIE_IS_HOTPLUG_ENABLED(dip) \
+ ((PCIE_DIP2BUS(dip)->bus_hp_curr_mode == PCIE_PCI_HP_MODE) || \
+ (PCIE_DIP2BUS(dip)->bus_hp_curr_mode == PCIE_NATIVE_HP_MODE))
+
/*
* This is a pseudo pcie "device type", but it's needed to explain describe
* nodes such as PX and NPE, which aren't really PCI devices but do control or
@@ -154,6 +162,14 @@ extern "C" {
#define PFD_IS_RC(pfd_p) PCIE_IS_RC(PCIE_PFD2BUS(pfd_p))
#define PFD_IS_RP(pfd_p) PCIE_IS_RP(PCIE_PFD2BUS(pfd_p))
+/* bus_hp_mode field */
+typedef enum {
+ PCIE_NONE_HP_MODE = 0x0,
+ PCIE_ACPI_HP_MODE = 0x1,
+ PCIE_PCI_HP_MODE = 0x2,
+ PCIE_NATIVE_HP_MODE = 0x4
+} pcie_hp_mode_t;
+
typedef struct pf_pci_bdg_err_regs {
uint16_t pci_bdg_sec_stat; /* PCI secondary status reg */
uint16_t pci_bdg_ctrl; /* PCI bridge control reg */
@@ -244,8 +260,9 @@ typedef struct pcie_bus {
/* Needed for PCI/PCIe fabric error handling */
dev_info_t *bus_dip;
dev_info_t *bus_rp_dip;
- ddi_acc_handle_t bus_cfg_hdl; /* error handling acc handle */
+ ddi_acc_handle_t bus_cfg_hdl; /* error handling acc hdle */
uint_t bus_fm_flags;
+ uint_t bus_soft_state;
/* Static PCI/PCIe information */
pcie_req_id_t bus_bdf;
@@ -258,6 +275,7 @@ typedef struct pcie_bus {
uint16_t bus_pcie_off; /* PCIe Capability Offset */
uint16_t bus_aer_off; /* PCIe Advanced Error Offset */
uint16_t bus_pcix_off; /* PCIx Capability Offset */
+ uint16_t bus_pci_hp_off; /* PCI HP (SHPC) Cap Offset */
uint16_t bus_ecc_ver; /* PCIX ecc version */
pci_bus_range_t bus_bus_range; /* pci bus-range property */
ppb_ranges_t *bus_addr_ranges; /* pci range property */
@@ -271,6 +289,11 @@ typedef struct pcie_bus {
int bus_mps; /* Maximum Payload Size */
void *bus_plat_private; /* Platform specific */
+ /* Hotplug specific fields */
+ pcie_hp_mode_t bus_hp_sup_modes; /* HP modes supported */
+ pcie_hp_mode_t bus_hp_curr_mode; /* HP mode used */
+ void *bus_hp_ctrl; /* HP bus ctrl data */
+ int bus_ari; /* ARI device */
} pcie_bus_t;
struct pf_data {
@@ -355,8 +378,28 @@ typedef struct {
int highest_common_mps;
} pcie_max_supported_t;
+/*
+ * Default interrupt priority for all PCI and PCIe nexus drivers including
+ * hotplug interrupts.
+ */
+#define PCIE_INTR_PRI (LOCK_LEVEL - 1)
+
+/*
+ * XXX - PCIE_IS_PCIE check is required in order not to invoke these macros
+ * for non-standard PCI or PCI Express Hotplug Controllers.
+ */
+#define PCIE_ENABLE_ERRORS(dip) \
+ if (PCIE_IS_PCIE(PCIE_DIP2BUS(dip))) { \
+ pcie_enable_errors(dip); \
+ (void) pcie_enable_ce(dip); \
+ }
+
+#define PCIE_DISABLE_ERRORS(dip) \
+ if (PCIE_IS_PCIE(PCIE_DIP2BUS(dip))) { \
+ pcie_disable_errors(dip); \
+ }
+
#ifdef DEBUG
-extern uint_t pcie_debug_flags;
#define PCIE_DBG pcie_dbg
/* Common Debugging shortcuts */
#define PCIE_DBG_CFG(dip, bus_p, name, sz, off, org) \
@@ -372,18 +415,29 @@ extern uint_t pcie_debug_flags;
ddi_get_instance(dip), bus_p->bus_bdf, name, off, org, \
PCIE_AER_GET(sz, bus_p, off))
-extern void pcie_dbg(char *fmt, ...);
-
#else /* DEBUG */
#define PCIE_DBG_CFG 0 &&
#define PCIE_DBG 0 &&
+#define PCIE_ARI_DBG 0 &&
#define PCIE_DBG_CAP 0 &&
#define PCIE_DBG_AER 0 &&
#endif /* DEBUG */
/* PCIe Friendly Functions */
+extern int pcie_init(dev_info_t *dip, caddr_t arg);
+extern int pcie_uninit(dev_info_t *dip);
+extern int pcie_intr(dev_info_t *dip);
+extern int pcie_open(dev_info_t *dip, dev_t *devp, int flags, int otyp,
+ cred_t *credp);
+extern int pcie_close(dev_info_t *dip, dev_t dev, int flags, int otyp,
+ cred_t *credp);
+extern int pcie_ioctl(dev_info_t *dip, dev_t dev, int cmd, intptr_t arg,
+ int mode, cred_t *credp, int *rvalp);
+extern int pcie_prop_op(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op,
+ int flags, char *name, caddr_t valuep, int *lengthp);
+
extern void pcie_init_root_port_mps(dev_info_t *dip);
extern int pcie_initchild(dev_info_t *dip);
extern void pcie_uninitchild(dev_info_t *dip);
@@ -422,6 +476,26 @@ extern void pcie_set_aer_suce_mask(uint32_t mask);
extern void pcie_set_serr_mask(uint32_t mask);
extern void pcie_init_plat(dev_info_t *dip);
extern void pcie_fini_plat(dev_info_t *dip);
+extern int pcie_read_only_probe(dev_info_t *, char *, dev_info_t **);
+extern dev_info_t *pcie_func_to_dip(dev_info_t *dip, pcie_req_id_t function);
+extern int pcie_ari_disable(dev_info_t *dip);
+extern int pcie_ari_enable(dev_info_t *dip);
+
+#define PCIE_ARI_FORW_NOT_SUPPORTED 0
+#define PCIE_ARI_FORW_SUPPORTED 1
+
+extern int pcie_ari_supported(dev_info_t *dip);
+
+#define PCIE_ARI_FORW_DISABLED 0
+#define PCIE_ARI_FORW_ENABLED 1
+
+extern int pcie_ari_is_enabled(dev_info_t *dip);
+
+#define PCIE_NOT_ARI_DEVICE 0
+#define PCIE_ARI_DEVICE 1
+
+extern int pcie_ari_device(dev_info_t *dip);
+extern int pcie_ari_get_next_function(dev_info_t *dip, int *func);
/* PCIe error handling functions */
extern int pf_scan_fabric(dev_info_t *rpdip, ddi_fm_error_t *derr,
@@ -432,6 +506,11 @@ extern int pf_hdl_lookup(dev_info_t *, uint64_t, uint32_t, uint64_t,
pcie_req_id_t);
extern int pf_tlp_decode(pcie_bus_t *, pf_pcie_adv_err_regs_t *);
+#ifdef DEBUG
+extern uint_t pcie_debug_flags;
+extern void pcie_dbg(char *fmt, ...);
+#endif /* DEBUG */
+
#ifdef __cplusplus
}
#endif
diff --git a/usr/src/uts/common/sys/sunndi.h b/usr/src/uts/common/sys/sunndi.h
index 22e61db408..f17ab18a7c 100644
--- a/usr/src/uts/common/sys/sunndi.h
+++ b/usr/src/uts/common/sys/sunndi.h
@@ -42,11 +42,15 @@ extern "C" {
#define NDI_SUCCESS DDI_SUCCESS /* successful return */
#define NDI_FAILURE DDI_FAILURE /* unsuccessful return */
-#define NDI_NOMEM -2 /* failed to allocate resources */
-#define NDI_BADHANDLE -3 /* bad handle passed to in function */
-#define NDI_FAULT -4 /* fault during copyin/copyout */
-#define NDI_BUSY -5 /* device busy - could not offline */
-#define NDI_UNBOUND -6 /* device not bound to a driver */
+#define NDI_NOMEM -2 /* failed to allocate resources */
+#define NDI_BADHANDLE -3 /* bad handle passed to in function */
+#define NDI_FAULT -4 /* fault during copyin/copyout */
+#define NDI_BUSY -5 /* device busy - could not offline */
+#define NDI_UNBOUND -6 /* device not bound to a driver */
+#define NDI_EINVAL -7 /* invalid request or arguments */
+#define NDI_ENOTSUP -8 /* operation or event not supported */
+#define NDI_CLAIMED NDI_SUCCESS /* event is claimed */
+#define NDI_UNCLAIMED -9 /* event is not claimed */
/*
* Property functions: See also, ddipropdefs.h.
@@ -636,6 +640,23 @@ int
ndi_busop_bus_unconfig(dev_info_t *dip, uint_t flags, ddi_bus_config_op_t op,
void *arg);
+/*
+ * Called by the Nexus/HPC drivers to register, unregister and interact
+ * with the hotplug framework for the specified hotplug connection.
+ */
+int
+ndi_hp_register(dev_info_t *dip, ddi_hp_cn_info_t *info_p);
+
+int
+ndi_hp_unregister(dev_info_t *dip, char *cn_name);
+
+int
+ndi_hp_state_change_req(dev_info_t *dip, char *cn_name,
+ ddi_hp_cn_state_t state, uint_t flag);
+
+void
+ndi_hp_walk_cn(dev_info_t *dip, int (*f)(ddi_hp_cn_info_t *, void *),
+ void *arg);
/*
* Bus Resource allocation structures and function prototypes exported
@@ -705,12 +726,9 @@ typedef struct ndi_ra_request {
#define NDI_RA_TYPE_PCI_PREFETCH_MEM "pci_prefetchable_memory"
#define NDI_RA_TYPE_INTR "interrupt"
-
-
/* flag bit definition */
#define NDI_RA_PASS 0x0001 /* pass request up the dev tree */
-
/*
* Prototype definitions for functions exported
*/
@@ -729,7 +747,6 @@ int
ndi_ra_free(dev_info_t *dip, uint64_t base, uint64_t len, char *type,
uint_t flag);
-
/*
* ndi_dev_is_prom_node: Return non-zero if the node is a prom node
*/
diff --git a/usr/src/uts/i86pc/Makefile.files b/usr/src/uts/i86pc/Makefile.files
index 1b52b47f93..0aa207afc3 100644
--- a/usr/src/uts/i86pc/Makefile.files
+++ b/usr/src/uts/i86pc/Makefile.files
@@ -177,7 +177,7 @@ GFX_PRIVATE_OBJS += gfx_private.o gfxp_pci.o gfxp_segmap.o \
FIPE_OBJS += fipe_drv.o fipe_pm.o
IOAT_OBJS += ioat.o ioat_rs.o ioat_ioctl.o ioat_chan.o
ISANEXUS_OBJS += isa.o dma_engine.o i8237A.o
-PCIE_MISC_OBJS += pcie_acpi.o pcie_x86.o
+PCIE_MISC_OBJS += pcie_acpi.o pciehpc_acpi.o pcie_x86.o
PCI_E_NEXUS_OBJS += npe.o npe_misc.o
PCI_E_NEXUS_OBJS += pci_common.o pci_kstats.o pci_tools.o
PCINEXUS_OBJS += pci.o pci_common.o pci_kstats.o pci_tools.o
diff --git a/usr/src/uts/i86pc/Makefile.rules b/usr/src/uts/i86pc/Makefile.rules
index d17ff166bd..7622dbf21f 100644
--- a/usr/src/uts/i86pc/Makefile.rules
+++ b/usr/src/uts/i86pc/Makefile.rules
@@ -95,6 +95,10 @@ $(OBJS_DIR)/%.o: $(UTSBASE)/i86pc/io/pciex/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
+$(OBJS_DIR)/%.o: $(UTSBASE)/intel/io/pciex/hotplug/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
$(OBJS_DIR)/%.o: $(UTSBASE)/i86pc/io/pcplusmp/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
@@ -311,6 +315,9 @@ $(LINTS_DIR)/%.ln: $(UTSBASE)/i86pc/io/pci/%.c
$(LINTS_DIR)/%.ln: $(UTSBASE)/i86pc/io/pciex/%.c
@($(LHEAD) $(LINT.c) $< $(LTAIL))
+$(LINTS_DIR)/%.ln: $(UTSBASE)/intel/io/pciex/hotplug/%.c
+ @($(LHEAD) $(LINT.c) $< $(LTAIL))
+
$(LINTS_DIR)/%.ln: $(UTSBASE)/i86pc/io/pcplusmp/%.c
@($(LHEAD) $(LINT.c) $< $(LTAIL))
diff --git a/usr/src/uts/i86pc/io/pci/pci.c b/usr/src/uts/i86pc/io/pci/pci.c
index 0cacdac6b4..a030965f62 100644
--- a/usr/src/uts/i86pc/io/pci/pci.c
+++ b/usr/src/uts/i86pc/io/pci/pci.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.
*/
@@ -163,9 +163,9 @@ static int pci_initchild(dev_info_t *child);
*/
static struct modldrv modldrv = {
- &mod_driverops, /* Type of module */
- "host to PCI nexus driver",
- &pci_ops, /* driver ops */
+ &mod_driverops, /* Type of module */
+ "x86 Host to PCI nexus driver", /* Name of module */
+ &pci_ops, /* driver ops */
};
static struct modlinkage modlinkage = {
@@ -247,6 +247,7 @@ pci_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
}
pcip->pci_dip = devi;
+ pcip->pci_soft_state = PCI_SOFT_STATE_CLOSED;
/*
* Initialize hotplug support on this bus. At minimum
@@ -267,6 +268,7 @@ pci_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
pcip->pci_fmcap = DDI_FM_ERRCB_CAPABLE |
DDI_FM_ACCCHK_CAPABLE | DDI_FM_DMACHK_CAPABLE;
ddi_fm_init(devi, &pcip->pci_fmcap, &pcip->pci_fm_ibc);
+ mutex_init(&pcip->pci_mutex, NULL, MUTEX_DRIVER, NULL);
mutex_init(&pcip->pci_err_mutex, NULL, MUTEX_DRIVER,
(void *)pcip->pci_fm_ibc);
mutex_init(&pcip->pci_peek_poke_mutex, NULL, MUTEX_DRIVER,
@@ -306,6 +308,7 @@ pci_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
}
mutex_destroy(&pcip->pci_peek_poke_mutex);
mutex_destroy(&pcip->pci_err_mutex);
+ mutex_destroy(&pcip->pci_mutex);
ddi_fm_fini(devi); /* Uninitialize pcitool support. */
pcitool_uninit(devi);
@@ -768,14 +771,28 @@ static int
pci_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp)
{
minor_t minor = getminor(dev);
- int instance = PCIHP_AP_MINOR_NUM_TO_INSTANCE(minor);
+ int instance = PCI_MINOR_NUM_TO_INSTANCE(minor);
pci_state_t *pci_p = ddi_get_soft_state(pci_statep, instance);
+ int ret = ENOTTY;
if (pci_p == NULL)
return (ENXIO);
- return (pci_common_ioctl(pci_p->pci_dip,
- dev, cmd, arg, mode, credp, rvalp));
+ switch (PCI_MINOR_NUM_TO_PCI_DEVNUM(minor)) {
+ case PCI_TOOL_REG_MINOR_NUM:
+ case PCI_TOOL_INTR_MINOR_NUM:
+ /* To handle pcitool related ioctls */
+ ret = pci_common_ioctl(pci_p->pci_dip, dev, cmd, arg, mode,
+ credp, rvalp);
+ break;
+ default:
+ /* To handle devctl and hotplug related ioctls */
+ ret = (pcihp_get_cb_ops())->cb_ioctl(dev, cmd, arg, mode,
+ credp, rvalp);
+ break;
+ }
+
+ return (ret);
}
diff --git a/usr/src/uts/i86pc/io/pci/pci_common.c b/usr/src/uts/i86pc/io/pci/pci_common.c
index 0c90537424..03840580a3 100644
--- a/usr/src/uts/i86pc/io/pci/pci_common.c
+++ b/usr/src/uts/i86pc/io/pci/pci_common.c
@@ -37,7 +37,6 @@
#include <sys/pci.h>
#include <sys/sunndi.h>
#include <sys/mach_intr.h>
-#include <sys/hotplug/pci/pcihp.h>
#include <sys/pci_intr_lib.h>
#include <sys/psm.h>
#include <sys/policy.h>
@@ -904,18 +903,18 @@ pci_common_get_reg_prop(dev_info_t *dip, pci_regspec_t *pci_rp)
/*
- * For pci_tools
+ * To handle PCI tool ioctls
*/
+/*ARGSUSED*/
int
pci_common_ioctl(dev_info_t *dip, dev_t dev, int cmd, intptr_t arg,
int mode, cred_t *credp, int *rvalp)
{
- int rv = ENOTTY;
-
- minor_t minor = getminor(dev);
+ minor_t minor = getminor(dev);
+ int rv = ENOTTY;
- switch (PCIHP_AP_MINOR_NUM_TO_PCI_DEVNUM(minor)) {
+ switch (PCI_MINOR_NUM_TO_PCI_DEVNUM(minor)) {
case PCI_TOOL_REG_MINOR_NUM:
switch (cmd) {
@@ -963,15 +962,7 @@ pci_common_ioctl(dev_info_t *dip, dev_t dev, int cmd, intptr_t arg,
}
break;
- /*
- * All non-PCItool ioctls go through here, including:
- * devctl ioctls with minor number PCIHP_DEVCTL_MINOR and
- * those for attachment points with where minor number is the
- * device number.
- */
default:
- rv = (pcihp_get_cb_ops())->cb_ioctl(dev, cmd, arg, mode,
- credp, rvalp);
break;
}
diff --git a/usr/src/uts/i86pc/io/pci/pci_common.h b/usr/src/uts/i86pc/io/pci/pci_common.h
index c5c05d4612..63fe4bb165 100644
--- a/usr/src/uts/i86pc/io/pci/pci_common.h
+++ b/usr/src/uts/i86pc/io/pci/pci_common.h
@@ -20,7 +20,7 @@
*/
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -40,7 +40,9 @@ extern "C" {
typedef struct pci_state {
dev_info_t *pci_dip;
int pci_fmcap;
+ uint_t pci_soft_state;
ddi_iblock_cookie_t pci_fm_ibc;
+ kmutex_t pci_mutex;
kmutex_t pci_peek_poke_mutex;
kmutex_t pci_err_mutex;
} pci_state_t;
diff --git a/usr/src/uts/i86pc/io/pci/pci_tools.c b/usr/src/uts/i86pc/io/pci/pci_tools.c
index c164037e2f..deb4fbb420 100644
--- a/usr/src/uts/i86pc/io/pci/pci_tools.c
+++ b/usr/src/uts/i86pc/io/pci/pci_tools.c
@@ -33,12 +33,12 @@
#include <sys/ontrap.h>
#include <sys/psm.h>
#include <sys/pcie.h>
-#include <sys/hotplug/pci/pcihp.h>
#include <sys/pci_cfgspace.h>
#include <sys/pci_tools.h>
#include <io/pci/pci_tools_ext.h>
#include <sys/apic.h>
#include <io/pci/pci_var.h>
+#include <sys/pci_impl.h>
#include <sys/promif.h>
#include <sys/x86_archext.h>
#include <sys/cpuvar.h>
@@ -98,13 +98,13 @@ pcitool_init(dev_info_t *dip, boolean_t is_pciex)
/* Create pcitool nodes for register access and interrupt routing. */
if (ddi_create_minor_node(dip, PCI_MINOR_REG, S_IFCHR,
- PCIHP_AP_MINOR_NUM(instance, PCI_TOOL_REG_MINOR_NUM),
+ PCI_MINOR_NUM(instance, PCI_TOOL_REG_MINOR_NUM),
DDI_NT_REGACC, 0) != DDI_SUCCESS) {
return (DDI_FAILURE);
}
if (ddi_create_minor_node(dip, PCI_MINOR_INTR, S_IFCHR,
- PCIHP_AP_MINOR_NUM(instance, PCI_TOOL_INTR_MINOR_NUM),
+ PCI_MINOR_NUM(instance, PCI_TOOL_INTR_MINOR_NUM),
DDI_NT_INTRCTL, 0) != DDI_SUCCESS) {
ddi_remove_minor_node(dip, PCI_MINOR_REG);
return (DDI_FAILURE);
diff --git a/usr/src/uts/i86pc/io/pci/pci_tools_ext.h b/usr/src/uts/i86pc/io/pci/pci_tools_ext.h
index c2afdc2f83..eaaacb665a 100644
--- a/usr/src/uts/i86pc/io/pci/pci_tools_ext.h
+++ b/usr/src/uts/i86pc/io/pci/pci_tools_ext.h
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -20,15 +19,13 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#ifndef _PCI_TOOLS_EXT_H
#define _PCI_TOOLS_EXT_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#ifdef __cplusplus
extern "C" {
#endif
@@ -36,13 +33,6 @@ extern "C" {
/* This file contains pcitool defs exported to other modules of a PCI driver. */
/*
- * Minor numbers for dedicated pcitool nodes.
- * Note that FF and FE minor numbers are used for other minor nodes.
- */
-#define PCI_TOOL_REG_MINOR_NUM 0xFD
-#define PCI_TOOL_INTR_MINOR_NUM 0xFC
-
-/*
* Functions exported from the pci_tools.c module.
*/
extern int pcitool_dev_reg_ops(dev_info_t *dip, void *arg, int cmd, int mode);
diff --git a/usr/src/uts/i86pc/io/pciex/inc.flg b/usr/src/uts/i86pc/io/pciex/inc.flg
index 42c94632c8..150d457bf0 100644
--- a/usr/src/uts/i86pc/io/pciex/inc.flg
+++ b/usr/src/uts/i86pc/io/pciex/inc.flg
@@ -57,7 +57,6 @@ find_files "s.*" \
usr/src/uts/i86xpv/npe \
usr/src/uts/intel/pci_autoconfig \
usr/src/uts/intel/pcieb \
- usr/src/uts/intel/pciehpc \
usr/src/uts/intel/pcicfg
# packaging files
diff --git a/usr/src/uts/i86pc/io/pciex/npe.c b/usr/src/uts/i86pc/io/pciex/npe.c
index 81485cf798..23b02640f5 100644
--- a/usr/src/uts/i86pc/io/pciex/npe.c
+++ b/usr/src/uts/i86pc/io/pciex/npe.c
@@ -30,6 +30,7 @@
#include <sys/conf.h>
#include <sys/modctl.h>
+#include <sys/file.h>
#include <sys/pci_impl.h>
#include <sys/pcie_impl.h>
#include <sys/sysmacros.h>
@@ -39,7 +40,7 @@
#include <sys/ddifm.h>
#include <sys/ndifm.h>
#include <sys/fm/util.h>
-#include <sys/hotplug/pci/pcihp.h>
+#include <sys/hotplug/pci/pcie_hp.h>
#include <io/pci/pci_tools_ext.h>
#include <io/pci/pci_common.h>
#include <io/pciex/pcie_nvidia.h>
@@ -96,33 +97,25 @@ struct bus_ops npe_bus_ops = {
ddi_dma_mctl,
npe_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)(); */
- npe_fm_init, /* (*bus_fm_init)(); */
- NULL, /* (*bus_fm_fini)(); */
- NULL, /* (*bus_fm_access_enter)(); */
- NULL, /* (*bus_fm_access_exit)(); */
- NULL, /* (*bus_power)(); */
- npe_intr_ops /* (*bus_intr_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)(); */
+ npe_fm_init, /* (*bus_fm_init)(); */
+ NULL, /* (*bus_fm_fini)(); */
+ NULL, /* (*bus_fm_access_enter)(); */
+ NULL, /* (*bus_fm_access_exit)(); */
+ NULL, /* (*bus_power)(); */
+ npe_intr_ops, /* (*bus_intr_op)(); */
+ pcie_hp_common_ops /* (*bus_hp_op)(); */
};
-/*
- * One 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, piggybacking
- * anything else needed by the pci_tools.c module. Only pci_tools and pcihp
- * will be using the PCI devctl node.
- */
static int npe_open(dev_t *, int, int, cred_t *);
static int npe_close(dev_t, int, int, cred_t *);
static int npe_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
-static int npe_prop_op(dev_t, dev_info_t *, ddi_prop_op_t, int, char *,
- caddr_t, int *);
-static int npe_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
struct cb_ops npe_cb_ops = {
npe_open, /* open */
@@ -137,7 +130,7 @@ struct cb_ops npe_cb_ops = {
nodev, /* mmap */
nodev, /* segmap */
nochpoll, /* poll */
- npe_prop_op, /* cb_prop_op */
+ pcie_prop_op, /* cb_prop_op */
NULL, /* streamtab */
D_NEW | D_MP | D_HOTPLUG, /* Driver compatibility flag */
CB_REV, /* rev */
@@ -151,6 +144,7 @@ struct cb_ops npe_cb_ops = {
*/
static int npe_attach(dev_info_t *devi, ddi_attach_cmd_t cmd);
static int npe_detach(dev_info_t *devi, ddi_detach_cmd_t cmd);
+static int npe_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
struct dev_ops npe_ops = {
DEVO_REV, /* devo_rev */
@@ -190,9 +184,9 @@ extern int npe_restore_htconfig_children(dev_info_t *dip);
* Module linkage information for the kernel.
*/
static struct modldrv modldrv = {
- &mod_driverops, /* Type of module */
- "Host to PCIe nexus driver",
- &npe_ops, /* driver ops */
+ &mod_driverops, /* Type of module */
+ "Host to PCIe nexus driver", /* Name of module */
+ &npe_ops, /* driver ops */
};
static struct modlinkage modlinkage = {
@@ -245,14 +239,39 @@ _info(struct modinfo *modinfop)
/*ARGSUSED*/
static int
+npe_info(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
+{
+ minor_t minor = getminor((dev_t)arg);
+ int instance = PCI_MINOR_NUM_TO_INSTANCE(minor);
+ pci_state_t *pcip = ddi_get_soft_state(npe_statep, instance);
+ int ret = DDI_SUCCESS;
+
+ switch (cmd) {
+ case DDI_INFO_DEVT2INSTANCE:
+ *result = (void *)(intptr_t)instance;
+ break;
+ case DDI_INFO_DEVT2DEVINFO:
+ if (pcip == NULL) {
+ ret = DDI_FAILURE;
+ break;
+ }
+
+ *result = (void *)pcip->pci_dip;
+ break;
+ default:
+ ret = DDI_FAILURE;
+ break;
+ }
+
+ return (ret);
+}
+
+/*ARGSUSED*/
+static int
npe_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
{
- /*
- * Use the minor number as constructed by pcihp, as the index value to
- * ddi_soft_state_zalloc.
- */
- int instance = ddi_get_instance(devi);
- pci_state_t *pcip = NULL;
+ int instance = ddi_get_instance(devi);
+ pci_state_t *pcip = NULL;
if (cmd == DDI_RESUME) {
/*
@@ -287,27 +306,16 @@ npe_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
return (DDI_FAILURE);
pcip->pci_dip = devi;
+ pcip->pci_soft_state = PCI_SOFT_STATE_CLOSED;
pcie_rc_init_bus(devi);
- /*
- * 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, "npe: Failed to setup hotplug framework");
- ddi_soft_state_free(npe_statep, instance);
- return (DDI_FAILURE);
- }
+ if (pcie_init(devi, NULL) != DDI_SUCCESS)
+ goto fail1;
/* Second arg: initialize for pci_express root nexus */
- if (pcitool_init(devi, B_TRUE) != DDI_SUCCESS) {
- (void) pcihp_uninit(devi);
- ddi_soft_state_free(npe_statep, instance);
- return (DDI_FAILURE);
- }
+ if (pcitool_init(devi, B_TRUE) != DDI_SUCCESS)
+ goto fail2;
pcip->pci_fmcap = DDI_FM_EREPORT_CAPABLE | DDI_FM_ERRCB_CAPABLE |
DDI_FM_ACCCHK_CAPABLE | DDI_FM_DMACHK_CAPABLE;
@@ -322,8 +330,16 @@ npe_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
npe_query_acpi_mcfg(devi);
ddi_report_dev(devi);
+
return (DDI_SUCCESS);
+fail2:
+ (void) pcie_uninit(devi);
+fail1:
+ pcie_rc_fini_bus(devi);
+ ddi_soft_state_free(npe_statep, instance);
+
+ return (DDI_FAILURE);
}
/*ARGSUSED*/
@@ -341,10 +357,8 @@ npe_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
/* Uninitialize pcitool support. */
pcitool_uninit(devi);
- /*
- * Uninitialize hotplug support on this bus.
- */
- (void) pcihp_uninit(devi);
+ if (pcie_uninit(devi) != DDI_SUCCESS)
+ return (DDI_FAILURE);
if (pcip->pci_fmcap & DDI_FM_ERRCB_CAPABLE)
ddi_fm_handler_unregister(devi);
@@ -961,53 +975,112 @@ npe_removechild(dev_info_t *dip)
return (DDI_SUCCESS);
}
-
-/*
- * When retrofitting this module for pci_tools, functions such as open, close,
- * and ioctl are now pulled into this module. Before this, the functions in
- * the pcihp module were referenced directly. Now they are called or
- * referenced through the pcihp cb_ops structure from functions in this module.
- */
static int
npe_open(dev_t *devp, int flags, int otyp, cred_t *credp)
{
- return ((pcihp_get_cb_ops())->cb_open(devp, flags, otyp, credp));
-}
+ minor_t minor = getminor(*devp);
+ int instance = PCI_MINOR_NUM_TO_INSTANCE(minor);
+ pci_state_t *pci_p = ddi_get_soft_state(npe_statep, instance);
+ int rv;
-static int
-npe_close(dev_t dev, int flags, int otyp, cred_t *credp)
-{
- return ((pcihp_get_cb_ops())->cb_close(dev, flags, otyp, credp));
+ /*
+ * Make sure the open is for the right file type.
+ */
+ if (otyp != OTYP_CHR)
+ return (EINVAL);
+
+ if (pci_p == NULL)
+ return (ENXIO);
+
+ mutex_enter(&pci_p->pci_mutex);
+ switch (PCI_MINOR_NUM_TO_PCI_DEVNUM(minor)) {
+ case PCI_TOOL_REG_MINOR_NUM:
+ case PCI_TOOL_INTR_MINOR_NUM:
+ break;
+ default:
+ /* Handle devctl ioctls */
+ rv = pcie_open(pci_p->pci_dip, devp, flags, otyp, credp);
+ mutex_exit(&pci_p->pci_mutex);
+ return (rv);
+ }
+
+ /* Handle pcitool ioctls */
+ if (flags & FEXCL) {
+ if (pci_p->pci_soft_state != PCI_SOFT_STATE_CLOSED) {
+ mutex_exit(&pci_p->pci_mutex);
+ cmn_err(CE_NOTE, "npe_open: busy");
+ return (EBUSY);
+ }
+ pci_p->pci_soft_state = PCI_SOFT_STATE_OPEN_EXCL;
+ } else {
+ if (pci_p->pci_soft_state == PCI_SOFT_STATE_OPEN_EXCL) {
+ mutex_exit(&pci_p->pci_mutex);
+ cmn_err(CE_NOTE, "npe_open: busy");
+ return (EBUSY);
+ }
+ pci_p->pci_soft_state = PCI_SOFT_STATE_OPEN;
+ }
+ mutex_exit(&pci_p->pci_mutex);
+
+ return (0);
}
static int
-npe_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp)
+npe_close(dev_t dev, int flags, int otyp, cred_t *credp)
{
minor_t minor = getminor(dev);
- int instance = PCIHP_AP_MINOR_NUM_TO_INSTANCE(minor);
+ int instance = PCI_MINOR_NUM_TO_INSTANCE(minor);
pci_state_t *pci_p = ddi_get_soft_state(npe_statep, instance);
- dev_info_t *dip;
+ int rv;
if (pci_p == NULL)
return (ENXIO);
- dip = pci_p->pci_dip;
+ mutex_enter(&pci_p->pci_mutex);
- return (pci_common_ioctl(dip, dev, cmd, arg, mode, credp, rvalp));
-}
+ switch (PCI_MINOR_NUM_TO_PCI_DEVNUM(minor)) {
+ case PCI_TOOL_REG_MINOR_NUM:
+ case PCI_TOOL_INTR_MINOR_NUM:
+ break;
+ default:
+ /* Handle devctl ioctls */
+ rv = pcie_close(pci_p->pci_dip, dev, flags, otyp, credp);
+ mutex_exit(&pci_p->pci_mutex);
+ return (rv);
+ }
-static int
-npe_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));
+ /* Handle pcitool ioctls */
+ pci_p->pci_soft_state = PCI_SOFT_STATE_CLOSED;
+ mutex_exit(&pci_p->pci_mutex);
+ return (0);
}
static int
-npe_info(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
+npe_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp)
{
- return (pcihp_info(dip, cmd, arg, result));
+ minor_t minor = getminor(dev);
+ int instance = PCI_MINOR_NUM_TO_INSTANCE(minor);
+ pci_state_t *pci_p = ddi_get_soft_state(npe_statep, instance);
+ int ret = ENOTTY;
+
+ if (pci_p == NULL)
+ return (ENXIO);
+
+ switch (PCI_MINOR_NUM_TO_PCI_DEVNUM(minor)) {
+ case PCI_TOOL_REG_MINOR_NUM:
+ case PCI_TOOL_INTR_MINOR_NUM:
+ /* To handle pcitool related ioctls */
+ ret = pci_common_ioctl(pci_p->pci_dip, dev, cmd, arg, mode,
+ credp, rvalp);
+ break;
+ default:
+ /* To handle devctl and hotplug related ioctls */
+ ret = pcie_ioctl(pci_p->pci_dip, dev, cmd, arg, mode, credp,
+ rvalp);
+ break;
+ }
+
+ return (ret);
}
/*ARGSUSED*/
diff --git a/usr/src/uts/i86pc/npe/Makefile b/usr/src/uts/i86pc/npe/Makefile
index ba35955760..4d28a86d31 100644
--- a/usr/src/uts/i86pc/npe/Makefile
+++ b/usr/src/uts/i86pc/npe/Makefile
@@ -21,11 +21,9 @@
#
# uts/i86pc/npe/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 PCI-E nexus driver
#
# i86pc implementation architecture dependent
@@ -57,16 +55,13 @@ LINT_TARGET = $(MODULE).lint
INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
#
-# depends on misc/pcihp, misc/acpica and misc/pcie
-#
-# For PCI Hotplug support, the misc/pcihp module provides devctl control
-# device and cb_ops functions to support hotplug operations.
+# depends on misc/acpica and misc/pcie
#
# acpica supplies ACPI access routines
#
# pcie supplies PCI Express fabric error support
#
-LDFLAGS += -dy -Nmisc/pcihp -Nmisc/acpica -Nmisc/pcie
+LDFLAGS += -dy -Nmisc/acpica -Nmisc/pcie
#
# Name of the module is needed by the source, to distinguish from other
diff --git a/usr/src/uts/i86pc/pcie/Makefile b/usr/src/uts/i86pc/pcie/Makefile
index 2fb938d98f..339a004139 100644
--- a/usr/src/uts/i86pc/pcie/Makefile
+++ b/usr/src/uts/i86pc/pcie/Makefile
@@ -40,8 +40,10 @@ UTSBASE = ../..
# Define the module and object file sets.
#
MODULE = pcie
-OBJECTS = $(PCIE_MISC_OBJS:%=$(OBJS_DIR)/%)
-LINTS = $(PCIE_MISC_OBJS:%.o=$(LINTS_DIR)/%.ln)
+OBJECTS = $(PCIE_MISC_OBJS:%=$(OBJS_DIR)/%) \
+ $(PCI_STRING_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(PCIE_MISC_OBJS:%.o=$(LINTS_DIR)/%.ln) \
+ $(PCI_STRING_OBJS:%.o=$(LINTS_DIR)/%.ln)
ROOTMODULE = $(ROOT_PSM_MISC_DIR)/$(MODULE)
#
@@ -49,7 +51,10 @@ ROOTMODULE = $(ROOT_PSM_MISC_DIR)/$(MODULE)
#
include $(UTSBASE)/i86pc/Makefile.i86pc
-LDFLAGS += -dy -Nmisc/acpica
+#
+# Dependency
+#
+LDFLAGS += -dy -Nmisc/acpica -Nmisc/busra
#
# Define targets
diff --git a/usr/src/uts/i86xpv/Makefile.files b/usr/src/uts/i86xpv/Makefile.files
index ecdb5fa64a..e2d153fac9 100644
--- a/usr/src/uts/i86xpv/Makefile.files
+++ b/usr/src/uts/i86xpv/Makefile.files
@@ -190,7 +190,7 @@ GFX_PRIVATE_OBJS += gfx_private.o gfxp_pci.o gfxp_segmap.o \
gfxp_devmap.o gfxp_vgatext.o gfxp_vm.o vgasubr.o
IOAT_OBJS += ioat.o ioat_rs.o ioat_ioctl.o ioat_chan.o
ISANEXUS_OBJS += isa.o dma_engine.o i8237A.o
-PCIE_MISC_OBJS += pcie_acpi.o pcie_x86.o
+PCIE_MISC_OBJS += pcie_acpi.o pciehpc_acpi.o pcie_x86.o
PCI_E_NEXUS_OBJS += npe.o npe_misc.o
PCI_E_NEXUS_OBJS += pci_common.o pci_kstats.o pci_tools.o
PCINEXUS_OBJS += pci.o pci_common.o pci_kstats.o pci_tools.o
diff --git a/usr/src/uts/i86xpv/Makefile.rules b/usr/src/uts/i86xpv/Makefile.rules
index 2383a9060a..744450684d 100644
--- a/usr/src/uts/i86xpv/Makefile.rules
+++ b/usr/src/uts/i86xpv/Makefile.rules
@@ -20,7 +20,7 @@
#
#
-# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
@@ -71,6 +71,10 @@ $(OBJS_DIR)/%.o: $(UTSBASE)/i86pc/io/pciex/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
+$(OBJS_DIR)/%.o: $(UTSBASE)/intel/io/pciex/hotplug/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
$(OBJS_DIR)/%.o: $(UTSBASE)/i86xpv/io/psm/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
@@ -230,6 +234,9 @@ $(LINTS_DIR)/%.ln: $(UTSBASE)/i86pc/io/pci/%.c
$(LINTS_DIR)/%.ln: $(UTSBASE)/i86pc/io/pciex/%.c
@($(LHEAD) $(LINT.c) $< $(LTAIL))
+$(LINTS_DIR)/%.ln: $(UTSBASE)/intel/io/pciex/hotplug/%.c
+ @($(LHEAD) $(LINT.c) $< $(LTAIL))
+
$(LINTS_DIR)/%.ln: $(UTSBASE)/i86pc/io/gfx_private/%.c
@($(LHEAD) $(LINT.c) $< $(LTAIL))
diff --git a/usr/src/uts/i86xpv/npe/Makefile b/usr/src/uts/i86xpv/npe/Makefile
index aecbdbbcc8..2754f9246b 100644
--- a/usr/src/uts/i86xpv/npe/Makefile
+++ b/usr/src/uts/i86xpv/npe/Makefile
@@ -20,11 +20,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"
-#
# This makefile drives the production of the PCI-E nexus driver
#
# i86xpv implementation architecture dependent
@@ -56,14 +54,13 @@ LINT_TARGET = $(MODULE).lint
INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
#
-# depends on misc/pcihp misc/acpica
-#
-# For PCI Hotplug support, the misc/pcihp module provides devctl control
-# device and cb_ops functions to support hotplug operations.
+# depends on misc/acpica and misc/pcie
#
# acpica supplies ACPI access routines
#
-LDFLAGS += -dy -Nmisc/pcihp -Nmisc/acpica -Nmisc/pcie
+# pcie supplies PCI Express fabric error support
+#
+LDFLAGS += -dy -Nmisc/acpica -Nmisc/pcie
#
# Name of the module is needed by the source, to distinguish from other
diff --git a/usr/src/uts/i86xpv/pcie/Makefile b/usr/src/uts/i86xpv/pcie/Makefile
index 8a868c4c28..2f9ec47b8b 100644
--- a/usr/src/uts/i86xpv/pcie/Makefile
+++ b/usr/src/uts/i86xpv/pcie/Makefile
@@ -39,8 +39,10 @@ UTSBASE = ../..
# Define the module and object file sets.
#
MODULE = pcie
-OBJECTS = $(PCIE_MISC_OBJS:%=$(OBJS_DIR)/%)
-LINTS = $(PCIE_MISC_OBJS:%.o=$(LINTS_DIR)/%.ln)
+OBJECTS = $(PCIE_MISC_OBJS:%=$(OBJS_DIR)/%) \
+ $(PCI_STRING_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(PCIE_MISC_OBJS:%.o=$(LINTS_DIR)/%.ln) \
+ $(PCI_STRING_OBJS:%.o=$(LINTS_DIR)/%.ln)
ROOTMODULE = $(ROOT_PSM_MISC_DIR)/$(MODULE)
#
@@ -48,7 +50,10 @@ ROOTMODULE = $(ROOT_PSM_MISC_DIR)/$(MODULE)
#
include $(UTSBASE)/i86xpv/Makefile.i86xpv
-LDFLAGS += -dy -Nmisc/acpica
+#
+# Dependency
+#
+LDFLAGS += -dy -Nmisc/acpica -Nmisc/busra
#
# Define targets
diff --git a/usr/src/uts/intel/Makefile.files b/usr/src/uts/intel/Makefile.files
index e47a47bf66..6adea5979f 100644
--- a/usr/src/uts/intel/Makefile.files
+++ b/usr/src/uts/intel/Makefile.files
@@ -149,7 +149,6 @@ I915_OBJS += i915_dma.o i915_drv.o i915_irq.o i915_mem.o
NSKERN_OBJS += nsc_asm.o
PCICFG_OBJS += pcicfg.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
diff --git a/usr/src/uts/intel/Makefile.intel.shared b/usr/src/uts/intel/Makefile.intel.shared
index 907375bcf8..6b40f9581f 100644
--- a/usr/src/uts/intel/Makefile.intel.shared
+++ b/usr/src/uts/intel/Makefile.intel.shared
@@ -621,7 +621,6 @@ MISC_KMODS += nfssrv
MISC_KMODS += neti
MISC_KMODS += pci_autoconfig
MISC_KMODS += pcicfg
-MISC_KMODS += pciehpc
MISC_KMODS += pcihp
MISC_KMODS += pcmcia
MISC_KMODS += rpcsec
diff --git a/usr/src/uts/intel/Makefile.rules b/usr/src/uts/intel/Makefile.rules
index a97a348257..c4978092ce 100644
--- a/usr/src/uts/intel/Makefile.rules
+++ b/usr/src/uts/intel/Makefile.rules
@@ -168,10 +168,6 @@ $(OBJS_DIR)/%.o: $(UTSBASE)/intel/io/hotplug/pcicfg/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
-$(OBJS_DIR)/%.o: $(UTSBASE)/intel/io/hotplug/pciehpc/%.c
- $(COMPILE.c) -o $@ $<
- $(CTFCONVERT_O)
-
$(OBJS_DIR)/%.o: $(UTSBASE)/intel/io/intel_nb5000/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
@@ -408,7 +404,7 @@ $(LINTS_DIR)/%.ln: $(UTSBASE)/intel/io/drm/%.c
$(LINTS_DIR)/%.ln: $(UTSBASE)/intel/io/hotplug/pcicfg/%.c
@($(LHEAD) $(LINT.c) $< $(LTAIL))
-$(LINTS_DIR)/%.ln: $(UTSBASE)/intel/io/hotplug/pciehpc/%.c
+$(LINTS_DIR)/%.ln: $(UTSBASE)/intel/io/hotplug/pci/%.c
@($(LHEAD) $(LINT.c) $< $(LTAIL))
$(LINTS_DIR)/%.ln: $(UTSBASE)/intel/io/intel_nb5000/%.c
diff --git a/usr/src/uts/intel/ia32/ml/modstubs.s b/usr/src/uts/intel/ia32/ml/modstubs.s
index ef23459d54..6cd415a78f 100644
--- a/usr/src/uts/intel/ia32/ml/modstubs.s
+++ b/usr/src/uts/intel/ia32/ml/modstubs.s
@@ -1041,14 +1041,12 @@ fcnname/**/_info: \
#endif
/*
- * Stubs for PCIEHPC (pci-ex hot plug support) module (misc/pciehpc).
- */
-#ifndef PCIEHPC_MODULE
- MODULE(pciehpc,misc);
- STUB(pciehpc, pciehpc_init, 0);
- STUB(pciehpc, pciehpc_uninit, 0);
- WSTUB(pciehpc, pciehpc_intr, nomod_zero);
- END_MODULE(pciehpc);
+ * Stubs for pcieb nexus driver.
+ */
+#ifndef PCIEB_MODULE
+ MODULE(pcieb,drv);
+ STUB(pcieb, pcieb_intel_error_workaround, 0);
+ END_MODULE(pcieb);
#endif
#ifndef IWSCN_MODULE
diff --git a/usr/src/uts/intel/io/hotplug/pcicfg/pcicfg.c b/usr/src/uts/intel/io/hotplug/pcicfg/pcicfg.c
index 86ab694cfe..790b2e727e 100644
--- a/usr/src/uts/intel/io/hotplug/pcicfg/pcicfg.c
+++ b/usr/src/uts/intel/io/hotplug/pcicfg/pcicfg.c
@@ -35,6 +35,7 @@
#include <sys/autoconf.h>
#include <sys/hwconf.h>
#include <sys/pcie.h>
+#include <sys/pcie_impl.h>
#include <sys/pci_cap.h>
#include <sys/ddi.h>
#include <sys/sunndi.h>
@@ -49,6 +50,8 @@
static int pcicfg_start_devno = 0; /* for Debug only */
+#define PCICFG_MAX_ARI_FUNCTION 256
+
#define PCICFG_NODEVICE 42
#define PCICFG_NOMEMORY 43
#define PCICFG_NOMULTI 44
@@ -104,7 +107,7 @@ struct pcicfg_phdl {
uint64_t memory_len;
/* prefetchable memory space */
- uint64_t pf_memory_base; /* PF Memory base for this AP */
+ uint64_t pf_memory_base; /* PF Memory base for this Connection */
uint64_t pf_memory_last;
uint64_t pf_memory_len;
@@ -214,9 +217,9 @@ static void debug(char *, uintptr_t, uintptr_t,
*/
static int pcicfg_add_config_reg(dev_info_t *,
- uint_t, uint_t, uint_t);
+ uint_t, uint_t, uint_t);
static int pcicfg_probe_children(dev_info_t *, uint_t, uint_t, uint_t,
- uint_t *);
+ uint_t *, pcicfg_flags_t);
static int pcicfg_match_dev(dev_info_t *, void *);
static dev_info_t *pcicfg_devi_find(dev_info_t *, uint_t, uint_t);
static pcicfg_phdl_t *pcicfg_find_phdl(dev_info_t *);
@@ -225,7 +228,8 @@ static int pcicfg_destroy_phdl(dev_info_t *);
static int pcicfg_sum_resources(dev_info_t *, void *);
static int pcicfg_device_assign(dev_info_t *);
static int pcicfg_bridge_assign(dev_info_t *, void *);
-static int pcicfg_free_resources(dev_info_t *);
+static int pcicfg_device_assign_readonly(dev_info_t *);
+static int pcicfg_free_resources(dev_info_t *, pcicfg_flags_t);
static void pcicfg_setup_bridge(pcicfg_phdl_t *, ddi_acc_handle_t);
static void pcicfg_update_bridge(pcicfg_phdl_t *, ddi_acc_handle_t);
static int pcicfg_update_assigned_prop(dev_info_t *, pci_regspec_t *);
@@ -234,7 +238,7 @@ static void pcicfg_device_off(ddi_acc_handle_t);
static int pcicfg_set_busnode_props(dev_info_t *, uint8_t);
static int pcicfg_free_bridge_resources(dev_info_t *);
static int pcicfg_free_device_resources(dev_info_t *);
-static int pcicfg_teardown_device(dev_info_t *);
+static int pcicfg_teardown_device(dev_info_t *, pcicfg_flags_t);
static void pcicfg_reparent_node(dev_info_t *, dev_info_t *);
static int pcicfg_config_setup(dev_info_t *, ddi_acc_handle_t *);
static void pcicfg_config_teardown(ddi_acc_handle_t *);
@@ -245,7 +249,7 @@ static int pcicfg_update_ranges_prop(dev_info_t *, ppb_ranges_t *);
static int pcicfg_configure_ntbridge(dev_info_t *, uint_t, uint_t);
static uint_t pcicfg_ntbridge_child(dev_info_t *);
static uint_t pcicfg_get_ntbridge_child_range(dev_info_t *, uint64_t *,
- uint64_t *, uint_t);
+ uint64_t *, uint_t);
static int pcicfg_is_ntbridge(dev_info_t *);
static int pcicfg_ntbridge_allocate_resources(dev_info_t *);
static int pcicfg_ntbridge_configure_done(dev_info_t *);
@@ -262,9 +266,15 @@ static int pcicfg_pcie_dev(dev_info_t *, ddi_acc_handle_t);
static int pcicfg_pcie_device_type(dev_info_t *, ddi_acc_handle_t);
static int pcicfg_pcie_port_type(dev_info_t *, ddi_acc_handle_t);
static int pcicfg_probe_bridge(dev_info_t *, ddi_acc_handle_t, uint_t,
- uint_t *);
+ uint_t *);
static int pcicfg_find_resource_end(dev_info_t *, void *);
+static int pcicfg_populate_reg_props(dev_info_t *, ddi_acc_handle_t);
+static int pcicfg_populate_props_from_bar(dev_info_t *, ddi_acc_handle_t);
+static int pcicfg_update_assigned_prop_value(dev_info_t *, uint32_t,
+ uint32_t, uint32_t, uint_t);
+static int pcicfg_ari_configure(dev_info_t *);
+
#ifdef DEBUG
static void pcicfg_dump_common_config(ddi_acc_handle_t config_handle);
static void pcicfg_dump_device_config(ddi_acc_handle_t);
@@ -382,31 +392,31 @@ pcicfg_dump_common_config(ddi_acc_handle_t config_handle)
if ((pcicfg_debug & 1) == 0)
return;
prom_printf(" Vendor ID = [0x%x]\n",
- pci_config_get16(config_handle, PCI_CONF_VENID));
+ pci_config_get16(config_handle, PCI_CONF_VENID));
prom_printf(" Device ID = [0x%x]\n",
- pci_config_get16(config_handle, PCI_CONF_DEVID));
+ pci_config_get16(config_handle, PCI_CONF_DEVID));
prom_printf(" Command REG = [0x%x]\n",
- pci_config_get16(config_handle, PCI_CONF_COMM));
+ pci_config_get16(config_handle, PCI_CONF_COMM));
prom_printf(" Status REG = [0x%x]\n",
- pci_config_get16(config_handle, PCI_CONF_STAT));
+ pci_config_get16(config_handle, PCI_CONF_STAT));
prom_printf(" Revision ID = [0x%x]\n",
- pci_config_get8(config_handle, PCI_CONF_REVID));
+ pci_config_get8(config_handle, PCI_CONF_REVID));
prom_printf(" Prog Class = [0x%x]\n",
- pci_config_get8(config_handle, PCI_CONF_PROGCLASS));
+ pci_config_get8(config_handle, PCI_CONF_PROGCLASS));
prom_printf(" Dev Class = [0x%x]\n",
- pci_config_get8(config_handle, PCI_CONF_SUBCLASS));
+ pci_config_get8(config_handle, PCI_CONF_SUBCLASS));
prom_printf(" Base Class = [0x%x]\n",
- pci_config_get8(config_handle, PCI_CONF_BASCLASS));
+ pci_config_get8(config_handle, PCI_CONF_BASCLASS));
prom_printf(" Device ID = [0x%x]\n",
- pci_config_get8(config_handle, PCI_CONF_CACHE_LINESZ));
+ pci_config_get8(config_handle, PCI_CONF_CACHE_LINESZ));
prom_printf(" Header Type = [0x%x]\n",
- pci_config_get8(config_handle, PCI_CONF_HEADER));
+ pci_config_get8(config_handle, PCI_CONF_HEADER));
prom_printf(" BIST = [0x%x]\n",
- pci_config_get8(config_handle, PCI_CONF_BIST));
+ pci_config_get8(config_handle, PCI_CONF_BIST));
prom_printf(" BASE 0 = [0x%x]\n",
- pci_config_get32(config_handle, PCI_CONF_BASE0));
+ pci_config_get32(config_handle, PCI_CONF_BASE0));
prom_printf(" BASE 1 = [0x%x]\n",
- pci_config_get32(config_handle, PCI_CONF_BASE1));
+ pci_config_get32(config_handle, PCI_CONF_BASE1));
}
@@ -418,29 +428,29 @@ pcicfg_dump_device_config(ddi_acc_handle_t config_handle)
pcicfg_dump_common_config(config_handle);
prom_printf(" BASE 2 = [0x%x]\n",
- pci_config_get32(config_handle, PCI_CONF_BASE2));
+ pci_config_get32(config_handle, PCI_CONF_BASE2));
prom_printf(" BASE 3 = [0x%x]\n",
- pci_config_get32(config_handle, PCI_CONF_BASE3));
+ pci_config_get32(config_handle, PCI_CONF_BASE3));
prom_printf(" BASE 4 = [0x%x]\n",
- pci_config_get32(config_handle, PCI_CONF_BASE4));
+ pci_config_get32(config_handle, PCI_CONF_BASE4));
prom_printf(" BASE 5 = [0x%x]\n",
- pci_config_get32(config_handle, PCI_CONF_BASE5));
+ pci_config_get32(config_handle, PCI_CONF_BASE5));
prom_printf(" Cardbus CIS = [0x%x]\n",
- pci_config_get32(config_handle, PCI_CONF_CIS));
+ pci_config_get32(config_handle, PCI_CONF_CIS));
prom_printf(" Sub VID = [0x%x]\n",
- pci_config_get16(config_handle, PCI_CONF_SUBVENID));
+ pci_config_get16(config_handle, PCI_CONF_SUBVENID));
prom_printf(" Sub SID = [0x%x]\n",
- pci_config_get16(config_handle, PCI_CONF_SUBSYSID));
+ pci_config_get16(config_handle, PCI_CONF_SUBSYSID));
prom_printf(" ROM = [0x%x]\n",
- pci_config_get32(config_handle, PCI_CONF_ROM));
+ pci_config_get32(config_handle, PCI_CONF_ROM));
prom_printf(" I Line = [0x%x]\n",
- pci_config_get8(config_handle, PCI_CONF_ILINE));
+ pci_config_get8(config_handle, PCI_CONF_ILINE));
prom_printf(" I Pin = [0x%x]\n",
- pci_config_get8(config_handle, PCI_CONF_IPIN));
+ pci_config_get8(config_handle, PCI_CONF_IPIN));
prom_printf(" Max Grant = [0x%x]\n",
- pci_config_get8(config_handle, PCI_CONF_MIN_G));
+ pci_config_get8(config_handle, PCI_CONF_MIN_G));
prom_printf(" Max Latent = [0x%x]\n",
- pci_config_get8(config_handle, PCI_CONF_MAX_L));
+ pci_config_get8(config_handle, PCI_CONF_MAX_L));
}
static void
@@ -453,43 +463,43 @@ pcicfg_dump_bridge_config(ddi_acc_handle_t config_handle)
prom_printf("........................................\n");
prom_printf(" Pri Bus = [0x%x]\n",
- pci_config_get8(config_handle, PCI_BCNF_PRIBUS));
+ pci_config_get8(config_handle, PCI_BCNF_PRIBUS));
prom_printf(" Sec Bus = [0x%x]\n",
- pci_config_get8(config_handle, PCI_BCNF_SECBUS));
+ pci_config_get8(config_handle, PCI_BCNF_SECBUS));
prom_printf(" Sub Bus = [0x%x]\n",
- pci_config_get8(config_handle, PCI_BCNF_SUBBUS));
+ pci_config_get8(config_handle, PCI_BCNF_SUBBUS));
prom_printf(" Latency = [0x%x]\n",
- pci_config_get8(config_handle, PCI_BCNF_LATENCY_TIMER));
+ pci_config_get8(config_handle, PCI_BCNF_LATENCY_TIMER));
prom_printf(" I/O Base LO = [0x%x]\n",
- pci_config_get8(config_handle, PCI_BCNF_IO_BASE_LOW));
+ pci_config_get8(config_handle, PCI_BCNF_IO_BASE_LOW));
prom_printf(" I/O Lim LO = [0x%x]\n",
- pci_config_get8(config_handle, PCI_BCNF_IO_LIMIT_LOW));
+ pci_config_get8(config_handle, PCI_BCNF_IO_LIMIT_LOW));
prom_printf(" Sec. Status = [0x%x]\n",
- pci_config_get16(config_handle, PCI_BCNF_SEC_STATUS));
+ pci_config_get16(config_handle, PCI_BCNF_SEC_STATUS));
prom_printf(" Mem Base = [0x%x]\n",
- pci_config_get16(config_handle, PCI_BCNF_MEM_BASE));
+ pci_config_get16(config_handle, PCI_BCNF_MEM_BASE));
prom_printf(" Mem Limit = [0x%x]\n",
- pci_config_get16(config_handle, PCI_BCNF_MEM_LIMIT));
+ pci_config_get16(config_handle, PCI_BCNF_MEM_LIMIT));
prom_printf(" PF Mem Base = [0x%x]\n",
- pci_config_get16(config_handle, PCI_BCNF_PF_BASE_LOW));
+ pci_config_get16(config_handle, PCI_BCNF_PF_BASE_LOW));
prom_printf(" PF Mem Lim = [0x%x]\n",
- pci_config_get16(config_handle, PCI_BCNF_PF_LIMIT_LOW));
+ pci_config_get16(config_handle, PCI_BCNF_PF_LIMIT_LOW));
prom_printf(" PF Base HI = [0x%x]\n",
- pci_config_get32(config_handle, PCI_BCNF_PF_BASE_HIGH));
+ pci_config_get32(config_handle, PCI_BCNF_PF_BASE_HIGH));
prom_printf(" PF Lim HI = [0x%x]\n",
- pci_config_get32(config_handle, PCI_BCNF_PF_LIMIT_HIGH));
+ pci_config_get32(config_handle, PCI_BCNF_PF_LIMIT_HIGH));
prom_printf(" I/O Base HI = [0x%x]\n",
- pci_config_get16(config_handle, PCI_BCNF_IO_BASE_HI));
+ pci_config_get16(config_handle, PCI_BCNF_IO_BASE_HI));
prom_printf(" I/O Lim HI = [0x%x]\n",
- pci_config_get16(config_handle, PCI_BCNF_IO_LIMIT_HI));
+ pci_config_get16(config_handle, PCI_BCNF_IO_LIMIT_HI));
prom_printf(" ROM addr = [0x%x]\n",
- pci_config_get32(config_handle, PCI_BCNF_ROM));
+ pci_config_get32(config_handle, PCI_BCNF_ROM));
prom_printf(" Intr Line = [0x%x]\n",
- pci_config_get8(config_handle, PCI_BCNF_ILINE));
+ pci_config_get8(config_handle, PCI_BCNF_ILINE));
prom_printf(" Intr Pin = [0x%x]\n",
- pci_config_get8(config_handle, PCI_BCNF_IPIN));
+ pci_config_get8(config_handle, PCI_BCNF_IPIN));
prom_printf(" Bridge Ctrl = [0x%x]\n",
- pci_config_get16(config_handle, PCI_BCNF_BCNTRL));
+ pci_config_get16(config_handle, PCI_BCNF_BCNTRL));
}
#endif
@@ -535,17 +545,24 @@ _info(struct modinfo *modinfop)
* registers.
*/
int
-pcicfg_configure(dev_info_t *devi, uint_t device)
+pcicfg_configure(dev_info_t *devi, uint_t device, uint_t function,
+ pcicfg_flags_t flags)
{
uint_t bus;
int len;
int func;
- dev_info_t *new_device;
dev_info_t *attach_point;
pci_bus_range_t pci_bus_range;
int rv;
int circ;
uint_t highest_bus;
+ int ari_mode = B_FALSE;
+ int max_function = PCI_MAX_FUNCTIONS;
+ int trans_device;
+ dev_info_t *new_device;
+
+ if (flags == PCICFG_FLAG_ENABLE_ARI)
+ return (pcicfg_ari_configure(devi));
/*
* Start probing at the device specified in "device" on the
@@ -563,27 +580,35 @@ pcicfg_configure(dev_info_t *devi, uint_t device)
attach_point = devi;
ndi_devi_enter(devi, &circ);
- for (func = 0; func < PCI_MAX_FUNCTIONS; func++) {
+ for (func = 0; func < max_function; ) {
- DEBUG3("Configuring [0x%x][0x%x][0x%x]\n", bus, device, func);
+ if ((function != PCICFG_ALL_FUNC) && (function != func))
+ goto next;
+
+ if (ari_mode)
+ trans_device = func >> 3;
+ else
+ trans_device = device;
switch (rv = pcicfg_probe_children(attach_point,
- bus, device, func, &highest_bus)) {
+ bus, trans_device, func & 7, &highest_bus, flags)) {
case PCICFG_NORESRC:
case PCICFG_FAILURE:
- DEBUG2("configure failed: "
- "bus [0x%x] device [0x%x]\n",
- bus, device);
+ DEBUG2("configure failed: bus [0x%x] device "
+ "[0x%x]\n", bus, trans_device);
goto cleanup;
case PCICFG_NODEVICE:
DEBUG3("no device : bus "
- "[0x%x] slot [0x%x] func [0x%x]\n",
- bus, device, func);
- continue;
+ "[0x%x] slot [0x%x] func [0x%x]\n",
+ bus, trans_device, func &7);
+
+ if (func)
+ goto next;
+ break;
default:
DEBUG3("configure: bus => [%d] "
- "slot => [%d] func => [%d]\n",
- bus, device, func);
+ "slot => [%d] func => [%d]\n",
+ bus, trans_device, func & 7);
break;
}
@@ -591,7 +616,7 @@ pcicfg_configure(dev_info_t *devi, uint_t device)
break;
if ((new_device = pcicfg_devi_find(attach_point,
- device, func)) == NULL) {
+ trans_device, func & 7)) == NULL) {
DEBUG0("Did'nt find device node just created\n");
goto cleanup;
}
@@ -623,10 +648,51 @@ pcicfg_configure(dev_info_t *devi, uint_t device)
if (pcicfg_is_ntbridge(new_device) != DDI_FAILURE) {
DEBUG0("pcicfg: Found nontransparent bridge.\n");
- rv = pcicfg_configure_ntbridge(new_device, bus, device);
+ rv = pcicfg_configure_ntbridge(new_device, bus,
+ trans_device);
if (rv != PCICFG_SUCCESS)
goto cleanup;
}
+
+next:
+ /*
+ * Determine if ARI Forwarding should be enabled.
+ */
+ if (func == 0) {
+ if ((pcie_ari_supported(devi)
+ == PCIE_ARI_FORW_SUPPORTED) &&
+ (pcie_ari_device(new_device) == PCIE_ARI_DEVICE)) {
+ if (pcie_ari_enable(devi) == DDI_SUCCESS) {
+ (void) ddi_prop_create(DDI_DEV_T_NONE,
+ devi, DDI_PROP_CANSLEEP,
+ "ari-enabled", NULL, 0);
+
+ ari_mode = B_TRUE;
+ max_function = PCICFG_MAX_ARI_FUNCTION;
+ }
+ }
+ }
+ if (ari_mode == B_TRUE) {
+ int next_function;
+
+ DEBUG0("Next Function - ARI Device\n");
+ if (pcie_ari_get_next_function(new_device,
+ &next_function) != DDI_SUCCESS)
+ goto cleanup;
+
+ /*
+ * Check if there are more fucntions to probe.
+ */
+ if (next_function == 0) {
+ DEBUG0("Next Function - "
+ "No more ARI Functions\n");
+ break;
+ }
+ func = next_function;
+ } else {
+ func++;
+ }
+ DEBUG1("Next Function - %x\n", func);
}
ndi_devi_exit(devi, circ);
@@ -644,13 +710,16 @@ cleanup:
*/
for (func = 0; func < PCI_MAX_FUNCTIONS; func++) {
- if ((new_device = pcicfg_devi_find(devi,
- device, func)) == NULL) {
+ if ((function != PCICFG_ALL_FUNC) && (function != func))
+ continue;
+
+ if ((new_device = pcicfg_devi_find(devi, device, func))
+ == NULL) {
continue;
}
DEBUG2("Cleaning up device [0x%x] function [0x%x]\n",
- device, func);
+ device, func);
/*
* If this was a bridge device it will have a
* probe handle - if not, no harm in calling this.
@@ -709,9 +778,7 @@ pcicfg_configure_ntbridge(dev_info_t *new_device, uint_t bus, uint_t device)
* mapped directly under the host.
*/
if ((rc = ndi_prop_update_int(DDI_DEV_T_NONE, new_device,
- PCI_DEV_CONF_MAP_PROP, (int)DDI_SUCCESS))
- != DDI_SUCCESS) {
-
+ PCI_DEV_CONF_MAP_PROP, (int)DDI_SUCCESS)) != DDI_SUCCESS) {
DEBUG0("Cannot create indirect conf map property.\n");
return ((int)PCICFG_FAILURE);
}
@@ -726,7 +793,7 @@ pcicfg_configure_ntbridge(dev_info_t *new_device, uint_t bus, uint_t device)
pci_config_teardown(&config_handle);
/* create Bus node properties for ntbridge. */
if (pcicfg_set_busnode_props(new_device, pcie_device_type)
- != PCICFG_SUCCESS) {
+ != PCICFG_SUCCESS) {
DEBUG0("Failed to set busnode props\n");
return (rc);
}
@@ -734,9 +801,8 @@ pcicfg_configure_ntbridge(dev_info_t *new_device, uint_t bus, uint_t device)
/* For now: Lets only support one layer of child */
bzero((caddr_t)&req, sizeof (ndi_ra_request_t));
req.ra_len = 1;
- if (ndi_ra_alloc(ddi_get_parent(new_device), &req,
- &next_bus, &blen, NDI_RA_TYPE_PCI_BUSNUM,
- NDI_RA_PASS) != NDI_SUCCESS) {
+ if (ndi_ra_alloc(ddi_get_parent(new_device), &req, &next_bus, &blen,
+ NDI_RA_TYPE_PCI_BUSNUM, NDI_RA_PASS) != NDI_SUCCESS) {
DEBUG0("ntbridge: Failed to get a bus number\n");
return (PCICFG_NORESRC);
}
@@ -750,8 +816,8 @@ pcicfg_configure_ntbridge(dev_info_t *new_device, uint_t bus, uint_t device)
bus_range[0] = (int)next_bus;
bus_range[1] = (int)next_bus;
- if (ndi_prop_update_int_array(DDI_DEV_T_NONE, new_device,
- "bus-range", bus_range, 2) != DDI_SUCCESS) {
+ if (ndi_prop_update_int_array(DDI_DEV_T_NONE, new_device, "bus-range",
+ bus_range, 2) != DDI_SUCCESS) {
DEBUG0("Cannot set ntbridge bus-range property");
return (rc);
}
@@ -777,8 +843,7 @@ pcicfg_configure_ntbridge(dev_info_t *new_device, uint_t bus, uint_t device)
DEBUG0("pcicfg: Success loading nontransparent bridge nexus driver..");
/* Now set aside pci resource allocation requests for our children */
- if (pcicfg_ntbridge_allocate_resources(new_device) !=
- PCICFG_SUCCESS) {
+ if (pcicfg_ntbridge_allocate_resources(new_device) != PCICFG_SUCCESS) {
max_devs = 0;
rc = PCICFG_FAILURE;
} else
@@ -792,18 +857,18 @@ pcicfg_configure_ntbridge(dev_info_t *new_device, uint_t bus, uint_t device)
(pnode_t)DEVI_SID_NODEID, &new_ntbridgechild);
if (pcicfg_add_config_reg(new_ntbridgechild, next_bus, devno, 0)
- != DDI_PROP_SUCCESS) {
+ != DDI_PROP_SUCCESS) {
cmn_err(CE_WARN,
- "Failed to add conf reg for ntbridge child.\n");
+ "Failed to add conf reg for ntbridge child.\n");
(void) ndi_devi_free(new_ntbridgechild);
rc = PCICFG_FAILURE;
break;
}
if (pci_config_setup(new_ntbridgechild, &config_handle)
- != DDI_SUCCESS) {
+ != DDI_SUCCESS) {
cmn_err(CE_WARN,
- "Cannot map ntbridge child %x\n", devno);
+ "Cannot map ntbridge child %x\n", devno);
(void) ndi_devi_free(new_ntbridgechild);
rc = PCICFG_FAILURE;
break;
@@ -822,20 +887,18 @@ pcicfg_configure_ntbridge(dev_info_t *new_device, uint_t bus, uint_t device)
continue;
/* Lets fake attachments points for each child, */
- rc = pcicfg_configure(new_device, devno);
+ rc = pcicfg_configure(new_device, devno, PCICFG_ALL_FUNC, 0);
if (rc != PCICFG_SUCCESS) {
int old_dev = pcicfg_start_devno;
cmn_err(CE_WARN,
- "Error configuring ntbridge child dev=%d\n", devno);
+ "Error configuring ntbridge child dev=%d\n", devno);
while (old_dev != devno) {
if (pcicfg_ntbridge_unconfigure_child(
- new_device, old_dev) == PCICFG_FAILURE)
-
- cmn_err(CE_WARN,
- "Unconfig Error ntbridge child "
- "dev=%d\n", old_dev);
+ new_device, old_dev) == PCICFG_FAILURE)
+ cmn_err(CE_WARN, "Unconfig Error "
+ "ntbridge child dev=%d\n", old_dev);
old_dev++;
}
break;
@@ -851,19 +914,19 @@ pcicfg_configure_ntbridge(dev_info_t *new_device, uint_t bus, uint_t device)
int k;
if (ddi_getlongprop(DDI_DEV_T_ANY, new_device,
- DDI_PROP_DONTPASS, "bus-range", (caddr_t)&bus,
- &k) != DDI_PROP_SUCCESS) {
+ DDI_PROP_DONTPASS, "bus-range", (caddr_t)&bus, &k)
+ != DDI_PROP_SUCCESS) {
DEBUG0("Failed to read bus-range property\n");
rc = PCICFG_FAILURE;
return (rc);
}
DEBUG2("Need to free bus [%d] range [%d]\n",
- bus[0], bus[1] - bus[0] + 1);
+ bus[0], bus[1] - bus[0] + 1);
- if (ndi_ra_free(ddi_get_parent(new_device),
- (uint64_t)bus[0], (uint64_t)(bus[1] - bus[0] + 1),
- NDI_RA_TYPE_PCI_BUSNUM, NDI_RA_PASS) != NDI_SUCCESS) {
+ if (ndi_ra_free(ddi_get_parent(new_device), (uint64_t)bus[0],
+ (uint64_t)(bus[1] - bus[0] + 1), NDI_RA_TYPE_PCI_BUSNUM,
+ NDI_RA_PASS) != NDI_SUCCESS) {
DEBUG0("Failed to free a bus number\n");
rc = PCICFG_FAILURE;
kmem_free(bus, k);
@@ -920,9 +983,9 @@ pcicfg_ntbridge_allocate_resources(dev_info_t *dip)
/* Set Memory space handle for ntbridge */
if (pcicfg_get_ntbridge_child_range(dip, &boundbase, &boundlen,
- PCI_BASE_SPACE_MEM) != DDI_SUCCESS) {
+ PCI_BASE_SPACE_MEM) != DDI_SUCCESS) {
cmn_err(CE_WARN,
- "ntbridge: Mem resource information failure\n");
+ "ntbridge: Mem resource information failure\n");
phdl->memory_len = 0;
return (PCICFG_FAILURE);
}
@@ -930,7 +993,7 @@ pcicfg_ntbridge_allocate_resources(dev_info_t *dip)
mem_request->ra_boundlen = boundbase + boundlen;
mem_request->ra_len = boundlen;
mem_request->ra_align_mask =
- PCICFG_MEMGRAN - 1; /* 1M alignment on memory space */
+ PCICFG_MEMGRAN - 1; /* 1M alignment on memory space */
mem_request->ra_flags |= NDI_RA_ALLOC_BOUNDED;
/*
@@ -944,19 +1007,19 @@ pcicfg_ntbridge_allocate_resources(dev_info_t *dip)
phdl->mem_hole.len = mem_request->ra_len;
phdl->mem_hole.next = (hole_t *)NULL;
- DEBUG2("AP requested [0x%llx], needs [0x%llx] bytes of memory\n",
- boundlen, mem_request->ra_len);
+ DEBUG2("Connector requested [0x%llx], needs [0x%llx] bytes of memory\n",
+ boundlen, mem_request->ra_len);
/* Set IO space handle for ntbridge */
if (pcicfg_get_ntbridge_child_range(dip, &boundbase, &boundlen,
- PCI_BASE_SPACE_IO) != DDI_SUCCESS) {
+ PCI_BASE_SPACE_IO) != DDI_SUCCESS) {
cmn_err(CE_WARN, "ntbridge: IO resource information failure\n");
phdl->io_len = 0;
return (PCICFG_FAILURE);
}
io_request->ra_len = boundlen;
io_request->ra_align_mask =
- PCICFG_IOGRAN - 1; /* 4K alignment on I/O space */
+ PCICFG_IOGRAN - 1; /* 4K alignment on I/O space */
io_request->ra_boundbase = boundbase;
io_request->ra_boundlen = boundbase + boundlen;
io_request->ra_flags |= NDI_RA_ALLOC_BOUNDED;
@@ -972,14 +1035,14 @@ pcicfg_ntbridge_allocate_resources(dev_info_t *dip)
phdl->io_hole.len = io_request->ra_len;
phdl->io_hole.next = (hole_t *)NULL;
- DEBUG2("AP requested [0x%llx], needs [0x%llx] bytes of IO\n",
- boundlen, io_request->ra_len);
+ DEBUG2("Connector requested [0x%llx], needs [0x%llx] bytes of IO\n",
+ boundlen, io_request->ra_len);
/* Set Prefetchable Memory space handle for ntbridge */
if (pcicfg_get_ntbridge_child_range(dip, &boundbase, &boundlen,
- PCI_BASE_SPACE_MEM | PCI_BASE_PREF_M) != DDI_SUCCESS) {
+ PCI_BASE_SPACE_MEM | PCI_BASE_PREF_M) != DDI_SUCCESS) {
cmn_err(CE_WARN,
- "ntbridge: PF Mem resource information failure\n");
+ "ntbridge: PF Mem resource information failure\n");
phdl->pf_memory_len = 0;
return (PCICFG_FAILURE);
}
@@ -987,7 +1050,7 @@ pcicfg_ntbridge_allocate_resources(dev_info_t *dip)
pf_mem_request->ra_boundlen = boundbase + boundlen;
pf_mem_request->ra_len = boundlen;
pf_mem_request->ra_align_mask =
- PCICFG_MEMGRAN - 1; /* 1M alignment on memory space */
+ PCICFG_MEMGRAN - 1; /* 1M alignment on memory space */
pf_mem_request->ra_flags |= NDI_RA_ALLOC_BOUNDED;
/*
@@ -1001,15 +1064,15 @@ pcicfg_ntbridge_allocate_resources(dev_info_t *dip)
phdl->pf_mem_hole.len = pf_mem_request->ra_len;
phdl->pf_mem_hole.next = (hole_t *)NULL;
- DEBUG2("AP requested [0x%llx], needs [0x%llx] bytes of PF memory\n",
- boundlen, pf_mem_request->ra_len);
+ DEBUG2("Connector requested [0x%llx], needs [0x%llx] bytes of PF "
+ "memory\n", boundlen, pf_mem_request->ra_len);
DEBUG2("MEMORY BASE = [0x%lx] length [0x%lx]\n",
- phdl->memory_base, phdl->memory_len);
+ phdl->memory_base, phdl->memory_len);
DEBUG2("IO BASE = [0x%x] length [0x%x]\n",
- phdl->io_base, phdl->io_len);
+ phdl->io_base, phdl->io_len);
DEBUG2("PF MEMORY BASE = [0x%lx] length [0x%lx]\n",
- phdl->pf_memory_base, phdl->pf_memory_len);
+ phdl->pf_memory_base, phdl->pf_memory_len);
return (PCICFG_SUCCESS);
}
@@ -1028,24 +1091,23 @@ pcicfg_ntbridge_configure_done(dev_info_t *dip)
entry = pcicfg_find_phdl(dip);
ASSERT(entry);
- bzero((caddr_t)range,
- sizeof (ppb_ranges_t) * PCICFG_RANGE_LEN);
+ bzero((caddr_t)range, sizeof (ppb_ranges_t) * PCICFG_RANGE_LEN);
range[1].child_high = range[1].parent_high |=
- (PCI_REG_REL_M | PCI_ADDR_MEM32);
+ (PCI_REG_REL_M | PCI_ADDR_MEM32);
range[1].child_low = range[1].parent_low = (uint32_t)entry->memory_base;
range[0].child_high = range[0].parent_high |=
- (PCI_REG_REL_M | PCI_ADDR_IO);
+ (PCI_REG_REL_M | PCI_ADDR_IO);
range[0].child_low = range[0].parent_low = (uint32_t)entry->io_base;
range[2].child_high = range[2].parent_high |=
- (PCI_REG_REL_M | PCI_ADDR_MEM32 | PCI_REG_PF_M);
+ (PCI_REG_REL_M | PCI_ADDR_MEM32 | PCI_REG_PF_M);
range[2].child_low = range[2].parent_low =
- (uint32_t)entry->pf_memory_base;
+ (uint32_t)entry->pf_memory_base;
len = sizeof (pci_bus_range_t);
if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
- "bus-range", (caddr_t)&bus_range, (int *)&len) != DDI_SUCCESS) {
+ "bus-range", (caddr_t)&bus_range, (int *)&len) != DDI_SUCCESS) {
DEBUG0("no bus-range property\n");
return (PCICFG_FAILURE);
}
@@ -1054,8 +1116,8 @@ pcicfg_ntbridge_configure_done(dev_info_t *dip)
if (entry->highest_bus) { /* secondary bus number */
if (entry->highest_bus < bus_range.lo) {
cmn_err(CE_WARN,
- "ntbridge bus range invalid !(%d,%d)\n",
- bus_range.lo, entry->highest_bus);
+ "ntbridge bus range invalid !(%d,%d)\n",
+ bus_range.lo, entry->highest_bus);
new_bus_range[1] = bus_range.lo + entry->highest_bus;
}
else
@@ -1064,11 +1126,11 @@ pcicfg_ntbridge_configure_done(dev_info_t *dip)
else
new_bus_range[1] = bus_range.hi;
- DEBUG2("ntbridge: bus range lo=%x, hi=%x\n",
- new_bus_range[0], new_bus_range[1]);
+ DEBUG2("ntbridge: bus range lo=%x, hi=%x\n", new_bus_range[0],
+ new_bus_range[1]);
- if (ndi_prop_update_int_array(DDI_DEV_T_NONE, dip,
- "bus-range", new_bus_range, 2) != DDI_SUCCESS) {
+ if (ndi_prop_update_int_array(DDI_DEV_T_NONE, dip, "bus-range",
+ new_bus_range, 2) != DDI_SUCCESS) {
DEBUG0("Failed to set bus-range property");
entry->error = PCICFG_FAILURE;
return (PCICFG_FAILURE);
@@ -1079,7 +1141,7 @@ pcicfg_ntbridge_configure_done(dev_info_t *dip)
uint64_t unused;
unused = pcicfg_unused_space(&entry->io_hole, &len);
DEBUG2("ntbridge: Unused IO space %llx bytes over %d holes\n",
- unused, len);
+ unused, len);
}
#endif
@@ -1095,7 +1157,7 @@ pcicfg_ntbridge_configure_done(dev_info_t *dip)
uint64_t unused;
unused = pcicfg_unused_space(&entry->mem_hole, &len);
DEBUG2("ntbridge: Unused Mem space %llx bytes over %d holes\n",
- unused, len);
+ unused, len);
}
#endif
@@ -1111,7 +1173,7 @@ pcicfg_ntbridge_configure_done(dev_info_t *dip)
uint64_t unused;
unused = pcicfg_unused_space(&entry->pf_mem_hole, &len);
DEBUG2("ntbridge: Unused PF Mem space %llx bytes over"
- " %d holes\n", unused, len);
+ " %d holes\n", unused, len);
}
#endif
@@ -1128,13 +1190,13 @@ pcicfg_ntbridge_configure_done(dev_info_t *dip)
static int
pcicfg_ntbridge_program_child(dev_info_t *dip)
{
- pcicfg_phdl_t *entry;
- int rc = PCICFG_SUCCESS;
+ pcicfg_phdl_t *entry;
+ int rc = PCICFG_SUCCESS;
dev_info_t *anode = dip;
- /* Find the attachment point node */
- while ((anode != NULL) && (strcmp(ddi_binding_name(anode),
- "hp_attachment") != 0)) {
+ /* Find the Hotplug Connection (CN) node */
+ while ((anode != NULL) &&
+ (strcmp(ddi_binding_name(anode), "hp_attachment") != 0)) {
anode = ddi_get_parent(anode);
}
@@ -1147,8 +1209,8 @@ pcicfg_ntbridge_program_child(dev_info_t *dip)
if (pcicfg_bridge_assign(dip, entry) == DDI_WALK_TERMINATE) {
cmn_err(CE_WARN,
- "ntbridge: Error assigning range for child %s\n",
- ddi_get_name(dip));
+ "ntbridge: Error assigning range for child %s\n",
+ ddi_get_name(dip));
rc = PCICFG_FAILURE;
}
return (rc);
@@ -1166,7 +1228,7 @@ pcicfg_ntbridge_unconfigure_child(dev_info_t *new_device, uint_t devno)
len = sizeof (pci_bus_range_t);
if (ddi_getlongprop_buf(DDI_DEV_T_ANY, new_device, DDI_PROP_DONTPASS,
- "bus-range", (caddr_t)&pci_bus_range, &len) != DDI_SUCCESS) {
+ "bus-range", (caddr_t)&pci_bus_range, &len) != DDI_SUCCESS) {
DEBUG0("no bus-range property\n");
return (PCICFG_FAILURE);
}
@@ -1177,18 +1239,17 @@ pcicfg_ntbridge_unconfigure_child(dev_info_t *new_device, uint_t devno)
(pnode_t)DEVI_SID_NODEID, &new_ntbridgechild);
if (pcicfg_add_config_reg(new_ntbridgechild, bus, devno, 0)
- != DDI_PROP_SUCCESS) {
- cmn_err(CE_WARN,
- "Unconfigure: Failed to add conf reg prop for ntbridge "
- "child.\n");
+ != DDI_PROP_SUCCESS) {
+ cmn_err(CE_WARN, "Unconfigure: Failed to add conf reg prop for "
+ "ntbridge child.\n");
(void) ndi_devi_free(new_ntbridgechild);
return (PCICFG_FAILURE);
}
if (pci_config_setup(new_ntbridgechild, &config_handle)
- != DDI_SUCCESS) {
- cmn_err(CE_WARN,
- "pcicfg: Cannot map ntbridge child %x\n", devno);
+ != DDI_SUCCESS) {
+ cmn_err(CE_WARN, "pcicfg: Cannot map ntbridge child %x\n",
+ devno);
(void) ndi_devi_free(new_ntbridgechild);
return (PCICFG_FAILURE);
}
@@ -1205,7 +1266,7 @@ pcicfg_ntbridge_unconfigure_child(dev_info_t *new_device, uint_t devno)
if (vid == 0xffff)
return (PCICFG_NODEVICE);
- return (pcicfg_unconfigure(new_device, devno));
+ return (pcicfg_unconfigure(new_device, devno, PCICFG_ALL_FUNC, 0));
}
static uint_t
@@ -1215,19 +1276,18 @@ pcicfg_ntbridge_unconfigure(dev_info_t *dip)
uint_t *bus;
int k, rc = DDI_FAILURE;
- if (ddi_getlongprop(DDI_DEV_T_ANY, dip,
- DDI_PROP_DONTPASS, "bus-range", (caddr_t)&bus,
- &k) != DDI_PROP_SUCCESS) {
+ if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "bus-range",
+ (caddr_t)&bus, &k) != DDI_PROP_SUCCESS) {
DEBUG0("ntbridge: Failed to read bus-range property\n");
return (rc);
}
DEBUG2("ntbridge: Need to free bus [%d] range [%d]\n",
- bus[0], bus[1] - bus[0] + 1);
+ bus[0], bus[1] - bus[0] + 1);
- if (ndi_ra_free(ddi_get_parent(dip),
- (uint64_t)bus[0], (uint64_t)(bus[1] - bus[0] + 1),
- NDI_RA_TYPE_PCI_BUSNUM, NDI_RA_PASS) != NDI_SUCCESS) {
+ if (ndi_ra_free(ddi_get_parent(dip), (uint64_t)bus[0],
+ (uint64_t)(bus[1] - bus[0] + 1),
+ NDI_RA_TYPE_PCI_BUSNUM, NDI_RA_PASS) != NDI_SUCCESS) {
DEBUG0("ntbridge: Failed to free a bus number\n");
kmem_free(bus, k);
return (rc);
@@ -1257,7 +1317,7 @@ pcicfg_is_ntbridge(dev_info_t *dip)
if (pci_config_setup(dip, &config_handle) != DDI_SUCCESS) {
cmn_err(CE_WARN,
- "pcicfg: cannot map config space, to get map type\n");
+ "pcicfg: cannot map config space, to get map type\n");
return (DDI_FAILURE);
}
class = pci_config_get8(config_handle, PCI_CONF_BASCLASS);
@@ -1268,9 +1328,9 @@ pcicfg_is_ntbridge(dev_info_t *dip)
rc = DDI_FAILURE;
DEBUG3("pcicfg: checking device %x,%x for indirect map. rc=%d\n",
- pci_config_get16(config_handle, PCI_CONF_VENID),
- pci_config_get16(config_handle, PCI_CONF_DEVID),
- rc);
+ pci_config_get16(config_handle, PCI_CONF_VENID),
+ pci_config_get16(config_handle, PCI_CONF_DEVID),
+ rc);
pci_config_teardown(&config_handle);
return (rc);
}
@@ -1282,10 +1342,10 @@ pcicfg_ntbridge_child(dev_info_t *dip)
dev_info_t *anode = dip;
/*
- * Find the attachment point node
+ * Find the Hotplug Connection (CN) node
*/
while ((anode != NULL) && (strcmp(ddi_binding_name(anode),
- "hp_attachment") != 0)) {
+ "hp_attachment") != 0)) {
anode = ddi_get_parent(anode);
}
@@ -1295,11 +1355,11 @@ pcicfg_ntbridge_child(dev_info_t *dip)
}
len = sizeof (int);
if (ddi_getlongprop_buf(DDI_DEV_T_ANY, ddi_get_parent(anode),
- DDI_PROP_DONTPASS, PCI_DEV_CONF_MAP_PROP, (caddr_t)&val,
- &len) != DDI_SUCCESS) {
+ DDI_PROP_DONTPASS, PCI_DEV_CONF_MAP_PROP, (caddr_t)&val, &len)
+ != DDI_SUCCESS) {
DEBUG1("ntbridge child: no \"%s\" property\n",
- PCI_DEV_CONF_MAP_PROP);
+ PCI_DEV_CONF_MAP_PROP);
return (rc);
}
DEBUG0("ntbridge child: success\n");
@@ -1316,41 +1376,39 @@ pcicfg_get_ntbridge_child_range(dev_info_t *dip, uint64_t *boundbase,
if ((ibridge = pcicfg_is_ntbridge(dip)) == DDI_FAILURE)
return (found);
- if (ddi_getlongprop(DDI_DEV_T_ANY, dip,
- DDI_PROP_DONTPASS, "assigned-addresses", (caddr_t)&assigned,
- &length) != DDI_PROP_SUCCESS) {
+ if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
+ "assigned-addresses", (caddr_t)&assigned, &length)
+ != DDI_PROP_SUCCESS) {
DEBUG1("Failed to get assigned-addresses property %llx\n", dip);
return (found);
}
DEBUG1("pcicfg: ntbridge child range: dip = %s\n",
- ddi_driver_name(dip));
+ ddi_driver_name(dip));
acount = length / sizeof (pci_regspec_t);
for (i = 0; i < acount; i++) {
- if ((PCI_REG_REG_G(assigned[i].pci_phys_hi)
- == pcicfg_indirect_map_devs[ibridge].mem_range_bar_offset) &&
- (space_type == PCI_BASE_SPACE_MEM)) {
+ if ((PCI_REG_REG_G(assigned[i].pci_phys_hi) ==
+ pcicfg_indirect_map_devs[ibridge].mem_range_bar_offset) &&
+ (space_type == PCI_BASE_SPACE_MEM)) {
+ found = DDI_SUCCESS;
+ break;
+ } else if ((PCI_REG_REG_G(assigned[i].pci_phys_hi) ==
+ pcicfg_indirect_map_devs[ibridge].io_range_bar_offset) &&
+ (space_type == PCI_BASE_SPACE_IO)) {
+ found = DDI_SUCCESS;
+ break;
+ } else if ((PCI_REG_REG_G(assigned[i].pci_phys_hi) ==
+ pcicfg_indirect_map_devs[ibridge].
+ prefetch_mem_range_bar_offset) &&
+ (space_type == (PCI_BASE_SPACE_MEM |
+ PCI_BASE_PREF_M))) {
found = DDI_SUCCESS;
break;
- } else if ((PCI_REG_REG_G(assigned[i].pci_phys_hi)
- == pcicfg_indirect_map_devs[ibridge].io_range_bar_offset) &&
- (space_type == PCI_BASE_SPACE_IO)) {
- found = DDI_SUCCESS;
- break;
- } else {
- if ((PCI_REG_REG_G(assigned[i].pci_phys_hi) ==
- pcicfg_indirect_map_devs[ibridge].
- prefetch_mem_range_bar_offset) &&
- (space_type == (PCI_BASE_SPACE_MEM |
- PCI_BASE_PREF_M))) {
- found = DDI_SUCCESS;
- break;
- }
}
}
DEBUG3("pcicfg: ntbridge child range: space=%x, base=%lx, len=%lx\n",
- space_type, assigned[i].pci_phys_low, assigned[i].pci_size_low);
+ space_type, assigned[i].pci_phys_low, assigned[i].pci_size_low);
if (found == DDI_SUCCESS) {
*boundbase = assigned[i].pci_phys_low;
@@ -1363,91 +1421,134 @@ pcicfg_get_ntbridge_child_range(dev_info_t *dip, uint64_t *boundbase,
/*
* This will turn resources allocated by pcicfg_configure()
- * and remove the device tree from the attachment point
+ * and remove the device tree from the Hotplug Connection (CN)
* and below. The routine assumes the devices have their
* drivers detached.
*/
int
-pcicfg_unconfigure(dev_info_t *devi, uint_t device)
+pcicfg_unconfigure(dev_info_t *devi, uint_t device, uint_t function,
+ pcicfg_flags_t flags)
{
dev_info_t *child_dip;
int func;
int i;
+ int max_function, trans_device;
+
+ if (pcie_ari_is_enabled(devi) == PCIE_ARI_FORW_ENABLED)
+ max_function = PCICFG_MAX_ARI_FUNCTION;
+ else
+ max_function = PCI_MAX_FUNCTIONS;
/*
* Cycle through devices to make sure none are busy.
* If a single device is busy fail the whole unconfigure.
*/
- for (func = 0; func < PCI_MAX_FUNCTIONS; func++) {
- if ((child_dip = pcicfg_devi_find(devi, device, func)) == NULL)
+ for (func = 0; func < max_function; func++) {
+ if ((function != PCICFG_ALL_FUNC) && (function != func))
+ continue;
+
+ if (max_function == PCICFG_MAX_ARI_FUNCTION)
+ trans_device = func >> 3; /* ARI Device */
+ else
+ trans_device = device;
+
+ if ((child_dip = pcicfg_devi_find(devi, trans_device,
+ func & 7)) == NULL)
continue;
if (ndi_devi_offline(child_dip, NDI_UNCONFIG) == NDI_SUCCESS)
- continue;
+ continue;
+
/*
* Device function is busy. Before returning we have to
* put all functions back online which were taken
* offline during the process.
*/
- DEBUG2("Device [0x%x] function [0x%x] is busy\n", device, func);
- for (i = 0; i < func; i++) {
- if ((child_dip = pcicfg_devi_find(devi, device, i))
- == NULL) {
- DEBUG0("No more devices to put back on line!!\n");
- /*
- * Made it through all functions
- */
- continue;
- }
- if (ndi_devi_online(child_dip, NDI_CONFIG) != NDI_SUCCESS) {
- DEBUG0("Failed to put back devices state\n");
+ DEBUG2("Device [0x%x] function [0x%x] is busy\n",
+ trans_device, func & 7);
+ /*
+ * If we are only asked to offline one specific function,
+ * and that fails, we just simply return.
+ */
+ if (function != PCICFG_ALL_FUNC)
return (PCICFG_FAILURE);
- }
+
+ for (i = 0; i < func; i++) {
+ if (max_function == PCICFG_MAX_ARI_FUNCTION)
+ trans_device = i >> 3;
+
+ if ((child_dip = pcicfg_devi_find(devi, trans_device,
+ i & 7)) == NULL) {
+ DEBUG0("No more devices to put back "
+ "on line!!\n");
+ /*
+ * Made it through all functions
+ */
+ continue;
+ }
+ if (ndi_devi_online(child_dip, NDI_CONFIG)
+ != NDI_SUCCESS) {
+ DEBUG0("Failed to put back devices state\n");
+ return (PCICFG_FAILURE);
+ }
}
return (PCICFG_FAILURE);
}
/*
- * Now, tear down all devinfo nodes for this AP.
+ * Now, tear down all devinfo nodes for this Connector.
*/
- for (func = 0; func < PCI_MAX_FUNCTIONS; func++) {
- if ((child_dip = pcicfg_devi_find(devi,
- device, func)) == NULL) {
- DEBUG2("No device at %x,%x\n", device, func);
+ for (func = 0; func < max_function; func++) {
+ if ((function != PCICFG_ALL_FUNC) && (function != func))
+ continue;
+
+ if (max_function == PCICFG_MAX_ARI_FUNCTION)
+ trans_device = func >> 3; /* ARI Device */
+ else
+ trans_device = device;
+
+ if ((child_dip = pcicfg_devi_find(devi, trans_device, func & 7))
+ == NULL) {
+ DEBUG2("No device at %x,%x\n", trans_device, func & 7);
continue;
}
DEBUG2("Tearing down device [0x%x] function [0x%x]\n",
- device, func);
+ trans_device, func & 7);
if (pcicfg_is_ntbridge(child_dip) != DDI_FAILURE)
if (pcicfg_ntbridge_unconfigure(child_dip) !=
- PCICFG_SUCCESS) {
+ PCICFG_SUCCESS) {
cmn_err(CE_WARN,
- "ntbridge: unconfigure failed\n");
+ "ntbridge: unconfigure failed\n");
return (PCICFG_FAILURE);
}
- if (pcicfg_teardown_device(child_dip) != PCICFG_SUCCESS) {
+ if (pcicfg_teardown_device(child_dip, flags)
+ != PCICFG_SUCCESS) {
DEBUG2("Failed to tear down device [0x%x]"
- "function [0x%x]\n",
- device, func);
+ "function [0x%x]\n", trans_device, func & 7);
return (PCICFG_FAILURE);
}
}
+
+ if (pcie_ari_is_enabled(devi) == PCIE_ARI_FORW_ENABLED) {
+ (void) ddi_prop_remove(DDI_DEV_T_NONE, devi, "ari-enabled");
+ (void) pcie_ari_disable(devi);
+ }
+
return (PCICFG_SUCCESS);
}
static int
-pcicfg_teardown_device(dev_info_t *dip)
+pcicfg_teardown_device(dev_info_t *dip, pcicfg_flags_t flags)
{
ddi_acc_handle_t handle;
/*
* Free up resources associated with 'dip'
*/
-
- if (pcicfg_free_resources(dip) != PCICFG_SUCCESS) {
+ if (pcicfg_free_resources(dip, flags) != PCICFG_SUCCESS) {
DEBUG0("Failed to free resources\n");
return (PCICFG_FAILURE);
}
@@ -1499,8 +1600,7 @@ pcicfg_create_phdl(dev_info_t *dip)
{
pcicfg_phdl_t *new;
- new = (pcicfg_phdl_t *)kmem_zalloc(sizeof (pcicfg_phdl_t),
- KM_SLEEP);
+ new = (pcicfg_phdl_t *)kmem_zalloc(sizeof (pcicfg_phdl_t), KM_SLEEP);
new->dip = dip;
mutex_enter(&pcicfg_list_mutex);
@@ -1519,7 +1619,7 @@ pcicfg_destroy_phdl(dev_info_t *dip)
mutex_enter(&pcicfg_list_mutex);
for (entry = pcicfg_phdl_list; entry != NULL; follow = entry,
- entry = entry->next) {
+ entry = entry->next) {
if (entry->dip == dip) {
if (entry == pcicfg_phdl_list) {
pcicfg_phdl_list = entry->next;
@@ -1533,26 +1633,22 @@ pcicfg_destroy_phdl(dev_info_t *dip)
*/
if (entry->memory_len > 0) {
(void) ndi_ra_free(ddi_get_parent(dip),
- entry->memory_base,
- entry->memory_len,
- NDI_RA_TYPE_MEM, NDI_RA_PASS);
+ entry->memory_base, entry->memory_len,
+ NDI_RA_TYPE_MEM, NDI_RA_PASS);
}
pcicfg_free_hole(&entry->mem_hole);
if (entry->io_len > 0) {
(void) ndi_ra_free(ddi_get_parent(dip),
- entry->io_base,
- entry->io_len,
- NDI_RA_TYPE_IO, NDI_RA_PASS);
+ entry->io_base, entry->io_len,
+ NDI_RA_TYPE_IO, NDI_RA_PASS);
}
pcicfg_free_hole(&entry->io_hole);
if (entry->pf_memory_len > 0) {
(void) ndi_ra_free(ddi_get_parent(dip),
- entry->pf_memory_base,
- entry->pf_memory_len,
- NDI_RA_TYPE_PCI_PREFETCH_MEM,
- NDI_RA_PASS);
+ entry->pf_memory_base, entry->pf_memory_len,
+ NDI_RA_TYPE_PCI_PREFETCH_MEM, NDI_RA_PASS);
}
pcicfg_free_hole(&entry->pf_mem_hole);
@@ -1592,8 +1688,7 @@ pcicfg_bridge_assign(dev_info_t *dip, void *hdl)
pcicfg_phdl_t *entry = (pcicfg_phdl_t *)hdl;
- DEBUG1("bridge assign: assigning addresses to %s\n",
- ddi_get_name(dip));
+ DEBUG1("bridge assign: assigning addresses to %s\n", ddi_get_name(dip));
entry->error = PCICFG_SUCCESS;
@@ -1603,8 +1698,7 @@ pcicfg_bridge_assign(dev_info_t *dip, void *hdl)
return (DDI_WALK_TERMINATE);
}
- if (pcicfg_config_setup(dip, &handle)
- != DDI_SUCCESS) {
+ if (pcicfg_config_setup(dip, &handle) != DDI_SUCCESS) {
DEBUG0("Failed to map config space!\n");
entry->error = PCICFG_FAILURE;
return (DDI_WALK_TERMINATE);
@@ -1614,27 +1708,25 @@ pcicfg_bridge_assign(dev_info_t *dip, void *hdl)
if ((header_type & PCI_HEADER_TYPE_M) == PCI_HEADER_PPB) {
- bzero((caddr_t)range,
- sizeof (ppb_ranges_t) * PCICFG_RANGE_LEN);
+ bzero((caddr_t)range, sizeof (ppb_ranges_t) * PCICFG_RANGE_LEN);
(void) pcicfg_setup_bridge(entry, handle);
range[0].child_high = range[0].parent_high |=
- (PCI_REG_REL_M | PCI_ADDR_IO);
- range[0].child_low = range[0].parent_low =
- entry->io_last;
+ (PCI_REG_REL_M | PCI_ADDR_IO);
+ range[0].child_low = range[0].parent_low = entry->io_last;
range[1].child_high = range[1].parent_high |=
- (PCI_REG_REL_M | PCI_ADDR_MEM32);
+ (PCI_REG_REL_M | PCI_ADDR_MEM32);
range[1].child_low = range[1].parent_low =
- entry->memory_last;
+ entry->memory_last;
range[2].child_high = range[2].parent_high |=
- (PCI_REG_REL_M | PCI_ADDR_MEM32 | PCI_REG_PF_M);
+ (PCI_REG_REL_M | PCI_ADDR_MEM32 | PCI_REG_PF_M);
range[2].child_low = range[2].parent_low =
- entry->pf_memory_last;
+ entry->pf_memory_last;
ndi_devi_enter(dip, &count);
ddi_walk_devs(ddi_get_child(dip),
- pcicfg_bridge_assign, (void *)entry);
+ pcicfg_bridge_assign, (void *)entry);
ndi_devi_exit(dip, count);
(void) pcicfg_update_bridge(entry, handle);
@@ -1643,7 +1735,7 @@ pcicfg_bridge_assign(dev_info_t *dip, void *hdl)
bus_range[1] = pci_config_get8(handle, PCI_BCNF_SUBBUS);
if (ndi_prop_update_int_array(DDI_DEV_T_NONE, dip,
- "bus-range", bus_range, 2) != DDI_SUCCESS) {
+ "bus-range", bus_range, 2) != DDI_SUCCESS) {
DEBUG0("Failed to set bus-range property");
entry->error = PCICFG_FAILURE;
(void) pcicfg_config_teardown(&handle);
@@ -1655,30 +1747,25 @@ pcicfg_bridge_assign(dev_info_t *dip, void *hdl)
* under the bridge.
*/
mem_residual = entry->memory_len -
- (entry->memory_last - entry->memory_base);
+ (entry->memory_last - entry->memory_base);
if (mem_residual > 0) {
(void) ndi_ra_free(ddi_get_parent(dip),
- entry->memory_last,
- mem_residual,
- NDI_RA_TYPE_MEM, NDI_RA_PASS);
+ entry->memory_last, mem_residual,
+ NDI_RA_TYPE_MEM, NDI_RA_PASS);
}
- io_residual = entry->io_len -
- (entry->io_last - entry->io_base);
+ io_residual = entry->io_len - (entry->io_last - entry->io_base);
if (io_residual > 0) {
- (void) ndi_ra_free(ddi_get_parent(dip),
- entry->io_last,
- io_residual,
- NDI_RA_TYPE_IO, NDI_RA_PASS);
+ (void) ndi_ra_free(ddi_get_parent(dip), entry->io_last,
+ io_residual, NDI_RA_TYPE_IO, NDI_RA_PASS);
}
pf_mem_residual = entry->pf_memory_len -
- (entry->pf_memory_last - entry->pf_memory_base);
+ (entry->pf_memory_last - entry->pf_memory_base);
if (pf_mem_residual > 0) {
(void) ndi_ra_free(ddi_get_parent(dip),
- entry->pf_memory_last,
- pf_mem_residual,
- NDI_RA_TYPE_PCI_PREFETCH_MEM, NDI_RA_PASS);
+ entry->pf_memory_last, pf_mem_residual,
+ NDI_RA_TYPE_PCI_PREFETCH_MEM, NDI_RA_PASS);
}
if (entry->io_len > 0) {
@@ -1692,7 +1779,7 @@ pcicfg_bridge_assign(dev_info_t *dip, void *hdl)
}
if (entry->memory_len > 0) {
range[1].size_low =
- entry->memory_last - entry->memory_base;
+ entry->memory_last - entry->memory_base;
if (pcicfg_update_ranges_prop(dip, &range[1])) {
DEBUG0("Failed to update ranges (memory)\n");
entry->error = PCICFG_FAILURE;
@@ -1702,7 +1789,7 @@ pcicfg_bridge_assign(dev_info_t *dip, void *hdl)
}
if (entry->pf_memory_len > 0) {
range[2].size_low =
- entry->pf_memory_last - entry->pf_memory_base;
+ entry->pf_memory_last - entry->pf_memory_base;
if (pcicfg_update_ranges_prop(dip, &range[2])) {
DEBUG0("Failed to update ranges (PF memory)\n");
entry->error = PCICFG_FAILURE;
@@ -1733,9 +1820,8 @@ pcicfg_bridge_assign(dev_info_t *dip, void *hdl)
* For each "reg" property with a length, allocate memory
* and program the base registers.
*/
- if (ddi_getlongprop(DDI_DEV_T_ANY, dip,
- DDI_PROP_DONTPASS, "reg", (caddr_t)&reg,
- &length) != DDI_PROP_SUCCESS) {
+ if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "reg",
+ (caddr_t)&reg, &length) != DDI_PROP_SUCCESS) {
DEBUG0("Failed to read reg property\n");
entry->error = PCICFG_FAILURE;
(void) pcicfg_config_teardown(&handle);
@@ -1745,8 +1831,7 @@ pcicfg_bridge_assign(dev_info_t *dip, void *hdl)
rcount = length / sizeof (pci_regspec_t);
offset = PCI_CONF_BASE0;
for (i = 0; i < rcount; i++) {
- if ((reg[i].pci_size_low != 0)||
- (reg[i].pci_size_hi != 0)) {
+ if ((reg[i].pci_size_low != 0) || (reg[i].pci_size_hi != 0)) {
offset = PCI_REG_REG_G(reg[i].pci_phys_hi);
@@ -1759,20 +1844,18 @@ pcicfg_bridge_assign(dev_info_t *dip, void *hdl)
reg[i].pci_size_low, &mem_answer);
} else { /* get non prefetchable memory */
pcicfg_get_mem(entry,
- reg[i].pci_size_low, &mem_answer);
+ reg[i].pci_size_low, &mem_answer);
}
pci_config_put64(handle, offset, mem_answer);
DEBUG2("REGISTER off %x (64)LO ----> [0x%x]\n",
- offset,
- pci_config_get32(handle, offset));
+ offset, pci_config_get32(handle, offset));
DEBUG2("REGISTER off %x (64)HI ----> [0x%x]\n",
- offset + 4,
- pci_config_get32(handle, offset + 4));
+ offset + 4,
+ pci_config_get32(handle, offset + 4));
reg[i].pci_phys_hi |= PCI_REG_REL_M;
reg[i].pci_phys_low = PCICFG_LOADDR(mem_answer);
- reg[i].pci_phys_mid =
- PCICFG_HIADDR(mem_answer);
+ reg[i].pci_phys_mid = PCICFG_HIADDR(mem_answer);
break;
case PCI_REG_ADDR_G(PCI_ADDR_MEM32):
@@ -1783,15 +1866,14 @@ pcicfg_bridge_assign(dev_info_t *dip, void *hdl)
} else {
/* get non prefetchable memory */
pcicfg_get_mem(entry,
- reg[i].pci_size_low, &mem_answer);
+ reg[i].pci_size_low, &mem_answer);
}
- pci_config_put32(handle,
- offset, (uint32_t)mem_answer);
+ pci_config_put32(handle, offset,
+ (uint32_t)mem_answer);
DEBUG2("REGISTER off %x(32)LO ----> [0x%x]\n",
- offset,
- pci_config_get32(handle, offset));
+ offset, pci_config_get32(handle, offset));
reg[i].pci_phys_hi |= PCI_REG_REL_M;
reg[i].pci_phys_low = (uint32_t)mem_answer;
@@ -1800,13 +1882,12 @@ pcicfg_bridge_assign(dev_info_t *dip, void *hdl)
case PCI_REG_ADDR_G(PCI_ADDR_IO):
/* allocate I/O space from the allocator */
- (void) pcicfg_get_io(entry,
- reg[i].pci_size_low, &io_answer);
+ (void) pcicfg_get_io(entry, reg[i].pci_size_low,
+ &io_answer);
pci_config_put32(handle, offset, io_answer);
DEBUG2("REGISTER off %x (I/O)LO ----> [0x%x]\n",
- offset,
- pci_config_get32(handle, offset));
+ offset, pci_config_get32(handle, offset));
reg[i].pci_phys_hi |= PCI_REG_REL_M;
reg[i].pci_phys_low = io_answer;
@@ -1824,8 +1905,8 @@ pcicfg_bridge_assign(dev_info_t *dip, void *hdl)
* Now that memory locations are assigned,
* update the assigned address property.
*/
- if (pcicfg_update_assigned_prop(dip,
- &reg[i]) != PCICFG_SUCCESS) {
+ if (pcicfg_update_assigned_prop(dip, &reg[i])
+ != PCICFG_SUCCESS) {
kmem_free(reg, length);
(void) pcicfg_config_teardown(&handle);
entry->error = PCICFG_FAILURE;
@@ -1855,7 +1936,6 @@ pcicfg_device_assign(dev_info_t *dip)
uint64_t answer;
uint64_t alen;
-
DEBUG1("%llx now under configuration\n", dip);
/* request.ra_len = PCICFG_ROUND_UP(request.ra_len, PCICFG_IOGRAN); */
@@ -1866,9 +1946,8 @@ pcicfg_device_assign(dev_info_t *dip)
/*
* XXX Failure here should be noted
*/
- if (ddi_getlongprop(DDI_DEV_T_ANY, dip,
- DDI_PROP_DONTPASS, "reg", (caddr_t)&reg,
- &length) != DDI_PROP_SUCCESS) {
+ if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "reg",
+ (caddr_t)&reg, &length) != DDI_PROP_SUCCESS) {
DEBUG0("Failed to read reg property\n");
return (PCICFG_FAILURE);
}
@@ -1911,8 +1990,7 @@ pcicfg_device_assign(dev_info_t *dip)
for (i = 0; i < rcount; i++) {
char *mem_type;
- if ((reg[i].pci_size_low != 0)||
- (reg[i].pci_size_hi != 0)) {
+ if ((reg[i].pci_size_low != 0)|| (reg[i].pci_size_hi != 0)) {
offset = PCI_REG_REG_G(reg[i].pci_phys_hi);
request.ra_len = reg[i].pci_size_low;
@@ -1920,29 +1998,28 @@ pcicfg_device_assign(dev_info_t *dip)
switch (PCI_REG_ADDR_G(reg[i].pci_phys_hi)) {
case PCI_REG_ADDR_G(PCI_ADDR_MEM64):
if (reg[i].pci_phys_hi & PCI_REG_PF_M) {
- mem_type = NDI_RA_TYPE_PCI_PREFETCH_MEM;
+ mem_type = NDI_RA_TYPE_PCI_PREFETCH_MEM;
} else {
- mem_type = NDI_RA_TYPE_MEM;
+ mem_type = NDI_RA_TYPE_MEM;
}
/* allocate memory space from the allocator */
- if (ndi_ra_alloc(ddi_get_parent(dip),
- &request, &answer, &alen,
- mem_type, NDI_RA_PASS) != NDI_SUCCESS) {
+ if (ndi_ra_alloc(ddi_get_parent(dip), &request,
+ &answer, &alen, mem_type, NDI_RA_PASS)
+ != NDI_SUCCESS) {
DEBUG0("Failed to allocate 64b mem\n");
kmem_free(reg, length);
(void) pcicfg_config_teardown(&handle);
return (PCICFG_NORESRC);
}
DEBUG3("64 addr = [0x%x.0x%x] len [0x%x]\n",
- PCICFG_HIADDR(answer),
- PCICFG_LOADDR(answer),
- alen);
+ PCICFG_HIADDR(answer),
+ PCICFG_LOADDR(answer), alen);
/* program the low word */
- pci_config_put32(handle,
- offset, PCICFG_LOADDR(answer));
+ pci_config_put32(handle, offset,
+ PCICFG_LOADDR(answer));
/* program the high word */
pci_config_put32(handle, offset + 4,
- PCICFG_HIADDR(answer));
+ PCICFG_HIADDR(answer));
reg[i].pci_phys_hi |= PCI_REG_REL_M;
reg[i].pci_phys_low = PCICFG_LOADDR(answer);
@@ -1951,8 +2028,8 @@ pcicfg_device_assign(dev_info_t *dip)
* currently support 32b address space
* assignments only.
*/
- reg[i].pci_phys_hi ^= PCI_ADDR_MEM64 ^
- PCI_ADDR_MEM32;
+ reg[i].pci_phys_hi ^=
+ PCI_ADDR_MEM64 ^ PCI_ADDR_MEM32;
offset += 8;
break;
@@ -1963,21 +2040,21 @@ pcicfg_device_assign(dev_info_t *dip)
else
mem_type = NDI_RA_TYPE_MEM;
/* allocate memory space from the allocator */
- if (ndi_ra_alloc(ddi_get_parent(dip),
- &request, &answer, &alen,
- mem_type, NDI_RA_PASS) != NDI_SUCCESS) {
+ if (ndi_ra_alloc(ddi_get_parent(dip), &request,
+ &answer, &alen, mem_type, NDI_RA_PASS)
+ != NDI_SUCCESS) {
DEBUG0("Failed to allocate 32b mem\n");
kmem_free(reg, length);
(void) pcicfg_config_teardown(&handle);
return (PCICFG_NORESRC);
}
DEBUG3("32 addr = [0x%x.0x%x] len [0x%x]\n",
- PCICFG_HIADDR(answer),
- PCICFG_LOADDR(answer),
- alen);
+ PCICFG_HIADDR(answer),
+ PCICFG_LOADDR(answer),
+ alen);
/* program the low word */
- pci_config_put32(handle,
- offset, PCICFG_LOADDR(answer));
+ pci_config_put32(handle, offset,
+ PCICFG_LOADDR(answer));
reg[i].pci_phys_hi |= PCI_REG_REL_M;
reg[i].pci_phys_low = PCICFG_LOADDR(answer);
@@ -1987,21 +2064,19 @@ pcicfg_device_assign(dev_info_t *dip)
break;
case PCI_REG_ADDR_G(PCI_ADDR_IO):
/* allocate I/O space from the allocator */
- if (ndi_ra_alloc(ddi_get_parent(dip),
- &request, &answer, &alen,
- NDI_RA_TYPE_IO, NDI_RA_PASS)
- != NDI_SUCCESS) {
+ if (ndi_ra_alloc(ddi_get_parent(dip), &request,
+ &answer, &alen, NDI_RA_TYPE_IO, NDI_RA_PASS)
+ != NDI_SUCCESS) {
DEBUG0("Failed to allocate I/O\n");
kmem_free(reg, length);
(void) pcicfg_config_teardown(&handle);
return (PCICFG_NORESRC);
}
DEBUG3("I/O addr = [0x%x.0x%x] len [0x%x]\n",
- PCICFG_HIADDR(answer),
- PCICFG_LOADDR(answer),
- alen);
- pci_config_put32(handle,
- offset, PCICFG_LOADDR(answer));
+ PCICFG_HIADDR(answer),
+ PCICFG_LOADDR(answer), alen);
+ pci_config_put32(handle, offset,
+ PCICFG_LOADDR(answer));
reg[i].pci_phys_hi |= PCI_REG_REL_M;
reg[i].pci_phys_low = PCICFG_LOADDR(answer);
@@ -2020,8 +2095,8 @@ pcicfg_device_assign(dev_info_t *dip)
* update the assigned address property.
*/
- if (pcicfg_update_assigned_prop(dip,
- &reg[i]) != PCICFG_SUCCESS) {
+ if (pcicfg_update_assigned_prop(dip, &reg[i])
+ != PCICFG_SUCCESS) {
kmem_free(reg, length);
(void) pcicfg_config_teardown(&handle);
return (PCICFG_FAILURE);
@@ -2038,6 +2113,138 @@ pcicfg_device_assign(dev_info_t *dip)
return (PCICFG_SUCCESS);
}
+static int
+pcicfg_device_assign_readonly(dev_info_t *dip)
+{
+ ddi_acc_handle_t handle;
+ pci_regspec_t *assigned;
+ int length;
+ int acount;
+ int i;
+ ndi_ra_request_t request;
+ uint64_t answer;
+ uint64_t alen;
+
+ DEBUG1("%llx now under configuration\n", dip);
+
+ /*
+ * we don't support ntbridges for readonly probe.
+ */
+ if (pcicfg_ntbridge_child(dip) == DDI_SUCCESS) {
+ return (PCICFG_FAILURE);
+ }
+
+ if (ddi_getlongprop(DDI_DEV_T_ANY, dip,
+ DDI_PROP_DONTPASS, "assigned-addresses", (caddr_t)&assigned,
+ &length) != DDI_PROP_SUCCESS) {
+ DEBUG0("Failed to read assigned-addresses property\n");
+ return (PCICFG_FAILURE);
+ }
+
+ if (pcicfg_config_setup(dip, &handle) != DDI_SUCCESS) {
+ DEBUG0("Failed to map config space!\n");
+ kmem_free(assigned, length);
+ return (PCICFG_FAILURE);
+ }
+
+ /*
+ * If there is an interrupt pin set program
+ * interrupt line with default values.
+ */
+ if (pci_config_get8(handle, PCI_CONF_IPIN)) {
+ pci_config_put8(handle, PCI_CONF_ILINE, 0xf);
+ }
+ /*
+ * Note: Both non-prefetchable and prefetchable memory space
+ * allocations are made within 32bit space. Currently, BIOSs
+ * allocate device memory for PCI devices within the 32bit space
+ * so this will not be a problem.
+ */
+ bzero((caddr_t)&request, sizeof (ndi_ra_request_t));
+
+ request.ra_flags = NDI_RA_ALLOC_SPECIFIED; /* specified addr */
+ request.ra_boundbase = 0;
+ request.ra_boundlen = PCICFG_4GIG_LIMIT;
+
+ acount = length / sizeof (pci_regspec_t);
+ for (i = 0; i < acount; i++) {
+ char *mem_type;
+
+ if ((assigned[i].pci_size_low != 0)||
+ (assigned[i].pci_size_hi != 0)) {
+
+ request.ra_len = assigned[i].pci_size_low;
+
+ switch (PCI_REG_ADDR_G(assigned[i].pci_phys_hi)) {
+ case PCI_REG_ADDR_G(PCI_ADDR_MEM64):
+ request.ra_addr = (uint64_t)PCICFG_LADDR(
+ assigned[i].pci_phys_low,
+ assigned[i].pci_phys_mid);
+
+ if (assigned[i].pci_phys_hi & PCI_REG_PF_M) {
+ mem_type = NDI_RA_TYPE_PCI_PREFETCH_MEM;
+ } else {
+ mem_type = NDI_RA_TYPE_MEM;
+ }
+ /* allocate memory space from the allocator */
+ if (ndi_ra_alloc(ddi_get_parent(dip), &request,
+ &answer, &alen, mem_type, NDI_RA_PASS)
+ != NDI_SUCCESS) {
+ DEBUG0("Failed to allocate 64b mem\n");
+ kmem_free(assigned, length);
+ return (PCICFG_NORESRC);
+ }
+
+ break;
+ case PCI_REG_ADDR_G(PCI_ADDR_MEM32):
+ request.ra_addr = (uint64_t)
+ assigned[i].pci_phys_low;
+
+ if (assigned[i].pci_phys_hi & PCI_REG_PF_M)
+ mem_type = NDI_RA_TYPE_PCI_PREFETCH_MEM;
+ else
+ mem_type = NDI_RA_TYPE_MEM;
+ /* allocate memory space from the allocator */
+ if (ndi_ra_alloc(ddi_get_parent(dip), &request,
+ &answer, &alen, mem_type, NDI_RA_PASS)
+ != NDI_SUCCESS) {
+ DEBUG0("Failed to allocate 32b mem\n");
+ kmem_free(assigned, length);
+ return (PCICFG_NORESRC);
+ }
+
+ break;
+ case PCI_REG_ADDR_G(PCI_ADDR_IO):
+ request.ra_addr = (uint64_t)
+ assigned[i].pci_phys_low;
+
+ /* allocate I/O space from the allocator */
+ if (ndi_ra_alloc(ddi_get_parent(dip), &request,
+ &answer, &alen, NDI_RA_TYPE_IO, NDI_RA_PASS)
+ != NDI_SUCCESS) {
+ DEBUG0("Failed to allocate I/O\n");
+ kmem_free(assigned, length);
+ return (PCICFG_NORESRC);
+ }
+
+ break;
+ default:
+ DEBUG0("Unknown register type\n");
+ kmem_free(assigned, length);
+ return (PCICFG_FAILURE);
+ } /* switch */
+ }
+ }
+
+ (void) pcicfg_device_on(handle);
+ kmem_free(assigned, length);
+
+ PCICFG_DUMP_DEVICE_CONFIG(handle);
+
+ (void) pcicfg_config_teardown(&handle);
+ return (PCICFG_SUCCESS);
+}
+
#ifdef DEBUG
/*
* This function is useful in debug mode, where we can measure how
@@ -2086,7 +2293,7 @@ pcicfg_alloc_hole(hole_t *addr_hole, uint64_t *alast, uint32_t length)
actual_hole_start = PCICFG_ROUND_UP(hole->start, length);
if (((actual_hole_start - hole->start) + length) <= hole->len) {
DEBUG3("hole found. start %llx, len %llx, req=0x%x\n",
- hole->start, hole->len, length);
+ hole->start, hole->len, length);
ostart = hole->start;
olen = hole->len;
/* current hole parameters adjust */
@@ -2098,7 +2305,7 @@ pcicfg_alloc_hole(hole_t *addr_hole, uint64_t *alast, uint32_t length)
} else {
hole->len = actual_hole_start - hole->start;
nhole = (hole_t *)kmem_zalloc(sizeof (hole_t),
- KM_SLEEP);
+ KM_SLEEP);
nhole->start = actual_hole_start + length;
nhole->len = (ostart + olen) - nhole->start;
nhole->next = NULL;
@@ -2108,10 +2315,10 @@ pcicfg_alloc_hole(hole_t *addr_hole, uint64_t *alast, uint32_t length)
if (nhole->start > *alast)
*alast = nhole->start;
DEBUG2("put new hole to %llx, %llx\n",
- nhole->start, nhole->len);
+ nhole->start, nhole->len);
}
DEBUG2("adjust current hole to %llx, %llx\n",
- hole->start, hole->len);
+ hole->start, hole->len);
break;
}
actual_hole_start = 0;
@@ -2123,20 +2330,19 @@ pcicfg_alloc_hole(hole_t *addr_hole, uint64_t *alast, uint32_t length)
}
static void
-pcicfg_get_mem(pcicfg_phdl_t *entry,
- uint32_t length, uint64_t *ans)
+pcicfg_get_mem(pcicfg_phdl_t *entry, uint32_t length, uint64_t *ans)
{
uint64_t new_mem;
/* See if there is a hole, that can hold this request. */
new_mem = pcicfg_alloc_hole(&entry->mem_hole, &entry->memory_last,
- length);
+ length);
if (new_mem) { /* if non-zero, found a hole. */
if (ans != NULL)
*ans = new_mem;
} else
cmn_err(CE_WARN, "No %u bytes memory window for %s\n",
- length, ddi_get_name(entry->dip));
+ length, ddi_get_name(entry->dip));
}
static void
@@ -2158,24 +2364,23 @@ pcicfg_get_io(pcicfg_phdl_t *entry,
*ans = new_io;
} else
cmn_err(CE_WARN, "No %u bytes IO space window for %s\n",
- length, ddi_get_name(entry->dip));
+ length, ddi_get_name(entry->dip));
}
static void
-pcicfg_get_pf_mem(pcicfg_phdl_t *entry,
- uint32_t length, uint64_t *ans)
+pcicfg_get_pf_mem(pcicfg_phdl_t *entry, uint32_t length, uint64_t *ans)
{
uint64_t new_mem;
/* See if there is a hole, that can hold this request. */
new_mem = pcicfg_alloc_hole(&entry->pf_mem_hole, &entry->pf_memory_last,
- length);
+ length);
if (new_mem) { /* if non-zero, found a hole. */
if (ans != NULL)
*ans = new_mem;
} else
cmn_err(CE_WARN, "No %u bytes PF memory window for %s\n",
- length, ddi_get_name(entry->dip));
+ length, ddi_get_name(entry->dip));
}
static int
@@ -2212,17 +2417,16 @@ pcicfg_sum_resources(dev_info_t *dip, void *hdl)
if ((header_type & PCI_HEADER_TYPE_M) == PCI_HEADER_PPB) {
if (entry->highest_bus < pci_config_get8(handle,
- PCI_BCNF_SECBUS)) {
+ PCI_BCNF_SECBUS)) {
entry->highest_bus =
- pci_config_get8(handle, PCI_BCNF_SECBUS);
+ pci_config_get8(handle, PCI_BCNF_SECBUS);
}
(void) pcicfg_config_teardown(&handle);
entry->error = PCICFG_FAILURE;
return (DDI_WALK_CONTINUE);
} else {
- if (ddi_getlongprop(DDI_DEV_T_ANY, dip,
- DDI_PROP_DONTPASS, "reg", (caddr_t)&pci_rp,
- &length) != DDI_PROP_SUCCESS) {
+ if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
+ "reg", (caddr_t)&pci_rp, &length) != DDI_PROP_SUCCESS) {
/*
* If one node in (the subtree of nodes)
* doesn't have a "reg" property fail the
@@ -2247,51 +2451,53 @@ pcicfg_sum_resources(dev_info_t *dip, void *hdl)
case PCI_REG_ADDR_G(PCI_ADDR_MEM32):
if (pci_rp[i].pci_phys_hi & PCI_REG_PF_M) {
- pf_mem_request->ra_len =
- pci_rp[i].pci_size_low +
- PCICFG_ROUND_UP(pf_mem_request->ra_len,
- pci_rp[i].pci_size_low);
- DEBUG1("ADDING 32 --->0x%x\n",
- pci_rp[i].pci_size_low);
+ pf_mem_request->ra_len =
+ pci_rp[i].pci_size_low +
+ PCICFG_ROUND_UP(
+ pf_mem_request->ra_len,
+ pci_rp[i].pci_size_low);
+ DEBUG1("ADDING 32 --->0x%x\n",
+ pci_rp[i].pci_size_low);
} else {
- mem_request->ra_len =
- pci_rp[i].pci_size_low +
- PCICFG_ROUND_UP(mem_request->ra_len,
- pci_rp[i].pci_size_low);
- DEBUG1("ADDING 32 --->0x%x\n",
- pci_rp[i].pci_size_low);
+ mem_request->ra_len =
+ pci_rp[i].pci_size_low +
+ PCICFG_ROUND_UP(mem_request->ra_len,
+ pci_rp[i].pci_size_low);
+ DEBUG1("ADDING 32 --->0x%x\n",
+ pci_rp[i].pci_size_low);
}
- break;
+ break;
case PCI_REG_ADDR_G(PCI_ADDR_MEM64):
if (pci_rp[i].pci_phys_hi & PCI_REG_PF_M) {
- pf_mem_request->ra_len =
- pci_rp[i].pci_size_low +
- PCICFG_ROUND_UP(pf_mem_request->ra_len,
- pci_rp[i].pci_size_low);
- DEBUG1("ADDING 64 --->0x%x\n",
- pci_rp[i].pci_size_low);
+ pf_mem_request->ra_len =
+ pci_rp[i].pci_size_low +
+ PCICFG_ROUND_UP(
+ pf_mem_request->ra_len,
+ pci_rp[i].pci_size_low);
+ DEBUG1("ADDING 64 --->0x%x\n",
+ pci_rp[i].pci_size_low);
} else {
- mem_request->ra_len =
- pci_rp[i].pci_size_low +
- PCICFG_ROUND_UP(mem_request->ra_len,
- pci_rp[i].pci_size_low);
- DEBUG1("ADDING 64 --->0x%x\n",
- pci_rp[i].pci_size_low);
+ mem_request->ra_len =
+ pci_rp[i].pci_size_low +
+ PCICFG_ROUND_UP(mem_request->ra_len,
+ pci_rp[i].pci_size_low);
+ DEBUG1("ADDING 64 --->0x%x\n",
+ pci_rp[i].pci_size_low);
}
- break;
+ break;
case PCI_REG_ADDR_G(PCI_ADDR_IO):
io_request->ra_len =
- pci_rp[i].pci_size_low +
- PCICFG_ROUND_UP(io_request->ra_len,
- pci_rp[i].pci_size_low);
+ pci_rp[i].pci_size_low +
+ PCICFG_ROUND_UP(io_request->ra_len,
+ pci_rp[i].pci_size_low);
DEBUG1("ADDING I/O --->0x%x\n",
- pci_rp[i].pci_size_low);
- break;
+ pci_rp[i].pci_size_low);
+ break;
default:
- /* Config space register - not included */
- break;
+ /* Config space register - not included */
+ break;
}
}
@@ -2320,13 +2526,12 @@ pcicfg_free_bridge_resources(dev_info_t *dip)
int i;
- if ((i = ddi_getlongprop(DDI_DEV_T_ANY, dip,
- DDI_PROP_DONTPASS, "ranges", (caddr_t)&ranges,
- &length)) != DDI_PROP_SUCCESS) {
+ if ((i = ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
+ "ranges", (caddr_t)&ranges, &length)) != DDI_PROP_SUCCESS) {
DEBUG0("Failed to read ranges property\n");
if (ddi_get_child(dip)) {
cmn_err(CE_WARN, "No ranges property found for %s",
- ddi_get_name(dip));
+ ddi_get_name(dip));
/*
* strictly speaking, we can check for children with
* assigned-addresses but for now it is better to
@@ -2342,57 +2547,53 @@ pcicfg_free_bridge_resources(dev_info_t *dip)
for (i = 0; i < length / sizeof (ppb_ranges_t); i++) {
char *mem_type;
- if (ranges[i].size_low != 0 ||
- ranges[i].size_high != 0) {
+ if (ranges[i].size_low != 0 || ranges[i].size_high != 0) {
switch (ranges[i].parent_high & PCI_REG_ADDR_M) {
- case PCI_ADDR_IO:
- DEBUG2("Free I/O "
- "base/length = [0x%x]/[0x%x]\n",
- ranges[i].child_low,
- ranges[i].size_low);
- if (ndi_ra_free(ddi_get_parent(dip),
- (uint64_t)ranges[i].child_low,
- (uint64_t)ranges[i].size_low,
- NDI_RA_TYPE_IO, NDI_RA_PASS)
- != NDI_SUCCESS) {
- DEBUG0("Trouble freeing "
- "PCI i/o space\n");
- kmem_free(ranges, length);
- return (PCICFG_FAILURE);
- }
+ case PCI_ADDR_IO:
+ DEBUG2("Free I/O base/length = "
+ "[0x%x]/[0x%x]\n", ranges[i].child_low,
+ ranges[i].size_low);
+ if (ndi_ra_free(ddi_get_parent(dip),
+ (uint64_t)ranges[i].child_low,
+ (uint64_t)ranges[i].size_low,
+ NDI_RA_TYPE_IO, NDI_RA_PASS)
+ != NDI_SUCCESS) {
+ DEBUG0("Trouble freeing "
+ "PCI i/o space\n");
+ kmem_free(ranges, length);
+ return (PCICFG_FAILURE);
+ }
break;
- case PCI_ADDR_MEM32:
- case PCI_ADDR_MEM64:
- if (ranges[i].parent_high & PCI_REG_PF_M) {
- DEBUG3("Free PF Memory base/length"
- " = [0x%x.0x%x]/[0x%x]\n",
- ranges[i].child_mid,
- ranges[i].child_low,
- ranges[i].size_low)
+ case PCI_ADDR_MEM32:
+ case PCI_ADDR_MEM64:
+ if (ranges[i].parent_high & PCI_REG_PF_M) {
+ DEBUG3("Free PF Memory base/length = "
+ "[0x%x.0x%x]/[0x%x]\n",
+ ranges[i].child_mid,
+ ranges[i].child_low,
+ ranges[i].size_low);
mem_type = NDI_RA_TYPE_PCI_PREFETCH_MEM;
- } else {
+ } else {
DEBUG3("Free Memory base/length"
- " = [0x%x.0x%x]/[0x%x]\n",
- ranges[i].child_mid,
- ranges[i].child_low,
- ranges[i].size_low)
+ " = [0x%x.0x%x]/[0x%x]\n",
+ ranges[i].child_mid,
+ ranges[i].child_low,
+ ranges[i].size_low)
mem_type = NDI_RA_TYPE_MEM;
- }
- if (ndi_ra_free(ddi_get_parent(dip),
- PCICFG_LADDR(
- ranges[i].child_low,
- ranges[i].child_mid),
- (uint64_t)ranges[i].size_low,
- mem_type,
- NDI_RA_PASS) != NDI_SUCCESS) {
- DEBUG0("Trouble freeing "
- "PCI memory space\n");
- kmem_free(ranges, length);
- return (PCICFG_FAILURE);
- }
+ }
+ if (ndi_ra_free(ddi_get_parent(dip),
+ PCICFG_LADDR(ranges[i].child_low,
+ ranges[i].child_mid),
+ (uint64_t)ranges[i].size_low,
+ mem_type, NDI_RA_PASS) != NDI_SUCCESS) {
+ DEBUG0("Trouble freeing "
+ "PCI memory space\n");
+ kmem_free(ranges, length);
+ return (PCICFG_FAILURE);
+ }
break;
- default:
- DEBUG0("Unknown memory space\n");
+ default:
+ DEBUG0("Unknown memory space\n");
break;
}
}
@@ -2401,19 +2602,18 @@ pcicfg_free_bridge_resources(dev_info_t *dip)
if (length)
kmem_free(ranges, length);
- if (ddi_getlongprop(DDI_DEV_T_ANY, dip,
- DDI_PROP_DONTPASS, "bus-range", (caddr_t)&bus,
- &k) != DDI_PROP_SUCCESS) {
+ if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
+ "bus-range", (caddr_t)&bus, &k) != DDI_PROP_SUCCESS) {
DEBUG0("Failed to read bus-range property\n");
return (PCICFG_FAILURE);
}
DEBUG2("Need to free bus [%d] range [%d]\n",
- bus[0], bus[1] - bus[0] + 1);
+ bus[0], bus[1] - bus[0] + 1);
- if (ndi_ra_free(ddi_get_parent(dip),
- (uint64_t)bus[0], (uint64_t)(bus[1] - bus[0] + 1),
- NDI_RA_TYPE_PCI_BUSNUM, NDI_RA_PASS) != NDI_SUCCESS) {
+ if (ndi_ra_free(ddi_get_parent(dip), (uint64_t)bus[0],
+ (uint64_t)(bus[1] - bus[0] + 1), NDI_RA_TYPE_PCI_BUSNUM,
+ NDI_RA_PASS) != NDI_SUCCESS) {
DEBUG0("Failed to free a bus number\n");
kmem_free(bus, k);
return (PCICFG_FAILURE);
@@ -2432,9 +2632,9 @@ pcicfg_free_device_resources(dev_info_t *dip)
int acount;
int i;
- if (ddi_getlongprop(DDI_DEV_T_ANY, dip,
- DDI_PROP_DONTPASS, "assigned-addresses", (caddr_t)&assigned,
- &length) != DDI_PROP_SUCCESS) {
+ if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
+ "assigned-addresses", (caddr_t)&assigned, &length)
+ != DDI_PROP_SUCCESS) {
DEBUG0("Failed to read assigned-addresses property\n");
return (PCICFG_FAILURE);
}
@@ -2452,7 +2652,7 @@ pcicfg_free_device_resources(dev_info_t *dip)
* Free the resource if the size of it is not zero.
*/
if ((assigned[i].pci_size_low != 0)||
- (assigned[i].pci_size_hi != 0)) {
+ (assigned[i].pci_size_hi != 0)) {
switch (PCI_REG_ADDR_G(assigned[i].pci_phys_hi)) {
case PCI_REG_ADDR_G(PCI_ADDR_MEM32):
/*
@@ -2476,17 +2676,16 @@ pcicfg_free_device_resources(dev_info_t *dip)
(uint64_t)assigned[i].pci_size_low,
mem_type, NDI_RA_PASS) != NDI_SUCCESS) {
DEBUG0("Trouble freeing "
- "PCI memory space\n");
+ "PCI memory space\n");
kmem_free(assigned, length);
return (PCICFG_FAILURE);
}
DEBUG4("Returned 0x%x of 32 bit %s space"
- " @ 0x%x from register 0x%x\n",
- assigned[i].pci_size_low,
- mem_type,
- assigned[i].pci_phys_low,
- PCI_REG_REG_G(assigned[i].pci_phys_hi));
+ " @ 0x%x from register 0x%x\n",
+ assigned[i].pci_size_low, mem_type,
+ assigned[i].pci_phys_low,
+ PCI_REG_REG_G(assigned[i].pci_phys_hi));
break;
case PCI_REG_ADDR_G(PCI_ADDR_MEM64):
@@ -2501,18 +2700,17 @@ pcicfg_free_device_resources(dev_info_t *dip)
(uint64_t)assigned[i].pci_size_low,
mem_type, NDI_RA_PASS) != NDI_SUCCESS) {
DEBUG0("Trouble freeing "
- "PCI memory space\n");
+ "PCI memory space\n");
kmem_free(assigned, length);
return (PCICFG_FAILURE);
}
DEBUG5("Returned 0x%x of 64 bit %s space"
- " @ 0x%x.0x%x from register 0x%x\n",
- assigned[i].pci_size_low,
- mem_type,
- assigned[i].pci_phys_mid,
- assigned[i].pci_phys_low,
- PCI_REG_REG_G(assigned[i].pci_phys_hi));
+ " @ 0x%x.0x%x from register 0x%x\n",
+ assigned[i].pci_size_low,
+ mem_type, assigned[i].pci_phys_mid,
+ assigned[i].pci_phys_low,
+ PCI_REG_REG_G(assigned[i].pci_phys_hi));
break;
case PCI_REG_ADDR_G(PCI_ADDR_IO):
@@ -2522,15 +2720,14 @@ pcicfg_free_device_resources(dev_info_t *dip)
NDI_RA_TYPE_IO, NDI_RA_PASS) !=
NDI_SUCCESS) {
DEBUG0("Trouble freeing "
- "PCI IO space\n");
+ "PCI IO space\n");
kmem_free(assigned, length);
return (PCICFG_FAILURE);
}
- DEBUG3("Returned 0x%x of IO space @ 0x%x"
- " from register 0x%x\n",
- assigned[i].pci_size_low,
- assigned[i].pci_phys_low,
- PCI_REG_REG_G(assigned[i].pci_phys_hi));
+ DEBUG3("Returned 0x%x of IO space @ 0x%x from "
+ "register 0x%x\n", assigned[i].pci_size_low,
+ assigned[i].pci_phys_low,
+ PCI_REG_REG_G(assigned[i].pci_phys_hi));
break;
default:
DEBUG0("Unknown register type\n");
@@ -2544,7 +2741,7 @@ pcicfg_free_device_resources(dev_info_t *dip)
}
static int
-pcicfg_free_resources(dev_info_t *dip)
+pcicfg_free_resources(dev_info_t *dip, pcicfg_flags_t flags)
{
ddi_acc_handle_t handle;
uint8_t header_type;
@@ -2562,6 +2759,12 @@ pcicfg_free_resources(dev_info_t *dip)
* A different algorithm is used for bridges and leaf devices.
*/
if ((header_type & PCI_HEADER_TYPE_M) == PCI_HEADER_PPB) {
+ /*
+ * We only support readonly probing for leaf devices.
+ */
+ if (flags & PCICFG_FLAG_READ_ONLY)
+ return (PCICFG_FAILURE);
+
if (pcicfg_free_bridge_resources(dip) != PCICFG_SUCCESS) {
DEBUG0("Failed freeing up bridge resources\n");
return (PCICFG_FAILURE);
@@ -2617,9 +2820,8 @@ pcicfg_match_dev(dev_info_t *dip, void *hdl)
int pci_dev;
int pci_func;
- if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip,
- DDI_PROP_DONTPASS, "reg", (int **)&pci_rp,
- (uint_t *)&length) != DDI_PROP_SUCCESS) {
+ if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
+ "reg", (int **)&pci_rp, (uint_t *)&length) != DDI_PROP_SUCCESS) {
ctrl->dip = NULL;
return (DDI_WALK_TERMINATE);
}
@@ -2655,7 +2857,7 @@ pcicfg_update_assigned_prop(dev_info_t *dip, pci_regspec_t *newone)
uint_t status;
status = ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
- "assigned-addresses", (caddr_t)&assigned, &alen);
+ "assigned-addresses", (caddr_t)&assigned, &alen);
switch (status) {
case DDI_PROP_SUCCESS:
break;
@@ -2664,8 +2866,8 @@ pcicfg_update_assigned_prop(dev_info_t *dip, pci_regspec_t *newone)
return (PCICFG_FAILURE);
default:
(void) ndi_prop_update_int_array(DDI_DEV_T_NONE, dip,
- "assigned-addresses", (int *)newone,
- sizeof (*newone)/sizeof (int));
+ "assigned-addresses", (int *)newone,
+ sizeof (*newone)/sizeof (int));
return (PCICFG_SUCCESS);
}
@@ -2684,8 +2886,8 @@ pcicfg_update_assigned_prop(dev_info_t *dip, pci_regspec_t *newone)
* Write out the new "assigned-addresses" spec
*/
(void) ndi_prop_update_int_array(DDI_DEV_T_NONE, dip,
- "assigned-addresses", (int *)newreg,
- (alen + sizeof (*newone))/sizeof (int));
+ "assigned-addresses", (int *)newreg,
+ (alen + sizeof (*newone))/sizeof (int));
kmem_free((caddr_t)newreg, alen+sizeof (*newone));
kmem_free(assigned, alen);
@@ -2701,8 +2903,8 @@ pcicfg_update_ranges_prop(dev_info_t *dip, ppb_ranges_t *addition)
caddr_t newreg;
uint_t status;
- status = ddi_getlongprop(DDI_DEV_T_ANY,
- dip, DDI_PROP_DONTPASS, "ranges", (caddr_t)&ranges, &rlen);
+ status = ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
+ "ranges", (caddr_t)&ranges, &rlen);
switch (status) {
@@ -2714,9 +2916,9 @@ pcicfg_update_ranges_prop(dev_info_t *dip, ppb_ranges_t *addition)
default:
DEBUG0("no ranges property - creating one\n");
if (ndi_prop_update_int_array(DDI_DEV_T_NONE,
- dip, "ranges", (int *)addition,
- sizeof (ppb_ranges_t)/sizeof (int))
- != DDI_SUCCESS) {
+ dip, "ranges", (int *)addition,
+ sizeof (ppb_ranges_t)/sizeof (int))
+ != DDI_SUCCESS) {
DEBUG0("Did'nt create ranges property\n");
return (PCICFG_FAILURE);
}
@@ -2735,12 +2937,11 @@ pcicfg_update_ranges_prop(dev_info_t *dip, ppb_ranges_t *addition)
/*
* Write out the new "ranges" property
*/
- (void) ndi_prop_update_int_array(DDI_DEV_T_NONE,
- dip, "ranges", (int *)newreg,
- (rlen + sizeof (ppb_ranges_t))/sizeof (int));
+ (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, dip, "ranges",
+ (int *)newreg, (rlen + sizeof (ppb_ranges_t))/sizeof (int));
DEBUG1("Updating ranges property for %d entries",
- rlen / sizeof (ppb_ranges_t) + 1);
+ rlen / sizeof (ppb_ranges_t) + 1);
kmem_free((caddr_t)newreg, rlen+sizeof (ppb_ranges_t));
@@ -2761,7 +2962,7 @@ pcicfg_update_reg_prop(dev_info_t *dip, uint32_t regvalue, uint_t reg_offset)
uint_t status;
status = ddi_getlongprop(DDI_DEV_T_ANY,
- dip, DDI_PROP_DONTPASS, "reg", (caddr_t)&reg, &rlen);
+ dip, DDI_PROP_DONTPASS, "reg", (caddr_t)&reg, &rlen);
switch (status) {
case DDI_PROP_SUCCESS:
@@ -2798,7 +2999,7 @@ pcicfg_update_reg_prop(dev_info_t *dip, uint32_t regvalue, uint_t reg_offset)
if ((PCI_BASE_TYPE_M & regvalue) == PCI_BASE_TYPE_MEM) {
hiword |= PCI_ADDR_MEM32;
} else if ((PCI_BASE_TYPE_M & regvalue)
- == PCI_BASE_TYPE_ALL) {
+ == PCI_BASE_TYPE_ALL) {
hiword |= PCI_ADDR_MEM64;
}
if (regvalue & PCI_BASE_PREF_M)
@@ -2821,9 +3022,8 @@ pcicfg_update_reg_prop(dev_info_t *dip, uint32_t regvalue, uint_t reg_offset)
/*
* Write out the new "reg" property
*/
- (void) ndi_prop_update_int_array(DDI_DEV_T_NONE,
- dip, "reg", (int *)newreg,
- (rlen + sizeof (pci_regspec_t))/sizeof (int));
+ (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, dip, "reg",
+ (int *)newreg, (rlen + sizeof (pci_regspec_t))/sizeof (int));
kmem_free((caddr_t)newreg, rlen+sizeof (pci_regspec_t));
kmem_free((caddr_t)reg, rlen);
@@ -2831,6 +3031,82 @@ pcicfg_update_reg_prop(dev_info_t *dip, uint32_t regvalue, uint_t reg_offset)
return (PCICFG_SUCCESS);
}
+static int
+pcicfg_update_assigned_prop_value(dev_info_t *dip, uint32_t size,
+ uint32_t base, uint32_t base_hi, uint_t reg_offset)
+{
+ int rlen;
+ pci_regspec_t *reg;
+ uint32_t hiword;
+ pci_regspec_t addition;
+ uint_t status;
+
+ status = ddi_getlongprop(DDI_DEV_T_ANY,
+ dip, DDI_PROP_DONTPASS, "reg", (caddr_t)&reg, &rlen);
+
+ switch (status) {
+ case DDI_PROP_SUCCESS:
+ break;
+ case DDI_PROP_NO_MEMORY:
+ DEBUG0("reg present, but unable to get memory\n");
+ return (PCICFG_FAILURE);
+ default:
+ /*
+ * Since the config space "reg" entry should have been
+ * created, we expect a "reg" property already
+ * present here.
+ */
+ DEBUG0("no reg property\n");
+ return (PCICFG_FAILURE);
+ }
+
+ /*
+ * Build the regspec, then add it to the existing one(s)
+ */
+
+ hiword = PCICFG_MAKE_REG_HIGH(PCI_REG_BUS_G(reg->pci_phys_hi),
+ PCI_REG_DEV_G(reg->pci_phys_hi),
+ PCI_REG_FUNC_G(reg->pci_phys_hi), reg_offset);
+
+ hiword |= PCI_REG_REL_M;
+
+ if (reg_offset == PCI_CONF_ROM) {
+ hiword |= PCI_ADDR_MEM32;
+
+ base = PCI_BASE_ROM_ADDR_M & base;
+ } else {
+ if ((PCI_BASE_SPACE_M & base) == PCI_BASE_SPACE_MEM) {
+ if ((PCI_BASE_TYPE_M & base) == PCI_BASE_TYPE_MEM) {
+ hiword |= PCI_ADDR_MEM32;
+ } else if ((PCI_BASE_TYPE_M & base)
+ == PCI_BASE_TYPE_ALL) {
+ hiword |= PCI_ADDR_MEM64;
+ }
+ if (base & PCI_BASE_PREF_M)
+ hiword |= PCI_REG_PF_M;
+
+ base = PCI_BASE_M_ADDR_M & base;
+ } else {
+ hiword |= PCI_ADDR_IO;
+
+ base = PCI_BASE_IO_ADDR_M & base;
+ base_hi = 0;
+ }
+ }
+
+ addition.pci_phys_hi = hiword;
+ addition.pci_phys_mid = base_hi;
+ addition.pci_phys_low = base;
+ addition.pci_size_hi = 0;
+ addition.pci_size_low = size;
+
+ DEBUG3("updating BAR@off %x with %x,%x\n", reg_offset, hiword, size);
+
+ kmem_free((caddr_t)reg, rlen);
+
+ return (pcicfg_update_assigned_prop(dip, &addition));
+}
+
static void
pcicfg_device_on(ddi_acc_handle_t config_handle)
{
@@ -2840,7 +3116,7 @@ pcicfg_device_on(ddi_acc_handle_t config_handle)
* fast back-to-back, and addr. stepping?
*/
pci_config_put16(config_handle, PCI_CONF_COMM,
- pci_config_get16(config_handle, PCI_CONF_COMM) | 0x7);
+ pci_config_get16(config_handle, PCI_CONF_COMM) | 0x7);
}
static void
@@ -2866,17 +3142,17 @@ pcicfg_set_standard_props(dev_info_t *dip, ddi_acc_handle_t config_handle,
uint8_t byteval;
/* These two exists only for non-bridges */
- if (((pci_config_get8(config_handle, PCI_CONF_HEADER)
- & PCI_HEADER_TYPE_M) == PCI_HEADER_ZERO) && !pcie_dev) {
+ if (((pci_config_get8(config_handle, PCI_CONF_HEADER) &
+ PCI_HEADER_TYPE_M) == PCI_HEADER_ZERO) && !pcie_dev) {
byteval = pci_config_get8(config_handle, PCI_CONF_MIN_G);
if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip,
- "min-grant", byteval)) != DDI_SUCCESS) {
+ "min-grant", byteval)) != DDI_SUCCESS) {
return (ret);
}
byteval = pci_config_get8(config_handle, PCI_CONF_MAX_L);
if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip,
- "max-latency", byteval)) != DDI_SUCCESS) {
+ "max-latency", byteval)) != DDI_SUCCESS) {
return (ret);
}
}
@@ -2887,32 +3163,32 @@ pcicfg_set_standard_props(dev_info_t *dip, ddi_acc_handle_t config_handle,
*/
val = pci_config_get16(config_handle, PCI_CONF_VENID);
- if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip,
- "vendor-id", val)) != DDI_SUCCESS) {
+ if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, "vendor-id", val))
+ != DDI_SUCCESS) {
return (ret);
}
val = pci_config_get16(config_handle, PCI_CONF_DEVID);
- if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip,
- "device-id", val)) != DDI_SUCCESS) {
+ if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, "device-id", val))
+ != DDI_SUCCESS) {
return (ret);
}
byteval = pci_config_get8(config_handle, PCI_CONF_REVID);
if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip,
- "revision-id", byteval)) != DDI_SUCCESS) {
+ "revision-id", byteval)) != DDI_SUCCESS) {
return (ret);
}
wordval = (pci_config_get16(config_handle, PCI_CONF_SUBCLASS)<< 8) |
- (pci_config_get8(config_handle, PCI_CONF_PROGCLASS));
+ (pci_config_get8(config_handle, PCI_CONF_PROGCLASS));
if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip,
- "class-code", wordval)) != DDI_SUCCESS) {
+ "class-code", wordval)) != DDI_SUCCESS) {
return (ret);
}
- val = (pci_config_get16(config_handle,
- PCI_CONF_STAT) & PCI_STAT_DEVSELT);
+ val = (pci_config_get16(config_handle, PCI_CONF_STAT) &
+ PCI_STAT_DEVSELT);
if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip,
- "devsel-speed", val)) != DDI_SUCCESS) {
+ "devsel-speed", val)) != DDI_SUCCESS) {
return (ret);
}
@@ -2922,24 +3198,22 @@ pcicfg_set_standard_props(dev_info_t *dip, ddi_acc_handle_t config_handle,
* is set, non-existent otherwise
*/
if ((!pcie_dev) &&
- (pci_config_get16(config_handle, PCI_CONF_STAT) &
- PCI_STAT_FBBC)) {
+ (pci_config_get16(config_handle, PCI_CONF_STAT) & PCI_STAT_FBBC)) {
if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip,
- "fast-back-to-back", 0)) != DDI_SUCCESS) {
+ "fast-back-to-back", 0)) != DDI_SUCCESS) {
return (ret);
}
}
if ((!pcie_dev) &&
- (pci_config_get16(config_handle, PCI_CONF_STAT) &
- PCI_STAT_66MHZ)) {
+ (pci_config_get16(config_handle, PCI_CONF_STAT) & PCI_STAT_66MHZ)) {
if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip,
- "66mhz-capable", 0)) != DDI_SUCCESS) {
+ "66mhz-capable", 0)) != DDI_SUCCESS) {
return (ret);
}
}
if (pci_config_get16(config_handle, PCI_CONF_STAT) & PCI_STAT_UDF) {
if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip,
- "udf-supported", 0)) != DDI_SUCCESS) {
+ "udf-supported", 0)) != DDI_SUCCESS) {
return (ret);
}
}
@@ -2950,24 +3224,22 @@ pcicfg_set_standard_props(dev_info_t *dip, ddi_acc_handle_t config_handle,
* is non-zero then the property exists with the value
* of the register.
*/
- if ((val = pci_config_get16(config_handle,
- PCI_CONF_SUBVENID)) != 0) {
+ if ((val = pci_config_get16(config_handle, PCI_CONF_SUBVENID)) != 0) {
if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip,
- "subsystem-vendor-id", val)) != DDI_SUCCESS) {
+ "subsystem-vendor-id", val)) != DDI_SUCCESS) {
return (ret);
}
}
- if ((val = pci_config_get16(config_handle,
- PCI_CONF_SUBSYSID)) != 0) {
+ if ((val = pci_config_get16(config_handle, PCI_CONF_SUBSYSID)) != 0) {
if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip,
- "subsystem-id", val)) != DDI_SUCCESS) {
+ "subsystem-id", val)) != DDI_SUCCESS) {
return (ret);
}
}
- if ((val = pci_config_get16(config_handle,
- PCI_CONF_CACHE_LINESZ)) != 0) {
+ if ((val = pci_config_get16(config_handle, PCI_CONF_CACHE_LINESZ))
+ != 0) {
if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip,
- "cache-line-size", val)) != DDI_SUCCESS) {
+ "cache-line-size", val)) != DDI_SUCCESS) {
return (ret);
}
}
@@ -2982,18 +3254,18 @@ pcicfg_set_standard_props(dev_info_t *dip, ddi_acc_handle_t config_handle,
* record the interrupt line used
*/
if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip,
- "interrupts", byteval)) != DDI_SUCCESS) {
+ "interrupts", byteval)) != DDI_SUCCESS) {
return (ret);
}
}
(void) PCI_CAP_LOCATE(config_handle, PCI_CAP_ID_PCI_E, &cap_id_loc);
if (pcie_dev && cap_id_loc != PCI_CAP_NEXT_PTR_NULL) {
- val = pci_config_get16(config_handle, cap_id_loc +
- PCIE_PCIECAP) & PCIE_PCIECAP_SLOT_IMPL;
+ val = pci_config_get16(config_handle, cap_id_loc + PCIE_PCIECAP)
+ & PCIE_PCIECAP_SLOT_IMPL;
/* if slot implemented, get physical slot number */
if (val) {
wordval = pci_config_get32(config_handle, cap_id_loc +
- PCIE_SLOTCAP);
+ PCIE_SLOTCAP);
/* create the property only if slotnum set correctly? */
if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip,
"physical-slot#", PCIE_SLOTCAP_PHY_SLOT_NUM(
@@ -3018,15 +3290,15 @@ pcicfg_set_busnode_props(dev_info_t *dip, uint8_t pcie_device_type)
(void) strcpy(device_type, "pci");
if ((ret = ndi_prop_update_string(DDI_DEV_T_NONE, dip,
- "device_type", device_type)) != DDI_SUCCESS) {
+ "device_type", device_type)) != DDI_SUCCESS) {
return (ret);
}
if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip,
- "#address-cells", 3)) != DDI_SUCCESS) {
+ "#address-cells", 3)) != DDI_SUCCESS) {
return (ret);
}
- if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip,
- "#size-cells", 2)) != DDI_SUCCESS) {
+ if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, "#size-cells", 2))
+ != DDI_SUCCESS) {
return (ret);
}
return (PCICFG_SUCCESS);
@@ -3059,9 +3331,9 @@ pcicfg_set_childnode_props(dev_info_t *dip, ddi_acc_handle_t config_handle,
/*
* NOTE: These are for both a child and PCI-PCI bridge node
*/
- sub_vid = pci_config_get16(config_handle, PCI_CONF_SUBVENID),
+ sub_vid = pci_config_get16(config_handle, PCI_CONF_SUBVENID);
sub_sid = pci_config_get16(config_handle, PCI_CONF_SUBSYSID);
- vid = pci_config_get16(config_handle, PCI_CONF_VENID),
+ vid = pci_config_get16(config_handle, PCI_CONF_VENID);
did = pci_config_get16(config_handle, PCI_CONF_DEVID);
revid = pci_config_get8(config_handle, PCI_CONF_REVID);
pif = pci_config_get8(config_handle, PCI_CONF_PROGCLASS);
@@ -3127,25 +3399,27 @@ pcicfg_set_childnode_props(dev_info_t *dip, ddi_acc_handle_t config_handle,
do {
if (sub_vid) {
- /* pci[ex]VVVV,DDDD.SSSS.ssss.RR */
- (void) sprintf(buffer, "%s%x,%x.%x.%x.%x", pprefix,
- vid, did, sub_vid, sub_sid, revid);
- compat[n] = kmem_alloc(strlen(buffer) + 1, KM_SLEEP);
- (void) strcpy(compat[n++], buffer);
-
- /* pci[ex]VVVV,DDDD.SSSS.ssss */
- (void) sprintf(buffer, "%s%x,%x.%x.%x", pprefix, vid, did,
- sub_vid, sub_sid);
- compat[n] = kmem_alloc(strlen(buffer) + 1, KM_SLEEP);
- (void) strcpy(compat[n++], buffer);
-
- /* pciSSSS.ssss -> not created for PCIe as per PCIe */
- /* binding to IEEE 1275 spec. */
- if (!pcie_dev && pcicfg_do_legacy_props) {
- (void) sprintf(buffer, "pci%x,%x", sub_vid, sub_sid);
+ /* pci[ex]VVVV,DDDD.SSSS.ssss.RR */
+ (void) sprintf(buffer, "%s%x,%x.%x.%x.%x", pprefix, vid,
+ did, sub_vid, sub_sid, revid);
+ compat[n] = kmem_alloc(strlen(buffer) + 1, KM_SLEEP);
+ (void) strcpy(compat[n++], buffer);
+
+ /* pci[ex]VVVV,DDDD.SSSS.ssss */
+ (void) sprintf(buffer, "%s%x,%x.%x.%x", pprefix, vid,
+ did, sub_vid, sub_sid);
compat[n] = kmem_alloc(strlen(buffer) + 1, KM_SLEEP);
(void) strcpy(compat[n++], buffer);
- }
+
+ /* pciSSSS.ssss -> not created for PCIe as per PCIe */
+ /* binding to IEEE 1275 spec. */
+ if (!pcie_dev && pcicfg_do_legacy_props) {
+ (void) sprintf(buffer, "pci%x,%x", sub_vid,
+ sub_sid);
+ compat[n] = kmem_alloc(strlen(buffer) + 1,
+ KM_SLEEP);
+ (void) strcpy(compat[n++], buffer);
+ }
}
/* pci[ex]VVVV,DDDD.RR */
@@ -3159,8 +3433,8 @@ pcicfg_set_childnode_props(dev_info_t *dip, ddi_acc_handle_t config_handle,
(void) strcpy(compat[n++], buffer);
/* pci[ex]class,CCSSPP */
- (void) sprintf(buffer, "%sclass,%02x%02x%02x", pprefix,
- pclass, psubclass, pif);
+ (void) sprintf(buffer, "%sclass,%02x%02x%02x", pprefix, pclass,
+ psubclass, pif);
compat[n] = kmem_alloc(strlen(buffer) + 1, KM_SLEEP);
(void) strcpy(compat[n++], buffer);
@@ -3178,8 +3452,8 @@ pcicfg_set_childnode_props(dev_info_t *dip, ddi_acc_handle_t config_handle,
} while (pcicfg_do_legacy_props);
- ret = ndi_prop_update_string_array(DDI_DEV_T_NONE, dip,
- "compatible", (char **)compat, n);
+ ret = ndi_prop_update_string_array(DDI_DEV_T_NONE, dip, "compatible",
+ (char **)compat, n);
for (i = 0; i < n; i++) {
kmem_free(compat[i], strlen(compat[i]) + 1);
@@ -3196,7 +3470,7 @@ pcicfg_set_bus_numbers(ddi_acc_handle_t config_handle,
uint_t primary, uint_t secondary, uint_t subordinate)
{
DEBUG3("Setting bridge bus-range %d,%d,%d\n", primary, secondary,
- subordinate);
+ subordinate);
/*
* Primary bus#
*/
@@ -3230,10 +3504,10 @@ pcicfg_setup_bridge(pcicfg_phdl_t *entry,
* Reset the secondary bus
*/
pci_config_put16(handle, PCI_BCNF_BCNTRL,
- pci_config_get16(handle, PCI_BCNF_BCNTRL) | 0x40);
+ pci_config_get16(handle, PCI_BCNF_BCNTRL) | 0x40);
drv_usecwait(1000);
pci_config_put16(handle, PCI_BCNF_BCNTRL,
- pci_config_get16(handle, PCI_BCNF_BCNTRL) & ~0x40);
+ pci_config_get16(handle, PCI_BCNF_BCNTRL) & ~0x40);
drv_usecwait(1000);
/*
@@ -3241,24 +3515,24 @@ pcicfg_setup_bridge(pcicfg_phdl_t *entry,
* start of the memory range
*/
pci_config_put16(handle, PCI_BCNF_MEM_BASE,
- PCICFG_HIWORD(PCICFG_LOADDR(entry->memory_last)));
+ PCICFG_HIWORD(PCICFG_LOADDR(entry->memory_last)));
/*
* Program the I/O base register with the start of the I/O range
*/
pci_config_put8(handle, PCI_BCNF_IO_BASE_LOW,
- PCICFG_HIBYTE(PCICFG_LOWORD(PCICFG_LOADDR(entry->io_last))));
+ PCICFG_HIBYTE(PCICFG_LOWORD(PCICFG_LOADDR(entry->io_last))));
pci_config_put16(handle, PCI_BCNF_IO_BASE_HI,
- PCICFG_HIWORD(PCICFG_LOADDR(entry->io_last)));
+ PCICFG_HIWORD(PCICFG_LOADDR(entry->io_last)));
/*
* Program the PF memory base register with the start of
* PF memory range
*/
pci_config_put16(handle, PCI_BCNF_PF_BASE_LOW,
- PCICFG_HIWORD(PCICFG_LOADDR(entry->pf_memory_last)));
+ PCICFG_HIWORD(PCICFG_LOADDR(entry->pf_memory_last)));
pci_config_put32(handle, PCI_BCNF_PF_BASE_HIGH,
- PCICFG_HIADDR(entry->pf_memory_last));
+ PCICFG_HIADDR(entry->pf_memory_last));
/*
* Clear status bits
@@ -3288,23 +3562,20 @@ pcicfg_update_bridge(pcicfg_phdl_t *entry,
*/
DEBUG1("DOWN ROUNDED ===>[0x%x]\n",
- PCICFG_ROUND_DOWN(entry->memory_last,
- PCICFG_MEMGRAN));
+ PCICFG_ROUND_DOWN(entry->memory_last, PCICFG_MEMGRAN));
pci_config_put16(handle, PCI_BCNF_MEM_LIMIT,
- PCICFG_HIWORD(PCICFG_LOADDR(
- PCICFG_ROUND_DOWN(entry->memory_last,
- PCICFG_MEMGRAN))));
+ PCICFG_HIWORD(PCICFG_LOADDR(
+ PCICFG_ROUND_DOWN(entry->memory_last, PCICFG_MEMGRAN))));
/*
* Since this is a bridge, the rest of this range will
* be responded to by the bridge. We have to round up
* so no other device claims it.
*/
- if ((length = (PCICFG_ROUND_UP(entry->memory_last,
- PCICFG_MEMGRAN) - entry->memory_last)) > 0) {
+ if ((length = (PCICFG_ROUND_UP(entry->memory_last, PCICFG_MEMGRAN)
+ - entry->memory_last)) > 0) {
(void) pcicfg_get_mem(entry, length, NULL);
- DEBUG1("Added [0x%x]at the top of "
- "the bridge (mem)\n", length);
+ DEBUG1("Added [0x%x]at the top of the bridge (mem)\n", length);
}
/*
@@ -3312,35 +3583,29 @@ pcicfg_update_bridge(pcicfg_phdl_t *entry,
*/
DEBUG1("DOWN ROUNDED ===>[0x%x]\n",
- PCICFG_ROUND_DOWN(entry->pf_memory_last,
- PCICFG_MEMGRAN));
+ PCICFG_ROUND_DOWN(entry->pf_memory_last, PCICFG_MEMGRAN));
pci_config_put16(handle, PCI_BCNF_PF_LIMIT_LOW,
- PCICFG_HIWORD(PCICFG_LOADDR(
- PCICFG_ROUND_DOWN(entry->pf_memory_last,
- PCICFG_MEMGRAN))));
- pci_config_put32(handle, PCI_BCNF_PF_LIMIT_HIGH,
- PCICFG_HIADDR(
- PCICFG_ROUND_DOWN(entry->pf_memory_last,
- PCICFG_MEMGRAN)));
- if ((length = (PCICFG_ROUND_UP(entry->pf_memory_last,
- PCICFG_MEMGRAN) - entry->pf_memory_last)) > 0) {
+ PCICFG_HIWORD(PCICFG_LOADDR(PCICFG_ROUND_DOWN(
+ entry->pf_memory_last, PCICFG_MEMGRAN))));
+ pci_config_put32(handle, PCI_BCNF_PF_LIMIT_HIGH, PCICFG_HIADDR(
+ PCICFG_ROUND_DOWN(entry->pf_memory_last, PCICFG_MEMGRAN)));
+ if ((length = (PCICFG_ROUND_UP(entry->pf_memory_last, PCICFG_MEMGRAN)
+ - entry->pf_memory_last)) > 0) {
(void) pcicfg_get_pf_mem(entry, length, NULL);
- DEBUG1("Added [0x%x]at the top of "
- "the bridge (PF mem)\n", length);
+ DEBUG1("Added [0x%x]at the top of the bridge (PF mem)\n",
+ length);
}
/*
* Program the I/O limit register with the end of the I/O range
*/
pci_config_put8(handle, PCI_BCNF_IO_LIMIT_LOW,
- PCICFG_HIBYTE(PCICFG_LOWORD(
- PCICFG_LOADDR(PCICFG_ROUND_DOWN(entry->io_last,
- PCICFG_IOGRAN)))));
+ PCICFG_HIBYTE(PCICFG_LOWORD(
+ PCICFG_LOADDR(PCICFG_ROUND_DOWN(entry->io_last, PCICFG_IOGRAN)))));
- pci_config_put16(handle, PCI_BCNF_IO_LIMIT_HI,
- PCICFG_HIWORD(PCICFG_LOADDR(PCICFG_ROUND_DOWN(entry->io_last,
- PCICFG_IOGRAN))));
+ pci_config_put16(handle, PCI_BCNF_IO_LIMIT_HI, PCICFG_HIWORD(
+ PCICFG_LOADDR(PCICFG_ROUND_DOWN(entry->io_last, PCICFG_IOGRAN))));
/*
* Same as above for I/O space. Since this is a
@@ -3348,23 +3613,20 @@ pcicfg_update_bridge(pcicfg_phdl_t *entry,
* to by the bridge. We have to round up so no
* other device claims it.
*/
- if ((length = (PCICFG_ROUND_UP(entry->io_last,
- PCICFG_IOGRAN) - entry->io_last)) > 0) {
+ if ((length = (PCICFG_ROUND_UP(entry->io_last, PCICFG_IOGRAN)
+ - entry->io_last)) > 0) {
(void) pcicfg_get_io(entry, length, NULL);
- DEBUG1("Added [0x%x]at the top of "
- "the bridge (I/O)\n", length);
+ DEBUG1("Added [0x%x]at the top of the bridge (I/O)\n", length);
}
}
static int
-pcicfg_probe_children(dev_info_t *parent, uint_t bus,
- uint_t device, uint_t func, uint_t *highest_bus)
+pcicfg_probe_children(dev_info_t *parent, uint_t bus, uint_t device,
+ uint_t func, uint_t *highest_bus, pcicfg_flags_t flags)
{
dev_info_t *new_child;
ddi_acc_handle_t config_handle;
uint8_t header_type, pcie_dev = 0;
- int i;
- uint32_t request;
int ret = PCICFG_FAILURE;
/*
@@ -3374,17 +3636,16 @@ pcicfg_probe_children(dev_info_t *parent, uint_t bus,
*/
ndi_devi_alloc_sleep(parent, DEVI_PSEUDO_NEXNAME,
- (pnode_t)DEVI_SID_NODEID, &new_child);
+ (pnode_t)DEVI_SID_NODEID, &new_child);
- if (pcicfg_add_config_reg(new_child, bus,
- device, func) != DDI_SUCCESS) {
- DEBUG0("pcicfg_probe_children():"
- "Failed to add candidate REG\n");
+ if (pcicfg_add_config_reg(new_child, bus, device, func)
+ != DDI_SUCCESS) {
+ DEBUG0("pcicfg_probe_children():Failed to add candidate REG\n");
goto failedconfig;
}
if ((ret = pcicfg_config_setup(new_child, &config_handle))
- != PCICFG_SUCCESS) {
+ != PCICFG_SUCCESS) {
if (ret == PCICFG_NODEVICE) {
(void) ndi_devi_free(new_child);
return (ret);
@@ -3410,8 +3671,8 @@ pcicfg_probe_children(dev_info_t *parent, uint_t bus,
/*
* Set 1275 properties common to all devices
*/
- if (pcicfg_set_standard_props(new_child, config_handle,
- pcie_dev) != PCICFG_SUCCESS) {
+ if (pcicfg_set_standard_props(new_child, config_handle, pcie_dev)
+ != PCICFG_SUCCESS) {
DEBUG0("Failed to set standard properties\n");
goto failedchild;
}
@@ -3419,8 +3680,8 @@ pcicfg_probe_children(dev_info_t *parent, uint_t bus,
/*
* Child node properties NOTE: Both for PCI-PCI bridge and child node
*/
- if (pcicfg_set_childnode_props(new_child, config_handle,
- pcie_dev) != PCICFG_SUCCESS) {
+ if (pcicfg_set_childnode_props(new_child, config_handle, pcie_dev)
+ != PCICFG_SUCCESS) {
goto failedchild;
}
@@ -3443,8 +3704,12 @@ pcicfg_probe_children(dev_info_t *parent, uint_t bus,
if ((header_type & PCI_HEADER_TYPE_M) == PCI_HEADER_PPB) {
- DEBUG3("--Bridge found bus [0x%x] device"
- "[0x%x] func [0x%x]\n", bus, device, func);
+ DEBUG3("--Bridge found bus [0x%x] device[0x%x] func [0x%x]\n",
+ bus, device, func);
+
+ /* Only support read-only probe for leaf device */
+ if (flags & PCICFG_FLAG_READ_ONLY)
+ goto failedchild;
ret = pcicfg_probe_bridge(new_child, config_handle, bus,
highest_bus);
@@ -3456,105 +3721,258 @@ pcicfg_probe_children(dev_info_t *parent, uint_t bus,
} else {
DEBUG3("--Leaf device found bus [0x%x] device"
- "[0x%x] func [0x%x]\n",
- bus, device, func);
-
- i = PCI_CONF_BASE0;
-
- while (i <= PCI_CONF_BASE5) {
+ "[0x%x] func [0x%x]\n", bus, device, func);
- pci_config_put32(config_handle, i, 0xffffffff);
+ if (flags & PCICFG_FLAG_READ_ONLY) {
+ /*
+ * with read-only probe, don't do any resource
+ * allocation, just read the BARs and update props.
+ */
+ ret = pcicfg_populate_props_from_bar(new_child,
+ config_handle);
+ if (ret != PCICFG_SUCCESS)
+ goto failedchild;
- request = pci_config_get32(config_handle, i);
/*
- * If its a zero length, don't do
- * any programming.
+ * now allocate the resources, just remove the
+ * resources from the parent busra pool.
*/
- if (request != 0) {
- /*
- * Add to the "reg" property
- */
- if (pcicfg_update_reg_prop(new_child,
- request, i) != PCICFG_SUCCESS) {
- goto failedchild;
- }
- } else {
- DEBUG1("BASE register [0x%x] asks for "
- "[0x0]=[0x0](32)\n", i);
- i += 4;
- continue;
+ ret = pcicfg_device_assign_readonly(new_child);
+ if (ret != PCICFG_SUCCESS) {
+ (void) pcicfg_free_device_resources(new_child);
+ goto failedchild;
}
+ } else {
/*
- * Increment by eight if it is 64 bit address space
+ * update "reg" property by sizing the BARs.
*/
- if ((PCI_BASE_TYPE_M & request) == PCI_BASE_TYPE_ALL) {
- DEBUG3("BASE register [0x%x] asks for "
- "[0x%x]=[0x%x] (64)\n",
- i, request,
- (~(PCI_BASE_M_ADDR_M & request))+1)
- i += 8;
- } else {
- DEBUG3("BASE register [0x%x] asks for "
- "[0x%x]=[0x%x](32)\n",
- i, request,
- (~(PCI_BASE_M_ADDR_M & request))+1)
- i += 4;
+ ret = pcicfg_populate_reg_props(new_child,
+ config_handle);
+ if (ret != PCICFG_SUCCESS)
+ goto failedchild;
+
+ /* now allocate & program the resources */
+ ret = pcicfg_device_assign(new_child);
+ if (ret != PCICFG_SUCCESS) {
+ (void) pcicfg_free_device_resources(new_child);
+ goto failedchild;
}
}
- /*
- * Get the ROM size and create register for it
- */
- pci_config_put32(config_handle, PCI_CONF_ROM, 0xfffffffe);
+ (void) ndi_devi_bind_driver(new_child, 0);
+ }
+
+ (void) pcicfg_config_teardown(&config_handle);
+
+ return (PCICFG_SUCCESS);
+
+failedchild:
+ /*
+ * XXX check if it should be taken offline (if online)
+ */
+ (void) pcicfg_config_teardown(&config_handle);
+
+failedconfig:
+
+ (void) ndi_devi_free(new_child);
+ return (ret);
+}
+
+/*
+ * Sizing the BARs and update "reg" property
+ */
+static int
+pcicfg_populate_reg_props(dev_info_t *new_child,
+ ddi_acc_handle_t config_handle)
+{
+ int i;
+ uint32_t request;
- request = pci_config_get32(config_handle, PCI_CONF_ROM);
+ i = PCI_CONF_BASE0;
+
+ while (i <= PCI_CONF_BASE5) {
+
+ pci_config_put32(config_handle, i, 0xffffffff);
+
+ request = pci_config_get32(config_handle, i);
/*
* If its a zero length, don't do
* any programming.
*/
-
if (request != 0) {
- DEBUG3("BASE register [0x%x] asks for [0x%x]=[0x%x]\n",
- PCI_CONF_ROM, request,
- (~(PCI_BASE_ROM_ADDR_M & request))+1);
/*
* Add to the "reg" property
*/
if (pcicfg_update_reg_prop(new_child,
- request, PCI_CONF_ROM) != PCICFG_SUCCESS) {
+ request, i) != PCICFG_SUCCESS) {
goto failedchild;
}
+ } else {
+ DEBUG1("BASE register [0x%x] asks for "
+ "[0x0]=[0x0](32)\n", i);
+ i += 4;
+ continue;
}
- /* now allocate & program the resources */
- ret = pcicfg_device_assign(new_child);
- if (ret != PCICFG_SUCCESS) {
- (void) pcicfg_free_device_resources(new_child);
- goto failedchild;
+ /*
+ * Increment by eight if it is 64 bit address space
+ */
+ if ((PCI_BASE_TYPE_M & request) == PCI_BASE_TYPE_ALL) {
+ DEBUG3("BASE register [0x%x] asks for "
+ "[0x%x]=[0x%x] (64)\n",
+ i, request, (~(PCI_BASE_M_ADDR_M & request))+1);
+ i += 8;
+ } else {
+ DEBUG3("BASE register [0x%x] asks for "
+ "[0x%x]=[0x%x](32)\n",
+ i, request, (~(PCI_BASE_M_ADDR_M & request))+1);
+ i += 4;
}
- (void) ndi_devi_bind_driver(new_child, 0);
}
- (void) pcicfg_config_teardown(&config_handle);
+ /*
+ * Get the ROM size and create register for it
+ */
+ pci_config_put32(config_handle, PCI_CONF_ROM, 0xfffffffe);
+
+ request = pci_config_get32(config_handle, PCI_CONF_ROM);
+ /*
+ * If its a zero length, don't do
+ * any programming.
+ */
+
+ if (request != 0) {
+ DEBUG3("BASE register [0x%x] asks for [0x%x]=[0x%x]\n",
+ PCI_CONF_ROM, request,
+ (~(PCI_BASE_ROM_ADDR_M & request)) + 1);
+ /*
+ * Add to the "reg" property
+ */
+ if (pcicfg_update_reg_prop(new_child, request, PCI_CONF_ROM)
+ != PCICFG_SUCCESS) {
+ goto failedchild;
+ }
+ }
return (PCICFG_SUCCESS);
failedchild:
+ return (PCICFG_FAILURE);
+}
+
+/*
+ * Read the BARs and update properties. Used in virtual hotplug.
+ */
+static int
+pcicfg_populate_props_from_bar(dev_info_t *new_child,
+ ddi_acc_handle_t config_handle)
+{
+ uint32_t request, base, base_hi, size;
+ int i;
+
+ i = PCI_CONF_BASE0;
+
+ while (i <= PCI_CONF_BASE5) {
+ /*
+ * determine the size of the address space
+ */
+ base = pci_config_get32(config_handle, i);
+ pci_config_put32(config_handle, i, 0xffffffff);
+ request = pci_config_get32(config_handle, i);
+ pci_config_put32(config_handle, i, base);
+
+ /*
+ * If its a zero length, don't do any programming.
+ */
+ if (request != 0) {
+ /*
+ * Add to the "reg" property
+ */
+ if (pcicfg_update_reg_prop(new_child,
+ request, i) != PCICFG_SUCCESS) {
+ goto failedchild;
+ }
+
+ if ((PCI_BASE_SPACE_IO & request) == 0 &&
+ (PCI_BASE_TYPE_M & request) == PCI_BASE_TYPE_ALL) {
+ base_hi = pci_config_get32(config_handle, i+4);
+ } else {
+ base_hi = 0;
+ }
+ /*
+ * Add to "assigned-addresses" property
+ */
+ size = (~(PCI_BASE_M_ADDR_M & request))+1;
+ if (pcicfg_update_assigned_prop_value(new_child,
+ size, base, base_hi, i) != PCICFG_SUCCESS) {
+ goto failedchild;
+ }
+ } else {
+ DEBUG1("BASE register [0x%x] asks for [0x0]=[0x0]"
+ "(32)\n", i);
+ i += 4;
+ continue;
+ }
+
+ /*
+ * Increment by eight if it is 64 bit address space
+ */
+ if ((PCI_BASE_TYPE_M & request) == PCI_BASE_TYPE_ALL) {
+ DEBUG3("BASE register [0x%x] asks for [0x%x]=[0x%x]"
+ "(64)\n", i, request,
+ (~(PCI_BASE_M_ADDR_M & request)) + 1);
+ i += 8;
+ } else {
+ DEBUG3("BASE register [0x%x] asks for [0x%x]=[0x%x]"
+ "(32)\n", i, request,
+ (~(PCI_BASE_M_ADDR_M & request)) + 1);
+ i += 4;
+ }
+ }
+
/*
- * XXX check if it should be taken offline (if online)
+ * Get the ROM size and create register for it
*/
- (void) pcicfg_config_teardown(&config_handle);
+ base = pci_config_get32(config_handle, PCI_CONF_ROM);
+ pci_config_put32(config_handle, PCI_CONF_ROM, 0xfffffffe);
+ request = pci_config_get32(config_handle, PCI_CONF_ROM);
+ pci_config_put32(config_handle, PCI_CONF_ROM, base);
-failedconfig:
+ /*
+ * If its a zero length, don't do
+ * any programming.
+ */
+ if (request != 0) {
+ DEBUG3("BASE register [0x%x] asks for [0x%x]=[0x%x]\n",
+ PCI_CONF_ROM, request,
+ (~(PCI_BASE_ROM_ADDR_M & request)) + 1);
+ /*
+ * Add to the "reg" property
+ */
+ if (pcicfg_update_reg_prop(new_child, request, PCI_CONF_ROM)
+ != PCICFG_SUCCESS) {
+ goto failedchild;
+ }
+ /*
+ * Add to "assigned-addresses" property
+ */
+ size = (~(PCI_BASE_ROM_ADDR_M & request))+1;
+ if (pcicfg_update_assigned_prop_value(new_child, size,
+ base, 0, PCI_CONF_ROM) != PCICFG_SUCCESS) {
+ goto failedchild;
+ }
+ }
- (void) ndi_devi_free(new_child);
- return (ret);
+ return (PCICFG_SUCCESS);
+
+failedchild:
+ return (PCICFG_FAILURE);
}
static int
pcicfg_probe_bridge(dev_info_t *new_child, ddi_acc_handle_t h, uint_t bus,
- uint_t *highest_bus)
+ uint_t *highest_bus)
{
uint64_t next_bus;
uint_t new_bus, num_slots;
@@ -3573,15 +3991,37 @@ pcicfg_probe_bridge(dev_info_t *new_child, ddi_acc_handle_t h, uint_t bus,
uint64_t max_bus;
uint8_t pcie_device_type = 0;
uint_t pf_mem_supported = 0;
+ dev_info_t *new_device;
+ int trans_device;
+ int ari_mode = B_FALSE;
+ int max_function = PCI_MAX_FUNCTIONS;
io_answer = io_base = io_alen = io_size = 0;
pf_mem_answer = pf_mem_base = pf_mem_size = pf_mem_alen = 0;
/*
+ * Set "device_type" to "pci", the actual type will be set later
+ * by pcicfg_set_busnode_props() below. This is needed as the
+ * pcicfg_ra_free() below would update "available" property based
+ * on "device_type".
+ *
+ * This code can be removed later after PCI configurator is changed
+ * to use PCIRM, which automatically update properties upon allocation
+ * and free, at that time we'll be able to remove the code inside
+ * ndi_ra_alloc/free() which currently updates "available" property
+ * for pci/pcie devices in pcie fabric.
+ */
+ if (ndi_prop_update_string(DDI_DEV_T_NONE, new_child,
+ "device_type", "pci") != DDI_SUCCESS) {
+ DEBUG0("Failed to set \"device_type\" props\n");
+ return (PCICFG_FAILURE);
+ }
+
+ /*
* setup resource maps for the bridge node
*/
if (ndi_ra_map_setup(new_child, NDI_RA_TYPE_PCI_BUSNUM)
- == NDI_FAILURE) {
+ == NDI_FAILURE) {
DEBUG0("Can not setup resource map - NDI_RA_TYPE_PCI_BUSNUM\n");
rval = PCICFG_FAILURE;
goto cleanup;
@@ -3597,9 +4037,9 @@ pcicfg_probe_bridge(dev_info_t *new_child, ddi_acc_handle_t h, uint_t bus,
goto cleanup;
}
if (ndi_ra_map_setup(new_child, NDI_RA_TYPE_PCI_PREFETCH_MEM) ==
- NDI_FAILURE) {
+ NDI_FAILURE) {
DEBUG0("Can not setup resource map -"
- " NDI_RA_TYPE_PCI_PREFETCH_MEM\n");
+ " NDI_RA_TYPE_PCI_PREFETCH_MEM\n");
rval = PCICFG_FAILURE;
goto cleanup;
}
@@ -3629,7 +4069,7 @@ pcicfg_probe_bridge(dev_info_t *new_child, ddi_acc_handle_t h, uint_t bus,
}
DEBUG2("Bus Range Allocated [base=%d] [len=%d]\n",
- pcibus_base, pcibus_alen);
+ pcibus_base, pcibus_alen);
/*
* Put available bus range into the pool.
@@ -3637,7 +4077,7 @@ pcicfg_probe_bridge(dev_info_t *new_child, ddi_acc_handle_t h, uint_t bus,
* to child.
*/
(void) ndi_ra_free(new_child, pcibus_base+1, pcibus_alen-1,
- NDI_RA_TYPE_PCI_BUSNUM, NDI_RA_PASS);
+ NDI_RA_TYPE_PCI_BUSNUM, NDI_RA_PASS);
next_bus = pcibus_base;
max_bus = pcibus_base + pcibus_alen - 1;
@@ -3967,7 +4407,7 @@ pf_setup_end:
* Set bus properties
*/
if (pcicfg_set_busnode_props(new_child, pcie_device_type)
- != PCICFG_SUCCESS) {
+ != PCICFG_SUCCESS) {
DEBUG0("Failed to set busnode props\n");
rval = PCICFG_FAILURE;
goto cleanup;
@@ -3996,24 +4436,78 @@ pf_setup_end:
*/
DEBUG0("Bridge Programming Complete - probe children\n");
ndi_devi_enter(new_child, &count);
- for (i = 0; i < PCI_MAX_DEVICES; i++) {
- for (j = 0; j < PCI_MAX_FUNCTIONS; j++) {
+ for (i = 0; ((i < PCI_MAX_DEVICES) && (ari_mode == B_FALSE));
+ i++) {
+ for (j = 0; j < max_function; ) {
+ if (ari_mode)
+ trans_device = j >> 3;
+ else
+ trans_device = i;
+
if ((rval = pcicfg_probe_children(new_child,
- new_bus, i, j, highest_bus))
- != PCICFG_SUCCESS) {
+ new_bus, trans_device, j & 7, highest_bus, 0))
+ != PCICFG_SUCCESS) {
if (rval == PCICFG_NODEVICE) {
DEBUG3("No Device at bus [0x%x]"
- "device [0x%x] "
- "func [0x%x]\n", new_bus, i, j);
+ "device [0x%x] "
+ "func [0x%x]\n", new_bus,
+ trans_device, j & 7);
+
if (j)
- continue;
+ goto next;
} else
/*EMPTY*/
DEBUG3("Failed to configure bus "
- "[0x%x] device [0x%x] "
- "func [0x%x]\n", new_bus, i, j);
+ "[0x%x] device [0x%x] "
+ "func [0x%x]\n", new_bus,
+ trans_device, j & 7);
break;
}
+next:
+ new_device = pcicfg_devi_find(new_child, trans_device,
+ (j & 7));
+
+ /*
+ * Determine if ARI Forwarding should be enabled.
+ */
+ if (j == 0) {
+ if (new_device == NULL)
+ break;
+
+ if ((pcie_ari_supported(new_child) ==
+ PCIE_ARI_FORW_SUPPORTED) &&
+ (pcie_ari_device(new_device) ==
+ PCIE_ARI_DEVICE)) {
+ if (pcie_ari_enable(new_child) ==
+ DDI_SUCCESS) {
+ (void) ddi_prop_create(
+ DDI_DEV_T_NONE,
+ new_child,
+ DDI_PROP_CANSLEEP,
+ "ari-enabled", NULL, 0);
+ ari_mode = B_TRUE;
+ max_function =
+ PCICFG_MAX_ARI_FUNCTION;
+ }
+ }
+ }
+ if (ari_mode == B_TRUE) {
+ int next_function;
+
+ if (new_device == NULL)
+ break;
+
+ if (pcie_ari_get_next_function(new_device,
+ &next_function) != DDI_SUCCESS)
+ break;
+
+ j = next_function;
+
+ if (next_function == 0)
+ break;
+ } else
+ j++;
+
}
/* if any function fails to be configured, no need to proceed */
if (rval != PCICFG_NODEVICE)
@@ -4023,8 +4517,15 @@ pf_setup_end:
/*
* Offline the bridge to allow reprogramming of resources.
+ *
+ * This should always succeed since nobody else has started to
+ * use it yet, failing to detach the driver would indicate a bug.
+ * Also in that case it's better just panic than allowing the
+ * configurator to proceed with BAR reprogramming without bridge
+ * driver detached.
*/
- (void) ndi_devi_offline(new_child, NDI_NO_EVENT|NDI_UNCONFIG);
+ VERIFY(ndi_devi_offline(new_child, NDI_NO_EVENT|NDI_UNCONFIG)
+ == NDI_SUCCESS);
phdl.dip = new_child;
phdl.memory_base = mem_answer;
@@ -4041,24 +4542,34 @@ pf_setup_end:
io_end = PCICFG_ROUND_UP(phdl.io_base, PCICFG_IOGRAN);
pf_mem_end = PCICFG_ROUND_UP(phdl.pf_memory_base, PCICFG_MEMGRAN);
- DEBUG4("Start of Unallocated Bridge(%d slots) Resources "
- "Mem=0x%lx I/O=0x%lx PF_mem=%x%lx\n", num_slots, mem_end,
- io_end, pf_mem_end);
+ DEBUG4("Start of Unallocated Bridge(%d slots) Resources Mem=0x%lx "
+ "I/O=0x%lx PF_mem=%x%lx\n", num_slots, mem_end, io_end, pf_mem_end);
/*
+ * Before probing the children we've allocated maximum MEM/IO
+ * resources from parent, and updated "available" property
+ * accordingly. Later we'll be giving up unused resources to
+ * the parent, thus we need to destroy "available" property
+ * here otherwise it will be out-of-sync with the actual free
+ * resources this bridge has. This property will be rebuilt below
+ * with the actual free resources reserved for hotplug slots
+ * (if any).
+ */
+ (void) ndi_prop_remove(DDI_DEV_T_NONE, new_child, "available");
+ /*
* if the bridge a slots, then preallocate. If not, assume static
* configuration. Also check for preallocation limits and spit
* warning messages appropriately (perhaps some can be in debug mode).
*/
if (num_slots) {
- uint64_t mem_reqd = mem_answer + (num_slots *
- pcicfg_slot_memsize);
- uint64_t io_reqd = io_answer + (num_slots *
- pcicfg_slot_iosize);
- uint64_t pf_mem_reqd = pf_mem_answer + (num_slots *
- pcicfg_slot_pf_memsize);
- uint8_t highest_bus_reqd = new_bus + (num_slots *
- pcicfg_slot_busnums);
+ uint64_t mem_reqd = mem_answer +
+ (num_slots * pcicfg_slot_memsize);
+ uint64_t io_reqd = io_answer +
+ (num_slots * pcicfg_slot_iosize);
+ uint64_t pf_mem_reqd = pf_mem_answer +
+ (num_slots * pcicfg_slot_pf_memsize);
+ uint8_t highest_bus_reqd = new_bus +
+ (num_slots * pcicfg_slot_busnums);
#ifdef DEBUG
if (mem_end > mem_reqd)
DEBUG3("Memory space consumed by bridge more "
@@ -4095,23 +4606,22 @@ pf_setup_end:
num_slots, new_bus, *highest_bus);
#endif
mem_end = MAX((MIN(mem_reqd, (mem_answer + mem_alen))),
- mem_end);
+ mem_end);
io_end = MAX((MIN(io_reqd, (io_answer + io_alen))), io_end);
pf_mem_end = MAX((MIN(pf_mem_reqd, (pf_mem_answer +
- pf_mem_alen))), pf_mem_end);
+ pf_mem_alen))), pf_mem_end);
*highest_bus = MAX((MIN(highest_bus_reqd, max_bus)),
- *highest_bus);
+ *highest_bus);
DEBUG4("mem_end %lx, io_end %lx, pf_mem_end %lx"
- " highest_bus %x\n", mem_end, io_end,
- pf_mem_end, *highest_bus);
+ " highest_bus %x\n", mem_end, io_end, pf_mem_end,
+ *highest_bus);
}
/*
* Give back unused memory space to parent.
*/
- (void) ndi_ra_free(ddi_get_parent(new_child),
- mem_end, (mem_answer + mem_alen) - mem_end, NDI_RA_TYPE_MEM,
- NDI_RA_PASS);
+ (void) ndi_ra_free(ddi_get_parent(new_child), mem_end,
+ (mem_answer + mem_alen) - mem_end, NDI_RA_TYPE_MEM, NDI_RA_PASS);
if (mem_end == mem_answer) {
DEBUG0("No memory resources used\n");
@@ -4172,33 +4682,33 @@ pf_setup_end:
* Give back unused PF memory space to parent.
*/
if (pf_mem_supported) {
- (void) ndi_ra_free(ddi_get_parent(new_child),
- pf_mem_end, (pf_mem_answer + pf_mem_alen) - pf_mem_end,
- NDI_RA_TYPE_PCI_PREFETCH_MEM, NDI_RA_PASS);
+ (void) ndi_ra_free(ddi_get_parent(new_child),
+ pf_mem_end, (pf_mem_answer + pf_mem_alen) - pf_mem_end,
+ NDI_RA_TYPE_PCI_PREFETCH_MEM, NDI_RA_PASS);
- if (pf_mem_end == pf_mem_answer) {
- DEBUG0("No PF memory resources used\n");
- /*
- * To prevent the bridge from forwarding any PF Memory
- * transactions, the PF Memory Limit will be programmed
- * with a smaller value than the Memory Base.
- */
- pci_config_put16(h, PCI_BCNF_PF_BASE_LOW, 0xfff0);
- pci_config_put32(h, PCI_BCNF_PF_BASE_HIGH, 0xffffffff);
- pci_config_put16(h, PCI_BCNF_PF_LIMIT_LOW, 0);
- pci_config_put32(h, PCI_BCNF_PF_LIMIT_HIGH, 0);
+ if (pf_mem_end == pf_mem_answer) {
+ DEBUG0("No PF memory resources used\n");
+ /*
+ * To prevent the bridge from forwarding any PF Memory
+ * transactions, the PF Memory Limit will be programmed
+ * with a smaller value than the Memory Base.
+ */
+ pci_config_put16(h, PCI_BCNF_PF_BASE_LOW, 0xfff0);
+ pci_config_put32(h, PCI_BCNF_PF_BASE_HIGH, 0xffffffff);
+ pci_config_put16(h, PCI_BCNF_PF_LIMIT_LOW, 0);
+ pci_config_put32(h, PCI_BCNF_PF_LIMIT_HIGH, 0);
- pf_mem_size = 0;
- } else {
- /*
- * Reprogram the end of the PF memory range.
- */
- pci_config_put16(h, PCI_BCNF_PF_LIMIT_LOW,
- PCICFG_HIWORD(PCICFG_LOADDR(pf_mem_end - 1)));
- pci_config_put32(h, PCI_BCNF_PF_LIMIT_HIGH,
- PCICFG_HIADDR(pf_mem_end - 1));
- pf_mem_size = pf_mem_end - pf_mem_base;
- }
+ pf_mem_size = 0;
+ } else {
+ /*
+ * Reprogram the end of the PF memory range.
+ */
+ pci_config_put16(h, PCI_BCNF_PF_LIMIT_LOW,
+ PCICFG_HIWORD(PCICFG_LOADDR(pf_mem_end - 1)));
+ pci_config_put32(h, PCI_BCNF_PF_LIMIT_HIGH,
+ PCICFG_HIADDR(pf_mem_end - 1));
+ pf_mem_size = pf_mem_end - pf_mem_base;
+ }
}
if ((max_bus - *highest_bus) > 0) {
@@ -4206,8 +4716,8 @@ pf_setup_end:
* Give back unused bus numbers
*/
(void) ndi_ra_free(ddi_get_parent(new_child),
- *highest_bus+1, max_bus - *highest_bus,
- NDI_RA_TYPE_PCI_BUSNUM, NDI_RA_PASS);
+ *highest_bus+1, max_bus - *highest_bus,
+ NDI_RA_TYPE_PCI_BUSNUM, NDI_RA_PASS);
}
/*
@@ -4268,18 +4778,20 @@ pf_setup_end:
cleanup:
/* free up resources (for error return case only) */
if (rval != PCICFG_SUCCESS) {
- if (mem_alen)
- (void) ndi_ra_free(ddi_get_parent(new_child), mem_base,
- mem_alen, NDI_RA_TYPE_MEM, NDI_RA_PASS);
- if (io_alen)
- (void) ndi_ra_free(ddi_get_parent(new_child), io_base,
- io_alen, NDI_RA_TYPE_IO, NDI_RA_PASS);
- if (pf_mem_alen)
- (void) ndi_ra_free(ddi_get_parent(new_child), pf_mem_base,
- pf_mem_alen, NDI_RA_TYPE_PCI_PREFETCH_MEM, NDI_RA_PASS);
- if (pcibus_alen)
- (void) ndi_ra_free(ddi_get_parent(new_child), pcibus_base,
- pcibus_alen, NDI_RA_TYPE_PCI_BUSNUM, NDI_RA_PASS);
+ if (mem_alen)
+ (void) ndi_ra_free(ddi_get_parent(new_child), mem_base,
+ mem_alen, NDI_RA_TYPE_MEM, NDI_RA_PASS);
+ if (io_alen)
+ (void) ndi_ra_free(ddi_get_parent(new_child), io_base,
+ io_alen, NDI_RA_TYPE_IO, NDI_RA_PASS);
+ if (pf_mem_alen)
+ (void) ndi_ra_free(ddi_get_parent(new_child),
+ pf_mem_base, pf_mem_alen,
+ NDI_RA_TYPE_PCI_PREFETCH_MEM, NDI_RA_PASS);
+ if (pcibus_alen)
+ (void) ndi_ra_free(ddi_get_parent(new_child),
+ pcibus_base, pcibus_alen, NDI_RA_TYPE_PCI_BUSNUM,
+ NDI_RA_PASS);
}
/* free up any resource maps setup for the bridge node */
@@ -4320,47 +4832,51 @@ pcicfg_find_resource_end(dev_info_t *dip, void *hdl)
switch (PCI_REG_ADDR_G(pci_ap[i].pci_phys_hi)) {
case PCI_REG_ADDR_G(PCI_ADDR_MEM32):
- if (pci_ap[i].pci_phys_hi & PCI_REG_PF_M) {
- if ((pci_ap[i].pci_phys_low +
- pci_ap[i].pci_size_low) >
- entry->pf_memory_base) {
- entry->pf_memory_base =
- pci_ap[i].pci_phys_low +
- pci_ap[i].pci_size_low;
- }
- } else {
- if ((pci_ap[i].pci_phys_low +
- pci_ap[i].pci_size_low) >
- entry->memory_base) {
- entry->memory_base =
- pci_ap[i].pci_phys_low +
- pci_ap[i].pci_size_low;
+ if (pci_ap[i].pci_phys_hi & PCI_REG_PF_M) {
+ if ((pci_ap[i].pci_phys_low +
+ pci_ap[i].pci_size_low) >
+ entry->pf_memory_base) {
+ entry->pf_memory_base =
+ pci_ap[i].pci_phys_low +
+ pci_ap[i].pci_size_low;
+ }
+ } else {
+ if ((pci_ap[i].pci_phys_low +
+ pci_ap[i].pci_size_low) >
+ entry->memory_base) {
+ entry->memory_base =
+ pci_ap[i].pci_phys_low +
+ pci_ap[i].pci_size_low;
+ }
}
- }
- break;
+ break;
case PCI_REG_ADDR_G(PCI_ADDR_MEM64):
- if (pci_ap[i].pci_phys_hi & PCI_REG_PF_M) {
- if ((PCICFG_LADDR(pci_ap[i].pci_phys_low,
- pci_ap[i].pci_phys_mid) +
- pci_ap[i].pci_size_low) >
- entry->pf_memory_base) {
- entry->pf_memory_base = PCICFG_LADDR(
+ if (pci_ap[i].pci_phys_hi & PCI_REG_PF_M) {
+ if ((PCICFG_LADDR(
pci_ap[i].pci_phys_low,
pci_ap[i].pci_phys_mid) +
- pci_ap[i].pci_size_low;
- }
- } else {
- if ((PCICFG_LADDR(pci_ap[i].pci_phys_low,
- pci_ap[i].pci_phys_mid) +
- pci_ap[i].pci_size_low) >
- entry->memory_base) {
- entry->memory_base = PCICFG_LADDR(
+ pci_ap[i].pci_size_low) >
+ entry->pf_memory_base) {
+ entry->pf_memory_base =
+ PCICFG_LADDR(
+ pci_ap[i].pci_phys_low,
+ pci_ap[i].pci_phys_mid) +
+ pci_ap[i].pci_size_low;
+ }
+ } else {
+ if ((PCICFG_LADDR(
pci_ap[i].pci_phys_low,
pci_ap[i].pci_phys_mid) +
- pci_ap[i].pci_size_low;
+ pci_ap[i].pci_size_low) >
+ entry->memory_base) {
+ entry->memory_base =
+ PCICFG_LADDR(
+ pci_ap[i].pci_phys_low,
+ pci_ap[i].pci_phys_mid) +
+ pci_ap[i].pci_size_low;
+ }
}
- }
- break;
+ break;
case PCI_REG_ADDR_G(PCI_ADDR_IO):
if ((pci_ap[i].pci_phys_low +
pci_ap[i].pci_size_low) >
@@ -4369,7 +4885,7 @@ pcicfg_find_resource_end(dev_info_t *dip, void *hdl)
pci_ap[i].pci_phys_low +
pci_ap[i].pci_size_low;
}
- break;
+ break;
}
}
@@ -4427,12 +4943,12 @@ pcicfg_config_setup(dev_info_t *dip, ddi_acc_handle_t *handle)
/*
* Get the pci register spec from the node
*/
- status = ddi_getlongprop(DDI_DEV_T_ANY,
- dip, DDI_PROP_DONTPASS, "reg", (caddr_t)&reg, &rlen);
+ status = ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "reg",
+ (caddr_t)&reg, &rlen);
switch (status) {
case DDI_PROP_SUCCESS:
- break;
+ break;
case DDI_PROP_NO_MEMORY:
DEBUG0("reg present, but unable to get memory\n");
return (PCICFG_FAILURE);
@@ -4448,8 +4964,8 @@ pcicfg_config_setup(dev_info_t *dip, ddi_acc_handle_t *handle)
attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
- if (ddi_regs_map_setup(anode, 0, &cfgaddr,
- 0, 0, &attr, handle) != DDI_SUCCESS) {
+ if (ddi_regs_map_setup(anode, 0, &cfgaddr, 0, 0, &attr, handle)
+ != DDI_SUCCESS) {
DEBUG0("Failed to setup registers\n");
kmem_free((caddr_t)reg, rlen);
return (PCICFG_FAILURE);
@@ -4495,10 +5011,23 @@ pcicfg_add_config_reg(dev_info_t *dip,
reg[0] = PCICFG_MAKE_REG_HIGH(bus, device, func, 0);
- return (ndi_prop_update_int_array(DDI_DEV_T_NONE, dip,
- "reg", reg, 5));
+ return (ndi_prop_update_int_array(DDI_DEV_T_NONE, dip, "reg", reg, 5));
}
+static int
+pcicfg_ari_configure(dev_info_t *dip)
+{
+ if (pcie_ari_supported(dip) == PCIE_ARI_FORW_NOT_SUPPORTED)
+ return (DDI_FAILURE);
+
+ /*
+ * Until we have resource balancing, dynamically configure
+ * ARI functions without firmware assistamce.
+ */
+ return (DDI_FAILURE);
+}
+
+
#ifdef DEBUG
static void
debug(char *fmt, uintptr_t a1, uintptr_t a2, uintptr_t a3,
@@ -4522,9 +5051,8 @@ pcicfg_get_nslots(dev_info_t *dip, ddi_acc_handle_t handle)
(void) PCI_CAP_LOCATE(handle, PCI_CAP_ID_PCI_E, &cap_id_loc);
(void) PCI_CAP_LOCATE(handle, PCI_CAP_ID_SLOT_ID, &slot_id_loc);
if (cap_id_loc != PCI_CAP_NEXT_PTR_NULL) {
- if (pci_config_get8(handle, cap_id_loc +
- PCI_CAP_ID_REGS_OFF) &
- PCIE_PCIECAP_SLOT_IMPL)
+ if (pci_config_get8(handle, cap_id_loc + PCI_CAP_ID_REGS_OFF) &
+ PCIE_PCIECAP_SLOT_IMPL)
num_slots = 1;
} else /* not a PCIe switch/bridge. Must be a PCI-PCI[-X] bridge */
if (slot_id_loc != PCI_CAP_NEXT_PTR_NULL) {
@@ -4544,11 +5072,10 @@ pcicfg_pcie_dev(dev_info_t *dip, ddi_acc_handle_t handle)
int val;
dev_info_t *pdip = ddi_get_parent(dip);
- if (ddi_prop_lookup_string(DDI_DEV_T_ANY, pdip,
- DDI_PROP_DONTPASS, "device_type", &device_type)
- != DDI_PROP_SUCCESS) {
+ if (ddi_prop_lookup_string(DDI_DEV_T_ANY, pdip, DDI_PROP_DONTPASS,
+ "device_type", &device_type) != DDI_PROP_SUCCESS) {
DEBUG2("device_type property missing for %s#%d",
- ddi_get_name(pdip), ddi_get_instance(pdip));
+ ddi_get_name(pdip), ddi_get_instance(pdip));
return (DDI_FAILURE);
}
DEBUG1("device_type=<%s>\n", device_type);
@@ -4572,9 +5099,9 @@ pcicfg_pcie_device_type(dev_info_t *dip, ddi_acc_handle_t handle)
/* check for all PCIe device_types */
if ((port_type == PCIE_PCIECAP_DEV_TYPE_UP) ||
- (port_type == PCIE_PCIECAP_DEV_TYPE_DOWN) ||
- (port_type == PCIE_PCIECAP_DEV_TYPE_ROOT) ||
- (port_type == PCIE_PCIECAP_DEV_TYPE_PCI2PCIE))
+ (port_type == PCIE_PCIECAP_DEV_TYPE_DOWN) ||
+ (port_type == PCIE_PCIECAP_DEV_TYPE_ROOT) ||
+ (port_type == PCIE_PCIECAP_DEV_TYPE_PCI2PCIE))
return (DDI_SUCCESS);
return (DDI_FAILURE);
@@ -4592,7 +5119,7 @@ pcicfg_pcie_port_type(dev_info_t *dip, ddi_acc_handle_t handle)
(void) PCI_CAP_LOCATE(handle, PCI_CAP_ID_PCI_E, &cap_loc);
if (cap_loc != PCI_CAP_NEXT_PTR_NULL)
port_type = pci_config_get16(handle,
- cap_loc + PCIE_PCIECAP) & PCIE_PCIECAP_DEV_TYPE_MASK;
+ cap_loc + PCIE_PCIECAP) & PCIE_PCIECAP_DEV_TYPE_MASK;
return (port_type);
}
diff --git a/usr/src/uts/intel/io/pci/pci_boot.c b/usr/src/uts/intel/io/pci/pci_boot.c
index 05cafad57a..f8b931dee0 100644
--- a/usr/src/uts/intel/io/pci/pci_boot.c
+++ b/usr/src/uts/intel/io/pci/pci_boot.c
@@ -39,7 +39,7 @@
#include "../../../../common/pci/pci_strings.h"
#include <sys/apic.h>
#include <io/pciex/pcie_nvidia.h>
-#include <io/hotplug/pciehpc/pciehpc_acpi.h>
+#include <sys/hotplug/pci/pciehpc_acpi.h>
#include <sys/acpi/acpi.h>
#include <sys/acpica.h>
#include <sys/intel_iommu.h>
diff --git a/usr/src/uts/intel/io/pci/pci_pci.c b/usr/src/uts/intel/io/pci/pci_pci.c
index be461462bd..66f7f69aae 100644
--- a/usr/src/uts/intel/io/pci/pci_pci.c
+++ b/usr/src/uts/intel/io/pci/pci_pci.c
@@ -34,6 +34,7 @@
#include <sys/autoconf.h>
#include <sys/ddi_impldefs.h>
#include <sys/pci.h>
+#include <sys/pci_impl.h>
#include <sys/pcie_impl.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
@@ -41,6 +42,7 @@
#include <sys/ddifm.h>
#include <sys/ndifm.h>
#include <sys/fm/protocol.h>
+#include <sys/hotplug/pci/pcie_hp.h>
#include <sys/hotplug/pci/pcihp.h>
#include <sys/pci_intr_lib.h>
#include <sys/psm.h>
@@ -101,19 +103,20 @@ struct bus_ops ppb_bus_ops = {
ddi_dma_mctl,
ppb_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)(); */
- ppb_fm_init, /* (*bus_fm_init)(); */
- NULL, /* (*bus_fm_fini)(); */
- NULL, /* (*bus_fm_access_enter)(); */
- NULL, /* (*bus_fm_access_exit)(); */
- NULL, /* (*bus_power)(); */
- ppb_intr_ops /* (*bus_intr_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)(); */
+ ppb_fm_init, /* (*bus_fm_init)(); */
+ NULL, /* (*bus_fm_fini)(); */
+ NULL, /* (*bus_fm_access_enter)(); */
+ NULL, /* (*bus_fm_access_exit)(); */
+ NULL, /* (*bus_power)(); */
+ ppb_intr_ops, /* (*bus_intr_op)(); */
+ pcie_hp_common_ops /* (*bus_hp_op)(); */
};
/*
@@ -175,7 +178,7 @@ struct dev_ops ppb_ops = {
static struct modldrv modldrv = {
&mod_driverops, /* Type of module */
- "PCI to PCI bridge nexus driver",
+ "Standard PCI to PCI bridge nexus driver",
&ppb_ops, /* driver ops */
};
@@ -194,6 +197,7 @@ typedef struct {
dev_info_t *dip;
int ppb_fmcap;
ddi_iblock_cookie_t ppb_fm_ibc;
+ kmutex_t ppb_mutex;
kmutex_t ppb_peek_poke_mutex;
kmutex_t ppb_err_mutex;
@@ -276,6 +280,7 @@ ppb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
dev_info_t *pdip;
ddi_acc_handle_t config_handle;
char *bus;
+ int ret;
switch (cmd) {
case DDI_ATTACH:
@@ -307,6 +312,7 @@ ppb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
DDI_FM_DMACHK_CAPABLE;
ddi_fm_init(devi, &ppb->ppb_fmcap, &ppb->ppb_fm_ibc);
+ mutex_init(&ppb->ppb_mutex, NULL, MUTEX_DRIVER, NULL);
mutex_init(&ppb->ppb_err_mutex, NULL, MUTEX_DRIVER,
(void *)ppb->ppb_fm_ibc);
mutex_init(&ppb->ppb_peek_poke_mutex, NULL, MUTEX_DRIVER,
@@ -355,14 +361,19 @@ ppb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
pci_config_teardown(&config_handle);
/*
- * 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.
+ * Initialize hotplug support on this bus.
*/
- if (pcihp_init(devi) != DDI_SUCCESS)
+ if (ppb->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV)
+ ret = pcie_init(devi, NULL);
+ else
+ ret = pcihp_init(devi);
+
+ if (ret != DDI_SUCCESS) {
cmn_err(CE_WARN,
"pci: Failed to setup hotplug framework");
+ (void) ppb_detach(devi, DDI_DETACH);
+ return (ret);
+ }
ddi_report_dev(devi);
return (DDI_SUCCESS);
@@ -387,6 +398,7 @@ static int
ppb_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
{
ppb_devstate_t *ppb;
+ int ret;
switch (cmd) {
case DDI_DETACH:
@@ -398,8 +410,18 @@ ppb_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
if (ppb->ppb_fmcap & (DDI_FM_ERRCB_CAPABLE |
DDI_FM_EREPORT_CAPABLE))
pci_ereport_teardown(devi);
+
+ /*
+ * Uninitialize hotplug support on this bus.
+ */
+ ret = (ppb->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) ?
+ pcie_uninit(devi) : pcihp_uninit(devi);
+ if (ret != DDI_SUCCESS)
+ return (DDI_FAILURE);
+
mutex_destroy(&ppb->ppb_peek_poke_mutex);
mutex_destroy(&ppb->ppb_err_mutex);
+ mutex_destroy(&ppb->ppb_mutex);
ddi_fm_fini(devi);
/*
@@ -407,10 +429,6 @@ ppb_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
*/
ddi_soft_state_free(ppb_state, ddi_get_instance(devi));
- /*
- * Uninitialize hotplug support on this bus.
- */
- (void) pcihp_uninit(devi);
return (DDI_SUCCESS);
case DDI_SUSPEND:
@@ -886,29 +904,97 @@ OUT:
return (rv);
}
+/* ARGSUSED */
static int
ppb_open(dev_t *devp, int flags, int otyp, cred_t *credp)
{
+ int instance = PCI_MINOR_NUM_TO_INSTANCE(getminor(*devp));
+ ppb_devstate_t *ppb_p = ddi_get_soft_state(ppb_state, instance);
+ int rv;
+
+ if (ppb_p == NULL)
+ return (ENXIO);
+
+ /*
+ * Ioctls will be handled by PCI Express framework for all
+ * PCIe platforms
+ */
+ if (ppb_p->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) {
+ mutex_enter(&ppb_p->ppb_mutex);
+ rv = pcie_open(ppb_p->dip, devp, flags, otyp, credp);
+ mutex_exit(&ppb_p->ppb_mutex);
+ return (rv);
+ }
+
return ((pcihp_get_cb_ops())->cb_open(devp, flags, otyp, credp));
}
+/* ARGSUSED */
static int
ppb_close(dev_t dev, int flags, int otyp, cred_t *credp)
{
+ int instance = PCI_MINOR_NUM_TO_INSTANCE(getminor(dev));
+ ppb_devstate_t *ppb_p = ddi_get_soft_state(ppb_state, instance);
+ int rv;
+
+ if (ppb_p == NULL)
+ return (ENXIO);
+
+ mutex_enter(&ppb_p->ppb_mutex);
+ /*
+ * Ioctls will be handled by PCI Express framework for all
+ * PCIe platforms
+ */
+ if (ppb_p->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) {
+ rv = pcie_close(ppb_p->dip, dev, flags, otyp, credp);
+ mutex_exit(&ppb_p->ppb_mutex);
+ return (rv);
+ }
+
+ mutex_exit(&ppb_p->ppb_mutex);
return ((pcihp_get_cb_ops())->cb_close(dev, flags, otyp, credp));
}
+/*
+ * ppb_ioctl: devctl hotplug controls
+ */
+/* ARGSUSED */
static int
-ppb_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp)
+ppb_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
+ int *rvalp)
{
+ int instance = PCI_MINOR_NUM_TO_INSTANCE(getminor(dev));
+ ppb_devstate_t *ppb_p = ddi_get_soft_state(ppb_state, instance);
+
+ if (ppb_p == NULL)
+ return (ENXIO);
+
+ /*
+ * Ioctls will be handled by PCI Express framework for all
+ * PCIe platforms
+ */
+ if (ppb_p->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV)
+ return (pcie_ioctl(ppb_p->dip, dev, cmd, arg, mode, credp,
+ rvalp));
+
return ((pcihp_get_cb_ops())->cb_ioctl(dev, cmd, arg, mode, credp,
rvalp));
}
static int
-ppb_prop_op(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op,
- int flags, char *name, caddr_t valuep, int *lengthp)
+ppb_prop_op(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op, int flags,
+ char *name, caddr_t valuep, int *lengthp)
{
+ int instance = PCI_MINOR_NUM_TO_INSTANCE(getminor(dev));
+ ppb_devstate_t *ppb_p = ddi_get_soft_state(ppb_state, instance);
+
+ if (ppb_p == NULL)
+ return (ENXIO);
+
+ if (ppb_p->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV)
+ return (pcie_prop_op(dev, dip, prop_op, flags, name,
+ valuep, lengthp));
+
return ((pcihp_get_cb_ops())->cb_prop_op(dev, dip, prop_op, flags,
name, valuep, lengthp));
}
@@ -916,7 +1002,30 @@ ppb_prop_op(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op,
static int
ppb_info(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
{
- return (pcihp_info(dip, cmd, arg, result));
+ minor_t minor = getminor((dev_t)arg);
+ int instance = PCI_MINOR_NUM_TO_INSTANCE(minor);
+ ppb_devstate_t *ppb_p = ddi_get_soft_state(ppb_state, instance);
+
+ if (ppb_p == NULL)
+ return (DDI_FAILURE);
+
+ if (ppb_p->parent_bus != PCIE_PCIECAP_DEV_TYPE_PCIE_DEV)
+ return (pcihp_info(dip, cmd, arg, result));
+
+ switch (cmd) {
+ default:
+ return (DDI_FAILURE);
+
+ case DDI_INFO_DEVT2INSTANCE:
+ *result = (void *)(uintptr_t)instance;
+ return (DDI_SUCCESS);
+
+ case DDI_INFO_DEVT2DEVINFO:
+ if (ppb_p == NULL)
+ return (DDI_FAILURE);
+ *result = (void *)ppb_p->dip;
+ return (DDI_SUCCESS);
+ }
}
void ppb_peekpoke_cb(dev_info_t *dip, ddi_fm_error_t *derr) {
diff --git a/usr/src/uts/intel/io/hotplug/pciehpc/pciehpc_acpi.c b/usr/src/uts/intel/io/pciex/hotplug/pciehpc_acpi.c
index 11aabfbcad..e7cac4cd65 100644
--- a/usr/src/uts/intel/io/hotplug/pciehpc/pciehpc_acpi.c
+++ b/usr/src/uts/intel/io/pciex/hotplug/pciehpc_acpi.c
@@ -26,6 +26,8 @@
/*
* ACPI interface related functions used in PCIEHPC driver module.
+ *
+ * NOTE: This file is compiled and delivered through misc/pcie module.
*/
#include <sys/note.h>
@@ -38,36 +40,38 @@
#include <sys/pci.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
+#include <sys/sunndi.h>
+#include <sys/pci_impl.h>
#include <sys/pcie_acpi.h>
-#include "pciehpc_acpi.h"
+#include <sys/hotplug/pci/pcie_hp.h>
+#include <sys/hotplug/pci/pciehpc_acpi.h>
/* local static functions */
-static int pciehpc_acpi_hpc_init(pciehpc_t *ctrl_p);
-static int pciehpc_acpi_hpc_uninit(pciehpc_t *ctrl_p);
-static int pciehpc_acpi_slotinfo_init(pciehpc_t *ctrl_p);
-static int pciehpc_acpi_slotinfo_uninit(pciehpc_t *ctrl_p);
-static int pciehpc_acpi_enable_intr(pciehpc_t *ctrl_p);
-static int pciehpc_acpi_disable_intr(pciehpc_t *ctrl_p);
-static int pciehpc_acpi_slot_connect(caddr_t ops_arg, hpc_slot_t slot_hdl,
- void *data, uint_t flags);
-static int pciehpc_acpi_slot_disconnect(caddr_t ops_arg, hpc_slot_t slot_hdl,
- void *data, uint_t flags);
-static void pciehpc_acpi_setup_ops(pciehpc_t *ctrl_p);
-
-static ACPI_STATUS pciehpc_acpi_install_event_handler(pciehpc_t *ctrl_p);
-static void pciehpc_acpi_uninstall_event_handler(pciehpc_t *ctrl_p);
-static ACPI_STATUS pciehpc_acpi_power_on_slot(pciehpc_t *ctrl_p);
-static ACPI_STATUS pciehpc_acpi_power_off_slot(pciehpc_t *ctrl_p);
+static int pciehpc_acpi_hpc_init(pcie_hp_ctrl_t *ctrl_p);
+static int pciehpc_acpi_hpc_uninit(pcie_hp_ctrl_t *ctrl_p);
+static int pciehpc_acpi_slotinfo_init(pcie_hp_ctrl_t *ctrl_p);
+static int pciehpc_acpi_slotinfo_uninit(pcie_hp_ctrl_t *ctrl_p);
+static int pciehpc_acpi_enable_intr(pcie_hp_ctrl_t *ctrl_p);
+static int pciehpc_acpi_disable_intr(pcie_hp_ctrl_t *ctrl_p);
+static int pciehpc_acpi_slot_poweron(pcie_hp_slot_t *slot_p,
+ ddi_hp_cn_state_t *result);
+static int pciehpc_acpi_slot_poweroff(pcie_hp_slot_t *slot_p,
+ ddi_hp_cn_state_t *result);
+static void pciehpc_acpi_setup_ops(pcie_hp_ctrl_t *ctrl_p);
+
+static ACPI_STATUS pciehpc_acpi_install_event_handler(pcie_hp_ctrl_t *ctrl_p);
+static void pciehpc_acpi_uninstall_event_handler(pcie_hp_ctrl_t *ctrl_p);
+static ACPI_STATUS pciehpc_acpi_power_on_slot(pcie_hp_ctrl_t *ctrl_p);
+static ACPI_STATUS pciehpc_acpi_power_off_slot(pcie_hp_ctrl_t *ctrl_p);
static void pciehpc_acpi_notify_handler(ACPI_HANDLE device, uint32_t val,
void *context);
-static ACPI_STATUS pciehpc_acpi_ej0_present(ACPI_HANDLE pcibus_obj);
static ACPI_STATUS pciehpc_acpi_get_dev_state(ACPI_HANDLE obj, int *statusp);
/*
* Update ops vector with platform specific (ACPI, CK8-04,...) functions.
*/
void
-pciehpc_update_ops(pciehpc_t *ctrl_p)
+pciehpc_update_ops(pcie_hp_ctrl_t *ctrl_p)
{
boolean_t hp_native_mode = B_FALSE;
uint32_t osc_flags = OSC_CONTROL_PCIE_NAT_HP;
@@ -82,34 +86,39 @@ pciehpc_update_ops(pciehpc_t *ctrl_p)
* (ACPI, CK8-04,...) impl. ops
*/
- if (pcie_acpi_osc(ctrl_p->dip, &osc_flags) == DDI_SUCCESS) {
+ if (pcie_acpi_osc(ctrl_p->hc_dip, &osc_flags) == DDI_SUCCESS) {
hp_native_mode = (osc_flags & OSC_CONTROL_PCIE_NAT_HP) ?
B_TRUE : B_FALSE;
}
if (!hp_native_mode) {
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
+
/* update ops vector for ACPI mode */
pciehpc_acpi_setup_ops(ctrl_p);
- ctrl_p->hp_mode = PCIEHPC_ACPI_HP_MODE;
+ bus_p->bus_hp_sup_modes |= PCIE_ACPI_HP_MODE;
+ bus_p->bus_hp_curr_mode = PCIE_ACPI_HP_MODE;
}
}
-static void
-pciehpc_acpi_setup_ops(pciehpc_t *ctrl_p)
+void
+pciehpc_acpi_setup_ops(pcie_hp_ctrl_t *ctrl_p)
{
- ctrl_p->ops.init_hpc_hw = pciehpc_acpi_hpc_init;
- ctrl_p->ops.init_hpc_slotinfo = pciehpc_acpi_slotinfo_init;
- ctrl_p->ops.disable_hpc_intr = pciehpc_acpi_disable_intr;
- ctrl_p->ops.enable_hpc_intr = pciehpc_acpi_enable_intr;
- ctrl_p->ops.uninit_hpc_hw = pciehpc_acpi_hpc_uninit;
- ctrl_p->ops.uninit_hpc_slotinfo = pciehpc_acpi_slotinfo_uninit;
+ ctrl_p->hc_ops.init_hpc_hw = pciehpc_acpi_hpc_init;
+ ctrl_p->hc_ops.uninit_hpc_hw = pciehpc_acpi_hpc_uninit;
+ ctrl_p->hc_ops.init_hpc_slotinfo = pciehpc_acpi_slotinfo_init;
+ ctrl_p->hc_ops.uninit_hpc_slotinfo = pciehpc_acpi_slotinfo_uninit;
+ ctrl_p->hc_ops.poweron_hpc_slot = pciehpc_acpi_slot_poweron;
+ ctrl_p->hc_ops.poweroff_hpc_slot = pciehpc_acpi_slot_poweroff;
+ ctrl_p->hc_ops.disable_hpc_intr = pciehpc_acpi_disable_intr;
+ ctrl_p->hc_ops.enable_hpc_intr = pciehpc_acpi_enable_intr;
}
/*
* Intialize hot plug control for ACPI mode.
*/
static int
-pciehpc_acpi_hpc_init(pciehpc_t *ctrl_p)
+pciehpc_acpi_hpc_init(pcie_hp_ctrl_t *ctrl_p)
{
ACPI_HANDLE pcibus_obj;
int status = AE_ERROR;
@@ -120,7 +129,7 @@ pciehpc_acpi_hpc_init(pciehpc_t *ctrl_p)
uint16_t slot_methods = 0;
/* get the ACPI object for the bus node */
- status = acpica_get_handle(ctrl_p->dip, &pcibus_obj);
+ status = acpica_get_handle(ctrl_p->hc_dip, &pcibus_obj);
if (status != AE_OK)
return (DDI_FAILURE);
@@ -159,12 +168,7 @@ pciehpc_acpi_hpc_init(pciehpc_t *ctrl_p)
acpi_p->slot_dev_obj = slot_dev_obj;
acpi_p->bus_methods = bus_methods;
acpi_p->slot_methods = slot_methods;
- ctrl_p->misc_data = acpi_p;
- ctrl_p->hp_mode = PCIEHPC_ACPI_HP_MODE;
-
- /* initialize the slot mutex */
- mutex_init(&ctrl_p->pciehpc_mutex, NULL, MUTEX_DRIVER,
- (void *)PCIEHPC_INTR_PRI);
+ ctrl_p->hc_misc_data = acpi_p;
return (DDI_SUCCESS);
}
@@ -173,17 +177,14 @@ pciehpc_acpi_hpc_init(pciehpc_t *ctrl_p)
* Uninitialize HPC.
*/
static int
-pciehpc_acpi_hpc_uninit(pciehpc_t *ctrl_p)
+pciehpc_acpi_hpc_uninit(pcie_hp_ctrl_t *ctrl_p)
{
/* free up buffer used for misc_data */
- if (ctrl_p->misc_data) {
- kmem_free(ctrl_p->misc_data, sizeof (pciehpc_acpi_t));
- ctrl_p->misc_data = NULL;
+ if (ctrl_p->hc_misc_data) {
+ kmem_free(ctrl_p->hc_misc_data, sizeof (pciehpc_acpi_t));
+ ctrl_p->hc_misc_data = NULL;
}
- /* destroy the mutex */
- mutex_destroy(&ctrl_p->pciehpc_mutex);
-
return (DDI_SUCCESS);
}
@@ -193,7 +194,7 @@ pciehpc_acpi_hpc_uninit(pciehpc_t *ctrl_p)
*/
/*ARGSUSED*/
static int
-pciehpc_acpi_enable_intr(pciehpc_t *ctrl_p)
+pciehpc_acpi_enable_intr(pcie_hp_ctrl_t *ctrl_p)
{
return (DDI_SUCCESS);
}
@@ -204,7 +205,7 @@ pciehpc_acpi_enable_intr(pciehpc_t *ctrl_p)
*/
/*ARGSUSED*/
static int
-pciehpc_acpi_disable_intr(pciehpc_t *ctrl_p)
+pciehpc_acpi_disable_intr(pcie_hp_ctrl_t *ctrl_p)
{
return (DDI_SUCCESS);
}
@@ -220,67 +221,60 @@ pciehpc_acpi_disable_intr(pciehpc_t *ctrl_p)
* the hot plug capabilities (ATTN button, MRL, etc.).
*/
static int
-pciehpc_acpi_slotinfo_init(pciehpc_t *ctrl_p)
+pciehpc_acpi_slotinfo_init(pcie_hp_ctrl_t *ctrl_p)
{
- uint32_t slot_capabilities;
- pciehpc_slot_t *p = &ctrl_p->slot;
+ uint32_t slot_capabilities;
+ pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0];
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
+ mutex_enter(&ctrl_p->hc_mutex);
/*
- * setup HPS framework slot ops structure
+ * setup DDI HP framework slot information structure
*/
- p->slot_ops.hpc_version = HPC_SLOT_OPS_VERSION;
- p->slot_ops.hpc_op_connect = pciehpc_acpi_slot_connect;
- p->slot_ops.hpc_op_disconnect = pciehpc_acpi_slot_disconnect;
- p->slot_ops.hpc_op_insert = NULL;
- p->slot_ops.hpc_op_remove = NULL;
- p->slot_ops.hpc_op_control = pciehpc_slot_control;
+ slot_p->hs_device_num = 0;
+ slot_p->hs_info.cn_type = DDI_HP_CN_TYPE_PCIE;
+ slot_p->hs_info.cn_type_str = PCIE_ACPI_HP_TYPE;
+ slot_p->hs_info.cn_child = NULL;
- /*
- * setup HPS framework slot information structure
- */
- p->slot_info.version = HPC_SLOT_OPS_VERSION;
- p->slot_info.slot_type = HPC_SLOT_TYPE_PCIE;
- p->slot_info.slot_flags =
- HPC_SLOT_CREATE_DEVLINK | HPC_SLOT_NO_AUTO_ENABLE;
- p->slot_info.pci_slot_capabilities = HPC_SLOT_64BITS;
- /* the device number is fixed as 0 as per the spec */
- p->slot_info.pci_dev_num = 0;
+ slot_p->hs_minor =
+ PCI_MINOR_NUM(ddi_get_instance(ctrl_p->hc_dip),
+ slot_p->hs_device_num);
/* read Slot Capabilities Register */
slot_capabilities = pciehpc_reg_get32(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCAP);
+ bus_p->bus_pcie_off + PCIE_SLOTCAP);
/* setup slot number/name */
pciehpc_set_slot_name(ctrl_p);
/* check if Attn Button present */
- ctrl_p->has_attn = (slot_capabilities & PCIE_SLOTCAP_ATTN_BUTTON) ?
- B_TRUE : B_FALSE;
+ ctrl_p->hc_has_attn = (slot_capabilities &
+ PCIE_SLOTCAP_ATTN_BUTTON) ? B_TRUE : B_FALSE;
/* check if Manual Retention Latch sensor present */
- ctrl_p->has_mrl = (slot_capabilities & PCIE_SLOTCAP_MRL_SENSOR) ?
+ ctrl_p->hc_has_mrl = (slot_capabilities & PCIE_SLOTCAP_MRL_SENSOR) ?
B_TRUE : B_FALSE;
/*
* PCI-E (draft) version 1.1 defines EMI Lock Present bit
* in Slot Capabilities register. Check for it.
*/
- ctrl_p->has_emi_lock = (slot_capabilities &
+ ctrl_p->hc_has_emi_lock = (slot_capabilities &
PCIE_SLOTCAP_EMI_LOCK_PRESENT) ? B_TRUE : B_FALSE;
- /* initialize synchronization conditional variable */
- cv_init(&ctrl_p->slot.cmd_comp_cv, NULL, CV_DRIVER, NULL);
- ctrl_p->slot.command_pending = B_FALSE;
-
/* get current slot state from the hw */
- pciehpc_get_slot_state(ctrl_p);
+ pciehpc_get_slot_state(slot_p);
+ if (slot_p->hs_info.cn_state >= DDI_HP_CN_STATE_ENABLED)
+ slot_p->hs_condition = AP_COND_OK;
+
+ mutex_exit(&ctrl_p->hc_mutex);
/* setup Notify() handler for hot plug events from ACPI BIOS */
if (pciehpc_acpi_install_event_handler(ctrl_p) != AE_OK)
return (DDI_FAILURE);
- PCIEHPC_DEBUG((CE_NOTE, "ACPI hot plug is enabled for slot #%d\n",
- ctrl_p->slot.slotNum));
+ PCIE_DBG("ACPI hot plug is enabled for slot #%d\n",
+ slot_p->hs_phy_slot_num);
return (DDI_SUCCESS);
}
@@ -290,134 +284,128 @@ pciehpc_acpi_slotinfo_init(pciehpc_t *ctrl_p)
* specific cleanup.
*/
static int
-pciehpc_acpi_slotinfo_uninit(pciehpc_t *ctrl_p)
+pciehpc_acpi_slotinfo_uninit(pcie_hp_ctrl_t *ctrl_p)
{
+ pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0];
+
/* uninstall Notify() event handler */
pciehpc_acpi_uninstall_event_handler(ctrl_p);
-
- cv_destroy(&ctrl_p->slot.cmd_comp_cv);
+ if (slot_p->hs_info.cn_name)
+ kmem_free(slot_p->hs_info.cn_name,
+ strlen(slot_p->hs_info.cn_name) + 1);
return (DDI_SUCCESS);
}
/*
- * This function is same as pciehpc_slot_connect() except that it
+ * This function is same as pciehpc_slot_poweron() except that it
* uses ACPI method PS0 to enable power to the slot. If no PS0 method
- * is present then it returns HPC_ERR_FAILED.
+ * is present then it returns DDI_FAILURE.
*/
/*ARGSUSED*/
static int
-pciehpc_acpi_slot_connect(caddr_t ops_arg, hpc_slot_t slot_hdl,
- void *data, uint_t flags)
+pciehpc_acpi_slot_poweron(pcie_hp_slot_t *slot_p, ddi_hp_cn_state_t *result)
{
- uint16_t status;
- uint16_t control;
-
- pciehpc_t *ctrl_p = (pciehpc_t *)ops_arg;
-
- ASSERT(slot_hdl == ctrl_p->slot.slot_handle);
+ pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl;
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
+ uint16_t status, control;
- mutex_enter(&ctrl_p->pciehpc_mutex);
+ ASSERT(MUTEX_HELD(&ctrl_p->hc_mutex));
/* get the current state of the slot */
- pciehpc_get_slot_state(ctrl_p);
+ pciehpc_get_slot_state(slot_p);
- /* check if the slot is already in the 'connected' state */
- if (ctrl_p->slot.slot_state == HPC_SLOT_CONNECTED) {
+ /* check if the slot is already in the 'ENABLED' state */
+ if (slot_p->hs_info.cn_state == DDI_HP_CN_STATE_ENABLED) {
/* slot is already in the 'connected' state */
- PCIEHPC_DEBUG((CE_NOTE, "slot %d already connected\n",
- ctrl_p->slot.slotNum));
- mutex_exit(&ctrl_p->pciehpc_mutex);
- return (HPC_SUCCESS);
+ PCIE_DBG("slot %d already connected\n",
+ slot_p->hs_phy_slot_num);
+
+ *result = slot_p->hs_info.cn_state;
+ return (DDI_SUCCESS);
}
/* read the Slot Status Register */
status = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTSTS);
+ bus_p->bus_pcie_off + PCIE_SLOTSTS);
/* make sure the MRL switch is closed if present */
- if ((ctrl_p->has_mrl) && (status & PCIE_SLOTSTS_MRL_SENSOR_OPEN)) {
+ if ((ctrl_p->hc_has_mrl) && (status & PCIE_SLOTSTS_MRL_SENSOR_OPEN)) {
/* MRL switch is open */
cmn_err(CE_WARN, "MRL switch is open on slot %d",
- ctrl_p->slot.slotNum);
+ slot_p->hs_phy_slot_num);
goto cleanup;
}
/* make sure the slot has a device present */
if (!(status & PCIE_SLOTSTS_PRESENCE_DETECTED)) {
/* slot is empty */
- PCIEHPC_DEBUG((CE_NOTE, "slot %d is empty\n",
- ctrl_p->slot.slotNum));
+ PCIE_DBG("slot %d is empty\n", slot_p->hs_phy_slot_num);
goto cleanup;
}
/* get the current state of Slot Control Register */
control = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL);
+ bus_p->bus_pcie_off + PCIE_SLOTCTL);
/* check if the slot's power state is ON */
if (!(control & PCIE_SLOTCTL_PWR_CONTROL)) {
/* slot is already powered up */
- PCIEHPC_DEBUG((CE_NOTE, "slot %d already connected\n",
- ctrl_p->slot.slotNum));
- ctrl_p->slot.slot_state = HPC_SLOT_CONNECTED;
- mutex_exit(&ctrl_p->pciehpc_mutex);
- return (HPC_SUCCESS);
+ PCIE_DBG("slot %d already connected\n",
+ slot_p->hs_phy_slot_num);
+
+ *result = slot_p->hs_info.cn_state;
+ return (DDI_SUCCESS);
}
/* turn on power to the slot using ACPI method (PS0) */
if (pciehpc_acpi_power_on_slot(ctrl_p) != AE_OK)
goto cleanup;
- ctrl_p->slot.slot_state = HPC_SLOT_CONNECTED;
- mutex_exit(&ctrl_p->pciehpc_mutex);
- return (HPC_SUCCESS);
+ *result = slot_p->hs_info.cn_state = DDI_HP_CN_STATE_POWERED;
+ return (DDI_SUCCESS);
cleanup:
- mutex_exit(&ctrl_p->pciehpc_mutex);
- return (HPC_ERR_FAILED);
+ return (DDI_FAILURE);
}
/*
- * This function is same as pciehpc_slot_disconnect() except that it
+ * This function is same as pciehpc_slot_poweroff() except that it
* uses ACPI method EJ0 to disable power to the slot. If no EJ0 method
- * is present then it returns HPC_ERR_FAILED.
+ * is present then it returns DDI_FAILURE.
*/
/*ARGSUSED*/
static int
-pciehpc_acpi_slot_disconnect(caddr_t ops_arg, hpc_slot_t slot_hdl,
- void *data, uint_t flags)
+pciehpc_acpi_slot_poweroff(pcie_hp_slot_t *slot_p, ddi_hp_cn_state_t *result)
{
- uint16_t status;
+ pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl;
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
+ uint16_t status;
- pciehpc_t *ctrl_p = (pciehpc_t *)ops_arg;
-
- ASSERT(slot_hdl == ctrl_p->slot.slot_handle);
-
- mutex_enter(&ctrl_p->pciehpc_mutex);
+ ASSERT(MUTEX_HELD(&ctrl_p->hc_mutex));
/* get the current state of the slot */
- pciehpc_get_slot_state(ctrl_p);
+ pciehpc_get_slot_state(slot_p);
- /* check if the slot is already in the 'disconnected' state */
- if (ctrl_p->slot.slot_state == HPC_SLOT_DISCONNECTED) {
+ /* check if the slot is already in the state less than 'powered' */
+ if (slot_p->hs_info.cn_state < DDI_HP_CN_STATE_POWERED) {
/* slot is in the 'disconnected' state */
- PCIEHPC_DEBUG((CE_NOTE, "slot %d already disconnected\n",
- ctrl_p->slot.slotNum));
- ASSERT(ctrl_p->slot.power_led_state == HPC_LED_OFF);
- mutex_exit(&ctrl_p->pciehpc_mutex);
- return (HPC_SUCCESS);
+ PCIE_DBG("slot %d already disconnected\n",
+ slot_p->hs_phy_slot_num);
+ ASSERT(slot_p->hs_power_led_state == PCIE_HP_LED_OFF);
+
+ *result = slot_p->hs_info.cn_state;
+ return (DDI_SUCCESS);
}
/* read the Slot Status Register */
status = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTSTS);
+ bus_p->bus_pcie_off + PCIE_SLOTSTS);
/* make sure the slot has a device present */
if (!(status & PCIE_SLOTSTS_PRESENCE_DETECTED)) {
/* slot is empty */
- PCIEHPC_DEBUG((CE_WARN, "slot %d is empty",
- ctrl_p->slot.slotNum));
+ PCIE_DBG("slot %d is empty", slot_p->hs_phy_slot_num);
goto cleanup;
}
@@ -425,13 +413,15 @@ pciehpc_acpi_slot_disconnect(caddr_t ops_arg, hpc_slot_t slot_hdl,
if (pciehpc_acpi_power_off_slot(ctrl_p) != AE_OK)
goto cleanup;
- ctrl_p->slot.slot_state = HPC_SLOT_DISCONNECTED;
- mutex_exit(&ctrl_p->pciehpc_mutex);
- return (HPC_SUCCESS);
+ /* get the current state of the slot */
+ pciehpc_get_slot_state(slot_p);
+
+ *result = slot_p->hs_info.cn_state;
+
+ return (DDI_SUCCESS);
cleanup:
- mutex_exit(&ctrl_p->pciehpc_mutex);
- return (HPC_ERR_FAILED);
+ return (DDI_FAILURE);
}
/*
@@ -439,14 +429,15 @@ cleanup:
* as device function (dev=0,func=0).
*/
static ACPI_STATUS
-pciehpc_acpi_install_event_handler(pciehpc_t *ctrl_p)
+pciehpc_acpi_install_event_handler(pcie_hp_ctrl_t *ctrl_p)
{
+ pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0];
int status = AE_OK;
pciehpc_acpi_t *acpi_p;
- PCIEHPC_DEBUG3((CE_CONT, "install event handler for slot %d\n",
- ctrl_p->slot.slotNum));
- acpi_p = ctrl_p->misc_data;
+ PCIE_DBG("install event handler for slot %d\n",
+ slot_p->hs_phy_slot_num);
+ acpi_p = ctrl_p->hc_misc_data;
if (acpi_p->slot_dev_obj == NULL)
return (AE_NOT_FOUND);
@@ -467,8 +458,8 @@ pciehpc_acpi_install_event_handler(pciehpc_t *ctrl_p)
* on all of the 8 possible device functions so, subscribing to
* one of them is sufficient.
*/
- status = AcpiInstallNotifyHandler(acpi_p->bus_obj, ACPI_SYSTEM_NOTIFY,
- pciehpc_acpi_notify_handler, (void *)ctrl_p);
+ status = AcpiInstallNotifyHandler(acpi_p->bus_obj,
+ ACPI_SYSTEM_NOTIFY, pciehpc_acpi_notify_handler, (void *)ctrl_p);
return (status);
cleanup:
@@ -481,37 +472,37 @@ cleanup:
static void
pciehpc_acpi_notify_handler(ACPI_HANDLE device, uint32_t val, void *context)
{
- pciehpc_t *ctrl_p = context;
+ pcie_hp_ctrl_t *ctrl_p = context;
+ pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0];
pciehpc_acpi_t *acpi_p;
+ ddi_hp_cn_state_t curr_state;
int dev_state = 0;
- PCIEHPC_DEBUG((CE_CONT, "received Notify(%d) event on slot #%d\n",
- val, ctrl_p->slot.slotNum));
+ PCIE_DBG("received Notify(%d) event on slot #%d\n",
+ val, slot_p->hs_phy_slot_num);
- mutex_enter(&ctrl_p->pciehpc_mutex);
+ mutex_enter(&ctrl_p->hc_mutex);
/*
* get the state of the device (from _STA method)
*/
- acpi_p = ctrl_p->misc_data;
+ acpi_p = ctrl_p->hc_misc_data;
if (pciehpc_acpi_get_dev_state(acpi_p->slot_dev_obj,
&dev_state) != AE_OK) {
cmn_err(CE_WARN, "failed to get device status on slot %d",
- ctrl_p->slot.slotNum);
+ slot_p->hs_phy_slot_num);
}
- PCIEHPC_DEBUG((CE_CONT, "(1)device state on slot #%d: 0x%x\n",
- ctrl_p->slot.slotNum, dev_state));
+ PCIE_DBG("(1)device state on slot #%d: 0x%x\n",
+ slot_p->hs_phy_slot_num, dev_state);
- pciehpc_get_slot_state(ctrl_p);
+ curr_state = slot_p->hs_info.cn_state;
+ pciehpc_get_slot_state(slot_p);
switch (val) {
case 0: /* (re)enumerate the device */
case 3: /* Request Eject */
- if (ctrl_p->slot.slot_state != HPC_SLOT_CONNECTED) {
- /* unexpected slot state; surprise removal? */
- cmn_err(CE_WARN, "Unexpected event on slot #%d"
- "(state 0x%x)", ctrl_p->slot.slotNum, dev_state);
- }
+ {
+ ddi_hp_cn_state_t target_state;
/*
* Ignore the event if ATTN button is not present (ACPI BIOS
@@ -521,32 +512,53 @@ pciehpc_acpi_notify_handler(ACPI_HANDLE device, uint32_t val, void *context)
* where the ACPI BIOS is generating the event for some other
* (non hot-plug) operations (bug).
*/
- if (ctrl_p->has_attn == B_FALSE) {
- PCIEHPC_DEBUG((CE_CONT, "Ignore the unexpected event "
+ if (ctrl_p->hc_has_attn == B_FALSE) {
+ PCIE_DBG("Ignore the unexpected event "
"on slot #%d (state 0x%x)",
- ctrl_p->slot.slotNum, dev_state));
+ slot_p->hs_phy_slot_num, dev_state);
break;
}
- /* send the ATTN button event to HPS framework */
- (void) hpc_slot_event_notify(ctrl_p->slot.slot_handle,
- HPC_EVENT_SLOT_ATTN, HPC_EVENT_NORMAL);
+ /* send the event to DDI Hotplug framework */
+ if (curr_state < DDI_HP_CN_STATE_POWERED) {
+ /* Insertion. Upgrade state to ENABLED */
+ target_state = DDI_HP_CN_STATE_ENABLED;
+
+ /*
+ * When pressing ATTN button to enable a card, the slot
+ * could be powered. Keep the slot state on PWOERED
+ * other than ENABLED.
+ */
+ if (slot_p->hs_info.cn_state == DDI_HP_CN_STATE_ENABLED)
+ slot_p->hs_info.cn_state =
+ DDI_HP_CN_STATE_POWERED;
+ } else {
+ /* Want to remove; Power off Connection */
+ target_state = DDI_HP_CN_STATE_EMPTY;
+ }
+
+ (void) ndi_hp_state_change_req(slot_p->hs_ctrl->hc_dip,
+ slot_p->hs_info.cn_name,
+ target_state, DDI_HP_REQ_ASYNC);
+
break;
+ }
default:
cmn_err(CE_NOTE, "Unknown Notify() event %d on slot #%d\n",
- val, ctrl_p->slot.slotNum);
+ val, slot_p->hs_phy_slot_num);
break;
}
- mutex_exit(&ctrl_p->pciehpc_mutex);
+ mutex_exit(&ctrl_p->hc_mutex);
}
static void
-pciehpc_acpi_uninstall_event_handler(pciehpc_t *ctrl_p)
+pciehpc_acpi_uninstall_event_handler(pcie_hp_ctrl_t *ctrl_p)
{
- pciehpc_acpi_t *acpi_p = ctrl_p->misc_data;
+ pciehpc_acpi_t *acpi_p = ctrl_p->hc_misc_data;
+ pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0];
- PCIEHPC_DEBUG((CE_CONT, "Uninstall event handler for slot #%d\n",
- ctrl_p->slot.slotNum));
+ PCIE_DBG("Uninstall event handler for slot #%d\n",
+ slot_p->hs_phy_slot_num);
(void) AcpiRemoveNotifyHandler(acpi_p->slot_dev_obj,
ACPI_SYSTEM_NOTIFY, pciehpc_acpi_notify_handler);
(void) AcpiRemoveNotifyHandler(acpi_p->bus_obj,
@@ -557,14 +569,14 @@ pciehpc_acpi_uninstall_event_handler(pciehpc_t *ctrl_p)
* Run _PS0 method to turn on power to the slot.
*/
static ACPI_STATUS
-pciehpc_acpi_power_on_slot(pciehpc_t *ctrl_p)
+pciehpc_acpi_power_on_slot(pcie_hp_ctrl_t *ctrl_p)
{
int status = AE_OK;
- pciehpc_acpi_t *acpi_p = ctrl_p->misc_data;
+ pciehpc_acpi_t *acpi_p = ctrl_p->hc_misc_data;
int dev_state = 0;
+ pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0];
- PCIEHPC_DEBUG((CE_CONT, "turn ON power to the slot #%d\n",
- ctrl_p->slot.slotNum));
+ PCIE_DBG("turn ON power to the slot #%d\n", slot_p->hs_phy_slot_num);
status = AcpiEvaluateObject(acpi_p->slot_dev_obj, "_PS0", NULL, NULL);
@@ -572,18 +584,19 @@ pciehpc_acpi_power_on_slot(pciehpc_t *ctrl_p)
if (status == AE_OK) {
if (pciehpc_acpi_get_dev_state(acpi_p->slot_dev_obj,
&dev_state) != AE_OK)
- cmn_err(CE_WARN, "failed to get device status on slot #%d",
- ctrl_p->slot.slotNum);
+ cmn_err(CE_WARN, "failed to get device status "
+ "on slot #%d", slot_p->hs_phy_slot_num);
}
- PCIEHPC_DEBUG((CE_CONT, "(3)device state on slot #%d: 0x%x\n",
- ctrl_p->slot.slotNum, dev_state));
- pciehpc_get_slot_state(ctrl_p);
+ PCIE_DBG("(3)device state on slot #%d: 0x%x\n",
+ slot_p->hs_phy_slot_num, dev_state);
+
+ pciehpc_get_slot_state(slot_p);
- if (ctrl_p->slot.slot_state != HPC_SLOT_CONNECTED) {
+ if (slot_p->hs_info.cn_state < DDI_HP_CN_STATE_POWERED) {
cmn_err(CE_WARN, "failed to power on the slot #%d"
"(dev_state 0x%x, ACPI_STATUS 0x%x)",
- ctrl_p->slot.slotNum, dev_state, status);
+ slot_p->hs_phy_slot_num, dev_state, status);
return (AE_ERROR);
}
@@ -594,14 +607,14 @@ pciehpc_acpi_power_on_slot(pciehpc_t *ctrl_p)
* Run _EJ0 method to turn off power to the slot.
*/
static ACPI_STATUS
-pciehpc_acpi_power_off_slot(pciehpc_t *ctrl_p)
+pciehpc_acpi_power_off_slot(pcie_hp_ctrl_t *ctrl_p)
{
int status = AE_OK;
- pciehpc_acpi_t *acpi_p = ctrl_p->misc_data;
+ pciehpc_acpi_t *acpi_p = ctrl_p->hc_misc_data;
int dev_state = 0;
+ pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0];
- PCIEHPC_DEBUG((CE_CONT, "turn OFF power to the slot #%d\n",
- ctrl_p->slot.slotNum));
+ PCIE_DBG("turn OFF power to the slot #%d\n", slot_p->hs_phy_slot_num);
status = AcpiEvaluateObject(acpi_p->slot_dev_obj, "_EJ0", NULL, NULL);
@@ -609,18 +622,19 @@ pciehpc_acpi_power_off_slot(pciehpc_t *ctrl_p)
if (status == AE_OK) {
if (pciehpc_acpi_get_dev_state(acpi_p->slot_dev_obj,
&dev_state) != AE_OK)
- cmn_err(CE_WARN, "failed to get device status on slot #%d",
- ctrl_p->slot.slotNum);
+ cmn_err(CE_WARN, "failed to get device status "
+ "on slot #%d", slot_p->hs_phy_slot_num);
}
- PCIEHPC_DEBUG((CE_CONT, "(2)device state on slot #%d: 0x%x\n",
- ctrl_p->slot.slotNum, dev_state));
- pciehpc_get_slot_state(ctrl_p);
+ PCIE_DBG("(2)device state on slot #%d: 0x%x\n",
+ slot_p->hs_phy_slot_num, dev_state);
- if (ctrl_p->slot.slot_state == HPC_SLOT_CONNECTED) {
+ pciehpc_get_slot_state(slot_p);
+
+ if (slot_p->hs_info.cn_state >= DDI_HP_CN_STATE_POWERED) {
cmn_err(CE_WARN, "failed to power OFF the slot #%d"
"(dev_state 0x%x, ACPI_STATUS 0x%x)",
- ctrl_p->slot.slotNum, dev_state, status);
+ slot_p->hs_phy_slot_num, dev_state, status);
return (AE_ERROR);
}
@@ -628,27 +642,6 @@ pciehpc_acpi_power_off_slot(pciehpc_t *ctrl_p)
}
/*
- * Check if the child device node of the bus object has _EJ0 method
- * present.
- */
-static ACPI_STATUS
-pciehpc_acpi_ej0_present(ACPI_HANDLE pcibus_obj)
-{
- int status = AE_OK;
- ACPI_HANDLE d0f0_obj;
- ACPI_HANDLE ej0_hdl;
-
- if ((status = AcpiGetNextObject(ACPI_TYPE_DEVICE, pcibus_obj,
- NULL, &d0f0_obj)) == AE_OK) {
- /* child device node(s) are present; check for _EJ0 method */
- status = AcpiGetHandle(d0f0_obj, "_EJ0", &ej0_hdl);
- }
-
- return (status);
-}
-
-
-/*
* Get the status info (as returned by _STA method) for the device.
*/
static ACPI_STATUS
diff --git a/usr/src/uts/intel/io/pciex/pcieb_x86.c b/usr/src/uts/intel/io/pciex/pcieb_x86.c
index 4ac78e6600..2db930bff2 100644
--- a/usr/src/uts/intel/io/pciex/pcieb_x86.c
+++ b/usr/src/uts/intel/io/pciex/pcieb_x86.c
@@ -667,15 +667,3 @@ pcieb_plat_ctlops(dev_info_t *rdip, ddi_ctl_enum_t ctlop, void *arg)
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/pci_autoconfig/Makefile b/usr/src/uts/intel/pci_autoconfig/Makefile
index d858a8bbfc..7d2c5e1756 100644
--- a/usr/src/uts/intel/pci_autoconfig/Makefile
+++ b/usr/src/uts/intel/pci_autoconfig/Makefile
@@ -20,11 +20,9 @@
#
#
# uts/intel/pci_autoconfig/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 PCI autoconfiguration
# kernel module.
#
@@ -40,10 +38,8 @@ UTSBASE = ../..
# Define the module and object file sets.
#
MODULE = pci_autoconfig
-OBJECTS = $(PCI_AUTOCONFIG_OBJS:%=$(OBJS_DIR)/%) \
- $(PCI_STRING_OBJS:%=$(OBJS_DIR)/%)
-LINTS = $(PCI_AUTOCONFIG_OBJS:%.o=$(LINTS_DIR)/%.ln) \
- $(PCI_STRING_OBJS:%.o=$(LINTS_DIR)/%.ln)
+OBJECTS = $(PCI_AUTOCONFIG_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(PCI_AUTOCONFIG_OBJS:%.o=$(LINTS_DIR)/%.ln)
ROOTMODULE = $(ROOT_MISC_DIR)/$(MODULE)
INC_PATH += -I$(UTSBASE)/i86pc
@@ -62,7 +58,7 @@ INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
#
# Depends on acpica ACPI CA interpreter
#
-LDFLAGS += -dy -N misc/acpica
+LDFLAGS += -dy -Nmisc/acpica -Nmisc/pcie
#
# For now, disable these lint checks; maintainers should endeavor
diff --git a/usr/src/uts/intel/pci_pci/Makefile b/usr/src/uts/intel/pci_pci/Makefile
index e8f47ecbc1..684162c90f 100644
--- a/usr/src/uts/intel/pci_pci/Makefile
+++ b/usr/src/uts/intel/pci_pci/Makefile
@@ -21,11 +21,9 @@
#
# uts/intel/pci_pci/Makefile
#
-# 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"
-#
# This makefile drives the production of the pci_pci driver kernel module.
#
@@ -57,9 +55,9 @@ LINT_TARGET = $(MODULE).lint
INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
#
-# depends on misc/pcihp
+# depends on misc/pcie and misc/pcihp
#
-LDFLAGS += -dy -Nmisc/pcihp -Nmisc/pcie
+LDFLAGS += -dy -Nmisc/pcie -Nmisc/pcihp
#
# Override defaults to build a unique, local modstubs.o.
diff --git a/usr/src/uts/intel/pcicfg/Makefile b/usr/src/uts/intel/pcicfg/Makefile
index f2970f8ac3..07a02666ae 100644
--- a/usr/src/uts/intel/pcicfg/Makefile
+++ b/usr/src/uts/intel/pcicfg/Makefile
@@ -20,11 +20,9 @@
#
#
# uts/intel/pcicfg/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 kernel/misc/pcicfg module
# (PCI configurator module) for PCI hotplug support in PCI nexus
# drivers.
@@ -67,7 +65,7 @@ CLEANFILES += $(MODSTUBS_O)
#
# Dependency
#
-LDFLAGS += -dy -Nmisc/busra
+LDFLAGS += -dy -Nmisc/busra -Nmisc/pcie
#
# For now, disable these lint checks; maintainers should endeavor
diff --git a/usr/src/uts/intel/pcieb/Makefile b/usr/src/uts/intel/pcieb/Makefile
index 420476a8cb..9e8c2d681e 100644
--- a/usr/src/uts/intel/pcieb/Makefile
+++ b/usr/src/uts/intel/pcieb/Makefile
@@ -55,14 +55,9 @@ LINT_TARGET = $(MODULE).lint
INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
#
-# depends on misc/pcihp and misc/pcie
+# depends on misc/pcie
#
-# For PCI Hotplug support, the misc/pcihp module provides devctl control
-# device and cb_ops functions to support hotplug operations.
-#
-# pcie supplies PCI Express fabric error support
-#
-LDFLAGS += -dy -Nmisc/pcihp -Nmisc/pcie
+LDFLAGS += -dy -Nmisc/pcie
#
# Override defaults to build a unique, local modstubs.o.
diff --git a/usr/src/uts/intel/pciehpc/Makefile b/usr/src/uts/intel/pciehpc/Makefile
deleted file mode 100644
index c457d0cfd1..0000000000
--- a/usr/src/uts/intel/pciehpc/Makefile
+++ /dev/null
@@ -1,104 +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 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 kernel/misc/pciehpc module
-# for PCI-E hotplug controller support in PCI-E nexus drivers.
-#
-# intel architecture dependent
-#
-
-#
-# Path to the base of the uts directory tree (usually /usr/src/uts).
-#
-UTSBASE = ../..
-
-#
-# Define the module and object file sets.
-#
-MODULE = pciehpc
-OBJECTS = $(PCIEHPCNEXUS_OBJS:%=$(OBJS_DIR)/%)
-LINTS = $(PCIEHPCNEXUS_OBJS:%.o=$(LINTS_DIR)/%.ln)
-ROOTMODULE = $(ROOT_MISC_DIR)/$(MODULE)
-
-#
-# Include common rules.
-#
-include $(UTSBASE)/intel/Makefile.intel
-
-#
-# Define targets
-#
-ALL_TARGET = $(BINARY)
-LINT_TARGET = $(MODULE).lint
-INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
-
-#
-# depends on misc/hpcsvc, misc/acpica and misc/pcie
-#
-# For Hotplug support, the misc/hpcsvc module provides various hooks
-# to support hotplug operations.
-#
-# acpica supplies ACPI access routines
-#
-# pcie supplies PCI Express fabric error support
-#
-
-#
-LDFLAGS += -dy -Nmisc/hpcsvc -Nmisc/acpica -Nmisc/pcie
-
-#
-# 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)/intel/Makefile.targ
diff --git a/usr/src/uts/intel/io/hotplug/pciehpc/pciehpc_acpi.h b/usr/src/uts/intel/sys/hotplug/pci/pciehpc_acpi.h
index ddb173278f..b35938f516 100644
--- a/usr/src/uts/intel/io/hotplug/pciehpc/pciehpc_acpi.h
+++ b/usr/src/uts/intel/sys/hotplug/pci/pciehpc_acpi.h
@@ -33,7 +33,8 @@ extern "C" {
#include <sys/acpi/acpi.h>
#include <sys/acpica.h>
-#include <sys/hotplug/pci/pciehpc_impl.h>
+#include <sys/hotplug/pci/pcie_hp.h>
+#include <sys/hotplug/pci/pciehpc.h>
/* soft state data structure for ACPI hot plug mode */
typedef struct pciehpc_acpi {
diff --git a/usr/src/uts/sparc/Makefile.files b/usr/src/uts/sparc/Makefile.files
index 8d3a7aa9a0..86ab983b7a 100644
--- a/usr/src/uts/sparc/Makefile.files
+++ b/usr/src/uts/sparc/Makefile.files
@@ -90,7 +90,7 @@ KRTLD_OBJS += \
kobj_reloc.o
SWAPGENERIC_OBJS += swapgeneric.o
-PCICFG_E_OBJS += pcicfg.e.o
+PCICFG_OBJS += pcicfg.o
FCPCI_OBJS += fcpci.o
FCODEM_OBJS += fc_ddi.o fc_physio.o fc_ops.o fc_subr.o
diff --git a/usr/src/uts/sparc/Makefile.sparc.shared b/usr/src/uts/sparc/Makefile.sparc.shared
index 19e2094dfb..27e995a8b8 100644
--- a/usr/src/uts/sparc/Makefile.sparc.shared
+++ b/usr/src/uts/sparc/Makefile.sparc.shared
@@ -399,7 +399,7 @@ MISC_KMODS += kmech_krb5
MISC_KMODS += fssnap_if
MISC_KMODS += hidparser kbtrans usba usba10 usbs49_fw
MISC_KMODS += s1394
-MISC_KMODS += hpcsvc pcihp pciehpc pcishpc
+MISC_KMODS += hpcsvc pcihp
MISC_KMODS += rsmops
MISC_KMODS += kcf
MISC_KMODS += ksocket
@@ -416,7 +416,7 @@ MISC_KMODS += ctf
MISC_KMODS += mac dls
MISC_KMODS += cmlb
MISC_KMODS += tem
-MISC_KMODS += pcicfg.e fcodem fcpci
+MISC_KMODS += pcicfg fcodem fcpci
MISC_KMODS += scsi_vhci_f_sym scsi_vhci_f_tpgs scsi_vhci_f_asym_sun
MISC_KMODS += scsi_vhci_f_sym_hds
MISC_KMODS += scsi_vhci_f_tape scsi_vhci_f_tpgs_tape
diff --git a/usr/src/uts/sparc/io/pciex/pcieb_sparc.c b/usr/src/uts/sparc/io/pciex/pcieb_sparc.c
index ad0b66aebc..9b61e05830 100644
--- a/usr/src/uts/sparc/io/pciex/pcieb_sparc.c
+++ b/usr/src/uts/sparc/io/pciex/pcieb_sparc.c
@@ -95,8 +95,10 @@ pcieb_plat_intr_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op,
intr = hdlp->ih_vector;
+ d = (pcie_ari_is_enabled(dip) == PCIE_ARI_FORW_ENABLED) ? 0 :
+ PCI_REG_DEV_G(pci_rp[0].pci_phys_hi);
+
/* 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
@@ -156,12 +158,6 @@ 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)
{
@@ -201,11 +197,17 @@ pcieb_attach_plx_workarounds(pcieb_devstate_t *pcieb)
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;
+ uint16_t device_id = bus_p->bus_dev_ven_id >> 16;
int ce_mask = 0;
if (!IS_PLX_VENDORID(vendor_id))
return;
+ if ((device_id == PXB_DEVICE_PLX_8532) &&
+ (bus_p->bus_rev_id <= PXB_DEVICE_PLX_AA_REV))
+ /* Clear hotplug capability */
+ bus_p->bus_hp_sup_modes = PCIE_NONE_HP_MODE;
+
/*
* 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
diff --git a/usr/src/uts/sparc/ml/modstubs.s b/usr/src/uts/sparc/ml/modstubs.s
index bb988adaa9..18eba0bdfa 100644
--- a/usr/src/uts/sparc/ml/modstubs.s
+++ b/usr/src/uts/sparc/ml/modstubs.s
@@ -1028,35 +1028,13 @@ stubs_base:
#endif
/*
- * Stubs for PCI configurator module (misc/pcicfg.e).
+ * Stubs for PCI configurator module (misc/pcicfg).
*/
-#ifndef PCICFG_E_MODULE
- MODULE(pcicfg.e,misc);
- STUB(pcicfg.e, pcicfg_configure, 0);
- STUB(pcicfg.e, pcicfg_unconfigure, 0);
- END_MODULE(pcicfg.e);
-#endif
-
-/*
- * Stubs for PCIEHPC (pci-ex hot plug support) module (misc/pciehpc).
- */
-#ifndef PCIEHPC_MODULE
- MODULE(pciehpc,misc);
- STUB(pciehpc, pciehpc_init, 0);
- STUB(pciehpc, pciehpc_uninit, 0);
- WSTUB(pciehpc, pciehpc_intr, 0);
- END_MODULE(pciehpc);
-#endif
-
-/*
- * Stubs for PCISHPC (pci/pci-x shpc hot plug support) module (misc/pcishpc).
- */
-#ifndef PCISHPC_MODULE
- MODULE(pcishpc,misc);
- STUB(pcishpc, pcishpc_init, 0);
- STUB(pcishpc, pcishpc_uninit, 0);
- WSTUB(pcishpc, pcishpc_intr, 0);
- END_MODULE(pcishpc);
+#ifndef PCICFG_MODULE
+ MODULE(pcicfg,misc);
+ STUB(pcicfg, pcicfg_configure, 0);
+ STUB(pcicfg, pcicfg_unconfigure, 0);
+ END_MODULE(pcicfg);
#endif
#ifndef PCIHP_MODULE
diff --git a/usr/src/uts/sparc/pci_pci/Makefile b/usr/src/uts/sparc/pci_pci/Makefile
index 71cd4a422d..37b8c63006 100644
--- a/usr/src/uts/sparc/pci_pci/Makefile
+++ b/usr/src/uts/sparc/pci_pci/Makefile
@@ -19,11 +19,9 @@
# CDDL HEADER END
#
# uts/sparc/pci_pci/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 pci driver kernel module
#
# sparc implementation architecture dependent
@@ -83,7 +81,7 @@ LINTTAGS += -erroff=E_SUSPICIOUS_COMPARISON
#
# Dependency
#
-LDFLAGS += -dy -Nmisc/pcie
+LDFLAGS += -dy -Nmisc/pcie -Nmisc/pcihp
#
# Default build targets.
diff --git a/usr/src/uts/sparc/pcicfg.e/Makefile b/usr/src/uts/sparc/pcicfg/Makefile
index 655da5898b..daa77d4c7e 100644
--- a/usr/src/uts/sparc/pcicfg.e/Makefile
+++ b/usr/src/uts/sparc/pcicfg/Makefile
@@ -19,12 +19,10 @@
# CDDL HEADER END
#
#
-# uts/sparc/pcicfg.e/Makefile
-# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# uts/sparc/pcicfg/Makefile
+# 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 EFCode Enabled
# PCI Configurator.
#
@@ -39,9 +37,9 @@ UTSBASE = ../..
#
# Define the module and object file sets.
#
-MODULE = pcicfg.e
-OBJECTS = $(PCICFG_E_OBJS:%=$(OBJS_DIR)/%)
-LINTS = $(PCICFG_E_OBJS:%.o=$(LINTS_DIR)/%.ln)
+MODULE = pcicfg
+OBJECTS = $(PCICFG_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(PCICFG_OBJS:%.o=$(LINTS_DIR)/%.ln)
ROOTMODULE = $(ROOT_MISC_DIR)/$(MODULE)
#
diff --git a/usr/src/uts/sparc/pcie/Makefile b/usr/src/uts/sparc/pcie/Makefile
index 6d7a4ede77..f65b5491e5 100644
--- a/usr/src/uts/sparc/pcie/Makefile
+++ b/usr/src/uts/sparc/pcie/Makefile
@@ -23,7 +23,6 @@
# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
-#
# This makefile drives the production of the PCIE driver kernel module.
#
# sparc architecture dependent
@@ -38,8 +37,10 @@ UTSBASE = ../..
# Define the module and object file sets.
#
MODULE = pcie
-OBJECTS = $(PCIE_MISC_OBJS:%=$(OBJS_DIR)/%)
-LINTS = $(PCIE_MISC_OBJS:%.o=$(LINTS_DIR)/%.ln)
+OBJECTS = $(PCIE_MISC_OBJS:%=$(OBJS_DIR)/%) \
+ $(PCI_STRING_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(PCIE_MISC_OBJS:%.o=$(LINTS_DIR)/%.ln) \
+ $(PCI_STRING_OBJS:%.o=$(LINTS_DIR)/%.ln)
ROOTMODULE = $(ROOT_MISC_DIR)/$(MODULE)
#
@@ -60,6 +61,11 @@ INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
CFLAGS += $(CCVERBOSE)
#
+# Dependency
+#
+LDFLAGS += -dy -Nmisc/busra
+
+#
# Overrides
#
MODSTUBS_DIR = $(OBJS_DIR)
diff --git a/usr/src/uts/sparc/pcieb/Makefile b/usr/src/uts/sparc/pcieb/Makefile
index f6690fec79..7e01f6386d 100644
--- a/usr/src/uts/sparc/pcieb/Makefile
+++ b/usr/src/uts/sparc/pcieb/Makefile
@@ -74,7 +74,7 @@ CPPFLAGS += -DPX_PLX
#
# Dependency
#
-LDFLAGS += -dy -Nmisc/pcie -Nmisc/pcishpc -Nmisc/pcihp -Nmisc/pciehpc
+LDFLAGS += -dy -Nmisc/pcie
#
# For now, disable these lint checks; maintainers should endeavor
diff --git a/usr/src/uts/sparc/pcieb_bcm/Makefile b/usr/src/uts/sparc/pcieb_bcm/Makefile
index a6eee4a8ce..a9a3f0927d 100644
--- a/usr/src/uts/sparc/pcieb_bcm/Makefile
+++ b/usr/src/uts/sparc/pcieb_bcm/Makefile
@@ -69,12 +69,12 @@ CFLAGS += -dalign
# Enable Broadcom 5714/5715 workaround code and lint duplicate symbol
# avoidance hack
#
-CPPFLAGS += -DBCM_SW_WORKAROUNDS -DPX_MOD_NAME=pcieb_bcm
+CPPFLAGS += -DPCIEB_BCM -DPX_MOD_NAME=pcieb_bcm
#
# Dependency
#
-LDFLAGS += -dy -Nmisc/pcie -Nmisc/pcishpc -Nmisc/pcihp -Nmisc/pciehpc
+LDFLAGS += -dy -Nmisc/pcie
#
# For now, disable these lint checks; maintainers should endeavor
diff --git a/usr/src/uts/sparc/pciehpc/Makefile b/usr/src/uts/sparc/pciehpc/Makefile
deleted file mode 100644
index bfc8dda284..0000000000
--- a/usr/src/uts/sparc/pciehpc/Makefile
+++ /dev/null
@@ -1,93 +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/pciehpc/Makefile
-# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
-# Use is subject to license terms.
-#
-#ident "%Z%%M% %I% %E% SMI"
-#
-# This makefile drives the production of the kernel/misc/pciehpc module
-# (PCIe hotplug controller module) for PCIe hotplug support in PCIe nexus
-# drivers.
-#
-
-#
-# Path to the base of the uts directory tree (usually /usr/src/uts).
-#
-UTSBASE = ../..
-
-#
-# Define the module and object file sets.
-#
-MODULE = pciehpc
-OBJECTS = $(PCIEHPCNEXUS_OBJS:%=$(OBJS_DIR)/%)
-LINTS = $(PCIEHPCNEXUS_OBJS:%.o=$(LINTS_DIR)/%.ln)
-ROOTMODULE = $(ROOT_MISC_DIR)/$(MODULE)
-
-#
-# Include common rules.
-
-include $(UTSBASE)/sparc/Makefile.sparc
-
-#
-# Define targets
-#
-ALL_TARGET = $(BINARY)
-LINT_TARGET = $(MODULE).lint
-INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
-
-#
-# Dependency
-LDFLAGS += -dy -Nmisc/hpcsvc -Nmisc/pcie
-
-#
-# 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
-
-#
-# Default build targets.
-#
-.KEEP_STATE:
-
-def: $(DEF_DEPS)
-
-all: $(ALL_DEPS)
-
-clean: $(CLEAN_DEPS)
-
-clobber: $(CLOBBER_DEPS)
-
-lint: $(LINT_DEPS)
-
-modlintlib: $(MODLINTLIB_DEPS)
-
-clean.lint: $(CLEAN_LINT_DEPS)
-
-install: $(INSTALL_DEPS)
-
-#
-# Include common targets.
-#
-include $(UTSBASE)/sparc/Makefile.targ
diff --git a/usr/src/uts/sparc/pcihp/Makefile b/usr/src/uts/sparc/pcihp/Makefile
index 282a427190..2ab823fe58 100644
--- a/usr/src/uts/sparc/pcihp/Makefile
+++ b/usr/src/uts/sparc/pcihp/Makefile
@@ -20,11 +20,9 @@
#
#
# uts/sparc/pcihp/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 kernel/misc/pcihp module
# for PCI hotplug support in PCI nexus drivers.
#
@@ -70,7 +68,7 @@ CLEANFILES += $(MODSTUBS_O)
#
# Dependency
-LDFLAGS += -dy -Nmisc/busra -Nmisc/hpcsvc -Nmisc/pcicfg.e
+LDFLAGS += -dy -Nmisc/busra -Nmisc/hpcsvc -Nmisc/pcicfg
#
# For now, disable these lint checks; maintainers should endeavor
diff --git a/usr/src/uts/sparc/pcishpc/Makefile b/usr/src/uts/sparc/pcishpc/Makefile
deleted file mode 100644
index 9964444587..0000000000
--- a/usr/src/uts/sparc/pcishpc/Makefile
+++ /dev/null
@@ -1,94 +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/pcishpc/Makefile
-# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
-# Use is subject to license terms.
-#
-#ident "%Z%%M% %I% %E% SMI"
-#
-# This makefile drives the production of the kernel/misc/pcishpc module
-# (PCI SHPC hotplug controller module) for PCI hotplug support in PCI
-# nexus drivers.
-#
-
-#
-# Path to the base of the uts directory tree (usually /usr/src/uts).
-#
-UTSBASE = ../..
-
-#
-# Define the module and object file sets.
-#
-MODULE = pcishpc
-OBJECTS = $(PCISHPC_OBJS:%=$(OBJS_DIR)/%)
-LINTS = $(PCISHPC_OBJS:%.o=$(LINTS_DIR)/%.ln)
-ROOTMODULE = $(ROOT_MISC_DIR)/$(MODULE)
-
-#
-# Include common rules.
-
-include $(UTSBASE)/sparc/Makefile.sparc
-
-#
-# Define targets
-#
-ALL_TARGET = $(BINARY)
-LINT_TARGET = $(MODULE).lint
-INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
-
-#
-# Dependency
-LDFLAGS += -dy -Nmisc/hpcsvc
-
-#
-# 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/pcicfg.e.c b/usr/src/uts/sun4/io/pcicfg.c
index daa4a5936f..3ce92e0d27 100644
--- a/usr/src/uts/sun4/io/pcicfg.e.c
+++ b/usr/src/uts/sun4/io/pcicfg.c
@@ -37,6 +37,7 @@
#include <sys/hwconf.h>
#include <sys/ddi_impldefs.h>
#include <sys/fcode.h>
+#include <sys/pci.h>
#include <sys/pcie.h>
#include <sys/pcie_impl.h>
#include <sys/ddi.h>
@@ -44,7 +45,6 @@
#include <sys/sunndi.h>
#include <sys/pci_cap.h>
#include <sys/hotplug/pci/pcicfg.h>
-#include <sys/hotplug/pci/pcishpc_regs.h>
#include <sys/ndi_impldefs.h>
#define PCICFG_DEVICE_TYPE_PCI 1
@@ -53,7 +53,7 @@
#define EFCODE21554 /* changes for supporting 21554 */
static int pcicfg_alloc_resource(dev_info_t *, pci_regspec_t);
-static int pcicfg_free_resource(dev_info_t *, pci_regspec_t);
+static int pcicfg_free_resource(dev_info_t *, pci_regspec_t, pcicfg_flags_t);
static int pcicfg_remove_assigned_prop(dev_info_t *, pci_regspec_t *);
#ifdef PCICFG_INTERPRET_FCODE
@@ -71,6 +71,7 @@ static int pcicfg_start_devno = 0; /* for Debug only */
#define PCICFG_MAX_DEVICE 32
#define PCICFG_MAX_FUNCTION 8
+#define PCICFG_MAX_ARI_FUNCTION 256
#define PCICFG_MAX_REGISTER 64
#define PCICFG_MAX_BUS_DEPTH 255
@@ -260,18 +261,19 @@ int pcicfg_dont_interpret = 1;
*/
static int pcicfg_add_config_reg(dev_info_t *,
- uint_t, uint_t, uint_t);
+ uint_t, uint_t, uint_t);
static int pcicfg_probe_children(dev_info_t *, uint_t, uint_t, uint_t,
- uint_t *);
+ uint_t *, pcicfg_flags_t);
#ifdef PCICFG_INTERPRET_FCODE
static int pcicfg_load_fcode(dev_info_t *, uint_t, uint_t, uint_t,
uint16_t, uint16_t, uchar_t **, int *, int, int);
#endif
-static int pcicfg_fcode_probe(dev_info_t *, uint_t, uint_t, uint_t, uint_t *);
+static int pcicfg_fcode_probe(dev_info_t *, uint_t, uint_t, uint_t,
+ uint_t *, pcicfg_flags_t);
static int pcicfg_probe_bridge(dev_info_t *, ddi_acc_handle_t, uint_t,
- uint_t *);
+ uint_t *);
static int pcicfg_free_all_resources(dev_info_t *);
static int pcicfg_alloc_new_resources(dev_info_t *);
static int pcicfg_match_dev(dev_info_t *, void *);
@@ -285,7 +287,8 @@ static int pcicfg_allocate_chunk(dev_info_t *);
static int pcicfg_program_ap(dev_info_t *);
static int pcicfg_device_assign(dev_info_t *);
static int pcicfg_bridge_assign(dev_info_t *, void *);
-static int pcicfg_free_resources(dev_info_t *);
+static int pcicfg_device_assign_readonly(dev_info_t *);
+static int pcicfg_free_resources(dev_info_t *, pcicfg_flags_t);
static void pcicfg_setup_bridge(pcicfg_phdl_t *, ddi_acc_handle_t,
dev_info_t *);
static void pcicfg_update_bridge(pcicfg_phdl_t *, ddi_acc_handle_t);
@@ -298,8 +301,8 @@ static void pcicfg_device_on(ddi_acc_handle_t);
static void pcicfg_device_off(ddi_acc_handle_t);
static int pcicfg_set_busnode_props(dev_info_t *, uint8_t, int, int);
static int pcicfg_free_bridge_resources(dev_info_t *);
-static int pcicfg_free_device_resources(dev_info_t *);
-static int pcicfg_teardown_device(dev_info_t *);
+static int pcicfg_free_device_resources(dev_info_t *, pcicfg_flags_t);
+static int pcicfg_teardown_device(dev_info_t *, pcicfg_flags_t);
static int pcicfg_config_setup(dev_info_t *, ddi_acc_handle_t *);
static void pcicfg_config_teardown(ddi_acc_handle_t *);
static void pcicfg_get_mem(pcicfg_phdl_t *, uint32_t, uint64_t *);
@@ -321,6 +324,11 @@ static int pcicfg_ntbridge_unconfigure_child(dev_info_t *, uint_t);
static void pcicfg_free_hole(hole_t *);
static uint64_t pcicfg_alloc_hole(hole_t *, uint64_t *, uint32_t);
static int pcicfg_update_available_prop(dev_info_t *, pci_regspec_t *);
+static int pcicfg_ari_configure(dev_info_t *);
+static int pcicfg_populate_reg_props(dev_info_t *, ddi_acc_handle_t);
+static int pcicfg_populate_props_from_bar(dev_info_t *, ddi_acc_handle_t);
+static int pcicfg_update_assigned_prop_value(dev_info_t *, uint32_t,
+ uint32_t, uint32_t, uint_t);
#ifdef DEBUG
static void pcicfg_dump_common_config(ddi_acc_handle_t config_handle);
@@ -568,10 +576,10 @@ pcicfg_get_nslots(dev_info_t *dip, ddi_acc_handle_t handle)
&cap_ptr)) == DDI_SUCCESS) {
uint32_t config;
- PCI_CAP_PUT8(handle, NULL, cap_ptr, SHPC_DWORD_SELECT_OFF,
- SHPC_SLOT_CONFIGURATION_REG);
+ PCI_CAP_PUT8(handle, NULL, cap_ptr, PCI_HP_DWORD_SELECT_OFF,
+ PCI_HP_SLOT_CONFIGURATION_REG);
config = PCI_CAP_GET32(handle, NULL, cap_ptr,
- SHPC_DWORD_DATA_OFF);
+ PCI_HP_DWORD_DATA_OFF);
num_slots = config & 0x1F;
} else if ((PCI_CAP_LOCATE(handle, PCI_CAP_ID_SLOT_ID, &cap_ptr))
== DDI_SUCCESS) {
@@ -699,16 +707,23 @@ pcicfg_pcie_device_type(dev_info_t *dip, ddi_acc_handle_t handle)
*/
int
-pcicfg_configure(dev_info_t *devi, uint_t device)
+pcicfg_configure(dev_info_t *devi, uint_t device, uint_t function,
+ pcicfg_flags_t flags)
{
uint_t bus;
int len;
int func;
+ int trans_device;
dev_info_t *new_device;
pcicfg_bus_range_t pci_bus_range;
int rv;
int circ;
uint_t highest_bus = 0;
+ int ari_mode = B_FALSE;
+ int max_function = PCICFG_MAX_FUNCTION;
+
+ if (flags == PCICFG_FLAG_ENABLE_ARI)
+ return (pcicfg_ari_configure(devi));
/*
* Start probing at the device specified in "device" on the
@@ -724,31 +739,39 @@ pcicfg_configure(dev_info_t *devi, uint_t device)
bus = pci_bus_range.lo; /* primary bus number of this bus node */
ndi_devi_enter(devi, &circ);
- for (func = 0; func < PCICFG_MAX_FUNCTION; func++) {
+ for (func = 0; func < max_function; ) {
+ if ((function != PCICFG_ALL_FUNC) && (function != func))
+ goto next;
+
+ if (ari_mode)
+ trans_device = func >> 3;
+ else
+ trans_device = device;
- DEBUG3("Configuring [0x%x][0x%x][0x%x]\n", bus, device, func);
+ DEBUG3("Configuring [0x%x][0x%x][0x%x]\n",
+ bus, trans_device, func & 7);
/*
* Try executing fcode if available.
*/
- switch (rv = pcicfg_fcode_probe(devi, bus, device, func,
- &highest_bus)) {
+ switch (rv = pcicfg_fcode_probe(devi, bus, trans_device,
+ func & 7, &highest_bus, flags)) {
case PCICFG_FAILURE:
DEBUG2("configure failed: "
"bus [0x%x] device [0x%x]\n",
- bus, device);
+ bus, trans_device);
break;
case PCICFG_NODEVICE:
DEBUG3("no device : bus "
"[0x%x] slot [0x%x] func [0x%x]\n",
- bus, device, func);
+ bus, trans_device, func & 7);
if (func)
- continue;
+ goto next;
break;
default:
DEBUG3("configure: bus => [%d] "
"slot => [%d] func => [%d]\n",
- bus, device, func);
+ bus, trans_device, func & 7);
break;
}
@@ -756,11 +779,54 @@ pcicfg_configure(dev_info_t *devi, uint_t device)
break;
if ((new_device = pcicfg_devi_find(devi,
- device, func)) == NULL) {
+ trans_device, (func & 7))) == NULL) {
DEBUG0("Did'nt find device node just created\n");
goto cleanup;
}
+
+next:
+ /*
+ * Determine if ARI Forwarding should be enabled.
+ */
+ if (func == 0) {
+ if ((pcie_ari_supported(devi)
+ == PCIE_ARI_FORW_SUPPORTED) &&
+ (pcie_ari_device(new_device) == PCIE_ARI_DEVICE)) {
+ if (pcie_ari_enable(devi) == DDI_SUCCESS) {
+ (void) ddi_prop_create(DDI_DEV_T_NONE,
+ devi, DDI_PROP_CANSLEEP,
+ "ari-enabled", NULL, 0);
+
+ ari_mode = B_TRUE;
+ max_function = PCICFG_MAX_ARI_FUNCTION;
+ }
+ }
+ }
+
+ if (ari_mode == B_TRUE) {
+ int next_function;
+
+ DEBUG0("Next Function - ARI Device\n");
+ if (pcie_ari_get_next_function(new_device,
+ &next_function) != DDI_SUCCESS)
+ goto cleanup;
+
+ /*
+ * Check if there are more fucntions to probe.
+ */
+ if (next_function == 0) {
+ DEBUG0("Next Function - "
+ "No more ARI Functions\n");
+ break;
+ }
+ func = next_function;
+ } else {
+ func++;
+ }
+
+ DEBUG1("Next Function - %x\n", func);
}
+
ndi_devi_exit(devi, circ);
if (func == 0)
@@ -774,16 +840,26 @@ cleanup:
* There are no resources allocated to the in the
* probe state.
*/
+ if (pcie_ari_is_enabled(devi) == PCIE_ARI_FORW_ENABLED)
+ max_function = PCICFG_MAX_ARI_FUNCTION;
+ else
+ max_function = PCICFG_MAX_FUNCTION;
+
+ for (func = 0; func < max_function; func++) {
+
+ if (max_function == PCICFG_MAX_ARI_FUNCTION)
+ trans_device = func >> 3; /* ARI Device */
+ else
+ trans_device = device;
- for (func = 0; func < PCICFG_MAX_FUNCTION; func++) {
if ((new_device = pcicfg_devi_find(devi,
- device, func)) == NULL) {
+ trans_device, (func & 0x7))) == NULL) {
DEBUG0("No more devices to clean up\n");
continue;
}
DEBUG2("Cleaning up device [0x%x] function [0x%x]\n",
- device, func);
+ trans_device, func & 7);
/*
* If this was a bridge device it will have a
* probe handle - if not, no harm in calling this.
@@ -940,7 +1016,8 @@ pcicfg_configure_ntbridge(dev_info_t *new_device, uint_t bus, uint_t device)
continue;
/* Lets fake attachments points for each child, */
- if (pcicfg_configure(new_device, devno) != PCICFG_SUCCESS) {
+ if (pcicfg_configure(new_device, devno, PCICFG_ALL_FUNC, 0)
+ != PCICFG_SUCCESS) {
int old_dev = pcicfg_start_devno;
cmn_err(CE_WARN,
@@ -1066,7 +1143,7 @@ pcicfg_ntbridge_allocate_resources(dev_info_t *dip)
phdl->mem_hole.len = mem_request->ra_len;
phdl->mem_hole.next = (hole_t *)NULL;
- DEBUG2("AP requested [0x%llx], needs [0x%llx] bytes of memory\n",
+ DEBUG2("Connector requested [0x%llx], needs [0x%llx] bytes of memory\n",
boundlen, mem_request->ra_len);
/* set up a memory resource map for NT bridge */
@@ -1105,7 +1182,7 @@ pcicfg_ntbridge_allocate_resources(dev_info_t *dip)
phdl->io_hole.len = io_request->ra_len;
phdl->io_hole.next = (hole_t *)NULL;
- DEBUG2("AP requested [0x%llx], needs [0x%llx] bytes of IO\n",
+ DEBUG2("Connector requested [0x%llx], needs [0x%llx] bytes of IO\n",
boundlen, io_request->ra_len);
DEBUG2("MEMORY BASE = [0x%x] length [0x%x]\n",
@@ -1273,7 +1350,7 @@ pcicfg_ntbridge_unconfigure_child(dev_info_t *new_device, uint_t devno)
if (vid == 0xffff)
return (PCICFG_NODEVICE);
- return (pcicfg_unconfigure(new_device, devno));
+ return (pcicfg_unconfigure(new_device, devno, PCICFG_ALL_FUNC, 0));
}
static int
@@ -1439,23 +1516,38 @@ pcicfg_get_ntbridge_child_range(dev_info_t *dip, uint64_t *boundbase,
/*
* This will turn resources allocated by pcicfg_configure()
- * and remove the device tree from the attachment point
+ * and remove the device tree from the Hotplug Connection (CN)
* and below. The routine assumes the devices have their
* drivers detached.
*/
int
-pcicfg_unconfigure(dev_info_t *devi, uint_t device)
+pcicfg_unconfigure(dev_info_t *devi, uint_t device, uint_t function,
+ pcicfg_flags_t flags)
{
dev_info_t *child_dip;
int func;
int i;
+ int max_function;
+ int trans_device;
+
+ if (pcie_ari_is_enabled(devi) == PCIE_ARI_FORW_ENABLED)
+ max_function = PCICFG_MAX_ARI_FUNCTION;
+ else
+ max_function = PCICFG_MAX_FUNCTION;
/*
* Cycle through devices to make sure none are busy.
* If a single device is busy fail the whole unconfigure.
*/
- for (func = 0; func < PCICFG_MAX_FUNCTION; func++) {
- if ((child_dip = pcicfg_devi_find(devi, device, func)) == NULL)
+ for (func = 0; func < max_function; func++) {
+
+ if (max_function == PCICFG_MAX_ARI_FUNCTION)
+ trans_device = func >> 3; /* ARI Device */
+ else
+ trans_device = device;
+
+ if ((child_dip = pcicfg_devi_find(devi, trans_device,
+ (func & 0x7))) == NULL)
continue;
if (ndi_devi_offline(child_dip, NDI_UNCONFIG) == NDI_SUCCESS)
@@ -1466,8 +1558,20 @@ pcicfg_unconfigure(dev_info_t *devi, uint_t device)
* offline during the process.
*/
DEBUG2("Device [0x%x] function [%x] is busy\n", device, func);
+ /*
+ * If we are only asked to offline one specific function,
+ * and that fails, we just simply return.
+ */
+ if (function != PCICFG_ALL_FUNC)
+ return (PCICFG_FAILURE);
+
for (i = 0; i < func; i++) {
- if ((child_dip = pcicfg_devi_find(devi, device, i))
+
+ if (max_function == PCICFG_MAX_ARI_FUNCTION)
+ trans_device = i >> 3;
+
+ if ((child_dip =
+ pcicfg_devi_find(devi, trans_device, (i & 7)))
== NULL) {
DEBUG0(
"No more devices to put back on line!!\n");
@@ -1486,17 +1590,23 @@ pcicfg_unconfigure(dev_info_t *devi, uint_t device)
}
/*
- * Now, tear down all devinfo nodes for this AP.
+ * Now, tear down all devinfo nodes for this Connector.
*/
- for (func = 0; func < PCICFG_MAX_FUNCTION; func++) {
+ for (func = 0; func < max_function; func++) {
+
+ if (max_function == PCICFG_MAX_ARI_FUNCTION)
+ trans_device = func >> 3; /* ARI Device */
+ else
+ trans_device = device;
+
if ((child_dip = pcicfg_devi_find(devi,
- device, func)) == NULL) {
+ trans_device, (func & 7))) == NULL) {
DEBUG0("No more devices to tear down!\n");
continue;
}
DEBUG2("Tearing down device [0x%x] function [0x%x]\n",
- device, func);
+ trans_device, (func & 7));
if (pcicfg_is_ntbridge(child_dip) != DDI_FAILURE)
if (pcicfg_ntbridge_unconfigure(child_dip) !=
@@ -1506,27 +1616,32 @@ pcicfg_unconfigure(dev_info_t *devi, uint_t device)
return (PCICFG_FAILURE);
}
- if (pcicfg_teardown_device(child_dip) != PCICFG_SUCCESS) {
+ if (pcicfg_teardown_device(child_dip, flags)
+ != PCICFG_SUCCESS) {
DEBUG2("Failed to tear down device [0x%x]"
"function [0x%x]\n",
- device, func);
+ trans_device, func & 7);
return (PCICFG_FAILURE);
}
}
+ if (pcie_ari_is_enabled(devi) == PCIE_ARI_FORW_ENABLED) {
+ (void) ddi_prop_remove(DDI_DEV_T_NONE, devi, "ari-enabled");
+ (void) pcie_ari_disable(devi);
+ }
+
return (PCICFG_SUCCESS);
}
static int
-pcicfg_teardown_device(dev_info_t *dip)
+pcicfg_teardown_device(dev_info_t *dip, pcicfg_flags_t flags)
{
ddi_acc_handle_t config_handle;
/*
* Free up resources associated with 'dip'
*/
-
- if (pcicfg_free_resources(dip) != PCICFG_SUCCESS) {
+ if (pcicfg_free_resources(dip, flags) != PCICFG_SUCCESS) {
DEBUG0("Failed to free resources\n");
return (PCICFG_FAILURE);
}
@@ -1905,6 +2020,7 @@ pcicfg_bridge_assign(dev_info_t *dip, void *hdl)
return (DDI_WALK_CONTINUE);
}
+
static int
pcicfg_device_assign(dev_info_t *dip)
{
@@ -1918,7 +2034,6 @@ pcicfg_device_assign(dev_info_t *dip)
uint64_t answer;
uint64_t alen;
-
DEBUG1("%llx now under configuration\n", dip);
/*
@@ -2085,9 +2200,131 @@ pcicfg_device_assign(dev_info_t *dip)
return (PCICFG_SUCCESS);
}
+static int
+pcicfg_device_assign_readonly(dev_info_t *dip)
+{
+ ddi_acc_handle_t handle;
+ pci_regspec_t *assigned;
+ int length;
+ int acount;
+ int i;
+ ndi_ra_request_t request;
+ uint64_t answer;
+ uint64_t alen;
+
+
+ DEBUG1("%llx now under configuration\n", dip);
+
+ if (ddi_getlongprop(DDI_DEV_T_ANY, dip,
+ DDI_PROP_DONTPASS, "assigned-addresses", (caddr_t)&assigned,
+ &length) != DDI_PROP_SUCCESS) {
+ DEBUG0("Failed to read assigned-addresses property\n");
+ return (PCICFG_FAILURE);
+ }
+
+ if (pcicfg_config_setup(dip, &handle) != DDI_SUCCESS) {
+ DEBUG0("Failed to map config space!\n");
+ /*
+ * Don't forget to free up memory from ddi_getlongprop
+ */
+ kmem_free((caddr_t)assigned, length);
+
+ return (PCICFG_FAILURE);
+ }
+
+ /*
+ * For each "assigned-addresses" property entry with a length,
+ * call the memory allocation routines to return the
+ * resource.
+ */
+ /*
+ * If there is an interrupt pin set program
+ * interrupt line with default values.
+ */
+ if (pci_config_get8(handle, PCI_CONF_IPIN)) {
+ pci_config_put8(handle, PCI_CONF_ILINE, 0xf);
+ }
+
+ bzero((caddr_t)&request, sizeof (ndi_ra_request_t));
+
+ request.ra_flags = NDI_RA_ALLOC_SPECIFIED; /* specified addr */
+ request.ra_boundbase = 0;
+ request.ra_boundlen = PCICFG_4GIG_LIMIT;
+
+ acount = length / sizeof (pci_regspec_t);
+ for (i = 0; i < acount; i++) {
+ if ((assigned[i].pci_size_low != 0)||
+ (assigned[i].pci_size_hi != 0)) {
+
+ request.ra_len = assigned[i].pci_size_low;
+
+ switch (PCI_REG_ADDR_G(assigned[i].pci_phys_hi)) {
+ case PCI_REG_ADDR_G(PCI_ADDR_MEM64):
+ request.ra_addr = (uint64_t)PCICFG_LADDR(
+ assigned[i].pci_phys_low,
+ assigned[i].pci_phys_mid);
+
+ /* allocate memory space from the allocator */
+ if (ndi_ra_alloc(ddi_get_parent(dip),
+ &request, &answer, &alen,
+ NDI_RA_TYPE_MEM, NDI_RA_PASS)
+ != NDI_SUCCESS) {
+ DEBUG0("Failed to allocate 64b mem\n");
+ kmem_free(assigned, length);
+ return (PCICFG_FAILURE);
+ }
+
+ break;
+ case PCI_REG_ADDR_G(PCI_ADDR_MEM32):
+ request.ra_addr = (uint64_t)
+ assigned[i].pci_phys_low;
+
+ /* allocate memory space from the allocator */
+ if (ndi_ra_alloc(ddi_get_parent(dip),
+ &request, &answer, &alen,
+ NDI_RA_TYPE_MEM, NDI_RA_PASS)
+ != NDI_SUCCESS) {
+ DEBUG0("Failed to allocate 32b mem\n");
+ kmem_free(assigned, length);
+ return (PCICFG_FAILURE);
+ }
+
+ break;
+ case PCI_REG_ADDR_G(PCI_ADDR_IO):
+ request.ra_addr = (uint64_t)
+ assigned[i].pci_phys_low;
+
+ /* allocate I/O space from the allocator */
+ if (ndi_ra_alloc(ddi_get_parent(dip),
+ &request, &answer, &alen,
+ NDI_RA_TYPE_IO, NDI_RA_PASS)
+ != NDI_SUCCESS) {
+ DEBUG0("Failed to allocate I/O\n");
+ kmem_free(assigned, length);
+ return (PCICFG_FAILURE);
+ }
+
+ break;
+ default:
+ DEBUG0("Unknown register type\n");
+ kmem_free(assigned, length);
+ return (PCICFG_FAILURE);
+ } /* switch */
+ }
+ }
+
+ (void) pcicfg_device_on(handle);
+ kmem_free(assigned, length);
+
+ PCICFG_DUMP_DEVICE_CONFIG(handle);
+
+ (void) pcicfg_config_teardown(&handle);
+ return (PCICFG_SUCCESS);
+}
+
/*
* The "dip" passed to this routine is assumed to be
- * the device at the attachment point. Currently it is
+ * the device at the Hotplug Connection (CN). Currently it is
* assumed to be a bridge.
*/
static int
@@ -2130,9 +2367,9 @@ pcicfg_allocate_chunk(dev_info_t *dip)
* Call into the memory allocator with the request.
* Record the addresses returned in the phdl
*/
- DEBUG1("AP requires [0x%x] bytes of memory space\n",
+ DEBUG1("Connector requires [0x%x] bytes of memory space\n",
mem_request->ra_len);
- DEBUG1("AP requires [0x%x] bytes of I/O space\n",
+ DEBUG1("Connector requires [0x%x] bytes of I/O space\n",
io_request->ra_len);
mem_request->ra_align_mask =
@@ -2635,7 +2872,7 @@ pcicfg_free_bridge_resources(dev_info_t *dip)
}
static int
-pcicfg_free_device_resources(dev_info_t *dip)
+pcicfg_free_device_resources(dev_info_t *dip, pcicfg_flags_t flags)
{
pci_regspec_t *assigned;
@@ -2666,7 +2903,7 @@ pcicfg_free_device_resources(dev_info_t *dip)
(PCI_REG_REG_G(assigned[i].pci_phys_hi) != PCI_CONF_ROM))
break;
- if (pcicfg_free_resource(dip, assigned[i])) {
+ if (pcicfg_free_resource(dip, assigned[i], flags)) {
DEBUG1("pcicfg_free_device_resources - Trouble freeing "
"%x\n", assigned[i].pci_phys_hi);
/*
@@ -2682,7 +2919,7 @@ pcicfg_free_device_resources(dev_info_t *dip)
}
static int
-pcicfg_free_resources(dev_info_t *dip)
+pcicfg_free_resources(dev_info_t *dip, pcicfg_flags_t flags)
{
ddi_acc_handle_t handle;
uint8_t header_type;
@@ -2700,12 +2937,19 @@ pcicfg_free_resources(dev_info_t *dip)
* A different algorithm is used for bridges and leaf devices.
*/
if ((header_type & PCI_HEADER_TYPE_M) == PCI_HEADER_PPB) {
+ /*
+ * We only support readonly probing for leaf devices.
+ */
+ if (flags & PCICFG_FLAG_READ_ONLY)
+ return (PCICFG_FAILURE);
+
if (pcicfg_free_bridge_resources(dip) != PCICFG_SUCCESS) {
DEBUG0("Failed freeing up bridge resources\n");
return (PCICFG_FAILURE);
}
} else {
- if (pcicfg_free_device_resources(dip) != PCICFG_SUCCESS) {
+ if (pcicfg_free_device_resources(dip, flags)
+ != PCICFG_SUCCESS) {
DEBUG0("Failed freeing up device resources\n");
return (PCICFG_FAILURE);
}
@@ -2873,7 +3117,7 @@ pcicfg_update_ranges_prop(dev_info_t *dip, pcicfg_range_t *addition)
* Allocate memory for the existing reg(s) plus one and then
* build it.
*/
- newreg = kmem_zalloc(rlen+sizeof (pcicfg_range_t), KM_SLEEP);
+ newreg = kmem_zalloc(rlen + sizeof (pcicfg_range_t), KM_SLEEP);
bcopy(ranges, newreg, rlen);
bcopy(addition, newreg + rlen, sizeof (pcicfg_range_t));
@@ -3018,6 +3262,83 @@ pcicfg_update_available_prop(dev_info_t *dip, pci_regspec_t *newone)
return (PCICFG_SUCCESS);
}
+static int
+pcicfg_update_assigned_prop_value(dev_info_t *dip, uint32_t size,
+ uint32_t base, uint32_t base_hi, uint_t reg_offset)
+{
+ int rlen;
+ pci_regspec_t *reg;
+ uint32_t hiword;
+ pci_regspec_t addition;
+ uint_t status;
+
+ status = ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
+ "reg", (caddr_t)&reg, &rlen);
+
+ switch (status) {
+ case DDI_PROP_SUCCESS:
+ break;
+ case DDI_PROP_NO_MEMORY:
+ DEBUG0("reg present, but unable to get memory\n");
+ return (PCICFG_FAILURE);
+ default:
+ /*
+ * Since the config space "reg" entry should have been
+ * created, we expect a "reg" property already
+ * present here.
+ */
+ DEBUG0("no reg property\n");
+ return (PCICFG_FAILURE);
+ }
+
+ /*
+ * Build the regspec, then add it to the existing one(s)
+ */
+
+ hiword = PCICFG_MAKE_REG_HIGH(PCI_REG_BUS_G(reg->pci_phys_hi),
+ PCI_REG_DEV_G(reg->pci_phys_hi),
+ PCI_REG_FUNC_G(reg->pci_phys_hi), reg_offset);
+
+ hiword |= PCI_REG_REL_M;
+
+ if (reg_offset == PCI_CONF_ROM) {
+ hiword |= PCI_ADDR_MEM32;
+
+ base = PCI_BASE_ROM_ADDR_M & base;
+ } else {
+ if ((PCI_BASE_SPACE_M & base) == PCI_BASE_SPACE_MEM) {
+ if ((PCI_BASE_TYPE_M & base) == PCI_BASE_TYPE_MEM) {
+ hiword |= PCI_ADDR_MEM32;
+ } else if ((PCI_BASE_TYPE_M & base)
+ == PCI_BASE_TYPE_ALL) {
+ hiword |= PCI_ADDR_MEM64;
+ }
+
+ if (base & PCI_BASE_PREF_M)
+ hiword |= PCI_REG_PF_M;
+
+ base = PCI_BASE_M_ADDR_M & base;
+ } else {
+ hiword |= PCI_ADDR_IO;
+
+ base = PCI_BASE_IO_ADDR_M & base;
+ base_hi = 0;
+ }
+ }
+
+ addition.pci_phys_hi = hiword;
+ addition.pci_phys_mid = base_hi;
+ addition.pci_phys_low = base;
+ addition.pci_size_hi = 0;
+ addition.pci_size_low = size;
+
+ DEBUG3("updating BAR@off %x with %x,%x\n", reg_offset, hiword, size);
+
+ kmem_free((caddr_t)reg, rlen);
+
+ return (pcicfg_update_assigned_prop(dip, &addition));
+}
+
static void
pcicfg_device_on(ddi_acc_handle_t config_handle)
{
@@ -3644,17 +3965,15 @@ pcicfg_enable_bridge_probe_err(dev_info_t *dip, ddi_acc_handle_t h,
}
static int
-pcicfg_probe_children(dev_info_t *parent, uint_t bus,
- uint_t device, uint_t func, uint_t *highest_bus)
+pcicfg_probe_children(dev_info_t *parent, uint_t bus, uint_t device,
+ uint_t func, uint_t *highest_bus, pcicfg_flags_t flags)
{
dev_info_t *new_child;
ddi_acc_handle_t config_handle;
uint8_t header_type, pcie_dev = 0;
-
- int i;
- uint32_t request;
int ret;
pcicfg_err_regs_t regs;
+
/*
* This node will be put immediately below
* "parent". Allocate a blank device node. It will either
@@ -3747,6 +4066,10 @@ pcicfg_probe_children(dev_info_t *parent, uint_t bus,
DEBUG3("--Bridge found bus [0x%x] device"
"[0x%x] func [0x%x]\n", bus, device, func);
+ /* Only support read-only probe for leaf device */
+ if (flags & PCICFG_FLAG_READ_ONLY)
+ goto failedchild;
+
if (pcicfg_probe_bridge(new_child, config_handle,
bus, highest_bus) != PCICFG_SUCCESS) {
(void) pcicfg_free_bridge_resources(new_child);
@@ -3759,98 +4082,149 @@ pcicfg_probe_children(dev_info_t *parent, uint_t bus,
"[0x%x] func [0x%x]\n",
bus, device, func);
- i = PCI_CONF_BASE0;
-
- while (i <= PCI_CONF_BASE5) {
-
- pci_config_put32(config_handle, i, 0xffffffff);
+ if (flags & PCICFG_FLAG_READ_ONLY) {
+ /*
+ * with read-only probe, don't do any resource
+ * allocation, just read the BARs and update props.
+ */
+ ret = pcicfg_populate_props_from_bar(new_child,
+ config_handle);
+ if (ret != PCICFG_SUCCESS)
+ goto failedchild;
- request = pci_config_get32(config_handle, i);
/*
- * If its a zero length, don't do
- * any programming.
+ * for readonly probe "assigned-addresses" property
+ * has already been setup by reading the BAR, here
+ * just substract the resource from its parent here.
*/
- if (request != 0) {
- /*
- * Add to the "reg" property
- */
- if (pcicfg_update_reg_prop(new_child,
- request, i) != PCICFG_SUCCESS) {
- goto failedchild;
- }
- } else {
- DEBUG1("BASE register [0x%x] asks for "
- "[0x0]=[0x0](32)\n", i);
- i += 4;
- continue;
+ ret = pcicfg_device_assign_readonly(new_child);
+ if (ret != PCICFG_SUCCESS) {
+ (void) pcicfg_free_device_resources(new_child,
+ flags);
+ goto failedchild;
}
-
+ } else {
/*
- * Increment by eight if it is 64 bit address space
+ * update "reg" property by sizing the BARs.
*/
- if ((PCI_BASE_TYPE_M & request) == PCI_BASE_TYPE_ALL) {
- DEBUG3("BASE register [0x%x] asks for "
- "[0x%x]=[0x%x] (64)\n",
- i, request,
- (~(PCI_BASE_M_ADDR_M & request))+1)
- i += 8;
- } else {
- DEBUG3("BASE register [0x%x] asks for "
- "[0x%x]=[0x%x](32)\n",
- i, request,
- (~(PCI_BASE_M_ADDR_M & request))+1)
- i += 4;
+ ret = pcicfg_populate_reg_props(new_child,
+ config_handle);
+ if (ret != PCICFG_SUCCESS)
+ goto failedchild;
+
+ /* now allocate & program the resources */
+ ret = pcicfg_device_assign(new_child);
+ if (ret != PCICFG_SUCCESS) {
+ (void) pcicfg_free_device_resources(new_child,
+ flags);
+ goto failedchild;
}
}
- /*
- * Get the ROM size and create register for it
- */
- pci_config_put32(config_handle, PCI_CONF_ROM, 0xfffffffe);
+ (void) ndi_devi_bind_driver(new_child, 0);
+ }
+
+ (void) pcicfg_config_teardown(&config_handle);
+ return (PCICFG_SUCCESS);
+
+failedchild:
+
+ (void) pcicfg_config_teardown(&config_handle);
+
+failedconfig:
+
+ (void) ndi_devi_free(new_child);
+ return (PCICFG_FAILURE);
+}
+
+/*
+ * Sizing the BARs and update "reg" property
+ */
+static int
+pcicfg_populate_reg_props(dev_info_t *new_child,
+ ddi_acc_handle_t config_handle)
+{
+ int i;
+ uint32_t request;
+
+ i = PCI_CONF_BASE0;
+
+ while (i <= PCI_CONF_BASE5) {
- request = pci_config_get32(config_handle, PCI_CONF_ROM);
+ pci_config_put32(config_handle, i, 0xffffffff);
+
+ request = pci_config_get32(config_handle, i);
/*
* If its a zero length, don't do
* any programming.
*/
-
if (request != 0) {
- DEBUG3("BASE register [0x%x] asks for [0x%x]=[0x%x]\n",
- PCI_CONF_ROM, request,
- (~(PCI_BASE_ROM_ADDR_M & request))+1);
/*
* Add to the "reg" property
*/
if (pcicfg_update_reg_prop(new_child,
- request, PCI_CONF_ROM) != PCICFG_SUCCESS) {
+ request, i) != PCICFG_SUCCESS) {
goto failedchild;
}
+ } else {
+ DEBUG1("BASE register [0x%x] asks for "
+ "[0x0]=[0x0](32)\n", i);
+ i += 4;
+ continue;
}
- /* now allocate & program the resources */
- if (pcicfg_device_assign(new_child) != PCICFG_SUCCESS) {
- (void) pcicfg_free_device_resources(new_child);
- goto failedchild;
+ /*
+ * Increment by eight if it is 64 bit address space
+ */
+ if ((PCI_BASE_TYPE_M & request) == PCI_BASE_TYPE_ALL) {
+ DEBUG3("BASE register [0x%x] asks for "
+ "[0x%x]=[0x%x] (64)\n",
+ i, request,
+ (~(PCI_BASE_M_ADDR_M & request))+1)
+ i += 8;
+ } else {
+ DEBUG3("BASE register [0x%x] asks for "
+ "[0x%x]=[0x%x](32)\n",
+ i, request,
+ (~(PCI_BASE_M_ADDR_M & request))+1)
+ i += 4;
}
- (void) ndi_devi_bind_driver(new_child, 0);
}
- (void) pcicfg_config_teardown(&config_handle);
- return (PCICFG_SUCCESS);
+ /*
+ * Get the ROM size and create register for it
+ */
+ pci_config_put32(config_handle, PCI_CONF_ROM, 0xfffffffe);
-failedchild:
+ request = pci_config_get32(config_handle, PCI_CONF_ROM);
+ /*
+ * If its a zero length, don't do
+ * any programming.
+ */
- (void) pcicfg_config_teardown(&config_handle);
+ if (request != 0) {
+ DEBUG3("BASE register [0x%x] asks for [0x%x]=[0x%x]\n",
+ PCI_CONF_ROM, request,
+ (~(PCI_BASE_ROM_ADDR_M & request))+1);
+ /*
+ * Add to the "reg" property
+ */
+ if (pcicfg_update_reg_prop(new_child,
+ request, PCI_CONF_ROM) != PCICFG_SUCCESS) {
+ goto failedchild;
+ }
+ }
-failedconfig:
- (void) ndi_devi_free(new_child);
+ return (PCICFG_SUCCESS);
+
+failedchild:
return (PCICFG_FAILURE);
}
static int
-pcicfg_fcode_probe(dev_info_t *parent, uint_t bus,
- uint_t device, uint_t func, uint_t *highest_bus)
+pcicfg_fcode_probe(dev_info_t *parent, uint_t bus, uint_t device,
+ uint_t func, uint_t *highest_bus, pcicfg_flags_t flags)
{
dev_info_t *new_child;
int8_t header_type;
@@ -3886,7 +4260,11 @@ pcicfg_fcode_probe(dev_info_t *parent, uint_t bus,
*/
if (pci_config_setup(parent, &ph) != DDI_SUCCESS)
return (DDI_FAILURE);
- pcicfg_disable_bridge_probe_err(parent, ph, &parent_regs);
+
+ if ((flags & PCICFG_FLAG_READ_ONLY) == 0) {
+ pcicfg_disable_bridge_probe_err(parent,
+ ph, &parent_regs);
+ }
}
/*
@@ -4013,6 +4391,10 @@ pcicfg_fcode_probe(dev_info_t *parent, uint_t bus,
DEBUG3("--Bridge found bus [0x%x] device"
"[0x%x] func [0x%x]\n", bus, device, func);
+ /* Only support read-only probe for leaf device */
+ if (flags & PCICFG_FLAG_READ_ONLY)
+ goto failed;
+
if ((ret = pcicfg_probe_bridge(new_child, h,
bus, highest_bus)) != PCICFG_SUCCESS)
(void) pcicfg_free_bridge_resources(new_child);
@@ -4029,6 +4411,10 @@ pcicfg_fcode_probe(dev_info_t *parent, uint_t bus,
*/
(void) i_ndi_config_node(new_child, DS_LINKED, 0);
+ /* XXX for now, don't run Fcode in read-only probe. */
+ if (flags & PCICFG_FLAG_READ_ONLY)
+ goto no_fcode;
+
if (pci_config_get8(h, PCI_CONF_IPIN)) {
pci_config_put8(h, PCI_CONF_ILINE, 0xf);
}
@@ -4087,7 +4473,7 @@ pcicfg_fcode_probe(dev_info_t *parent, uint_t bus,
/*
* Free the ROM resources.
*/
- (void) pcicfg_free_resource(new_child, p);
+ (void) pcicfg_free_resource(new_child, p, 0);
DEBUG2("configure: fcode addr %lx size %x\n",
fcode_addr, fcode_size);
@@ -4210,10 +4596,11 @@ pcicfg_fcode_probe(dev_info_t *parent, uint_t bus,
* processing. Cleanup and fail the operation.
*/
DEBUG0("Interpreter detected fcode failure\n");
- (void) pcicfg_free_resources(new_child);
+ (void) pcicfg_free_resources(new_child, flags);
ret = PCICFG_FAILURE;
goto failed;
} else {
+no_fcode:
/*
* Either the interpreter failed with FC_NO_FCODE or we
* chose not to run the interpreter
@@ -4228,7 +4615,7 @@ pcicfg_fcode_probe(dev_info_t *parent, uint_t bus,
* during fcode loading/execution since we need
* to start over.
*/
- (void) pcicfg_free_resources(new_child);
+ (void) pcicfg_free_resources(new_child, flags);
#ifdef EFCODE21554
pcicfg_config_teardown(&h);
@@ -4240,7 +4627,7 @@ pcicfg_fcode_probe(dev_info_t *parent, uint_t bus,
DEBUG0("No Drop-in Probe device ourself\n");
ret = pcicfg_probe_children(parent, bus, device, func,
- highest_bus);
+ highest_bus, flags);
if (ret != PCICFG_SUCCESS) {
DEBUG0("Could not self probe child\n");
@@ -4293,7 +4680,7 @@ pcicfg_fcode_probe(dev_info_t *parent, uint_t bus,
* warnings on retries of the failed configure.
*/
(void) pcicfg_ntbridge_unconfigure(new_child);
- (void) pcicfg_teardown_device(new_child);
+ (void) pcicfg_teardown_device(new_child, flags);
}
#endif
goto done2;
@@ -4312,15 +4699,129 @@ failed3:
done2:
failed2:
if (parent_regs.pcie_dev) {
- pcicfg_enable_bridge_probe_err(parent, ph, &parent_regs);
+ if ((flags & PCICFG_FLAG_READ_ONLY) == 0) {
+ pcicfg_enable_bridge_probe_err(parent,
+ ph, &parent_regs);
+ }
pci_config_teardown(&ph);
}
return (ret);
}
+/*
+ * Read the BARs and update properties. Used in virtual hotplug.
+ */
+static int
+pcicfg_populate_props_from_bar(dev_info_t *new_child,
+ ddi_acc_handle_t config_handle)
+{
+ uint32_t request, base, base_hi, size;
+ int i;
+
+ i = PCI_CONF_BASE0;
+
+ while (i <= PCI_CONF_BASE5) {
+ /*
+ * determine the size of the address space
+ */
+ base = pci_config_get32(config_handle, i);
+ pci_config_put32(config_handle, i, 0xffffffff);
+ request = pci_config_get32(config_handle, i);
+ pci_config_put32(config_handle, i, base);
+
+ /*
+ * If its a zero length, don't do any programming.
+ */
+ if (request != 0) {
+ /*
+ * Add to the "reg" property
+ */
+ if (pcicfg_update_reg_prop(new_child,
+ request, i) != PCICFG_SUCCESS) {
+ goto failedchild;
+ }
+
+ if ((PCI_BASE_SPACE_IO & request) == 0 &&
+ (PCI_BASE_TYPE_M & request) == PCI_BASE_TYPE_ALL) {
+ base_hi = pci_config_get32(config_handle, i+4);
+ } else {
+ base_hi = 0;
+ }
+ /*
+ * Add to "assigned-addresses" property
+ */
+ size = (~(PCI_BASE_M_ADDR_M & request))+1;
+ if (pcicfg_update_assigned_prop_value(new_child,
+ size, base, base_hi, i) != PCICFG_SUCCESS) {
+ goto failedchild;
+ }
+ } else {
+ DEBUG1("BASE register [0x%x] asks for "
+ "[0x0]=[0x0](32)\n", i);
+ i += 4;
+ continue;
+ }
+
+ /*
+ * Increment by eight if it is 64 bit address space
+ */
+ if ((PCI_BASE_TYPE_M & request) == PCI_BASE_TYPE_ALL) {
+ DEBUG3("BASE register [0x%x] asks for "
+ "[0x%x]=[0x%x] (64)\n",
+ i, request,
+ (~(PCI_BASE_M_ADDR_M & request))+1)
+ i += 8;
+ } else {
+ DEBUG3("BASE register [0x%x] asks for "
+ "[0x%x]=[0x%x](32)\n",
+ i, request,
+ (~(PCI_BASE_M_ADDR_M & request))+1)
+ i += 4;
+ }
+ }
+
+ /*
+ * Get the ROM size and create register for it
+ */
+ base = pci_config_get32(config_handle, PCI_CONF_ROM);
+ pci_config_put32(config_handle, PCI_CONF_ROM, 0xfffffffe);
+ request = pci_config_get32(config_handle, PCI_CONF_ROM);
+ pci_config_put32(config_handle, PCI_CONF_ROM, base);
+
+ /*
+ * If its a zero length, don't do
+ * any programming.
+ */
+ if (request != 0) {
+ DEBUG3("BASE register [0x%x] asks for [0x%x]=[0x%x]\n",
+ PCI_CONF_ROM, request,
+ (~(PCI_BASE_ROM_ADDR_M & request))+1);
+ /*
+ * Add to the "reg" property
+ */
+ if (pcicfg_update_reg_prop(new_child,
+ request, PCI_CONF_ROM) != PCICFG_SUCCESS) {
+ goto failedchild;
+ }
+ /*
+ * Add to "assigned-addresses" property
+ */
+ size = (~(PCI_BASE_ROM_ADDR_M & request))+1;
+ if (pcicfg_update_assigned_prop_value(new_child, size,
+ base, 0, PCI_CONF_ROM) != PCICFG_SUCCESS) {
+ goto failedchild;
+ }
+ }
+
+ return (PCICFG_SUCCESS);
+
+failedchild:
+ return (PCICFG_FAILURE);
+}
+
static int
pcicfg_probe_bridge(dev_info_t *new_child, ddi_acc_handle_t h, uint_t bus,
- uint_t *highest_bus)
+ uint_t *highest_bus)
{
uint64_t next_bus;
uint_t new_bus, num_slots;
@@ -4336,6 +4837,28 @@ pcicfg_probe_bridge(dev_info_t *new_child, ddi_acc_handle_t h, uint_t bus,
uint64_t pcibus_base, pcibus_alen;
uint64_t max_bus;
uint8_t pcie_device_type = 0;
+ dev_info_t *new_device;
+ int trans_device;
+ int ari_mode = B_FALSE;
+ int max_function = PCICFG_MAX_FUNCTION;
+
+ /*
+ * Set "device_type" to "pci", the actual type will be set later
+ * by pcicfg_set_busnode_props() below. This is needed as the
+ * pcicfg_ra_free() below would update "available" property based
+ * on "device_type".
+ *
+ * This code can be removed later after PCI configurator is changed
+ * to use PCIRM, which automatically update properties upon allocation
+ * and free, at that time we'll be able to remove the code inside
+ * ndi_ra_alloc/free() which currently updates "available" property
+ * for pci/pcie devices in pcie fabric.
+ */
+ if (ndi_prop_update_string(DDI_DEV_T_NONE, new_child,
+ "device_type", "pci") != DDI_SUCCESS) {
+ DEBUG0("Failed to set \"device_type\" props\n");
+ return (PCICFG_FAILURE);
+ }
bzero((caddr_t)&req, sizeof (ndi_ra_request_t));
req.ra_flags = (NDI_RA_ALLOC_BOUNDED | NDI_RA_ALLOC_PARTIAL_OK);
@@ -4651,32 +5174,83 @@ pcicfg_probe_bridge(dev_info_t *new_child, ddi_acc_handle_t h, uint_t bus,
*/
DEBUG0("Bridge Programming Complete - probe children\n");
ndi_devi_enter(new_child, &count);
- for (i = 0; i < PCICFG_MAX_DEVICE; i++) {
- for (j = 0; j < PCICFG_MAX_FUNCTION; j++) {
+ for (i = 0; ((i < PCICFG_MAX_DEVICE) && (ari_mode == B_FALSE));
+ i++) {
+ for (j = 0; j < max_function; ) {
+ if (ari_mode)
+ trans_device = j >> 3;
+ else
+ trans_device = i;
+
if ((rval = pcicfg_fcode_probe(new_child,
- new_bus, i, j, highest_bus))
+ new_bus, trans_device, (j & 7), highest_bus, 0))
!= PCICFG_SUCCESS) {
if (rval == PCICFG_NODEVICE) {
DEBUG3("No Device at bus [0x%x]"
"device [0x%x] "
- "func [0x%x]\n", new_bus, i, j);
+ "func [0x%x]\n", new_bus,
+ trans_device, j & 7);
+
if (j)
- continue;
+ goto next;
} else {
DEBUG3("Failed to configure bus "
"[0x%x] device [0x%x] "
- "func [0x%x]\n", new_bus, i, j);
+ "func [0x%x]\n", new_bus,
+ trans_device, j & 7);
+
rval = PCICFG_FAILURE;
}
-
break;
}
- }
- /* if any function fails to be configured, no need to proceed */
- if (rval != PCICFG_NODEVICE) {
- break;
+next:
+ new_device = pcicfg_devi_find(new_child,
+ trans_device, (j & 7));
+
+ /*
+ * Determine if ARI Forwarding should be enabled.
+ */
+ if (j == 0) {
+ if (new_device == NULL)
+ break;
+
+ if ((pcie_ari_supported(new_child) ==
+ PCIE_ARI_FORW_ENABLED) &&
+ (pcie_ari_device(new_device) ==
+ PCIE_ARI_DEVICE)) {
+ if (pcie_ari_enable(new_child) ==
+ DDI_SUCCESS) {
+ (void) ddi_prop_create(
+ DDI_DEV_T_NONE,
+ new_child,
+ DDI_PROP_CANSLEEP,
+ "ari-enabled", NULL, 0);
+ ari_mode = B_TRUE;
+ max_function =
+ PCICFG_MAX_ARI_FUNCTION;
+ }
+ }
+ }
+
+ if (ari_mode == B_TRUE) {
+ int next_function;
+
+ if (new_device == NULL)
+ break;
+
+ if (pcie_ari_get_next_function(new_device,
+ &next_function) != DDI_SUCCESS)
+ break;
+
+ j = next_function;
+
+ if (next_function == 0)
+ break;
+ } else
+ j++;
}
}
+
ndi_devi_exit(new_child, count);
/* if empty topology underneath, it is still a success. */
@@ -4685,8 +5259,15 @@ pcicfg_probe_bridge(dev_info_t *new_child, ddi_acc_handle_t h, uint_t bus,
/*
* Offline the bridge to allow reprogramming of resources.
+ *
+ * This should always succeed since nobody else has started to
+ * use it yet, failing to detach the driver would indicate a bug.
+ * Also in that case it's better just panic than allowing the
+ * configurator to proceed with BAR reprogramming without bridge
+ * driver detached.
*/
- (void) ndi_devi_offline(new_child, NDI_NO_EVENT|NDI_UNCONFIG);
+ VERIFY(ndi_devi_offline(new_child, NDI_NO_EVENT|NDI_UNCONFIG)
+ == NDI_SUCCESS);
phdl.dip = new_child;
phdl.memory_base = mem_answer;
@@ -4710,6 +5291,17 @@ pcicfg_probe_bridge(dev_info_t *new_child, ddi_acc_handle_t h, uint_t bus,
"Mem=0x%lx I/O=0x%lx\n", num_slots, mem_end, io_end);
/*
+ * Before probing the children we've allocated maximum MEM/IO
+ * resources from parent, and updated "available" property
+ * accordingly. Later we'll be giving up unused resources to
+ * the parent, thus we need to destroy "available" property
+ * here otherwise it will be out-of-sync with the actual free
+ * resources this bridge has. This property will be rebuilt below
+ * with the actual free resources reserved for hotplug slots
+ * (if any).
+ */
+ (void) ndi_prop_remove(DDI_DEV_T_NONE, new_child, "available");
+ /*
* if the bridge a slots, then preallocate. If not, assume static
* configuration. Also check for preallocation limits and spit
* warning messages appropriately (perhaps some can be in debug mode).
@@ -5485,7 +6077,7 @@ failure:
* We came in with no "assigned-addresses".
* Free up the resources we may have allocated.
*/
- (void) pcicfg_free_device_resources(dip);
+ (void) pcicfg_free_device_resources(dip, 0);
return (PCICFG_FAILURE);
}
@@ -5510,7 +6102,7 @@ pcicfg_free_all_resources(dev_info_t *dip)
acount = assigned_len / sizeof (pci_regspec_t);
for (i = 0; i < acount; i++) {
- if (pcicfg_free_resource(dip, assigned[i])) {
+ if (pcicfg_free_resource(dip, assigned[i], 0)) {
/*
* Dont forget to free mem from ddi_getlongprop
*/
@@ -5591,7 +6183,7 @@ pcicfg_alloc_new_resources(dev_info_t *dip)
* Free the old resource.
*/
(void) pcicfg_free_resource(dip,
- assigned[j]);
+ assigned[j], 0);
} else {
DEBUG3("pcicfg_alloc_new_resources"
" - %x - ENOUGH"
@@ -5635,7 +6227,8 @@ pcicfg_alloc_new_resources(dev_info_t *dip)
* Free the old resource. Again, adjust
* the size to be safe.
*/
- (void) pcicfg_free_resource(dip, assigned[j]);
+ (void) pcicfg_free_resource(dip,
+ assigned[j], 0);
alloc_size = MAX(reg[i].pci_size_low,
assigned[j].pci_size_low);
@@ -5678,6 +6271,7 @@ pcicfg_alloc_new_resources(dev_info_t *dip)
return (PCICFG_SUCCESS);
}
+
static int
pcicfg_alloc_resource(dev_info_t *dip, pci_regspec_t phys_spec)
{
@@ -5883,7 +6477,8 @@ pcicfg_alloc_resource(dev_info_t *dip, pci_regspec_t phys_spec)
}
static int
-pcicfg_free_resource(dev_info_t *dip, pci_regspec_t phys_spec)
+pcicfg_free_resource(dev_info_t *dip, pci_regspec_t phys_spec,
+ pcicfg_flags_t flags)
{
int offset;
pci_regspec_t config;
@@ -5933,7 +6528,8 @@ pcicfg_free_resource(dev_info_t *dip, pci_regspec_t phys_spec)
/* Unmap the BAR by writing a zero */
- ddi_put32(h, (uint32_t *)v, (uint32_t)0);
+ if ((flags & PCICFG_FLAG_READ_ONLY) == 0)
+ ddi_put32(h, (uint32_t *)v, (uint32_t)0);
} else {
switch (PCI_REG_ADDR_G(phys_spec.pci_phys_hi)) {
@@ -6130,6 +6726,20 @@ pcicfg_unmap_phys(ddi_acc_handle_t *handlep, pci_regspec_t *ph)
impl_acc_hdl_free(*handlep);
*handlep = (ddi_acc_handle_t)NULL;
}
+
+static int
+pcicfg_ari_configure(dev_info_t *dip)
+{
+ if (pcie_ari_supported(dip) == PCIE_ARI_FORW_NOT_SUPPORTED)
+ return (DDI_FAILURE);
+
+ /*
+ * Until we have resource balancing, dynamically configure
+ * ARI functions without firmware assistamce.
+ */
+ return (DDI_FAILURE);
+}
+
#ifdef DEBUG
static void
debug(char *fmt, uintptr_t a1, uintptr_t a2, uintptr_t a3,
diff --git a/usr/src/uts/sun4/io/px/px.c b/usr/src/uts/sun4/io/px/px.c
index aad5dcbb10..e436063b1b 100644
--- a/usr/src/uts/sun4/io/px/px.c
+++ b/usr/src/uts/sun4/io/px/px.c
@@ -24,7 +24,7 @@
*/
/*
- * PCI Express nexus driver interface
+ * SPARC Host to PCI Express nexus driver
*/
#include <sys/types.h>
@@ -33,14 +33,12 @@
#include <sys/kmem.h>
#include <sys/sunddi.h>
#include <sys/sunndi.h>
-#include <sys/ddi_impldefs.h>
#include <sys/ddi_subrdefs.h>
#include <sys/spl.h>
#include <sys/epm.h>
#include <sys/iommutsb.h>
-#include <sys/hotplug/pci/pcihp.h>
-#include <sys/hotplug/pci/pciehpc.h>
#include "px_obj.h"
+#include <sys/hotplug/pci/pcie_hp.h>
#include <sys/pci_tools.h>
#include "px_tools_ext.h"
#include <sys/pcie_pwr.h>
@@ -64,12 +62,6 @@ static void px_set_mps(px_t *px_p);
extern int pcie_max_mps;
/*
- * function prototypes for hotplug routines:
- */
-static int px_init_hotplug(px_t *px_p);
-static int px_uninit_hotplug(dev_info_t *dip);
-
-/*
* bus ops and dev ops structures:
*/
static struct bus_ops px_bus_ops = {
@@ -101,7 +93,8 @@ static struct bus_ops px_bus_ops = {
px_bus_enter, /* (*bus_fm_access_enter)(); */
px_bus_exit, /* (*bus_fm_access_fini)(); */
pcie_bus_power, /* (*bus_power)(); */
- px_intr_ops /* (*bus_intr_op)(); */
+ px_intr_ops, /* (*bus_intr_op)(); */
+ pcie_hp_common_ops /* (*bus_hp_op)(); */
};
extern struct cb_ops px_cb_ops;
@@ -128,9 +121,13 @@ static struct dev_ops px_ops = {
extern struct mod_ops mod_driverops;
static struct modldrv modldrv = {
- &mod_driverops, /* Type of module - driver */
- "PCI Express nexus driver", /* Name of module. */
- &px_ops, /* driver ops */
+ &mod_driverops, /* Type of module - driver */
+#if defined(sun4u)
+ "Sun4u Host to PCIe nexus driver", /* Name of module. */
+#elif defined(sun4v)
+ "Sun4v Host to PCIe nexus driver", /* Name of module. */
+#endif
+ &px_ops, /* driver ops */
};
static struct modlinkage modlinkage = {
@@ -189,31 +186,29 @@ _info(struct modinfo *modinfop)
static int
px_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
{
- int instance = getminor((dev_t)arg);
+ minor_t minor = getminor((dev_t)arg);
+ int instance = PCI_MINOR_NUM_TO_INSTANCE(minor);
px_t *px_p = INST_TO_STATE(instance);
+ int ret = DDI_SUCCESS;
- /*
- * Allow hotplug to deal with ones it manages
- * Hot Plug will be done later.
- */
- if (px_p && (px_p->px_dev_caps & PX_HOTPLUG_CAPABLE))
- return (pcihp_info(dip, infocmd, arg, result));
-
- /* non-hotplug or not attached */
switch (infocmd) {
case DDI_INFO_DEVT2INSTANCE:
*result = (void *)(intptr_t)instance;
- return (DDI_SUCCESS);
-
+ break;
case DDI_INFO_DEVT2DEVINFO:
- if (px_p == NULL)
- return (DDI_FAILURE);
- *result = (void *)px_p->px_dip;
- return (DDI_SUCCESS);
+ if (px_p == NULL) {
+ ret = DDI_FAILURE;
+ break;
+ }
+ *result = (void *)px_p->px_dip;
+ break;
default:
- return (DDI_FAILURE);
+ ret = DDI_FAILURE;
+ break;
}
+
+ return (ret);
}
/* device driver entry points */
@@ -228,6 +223,7 @@ px_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
int instance = DIP_TO_INST(dip);
int ret = DDI_SUCCESS;
devhandle_t dev_hdl = NULL;
+ pcie_hp_regops_t regops;
switch (cmd) {
case DDI_ATTACH:
@@ -245,14 +241,14 @@ px_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
px_p = INST_TO_STATE(instance);
px_p->px_dip = dip;
mutex_init(&px_p->px_mutex, NULL, MUTEX_DRIVER, NULL);
- px_p->px_soft_state = PX_SOFT_STATE_CLOSED;
- px_p->px_open_count = 0;
+ px_p->px_soft_state = PCI_SOFT_STATE_CLOSED;
(void) ddi_prop_update_string(DDI_DEV_T_NONE, dip,
"device_type", "pciex");
/* Initialize px_dbg for high pil printing */
px_dbg_attach(dip, &px_p->px_dbg_hdl);
+ pcie_rc_init_bus(dip);
/*
* Get key properties of the pci bridge node and
@@ -311,20 +307,16 @@ px_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
if ((ret = px_err_add_intr(&px_p->px_fault)) != DDI_SUCCESS)
goto err_bad_intr;
- (void) px_init_hotplug(px_p);
+ if (px_lib_hotplug_init(dip, (void *)&regops) == DDI_SUCCESS) {
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
+
+ bus_p->bus_hp_sup_modes |= PCIE_NATIVE_HP_MODE;
+ }
(void) px_set_mps(px_p);
- /*
- * Create the "devctl" node for hotplug and pcitool support.
- * For non-hotplug bus, we still need ":devctl" to
- * support DEVCTL_DEVICE_* and DEVCTL_BUS_* ioctls.
- */
- if (ddi_create_minor_node(dip, "devctl", S_IFCHR,
- PCIHP_AP_MINOR_NUM(instance, PCIHP_DEVCTL_MINOR),
- DDI_NT_NEXUS, 0) != DDI_SUCCESS) {
- goto err_bad_devctl_node;
- }
+ if (pcie_init(dip, (caddr_t)&regops) != DDI_SUCCESS)
+ goto err_bad_hotplug;
if (pxtool_init(dip) != DDI_SUCCESS)
goto err_bad_pcitool_node;
@@ -353,8 +345,9 @@ px_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
break;
err_bad_pcitool_node:
- ddi_remove_minor_node(dip, "devctl");
-err_bad_devctl_node:
+ (void) pcie_uninit(dip);
+err_bad_hotplug:
+ (void) px_lib_hotplug_uninit(dip);
px_err_rem_intr(&px_p->px_fault);
err_bad_intr:
px_fm_detach(px_p);
@@ -377,6 +370,7 @@ err_bad_ib:
err_bad_dev_init:
px_free_props(px_p);
err_bad_px_prop:
+ pcie_rc_fini_bus(dip);
px_dbg_detach(dip, &px_p->px_dbg_hdl);
mutex_destroy(&px_p->px_mutex);
ddi_soft_state_free(px_state_p, instance);
@@ -423,9 +417,10 @@ err_bad_px_softstate:
static int
px_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
- int instance = ddi_get_instance(dip);
- px_t *px_p = INST_TO_STATE(instance);
- int ret;
+ int instance = ddi_get_instance(dip);
+ px_t *px_p = INST_TO_STATE(instance);
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
+ int ret;
/*
* Make sure we are currently attached
@@ -446,11 +441,13 @@ px_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
*/
px_cpr_rem_callb(px_p);
- if (px_p->px_dev_caps & PX_HOTPLUG_CAPABLE)
- if (px_uninit_hotplug(dip) != DDI_SUCCESS) {
- mutex_exit(&px_p->px_mutex);
- return (DDI_FAILURE);
- }
+ if (PCIE_IS_PCIE_HOTPLUG_ENABLED(bus_p))
+ (void) px_lib_hotplug_uninit(dip);
+
+ if (pcie_uninit(dip) != DDI_SUCCESS) {
+ mutex_exit(&px_p->px_mutex);
+ return (DDI_FAILURE);
+ }
/*
* things which used to be done in obj_destroy
@@ -461,7 +458,6 @@ px_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
pxtool_uninit(dip);
- ddi_remove_minor_node(dip, "devctl");
px_err_rem_intr(&px_p->px_fault);
px_fm_detach(px_p);
px_pec_detach(px_p);
@@ -481,21 +477,11 @@ px_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
* resources it's using.
*/
px_free_props(px_p);
+ pcie_rc_fini_bus(dip);
px_dbg_detach(dip, &px_p->px_dbg_hdl);
mutex_exit(&px_p->px_mutex);
mutex_destroy(&px_p->px_mutex);
- /* Free the interrupt-priorities prop if we created it. */
- {
- int len;
-
- if (ddi_getproplen(DDI_DEV_T_ANY, dip,
- DDI_PROP_NOTPROM | DDI_PROP_DONTPASS,
- "interrupt-priorities", &len) == DDI_PROP_SUCCESS)
- (void) ddi_prop_remove(DDI_DEV_T_NONE, dip,
- "interrupt-priorities");
- }
-
px_p->px_dev_hdl = NULL;
ddi_soft_state_free(px_state_p, instance);
@@ -1370,81 +1356,6 @@ px_intr_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op,
return (ret);
}
-static int
-px_init_hotplug(px_t *px_p)
-{
- px_bus_range_t bus_range;
- dev_info_t *dip;
- pciehpc_regops_t regops;
-
- dip = px_p->px_dip;
-
- if (ddi_prop_exists(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
- "hotplug-capable") == 0)
- return (DDI_FAILURE);
-
- /*
- * Before initializing hotplug - open up bus range. The busra
- * module will initialize its pool of bus numbers from this.
- * "busra" will be the agent that keeps track of them during
- * hotplug. Also, note, that busra will remove any bus numbers
- * already in use from boot time.
- */
- if (ddi_prop_exists(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
- "bus-range") == 0) {
- cmn_err(CE_WARN, "%s%d: bus-range not found\n",
- ddi_driver_name(dip), ddi_get_instance(dip));
-#ifdef DEBUG
- bus_range.lo = 0x0;
- bus_range.hi = 0xff;
-
- if (ndi_prop_update_int_array(DDI_DEV_T_NONE,
- dip, "bus-range", (int *)&bus_range, 2)
- != DDI_PROP_SUCCESS) {
- return (DDI_FAILURE);
- }
-#else
- return (DDI_FAILURE);
-#endif
- }
-
- if (px_lib_hotplug_init(dip, (void *)&regops) != DDI_SUCCESS)
- return (DDI_FAILURE);
-
- if (pciehpc_init(dip, &regops) != DDI_SUCCESS) {
- px_lib_hotplug_uninit(dip);
- return (DDI_FAILURE);
- }
-
- if (pcihp_init(dip) != DDI_SUCCESS) {
- (void) pciehpc_uninit(dip);
- px_lib_hotplug_uninit(dip);
- return (DDI_FAILURE);
- }
-
- if (pcihp_get_cb_ops() != NULL) {
- DBG(DBG_ATTACH, dip, "%s%d hotplug enabled",
- ddi_driver_name(dip), ddi_get_instance(dip));
- px_p->px_dev_caps |= PX_HOTPLUG_CAPABLE;
- }
-
- return (DDI_SUCCESS);
-}
-
-static int
-px_uninit_hotplug(dev_info_t *dip)
-{
- if (pcihp_uninit(dip) != DDI_SUCCESS)
- return (DDI_FAILURE);
-
- if (pciehpc_uninit(dip) != DDI_SUCCESS)
- return (DDI_FAILURE);
-
- px_lib_hotplug_uninit(dip);
-
- return (DDI_SUCCESS);
-}
-
static void
px_set_mps(px_t *px_p)
{
diff --git a/usr/src/uts/sun4/io/px/px_devctl.c b/usr/src/uts/sun4/io/px/px_devctl.c
index 253dfd6503..4a8805f841 100644
--- a/usr/src/uts/sun4/io/px/px_devctl.c
+++ b/usr/src/uts/sun4/io/px/px_devctl.c
@@ -38,7 +38,6 @@
#include <sys/errno.h>
#include <sys/file.h>
#include <sys/policy.h>
-#include <sys/hotplug/pci/pcihp.h>
#include "px_obj.h"
#include <sys/pci_tools.h>
#include "px_tools_ext.h"
@@ -50,8 +49,6 @@ static int px_open(dev_t *devp, int flags, int otyp, cred_t *credp);
static int px_close(dev_t dev, int flags, int otyp, cred_t *credp);
static int px_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
cred_t *credp, int *rvalp);
-static int px_prop_op(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op,
- int flags, char *name, caddr_t valuep, int *lengthp);
struct cb_ops px_cb_ops = {
px_open, /* open */
@@ -66,7 +63,7 @@ struct cb_ops px_cb_ops = {
nodev, /* mmap */
nodev, /* segmap */
nochpoll, /* poll */
- px_prop_op, /* cb_prop_op */
+ pcie_prop_op, /* cb_prop_op */
NULL, /* streamtab */
D_NEW | D_MP | D_HOTPLUG, /* Driver compatibility flag */
CB_REV, /* rev */
@@ -78,9 +75,9 @@ struct cb_ops px_cb_ops = {
static int
px_open(dev_t *devp, int flags, int otyp, cred_t *credp)
{
- px_t *px_p;
- int rval;
- uint_t orig_px_soft_state;
+ px_t *px_p = PX_DEV_TO_SOFTSTATE(*devp);
+ int minor = getminor(*devp);
+ int rval;
/*
* Make sure the open is for the right file type.
@@ -91,41 +88,44 @@ px_open(dev_t *devp, int flags, int otyp, cred_t *credp)
/*
* Get the soft state structure for the device.
*/
- px_p = PX_DEV_TO_SOFTSTATE(*devp);
if (px_p == NULL)
return (ENXIO);
+ DBG(DBG_OPEN, px_p->px_dip, "devp=%x: flags=%x\n", devp, flags);
+
/*
* Handle the open by tracking the device state.
*/
- DBG(DBG_OPEN, px_p->px_dip, "devp=%x: flags=%x\n", devp, flags);
mutex_enter(&px_p->px_mutex);
- orig_px_soft_state = px_p->px_soft_state;
+
+ switch (PCI_MINOR_NUM_TO_PCI_DEVNUM(minor)) {
+ case PCI_TOOL_REG_MINOR_NUM:
+ case PCI_TOOL_INTR_MINOR_NUM:
+ break;
+ default:
+ /* To handle devctl and hotplug related ioctls */
+ if (rval = pcie_open(px_p->px_dip, devp, flags, otyp, credp)) {
+ mutex_exit(&px_p->px_mutex);
+ return (rval);
+ }
+ }
+
if (flags & FEXCL) {
- if (px_p->px_soft_state != PX_SOFT_STATE_CLOSED) {
+ if (px_p->px_soft_state != PCI_SOFT_STATE_CLOSED) {
mutex_exit(&px_p->px_mutex);
DBG(DBG_OPEN, px_p->px_dip, "busy\n");
return (EBUSY);
}
- px_p->px_soft_state = PX_SOFT_STATE_OPEN_EXCL;
+ px_p->px_soft_state = PCI_SOFT_STATE_OPEN_EXCL;
} else {
- if (px_p->px_soft_state == PX_SOFT_STATE_OPEN_EXCL) {
+ if (px_p->px_soft_state == PCI_SOFT_STATE_OPEN_EXCL) {
mutex_exit(&px_p->px_mutex);
DBG(DBG_OPEN, px_p->px_dip, "busy\n");
return (EBUSY);
}
- px_p->px_soft_state = PX_SOFT_STATE_OPEN;
+ px_p->px_soft_state = PCI_SOFT_STATE_OPEN;
}
- if (px_p->px_dev_caps & PX_HOTPLUG_CAPABLE)
- if (rval = (pcihp_get_cb_ops())->cb_open(devp, flags,
- otyp, credp)) {
- px_p->px_soft_state = orig_px_soft_state;
- mutex_exit(&px_p->px_mutex);
- return (rval);
- }
-
- px_p->px_open_count++;
mutex_exit(&px_p->px_mutex);
return (0);
}
@@ -135,28 +135,32 @@ px_open(dev_t *devp, int flags, int otyp, cred_t *credp)
static int
px_close(dev_t dev, int flags, int otyp, cred_t *credp)
{
- px_t *px_p;
- int rval;
+ px_t *px_p = PX_DEV_TO_SOFTSTATE(dev);
+ int minor = getminor(dev);
+ int rval;
if (otyp != OTYP_CHR)
return (EINVAL);
- px_p = PX_DEV_TO_SOFTSTATE(dev);
if (px_p == NULL)
return (ENXIO);
DBG(DBG_CLOSE, px_p->px_dip, "dev=%x: flags=%x\n", dev, flags);
mutex_enter(&px_p->px_mutex);
- if (px_p->px_dev_caps & PX_HOTPLUG_CAPABLE)
- if (rval = (pcihp_get_cb_ops())->cb_close(dev, flags,
- otyp, credp)) {
+ switch (PCI_MINOR_NUM_TO_PCI_DEVNUM(minor)) {
+ case PCI_TOOL_REG_MINOR_NUM:
+ case PCI_TOOL_INTR_MINOR_NUM:
+ break;
+ default:
+ /* To handle devctl and hotplug related ioctls */
+ if (rval = pcie_close(px_p->px_dip, dev, flags, otyp, credp)) {
mutex_exit(&px_p->px_mutex);
return (rval);
}
+ }
- px_p->px_soft_state = PX_SOFT_STATE_CLOSED;
- px_p->px_open_count = 0;
+ px_p->px_soft_state = PCI_SOFT_STATE_CLOSED;
mutex_exit(&px_p->px_mutex);
return (0);
}
@@ -165,14 +169,11 @@ px_close(dev_t dev, int flags, int otyp, cred_t *credp)
static int
px_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp)
{
- px_t *px_p;
- dev_info_t *dip;
- struct devctl_iocdata *dcp;
- uint_t bus_state;
- int rv = DDI_SUCCESS;
- int minor = getminor(dev);
-
- px_p = PX_DEV_TO_SOFTSTATE(dev);
+ px_t *px_p = PX_DEV_TO_SOFTSTATE(dev);
+ int minor = getminor(dev);
+ dev_info_t *dip;
+ int rv = ENOTTY;
+
if (px_p == NULL)
return (ENXIO);
@@ -186,13 +187,11 @@ px_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp)
}
#endif /* PX_DMA_TEST */
- switch (PCIHP_AP_MINOR_NUM_TO_PCI_DEVNUM(minor)) {
-
+ switch (PCI_MINOR_NUM_TO_PCI_DEVNUM(minor)) {
/*
* PCI tools.
*/
case PCI_TOOL_REG_MINOR_NUM:
-
switch (cmd) {
case PCITOOL_DEVICE_SET_REG:
case PCITOOL_DEVICE_GET_REG:
@@ -220,9 +219,7 @@ px_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp)
rv = ENOTTY;
}
return (rv);
-
case PCI_TOOL_INTR_MINOR_NUM:
-
switch (cmd) {
case PCITOOL_DEVICE_SET_INTR:
@@ -243,11 +240,9 @@ px_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp)
rv = ENOTTY;
}
return (rv);
-
default:
- if (px_p->px_dev_caps & PX_HOTPLUG_CAPABLE)
- return ((pcihp_get_cb_ops())->cb_ioctl(dev, cmd,
- arg, mode, credp, rvalp));
+ /* To handle devctl and hotplug related ioctls */
+ rv = pcie_ioctl(dip, dev, cmd, arg, mode, credp, rvalp);
break;
}
@@ -263,72 +258,5 @@ px_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp)
return (px_lib_pmctl(cmd, px_p));
}
- /*
- * 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(dip, 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:
- DBG(DBG_IOCTL, dip, "DEVCTL_DEVICE_RESET\n");
- rv = ENOTSUP;
- break;
-
-
- case DEVCTL_BUS_QUIESCE:
- DBG(DBG_IOCTL, dip, "DEVCTL_BUS_QUIESCE\n");
- if (ndi_get_bus_state(dip, &bus_state) == NDI_SUCCESS)
- if (bus_state == BUS_QUIESCED)
- break;
- (void) ndi_set_bus_state(dip, BUS_QUIESCED);
- break;
-
- case DEVCTL_BUS_UNQUIESCE:
- DBG(DBG_IOCTL, dip, "DEVCTL_BUS_UNQUIESCE\n");
- if (ndi_get_bus_state(dip, &bus_state) == NDI_SUCCESS)
- if (bus_state == BUS_ACTIVE)
- break;
- (void) ndi_set_bus_state(dip, BUS_ACTIVE);
- break;
-
- case DEVCTL_BUS_RESET:
- DBG(DBG_IOCTL, dip, "DEVCTL_BUS_RESET\n");
- rv = ENOTSUP;
- break;
-
- case DEVCTL_BUS_RESETALL:
- DBG(DBG_IOCTL, dip, "DEVCTL_BUS_RESETALL\n");
- rv = ENOTSUP;
- break;
-
- default:
- rv = ENOTTY;
- }
-
- ndi_dc_freehdl(dcp);
return (rv);
}
-
-static int px_prop_op(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op,
- int flags, char *name, caddr_t valuep, int *lengthp)
-{
- if (ddi_prop_exists(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
- "hotplug-capable"))
- 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));
-}
diff --git a/usr/src/uts/sun4/io/px/px_fm.c b/usr/src/uts/sun4/io/px/px_fm.c
index 958c18f42b..fe98ff1d26 100644
--- a/usr/src/uts/sun4/io/px/px_fm.c
+++ b/usr/src/uts/sun4/io/px/px_fm.c
@@ -93,9 +93,6 @@ px_fm_attach(px_t *px_p)
mutex_init(&px_p->px_fm_mutex, NULL, MUTEX_DRIVER,
(void *)px_p->px_fm_ibc);
-
- pcie_rc_init_bus(dip);
-
px_p->px_pfd_idx = 0;
for (i = 0; i < 5; i++)
pcie_rc_init_pfd(dip, &px_p->px_pfd_arr[i]);
@@ -126,7 +123,6 @@ px_fm_detach(px_t *px_p)
ddi_fm_fini(px_p->px_dip);
for (i = 0; i < 5; i++)
pcie_rc_fini_pfd(&px_p->px_pfd_arr[i]);
- pcie_rc_fini_bus(px_p->px_dip);
}
/*
@@ -260,7 +256,7 @@ px_bus_exit(dev_info_t *dip, ddi_acc_handle_t handle)
}
static uint64_t
-px_in_addr_range(dev_info_t *dip, px_ranges_t *ranges_p, uint64_t addr)
+px_in_addr_range(dev_info_t *dip, pci_ranges_t *ranges_p, uint64_t addr)
{
uint64_t addr_low, addr_high;
@@ -294,7 +290,7 @@ px_fm_callback(dev_info_t *dip, ddi_fm_error_t *derr, const void *impl_data)
uint64_t addr, base_addr;
uint64_t fault_addr = (uint64_t)derr->fme_bus_specific;
pcie_req_id_t bdf = PCIE_INVALID_BDF;
- px_ranges_t *ranges_p;
+ pci_ranges_t *ranges_p;
int range_len;
/*
@@ -316,7 +312,7 @@ px_fm_callback(dev_info_t *dip, ddi_fm_error_t *derr, const void *impl_data)
* Make sure this failed load came from this PCIe port. Check by
* matching the upper 32 bits of the address with the ranges property.
*/
- range_len = px_p->px_ranges_length / sizeof (px_ranges_t);
+ range_len = px_p->px_ranges_length / sizeof (pci_ranges_t);
i = 0;
for (ranges_p = px_p->px_ranges_p; i < range_len; i++, ranges_p++) {
base_addr = px_in_addr_range(dip, ranges_p, fault_addr);
@@ -797,7 +793,7 @@ px_err_pio_hdl_check(dev_info_t *dip, const void *handle, const void *arg1,
{
dev_info_t *px_dip = PCIE_DIP2BUS(dip)->bus_rp_dip;
px_t *px_p = INST_TO_STATE(ddi_get_instance(px_dip));
- px_ranges_t *ranges_p;
+ pci_ranges_t *ranges_p;
int range_len;
ddi_acc_handle_t ap = (ddi_acc_handle_t)handle;
ddi_acc_hdl_t *hp = impl_acc_hdl_get(ap);
@@ -812,7 +808,7 @@ px_err_pio_hdl_check(dev_info_t *dip, const void *handle, const void *arg1,
/* Normalize the base addr to the addr and strip off the HB info. */
base_addr = (hp->ah_pfn << MMU_PAGESHIFT) + hp->ah_offset;
- range_len = px_p->px_ranges_length / sizeof (px_ranges_t);
+ range_len = px_p->px_ranges_length / sizeof (pci_ranges_t);
i = 0;
for (ranges_p = px_p->px_ranges_p; i < range_len; i++, ranges_p++) {
range_addr = px_in_addr_range(dip, ranges_p, base_addr);
diff --git a/usr/src/uts/sun4/io/px/px_msi.c b/usr/src/uts/sun4/io/px/px_msi.c
index 2f84fdd986..996b3a9288 100644
--- a/usr/src/uts/sun4/io/px/px_msi.c
+++ b/usr/src/uts/sun4/io/px/px_msi.c
@@ -107,23 +107,8 @@ px_msi_detach(px_t *px_p)
DBG(DBG_MSIQ, dip, "px_msi_detach\n");
- if (msi_state_p->msi_pool_p) {
+ if (msi_state_p->msi_pool_p)
(void) ndi_irm_destroy(msi_state_p->msi_pool_p);
- }
-
- if (msi_state_p->msi_addr64 && msi_state_p->msi_mem_flg) {
- ndi_ra_free(dip, msi_state_p->msi_addr64,
- msi_state_p->msi_addr64_len,
- NDI_RA_TYPE_MEM, NDI_RA_PASS);
- }
-
- if (msi_state_p->msi_addr32 && msi_state_p->msi_mem_flg) {
- ndi_ra_free(dip, msi_state_p->msi_addr32,
- msi_state_p->msi_addr32_len,
- NDI_RA_TYPE_MEM, NDI_RA_PASS);
-
- pci_resource_destroy(dip);
- }
if (msi_state_p->msi_p) {
kmem_free(msi_state_p->msi_p,
@@ -336,50 +321,45 @@ px_msi_get_msinum(px_t *px_p, dev_info_t *rdip, int inum, msinum_t *msi_num_p)
static int
px_msi_get_props(px_t *px_p)
{
- dev_info_t *dip = px_p->px_dip;
- px_msi_state_t *msi_state_p = &px_p->px_ib_p->ib_msi_state;
- int ret = DDI_SUCCESS;
- int length = sizeof (int);
- int *valuep = NULL;
- uint64_t msi_addr_hi, msi_addr_lo;
- uint64_t mem_answer, mem_alen;
- ndi_ra_request_t request;
+ dev_info_t *dip = px_p->px_dip;
+ px_msi_state_t *msi_state_p = &px_p->px_ib_p->ib_msi_state;
+ int length = sizeof (int);
+ int *valuep = NULL;
+ uint64_t msi_addr_hi, msi_addr_lo;
DBG(DBG_MSIQ, dip, "px_msi_get_props\n");
/* #msi */
msi_state_p->msi_cnt = ddi_getprop(DDI_DEV_T_ANY, dip,
- DDI_PROP_DONTPASS, "#msi", PX_DEFAULT_MSI_CNT);
+ DDI_PROP_DONTPASS, "#msi", 0);
- DBG(DBG_MSIQ, dip, "obp: #msi=%d\n",
- msi_state_p->msi_cnt);
+ DBG(DBG_MSIQ, dip, "#msi=%d\n", msi_state_p->msi_cnt);
+ if (msi_state_p->msi_cnt == 0)
+ return (DDI_FAILURE);
/* msi-ranges: msi# field */
- ret = ddi_prop_op(DDI_DEV_T_ANY, dip, PROP_LEN_AND_VAL_ALLOC,
- DDI_PROP_DONTPASS, "msi-ranges", (caddr_t)&valuep, &length);
+ if (ddi_prop_op(DDI_DEV_T_ANY, dip, PROP_LEN_AND_VAL_ALLOC,
+ DDI_PROP_DONTPASS, "msi-ranges", (caddr_t)&valuep, &length)
+ != DDI_PROP_SUCCESS)
+ return (DDI_FAILURE);
- if (ret == DDI_PROP_SUCCESS) {
- msi_state_p->msi_1st_msinum =
- ((px_msi_ranges_t *)valuep)->msi_no;
- kmem_free(valuep, (size_t)length);
- } else
- msi_state_p->msi_1st_msinum = PX_DEFAULT_MSI_1ST_MSINUM;
+ msi_state_p->msi_1st_msinum = ((px_msi_ranges_t *)valuep)->msi_no;
+ kmem_free(valuep, (size_t)length);
- DBG(DBG_MSIQ, dip, "obp: msi_1st_msinum=%d\n",
- msi_state_p->msi_1st_msinum);
+ DBG(DBG_MSIQ, dip, "msi_1st_msinum=%d\n", msi_state_p->msi_1st_msinum);
/* msi-data-mask */
msi_state_p->msi_data_mask = ddi_getprop(DDI_DEV_T_ANY, dip,
- DDI_PROP_DONTPASS, "msi-data-mask", PX_DEFAULT_MSI_DATA_MASK);
+ DDI_PROP_DONTPASS, "msi-data-mask", 0);
- DBG(DBG_MSIQ, dip, "obp: msi-data-mask=0x%x\n",
+ DBG(DBG_MSIQ, dip, "msi-data-mask=0x%x\n",
msi_state_p->msi_data_mask);
/* msi-data-width */
msi_state_p->msi_data_width = ddi_getprop(DDI_DEV_T_ANY, dip,
- DDI_PROP_DONTPASS, "msix-data-width", PX_DEFAULT_MSI_DATA_WIDTH);
+ DDI_PROP_DONTPASS, "msix-data-width", 0);
- DBG(DBG_MSIQ, dip, "obp: msix-data-width=%d\n",
+ DBG(DBG_MSIQ, dip, "msix-data-width=%d\n",
msi_state_p->msi_data_width);
/*
@@ -389,127 +369,31 @@ px_msi_get_props(px_t *px_p)
msi_state_p->msi_type = DDI_INTR_TYPE_MSI;
if (msi_state_p->msi_data_width == PX_MSIX_WIDTH)
msi_state_p->msi_type |= DDI_INTR_TYPE_MSIX;
- }
-
- /* msi-address-ranges */
- ret = ddi_prop_op(DDI_DEV_T_ANY, dip, PROP_LEN_AND_VAL_ALLOC,
- DDI_PROP_DONTPASS, "msi-address-ranges", (caddr_t)&valuep,
- &length);
-
- if (ret == DDI_PROP_SUCCESS) {
- msi_addr_hi =
- ((px_msi_address_ranges_t *)valuep)->msi_addr32_hi;
- msi_addr_lo =
- ((px_msi_address_ranges_t *)valuep)->msi_addr32_lo;
- msi_state_p->msi_addr32 =
- (msi_addr_hi << 32) | msi_addr_lo;
-
- msi_state_p->msi_addr32_len =
- ((px_msi_address_ranges_t *)valuep)->msi_addr32_len;
-
- msi_addr_hi =
- ((px_msi_address_ranges_t *)valuep)->msi_addr64_hi;
- msi_addr_lo =
- ((px_msi_address_ranges_t *)valuep)->msi_addr64_lo;
- msi_state_p->msi_addr64 =
- (msi_addr_hi << 32) | msi_addr_lo;
-
- msi_state_p->msi_addr64_len =
- ((px_msi_address_ranges_t *)valuep)->msi_addr64_len;
-
- kmem_free(valuep, (size_t)length);
-
- msi_state_p->msi_mem_flg = B_FALSE;
-
- DBG(DBG_MSIQ, dip, "obp: msi_addr32=0x%llx\n",
- msi_state_p->msi_addr32);
-
- DBG(DBG_MSIQ, dip, "obp: msi_addr64=0x%llx\n",
- msi_state_p->msi_addr64);
-
- return (ret);
- }
-
- /*
- * If msi-address-ranges property does not exist in OBP, Fire
- * driver will need to allocate memory.
- *
- * Allocate 64KB of memory from unused PCI-E address space for the MSI
- * transactions and program MSI 32-bit address register.
- *
- * This register is used by the Fire hardware to compare against the
- * address of incoming PCI-E 32-bit addressed memory write commands.
- * If the address matches bits 31:16 then PCI-E command is considered
- * to be MSI transaction.
- *
- * pci_resource_setup() is called in context of PCI hotplug
- * initialization.
- *
- * Setup resource maps for this bus node.
- */
- if (pci_resource_setup(dip) != NDI_SUCCESS) {
- DBG(DBG_MSIQ, dip, "px_msi_getprops: dip=%s%d"
- "pci_resource_setup failed\n",
- ddi_driver_name(dip), ddi_get_instance(dip));
-
+ } else {
return (DDI_FAILURE);
}
- msi_state_p->msi_mem_flg = B_TRUE;
-
- /*
- * Reserve PCI MEM 32 resources to perform 32 bit MSI transactions.
- */
- bzero((caddr_t)&request, sizeof (ndi_ra_request_t));
- request.ra_flags = NDI_RA_ALLOC_BOUNDED;
- request.ra_boundbase = 0;
- request.ra_boundlen = PX_MSI_4GIG_LIMIT;
- request.ra_len = PX_MSI_ADDR_LEN;
- request.ra_align_mask = 0;
-
- if (ndi_ra_alloc(dip, &request, &mem_answer, &mem_alen,
- NDI_RA_TYPE_MEM, NDI_RA_PASS) != NDI_SUCCESS) {
- DBG(DBG_MSIQ, dip, "px_msi_getprops: Failed to allocate "
- "64KB mem\n");
-
+ /* msi-address-ranges */
+ if (ddi_prop_op(DDI_DEV_T_ANY, dip, PROP_LEN_AND_VAL_ALLOC,
+ DDI_PROP_DONTPASS, "msi-address-ranges", (caddr_t)&valuep, &length)
+ != DDI_PROP_SUCCESS)
return (DDI_FAILURE);
- }
-
- msi_state_p->msi_addr32 = mem_answer;
- msi_state_p->msi_addr32_len = mem_alen;
-
- DBG(DBG_MSIQ, dip, "px_msi_getprops: 32 Addr 0x%llx\n",
- msi_state_p->msi_addr32);
- /*
- * Reserve PCI MEM 64 resources to perform 64 bit MSI transactions.
- *
- * NOTE:
- *
- * Currently OBP do not export any "available" property or range in
- * the MEM64 space. Hence ndi_ra_alloc() request will return failure.
- * So, for time being ignore this failure.
- */
- bzero((caddr_t)&request, sizeof (ndi_ra_request_t));
- request.ra_flags = NDI_RA_ALLOC_BOUNDED;
- request.ra_boundbase = PX_MSI_4GIG_LIMIT + 1;
- request.ra_boundlen = PX_MSI_4GIG_LIMIT;
- request.ra_len = PX_MSI_ADDR_LEN;
- request.ra_align_mask = 0;
-
- if (ndi_ra_alloc(dip, &request, &mem_answer, &mem_alen,
- NDI_RA_TYPE_MEM, NDI_RA_PASS) != NDI_SUCCESS) {
- DBG(DBG_MSIQ, dip, "px_msi_getprops: Failed to allocate "
- "64KB mem\n");
-
- return (DDI_SUCCESS);
- }
+ msi_addr_hi = ((px_msi_address_ranges_t *)valuep)->msi_addr32_hi;
+ msi_addr_lo = ((px_msi_address_ranges_t *)valuep)->msi_addr32_lo;
+ msi_state_p->msi_addr32 = (msi_addr_hi << 32) | msi_addr_lo;
+ msi_state_p->msi_addr32_len =
+ ((px_msi_address_ranges_t *)valuep)->msi_addr32_len;
- msi_state_p->msi_addr64 = mem_answer;
- msi_state_p->msi_addr64_len = mem_alen;
+ msi_addr_hi = ((px_msi_address_ranges_t *)valuep)->msi_addr64_hi;
+ msi_addr_lo = ((px_msi_address_ranges_t *)valuep)->msi_addr64_lo;
+ msi_state_p->msi_addr64 = (msi_addr_hi << 32) | msi_addr_lo;
+ msi_state_p->msi_addr64_len =
+ ((px_msi_address_ranges_t *)valuep)->msi_addr64_len;
- DBG(DBG_MSIQ, dip, "px_msi_getprops: 64 Addr 0x%llx\n",
- msi_state_p->msi_addr64);
+ DBG(DBG_MSIQ, dip, "msi_addr32=0x%llx\n", msi_state_p->msi_addr32);
+ DBG(DBG_MSIQ, dip, "msi_addr64=0x%llx\n", msi_state_p->msi_addr64);
+ kmem_free(valuep, (size_t)length);
return (DDI_SUCCESS);
}
diff --git a/usr/src/uts/sun4/io/px/px_msi.h b/usr/src/uts/sun4/io/px/px_msi.h
index 9d68c10e52..8ccbe28565 100644
--- a/usr/src/uts/sun4/io/px/px_msi.h
+++ b/usr/src/uts/sun4/io/px/px_msi.h
@@ -88,17 +88,6 @@ typedef struct px_msi_address_ranges {
#define PX_MSI_WIDTH 16
#define PX_MSIX_WIDTH 32
-/*
- * Default MSI configurations
- */
-#define PX_DEFAULT_MSI_CNT 256
-#define PX_DEFAULT_MSI_1ST_MSINUM 0
-#define PX_DEFAULT_MSI_DATA_MASK 0xff
-#define PX_DEFAULT_MSI_DATA_WIDTH PX_MSIX_WIDTH
-
-#define PX_MSI_4GIG_LIMIT 0xFFFFFFFFUL
-#define PX_MSI_ADDR_LEN 0x10000 /* 64K bytes */
-
extern int px_msi_attach(px_t *px_p);
extern void px_msi_detach(px_t *px_p);
diff --git a/usr/src/uts/sun4/io/px/px_msiq.c b/usr/src/uts/sun4/io/px/px_msiq.c
index 7717f98b6f..c8b38803c5 100644
--- a/usr/src/uts/sun4/io/px/px_msiq.c
+++ b/usr/src/uts/sun4/io/px/px_msiq.c
@@ -40,7 +40,7 @@
#include <sys/ddi_impldefs.h>
#include "px_obj.h"
-static void px_msiq_get_props(px_t *px_p);
+static int px_msiq_get_props(px_t *px_p);
/*
* px_msiq_attach()
@@ -60,7 +60,8 @@ px_msiq_attach(px_t *px_p)
*
* Avaialble MSIQs and its properties.
*/
- px_msiq_get_props(px_p);
+ if (px_msiq_get_props(px_p) != DDI_SUCCESS)
+ return (DDI_FAILURE);
/*
* 10% of available MSIQs are reserved for the PCIe messages.
@@ -406,49 +407,63 @@ px_devino_to_msiqid(px_t *px_p, devino_t devino)
/*
* px_msiq_get_props()
*/
-static void
+static int
px_msiq_get_props(px_t *px_p)
{
- px_msiq_state_t *msiq_state_p = &px_p->px_ib_p->ib_msiq_state;
- int ret = DDI_SUCCESS;
- int length = sizeof (int);
- char *valuep = NULL;
+ px_msiq_state_t *msiq_state_p = &px_p->px_ib_p->ib_msiq_state;
+ int length = sizeof (int);
+ char *valuep = NULL;
+ int ret;
DBG(DBG_MSIQ, px_p->px_dip, "px_msiq_get_props\n");
/* #msi-eqs */
msiq_state_p->msiq_cnt = ddi_getprop(DDI_DEV_T_ANY, px_p->px_dip,
- DDI_PROP_DONTPASS, "#msi-eqs", PX_DEFAULT_MSIQ_CNT);
+ DDI_PROP_DONTPASS, "#msi-eqs", 0);
- DBG(DBG_MSIQ, px_p->px_dip, "obp: msiq_cnt=%d\n",
- msiq_state_p->msiq_cnt);
+ DBG(DBG_MSIQ, px_p->px_dip, "msiq_cnt=%d\n", msiq_state_p->msiq_cnt);
/* msi-eq-size */
msiq_state_p->msiq_rec_cnt = ddi_getprop(DDI_DEV_T_ANY, px_p->px_dip,
- DDI_PROP_DONTPASS, "msi-eq-size", PX_DEFAULT_MSIQ_REC_CNT);
+ DDI_PROP_DONTPASS, "msi-eq-size", 0);
- DBG(DBG_MSIQ, px_p->px_dip, "obp: msiq_rec_cnt=%d\n",
+ DBG(DBG_MSIQ, px_p->px_dip, "msiq_rec_cnt=%d\n",
msiq_state_p->msiq_rec_cnt);
+ if ((msiq_state_p->msiq_cnt == 0) || (msiq_state_p->msiq_rec_cnt == 0))
+ return (DDI_FAILURE);
+
/* msi-eq-to-devino: msi-eq#, devino# fields */
ret = ddi_prop_op(DDI_DEV_T_ANY, px_p->px_dip, PROP_LEN_AND_VAL_ALLOC,
- DDI_PROP_DONTPASS, "msi-eq-to-devino", (caddr_t)&valuep,
- &length);
-
- if (ret == DDI_PROP_SUCCESS) {
- msiq_state_p->msiq_1st_msiq_id =
- ((px_msi_eq_to_devino_t *)valuep)->msi_eq_no;
- msiq_state_p->msiq_1st_devino =
- ((px_msi_eq_to_devino_t *)valuep)->devino_no;
- kmem_free(valuep, (size_t)length);
- } else {
- msiq_state_p->msiq_1st_msiq_id = PX_DEFAULT_MSIQ_1ST_MSIQ_ID;
- msiq_state_p->msiq_1st_devino = PX_DEFAULT_MSIQ_1ST_DEVINO;
+ DDI_PROP_DONTPASS, "msi-eq-to-devino", (caddr_t)&valuep, &length);
+
+ /*
+ * NOTE:
+ * On sun4u PCIe systems, the msi-eq-to-devino property is broken and
+ * these systems defines this property as msi-eq-devino.
+ */
+ if (ret == DDI_PROP_NOT_FOUND) {
+ DBG(DBG_MSIQ, px_p->px_dip, "msi-eq-to-devino is not found\n");
+ ret = ddi_prop_op(DDI_DEV_T_ANY, px_p->px_dip,
+ PROP_LEN_AND_VAL_ALLOC, DDI_PROP_DONTPASS, "msi-eq-devino",
+ (caddr_t)&valuep, &length);
+ }
+
+ if (ret != DDI_PROP_SUCCESS) {
+ return (DDI_FAILURE);
}
- DBG(DBG_MSIQ, px_p->px_dip, "obp: msiq_1st_msiq_id=%d\n",
+ msiq_state_p->msiq_1st_msiq_id =
+ ((px_msi_eq_to_devino_t *)valuep)->msi_eq_no;
+ msiq_state_p->msiq_1st_devino =
+ ((px_msi_eq_to_devino_t *)valuep)->devino_no;
+
+ DBG(DBG_MSIQ, px_p->px_dip, "msiq_1st_msiq_id=%d\n",
msiq_state_p->msiq_1st_msiq_id);
- DBG(DBG_MSIQ, px_p->px_dip, "obp: msiq_1st_devino=%d\n",
+ DBG(DBG_MSIQ, px_p->px_dip, "msiq_1st_devino=%d\n",
msiq_state_p->msiq_1st_devino);
+
+ kmem_free(valuep, (size_t)length);
+ return (DDI_SUCCESS);
}
diff --git a/usr/src/uts/sun4/io/px/px_msiq.h b/usr/src/uts/sun4/io/px/px_msiq.h
index e9213f1a49..2a44a86efe 100644
--- a/usr/src/uts/sun4/io/px/px_msiq.h
+++ b/usr/src/uts/sun4/io/px/px_msiq.h
@@ -81,14 +81,6 @@ typedef struct px_msi_eq_to_devino {
int devino_no;
} px_msi_eq_to_devino_t;
-/*
- * Default MSIQ Configurations
- */
-#define PX_DEFAULT_MSIQ_CNT 36
-#define PX_DEFAULT_MSIQ_REC_CNT 128
-#define PX_DEFAULT_MSIQ_1ST_MSIQ_ID 0
-#define PX_DEFAULT_MSIQ_1ST_DEVINO 24
-
extern int px_msiq_attach(px_t *px_p);
extern void px_msiq_detach(px_t *px_p);
extern void px_msiq_resume(px_t *px_p);
diff --git a/usr/src/uts/sun4/io/px/px_obj.h b/usr/src/uts/sun4/io/px/px_obj.h
index 93a9e6a98b..84965e2470 100644
--- a/usr/src/uts/sun4/io/px/px_obj.h
+++ b/usr/src/uts/sun4/io/px/px_obj.h
@@ -19,20 +19,17 @@
* 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_PX_OBJ_H
#define _SYS_PX_OBJ_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#ifdef __cplusplus
extern "C" {
#endif
-#include <sys/pcie.h>
#include <sys/pcie_impl.h>
#include <sys/pci_impl.h>
#include <sys/fm/io/sun4_fire.h>
diff --git a/usr/src/uts/sun4/io/px/px_pec.c b/usr/src/uts/sun4/io/px/px_pec.c
index edc11213a9..c0efc94eae 100644
--- a/usr/src/uts/sun4/io/px/px_pec.c
+++ b/usr/src/uts/sun4/io/px/px_pec.c
@@ -54,9 +54,9 @@ px_pec_attach(px_t *px_p)
{
px_pec_t *pec_p;
int i, len;
- int nrange = px_p->px_ranges_length / sizeof (px_ranges_t);
+ int nrange = px_p->px_ranges_length / sizeof (pci_ranges_t);
dev_info_t *dip = px_p->px_dip;
- px_ranges_t *rangep = px_p->px_ranges_p;
+ pci_ranges_t *rangep = px_p->px_ranges_p;
int ret;
/*
diff --git a/usr/src/uts/sun4/io/px/px_space.c b/usr/src/uts/sun4/io/px/px_space.c
index 46474986c6..73737a0714 100644
--- a/usr/src/uts/sun4/io/px/px_space.c
+++ b/usr/src/uts/sun4/io/px/px_space.c
@@ -32,7 +32,6 @@
#include <sys/sunddi.h>
#include <sys/cmn_err.h>
#include <sys/time.h>
-#include <sys/pcie.h>
#include "px_obj.h"
/*LINTLIBRARY*/
diff --git a/usr/src/uts/sun4/io/px/px_tools.c b/usr/src/uts/sun4/io/px/px_tools.c
index 4027f0232b..8d424f7957 100644
--- a/usr/src/uts/sun4/io/px/px_tools.c
+++ b/usr/src/uts/sun4/io/px/px_tools.c
@@ -28,7 +28,6 @@
#include <sys/cpuvar.h>
#include <sys/kmem.h>
#include <sys/sunddi.h>
-#include <sys/hotplug/pci/pcihp.h>
#include "px_obj.h"
#include <sys/pci_tools.h>
#include "px_tools_ext.h"
@@ -774,13 +773,13 @@ pxtool_init(dev_info_t *dip)
int instance = ddi_get_instance(dip);
if (ddi_create_minor_node(dip, PCI_MINOR_REG, S_IFCHR,
- PCIHP_AP_MINOR_NUM(instance, PCI_TOOL_REG_MINOR_NUM),
+ PCI_MINOR_NUM(instance, PCI_TOOL_REG_MINOR_NUM),
DDI_NT_REGACC, 0) != DDI_SUCCESS) {
return (DDI_FAILURE);
}
if (ddi_create_minor_node(dip, PCI_MINOR_INTR, S_IFCHR,
- PCIHP_AP_MINOR_NUM(instance, PCI_TOOL_INTR_MINOR_NUM),
+ PCI_MINOR_NUM(instance, PCI_TOOL_INTR_MINOR_NUM),
DDI_NT_INTRCTL, 0) != DDI_SUCCESS) {
ddi_remove_minor_node(dip, PCI_MINOR_REG);
return (DDI_FAILURE);
diff --git a/usr/src/uts/sun4/io/px/px_tools_ext.h b/usr/src/uts/sun4/io/px/px_tools_ext.h
index bab94f06eb..83a442fa80 100644
--- a/usr/src/uts/sun4/io/px/px_tools_ext.h
+++ b/usr/src/uts/sun4/io/px/px_tools_ext.h
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -20,28 +19,18 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#ifndef _SYS_PX_TOOLS_EXT_H
#define _SYS_PX_TOOLS_EXT_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#ifdef __cplusplus
extern "C" {
#endif
-/*
- * Minor numbers for dedicated pcitool nodes.
- * Note that FF and FE minor numbers are used for other minor nodes.
- */
-#define PCI_TOOL_REG_MINOR_NUM 0xFD
-#define PCI_TOOL_INTR_MINOR_NUM 0xFC
-
/* Stuff exported by px_tools.c and px_tools_4[u/v].c */
-
int pxtool_dev_reg_ops(dev_info_t *dip, void *arg, int cmd, int mode);
int pxtool_bus_reg_ops(dev_info_t *dip, void *arg, int cmd, int mode);
int pxtool_intr(dev_info_t *dip, void *arg, int cmd, int mode);
diff --git a/usr/src/uts/sun4/io/px/px_util.c b/usr/src/uts/sun4/io/px/px_util.c
index 7b951d2c5d..570dc26bd4 100644
--- a/usr/src/uts/sun4/io/px/px_util.c
+++ b/usr/src/uts/sun4/io/px/px_util.c
@@ -170,6 +170,7 @@ px_reloc_reg(dev_info_t *dip, dev_info_t *rdip, px_t *px_p,
i = ddi_getlongprop(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS,
"assigned-addresses", (caddr_t)&assign_p, &assign_len);
+
if (i) {
DBG(DBG_MAP | DBG_CONT, dip, "%s%d: assigned-addresses %d\n",
ddi_driver_name(rdip), ddi_get_instance(rdip), i);
@@ -210,8 +211,8 @@ int
px_xlate_reg(px_t *px_p, pci_regspec_t *px_rp, struct regspec *new_rp)
{
int n;
- px_ranges_t *rng_p = px_p->px_ranges_p;
- int rng_n = px_p->px_ranges_length / sizeof (px_ranges_t);
+ pci_ranges_t *rng_p = px_p->px_ranges_p;
+ int rng_n = px_p->px_ranges_length / sizeof (pci_ranges_t);
uint32_t space_type = PCI_REG_ADDR_G(px_rp->pci_phys_hi);
uint64_t reg_begin, reg_end, reg_sz;
uint64_t rng_begin, rng_end, rng_sz;
@@ -631,8 +632,8 @@ uint64_t
px_get_cfg_pabase(px_t *px_p)
{
int i;
- px_ranges_t *rangep = px_p->px_ranges_p;
- int nrange = px_p->px_ranges_length / sizeof (px_ranges_t);
+ pci_ranges_t *rangep = px_p->px_ranges_p;
+ int nrange = px_p->px_ranges_length / sizeof (pci_ranges_t);
uint32_t cfg_space_type = PCI_REG_ADDR_G(PCI_ADDR_CONFIG);
ASSERT(cfg_space_type == 0);
diff --git a/usr/src/uts/sun4/io/px/px_util.h b/usr/src/uts/sun4/io/px/px_util.h
index 0d186360e9..c1cfe1534c 100644
--- a/usr/src/uts/sun4/io/px/px_util.h
+++ b/usr/src/uts/sun4/io/px/px_util.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_PX_UTIL_H
#define _SYS_PX_UTIL_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#ifdef __cplusplus
extern "C" {
#endif
@@ -52,7 +50,7 @@ extern int px_reloc_reg(dev_info_t *dip, dev_info_t *rdip, px_t *px_p,
extern int px_xlate_reg(px_t *px_p, pci_regspec_t *pci_rp,
struct regspec *new_rp);
extern int px_search_ranges(px_t *px_p, uint32_t space_type, uint32_t reg_begin,
- uint32_t reg_end, px_ranges_t **sel_rng_p, uint_t *base_offset_p);
+ uint32_t reg_end, pci_ranges_t **sel_rng_p, uint_t *base_offset_p);
/* bus add intrspec */
extern off_t px_get_reg_set_size(dev_info_t *child, int rnumber);
diff --git a/usr/src/uts/sun4/io/px/px_var.h b/usr/src/uts/sun4/io/px/px_var.h
index ba395d8b9d..3d52484c1f 100644
--- a/usr/src/uts/sun4/io/px/px_var.h
+++ b/usr/src/uts/sun4/io/px/px_var.h
@@ -19,15 +19,13 @@
* CDDL HEADER END
*/
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#ifndef _SYS_PX_VAR_H
#define _SYS_PX_VAR_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/callb.h>
#ifdef __cplusplus
@@ -47,29 +45,6 @@ extern "C" {
/*
* The following typedef is used to represent a
- * 1275 "bus-range" property of a PCI Bus node.
- */
-typedef struct px_bus_range {
- uint32_t lo;
- uint32_t hi;
-} px_bus_range_t;
-
-/*
- * The following typedef is used to represent an entry in the "ranges"
- * property of a device node.
- */
-typedef struct px_ranges {
- uint32_t child_high;
- uint32_t child_mid;
- uint32_t child_low;
- uint32_t parent_high;
- uint32_t parent_low;
- uint32_t size_high;
- uint32_t size_low;
-} px_ranges_t;
-
-/*
- * The following typedef is used to represent a
* 1275 "reg" property of a PCI nexus.
*/
typedef struct px_nexus_regspec {
@@ -99,7 +74,6 @@ struct px {
*/
px_state_t px_state;
uint_t px_soft_state;
- uint_t px_open_count;
kmutex_t px_mutex;
/*
@@ -115,8 +89,8 @@ struct px {
* px device node properties:
*/
pcie_req_id_t px_bdf;
- px_bus_range_t px_bus_range; /* "bus-range" */
- px_ranges_t *px_ranges_p; /* "ranges" data & length */
+ pci_bus_range_t px_bus_range; /* "bus-range" */
+ pci_ranges_t *px_ranges_p; /* "ranges" data & length */
int px_ranges_length;
devino_t *px_inos; /* inos from "interrupts" prop */
int px_inos_len; /* "interrupts" length */
@@ -154,14 +128,8 @@ struct px {
ddi_softint_handle_t px_dbg_hdl; /* HDL for dbg printing */
};
-/* px soft state flag */
-#define PX_SOFT_STATE_OPEN 1
-#define PX_SOFT_STATE_OPEN_EXCL 2
-#define PX_SOFT_STATE_CLOSED 4
-
/* px_dev_caps definition */
#define PX_BYPASS_DMA_ALLOWED 1
-#define PX_HOTPLUG_CAPABLE 2
#define PX_DMA_SYNC_REQUIRED 4
/* px_pm_flags definitions used with interrupts and FMA code */
@@ -174,7 +142,7 @@ struct px {
#define DIP_TO_STATE(dip) INST_TO_STATE(DIP_TO_INST(dip))
#define PX_DEV_TO_SOFTSTATE(dev) ((px_t *)ddi_get_soft_state( \
- px_state_p, PCIHP_AP_MINOR_NUM_TO_INSTANCE(getminor(dev))))
+ px_state_p, PCI_MINOR_NUM_TO_INSTANCE(getminor(dev))))
extern void *px_state_p;
diff --git a/usr/src/uts/sun4u/io/pci/pci.c b/usr/src/uts/sun4u/io/pci/pci.c
index 40efc968e2..30787b7fcd 100644
--- a/usr/src/uts/sun4u/io/pci/pci.c
+++ b/usr/src/uts/sun4u/io/pci/pci.c
@@ -23,7 +23,6 @@
* Use is subject to license terms.
*/
-
/*
* PCI nexus driver interface
*/
@@ -124,9 +123,9 @@ static struct dev_ops pci_ops = {
extern struct mod_ops mod_driverops;
static struct modldrv modldrv = {
- &mod_driverops, /* Type of module - driver */
- "PCI Bus nexus driver", /* Name of module. */
- &pci_ops, /* driver ops */
+ &mod_driverops, /* Type of module - driver */
+ "Sun4u Host to PCI nexus driver", /* Name of module. */
+ &pci_ops, /* driver ops */
};
static struct modlinkage modlinkage = {
@@ -296,7 +295,6 @@ pci_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
pci_p->pci_dip = dip;
mutex_init(&pci_p->pci_mutex, NULL, MUTEX_DRIVER, NULL);
pci_p->pci_soft_state = PCI_SOFT_STATE_CLOSED;
- pci_p->pci_open_count = 0;
/*
* Get key properties of the pci bridge node and
diff --git a/usr/src/uts/sun4u/io/pci/pci_devctl.c b/usr/src/uts/sun4u/io/pci/pci_devctl.c
index 4bd413f06f..2872b8c24f 100644
--- a/usr/src/uts/sun4u/io/pci/pci_devctl.c
+++ b/usr/src/uts/sun4u/io/pci/pci_devctl.c
@@ -19,12 +19,10 @@
* 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.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
/*
* PCI nexus HotPlug devctl interface
*/
@@ -130,7 +128,6 @@ pci_open(dev_t *devp, int flags, int otyp, cred_t *credp)
}
}
- pci_p->pci_open_count++;
mutex_exit(&pci_p->pci_mutex);
return (0);
@@ -161,7 +158,6 @@ pci_close(dev_t dev, int flags, int otyp, cred_t *credp)
}
pci_p->pci_soft_state = PCI_SOFT_STATE_CLOSED;
- pci_p->pci_open_count = 0;
mutex_exit(&pci_p->pci_mutex);
return (0);
}
diff --git a/usr/src/uts/sun4u/io/pci/pci_pci.c b/usr/src/uts/sun4u/io/pci/pci_pci.c
index 9b1799feec..d2d4718fa1 100644
--- a/usr/src/uts/sun4u/io/pci/pci_pci.c
+++ b/usr/src/uts/sun4u/io/pci/pci_pci.c
@@ -23,7 +23,6 @@
* Use is subject to license terms.
*/
-
/*
* Sun4u PCI to PCI bus bridge nexus driver
*/
@@ -35,7 +34,7 @@
#include <sys/autoconf.h>
#include <sys/ddi_impldefs.h>
#include <sys/ddi_subrdefs.h>
-#include <sys/pcie.h>
+#include <sys/pci_impl.h>
#include <sys/pcie_impl.h>
#include <sys/pci_cap.h>
#include <sys/pci/pci_nexus.h>
@@ -47,6 +46,7 @@
#include <sys/ddifm.h>
#include <sys/pci/pci_pwr.h>
#include <sys/pci/pci_debug.h>
+#include <sys/hotplug/pci/pcie_hp.h>
#include <sys/hotplug/pci/pcihp.h>
#include <sys/open.h>
#include <sys/stat.h>
@@ -130,7 +130,8 @@ struct bus_ops ppb_bus_ops = {
ppb_bus_enter, /* (*bus_enter)() */
ppb_bus_exit, /* (*bus_exit)() */
ppb_bus_power, /* (*bus_power)() */
- ppb_intr_ops /* (*bus_intr_op)(); */
+ ppb_intr_ops, /* (*bus_intr_op)(); */
+ pcie_hp_common_ops /* (*bus_hp_op)(); */
};
static int ppb_open(dev_t *devp, int flags, int otyp, cred_t *credp);
@@ -164,7 +165,7 @@ static struct cb_ops ppb_cb_ops = {
static int ppb_probe(dev_info_t *);
static int ppb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd);
static int ppb_detach(dev_info_t *devi, ddi_detach_cmd_t cmd);
-static int ppb_info(dev_info_t *dip, ddi_info_cmd_t infocmd,
+static int ppb_info(dev_info_t *dip, ddi_info_cmd_t cmd,
void *arg, void **result);
static int ppb_pwr(dev_info_t *dip, int component, int level);
@@ -238,9 +239,6 @@ typedef struct {
kmutex_t ppb_mutex;
uint_t ppb_soft_state;
-#define PPB_SOFT_STATE_CLOSED 0x00
-#define PPB_SOFT_STATE_OPEN 0x01
-#define PPB_SOFT_STATE_OPEN_EXCL 0x02
int fm_cap;
ddi_iblock_cookie_t fm_ibc;
@@ -328,16 +326,18 @@ _info(struct modinfo *modinfop)
/*ARGSUSED*/
static int
-ppb_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
+ppb_info(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
{
- ppb_devstate_t *ppb_p; /* per ppb state pointer */
minor_t minor = getminor((dev_t)arg);
- int instance = PCIHP_AP_MINOR_NUM_TO_INSTANCE(minor);
-
- ppb_p = (ppb_devstate_t *)ddi_get_soft_state(ppb_state,
+ int instance = PCI_MINOR_NUM_TO_INSTANCE(minor);
+ ppb_devstate_t *ppb_p = (ppb_devstate_t *)ddi_get_soft_state(ppb_state,
instance);
- switch (infocmd) {
+
+ if (ppb_p->parent_bus != PCIE_PCIECAP_DEV_TYPE_PCIE_DEV)
+ return (pcihp_info(dip, cmd, arg, result));
+
+ switch (cmd) {
default:
return (DDI_FAILURE);
@@ -364,9 +364,12 @@ ppb_probe(register dev_info_t *devi)
static int
ppb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
{
+ dev_info_t *root = ddi_root_node();
int instance;
ppb_devstate_t *ppb;
+ dev_info_t *pdip;
ddi_acc_handle_t config_handle;
+ char *bus;
switch (cmd) {
case DDI_ATTACH:
@@ -386,7 +389,7 @@ ppb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
ppb = (ppb_devstate_t *)ddi_get_soft_state(ppb_state, instance);
ppb->dip = devi;
mutex_init(&ppb->ppb_mutex, NULL, MUTEX_DRIVER, NULL);
- ppb->ppb_soft_state = PPB_SOFT_STATE_CLOSED;
+ ppb->ppb_soft_state = PCI_SOFT_STATE_CLOSED;
if (pci_config_setup(devi, &config_handle) != DDI_SUCCESS) {
mutex_destroy(&ppb->ppb_mutex);
ddi_soft_state_free(ppb_state, instance);
@@ -435,30 +438,32 @@ ppb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
mutex_exit(&ppb->ppb_pwr_p->pwr_mutex);
}
+ ppb->parent_bus = PCIE_PCIECAP_DEV_TYPE_PCI_PSEUDO;
+ for (pdip = ddi_get_parent(ppb->dip); pdip && (pdip != root) &&
+ (ppb->parent_bus != PCIE_PCIECAP_DEV_TYPE_PCIE_DEV);
+ pdip = ddi_get_parent(pdip)) {
+ if (ddi_prop_lookup_string(DDI_DEV_T_ANY, pdip,
+ DDI_PROP_DONTPASS, "device_type", &bus) !=
+ DDI_PROP_SUCCESS)
+ break;
+
+ if (strcmp(bus, "pciex") == 0)
+ ppb->parent_bus =
+ PCIE_PCIECAP_DEV_TYPE_PCIE_DEV;
+
+ ddi_prop_free(bus);
+ }
+
/*
- * 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. This all takes place if this nexus has hot-plug
- * slots and successfully initializes Hot Plug Framework.
+ * Initialize hotplug support on this bus.
*/
- ppb->hotplug_capable = B_FALSE;
- ppb_init_hotplug(ppb);
- if (ppb->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) {
- if (ppb->ppb_pwr_p != NULL) {
- ppb_pwr_teardown(ppb, devi);
- }
- mutex_destroy(&ppb->ppb_mutex);
- ddi_soft_state_free(ppb_state, instance);
+ if (ppb->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV)
+ if (pcie_init(devi, NULL) != DDI_SUCCESS) {
+ (void) ppb_detach(devi, DDI_DETACH);
return (DDI_FAILURE);
}
- }
+ else
+ ppb_init_hotplug(ppb);
DEBUG1(DBG_ATTACH, devi,
"ppb_attach(): this nexus %s hotplug slots\n",
@@ -488,6 +493,7 @@ static int
ppb_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
{
ppb_devstate_t *ppb;
+ int ret = DDI_SUCCESS;
switch (cmd) {
case DDI_DETACH:
@@ -500,12 +506,16 @@ ppb_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
ppb_fm_fini(ppb);
- if (ppb->hotplug_capable == B_TRUE)
- if (pcihp_uninit(devi) == DDI_FAILURE)
- return (DDI_FAILURE);
+ if (ppb->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV)
+ ret = pcie_uninit(devi);
+ else if (ppb->hotplug_capable == B_TRUE)
+ ret = pcihp_init(devi);
else
ddi_remove_minor_node(devi, "devctl");
+ if (ret != DDI_SUCCESS)
+ return (DDI_FAILURE);
+
(void) ddi_prop_remove(DDI_DEV_T_NONE, devi, "device_type");
if (ppb->ppb_pwr_p != NULL) {
@@ -1428,6 +1438,8 @@ ppb_pwr(dev_info_t *dip, int component, int lvl)
static void
ppb_init_hotplug(ppb_devstate_t *ppb)
{
+ ppb->hotplug_capable = B_FALSE;
+
if (ddi_prop_exists(DDI_DEV_T_ANY, ppb->dip, DDI_PROP_DONTPASS,
"hotplug-capable")) {
(void) modload("misc", "pcihp");
@@ -1441,6 +1453,18 @@ ppb_init_hotplug(ppb_devstate_t *ppb)
ppb->hotplug_capable = B_TRUE;
}
+ if (ppb->hotplug_capable == B_FALSE) {
+ /*
+ * create minor node for devctl interfaces
+ */
+ if (ddi_create_minor_node(ppb->dip, "devctl", S_IFCHR,
+ PCI_MINOR_NUM(ddi_get_instance(ppb->dip), PCI_DEVCTL_MINOR),
+ DDI_NT_NEXUS, 0) != DDI_SUCCESS)
+ cmn_err(CE_WARN,
+ "%s #%d: Failed to create a minor node",
+ ddi_driver_name(ppb->dip),
+ ddi_get_instance(ppb->dip));
+ }
}
static void
@@ -1516,9 +1540,8 @@ ppb_create_ranges_prop(dev_info_t *dip,
static int
ppb_open(dev_t *devp, int flags, int otyp, cred_t *credp)
{
- ppb_devstate_t *ppb_p;
- minor_t minor = getminor(*devp);
- int instance = PCIHP_AP_MINOR_NUM_TO_INSTANCE(minor);
+ int instance = PCI_MINOR_NUM_TO_INSTANCE(getminor(*devp));
+ ppb_devstate_t *ppb_p = ddi_get_soft_state(ppb_state, instance);
/*
* Make sure the open is for the right file type.
@@ -1526,35 +1549,44 @@ ppb_open(dev_t *devp, int flags, int otyp, cred_t *credp)
if (otyp != OTYP_CHR)
return (EINVAL);
+ if (ppb_p == NULL)
+ return (ENXIO);
+
+ mutex_enter(&ppb_p->ppb_mutex);
+
/*
- * Get the soft state structure for the device.
+ * Ioctls will be handled by SPARC PCI Express framework for all
+ * PCIe platforms
*/
- ppb_p = (ppb_devstate_t *)ddi_get_soft_state(ppb_state,
- instance);
+ if (ppb_p->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) {
+ int rv;
- if (ppb_p == NULL)
- return (ENXIO);
+ rv = pcie_open(ppb_p->dip, devp, flags, otyp, credp);
+ mutex_exit(&ppb_p->ppb_mutex);
+
+ return (rv);
+ } else if (ppb_p->hotplug_capable == B_TRUE) {
+ mutex_exit(&ppb_p->ppb_mutex);
- if (ppb_p->hotplug_capable == B_TRUE)
- return ((pcihp_get_cb_ops())->cb_open(devp, flags,
- otyp, credp));
+ return ((pcihp_get_cb_ops())->cb_open(devp, flags, otyp,
+ credp));
+ }
/*
* Handle the open by tracking the device state.
*/
- mutex_enter(&ppb_p->ppb_mutex);
if (flags & FEXCL) {
- if (ppb_p->ppb_soft_state != PPB_SOFT_STATE_CLOSED) {
+ if (ppb_p->ppb_soft_state != PCI_SOFT_STATE_CLOSED) {
mutex_exit(&ppb_p->ppb_mutex);
return (EBUSY);
}
- ppb_p->ppb_soft_state = PPB_SOFT_STATE_OPEN_EXCL;
+ ppb_p->ppb_soft_state = PCI_SOFT_STATE_OPEN_EXCL;
} else {
- if (ppb_p->ppb_soft_state == PPB_SOFT_STATE_OPEN_EXCL) {
+ if (ppb_p->ppb_soft_state == PCI_SOFT_STATE_OPEN_EXCL) {
mutex_exit(&ppb_p->ppb_mutex);
return (EBUSY);
}
- ppb_p->ppb_soft_state = PPB_SOFT_STATE_OPEN;
+ ppb_p->ppb_soft_state = PCI_SOFT_STATE_OPEN;
}
mutex_exit(&ppb_p->ppb_mutex);
return (0);
@@ -1565,25 +1597,34 @@ ppb_open(dev_t *devp, int flags, int otyp, cred_t *credp)
static int
ppb_close(dev_t dev, int flags, int otyp, cred_t *credp)
{
- ppb_devstate_t *ppb_p;
- minor_t minor = getminor(dev);
- int instance = PCIHP_AP_MINOR_NUM_TO_INSTANCE(minor);
+ int instance = PCI_MINOR_NUM_TO_INSTANCE(getminor(dev));
+ ppb_devstate_t *ppb_p = ddi_get_soft_state(ppb_state, instance);
if (otyp != OTYP_CHR)
return (EINVAL);
- ppb_p = (ppb_devstate_t *)ddi_get_soft_state(ppb_state,
- instance);
-
if (ppb_p == NULL)
return (ENXIO);
- if (ppb_p->hotplug_capable == B_TRUE)
- return ((pcihp_get_cb_ops())->cb_close(dev, flags,
- otyp, credp));
-
mutex_enter(&ppb_p->ppb_mutex);
- ppb_p->ppb_soft_state = PPB_SOFT_STATE_CLOSED;
+ /*
+ * Ioctls will be handled by SPARC PCI Express framework for all
+ * PCIe platforms
+ */
+ if (ppb_p->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) {
+ int rv;
+
+ rv = pcie_close(ppb_p->dip, dev, flags, otyp, credp);
+ mutex_exit(&ppb_p->ppb_mutex);
+
+ return (rv);
+ } else if (ppb_p->hotplug_capable == B_TRUE) {
+ mutex_exit(&ppb_p->ppb_mutex);
+ return ((pcihp_get_cb_ops())->cb_close(dev, flags, otyp,
+ credp));
+ }
+
+ ppb_p->ppb_soft_state = PCI_SOFT_STATE_CLOSED;
mutex_exit(&ppb_p->ppb_mutex);
return (0);
}
@@ -1597,23 +1638,26 @@ static int
ppb_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
int *rvalp)
{
- ppb_devstate_t *ppb_p;
- dev_info_t *self;
+ int instance = PCI_MINOR_NUM_TO_INSTANCE(getminor(dev));
+ ppb_devstate_t *ppb_p = ddi_get_soft_state(ppb_state, instance);
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);
-
- ppb_p = (ppb_devstate_t *)ddi_get_soft_state(ppb_state,
- instance);
+ uint_t bus_state;
+ dev_info_t *self;
+ int rv = 0;
if (ppb_p == NULL)
return (ENXIO);
- if (ppb_p->hotplug_capable == B_TRUE)
- return ((pcihp_get_cb_ops())->cb_ioctl(dev, cmd,
- arg, mode, credp, rvalp));
+ /*
+ * Ioctls will be handled by SPARC PCI Express framework for all
+ * PCIe platforms
+ */
+ if (ppb_p->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV)
+ return (pcie_ioctl(ppb_p->dip, dev, cmd, arg, mode, credp,
+ rvalp));
+ else if (ppb_p->hotplug_capable == B_TRUE)
+ return ((pcihp_get_cb_ops())->cb_ioctl(dev, cmd, arg, mode,
+ credp, rvalp));
self = ppb_p->dip;
@@ -1670,24 +1714,23 @@ ppb_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
return (rv);
}
-static int ppb_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 int
+ppb_prop_op(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op, int flags,
+ char *name, caddr_t valuep, int *lengthp)
{
- ppb_devstate_t *ppb_p;
- minor_t minor = getminor(dev);
- int instance = PCIHP_AP_MINOR_NUM_TO_INSTANCE(minor);
-
- ppb_p = (ppb_devstate_t *)ddi_get_soft_state(ppb_state,
- instance);
+ int instance = PCI_MINOR_NUM_TO_INSTANCE(getminor(dev));
+ ppb_devstate_t *ppb_p = (ppb_devstate_t *)
+ ddi_get_soft_state(ppb_state, instance);
if (ppb_p == NULL)
return (ENXIO);
- if (ppb_p->hotplug_capable == B_TRUE)
- return ((pcihp_get_cb_ops())->cb_prop_op(dev, dip, prop_op,
- flags, name, valuep, lengthp));
+ if (ppb_p->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV)
+ return (pcie_prop_op(dev, dip, prop_op, flags, name,
+ valuep, lengthp));
- return (ddi_prop_op(dev, dip, prop_op, flags, name, valuep, lengthp));
+ return ((pcihp_get_cb_ops())->cb_prop_op(dev, dip, prop_op, flags,
+ name, valuep, lengthp));
}
/*
@@ -1696,10 +1739,6 @@ static int ppb_prop_op(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op,
static void
ppb_fm_init(ppb_devstate_t *ppb_p)
{
- dev_info_t *root = ddi_root_node();
- dev_info_t *pdip;
- char *bus;
-
ppb_p->fm_cap = DDI_FM_EREPORT_CAPABLE | DDI_FM_ERRCB_CAPABLE |
DDI_FM_ACCCHK_CAPABLE | DDI_FM_DMACHK_CAPABLE;
@@ -1717,21 +1756,6 @@ ppb_fm_init(ppb_devstate_t *ppb_p)
* Register error callback with our parent.
*/
ddi_fm_handler_register(ppb_p->dip, ppb_err_callback, NULL);
-
- ppb_p->parent_bus = PCIE_PCIECAP_DEV_TYPE_PCI_PSEUDO;
- for (pdip = ddi_get_parent(ppb_p->dip); pdip && (pdip != root) &&
- (ppb_p->parent_bus != PCIE_PCIECAP_DEV_TYPE_PCIE_DEV);
- pdip = ddi_get_parent(pdip)) {
- if (ddi_prop_lookup_string(DDI_DEV_T_ANY, pdip,
- DDI_PROP_DONTPASS, "device_type", &bus) !=
- DDI_PROP_SUCCESS)
- break;
-
- if (strcmp(bus, "pciex") == 0)
- ppb_p->parent_bus = PCIE_PCIECAP_DEV_TYPE_PCIE_DEV;
-
- ddi_prop_free(bus);
- }
}
/*
diff --git a/usr/src/uts/sun4u/io/px/oberon_regs.h b/usr/src/uts/sun4u/io/px/oberon_regs.h
index 29fed72c4d..7c2597d22b 100644
--- a/usr/src/uts/sun4u/io/px/oberon_regs.h
+++ b/usr/src/uts/sun4u/io/px/oberon_regs.h
@@ -19,15 +19,13 @@
* 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.
*/
#ifndef _SYS_OBERON_REGS_H
#define _SYS_OBERON_REGS_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#ifdef __cplusplus
extern "C" {
#endif
@@ -226,7 +224,6 @@ extern "C" {
#define DRAIN_CONTROL_STATUS 0x51100
#define DRAIN_CONTROL_STATUS_DRAIN 0
-#define PX_PCIEHP_PIL (LOCK_LEVEL - 1)
#ifdef __cplusplus
}
#endif
diff --git a/usr/src/uts/sun4u/io/px/px_hlib.c b/usr/src/uts/sun4u/io/px/px_hlib.c
index a98657031c..732c7a5a5a 100644
--- a/usr/src/uts/sun4u/io/px/px_hlib.c
+++ b/usr/src/uts/sun4u/io/px/px_hlib.c
@@ -29,9 +29,8 @@
#include <sys/vmem.h>
#include <sys/machsystm.h> /* lddphys() */
#include <sys/iommutsb.h>
-#include <sys/pci.h>
-#include <sys/hotplug/pci/pciehpc.h>
#include <px_obj.h>
+#include <sys/hotplug/pci/pcie_hp.h>
#include "px_regs.h"
#include "oberon_regs.h"
#include "px_csr.h"
@@ -1389,6 +1388,7 @@ lpu_init(caddr_t csr_base, pxu_t *pxu_p)
LPU_LTSSM_CONFIG4_DATA_RATE) |
(LPU_LTSSM_CONFIG4_N_FTS_DEFAULT <<
LPU_LTSSM_CONFIG4_N_FTS));
+
CSR_XS(csr_base, LPU_LTSSM_CONFIG4, val);
DBG(DBG_LPU, NULL, "lpu_init - LPU_LTSSM_CONFIG4: 0x%llx\n",
CSR_XR(csr_base, LPU_LTSSM_CONFIG4));
@@ -1734,7 +1734,8 @@ hvio_mmu_init(caddr_t csr_base, pxu_t *pxu_p)
* Write the most significant 30 bits of the TSB physical address
* and the encoded TSB table size.
*/
- for (i = 8; i && (pxu_p->tsb_size < (0x2000 << i)); i--) {}
+ for (i = 8; i && (pxu_p->tsb_size < (0x2000 << i)); i--)
+ ;
val = (((((va_to_pa(pxu_p->tsb_vaddr)) >> 13) << 13) |
((MMU_PAGE_SHIFT == 13) ? 0 : 1) << 8) | i);
@@ -2128,7 +2129,6 @@ uint64_t
hvio_intr_settarget(devhandle_t dev_hdl, pxu_t *pxu_p, sysino_t sysino,
cpuid_t cpuid)
{
-
uint64_t val, intr_controller;
uint32_t ino = SYSINO_TO_DEVINO(sysino);
@@ -2162,8 +2162,7 @@ hvio_intr_settarget(devhandle_t dev_hdl, pxu_t *pxu_p, sysino_t sysino,
}
/* For EQ interrupts, set DATA MONDO bit */
- if ((ino >= PX_DEFAULT_MSIQ_1ST_DEVINO) &&
- (ino < (PX_DEFAULT_MSIQ_1ST_DEVINO + PX_DEFAULT_MSIQ_CNT)))
+ if ((ino >= EQ_1ST_DEVINO) && (ino < (EQ_1ST_DEVINO + EQ_CNT)))
val |= (0x1ull << INTERRUPT_MAPPING_ENTRIES_MDO_MODE);
CSRA_XS((caddr_t)dev_hdl, INTERRUPT_MAPPING, ino, val);
@@ -3093,17 +3092,19 @@ oberon_hp_pwron(caddr_t csr_base)
delay(drv_usectohz(link_status_check));
reg = CSR_XR(csr_base, DLU_LINK_LAYER_STATUS);
- if ((((reg >> DLU_LINK_LAYER_STATUS_INIT_FC_SM_STS) &
- DLU_LINK_LAYER_STATUS_INIT_FC_SM_STS_MASK) ==
- DLU_LINK_LAYER_STATUS_INIT_FC_SM_STS_FC_INIT_DONE) &&
- (reg & (1ull << DLU_LINK_LAYER_STATUS_DLUP_STS)) &&
- ((reg & DLU_LINK_LAYER_STATUS_LNK_STATE_MACH_STS_MASK)
- ==
- DLU_LINK_LAYER_STATUS_LNK_STATE_MACH_STS_DL_ACTIVE)) {
- DBG(DBG_HP, NULL, "oberon_hp_pwron : link is up\n");
- link_up = B_TRUE;
- } else
+ if ((((reg >> DLU_LINK_LAYER_STATUS_INIT_FC_SM_STS) &
+ DLU_LINK_LAYER_STATUS_INIT_FC_SM_STS_MASK) ==
+ DLU_LINK_LAYER_STATUS_INIT_FC_SM_STS_FC_INIT_DONE) &&
+ (reg & (1ull << DLU_LINK_LAYER_STATUS_DLUP_STS)) &&
+ ((reg &
+ DLU_LINK_LAYER_STATUS_LNK_STATE_MACH_STS_MASK) ==
+ DLU_LINK_LAYER_STATUS_LNK_STATE_MACH_STS_DL_ACTIVE)) {
+ DBG(DBG_HP, NULL, "oberon_hp_pwron : "
+ "link is up\n");
+ link_up = B_TRUE;
+ } else
link_retry = B_TRUE;
+
}
/* END CSTYLED */
}
@@ -3382,7 +3383,7 @@ oberon_hpreg_put(void *cookie, off_t off, uint_t val)
int
hvio_hotplug_init(dev_info_t *dip, void *arg)
{
- pciehpc_regops_t *regops = (pciehpc_regops_t *)arg;
+ pcie_hp_regops_t *regops = (pcie_hp_regops_t *)arg;
px_t *px_p = DIP_TO_STATE(dip);
pxu_t *pxu_p = (pxu_t *)px_p->px_plat_p;
volatile uint64_t reg;
diff --git a/usr/src/uts/sun4u/io/px/px_lib4u.c b/usr/src/uts/sun4u/io/px/px_lib4u.c
index a8c655d8e2..0a588ebe6e 100644
--- a/usr/src/uts/sun4u/io/px/px_lib4u.c
+++ b/usr/src/uts/sun4u/io/px/px_lib4u.c
@@ -40,7 +40,6 @@
#include <sys/cpuvar.h>
#include <sys/ivintr.h>
#include <sys/byteorder.h>
-#include <sys/hotplug/pci/pciehpc.h>
#include <sys/spl.h>
#include <px_obj.h>
#include <sys/pcie_pwr.h>
@@ -51,6 +50,7 @@
#include "px_lib4u.h"
#include "px_err.h"
#include "oberon_regs.h"
+#include <sys/hotplug/pci/pcie_hp.h>
#pragma weak jbus_stst_order
@@ -1495,7 +1495,7 @@ px_lib_clr_errs(px_t *px_p, dev_info_t *rdip, uint64_t addr)
int rc_err, fab_err, i;
int acctype = pec_p->pec_safeacc_type;
ddi_fm_error_t derr;
- px_ranges_t *ranges_p;
+ pci_ranges_t *ranges_p;
int range_len;
uint32_t addr_high, addr_low;
pcie_req_id_t bdf = PCIE_INVALID_BDF;
@@ -1520,7 +1520,7 @@ px_lib_clr_errs(px_t *px_p, dev_info_t *rdip, uint64_t addr)
/* Figure out if this is a cfg or mem32 access */
addr_high = (uint32_t)(addr >> 32);
addr_low = (uint32_t)addr;
- range_len = px_p->px_ranges_length / sizeof (px_ranges_t);
+ range_len = px_p->px_ranges_length / sizeof (pci_ranges_t);
i = 0;
for (ranges_p = px_p->px_ranges_p; i < range_len; i++, ranges_p++) {
if (ranges_p->parent_high == addr_high) {
@@ -2350,7 +2350,7 @@ px_fill_rc_status(px_fault_t *px_fault_p, pciex_rc_error_regs_t *rc_status)
uint32_t
px_fab_get(px_t *px_p, pcie_req_id_t bdf, uint16_t offset)
{
- px_ranges_t *rp = px_p->px_ranges_p;
+ pci_ranges_t *rp = px_p->px_ranges_p;
uint64_t range_prop, base_addr;
int bank = PCI_REG_ADDR_G(PCI_ADDR_CONFIG);
uint32_t val;
@@ -2369,7 +2369,7 @@ px_fab_get(px_t *px_p, pcie_req_id_t bdf, uint16_t offset)
void
px_fab_set(px_t *px_p, pcie_req_id_t bdf, uint16_t offset,
uint32_t val) {
- px_ranges_t *rp = px_p->px_ranges_p;
+ pci_ranges_t *rp = px_p->px_ranges_p;
uint64_t range_prop, base_addr;
int bank = PCI_REG_ADDR_G(PCI_ADDR_CONFIG);
@@ -2509,7 +2509,7 @@ px_get_rng_parent_hi_mask(px_t *px_p)
* fetch chip's range propery's value
*/
uint64_t
-px_get_range_prop(px_t *px_p, px_ranges_t *rp, int bank)
+px_get_range_prop(px_t *px_p, pci_ranges_t *rp, int bank)
{
uint64_t mask, range_prop;
@@ -2543,11 +2543,11 @@ px_cpr_rem_callb(px_t *px_p)
static uint_t
px_hp_intr(caddr_t arg1, caddr_t arg2)
{
- px_t *px_p = (px_t *)arg1;
- pxu_t *pxu_p = (pxu_t *)px_p->px_plat_p;
- int rval;
+ px_t *px_p = (px_t *)arg1;
+ pxu_t *pxu_p = (pxu_t *)px_p->px_plat_p;
+ int rval;
- rval = pciehpc_intr(px_p->px_dip);
+ rval = pcie_intr(px_p->px_dip);
#ifdef DEBUG
if (rval == DDI_INTR_UNCLAIMED)
@@ -2571,6 +2571,10 @@ px_lib_hotplug_init(dev_info_t *dip, void *arg)
pxu_t *pxu_p = (pxu_t *)px_p->px_plat_p;
uint64_t ret;
+ if (ddi_prop_exists(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
+ "hotplug-capable") == 0)
+ return (DDI_FAILURE);
+
if ((ret = hvio_hotplug_init(dip, arg)) == DDI_SUCCESS) {
if (px_lib_intr_devino_to_sysino(px_p->px_dip,
px_p->px_inos[PX_INTR_HOTPLUG], &pxu_p->hp_sysino) !=
@@ -2583,7 +2587,7 @@ px_lib_hotplug_init(dev_info_t *dip, void *arg)
return (DDI_FAILURE);
}
- VERIFY(add_ivintr(pxu_p->hp_sysino, PX_PCIEHP_PIL,
+ VERIFY(add_ivintr(pxu_p->hp_sysino, PCIE_INTR_PRI,
(intrfunc)px_hp_intr, (caddr_t)px_p, NULL, NULL) == 0);
px_ib_intr_enable(px_p, intr_dist_cpuid(),
@@ -2603,7 +2607,7 @@ px_lib_hotplug_uninit(dev_info_t *dip)
px_ib_intr_disable(px_p->px_ib_p,
px_p->px_inos[PX_INTR_HOTPLUG], IB_INTR_WAIT);
- VERIFY(rem_ivintr(pxu_p->hp_sysino, PX_PCIEHP_PIL) == 0);
+ VERIFY(rem_ivintr(pxu_p->hp_sysino, PCIE_INTR_PRI) == 0);
}
}
@@ -2613,7 +2617,9 @@ px_lib_hotplug_uninit(dev_info_t *dip)
void
px_hp_intr_redist(px_t *px_p)
{
- if (px_p && (px_p->px_dev_caps & PX_HOTPLUG_CAPABLE)) {
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(px_p->px_dip);
+
+ if (px_p && PCIE_IS_PCIE_HOTPLUG_ENABLED(bus_p)) {
px_ib_intr_dist_en(px_p->px_dip, intr_dist_cpuid(),
px_p->px_inos[PX_INTR_HOTPLUG], B_FALSE);
}
diff --git a/usr/src/uts/sun4u/io/px/px_lib4u.h b/usr/src/uts/sun4u/io/px/px_lib4u.h
index e2b8bbbbf1..45c15ea65d 100644
--- a/usr/src/uts/sun4u/io/px/px_lib4u.h
+++ b/usr/src/uts/sun4u/io/px/px_lib4u.h
@@ -26,7 +26,6 @@
#ifndef _SYS_PX_LIB4U_H
#define _SYS_PX_LIB4U_H
-
#ifdef __cplusplus
extern "C" {
#endif
@@ -153,6 +152,14 @@ typedef struct eq_rec {
#define EQ_ACTIVE_STATE 0x2 /* ACTIVE */
#define EQ_ERROR_STATE 0x4 /* ERROR */
+/*
+ * Default EQ Configurations
+ */
+#define EQ_CNT 36
+#define EQ_REC_CNT 128
+#define EQ_1ST_ID 0
+#define EQ_1ST_DEVINO 24
+
#define MMU_INVALID_TTE 0ull
#define MMU_TTE_VALID(tte) (((tte) & MMU_TTE_V) == MMU_TTE_V)
#define MMU_OBERON_PADDR_MASK 0x7fffffffffff
@@ -324,7 +331,7 @@ extern uint64_t hvio_iommu_getbypass(devhandle_t dev_hdl, pxu_t *pxu_p,
r_addr_t ra, io_attributes_t attr, io_addr_t *io_addr_p);
extern uint64_t hvio_get_bypass_base(pxu_t *pxu_p);
extern uint64_t hvio_get_bypass_end(pxu_t *pxu_p);
-extern uint64_t px_get_range_prop(px_t *px_p, px_ranges_t *rp, int bank);
+extern uint64_t px_get_range_prop(px_t *px_p, pci_ranges_t *rp, int bank);
/*
diff --git a/usr/src/uts/sun4u/px/Makefile b/usr/src/uts/sun4u/px/Makefile
index 27bcdd2b1a..c14d4a2f29 100644
--- a/usr/src/uts/sun4u/px/Makefile
+++ b/usr/src/uts/sun4u/px/Makefile
@@ -21,11 +21,9 @@
#
# uts/sun4u/px/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 pci driver kernel module
#
# sun4u implementation architecture dependent
@@ -87,7 +85,7 @@ LINTTAGS += -erroff=E_ASSIGN_NARROW_CONV
#
# Dependency
#
-LDFLAGS += -dy -Nmisc/busra -Nmisc/pcie -Nmisc/pcihp -Nmisc/pciehpc
+LDFLAGS += -dy -Nmisc/pcie
#
# Default build targets.
diff --git a/usr/src/uts/sun4u/sys/pci/pci_obj.h b/usr/src/uts/sun4u/sys/pci/pci_obj.h
index 358ccd132d..d71c4942ab 100644
--- a/usr/src/uts/sun4u/sys/pci/pci_obj.h
+++ b/usr/src/uts/sun4u/sys/pci/pci_obj.h
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -20,20 +19,19 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#ifndef _SYS_PCI_OBJ_H
#define _SYS_PCI_OBJ_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#ifdef __cplusplus
extern "C" {
#endif
#include <sys/pci.h>
+#include <sys/pci_impl.h>
#include <sys/pci_intr_lib.h>
#include <sys/pci/pci_nexus.h>
#include <sys/pci/pci_types.h>
diff --git a/usr/src/uts/sun4u/sys/pci/pci_tools_ext.h b/usr/src/uts/sun4u/sys/pci/pci_tools_ext.h
index 74be3a6dd0..8658013f7b 100644
--- a/usr/src/uts/sun4u/sys/pci/pci_tools_ext.h
+++ b/usr/src/uts/sun4u/sys/pci/pci_tools_ext.h
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -20,15 +19,13 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#ifndef _SYS_PCI_TOOLS_EXT_H
#define _SYS_PCI_TOOLS_EXT_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#ifdef __cplusplus
extern "C" {
#endif
@@ -36,13 +33,6 @@ extern "C" {
/* This file contains pcitool defs exported to other modules of a PCI driver. */
/*
- * Minor numbers for dedicated pcitool nodes.
- * Note that FF and FE minor numbers are used for other minor nodes.
- */
-#define PCI_TOOL_REG_MINOR_NUM 0xFD
-#define PCI_TOOL_INTR_MINOR_NUM 0xFC
-
-/*
* Functions exported from the pci_tools.c module.
*/
extern int pcitool_dev_reg_ops(dev_t dev, void *arg, int cmd, int mode);
diff --git a/usr/src/uts/sun4u/sys/pci/pci_var.h b/usr/src/uts/sun4u/sys/pci/pci_var.h
index 53d12a538e..fb43a92061 100644
--- a/usr/src/uts/sun4u/sys/pci/pci_var.h
+++ b/usr/src/uts/sun4u/sys/pci/pci_var.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_PCI_VAR_H
#define _SYS_PCI_VAR_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#ifdef __cplusplus
extern "C" {
#endif
@@ -126,10 +124,6 @@ struct pci {
*/
pci_state_t pci_state;
uint_t pci_soft_state;
-#define PCI_SOFT_STATE_OPEN 0x01
-#define PCI_SOFT_STATE_OPEN_EXCL 0x02
-#define PCI_SOFT_STATE_CLOSED 0x04
- uint_t pci_open_count;
uint16_t pci_tsb_cookie; /* IOMMU TSB allocation */
kmutex_t pci_mutex;
diff --git a/usr/src/uts/sun4v/io/px/px_lib4v.c b/usr/src/uts/sun4v/io/px/px_lib4v.c
index 89a752ba1f..5efbc38442 100644
--- a/usr/src/uts/sun4v/io/px/px_lib4v.c
+++ b/usr/src/uts/sun4v/io/px/px_lib4v.c
@@ -38,7 +38,6 @@
#include <sys/hsvc.h>
#include <px_obj.h>
#include <sys/machsystm.h>
-#include <sys/hotplug/pci/pcihp.h>
#include "px_lib4v.h"
#include "px_err.h"
@@ -103,7 +102,7 @@ px_lib_dev_init(dev_info_t *dip, devhandle_t *dev_hdl)
* any indirect PCI config access services
*/
(void) ddi_prop_update_int(makedevice(ddi_driver_major(dip),
- PCIHP_AP_MINOR_NUM(ddi_get_instance(dip), PCIHP_DEVCTL_MINOR)), dip,
+ PCI_MINOR_NUM(ddi_get_instance(dip), PCI_DEVCTL_MINOR)), dip,
PCI_BUS_CONF_MAP_PROP, 1);
DBG(DBG_ATTACH, dip, "px_lib_dev_init: dev_hdl 0x%llx\n", *dev_hdl);
@@ -136,7 +135,7 @@ px_lib_dev_fini(dev_info_t *dip)
DBG(DBG_DETACH, dip, "px_lib_dev_fini: dip 0x%p\n", dip);
(void) ddi_prop_remove(makedevice(ddi_driver_major(dip),
- PCIHP_AP_MINOR_NUM(ddi_get_instance(dip), PCIHP_DEVCTL_MINOR)), dip,
+ PCI_MINOR_NUM(ddi_get_instance(dip), PCI_DEVCTL_MINOR)), dip,
PCI_BUS_CONF_MAP_PROP);
if (--px_vpci_users == 0)
@@ -1508,7 +1507,7 @@ px_lib_log_safeacc_err(px_t *px_p, ddi_acc_handle_t handle, int fme_flag,
{
uint32_t addr_high, addr_low;
pcie_req_id_t bdf = PCIE_INVALID_BDF;
- px_ranges_t *ranges_p;
+ pci_ranges_t *ranges_p;
int range_len, i;
ddi_acc_impl_t *hp = (ddi_acc_impl_t *)handle;
ddi_fm_error_t derr;
@@ -1528,7 +1527,7 @@ px_lib_log_safeacc_err(px_t *px_p, ddi_acc_handle_t handle, int fme_flag,
* Make sure this failed load came from this PCIe port. Check by
* matching the upper 32 bits of the address with the ranges property.
*/
- range_len = px_p->px_ranges_length / sizeof (px_ranges_t);
+ range_len = px_p->px_ranges_length / sizeof (pci_ranges_t);
i = 0;
for (ranges_p = px_p->px_ranges_p; i < range_len; i++, ranges_p++) {
if (ranges_p->parent_high == addr_high) {
diff --git a/usr/src/uts/sun4v/niumx/Makefile b/usr/src/uts/sun4v/niumx/Makefile
index b52a7da69a..fcb74811ec 100644
--- a/usr/src/uts/sun4v/niumx/Makefile
+++ b/usr/src/uts/sun4v/niumx/Makefile
@@ -20,11 +20,9 @@
#
#
# uts/sun4v/niumx/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 niumx driver kernel module
#
@@ -57,9 +55,8 @@ LINT_TARGET = $(MODULE).lint
INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
#
-# Include SUN4 and SUN4U specific headers files
+# Include SUN4V specific headers files
#
-INC_PATH += -I$(UTSBASE)/sun4/io/niumx
INC_PATH += -I$(UTSBASE)/sun4v/io/niumx
#
@@ -73,11 +70,6 @@ CFLAGS += $(CCVERBOSE)
CFLAGS += -dalign
#
-# Dependency
-#
-LDFLAGS += -dy -Nmisc/busra -Nmisc/pcie
-
-#
# Default build targets.
#
.KEEP_STATE:
diff --git a/usr/src/uts/sun4v/px/Makefile b/usr/src/uts/sun4v/px/Makefile
index e1389799b0..30b67978db 100644
--- a/usr/src/uts/sun4v/px/Makefile
+++ b/usr/src/uts/sun4v/px/Makefile
@@ -21,11 +21,9 @@
#
# uts/sun4v/px/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 px driver kernel module.
# sun4v implementation architecture dependent
#
@@ -89,7 +87,7 @@ LINTTAGS += -erroff=E_ASSIGN_NARROW_CONV
# Dependency
#
-LDFLAGS += -dy -Nmisc/busra -Nmisc/pcie -Nmisc/pciehpc -Nmisc/pcihp
+LDFLAGS += -dy -Nmisc/pcie
# Default build targets.
#