xref: /linux/drivers/iio/industrialio-acpi.c (revision 7f71507851fc7764b36a3221839607d3a45c2025)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /* IIO ACPI helper functions */
3 
4 #include <linux/acpi.h>
5 #include <linux/device.h>
6 #include <linux/export.h>
7 #include <linux/iio/iio.h>
8 #include <linux/sprintf.h>
9 
10 /**
11  * iio_read_acpi_mount_matrix() - Read accelerometer mount matrix info from ACPI
12  * @dev:		Device structure
13  * @orientation:	iio_mount_matrix struct to fill
14  * @acpi_method:	ACPI method name to read the matrix from, usually "ROTM"
15  *
16  * Try to read the mount-matrix by calling the specified method on the device's
17  * ACPI firmware-node. If the device has no ACPI firmware-node; or the method
18  * does not exist then this will fail silently. This expects the method to
19  * return data in the ACPI "ROTM" format defined by Microsoft:
20  * https://learn.microsoft.com/en-us/windows-hardware/drivers/sensors/sensors-acpi-entries
21  * This is a Microsoft extension and not part of the official ACPI spec.
22  * The method name is configurable because some dual-accel setups define 2 mount
23  * matrices in a single ACPI device using separate "ROMK" and "ROMS" methods.
24  *
25  * Returns: true if the matrix was successfully, false otherwise.
26  */
27 bool iio_read_acpi_mount_matrix(struct device *dev,
28 				struct iio_mount_matrix *orientation,
29 				char *acpi_method)
30 {
31 	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
32 	char *str;
33 	union acpi_object *obj, *elements;
34 	acpi_handle handle;
35 	acpi_status status;
36 	int i, j, val[3];
37 	bool ret = false;
38 
39 	handle = ACPI_HANDLE(dev);
40 	if (!handle)
41 		return false;
42 
43 	if (!acpi_has_method(handle, acpi_method))
44 		return false;
45 
46 	status = acpi_evaluate_object(handle, acpi_method, NULL, &buffer);
47 	if (ACPI_FAILURE(status)) {
48 		dev_err(dev, "Failed to get ACPI mount matrix: %d\n", status);
49 		return false;
50 	}
51 
52 	obj = buffer.pointer;
53 	if (obj->type != ACPI_TYPE_PACKAGE || obj->package.count != 3) {
54 		dev_err(dev, "Unknown ACPI mount matrix package format\n");
55 		goto out_free_buffer;
56 	}
57 
58 	elements = obj->package.elements;
59 	for (i = 0; i < 3; i++) {
60 		if (elements[i].type != ACPI_TYPE_STRING) {
61 			dev_err(dev, "Unknown ACPI mount matrix element format\n");
62 			goto out_free_buffer;
63 		}
64 
65 		str = elements[i].string.pointer;
66 		if (sscanf(str, "%d %d %d", &val[0], &val[1], &val[2]) != 3) {
67 			dev_err(dev, "Incorrect ACPI mount matrix string format\n");
68 			goto out_free_buffer;
69 		}
70 
71 		for (j = 0; j < 3; j++) {
72 			switch (val[j]) {
73 			case -1: str = "-1"; break;
74 			case 0:  str = "0";  break;
75 			case 1:  str = "1";  break;
76 			default:
77 				dev_err(dev, "Invalid value in ACPI mount matrix: %d\n", val[j]);
78 				goto out_free_buffer;
79 			}
80 			orientation->rotation[i * 3 + j] = str;
81 		}
82 	}
83 
84 	ret = true;
85 
86 out_free_buffer:
87 	kfree(buffer.pointer);
88 	return ret;
89 }
90 EXPORT_SYMBOL_GPL(iio_read_acpi_mount_matrix);
91 
92 /**
93  * iio_get_acpi_device_name_and_data() - Return ACPI device instance name and driver data
94  * @dev:		Device structure
95  * @data:		Optional pointer to return driver data
96  *
97  * When device was enumerated by ACPI ID matching, the user might
98  * want to set description for the physical chip. In such cases
99  * the ACPI device instance name might be used. This call may be
100  * performed to retrieve this information.
101  *
102  * NOTE: This helper function exists only for backward compatibility,
103  * do not use in a new code!
104  *
105  * Returns: ACPI device instance name or %NULL.
106  */
107 const char *iio_get_acpi_device_name_and_data(struct device *dev, const void **data)
108 {
109 	const struct acpi_device_id *id;
110 	acpi_handle handle;
111 
112 	handle = ACPI_HANDLE(dev);
113 	if (!handle)
114 		return NULL;
115 
116 	id = acpi_match_device(dev->driver->acpi_match_table, dev);
117 	if (!id)
118 		return NULL;
119 
120 	if (data)
121 		*data = (const void *)id->driver_data;
122 
123 	return dev_name(dev);
124 }
125 EXPORT_SYMBOL_GPL(iio_get_acpi_device_name_and_data);
126