acpi_machdep.c revision 193530
1/*- 2 * Copyright (c) 2001 Mitsuru IWASAKI 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27#include <sys/cdefs.h> 28__FBSDID("$FreeBSD: head/sys/i386/acpica/acpi_machdep.c 193530 2009-06-05 18:44:36Z jkim $"); 29 30#include <sys/param.h> 31#include <sys/bus.h> 32#include <sys/condvar.h> 33#include <sys/conf.h> 34#include <sys/fcntl.h> 35#include <sys/kernel.h> 36#include <sys/malloc.h> 37#include <sys/module.h> 38#include <sys/poll.h> 39#include <sys/sysctl.h> 40#include <sys/uio.h> 41#include <vm/vm.h> 42#include <vm/pmap.h> 43 44#include <contrib/dev/acpica/include/acpi.h> 45 46#include <dev/acpica/acpivar.h> 47#include <dev/acpica/acpiio.h> 48 49#include <machine/nexusvar.h> 50 51/* 52 * APM driver emulation 53 */ 54 55#include <machine/apm_bios.h> 56#include <machine/pc/bios.h> 57 58#include <i386/bios/apm.h> 59 60SYSCTL_DECL(_debug_acpi); 61 62uint32_t acpi_resume_beep; 63TUNABLE_INT("debug.acpi.resume_beep", &acpi_resume_beep); 64SYSCTL_UINT(_debug_acpi, OID_AUTO, resume_beep, CTLFLAG_RW, &acpi_resume_beep, 65 0, "Beep the PC speaker when resuming"); 66uint32_t acpi_reset_video; 67TUNABLE_INT("hw.acpi.reset_video", &acpi_reset_video); 68 69static int intr_model = ACPI_INTR_PIC; 70static int apm_active; 71static struct clonedevs *apm_clones; 72 73MALLOC_DEFINE(M_APMDEV, "apmdev", "APM device emulation"); 74 75static d_open_t apmopen; 76static d_close_t apmclose; 77static d_write_t apmwrite; 78static d_ioctl_t apmioctl; 79static d_poll_t apmpoll; 80static d_kqfilter_t apmkqfilter; 81static void apmreadfiltdetach(struct knote *kn); 82static int apmreadfilt(struct knote *kn, long hint); 83static struct filterops apm_readfiltops = 84 { 1, NULL, apmreadfiltdetach, apmreadfilt }; 85 86static struct cdevsw apm_cdevsw = { 87 .d_version = D_VERSION, 88 .d_flags = D_TRACKCLOSE | D_NEEDMINOR, 89 .d_open = apmopen, 90 .d_close = apmclose, 91 .d_write = apmwrite, 92 .d_ioctl = apmioctl, 93 .d_poll = apmpoll, 94 .d_name = "apm", 95 .d_kqfilter = apmkqfilter 96}; 97 98static int 99acpi_capm_convert_battstate(struct acpi_battinfo *battp) 100{ 101 int state; 102 103 state = APM_UNKNOWN; 104 105 if (battp->state & ACPI_BATT_STAT_DISCHARG) { 106 if (battp->cap >= 50) 107 state = 0; /* high */ 108 else 109 state = 1; /* low */ 110 } 111 if (battp->state & ACPI_BATT_STAT_CRITICAL) 112 state = 2; /* critical */ 113 if (battp->state & ACPI_BATT_STAT_CHARGING) 114 state = 3; /* charging */ 115 116 /* If still unknown, determine it based on the battery capacity. */ 117 if (state == APM_UNKNOWN) { 118 if (battp->cap >= 50) 119 state = 0; /* high */ 120 else 121 state = 1; /* low */ 122 } 123 124 return (state); 125} 126 127static int 128acpi_capm_convert_battflags(struct acpi_battinfo *battp) 129{ 130 int flags; 131 132 flags = 0; 133 134 if (battp->cap >= 50) 135 flags |= APM_BATT_HIGH; 136 else { 137 if (battp->state & ACPI_BATT_STAT_CRITICAL) 138 flags |= APM_BATT_CRITICAL; 139 else 140 flags |= APM_BATT_LOW; 141 } 142 if (battp->state & ACPI_BATT_STAT_CHARGING) 143 flags |= APM_BATT_CHARGING; 144 if (battp->state == ACPI_BATT_STAT_NOT_PRESENT) 145 flags = APM_BATT_NOT_PRESENT; 146 147 return (flags); 148} 149 150static int 151acpi_capm_get_info(apm_info_t aip) 152{ 153 int acline; 154 struct acpi_battinfo batt; 155 156 aip->ai_infoversion = 1; 157 aip->ai_major = 1; 158 aip->ai_minor = 2; 159 aip->ai_status = apm_active; 160 aip->ai_capabilities= 0xff00; /* unknown */ 161 162 if (acpi_acad_get_acline(&acline)) 163 aip->ai_acline = APM_UNKNOWN; /* unknown */ 164 else 165 aip->ai_acline = acline; /* on/off */ 166 167 if (acpi_battery_get_battinfo(NULL, &batt) != 0) { 168 aip->ai_batt_stat = APM_UNKNOWN; 169 aip->ai_batt_life = APM_UNKNOWN; 170 aip->ai_batt_time = -1; /* unknown */ 171 aip->ai_batteries = ~0U; /* unknown */ 172 } else { 173 aip->ai_batt_stat = acpi_capm_convert_battstate(&batt); 174 aip->ai_batt_life = batt.cap; 175 aip->ai_batt_time = (batt.min == -1) ? -1 : batt.min * 60; 176 aip->ai_batteries = acpi_battery_get_units(); 177 } 178 179 return (0); 180} 181 182static int 183acpi_capm_get_pwstatus(apm_pwstatus_t app) 184{ 185 device_t dev; 186 int acline, unit, error; 187 struct acpi_battinfo batt; 188 189 if (app->ap_device != PMDV_ALLDEV && 190 (app->ap_device < PMDV_BATT0 || app->ap_device > PMDV_BATT_ALL)) 191 return (1); 192 193 if (app->ap_device == PMDV_ALLDEV) 194 error = acpi_battery_get_battinfo(NULL, &batt); 195 else { 196 unit = app->ap_device - PMDV_BATT0; 197 dev = devclass_get_device(devclass_find("battery"), unit); 198 if (dev != NULL) 199 error = acpi_battery_get_battinfo(dev, &batt); 200 else 201 error = ENXIO; 202 } 203 if (error) 204 return (1); 205 206 app->ap_batt_stat = acpi_capm_convert_battstate(&batt); 207 app->ap_batt_flag = acpi_capm_convert_battflags(&batt); 208 app->ap_batt_life = batt.cap; 209 app->ap_batt_time = (batt.min == -1) ? -1 : batt.min * 60; 210 211 if (acpi_acad_get_acline(&acline)) 212 app->ap_acline = APM_UNKNOWN; 213 else 214 app->ap_acline = acline; /* on/off */ 215 216 return (0); 217} 218 219/* Create single-use devices for /dev/apm and /dev/apmctl. */ 220static void 221apm_clone(void *arg, struct ucred *cred, char *name, int namelen, 222 struct cdev **dev) 223{ 224 int ctl_dev, unit; 225 226 if (*dev != NULL) 227 return; 228 if (strcmp(name, "apmctl") == 0) 229 ctl_dev = TRUE; 230 else if (strcmp(name, "apm") == 0) 231 ctl_dev = FALSE; 232 else 233 return; 234 235 /* Always create a new device and unit number. */ 236 unit = -1; 237 if (clone_create(&apm_clones, &apm_cdevsw, &unit, dev, 0)) { 238 if (ctl_dev) { 239 *dev = make_dev(&apm_cdevsw, unit, 240 UID_ROOT, GID_OPERATOR, 0660, "apmctl%d", unit); 241 } else { 242 *dev = make_dev(&apm_cdevsw, unit, 243 UID_ROOT, GID_OPERATOR, 0664, "apm%d", unit); 244 } 245 if (*dev != NULL) { 246 dev_ref(*dev); 247 (*dev)->si_flags |= SI_CHEAPCLONE; 248 } 249 } 250} 251 252/* Create a struct for tracking per-device suspend notification. */ 253static struct apm_clone_data * 254apm_create_clone(struct cdev *dev, struct acpi_softc *acpi_sc) 255{ 256 struct apm_clone_data *clone; 257 258 clone = malloc(sizeof(*clone), M_APMDEV, M_WAITOK); 259 clone->cdev = dev; 260 clone->acpi_sc = acpi_sc; 261 clone->notify_status = APM_EV_NONE; 262 bzero(&clone->sel_read, sizeof(clone->sel_read)); 263 knlist_init(&clone->sel_read.si_note, &acpi_mutex, NULL, NULL, NULL); 264 265 /* 266 * The acpi device is always managed by devd(8) and is considered 267 * writable (i.e., ack is required to allow suspend to proceed.) 268 */ 269 if (strcmp("acpi", devtoname(dev)) == 0) 270 clone->flags = ACPI_EVF_DEVD | ACPI_EVF_WRITE; 271 else 272 clone->flags = ACPI_EVF_NONE; 273 274 ACPI_LOCK(acpi); 275 STAILQ_INSERT_TAIL(&acpi_sc->apm_cdevs, clone, entries); 276 ACPI_UNLOCK(acpi); 277 return (clone); 278} 279 280static int 281apmopen(struct cdev *dev, int flag, int fmt, struct thread *td) 282{ 283 struct acpi_softc *acpi_sc; 284 struct apm_clone_data *clone; 285 286 acpi_sc = devclass_get_softc(devclass_find("acpi"), 0); 287 clone = apm_create_clone(dev, acpi_sc); 288 dev->si_drv1 = clone; 289 290 /* If the device is opened for write, record that. */ 291 if ((flag & FWRITE) != 0) 292 clone->flags |= ACPI_EVF_WRITE; 293 294 return (0); 295} 296 297static int 298apmclose(struct cdev *dev, int flag, int fmt, struct thread *td) 299{ 300 struct apm_clone_data *clone; 301 struct acpi_softc *acpi_sc; 302 303 clone = dev->si_drv1; 304 acpi_sc = clone->acpi_sc; 305 306 /* We are about to lose a reference so check if suspend should occur */ 307 if (acpi_sc->acpi_next_sstate != 0 && 308 clone->notify_status != APM_EV_ACKED) 309 acpi_AckSleepState(clone, 0); 310 311 /* Remove this clone's data from the list and free it. */ 312 ACPI_LOCK(acpi); 313 STAILQ_REMOVE(&acpi_sc->apm_cdevs, clone, apm_clone_data, entries); 314 knlist_destroy(&clone->sel_read.si_note); 315 ACPI_UNLOCK(acpi); 316 free(clone, M_APMDEV); 317 destroy_dev_sched(dev); 318 return (0); 319} 320 321static int 322apmioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, struct thread *td) 323{ 324 int error; 325 struct apm_clone_data *clone; 326 struct acpi_softc *acpi_sc; 327 struct apm_info info; 328 struct apm_event_info *ev_info; 329 apm_info_old_t aiop; 330 331 error = 0; 332 clone = dev->si_drv1; 333 acpi_sc = clone->acpi_sc; 334 335 switch (cmd) { 336 case APMIO_SUSPEND: 337 if ((flag & FWRITE) == 0) 338 return (EPERM); 339 if (acpi_sc->acpi_next_sstate == 0) { 340 if (acpi_sc->acpi_suspend_sx != ACPI_STATE_S5) { 341 error = acpi_ReqSleepState(acpi_sc, 342 acpi_sc->acpi_suspend_sx); 343 } else { 344 printf( 345 "power off via apm suspend not supported\n"); 346 error = ENXIO; 347 } 348 } else 349 error = acpi_AckSleepState(clone, 0); 350 break; 351 case APMIO_STANDBY: 352 if ((flag & FWRITE) == 0) 353 return (EPERM); 354 if (acpi_sc->acpi_next_sstate == 0) { 355 if (acpi_sc->acpi_standby_sx != ACPI_STATE_S5) { 356 error = acpi_ReqSleepState(acpi_sc, 357 acpi_sc->acpi_standby_sx); 358 } else { 359 printf( 360 "power off via apm standby not supported\n"); 361 error = ENXIO; 362 } 363 } else 364 error = acpi_AckSleepState(clone, 0); 365 break; 366 case APMIO_NEXTEVENT: 367 printf("apm nextevent start\n"); 368 ACPI_LOCK(acpi); 369 if (acpi_sc->acpi_next_sstate != 0 && clone->notify_status == 370 APM_EV_NONE) { 371 ev_info = (struct apm_event_info *)addr; 372 if (acpi_sc->acpi_next_sstate <= ACPI_STATE_S3) 373 ev_info->type = PMEV_STANDBYREQ; 374 else 375 ev_info->type = PMEV_SUSPENDREQ; 376 ev_info->index = 0; 377 clone->notify_status = APM_EV_NOTIFIED; 378 printf("apm event returning %d\n", ev_info->type); 379 } else 380 error = EAGAIN; 381 ACPI_UNLOCK(acpi); 382 break; 383 case APMIO_GETINFO_OLD: 384 if (acpi_capm_get_info(&info)) 385 error = ENXIO; 386 aiop = (apm_info_old_t)addr; 387 aiop->ai_major = info.ai_major; 388 aiop->ai_minor = info.ai_minor; 389 aiop->ai_acline = info.ai_acline; 390 aiop->ai_batt_stat = info.ai_batt_stat; 391 aiop->ai_batt_life = info.ai_batt_life; 392 aiop->ai_status = info.ai_status; 393 break; 394 case APMIO_GETINFO: 395 if (acpi_capm_get_info((apm_info_t)addr)) 396 error = ENXIO; 397 break; 398 case APMIO_GETPWSTATUS: 399 if (acpi_capm_get_pwstatus((apm_pwstatus_t)addr)) 400 error = ENXIO; 401 break; 402 case APMIO_ENABLE: 403 if ((flag & FWRITE) == 0) 404 return (EPERM); 405 apm_active = 1; 406 break; 407 case APMIO_DISABLE: 408 if ((flag & FWRITE) == 0) 409 return (EPERM); 410 apm_active = 0; 411 break; 412 case APMIO_HALTCPU: 413 break; 414 case APMIO_NOTHALTCPU: 415 break; 416 case APMIO_DISPLAY: 417 if ((flag & FWRITE) == 0) 418 return (EPERM); 419 break; 420 case APMIO_BIOS: 421 if ((flag & FWRITE) == 0) 422 return (EPERM); 423 bzero(addr, sizeof(struct apm_bios_arg)); 424 break; 425 default: 426 error = EINVAL; 427 break; 428 } 429 430 return (error); 431} 432 433static int 434apmwrite(struct cdev *dev, struct uio *uio, int ioflag) 435{ 436 return (uio->uio_resid); 437} 438 439static int 440apmpoll(struct cdev *dev, int events, struct thread *td) 441{ 442 struct apm_clone_data *clone; 443 int revents; 444 445 revents = 0; 446 ACPI_LOCK(acpi); 447 clone = dev->si_drv1; 448 if (clone->acpi_sc->acpi_next_sstate) 449 revents |= events & (POLLIN | POLLRDNORM); 450 else 451 selrecord(td, &clone->sel_read); 452 ACPI_UNLOCK(acpi); 453 return (revents); 454} 455 456static int 457apmkqfilter(struct cdev *dev, struct knote *kn) 458{ 459 struct apm_clone_data *clone; 460 461 ACPI_LOCK(acpi); 462 clone = dev->si_drv1; 463 kn->kn_hook = clone; 464 kn->kn_fop = &apm_readfiltops; 465 knlist_add(&clone->sel_read.si_note, kn, 0); 466 ACPI_UNLOCK(acpi); 467 return (0); 468} 469 470static void 471apmreadfiltdetach(struct knote *kn) 472{ 473 struct apm_clone_data *clone; 474 475 ACPI_LOCK(acpi); 476 clone = kn->kn_hook; 477 knlist_remove(&clone->sel_read.si_note, kn, 0); 478 ACPI_UNLOCK(acpi); 479} 480 481static int 482apmreadfilt(struct knote *kn, long hint) 483{ 484 struct apm_clone_data *clone; 485 int sleeping; 486 487 ACPI_LOCK(acpi); 488 clone = kn->kn_hook; 489 sleeping = clone->acpi_sc->acpi_next_sstate ? 1 : 0; 490 ACPI_UNLOCK(acpi); 491 return (sleeping); 492} 493 494int 495acpi_machdep_init(device_t dev) 496{ 497 struct acpi_softc *acpi_sc; 498 499 acpi_sc = devclass_get_softc(devclass_find("acpi"), 0); 500 501 /* Create a clone for /dev/acpi also. */ 502 STAILQ_INIT(&acpi_sc->apm_cdevs); 503 acpi_sc->acpi_clone = apm_create_clone(acpi_sc->acpi_dev_t, acpi_sc); 504 clone_setup(&apm_clones); 505 EVENTHANDLER_REGISTER(dev_clone, apm_clone, 0, 1000); 506 acpi_install_wakeup_handler(acpi_sc); 507 508 if (intr_model == ACPI_INTR_PIC) 509 BUS_CONFIG_INTR(dev, AcpiGbl_FADT.SciInterrupt, 510 INTR_TRIGGER_LEVEL, INTR_POLARITY_LOW); 511 else 512 acpi_SetIntrModel(intr_model); 513 514 SYSCTL_ADD_UINT(&acpi_sc->acpi_sysctl_ctx, 515 SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree), OID_AUTO, 516 "reset_video", CTLFLAG_RW, &acpi_reset_video, 0, 517 "Call the VESA reset BIOS vector on the resume path"); 518 519 return (0); 520} 521 522void 523acpi_SetDefaultIntrModel(int model) 524{ 525 526 intr_model = model; 527} 528 529/* Check BIOS date. If 1998 or older, disable ACPI. */ 530int 531acpi_machdep_quirks(int *quirks) 532{ 533 char *va; 534 int year; 535 536 /* BIOS address 0xffff5 contains the date in the format mm/dd/yy. */ 537 va = pmap_mapbios(0xffff0, 16); 538 sscanf(va + 11, "%2d", &year); 539 pmap_unmapbios((vm_offset_t)va, 16); 540 541 /* 542 * Date must be >= 1/1/1999 or we don't trust ACPI. Note that this 543 * check must be changed by my 114th birthday. 544 */ 545 if (year > 90 && year < 99) 546 *quirks = ACPI_Q_BROKEN; 547 548 return (0); 549} 550 551void 552acpi_cpu_c1() 553{ 554 __asm __volatile("sti; hlt"); 555} 556 557/* 558 * ACPI nexus(4) driver. 559 */ 560static int 561nexus_acpi_probe(device_t dev) 562{ 563 int error; 564 565 error = acpi_identify(); 566 if (error) 567 return (error); 568 569 return (BUS_PROBE_DEFAULT); 570} 571 572static int 573nexus_acpi_attach(device_t dev) 574{ 575 576 nexus_init_resources(); 577 bus_generic_probe(dev); 578 if (BUS_ADD_CHILD(dev, 10, "acpi", 0) == NULL) 579 panic("failed to add acpi0 device"); 580 581 return (bus_generic_attach(dev)); 582} 583 584static device_method_t nexus_acpi_methods[] = { 585 /* Device interface */ 586 DEVMETHOD(device_probe, nexus_acpi_probe), 587 DEVMETHOD(device_attach, nexus_acpi_attach), 588 589 { 0, 0 } 590}; 591 592DEFINE_CLASS_1(nexus, nexus_acpi_driver, nexus_acpi_methods, 1, nexus_driver); 593static devclass_t nexus_devclass; 594 595DRIVER_MODULE(nexus_acpi, root, nexus_acpi_driver, nexus_devclass, 0, 0); 596