diff options
Diffstat (limited to 'usr/src/uts/i86pc/io/ppm')
-rw-r--r-- | usr/src/uts/i86pc/io/ppm/acpippm.c | 443 | ||||
-rw-r--r-- | usr/src/uts/i86pc/io/ppm/acpippm.h | 41 | ||||
-rw-r--r-- | usr/src/uts/i86pc/io/ppm/acpisleep.c | 214 |
3 files changed, 698 insertions, 0 deletions
diff --git a/usr/src/uts/i86pc/io/ppm/acpippm.c b/usr/src/uts/i86pc/io/ppm/acpippm.c new file mode 100644 index 0000000000..a8e5019e50 --- /dev/null +++ b/usr/src/uts/i86pc/io/ppm/acpippm.c @@ -0,0 +1,443 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/types.h> +#include <sys/conf.h> +#include <sys/open.h> +#include <sys/modctl.h> +#include <sys/promif.h> +#include <sys/stat.h> +#include <sys/ddi_impldefs.h> +#include <sys/ddi.h> +#include <sys/sunddi.h> +#include <sys/epm.h> +#include <sys/acpi/acpi.h> +#include <sys/acpica.h> +#include <sys/psm_types.h> + +/* + * ACPI Power Management Driver + * + * acpippm deals with those bits of ppm functionality that + * must be mediated by ACPI + * + * The routines in this driver is referenced by Platform + * Power Management driver of X86 workstation systems. + * acpippm driver is loaded because it is listed as a platform driver + * It is initially configured as a pseudo driver. + */ + +/* + * Configuration Function prototypes and data structures + */ +static int appm_attach(dev_info_t *, ddi_attach_cmd_t); +static int appm_detach(dev_info_t *, ddi_detach_cmd_t); +static int appm_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); +static int appm_open(dev_t *dev_p, int flag, int otyp, cred_t *cred_p); +static int appm_close(dev_t dev, int flag, int otyp, cred_t *cred_p); +static int appm_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); + +/* + * Configuration data structures + */ +static struct cb_ops appm_cbops = { + appm_open, /* open */ + appm_close, /* close */ + nodev, /* strategy */ + nodev, /* print */ + nodev, /* dump */ + nodev, /* read */ + nodev, /* write */ + appm_ioctl, /* ioctl */ + nodev, /* devmap */ + nodev, /* mmap */ + nodev, /* segmap */ + nochpoll, /* chpoll */ + ddi_prop_op, /* prop_op */ + NULL, /* stream */ + D_MP | D_NEW, /* flag */ + CB_REV, /* rev */ + nodev, /* aread */ + nodev, /* awrite */ +}; + +static struct dev_ops appm_ops = { + DEVO_REV, /* devo_rev */ + 0, /* refcnt */ + appm_getinfo, /* getinfo */ + nulldev, /* identify */ + nulldev, /* probe */ + appm_attach, /* attach */ + appm_detach, /* detach */ + nodev, /* reset */ + &appm_cbops, /* cb_ops */ + NULL, /* bus_ops */ + NULL /* power */ +}; + +extern struct mod_ops mod_driverops; + +static struct modldrv modldrv = { + &mod_driverops, + "ACPI ppm driver v1.8", + &appm_ops, +}; + +static struct modlinkage modlinkage = { + MODREV_1, + &modldrv, + NULL +}; + +/* + * Driver state structure + */ +typedef struct { + dev_info_t *dip; + ddi_acc_handle_t devid_hndl; + ddi_acc_handle_t estar_hndl; + int lyropen; /* ref count */ +} appm_unit; + +/* + * Driver global variables + * + * appm_lock synchronize the access of lyr handle to each appm + * minor device, therefore write to tomatillo device is + * sequentialized. Lyr protocol requires pairing up lyr open + * and close, so only a single reference is allowed per minor node. + */ +static void *appm_statep; +static kmutex_t appm_lock; + +/* + * S3 stuff: + */ +char _depends_on[] = "misc/acpica"; + +extern int acpi_enter_sleepstate(s3a_t *); +extern int acpi_exit_sleepstate(s3a_t *); + + +int +_init(void) +{ + int error; + + if ((error = ddi_soft_state_init(&appm_statep, + sizeof (appm_unit), 0)) != DDI_SUCCESS) { + return (error); + } + + mutex_init(&appm_lock, NULL, MUTEX_DRIVER, NULL); + + if ((error = mod_install(&modlinkage)) != DDI_SUCCESS) { + mutex_destroy(&appm_lock); + ddi_soft_state_fini(&appm_statep); + return (error); + } + + return (error); +} + +int +_fini(void) +{ + int error; + + if ((error = mod_remove(&modlinkage)) == DDI_SUCCESS) { + mutex_destroy(&appm_lock); + ddi_soft_state_fini(&appm_statep); + } + + return (error); + +} + +int +_info(struct modinfo *modinfop) +{ + return (mod_info(&modlinkage, modinfop)); +} + + + +/* + * Driver attach(9e) entry point + */ +static int +appm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) +{ + char *str = "appm_attach"; + int instance; + appm_unit *unitp; + int rv = DDI_SUCCESS; + + switch (cmd) { + case DDI_ATTACH: + break; + case DDI_RESUME: + return (DDI_SUCCESS); + default: + cmn_err(CE_WARN, "%s: cmd %d unsupported.\n", str, cmd); + return (DDI_FAILURE); + } + + instance = ddi_get_instance(dip); + rv = ddi_soft_state_zalloc(appm_statep, instance); + if (rv != DDI_SUCCESS) { + cmn_err(CE_WARN, "%s: failed alloc for dev(%s@%s)", + str, ddi_binding_name(dip), + ddi_get_name_addr(dip) ? ddi_get_name_addr(dip) : " "); + return (rv); + } + + if ((unitp = ddi_get_soft_state(appm_statep, instance)) == NULL) { + rv = DDI_FAILURE; + goto doerrs; + } + + /* + * Export "ddi-kernel-ioctl" property - prepared to support + * kernel ioctls (driver layering). + * XXX is this still needed? + * XXXX (RSF) Not that I am aware of. + */ + rv = ddi_prop_create(DDI_DEV_T_NONE, dip, DDI_PROP_CANSLEEP, + DDI_KERNEL_IOCTL, NULL, 0); + if (rv != DDI_PROP_SUCCESS) + goto doerrs; + + ddi_report_dev(dip); + unitp->dip = dip; + + /* + * XXX here we would do whatever we need to to determine if the + * XXX platform supports ACPI, and fail the attach if not. + * XXX If it does, we do whatever setup is needed to get access to + * XXX ACPI register space. + */ + + unitp->lyropen = 0; + + /* + * create minor node for kernel_ioctl calls + */ + rv = ddi_create_minor_node(dip, "acpi-ppm", S_IFCHR, instance, 0, 0); + if (rv != DDI_SUCCESS) + goto doerrs; + + return (rv); + +doerrs: + + if (ddi_prop_exists(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS | + DDI_PROP_NOTPROM, DDI_KERNEL_IOCTL)) + ddi_prop_remove_all(dip); + + ddi_soft_state_free(appm_statep, instance); + + return (rv); +} + + +/* + * Driver getinfo(9e) entry routine + */ +/* ARGSUSED */ +static int +appm_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result) +{ + appm_unit *unitp; + int instance; + + switch (cmd) { + case DDI_INFO_DEVT2DEVINFO: + instance = getminor((dev_t)arg); + unitp = ddi_get_soft_state(appm_statep, instance); + if (unitp == NULL) { + return (DDI_FAILURE); + } + *result = (void *) unitp->dip; + return (DDI_SUCCESS); + + case DDI_INFO_DEVT2INSTANCE: + instance = getminor((dev_t)arg); + *result = (void *)(uintptr_t)instance; + return (DDI_SUCCESS); + + default: + return (DDI_FAILURE); + } +} + + +/* + * detach(9e) + */ +/* ARGSUSED */ +static int +appm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) +{ + char *str = "appm_detach"; + + switch (cmd) { + case DDI_DETACH: + return (DDI_FAILURE); + case DDI_SUSPEND: + return (DDI_SUCCESS); + default: + cmn_err(CE_WARN, "%s: cmd %d unsupported", str, cmd); + return (DDI_FAILURE); + } +} + + +/* ARGSUSED */ +static int +appm_open(dev_t *dev_p, int flag, int otyp, cred_t *cred_p) +{ + appm_unit *unitp; + + /* not intended to allow sysadmin level root process to open it */ + if (drv_priv(cred_p) != DDI_SUCCESS) + return (EPERM); + + if ((unitp = ddi_get_soft_state( + appm_statep, getminor(*dev_p))) == NULL) { + cmn_err(CE_WARN, "appm_open: failed to get soft state!"); + return (DDI_FAILURE); + } + + mutex_enter(&appm_lock); + if (unitp->lyropen != 0) { + mutex_exit(&appm_lock); + return (EBUSY); + } + unitp->lyropen++; + mutex_exit(&appm_lock); + + return (DDI_SUCCESS); +} + + +/* ARGSUSED */ +static int +appm_close(dev_t dev, int flag, int otyp, cred_t *cred_p) +{ + appm_unit *unitp; + + if ((unitp = + ddi_get_soft_state(appm_statep, getminor(dev))) == NULL) + return (DDI_FAILURE); + + mutex_enter(&appm_lock); + unitp->lyropen = 0; + mutex_exit(&appm_lock); + + return (DDI_SUCCESS); +} + + +/* + * must match ppm.conf + */ +#define APPMIOC ('A' << 8) +#define APPMIOC_ENTER_S3 (APPMIOC | 1) /* arg *s3a_t */ +#define APPMIOC_EXIT_S3 (APPMIOC | 2) /* arg *s3a_t */ + +/* ARGSUSED3 */ +static int +appm_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, + cred_t *cred_p, int *rval_p) +{ + static boolean_t acpi_initted = B_FALSE; + char *str = "appm_ioctl"; + int ret; + s3a_t *s3ap = (s3a_t *)arg; + + PMD(PMD_SX, ("%s: called with %x\n", str, cmd)) + + if (drv_priv(cred_p) != 0) { + PMD(PMD_SX, ("%s: EPERM\n", str)) + return (EPERM); + } + + if (ddi_get_soft_state(appm_statep, getminor(dev)) == NULL) { + PMD(PMD_SX, ("%s: no soft state: EIO\n", str)) + return (EIO); + } + + if (!acpi_initted) { + PMD(PMD_SX, ("%s: !acpi_initted\n", str)) + if (acpica_init() == 0) { + acpi_initted = B_TRUE; + } else { + if (rval_p != NULL) { + *rval_p = EINVAL; + } + PMD(PMD_SX, ("%s: EINVAL\n", str)) + return (EINVAL); + } + } + + PMD(PMD_SX, ("%s: looking for cmd %x\n", str, cmd)) + switch (cmd) { + case APPMIOC_ENTER_S3: + /* + * suspend to RAM (ie S3) + */ + PMD(PMD_SX, ("%s: cmd %x, arg %p\n", str, cmd, (void *)arg)) + ret = acpi_enter_sleepstate(s3ap); + break; + + case APPMIOC_EXIT_S3: + /* + * return from S3 + */ + PMD(PMD_SX, ("%s: cmd %x, arg %p\n", str, cmd, (void *)arg)) + ret = acpi_exit_sleepstate(s3ap); + break; + + default: + PMD(PMD_SX, ("%s: cmd %x unrecognized: ENOTTY\n", str, cmd)) + return (ENOTTY); + } + + /* + * upon failure return EINVAL + */ + if (ret != 0) { + if (rval_p != NULL) { + *rval_p = EINVAL; + } + return (EINVAL); + } + + return (0); +} diff --git a/usr/src/uts/i86pc/io/ppm/acpippm.h b/usr/src/uts/i86pc/io/ppm/acpippm.h new file mode 100644 index 0000000000..c60aedfc15 --- /dev/null +++ b/usr/src/uts/i86pc/io/ppm/acpippm.h @@ -0,0 +1,41 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SYS_ACPIPPM_H +#define _SYS_ACPIPPM_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_ACPIPPM_H */ diff --git a/usr/src/uts/i86pc/io/ppm/acpisleep.c b/usr/src/uts/i86pc/io/ppm/acpisleep.c new file mode 100644 index 0000000000..d1ab6c5c34 --- /dev/null +++ b/usr/src/uts/i86pc/io/ppm/acpisleep.c @@ -0,0 +1,214 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/types.h> +#include <sys/smp_impldefs.h> +#include <sys/promif.h> + +#include <sys/kmem.h> +#include <sys/archsystm.h> +#include <sys/cpuvar.h> +#include <sys/pte.h> +#include <vm/seg_kmem.h> +#include <sys/epm.h> +#include <sys/machsystm.h> +#include <sys/clock.h> + +#include <sys/cpr_wakecode.h> +#include <sys/acpi/acpi.h> + +#ifdef OLDPMCODE +#include "acpi.h" +#endif + +#include <sys/x86_archext.h> +#include <sys/reboot.h> +#include <sys/cpu_module.h> +#include <sys/kdi.h> + +/* + * S3 stuff + */ + +int acpi_rtc_wake = 0x0; /* wake in N seconds */ + +#if 0 /* debug */ +static uint8_t branchbuf[64 * 1024]; /* for the HDT branch trace stuff */ +#endif /* debug */ + +extern int boothowto; + +#define BOOTCPU 0 /* cpu 0 is always the boot cpu */ + +extern void kernel_wc_code(void); +extern tod_ops_t *tod_ops; +extern int flushes_require_xcalls; +extern int tsc_gethrtime_enable; + +extern cpuset_t cpu_ready_set; +extern void *(*cpu_pause_func)(void *); + +/* + * This probably belong in apic.c, along with the save/restore stuff. + */ +static void +reinit_picmode(void) +{ + ACPI_OBJECT_LIST arglist; + ACPI_OBJECT arg; + ACPI_STATUS status; + + /* Setup parameter object */ + arglist.Count = 1; + arglist.Pointer = &arg; + arg.Type = ACPI_TYPE_INTEGER; + arg.Integer.Value = 1; + + status = AcpiEvaluateObject(NULL, "\\_PIC", &arglist, NULL); + if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) { + PMD(PMD_SX, ("Method _PIC failed, %d\n", status)) + } +} + + +/* + * This is what we've all been waiting for! + */ +int +acpi_enter_sleepstate(s3a_t *s3ap) +{ + ACPI_PHYSICAL_ADDRESS wakephys = s3ap->s3a_wakephys; + caddr_t wakevirt = rm_platter_va; + /*LINTED*/ + wakecode_t *wp = (wakecode_t *)wakevirt; + uint_t Sx = s3ap->s3a_state; + + PT(PT_SWV); + /* Set waking vector */ + if (AcpiSetFirmwareWakingVector(wakephys) != AE_OK) { + PT(PT_SWV_FAIL); + PMD(PMD_SX, ("Can't SetFirmwareWakingVector(%lx)\n", + (long)wakephys)) + goto insomnia; + } + + PT(PT_EWE); + /* Enable wake events */ + if (AcpiEnableEvent(ACPI_EVENT_POWER_BUTTON, 0) != AE_OK) { + PT(PT_EWE_FAIL); + PMD(PMD_SX, ("Can't EnableEvent(POWER_BUTTON)\n")) + } + if (acpi_rtc_wake > 0) { + PT(PT_RTCW); + if (AcpiEnableEvent(ACPI_EVENT_RTC, 0) != AE_OK) { + PT(PT_RTCW_FAIL); + PMD(PMD_SX, ("Can't EnableEvent(RTC)\n")) + } + + /* + * Set RTC to wake us in a wee while. + */ + mutex_enter(&tod_lock); + PT(PT_TOD); + TODOP_SETWAKE(tod_ops, acpi_rtc_wake); + mutex_exit(&tod_lock); + } + + /* + * Prepare for sleep ... could've done this earlier? + */ + PT(PT_SXP); + PMD(PMD_SX, ("Calling AcpiEnterSleepStatePrep(%d) ...\n", Sx)) + if (AcpiEnterSleepStatePrep(Sx) != AE_OK) { + PMD(PMD_SX, ("... failed\n!")) + goto insomnia; + } + + switch (s3ap->s3a_test_point) { + case DEVICE_SUSPEND_TO_RAM: + case LOOP_BACK_PASS: + return (0); + case LOOP_BACK_FAIL: + return (1); + default: + ASSERT(s3ap->s3a_test_point == LOOP_BACK_NONE || + s3ap->s3a_test_point == FORCE_SUSPEND_TO_RAM); + } + + /* + * Tell the hardware to sleep. + */ + PT(PT_SXE); + PMD(PMD_SX, ("Calling AcpiEnterSleepState(%d) ...\n", Sx)) + if (AcpiEnterSleepState(Sx) != AE_OK) { + PT(PT_SXE_FAIL); + PMD(PMD_SX, ("... failed!\n")) + } + +insomnia: + PT(PT_INSOM); + /* cleanup is done in the caller */ + return (1); +} + +int +acpi_exit_sleepstate(s3a_t *s3ap) +{ + int Sx = s3ap->s3a_state; + + PT(PT_WOKE); + PMD(PMD_SX, ("!We woke up!\n")) + + PT(PT_LSS); + if (AcpiLeaveSleepState(Sx) != AE_OK) { + PT(PT_LSS_FAIL); + PMD(PMD_SX, ("Problem with LeaveSleepState!\n")) + } + + PT(PT_DPB); + if (AcpiDisableEvent(ACPI_EVENT_POWER_BUTTON, 0) != AE_OK) { + PT(PT_DPB_FAIL); + PMD(PMD_SX, ("Problem w/ DisableEvent(POWER_BUTTON)\n")) + } + if (acpi_rtc_wake > 0 && + AcpiDisableEvent(ACPI_EVENT_RTC, 0) != AE_OK) { + PT(PT_DRTC_FAIL); + PMD(PMD_SX, ("Problem w/ DisableEvent(RTC)\n")) + } + + PMD(PMD_SX, ("Restore state of APICs\n")) + + /* Restore state of APICs */ + PT(PT_ACPIREINIT); + reinit_picmode(); + PT(PT_ACPIRESTORE); + + PMD(PMD_SX, ("Exiting acpi_sleepstate() => 0\n")) + + return (0); +} |