diff options
33 files changed, 5638 insertions, 990 deletions
diff --git a/Documentation/ABI/testing/debugfs-cros-ec b/Documentation/ABI/testing/debugfs-cros-ec new file mode 100644 index 000000000000..1fe0add99a2a --- /dev/null +++ b/Documentation/ABI/testing/debugfs-cros-ec @@ -0,0 +1,56 @@ +What: /sys/kernel/debug/<cros-ec-device>/console_log +Date: September 2017 +KernelVersion: 4.13 +Description: + If the EC supports the CONSOLE_READ command type, this file + can be used to grab the EC logs. The kernel polls for the log + and keeps its own buffer but userspace should grab this and + write it out to some logs. + +What: /sys/kernel/debug/<cros-ec-device>/panicinfo +Date: September 2017 +KernelVersion: 4.13 +Description: + This file dumps the EC panic information from the previous + reboot. This file will only exist if the PANIC_INFO command + type is supported by the EC. + +What: /sys/kernel/debug/<cros-ec-device>/pdinfo +Date: June 2018 +KernelVersion: 4.17 +Description: + This file provides the port role, muxes and power debug + information for all the USB PD/type-C ports available. If + the are no ports available, this file will be just an empty + file. + +What: /sys/kernel/debug/<cros-ec-device>/uptime +Date: June 2019 +KernelVersion: 5.3 +Description: + A u32 providing the time since EC booted in ms. This is + is used for synchronizing the AP host time with the EC + log. An error is returned if the command is not supported + by the EC or there is a communication problem. + +What: /sys/kernel/debug/<cros-ec-device>/last_resume_result +Date: June 2019 +KernelVersion: 5.3 +Description: + Some ECs have a feature where they will track transitions to + the (Intel) processor's SLP_S0 line, in order to detect cases + where a system failed to go into S0ix. When the system resumes, + an EC with this feature will return a summary of SLP_S0 + transitions that occurred. The last_resume_result file returns + the most recent response from the AP's resume message to the EC. + + The bottom 31 bits contain a count of the number of SLP_S0 + transitions that occurred since the suspend message was + received. Bit 31 is set if the EC attempted to wake the + system due to a timeout when watching for SLP_S0 transitions. + Callers can use this to detect a wake from the EC due to + S0ix timeouts. The result will be zero if no suspend + transitions have been attempted, or the EC does not support + this feature. + + Output will be in the format: "0x%08x\n". diff --git a/Documentation/ABI/testing/debugfs-wilco-ec b/Documentation/ABI/testing/debugfs-wilco-ec index 73a5a66ddca6..9d8d9d2def5b 100644 --- a/Documentation/ABI/testing/debugfs-wilco-ec +++ b/Documentation/ABI/testing/debugfs-wilco-ec @@ -23,11 +23,9 @@ Description: For writing, bytes 0-1 indicate the message type, one of enum wilco_ec_msg_type. Byte 2+ consist of the data passed in the - request, starting at MBOX[0] - - At least three bytes are required for writing, two for the type - and at least a single byte of data. Only the first - EC_MAILBOX_DATA_SIZE bytes of MBOX will be used. + request, starting at MBOX[0]. At least three bytes are required + for writing, two for the type and at least a single byte of + data. Example: // Request EC info type 3 (EC firmware build date) @@ -40,7 +38,7 @@ Description: $ cat /sys/kernel/debug/wilco_ec/raw 00 00 31 32 2f 32 31 2f 31 38 00 38 00 01 00 2f 00 ..12/21/18.8... - Note that the first 32 bytes of the received MBOX[] will be - printed, even if some of the data is junk. It is up to you to - know how many of the first bytes of data are the actual - response. + Note that the first 16 bytes of the received MBOX[] will be + printed, even if some of the data is junk, and skipping bytes + 17 to 32. It is up to you to know how many of the first bytes of + data are the actual response. diff --git a/Documentation/ABI/testing/sysfs-platform-wilco-ec b/Documentation/ABI/testing/sysfs-platform-wilco-ec new file mode 100644 index 000000000000..8827a734f933 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-platform-wilco-ec @@ -0,0 +1,40 @@ +What: /sys/bus/platform/devices/GOOG000C\:00/boot_on_ac +Date: April 2019 +KernelVersion: 5.3 +Description: + Boot on AC is a policy which makes the device boot from S5 + when AC power is connected. This is useful for users who + want to run their device headless or with a dock. + + Input should be parseable by kstrtou8() to 0 or 1. + +What: /sys/bus/platform/devices/GOOG000C\:00/build_date +Date: May 2019 +KernelVersion: 5.3 +Description: + Display Wilco Embedded Controller firmware build date. + Output will a MM/DD/YY string. + +What: /sys/bus/platform/devices/GOOG000C\:00/build_revision +Date: May 2019 +KernelVersion: 5.3 +Description: + Display Wilco Embedded Controller build revision. + Output will a version string be similar to the example below: + d2592cae0 + +What: /sys/bus/platform/devices/GOOG000C\:00/model_number +Date: May 2019 +KernelVersion: 5.3 +Description: + Display Wilco Embedded Controller model number. + Output will a version string be similar to the example below: + 08B6 + +What: /sys/bus/platform/devices/GOOG000C\:00/version +Date: May 2019 +KernelVersion: 5.3 +Description: + Display Wilco Embedded Controller firmware version. + The format of the string is x.y.z. Where x is major, y is minor + and z is the build number. For example: 95.00.06 diff --git a/drivers/iio/common/cros_ec_sensors/Kconfig b/drivers/iio/common/cros_ec_sensors/Kconfig index f9bf7ff7fcaf..bcb58fb76b9f 100644 --- a/drivers/iio/common/cros_ec_sensors/Kconfig +++ b/drivers/iio/common/cros_ec_sensors/Kconfig @@ -21,3 +21,12 @@ config IIO_CROS_EC_SENSORS Accelerometers, Gyroscope and Magnetometer that are presented by the ChromeOS EC Sensor hub. Creates an IIO device for each functions. + +config IIO_CROS_EC_SENSORS_LID_ANGLE + tristate "ChromeOS EC Sensor for lid angle" + depends on IIO_CROS_EC_SENSORS_CORE + help + Module to report the angle between lid and base for some + convertible devices. + This module is loaded when the EC can calculate the angle between the base + and the lid. diff --git a/drivers/iio/common/cros_ec_sensors/Makefile b/drivers/iio/common/cros_ec_sensors/Makefile index 7c2d6a966fe6..e0a33ab66d21 100644 --- a/drivers/iio/common/cros_ec_sensors/Makefile +++ b/drivers/iio/common/cros_ec_sensors/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_IIO_CROS_EC_SENSORS_CORE) += cros_ec_sensors_core.o obj-$(CONFIG_IIO_CROS_EC_SENSORS) += cros_ec_sensors.o +obj-$(CONFIG_IIO_CROS_EC_SENSORS_LID_ANGLE) += cros_ec_lid_angle.o diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_lid_angle.c b/drivers/iio/common/cros_ec_sensors/cros_ec_lid_angle.c new file mode 100644 index 000000000000..876dfd176b0e --- /dev/null +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_lid_angle.c @@ -0,0 +1,139 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * cros_ec_lid_angle - Driver for CrOS EC lid angle sensor. + * + * Copyright 2018 Google, Inc + * + * This driver uses the cros-ec interface to communicate with the Chrome OS + * EC about counter sensors. Counters are presented through + * iio sysfs. + */ + +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/iio/buffer.h> +#include <linux/iio/common/cros_ec_sensors_core.h> +#include <linux/iio/iio.h> +#include <linux/iio/kfifo_buf.h> +#include <linux/iio/trigger.h> +#include <linux/iio/triggered_buffer.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/kernel.h> +#include <linux/mfd/cros_ec.h> +#include <linux/mfd/cros_ec_commands.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#define DRV_NAME "cros-ec-lid-angle" + +/* + * One channel for the lid angle, the other for timestamp. + */ +static const struct iio_chan_spec cros_ec_lid_angle_channels[] = { + { + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .scan_type.realbits = CROS_EC_SENSOR_BITS, + .scan_type.storagebits = CROS_EC_SENSOR_BITS, + .scan_type.sign = 'u', + .type = IIO_ANGL + }, + IIO_CHAN_SOFT_TIMESTAMP(1) +}; + +/* State data for ec_sensors iio driver. */ +struct cros_ec_lid_angle_state { + /* Shared by all sensors */ + struct cros_ec_sensors_core_state core; +}; + +static int cros_ec_sensors_read_lid_angle(struct iio_dev *indio_dev, + unsigned long scan_mask, s16 *data) +{ + struct cros_ec_sensors_core_state *st = iio_priv(indio_dev); + int ret; + + st->param.cmd = MOTIONSENSE_CMD_LID_ANGLE; + ret = cros_ec_motion_send_host_cmd(st, sizeof(st->resp->lid_angle)); + if (ret) { + dev_warn(&indio_dev->dev, "Unable to read lid angle\n"); + return ret; + } + + *data = st->resp->lid_angle.value; + return 0; +} + +static int cros_ec_lid_angle_read(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct cros_ec_lid_angle_state *st = iio_priv(indio_dev); + s16 data; + int ret; + + mutex_lock(&st->core.cmd_lock); + ret = cros_ec_sensors_read_lid_angle(indio_dev, 1, &data); + if (ret == 0) { + *val = data; + ret = IIO_VAL_INT; + } + mutex_unlock(&st->core.cmd_lock); + return ret; +} + +static const struct iio_info cros_ec_lid_angle_info = { + .read_raw = &cros_ec_lid_angle_read, +}; + +static int cros_ec_lid_angle_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct iio_dev *indio_dev; + struct cros_ec_lid_angle_state *state; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*state)); + if (!indio_dev) + return -ENOMEM; + + ret = cros_ec_sensors_core_init(pdev, indio_dev, false); + if (ret) + return ret; + + indio_dev->info = &cros_ec_lid_angle_info; + state = iio_priv(indio_dev); + indio_dev->channels = cros_ec_lid_angle_channels; + indio_dev->num_channels = ARRAY_SIZE(cros_ec_lid_angle_channels); + + state->core.read_ec_sensors_data = cros_ec_sensors_read_lid_angle; + + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL, + cros_ec_sensors_capture, NULL); + if (ret) + return ret; + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct platform_device_id cros_ec_lid_angle_ids[] = { + { + .name = DRV_NAME, + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(platform, cros_ec_lid_angle_ids); + +static struct platform_driver cros_ec_lid_angle_platform_driver = { + .driver = { + .name = DRV_NAME, + .pm = &cros_ec_sensors_pm_ops, + }, + .probe = cros_ec_lid_angle_probe, + .id_table = cros_ec_lid_angle_ids, +}; +module_platform_driver(cros_ec_lid_angle_platform_driver); + +MODULE_DESCRIPTION("ChromeOS EC driver for reporting convertible lid angle."); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/keyboard/cros_ec_keyb.c b/drivers/input/keyboard/cros_ec_keyb.c index d56001181598..38cb6d82d8fe 100644 --- a/drivers/input/keyboard/cros_ec_keyb.c +++ b/drivers/input/keyboard/cros_ec_keyb.c @@ -237,7 +237,7 @@ static int cros_ec_keyb_work(struct notifier_block *nb, if (queued_during_suspend && !device_may_wakeup(ckdev->dev)) return NOTIFY_OK; - switch (ckdev->ec->event_data.event_type) { + switch (ckdev->ec->event_data.event_type & EC_MKBP_EVENT_TYPE_MASK) { case EC_MKBP_EVENT_KEY_MATRIX: pm_wakeup_event(ckdev->dev, 0); diff --git a/drivers/mfd/cros_ec.c b/drivers/mfd/cros_ec.c index 5d5c41ac3845..2a9ac5213893 100644 --- a/drivers/mfd/cros_ec.c +++ b/drivers/mfd/cros_ec.c @@ -102,12 +102,16 @@ static int cros_ec_sleep_event(struct cros_ec_device *ec_dev, u8 sleep_event) /* For now, report failure to transition to S0ix with a warning. */ if (ret >= 0 && ec_dev->host_sleep_v1 && - (sleep_event == HOST_SLEEP_EVENT_S0IX_RESUME)) + (sleep_event == HOST_SLEEP_EVENT_S0IX_RESUME)) { + ec_dev->last_resume_result = + buf.u.resp1.resume_response.sleep_transitions; + WARN_ONCE(buf.u.resp1.resume_response.sleep_transitions & EC_HOST_RESUME_SLEEP_TIMEOUT, "EC detected sleep transition timeout. Total slp_s0 transitions: %d", buf.u.resp1.resume_response.sleep_transitions & EC_HOST_RESUME_SLEEP_TRANSITIONS_MASK); + } return ret; } diff --git a/drivers/platform/chrome/Kconfig b/drivers/platform/chrome/Kconfig index 2826f7136f65..970679d0b6f6 100644 --- a/drivers/platform/chrome/Kconfig +++ b/drivers/platform/chrome/Kconfig @@ -72,6 +72,19 @@ config CROS_EC_RPMSG To compile this driver as a module, choose M here: the module will be called cros_ec_rpmsg. +config CROS_EC_ISHTP + tristate "ChromeOS Embedded Controller (ISHTP)" + depends on MFD_CROS_EC + depends on INTEL_ISH_HID + help + If you say Y here, you get support for talking to the ChromeOS EC + firmware running on Intel Integrated Sensor Hub (ISH), using the + ISH Transport protocol (ISH-TP). This uses a simple byte-level + protocol with a checksum. + + To compile this driver as a module, choose M here: the + module will be called cros_ec_ishtp. + config CROS_EC_SPI tristate "ChromeOS Embedded Controller (SPI)" depends on MFD_CROS_EC && SPI @@ -83,28 +96,17 @@ config CROS_EC_SPI 'pre-amble' bytes before the response actually starts. config CROS_EC_LPC - tristate "ChromeOS Embedded Controller (LPC)" - depends on MFD_CROS_EC && ACPI && (X86 || COMPILE_TEST) - help - If you say Y here, you get support for talking to the ChromeOS EC - over an LPC bus. This uses a simple byte-level protocol with a - checksum. This is used for userspace access only. The kernel - typically has its own communication methods. - - To compile this driver as a module, choose M here: the - module will be called cros_ec_lpc. - -config CROS_EC_LPC_MEC - bool "ChromeOS Embedded Controller LPC Microchip EC (MEC) variant" - depends on CROS_EC_LPC - default n + tristate "ChromeOS Embedded Controller (LPC)" + depends on MFD_CROS_EC && ACPI && (X86 || COMPILE_TEST) help - If you say Y here, a variant LPC protocol for the Microchip EC - will be used. Note that this variant is not backward compatible - with non-Microchip ECs. + If you say Y here, you get support for talking to the ChromeOS EC + over an LPC bus, including the LPC Microchip EC (MEC) variant. + This uses a simple byte-level protocol with a checksum. This is + used for userspace access only. The kernel typically has its own + communication methods. - If you have a ChromeOS Embedded Controller Microchip EC variant - choose Y here. + To compile this driver as a module, choose M here: the + module will be called cros_ec_lpcs. config CROS_EC_PROTO bool diff --git a/drivers/platform/chrome/Makefile b/drivers/platform/chrome/Makefile index 1b2f1dcfcd5c..fd0af05cc14c 100644 --- a/drivers/platform/chrome/Makefile +++ b/drivers/platform/chrome/Makefile @@ -7,10 +7,10 @@ obj-$(CONFIG_CHROMEOS_LAPTOP) += chromeos_laptop.o obj-$(CONFIG_CHROMEOS_PSTORE) += chromeos_pstore.o obj-$(CONFIG_CHROMEOS_TBMC) += chromeos_tbmc.o obj-$(CONFIG_CROS_EC_I2C) += cros_ec_i2c.o +obj-$(CONFIG_CROS_EC_ISHTP) += cros_ec_ishtp.o obj-$(CONFIG_CROS_EC_RPMSG) += cros_ec_rpmsg.o obj-$(CONFIG_CROS_EC_SPI) += cros_ec_spi.o -cros_ec_lpcs-objs := cros_ec_lpc.o cros_ec_lpc_reg.o -cros_ec_lpcs-$(CONFIG_CROS_EC_LPC_MEC) += cros_ec_lpc_mec.o +cros_ec_lpcs-objs := cros_ec_lpc.o cros_ec_lpc_mec.o obj-$(CONFIG_CROS_EC_LPC) += cros_ec_lpcs.o obj-$(CONFIG_CROS_EC_PROTO) += cros_ec_proto.o cros_ec_trace.o obj-$(CONFIG_CROS_KBD_LED_BACKLIGHT) += cros_kbd_led_backlight.o diff --git a/drivers/platform/chrome/cros_ec_debugfs.c b/drivers/platform/chrome/cros_ec_debugfs.c index 4c2a27f6a6d0..8ec1cc2889f2 100644 --- a/drivers/platform/chrome/cros_ec_debugfs.c +++ b/drivers/platform/chrome/cros_ec_debugfs.c @@ -25,7 +25,8 @@ #define CIRC_ADD(idx, size, value) (((idx) + (value)) & ((size) - 1)) -/* struct cros_ec_debugfs - ChromeOS EC debugging information +/** + * struct cros_ec_debugfs - EC debugging information. * * @ec: EC device this debugfs information belongs to * @dir: dentry for debugfs files @@ -241,7 +242,35 @@ static ssize_t cros_ec_pdinfo_read(struct file *file, read_buf, p - read_buf); } -const struct file_operations cros_ec_console_log_fops = { +static ssize_t cros_ec_uptime_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct cros_ec_debugfs *debug_info = file->private_data; + struct cros_ec_device *ec_dev = debug_info->ec->ec_dev; + struct { + struct cros_ec_command cmd; + struct ec_response_uptime_info resp; + } __packed msg = {}; + struct ec_response_uptime_info *resp; + char read_buf[32]; + int ret; + + resp = (struct ec_response_uptime_info *)&msg.resp; + + msg.cmd.command = EC_CMD_GET_UPTIME_INFO; + msg.cmd.insize = sizeof(*resp); + + ret = cros_ec_cmd_xfer_status(ec_dev, &msg.cmd); + if (ret < 0) + return ret; + + ret = scnprintf(read_buf, sizeof(read_buf), "%u\n", + resp->time_since_ec_boot_ms); + + return simple_read_from_buffer(user_buf, count, ppos, read_buf, ret); +} + +static const struct file_operations cros_ec_console_log_fops = { .owner = THIS_MODULE, .open = cros_ec_console_log_open, .read = cros_ec_console_log_read, @@ -250,13 +279,20 @@ const struct file_operations cros_ec_console_log_fops = { .release = cros_ec_console_log_release, }; -const struct file_operations cros_ec_pdinfo_fops = { +static const struct file_operations cros_ec_pdinfo_fops = { .owner = THIS_MODULE, .open = simple_open, .read = cros_ec_pdinfo_read, .llseek = default_llseek, }; +static const struct file_operations cros_ec_uptime_fops = { + .owner = THIS_MODULE, + .open = simple_open, + .read = cros_ec_uptime_read, + .llseek = default_llseek, +}; + static int ec_read_version_supported(struct cros_ec_dev *ec) { struct ec_params_get_cmd_versions_v1 *params; @@ -408,6 +444,12 @@ static int cros_ec_debugfs_probe(struct platform_device *pd) debugfs_create_file("pdinfo", 0444, debug_info->dir, debug_info, &cros_ec_pdinfo_fops); + debugfs_create_file("uptime", 0444, debug_info->dir, debug_info, + &cros_ec_uptime_fops); + + debugfs_create_x32("last_resume_result", 0444, debug_info->dir, + &ec->ec_dev->last_resume_result); + ec->debug_info = debug_info; dev_set_drvdata(&pd->dev, ec); diff --git a/drivers/platform/chrome/cros_ec_ishtp.c b/drivers/platform/chrome/cros_ec_ishtp.c new file mode 100644 index 000000000000..e504d255d5ce --- /dev/null +++ b/drivers/platform/chrome/cros_ec_ishtp.c @@ -0,0 +1,763 @@ +// SPDX-License-Identifier: GPL-2.0 +// ISHTP interface for ChromeOS Embedded Controller +// +// Copyright (c) 2019, Intel Corporation. +// +// ISHTP client driver for talking to the Chrome OS EC firmware running +// on Intel Integrated Sensor Hub (ISH) using the ISH Transport protocol +// (ISH-TP). + +#include <linux/delay.h> +#include <linux/mfd/core.h> +#include <linux/mfd/cros_ec.h> +#include <linux/mfd/cros_ec_commands.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/intel-ish-client-if.h> + +/* + * ISH TX/RX ring buffer pool size + * + * The AP->ISH messages and corresponding ISH->AP responses are + * serialized. We need 1 TX and 1 RX buffer for these. + * + * The MKBP ISH->AP events are serialized. We need one additional RX + * buffer for them. + */ +#define CROS_ISH_CL_TX_RING_SIZE 8 +#define CROS_ISH_CL_RX_RING_SIZE 8 + +/* ISH CrOS EC Host Commands */ +enum cros_ec_ish_channel { + CROS_EC_COMMAND = 1, /* AP->ISH message */ + CROS_MKBP_EVENT = 2, /* ISH->AP events */ +}; + +/* + * ISH firmware timeout for 1 message send failure is 1Hz, and the + * firmware will retry 2 times, so 3Hz is used for timeout. + */ +#define ISHTP_SEND_TIMEOUT (3 * HZ) + +/* ISH Transport CrOS EC ISH client unique GUID */ +static const guid_t cros_ish_guid = + GUID_INIT(0x7b7154d0, 0x56f4, 0x4bdc, + 0xb0, 0xd8, 0x9e, 0x7c, 0xda, 0xe0, 0xd6, 0xa0); + +struct header { + u8 channel; + u8 status; + u8 reserved[2]; +} __packed; + +struct cros_ish_out_msg { + struct header hdr; + struct ec_host_request ec_request; +} __packed; + +struct cros_ish_in_msg { + struct header hdr; + struct ec_host_response ec_response; +} __packed; + +#define IN_MSG_EC_RESPONSE_PREAMBLE \ + offsetof(struct cros_ish_in_msg, ec_response) + +#define OUT_MSG_EC_REQUEST_PREAMBLE \ + offsetof(struct cros_ish_out_msg, ec_request) + +#define cl_data_to_dev(client_data) ishtp_device((client_data)->cl_device) + +/* + * The Read-Write Semaphore is used to prevent message TX or RX while + * the ishtp client is being initialized or undergoing reset. + * + * The readers are the kernel function calls responsible for IA->ISH + * and ISH->AP messaging. + * + * The writers are .reset() and .probe() function. + */ +DECLARE_RWSEM(init_lock); + +/** + * struct response_info - Encapsulate firmware response related + * information for passing between function ish_send() and + * process_recv() callback. + * + * @data: Copy the data received from firmware here. + * @max_size: Max size allocated for the @data buffer. If the received + * data exceeds this value, we log an error. + * @size: Actual size of data received from firmware. + * @error: 0 for success, negative error code for a failure in process_recv(). + * @received: Set to true on receiving a valid firmware response to host command + * @wait_queue: Wait queue for host to wait for firmware response. + */ +struct response_info { + void *data; + size_t max_size; + size_t size; + int error; + bool received; + wait_queue_head_t wait_queue; +}; + +/** + * struct ishtp_cl_data - Encapsulate per ISH TP Client. + * + * @cros_ish_cl: ISHTP firmware client instance. + * @cl_device: ISHTP client device instance. + * @response: Response info passing between ish_send() and process_recv(). + * @work_ishtp_reset: Work queue reset handling. + * @work_ec_evt: Work queue for EC events. + * @ec_dev: CrOS EC MFD device. + * + * This structure is used to store per client data. + */ +struct ishtp_cl_data { + struct ishtp_cl *cros_ish_cl; + struct ishtp_cl_device *cl_device; + + /* + * Used for passing firmware response information between + * ish_send() and process_recv() callback. + */ + struct response_info response; + + struct work_struct work_ishtp_reset; + struct work_struct work_ec_evt; + struct cros_ec_device *ec_dev; +}; + +/** + * ish_evt_handler - ISH to AP event handler + * @work: Work struct + */ +static void ish_evt_handler(struct work_struct *work) +{ + struct ishtp_cl_data *client_data = + container_of(work, struct ishtp_cl_data, work_ec_evt); + struct cros_ec_device *ec_dev = client_data->ec_dev; + + if (cros_ec_get_next_event(ec_dev, NULL) > 0) { + blocking_notifier_call_chain(&ec_dev->event_notifier, + 0, ec_dev); + } +} + +/** + * ish_send() - Send message from host to firmware + * + * @client_data: Client data instance + * @out_msg: Message buffer to be sent to firmware + * @out_size: Size of out going message + * @in_msg: Message buffer where the incoming data is copied. This buffer + * is allocated by calling + * @in_size: Max size of incoming message + * + * Return: Number of bytes copied in the in_msg on success, negative + * error code on failure. + */ +static int ish_send(struct ishtp_cl_data *client_data, + u8 *out_msg, size_t out_size, + u8 *in_msg, size_t in_size) +{ + int rv; + struct header *out_hdr = (struct header *)out_msg; + struct ishtp_cl *cros_ish_cl = client_data->cros_ish_cl; + + dev_dbg(cl_data_to_dev(client_data), + "%s: channel=%02u status=%02u\n", + __func__, out_hdr->channel, out_hdr->status); + + /* Setup for incoming response */ + client_data->response.data = in_msg; + client_data->response.max_size = in_size; + client_data->response.error = 0; + client_data->response.received = false; + + rv = ishtp_cl_send(cros_ish_cl, out_msg, out_size); + if (rv) { + dev_err(cl_data_to_dev(client_data), + "ishtp_cl_send error %d\n", rv); + return rv; + } + + wait_event_interruptible_timeout(client_data->response.wait_queue, + client_data->response.received, + ISHTP_SEND_TIMEOUT); + if (!client_data->response.received) { + dev_err(cl_data_to_dev(client_data), + "Timed out for response to host message\n"); + return -ETIMEDOUT; + } + + if (client_data->response.error < 0) + return client_data->response.error; + + return client_data->response.size; +} + +/** + * process_recv() - Received and parse incoming packet + * @cros_ish_cl: Client instance to get stats + * @rb_in_proc: Host interface message buffer + * + * Parse the incoming packet. If it is a response packet then it will + * update per instance flags and wake up the caller waiting to for the + * response. If it is an event packet then it will schedule event work. + */ +static void process_recv(struct ishtp_cl *cros_ish_cl, + struct ishtp_cl_rb *rb_in_proc) +{ + size_t data_len = rb_in_proc->buf_idx; + struct ishtp_cl_data *client_data = + ishtp_get_client_data(cros_ish_cl); + struct device *dev = cl_data_to_dev(client_data); + struct cros_ish_in_msg *in_msg = + (struct cros_ish_in_msg *)rb_in_proc->buffer.data; + + /* Proceed only if reset or init is not in progress */ + if (!down_read_trylock(&init_lock)) { + /* Free the buffer */ + ishtp_cl_io_rb_recycle(rb_in_proc); + dev_warn(dev, + "Host is not ready to receive incoming messages\n"); + return; + } + + /* + * All firmware messages contain a header. Check the buffer size + * before accessing elements inside. + */ + if (!rb_in_proc->buffer.data) { + dev_warn(dev, "rb_in_proc->buffer.data returned null"); + client_data->response.error = -EBADMSG; + goto end_error; + } + + if (data_len < sizeof(struct header)) { + dev_err(dev, "data size %zu is less than header %zu\n", + data_len, sizeof(struct header)); + client_data->response.error = -EMSGSIZE; + goto end_error; + } + + dev_dbg(dev, "channel=%02u status=%02u\n", + in_msg->hdr.channel, in_msg->hdr.status); + + switch (in_msg->hdr.channel) { + case CROS_EC_COMMAND: + /* Sanity check */ + if (!client_data->response.data) { + dev_err(dev, + "Receiving buffer is null. Should be allocated by calling function\n"); + client_data->response.error = -EINVAL; + goto error_wake_up; + } + + if (client_data->response.received) { + dev_err(dev, + "Previous firmware message not yet processed\n"); + client_data->response.error = -EINVAL; + goto error_wake_up; + } + + if (data_len > client_data->response.max_size) { + dev_err(dev, + "Received buffer size %zu is larger than allocated buffer %zu\n", + data_len, client_data->response.max_size); + client_data->response.error = -EMSGSIZE; + goto error_wake_up; + } + + if (in_msg->hdr.status) { + dev_err(dev, "firmware returned status %d\n", + in_msg->hdr.status); + client_data->response.error = -EIO; + goto error_wake_up; + } + + /* Update the actual received buffer size */ + client_data->response.size = data_len; + + /* + * Copy the buffer received in firmware response for the + * calling thread. + */ + memcpy(client_data->response.data, + rb_in_proc->buffer.data, data_len); + + /* Set flag before waking up the caller */ + client_data->response.received = true; +error_wake_up: + /* Wake the calling thread */ + wake_up_interruptible(&client_data->response.wait_queue); + + break; + + case CROS_MKBP_EVENT: + /* The event system doesn't send any data in buffer */ + schedule_work(&client_data->work_ec_evt); + + break; + + default: + dev_err(dev, "Invalid channel=%02d\n", in_msg->hdr.channel); + } + +end_error: + /* Free the buffer */ + ishtp_cl_io_rb_recycle(rb_in_proc); + + up_read(&init_lock); +} + +/** + * ish_event_cb() - bus driver callback for incoming message + * @cl_device: ISHTP client device for which this message is targeted. + * + * Remove the packet from the list and process the message by calling + * process_recv. + */ +static void ish_event_cb(struct ishtp_cl_device *cl_device) +{ + struct ishtp_cl_rb *rb_in_proc; + struct ishtp_cl *cros_ish_cl = ishtp_get_drvdata(cl_device); + + while ((rb_in_proc = ishtp_cl_rx_get_rb(cros_ish_cl)) != NULL) { + /* Decide what to do with received data */ + process_recv(cros_ish_cl, rb_in_proc); + } +} + +/** + * cros_ish_init() - Init function for ISHTP client + * @cros_ish_cl: ISHTP client instance + * + * This function complete the initializtion of the client. + * + * Return: 0 for success, negative error code for failure. + */ +static int cros_ish_init(struct ishtp_cl *cros_ish_cl) +{ + int rv; + struct ishtp_device *dev; + struct ishtp_fw_client *fw_client; + struct ishtp_cl_data *client_data = ishtp_get_client_data(cros_ish_cl); + + rv = ishtp_cl_link(cros_ish_cl); + if (rv) { + dev_err(cl_data_to_dev(client_data), + "ishtp_cl_link failed\n"); + return rv; + } + + dev = ishtp_get_ishtp_device(cros_ish_cl); + + /* Connect to firmware client */ + ishtp_set_tx_ring_size(cros_ish_cl, CROS_ISH_CL_TX_RING_SIZE); + ishtp_set_rx_ring_size(cros_ish_cl, CROS_ISH_CL_RX_RING_SIZE); + + fw_client = ishtp_fw_cl_get_client(dev, &cros_ish_guid); + if (!fw_client) { + dev_err(cl_data_to_dev(client_data), + "ish client uuid not found\n"); + rv = -ENOENT; + goto err_cl_unlink; + } + + ishtp_cl_set_fw_client_id(cros_ish_cl, + ishtp_get_fw_client_id(fw_client)); + ishtp_set_connection_state(cros_ish_cl, ISHTP_CL_CONNECTING); + + rv = ishtp_cl_connect(cros_ish_cl); + if (rv) { + dev_err(cl_data_to_dev(client_data), + "client connect fail\n"); + goto err_cl_unlink; + } + + ishtp_register_event_cb(client_data->cl_device, ish_event_cb); + return 0; + +err_cl_unlink: + ishtp_cl_unlink(cros_ish_cl); + return rv; +} + +/** + * cros_ish_deinit() - Deinit function for ISHTP client + * @cros_ish_cl: ISHTP client instance + * + * Unlink and free cros_ec client + */ +static void cros_ish_deinit(struct ishtp_cl *cros_ish_cl) +{ + ishtp_set_connection_state(cros_ish_cl, ISHTP_CL_DISCONNECTING); + ishtp_cl_disconnect(cros_ish_cl); + ishtp_cl_unlink(cros_ish_cl); + ishtp_cl_flush_queues(cros_ish_cl); + + /* Disband and free all Tx and Rx client-level rings */ + ishtp_cl_free(cros_ish_cl); +} + +/** + * prepare_cros_ec_rx() - Check & prepare receive buffer + * @ec_dev: CrOS EC MFD device. + * @in_msg: Incoming message buffer + * @msg: cros_ec command used to send & receive data + * + * Return: 0 for success, negative error code for failure. + * + * Check the received buffer. Convert to cros_ec_command format. + */ +static int prepare_cros_ec_rx(struct cros_ec_device *ec_dev, + const struct cros_ish_in_msg *in_msg, + struct cros_ec_command *msg) +{ + u8 sum = 0; + int i, rv, offset; + + /* Check response error code */ + msg->result = in_msg->ec_response.result; + rv = cros_ec_check_result(ec_dev, msg); + if (rv < 0) + return rv; + + if (in_msg->ec_response.data_len > msg->insize) { + dev_err(ec_dev->dev, "Packet too long (%d bytes, expected %d)", + in_msg->ec_response.data_len, msg->insize); + return -ENOSPC; + } + + /* Copy response packet payload and compute checksum */ + for (i = 0; i < sizeof(struct ec_host_response); i++) + sum += ((u8 *)in_msg)[IN_MSG_EC_RESPONSE_PREAMBLE + i]; + + offset = sizeof(struct cros_ish_in_msg); + for (i = 0; i < in_msg->ec_response.data_len; i++) + sum += msg->data[i] = ((u8 *)in_msg)[offset + i]; + + if (sum) { + dev_dbg(ec_dev->dev, "Bad received packet checksum %d\n", sum); + return -EBADMSG; + } + + return 0; +} + +static int cros_ec_pkt_xfer_ish(struct cros_ec_device *ec_dev, + struct cros_ec_command *msg) +{ + int rv; + struct ishtp_cl *cros_ish_cl = ec_dev->priv; + struct ishtp_cl_data *client_data = ishtp_get_client_data(cros_ish_cl); + struct device *dev = cl_data_to_dev(client_data); + struct cros_ish_in_msg *in_msg = (struct cros_ish_in_msg *)ec_dev->din; + struct cros_ish_out_msg *out_msg = + (struct cros_ish_out_msg *)ec_dev->dout; + size_t in_size = sizeof(struct cros_ish_in_msg) + msg->insize; + size_t out_size = sizeof(struct cros_ish_out_msg) + msg->outsize; + + /* Sanity checks */ + if (in_size > ec_dev->din_size) { + dev_err(dev, + "Incoming payload size %zu is too large for ec_dev->din_size %d\n", + in_size, ec_dev->din_size); + return -EMSGSIZE; + } + + if (out_size > ec_dev->dout_size) { + dev_err(dev, + "Outgoing payload size %zu is too large for ec_dev->dout_size %d\n", + out_size, ec_dev->dout_size); + return -EMSGSIZE; + } + + /* Proceed only if reset-init is not in progress */ + if (!down_read_trylock(&init_lock)) { + dev_warn(dev, + "Host is not ready to send messages to ISH. Try again\n"); + return -EAGAIN; + } + + /* Prepare the package to be sent over ISH TP */ + out_msg->hdr.channel = CROS_EC_COMMAND; + out_msg->hdr.status = 0; + + ec_dev->dout += OUT_MSG_EC_REQUEST_PREAMBLE; + cros_ec_prepare_tx(ec_dev, msg); + ec_dev->dout -= OUT_MSG_EC_REQUEST_PREAMBLE; + + dev_dbg(dev, + "out_msg: struct_ver=0x%x checksum=0x%x command=0x%x command_ver=0x%x data_len=0x%x\n", + out_msg->ec_request.struct_version, + out_msg->ec_request.checksum, + out_msg->ec_request.command, + out_msg->ec_request.command_version, + out_msg->ec_request.data_len); + + /* Send command to ISH EC firmware and read response */ + rv = ish_send(client_data, + (u8 *)out_msg, out_size, + (u8 *)in_msg, in_size); + if (rv < 0) + goto end_error; + + rv = prepare_cros_ec_rx(ec_dev, in_msg, msg); + if (rv) + goto end_error; + + rv = in_msg->ec_response.data_len; + + dev_dbg(dev, + "in_msg: struct_ver=0x%x checksum=0x%x result=0x%x data_len=0x%x\n", + in_msg->ec_response.struct_version, + in_msg->ec_response.checksum, + in_msg->ec_response.result, + in_msg->ec_response.data_len); + +end_error: + if (msg->command == EC_CMD_REBOOT_EC) + msleep(EC_REBOOT_DELAY_MS); + + up_read(&init_lock); + + return rv; +} + +static int cros_ec_dev_init(struct ishtp_cl_data *client_data) +{ + struct cros_ec_device *ec_dev; + struct device *dev = cl_data_to_dev(client_data); + + ec_dev = devm_kzalloc(dev, sizeof(*ec_dev), GFP_KERNEL); + if (!ec_dev) + return -ENOMEM; + + client_data->ec_dev = ec_dev; + dev->driver_data = ec_dev; + + ec_dev->dev = dev; + ec_dev->priv = client_data->cros_ish_cl; + ec_dev->cmd_xfer = NULL; + ec_dev->pkt_xfer = cros_ec_pkt_xfer_ish; + ec_dev->phys_name = dev_name(dev); + ec_dev->din_size = sizeof(struct cros_ish_in_msg) + + sizeof(struct ec_response_get_protocol_info); + ec_dev->dout_size = sizeof(struct cros_ish_out_msg); + + return cros_ec_register(ec_dev); +} + +static void reset_handler(struct work_struct *work) +{ + int rv; + struct device *dev; + struct ishtp_cl *cros_ish_cl; + struct ishtp_cl_device *cl_device; + struct ishtp_cl_data *client_data = + container_of(work, struct ishtp_cl_data, work_ishtp_reset); + + /* Lock for reset to complete */ + down_write(&init_lock); + + cros_ish_cl = client_data->cros_ish_cl; + cl_device = client_data->cl_device; + + /* Unlink, flush queues & start again */ + ishtp_cl_unlink(cros_ish_cl); + ishtp_cl_flush_queues(cros_ish_cl); + ishtp_cl_free(cros_ish_cl); + + cros_ish_cl = ishtp_cl_allocate(cl_device); + if (!cros_ish_cl) { + up_write(&init_lock); + return; + } + + ishtp_set_drvdata(cl_device, cros_ish_cl); + ishtp_set_client_data(cros_ish_cl, client_data); + client_data->cros_ish_cl = cros_ish_cl; + + rv = cros_ish_init(cros_ish_cl); + if (rv) { + ishtp_cl_free(cros_ish_cl); + dev_err(cl_data_to_dev(client_data), "Reset Failed\n"); + up_write(&init_lock); + return; + } + + /* Refresh ec_dev device pointers */ + client_data->ec_dev->priv = client_data->cros_ish_cl; + dev = cl_data_to_dev(client_data); + dev->driver_data = client_data->ec_dev; + + dev_info(cl_data_to_dev(client_data), "Chrome EC ISH reset done\n"); + + up_write(&init_lock); +} + +/** + * cros_ec_ishtp_probe() - ISHTP client driver probe callback + * @cl_device: ISHTP client device instance + * + * Return: 0 for success, negative error code for failure. + */ +static int cros_ec_ishtp_probe(struct ishtp_cl_device *cl_device) +{ + int rv; + struct ishtp_cl *cros_ish_cl; + struct ishtp_cl_data *client_data = + devm_kzalloc(ishtp_device(cl_device), + sizeof(*client_data), GFP_KERNEL); + if (!client_data) + return -ENOMEM; + + /* Lock for initialization to complete */ + down_write(&init_lock); + + cros_ish_cl = ishtp_cl_allocate(cl_device); + if (!cros_ish_cl) { + rv = -ENOMEM; + goto end_ishtp_cl_alloc_error; + } + + ishtp_set_drvdata(cl_device, cros_ish_cl); + ishtp_set_client_data(cros_ish_cl, client_data); + client_data->cros_ish_cl = cros_ish_cl; + client_data->cl_device = cl_device; + + init_waitqueue_head(&client_data->response.wait_queue); + + INIT_WORK(&client_data->work_ishtp_reset, + reset_handler); + INIT_WORK(&client_data->work_ec_evt, + ish_evt_handler); + + rv = cros_ish_init(cros_ish_cl); + if (rv) + goto end_ishtp_cl_init_error; + + ishtp_get_device(cl_device); + + up_write(&init_lock); + + /* Register croc_ec_dev mfd */ + rv = cros_ec_dev_init(client_data); + if (rv) + goto end_cros_ec_dev_init_error; + + return 0; + +end_cros_ec_dev_init_error: + ishtp_set_connection_state(cros_ish_cl, ISHTP_CL_DISCONNECTING); + ishtp_cl_disconnect(cros_ish_cl); + ishtp_cl_unlink(cros_ish_cl); + ishtp_cl_flush_queues(cros_ish_cl); + ishtp_put_device(cl_device); +end_ishtp_cl_init_error: + ishtp_cl_free(cros_ish_cl); +end_ishtp_cl_alloc_error: + up_write(&init_lock); + return rv; +} + +/** + * cros_ec_ishtp_remove() - ISHTP client driver remove callback + * @cl_device: ISHTP client device instance + * + * Return: 0 + */ +static int cros_ec_ishtp_remove(struct ishtp_cl_device *cl_device) +{ + struct ishtp_cl *cros_ish_cl = ishtp_get_drvdata(cl_device); + struct ishtp_cl_data *client_data = ishtp_get_client_data(cros_ish_cl); + + cancel_work_sync(&client_data->work_ishtp_reset); + cancel_work_sync(&client_data->work_ec_evt); + cros_ish_deinit(cros_ish_cl); + ishtp_put_device(cl_device); + + return 0; +} + +/** + * cros_ec_ishtp_reset() - ISHTP client driver reset callback + * @cl_device: ISHTP client device instance + * + * Return: 0 + */ +static int cros_ec_ishtp_reset(struct ishtp_cl_device *cl_device) +{ + struct ishtp_cl *cros_ish_cl = ishtp_get_drvdata(cl_device); + struct ishtp_cl_data *client_data = ishtp_get_client_data(cros_ish_cl); + + schedule_work(&client_data->work_ishtp_reset); + + return 0; +} + +/** + * cros_ec_ishtp_suspend() - ISHTP client driver suspend callback + * @device: device instance + * + * Return: 0 for success, negative error code for failure. + */ +static int __maybe_unused cros_ec_ishtp_suspend(struct device *device) +{ + struct ishtp_cl_device *cl_device = dev_get_drvdata(device); + struct ishtp_cl *cros_ish_cl = ishtp_get_drvdata(cl_device); + struct ishtp_cl_data *client_data = ishtp_get_client_data(cros_ish_cl); + + return cros_ec_suspend(client_data->ec_dev); +} + +/** + * cros_ec_ishtp_resume() - ISHTP client driver resume callback + * @device: device instance + * + * Return: 0 for success, negative error code for failure. + */ +static int __maybe_unused cros_ec_ishtp_resume(struct device *device) +{ + struct ishtp_cl_device *cl_device = dev_get_drvdata(device); + struct ishtp_cl *cros_ish_cl = ishtp_get_drvdata(cl_device); + struct ishtp_cl_data *client_data = ishtp_get_client_data(cros_ish_cl); + + return cros_ec_resume(client_data->ec_dev); +} + +static SIMPLE_DEV_PM_OPS(cros_ec_ishtp_pm_ops, cros_ec_ishtp_suspend, + cros_ec_ishtp_resume); + +static struct ishtp_cl_driver cros_ec_ishtp_driver = { + .name = "cros_ec_ishtp", + .guid = &cros_ish_guid, + .probe = cros_ec_ishtp_probe, + .remove = cros_ec_ishtp_remove, + .reset = cros_ec_ishtp_reset, + .driver = { + .pm = &cros_ec_ishtp_pm_ops, + }, +}; + +static int __init cros_ec_ishtp_mod_init(void) +{ + return ishtp_cl_driver_register(&cros_ec_ishtp_driver, THIS_MODULE); +} + +static void __exit cros_ec_ishtp_mod_exit(void) +{ + ishtp_cl_driver_unregister(&cros_ec_ishtp_driver); +} + +module_init(cros_ec_ishtp_mod_init); +module_exit(cros_ec_ishtp_mod_exit); + +MODULE_DESCRIPTION("ChromeOS EC ISHTP Client Driver"); +MODULE_AUTHOR("Rushikesh S Kadam <rushikesh.s.kadam@intel.com>"); + +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("ishtp:*"); diff --git a/drivers/platform/chrome/cros_ec_lightbar.c b/drivers/platform/chrome/cros_ec_lightbar.c index d30a6650b0b5..609598bbb6c3 100644 --- a/drivers/platform/chrome/cros_ec_lightbar.c +++ b/drivers/platform/chrome/cros_ec_lightbar.c @@ -547,7 +547,7 @@ static struct attribute *__lb_cmds_attrs[] = { NULL, }; -struct attribute_group cros_ec_lightbar_attr_group = { +static struct attribute_group cros_ec_lightbar_attr_group = { .name = "lightbar", .attrs = __lb_cmds_attrs, }; @@ -600,7 +600,7 @@ static int cros_ec_lightbar_remove(struct platform_device *pd) static int __maybe_unused cros_ec_lightbar_resume(struct device *dev) { - struct cros_ec_dev *ec_dev = dev_get_drvdata(dev); + struct cros_ec_dev *ec_dev = dev_get_drvdata(dev->parent); if (userspace_control) return 0; @@ -610,7 +610,7 @@ static int __maybe_unused cros_ec_lightbar_resume(struct device *dev) static int __maybe_unused cros_ec_lightbar_suspend(struct device *dev) { - struct cros_ec_dev *ec_dev = dev_get_drvdata(dev); + struct cros_ec_dev *ec_dev = dev_get_drvdata(dev->parent); if (userspace_control) return 0; diff --git a/drivers/platform/chrome/cros_ec_lpc.c b/drivers/platform/chrome/cros_ec_lpc.c index c9c240fbe7c6..2c44c7f3322a 100644 --- a/drivers/platform/chrome/cros_ec_lpc.c +++ b/drivers/platform/chrome/cros_ec_lpc.c @@ -23,7 +23,7 @@ #include <linux/printk.h> #include <linux/suspend.h> -#include "cros_ec_lpc_reg.h" +#include "cros_ec_lpc_mec.h" #define DRV_NAME "cros_ec_lpcs" #define ACPI_DRV_NAME "GOOG0004" @@ -31,6 +31,96 @@ /* True if ACPI device is present */ static bool cros_ec_lpc_acpi_device_found; +/** + * struct lpc_driver_ops - LPC driver operations + * @read: Copy length bytes from EC address offset into buffer dest. Returns + * the 8-bit checksum of all bytes read. + * @write: Copy length bytes from buffer msg into EC address offset. Returns + * the 8-bit checksum of all bytes written. + */ +struct lpc_driver_ops { + u8 (*read)(unsigned int offset, unsigned int length, u8 *dest); + u8 (*write)(unsigned int offset, unsigned int length, const u8 *msg); +}; + +static struct lpc_driver_ops cros_ec_lpc_ops = { }; + +/* + * A generic instance of the read function of struct lpc_driver_ops, used for + * the LPC EC. + */ +static u8 cros_ec_lpc_read_bytes(unsigned int offset, unsigned int length, + u8 *dest) +{ + int sum = 0; + int i; + + for (i = 0; i < length; ++i) { + dest[i] = inb(offset + i); + sum += dest[i]; + } + + /* Return checksum of all bytes read */ + return sum; +} + +/* + * A generic instance of the write function of struct lpc_driver_ops, used for + * the LPC EC. + */ +static u8 cros_ec_lpc_write_bytes(unsigned int offset, unsigned int length, + const u8 *msg) +{ + int sum = 0; + int i; + + for (i = 0; i < length; ++i) { + outb(msg[i], offset + i); + sum += msg[i]; + } + + /* Return checksum of all bytes written */ + return sum; +} + +/* + * An instance of the read function of struct lpc_driver_ops, used for the + * MEC variant of LPC EC. + */ +static u8 cros_ec_lpc_mec_read_bytes(unsigned int offset, unsigned int length, + u8 *dest) +{ + int in_range = cros_ec_lpc_mec_in_range(offset, length); + + if (in_range < 0) + return 0; + + return in_range ? + cros_ec_lpc_io_bytes_mec(MEC_IO_READ, + offset - EC_HOST_CMD_REGION0, + length, dest) : + cros_ec_lpc_read_bytes(offset, length, dest); +} + +/* + * An instance of the write function of struct lpc_driver_ops, used for the + * MEC variant of LPC EC. + */ +static u8 cros_ec_lpc_mec_write_bytes(unsigned int offset, unsigned int length, + const u8 *msg) +{ + int in_range = cros_ec_lpc_mec_in_range(offset, length); + + if (in_range < 0) + return 0; + + return in_range ? + cros_ec_lpc_io_bytes_mec(MEC_IO_WRITE, + offset - EC_HOST_CMD_REGION0, + length, (u8 *)msg) : + cros_ec_lpc_write_bytes(offset, length, msg); +} + static int ec_response_timed_out(void) { unsigned long one_second = jiffies + HZ; @@ -38,7 +128,7 @@ static int ec_response_timed_out(void) usleep_range(200, 300); do { - if (!(cros_ec_lpc_read_bytes(EC_LPC_ADDR_HOST_CMD, 1, &data) & + if (!(cros_ec_lpc_ops.read(EC_LPC_ADDR_HOST_CMD, 1, &data) & EC_LPC_STATUS_BUSY_MASK)) return 0; usleep_range(100, 200); @@ -58,11 +148,11 @@ static int cros_ec_pkt_xfer_lpc(struct cros_ec_device *ec, ret = cros_ec_prepare_tx(ec, msg); /* Write buffer */ - cros_ec_lpc_write_bytes(EC_LPC_ADDR_HOST_PACKET, ret, ec->dout); + cros_ec_lpc_ops.write(EC_LPC_ADDR_HOST_PACKET, ret, ec->dout); /* Here we go */ sum = EC_COMMAND_PROTOCOL_3; - cros_ec_lpc_write_bytes(EC_LPC_ADDR_HOST_CMD, 1, &sum); + cros_ec_lpc_ops.write(EC_LPC_ADDR_HOST_CMD, 1, &sum); if (ec_response_timed_out()) { dev_warn(ec->dev, "EC responsed timed out\n"); @@ -71,15 +161,15 @@ static int cros_ec_pkt_xfer_lpc(struct cros_ec_device *ec, } /* Check result */ - msg->result = cros_ec_lpc_read_bytes(EC_LPC_ADDR_HOST_DATA, 1, &sum); + msg->result = cros_ec_lpc_ops.read(EC_LPC_ADDR_HOST_DATA, 1, &sum); ret = cros_ec_check_result(ec, msg); if (ret) goto done; /* Read back response */ dout = (u8 *)&response; - sum = cros_ec_lpc_read_bytes(EC_LPC_ADDR_HOST_PACKET, sizeof(response), - dout); + sum = cros_ec_lpc_ops.read(EC_LPC_ADDR_HOST_PACKET, sizeof(response), + dout); msg->result = response.result; @@ -92,9 +182,9 @@ static int cros_ec_pkt_xfer_lpc(struct cros_ec_device *ec, } /* Read response and process checksum */ - sum += cros_ec_lpc_read_bytes(EC_LPC_ADDR_HOST_PACKET + - sizeof(response), response.data_len, - msg->data); + sum += cros_ec_lpc_ops.read(EC_LPC_ADDR_HOST_PACKET + + sizeof(response), response.data_len, + msg->data); if (sum) { dev_err(ec->dev, @@ -134,17 +224,17 @@ static int cros_ec_cmd_xfer_lpc(struct cros_ec_device *ec, sum = msg->command + args.flags + args.command_version + args.data_size; /* Copy data and update checksum */ - sum += cros_ec_lpc_write_bytes(EC_LPC_ADDR_HOST_PARAM, msg->outsize, - msg->data); + sum += cros_ec_lpc_ops.write(EC_LPC_ADDR_HOST_PARAM, msg->outsize, + msg->data); /* Finalize checksum and write args */ args.checksum = sum; - cros_ec_lpc_write_bytes(EC_LPC_ADDR_HOST_ARGS, sizeof(args), - (u8 *)&args); + cros_ec_lpc_ops.write(EC_LPC_ADDR_HOST_ARGS, sizeof(args), + (u8 *)&args); /* Here we go */ sum = msg->command; - cros_ec_lpc_write_bytes(EC_LPC_ADDR_HOST_CMD, 1, &sum); + cros_ec_lpc_ops.write(EC_LPC_ADDR_HOST_CMD, 1, &sum); if (ec_response_timed_out()) { dev_warn(ec->dev, "EC responsed timed out\n"); @@ -153,14 +243,13 @@ static int cros_ec_cmd_xfer_lpc(struct cros_ec_device *ec, } /* Check result */ - msg->result = cros_ec_lpc_read_bytes(EC_LPC_ADDR_HOST_DATA, 1, &sum); + msg->result = cros_ec_lpc_ops.read(EC_LPC_ADDR_HOST_DATA, 1, &sum); ret = cros_ec_check_result(ec, msg); if (ret) goto done; /* Read back args */ - cros_ec_lpc_read_bytes(EC_LPC_ADDR_HOST_ARGS, sizeof(args), - (u8 *)&args); + cros_ec_lpc_ops.read(EC_LPC_ADDR_HOST_ARGS, sizeof(args), (u8 *)&args); if (args.data_size > msg->insize) { dev_err(ec->dev, @@ -174,8 +263,8 @@ static int cros_ec_cmd_xfer_lpc(struct cros_ec_device *ec, sum = msg->command + args.flags + args.command_version + args.data_size; /* Read response and update checksum */ - sum += cros_ec_lpc_read_bytes(EC_LPC_ADDR_HOST_PARAM, args.data_size, - msg->data); + sum += cros_ec_lpc_ops.read(EC_LPC_ADDR_HOST_PARAM, args.data_size, + msg->data); /* Verify checksum */ if (args.checksum != sum) { @@ -205,13 +294,13 @@ static int cros_ec_lpc_readmem(struct cros_ec_device *ec, unsigned int offset, /* fixed length */ if (bytes) { - cros_ec_lpc_read_bytes(EC_LPC_ADDR_MEMMAP + offset, bytes, s); + cros_ec_lpc_ops.read(EC_LPC_ADDR_MEMMAP + offset, bytes, s); return bytes; } /* string */ for (; i < EC_MEMMAP_SIZE; i++, s++) { - cros_ec_lpc_read_bytes(EC_LPC_ADDR_MEMMAP + i, 1, s); + cros_ec_lpc_ops.read(EC_LPC_ADDR_MEMMAP + i, 1, s); cnt++; if (!*s) break; @@ -248,10 +337,25 @@ static int cros_ec_lpc_probe(struct platform_device *pdev) return -EBUSY; } - cros_ec_lpc_read_bytes(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID, 2, buf); + /* + * Read the mapped ID twice, the first one is assuming the + * EC is a Microchip Embedded Controller (MEC) variant, if the + * protocol fails, fallback to the non MEC variant and try to + * read again the ID. + */ + cros_ec_lpc_ops.read = cros_ec_lpc_mec_read_bytes; + 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') { - dev_err(dev, "EC ID not detected\n"); - return -ENODEV; + /* 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; + cros_ec_lpc_ops.read(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID, 2, + buf); + if (buf[0] != 'E' || buf[1] != 'C') { + dev_err(dev, "EC ID not detected\n"); + return -ENODEV; + } } if (!devm_request_region(dev, EC_HOST_CMD_REGION0, @@ -405,7 +509,7 @@ static int cros_ec_lpc_resume(struct device *dev) } #endif -const struct dev_pm_ops cros_ec_lpc_pm_ops = { +static const struct dev_pm_ops cros_ec_lpc_pm_ops = { SET_LATE_SYSTEM_SLEEP_PM_OPS(cros_ec_lpc_suspend, cros_ec_lpc_resume) }; @@ -446,13 +550,14 @@ static int __init cros_ec_lpc_init(void) return -ENODEV; } - cros_ec_lpc_reg_init(); + cros_ec_lpc_mec_init(EC_HOST_CMD_REGION0, + EC_LPC_ADDR_MEMMAP + EC_MEMMAP_SIZE); /* Register the driver */ ret = platform_driver_register(&cros_ec_lpc_driver); if (ret) { pr_err(DRV_NAME ": can't register driver: %d\n", ret); - cros_ec_lpc_reg_destroy(); + cros_ec_lpc_mec_destroy(); return ret; } @@ -462,7 +567,7 @@ static int __init cros_ec_lpc_init(void) if (ret) { pr_err(DRV_NAME ": can't register device: %d\n", ret); platform_driver_unregister(&cros_ec_lpc_driver); - cros_ec_lpc_reg_destroy(); + cros_ec_lpc_mec_destroy(); } } @@ -474,7 +579,7 @@ static void __exit cros_ec_lpc_exit(void) if (!cros_ec_lpc_acpi_device_found) platform_device_unregister(&cros_ec_lpc_device); platform_driver_unregister(&cros_ec_lpc_driver); - cros_ec_lpc_reg_destroy(); + cros_ec_lpc_mec_destroy(); } module_init(cros_ec_lpc_init); diff --git a/drivers/platform/chrome/cros_ec_lpc_mec.c b/drivers/platform/chrome/cros_ec_lpc_mec.c index d8890bafb55d..9035b17e8c86 100644 --- a/drivers/platform/chrome/cros_ec_lpc_mec.c +++ b/drivers/platform/chrome/cros_ec_lpc_mec.c @@ -17,12 +17,10 @@ static struct mutex io_mutex; static u16 mec_emi_base, mec_emi_end; -/* - * cros_ec_lpc_mec_emi_write_address - * - * Initialize EMI read / write at a given address. +/** + * cros_ec_lpc_mec_emi_write_address() - Initialize EMI at a given address. * - * @addr: Starting read / write address + * @addr: Starting read / write address * @access_type: Type of access, typically 32-bit auto-increment */ static void cros_ec_lpc_mec_emi_write_address(u16 addr, @@ -61,15 +59,15 @@ int cros_ec_lpc_mec_in_range(unsigned int offset, unsigned int length) return 0; } -/* - * cros_ec_lpc_io_bytes_mec - Read / write bytes to MEC EMI port +/** + * cros_ec_lpc_io_bytes_mec() - Read / write bytes to MEC EMI port. * * @io_type: MEC_IO_READ or MEC_IO_WRITE, depending on request * @offset: Base read / write address * @length: Number of bytes to read / write * @buf: Destination / source buffer * - * @return 8-bit checksum of all bytes read / written + * Return: 8-bit checksum of all bytes read / written */ u8 cros_ec_lpc_io_bytes_mec(enum cros_ec_lpc_mec_io_type io_type, unsigned int offset, unsigned int length, diff --git a/drivers/platform/chrome/cros_ec_lpc_reg.c b/drivers/platform/chrome/cros_ec_lpc_reg.c deleted file mode 100644 index 0f5cd0ac8b49..000000000000 --- a/drivers/platform/chrome/cros_ec_lpc_reg.c +++ /dev/null @@ -1,101 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// LPC interface for ChromeOS Embedded Controller -// -// Copyright (C) 2016 Google, Inc - -#include <linux/io.h> -#include <linux/mfd/cros_ec.h> -#include <linux/mfd/cros_ec_commands.h> - -#include "cros_ec_lpc_mec.h" - -static u8 lpc_read_bytes(unsigned int offset, unsigned int length, u8 *dest) -{ - int i; - int sum = 0; - - for (i = 0; i < length; ++i) { - dest[i] = inb(offset + i); - sum += dest[i]; - } - - /* Return checksum of all bytes read */ - return sum; -} - -static u8 lpc_write_bytes(unsigned int offset, unsigned int length, u8 *msg) -{ - int i; - int sum = 0; - - for (i = 0; i < length; ++i) { - outb(msg[i], offset + i); - sum += msg[i]; - } - - /* Return checksum of all bytes written */ - return sum; -} - -#ifdef CONFIG_CROS_EC_LPC_MEC - -u8 cros_ec_lpc_read_bytes(unsigned int offset, unsigned int length, u8 *dest) -{ - int in_range = cros_ec_lpc_mec_in_range(offset, length); - - if (in_range < 0) - return 0; - - return in_range ? - cros_ec_lpc_io_bytes_mec(MEC_IO_READ, - offset - EC_HOST_CMD_REGION0, - length, dest) : - lpc_read_bytes(offset, length, dest); -} - -u8 cros_ec_lpc_write_bytes(unsigned int offset, unsigned int length, u8 *msg) -{ - int in_range = cros_ec_lpc_mec_in_range(offset, length); - - if (in_range < 0) - return 0; - - return in_range ? - cros_ec_lpc_io_bytes_mec(MEC_IO_WRITE, - offset - EC_HOST_CMD_REGION0, - length, msg) : - lpc_write_bytes(offset, length, msg); -} - -void cros_ec_lpc_reg_init(void) -{ - cros_ec_lpc_mec_init(EC_HOST_CMD_REGION0, - EC_LPC_ADDR_MEMMAP + EC_MEMMAP_SIZE); -} - -void cros_ec_lpc_reg_destroy(void) -{ - cros_ec_lpc_mec_destroy(); -} - -#else /* CONFIG_CROS_EC_LPC_MEC */ - -u8 cros_ec_lpc_read_bytes(unsigned int offset, unsigned int length, u8 *dest) -{ - return lpc_read_bytes(offset, length, dest); -} - -u8 cros_ec_lpc_write_bytes(unsigned int offset, unsigned int length, u8 *msg) -{ - return lpc_write_bytes(offset, length, msg); -} - -void cros_ec_lpc_reg_init(void) -{ -} - -void cros_ec_lpc_reg_destroy(void) -{ -} - -#endif /* CONFIG_CROS_EC_LPC_MEC */ diff --git a/drivers/platform/chrome/cros_ec_lpc_reg.h b/drivers/platform/chrome/cros_ec_lpc_reg.h deleted file mode 100644 index 416fd2572182..000000000000 --- a/drivers/platform/chrome/cros_ec_lpc_reg.h +++ /dev/null @@ -1,45 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * LPC interface for ChromeOS Embedded Controller - * - * Copyright (C) 2016 Google, Inc - */ - -#ifndef __CROS_EC_LPC_REG_H -#define __CROS_EC_LPC_REG_H - -/** - * cros_ec_lpc_read_bytes - Read bytes from a given LPC-mapped address. - * Returns 8-bit checksum of all bytes read. - * - * @offset: Base read address - * @length: Number of bytes to read - * @dest: Destination buffer - */ -u8 cros_ec_lpc_read_bytes(unsigned int offset, unsigned int length, u8 *dest); - -/** - * cros_ec_lpc_write_bytes - Write bytes to a given LPC-mapped address. - * Returns 8-bit checksum of all bytes written. - * - * @offset: Base write address - * @length: Number of bytes to write - * @msg: Write data buffer - */ -u8 cros_ec_lpc_write_bytes(unsigned int offset, unsigned int length, u8 *msg); - -/** - * cros_ec_lpc_reg_init - * - * Initialize register I/O. - */ -void cros_ec_lpc_reg_init(void); - -/** - * cros_ec_lpc_reg_destroy - * - * Cleanup reg I/O. - */ -void cros_ec_lpc_reg_destroy(void); - -#endif /* __CROS_EC_LPC_REG_H */ diff --git a/drivers/platform/chrome/cros_ec_spi.c b/drivers/platform/chrome/cros_ec_spi.c index 8e9451720e73..006a8ff64057 100644 --- a/drivers/platform/chrome/cros_ec_spi.c +++ b/drivers/platform/chrome/cros_ec_spi.c @@ -12,7 +12,7 @@ #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/spi/spi.h> - +#include <uapi/linux/sched/types.h> /* The header byte, which follows the preamble */ #define EC_MSG_HEADER 0xec @@ -67,12 +67,14 @@ * is sent when we want to turn on CS at the start of a transaction. * @end_of_msg_delay: used to set the delay_usecs on the spi_transfer that * is sent when we want to turn off CS at the end of a transaction. + * @high_pri_worker: Used to schedule high priority work. */ struct cros_ec_spi { struct spi_device *spi; s64 last_transfer_ns; unsigned int start_of_msg_delay; unsigned int end_of_msg_delay; + struct kthread_worker *high_pri_worker; }; typedef int (*cros_ec_xfer_fn_t) (struct cros_ec_device *ec_dev, @@ -89,7 +91,7 @@ typedef int (*cros_ec_xfer_fn_t) (struct cros_ec_device *ec_dev, */ struct cros_ec_xfer_work_params { - struct work_struct work; + struct kthread_work work; cros_ec_xfer_fn_t fn; struct cros_ec_device *ec_dev; struct cros_ec_command *ec_msg; @@ -632,7 +634,7 @@ exit: return ret; } -static void cros_ec_xfer_high_pri_work(struct work_struct *work) +static void cros_ec_xfer_high_pri_work(struct kthread_work *work) { struct cros_ec_xfer_work_params *params; @@ -644,12 +646,14 @@ static int cros_ec_xfer_high_pri(struct cros_ec_device *ec_dev, struct cros_ec_command *ec_msg, cros_ec_xfer_fn_t fn) { - struct cros_ec_xfer_work_params params; - - INIT_WORK_ONSTACK(¶ms.work, cros_ec_xfer_high_pri_work); - params.ec_dev = ec_dev; - params.ec_msg = ec_msg; - params.fn = fn; + struct cros_ec_spi *ec_spi = ec_dev->priv; + struct cros_ec_xfer_work_params params = { + .work = KTHREAD_WORK_INIT(params.work, + cros_ec_xfer_high_pri_work), + .ec_dev = ec_dev, + .ec_msg = ec_msg, + .fn = fn, + }; /* * This looks a bit ridiculous. Why do the work on a @@ -660,9 +664,8 @@ static int cros_ec_xfer_high_pri(struct cros_ec_device *ec_dev, * context switched out for too long and the EC giving up on * the transfer. */ - queue_work(system_highpri_wq, ¶ms.work); - flush_work(¶ms.work); - destroy_work_on_stack(¶ms.work); + kthread_queue_work(ec_spi->high_pri_worker, ¶ms.work); + kthread_flush_work(¶ms.work); return params.ret; } @@ -694,6 +697,40 @@ static void cros_ec_spi_dt_probe(struct cros_ec_spi *ec_spi, struct device *dev) ec_spi->end_of_msg_delay = val; } +static void cros_ec_spi_high_pri_release(void *worker) +{ + kthread_destroy_worker(worker); +} + +static int cros_ec_spi_devm_high_pri_alloc(struct device *dev, + struct cros_ec_spi *ec_spi) +{ + struct sched_param sched_priority = { + .sched_priority = MAX_RT_PRIO - 1, + }; + int err; + + ec_spi->high_pri_worker = + kthread_create_worker(0, "cros_ec_spi_high_pri"); + + if (IS_ERR(ec_spi->high_pri_worker)) { + err = PTR_ERR(ec_spi->high_pri_worker); + dev_err(dev, "Can't create cros_ec high pri worker: %d\n", err); + return err; + } + + err = devm_add_action_or_reset(dev, cros_ec_spi_high_pri_release, + ec_spi->high_pri_worker); + if (err) + return err; + + err = sched_setscheduler_nocheck(ec_spi->high_pri_worker->task, + SCHED_FIFO, &sched_priority); + if (err) + dev_err(dev, "Can't set cros_ec high pri priority: %d\n", err); + return err; +} + static int cros_ec_spi_probe(struct spi_device *spi) { struct device *dev = &spi->dev; @@ -703,6 +740,7 @@ static int cros_ec_spi_probe(struct spi_device *spi) spi->bits_per_word = 8; spi->mode = SPI_MODE_0; + spi->rt = true; err = spi_setup(spi); if (err < 0) return err; @@ -732,6 +770,10 @@ static int cros_ec_spi_probe(struct spi_device *spi) ec_spi->last_transfer_ns = ktime_get_ns(); + err = cros_ec_spi_devm_high_pri_alloc(dev, ec_spi); + if (err) + return err; + err = cros_ec_register(ec_dev); if (err) { dev_err(dev, "cannot register EC\n"); @@ -777,7 +819,7 @@ MODULE_DEVICE_TABLE(spi, cros_ec_spi_id); static struct spi_driver cros_ec_driver_spi = { .driver = { .name = "cros-ec-spi", - .of_match_table = of_match_ptr(cros_ec_spi_of_match), + .of_match_table = cros_ec_spi_of_match, .pm = &cros_ec_spi_pm_ops, }, .probe = cros_ec_spi_probe, diff --git a/drivers/platform/chrome/cros_ec_sysfs.c b/drivers/platform/chrome/cros_ec_sysfs.c index fe0b7614ae1b..3edb237bf8ed 100644 --- a/drivers/platform/chrome/cros_ec_sysfs.c +++ b/drivers/platform/chrome/cros_ec_sysfs.c @@ -335,7 +335,7 @@ static umode_t cros_ec_ctrl_visible(struct kobject *kobj, return a->mode; } -struct attribute_group cros_ec_attr_group = { +static struct attribute_group cros_ec_attr_group = { .attrs = __ec_attrs, .is_visible = cros_ec_ctrl_visible, }; diff --git a/drivers/platform/chrome/cros_ec_vbc.c b/drivers/platform/chrome/cros_ec_vbc.c index 8392a1ec33a7..2aaefed87eb4 100644 --- a/drivers/platform/chrome/cros_ec_vbc.c +++ b/drivers/platform/chrome/cros_ec_vbc.c @@ -101,7 +101,7 @@ static struct bin_attribute *cros_ec_vbc_bin_attrs[] = { NULL }; -struct attribute_group cros_ec_vbc_attr_group = { +static struct attribute_group cros_ec_vbc_attr_group = { .name = "vbc", .bin_attrs = cros_ec_vbc_bin_attrs, }; diff --git a/drivers/platform/chrome/wilco_ec/Kconfig b/drivers/platform/chrome/wilco_ec/Kconfig index fd29cbfd3d5d..89007b0bc743 100644 --- a/drivers/platform/chrome/wilco_ec/Kconfig +++ b/drivers/platform/chrome/wilco_ec/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only config WILCO_EC tristate "ChromeOS Wilco Embedded Controller" - depends on ACPI && X86 && CROS_EC_LPC && CROS_EC_LPC_MEC + depends on ACPI && X86 && CROS_EC_LPC help If you say Y here, you get support for talking to the ChromeOS Wilco EC over an eSPI bus. This uses a simple byte-level protocol @@ -19,3 +19,19 @@ config WILCO_EC_DEBUGFS manipulation and allow for testing arbitrary commands. This interface is intended for debug only and will not be present on production devices. + +config WILCO_EC_EVENTS + tristate "Enable event forwarding from EC to userspace" + depends on WILCO_EC + help + If you say Y here, you get support for the EC to send events + (such as power state changes) to userspace. The EC sends the events + over ACPI, and a driver queues up the events to be read by a + userspace daemon from /dev/wilco_event using read() and poll(). + +config WILCO_EC_TELEMETRY + tristate "Enable querying telemetry data from EC" + depends on WILCO_EC + help + If you say Y here, you get support to query EC telemetry data from + /dev/wilco_telem0 using write() and then read(). diff --git a/drivers/platform/chrome/wilco_ec/Makefile b/drivers/platform/chrome/wilco_ec/Makefile index 063e7fb4ea17..bc817164596e 100644 --- a/drivers/platform/chrome/wilco_ec/Makefile +++ b/drivers/platform/chrome/wilco_ec/Makefile @@ -1,6 +1,10 @@ # SPDX-License-Identifier: GPL-2.0 -wilco_ec-objs := core.o mailbox.o +wilco_ec-objs := core.o mailbox.o properties.o sysfs.o obj-$(CONFIG_WILCO_EC) += wilco_ec.o wilco_ec_debugfs-objs := debugfs.o obj-$(CONFIG_WILCO_EC_DEBUGFS) += wilco_ec_debugfs.o +wilco_ec_events-objs := event.o +obj-$(CONFIG_WILCO_EC_EVENTS) += wilco_ec_events.o +wilco_ec_telem-objs := telemetry.o +obj-$(CONFIG_WILCO_EC_TELEMETRY) += wilco_ec_telem.o diff --git a/drivers/platform/chrome/wilco_ec/core.c b/drivers/platform/chrome/wilco_ec/core.c index 05e1e2be1c91..3724bf4b77c6 100644 --- a/drivers/platform/chrome/wilco_ec/core.c +++ b/drivers/platform/chrome/wilco_ec/core.c @@ -52,9 +52,7 @@ static int wilco_ec_probe(struct platform_device *pdev) ec->dev = dev; mutex_init(&ec->mailbox_lock); - /* Largest data buffer size requirement is extended data response */ - ec->data_size = sizeof(struct wilco_ec_response) + - EC_MAILBOX_DATA_SIZE_EXTENDED; + ec->data_size = sizeof(struct wilco_ec_response) + EC_MAILBOX_DATA_SIZE; ec->data_buffer = devm_kzalloc(dev, ec->data_size, GFP_KERNEL); if (!ec->data_buffer) return -ENOMEM; @@ -89,8 +87,28 @@ static int wilco_ec_probe(struct platform_device *pdev) goto unregister_debugfs; } + ret = wilco_ec_add_sysfs(ec); + if (ret < 0) { + dev_err(dev, "Failed to create sysfs entries: %d", ret); + goto unregister_rtc; + } + + /* Register child device that will be found by the telemetry driver. */ + ec->telem_pdev = platform_device_register_data(dev, "wilco_telem", + PLATFORM_DEVID_AUTO, + ec, sizeof(*ec)); + if (IS_ERR(ec->telem_pdev)) { + dev_err(dev, "Failed to create telemetry platform device\n"); + ret = PTR_ERR(ec->telem_pdev); + goto remove_sysfs; + } + return 0; +remove_sysfs: + wilco_ec_remove_sysfs(ec); +unregister_rtc: + platform_device_unregister(ec->rtc_pdev); unregister_debugfs: if (ec->debugfs_pdev) platform_device_unregister(ec->debugfs_pdev); @@ -102,6 +120,8 @@ static int wilco_ec_remove(struct platform_device *pdev) { struct wilco_ec_device *ec = platform_get_drvdata(pdev); + wilco_ec_remove_sysfs(ec); + platform_device_unregister(ec->telem_pdev); platform_device_unregister(ec->rtc_pdev); if (ec->debugfs_pdev) platform_device_unregister(ec->debugfs_pdev); diff --git a/drivers/platform/chrome/wilco_ec/debugfs.c b/drivers/platform/chrome/wilco_ec/debugfs.c index f163476d080d..8d65a1e2f1a3 100644 --- a/drivers/platform/chrome/wilco_ec/debugfs.c +++ b/drivers/platform/chrome/wilco_ec/debugfs.c @@ -16,14 +16,14 @@ #define DRV_NAME "wilco-ec-debugfs" -/* The 256 raw bytes will take up more space when represented as a hex string */ -#define FORMATTED_BUFFER_SIZE (EC_MAILBOX_DATA_SIZE_EXTENDED * 4) +/* The raw bytes will take up more space when represented as a hex string */ +#define FORMATTED_BUFFER_SIZE (EC_MAILBOX_DATA_SIZE * 4) struct wilco_ec_debugfs { struct wilco_ec_device *ec; struct dentry *dir; size_t response_size; - u8 raw_data[EC_MAILBOX_DATA_SIZE_EXTENDED]; + u8 raw_data[EC_MAILBOX_DATA_SIZE]; u8 formatted_data[FORMATTED_BUFFER_SIZE]; }; static struct wilco_ec_debugfs *debug_info; @@ -124,12 +124,6 @@ static ssize_t raw_write(struct file *file, const char __user *user_buf, msg.response_data = debug_info->raw_data; msg.response_size = EC_MAILBOX_DATA_SIZE; - /* Telemetry commands use extended response data */ - if (msg.type == WILCO_EC_MSG_TELEMETRY_LONG) { - msg.flags |= WILCO_EC_FLAG_EXTENDED_DATA; - msg.response_size = EC_MAILBOX_DATA_SIZE_EXTENDED; - } - ret = wilco_ec_mailbox(debug_info->ec, &msg); if (ret < 0) return ret; diff --git a/drivers/platform/chrome/wilco_ec/event.c b/drivers/platform/chrome/wilco_ec/event.c new file mode 100644 index 000000000000..dba3d445623f --- /dev/null +++ b/drivers/platform/chrome/wilco_ec/event.c @@ -0,0 +1,581 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ACPI event handling for Wilco Embedded Controller + * + * Copyright 2019 Google LLC + * + * The Wilco Embedded Controller can create custom events that + * are not handled as standard ACPI objects. These events can + * contain information about changes in EC controlled features, + * such as errors and events in the dock or display. For example, + * an event is triggered if the dock is plugged into a display + * incorrectly. These events are needed for telemetry and + * diagnostics reasons, and for possibly alerting the user. + + * These events are triggered by the EC with an ACPI Notify(0x90), + * and then the BIOS reads the event buffer from EC RAM via an + * ACPI method. When the OS receives these events via ACPI, + * it passes them along to this driver. The events are put into + * a queue which can be read by a userspace daemon via a char device + * that implements read() and poll(). The event queue acts as a + * circular buffer of size 64, so if there are no userspace consumers + * the kernel will not run out of memory. The char device will appear at + * /dev/wilco_event{n}, where n is some small non-negative integer, + * starting from 0. Standard ACPI events such as the battery getting + * plugged/unplugged can also come through this path, but they are + * dealt with via other paths, and are ignored here. + + * To test, you can tail the binary data with + * $ cat /dev/wilco_event0 | hexdump -ve '1/1 "%x\n"' + * and then create an event by plugging/unplugging the battery. + */ + +#include <linux/acpi.h> +#include <linux/cdev.h> +#include <linux/device.h> +#include <linux/fs.h> +#include <linux/idr.h> +#include <linux/io.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/poll.h> +#include <linux/spinlock.h> +#include <linux/uaccess.h> +#include <linux/wait.h> + +/* ACPI Notify event code indicating event data is available. */ +#define EC_ACPI_NOTIFY_EVENT 0x90 +/* ACPI Method to execute to retrieve event data buffer from the EC. */ +#define EC_ACPI_GET_EVENT "QSET" +/* Maximum number of words in event data returned by the EC. */ +#define EC_ACPI_MAX_EVENT_WORDS 6 +#define EC_ACPI_MAX_EVENT_SIZE \ + (sizeof(struct ec_event) + (EC_ACPI_MAX_EVENT_WORDS) * sizeof(u16)) + +/* Node will appear in /dev/EVENT_DEV_NAME */ +#define EVENT_DEV_NAME "wilco_event" +#define EVENT_CLASS_NAME EVENT_DEV_NAME +#define DRV_NAME EVENT_DEV_NAME +#define EVENT_DEV_NAME_FMT (EVENT_DEV_NAME "%d") +static struct class event_class = { + .owner = THIS_MODULE, + .name = EVENT_CLASS_NAME, +}; + +/* Keep track of all the device numbers used. */ +#define EVENT_MAX_DEV 128 +static int event_major; +static DEFINE_IDA(event_ida); + +/* Size of circular queue of events. */ +#define MAX_NUM_EVENTS 64 + +/** + * struct ec_event - Extended event returned by the EC. + * @size: Number of 16bit words in structure after the size word. + * @type: Extended event type, meaningless for us. + * @event: Event data words. Max count is %EC_ACPI_MAX_EVENT_WORDS. + */ +struct ec_event { + u16 size; + u16 type; + u16 event[0]; +} __packed; + +#define ec_event_num_words(ev) (ev->size - 1) +#define ec_event_size(ev) (sizeof(*ev) + (ec_event_num_words(ev) * sizeof(u16))) + +/** + * struct ec_event_queue - Circular queue for events. + * @capacity: Number of elements the queue can hold. + * @head: Next index to write to. + * @tail: Next index to read from. + * @entries: Array of events. + */ +struct ec_event_queue { + int capacity; + int head; + int tail; + struct ec_event *entries[0]; +}; + +/* Maximum number of events to store in ec_event_queue */ +static int queue_size = 64; +module_param(queue_size, int, 0644); + +static struct ec_event_queue *event_queue_new(int capacity) +{ + struct ec_event_queue *q; + + q = kzalloc(struct_size(q, entries, capacity), GFP_KERNEL); + if (!q) + return NULL; + + q->capacity = capacity; + + return q; +} + +static inline bool event_queue_empty(struct ec_event_queue *q) +{ + /* head==tail when both full and empty, but head==NULL when empty */ + return q->head == q->tail && !q->entries[q->head]; +} + +static inline bool event_queue_full(struct ec_event_queue *q) +{ + /* head==tail when both full and empty, but head!=NULL when full */ + return q->head == q->tail && q->entries[q->head]; +} + +static struct ec_event *event_queue_pop(struct ec_event_queue *q) +{ + struct ec_event *ev; + + if (event_queue_empty(q)) + return NULL; + + ev = q->entries[q->tail]; + q->entries[q->tail] = NULL; + q->tail = (q->tail + 1) % q->capacity; + + return ev; +} + +/* + * If full, overwrite the oldest event and return it so the caller + * can kfree it. If not full, return NULL. + */ +static struct ec_event *event_queue_push(struct ec_event_queue *q, + struct ec_event *ev) +{ + struct ec_event *popped = NULL; + + if (event_queue_full(q)) + popped = event_queue_pop(q); + q->entries[q->head] = ev; + q->head = (q->head + 1) % q->capacity; + + return popped; +} + +static void event_queue_free(struct ec_event_queue *q) +{ + struct ec_event *event; + + while ((event = event_queue_pop(q)) != NULL) + kfree(event); + + kfree(q); +} + +/** + * struct event_device_data - Data for a Wilco EC device that responds to ACPI. + * @events: Circular queue of EC events to be provided to userspace. + * @queue_lock: Protect the queue from simultaneous read/writes. + * @wq: Wait queue to notify processes when events are available or the + * device has been removed. + * @cdev: Char dev that userspace reads() and polls() from. + * @dev: Device associated with the %cdev. + * @exist: Has the device been not been removed? Once a device has been removed, + * writes, reads, and new opens will fail. + * @available: Guarantee only one client can open() file and read from queue. + * + * There will be one of these structs for each ACPI device registered. This data + * is the queue of events received from ACPI that still need to be read from + * userspace, the device and char device that userspace is using, a wait queue + * used to notify different threads when something has changed, plus a flag + * on whether the ACPI device has been removed. + */ +struct event_device_data { + struct ec_event_queue *events; + spinlock_t queue_lock; + wait_queue_head_t wq; + struct device dev; + struct cdev cdev; + bool exist; + atomic_t available; +}; + +/** + * enqueue_events() - Place EC events in queue to be read by userspace. + * @adev: Device the events came from. + * @buf: Buffer of event data. + * @length: Length of event data buffer. + * + * %buf contains a number of ec_event's, packed one after the other. + * Each ec_event is of variable length. Start with the first event, copy it + * into a persistent ec_event, store that entry in the queue, move on + * to the next ec_event in buf, and repeat. + * + * Return: 0 on success or negative error code on failure. + */ +static int enqueue_events(struct acpi_device *adev, const u8 *buf, u32 length) +{ + struct event_device_data *dev_data = adev->driver_data; + struct ec_event *event, *queue_event, *old_event; + size_t num_words, event_size; + u32 offset = 0; + + while (offset < length) { + event = (struct ec_event *)(buf + offset); + + num_words = ec_event_num_words(event); + event_size = ec_event_size(event); + if (num_words > EC_ACPI_MAX_EVENT_WORDS) { + dev_err(&adev->dev, "Too many event words: %zu > %d\n", + num_words, EC_ACPI_MAX_EVENT_WORDS); + return -EOVERFLOW; + } + + /* Ensure event does not overflow the available buffer */ + if ((offset + event_size) > length) { + dev_err(&adev->dev, "Event exceeds buffer: %zu > %d\n", + offset + event_size, length); + return -EOVERFLOW; + } + + /* Point to the next event in the buffer */ + offset += event_size; + + /* Copy event into the queue */ + queue_event = kmemdup(event, event_size, GFP_KERNEL); + if (!queue_event) + return -ENOMEM; + spin_lock(&dev_data->queue_lock); + old_event = event_queue_push(dev_data->events, queue_event); + spin_unlock(&dev_data->queue_lock); + kfree(old_event); + wake_up_interruptible(&dev_data->wq); + } + + return 0; +} + +/** + * event_device_notify() - Callback when EC generates an event over ACPI. + * @adev: The device that the event is coming from. + * @value: Value passed to Notify() in ACPI. + * + * This function will read the events from the device and enqueue them. + */ +static void event_device_notify(struct acpi_device *adev, u32 value) +{ + struct acpi_buffer event_buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *obj; + acpi_status status; + + if (value != EC_ACPI_NOTIFY_EVENT) { + dev_err(&adev->dev, "Invalid event: 0x%08x\n", value); + return; + } + + /* Execute ACPI method to get event data buffer. */ + status = acpi_evaluate_object(adev->handle, EC_ACPI_GET_EVENT, + NULL, &event_buffer); + if (ACPI_FAILURE(status)) { + dev_err(&adev->dev, "Error executing ACPI method %s()\n", + EC_ACPI_GET_EVENT); + return; + } + + obj = (union acpi_object *)event_buffer.pointer; + if (!obj) { + dev_err(&adev->dev, "Nothing returned from %s()\n", + EC_ACPI_GET_EVENT); + return; + } + if (obj->type != ACPI_TYPE_BUFFER) { + dev_err(&adev->dev, "Invalid object returned from %s()\n", + EC_ACPI_GET_EVENT); + kfree(obj); + return; + } + if (obj->buffer.length < sizeof(struct ec_event)) { + dev_err(&adev->dev, "Invalid buffer length %d from %s()\n", + obj->buffer.length, EC_ACPI_GET_EVENT); + kfree(obj); + return; + } + + enqueue_events(adev, obj->buffer.pointer, obj->buffer.length); + kfree(obj); +} + +static int event_open(struct inode *inode, struct file *filp) +{ + struct event_device_data *dev_data; + + dev_data = container_of(inode->i_cdev, struct event_device_data, cdev); + if (!dev_data->exist) + return -ENODEV; + + if (atomic_cmpxchg(&dev_data->available, 1, 0) == 0) + return -EBUSY; + + /* Increase refcount on device so dev_data is not freed */ + get_device(&dev_data->dev); + stream_open(inode, filp); + filp->private_data = dev_data; + + return 0; +} + +static __poll_t event_poll(struct file *filp, poll_table *wait) +{ + struct event_device_data *dev_data = filp->private_data; + __poll_t mask = 0; + + poll_wait(filp, &dev_data->wq, wait); + if (!dev_data->exist) + return EPOLLHUP; + if (!event_queue_empty(dev_data->events)) + mask |= EPOLLIN | EPOLLRDNORM | EPOLLPRI; + return mask; +} + +/** + * event_read() - Callback for passing event data to userspace via read(). + * @filp: The file we are reading from. + * @buf: Pointer to userspace buffer to fill with one event. + * @count: Number of bytes requested. Must be at least EC_ACPI_MAX_EVENT_SIZE. + * @pos: File position pointer, irrelevant since we don't support seeking. + * + * Removes the first event from the queue, places it in the passed buffer. + * + * If there are no events in the the queue, then one of two things happens, + * depending on if the file was opened in nonblocking mode: If in nonblocking + * mode, then return -EAGAIN to say there's no data. If in blocking mode, then + * block until an event is available. + * + * Return: Number of bytes placed in buffer, negative error code on failure. + */ +static ssize_t event_read(struct file *filp, char __user *buf, size_t count, + loff_t *pos) +{ + struct event_device_data *dev_data = filp->private_data; + struct ec_event *event; + ssize_t n_bytes_written = 0; + int err; + + /* We only will give them the entire event at once */ + if (count != 0 && count < EC_ACPI_MAX_EVENT_SIZE) + return -EINVAL; + + spin_lock(&dev_data->queue_lock); + while (event_queue_empty(dev_data->events)) { + spin_unlock(&dev_data->queue_lock); + if (filp->f_flags & O_NONBLOCK) + return -EAGAIN; + + err = wait_event_interruptible(dev_data->wq, + !event_queue_empty(dev_data->events) || + !dev_data->exist); + if (err) + return err; + + /* Device was removed as we waited? */ + if (!dev_data->exist) + return -ENODEV; + spin_lock(&dev_data->queue_lock); + } + event = event_queue_pop(dev_data->events); + spin_unlock(&dev_data->queue_lock); + n_bytes_written = ec_event_size(event); + if (copy_to_user(buf, event, n_bytes_written)) + n_bytes_written = -EFAULT; + kfree(event); + + return n_bytes_written; +} + +static int event_release(struct inode *inode, struct file *filp) +{ + struct event_device_data *dev_data = filp->private_data; + + atomic_set(&dev_data->available, 1); + put_device(&dev_data->dev); + + return 0; +} + +static const struct file_operations event_fops = { + .open = event_open, + .poll = event_poll, + .read = event_read, + .release = event_release, + .llseek = no_llseek, + .owner = THIS_MODULE, +}; + +/** + * free_device_data() - Callback to free the event_device_data structure. + * @d: The device embedded in our device data, which we have been ref counting. + * + * This is called only after event_device_remove() has been called and all + * userspace programs have called event_release() on all the open file + * descriptors. + */ +static void free_device_data(struct device *d) +{ + struct event_device_data *dev_data; + + dev_data = container_of(d, struct event_device_data, dev); + event_queue_free(dev_data->events); + kfree(dev_data); +} + +static void hangup_device(struct event_device_data *dev_data) +{ + dev_data->exist = false; + /* Wake up the waiting processes so they can close. */ + wake_up_interruptible(&dev_data->wq); + put_device(&dev_data->dev); +} + +/** + * event_device_add() - Callback when creating a new device. + * @adev: ACPI device that we will be receiving events from. + * + * This finds a free minor number for the device, allocates and initializes + * some device data, and creates a new device and char dev node. + * + * The device data is freed in free_device_data(), which is called when + * %dev_data->dev is release()ed. This happens after all references to + * %dev_data->dev are dropped, which happens once both event_device_remove() + * has been called and every open()ed file descriptor has been release()ed. + * + * Return: 0 on success, negative error code on failure. + */ +static int event_device_add(struct acpi_device *adev) +{ + struct event_device_data *dev_data; + int error, minor; + + minor = ida_alloc_max(&event_ida, EVENT_MAX_DEV-1, GFP_KERNEL); + if (minor < 0) { + error = minor; + dev_err(&adev->dev, "Failed to find minor number: %d\n", error); + return error; + } + + dev_data = kzalloc(sizeof(*dev_data), GFP_KERNEL); + if (!dev_data) { + error = -ENOMEM; + goto free_minor; + } + + /* Initialize the device data. */ + adev->driver_data = dev_data; + dev_data->events = event_queue_new(queue_size); + if (!dev_data->events) { + kfree(dev_data); + error = -ENOMEM; + goto free_minor; + } + spin_lock_init(&dev_data->queue_lock); + init_waitqueue_head(&dev_data->wq); + dev_data->exist = true; + atomic_set(&dev_data->available, 1); + + /* Initialize the device. */ + dev_data->dev.devt = MKDEV(event_major, minor); + dev_data->dev.class = &event_class; + dev_data->dev.release = free_device_data; + dev_set_name(&dev_data->dev, EVENT_DEV_NAME_FMT, minor); + device_initialize(&dev_data->dev); + + /* Initialize the character device, and add it to userspace. */ + cdev_init(&dev_data->cdev, &event_fops); + error = cdev_device_add(&dev_data->cdev, &dev_data->dev); + if (error) + goto free_dev_data; + + return 0; + +free_dev_data: + hangup_device(dev_data); +free_minor: + ida_simple_remove(&event_ida, minor); + return error; +} + +static int event_device_remove(struct acpi_device *adev) +{ + struct event_device_data *dev_data = adev->driver_data; + + cdev_device_del(&dev_data->cdev, &dev_data->dev); + ida_simple_remove(&event_ida, MINOR(dev_data->dev.devt)); + hangup_device(dev_data); + + return 0; +} + +static const struct acpi_device_id event_acpi_ids[] = { + { "GOOG000D", 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, event_acpi_ids); + +static struct acpi_driver event_driver = { + .name = DRV_NAME, + .class = DRV_NAME, + .ids = event_acpi_ids, + .ops = { + .add = event_device_add, + .notify = event_device_notify, + .remove = event_device_remove, + }, + .owner = THIS_MODULE, +}; + +static int __init event_module_init(void) +{ + dev_t dev_num = 0; + int ret; + + ret = class_register(&event_class); + if (ret) { + pr_err(DRV_NAME ": Failed registering class: %d\n", ret); + return ret; + } + + /* Request device numbers, starting with minor=0. Save the major num. */ + ret = alloc_chrdev_region(&dev_num, 0, EVENT_MAX_DEV, EVENT_DEV_NAME); + if (ret) { + pr_err(DRV_NAME ": Failed allocating dev numbers: %d\n", ret); + goto destroy_class; + } + event_major = MAJOR(dev_num); + + ret = acpi_bus_register_driver(&event_driver); + if (ret < 0) { + pr_err(DRV_NAME ": Failed registering driver: %d\n", ret); + goto unregister_region; + } + + return 0; + +unregister_region: + unregister_chrdev_region(MKDEV(event_major, 0), EVENT_MAX_DEV); +destroy_class: + class_unregister(&event_class); + ida_destroy(&event_ida); + return ret; +} + +static void __exit event_module_exit(void) +{ + acpi_bus_unregister_driver(&event_driver); + unregister_chrdev_region(MKDEV(event_major, 0), EVENT_MAX_DEV); + class_unregister(&event_class); + ida_destroy(&event_ida); +} + +module_init(event_module_init); +module_exit(event_module_exit); + +MODULE_AUTHOR("Nick Crews <ncrews@chromium.org>"); +MODULE_DESCRIPTION("Wilco EC ACPI event driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/drivers/platform/chrome/wilco_ec/mailbox.c b/drivers/platform/chrome/wilco_ec/mailbox.c index 7fb58b487963..ced1f9f3dcee 100644 --- a/drivers/platform/chrome/wilco_ec/mailbox.c +++ b/drivers/platform/chrome/wilco_ec/mailbox.c @@ -119,7 +119,6 @@ static int wilco_ec_transfer(struct wilco_ec_device *ec, struct wilco_ec_response *rs; u8 checksum; u8 flag; - size_t size; /* Write request header, then data */ cros_ec_lpc_io_bytes_mec(MEC_IO_WRITE, 0, sizeof(*rq), (u8 *)rq); @@ -148,21 +147,11 @@ static int wilco_ec_transfer(struct wilco_ec_device *ec, return -EIO; } - /* - * The EC always returns either EC_MAILBOX_DATA_SIZE or - * EC_MAILBOX_DATA_SIZE_EXTENDED bytes of data, so we need to - * calculate the checksum on **all** of this data, even if we - * won't use all of it. - */ - if (msg->flags & WILCO_EC_FLAG_EXTENDED_DATA) - size = EC_MAILBOX_DATA_SIZE_EXTENDED; - else - size = EC_MAILBOX_DATA_SIZE; - /* Read back response */ rs = ec->data_buffer; checksum = cros_ec_lpc_io_bytes_mec(MEC_IO_READ, 0, - sizeof(*rs) + size, (u8 *)rs); + sizeof(*rs) + EC_MAILBOX_DATA_SIZE, + (u8 *)rs); if (checksum) { dev_dbg(ec->dev, "bad packet checksum 0x%02x\n", rs->checksum); return -EBADMSG; @@ -173,9 +162,9 @@ static int wilco_ec_transfer(struct wilco_ec_device *ec, return -EBADMSG; } - if (rs->data_size != size) { - dev_dbg(ec->dev, "unexpected packet size (%u != %zu)", - rs->data_size, size); + if (rs->data_size != EC_MAILBOX_DATA_SIZE) { + dev_dbg(ec->dev, "unexpected packet size (%u != %u)", + rs->data_size, EC_MAILBOX_DATA_SIZE); return -EMSGSIZE; } diff --git a/drivers/platform/chrome/wilco_ec/properties.c b/drivers/platform/chrome/wilco_ec/properties.c new file mode 100644 index 000000000000..e69682c95ea2 --- /dev/null +++ b/drivers/platform/chrome/wilco_ec/properties.c @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2019 Google LLC + */ + +#include <linux/platform_data/wilco-ec.h> +#include <linux/string.h> +#include <linux/unaligned/le_memmove.h> + +/* Operation code; what the EC should do with the property */ +enum ec_property_op { + EC_OP_GET = 0, + EC_OP_SET = 1, +}; + +struct ec_property_request { + u8 op; /* One of enum ec_property_op */ + u8 property_id[4]; /* The 32 bit PID is stored Little Endian */ + u8 length; + u8 data[WILCO_EC_PROPERTY_MAX_SIZE]; +} __packed; + +struct ec_property_response { + u8 reserved[2]; + u8 op; /* One of enum ec_property_op */ + u8 property_id[4]; /* The 32 bit PID is stored Little Endian */ + u8 length; + u8 data[WILCO_EC_PROPERTY_MAX_SIZE]; +} __packed; + +static int send_property_msg(struct wilco_ec_device *ec, + struct ec_property_request *rq, + struct ec_property_response *rs) +{ + struct wilco_ec_message ec_msg; + int ret; + + memset(&ec_msg, 0, sizeof(ec_msg)); + ec_msg.type = WILCO_EC_MSG_PROPERTY; + ec_msg.request_data = rq; + ec_msg.request_size = sizeof(*rq); + ec_msg.response_data = rs; + ec_msg.response_size = sizeof(*rs); + + ret = wilco_ec_mailbox(ec, &ec_msg); + if (ret < 0) + return ret; + if (rs->op != rq->op) + return -EBADMSG; + if (memcmp(rq->property_id, rs->property_id, sizeof(rs->property_id))) + return -EBADMSG; + + return 0; +} + +int wilco_ec_get_property(struct wilco_ec_device *ec, + struct wilco_ec_property_msg *prop_msg) +{ + struct ec_property_request rq; + struct ec_property_response rs; + int ret; + + memset(&rq, 0, sizeof(rq)); + rq.op = EC_OP_GET; + put_unaligned_le32(prop_msg->property_id, rq.property_id); + + ret = send_property_msg(ec, &rq, &rs); + if (ret < 0) + return ret; + + prop_msg->length = rs.length; + memcpy(prop_msg->data, rs.data, rs.length); + + return 0; +} +EXPORT_SYMBOL_GPL(wilco_ec_get_property); + +int wilco_ec_set_property(struct wilco_ec_device *ec, + struct wilco_ec_property_msg *prop_msg) +{ + struct ec_property_request rq; + struct ec_property_response rs; + int ret; + + memset(&rq, 0, sizeof(rq)); + rq.op = EC_OP_SET; + put_unaligned_le32(prop_msg->property_id, rq.property_id); + rq.length = prop_msg->length; + memcpy(rq.data, prop_msg->data, prop_msg->length); + + ret = send_property_msg(ec, &rq, &rs); + if (ret < 0) + return ret; + if (rs.length != prop_msg->length) + return -EBADMSG; + + return 0; +} +EXPORT_SYMBOL_GPL(wilco_ec_set_property); + +int wilco_ec_get_byte_property(struct wilco_ec_device *ec, u32 property_id, + u8 *val) +{ + struct wilco_ec_property_msg msg; + int ret; + + msg.property_id = property_id; + + ret = wilco_ec_get_property(ec, &msg); + if (ret < 0) + return ret; + if (msg.length != 1) + return -EBADMSG; + + *val = msg.data[0]; + + return 0; +} +EXPORT_SYMBOL_GPL(wilco_ec_get_byte_property); + +int wilco_ec_set_byte_property(struct wilco_ec_device *ec, u32 property_id, + u8 val) +{ + struct wilco_ec_property_msg msg; + + msg.property_id = property_id; + msg.data[0] = val; + msg.length = 1; + + return wilco_ec_set_property(ec, &msg); +} +EXPORT_SYMBOL_GPL(wilco_ec_set_byte_property); diff --git a/drivers/platform/chrome/wilco_ec/sysfs.c b/drivers/platform/chrome/wilco_ec/sysfs.c new file mode 100644 index 000000000000..3b86a21005d3 --- /dev/null +++ b/drivers/platform/chrome/wilco_ec/sysfs.c @@ -0,0 +1,156 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2019 Google LLC + * + * Sysfs properties to view and modify EC-controlled features on Wilco devices. + * The entries will appear under /sys/bus/platform/devices/GOOG000C:00/ + * + * See Documentation/ABI/testing/sysfs-platform-wilco-ec for more information. + */ + +#include <linux/platform_data/wilco-ec.h> +#include <linux/sysfs.h> + +#define CMD_KB_CMOS 0x7C +#define SUB_CMD_KB_CMOS_AUTO_ON 0x03 + +struct boot_on_ac_request { + u8 cmd; /* Always CMD_KB_CMOS */ + u8 reserved1; + u8 sub_cmd; /* Always SUB_CMD_KB_CMOS_AUTO_ON */ + u8 reserved3to5[3]; + u8 val; /* Either 0 or 1 */ + u8 reserved7; +} __packed; + +#define CMD_EC_INFO 0x38 +enum get_ec_info_op { + CMD_GET_EC_LABEL = 0, + CMD_GET_EC_REV = 1, + CMD_GET_EC_MODEL = 2, + CMD_GET_EC_BUILD_DATE = 3, +}; + +struct get_ec_info_req { + u8 cmd; /* Always CMD_EC_INFO */ + u8 reserved; + u8 op; /* One of enum get_ec_info_op */ +} __packed; + +struct get_ec_info_resp { + u8 reserved[2]; + char value[9]; /* __nonstring: might not be null terminated */ +} __packed; + +static ssize_t boot_on_ac_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct wilco_ec_device *ec = dev_get_drvdata(dev); + struct boot_on_ac_request rq; + struct wilco_ec_message msg; + int ret; + u8 val; + + ret = kstrtou8(buf, 10, &val); + if (ret < 0) + return ret; + if (val > 1) + return -EINVAL; + + memset(&rq, 0, sizeof(rq)); + rq.cmd = CMD_KB_CMOS; + rq.sub_cmd = SUB_CMD_KB_CMOS_AUTO_ON; + rq.val = val; + + memset(&msg, 0, sizeof(msg)); + msg.type = WILCO_EC_MSG_LEGACY; + msg.request_data = &rq; + msg.request_size = sizeof(rq); + ret = wilco_ec_mailbox(ec, &msg); + if (ret < 0) + return ret; + + return count; +} + +static DEVICE_ATTR_WO(boot_on_ac); + +static ssize_t get_info(struct device *dev, char *buf, enum get_ec_info_op op) +{ + struct wilco_ec_device *ec = dev_get_drvdata(dev); + struct get_ec_info_req req = { .cmd = CMD_EC_INFO, .op = op }; + struct get_ec_info_resp resp; + int ret; + + struct wilco_ec_message msg = { + .type = WILCO_EC_MSG_LEGACY, + .request_data = &req, + .request_size = sizeof(req), + .response_data = &resp, + .response_size = sizeof(resp), + }; + + ret = wilco_ec_mailbox(ec, &msg); + if (ret < 0) + return ret; + + return scnprintf(buf, PAGE_SIZE, "%.*s\n", (int)sizeof(resp.value), + (char *)&resp.value); +} + +static ssize_t version_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + return get_info(dev, buf, CMD_GET_EC_LABEL); +} + +static DEVICE_ATTR_RO(version); + +static ssize_t build_revision_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return get_info(dev, buf, CMD_GET_EC_REV); +} + +static DEVICE_ATTR_RO(build_revision); + +static ssize_t build_date_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return get_info(dev, buf, CMD_GET_EC_BUILD_DATE); +} + +static DEVICE_ATTR_RO(build_date); + +static ssize_t model_number_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return get_info(dev, buf, CMD_GET_EC_MODEL); +} + +static DEVICE_ATTR_RO(model_number); + + +static struct attribute *wilco_dev_attrs[] = { + &dev_attr_boot_on_ac.attr, + &dev_attr_build_date.attr, + &dev_attr_build_revision.attr, + &dev_attr_model_number.attr, + &dev_attr_version.attr, + NULL, +}; + +static struct attribute_group wilco_dev_attr_group = { + .attrs = wilco_dev_attrs, +}; + +int wilco_ec_add_sysfs(struct wilco_ec_device *ec) +{ + return sysfs_create_group(&ec->dev->kobj, &wilco_dev_attr_group); +} + +void wilco_ec_remove_sysfs(struct wilco_ec_device *ec) +{ + sysfs_remove_group(&ec->dev->kobj, &wilco_dev_attr_group); +} diff --git a/drivers/platform/chrome/wilco_ec/telemetry.c b/drivers/platform/chrome/wilco_ec/telemetry.c new file mode 100644 index 000000000000..94cdc166c840 --- /dev/null +++ b/drivers/platform/chrome/wilco_ec/telemetry.c @@ -0,0 +1,450 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Telemetry communication for Wilco EC + * + * Copyright 2019 Google LLC + * + * The Wilco Embedded Controller is able to send telemetry data + * which is useful for enterprise applications. A daemon running on + * the OS sends a command to the EC via a write() to a char device, + * and can read the response with a read(). The write() request is + * verified by the driver to ensure that it is performing only one + * of the whitelisted commands, and that no extraneous data is + * being transmitted to the EC. The response is passed directly + * back to the reader with no modification. + * + * The character device will appear as /dev/wilco_telemN, where N + * is some small non-negative integer, starting with 0. Only one + * process may have the file descriptor open at a time. The calling + * userspace program needs to keep the device file descriptor open + * between the calls to write() and read() in order to preserve the + * response. Up to 32 bytes will be available for reading. + * + * For testing purposes, try requesting the EC's firmware build + * date, by sending the WILCO_EC_TELEM_GET_VERSION command with + * argument index=3. i.e. write [0x38, 0x00, 0x03] + * to the device node. An ASCII string of the build date is + * returned. + */ + +#include <linux/cdev.h> +#include <linux/device.h> +#include <linux/fs.h> +#include <linux/module.h> +#include <linux/platform_data/wilco-ec.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/uaccess.h> + +#define TELEM_DEV_NAME "wilco_telem" +#define TELEM_CLASS_NAME TELEM_DEV_NAME +#define DRV_NAME TELEM_DEV_NAME +#define TELEM_DEV_NAME_FMT (TELEM_DEV_NAME "%d") +static struct class telem_class = { + .owner = THIS_MODULE, + .name = TELEM_CLASS_NAME, +}; + +/* Keep track of all the device numbers used. */ +#define TELEM_MAX_DEV 128 +static int telem_major; +static DEFINE_IDA(telem_ida); + +/* EC telemetry command codes */ +#define WILCO_EC_TELEM_GET_LOG 0x99 +#define WILCO_EC_TELEM_GET_VERSION 0x38 +#define WILCO_EC_TELEM_GET_FAN_INFO 0x2E +#define WILCO_EC_TELEM_GET_DIAG_INFO 0xFA +#define WILCO_EC_TELEM_GET_TEMP_INFO 0x95 +#define WILCO_EC_TELEM_GET_TEMP_READ 0x2C +#define WILCO_EC_TELEM_GET_BATT_EXT_INFO 0x07 + +#define TELEM_ARGS_SIZE_MAX 30 + +/** + * struct wilco_ec_telem_request - Telemetry command and arguments sent to EC. + * @command: One of WILCO_EC_TELEM_GET_* command codes. + * @reserved: Must be 0. + * @args: The first N bytes are one of telem_args_get_* structs, the rest is 0. + */ +struct wilco_ec_telem_request { + u8 command; + u8 reserved; + u8 args[TELEM_ARGS_SIZE_MAX]; +} __packed; + +/* + * The following telem_args_get_* structs are embedded within the |args| field + * of wilco_ec_telem_request. + */ + +struct telem_args_get_log { + u8 log_type; + u8 log_index; +} __packed; + +/* + * Get a piece of info about the EC firmware version: + * 0 = label + * 1 = svn_rev + * 2 = model_no + * 3 = build_date + * 4 = frio_version + */ +struct telem_args_get_version { + u8 index; +} __packed; + +struct telem_args_get_fan_info { + u8 command; + u8 fan_number; + u8 arg; +} __packed; + +struct telem_args_get_diag_info { + u8 type; + u8 sub_type; +} __packed; + +struct telem_args_get_temp_info { + u8 command; + u8 index; + u8 field; + u8 zone; +} __packed; + +struct telem_args_get_temp_read { + u8 sensor_index; +} __packed; + +struct telem_args_get_batt_ext_info { + u8 var_args[5]; +} __packed; + +/** + * check_telem_request() - Ensure that a request from userspace is valid. + * @rq: Request buffer copied from userspace. + * @size: Number of bytes copied from userspace. + * + * Return: 0 if valid, -EINVAL if bad command or reserved byte is non-zero, + * -EMSGSIZE if the request is too long. + * + * We do not want to allow userspace to send arbitrary telemetry commands to + * the EC. Therefore we check to ensure that + * 1. The request follows the format of struct wilco_ec_telem_request. + * 2. The supplied command code is one of the whitelisted commands. + * 3. The request only contains the necessary data for the header and arguments. + */ +static int check_telem_request(struct wilco_ec_telem_request *rq, + size_t size) +{ + size_t max_size = offsetof(struct wilco_ec_telem_request, args); + + if (rq->reserved) + return -EINVAL; + + switch (rq->command) { + case WILCO_EC_TELEM_GET_LOG: + max_size += sizeof(struct telem_args_get_log); + break; + case WILCO_EC_TELEM_GET_VERSION: + max_size += sizeof(struct telem_args_get_version); + break; + case WILCO_EC_TELEM_GET_FAN_INFO: + max_size += sizeof(struct telem_args_get_fan_info); + break; + case WILCO_EC_TELEM_GET_DIAG_INFO: + max_size += sizeof(struct telem_args_get_diag_info); + break; + case WILCO_EC_TELEM_GET_TEMP_INFO: + max_size += sizeof(struct telem_args_get_temp_info); + break; + case WILCO_EC_TELEM_GET_TEMP_READ: + max_size += sizeof(struct telem_args_get_temp_read); + break; + case WILCO_EC_TELEM_GET_BATT_EXT_INFO: + max_size += sizeof(struct telem_args_get_batt_ext_info); + break; + default: + return -EINVAL; + } + + return (size <= max_size) ? 0 : -EMSGSIZE; +} + +/** + * struct telem_device_data - Data for a Wilco EC device that queries telemetry. + * @cdev: Char dev that userspace reads and polls from. + * @dev: Device associated with the %cdev. + * @ec: Wilco EC that we will be communicating with using the mailbox interface. + * @available: Boolean of if the device can be opened. + */ +struct telem_device_data { + struct device dev; + struct cdev cdev; + struct wilco_ec_device *ec; + atomic_t available; +}; + +#define TELEM_RESPONSE_SIZE EC_MAILBOX_DATA_SIZE + +/** + * struct telem_session_data - Data that exists between open() and release(). + * @dev_data: Pointer to get back to the device data and EC. + * @request: Command and arguments sent to EC. + * @response: Response buffer of data from EC. + * @has_msg: Is there data available to read from a previous write? + */ +struct telem_session_data { + struct telem_device_data *dev_data; + struct wilco_ec_telem_request request; + u8 response[TELEM_RESPONSE_SIZE]; + bool has_msg; +}; + +/** + * telem_open() - Callback for when the device node is opened. + * @inode: inode for this char device node. + * @filp: file for this char device node. + * + * We need to ensure that after writing a command to the device, + * the same userspace process reads the corresponding result. + * Therefore, we increment a refcount on opening the device, so that + * only one process can communicate with the EC at a time. + * + * Return: 0 on success, or negative error code on failure. + */ +static int telem_open(struct inode *inode, struct file *filp) +{ + struct telem_device_data *dev_data; + struct telem_session_data *sess_data; + + /* Ensure device isn't already open */ + dev_data = container_of(inode->i_cdev, struct telem_device_data, cdev); + if (atomic_cmpxchg(&dev_data->available, 1, 0) == 0) + return -EBUSY; + + get_device(&dev_data->dev); + + sess_data = kzalloc(sizeof(*sess_data), GFP_KERNEL); + if (!sess_data) { + atomic_set(&dev_data->available, 1); + return -ENOMEM; + } + sess_data->dev_data = dev_data; + sess_data->has_msg = false; + + nonseekable_open(inode, filp); + filp->private_data = sess_data; + + return 0; +} + +static ssize_t telem_write(struct file *filp, const char __user *buf, + size_t count, loff_t *pos) +{ + struct telem_session_data *sess_data = filp->private_data; + struct wilco_ec_message msg = {}; + int ret; + + if (count > sizeof(sess_data->request)) + return -EMSGSIZE; + if (copy_from_user(&sess_data->request, buf, count)) + return -EFAULT; + ret = check_telem_request(&sess_data->request, count); + if (ret < 0) + return ret; + + memset(sess_data->response, 0, sizeof(sess_data->response)); + msg.type = WILCO_EC_MSG_TELEMETRY; + msg.request_data = &sess_data->request; + msg.request_size = sizeof(sess_data->request); + msg.response_data = sess_data->response; + msg.response_size = sizeof(sess_data->response); + + ret = wilco_ec_mailbox(sess_data->dev_data->ec, &msg); + if (ret < 0) + return ret; + if (ret != sizeof(sess_data->response)) + return -EMSGSIZE; + + sess_data->has_msg = true; + + return count; +} + +static ssize_t telem_read(struct file *filp, char __user *buf, size_t count, + loff_t *pos) +{ + struct telem_session_data *sess_data = filp->private_data; + + if (!sess_data->has_msg) + return -ENODATA; + if (count > sizeof(sess_data->response)) + return -EINVAL; + + if (copy_to_user(buf, sess_data->response, count)) + return -EFAULT; + + sess_data->has_msg = false; + + return count; +} + +static int telem_release(struct inode *inode, struct file *filp) +{ + struct telem_session_data *sess_data = filp->private_data; + + atomic_set(&sess_data->dev_data->available, 1); + put_device(&sess_data->dev_data->dev); + kfree(sess_data); + + return 0; +} + +static const struct file_operations telem_fops = { + .open = telem_open, + .write = telem_write, + .read = telem_read, + .release = telem_release, + .llseek = no_llseek, + .owner = THIS_MODULE, +}; + +/** + * telem_device_free() - Callback to free the telem_device_data structure. + * @d: The device embedded in our device data, which we have been ref counting. + * + * Once all open file descriptors are closed and the device has been removed, + * the refcount of the device will fall to 0 and this will be called. + */ +static void telem_device_free(struct device *d) +{ + struct telem_device_data *dev_data; + + dev_data = container_of(d, struct telem_device_data, dev); + kfree(dev_data); +} + +/** + * telem_device_probe() - Callback when creating a new device. + * @pdev: platform device that we will be receiving telems from. + * + * This finds a free minor number for the device, allocates and initializes + * some device data, and creates a new device and char dev node. + * + * Return: 0 on success, negative error code on failure. + */ +static int telem_device_probe(struct platform_device *pdev) +{ + struct telem_device_data *dev_data; + int error, minor; + + /* Get the next available device number */ + minor = ida_alloc_max(&telem_ida, TELEM_MAX_DEV-1, GFP_KERNEL); + if (minor < 0) { + error = minor; + dev_err(&pdev->dev, "Failed to find minor number: %d", error); + return error; + } + + dev_data = kzalloc(sizeof(*dev_data), GFP_KERNEL); + if (!dev_data) { + ida_simple_remove(&telem_ida, minor); + return -ENOMEM; + } + + /* Initialize the device data */ + dev_data->ec = dev_get_platdata(&pdev->dev); + atomic_set(&dev_data->available, 1); + platform_set_drvdata(pdev, dev_data); + + /* Initialize the device */ + dev_data->dev.devt = MKDEV(telem_major, minor); + dev_data->dev.class = &telem_class; + dev_data->dev.release = telem_device_free; + dev_set_name(&dev_data->dev, TELEM_DEV_NAME_FMT, minor); + device_initialize(&dev_data->dev); + + /* Initialize the character device and add it to userspace */; + cdev_init(&dev_data->cdev, &telem_fops); + error = cdev_device_add(&dev_data->cdev, &dev_data->dev); + if (error) { + put_device(&dev_data->dev); + ida_simple_remove(&telem_ida, minor); + return error; + } + + return 0; +} + +static int telem_device_remove(struct platform_device *pdev) +{ + struct telem_device_data *dev_data = platform_get_drvdata(pdev); + + cdev_device_del(&dev_data->cdev, &dev_data->dev); + put_device(&dev_data->dev); + ida_simple_remove(&telem_ida, MINOR(dev_data->dev.devt)); + + return 0; +} + +static struct platform_driver telem_driver = { + .probe = telem_device_probe, + .remove = telem_device_remove, + .driver = { + .name = DRV_NAME, + }, +}; + +static int __init telem_module_init(void) +{ + dev_t dev_num = 0; + int ret; + + ret = class_register(&telem_class); + if (ret) { + pr_err(DRV_NAME ": Failed registering class: %d", ret); + return ret; + } + + /* Request the kernel for device numbers, starting with minor=0 */ + ret = alloc_chrdev_region(&dev_num, 0, TELEM_MAX_DEV, TELEM_DEV_NAME); + if (ret) { + pr_err(DRV_NAME ": Failed allocating dev numbers: %d", ret); + goto destroy_class; + } + telem_major = MAJOR(dev_num); + + ret = platform_driver_register(&telem_driver); + if (ret < 0) { + pr_err(DRV_NAME ": Failed registering driver: %d\n", ret); + goto unregister_region; + } + + return 0; + +unregister_region: + unregister_chrdev_region(MKDEV(telem_major, 0), TELEM_MAX_DEV); +destroy_class: + class_unregister(&telem_class); + ida_destroy(&telem_ida); + return ret; +} + +static void __exit telem_module_exit(void) +{ + platform_driver_unregister(&telem_driver); + unregister_chrdev_region(MKDEV(telem_major, 0), TELEM_MAX_DEV); + class_unregister(&telem_class); + ida_destroy(&telem_ida); +} + +module_init(telem_module_init); +module_exit(telem_module_exit); + +MODULE_AUTHOR("Nick Crews <ncrews@chromium.org>"); +MODULE_DESCRIPTION("Wilco EC telemetry driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/include/linux/mfd/cros_ec.h b/include/linux/mfd/cros_ec.h index 5ddca44be06d..45aba26db964 100644 --- a/include/linux/mfd/cros_ec.h +++ b/include/linux/mfd/cros_ec.h @@ -155,6 +155,7 @@ struct cros_ec_device { struct ec_response_get_next_event_v1 event_data; int event_size; u32 host_event_wake_mask; + u32 last_resume_result; }; /** diff --git a/include/linux/mfd/cros_ec_commands.h b/include/linux/mfd/cros_ec_commands.h index 114614e20e4d..7ccb8757b79d 100644 --- a/include/linux/mfd/cros_ec_commands.h +++ b/include/linux/mfd/cros_ec_commands.h @@ -4,17 +4,20 @@ * * Copyright (C) 2012 Google, Inc * - * The ChromeOS EC multi function device is used to mux all the requests - * to the EC device for its multiple features: keyboard controller, - * battery charging and regulator control, firmware update. - * - * NOTE: This file is copied verbatim from the ChromeOS EC Open Source - * project in an attempt to make future updates easy to make. + * NOTE: This file is auto-generated from ChromeOS EC Open Source code from + * https://chromium.googlesource.com/chromiumos/platform/ec/+/master/include/ec_commands.h */ +/* Host communication command constants for Chrome EC */ + #ifndef __CROS_EC_COMMANDS_H #define __CROS_EC_COMMANDS_H + + + +#define BUILD_ASSERT(_cond) + /* * Current version of this protocol * @@ -25,7 +28,7 @@ #define EC_PROTO_VERSION 0x00000002 /* Command version mask */ -#define EC_VER_MASK(version) (1UL << (version)) +#define EC_VER_MASK(version) BIT(version) /* I/O addresses for ACPI commands */ #define EC_LPC_ADDR_ACPI_DATA 0x62 @@ -39,25 +42,28 @@ /* Protocol version 2 */ #define EC_LPC_ADDR_HOST_ARGS 0x800 /* And 0x801, 0x802, 0x803 */ #define EC_LPC_ADDR_HOST_PARAM 0x804 /* For version 2 params; size is - * EC_PROTO2_MAX_PARAM_SIZE */ + * EC_PROTO2_MAX_PARAM_SIZE + */ /* Protocol version 3 */ #define EC_LPC_ADDR_HOST_PACKET 0x800 /* Offset of version 3 packet */ #define EC_LPC_HOST_PACKET_SIZE 0x100 /* Max size of version 3 packet */ -/* 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. */ +/* + * 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. + */ #define EC_HOST_CMD_REGION0 0x800 #define EC_HOST_CMD_REGION1 0x880 #define EC_HOST_CMD_REGION_SIZE 0x80 /* EC command register bit functions */ -#define EC_LPC_CMDR_DATA (1 << 0) /* Data ready for host to read */ -#define EC_LPC_CMDR_PENDING (1 << 1) /* Write pending to EC */ -#define EC_LPC_CMDR_BUSY (1 << 2) /* EC is busy processing a command */ -#define EC_LPC_CMDR_CMD (1 << 3) /* Last host write was a command */ -#define EC_LPC_CMDR_ACPI_BRST (1 << 4) /* Burst mode (not used) */ -#define EC_LPC_CMDR_SCI (1 << 5) /* SCI event is pending */ -#define EC_LPC_CMDR_SMI (1 << 6) /* SMI event is pending */ +#define EC_LPC_CMDR_DATA BIT(0) /* Data ready for host to read */ +#define EC_LPC_CMDR_PENDING BIT(1) /* Write pending to EC */ +#define EC_LPC_CMDR_BUSY BIT(2) /* EC is busy processing a command */ +#define EC_LPC_CMDR_CMD BIT(3) /* Last host write was a command */ +#define EC_LPC_CMDR_ACPI_BRST BIT(4) /* Burst mode (not used) */ +#define EC_LPC_CMDR_SCI BIT(5) /* SCI event is pending */ +#define EC_LPC_CMDR_SMI BIT(6) /* SMI event is pending */ #define EC_LPC_ADDR_MEMMAP 0x900 #define EC_MEMMAP_SIZE 255 /* ACPI IO buffer max is 255 bytes */ @@ -77,13 +83,15 @@ /* Unused 0x28 - 0x2f */ #define EC_MEMMAP_SWITCHES 0x30 /* 8 bits */ /* Unused 0x31 - 0x33 */ -#define EC_MEMMAP_HOST_EVENTS 0x34 /* 32 bits */ -/* Reserve 0x38 - 0x3f for additional host event-related stuff */ -/* Battery values are all 32 bits */ +#define EC_MEMMAP_HOST_EVENTS 0x34 /* 64 bits */ +/* Battery values are all 32 bits, unless otherwise noted. */ #define EC_MEMMAP_BATT_VOLT 0x40 /* Battery Present Voltage */ #define EC_MEMMAP_BATT_RATE 0x44 /* Battery Present Rate */ #define EC_MEMMAP_BATT_CAP 0x48 /* Battery Remaining Capacity */ -#define EC_MEMMAP_BATT_FLAG 0x4c /* Battery State, defined below */ +#define EC_MEMMAP_BATT_FLAG 0x4c /* Battery State, see below (8-bit) */ +#define EC_MEMMAP_BATT_COUNT 0x4d /* Battery Count (8-bit) */ +#define EC_MEMMAP_BATT_INDEX 0x4e /* Current Battery Data Index (8-bit) */ +/* Unused 0x4f */ #define EC_MEMMAP_BATT_DCAP 0x50 /* Battery Design Capacity */ #define EC_MEMMAP_BATT_DVLT 0x54 /* Battery Design Voltage */ #define EC_MEMMAP_BATT_LFCC 0x58 /* Battery Last Full Charge Capacity */ @@ -97,15 +105,24 @@ /* Unused 0x84 - 0x8f */ #define EC_MEMMAP_ACC_STATUS 0x90 /* Accelerometer status (8 bits )*/ /* Unused 0x91 */ -#define EC_MEMMAP_ACC_DATA 0x92 /* Accelerometer data 0x92 - 0x9f */ +#define EC_MEMMAP_ACC_DATA 0x92 /* Accelerometers data 0x92 - 0x9f */ +/* 0x92: Lid Angle if available, LID_ANGLE_UNRELIABLE otherwise */ +/* 0x94 - 0x99: 1st Accelerometer */ +/* 0x9a - 0x9f: 2nd Accelerometer */ #define EC_MEMMAP_GYRO_DATA 0xa0 /* Gyroscope data 0xa0 - 0xa5 */ -/* Unused 0xa6 - 0xfe (remember, 0xff is NOT part of the memmap region) */ +/* Unused 0xa6 - 0xdf */ +/* + * ACPI is unable to access memory mapped data at or above this offset due to + * limitations of the ACPI protocol. Do not place data in the range 0xe0 - 0xfe + * which might be needed by ACPI. + */ +#define EC_MEMMAP_NO_ACPI 0xe0 /* Define the format of the accelerometer mapped memory status byte. */ #define EC_MEMMAP_ACC_STATUS_SAMPLE_ID_MASK 0x0f -#define EC_MEMMAP_ACC_STATUS_BUSY_BIT (1 << 4) -#define EC_MEMMAP_ACC_STATUS_PRESENCE_BIT (1 << 7) +#define EC_MEMMAP_ACC_STATUS_BUSY_BIT BIT(4) +#define EC_MEMMAP_ACC_STATUS_PRESENCE_BIT BIT(7) /* Number of temp sensors at EC_MEMMAP_TEMP_SENSOR */ #define EC_TEMP_SENSOR_ENTRIES 16 @@ -149,6 +166,8 @@ #define EC_BATT_FLAG_DISCHARGING 0x04 #define EC_BATT_FLAG_CHARGING 0x08 #define EC_BATT_FLAG_LEVEL_CRITICAL 0x10 +/* Set if some of the static/dynamic data is invalid (or outdated). */ +#define EC_BATT_FLAG_INVALID_DATA 0x20 /* Switch flags at EC_MEMMAP_SWITCHES */ #define EC_SWITCH_LID_OPEN 0x01 @@ -174,20 +193,242 @@ #define EC_WIRELESS_SWITCH_WWAN 0x04 /* WWAN power */ #define EC_WIRELESS_SWITCH_WLAN_POWER 0x08 /* WLAN power */ +/*****************************************************************************/ +/* + * ACPI commands + * + * These are valid ONLY on the ACPI command/data port. + */ + +/* + * ACPI Read Embedded Controller + * + * This reads from ACPI memory space on the EC (EC_ACPI_MEM_*). + * + * Use the following sequence: + * + * - Write EC_CMD_ACPI_READ to EC_LPC_ADDR_ACPI_CMD + * - Wait for EC_LPC_CMDR_PENDING bit to clear + * - Write address to EC_LPC_ADDR_ACPI_DATA + * - Wait for EC_LPC_CMDR_DATA bit to set + * - Read value from EC_LPC_ADDR_ACPI_DATA + */ +#define EC_CMD_ACPI_READ 0x0080 + +/* + * ACPI Write Embedded Controller + * + * This reads from ACPI memory space on the EC (EC_ACPI_MEM_*). + * + * Use the following sequence: + * + * - Write EC_CMD_ACPI_WRITE to EC_LPC_ADDR_ACPI_CMD + * - Wait for EC_LPC_CMDR_PENDING bit to clear + * - Write address to EC_LPC_ADDR_ACPI_DATA + * - Wait for EC_LPC_CMDR_PENDING bit to clear + * - Write value to EC_LPC_ADDR_ACPI_DATA + */ +#define EC_CMD_ACPI_WRITE 0x0081 + +/* + * ACPI Burst Enable Embedded Controller + * + * This enables burst mode on the EC to allow the host to issue several + * commands back-to-back. While in this mode, writes to mapped multi-byte + * data are locked out to ensure data consistency. + */ +#define EC_CMD_ACPI_BURST_ENABLE 0x0082 + +/* + * ACPI Burst Disable Embedded Controller + * + * This disables burst mode on the EC and stops preventing EC writes to mapped + * multi-byte data. + */ +#define EC_CMD_ACPI_BURST_DISABLE 0x0083 + +/* + * ACPI Query Embedded Controller + * + * This clears the lowest-order bit in the currently pending host events, and + * sets the result code to the 1-based index of the bit (event 0x00000001 = 1, + * event 0x80000000 = 32), or 0 if no event was pending. + */ +#define EC_CMD_ACPI_QUERY_EVENT 0x0084 + +/* Valid addresses in ACPI memory space, for read/write commands */ + +/* Memory space version; set to EC_ACPI_MEM_VERSION_CURRENT */ +#define EC_ACPI_MEM_VERSION 0x00 +/* + * Test location; writing value here updates test compliment byte to (0xff - + * value). + */ +#define EC_ACPI_MEM_TEST 0x01 +/* Test compliment; writes here are ignored. */ +#define EC_ACPI_MEM_TEST_COMPLIMENT 0x02 + +/* Keyboard backlight brightness percent (0 - 100) */ +#define EC_ACPI_MEM_KEYBOARD_BACKLIGHT 0x03 +/* DPTF Target Fan Duty (0-100, 0xff for auto/none) */ +#define EC_ACPI_MEM_FAN_DUTY 0x04 + +/* + * DPTF temp thresholds. Any of the EC's temp sensors can have up to two + * independent thresholds attached to them. The current value of the ID + * register determines which sensor is affected by the THRESHOLD and COMMIT + * registers. The THRESHOLD register uses the same EC_TEMP_SENSOR_OFFSET scheme + * as the memory-mapped sensors. The COMMIT register applies those settings. + * + * The spec does not mandate any way to read back the threshold settings + * themselves, but when a threshold is crossed the AP needs a way to determine + * which sensor(s) are responsible. Each reading of the ID register clears and + * returns one sensor ID that has crossed one of its threshold (in either + * direction) since the last read. A value of 0xFF means "no new thresholds + * have tripped". Setting or enabling the thresholds for a sensor will clear + * the unread event count for that sensor. + */ +#define EC_ACPI_MEM_TEMP_ID 0x05 +#define EC_ACPI_MEM_TEMP_THRESHOLD 0x06 +#define EC_ACPI_MEM_TEMP_COMMIT 0x07 +/* + * Here are the bits for the COMMIT register: + * bit 0 selects the threshold index for the chosen sensor (0/1) + * bit 1 enables/disables the selected threshold (0 = off, 1 = on) + * Each write to the commit register affects one threshold. + */ +#define EC_ACPI_MEM_TEMP_COMMIT_SELECT_MASK BIT(0) +#define EC_ACPI_MEM_TEMP_COMMIT_ENABLE_MASK BIT(1) +/* + * Example: + * + * Set the thresholds for sensor 2 to 50 C and 60 C: + * write 2 to [0x05] -- select temp sensor 2 + * write 0x7b to [0x06] -- C_TO_K(50) - EC_TEMP_SENSOR_OFFSET + * write 0x2 to [0x07] -- enable threshold 0 with this value + * write 0x85 to [0x06] -- C_TO_K(60) - EC_TEMP_SENSOR_OFFSET + * write 0x3 to [0x07] -- enable threshold 1 with this value + * + * Disable the 60 C threshold, leaving the 50 C threshold unchanged: + * write 2 to [0x05] -- select temp sensor 2 + * write 0x1 to [0x07] -- disable threshold 1 + */ + +/* DPTF battery charging current limit */ +#define EC_ACPI_MEM_CHARGING_LIMIT 0x08 + +/* Charging limit is specified in 64 mA steps */ +#define EC_ACPI_MEM_CHARGING_LIMIT_STEP_MA 64 +/* Value to disable DPTF battery charging limit */ +#define EC_ACPI_MEM_CHARGING_LIMIT_DISABLED 0xff + +/* + * Report device orientation + * Bits Definition + * 3:1 Device DPTF Profile Number (DDPN) + * 0 = Reserved for backward compatibility (indicates no valid + * profile number. Host should fall back to using TBMD). + * 1..7 = DPTF Profile number to indicate to host which table needs + * to be loaded. + * 0 Tablet Mode Device Indicator (TBMD) + */ +#define EC_ACPI_MEM_DEVICE_ORIENTATION 0x09 +#define EC_ACPI_MEM_TBMD_SHIFT 0 +#define EC_ACPI_MEM_TBMD_MASK 0x1 +#define EC_ACPI_MEM_DDPN_SHIFT 1 +#define EC_ACPI_MEM_DDPN_MASK 0x7 + +/* + * Report device features. Uses the same format as the host command, except: + * + * bit 0 (EC_FEATURE_LIMITED) changes meaning from "EC code has a limited set + * of features", which is of limited interest when the system is already + * interpreting ACPI bytecode, to "EC_FEATURES[0-7] is not supported". Since + * these are supported, it defaults to 0. + * This allows detecting the presence of this field since older versions of + * the EC codebase would simply return 0xff to that unknown address. Check + * FEATURES0 != 0xff (or FEATURES0[0] == 0) to make sure that the other bits + * are valid. + */ +#define EC_ACPI_MEM_DEVICE_FEATURES0 0x0a +#define EC_ACPI_MEM_DEVICE_FEATURES1 0x0b +#define EC_ACPI_MEM_DEVICE_FEATURES2 0x0c +#define EC_ACPI_MEM_DEVICE_FEATURES3 0x0d +#define EC_ACPI_MEM_DEVICE_FEATURES4 0x0e +#define EC_ACPI_MEM_DEVICE_FEATURES5 0x0f +#define EC_ACPI_MEM_DEVICE_FEATURES6 0x10 +#define EC_ACPI_MEM_DEVICE_FEATURES7 0x11 + +#define EC_ACPI_MEM_BATTERY_INDEX 0x12 + +/* + * USB Port Power. Each bit indicates whether the corresponding USB ports' power + * is enabled (1) or disabled (0). + * bit 0 USB port ID 0 + * ... + * bit 7 USB port ID 7 + */ +#define EC_ACPI_MEM_USB_PORT_POWER 0x13 + +/* + * ACPI addresses 0x20 - 0xff map to EC_MEMMAP offset 0x00 - 0xdf. This data + * is read-only from the AP. Added in EC_ACPI_MEM_VERSION 2. + */ +#define EC_ACPI_MEM_MAPPED_BEGIN 0x20 +#define EC_ACPI_MEM_MAPPED_SIZE 0xe0 + +/* Current version of ACPI memory address space */ +#define EC_ACPI_MEM_VERSION_CURRENT 2 + + /* * This header file is used in coreboot both in C and ACPI code. The ACPI code * is pre-processed to handle constants but the ASL compiler is unable to * handle actual C code so keep it separate. */ -#ifndef __ACPI__ + /* - * Define __packed if someone hasn't beat us to it. Linux kernel style - * checking prefers __packed over __attribute__((packed)). + * Attributes for EC request and response packets. Just defining __packed + * results in inefficient assembly code on ARM, if the structure is actually + * 32-bit aligned, as it should be for all buffers. + * + * Be very careful when adding these to existing structures. They will round + * up the structure size to the specified boundary. + * + * Also be very careful to make that if a structure is included in some other + * parent structure that the alignment will still be true given the packing of + * the parent structure. This is particularly important if the sub-structure + * will be passed as a pointer to another function, since that function will + * not know about the misaligment caused by the parent structure's packing. + * + * Also be very careful using __packed - particularly when nesting non-packed + * structures inside packed ones. In fact, DO NOT use __packed directly; + * always use one of these attributes. + * + * Once everything is annotated properly, the following search strings should + * not return ANY matches in this file other than right here: + * + * "__packed" - generates inefficient code; all sub-structs must also be packed + * + * "struct [^_]" - all structs should be annotated, except for structs that are + * members of other structs/unions (and their original declarations should be + * annotated). + */ + +/* + * Packed structures make no assumption about alignment, so they do inefficient + * byte-wise reads. */ -#ifndef __packed -#define __packed __attribute__((packed)) -#endif +#define __ec_align1 __packed +#define __ec_align2 __packed +#define __ec_align4 __packed +#define __ec_align_size1 __packed +#define __ec_align_offset1 __packed +#define __ec_align_offset2 __packed +#define __ec_todo_packed __packed +#define __ec_todo_unpacked + /* LPC command status byte masks */ /* EC has written a byte in the data register and host hasn't read it yet */ @@ -198,7 +439,7 @@ #define EC_LPC_STATUS_PROCESSING 0x04 /* Last write to EC was a command, not data */ #define EC_LPC_STATUS_LAST_CMD 0x08 -/* EC is in burst mode. Unsupported by Chrome EC, so this bit is never set */ +/* EC is in burst mode */ #define EC_LPC_STATUS_BURST_MODE 0x10 /* SCI event is pending (requesting SCI query) */ #define EC_LPC_STATUS_SCI_PENDING 0x20 @@ -214,7 +455,10 @@ #define EC_LPC_STATUS_BUSY_MASK \ (EC_LPC_STATUS_FROM_HOST | EC_LPC_STATUS_PROCESSING) -/* Host command response codes */ +/* + * Host command response codes (16-bit). Note that response codes should be + * stored in a uint16_t rather than directly in a value of this type. + */ enum ec_status { EC_RES_SUCCESS = 0, EC_RES_INVALID_COMMAND = 1, @@ -230,7 +474,13 @@ enum ec_status { EC_RES_OVERFLOW = 11, /* Table / data overflow */ EC_RES_INVALID_HEADER = 12, /* Header contains invalid data */ EC_RES_REQUEST_TRUNCATED = 13, /* Didn't get the entire request */ - EC_RES_RESPONSE_TOO_BIG = 14 /* Response was too big to handle */ + EC_RES_RESPONSE_TOO_BIG = 14, /* Response was too big to handle */ + EC_RES_BUS_ERROR = 15, /* Communications bus error */ + EC_RES_BUSY = 16, /* Up but too busy. Should retry */ + EC_RES_INVALID_HEADER_VERSION = 17, /* Header version invalid */ + EC_RES_INVALID_HEADER_CRC = 18, /* Header CRC invalid */ + EC_RES_INVALID_DATA_CRC = 19, /* Data CRC invalid */ + EC_RES_DUP_UNAVAILABLE = 20, /* Can't resend response */ }; /* @@ -250,7 +500,8 @@ enum host_event_code { EC_HOST_EVENT_BATTERY_CRITICAL = 7, EC_HOST_EVENT_BATTERY = 8, EC_HOST_EVENT_THERMAL_THRESHOLD = 9, - EC_HOST_EVENT_THERMAL_OVERLOAD = 10, + /* Event generated by a device attached to the EC */ + EC_HOST_EVENT_DEVICE = 10, EC_HOST_EVENT_THERMAL = 11, EC_HOST_EVENT_USB_CHARGER = 12, EC_HOST_EVENT_KEY_PRESSED = 13, @@ -277,15 +528,34 @@ enum host_event_code { EC_HOST_EVENT_HANG_DETECT = 20, /* Hang detect logic detected a hang and warm rebooted the AP */ EC_HOST_EVENT_HANG_REBOOT = 21, + /* PD MCU triggering host event */ EC_HOST_EVENT_PD_MCU = 22, - /* EC desires to change state of host-controlled USB mux */ - EC_HOST_EVENT_USB_MUX = 28, + /* Battery Status flags have changed */ + EC_HOST_EVENT_BATTERY_STATUS = 23, + + /* EC encountered a panic, triggering a reset */ + EC_HOST_EVENT_PANIC = 24, + + /* Keyboard fastboot combo has been pressed */ + EC_HOST_EVENT_KEYBOARD_FASTBOOT = 25, /* EC RTC event occurred */ EC_HOST_EVENT_RTC = 26, + /* Emulate MKBP event */ + EC_HOST_EVENT_MKBP = 27, + + /* EC desires to change state of host-controlled USB mux */ + EC_HOST_EVENT_USB_MUX = 28, + + /* TABLET/LAPTOP mode or detachable base attach/detach event */ + EC_HOST_EVENT_MODE_CHANGE = 29, + + /* Keyboard recovery combo with hardware reinitialization */ + EC_HOST_EVENT_KEYBOARD_RECOVERY_HW_REINIT = 30, + /* * The high bit of the event mask is not used as a host event code. If * it reads back as set, then the entire event mask should be @@ -296,7 +566,7 @@ enum host_event_code { EC_HOST_EVENT_INVALID = 32 }; /* Host event mask */ -#define EC_HOST_EVENT_MASK(event_code) (1UL << ((event_code) - 1)) +#define EC_HOST_EVENT_MASK(event_code) BIT_ULL((event_code) - 1) /** * struct ec_lpc_host_args - Arguments at EC_LPC_ADDR_HOST_ARGS @@ -311,7 +581,7 @@ struct ec_lpc_host_args { uint8_t command_version; uint8_t data_size; uint8_t checksum; -} __packed; +} __ec_align4; /* Flags for ec_lpc_host_args.flags */ /* @@ -321,7 +591,7 @@ struct ec_lpc_host_args { * If EC gets a command and this flag is not set, this is an old-style command. * Command version is 0 and params from host are at EC_LPC_ADDR_OLD_PARAM with * unknown length. EC must respond with an old-style response (that is, - * withouth setting EC_HOST_ARGS_FLAG_TO_HOST). + * without setting EC_HOST_ARGS_FLAG_TO_HOST). */ #define EC_HOST_ARGS_FLAG_FROM_HOST 0x01 /* @@ -482,7 +752,7 @@ struct ec_host_request { uint8_t command_version; uint8_t reserved; uint16_t data_len; -} __packed; +} __ec_align4; #define EC_HOST_RESPONSE_VERSION 3 @@ -501,18 +771,151 @@ struct ec_host_response { uint16_t result; uint16_t data_len; uint16_t reserved; -} __packed; +} __ec_align4; + +/*****************************************************************************/ + +/* + * Host command protocol V4. + * + * Packets always start with a request or response header. They are followed + * by data_len bytes of data. If the data_crc_present flag is set, the data + * bytes are followed by a CRC-8 of that data, using using x^8 + x^2 + x + 1 + * polynomial. + * + * Host algorithm when sending a request q: + * + * 101) tries_left=(some value, e.g. 3); + * 102) q.seq_num++ + * 103) q.seq_dup=0 + * 104) Calculate q.header_crc. + * 105) Send request q to EC. + * 106) Wait for response r. Go to 201 if received or 301 if timeout. + * + * 201) If r.struct_version != 4, go to 301. + * 202) If r.header_crc mismatches calculated CRC for r header, go to 301. + * 203) If r.data_crc_present and r.data_crc mismatches, go to 301. + * 204) If r.seq_num != q.seq_num, go to 301. + * 205) If r.seq_dup == q.seq_dup, return success. + * 207) If r.seq_dup == 1, go to 301. + * 208) Return error. + * + * 301) If --tries_left <= 0, return error. + * 302) If q.seq_dup == 1, go to 105. + * 303) q.seq_dup = 1 + * 304) Go to 104. + * + * EC algorithm when receiving a request q. + * EC has response buffer r, error buffer e. + * + * 101) If q.struct_version != 4, set e.result = EC_RES_INVALID_HEADER_VERSION + * and go to 301 + * 102) If q.header_crc mismatches calculated CRC, set e.result = + * EC_RES_INVALID_HEADER_CRC and go to 301 + * 103) If q.data_crc_present, calculate data CRC. If that mismatches the CRC + * byte at the end of the packet, set e.result = EC_RES_INVALID_DATA_CRC + * and go to 301. + * 104) If q.seq_dup == 0, go to 201. + * 105) If q.seq_num != r.seq_num, go to 201. + * 106) If q.seq_dup == r.seq_dup, go to 205, else go to 203. + * + * 201) Process request q into response r. + * 202) r.seq_num = q.seq_num + * 203) r.seq_dup = q.seq_dup + * 204) Calculate r.header_crc + * 205) If r.data_len > 0 and data is no longer available, set e.result = + * EC_RES_DUP_UNAVAILABLE and go to 301. + * 206) Send response r. + * + * 301) e.seq_num = q.seq_num + * 302) e.seq_dup = q.seq_dup + * 303) Calculate e.header_crc. + * 304) Send error response e. + */ + +/* Version 4 request from host */ +struct ec_host_request4 { + /* + * bits 0-3: struct_version: Structure version (=4) + * bit 4: is_response: Is response (=0) + * bits 5-6: seq_num: Sequence number + * bit 7: seq_dup: Sequence duplicate flag + */ + uint8_t fields0; + + /* + * bits 0-4: command_version: Command version + * bits 5-6: Reserved (set 0, ignore on read) + * bit 7: data_crc_present: Is data CRC present after data + */ + uint8_t fields1; + + /* Command code (EC_CMD_*) */ + uint16_t command; + + /* Length of data which follows this header (not including data CRC) */ + uint16_t data_len; + + /* Reserved (set 0, ignore on read) */ + uint8_t reserved; + + /* CRC-8 of above fields, using x^8 + x^2 + x + 1 polynomial */ + uint8_t header_crc; +} __ec_align4; + +/* Version 4 response from EC */ +struct ec_host_response4 { + /* + * bits 0-3: struct_version: Structure version (=4) + * bit 4: is_response: Is response (=1) + * bits 5-6: seq_num: Sequence number + * bit 7: seq_dup: Sequence duplicate flag + */ + uint8_t fields0; + + /* + * bits 0-6: Reserved (set 0, ignore on read) + * bit 7: data_crc_present: Is data CRC present after data + */ + uint8_t fields1; + + /* Result code (EC_RES_*) */ + uint16_t result; + + /* Length of data which follows this header (not including data CRC) */ + uint16_t data_len; + + /* Reserved (set 0, ignore on read) */ + uint8_t reserved; + + /* CRC-8 of above fields, using x^8 + x^2 + x + 1 polynomial */ + uint8_t header_crc; +} __ec_align4; + +/* Fields in fields0 byte */ +#define EC_PACKET4_0_STRUCT_VERSION_MASK 0x0f +#define EC_PACKET4_0_IS_RESPONSE_MASK 0x10 +#define EC_PACKET4_0_SEQ_NUM_SHIFT 5 +#define EC_PACKET4_0_SEQ_NUM_MASK 0x60 +#define EC_PACKET4_0_SEQ_DUP_MASK 0x80 + +/* Fields in fields1 byte */ +#define EC_PACKET4_1_COMMAND_VERSION_MASK 0x1f /* (request only) */ +#define EC_PACKET4_1_DATA_CRC_PRESENT_MASK 0x80 /*****************************************************************************/ /* * Notes on commands: * * Each command is an 16-bit command value. Commands which take params or - * return response data specify structs for that data. If no struct is + * return response data specify structures for that data. If no structure is * specified, the command does not input or output data, respectively. * Parameter/response length is implicit in the structs. Some underlying * communication protocols (I2C, SPI) may add length or checksum headers, but * those are implementation-dependent and not defined here. + * + * All commands MUST be #defined to be 4-digit UPPER CASE hex values + * (e.g., 0x00AB, not 0xab) for CONFIG_HOSTCMD_SECTION_SORTED to work. */ /*****************************************************************************/ @@ -522,7 +925,7 @@ struct ec_host_response { * Get protocol version, used to deal with non-backward compatible protocol * changes. */ -#define EC_CMD_PROTO_VERSION 0x00 +#define EC_CMD_PROTO_VERSION 0x0000 /** * struct ec_response_proto_version - Response to the proto version command. @@ -530,13 +933,13 @@ struct ec_host_response { */ struct ec_response_proto_version { uint32_t version; -} __packed; +} __ec_align4; /* * Hello. This is a simple command to test the EC is responsive to * commands. */ -#define EC_CMD_HELLO 0x01 +#define EC_CMD_HELLO 0x0001 /** * struct ec_params_hello - Parameters to the hello command. @@ -544,7 +947,7 @@ struct ec_response_proto_version { */ struct ec_params_hello { uint32_t in_data; -} __packed; +} __ec_align4; /** * struct ec_response_hello - Response to the hello command. @@ -552,10 +955,10 @@ struct ec_params_hello { */ struct ec_response_hello { uint32_t out_data; -} __packed; +} __ec_align4; /* Get version number */ -#define EC_CMD_GET_VERSION 0x02 +#define EC_CMD_GET_VERSION 0x0002 enum ec_current_image { EC_IMAGE_UNKNOWN = 0, @@ -575,10 +978,10 @@ struct ec_response_get_version { char version_string_rw[32]; char reserved[32]; uint32_t current_image; -} __packed; +} __ec_align4; /* Read test */ -#define EC_CMD_READ_TEST 0x03 +#define EC_CMD_READ_TEST 0x0003 /** * struct ec_params_read_test - Parameters for the read test command. @@ -588,7 +991,7 @@ struct ec_response_get_version { struct ec_params_read_test { uint32_t offset; uint32_t size; -} __packed; +} __ec_align4; /** * struct ec_response_read_test - Response to the read test command. @@ -596,17 +999,17 @@ struct ec_params_read_test { */ struct ec_response_read_test { uint32_t data[32]; -} __packed; +} __ec_align4; /* * Get build information * * Response is null-terminated string. */ -#define EC_CMD_GET_BUILD_INFO 0x04 +#define EC_CMD_GET_BUILD_INFO 0x0004 /* Get chip info */ -#define EC_CMD_GET_CHIP_INFO 0x05 +#define EC_CMD_GET_CHIP_INFO 0x0005 /** * struct ec_response_get_chip_info - Response to the get chip info command. @@ -618,10 +1021,10 @@ struct ec_response_get_chip_info { char vendor[32]; char name[32]; char revision[32]; -} __packed; +} __ec_align4; /* Get board HW version */ -#define EC_CMD_GET_BOARD_VERSION 0x06 +#define EC_CMD_GET_BOARD_VERSION 0x0006 /** * struct ec_response_board_version - Response to the board version command. @@ -629,7 +1032,7 @@ struct ec_response_get_chip_info { */ struct ec_response_board_version { uint16_t board_version; -} __packed; +} __ec_align2; /* * Read memory-mapped data. @@ -639,7 +1042,7 @@ struct ec_response_board_version { * * Response is params.size bytes of data. */ -#define EC_CMD_READ_MEMMAP 0x07 +#define EC_CMD_READ_MEMMAP 0x0007 /** * struct ec_params_read_memmap - Parameters for the read memory map command. @@ -649,10 +1052,10 @@ struct ec_response_board_version { struct ec_params_read_memmap { uint8_t offset; uint8_t size; -} __packed; +} __ec_align1; /* Read versions supported for a command */ -#define EC_CMD_GET_CMD_VERSIONS 0x08 +#define EC_CMD_GET_CMD_VERSIONS 0x0008 /** * struct ec_params_get_cmd_versions - Parameters for the get command versions. @@ -660,7 +1063,7 @@ struct ec_params_read_memmap { */ struct ec_params_get_cmd_versions { uint8_t cmd; -} __packed; +} __ec_align1; /** * struct ec_params_get_cmd_versions_v1 - Parameters for the get command @@ -669,7 +1072,7 @@ struct ec_params_get_cmd_versions { */ struct ec_params_get_cmd_versions_v1 { uint16_t cmd; -} __packed; +} __ec_align2; /** * struct ec_response_get_cmd_version - Response to the get command versions. @@ -678,20 +1081,20 @@ struct ec_params_get_cmd_versions_v1 { */ struct ec_response_get_cmd_versions { uint32_t version_mask; -} __packed; +} __ec_align4; /* - * Check EC communcations status (busy). This is needed on i2c/spi but not + * Check EC communications status (busy). This is needed on i2c/spi but not * on lpc since it has its own out-of-band busy indicator. * * lpc must read the status from the command register. Attempting this on * lpc will overwrite the args/parameter space and corrupt its data. */ -#define EC_CMD_GET_COMMS_STATUS 0x09 +#define EC_CMD_GET_COMMS_STATUS 0x0009 /* Avoid using ec_status which is for return values */ enum ec_comms_status { - EC_COMMS_STATUS_PROCESSING = 1 << 0, /* Processing cmd */ + EC_COMMS_STATUS_PROCESSING = BIT(0), /* Processing cmd */ }; /** @@ -701,29 +1104,29 @@ enum ec_comms_status { */ struct ec_response_get_comms_status { uint32_t flags; /* Mask of enum ec_comms_status */ -} __packed; +} __ec_align4; /* Fake a variety of responses, purely for testing purposes. */ -#define EC_CMD_TEST_PROTOCOL 0x0a +#define EC_CMD_TEST_PROTOCOL 0x000A /* Tell the EC what to send back to us. */ struct ec_params_test_protocol { uint32_t ec_result; uint32_t ret_len; uint8_t buf[32]; -} __packed; +} __ec_align4; /* Here it comes... */ struct ec_response_test_protocol { uint8_t buf[32]; -} __packed; +} __ec_align4; -/* Get prococol information */ -#define EC_CMD_GET_PROTOCOL_INFO 0x0b +/* Get protocol information */ +#define EC_CMD_GET_PROTOCOL_INFO 0x000B /* Flags for ec_response_get_protocol_info.flags */ /* EC_RES_IN_PROGRESS may be returned if a command is slow */ -#define EC_PROTOCOL_INFO_IN_PROGRESS_SUPPORTED (1 << 0) +#define EC_PROTOCOL_INFO_IN_PROGRESS_SUPPORTED BIT(0) /** * struct ec_response_get_protocol_info - Response to the get protocol info. @@ -739,7 +1142,7 @@ struct ec_response_get_protocol_info { uint16_t max_request_packet_size; uint16_t max_response_packet_size; uint32_t flags; -} __packed; +} __ec_align4; /*****************************************************************************/ @@ -757,19 +1160,19 @@ struct ec_response_get_protocol_info { struct ec_params_get_set_value { uint32_t flags; uint32_t value; -} __packed; +} __ec_align4; struct ec_response_get_set_value { uint32_t flags; uint32_t value; -} __packed; +} __ec_align4; -/* More than one command can use these structs to get/set paramters. */ -#define EC_CMD_GSV_PAUSE_IN_S5 0x0c +/* More than one command can use these structs to get/set parameters. */ +#define EC_CMD_GSV_PAUSE_IN_S5 0x000C /*****************************************************************************/ /* List the features supported by the firmware */ -#define EC_CMD_GET_FEATURES 0x0d +#define EC_CMD_GET_FEATURES 0x000D /* Supported features */ enum ec_feature_code { @@ -876,24 +1279,36 @@ enum ec_feature_code { EC_FEATURE_REFINED_TABLET_MODE_HYSTERESIS = 37, /* EC supports audio codec. */ EC_FEATURE_AUDIO_CODEC = 38, - /* EC Supports SCP. */ + /* The MCU is a System Companion Processor (SCP). */ EC_FEATURE_SCP = 39, /* The MCU is an Integrated Sensor Hub */ EC_FEATURE_ISH = 40, }; -#define EC_FEATURE_MASK_0(event_code) (1UL << (event_code % 32)) -#define EC_FEATURE_MASK_1(event_code) (1UL << (event_code - 32)) +#define EC_FEATURE_MASK_0(event_code) BIT(event_code % 32) +#define EC_FEATURE_MASK_1(event_code) BIT(event_code - 32) struct ec_response_get_features { uint32_t flags[2]; -} __packed; +} __ec_align4; + +/*****************************************************************************/ +/* Get the board's SKU ID from EC */ +#define EC_CMD_GET_SKU_ID 0x000E + +/* Set SKU ID from AP */ +#define EC_CMD_SET_SKU_ID 0x000F + +struct ec_sku_id_info { + uint32_t sku_id; +} __ec_align4; /*****************************************************************************/ /* Flash commands */ /* Get flash info */ -#define EC_CMD_FLASH_INFO 0x10 +#define EC_CMD_FLASH_INFO 0x0010 +#define EC_VER_FLASH_INFO 2 /** * struct ec_response_flash_info - Response to the flash info command. @@ -912,11 +1327,22 @@ struct ec_response_flash_info { uint32_t write_block_size; uint32_t erase_block_size; uint32_t protect_block_size; -} __packed; +} __ec_align4; -/* Flags for version 1+ flash info command */ -/* EC flash erases bits to 0 instead of 1 */ -#define EC_FLASH_INFO_ERASE_TO_0 (1 << 0) +/* + * Flags for version 1+ flash info command + * EC flash erases bits to 0 instead of 1. + */ +#define EC_FLASH_INFO_ERASE_TO_0 BIT(0) + +/* + * Flash must be selected for read/write/erase operations to succeed. This may + * be necessary on a chip where write/erase can be corrupted by other board + * activity, or where the chip needs to enable some sort of programming voltage, + * or where the read/write/erase operations require cleanly suspending other + * chip functionality. + */ +#define EC_FLASH_INFO_SELECT_REQUIRED BIT(1) /** * struct ec_response_flash_info_1 - Response to the flash info v1 command. @@ -938,7 +1364,14 @@ struct ec_response_flash_info { * fields following. * * gcc anonymous structs don't seem to get along with the __packed directive; - * if they did we'd define the version 0 struct as a sub-struct of this one. + * if they did we'd define the version 0 structure as a sub-structure of this + * one. + * + * Version 2 supports flash banks of different sizes: + * The caller specified the number of banks it has preallocated + * (num_banks_desc) + * The EC returns the number of banks describing the flash memory. + * It adds banks descriptions up to num_banks_desc. */ struct ec_response_flash_info_1 { /* Version 0 fields; see above for description */ @@ -950,14 +1383,50 @@ struct ec_response_flash_info_1 { /* Version 1 adds these fields: */ uint32_t write_ideal_size; uint32_t flags; -} __packed; +} __ec_align4; + +struct ec_params_flash_info_2 { + /* Number of banks to describe */ + uint16_t num_banks_desc; + /* Reserved; set 0; ignore on read */ + uint8_t reserved[2]; +} __ec_align4; + +struct ec_flash_bank { + /* Number of sector is in this bank. */ + uint16_t count; + /* Size in power of 2 of each sector (8 --> 256 bytes) */ + uint8_t size_exp; + /* Minimal write size for the sectors in this bank */ + uint8_t write_size_exp; + /* Erase size for the sectors in this bank */ + uint8_t erase_size_exp; + /* Size for write protection, usually identical to erase size. */ + uint8_t protect_size_exp; + /* Reserved; set 0; ignore on read */ + uint8_t reserved[2]; +}; + +struct ec_response_flash_info_2 { + /* Total flash in the EC. */ + uint32_t flash_size; + /* Flags; see EC_FLASH_INFO_* */ + uint32_t flags; + /* Maximum size to use to send data to write to the EC. */ + uint32_t write_ideal_size; + /* Number of banks present in the EC. */ + uint16_t num_banks_total; + /* Number of banks described in banks array. */ + uint16_t num_banks_desc; + struct ec_flash_bank banks[0]; +} __ec_align4; /* * Read flash * * Response is params.size bytes of data. */ -#define EC_CMD_FLASH_READ 0x11 +#define EC_CMD_FLASH_READ 0x0011 /** * struct ec_params_flash_read - Parameters for the flash read command. @@ -967,10 +1436,10 @@ struct ec_response_flash_info_1 { struct ec_params_flash_read { uint32_t offset; uint32_t size; -} __packed; +} __ec_align4; /* Write flash */ -#define EC_CMD_FLASH_WRITE 0x12 +#define EC_CMD_FLASH_WRITE 0x0012 #define EC_VER_FLASH_WRITE 1 /* Version 0 of the flash command supported only 64 bytes of data */ @@ -985,20 +1454,57 @@ struct ec_params_flash_write { uint32_t offset; uint32_t size; /* Followed by data to write */ -} __packed; +} __ec_align4; /* Erase flash */ -#define EC_CMD_FLASH_ERASE 0x13 +#define EC_CMD_FLASH_ERASE 0x0013 /** - * struct ec_params_flash_erase - Parameters for the flash erase command. + * struct ec_params_flash_erase - Parameters for the flash erase command, v0. * @offset: Byte offset to erase. * @size: Size to erase in bytes. */ struct ec_params_flash_erase { uint32_t offset; uint32_t size; -} __packed; +} __ec_align4; + +/* + * v1 add async erase: + * subcommands can returns: + * EC_RES_SUCCESS : erased (see ERASE_SECTOR_ASYNC case below). + * EC_RES_INVALID_PARAM : offset/size are not aligned on a erase boundary. + * EC_RES_ERROR : other errors. + * EC_RES_BUSY : an existing erase operation is in progress. + * EC_RES_ACCESS_DENIED: Trying to erase running image. + * + * When ERASE_SECTOR_ASYNC returns EC_RES_SUCCESS, the operation is just + * properly queued. The user must call ERASE_GET_RESULT subcommand to get + * the proper result. + * When ERASE_GET_RESULT returns EC_RES_BUSY, the caller must wait and send + * ERASE_GET_RESULT again to get the result of ERASE_SECTOR_ASYNC. + * ERASE_GET_RESULT command may timeout on EC where flash access is not + * permitted while erasing. (For instance, STM32F4). + */ +enum ec_flash_erase_cmd { + FLASH_ERASE_SECTOR, /* Erase and wait for result */ + FLASH_ERASE_SECTOR_ASYNC, /* Erase and return immediately. */ + FLASH_ERASE_GET_RESULT, /* Ask for last erase result */ +}; + +/** + * struct ec_params_flash_erase_v1 - Parameters for the flash erase command, v1. + * @cmd: One of ec_flash_erase_cmd. + * @reserved: Pad byte; currently always contains 0. + * @flag: No flags defined yet; set to 0. + * @params: Same as v0 parameters. + */ +struct ec_params_flash_erase_v1 { + uint8_t cmd; + uint8_t reserved; + uint16_t flag; + struct ec_params_flash_erase params; +} __ec_align4; /* * Get/set flash protection. @@ -1010,31 +1516,40 @@ struct ec_params_flash_erase { * * If mask=0, simply returns the current flags state. */ -#define EC_CMD_FLASH_PROTECT 0x15 +#define EC_CMD_FLASH_PROTECT 0x0015 #define EC_VER_FLASH_PROTECT 1 /* Command version 1 */ /* Flags for flash protection */ /* RO flash code protected when the EC boots */ -#define EC_FLASH_PROTECT_RO_AT_BOOT (1 << 0) +#define EC_FLASH_PROTECT_RO_AT_BOOT BIT(0) /* * RO flash code protected now. If this bit is set, at-boot status cannot * be changed. */ -#define EC_FLASH_PROTECT_RO_NOW (1 << 1) +#define EC_FLASH_PROTECT_RO_NOW BIT(1) /* Entire flash code protected now, until reboot. */ -#define EC_FLASH_PROTECT_ALL_NOW (1 << 2) +#define EC_FLASH_PROTECT_ALL_NOW BIT(2) /* Flash write protect GPIO is asserted now */ -#define EC_FLASH_PROTECT_GPIO_ASSERTED (1 << 3) +#define EC_FLASH_PROTECT_GPIO_ASSERTED BIT(3) /* Error - at least one bank of flash is stuck locked, and cannot be unlocked */ -#define EC_FLASH_PROTECT_ERROR_STUCK (1 << 4) +#define EC_FLASH_PROTECT_ERROR_STUCK BIT(4) /* * Error - flash protection is in inconsistent state. At least one bank of * flash which should be protected is not protected. Usually fixed by * re-requesting the desired flags, or by a hard reset if that fails. */ -#define EC_FLASH_PROTECT_ERROR_INCONSISTENT (1 << 5) -/* Entile flash code protected when the EC boots */ -#define EC_FLASH_PROTECT_ALL_AT_BOOT (1 << 6) +#define EC_FLASH_PROTECT_ERROR_INCONSISTENT BIT(5) +/* Entire flash code protected when the EC boots */ +#define EC_FLASH_PROTECT_ALL_AT_BOOT BIT(6) +/* RW flash code protected when the EC boots */ +#define EC_FLASH_PROTECT_RW_AT_BOOT BIT(7) +/* RW flash code protected now. */ +#define EC_FLASH_PROTECT_RW_NOW BIT(8) +/* Rollback information flash region protected when the EC boots */ +#define EC_FLASH_PROTECT_ROLLBACK_AT_BOOT BIT(9) +/* Rollback information flash region protected now */ +#define EC_FLASH_PROTECT_ROLLBACK_NOW BIT(10) + /** * struct ec_params_flash_protect - Parameters for the flash protect command. @@ -1044,7 +1559,7 @@ struct ec_params_flash_erase { struct ec_params_flash_protect { uint32_t mask; uint32_t flags; -} __packed; +} __ec_align4; /** * struct ec_response_flash_protect - Response to the flash protect command. @@ -1059,7 +1574,7 @@ struct ec_response_flash_protect { uint32_t flags; uint32_t valid_flags; uint32_t writable_flags; -} __packed; +} __ec_align4; /* * Note: commands 0x14 - 0x19 version 0 were old commands to get/set flash @@ -1067,22 +1582,37 @@ struct ec_response_flash_protect { */ /* Get the region offset/size */ -#define EC_CMD_FLASH_REGION_INFO 0x16 +#define EC_CMD_FLASH_REGION_INFO 0x0016 #define EC_VER_FLASH_REGION_INFO 1 enum ec_flash_region { /* Region which holds read-only EC image */ EC_FLASH_REGION_RO = 0, - /* Region which holds rewritable EC image */ - EC_FLASH_REGION_RW, + /* + * Region which holds active RW image. 'Active' is different from + * 'running'. Active means 'scheduled-to-run'. Since RO image always + * scheduled to run, active/non-active applies only to RW images (for + * the same reason 'update' applies only to RW images. It's a state of + * an image on a flash. Running image can be RO, RW_A, RW_B but active + * image can only be RW_A or RW_B. In recovery mode, an active RW image + * doesn't enter 'running' state but it's still active on a flash. + */ + EC_FLASH_REGION_ACTIVE, /* * Region which should be write-protected in the factory (a superset of * EC_FLASH_REGION_RO) */ EC_FLASH_REGION_WP_RO, + /* Region which holds updatable (non-active) RW image */ + EC_FLASH_REGION_UPDATE, /* Number of regions */ EC_FLASH_REGION_COUNT, }; +/* + * 'RW' is vague if there are multiple RW images; we mean the active one, + * so the old constant is deprecated. + */ +#define EC_FLASH_REGION_RW EC_FLASH_REGION_ACTIVE /** * struct ec_params_flash_region_info - Parameters for the flash region info @@ -1091,15 +1621,15 @@ enum ec_flash_region { */ struct ec_params_flash_region_info { uint32_t region; -} __packed; +} __ec_align4; struct ec_response_flash_region_info { uint32_t offset; uint32_t size; -} __packed; +} __ec_align4; /* Read/write VbNvContext */ -#define EC_CMD_VBNV_CONTEXT 0x17 +#define EC_CMD_VBNV_CONTEXT 0x0017 #define EC_VER_VBNV_CONTEXT 1 #define EC_VBNV_BLOCK_SIZE 16 @@ -1111,52 +1641,99 @@ enum ec_vbnvcontext_op { struct ec_params_vbnvcontext { uint32_t op; uint8_t block[EC_VBNV_BLOCK_SIZE]; -} __packed; +} __ec_align4; struct ec_response_vbnvcontext { uint8_t block[EC_VBNV_BLOCK_SIZE]; -} __packed; +} __ec_align4; + + +/* Get SPI flash information */ +#define EC_CMD_FLASH_SPI_INFO 0x0018 + +struct ec_response_flash_spi_info { + /* JEDEC info from command 0x9F (manufacturer, memory type, size) */ + uint8_t jedec[3]; + + /* Pad byte; currently always contains 0 */ + uint8_t reserved0; + + /* Manufacturer / device ID from command 0x90 */ + uint8_t mfr_dev_id[2]; + + /* Status registers from command 0x05 and 0x35 */ + uint8_t sr1, sr2; +} __ec_align1; + + +/* Select flash during flash operations */ +#define EC_CMD_FLASH_SELECT 0x0019 + +/** + * struct ec_params_flash_select - Parameters for the flash select command. + * @select: 1 to select flash, 0 to deselect flash + */ +struct ec_params_flash_select { + uint8_t select; +} __ec_align4; + /*****************************************************************************/ /* PWM commands */ /* Get fan target RPM */ -#define EC_CMD_PWM_GET_FAN_TARGET_RPM 0x20 +#define EC_CMD_PWM_GET_FAN_TARGET_RPM 0x0020 struct ec_response_pwm_get_fan_rpm { uint32_t rpm; -} __packed; +} __ec_align4; /* Set target fan RPM */ -#define EC_CMD_PWM_SET_FAN_TARGET_RPM 0x21 +#define EC_CMD_PWM_SET_FAN_TARGET_RPM 0x0021 + +/* Version 0 of input params */ +struct ec_params_pwm_set_fan_target_rpm_v0 { + uint32_t rpm; +} __ec_align4; -struct ec_params_pwm_set_fan_target_rpm { +/* Version 1 of input params */ +struct ec_params_pwm_set_fan_target_rpm_v1 { uint32_t rpm; -} __packed; + uint8_t fan_idx; +} __ec_align_size1; /* Get keyboard backlight */ -#define EC_CMD_PWM_GET_KEYBOARD_BACKLIGHT 0x22 +/* OBSOLETE - Use EC_CMD_PWM_SET_DUTY */ +#define EC_CMD_PWM_GET_KEYBOARD_BACKLIGHT 0x0022 struct ec_response_pwm_get_keyboard_backlight { uint8_t percent; uint8_t enabled; -} __packed; +} __ec_align1; /* Set keyboard backlight */ -#define EC_CMD_PWM_SET_KEYBOARD_BACKLIGHT 0x23 +/* OBSOLETE - Use EC_CMD_PWM_SET_DUTY */ +#define EC_CMD_PWM_SET_KEYBOARD_BACKLIGHT 0x0023 struct ec_params_pwm_set_keyboard_backlight { uint8_t percent; -} __packed; +} __ec_align1; /* Set target fan PWM duty cycle */ -#define EC_CMD_PWM_SET_FAN_DUTY 0x24 +#define EC_CMD_PWM_SET_FAN_DUTY 0x0024 + +/* Version 0 of input params */ +struct ec_params_pwm_set_fan_duty_v0 { + uint32_t percent; +} __ec_align4; -struct ec_params_pwm_set_fan_duty { +/* Version 1 of input params */ +struct ec_params_pwm_set_fan_duty_v1 { uint32_t percent; -} __packed; + uint8_t fan_idx; +} __ec_align_size1; -#define EC_CMD_PWM_SET_DUTY 0x25 +#define EC_CMD_PWM_SET_DUTY 0x0025 /* 16 bit duty cycle, 0xffff = 100% */ #define EC_PWM_MAX_DUTY 0xffff @@ -1174,18 +1751,18 @@ struct ec_params_pwm_set_duty { uint16_t duty; /* Duty cycle, EC_PWM_MAX_DUTY = 100% */ uint8_t pwm_type; /* ec_pwm_type */ uint8_t index; /* Type-specific index, or 0 if unique */ -} __packed; +} __ec_align4; -#define EC_CMD_PWM_GET_DUTY 0x26 +#define EC_CMD_PWM_GET_DUTY 0x0026 struct ec_params_pwm_get_duty { uint8_t pwm_type; /* ec_pwm_type */ uint8_t index; /* Type-specific index, or 0 if unique */ -} __packed; +} __ec_align1; struct ec_response_pwm_get_duty { uint16_t duty; /* Duty cycle, EC_PWM_MAX_DUTY = 100% */ -} __packed; +} __ec_align2; /*****************************************************************************/ /* @@ -1194,11 +1771,11 @@ struct ec_response_pwm_get_duty { * into a subcommand. We'll make separate structs for subcommands with * different input args, so that we know how much to expect. */ -#define EC_CMD_LIGHTBAR_CMD 0x28 +#define EC_CMD_LIGHTBAR_CMD 0x0028 struct rgb_s { uint8_t r, g, b; -}; +} __ec_todo_unpacked; #define LB_BATTERY_LEVELS 4 @@ -1238,7 +1815,7 @@ struct lightbar_params_v0 { /* Color palette */ struct rgb_s color[8]; /* 0-3 are Google colors */ -} __packed; +} __ec_todo_packed; struct lightbar_params_v1 { /* Timing */ @@ -1251,7 +1828,10 @@ struct lightbar_params_v1 { int32_t s3_sleep_for; int32_t s3_ramp_up; int32_t s3_ramp_down; + int32_t s5_ramp_up; + int32_t s5_ramp_down; int32_t tap_tick_delay; + int32_t tap_gate_delay; int32_t tap_display_time; /* Tap-for-battery params */ @@ -1279,84 +1859,182 @@ struct lightbar_params_v1 { uint8_t s0_idx[2][LB_BATTERY_LEVELS]; /* AP is running */ uint8_t s3_idx[2][LB_BATTERY_LEVELS]; /* AP is sleeping */ + /* s5: single color pulse on inhibited power-up */ + uint8_t s5_idx; + /* Color palette */ struct rgb_s color[8]; /* 0-3 are Google colors */ -} __packed; +} __ec_todo_packed; + +/* Lightbar command params v2 + * crbug.com/467716 + * + * lightbar_parms_v1 was too big for i2c, therefore in v2, we split them up by + * logical groups to make it more manageable ( < 120 bytes). + * + * NOTE: Each of these groups must be less than 120 bytes. + */ + +struct lightbar_params_v2_timing { + /* Timing */ + int32_t google_ramp_up; + int32_t google_ramp_down; + int32_t s3s0_ramp_up; + int32_t s0_tick_delay[2]; /* AC=0/1 */ + int32_t s0a_tick_delay[2]; /* AC=0/1 */ + int32_t s0s3_ramp_down; + int32_t s3_sleep_for; + int32_t s3_ramp_up; + int32_t s3_ramp_down; + int32_t s5_ramp_up; + int32_t s5_ramp_down; + int32_t tap_tick_delay; + int32_t tap_gate_delay; + int32_t tap_display_time; +} __ec_todo_packed; + +struct lightbar_params_v2_tap { + /* Tap-for-battery params */ + uint8_t tap_pct_red; + uint8_t tap_pct_green; + uint8_t tap_seg_min_on; + uint8_t tap_seg_max_on; + uint8_t tap_seg_osc; + uint8_t tap_idx[3]; +} __ec_todo_packed; + +struct lightbar_params_v2_oscillation { + /* Oscillation */ + uint8_t osc_min[2]; /* AC=0/1 */ + uint8_t osc_max[2]; /* AC=0/1 */ + uint8_t w_ofs[2]; /* AC=0/1 */ +} __ec_todo_packed; + +struct lightbar_params_v2_brightness { + /* Brightness limits based on the backlight and AC. */ + uint8_t bright_bl_off_fixed[2]; /* AC=0/1 */ + uint8_t bright_bl_on_min[2]; /* AC=0/1 */ + uint8_t bright_bl_on_max[2]; /* AC=0/1 */ +} __ec_todo_packed; + +struct lightbar_params_v2_thresholds { + /* Battery level thresholds */ + uint8_t battery_threshold[LB_BATTERY_LEVELS - 1]; +} __ec_todo_packed; -/* Lightbar program */ +struct lightbar_params_v2_colors { + /* Map [AC][battery_level] to color index */ + uint8_t s0_idx[2][LB_BATTERY_LEVELS]; /* AP is running */ + uint8_t s3_idx[2][LB_BATTERY_LEVELS]; /* AP is sleeping */ + + /* s5: single color pulse on inhibited power-up */ + uint8_t s5_idx; + + /* Color palette */ + struct rgb_s color[8]; /* 0-3 are Google colors */ +} __ec_todo_packed; + +/* Lightbar program. */ #define EC_LB_PROG_LEN 192 struct lightbar_program { uint8_t size; uint8_t data[EC_LB_PROG_LEN]; -}; +} __ec_todo_unpacked; struct ec_params_lightbar { uint8_t cmd; /* Command (see enum lightbar_command) */ union { - struct { - /* no args */ - } dump, off, on, init, get_seq, get_params_v0, get_params_v1, - version, get_brightness, get_demo, suspend, resume; + /* + * The following commands have no args: + * + * dump, off, on, init, get_seq, get_params_v0, get_params_v1, + * version, get_brightness, get_demo, suspend, resume, + * get_params_v2_timing, get_params_v2_tap, get_params_v2_osc, + * get_params_v2_bright, get_params_v2_thlds, + * get_params_v2_colors + * + * Don't use an empty struct, because C++ hates that. + */ - struct { + struct __ec_todo_unpacked { uint8_t num; } set_brightness, seq, demo; - struct { + struct __ec_todo_unpacked { uint8_t ctrl, reg, value; } reg; - struct { + struct __ec_todo_unpacked { uint8_t led, red, green, blue; } set_rgb; - struct { + struct __ec_todo_unpacked { uint8_t led; } get_rgb; - struct { + struct __ec_todo_unpacked { uint8_t enable; } manual_suspend_ctrl; struct lightbar_params_v0 set_params_v0; struct lightbar_params_v1 set_params_v1; + + struct lightbar_params_v2_timing set_v2par_timing; + struct lightbar_params_v2_tap set_v2par_tap; + struct lightbar_params_v2_oscillation set_v2par_osc; + struct lightbar_params_v2_brightness set_v2par_bright; + struct lightbar_params_v2_thresholds set_v2par_thlds; + struct lightbar_params_v2_colors set_v2par_colors; + struct lightbar_program set_program; }; -} __packed; +} __ec_todo_packed; struct ec_response_lightbar { union { - struct { - struct { + struct __ec_todo_unpacked { + struct __ec_todo_unpacked { uint8_t reg; uint8_t ic0; uint8_t ic1; } vals[23]; } dump; - struct { + struct __ec_todo_unpacked { uint8_t num; } get_seq, get_brightness, get_demo; struct lightbar_params_v0 get_params_v0; struct lightbar_params_v1 get_params_v1; - struct { + + struct lightbar_params_v2_timing get_params_v2_timing; + struct lightbar_params_v2_tap get_params_v2_tap; + struct lightbar_params_v2_oscillation get_params_v2_osc; + struct lightbar_params_v2_brightness get_params_v2_bright; + struct lightbar_params_v2_thresholds get_params_v2_thlds; + struct lightbar_params_v2_colors get_params_v2_colors; + + struct __ec_todo_unpacked { uint32_t num; uint32_t flags; } version; - struct { + struct __ec_todo_unpacked { uint8_t red, green, blue; } get_rgb; - struct { - /* no return params */ - } off, on, init, set_brightness, seq, reg, set_rgb, - demo, set_params_v0, set_params_v1, - set_program, manual_suspend_ctrl, suspend, resume; + /* + * The following commands have no response: + * + * off, on, init, set_brightness, seq, reg, set_rgb, demo, + * set_params_v0, set_params_v1, set_program, + * manual_suspend_ctrl, suspend, resume, set_v2par_timing, + * set_v2par_tap, set_v2par_osc, set_v2par_bright, + * set_v2par_thlds, set_v2par_colors + */ }; -} __packed; +} __ec_todo_packed; /* Lightbar commands */ enum lightbar_command { @@ -1382,13 +2060,25 @@ enum lightbar_command { LIGHTBAR_CMD_MANUAL_SUSPEND_CTRL = 19, LIGHTBAR_CMD_SUSPEND = 20, LIGHTBAR_CMD_RESUME = 21, + LIGHTBAR_CMD_GET_PARAMS_V2_TIMING = 22, + LIGHTBAR_CMD_SET_PARAMS_V2_TIMING = 23, + LIGHTBAR_CMD_GET_PARAMS_V2_TAP = 24, + LIGHTBAR_CMD_SET_PARAMS_V2_TAP = 25, + LIGHTBAR_CMD_GET_PARAMS_V2_OSCILLATION = 26, + LIGHTBAR_CMD_SET_PARAMS_V2_OSCILLATION = 27, + LIGHTBAR_CMD_GET_PARAMS_V2_BRIGHTNESS = 28, + LIGHTBAR_CMD_SET_PARAMS_V2_BRIGHTNESS = 29, + LIGHTBAR_CMD_GET_PARAMS_V2_THRESHOLDS = 30, + LIGHTBAR_CMD_SET_PARAMS_V2_THRESHOLDS = 31, + LIGHTBAR_CMD_GET_PARAMS_V2_COLORS = 32, + LIGHTBAR_CMD_SET_PARAMS_V2_COLORS = 33, LIGHTBAR_NUM_CMDS }; /*****************************************************************************/ /* LED control commands */ -#define EC_CMD_LED_CONTROL 0x29 +#define EC_CMD_LED_CONTROL 0x0029 enum ec_led_id { /* LED to indicate battery state of charge */ @@ -1400,13 +2090,21 @@ enum ec_led_id { EC_LED_ID_POWER_LED, /* LED on power adapter or its plug */ EC_LED_ID_ADAPTER_LED, + /* LED to indicate left side */ + EC_LED_ID_LEFT_LED, + /* LED to indicate right side */ + EC_LED_ID_RIGHT_LED, + /* LED to indicate recovery mode with HW_REINIT */ + EC_LED_ID_RECOVERY_HW_REINIT_LED, + /* LED to indicate sysrq debug mode. */ + EC_LED_ID_SYSRQ_DEBUG_LED, EC_LED_ID_COUNT }; /* LED control flags */ -#define EC_LED_FLAGS_QUERY (1 << 0) /* Query LED capability only */ -#define EC_LED_FLAGS_AUTO (1 << 1) /* Switch LED back to automatic control */ +#define EC_LED_FLAGS_QUERY BIT(0) /* Query LED capability only */ +#define EC_LED_FLAGS_AUTO BIT(1) /* Switch LED back to automatic control */ enum ec_led_colors { EC_LED_COLOR_RED = 0, @@ -1414,6 +2112,7 @@ enum ec_led_colors { EC_LED_COLOR_BLUE, EC_LED_COLOR_YELLOW, EC_LED_COLOR_WHITE, + EC_LED_COLOR_AMBER, EC_LED_COLOR_COUNT }; @@ -1423,7 +2122,7 @@ struct ec_params_led_control { uint8_t flags; /* Control flags */ uint8_t brightness[EC_LED_COLOR_COUNT]; -} __packed; +} __ec_align1; struct ec_response_led_control { /* @@ -1434,7 +2133,7 @@ struct ec_response_led_control { * Other values means the LED is control by PWM. */ uint8_t brightness_range[EC_LED_COLOR_COUNT]; -} __packed; +} __ec_align1; /*****************************************************************************/ /* Verified boot commands */ @@ -1445,7 +2144,7 @@ struct ec_response_led_control { */ /* Verified boot hash command */ -#define EC_CMD_VBOOT_HASH 0x2A +#define EC_CMD_VBOOT_HASH 0x002A struct ec_params_vboot_hash { uint8_t cmd; /* enum ec_vboot_hash_cmd */ @@ -1455,7 +2154,7 @@ struct ec_params_vboot_hash { uint32_t offset; /* Offset in flash to hash */ uint32_t size; /* Number of bytes to hash */ uint8_t nonce_data[64]; /* Nonce data; ignored if nonce_size=0 */ -} __packed; +} __ec_align4; struct ec_response_vboot_hash { uint8_t status; /* enum ec_vboot_hash_status */ @@ -1465,7 +2164,7 @@ struct ec_response_vboot_hash { uint32_t offset; /* Offset in flash which was hashed */ uint32_t size; /* Number of bytes hashed */ uint8_t hash_digest[64]; /* Hash digest data */ -} __packed; +} __ec_align4; enum ec_vboot_hash_cmd { EC_VBOOT_HASH_GET = 0, /* Get current hash status */ @@ -1489,15 +2188,22 @@ enum ec_vboot_hash_status { * If one of these is specified, the EC will automatically update offset and * size to the correct values for the specified image (RO or RW). */ -#define EC_VBOOT_HASH_OFFSET_RO 0xfffffffe -#define EC_VBOOT_HASH_OFFSET_RW 0xfffffffd +#define EC_VBOOT_HASH_OFFSET_RO 0xfffffffe +#define EC_VBOOT_HASH_OFFSET_ACTIVE 0xfffffffd +#define EC_VBOOT_HASH_OFFSET_UPDATE 0xfffffffc + +/* + * 'RW' is vague if there are multiple RW images; we mean the active one, + * so the old constant is deprecated. + */ +#define EC_VBOOT_HASH_OFFSET_RW EC_VBOOT_HASH_OFFSET_ACTIVE /*****************************************************************************/ /* * Motion sense commands. We'll make separate structs for sub-commands with * different input args, so that we know how much to expect. */ -#define EC_CMD_MOTION_SENSE_CMD 0x2B +#define EC_CMD_MOTION_SENSE_CMD 0x002B /* Motion sense commands */ enum motionsense_command { @@ -1516,7 +2222,13 @@ enum motionsense_command { /* * EC Rate command is a setter/getter command for the EC sampling rate - * of all motion sensors in milliseconds. + * in milliseconds. + * It is per sensor, the EC run sample task at the minimum of all + * sensors EC_RATE. + * For sensors without hardware FIFO, EC_RATE should be equals to 1/ODR + * to collect all the sensor samples. + * For sensor with hardware FIFO, EC_RATE is used as the maximal delay + * to process of all motion sensors in milliseconds. */ MOTIONSENSE_CMD_EC_RATE = 2, @@ -1547,32 +2259,76 @@ enum motionsense_command { MOTIONSENSE_CMD_DATA = 6, /* - * Perform low level calibration.. On sensors that support it, ask to - * do offset calibration. + * Return sensor fifo info. + */ + MOTIONSENSE_CMD_FIFO_INFO = 7, + + /* + * Insert a flush element in the fifo and return sensor fifo info. + * The host can use that element to synchronize its operation. + */ + MOTIONSENSE_CMD_FIFO_FLUSH = 8, + + /* + * Return a portion of the fifo. + */ + MOTIONSENSE_CMD_FIFO_READ = 9, + + /* + * Perform low level calibration. + * On sensors that support it, ask to do offset calibration. */ MOTIONSENSE_CMD_PERFORM_CALIB = 10, /* - * Sensor Offset command is a setter/getter command for the offset used - * for calibration. The offsets can be calculated by the host, or via + * Sensor Offset command is a setter/getter command for the offset + * used for calibration. + * The offsets can be calculated by the host, or via * PERFORM_CALIB command. */ MOTIONSENSE_CMD_SENSOR_OFFSET = 11, - /* Number of motionsense sub-commands. */ - MOTIONSENSE_NUM_CMDS -}; + /* + * List available activities for a MOTION sensor. + * Indicates if they are enabled or disabled. + */ + MOTIONSENSE_CMD_LIST_ACTIVITIES = 12, + + /* + * Activity management + * Enable/Disable activity recognition. + */ + MOTIONSENSE_CMD_SET_ACTIVITY = 13, + + /* + * Lid Angle + */ + MOTIONSENSE_CMD_LID_ANGLE = 14, + + /* + * Allow the FIFO to trigger interrupt via MKBP events. + * By default the FIFO does not send interrupt to process the FIFO + * until the AP is ready or it is coming from a wakeup sensor. + */ + MOTIONSENSE_CMD_FIFO_INT_ENABLE = 15, + + /* + * Spoof the readings of the sensors. The spoofed readings can be set + * to arbitrary values, or will lock to the last read actual values. + */ + MOTIONSENSE_CMD_SPOOF = 16, -enum motionsensor_id { - EC_MOTION_SENSOR_ACCEL_BASE = 0, - EC_MOTION_SENSOR_ACCEL_LID = 1, - EC_MOTION_SENSOR_GYRO = 2, + /* Set lid angle for tablet mode detection. */ + MOTIONSENSE_CMD_TABLET_MODE_LID_ANGLE = 17, /* - * Note, if more sensors are added and this count changes, the padding - * in ec_response_motion_sense dump command must be modified. + * Sensor Scale command is a setter/getter command for the calibration + * scale. */ - EC_MOTION_SENSOR_COUNT = 3 + MOTIONSENSE_CMD_SENSOR_SCALE = 18, + + /* Number of motionsense sub-commands. */ + MOTIONSENSE_NUM_CMDS }; /* List of motion sensor types. */ @@ -1584,6 +2340,7 @@ enum motionsensor_type { MOTIONSENSE_TYPE_LIGHT = 4, MOTIONSENSE_TYPE_ACTIVITY = 5, MOTIONSENSE_TYPE_BARO = 6, + MOTIONSENSE_TYPE_SYNC = 7, MOTIONSENSE_TYPE_MAX, }; @@ -1591,19 +2348,116 @@ enum motionsensor_type { enum motionsensor_location { MOTIONSENSE_LOC_BASE = 0, MOTIONSENSE_LOC_LID = 1, + MOTIONSENSE_LOC_CAMERA = 2, MOTIONSENSE_LOC_MAX, }; /* List of motion sensor chips. */ enum motionsensor_chip { MOTIONSENSE_CHIP_KXCJ9 = 0, + MOTIONSENSE_CHIP_LSM6DS0 = 1, + MOTIONSENSE_CHIP_BMI160 = 2, + MOTIONSENSE_CHIP_SI1141 = 3, + MOTIONSENSE_CHIP_SI1142 = 4, + MOTIONSENSE_CHIP_SI1143 = 5, + MOTIONSENSE_CHIP_KX022 = 6, + MOTIONSENSE_CHIP_L3GD20H = 7, + MOTIONSENSE_CHIP_BMA255 = 8, + MOTIONSENSE_CHIP_BMP280 = 9, + MOTIONSENSE_CHIP_OPT3001 = 10, + MOTIONSENSE_CHIP_BH1730 = 11, + MOTIONSENSE_CHIP_GPIO = 12, + MOTIONSENSE_CHIP_LIS2DH = 13, + MOTIONSENSE_CHIP_LSM6DSM = 14, + MOTIONSENSE_CHIP_LIS2DE = 15, + MOTIONSENSE_CHIP_LIS2MDL = 16, + MOTIONSENSE_CHIP_LSM6DS3 = 17, + MOTIONSENSE_CHIP_LSM6DSO = 18, + MOTIONSENSE_CHIP_LNG2DM = 19, + MOTIONSENSE_CHIP_MAX, +}; + +/* List of orientation positions */ +enum motionsensor_orientation { + MOTIONSENSE_ORIENTATION_LANDSCAPE = 0, + MOTIONSENSE_ORIENTATION_PORTRAIT = 1, + MOTIONSENSE_ORIENTATION_UPSIDE_DOWN_PORTRAIT = 2, + MOTIONSENSE_ORIENTATION_UPSIDE_DOWN_LANDSCAPE = 3, + MOTIONSENSE_ORIENTATION_UNKNOWN = 4, +}; + +struct ec_response_motion_sensor_data { + /* Flags for each sensor. */ + uint8_t flags; + /* Sensor number the data comes from. */ + uint8_t sensor_num; + /* Each sensor is up to 3-axis. */ + union { + int16_t data[3]; + struct __ec_todo_packed { + uint16_t reserved; + uint32_t timestamp; + }; + struct __ec_todo_unpacked { + uint8_t activity; /* motionsensor_activity */ + uint8_t state; + int16_t add_info[2]; + }; + }; +} __ec_todo_packed; + +/* Note: used in ec_response_get_next_data */ +struct ec_response_motion_sense_fifo_info { + /* Size of the fifo */ + uint16_t size; + /* Amount of space used in the fifo */ + uint16_t count; + /* Timestamp recorded in us. + * aka accurate timestamp when host event was triggered. + */ + uint32_t timestamp; + /* Total amount of vector lost */ + uint16_t total_lost; + /* Lost events since the last fifo_info, per sensors */ + uint16_t lost[0]; +} __ec_todo_packed; + +struct ec_response_motion_sense_fifo_data { + uint32_t number_data; + struct ec_response_motion_sensor_data data[0]; +} __ec_todo_packed; + +/* List supported activity recognition */ +enum motionsensor_activity { + MOTIONSENSE_ACTIVITY_RESERVED = 0, + MOTIONSENSE_ACTIVITY_SIG_MOTION = 1, + MOTIONSENSE_ACTIVITY_DOUBLE_TAP = 2, + MOTIONSENSE_ACTIVITY_ORIENTATION = 3, }; +struct ec_motion_sense_activity { + uint8_t sensor_num; + uint8_t activity; /* one of enum motionsensor_activity */ + uint8_t enable; /* 1: enable, 0: disable */ + uint8_t reserved; + uint16_t parameters[3]; /* activity dependent parameters */ +} __ec_todo_unpacked; + /* Module flag masks used for the dump sub-command. */ -#define MOTIONSENSE_MODULE_FLAG_ACTIVE (1<<0) +#define MOTIONSENSE_MODULE_FLAG_ACTIVE BIT(0) /* Sensor flag masks used for the dump sub-command. */ -#define MOTIONSENSE_SENSOR_FLAG_PRESENT (1<<0) +#define MOTIONSENSE_SENSOR_FLAG_PRESENT BIT(0) + +/* + * Flush entry for synchronization. + * data contains time stamp + */ +#define MOTIONSENSE_SENSOR_FLAG_FLUSH BIT(0) +#define MOTIONSENSE_SENSOR_FLAG_TIMESTAMP BIT(1) +#define MOTIONSENSE_SENSOR_FLAG_WAKEUP BIT(2) +#define MOTIONSENSE_SENSOR_FLAG_TABLET_MODE BIT(3) +#define MOTIONSENSE_SENSOR_FLAG_ODR BIT(4) /* * Send this value for the data element to only perform a read. If you @@ -1614,48 +2468,79 @@ enum motionsensor_chip { #define EC_MOTION_SENSE_INVALID_CALIB_TEMP 0x8000 +/* MOTIONSENSE_CMD_SENSOR_OFFSET subcommand flag */ /* Set Calibration information */ -#define MOTION_SENSE_SET_OFFSET 1 +#define MOTION_SENSE_SET_OFFSET BIT(0) -struct ec_response_motion_sensor_data { - /* Flags for each sensor. */ - uint8_t flags; - /* Sensor number the data comes from */ - uint8_t sensor_num; - /* Each sensor is up to 3-axis. */ - union { - int16_t data[3]; - struct { - uint16_t rsvd; - uint32_t timestamp; - } __packed; - struct { - uint8_t activity; /* motionsensor_activity */ - uint8_t state; - int16_t add_info[2]; - }; - }; -} __packed; +/* Default Scale value, factor 1. */ +#define MOTION_SENSE_DEFAULT_SCALE BIT(15) + +#define LID_ANGLE_UNRELIABLE 500 + +enum motionsense_spoof_mode { + /* Disable spoof mode. */ + MOTIONSENSE_SPOOF_MODE_DISABLE = 0, + + /* Enable spoof mode, but use provided component values. */ + MOTIONSENSE_SPOOF_MODE_CUSTOM, + + /* Enable spoof mode, but use the current sensor values. */ + MOTIONSENSE_SPOOF_MODE_LOCK_CURRENT, + + /* Query the current spoof mode status for the sensor. */ + MOTIONSENSE_SPOOF_MODE_QUERY, +}; struct ec_params_motion_sense { uint8_t cmd; union { /* Used for MOTIONSENSE_CMD_DUMP. */ - struct { - /* no args */ + struct __ec_todo_unpacked { + /* + * Maximal number of sensor the host is expecting. + * 0 means the host is only interested in the number + * of sensors controlled by the EC. + */ + uint8_t max_sensor_count; } dump; /* - * Used for MOTIONSENSE_CMD_EC_RATE and - * MOTIONSENSE_CMD_KB_WAKE_ANGLE. + * Used for MOTIONSENSE_CMD_KB_WAKE_ANGLE. */ - struct { - /* Data to set or EC_MOTION_SENSE_NO_VALUE to read. */ + struct __ec_todo_unpacked { + /* Data to set or EC_MOTION_SENSE_NO_VALUE to read. + * kb_wake_angle: angle to wakup AP. + */ int16_t data; - } ec_rate, kb_wake_angle; + } kb_wake_angle; + + /* + * Used for MOTIONSENSE_CMD_INFO, MOTIONSENSE_CMD_DATA + * and MOTIONSENSE_CMD_PERFORM_CALIB. + */ + struct __ec_todo_unpacked { + uint8_t sensor_num; + } info, info_3, data, fifo_flush, perform_calib, + list_activities; + + /* + * Used for MOTIONSENSE_CMD_EC_RATE, MOTIONSENSE_CMD_SENSOR_ODR + * and MOTIONSENSE_CMD_SENSOR_RANGE. + */ + struct __ec_todo_unpacked { + uint8_t sensor_num; + + /* Rounding flag, true for round-up, false for down. */ + uint8_t roundup; + + uint16_t reserved; + + /* Data to set or EC_MOTION_SENSE_NO_VALUE to read. */ + int32_t data; + } ec_rate, sensor_odr, sensor_range; /* Used for MOTIONSENSE_CMD_SENSOR_OFFSET */ - struct { + struct __ec_todo_packed { uint8_t sensor_num; /* @@ -1681,36 +2566,102 @@ struct ec_params_motion_sense { * Compass: 1/16 uT */ int16_t offset[3]; - } __packed sensor_offset; + } sensor_offset; - /* Used for MOTIONSENSE_CMD_INFO. */ - struct { + /* Used for MOTIONSENSE_CMD_SENSOR_SCALE */ + struct __ec_todo_packed { uint8_t sensor_num; - } info; - /* - * Used for MOTIONSENSE_CMD_SENSOR_ODR and - * MOTIONSENSE_CMD_SENSOR_RANGE. - */ - struct { - /* Should be element of enum motionsensor_id. */ - uint8_t sensor_num; + /* + * bit 0: If set (MOTION_SENSE_SET_OFFSET), set + * the calibration information in the EC. + * If unset, just retrieve calibration information. + */ + uint16_t flags; - /* Rounding flag, true for round-up, false for down. */ - uint8_t roundup; + /* + * Temperature at calibration, in units of 0.01 C + * 0x8000: invalid / unknown. + * 0x0: 0C + * 0x7fff: +327.67C + */ + int16_t temp; - uint16_t reserved; + /* + * Scale for calibration: + * By default scale is 1, it is encoded on 16bits: + * 1 = BIT(15) + * ~2 = 0xFFFF + * ~0 = 0. + */ + uint16_t scale[3]; + } sensor_scale; - /* Data to set or EC_MOTION_SENSE_NO_VALUE to read. */ - int32_t data; - } sensor_odr, sensor_range; + + /* Used for MOTIONSENSE_CMD_FIFO_INFO */ + /* (no params) */ + + /* Used for MOTIONSENSE_CMD_FIFO_READ */ + struct __ec_todo_unpacked { + /* + * Number of expected vector to return. + * EC may return less or 0 if none available. + */ + uint32_t max_data_vector; + } fifo_read; + + struct ec_motion_sense_activity set_activity; + + /* Used for MOTIONSENSE_CMD_LID_ANGLE */ + /* (no params) */ + + /* Used for MOTIONSENSE_CMD_FIFO_INT_ENABLE */ + struct __ec_todo_unpacked { + /* + * 1: enable, 0 disable fifo, + * EC_MOTION_SENSE_NO_VALUE return value. + */ + int8_t enable; + } fifo_int_enable; + + /* Used for MOTIONSENSE_CMD_SPOOF */ + struct __ec_todo_packed { + uint8_t sensor_id; + + /* See enum motionsense_spoof_mode. */ + uint8_t spoof_enable; + + /* Ignored, used for alignment. */ + uint8_t reserved; + + /* Individual component values to spoof. */ + int16_t components[3]; + } spoof; + + /* Used for MOTIONSENSE_CMD_TABLET_MODE_LID_ANGLE. */ + struct __ec_todo_unpacked { + /* + * Lid angle threshold for switching between tablet and + * clamshell mode. + */ + int16_t lid_angle; + + /* + * Hysteresis degree to prevent fluctuations between + * clamshell and tablet mode if lid angle keeps + * changing around the threshold. Lid motion driver will + * use lid_angle + hys_degree to trigger tablet mode and + * lid_angle - hys_degree to trigger clamshell mode. + */ + int16_t hys_degree; + } tablet_mode_threshold; }; -} __packed; +} __ec_todo_packed; struct ec_response_motion_sense { union { - /* Used for MOTIONSENSE_CMD_DUMP. */ - struct { + /* Used for MOTIONSENSE_CMD_DUMP */ + struct __ec_todo_unpacked { /* Flags representing the motion sensor module. */ uint8_t module_flags; @@ -1725,7 +2676,7 @@ struct ec_response_motion_sense { } dump; /* Used for MOTIONSENSE_CMD_INFO. */ - struct { + struct __ec_todo_unpacked { /* Should be element of enum motionsensor_type. */ uint8_t type; @@ -1736,37 +2687,129 @@ struct ec_response_motion_sense { uint8_t chip; } info; + /* Used for MOTIONSENSE_CMD_INFO version 3 */ + struct __ec_todo_unpacked { + /* Should be element of enum motionsensor_type. */ + uint8_t type; + + /* Should be element of enum motionsensor_location. */ + uint8_t location; + + /* Should be element of enum motionsensor_chip. */ + uint8_t chip; + + /* Minimum sensor sampling frequency */ + uint32_t min_frequency; + + /* Maximum sensor sampling frequency */ + uint32_t max_frequency; + + /* Max number of sensor events that could be in fifo */ + uint32_t fifo_max_event_count; + } info_3; + /* Used for MOTIONSENSE_CMD_DATA */ struct ec_response_motion_sensor_data data; /* * Used for MOTIONSENSE_CMD_EC_RATE, MOTIONSENSE_CMD_SENSOR_ODR, - * MOTIONSENSE_CMD_SENSOR_RANGE, and - * MOTIONSENSE_CMD_KB_WAKE_ANGLE. + * MOTIONSENSE_CMD_SENSOR_RANGE, + * MOTIONSENSE_CMD_KB_WAKE_ANGLE, + * MOTIONSENSE_CMD_FIFO_INT_ENABLE and + * MOTIONSENSE_CMD_SPOOF. */ - struct { + struct __ec_todo_unpacked { /* Current value of the parameter queried. */ int32_t ret; - } ec_rate, sensor_odr, sensor_range, kb_wake_angle; + } ec_rate, sensor_odr, sensor_range, kb_wake_angle, + fifo_int_enable, spoof; - /* Used for MOTIONSENSE_CMD_SENSOR_OFFSET */ - struct { + /* + * Used for MOTIONSENSE_CMD_SENSOR_OFFSET, + * PERFORM_CALIB. + */ + struct __ec_todo_unpacked { int16_t temp; int16_t offset[3]; } sensor_offset, perform_calib; + + /* Used for MOTIONSENSE_CMD_SENSOR_SCALE */ + struct __ec_todo_unpacked { + int16_t temp; + uint16_t scale[3]; + } sensor_scale; + + struct ec_response_motion_sense_fifo_info fifo_info, fifo_flush; + + struct ec_response_motion_sense_fifo_data fifo_read; + + struct __ec_todo_packed { + uint16_t reserved; + uint32_t enabled; + uint32_t disabled; + } list_activities; + + /* No params for set activity */ + + /* Used for MOTIONSENSE_CMD_LID_ANGLE */ + struct __ec_todo_unpacked { + /* + * Angle between 0 and 360 degree if available, + * LID_ANGLE_UNRELIABLE otherwise. + */ + uint16_t value; + } lid_angle; + + /* Used for MOTIONSENSE_CMD_TABLET_MODE_LID_ANGLE. */ + struct __ec_todo_unpacked { + /* + * Lid angle threshold for switching between tablet and + * clamshell mode. + */ + uint16_t lid_angle; + + /* Hysteresis degree. */ + uint16_t hys_degree; + } tablet_mode_threshold; + }; -} __packed; +} __ec_todo_packed; + +/*****************************************************************************/ +/* Force lid open command */ + +/* Make lid event always open */ +#define EC_CMD_FORCE_LID_OPEN 0x002C + +struct ec_params_force_lid_open { + uint8_t enabled; +} __ec_align1; + +/*****************************************************************************/ +/* Configure the behavior of the power button */ +#define EC_CMD_CONFIG_POWER_BUTTON 0x002D + +enum ec_config_power_button_flags { + /* Enable/Disable power button pulses for x86 devices */ + EC_POWER_BUTTON_ENABLE_PULSE = BIT(0), +}; + +struct ec_params_config_power_button { + /* See enum ec_config_power_button_flags */ + uint8_t flags; +} __ec_align1; /*****************************************************************************/ /* USB charging control commands */ /* Set USB port charging mode */ -#define EC_CMD_USB_CHARGE_SET_MODE 0x30 +#define EC_CMD_USB_CHARGE_SET_MODE 0x0030 struct ec_params_usb_charge_set_mode { uint8_t usb_port_id; - uint8_t mode; -} __packed; + uint8_t mode:7; + uint8_t inhibit_charge:1; +} __ec_align1; /*****************************************************************************/ /* Persistent storage for host */ @@ -1775,35 +2818,35 @@ struct ec_params_usb_charge_set_mode { #define EC_PSTORE_SIZE_MAX 64 /* Get persistent storage info */ -#define EC_CMD_PSTORE_INFO 0x40 +#define EC_CMD_PSTORE_INFO 0x0040 struct ec_response_pstore_info { /* Persistent storage size, in bytes */ uint32_t pstore_size; /* Access size; read/write offset and size must be a multiple of this */ uint32_t access_size; -} __packed; +} __ec_align4; /* * Read persistent storage * * Response is params.size bytes of data. */ -#define EC_CMD_PSTORE_READ 0x41 +#define EC_CMD_PSTORE_READ 0x0041 struct ec_params_pstore_read { uint32_t offset; /* Byte offset to read */ uint32_t size; /* Size to read in bytes */ -} __packed; +} __ec_align4; /* Write persistent storage */ -#define EC_CMD_PSTORE_WRITE 0x42 +#define EC_CMD_PSTORE_WRITE 0x0042 struct ec_params_pstore_write { uint32_t offset; /* Byte offset to write */ uint32_t size; /* Size to write in bytes */ uint8_t data[EC_PSTORE_SIZE_MAX]; -} __packed; +} __ec_align4; /*****************************************************************************/ /* Real-time clock */ @@ -1811,21 +2854,21 @@ struct ec_params_pstore_write { /* RTC params and response structures */ struct ec_params_rtc { uint32_t time; -} __packed; +} __ec_align4; struct ec_response_rtc { uint32_t time; -} __packed; +} __ec_align4; /* These use ec_response_rtc */ -#define EC_CMD_RTC_GET_VALUE 0x44 -#define EC_CMD_RTC_GET_ALARM 0x45 +#define EC_CMD_RTC_GET_VALUE 0x0044 +#define EC_CMD_RTC_GET_ALARM 0x0045 /* These all use ec_params_rtc */ -#define EC_CMD_RTC_SET_VALUE 0x46 -#define EC_CMD_RTC_SET_ALARM 0x47 +#define EC_CMD_RTC_SET_VALUE 0x0046 +#define EC_CMD_RTC_SET_ALARM 0x0047 -/* Pass as param to SET_ALARM to clear the current alarm */ +/* Pass as time param to SET_ALARM to clear the current alarm */ #define EC_RTC_ALARM_CLEAR 0 /*****************************************************************************/ @@ -1835,8 +2878,8 @@ struct ec_response_rtc { #define EC_PORT80_SIZE_MAX 32 /* Get last port80 code from previous boot */ -#define EC_CMD_PORT80_LAST_BOOT 0x48 -#define EC_CMD_PORT80_READ 0x48 +#define EC_CMD_PORT80_LAST_BOOT 0x0048 +#define EC_CMD_PORT80_READ 0x0048 enum ec_port80_subcmd { EC_PORT80_GET_INFO = 0, @@ -1846,29 +2889,72 @@ enum ec_port80_subcmd { struct ec_params_port80_read { uint16_t subcmd; union { - struct { + struct __ec_todo_unpacked { uint32_t offset; uint32_t num_entries; } read_buffer; }; -} __packed; +} __ec_todo_packed; struct ec_response_port80_read { union { - struct { + struct __ec_todo_unpacked { uint32_t writes; uint32_t history_size; uint32_t last_boot; } get_info; - struct { + struct __ec_todo_unpacked { uint16_t codes[EC_PORT80_SIZE_MAX]; } data; }; -} __packed; +} __ec_todo_packed; struct ec_response_port80_last_boot { uint16_t code; -} __packed; +} __ec_align2; + +/*****************************************************************************/ +/* Temporary secure storage for host verified boot use */ + +/* Number of bytes in a vstore slot */ +#define EC_VSTORE_SLOT_SIZE 64 + +/* Maximum number of vstore slots */ +#define EC_VSTORE_SLOT_MAX 32 + +/* Get persistent storage info */ +#define EC_CMD_VSTORE_INFO 0x0049 +struct ec_response_vstore_info { + /* Indicates which slots are locked */ + uint32_t slot_locked; + /* Total number of slots available */ + uint8_t slot_count; +} __ec_align_size1; + +/* + * Read temporary secure storage + * + * Response is EC_VSTORE_SLOT_SIZE bytes of data. + */ +#define EC_CMD_VSTORE_READ 0x004A + +struct ec_params_vstore_read { + uint8_t slot; /* Slot to read from */ +} __ec_align1; + +struct ec_response_vstore_read { + uint8_t data[EC_VSTORE_SLOT_SIZE]; +} __ec_align1; + +/* + * Write temporary secure storage and lock it. + */ +#define EC_CMD_VSTORE_WRITE 0x004B + +struct ec_params_vstore_write { + uint8_t slot; /* Slot to write to */ + uint8_t data[EC_VSTORE_SLOT_SIZE]; +} __ec_align1; /*****************************************************************************/ /* Thermal engine commands. Note that there are two implementations. We'll @@ -1877,8 +2963,8 @@ struct ec_response_port80_last_boot { * Version 1 separates the CPU thermal limits from the fan control. */ -#define EC_CMD_THERMAL_SET_THRESHOLD 0x50 -#define EC_CMD_THERMAL_GET_THRESHOLD 0x51 +#define EC_CMD_THERMAL_SET_THRESHOLD 0x0050 +#define EC_CMD_THERMAL_GET_THRESHOLD 0x0051 /* The version 0 structs are opaque. You have to know what they are for * the get/set commands to make any sense. @@ -1889,17 +2975,17 @@ struct ec_params_thermal_set_threshold { uint8_t sensor_type; uint8_t threshold_id; uint16_t value; -} __packed; +} __ec_align2; /* Version 0 - get */ struct ec_params_thermal_get_threshold { uint8_t sensor_type; uint8_t threshold_id; -} __packed; +} __ec_align1; struct ec_response_thermal_get_threshold { uint16_t value; -} __packed; +} __ec_align2; /* The version 1 structs are visible. */ @@ -1911,71 +2997,124 @@ enum ec_temp_thresholds { EC_TEMP_THRESH_COUNT }; -/* Thermal configuration for one temperature sensor. Temps are in degrees K. +/* + * Thermal configuration for one temperature sensor. Temps are in degrees K. * Zero values will be silently ignored by the thermal task. + * + * Set 'temp_host' value allows thermal task to trigger some event with 1 degree + * hysteresis. + * For example, + * temp_host[EC_TEMP_THRESH_HIGH] = 300 K + * temp_host_release[EC_TEMP_THRESH_HIGH] = 0 K + * EC will throttle ap when temperature >= 301 K, and release throttling when + * temperature <= 299 K. + * + * Set 'temp_host_release' value allows thermal task has a custom hysteresis. + * For example, + * temp_host[EC_TEMP_THRESH_HIGH] = 300 K + * temp_host_release[EC_TEMP_THRESH_HIGH] = 295 K + * EC will throttle ap when temperature >= 301 K, and release throttling when + * temperature <= 294 K. + * + * Note that this structure is a sub-structure of + * ec_params_thermal_set_threshold_v1, but maintains its alignment there. */ struct ec_thermal_config { uint32_t temp_host[EC_TEMP_THRESH_COUNT]; /* levels of hotness */ + uint32_t temp_host_release[EC_TEMP_THRESH_COUNT]; /* release levels */ uint32_t temp_fan_off; /* no active cooling needed */ uint32_t temp_fan_max; /* max active cooling needed */ -} __packed; +} __ec_align4; /* Version 1 - get config for one sensor. */ struct ec_params_thermal_get_threshold_v1 { uint32_t sensor_num; -} __packed; +} __ec_align4; /* This returns a struct ec_thermal_config */ -/* Version 1 - set config for one sensor. - * Use read-modify-write for best results! */ +/* + * Version 1 - set config for one sensor. + * Use read-modify-write for best results! + */ struct ec_params_thermal_set_threshold_v1 { uint32_t sensor_num; struct ec_thermal_config cfg; -} __packed; +} __ec_align4; /* This returns no data */ /****************************************************************************/ /* Toggle automatic fan control */ -#define EC_CMD_THERMAL_AUTO_FAN_CTRL 0x52 +#define EC_CMD_THERMAL_AUTO_FAN_CTRL 0x0052 + +/* Version 1 of input params */ +struct ec_params_auto_fan_ctrl_v1 { + uint8_t fan_idx; +} __ec_align1; -/* Get TMP006 calibration data */ -#define EC_CMD_TMP006_GET_CALIBRATION 0x53 +/* Get/Set TMP006 calibration data */ +#define EC_CMD_TMP006_GET_CALIBRATION 0x0053 +#define EC_CMD_TMP006_SET_CALIBRATION 0x0054 +/* + * The original TMP006 calibration only needed four params, but now we need + * more. Since the algorithm is nothing but magic numbers anyway, we'll leave + * the params opaque. The v1 "get" response will include the algorithm number + * and how many params it requires. That way we can change the EC code without + * needing to update this file. We can also use a different algorithm on each + * sensor. + */ + +/* This is the same struct for both v0 and v1. */ struct ec_params_tmp006_get_calibration { uint8_t index; -} __packed; +} __ec_align1; -struct ec_response_tmp006_get_calibration { +/* Version 0 */ +struct ec_response_tmp006_get_calibration_v0 { float s0; float b0; float b1; float b2; -} __packed; +} __ec_align4; -/* Set TMP006 calibration data */ -#define EC_CMD_TMP006_SET_CALIBRATION 0x54 - -struct ec_params_tmp006_set_calibration { +struct ec_params_tmp006_set_calibration_v0 { uint8_t index; - uint8_t reserved[3]; /* Reserved; set 0 */ + uint8_t reserved[3]; float s0; float b0; float b1; float b2; -} __packed; +} __ec_align4; + +/* Version 1 */ +struct ec_response_tmp006_get_calibration_v1 { + uint8_t algorithm; + uint8_t num_params; + uint8_t reserved[2]; + float val[0]; +} __ec_align4; + +struct ec_params_tmp006_set_calibration_v1 { + uint8_t index; + uint8_t algorithm; + uint8_t num_params; + uint8_t reserved; + float val[0]; +} __ec_align4; + /* Read raw TMP006 data */ -#define EC_CMD_TMP006_GET_RAW 0x55 +#define EC_CMD_TMP006_GET_RAW 0x0055 struct ec_params_tmp006_get_raw { uint8_t index; -} __packed; +} __ec_align1; struct ec_response_tmp006_get_raw { int32_t t; /* In 1/100 K */ int32_t v; /* In nV */ -}; +} __ec_align4; /*****************************************************************************/ /* MKBP - Matrix KeyBoard Protocol */ @@ -1990,24 +3129,24 @@ struct ec_response_tmp006_get_raw { * to obtain the instantaneous state, use EC_CMD_MKBP_INFO with the type * EC_MKBP_INFO_CURRENT and event EC_MKBP_EVENT_KEY_MATRIX. */ -#define EC_CMD_MKBP_STATE 0x60 +#define EC_CMD_MKBP_STATE 0x0060 /* * Provide information about various MKBP things. See enum ec_mkbp_info_type. */ -#define EC_CMD_MKBP_INFO 0x61 +#define EC_CMD_MKBP_INFO 0x0061 struct ec_response_mkbp_info { uint32_t rows; uint32_t cols; /* Formerly "switches", which was 0. */ uint8_t reserved; -} __packed; +} __ec_align_size1; struct ec_params_mkbp_info { uint8_t info_type; uint8_t event_type; -} __packed; +} __ec_align1; enum ec_mkbp_info_type { /* @@ -2049,17 +3188,28 @@ enum ec_mkbp_info_type { }; /* Simulate key press */ -#define EC_CMD_MKBP_SIMULATE_KEY 0x62 +#define EC_CMD_MKBP_SIMULATE_KEY 0x0062 struct ec_params_mkbp_simulate_key { uint8_t col; uint8_t row; uint8_t pressed; -} __packed; +} __ec_align1; + +#define EC_CMD_GET_KEYBOARD_ID 0x0063 + +struct ec_response_keyboard_id { + uint32_t keyboard_id; +} __ec_align4; + +enum keyboard_id { + KEYBOARD_ID_UNSUPPORTED = 0, + KEYBOARD_ID_UNREADABLE = 0xffffffff, +}; /* Configure keyboard scanning */ -#define EC_CMD_MKBP_SET_CONFIG 0x64 -#define EC_CMD_MKBP_GET_CONFIG 0x65 +#define EC_CMD_MKBP_SET_CONFIG 0x0064 +#define EC_CMD_MKBP_GET_CONFIG 0x0065 /* flags */ enum mkbp_config_flags { @@ -2067,16 +3217,21 @@ enum mkbp_config_flags { }; enum mkbp_config_valid { - EC_MKBP_VALID_SCAN_PERIOD = 1 << 0, - EC_MKBP_VALID_POLL_TIMEOUT = 1 << 1, - EC_MKBP_VALID_MIN_POST_SCAN_DELAY = 1 << 3, - EC_MKBP_VALID_OUTPUT_SETTLE = 1 << 4, - EC_MKBP_VALID_DEBOUNCE_DOWN = 1 << 5, - EC_MKBP_VALID_DEBOUNCE_UP = 1 << 6, - EC_MKBP_VALID_FIFO_MAX_DEPTH = 1 << 7, + EC_MKBP_VALID_SCAN_PERIOD = BIT(0), + EC_MKBP_VALID_POLL_TIMEOUT = BIT(1), + EC_MKBP_VALID_MIN_POST_SCAN_DELAY = BIT(3), + EC_MKBP_VALID_OUTPUT_SETTLE = BIT(4), + EC_MKBP_VALID_DEBOUNCE_DOWN = BIT(5), + EC_MKBP_VALID_DEBOUNCE_UP = BIT(6), + EC_MKBP_VALID_FIFO_MAX_DEPTH = BIT(7), }; -/* Configuration for our key scanning algorithm */ +/* + * Configuration for our key scanning algorithm. + * + * Note that this is used as a sub-structure of + * ec_{params/response}_mkbp_get_config. + */ struct ec_mkbp_config { uint32_t valid_mask; /* valid fields */ uint8_t flags; /* some flags (enum mkbp_config_flags) */ @@ -2096,18 +3251,18 @@ struct ec_mkbp_config { uint16_t debounce_up_us; /* time for debounce on key up */ /* maximum depth to allow for fifo (0 = no keyscan output) */ uint8_t fifo_max_depth; -} __packed; +} __ec_align_size1; struct ec_params_mkbp_set_config { struct ec_mkbp_config config; -} __packed; +} __ec_align_size1; struct ec_response_mkbp_get_config { struct ec_mkbp_config config; -} __packed; +} __ec_align_size1; /* Run the key scan emulation */ -#define EC_CMD_KEYSCAN_SEQ_CTRL 0x66 +#define EC_CMD_KEYSCAN_SEQ_CTRL 0x0066 enum ec_keyscan_seq_cmd { EC_KEYSCAN_SEQ_STATUS = 0, /* Get status information */ @@ -2122,23 +3277,23 @@ enum ec_collect_flags { * Indicates this scan was processed by the EC. Due to timing, some * scans may be skipped. */ - EC_KEYSCAN_SEQ_FLAG_DONE = 1 << 0, + EC_KEYSCAN_SEQ_FLAG_DONE = BIT(0), }; struct ec_collect_item { uint8_t flags; /* some flags (enum ec_collect_flags) */ -}; +} __ec_align1; struct ec_params_keyscan_seq_ctrl { uint8_t cmd; /* Command to send (enum ec_keyscan_seq_cmd) */ union { - struct { + struct __ec_align1 { uint8_t active; /* still active */ uint8_t num_items; /* number of items */ /* Current item being presented */ uint8_t cur_item; } status; - struct { + struct __ec_todo_unpacked { /* * Absolute time for this scan, measured from the * start of the sequence. @@ -2146,29 +3301,40 @@ struct ec_params_keyscan_seq_ctrl { uint32_t time_us; uint8_t scan[0]; /* keyscan data */ } add; - struct { + struct __ec_align1 { uint8_t start_item; /* First item to return */ uint8_t num_items; /* Number of items to return */ } collect; }; -} __packed; +} __ec_todo_packed; struct ec_result_keyscan_seq_ctrl { union { - struct { + struct __ec_todo_unpacked { uint8_t num_items; /* Number of items */ /* Data for each item */ struct ec_collect_item item[0]; } collect; }; -} __packed; +} __ec_todo_packed; /* - * Command for retrieving the next pending MKBP event from the EC device + * Get the next pending MKBP event. * - * The device replies with UNAVAILABLE if there aren't any pending events. + * Returns EC_RES_UNAVAILABLE if there is no event pending. */ -#define EC_CMD_GET_NEXT_EVENT 0x67 +#define EC_CMD_GET_NEXT_EVENT 0x0067 + +#define EC_MKBP_HAS_MORE_EVENTS_SHIFT 7 + +/* + * We use the most significant bit of the event type to indicate to the host + * that the EC has more MKBP events available to provide. + */ +#define EC_MKBP_HAS_MORE_EVENTS BIT(EC_MKBP_HAS_MORE_EVENTS_SHIFT) + +/* The mask to apply to get the raw event type */ +#define EC_MKBP_EVENT_TYPE_MASK (BIT(EC_MKBP_HAS_MORE_EVENTS_SHIFT) - 1) enum ec_mkbp_event { /* Keyboard matrix changed. The event data is the new matrix state. */ @@ -2186,9 +3352,21 @@ enum ec_mkbp_event { /* The state of the switches have changed. */ EC_MKBP_EVENT_SWITCH = 4, - /* EC sent a sysrq command */ + /* New Fingerprint sensor event, the event data is fp_events bitmap. */ + EC_MKBP_EVENT_FINGERPRINT = 5, + + /* + * Sysrq event: send emulated sysrq. The event data is sysrq, + * corresponding to the key to be pressed. + */ EC_MKBP_EVENT_SYSRQ = 6, + /* + * New 64-bit host event. + * The event data is 8 bytes of host event flags. + */ + EC_MKBP_EVENT_HOST_EVENT64 = 7, + /* Notify the AP that something happened on CEC */ EC_MKBP_EVENT_CEC_EVENT = 8, @@ -2198,65 +3376,140 @@ enum ec_mkbp_event { /* Number of MKBP events */ EC_MKBP_EVENT_COUNT, }; +BUILD_ASSERT(EC_MKBP_EVENT_COUNT <= EC_MKBP_EVENT_TYPE_MASK); -union ec_response_get_next_data { - uint8_t key_matrix[13]; +union __ec_align_offset1 ec_response_get_next_data { + uint8_t key_matrix[13]; /* Unaligned */ - uint32_t host_event; + uint32_t host_event; + uint64_t host_event64; + + struct __ec_todo_unpacked { + /* For aligning the fifo_info */ + uint8_t reserved[3]; + struct ec_response_motion_sense_fifo_info info; + } sensor_fifo; + + uint32_t buttons; + + uint32_t switches; - uint32_t buttons; - uint32_t switches; - uint32_t sysrq; -} __packed; + uint32_t fp_events; + + uint32_t sysrq; -union ec_response_get_next_data_v1 { + /* CEC events from enum mkbp_cec_event */ + uint32_t cec_events; +}; + +union __ec_align_offset1 ec_response_get_next_data_v1 { uint8_t key_matrix[16]; + + /* Unaligned */ uint32_t host_event; + uint64_t host_event64; + + struct __ec_todo_unpacked { + /* For aligning the fifo_info */ + uint8_t reserved[3]; + struct ec_response_motion_sense_fifo_info info; + } sensor_fifo; + uint32_t buttons; + uint32_t switches; + + uint32_t fp_events; + uint32_t sysrq; + + /* CEC events from enum mkbp_cec_event */ uint32_t cec_events; + uint8_t cec_message[16]; -} __packed; +}; +BUILD_ASSERT(sizeof(union ec_response_get_next_data_v1) == 16); struct ec_response_get_next_event { uint8_t event_type; /* Followed by event data if any */ union ec_response_get_next_data data; -} __packed; +} __ec_align1; struct ec_response_get_next_event_v1 { uint8_t event_type; /* Followed by event data if any */ union ec_response_get_next_data_v1 data; -} __packed; +} __ec_align1; /* Bit indices for buttons and switches.*/ /* Buttons */ #define EC_MKBP_POWER_BUTTON 0 #define EC_MKBP_VOL_UP 1 #define EC_MKBP_VOL_DOWN 2 +#define EC_MKBP_RECOVERY 3 /* Switches */ #define EC_MKBP_LID_OPEN 0 #define EC_MKBP_TABLET_MODE 1 #define EC_MKBP_BASE_ATTACHED 2 +/* Run keyboard factory test scanning */ +#define EC_CMD_KEYBOARD_FACTORY_TEST 0x0068 + +struct ec_response_keyboard_factory_test { + uint16_t shorted; /* Keyboard pins are shorted */ +} __ec_align2; + +/* Fingerprint events in 'fp_events' for EC_MKBP_EVENT_FINGERPRINT */ +#define EC_MKBP_FP_RAW_EVENT(fp_events) ((fp_events) & 0x00FFFFFF) +#define EC_MKBP_FP_ERRCODE(fp_events) ((fp_events) & 0x0000000F) +#define EC_MKBP_FP_ENROLL_PROGRESS_OFFSET 4 +#define EC_MKBP_FP_ENROLL_PROGRESS(fpe) (((fpe) & 0x00000FF0) \ + >> EC_MKBP_FP_ENROLL_PROGRESS_OFFSET) +#define EC_MKBP_FP_MATCH_IDX_OFFSET 12 +#define EC_MKBP_FP_MATCH_IDX_MASK 0x0000F000 +#define EC_MKBP_FP_MATCH_IDX(fpe) (((fpe) & EC_MKBP_FP_MATCH_IDX_MASK) \ + >> EC_MKBP_FP_MATCH_IDX_OFFSET) +#define EC_MKBP_FP_ENROLL BIT(27) +#define EC_MKBP_FP_MATCH BIT(28) +#define EC_MKBP_FP_FINGER_DOWN BIT(29) +#define EC_MKBP_FP_FINGER_UP BIT(30) +#define EC_MKBP_FP_IMAGE_READY BIT(31) +/* code given by EC_MKBP_FP_ERRCODE() when EC_MKBP_FP_ENROLL is set */ +#define EC_MKBP_FP_ERR_ENROLL_OK 0 +#define EC_MKBP_FP_ERR_ENROLL_LOW_QUALITY 1 +#define EC_MKBP_FP_ERR_ENROLL_IMMOBILE 2 +#define EC_MKBP_FP_ERR_ENROLL_LOW_COVERAGE 3 +#define EC_MKBP_FP_ERR_ENROLL_INTERNAL 5 +/* Can be used to detect if image was usable for enrollment or not. */ +#define EC_MKBP_FP_ERR_ENROLL_PROBLEM_MASK 1 +/* code given by EC_MKBP_FP_ERRCODE() when EC_MKBP_FP_MATCH is set */ +#define EC_MKBP_FP_ERR_MATCH_NO 0 +#define EC_MKBP_FP_ERR_MATCH_NO_INTERNAL 6 +#define EC_MKBP_FP_ERR_MATCH_NO_TEMPLATES 7 +#define EC_MKBP_FP_ERR_MATCH_NO_LOW_QUALITY 2 +#define EC_MKBP_FP_ERR_MATCH_NO_LOW_COVERAGE 4 +#define EC_MKBP_FP_ERR_MATCH_YES 1 +#define EC_MKBP_FP_ERR_MATCH_YES_UPDATED 3 +#define EC_MKBP_FP_ERR_MATCH_YES_UPDATE_FAILED 5 + + /*****************************************************************************/ /* Temperature sensor commands */ /* Read temperature sensor info */ -#define EC_CMD_TEMP_SENSOR_GET_INFO 0x70 +#define EC_CMD_TEMP_SENSOR_GET_INFO 0x0070 struct ec_params_temp_sensor_get_info { uint8_t id; -} __packed; +} __ec_align1; struct ec_response_temp_sensor_get_info { char sensor_name[32]; uint8_t sensor_type; -} __packed; +} __ec_align1; /*****************************************************************************/ @@ -2269,49 +3522,131 @@ struct ec_response_temp_sensor_get_info { /*****************************************************************************/ /* Host event commands */ + +/* Obsolete. New implementation should use EC_CMD_HOST_EVENT instead */ /* * Host event mask params and response structures, shared by all of the host * event commands below. */ struct ec_params_host_event_mask { uint32_t mask; -} __packed; +} __ec_align4; struct ec_response_host_event_mask { uint32_t mask; -} __packed; +} __ec_align4; /* These all use ec_response_host_event_mask */ -#define EC_CMD_HOST_EVENT_GET_B 0x87 -#define EC_CMD_HOST_EVENT_GET_SMI_MASK 0x88 -#define EC_CMD_HOST_EVENT_GET_SCI_MASK 0x89 -#define EC_CMD_HOST_EVENT_GET_WAKE_MASK 0x8d +#define EC_CMD_HOST_EVENT_GET_B 0x0087 +#define EC_CMD_HOST_EVENT_GET_SMI_MASK 0x0088 +#define EC_CMD_HOST_EVENT_GET_SCI_MASK 0x0089 +#define EC_CMD_HOST_EVENT_GET_WAKE_MASK 0x008D /* These all use ec_params_host_event_mask */ -#define EC_CMD_HOST_EVENT_SET_SMI_MASK 0x8a -#define EC_CMD_HOST_EVENT_SET_SCI_MASK 0x8b -#define EC_CMD_HOST_EVENT_CLEAR 0x8c -#define EC_CMD_HOST_EVENT_SET_WAKE_MASK 0x8e -#define EC_CMD_HOST_EVENT_CLEAR_B 0x8f +#define EC_CMD_HOST_EVENT_SET_SMI_MASK 0x008A +#define EC_CMD_HOST_EVENT_SET_SCI_MASK 0x008B +#define EC_CMD_HOST_EVENT_CLEAR 0x008C +#define EC_CMD_HOST_EVENT_SET_WAKE_MASK 0x008E +#define EC_CMD_HOST_EVENT_CLEAR_B 0x008F + +/* + * Unified host event programming interface - Should be used by newer versions + * of BIOS/OS to program host events and masks + */ + +struct ec_params_host_event { + + /* Action requested by host - one of enum ec_host_event_action. */ + uint8_t action; + + /* + * Mask type that the host requested the action on - one of + * enum ec_host_event_mask_type. + */ + uint8_t mask_type; + + /* Set to 0, ignore on read */ + uint16_t reserved; + + /* Value to be used in case of set operations. */ + uint64_t value; +} __ec_align4; + +/* + * Response structure returned by EC_CMD_HOST_EVENT. + * Update the value on a GET request. Set to 0 on GET/CLEAR + */ + +struct ec_response_host_event { + + /* Mask value in case of get operation */ + uint64_t value; +} __ec_align4; + +enum ec_host_event_action { + /* + * params.value is ignored. Value of mask_type populated + * in response.value + */ + EC_HOST_EVENT_GET, + + /* Bits in params.value are set */ + EC_HOST_EVENT_SET, + + /* Bits in params.value are cleared */ + EC_HOST_EVENT_CLEAR, +}; + +enum ec_host_event_mask_type { + + /* Main host event copy */ + EC_HOST_EVENT_MAIN, + + /* Copy B of host events */ + EC_HOST_EVENT_B, + + /* SCI Mask */ + EC_HOST_EVENT_SCI_MASK, + + /* SMI Mask */ + EC_HOST_EVENT_SMI_MASK, + + /* Mask of events that should be always reported in hostevents */ + EC_HOST_EVENT_ALWAYS_REPORT_MASK, + + /* Active wake mask */ + EC_HOST_EVENT_ACTIVE_WAKE_MASK, + + /* Lazy wake mask for S0ix */ + EC_HOST_EVENT_LAZY_WAKE_MASK_S0IX, + + /* Lazy wake mask for S3 */ + EC_HOST_EVENT_LAZY_WAKE_MASK_S3, + + /* Lazy wake mask for S5 */ + EC_HOST_EVENT_LAZY_WAKE_MASK_S5, +}; + +#define EC_CMD_HOST_EVENT 0x00A4 /*****************************************************************************/ /* Switch commands */ /* Enable/disable LCD backlight */ -#define EC_CMD_SWITCH_ENABLE_BKLIGHT 0x90 +#define EC_CMD_SWITCH_ENABLE_BKLIGHT 0x0090 struct ec_params_switch_enable_backlight { uint8_t enabled; -} __packed; +} __ec_align1; /* Enable/disable WLAN/Bluetooth */ -#define EC_CMD_SWITCH_ENABLE_WIRELESS 0x91 +#define EC_CMD_SWITCH_ENABLE_WIRELESS 0x0091 #define EC_VER_SWITCH_ENABLE_WIRELESS 1 /* Version 0 params; no response */ struct ec_params_switch_enable_wireless_v0 { uint8_t enabled; -} __packed; +} __ec_align1; /* Version 1 params */ struct ec_params_switch_enable_wireless_v1 { @@ -2330,7 +3665,7 @@ struct ec_params_switch_enable_wireless_v1 { /* Which flags to copy from suspend_flags */ uint8_t suspend_mask; -} __packed; +} __ec_align1; /* Version 1 response */ struct ec_response_switch_enable_wireless_v1 { @@ -2339,55 +3674,56 @@ struct ec_response_switch_enable_wireless_v1 { /* Flags to leave enabled in S3 */ uint8_t suspend_flags; -} __packed; +} __ec_align1; /*****************************************************************************/ /* GPIO commands. Only available on EC if write protect has been disabled. */ /* Set GPIO output value */ -#define EC_CMD_GPIO_SET 0x92 +#define EC_CMD_GPIO_SET 0x0092 struct ec_params_gpio_set { char name[32]; uint8_t val; -} __packed; +} __ec_align1; /* Get GPIO value */ -#define EC_CMD_GPIO_GET 0x93 +#define EC_CMD_GPIO_GET 0x0093 /* Version 0 of input params and response */ struct ec_params_gpio_get { char name[32]; -} __packed; +} __ec_align1; + struct ec_response_gpio_get { uint8_t val; -} __packed; +} __ec_align1; /* Version 1 of input params and response */ struct ec_params_gpio_get_v1 { uint8_t subcmd; union { - struct { + struct __ec_align1 { char name[32]; } get_value_by_name; - struct { + struct __ec_align1 { uint8_t index; } get_info; }; -} __packed; +} __ec_align1; struct ec_response_gpio_get_v1 { union { - struct { + struct __ec_align1 { uint8_t val; } get_value_by_name, get_count; - struct { + struct __ec_todo_unpacked { uint8_t val; char name[32]; uint32_t flags; } get_info; }; -} __packed; +} __ec_todo_packed; enum gpio_get_subcmd { EC_GPIO_GET_BY_NAME = 0, @@ -2399,25 +3735,28 @@ enum gpio_get_subcmd { /* I2C commands. Only available when flash write protect is unlocked. */ /* - * TODO(crosbug.com/p/23570): These commands are deprecated, and will be - * removed soon. Use EC_CMD_I2C_XFER instead. + * CAUTION: These commands are deprecated, and are not supported anymore in EC + * builds >= 8398.0.0 (see crosbug.com/p/23570). + * + * Use EC_CMD_I2C_PASSTHRU instead. */ /* Read I2C bus */ -#define EC_CMD_I2C_READ 0x94 +#define EC_CMD_I2C_READ 0x0094 struct ec_params_i2c_read { uint16_t addr; /* 8-bit address (7-bit shifted << 1) */ uint8_t read_size; /* Either 8 or 16. */ uint8_t port; uint8_t offset; -} __packed; +} __ec_align_size1; + struct ec_response_i2c_read { uint16_t data; -} __packed; +} __ec_align2; /* Write I2C bus */ -#define EC_CMD_I2C_WRITE 0x95 +#define EC_CMD_I2C_WRITE 0x0095 struct ec_params_i2c_write { uint16_t data; @@ -2425,7 +3764,7 @@ struct ec_params_i2c_write { uint8_t write_size; /* Either 8 or 16. */ uint8_t port; uint8_t offset; -} __packed; +} __ec_align_size1; /*****************************************************************************/ /* Charge state commands. Only available when flash write protect unlocked. */ @@ -2433,7 +3772,7 @@ struct ec_params_i2c_write { /* Force charge state machine to stop charging the battery or force it to * discharge the battery. */ -#define EC_CMD_CHARGE_CONTROL 0x96 +#define EC_CMD_CHARGE_CONTROL 0x0096 #define EC_VER_CHARGE_CONTROL 1 enum ec_charge_control_mode { @@ -2444,13 +3783,12 @@ enum ec_charge_control_mode { struct ec_params_charge_control { uint32_t mode; /* enum charge_control_mode */ -} __packed; +} __ec_align4; /*****************************************************************************/ -/* Console commands. Only available when flash write protect is unlocked. */ /* Snapshot console output buffer for use by EC_CMD_CONSOLE_READ. */ -#define EC_CMD_CONSOLE_SNAPSHOT 0x97 +#define EC_CMD_CONSOLE_SNAPSHOT 0x0097 /* * Read data from the saved snapshot. If the subcmd parameter is @@ -2464,7 +3802,7 @@ struct ec_params_charge_control { * Response is null-terminated string. Empty string, if there is no more * remaining output. */ -#define EC_CMD_CONSOLE_READ 0x98 +#define EC_CMD_CONSOLE_READ 0x0098 enum ec_console_read_subcmd { CONSOLE_READ_NEXT = 0, @@ -2473,7 +3811,7 @@ enum ec_console_read_subcmd { struct ec_params_console_read_v1 { uint8_t subcmd; /* enum ec_console_read_subcmd */ -} __packed; +} __ec_align1; /*****************************************************************************/ @@ -2484,14 +3822,13 @@ struct ec_params_console_read_v1 { * EC_RES_SUCCESS if the command was successful. * EC_RES_ERROR if the cut off command failed. */ +#define EC_CMD_BATTERY_CUT_OFF 0x0099 -#define EC_CMD_BATTERY_CUT_OFF 0x99 - -#define EC_BATTERY_CUTOFF_FLAG_AT_SHUTDOWN (1 << 0) +#define EC_BATTERY_CUTOFF_FLAG_AT_SHUTDOWN BIT(0) struct ec_params_battery_cutoff { uint8_t flags; -} __packed; +} __ec_align1; /*****************************************************************************/ /* USB port mux control. */ @@ -2499,11 +3836,11 @@ struct ec_params_battery_cutoff { /* * Switch USB mux or return to automatic switching. */ -#define EC_CMD_USB_MUX 0x9a +#define EC_CMD_USB_MUX 0x009A struct ec_params_usb_mux { uint8_t mux; -} __packed; +} __ec_align1; /*****************************************************************************/ /* LDOs / FETs control. */ @@ -2516,25 +3853,25 @@ enum ec_ldo_state { /* * Switch on/off a LDO. */ -#define EC_CMD_LDO_SET 0x9b +#define EC_CMD_LDO_SET 0x009B struct ec_params_ldo_set { uint8_t index; uint8_t state; -} __packed; +} __ec_align1; /* * Get LDO state. */ -#define EC_CMD_LDO_GET 0x9c +#define EC_CMD_LDO_GET 0x009C struct ec_params_ldo_get { uint8_t index; -} __packed; +} __ec_align1; struct ec_response_ldo_get { uint8_t state; -} __packed; +} __ec_align1; /*****************************************************************************/ /* Power info. */ @@ -2542,7 +3879,7 @@ struct ec_response_ldo_get { /* * Get power info. */ -#define EC_CMD_POWER_INFO 0x9d +#define EC_CMD_POWER_INFO 0x009D struct ec_response_power_info { uint32_t usb_dev_type; @@ -2550,21 +3887,21 @@ struct ec_response_power_info { uint16_t voltage_system; uint16_t current_system; uint16_t usb_current_limit; -} __packed; +} __ec_align4; /*****************************************************************************/ /* I2C passthru command */ -#define EC_CMD_I2C_PASSTHRU 0x9e +#define EC_CMD_I2C_PASSTHRU 0x009E /* Read data; if not present, message is a write */ -#define EC_I2C_FLAG_READ (1 << 15) +#define EC_I2C_FLAG_READ BIT(15) /* Mask for address */ #define EC_I2C_ADDR_MASK 0x3ff -#define EC_I2C_STATUS_NAK (1 << 0) /* Transfer was not acknowledged */ -#define EC_I2C_STATUS_TIMEOUT (1 << 1) /* Timeout during transfer */ +#define EC_I2C_STATUS_NAK BIT(0) /* Transfer was not acknowledged */ +#define EC_I2C_STATUS_TIMEOUT BIT(1) /* Timeout during transfer */ /* Any error */ #define EC_I2C_STATUS_ERROR (EC_I2C_STATUS_NAK | EC_I2C_STATUS_TIMEOUT) @@ -2572,49 +3909,49 @@ struct ec_response_power_info { struct ec_params_i2c_passthru_msg { uint16_t addr_flags; /* I2C slave address (7 or 10 bits) and flags */ uint16_t len; /* Number of bytes to read or write */ -} __packed; +} __ec_align2; struct ec_params_i2c_passthru { uint8_t port; /* I2C port number */ uint8_t num_msgs; /* Number of messages */ struct ec_params_i2c_passthru_msg msg[]; /* Data to write for all messages is concatenated here */ -} __packed; +} __ec_align2; struct ec_response_i2c_passthru { uint8_t i2c_status; /* Status flags (EC_I2C_STATUS_...) */ uint8_t num_msgs; /* Number of messages processed */ uint8_t data[]; /* Data read by messages concatenated here */ -} __packed; +} __ec_align1; /*****************************************************************************/ /* Power button hang detect */ -#define EC_CMD_HANG_DETECT 0x9f +#define EC_CMD_HANG_DETECT 0x009F /* Reasons to start hang detection timer */ /* Power button pressed */ -#define EC_HANG_START_ON_POWER_PRESS (1 << 0) +#define EC_HANG_START_ON_POWER_PRESS BIT(0) /* Lid closed */ -#define EC_HANG_START_ON_LID_CLOSE (1 << 1) +#define EC_HANG_START_ON_LID_CLOSE BIT(1) /* Lid opened */ -#define EC_HANG_START_ON_LID_OPEN (1 << 2) +#define EC_HANG_START_ON_LID_OPEN BIT(2) /* Start of AP S3->S0 transition (booting or resuming from suspend) */ -#define EC_HANG_START_ON_RESUME (1 << 3) +#define EC_HANG_START_ON_RESUME BIT(3) /* Reasons to cancel hang detection */ /* Power button released */ -#define EC_HANG_STOP_ON_POWER_RELEASE (1 << 8) +#define EC_HANG_STOP_ON_POWER_RELEASE BIT(8) /* Any host command from AP received */ -#define EC_HANG_STOP_ON_HOST_COMMAND (1 << 9) +#define EC_HANG_STOP_ON_HOST_COMMAND BIT(9) /* Stop on end of AP S0->S3 transition (suspending or shutting down) */ -#define EC_HANG_STOP_ON_SUSPEND (1 << 10) +#define EC_HANG_STOP_ON_SUSPEND BIT(10) /* * If this flag is set, all the other fields are ignored, and the hang detect @@ -2622,14 +3959,14 @@ struct ec_response_i2c_passthru { * without reconfiguring any of the other hang detect settings. Note that * you must previously have configured the timeouts. */ -#define EC_HANG_START_NOW (1 << 30) +#define EC_HANG_START_NOW BIT(30) /* * If this flag is set, all the other fields are ignored (including * EC_HANG_START_NOW). This provides the AP a way to stop the hang timer * without reconfiguring any of the other hang detect settings. */ -#define EC_HANG_STOP_NOW (1 << 31) +#define EC_HANG_STOP_NOW BIT(31) struct ec_params_hang_detect { /* Flags; see EC_HANG_* */ @@ -2640,7 +3977,7 @@ struct ec_params_hang_detect { /* Timeout in msec before generating warm reboot, if enabled */ uint16_t warm_reboot_timeout_msec; -} __packed; +} __ec_align4; /*****************************************************************************/ /* Commands for battery charging */ @@ -2649,7 +3986,7 @@ struct ec_params_hang_detect { * This is the single catch-all host command to exchange data regarding the * charge state machine (v2 and up). */ -#define EC_CMD_CHARGE_STATE 0xa0 +#define EC_CMD_CHARGE_STATE 0x00A0 /* Subcommands for this host command */ enum charge_state_command { @@ -2669,6 +4006,11 @@ enum charge_state_params { CS_PARAM_CHG_INPUT_CURRENT, /* charger input current limit */ CS_PARAM_CHG_STATUS, /* charger-specific status */ CS_PARAM_CHG_OPTION, /* charger-specific options */ + CS_PARAM_LIMIT_POWER, /* + * Check if power is limited due to + * low battery and / or a weak external + * charger. READ ONLY. + */ /* How many so far? */ CS_NUM_BASE_PARAMS, @@ -2676,30 +4018,39 @@ enum charge_state_params { CS_PARAM_CUSTOM_PROFILE_MIN = 0x10000, CS_PARAM_CUSTOM_PROFILE_MAX = 0x1ffff, + /* Range for CONFIG_CHARGE_STATE_DEBUG params */ + CS_PARAM_DEBUG_MIN = 0x20000, + CS_PARAM_DEBUG_CTL_MODE = 0x20000, + CS_PARAM_DEBUG_MANUAL_MODE, + CS_PARAM_DEBUG_SEEMS_DEAD, + CS_PARAM_DEBUG_SEEMS_DISCONNECTED, + CS_PARAM_DEBUG_BATT_REMOVED, + CS_PARAM_DEBUG_MANUAL_CURRENT, + CS_PARAM_DEBUG_MANUAL_VOLTAGE, + CS_PARAM_DEBUG_MAX = 0x2ffff, + /* Other custom param ranges go here... */ }; struct ec_params_charge_state { uint8_t cmd; /* enum charge_state_command */ union { - struct { - /* no args */ - } get_state; + /* get_state has no args */ - struct { + struct __ec_todo_unpacked { uint32_t param; /* enum charge_state_param */ } get_param; - struct { + struct __ec_todo_unpacked { uint32_t param; /* param to set */ uint32_t value; /* value to set */ } set_param; }; -} __packed; +} __ec_todo_packed; struct ec_response_charge_state { union { - struct { + struct __ec_align4 { int ac; int chg_voltage; int chg_current; @@ -2707,24 +4058,23 @@ struct ec_response_charge_state { int batt_state_of_charge; } get_state; - struct { + struct __ec_align4 { uint32_t value; } get_param; - struct { - /* no return values */ - } set_param; + + /* set_param returns no args */ }; -} __packed; +} __ec_align4; /* * Set maximum battery charging current. */ -#define EC_CMD_CHARGE_CURRENT_LIMIT 0xa1 +#define EC_CMD_CHARGE_CURRENT_LIMIT 0x00A1 struct ec_params_current_limit { uint32_t limit; /* in mA */ -} __packed; +} __ec_align4; /* * Set maximum external voltage / current. @@ -2735,23 +4085,69 @@ struct ec_params_current_limit { struct ec_params_external_power_limit_v1 { uint16_t current_lim; /* in mA, or EC_POWER_LIMIT_NONE to clear limit */ uint16_t voltage_lim; /* in mV, or EC_POWER_LIMIT_NONE to clear limit */ -} __packed; +} __ec_align2; #define EC_POWER_LIMIT_NONE 0xffff +/* + * Set maximum voltage & current of a dedicated charge port + */ +#define EC_CMD_OVERRIDE_DEDICATED_CHARGER_LIMIT 0x00A3 + +struct ec_params_dedicated_charger_limit { + uint16_t current_lim; /* in mA */ + uint16_t voltage_lim; /* in mV */ +} __ec_align2; + +/*****************************************************************************/ +/* Hibernate/Deep Sleep Commands */ + +/* Set the delay before going into hibernation. */ +#define EC_CMD_HIBERNATION_DELAY 0x00A8 + +struct ec_params_hibernation_delay { + /* + * Seconds to wait in G3 before hibernate. Pass in 0 to read the + * current settings without changing them. + */ + uint32_t seconds; +} __ec_align4; + +struct ec_response_hibernation_delay { + /* + * The current time in seconds in which the system has been in the G3 + * state. This value is reset if the EC transitions out of G3. + */ + uint32_t time_g3; + + /* + * The current time remaining in seconds until the EC should hibernate. + * This value is also reset if the EC transitions out of G3. + */ + uint32_t time_remaining; + + /* + * The current time in seconds that the EC should wait in G3 before + * hibernating. + */ + uint32_t hibernate_delay; +} __ec_align4; + /* Inform the EC when entering a sleep state */ -#define EC_CMD_HOST_SLEEP_EVENT 0xa9 +#define EC_CMD_HOST_SLEEP_EVENT 0x00A9 enum host_sleep_event { HOST_SLEEP_EVENT_S3_SUSPEND = 1, HOST_SLEEP_EVENT_S3_RESUME = 2, HOST_SLEEP_EVENT_S0IX_SUSPEND = 3, - HOST_SLEEP_EVENT_S0IX_RESUME = 4 + HOST_SLEEP_EVENT_S0IX_RESUME = 4, + /* S3 suspend with additional enabled wake sources */ + HOST_SLEEP_EVENT_S3_WAKEABLE_SUSPEND = 5, }; struct ec_params_host_sleep_event { uint8_t sleep_event; -} __packed; +} __ec_align1; /* * Use a default timeout value (CONFIG_SLEEP_TIMEOUT_MS) for detecting sleep @@ -2782,7 +4178,7 @@ struct ec_params_host_sleep_event_v1 { /* No parameters for non-suspend messages. */ }; -} __packed; +} __ec_align2; /* A timeout occurred when this bit is set */ #define EC_HOST_RESUME_SLEEP_TIMEOUT 0x80000000 @@ -2808,42 +4204,72 @@ struct ec_response_host_sleep_event_v1 { /* No response fields for non-resume messages. */ }; -} __packed; +} __ec_align4; + +/*****************************************************************************/ +/* Device events */ +#define EC_CMD_DEVICE_EVENT 0x00AA + +enum ec_device_event { + EC_DEVICE_EVENT_TRACKPAD, + EC_DEVICE_EVENT_DSP, + EC_DEVICE_EVENT_WIFI, +}; + +enum ec_device_event_param { + /* Get and clear pending device events */ + EC_DEVICE_EVENT_PARAM_GET_CURRENT_EVENTS, + /* Get device event mask */ + EC_DEVICE_EVENT_PARAM_GET_ENABLED_EVENTS, + /* Set device event mask */ + EC_DEVICE_EVENT_PARAM_SET_ENABLED_EVENTS, +}; + +#define EC_DEVICE_EVENT_MASK(event_code) BIT(event_code % 32) + +struct ec_params_device_event { + uint32_t event_mask; + uint8_t param; +} __ec_align_size1; + +struct ec_response_device_event { + uint32_t event_mask; +} __ec_align4; /*****************************************************************************/ /* Smart battery pass-through */ /* Get / Set 16-bit smart battery registers */ -#define EC_CMD_SB_READ_WORD 0xb0 -#define EC_CMD_SB_WRITE_WORD 0xb1 +#define EC_CMD_SB_READ_WORD 0x00B0 +#define EC_CMD_SB_WRITE_WORD 0x00B1 /* Get / Set string smart battery parameters * formatted as SMBUS "block". */ -#define EC_CMD_SB_READ_BLOCK 0xb2 -#define EC_CMD_SB_WRITE_BLOCK 0xb3 +#define EC_CMD_SB_READ_BLOCK 0x00B2 +#define EC_CMD_SB_WRITE_BLOCK 0x00B3 struct ec_params_sb_rd { uint8_t reg; -} __packed; +} __ec_align1; struct ec_response_sb_rd_word { uint16_t value; -} __packed; +} __ec_align2; struct ec_params_sb_wr_word { uint8_t reg; uint16_t value; -} __packed; +} __ec_align1; struct ec_response_sb_rd_block { uint8_t data[32]; -} __packed; +} __ec_align1; struct ec_params_sb_wr_block { uint8_t reg; uint16_t data[32]; -} __packed; +} __ec_align1; /*****************************************************************************/ /* Battery vendor parameters @@ -2854,7 +4280,7 @@ struct ec_params_sb_wr_block { * requested value. */ -#define EC_CMD_BATTERY_VENDOR_PARAM 0xb4 +#define EC_CMD_BATTERY_VENDOR_PARAM 0x00B4 enum ec_battery_vendor_param_mode { BATTERY_VENDOR_PARAM_MODE_GET = 0, @@ -2865,16 +4291,187 @@ struct ec_params_battery_vendor_param { uint32_t param; uint32_t value; uint8_t mode; -} __packed; +} __ec_align_size1; struct ec_response_battery_vendor_param { uint32_t value; -} __packed; +} __ec_align4; + +/*****************************************************************************/ +/* + * Smart Battery Firmware Update Commands + */ +#define EC_CMD_SB_FW_UPDATE 0x00B5 + +enum ec_sb_fw_update_subcmd { + EC_SB_FW_UPDATE_PREPARE = 0x0, + EC_SB_FW_UPDATE_INFO = 0x1, /*query sb info */ + EC_SB_FW_UPDATE_BEGIN = 0x2, /*check if protected */ + EC_SB_FW_UPDATE_WRITE = 0x3, /*check if protected */ + EC_SB_FW_UPDATE_END = 0x4, + EC_SB_FW_UPDATE_STATUS = 0x5, + EC_SB_FW_UPDATE_PROTECT = 0x6, + EC_SB_FW_UPDATE_MAX = 0x7, +}; + +#define SB_FW_UPDATE_CMD_WRITE_BLOCK_SIZE 32 +#define SB_FW_UPDATE_CMD_STATUS_SIZE 2 +#define SB_FW_UPDATE_CMD_INFO_SIZE 8 + +struct ec_sb_fw_update_header { + uint16_t subcmd; /* enum ec_sb_fw_update_subcmd */ + uint16_t fw_id; /* firmware id */ +} __ec_align4; + +struct ec_params_sb_fw_update { + struct ec_sb_fw_update_header hdr; + union { + /* EC_SB_FW_UPDATE_PREPARE = 0x0 */ + /* EC_SB_FW_UPDATE_INFO = 0x1 */ + /* EC_SB_FW_UPDATE_BEGIN = 0x2 */ + /* EC_SB_FW_UPDATE_END = 0x4 */ + /* EC_SB_FW_UPDATE_STATUS = 0x5 */ + /* EC_SB_FW_UPDATE_PROTECT = 0x6 */ + /* Those have no args */ + + /* EC_SB_FW_UPDATE_WRITE = 0x3 */ + struct __ec_align4 { + uint8_t data[SB_FW_UPDATE_CMD_WRITE_BLOCK_SIZE]; + } write; + }; +} __ec_align4; + +struct ec_response_sb_fw_update { + union { + /* EC_SB_FW_UPDATE_INFO = 0x1 */ + struct __ec_align1 { + uint8_t data[SB_FW_UPDATE_CMD_INFO_SIZE]; + } info; + + /* EC_SB_FW_UPDATE_STATUS = 0x5 */ + struct __ec_align1 { + uint8_t data[SB_FW_UPDATE_CMD_STATUS_SIZE]; + } status; + }; +} __ec_align1; + +/* + * Entering Verified Boot Mode Command + * Default mode is VBOOT_MODE_NORMAL if EC did not receive this command. + * Valid Modes are: normal, developer, and recovery. + */ +#define EC_CMD_ENTERING_MODE 0x00B6 + +struct ec_params_entering_mode { + int vboot_mode; +} __ec_align4; + +#define VBOOT_MODE_NORMAL 0 +#define VBOOT_MODE_DEVELOPER 1 +#define VBOOT_MODE_RECOVERY 2 + +/*****************************************************************************/ +/* + * I2C passthru protection command: Protects I2C tunnels against access on + * certain addresses (board-specific). + */ +#define EC_CMD_I2C_PASSTHRU_PROTECT 0x00B7 + +enum ec_i2c_passthru_protect_subcmd { + EC_CMD_I2C_PASSTHRU_PROTECT_STATUS = 0x0, + EC_CMD_I2C_PASSTHRU_PROTECT_ENABLE = 0x1, +}; + +struct ec_params_i2c_passthru_protect { + uint8_t subcmd; + uint8_t port; /* I2C port number */ +} __ec_align1; + +struct ec_response_i2c_passthru_protect { + uint8_t status; /* Status flags (0: unlocked, 1: locked) */ +} __ec_align1; + + +/*****************************************************************************/ +/* + * HDMI CEC commands + * + * These commands are for sending and receiving message via HDMI CEC + */ + +#define MAX_CEC_MSG_LEN 16 + +/* CEC message from the AP to be written on the CEC bus */ +#define EC_CMD_CEC_WRITE_MSG 0x00B8 + +/** + * struct ec_params_cec_write - Message to write to the CEC bus + * @msg: message content to write to the CEC bus + */ +struct ec_params_cec_write { + uint8_t msg[MAX_CEC_MSG_LEN]; +} __ec_align1; + +/* Set various CEC parameters */ +#define EC_CMD_CEC_SET 0x00BA + +/** + * struct ec_params_cec_set - CEC parameters set + * @cmd: parameter type, can be CEC_CMD_ENABLE or CEC_CMD_LOGICAL_ADDRESS + * @val: in case cmd is CEC_CMD_ENABLE, this field can be 0 to disable CEC + * or 1 to enable CEC functionality, in case cmd is + * CEC_CMD_LOGICAL_ADDRESS, this field encodes the requested logical + * address between 0 and 15 or 0xff to unregister + */ +struct ec_params_cec_set { + uint8_t cmd; /* enum cec_command */ + uint8_t val; +} __ec_align1; + +/* Read various CEC parameters */ +#define EC_CMD_CEC_GET 0x00BB + +/** + * struct ec_params_cec_get - CEC parameters get + * @cmd: parameter type, can be CEC_CMD_ENABLE or CEC_CMD_LOGICAL_ADDRESS + */ +struct ec_params_cec_get { + uint8_t cmd; /* enum cec_command */ +} __ec_align1; + +/** + * struct ec_response_cec_get - CEC parameters get response + * @val: in case cmd was CEC_CMD_ENABLE, this field will 0 if CEC is + * disabled or 1 if CEC functionality is enabled, + * in case cmd was CEC_CMD_LOGICAL_ADDRESS, this will encode the + * configured logical address between 0 and 15 or 0xff if unregistered + */ +struct ec_response_cec_get { + uint8_t val; +} __ec_align1; + +/* CEC parameters command */ +enum cec_command { + /* CEC reading, writing and events enable */ + CEC_CMD_ENABLE, + /* CEC logical address */ + CEC_CMD_LOGICAL_ADDRESS, +}; + +/* Events from CEC to AP */ +enum mkbp_cec_event { + /* Outgoing message was acknowledged by a follower */ + EC_MKBP_CEC_SEND_OK = BIT(0), + /* Outgoing message was not acknowledged */ + EC_MKBP_CEC_SEND_FAILED = BIT(1), +}; /*****************************************************************************/ + /* Commands for I2S recording on audio codec. */ #define EC_CMD_CODEC_I2S 0x00BC +#define EC_WOV_I2S_SAMPLE_RATE 48000 enum ec_codec_i2s_subcmd { EC_CODEC_SET_SAMPLE_DEPTH = 0x0, @@ -2884,6 +4481,7 @@ enum ec_codec_i2s_subcmd { EC_CODEC_I2S_SET_CONFIG = 0x4, EC_CODEC_I2S_SET_TDM_CONFIG = 0x5, EC_CODEC_I2S_SET_BCLK = 0x6, + EC_CODEC_I2S_SUBCMD_COUNT = 0x7, }; enum ec_sample_depth_value { @@ -2900,10 +4498,23 @@ enum ec_i2s_config { EC_DAI_FMT_PCM_TDM = 5, }; -struct ec_param_codec_i2s { - /* - * enum ec_codec_i2s_subcmd - */ +/* + * For subcommand EC_CODEC_GET_GAIN. + */ +struct __ec_align1 ec_codec_i2s_gain { + uint8_t left; + uint8_t right; +}; + +struct __ec_todo_unpacked ec_param_codec_i2s_tdm { + int16_t ch0_delay; /* 0 to 496 */ + int16_t ch1_delay; /* -1 to 496 */ + uint8_t adjacent_to_ch0; + uint8_t adjacent_to_ch1; +}; + +struct __ec_todo_packed ec_param_codec_i2s { + /* enum ec_codec_i2s_subcmd */ uint8_t cmd; union { /* @@ -2916,10 +4527,7 @@ struct ec_param_codec_i2s { * EC_CODEC_SET_GAIN * Value should be 0~43 for both channels. */ - struct ec_param_codec_i2s_set_gain { - uint8_t left; - uint8_t right; - } __packed gain; + struct ec_codec_i2s_gain gain; /* * EC_CODEC_I2S_ENABLE @@ -2928,7 +4536,7 @@ struct ec_param_codec_i2s { uint8_t i2s_enable; /* - * EC_CODEC_I2S_SET_COFNIG + * EC_CODEC_I2S_SET_CONFIG * Value should be one of ec_i2s_config. */ uint8_t i2s_config; @@ -2937,33 +4545,15 @@ struct ec_param_codec_i2s { * EC_CODEC_I2S_SET_TDM_CONFIG * Value should be one of ec_i2s_config. */ - struct ec_param_codec_i2s_tdm { - /* - * 0 to 496 - */ - int16_t ch0_delay; - /* - * -1 to 496 - */ - int16_t ch1_delay; - uint8_t adjacent_to_ch0; - uint8_t adjacent_to_ch1; - } __packed tdm_param; + struct ec_param_codec_i2s_tdm tdm_param; /* * EC_CODEC_I2S_SET_BCLK */ uint32_t bclk; }; -} __packed; +}; -/* - * For subcommand EC_CODEC_GET_GAIN. - */ -struct ec_response_codec_gain { - uint8_t left; - uint8_t right; -} __packed; /*****************************************************************************/ /* System commands */ @@ -2972,27 +4562,29 @@ struct ec_response_codec_gain { * TODO(crosbug.com/p/23747): This is a confusing name, since it doesn't * necessarily reboot the EC. Rename to "image" or something similar? */ -#define EC_CMD_REBOOT_EC 0xd2 +#define EC_CMD_REBOOT_EC 0x00D2 /* Command */ enum ec_reboot_cmd { EC_REBOOT_CANCEL = 0, /* Cancel a pending reboot */ EC_REBOOT_JUMP_RO = 1, /* Jump to RO without rebooting */ - EC_REBOOT_JUMP_RW = 2, /* Jump to RW without rebooting */ + EC_REBOOT_JUMP_RW = 2, /* Jump to active RW without rebooting */ /* (command 3 was jump to RW-B) */ EC_REBOOT_COLD = 4, /* Cold-reboot */ EC_REBOOT_DISABLE_JUMP = 5, /* Disable jump until next reboot */ - EC_REBOOT_HIBERNATE = 6 /* Hibernate EC */ + EC_REBOOT_HIBERNATE = 6, /* Hibernate EC */ + EC_REBOOT_HIBERNATE_CLEAR_AP_OFF = 7, /* and clears AP_OFF flag */ }; /* Flags for ec_params_reboot_ec.reboot_flags */ -#define EC_REBOOT_FLAG_RESERVED0 (1 << 0) /* Was recovery request */ -#define EC_REBOOT_FLAG_ON_AP_SHUTDOWN (1 << 1) /* Reboot after AP shutdown */ +#define EC_REBOOT_FLAG_RESERVED0 BIT(0) /* Was recovery request */ +#define EC_REBOOT_FLAG_ON_AP_SHUTDOWN BIT(1) /* Reboot after AP shutdown */ +#define EC_REBOOT_FLAG_SWITCH_RW_SLOT BIT(2) /* Switch RW slot */ struct ec_params_reboot_ec { uint8_t cmd; /* enum ec_reboot_cmd */ uint8_t flags; /* See EC_REBOOT_FLAG_* */ -} __packed; +} __ec_align1; /* * Get information on last EC panic. @@ -3000,196 +4592,7 @@ struct ec_params_reboot_ec { * Returns variable-length platform-dependent panic information. See panic.h * for details. */ -#define EC_CMD_GET_PANIC_INFO 0xd3 - -/*****************************************************************************/ -/* - * ACPI commands - * - * These are valid ONLY on the ACPI command/data port. - */ - -/* - * ACPI Read Embedded Controller - * - * This reads from ACPI memory space on the EC (EC_ACPI_MEM_*). - * - * Use the following sequence: - * - * - Write EC_CMD_ACPI_READ to EC_LPC_ADDR_ACPI_CMD - * - Wait for EC_LPC_CMDR_PENDING bit to clear - * - Write address to EC_LPC_ADDR_ACPI_DATA - * - Wait for EC_LPC_CMDR_DATA bit to set - * - Read value from EC_LPC_ADDR_ACPI_DATA - */ -#define EC_CMD_ACPI_READ 0x80 - -/* - * ACPI Write Embedded Controller - * - * This reads from ACPI memory space on the EC (EC_ACPI_MEM_*). - * - * Use the following sequence: - * - * - Write EC_CMD_ACPI_WRITE to EC_LPC_ADDR_ACPI_CMD - * - Wait for EC_LPC_CMDR_PENDING bit to clear - * - Write address to EC_LPC_ADDR_ACPI_DATA - * - Wait for EC_LPC_CMDR_PENDING bit to clear - * - Write value to EC_LPC_ADDR_ACPI_DATA - */ -#define EC_CMD_ACPI_WRITE 0x81 - -/* - * ACPI Query Embedded Controller - * - * This clears the lowest-order bit in the currently pending host events, and - * sets the result code to the 1-based index of the bit (event 0x00000001 = 1, - * event 0x80000000 = 32), or 0 if no event was pending. - */ -#define EC_CMD_ACPI_QUERY_EVENT 0x84 - -/* Valid addresses in ACPI memory space, for read/write commands */ - -/* Memory space version; set to EC_ACPI_MEM_VERSION_CURRENT */ -#define EC_ACPI_MEM_VERSION 0x00 -/* - * Test location; writing value here updates test compliment byte to (0xff - - * value). - */ -#define EC_ACPI_MEM_TEST 0x01 -/* Test compliment; writes here are ignored. */ -#define EC_ACPI_MEM_TEST_COMPLIMENT 0x02 - -/* Keyboard backlight brightness percent (0 - 100) */ -#define EC_ACPI_MEM_KEYBOARD_BACKLIGHT 0x03 -/* DPTF Target Fan Duty (0-100, 0xff for auto/none) */ -#define EC_ACPI_MEM_FAN_DUTY 0x04 - -/* - * DPTF temp thresholds. Any of the EC's temp sensors can have up to two - * independent thresholds attached to them. The current value of the ID - * register determines which sensor is affected by the THRESHOLD and COMMIT - * registers. The THRESHOLD register uses the same EC_TEMP_SENSOR_OFFSET scheme - * as the memory-mapped sensors. The COMMIT register applies those settings. - * - * The spec does not mandate any way to read back the threshold settings - * themselves, but when a threshold is crossed the AP needs a way to determine - * which sensor(s) are responsible. Each reading of the ID register clears and - * returns one sensor ID that has crossed one of its threshold (in either - * direction) since the last read. A value of 0xFF means "no new thresholds - * have tripped". Setting or enabling the thresholds for a sensor will clear - * the unread event count for that sensor. - */ -#define EC_ACPI_MEM_TEMP_ID 0x05 -#define EC_ACPI_MEM_TEMP_THRESHOLD 0x06 -#define EC_ACPI_MEM_TEMP_COMMIT 0x07 -/* - * Here are the bits for the COMMIT register: - * bit 0 selects the threshold index for the chosen sensor (0/1) - * bit 1 enables/disables the selected threshold (0 = off, 1 = on) - * Each write to the commit register affects one threshold. - */ -#define EC_ACPI_MEM_TEMP_COMMIT_SELECT_MASK (1 << 0) -#define EC_ACPI_MEM_TEMP_COMMIT_ENABLE_MASK (1 << 1) -/* - * Example: - * - * Set the thresholds for sensor 2 to 50 C and 60 C: - * write 2 to [0x05] -- select temp sensor 2 - * write 0x7b to [0x06] -- C_TO_K(50) - EC_TEMP_SENSOR_OFFSET - * write 0x2 to [0x07] -- enable threshold 0 with this value - * write 0x85 to [0x06] -- C_TO_K(60) - EC_TEMP_SENSOR_OFFSET - * write 0x3 to [0x07] -- enable threshold 1 with this value - * - * Disable the 60 C threshold, leaving the 50 C threshold unchanged: - * write 2 to [0x05] -- select temp sensor 2 - * write 0x1 to [0x07] -- disable threshold 1 - */ - -/* DPTF battery charging current limit */ -#define EC_ACPI_MEM_CHARGING_LIMIT 0x08 - -/* Charging limit is specified in 64 mA steps */ -#define EC_ACPI_MEM_CHARGING_LIMIT_STEP_MA 64 -/* Value to disable DPTF battery charging limit */ -#define EC_ACPI_MEM_CHARGING_LIMIT_DISABLED 0xff - -/* Current version of ACPI memory address space */ -#define EC_ACPI_MEM_VERSION_CURRENT 1 - - -/*****************************************************************************/ -/* - * HDMI CEC commands - * - * These commands are for sending and receiving message via HDMI CEC - */ -#define EC_MAX_CEC_MSG_LEN 16 - -/* CEC message from the AP to be written on the CEC bus */ -#define EC_CMD_CEC_WRITE_MSG 0x00B8 - -/** - * struct ec_params_cec_write - Message to write to the CEC bus - * @msg: message content to write to the CEC bus - */ -struct ec_params_cec_write { - uint8_t msg[EC_MAX_CEC_MSG_LEN]; -} __packed; - -/* Set various CEC parameters */ -#define EC_CMD_CEC_SET 0x00BA - -/** - * struct ec_params_cec_set - CEC parameters set - * @cmd: parameter type, can be CEC_CMD_ENABLE or CEC_CMD_LOGICAL_ADDRESS - * @val: in case cmd is CEC_CMD_ENABLE, this field can be 0 to disable CEC - * or 1 to enable CEC functionality, in case cmd is CEC_CMD_LOGICAL_ADDRESS, - * this field encodes the requested logical address between 0 and 15 - * or 0xff to unregister - */ -struct ec_params_cec_set { - uint8_t cmd; /* enum cec_command */ - uint8_t val; -} __packed; - -/* Read various CEC parameters */ -#define EC_CMD_CEC_GET 0x00BB - -/** - * struct ec_params_cec_get - CEC parameters get - * @cmd: parameter type, can be CEC_CMD_ENABLE or CEC_CMD_LOGICAL_ADDRESS - */ -struct ec_params_cec_get { - uint8_t cmd; /* enum cec_command */ -} __packed; - -/** - * struct ec_response_cec_get - CEC parameters get response - * @val: in case cmd was CEC_CMD_ENABLE, this field will 0 if CEC is - * disabled or 1 if CEC functionality is enabled, - * in case cmd was CEC_CMD_LOGICAL_ADDRESS, this will encode the - * configured logical address between 0 and 15 or 0xff if unregistered - */ -struct ec_response_cec_get { - uint8_t val; -} __packed; - -/* CEC parameters command */ -enum ec_cec_command { - /* CEC reading, writing and events enable */ - CEC_CMD_ENABLE, - /* CEC logical address */ - CEC_CMD_LOGICAL_ADDRESS, -}; - -/* Events from CEC to AP */ -enum mkbp_cec_event { - /* Outgoing message was acknowledged by a follower */ - EC_MKBP_CEC_SEND_OK = BIT(0), - /* Outgoing message was not acknowledged */ - EC_MKBP_CEC_SEND_FAILED = BIT(1), -}; +#define EC_CMD_GET_PANIC_INFO 0x00D3 /*****************************************************************************/ /* @@ -3208,7 +4611,7 @@ enum mkbp_cec_event { * * Use EC_CMD_REBOOT_EC to reboot the EC more politely. */ -#define EC_CMD_REBOOT 0xd1 /* Think "die" */ +#define EC_CMD_REBOOT 0x00D1 /* Think "die" */ /* * Resend last response (not supported on LPC). @@ -3217,7 +4620,7 @@ enum mkbp_cec_event { * there was no previous command, or the previous command's response was too * big to save. */ -#define EC_CMD_RESEND_RESPONSE 0xdb +#define EC_CMD_RESEND_RESPONSE 0x00DB /* * This header byte on a command indicate version 0. Any header byte less @@ -3229,9 +4632,7 @@ enum mkbp_cec_event { * * The old EC interface must not use commands 0xdc or higher. */ -#define EC_CMD_VERSION0 0xdc - -#endif /* !__ACPI__ */ +#define EC_CMD_VERSION0 0x00DC /*****************************************************************************/ /* @@ -3241,21 +4642,56 @@ enum mkbp_cec_event { */ /* EC to PD MCU exchange status command */ -#define EC_CMD_PD_EXCHANGE_STATUS 0x100 +#define EC_CMD_PD_EXCHANGE_STATUS 0x0100 +#define EC_VER_PD_EXCHANGE_STATUS 2 + +enum pd_charge_state { + PD_CHARGE_NO_CHANGE = 0, /* Don't change charge state */ + PD_CHARGE_NONE, /* No charging allowed */ + PD_CHARGE_5V, /* 5V charging only */ + PD_CHARGE_MAX /* Charge at max voltage */ +}; /* Status of EC being sent to PD */ +#define EC_STATUS_HIBERNATING BIT(0) + struct ec_params_pd_status { - int8_t batt_soc; /* battery state of charge */ -} __packed; + uint8_t status; /* EC status */ + int8_t batt_soc; /* battery state of charge */ + uint8_t charge_state; /* charging state (from enum pd_charge_state) */ +} __ec_align1; /* Status of PD being sent back to EC */ +#define PD_STATUS_HOST_EVENT BIT(0) /* Forward host event to AP */ +#define PD_STATUS_IN_RW BIT(1) /* Running RW image */ +#define PD_STATUS_JUMPED_TO_IMAGE BIT(2) /* Current image was jumped to */ +#define PD_STATUS_TCPC_ALERT_0 BIT(3) /* Alert active in port 0 TCPC */ +#define PD_STATUS_TCPC_ALERT_1 BIT(4) /* Alert active in port 1 TCPC */ +#define PD_STATUS_TCPC_ALERT_2 BIT(5) /* Alert active in port 2 TCPC */ +#define PD_STATUS_TCPC_ALERT_3 BIT(6) /* Alert active in port 3 TCPC */ +#define PD_STATUS_EC_INT_ACTIVE (PD_STATUS_TCPC_ALERT_0 | \ + PD_STATUS_TCPC_ALERT_1 | \ + PD_STATUS_HOST_EVENT) struct ec_response_pd_status { - int8_t status; /* PD MCU status */ - uint32_t curr_lim_ma; /* input current limit */ -} __packed; + uint32_t curr_lim_ma; /* input current limit */ + uint16_t status; /* PD MCU status */ + int8_t active_charge_port; /* active charging port */ +} __ec_align_size1; + +/* AP to PD MCU host event status command, cleared on read */ +#define EC_CMD_PD_HOST_EVENT_STATUS 0x0104 + +/* PD MCU host event status bits */ +#define PD_EVENT_UPDATE_DEVICE BIT(0) +#define PD_EVENT_POWER_CHANGE BIT(1) +#define PD_EVENT_IDENTITY_RECEIVED BIT(2) +#define PD_EVENT_DATA_SWAP BIT(3) +struct ec_response_host_event_status { + uint32_t status; /* PD MCU host event status */ +} __ec_align4; /* Set USB type-C port role and muxes */ -#define EC_CMD_USB_PD_CONTROL 0x101 +#define EC_CMD_USB_PD_CONTROL 0x0101 enum usb_pd_control_role { USB_PD_CTRL_ROLE_NO_CHANGE = 0, @@ -3263,6 +4699,8 @@ enum usb_pd_control_role { USB_PD_CTRL_ROLE_TOGGLE_OFF = 2, USB_PD_CTRL_ROLE_FORCE_SINK = 3, USB_PD_CTRL_ROLE_FORCE_SOURCE = 4, + USB_PD_CTRL_ROLE_FREEZE = 5, + USB_PD_CTRL_ROLE_COUNT }; enum usb_pd_control_mux { @@ -3272,6 +4710,7 @@ enum usb_pd_control_mux { USB_PD_CTRL_MUX_DP = 3, USB_PD_CTRL_MUX_DOCK = 4, USB_PD_CTRL_MUX_AUTO = 5, + USB_PD_CTRL_MUX_COUNT }; enum usb_pd_control_swap { @@ -3287,11 +4726,11 @@ struct ec_params_usb_pd_control { uint8_t role; uint8_t mux; uint8_t swap; -} __packed; +} __ec_align1; -#define PD_CTRL_RESP_ENABLED_COMMS (1 << 0) /* Communication enabled */ -#define PD_CTRL_RESP_ENABLED_CONNECTED (1 << 1) /* Device connected */ -#define PD_CTRL_RESP_ENABLED_PD_CAPABLE (1 << 2) /* Partner is PD capable */ +#define PD_CTRL_RESP_ENABLED_COMMS BIT(0) /* Communication enabled */ +#define PD_CTRL_RESP_ENABLED_CONNECTED BIT(1) /* Device connected */ +#define PD_CTRL_RESP_ENABLED_PD_CAPABLE BIT(2) /* Partner is PD capable */ #define PD_CTRL_RESP_ROLE_POWER BIT(0) /* 0=SNK/1=SRC */ #define PD_CTRL_RESP_ROLE_DATA BIT(1) /* 0=UFP/1=DFP */ @@ -3301,28 +4740,54 @@ struct ec_params_usb_pd_control { #define PD_CTRL_RESP_ROLE_USB_COMM BIT(5) /* Partner USB comm capable */ #define PD_CTRL_RESP_ROLE_EXT_POWERED BIT(6) /* Partner externally powerd */ +struct ec_response_usb_pd_control { + uint8_t enabled; + uint8_t role; + uint8_t polarity; + uint8_t state; +} __ec_align1; + struct ec_response_usb_pd_control_v1 { uint8_t enabled; uint8_t role; uint8_t polarity; char state[32]; -} __packed; +} __ec_align1; + +/* Values representing usbc PD CC state */ +#define USBC_PD_CC_NONE 0 /* No accessory connected */ +#define USBC_PD_CC_NO_UFP 1 /* No UFP accessory connected */ +#define USBC_PD_CC_AUDIO_ACC 2 /* Audio accessory connected */ +#define USBC_PD_CC_DEBUG_ACC 3 /* Debug accessory connected */ +#define USBC_PD_CC_UFP_ATTACHED 4 /* UFP attached to usbc */ +#define USBC_PD_CC_DFP_ATTACHED 5 /* DPF attached to usbc */ + +struct ec_response_usb_pd_control_v2 { + uint8_t enabled; + uint8_t role; + uint8_t polarity; + char state[32]; + uint8_t cc_state; /* USBC_PD_CC_*Encoded cc state */ + uint8_t dp_mode; /* Current DP pin mode (MODE_DP_PIN_[A-E]) */ + /* CL:1500994 Current cable type */ + uint8_t reserved_cable_type; +} __ec_align1; -#define EC_CMD_USB_PD_PORTS 0x102 +#define EC_CMD_USB_PD_PORTS 0x0102 /* Maximum number of PD ports on a device, num_ports will be <= this */ #define EC_USB_PD_MAX_PORTS 8 struct ec_response_usb_pd_ports { uint8_t num_ports; -} __packed; +} __ec_align1; -#define EC_CMD_USB_PD_POWER_INFO 0x103 +#define EC_CMD_USB_PD_POWER_INFO 0x0103 #define PD_POWER_CHARGING_PORT 0xff struct ec_params_usb_pd_power_info { uint8_t port; -} __packed; +} __ec_align1; enum usb_chg_type { USB_CHG_TYPE_NONE, @@ -3335,6 +4800,7 @@ enum usb_chg_type { USB_CHG_TYPE_OTHER, USB_CHG_TYPE_VBUS, USB_CHG_TYPE_UNKNOWN, + USB_CHG_TYPE_DEDICATED, }; enum usb_power_roles { USB_PD_PORT_POWER_DISCONNECTED, @@ -3348,7 +4814,7 @@ struct usb_chg_measures { uint16_t voltage_now; uint16_t current_max; uint16_t current_lim; -} __packed; +} __ec_align2; struct ec_response_usb_pd_power_info { uint8_t role; @@ -3357,11 +4823,8 @@ struct ec_response_usb_pd_power_info { uint8_t reserved1; struct usb_chg_measures meas; uint32_t max_power; -} __packed; +} __ec_align4; -struct ec_params_usb_pd_info_request { - uint8_t port; -} __packed; /* * This command will return the number of USB PD charge port + the number @@ -3371,7 +4834,47 @@ struct ec_params_usb_pd_info_request { #define EC_CMD_CHARGE_PORT_COUNT 0x0105 struct ec_response_charge_port_count { uint8_t port_count; -} __packed; +} __ec_align1; + +/* Write USB-PD device FW */ +#define EC_CMD_USB_PD_FW_UPDATE 0x0110 + +enum usb_pd_fw_update_cmds { + USB_PD_FW_REBOOT, + USB_PD_FW_FLASH_ERASE, + USB_PD_FW_FLASH_WRITE, + USB_PD_FW_ERASE_SIG, +}; + +struct ec_params_usb_pd_fw_update { + uint16_t dev_id; + uint8_t cmd; + uint8_t port; + uint32_t size; /* Size to write in bytes */ + /* Followed by data to write */ +} __ec_align4; + +/* Write USB-PD Accessory RW_HASH table entry */ +#define EC_CMD_USB_PD_RW_HASH_ENTRY 0x0111 +/* RW hash is first 20 bytes of SHA-256 of RW section */ +#define PD_RW_HASH_SIZE 20 +struct ec_params_usb_pd_rw_hash_entry { + uint16_t dev_id; + uint8_t dev_rw_hash[PD_RW_HASH_SIZE]; + uint8_t reserved; /* + * For alignment of current_image + * TODO(rspangler) but it's not aligned! + * Should have been reserved[2]. + */ + uint32_t current_image; /* One of ec_current_image */ +} __ec_align1; + +/* Read USB-PD Accessory info */ +#define EC_CMD_USB_PD_DEV_INFO 0x0112 + +struct ec_params_usb_pd_info_request { + uint8_t port; +} __ec_align1; /* Read USB-PD Device discovery info */ #define EC_CMD_USB_PD_DISCOVERY 0x0113 @@ -3379,7 +4882,7 @@ struct ec_params_usb_pd_discovery_entry { uint16_t vid; /* USB-IF VID */ uint16_t pid; /* USB-IF PID */ uint8_t ptype; /* product type (hub,periph,cable,ama) */ -} __packed; +} __ec_align_size1; /* Override default charge behavior */ #define EC_CMD_PD_CHARGE_PORT_OVERRIDE 0x0114 @@ -3393,9 +4896,13 @@ enum usb_pd_override_ports { struct ec_params_charge_port_override { int16_t override_port; /* Override port# */ -} __packed; +} __ec_align2; -/* Read (and delete) one entry of PD event log */ +/* + * Read (and delete) one entry of PD event log. + * TODO(crbug.com/751742): Make this host command more generic to accommodate + * future non-PD logs that use the same internal EC event_log. + */ #define EC_CMD_PD_GET_LOG_ENTRY 0x0115 struct ec_response_pd_log { @@ -3404,7 +4911,7 @@ struct ec_response_pd_log { uint8_t size_port; /* [7:5] port number [4:0] payload size in bytes */ uint16_t data; /* type-defined data payload */ uint8_t payload[0]; /* optional additional data payload: 0..16 bytes */ -} __packed; +} __ec_align4; /* The timestamp is the microsecond counter shifted to get about a ms. */ #define PD_LOG_TIMESTAMP_SHIFT 10 /* 1 LSB = 1024us */ @@ -3470,35 +4977,698 @@ struct mcdp_version { uint8_t major; uint8_t minor; uint16_t build; -} __packed; +} __ec_align4; struct mcdp_info { uint8_t family[2]; uint8_t chipid[2]; struct mcdp_version irom; struct mcdp_version fw; -} __packed; +} __ec_align4; /* struct mcdp_info field decoding */ #define MCDP_CHIPID(chipid) ((chipid[0] << 8) | chipid[1]) #define MCDP_FAMILY(family) ((family[0] << 8) | family[1]) +/* Get/Set USB-PD Alternate mode info */ +#define EC_CMD_USB_PD_GET_AMODE 0x0116 +struct ec_params_usb_pd_get_mode_request { + uint16_t svid_idx; /* SVID index to get */ + uint8_t port; /* port */ +} __ec_align_size1; + +struct ec_params_usb_pd_get_mode_response { + uint16_t svid; /* SVID */ + uint16_t opos; /* Object Position */ + uint32_t vdo[6]; /* Mode VDOs */ +} __ec_align4; + +#define EC_CMD_USB_PD_SET_AMODE 0x0117 + +enum pd_mode_cmd { + PD_EXIT_MODE = 0, + PD_ENTER_MODE = 1, + /* Not a command. Do NOT remove. */ + PD_MODE_CMD_COUNT, +}; + +struct ec_params_usb_pd_set_mode_request { + uint32_t cmd; /* enum pd_mode_cmd */ + uint16_t svid; /* SVID to set */ + uint8_t opos; /* Object Position */ + uint8_t port; /* port */ +} __ec_align4; + +/* Ask the PD MCU to record a log of a requested type */ +#define EC_CMD_PD_WRITE_LOG_ENTRY 0x0118 + +struct ec_params_pd_write_log_entry { + uint8_t type; /* event type : see PD_EVENT_xx above */ + uint8_t port; /* port#, or 0 for events unrelated to a given port */ +} __ec_align1; + + +/* Control USB-PD chip */ +#define EC_CMD_PD_CONTROL 0x0119 + +enum ec_pd_control_cmd { + PD_SUSPEND = 0, /* Suspend the PD chip (EC: stop talking to PD) */ + PD_RESUME, /* Resume the PD chip (EC: start talking to PD) */ + PD_RESET, /* Force reset the PD chip */ + PD_CONTROL_DISABLE, /* Disable further calls to this command */ + PD_CHIP_ON, /* Power on the PD chip */ +}; + +struct ec_params_pd_control { + uint8_t chip; /* chip id */ + uint8_t subcmd; +} __ec_align1; + /* Get info about USB-C SS muxes */ -#define EC_CMD_USB_PD_MUX_INFO 0x11a +#define EC_CMD_USB_PD_MUX_INFO 0x011A struct ec_params_usb_pd_mux_info { uint8_t port; /* USB-C port number */ -} __packed; +} __ec_align1; /* Flags representing mux state */ -#define USB_PD_MUX_USB_ENABLED (1 << 0) -#define USB_PD_MUX_DP_ENABLED (1 << 1) -#define USB_PD_MUX_POLARITY_INVERTED (1 << 2) -#define USB_PD_MUX_HPD_IRQ (1 << 3) +#define USB_PD_MUX_USB_ENABLED BIT(0) /* USB connected */ +#define USB_PD_MUX_DP_ENABLED BIT(1) /* DP connected */ +#define USB_PD_MUX_POLARITY_INVERTED BIT(2) /* CC line Polarity inverted */ +#define USB_PD_MUX_HPD_IRQ BIT(3) /* HPD IRQ is asserted */ +#define USB_PD_MUX_HPD_LVL BIT(4) /* HPD level is asserted */ struct ec_response_usb_pd_mux_info { uint8_t flags; /* USB_PD_MUX_*-encoded USB mux state */ -} __packed; +} __ec_align1; + +#define EC_CMD_PD_CHIP_INFO 0x011B + +struct ec_params_pd_chip_info { + uint8_t port; /* USB-C port number */ + uint8_t renew; /* Force renewal */ +} __ec_align1; + +struct ec_response_pd_chip_info { + uint16_t vendor_id; + uint16_t product_id; + uint16_t device_id; + union { + uint8_t fw_version_string[8]; + uint64_t fw_version_number; + }; +} __ec_align2; + +struct ec_response_pd_chip_info_v1 { + uint16_t vendor_id; + uint16_t product_id; + uint16_t device_id; + union { + uint8_t fw_version_string[8]; + uint64_t fw_version_number; + }; + union { + uint8_t min_req_fw_version_string[8]; + uint64_t min_req_fw_version_number; + }; +} __ec_align2; + +/* Run RW signature verification and get status */ +#define EC_CMD_RWSIG_CHECK_STATUS 0x011C + +struct ec_response_rwsig_check_status { + uint32_t status; +} __ec_align4; + +/* For controlling RWSIG task */ +#define EC_CMD_RWSIG_ACTION 0x011D + +enum rwsig_action { + RWSIG_ACTION_ABORT = 0, /* Abort RWSIG and prevent jumping */ + RWSIG_ACTION_CONTINUE = 1, /* Jump to RW immediately */ +}; + +struct ec_params_rwsig_action { + uint32_t action; +} __ec_align4; + +/* Run verification on a slot */ +#define EC_CMD_EFS_VERIFY 0x011E + +struct ec_params_efs_verify { + uint8_t region; /* enum ec_flash_region */ +} __ec_align1; + +/* + * Retrieve info from Cros Board Info store. Response is based on the data + * type. Integers return a uint32. Strings return a string, using the response + * size to determine how big it is. + */ +#define EC_CMD_GET_CROS_BOARD_INFO 0x011F +/* + * Write info into Cros Board Info on EEPROM. Write fails if the board has + * hardware write-protect enabled. + */ +#define EC_CMD_SET_CROS_BOARD_INFO 0x0120 + +enum cbi_data_tag { + CBI_TAG_BOARD_VERSION = 0, /* uint32_t or smaller */ + CBI_TAG_OEM_ID = 1, /* uint32_t or smaller */ + CBI_TAG_SKU_ID = 2, /* uint32_t or smaller */ + CBI_TAG_DRAM_PART_NUM = 3, /* variable length ascii, nul terminated. */ + CBI_TAG_OEM_NAME = 4, /* variable length ascii, nul terminated. */ + CBI_TAG_MODEL_ID = 5, /* uint32_t or smaller */ + CBI_TAG_COUNT, +}; + +/* + * Flags to control read operation + * + * RELOAD: Invalidate cache and read data from EEPROM. Useful to verify + * write was successful without reboot. + */ +#define CBI_GET_RELOAD BIT(0) + +struct ec_params_get_cbi { + uint32_t tag; /* enum cbi_data_tag */ + uint32_t flag; /* CBI_GET_* */ +} __ec_align4; + +/* + * Flags to control write behavior. + * + * NO_SYNC: Makes EC update data in RAM but skip writing to EEPROM. It's + * useful when writing multiple fields in a row. + * INIT: Need to be set when creating a new CBI from scratch. All fields + * will be initialized to zero first. + */ +#define CBI_SET_NO_SYNC BIT(0) +#define CBI_SET_INIT BIT(1) + +struct ec_params_set_cbi { + uint32_t tag; /* enum cbi_data_tag */ + uint32_t flag; /* CBI_SET_* */ + uint32_t size; /* Data size */ + uint8_t data[]; /* For string and raw data */ +} __ec_align1; + +/* + * Information about resets of the AP by the EC and the EC's own uptime. + */ +#define EC_CMD_GET_UPTIME_INFO 0x0121 + +struct ec_response_uptime_info { + /* + * Number of milliseconds since the last EC boot. Sysjump resets + * typically do not restart the EC's time_since_boot epoch. + * + * WARNING: The EC's sense of time is much less accurate than the AP's + * sense of time, in both phase and frequency. This timebase is similar + * to CLOCK_MONOTONIC_RAW, but with 1% or more frequency error. + */ + uint32_t time_since_ec_boot_ms; + + /* + * Number of times the AP was reset by the EC since the last EC boot. + * Note that the AP may be held in reset by the EC during the initial + * boot sequence, such that the very first AP boot may count as more + * than one here. + */ + uint32_t ap_resets_since_ec_boot; + + /* + * The set of flags which describe the EC's most recent reset. See + * include/system.h RESET_FLAG_* for details. + */ + uint32_t ec_reset_flags; + + /* Empty log entries have both the cause and timestamp set to zero. */ + struct ap_reset_log_entry { + /* + * See include/chipset.h: enum chipset_{reset,shutdown}_reason + * for details. + */ + uint16_t reset_cause; + + /* Reserved for protocol growth. */ + uint16_t reserved; + + /* + * The time of the reset's assertion, in milliseconds since the + * last EC boot, in the same epoch as time_since_ec_boot_ms. + * Set to zero if the log entry is empty. + */ + uint32_t reset_time_ms; + } recent_ap_reset[4]; +} __ec_align4; + +/* + * Add entropy to the device secret (stored in the rollback region). + * + * Depending on the chip, the operation may take a long time (e.g. to erase + * flash), so the commands are asynchronous. + */ +#define EC_CMD_ADD_ENTROPY 0x0122 + +enum add_entropy_action { + /* Add entropy to the current secret. */ + ADD_ENTROPY_ASYNC = 0, + /* + * Add entropy, and also make sure that the previous secret is erased. + * (this can be implemented by adding entropy multiple times until + * all rolback blocks have been overwritten). + */ + ADD_ENTROPY_RESET_ASYNC = 1, + /* Read back result from the previous operation. */ + ADD_ENTROPY_GET_RESULT = 2, +}; + +struct ec_params_rollback_add_entropy { + uint8_t action; +} __ec_align1; + +/* + * Perform a single read of a given ADC channel. + */ +#define EC_CMD_ADC_READ 0x0123 + +struct ec_params_adc_read { + uint8_t adc_channel; +} __ec_align1; + +struct ec_response_adc_read { + int32_t adc_value; +} __ec_align4; + +/* + * Read back rollback info + */ +#define EC_CMD_ROLLBACK_INFO 0x0124 + +struct ec_response_rollback_info { + int32_t id; /* Incrementing number to indicate which region to use. */ + int32_t rollback_min_version; + int32_t rw_rollback_version; +} __ec_align4; + + +/* Issue AP reset */ +#define EC_CMD_AP_RESET 0x0125 + +/*****************************************************************************/ +/* The command range 0x200-0x2FF is reserved for Rotor. */ + +/*****************************************************************************/ +/* + * Reserve a range of host commands for the CR51 firmware. + */ +#define EC_CMD_CR51_BASE 0x0300 +#define EC_CMD_CR51_LAST 0x03FF + +/*****************************************************************************/ +/* Fingerprint MCU commands: range 0x0400-0x040x */ + +/* Fingerprint SPI sensor passthru command: prototyping ONLY */ +#define EC_CMD_FP_PASSTHRU 0x0400 + +#define EC_FP_FLAG_NOT_COMPLETE 0x1 + +struct ec_params_fp_passthru { + uint16_t len; /* Number of bytes to write then read */ + uint16_t flags; /* EC_FP_FLAG_xxx */ + uint8_t data[]; /* Data to send */ +} __ec_align2; + +/* Configure the Fingerprint MCU behavior */ +#define EC_CMD_FP_MODE 0x0402 + +/* Put the sensor in its lowest power mode */ +#define FP_MODE_DEEPSLEEP BIT(0) +/* Wait to see a finger on the sensor */ +#define FP_MODE_FINGER_DOWN BIT(1) +/* Poll until the finger has left the sensor */ +#define FP_MODE_FINGER_UP BIT(2) +/* Capture the current finger image */ +#define FP_MODE_CAPTURE BIT(3) +/* Finger enrollment session on-going */ +#define FP_MODE_ENROLL_SESSION BIT(4) +/* Enroll the current finger image */ +#define FP_MODE_ENROLL_IMAGE BIT(5) +/* Try to match the current finger image */ +#define FP_MODE_MATCH BIT(6) +/* Reset and re-initialize the sensor. */ +#define FP_MODE_RESET_SENSOR BIT(7) +/* special value: don't change anything just read back current mode */ +#define FP_MODE_DONT_CHANGE BIT(31) + +#define FP_VALID_MODES (FP_MODE_DEEPSLEEP | \ + FP_MODE_FINGER_DOWN | \ + FP_MODE_FINGER_UP | \ + FP_MODE_CAPTURE | \ + FP_MODE_ENROLL_SESSION | \ + FP_MODE_ENROLL_IMAGE | \ + FP_MODE_MATCH | \ + FP_MODE_RESET_SENSOR | \ + FP_MODE_DONT_CHANGE) + +/* Capture types defined in bits [30..28] */ +#define FP_MODE_CAPTURE_TYPE_SHIFT 28 +#define FP_MODE_CAPTURE_TYPE_MASK (0x7 << FP_MODE_CAPTURE_TYPE_SHIFT) +/* + * This enum must remain ordered, if you add new values you must ensure that + * FP_CAPTURE_TYPE_MAX is still the last one. + */ +enum fp_capture_type { + /* Full blown vendor-defined capture (produces 'frame_size' bytes) */ + FP_CAPTURE_VENDOR_FORMAT = 0, + /* Simple raw image capture (produces width x height x bpp bits) */ + FP_CAPTURE_SIMPLE_IMAGE = 1, + /* Self test pattern (e.g. checkerboard) */ + FP_CAPTURE_PATTERN0 = 2, + /* Self test pattern (e.g. inverted checkerboard) */ + FP_CAPTURE_PATTERN1 = 3, + /* Capture for Quality test with fixed contrast */ + FP_CAPTURE_QUALITY_TEST = 4, + /* Capture for pixel reset value test */ + FP_CAPTURE_RESET_TEST = 5, + FP_CAPTURE_TYPE_MAX, +}; +/* Extracts the capture type from the sensor 'mode' word */ +#define FP_CAPTURE_TYPE(mode) (((mode) & FP_MODE_CAPTURE_TYPE_MASK) \ + >> FP_MODE_CAPTURE_TYPE_SHIFT) + +struct ec_params_fp_mode { + uint32_t mode; /* as defined by FP_MODE_ constants */ +} __ec_align4; + +struct ec_response_fp_mode { + uint32_t mode; /* as defined by FP_MODE_ constants */ +} __ec_align4; + +/* Retrieve Fingerprint sensor information */ +#define EC_CMD_FP_INFO 0x0403 + +/* Number of dead pixels detected on the last maintenance */ +#define FP_ERROR_DEAD_PIXELS(errors) ((errors) & 0x3FF) +/* Unknown number of dead pixels detected on the last maintenance */ +#define FP_ERROR_DEAD_PIXELS_UNKNOWN (0x3FF) +/* No interrupt from the sensor */ +#define FP_ERROR_NO_IRQ BIT(12) +/* SPI communication error */ +#define FP_ERROR_SPI_COMM BIT(13) +/* Invalid sensor Hardware ID */ +#define FP_ERROR_BAD_HWID BIT(14) +/* Sensor initialization failed */ +#define FP_ERROR_INIT_FAIL BIT(15) + +struct ec_response_fp_info_v0 { + /* Sensor identification */ + uint32_t vendor_id; + uint32_t product_id; + uint32_t model_id; + uint32_t version; + /* Image frame characteristics */ + uint32_t frame_size; + uint32_t pixel_format; /* using V4L2_PIX_FMT_ */ + uint16_t width; + uint16_t height; + uint16_t bpp; + uint16_t errors; /* see FP_ERROR_ flags above */ +} __ec_align4; + +struct ec_response_fp_info { + /* Sensor identification */ + uint32_t vendor_id; + uint32_t product_id; + uint32_t model_id; + uint32_t version; + /* Image frame characteristics */ + uint32_t frame_size; + uint32_t pixel_format; /* using V4L2_PIX_FMT_ */ + uint16_t width; + uint16_t height; + uint16_t bpp; + uint16_t errors; /* see FP_ERROR_ flags above */ + /* Template/finger current information */ + uint32_t template_size; /* max template size in bytes */ + uint16_t template_max; /* maximum number of fingers/templates */ + uint16_t template_valid; /* number of valid fingers/templates */ + uint32_t template_dirty; /* bitmap of templates with MCU side changes */ + uint32_t template_version; /* version of the template format */ +} __ec_align4; + +/* Get the last captured finger frame or a template content */ +#define EC_CMD_FP_FRAME 0x0404 + +/* constants defining the 'offset' field which also contains the frame index */ +#define FP_FRAME_INDEX_SHIFT 28 +/* Frame buffer where the captured image is stored */ +#define FP_FRAME_INDEX_RAW_IMAGE 0 +/* First frame buffer holding a template */ +#define FP_FRAME_INDEX_TEMPLATE 1 +#define FP_FRAME_GET_BUFFER_INDEX(offset) ((offset) >> FP_FRAME_INDEX_SHIFT) +#define FP_FRAME_OFFSET_MASK 0x0FFFFFFF + +/* Version of the format of the encrypted templates. */ +#define FP_TEMPLATE_FORMAT_VERSION 3 + +/* Constants for encryption parameters */ +#define FP_CONTEXT_NONCE_BYTES 12 +#define FP_CONTEXT_USERID_WORDS (32 / sizeof(uint32_t)) +#define FP_CONTEXT_TAG_BYTES 16 +#define FP_CONTEXT_SALT_BYTES 16 +#define FP_CONTEXT_TPM_BYTES 32 + +struct ec_fp_template_encryption_metadata { + /* + * Version of the structure format (N=3). + */ + uint16_t struct_version; + /* Reserved bytes, set to 0. */ + uint16_t reserved; + /* + * The salt is *only* ever used for key derivation. The nonce is unique, + * a different one is used for every message. + */ + uint8_t nonce[FP_CONTEXT_NONCE_BYTES]; + uint8_t salt[FP_CONTEXT_SALT_BYTES]; + uint8_t tag[FP_CONTEXT_TAG_BYTES]; +}; + +struct ec_params_fp_frame { + /* + * The offset contains the template index or FP_FRAME_INDEX_RAW_IMAGE + * in the high nibble, and the real offset within the frame in + * FP_FRAME_OFFSET_MASK. + */ + uint32_t offset; + uint32_t size; +} __ec_align4; + +/* Load a template into the MCU */ +#define EC_CMD_FP_TEMPLATE 0x0405 + +/* Flag in the 'size' field indicating that the full template has been sent */ +#define FP_TEMPLATE_COMMIT 0x80000000 + +struct ec_params_fp_template { + uint32_t offset; + uint32_t size; + uint8_t data[]; +} __ec_align4; + +/* Clear the current fingerprint user context and set a new one */ +#define EC_CMD_FP_CONTEXT 0x0406 + +struct ec_params_fp_context { + uint32_t userid[FP_CONTEXT_USERID_WORDS]; +} __ec_align4; + +#define EC_CMD_FP_STATS 0x0407 + +#define FPSTATS_CAPTURE_INV BIT(0) +#define FPSTATS_MATCHING_INV BIT(1) + +struct ec_response_fp_stats { + uint32_t capture_time_us; + uint32_t matching_time_us; + uint32_t overall_time_us; + struct { + uint32_t lo; + uint32_t hi; + } overall_t0; + uint8_t timestamps_invalid; + int8_t template_matched; +} __ec_align2; + +#define EC_CMD_FP_SEED 0x0408 +struct ec_params_fp_seed { + /* + * Version of the structure format (N=3). + */ + uint16_t struct_version; + /* Reserved bytes, set to 0. */ + uint16_t reserved; + /* Seed from the TPM. */ + uint8_t seed[FP_CONTEXT_TPM_BYTES]; +} __ec_align4; + +/*****************************************************************************/ +/* Touchpad MCU commands: range 0x0500-0x05FF */ + +/* Perform touchpad self test */ +#define EC_CMD_TP_SELF_TEST 0x0500 + +/* Get number of frame types, and the size of each type */ +#define EC_CMD_TP_FRAME_INFO 0x0501 + +struct ec_response_tp_frame_info { + uint32_t n_frames; + uint32_t frame_sizes[0]; +} __ec_align4; + +/* Create a snapshot of current frame readings */ +#define EC_CMD_TP_FRAME_SNAPSHOT 0x0502 + +/* Read the frame */ +#define EC_CMD_TP_FRAME_GET 0x0503 + +struct ec_params_tp_frame_get { + uint32_t frame_index; + uint32_t offset; + uint32_t size; +} __ec_align4; + +/*****************************************************************************/ +/* EC-EC communication commands: range 0x0600-0x06FF */ + +#define EC_COMM_TEXT_MAX 8 + +/* + * Get battery static information, i.e. information that never changes, or + * very infrequently. + */ +#define EC_CMD_BATTERY_GET_STATIC 0x0600 + +/** + * struct ec_params_battery_static_info - Battery static info parameters + * @index: Battery index. + */ +struct ec_params_battery_static_info { + uint8_t index; +} __ec_align_size1; + +/** + * struct ec_response_battery_static_info - Battery static info response + * @design_capacity: Battery Design Capacity (mAh) + * @design_voltage: Battery Design Voltage (mV) + * @manufacturer: Battery Manufacturer String + * @model: Battery Model Number String + * @serial: Battery Serial Number String + * @type: Battery Type String + * @cycle_count: Battery Cycle Count + */ +struct ec_response_battery_static_info { + uint16_t design_capacity; + uint16_t design_voltage; + char manufacturer[EC_COMM_TEXT_MAX]; + char model[EC_COMM_TEXT_MAX]; + char serial[EC_COMM_TEXT_MAX]; + char type[EC_COMM_TEXT_MAX]; + /* TODO(crbug.com/795991): Consider moving to dynamic structure. */ + uint32_t cycle_count; +} __ec_align4; + +/* + * Get battery dynamic information, i.e. information that is likely to change + * every time it is read. + */ +#define EC_CMD_BATTERY_GET_DYNAMIC 0x0601 + +/** + * struct ec_params_battery_dynamic_info - Battery dynamic info parameters + * @index: Battery index. + */ +struct ec_params_battery_dynamic_info { + uint8_t index; +} __ec_align_size1; + +/** + * struct ec_response_battery_dynamic_info - Battery dynamic info response + * @actual_voltage: Battery voltage (mV) + * @actual_current: Battery current (mA); negative=discharging + * @remaining_capacity: Remaining capacity (mAh) + * @full_capacity: Capacity (mAh, might change occasionally) + * @flags: Flags, see EC_BATT_FLAG_* + * @desired_voltage: Charging voltage desired by battery (mV) + * @desired_current: Charging current desired by battery (mA) + */ +struct ec_response_battery_dynamic_info { + int16_t actual_voltage; + int16_t actual_current; + int16_t remaining_capacity; + int16_t full_capacity; + int16_t flags; + int16_t desired_voltage; + int16_t desired_current; +} __ec_align2; + +/* + * Control charger chip. Used to control charger chip on the slave. + */ +#define EC_CMD_CHARGER_CONTROL 0x0602 + +/** + * struct ec_params_charger_control - Charger control parameters + * @max_current: Charger current (mA). Positive to allow base to draw up to + * max_current and (possibly) charge battery, negative to request current + * from base (OTG). + * @otg_voltage: Voltage (mV) to use in OTG mode, ignored if max_current is + * >= 0. + * @allow_charging: Allow base battery charging (only makes sense if + * max_current > 0). + */ +struct ec_params_charger_control { + int16_t max_current; + uint16_t otg_voltage; + uint8_t allow_charging; +} __ec_align_size1; + +/*****************************************************************************/ +/* + * Reserve a range of host commands for board-specific, experimental, or + * special purpose features. These can be (re)used without updating this file. + * + * CAUTION: Don't go nuts with this. Shipping products should document ALL + * their EC commands for easier development, testing, debugging, and support. + * + * All commands MUST be #defined to be 4-digit UPPER CASE hex values + * (e.g., 0x00AB, not 0xab) for CONFIG_HOSTCMD_SECTION_SORTED to work. + * + * In your experimental code, you may want to do something like this: + * + * #define EC_CMD_MAGIC_FOO 0x0000 + * #define EC_CMD_MAGIC_BAR 0x0001 + * #define EC_CMD_MAGIC_HEY 0x0002 + * + * DECLARE_PRIVATE_HOST_COMMAND(EC_CMD_MAGIC_FOO, magic_foo_handler, + * EC_VER_MASK(0); + * + * DECLARE_PRIVATE_HOST_COMMAND(EC_CMD_MAGIC_BAR, magic_bar_handler, + * EC_VER_MASK(0); + * + * DECLARE_PRIVATE_HOST_COMMAND(EC_CMD_MAGIC_HEY, magic_hey_handler, + * EC_VER_MASK(0); + */ +#define EC_CMD_BOARD_SPECIFIC_BASE 0x3E00 +#define EC_CMD_BOARD_SPECIFIC_LAST 0x3FFF + +/* + * Given the private host command offset, calculate the true private host + * command value. + */ +#define EC_PRIVATE_HOST_COMMAND_VALUE(command) \ + (EC_CMD_BOARD_SPECIFIC_BASE + (command)) /*****************************************************************************/ /* @@ -3538,4 +5708,6 @@ struct ec_response_usb_pd_mux_info { #define EC_LPC_ADDR_OLD_PARAM EC_HOST_CMD_REGION1 #define EC_OLD_PARAM_SIZE EC_HOST_CMD_REGION_SIZE + + #endif /* __CROS_EC_COMMANDS_H */ diff --git a/include/linux/platform_data/wilco-ec.h b/include/linux/platform_data/wilco-ec.h index 1ff224793c99..ad03b586a095 100644 --- a/include/linux/platform_data/wilco-ec.h +++ b/include/linux/platform_data/wilco-ec.h @@ -13,12 +13,9 @@ /* Message flags for using the mailbox() interface */ #define WILCO_EC_FLAG_NO_RESPONSE BIT(0) /* EC does not respond */ -#define WILCO_EC_FLAG_EXTENDED_DATA BIT(1) /* EC returns 256 data bytes */ /* Normal commands have a maximum 32 bytes of data */ #define EC_MAILBOX_DATA_SIZE 32 -/* Extended commands have 256 bytes of response data */ -#define EC_MAILBOX_DATA_SIZE_EXTENDED 256 /** * struct wilco_ec_device - Wilco Embedded Controller handle. @@ -32,6 +29,7 @@ * @data_size: Size of the data buffer used for EC communication. * @debugfs_pdev: The child platform_device used by the debugfs sub-driver. * @rtc_pdev: The child platform_device used by the RTC sub-driver. + * @telem_pdev: The child platform_device used by the telemetry sub-driver. */ struct wilco_ec_device { struct device *dev; @@ -43,6 +41,7 @@ struct wilco_ec_device { size_t data_size; struct platform_device *debugfs_pdev; struct platform_device *rtc_pdev; + struct platform_device *telem_pdev; }; /** @@ -85,14 +84,12 @@ struct wilco_ec_response { * enum wilco_ec_msg_type - Message type to select a set of command codes. * @WILCO_EC_MSG_LEGACY: Legacy EC messages for standard EC behavior. * @WILCO_EC_MSG_PROPERTY: Get/Set/Sync EC controlled NVRAM property. - * @WILCO_EC_MSG_TELEMETRY_SHORT: 32 bytes of telemetry data provided by the EC. - * @WILCO_EC_MSG_TELEMETRY_LONG: 256 bytes of telemetry data provided by the EC. + * @WILCO_EC_MSG_TELEMETRY: Request telemetry data from the EC. */ enum wilco_ec_msg_type { WILCO_EC_MSG_LEGACY = 0x00f0, WILCO_EC_MSG_PROPERTY = 0x00f2, - WILCO_EC_MSG_TELEMETRY_SHORT = 0x00f5, - WILCO_EC_MSG_TELEMETRY_LONG = 0x00f6, + WILCO_EC_MSG_TELEMETRY = 0x00f5, }; /** @@ -123,4 +120,87 @@ struct wilco_ec_message { */ int wilco_ec_mailbox(struct wilco_ec_device *ec, struct wilco_ec_message *msg); +/* + * A Property is typically a data item that is stored to NVRAM + * by the EC. Each of these data items has an index associated + * with it, known as the Property ID (PID). Properties may have + * variable lengths, up to a max of WILCO_EC_PROPERTY_MAX_SIZE + * bytes. Properties can be simple integers, or they may be more + * complex binary data. + */ + +#define WILCO_EC_PROPERTY_MAX_SIZE 4 + +/** + * struct ec_property_set_msg - Message to get or set a property. + * @property_id: Which property to get or set. + * @length: Number of bytes of |data| that are used. + * @data: Actual property data. + */ +struct wilco_ec_property_msg { + u32 property_id; + int length; + u8 data[WILCO_EC_PROPERTY_MAX_SIZE]; +}; + +/** + * wilco_ec_get_property() - Retrieve a property from the EC. + * @ec: Embedded Controller device. + * @prop_msg: Message for request and response. + * + * The property_id field of |prop_msg| should be filled before calling this + * function. The result will be stored in the data and length fields. + * + * Return: 0 on success, negative error code on failure. + */ +int wilco_ec_get_property(struct wilco_ec_device *ec, + struct wilco_ec_property_msg *prop_msg); + +/** + * wilco_ec_set_property() - Store a property on the EC. + * @ec: Embedded Controller device. + * @prop_msg: Message for request and response. + * + * The property_id, length, and data fields of |prop_msg| should be + * filled before calling this function. + * + * Return: 0 on success, negative error code on failure. + */ +int wilco_ec_set_property(struct wilco_ec_device *ec, + struct wilco_ec_property_msg *prop_msg); + +/** + * wilco_ec_get_byte_property() - Retrieve a byte-size property from the EC. + * @ec: Embedded Controller device. + * @property_id: Which property to retrieve. + * @val: The result value, will be filled by this function. + * + * Return: 0 on success, negative error code on failure. + */ +int wilco_ec_get_byte_property(struct wilco_ec_device *ec, u32 property_id, + u8 *val); + +/** + * wilco_ec_get_byte_property() - Store a byte-size property on the EC. + * @ec: Embedded Controller device. + * @property_id: Which property to store. + * @val: Value to store. + * + * Return: 0 on success, negative error code on failure. + */ +int wilco_ec_set_byte_property(struct wilco_ec_device *ec, u32 property_id, + u8 val); + +/** + * wilco_ec_add_sysfs() - Create sysfs entries + * @ec: Wilco EC device + * + * wilco_ec_remove_sysfs() needs to be called afterwards + * to perform the necessary cleanup. + * + * Return: 0 on success or negative error code on failure. + */ +int wilco_ec_add_sysfs(struct wilco_ec_device *ec); +void wilco_ec_remove_sysfs(struct wilco_ec_device *ec); + #endif /* WILCO_EC_H */ diff --git a/sound/soc/codecs/cros_ec_codec.c b/sound/soc/codecs/cros_ec_codec.c index 0ac3e520653f..85beef265cc8 100644 --- a/sound/soc/codecs/cros_ec_codec.c +++ b/sound/soc/codecs/cros_ec_codec.c @@ -38,21 +38,21 @@ static const DECLARE_TLV_DB_SCALE(ec_mic_gain_tlv, 0, 100, 0); static int ec_command_get_gain(struct snd_soc_component *component, struct ec_param_codec_i2s *param, - struct ec_response_codec_gain *resp) + struct ec_codec_i2s_gain *resp) { struct cros_ec_codec_data *codec_data = snd_soc_component_get_drvdata(component); struct cros_ec_device *ec_device = codec_data->ec_device; u8 buffer[sizeof(struct cros_ec_command) + max(sizeof(struct ec_param_codec_i2s), - sizeof(struct ec_response_codec_gain))]; + sizeof(struct ec_codec_i2s_gain))]; struct cros_ec_command *msg = (struct cros_ec_command *)&buffer; int ret; msg->version = 0; msg->command = EC_CMD_CODEC_I2S; msg->outsize = sizeof(struct ec_param_codec_i2s); - msg->insize = sizeof(struct ec_response_codec_gain); + msg->insize = sizeof(struct ec_codec_i2s_gain); memcpy(msg->data, param, msg->outsize); @@ -226,7 +226,7 @@ static int get_ec_mic_gain(struct snd_soc_component *component, u8 *left, u8 *right) { struct ec_param_codec_i2s param; - struct ec_response_codec_gain resp; + struct ec_codec_i2s_gain resp; int ret; param.cmd = EC_CODEC_GET_GAIN; |