126947304SEvan Yan /*
226947304SEvan Yan * CDDL HEADER START
326947304SEvan Yan *
426947304SEvan Yan * The contents of this file are subject to the terms of the
526947304SEvan Yan * Common Development and Distribution License (the "License").
626947304SEvan Yan * You may not use this file except in compliance with the License.
726947304SEvan Yan *
826947304SEvan Yan * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
926947304SEvan Yan * or http://www.opensolaris.org/os/licensing.
1026947304SEvan Yan * See the License for the specific language governing permissions
1126947304SEvan Yan * and limitations under the License.
1226947304SEvan Yan *
1326947304SEvan Yan * When distributing Covered Code, include this CDDL HEADER in each
1426947304SEvan Yan * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1526947304SEvan Yan * If applicable, add the following below this CDDL HEADER, with the
1626947304SEvan Yan * fields enclosed by brackets "[]" replaced with your own identifying
1726947304SEvan Yan * information: Portions Copyright [yyyy] [name of copyright owner]
1826947304SEvan Yan *
1926947304SEvan Yan * CDDL HEADER END
2026947304SEvan Yan */
2126947304SEvan Yan /*
2226947304SEvan Yan * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
2326947304SEvan Yan * Use is subject to license terms.
24ffb64830SJordan Paige Hendricks *
25ffb64830SJordan Paige Hendricks * Copyright 2019 Joyent, Inc.
26b31f5cf7SDan Cross * Copyright 2023 Oxide Computer Company
2726947304SEvan Yan */
2826947304SEvan Yan
2926947304SEvan Yan /*
3026947304SEvan Yan * Sun DDI hotplug implementation specific functions
3126947304SEvan Yan */
3226947304SEvan Yan
33ffb64830SJordan Paige Hendricks /*
34ffb64830SJordan Paige Hendricks * HOTPLUG FRAMEWORK
35ffb64830SJordan Paige Hendricks *
36ffb64830SJordan Paige Hendricks * The hotplug framework (also referred to "SHP", for "Solaris Hotplug
37ffb64830SJordan Paige Hendricks * Framework") refers to a large set of userland and kernel interfaces,
38ffb64830SJordan Paige Hendricks * including those in this file, that provide functionality related to device
39ffb64830SJordan Paige Hendricks * hotplug.
40ffb64830SJordan Paige Hendricks *
41ffb64830SJordan Paige Hendricks * Hotplug is a broad term that refers to both removal and insertion of devices
42ffb64830SJordan Paige Hendricks * on a live system. Such operations can have varying levels of notification to
43ffb64830SJordan Paige Hendricks * the system. Coordinated hotplug means that the operating system is notified
44ffb64830SJordan Paige Hendricks * in advance that a device will have a hotplug operation performed on it.
45ffb64830SJordan Paige Hendricks * Non-coordinated hotplug, also called "surprise removal", does not have such
46ffb64830SJordan Paige Hendricks * notification, and the device is simply removed or inserted from the system.
47ffb64830SJordan Paige Hendricks *
48ffb64830SJordan Paige Hendricks * The goals of a correct hotplug operation will vary based on the device. In
49ffb64830SJordan Paige Hendricks * general, though, we want the system to gracefully notice the device change
50ffb64830SJordan Paige Hendricks * and clean up (or create) any relevant structures related to using the device
51ffb64830SJordan Paige Hendricks * in the system.
52ffb64830SJordan Paige Hendricks *
53ffb64830SJordan Paige Hendricks * The goals of the hotplug framework are to provide common interfaces for nexus
54ffb64830SJordan Paige Hendricks * drivers, device drivers, and userland programs to build a foundation for
55ffb64830SJordan Paige Hendricks * implementing hotplug for a variety of devices. Notably, common support for
56ffb64830SJordan Paige Hendricks * PCIe devices is available. See also: the nexus driver for PCIe devices at
57ffb64830SJordan Paige Hendricks * uts/i86pc/io/pciex/npe.c.
58ffb64830SJordan Paige Hendricks *
59ffb64830SJordan Paige Hendricks *
60ffb64830SJordan Paige Hendricks * TERMINOLOGY
61ffb64830SJordan Paige Hendricks *
62ffb64830SJordan Paige Hendricks * The following terms may be useful when exploring hotplug-related code.
63ffb64830SJordan Paige Hendricks *
64ffb64830SJordan Paige Hendricks * PHYSICAL HOTPLUG
65ffb64830SJordan Paige Hendricks * Refers to hotplug operations on a physical hardware receptacle.
66ffb64830SJordan Paige Hendricks *
67ffb64830SJordan Paige Hendricks * VIRTUAL HOTPLUG
68ffb64830SJordan Paige Hendricks * Refers to hotplug operations on an arbitrary device node in the device
69ffb64830SJordan Paige Hendricks * tree.
70ffb64830SJordan Paige Hendricks *
71ffb64830SJordan Paige Hendricks * CONNECTION (often abbreviated "cn")
72ffb64830SJordan Paige Hendricks * A place where either physical or virtual hotplug happens. This is a more
73ffb64830SJordan Paige Hendricks * generic term to refer to "connectors" and "ports", which represent
74ffb64830SJordan Paige Hendricks * physical and virtual places where hotplug happens, respectively.
75ffb64830SJordan Paige Hendricks *
76ffb64830SJordan Paige Hendricks * CONNECTOR
77ffb64830SJordan Paige Hendricks * A place where physical hotplug happens. For example: a PCIe slot, a USB
78ffb64830SJordan Paige Hendricks * port, a SAS port, and a fiber channel port are all connectors.
79ffb64830SJordan Paige Hendricks *
80ffb64830SJordan Paige Hendricks * PORT
81ffb64830SJordan Paige Hendricks * A place where virtual hotplug happens. A port refers to an arbitrary
82ffb64830SJordan Paige Hendricks * place under a nexus dev_info node in the device tree.
83ffb64830SJordan Paige Hendricks *
84ffb64830SJordan Paige Hendricks *
85ffb64830SJordan Paige Hendricks * CONNECTION STATE MACHINE
86ffb64830SJordan Paige Hendricks *
87ffb64830SJordan Paige Hendricks * Connections have the states below. Connectors and ports are grouped into
88ffb64830SJordan Paige Hendricks * the same state machine. It is worth noting that the edges here are incomplete
89ffb64830SJordan Paige Hendricks * -- it is possible for a connection to move straight from ENABLED to EMPTY,
90ffb64830SJordan Paige Hendricks * for instance, if there is a surprise removal of its device.
91ffb64830SJordan Paige Hendricks *
92ffb64830SJordan Paige Hendricks * State changes are kicked off through two ways:
93ffb64830SJordan Paige Hendricks * - Through the nexus driver interface, ndi_hp_state_change_req. PCIe
94ffb64830SJordan Paige Hendricks * nexus drivers that pass a hotplug interrupt through to pciehpc will kick
95ffb64830SJordan Paige Hendricks * off state changes in this way.
96bbf21555SRichard Lowe * - Through coordinated removal, ddihp_modctl. Both cfgadm(8) and
97bbf21555SRichard Lowe * hotplug(8) pass state change requests through hotplugd, which uses
98ffb64830SJordan Paige Hendricks * modctl to request state changes to the DDI hotplug framework. That
99ffb64830SJordan Paige Hendricks * interface is ultimately implemented by ddihp_modctl.
100ffb64830SJordan Paige Hendricks *
101ffb64830SJordan Paige Hendricks * (start)
102ffb64830SJordan Paige Hendricks * |
103ffb64830SJordan Paige Hendricks * v
104ffb64830SJordan Paige Hendricks * EMPTY no component plugged into connector
105ffb64830SJordan Paige Hendricks * ^
106ffb64830SJordan Paige Hendricks * v
107ffb64830SJordan Paige Hendricks * PRESENT component plugged into connector
108ffb64830SJordan Paige Hendricks * ^
109ffb64830SJordan Paige Hendricks * v
110ffb64830SJordan Paige Hendricks * POWERED connector is powered
111ffb64830SJordan Paige Hendricks * ^
112ffb64830SJordan Paige Hendricks * v
113ffb64830SJordan Paige Hendricks * ENABLED connector is fully functional
114ffb64830SJordan Paige Hendricks * |
115ffb64830SJordan Paige Hendricks * .
116ffb64830SJordan Paige Hendricks * .
117ffb64830SJordan Paige Hendricks * .
118ffb64830SJordan Paige Hendricks * v
119ffb64830SJordan Paige Hendricks * (create port)
120ffb64830SJordan Paige Hendricks * |
121ffb64830SJordan Paige Hendricks * v
122ffb64830SJordan Paige Hendricks * PORT EMPTY port has no device occupying it
123ffb64830SJordan Paige Hendricks * ^
124ffb64830SJordan Paige Hendricks * v
125ffb64830SJordan Paige Hendricks * PORT PRESENT port occupied by device
126ffb64830SJordan Paige Hendricks *
127ffb64830SJordan Paige Hendricks *
128ffb64830SJordan Paige Hendricks * ARCHITECTURE DIAGRAM
129ffb64830SJordan Paige Hendricks *
130ffb64830SJordan Paige Hendricks * The following is a non-exhaustive summary of various components in the system
131ffb64830SJordan Paige Hendricks * that implement pieces of the hotplug framework. More detailed descriptions
132ffb64830SJordan Paige Hendricks * of some key components are below.
133ffb64830SJordan Paige Hendricks *
134ffb64830SJordan Paige Hendricks * +------------+
135bbf21555SRichard Lowe * | cfgadm(8) |
136ffb64830SJordan Paige Hendricks * +------------+
137ffb64830SJordan Paige Hendricks * |
138ffb64830SJordan Paige Hendricks * +-------------------+
139ffb64830SJordan Paige Hendricks * | SHP cfgadm plugin |
140ffb64830SJordan Paige Hendricks * +-------------------+
141ffb64830SJordan Paige Hendricks * |
142ffb64830SJordan Paige Hendricks * +-------------+ +------------+
143bbf21555SRichard Lowe * | hotplug(8) |----------| libhotplug |
144ffb64830SJordan Paige Hendricks * +-------------+ +------------+
145ffb64830SJordan Paige Hendricks * |
146ffb64830SJordan Paige Hendricks * +----------+
147ffb64830SJordan Paige Hendricks * | hotplugd |
148ffb64830SJordan Paige Hendricks * +----------+
149ffb64830SJordan Paige Hendricks * |
150ffb64830SJordan Paige Hendricks * +----------------+
151ffb64830SJordan Paige Hendricks * | modctl (HP op) |
152ffb64830SJordan Paige Hendricks * +----------------+
153ffb64830SJordan Paige Hendricks * |
154ffb64830SJordan Paige Hendricks * |
155ffb64830SJordan Paige Hendricks * User |
156ffb64830SJordan Paige Hendricks * =============================|===============================================
157ffb64830SJordan Paige Hendricks * Kernel |
158ffb64830SJordan Paige Hendricks * |
159ffb64830SJordan Paige Hendricks * |
160ffb64830SJordan Paige Hendricks * +------------------------+ +----------------+
161ffb64830SJordan Paige Hendricks * | DDI hotplug interfaces | --- | Device Drivers |
162ffb64830SJordan Paige Hendricks * +------------------------+ +----------------+
163ffb64830SJordan Paige Hendricks * | |
164ffb64830SJordan Paige Hendricks * | +------------------------+
165ffb64830SJordan Paige Hendricks * | | NDI hotplug interfaces |
166ffb64830SJordan Paige Hendricks * | +------------------------+
167ffb64830SJordan Paige Hendricks * | |
168ffb64830SJordan Paige Hendricks * | |
169ffb64830SJordan Paige Hendricks * +-------------+ +--------------+ +---------------------------+
170ffb64830SJordan Paige Hendricks * | `bus_hp_op` | -- |"pcie" module | --- | "npe" (PCIe nexus driver) |
171ffb64830SJordan Paige Hendricks * +-------------+ +--------------+ +---------------------------+
172ffb64830SJordan Paige Hendricks * | |
173ffb64830SJordan Paige Hendricks * | +-------------------+
174ffb64830SJordan Paige Hendricks * | | PCIe configurator |
175ffb64830SJordan Paige Hendricks * | +-------------------+
176ffb64830SJordan Paige Hendricks * |
177ffb64830SJordan Paige Hendricks * +-------------------------------------+
178ffb64830SJordan Paige Hendricks * | "pciehpc" (PCIe hotplug controller) |
179ffb64830SJordan Paige Hendricks * +-------------------------------------+
180ffb64830SJordan Paige Hendricks *
181ffb64830SJordan Paige Hendricks *
182ffb64830SJordan Paige Hendricks * .
183ffb64830SJordan Paige Hendricks * .
184ffb64830SJordan Paige Hendricks * .
185ffb64830SJordan Paige Hendricks * .
186ffb64830SJordan Paige Hendricks * .
187ffb64830SJordan Paige Hendricks * |
188ffb64830SJordan Paige Hendricks * |
189ffb64830SJordan Paige Hendricks * +-----------------------------------+
190ffb64830SJordan Paige Hendricks * | I/O Subsystem |
191ffb64830SJordan Paige Hendricks * | (LDI notifications and contracts) |
192ffb64830SJordan Paige Hendricks * +-----------------------------------+
193ffb64830SJordan Paige Hendricks *
194ffb64830SJordan Paige Hendricks *
195ffb64830SJordan Paige Hendricks * KEY HOTPLUG SOFTWARE COMPONENTS
196ffb64830SJordan Paige Hendricks *
197bbf21555SRichard Lowe * cfgadm(8)
198ffb64830SJordan Paige Hendricks *
199ffb64830SJordan Paige Hendricks * cfgadm is the canonical tool for hotplug operations. It can be used to
200ffb64830SJordan Paige Hendricks * list connections on the system and change their state in a coordinated
201ffb64830SJordan Paige Hendricks * fashion. For more information, see its manual page.
202ffb64830SJordan Paige Hendricks *
203ffb64830SJordan Paige Hendricks *
204bbf21555SRichard Lowe * hotplug(8)
205ffb64830SJordan Paige Hendricks *
206ffb64830SJordan Paige Hendricks * hotplug is a command line tool for managing hotplug connections for
207ffb64830SJordan Paige Hendricks * connectors. For more information, see its manual page.
208ffb64830SJordan Paige Hendricks *
209ffb64830SJordan Paige Hendricks *
210ffb64830SJordan Paige Hendricks * DDI HOTPLUG INTERFACES
211ffb64830SJordan Paige Hendricks *
212ffb64830SJordan Paige Hendricks * This part of the framework provides interfaces for changing device state
213ffb64830SJordan Paige Hendricks * for connectors, including onlining and offlining child devices. Many of
214ffb64830SJordan Paige Hendricks * these functions are defined in this file.
215ffb64830SJordan Paige Hendricks *
216ffb64830SJordan Paige Hendricks *
217ffb64830SJordan Paige Hendricks * NDI HOTPLUG INTERFACES
218ffb64830SJordan Paige Hendricks *
219ffb64830SJordan Paige Hendricks * Nexus drivers can define their own hotplug bus implementations by
220ffb64830SJordan Paige Hendricks * defining a bus_hp_op entry point. This entry point must implement
221ffb64830SJordan Paige Hendricks * a set of hotplug related commands, including getting, probing, and
222ffb64830SJordan Paige Hendricks * changing connection state, as well as port creation and removal.
223ffb64830SJordan Paige Hendricks *
224ffb64830SJordan Paige Hendricks * Nexus drivers may also want to use the following interfaces for
225ffb64830SJordan Paige Hendricks * implementing hotplug. Note that the PCIe Hotplug Controller ("pciehpc")
226ffb64830SJordan Paige Hendricks * already takes care of using these:
227ffb64830SJordan Paige Hendricks * ndi_hp_{register,unregister}
228ffb64830SJordan Paige Hendricks * ndi_hp_state_change_req
229ffb64830SJordan Paige Hendricks * ndi_hp_walk_cn
230ffb64830SJordan Paige Hendricks *
231ffb64830SJordan Paige Hendricks * PCIe nexus drivers should use the common entry point pcie_hp_common_ops,
232ffb64830SJordan Paige Hendricks * which implements hotplug commands for PCIe devices, calling into other
233ffb64830SJordan Paige Hendricks * parts of the framework as needed.
234ffb64830SJordan Paige Hendricks *
235ffb64830SJordan Paige Hendricks *
236ffb64830SJordan Paige Hendricks * NPE DRIVER ("npe")
237ffb64830SJordan Paige Hendricks *
238ffb64830SJordan Paige Hendricks * npe is the common nexus driver for PCIe devices on x86. It implements
239ffb64830SJordan Paige Hendricks * hotplug using the NDI interfaces. For more information, see
240ffb64830SJordan Paige Hendricks * uts/i86pc/io/pciex/npe.c.
241ffb64830SJordan Paige Hendricks *
242ffb64830SJordan Paige Hendricks * The equivalent driver for SPARC is "px".
243ffb64830SJordan Paige Hendricks *
244ffb64830SJordan Paige Hendricks *
245ffb64830SJordan Paige Hendricks * PCIe HOTPLUG CONTROLLER DRIVER ("pciehpc")
246ffb64830SJordan Paige Hendricks *
247ffb64830SJordan Paige Hendricks * All hotplug-capable PCIe buses will initialize their own PCIe HPC,
248ffb64830SJordan Paige Hendricks * including the pcieb and ppb drivers. The controller maintains
249ffb64830SJordan Paige Hendricks * hotplug-related state about the slots on its bus, including their status
250ffb64830SJordan Paige Hendricks * and port state. It also features a common implementation of handling
251ffb64830SJordan Paige Hendricks * hotplug-related PCIe interrupts.
252ffb64830SJordan Paige Hendricks *
253ffb64830SJordan Paige Hendricks * For more information, see its interfaces in
254ffb64830SJordan Paige Hendricks * uts/common/sys/hotplug/pci/pciehpc.h.
255ffb64830SJordan Paige Hendricks *
256ffb64830SJordan Paige Hendricks */
257ffb64830SJordan Paige Hendricks
25826947304SEvan Yan #include <sys/sysmacros.h>
25926947304SEvan Yan #include <sys/types.h>
26026947304SEvan Yan #include <sys/file.h>
26126947304SEvan Yan #include <sys/param.h>
26226947304SEvan Yan #include <sys/systm.h>
26326947304SEvan Yan #include <sys/kmem.h>
26426947304SEvan Yan #include <sys/cmn_err.h>
26526947304SEvan Yan #include <sys/debug.h>
26626947304SEvan Yan #include <sys/avintr.h>
26726947304SEvan Yan #include <sys/autoconf.h>
26826947304SEvan Yan #include <sys/ddi.h>
26926947304SEvan Yan #include <sys/sunndi.h>
27026947304SEvan Yan #include <sys/ndi_impldefs.h>
27126947304SEvan Yan #include <sys/sysevent.h>
27226947304SEvan Yan #include <sys/sysevent/eventdefs.h>
27326947304SEvan Yan #include <sys/sysevent/dr.h>
27426947304SEvan Yan #include <sys/fs/dv_node.h>
27526947304SEvan Yan
27626947304SEvan Yan /*
27726947304SEvan Yan * Local function prototypes
27826947304SEvan Yan */
27926947304SEvan Yan /* Connector operations */
28026947304SEvan Yan static int ddihp_cn_pre_change_state(ddi_hp_cn_handle_t *hdlp,
28126947304SEvan Yan ddi_hp_cn_state_t target_state);
28226947304SEvan Yan static int ddihp_cn_post_change_state(ddi_hp_cn_handle_t *hdlp,
28326947304SEvan Yan ddi_hp_cn_state_t new_state);
28426947304SEvan Yan static int ddihp_cn_handle_state_change(ddi_hp_cn_handle_t *hdlp);
28526947304SEvan Yan static int ddihp_cn_change_children_state(ddi_hp_cn_handle_t *hdlp,
28626947304SEvan Yan boolean_t online);
28726947304SEvan Yan /* Port operations */
28826947304SEvan Yan static int ddihp_port_change_state(ddi_hp_cn_handle_t *hdlp,
28926947304SEvan Yan ddi_hp_cn_state_t target_state);
29026947304SEvan Yan static int ddihp_port_upgrade_state(ddi_hp_cn_handle_t *hdlp,
29126947304SEvan Yan ddi_hp_cn_state_t target_state);
29226947304SEvan Yan static int ddihp_port_downgrade_state(ddi_hp_cn_handle_t *hdlp,
29326947304SEvan Yan ddi_hp_cn_state_t target_state);
29426947304SEvan Yan /* Misc routines */
29526947304SEvan Yan static void ddihp_update_last_change(ddi_hp_cn_handle_t *hdlp);
29626947304SEvan Yan static boolean_t ddihp_check_status_prop(dev_info_t *dip);
29726947304SEvan Yan
29826947304SEvan Yan /*
29926947304SEvan Yan * Global functions (called within hotplug framework)
30026947304SEvan Yan */
30126947304SEvan Yan
30226947304SEvan Yan /*
30326947304SEvan Yan * Implement modctl() commands for hotplug.
30426947304SEvan Yan * Called by modctl_hp() in modctl.c
30526947304SEvan Yan */
30626947304SEvan Yan int
ddihp_modctl(int hp_op,char * path,char * cn_name,uintptr_t arg,uintptr_t rval)30726947304SEvan Yan ddihp_modctl(int hp_op, char *path, char *cn_name, uintptr_t arg,
30826947304SEvan Yan uintptr_t rval)
30926947304SEvan Yan {
310b31f5cf7SDan Cross dev_info_t *pdip, *dip;
31126947304SEvan Yan ddi_hp_cn_handle_t *hdlp;
31226947304SEvan Yan ddi_hp_op_t op = (ddi_hp_op_t)hp_op;
313*3fe80ca4SDan Cross int rv, error;
31426947304SEvan Yan
31526947304SEvan Yan /* Get the dip of nexus node */
31626947304SEvan Yan dip = e_ddi_hold_devi_by_path(path, 0);
31726947304SEvan Yan
31826947304SEvan Yan if (dip == NULL)
31926947304SEvan Yan return (ENXIO);
32026947304SEvan Yan
32126947304SEvan Yan DDI_HP_IMPLDBG((CE_CONT, "ddihp_modctl: dip %p op %x path %s "
32226947304SEvan Yan "cn_name %s arg %p rval %p\n", (void *)dip, hp_op, path, cn_name,
32326947304SEvan Yan (void *)arg, (void *)rval));
32426947304SEvan Yan
32526947304SEvan Yan if (!NEXUS_HAS_HP_OP(dip)) {
32626947304SEvan Yan ddi_release_devi(dip);
32726947304SEvan Yan return (ENOTSUP);
32826947304SEvan Yan }
32926947304SEvan Yan
330b31f5cf7SDan Cross /*
331b31f5cf7SDan Cross * We know that some of the functions that are called further from here
332b31f5cf7SDan Cross * on may enter critical sections on the parent of this node. In order
333b31f5cf7SDan Cross * to prevent deadlocks, we maintain the invariant that, if we lock a
334b31f5cf7SDan Cross * child, the parent must already be locked. This is the first place
335b31f5cf7SDan Cross * in the call stack where we may do so, so we lock the parent here.
336b31f5cf7SDan Cross */
337b31f5cf7SDan Cross pdip = ddi_get_parent(dip);
338b31f5cf7SDan Cross if (pdip != NULL)
339*3fe80ca4SDan Cross ndi_devi_enter(pdip);
340b31f5cf7SDan Cross
34126947304SEvan Yan /* Lock before access */
342*3fe80ca4SDan Cross ndi_devi_enter(dip);
34326947304SEvan Yan
34426947304SEvan Yan hdlp = ddihp_cn_name_to_handle(dip, cn_name);
34526947304SEvan Yan
34626947304SEvan Yan if (hp_op == DDI_HPOP_CN_CREATE_PORT) {
34726947304SEvan Yan if (hdlp != NULL) {
34826947304SEvan Yan /* this port already exists. */
34926947304SEvan Yan error = EEXIST;
35026947304SEvan Yan
35126947304SEvan Yan goto done;
35226947304SEvan Yan }
35326947304SEvan Yan rv = (*(DEVI(dip)->devi_ops->devo_bus_ops->bus_hp_op))(
35426947304SEvan Yan dip, cn_name, op, NULL, NULL);
35526947304SEvan Yan } else {
35626947304SEvan Yan if (hdlp == NULL) {
35726947304SEvan Yan /* Invalid Connection name */
35826947304SEvan Yan error = ENXIO;
35926947304SEvan Yan
36026947304SEvan Yan goto done;
36126947304SEvan Yan }
36226947304SEvan Yan if (hp_op == DDI_HPOP_CN_CHANGE_STATE) {
36326947304SEvan Yan ddi_hp_cn_state_t target_state = (ddi_hp_cn_state_t)arg;
36426947304SEvan Yan ddi_hp_cn_state_t result_state = 0;
36526947304SEvan Yan
36626947304SEvan Yan DDIHP_CN_OPS(hdlp, op, (void *)&target_state,
36726947304SEvan Yan (void *)&result_state, rv);
36826947304SEvan Yan
36926947304SEvan Yan DDI_HP_IMPLDBG((CE_CONT, "ddihp_modctl: target_state="
37026947304SEvan Yan "%x, result_state=%x, rv=%x \n",
37126947304SEvan Yan target_state, result_state, rv));
37226947304SEvan Yan } else {
37326947304SEvan Yan DDIHP_CN_OPS(hdlp, op, (void *)arg, (void *)rval, rv);
37426947304SEvan Yan }
37526947304SEvan Yan }
37626947304SEvan Yan switch (rv) {
37726947304SEvan Yan case DDI_SUCCESS:
37826947304SEvan Yan error = 0;
37926947304SEvan Yan break;
38026947304SEvan Yan case DDI_EINVAL:
38126947304SEvan Yan error = EINVAL;
38226947304SEvan Yan break;
38326947304SEvan Yan case DDI_EBUSY:
38426947304SEvan Yan error = EBUSY;
38526947304SEvan Yan break;
38626947304SEvan Yan case DDI_ENOTSUP:
38726947304SEvan Yan error = ENOTSUP;
38826947304SEvan Yan break;
38926947304SEvan Yan case DDI_ENOMEM:
39026947304SEvan Yan error = ENOMEM;
39126947304SEvan Yan break;
39226947304SEvan Yan default:
39326947304SEvan Yan error = EIO;
39426947304SEvan Yan }
39526947304SEvan Yan
39626947304SEvan Yan done:
397*3fe80ca4SDan Cross ndi_devi_exit(dip);
398b31f5cf7SDan Cross if (pdip != NULL)
399*3fe80ca4SDan Cross ndi_devi_exit(pdip);
40026947304SEvan Yan
40126947304SEvan Yan ddi_release_devi(dip);
40226947304SEvan Yan
40326947304SEvan Yan return (error);
40426947304SEvan Yan }
40526947304SEvan Yan
40626947304SEvan Yan /*
407ffb64830SJordan Paige Hendricks * Fetch the state of Hotplug Connection (CN).
408ffb64830SJordan Paige Hendricks * This function will also update the state and last changed timestamp in the
409ffb64830SJordan Paige Hendricks * connection handle structure if the state has changed.
41026947304SEvan Yan */
41126947304SEvan Yan int
ddihp_cn_getstate(ddi_hp_cn_handle_t * hdlp)41226947304SEvan Yan ddihp_cn_getstate(ddi_hp_cn_handle_t *hdlp)
41326947304SEvan Yan {
41426947304SEvan Yan ddi_hp_cn_state_t new_state;
41526947304SEvan Yan int ret;
41626947304SEvan Yan
41726947304SEvan Yan DDI_HP_IMPLDBG((CE_CONT, "ddihp_cn_getstate: pdip %p hdlp %p\n",
41826947304SEvan Yan (void *)hdlp->cn_dip, (void *)hdlp));
41926947304SEvan Yan
42026947304SEvan Yan ASSERT(DEVI_BUSY_OWNED(hdlp->cn_dip));
42126947304SEvan Yan
42226947304SEvan Yan DDIHP_CN_OPS(hdlp, DDI_HPOP_CN_GET_STATE,
42326947304SEvan Yan NULL, (void *)&new_state, ret);
42426947304SEvan Yan if (ret != DDI_SUCCESS) {
42526947304SEvan Yan DDI_HP_IMPLDBG((CE_CONT, "ddihp_cn_getstate: "
42626947304SEvan Yan "CN %p getstate command failed\n", (void *)hdlp));
42726947304SEvan Yan
42826947304SEvan Yan return (ret);
42926947304SEvan Yan }
43026947304SEvan Yan
43126947304SEvan Yan DDI_HP_IMPLDBG((CE_CONT, "ddihp_cn_getstate: hdlp %p "
43226947304SEvan Yan "current Connection state %x new Connection state %x\n",
43326947304SEvan Yan (void *)hdlp, hdlp->cn_info.cn_state, new_state));
43426947304SEvan Yan
43526947304SEvan Yan if (new_state != hdlp->cn_info.cn_state) {
43626947304SEvan Yan hdlp->cn_info.cn_state = new_state;
43726947304SEvan Yan ddihp_update_last_change(hdlp);
43826947304SEvan Yan }
43926947304SEvan Yan
44026947304SEvan Yan return (ret);
44126947304SEvan Yan }
44226947304SEvan Yan
44326947304SEvan Yan /*
44426947304SEvan Yan * Implementation function for unregistering the Hotplug Connection (CN)
44526947304SEvan Yan */
44626947304SEvan Yan int
ddihp_cn_unregister(ddi_hp_cn_handle_t * hdlp)44726947304SEvan Yan ddihp_cn_unregister(ddi_hp_cn_handle_t *hdlp)
44826947304SEvan Yan {
44926947304SEvan Yan dev_info_t *dip = hdlp->cn_dip;
45026947304SEvan Yan
45126947304SEvan Yan DDI_HP_NEXDBG((CE_CONT, "ddihp_cn_unregister: hdlp %p\n",
45226947304SEvan Yan (void *)hdlp));
45326947304SEvan Yan
45426947304SEvan Yan ASSERT(DEVI_BUSY_OWNED(dip));
45526947304SEvan Yan
45626947304SEvan Yan (void) ddihp_cn_getstate(hdlp);
45726947304SEvan Yan
45826947304SEvan Yan if (hdlp->cn_info.cn_state > DDI_HP_CN_STATE_OFFLINE) {
45926947304SEvan Yan DDI_HP_NEXDBG((CE_CONT, "ddihp_cn_unregister: dip %p, hdlp %p "
46026947304SEvan Yan "state %x. Device busy, failed to unregister connection!\n",
46126947304SEvan Yan (void *)dip, (void *)hdlp, hdlp->cn_info.cn_state));
46226947304SEvan Yan
46326947304SEvan Yan return (DDI_EBUSY);
46426947304SEvan Yan }
46526947304SEvan Yan
46626947304SEvan Yan /* unlink the handle */
46726947304SEvan Yan DDIHP_LIST_REMOVE(ddi_hp_cn_handle_t, (DEVI(dip)->devi_hp_hdlp), hdlp);
46826947304SEvan Yan
46926947304SEvan Yan kmem_free(hdlp->cn_info.cn_name, strlen(hdlp->cn_info.cn_name) + 1);
47026947304SEvan Yan kmem_free(hdlp, sizeof (ddi_hp_cn_handle_t));
47126947304SEvan Yan return (DDI_SUCCESS);
47226947304SEvan Yan }
47326947304SEvan Yan
47426947304SEvan Yan /*
47526947304SEvan Yan * For a given Connection name and the dip node where the Connection is
47626947304SEvan Yan * supposed to be, find the corresponding hotplug handle.
47726947304SEvan Yan */
47826947304SEvan Yan ddi_hp_cn_handle_t *
ddihp_cn_name_to_handle(dev_info_t * dip,char * cn_name)47926947304SEvan Yan ddihp_cn_name_to_handle(dev_info_t *dip, char *cn_name)
48026947304SEvan Yan {
48126947304SEvan Yan ddi_hp_cn_handle_t *hdlp;
48226947304SEvan Yan
48326947304SEvan Yan ASSERT(DEVI_BUSY_OWNED(dip));
48426947304SEvan Yan
48526947304SEvan Yan DDI_HP_IMPLDBG((CE_CONT, "ddihp_cn_name_to_handle: "
48626947304SEvan Yan "dip %p cn_name to find: %s", (void *)dip, cn_name));
48726947304SEvan Yan for (hdlp = DEVI(dip)->devi_hp_hdlp; hdlp; hdlp = hdlp->next) {
48826947304SEvan Yan DDI_HP_IMPLDBG((CE_CONT, "ddihp_cn_name_to_handle: "
48926947304SEvan Yan "current cn_name: %s", hdlp->cn_info.cn_name));
49026947304SEvan Yan
49126947304SEvan Yan if (strcmp(cn_name, hdlp->cn_info.cn_name) == 0) {
49226947304SEvan Yan /* found */
49326947304SEvan Yan return (hdlp);
49426947304SEvan Yan }
49526947304SEvan Yan }
49626947304SEvan Yan DDI_HP_IMPLDBG((CE_CONT, "ddihp_cn_name_to_handle: "
49726947304SEvan Yan "failed to find cn_name"));
49826947304SEvan Yan return (NULL);
49926947304SEvan Yan }
50026947304SEvan Yan
50126947304SEvan Yan /*
50226947304SEvan Yan * Process the hotplug operations for Connector and also create Port
50326947304SEvan Yan * upon user command.
50426947304SEvan Yan */
50526947304SEvan Yan int
ddihp_connector_ops(ddi_hp_cn_handle_t * hdlp,ddi_hp_op_t op,void * arg,void * result)50626947304SEvan Yan ddihp_connector_ops(ddi_hp_cn_handle_t *hdlp, ddi_hp_op_t op,
50726947304SEvan Yan void *arg, void *result)
50826947304SEvan Yan {
50926947304SEvan Yan int rv = DDI_SUCCESS;
51026947304SEvan Yan dev_info_t *dip = hdlp->cn_dip;
51126947304SEvan Yan
51226947304SEvan Yan ASSERT(DEVI_BUSY_OWNED(dip));
51326947304SEvan Yan
51426947304SEvan Yan DDI_HP_IMPLDBG((CE_CONT, "ddihp_connector_ops: pdip=%p op=%x "
51526947304SEvan Yan "hdlp=%p arg=%p\n", (void *)dip, op, (void *)hdlp, arg));
51626947304SEvan Yan
51726947304SEvan Yan if (op == DDI_HPOP_CN_CHANGE_STATE) {
51826947304SEvan Yan ddi_hp_cn_state_t target_state = *(ddi_hp_cn_state_t *)arg;
51926947304SEvan Yan
52026947304SEvan Yan rv = ddihp_cn_pre_change_state(hdlp, target_state);
52126947304SEvan Yan if (rv != DDI_SUCCESS) {
52226947304SEvan Yan /* the state is not changed */
52326947304SEvan Yan *((ddi_hp_cn_state_t *)result) =
52426947304SEvan Yan hdlp->cn_info.cn_state;
52526947304SEvan Yan return (rv);
52626947304SEvan Yan }
52726947304SEvan Yan }
52826947304SEvan Yan ASSERT(NEXUS_HAS_HP_OP(dip));
52926947304SEvan Yan rv = (*(DEVI(dip)->devi_ops->devo_bus_ops->bus_hp_op))(
53026947304SEvan Yan dip, hdlp->cn_info.cn_name, op, arg, result);
53126947304SEvan Yan
53226947304SEvan Yan if (rv != DDI_SUCCESS) {
53326947304SEvan Yan DDI_HP_IMPLDBG((CE_CONT, "ddihp_connector_ops: "
53426947304SEvan Yan "bus_hp_op failed: pdip=%p cn_name:%s op=%x "
53526947304SEvan Yan "hdlp=%p arg=%p\n", (void *)dip, hdlp->cn_info.cn_name,
53626947304SEvan Yan op, (void *)hdlp, arg));
53726947304SEvan Yan }
53826947304SEvan Yan if (op == DDI_HPOP_CN_CHANGE_STATE) {
53926947304SEvan Yan int rv_post;
54026947304SEvan Yan
54126947304SEvan Yan DDI_HP_IMPLDBG((CE_CONT, "ddihp_connector_ops: "
54226947304SEvan Yan "old_state=%x, new_state=%x, rv=%x\n",
54326947304SEvan Yan hdlp->cn_info.cn_state, *(ddi_hp_cn_state_t *)result, rv));
54426947304SEvan Yan
54526947304SEvan Yan /*
54626947304SEvan Yan * After state change op is successfully done or
54726947304SEvan Yan * failed at some stages, continue to do some jobs.
54826947304SEvan Yan */
54926947304SEvan Yan rv_post = ddihp_cn_post_change_state(hdlp,
55026947304SEvan Yan *(ddi_hp_cn_state_t *)result);
55126947304SEvan Yan
55226947304SEvan Yan if (rv_post != DDI_SUCCESS)
55326947304SEvan Yan rv = rv_post;
55426947304SEvan Yan }
55526947304SEvan Yan
55626947304SEvan Yan return (rv);
55726947304SEvan Yan }
55826947304SEvan Yan
55926947304SEvan Yan /*
56026947304SEvan Yan * Process the hotplug op for Port
56126947304SEvan Yan */
56226947304SEvan Yan int
ddihp_port_ops(ddi_hp_cn_handle_t * hdlp,ddi_hp_op_t op,void * arg,void * result)56326947304SEvan Yan ddihp_port_ops(ddi_hp_cn_handle_t *hdlp, ddi_hp_op_t op,
56426947304SEvan Yan void *arg, void *result)
56526947304SEvan Yan {
56626947304SEvan Yan int ret = DDI_SUCCESS;
56726947304SEvan Yan
56826947304SEvan Yan ASSERT(DEVI_BUSY_OWNED(hdlp->cn_dip));
56926947304SEvan Yan
57026947304SEvan Yan DDI_HP_IMPLDBG((CE_CONT, "ddihp_port_ops: pdip=%p op=%x hdlp=%p "
57126947304SEvan Yan "arg=%p\n", (void *)hdlp->cn_dip, op, (void *)hdlp, arg));
57226947304SEvan Yan
57326947304SEvan Yan switch (op) {
57426947304SEvan Yan case DDI_HPOP_CN_GET_STATE:
57526947304SEvan Yan {
57626947304SEvan Yan int state;
57726947304SEvan Yan
57826947304SEvan Yan state = hdlp->cn_info.cn_state;
57926947304SEvan Yan
58026947304SEvan Yan if (hdlp->cn_info.cn_child == NULL) {
58126947304SEvan Yan /* No child. Either present or empty. */
58226947304SEvan Yan if (state >= DDI_HP_CN_STATE_PORT_PRESENT)
58326947304SEvan Yan state = DDI_HP_CN_STATE_PORT_PRESENT;
58426947304SEvan Yan else
58526947304SEvan Yan state = DDI_HP_CN_STATE_PORT_EMPTY;
58626947304SEvan Yan
58726947304SEvan Yan } else { /* There is a child of this Port */
58826947304SEvan Yan
58926947304SEvan Yan /* Check DEVI(dip)->devi_node_state */
59026947304SEvan Yan switch (i_ddi_node_state(hdlp->cn_info.cn_child)) {
59126947304SEvan Yan case DS_INVAL:
59226947304SEvan Yan case DS_PROTO:
59326947304SEvan Yan case DS_LINKED:
59426947304SEvan Yan case DS_BOUND:
59526947304SEvan Yan case DS_INITIALIZED:
59626947304SEvan Yan case DS_PROBED:
59726947304SEvan Yan state = DDI_HP_CN_STATE_OFFLINE;
59826947304SEvan Yan break;
59926947304SEvan Yan case DS_ATTACHED:
60026947304SEvan Yan state = DDI_HP_CN_STATE_MAINTENANCE;
60126947304SEvan Yan break;
60226947304SEvan Yan case DS_READY:
60326947304SEvan Yan state = DDI_HP_CN_STATE_ONLINE;
60426947304SEvan Yan break;
60526947304SEvan Yan default:
60626947304SEvan Yan /* should never reach here */
60726947304SEvan Yan ASSERT("unknown devinfo state");
60826947304SEvan Yan }
60926947304SEvan Yan /*
61026947304SEvan Yan * Check DEVI(dip)->devi_state in case the node is
61126947304SEvan Yan * downgraded or quiesced.
61226947304SEvan Yan */
61326947304SEvan Yan if (state == DDI_HP_CN_STATE_ONLINE &&
61426947304SEvan Yan ddi_get_devstate(hdlp->cn_info.cn_child) !=
61526947304SEvan Yan DDI_DEVSTATE_UP)
61626947304SEvan Yan state = DDI_HP_CN_STATE_MAINTENANCE;
61726947304SEvan Yan }
61826947304SEvan Yan
61926947304SEvan Yan *((ddi_hp_cn_state_t *)result) = state;
62026947304SEvan Yan
62126947304SEvan Yan break;
62226947304SEvan Yan }
62326947304SEvan Yan case DDI_HPOP_CN_CHANGE_STATE:
62426947304SEvan Yan {
62526947304SEvan Yan ddi_hp_cn_state_t target_state = *(ddi_hp_cn_state_t *)arg;
62626947304SEvan Yan ddi_hp_cn_state_t curr_state = hdlp->cn_info.cn_state;
62726947304SEvan Yan
62826947304SEvan Yan ret = ddihp_port_change_state(hdlp, target_state);
62926947304SEvan Yan if (curr_state != hdlp->cn_info.cn_state) {
63026947304SEvan Yan ddihp_update_last_change(hdlp);
63126947304SEvan Yan }
63226947304SEvan Yan *((ddi_hp_cn_state_t *)result) = hdlp->cn_info.cn_state;
63326947304SEvan Yan
63426947304SEvan Yan break;
63526947304SEvan Yan }
63626947304SEvan Yan case DDI_HPOP_CN_REMOVE_PORT:
63726947304SEvan Yan {
63826947304SEvan Yan (void) ddihp_cn_getstate(hdlp);
63926947304SEvan Yan
64026947304SEvan Yan if (hdlp->cn_info.cn_state != DDI_HP_CN_STATE_PORT_EMPTY) {
64126947304SEvan Yan /* Only empty PORT can be removed by commands */
64226947304SEvan Yan ret = DDI_EBUSY;
64326947304SEvan Yan
64426947304SEvan Yan break;
64526947304SEvan Yan }
64626947304SEvan Yan
64726947304SEvan Yan ret = ddihp_cn_unregister(hdlp);
64826947304SEvan Yan break;
64926947304SEvan Yan }
65026947304SEvan Yan default:
65126947304SEvan Yan ret = DDI_ENOTSUP;
65226947304SEvan Yan break;
65326947304SEvan Yan }
65426947304SEvan Yan
65526947304SEvan Yan return (ret);
65626947304SEvan Yan }
65726947304SEvan Yan
65826947304SEvan Yan /*
65926947304SEvan Yan * Generate the system event with a possible hint
66026947304SEvan Yan */
66126947304SEvan Yan /* ARGSUSED */
66226947304SEvan Yan void
ddihp_cn_gen_sysevent(ddi_hp_cn_handle_t * hdlp,ddi_hp_cn_sysevent_t event_sub_class,int hint,int kmflag)66326947304SEvan Yan ddihp_cn_gen_sysevent(ddi_hp_cn_handle_t *hdlp,
66426947304SEvan Yan ddi_hp_cn_sysevent_t event_sub_class, int hint, int kmflag)
66526947304SEvan Yan {
66626947304SEvan Yan dev_info_t *dip = hdlp->cn_dip;
66726947304SEvan Yan char *cn_path, *ap_id;
66826947304SEvan Yan char *ev_subclass = NULL;
66926947304SEvan Yan nvlist_t *ev_attr_list = NULL;
67026947304SEvan Yan sysevent_id_t eid;
67126947304SEvan Yan int ap_id_len, err;
67226947304SEvan Yan
67326947304SEvan Yan cn_path = kmem_zalloc(MAXPATHLEN, kmflag);
67426947304SEvan Yan if (cn_path == NULL) {
67526947304SEvan Yan cmn_err(CE_WARN,
67626947304SEvan Yan "%s%d: Failed to allocate memory for hotplug"
67726947304SEvan Yan " connection: %s\n",
67826947304SEvan Yan ddi_driver_name(dip), ddi_get_instance(dip),
67926947304SEvan Yan hdlp->cn_info.cn_name);
68026947304SEvan Yan
68126947304SEvan Yan return;
68226947304SEvan Yan }
68326947304SEvan Yan
68426947304SEvan Yan /*
68526947304SEvan Yan * Minor device name will be bus path
68626947304SEvan Yan * concatenated with connection name.
68726947304SEvan Yan * One of consumers of the sysevent will pass it
68826947304SEvan Yan * to cfgadm as AP ID.
68926947304SEvan Yan */
69026947304SEvan Yan (void) strcpy(cn_path, "/devices");
69126947304SEvan Yan (void) ddi_pathname(dip, cn_path + strlen("/devices"));
69226947304SEvan Yan
69326947304SEvan Yan ap_id_len = strlen(cn_path) + strlen(":") +
69426947304SEvan Yan strlen(hdlp->cn_info.cn_name) + 1;
69526947304SEvan Yan ap_id = kmem_zalloc(ap_id_len, kmflag);
69626947304SEvan Yan if (ap_id == NULL) {
69726947304SEvan Yan cmn_err(CE_WARN,
69826947304SEvan Yan "%s%d: Failed to allocate memory for AP ID: %s:%s\n",
69926947304SEvan Yan ddi_driver_name(dip), ddi_get_instance(dip),
70026947304SEvan Yan cn_path, hdlp->cn_info.cn_name);
70126947304SEvan Yan kmem_free(cn_path, MAXPATHLEN);
70226947304SEvan Yan
70326947304SEvan Yan return;
70426947304SEvan Yan }
70526947304SEvan Yan
70626947304SEvan Yan (void) strcpy(ap_id, cn_path);
70726947304SEvan Yan (void) strcat(ap_id, ":");
70826947304SEvan Yan (void) strcat(ap_id, hdlp->cn_info.cn_name);
70926947304SEvan Yan kmem_free(cn_path, MAXPATHLEN);
71026947304SEvan Yan
71126947304SEvan Yan err = nvlist_alloc(&ev_attr_list, NV_UNIQUE_NAME_TYPE, kmflag);
71226947304SEvan Yan
71326947304SEvan Yan if (err != 0) {
71426947304SEvan Yan cmn_err(CE_WARN,
71526947304SEvan Yan "%s%d: Failed to allocate memory for event subclass %d\n",
71626947304SEvan Yan ddi_driver_name(dip), ddi_get_instance(dip),
71726947304SEvan Yan event_sub_class);
71826947304SEvan Yan kmem_free(ap_id, ap_id_len);
71926947304SEvan Yan
72026947304SEvan Yan return;
72126947304SEvan Yan }
72226947304SEvan Yan
72326947304SEvan Yan switch (event_sub_class) {
72426947304SEvan Yan case DDI_HP_CN_STATE_CHANGE:
72526947304SEvan Yan ev_subclass = ESC_DR_AP_STATE_CHANGE;
72626947304SEvan Yan
72726947304SEvan Yan switch (hint) {
72826947304SEvan Yan case SE_NO_HINT: /* fall through */
72926947304SEvan Yan case SE_HINT_INSERT: /* fall through */
73026947304SEvan Yan case SE_HINT_REMOVE:
73126947304SEvan Yan err = nvlist_add_string(ev_attr_list, DR_HINT,
73226947304SEvan Yan SE_HINT2STR(hint));
73326947304SEvan Yan
73426947304SEvan Yan if (err != 0) {
73526947304SEvan Yan cmn_err(CE_WARN, "%s%d: Failed to add attr [%s]"
73626947304SEvan Yan " for %s event\n", ddi_driver_name(dip),
73726947304SEvan Yan ddi_get_instance(dip), DR_HINT,
73826947304SEvan Yan ESC_DR_AP_STATE_CHANGE);
73926947304SEvan Yan
74026947304SEvan Yan goto done;
74126947304SEvan Yan }
74226947304SEvan Yan break;
74326947304SEvan Yan
74426947304SEvan Yan default:
74526947304SEvan Yan cmn_err(CE_WARN, "%s%d: Unknown hint on sysevent\n",
74626947304SEvan Yan ddi_driver_name(dip), ddi_get_instance(dip));
74726947304SEvan Yan
74826947304SEvan Yan goto done;
74926947304SEvan Yan }
75026947304SEvan Yan
75126947304SEvan Yan break;
75226947304SEvan Yan
75326947304SEvan Yan /* event sub class: DDI_HP_CN_REQ */
75426947304SEvan Yan case DDI_HP_CN_REQ:
75526947304SEvan Yan ev_subclass = ESC_DR_REQ;
75626947304SEvan Yan
75726947304SEvan Yan switch (hint) {
75826947304SEvan Yan case SE_INVESTIGATE_RES: /* fall through */
75926947304SEvan Yan case SE_INCOMING_RES: /* fall through */
76026947304SEvan Yan case SE_OUTGOING_RES: /* fall through */
76126947304SEvan Yan err = nvlist_add_string(ev_attr_list, DR_REQ_TYPE,
76226947304SEvan Yan SE_REQ2STR(hint));
76326947304SEvan Yan
76426947304SEvan Yan if (err != 0) {
76526947304SEvan Yan cmn_err(CE_WARN,
76626947304SEvan Yan "%s%d: Failed to add attr [%s] for %s \n"
76726947304SEvan Yan "event", ddi_driver_name(dip),
76826947304SEvan Yan ddi_get_instance(dip),
76926947304SEvan Yan DR_REQ_TYPE, ESC_DR_REQ);
77026947304SEvan Yan
77126947304SEvan Yan goto done;
77226947304SEvan Yan }
77326947304SEvan Yan break;
77426947304SEvan Yan
77526947304SEvan Yan default:
77626947304SEvan Yan cmn_err(CE_WARN, "%s%d: Unknown hint on sysevent\n",
77726947304SEvan Yan ddi_driver_name(dip), ddi_get_instance(dip));
77826947304SEvan Yan
77926947304SEvan Yan goto done;
78026947304SEvan Yan }
78126947304SEvan Yan
78226947304SEvan Yan break;
78326947304SEvan Yan
78426947304SEvan Yan default:
78526947304SEvan Yan cmn_err(CE_WARN, "%s%d: Unknown Event subclass\n",
78626947304SEvan Yan ddi_driver_name(dip), ddi_get_instance(dip));
78726947304SEvan Yan
78826947304SEvan Yan goto done;
78926947304SEvan Yan }
79026947304SEvan Yan
79126947304SEvan Yan /*
79226947304SEvan Yan * Add Hotplug Connection (CN) as attribute (common attribute)
79326947304SEvan Yan */
79426947304SEvan Yan err = nvlist_add_string(ev_attr_list, DR_AP_ID, ap_id);
79526947304SEvan Yan if (err != 0) {
79626947304SEvan Yan cmn_err(CE_WARN, "%s%d: Failed to add attr [%s] for %s event\n",
79726947304SEvan Yan ddi_driver_name(dip), ddi_get_instance(dip),
79826947304SEvan Yan DR_AP_ID, EC_DR);
79926947304SEvan Yan
80026947304SEvan Yan goto done;
80126947304SEvan Yan }
80226947304SEvan Yan
80326947304SEvan Yan /*
80426947304SEvan Yan * Log this event with sysevent framework.
80526947304SEvan Yan */
80626947304SEvan Yan err = ddi_log_sysevent(dip, DDI_VENDOR_SUNW, EC_DR,
80726947304SEvan Yan ev_subclass, ev_attr_list, &eid,
80826947304SEvan Yan ((kmflag == KM_SLEEP) ? DDI_SLEEP : DDI_NOSLEEP));
80926947304SEvan Yan
81026947304SEvan Yan if (err != 0) {
81126947304SEvan Yan cmn_err(CE_WARN, "%s%d: Failed to log %s event\n",
81226947304SEvan Yan ddi_driver_name(dip), ddi_get_instance(dip), EC_DR);
81326947304SEvan Yan }
81426947304SEvan Yan
81526947304SEvan Yan done:
81626947304SEvan Yan nvlist_free(ev_attr_list);
81726947304SEvan Yan kmem_free(ap_id, ap_id_len);
81826947304SEvan Yan }
81926947304SEvan Yan
82026947304SEvan Yan /*
82126947304SEvan Yan * Local functions (called within this file)
82226947304SEvan Yan */
82326947304SEvan Yan
82426947304SEvan Yan /*
82526947304SEvan Yan * Connector operations
82626947304SEvan Yan */
82726947304SEvan Yan
82826947304SEvan Yan /*
82926947304SEvan Yan * Prepare to change state for a Connector: offline, unprobe, etc.
83026947304SEvan Yan */
83126947304SEvan Yan static int
ddihp_cn_pre_change_state(ddi_hp_cn_handle_t * hdlp,ddi_hp_cn_state_t target_state)83226947304SEvan Yan ddihp_cn_pre_change_state(ddi_hp_cn_handle_t *hdlp,
83326947304SEvan Yan ddi_hp_cn_state_t target_state)
83426947304SEvan Yan {
83526947304SEvan Yan ddi_hp_cn_state_t curr_state = hdlp->cn_info.cn_state;
83626947304SEvan Yan dev_info_t *dip = hdlp->cn_dip;
83726947304SEvan Yan int rv = DDI_SUCCESS;
83826947304SEvan Yan
83926947304SEvan Yan if (curr_state > target_state &&
84026947304SEvan Yan curr_state == DDI_HP_CN_STATE_ENABLED) {
84126947304SEvan Yan /*
84226947304SEvan Yan * If the Connection goes to a lower state from ENABLED,
843ffb64830SJordan Paige Hendricks * then offline all children under it.
84426947304SEvan Yan */
84526947304SEvan Yan rv = ddihp_cn_change_children_state(hdlp, B_FALSE);
84626947304SEvan Yan if (rv != DDI_SUCCESS) {
84726947304SEvan Yan cmn_err(CE_WARN,
84826947304SEvan Yan "(%s%d): "
84926947304SEvan Yan "failed to unconfigure the device in the"
85026947304SEvan Yan " Connection %s\n", ddi_driver_name(dip),
85126947304SEvan Yan ddi_get_instance(dip),
85226947304SEvan Yan hdlp->cn_info.cn_name);
85326947304SEvan Yan
85426947304SEvan Yan return (rv);
85526947304SEvan Yan }
85626947304SEvan Yan ASSERT(NEXUS_HAS_HP_OP(dip));
85726947304SEvan Yan /*
85826947304SEvan Yan * Remove all the children and their ports
85926947304SEvan Yan * after they are offlined.
86026947304SEvan Yan */
86126947304SEvan Yan rv = (*(DEVI(dip)->devi_ops->devo_bus_ops->bus_hp_op))(
86226947304SEvan Yan dip, hdlp->cn_info.cn_name, DDI_HPOP_CN_UNPROBE,
86326947304SEvan Yan NULL, NULL);
86426947304SEvan Yan if (rv != DDI_SUCCESS) {
86526947304SEvan Yan cmn_err(CE_WARN,
86626947304SEvan Yan "(%s%d): failed"
86726947304SEvan Yan " to unprobe the device in the Connector"
86826947304SEvan Yan " %s\n", ddi_driver_name(dip),
86926947304SEvan Yan ddi_get_instance(dip),
87026947304SEvan Yan hdlp->cn_info.cn_name);
87126947304SEvan Yan
87226947304SEvan Yan return (rv);
87326947304SEvan Yan }
87426947304SEvan Yan
87526947304SEvan Yan DDI_HP_NEXDBG((CE_CONT,
87626947304SEvan Yan "ddihp_connector_ops (%s%d): device"
87726947304SEvan Yan " is unconfigured and unprobed in Connector %s\n",
87826947304SEvan Yan ddi_driver_name(dip), ddi_get_instance(dip),
87926947304SEvan Yan hdlp->cn_info.cn_name));
88026947304SEvan Yan }
88126947304SEvan Yan
88226947304SEvan Yan return (rv);
88326947304SEvan Yan }
88426947304SEvan Yan
88526947304SEvan Yan /*
886ffb64830SJordan Paige Hendricks * Jobs after change state of a Connector: update state, last change time,
88726947304SEvan Yan * probe, online, sysevent, etc.
88826947304SEvan Yan */
88926947304SEvan Yan static int
ddihp_cn_post_change_state(ddi_hp_cn_handle_t * hdlp,ddi_hp_cn_state_t new_state)89026947304SEvan Yan ddihp_cn_post_change_state(ddi_hp_cn_handle_t *hdlp,
89126947304SEvan Yan ddi_hp_cn_state_t new_state)
89226947304SEvan Yan {
89326947304SEvan Yan int rv = DDI_SUCCESS;
89426947304SEvan Yan ddi_hp_cn_state_t curr_state = hdlp->cn_info.cn_state;
89526947304SEvan Yan
89626947304SEvan Yan /* Update the state in handle */
89726947304SEvan Yan if (new_state != curr_state) {
89826947304SEvan Yan hdlp->cn_info.cn_state = new_state;
89926947304SEvan Yan ddihp_update_last_change(hdlp);
90026947304SEvan Yan }
90126947304SEvan Yan
90226947304SEvan Yan if (curr_state < new_state &&
90326947304SEvan Yan new_state == DDI_HP_CN_STATE_ENABLED) {
90426947304SEvan Yan /*
90526947304SEvan Yan * Probe and online devices if state is
90626947304SEvan Yan * upgraded to ENABLED.
90726947304SEvan Yan */
90826947304SEvan Yan rv = ddihp_cn_handle_state_change(hdlp);
90926947304SEvan Yan }
91026947304SEvan Yan if (curr_state != hdlp->cn_info.cn_state) {
91126947304SEvan Yan /*
91226947304SEvan Yan * For Connector, generate a sysevent on
91326947304SEvan Yan * state change.
91426947304SEvan Yan */
91526947304SEvan Yan ddihp_cn_gen_sysevent(hdlp, DDI_HP_CN_STATE_CHANGE,
91626947304SEvan Yan SE_NO_HINT, KM_SLEEP);
91726947304SEvan Yan }
91826947304SEvan Yan
91926947304SEvan Yan return (rv);
92026947304SEvan Yan }
92126947304SEvan Yan
92226947304SEvan Yan /*
92326947304SEvan Yan * Handle Connector state change.
92426947304SEvan Yan *
92526947304SEvan Yan * This function is called after connector is upgraded to ENABLED sate.
92626947304SEvan Yan * It probes the device plugged in the connector to setup devinfo nodes
92726947304SEvan Yan * and then online the nodes.
92826947304SEvan Yan */
92926947304SEvan Yan static int
ddihp_cn_handle_state_change(ddi_hp_cn_handle_t * hdlp)93026947304SEvan Yan ddihp_cn_handle_state_change(ddi_hp_cn_handle_t *hdlp)
93126947304SEvan Yan {
93226947304SEvan Yan dev_info_t *dip = hdlp->cn_dip;
93326947304SEvan Yan int rv = DDI_SUCCESS;
93426947304SEvan Yan
93526947304SEvan Yan ASSERT(DEVI_BUSY_OWNED(dip));
93626947304SEvan Yan ASSERT(NEXUS_HAS_HP_OP(dip));
93726947304SEvan Yan /*
93826947304SEvan Yan * If the Connection went to state ENABLED from a lower state,
93926947304SEvan Yan * probe it.
94026947304SEvan Yan */
94126947304SEvan Yan rv = (*(DEVI(dip)->devi_ops->devo_bus_ops->bus_hp_op))(
94226947304SEvan Yan dip, hdlp->cn_info.cn_name, DDI_HPOP_CN_PROBE, NULL, NULL);
94326947304SEvan Yan
94426947304SEvan Yan if (rv != DDI_SUCCESS) {
94526947304SEvan Yan ddi_hp_cn_state_t target_state = DDI_HP_CN_STATE_POWERED;
94626947304SEvan Yan ddi_hp_cn_state_t result_state = 0;
94726947304SEvan Yan
94826947304SEvan Yan /*
94926947304SEvan Yan * Probe failed. Disable the connector so that it can
95026947304SEvan Yan * be enabled again by a later try from userland.
95126947304SEvan Yan */
95226947304SEvan Yan (void) (*(DEVI(dip)->devi_ops->devo_bus_ops->bus_hp_op))(
95326947304SEvan Yan dip, hdlp->cn_info.cn_name, DDI_HPOP_CN_CHANGE_STATE,
95426947304SEvan Yan (void *)&target_state, (void *)&result_state);
95526947304SEvan Yan
95626947304SEvan Yan if (result_state && result_state != hdlp->cn_info.cn_state) {
95726947304SEvan Yan hdlp->cn_info.cn_state = result_state;
95826947304SEvan Yan ddihp_update_last_change(hdlp);
95926947304SEvan Yan }
96026947304SEvan Yan
96126947304SEvan Yan cmn_err(CE_WARN,
96226947304SEvan Yan "(%s%d): failed to probe the Connection %s\n",
96326947304SEvan Yan ddi_driver_name(dip), ddi_get_instance(dip),
96426947304SEvan Yan hdlp->cn_info.cn_name);
96526947304SEvan Yan
96626947304SEvan Yan return (rv);
96726947304SEvan Yan }
96826947304SEvan Yan /*
96926947304SEvan Yan * Try to online all the children of CN.
97026947304SEvan Yan */
97126947304SEvan Yan (void) ddihp_cn_change_children_state(hdlp, B_TRUE);
97226947304SEvan Yan
97326947304SEvan Yan DDI_HP_NEXDBG((CE_CONT, "ddihp_cn_event_handler (%s%d): "
97426947304SEvan Yan "device is configured in the Connection %s\n",
97526947304SEvan Yan ddi_driver_name(dip), ddi_get_instance(dip),
97626947304SEvan Yan hdlp->cn_info.cn_name));
97726947304SEvan Yan return (rv);
97826947304SEvan Yan }
97926947304SEvan Yan
98026947304SEvan Yan /*
98126947304SEvan Yan * Online/Offline all the children under the Hotplug Connection (CN)
98226947304SEvan Yan *
98326947304SEvan Yan * Do online operation when the online parameter is true; otherwise do offline.
98426947304SEvan Yan */
98526947304SEvan Yan static int
ddihp_cn_change_children_state(ddi_hp_cn_handle_t * hdlp,boolean_t online)98626947304SEvan Yan ddihp_cn_change_children_state(ddi_hp_cn_handle_t *hdlp, boolean_t online)
98726947304SEvan Yan {
98826947304SEvan Yan dev_info_t *dip = hdlp->cn_dip;
98926947304SEvan Yan dev_info_t *cdip;
99026947304SEvan Yan ddi_hp_cn_handle_t *h;
99126947304SEvan Yan int rv = DDI_SUCCESS;
99226947304SEvan Yan
99326947304SEvan Yan DDI_HP_IMPLDBG((CE_CONT, "ddihp_cn_change_children_state:"
99426947304SEvan Yan " dip %p hdlp %p, online %x\n",
99526947304SEvan Yan (void *)dip, (void *)hdlp, online));
99626947304SEvan Yan
99726947304SEvan Yan ASSERT(DEVI_BUSY_OWNED(dip));
99826947304SEvan Yan
99926947304SEvan Yan /*
100026947304SEvan Yan * Return invalid if Connection state is < DDI_HP_CN_STATE_ENABLED
100126947304SEvan Yan * when try to online children.
100226947304SEvan Yan */
100326947304SEvan Yan if (online && hdlp->cn_info.cn_state < DDI_HP_CN_STATE_ENABLED) {
100426947304SEvan Yan DDI_HP_IMPLDBG((CE_CONT, "ddihp_cn_change_children_state: "
100526947304SEvan Yan "Connector %p is not in probed state\n", (void *)hdlp));
100626947304SEvan Yan
100726947304SEvan Yan return (DDI_EINVAL);
100826947304SEvan Yan }
100926947304SEvan Yan
101026947304SEvan Yan /* Now, online/offline all the devices depending on the Connector */
101126947304SEvan Yan
101226947304SEvan Yan if (!online) {
101326947304SEvan Yan /*
101426947304SEvan Yan * For offline operation we need to firstly clean up devfs
101526947304SEvan Yan * so as not to prevent driver detach.
101626947304SEvan Yan */
101726947304SEvan Yan (void) devfs_clean(dip, NULL, DV_CLEAN_FORCE);
101826947304SEvan Yan }
101926947304SEvan Yan for (h = DEVI(dip)->devi_hp_hdlp; h; h = h->next) {
102026947304SEvan Yan if (h->cn_info.cn_type != DDI_HP_CN_TYPE_VIRTUAL_PORT)
102126947304SEvan Yan continue;
102226947304SEvan Yan
102326947304SEvan Yan if (h->cn_info.cn_num_dpd_on !=
102426947304SEvan Yan hdlp->cn_info.cn_num)
102526947304SEvan Yan continue;
102626947304SEvan Yan
102726947304SEvan Yan cdip = h->cn_info.cn_child;
102826947304SEvan Yan ASSERT(cdip);
102926947304SEvan Yan if (online) {
103026947304SEvan Yan /* online children */
103126947304SEvan Yan if (!ddihp_check_status_prop(dip))
103226947304SEvan Yan continue;
103326947304SEvan Yan
103426947304SEvan Yan if (ndi_devi_online(cdip,
103526947304SEvan Yan NDI_ONLINE_ATTACH | NDI_CONFIG) != NDI_SUCCESS) {
103626947304SEvan Yan cmn_err(CE_WARN,
103726947304SEvan Yan "(%s%d):"
103826947304SEvan Yan " failed to attach driver for a device"
103926947304SEvan Yan " (%s%d) under the Connection %s\n",
104026947304SEvan Yan ddi_driver_name(dip), ddi_get_instance(dip),
104126947304SEvan Yan ddi_driver_name(cdip),
104226947304SEvan Yan ddi_get_instance(cdip),
104326947304SEvan Yan hdlp->cn_info.cn_name);
104426947304SEvan Yan /*
104526947304SEvan Yan * One of the devices failed to online, but we
104626947304SEvan Yan * want to continue to online the rest siblings
104726947304SEvan Yan * after mark the failure here.
104826947304SEvan Yan */
104926947304SEvan Yan rv = DDI_FAILURE;
105026947304SEvan Yan
105126947304SEvan Yan continue;
105226947304SEvan Yan }
105326947304SEvan Yan } else {
105426947304SEvan Yan /* offline children */
105526947304SEvan Yan if (ndi_devi_offline(cdip, NDI_UNCONFIG) !=
105626947304SEvan Yan NDI_SUCCESS) {
105726947304SEvan Yan cmn_err(CE_WARN,
105826947304SEvan Yan "(%s%d):"
1059ffb64830SJordan Paige Hendricks " failed to detach driver for the device"
106026947304SEvan Yan " (%s%d) in the Connection %s\n",
106126947304SEvan Yan ddi_driver_name(dip), ddi_get_instance(dip),
106226947304SEvan Yan ddi_driver_name(cdip),
106326947304SEvan Yan ddi_get_instance(cdip),
106426947304SEvan Yan hdlp->cn_info.cn_name);
106526947304SEvan Yan
106626947304SEvan Yan return (DDI_EBUSY);
106726947304SEvan Yan }
106826947304SEvan Yan }
106926947304SEvan Yan }
107026947304SEvan Yan
107126947304SEvan Yan return (rv);
107226947304SEvan Yan }
107326947304SEvan Yan
107426947304SEvan Yan /*
107526947304SEvan Yan * Port operations
107626947304SEvan Yan */
107726947304SEvan Yan
107826947304SEvan Yan /*
107926947304SEvan Yan * Change Port state to target_state.
108026947304SEvan Yan */
108126947304SEvan Yan static int
ddihp_port_change_state(ddi_hp_cn_handle_t * hdlp,ddi_hp_cn_state_t target_state)108226947304SEvan Yan ddihp_port_change_state(ddi_hp_cn_handle_t *hdlp,
108326947304SEvan Yan ddi_hp_cn_state_t target_state)
108426947304SEvan Yan {
108526947304SEvan Yan ddi_hp_cn_state_t curr_state = hdlp->cn_info.cn_state;
108626947304SEvan Yan
108726947304SEvan Yan if (target_state < DDI_HP_CN_STATE_PORT_EMPTY ||
108826947304SEvan Yan target_state > DDI_HP_CN_STATE_ONLINE) {
108926947304SEvan Yan
109026947304SEvan Yan return (DDI_EINVAL);
109126947304SEvan Yan }
109226947304SEvan Yan
109326947304SEvan Yan if (curr_state < target_state)
109426947304SEvan Yan return (ddihp_port_upgrade_state(hdlp, target_state));
109526947304SEvan Yan else if (curr_state > target_state)
109626947304SEvan Yan return (ddihp_port_downgrade_state(hdlp, target_state));
109726947304SEvan Yan else
109826947304SEvan Yan return (DDI_SUCCESS);
109926947304SEvan Yan }
110026947304SEvan Yan
110126947304SEvan Yan /*
110226947304SEvan Yan * Upgrade port state to target_state.
110326947304SEvan Yan */
110426947304SEvan Yan static int
ddihp_port_upgrade_state(ddi_hp_cn_handle_t * hdlp,ddi_hp_cn_state_t target_state)110526947304SEvan Yan ddihp_port_upgrade_state(ddi_hp_cn_handle_t *hdlp,
110626947304SEvan Yan ddi_hp_cn_state_t target_state)
110726947304SEvan Yan {
110826947304SEvan Yan ddi_hp_cn_state_t curr_state, new_state, result_state;
110926947304SEvan Yan dev_info_t *cdip;
111026947304SEvan Yan int rv = DDI_SUCCESS;
111126947304SEvan Yan
111226947304SEvan Yan curr_state = hdlp->cn_info.cn_state;
111326947304SEvan Yan while (curr_state < target_state) {
111426947304SEvan Yan switch (curr_state) {
111526947304SEvan Yan case DDI_HP_CN_STATE_PORT_EMPTY:
111626947304SEvan Yan /* Check the existence of the corresponding hardware */
111726947304SEvan Yan new_state = DDI_HP_CN_STATE_PORT_PRESENT;
111826947304SEvan Yan rv = ddihp_connector_ops(hdlp,
111926947304SEvan Yan DDI_HPOP_CN_CHANGE_STATE,
112026947304SEvan Yan (void *)&new_state, (void *)&result_state);
112126947304SEvan Yan if (rv == DDI_SUCCESS) {
112226947304SEvan Yan hdlp->cn_info.cn_state =
112326947304SEvan Yan result_state;
112426947304SEvan Yan }
112526947304SEvan Yan break;
112626947304SEvan Yan case DDI_HP_CN_STATE_PORT_PRESENT:
112726947304SEvan Yan /* Read-only probe the corresponding hardware. */
112826947304SEvan Yan new_state = DDI_HP_CN_STATE_OFFLINE;
112926947304SEvan Yan rv = ddihp_connector_ops(hdlp,
113026947304SEvan Yan DDI_HPOP_CN_CHANGE_STATE,
113126947304SEvan Yan (void *)&new_state, &cdip);
113226947304SEvan Yan if (rv == DDI_SUCCESS) {
113326947304SEvan Yan hdlp->cn_info.cn_state =
113426947304SEvan Yan DDI_HP_CN_STATE_OFFLINE;
113526947304SEvan Yan
113626947304SEvan Yan ASSERT(hdlp->cn_info.cn_child == NULL);
113726947304SEvan Yan hdlp->cn_info.cn_child = cdip;
113826947304SEvan Yan }
113926947304SEvan Yan break;
114026947304SEvan Yan case DDI_HP_CN_STATE_OFFLINE:
114126947304SEvan Yan /* fall through */
114226947304SEvan Yan case DDI_HP_CN_STATE_MAINTENANCE:
114326947304SEvan Yan
114426947304SEvan Yan cdip = hdlp->cn_info.cn_child;
114526947304SEvan Yan
114626947304SEvan Yan rv = ndi_devi_online(cdip,
114726947304SEvan Yan NDI_ONLINE_ATTACH | NDI_CONFIG);
114826947304SEvan Yan if (rv == NDI_SUCCESS) {
114926947304SEvan Yan hdlp->cn_info.cn_state =
115026947304SEvan Yan DDI_HP_CN_STATE_ONLINE;
115126947304SEvan Yan rv = DDI_SUCCESS;
115226947304SEvan Yan } else {
115326947304SEvan Yan rv = DDI_FAILURE;
115426947304SEvan Yan DDI_HP_IMPLDBG((CE_CONT,
115526947304SEvan Yan "ddihp_port_upgrade_state: "
115626947304SEvan Yan "failed to online device %p at port: %s\n",
115726947304SEvan Yan (void *)cdip, hdlp->cn_info.cn_name));
115826947304SEvan Yan }
115926947304SEvan Yan break;
116026947304SEvan Yan case DDI_HP_CN_STATE_ONLINE:
116126947304SEvan Yan
116226947304SEvan Yan break;
116326947304SEvan Yan default:
116426947304SEvan Yan /* should never reach here */
116526947304SEvan Yan ASSERT("unknown devinfo state");
116626947304SEvan Yan }
116726947304SEvan Yan curr_state = hdlp->cn_info.cn_state;
116826947304SEvan Yan if (rv != DDI_SUCCESS) {
116926947304SEvan Yan DDI_HP_IMPLDBG((CE_CONT, "ddihp_port_upgrade_state: "
117026947304SEvan Yan "failed curr_state=%x, target_state=%x \n",
117126947304SEvan Yan curr_state, target_state));
117226947304SEvan Yan return (rv);
117326947304SEvan Yan }
117426947304SEvan Yan }
117526947304SEvan Yan
117626947304SEvan Yan return (rv);
117726947304SEvan Yan }
117826947304SEvan Yan
117926947304SEvan Yan /*
118026947304SEvan Yan * Downgrade state to target_state
118126947304SEvan Yan */
118226947304SEvan Yan static int
ddihp_port_downgrade_state(ddi_hp_cn_handle_t * hdlp,ddi_hp_cn_state_t target_state)118326947304SEvan Yan ddihp_port_downgrade_state(ddi_hp_cn_handle_t *hdlp,
118426947304SEvan Yan ddi_hp_cn_state_t target_state)
118526947304SEvan Yan {
118626947304SEvan Yan ddi_hp_cn_state_t curr_state, new_state, result_state;
118726947304SEvan Yan dev_info_t *dip = hdlp->cn_dip;
118826947304SEvan Yan dev_info_t *cdip;
118926947304SEvan Yan int rv = DDI_SUCCESS;
119026947304SEvan Yan
119126947304SEvan Yan curr_state = hdlp->cn_info.cn_state;
119226947304SEvan Yan while (curr_state > target_state) {
119326947304SEvan Yan
119426947304SEvan Yan switch (curr_state) {
119526947304SEvan Yan case DDI_HP_CN_STATE_PORT_EMPTY:
119626947304SEvan Yan
119726947304SEvan Yan break;
119826947304SEvan Yan case DDI_HP_CN_STATE_PORT_PRESENT:
119926947304SEvan Yan /* Check the existence of the corresponding hardware */
120026947304SEvan Yan new_state = DDI_HP_CN_STATE_PORT_EMPTY;
120126947304SEvan Yan rv = ddihp_connector_ops(hdlp,
120226947304SEvan Yan DDI_HPOP_CN_CHANGE_STATE,
120326947304SEvan Yan (void *)&new_state, (void *)&result_state);
120426947304SEvan Yan if (rv == DDI_SUCCESS)
120526947304SEvan Yan hdlp->cn_info.cn_state =
120626947304SEvan Yan result_state;
120726947304SEvan Yan
120826947304SEvan Yan break;
120926947304SEvan Yan case DDI_HP_CN_STATE_OFFLINE:
121026947304SEvan Yan /*
121126947304SEvan Yan * Read-only unprobe the corresponding hardware:
121226947304SEvan Yan * 1. release the assigned resource;
121326947304SEvan Yan * 2. remove the node pointed by the port's cn_child
121426947304SEvan Yan */
121526947304SEvan Yan new_state = DDI_HP_CN_STATE_PORT_PRESENT;
121626947304SEvan Yan rv = ddihp_connector_ops(hdlp,
121726947304SEvan Yan DDI_HPOP_CN_CHANGE_STATE,
121826947304SEvan Yan (void *)&new_state, (void *)&result_state);
121926947304SEvan Yan if (rv == DDI_SUCCESS)
122026947304SEvan Yan hdlp->cn_info.cn_state =
122126947304SEvan Yan DDI_HP_CN_STATE_PORT_PRESENT;
122226947304SEvan Yan break;
122326947304SEvan Yan case DDI_HP_CN_STATE_MAINTENANCE:
122426947304SEvan Yan /* fall through. */
122526947304SEvan Yan case DDI_HP_CN_STATE_ONLINE:
122626947304SEvan Yan cdip = hdlp->cn_info.cn_child;
122726947304SEvan Yan
122826947304SEvan Yan (void) devfs_clean(dip, NULL, DV_CLEAN_FORCE);
122926947304SEvan Yan rv = ndi_devi_offline(cdip, NDI_UNCONFIG);
123026947304SEvan Yan if (rv == NDI_SUCCESS) {
123126947304SEvan Yan hdlp->cn_info.cn_state =
123226947304SEvan Yan DDI_HP_CN_STATE_OFFLINE;
123326947304SEvan Yan rv = DDI_SUCCESS;
123426947304SEvan Yan } else {
123526947304SEvan Yan rv = DDI_EBUSY;
123626947304SEvan Yan DDI_HP_IMPLDBG((CE_CONT,
123726947304SEvan Yan "ddihp_port_downgrade_state: failed "
123826947304SEvan Yan "to offline node, rv=%x, cdip=%p \n",
123926947304SEvan Yan rv, (void *)cdip));
124026947304SEvan Yan }
124126947304SEvan Yan
124226947304SEvan Yan break;
124326947304SEvan Yan default:
124426947304SEvan Yan /* should never reach here */
124526947304SEvan Yan ASSERT("unknown devinfo state");
124626947304SEvan Yan }
124726947304SEvan Yan curr_state = hdlp->cn_info.cn_state;
124826947304SEvan Yan if (rv != DDI_SUCCESS) {
124926947304SEvan Yan DDI_HP_IMPLDBG((CE_CONT,
125026947304SEvan Yan "ddihp_port_downgrade_state: failed "
125126947304SEvan Yan "curr_state=%x, target_state=%x \n",
125226947304SEvan Yan curr_state, target_state));
125326947304SEvan Yan return (rv);
125426947304SEvan Yan }
125526947304SEvan Yan }
125626947304SEvan Yan
125726947304SEvan Yan return (rv);
125826947304SEvan Yan }
125926947304SEvan Yan
126026947304SEvan Yan /*
126126947304SEvan Yan * Misc routines
126226947304SEvan Yan */
126326947304SEvan Yan
126426947304SEvan Yan /* Update the last state change time */
126526947304SEvan Yan static void
ddihp_update_last_change(ddi_hp_cn_handle_t * hdlp)126626947304SEvan Yan ddihp_update_last_change(ddi_hp_cn_handle_t *hdlp)
126726947304SEvan Yan {
126826947304SEvan Yan time_t time;
126926947304SEvan Yan
127026947304SEvan Yan if (drv_getparm(TIME, (void *)&time) != DDI_SUCCESS)
127126947304SEvan Yan hdlp->cn_info.cn_last_change = (time_t)-1;
127226947304SEvan Yan else
127326947304SEvan Yan hdlp->cn_info.cn_last_change = (time32_t)time;
127426947304SEvan Yan }
127526947304SEvan Yan
127626947304SEvan Yan /*
127726947304SEvan Yan * Check the device for a 'status' property. A conforming device
127826947304SEvan Yan * should have a status of "okay", "disabled", "fail", or "fail-xxx".
127926947304SEvan Yan *
128026947304SEvan Yan * Return FALSE for a conforming device that is disabled or faulted.
128126947304SEvan Yan * Return TRUE in every other case.
128226947304SEvan Yan *
128326947304SEvan Yan * 'status' property is NOT a bus specific property. It is defined in page 184,
128426947304SEvan Yan * IEEE 1275 spec. The full name of the spec is "IEEE Standard for
128526947304SEvan Yan * Boot (Initialization Configuration) Firmware: Core Requirements and
128626947304SEvan Yan * Practices".
128726947304SEvan Yan */
128826947304SEvan Yan static boolean_t
ddihp_check_status_prop(dev_info_t * dip)128926947304SEvan Yan ddihp_check_status_prop(dev_info_t *dip)
129026947304SEvan Yan {
129126947304SEvan Yan char *status_prop;
129226947304SEvan Yan boolean_t rv = B_TRUE;
129326947304SEvan Yan
129426947304SEvan Yan /* try to get the 'status' property */
129526947304SEvan Yan if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
129626947304SEvan Yan "status", &status_prop) == DDI_PROP_SUCCESS) {
129726947304SEvan Yan /*
129826947304SEvan Yan * test if the status is "disabled", "fail", or
129926947304SEvan Yan * "fail-xxx".
130026947304SEvan Yan */
130126947304SEvan Yan if (strcmp(status_prop, "disabled") == 0) {
130226947304SEvan Yan rv = B_FALSE;
130326947304SEvan Yan DDI_HP_IMPLDBG((CE_CONT, "ddihp_check_status_prop "
130426947304SEvan Yan "(%s%d): device is in disabled state",
130526947304SEvan Yan ddi_driver_name(dip), ddi_get_instance(dip)));
130626947304SEvan Yan } else if (strncmp(status_prop, "fail", 4) == 0) {
130726947304SEvan Yan rv = B_FALSE;
130826947304SEvan Yan cmn_err(CE_WARN,
130926947304SEvan Yan "hotplug (%s%d): device is in fault state (%s)\n",
131026947304SEvan Yan ddi_driver_name(dip), ddi_get_instance(dip),
131126947304SEvan Yan status_prop);
131226947304SEvan Yan }
131326947304SEvan Yan
131426947304SEvan Yan ddi_prop_free(status_prop);
131526947304SEvan Yan }
131626947304SEvan Yan
131726947304SEvan Yan return (rv);
131826947304SEvan Yan }
1319