xref: /onnv-gate/usr/src/uts/i86pc/io/ppm/acpippm.c (revision 8122:e149e036c834)
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