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