new file mode 100644
@@ -0,0 +1,126 @@
+What: /sys/bus/acpi/devices/GGL0001:00/BINF.2
+Date: April 2022
+KernelVersion: 5.19
+Description:
+ This file shows information about the current boot of
+ the active EC firmware.
+ * 0 - Read only (recovery) firmware.
+ * 1 - Rewritable firmware.
+
+What: /sys/bus/acpi/devices/GGL0001:00/BINF.3
+Date: April 2022
+KernelVersion: 5.19
+Description:
+ This file shows information about the current boot of
+ the active main firmware type.
+ * 0 - Recovery.
+ * 1 - Normal.
+ * 2 - Developer.
+ * 3 - Netboot (factory installation only).
+
+What: /sys/bus/acpi/devices/GGL0001:00/CHSW
+Date: April 2022
+KernelVersion: 5.19
+Description:
+ This file shows the switch position for the Chrome OS specific
+ hardware switches.
+ * 0 - No changes.
+ * 2 - Recovery button was pressed when firmware booted.
+ * 4 - Recovery button was pressed when EC firmware booted.
+ * 32 - Developer switch was enabled when firmware booted.
+ * 512 - Firmware write protect was disabled when firmware
+ booted.
+
+What: /sys/bus/acpi/devices/GGL0001:00/FMAP
+Date: April 2022
+KernelVersion: 5.19
+Description:
+ This file shows the physical memory address of the start of
+ the main processor firmware flashmap.
+
+What: /sys/bus/acpi/devices/GGL0001:00/FRID
+Date: April 2022
+KernelVersion: 5.19
+Description:
+ This file shows the firmware version for the read-only portion
+ of the main processor firmware.
+
+What: /sys/bus/acpi/devices/GGL0001:00/FWID
+Date: April 2022
+KernelVersion: 5.19
+Description:
+ This file shows the firmware version for the rewritable portion
+ of the main processor firmware.
+
+What: /sys/bus/acpi/devices/GGL0001:00/GPIO.X/GPIO.0
+Date: April 2022
+KernelVersion: 5.19
+Description:
+ This file shows the type of the GPIO signal for the Chrome OS
+ specific GPIO assignments.
+ * 1 - Recovery button.
+ * 2 - Developer mode switch.
+ * 3 - Firmware write protect switch.
+ * 256 to 511 - Debug header GPIO 0 to GPIO 255.
+
+What: /sys/bus/acpi/devices/GGL0001:00/GPIO.X/GPIO.1
+Date: April 2022
+KernelVersion: 5.19
+Description:
+ This file shows the signal attributes of the GPIO signal.
+ * 0 - Signal is active low.
+ * 1 - Signal is active high.
+
+What: /sys/bus/acpi/devices/GGL0001:00/GPIO.X/GPIO.2
+Date: April 2022
+KernelVersion: 5.19
+Description:
+ This file shows the GPIO number on the specified GPIO
+ controller.
+
+What: /sys/bus/acpi/devices/GGL0001:00/GPIO.X/GPIO.3
+Date: April 2022
+KernelVersion: 5.19
+Description:
+ This file shows the name of the GPIO controller.
+
+What: /sys/bus/acpi/devices/GGL0001:00/HWID
+Date: April 2022
+KernelVersion: 5.19
+Description:
+ This file shows the hardware ID for the Chromebook.
+
+What: /sys/bus/acpi/devices/GGL0001:00/MECK
+Date: April 2022
+KernelVersion: 5.19
+Description:
+ This binary file returns the SHA-1 or SHA-256 hash that is
+ read out of the Management Engine extend registers during
+ boot. The hash is exported vi ACPI so the OS can verify that
+ the Management Engine firmware has not changed. If Management
+ Engine is not present, or if the firmware was unable to read the
+ extend registers, this buffer size can be zero.
+
+What: /sys/bus/acpi/devices/GGL0001:00/VBNV.0
+Date: April 2022
+KernelVersion: 5.19
+Description:
+ This file shows the offset in CMOS bank 0 of the verified boot
+ non-volatile storage block, counting from the first writable
+ CMOS byte (that is, 'offset = 0' is the byte following the 14
+ bytes of clock data).
+
+What: /sys/bus/acpi/devices/GGL0001:00/VBNV.1
+Date: April 2022
+KernelVersion: 5.19
+Description:
+ This file shows the size in bytes of the verified boot
+ non-volatile storage block.
+
+What: /sys/bus/acpi/devices/GGL0001:00/VDAT
+Date: April 2022
+KernelVersion: 5.19
+Description:
+ This binary file returns the verified boot data block shared
+ between the firmware verification step and the kernel
+ verification step.
new file mode 100644
@@ -0,0 +1,363 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=====================
+Chrome OS ACPI Device
+=====================
+
+Hardware functionality specific to Chrome OS is exposed through a Chrome OS ACPI device.
+The plug and play ID of a Chrome OS ACPI device is GGL0001. GGL is valid PNP ID of Google.
+PNP ID can be used with the ACPI devices accourding to the guidelines. The following ACPI
+objects are supported:
+
+.. flat-table:: Supported ACPI Objects
+ :widths: 1 2
+ :header-rows: 1
+
+ * - Object
+ - Description
+
+ * - CHSW
+ - Chrome OS switch positions
+
+ * - HWID
+ - Chrome OS hardware ID
+
+ * - FWID
+ - Chrome OS firmware version
+
+ * - FRID
+ - Chrome OS read-only firmware version
+
+ * - BINF
+ - Chrome OS boot information
+
+ * - GPIO
+ - Chrome OS GPIO assignments
+
+ * - VBNV
+ - Chrome OS NVRAM locations
+
+ * - VDTA
+ - Chrome OS verified boot data
+
+ * - FMAP
+ - Chrome OS flashmap base address
+
+ * - MLST
+ - Chrome OS method list
+
+CHSW (Chrome OS switch positions)
+=================================
+This control method returns the switch positions for Chrome OS specific hardware switches.
+
+Arguments:
+----------
+None
+
+Result code:
+------------
+An integer containing the switch positions as bitfields:
+
+.. flat-table::
+ :widths: 1 2
+
+ * - 0x00000002
+ - Recovery button was pressed when x86 firmware booted.
+
+ * - 0x00000004
+ - Recovery button was pressed when EC firmware booted. (required if EC EEPROM is
+ rewritable; otherwise optional)
+
+ * - 0x00000020
+ - Developer switch was enabled when x86 firmware booted.
+
+ * - 0x00000200
+ - Firmware write protect was disabled when x86 firmware booted. (required if
+ firmware write protect is controlled through x86 BIOS; otherwise optional)
+
+All other bits are reserved and should be set to 0.
+
+HWID (Chrome OS hardware ID)
+============================
+This control method returns the hardware ID for the Chromebook.
+
+Arguments:
+----------
+None
+
+Result code:
+------------
+A null-terminated ASCII string containing the hardware ID from the Model-Specific Data area of
+EEPROM.
+
+Note that the hardware ID can be up to 256 characters long, including the terminating null.
+
+FWID (Chrome OS firmware version)
+=================================
+This control method returns the firmware version for the rewritable portion of the main
+processor firmware.
+
+Arguments:
+----------
+None
+
+Result code:
+------------
+A null-terminated ASCII string containing the complete firmware version for the rewritable
+portion of the main processor firmware.
+
+FRID (Chrome OS read-only firmware version)
+===========================================
+This control method returns the firmware version for the read-only portion of the main
+processor firmware.
+
+Arguments:
+----------
+None
+
+Result code:
+------------
+A null-terminated ASCII string containing the complete firmware version for the read-only
+(bootstrap + recovery ) portion of the main processor firmware.
+
+BINF (Chrome OS boot information)
+=================================
+This control method returns information about the current boot.
+
+Arguments:
+----------
+None
+
+Result code:
+------------
+
+.. code-block::
+
+ Package {
+ Reserved1
+ Reserved2
+ Active EC Firmware
+ Active Main Firmware Type
+ Reserved5
+ }
+
+.. flat-table::
+ :widths: 1 1 2
+ :header-rows: 1
+
+ * - Field
+ - Format
+ - Description
+
+ * - Reserved1
+ - DWORD
+ - Set to 256 (0x100). This indicates this field is no longer used.
+
+ * - Reserved2
+ - DWORD
+ - Set to 256 (0x100). This indicates this field is no longer used.
+
+ * - Active EC firmware
+ - DWORD
+ - The EC firmware which was used during boot.
+
+ - 0 - Read-only (recovery) firmware
+ - 1 - Rewritable firmware.
+
+ Set to 0 if EC firmware is always read-only.
+
+ * - Active Main Firmware Type
+ - DWORD
+ - The main firmware type which was used during boot.
+
+ - 0 - Recovery
+ - 1 - Normal
+ - 2 - Developer
+ - 3 - netboot (factory installation only)
+
+ Other values are reserved.
+
+ * - Reserved5
+ - DWORD
+ - Set to 256 (0x100). This indicates this field is no longer used.
+
+GPIO (Chrome OS GPIO assignments)
+=================================
+This control method returns information about Chrome OS specific GPIO assignments for
+Chrome OS hardware, so the kernel can directly control that hardware.
+
+Arguments:
+----------
+None
+
+Result code:
+------------
+.. code-block::
+
+ Package {
+ Package {
+ // First GPIO assignment
+ Signal Type //DWORD
+ Attributes //DWORD
+ Controller Offset //DWORD
+ Controller Name //ASCIIZ
+ },
+ ...
+ Package {
+ // Last GPIO assignment
+ Signal Type //DWORD
+ Attributes //DWORD
+ Controller Offset //DWORD
+ Controller Name //ASCIIZ
+ }
+ }
+
+Where ASCIIZ means a null-terminated ASCII string.
+
+.. flat-table::
+ :widths: 1 1 2
+ :header-rows: 1
+
+ * - Field
+ - Format
+ - Description
+
+ * - Signal Type
+ - DWORD
+ - Type of GPIO signal
+
+ - 0x00000001 - Recovery button
+ - 0x00000002 - Developer mode switch
+ - 0x00000003 - Firmware write protect switch
+ - 0x00000100 - Debug header GPIO 0
+ - ...
+ - 0x000001FF - Debug header GPIO 255
+
+ Other values are reserved.
+
+ * - Attributes
+ - DWORD
+ - Signal attributes as bitfields:
+
+ - 0x00000001 - Signal is active-high (for button, a GPIO value
+ of 1 means the button is pressed; for switches, a GPIO value
+ of 1 means the switch is enabled). If this bit is 0, the signal
+ is active low. Set to 0 for debug header GPIOs.
+
+ * - Controller Offset
+ - DWORD
+ - GPIO number on the specified controller.
+
+ * - Controller Name
+ - ASCIIZ
+ - Name of the controller for the GPIO.
+ Currently supported names:
+ "NM10" - Intel NM10 chip
+
+VBNV (Chrome OS NVRAM locations)
+================================
+This control method returns information about the NVRAM (CMOS) locations used to
+communicate with the BIOS.
+
+Arguments:
+----------
+None
+
+Result code:
+------------
+.. code-block::
+
+ Package {
+ NV Storage Block Offset //DWORD
+ NV Storage Block Size //DWORD
+ }
+
+.. flat-table::
+ :widths: 1 1 2
+ :header-rows: 1
+
+ * - Field
+ - Format
+ - Description
+
+ * - NV Storage Block Offset
+ - DWORD
+ - Offset in CMOS bank 0 of the verified boot non-volatile storage block, counting from
+ the first writable CMOS byte (that is, offset=0 is the byte following the 14 bytes of
+ clock data).
+
+ * - NV Storage Block Size
+ - DWORD
+ - Size in bytes of the verified boot non-volatile storage block.
+
+FMAP (Chrome OS flashmap address)
+=================================
+This control method returns the physical memory address of the start of the main processor
+firmware flashmap.
+
+Arguments:
+----------
+None
+
+NoneResult code:
+----------------
+A DWORD containing the physical memory address of the start of the main processor firmware
+flashmap.
+
+VDTA (Chrome OS verified boot data)
+===================================
+This control method returns the verified boot data block shared between the firmware
+verification step and the kernel verification step.
+
+Arguments:
+----------
+None
+
+Result code:
+------------
+A buffer containing the verified boot data block.
+
+MECK (Management Engine Checksum)
+=================================
+This control method returns the SHA-1 or SHA-256 hash that is read out of the Management
+Engine extend registers during boot. The hash is exported via ACPI so the OS can verify that
+the ME firmware has not changed. If Management Engine is not present, or if the firmware was
+unable to read the extend registers, this buffer can be zero.
+
+Arguments:
+----------
+None
+
+Result code:
+------------
+A buffer containing the ME hash.
+
+MLST (Chrome OS method list)
+============================
+This control method returns a list of the other control methods supported by the Chrome OS
+hardware device.
+
+Arguments:
+----------
+None
+
+Result code:
+------------
+A package containing a list of null-terminated ASCII strings, one for each control method
+supported by the Chrome OS hardware device, not including the MLST method itself.
+For this version of the specification, the result is:
+
+.. code-block::
+
+ Package {
+ "CHSW",
+ "FWID",
+ "HWID",
+ "FRID",
+ "BINF",
+ "GPIO",
+ "VBNV",
+ "FMAP",
+ "VDTA",
+ "MECK"
+ }
@@ -29,3 +29,4 @@ ACPI Support
non-d0-probe
extcon-intel-int3496
intel-pmc-mux
+ chromeos-acpi-device
@@ -322,6 +322,17 @@ config ASUS_NB_WMI
If you have an ACPI-WMI compatible Asus Notebook, say Y or M
here.
+config ACPI_CHROMEOS
+ tristate "ChromeOS specific ACPI extensions"
+ depends on ACPI || COMPILE_TEST
+ help
+ This driver provides the firmware interface for the services
+ exported through the ChromeOS interfaces when using ChromeOS
+ ACPI firmware.
+
+ If you have an ACPI-compatible Chromebook, say Y or M
+ here.
+
config ASUS_TF103C_DOCK
tristate "Asus TF103C 2-in-1 keyboard dock"
depends on ACPI
@@ -44,6 +44,9 @@ obj-$(CONFIG_EEEPC_WMI) += eeepc-wmi.o
# Cisco/Meraki
obj-$(CONFIG_MERAKI_MX100) += meraki-mx100.o
+# Chrome
+obj-$(CONFIG_ACPI_CHROMEOS) += chromeos_acpi.o
+
# Dell
obj-$(CONFIG_X86_PLATFORM_DRIVERS_DELL) += dell/
new file mode 100644
@@ -0,0 +1,515 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ChromeOS specific ACPI extensions
+ *
+ * Copyright 2011 Google, Inc.
+ * Copyright 2020 Google LLC
+ *
+ * This file is a rework and part of the code is ported from chromeos-3.18
+ * kernel and was originally written by Vadim Bendebury <vbendeb@chromium.org>.
+ *
+ * This driver attaches to the ChromeOS ACPI device and then exports the
+ * values reported by the ACPI in a sysfs directory. All values are
+ * presented in the string form (numbers as decimal values) and can be
+ * accessed as the contents of the appropriate read only files in the
+ * sysfs directory tree.
+ */
+
+#include <linux/acpi.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+
+/*
+ * ACPI method name for MLST; the response for this method is a package of
+ * strings listing the methods which should be reflected in sysfs.
+ */
+#define MLST "MLST"
+
+/*
+ * The default list of methods the ChromeOS ACPI device is supposed to export,
+ * if the MLST method is not present or is poorly formed. The MLST method
+ * itself is included, to aid in debugging.
+ */
+static char *chromeos_acpi_default_methods[] = {
+ "CHSW", "HWID", "BINF", "GPIO", "CHNV", "FWID", "FRID", MLST
+};
+
+/*
+ * Representation of a single sysfs attribute. In addition to the standard
+ * bin_attribute structure has a list of these structures (to keep track for
+ * de-allocation when removing the driver) and a pointer to the actual
+ * attribute name and value, reported when accessing the appropriate sysfs
+ * file.
+ */
+struct chromeos_acpi_attribute {
+ struct bin_attribute bin_attr;
+ struct list_head list;
+ char *name;
+ char *data;
+};
+
+/*
+ * Representation of a sysfs attribute group (a sub directory in the device's
+ * sysfs directory). In addition to the standard structure has lists to allow
+ * to keep track of the allocated structures.
+ */
+struct chromeos_acpi_attribute_group {
+ struct attribute_group group;
+ struct list_head attribs;
+ struct list_head list;
+ char *name;
+};
+
+/*
+ * This is the main structure, we use it to store data and adds links pointing
+ * at lists of allocated attributes and attribute groups.
+ */
+struct chromeos_acpi_dev {
+ struct chromeos_acpi_attribute_group *root;
+ const struct attribute_group **dev_groups;
+ struct list_head groups;
+ unsigned int num_groups;
+ unsigned int num_attrs;
+};
+
+static struct chromeos_acpi_dev chromeos_acpi;
+
+static ssize_t chromeos_acpi_read_bin_attribute(struct file *filp,
+ struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buffer, loff_t pos,
+ size_t count)
+{
+ struct chromeos_acpi_attribute *info = bin_attr->private;
+
+ return memory_read_from_buffer(buffer, count, &pos, info->data,
+ info->bin_attr.size);
+}
+
+static char *chromeos_acpi_gen_file_name(char *name, int count, int index)
+{
+ char *str;
+
+ if (count == 1)
+ str = kstrdup(name, GFP_KERNEL);
+ else
+ str = kasprintf(GFP_KERNEL, "%s.%d", name, index);
+
+ return str;
+}
+
+static int
+chromeos_acpi_add_attr(struct chromeos_acpi_attribute_group *aag,
+ union acpi_object *element, char *name,
+ int count, int index)
+{
+ struct chromeos_acpi_attribute *info;
+ char buffer[24]; /* enough to store a u64 and null character */
+ int length;
+ int ret;
+
+ /* Files BINF.{0,1,4} are historical and no longer used. */
+ if (!strcmp(name, "BINF") && (index == 0 || index == 1 || index == 4))
+ return 0;
+
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ info->name = chromeos_acpi_gen_file_name(name, count, index);
+ if (!info->name) {
+ ret = -ENOMEM;
+ goto free_attribute;
+ }
+ sysfs_bin_attr_init(&info->bin_attr);
+ info->bin_attr.attr.name = info->name;
+ info->bin_attr.attr.mode = 0444;
+
+ switch (element->type) {
+ case ACPI_TYPE_BUFFER:
+ length = element->buffer.length;
+ info->data = kmemdup(element->buffer.pointer,
+ length, GFP_KERNEL);
+ break;
+ case ACPI_TYPE_INTEGER:
+ length = snprintf(buffer, sizeof(buffer), "%d",
+ (int)element->integer.value);
+ info->data = kmemdup(buffer, length, GFP_KERNEL);
+ break;
+ case ACPI_TYPE_STRING:
+ length = element->string.length + 1;
+ info->data = kstrdup(element->string.pointer, GFP_KERNEL);
+ break;
+ default:
+ ret = -EINVAL;
+ goto free_attr_name;
+ }
+
+ if (!info->data) {
+ ret = -ENOMEM;
+ goto free_attr_name;
+ }
+
+ info->bin_attr.size = length;
+ info->bin_attr.read = chromeos_acpi_read_bin_attribute;
+ info->bin_attr.private = info;
+
+ INIT_LIST_HEAD(&info->list);
+
+ list_add(&info->list, &aag->attribs);
+ return 0;
+
+free_attr_name:
+ kfree(info->name);
+free_attribute:
+ kfree(info);
+ return ret;
+}
+
+static void
+chromeos_acpi_remove_attribs(struct chromeos_acpi_attribute_group *aag)
+{
+ struct chromeos_acpi_attribute *attr, *tmp_attr;
+
+ list_for_each_entry_safe(attr, tmp_attr, &aag->attribs, list) {
+ kfree(attr->name);
+ kfree(attr->data);
+ kfree(attr);
+ }
+}
+
+static int
+chromeos_acpi_add_attribs_to_group(struct chromeos_acpi_attribute_group *aag,
+ unsigned int num_attrs)
+{
+ struct chromeos_acpi_attribute *attr;
+ int count = 0;
+
+ aag->group.bin_attrs = kcalloc(num_attrs + 1,
+ sizeof(*aag->group.bin_attrs),
+ GFP_KERNEL);
+ if (!aag->group.bin_attrs)
+ return -ENOMEM;
+
+ list_for_each_entry(attr, &aag->attribs, list) {
+ aag->group.bin_attrs[count] = &attr->bin_attr;
+ count++;
+ }
+
+ chromeos_acpi.num_groups++;
+ list_add(&aag->list, &chromeos_acpi.groups);
+
+ return 0;
+}
+
+/**
+ * chromeos_acpi_add_group() - Create a sysfs group including attributes
+ * representing a nested ACPI package.
+ *
+ * @adev: ACPI device.
+ * @obj: Package contents as returned by ACPI.
+ * @name: Name of the group.
+ * @num_attrs: Number of attributes of this package.
+ * @index: Index number of this particular group.
+ *
+ * The created group is called @name in case there is a single instance, or
+ * @name.@index otherwise.
+ *
+ * All group and attribute storage allocations are included in the lists for
+ * tracking of allocated memory.
+ *
+ * Return: 0 on success, negative errno on failure.
+ */
+static int chromeos_acpi_add_group(struct acpi_device *adev,
+ union acpi_object *obj, char *name,
+ int num_attrs, int index)
+{
+ struct chromeos_acpi_attribute_group *aag;
+ union acpi_object *element;
+ int i, count, ret;
+
+ aag = kzalloc(sizeof(*aag), GFP_KERNEL);
+ if (!aag)
+ return -ENOMEM;
+ aag->name = chromeos_acpi_gen_file_name(name, num_attrs, index);
+ if (!aag->name) {
+ ret = -ENOMEM;
+ goto free_group;
+ }
+
+ INIT_LIST_HEAD(&aag->attribs);
+ INIT_LIST_HEAD(&aag->list);
+
+ count = obj->package.count;
+ element = obj->package.elements;
+ for (i = 0; i < count; i++, element++) {
+ ret = chromeos_acpi_add_attr(aag, element, name, count, i);
+ if (ret)
+ goto free_group_attr;
+ }
+
+ aag->group.name = aag->name;
+
+ ret = chromeos_acpi_add_attribs_to_group(aag, count);
+ if (ret)
+ goto free_group_attr;
+
+ return 0;
+
+free_group_attr:
+ chromeos_acpi_remove_attribs(aag);
+ kfree(aag->name);
+free_group:
+ kfree(aag);
+ return ret;
+}
+
+static void chromeos_acpi_remove_groups(void)
+{
+ struct chromeos_acpi_attribute_group *aag, *tmp_aag;
+
+ list_for_each_entry_safe(aag, tmp_aag, &chromeos_acpi.groups, list) {
+ chromeos_acpi_remove_attribs(aag);
+ kfree(aag->group.bin_attrs);
+ kfree(aag->name);
+ kfree(aag);
+ }
+}
+
+/**
+ * chromeos_acpi_handle_package() - Create sysfs group including attributes
+ * representing an ACPI package.
+ *
+ * @adev: ACPI device.
+ * @obj: Package contents as returned by ACPI.
+ * @name: Name of the group.
+ *
+ * Scalar objects included in the package get sysfs attributes created for
+ * them. Nested packages are passed to a function creating a sysfs group per
+ * package.
+ *
+ * Return: 0 on success, negative errno on failure.
+ */
+static int chromeos_acpi_handle_package(struct acpi_device *adev,
+ union acpi_object *obj, char *name)
+{
+ struct device *dev = &adev->dev;
+ int count = obj->package.count;
+ union acpi_object *element;
+ int i, ret;
+
+ element = obj->package.elements;
+ for (i = 0; i < count; i++, element++) {
+ if (element->type == ACPI_TYPE_BUFFER ||
+ element->type == ACPI_TYPE_STRING ||
+ element->type == ACPI_TYPE_INTEGER) {
+ /* Create a single attribute in the root directory */
+ ret = chromeos_acpi_add_attr(chromeos_acpi.root,
+ element, name,
+ count, i);
+ if (ret) {
+ dev_err(dev, "error adding attributes (%d)\n",
+ ret);
+ return ret;
+ }
+ chromeos_acpi.num_attrs++;
+ } else if (element->type == ACPI_TYPE_PACKAGE) {
+ /* Create a group of attributes */
+ ret = chromeos_acpi_add_group(adev, element, name,
+ count, i);
+ if (ret) {
+ dev_err(dev, "error adding a group (%d)\n",
+ ret);
+ return ret;
+ }
+ } else {
+ if (ret) {
+ dev_err(dev, "error on element type (%d)\n",
+ ret);
+ return -EINVAL;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * chromeos_acpi_add_method() - Evaluate an ACPI method and create sysfs
+ * attributes.
+ *
+ * @adev: ACPI device
+ * @name: Name of the method to evaluate
+ *
+ * Return: 0 on success, non-zero on failure
+ */
+static int chromeos_acpi_add_method(struct acpi_device *adev, char *name)
+{
+ struct device *dev = &adev->dev;
+ struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
+ acpi_status status;
+ int ret = 0;
+
+ status = acpi_evaluate_object(adev->handle, name, NULL, &output);
+ if (ACPI_FAILURE(status)) {
+ dev_err(dev, "failed to retrieve %s (%d)\n", name, status);
+ return status;
+ }
+
+ if (((union acpi_object *)output.pointer)->type == ACPI_TYPE_PACKAGE)
+ ret = chromeos_acpi_handle_package(adev, output.pointer, name);
+
+ kfree(output.pointer);
+ return ret;
+}
+
+/**
+ * chromeos_acpi_process_mlst() - Evaluate the MLST method and add methods
+ * listed in the response.
+ *
+ * @adev: ACPI device
+ *
+ * Returns: 0 if successful, non-zero if error.
+ */
+static int chromeos_acpi_process_mlst(struct acpi_device *adev)
+{
+ struct chromeos_acpi_attribute_group *aag;
+ char name[ACPI_NAMESEG_SIZE + 1];
+ union acpi_object *element, *obj;
+ struct device *dev = &adev->dev;
+ struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
+ acpi_status status;
+ int ret = 0;
+ int size;
+ int i;
+
+ status = acpi_evaluate_object(adev->handle, MLST, NULL,
+ &output);
+ if (ACPI_FAILURE(status))
+ return status;
+
+ obj = output.pointer;
+ if (obj->type != ACPI_TYPE_PACKAGE) {
+ ret = -EINVAL;
+ goto free_acpi_buffer;
+ }
+
+ element = obj->package.elements;
+ for (i = 0; i < obj->package.count; i++, element++) {
+ if (element->type == ACPI_TYPE_STRING) {
+ size = min(element->string.length + 1,
+ (u32)ACPI_NAMESEG_SIZE + 1);
+ strscpy(name, element->string.pointer, size);
+ ret = chromeos_acpi_add_method(adev, name);
+ if (ret) {
+ chromeos_acpi_remove_groups();
+ break;
+ }
+ }
+ }
+
+ /* Add root attributes to the main group */
+ ret = chromeos_acpi_add_attribs_to_group(chromeos_acpi.root,
+ chromeos_acpi.num_attrs);
+ if (ret)
+ goto free_acpi_buffer;
+
+ chromeos_acpi.dev_groups = kcalloc(chromeos_acpi.num_groups + 1,
+ sizeof(struct attribute_group),
+ GFP_KERNEL);
+
+ i = 0;
+ list_for_each_entry(aag, &chromeos_acpi.groups, list) {
+ chromeos_acpi.dev_groups[i] = &aag->group;
+ i++;
+ }
+
+ ret = sysfs_create_groups(&dev->kobj, chromeos_acpi.dev_groups);
+ if (ret) {
+ kfree(chromeos_acpi.dev_groups);
+
+ /* Remove allocated chromeos acpi groups and attributes */
+ chromeos_acpi_remove_groups();
+ }
+
+free_acpi_buffer:
+ kfree(output.pointer);
+ return ret;
+}
+
+static int chromeos_acpi_device_add(struct acpi_device *adev)
+{
+ struct chromeos_acpi_attribute_group *aag;
+ struct device *dev = &adev->dev;
+ int i, ret;
+
+ aag = kzalloc(sizeof(*aag), GFP_KERNEL);
+ if (!aag)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&aag->attribs);
+ INIT_LIST_HEAD(&aag->list);
+ INIT_LIST_HEAD(&chromeos_acpi.groups);
+
+ chromeos_acpi.root = aag;
+
+ /*
+ * Attempt to add methods by querying the device's MLST method
+ * for the list of methods.
+ */
+ if (!chromeos_acpi_process_mlst(adev))
+ return 0;
+
+ dev_dbg(dev, "falling back to default list of methods\n");
+
+ for (i = 0; i < ARRAY_SIZE(chromeos_acpi_default_methods); i++) {
+ ret = chromeos_acpi_add_method(adev,
+ chromeos_acpi_default_methods[i]);
+ if (ret) {
+ dev_err(dev, "failed to add default methods (%d)\n",
+ ret);
+ goto free_group_root;
+ }
+ }
+
+ return 0;
+
+free_group_root:
+ kfree(chromeos_acpi.root);
+ return ret;
+}
+
+static int chromeos_acpi_device_remove(struct acpi_device *adev)
+{
+ /* Remove sysfs groups */
+ sysfs_remove_groups(&adev->dev.kobj, chromeos_acpi.dev_groups);
+ kfree(chromeos_acpi.dev_groups);
+
+ /* Remove allocated chromeos acpi groups and attributes */
+ chromeos_acpi_remove_groups();
+
+ return 0;
+}
+
+/* GGL is valid PNP ID of Google. PNP ID can be used with the ACPI devices. */
+static const struct acpi_device_id chromeos_device_ids[] = {
+ { "GGL0001", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, chromeos_device_ids);
+
+static struct acpi_driver chromeos_acpi_driver = {
+ .name = "chromeos-acpi",
+ .class = "chromeos-acpi",
+ .ids = chromeos_device_ids,
+ .ops = {
+ .add = chromeos_acpi_device_add,
+ .remove = chromeos_acpi_device_remove,
+ },
+};
+module_acpi_driver(chromeos_acpi_driver);
+
+MODULE_AUTHOR("Enric Balletbo i Serra <enric.balletbo@collabora.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("ChromeOS specific ACPI extensions");