2 * chromeos_acpi.c - ChromeOS specific ACPI support
5 * Copyright (C) 2011 The Chromium OS Authors
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 * This driver attaches to the ChromeOS ACPI device and the exports the values
22 * reported by the ACPI in a sysfs directory
23 * (/sys/devices/platform/chromeos_acpi).
25 * The first version of the driver provides only static information; the
26 * values reported by the driver are the snapshot reported by the ACPI at
27 * driver installation time.
29 * All values are presented in the string form (numbers as decimal values) and
30 * can be accessed as the contents of the appropriate read only files in the
31 * sysfs directory tree originating in /sys/devices/platform/chromeos_acpi.
34 #include <linux/kernel.h>
35 #include <linux/module.h>
36 #include <linux/nvram.h>
37 #include <linux/platform_device.h>
38 #include <linux/acpi.h>
40 #include "../chromeos.h"
42 #define CHNV_DEBUG_RESET_FLAG 0x40 /* flag for S3 reboot */
43 #define CHNV_RECOVERY_FLAG 0x80 /* flag for recovery reboot */
45 #define CHSW_RECOVERY_FW 0x00000002 /* recovery button depressed */
46 #define CHSW_RECOVERY_EC 0x00000004 /* recovery button depressed */
47 #define CHSW_DEVELOPER_MODE 0x00000020 /* developer switch set */
48 #define CHSW_WP 0x00000200 /* write-protect (optional) */
51 * Structure containing one ACPI exported integer along with the validity
54 struct chromeos_acpi_datum {
60 * Structure containing the set of ACPI exported integers required by chromeos
63 struct chromeos_acpi_if {
64 struct chromeos_acpi_datum switch_state;
66 /* chnv is a single byte offset in nvram. exported by older firmware */
67 struct chromeos_acpi_datum chnv;
69 /* vbnv is an address range in nvram, exported by newer firmware */
70 struct chromeos_acpi_datum nv_base;
71 struct chromeos_acpi_datum nv_size;
74 #define MY_LOGPREFIX "chromeos_acpi: "
75 #define MY_ERR KERN_ERR MY_LOGPREFIX
76 #define MY_NOTICE KERN_NOTICE MY_LOGPREFIX
77 #define MY_INFO KERN_INFO MY_LOGPREFIX
79 /* ACPI method name for MLST; the response for this method is a
80 * package of strings listing the methods which should be reflected in
82 #define MLST_METHOD "MLST"
84 static const struct acpi_device_id chromeos_device_ids[] = {
85 {"GGL0001", 0}, /* Google's own */
89 MODULE_DEVICE_TABLE(acpi, chromeos_device_ids);
91 static int chromeos_device_add(struct acpi_device *device);
92 static int chromeos_device_remove(struct acpi_device *device, int type);
94 static struct chromeos_acpi_if chromeos_acpi_if_data;
95 static struct acpi_driver chromeos_acpi_driver = {
96 .name = "ChromeOS Device",
98 .ids = chromeos_device_ids,
100 .add = chromeos_device_add,
101 .remove = chromeos_device_remove,
103 .owner = THIS_MODULE,
106 /* The default list of methods the chromeos ACPI device is supposed to export,
107 * if the MLST method is not present or is poorly formed. The MLST method
108 * itself is included, to aid in debugging. */
109 static char *default_methods[] = {
110 "CHSW", "HWID", "BINF", "GPIO", "CHNV", "FWID", "FRID", MLST_METHOD
114 * Representation of a single sys fs attribute. In addition to the standard
115 * device_attribute structure has a link field, allowing to create a list of
116 * these structures (to keep track for de-allocation when removing the driver)
117 * and a pointer to the actual attribute value, reported when accessing the
118 * appropriate sys fs file
120 struct acpi_attribute {
121 struct device_attribute dev_attr;
122 struct acpi_attribute *next_acpi_attr;
127 * Representation of a sys fs attribute group (a sub directory in the device's
128 * sys fs directory). In addition to the standard structure has a link to
129 * allow to keep track of the allocated structures.
131 struct acpi_attribute_group {
132 struct attribute_group ag;
133 struct acpi_attribute_group *next_acpi_attr_group;
137 * ChromeOS ACPI device wrapper adds links pointing at lists of allocated
138 * attributes and attribute groups.
140 struct chromeos_acpi_dev {
141 struct platform_device *p_dev;
142 struct acpi_attribute *attributes;
143 struct acpi_attribute_group *groups;
146 static struct chromeos_acpi_dev chromeos_acpi = { };
148 static bool chromeos_on_legacy_firmware(void)
151 * Presense of the CHNV ACPI element implies running on a legacy
154 return chromeos_acpi_if_data.chnv.cad_is_set;
158 * This function operates on legacy BIOSes which do not export VBNV element
159 * through ACPI. These BIOSes use a fixed location in NVRAM to contain a
160 * bitmask of known flags.
162 * @flag - the bitmask to set, it is the responsibility of the caller to set
165 * returns 0 on success (is running in legacy mode and chnv is initialized) or
168 static int chromeos_set_nvram_flag(u8 flag)
171 unsigned index = chromeos_acpi_if_data.chnv.cad_value;
173 if (!chromeos_on_legacy_firmware())
176 cur = nvram_read_byte(index);
178 if ((cur & flag) != flag)
179 nvram_write_byte(cur | flag, index);
183 int chromeos_legacy_set_need_recovery(void)
185 return chromeos_set_nvram_flag(CHNV_RECOVERY_FLAG);
189 * Read the nvram buffer contents into the user provided space.
191 * retrun number of bytes copied, or -1 on any error.
193 static ssize_t chromeos_vbc_nvram_read(void *buf, size_t count)
198 if (!chromeos_acpi_if_data.nv_base.cad_is_set ||
199 !chromeos_acpi_if_data.nv_size.cad_is_set) {
200 printk(MY_ERR "%s: NVRAM not configured!\n", __func__);
204 base = chromeos_acpi_if_data.nv_base.cad_value;
205 size = chromeos_acpi_if_data.nv_size.cad_value;
208 pr_err("%s: not enough room to read nvram (%zd < %d)\n",
209 __func__, count, size);
213 for (i = 0; i < size; i++)
214 ((u8 *)buf)[i] = nvram_read_byte(base++);
219 static ssize_t chromeos_vbc_nvram_write(const void *buf, size_t count)
221 unsigned base, size, i;
223 if (!chromeos_acpi_if_data.nv_base.cad_is_set ||
224 !chromeos_acpi_if_data.nv_size.cad_is_set) {
225 printk(MY_ERR "%s: NVRAM not configured!\n", __func__);
229 size = chromeos_acpi_if_data.nv_size.cad_value;
230 base = chromeos_acpi_if_data.nv_base.cad_value;
233 printk(MY_ERR "%s: wrong buffer size (%zd != %d)!\n", __func__,
238 for (i = 0; i < size; i++) {
241 c = nvram_read_byte(base + i);
242 if (c == ((u8 *)buf)[i])
244 nvram_write_byte(((u8 *)buf)[i], base + i);
250 * To show attribute value just access the container structure's `value'
253 static ssize_t show_acpi_attribute(struct device *dev,
254 struct device_attribute *attr, char *buf)
256 struct acpi_attribute *paa;
258 paa = container_of(attr, struct acpi_attribute, dev_attr);
259 return snprintf(buf, PAGE_SIZE, paa->value);
263 * create_sysfs_attribute() create and initialize an ACPI sys fs attribute
265 * @value: attribute value
266 * @name: base attribute name
267 * @count: total number of instances of this attribute
268 * @instance: instance number of this particular attribute
270 * This function allocates and initializes the structure containing all
271 * information necessary to add a sys fs attribute. In case the attribute has
272 * just a single instance, the attribute file name is equal to the @name
273 * parameter . In case the attribute has several instances, the attribute
274 * file name is @name.@instance.
276 * Returns: a pointer to the allocated and initialized structure, or null if
279 * As a side effect, the allocated structure is added to the list in the
280 * chromeos_acpi structure. Note that the actual attribute creation is not
281 * attempted yet, in case of creation error the structure would not have an
282 * actual attribute associated with it, so when de-installing the driver this
283 * structure would be used to try to remove an attribute which does not exist.
284 * This is considered acceptable, as there is no reason for sys fs attribute
287 static struct acpi_attribute *create_sysfs_attribute(char *value, char *name,
288 int count, int instance)
290 struct acpi_attribute *paa;
291 int total_size, room_left;
292 int value_len = strlen(value);
297 value_len++; /* include the terminating zero */
300 * total allocation size includes (all strings with including
301 * terminating zeros):
304 * - attribute structure size
306 * - suffix string (in case there are multiple instances)
307 * - dot separating the instance suffix
310 total_size = value_len + sizeof(struct acpi_attribute) +
315 printk(MY_ERR "%s: too many (%d) instances of %s\n",
316 __func__, count, name);
319 /* allow up to three digits and the dot */
323 paa = kzalloc(total_size, GFP_KERNEL);
325 printk(MY_ERR "out of memory in %s!\n", __func__);
329 sysfs_attr_init(&paa->dev_attr.attr);
330 paa->dev_attr.attr.mode = 0444; /* read only */
331 paa->dev_attr.show = show_acpi_attribute;
332 paa->value = (char *)(paa + 1);
333 strcpy(paa->value, value);
334 paa->dev_attr.attr.name = paa->value + value_len;
336 room_left = total_size - value_len -
337 offsetof(struct acpi_attribute, value);
340 snprintf((char *)paa->dev_attr.attr.name, room_left, name);
342 snprintf((char *)paa->dev_attr.attr.name, room_left,
343 "%s.%d", name, instance);
346 paa->next_acpi_attr = chromeos_acpi.attributes;
347 chromeos_acpi.attributes = paa;
353 * add_sysfs_attribute() create and initialize an ACPI sys fs attribute
354 * structure and create the attribute.
355 * @value: attribute value
356 * @name: base attribute name
357 * @count: total number of instances of this attribute
358 * @instance: instance number of this particular attribute
361 static void add_sysfs_attribute(char *value, char *name,
362 int count, int instance)
364 struct acpi_attribute *paa =
365 create_sysfs_attribute(value, name, count, instance);
370 if (device_create_file(&chromeos_acpi.p_dev->dev, &paa->dev_attr))
371 printk(MY_ERR "failed to create attribute for %s\n", name);
375 * handle_nested_acpi_package() create sysfs group including attributes
376 * representing a nested ACPI package.
378 * @po: package contents as returned by ACPI
379 * @pm: name of the group
380 * @total: number of instances of this package
381 * @instance: instance number of this particular group
383 * The created group is called @pm in case there is a single instance, or
384 * @pm.@instance otherwise.
386 * All group and attribute storage allocations are included in the lists for
387 * tracking of allocated memory.
389 static void handle_nested_acpi_package(union acpi_object *po, char *pm,
390 int total, int instance)
392 int i, size, count, j;
393 struct acpi_attribute_group *aag;
395 count = po->package.count;
397 size = strlen(pm) + 1 + sizeof(struct acpi_attribute_group) +
398 sizeof(struct attribute *) * (count + 1);
402 printk(MY_ERR "%s: too many (%d) instances of %s\n",
403 __func__, total, pm);
406 /* allow up to three digits and the dot */
410 aag = kzalloc(size, GFP_KERNEL);
412 printk(MY_ERR "out of memory in %s!\n", __func__);
416 aag->next_acpi_attr_group = chromeos_acpi.groups;
417 chromeos_acpi.groups = aag->next_acpi_attr_group;
418 aag->ag.attrs = (struct attribute **)(aag + 1);
419 aag->ag.name = (const char *)&aag->ag.attrs[count + 1];
421 /* room left in the buffer */
422 size = size - (aag->ag.name - (char *)aag);
425 snprintf((char *)aag->ag.name, size, "%s.%d", pm, instance);
427 snprintf((char *)aag->ag.name, size, "%s", pm);
429 j = 0; /* attribute index */
430 for (i = 0; i < count; i++) {
431 union acpi_object *element = po->package.elements + i;
433 char attr_value[40]; /* 40 chars be enough for names */
434 struct acpi_attribute *paa;
436 switch (element->type) {
437 case ACPI_TYPE_INTEGER:
438 copy_size = snprintf(attr_value, sizeof(attr_value),
439 "%d", (int)element->integer.value);
440 paa = create_sysfs_attribute(attr_value, pm, count, i);
443 case ACPI_TYPE_STRING:
444 copy_size = min(element->string.length,
445 (u32)(sizeof(attr_value)) - 1);
446 memcpy(attr_value, element->string.pointer, copy_size);
447 attr_value[copy_size] = '\0';
448 paa = create_sysfs_attribute(attr_value, pm, count, i);
452 printk(MY_ERR "ignoring nested type %d\n",
456 aag->ag.attrs[j++] = &paa->dev_attr.attr;
459 if (sysfs_create_group(&chromeos_acpi.p_dev->dev.kobj, &aag->ag))
460 printk(MY_ERR "failed to create group %s.%d\n", pm, instance);
464 * maybe_export_acpi_int() export a single int value when required
466 * @pm: name of the package
467 * @index: index of the element of the package
468 * @value: value of the element
470 static void maybe_export_acpi_int(const char *pm, int index, unsigned value)
473 struct chromeos_acpi_exported_ints {
474 const char *acpi_name;
476 struct chromeos_acpi_datum *cad;
477 } exported_ints[] = {
478 { "VBNV", 0, &chromeos_acpi_if_data.nv_base },
479 { "VBNV", 1, &chromeos_acpi_if_data.nv_size },
480 { "CHSW", 0, &chromeos_acpi_if_data.switch_state },
481 { "CHNV", 0, &chromeos_acpi_if_data.chnv }
484 for (i = 0; i < ARRAY_SIZE(exported_ints); i++) {
485 struct chromeos_acpi_exported_ints *exported_int;
487 exported_int = exported_ints + i;
489 if (!strncmp(pm, exported_int->acpi_name, 4) &&
490 (exported_int->acpi_index == index)) {
491 printk(MY_NOTICE "registering %s %d\n", pm, index);
492 exported_int->cad->cad_value = value;
493 exported_int->cad->cad_is_set = true;
500 * acpi_buffer_to_string() convert contents of an ACPI buffer element into a
501 * hex string truncating it if necessary to fit into one page.
503 * @element: an acpi element known to contain an ACPI buffer.
505 * Returns: pointer to an ASCII string containing the buffer representation
506 * (whatever fit into PAGE_SIZE). The caller is responsible for
507 * freeing the memory.
509 static char *acpi_buffer_to_string(union acpi_object *element)
514 /* Include this many characters per line */
515 unsigned char_per_line = 16;
517 unsigned string_buffer_size;
520 * As of now the VDAT structure can supply as much as 3700 bytes. When
521 * expressed as a hex dump it becomes 3700 * 3 + 3700/16 + .. which
522 * clearly exceeds the maximum allowed sys fs buffer size of one page
525 * What this means is that we can't keep the entire blob in one sysfs
526 * file. Currently verified boot (the consumer of the VDAT contents)
527 * does not care about the most of the data, so as a quick fix we will
528 * truncate it here. Once the blob data beyond the 4K boundary is
529 * required this approach will have to be reworked.
531 * TODO(vbendeb): Split the data into multiple VDAT instances, each
532 * not exceeding 4K or consider exporting as a binary using
533 * sysfs_create_bin_file().
537 * X, the maximum number of bytes which will fit into a sysfs file
538 * (one memory page) can be derived from the following equation (where
539 * N is number of bytes included in every hex string):
541 * 3X + X/N + 4 <= PAGE_SIZE.
543 * Solving this for X gives the following
545 blob_size = ((PAGE_SIZE - 4) * char_per_line) / (char_per_line * 3 + 1);
547 if (element->buffer.length > blob_size)
548 printk(MY_INFO "truncating buffer from %d to %d\n",
549 element->buffer.length, blob_size);
551 blob_size = element->buffer.length;
554 /* three characters to display one byte */
556 /* one newline per line, all rounded up, plus
557 * extra newline in the end, plus terminating
560 blob_size/char_per_line + 4;
562 p = kzalloc(string_buffer_size, GFP_KERNEL);
564 printk(MY_ERR "out of memory in %s!\n", __func__);
569 room_left = string_buffer_size;
570 for (i = 0; i < blob_size; i++) {
572 printed = snprintf(p, room_left, " %2.2x",
573 element->buffer.pointer[i]);
574 room_left -= printed;
576 if (((i + 1) % char_per_line) == 0) {
584 printk(MY_ERR "%s: no room in the buffer!\n", __func__);
594 * handle_acpi_package() create sysfs group including attributes
595 * representing an ACPI package.
597 * @po: package contents as returned by ACPI
598 * @pm: name of the group
600 * Scalar objects included in the package get sys fs attributes created for
601 * them. Nested packages are passed to a function creating a sys fs group per
604 static void handle_acpi_package(union acpi_object *po, char *pm)
607 int count = po->package.count;
608 for (j = 0; j < count; j++) {
609 union acpi_object *element = po->package.elements + j;
611 char attr_value[256]; /* strings could be this long */
613 switch (element->type) {
614 case ACPI_TYPE_INTEGER:
615 copy_size = snprintf(attr_value, sizeof(attr_value),
616 "%d", (int)element->integer.value);
617 add_sysfs_attribute(attr_value, pm, count, j);
618 maybe_export_acpi_int(pm, j, (unsigned)
619 element->integer.value);
622 case ACPI_TYPE_STRING:
623 copy_size = min(element->string.length,
624 (u32)(sizeof(attr_value)) - 1);
625 memcpy(attr_value, element->string.pointer, copy_size);
626 attr_value[copy_size] = '\0';
627 add_sysfs_attribute(attr_value, pm, count, j);
630 case ACPI_TYPE_BUFFER: {
632 buf_str = acpi_buffer_to_string(element);
634 add_sysfs_attribute(buf_str, pm, count, j);
639 case ACPI_TYPE_PACKAGE:
640 handle_nested_acpi_package(element, pm, count, j);
644 printk(MY_ERR "ignoring type %d (%s)\n",
653 * add_acpi_method() evaluate an ACPI method and create sysfs attributes.
655 * @device: ACPI device
656 * @pm: name of the method to evaluate
658 static void add_acpi_method(struct acpi_device *device, char *pm)
661 struct acpi_buffer output;
662 union acpi_object *po;
664 output.length = ACPI_ALLOCATE_BUFFER;
665 output.pointer = NULL;
667 status = acpi_evaluate_object(device->handle, pm, NULL, &output);
669 if (!ACPI_SUCCESS(status)) {
670 printk(MY_ERR "failed to retrieve %s (%d)\n", pm, status);
676 if (po->type != ACPI_TYPE_PACKAGE)
677 printk(MY_ERR "%s is not a package, ignored\n", pm);
679 handle_acpi_package(po, pm);
680 kfree(output.pointer);
684 * chromeos_process_mlst() Evaluate the MLST method and add methods listed
687 * @device: ACPI device
689 * Returns: 0 if successful, non-zero if error.
691 static int chromeos_process_mlst(struct acpi_device *device)
694 struct acpi_buffer output;
695 union acpi_object *po;
698 output.length = ACPI_ALLOCATE_BUFFER;
699 output.pointer = NULL;
701 status = acpi_evaluate_object(device->handle, MLST_METHOD, NULL,
703 if (!ACPI_SUCCESS(status)) {
704 pr_debug(MY_LOGPREFIX "failed to retrieve MLST (%d)\n",
710 if (po->type != ACPI_TYPE_PACKAGE) {
711 printk(MY_ERR MLST_METHOD "is not a package, ignored\n");
712 kfree(output.pointer);
716 for (j = 0; j < po->package.count; j++) {
717 union acpi_object *element = po->package.elements + j;
719 char method[ACPI_NAME_SIZE + 1];
721 if (element->type == ACPI_TYPE_STRING) {
722 copy_size = min(element->string.length,
723 (u32)ACPI_NAME_SIZE);
724 memcpy(method, element->string.pointer, copy_size);
725 method[copy_size] = '\0';
726 add_acpi_method(device, method);
728 pr_debug(MY_LOGPREFIX "ignoring type %d\n",
733 kfree(output.pointer);
737 static int chromeos_device_add(struct acpi_device *device)
741 /* Attempt to add methods by querying the device's MLST method
742 * for the list of methods. */
743 if (!chromeos_process_mlst(device))
746 printk(MY_INFO "falling back to default list of methods\n");
747 for (i = 0; i < ARRAY_SIZE(default_methods); i++)
748 add_acpi_method(device, default_methods[i]);
752 static int chromeos_device_remove(struct acpi_device *device, int type)
757 static struct chromeos_vbc chromeos_vbc_nvram = {
758 .name = "chromeos_vbc_nvram",
759 .read = chromeos_vbc_nvram_read,
760 .write = chromeos_vbc_nvram_write,
763 static int __init chromeos_acpi_init(void)
770 ret = chromeos_vbc_register(&chromeos_vbc_nvram);
774 chromeos_acpi.p_dev = platform_device_register_simple("chromeos_acpi",
776 if (IS_ERR(chromeos_acpi.p_dev)) {
777 printk(MY_ERR "unable to register platform device\n");
778 return PTR_ERR(chromeos_acpi.p_dev);
781 ret = acpi_bus_register_driver(&chromeos_acpi_driver);
783 printk(MY_ERR "failed to register driver (%d)\n", ret);
784 platform_device_unregister(chromeos_acpi.p_dev);
785 chromeos_acpi.p_dev = NULL;
788 printk(MY_INFO "installed%s\n",
789 chromeos_on_legacy_firmware() ? " (legacy mode)" : "");
793 subsys_initcall(chromeos_acpi_init);