1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 /*
  27  * pm   This driver now only handles the ioctl interface.  The scanning
  28  *      and policy stuff now lives in common/os/sunpm.c.
  29  *      Not DDI compliant
  30  */
  31 
  32 #include <sys/types.h>
  33 #include <sys/errno.h>
  34 #include <sys/modctl.h>
  35 #include <sys/callb.h>            /* callback registration for cpu_deep_idle */
  36 #include <sys/conf.h>             /* driver flags and functions */
  37 #include <sys/open.h>             /* OTYP_CHR definition */
  38 #include <sys/stat.h>             /* S_IFCHR definition */
  39 #include <sys/pathname.h> /* name -> dev_info xlation */
  40 #include <sys/kmem.h>             /* memory alloc stuff */
  41 #include <sys/debug.h>
  42 #include <sys/pm.h>
  43 #include <sys/ddi.h>
  44 #include <sys/sunddi.h>
  45 #include <sys/epm.h>
  46 #include <sys/vfs.h>
  47 #include <sys/mode.h>
  48 #include <sys/mkdev.h>
  49 #include <sys/promif.h>
  50 #include <sys/consdev.h>
  51 #include <sys/ddi_impldefs.h>
  52 #include <sys/poll.h>
  53 #include <sys/note.h>
  54 #include <sys/taskq.h>
  55 #include <sys/policy.h>
  56 #include <sys/cpu_pm.h>
  57 
  58 /*
  59  * Minor number is instance<<8 + clone minor from range 1-254; (0 reserved
  60  * for "original")
  61  */
  62 #define PM_MINOR_TO_CLONE(minor) ((minor) & (PM_MAX_CLONE -1))
  63 
  64 #define PM_NUMCMPTS(dip) (DEVI(dip)->devi_pm_num_components)
  65 #define PM_IS_CFB(dip) (DEVI(dip)->devi_pm_flags & PMC_CONSOLE_FB)
  66 #define PM_MAJOR(dip) ddi_driver_major(dip)
  67 #define PM_RELE(dip) ddi_release_devi(dip)
  68 
  69 #define PM_IDLEDOWN_TIME        10
  70 #define MAXSMBIOSSTRLEN 64      /* from SMBIOS spec */
  71 #define MAXCOPYBUF      (MAXSMBIOSSTRLEN + 1)
  72 
  73 extern kmutex_t pm_scan_lock;   /* protects autopm_enable, pm_scans_disabled */
  74 extern kmutex_t pm_clone_lock;  /* protects pm_clones array */
  75 extern int      autopm_enabled;
  76 extern pm_cpupm_t cpupm;
  77 extern pm_cpupm_t cpupm_default_mode;
  78 extern int      pm_default_idle_threshold;
  79 extern int      pm_system_idle_threshold;
  80 extern int      pm_cpu_idle_threshold;
  81 extern kcondvar_t pm_clones_cv[PM_MAX_CLONE];
  82 extern uint_t   pm_poll_cnt[PM_MAX_CLONE];
  83 extern int      autoS3_enabled;
  84 extern void     pm_record_thresh(pm_thresh_rec_t *);
  85 extern void     pm_register_watcher(int, dev_info_t *);
  86 extern int      pm_get_current_power(dev_info_t *, int, int *);
  87 extern int      pm_interest_registered(int);
  88 extern void     pm_all_to_default_thresholds(void);
  89 extern int      pm_current_threshold(dev_info_t *, int, int *);
  90 extern void     pm_deregister_watcher(int, dev_info_t *);
  91 extern void     pm_unrecord_threshold(char *);
  92 extern int      pm_S3_enabled;
  93 extern int      pm_ppm_searchlist(pm_searchargs_t *);
  94 extern psce_t   *pm_psc_clone_to_direct(int);
  95 extern psce_t   *pm_psc_clone_to_interest(int);
  96 
  97 /*
  98  * The soft state of the power manager.  Since there will only
  99  * one of these, just reference it through a static pointer.
 100  */
 101 static struct pmstate {
 102         dev_info_t      *pm_dip;                /* ptr to our dev_info node */
 103         int             pm_instance;            /* for ddi_get_instance() */
 104         timeout_id_t    pm_idledown_id;         /* pm idledown timeout id */
 105         uchar_t         pm_clones[PM_MAX_CLONE]; /* uniqueify multiple opens */
 106         struct cred     *pm_cred[PM_MAX_CLONE]; /* cred for each unique open */
 107 } pm_state = { NULL, -1, (timeout_id_t)0 };
 108 typedef struct pmstate *pm_state_t;
 109 static pm_state_t pmstp = &pm_state;
 110 
 111 static int      pm_open(dev_t *, int, int, cred_t *);
 112 static int      pm_close(dev_t, int, int, cred_t *);
 113 static int      pm_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
 114 static int      pm_chpoll(dev_t, short, int, short *, struct pollhead **);
 115 
 116 static struct cb_ops pm_cb_ops = {
 117         pm_open,        /* open */
 118         pm_close,       /* close */
 119         nodev,          /* strategy */
 120         nodev,          /* print */
 121         nodev,          /* dump */
 122         nodev,          /* read */
 123         nodev,          /* write */
 124         pm_ioctl,       /* ioctl */
 125         nodev,          /* devmap */
 126         nodev,          /* mmap */
 127         nodev,          /* segmap */
 128         pm_chpoll,      /* poll */
 129         ddi_prop_op,    /* prop_op */
 130         NULL,           /* streamtab */
 131         D_NEW | D_MP    /* driver compatibility flag */
 132 };
 133 
 134 static int pm_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
 135     void **result);
 136 static int pm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
 137 static int pm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
 138 
 139 static struct dev_ops pm_ops = {
 140         DEVO_REV,               /* devo_rev */
 141         0,                      /* refcnt */
 142         pm_getinfo,             /* info */
 143         nulldev,                /* identify */
 144         nulldev,                /* probe */
 145         pm_attach,              /* attach */
 146         pm_detach,              /* detach */
 147         nodev,                  /* reset */
 148         &pm_cb_ops,         /* driver operations */
 149         NULL,                   /* bus operations */
 150         NULL,                   /* power */
 151         ddi_quiesce_not_needed,         /* quiesce */
 152 };
 153 
 154 static struct modldrv modldrv = {
 155         &mod_driverops,
 156         "power management driver",
 157         &pm_ops
 158 };
 159 
 160 static struct modlinkage modlinkage = {
 161         MODREV_1, { &modldrv, NULL }
 162 };
 163 
 164 /* Local functions */
 165 #ifdef DEBUG
 166 static int      print_info(dev_info_t *, void *);
 167 
 168 #endif
 169 
 170 int
 171 _init(void)
 172 {
 173         return (mod_install(&modlinkage));
 174 }
 175 
 176 int
 177 _fini(void)
 178 {
 179         return (mod_remove(&modlinkage));
 180 }
 181 
 182 int
 183 _info(struct modinfo *modinfop)
 184 {
 185         return (mod_info(&modlinkage, modinfop));
 186 }
 187 
 188 static int
 189 pm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
 190 {
 191         int             i;
 192 
 193         switch (cmd) {
 194 
 195         case DDI_ATTACH:
 196                 if (pmstp->pm_instance != -1)        /* Only allow one instance */
 197                         return (DDI_FAILURE);
 198                 pmstp->pm_instance = ddi_get_instance(dip);
 199                 if (ddi_create_minor_node(dip, "pm", S_IFCHR,
 200                     (pmstp->pm_instance << 8) + 0,
 201                     DDI_PSEUDO, 0) != DDI_SUCCESS) {
 202                         return (DDI_FAILURE);
 203                 }
 204                 pmstp->pm_dip = dip; /* pm_init and getinfo depend on it */
 205 
 206                 for (i = 0; i < PM_MAX_CLONE; i++)
 207                         cv_init(&pm_clones_cv[i], NULL, CV_DEFAULT, NULL);
 208 
 209                 ddi_report_dev(dip);
 210                 return (DDI_SUCCESS);
 211 
 212         default:
 213                 return (DDI_FAILURE);
 214         }
 215 }
 216 
 217 /* ARGSUSED */
 218 static int
 219 pm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
 220 {
 221         int i;
 222 
 223         switch (cmd) {
 224         case DDI_DETACH:
 225                 /*
 226                  * Don't detach while idledown timeout is pending.  Note that
 227                  * we already know we're not in pm_ioctl() due to framework
 228                  * synchronization, so this is a sufficient test
 229                  */
 230                 if (pmstp->pm_idledown_id)
 231                         return (DDI_FAILURE);
 232 
 233                 for (i = 0; i < PM_MAX_CLONE; i++)
 234                         cv_destroy(&pm_clones_cv[i]);
 235 
 236                 ddi_remove_minor_node(dip, NULL);
 237                 pmstp->pm_instance = -1;
 238                 return (DDI_SUCCESS);
 239 
 240         default:
 241                 return (DDI_FAILURE);
 242         }
 243 }
 244 
 245 static int
 246 pm_close_direct_pm_device(dev_info_t *dip, void *arg)
 247 {
 248         int clone;
 249         char *pathbuf;
 250         pm_info_t *info = PM_GET_PM_INFO(dip);
 251 
 252         clone = *((int *)arg);
 253 
 254         if (!info)
 255                 return (DDI_WALK_CONTINUE);
 256 
 257         pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP);
 258         PM_LOCK_DIP(dip);
 259         if (clone == info->pmi_clone) {
 260                 PMD(PMD_CLOSE, ("pm_close: found %s@%s(%s#%d)\n",
 261                     PM_DEVICE(dip)))
 262                 ASSERT(PM_ISDIRECT(dip));
 263                 info->pmi_dev_pm_state &= ~PM_DIRECT;
 264                 PM_UNLOCK_DIP(dip);
 265                 pm_proceed(dip, PMP_RELEASE, -1, -1);
 266                 /* Bring ourselves up if there is a keeper that is up */
 267                 (void) ddi_pathname(dip, pathbuf);
 268                 pm_dispatch_to_dep_thread(PM_DEP_WK_BRINGUP_SELF, NULL,
 269                     pathbuf, PM_DEP_NOWAIT, NULL, 0);
 270                 PM_LOCK_DIP(dip);
 271                 info->pmi_clone = 0;
 272                 PM_UNLOCK_DIP(dip);
 273         } else {
 274                 PM_UNLOCK_DIP(dip);
 275         }
 276         kmem_free(pathbuf, MAXPATHLEN);
 277 
 278         /* restart autopm on device released from direct pm */
 279         pm_rescan(dip);
 280 
 281         return (DDI_WALK_CONTINUE);
 282 }
 283 
 284 #define PM_REQ          1
 285 #define NOSTRUCT        2
 286 #define DIP             3
 287 #define NODIP           4
 288 #define NODEP           5
 289 #define DEP             6
 290 #define PM_PSC          7
 291 #define PM_SRCH         8
 292 
 293 #define CHECKPERMS      0x001
 294 #define SU              0x002
 295 #define SG              0x004
 296 #define OWNER           0x008
 297 
 298 #define INWHO           0x001
 299 #define INDATAINT       0x002
 300 #define INDATASTRING    0x004
 301 #define INDEP           0x008
 302 #define INDATAOUT       0x010
 303 #define INDATA  (INDATAOUT | INDATAINT | INDATASTRING | INDEP)
 304 
 305 struct pm_cmd_info {
 306         int cmd;                /* command code */
 307         char *name;             /* printable string */
 308         int supported;          /* true if still supported */
 309         int str_type;           /* PM_REQ or NOSTRUCT */
 310         int inargs;             /* INWHO, INDATAINT, INDATASTRING, INDEP, */
 311                                 /* INDATAOUT */
 312         int diptype;            /* DIP or NODIP */
 313         int deptype;            /* DEP or NODEP */
 314         int permission;         /* SU, GU, or CHECKPERMS */
 315 };
 316 
 317 #ifdef DEBUG
 318 char *pm_cmd_string;
 319 int pm_cmd;
 320 #endif
 321 
 322 /*
 323  * Returns true if permission granted by credentials
 324  */
 325 static int
 326 pm_perms(int perm, cred_t *cr)
 327 {
 328         if (perm == 0)                  /* no restrictions */
 329                 return (1);
 330         if (perm == CHECKPERMS)         /* ok for now (is checked later) */
 331                 return (1);
 332         if ((perm & SU) && secpolicy_power_mgmt(cr) == 0) /* privileged? */
 333                 return (1);
 334         if ((perm & SG) && (crgetgid(cr) == 0))     /* group 0 is ok */
 335                 return (1);
 336         return (0);
 337 }
 338 
 339 #ifdef DEBUG
 340 static int
 341 print_info(dev_info_t *dip, void *arg)
 342 {
 343         _NOTE(ARGUNUSED(arg))
 344         pm_info_t       *info;
 345         int             i, j;
 346         struct pm_component *cp;
 347         extern int pm_cur_power(pm_component_t *cp);
 348 
 349         info = PM_GET_PM_INFO(dip);
 350         if (!info)
 351                 return (DDI_WALK_CONTINUE);
 352         cmn_err(CE_CONT, "pm_info for %s\n", ddi_node_name(dip));
 353         for (i = 0; i < PM_NUMCMPTS(dip); i++) {
 354                 cp = PM_CP(dip, i);
 355                 cmn_err(CE_CONT, "\tThresholds[%d] =",  i);
 356                 for (j = 0; j < cp->pmc_comp.pmc_numlevels; j++)
 357                         cmn_err(CE_CONT, " %d", cp->pmc_comp.pmc_thresh[i]);
 358                 cmn_err(CE_CONT, "\n");
 359                 cmn_err(CE_CONT, "\tCurrent power[%d] = %d\n", i,
 360                     pm_cur_power(cp));
 361         }
 362         if (PM_ISDIRECT(dip))
 363                 cmn_err(CE_CONT, "\tDirect power management\n");
 364         return (DDI_WALK_CONTINUE);
 365 }
 366 #endif
 367 
 368 /*
 369  * command, name, supported, str_type, inargs, diptype, deptype, permission
 370  */
 371 static struct pm_cmd_info pmci[] = {
 372         {PM_SCHEDULE, "PM_SCHEDULE", 0},
 373         {PM_GET_IDLE_TIME, "PM_GET_IDLE_TIME", 0},
 374         {PM_GET_NUM_CMPTS, "PM_GET_NUM_CMPTS", 0},
 375         {PM_GET_THRESHOLD, "PM_GET_THRESHOLD", 0},
 376         {PM_SET_THRESHOLD, "PM_SET_THRESHOLD", 0},
 377         {PM_GET_NORM_PWR, "PM_GET_NORM_PWR", 0},
 378         {PM_SET_CUR_PWR, "PM_SET_CUR_PWR", 0},
 379         {PM_GET_CUR_PWR, "PM_GET_CUR_PWR", 0},
 380         {PM_GET_NUM_DEPS, "PM_GET_NUM_DEPS", 0},
 381         {PM_GET_DEP, "PM_GET_DEP", 0},
 382         {PM_ADD_DEP, "PM_ADD_DEP", 0},
 383         {PM_REM_DEP, "PM_REM_DEP", 0},
 384         {PM_REM_DEVICE, "PM_REM_DEVICE", 0},
 385         {PM_REM_DEVICES, "PM_REM_DEVICES", 0},
 386         {PM_REPARSE_PM_PROPS, "PM_REPARSE_PM_PROPS", 1, PM_REQ, INWHO, DIP,
 387             NODEP},
 388         {PM_DISABLE_AUTOPM, "PM_DISABLE_AUTOPM", 0},
 389         {PM_REENABLE_AUTOPM, "PM_REENABLE_AUTOPM", 0},
 390         {PM_SET_NORM_PWR, "PM_SET_NORM_PWR", 0 },
 391         {PM_SET_DEVICE_THRESHOLD, "PM_SET_DEVICE_THRESHOLD", 1, PM_REQ,
 392             INWHO, NODIP, NODEP, SU},
 393         {PM_GET_SYSTEM_THRESHOLD, "PM_GET_SYSTEM_THRESHOLD", 1, NOSTRUCT},
 394         {PM_GET_DEFAULT_SYSTEM_THRESHOLD, "PM_GET_DEFAULT_SYSTEM_THRESHOLD",
 395             1, NOSTRUCT},
 396         {PM_SET_SYSTEM_THRESHOLD, "PM_SET_SYSTEM_THRESHOLD", 1, NOSTRUCT,
 397             0, 0, 0, SU},
 398         {PM_START_PM, "PM_START_PM", 1, NOSTRUCT, 0, 0, 0, SU},
 399         {PM_STOP_PM, "PM_STOP_PM", 1, NOSTRUCT, 0, 0, 0, SU},
 400         {PM_RESET_PM, "PM_RESET_PM", 1, NOSTRUCT, 0, 0, 0, SU},
 401         {PM_GET_STATS, "PM_GET_STATS", 1, PM_REQ, INWHO | INDATAOUT,
 402             DIP, NODEP},
 403         {PM_GET_DEVICE_THRESHOLD, "PM_GET_DEVICE_THRESHOLD", 1, PM_REQ, INWHO,
 404             DIP, NODEP},
 405         {PM_GET_POWER_NAME, "PM_GET_POWER_NAME", 1, PM_REQ, INWHO | INDATAOUT,
 406             DIP, NODEP},
 407         {PM_GET_POWER_LEVELS, "PM_GET_POWER_LEVELS", 1, PM_REQ,
 408             INWHO | INDATAOUT, DIP, NODEP},
 409         {PM_GET_NUM_COMPONENTS, "PM_GET_NUM_COMPONENTS", 1, PM_REQ, INWHO,
 410             DIP, NODEP},
 411         {PM_GET_COMPONENT_NAME, "PM_GET_COMPONENT_NAME", 1, PM_REQ,
 412             INWHO | INDATAOUT, DIP, NODEP},
 413         {PM_GET_NUM_POWER_LEVELS, "PM_GET_NUM_POWER_LEVELS", 1, PM_REQ, INWHO,
 414             DIP, NODEP},
 415         {PM_GET_STATE_CHANGE, "PM_GET_STATE_CHANGE", 1, PM_PSC},
 416         {PM_GET_STATE_CHANGE_WAIT, "PM_GET_STATE_CHANGE_WAIT", 1, PM_PSC},
 417         {PM_DIRECT_PM, "PM_DIRECT_PM", 1, PM_REQ, INWHO, DIP, NODEP,
 418             (SU | SG)},
 419         {PM_RELEASE_DIRECT_PM, "PM_RELEASE_DIRECT_PM", 1, PM_REQ, INWHO,
 420             DIP, NODEP},
 421         {PM_DIRECT_NOTIFY, "PM_DIRECT_NOTIFY", 1, PM_PSC},
 422         {PM_DIRECT_NOTIFY_WAIT, "PM_DIRECT_NOTIFY_WAIT", 1, PM_PSC},
 423         {PM_RESET_DEVICE_THRESHOLD, "PM_RESET_DEVICE_THRESHOLD", 1, PM_REQ,
 424             INWHO, DIP, NODEP, SU},
 425         {PM_GET_PM_STATE, "PM_GET_PM_STATE", 1, NOSTRUCT},
 426         {PM_GET_AUTOS3_STATE, "PM_GET_AUTOS3_STATE", 1, NOSTRUCT},
 427         {PM_GET_S3_SUPPORT_STATE, "PM_GET_S3_SUPPORT_STATE", 1, NOSTRUCT},
 428         {PM_GET_DEVICE_TYPE, "PM_GET_DEVICE_TYPE", 1, PM_REQ, INWHO,
 429             DIP, NODEP},
 430         {PM_SET_COMPONENT_THRESHOLDS, "PM_SET_COMPONENT_THRESHOLDS", 1, PM_REQ,
 431             INWHO | INDATAINT, NODIP, NODEP, SU},
 432         {PM_GET_COMPONENT_THRESHOLDS, "PM_GET_COMPONENT_THRESHOLDS", 1, PM_REQ,
 433             INWHO | INDATAOUT, DIP, NODEP},
 434         {PM_IDLE_DOWN, "PM_IDLE_DOWN", 1, NOSTRUCT, 0, 0, 0, SU},
 435         {PM_GET_DEVICE_THRESHOLD_BASIS, "PM_GET_DEVICE_THRESHOLD_BASIS", 1,
 436             PM_REQ, INWHO, DIP, NODEP},
 437         {PM_SET_CURRENT_POWER, "PM_SET_CURRENT_POWER", 1, PM_REQ, INWHO, DIP,
 438             NODEP},
 439         {PM_GET_CURRENT_POWER, "PM_GET_CURRENT_POWER", 1, PM_REQ, INWHO, DIP,
 440             NODEP},
 441         {PM_GET_FULL_POWER, "PM_GET_FULL_POWER", 1, PM_REQ, INWHO, DIP,
 442             NODEP},
 443         {PM_ADD_DEPENDENT, "PM_ADD_DEPENDENT", 1, PM_REQ, INWHO | INDATASTRING,
 444             DIP, DEP, SU},
 445         {PM_GET_TIME_IDLE, "PM_GET_TIME_IDLE", 1, PM_REQ, INWHO, DIP, NODEP},
 446         {PM_ADD_DEPENDENT_PROPERTY, "PM_ADD_DEPENDENT_PROPERTY", 1, PM_REQ,
 447             INWHO | INDATASTRING, NODIP, DEP, SU},
 448         {PM_START_CPUPM, "PM_START_CPUPM", 1, NOSTRUCT, 0, 0, 0, SU},
 449         {PM_START_CPUPM_EV, "PM_START_CPUPM_EV", 1, NOSTRUCT, 0,
 450             0, 0, SU},
 451         {PM_START_CPUPM_POLL, "PM_START_CPUPM_POLL", 1, NOSTRUCT, 0,
 452             0, 0, SU},
 453         {PM_STOP_CPUPM, "PM_STOP_CPUPM", 1, NOSTRUCT, 0, 0, 0, SU},
 454         {PM_GET_CPU_THRESHOLD, "PM_GET_CPU_THRESHOLD", 1, NOSTRUCT},
 455         {PM_SET_CPU_THRESHOLD, "PM_SET_CPU_THRESHOLD", 1, NOSTRUCT,
 456             0, 0, 0, SU},
 457         {PM_GET_CPUPM_STATE, "PM_GET_CPUPM_STATE", 1, NOSTRUCT},
 458         {PM_START_AUTOS3, "PM_START_AUTOS3", 1, NOSTRUCT, 0, 0, 0, SU},
 459         {PM_STOP_AUTOS3, "PM_STOP_AUTOS3", 1, NOSTRUCT, 0, 0, 0, SU},
 460         {PM_ENABLE_S3, "PM_ENABLE_S3", 1, NOSTRUCT, 0, 0, 0, SU},
 461         {PM_DISABLE_S3, "PM_DISABLE_S3", 1, NOSTRUCT, 0, 0, 0, SU},
 462         {PM_ENTER_S3, "PM_ENTER_S3", 1, NOSTRUCT, 0, 0, 0, SU},
 463         {PM_SEARCH_LIST, "PM_SEARCH_LIST", 1, PM_SRCH, 0, 0, 0, SU},
 464         {PM_GET_CMD_NAME, "PM_GET_CMD_NAME", 1, PM_REQ, INDATAOUT, NODIP,
 465             NODEP, 0},
 466         {PM_DISABLE_CPU_DEEP_IDLE, "PM_DISABLE_CPU_DEEP_IDLE", 1, NOSTRUCT, 0,
 467             0, 0, SU},
 468         {PM_ENABLE_CPU_DEEP_IDLE, "PM_START_CPU_DEEP_IDLE", 1, NOSTRUCT, 0,
 469             0, 0, SU},
 470         {PM_DEFAULT_CPU_DEEP_IDLE, "PM_DFLT_CPU_DEEP_IDLE", 1, NOSTRUCT, 0,
 471             0, 0, SU},
 472         {0, NULL}
 473 };
 474 
 475 struct pm_cmd_info *
 476 pc_info(int cmd)
 477 {
 478         struct pm_cmd_info *pcip;
 479 
 480         for (pcip = pmci; pcip->name; pcip++) {
 481                 if (cmd == pcip->cmd)
 482                         return (pcip);
 483         }
 484         return (NULL);
 485 }
 486 
 487 static char *
 488 pm_decode_cmd(int cmd)
 489 {
 490         static char invbuf[64];
 491         struct pm_cmd_info *pcip = pc_info(cmd);
 492         if (pcip != NULL)
 493                 return (pcip->name);
 494         (void) sprintf(invbuf, "ioctl: invalid command %d\n", cmd);
 495         return (invbuf);
 496 }
 497 
 498 /*
 499  * Allocate scan resource, create taskq, then dispatch scan,
 500  * called only if autopm is enabled.
 501  */
 502 int
 503 pm_start_pm_walk(dev_info_t *dip, void *arg)
 504 {
 505         int cmd = *((int *)arg);
 506 #ifdef PMDDEBUG
 507         char *cmdstr = pm_decode_cmd(cmd);
 508 #endif
 509 
 510         if (!PM_GET_PM_INFO(dip) || PM_ISBC(dip))
 511                 return (DDI_WALK_CONTINUE);
 512 
 513         switch (cmd) {
 514         case PM_START_CPUPM:
 515         case PM_START_CPUPM_POLL:
 516                 if (!PM_ISCPU(dip))
 517                         return (DDI_WALK_CONTINUE);
 518                 mutex_enter(&pm_scan_lock);
 519                 if (!PM_CPUPM_DISABLED && !PM_EVENT_CPUPM)
 520                         pm_scan_init(dip);
 521                 mutex_exit(&pm_scan_lock);
 522                 break;
 523         case PM_START_PM:
 524                 mutex_enter(&pm_scan_lock);
 525                 if (PM_ISCPU(dip) && (PM_CPUPM_DISABLED || PM_EVENT_CPUPM)) {
 526                         mutex_exit(&pm_scan_lock);
 527                         return (DDI_WALK_CONTINUE);
 528                 }
 529                 if (autopm_enabled)
 530                         pm_scan_init(dip);
 531                 mutex_exit(&pm_scan_lock);
 532                 break;
 533         }
 534 
 535         /*
 536          * Start doing pm on device: ensure pm_scan data structure initiated,
 537          * no need to guarantee a successful scan run.
 538          */
 539         PMD(PMD_SCAN | PMD_IOCTL, ("ioctl: %s: scan %s@%s(%s#%d)\n", cmdstr,
 540             PM_DEVICE(dip)))
 541         pm_rescan(dip);
 542 
 543         return (DDI_WALK_CONTINUE);
 544 }
 545 
 546 /*
 547  * Bring devices to full power level, then stop scan
 548  */
 549 int
 550 pm_stop_pm_walk(dev_info_t *dip, void *arg)
 551 {
 552         pm_info_t *info = PM_GET_PM_INFO(dip);
 553         int cmd = *((int *)arg);
 554 #ifdef PMDDEBUG
 555         char *cmdstr = pm_decode_cmd(cmd);
 556 #endif
 557 
 558         if (!info)
 559                 return (DDI_WALK_CONTINUE);
 560 
 561         switch (cmd) {
 562         case PM_STOP_PM:
 563                 /*
 564                  * If CPU devices are being managed independently, then don't
 565                  * stop them as part of PM_STOP_PM. Only stop them as part of
 566                  * PM_STOP_CPUPM and PM_RESET_PM.
 567                  */
 568                 if (PM_ISCPU(dip) && PM_POLLING_CPUPM)
 569                         return (DDI_WALK_CONTINUE);
 570                 break;
 571         case PM_STOP_CPUPM:
 572                 /*
 573                  * If stopping CPU devices and this device is not marked
 574                  * as a CPU device, then skip.
 575                  */
 576                 if (!PM_ISCPU(dip))
 577                         return (DDI_WALK_CONTINUE);
 578                 break;
 579         }
 580 
 581         /*
 582          * Stop the current scan, and then bring it back to normal power.
 583          */
 584         if (!PM_ISBC(dip)) {
 585                 PMD(PMD_SCAN | PMD_IOCTL, ("ioctl: %s: stop scan for "
 586                     "%s@%s(%s#%d)\n", cmdstr, PM_DEVICE(dip)))
 587                 pm_scan_stop(dip);
 588         }
 589 
 590         if (!PM_ISBC(dip) && !PM_ISDIRECT(dip) &&
 591             !pm_all_at_normal(dip)) {
 592                 PM_LOCK_DIP(dip);
 593                 if (info->pmi_dev_pm_state & PM_DETACHING) {
 594                         PMD(PMD_ALLNORM, ("ioctl: %s: deferring "
 595                             "all_to_normal because %s@%s(%s#%d) is detaching\n",
 596                             cmdstr, PM_DEVICE(dip)))
 597                         info->pmi_dev_pm_state |= PM_ALLNORM_DEFERRED;
 598                         PM_UNLOCK_DIP(dip);
 599                         return (DDI_WALK_CONTINUE);
 600                 }
 601                 PM_UNLOCK_DIP(dip);
 602                 if (pm_all_to_normal(dip, PM_CANBLOCK_FAIL) != DDI_SUCCESS) {
 603                         PMD(PMD_ERROR, ("ioctl: %s: could not bring %s@%s"
 604                             "(%s#%d) to normal\n", cmdstr, PM_DEVICE(dip)))
 605                 }
 606         }
 607 
 608         return (DDI_WALK_CONTINUE);
 609 }
 610 
 611 static int
 612 pm_start_idledown(dev_info_t *dip, void *arg)
 613 {
 614         int             flag = (int)(intptr_t)arg;
 615         pm_scan_t       *scanp = PM_GET_PM_SCAN(dip);
 616 
 617         if (!scanp)
 618                 return (DDI_WALK_CONTINUE);
 619 
 620         PM_LOCK_DIP(dip);
 621         scanp->ps_idle_down |= flag;
 622         PM_UNLOCK_DIP(dip);
 623         pm_rescan(dip);
 624 
 625         return (DDI_WALK_CONTINUE);
 626 }
 627 
 628 /*ARGSUSED*/
 629 static int
 630 pm_end_idledown(dev_info_t *dip, void *ignore)
 631 {
 632         pm_scan_t       *scanp = PM_GET_PM_SCAN(dip);
 633 
 634         if (!scanp)
 635                 return (DDI_WALK_CONTINUE);
 636 
 637         PM_LOCK_DIP(dip);
 638         /*
 639          * The PMID_TIMERS bits are place holder till idledown expires.
 640          * The bits are also the base for regenerating PMID_SCANS bits.
 641          * While it's up to scan thread to clear up the PMID_SCANS bits
 642          * after each scan run, PMID_TIMERS ensure aggressive scan down
 643          * performance throughout the idledown period.
 644          */
 645         scanp->ps_idle_down &= ~PMID_TIMERS;
 646         PM_UNLOCK_DIP(dip);
 647 
 648         return (DDI_WALK_CONTINUE);
 649 }
 650 
 651 /*ARGSUSED*/
 652 static void
 653 pm_end_idledown_walk(void *ignore)
 654 {
 655         PMD(PMD_IDLEDOWN, ("ioctl: end_idledown: idledown_id(%lx) timer is "
 656             "off\n", (ulong_t)pmstp->pm_idledown_id));
 657 
 658         mutex_enter(&pm_scan_lock);
 659         pmstp->pm_idledown_id = 0;
 660         mutex_exit(&pm_scan_lock);
 661 
 662         ddi_walk_devs(ddi_root_node(), pm_end_idledown, NULL);
 663 }
 664 
 665 /*
 666  * pm_timeout_idledown - keep idledown effect for 10 seconds.
 667  *
 668  * Return 0 if another competing caller scheduled idledown timeout,
 669  * otherwise, return idledown timeout_id.
 670  */
 671 static timeout_id_t
 672 pm_timeout_idledown(void)
 673 {
 674         timeout_id_t    to_id;
 675 
 676         /*
 677          * Keep idle-down in effect for either 10 seconds
 678          * or length of a scan interval, which ever is greater.
 679          */
 680         mutex_enter(&pm_scan_lock);
 681         if (pmstp->pm_idledown_id != 0) {
 682                 to_id = pmstp->pm_idledown_id;
 683                 pmstp->pm_idledown_id = 0;
 684                 mutex_exit(&pm_scan_lock);
 685                 (void) untimeout(to_id);
 686                 mutex_enter(&pm_scan_lock);
 687                 if (pmstp->pm_idledown_id != 0) {
 688                         PMD(PMD_IDLEDOWN, ("ioctl: timeout_idledown: "
 689                             "another caller got it, idledown_id(%lx)!\n",
 690                             (ulong_t)pmstp->pm_idledown_id))
 691                         mutex_exit(&pm_scan_lock);
 692                         return (0);
 693                 }
 694         }
 695         pmstp->pm_idledown_id = timeout(pm_end_idledown_walk, NULL,
 696             PM_IDLEDOWN_TIME * hz);
 697         PMD(PMD_IDLEDOWN, ("ioctl: timeout_idledown: idledown_id(%lx)\n",
 698             (ulong_t)pmstp->pm_idledown_id))
 699         mutex_exit(&pm_scan_lock);
 700 
 701         return (pmstp->pm_idledown_id);
 702 }
 703 
 704 static int
 705 pm_chpoll(dev_t dev, short events, int anyyet, short *reventsp,
 706         struct pollhead **phpp)
 707 {
 708         extern struct pollhead pm_pollhead;     /* common/os/sunpm.c */
 709         int     clone;
 710 
 711         clone = PM_MINOR_TO_CLONE(getminor(dev));
 712         PMD(PMD_IOCTL, ("ioctl: pm_chpoll: clone %d\n", clone))
 713         if ((events & (POLLIN | POLLRDNORM)) && pm_poll_cnt[clone]) {
 714                 *reventsp |= (POLLIN | POLLRDNORM);
 715                 PMD(PMD_IOCTL, ("ioctl: pm_chpoll: reventsp set\n"))
 716         } else {
 717                 *reventsp = 0;
 718                 if (!anyyet) {
 719                         PMD(PMD_IOCTL, ("ioctl: pm_chpoll: not anyyet\n"))
 720                         *phpp = &pm_pollhead;
 721                 }
 722 #ifdef DEBUG
 723                 else {
 724                         PMD(PMD_IOCTL, ("ioctl: pm_chpoll: anyyet\n"))
 725                 }
 726 #endif
 727         }
 728         return (0);
 729 }
 730 
 731 /*
 732  * called by pm_dicard_entries to free up the memory. It also decrements
 733  * pm_poll_cnt, if direct is non zero.
 734  */
 735 static void
 736 pm_free_entries(psce_t *pscep, int clone, int direct)
 737 {
 738         pm_state_change_t       *p;
 739 
 740         if (pscep) {
 741                 p = pscep->psce_out;
 742                 while (p->size) {
 743                         if (direct) {
 744                                 PMD(PMD_IOCTL, ("ioctl: discard: "
 745                                     "pm_poll_cnt[%d] is %d before "
 746                                     "ASSERT\n", clone,
 747                                     pm_poll_cnt[clone]))
 748                                 ASSERT(pm_poll_cnt[clone]);
 749                                 pm_poll_cnt[clone]--;
 750                         }
 751                         kmem_free(p->physpath, p->size);
 752                         p->size = 0;
 753                         if (p == pscep->psce_last)
 754                                 p = pscep->psce_first;
 755                         else
 756                                 p++;
 757                 }
 758                 pscep->psce_out = pscep->psce_first;
 759                 pscep->psce_in = pscep->psce_first;
 760                 mutex_exit(&pscep->psce_lock);
 761         }
 762 }
 763 
 764 /*
 765  * Discard entries for this clone. Calls pm_free_entries to free up memory.
 766  */
 767 static void
 768 pm_discard_entries(int clone)
 769 {
 770         psce_t  *pscep;
 771         int                     direct = 0;
 772 
 773         mutex_enter(&pm_clone_lock);
 774         if ((pscep = pm_psc_clone_to_direct(clone)) != NULL)
 775                 direct = 1;
 776         pm_free_entries(pscep, clone, direct);
 777         pscep = pm_psc_clone_to_interest(clone);
 778         pm_free_entries(pscep, clone, 0);
 779         mutex_exit(&pm_clone_lock);
 780 }
 781 
 782 
 783 static void
 784 pm_set_idle_threshold(dev_info_t *dip, int thresh, int flag)
 785 {
 786         if (!PM_ISBC(dip) && !PM_ISDIRECT(dip)) {
 787                 switch (DEVI(dip)->devi_pm_flags & PMC_THRESH_ALL) {
 788                 case PMC_DEF_THRESH:
 789                 case PMC_CPU_THRESH:
 790                         PMD(PMD_IOCTL, ("ioctl: set_idle_threshold: set "
 791                             "%s@%s(%s#%d) default thresh to 0t%d\n",
 792                             PM_DEVICE(dip), thresh))
 793                         pm_set_device_threshold(dip, thresh, flag);
 794                         break;
 795                 default:
 796                         break;
 797                 }
 798         }
 799 }
 800 
 801 static int
 802 pm_set_idle_thresh_walk(dev_info_t *dip, void *arg)
 803 {
 804         int cmd = *((int *)arg);
 805 
 806         if (!PM_GET_PM_INFO(dip))
 807                 return (DDI_WALK_CONTINUE);
 808 
 809         switch (cmd) {
 810         case PM_SET_SYSTEM_THRESHOLD:
 811                 if (DEVI(dip)->devi_pm_flags & PMC_CPU_THRESH)
 812                         break;
 813                 pm_set_idle_threshold(dip, pm_system_idle_threshold,
 814                     PMC_DEF_THRESH);
 815                 pm_rescan(dip);
 816                 break;
 817         case PM_SET_CPU_THRESHOLD:
 818                 if (!PM_ISCPU(dip))
 819                         break;
 820                 pm_set_idle_threshold(dip, pm_cpu_idle_threshold,
 821                     PMC_CPU_THRESH);
 822                 pm_rescan(dip);
 823                 break;
 824         }
 825 
 826         return (DDI_WALK_CONTINUE);
 827 }
 828 
 829 /*ARGSUSED*/
 830 static int
 831 pm_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
 832 {
 833         dev_t   dev;
 834         int     instance;
 835 
 836         switch (infocmd) {
 837         case DDI_INFO_DEVT2DEVINFO:
 838                 if (pmstp->pm_instance == -1)
 839                         return (DDI_FAILURE);
 840                 *result = pmstp->pm_dip;
 841                 return (DDI_SUCCESS);
 842 
 843         case DDI_INFO_DEVT2INSTANCE:
 844                 dev = (dev_t)arg;
 845                 instance = getminor(dev) >> 8;
 846                 *result = (void *)(uintptr_t)instance;
 847                 return (DDI_SUCCESS);
 848 
 849         default:
 850                 return (DDI_FAILURE);
 851         }
 852 }
 853 
 854 
 855 /*ARGSUSED1*/
 856 static int
 857 pm_open(dev_t *devp, int flag, int otyp, cred_t *cr)
 858 {
 859         int             clone;
 860 
 861         if (otyp != OTYP_CHR)
 862                 return (EINVAL);
 863 
 864         mutex_enter(&pm_clone_lock);
 865         for (clone = 1; clone < PM_MAX_CLONE; clone++)
 866                 if (!pmstp->pm_clones[clone])
 867                         break;
 868 
 869         if (clone == PM_MAX_CLONE) {
 870                 mutex_exit(&pm_clone_lock);
 871                 return (ENXIO);
 872         }
 873         pmstp->pm_cred[clone] = cr;
 874         crhold(cr);
 875 
 876         *devp = makedevice(getmajor(*devp), (pmstp->pm_instance << 8) + clone);
 877         pmstp->pm_clones[clone] = 1;
 878         mutex_exit(&pm_clone_lock);
 879 
 880         return (0);
 881 }
 882 
 883 /*ARGSUSED1*/
 884 static int
 885 pm_close(dev_t dev, int flag, int otyp, cred_t *cr)
 886 {
 887         int clone;
 888 
 889         if (otyp != OTYP_CHR)
 890                 return (EINVAL);
 891 
 892         clone = PM_MINOR_TO_CLONE(getminor(dev));
 893         PMD(PMD_CLOSE, ("pm_close: minor %x, clone %x\n", getminor(dev),
 894             clone))
 895 
 896         /*
 897          * Walk the entire device tree to find the corresponding
 898          * device and operate on it.
 899          */
 900         ddi_walk_devs(ddi_root_node(), pm_close_direct_pm_device,
 901             (void *) &clone);
 902 
 903         crfree(pmstp->pm_cred[clone]);
 904         pmstp->pm_cred[clone] = 0;
 905         pmstp->pm_clones[clone] = 0;
 906         pm_discard_entries(clone);
 907         ASSERT(pm_poll_cnt[clone] == 0);
 908         pm_deregister_watcher(clone, NULL);
 909         return (0);
 910 }
 911 
 912 /*ARGSUSED*/
 913 static int
 914 pm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p)
 915 {
 916         struct pm_cmd_info *pc_info(int);
 917         struct pm_cmd_info *pcip = pc_info(cmd);
 918         pm_req_t        req;
 919         dev_info_t      *dip = NULL;
 920         pm_info_t       *info = NULL;
 921         int             clone;
 922         char            *cmdstr = pm_decode_cmd(cmd);
 923         /*
 924          * To keep devinfo nodes from going away while we're holding a
 925          * pointer to their dip, pm_name_to_dip() optionally holds
 926          * the devinfo node.  If we've done that, we set dipheld
 927          * so we know at the end of the ioctl processing to release the
 928          * node again.
 929          */
 930         int             dipheld = 0;
 931         int             icount = 0;
 932         int             i;
 933         int             comps;
 934         size_t          lencopied;
 935         int             ret = ENOTTY;
 936         int             curpower;
 937         char            who[MAXNAMELEN];
 938         size_t          wholen;                 /* copyinstr length */
 939         size_t          deplen = MAXNAMELEN;
 940         char            *dep, i_dep_buf[MAXNAMELEN];
 941         char            pathbuf[MAXNAMELEN];
 942         struct pm_component *cp;
 943 #ifdef  _MULTI_DATAMODEL
 944         pm_state_change32_t             *pscp32;
 945         pm_state_change32_t             psc32;
 946         pm_searchargs32_t               psa32;
 947         size_t                          copysize32;
 948 #endif
 949         pm_state_change_t               *pscp;
 950         pm_state_change_t               psc;
 951         pm_searchargs_t         psa;
 952         char            listname[MAXCOPYBUF];
 953         char            manufacturer[MAXCOPYBUF];
 954         char            product[MAXCOPYBUF];
 955         size_t          copysize;
 956 
 957         PMD(PMD_IOCTL, ("ioctl: %s: begin\n", cmdstr))
 958 
 959 #ifdef DEBUG
 960         if (cmd == 666) {
 961                 ddi_walk_devs(ddi_root_node(), print_info, NULL);
 962                 return (0);
 963         }
 964         ret = 0x0badcafe;                       /* sanity checking */
 965         pm_cmd = cmd;                           /* for ASSERT debugging */
 966         pm_cmd_string = cmdstr; /* for ASSERT debugging */
 967 #endif
 968 
 969 
 970         if (pcip == NULL) {
 971                 PMD(PMD_ERROR, ("ioctl: unknown command %d\n", cmd))
 972                 return (ENOTTY);
 973         }
 974         if (pcip == NULL || pcip->supported == 0) {
 975                 PMD(PMD_ERROR, ("ioctl: command %s no longer supported\n",
 976                     pcip->name))
 977                 return (ENOTTY);
 978         }
 979 
 980         wholen = 0;
 981         dep = i_dep_buf;
 982         i_dep_buf[0] = 0;
 983         clone = PM_MINOR_TO_CLONE(getminor(dev));
 984         if (!pm_perms(pcip->permission, pmstp->pm_cred[clone])) {
 985                 ret = EPERM;
 986                 return (ret);
 987         }
 988         switch (pcip->str_type) {
 989         case PM_REQ:
 990         {
 991 #ifdef  _MULTI_DATAMODEL
 992                 if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) {
 993                         pm_req32_t      req32;
 994 
 995                         if (ddi_copyin((caddr_t)arg, &req32,
 996                             sizeof (req32), mode) != 0) {
 997                                 PMD(PMD_ERROR, ("ioctl: %s: ddi_copyin "
 998                                     "EFAULT\n\n", cmdstr))
 999                                 ret = EFAULT;
1000                                 break;
1001                         }
1002                         req.component = req32.component;
1003                         req.value = req32.value;
1004                         req.datasize = req32.datasize;
1005                         if (pcip->inargs & INWHO) {
1006                                 ret = copyinstr((char *)(uintptr_t)
1007                                     req32.physpath, who, MAXNAMELEN, &wholen);
1008                                 if (ret) {
1009                                         PMD(PMD_ERROR, ("ioctl: %s: "
1010                                             "copyinstr fails returning %d\n",
1011                                             cmdstr, ret))
1012                                         break;
1013                                 }
1014                                 req.physpath = who;
1015                                 PMD(PMD_IOCTL, ("ioctl: %s: physpath=%s\n",
1016                                     cmdstr, req.physpath))
1017                         }
1018                         if (pcip->inargs & INDATA) {
1019                                 req.data = (void *)(uintptr_t)req32.data;
1020                                 req.datasize = req32.datasize;
1021                         } else {
1022                                 req.data = NULL;
1023                                 req.datasize = 0;
1024                         }
1025                         switch (pcip->diptype) {
1026                         case DIP:
1027                                 if (!(dip =
1028                                     pm_name_to_dip(req.physpath, 1))) {
1029                                         PMD(PMD_ERROR, ("ioctl: %s: "
1030                                             "pm_name_to_dip for %s failed\n",
1031                                             cmdstr, req.physpath))
1032                                         return (ENODEV);
1033                                 }
1034                                 ASSERT(!dipheld);
1035                                 dipheld++;
1036                                 break;
1037                         case NODIP:
1038                                 break;
1039                         default:
1040                                 /*
1041                                  * Internal error, invalid ioctl description
1042                                  * force debug entry even if pm_debug not set
1043                                  */
1044 #ifdef  DEBUG
1045                                 pm_log("invalid diptype %d for cmd %d (%s)\n",
1046                                     pcip->diptype, cmd, pcip->name);
1047 #endif
1048                                 ASSERT(0);
1049                                 return (EIO);
1050                         }
1051                         if (pcip->inargs & INDATAINT) {
1052                                 int32_t int32buf;
1053                                 int32_t *i32p;
1054                                 int *ip;
1055                                 icount = req32.datasize / sizeof (int32_t);
1056                                 if (icount <= 0) {
1057                                         PMD(PMD_ERROR, ("ioctl: %s: datasize"
1058                                             " 0 or neg EFAULT\n\n", cmdstr))
1059                                         ret = EFAULT;
1060                                         break;
1061                                 }
1062                                 ASSERT(!(pcip->inargs & INDATASTRING));
1063                                 req.datasize = icount * sizeof (int);
1064                                 req.data = kmem_alloc(req.datasize, KM_SLEEP);
1065                                 ip = req.data;
1066                                 ret = 0;
1067                                 for (i = 0,
1068                                     i32p = (int32_t *)(uintptr_t)req32.data;
1069                                     i < icount; i++, i32p++) {
1070                                         if (ddi_copyin((void *)i32p, &int32buf,
1071                                             sizeof (int32_t), mode)) {
1072                                                 kmem_free(req.data,
1073                                                     req.datasize);
1074                                                 PMD(PMD_ERROR, ("ioctl: %s: "
1075                                                     "entry %d EFAULT\n",
1076                                                     cmdstr, i))
1077                                                 ret = EFAULT;
1078                                                 break;
1079                                         }
1080                                         *ip++ = (int)int32buf;
1081                                 }
1082                                 if (ret)
1083                                         break;
1084                         }
1085                         if (pcip->inargs & INDATASTRING) {
1086                                 ASSERT(!(pcip->inargs & INDATAINT));
1087                                 ASSERT(pcip->deptype == DEP);
1088                                 if (req32.data != NULL) {
1089                                         if (copyinstr((void *)(uintptr_t)
1090                                             req32.data, dep, deplen, NULL)) {
1091                                                 PMD(PMD_ERROR, ("ioctl: %s: "
1092                                                     "0x%p dep size %lx, EFAULT"
1093                                                     "\n", cmdstr,
1094                                                     (void *)req.data, deplen))
1095                                                 ret = EFAULT;
1096                                                 break;
1097                                         }
1098 #ifdef DEBUG
1099                                         else {
1100                                                 PMD(PMD_DEP, ("ioctl: %s: "
1101                                                     "dep %s\n", cmdstr, dep))
1102                                         }
1103 #endif
1104                                 } else {
1105                                         PMD(PMD_ERROR, ("ioctl: %s: no "
1106                                             "dependent\n", cmdstr))
1107                                         ret = EINVAL;
1108                                         break;
1109                                 }
1110                         }
1111                 } else
1112 #endif /* _MULTI_DATAMODEL */
1113                 {
1114                         if (ddi_copyin((caddr_t)arg,
1115                             &req, sizeof (req), mode) != 0) {
1116                                 PMD(PMD_ERROR, ("ioctl: %s: ddi_copyin "
1117                                     "EFAULT\n\n", cmdstr))
1118                                 ret = EFAULT;
1119                                 break;
1120                         }
1121                         if (pcip->inargs & INWHO) {
1122                                 ret = copyinstr((char *)req.physpath, who,
1123                                     MAXNAMELEN, &wholen);
1124                                 if (ret) {
1125                                         PMD(PMD_ERROR, ("ioctl: %s copyinstr"
1126                                             " fails returning %d\n", cmdstr,
1127                                             ret))
1128                                         break;
1129                                 }
1130                                 req.physpath = who;
1131                                 PMD(PMD_IOCTL, ("ioctl: %s: physpath=%s\n",
1132                                     cmdstr, req.physpath))
1133                         }
1134                         if (!(pcip->inargs & INDATA)) {
1135                                 req.data = NULL;
1136                                 req.datasize = 0;
1137                         }
1138                         switch (pcip->diptype) {
1139                         case DIP:
1140                                 if (!(dip =
1141                                     pm_name_to_dip(req.physpath, 1))) {
1142                                         PMD(PMD_ERROR, ("ioctl: %s: "
1143                                             "pm_name_to_dip for %s failed\n",
1144                                             cmdstr, req.physpath))
1145                                         return (ENODEV);
1146                                 }
1147                                 ASSERT(!dipheld);
1148                                 dipheld++;
1149                                 break;
1150                         case NODIP:
1151                                 break;
1152                         default:
1153                                 /*
1154                                  * Internal error, invalid ioctl description
1155                                  * force debug entry even if pm_debug not set
1156                                  */
1157 #ifdef  DEBUG
1158                                 pm_log("invalid diptype %d for cmd %d (%s)\n",
1159                                     pcip->diptype, cmd, pcip->name);
1160 #endif
1161                                 ASSERT(0);
1162                                 return (EIO);
1163                         }
1164                         if (pcip->inargs & INDATAINT) {
1165                                 int *ip;
1166 
1167                                 ASSERT(!(pcip->inargs & INDATASTRING));
1168                                 ip = req.data;
1169                                 icount = req.datasize / sizeof (int);
1170                                 if (icount <= 0) {
1171                                         PMD(PMD_ERROR, ("ioctl: %s: datasize"
1172                                             " 0 or neg EFAULT\n\n", cmdstr))
1173                                         ret = EFAULT;
1174                                         break;
1175                                 }
1176                                 req.data = kmem_alloc(req.datasize, KM_SLEEP);
1177                                 if (ddi_copyin((caddr_t)ip, req.data,
1178                                     req.datasize, mode) != 0) {
1179                                         PMD(PMD_ERROR, ("ioctl: %s: ddi_copyin "
1180                                             "EFAULT\n\n", cmdstr))
1181                                         ret = EFAULT;
1182                                         break;
1183                                 }
1184                         }
1185                         if (pcip->inargs & INDATASTRING) {
1186                                 ASSERT(!(pcip->inargs & INDATAINT));
1187                                 ASSERT(pcip->deptype == DEP);
1188                                 if (req.data != NULL) {
1189                                         if (copyinstr((caddr_t)req.data,
1190                                             dep, deplen, NULL)) {
1191                                                 PMD(PMD_ERROR, ("ioctl: %s: "
1192                                                     "0x%p dep size %lu, "
1193                                                     "EFAULT\n", cmdstr,
1194                                                     (void *)req.data, deplen))
1195                                                 ret = EFAULT;
1196                                                 break;
1197                                         }
1198 #ifdef DEBUG
1199                                         else {
1200                                                 PMD(PMD_DEP, ("ioctl: %s: "
1201                                                     "dep %s\n", cmdstr, dep))
1202                                         }
1203 #endif
1204                                 } else {
1205                                         PMD(PMD_ERROR, ("ioctl: %s: no "
1206                                             "dependent\n", cmdstr))
1207                                         ret = EINVAL;
1208                                         break;
1209                                 }
1210                         }
1211                 }
1212                 /*
1213                  * Now we've got all the args in for the commands that
1214                  * use the new pm_req struct.
1215                  */
1216                 switch (cmd) {
1217                 case PM_REPARSE_PM_PROPS:
1218                 {
1219                         struct dev_ops  *drv;
1220                         struct cb_ops   *cb;
1221                         void            *propval;
1222                         int length;
1223                         /*
1224                          * This ioctl is provided only for the ddivs pm test.
1225                          * We only do it to a driver which explicitly allows
1226                          * us to do so by exporting a pm-reparse-ok property.
1227                          * We only care whether the property exists or not.
1228                          */
1229                         if ((drv = ddi_get_driver(dip)) == NULL) {
1230                                 ret = EINVAL;
1231                                 break;
1232                         }
1233                         if ((cb = drv->devo_cb_ops) != NULL) {
1234                                 if ((*cb->cb_prop_op)(DDI_DEV_T_ANY, dip,
1235                                     PROP_LEN_AND_VAL_ALLOC, (DDI_PROP_CANSLEEP |
1236                                     DDI_PROP_DONTPASS | DDI_PROP_NOTPROM),
1237                                     "pm-reparse-ok", (caddr_t)&propval,
1238                                     &length) != DDI_SUCCESS) {
1239                                         ret = EINVAL;
1240                                         break;
1241                                 }
1242                         } else if (ddi_prop_op(DDI_DEV_T_ANY, dip,
1243                             PROP_LEN_AND_VAL_ALLOC, (DDI_PROP_CANSLEEP |
1244                             DDI_PROP_DONTPASS | DDI_PROP_NOTPROM),
1245                             "pm-reparse-ok", (caddr_t)&propval,
1246                             &length) != DDI_SUCCESS) {
1247                                 ret = EINVAL;
1248                                 break;
1249                         }
1250                         kmem_free(propval, length);
1251                         ret =  e_new_pm_props(dip);
1252                         break;
1253                 }
1254 
1255                 case PM_GET_DEVICE_THRESHOLD:
1256                 {
1257                         PM_LOCK_DIP(dip);
1258                         if (!PM_GET_PM_INFO(dip) || PM_ISBC(dip)) {
1259                                 PM_UNLOCK_DIP(dip);
1260                                 PMD(PMD_ERROR, ("ioctl: %s: ENODEV\n",
1261                                     cmdstr))
1262                                 ret = ENODEV;
1263                                 break;
1264                         }
1265                         *rval_p = DEVI(dip)->devi_pm_dev_thresh;
1266                         PM_UNLOCK_DIP(dip);
1267                         ret = 0;
1268                         break;
1269                 }
1270 
1271                 case PM_DIRECT_PM:
1272                 {
1273                         int has_dep;
1274                         if ((info = PM_GET_PM_INFO(dip)) == NULL) {
1275                                 PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s: "
1276                                     "ENODEV\n", cmdstr))
1277                                 ret = ENODEV;
1278                                 break;
1279                         }
1280                         /*
1281                          * Check to see if we are there is a dependency on
1282                          * this kept device, if so, return EBUSY.
1283                          */
1284                         (void) ddi_pathname(dip, pathbuf);
1285                         pm_dispatch_to_dep_thread(PM_DEP_WK_CHECK_KEPT,
1286                             NULL, pathbuf, PM_DEP_WAIT, &has_dep, 0);
1287                         if (has_dep) {
1288                                 PMD(PMD_ERROR | PMD_DPM, ("%s EBUSY\n",
1289                                     cmdstr))
1290                                 ret = EBUSY;
1291                                 break;
1292                         }
1293                         PM_LOCK_DIP(dip);
1294                         if (PM_ISDIRECT(dip) || (info->pmi_clone != 0)) {
1295                                 PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s: "
1296                                     "%s@%s(%s#%d): EBUSY\n", cmdstr,
1297                                     PM_DEVICE(dip)))
1298                                 PM_UNLOCK_DIP(dip);
1299                                 ret = EBUSY;
1300                                 break;
1301                         }
1302                         info->pmi_dev_pm_state |= PM_DIRECT;
1303                         info->pmi_clone = clone;
1304                         PM_UNLOCK_DIP(dip);
1305                         PMD(PMD_DPM, ("ioctl: %s: info %p, pmi_clone %d\n",
1306                             cmdstr, (void *)info, clone))
1307                         mutex_enter(&pm_clone_lock);
1308                         pm_register_watcher(clone, dip);
1309                         mutex_exit(&pm_clone_lock);
1310                         ret = 0;
1311                         break;
1312                 }
1313 
1314                 case PM_RELEASE_DIRECT_PM:
1315                 {
1316                         if ((info = PM_GET_PM_INFO(dip)) == NULL) {
1317                                 PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s: "
1318                                     "ENODEV\n", cmdstr))
1319                                 ret = ENODEV;
1320                                 break;
1321                         }
1322                         PM_LOCK_DIP(dip);
1323                         if (info->pmi_clone != clone) {
1324                                 PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s: "
1325                                     "%s@%s(%s#%d) EINVAL\n", cmdstr,
1326                                     PM_DEVICE(dip)))
1327                                 ret = EINVAL;
1328                                 PM_UNLOCK_DIP(dip);
1329                                 break;
1330                         }
1331                         ASSERT(PM_ISDIRECT(dip));
1332                         info->pmi_dev_pm_state &= ~PM_DIRECT;
1333                         PM_UNLOCK_DIP(dip);
1334                         /* Bring ourselves up if there is a keeper. */
1335                         (void) ddi_pathname(dip, pathbuf);
1336                         pm_dispatch_to_dep_thread(PM_DEP_WK_BRINGUP_SELF,
1337                             NULL, pathbuf, PM_DEP_WAIT, NULL, 0);
1338                         pm_discard_entries(clone);
1339                         pm_deregister_watcher(clone, dip);
1340                         /*
1341                          * Now we could let the other threads that are
1342                          * trying to do a DIRECT_PM thru
1343                          */
1344                         PM_LOCK_DIP(dip);
1345                         info->pmi_clone = 0;
1346                         PM_UNLOCK_DIP(dip);
1347                         pm_proceed(dip, PMP_RELEASE, -1, -1);
1348                         PMD(PMD_RESCAN | PMD_DPM, ("ioctl: %s: rescan\n",
1349                             cmdstr))
1350                         pm_rescan(dip);
1351                         ret = 0;
1352                         break;
1353                 }
1354 
1355                 case PM_SET_CURRENT_POWER:
1356                 {
1357                         int comp = req.component;
1358                         int  value = req.value;
1359                         PMD(PMD_DPM, ("ioctl: %s: %s component %d to value "
1360                             "%d\n", cmdstr, req.physpath, comp, value))
1361                         if (!e_pm_valid_comp(dip, comp, NULL) ||
1362                             !e_pm_valid_power(dip, comp, value)) {
1363                                 PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s: "
1364                                     "physpath=%s, comp=%d, level=%d, fails\n",
1365                                     cmdstr, req.physpath, comp, value))
1366                                 ret = EINVAL;
1367                                 break;
1368                         }
1369 
1370                         if ((info = PM_GET_PM_INFO(dip)) == NULL) {
1371                                 PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s: "
1372                                     "ENODEV\n", cmdstr))
1373                                 ret = ENODEV;
1374                                 break;
1375                         }
1376                         if (info->pmi_clone != clone) {
1377                                 PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s: "
1378                                     "(not owner) %s fails; clone %d, owner %d"
1379                                     "\n", cmdstr, req.physpath, clone,
1380                                     info->pmi_clone))
1381                                 ret = EINVAL;
1382                                 break;
1383                         }
1384                         ASSERT(PM_ISDIRECT(dip));
1385 
1386                         if (pm_set_power(dip, comp, value, PM_LEVEL_EXACT,
1387                             PM_CANBLOCK_BLOCK, 0, &ret) != DDI_SUCCESS) {
1388                                 PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s: "
1389                                     "pm_set_power for %s fails, errno=%d\n",
1390                                     cmdstr, req.physpath, ret))
1391                                 break;
1392                         }
1393 
1394                         pm_proceed(dip, PMP_SETPOWER, comp, value);
1395 
1396                         /*
1397                          * Power down all idle components if console framebuffer
1398                          * is powered off.
1399                          */
1400                         if (PM_IS_CFB(dip) && (pm_system_idle_threshold ==
1401                             pm_default_idle_threshold)) {
1402                                 dev_info_t      *root = ddi_root_node();
1403                                 if (PM_ISBC(dip)) {
1404                                         if (comp == 0 && value == 0 &&
1405                                             (pm_timeout_idledown() != 0)) {
1406                                                 ddi_walk_devs(root,
1407                                                     pm_start_idledown,
1408                                                     (void *)PMID_CFB);
1409                                         }
1410                                 } else {
1411                                         int count = 0;
1412                                         for (i = 0; i < PM_NUMCMPTS(dip); i++) {
1413                                                 ret = pm_get_current_power(dip,
1414                                                     i, &curpower);
1415                                                 if (ret == DDI_SUCCESS &&
1416                                                     curpower == 0)
1417                                                         count++;
1418                                         }
1419                                         if ((count == PM_NUMCMPTS(dip)) &&
1420                                             (pm_timeout_idledown() != 0)) {
1421                                                 ddi_walk_devs(root,
1422                                                     pm_start_idledown,
1423                                                     (void *)PMID_CFB);
1424                                         }
1425                                 }
1426                         }
1427 
1428                         PMD(PMD_RESCAN | PMD_DPM, ("ioctl: %s: rescan\n",
1429                             cmdstr))
1430                         pm_rescan(dip);
1431                         *rval_p = 0;
1432                         ret = 0;
1433                         break;
1434                 }
1435 
1436                 case PM_GET_FULL_POWER:
1437                 {
1438                         int normal;
1439                         ASSERT(dip);
1440                         PMD(PMD_NORM, ("ioctl: %s: %s component %d\n",
1441                             cmdstr, req.physpath, req.component))
1442                         normal =  pm_get_normal_power(dip, req.component);
1443 
1444                         if (normal == DDI_FAILURE) {
1445                                 PMD(PMD_ERROR | PMD_NORM, ("ioctl: %s: "
1446                                     "returns EINVAL\n", cmdstr))
1447                                 ret = EINVAL;
1448                                 break;
1449                         }
1450                         *rval_p = normal;
1451                         PMD(PMD_NORM, ("ioctl: %s: returns %d\n",
1452                             cmdstr, normal))
1453                         ret = 0;
1454                         break;
1455                 }
1456 
1457                 case PM_GET_CURRENT_POWER:
1458                 {
1459                         if (pm_get_current_power(dip, req.component,
1460                             rval_p) != DDI_SUCCESS) {
1461                                 PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s "
1462                                     "EINVAL\n", cmdstr))
1463                                 ret = EINVAL;
1464                                 break;
1465                         }
1466                         PMD(PMD_DPM, ("ioctl: %s: %s comp %d returns %d\n",
1467                             cmdstr, req.physpath, req.component, *rval_p))
1468                         if (*rval_p == PM_LEVEL_UNKNOWN)
1469                                 ret = EAGAIN;
1470                         else
1471                                 ret = 0;
1472                         break;
1473                 }
1474 
1475                 case PM_GET_TIME_IDLE:
1476                 {
1477                         time_t timestamp;
1478                         int comp = req.component;
1479                         pm_component_t *cp;
1480                         if (!e_pm_valid_comp(dip, comp, &cp)) {
1481                                 PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) "
1482                                     "component %d > numcmpts - 1 %d--EINVAL\n",
1483                                     cmdstr, PM_DEVICE(dip), comp,
1484                                     PM_NUMCMPTS(dip) - 1))
1485                                 ret = EINVAL;
1486                                 break;
1487                         }
1488                         timestamp = cp->pmc_timestamp;
1489                         if (timestamp) {
1490                                 time_t now;
1491                                 (void) drv_getparm(TIME, &now);
1492                                 *rval_p = (now - timestamp);
1493                         } else {
1494                                 *rval_p = 0;
1495                         }
1496                         ret = 0;
1497                         break;
1498                 }
1499 
1500                 case PM_ADD_DEPENDENT:
1501                 {
1502                         dev_info_t      *kept_dip;
1503 
1504                         PMD(PMD_KEEPS, ("%s, kept %s, keeper %s\n", cmdstr,
1505                             dep, req.physpath))
1506 
1507                         /*
1508                          * hold and install kept while processing dependency
1509                          * keeper (in .physpath) has already been held.
1510                          */
1511                         if (dep[0] == '\0') {
1512                                 PMD(PMD_ERROR, ("kept NULL or null\n"))
1513                                 ret = EINVAL;
1514                                 break;
1515                         } else if ((kept_dip =
1516                             pm_name_to_dip(dep, 1)) == NULL) {
1517                                 PMD(PMD_ERROR, ("no dip for kept %s\n", dep))
1518                                 ret = ENODEV;
1519                                 break;
1520                         } else if (kept_dip == dip) {
1521                                 PMD(PMD_ERROR, ("keeper(%s, %p) - kept(%s, %p) "
1522                                     "self-dependency not allowed.\n",
1523                                     dep, (void *)kept_dip, req.physpath,
1524                                     (void *) dip))
1525                                 PM_RELE(dip);   /* release "double" hold */
1526                                 ret = EINVAL;
1527                                 break;
1528                         }
1529                         ASSERT(!(strcmp(req.physpath, (char *)dep) == 0));
1530 
1531                         /*
1532                          * record dependency, then walk through device tree
1533                          * independently on behalf of kept and keeper to
1534                          * establish newly created dependency.
1535                          */
1536                         pm_dispatch_to_dep_thread(PM_DEP_WK_RECORD_KEEPER,
1537                             req.physpath, dep, PM_DEP_WAIT, NULL, 0);
1538 
1539                         /*
1540                          * release kept after establishing dependency, keeper
1541                          * is released as part of ioctl exit processing.
1542                          */
1543                         PM_RELE(kept_dip);
1544                         *rval_p = 0;
1545                         ret = 0;
1546                         break;
1547                 }
1548 
1549                 case PM_ADD_DEPENDENT_PROPERTY:
1550                 {
1551                         char *keeper, *kept;
1552 
1553                         if (dep[0] == '\0') {
1554                                 PMD(PMD_ERROR, ("ioctl: %s: dep NULL or "
1555                                     "null\n", cmdstr))
1556                                 ret = EINVAL;
1557                                 break;
1558                         }
1559                         kept = dep;
1560                         keeper = req.physpath;
1561                         /*
1562                          * record keeper - kept dependency, then walk through
1563                          * device tree to find out all attached keeper, walk
1564                          * through again to apply dependency to all the
1565                          * potential kept.
1566                          */
1567                         pm_dispatch_to_dep_thread(
1568                             PM_DEP_WK_RECORD_KEEPER_PROP, keeper, kept,
1569                             PM_DEP_WAIT, NULL, 0);
1570 
1571                         *rval_p = 0;
1572                         ret = 0;
1573                         break;
1574                 }
1575 
1576                 case PM_SET_DEVICE_THRESHOLD:
1577                 {
1578                         pm_thresh_rec_t *rp;
1579                         pm_pte_t *ep;   /* threshold header storage */
1580                         int *tp;        /* threshold storage */
1581                         size_t size;
1582                         extern int pm_thresh_specd(dev_info_t *);
1583 
1584                         /*
1585                          * The header struct plus one entry struct plus one
1586                          * threshold plus the length of the string
1587                          */
1588                         size = sizeof (pm_thresh_rec_t) +
1589                             (sizeof (pm_pte_t) * 1) +
1590                             (1 * sizeof (int)) +
1591                             strlen(req.physpath) + 1;
1592 
1593                         rp = kmem_zalloc(size, KM_SLEEP);
1594                         rp->ptr_size = size;
1595                         rp->ptr_numcomps = 0;        /* means device threshold */
1596                         ep = (pm_pte_t *)((intptr_t)rp + sizeof (*rp));
1597                         rp->ptr_entries = ep;
1598                         tp = (int *)((intptr_t)ep +
1599                             (1 * sizeof (pm_pte_t)));
1600                         ep->pte_numthresh = 1;
1601                         ep->pte_thresh = tp;
1602                         *tp++ = req.value;
1603                         (void) strcat((char *)tp, req.physpath);
1604                         rp->ptr_physpath = (char *)tp;
1605                         ASSERT((intptr_t)tp + strlen(req.physpath) + 1 ==
1606                             (intptr_t)rp + rp->ptr_size);
1607                         PMD(PMD_THRESH, ("ioctl: %s: record thresh %d for "
1608                             "%s\n", cmdstr, req.value, req.physpath))
1609                         pm_record_thresh(rp);
1610                         /*
1611                          * Don't free rp, pm_record_thresh() keeps it.
1612                          * We don't try to apply it ourselves because we'd need
1613                          * to know too much about locking.  Since we don't
1614                          * hold a lock the entry could be removed before
1615                          * we get here
1616                          */
1617                         ASSERT(dip == NULL);
1618                         ret = 0;                /* can't fail now */
1619                         if (!(dip = pm_name_to_dip(req.physpath, 1))) {
1620                                 break;
1621                         }
1622                         (void) pm_thresh_specd(dip);
1623                         PMD(PMD_DHR, ("ioctl: %s: releasing %s@%s(%s#%d)\n",
1624                             cmdstr, PM_DEVICE(dip)))
1625                         PM_RELE(dip);
1626                         break;
1627                 }
1628 
1629                 case PM_RESET_DEVICE_THRESHOLD:
1630                 {
1631                         /*
1632                          * This only applies to a currently attached and power
1633                          * managed node
1634                          */
1635                         /*
1636                          * We don't do this to old-style drivers
1637                          */
1638                         info = PM_GET_PM_INFO(dip);
1639                         if (info == NULL) {
1640                                 PMD(PMD_ERROR, ("ioctl: %s: %s not power "
1641                                     "managed\n", cmdstr, req.physpath))
1642                                 ret = EINVAL;
1643                                 break;
1644                         }
1645                         if (PM_ISBC(dip)) {
1646                                 PMD(PMD_ERROR, ("ioctl: %s: %s is BC\n",
1647                                     cmdstr, req.physpath))
1648                                 ret = EINVAL;
1649                                 break;
1650                         }
1651                         pm_unrecord_threshold(req.physpath);
1652                         if (DEVI(dip)->devi_pm_flags & PMC_CPU_THRESH)
1653                                 pm_set_device_threshold(dip,
1654                                     pm_cpu_idle_threshold, PMC_CPU_THRESH);
1655                         else
1656                                 pm_set_device_threshold(dip,
1657                                     pm_system_idle_threshold, PMC_DEF_THRESH);
1658                         ret = 0;
1659                         break;
1660                 }
1661 
1662                 case PM_GET_NUM_COMPONENTS:
1663                 {
1664                         ret = 0;
1665                         *rval_p = PM_NUMCMPTS(dip);
1666                         break;
1667                 }
1668 
1669                 case PM_GET_DEVICE_TYPE:
1670                 {
1671                         ret = 0;
1672                         if ((info = PM_GET_PM_INFO(dip)) == NULL) {
1673                                 PMD(PMD_ERROR, ("ioctl: %s: "
1674                                     "PM_NO_PM_COMPONENTS\n", cmdstr))
1675                                 *rval_p = PM_NO_PM_COMPONENTS;
1676                                 break;
1677                         }
1678                         if (PM_ISBC(dip)) {
1679                                 *rval_p = PM_CREATE_COMPONENTS;
1680                         } else {
1681                                 *rval_p = PM_AUTOPM;
1682                         }
1683                         break;
1684                 }
1685 
1686                 case PM_SET_COMPONENT_THRESHOLDS:
1687                 {
1688                         int comps = 0;
1689                         int *end = (int *)req.data + icount;
1690                         pm_thresh_rec_t *rp;
1691                         pm_pte_t *ep;   /* threshold header storage */
1692                         int *tp;        /* threshold storage */
1693                         int *ip;
1694                         int j;
1695                         size_t size;
1696                         extern int pm_thresh_specd(dev_info_t *);
1697                         extern int pm_valid_thresh(dev_info_t *,
1698                             pm_thresh_rec_t *);
1699 
1700                         for (ip = req.data; *ip; ip++) {
1701                                 if (ip >= end) {
1702                                         ret = EFAULT;
1703                                         break;
1704                                 }
1705                                 comps++;
1706                                 /* skip over indicated number of entries */
1707                                 for (j = *ip; j; j--) {
1708                                         if (++ip >= end) {
1709                                                 ret = EFAULT;
1710                                                 break;
1711                                         }
1712                                 }
1713                                 if (ret)
1714                                         break;
1715                         }
1716                         if (ret)
1717                                 break;
1718                         if ((intptr_t)ip != (intptr_t)end - sizeof (int)) {
1719                                 /* did not exactly fill buffer */
1720                                 ret = EINVAL;
1721                                 break;
1722                         }
1723                         if (comps == 0) {
1724                                 PMD(PMD_ERROR, ("ioctl: %s: %s 0 components"
1725                                     "--EINVAL\n", cmdstr, req.physpath))
1726                                 ret = EINVAL;
1727                                 break;
1728                         }
1729                         /*
1730                          * The header struct plus one entry struct per component
1731                          * plus the size of the lists minus the counts
1732                          * plus the length of the string
1733                          */
1734                         size = sizeof (pm_thresh_rec_t) +
1735                             (sizeof (pm_pte_t) * comps) + req.datasize -
1736                             ((comps + 1) * sizeof (int)) +
1737                             strlen(req.physpath) + 1;
1738 
1739                         rp = kmem_zalloc(size, KM_SLEEP);
1740                         rp->ptr_size = size;
1741                         rp->ptr_numcomps = comps;
1742                         ep = (pm_pte_t *)((intptr_t)rp + sizeof (*rp));
1743                         rp->ptr_entries = ep;
1744                         tp = (int *)((intptr_t)ep +
1745                             (comps * sizeof (pm_pte_t)));
1746                         for (ip = req.data; *ip; ep++) {
1747                                 ep->pte_numthresh = *ip;
1748                                 ep->pte_thresh = tp;
1749                                 for (j = *ip++; j; j--) {
1750                                         *tp++ = *ip++;
1751                                 }
1752                         }
1753                         (void) strcat((char *)tp, req.physpath);
1754                         rp->ptr_physpath = (char *)tp;
1755                         ASSERT((intptr_t)end == (intptr_t)ip + sizeof (int));
1756                         ASSERT((intptr_t)tp + strlen(req.physpath) + 1 ==
1757                             (intptr_t)rp + rp->ptr_size);
1758 
1759                         ASSERT(dip == NULL);
1760                         /*
1761                          * If this is not a currently power managed node,
1762                          * then we can't check for validity of the thresholds
1763                          */
1764                         if (!(dip = pm_name_to_dip(req.physpath, 1))) {
1765                                 /* don't free rp, pm_record_thresh uses it */
1766                                 pm_record_thresh(rp);
1767                                 PMD(PMD_ERROR, ("ioctl: %s: pm_name_to_dip "
1768                                     "for %s failed\n", cmdstr, req.physpath))
1769                                 ret = 0;
1770                                 break;
1771                         }
1772                         ASSERT(!dipheld);
1773                         dipheld++;
1774 
1775                         if (!pm_valid_thresh(dip, rp)) {
1776                                 PMD(PMD_ERROR, ("ioctl: %s: invalid thresh "
1777                                     "for %s@%s(%s#%d)\n", cmdstr,
1778                                     PM_DEVICE(dip)))
1779                                 kmem_free(rp, size);
1780                                 ret = EINVAL;
1781                                 break;
1782                         }
1783                         /*
1784                          * We don't just apply it ourselves because we'd need
1785                          * to know too much about locking.  Since we don't
1786                          * hold a lock the entry could be removed before
1787                          * we get here
1788                          */
1789                         pm_record_thresh(rp);
1790                         (void) pm_thresh_specd(dip);
1791                         ret = 0;
1792                         break;
1793                 }
1794 
1795                 case PM_GET_COMPONENT_THRESHOLDS:
1796                 {
1797                         int musthave;
1798                         int numthresholds = 0;
1799                         int wordsize;
1800                         int numcomps;
1801                         caddr_t uaddr = req.data;       /* user address */
1802                         int val;        /* int value to be copied out */
1803                         int32_t val32;  /* int32 value to be copied out */
1804                         caddr_t vaddr;  /* address to copyout from */
1805                         int j;
1806 
1807 #ifdef  _MULTI_DATAMODEL
1808                         if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) {
1809                                 wordsize = sizeof (int32_t);
1810                         } else
1811 #endif /* _MULTI_DATAMODEL */
1812                         {
1813                                 wordsize = sizeof (int);
1814                         }
1815 
1816                         ASSERT(dip);
1817 
1818                         numcomps = PM_NUMCMPTS(dip);
1819                         for (i = 0; i < numcomps; i++) {
1820                                 cp = PM_CP(dip, i);
1821                                 numthresholds += cp->pmc_comp.pmc_numlevels - 1;
1822                         }
1823                         musthave = (numthresholds + numcomps + 1) *  wordsize;
1824                         if (req.datasize < musthave) {
1825                                 PMD(PMD_ERROR, ("ioctl: %s: size %ld, need "
1826                                     "%d--EINVAL\n", cmdstr, req.datasize,
1827                                     musthave))
1828                                 ret = EINVAL;
1829                                 break;
1830                         }
1831                         PM_LOCK_DIP(dip);
1832                         for (i = 0; i < numcomps; i++) {
1833                                 int *thp;
1834                                 cp = PM_CP(dip, i);
1835                                 thp = cp->pmc_comp.pmc_thresh;
1836                                 /* first copyout the count */
1837                                 if (wordsize == sizeof (int32_t)) {
1838                                         val32 = cp->pmc_comp.pmc_numlevels - 1;
1839                                         vaddr = (caddr_t)&val32;
1840                                 } else {
1841                                         val = cp->pmc_comp.pmc_numlevels - 1;
1842                                         vaddr = (caddr_t)&val;
1843                                 }
1844                                 if (ddi_copyout(vaddr, (void *)uaddr,
1845                                     wordsize, mode) != 0) {
1846                                         PM_UNLOCK_DIP(dip);
1847                                         PMD(PMD_ERROR, ("ioctl: %s: %s@%s"
1848                                             "(%s#%d) vaddr %p EFAULT\n",
1849                                             cmdstr, PM_DEVICE(dip),
1850                                             (void*)vaddr))
1851                                         ret = EFAULT;
1852                                         break;
1853                                 }
1854                                 vaddr = uaddr;
1855                                 vaddr += wordsize;
1856                                 uaddr = (caddr_t)vaddr;
1857                                 /* then copyout each threshold value */
1858                                 for (j = 0; j < cp->pmc_comp.pmc_numlevels - 1;
1859                                     j++) {
1860                                         if (wordsize == sizeof (int32_t)) {
1861                                                 val32 = thp[j + 1];
1862                                                 vaddr = (caddr_t)&val32;
1863                                         } else {
1864                                                 val = thp[i + 1];
1865                                                 vaddr = (caddr_t)&val;
1866                                         }
1867                                         if (ddi_copyout(vaddr, (void *) uaddr,
1868                                             wordsize, mode) != 0) {
1869                                                 PM_UNLOCK_DIP(dip);
1870                                                 PMD(PMD_ERROR, ("ioctl: %s: "
1871                                                     "%s@%s(%s#%d) uaddr %p "
1872                                                     "EFAULT\n", cmdstr,
1873                                                     PM_DEVICE(dip),
1874                                                     (void *)uaddr))
1875                                                 ret = EFAULT;
1876                                                 break;
1877                                         }
1878                                         vaddr = uaddr;
1879                                         vaddr += wordsize;
1880                                         uaddr = (caddr_t)vaddr;
1881                                 }
1882                         }
1883                         if (ret)
1884                                 break;
1885                         /* last copyout a terminating 0 count */
1886                         if (wordsize == sizeof (int32_t)) {
1887                                 val32 = 0;
1888                                 vaddr = (caddr_t)&val32;
1889                         } else {
1890                                 ASSERT(wordsize == sizeof (int));
1891                                 val = 0;
1892                                 vaddr = (caddr_t)&val;
1893                         }
1894                         if (ddi_copyout(vaddr, uaddr, wordsize, mode) != 0) {
1895                                 PM_UNLOCK_DIP(dip);
1896                                 PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) "
1897                                     "vaddr %p (0 count) EFAULT\n", cmdstr,
1898                                     PM_DEVICE(dip), (void *)vaddr))
1899                                 ret = EFAULT;
1900                                 break;
1901                         }
1902                         /* finished, so don't need to increment addresses */
1903                         PM_UNLOCK_DIP(dip);
1904                         ret = 0;
1905                         break;
1906                 }
1907 
1908                 case PM_GET_STATS:
1909                 {
1910                         time_t now;
1911                         time_t *timestamp;
1912                         extern int pm_cur_power(pm_component_t *cp);
1913                         int musthave;
1914                         int wordsize;
1915 
1916 #ifdef  _MULTI_DATAMODEL
1917                         if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) {
1918                                 wordsize = sizeof (int32_t);
1919                         } else
1920 #endif /* _MULTI_DATAMODEL */
1921                         {
1922                                 wordsize = sizeof (int);
1923                         }
1924 
1925                         comps = PM_NUMCMPTS(dip);
1926                         if (comps == 0 || PM_GET_PM_INFO(dip) == NULL) {
1927                                 PMD(PMD_ERROR, ("ioctl: %s: %s no components"
1928                                     " or not power managed--EINVAL\n", cmdstr,
1929                                     req.physpath))
1930                                 ret = EINVAL;
1931                                 break;
1932                         }
1933                         musthave = comps * 2 * wordsize;
1934                         if (req.datasize < musthave) {
1935                                 PMD(PMD_ERROR, ("ioctl: %s: size %lu, need "
1936                                     "%d--EINVAL\n", cmdstr, req.datasize,
1937                                     musthave))
1938                                 ret = EINVAL;
1939                                 break;
1940                         }
1941 
1942                         PM_LOCK_DIP(dip);
1943                         (void) drv_getparm(TIME, &now);
1944                         timestamp = kmem_zalloc(comps * sizeof (time_t),
1945                             KM_SLEEP);
1946                         pm_get_timestamps(dip, timestamp);
1947                         /*
1948                          * First the current power levels
1949                          */
1950                         for (i = 0; i < comps; i++) {
1951                                 int curpwr;
1952                                 int32_t curpwr32;
1953                                 caddr_t cpaddr;
1954 
1955                                 cp = PM_CP(dip, i);
1956                                 if (wordsize == sizeof (int)) {
1957                                         curpwr = pm_cur_power(cp);
1958                                         cpaddr = (caddr_t)&curpwr;
1959                                 } else {
1960                                         ASSERT(wordsize == sizeof (int32_t));
1961                                         curpwr32 = pm_cur_power(cp);
1962                                         cpaddr = (caddr_t)&curpwr32;
1963                                 }
1964                                 if (ddi_copyout(cpaddr, (void *) req.data,
1965                                     wordsize, mode) != 0) {
1966                                         PM_UNLOCK_DIP(dip);
1967                                         PMD(PMD_ERROR, ("ioctl: %s: %s@%s"
1968                                             "(%s#%d) req.data %p EFAULT\n",
1969                                             cmdstr, PM_DEVICE(dip),
1970                                             (void *)req.data))
1971                                         ASSERT(!dipheld);
1972                                         return (EFAULT);
1973                                 }
1974                                 cpaddr = (caddr_t)req.data;
1975                                 cpaddr += wordsize;
1976                                 req.data = cpaddr;
1977                         }
1978                         /*
1979                          * Then the times remaining
1980                          */
1981                         for (i = 0; i < comps; i++) {
1982                                 int retval;
1983                                 int32_t retval32;
1984                                 caddr_t rvaddr;
1985                                 int curpwr;
1986 
1987                                 cp = PM_CP(dip, i);
1988                                 curpwr = cp->pmc_cur_pwr;
1989                                 if (curpwr == 0 || timestamp[i] == 0) {
1990                                         PMD(PMD_STATS, ("ioctl: %s: "
1991                                             "cur_pwer %x, timestamp %lx\n",
1992                                             cmdstr, curpwr, timestamp[i]))
1993                                         retval = INT_MAX;
1994                                 } else {
1995                                         int thresh;
1996                                         (void) pm_current_threshold(dip, i,
1997                                             &thresh);
1998                                         retval = thresh - (now - timestamp[i]);
1999                                         PMD(PMD_STATS, ("ioctl: %s: current "
2000                                             "thresh %x, now %lx, timestamp %lx,"
2001                                             " retval %x\n", cmdstr, thresh, now,
2002                                             timestamp[i], retval))
2003                                 }
2004                                 if (wordsize == sizeof (int)) {
2005                                         rvaddr = (caddr_t)&retval;
2006                                 } else {
2007                                         ASSERT(wordsize == sizeof (int32_t));
2008                                         retval32 = retval;
2009                                         rvaddr = (caddr_t)&retval32;
2010                                 }
2011                                 if (ddi_copyout(rvaddr, (void *) req.data,
2012                                     wordsize, mode) != 0) {
2013                                         PM_UNLOCK_DIP(dip);
2014                                         PMD(PMD_ERROR, ("ioctl: %s: %s@%s"
2015                                             "(%s#%d) req.data %p EFAULT\n",
2016                                             cmdstr, PM_DEVICE(dip),
2017                                             (void *)req.data))
2018                                         ASSERT(!dipheld);
2019                                         kmem_free(timestamp,
2020                                             comps * sizeof (time_t));
2021                                         return (EFAULT);
2022                                 }
2023                                 rvaddr = (caddr_t)req.data;
2024                                 rvaddr += wordsize;
2025                                 req.data = (int *)rvaddr;
2026                         }
2027                         PM_UNLOCK_DIP(dip);
2028                         *rval_p = comps;
2029                         ret = 0;
2030                         kmem_free(timestamp, comps * sizeof (time_t));
2031                         break;
2032                 }
2033 
2034                 case PM_GET_CMD_NAME:
2035                 {
2036                         PMD(PMD_IOCTL, ("%s: %s\n", cmdstr,
2037                             pm_decode_cmd(req.value)))
2038                         if (ret = copyoutstr(pm_decode_cmd(req.value),
2039                             (char *)req.data, req.datasize, &lencopied)) {
2040                                 PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) "
2041                                     "copyoutstr %p failed--EFAULT\n", cmdstr,
2042                                     PM_DEVICE(dip), (void *)req.data))
2043                                 break;
2044                         }
2045                         *rval_p = lencopied;
2046                         ret = 0;
2047                         break;
2048                 }
2049 
2050                 case PM_GET_COMPONENT_NAME:
2051                 {
2052                         ASSERT(dip);
2053                         if (!e_pm_valid_comp(dip, req.component, &cp)) {
2054                                 PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) "
2055                                     "component %d > numcmpts - 1 %d--EINVAL\n",
2056                                     cmdstr, PM_DEVICE(dip), req.component,
2057                                     PM_NUMCMPTS(dip) - 1))
2058                                 ret = EINVAL;
2059                                 break;
2060                         }
2061                         if (ret = copyoutstr(cp->pmc_comp.pmc_name,
2062                             (char *)req.data, req.datasize, &lencopied)) {
2063                                 PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) "
2064                                     "copyoutstr %p failed--EFAULT\n", cmdstr,
2065                                     PM_DEVICE(dip), (void *)req.data))
2066                                 break;
2067                         }
2068                         *rval_p = lencopied;
2069                         ret = 0;
2070                         break;
2071                 }
2072 
2073                 case PM_GET_POWER_NAME:
2074                 {
2075                         int i;
2076 
2077                         ASSERT(dip);
2078                         if (!e_pm_valid_comp(dip, req.component, &cp)) {
2079                                 PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) "
2080                                     "component %d > numcmpts - 1 %d--EINVAL\n",
2081                                     cmdstr, PM_DEVICE(dip), req.component,
2082                                     PM_NUMCMPTS(dip) - 1))
2083                                 ret = EINVAL;
2084                                 break;
2085                         }
2086                         if ((i = req.value) < 0 ||
2087                             i > cp->pmc_comp.pmc_numlevels - 1) {
2088                                 PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) "
2089                                     "value %d > num_levels - 1 %d--EINVAL\n",
2090                                     cmdstr, PM_DEVICE(dip), req.value,
2091                                     cp->pmc_comp.pmc_numlevels - 1))
2092                                 ret = EINVAL;
2093                                 break;
2094                         }
2095                         dep = cp->pmc_comp.pmc_lnames[req.value];
2096                         if (ret = copyoutstr(dep,
2097                             req.data, req.datasize, &lencopied)) {
2098                                 PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) "
2099                                     "copyoutstr %p failed--EFAULT\n", cmdstr,
2100                                     PM_DEVICE(dip), (void *)req.data))
2101                                 break;
2102                         }
2103                         *rval_p = lencopied;
2104                         ret = 0;
2105                         break;
2106                 }
2107 
2108                 case PM_GET_POWER_LEVELS:
2109                 {
2110                         int musthave;
2111                         int numlevels;
2112                         int wordsize;
2113 
2114 #ifdef  _MULTI_DATAMODEL
2115                         if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) {
2116                                 wordsize = sizeof (int32_t);
2117                         } else
2118 #endif /* _MULTI_DATAMODEL */
2119                         {
2120                                 wordsize = sizeof (int);
2121                         }
2122                         ASSERT(dip);
2123 
2124                         if (!e_pm_valid_comp(dip, req.component, &cp)) {
2125                                 PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) "
2126                                     "has %d components, component %d requested"
2127                                     "--EINVAL\n", cmdstr, PM_DEVICE(dip),
2128                                     PM_NUMCMPTS(dip), req.component))
2129                                 ret = EINVAL;
2130                                 break;
2131                         }
2132                         numlevels = cp->pmc_comp.pmc_numlevels;
2133                         musthave = numlevels *  wordsize;
2134                         if (req.datasize < musthave) {
2135                                 PMD(PMD_ERROR, ("ioctl: %s: size %lu, need "
2136                                     "%d--EINVAL\n", cmdstr, req.datasize,
2137                                     musthave))
2138                                 ret = EINVAL;
2139                                 break;
2140                         }
2141                         PM_LOCK_DIP(dip);
2142                         for (i = 0; i < numlevels; i++) {
2143                                 int level;
2144                                 int32_t level32;
2145                                 caddr_t laddr;
2146 
2147                                 if (wordsize == sizeof (int)) {
2148                                         level = cp->pmc_comp.pmc_lvals[i];
2149                                         laddr = (caddr_t)&level;
2150                                 } else {
2151                                         level32 = cp->pmc_comp.pmc_lvals[i];
2152                                         laddr = (caddr_t)&level32;
2153                                 }
2154                                 if (ddi_copyout(laddr, (void *) req.data,
2155                                     wordsize, mode) != 0) {
2156                                         PM_UNLOCK_DIP(dip);
2157                                         PMD(PMD_ERROR, ("ioctl: %s: %s@%s"
2158                                             "(%s#%d) laddr %p EFAULT\n",
2159                                             cmdstr, PM_DEVICE(dip),
2160                                             (void *)laddr))
2161                                         ASSERT(!dipheld);
2162                                         return (EFAULT);
2163                                 }
2164                                 laddr = (caddr_t)req.data;
2165                                 laddr += wordsize;
2166                                 req.data = (int *)laddr;
2167                         }
2168                         PM_UNLOCK_DIP(dip);
2169                         *rval_p = numlevels;
2170                         ret = 0;
2171                         break;
2172                 }
2173 
2174 
2175                 case PM_GET_NUM_POWER_LEVELS:
2176                 {
2177                         if (!e_pm_valid_comp(dip, req.component, &cp)) {
2178                                 PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) "
2179                                     "component %d > numcmpts - 1 %d--EINVAL\n",
2180                                     cmdstr, PM_DEVICE(dip), req.component,
2181                                     PM_NUMCMPTS(dip) - 1))
2182                                 ret = EINVAL;
2183                                 break;
2184                         }
2185                         *rval_p = cp->pmc_comp.pmc_numlevels;
2186                         ret = 0;
2187                         break;
2188                 }
2189 
2190                 case PM_GET_DEVICE_THRESHOLD_BASIS:
2191                 {
2192                         ret = 0;
2193                         PM_LOCK_DIP(dip);
2194                         if ((info = PM_GET_PM_INFO(dip)) == NULL) {
2195                                 PM_UNLOCK_DIP(dip);
2196                                 PMD(PMD_ERROR, ("ioctl: %s: "
2197                                     "PM_NO_PM_COMPONENTS\n", cmdstr))
2198                                 *rval_p = PM_NO_PM_COMPONENTS;
2199                                 break;
2200                         }
2201                         if (PM_ISDIRECT(dip)) {
2202                                 PM_UNLOCK_DIP(dip);
2203                                 *rval_p = PM_DIRECTLY_MANAGED;
2204                                 break;
2205                         }
2206                         switch (DEVI(dip)->devi_pm_flags & PMC_THRESH_ALL) {
2207                         case PMC_DEF_THRESH:
2208                         case PMC_NEXDEF_THRESH:
2209                                 *rval_p = PM_DEFAULT_THRESHOLD;
2210                                 break;
2211                         case PMC_DEV_THRESH:
2212                                 *rval_p = PM_DEVICE_THRESHOLD;
2213                                 break;
2214                         case PMC_COMP_THRESH:
2215                                 *rval_p = PM_COMPONENT_THRESHOLD;
2216                                 break;
2217                         case PMC_CPU_THRESH:
2218                                 *rval_p = PM_CPU_THRESHOLD;
2219                                 break;
2220                         default:
2221                                 if (PM_ISBC(dip)) {
2222                                         *rval_p = PM_OLD_THRESHOLD;
2223                                         break;
2224                                 }
2225                                 PMD(PMD_ERROR, ("ioctl: %s: default, not "
2226                                     "BC--EINVAL", cmdstr))
2227                                 ret = EINVAL;
2228                                 break;
2229                         }
2230                         PM_UNLOCK_DIP(dip);
2231                         break;
2232                 }
2233                 default:
2234                         /*
2235                          * Internal error, invalid ioctl description
2236                          * force debug entry even if pm_debug not set
2237                          */
2238 #ifdef  DEBUG
2239                         pm_log("invalid diptype %d for cmd %d (%s)\n",
2240                             pcip->diptype, cmd, pcip->name);
2241 #endif
2242                         ASSERT(0);
2243                         return (EIO);
2244                 }
2245                 break;
2246         }
2247 
2248         case PM_PSC:
2249         {
2250                 /*
2251                  * Commands that require pm_state_change_t as arg
2252                  */
2253 #ifdef  _MULTI_DATAMODEL
2254                 if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) {
2255                         pscp32 = (pm_state_change32_t *)arg;
2256                         if (ddi_copyin((caddr_t)arg, &psc32,
2257                             sizeof (psc32), mode) != 0) {
2258                                 PMD(PMD_ERROR, ("ioctl: %s: ddi_copyin "
2259                                     "EFAULT\n\n", cmdstr))
2260                                 ASSERT(!dipheld);
2261                                 return (EFAULT);
2262                         }
2263                         psc.physpath = (caddr_t)(uintptr_t)psc32.physpath;
2264                         psc.size = psc32.size;
2265                 } else
2266 #endif /* _MULTI_DATAMODEL */
2267                 {
2268                         pscp = (pm_state_change_t *)arg;
2269                         if (ddi_copyin((caddr_t)arg, &psc,
2270                             sizeof (psc), mode) != 0) {
2271                                 PMD(PMD_ERROR, ("ioctl: %s: ddi_copyin "
2272                                     "EFAULT\n\n", cmdstr))
2273                                 ASSERT(!dipheld);
2274                                 return (EFAULT);
2275                         }
2276                 }
2277                 switch (cmd) {
2278 
2279                 case PM_GET_STATE_CHANGE:
2280                 case PM_GET_STATE_CHANGE_WAIT:
2281                 {
2282                         psce_t                  *pscep;
2283                         pm_state_change_t       *p;
2284                         caddr_t                 physpath;
2285                         size_t                  physlen;
2286 
2287                         /*
2288                          * We want to know if any device has changed state.
2289                          * We look up by clone.  In case we have another thread
2290                          * from the same process, we loop.
2291                          * pm_psc_clone_to_interest() returns a locked entry.
2292                          * We create an internal copy of the event entry prior
2293                          * to copyout to user space because we don't want to
2294                          * hold the psce_lock while doing copyout as we might
2295                          * hit page fault  which eventually brings us back
2296                          * here requesting the same lock.
2297                          */
2298                         mutex_enter(&pm_clone_lock);
2299                         if (!pm_interest_registered(clone))
2300                                 pm_register_watcher(clone, NULL);
2301                         while ((pscep =
2302                             pm_psc_clone_to_interest(clone)) == NULL) {
2303                                 if (cmd == PM_GET_STATE_CHANGE) {
2304                                         PMD(PMD_IOCTL, ("ioctl: %s: "
2305                                             "EWOULDBLOCK\n", cmdstr))
2306                                         mutex_exit(&pm_clone_lock);
2307                                         ASSERT(!dipheld);
2308                                         return (EWOULDBLOCK);
2309                                 } else {
2310                                         if (cv_wait_sig(&pm_clones_cv[clone],
2311                                             &pm_clone_lock) == 0) {
2312                                                 mutex_exit(&pm_clone_lock);
2313                                                 PMD(PMD_ERROR, ("ioctl: %s "
2314                                                     "EINTR\n", cmdstr))
2315                                                 ASSERT(!dipheld);
2316                                                 return (EINTR);
2317                                         }
2318                                 }
2319                         }
2320                         mutex_exit(&pm_clone_lock);
2321 
2322                         physlen = pscep->psce_out->size;
2323                         physpath = NULL;
2324                         /*
2325                          * If we were unable to store the path while bringing
2326                          * up the console fb upon entering the prom, we give
2327                          * a "" name with the overrun event set
2328                          */
2329                         if (physlen == (size_t)-1) {    /* kmemalloc failed */
2330                                 physpath = kmem_zalloc(1, KM_SLEEP);
2331                                 physlen = 1;
2332                         }
2333                         if ((psc.physpath == NULL) || (psc.size < physlen)) {
2334                                 PMD(PMD_ERROR, ("ioctl: %s: EFAULT\n", cmdstr))
2335                                 mutex_exit(&pscep->psce_lock);
2336                                 ret = EFAULT;
2337                                 break;
2338                         }
2339                         if (physpath == NULL) {
2340                                 physpath = kmem_zalloc(physlen, KM_SLEEP);
2341                                 bcopy((const void *) pscep->psce_out->physpath,
2342                                     (void *) physpath, physlen);
2343                         }
2344 
2345                         p = pscep->psce_out;
2346 #ifdef  _MULTI_DATAMODEL
2347                         if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) {
2348 #ifdef DEBUG
2349                                 size_t usrcopysize;
2350 #endif
2351                                 psc32.flags = (ushort_t)p->flags;
2352                                 psc32.event = (ushort_t)p->event;
2353                                 psc32.timestamp = (int32_t)p->timestamp;
2354                                 psc32.component = (int32_t)p->component;
2355                                 psc32.old_level = (int32_t)p->old_level;
2356                                 psc32.new_level = (int32_t)p->new_level;
2357                                 copysize32 = ((intptr_t)&psc32.size -
2358                                     (intptr_t)&psc32.component);
2359 #ifdef DEBUG
2360                                 usrcopysize = ((intptr_t)&pscp32->size -
2361                                     (intptr_t)&pscp32->component);
2362                                 ASSERT(usrcopysize == copysize32);
2363 #endif
2364                         } else
2365 #endif /* _MULTI_DATAMODEL */
2366                         {
2367                                 psc.flags = p->flags;
2368                                 psc.event = p->event;
2369                                 psc.timestamp = p->timestamp;
2370                                 psc.component = p->component;
2371                                 psc.old_level = p->old_level;
2372                                 psc.new_level = p->new_level;
2373                                 copysize = ((long)&p->size -
2374                                     (long)&p->component);
2375                         }
2376                         if (p->size != (size_t)-1)
2377                                 kmem_free(p->physpath, p->size);
2378                         p->size = 0;
2379                         p->physpath = NULL;
2380                         if (pscep->psce_out == pscep->psce_last)
2381                                 p = pscep->psce_first;
2382                         else
2383                                 p++;
2384                         pscep->psce_out = p;
2385                         mutex_exit(&pscep->psce_lock);
2386 
2387                         ret = copyoutstr(physpath, psc.physpath,
2388                             physlen, &lencopied);
2389                         kmem_free(physpath, physlen);
2390                         if (ret) {
2391                                 PMD(PMD_ERROR, ("ioctl: %s: copyoutstr %p "
2392                                     "failed--EFAULT\n", cmdstr,
2393                                     (void *)psc.physpath))
2394                                 break;
2395                         }
2396 
2397 #ifdef  _MULTI_DATAMODEL
2398                         if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) {
2399                                 if (ddi_copyout(&psc32.component,
2400                                     &pscp32->component, copysize32, mode)
2401                                     != 0) {
2402                                         PMD(PMD_ERROR, ("ioctl: %s: copyout "
2403                                             "failed--EFAULT\n", cmdstr))
2404                                         ret = EFAULT;
2405                                         break;
2406                                 }
2407                         } else
2408 #endif  /* _MULTI_DATAMODEL */
2409                         {
2410                                 if (ddi_copyout(&psc.component,
2411                                     &pscp->component, copysize, mode) != 0) {
2412                                         PMD(PMD_ERROR, ("ioctl: %s: copyout "
2413                                             "failed--EFAULT\n", cmdstr))
2414                                         ret = EFAULT;
2415                                         break;
2416                                 }
2417                         }
2418                         ret = 0;
2419                         break;
2420                 }
2421 
2422                 case PM_DIRECT_NOTIFY:
2423                 case PM_DIRECT_NOTIFY_WAIT:
2424                 {
2425                         psce_t                  *pscep;
2426                         pm_state_change_t       *p;
2427                         caddr_t                 physpath;
2428                         size_t                  physlen;
2429                         /*
2430                          * We want to know if any direct device of ours has
2431                          * something we should know about.  We look up by clone.
2432                          * In case we have another thread from the same process,
2433                          * we loop.
2434                          * pm_psc_clone_to_direct() returns a locked entry.
2435                          */
2436                         mutex_enter(&pm_clone_lock);
2437                         while (pm_poll_cnt[clone] == 0 ||
2438                             (pscep = pm_psc_clone_to_direct(clone)) == NULL) {
2439                                 if (cmd == PM_DIRECT_NOTIFY) {
2440                                         PMD(PMD_IOCTL, ("ioctl: %s: "
2441                                             "EWOULDBLOCK\n", cmdstr))
2442                                         mutex_exit(&pm_clone_lock);
2443                                         ASSERT(!dipheld);
2444                                         return (EWOULDBLOCK);
2445                                 } else {
2446                                         if (cv_wait_sig(&pm_clones_cv[clone],
2447                                             &pm_clone_lock) == 0) {
2448                                                 mutex_exit(&pm_clone_lock);
2449                                                 PMD(PMD_ERROR, ("ioctl: %s: "
2450                                                     "EINTR\n", cmdstr))
2451                                                 ASSERT(!dipheld);
2452                                                 return (EINTR);
2453                                         }
2454                                 }
2455                         }
2456                         mutex_exit(&pm_clone_lock);
2457                         physlen = pscep->psce_out->size;
2458                         if ((psc.physpath == NULL) || (psc.size < physlen)) {
2459                                 mutex_exit(&pscep->psce_lock);
2460                                 PMD(PMD_ERROR, ("ioctl: %s: EFAULT\n",
2461                                     cmdstr))
2462                                 ret = EFAULT;
2463                                 break;
2464                         }
2465                         physpath = kmem_zalloc(physlen, KM_SLEEP);
2466                         bcopy((const void *) pscep->psce_out->physpath,
2467                             (void *) physpath, physlen);
2468 
2469                         p = pscep->psce_out;
2470 #ifdef  _MULTI_DATAMODEL
2471                         if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) {
2472 #ifdef DEBUG
2473                                 size_t usrcopysize;
2474 #endif
2475                                 psc32.component = (int32_t)p->component;
2476                                 psc32.flags = (ushort_t)p->flags;
2477                                 psc32.event = (ushort_t)p->event;
2478                                 psc32.timestamp = (int32_t)p->timestamp;
2479                                 psc32.old_level = (int32_t)p->old_level;
2480                                 psc32.new_level = (int32_t)p->new_level;
2481                                 copysize32 = (intptr_t)&psc32.size -
2482                                     (intptr_t)&psc32.component;
2483                                 PMD(PMD_DPM, ("ioctl: %s: PDN32 %s, comp %d "
2484                                     "%d -> %d\n", cmdstr, physpath,
2485                                     p->component, p->old_level, p->new_level))
2486 #ifdef DEBUG
2487                                 usrcopysize = (intptr_t)&pscp32->size -
2488                                     (intptr_t)&pscp32->component;
2489                                 ASSERT(usrcopysize == copysize32);
2490 #endif
2491                         } else
2492 #endif
2493                         {
2494                                 psc.component = p->component;
2495                                 psc.flags = p->flags;
2496                                 psc.event = p->event;
2497                                 psc.timestamp = p->timestamp;
2498                                 psc.old_level = p->old_level;
2499                                 psc.new_level = p->new_level;
2500                                 copysize = (intptr_t)&p->size -
2501                                     (intptr_t)&p->component;
2502                                 PMD(PMD_DPM, ("ioctl: %s: PDN %s, comp %d "
2503                                     "%d -> %d\n", cmdstr, physpath,
2504                                     p->component, p->old_level, p->new_level))
2505                         }
2506                         mutex_enter(&pm_clone_lock);
2507                         PMD(PMD_IOCTL, ("ioctl: %s: pm_poll_cnt[%d] is %d "
2508                             "before decrement\n", cmdstr, clone,
2509                             pm_poll_cnt[clone]))
2510                         pm_poll_cnt[clone]--;
2511                         mutex_exit(&pm_clone_lock);
2512                         kmem_free(p->physpath, p->size);
2513                         p->size = 0;
2514                         p->physpath = NULL;
2515                         if (pscep->psce_out == pscep->psce_last)
2516                                 p = pscep->psce_first;
2517                         else
2518                                 p++;
2519                         pscep->psce_out = p;
2520                         mutex_exit(&pscep->psce_lock);
2521 
2522                         ret = copyoutstr(physpath, psc.physpath,
2523                             physlen, &lencopied);
2524                         kmem_free(physpath, physlen);
2525                         if (ret) {
2526                                 PMD(PMD_ERROR, ("ioctl: %s: copyoutstr %p "
2527                                     "failed--EFAULT\n", cmdstr,
2528                                     (void *)psc.physpath))
2529                                 break;
2530                         }
2531 
2532 #ifdef  _MULTI_DATAMODEL
2533                         if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) {
2534                                 if (ddi_copyout(&psc32.component,
2535                                     &pscp32->component, copysize32, mode)
2536                                     != 0) {
2537                                         PMD(PMD_ERROR, ("ioctl: %s: copyout "
2538                                             "failed--EFAULT\n", cmdstr))
2539                                         ret = EFAULT;
2540                                         break;
2541                                 }
2542                         } else
2543 #endif  /* _MULTI_DATAMODEL */
2544                         {
2545                                 if (ddi_copyout(&psc.component,
2546                                     &pscp->component, copysize, mode) != 0) {
2547                                         PMD(PMD_ERROR, ("ioctl: %s: copyout "
2548                                             "failed--EFAULT\n", cmdstr))
2549                                         ret = EFAULT;
2550                                         break;
2551                                 }
2552                         }
2553                         ret = 0;
2554                         break;
2555                 }
2556                 default:
2557                         /*
2558                          * Internal error, invalid ioctl description
2559                          * force debug entry even if pm_debug not set
2560                          */
2561 #ifdef  DEBUG
2562                         pm_log("invalid diptype %d for cmd %d (%s)\n",
2563                             pcip->diptype, cmd, pcip->name);
2564 #endif
2565                         ASSERT(0);
2566                         return (EIO);
2567                 }
2568                 break;
2569         }
2570 
2571         case PM_SRCH:           /* command that takes a pm_searchargs_t arg */
2572         {
2573                 /*
2574                  * If no ppm, then there is nothing to search.
2575                  */
2576                 if (DEVI(ddi_root_node())->devi_pm_ppm == NULL) {
2577                         ret = ENODEV;
2578                         break;
2579                 }
2580 
2581 #ifdef  _MULTI_DATAMODEL
2582                 if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) {
2583                         if (ddi_copyin((caddr_t)arg, &psa32,
2584                             sizeof (psa32), mode) != 0) {
2585                                 PMD(PMD_ERROR, ("ioctl: %s: ddi_copyin "
2586                                     "EFAULT\n\n", cmdstr))
2587                                 return (EFAULT);
2588                         }
2589                         if (copyinstr((void *)(uintptr_t)psa32.pms_listname,
2590                             listname, MAXCOPYBUF, NULL)) {
2591                                 PMD(PMD_ERROR, ("ioctl: %s: 0x%p MAXCOPYBUF "
2592                                     "%d, " "EFAULT\n", cmdstr,
2593                                     (void *)(uintptr_t)psa32.pms_listname,
2594                                     MAXCOPYBUF))
2595                                 ret = EFAULT;
2596                                 break;
2597                         }
2598                         if (copyinstr((void *)(uintptr_t)psa32.pms_manufacturer,
2599                             manufacturer, MAXCOPYBUF, NULL)) {
2600                                 PMD(PMD_ERROR, ("ioctl: %s: 0x%p MAXCOPYBUF "
2601                                     "%d, " "EFAULT\n", cmdstr,
2602                                     (void *)(uintptr_t)psa32.pms_manufacturer,
2603                                     MAXCOPYBUF))
2604                                 ret = EFAULT;
2605                                 break;
2606                         }
2607                         if (copyinstr((void *)(uintptr_t)psa32.pms_product,
2608                             product, MAXCOPYBUF, NULL)) {
2609                                 PMD(PMD_ERROR, ("ioctl: %s: 0x%p MAXCOPYBUF "
2610                                     "%d, " "EFAULT\n", cmdstr,
2611                                     (void *)(uintptr_t)psa32.pms_product,
2612                                     MAXCOPYBUF))
2613                                 ret = EFAULT;
2614                                 break;
2615                         }
2616                 } else
2617 #endif /* _MULTI_DATAMODEL */
2618                 {
2619                         if (ddi_copyin((caddr_t)arg, &psa,
2620                             sizeof (psa), mode) != 0) {
2621                                 PMD(PMD_ERROR, ("ioctl: %s: ddi_copyin "
2622                                     "EFAULT\n\n", cmdstr))
2623                                 return (EFAULT);
2624                         }
2625                         if (copyinstr(psa.pms_listname,
2626                             listname, MAXCOPYBUF, NULL)) {
2627                                 PMD(PMD_ERROR, ("ioctl: %s: 0x%p MAXCOPYBUF "
2628                                     "%d, " "EFAULT\n", cmdstr,
2629                                     (void *)psa.pms_listname, MAXCOPYBUF))
2630                                 ret = EFAULT;
2631                                 break;
2632                         }
2633                         if (copyinstr(psa.pms_manufacturer,
2634                             manufacturer, MAXCOPYBUF, NULL)) {
2635                                 PMD(PMD_ERROR, ("ioctl: %s: 0x%p MAXCOPYBUF "
2636                                     "%d, " "EFAULT\n", cmdstr,
2637                                     (void *)psa.pms_manufacturer, MAXCOPYBUF))
2638                                 ret = EFAULT;
2639                                 break;
2640                         }
2641                         if (copyinstr(psa.pms_product,
2642                             product, MAXCOPYBUF, NULL)) {
2643                                 PMD(PMD_ERROR, ("ioctl: %s: 0x%p MAXCOPYBUF "
2644                                     "%d, " "EFAULT\n", cmdstr,
2645                                     (void *)psa.pms_product, MAXCOPYBUF))
2646                                 ret = EFAULT;
2647                                 break;
2648                         }
2649                 }
2650                 psa.pms_listname = listname;
2651                 psa.pms_manufacturer = manufacturer;
2652                 psa.pms_product = product;
2653                 switch (cmd) {
2654                 case PM_SEARCH_LIST:
2655                         ret = pm_ppm_searchlist(&psa);
2656                         break;
2657 
2658                 default:
2659                         /*
2660                          * Internal error, invalid ioctl description
2661                          * force debug entry even if pm_debug not set
2662                          */
2663 #ifdef  DEBUG
2664                         pm_log("invalid diptype %d for cmd %d (%s)\n",
2665                             pcip->diptype, cmd, pcip->name);
2666 #endif
2667                         ASSERT(0);
2668                         return (EIO);
2669                 }
2670                 break;
2671         }
2672 
2673         case NOSTRUCT:
2674         {
2675                 switch (cmd) {
2676                 case PM_START_PM:
2677                 case PM_START_CPUPM:
2678                 case PM_START_CPUPM_EV:
2679                 case PM_START_CPUPM_POLL:
2680                 {
2681                         pm_cpupm_t      new_mode = PM_CPUPM_NOTSET;
2682                         pm_cpupm_t      old_mode = PM_CPUPM_NOTSET;
2683                         int             r;
2684 
2685                         mutex_enter(&pm_scan_lock);
2686                         if ((cmd == PM_START_PM && autopm_enabled) ||
2687                             (cmd == PM_START_CPUPM && PM_DEFAULT_CPUPM) ||
2688                             (cmd == PM_START_CPUPM_EV && PM_EVENT_CPUPM) ||
2689                             (cmd == PM_START_CPUPM_POLL && PM_POLLING_CPUPM)) {
2690                                 mutex_exit(&pm_scan_lock);
2691                                 PMD(PMD_ERROR, ("ioctl: %s: EBUSY\n", cmdstr))
2692                                 ret = EBUSY;
2693                                 break;
2694                         }
2695 
2696                         if (cmd == PM_START_PM) {
2697                                 autopm_enabled = 1;
2698                         } else if (cmd == PM_START_CPUPM) {
2699                                 old_mode = cpupm;
2700                                 new_mode = cpupm = cpupm_default_mode;
2701                         } else if (cmd == PM_START_CPUPM_EV) {
2702                                 old_mode = cpupm;
2703                                 new_mode = cpupm = PM_CPUPM_EVENT;
2704                         } else if (cmd == PM_START_CPUPM_POLL) {
2705                                 old_mode = cpupm;
2706                                 new_mode = cpupm = PM_CPUPM_POLLING;
2707                         }
2708 
2709                         mutex_exit(&pm_scan_lock);
2710 
2711                         /*
2712                          * If we are changing CPUPM modes, and it is active,
2713                          * then stop it from operating in the old mode.
2714                          */
2715                         if (old_mode == PM_CPUPM_POLLING) {
2716                                 int c = PM_STOP_CPUPM;
2717                                 ddi_walk_devs(ddi_root_node(), pm_stop_pm_walk,
2718                                     &c);
2719                         } else if (old_mode == PM_CPUPM_EVENT) {
2720                                 r = cpupm_set_policy(CPUPM_POLICY_DISABLED);
2721 
2722                                 /*
2723                                  * Disabling CPUPM policy should always
2724                                  * succeed
2725                                  */
2726                                 ASSERT(r == 0);
2727                         }
2728 
2729                         /*
2730                          * If we are changing to event based CPUPM, enable it.
2731                          * In the event it's not supported, fall back to
2732                          * polling based CPUPM.
2733                          */
2734                         if (new_mode == PM_CPUPM_EVENT &&
2735                             cpupm_set_policy(CPUPM_POLICY_ELASTIC) < 0) {
2736                                 mutex_enter(&pm_scan_lock);
2737                                 new_mode = cpupm = PM_CPUPM_POLLING;
2738                                 cmd = PM_START_CPUPM_POLL;
2739                                 mutex_exit(&pm_scan_lock);
2740                         }
2741                         if (new_mode == PM_CPUPM_POLLING ||
2742                             cmd == PM_START_PM) {
2743                                 ddi_walk_devs(ddi_root_node(), pm_start_pm_walk,
2744                                     &cmd);
2745                         }
2746                         ret = 0;
2747                         break;
2748                 }
2749 
2750                 case PM_RESET_PM:
2751                 case PM_STOP_PM:
2752                 case PM_STOP_CPUPM:
2753                 {
2754                         extern void pm_discard_thresholds(void);
2755                         pm_cpupm_t old_mode = PM_CPUPM_NOTSET;
2756 
2757                         mutex_enter(&pm_scan_lock);
2758                         if ((cmd == PM_STOP_PM && !autopm_enabled) ||
2759                             (cmd == PM_STOP_CPUPM && PM_CPUPM_DISABLED)) {
2760                                 mutex_exit(&pm_scan_lock);
2761                                 PMD(PMD_ERROR, ("ioctl: %s: EINVAL\n",
2762                                     cmdstr))
2763                                 ret = EINVAL;
2764                                 break;
2765                         }
2766 
2767                         if (cmd == PM_STOP_PM) {
2768                                 autopm_enabled = 0;
2769                                 pm_S3_enabled = 0;
2770                                 autoS3_enabled = 0;
2771                         } else if (cmd == PM_STOP_CPUPM) {
2772                                 old_mode = cpupm;
2773                                 cpupm = PM_CPUPM_DISABLE;
2774                         } else {
2775                                 autopm_enabled = 0;
2776                                 autoS3_enabled = 0;
2777                                 old_mode = cpupm;
2778                                 cpupm = PM_CPUPM_NOTSET;
2779                         }
2780                         mutex_exit(&pm_scan_lock);
2781 
2782                         /*
2783                          * bring devices to full power level, stop scan
2784                          * If CPUPM was operating in event driven mode, disable
2785                          * that.
2786                          */
2787                         if (old_mode == PM_CPUPM_EVENT) {
2788                                 (void) cpupm_set_policy(CPUPM_POLICY_DISABLED);
2789                         }
2790                         ddi_walk_devs(ddi_root_node(), pm_stop_pm_walk, &cmd);
2791                         ret = 0;
2792                         if (cmd == PM_STOP_PM || cmd == PM_STOP_CPUPM)
2793                                 break;
2794                         /*
2795                          * Now do only PM_RESET_PM stuff.
2796                          */
2797                         pm_system_idle_threshold = pm_default_idle_threshold;
2798                         pm_cpu_idle_threshold = 0;
2799                         pm_discard_thresholds();
2800                         pm_all_to_default_thresholds();
2801                         pm_dispatch_to_dep_thread(PM_DEP_WK_REMOVE_DEP,
2802                             NULL, NULL, PM_DEP_WAIT, NULL, 0);
2803                         break;
2804                 }
2805 
2806                 case PM_GET_SYSTEM_THRESHOLD:
2807                 {
2808                         *rval_p = pm_system_idle_threshold;
2809                         ret = 0;
2810                         break;
2811                 }
2812 
2813                 case PM_GET_DEFAULT_SYSTEM_THRESHOLD:
2814                 {
2815                         *rval_p = pm_default_idle_threshold;
2816                         ret = 0;
2817                         break;
2818                 }
2819 
2820                 case PM_GET_CPU_THRESHOLD:
2821                 {
2822                         *rval_p = pm_cpu_idle_threshold;
2823                         ret = 0;
2824                         break;
2825                 }
2826 
2827                 case PM_SET_SYSTEM_THRESHOLD:
2828                 case PM_SET_CPU_THRESHOLD:
2829                 {
2830                         if ((int)arg < 0) {
2831                                 PMD(PMD_ERROR, ("ioctl: %s: arg 0x%x < 0"
2832                                     "--EINVAL\n", cmdstr, (int)arg))
2833                                 ret = EINVAL;
2834                                 break;
2835                         }
2836                         PMD(PMD_IOCTL, ("ioctl: %s: 0x%x 0t%d\n", cmdstr,
2837                             (int)arg, (int)arg))
2838                         if (cmd == PM_SET_SYSTEM_THRESHOLD)
2839                                 pm_system_idle_threshold = (int)arg;
2840                         else {
2841                                 pm_cpu_idle_threshold = (int)arg;
2842                         }
2843                         ddi_walk_devs(ddi_root_node(), pm_set_idle_thresh_walk,
2844                             (void *) &cmd);
2845 
2846                         ret = 0;
2847                         break;
2848                 }
2849 
2850                 case PM_IDLE_DOWN:
2851                 {
2852                         if (pm_timeout_idledown() != 0) {
2853                                 ddi_walk_devs(ddi_root_node(),
2854                                     pm_start_idledown, (void *)PMID_IOC);
2855                         }
2856                         ret = 0;
2857                         break;
2858                 }
2859 
2860                 case PM_GET_PM_STATE:
2861                 {
2862                         if (autopm_enabled) {
2863                                 *rval_p = PM_SYSTEM_PM_ENABLED;
2864                         } else {
2865                                 *rval_p = PM_SYSTEM_PM_DISABLED;
2866                         }
2867                         ret = 0;
2868                         break;
2869                 }
2870 
2871                 case PM_GET_CPUPM_STATE:
2872                 {
2873                         if (PM_POLLING_CPUPM || PM_EVENT_CPUPM)
2874                                 *rval_p = PM_CPU_PM_ENABLED;
2875                         else if (PM_CPUPM_DISABLED)
2876                                 *rval_p = PM_CPU_PM_DISABLED;
2877                         else
2878                                 *rval_p = PM_CPU_PM_NOTSET;
2879                         ret = 0;
2880                         break;
2881                 }
2882 
2883                 case PM_GET_AUTOS3_STATE:
2884                 {
2885                         if (autoS3_enabled) {
2886                                 *rval_p = PM_AUTOS3_ENABLED;
2887                         } else {
2888                                 *rval_p = PM_AUTOS3_DISABLED;
2889                         }
2890                         ret = 0;
2891                         break;
2892                 }
2893 
2894                 case PM_GET_S3_SUPPORT_STATE:
2895                 {
2896                         if (pm_S3_enabled) {
2897                                 *rval_p = PM_S3_SUPPORT_ENABLED;
2898                         } else {
2899                                 *rval_p = PM_S3_SUPPORT_DISABLED;
2900                         }
2901                         ret = 0;
2902                         break;
2903                 }
2904 
2905                 /*
2906                  * pmconfig tells us if the platform supports S3
2907                  */
2908                 case PM_ENABLE_S3:
2909                 {
2910                         mutex_enter(&pm_scan_lock);
2911                         if (pm_S3_enabled) {
2912                                 mutex_exit(&pm_scan_lock);
2913                                 PMD(PMD_ERROR, ("ioctl: %s: EBUSY\n",
2914                                     cmdstr))
2915                                 ret = EBUSY;
2916                                 break;
2917                         }
2918                         pm_S3_enabled = 1;
2919                         mutex_exit(&pm_scan_lock);
2920                         ret = 0;
2921                         break;
2922                 }
2923 
2924                 case PM_DISABLE_S3:
2925                 {
2926                         mutex_enter(&pm_scan_lock);
2927                         pm_S3_enabled = 0;
2928                         mutex_exit(&pm_scan_lock);
2929                         ret = 0;
2930                         break;
2931                 }
2932 
2933                 case PM_START_AUTOS3:
2934                 {
2935                         mutex_enter(&pm_scan_lock);
2936                         if (autoS3_enabled) {
2937                                 mutex_exit(&pm_scan_lock);
2938                                 PMD(PMD_ERROR, ("ioctl: %s: EBUSY\n",
2939                                     cmdstr))
2940                                 ret = EBUSY;
2941                                 break;
2942                         }
2943                         autoS3_enabled = 1;
2944                         mutex_exit(&pm_scan_lock);
2945                         ret = 0;
2946                         break;
2947                 }
2948 
2949                 case PM_STOP_AUTOS3:
2950                 {
2951                         mutex_enter(&pm_scan_lock);
2952                         autoS3_enabled = 0;
2953                         mutex_exit(&pm_scan_lock);
2954                         ret = 0;
2955                         break;
2956                 }
2957 
2958                 case PM_ENABLE_CPU_DEEP_IDLE:
2959                 {
2960                         if (callb_execute_class(CB_CL_CPU_DEEP_IDLE,
2961                             PM_ENABLE_CPU_DEEP_IDLE) == NULL)
2962                                 ret = 0;
2963                         else
2964                                 ret = EBUSY;
2965                         break;
2966                 }
2967                 case PM_DISABLE_CPU_DEEP_IDLE:
2968                 {
2969                         if (callb_execute_class(CB_CL_CPU_DEEP_IDLE,
2970                             PM_DISABLE_CPU_DEEP_IDLE) == NULL)
2971                                 ret = 0;
2972                         else
2973                                 ret = EINVAL;
2974                         break;
2975                 }
2976                 case PM_DEFAULT_CPU_DEEP_IDLE:
2977                 {
2978                         if (callb_execute_class(CB_CL_CPU_DEEP_IDLE,
2979                             PM_DEFAULT_CPU_DEEP_IDLE) == NULL)
2980                                 ret = 0;
2981                         else
2982                                 ret = EBUSY;
2983                         break;
2984                 }
2985 
2986                 default:
2987                         /*
2988                          * Internal error, invalid ioctl description
2989                          * force debug entry even if pm_debug not set
2990                          */
2991 #ifdef  DEBUG
2992                         pm_log("invalid diptype %d for cmd %d (%s)\n",
2993                             pcip->diptype, cmd, pcip->name);
2994 #endif
2995                         ASSERT(0);
2996                         return (EIO);
2997                 }
2998                 break;
2999         }
3000 
3001 default:
3002                 /*
3003                  * Internal error, invalid ioctl description
3004                  * force debug entry even if pm_debug not set
3005                  */
3006 #ifdef  DEBUG
3007                 pm_log("ioctl: invalid str_type %d for cmd %d (%s)\n",
3008                     pcip->str_type, cmd, pcip->name);
3009 #endif
3010                 ASSERT(0);
3011                 return (EIO);
3012         }
3013         ASSERT(ret != 0x0badcafe);      /* some cmd in wrong case! */
3014         if (dipheld) {
3015                 ASSERT(dip);
3016                 PMD(PMD_DHR, ("ioctl: %s: releasing %s@%s(%s#%d) for "
3017                     "exiting pm_ioctl\n", cmdstr, PM_DEVICE(dip)))
3018                 PM_RELE(dip);
3019         }
3020         PMD(PMD_IOCTL, ("ioctl: %s: end, ret=%d\n", cmdstr, ret))
3021         return (ret);
3022 }