1 // SPDX-License-Identifier: GPL-2.0-only
3 * ChromeOS specific ACPI extensions
5 * Copyright 2022 Google LLC
7 * This driver attaches to the ChromeOS ACPI device and then exports the
8 * values reported by the ACPI in a sysfs directory. All values are
9 * presented in the string form (numbers as decimal values) and can be
10 * accessed as the contents of the appropriate read only files in the
11 * sysfs directory tree.
13 #include <linux/acpi.h>
14 #include <linux/platform_device.h>
15 #include <linux/kernel.h>
16 #include <linux/list.h>
17 #include <linux/module.h>
19 #define ACPI_ATTR_NAME_LEN 4
21 #define DEV_ATTR(_var, _name) \
22 static struct device_attribute dev_attr_##_var = \
23 __ATTR(_name, 0444, chromeos_first_level_attr_show, NULL);
25 #define GPIO_ATTR_GROUP(_group, _name, _num) \
26 static umode_t attr_is_visible_gpio_##_num(struct kobject *kobj, \
27 struct attribute *attr, int n) \
29 if (_num < chromeos_acpi_gpio_groups) \
33 static ssize_t chromeos_attr_show_gpio_##_num(struct device *dev, \
34 struct device_attribute *attr, \
37 char name[ACPI_ATTR_NAME_LEN + 1]; \
40 ret = parse_attr_name(attr->attr.name, name, &num); \
43 return chromeos_acpi_evaluate_method(dev, _num, num, name, buf); \
45 static struct device_attribute dev_attr_0_##_group = \
46 __ATTR(GPIO.0, 0444, chromeos_attr_show_gpio_##_num, NULL); \
47 static struct device_attribute dev_attr_1_##_group = \
48 __ATTR(GPIO.1, 0444, chromeos_attr_show_gpio_##_num, NULL); \
49 static struct device_attribute dev_attr_2_##_group = \
50 __ATTR(GPIO.2, 0444, chromeos_attr_show_gpio_##_num, NULL); \
51 static struct device_attribute dev_attr_3_##_group = \
52 __ATTR(GPIO.3, 0444, chromeos_attr_show_gpio_##_num, NULL); \
54 static struct attribute *attrs_##_group[] = { \
55 &dev_attr_0_##_group.attr, \
56 &dev_attr_1_##_group.attr, \
57 &dev_attr_2_##_group.attr, \
58 &dev_attr_3_##_group.attr, \
61 static const struct attribute_group attr_group_##_group = { \
63 .is_visible = attr_is_visible_gpio_##_num, \
64 .attrs = attrs_##_group, \
67 static unsigned int chromeos_acpi_gpio_groups
;
69 /* Parse the ACPI package and return the data related to that attribute */
70 static int chromeos_acpi_handle_package(struct device
*dev
, union acpi_object
*obj
,
71 int pkg_num
, int sub_pkg_num
, char *name
, char *buf
)
73 union acpi_object
*element
= obj
->package
.elements
;
75 if (pkg_num
>= obj
->package
.count
)
79 if (element
->type
== ACPI_TYPE_PACKAGE
) {
80 if (sub_pkg_num
>= element
->package
.count
)
82 /* select sub element inside this package */
83 element
= element
->package
.elements
;
84 element
+= sub_pkg_num
;
87 switch (element
->type
) {
88 case ACPI_TYPE_INTEGER
:
89 return sysfs_emit(buf
, "%d\n", (int)element
->integer
.value
);
90 case ACPI_TYPE_STRING
:
91 return sysfs_emit(buf
, "%s\n", element
->string
.pointer
);
92 case ACPI_TYPE_BUFFER
:
94 int i
, r
, at
, room_left
;
95 const int byte_per_line
= 16;
98 room_left
= PAGE_SIZE
- 1;
99 for (i
= 0; i
< element
->buffer
.length
&& room_left
; i
+= byte_per_line
) {
100 r
= hex_dump_to_buffer(element
->buffer
.pointer
+ i
,
101 element
->buffer
.length
- i
,
102 byte_per_line
, 1, buf
+ at
, room_left
,
109 r
= sysfs_emit_at(buf
, at
, "\n");
119 dev_info_once(dev
, "truncating sysfs content for %s\n", name
);
120 sysfs_emit_at(buf
, PAGE_SIZE
- 4, "..\n");
121 return PAGE_SIZE
- 1;
124 dev_err(dev
, "element type %d not supported\n", element
->type
);
129 static int chromeos_acpi_evaluate_method(struct device
*dev
, int pkg_num
, int sub_pkg_num
,
130 char *name
, char *buf
)
132 struct acpi_buffer output
= { ACPI_ALLOCATE_BUFFER
, NULL
};
136 status
= acpi_evaluate_object(ACPI_HANDLE(dev
), name
, NULL
, &output
);
137 if (ACPI_FAILURE(status
)) {
138 dev_err(dev
, "failed to retrieve %s. %s\n", name
, acpi_format_exception(status
));
142 if (((union acpi_object
*)output
.pointer
)->type
== ACPI_TYPE_PACKAGE
)
143 ret
= chromeos_acpi_handle_package(dev
, output
.pointer
, pkg_num
, sub_pkg_num
,
146 kfree(output
.pointer
);
150 static int parse_attr_name(const char *name
, char *attr_name
, int *attr_num
)
154 ret
= strscpy(attr_name
, name
, ACPI_ATTR_NAME_LEN
+ 1);
156 return kstrtoint(&name
[ACPI_ATTR_NAME_LEN
+ 1], 0, attr_num
);
160 static ssize_t
chromeos_first_level_attr_show(struct device
*dev
, struct device_attribute
*attr
,
163 char attr_name
[ACPI_ATTR_NAME_LEN
+ 1];
164 int ret
, attr_num
= 0;
166 ret
= parse_attr_name(attr
->attr
.name
, attr_name
, &attr_num
);
169 return chromeos_acpi_evaluate_method(dev
, attr_num
, 0, attr_name
, buf
);
172 static unsigned int get_gpio_pkg_num(struct device
*dev
)
174 struct acpi_buffer output
= { ACPI_ALLOCATE_BUFFER
, NULL
};
175 union acpi_object
*obj
;
177 unsigned int count
= 0;
180 status
= acpi_evaluate_object(ACPI_HANDLE(dev
), name
, NULL
, &output
);
181 if (ACPI_FAILURE(status
)) {
182 dev_err(dev
, "failed to retrieve %s. %s\n", name
, acpi_format_exception(status
));
186 obj
= output
.pointer
;
188 if (obj
->type
== ACPI_TYPE_PACKAGE
)
189 count
= obj
->package
.count
;
191 kfree(output
.pointer
);
195 DEV_ATTR(binf2
, BINF
.2)
196 DEV_ATTR(binf3
, BINF
.3)
203 DEV_ATTR(vbnv0
, VBNV
.0)
204 DEV_ATTR(vbnv1
, VBNV
.1)
207 static struct attribute
*first_level_attrs
[] = {
208 &dev_attr_binf2
.attr
,
209 &dev_attr_binf3
.attr
,
216 &dev_attr_vbnv0
.attr
,
217 &dev_attr_vbnv1
.attr
,
222 static const struct attribute_group first_level_attr_group
= {
223 .attrs
= first_level_attrs
,
227 * Every platform can have a different number of GPIO attribute groups.
228 * Define upper limit groups. At run time, the platform decides to show
229 * the present number of groups only, others are hidden.
231 GPIO_ATTR_GROUP(gpio0
, "GPIO.0", 0)
232 GPIO_ATTR_GROUP(gpio1
, "GPIO.1", 1)
233 GPIO_ATTR_GROUP(gpio2
, "GPIO.2", 2)
234 GPIO_ATTR_GROUP(gpio3
, "GPIO.3", 3)
235 GPIO_ATTR_GROUP(gpio4
, "GPIO.4", 4)
236 GPIO_ATTR_GROUP(gpio5
, "GPIO.5", 5)
237 GPIO_ATTR_GROUP(gpio6
, "GPIO.6", 6)
238 GPIO_ATTR_GROUP(gpio7
, "GPIO.7", 7)
240 static const struct attribute_group
*chromeos_acpi_all_groups
[] = {
241 &first_level_attr_group
,
253 static int chromeos_acpi_device_probe(struct platform_device
*pdev
)
255 chromeos_acpi_gpio_groups
= get_gpio_pkg_num(&pdev
->dev
);
258 * If the platform has more GPIO attribute groups than the number of
259 * groups this driver supports, give out a warning message.
261 if (chromeos_acpi_gpio_groups
> ARRAY_SIZE(chromeos_acpi_all_groups
) - 2)
262 dev_warn(&pdev
->dev
, "Only %zu GPIO attr groups supported by the driver out of total %u.\n",
263 ARRAY_SIZE(chromeos_acpi_all_groups
) - 2, chromeos_acpi_gpio_groups
);
267 static const struct acpi_device_id chromeos_device_ids
[] = {
272 MODULE_DEVICE_TABLE(acpi
, chromeos_device_ids
);
274 static struct platform_driver chromeos_acpi_device_driver
= {
275 .probe
= chromeos_acpi_device_probe
,
277 .name
= KBUILD_MODNAME
,
278 .dev_groups
= chromeos_acpi_all_groups
,
279 .acpi_match_table
= chromeos_device_ids
,
282 module_platform_driver(chromeos_acpi_device_driver
);
284 MODULE_AUTHOR("Muhammad Usama Anjum <usama.anjum@collabora.com>");
285 MODULE_LICENSE("GPL");
286 MODULE_DESCRIPTION("ChromeOS specific ACPI extensions");