summaryrefslogtreecommitdiffstats
path: root/drivers/platform
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2019-09-19 14:14:28 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2019-09-19 14:14:28 -0700
commit32b90daf5cafdda62b1ce62d0b7445fa9107babf (patch)
treea22ae3217a1f8c32e28c70d878f830b99b4a113b /drivers/platform
parentc6cfaf4f86d9d15e5541adb3bb899d0b80f89ec7 (diff)
parent4c1fde5077dcad1a2a10a6a0883c8f94326c4971 (diff)
downloadlinux-32b90daf5cafdda62b1ce62d0b7445fa9107babf.tar.bz2
Merge tag 'tag-chrome-platform-for-v5.4' of git://git.kernel.org/pub/scm/linux/kernel/git/chrome-platform/linux
Pull chrome platform updates from Benson Leung: "CrOS EC / MFD Migration: - Move cros_ec core driver from mfd into chrome platform. Wilco EC: - Add batt_ppid_info command to Wilco telemetry driver. CrOS EC: - cros_ec_rpmsg : Add support to inform EC of suspend/resume status - cros_ec_rpmsg : Fix race condition on probe failed - cros_ec_chardev : Add a poll handler to receive MKBP events Misc: - bugfixes in cros_usbpd_logger and cros_ec_ishtp" * tag 'tag-chrome-platform-for-v5.4' of git://git.kernel.org/pub/scm/linux/kernel/git/chrome-platform/linux: platform/chrome: cros_usbpd_logger: null check create_singlethread_workqueue platform/chrome: cros_ec_chardev: Add a poll handler to receive MKBP events platform/chrome: cros_ec_rpmsg: Fix race with host command when probe failed platform/chrome: chromeos_tbmc: Report wake events mfd: cros_ec: Use mfd_add_hotplug_devices() helper mfd: cros_ec: Add convenience struct to define autodetectable CrOS EC subdevices mfd: cros_ec: Add convenience struct to define dedicated CrOS EC MCUs mfd: cros_ec: Use kzalloc and cros_ec_cmd_xfer_status helper mfd / platform: cros_ec: Reorganize platform and mfd includes mfd / platform: cros_ec: Rename config to a better name mfd: cros_ec: Switch to use the new cros-ec-chardev driver mfd / platform: cros_ec: Miscellaneous character device to talk with the EC mfd / platform: cros_ec: Move cros-ec core driver out from MFD mfd / platform: cros_ec: Handle chained ECs as platform devices platform/chrome: cros_ec_rpmsg: Add host command AP sleep state support platform/chrome: chromeos_laptop: drop checks of NULL-safe functions platform/chrome: wilco_ec: Add batt_ppid_info command to telemetry driver
Diffstat (limited to 'drivers/platform')
-rw-r--r--drivers/platform/chrome/Kconfig60
-rw-r--r--drivers/platform/chrome/Makefile2
-rw-r--r--drivers/platform/chrome/chromeos_laptop.c10
-rw-r--r--drivers/platform/chrome/chromeos_tbmc.c2
-rw-r--r--drivers/platform/chrome/cros_ec.c281
-rw-r--r--drivers/platform/chrome/cros_ec_chardev.c419
-rw-r--r--drivers/platform/chrome/cros_ec_debugfs.c3
-rw-r--r--drivers/platform/chrome/cros_ec_i2c.c12
-rw-r--r--drivers/platform/chrome/cros_ec_ishtp.c5
-rw-r--r--drivers/platform/chrome/cros_ec_lightbar.c3
-rw-r--r--drivers/platform/chrome/cros_ec_lpc.c7
-rw-r--r--drivers/platform/chrome/cros_ec_proto.c3
-rw-r--r--drivers/platform/chrome/cros_ec_rpmsg.c57
-rw-r--r--drivers/platform/chrome/cros_ec_spi.c12
-rw-r--r--drivers/platform/chrome/cros_ec_sysfs.c3
-rw-r--r--drivers/platform/chrome/cros_ec_trace.c2
-rw-r--r--drivers/platform/chrome/cros_ec_trace.h4
-rw-r--r--drivers/platform/chrome/cros_ec_vbc.c3
-rw-r--r--drivers/platform/chrome/cros_usbpd_logger.c8
-rw-r--r--drivers/platform/chrome/wilco_ec/telemetry.c64
20 files changed, 894 insertions, 66 deletions
diff --git a/drivers/platform/chrome/Kconfig b/drivers/platform/chrome/Kconfig
index 970679d0b6f6..ee5f08ea57b6 100644
--- a/drivers/platform/chrome/Kconfig
+++ b/drivers/platform/chrome/Kconfig
@@ -3,6 +3,16 @@
# Platform support for Chrome OS hardware (Chromebooks and Chromeboxes)
#
+config MFD_CROS_EC
+ tristate "Platform support for Chrome hardware (transitional)"
+ select CHROME_PLATFORMS
+ select CROS_EC
+ select CONFIG_MFD_CROS_EC_DEV
+ depends on X86 || ARM || ARM64 || COMPILE_TEST
+ help
+ This is a transitional Kconfig option and will be removed after
+ everyone enables the parts individually.
+
menuconfig CHROME_PLATFORMS
bool "Platform support for Chrome hardware"
depends on X86 || ARM || ARM64 || COMPILE_TEST
@@ -50,9 +60,22 @@ config CHROMEOS_TBMC
To compile this driver as a module, choose M here: the
module will be called chromeos_tbmc.
+config CROS_EC
+ tristate "ChromeOS Embedded Controller"
+ select CROS_EC_PROTO
+ depends on X86 || ARM || ARM64 || COMPILE_TEST
+ help
+ If you say Y here you get support for the ChromeOS Embedded
+ Controller (EC) providing keyboard, battery and power services.
+ You also need to enable the driver for the bus you are using. The
+ protocol for talking to the EC is defined by the bus driver.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cros_ec.
+
config CROS_EC_I2C
tristate "ChromeOS Embedded Controller (I2C)"
- depends on MFD_CROS_EC && I2C
+ depends on CROS_EC && I2C
help
If you say Y here, you get support for talking to the ChromeOS
@@ -62,7 +85,7 @@ config CROS_EC_I2C
config CROS_EC_RPMSG
tristate "ChromeOS Embedded Controller (rpmsg)"
- depends on MFD_CROS_EC && RPMSG && OF
+ depends on CROS_EC && RPMSG && OF
help
If you say Y here, you get support for talking to the ChromeOS EC
through rpmsg. This uses a simple byte-level protocol with a
@@ -74,7 +97,7 @@ config CROS_EC_RPMSG
config CROS_EC_ISHTP
tristate "ChromeOS Embedded Controller (ISHTP)"
- depends on MFD_CROS_EC
+ depends on CROS_EC
depends on INTEL_ISH_HID
help
If you say Y here, you get support for talking to the ChromeOS EC
@@ -87,7 +110,7 @@ config CROS_EC_ISHTP
config CROS_EC_SPI
tristate "ChromeOS Embedded Controller (SPI)"
- depends on MFD_CROS_EC && SPI
+ depends on CROS_EC && SPI
---help---
If you say Y here, you get support for talking to the ChromeOS EC
@@ -97,7 +120,7 @@ config CROS_EC_SPI
config CROS_EC_LPC
tristate "ChromeOS Embedded Controller (LPC)"
- depends on MFD_CROS_EC && ACPI && (X86 || COMPILE_TEST)
+ depends on 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, including the LPC Microchip EC (MEC) variant.
@@ -123,10 +146,21 @@ config CROS_KBD_LED_BACKLIGHT
To compile this driver as a module, choose M here: the
module will be called cros_kbd_led_backlight.
+config CROS_EC_CHARDEV
+ tristate "ChromeOS EC miscdevice"
+ depends on MFD_CROS_EC_DEV
+ default MFD_CROS_EC_DEV
+ help
+ This driver adds file operations support to talk with the
+ ChromeOS EC from userspace via a character device.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cros_ec_chardev.
+
config CROS_EC_LIGHTBAR
tristate "Chromebook Pixel's lightbar support"
- depends on MFD_CROS_EC_CHARDEV
- default MFD_CROS_EC_CHARDEV
+ depends on MFD_CROS_EC_DEV
+ default MFD_CROS_EC_DEV
help
This option exposes the Chromebook Pixel's lightbar to
userspace.
@@ -136,8 +170,8 @@ config CROS_EC_LIGHTBAR
config CROS_EC_VBC
tristate "ChromeOS EC vboot context support"
- depends on MFD_CROS_EC_CHARDEV && OF
- default MFD_CROS_EC_CHARDEV
+ depends on MFD_CROS_EC_DEV && OF
+ default MFD_CROS_EC_DEV
help
This option exposes the ChromeOS EC vboot context nvram to
userspace.
@@ -147,8 +181,8 @@ config CROS_EC_VBC
config CROS_EC_DEBUGFS
tristate "Export ChromeOS EC internals in DebugFS"
- depends on MFD_CROS_EC_CHARDEV && DEBUG_FS
- default MFD_CROS_EC_CHARDEV
+ depends on MFD_CROS_EC_DEV && DEBUG_FS
+ default MFD_CROS_EC_DEV
help
This option exposes the ChromeOS EC device internals to
userspace.
@@ -158,8 +192,8 @@ config CROS_EC_DEBUGFS
config CROS_EC_SYSFS
tristate "ChromeOS EC control and information through sysfs"
- depends on MFD_CROS_EC_CHARDEV && SYSFS
- default MFD_CROS_EC_CHARDEV
+ depends on MFD_CROS_EC_DEV && SYSFS
+ default MFD_CROS_EC_DEV
help
This option exposes some sysfs attributes to control and get
information from ChromeOS EC.
diff --git a/drivers/platform/chrome/Makefile b/drivers/platform/chrome/Makefile
index fd0af05cc14c..477ec3d1d1c9 100644
--- a/drivers/platform/chrome/Makefile
+++ b/drivers/platform/chrome/Makefile
@@ -6,6 +6,7 @@ CFLAGS_cros_ec_trace.o:= -I$(src)
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) += cros_ec.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
@@ -14,6 +15,7 @@ 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
+obj-$(CONFIG_CROS_EC_CHARDEV) += cros_ec_chardev.o
obj-$(CONFIG_CROS_EC_LIGHTBAR) += cros_ec_lightbar.o
obj-$(CONFIG_CROS_EC_VBC) += cros_ec_vbc.o
obj-$(CONFIG_CROS_EC_DEBUGFS) += cros_ec_debugfs.o
diff --git a/drivers/platform/chrome/chromeos_laptop.c b/drivers/platform/chrome/chromeos_laptop.c
index 7abbb6167766..8723bcf10c93 100644
--- a/drivers/platform/chrome/chromeos_laptop.c
+++ b/drivers/platform/chrome/chromeos_laptop.c
@@ -838,18 +838,14 @@ static void chromeos_laptop_destroy(const struct chromeos_laptop *cros_laptop)
i2c_dev = &cros_laptop->i2c_peripherals[i];
info = &i2c_dev->board_info;
- if (i2c_dev->client)
- i2c_unregister_device(i2c_dev->client);
-
- if (info->properties)
- property_entries_free(info->properties);
+ i2c_unregister_device(i2c_dev->client);
+ property_entries_free(info->properties);
}
for (i = 0; i < cros_laptop->num_acpi_peripherals; i++) {
acpi_dev = &cros_laptop->acpi_peripherals[i];
- if (acpi_dev->properties)
- property_entries_free(acpi_dev->properties);
+ property_entries_free(acpi_dev->properties);
}
kfree(cros_laptop->i2c_peripherals);
diff --git a/drivers/platform/chrome/chromeos_tbmc.c b/drivers/platform/chrome/chromeos_tbmc.c
index ce259ec9f990..d1cf8f3463ce 100644
--- a/drivers/platform/chrome/chromeos_tbmc.c
+++ b/drivers/platform/chrome/chromeos_tbmc.c
@@ -47,6 +47,7 @@ static __maybe_unused int chromeos_tbmc_resume(struct device *dev)
static void chromeos_tbmc_notify(struct acpi_device *adev, u32 event)
{
+ acpi_pm_wakeup_event(&adev->dev);
switch (event) {
case 0x80:
chromeos_tbmc_query_switch(adev, adev->driver_data);
@@ -90,6 +91,7 @@ static int chromeos_tbmc_add(struct acpi_device *adev)
dev_err(dev, "cannot register input device\n");
return ret;
}
+ device_init_wakeup(dev, true);
return 0;
}
diff --git a/drivers/platform/chrome/cros_ec.c b/drivers/platform/chrome/cros_ec.c
new file mode 100644
index 000000000000..fd77e6fa74c2
--- /dev/null
+++ b/drivers/platform/chrome/cros_ec.c
@@ -0,0 +1,281 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ChromeOS EC multi-function device
+ *
+ * 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.
+ */
+
+#include <linux/of_platform.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/platform_data/cros_ec_commands.h>
+#include <linux/platform_data/cros_ec_proto.h>
+#include <linux/suspend.h>
+#include <asm/unaligned.h>
+
+#define CROS_EC_DEV_EC_INDEX 0
+#define CROS_EC_DEV_PD_INDEX 1
+
+static struct cros_ec_platform ec_p = {
+ .ec_name = CROS_EC_DEV_NAME,
+ .cmd_offset = EC_CMD_PASSTHRU_OFFSET(CROS_EC_DEV_EC_INDEX),
+};
+
+static struct cros_ec_platform pd_p = {
+ .ec_name = CROS_EC_DEV_PD_NAME,
+ .cmd_offset = EC_CMD_PASSTHRU_OFFSET(CROS_EC_DEV_PD_INDEX),
+};
+
+static irqreturn_t ec_irq_thread(int irq, void *data)
+{
+ struct cros_ec_device *ec_dev = data;
+ bool wake_event = true;
+ int ret;
+
+ ret = cros_ec_get_next_event(ec_dev, &wake_event);
+
+ /*
+ * Signal only if wake host events or any interrupt if
+ * cros_ec_get_next_event() returned an error (default value for
+ * wake_event is true)
+ */
+ if (wake_event && device_may_wakeup(ec_dev->dev))
+ pm_wakeup_event(ec_dev->dev, 0);
+
+ if (ret > 0)
+ blocking_notifier_call_chain(&ec_dev->event_notifier,
+ 0, ec_dev);
+ return IRQ_HANDLED;
+}
+
+static int cros_ec_sleep_event(struct cros_ec_device *ec_dev, u8 sleep_event)
+{
+ int ret;
+ struct {
+ struct cros_ec_command msg;
+ union {
+ struct ec_params_host_sleep_event req0;
+ struct ec_params_host_sleep_event_v1 req1;
+ struct ec_response_host_sleep_event_v1 resp1;
+ } u;
+ } __packed buf;
+
+ memset(&buf, 0, sizeof(buf));
+
+ if (ec_dev->host_sleep_v1) {
+ buf.u.req1.sleep_event = sleep_event;
+ buf.u.req1.suspend_params.sleep_timeout_ms =
+ EC_HOST_SLEEP_TIMEOUT_DEFAULT;
+
+ buf.msg.outsize = sizeof(buf.u.req1);
+ if ((sleep_event == HOST_SLEEP_EVENT_S3_RESUME) ||
+ (sleep_event == HOST_SLEEP_EVENT_S0IX_RESUME))
+ buf.msg.insize = sizeof(buf.u.resp1);
+
+ buf.msg.version = 1;
+
+ } else {
+ buf.u.req0.sleep_event = sleep_event;
+ buf.msg.outsize = sizeof(buf.u.req0);
+ }
+
+ buf.msg.command = EC_CMD_HOST_SLEEP_EVENT;
+
+ ret = cros_ec_cmd_xfer(ec_dev, &buf.msg);
+
+ /* 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)) {
+ 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;
+}
+
+int cros_ec_register(struct cros_ec_device *ec_dev)
+{
+ struct device *dev = ec_dev->dev;
+ int err = 0;
+
+ BLOCKING_INIT_NOTIFIER_HEAD(&ec_dev->event_notifier);
+
+ ec_dev->max_request = sizeof(struct ec_params_hello);
+ ec_dev->max_response = sizeof(struct ec_response_get_protocol_info);
+ ec_dev->max_passthru = 0;
+
+ ec_dev->din = devm_kzalloc(dev, ec_dev->din_size, GFP_KERNEL);
+ if (!ec_dev->din)
+ return -ENOMEM;
+
+ ec_dev->dout = devm_kzalloc(dev, ec_dev->dout_size, GFP_KERNEL);
+ if (!ec_dev->dout)
+ return -ENOMEM;
+
+ mutex_init(&ec_dev->lock);
+
+ err = cros_ec_query_all(ec_dev);
+ if (err) {
+ dev_err(dev, "Cannot identify the EC: error %d\n", err);
+ return err;
+ }
+
+ if (ec_dev->irq) {
+ err = devm_request_threaded_irq(dev, ec_dev->irq, NULL,
+ ec_irq_thread, IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ "chromeos-ec", ec_dev);
+ if (err) {
+ dev_err(dev, "Failed to request IRQ %d: %d",
+ ec_dev->irq, err);
+ return err;
+ }
+ }
+
+ /* Register a platform device for the main EC instance */
+ ec_dev->ec = platform_device_register_data(ec_dev->dev, "cros-ec-dev",
+ PLATFORM_DEVID_AUTO, &ec_p,
+ sizeof(struct cros_ec_platform));
+ if (IS_ERR(ec_dev->ec)) {
+ dev_err(ec_dev->dev,
+ "Failed to create CrOS EC platform device\n");
+ return PTR_ERR(ec_dev->ec);
+ }
+
+ if (ec_dev->max_passthru) {
+ /*
+ * Register a platform device for the PD behind the main EC.
+ * We make the following assumptions:
+ * - behind an EC, we have a pd
+ * - only one device added.
+ * - the EC is responsive at init time (it is not true for a
+ * sensor hub).
+ */
+ ec_dev->pd = platform_device_register_data(ec_dev->dev,
+ "cros-ec-dev",
+ PLATFORM_DEVID_AUTO, &pd_p,
+ sizeof(struct cros_ec_platform));
+ if (IS_ERR(ec_dev->pd)) {
+ dev_err(ec_dev->dev,
+ "Failed to create CrOS PD platform device\n");
+ platform_device_unregister(ec_dev->ec);
+ return PTR_ERR(ec_dev->pd);
+ }
+ }
+
+ if (IS_ENABLED(CONFIG_OF) && dev->of_node) {
+ err = devm_of_platform_populate(dev);
+ if (err) {
+ platform_device_unregister(ec_dev->pd);
+ platform_device_unregister(ec_dev->ec);
+ dev_err(dev, "Failed to register sub-devices\n");
+ return err;
+ }
+ }
+
+ /*
+ * Clear sleep event - this will fail harmlessly on platforms that
+ * don't implement the sleep event host command.
+ */
+ err = cros_ec_sleep_event(ec_dev, 0);
+ if (err < 0)
+ dev_dbg(ec_dev->dev, "Error %d clearing sleep event to ec",
+ err);
+
+ dev_info(dev, "Chrome EC device registered\n");
+
+ return 0;
+}
+EXPORT_SYMBOL(cros_ec_register);
+
+int cros_ec_unregister(struct cros_ec_device *ec_dev)
+{
+ if (ec_dev->pd)
+ platform_device_unregister(ec_dev->pd);
+ platform_device_unregister(ec_dev->ec);
+
+ return 0;
+}
+EXPORT_SYMBOL(cros_ec_unregister);
+
+#ifdef CONFIG_PM_SLEEP
+int cros_ec_suspend(struct cros_ec_device *ec_dev)
+{
+ struct device *dev = ec_dev->dev;
+ int ret;
+ u8 sleep_event;
+
+ sleep_event = (!IS_ENABLED(CONFIG_ACPI) || pm_suspend_via_firmware()) ?
+ HOST_SLEEP_EVENT_S3_SUSPEND :
+ HOST_SLEEP_EVENT_S0IX_SUSPEND;
+
+ ret = cros_ec_sleep_event(ec_dev, sleep_event);
+ if (ret < 0)
+ dev_dbg(ec_dev->dev, "Error %d sending suspend event to ec",
+ ret);
+
+ if (device_may_wakeup(dev))
+ ec_dev->wake_enabled = !enable_irq_wake(ec_dev->irq);
+
+ disable_irq(ec_dev->irq);
+ ec_dev->was_wake_device = ec_dev->wake_enabled;
+ ec_dev->suspended = true;
+
+ return 0;
+}
+EXPORT_SYMBOL(cros_ec_suspend);
+
+static void cros_ec_report_events_during_suspend(struct cros_ec_device *ec_dev)
+{
+ while (ec_dev->mkbp_event_supported &&
+ cros_ec_get_next_event(ec_dev, NULL) > 0)
+ blocking_notifier_call_chain(&ec_dev->event_notifier,
+ 1, ec_dev);
+}
+
+int cros_ec_resume(struct cros_ec_device *ec_dev)
+{
+ int ret;
+ u8 sleep_event;
+
+ ec_dev->suspended = false;
+ enable_irq(ec_dev->irq);
+
+ sleep_event = (!IS_ENABLED(CONFIG_ACPI) || pm_suspend_via_firmware()) ?
+ HOST_SLEEP_EVENT_S3_RESUME :
+ HOST_SLEEP_EVENT_S0IX_RESUME;
+
+ ret = cros_ec_sleep_event(ec_dev, sleep_event);
+ if (ret < 0)
+ dev_dbg(ec_dev->dev, "Error %d sending resume event to ec",
+ ret);
+
+ if (ec_dev->wake_enabled) {
+ disable_irq_wake(ec_dev->irq);
+ ec_dev->wake_enabled = 0;
+ }
+ /*
+ * Let the mfd devices know about events that occur during
+ * suspend. This way the clients know what to do with them.
+ */
+ cros_ec_report_events_during_suspend(ec_dev);
+
+
+ return 0;
+}
+EXPORT_SYMBOL(cros_ec_resume);
+
+#endif
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("ChromeOS EC core driver");
diff --git a/drivers/platform/chrome/cros_ec_chardev.c b/drivers/platform/chrome/cros_ec_chardev.c
new file mode 100644
index 000000000000..74ded441bb50
--- /dev/null
+++ b/drivers/platform/chrome/cros_ec_chardev.c
@@ -0,0 +1,419 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Miscellaneous character driver for ChromeOS Embedded Controller
+ *
+ * Copyright 2014 Google, Inc.
+ * Copyright 2019 Google LLC
+ *
+ * This file is a rework and part of the code is ported from
+ * drivers/mfd/cros_ec_dev.c that was originally written by
+ * Bill Richardson.
+ */
+
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/mfd/cros_ec.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/notifier.h>
+#include <linux/platform_data/cros_ec_chardev.h>
+#include <linux/platform_data/cros_ec_commands.h>
+#include <linux/platform_data/cros_ec_proto.h>
+#include <linux/platform_device.h>
+#include <linux/poll.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+
+#define DRV_NAME "cros-ec-chardev"
+
+/* Arbitrary bounded size for the event queue */
+#define CROS_MAX_EVENT_LEN PAGE_SIZE
+
+struct chardev_data {
+ struct cros_ec_dev *ec_dev;
+ struct miscdevice misc;
+};
+
+struct chardev_priv {
+ struct cros_ec_dev *ec_dev;
+ struct notifier_block notifier;
+ wait_queue_head_t wait_event;
+ unsigned long event_mask;
+ struct list_head events;
+ size_t event_len;
+};
+
+struct ec_event {
+ struct list_head node;
+ size_t size;
+ u8 event_type;
+ u8 data[0];
+};
+
+static int ec_get_version(struct cros_ec_dev *ec, char *str, int maxlen)
+{
+ static const char * const current_image_name[] = {
+ "unknown", "read-only", "read-write", "invalid",
+ };
+ struct ec_response_get_version *resp;
+ struct cros_ec_command *msg;
+ int ret;
+
+ msg = kzalloc(sizeof(*msg) + sizeof(*resp), GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ msg->command = EC_CMD_GET_VERSION + ec->cmd_offset;
+ msg->insize = sizeof(*resp);
+
+ ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
+ if (ret < 0) {
+ snprintf(str, maxlen,
+ "Unknown EC version, returned error: %d\n",
+ msg->result);
+ goto exit;
+ }
+
+ resp = (struct ec_response_get_version *)msg->data;
+ if (resp->current_image >= ARRAY_SIZE(current_image_name))
+ resp->current_image = 3; /* invalid */
+
+ snprintf(str, maxlen, "%s\n%s\n%s\n%s\n", CROS_EC_DEV_VERSION,
+ resp->version_string_ro, resp->version_string_rw,
+ current_image_name[resp->current_image]);
+
+ ret = 0;
+exit:
+ kfree(msg);
+ return ret;
+}
+
+static int cros_ec_chardev_mkbp_event(struct notifier_block *nb,
+ unsigned long queued_during_suspend,
+ void *_notify)
+{
+ struct chardev_priv *priv = container_of(nb, struct chardev_priv,
+ notifier);
+ struct cros_ec_device *ec_dev = priv->ec_dev->ec_dev;
+ struct ec_event *event;
+ unsigned long event_bit = 1 << ec_dev->event_data.event_type;
+ int total_size = sizeof(*event) + ec_dev->event_size;
+
+ if (!(event_bit & priv->event_mask) ||
+ (priv->event_len + total_size) > CROS_MAX_EVENT_LEN)
+ return NOTIFY_DONE;
+
+ event = kzalloc(total_size, GFP_KERNEL);
+ if (!event)
+ return NOTIFY_DONE;
+
+ event->size = ec_dev->event_size;
+ event->event_type = ec_dev->event_data.event_type;
+ memcpy(event->data, &ec_dev->event_data.data, ec_dev->event_size);
+
+ spin_lock(&priv->wait_event.lock);
+ list_add_tail(&event->node, &priv->events);
+ priv->event_len += total_size;
+ wake_up_locked(&priv->wait_event);
+ spin_unlock(&priv->wait_event.lock);
+
+ return NOTIFY_OK;
+}
+
+static struct ec_event *cros_ec_chardev_fetch_event(struct chardev_priv *priv,
+ bool fetch, bool block)
+{
+ struct ec_event *event;
+ int err;
+
+ spin_lock(&priv->wait_event.lock);
+ if (!block && list_empty(&priv->events)) {
+ event = ERR_PTR(-EWOULDBLOCK);
+ goto out;
+ }
+
+ if (!fetch) {
+ event = NULL;
+ goto out;
+ }
+
+ err = wait_event_interruptible_locked(priv->wait_event,
+ !list_empty(&priv->events));
+ if (err) {
+ event = ERR_PTR(err);
+ goto out;
+ }
+
+ event = list_first_entry(&priv->events, struct ec_event, node);
+ list_del(&event->node);
+ priv->event_len -= sizeof(*event) + event->size;
+
+out:
+ spin_unlock(&priv->wait_event.lock);
+ return event;
+}
+
+/*
+ * Device file ops
+ */
+static int cros_ec_chardev_open(struct inode *inode, struct file *filp)
+{
+ struct miscdevice *mdev = filp->private_data;
+ struct cros_ec_dev *ec_dev = dev_get_drvdata(mdev->parent);
+ struct chardev_priv *priv;
+ int ret;
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->ec_dev = ec_dev;
+ filp->private_data = priv;
+ INIT_LIST_HEAD(&priv->events);
+ init_waitqueue_head(&priv->wait_event);
+ nonseekable_open(inode, filp);
+
+ priv->notifier.notifier_call = cros_ec_chardev_mkbp_event;
+ ret = blocking_notifier_chain_register(&ec_dev->ec_dev->event_notifier,
+ &priv->notifier);
+ if (ret) {
+ dev_err(ec_dev->dev, "failed to register event notifier\n");
+ kfree(priv);
+ }
+
+ return ret;
+}
+
+static __poll_t cros_ec_chardev_poll(struct file *filp, poll_table *wait)
+{
+ struct chardev_priv *priv = filp->private_data;
+
+ poll_wait(filp, &priv->wait_event, wait);
+
+ if (list_empty(&priv->events))
+ return 0;
+
+ return EPOLLIN | EPOLLRDNORM;
+}
+
+static ssize_t cros_ec_chardev_read(struct file *filp, char __user *buffer,
+ size_t length, loff_t *offset)
+{
+ char msg[sizeof(struct ec_response_get_version) +
+ sizeof(CROS_EC_DEV_VERSION)];
+ struct chardev_priv *priv = filp->private_data;
+ struct cros_ec_dev *ec_dev = priv->ec_dev;
+ size_t count;
+ int ret;
+
+ if (priv->event_mask) { /* queued MKBP event */
+ struct ec_event *event;
+
+ event = cros_ec_chardev_fetch_event(priv, length != 0,
+ !(filp->f_flags & O_NONBLOCK));
+ if (IS_ERR(event))
+ return PTR_ERR(event);
+ /*
+ * length == 0 is special - no IO is done but we check
+ * for error conditions.
+ */
+ if (length == 0)
+ return 0;
+
+ /* The event is 1 byte of type plus the payload */
+ count = min(length, event->size + 1);
+ ret = copy_to_user(buffer, &event->event_type, count);
+ kfree(event);
+ if (ret) /* the copy failed */
+ return -EFAULT;
+ *offset = count;
+ return count;
+ }
+
+ /*
+ * Legacy behavior if no event mask is defined
+ */
+ if (*offset != 0)
+ return 0;
+
+ ret = ec_get_version(ec_dev, msg, sizeof(msg));
+ if (ret)
+ return ret;
+
+ count = min(length, strlen(msg));
+
+ if (copy_to_user(buffer, msg, count))
+ return -EFAULT;
+
+ *offset = count;
+ return count;
+}
+
+static int cros_ec_chardev_release(struct inode *inode, struct file *filp)
+{
+ struct chardev_priv *priv = filp->private_data;
+ struct cros_ec_dev *ec_dev = priv->ec_dev;
+ struct ec_event *event, *e;
+
+ blocking_notifier_chain_unregister(&ec_dev->ec_dev->event_notifier,
+ &priv->notifier);
+
+ list_for_each_entry_safe(event, e, &priv->events, node) {
+ list_del(&event->node);
+ kfree(event);
+ }
+ kfree(priv);
+
+ return 0;
+}
+
+/*
+ * Ioctls
+ */
+static long cros_ec_chardev_ioctl_xcmd(struct cros_ec_dev *ec, void __user *arg)
+{
+ struct cros_ec_command *s_cmd;
+ struct cros_ec_command u_cmd;
+ long ret;
+
+ if (copy_from_user(&u_cmd, arg, sizeof(u_cmd)))
+ return -EFAULT;
+
+ if (u_cmd.outsize > EC_MAX_MSG_BYTES ||
+ u_cmd.insize > EC_MAX_MSG_BYTES)
+ return -EINVAL;
+
+ s_cmd = kmalloc(sizeof(*s_cmd) + max(u_cmd.outsize, u_cmd.insize),
+ GFP_KERNEL);
+ if (!s_cmd)
+ return -ENOMEM;
+
+ if (copy_from_user(s_cmd, arg, sizeof(*s_cmd) + u_cmd.outsize)) {
+ ret = -EFAULT;
+ goto exit;
+ }
+
+ if (u_cmd.outsize != s_cmd->outsize ||
+ u_cmd.insize != s_cmd->insize) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ s_cmd->command += ec->cmd_offset;
+ ret = cros_ec_cmd_xfer(ec->ec_dev, s_cmd);
+ /* Only copy data to userland if data was received. */
+ if (ret < 0)
+ goto exit;
+
+ if (copy_to_user(arg, s_cmd, sizeof(*s_cmd) + s_cmd->insize))
+ ret = -EFAULT;
+exit:
+ kfree(s_cmd);
+ return ret;
+}
+
+static long cros_ec_chardev_ioctl_readmem(struct cros_ec_dev *ec,
+ void __user *arg)
+{
+ struct cros_ec_device *ec_dev = ec->ec_dev;
+ struct cros_ec_readmem s_mem = { };
+ long num;
+
+ /* Not every platform supports direct reads */
+ if (!ec_dev->cmd_readmem)
+ return -ENOTTY;
+
+ if (copy_from_user(&s_mem, arg, sizeof(s_mem)))
+ return -EFAULT;
+
+ num = ec_dev->cmd_readmem(ec_dev, s_mem.offset, s_mem.bytes,
+ s_mem.buffer);
+ if (num <= 0)
+ return num;
+
+ if (copy_to_user((void __user *)arg, &s_mem, sizeof(s_mem)))
+ return -EFAULT;
+
+ return num;
+}
+
+static long cros_ec_chardev_ioctl(struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ struct chardev_priv *priv = filp->private_data;
+ struct cros_ec_dev *ec = priv->ec_dev;
+
+ if (_IOC_TYPE(cmd) != CROS_EC_DEV_IOC)
+ return -ENOTTY;
+
+ switch (cmd) {
+ case CROS_EC_DEV_IOCXCMD:
+ return cros_ec_chardev_ioctl_xcmd(ec, (void __user *)arg);
+ case CROS_EC_DEV_IOCRDMEM:
+ return cros_ec_chardev_ioctl_readmem(ec, (void __user *)arg);
+ case CROS_EC_DEV_IOCEVENTMASK:
+ priv->event_mask = arg;
+ return 0;
+ }
+
+ return -ENOTTY;
+}
+
+static const struct file_operations chardev_fops = {
+ .open = cros_ec_chardev_open,
+ .poll = cros_ec_chardev_poll,
+ .read = cros_ec_chardev_read,
+ .release = cros_ec_chardev_release,
+ .unlocked_ioctl = cros_ec_chardev_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = cros_ec_chardev_ioctl,
+#endif
+};
+
+static int cros_ec_chardev_probe(struct platform_device *pdev)
+{
+ struct cros_ec_dev *ec_dev = dev_get_drvdata(pdev->dev.parent);
+ struct cros_ec_platform *ec_platform = dev_get_platdata(ec_dev->dev);
+ struct chardev_data *data;
+
+ /* Create a char device: we want to create it anew */
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->ec_dev = ec_dev;
+ data->misc.minor = MISC_DYNAMIC_MINOR;
+ data->misc.fops = &chardev_fops;
+ data->misc.name = ec_platform->ec_name;
+ data->misc.parent = pdev->dev.parent;
+
+ dev_set_drvdata(&pdev->dev, data);
+
+ return misc_register(&data->misc);
+}
+
+static int cros_ec_chardev_remove(struct platform_device *pdev)
+{
+ struct chardev_data *data = dev_get_drvdata(&pdev->dev);
+
+ misc_deregister(&data->misc);
+
+ return 0;
+}
+
+static struct platform_driver cros_ec_chardev_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ },
+ .probe = cros_ec_chardev_probe,
+ .remove = cros_ec_chardev_remove,
+};
+
+module_platform_driver(cros_ec_chardev_driver);
+
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_AUTHOR("Enric Balletbo i Serra <enric.balletbo@collabora.com>");
+MODULE_DESCRIPTION("ChromeOS EC Miscellaneous Character Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/platform/chrome/cros_ec_debugfs.c b/drivers/platform/chrome/cros_ec_debugfs.c
index 8ec1cc2889f2..6ae484989d1f 100644
--- a/drivers/platform/chrome/cros_ec_debugfs.c
+++ b/drivers/platform/chrome/cros_ec_debugfs.c
@@ -8,9 +8,10 @@
#include <linux/delay.h>
#include <linux/fs.h>
#include <linux/mfd/cros_ec.h>
-#include <linux/mfd/cros_ec_commands.h>
#include <linux/module.h>
#include <linux/mutex.h>
+#include <linux/platform_data/cros_ec_commands.h>
+#include <linux/platform_data/cros_ec_proto.h>
#include <linux/platform_device.h>
#include <linux/poll.h>
#include <linux/sched.h>
diff --git a/drivers/platform/chrome/cros_ec_i2c.c b/drivers/platform/chrome/cros_ec_i2c.c
index 61d75395f86d..9bd97bc8454b 100644
--- a/drivers/platform/chrome/cros_ec_i2c.c
+++ b/drivers/platform/chrome/cros_ec_i2c.c
@@ -9,8 +9,8 @@
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
-#include <linux/mfd/cros_ec.h>
-#include <linux/mfd/cros_ec_commands.h>
+#include <linux/platform_data/cros_ec_commands.h>
+#include <linux/platform_data/cros_ec_proto.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
@@ -307,6 +307,13 @@ static int cros_ec_i2c_probe(struct i2c_client *client,
return 0;
}
+static int cros_ec_i2c_remove(struct i2c_client *client)
+{
+ struct cros_ec_device *ec_dev = i2c_get_clientdata(client);
+
+ return cros_ec_unregister(ec_dev);
+}
+
#ifdef CONFIG_PM_SLEEP
static int cros_ec_i2c_suspend(struct device *dev)
{
@@ -357,6 +364,7 @@ static struct i2c_driver cros_ec_driver = {
.pm = &cros_ec_i2c_pm_ops,
},
.probe = cros_ec_i2c_probe,
+ .remove = cros_ec_i2c_remove,
.id_table = cros_ec_i2c_id,
};
diff --git a/drivers/platform/chrome/cros_ec_ishtp.c b/drivers/platform/chrome/cros_ec_ishtp.c
index 430731cdf827..25ca2c894b4d 100644
--- a/drivers/platform/chrome/cros_ec_ishtp.c
+++ b/drivers/platform/chrome/cros_ec_ishtp.c
@@ -8,11 +8,10 @@
// (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/platform_data/cros_ec_commands.h>
+#include <linux/platform_data/cros_ec_proto.h>
#include <linux/intel-ish-client-if.h>
/*
diff --git a/drivers/platform/chrome/cros_ec_lightbar.c b/drivers/platform/chrome/cros_ec_lightbar.c
index 609598bbb6c3..c0f2eec35a48 100644
--- a/drivers/platform/chrome/cros_ec_lightbar.c
+++ b/drivers/platform/chrome/cros_ec_lightbar.c
@@ -9,8 +9,9 @@
#include <linux/fs.h>
#include <linux/kobject.h>
#include <linux/mfd/cros_ec.h>
-#include <linux/mfd/cros_ec_commands.h>
#include <linux/module.h>
+#include <linux/platform_data/cros_ec_commands.h>
+#include <linux/platform_data/cros_ec_proto.h>
#include <linux/platform_device.h>
#include <linux/sched.h>
#include <linux/types.h>
diff --git a/drivers/platform/chrome/cros_ec_lpc.c b/drivers/platform/chrome/cros_ec_lpc.c
index 2c44c7f3322a..7d10d909435f 100644
--- a/drivers/platform/chrome/cros_ec_lpc.c
+++ b/drivers/platform/chrome/cros_ec_lpc.c
@@ -16,9 +16,9 @@
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/interrupt.h>
-#include <linux/mfd/cros_ec.h>
-#include <linux/mfd/cros_ec_commands.h>
#include <linux/module.h>
+#include <linux/platform_data/cros_ec_commands.h>
+#include <linux/platform_data/cros_ec_proto.h>
#include <linux/platform_device.h>
#include <linux/printk.h>
#include <linux/suspend.h>
@@ -421,6 +421,7 @@ static int cros_ec_lpc_probe(struct platform_device *pdev)
static int cros_ec_lpc_remove(struct platform_device *pdev)
{
+ struct cros_ec_device *ec_dev = platform_get_drvdata(pdev);
struct acpi_device *adev;
adev = ACPI_COMPANION(&pdev->dev);
@@ -428,7 +429,7 @@ static int cros_ec_lpc_remove(struct platform_device *pdev)
acpi_remove_notify_handler(adev->handle, ACPI_ALL_NOTIFY,
cros_ec_lpc_acpi_notify);
- return 0;
+ return cros_ec_unregister(ec_dev);
}
static const struct acpi_device_id cros_ec_lpc_acpi_device_ids[] = {
diff --git a/drivers/platform/chrome/cros_ec_proto.c b/drivers/platform/chrome/cros_ec_proto.c
index 3d2325197a68..f659f96bda12 100644
--- a/drivers/platform/chrome/cros_ec_proto.c
+++ b/drivers/platform/chrome/cros_ec_proto.c
@@ -3,10 +3,11 @@
//
// Copyright (C) 2015 Google, Inc
-#include <linux/mfd/cros_ec.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/module.h>
+#include <linux/platform_data/cros_ec_commands.h>
+#include <linux/platform_data/cros_ec_proto.h>
#include <linux/slab.h>
#include <asm/unaligned.h>
diff --git a/drivers/platform/chrome/cros_ec_rpmsg.c b/drivers/platform/chrome/cros_ec_rpmsg.c
index 5d3fb2abad1d..0c3738c3244d 100644
--- a/drivers/platform/chrome/cros_ec_rpmsg.c
+++ b/drivers/platform/chrome/cros_ec_rpmsg.c
@@ -6,9 +6,9 @@
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/mfd/cros_ec.h>
-#include <linux/mfd/cros_ec_commands.h>
#include <linux/of.h>
+#include <linux/platform_data/cros_ec_commands.h>
+#include <linux/platform_data/cros_ec_proto.h>
#include <linux/platform_device.h>
#include <linux/rpmsg.h>
#include <linux/slab.h>
@@ -41,6 +41,7 @@ struct cros_ec_rpmsg {
struct rpmsg_device *rpdev;
struct completion xfer_ack;
struct work_struct host_event_work;
+ struct rpmsg_endpoint *ept;
};
/**
@@ -72,7 +73,6 @@ static int cros_ec_pkt_xfer_rpmsg(struct cros_ec_device *ec_dev,
struct cros_ec_command *ec_msg)
{
struct cros_ec_rpmsg *ec_rpmsg = ec_dev->priv;
- struct rpmsg_device *rpdev = ec_rpmsg->rpdev;
struct ec_host_response *response;
unsigned long timeout;
int len;
@@ -85,7 +85,7 @@ static int cros_ec_pkt_xfer_rpmsg(struct cros_ec_device *ec_dev,
dev_dbg(ec_dev->dev, "prepared, len=%d\n", len);
reinit_completion(&ec_rpmsg->xfer_ack);
- ret = rpmsg_send(rpdev->ept, ec_dev->dout, len);
+ ret = rpmsg_send(ec_rpmsg->ept, ec_dev->dout, len);
if (ret) {
dev_err(ec_dev->dev, "rpmsg send failed\n");
return ret;
@@ -196,11 +196,24 @@ static int cros_ec_rpmsg_callback(struct rpmsg_device *rpdev, void *data,
return 0;
}
+static struct rpmsg_endpoint *
+cros_ec_rpmsg_create_ept(struct rpmsg_device *rpdev)
+{
+ struct rpmsg_channel_info chinfo = {};
+
+ strscpy(chinfo.name, rpdev->id.name, RPMSG_NAME_SIZE);
+ chinfo.src = rpdev->src;
+ chinfo.dst = RPMSG_ADDR_ANY;
+
+ return rpmsg_create_ept(rpdev, cros_ec_rpmsg_callback, NULL, chinfo);
+}
+
static int cros_ec_rpmsg_probe(struct rpmsg_device *rpdev)
{
struct device *dev = &rpdev->dev;
struct cros_ec_rpmsg *ec_rpmsg;
struct cros_ec_device *ec_dev;
+ int ret;
ec_dev = devm_kzalloc(dev, sizeof(*ec_dev), GFP_KERNEL);
if (!ec_dev)
@@ -225,7 +238,18 @@ static int cros_ec_rpmsg_probe(struct rpmsg_device *rpdev)
INIT_WORK(&ec_rpmsg->host_event_work,
cros_ec_rpmsg_host_event_function);
- return cros_ec_register(ec_dev);
+ ec_rpmsg->ept = cros_ec_rpmsg_create_ept(rpdev);
+ if (!ec_rpmsg->ept)
+ return -ENOMEM;
+
+ ret = cros_ec_register(ec_dev);
+ if (ret < 0) {
+ rpmsg_destroy_ept(ec_rpmsg->ept);
+ cancel_work_sync(&ec_rpmsg->host_event_work);
+ return ret;
+ }
+
+ return 0;
}
static void cros_ec_rpmsg_remove(struct rpmsg_device *rpdev)
@@ -233,9 +257,30 @@ static void cros_ec_rpmsg_remove(struct rpmsg_device *rpdev)
struct cros_ec_device *ec_dev = dev_get_drvdata(&rpdev->dev);
struct cros_ec_rpmsg *ec_rpmsg = ec_dev->priv;
+ cros_ec_unregister(ec_dev);
+ rpmsg_destroy_ept(ec_rpmsg->ept);
cancel_work_sync(&ec_rpmsg->host_event_work);
}
+#ifdef CONFIG_PM_SLEEP
+static int cros_ec_rpmsg_suspend(struct device *dev)
+{
+ struct cros_ec_device *ec_dev = dev_get_drvdata(dev);
+
+ return cros_ec_suspend(ec_dev);
+}
+
+static int cros_ec_rpmsg_resume(struct device *dev)
+{
+ struct cros_ec_device *ec_dev = dev_get_drvdata(dev);
+
+ return cros_ec_resume(ec_dev);
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(cros_ec_rpmsg_pm_ops, cros_ec_rpmsg_suspend,
+ cros_ec_rpmsg_resume);
+
static const struct of_device_id cros_ec_rpmsg_of_match[] = {
{ .compatible = "google,cros-ec-rpmsg", },
{ }
@@ -246,10 +291,10 @@ static struct rpmsg_driver cros_ec_driver_rpmsg = {
.drv = {
.name = "cros-ec-rpmsg",
.of_match_table = cros_ec_rpmsg_of_match,
+ .pm = &cros_ec_rpmsg_pm_ops,
},
.probe = cros_ec_rpmsg_probe,
.remove = cros_ec_rpmsg_remove,
- .callback = cros_ec_rpmsg_callback,
};
module_rpmsg_driver(cros_ec_driver_rpmsg);
diff --git a/drivers/platform/chrome/cros_ec_spi.c b/drivers/platform/chrome/cros_ec_spi.c
index 714306bc3f79..a831bd5a5b2f 100644
--- a/drivers/platform/chrome/cros_ec_spi.c
+++ b/drivers/platform/chrome/cros_ec_spi.c
@@ -6,9 +6,9 @@
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/mfd/cros_ec.h>
-#include <linux/mfd/cros_ec_commands.h>
#include <linux/of.h>
+#include <linux/platform_data/cros_ec_commands.h>
+#include <linux/platform_data/cros_ec_proto.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/spi/spi.h>
@@ -785,6 +785,13 @@ static int cros_ec_spi_probe(struct spi_device *spi)
return 0;
}
+static int cros_ec_spi_remove(struct spi_device *spi)
+{
+ struct cros_ec_device *ec_dev = spi_get_drvdata(spi);
+
+ return cros_ec_unregister(ec_dev);
+}
+
#ifdef CONFIG_PM_SLEEP
static int cros_ec_spi_suspend(struct device *dev)
{
@@ -823,6 +830,7 @@ static struct spi_driver cros_ec_driver_spi = {
.pm = &cros_ec_spi_pm_ops,
},
.probe = cros_ec_spi_probe,
+ .remove = cros_ec_spi_remove,
.id_table = cros_ec_spi_id,
};
diff --git a/drivers/platform/chrome/cros_ec_sysfs.c b/drivers/platform/chrome/cros_ec_sysfs.c
index 3edb237bf8ed..74d36b8d4f46 100644
--- a/drivers/platform/chrome/cros_ec_sysfs.c
+++ b/drivers/platform/chrome/cros_ec_sysfs.c
@@ -9,8 +9,9 @@
#include <linux/fs.h>
#include <linux/kobject.h>
#include <linux/mfd/cros_ec.h>
-#include <linux/mfd/cros_ec_commands.h>
#include <linux/module.h>
+#include <linux/platform_data/cros_ec_commands.h>
+#include <linux/platform_data/cros_ec_proto.h>
#include <linux/platform_device.h>
#include <linux/printk.h>
#include <linux/slab.h>
diff --git a/drivers/platform/chrome/cros_ec_trace.c b/drivers/platform/chrome/cros_ec_trace.c
index 0a76412095a9..6f80ff4532ae 100644
--- a/drivers/platform/chrome/cros_ec_trace.c
+++ b/drivers/platform/chrome/cros_ec_trace.c
@@ -6,7 +6,7 @@
#define TRACE_SYMBOL(a) {a, #a}
// Generate the list using the following script:
-// sed -n 's/^#define \(EC_CMD_[[:alnum:]_]*\)\s.*/\tTRACE_SYMBOL(\1), \\/p' include/linux/mfd/cros_ec_commands.h
+// sed -n 's/^#define \(EC_CMD_[[:alnum:]_]*\)\s.*/\tTRACE_SYMBOL(\1), \\/p' include/linux/platform_data/cros_ec_commands.h
#define EC_CMDS \
TRACE_SYMBOL(EC_CMD_PROTO_VERSION), \
TRACE_SYMBOL(EC_CMD_HELLO), \
diff --git a/drivers/platform/chrome/cros_ec_trace.h b/drivers/platform/chrome/cros_ec_trace.h
index 7ae3b89c78b9..0dd4df30fa89 100644
--- a/drivers/platform/chrome/cros_ec_trace.h
+++ b/drivers/platform/chrome/cros_ec_trace.h
@@ -11,8 +11,10 @@
#if !defined(_CROS_EC_TRACE_H_) || defined(TRACE_HEADER_MULTI_READ)
#define _CROS_EC_TRACE_H_
+#include <linux/bits.h>
#include <linux/types.h>
-#include <linux/mfd/cros_ec.h>
+#include <linux/platform_data/cros_ec_commands.h>
+#include <linux/platform_data/cros_ec_proto.h>
#include <linux/tracepoint.h>
diff --git a/drivers/platform/chrome/cros_ec_vbc.c b/drivers/platform/chrome/cros_ec_vbc.c
index 2aaefed87eb4..f11a1283e5c8 100644
--- a/drivers/platform/chrome/cros_ec_vbc.c
+++ b/drivers/platform/chrome/cros_ec_vbc.c
@@ -7,8 +7,9 @@
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/mfd/cros_ec.h>
-#include <linux/mfd/cros_ec_commands.h>
#include <linux/module.h>
+#include <linux/platform_data/cros_ec_commands.h>
+#include <linux/platform_data/cros_ec_proto.h>
#include <linux/slab.h>
#define DRV_NAME "cros-ec-vbc"
diff --git a/drivers/platform/chrome/cros_usbpd_logger.c b/drivers/platform/chrome/cros_usbpd_logger.c
index 7c7b267626a0..2430e8b82810 100644
--- a/drivers/platform/chrome/cros_usbpd_logger.c
+++ b/drivers/platform/chrome/cros_usbpd_logger.c
@@ -6,10 +6,11 @@
*/
#include <linux/ktime.h>
-#include <linux/math64.h>
#include <linux/mfd/cros_ec.h>
-#include <linux/mfd/cros_ec_commands.h>
+#include <linux/math64.h>
#include <linux/module.h>
+#include <linux/platform_data/cros_ec_commands.h>
+#include <linux/platform_data/cros_ec_proto.h>
#include <linux/platform_device.h>
#include <linux/rtc.h>
@@ -209,6 +210,9 @@ static int cros_usbpd_logger_probe(struct platform_device *pd)
/* Retrieve PD event logs periodically */
INIT_DELAYED_WORK(&logger->log_work, cros_usbpd_log_check);
logger->log_workqueue = create_singlethread_workqueue("cros_usbpd_log");
+ if (!logger->log_workqueue)
+ return -ENOMEM;
+
queue_delayed_work(logger->log_workqueue, &logger->log_work,
CROS_USBPD_LOG_UPDATE_DELAY);
diff --git a/drivers/platform/chrome/wilco_ec/telemetry.c b/drivers/platform/chrome/wilco_ec/telemetry.c
index 94cdc166c840..b9d03c33d8dc 100644
--- a/drivers/platform/chrome/wilco_ec/telemetry.c
+++ b/drivers/platform/chrome/wilco_ec/telemetry.c
@@ -9,7 +9,7 @@
* 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
+ * of the allowlisted commands, and that no extraneous data is
* being transmitted to the EC. The response is passed directly
* back to the reader with no modification.
*
@@ -59,21 +59,10 @@ static DEFINE_IDA(telem_ida);
#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 WILCO_EC_TELEM_GET_BATT_PPID_INFO 0x8A
#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.
@@ -122,6 +111,32 @@ struct telem_args_get_batt_ext_info {
u8 var_args[5];
} __packed;
+struct telem_args_get_batt_ppid_info {
+ u8 always1; /* Should always be 1 */
+} __packed;
+
+/**
+ * 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;
+ union {
+ u8 buf[TELEM_ARGS_SIZE_MAX];
+ struct telem_args_get_log get_log;
+ struct telem_args_get_version get_version;
+ struct telem_args_get_fan_info get_fan_info;
+ struct telem_args_get_diag_info get_diag_info;
+ struct telem_args_get_temp_info get_temp_info;
+ struct telem_args_get_temp_read get_temp_read;
+ struct telem_args_get_batt_ext_info get_batt_ext_info;
+ struct telem_args_get_batt_ppid_info get_batt_ppid_info;
+ } args;
+} __packed;
+
/**
* check_telem_request() - Ensure that a request from userspace is valid.
* @rq: Request buffer copied from userspace.
@@ -133,7 +148,7 @@ struct telem_args_get_batt_ext_info {
* 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.
+ * 2. The supplied command code is one of the allowlisted 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,
@@ -146,25 +161,31 @@ static int check_telem_request(struct wilco_ec_telem_request *rq,
switch (rq->command) {
case WILCO_EC_TELEM_GET_LOG:
- max_size += sizeof(struct telem_args_get_log);
+ max_size += sizeof(rq->args.get_log);
break;
case WILCO_EC_TELEM_GET_VERSION:
- max_size += sizeof(struct telem_args_get_version);
+ max_size += sizeof(rq->args.get_version);
break;
case WILCO_EC_TELEM_GET_FAN_INFO:
- max_size += sizeof(struct telem_args_get_fan_info);
+ max_size += sizeof(rq->args.get_fan_info);
break;
case WILCO_EC_TELEM_GET_DIAG_INFO:
- max_size += sizeof(struct telem_args_get_diag_info);
+ max_size += sizeof(rq->args.get_diag_info);
break;
case WILCO_EC_TELEM_GET_TEMP_INFO:
- max_size += sizeof(struct telem_args_get_temp_info);
+ max_size += sizeof(rq->args.get_temp_info);
break;
case WILCO_EC_TELEM_GET_TEMP_READ:
- max_size += sizeof(struct telem_args_get_temp_read);
+ max_size += sizeof(rq->args.get_temp_read);
break;
case WILCO_EC_TELEM_GET_BATT_EXT_INFO:
- max_size += sizeof(struct telem_args_get_batt_ext_info);
+ max_size += sizeof(rq->args.get_batt_ext_info);
+ break;
+ case WILCO_EC_TELEM_GET_BATT_PPID_INFO:
+ if (rq->args.get_batt_ppid_info.always1 != 1)
+ return -EINVAL;
+
+ max_size += sizeof(rq->args.get_batt_ppid_info);
break;
default:
return -EINVAL;
@@ -250,6 +271,7 @@ static ssize_t telem_write(struct file *filp, const char __user *buf,
if (count > sizeof(sess_data->request))
return -EMSGSIZE;
+ memset(&sess_data->request, 0, sizeof(sess_data->request));
if (copy_from_user(&sess_data->request, buf, count))
return -EFAULT;
ret = check_telem_request(&sess_data->request, count);