summaryrefslogtreecommitdiff
path: root/drivers/platform
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2024-07-15 17:25:38 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2024-07-15 17:25:38 -0700
commit89c491389331faea09a247da47ebd95982dae06e (patch)
treeac8101800b1f0ebd5579039a9be51717d274a3b1 /drivers/platform
parentd8764c1931a4c91b9b53ee183757f70999da2bb3 (diff)
parent4baf1cc54433ff7c6e5178517bc8768001416681 (diff)
Merge tag 'tag-chrome-platform-for-v6.11' of git://git.kernel.org/pub/scm/linux/kernel/git/chrome-platform/linux
Pull chrome platform updates from Tzung-Bi Shih: "New code: - Add "cros_ec_hwmon" driver to expose fan speed and temperature - Add "cros_charge-control" driver to control charge thresholds and behaviour - Add module parameter "log_poll_period_ms" in cros_ec_debugfs for tuning the poll period - Support version 3 of EC_CMD_GET_NEXT_EVENT and keyboard matrix Fixes: - Fix a race condition in accessing MEC (Microchip EC) memory between ACPI and kernel. Serialize the memory access by an AML (ACPI Machine Language) mutex - Fix an issue of wrong EC message version in cros_ec_debugfs Misc: - Fix kernel-doc errors and cleanups" * tag 'tag-chrome-platform-for-v6.11' of git://git.kernel.org/pub/scm/linux/kernel/git/chrome-platform/linux: (28 commits) power: supply: cros_charge-control: Fix signedness bug in charge_behaviour_store() power: supply: cros_charge-control: Avoid accessing attributes out of bounds power: supply: cros_charge-control: don't load if Framework control is present power: supply: add ChromeOS EC based charge control driver platform/chrome: cros_ec_proto: Introduce cros_ec_get_cmd_versions() platform/chrome: Update binary interface for EC-based charge control ACPI: battery: add devm_battery_hook_register() dt-bindings: input: cros-ec-keyboard: Add keyboard matrix v3.0 platform/chrome: cros_ec_lpc: Handle zero length read/write platform/chrome: cros_ec_lpc: Fix error code in cros_ec_lpc_mec_read_bytes() platform/chrome: cros_ec_debugfs: fix wrong EC message version platform/chrome: cros_ec_proto: update Kunit test for get_next_data_v3 platform/chrome: cros_ec_proto: add missing MODULE_DESCRIPTION() macro hwmon: (cros_ec) Fix access to restricted __le16 hwmon: (cros_ec) Prevent read overflow in probe() platform/chrome: cros_ec_lpc: Add quirks for Framework Laptop platform/chrome: cros_ec_lpc: Add a new quirk for AML mutex platform/chrome: cros_ec_lpc: Add a new quirk for ACPI id platform/chrome: cros_ec_lpc: MEC access can use an AML mutex platform/chrome: cros_ec_lpc: MEC access can return error code ...
Diffstat (limited to 'drivers/platform')
-rw-r--r--drivers/platform/chrome/cros_ec.c4
-rw-r--r--drivers/platform/chrome/cros_ec_debugfs.c9
-rw-r--r--drivers/platform/chrome/cros_ec_lpc.c210
-rw-r--r--drivers/platform/chrome/cros_ec_lpc_mec.c91
-rw-r--r--drivers/platform/chrome/cros_ec_lpc_mec.h18
-rw-r--r--drivers/platform/chrome/cros_ec_proto.c95
-rw-r--r--drivers/platform/chrome/cros_ec_proto_test.c9
-rw-r--r--drivers/platform/chrome/wilco_ec/mailbox.c22
8 files changed, 362 insertions, 96 deletions
diff --git a/drivers/platform/chrome/cros_ec.c b/drivers/platform/chrome/cros_ec.c
index 47d19f7e295a..e821b3d39590 100644
--- a/drivers/platform/chrome/cros_ec.c
+++ b/drivers/platform/chrome/cros_ec.c
@@ -388,8 +388,8 @@ EXPORT_SYMBOL(cros_ec_suspend_late);
*/
int cros_ec_suspend(struct cros_ec_device *ec_dev)
{
- cros_ec_send_suspend_event(ec_dev);
- cros_ec_disable_irq(ec_dev);
+ cros_ec_suspend_prepare(ec_dev);
+ cros_ec_suspend_late(ec_dev);
return 0;
}
EXPORT_SYMBOL(cros_ec_suspend);
diff --git a/drivers/platform/chrome/cros_ec_debugfs.c b/drivers/platform/chrome/cros_ec_debugfs.c
index e1d313246beb..4525ad1b59f4 100644
--- a/drivers/platform/chrome/cros_ec_debugfs.c
+++ b/drivers/platform/chrome/cros_ec_debugfs.c
@@ -26,6 +26,10 @@
#define CIRC_ADD(idx, size, value) (((idx) + (value)) & ((size) - 1))
+static unsigned int log_poll_period_ms = LOG_POLL_SEC * MSEC_PER_SEC;
+module_param(log_poll_period_ms, uint, 0644);
+MODULE_PARM_DESC(log_poll_period_ms, "EC log polling period(ms)");
+
/* waitqueue for log readers */
static DECLARE_WAIT_QUEUE_HEAD(cros_ec_debugfs_log_wq);
@@ -57,7 +61,7 @@ struct cros_ec_debugfs {
/*
* We need to make sure that the EC log buffer on the UART is large enough,
- * so that it is unlikely enough to overlow within LOG_POLL_SEC.
+ * so that it is unlikely enough to overlow within log_poll_period_ms.
*/
static void cros_ec_console_log_work(struct work_struct *__work)
{
@@ -119,7 +123,7 @@ static void cros_ec_console_log_work(struct work_struct *__work)
resched:
schedule_delayed_work(&debug_info->log_poll_work,
- msecs_to_jiffies(LOG_POLL_SEC * 1000));
+ msecs_to_jiffies(log_poll_period_ms));
}
static int cros_ec_console_log_open(struct inode *inode, struct file *file)
@@ -330,6 +334,7 @@ static int ec_read_version_supported(struct cros_ec_dev *ec)
if (!msg)
return 0;
+ msg->version = 1;
msg->command = EC_CMD_GET_CMD_VERSIONS + ec->cmd_offset;
msg->outsize = sizeof(*params);
msg->insize = sizeof(*response);
diff --git a/drivers/platform/chrome/cros_ec_lpc.c b/drivers/platform/chrome/cros_ec_lpc.c
index ddfbfec44f4c..f0470248b109 100644
--- a/drivers/platform/chrome/cros_ec_lpc.c
+++ b/drivers/platform/chrome/cros_ec_lpc.c
@@ -39,6 +39,16 @@ static bool cros_ec_lpc_acpi_device_found;
* be used as the base port for EC mapped memory.
*/
#define CROS_EC_LPC_QUIRK_REMAP_MEMORY BIT(0)
+/*
+ * Indicates that lpc_driver_data.quirk_acpi_id should be used to find
+ * the ACPI device.
+ */
+#define CROS_EC_LPC_QUIRK_ACPI_ID BIT(1)
+/*
+ * Indicates that lpc_driver_data.quirk_aml_mutex_name should be used
+ * to find an AML mutex to protect access to Microchip EC.
+ */
+#define CROS_EC_LPC_QUIRK_AML_MUTEX BIT(2)
/**
* struct lpc_driver_data - driver data attached to a DMI device ID to indicate
@@ -46,10 +56,15 @@ static bool cros_ec_lpc_acpi_device_found;
* @quirks: a bitfield composed of quirks from CROS_EC_LPC_QUIRK_*
* @quirk_mmio_memory_base: The first I/O port addressing EC mapped memory (used
* when quirk ...REMAP_MEMORY is set.)
+ * @quirk_acpi_id: An ACPI HID to be used to find the ACPI device.
+ * @quirk_aml_mutex_name: The name of an AML mutex to be used to protect access
+ * to Microchip EC.
*/
struct lpc_driver_data {
u32 quirks;
u16 quirk_mmio_memory_base;
+ const char *quirk_acpi_id;
+ const char *quirk_aml_mutex_name;
};
/**
@@ -62,14 +77,16 @@ struct cros_ec_lpc {
/**
* 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.
+ * @read: Copy length bytes from EC address offset into buffer dest.
+ * Returns a negative error code on error, or the 8-bit checksum
+ * of all bytes read.
+ * @write: Copy length bytes from buffer msg into EC address offset.
+ * Returns a negative error code on error, or 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);
+ int (*read)(unsigned int offset, unsigned int length, u8 *dest);
+ int (*write)(unsigned int offset, unsigned int length, const u8 *msg);
};
static struct lpc_driver_ops cros_ec_lpc_ops = { };
@@ -78,10 +95,10 @@ 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)
+static int cros_ec_lpc_read_bytes(unsigned int offset, unsigned int length,
+ u8 *dest)
{
- int sum = 0;
+ u8 sum = 0;
int i;
for (i = 0; i < length; ++i) {
@@ -97,10 +114,10 @@ static u8 cros_ec_lpc_read_bytes(unsigned int offset, unsigned int length,
* 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)
+static int cros_ec_lpc_write_bytes(unsigned int offset, unsigned int length,
+ const u8 *msg)
{
- int sum = 0;
+ u8 sum = 0;
int i;
for (i = 0; i < length; ++i) {
@@ -116,13 +133,13 @@ static u8 cros_ec_lpc_write_bytes(unsigned int offset, unsigned int length,
* 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)
+static int 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;
return in_range ?
cros_ec_lpc_io_bytes_mec(MEC_IO_READ,
@@ -135,13 +152,13 @@ static u8 cros_ec_lpc_mec_read_bytes(unsigned int offset, unsigned int length,
* 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)
+static int 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;
return in_range ?
cros_ec_lpc_io_bytes_mec(MEC_IO_WRITE,
@@ -154,11 +171,14 @@ static int ec_response_timed_out(void)
{
unsigned long one_second = jiffies + HZ;
u8 data;
+ int ret;
usleep_range(200, 300);
do {
- if (!(cros_ec_lpc_ops.read(EC_LPC_ADDR_HOST_CMD, 1, &data) &
- EC_LPC_STATUS_BUSY_MASK))
+ ret = cros_ec_lpc_ops.read(EC_LPC_ADDR_HOST_CMD, 1, &data);
+ if (ret < 0)
+ return ret;
+ if (!(data & EC_LPC_STATUS_BUSY_MASK))
return 0;
usleep_range(100, 200);
} while (time_before(jiffies, one_second));
@@ -179,28 +199,41 @@ static int cros_ec_pkt_xfer_lpc(struct cros_ec_device *ec,
goto done;
/* Write buffer */
- cros_ec_lpc_ops.write(EC_LPC_ADDR_HOST_PACKET, ret, ec->dout);
+ ret = cros_ec_lpc_ops.write(EC_LPC_ADDR_HOST_PACKET, ret, ec->dout);
+ if (ret < 0)
+ goto done;
/* Here we go */
sum = EC_COMMAND_PROTOCOL_3;
- cros_ec_lpc_ops.write(EC_LPC_ADDR_HOST_CMD, 1, &sum);
+ ret = cros_ec_lpc_ops.write(EC_LPC_ADDR_HOST_CMD, 1, &sum);
+ if (ret < 0)
+ goto done;
- if (ec_response_timed_out()) {
+ ret = ec_response_timed_out();
+ if (ret < 0)
+ goto done;
+ if (ret) {
dev_warn(ec->dev, "EC response timed out\n");
ret = -EIO;
goto done;
}
/* Check result */
- msg->result = cros_ec_lpc_ops.read(EC_LPC_ADDR_HOST_DATA, 1, &sum);
+ ret = cros_ec_lpc_ops.read(EC_LPC_ADDR_HOST_DATA, 1, &sum);
+ if (ret < 0)
+ goto done;
+ msg->result = ret;
ret = cros_ec_check_result(ec, msg);
if (ret)
goto done;
/* Read back response */
dout = (u8 *)&response;
- sum = cros_ec_lpc_ops.read(EC_LPC_ADDR_HOST_PACKET, sizeof(response),
+ ret = cros_ec_lpc_ops.read(EC_LPC_ADDR_HOST_PACKET, sizeof(response),
dout);
+ if (ret < 0)
+ goto done;
+ sum = ret;
msg->result = response.result;
@@ -213,9 +246,12 @@ static int cros_ec_pkt_xfer_lpc(struct cros_ec_device *ec,
}
/* Read response and process checksum */
- sum += cros_ec_lpc_ops.read(EC_LPC_ADDR_HOST_PACKET +
- sizeof(response), response.data_len,
- msg->data);
+ ret = cros_ec_lpc_ops.read(EC_LPC_ADDR_HOST_PACKET +
+ sizeof(response), response.data_len,
+ msg->data);
+ if (ret < 0)
+ goto done;
+ sum += ret;
if (sum) {
dev_err(ec->dev,
@@ -255,32 +291,47 @@ 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_ops.write(EC_LPC_ADDR_HOST_PARAM, msg->outsize,
- msg->data);
+ ret = cros_ec_lpc_ops.write(EC_LPC_ADDR_HOST_PARAM, msg->outsize,
+ msg->data);
+ if (ret < 0)
+ goto done;
+ sum += ret;
/* Finalize checksum and write args */
args.checksum = sum;
- cros_ec_lpc_ops.write(EC_LPC_ADDR_HOST_ARGS, sizeof(args),
- (u8 *)&args);
+ ret = cros_ec_lpc_ops.write(EC_LPC_ADDR_HOST_ARGS, sizeof(args),
+ (u8 *)&args);
+ if (ret < 0)
+ goto done;
/* Here we go */
sum = msg->command;
- cros_ec_lpc_ops.write(EC_LPC_ADDR_HOST_CMD, 1, &sum);
+ ret = cros_ec_lpc_ops.write(EC_LPC_ADDR_HOST_CMD, 1, &sum);
+ if (ret < 0)
+ goto done;
- if (ec_response_timed_out()) {
+ ret = ec_response_timed_out();
+ if (ret < 0)
+ goto done;
+ if (ret) {
dev_warn(ec->dev, "EC response timed out\n");
ret = -EIO;
goto done;
}
/* Check result */
- msg->result = cros_ec_lpc_ops.read(EC_LPC_ADDR_HOST_DATA, 1, &sum);
+ ret = cros_ec_lpc_ops.read(EC_LPC_ADDR_HOST_DATA, 1, &sum);
+ if (ret < 0)
+ goto done;
+ msg->result = ret;
ret = cros_ec_check_result(ec, msg);
if (ret)
goto done;
/* Read back args */
- cros_ec_lpc_ops.read(EC_LPC_ADDR_HOST_ARGS, sizeof(args), (u8 *)&args);
+ ret = cros_ec_lpc_ops.read(EC_LPC_ADDR_HOST_ARGS, sizeof(args), (u8 *)&args);
+ if (ret < 0)
+ goto done;
if (args.data_size > msg->insize) {
dev_err(ec->dev,
@@ -294,8 +345,11 @@ 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_ops.read(EC_LPC_ADDR_HOST_PARAM, args.data_size,
- msg->data);
+ ret = cros_ec_lpc_ops.read(EC_LPC_ADDR_HOST_PARAM, args.data_size,
+ msg->data);
+ if (ret < 0)
+ goto done;
+ sum += ret;
/* Verify checksum */
if (args.checksum != sum) {
@@ -320,19 +374,24 @@ static int cros_ec_lpc_readmem(struct cros_ec_device *ec, unsigned int offset,
int i = offset;
char *s = dest;
int cnt = 0;
+ int ret;
if (offset >= EC_MEMMAP_SIZE - bytes)
return -EINVAL;
/* fixed length */
if (bytes) {
- cros_ec_lpc_ops.read(ec_lpc->mmio_memory_base + offset, bytes, s);
+ ret = cros_ec_lpc_ops.read(ec_lpc->mmio_memory_base + offset, bytes, s);
+ if (ret < 0)
+ return ret;
return bytes;
}
/* string */
for (; i < EC_MEMMAP_SIZE; i++, s++) {
- cros_ec_lpc_ops.read(ec_lpc->mmio_memory_base + i, 1, s);
+ ret = cros_ec_lpc_ops.read(ec_lpc->mmio_memory_base + i, 1, s);
+ if (ret < 0)
+ return ret;
cnt++;
if (!*s)
break;
@@ -374,6 +433,26 @@ static void cros_ec_lpc_acpi_notify(acpi_handle device, u32 value, void *data)
pm_system_wakeup();
}
+static acpi_status cros_ec_lpc_parse_device(acpi_handle handle, u32 level,
+ void *context, void **retval)
+{
+ *(struct acpi_device **)context = acpi_fetch_acpi_dev(handle);
+ return AE_CTRL_TERMINATE;
+}
+
+static struct acpi_device *cros_ec_lpc_get_device(const char *id)
+{
+ struct acpi_device *adev = NULL;
+ acpi_status status = acpi_get_devices(id, cros_ec_lpc_parse_device,
+ &adev, NULL);
+ if (ACPI_FAILURE(status)) {
+ pr_warn(DRV_NAME ": Looking for %s failed\n", id);
+ return NULL;
+ }
+
+ return adev;
+}
+
static int cros_ec_lpc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -401,6 +480,27 @@ static int cros_ec_lpc_probe(struct platform_device *pdev)
if (quirks & CROS_EC_LPC_QUIRK_REMAP_MEMORY)
ec_lpc->mmio_memory_base = driver_data->quirk_mmio_memory_base;
+
+ if (quirks & CROS_EC_LPC_QUIRK_ACPI_ID) {
+ adev = cros_ec_lpc_get_device(driver_data->quirk_acpi_id);
+ if (!adev) {
+ dev_err(dev, "failed to get ACPI device '%s'",
+ driver_data->quirk_acpi_id);
+ return -ENODEV;
+ }
+ ACPI_COMPANION_SET(dev, adev);
+ }
+
+ if (quirks & CROS_EC_LPC_QUIRK_AML_MUTEX) {
+ const char *name
+ = driver_data->quirk_aml_mutex_name;
+ ret = cros_ec_lpc_mec_acpi_mutex(ACPI_COMPANION(dev), name);
+ if (ret) {
+ dev_err(dev, "failed to get AML mutex '%s'", name);
+ return ret;
+ }
+ dev_info(dev, "got AML mutex '%s'", name);
+ }
}
/*
@@ -425,7 +525,9 @@ static int cros_ec_lpc_probe(struct platform_device *pdev)
*/
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);
+ ret = cros_ec_lpc_ops.read(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID, 2, buf);
+ if (ret < 0)
+ return ret;
if (buf[0] != 'E' || buf[1] != 'C') {
if (!devm_request_region(dev, ec_lpc->mmio_memory_base, EC_MEMMAP_SIZE,
dev_name(dev))) {
@@ -436,8 +538,10 @@ static int cros_ec_lpc_probe(struct platform_device *pdev)
/* 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->mmio_memory_base + EC_MEMMAP_ID, 2,
- buf);
+ ret = cros_ec_lpc_ops.read(ec_lpc->mmio_memory_base + EC_MEMMAP_ID, 2,
+ buf);
+ if (ret < 0)
+ return ret;
if (buf[0] != 'E' || buf[1] != 'C') {
dev_err(dev, "EC ID not detected\n");
return -ENODEV;
@@ -532,6 +636,12 @@ static const struct lpc_driver_data framework_laptop_amd_lpc_driver_data __initc
.quirk_mmio_memory_base = 0xE00,
};
+static const struct lpc_driver_data framework_laptop_11_lpc_driver_data __initconst = {
+ .quirks = CROS_EC_LPC_QUIRK_ACPI_ID|CROS_EC_LPC_QUIRK_AML_MUTEX,
+ .quirk_acpi_id = "PNP0C09",
+ .quirk_aml_mutex_name = "ECMT",
+};
+
static const struct dmi_system_id cros_ec_lpc_dmi_table[] __initconst = {
{
/*
@@ -600,6 +710,7 @@ static const struct dmi_system_id cros_ec_lpc_dmi_table[] __initconst = {
DMI_MATCH(DMI_SYS_VENDOR, "Framework"),
DMI_MATCH(DMI_PRODUCT_NAME, "Laptop"),
},
+ .driver_data = (void *)&framework_laptop_11_lpc_driver_data,
},
{ /* sentinel */ }
};
@@ -661,23 +772,12 @@ static struct platform_device cros_ec_lpc_device = {
.name = DRV_NAME
};
-static acpi_status cros_ec_lpc_parse_device(acpi_handle handle, u32 level,
- void *context, void **retval)
-{
- *(bool *)context = true;
- return AE_CTRL_TERMINATE;
-}
-
static int __init cros_ec_lpc_init(void)
{
int ret;
- acpi_status status;
const struct dmi_system_id *dmi_match;
- status = acpi_get_devices(ACPI_DRV_NAME, cros_ec_lpc_parse_device,
- &cros_ec_lpc_acpi_device_found, NULL);
- if (ACPI_FAILURE(status))
- pr_warn(DRV_NAME ": Looking for %s failed\n", ACPI_DRV_NAME);
+ cros_ec_lpc_acpi_device_found = !!cros_ec_lpc_get_device(ACPI_DRV_NAME);
dmi_match = dmi_first_match(cros_ec_lpc_dmi_table);
diff --git a/drivers/platform/chrome/cros_ec_lpc_mec.c b/drivers/platform/chrome/cros_ec_lpc_mec.c
index 0d9c79b270ce..a56584171168 100644
--- a/drivers/platform/chrome/cros_ec_lpc_mec.c
+++ b/drivers/platform/chrome/cros_ec_lpc_mec.c
@@ -10,14 +10,66 @@
#include "cros_ec_lpc_mec.h"
+#define ACPI_LOCK_DELAY_MS 500
+
/*
* This mutex must be held while accessing the EMI unit. We can't rely on the
* EC mutex because memmap data may be accessed without it being held.
*/
static DEFINE_MUTEX(io_mutex);
+/*
+ * An alternative mutex to be used when the ACPI AML code may also
+ * access memmap data. When set, this mutex is used in preference to
+ * io_mutex.
+ */
+static acpi_handle aml_mutex;
+
static u16 mec_emi_base, mec_emi_end;
/**
+ * cros_ec_lpc_mec_lock() - Acquire mutex for EMI
+ *
+ * @return: Negative error code, or zero for success
+ */
+static int cros_ec_lpc_mec_lock(void)
+{
+ bool success;
+
+ if (!aml_mutex) {
+ mutex_lock(&io_mutex);
+ return 0;
+ }
+
+ success = ACPI_SUCCESS(acpi_acquire_mutex(aml_mutex,
+ NULL, ACPI_LOCK_DELAY_MS));
+ if (!success)
+ return -EBUSY;
+
+ return 0;
+}
+
+/**
+ * cros_ec_lpc_mec_unlock() - Release mutex for EMI
+ *
+ * @return: Negative error code, or zero for success
+ */
+static int cros_ec_lpc_mec_unlock(void)
+{
+ bool success;
+
+ if (!aml_mutex) {
+ mutex_unlock(&io_mutex);
+ return 0;
+ }
+
+ success = ACPI_SUCCESS(acpi_release_mutex(aml_mutex, NULL));
+ if (!success)
+ return -EBUSY;
+
+ return 0;
+}
+
+/**
* cros_ec_lpc_mec_emi_write_address() - Initialize EMI at a given address.
*
* @addr: Starting read / write address
@@ -41,9 +93,6 @@ static void cros_ec_lpc_mec_emi_write_address(u16 addr,
*/
int cros_ec_lpc_mec_in_range(unsigned int offset, unsigned int length)
{
- if (length == 0)
- return -EINVAL;
-
if (WARN_ON(mec_emi_base == 0 || mec_emi_end == 0))
return -EINVAL;
@@ -67,16 +116,21 @@ int cros_ec_lpc_mec_in_range(unsigned int offset, unsigned int length)
* @length: Number of bytes to read / write
* @buf: Destination / source buffer
*
- * Return: 8-bit checksum of all bytes read / written
+ * @return: A negative error code on error, or 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,
- u8 *buf)
+int cros_ec_lpc_io_bytes_mec(enum cros_ec_lpc_mec_io_type io_type,
+ unsigned int offset, unsigned int length,
+ u8 *buf)
{
int i = 0;
int io_addr;
u8 sum = 0;
enum cros_ec_lpc_mec_emi_access_mode access, new_access;
+ int ret;
+
+ if (length == 0)
+ return 0;
/* Return checksum of 0 if window is not initialized */
WARN_ON(mec_emi_base == 0 || mec_emi_end == 0);
@@ -92,7 +146,9 @@ u8 cros_ec_lpc_io_bytes_mec(enum cros_ec_lpc_mec_io_type io_type,
else
access = ACCESS_TYPE_LONG_AUTO_INCREMENT;
- mutex_lock(&io_mutex);
+ ret = cros_ec_lpc_mec_lock();
+ if (ret)
+ return ret;
/* Initialize I/O at desired address */
cros_ec_lpc_mec_emi_write_address(offset, access);
@@ -134,7 +190,9 @@ u8 cros_ec_lpc_io_bytes_mec(enum cros_ec_lpc_mec_io_type io_type,
}
done:
- mutex_unlock(&io_mutex);
+ ret = cros_ec_lpc_mec_unlock();
+ if (ret)
+ return ret;
return sum;
}
@@ -146,3 +204,18 @@ void cros_ec_lpc_mec_init(unsigned int base, unsigned int end)
mec_emi_end = end;
}
EXPORT_SYMBOL(cros_ec_lpc_mec_init);
+
+int cros_ec_lpc_mec_acpi_mutex(struct acpi_device *adev, const char *pathname)
+{
+ int status;
+
+ if (!adev)
+ return -ENOENT;
+
+ status = acpi_get_handle(adev->handle, pathname, &aml_mutex);
+ if (ACPI_FAILURE(status))
+ return -ENOENT;
+
+ return 0;
+}
+EXPORT_SYMBOL(cros_ec_lpc_mec_acpi_mutex);
diff --git a/drivers/platform/chrome/cros_ec_lpc_mec.h b/drivers/platform/chrome/cros_ec_lpc_mec.h
index 9d0521b23e8a..69f9d8786f61 100644
--- a/drivers/platform/chrome/cros_ec_lpc_mec.h
+++ b/drivers/platform/chrome/cros_ec_lpc_mec.h
@@ -8,6 +8,8 @@
#ifndef __CROS_EC_LPC_MEC_H
#define __CROS_EC_LPC_MEC_H
+#include <linux/acpi.h>
+
enum cros_ec_lpc_mec_emi_access_mode {
/* 8-bit access */
ACCESS_TYPE_BYTE = 0x0,
@@ -46,6 +48,15 @@ enum cros_ec_lpc_mec_io_type {
void cros_ec_lpc_mec_init(unsigned int base, unsigned int end);
/**
+ * cros_ec_lpc_mec_acpi_mutex() - Find and set ACPI mutex for MEC
+ *
+ * @adev: Parent ACPI device
+ * @pathname: Name of AML mutex
+ * @return: Negative error code, or zero for success
+ */
+int cros_ec_lpc_mec_acpi_mutex(struct acpi_device *adev, const char *pathname);
+
+/**
* cros_ec_lpc_mec_in_range() - Determine if addresses are in MEC EMI range.
*
* @offset: Address offset
@@ -64,9 +75,10 @@ int cros_ec_lpc_mec_in_range(unsigned int offset, unsigned int length);
* @length: Number of bytes to read / write
* @buf: Destination / source buffer
*
- * @return 8-bit checksum of all bytes read / written
+ * @return: A negative error code on error, or 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, u8 *buf);
+int cros_ec_lpc_io_bytes_mec(enum cros_ec_lpc_mec_io_type io_type,
+ unsigned int offset, unsigned int length, u8 *buf);
#endif /* __CROS_EC_LPC_MEC_H */
diff --git a/drivers/platform/chrome/cros_ec_proto.c b/drivers/platform/chrome/cros_ec_proto.c
index 945b1b15a04c..f776fd42244f 100644
--- a/drivers/platform/chrome/cros_ec_proto.c
+++ b/drivers/platform/chrome/cros_ec_proto.c
@@ -5,6 +5,7 @@
#include <linux/delay.h>
#include <linux/device.h>
+#include <linux/limits.h>
#include <linux/module.h>
#include <linux/platform_data/cros_ec_commands.h>
#include <linux/platform_data/cros_ec_proto.h>
@@ -239,13 +240,12 @@ int cros_ec_check_result(struct cros_ec_device *ec_dev,
}
EXPORT_SYMBOL(cros_ec_check_result);
-/*
+/**
* cros_ec_get_host_event_wake_mask
*
* Get the mask of host events that cause wake from suspend.
*
* @ec_dev: EC device to call
- * @msg: message structure to use
* @mask: result when function returns 0.
*
* LOCKING:
@@ -427,13 +427,12 @@ exit:
return ret;
}
-/*
+/**
* cros_ec_get_host_command_version_mask
*
* Get the version mask of a given command.
*
* @ec_dev: EC device to call
- * @msg: message structure to use
* @cmd: command to get the version of.
* @mask: result when function returns 0.
*
@@ -686,7 +685,7 @@ EXPORT_SYMBOL(cros_ec_cmd_xfer_status);
static int get_next_event_xfer(struct cros_ec_device *ec_dev,
struct cros_ec_command *msg,
- struct ec_response_get_next_event_v1 *event,
+ struct ec_response_get_next_event_v3 *event,
int version, uint32_t size)
{
int ret;
@@ -709,11 +708,12 @@ static int get_next_event(struct cros_ec_device *ec_dev)
{
struct {
struct cros_ec_command msg;
- struct ec_response_get_next_event_v1 event;
+ struct ec_response_get_next_event_v3 event;
} __packed buf;
struct cros_ec_command *msg = &buf.msg;
- struct ec_response_get_next_event_v1 *event = &buf.event;
- const int cmd_version = ec_dev->mkbp_event_supported - 1;
+ struct ec_response_get_next_event_v3 *event = &buf.event;
+ int cmd_version = ec_dev->mkbp_event_supported - 1;
+ u32 size;
memset(msg, 0, sizeof(*msg));
if (ec_dev->suspended) {
@@ -721,12 +721,20 @@ static int get_next_event(struct cros_ec_device *ec_dev)
return -EHOSTDOWN;
}
- if (cmd_version == 0)
- return get_next_event_xfer(ec_dev, msg, event, 0,
- sizeof(struct ec_response_get_next_event));
+ if (cmd_version == 0) {
+ size = sizeof(struct ec_response_get_next_event);
+ } else if (cmd_version < 3) {
+ size = sizeof(struct ec_response_get_next_event_v1);
+ } else {
+ /*
+ * The max version we support is v3. So, we speak v3 even if the
+ * EC says it supports v4+.
+ */
+ cmd_version = 3;
+ size = sizeof(struct ec_response_get_next_event_v3);
+ }
- return get_next_event_xfer(ec_dev, msg, event, cmd_version,
- sizeof(struct ec_response_get_next_event_v1));
+ return get_next_event_xfer(ec_dev, msg, event, cmd_version, size);
}
static int get_keyboard_state_event(struct cros_ec_device *ec_dev)
@@ -1035,3 +1043,64 @@ error:
return ret;
}
EXPORT_SYMBOL_GPL(cros_ec_cmd);
+
+/**
+ * cros_ec_cmd_readmem - Read from EC memory.
+ *
+ * @ec_dev: EC device
+ * @offset: Is within EC_LPC_ADDR_MEMMAP region.
+ * @size: Number of bytes to read.
+ * @dest: EC command output data
+ *
+ * Return: >= 0 on success, negative error number on failure.
+ */
+int cros_ec_cmd_readmem(struct cros_ec_device *ec_dev, u8 offset, u8 size, void *dest)
+{
+ struct ec_params_read_memmap params = {};
+
+ if (!size)
+ return -EINVAL;
+
+ if (ec_dev->cmd_readmem)
+ return ec_dev->cmd_readmem(ec_dev, offset, size, dest);
+
+ params.offset = offset;
+ params.size = size;
+ return cros_ec_cmd(ec_dev, 0, EC_CMD_READ_MEMMAP,
+ &params, sizeof(params), dest, size);
+}
+EXPORT_SYMBOL_GPL(cros_ec_cmd_readmem);
+
+/**
+ * cros_ec_get_cmd_versions - Get supported version mask.
+ *
+ * @ec_dev: EC device
+ * @cmd: Command to test
+ *
+ * Return: version mask on success, negative error number on failure.
+ */
+int cros_ec_get_cmd_versions(struct cros_ec_device *ec_dev, u16 cmd)
+{
+ struct ec_params_get_cmd_versions req_v0;
+ struct ec_params_get_cmd_versions_v1 req_v1;
+ struct ec_response_get_cmd_versions resp;
+ int ret;
+
+ if (cmd <= U8_MAX) {
+ req_v0.cmd = cmd;
+ ret = cros_ec_cmd(ec_dev, 0, EC_CMD_GET_CMD_VERSIONS,
+ &req_v0, sizeof(req_v0), &resp, sizeof(resp));
+ } else {
+ req_v1.cmd = cmd;
+ ret = cros_ec_cmd(ec_dev, 1, EC_CMD_GET_CMD_VERSIONS,
+ &req_v1, sizeof(req_v1), &resp, sizeof(resp));
+ }
+
+ if (ret == -EINVAL)
+ return 0; /* Command not implemented */
+ else if (ret < 0)
+ return ret;
+ else
+ return resp.version_mask;
+}
+EXPORT_SYMBOL_GPL(cros_ec_get_cmd_versions);
diff --git a/drivers/platform/chrome/cros_ec_proto_test.c b/drivers/platform/chrome/cros_ec_proto_test.c
index 41378c2ee6a0..7ca9895a0065 100644
--- a/drivers/platform/chrome/cros_ec_proto_test.c
+++ b/drivers/platform/chrome/cros_ec_proto_test.c
@@ -2060,17 +2060,17 @@ static void cros_ec_proto_test_get_next_event_no_mkbp_event(struct kunit *test)
/* For get_keyboard_state_event(). */
{
- union ec_response_get_next_data_v1 *data;
+ union ec_response_get_next_data_v3 *data;
mock = cros_kunit_ec_xfer_mock_add(test, sizeof(*data));
KUNIT_ASSERT_PTR_NE(test, mock, NULL);
- data = (union ec_response_get_next_data_v1 *)mock->o_data;
+ data = (union ec_response_get_next_data_v3 *)mock->o_data;
data->host_event = 0xbeef;
}
ret = cros_ec_get_next_event(ec_dev, &wake_event, &more_events);
- KUNIT_EXPECT_EQ(test, ret, sizeof(union ec_response_get_next_data_v1));
+ KUNIT_EXPECT_EQ(test, ret, sizeof(union ec_response_get_next_data_v3));
KUNIT_EXPECT_EQ(test, ec_dev->event_data.event_type, EC_MKBP_EVENT_KEY_MATRIX);
KUNIT_EXPECT_EQ(test, ec_dev->event_data.data.host_event, 0xbeef);
@@ -2085,7 +2085,7 @@ static void cros_ec_proto_test_get_next_event_no_mkbp_event(struct kunit *test)
KUNIT_EXPECT_EQ(test, mock->msg.version, 0);
KUNIT_EXPECT_EQ(test, mock->msg.command, EC_CMD_MKBP_STATE);
- KUNIT_EXPECT_EQ(test, mock->msg.insize, sizeof(union ec_response_get_next_data_v1));
+ KUNIT_EXPECT_EQ(test, mock->msg.insize, sizeof(union ec_response_get_next_data_v3));
KUNIT_EXPECT_EQ(test, mock->msg.outsize, 0);
}
}
@@ -2740,4 +2740,5 @@ static struct kunit_suite cros_ec_proto_test_suite = {
kunit_test_suite(cros_ec_proto_test_suite);
+MODULE_DESCRIPTION("Kunit tests for ChromeOS Embedded Controller protocol");
MODULE_LICENSE("GPL");
diff --git a/drivers/platform/chrome/wilco_ec/mailbox.c b/drivers/platform/chrome/wilco_ec/mailbox.c
index 0f98358ea824..4d8273b47cde 100644
--- a/drivers/platform/chrome/wilco_ec/mailbox.c
+++ b/drivers/platform/chrome/wilco_ec/mailbox.c
@@ -117,13 +117,17 @@ static int wilco_ec_transfer(struct wilco_ec_device *ec,
struct wilco_ec_request *rq)
{
struct wilco_ec_response *rs;
- u8 checksum;
+ int ret;
u8 flag;
/* Write request header, then data */
- cros_ec_lpc_io_bytes_mec(MEC_IO_WRITE, 0, sizeof(*rq), (u8 *)rq);
- cros_ec_lpc_io_bytes_mec(MEC_IO_WRITE, sizeof(*rq), msg->request_size,
- msg->request_data);
+ ret = cros_ec_lpc_io_bytes_mec(MEC_IO_WRITE, 0, sizeof(*rq), (u8 *)rq);
+ if (ret < 0)
+ return ret;
+ ret = cros_ec_lpc_io_bytes_mec(MEC_IO_WRITE, sizeof(*rq), msg->request_size,
+ msg->request_data);
+ if (ret < 0)
+ return ret;
/* Start the command */
outb(EC_MAILBOX_START_COMMAND, ec->io_command->start);
@@ -149,10 +153,12 @@ static int wilco_ec_transfer(struct wilco_ec_device *ec,
/* Read back response */
rs = ec->data_buffer;
- checksum = cros_ec_lpc_io_bytes_mec(MEC_IO_READ, 0,
- sizeof(*rs) + EC_MAILBOX_DATA_SIZE,
- (u8 *)rs);
- if (checksum) {
+ ret = cros_ec_lpc_io_bytes_mec(MEC_IO_READ, 0,
+ sizeof(*rs) + EC_MAILBOX_DATA_SIZE,
+ (u8 *)rs);
+ if (ret < 0)
+ return ret;
+ if (ret) {
dev_dbg(ec->dev, "bad packet checksum 0x%02x\n", rs->checksum);
return -EBADMSG;
}