diff options
author | Evan Yan <Evan.Yan@Sun.COM> | 2009-11-02 15:58:28 +0800 |
---|---|---|
committer | Evan Yan <Evan.Yan@Sun.COM> | 2009-11-02 15:58:28 +0800 |
commit | 269473047d747f7815af570197e4ef7322d3632c (patch) | |
tree | e93761fa235a8ba4c1b5c637a8f3a429be21a508 /usr/src | |
parent | b24ab6762772a3f6a89393947930c7fa61306783 (diff) | |
download | illumos-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')
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> +</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)®s, &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)®s, &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)®s, &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)®spec, ®len) != 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)®spec, ®len) - != DDI_SUCCESS) { - pcishpc_debug("pcishpc_get_pci_info() failed to get regspec."); - return (DDI_FAILURE); - } - - pcishpc_p->shpc_bus = PCI_REG_BUS_G(regspec[0].pci_phys_hi); - pcishpc_p->shpc_dev = PCI_REG_DEV_G(regspec[0].pci_phys_hi); - pcishpc_p->shpc_func = PCI_REG_FUNC_G(regspec[0].pci_phys_hi); - - kmem_free(regspec, reglen); - - pcishpc_debug("pcishpc_get_pci_info() %s%d: bus=%d, dev=%d, func=%d", - ddi_driver_name(pcishpc_p->shpc_dip), - ddi_get_instance(pcishpc_p->shpc_dip), - pcishpc_p->shpc_bus, pcishpc_p->shpc_dev, - pcishpc_p->shpc_func); - - return (DDI_SUCCESS); -} - - -/* - * pcishpc_init() - * - * Install and configure an SHPC controller and register the HotPlug slots - * with the Solaris HotPlug framework. This function is usually called by - * a PCI bridge Nexus driver that has a built in SHPC controller. - */ -int -pcishpc_init(dev_info_t *dip) -{ - pcishpc_ctrl_t *ctrl_p; - int i; - - pcishpc_debug("pcishpc_init() called from %s#%d", - ddi_driver_name(dip), ddi_get_instance(dip)); - - mutex_enter(&pcishpc_control_mutex); - - if (pcishpc_get_controller(dip) != NULL) { - pcishpc_debug("pcishpc_init() shpc instance already " - "initialized!"); - mutex_exit(&pcishpc_control_mutex); - return (DDI_SUCCESS); - } - - /* Initialize soft state structure for the SHPC instance. */ - ctrl_p = pcishpc_create_controller(dip); - - if (ctrl_p == NULL) { - pcishpc_debug("pcishpc_init() failed to create shpc softstate"); - mutex_exit(&pcishpc_control_mutex); - return (DDI_FAILURE); - } - - if (pcishpc_setup_controller(ctrl_p) != DDI_SUCCESS) { - pcishpc_debug("pcishpc_init() failed to setup controller"); - (void) pcishpc_destroy_controller(dip); - mutex_exit(&pcishpc_control_mutex); - return (DDI_FAILURE); - } - -#if 0 - pcishpc_debug("%s%d: P2P bridge register dump:", - ddi_driver_name(dip), ddi_get_instance(dip)); - - for (i = 0; i < 0x100; i += 4) { - pcishpc_debug("SHPC Cfg reg 0x%02x: %08x", i, - pci_config_get32(ctrl_p->shpc_config_hdl, i)); - } -#endif - - /* Setup each HotPlug slot on this SHPC controller. */ - for (i = 0; i < ctrl_p->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)®, - &length) != DDI_PROP_SUCCESS) { + if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "reg", + (caddr_t)®, &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, - ®[i]) != PCICFG_SUCCESS) { + if (pcicfg_update_assigned_prop(dip, ®[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)®, - &length) != DDI_PROP_SUCCESS) { + if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "reg", + (caddr_t)®, &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, - ®[i]) != PCICFG_SUCCESS) { + if (pcicfg_update_assigned_prop(dip, ®[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)®, &rlen); + dip, DDI_PROP_DONTPASS, "reg", (caddr_t)®, &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)®, &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)®, &rlen); + status = ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "reg", + (caddr_t)®, &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)®, &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 *)®ops) == 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)®ops) != 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 *)®ops) != DDI_SUCCESS) - return (DDI_FAILURE); - - if (pciehpc_init(dip, ®ops) != DDI_SUCCESS) { - px_lib_hotplug_uninit(dip); - return (DDI_FAILURE); - } - - if (pcihp_init(dip) != DDI_SUCCESS) { - (void) pciehpc_uninit(dip); - px_lib_hotplug_uninit(dip); - return (DDI_FAILURE); - } - - if (pcihp_get_cb_ops() != NULL) { - DBG(DBG_ATTACH, dip, "%s%d hotplug enabled", - ddi_driver_name(dip), ddi_get_instance(dip)); - px_p->px_dev_caps |= PX_HOTPLUG_CAPABLE; - } - - return (DDI_SUCCESS); -} - -static 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. # |