acpi_lid.c revision 1.32
11.32Sjruoho/*	$NetBSD: acpi_lid.c,v 1.32 2010/01/30 18:35:48 jruoho Exp $	*/
21.1Sthorpej
31.1Sthorpej/*
41.7Sthorpej * Copyright 2001, 2003 Wasabi Systems, Inc.
51.1Sthorpej * All rights reserved.
61.1Sthorpej *
71.1Sthorpej * Written by Jason R. Thorpe for Wasabi Systems, Inc.
81.1Sthorpej *
91.1Sthorpej * Redistribution and use in source and binary forms, with or without
101.1Sthorpej * modification, are permitted provided that the following conditions
111.1Sthorpej * are met:
121.1Sthorpej * 1. Redistributions of source code must retain the above copyright
131.1Sthorpej *    notice, this list of conditions and the following disclaimer.
141.1Sthorpej * 2. Redistributions in binary form must reproduce the above copyright
151.1Sthorpej *    notice, this list of conditions and the following disclaimer in the
161.1Sthorpej *    documentation and/or other materials provided with the distribution.
171.1Sthorpej * 3. All advertising materials mentioning features or use of this software
181.1Sthorpej *    must display the following acknowledgement:
191.1Sthorpej *	This product includes software developed for the NetBSD Project by
201.1Sthorpej *	Wasabi Systems, Inc.
211.1Sthorpej * 4. The name of Wasabi Systems, Inc. may not be used to endorse
221.1Sthorpej *    or promote products derived from this software without specific prior
231.1Sthorpej *    written permission.
241.1Sthorpej *
251.1Sthorpej * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
261.1Sthorpej * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
271.1Sthorpej * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
281.1Sthorpej * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL WASABI SYSTEMS, INC
291.1Sthorpej * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
301.1Sthorpej * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
311.1Sthorpej * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
321.1Sthorpej * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
331.1Sthorpej * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
341.1Sthorpej * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
351.1Sthorpej * POSSIBILITY OF SUCH DAMAGE.
361.1Sthorpej */
371.1Sthorpej
381.1Sthorpej/*
391.1Sthorpej * ACPI Lid Switch driver.
401.1Sthorpej */
411.3Slukem
421.3Slukem#include <sys/cdefs.h>
431.32Sjruoho__KERNEL_RCSID(0, "$NetBSD: acpi_lid.c,v 1.32 2010/01/30 18:35:48 jruoho Exp $");
441.1Sthorpej
451.1Sthorpej#include <sys/param.h>
461.1Sthorpej#include <sys/systm.h>
471.1Sthorpej#include <sys/device.h>
481.1Sthorpej
491.1Sthorpej#include <dev/acpi/acpica.h>
501.1Sthorpej#include <dev/acpi/acpireg.h>
511.1Sthorpej#include <dev/acpi/acpivar.h>
521.1Sthorpej
531.7Sthorpej#include <dev/sysmon/sysmonvar.h>
541.7Sthorpej
551.32Sjruoho#define _COMPONENT		ACPI_LID_COMPONENT
561.32SjruohoACPI_MODULE_NAME		("acpi_lid")
571.32Sjruoho
581.1Sthorpejstruct acpilid_softc {
591.1Sthorpej	struct acpi_devnode *sc_node;	/* our ACPI devnode */
601.7Sthorpej	struct sysmon_pswitch sc_smpsw;	/* our sysmon glue */
611.1Sthorpej};
621.1Sthorpej
631.10Skochistatic const char * const lid_hid[] = {
641.10Skochi	"PNP0C0D",
651.10Skochi	NULL
661.10Skochi};
671.10Skochi
681.25Sxtraemestatic int	acpilid_match(device_t, cfdata_t, void *);
691.25Sxtraemestatic void	acpilid_attach(device_t, device_t, void *);
701.26Sdyoungstatic int	acpilid_detach(device_t, int);
711.1Sthorpej
721.25SxtraemeCFATTACH_DECL_NEW(acpilid, sizeof(struct acpilid_softc),
731.26Sdyoung    acpilid_match, acpilid_attach, acpilid_detach, NULL);
741.1Sthorpej
751.15Skochistatic void	acpilid_status_changed(void *);
761.18Skochistatic void	acpilid_notify_handler(ACPI_HANDLE, UINT32, void *);
771.1Sthorpej
781.25Sxtraemestatic void	acpilid_wake_event(device_t, bool);
791.31Sdyoungstatic bool	acpilid_suspend(device_t, pmf_qual_t);
801.22Sjmcneill
811.1Sthorpej/*
821.1Sthorpej * acpilid_match:
831.1Sthorpej *
841.1Sthorpej *	Autoconfiguration `match' routine.
851.1Sthorpej */
861.15Skochistatic int
871.25Sxtraemeacpilid_match(device_t parent, cfdata_t match, void *aux)
881.1Sthorpej{
891.1Sthorpej	struct acpi_attach_args *aa = aux;
901.1Sthorpej
911.1Sthorpej	if (aa->aa_node->ad_type != ACPI_TYPE_DEVICE)
921.14Skochi		return 0;
931.1Sthorpej
941.14Skochi	return acpi_match_hid(aa->aa_node->ad_devinfo, lid_hid);
951.1Sthorpej}
961.1Sthorpej
971.1Sthorpej/*
981.1Sthorpej * acpilid_attach:
991.1Sthorpej *
1001.1Sthorpej *	Autoconfiguration `attach' routine.
1011.1Sthorpej */
1021.15Skochistatic void
1031.25Sxtraemeacpilid_attach(device_t parent, device_t self, void *aux)
1041.1Sthorpej{
1051.25Sxtraeme	struct acpilid_softc *sc = device_private(self);
1061.1Sthorpej	struct acpi_attach_args *aa = aux;
1071.1Sthorpej	ACPI_STATUS rv;
1081.1Sthorpej
1091.19Skochi	aprint_naive(": ACPI Lid Switch\n");
1101.19Skochi	aprint_normal(": ACPI Lid Switch\n");
1111.1Sthorpej
1121.1Sthorpej	sc->sc_node = aa->aa_node;
1131.1Sthorpej
1141.25Sxtraeme	sc->sc_smpsw.smpsw_name = device_xname(self);
1151.8Sthorpej	sc->sc_smpsw.smpsw_type = PSWITCH_TYPE_LID;
1161.7Sthorpej	if (sysmon_pswitch_register(&sc->sc_smpsw) != 0) {
1171.25Sxtraeme		aprint_error_dev(self, "unable to register with sysmon\n");
1181.7Sthorpej		return;
1191.7Sthorpej	}
1201.7Sthorpej
1211.1Sthorpej	rv = AcpiInstallNotifyHandler(sc->sc_node->ad_handle,
1221.25Sxtraeme	    ACPI_DEVICE_NOTIFY, acpilid_notify_handler, self);
1231.12Smycroft	if (ACPI_FAILURE(rv)) {
1241.25Sxtraeme		aprint_error_dev(self,
1251.25Sxtraeme		    "unable to register DEVICE NOTIFY handler: %s\n",
1261.25Sxtraeme		    AcpiFormatException(rv));
1271.1Sthorpej		return;
1281.1Sthorpej	}
1291.16Skochi
1301.22Sjmcneill	if (!pmf_device_register(self, acpilid_suspend, NULL))
1311.22Sjmcneill		aprint_error_dev(self, "couldn't establish power handler\n");
1321.22Sjmcneill}
1331.22Sjmcneill
1341.26Sdyoungstatic int
1351.26Sdyoungacpilid_detach(device_t self, int flags)
1361.26Sdyoung{
1371.26Sdyoung	struct acpilid_softc *sc = device_private(self);
1381.26Sdyoung	ACPI_STATUS rv;
1391.26Sdyoung
1401.26Sdyoung	rv = AcpiRemoveNotifyHandler(sc->sc_node->ad_handle,
1411.26Sdyoung	    ACPI_DEVICE_NOTIFY, acpilid_notify_handler);
1421.26Sdyoung	if (ACPI_FAILURE(rv)) {
1431.26Sdyoung		aprint_error_dev(self,
1441.26Sdyoung		    "unable to deregister DEVICE NOTIFY handler: %s\n",
1451.26Sdyoung		    AcpiFormatException(rv));
1461.26Sdyoung		return EBUSY;
1471.26Sdyoung	}
1481.26Sdyoung
1491.26Sdyoung	pmf_device_deregister(self);
1501.26Sdyoung	sysmon_pswitch_unregister(&sc->sc_smpsw);
1511.26Sdyoung
1521.26Sdyoung	return 0;
1531.26Sdyoung}
1541.26Sdyoung
1551.22Sjmcneillstatic void
1561.25Sxtraemeacpilid_wake_event(device_t dv, bool enable)
1571.22Sjmcneill{
1581.25Sxtraeme	struct acpilid_softc *sc = device_private(dv);
1591.30Sjruoho	ACPI_OBJECT_LIST arg;
1601.30Sjruoho	ACPI_OBJECT obj[3];
1611.30Sjruoho	ACPI_STATUS rv;
1621.30Sjruoho
1631.30Sjruoho	/*
1641.30Sjruoho	 * First try to call the Device Sleep Wake control method, _DSW.
1651.30Sjruoho	 * Only if this is not available, resort to to the Power State
1661.30Sjruoho	 * Wake control method, _PSW, which was deprecated in ACPI 3.0.
1671.30Sjruoho	 */
1681.30Sjruoho	obj[0].Integer.Value = enable ? 1 : 0;
1691.30Sjruoho	obj[1].Integer.Value = obj[2].Integer.Value = 0;
1701.30Sjruoho	obj[0].Type = obj[1].Type = obj[2].Type = ACPI_TYPE_INTEGER;
1711.30Sjruoho
1721.30Sjruoho	arg.Count = 3;
1731.30Sjruoho	arg.Pointer = obj;
1741.30Sjruoho
1751.30Sjruoho	rv = AcpiEvaluateObject(sc->sc_node->ad_handle, "_DSW", &arg, NULL);
1761.30Sjruoho
1771.30Sjruoho	if (ACPI_SUCCESS(rv))
1781.30Sjruoho		return;
1791.25Sxtraeme
1801.30Sjruoho	if (rv != AE_NOT_FOUND)
1811.30Sjruoho		goto fail;
1821.22Sjmcneill
1831.29Scegger	rv = acpi_eval_set_integer(sc->sc_node->ad_handle, "_PSW",
1841.29Scegger	    enable ? 1 : 0);
1851.30Sjruoho
1861.22Sjmcneill	if (ACPI_FAILURE(rv) && rv != AE_NOT_FOUND)
1871.30Sjruoho		goto fail;
1881.30Sjruoho
1891.30Sjruoho	return;
1901.30Sjruoho
1911.30Sjruohofail:
1921.30Sjruoho	aprint_error_dev(dv, "unable to evaluate wake control method: %s\n",
1931.30Sjruoho	    AcpiFormatException(rv));
1941.1Sthorpej}
1951.1Sthorpej
1961.1Sthorpej/*
1971.1Sthorpej * acpilid_status_changed:
1981.1Sthorpej *
1991.1Sthorpej *	Get, and possibly display, the lid status, and take action.
2001.1Sthorpej */
2011.15Skochistatic void
2021.1Sthorpejacpilid_status_changed(void *arg)
2031.1Sthorpej{
2041.1Sthorpej	struct acpilid_softc *sc = arg;
2051.13Skanaoka	ACPI_INTEGER status;
2061.12Smycroft	ACPI_STATUS rv;
2071.1Sthorpej
2081.12Smycroft	rv = acpi_eval_integer(sc->sc_node->ad_handle, "_LID", &status);
2091.12Smycroft	if (ACPI_FAILURE(rv))
2101.1Sthorpej		return;
2111.1Sthorpej
2121.7Sthorpej	sysmon_pswitch_event(&sc->sc_smpsw, status == 0 ?
2131.8Sthorpej	    PSWITCH_EVENT_PRESSED : PSWITCH_EVENT_RELEASED);
2141.1Sthorpej}
2151.1Sthorpej
2161.1Sthorpej/*
2171.1Sthorpej * acpilid_notify_handler:
2181.1Sthorpej *
2191.1Sthorpej *	Callback from ACPI interrupt handler to notify us of an event.
2201.1Sthorpej */
2211.15Skochistatic void
2221.25Sxtraemeacpilid_notify_handler(ACPI_HANDLE handle, UINT32 notify, void *context)
2231.1Sthorpej{
2241.25Sxtraeme	device_t dv = context;
2251.25Sxtraeme	struct acpilid_softc *sc = device_private(dv);
2261.28Sjmcneill	ACPI_STATUS rv;
2271.1Sthorpej
2281.1Sthorpej	switch (notify) {
2291.1Sthorpej	case ACPI_NOTIFY_LidStatusChanged:
2301.1Sthorpej#ifdef ACPI_LID_DEBUG
2311.1Sthorpej		printf("%s: received LidStatusChanged message\n",
2321.25Sxtraeme		    device_xname(dv));
2331.1Sthorpej#endif
2341.22Sjmcneill		rv = AcpiOsExecute(OSL_NOTIFY_HANDLER,
2351.1Sthorpej		    acpilid_status_changed, sc);
2361.12Smycroft		if (ACPI_FAILURE(rv))
2371.25Sxtraeme			aprint_error_dev(dv,
2381.25Sxtraeme			    "WARNING: unable to queue lid change "
2391.25Sxtraeme			    "callback: %s\n", AcpiFormatException(rv));
2401.1Sthorpej		break;
2411.1Sthorpej
2421.1Sthorpej	default:
2431.25Sxtraeme		aprint_debug_dev(dv,
2441.25Sxtraeme		    "received unknown notify message: 0x%x\n", notify);
2451.1Sthorpej	}
2461.1Sthorpej}
2471.22Sjmcneill
2481.22Sjmcneillstatic bool
2491.31Sdyoungacpilid_suspend(device_t dv, pmf_qual_t qual)
2501.22Sjmcneill{
2511.22Sjmcneill	struct acpilid_softc *sc = device_private(dv);
2521.22Sjmcneill	ACPI_INTEGER status;
2531.22Sjmcneill	ACPI_STATUS rv;
2541.22Sjmcneill
2551.22Sjmcneill	rv = acpi_eval_integer(sc->sc_node->ad_handle, "_LID", &status);
2561.22Sjmcneill	if (ACPI_FAILURE(rv))
2571.22Sjmcneill		return true;
2581.22Sjmcneill
2591.25Sxtraeme	acpilid_wake_event(dv, status == 0);
2601.22Sjmcneill	return true;
2611.22Sjmcneill}
262