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