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  * Copyright (c) 2009,  Intel Corporation.
  27  * All Rights Reserved.
  28  */
  29 
  30 
  31 /*
  32  * Platform Power Management master pseudo driver -
  33  *    - attaches only  when ppm.conf file is present, indicating a
  34  *      workstation (since Excalibur era ) that is designed to
  35  *      be MOU-3 EPA compliant and which uses platform-specific
  36  *      hardware to do so;
  37  *    - this pseudo driver uses a set of simple satellite
  38  *      device drivers responsible for accessing platform
  39  *      specific devices to modify the registers they own.
  40  *      ppm drivers tells these satellite drivers what to do
  41  *      according to using command values taken from ppm.conf.
  42  */
  43 #include <sys/conf.h>
  44 #include <sys/stat.h>
  45 #include <sys/file.h>
  46 #include <sys/types.h>
  47 #include <sys/param.h>
  48 #include <sys/open.h>
  49 #include <sys/callb.h>
  50 #include <sys/va_list.h>
  51 #include <sys/errno.h>
  52 #include <sys/modctl.h>
  53 #include <sys/sysmacros.h>
  54 #include <sys/ddi_impldefs.h>
  55 #include <sys/promif.h>
  56 #include <sys/epm.h>
  57 #include <sys/sunpm.h>
  58 #include <sys/ppmio.h>
  59 #include <sys/sunldi.h>
  60 #include <sys/ppmvar.h>
  61 #include <sys/ddi.h>
  62 #include <sys/sunddi.h>
  63 #include <sys/ppm_plat.h>
  64 
  65 /*
  66  * Note: When pm_power() is called (directly or indirectly) to change the
  67  * power level of a device and the call returns failure, DO NOT assume the
  68  * level is unchanged.  Doublecheck it against ppmd->level.
  69  */
  70 
  71 /*
  72  * cb_ops
  73  */
  74 static int      ppm_open(dev_t *, int, int, cred_t *);
  75 static int      ppm_close(dev_t, int, int, cred_t *);
  76 static int      ppm_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
  77 
  78 static struct cb_ops ppm_cb_ops = {
  79         ppm_open,               /* open */
  80         ppm_close,              /* close */
  81         nodev,                  /* strategy */
  82         nodev,                  /* print */
  83         nodev,                  /* dump */
  84         nodev,                  /* read */
  85         nodev,                  /* write */
  86         ppm_ioctl,              /* ioctl */
  87         nodev,                  /* devmap */
  88         nodev,                  /* mmap */
  89         nodev,                  /* segmap */
  90         nochpoll,               /* poll */
  91         ddi_prop_op,            /* prop_op */
  92         NULL,                   /* streamtab */
  93         D_MP | D_NEW,           /* driver compatibility flag */
  94         CB_REV,                 /* cb_ops revision */
  95         nodev,                  /* async read */
  96         nodev                   /* async write */
  97 };
  98 
  99 /*
 100  * bus_ops
 101  */
 102 static int      ppm_ctlops(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *,
 103     void *);
 104 
 105 static struct bus_ops ppm_bus_ops = {
 106         BUSO_REV,               /* busops_rev           */
 107         0,                      /* bus_map              */
 108         0,                      /* bus_get_intrspec     */
 109         0,                      /* bus_add_intrspec     */
 110         0,                      /* bus_remove_intrspec  */
 111         0,                      /* bus_map_fault        */
 112         ddi_no_dma_map,         /* bus_dma_map          */
 113         ddi_no_dma_allochdl,    /* bus_dma_allochdl     */
 114         NULL,                   /* bus_dma_freehdl      */
 115         NULL,                   /* bus_dma_bindhdl      */
 116         NULL,                   /* bus_dma_unbindhdl    */
 117         NULL,                   /* bus_dma_flush        */
 118         NULL,                   /* bus_dma_win          */
 119         NULL,                   /* bus_dma_ctl          */
 120         ppm_ctlops,             /* bus_ctl              */
 121         0,                      /* bus_prop_op          */
 122         0,                      /* bus_get_eventcookie  */
 123         0,                      /* bus_add_eventcall    */
 124         0,                      /* bus_remove_eventcall */
 125         0,                      /* bus_post_event       */
 126         0                       /* bus_intr_ctl         */
 127 };
 128 
 129 /*
 130  * dev_ops
 131  */
 132 static int      ppm_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
 133 static int      ppm_attach(dev_info_t *, ddi_attach_cmd_t);
 134 static int      ppm_detach(dev_info_t *, ddi_detach_cmd_t);
 135 
 136 static struct dev_ops ppm_ops = {
 137         DEVO_REV,               /* devo_rev */
 138         0,                      /* refcnt */
 139         ppm_getinfo,            /* info */
 140         nulldev,                /* identify */
 141         nulldev,                /* probe */
 142         ppm_attach,             /* attach */
 143         ppm_detach,             /* detach */
 144         nodev,                  /* reset */
 145         &ppm_cb_ops,                /* cb_ops */
 146         &ppm_bus_ops,               /* bus_ops */
 147         nulldev,                /* power */
 148         ddi_quiesce_not_needed,         /* quiesce */
 149 };
 150 
 151 extern struct mod_ops mod_driverops;
 152 
 153 static struct modldrv modldrv = {
 154         &mod_driverops,
 155         "platform pm driver",
 156         &ppm_ops
 157 };
 158 
 159 static struct modlinkage modlinkage = {
 160         MODREV_1,
 161         { &modldrv, NULL }
 162 };
 163 
 164 /*
 165  * Global data structure and variables
 166  */
 167 int     ppm_inst = -1;
 168 void    *ppm_statep;
 169 ppm_domain_t *ppm_domain_p;
 170 callb_id_t   *ppm_cprcb_id;
 171 static kmutex_t ppm_cpr_window_lock;    /* guard ppm_cpr_window_flag */
 172 static  boolean_t ppm_cpr_window_flag;  /* set indicating chpt-resume period */
 173 
 174 /* LED actions */
 175 #define PPM_LED_SOLIDON         0
 176 #define PPM_LED_BLINKING        1
 177 
 178 /*
 179  * Debug
 180  */
 181 #ifdef  DEBUG
 182 uint_t  ppm_debug = 0;
 183 #endif
 184 
 185 /*
 186  * Local function prototypes and data
 187  */
 188 static boolean_t        ppm_cpr_callb(void *, int);
 189 static int              ppm_fetset(ppm_domain_t *, uint8_t);
 190 static int              ppm_fetget(ppm_domain_t *, uint8_t *);
 191 static int              ppm_gpioset(ppm_domain_t *, int);
 192 static int              ppm_manage_cpus(dev_info_t *, power_req_t *, int *);
 193 static int              ppm_manage_pci(dev_info_t *, power_req_t *, int *);
 194 static int              ppm_manage_pcie(dev_info_t *, power_req_t *, int *);
 195 static int              ppm_manage_fet(dev_info_t *, power_req_t *, int *);
 196 static void             ppm_manage_led(int);
 197 static void             ppm_set_led(ppm_domain_t *, int);
 198 static void             ppm_blink_led(void *);
 199 static void             ppm_svc_resume_ctlop(dev_info_t *, power_req_t *);
 200 static int              ppm_set_level(ppm_dev_t *, int, int, boolean_t);
 201 static int              ppm_change_power_level(ppm_dev_t *, int, int);
 202 static int              ppm_record_level_change(ppm_dev_t *, int, int);
 203 static int              ppm_switch_clock(ppm_domain_t *, int);
 204 static int              ppm_pcie_pwr(ppm_domain_t *, int);
 205 static int              ppm_power_up_domain(dev_info_t *dip);
 206 static int              ppm_power_down_domain(dev_info_t *dip);
 207 
 208 int
 209 _init(void)
 210 {
 211         if (ddi_soft_state_init(
 212             &ppm_statep, sizeof (ppm_unit_t), 1) != DDI_SUCCESS) {
 213                 PPMD(D_INIT, ("ppm: soft state init\n"))
 214                 return (DDI_FAILURE);
 215         }
 216 
 217         if (mod_install(&modlinkage) != DDI_SUCCESS) {
 218                 ddi_soft_state_fini(&ppm_statep);
 219                 return (DDI_FAILURE);
 220         }
 221         return (DDI_SUCCESS);
 222 }
 223 
 224 
 225 int
 226 _fini(void)
 227 {
 228         int error;
 229 
 230         if ((error = mod_remove(&modlinkage)) == DDI_SUCCESS)
 231                 ddi_soft_state_fini(&ppm_statep);
 232 
 233         return (error);
 234 }
 235 
 236 
 237 int
 238 _info(struct modinfo *modinfop)
 239 {
 240         return (mod_info(&modlinkage, modinfop));
 241 }
 242 
 243 
 244 /* ARGSUSED */
 245 int
 246 ppm_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp)
 247 {
 248         struct ppm_unit *unitp;
 249         dev_t   dev;
 250         int     instance;
 251         int     rval;
 252 
 253         if (ppm_inst == -1)
 254                 return (DDI_FAILURE);
 255 
 256         switch (cmd) {
 257         case DDI_INFO_DEVT2DEVINFO:
 258                 if (unitp = ddi_get_soft_state(ppm_statep, (dev_t)arg)) {
 259                         *resultp = unitp->dip;
 260                         rval = DDI_SUCCESS;
 261                 } else
 262                         rval = DDI_FAILURE;
 263 
 264                 return (rval);
 265 
 266         case DDI_INFO_DEVT2INSTANCE:
 267                 dev = (dev_t)arg;
 268                 instance = getminor(dev);
 269                 *resultp = (void *)(uintptr_t)instance;
 270                 return (DDI_SUCCESS);
 271 
 272         default:
 273                 return (DDI_FAILURE);
 274         }
 275 }
 276 
 277 
 278 /*
 279  * attach(9E)
 280  */
 281 static int
 282 ppm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
 283 {
 284         ppm_unit_t *unitp;
 285         int ret;
 286 #ifdef  DEBUG
 287         char *str = "ppm_attach";
 288 #endif
 289 
 290 
 291         switch (cmd) {
 292         case DDI_ATTACH:
 293                 PPMD(D_ATTACH, ("%s: attaching ...\n", str))
 294                 break;
 295 
 296         case DDI_RESUME:
 297                 PPMD(D_ATTACH, ("%s: Resuming ...\n", str))
 298                 unitp = ddi_get_soft_state(ppm_statep, ppm_inst);
 299                 mutex_enter(&unitp->lock);
 300                 unitp->states &= ~PPM_STATE_SUSPENDED;
 301                 mutex_exit(&unitp->lock);
 302                 return (DDI_SUCCESS);
 303 
 304         default:
 305                 cmn_err(CE_WARN, "ppm_attach: unknown command %d, dip(0x%p)",
 306                     cmd, (void *)dip);
 307                 return (DDI_FAILURE);
 308         }
 309 
 310         if (ppm_inst != -1) {
 311                 PPMD(D_ATTACH, ("%s: Already attached !", str))
 312                 return (DDI_FAILURE);
 313         }
 314 
 315         ppm_inst = ddi_get_instance(dip);
 316         if (ddi_soft_state_zalloc(ppm_statep, ppm_inst) != DDI_SUCCESS) {
 317                 PPMD(D_ATTACH, ("%s: soft states alloc error!\n", str))
 318                 return (DDI_FAILURE);
 319         }
 320         unitp = ddi_get_soft_state(ppm_statep, ppm_inst);
 321 
 322         ret = ddi_create_minor_node(dip, "ppm", S_IFCHR, ppm_inst,
 323             "ddi_ppm", 0);
 324         if (ret != DDI_SUCCESS) {
 325                 PPMD(D_ATTACH, ("%s: can't create minor node!\n", str))
 326                 goto fail1;
 327         }
 328 
 329         unitp->dip = dip;
 330         mutex_init(&unitp->lock, NULL, MUTEX_DRIVER, NULL);
 331 
 332         /*
 333          * read ppm.conf, construct ppm_domain data structure and
 334          * their sub data structure.
 335          */
 336         if ((ret = ppm_create_db(dip)) != DDI_SUCCESS)
 337                 goto fail2;
 338 
 339         /*
 340          * walk down ppm domain control from each domain, initialize
 341          * domain control orthogonal function call handle
 342          */
 343         ppm_init_cb(dip);
 344 
 345         if ((ret = pm_register_ppm(ppm_claim_dev, dip)) != DDI_SUCCESS) {
 346                 cmn_err(CE_WARN, "ppm_attach: can't register ppm handler!");
 347                 goto fail2;
 348         }
 349 
 350         mutex_init(&ppm_cpr_window_lock, NULL, MUTEX_DRIVER, NULL);
 351         ppm_cpr_window_flag = B_FALSE;
 352         ppm_cprcb_id = callb_add(ppm_cpr_callb, (void *)NULL,
 353             CB_CL_CPR_PM, "ppm_cpr");
 354 
 355 #if defined(__x86)
 356         /*
 357          * Register callback so that once CPUs have been added to
 358          * the device tree, ppm CPU domains can be allocated using ACPI
 359          * data.
 360          */
 361         cpupm_ppm_alloc_pstate_domains = ppm_alloc_pstate_domains;
 362         cpupm_ppm_free_pstate_domains = ppm_free_pstate_domains;
 363 
 364         /*
 365          * Register callback so that whenever max speed throttle requests
 366          * are received, ppm can redefine the high power level for
 367          * all CPUs in the domain.
 368          */
 369         cpupm_redefine_topspeed = ppm_redefine_topspeed;
 370 #endif
 371 
 372         ddi_report_dev(dip);
 373         return (DDI_SUCCESS);
 374 
 375 fail2:
 376         ddi_remove_minor_node(dip, "ddi_ppm");
 377         mutex_destroy(&unitp->lock);
 378 fail1:
 379         ddi_soft_state_free(ppm_statep, ppm_inst);
 380         ppm_inst = -1;
 381         return (DDI_FAILURE);
 382 }
 383 
 384 
 385 /* ARGSUSED */
 386 static int
 387 ppm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
 388 {
 389         ppm_unit_t *unitp;
 390 #ifdef  DEBUG
 391         char *str = "ppm_detach";
 392 #endif
 393 
 394         switch (cmd) {
 395         case DDI_DETACH:
 396                 PPMD(D_DETACH, ("%s: detach not allowed.\n", str))
 397                 return (DDI_FAILURE);
 398 
 399         case DDI_SUSPEND:
 400                 PPMD(D_DETACH, ("%s: suspending ...\n", str))
 401                 unitp = ddi_get_soft_state(ppm_statep, ppm_inst);
 402                 mutex_enter(&unitp->lock);
 403                 unitp->states |= PPM_STATE_SUSPENDED;
 404                 mutex_exit(&unitp->lock);
 405 
 406                 /*
 407                  * Suspend requires that timeout callouts to be canceled.
 408                  * Turning off the LED blinking will cancel the timeout.
 409                  */
 410                 ppm_manage_led(PPM_LED_SOLIDON);
 411                 return (DDI_SUCCESS);
 412 
 413         default:
 414                 cmn_err(CE_WARN, "ppm_detach: unsupported command %d, dip(%p)",
 415                     cmd, (void *)dip);
 416                 return (DDI_FAILURE);
 417         }
 418 }
 419 
 420 
 421 /* ARGSUSED */
 422 int
 423 ppm_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
 424 {
 425         if (otyp != OTYP_CHR)
 426                 return (EINVAL);
 427         PPMD(D_OPEN, ("ppm_open: devp 0x%p, flag 0x%x, otyp %d\n",
 428             (void *)devp, flag, otyp))
 429         return (0);
 430 }
 431 
 432 
 433 /* ARGSUSED */
 434 int
 435 ppm_close(dev_t dev, int flag, int otyp, cred_t *credp)
 436 {
 437         PPMD(D_CLOSE, ("ppm_close: dev 0x%lx, flag 0x%x, otyp %d\n",
 438             dev, flag, otyp))
 439         return (0);
 440 }
 441 
 442 
 443 /* ARGSUSED */
 444 int
 445 ppm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cred_p,
 446     int *rval_p)
 447 {
 448 #ifdef DEBUG
 449         char *str = "ppm_ioctl";
 450 #endif
 451         ppm_domain_t *domp = NULL;
 452         uint8_t level, lvl;
 453         int ret = 0;
 454 
 455         PPMD(D_IOCTL, ("%s: dev 0x%lx, cmd 0x%x, mode 0x%x\n",
 456             str, dev, cmd, mode))
 457 
 458         switch (cmd) {
 459         case PPMGET_DPWR:
 460         {
 461                 STRUCT_DECL(ppm_dpwr, dpwr);
 462                 struct ppm_unit *unitp;
 463                 char *domain;
 464 
 465                 STRUCT_INIT(dpwr, mode);
 466                 ret = ddi_copyin((caddr_t)arg, STRUCT_BUF(dpwr),
 467                     STRUCT_SIZE(dpwr), mode);
 468                 if (ret != 0)
 469                         return (EFAULT);
 470 
 471                 /* copyin domain name */
 472                 domain = kmem_zalloc(MAXNAMELEN, KM_SLEEP);
 473                 ret = copyinstr(
 474                     STRUCT_FGETP(dpwr, domain), domain, MAXNAMELEN, NULL);
 475                 if (ret != 0) {
 476                         PPMD(D_IOCTL, ("%s: can't copyin domain, line(%d)\n",
 477                             str, __LINE__))
 478                         ret = EFAULT;
 479                         goto err_dpwr;
 480                 }
 481 
 482                 /* locate domain */
 483                 if ((domp = ppm_lookup_domain(domain)) == NULL) {
 484                         PPMD(D_IOCTL, ("%s: no such domain %s\n", str, domain))
 485                         ret = ENODEV;
 486                         goto err_dpwr;
 487                 }
 488 
 489                 switch (domp->model) {
 490                 case PPMD_FET:  /* report power fet ON or OFF */
 491                         if ((ret = ppm_fetget(domp, &lvl)) != 0) {
 492                                 ret = EIO;
 493                                 goto err_dpwr;
 494                         }
 495                         level = (lvl == PPMD_ON) ?
 496                             PPMIO_POWER_ON : PPMIO_POWER_OFF;
 497                         break;
 498 
 499                 case PPMD_PCI:  /* report pci slot clock ON or OFF */
 500                 case PPMD_PCI_PROP:
 501                 case PPMD_PCIE:
 502                         level = (domp->status == PPMD_ON) ?
 503                             PPMIO_POWER_ON : PPMIO_POWER_OFF;
 504                         break;
 505 
 506                 case PPMD_LED:  /* report LED blinking or solid on */
 507 
 508                         unitp = ddi_get_soft_state(ppm_statep, ppm_inst);
 509                         if (unitp->led_tid == 0)
 510                                 level = PPMIO_LED_SOLIDON;
 511                         else
 512                                 level = PPMIO_LED_BLINKING;
 513                         break;
 514 
 515                 case PPMD_CPU:  /* report cpu speed divisor */
 516                         level = domp->devlist->level;
 517                         break;
 518 
 519                 default:
 520                         ret = EINVAL;
 521                         goto err_dpwr;
 522                 }
 523 
 524                 STRUCT_FSET(dpwr, level, level);
 525                 ret = ddi_copyout(STRUCT_BUF(dpwr), (caddr_t)arg,
 526                     STRUCT_SIZE(dpwr), mode);
 527                 if (ret != 0) {
 528                         PPMD(D_IOCTL, ("%s: can't copyout, line(%d)\n",
 529                             str, __LINE__))
 530                         ret = EFAULT;
 531                 }
 532 err_dpwr:
 533                 kmem_free(domain, MAXNAMELEN);
 534 
 535                 break;
 536         }
 537 
 538         case PPMGET_DOMBYDEV:
 539         {
 540                 STRUCT_DECL(ppm_bydev, bydev);
 541                 char *path = NULL;
 542                 size_t   size, l;
 543 
 544                 STRUCT_INIT(bydev, mode);
 545                 ret = ddi_copyin((caddr_t)arg, STRUCT_BUF(bydev),
 546                     STRUCT_SIZE(bydev), mode);
 547                 if (ret != 0)
 548                         return (EFAULT);
 549 
 550                 /* copyin .path */
 551                 path = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
 552                 ret = copyinstr(
 553                     STRUCT_FGETP(bydev, path), path, MAXPATHLEN, NULL);
 554                 if (ret != 0) {
 555                         PPMD(D_IOCTL, ("%s: can't copyin path, line(%d)\n",
 556                             str, __LINE__))
 557                         kmem_free(path, MAXPATHLEN);
 558                         return (EFAULT);
 559                 }
 560 
 561                 /* so far we have up to one domain for a given device */
 562                 size = STRUCT_FGET(bydev, size);
 563                 domp = ppm_get_domain_by_dev(path);
 564                 kmem_free(path, MAXPATHLEN);
 565                 if (domp != NULL) {
 566                         l = strlen(domp->name) + 1;
 567                         if (l > size) {
 568                                 PPMD(D_IOCTL, ("%s: buffer too small\n", str))
 569                                 return ((size == 0) ? EINVAL : EFAULT);
 570                         }
 571                 } else  /* no domain found to be associated with given device */
 572                         return (ENODEV);
 573 
 574                 ret = copyoutstr(
 575                     domp->name, STRUCT_FGETP(bydev, domlist), l, &l);
 576                 if (ret != 0) {
 577                         PPMD(D_IOCTL, ("%s: can't copyout domlist, line(%d)"
 578                             " \n", str, __LINE__))
 579                         return (EFAULT);
 580                 }
 581 
 582                 break;
 583         }
 584 
 585 
 586         case PPMGET_DEVBYDOM:
 587         {
 588                 STRUCT_DECL(ppm_bydom, bydom);
 589                 char *domain = NULL;
 590                 char *devlist = NULL;
 591                 ppm_dev_t *ppmd;
 592                 dev_info_t *odip = NULL;
 593                 char *s, *d;
 594                 size_t  size, l;
 595 
 596                 STRUCT_INIT(bydom, mode);
 597                 ret = ddi_copyin((caddr_t)arg, STRUCT_BUF(bydom),
 598                     STRUCT_SIZE(bydom), mode);
 599                 if (ret != 0)
 600                         return (EFAULT);
 601 
 602                 /* copyin .domain */
 603                 domain = kmem_zalloc(MAXNAMELEN, KM_SLEEP);
 604                 ret = copyinstr(STRUCT_FGETP(bydom, domain), domain,
 605                     MAXNAMELEN, NULL);
 606                 if (ret != 0) {
 607                         PPMD(D_IOCTL, ("%s: can't copyin domain, line(%d)\n",
 608                             str, __LINE__))
 609                         ret = EFAULT;
 610                         goto err_bydom;
 611                 }
 612 
 613                 /* locate domain */
 614                 if ((domp = ppm_lookup_domain(domain)) == NULL) {
 615                         ret = ENODEV;
 616                         goto err_bydom;
 617                 }
 618 
 619                 l = 0;
 620                 if ((size = STRUCT_FGET(bydom, size)) == 0)
 621                         ret = EINVAL;
 622                 else
 623                         if ((d = devlist = kmem_zalloc(size, KM_SLEEP)) == NULL)
 624                                 ret = EFAULT;
 625                 if (ret != 0)
 626                         goto err_bydom;
 627 
 628                 for (ppmd = domp->devlist; ppmd;
 629                     odip = ppmd->dip, ppmd = ppmd->next) {
 630 
 631                         if (ppmd->dip == odip)
 632                                 continue;
 633                         if (ppmd != domp->devlist)
 634                                 *d++ = ' ';
 635 
 636                         l += strlen(ppmd->path) + 1;
 637                         if (l > size) {
 638                                 PPMD(D_IOCTL, ("%s: buffer overflow\n", str))
 639                                 ret = EFAULT;
 640                                 goto err_bydom;
 641                         }
 642 
 643                         for (s = ppmd->path; *s != 0; )
 644                                 *d++ = *s++;
 645                 }
 646                 *d = 0;
 647 
 648                 if (*devlist == 0)
 649                         goto err_bydom;
 650 
 651                 ret = copyoutstr(
 652                     devlist, STRUCT_FGETP(bydom, devlist), l, &l);
 653                 if (ret != 0) {
 654                         PPMD(D_IOCTL, ("%s: can't copyout devlist, line(%d)"
 655                             " \n", str, __LINE__))
 656                         ret = EFAULT;
 657                 }
 658 
 659 err_bydom:
 660                 if (devlist)
 661                         kmem_free(devlist, size);
 662                 if (domain)
 663                         kmem_free(domain, MAXNAMELEN);
 664 
 665                 break;
 666         }
 667 
 668 #if defined(__x86)
 669         /*
 670          * Note that these two ioctls exist for test purposes only.
 671          * Unfortunately, there really isn't any other good way of
 672          * unit testing the dynamic redefinition of the top speed as it
 673          * usually occurs due to environmental conditions.
 674          */
 675         case PPMGET_NORMAL:
 676         case PPMSET_NORMAL:
 677         {
 678                 STRUCT_DECL(ppm_norm, norm);
 679                 char *path = NULL;
 680                 struct pm_component *dcomps;
 681                 struct pm_comp *pm_comp;
 682                 ppm_dev_t *ppmd;
 683                 int i;
 684 
 685                 STRUCT_INIT(norm, mode);
 686                 ret = ddi_copyin((caddr_t)arg, STRUCT_BUF(norm),
 687                     STRUCT_SIZE(norm), mode);
 688                 if (ret != 0)
 689                         return (EFAULT);
 690 
 691                 /* copyin .path */
 692                 path = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
 693                 ret = copyinstr(
 694                     STRUCT_FGETP(norm, path), path, MAXPATHLEN, NULL);
 695                 if (ret != 0) {
 696                         PPMD(D_IOCTL, ("%s: can't copyin path, line(%d)\n",
 697                             str, __LINE__))
 698                         kmem_free(path, MAXPATHLEN);
 699                         return (EFAULT);
 700                 }
 701 
 702                 domp = ppm_get_domain_by_dev(path);
 703                 kmem_free(path, MAXPATHLEN);
 704 
 705                 if (domp == NULL)
 706                         return (ENODEV);
 707 
 708                 ppmd = domp->devlist;
 709                 if (cmd == PPMSET_NORMAL) {
 710                         if (domp->model != PPMD_CPU)
 711                                 return (EINVAL);
 712                         level = STRUCT_FGET(norm, norm);
 713                         dcomps = DEVI(ppmd->dip)->devi_pm_components;
 714                         pm_comp = &dcomps[ppmd->cmpt].pmc_comp;
 715                         for (i = pm_comp->pmc_numlevels; i > 0; i--) {
 716                                 if (pm_comp->pmc_lvals[i-1] == level)
 717                                         break;
 718                         }
 719                         if (i == 0)
 720                                 return (EINVAL);
 721 
 722                         ppm_set_topspeed(ppmd, pm_comp->pmc_numlevels - i);
 723                 }
 724 
 725                 level = pm_get_normal_power(ppmd->dip, 0);
 726 
 727                 STRUCT_FSET(norm, norm, level);
 728                 ret = ddi_copyout(STRUCT_BUF(norm), (caddr_t)arg,
 729                     STRUCT_SIZE(norm), mode);
 730                 if (ret != 0) {
 731                         PPMD(D_IOCTL, ("%s: can't copyout, line(%d)\n",
 732                             str, __LINE__))
 733                         ret = EFAULT;
 734                 }
 735                 break;
 736         }
 737 #endif
 738         default:
 739                 PPMD(D_IOCTL, ("%s: unsupported ioctl command(%d)\n", str, cmd))
 740                 return (EINVAL);
 741         }
 742 
 743         return (ret);
 744 }
 745 
 746 
 747 static int      ppm_manage_sx(s3a_t *, int);
 748 static int      ppm_search_list(pm_searchargs_t *);
 749 
 750 /*
 751  * interface between pm framework and ppm driver
 752  */
 753 /* ARGSUSED */
 754 static int
 755 ppm_ctlops(dev_info_t *dip, dev_info_t *rdip,
 756     ddi_ctl_enum_t ctlop, void *arg, void *result)
 757 {
 758         power_req_t     *reqp = (power_req_t *)arg;
 759         ppm_unit_t      *unitp;
 760         ppm_domain_t    *domp;
 761         ppm_dev_t       *ppmd;
 762         char            path[MAXNAMELEN];
 763         ppm_owned_t     *owned;
 764         int             mode;
 765         int             ret = DDI_SUCCESS;
 766         int             *res = (int *)result;
 767         s3a_t s3args;
 768 
 769 #ifdef DEBUG
 770         char    *str = "ppm_ctlops";
 771         int     mask = ppm_debug & (D_CTLOPS1 | D_CTLOPS2);
 772         char *ctlstr = ppm_get_ctlstr(reqp->request_type, mask);
 773         if (mask && ctlstr)
 774                 PPMD(mask, ("%s: %s, %s\n",
 775                     str, ddi_binding_name(rdip), ctlstr))
 776 #endif
 777 
 778         if (ctlop != DDI_CTLOPS_POWER) {
 779                 return (DDI_FAILURE);
 780         }
 781 
 782         unitp = (ppm_unit_t *)ddi_get_soft_state(ppm_statep, ppm_inst);
 783 
 784         switch (reqp->request_type) {
 785 
 786         /* attempt to blink led if indeed all at lowest */
 787         case PMR_PPM_ALL_LOWEST:
 788                 mode = (reqp->req.ppm_all_lowest_req.mode == PM_ALL_LOWEST);
 789                 if (!(unitp->states & PPM_STATE_SUSPENDED) && mode)
 790                         ppm_manage_led(PPM_LED_BLINKING);
 791                 else
 792                         ppm_manage_led(PPM_LED_SOLIDON);
 793                 return (DDI_SUCCESS);
 794 
 795         /* undo the claiming of 'rdip' at attach time */
 796         case PMR_PPM_POST_DETACH:
 797                 ASSERT(reqp->req.ppm_set_power_req.who == rdip);
 798                 mutex_enter(&unitp->lock);
 799                 if (reqp->req.ppm_config_req.result != DDI_SUCCESS ||
 800                     (PPM_GET_PRIVATE(rdip) == NULL)) {
 801                         mutex_exit(&unitp->lock);
 802                         return (DDI_FAILURE);
 803                 }
 804                 mutex_exit(&unitp->lock);
 805                 ppm_rem_dev(rdip);
 806                 return (DDI_SUCCESS);
 807 
 808         /* chance to adjust pwr_cnt if resume is about to power up rdip */
 809         case PMR_PPM_PRE_RESUME:
 810                 ppm_svc_resume_ctlop(rdip, reqp);
 811                 return (DDI_SUCCESS);
 812 
 813         /*
 814          * synchronizing, so that only the owner of the power lock is
 815          * permitted to change device and component's power level.
 816          */
 817         case PMR_PPM_UNLOCK_POWER:
 818         case PMR_PPM_TRY_LOCK_POWER:
 819         case PMR_PPM_LOCK_POWER:
 820                 ppmd = PPM_GET_PRIVATE(rdip);
 821                 if (ppmd)
 822                         domp = ppmd->domp;
 823                 else if (reqp->request_type != PMR_PPM_UNLOCK_POWER) {
 824                         domp = ppm_lookup_dev(rdip);
 825                         ASSERT(domp);
 826                         ppmd = ppm_get_dev(rdip, domp);
 827                 }
 828 
 829                 PPMD(D_LOCKS, ("ppm_lock_%s: %s, %s\n",
 830                     (domp->dflags & PPMD_LOCK_ALL) ? "all" : "one",
 831                     ppmd->path, ppm_get_ctlstr(reqp->request_type, D_LOCKS)))
 832 
 833                 if (domp->dflags & PPMD_LOCK_ALL)
 834                         ppm_lock_all(domp, reqp, result);
 835                 else
 836                         ppm_lock_one(ppmd, reqp, result);
 837                 return (DDI_SUCCESS);
 838 
 839         case PMR_PPM_POWER_LOCK_OWNER:
 840                 ASSERT(reqp->req.ppm_power_lock_owner_req.who == rdip);
 841                 ppmd = PPM_GET_PRIVATE(rdip);
 842                 if (ppmd)
 843                         domp = ppmd->domp;
 844                 else {
 845                         domp = ppm_lookup_dev(rdip);
 846                         ASSERT(domp);
 847                         ppmd = ppm_get_dev(rdip, domp);
 848                 }
 849 
 850                 /*
 851                  * In case of LOCK_ALL, effective owner of the power lock
 852                  * is the owner of the domain lock. otherwise, it is the owner
 853                  * of the power lock.
 854                  */
 855                 if (domp->dflags & PPMD_LOCK_ALL)
 856                         reqp->req.ppm_power_lock_owner_req.owner =
 857                             mutex_owner(&domp->lock);
 858                 else {
 859                         reqp->req.ppm_power_lock_owner_req.owner =
 860                             DEVI(rdip)->devi_busy_thread;
 861                 }
 862                 return (DDI_SUCCESS);
 863 
 864         case PMR_PPM_INIT_CHILD:
 865                 ASSERT(reqp->req.ppm_lock_power_req.who == rdip);
 866                 if ((domp = ppm_lookup_dev(rdip)) == NULL)
 867                         return (DDI_SUCCESS);
 868 
 869                 /*
 870                  * We keep track of power-manageable devices starting with
 871                  * initialization process.  The initializing flag remains
 872                  * set until it is cleared by ppm_add_dev().  Power management
 873                  * policy for some domains are affected even during device
 874                  * initialization.  For example, PCI domains should leave
 875                  * their clock running meanwhile a device in that domain
 876                  * is initializing.
 877                  */
 878                 mutex_enter(&domp->lock);
 879                 owned = ppm_add_owned(rdip, domp);
 880                 ASSERT(owned->initializing == 0);
 881                 owned->initializing = 1;
 882 
 883                 if (PPMD_IS_PCI(domp->model) && domp->status == PPMD_OFF) {
 884                         ret = ppm_switch_clock(domp, PPMD_ON);
 885                         if (ret == DDI_SUCCESS)
 886                                 domp->dflags |= PPMD_INITCHILD_CLKON;
 887                 }
 888                 mutex_exit(&domp->lock);
 889                 return (ret);
 890 
 891         case PMR_PPM_POST_ATTACH:
 892                 ASSERT(reqp->req.ppm_config_req.who == rdip);
 893                 domp = ppm_lookup_dev(rdip);
 894                 ASSERT(domp);
 895                 ASSERT(domp->status == PPMD_ON);
 896                 if (reqp->req.ppm_config_req.result == DDI_SUCCESS) {
 897                         /*
 898                          * call ppm_get_dev, which will increment the
 899                          * domain power count by the right number.
 900                          * Undo the power count increment, done in PRE_PROBE.
 901                          */
 902                         if (PM_GET_PM_INFO(rdip))
 903                                 ppmd = ppm_get_dev(rdip, domp);
 904                         mutex_enter(&domp->lock);
 905                         ASSERT(domp->pwr_cnt > 0);
 906                         domp->pwr_cnt--;
 907                         mutex_exit(&domp->lock);
 908                         return (DDI_SUCCESS);
 909                 }
 910 
 911                 ret = ppm_power_down_domain(rdip);
 912                 /* FALLTHROUGH */
 913         case PMR_PPM_UNINIT_CHILD:
 914                 ASSERT(reqp->req.ppm_lock_power_req.who == rdip);
 915                 if ((domp = ppm_lookup_dev(rdip)) == NULL)
 916                         return (DDI_SUCCESS);
 917 
 918                 (void) ddi_pathname(rdip, path);
 919                 mutex_enter(&domp->lock);
 920                 for (owned = domp->owned; owned; owned = owned->next)
 921                         if (strcmp(owned->path, path) == 0)
 922                                 break;
 923 
 924                 /*
 925                  * In case we didn't go through a complete attach and detach,
 926                  * the initializing flag will still be set, so clear it.
 927                  */
 928                 if ((owned != NULL) && (owned->initializing))
 929                         owned->initializing = 0;
 930 
 931                 if (PPMD_IS_PCI(domp->model) &&
 932                     domp->status == PPMD_ON && domp->pwr_cnt == 0 &&
 933                     (domp->dflags & PPMD_INITCHILD_CLKON) &&
 934                     ppm_none_else_holds_power(domp)) {
 935                         ret = ppm_switch_clock(domp, PPMD_OFF);
 936                         if (ret == DDI_SUCCESS)
 937                                 domp->dflags &= ~PPMD_INITCHILD_CLKON;
 938                 }
 939                 mutex_exit(&domp->lock);
 940                 return (ret);
 941 
 942         /* place holders */
 943         case PMR_PPM_UNMANAGE:
 944         case PMR_PPM_PRE_DETACH:
 945                 return (DDI_SUCCESS);
 946 
 947         case PMR_PPM_PRE_PROBE:
 948                 ASSERT(reqp->req.ppm_config_req.who == rdip);
 949                 return (ppm_power_up_domain(rdip));
 950 
 951         case PMR_PPM_POST_PROBE:
 952                 ASSERT(reqp->req.ppm_config_req.who == rdip);
 953                 if (reqp->req.ppm_config_req.result == DDI_PROBE_SUCCESS ||
 954                     reqp->req.ppm_config_req.result == DDI_PROBE_DONTCARE)
 955                         return (DDI_SUCCESS);
 956 
 957                 /* Probe failed */
 958                 PPMD(D_CTLOPS1 | D_CTLOPS2, ("%s: probe failed for %s@%s "
 959                     "rv %d\n", str, PM_NAME(rdip), PM_ADDR(rdip),
 960                     reqp->req.ppm_config_req.result))
 961                 return (ppm_power_down_domain(rdip));
 962 
 963         case PMR_PPM_PRE_ATTACH:
 964                 ASSERT(reqp->req.ppm_config_req.who == rdip);
 965                 /* Domain has already been powered up in PRE_PROBE */
 966                 domp = ppm_lookup_dev(rdip);
 967                 ASSERT(domp);
 968                 ASSERT(domp->status == PPMD_ON);
 969                 return (DDI_SUCCESS);
 970 
 971         /* ppm intercepts power change process to the claimed devices */
 972         case PMR_PPM_SET_POWER:
 973         case PMR_PPM_POWER_CHANGE_NOTIFY:
 974                 if ((ppmd = PPM_GET_PRIVATE(rdip)) == NULL) {
 975                         domp = ppm_lookup_dev(rdip);
 976                         ASSERT(domp);
 977                         ppmd = ppm_get_dev(rdip, domp);
 978                 }
 979                 switch (ppmd->domp->model) {
 980                 case PPMD_CPU:
 981                         return (ppm_manage_cpus(rdip, reqp, result));
 982                 case PPMD_FET:
 983                         return (ppm_manage_fet(rdip, reqp, result));
 984                 case PPMD_PCI:
 985                 case PPMD_PCI_PROP:
 986                         return (ppm_manage_pci(rdip, reqp, result));
 987                 case PPMD_PCIE:
 988                         return (ppm_manage_pcie(rdip, reqp, result));
 989                 default:
 990                         cmn_err(CE_WARN, "ppm_ctlops: domain model %d does"
 991                             " not support PMR_PPM_SET_POWER ctlop",
 992                             ppmd->domp->model);
 993                         return (DDI_FAILURE);
 994                 }
 995 
 996         case PMR_PPM_ENTER_SX:
 997         case PMR_PPM_EXIT_SX:
 998                 s3args.s3a_state = reqp->req.ppm_power_enter_sx_req.sx_state;
 999                 s3args.s3a_test_point =
1000                     reqp->req.ppm_power_enter_sx_req.test_point;
1001                 s3args.s3a_wakephys = reqp->req.ppm_power_enter_sx_req.wakephys;
1002                 s3args.s3a_psr = reqp->req.ppm_power_enter_sx_req.psr;
1003                 ret = ppm_manage_sx(&s3args,
1004                     reqp->request_type == PMR_PPM_ENTER_SX);
1005                 if (ret) {
1006                         PPMD(D_CPR, ("ppm_manage_sx returns %d\n", ret))
1007                         return (DDI_FAILURE);
1008                 } else {
1009                         return (DDI_SUCCESS);
1010                 }
1011 
1012         case PMR_PPM_SEARCH_LIST:
1013                 ret = ppm_search_list(reqp->req.ppm_search_list_req.searchlist);
1014                 reqp->req.ppm_search_list_req.result = ret;
1015                 *res = ret;
1016                 if (ret) {
1017                         PPMD(D_CPR, ("ppm_search_list returns %d\n", ret))
1018                         return (DDI_FAILURE);
1019                 } else {
1020                         PPMD(D_CPR, ("ppm_search_list returns %d\n", ret))
1021                         return (DDI_SUCCESS);
1022                 }
1023 
1024         default:
1025                 cmn_err(CE_WARN, "ppm_ctlops: unrecognized ctlops req(%d)",
1026                     reqp->request_type);
1027                 return (DDI_FAILURE);
1028         }
1029 }
1030 
1031 
1032 /*
1033  * Raise the power level of a subrange of cpus.  Used when cpu driver
1034  * failed an attempt to lower the power of a cpu (probably because
1035  * it got busy).  Need to revert the ones we already changed.
1036  *
1037  * ecpup = the ppm_dev_t for the cpu which failed to lower power
1038  * level = power level to reset prior cpus to
1039  */
1040 int
1041 ppm_revert_cpu_power(ppm_dev_t *ecpup, int level)
1042 {
1043         ppm_dev_t *cpup;
1044         int ret = DDI_SUCCESS;
1045 
1046         for (cpup = ecpup->domp->devlist; cpup != ecpup; cpup = cpup->next) {
1047                 PPMD(D_CPU, ("ppm_revert_cpu_power: \"%s\", revert to "
1048                     "level %d\n", cpup->path, level))
1049 
1050                 ret = pm_power(cpup->dip, 0, level);
1051                 if (ret == DDI_SUCCESS) {
1052                         cpup->level = level;
1053                         cpup->rplvl = PM_LEVEL_UNKNOWN;
1054                 }
1055         }
1056         return (ret);
1057 }
1058 
1059 
1060 /*
1061  * ppm_manage_cpus - Process a request to change the power level of a cpu.
1062  * If not all cpus want to be at the same level, OR if we are currently
1063  * refusing slowdown requests due to thermal stress, we cache the request.
1064  * Otherwise, set all cpus to the new power level.
1065  */
1066 /* ARGSUSED */
1067 static int
1068 ppm_manage_cpus(dev_info_t *dip, power_req_t *reqp, int *result)
1069 {
1070 #ifdef  DEBUG
1071         char *str = "ppm_manage_cpus";
1072 #endif
1073         int old, new, ret, kmflag;
1074         ppm_dev_t *ppmd, *cpup;
1075         int change_notify = 0;
1076         pm_ppm_devlist_t *devlist = NULL, *p;
1077         int             do_rescan = 0;
1078 
1079         *result = DDI_SUCCESS;
1080 
1081         switch (reqp->request_type) {
1082         case PMR_PPM_SET_POWER:
1083                 break;
1084 
1085         case PMR_PPM_POWER_CHANGE_NOTIFY:
1086                 change_notify = 1;
1087                 break;
1088 
1089         default:
1090                 return (DDI_FAILURE);
1091         }
1092 
1093         ppmd = PPM_GET_PRIVATE(dip);
1094         ASSERT(MUTEX_HELD(&ppmd->domp->lock));
1095         old = reqp->req.ppm_set_power_req.old_level;
1096         new = reqp->req.ppm_set_power_req.new_level;
1097 
1098         if (change_notify) {
1099                 ppmd->level = new;
1100                 ppmd->rplvl = PM_LEVEL_UNKNOWN;
1101 
1102                 PPMD(D_CPU, ("%s: Notify cpu dip %p power level has changed "
1103                     "from %d to %d", str, (void *)dip, old, new))
1104                 return (DDI_SUCCESS);
1105         }
1106 
1107         if (ppm_manage_early_cpus(dip, new, result))
1108                 return (*result);
1109 
1110         if (new == ppmd->level) {
1111                 PPMD(D_CPU, ("%s: already at power level %d\n", str, new))
1112                 return (DDI_SUCCESS);
1113         }
1114 
1115         /*
1116          * A request from lower to higher level transition is granted and
1117          * made effective on all cpus. A request from higher to lower must
1118          * be agreed upon by all cpus.
1119          */
1120         ppmd->rplvl = new;
1121         for (cpup = ppmd->domp->devlist; cpup; cpup = cpup->next) {
1122                 if (cpup->rplvl == new)
1123                         continue;
1124 
1125                 if (new < old) {
1126                         PPMD(D_SOME, ("%s: not all cpus wants to be at new "
1127                             "level %d yet.\n", str, new))
1128                         return (DDI_SUCCESS);
1129                 }
1130 
1131                 /*
1132                  * If a single cpu requests power up, honor the request
1133                  * powering up all cpus.
1134                  */
1135                 if (new > old) {
1136                         PPMD(D_SOME, ("%s: powering up device(%s@%s, %p) "
1137                             "because of request from dip(%s@%s, %p), "
1138                             "need pm_rescan\n", str, PM_NAME(cpup->dip),
1139                             PM_ADDR(cpup->dip), (void *)cpup->dip,
1140                             PM_NAME(dip), PM_ADDR(dip), (void *)dip))
1141                         do_rescan++;
1142                 }
1143         }
1144 
1145         PPMD(D_SETLVL, ("%s: \"%s\" set power level old %d, new %d \n",
1146             str, ppmd->path, ppmd->level, new))
1147         ret = ppm_change_cpu_power(ppmd, new);
1148         *result = ret;
1149 
1150         if (ret == DDI_SUCCESS) {
1151                 if (reqp->req.ppm_set_power_req.canblock == PM_CANBLOCK_BLOCK)
1152                         kmflag = KM_SLEEP;
1153                 else
1154                         kmflag = KM_NOSLEEP;
1155 
1156                 for (cpup = ppmd->domp->devlist; cpup; cpup = cpup->next) {
1157                         if (cpup->dip == dip)
1158                                 continue;
1159 
1160                         if ((p = kmem_zalloc(sizeof (pm_ppm_devlist_t),
1161                             kmflag)) == NULL) {
1162                                 break;
1163                         }
1164                         p->ppd_who = cpup->dip;
1165                         p->ppd_cmpt = cpup->cmpt;
1166                         p->ppd_old_level = old;
1167                         p->ppd_new_level = new;
1168                         p->ppd_next = devlist;
1169 
1170                         PPMD(D_SETLVL, ("%s: devlist entry[\"%s\"] %d -> %d\n",
1171                             str, cpup->path, old, new))
1172 
1173                         devlist = p;
1174                 }
1175                 reqp->req.ppm_set_power_req.cookie = (void *) devlist;
1176 
1177                 if (do_rescan > 0) {
1178                         for (cpup = ppmd->domp->devlist; cpup;
1179                             cpup = cpup->next) {
1180                                 if (cpup->dip == dip)
1181                                         continue;
1182                                 pm_rescan(cpup->dip);
1183                         }
1184                 }
1185         }
1186 
1187         return (ret);
1188 }
1189 
1190 
1191 /*
1192  * ppm_svc_resume_ctlop - this is a small bookkeeping ppm does -
1193  * increments its FET domain power count, in anticipation of that
1194  * the indicated device(dip) would be powered up by its driver as
1195  * a result of cpr resuming.
1196  */
1197 /* ARGSUSED */
1198 static void
1199 ppm_svc_resume_ctlop(dev_info_t *dip, power_req_t *reqp)
1200 {
1201         ppm_domain_t *domp;
1202         ppm_dev_t *ppmd;
1203         int powered;    /* power up count per dip */
1204 
1205         ppmd = PPM_GET_PRIVATE(dip);
1206         if (ppmd == NULL)
1207                 return;
1208 
1209         /*
1210          * Maintain correct powered count for domain which cares
1211          */
1212         powered = 0;
1213         domp = ppmd->domp;
1214         mutex_enter(&domp->lock);
1215         if ((domp->model == PPMD_FET) || PPMD_IS_PCI(domp->model) ||
1216             (domp->model == PPMD_PCIE)) {
1217                 for (ppmd = domp->devlist; ppmd; ppmd = ppmd->next) {
1218                         if (ppmd->dip == dip && ppmd->level)
1219                                 powered++;
1220                 }
1221 
1222                 /*
1223                  * All fets and clocks are held on during suspend -
1224                  * resume window regardless their domain devices' power
1225                  * level.
1226                  */
1227                 ASSERT(domp->status == PPMD_ON);
1228 
1229                 /*
1230                  * The difference indicates the number of components
1231                  * being off prior to suspend operation, that is the
1232                  * amount needs to be compensated in order to sync up
1233                  * bookkeeping with reality, for PROM reset would have
1234                  * brought up all devices.
1235                  */
1236                 if (powered < PM_NUMCMPTS(dip))
1237                         domp->pwr_cnt += PM_NUMCMPTS(dip) - powered;
1238         }
1239         for (ppmd = domp->devlist; ppmd; ppmd = ppmd->next) {
1240                 if (ppmd->dip == dip)
1241                         ppmd->level = ppmd->rplvl = PM_LEVEL_UNKNOWN;
1242         }
1243         mutex_exit(&domp->lock);
1244 }
1245 
1246 #ifdef  DEBUG
1247 static int ppmbringup = 0;
1248 #endif
1249 
1250 int
1251 ppm_bringup_domains()
1252 {
1253 #ifdef DEBUG
1254         char *str = "ppm_bringup_domains";
1255 #endif
1256         ppm_domain_t    *domp;
1257         int     ret = DDI_SUCCESS;
1258 
1259         PPMD(D_CPR, ("%s[%d]: enter\n", str, ++ppmbringup))
1260         for (domp = ppm_domain_p; domp; domp = domp->next) {
1261                 if ((!PPMD_IS_PCI(domp->model) && (domp->model != PPMD_FET) &&
1262                     (domp->model != PPMD_PCIE)) || (domp->devlist == NULL))
1263                         continue;
1264 
1265                 mutex_enter(&domp->lock);
1266                 if (domp->status == PPMD_ON) {
1267                         mutex_exit(&domp->lock);
1268                         continue;
1269                 }
1270                 switch (domp->model) {
1271                 case PPMD_FET:
1272                         ret = ppm_fetset(domp, PPMD_ON);
1273                         break;
1274                 case PPMD_PCI:
1275                 case PPMD_PCI_PROP:
1276                         ret = ppm_switch_clock(domp, PPMD_ON);
1277                         break;
1278                 case PPMD_PCIE:
1279                         ret = ppm_pcie_pwr(domp, PPMD_ON);
1280                         break;
1281                 default:
1282                         break;
1283                 }
1284                 mutex_exit(&domp->lock);
1285         }
1286         PPMD(D_CPR, ("%s[%d]: exit\n", str, ppmbringup))
1287 
1288         return (ret);
1289 }
1290 
1291 #ifdef  DEBUG
1292 static int ppmsyncbp = 0;
1293 #endif
1294 
1295 int
1296 ppm_sync_bookkeeping()
1297 {
1298 #ifdef DEBUG
1299         char *str = "ppm_sync_bookkeeping";
1300 #endif
1301         ppm_domain_t    *domp;
1302         int     ret = DDI_SUCCESS;
1303 
1304         PPMD(D_CPR, ("%s[%d]: enter\n", str, ++ppmsyncbp))
1305         for (domp = ppm_domain_p; domp; domp = domp->next) {
1306                 if ((!PPMD_IS_PCI(domp->model) && (domp->model != PPMD_FET) &&
1307                     (domp->model != PPMD_PCIE)) || (domp->devlist == NULL))
1308                         continue;
1309 
1310                 mutex_enter(&domp->lock);
1311                 if ((domp->pwr_cnt != 0) || !ppm_none_else_holds_power(domp)) {
1312                         mutex_exit(&domp->lock);
1313                         continue;
1314                 }
1315 
1316                 /*
1317                  * skip NULL .devlist slot, for some may host pci device
1318                  * that can not tolerate clock off or not even participate
1319                  * in PM.
1320                  */
1321                 if (domp->devlist == NULL)
1322                         continue;
1323 
1324                 switch (domp->model) {
1325                 case PPMD_FET:
1326                         ret = ppm_fetset(domp, PPMD_OFF);
1327                         break;
1328                 case PPMD_PCI:
1329                 case PPMD_PCI_PROP:
1330                         ret = ppm_switch_clock(domp, PPMD_OFF);
1331                         break;
1332                 case PPMD_PCIE:
1333                         ret = ppm_pcie_pwr(domp, PPMD_OFF);
1334                         break;
1335                 default:
1336                         break;
1337                 }
1338                 mutex_exit(&domp->lock);
1339         }
1340         PPMD(D_CPR, ("%s[%d]: exit\n", str, ppmsyncbp))
1341 
1342         return (ret);
1343 }
1344 
1345 
1346 
1347 /*
1348  * pre-suspend window;
1349  *
1350  * power up every FET and PCI clock that are off;
1351  *
1352  * set ppm_cpr_window global flag to indicate
1353  * that even though all pm_scan requested power transitions
1354  * will be honored as usual but that until we're out
1355  * of this window,  no FET or clock will be turned off
1356  * for domains with pwr_cnt decremented down to 0.
1357  * Such is to avoid accessing the orthogonal drivers that own
1358  * the FET and clock registers that may not be resumed yet.
1359  *
1360  * at post-resume window, walk through each FET and PCI domains,
1361  * bring pwr_cnt and domp->status to sense: if pwr-cnt == 0,
1362  * and noinvol check okays, power down the FET or PCI.  At last,
1363  * clear the global flag ppm_cpr_window.
1364  *
1365  * ASSERT case 1, during cpr window, checks pwr_cnt against power
1366  *      transitions;
1367  * ASSERT case 2, out of cpr window, checks four things:
1368  *      pwr_cnt <> power transition in/out of 0
1369  *      <> status <> record of noinvol device detached
1370  *
1371  */
1372 /* ARGSUSED */
1373 static boolean_t
1374 ppm_cpr_callb(void *arg, int code)
1375 {
1376         int     ret;
1377 
1378         switch (code) {
1379         case CB_CODE_CPR_CHKPT:
1380 
1381                 /* pre-suspend: start of cpr window */
1382                 mutex_enter(&ppm_cpr_window_lock);
1383                 ASSERT(ppm_cpr_window_flag == B_FALSE);
1384                 ppm_cpr_window_flag = B_TRUE;
1385                 mutex_exit(&ppm_cpr_window_lock);
1386 
1387                 ret = ppm_bringup_domains();
1388 
1389                 break;
1390 
1391         case CB_CODE_CPR_RESUME:
1392 
1393                 /* post-resume: end of cpr window */
1394                 ret = ppm_sync_bookkeeping();
1395 
1396                 mutex_enter(&ppm_cpr_window_lock);
1397                 ASSERT(ppm_cpr_window_flag == B_TRUE);
1398                 ppm_cpr_window_flag = B_FALSE;
1399                 mutex_exit(&ppm_cpr_window_lock);
1400 
1401                 break;
1402         }
1403 
1404         return (ret == DDI_SUCCESS);
1405 }
1406 
1407 
1408 /*
1409  * Initialize our private version of real power level
1410  * as well as lowest and highest levels the device supports;
1411  * relate to ppm_add_dev
1412  */
1413 void
1414 ppm_dev_init(ppm_dev_t *ppmd)
1415 {
1416         struct pm_component *dcomps;
1417         struct pm_comp *pm_comp;
1418         dev_info_t *dip;
1419         int maxi, i;
1420 
1421         ASSERT(MUTEX_HELD(&ppmd->domp->lock));
1422         ppmd->level = PM_LEVEL_UNKNOWN;
1423         ppmd->rplvl = PM_LEVEL_UNKNOWN;
1424 
1425         /* increment pwr_cnt per component */
1426         if ((ppmd->domp->model == PPMD_FET) ||
1427             PPMD_IS_PCI(ppmd->domp->model) ||
1428             (ppmd->domp->model == PPMD_PCIE))
1429                 ppmd->domp->pwr_cnt++;
1430 
1431         dip = ppmd->dip;
1432 
1433         /*
1434          * ppm exists to handle power-manageable devices which require
1435          * special handling on the current platform.  However, a
1436          * driver for such a device may choose not to support power
1437          * management on a particular load/attach.  In this case we
1438          * we create a structure to represent a single-component device
1439          * for which "level" = PM_LEVEL_UNKNOWN and "lowest" = 0
1440          * are effectively constant.
1441          */
1442         if (PM_GET_PM_INFO(dip)) {
1443                 dcomps = DEVI(dip)->devi_pm_components;
1444                 pm_comp = &dcomps[ppmd->cmpt].pmc_comp;
1445 
1446                 ppmd->lowest = pm_comp->pmc_lvals[0];
1447                 ASSERT(ppmd->lowest >= 0);
1448                 maxi = pm_comp->pmc_numlevels - 1;
1449                 ppmd->highest = pm_comp->pmc_lvals[maxi];
1450 
1451                 /*
1452                  * If 66mhz PCI device on pci 66mhz bus supports D2 state
1453                  * (config reg PMC bit 10 set), ppm could turn off its bus
1454                  * clock once it is at D3hot.
1455                  */
1456                 if (ppmd->domp->dflags & PPMD_PCI66MHZ) {
1457                         for (i = 0; i < maxi; i++)
1458                                 if (pm_comp->pmc_lvals[i] == PM_LEVEL_D2) {
1459                                         ppmd->flags |= PPMDEV_PCI66_D2;
1460                                         break;
1461                                 }
1462                 }
1463         }
1464 
1465         /*
1466          * If device is in PCI_PROP domain and has exported the
1467          * property listed in ppm.conf, its clock will be turned
1468          * off when all pm'able devices in that domain are at D3.
1469          */
1470         if ((ppmd->domp->model == PPMD_PCI_PROP) &&
1471             (ppmd->domp->propname != NULL) &&
1472             ddi_prop_exists(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
1473             ppmd->domp->propname))
1474                 ppmd->flags |= PPMDEV_PCI_PROP_CLKPM;
1475 }
1476 
1477 
1478 /*
1479  * relate to ppm_rem_dev
1480  */
1481 void
1482 ppm_dev_fini(ppm_dev_t *ppmd)
1483 {
1484         ASSERT(MUTEX_HELD(&ppmd->domp->lock));
1485 
1486         /* decrement pwr_cnt per component */
1487         if ((ppmd->domp->model == PPMD_FET) ||
1488             PPMD_IS_PCI(ppmd->domp->model) ||
1489             (ppmd->domp->model == PPMD_PCIE))
1490                 if (ppmd->level != ppmd->lowest)
1491                         ppmd->domp->pwr_cnt--;
1492 }
1493 
1494 /*
1495  * Each power fet controls the power of one or more platform
1496  * device(s) within their domain.  Hence domain devices' power
1497  * level change has been monitored, such that once all devices
1498  * are powered off, the fet is turned off to save more power.
1499  *
1500  * To power on any domain device, the domain power fet
1501  * needs to be turned on first. always one fet per domain.
1502  */
1503 static int
1504 ppm_manage_fet(dev_info_t *dip, power_req_t *reqp, int *result)
1505 {
1506 #ifdef DEBUG
1507         char *str = "ppm_manage_fet";
1508 #endif
1509         int (*pwr_func)(ppm_dev_t *, int, int);
1510         int             new, old, cmpt;
1511         ppm_dev_t       *ppmd;
1512         ppm_domain_t    *domp;
1513         int             incr = 0;
1514         int             dummy_ret;
1515 
1516 
1517         *result = DDI_SUCCESS;
1518         switch (reqp->request_type) {
1519         case PMR_PPM_SET_POWER:
1520                 pwr_func = ppm_change_power_level;
1521                 old = reqp->req.ppm_set_power_req.old_level;
1522                 new = reqp->req.ppm_set_power_req.new_level;
1523                 cmpt = reqp->req.ppm_set_power_req.cmpt;
1524                 break;
1525         case PMR_PPM_POWER_CHANGE_NOTIFY:
1526                 pwr_func = ppm_record_level_change;
1527                 old = reqp->req.ppm_notify_level_req.old_level;
1528                 new = reqp->req.ppm_notify_level_req.new_level;
1529                 cmpt = reqp->req.ppm_notify_level_req.cmpt;
1530                 break;
1531         default:
1532                 *result = DDI_FAILURE;
1533                 PPMD(D_FET, ("%s: unknown request type %d for %s@%s\n",
1534                     str, reqp->request_type, PM_NAME(dip), PM_ADDR(dip)))
1535                 return (DDI_FAILURE);
1536         }
1537 
1538         for (ppmd = PPM_GET_PRIVATE(dip); ppmd; ppmd = ppmd->next)
1539                 if (cmpt == ppmd->cmpt)
1540                         break;
1541         if (!ppmd) {
1542                 PPMD(D_FET, ("%s: dip(%p): old(%d)->new(%d): no ppm_dev"
1543                     " found for cmpt(%d)", str, (void *)dip, old, new, cmpt))
1544                 *result = DDI_FAILURE;
1545                 return (DDI_FAILURE);
1546         }
1547         domp = ppmd->domp;
1548         PPMD(D_FET, ("%s: %s@%s %s old %d, new %d, c%d, level %d, "
1549             "status %s\n", str, PM_NAME(dip), PM_ADDR(dip),
1550             ppm_get_ctlstr(reqp->request_type, ~0), old, new, cmpt,
1551             ppmd->level, (domp->status == PPMD_OFF ? "off" : "on")))
1552 
1553 
1554         ASSERT(old == ppmd->level);
1555 
1556         if (new == ppmd->level) {
1557                 PPMD(D_FET, ("nop\n"))
1558                 return (DDI_SUCCESS);
1559         }
1560 
1561         PPM_LOCK_DOMAIN(domp);
1562 
1563         /*
1564          * In general, a device's published lowest power level does not
1565          * have to be 0 if power-off is not tolerated. i.e. a device
1566          * instance may export its lowest level > 0.  It is reasonable to
1567          * assume that level 0 indicates off state, positive level values
1568          * indicate power states above off, include full power state.
1569          */
1570         if (new > 0) { /* device powering up or to different positive level */
1571                 if (domp->status == PPMD_OFF) {
1572 
1573                         /* can not be in (chpt, resume) window */
1574                         ASSERT(ppm_cpr_window_flag == B_FALSE);
1575 
1576                         ASSERT(old == 0 && domp->pwr_cnt == 0);
1577 
1578                         PPMD(D_FET, ("About to turn fet on for %s@%s c%d\n",
1579                             PM_NAME(dip), PM_ADDR(dip), cmpt))
1580 
1581                         *result = ppm_fetset(domp, PPMD_ON);
1582                         if (*result != DDI_SUCCESS) {
1583                                 PPMD(D_FET, ("\tCan't turn on power FET: "
1584                                     "ret(%d)\n", *result))
1585                                 PPM_UNLOCK_DOMAIN(domp);
1586                                 return (DDI_FAILURE);
1587                         }
1588                 }
1589 
1590                 /*
1591                  * If powering up, pre-increment the count before
1592                  * calling pwr_func, because we are going to release
1593                  * the domain lock and another thread might turn off
1594                  * domain power otherwise.
1595                  */
1596                 if (old == 0) {
1597                         domp->pwr_cnt++;
1598                         incr = 1;
1599                 }
1600 
1601                 PPMD(D_FET, ("\t%s domain power count: %d\n",
1602                     domp->name, domp->pwr_cnt))
1603         }
1604 
1605 
1606         PPM_UNLOCK_DOMAIN(domp);
1607 
1608         ASSERT(domp->pwr_cnt > 0);
1609 
1610         if ((*result = (*pwr_func)(ppmd, cmpt, new)) != DDI_SUCCESS) {
1611                 PPMD(D_FET, ("\t%s power change failed: ret(%d)\n",
1612                     ppmd->path, *result))
1613         }
1614 
1615         PPM_LOCK_DOMAIN(domp);
1616 
1617         /*
1618          * Decr the power count in two cases:
1619          *
1620          *   1) request was to power device down and was successful
1621          *   2) request was to power up (we pre-incremented count), but failed.
1622          */
1623         if ((*result == DDI_SUCCESS && ppmd->level == 0) ||
1624             (*result != DDI_SUCCESS && incr)) {
1625                 ASSERT(domp->pwr_cnt > 0);
1626                 domp->pwr_cnt--;
1627         }
1628 
1629         PPMD(D_FET, ("\t%s domain power count: %d\n",
1630             domp->name, domp->pwr_cnt))
1631 
1632         /*
1633          * call to pwr_func will update ppm data structures, if it
1634          * succeeds. ppm should return whatever is the return value
1635          * from call to pwr_func. This way pm and ppm data structures
1636          * always in sync. Use dummy_ret from here for any further
1637          * return values.
1638          */
1639         if ((domp->pwr_cnt == 0) &&
1640             (ppm_cpr_window_flag == B_FALSE) &&
1641             ppm_none_else_holds_power(domp)) {
1642 
1643                 PPMD(D_FET, ("About to turn FET off for %s@%s c%d\n",
1644                     PM_NAME(dip), PM_ADDR(dip), cmpt))
1645 
1646                 dummy_ret = ppm_fetset(domp, PPMD_OFF);
1647                 if (dummy_ret != DDI_SUCCESS) {
1648                         PPMD(D_FET, ("\tCan't turn off FET: ret(%d)\n",
1649                             dummy_ret))
1650                 }
1651         }
1652 
1653         PPM_UNLOCK_DOMAIN(domp);
1654         ASSERT(domp->pwr_cnt >= 0);
1655         return (*result);
1656 }
1657 
1658 
1659 /*
1660  * the actual code that turn on or off domain power fet and
1661  * update domain status
1662  */
1663 static int
1664 ppm_fetset(ppm_domain_t *domp, uint8_t value)
1665 {
1666         char    *str = "ppm_fetset";
1667         int     key;
1668         ppm_dc_t *dc;
1669         int     ret;
1670         clock_t temp;
1671         clock_t delay = 0;
1672 
1673         key = (value == PPMD_ON) ? PPMDC_FET_ON : PPMDC_FET_OFF;
1674         for (dc = domp->dc; dc; dc = dc->next)
1675                 if (dc->cmd == key)
1676                         break;
1677         if (!dc || !dc->lh) {
1678                 PPMD(D_FET, ("%s: %s domain: NULL ppm_dc handle\n",
1679                     str, domp->name))
1680                 return (DDI_FAILURE);
1681         }
1682 
1683         if (key == PPMDC_FET_ON) {
1684                 PPM_GET_IO_DELAY(dc, delay);
1685                 if (delay > 0 && domp->last_off_time > 0) {
1686                         /*
1687                          * provide any delay required before turning on.
1688                          * some devices e.g. Samsung DVD require minimum
1689                          * of 1 sec between OFF->ON. no delay is required
1690                          * for the first time.
1691                          */
1692                         temp = ddi_get_lbolt();
1693                         temp -= domp->last_off_time;
1694                         temp = drv_hztousec(temp);
1695 
1696                         if (temp < delay) {
1697                                 /*
1698                                  * busy wait untill we meet the
1699                                  * required delay. Since we maintain
1700                                  * time stamps in terms of clock ticks
1701                                  * we might wait for longer than required
1702                                  */
1703                                 PPMD(D_FET, ("%s : waiting %lu micro seconds "
1704                                     "before on\n", domp->name,
1705                                     delay - temp));
1706                                 drv_usecwait(delay - temp);
1707                         }
1708                 }
1709         }
1710         switch (dc->method) {
1711 #ifdef sun4u
1712         case PPMDC_I2CKIO: {
1713                 i2c_gpio_t i2c_req;
1714                 i2c_req.reg_mask = dc->m_un.i2c.mask;
1715                 i2c_req.reg_val = dc->m_un.i2c.val;
1716                 ret = ldi_ioctl(dc->lh, dc->m_un.i2c.iowr,
1717                     (intptr_t)&i2c_req, FWRITE | FKIOCTL, kcred, NULL);
1718                 break;
1719         }
1720 #endif
1721 
1722         case PPMDC_KIO:
1723                 ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr,
1724                     (intptr_t)&(dc->m_un.kio.val), FWRITE | FKIOCTL, kcred,
1725                     NULL);
1726                 break;
1727 
1728         default:
1729                 PPMD(D_FET, ("\t%s: unsupported domain control method %d\n",
1730                     str, domp->dc->method))
1731                 return (DDI_FAILURE);
1732         }
1733 
1734         PPMD(D_FET, ("%s: %s domain(%s) FET from %s to %s\n", str,
1735             (ret == 0) ? "turned" : "failed to turn",
1736             domp->name,
1737             (domp->status == PPMD_ON) ? "ON" : "OFF",
1738             (value == PPMD_ON) ? "ON" : "OFF"))
1739 
1740         if (ret == DDI_SUCCESS) {
1741                 domp->status = value;
1742 
1743                 if (key == PPMDC_FET_OFF)
1744                         /*
1745                          * record the time, when it is off. time is recorded
1746                          * in clock ticks
1747                          */
1748                         domp->last_off_time = ddi_get_lbolt();
1749 
1750                 /* implement any post op delay. */
1751                 if (key == PPMDC_FET_ON) {
1752                         PPM_GET_IO_POST_DELAY(dc, delay);
1753                         PPMD(D_FET, ("%s : waiting %lu micro seconds "
1754                             "after on\n", domp->name, delay))
1755                         if (delay > 0)
1756                                 drv_usecwait(delay);
1757                 }
1758         }
1759 
1760         return (ret);
1761 }
1762 
1763 
1764 /*
1765  * read power fet status
1766  */
1767 static int
1768 ppm_fetget(ppm_domain_t *domp, uint8_t *lvl)
1769 {
1770         char    *str = "ppm_fetget";
1771         ppm_dc_t *dc = domp->dc;
1772         uint_t  kio_val;
1773         int     off_val;
1774         int     ret;
1775 
1776         if (!dc->lh) {
1777                 PPMD(D_FET, ("%s: %s domain NULL ppm_dc layered handle\n",
1778                     str, domp->name))
1779                 return (DDI_FAILURE);
1780         }
1781         if (!dc->next) {
1782                 cmn_err(CE_WARN, "%s: expect both fet on and fet off ops "
1783                     "defined, found only one in domain(%s)", str, domp->name);
1784                 return (DDI_FAILURE);
1785         }
1786 
1787         switch (dc->method) {
1788 #ifdef sun4u
1789         case PPMDC_I2CKIO: {
1790                 i2c_gpio_t i2c_req;
1791                 i2c_req.reg_mask = dc->m_un.i2c.mask;
1792                 ret = ldi_ioctl(dc->lh, dc->m_un.i2c.iord,
1793                     (intptr_t)&i2c_req, FWRITE | FKIOCTL, kcred, NULL);
1794 
1795                 if (ret) {
1796                         PPMD(D_FET, ("%s: PPMDC_I2CKIO failed: ret(%d)\n",
1797                             str, ret))
1798                         return (ret);
1799                 }
1800 
1801                 off_val = (dc->cmd == PPMDC_FET_OFF) ? dc->m_un.i2c.val :
1802                     dc->next->m_un.i2c.val;
1803                 *lvl = (i2c_req.reg_val == off_val) ? PPMD_OFF : PPMD_ON;
1804 
1805                 PPMD(D_FET, ("%s: %s domain FET %s\n", str, domp->name,
1806                     (i2c_req.reg_val == off_val) ? "OFF" : "ON"))
1807 
1808                 break;
1809         }
1810 #endif
1811 
1812         case PPMDC_KIO:
1813                 ret = ldi_ioctl(dc->lh, dc->m_un.kio.iord,
1814                     (intptr_t)&kio_val, FWRITE | FKIOCTL, kcred, NULL);
1815                 if (ret) {
1816                         PPMD(D_FET, ("%s: PPMDC_KIO failed: ret(%d)\n",
1817                             str, ret))
1818                         return (ret);
1819                 }
1820 
1821                 off_val = (dc->cmd == PPMDC_FET_OFF) ? dc->m_un.kio.val :
1822                     dc->next->m_un.kio.val;
1823                 *lvl = (kio_val == off_val) ? PPMD_OFF : PPMD_ON;
1824 
1825                 PPMD(D_FET, ("%s: %s domain FET %s\n", str, domp->name,
1826                     (kio_val == off_val) ? "OFF" : "ON"))
1827 
1828                 break;
1829 
1830         default:
1831                 PPMD(D_FET, ("%s: unsupported domain control method %d\n",
1832                     str, domp->dc->method))
1833                 return (DDI_FAILURE);
1834         }
1835 
1836         return (DDI_SUCCESS);
1837 }
1838 
1839 
1840 /*
1841  * the actual code that switches pci clock and update domain status
1842  */
1843 static int
1844 ppm_switch_clock(ppm_domain_t *domp, int onoff)
1845 {
1846 #ifdef DEBUG
1847         char *str = "ppm_switch_clock";
1848 #endif
1849         int     cmd, pio_save;
1850         ppm_dc_t *dc;
1851         int ret;
1852         extern int do_polled_io;
1853         extern uint_t cfb_inuse;
1854         ppm_dev_t       *pdev;
1855 
1856         cmd = (onoff == PPMD_ON) ? PPMDC_CLK_ON : PPMDC_CLK_OFF;
1857         dc = ppm_lookup_dc(domp, cmd);
1858         if (!dc) {
1859                 PPMD(D_PCI, ("%s: no ppm_dc found for domain (%s)\n",
1860                     str, domp->name))
1861                 return (DDI_FAILURE);
1862         }
1863 
1864         switch (dc->method) {
1865         case PPMDC_KIO:
1866                 /*
1867                  * If we're powering up cfb on a Stop-A, we only
1868                  * want to do polled i/o to turn ON the clock
1869                  */
1870                 pio_save = do_polled_io;
1871                 if ((cfb_inuse) && (cmd == PPMDC_CLK_ON)) {
1872                         for (pdev = domp->devlist; pdev; pdev = pdev->next) {
1873                                 if (pm_is_cfb(pdev->dip)) {
1874                                         do_polled_io = 1;
1875                                         break;
1876                                 }
1877                         }
1878                 }
1879 
1880                 ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr,
1881                     (intptr_t)&(dc->m_un.kio.val), FWRITE | FKIOCTL,
1882                     kcred, NULL);
1883 
1884                 do_polled_io = pio_save;
1885 
1886                 if (ret == 0) {
1887                         if (cmd == PPMDC_CLK_ON) {
1888                                 domp->status = PPMD_ON;
1889 
1890                                 /*
1891                                  * PCI PM spec requires 50ms delay
1892                                  */
1893                                 drv_usecwait(50000);
1894                         } else
1895                                 domp->status = PPMD_OFF;
1896                 }
1897 
1898                 PPMD(D_PCI, ("%s: %s pci clock %s for domain (%s)\n", str,
1899                     (ret == 0) ? "turned" : "failed to turn",
1900                     (cmd == PPMDC_CLK_OFF) ? "OFF" : "ON",
1901                     domp->name))
1902 
1903                 break;
1904 
1905         default:
1906                 PPMD(D_PCI, ("%s: unsupported domain control method %d\n",
1907                     str, dc->method))
1908                 return (DDI_FAILURE);
1909         }
1910 
1911         return (DDI_SUCCESS);
1912 }
1913 
1914 
1915 /*
1916  * pci slot domain is formed of pci device(s) reside in a pci slot.
1917  * This function monitors domain device's power level change, such
1918  * that,
1919  *   when all domain power count has gone to 0, it attempts to turn off
1920  *        the pci slot's clock;
1921  *   if any domain device is powering up, it'll turn on the pci slot's
1922  *        clock as the first thing.
1923  */
1924 /* ARGUSED */
1925 static int
1926 ppm_manage_pci(dev_info_t *dip, power_req_t *reqp, int *result)
1927 {
1928 #ifdef DEBUG
1929         char *str = "ppm_manage_pci";
1930 #endif
1931         int (*pwr_func)(ppm_dev_t *, int, int);
1932         int old, new, cmpt;
1933         ppm_dev_t *ppmd;
1934         ppm_domain_t *domp;
1935         int incr = 0;
1936         int dummy_ret;
1937 
1938         *result = DDI_SUCCESS;
1939         switch (reqp->request_type) {
1940         case PMR_PPM_SET_POWER:
1941                 pwr_func = ppm_change_power_level;
1942                 old = reqp->req.ppm_set_power_req.old_level;
1943                 new = reqp->req.ppm_set_power_req.new_level;
1944                 cmpt = reqp->req.ppm_set_power_req.cmpt;
1945                 break;
1946 
1947         case PMR_PPM_POWER_CHANGE_NOTIFY:
1948                 pwr_func = ppm_record_level_change;
1949                 old = reqp->req.ppm_notify_level_req.old_level;
1950                 new = reqp->req.ppm_notify_level_req.new_level;
1951                 cmpt = reqp->req.ppm_notify_level_req.cmpt;
1952                 break;
1953 
1954         default:
1955                 *result = DDI_FAILURE;
1956                 return (DDI_FAILURE);
1957         }
1958 
1959         for (ppmd = PPM_GET_PRIVATE(dip); ppmd; ppmd = ppmd->next)
1960                 if (cmpt == ppmd->cmpt)
1961                         break;
1962         if (!ppmd) {
1963                 PPMD(D_PCI, ("%s: dip(%p): old(%d), new(%d): no ppm_dev"
1964                     " found for cmpt(%d)", str, (void *)dip, old, new, cmpt))
1965                 *result = DDI_FAILURE;
1966                 return (DDI_FAILURE);
1967         }
1968         domp = ppmd->domp;
1969         PPMD(D_PCI, ("%s: %s, dev(%s), c%d, old %d, new %d\n", str,
1970             ppm_get_ctlstr(reqp->request_type, ~0),
1971             ppmd->path, cmpt, old, new))
1972 
1973         ASSERT(old == ppmd->level);
1974         if (new == ppmd->level)
1975                 return (DDI_SUCCESS);
1976 
1977         PPM_LOCK_DOMAIN(domp);
1978 
1979         if (new > 0) {               /* device powering up */
1980                 if (domp->status == PPMD_OFF) {
1981 
1982                         /* cannot be off during (chpt, resume) window */
1983                         ASSERT(ppm_cpr_window_flag == B_FALSE);
1984 
1985                         /* either both OFF or both ON */
1986                         ASSERT(!((old == 0) ^ (domp->pwr_cnt == 0)));
1987 
1988                         PPMD(D_PCI, ("About to turn clock on for %s@%s c%d\n",
1989                             PM_NAME(dip), PM_ADDR(dip), cmpt))
1990 
1991                         *result = ppm_switch_clock(domp, PPMD_ON);
1992                         if (*result != DDI_SUCCESS) {
1993                                 PPMD(D_PCI, ("\tcan't switch on pci clock: "
1994                                     "ret(%d)\n", *result))
1995                                 PPM_UNLOCK_DOMAIN(domp);
1996                                 return (DDI_FAILURE);
1997                         }
1998                 }
1999 
2000                 if (old == 0) {
2001                         domp->pwr_cnt++;
2002                         incr = 1;
2003                 }
2004 
2005                 PPMD(D_PCI, ("\t%s domain power count: %d\n",
2006                     domp->name, domp->pwr_cnt))
2007         }
2008 
2009         PPM_UNLOCK_DOMAIN(domp);
2010 
2011         ASSERT(domp->pwr_cnt > 0);
2012 
2013         if ((*result = (*pwr_func)(ppmd, cmpt, new)) != DDI_SUCCESS) {
2014                 PPMD(D_PCI, ("\t%s power change failed: ret(%d)\n",
2015                     ppmd->path, *result))
2016         }
2017 
2018         PPM_LOCK_DOMAIN(domp);
2019 
2020         /*
2021          * Decr the power count in two cases:
2022          *
2023          *   1) request was to power device down and was successful
2024          *   2) request was to power up (we pre-incremented count), but failed.
2025          */
2026         if ((*result == DDI_SUCCESS && ppmd->level == 0) ||
2027             (*result != DDI_SUCCESS && incr)) {
2028                 ASSERT(domp->pwr_cnt > 0);
2029                 domp->pwr_cnt--;
2030         }
2031 
2032         PPMD(D_PCI, ("\t%s domain power count: %d\n",
2033             domp->name, domp->pwr_cnt))
2034 
2035         /*
2036          * call to pwr_func will update ppm data structures, if it
2037          * succeeds. ppm should return whatever is the return value
2038          * from call to pwr_func. This way pm and ppm data structures
2039          * always in sync. Use dummy_ret from here for any further
2040          * return values.
2041          */
2042         if ((domp->pwr_cnt == 0) &&
2043             (ppm_cpr_window_flag == B_FALSE) &&
2044             ppm_none_else_holds_power(domp)) {
2045 
2046                 PPMD(D_PCI, ("About to turn clock off for %s@%s c%d\n",
2047                     PM_NAME(dip), PM_ADDR(dip), cmpt))
2048 
2049                 dummy_ret = ppm_switch_clock(domp, PPMD_OFF);
2050                 if (dummy_ret != DDI_SUCCESS) {
2051                         PPMD(D_PCI, ("\tCan't switch clock off: "
2052                             "ret(%d)\n", dummy_ret))
2053                 }
2054         }
2055 
2056         PPM_UNLOCK_DOMAIN(domp);
2057         ASSERT(domp->pwr_cnt >= 0);
2058         return (*result);
2059 }
2060 
2061 /*
2062  * When the driver for the primary PCI-Express child has set the device to
2063  * lowest power (D3hot), we come here to save even more power by transitioning
2064  * the slot to D3cold.  Similarly, if the slot is in D3cold and we need to
2065  * power up the child, we come here first to power up the slot.
2066  */
2067 /* ARGUSED */
2068 static int
2069 ppm_manage_pcie(dev_info_t *dip, power_req_t *reqp, int *result)
2070 {
2071 #ifdef DEBUG
2072         char *str = "ppm_manage_pcie";
2073 #endif
2074         int (*pwr_func)(ppm_dev_t *, int, int);
2075         int old, new, cmpt;
2076         ppm_dev_t *ppmd;
2077         ppm_domain_t *domp;
2078         int incr = 0;
2079         int dummy_ret;
2080 
2081         *result = DDI_SUCCESS;
2082         switch (reqp->request_type) {
2083         case PMR_PPM_SET_POWER:
2084                 pwr_func = ppm_change_power_level;
2085                 old = reqp->req.ppm_set_power_req.old_level;
2086                 new = reqp->req.ppm_set_power_req.new_level;
2087                 cmpt = reqp->req.ppm_set_power_req.cmpt;
2088                 break;
2089 
2090         case PMR_PPM_POWER_CHANGE_NOTIFY:
2091                 pwr_func = ppm_record_level_change;
2092                 old = reqp->req.ppm_notify_level_req.old_level;
2093                 new = reqp->req.ppm_notify_level_req.new_level;
2094                 cmpt = reqp->req.ppm_notify_level_req.cmpt;
2095                 break;
2096 
2097         default:
2098                 *result = DDI_FAILURE;
2099                 return (DDI_FAILURE);
2100         }
2101 
2102         for (ppmd = PPM_GET_PRIVATE(dip); ppmd; ppmd = ppmd->next)
2103                 if (cmpt == ppmd->cmpt)
2104                         break;
2105         if (!ppmd) {
2106                 PPMD(D_PCI, ("%s: dip(%p): old(%d), new(%d): no ppm_dev"
2107                     " found for cmpt(%d)", str, (void *)dip, old, new, cmpt))
2108                 *result = DDI_FAILURE;
2109                 return (DDI_FAILURE);
2110         }
2111         domp = ppmd->domp;
2112         PPMD(D_PCI, ("%s: %s, dev(%s), c%d, old %d, new %d\n", str,
2113             ppm_get_ctlstr(reqp->request_type, ~0),
2114             ppmd->path, cmpt, old, new))
2115 
2116         ASSERT(old == ppmd->level);
2117         if (new == ppmd->level)
2118                 return (DDI_SUCCESS);
2119 
2120         PPM_LOCK_DOMAIN(domp);
2121 
2122         if (new > 0) {               /* device powering up */
2123                 if (domp->status == PPMD_OFF) {
2124 
2125                         /* cannot be off during (chpt, resume) window */
2126                         ASSERT(ppm_cpr_window_flag == B_FALSE);
2127 
2128                         /* either both OFF or both ON */
2129                         ASSERT(!((old == 0) ^ (domp->pwr_cnt == 0)));
2130 
2131                         PPMD(D_PCI, ("About to turn on pcie slot for "
2132                             "%s@%s c%d\n", PM_NAME(dip), PM_ADDR(dip), cmpt))
2133 
2134                         *result = ppm_pcie_pwr(domp, PPMD_ON);
2135                         if (*result != DDI_SUCCESS) {
2136                                 PPMD(D_PCI, ("\tcan't switch on pcie slot: "
2137                                     "ret(%d)\n", *result))
2138                                 PPM_UNLOCK_DOMAIN(domp);
2139                                 return (DDI_FAILURE);
2140                         }
2141                 }
2142 
2143                 if (old == 0) {
2144                         domp->pwr_cnt++;
2145                         incr = 1;
2146                 }
2147 
2148                 PPMD(D_PCI, ("\t%s domain power count: %d\n",
2149                     domp->name, domp->pwr_cnt))
2150         }
2151 
2152         PPM_UNLOCK_DOMAIN(domp);
2153 
2154         ASSERT(domp->pwr_cnt > 0);
2155 
2156         if ((*result = (*pwr_func)(ppmd, cmpt, new)) != DDI_SUCCESS) {
2157                 PPMD(D_PCI, ("\t%s power change failed: ret(%d)\n",
2158                     ppmd->path, *result))
2159         }
2160 
2161         PPM_LOCK_DOMAIN(domp);
2162 
2163         /*
2164          * Decr the power count in two cases:
2165          *
2166          *   1) request was to power device down and was successful
2167          *   2) request was to power up (we pre-incremented count), but failed.
2168          */
2169         if ((*result == DDI_SUCCESS && ppmd->level == 0) ||
2170             (*result != DDI_SUCCESS && incr)) {
2171                 ASSERT(domp->pwr_cnt > 0);
2172                 domp->pwr_cnt--;
2173         }
2174 
2175         PPMD(D_PCI, ("\t%s domain power count: %d\n",
2176             domp->name, domp->pwr_cnt))
2177 
2178         /*
2179          * call to pwr_func will update ppm data structures, if it
2180          * succeeds. ppm should return whatever is the return value
2181          * from call to pwr_func. This way pm and ppm data structures
2182          * always in sync. Use dummy_ret from here for any further
2183          * return values.
2184          */
2185         if ((domp->pwr_cnt == 0) &&
2186             (ppm_cpr_window_flag == B_FALSE) &&
2187             ppm_none_else_holds_power(domp)) {
2188 
2189                 PPMD(D_PCI, ("About to turn off pcie slot for %s@%s c%d\n",
2190                     PM_NAME(dip), PM_ADDR(dip), cmpt))
2191 
2192                 dummy_ret = ppm_pcie_pwr(domp, PPMD_OFF);
2193                 if (dummy_ret != DDI_SUCCESS) {
2194                         PPMD(D_PCI, ("\tCan't switch pcie slot off: "
2195                             "ret(%d)\n", dummy_ret))
2196                 }
2197         }
2198 
2199         PPM_UNLOCK_DOMAIN(domp);
2200         ASSERT(domp->pwr_cnt >= 0);
2201         return (*result);
2202 
2203 }
2204 
2205 /*
2206  * Set or clear a bit on a GPIO device.  These bits are used for various device-
2207  * specific purposes.
2208  */
2209 static int
2210 ppm_gpioset(ppm_domain_t *domp, int key)
2211 {
2212 #ifdef DEBUG
2213         char    *str = "ppm_gpioset";
2214 #endif
2215         ppm_dc_t *dc;
2216         int     ret;
2217         clock_t delay = 0;
2218 
2219         for (dc = domp->dc; dc; dc = dc->next)
2220                 if (dc->cmd == key)
2221                         break;
2222         if (!dc || !dc->lh) {
2223                 PPMD(D_GPIO, ("%s: %s domain: NULL ppm_dc handle\n",
2224                     str, domp->name))
2225                 return (DDI_FAILURE);
2226         }
2227 
2228         PPM_GET_IO_DELAY(dc, delay);
2229         if (delay > 0) {
2230                 PPMD(D_GPIO, ("%s : waiting %lu micro seconds "
2231                     "before change\n", domp->name, delay))
2232                 drv_usecwait(delay);
2233         }
2234 
2235         switch (dc->method) {
2236 #ifdef sun4u
2237         case PPMDC_I2CKIO: {
2238                 i2c_gpio_t i2c_req;
2239                 ppm_dev_t *pdev;
2240                 int pio_save;
2241                 extern int do_polled_io;
2242                 extern uint_t cfb_inuse;
2243                 i2c_req.reg_mask = dc->m_un.i2c.mask;
2244                 i2c_req.reg_val = dc->m_un.i2c.val;
2245 
2246                 pio_save = do_polled_io;
2247                 if (cfb_inuse) {
2248                         for (pdev = domp->devlist; pdev; pdev = pdev->next) {
2249                                 if (pm_is_cfb(pdev->dip)) {
2250                                         do_polled_io = 1;
2251                                         PPMD(D_GPIO, ("%s: cfb is in use, "
2252                                             "i2c transaction is done in "
2253                                             "poll-mode.\n", str))
2254                                         break;
2255                                 }
2256                         }
2257                 }
2258                 ret = ldi_ioctl(dc->lh, dc->m_un.i2c.iowr,
2259                     (intptr_t)&i2c_req, FWRITE | FKIOCTL, kcred, NULL);
2260                 do_polled_io = pio_save;
2261 
2262                 PPMD(D_GPIO, ("%s: %s domain(%s) from %s by writing %x "
2263                     "to gpio\n",
2264                     str, (ret == 0) ? "turned" : "FAILed to turn",
2265                     domp->name,
2266                     (domp->status == PPMD_ON) ? "ON" : "OFF",
2267                     dc->m_un.i2c.val))
2268 
2269                 break;
2270         }
2271 #endif
2272 
2273         case PPMDC_KIO:
2274                 ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr,
2275                     (intptr_t)&(dc->m_un.kio.val), FWRITE | FKIOCTL, kcred,
2276                     NULL);
2277 
2278                 PPMD(D_GPIO, ("%s: %s domain(%s) from %s by writing %x "
2279                     "to gpio\n",
2280                     str, (ret == 0) ? "turned" : "FAILed to turn",
2281                     domp->name,
2282                     (domp->status == PPMD_ON) ? "ON" : "OFF",
2283                     dc->m_un.kio.val))
2284 
2285                 break;
2286 
2287         default:
2288                 PPMD(D_GPIO, ("\t%s: unsupported domain control method %d\n",
2289                     str, domp->dc->method))
2290                 return (DDI_FAILURE);
2291         }
2292 
2293         /* implement any post op delay. */
2294         PPM_GET_IO_POST_DELAY(dc, delay);
2295         if (delay > 0) {
2296                 PPMD(D_GPIO, ("%s : waiting %lu micro seconds "
2297                     "after change\n", domp->name, delay))
2298                 drv_usecwait(delay);
2299         }
2300 
2301         return (ret);
2302 }
2303 
2304 static int
2305 ppm_pcie_pwr(ppm_domain_t *domp, int onoff)
2306 {
2307 #ifdef DEBUG
2308         char *str = "ppm_pcie_pwr";
2309 #endif
2310         int ret = DDI_FAILURE;
2311         ppm_dc_t *dc;
2312         clock_t delay;
2313 
2314         ASSERT(onoff == PPMD_OFF || onoff == PPMD_ON);
2315 
2316         dc = ppm_lookup_dc(domp,
2317             onoff == PPMD_ON ? PPMDC_PRE_PWR_ON : PPMDC_PRE_PWR_OFF);
2318         if (dc) {
2319 
2320                 /*
2321                  * Invoke layered ioctl for pcie root complex nexus to
2322                  * transition the link
2323                  */
2324                 ASSERT(dc->method == PPMDC_KIO);
2325                 delay = dc->m_un.kio.delay;
2326                 if (delay > 0) {
2327                         PPMD(D_GPIO, ("%s : waiting %lu micro seconds "
2328                             "before change\n", domp->name, delay))
2329                         drv_usecwait(delay);
2330                 }
2331                 ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr,
2332                     (intptr_t)&(dc->m_un.kio.val),
2333                     FWRITE | FKIOCTL, kcred, NULL);
2334                 if (ret == DDI_SUCCESS) {
2335                         delay = dc->m_un.kio.post_delay;
2336                         if (delay > 0) {
2337                                 PPMD(D_GPIO, ("%s : waiting %lu micro seconds "
2338                                     "after change\n", domp->name, delay))
2339                                 drv_usecwait(delay);
2340                         }
2341                 } else {
2342                         PPMD(D_PCI, ("%s: ldi_ioctl FAILED for domain(%s)\n",
2343                             str, domp->name))
2344                         return (ret);
2345                 }
2346         }
2347 
2348         switch (onoff) {
2349         case PPMD_OFF:
2350                 /* Turn off the clock for this slot. */
2351                 if ((ret = ppm_gpioset(domp, PPMDC_CLK_OFF)) != DDI_SUCCESS) {
2352                         PPMD(D_GPIO,
2353                             ("%s: failed to turn off domain(%s) clock\n",
2354                             str, domp->name))
2355                         return (ret);
2356                 }
2357 
2358                 /* Turn off the power to this slot */
2359                 if ((ret = ppm_gpioset(domp, PPMDC_PWR_OFF)) != DDI_SUCCESS) {
2360                         PPMD(D_GPIO,
2361                             ("%s: failed to turn off domain(%s) power\n",
2362                             str, domp->name))
2363                         return (ret);
2364                 }
2365                 break;
2366         case PPMD_ON:
2367                 /* Assert RESET for this slot. */
2368                 if ((ret = ppm_gpioset(domp, PPMDC_RESET_ON)) != DDI_SUCCESS) {
2369                         PPMD(D_GPIO,
2370                             ("%s: failed to assert reset for domain(%s)\n",
2371                             str, domp->name))
2372                         return (ret);
2373                 }
2374 
2375                 /* Turn on the power to this slot */
2376                 if ((ret = ppm_gpioset(domp, PPMDC_PWR_ON)) != DDI_SUCCESS) {
2377                         PPMD(D_GPIO,
2378                             ("%s: failed to turn on domain(%s) power\n",
2379                             str, domp->name))
2380                         return (ret);
2381                 }
2382 
2383                 /* Turn on the clock for this slot */
2384                 if ((ret = ppm_gpioset(domp, PPMDC_CLK_ON)) != DDI_SUCCESS) {
2385                         PPMD(D_GPIO,
2386                             ("%s: failed to turn on domain(%s) clock\n",
2387                             str, domp->name))
2388                         return (ret);
2389                 }
2390 
2391                 /* De-assert RESET for this slot. */
2392                 if ((ret = ppm_gpioset(domp, PPMDC_RESET_OFF)) != DDI_SUCCESS) {
2393                         PPMD(D_GPIO,
2394                             ("%s: failed to de-assert reset for domain(%s)\n",
2395                             str, domp->name))
2396                         return (ret);
2397                 }
2398 
2399                 dc = ppm_lookup_dc(domp, PPMDC_POST_PWR_ON);
2400                 if (dc) {
2401                         /*
2402                          * Invoke layered ioctl to PCIe root complex nexus
2403                          * to transition the link.
2404                          */
2405                         ASSERT(dc->method == PPMDC_KIO);
2406                         delay = dc->m_un.kio.delay;
2407                         if (delay > 0) {
2408                                 PPMD(D_GPIO, ("%s: waiting %lu micro seconds "
2409                                     "before change\n", domp->name, delay))
2410                                 drv_usecwait(delay);
2411                         }
2412                         ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr,
2413                             (intptr_t)&(dc->m_un.kio.val),
2414                             FWRITE | FKIOCTL, kcred, NULL);
2415 
2416                         if (ret != DDI_SUCCESS) {
2417                                 PPMD(D_PCI, ("%s: layered ioctl to PCIe"
2418                                     "root complex nexus FAILed\n", str))
2419                                 return (ret);
2420                         }
2421 
2422                         delay = dc->m_un.kio.post_delay;
2423                         if (delay > 0) {
2424                                 PPMD(D_GPIO, ("%s: waiting %lu micro "
2425                                     "seconds after change\n",
2426                                     domp->name, delay))
2427                                 drv_usecwait(delay);
2428                         }
2429                 }
2430                 break;
2431         default:
2432                 ASSERT(0);
2433         }
2434 
2435         PPMD(D_PCI, ("%s: turned domain(%s) PCIe slot power from %s to %s\n",
2436             str, domp->name, (domp->status == PPMD_ON) ? "ON" : "OFF",
2437             onoff == PPMD_ON ? "ON" : "OFF"))
2438 
2439         domp->status = onoff;
2440         return (ret);
2441 }
2442 
2443 
2444 /*
2445  * Change the power level for a component of a device.  If the change
2446  * arg is true, we call the framework to actually change the device's
2447  * power; otherwise, we just update our own copy of the power level.
2448  */
2449 static int
2450 ppm_set_level(ppm_dev_t *ppmd, int cmpt, int level, boolean_t change)
2451 {
2452 #ifdef DEBUG
2453         char *str = "ppm_set_level";
2454 #endif
2455         int ret;
2456 
2457         ret = DDI_SUCCESS;
2458         if (change)
2459                 ret = pm_power(ppmd->dip, cmpt, level);
2460 
2461         PPMD(D_SETLVL, ("%s: %s change=%d, old %d, new %d, ret %d\n",
2462             str, ppmd->path, change, ppmd->level, level, ret))
2463 
2464         if (ret == DDI_SUCCESS) {
2465                 ppmd->level = level;
2466                 ppmd->rplvl = PM_LEVEL_UNKNOWN;
2467         }
2468 
2469         return (ret);
2470 }
2471 
2472 
2473 static int
2474 ppm_change_power_level(ppm_dev_t *ppmd, int cmpt, int level)
2475 {
2476         return (ppm_set_level(ppmd, cmpt, level, B_TRUE));
2477 }
2478 
2479 
2480 static int
2481 ppm_record_level_change(ppm_dev_t *ppmd, int cmpt, int level)
2482 {
2483         return (ppm_set_level(ppmd, cmpt, level, B_FALSE));
2484 }
2485 
2486 
2487 static void
2488 ppm_manage_led(int action)
2489 {
2490         ppm_domain_t *domp;
2491         ppm_unit_t *unitp;
2492         timeout_id_t    tid;
2493 
2494 
2495         PPMD(D_LED, ("ppm_manage_led: action: %s\n",
2496             (action == PPM_LED_BLINKING) ? "PPM_LED_BLINKING" :
2497             "PPM_LED_SOLIDON"))
2498 
2499         /*
2500          * test whether led operation is practically supported,
2501          * if not, we waive without pressing for reasons
2502          */
2503         if (!ppm_lookup_dc(NULL, PPMDC_LED_ON))
2504                 return;
2505 
2506         unitp = ddi_get_soft_state(ppm_statep, ppm_inst);
2507         for (domp = ppm_domain_p; (domp && (domp->model != PPMD_LED)); )
2508                 domp = domp->next;
2509 
2510         mutex_enter(&unitp->lock);
2511         if (action == PPM_LED_BLINKING) {
2512                 ppm_set_led(domp, PPMD_OFF);
2513                 unitp->led_tid = timeout(
2514                     ppm_blink_led, domp, PPM_LEDOFF_INTERVAL);
2515 
2516         } else {        /* PPM_LED_SOLIDON */
2517                 ASSERT(action == PPM_LED_SOLIDON);
2518                 tid = unitp->led_tid;
2519                 unitp->led_tid = 0;
2520 
2521                 mutex_exit(&unitp->lock);
2522                 (void) untimeout(tid);
2523 
2524                 mutex_enter(&unitp->lock);
2525                 ppm_set_led(domp, PPMD_ON);
2526         }
2527         mutex_exit(&unitp->lock);
2528 }
2529 
2530 
2531 static void
2532 ppm_set_led(ppm_domain_t *domp, int val)
2533 {
2534         int ret;
2535 
2536         ret = ppm_gpioset(domp,
2537             (val == PPMD_ON) ? PPMDC_LED_ON : PPMDC_LED_OFF);
2538 
2539         PPMD(D_LED, ("ppm_set_led:  %s LED from %s\n",
2540             (ret == 0) ? "turned" : "FAILed to turn",
2541             (domp->status == PPMD_ON) ? "ON to OFF" : "OFF to ON"))
2542 
2543         if (ret == DDI_SUCCESS)
2544                 domp->status = val;
2545 }
2546 
2547 
2548 static void
2549 ppm_blink_led(void *arg)
2550 {
2551         ppm_unit_t *unitp;
2552         clock_t intvl;
2553         ppm_domain_t *domp = arg;
2554 
2555         unitp = ddi_get_soft_state(ppm_statep, ppm_inst);
2556 
2557         mutex_enter(&unitp->lock);
2558         if (unitp->led_tid == 0) {
2559                 mutex_exit(&unitp->lock);
2560                 return;
2561         }
2562 
2563         if (domp->status == PPMD_ON) {
2564                 ppm_set_led(domp, PPMD_OFF);
2565                 intvl = PPM_LEDOFF_INTERVAL;
2566         } else {
2567                 ppm_set_led(domp, PPMD_ON);
2568                 intvl = PPM_LEDON_INTERVAL;
2569         }
2570 
2571         unitp->led_tid = timeout(ppm_blink_led, domp, intvl);
2572         mutex_exit(&unitp->lock);
2573 }
2574 
2575 /*
2576  * Function to power up a domain, if required. It also increments the
2577  * domain pwr_cnt to prevent it from going down.
2578  */
2579 static int
2580 ppm_power_up_domain(dev_info_t *dip)
2581 {
2582         int             ret = DDI_SUCCESS;
2583         ppm_domain_t    *domp;
2584         char            *str = "ppm_power_up_domain";
2585 
2586         domp = ppm_lookup_dev(dip);
2587         ASSERT(domp);
2588         mutex_enter(&domp->lock);
2589         switch (domp->model) {
2590         case PPMD_FET:
2591                 if (domp->status == PPMD_OFF) {
2592                         if ((ret = ppm_fetset(domp,  PPMD_ON)) ==
2593                             DDI_SUCCESS) {
2594                                 PPMD(D_FET, ("%s: turned on fet for %s@%s\n",
2595                                     str, PM_NAME(dip), PM_ADDR(dip)))
2596                         } else {
2597                                 PPMD(D_FET, ("%s: couldn't turn on fet "
2598                                     "for %s@%s\n", str, PM_NAME(dip),
2599                                     PM_ADDR(dip)))
2600                         }
2601                 }
2602                 break;
2603 
2604         case PPMD_PCI:
2605         case PPMD_PCI_PROP:
2606                 if (domp->status == PPMD_OFF) {
2607                         if ((ret = ppm_switch_clock(domp, PPMD_ON)) ==
2608                             DDI_SUCCESS) {
2609                                 PPMD(D_PCI, ("%s: turned on clock for "
2610                                     "%s@%s\n", str, PM_NAME(dip),
2611                                     PM_ADDR(dip)))
2612                         } else {
2613                                 PPMD(D_PCI, ("%s: couldn't turn on clock "
2614                                     "for %s@%s\n", str, PM_NAME(dip),
2615                                     PM_ADDR(dip)))
2616                         }
2617                 }
2618                 break;
2619 
2620         case PPMD_PCIE:
2621                 if (domp->status == PPMD_OFF) {
2622                         if ((ret = ppm_pcie_pwr(domp, PPMD_ON)) ==
2623                             DDI_SUCCESS) {
2624                                 PPMD(D_PCI, ("%s: turned on link for "
2625                                     "%s@%s\n", str, PM_NAME(dip),
2626                                     PM_ADDR(dip)))
2627                         } else {
2628                                 PPMD(D_PCI, ("%s: couldn't turn on link "
2629                                     "for %s@%s\n", str, PM_NAME(dip),
2630                                     PM_ADDR(dip)))
2631                         }
2632                 }
2633                 break;
2634 
2635         default:
2636                 break;
2637         }
2638         if (ret == DDI_SUCCESS)
2639                 domp->pwr_cnt++;
2640         mutex_exit(&domp->lock);
2641         return (ret);
2642 }
2643 
2644 /*
2645  * Decrements the domain pwr_cnt. if conditions to power down the domain
2646  * are met, powers down the domain,.
2647  */
2648 static int
2649 ppm_power_down_domain(dev_info_t *dip)
2650 {
2651         int             ret = DDI_SUCCESS;
2652         char            *str = "ppm_power_down_domain";
2653         ppm_domain_t    *domp;
2654 
2655         domp = ppm_lookup_dev(dip);
2656         ASSERT(domp);
2657         mutex_enter(&domp->lock);
2658         ASSERT(domp->pwr_cnt > 0);
2659         domp->pwr_cnt--;
2660         switch (domp->model) {
2661         case PPMD_FET:
2662                 if ((domp->pwr_cnt == 0) &&
2663                     (ppm_cpr_window_flag == B_FALSE) &&
2664                     ppm_none_else_holds_power(domp)) {
2665                         if ((ret = ppm_fetset(domp, PPMD_OFF)) ==
2666                             DDI_SUCCESS) {
2667                                 PPMD(D_FET, ("%s: turned off FET for %s@%s \n",
2668                                     str, PM_NAME(dip), PM_ADDR(dip)))
2669                         } else {
2670                                 PPMD(D_FET, ("%s: couldn't turn off FET for "
2671                                     " %s@%s\n", str, PM_NAME(dip),
2672                                     PM_ADDR(dip)))
2673                         }
2674                 }
2675                 break;
2676 
2677         case PPMD_PCI:
2678         case PPMD_PCI_PROP:
2679                 if ((domp->pwr_cnt == 0) &&
2680                     (ppm_cpr_window_flag == B_FALSE) &&
2681                     ppm_none_else_holds_power(domp)) {
2682                         if ((ret = ppm_switch_clock(domp, PPMD_OFF)) ==
2683                             DDI_SUCCESS) {
2684                                 PPMD(D_PCI, ("%s: turned off clock for %s@%s\n",
2685                                     str, PM_NAME(dip), PM_ADDR(dip)))
2686                         } else {
2687                                 PPMD(D_PCI, ("%s: couldn't turn off clock "
2688                                     "for %s@%s\n", str, PM_NAME(dip),
2689                                     PM_ADDR(dip)))
2690                         }
2691                 }
2692                 break;
2693 
2694         case PPMD_PCIE:
2695                 if ((domp->pwr_cnt == 0) &&
2696                     (ppm_cpr_window_flag == B_FALSE) &&
2697                     ppm_none_else_holds_power(domp)) {
2698                         if ((ret = ppm_pcie_pwr(domp, PPMD_OFF)) ==
2699                             DDI_SUCCESS) {
2700                                 PPMD(D_PCI, ("%s: turned off link for %s@%s\n",
2701                                     str, PM_NAME(dip), PM_ADDR(dip)))
2702                         } else {
2703                                 PPMD(D_PCI, ("%s: couldn't turn off link "
2704                                     "for %s@%s\n", str, PM_NAME(dip),
2705                                     PM_ADDR(dip)))
2706                         }
2707                 }
2708                 break;
2709 
2710         default:
2711                 break;
2712         }
2713         mutex_exit(&domp->lock);
2714         return (ret);
2715 }
2716 
2717 static int
2718 ppm_manage_sx(s3a_t *s3ap, int enter)
2719 {
2720         ppm_domain_t *domp = ppm_lookup_domain("domain_estar");
2721         ppm_dc_t *dc;
2722         int ret = 0;
2723 
2724         if (domp == NULL) {
2725                 PPMD(D_CPR, ("ppm_manage_sx: can't find estar domain\n"))
2726                 return (ENODEV);
2727         }
2728         PPMD(D_CPR, ("ppm_manage_sx %x, enter %d\n", s3ap->s3a_state,
2729             enter))
2730         switch (s3ap->s3a_state) {
2731         case S3:
2732                 if (enter) {
2733                         dc = ppm_lookup_dc(domp, PPMDC_ENTER_S3);
2734                 } else {
2735                         dc = ppm_lookup_dc(domp, PPMDC_EXIT_S3);
2736                 }
2737                 ASSERT(dc && dc->method == PPMDC_KIO);
2738                 PPMD(D_CPR,
2739                     ("ppm_manage_sx: calling acpi driver (handle %p)"
2740                     " with %x\n", (void *)dc->lh, dc->m_un.kio.iowr))
2741                 ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr,
2742                     (intptr_t)s3ap, FWRITE | FKIOCTL, kcred, NULL);
2743                 break;
2744 
2745         case S4:
2746                 /* S4 is not supported yet */
2747                 return (EINVAL);
2748         default:
2749                 ASSERT(0);
2750         }
2751         return (ret);
2752 }
2753 
2754 /*
2755  * Search enable/disable lists, which are encoded in ppm.conf as an array
2756  * of char strings.
2757  */
2758 static int
2759 ppm_search_list(pm_searchargs_t *sl)
2760 {
2761         int i;
2762         int flags = DDI_PROP_DONTPASS;
2763         ppm_unit_t *unitp = ddi_get_soft_state(ppm_statep, ppm_inst);
2764         char **pp;
2765         char *starp;
2766         uint_t nelements;
2767         char *manuf = sl->pms_manufacturer;
2768         char *prod = sl->pms_product;
2769 
2770         if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, unitp->dip, flags,
2771             sl->pms_listname, &pp, &nelements) != DDI_PROP_SUCCESS) {
2772                 PPMD(D_CPR, ("ppm_search_list prop lookup %s failed--EINVAL\n",
2773                     sl->pms_listname))
2774                 return (EINVAL);
2775         }
2776         ASSERT((nelements & 1) == 0);               /* must be even */
2777 
2778         PPMD(D_CPR, ("ppm_search_list looking for %s, %s\n", manuf, prod))
2779 
2780         for (i = 0; i < nelements; i += 2) {
2781                 PPMD(D_CPR, ("checking %s, %s", pp[i], pp[i+1]))
2782                 /* we support only a trailing '*' pattern match */
2783                 if ((starp = strchr(pp[i], '*')) != NULL && *(starp + 1) == 0) {
2784                         /* LINTED - ptrdiff overflow */
2785                         if (strncmp(manuf, pp[i], (starp - pp[i])) != 0) {
2786                                 PPMD(D_CPR, (" no match %s with %s\n",
2787                                     manuf, pp[i + 1]))
2788                                 continue;
2789                         }
2790                 }
2791                 if ((starp = strchr(pp[i + 1], '*')) != NULL &&
2792                     *(starp + 1) == 0) {
2793                         if (strncmp(prod,
2794                             /* LINTED - ptrdiff overflow */
2795                             pp[i + 1], (starp - pp[i + 1])) != 0) {
2796                                 PPMD(D_CPR, (" no match %s with %s\n",
2797                                     prod, pp[i + 1]))
2798                                 continue;
2799                         }
2800                 }
2801                 if (strcmp(manuf, pp[i]) == 0 &&
2802                     (strcmp(prod, pp[i + 1]) == 0)) {
2803                         PPMD(D_CPR, (" match\n"))
2804                         ddi_prop_free(pp);
2805                         return (0);
2806                 }
2807                 PPMD(D_CPR, (" no match %s with %s or %s with %s\n",
2808                     manuf, pp[i], prod, pp[i + 1]))
2809         }
2810         ddi_prop_free(pp);
2811         return (ENODEV);
2812 }