chrome platform changes for 5.19

cros_ec:
 * Fix wrong error handling path.
 * Clean-up patches.
 
 cros_ec_chardev:
 * Re-introduce cros_ec_cmd_xfer to fix ABI broken.
 
 cros_ec_lpcs:
 * Support the Framework Laptop.
 
 cros_ec_typec:
 * Fix NULL dereference.
 
 chromeos_acpi:
 * Add ChromeOS ACPI device driver.
 * Fix Sphinx errors when `make htmldocs`.
 
 misc:
 * Drop BUG_ON()s.
 -----BEGIN PGP SIGNATURE-----
 
 iIkEABYIADEWIQS0yQeDP3cjLyifNRUrxTEGBto89AUCYoxDdBMcdHp1bmdiaUBr
 ZXJuZWwub3JnAAoJECvFMQYG2jz0UtgA/i2XEXq9znb7BriG43ZvplcFD4sMVGcQ
 xz4V4jHjNWIPAQDPOf28QMybiBQv3wWpLrx+gBLhYxRvEoeWxmbwN0UHAA==
 =9qNL
 -----END PGP SIGNATURE-----

Merge tag 'tag-chrome-platform-for-v5.19' of git://git.kernel.org/pub/scm/linux/kernel/git/chrome-platform/linux

Pull chrome platform updates from Tzung-Bi Shih:
 "cros_ec:
   - Fix wrong error handling path
   - Clean-up patches

  cros_ec_chardev:
   - Re-introduce cros_ec_cmd_xfer to fix ABI broken

  cros_ec_lpcs:
   - Support the Framework Laptop

  cros_ec_typec:
   - Fix NULL dereference

  chromeos_acpi:
   - Add ChromeOS ACPI device driver
   - Fix Sphinx errors when `make htmldocs`

  misc:
   - Drop BUG_ON()s"

* tag 'tag-chrome-platform-for-v5.19' of git://git.kernel.org/pub/scm/linux/kernel/git/chrome-platform/linux:
  platform/chrome: Use imperative mood for ChromeOS ACPI sysfs ABI descriptions
  platform/chrome: Use tables for values lists of ChromeOS ACPI sysfs ABI
  platform/chrome: cros_ec_spi: drop BUG_ON() if `din` isn't large enough
  platform/chrome: cros_ec_spi: drop unneeded BUG_ON()
  platform/chrome: cros_ec_i2c: drop BUG_ON() in cros_ec_pkt_xfer_i2c()
  platform/chrome: cros_ec_proto: drop BUG_ON() in cros_ec_get_host_event()
  platform/chrome: cros_ec_proto: drop BUG_ON() in cros_ec_prepare_tx()
  platform/chrome: correct cros_ec_prepare_tx() usage
  platform/chrome: cros_ec_proto: drop unneeded BUG_ON() in prepare_packet()
  platform/chrome: Add ChromeOS ACPI device driver
  platform/chrome: cros_ec_typec: Check for EC driver
  platform/chrome: cros_ec_lpcs: reserve the MEC LPC I/O ports first
  platform/chrome: cros_ec_lpcs: detect the Framework Laptop
  platform/chrome: Re-introduce cros_ec_cmd_xfer and use it for ioctls
  platform/chrome: cros_ec: append newline to all logs
  platform/chrome: cros_ec: sort header inclusion alphabetically
  platform/chrome: cros_ec: determine `wake_enabled` in cros_ec_suspend()
  platform/chrome: cros_ec: remove unused variable `was_wake_device`
  platform/chrome: cros_ec: fix error handling in cros_ec_register()
This commit is contained in:
Linus Torvalds 2022-05-26 14:46:01 -07:00
commit f1f88bb51f
17 changed files with 914 additions and 58 deletions

View File

@ -0,0 +1,137 @@
What: /sys/bus/platform/devices/GGL0001:*/BINF.2
Date: May 2022
KernelVersion: 5.19
Description:
Returns active EC firmware of current boot (boolean).
== ===============================
0 Read only (recovery) firmware.
1 Rewritable firmware.
== ===============================
What: /sys/bus/platform/devices/GGL0001:*/BINF.3
Date: May 2022
KernelVersion: 5.19
Description:
Returns main firmware type for current boot (integer).
== =====================================
0 Recovery.
1 Normal.
2 Developer.
3 Netboot (factory installation only).
== =====================================
What: /sys/bus/platform/devices/GGL0001:*/CHSW
Date: May 2022
KernelVersion: 5.19
Description:
Returns switch position for Chrome OS specific hardware
switches when the firmware is booted (integer).
==== ===========================================
0 No changes.
2 Recovery button was pressed.
4 Recovery button was pressed (EC firmware).
32 Developer switch was enabled.
512 Firmware write protection was disabled.
==== ===========================================
What: /sys/bus/platform/devices/GGL0001:*/FMAP
Date: May 2022
KernelVersion: 5.19
Description:
Returns physical memory address of the start of the main
processor firmware flashmap.
What: /sys/bus/platform/devices/GGL0001:*/FRID
Date: May 2022
KernelVersion: 5.19
Description:
Returns firmware version for the read-only portion of the
main processor firmware.
What: /sys/bus/platform/devices/GGL0001:*/FWID
Date: May 2022
KernelVersion: 5.19
Description:
Returns firmware version for the rewritable portion of the
main processor firmware.
What: /sys/bus/platform/devices/GGL0001:*/GPIO.X/GPIO.0
Date: May 2022
KernelVersion: 5.19
Description:
Returns type of the GPIO signal for the Chrome OS specific
GPIO assignments (integer).
=========== ==================================
1 Recovery button.
2 Developer mode switch.
3 Firmware write protection switch.
256 to 511 Debug header GPIO 0 to GPIO 255.
=========== ==================================
What: /sys/bus/platform/devices/GGL0001:*/GPIO.X/GPIO.1
Date: May 2022
KernelVersion: 5.19
Description:
Returns signal attributes of the GPIO signal (integer bitfield).
== =======================
0 Signal is active low.
1 Signal is active high.
== =======================
What: /sys/bus/platform/devices/GGL0001:*/GPIO.X/GPIO.2
Date: May 2022
KernelVersion: 5.19
Description:
Returns the GPIO number on the specified GPIO
controller.
What: /sys/bus/platform/devices/GGL0001:*/GPIO.X/GPIO.3
Date: May 2022
KernelVersion: 5.19
Description:
Returns name of the GPIO controller.
What: /sys/bus/platform/devices/GGL0001:*/HWID
Date: May 2022
KernelVersion: 5.19
Description:
Returns hardware ID for the Chromebook.
What: /sys/bus/platform/devices/GGL0001:*/MECK
Date: May 2022
KernelVersion: 5.19
Description:
Returns the SHA-1 or SHA-256 hash that is read out of the
Management Engine extended registers during boot. The hash
is exported via 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 extended registers, this buffer size can be zero.
What: /sys/bus/platform/devices/GGL0001:*/VBNV.0
Date: May 2022
KernelVersion: 5.19
Description:
Returns 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/platform/devices/GGL0001:*/VBNV.1
Date: May 2022
KernelVersion: 5.19
Description:
Return the size in bytes of the verified boot non-volatile
storage block.
What: /sys/bus/platform/devices/GGL0001:*/VDAT
Date: May 2022
KernelVersion: 5.19
Description:
Returns the verified boot data block shared between the
firmware verification step and the kernel verification step
(binary).

View File

@ -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 a valid PNP ID of Google.
PNP ID can be used with the ACPI devices according 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 protection was disabled when x86 firmware booted. (required if
firmware write protection 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 protection 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 extended 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 extended 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"
}

View File

@ -29,3 +29,4 @@ ACPI Support
non-d0-probe
extcon-intel-int3496
intel-pmc-mux
chromeos-acpi-device

View File

@ -15,6 +15,17 @@ menuconfig CHROME_PLATFORMS
if CHROME_PLATFORMS
config CHROMEOS_ACPI
tristate "ChromeOS specific ACPI extensions"
depends on ACPI
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.
The module will be called chromeos_acpi.
config CHROMEOS_LAPTOP
tristate "Chrome OS Laptop"
depends on I2C && DMI && X86

View File

@ -4,6 +4,7 @@
CFLAGS_cros_ec_trace.o:= -I$(src)
CFLAGS_cros_ec_sensorhub_ring.o:= -I$(src)
obj-$(CONFIG_CHROMEOS_ACPI) += chromeos_acpi.o
obj-$(CONFIG_CHROMEOS_LAPTOP) += chromeos_laptop.o
obj-$(CONFIG_CHROMEOS_PRIVACY_SCREEN) += chromeos_privacy_screen.o
obj-$(CONFIG_CHROMEOS_PSTORE) += chromeos_pstore.o

View File

@ -0,0 +1,257 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* ChromeOS specific ACPI extensions
*
* Copyright 2022 Google LLC
*
* 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/platform_device.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/module.h>
#define ACPI_ATTR_NAME_LEN 4
#define DEV_ATTR(_var, _name) \
static struct device_attribute dev_attr_##_var = \
__ATTR(_name, 0444, chromeos_first_level_attr_show, NULL);
#define GPIO_ATTR_GROUP(_group, _name, _num) \
static umode_t attr_is_visible_gpio_##_num(struct kobject *kobj, \
struct attribute *attr, int n) \
{ \
if (_num < chromeos_acpi_gpio_groups) \
return attr->mode; \
return 0; \
} \
static ssize_t chromeos_attr_show_gpio_##_num(struct device *dev, \
struct device_attribute *attr, \
char *buf) \
{ \
char name[ACPI_ATTR_NAME_LEN + 1]; \
int ret, num; \
\
ret = parse_attr_name(attr->attr.name, name, &num); \
if (ret) \
return ret; \
return chromeos_acpi_evaluate_method(dev, _num, num, name, buf); \
} \
static struct device_attribute dev_attr_0_##_group = \
__ATTR(GPIO.0, 0444, chromeos_attr_show_gpio_##_num, NULL); \
static struct device_attribute dev_attr_1_##_group = \
__ATTR(GPIO.1, 0444, chromeos_attr_show_gpio_##_num, NULL); \
static struct device_attribute dev_attr_2_##_group = \
__ATTR(GPIO.2, 0444, chromeos_attr_show_gpio_##_num, NULL); \
static struct device_attribute dev_attr_3_##_group = \
__ATTR(GPIO.3, 0444, chromeos_attr_show_gpio_##_num, NULL); \
\
static struct attribute *attrs_##_group[] = { \
&dev_attr_0_##_group.attr, \
&dev_attr_1_##_group.attr, \
&dev_attr_2_##_group.attr, \
&dev_attr_3_##_group.attr, \
NULL \
}; \
static const struct attribute_group attr_group_##_group = { \
.name = _name, \
.is_visible = attr_is_visible_gpio_##_num, \
.attrs = attrs_##_group, \
};
static unsigned int chromeos_acpi_gpio_groups;
/* Parse the ACPI package and return the data related to that attribute */
static int chromeos_acpi_handle_package(struct device *dev, union acpi_object *obj,
int pkg_num, int sub_pkg_num, char *name, char *buf)
{
union acpi_object *element = obj->package.elements;
if (pkg_num >= obj->package.count)
return -EINVAL;
element += pkg_num;
if (element->type == ACPI_TYPE_PACKAGE) {
if (sub_pkg_num >= element->package.count)
return -EINVAL;
/* select sub element inside this package */
element = element->package.elements;
element += sub_pkg_num;
}
switch (element->type) {
case ACPI_TYPE_INTEGER:
return sysfs_emit(buf, "%d\n", (int)element->integer.value);
case ACPI_TYPE_STRING:
return sysfs_emit(buf, "%s\n", element->string.pointer);
case ACPI_TYPE_BUFFER:
return sysfs_emit(buf, "%s\n", element->buffer.pointer);
default:
dev_err(dev, "element type %d not supported\n", element->type);
return -EINVAL;
}
}
static int chromeos_acpi_evaluate_method(struct device *dev, int pkg_num, int sub_pkg_num,
char *name, char *buf)
{
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
acpi_status status;
int ret = -EINVAL;
status = acpi_evaluate_object(ACPI_HANDLE(dev), name, NULL, &output);
if (ACPI_FAILURE(status)) {
dev_err(dev, "failed to retrieve %s. %s\n", name, acpi_format_exception(status));
return ret;
}
if (((union acpi_object *)output.pointer)->type == ACPI_TYPE_PACKAGE)
ret = chromeos_acpi_handle_package(dev, output.pointer, pkg_num, sub_pkg_num,
name, buf);
kfree(output.pointer);
return ret;
}
static int parse_attr_name(const char *name, char *attr_name, int *attr_num)
{
int ret;
ret = strscpy(attr_name, name, ACPI_ATTR_NAME_LEN + 1);
if (ret == -E2BIG)
return kstrtoint(&name[ACPI_ATTR_NAME_LEN + 1], 0, attr_num);
return 0;
}
static ssize_t chromeos_first_level_attr_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
char attr_name[ACPI_ATTR_NAME_LEN + 1];
int ret, attr_num = 0;
ret = parse_attr_name(attr->attr.name, attr_name, &attr_num);
if (ret)
return ret;
return chromeos_acpi_evaluate_method(dev, attr_num, 0, attr_name, buf);
}
static unsigned int get_gpio_pkg_num(struct device *dev)
{
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
union acpi_object *obj;
acpi_status status;
unsigned int count = 0;
char *name = "GPIO";
status = acpi_evaluate_object(ACPI_HANDLE(dev), name, NULL, &output);
if (ACPI_FAILURE(status)) {
dev_err(dev, "failed to retrieve %s. %s\n", name, acpi_format_exception(status));
return count;
}
obj = output.pointer;
if (obj->type == ACPI_TYPE_PACKAGE)
count = obj->package.count;
kfree(output.pointer);
return count;
}
DEV_ATTR(binf2, BINF.2)
DEV_ATTR(binf3, BINF.3)
DEV_ATTR(chsw, CHSW)
DEV_ATTR(fmap, FMAP)
DEV_ATTR(frid, FRID)
DEV_ATTR(fwid, FWID)
DEV_ATTR(hwid, HWID)
DEV_ATTR(meck, MECK)
DEV_ATTR(vbnv0, VBNV.0)
DEV_ATTR(vbnv1, VBNV.1)
DEV_ATTR(vdat, VDAT)
static struct attribute *first_level_attrs[] = {
&dev_attr_binf2.attr,
&dev_attr_binf3.attr,
&dev_attr_chsw.attr,
&dev_attr_fmap.attr,
&dev_attr_frid.attr,
&dev_attr_fwid.attr,
&dev_attr_hwid.attr,
&dev_attr_meck.attr,
&dev_attr_vbnv0.attr,
&dev_attr_vbnv1.attr,
&dev_attr_vdat.attr,
NULL
};
static const struct attribute_group first_level_attr_group = {
.attrs = first_level_attrs,
};
/*
* Every platform can have a different number of GPIO attribute groups.
* Define upper limit groups. At run time, the platform decides to show
* the present number of groups only, others are hidden.
*/
GPIO_ATTR_GROUP(gpio0, "GPIO.0", 0)
GPIO_ATTR_GROUP(gpio1, "GPIO.1", 1)
GPIO_ATTR_GROUP(gpio2, "GPIO.2", 2)
GPIO_ATTR_GROUP(gpio3, "GPIO.3", 3)
GPIO_ATTR_GROUP(gpio4, "GPIO.4", 4)
GPIO_ATTR_GROUP(gpio5, "GPIO.5", 5)
GPIO_ATTR_GROUP(gpio6, "GPIO.6", 6)
GPIO_ATTR_GROUP(gpio7, "GPIO.7", 7)
static const struct attribute_group *chromeos_acpi_all_groups[] = {
&first_level_attr_group,
&attr_group_gpio0,
&attr_group_gpio1,
&attr_group_gpio2,
&attr_group_gpio3,
&attr_group_gpio4,
&attr_group_gpio5,
&attr_group_gpio6,
&attr_group_gpio7,
NULL
};
static int chromeos_acpi_device_probe(struct platform_device *pdev)
{
chromeos_acpi_gpio_groups = get_gpio_pkg_num(&pdev->dev);
/*
* If the platform has more GPIO attribute groups than the number of
* groups this driver supports, give out a warning message.
*/
if (chromeos_acpi_gpio_groups > ARRAY_SIZE(chromeos_acpi_all_groups) - 2)
dev_warn(&pdev->dev, "Only %zu GPIO attr groups supported by the driver out of total %u.\n",
ARRAY_SIZE(chromeos_acpi_all_groups) - 2, chromeos_acpi_gpio_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 platform_driver chromeos_acpi_device_driver = {
.probe = chromeos_acpi_device_probe,
.driver = {
.name = KBUILD_MODNAME,
.dev_groups = chromeos_acpi_all_groups,
.acpi_match_table = chromeos_device_ids,
}
};
module_platform_driver(chromeos_acpi_device_driver);
MODULE_AUTHOR("Muhammad Usama Anjum <usama.anjum@collabora.com>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("ChromeOS specific ACPI extensions");

View File

@ -9,12 +9,12 @@
* battery charging and regulator control, firmware update.
*/
#include <linux/of_platform.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/platform_data/cros_ec_commands.h>
#include <linux/platform_data/cros_ec_proto.h>
#include <linux/slab.h>
#include <linux/suspend.h>
#include "cros_ec.h"
@ -189,6 +189,8 @@ int cros_ec_register(struct cros_ec_device *ec_dev)
ec_dev->max_request = sizeof(struct ec_params_hello);
ec_dev->max_response = sizeof(struct ec_response_get_protocol_info);
ec_dev->max_passthru = 0;
ec_dev->ec = NULL;
ec_dev->pd = NULL;
ec_dev->din = devm_kzalloc(dev, ec_dev->din_size, GFP_KERNEL);
if (!ec_dev->din)
@ -213,7 +215,7 @@ int cros_ec_register(struct cros_ec_device *ec_dev)
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
"chromeos-ec", ec_dev);
if (err) {
dev_err(dev, "Failed to request IRQ %d: %d",
dev_err(dev, "Failed to request IRQ %d: %d\n",
ec_dev->irq, err);
return err;
}
@ -245,18 +247,16 @@ int cros_ec_register(struct cros_ec_device *ec_dev)
if (IS_ERR(ec_dev->pd)) {
dev_err(ec_dev->dev,
"Failed to create CrOS PD platform device\n");
platform_device_unregister(ec_dev->ec);
return PTR_ERR(ec_dev->pd);
err = PTR_ERR(ec_dev->pd);
goto exit;
}
}
if (IS_ENABLED(CONFIG_OF) && dev->of_node) {
err = devm_of_platform_populate(dev);
if (err) {
platform_device_unregister(ec_dev->pd);
platform_device_unregister(ec_dev->ec);
dev_err(dev, "Failed to register sub-devices\n");
return err;
goto exit;
}
}
@ -266,7 +266,7 @@ int cros_ec_register(struct cros_ec_device *ec_dev)
*/
err = cros_ec_sleep_event(ec_dev, 0);
if (err < 0)
dev_dbg(ec_dev->dev, "Error %d clearing sleep event to ec",
dev_dbg(ec_dev->dev, "Error %d clearing sleep event to ec\n",
err);
if (ec_dev->mkbp_event_supported) {
@ -278,7 +278,7 @@ int cros_ec_register(struct cros_ec_device *ec_dev)
err = blocking_notifier_chain_register(&ec_dev->event_notifier,
&ec_dev->notifier_ready);
if (err)
return err;
goto exit;
}
dev_info(dev, "Chrome EC device registered\n");
@ -291,6 +291,10 @@ int cros_ec_register(struct cros_ec_device *ec_dev)
cros_ec_irq_thread(0, ec_dev);
return 0;
exit:
platform_device_unregister(ec_dev->ec);
platform_device_unregister(ec_dev->pd);
return err;
}
EXPORT_SYMBOL(cros_ec_register);
@ -331,14 +335,15 @@ int cros_ec_suspend(struct cros_ec_device *ec_dev)
ret = cros_ec_sleep_event(ec_dev, sleep_event);
if (ret < 0)
dev_dbg(ec_dev->dev, "Error %d sending suspend event to ec",
dev_dbg(ec_dev->dev, "Error %d sending suspend event to ec\n",
ret);
if (device_may_wakeup(dev))
ec_dev->wake_enabled = !enable_irq_wake(ec_dev->irq);
else
ec_dev->wake_enabled = false;
disable_irq(ec_dev->irq);
ec_dev->was_wake_device = ec_dev->wake_enabled;
ec_dev->suspended = true;
return 0;
@ -375,13 +380,12 @@ int cros_ec_resume(struct cros_ec_device *ec_dev)
ret = cros_ec_sleep_event(ec_dev, sleep_event);
if (ret < 0)
dev_dbg(ec_dev->dev, "Error %d sending resume event to ec",
dev_dbg(ec_dev->dev, "Error %d sending resume event to ec\n",
ret);
if (ec_dev->wake_enabled) {
if (ec_dev->wake_enabled)
disable_irq_wake(ec_dev->irq);
ec_dev->wake_enabled = 0;
}
/*
* Let the mfd devices know about events that occur during
* suspend. This way the clients know what to do with them.

View File

@ -301,7 +301,7 @@ static long cros_ec_chardev_ioctl_xcmd(struct cros_ec_dev *ec, void __user *arg)
}
s_cmd->command += ec->cmd_offset;
ret = cros_ec_cmd_xfer_status(ec->ec_dev, s_cmd);
ret = cros_ec_cmd_xfer(ec->ec_dev, s_cmd);
/* Only copy data to userland if data was received. */
if (ret < 0)
goto exit;

View File

@ -72,13 +72,19 @@ static int cros_ec_pkt_xfer_i2c(struct cros_ec_device *ec_dev,
i2c_msg[1].flags = I2C_M_RD;
packet_len = msg->insize + response_header_size;
BUG_ON(packet_len > ec_dev->din_size);
if (packet_len > ec_dev->din_size) {
ret = -EINVAL;
goto done;
}
in_buf = ec_dev->din;
i2c_msg[1].len = packet_len;
i2c_msg[1].buf = (char *) in_buf;
packet_len = msg->outsize + request_header_size;
BUG_ON(packet_len > ec_dev->dout_size);
if (packet_len > ec_dev->dout_size) {
ret = -EINVAL;
goto done;
}
out_buf = ec_dev->dout;
i2c_msg[0].len = packet_len;
i2c_msg[0].buf = (char *) out_buf;
@ -89,6 +95,8 @@ static int cros_ec_pkt_xfer_i2c(struct cros_ec_device *ec_dev,
ec_dev->dout++;
ret = cros_ec_prepare_tx(ec_dev, msg);
if (ret < 0)
goto done;
ec_dev->dout--;
/* send command to EC and read answer */

View File

@ -521,7 +521,9 @@ static int cros_ec_pkt_xfer_ish(struct cros_ec_device *ec_dev,
out_msg->hdr.status = 0;
ec_dev->dout += OUT_MSG_EC_REQUEST_PREAMBLE;
cros_ec_prepare_tx(ec_dev, msg);
rv = cros_ec_prepare_tx(ec_dev, msg);
if (rv < 0)
goto end_error;
ec_dev->dout -= OUT_MSG_EC_REQUEST_PREAMBLE;
dev_dbg(dev,

View File

@ -147,6 +147,8 @@ static int cros_ec_pkt_xfer_lpc(struct cros_ec_device *ec,
u8 *dout;
ret = cros_ec_prepare_tx(ec, msg);
if (ret < 0)
goto done;
/* Write buffer */
cros_ec_lpc_ops.write(EC_LPC_ADDR_HOST_PACKET, ret, ec->dout);
@ -341,9 +343,14 @@ static int cros_ec_lpc_probe(struct platform_device *pdev)
u8 buf[2];
int irq, ret;
if (!devm_request_region(dev, EC_LPC_ADDR_MEMMAP, EC_MEMMAP_SIZE,
dev_name(dev))) {
dev_err(dev, "couldn't reserve memmap region\n");
/*
* The Framework Laptop (and possibly other non-ChromeOS devices)
* only exposes the eight I/O ports that are required for the Microchip EC.
* Requesting a larger reservation will fail.
*/
if (!devm_request_region(dev, EC_HOST_CMD_REGION0,
EC_HOST_CMD_MEC_REGION_SIZE, dev_name(dev))) {
dev_err(dev, "couldn't reserve MEC region\n");
return -EBUSY;
}
@ -357,6 +364,12 @@ static int cros_ec_lpc_probe(struct platform_device *pdev)
cros_ec_lpc_ops.write = cros_ec_lpc_mec_write_bytes;
cros_ec_lpc_ops.read(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID, 2, buf);
if (buf[0] != 'E' || buf[1] != 'C') {
if (!devm_request_region(dev, EC_LPC_ADDR_MEMMAP, EC_MEMMAP_SIZE,
dev_name(dev))) {
dev_err(dev, "couldn't reserve memmap region\n");
return -EBUSY;
}
/* Re-assign read/write operations for the non MEC variant */
cros_ec_lpc_ops.read = cros_ec_lpc_read_bytes;
cros_ec_lpc_ops.write = cros_ec_lpc_write_bytes;
@ -366,17 +379,19 @@ static int cros_ec_lpc_probe(struct platform_device *pdev)
dev_err(dev, "EC ID not detected\n");
return -ENODEV;
}
}
if (!devm_request_region(dev, EC_HOST_CMD_REGION0,
EC_HOST_CMD_REGION_SIZE, dev_name(dev))) {
dev_err(dev, "couldn't reserve region0\n");
return -EBUSY;
}
if (!devm_request_region(dev, EC_HOST_CMD_REGION1,
EC_HOST_CMD_REGION_SIZE, dev_name(dev))) {
dev_err(dev, "couldn't reserve region1\n");
return -EBUSY;
/* Reserve the remaining I/O ports required by the non-MEC protocol. */
if (!devm_request_region(dev, EC_HOST_CMD_REGION0 + EC_HOST_CMD_MEC_REGION_SIZE,
EC_HOST_CMD_REGION_SIZE - EC_HOST_CMD_MEC_REGION_SIZE,
dev_name(dev))) {
dev_err(dev, "couldn't reserve remainder of region0\n");
return -EBUSY;
}
if (!devm_request_region(dev, EC_HOST_CMD_REGION1,
EC_HOST_CMD_REGION_SIZE, dev_name(dev))) {
dev_err(dev, "couldn't reserve region1\n");
return -EBUSY;
}
}
ec_dev = devm_kzalloc(dev, sizeof(*ec_dev), GFP_KERNEL);
@ -502,6 +517,14 @@ static const struct dmi_system_id cros_ec_lpc_dmi_table[] __initconst = {
DMI_MATCH(DMI_PRODUCT_NAME, "Glimmer"),
},
},
/* A small number of non-Chromebook/box machines also use the ChromeOS EC */
{
/* the Framework Laptop */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Framework"),
DMI_MATCH(DMI_PRODUCT_NAME, "Laptop"),
},
},
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(dmi, cros_ec_lpc_dmi_table);

View File

@ -60,8 +60,8 @@ static int prepare_packet(struct cros_ec_device *ec_dev,
int i;
u8 csum = 0;
BUG_ON(ec_dev->proto_version != EC_HOST_REQUEST_VERSION);
BUG_ON(msg->outsize + sizeof(*request) > ec_dev->dout_size);
if (msg->outsize + sizeof(*request) > ec_dev->dout_size)
return -EINVAL;
out = ec_dev->dout;
request = (struct ec_host_request *)out;
@ -165,7 +165,7 @@ static int send_command(struct cros_ec_device *ec_dev,
* only SPI uses it. Once LPC uses the same protocol it can start using it.
* I2C could use it now, with a refactor of the existing code.
*
* Return: 0 on success or negative error code.
* Return: number of prepared bytes on success or negative error code.
*/
int cros_ec_prepare_tx(struct cros_ec_device *ec_dev,
struct cros_ec_command *msg)
@ -177,7 +177,9 @@ int cros_ec_prepare_tx(struct cros_ec_device *ec_dev,
if (ec_dev->proto_version > 2)
return prepare_packet(ec_dev, msg);
BUG_ON(msg->outsize > EC_PROTO2_MAX_PARAM_SIZE);
if (msg->outsize > EC_PROTO2_MAX_PARAM_SIZE)
return -EINVAL;
out = ec_dev->dout;
out[0] = EC_CMD_VERSION0 + msg->version;
out[1] = msg->command;
@ -560,22 +562,28 @@ exit:
EXPORT_SYMBOL(cros_ec_query_all);
/**
* cros_ec_cmd_xfer_status() - Send a command to the ChromeOS EC.
* cros_ec_cmd_xfer() - Send a command to the ChromeOS EC.
* @ec_dev: EC device.
* @msg: Message to write.
*
* Call this to send a command to the ChromeOS EC. This should be used instead of calling the EC's
* cmd_xfer() callback directly. It returns success status only if both the command was transmitted
* successfully and the EC replied with success status.
* Call this to send a command to the ChromeOS EC. This should be used instead
* of calling the EC's cmd_xfer() callback directly. This function does not
* convert EC command execution error codes to Linux error codes. Most
* in-kernel users will want to use cros_ec_cmd_xfer_status() instead since
* that function implements the conversion.
*
* Return:
* >=0 - The number of bytes transferred
* <0 - Linux error code
* >0 - EC command was executed successfully. The return value is the number
* of bytes returned by the EC (excluding the header).
* =0 - EC communication was successful. EC command execution results are
* reported in msg->result. The result will be EC_RES_SUCCESS if the
* command was executed successfully or report an EC command execution
* error.
* <0 - EC communication error. Return value is the Linux error code.
*/
int cros_ec_cmd_xfer_status(struct cros_ec_device *ec_dev,
struct cros_ec_command *msg)
int cros_ec_cmd_xfer(struct cros_ec_device *ec_dev, struct cros_ec_command *msg)
{
int ret, mapped;
int ret;
mutex_lock(&ec_dev->lock);
if (ec_dev->proto_version == EC_PROTO_VERSION_UNKNOWN) {
@ -616,6 +624,32 @@ int cros_ec_cmd_xfer_status(struct cros_ec_device *ec_dev,
ret = send_command(ec_dev, msg);
mutex_unlock(&ec_dev->lock);
return ret;
}
EXPORT_SYMBOL(cros_ec_cmd_xfer);
/**
* cros_ec_cmd_xfer_status() - Send a command to the ChromeOS EC.
* @ec_dev: EC device.
* @msg: Message to write.
*
* Call this to send a command to the ChromeOS EC. This should be used instead of calling the EC's
* cmd_xfer() callback directly. It returns success status only if both the command was transmitted
* successfully and the EC replied with success status.
*
* Return:
* >=0 - The number of bytes transferred.
* <0 - Linux error code
*/
int cros_ec_cmd_xfer_status(struct cros_ec_device *ec_dev,
struct cros_ec_command *msg)
{
int ret, mapped;
ret = cros_ec_cmd_xfer(ec_dev, msg);
if (ret < 0)
return ret;
mapped = cros_ec_map_error(msg->result);
if (mapped) {
dev_dbg(ec_dev->dev, "Command result (err: %d [%d])\n",
@ -783,7 +817,8 @@ u32 cros_ec_get_host_event(struct cros_ec_device *ec_dev)
{
u32 host_event;
BUG_ON(!ec_dev->mkbp_event_supported);
if (!ec_dev->mkbp_event_supported)
return 0;
if (ec_dev->event_data.event_type != EC_MKBP_EVENT_HOST_EVENT)
return 0;

View File

@ -89,6 +89,8 @@ static int cros_ec_pkt_xfer_rpmsg(struct cros_ec_device *ec_dev,
ec_msg->result = 0;
len = cros_ec_prepare_tx(ec_dev, ec_msg);
if (len < 0)
return len;
dev_dbg(ec_dev->dev, "prepared, len=%d\n", len);
reinit_completion(&ec_rpmsg->xfer_ack);

View File

@ -160,7 +160,8 @@ static int receive_n_bytes(struct cros_ec_device *ec_dev, u8 *buf, int n)
struct spi_message msg;
int ret;
BUG_ON(buf - ec_dev->din + n > ec_dev->din_size);
if (buf - ec_dev->din + n > ec_dev->din_size)
return -EINVAL;
memset(&trans, 0, sizeof(trans));
trans.cs_change = 1;
@ -197,7 +198,8 @@ static int cros_ec_spi_receive_packet(struct cros_ec_device *ec_dev,
unsigned long deadline;
int todo;
BUG_ON(ec_dev->din_size < EC_MSG_PREAMBLE_COUNT);
if (ec_dev->din_size < EC_MSG_PREAMBLE_COUNT)
return -EINVAL;
/* Receive data until we see the header byte */
deadline = jiffies + msecs_to_jiffies(EC_MSG_DEADLINE_MS);
@ -237,7 +239,6 @@ static int cros_ec_spi_receive_packet(struct cros_ec_device *ec_dev,
* start of our buffer
*/
todo = end - ++ptr;
BUG_ON(todo < 0 || todo > ec_dev->din_size);
todo = min(todo, need_len);
memmove(ec_dev->din, ptr, todo);
ptr = ec_dev->din + todo;
@ -305,7 +306,8 @@ static int cros_ec_spi_receive_response(struct cros_ec_device *ec_dev,
unsigned long deadline;
int todo;
BUG_ON(ec_dev->din_size < EC_MSG_PREAMBLE_COUNT);
if (ec_dev->din_size < EC_MSG_PREAMBLE_COUNT)
return -EINVAL;
/* Receive data until we see the header byte */
deadline = jiffies + msecs_to_jiffies(EC_MSG_DEADLINE_MS);
@ -345,7 +347,6 @@ static int cros_ec_spi_receive_response(struct cros_ec_device *ec_dev,
* start of our buffer
*/
todo = end - ++ptr;
BUG_ON(todo < 0 || todo > ec_dev->din_size);
todo = min(todo, need_len);
memmove(ec_dev->din, ptr, todo);
ptr = ec_dev->din + todo;
@ -401,6 +402,8 @@ static int do_cros_ec_pkt_xfer_spi(struct cros_ec_device *ec_dev,
unsigned long delay;
len = cros_ec_prepare_tx(ec_dev, ec_msg);
if (len < 0)
return len;
dev_dbg(ec_dev->dev, "prepared, len=%d\n", len);
/* If it's too soon to do another transaction, wait */
@ -544,6 +547,8 @@ static int do_cros_ec_cmd_xfer_spi(struct cros_ec_device *ec_dev,
unsigned long delay;
len = cros_ec_prepare_tx(ec_dev, ec_msg);
if (len < 0)
return len;
dev_dbg(ec_dev->dev, "prepared, len=%d\n", len);
/* If it's too soon to do another transaction, wait */

View File

@ -1084,6 +1084,9 @@ static int cros_typec_probe(struct platform_device *pdev)
}
ec_dev = dev_get_drvdata(&typec->ec->ec->dev);
if (!ec_dev)
return -EPROBE_DEFER;
typec->typec_cmd_supported = cros_ec_check_features(ec_dev, EC_FEATURE_TYPEC_CMD);
typec->needs_mux_ack = cros_ec_check_features(ec_dev, EC_FEATURE_TYPEC_MUX_REQUIRE_AP_ACK);

View File

@ -51,10 +51,14 @@
/*
* The actual block is 0x800-0x8ff, but some BIOSes think it's 0x880-0x8ff
* and they tell the kernel that so we have to think of it as two parts.
*
* Other BIOSes report only the I/O port region spanned by the Microchip
* MEC series EC; an attempt to address a larger region may fail.
*/
#define EC_HOST_CMD_REGION0 0x800
#define EC_HOST_CMD_REGION1 0x880
#define EC_HOST_CMD_REGION_SIZE 0x80
#define EC_HOST_CMD_REGION0 0x800
#define EC_HOST_CMD_REGION1 0x880
#define EC_HOST_CMD_REGION_SIZE 0x80
#define EC_HOST_CMD_MEC_REGION_SIZE 0x8
/* EC command register bit functions */
#define EC_LPC_CMDR_DATA BIT(0) /* Data ready for host to read */

View File

@ -76,8 +76,6 @@ struct cros_ec_command {
* struct cros_ec_device - Information about a ChromeOS EC device.
* @phys_name: Name of physical comms layer (e.g. 'i2c-4').
* @dev: Device pointer for physical comms device
* @was_wake_device: True if this device was set to wake the system from
* sleep at the last suspend.
* @cros_class: The class structure for this device.
* @cmd_readmem: Direct read of the EC memory-mapped region, if supported.
* @offset: Is within EC_LPC_ADDR_MEMMAP region.
@ -137,7 +135,6 @@ struct cros_ec_device {
/* These are used by other drivers that want to talk to the EC */
const char *phys_name;
struct device *dev;
bool was_wake_device;
struct class *cros_class;
int (*cmd_readmem)(struct cros_ec_device *ec, unsigned int offset,
unsigned int bytes, void *dest);
@ -216,6 +213,9 @@ int cros_ec_prepare_tx(struct cros_ec_device *ec_dev,
int cros_ec_check_result(struct cros_ec_device *ec_dev,
struct cros_ec_command *msg);
int cros_ec_cmd_xfer(struct cros_ec_device *ec_dev,
struct cros_ec_command *msg);
int cros_ec_cmd_xfer_status(struct cros_ec_device *ec_dev,
struct cros_ec_command *msg);