1 /* $NetBSD: asus_acpi.c,v 1.11 2009/09/16 10:47:55 mlelstv Exp $ */
4 * Copyright (c) 2007, 2008, 2009 Jared D. McNeill <jmcneill@invisible.ca>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: asus_acpi.c,v 1.11 2009/09/16 10:47:55 mlelstv Exp $");
32 #include <sys/types.h>
33 #include <sys/param.h>
34 #include <sys/malloc.h>
36 #include <sys/callout.h>
37 #include <sys/kernel.h>
38 #include <sys/device.h>
39 #include <sys/sysctl.h>
41 #include <dev/acpi/acpivar.h>
42 #include <dev/acpi/acpireg.h>
44 #define _COMPONENT ACPI_RESOURCE_COMPONENT
45 ACPI_MODULE_NAME ("asus_acpi")
49 struct acpi_devnode
*sc_node
;
51 #define ASUS_PSW_DISPLAY_CYCLE 0
52 #define ASUS_PSW_LAST 1
53 struct sysmon_pswitch sc_smpsw
[ASUS_PSW_LAST
];
56 struct sysmon_envsys
*sc_sme
;
57 #define ASUS_SENSOR_FAN 0
58 #define ASUS_SENSOR_LAST 1
59 envsys_data_t sc_sensor
[ASUS_SENSOR_LAST
];
61 ACPI_INTEGER sc_brightness
;
62 ACPI_INTEGER sc_cfvnum
;
64 struct sysctllog
*sc_log
;
69 #define ASUS_NOTIFY_WirelessSwitch 0x10
70 #define ASUS_NOTIFY_BrightnessLow 0x20
71 #define ASUS_NOTIFY_BrightnessHigh 0x2f
72 #define ASUS_NOTIFY_DisplayCycle 0x30
73 #define ASUS_NOTIFY_WindowSwitch 0x12 /* XXXJDM ?? */
74 #define ASUS_NOTIFY_VolumeMute 0x13
75 #define ASUS_NOTIFY_VolumeDown 0x14
76 #define ASUS_NOTIFY_VolumeUp 0x15
78 #define ASUS_METHOD_SDSP "SDSP"
79 #define ASUS_SDSP_LCD 0x01
80 #define ASUS_SDSP_CRT 0x02
81 #define ASUS_SDSP_TV 0x04
82 #define ASUS_SDSP_DVI 0x08
83 #define ASUS_SDSP_ALL \
84 (ASUS_SDSP_LCD | ASUS_SDSP_CRT | ASUS_SDSP_TV | ASUS_SDSP_DVI)
85 #define ASUS_METHOD_PBLG "PBLG"
86 #define ASUS_METHOD_PBLS "PBLS"
87 #define ASUS_METHOD_CFVS "CFVS"
88 #define ASUS_METHOD_CFVG "CFVG"
90 #define ASUS_EC_METHOD_FAN_RPMH "\\_SB.PCI0.SBRG.EC0.SC05"
91 #define ASUS_EC_METHOD_FAN_RPML "\\_SB.PCI0.SBRG.EC0.SC06"
93 static int asus_match(device_t
, cfdata_t
, void *);
94 static void asus_attach(device_t
, device_t
, void *);
95 static int asus_detach(device_t
, int);
97 static void asus_notify_handler(ACPI_HANDLE
, UINT32
, void *);
99 static void asus_init(device_t
);
100 static bool asus_suspend(device_t
, pmf_qual_t
);
101 static bool asus_resume(device_t
, pmf_qual_t
);
103 static void asus_sysctl_setup(struct asus_softc
*);
105 static void asus_sensors_refresh(struct sysmon_envsys
*, envsys_data_t
*);
106 static bool asus_get_fan_speed(struct asus_softc
*, uint32_t *);
108 CFATTACH_DECL_NEW(asus
, sizeof(struct asus_softc
),
109 asus_match
, asus_attach
, asus_detach
, NULL
);
111 static const char * const asus_ids
[] = {
117 asus_match(device_t parent
, cfdata_t match
, void *opaque
)
119 struct acpi_attach_args
*aa
= opaque
;
121 if (aa
->aa_node
->ad_type
!= ACPI_TYPE_DEVICE
)
124 return acpi_match_hid(aa
->aa_node
->ad_devinfo
, asus_ids
);
128 asus_attach(device_t parent
, device_t self
, void *opaque
)
130 struct asus_softc
*sc
= device_private(self
);
131 struct acpi_attach_args
*aa
= opaque
;
134 sc
->sc_node
= aa
->aa_node
;
141 asus_sysctl_setup(sc
);
143 sc
->sc_smpsw_valid
= true;
144 sc
->sc_smpsw
[ASUS_PSW_DISPLAY_CYCLE
].smpsw_name
=
145 PSWITCH_HK_DISPLAY_CYCLE
;
146 sc
->sc_smpsw
[ASUS_PSW_DISPLAY_CYCLE
].smpsw_type
=
148 if (sysmon_pswitch_register(&sc
->sc_smpsw
[ASUS_PSW_DISPLAY_CYCLE
])) {
149 aprint_error_dev(self
, "couldn't register with sysmon\n");
150 sc
->sc_smpsw_valid
= false;
153 if (asus_get_fan_speed(sc
, NULL
) == false)
156 sc
->sc_sme
= sysmon_envsys_create();
158 strcpy(sc
->sc_sensor
[ASUS_SENSOR_FAN
].desc
, "fan");
159 sc
->sc_sensor
[ASUS_SENSOR_FAN
].units
= ENVSYS_SFANRPM
;
160 sysmon_envsys_sensor_attach(sc
->sc_sme
,
161 &sc
->sc_sensor
[ASUS_SENSOR_FAN
]);
163 sc
->sc_sme
->sme_name
= device_xname(self
);
164 sc
->sc_sme
->sme_cookie
= sc
;
165 sc
->sc_sme
->sme_refresh
= asus_sensors_refresh
;
166 sc
->sc_sme
->sme_flags
= SME_POLL_ONLY
;
168 if (sysmon_envsys_register(sc
->sc_sme
)) {
169 aprint_error_dev(self
, "couldn't register with envsys\n");
170 sysmon_envsys_destroy(sc
->sc_sme
);
175 rv
= AcpiInstallNotifyHandler(sc
->sc_node
->ad_handle
, ACPI_ALL_NOTIFY
,
176 asus_notify_handler
, sc
);
177 if (ACPI_FAILURE(rv
))
178 aprint_error_dev(self
, "couldn't install notify handler: %s\n",
179 AcpiFormatException(rv
));
181 if (!pmf_device_register(self
, asus_suspend
, asus_resume
))
182 aprint_error_dev(self
, "couldn't establish power handler\n");
186 asus_detach(device_t self
, int flags
)
188 struct asus_softc
*sc
= device_private(self
);
191 if (sc
->sc_smpsw_valid
)
192 for (i
= 0; i
< ASUS_PSW_LAST
; i
++)
193 sysmon_pswitch_unregister(&sc
->sc_smpsw
[i
]);
196 sysmon_envsys_unregister(sc
->sc_sme
);
198 sysctl_teardown(&sc
->sc_log
);
199 pmf_device_deregister(self
);
205 asus_notify_handler(ACPI_HANDLE hdl
, UINT32 notify
, void *opaque
)
207 struct asus_softc
*sc
= opaque
;
209 if (notify
>= ASUS_NOTIFY_BrightnessLow
&&
210 notify
<= ASUS_NOTIFY_BrightnessHigh
) {
211 aprint_debug_dev(sc
->sc_dev
, "brightness %d percent\n",
212 (notify
& 0xf) * 100 / 0xf);
217 case ASUS_NOTIFY_WirelessSwitch
: /* handled by AML */
218 case ASUS_NOTIFY_WindowSwitch
: /* XXXJDM what is this? */
220 case ASUS_NOTIFY_DisplayCycle
:
221 if (sc
->sc_smpsw_valid
== false)
223 sysmon_pswitch_event(&sc
->sc_smpsw
[ASUS_PSW_DISPLAY_CYCLE
],
224 PSWITCH_EVENT_PRESSED
);
226 case ASUS_NOTIFY_VolumeMute
:
227 pmf_event_inject(NULL
, PMFE_AUDIO_VOLUME_TOGGLE
);
229 case ASUS_NOTIFY_VolumeDown
:
230 pmf_event_inject(NULL
, PMFE_AUDIO_VOLUME_DOWN
);
232 case ASUS_NOTIFY_VolumeUp
:
233 pmf_event_inject(NULL
, PMFE_AUDIO_VOLUME_UP
);
236 aprint_debug_dev(sc
->sc_dev
, "unknown event 0x%02x\n", notify
);
242 asus_init(device_t self
)
244 struct asus_softc
*sc
= device_private(self
);
247 ACPI_OBJECT_LIST params
;
252 ret
.Length
= ACPI_ALLOCATE_LOCAL_BUFFER
;
253 param
.Type
= ACPI_TYPE_INTEGER
;
254 param
.Integer
.Value
= 0x40; /* disable ASL display switching */
255 params
.Pointer
= ¶m
;
258 rv
= AcpiEvaluateObject(sc
->sc_node
->ad_handle
, "INIT",
260 if (ACPI_FAILURE(rv
))
261 aprint_error_dev(self
, "couldn't evaluate INIT: %s\n",
262 AcpiFormatException(rv
));
265 ACPI_FREE(ret
.Pointer
);
267 rv
= acpi_eval_integer(sc
->sc_node
->ad_handle
, ASUS_METHOD_CFVG
, &cfv
);
268 if (ACPI_FAILURE(rv
))
271 sc
->sc_cfvnum
= (cfv
>> 8) & 0xff;
275 asus_suspend(device_t self
, pmf_qual_t qual
)
277 struct asus_softc
*sc
= device_private(self
);
280 /* capture display brightness when we're sleeping */
281 rv
= acpi_eval_integer(sc
->sc_node
->ad_handle
, ASUS_METHOD_PBLG
,
283 if (ACPI_FAILURE(rv
))
284 aprint_error_dev(sc
->sc_dev
, "couldn't evaluate PBLG: %s\n",
285 AcpiFormatException(rv
));
291 asus_resume(device_t self
, pmf_qual_t qual
)
293 struct asus_softc
*sc
= device_private(self
);
296 ACPI_OBJECT_LIST params
;
301 /* restore previous display brightness */
303 ret
.Length
= ACPI_ALLOCATE_LOCAL_BUFFER
;
304 param
.Type
= ACPI_TYPE_INTEGER
;
305 param
.Integer
.Value
= sc
->sc_brightness
;
306 params
.Pointer
= ¶m
;
309 rv
= AcpiEvaluateObject(sc
->sc_node
->ad_handle
, ASUS_METHOD_PBLS
,
311 if (ACPI_FAILURE(rv
))
312 aprint_error_dev(self
, "couldn't evaluate PBLS: %s\n",
313 AcpiFormatException(rv
));
319 asus_sysctl_verify(SYSCTLFN_ARGS
)
321 struct sysctlnode node
;
322 struct asus_softc
*sc
;
325 ACPI_OBJECT param
, retval
;
326 ACPI_OBJECT_LIST params
;
331 sc
= rnode
->sysctl_data
;
332 if (node
.sysctl_num
== sc
->sc_cfv_mib
) {
333 rv
= acpi_eval_integer(sc
->sc_node
->ad_handle
,
334 ASUS_METHOD_CFVG
, &cfv
);
335 if (ACPI_FAILURE(rv
))
338 node
.sysctl_data
= &tmp
;
339 err
= sysctl_lookup(SYSCTLFN_CALL(&node
));
340 if (err
|| newp
== NULL
)
343 if (tmp
< 0 || tmp
>= sc
->sc_cfvnum
)
346 ret
.Pointer
= &retval
;
347 ret
.Length
= sizeof(retval
);
348 param
.Type
= ACPI_TYPE_INTEGER
;
349 param
.Integer
.Value
= tmp
;
350 params
.Pointer
= ¶m
;
353 rv
= AcpiEvaluateObject(sc
->sc_node
->ad_handle
,
354 ASUS_METHOD_CFVS
, ¶ms
, &ret
);
355 if (ACPI_FAILURE(rv
))
363 asus_sysctl_setup(struct asus_softc
*sc
)
365 const struct sysctlnode
*node
, *node_cfv
, *node_ncfv
;
368 if (sc
->sc_cfvnum
== 0)
371 err
= sysctl_createv(&sc
->sc_log
, 0, NULL
, NULL
, 0,
372 CTLTYPE_NODE
, "hw", NULL
, NULL
, 0, NULL
, 0, CTL_HW
, CTL_EOL
);
375 err
= sysctl_createv(&sc
->sc_log
, 0, NULL
, &node
, 0,
376 CTLTYPE_NODE
, device_xname(sc
->sc_dev
), NULL
, NULL
, 0,
377 NULL
, 0, CTL_HW
, CTL_CREATE
, CTL_EOL
);
380 node_mib
= node
->sysctl_num
;
381 err
= sysctl_createv(&sc
->sc_log
, 0, NULL
, &node_ncfv
,
382 CTLFLAG_READONLY
, CTLTYPE_INT
, "ncfv",
383 SYSCTL_DESCR("Number of CPU frequency/voltage modes"),
384 NULL
, 0, &sc
->sc_cfvnum
, 0,
385 CTL_HW
, node_mib
, CTL_CREATE
, CTL_EOL
);
388 sc
->sc_cfvnum_mib
= node_ncfv
->sysctl_num
;
389 err
= sysctl_createv(&sc
->sc_log
, 0, NULL
, &node_cfv
,
390 CTLFLAG_READWRITE
, CTLTYPE_INT
, "cfv",
391 SYSCTL_DESCR("Current CPU frequency/voltage mode"),
392 asus_sysctl_verify
, 0, sc
, 0,
393 CTL_HW
, node_mib
, CTL_CREATE
, CTL_EOL
);
396 sc
->sc_cfv_mib
= node_cfv
->sysctl_num
;
400 aprint_error_dev(sc
->sc_dev
, "failed to add sysctl nodes. (%d)\n", err
);
404 asus_sensors_refresh(struct sysmon_envsys
*sme
, envsys_data_t
*edata
)
406 struct asus_softc
*sc
= sme
->sme_cookie
;
409 switch (edata
->sensor
) {
410 case ASUS_SENSOR_FAN
:
411 if (asus_get_fan_speed(sc
, &rpm
)) {
412 edata
->value_cur
= rpm
;
413 edata
->state
= ENVSYS_SVALID
;
415 edata
->state
= ENVSYS_SINVALID
;
421 asus_get_fan_speed(struct asus_softc
*sc
, uint32_t *speed
)
423 ACPI_INTEGER rpmh
, rpml
;
426 rv
= acpi_eval_integer(sc
->sc_node
->ad_handle
,
427 ASUS_EC_METHOD_FAN_RPMH
, &rpmh
);
428 if (ACPI_FAILURE(rv
))
430 rv
= acpi_eval_integer(sc
->sc_node
->ad_handle
,
431 ASUS_EC_METHOD_FAN_RPML
, &rpml
);
432 if (ACPI_FAILURE(rv
))
436 *speed
= (rpmh
<< 8) | rpml
;