15295Srandyf /*
25295Srandyf * CDDL HEADER START
35295Srandyf *
45295Srandyf * The contents of this file are subject to the terms of the
55295Srandyf * Common Development and Distribution License (the "License").
65295Srandyf * You may not use this file except in compliance with the License.
75295Srandyf *
85295Srandyf * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
95295Srandyf * or http://www.opensolaris.org/os/licensing.
105295Srandyf * See the License for the specific language governing permissions
115295Srandyf * and limitations under the License.
125295Srandyf *
135295Srandyf * When distributing Covered Code, include this CDDL HEADER in each
145295Srandyf * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
155295Srandyf * If applicable, add the following below this CDDL HEADER, with the
165295Srandyf * fields enclosed by brackets "[]" replaced with your own identifying
175295Srandyf * information: Portions Copyright [yyyy] [name of copyright owner]
185295Srandyf *
195295Srandyf * CDDL HEADER END
205295Srandyf */
215295Srandyf
225295Srandyf /*
236210Sgs150176 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
245295Srandyf * Use is subject to license terms.
255295Srandyf */
265295Srandyf
275295Srandyf #include <sys/types.h>
285295Srandyf #include <sys/conf.h>
295295Srandyf #include <sys/open.h>
305295Srandyf #include <sys/modctl.h>
315295Srandyf #include <sys/promif.h>
325295Srandyf #include <sys/stat.h>
335295Srandyf #include <sys/ddi_impldefs.h>
345295Srandyf #include <sys/ddi.h>
355295Srandyf #include <sys/sunddi.h>
365295Srandyf #include <sys/epm.h>
375295Srandyf #include <sys/acpi/acpi.h>
385295Srandyf #include <sys/acpica.h>
395295Srandyf #include <sys/psm_types.h>
405295Srandyf
415295Srandyf /*
425295Srandyf * ACPI Power Management Driver
435295Srandyf *
445295Srandyf * acpippm deals with those bits of ppm functionality that
455295Srandyf * must be mediated by ACPI
465295Srandyf *
475295Srandyf * The routines in this driver is referenced by Platform
485295Srandyf * Power Management driver of X86 workstation systems.
495295Srandyf * acpippm driver is loaded because it is listed as a platform driver
505295Srandyf * It is initially configured as a pseudo driver.
515295Srandyf */
527851SDana.Myers@Sun.COM extern void pc_tod_set_rtc_offsets(ACPI_TABLE_FADT *);
53*8122SKerry.Shu@Sun.COM extern int acpica_use_safe_delay;
545295Srandyf
555295Srandyf /*
565295Srandyf * Configuration Function prototypes and data structures
575295Srandyf */
585295Srandyf static int appm_attach(dev_info_t *, ddi_attach_cmd_t);
595295Srandyf static int appm_detach(dev_info_t *, ddi_detach_cmd_t);
605295Srandyf static int appm_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
615295Srandyf static int appm_open(dev_t *dev_p, int flag, int otyp, cred_t *cred_p);
625295Srandyf static int appm_close(dev_t dev, int flag, int otyp, cred_t *cred_p);
635295Srandyf static int appm_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
645295Srandyf
655295Srandyf /*
665295Srandyf * Configuration data structures
675295Srandyf */
685295Srandyf static struct cb_ops appm_cbops = {
695295Srandyf appm_open, /* open */
705295Srandyf appm_close, /* close */
715295Srandyf nodev, /* strategy */
725295Srandyf nodev, /* print */
735295Srandyf nodev, /* dump */
745295Srandyf nodev, /* read */
755295Srandyf nodev, /* write */
765295Srandyf appm_ioctl, /* ioctl */
775295Srandyf nodev, /* devmap */
785295Srandyf nodev, /* mmap */
795295Srandyf nodev, /* segmap */
805295Srandyf nochpoll, /* chpoll */
815295Srandyf ddi_prop_op, /* prop_op */
825295Srandyf NULL, /* stream */
835295Srandyf D_MP | D_NEW, /* flag */
845295Srandyf CB_REV, /* rev */
855295Srandyf nodev, /* aread */
865295Srandyf nodev, /* awrite */
875295Srandyf };
885295Srandyf
895295Srandyf static struct dev_ops appm_ops = {
905295Srandyf DEVO_REV, /* devo_rev */
915295Srandyf 0, /* refcnt */
925295Srandyf appm_getinfo, /* getinfo */
935295Srandyf nulldev, /* identify */
945295Srandyf nulldev, /* probe */
955295Srandyf appm_attach, /* attach */
965295Srandyf appm_detach, /* detach */
975295Srandyf nodev, /* reset */
985295Srandyf &appm_cbops, /* cb_ops */
995295Srandyf NULL, /* bus_ops */
1007656SSherry.Moore@Sun.COM NULL, /* power */
1017656SSherry.Moore@Sun.COM ddi_quiesce_not_needed, /* quiesce */
1025295Srandyf };
1035295Srandyf
1045295Srandyf extern struct mod_ops mod_driverops;
1055295Srandyf
1065295Srandyf static struct modldrv modldrv = {
1075295Srandyf &mod_driverops,
1087656SSherry.Moore@Sun.COM "ACPI ppm driver",
1095295Srandyf &appm_ops,
1105295Srandyf };
1115295Srandyf
1125295Srandyf static struct modlinkage modlinkage = {
1135295Srandyf MODREV_1,
1145295Srandyf &modldrv,
1155295Srandyf NULL
1165295Srandyf };
1175295Srandyf
1185295Srandyf /*
1195295Srandyf * Driver state structure
1205295Srandyf */
1215295Srandyf typedef struct {
1225295Srandyf dev_info_t *dip;
1235295Srandyf ddi_acc_handle_t devid_hndl;
1245295Srandyf ddi_acc_handle_t estar_hndl;
1255295Srandyf int lyropen; /* ref count */
1265295Srandyf } appm_unit;
1275295Srandyf
1285295Srandyf /*
1295295Srandyf * Driver global variables
1305295Srandyf *
1315295Srandyf * appm_lock synchronize the access of lyr handle to each appm
1325295Srandyf * minor device, therefore write to tomatillo device is
1335295Srandyf * sequentialized. Lyr protocol requires pairing up lyr open
1345295Srandyf * and close, so only a single reference is allowed per minor node.
1355295Srandyf */
1365295Srandyf static void *appm_statep;
1375295Srandyf static kmutex_t appm_lock;
1385295Srandyf
1395295Srandyf /*
1405295Srandyf * S3 stuff:
1415295Srandyf */
1425295Srandyf char _depends_on[] = "misc/acpica";
1435295Srandyf
1445295Srandyf extern int acpi_enter_sleepstate(s3a_t *);
1455295Srandyf extern int acpi_exit_sleepstate(s3a_t *);
1465295Srandyf
1475295Srandyf
1485295Srandyf int
_init(void)1495295Srandyf _init(void)
1505295Srandyf {
1515295Srandyf int error;
1525295Srandyf
1535295Srandyf if ((error = ddi_soft_state_init(&appm_statep,
1545295Srandyf sizeof (appm_unit), 0)) != DDI_SUCCESS) {
1555295Srandyf return (error);
1565295Srandyf }
1575295Srandyf
1585295Srandyf mutex_init(&appm_lock, NULL, MUTEX_DRIVER, NULL);
1595295Srandyf
1605295Srandyf if ((error = mod_install(&modlinkage)) != DDI_SUCCESS) {
1615295Srandyf mutex_destroy(&appm_lock);
1625295Srandyf ddi_soft_state_fini(&appm_statep);
1635295Srandyf return (error);
1645295Srandyf }
1655295Srandyf
1665295Srandyf return (error);
1675295Srandyf }
1685295Srandyf
1695295Srandyf int
_fini(void)1705295Srandyf _fini(void)
1715295Srandyf {
1725295Srandyf int error;
1735295Srandyf
1745295Srandyf if ((error = mod_remove(&modlinkage)) == DDI_SUCCESS) {
1755295Srandyf mutex_destroy(&appm_lock);
1765295Srandyf ddi_soft_state_fini(&appm_statep);
1775295Srandyf }
1785295Srandyf
1795295Srandyf return (error);
1805295Srandyf
1815295Srandyf }
1825295Srandyf
1835295Srandyf int
_info(struct modinfo * modinfop)1845295Srandyf _info(struct modinfo *modinfop)
1855295Srandyf {
1865295Srandyf return (mod_info(&modlinkage, modinfop));
1875295Srandyf }
1885295Srandyf
1895295Srandyf
1905295Srandyf
1915295Srandyf /*
1925295Srandyf * Driver attach(9e) entry point
1935295Srandyf */
1945295Srandyf static int
appm_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)1955295Srandyf appm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
1965295Srandyf {
1975295Srandyf char *str = "appm_attach";
1985295Srandyf int instance;
1995295Srandyf appm_unit *unitp;
2007851SDana.Myers@Sun.COM ACPI_TABLE_FADT *fadt = NULL;
2015295Srandyf int rv = DDI_SUCCESS;
2025295Srandyf
2035295Srandyf switch (cmd) {
2045295Srandyf case DDI_ATTACH:
2055295Srandyf break;
2065295Srandyf case DDI_RESUME:
2075295Srandyf return (DDI_SUCCESS);
2085295Srandyf default:
2095295Srandyf cmn_err(CE_WARN, "%s: cmd %d unsupported.\n", str, cmd);
2105295Srandyf return (DDI_FAILURE);
2115295Srandyf }
2125295Srandyf
2135295Srandyf instance = ddi_get_instance(dip);
2145295Srandyf rv = ddi_soft_state_zalloc(appm_statep, instance);
2155295Srandyf if (rv != DDI_SUCCESS) {
2165295Srandyf cmn_err(CE_WARN, "%s: failed alloc for dev(%s@%s)",
2175295Srandyf str, ddi_binding_name(dip),
2185295Srandyf ddi_get_name_addr(dip) ? ddi_get_name_addr(dip) : " ");
2195295Srandyf return (rv);
2205295Srandyf }
2215295Srandyf
2225295Srandyf if ((unitp = ddi_get_soft_state(appm_statep, instance)) == NULL) {
2235295Srandyf rv = DDI_FAILURE;
2245295Srandyf goto doerrs;
2255295Srandyf }
2265295Srandyf
2275295Srandyf /*
2285295Srandyf * Export "ddi-kernel-ioctl" property - prepared to support
2295295Srandyf * kernel ioctls (driver layering).
2305295Srandyf * XXX is this still needed?
2315295Srandyf * XXXX (RSF) Not that I am aware of.
2325295Srandyf */
2335295Srandyf rv = ddi_prop_create(DDI_DEV_T_NONE, dip, DDI_PROP_CANSLEEP,
2345295Srandyf DDI_KERNEL_IOCTL, NULL, 0);
2355295Srandyf if (rv != DDI_PROP_SUCCESS)
2365295Srandyf goto doerrs;
2375295Srandyf
2385295Srandyf ddi_report_dev(dip);
2395295Srandyf unitp->dip = dip;
2405295Srandyf
2415295Srandyf /*
2425295Srandyf * XXX here we would do whatever we need to to determine if the
2435295Srandyf * XXX platform supports ACPI, and fail the attach if not.
2445295Srandyf * XXX If it does, we do whatever setup is needed to get access to
2455295Srandyf * XXX ACPI register space.
2465295Srandyf */
2475295Srandyf
2485295Srandyf unitp->lyropen = 0;
2495295Srandyf
2505295Srandyf /*
2515295Srandyf * create minor node for kernel_ioctl calls
2525295Srandyf */
2535295Srandyf rv = ddi_create_minor_node(dip, "acpi-ppm", S_IFCHR, instance, 0, 0);
2545295Srandyf if (rv != DDI_SUCCESS)
2555295Srandyf goto doerrs;
2565295Srandyf
2576324Sgs150176 /* Get the FADT */
2587851SDana.Myers@Sun.COM if (AcpiGetTable(ACPI_SIG_FADT, 1,
2596324Sgs150176 (ACPI_TABLE_HEADER **)&fadt) != AE_OK)
2606324Sgs150176 return (rv);
2616324Sgs150176
2626324Sgs150176 /* Init the RTC offsets */
2636324Sgs150176 if (fadt != NULL)
2646324Sgs150176 pc_tod_set_rtc_offsets(fadt);
2656210Sgs150176
2665295Srandyf return (rv);
2675295Srandyf
2685295Srandyf doerrs:
2695295Srandyf
2705295Srandyf if (ddi_prop_exists(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS |
2715295Srandyf DDI_PROP_NOTPROM, DDI_KERNEL_IOCTL))
2725295Srandyf ddi_prop_remove_all(dip);
2735295Srandyf
2745295Srandyf ddi_soft_state_free(appm_statep, instance);
2755295Srandyf
2765295Srandyf return (rv);
2775295Srandyf }
2785295Srandyf
2795295Srandyf
2805295Srandyf /*
2815295Srandyf * Driver getinfo(9e) entry routine
2825295Srandyf */
2835295Srandyf /* ARGSUSED */
2845295Srandyf static int
appm_getinfo(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** result)2855295Srandyf appm_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
2865295Srandyf {
2875295Srandyf appm_unit *unitp;
2885295Srandyf int instance;
2895295Srandyf
2905295Srandyf switch (cmd) {
2915295Srandyf case DDI_INFO_DEVT2DEVINFO:
2925295Srandyf instance = getminor((dev_t)arg);
2935295Srandyf unitp = ddi_get_soft_state(appm_statep, instance);
2945295Srandyf if (unitp == NULL) {
2955295Srandyf return (DDI_FAILURE);
2965295Srandyf }
2975295Srandyf *result = (void *) unitp->dip;
2985295Srandyf return (DDI_SUCCESS);
2995295Srandyf
3005295Srandyf case DDI_INFO_DEVT2INSTANCE:
3015295Srandyf instance = getminor((dev_t)arg);
3025295Srandyf *result = (void *)(uintptr_t)instance;
3035295Srandyf return (DDI_SUCCESS);
3045295Srandyf
3055295Srandyf default:
3065295Srandyf return (DDI_FAILURE);
3075295Srandyf }
3085295Srandyf }
3095295Srandyf
3105295Srandyf
3115295Srandyf /*
3125295Srandyf * detach(9e)
3135295Srandyf */
3145295Srandyf /* ARGSUSED */
3155295Srandyf static int
appm_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)3165295Srandyf appm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
3175295Srandyf {
3185295Srandyf char *str = "appm_detach";
3195295Srandyf
3205295Srandyf switch (cmd) {
3215295Srandyf case DDI_DETACH:
3225295Srandyf return (DDI_FAILURE);
3235295Srandyf case DDI_SUSPEND:
3245295Srandyf return (DDI_SUCCESS);
3255295Srandyf default:
3265295Srandyf cmn_err(CE_WARN, "%s: cmd %d unsupported", str, cmd);
3275295Srandyf return (DDI_FAILURE);
3285295Srandyf }
3295295Srandyf }
3305295Srandyf
3315295Srandyf
3325295Srandyf /* ARGSUSED */
3335295Srandyf static int
appm_open(dev_t * dev_p,int flag,int otyp,cred_t * cred_p)3345295Srandyf appm_open(dev_t *dev_p, int flag, int otyp, cred_t *cred_p)
3355295Srandyf {
3365295Srandyf appm_unit *unitp;
3375295Srandyf
3385295Srandyf /* not intended to allow sysadmin level root process to open it */
3395295Srandyf if (drv_priv(cred_p) != DDI_SUCCESS)
3405295Srandyf return (EPERM);
3415295Srandyf
3425295Srandyf if ((unitp = ddi_get_soft_state(
3435295Srandyf appm_statep, getminor(*dev_p))) == NULL) {
3445295Srandyf cmn_err(CE_WARN, "appm_open: failed to get soft state!");
3455295Srandyf return (DDI_FAILURE);
3465295Srandyf }
3475295Srandyf
3485295Srandyf mutex_enter(&appm_lock);
3495295Srandyf if (unitp->lyropen != 0) {
3505295Srandyf mutex_exit(&appm_lock);
3515295Srandyf return (EBUSY);
3525295Srandyf }
3535295Srandyf unitp->lyropen++;
3545295Srandyf mutex_exit(&appm_lock);
3555295Srandyf
3565295Srandyf return (DDI_SUCCESS);
3575295Srandyf }
3585295Srandyf
3595295Srandyf
3605295Srandyf /* ARGSUSED */
3615295Srandyf static int
appm_close(dev_t dev,int flag,int otyp,cred_t * cred_p)3625295Srandyf appm_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
3635295Srandyf {
3645295Srandyf appm_unit *unitp;
3655295Srandyf
3665295Srandyf if ((unitp =
3675295Srandyf ddi_get_soft_state(appm_statep, getminor(dev))) == NULL)
3685295Srandyf return (DDI_FAILURE);
3695295Srandyf
3705295Srandyf mutex_enter(&appm_lock);
3715295Srandyf unitp->lyropen = 0;
3725295Srandyf mutex_exit(&appm_lock);
3735295Srandyf
3745295Srandyf return (DDI_SUCCESS);
3755295Srandyf }
3765295Srandyf
3775295Srandyf
3785295Srandyf /*
3795295Srandyf * must match ppm.conf
3805295Srandyf */
3815295Srandyf #define APPMIOC ('A' << 8)
3825295Srandyf #define APPMIOC_ENTER_S3 (APPMIOC | 1) /* arg *s3a_t */
3835295Srandyf #define APPMIOC_EXIT_S3 (APPMIOC | 2) /* arg *s3a_t */
3845295Srandyf
3855295Srandyf /* ARGSUSED3 */
3865295Srandyf static int
appm_ioctl(dev_t dev,int cmd,intptr_t arg,int flag,cred_t * cred_p,int * rval_p)3875295Srandyf appm_ioctl(dev_t dev, int cmd, intptr_t arg, int flag,
3885295Srandyf cred_t *cred_p, int *rval_p)
3895295Srandyf {
3905295Srandyf static boolean_t acpi_initted = B_FALSE;
3915295Srandyf char *str = "appm_ioctl";
3925295Srandyf int ret;
3935295Srandyf s3a_t *s3ap = (s3a_t *)arg;
3945295Srandyf
3955295Srandyf PMD(PMD_SX, ("%s: called with %x\n", str, cmd))
3965295Srandyf
3975295Srandyf if (drv_priv(cred_p) != 0) {
3985295Srandyf PMD(PMD_SX, ("%s: EPERM\n", str))
3995295Srandyf return (EPERM);
4005295Srandyf }
4015295Srandyf
4025295Srandyf if (ddi_get_soft_state(appm_statep, getminor(dev)) == NULL) {
4035295Srandyf PMD(PMD_SX, ("%s: no soft state: EIO\n", str))
4045295Srandyf return (EIO);
4055295Srandyf }
4065295Srandyf
4075295Srandyf if (!acpi_initted) {
4085295Srandyf PMD(PMD_SX, ("%s: !acpi_initted\n", str))
4095295Srandyf if (acpica_init() == 0) {
4105295Srandyf acpi_initted = B_TRUE;
4115295Srandyf } else {
4125295Srandyf if (rval_p != NULL) {
4135295Srandyf *rval_p = EINVAL;
4145295Srandyf }
4155295Srandyf PMD(PMD_SX, ("%s: EINVAL\n", str))
4165295Srandyf return (EINVAL);
4175295Srandyf }
4185295Srandyf }
4195295Srandyf
4205295Srandyf PMD(PMD_SX, ("%s: looking for cmd %x\n", str, cmd))
4215295Srandyf switch (cmd) {
4225295Srandyf case APPMIOC_ENTER_S3:
4235295Srandyf /*
4245295Srandyf * suspend to RAM (ie S3)
4255295Srandyf */
4265295Srandyf PMD(PMD_SX, ("%s: cmd %x, arg %p\n", str, cmd, (void *)arg))
427*8122SKerry.Shu@Sun.COM acpica_use_safe_delay = 1;
4285295Srandyf ret = acpi_enter_sleepstate(s3ap);
4295295Srandyf break;
4305295Srandyf
4315295Srandyf case APPMIOC_EXIT_S3:
4325295Srandyf /*
4335295Srandyf * return from S3
4345295Srandyf */
4355295Srandyf PMD(PMD_SX, ("%s: cmd %x, arg %p\n", str, cmd, (void *)arg))
4365295Srandyf ret = acpi_exit_sleepstate(s3ap);
437*8122SKerry.Shu@Sun.COM acpica_use_safe_delay = 0;
4385295Srandyf break;
4395295Srandyf
4405295Srandyf default:
4415295Srandyf PMD(PMD_SX, ("%s: cmd %x unrecognized: ENOTTY\n", str, cmd))
4425295Srandyf return (ENOTTY);
4435295Srandyf }
4445295Srandyf
4455295Srandyf /*
4465295Srandyf * upon failure return EINVAL
4475295Srandyf */
4485295Srandyf if (ret != 0) {
4495295Srandyf if (rval_p != NULL) {
4505295Srandyf *rval_p = EINVAL;
4515295Srandyf }
4525295Srandyf return (EINVAL);
4535295Srandyf }
4545295Srandyf
4555295Srandyf return (0);
4565295Srandyf }
457