1*8cccc96eSthorpej /* $NetBSD: acpi_acad.c,v 1.52 2021/01/29 15:20:13 thorpej Exp $ */
22945805fSthorpej
32945805fSthorpej /*
42945805fSthorpej * Copyright 2001 Wasabi Systems, Inc.
52945805fSthorpej * All rights reserved.
62945805fSthorpej *
72945805fSthorpej * Written by Jason R. Thorpe for Wasabi Systems, Inc.
82945805fSthorpej *
92945805fSthorpej * Redistribution and use in source and binary forms, with or without
102945805fSthorpej * modification, are permitted provided that the following conditions
112945805fSthorpej * are met:
122945805fSthorpej * 1. Redistributions of source code must retain the above copyright
132945805fSthorpej * notice, this list of conditions and the following disclaimer.
142945805fSthorpej * 2. Redistributions in binary form must reproduce the above copyright
152945805fSthorpej * notice, this list of conditions and the following disclaimer in the
162945805fSthorpej * documentation and/or other materials provided with the distribution.
172945805fSthorpej * 3. All advertising materials mentioning features or use of this software
182945805fSthorpej * must display the following acknowledgement:
192945805fSthorpej * This product includes software developed for the NetBSD Project by
202945805fSthorpej * Wasabi Systems, Inc.
212945805fSthorpej * 4. The name of Wasabi Systems, Inc. may not be used to endorse
222945805fSthorpej * or promote products derived from this software without specific prior
232945805fSthorpej * written permission.
242945805fSthorpej *
252945805fSthorpej * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
262945805fSthorpej * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
272945805fSthorpej * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
282945805fSthorpej * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
292945805fSthorpej * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
302945805fSthorpej * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
312945805fSthorpej * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
322945805fSthorpej * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
332945805fSthorpej * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
342945805fSthorpej * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
352945805fSthorpej * POSSIBILITY OF SUCH DAMAGE.
362945805fSthorpej */
372945805fSthorpej
382945805fSthorpej /*
392945805fSthorpej * ACPI AC Adapter driver.
402945805fSthorpej */
412945805fSthorpej
4213ac4302Slukem #include <sys/cdefs.h>
43*8cccc96eSthorpej __KERNEL_RCSID(0, "$NetBSD: acpi_acad.c,v 1.52 2021/01/29 15:20:13 thorpej Exp $");
4413ac4302Slukem
452945805fSthorpej #include <sys/param.h>
462945805fSthorpej #include <sys/device.h>
4786c30149Sjruoho #include <sys/module.h>
485a425210Sjruoho #include <sys/systm.h>
492945805fSthorpej
502945805fSthorpej #include <dev/acpi/acpireg.h>
512945805fSthorpej #include <dev/acpi/acpivar.h>
522945805fSthorpej
53d5693430Sjruoho #define _COMPONENT ACPI_ACAD_COMPONENT
54d5693430Sjruoho ACPI_MODULE_NAME ("acpi_acad")
55d5693430Sjruoho
56f04ee530Sjruoho #define ACPI_NOTIFY_ACAD 0x80
57f04ee530Sjruoho #define ACPI_NOTIFY_ACAD_2 0x81 /* XXX. */
58f04ee530Sjruoho
592945805fSthorpej struct acpiacad_softc {
60d9ca089dSjruoho struct acpi_devnode *sc_node;
6131962fc6Sxtraeme struct sysmon_envsys *sc_sme;
62d9ca089dSjruoho struct sysmon_pswitch sc_smpsw;
6331962fc6Sxtraeme envsys_data_t sc_sensor;
64d9ca089dSjruoho int sc_status;
652945805fSthorpej };
662945805fSthorpej
67*8cccc96eSthorpej static const struct device_compatible_entry compat_data[] = {
68*8cccc96eSthorpej { .compat = "ACPI0003" },
69*8cccc96eSthorpej DEVICE_COMPAT_EOL
7084795bd3Skochi };
7184795bd3Skochi
72e5339e11Scegger static int acpiacad_match(device_t, cfdata_t, void *);
73649a2156Sjoerg static void acpiacad_attach(device_t, device_t, void *);
746c41d7d7Sjruoho static int acpiacad_detach(device_t, int);
75c1b390d4Sdyoung static bool acpiacad_resume(device_t, const pmf_qual_t *);
76d9ca089dSjruoho static void acpiacad_get_status(void *);
77d9ca089dSjruoho static void acpiacad_notify_handler(ACPI_HANDLE, uint32_t, void *);
78d9ca089dSjruoho static void acpiacad_init_envsys(device_t);
792945805fSthorpej
806c41d7d7Sjruoho CFATTACH_DECL_NEW(acpiacad, sizeof(struct acpiacad_softc),
816c41d7d7Sjruoho acpiacad_match, acpiacad_attach, acpiacad_detach, NULL);
826c41d7d7Sjruoho
832945805fSthorpej /*
842945805fSthorpej * acpiacad_match:
852945805fSthorpej *
862945805fSthorpej * Autoconfiguration `match' routine.
872945805fSthorpej */
88beb4a7feSkochi static int
acpiacad_match(device_t parent,cfdata_t match,void * aux)89e5339e11Scegger acpiacad_match(device_t parent, cfdata_t match, void *aux)
902945805fSthorpej {
912945805fSthorpej struct acpi_attach_args *aa = aux;
922945805fSthorpej
93*8cccc96eSthorpej return acpi_compatible_match(aa, compat_data);
942945805fSthorpej }
952945805fSthorpej
962945805fSthorpej /*
972945805fSthorpej * acpiacad_attach:
982945805fSthorpej *
992945805fSthorpej * Autoconfiguration `attach' routine.
1002945805fSthorpej */
101beb4a7feSkochi static void
acpiacad_attach(device_t parent,device_t self,void * aux)102649a2156Sjoerg acpiacad_attach(device_t parent, device_t self, void *aux)
1032945805fSthorpej {
104649a2156Sjoerg struct acpiacad_softc *sc = device_private(self);
1052945805fSthorpej struct acpi_attach_args *aa = aux;
1062945805fSthorpej
1076f202593Skochi aprint_naive(": ACPI AC Adapter\n");
1086f202593Skochi aprint_normal(": ACPI AC Adapter\n");
1092945805fSthorpej
110d9ca089dSjruoho sc->sc_sme = NULL;
111d9ca089dSjruoho sc->sc_status = -1;
1122945805fSthorpej sc->sc_node = aa->aa_node;
113d9ca089dSjruoho
11455052ecaSjruoho acpiacad_init_envsys(self);
1152945805fSthorpej
116649a2156Sjoerg sc->sc_smpsw.smpsw_name = device_xname(self);
117e48192f9Skochi sc->sc_smpsw.smpsw_type = PSWITCH_TYPE_ACADAPTER;
118e48192f9Skochi
119d9ca089dSjruoho (void)sysmon_pswitch_register(&sc->sc_smpsw);
120d9ca089dSjruoho (void)pmf_device_register(self, NULL, acpiacad_resume);
12155052ecaSjruoho (void)acpi_register_notify(sc->sc_node, acpiacad_notify_handler);
1222945805fSthorpej }
1232945805fSthorpej
1242945805fSthorpej /*
1256c41d7d7Sjruoho * acpiacad_detach:
1266c41d7d7Sjruoho *
1276c41d7d7Sjruoho * Autoconfiguration `detach' routine.
1286c41d7d7Sjruoho */
1296c41d7d7Sjruoho static int
acpiacad_detach(device_t self,int flags)1306c41d7d7Sjruoho acpiacad_detach(device_t self, int flags)
1316c41d7d7Sjruoho {
1326c41d7d7Sjruoho struct acpiacad_softc *sc = device_private(self);
1336c41d7d7Sjruoho
13455052ecaSjruoho acpi_deregister_notify(sc->sc_node);
1356c41d7d7Sjruoho
1366c41d7d7Sjruoho if (sc->sc_sme != NULL)
1376c41d7d7Sjruoho sysmon_envsys_unregister(sc->sc_sme);
1386c41d7d7Sjruoho
1396c41d7d7Sjruoho pmf_device_deregister(self);
1406c41d7d7Sjruoho sysmon_pswitch_unregister(&sc->sc_smpsw);
1416c41d7d7Sjruoho
1426c41d7d7Sjruoho return 0;
1436c41d7d7Sjruoho }
1446c41d7d7Sjruoho
1456c41d7d7Sjruoho /*
146a7e66972Sxtraeme * acpiacad_resume:
147a7e66972Sxtraeme *
148d9ca089dSjruoho * Queue a new status check.
149a7e66972Sxtraeme */
150a7e66972Sxtraeme static bool
acpiacad_resume(device_t dv,const pmf_qual_t * qual)151c1b390d4Sdyoung acpiacad_resume(device_t dv, const pmf_qual_t *qual)
152a7e66972Sxtraeme {
153a7e66972Sxtraeme
154c96fe4baSjruoho (void)AcpiOsExecute(OSL_NOTIFY_HANDLER, acpiacad_get_status, dv);
155d9ca089dSjruoho
156a7e66972Sxtraeme return true;
157a7e66972Sxtraeme }
158a7e66972Sxtraeme
159a7e66972Sxtraeme /*
1602945805fSthorpej * acpiacad_get_status:
1612945805fSthorpej *
1622945805fSthorpej * Get, and possibly display, the current AC line status.
1632945805fSthorpej */
164beb4a7feSkochi static void
acpiacad_get_status(void * arg)1652945805fSthorpej acpiacad_get_status(void *arg)
1662945805fSthorpej {
167649a2156Sjoerg device_t dv = arg;
168649a2156Sjoerg struct acpiacad_softc *sc = device_private(dv);
1695886bca6Skanaoka ACPI_INTEGER status;
1704ac0e3e5Smycroft ACPI_STATUS rv;
1712945805fSthorpej
172d9ca089dSjruoho rv = acpi_eval_integer(sc->sc_node->ad_handle, "_PSR", &status);
173d9ca089dSjruoho
174d9ca089dSjruoho if (ACPI_FAILURE(rv))
175d9ca089dSjruoho goto fail;
176d9ca089dSjruoho
177d9ca089dSjruoho if (status != 0 && status != 1) {
178d9ca089dSjruoho rv = AE_BAD_VALUE;
179d9ca089dSjruoho goto fail;
180da3f163bSxtraeme }
18159e0238fSexplorer
18270e14dc9Sjruoho if (sc->sc_status != (int)status) {
183d9ca089dSjruoho
184e48192f9Skochi /*
185d9ca089dSjruoho * If status has changed, send the event:
186da3f163bSxtraeme *
187d9ca089dSjruoho * PSWITCH_EVENT_PRESSED : _PSR = 1 : AC online.
188d9ca089dSjruoho * PSWITCH_EVENT_RELEASED : _PSR = 0 : AC offline.
189e48192f9Skochi */
190d9ca089dSjruoho sysmon_pswitch_event(&sc->sc_smpsw, (status != 0) ?
191da3f163bSxtraeme PSWITCH_EVENT_PRESSED : PSWITCH_EVENT_RELEASED);
192d9ca089dSjruoho
193d9ca089dSjruoho aprint_debug_dev(dv, "AC adapter %sconnected\n",
194649a2156Sjoerg status == 0 ? "not " : "");
195f27a62daStshiozak }
196f27a62daStshiozak
197d9ca089dSjruoho sc->sc_status = status;
198d9ca089dSjruoho sc->sc_sensor.state = ENVSYS_SVALID;
199d9ca089dSjruoho sc->sc_sensor.value_cur = sc->sc_status;
200d9ca089dSjruoho
201d9ca089dSjruoho return;
202d9ca089dSjruoho
203d9ca089dSjruoho fail:
204d9ca089dSjruoho sc->sc_status = -1;
20531962fc6Sxtraeme sc->sc_sensor.state = ENVSYS_SINVALID;
206d9ca089dSjruoho
207d9ca089dSjruoho aprint_debug_dev(dv, "failed to evaluate _PSR: %s\n",
208d9ca089dSjruoho AcpiFormatException(rv));
2092945805fSthorpej }
2102945805fSthorpej
2112945805fSthorpej /*
2122945805fSthorpej * acpiacad_notify_handler:
2132945805fSthorpej *
2142945805fSthorpej * Callback from ACPI interrupt handler to notify us of an event.
2152945805fSthorpej */
216beb4a7feSkochi static void
acpiacad_notify_handler(ACPI_HANDLE handle,uint32_t notify,void * context)217d9ca089dSjruoho acpiacad_notify_handler(ACPI_HANDLE handle, uint32_t notify, void *context)
2182945805fSthorpej {
219d9ca089dSjruoho static const int handler = OSL_NOTIFY_HANDLER;
220649a2156Sjoerg device_t dv = context;
2212945805fSthorpej
2222945805fSthorpej switch (notify) {
2232945805fSthorpej /*
2242945805fSthorpej * XXX So, BusCheck is not exactly what I would expect,
2252945805fSthorpej * but at least my IBM T21 sends it on AC adapter status
2262945805fSthorpej * change. --thorpej@wasabisystems.com
2272945805fSthorpej */
2285cd433e9Srpaulo /*
2295cd433e9Srpaulo * XXX My Acer TravelMate 291 sends DeviceCheck on AC
2305cd433e9Srpaulo * adapter status change.
2315cd433e9Srpaulo * --rpaulo@NetBSD.org
2325cd433e9Srpaulo */
233f4e11163Sjmcneill /*
234f04ee530Sjruoho * XXX Sony VAIO VGN-N250E sends 0x81 on AC adapter status change.
235f4e11163Sjmcneill * --jmcneill@NetBSD.org
236f4e11163Sjmcneill */
237f04ee530Sjruoho case ACPI_NOTIFY_ACAD:
238f04ee530Sjruoho case ACPI_NOTIFY_ACAD_2:
239f04ee530Sjruoho case ACPI_NOTIFY_BUS_CHECK:
240f04ee530Sjruoho case ACPI_NOTIFY_DEVICE_CHECK:
241d9ca089dSjruoho (void)AcpiOsExecute(handler, acpiacad_get_status, dv);
2422945805fSthorpej break;
2432945805fSthorpej
244b6050c68Sjruoho case ACPI_NOTIFY_DEVICE_WAKE:
245b6050c68Sjruoho break;
246b6050c68Sjruoho
2472945805fSthorpej default:
248b6050c68Sjruoho aprint_debug_dev(dv, "unknown notify 0x%02X\n", notify);
2492945805fSthorpej }
2502945805fSthorpej }
25159e0238fSexplorer
252beb4a7feSkochi static void
acpiacad_init_envsys(device_t dv)253649a2156Sjoerg acpiacad_init_envsys(device_t dv)
25459e0238fSexplorer {
255649a2156Sjoerg struct acpiacad_softc *sc = device_private(dv);
256649a2156Sjoerg
25731962fc6Sxtraeme sc->sc_sme = sysmon_envsys_create();
258d9ca089dSjruoho
259d9ca089dSjruoho sc->sc_sensor.state = ENVSYS_SINVALID;
26031962fc6Sxtraeme sc->sc_sensor.units = ENVSYS_INDICATOR;
261d9ca089dSjruoho
262d9ca089dSjruoho (void)strlcpy(sc->sc_sensor.desc, "connected", ENVSYS_DESCLEN);
26359e0238fSexplorer
2646c41d7d7Sjruoho if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor) != 0)
2656c41d7d7Sjruoho goto fail;
26659e0238fSexplorer
26731962fc6Sxtraeme sc->sc_sme->sme_name = device_xname(dv);
26831962fc6Sxtraeme sc->sc_sme->sme_class = SME_CLASS_ACADAPTER;
269d9ca089dSjruoho sc->sc_sme->sme_flags = SME_DISABLE_REFRESH;
27031962fc6Sxtraeme
2716c41d7d7Sjruoho if (sysmon_envsys_register(sc->sc_sme) != 0)
2726c41d7d7Sjruoho goto fail;
2736c41d7d7Sjruoho
274d9ca089dSjruoho (void)AcpiOsExecute(OSL_NOTIFY_HANDLER, acpiacad_get_status, dv);
275d9ca089dSjruoho
2766c41d7d7Sjruoho return;
2776c41d7d7Sjruoho
2786c41d7d7Sjruoho fail:
2796c41d7d7Sjruoho aprint_error_dev(dv, "failed to initialize sysmon\n");
280308501aaSjruoho
28131962fc6Sxtraeme sysmon_envsys_destroy(sc->sc_sme);
2826c41d7d7Sjruoho sc->sc_sme = NULL;
28331962fc6Sxtraeme }
28486c30149Sjruoho
28576a2f91aSpgoyette MODULE(MODULE_CLASS_DRIVER, acpiacad, "sysmon_envsys,sysmon_power");
28686c30149Sjruoho
2872b0b13f2Sjruoho #ifdef _MODULE
2882b0b13f2Sjruoho #include "ioconf.c"
2892b0b13f2Sjruoho #endif
29086c30149Sjruoho
29186c30149Sjruoho static int
acpiacad_modcmd(modcmd_t cmd,void * aux)2922b0b13f2Sjruoho acpiacad_modcmd(modcmd_t cmd, void *aux)
29386c30149Sjruoho {
2942b0b13f2Sjruoho int rv = 0;
29586c30149Sjruoho
29686c30149Sjruoho switch (cmd) {
29786c30149Sjruoho
29886c30149Sjruoho case MODULE_CMD_INIT:
29986c30149Sjruoho
3002b0b13f2Sjruoho #ifdef _MODULE
3012b0b13f2Sjruoho rv = config_init_component(cfdriver_ioconf_acpiacad,
3022b0b13f2Sjruoho cfattach_ioconf_acpiacad, cfdata_ioconf_acpiacad);
3032b0b13f2Sjruoho #endif
3042b0b13f2Sjruoho break;
30586c30149Sjruoho
30686c30149Sjruoho case MODULE_CMD_FINI:
30786c30149Sjruoho
3082b0b13f2Sjruoho #ifdef _MODULE
3092b0b13f2Sjruoho rv = config_fini_component(cfdriver_ioconf_acpiacad,
3102b0b13f2Sjruoho cfattach_ioconf_acpiacad, cfdata_ioconf_acpiacad);
3112b0b13f2Sjruoho #endif
3122b0b13f2Sjruoho break;
31386c30149Sjruoho
31486c30149Sjruoho default:
3152b0b13f2Sjruoho rv = ENOTTY;
31686c30149Sjruoho }
31786c30149Sjruoho
3182b0b13f2Sjruoho return rv;
3192b0b13f2Sjruoho }
320