From 7aa703bb8824384baad732043a925b46a4f3efa8 Mon Sep 17 00:00:00 2001 From: Enric Balletbo i Serra Date: Mon, 2 Sep 2019 11:53:00 +0200 Subject: mfd / platform: cros_ec: Handle chained ECs as platform devices An MFD is a device that contains several sub-devices (cells). For instance, the ChromeOS EC fits in this description as usually contains a charger and can have other devices with different functions like a Real-Time Clock, an Audio codec, a Real-Time Clock, ... If you look at the driver, though, we're doing something odd. We have two MFD cros-ec drivers where one of them (cros-ec-core) instantiates another MFD driver as sub-driver (cros-ec-dev), and the latest instantiates the different sub-devices (Real-Time Clock, Audio codec, etc). MFD ------------------------------------------ cros-ec-core |___ mfd-cellA (cros-ec-dev) | |__ mfd-cell0 | |__ mfd-cell1 | |__ ... | |___ mfd-cellB (cros-ec-dev) |__ mfd-cell0 |__ mfd-cell1 |__ ... The problem that was trying to solve is to describe some kind of topology for the case where we have an EC (cros-ec) chained with another EC (cros-pd). Apart from that this extends the bounds of what MFD was designed to do we might be interested on have other kinds of topology that can't be implemented in that way. Let's prepare the code to move the cros-ec-core part from MFD to platform/chrome as this is clearly a platform specific thing non-related to a MFD device. platform/chrome | MFD ------------------------------------------ | cros-ec ________|___ cros-ec-dev | |__ mfd-cell0 | |__ mfd-cell1 | |__ ... | cros-pd ________|___ cros-ec-dev | |__ mfd-cell0 | |__ mfd-cell1 | |__ ... Signed-off-by: Enric Balletbo i Serra Acked-by: Andy Shevchenko Reviewed-by: Gwendal Grignou Tested-by: Gwendal Grignou Signed-off-by: Lee Jones --- drivers/platform/chrome/cros_ec_i2c.c | 8 ++++++++ drivers/platform/chrome/cros_ec_lpc.c | 3 ++- drivers/platform/chrome/cros_ec_rpmsg.c | 2 ++ drivers/platform/chrome/cros_ec_spi.c | 8 ++++++++ 4 files changed, 20 insertions(+), 1 deletion(-) (limited to 'drivers/platform') diff --git a/drivers/platform/chrome/cros_ec_i2c.c b/drivers/platform/chrome/cros_ec_i2c.c index 61d75395f86d..6bb82dfa7dae 100644 --- a/drivers/platform/chrome/cros_ec_i2c.c +++ b/drivers/platform/chrome/cros_ec_i2c.c @@ -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_lpc.c b/drivers/platform/chrome/cros_ec_lpc.c index 2c44c7f3322a..5939c4a5869c 100644 --- a/drivers/platform/chrome/cros_ec_lpc.c +++ b/drivers/platform/chrome/cros_ec_lpc.c @@ -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_rpmsg.c b/drivers/platform/chrome/cros_ec_rpmsg.c index 5d3fb2abad1d..520e507bfa54 100644 --- a/drivers/platform/chrome/cros_ec_rpmsg.c +++ b/drivers/platform/chrome/cros_ec_rpmsg.c @@ -233,6 +233,8 @@ 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); + cancel_work_sync(&ec_rpmsg->host_event_work); } diff --git a/drivers/platform/chrome/cros_ec_spi.c b/drivers/platform/chrome/cros_ec_spi.c index 006a8ff64057..2e21f2776063 100644 --- a/drivers/platform/chrome/cros_ec_spi.c +++ b/drivers/platform/chrome/cros_ec_spi.c @@ -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, }; -- cgit From 47f11e0b40e97f373da4efbacee0a9526c816ed5 Mon Sep 17 00:00:00 2001 From: Enric Balletbo i Serra Date: Mon, 2 Sep 2019 11:53:01 +0200 Subject: mfd / platform: cros_ec: Move cros-ec core driver out from MFD Now, the ChromeOS EC core driver has nothing related to an MFD device, so move that driver from the MFD subsystem to the platform/chrome subsystem. Signed-off-by: Enric Balletbo i Serra Acked-by: Andy Shevchenko Acked-by: Thierry Reding Acked-by: Mark Brown Acked-by: Wolfram Sang Acked-by: Neil Armstrong Acked-by: Alexandre Belloni Acked-by: Jonathan Cameron Acked-by: Benjamin Tissoires Acked-by: Dmitry Torokhov Acked-by: Sebastian Reichel Acked-by: Chanwoo Choi Reviewed-by: Gwendal Grignou Tested-by: Gwendal Grignou Signed-off-by: Lee Jones --- drivers/platform/chrome/Kconfig | 21 ++- drivers/platform/chrome/Makefile | 1 + drivers/platform/chrome/cros_ec.c | 280 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 298 insertions(+), 4 deletions(-) create mode 100644 drivers/platform/chrome/cros_ec.c (limited to 'drivers/platform') diff --git a/drivers/platform/chrome/Kconfig b/drivers/platform/chrome/Kconfig index 970679d0b6f6..eaeb04e07335 100644 --- a/drivers/platform/chrome/Kconfig +++ b/drivers/platform/chrome/Kconfig @@ -50,9 +50,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 +75,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 @@ -87,7 +100,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 +110,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. diff --git a/drivers/platform/chrome/Makefile b/drivers/platform/chrome/Makefile index fd0af05cc14c..12ff8de5ac7a 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 diff --git a/drivers/platform/chrome/cros_ec.c b/drivers/platform/chrome/cros_ec.c new file mode 100644 index 000000000000..a54ad47c7b02 --- /dev/null +++ b/drivers/platform/chrome/cros_ec.c @@ -0,0 +1,280 @@ +// 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 +#include +#include +#include +#include +#include +#include + +#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"); -- cgit From eda2e30c6684d67288edb841c6125d48c608a242 Mon Sep 17 00:00:00 2001 From: Enric Balletbo i Serra Date: Mon, 2 Sep 2019 11:53:02 +0200 Subject: mfd / platform: cros_ec: Miscellaneous character device to talk with the EC That's a driver to talk with the ChromeOS Embedded Controller via a miscellaneous character device, it creates an entry in /dev for every instance and implements basic file operations for communicating with the Embedded Controller with an userspace application. The API is moved to the uapi folder, which is supposed to contain the user space API of the kernel. Note that this will replace current character device interface implemented in the cros-ec-dev driver in the MFD subsystem. The idea is to move all the functionality that extends the bounds of what MFD was designed to platform/chrome subsystem. Signed-off-by: Enric Balletbo i Serra Acked-by: Andy Shevchenko Reviewed-by: Gwendal Grignou Tested-by: Gwendal Grignou Signed-off-by: Lee Jones --- drivers/platform/chrome/Kconfig | 11 ++ drivers/platform/chrome/Makefile | 1 + drivers/platform/chrome/cros_ec_chardev.c | 251 ++++++++++++++++++++++++++++++ 3 files changed, 263 insertions(+) create mode 100644 drivers/platform/chrome/cros_ec_chardev.c (limited to 'drivers/platform') diff --git a/drivers/platform/chrome/Kconfig b/drivers/platform/chrome/Kconfig index eaeb04e07335..bd3524bd6b37 100644 --- a/drivers/platform/chrome/Kconfig +++ b/drivers/platform/chrome/Kconfig @@ -136,6 +136,17 @@ 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_CHARDEV + default MFD_CROS_EC_CHARDEV + 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 diff --git a/drivers/platform/chrome/Makefile b/drivers/platform/chrome/Makefile index 12ff8de5ac7a..477ec3d1d1c9 100644 --- a/drivers/platform/chrome/Makefile +++ b/drivers/platform/chrome/Makefile @@ -15,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/cros_ec_chardev.c b/drivers/platform/chrome/cros_ec_chardev.c new file mode 100644 index 000000000000..174f940822c9 --- /dev/null +++ b/drivers/platform/chrome/cros_ec_chardev.c @@ -0,0 +1,251 @@ +// 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "cros-ec-chardev" + +struct chardev_data { + struct cros_ec_dev *ec_dev; + struct miscdevice misc; +}; + +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; +} + +/* + * 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); + + filp->private_data = ec_dev; + nonseekable_open(inode, filp); + + return 0; +} + +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 cros_ec_dev *ec = filp->private_data; + size_t count; + int ret; + + if (*offset != 0) + return 0; + + ret = ec_get_version(ec, 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; +} + +/* + * 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 cros_ec_dev *ec = filp->private_data; + + 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); + } + + return -ENOTTY; +} + +static const struct file_operations chardev_fops = { + .open = cros_ec_chardev_open, + .read = cros_ec_chardev_read, + .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 "); +MODULE_DESCRIPTION("ChromeOS EC Miscellaneous Character Driver"); +MODULE_LICENSE("GPL"); -- cgit From 2fa2b980e3fe187a0b916bfe6bd5822889b44d5e Mon Sep 17 00:00:00 2001 From: Enric Balletbo i Serra Date: Mon, 2 Sep 2019 11:53:04 +0200 Subject: mfd / platform: cros_ec: Rename config to a better name The cros-ec-dev is a multifunction device that now doesn't implement any chardev communication interface. MFD_CROS_EC_CHARDEV doesn't look a good name to describe that device and can cause confusion. Hence rename it to CROS_EC_DEV. Signed-off-by: Enric Balletbo i Serra Acked-by: Andy Shevchenko Reviewed-by: Gwendal Grignou Tested-by: Gwendal Grignou Signed-off-by: Lee Jones --- drivers/platform/chrome/Kconfig | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/chrome/Kconfig b/drivers/platform/chrome/Kconfig index bd3524bd6b37..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 @@ -87,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 @@ -138,8 +148,8 @@ config CROS_KBD_LED_BACKLIGHT config CROS_EC_CHARDEV tristate "ChromeOS EC miscdevice" - depends on MFD_CROS_EC_CHARDEV - default MFD_CROS_EC_CHARDEV + 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. @@ -149,8 +159,8 @@ config 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. @@ -160,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. @@ -171,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. @@ -182,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. -- cgit From 840d9f131f65b021e0a73f3371f3194897dba6ad Mon Sep 17 00:00:00 2001 From: Enric Balletbo i Serra Date: Mon, 2 Sep 2019 11:53:05 +0200 Subject: mfd / platform: cros_ec: Reorganize platform and mfd includes There is a bit of mess between cros-ec mfd includes and platform includes. For example, we have a linux/mfd/cros_ec.h include that exports the interface implemented in platform/chrome/cros_ec_proto.c. Or we have a linux/mfd/cros_ec_commands.h file that is non related to the multifunction device (in the sense that is not exporting any function of the mfd device). This causes crossed includes between mfd and platform/chrome subsystems and makes the code difficult to read, apart from creating 'curious' situations where a platform/chrome driver includes a linux/mfd/cros_ec.h file just to get the exported functions that are implemented in another platform/chrome driver. In order to have a better separation on what the cros-ec multifunction driver does and what the cros-ec core provides move and rework the affected includes doing: - Move cros_ec_commands.h to include/linux/platform_data/cros_ec_commands.h - Get rid of the parts that are implemented in the platform/chrome/cros_ec_proto.c driver from include/linux/mfd/cros_ec.h to a new file include/linux/platform_data/cros_ec_proto.h - Update all the drivers with the new includes, so - Drivers that only need to know about the protocol include - linux/platform_data/cros_ec_proto.h - linux/platform_data/cros_ec_commands.h - Drivers that need to know about the cros-ec mfd device also include - linux/mfd/cros_ec.h Signed-off-by: Enric Balletbo i Serra Acked-by: Andy Shevchenko Acked-by: Mark Brown Acked-by: Wolfram Sang Acked-by: Neil Armstrong Acked-by: Alexandre Belloni Acked-by: Jonathan Cameron Acked-by: Benjamin Tissoires Acked-by: Dmitry Torokhov Acked-by: Sebastian Reichel Acked-by: Chanwoo Choi Reviewed-by: Gwendal Grignou Tested-by: Gwendal Grignou Series changes: 3 - Fix dereferencing pointer to incomplete type 'struct cros_ec_dev' (lkp) Signed-off-by: Lee Jones --- drivers/platform/chrome/cros_ec.c | 3 ++- drivers/platform/chrome/cros_ec_chardev.c | 3 ++- drivers/platform/chrome/cros_ec_debugfs.c | 3 ++- drivers/platform/chrome/cros_ec_i2c.c | 4 ++-- drivers/platform/chrome/cros_ec_ishtp.c | 5 ++--- drivers/platform/chrome/cros_ec_lightbar.c | 3 ++- drivers/platform/chrome/cros_ec_lpc.c | 4 ++-- drivers/platform/chrome/cros_ec_proto.c | 3 ++- drivers/platform/chrome/cros_ec_rpmsg.c | 4 ++-- drivers/platform/chrome/cros_ec_spi.c | 4 ++-- drivers/platform/chrome/cros_ec_sysfs.c | 3 ++- drivers/platform/chrome/cros_ec_trace.c | 2 +- drivers/platform/chrome/cros_ec_trace.h | 4 +++- drivers/platform/chrome/cros_ec_vbc.c | 3 ++- drivers/platform/chrome/cros_usbpd_logger.c | 5 +++-- 15 files changed, 31 insertions(+), 22 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/chrome/cros_ec.c b/drivers/platform/chrome/cros_ec.c index a54ad47c7b02..fd77e6fa74c2 100644 --- a/drivers/platform/chrome/cros_ec.c +++ b/drivers/platform/chrome/cros_ec.c @@ -13,7 +13,8 @@ #include #include #include -#include +#include +#include #include #include diff --git a/drivers/platform/chrome/cros_ec_chardev.c b/drivers/platform/chrome/cros_ec_chardev.c index 174f940822c9..08abd7e5c7bf 100644 --- a/drivers/platform/chrome/cros_ec_chardev.c +++ b/drivers/platform/chrome/cros_ec_chardev.c @@ -14,10 +14,11 @@ #include #include #include -#include #include #include #include +#include +#include #include #include #include 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 #include #include -#include #include #include +#include +#include #include #include #include diff --git a/drivers/platform/chrome/cros_ec_i2c.c b/drivers/platform/chrome/cros_ec_i2c.c index 6bb82dfa7dae..9bd97bc8454b 100644 --- a/drivers/platform/chrome/cros_ec_i2c.c +++ b/drivers/platform/chrome/cros_ec_i2c.c @@ -9,8 +9,8 @@ #include #include #include -#include -#include +#include +#include #include #include diff --git a/drivers/platform/chrome/cros_ec_ishtp.c b/drivers/platform/chrome/cros_ec_ishtp.c index e504d255d5ce..7d050db5b1ca 100644 --- a/drivers/platform/chrome/cros_ec_ishtp.c +++ b/drivers/platform/chrome/cros_ec_ishtp.c @@ -8,11 +8,10 @@ // (ISH-TP). #include -#include -#include -#include #include #include +#include +#include #include /* 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 #include #include -#include #include +#include +#include #include #include #include diff --git a/drivers/platform/chrome/cros_ec_lpc.c b/drivers/platform/chrome/cros_ec_lpc.c index 5939c4a5869c..7d10d909435f 100644 --- a/drivers/platform/chrome/cros_ec_lpc.c +++ b/drivers/platform/chrome/cros_ec_lpc.c @@ -16,9 +16,9 @@ #include #include #include -#include -#include #include +#include +#include #include #include #include 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 #include #include #include +#include +#include #include #include diff --git a/drivers/platform/chrome/cros_ec_rpmsg.c b/drivers/platform/chrome/cros_ec_rpmsg.c index 520e507bfa54..9633e5417686 100644 --- a/drivers/platform/chrome/cros_ec_rpmsg.c +++ b/drivers/platform/chrome/cros_ec_rpmsg.c @@ -6,9 +6,9 @@ #include #include #include -#include -#include #include +#include +#include #include #include #include diff --git a/drivers/platform/chrome/cros_ec_spi.c b/drivers/platform/chrome/cros_ec_spi.c index 2e21f2776063..9006e1872942 100644 --- a/drivers/platform/chrome/cros_ec_spi.c +++ b/drivers/platform/chrome/cros_ec_spi.c @@ -6,9 +6,9 @@ #include #include #include -#include -#include #include +#include +#include #include #include #include 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 #include #include -#include #include +#include +#include #include #include #include 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 #include -#include +#include +#include #include 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 #include #include -#include #include +#include +#include #include #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..c549a9b49b56 100644 --- a/drivers/platform/chrome/cros_usbpd_logger.c +++ b/drivers/platform/chrome/cros_usbpd_logger.c @@ -6,10 +6,11 @@ */ #include -#include #include -#include +#include #include +#include +#include #include #include -- cgit