xref: /freebsd/sys/dev/acpi_support/acpi_ibm.c (revision 6af83ee0d2941d18880b6aaa2b4facd1d30c6106)
1 /*-
2  * Copyright (c) 2004 Takanori Watanabe
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  *	$FreeBSD$
27  */
28 
29 #include "opt_acpi.h"
30 #include <sys/param.h>
31 #include <sys/kernel.h>
32 #include <sys/bus.h>
33 #include <machine/cpufunc.h>
34 #include "acpi.h"
35 #include "acpi_if.h"
36 #include <sys/module.h>
37 #include <dev/acpica/acpivar.h>
38 #include <sys/sysctl.h>
39 #include <machine/clock.h>
40 
41 #define _COMPONENT	ACPI_OEM
42 ACPI_MODULE_NAME("IBM")
43 
44 #define IBM_RTC_MISCKEY 0x65
45 #define IBM_RTC_BRIGHTNESS 0x6c
46 #define   IBM_RTC_MASK_BRI 0x7
47 #define   IBM_RTC_MASK_BRKEY 0x40
48 #define IBM_RTC_KEYLIGHT 0x66
49 #define   IBM_RTC_MASK_KEYLIGHT 0x10
50 #define IBM_RTC_VOLUME 0x6e
51 #define   IBM_RTC_MASK_VOL 0xf
52 #define   IBM_RTC_MASK_MUTE 0x40
53 #define   IBM_RTC_MASK_VOLKEY 0x80
54 
55 #define IBM_NAME_GET_WIRELESS "GBDC"
56 #define IBM_NAME_SET_WIRELESS "SBDC"
57 #define IBM_NAME_INTERFACE_VERSION "MHKV"
58 #define IBM_NAME_AVAIL_MASK "MHKA"
59 #define IBM_NAME_CURRENT_MASK "MHKN"
60 #define IBM_NAME_MODIFY_MASK "MHKM"
61 #define IBM_NAME_GET_EVENT "MHKP"
62 #define IBM_NAME_ENABLE "MHKC"
63 #if 0
64 /* TPX31 Specific? */
65 #define IBM_UCMS_VOLDN 0x0
66 #define IBM_UCMS_VOLUP 0x1
67 #define IBM_UCMS_MUTE 0x2
68 #define IBM_UCMS_BRIUP 0x4
69 #define IBM_UCMS_BRIDN 0x5
70 #define IBM_UCMS_KEYLIGHT 0xe
71 #endif
72 
73 struct acpi_ibm_softc {
74 	unsigned int	ibm_version;
75 	unsigned int	ibm_availmask;
76 	unsigned int	ibm_initialmask;
77 	int		ibm_enable;
78 	int		device_flag;
79 #define IBM_MHKN_AVAIL 1
80 #define IBM_MHKM_AVAIL 2
81 	struct sysctl_oid *oid_bluetooth;
82 	struct sysctl_oid *oid_wlan;
83 };
84 
85 static int	acpi_ibm_probe(device_t dev);
86 static int	acpi_ibm_attach(device_t dev);
87 static int	acpi_ibm_detach(device_t dev);
88 static void
89 acpi_ibm_notify_handler(ACPI_HANDLE h, UINT32 notify,
90 			void *context);
91 static int	sysctl_acpi_ibm_mask_handler(SYSCTL_HANDLER_ARGS);
92 static int	sysctl_acpi_ibm_enable_handler(SYSCTL_HANDLER_ARGS);
93 static int	sysctl_acpi_ibm_misckey_handler(SYSCTL_HANDLER_ARGS);
94 static int	sysctl_acpi_ibm_volume_handler(SYSCTL_HANDLER_ARGS);
95 static int	sysctl_acpi_ibm_mute_handler(SYSCTL_HANDLER_ARGS);
96 static int	sysctl_acpi_ibm_brightness_handler(SYSCTL_HANDLER_ARGS);
97 static int	sysctl_acpi_ibm_keylight_handler(SYSCTL_HANDLER_ARGS);
98 static int	sysctl_acpi_ibm_wireless_handler(SYSCTL_HANDLER_ARGS);
99 static int	acpi_ibm_enable_mask(device_t dev, int val);
100 
101 static device_method_t acpi_ibm_methods[] = {
102 	/* Device interface */
103 	DEVMETHOD(device_probe, acpi_ibm_probe),
104 	DEVMETHOD(device_attach, acpi_ibm_attach),
105 	DEVMETHOD(device_detach, acpi_ibm_detach),
106 
107 	{0, 0}
108 };
109 
110 static driver_t	acpi_ibm_driver = {
111 	"acpi_ibm",
112 	acpi_ibm_methods,
113 	sizeof(struct acpi_ibm_softc),
114 };
115 
116 static devclass_t acpi_ibm_devclass;
117 
118 DRIVER_MODULE(acpi_ibm, acpi, acpi_ibm_driver, acpi_ibm_devclass,
119 	      0, 0);
120 MODULE_DEPEND(acpi_ibm, acpi, 1, 1, 1);
121 static char    *ibm_id[] = {"IBM0068", NULL};
122 
123 static int
124 acpi_ibm_probe(device_t dev)
125 {
126 	struct acpi_ibm_softc *sc;
127 	int		ret = ENXIO;
128 
129 	sc = device_get_softc(dev);
130 
131 	if (ACPI_ID_PROBE(device_get_parent(dev), dev, ibm_id)) {
132 		device_set_desc(dev, "IBM ThinkPad Button");
133 		ret = 0;
134 	}
135 	return (ret);
136 }
137 
138 static int
139 acpi_ibm_call_two_method(device_t dev, char *name, int val1, int val2)
140 {
141 	ACPI_OBJECT	arg [2];
142 	ACPI_OBJECT_LIST args = {.Count = 2,.Pointer = arg};
143 	arg[0].Type = ACPI_TYPE_INTEGER;
144 	arg[0].Integer.Value = val1;
145 	arg[1].Type = ACPI_TYPE_INTEGER;
146 	arg[1].Integer.Value = val2;
147 	return AcpiEvaluateObject(acpi_get_handle(dev), name, &args, NULL);
148 }
149 
150 static int
151 acpi_ibm_attach(device_t dev)
152 {
153 	struct acpi_ibm_softc *sc;
154 	ACPI_STATUS	status;
155 	ACPI_HANDLE	h;
156 	int		dummy;
157 	struct sysctl_oid *oid;
158 	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
159 
160 	sc = device_get_softc(dev);
161 	sc->device_flag = 0;
162 	if (ACPI_FAILURE
163 	    (acpi_GetInteger(acpi_get_handle(dev), IBM_NAME_INTERFACE_VERSION, &sc->ibm_version))) {
164 		sc->ibm_version = 0;
165 	}
166 	device_printf(dev, "Version %x\n", sc->ibm_version);
167 	if (ACPI_FAILURE
168 	    (acpi_GetInteger(acpi_get_handle(dev), IBM_NAME_AVAIL_MASK, &sc->ibm_availmask)))
169 		sc->ibm_availmask = 0xffffffff;
170 
171 	if (ACPI_FAILURE
172 	    (acpi_GetInteger(acpi_get_handle(dev), IBM_NAME_CURRENT_MASK, &sc->ibm_initialmask)))
173 		sc->ibm_initialmask = 0xffffffff;
174 	else
175 		sc->device_flag |= IBM_MHKN_AVAIL;
176 
177 	if (ACPI_SUCCESS(status = AcpiGetHandle(acpi_get_handle(dev), IBM_NAME_MODIFY_MASK, &h)))
178 		sc->device_flag |= IBM_MHKM_AVAIL;
179 	else
180 		printf("%s\n", AcpiFormatException(status));
181 
182 	device_printf(dev, "Available Mask %x\n", sc->ibm_availmask);
183 	device_printf(dev, "Initial Mask %x\n", sc->ibm_initialmask);
184 	/* Install Specific Handler */
185 	status = AcpiInstallNotifyHandler(acpi_get_handle(dev), ACPI_DEVICE_NOTIFY, acpi_ibm_notify_handler, dev);
186 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
187 			SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
188 			OID_AUTO, "key_mask", CTLTYPE_INT | CTLFLAG_RW,
189 			dev, 0,
190 			sysctl_acpi_ibm_mask_handler, "I", "Hot key mask");
191 	SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
192 		       SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
193 		       OID_AUTO, "version", CTLFLAG_RD,
194 		       &sc->ibm_version, 0, "Interface version");
195 	SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
196 		       SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
197 		       OID_AUTO, "avail_mask", CTLFLAG_RD,
198 		       &sc->ibm_availmask, 0, "Available Key mask");
199 	if (ACPI_FAILURE(acpi_SetInteger(acpi_get_handle(dev), IBM_NAME_ENABLE, 1)))
200 		goto fail;
201 	sc->ibm_enable = 1;
202 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
203 			SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
204 			OID_AUTO, "enable", CTLTYPE_INT | CTLFLAG_RW,
205 			dev, 0,
206 		     sysctl_acpi_ibm_enable_handler, "I", "Hot key enable");
207 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
208 			SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
209 			OID_AUTO, "misckey", CTLTYPE_INT | CTLFLAG_RD,
210 			dev, 0,
211 	       sysctl_acpi_ibm_misckey_handler, "I", "Key Status: Poll me");
212 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
213 			SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
214 			OID_AUTO, "brightness", CTLTYPE_INT | CTLFLAG_RD,
215 			dev, 0,
216 		     sysctl_acpi_ibm_brightness_handler, "I", "Brightness");
217 
218 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
219 			SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
220 			OID_AUTO, "volume", CTLTYPE_INT | CTLFLAG_RD,
221 			dev, 0,
222 			sysctl_acpi_ibm_volume_handler, "I", "Volume");
223 
224 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
225 			SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
226 			OID_AUTO, "mute", CTLTYPE_INT | CTLFLAG_RD,
227 			dev, 0,
228 			sysctl_acpi_ibm_mute_handler, "I", "Muting");
229 
230 
231 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
232 			SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
233 			OID_AUTO, "keylight", CTLTYPE_INT | CTLFLAG_RD,
234 			dev, 0,
235 			sysctl_acpi_ibm_keylight_handler, "I", "Key Light");
236 	if (ACPI_SUCCESS(acpi_GetInteger(acpi_get_handle(dev), IBM_NAME_GET_WIRELESS, &dummy))) {
237 		oid = SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
238 			       SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
239 		    OID_AUTO, "bluetooth", CTLTYPE_INT | CTLFLAG_RW, dev, 0,
240 		 sysctl_acpi_ibm_wireless_handler, "I", "Bluetooth Enable");
241 		sc->oid_bluetooth = oid;
242 		oid = SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
243 			       SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
244 			 OID_AUTO, "wlan", CTLTYPE_INT | CTLFLAG_RW, dev, 0,
245 		      sysctl_acpi_ibm_wireless_handler, "I", "WLAN Enable");
246 		sc->oid_wlan = oid;
247 	}
248 	return_VALUE(0);
249 fail:
250 	device_printf(dev, "FAILED\n");
251 	AcpiRemoveNotifyHandler(acpi_get_handle(dev), ACPI_DEVICE_NOTIFY, acpi_ibm_notify_handler);
252 	return_VALUE(EINVAL);
253 }
254 
255 static int
256 acpi_ibm_detach(device_t dev)
257 {
258 	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
259 
260 	struct acpi_ibm_softc *sc = device_get_softc(dev);
261 	acpi_SetInteger(acpi_get_handle(dev), IBM_NAME_ENABLE, 0);
262 	acpi_ibm_enable_mask(dev, sc->ibm_initialmask);
263 
264 	AcpiRemoveNotifyHandler(acpi_get_handle(dev), ACPI_DEVICE_NOTIFY, acpi_ibm_notify_handler);
265 	return_VALUE(0);
266 }
267 #if 0
268 static int
269 acpi_ibm_suspend(device_t dev)
270 {
271 	struct acpi_ibm_softc *sc = device_get_softc(dev);
272 	return_VALUE(0);
273 }
274 
275 static int
276 acpi_ibm_resume(device_t dev)
277 {
278 	return (0);
279 }
280 #endif
281 static void
282 acpi_ibm_notify_handler(ACPI_HANDLE h, UINT32 notify,
283 			void *context)
284 {
285 	int		mhkp      , arg, type;
286 	device_t	dev = context;
287 	struct acpi_ibm_softc *sc = device_get_softc(dev);
288 
289 	printf("IBM:NOTIFY:%x\n", notify);
290 	if (notify != 0x80) {
291 		printf("Unknown notify\n");
292 	}
293 	for (;;) {
294 
295 		acpi_GetInteger(acpi_get_handle(dev), IBM_NAME_GET_EVENT, &mhkp);
296 
297 		if (mhkp == 0) {
298 			break;
299 		}
300 		printf("notify:%x\n", mhkp);
301 
302 		type = (mhkp >> 12) & 0xf;
303 		arg = mhkp & 0xfff;
304 		switch (type) {
305 		case 1:
306 			if (!(sc->ibm_availmask & (1 << (arg - 1)))) {
307 				printf("Unknown key %d\n", arg);
308 				break;
309 			}
310 			acpi_UserNotify("IBM", h, (arg & 0xff));
311 			break;
312 		default:
313 			break;
314 		}
315 	}
316 }
317 
318 static int
319 acpi_ibm_enable_mask(device_t dev, int val)
320 {
321 	int		i;
322 	struct acpi_ibm_softc *sc = device_get_softc(dev);
323 
324 	if (!(sc->device_flag | IBM_MHKM_AVAIL)) {
325 		return -1;
326 	}
327 	for (i = 0; i < 32; i++) {
328 		acpi_ibm_call_two_method(dev, IBM_NAME_MODIFY_MASK, i + 1, 1);
329 		if (!((1 << i) & val))
330 			acpi_ibm_call_two_method(dev, IBM_NAME_MODIFY_MASK, i + 1, 0);
331 	}
332 	return 0;
333 }
334 
335 static int
336 sysctl_acpi_ibm_mask_handler(SYSCTL_HANDLER_ARGS)
337 {
338 	device_t	dev = arg1;
339 	int		val = 0xffffffff;
340 	int		error = 0;
341 
342 	struct acpi_ibm_softc *sc = device_get_softc(dev);
343 
344 	if (sc->device_flag & IBM_MHKN_AVAIL)
345 		acpi_GetInteger(acpi_get_handle(dev), IBM_NAME_CURRENT_MASK, &val);
346 
347 	error = sysctl_handle_int(oidp, &val, 0, req);
348 
349 	if (error || !req->newptr)
350 		return error;
351 
352 	val &= sc->ibm_availmask;
353 	val |= sc->ibm_initialmask;
354 
355 	acpi_ibm_enable_mask(dev, val);
356 
357 	return 0;
358 }
359 
360 static int
361 sysctl_acpi_ibm_misckey_handler(SYSCTL_HANDLER_ARGS)
362 {
363 	int		val       , error;
364 	val = rtcin(IBM_RTC_MISCKEY);
365 	error = sysctl_handle_int(oidp, &val, 0, req);
366 	if (error || !req->newptr)
367 		return error;
368 	return 0;
369 }
370 
371 
372 static int
373 sysctl_acpi_ibm_brightness_handler(SYSCTL_HANDLER_ARGS)
374 {
375 	int		val       , error;
376 	val = rtcin(IBM_RTC_BRIGHTNESS);
377 	val &= IBM_RTC_MASK_BRI;
378 	error = sysctl_handle_int(oidp, &val, 0, req);
379 	if (error || !req->newptr)
380 		return error;
381 	return 0;
382 }
383 
384 static int
385 sysctl_acpi_ibm_mute_handler(SYSCTL_HANDLER_ARGS)
386 {
387 	int		val       , error;
388 	val = rtcin(IBM_RTC_VOLUME);
389 	val = ((val & IBM_RTC_MASK_MUTE) == IBM_RTC_MASK_MUTE);
390 
391 	error = sysctl_handle_int(oidp, &val, 0, req);
392 	if (error || !req->newptr)
393 		return error;
394 	return 0;
395 }
396 
397 static int
398 sysctl_acpi_ibm_keylight_handler(SYSCTL_HANDLER_ARGS)
399 {
400 	int		val       , error;
401 	val = ((rtcin(IBM_RTC_KEYLIGHT) & IBM_RTC_MASK_KEYLIGHT)
402 	       == IBM_RTC_MASK_KEYLIGHT);
403 
404 	error = sysctl_handle_int(oidp, &val, 0, req);
405 	if (error || !req->newptr)
406 		return error;
407 	return 0;
408 }
409 
410 static int
411 sysctl_acpi_ibm_volume_handler(SYSCTL_HANDLER_ARGS)
412 {
413 	int		val       , error;
414 	val = rtcin(IBM_RTC_VOLUME);
415 	val &= IBM_RTC_MASK_VOL;
416 	error = sysctl_handle_int(oidp, &val, 0, req);
417 	if (error || !req->newptr)
418 		return error;
419 	return 0;
420 }
421 
422 static int
423 sysctl_acpi_ibm_enable_handler(SYSCTL_HANDLER_ARGS)
424 {
425 	device_t	dev = arg1;
426 	struct acpi_ibm_softc *sc = device_get_softc(dev);
427 	int		error = 0;
428 
429 	error = sysctl_handle_int(oidp, &sc->ibm_enable, 0, req);
430 
431 	if (error || !req->newptr)
432 		return error;
433 
434 	if (sc->ibm_enable)
435 		sc->ibm_enable = 1;
436 	else
437 		sc->ibm_enable = 0;
438 	acpi_SetInteger(acpi_get_handle(dev), IBM_NAME_ENABLE, sc->ibm_enable);
439 
440 	return 0;
441 }
442 
443 static int
444 sysctl_acpi_ibm_wireless_handler(SYSCTL_HANDLER_ARGS)
445 {
446 	device_t	dev = arg1;
447 	struct acpi_ibm_softc *sc = device_get_softc(dev);
448 	int		error = 0,	val, oldval, mask;
449 	if (sc->oid_bluetooth == oidp) {
450 		mask = 2;
451 	} else if (sc->oid_wlan == oidp) {
452 		mask = 4;
453 	} else {
454 		printf("WARNING: wrong handler invoked\n");
455 		return ENOENT;
456 	}
457 
458 	acpi_GetInteger(acpi_get_handle(dev), IBM_NAME_GET_WIRELESS, &oldval);
459 	val = !((oldval & mask) == 0);
460 	error = sysctl_handle_int(oidp, &val, 0, req);
461 
462 	if (error || !req->newptr)
463 		return error;
464 	oldval &= (~mask);
465 	if (val)
466 		oldval |= mask;
467 	acpi_SetInteger(acpi_get_handle(dev), IBM_NAME_SET_WIRELESS, oldval);
468 	return 0;
469 
470 
471 }
472