10Sstevel@tonic-gate /*
20Sstevel@tonic-gate * CDDL HEADER START
30Sstevel@tonic-gate *
40Sstevel@tonic-gate * The contents of this file are subject to the terms of the
53446Smrj * Common Development and Distribution License (the "License").
63446Smrj * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate *
80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate * See the License for the specific language governing permissions
110Sstevel@tonic-gate * and limitations under the License.
120Sstevel@tonic-gate *
130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate *
190Sstevel@tonic-gate * CDDL HEADER END
200Sstevel@tonic-gate */
210Sstevel@tonic-gate /*
223446Smrj * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
230Sstevel@tonic-gate * Use is subject to license terms.
240Sstevel@tonic-gate */
250Sstevel@tonic-gate
260Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI"
270Sstevel@tonic-gate
280Sstevel@tonic-gate /*
290Sstevel@tonic-gate * CPR driver support routines
300Sstevel@tonic-gate */
310Sstevel@tonic-gate
320Sstevel@tonic-gate #include <sys/types.h>
330Sstevel@tonic-gate #include <sys/errno.h>
340Sstevel@tonic-gate #include <sys/kmem.h>
350Sstevel@tonic-gate #include <sys/systm.h>
360Sstevel@tonic-gate #include <sys/sunddi.h>
370Sstevel@tonic-gate #include <sys/ddi_impldefs.h>
380Sstevel@tonic-gate #include <sys/epm.h>
390Sstevel@tonic-gate #include <sys/cpr.h>
400Sstevel@tonic-gate
410Sstevel@tonic-gate #define CPR_BUFSIZE 128
420Sstevel@tonic-gate
430Sstevel@tonic-gate extern int devi_detach(dev_info_t *, int);
440Sstevel@tonic-gate extern int devi_attach(dev_info_t *, int);
450Sstevel@tonic-gate
460Sstevel@tonic-gate static char *devi_string(dev_info_t *, char *);
470Sstevel@tonic-gate static int cpr_is_real_device(dev_info_t *);
48*5295Srandyf /*
49*5295Srandyf * Xen uses this code to suspend _all_ drivers quickly and easily.
50*5295Srandyf * Suspend and Resume uses it for the same reason, but also has
51*5295Srandyf * to contend with some platform specific code that Xen does not.
52*5295Srandyf * it is also used as a test entry point for developers/testers to
53*5295Srandyf * execute code without going through a complete suspend. So additions
54*5295Srandyf * that have platform implications shall need #if[n]def's.
55*5295Srandyf */
56*5295Srandyf #ifndef __xpv
57*5295Srandyf extern void i_cpr_save_configuration(dev_info_t *);
58*5295Srandyf extern void i_cpr_restore_configuration(dev_info_t *);
59*5295Srandyf #endif
600Sstevel@tonic-gate
610Sstevel@tonic-gate /*
620Sstevel@tonic-gate * Traverse the dev info tree:
630Sstevel@tonic-gate * Call each device driver in the system via a special case
640Sstevel@tonic-gate * of the detach() entry point to quiesce itself.
650Sstevel@tonic-gate * Suspend children first.
660Sstevel@tonic-gate *
670Sstevel@tonic-gate * We only suspend/resume real devices.
680Sstevel@tonic-gate */
690Sstevel@tonic-gate
700Sstevel@tonic-gate int
cpr_suspend_devices(dev_info_t * dip)710Sstevel@tonic-gate cpr_suspend_devices(dev_info_t *dip)
720Sstevel@tonic-gate {
730Sstevel@tonic-gate int error;
740Sstevel@tonic-gate char buf[CPR_BUFSIZE];
750Sstevel@tonic-gate
760Sstevel@tonic-gate for (; dip != NULL; dip = ddi_get_next_sibling(dip)) {
770Sstevel@tonic-gate if (cpr_suspend_devices(ddi_get_child(dip)))
780Sstevel@tonic-gate return (ENXIO);
790Sstevel@tonic-gate if (!cpr_is_real_device(dip))
800Sstevel@tonic-gate continue;
813446Smrj CPR_DEBUG(CPR_DEBUG2, "Suspending device %s\n",
823446Smrj devi_string(dip, buf));
830Sstevel@tonic-gate ASSERT((DEVI(dip)->devi_cpr_flags & DCF_CPR_SUSPENDED) == 0);
840Sstevel@tonic-gate
85*5295Srandyf #ifndef __xpv
86*5295Srandyf i_cpr_save_configuration(dip);
87*5295Srandyf #endif
88*5295Srandyf
89*5295Srandyf
90*5295Srandyf if (!i_ddi_devi_attached(dip)) {
910Sstevel@tonic-gate error = DDI_FAILURE;
92*5295Srandyf } else {
93*5295Srandyf #ifndef __xpv
94*5295Srandyf if (cpr_test_point != DEVICE_SUSPEND_TO_RAM ||
95*5295Srandyf (cpr_test_point == DEVICE_SUSPEND_TO_RAM &&
96*5295Srandyf cpr_device == ddi_driver_major(dip))) {
97*5295Srandyf #endif
98*5295Srandyf error = devi_detach(dip, DDI_SUSPEND);
99*5295Srandyf #ifndef __xpv
100*5295Srandyf } else {
101*5295Srandyf error = DDI_SUCCESS;
102*5295Srandyf }
103*5295Srandyf #endif
104*5295Srandyf }
1050Sstevel@tonic-gate
106*5295Srandyf if (error == DDI_SUCCESS) {
1070Sstevel@tonic-gate DEVI(dip)->devi_cpr_flags |= DCF_CPR_SUSPENDED;
108*5295Srandyf }
109*5295Srandyf
1100Sstevel@tonic-gate else {
1113446Smrj CPR_DEBUG(CPR_DEBUG2,
1123446Smrj "WARNING: Unable to suspend device %s\n",
1133446Smrj devi_string(dip, buf));
1140Sstevel@tonic-gate cpr_err(CE_WARN, "Unable to suspend device %s.",
115*5295Srandyf devi_string(dip, buf));
1160Sstevel@tonic-gate cpr_err(CE_WARN, "Device is busy or does not "
117*5295Srandyf "support suspend/resume.");
118*5295Srandyf #ifndef __xpv
119*5295Srandyf /*
120*5295Srandyf * the device has failed to suspend however,
121*5295Srandyf * if cpr_test_point == FORCE_SUSPEND_TO_RAM
122*5295Srandyf * after putting out the warning message above,
123*5295Srandyf * we carry on as if suspending the device had
124*5295Srandyf * been successful
125*5295Srandyf */
126*5295Srandyf if (cpr_test_point == FORCE_SUSPEND_TO_RAM)
127*5295Srandyf DEVI(dip)->devi_cpr_flags |= DCF_CPR_SUSPENDED;
128*5295Srandyf else
129*5295Srandyf #endif
130*5295Srandyf return (ENXIO);
1310Sstevel@tonic-gate }
1320Sstevel@tonic-gate }
1330Sstevel@tonic-gate return (0);
1340Sstevel@tonic-gate }
1350Sstevel@tonic-gate
1360Sstevel@tonic-gate /*
1370Sstevel@tonic-gate * Traverse the dev info tree:
1380Sstevel@tonic-gate * Call each device driver in the system via a special case
1390Sstevel@tonic-gate * of the attach() entry point to restore itself.
1400Sstevel@tonic-gate * This is a little tricky because it has to reverse the traversal
1410Sstevel@tonic-gate * order of cpr_suspend_devices().
1420Sstevel@tonic-gate */
1430Sstevel@tonic-gate int
cpr_resume_devices(dev_info_t * start,int resume_failed)1440Sstevel@tonic-gate cpr_resume_devices(dev_info_t *start, int resume_failed)
1450Sstevel@tonic-gate {
1460Sstevel@tonic-gate dev_info_t *dip, *next, *last = NULL;
1470Sstevel@tonic-gate int did_suspend;
1480Sstevel@tonic-gate int error = resume_failed;
1490Sstevel@tonic-gate char buf[CPR_BUFSIZE];
1500Sstevel@tonic-gate
1510Sstevel@tonic-gate while (last != start) {
1520Sstevel@tonic-gate dip = start;
1530Sstevel@tonic-gate next = ddi_get_next_sibling(dip);
1540Sstevel@tonic-gate while (next != last) {
1550Sstevel@tonic-gate dip = next;
1560Sstevel@tonic-gate next = ddi_get_next_sibling(dip);
1570Sstevel@tonic-gate }
1580Sstevel@tonic-gate
1590Sstevel@tonic-gate /*
1600Sstevel@tonic-gate * cpr is the only one that uses this field and the device
1610Sstevel@tonic-gate * itself hasn't resumed yet, there is no need to use a
1620Sstevel@tonic-gate * lock, even though kernel threads are active by now.
1630Sstevel@tonic-gate */
1640Sstevel@tonic-gate did_suspend = DEVI(dip)->devi_cpr_flags & DCF_CPR_SUSPENDED;
1650Sstevel@tonic-gate if (did_suspend)
1660Sstevel@tonic-gate DEVI(dip)->devi_cpr_flags &= ~DCF_CPR_SUSPENDED;
1670Sstevel@tonic-gate
1680Sstevel@tonic-gate /*
169*5295Srandyf * Always attempt to restore device configuration before
170*5295Srandyf * attempting resume
171*5295Srandyf */
172*5295Srandyf #ifndef __xpv
173*5295Srandyf i_cpr_restore_configuration(dip);
174*5295Srandyf #endif
175*5295Srandyf
176*5295Srandyf /*
1770Sstevel@tonic-gate * There may be background attaches happening on devices
1780Sstevel@tonic-gate * that were not originally suspended by cpr, so resume
1790Sstevel@tonic-gate * only devices that were suspended by cpr. Also, stop
1800Sstevel@tonic-gate * resuming after the first resume failure, but traverse
181*5295Srandyf * the entire tree to clear the suspend flag unless the
182*5295Srandyf * FORCE_SUSPEND_TO_RAM test point is set.
1830Sstevel@tonic-gate */
184*5295Srandyf #ifndef __xpv
185*5295Srandyf if (did_suspend && (!error ||
186*5295Srandyf cpr_test_point == FORCE_SUSPEND_TO_RAM)) {
187*5295Srandyf #else
1880Sstevel@tonic-gate if (did_suspend && !error) {
189*5295Srandyf #endif
1903446Smrj CPR_DEBUG(CPR_DEBUG2, "Resuming device %s\n",
1913446Smrj devi_string(dip, buf));
1920Sstevel@tonic-gate /*
1930Sstevel@tonic-gate * If a device suspended by cpr gets detached during
1940Sstevel@tonic-gate * the resume process (for example, due to hotplugging)
1950Sstevel@tonic-gate * before cpr gets around to issuing it a DDI_RESUME,
1960Sstevel@tonic-gate * we'll have problems.
1970Sstevel@tonic-gate */
1981333Scth if (!i_ddi_devi_attached(dip)) {
1993446Smrj CPR_DEBUG(CPR_DEBUG2, "WARNING: Skipping "
2003446Smrj "%s, device not ready for resume\n",
2013446Smrj devi_string(dip, buf));
2020Sstevel@tonic-gate cpr_err(CE_WARN, "Skipping %s, device "
2030Sstevel@tonic-gate "not ready for resume",
2040Sstevel@tonic-gate devi_string(dip, buf));
205*5295Srandyf #ifndef __xpv
206*5295Srandyf } else if (cpr_test_point != DEVICE_SUSPEND_TO_RAM ||
207*5295Srandyf (cpr_test_point == DEVICE_SUSPEND_TO_RAM &&
208*5295Srandyf cpr_device == ddi_driver_major(dip))) {
209*5295Srandyf #else
210*5295Srandyf } else {
211*5295Srandyf #endif
212*5295Srandyf if (devi_attach(dip, DDI_RESUME) !=
213*5295Srandyf DDI_SUCCESS) {
214*5295Srandyf error = ENXIO;
215*5295Srandyf }
2160Sstevel@tonic-gate }
2170Sstevel@tonic-gate }
2180Sstevel@tonic-gate
219*5295Srandyf if (error == ENXIO) {
220*5295Srandyf CPR_DEBUG(CPR_DEBUG2,
221*5295Srandyf "WARNING: Unable to resume device %s\n",
222*5295Srandyf devi_string(dip, buf));
223*5295Srandyf cpr_err(CE_WARN, "Unable to resume device %s",
224*5295Srandyf devi_string(dip, buf));
225*5295Srandyf }
226*5295Srandyf
2270Sstevel@tonic-gate error = cpr_resume_devices(ddi_get_child(dip), error);
2280Sstevel@tonic-gate last = dip;
2290Sstevel@tonic-gate }
2300Sstevel@tonic-gate
2310Sstevel@tonic-gate return (error);
2320Sstevel@tonic-gate }
2330Sstevel@tonic-gate
2340Sstevel@tonic-gate /*
2350Sstevel@tonic-gate * Returns a string which contains device name and address.
2360Sstevel@tonic-gate */
2370Sstevel@tonic-gate static char *
2380Sstevel@tonic-gate devi_string(dev_info_t *devi, char *buf)
2390Sstevel@tonic-gate {
2400Sstevel@tonic-gate char *name;
2410Sstevel@tonic-gate char *address;
2420Sstevel@tonic-gate int size;
2430Sstevel@tonic-gate
2440Sstevel@tonic-gate name = ddi_node_name(devi);
2450Sstevel@tonic-gate address = ddi_get_name_addr(devi);
246*5295Srandyf size = (name == NULL) ? strlen("<null name>") : strlen(name);
247*5295Srandyf size += (address == NULL) ? strlen("<null>") : strlen(address);
2480Sstevel@tonic-gate
2490Sstevel@tonic-gate /*
2500Sstevel@tonic-gate * Make sure that we don't over-run the buffer.
2510Sstevel@tonic-gate * There are 2 additional characters in the string.
2520Sstevel@tonic-gate */
2530Sstevel@tonic-gate ASSERT((size + 2) <= CPR_BUFSIZE);
2540Sstevel@tonic-gate
2550Sstevel@tonic-gate if (name == NULL)
2560Sstevel@tonic-gate (void) strcpy(buf, "<null name>");
2570Sstevel@tonic-gate else
2580Sstevel@tonic-gate (void) strcpy(buf, name);
2590Sstevel@tonic-gate
2600Sstevel@tonic-gate (void) strcat(buf, "@");
2610Sstevel@tonic-gate if (address == NULL)
2620Sstevel@tonic-gate (void) strcat(buf, "<null>");
2630Sstevel@tonic-gate else
2640Sstevel@tonic-gate (void) strcat(buf, address);
2650Sstevel@tonic-gate
2660Sstevel@tonic-gate return (buf);
2670Sstevel@tonic-gate }
2680Sstevel@tonic-gate
2690Sstevel@tonic-gate /*
2700Sstevel@tonic-gate * This function determines whether the given device is real (and should
2710Sstevel@tonic-gate * be suspended) or not (pseudo like). If the device has a "reg" property
2720Sstevel@tonic-gate * then it is presumed to have register state to save/restore.
2730Sstevel@tonic-gate */
2740Sstevel@tonic-gate static int
2750Sstevel@tonic-gate cpr_is_real_device(dev_info_t *dip)
2760Sstevel@tonic-gate {
2770Sstevel@tonic-gate struct regspec *regbuf;
2780Sstevel@tonic-gate int length;
2790Sstevel@tonic-gate int rc;
2800Sstevel@tonic-gate
2810Sstevel@tonic-gate if (ddi_get_driver(dip) == NULL)
2820Sstevel@tonic-gate return (0);
2830Sstevel@tonic-gate
2840Sstevel@tonic-gate /*
2850Sstevel@tonic-gate * First those devices for which special arrangements have been made
2860Sstevel@tonic-gate */
2870Sstevel@tonic-gate if (DEVI(dip)->devi_pm_flags & (PMC_NEEDS_SR|PMC_PARENTAL_SR))
2880Sstevel@tonic-gate return (1);
2890Sstevel@tonic-gate if (DEVI(dip)->devi_pm_flags & PMC_NO_SR)
2900Sstevel@tonic-gate return (0);
2910Sstevel@tonic-gate
2920Sstevel@tonic-gate /*
2930Sstevel@tonic-gate * now the general case
2940Sstevel@tonic-gate */
295506Scth rc = ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "reg",
2960Sstevel@tonic-gate (caddr_t)®buf, &length);
2970Sstevel@tonic-gate ASSERT(rc != DDI_PROP_NO_MEMORY);
2980Sstevel@tonic-gate if (rc != DDI_PROP_SUCCESS) {
2990Sstevel@tonic-gate return (0);
3000Sstevel@tonic-gate } else {
3010Sstevel@tonic-gate kmem_free((caddr_t)regbuf, length);
3020Sstevel@tonic-gate return (1);
3030Sstevel@tonic-gate }
3040Sstevel@tonic-gate }
305