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