1 /* $OpenBSD: pchgpio.c,v 1.9 2021/12/07 18:06:08 kettenis Exp $ */ 2 /* 3 * Copyright (c) 2020 Mark Kettenis 4 * Copyright (c) 2020 James Hastings 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/param.h> 20 #include <sys/malloc.h> 21 #include <sys/systm.h> 22 23 #include <dev/acpi/acpireg.h> 24 #include <dev/acpi/acpivar.h> 25 #include <dev/acpi/acpidev.h> 26 #include <dev/acpi/amltypes.h> 27 #include <dev/acpi/dsdt.h> 28 29 #define PCHGPIO_MAXCOM 5 30 31 #define PCHGPIO_CONF_TXSTATE 0x00000001 32 #define PCHGPIO_CONF_RXSTATE 0x00000002 33 #define PCHGPIO_CONF_RXINV 0x00800000 34 #define PCHGPIO_CONF_RXEV_EDGE 0x02000000 35 #define PCHGPIO_CONF_RXEV_ZERO 0x04000000 36 #define PCHGPIO_CONF_RXEV_MASK 0x06000000 37 #define PCHGPIO_CONF_PADRSTCFG_MASK 0xc0000000 38 39 #define PCHGPIO_PADBAR 0x00c 40 41 struct pchgpio_group { 42 uint8_t bar; 43 uint8_t bank; 44 uint16_t base; 45 uint16_t limit; 46 int16_t gpiobase; 47 }; 48 49 struct pchgpio_device { 50 uint16_t pad_size; 51 uint16_t gpi_is; 52 uint16_t gpi_ie; 53 const struct pchgpio_group *groups; 54 int ngroups; 55 int npins; 56 }; 57 58 struct pchgpio_match { 59 const char *hid; 60 const struct pchgpio_device *device; 61 }; 62 63 struct pchgpio_pincfg { 64 uint32_t pad_cfg_dw0; 65 uint32_t pad_cfg_dw1; 66 int gpi_ie; 67 }; 68 69 struct pchgpio_intrhand { 70 int (*ih_func)(void *); 71 void *ih_arg; 72 }; 73 74 struct pchgpio_softc { 75 struct device sc_dev; 76 struct acpi_softc *sc_acpi; 77 struct aml_node *sc_node; 78 79 bus_space_tag_t sc_memt[PCHGPIO_MAXCOM]; 80 bus_space_handle_t sc_memh[PCHGPIO_MAXCOM]; 81 void *sc_ih; 82 int sc_naddr; 83 84 const struct pchgpio_device *sc_device; 85 uint16_t sc_padbar[PCHGPIO_MAXCOM]; 86 uint16_t sc_padbase[PCHGPIO_MAXCOM]; 87 int sc_padsize; 88 89 int sc_npins; 90 struct pchgpio_pincfg *sc_pin_cfg; 91 struct pchgpio_intrhand *sc_pin_ih; 92 93 struct acpi_gpio sc_gpio; 94 }; 95 96 int pchgpio_match(struct device *, void *, void *); 97 void pchgpio_attach(struct device *, struct device *, void *); 98 int pchgpio_activate(struct device *, int); 99 100 struct cfattach pchgpio_ca = { 101 sizeof(struct pchgpio_softc), pchgpio_match, pchgpio_attach, 102 NULL, pchgpio_activate 103 }; 104 105 struct cfdriver pchgpio_cd = { 106 NULL, "pchgpio", DV_DULL 107 }; 108 109 const char *pchgpio_hids[] = { 110 "INT3450", 111 "INT34BB", 112 "INT34C5", 113 "INT34C6", 114 NULL 115 }; 116 117 const struct pchgpio_group cnl_h_groups[] = 118 { 119 /* Community 0 */ 120 { 0, 0, 0, 24, 0 }, /* GPP_A */ 121 { 0, 1, 25, 50, 32 }, /* GPP_B */ 122 123 /* Community 1 */ 124 { 1, 0, 51, 74, 64 }, /* GPP_C */ 125 { 1, 1, 75, 98, 96 }, /* GPP_D */ 126 { 1, 2, 99, 106, 128 }, /* GPP_G */ 127 128 /* Community 3 */ 129 { 2, 0, 155, 178, 192 }, /* GPP_K */ 130 { 2, 1, 179, 202, 224 }, /* GPP_H */ 131 { 2, 2, 203, 215, 256 }, /* GPP_E */ 132 { 2, 3, 216, 239, 288 }, /* GPP_F */ 133 134 /* Community 4 */ 135 { 3, 2, 269, 286, 320 }, /* GPP_I */ 136 { 3, 3, 287, 298, 352 }, /* GPP_J */ 137 }; 138 139 const struct pchgpio_device cnl_h_device = 140 { 141 .pad_size = 16, 142 .gpi_is = 0x100, 143 .gpi_ie = 0x120, 144 .groups = cnl_h_groups, 145 .ngroups = nitems(cnl_h_groups), 146 .npins = 384, 147 }; 148 149 const struct pchgpio_group cnl_lp_groups[] = 150 { 151 /* Community 0 */ 152 { 0, 0, 0, 24, 0 }, /* GPP_A */ 153 { 0, 1, 25, 50, 32 }, /* GPP_B */ 154 { 0, 2, 51, 58, 64 }, /* GPP_G */ 155 156 /* Community 1 */ 157 { 1, 0, 68, 92, 96 }, /* GPP_D */ 158 { 1, 1, 93, 116, 128 }, /* GPP_F */ 159 { 1, 2, 117, 140, 160 }, /* GPP_H */ 160 161 /* Community 4 */ 162 { 2, 0, 181, 204, 256 }, /* GPP_C */ 163 { 2, 1, 205, 228, 288 }, /* GPP_E */ 164 }; 165 166 const struct pchgpio_device cnl_lp_device = 167 { 168 .pad_size = 16, 169 .gpi_is = 0x100, 170 .gpi_ie = 0x120, 171 .groups = cnl_lp_groups, 172 .ngroups = nitems(cnl_lp_groups), 173 .npins = 320, 174 }; 175 176 const struct pchgpio_group tgl_lp_groups[] = 177 { 178 /* Community 0 */ 179 { 0, 0, 0, 25, 0 }, /* GPP_B */ 180 { 0, 1, 26, 41, 32 }, /* GPP_T */ 181 { 0, 2, 42, 66, 64 }, /* GPP_A */ 182 183 /* Community 1 */ 184 { 1, 0, 67, 74, 96 }, /* GPP_S */ 185 { 1, 1, 75, 98, 128 }, /* GPP_H */ 186 { 1, 2, 99, 119, 160 }, /* GPP_D */ 187 { 1, 3, 120, 143, 192 }, /* GPP_U */ 188 189 /* Community 4 */ 190 { 2, 0, 171, 194, 256 }, /* GPP_C */ 191 { 2, 1, 195, 219, 288 }, /* GPP_F */ 192 { 2, 3, 226, 250, 320 }, /* GPP_E */ 193 194 /* Community 5 */ 195 { 3, 0, 260, 267, 352 }, /* GPP_R */ 196 }; 197 198 const struct pchgpio_device tgl_lp_device = 199 { 200 .pad_size = 16, 201 .gpi_is = 0x100, 202 .gpi_ie = 0x120, 203 .groups = tgl_lp_groups, 204 .ngroups = nitems(tgl_lp_groups), 205 .npins = 360, 206 }; 207 208 const struct pchgpio_group tgl_h_groups[] = 209 { 210 /* Community 0 */ 211 { 0, 0, 0, 24, 0 }, /* GPP_A */ 212 { 0, 1, 25, 44, 32 }, /* GPP_R */ 213 { 0, 2, 45, 70, 64 }, /* GPP_B */ 214 215 /* Community 1 */ 216 { 1, 0, 79, 104, 128 }, /* GPP_D */ 217 { 1, 1, 105, 128, 160 }, /* GPP_C */ 218 { 1, 2, 129, 136, 192 }, /* GPP_S */ 219 { 1, 3, 137, 153, 224 }, /* GPP_G */ 220 221 /* Community 3 */ 222 { 2, 0, 181, 193, 288 }, /* GPP_E */ 223 { 2, 1, 194, 217, 320 }, /* GPP_F */ 224 225 /* Community 4 */ 226 { 2, 0, 218, 241, 352 }, /* GPP_H */ 227 { 2, 1, 242, 251, 384 }, /* GPP_J */ 228 { 2, 2, 252, 266, 416 }, /* GPP_K */ 229 230 /* Community 5 */ 231 { 3, 0, 267, 281, 448 }, /* GPP_I */ 232 }; 233 234 const struct pchgpio_device tgl_h_device = 235 { 236 .pad_size = 16, 237 .gpi_is = 0x100, 238 .gpi_ie = 0x120, 239 .groups = tgl_h_groups, 240 .ngroups = nitems(tgl_h_groups), 241 .npins = 480, 242 }; 243 244 struct pchgpio_match pchgpio_devices[] = { 245 { "INT3450", &cnl_h_device }, 246 { "INT34BB", &cnl_lp_device }, 247 { "INT34C5", &tgl_lp_device }, 248 { "INT34C6", &tgl_h_device }, 249 }; 250 251 int pchgpio_read_pin(void *, int); 252 void pchgpio_write_pin(void *, int, int); 253 void pchgpio_intr_establish(void *, int, int, int (*)(void *), void *); 254 int pchgpio_intr(void *); 255 void pchgpio_save(struct pchgpio_softc *); 256 void pchgpio_restore(struct pchgpio_softc *); 257 258 int 259 pchgpio_match(struct device *parent, void *match, void *aux) 260 { 261 struct acpi_attach_args *aaa = aux; 262 struct cfdata *cf = match; 263 264 return acpi_matchhids(aaa, pchgpio_hids, cf->cf_driver->cd_name); 265 } 266 267 void 268 pchgpio_attach(struct device *parent, struct device *self, void *aux) 269 { 270 struct pchgpio_softc *sc = (struct pchgpio_softc *)self; 271 struct acpi_attach_args *aaa = aux; 272 uint16_t bar; 273 int i; 274 275 sc->sc_acpi = (struct acpi_softc *)parent; 276 sc->sc_node = aaa->aaa_node; 277 printf(" %s", sc->sc_node->name); 278 279 if (aaa->aaa_naddr < 1) { 280 printf(": no registers\n"); 281 return; 282 } 283 284 if (aaa->aaa_nirq < 1) { 285 printf(": no interrupt\n"); 286 return; 287 } 288 289 printf(" addr"); 290 291 for (i = 0; i < aaa->aaa_naddr; i++) { 292 printf(" 0x%llx/0x%llx", aaa->aaa_addr[i], aaa->aaa_size[i]); 293 294 sc->sc_memt[i] = aaa->aaa_bst[i]; 295 if (bus_space_map(sc->sc_memt[i], aaa->aaa_addr[i], 296 aaa->aaa_size[i], 0, &sc->sc_memh[i])) { 297 printf(": can't map registers\n"); 298 goto unmap; 299 } 300 301 sc->sc_padbar[i] = bus_space_read_4(sc->sc_memt[i], 302 sc->sc_memh[i], PCHGPIO_PADBAR); 303 sc->sc_naddr++; 304 } 305 306 printf(" irq %d", aaa->aaa_irq[0]); 307 308 for (i = 0; i < nitems(pchgpio_devices); i++) { 309 if (strcmp(pchgpio_devices[i].hid, aaa->aaa_dev) == 0) { 310 sc->sc_device = pchgpio_devices[i].device; 311 break; 312 } 313 } 314 KASSERT(sc->sc_device); 315 316 /* Figure out the first pin for each community. */ 317 bar = -1; 318 for (i = 0; i < sc->sc_device->ngroups; i++) { 319 if (sc->sc_device->groups[i].bar != bar) { 320 bar = sc->sc_device->groups[i].bar; 321 sc->sc_padbase[bar] = sc->sc_device->groups[i].base; 322 } 323 } 324 325 sc->sc_padsize = sc->sc_device->pad_size; 326 sc->sc_npins = sc->sc_device->npins; 327 sc->sc_pin_cfg = mallocarray(sc->sc_npins, sizeof(*sc->sc_pin_cfg), 328 M_DEVBUF, M_WAITOK); 329 sc->sc_pin_ih = mallocarray(sc->sc_npins, sizeof(*sc->sc_pin_ih), 330 M_DEVBUF, M_WAITOK | M_ZERO); 331 332 sc->sc_ih = acpi_intr_establish(aaa->aaa_irq[0], aaa->aaa_irq_flags[0], 333 IPL_BIO, pchgpio_intr, sc, sc->sc_dev.dv_xname); 334 if (sc->sc_ih == NULL) { 335 printf(": can't establish interrupt\n"); 336 goto unmap; 337 } 338 339 sc->sc_gpio.cookie = sc; 340 sc->sc_gpio.read_pin = pchgpio_read_pin; 341 sc->sc_gpio.write_pin = pchgpio_write_pin; 342 sc->sc_gpio.intr_establish = pchgpio_intr_establish; 343 sc->sc_node->gpio = &sc->sc_gpio; 344 345 printf(", %d pins\n", sc->sc_npins); 346 347 acpi_register_gpio(sc->sc_acpi, sc->sc_node); 348 return; 349 350 unmap: 351 free(sc->sc_pin_ih, M_DEVBUF, sc->sc_npins * sizeof(*sc->sc_pin_ih)); 352 free(sc->sc_pin_cfg, M_DEVBUF, sc->sc_npins * sizeof(*sc->sc_pin_cfg)); 353 for (i = 0; i < sc->sc_naddr; i++) 354 bus_space_unmap(sc->sc_memt[i], sc->sc_memh[i], 355 aaa->aaa_size[i]); 356 } 357 358 int 359 pchgpio_activate(struct device *self, int act) 360 { 361 struct pchgpio_softc *sc = (struct pchgpio_softc *)self; 362 363 switch (act) { 364 case DVACT_SUSPEND: 365 pchgpio_save(sc); 366 break; 367 case DVACT_RESUME: 368 pchgpio_restore(sc); 369 break; 370 } 371 372 return 0; 373 } 374 375 const struct pchgpio_group * 376 pchgpio_find_group(struct pchgpio_softc *sc, int pin) 377 { 378 int i, npads; 379 380 for (i = 0; i < sc->sc_device->ngroups; i++) { 381 npads = 1 + sc->sc_device->groups[i].limit - 382 sc->sc_device->groups[i].base; 383 384 if (pin >= sc->sc_device->groups[i].gpiobase && 385 pin < sc->sc_device->groups[i].gpiobase + npads) 386 return &sc->sc_device->groups[i]; 387 } 388 return NULL; 389 } 390 391 int 392 pchgpio_read_pin(void *cookie, int pin) 393 { 394 struct pchgpio_softc *sc = cookie; 395 const struct pchgpio_group *group; 396 uint32_t reg; 397 uint16_t pad; 398 uint8_t bar; 399 400 group = pchgpio_find_group(sc, pin); 401 bar = group->bar; 402 pad = group->base + (pin - group->gpiobase) - sc->sc_padbase[bar]; 403 404 reg = bus_space_read_4(sc->sc_memt[bar], sc->sc_memh[bar], 405 sc->sc_padbar[bar] + pad * sc->sc_padsize); 406 407 return !!(reg & PCHGPIO_CONF_RXSTATE); 408 } 409 410 void 411 pchgpio_write_pin(void *cookie, int pin, int value) 412 { 413 struct pchgpio_softc *sc = cookie; 414 const struct pchgpio_group *group; 415 uint32_t reg; 416 uint16_t pad; 417 uint8_t bar; 418 419 group = pchgpio_find_group(sc, pin); 420 bar = group->bar; 421 pad = group->base + (pin - group->gpiobase) - sc->sc_padbase[bar]; 422 423 reg = bus_space_read_4(sc->sc_memt[bar], sc->sc_memh[bar], 424 sc->sc_padbar[bar] + pad * sc->sc_padsize); 425 if (value) 426 reg |= PCHGPIO_CONF_TXSTATE; 427 else 428 reg &= ~PCHGPIO_CONF_TXSTATE; 429 bus_space_write_4(sc->sc_memt[bar], sc->sc_memh[bar], 430 sc->sc_padbar[bar] + pad * sc->sc_padsize, reg); 431 } 432 433 void 434 pchgpio_intr_establish(void *cookie, int pin, int flags, 435 int (*func)(void *), void *arg) 436 { 437 struct pchgpio_softc *sc = cookie; 438 const struct pchgpio_group *group; 439 uint32_t reg; 440 uint16_t pad; 441 uint8_t bank, bar; 442 443 KASSERT(pin >= 0); 444 445 group = pchgpio_find_group(sc, pin); 446 if (group == NULL) 447 return; 448 449 bar = group->bar; 450 bank = group->bank; 451 pad = group->base + (pin - group->gpiobase) - sc->sc_padbase[bar]; 452 453 sc->sc_pin_ih[pin].ih_func = func; 454 sc->sc_pin_ih[pin].ih_arg = arg; 455 456 reg = bus_space_read_4(sc->sc_memt[bar], sc->sc_memh[bar], 457 sc->sc_padbar[bar] + pad * sc->sc_padsize); 458 reg &= ~(PCHGPIO_CONF_RXEV_MASK | PCHGPIO_CONF_RXINV); 459 if ((flags & LR_GPIO_MODE) == 1) 460 reg |= PCHGPIO_CONF_RXEV_EDGE; 461 if ((flags & LR_GPIO_POLARITY) == LR_GPIO_ACTLO) 462 reg |= PCHGPIO_CONF_RXINV; 463 if ((flags & LR_GPIO_POLARITY) == LR_GPIO_ACTBOTH) 464 reg |= PCHGPIO_CONF_RXEV_EDGE | PCHGPIO_CONF_RXEV_ZERO; 465 bus_space_write_4(sc->sc_memt[bar], sc->sc_memh[bar], 466 sc->sc_padbar[bar] + pad * sc->sc_padsize, reg); 467 468 reg = bus_space_read_4(sc->sc_memt[bar], sc->sc_memh[bar], 469 sc->sc_device->gpi_ie + bank * 4); 470 reg |= (1 << (pin - group->gpiobase)); 471 bus_space_write_4(sc->sc_memt[bar], sc->sc_memh[bar], 472 sc->sc_device->gpi_ie + bank * 4, reg); 473 } 474 475 int 476 pchgpio_intr(void *arg) 477 { 478 struct pchgpio_softc *sc = arg; 479 uint32_t status, enable; 480 int gpiobase, group, bit, pin, handled = 0; 481 uint16_t base, limit; 482 uint8_t bank, bar; 483 484 for (group = 0; group < sc->sc_device->ngroups; group++) { 485 bar = sc->sc_device->groups[group].bar; 486 bank = sc->sc_device->groups[group].bank; 487 base = sc->sc_device->groups[group].base; 488 limit = sc->sc_device->groups[group].limit; 489 gpiobase = sc->sc_device->groups[group].gpiobase; 490 491 status = bus_space_read_4(sc->sc_memt[bar], sc->sc_memh[bar], 492 sc->sc_device->gpi_is + bank * 4); 493 bus_space_write_4(sc->sc_memt[bar], sc->sc_memh[bar], 494 sc->sc_device->gpi_is + bank * 4, status); 495 enable = bus_space_read_4(sc->sc_memt[bar], sc->sc_memh[bar], 496 sc->sc_device->gpi_ie + bank * 4); 497 status &= enable; 498 if (status == 0) 499 continue; 500 501 for (bit = 0; bit <= (limit - base); bit++) { 502 pin = gpiobase + bit; 503 if (status & (1 << bit) && sc->sc_pin_ih[pin].ih_func) 504 sc->sc_pin_ih[pin].ih_func(sc->sc_pin_ih[pin].ih_arg); 505 handled = 1; 506 } 507 } 508 509 return handled; 510 } 511 512 void 513 pchgpio_save_pin(struct pchgpio_softc *sc, int pin) 514 { 515 const struct pchgpio_group *group; 516 uint32_t gpi_ie; 517 uint16_t pad; 518 uint8_t bank, bar; 519 520 group = pchgpio_find_group(sc, pin); 521 if (group == NULL) 522 return; 523 524 bar = group->bar; 525 bank = group->bank; 526 pad = group->base + (pin - group->gpiobase) - sc->sc_padbase[bar]; 527 528 sc->sc_pin_cfg[pin].pad_cfg_dw0 = 529 bus_space_read_4(sc->sc_memt[bar], sc->sc_memh[bar], 530 sc->sc_padbar[bar] + pad * sc->sc_padsize); 531 sc->sc_pin_cfg[pin].pad_cfg_dw1 = 532 bus_space_read_4(sc->sc_memt[bar], sc->sc_memh[bar], 533 sc->sc_padbar[bar] + pad * sc->sc_padsize + 4); 534 535 gpi_ie = bus_space_read_4(sc->sc_memt[bar], sc->sc_memh[bar], 536 sc->sc_device->gpi_ie + bank * 4); 537 sc->sc_pin_cfg[pin].gpi_ie = (gpi_ie & (1 << (pin - group->gpiobase))); 538 } 539 540 void 541 pchgpio_save(struct pchgpio_softc *sc) 542 { 543 int pin; 544 545 for (pin = 0; pin < sc->sc_npins; pin++) 546 pchgpio_save_pin(sc, pin); 547 } 548 549 void 550 pchgpio_restore_pin(struct pchgpio_softc *sc, int pin) 551 { 552 const struct pchgpio_group *group; 553 int restore = 0; 554 uint32_t pad_cfg_dw0, gpi_ie; 555 uint16_t pad; 556 uint8_t bank, bar; 557 558 group = pchgpio_find_group(sc, pin); 559 if (group == NULL) 560 return; 561 562 bar = group->bar; 563 bank = group->bank; 564 pad = group->base + (pin - group->gpiobase) - sc->sc_padbase[bar]; 565 566 pad_cfg_dw0 = bus_space_read_4(sc->sc_memt[bar], sc->sc_memh[bar], 567 sc->sc_padbar[bar] + pad * sc->sc_padsize); 568 569 if (sc->sc_pin_ih[pin].ih_func) 570 restore = 1; 571 572 /* 573 * The BIOS on Lenovo Thinkpads based on Intel's Tiger Lake 574 * platform have a bug where the GPIO pin that is used for the 575 * touchpad interrupt gets reset when entering S3 and isn't 576 * properly restored upon resume. We detect this issue by 577 * comparing the bits in the PAD_CFG_DW0 register PADRSTCFG 578 * field before suspend and after resume and restore the pin 579 * configuration if the bits don't match. 580 */ 581 if ((sc->sc_pin_cfg[pin].pad_cfg_dw0 & PCHGPIO_CONF_PADRSTCFG_MASK) != 582 (pad_cfg_dw0 & PCHGPIO_CONF_PADRSTCFG_MASK)) 583 restore = 1; 584 585 if (restore) { 586 bus_space_write_4(sc->sc_memt[bar], sc->sc_memh[bar], 587 sc->sc_padbar[bar] + pad * sc->sc_padsize, 588 sc->sc_pin_cfg[pin].pad_cfg_dw0); 589 bus_space_write_4(sc->sc_memt[bar], sc->sc_memh[bar], 590 sc->sc_padbar[bar] + pad * sc->sc_padsize + 4, 591 sc->sc_pin_cfg[pin].pad_cfg_dw1); 592 593 gpi_ie = bus_space_read_4(sc->sc_memt[bar], sc->sc_memh[bar], 594 sc->sc_device->gpi_ie + bank * 4); 595 if (sc->sc_pin_cfg[pin].gpi_ie) 596 gpi_ie |= (1 << (pin - group->gpiobase)); 597 else 598 gpi_ie &= ~(1 << (pin - group->gpiobase)); 599 bus_space_write_4(sc->sc_memt[bar], sc->sc_memh[bar], 600 sc->sc_device->gpi_ie + bank * 4, gpi_ie); 601 } 602 } 603 604 void 605 pchgpio_restore(struct pchgpio_softc *sc) 606 { 607 int pin; 608 609 for (pin = 0; pin < sc->sc_npins; pin++) 610 pchgpio_restore_pin(sc, pin); 611 } 612