1/*
2 * Copyright (c) International Business Machines  Corp., 2001
3 * Copyright (c) 2013 Oracle and/or its affiliates. All Rights Reserved.
4 *
5 * This program is free software;  you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY;  without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
13 * the GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program;  if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20/*
21 *  HISTORY:
22 *    06/09/2003 Initial creation mridge@us.ibm.com
23 *      -Ported
24 *  updated - 01/09/2005 Updates from Intel to add functionality
25 *
26 *  01/03/2009 Márton Németh <nm127@freemail.hu>
27 *   - Updated for Linux kernel 2.6.28
28 */
29
30#include <linux/kernel.h>
31#include <linux/module.h>
32#include <linux/init.h>
33#include <linux/types.h>
34#include <linux/fs.h>
35#include <linux/blkdev.h>
36#include <linux/ioctl.h>
37#include <linux/pm.h>
38#include <linux/acpi.h>
39#include <linux/genhd.h>
40#include <linux/dmi.h>
41#include <linux/nls.h>
42
43#include "ltp_acpi.h"
44
45MODULE_AUTHOR("Martin Ridgeway <mridge@us.ibm.com>");
46MODULE_AUTHOR("Alexey Kodanev <alexey.kodanev@oracle.com>");
47MODULE_DESCRIPTION("ACPI LTP Test Driver");
48MODULE_LICENSE("GPL");
49ACPI_MODULE_NAME("LTP_ACPI")
50
51#define prk_err(fmt, ...) \
52	pr_err(ACPI_TEST_NAME ": " fmt "\n", ##__VA_ARGS__)
53#define prk_alert(fmt, ...) \
54	pr_alert(ACPI_TEST_NAME ": " fmt "\n", ##__VA_ARGS__)
55#define prk_info(fmt, ...) \
56	pr_info(ACPI_TEST_NAME ": " fmt "\n", ##__VA_ARGS__)
57
58static int acpi_failure(acpi_status status, const char *name)
59{
60	if (ACPI_FAILURE(status)) {
61		ACPI_EXCEPTION((AE_INFO, status, name));
62		return 1;
63	}
64	return 0;
65}
66
67/* points to the string of the last found object _STR */
68static char *str_obj_result;
69
70/* sysfs device path of the last found device */
71static char *sysfs_path;
72
73/* first found device with _CRS */
74static acpi_handle res_handle;
75
76static acpi_status get_str_object(acpi_handle handle)
77{
78	int res;
79	acpi_status status;
80	acpi_handle temp = 0;
81	union acpi_object *str_obj;
82	char *buf = NULL;
83
84	struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
85
86	status = acpi_get_handle(handle, "_STR", &temp);
87
88	if (ACPI_SUCCESS(status) &&
89	    !acpi_evaluate_object(handle, "_STR", NULL, &buffer)) {
90
91		str_obj = buffer.pointer;
92
93		buf = kmalloc(str_obj->buffer.length / 2, GFP_KERNEL);
94		if (!buf) {
95			kfree(str_obj);
96			return AE_NO_MEMORY;
97		}
98
99		res = utf16s_to_utf8s((wchar_t *)str_obj->buffer.pointer,
100			str_obj->buffer.length, UTF16_LITTLE_ENDIAN, buf,
101			str_obj->buffer.length / 2);
102
103		buf[res] = '\0';
104
105		kfree(str_obj_result);
106		str_obj_result = buf;
107		kfree(str_obj);
108	}
109
110	return status;
111}
112
113static void get_crs_object(acpi_handle handle)
114{
115	acpi_status status;
116	acpi_handle temp;
117	if (!res_handle) {
118		status = acpi_get_handle(handle, METHOD_NAME__CRS, &temp);
119		if (ACPI_SUCCESS(status))
120			res_handle = handle;
121	}
122}
123
124static void get_sysfs_path(acpi_handle handle)
125{
126	acpi_status status;
127	struct acpi_device *device;
128
129	kfree(sysfs_path);
130	sysfs_path = NULL;
131
132	status = acpi_bus_get_device(handle, &device);
133	if (ACPI_SUCCESS(status))
134		sysfs_path = kobject_get_path(&device->dev.kobj, GFP_KERNEL);
135}
136
137/* acpi handle of the last visited device */
138static acpi_handle start_parent;
139
140static int acpi_traverse(acpi_handle parent, acpi_handle child)
141{
142	static char indent[64];
143	const char * const ind_end = indent + 63;
144	static const char *ind = ind_end;
145	acpi_status status;
146	struct acpi_device_info *dev_info;
147	acpi_handle new_child;
148
149	if (!indent[0])
150		memset(indent, 0x20, 63);
151
152	while (parent) {
153		status = acpi_get_next_object(ACPI_TYPE_DEVICE,
154			parent, child, &new_child);
155
156		if (ACPI_FAILURE(status)) {
157			ind += 4;
158
159			child = parent;
160			status = acpi_get_parent(child, &parent);
161
162			/* no more devices */
163			if (ACPI_FAILURE(status)) {
164				start_parent = 0;
165				kfree(str_obj_result);
166				str_obj_result = NULL;
167				return 0;
168			}
169			continue;
170		}
171
172		status = acpi_get_object_info(new_child, &dev_info);
173		if (acpi_failure(status, "acpi_object_info failed"))
174			return 1;
175
176		get_sysfs_path(new_child);
177
178		get_crs_object(new_child);
179
180		if (ind < indent)
181			ind = indent;
182		else if (ind > ind_end)
183			ind = ind_end;
184
185		/*
186		 * if we find _STR object we will stop here
187		 * and save last visited child
188		 */
189		if (ACPI_SUCCESS(get_str_object(new_child))) {
190			prk_info("%s%4.4s: has '_STR' '%s' path '%s'",
191				ind, (char *)&dev_info->name, str_obj_result,
192				(sysfs_path) ? sysfs_path : "no path");
193			ind -= 4;
194			start_parent = new_child;
195			kfree(dev_info);
196			return 0;
197		}
198		prk_info("%s%4.4s: path '%s'", ind, (char *)&dev_info->name,
199			(sysfs_path) ? sysfs_path : "no path");
200
201		ind -= 4;
202		parent = new_child;
203		child = 0;
204		kfree(dev_info);
205	}
206
207	return 0;
208}
209
210static int acpi_traverse_from_root(void)
211{
212	acpi_status status;
213	struct acpi_device_info *dev_info;
214	acpi_handle parent = 0, child = 0;
215
216	if (!start_parent) {
217		status = acpi_get_handle(NULL, ACPI_NS_ROOT_PATH, &parent);
218		if (acpi_failure(status, "acpi_get_handle"))
219			return 1;
220		status = acpi_get_object_info(parent, &dev_info);
221		if (acpi_failure(status, "acpi_object_info failed"))
222			return 1;
223		prk_info("start from %4.4s", (char *)&dev_info->name);
224		kfree(dev_info);
225	} else {
226		/* continue with the last visited child */
227		parent = start_parent;
228	}
229
230	return acpi_traverse(parent, child);
231}
232
233/* first found device with _STR */
234static acpi_handle dev_handle;
235static int acpi_hw_reduced;
236
237/* check if PM2 control register supported */
238static bool pm2_supported;
239
240static int acpi_init(void)
241{
242	acpi_status status;
243	acpi_handle parent_handle;
244	struct acpi_table_fadt *fadt;
245	struct acpi_table_header *table;
246	struct acpi_device_info *dev_info;
247	pm2_supported = true;
248
249	status = acpi_get_table(ACPI_SIG_FADT, 0, &table);
250	if (ACPI_SUCCESS(status)) {
251		fadt = (struct acpi_table_fadt *)table;
252		if (fadt->flags & ACPI_FADT_HW_REDUCED)
253			acpi_hw_reduced = 1;
254		if (fadt->pm2_control_block == 0 || fadt->pm2_control_length == 0)
255			pm2_supported = false;
256	}
257	if (acpi_hw_reduced)
258		prk_alert("Detected the Hardware-reduced ACPI mode");
259
260	prk_alert("TEST -- acpi_get_handle ");
261	status = acpi_get_handle(NULL, "\\_SB", &parent_handle);
262	if (acpi_failure(status, "acpi_get_handle"))
263		return 1;
264
265	/* get first device on SYS bus, it will be used in other tests */
266	while (acpi_get_next_object(ACPI_TYPE_DEVICE,
267		parent_handle, 0, &dev_handle) == 0) {
268		parent_handle = dev_handle;
269	}
270
271	status = acpi_get_object_info(dev_handle, &dev_info);
272	if (acpi_failure(status, "acpi_object_info failed"))
273		return 1;
274
275	prk_alert("ACPI object name %4.4s, type %d", (char *)&dev_info->name,
276		dev_info->type);
277	kfree(dev_info);
278
279	prk_alert("TEST -- acpi_get_parent ");
280	status = acpi_get_parent(dev_handle, &parent_handle);
281	return acpi_failure(status, "acpi_get_parent failed");
282}
283
284/*
285 * acpi_bus_notify
286 * ---------------
287 * Callback for all 'system-level' device notifications (values 0x00-0x7F).
288 */
289static void acpi_bus_notify(acpi_handle handle, u32 type, void *data)
290{
291	prk_alert("Register ACPI Bus Notify callback function");
292}
293
294static int acpi_test_notify_handler(void)
295{
296	acpi_status status;
297
298	prk_alert("TEST -- acpi_install_notify_handler");
299
300	status = acpi_install_notify_handler(dev_handle,
301		ACPI_SYSTEM_NOTIFY, &acpi_bus_notify, NULL);
302
303	if (ACPI_SUCCESS(status)) {
304		prk_alert("TEST -- acpi_remove_notify_handler");
305		status = acpi_remove_notify_handler(dev_handle,
306			ACPI_SYSTEM_NOTIFY, &acpi_bus_notify);
307		return acpi_failure(status, "acpi_remove_notify_handler");
308	} else if (status != AE_ALREADY_EXISTS) {
309		return acpi_failure(status, "acpi_install_notify_handler");
310	}
311
312	return 0;
313}
314
315static u32 ltp_test_power_button_ev_handler(void *context)
316{
317	prk_alert("ltp_test_power_button_ev_handler");
318	return 1;
319}
320
321static u32 ltp_test_sleep_button_ev_handler(void *context)
322{
323	prk_alert("ltp_test_sleep_button_ev_handler");
324	return 1;
325}
326
327static int acpi_test_event_handler(void)
328{
329	int err = 0;
330	acpi_status status;
331
332	prk_alert("TEST -- acpi_install_fixed_event_handler");
333	if (acpi_hw_reduced) {
334		prk_alert("Skipped due to the HW-reduced mode");
335		return 0;
336	}
337	status = acpi_install_fixed_event_handler(ACPI_EVENT_POWER_BUTTON,
338		ltp_test_power_button_ev_handler, NULL);
339
340	if (ACPI_SUCCESS(status)) {
341		prk_alert("TEST -- acpi_remove_fixed_event_handler");
342		status = acpi_remove_fixed_event_handler(
343			ACPI_EVENT_POWER_BUTTON,
344			ltp_test_power_button_ev_handler);
345		err = acpi_failure(status, "remove fixed event handler");
346	} else if (status != AE_ALREADY_EXISTS) {
347		err = acpi_failure(status, "install fixed event handler");
348	}
349
350	prk_alert("TEST -- acpi_install_fixed_event_handler");
351	status = acpi_install_fixed_event_handler(ACPI_EVENT_RTC,
352		ltp_test_sleep_button_ev_handler, NULL);
353
354	if (ACPI_SUCCESS(status)) {
355		prk_alert("TEST -- acpi_remove_fixed_event_handler");
356		status = acpi_remove_fixed_event_handler(
357			ACPI_EVENT_RTC,
358			ltp_test_sleep_button_ev_handler);
359		err |= acpi_failure(status, "remove fixed event handler");
360	} else if (status != AE_ALREADY_EXISTS) {
361		err |= acpi_failure(status, "install fixed event handler");
362	}
363
364	return err;
365}
366
367#ifndef ACPI_EC_UDELAY_GLK
368#define ACPI_EC_UDELAY_GLK	1000	/* Wait 1ms max. to get global lock */
369#endif
370
371static int acpi_global_lock(void)
372{
373	acpi_status status;
374	u32 global_lock = 0;
375
376	prk_alert("TEST -- acpi_acquire_global_lock ");
377	if (acpi_hw_reduced) {
378		prk_alert("Skipped due to the HW-reduced mode");
379		return 0;
380	}
381	status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &global_lock);
382	if (acpi_failure(status, "acpi_acquire_global_lock"))
383		return 1;
384
385	prk_alert("TEST -- acpi_release_global_lock ");
386	status = acpi_release_global_lock(global_lock);
387	return acpi_failure(status, "acpi_release_global_lock");
388}
389
390static int acpi_test_bus(void)
391{
392	int state = 0;
393	acpi_status status;
394	acpi_handle bus_handle;
395	struct acpi_device *device;
396
397	status = acpi_get_handle(NULL, "\\_SB", &bus_handle);
398	if (acpi_failure(status, "acpi_get_handle"))
399		return 1;
400
401	prk_alert("TEST -- acpi_bus_get_device");
402	status = acpi_bus_get_device(bus_handle, &device);
403	if (acpi_failure(status, "acpi_bus_get_device"))
404		return 1;
405
406	prk_alert("TEST -- acpi_bus_update_power ");
407	status = acpi_bus_update_power(device->handle, &state);
408	if (acpi_failure(status, "error reading power state"))
409		return 1;
410
411	prk_info("acpi bus power state is %d", state);
412	return 0;
413}
414
415static acpi_status acpi_ec_io_ports(struct acpi_resource *resource,
416	void *context)
417{
418	return 0;
419}
420
421static int acpi_test_resources(void)
422{
423	int err = 0;
424	acpi_status status;
425	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
426
427	/* skip if we don't find device with _CRC */
428	if (res_handle == 0)
429		return 0;
430
431	prk_alert("TEST -- acpi_get_current_resources");
432	status = acpi_get_current_resources(res_handle, &buffer);
433	err = acpi_failure(status, "failed get_current_resources");
434	ACPI_FREE(buffer.pointer);
435
436#ifdef ACPI_FUTURE_USAGE
437	prk_alert("TEST -- acpi_get_possible_resources");
438	status = acpi_get_possible_resources(res_handle, &buffer);
439	err |= acpi_failure(status, "get_possible_resources");
440#endif
441
442	prk_alert("TEST -- acpi_walk_resources ");
443	status = acpi_walk_resources(res_handle, METHOD_NAME__CRS,
444		acpi_ec_io_ports, NULL);
445	err |= acpi_failure(status, "Failed walk_resources");
446
447	return err;
448}
449
450static int acpi_sleep_test(void)
451{
452	int err = 0;
453	acpi_status status;
454	u32 i;
455	u8 type_a, type_b;
456	prk_alert("TEST -- acpi_get_sleep_type_data ");
457
458	for (i = 0; i < ACPI_S_STATE_COUNT; ++i) {
459		status = acpi_get_sleep_type_data(i, &type_a, &type_b);
460		if (ACPI_SUCCESS(status)) {
461			prk_info("get_sleep_type_data S%d a:%d b:%d",
462				i, type_a, type_b);
463		} else if (status != AE_NOT_FOUND) {
464			err |= 1;
465		}
466	}
467
468	return err;
469}
470
471static int acpi_test_register(void)
472{
473	int i, err = 0;
474	u32 val;
475	acpi_status status;
476
477	prk_alert("TEST -- acpi_read_bit_register");
478	if (acpi_hw_reduced) {
479		prk_alert("Skipped due to the HW-reduced mode");
480		return 0;
481	}
482	/*
483	 * ACPICA: Remove obsolete Flags parameter.
484	 * http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;
485	 * a=commitdiff;h=d8c71b6d3b21cf21ad775e1cf6da95bf87bd5ad4
486	 *
487	 * ACPICA: Rename ACPI bit register access functions
488	 * http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/
489	 * commit/?id=50ffba1bd3120b069617455545bc27bcf3cf7579
490	 */
491	for (i = 0; i < ACPI_NUM_BITREG; ++i) {
492		if (i == ACPI_BITREG_ARB_DISABLE && !pm2_supported)
493			continue;
494		status = acpi_read_bit_register(i, &val);
495		err |= acpi_failure(status, "acpi_read_bit_register");
496		if (ACPI_SUCCESS(status))
497			prk_alert("get register: %02x val: %04x", i, val);
498	}
499
500	return err;
501}
502
503static acpi_status ltp_get_dev_callback(acpi_handle obj, u32 depth,
504	void *context, void **ret)
505{
506	char *name = context;
507	char fullname[20];
508
509	/*
510	 * Only SBA shows up in ACPI namespace, so its CSR space
511	 * includes both SBA and IOC.  Make SBA and IOC show up
512	 * separately in PCI space.
513	 */
514	sprintf(fullname, "%s SBA", name);
515	prk_info("get_dev_callback SBA name %s", fullname);
516	sprintf(fullname, "%s IOC", name);
517	prk_info("get_dev_callback IOC name %s", fullname);
518
519	return 0;
520}
521
522static int acpi_test_dev_callback(void)
523{
524	acpi_status status;
525	prk_alert("TEST -- acpi_get_devices ");
526	status = acpi_get_devices(NULL, ltp_get_dev_callback, "LTP0001", NULL);
527	return acpi_failure(status, "acpi_get_devices");
528}
529
530static int current_test_case;
531static int test_result;
532
533static void device_release(struct device *dev)
534{
535	prk_info("device released");
536}
537
538static struct device tdev = {
539	.init_name	= ACPI_TEST_NAME,
540	.release	= device_release,
541};
542
543/* print test result to sysfs file */
544static ssize_t sys_result(struct device *dev,
545	struct device_attribute *attr, char *buf)
546{
547	return scnprintf(buf, PAGE_SIZE, "%d\n", test_result);
548}
549static DEVICE_ATTR(result, S_IRUSR, sys_result, NULL);
550
551/* print found device description */
552static ssize_t sys_str(struct device *dev,
553	struct device_attribute *attr, char *buf)
554{
555	if (str_obj_result)
556		return scnprintf(buf, PAGE_SIZE, "%s", str_obj_result);
557	else
558		return 0;
559}
560static DEVICE_ATTR(str, S_IRUSR, sys_str, NULL);
561
562/* print found device's sysfs path */
563static ssize_t sys_path(struct device *dev,
564	struct device_attribute *attr, char *buf)
565{
566	if (sysfs_path)
567		return scnprintf(buf, PAGE_SIZE, "%s", sysfs_path);
568	else
569		return 0;
570}
571static DEVICE_ATTR(path, S_IRUSR, sys_path, NULL);
572
573static ssize_t sys_acpi_disabled(struct device *dev,
574				    struct device_attribute *attr, char *buf)
575{
576	return scnprintf(buf, PAGE_SIZE, "%d", acpi_disabled);
577}
578static DEVICE_ATTR(acpi_disabled, S_IRUSR, sys_acpi_disabled, NULL);
579
580static ssize_t sys_tcase(struct device *dev,
581	struct device_attribute *attr,  const char *buf, size_t count)
582{
583	sscanf(buf, "%d", &current_test_case);
584	prk_info("test-case %d", current_test_case);
585
586	switch (current_test_case) {
587	case ACPI_INIT:
588		test_result = acpi_init();
589	break;
590	case ACPI_TRAVERSE:
591		test_result = acpi_traverse_from_root();
592	break;
593	case ACPI_NOTIFY_HANDLER:
594		test_result = acpi_test_notify_handler();
595	break;
596	case ACPI_EVENT_HANDLER:
597		test_result = acpi_test_event_handler();
598	break;
599	case ACPI_GLOBAL_LOCK:
600		test_result = acpi_global_lock();
601	break;
602	case ACPI_TEST_BUS:
603		test_result = acpi_test_bus();
604	break;
605	case ACPI_TEST_RESOURCES:
606		test_result = acpi_test_resources();
607	break;
608	case ACPI_SLEEP_TEST:
609		test_result = acpi_sleep_test();
610	break;
611	case ACPI_TEST_REGISTER:
612		test_result = acpi_test_register();
613	break;
614	case ACPI_TEST_DEV_CALLBACK:
615		test_result = acpi_test_dev_callback();
616	break;
617	}
618
619	return count;
620}
621static DEVICE_ATTR(tcase, S_IWUSR, NULL, sys_tcase);
622
623int init_module(void)
624{
625	int err = 0;
626	prk_info("Starting module");
627
628	err = device_register(&tdev);
629	if (err) {
630		prk_err("Unable to register device");
631		goto err0;
632	}
633	prk_info("device registered");
634
635	err = device_create_file(&tdev, &dev_attr_result);
636	if (err) {
637		prk_err("Can't create sysfs file 'result'");
638		goto err1;
639	}
640
641	err = device_create_file(&tdev, &dev_attr_str);
642	if (err) {
643		prk_err("Can't create sysfs file 'str'");
644		goto err2;
645	}
646
647	err = device_create_file(&tdev, &dev_attr_tcase);
648	if (err) {
649		prk_err(": Can't create sysfs file 'tc'");
650		goto err3;
651	}
652
653	err = device_create_file(&tdev, &dev_attr_path);
654	if (err) {
655		prk_err(": Can't create sysfs file 'path'");
656		goto err4;
657	}
658
659	err = device_create_file(&tdev, &dev_attr_acpi_disabled);
660	if (err) {
661		prk_err("Can't create sysfs file 'acpi_disabled'");
662		goto err5;
663	}
664
665	return 0;
666
667err5:
668	device_remove_file(&tdev, &dev_attr_path);
669err4:
670	device_remove_file(&tdev, &dev_attr_tcase);
671err3:
672	device_remove_file(&tdev, &dev_attr_str);
673err2:
674	device_remove_file(&tdev, &dev_attr_result);
675err1:
676	device_unregister(&tdev);
677err0:
678	return err;
679}
680
681void cleanup_module(void)
682{
683	prk_info("Unloading module\n");
684
685	kfree(str_obj_result);
686	kfree(sysfs_path);
687
688	device_remove_file(&tdev, &dev_attr_result);
689	device_remove_file(&tdev, &dev_attr_str);
690	device_remove_file(&tdev, &dev_attr_tcase);
691	device_remove_file(&tdev, &dev_attr_path);
692	device_unregister(&tdev);
693}
694