acpi_apm.c revision 150003
185556Siwasaki/*- 285556Siwasaki * Copyright (c) 2001 Mitsuru IWASAKI 385556Siwasaki * All rights reserved. 485556Siwasaki * 585556Siwasaki * Redistribution and use in source and binary forms, with or without 685556Siwasaki * modification, are permitted provided that the following conditions 785556Siwasaki * are met: 885556Siwasaki * 1. Redistributions of source code must retain the above copyright 985556Siwasaki * notice, this list of conditions and the following disclaimer. 1085556Siwasaki * 2. Redistributions in binary form must reproduce the above copyright 1185556Siwasaki * notice, this list of conditions and the following disclaimer in the 1285556Siwasaki * documentation and/or other materials provided with the distribution. 1385556Siwasaki * 1485556Siwasaki * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1585556Siwasaki * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1685556Siwasaki * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1785556Siwasaki * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1885556Siwasaki * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1985556Siwasaki * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2085556Siwasaki * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2185556Siwasaki * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2285556Siwasaki * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2385556Siwasaki * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2485556Siwasaki * SUCH DAMAGE. 2585556Siwasaki */ 2685556Siwasaki 27115681Sobrien#include <sys/cdefs.h> 28115681Sobrien__FBSDID("$FreeBSD: head/sys/i386/acpica/acpi_machdep.c 150003 2005-09-11 18:39:03Z obrien $"); 29115681Sobrien 3085556Siwasaki#include <sys/param.h> 3185556Siwasaki#include <sys/bus.h> 3285556Siwasaki#include <sys/conf.h> 3385556Siwasaki#include <sys/fcntl.h> 34121743Siwasaki#include <sys/kernel.h> 35121743Siwasaki#include <sys/sysctl.h> 3685556Siwasaki#include <sys/uio.h> 37131312Snjl#include <vm/vm.h> 38131312Snjl#include <vm/pmap.h> 3985556Siwasaki 40150003Sobrien#include <contrib/dev/acpica/acpi.h> 4185556Siwasaki#include <dev/acpica/acpivar.h> 4285556Siwasaki#include <dev/acpica/acpiio.h> 4385556Siwasaki 4485556Siwasaki/* 4585556Siwasaki * APM driver emulation 4685556Siwasaki */ 4785556Siwasaki 4885556Siwasaki#include <sys/selinfo.h> 4985556Siwasaki 5085556Siwasaki#include <machine/apm_bios.h> 5185556Siwasaki#include <machine/pc/bios.h> 5285556Siwasaki 53112551Smdodd#include <i386/bios/apm.h> 5485556Siwasaki 55128975Snjluint32_t acpi_reset_video = 1; 56121830SnjlTUNABLE_INT("hw.acpi.reset_video", &acpi_reset_video); 57121743Siwasaki 58128975Snjlstatic int intr_model = ACPI_INTR_PIC; 59132619Snjlstatic int apm_active; 6085556Siwasaki 6185556Siwasakistatic d_open_t apmopen; 6285556Siwasakistatic d_close_t apmclose; 6385556Siwasakistatic d_write_t apmwrite; 6485556Siwasakistatic d_ioctl_t apmioctl; 6585556Siwasakistatic d_poll_t apmpoll; 6685556Siwasaki 6785556Siwasakistatic struct cdevsw apm_cdevsw = { 68126080Sphk .d_version = D_VERSION, 69111815Sphk .d_open = apmopen, 70111815Sphk .d_close = apmclose, 71111815Sphk .d_write = apmwrite, 72111815Sphk .d_ioctl = apmioctl, 73111815Sphk .d_poll = apmpoll, 74111815Sphk .d_name = "apm", 7585556Siwasaki}; 7685556Siwasaki 7785556Siwasakistatic int 7885556Siwasakiacpi_capm_convert_battstate(struct acpi_battinfo *battp) 7985556Siwasaki{ 8085556Siwasaki int state; 8185556Siwasaki 82131218Simp state = APM_UNKNOWN; 8385556Siwasaki 8485556Siwasaki if (battp->state & ACPI_BATT_STAT_DISCHARG) { 85119530Snjl if (battp->cap >= 50) 8685556Siwasaki state = 0; /* high */ 87119530Snjl else 8885556Siwasaki state = 1; /* low */ 8985556Siwasaki } 90119530Snjl if (battp->state & ACPI_BATT_STAT_CRITICAL) 9185556Siwasaki state = 2; /* critical */ 92119530Snjl if (battp->state & ACPI_BATT_STAT_CHARGING) 9385556Siwasaki state = 3; /* charging */ 94119530Snjl 95120156Siwasaki /* If still unknown, determine it based on the battery capacity. */ 96131218Simp if (state == APM_UNKNOWN) { 97128975Snjl if (battp->cap >= 50) 98120156Siwasaki state = 0; /* high */ 99128975Snjl else 100120156Siwasaki state = 1; /* low */ 101120156Siwasaki } 102120156Siwasaki 10385556Siwasaki return (state); 10485556Siwasaki} 10585556Siwasaki 10685556Siwasakistatic int 10785556Siwasakiacpi_capm_convert_battflags(struct acpi_battinfo *battp) 10885556Siwasaki{ 10985556Siwasaki int flags; 11085556Siwasaki 11185556Siwasaki flags = 0; 11285556Siwasaki 113128975Snjl if (battp->cap >= 50) 11485556Siwasaki flags |= APM_BATT_HIGH; 115128975Snjl else { 116119530Snjl if (battp->state & ACPI_BATT_STAT_CRITICAL) 11785556Siwasaki flags |= APM_BATT_CRITICAL; 118119530Snjl else 11985556Siwasaki flags |= APM_BATT_LOW; 12085556Siwasaki } 121119530Snjl if (battp->state & ACPI_BATT_STAT_CHARGING) 12285556Siwasaki flags |= APM_BATT_CHARGING; 123119530Snjl if (battp->state == ACPI_BATT_STAT_NOT_PRESENT) 12485556Siwasaki flags = APM_BATT_NOT_PRESENT; 12585556Siwasaki 12685556Siwasaki return (flags); 12785556Siwasaki} 12885556Siwasaki 12985556Siwasakistatic int 13085556Siwasakiacpi_capm_get_info(apm_info_t aip) 13185556Siwasaki{ 13285556Siwasaki int acline; 13385556Siwasaki struct acpi_battinfo batt; 13485556Siwasaki 13585556Siwasaki aip->ai_infoversion = 1; 13685556Siwasaki aip->ai_major = 1; 13785556Siwasaki aip->ai_minor = 2; 138132619Snjl aip->ai_status = apm_active; 139131218Simp aip->ai_capabilities= 0xff00; /* unknown */ 14085556Siwasaki 141119530Snjl if (acpi_acad_get_acline(&acline)) 142131218Simp aip->ai_acline = APM_UNKNOWN; /* unknown */ 143119530Snjl else 14485556Siwasaki aip->ai_acline = acline; /* on/off */ 14585556Siwasaki 146148352Snjl if (acpi_battery_get_battinfo(NULL, &batt) != 0) { 147131218Simp aip->ai_batt_stat = APM_UNKNOWN; 148131218Simp aip->ai_batt_life = APM_UNKNOWN; 149131218Simp aip->ai_batt_time = -1; /* unknown */ 150131218Simp aip->ai_batteries = ~0U; /* unknown */ 15185556Siwasaki } else { 15285556Siwasaki aip->ai_batt_stat = acpi_capm_convert_battstate(&batt); 15385556Siwasaki aip->ai_batt_life = batt.cap; 15485556Siwasaki aip->ai_batt_time = (batt.min == -1) ? -1 : batt.min * 60; 15585556Siwasaki aip->ai_batteries = acpi_battery_get_units(); 15685556Siwasaki } 15785556Siwasaki 15885556Siwasaki return (0); 15985556Siwasaki} 16085556Siwasaki 16185556Siwasakistatic int 16285556Siwasakiacpi_capm_get_pwstatus(apm_pwstatus_t app) 16385556Siwasaki{ 164148352Snjl device_t dev; 165148352Snjl int acline, unit, error; 16685556Siwasaki struct acpi_battinfo batt; 16785556Siwasaki 16885556Siwasaki if (app->ap_device != PMDV_ALLDEV && 169128975Snjl (app->ap_device < PMDV_BATT0 || app->ap_device > PMDV_BATT_ALL)) 17085556Siwasaki return (1); 17185556Siwasaki 172119530Snjl if (app->ap_device == PMDV_ALLDEV) 173148352Snjl error = acpi_battery_get_battinfo(NULL, &batt); 174148352Snjl else { 175148352Snjl unit = app->ap_device - PMDV_BATT0; 176148352Snjl dev = devclass_get_device(devclass_find("battery"), unit); 177148352Snjl if (dev != NULL) 178148352Snjl error = acpi_battery_get_battinfo(dev, &batt); 179148352Snjl else 180148352Snjl error = ENXIO; 181148352Snjl } 182148352Snjl if (error) 18385556Siwasaki return (1); 18485556Siwasaki 18585556Siwasaki app->ap_batt_stat = acpi_capm_convert_battstate(&batt); 18685556Siwasaki app->ap_batt_flag = acpi_capm_convert_battflags(&batt); 18785556Siwasaki app->ap_batt_life = batt.cap; 18885556Siwasaki app->ap_batt_time = (batt.min == -1) ? -1 : batt.min * 60; 18985556Siwasaki 190119530Snjl if (acpi_acad_get_acline(&acline)) 191131218Simp app->ap_acline = APM_UNKNOWN; 192119530Snjl else 19385556Siwasaki app->ap_acline = acline; /* on/off */ 19485556Siwasaki 19585556Siwasaki return (0); 19685556Siwasaki} 19785556Siwasaki 19885556Siwasakistatic int 199130585Sphkapmopen(struct cdev *dev, int flag, int fmt, d_thread_t *td) 20085556Siwasaki{ 20185556Siwasaki return (0); 20285556Siwasaki} 20385556Siwasaki 20485556Siwasakistatic int 205130585Sphkapmclose(struct cdev *dev, int flag, int fmt, d_thread_t *td) 20685556Siwasaki{ 20785556Siwasaki return (0); 20885556Siwasaki} 20985556Siwasaki 21085556Siwasakistatic int 211130585Sphkapmioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, d_thread_t *td) 21285556Siwasaki{ 21385556Siwasaki int error = 0; 21485556Siwasaki struct acpi_softc *acpi_sc; 21585556Siwasaki struct apm_info info; 21685556Siwasaki apm_info_old_t aiop; 21785556Siwasaki 218132619Snjl acpi_sc = devclass_get_softc(devclass_find("acpi"), 0); 21985556Siwasaki 22085556Siwasaki switch (cmd) { 22185556Siwasaki case APMIO_SUSPEND: 222119530Snjl if ((flag & FWRITE) == 0) 22385556Siwasaki return (EPERM); 224132619Snjl if (apm_active) 22585556Siwasaki acpi_SetSleepState(acpi_sc, acpi_sc->acpi_suspend_sx); 22685556Siwasaki else 22785556Siwasaki error = EINVAL; 22885556Siwasaki break; 22985556Siwasaki case APMIO_STANDBY: 230119530Snjl if ((flag & FWRITE) == 0) 23185556Siwasaki return (EPERM); 232132619Snjl if (apm_active) 23385556Siwasaki acpi_SetSleepState(acpi_sc, acpi_sc->acpi_standby_sx); 23485556Siwasaki else 23585556Siwasaki error = EINVAL; 23685556Siwasaki break; 23785556Siwasaki case APMIO_GETINFO_OLD: 23885556Siwasaki if (acpi_capm_get_info(&info)) 23985556Siwasaki error = ENXIO; 24085556Siwasaki aiop = (apm_info_old_t)addr; 24185556Siwasaki aiop->ai_major = info.ai_major; 24285556Siwasaki aiop->ai_minor = info.ai_minor; 24385556Siwasaki aiop->ai_acline = info.ai_acline; 24485556Siwasaki aiop->ai_batt_stat = info.ai_batt_stat; 24585556Siwasaki aiop->ai_batt_life = info.ai_batt_life; 24685556Siwasaki aiop->ai_status = info.ai_status; 24785556Siwasaki break; 24885556Siwasaki case APMIO_GETINFO: 24985556Siwasaki if (acpi_capm_get_info((apm_info_t)addr)) 25085556Siwasaki error = ENXIO; 25185556Siwasaki break; 25285556Siwasaki case APMIO_GETPWSTATUS: 25385556Siwasaki if (acpi_capm_get_pwstatus((apm_pwstatus_t)addr)) 25485556Siwasaki error = ENXIO; 25585556Siwasaki break; 25685556Siwasaki case APMIO_ENABLE: 257119530Snjl if ((flag & FWRITE) == 0) 25885556Siwasaki return (EPERM); 259132619Snjl apm_active = 1; 26085556Siwasaki break; 26185556Siwasaki case APMIO_DISABLE: 262119530Snjl if ((flag & FWRITE) == 0) 26385556Siwasaki return (EPERM); 264132619Snjl apm_active = 0; 26585556Siwasaki break; 26685556Siwasaki case APMIO_HALTCPU: 26785556Siwasaki break; 26885556Siwasaki case APMIO_NOTHALTCPU: 26985556Siwasaki break; 27085556Siwasaki case APMIO_DISPLAY: 271119530Snjl if ((flag & FWRITE) == 0) 27285556Siwasaki return (EPERM); 27385556Siwasaki break; 27485556Siwasaki case APMIO_BIOS: 275119530Snjl if ((flag & FWRITE) == 0) 27685556Siwasaki return (EPERM); 27785556Siwasaki bzero(addr, sizeof(struct apm_bios_arg)); 27885556Siwasaki break; 27985556Siwasaki default: 28085556Siwasaki error = EINVAL; 28185556Siwasaki break; 28285556Siwasaki } 28385556Siwasaki 28485556Siwasaki return (error); 28585556Siwasaki} 28685556Siwasaki 28785556Siwasakistatic int 288130585Sphkapmwrite(struct cdev *dev, struct uio *uio, int ioflag) 28985556Siwasaki{ 29085556Siwasaki return (uio->uio_resid); 29185556Siwasaki} 29285556Siwasaki 29385556Siwasakistatic int 294130585Sphkapmpoll(struct cdev *dev, int events, d_thread_t *td) 29585556Siwasaki{ 29685556Siwasaki return (0); 29785556Siwasaki} 29885556Siwasaki 29985556Siwasakistatic void 30085556Siwasakiacpi_capm_init(struct acpi_softc *sc) 30185556Siwasaki{ 30285556Siwasaki make_dev(&apm_cdevsw, 0, 0, 5, 0664, "apm"); 30385556Siwasaki} 30485556Siwasaki 30585556Siwasakiint 30685556Siwasakiacpi_machdep_init(device_t dev) 30785556Siwasaki{ 30885556Siwasaki struct acpi_softc *sc; 30985556Siwasaki 310132619Snjl sc = devclass_get_softc(devclass_find("acpi"), 0); 31185556Siwasaki acpi_capm_init(sc); 31285556Siwasaki 31385556Siwasaki acpi_install_wakeup_handler(sc); 31485556Siwasaki 315128932Sjhb if (intr_model == ACPI_INTR_PIC) 316128932Sjhb BUS_CONFIG_INTR(dev, AcpiGbl_FADT->SciInt, INTR_TRIGGER_LEVEL, 317128932Sjhb INTR_POLARITY_LOW); 318128932Sjhb else 319119944Sjhb acpi_SetIntrModel(intr_model); 320121743Siwasaki 321121830Snjl SYSCTL_ADD_UINT(&sc->acpi_sysctl_ctx, 322121830Snjl SYSCTL_CHILDREN(sc->acpi_sysctl_tree), OID_AUTO, 323121830Snjl "reset_video", CTLFLAG_RD | CTLFLAG_RW, &acpi_reset_video, 0, 324121830Snjl "Call the VESA reset BIOS vector on the resume path"); 325121743Siwasaki 32685556Siwasaki return (0); 32785556Siwasaki} 328119944Sjhb 329119944Sjhbvoid 330119944Sjhbacpi_SetDefaultIntrModel(int model) 331119944Sjhb{ 332119944Sjhb 333119944Sjhb intr_model = model; 334119944Sjhb} 335131312Snjl 336131312Snjl/* Check BIOS date. If 1998 or older, disable ACPI. */ 337131312Snjlint 338131312Snjlacpi_machdep_quirks(int *quirks) 339131312Snjl{ 340136367Snjl char *va; 341136367Snjl int year; 342131312Snjl 343136367Snjl /* BIOS address 0xffff5 contains the date in the format mm/dd/yy. */ 344136367Snjl va = pmap_mapdev(0xffff0, 16); 345136367Snjl sscanf(va + 11, "%2d", &year); 346136367Snjl pmap_unmapdev((vm_offset_t)va, 16); 347131312Snjl 348136367Snjl /* 349136367Snjl * Date must be >= 1/1/1999 or we don't trust ACPI. Note that this 350136367Snjl * check must be changed by my 114th birthday. 351136367Snjl */ 352136367Snjl if (year > 90 && year < 99) 353136367Snjl *quirks = ACPI_Q_BROKEN; 354131312Snjl 355136367Snjl return (0); 356131312Snjl} 357136366Snjl 358136366Snjlvoid 359136366Snjlacpi_cpu_c1() 360136366Snjl{ 361136366Snjl __asm __volatile("sti; hlt"); 362136366Snjl} 363