chrome platform changes for v5.4

* 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
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQQCtZK6p/AktxXfkOlzbaomhzOwwgUCXYKpygAKCRBzbaomhzOw
 wlkXAP9QCKia7LiNujIl9kh7WXSloxdO0BzL93pgSpNHfUDeSAD+Mlcp+54bDqkB
 WaF2SR14Z2vzAFafroQTl6m41xJTog4=
 =slD7
 -----END PGP SIGNATURE-----

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
This commit is contained in:
Linus Torvalds 2019-09-19 14:14:28 -07:00
commit 32b90daf5c
55 changed files with 1166 additions and 778 deletions

View file

@ -211,26 +211,18 @@ config MFD_AXP20X_RSB
components like regulators or the PEK (Power Enable Key) under the
corresponding menus.
config MFD_CROS_EC
tristate "ChromeOS Embedded Controller"
config MFD_CROS_EC_DEV
tristate "ChromeOS Embedded Controller multifunction device"
select MFD_CORE
select CHROME_PLATFORMS
select CROS_EC_PROTO
depends on X86 || ARM || ARM64 || COMPILE_TEST
depends on CROS_EC
default CROS_EC
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.
Select this to get support for ChromeOS Embedded Controller
sub-devices. This driver will instantiate additional drivers such
as RTC, USBPD, etc. but you have to select the individual drivers.
config MFD_CROS_EC_CHARDEV
tristate "Chrome OS Embedded Controller userspace device interface"
depends on MFD_CROS_EC
---help---
This driver adds support to talk with the ChromeOS EC from userspace.
If you have a supported Chromebook, choose Y or M here.
The module will be called cros_ec_dev.
To compile this driver as a module, choose M here: the module will be
called cros-ec-dev.
config MFD_MADERA
tristate "Cirrus Logic Madera codecs"

View file

@ -13,9 +13,7 @@ obj-$(CONFIG_MFD_ASIC3) += asic3.o tmio_core.o
obj-$(CONFIG_ARCH_BCM2835) += bcm2835-pm.o
obj-$(CONFIG_MFD_BCM590XX) += bcm590xx.o
obj-$(CONFIG_MFD_BD9571MWV) += bd9571mwv.o
cros_ec_core-objs := cros_ec.o
obj-$(CONFIG_MFD_CROS_EC) += cros_ec_core.o
obj-$(CONFIG_MFD_CROS_EC_CHARDEV) += cros_ec_dev.o
obj-$(CONFIG_MFD_CROS_EC_DEV) += cros_ec_dev.o
obj-$(CONFIG_MFD_EXYNOS_LPASS) += exynos-lpass.o
obj-$(CONFIG_HTC_PASIC3) += htc-pasic3.o

View file

@ -1,279 +0,0 @@
// 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/mfd/core.h>
#include <linux/mfd/cros_ec.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 const struct mfd_cell ec_cell = {
.name = "cros-ec-dev",
.platform_data = &ec_p,
.pdata_size = sizeof(ec_p),
};
static const struct mfd_cell ec_pd_cell = {
.name = "cros-ec-dev",
.platform_data = &pd_p,
.pdata_size = sizeof(pd_p),
};
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;
}
}
err = devm_mfd_add_devices(ec_dev->dev, PLATFORM_DEVID_AUTO, &ec_cell,
1, NULL, ec_dev->irq, NULL);
if (err) {
dev_err(dev,
"Failed to register Embedded Controller subdevice %d\n",
err);
return err;
}
if (ec_dev->max_passthru) {
/*
* Register a PD device as well on top of this device.
* 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.
*/
err = devm_mfd_add_devices(ec_dev->dev, PLATFORM_DEVID_AUTO,
&ec_pd_cell, 1, NULL, ec_dev->irq, NULL);
if (err) {
dev_err(dev,
"Failed to register Power Delivery subdevice %d\n",
err);
return err;
}
}
if (IS_ENABLED(CONFIG_OF) && dev->of_node) {
err = devm_of_platform_populate(dev);
if (err) {
mfd_remove_devices(dev);
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);
#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");

View file

@ -5,73 +5,112 @@
* Copyright (C) 2014 Google, Inc.
*/
#include <linux/fs.h>
#include <linux/mfd/core.h>
#include <linux/mfd/cros_ec.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/pm.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/slab.h>
#include <linux/uaccess.h>
#include "cros_ec_dev.h"
#define DRV_NAME "cros-ec-dev"
/* Device variables */
#define CROS_MAX_DEV 128
static int ec_major;
static struct class cros_class = {
.owner = THIS_MODULE,
.name = "chromeos",
};
/* Basic communication */
static int ec_get_version(struct cros_ec_dev *ec, char *str, int maxlen)
{
struct ec_response_get_version *resp;
static const char * const current_image_name[] = {
"unknown", "read-only", "read-write", "invalid",
};
struct cros_ec_command *msg;
int ret;
/**
* cros_feature_to_name - CrOS feature id to name/short description.
* @id: The feature identifier.
* @name: Device name associated with the feature id.
* @desc: Short name that will be displayed.
*/
struct cros_feature_to_name {
unsigned int id;
const char *name;
const char *desc;
};
msg = kmalloc(sizeof(*msg) + sizeof(*resp), GFP_KERNEL);
if (!msg)
return -ENOMEM;
/**
* cros_feature_to_cells - CrOS feature id to mfd cells association.
* @id: The feature identifier.
* @mfd_cells: Pointer to the array of mfd cells that needs to be added.
* @num_cells: Number of mfd cells into the array.
*/
struct cros_feature_to_cells {
unsigned int id;
const struct mfd_cell *mfd_cells;
unsigned int num_cells;
};
msg->version = 0;
msg->command = EC_CMD_GET_VERSION + ec->cmd_offset;
msg->insize = sizeof(*resp);
msg->outsize = 0;
static const struct cros_feature_to_name cros_mcu_devices[] = {
{
.id = EC_FEATURE_FINGERPRINT,
.name = CROS_EC_DEV_FP_NAME,
.desc = "Fingerprint",
},
{
.id = EC_FEATURE_ISH,
.name = CROS_EC_DEV_ISH_NAME,
.desc = "Integrated Sensor Hub",
},
{
.id = EC_FEATURE_SCP,
.name = CROS_EC_DEV_SCP_NAME,
.desc = "System Control Processor",
},
{
.id = EC_FEATURE_TOUCHPAD,
.name = CROS_EC_DEV_TP_NAME,
.desc = "Touchpad",
},
};
ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
if (ret < 0)
goto exit;
static const struct mfd_cell cros_ec_cec_cells[] = {
{ .name = "cros-ec-cec", },
};
if (msg->result != EC_RES_SUCCESS) {
snprintf(str, maxlen,
"%s\nUnknown EC version: EC returned %d\n",
CROS_EC_DEV_VERSION, msg->result);
ret = -EINVAL;
goto exit;
}
static const struct mfd_cell cros_ec_rtc_cells[] = {
{ .name = "cros-ec-rtc", },
};
resp = (struct ec_response_get_version *)msg->data;
if (resp->current_image >= ARRAY_SIZE(current_image_name))
resp->current_image = 3; /* invalid */
static const struct mfd_cell cros_usbpd_charger_cells[] = {
{ .name = "cros-usbpd-charger", },
{ .name = "cros-usbpd-logger", },
};
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]);
static const struct cros_feature_to_cells cros_subdevices[] = {
{
.id = EC_FEATURE_CEC,
.mfd_cells = cros_ec_cec_cells,
.num_cells = ARRAY_SIZE(cros_ec_cec_cells),
},
{
.id = EC_FEATURE_RTC,
.mfd_cells = cros_ec_rtc_cells,
.num_cells = ARRAY_SIZE(cros_ec_rtc_cells),
},
{
.id = EC_FEATURE_USB_PD,
.mfd_cells = cros_usbpd_charger_cells,
.num_cells = ARRAY_SIZE(cros_usbpd_charger_cells),
},
};
ret = 0;
exit:
kfree(msg);
return ret;
}
static const struct mfd_cell cros_ec_platform_cells[] = {
{ .name = "cros-ec-chardev", },
{ .name = "cros-ec-debugfs", },
{ .name = "cros-ec-lightbar", },
{ .name = "cros-ec-sysfs", },
};
static const struct mfd_cell cros_ec_vbc_cells[] = {
{ .name = "cros-ec-vbc", }
};
static int cros_ec_check_features(struct cros_ec_dev *ec, int feature)
{
@ -80,18 +119,15 @@ static int cros_ec_check_features(struct cros_ec_dev *ec, int feature)
if (ec->features[0] == -1U && ec->features[1] == -1U) {
/* features bitmap not read yet */
msg = kmalloc(sizeof(*msg) + sizeof(ec->features), GFP_KERNEL);
msg = kzalloc(sizeof(*msg) + sizeof(ec->features), GFP_KERNEL);
if (!msg)
return -ENOMEM;
msg->version = 0;
msg->command = EC_CMD_GET_FEATURES + ec->cmd_offset;
msg->insize = sizeof(ec->features);
msg->outsize = 0;
ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
if (ret < 0 || msg->result != EC_RES_SUCCESS) {
ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
if (ret < 0) {
dev_warn(ec->dev, "cannot get EC features: %d/%d\n",
ret, msg->result);
memset(ec->features, 0, sizeof(ec->features));
@ -108,142 +144,6 @@ static int cros_ec_check_features(struct cros_ec_dev *ec, int feature)
return ec->features[feature / 32] & EC_FEATURE_MASK_0(feature);
}
/* Device file ops */
static int ec_device_open(struct inode *inode, struct file *filp)
{
struct cros_ec_dev *ec = container_of(inode->i_cdev,
struct cros_ec_dev, cdev);
filp->private_data = ec;
nonseekable_open(inode, filp);
return 0;
}
static int ec_device_release(struct inode *inode, struct file *filp)
{
return 0;
}
static ssize_t ec_device_read(struct file *filp, char __user *buffer,
size_t length, loff_t *offset)
{
struct cros_ec_dev *ec = filp->private_data;
char msg[sizeof(struct ec_response_get_version) +
sizeof(CROS_EC_DEV_VERSION)];
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 ec_device_ioctl_xcmd(struct cros_ec_dev *ec, void __user *arg)
{
long ret;
struct cros_ec_command u_cmd;
struct cros_ec_command *s_cmd;
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 ec_device_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 ec_device_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 ec_device_ioctl_xcmd(ec, (void __user *)arg);
case CROS_EC_DEV_IOCRDMEM:
return ec_device_ioctl_readmem(ec, (void __user *)arg);
}
return -ENOTTY;
}
/* Module initialization */
static const struct file_operations fops = {
.open = ec_device_open,
.release = ec_device_release,
.read = ec_device_read,
.unlocked_ioctl = ec_device_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = ec_device_ioctl,
#endif
};
static void cros_ec_class_release(struct device *dev)
{
kfree(to_cros_ec_dev(dev));
@ -276,8 +176,8 @@ static void cros_ec_sensors_register(struct cros_ec_dev *ec)
params = (struct ec_params_motion_sense *)msg->data;
params->cmd = MOTIONSENSE_CMD_DUMP;
ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
if (ret < 0 || msg->result != EC_RES_SUCCESS) {
ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
if (ret < 0) {
dev_warn(ec->dev, "cannot get EC sensor information: %d/%d\n",
ret, msg->result);
goto error;
@ -304,8 +204,8 @@ static void cros_ec_sensors_register(struct cros_ec_dev *ec)
for (i = 0; i < sensor_num; i++) {
params->cmd = MOTIONSENSE_CMD_INFO;
params->info.sensor_num = i;
ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
if (ret < 0 || msg->result != EC_RES_SUCCESS) {
ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
if (ret < 0) {
dev_warn(ec->dev, "no info for EC sensor %d : %d/%d\n",
i, ret, msg->result);
continue;
@ -429,37 +329,12 @@ static void cros_ec_accel_legacy_register(struct cros_ec_dev *ec)
* Register 2 accelerometers, we will fail in the IIO driver if there
* are no sensors.
*/
ret = mfd_add_devices(ec->dev, PLATFORM_DEVID_AUTO,
cros_ec_accel_legacy_cells,
ARRAY_SIZE(cros_ec_accel_legacy_cells),
NULL, 0, NULL);
ret = mfd_add_hotplug_devices(ec->dev, cros_ec_accel_legacy_cells,
ARRAY_SIZE(cros_ec_accel_legacy_cells));
if (ret)
dev_err(ec_dev->dev, "failed to add EC sensors\n");
}
static const struct mfd_cell cros_ec_cec_cells[] = {
{ .name = "cros-ec-cec" }
};
static const struct mfd_cell cros_ec_rtc_cells[] = {
{ .name = "cros-ec-rtc" }
};
static const struct mfd_cell cros_usbpd_charger_cells[] = {
{ .name = "cros-usbpd-charger" },
{ .name = "cros-usbpd-logger" },
};
static const struct mfd_cell cros_ec_platform_cells[] = {
{ .name = "cros-ec-debugfs" },
{ .name = "cros-ec-lightbar" },
{ .name = "cros-ec-sysfs" },
};
static const struct mfd_cell cros_ec_vbc_cells[] = {
{ .name = "cros-ec-vbc" }
};
static int ec_device_probe(struct platform_device *pdev)
{
int retval = -ENOMEM;
@ -467,6 +342,7 @@ static int ec_device_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct cros_ec_platform *ec_platform = dev_get_platdata(dev);
struct cros_ec_dev *ec = kzalloc(sizeof(*ec), GFP_KERNEL);
int i;
if (!ec)
return retval;
@ -478,57 +354,27 @@ static int ec_device_probe(struct platform_device *pdev)
ec->features[0] = -1U; /* Not cached yet */
ec->features[1] = -1U; /* Not cached yet */
device_initialize(&ec->class_dev);
cdev_init(&ec->cdev, &fops);
/* Check whether this is actually a Fingerprint MCU rather than an EC */
if (cros_ec_check_features(ec, EC_FEATURE_FINGERPRINT)) {
dev_info(dev, "CrOS Fingerprint MCU detected.\n");
for (i = 0; i < ARRAY_SIZE(cros_mcu_devices); i++) {
/*
* Help userspace differentiating ECs from FP MCU,
* regardless of the probing order.
* Check whether this is actually a dedicated MCU rather
* than an standard EC.
*/
ec_platform->ec_name = CROS_EC_DEV_FP_NAME;
}
/*
* Check whether this is actually an Integrated Sensor Hub (ISH)
* rather than an EC.
*/
if (cros_ec_check_features(ec, EC_FEATURE_ISH)) {
dev_info(dev, "CrOS ISH MCU detected.\n");
/*
* Help userspace differentiating ECs from ISH MCU,
* regardless of the probing order.
*/
ec_platform->ec_name = CROS_EC_DEV_ISH_NAME;
}
/* Check whether this is actually a Touchpad MCU rather than an EC */
if (cros_ec_check_features(ec, EC_FEATURE_TOUCHPAD)) {
dev_info(dev, "CrOS Touchpad MCU detected.\n");
/*
* Help userspace differentiating ECs from TP MCU,
* regardless of the probing order.
*/
ec_platform->ec_name = CROS_EC_DEV_TP_NAME;
}
/* Check whether this is actually a SCP rather than an EC. */
if (cros_ec_check_features(ec, EC_FEATURE_SCP)) {
dev_info(dev, "CrOS SCP MCU detected.\n");
/*
* Help userspace differentiating ECs from SCP,
* regardless of the probing order.
*/
ec_platform->ec_name = CROS_EC_DEV_SCP_NAME;
if (cros_ec_check_features(ec, cros_mcu_devices[i].id)) {
dev_info(dev, "CrOS %s MCU detected\n",
cros_mcu_devices[i].desc);
/*
* Help userspace differentiating ECs from other MCU,
* regardless of the probing order.
*/
ec_platform->ec_name = cros_mcu_devices[i].name;
break;
}
}
/*
* Add the class device
* Link to the character device for creating the /dev entry
* in devtmpfs.
*/
ec->class_dev.devt = MKDEV(ec_major, pdev->id);
ec->class_dev.class = &cros_class;
ec->class_dev.parent = dev;
ec->class_dev.release = cros_ec_class_release;
@ -539,6 +385,10 @@ static int ec_device_probe(struct platform_device *pdev)
goto failed;
}
retval = device_add(&ec->class_dev);
if (retval)
goto failed;
/* check whether this EC is a sensor hub. */
if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE))
cros_ec_sensors_register(ec);
@ -546,53 +396,29 @@ static int ec_device_probe(struct platform_device *pdev)
/* Workaroud for older EC firmware */
cros_ec_accel_legacy_register(ec);
/* Check whether this EC instance has CEC host command support */
if (cros_ec_check_features(ec, EC_FEATURE_CEC)) {
retval = mfd_add_devices(ec->dev, PLATFORM_DEVID_AUTO,
cros_ec_cec_cells,
ARRAY_SIZE(cros_ec_cec_cells),
NULL, 0, NULL);
if (retval)
dev_err(ec->dev,
"failed to add cros-ec-cec device: %d\n",
retval);
/*
* The following subdevices can be detected by sending the
* EC_FEATURE_GET_CMD Embedded Controller device.
*/
for (i = 0; i < ARRAY_SIZE(cros_subdevices); i++) {
if (cros_ec_check_features(ec, cros_subdevices[i].id)) {
retval = mfd_add_hotplug_devices(ec->dev,
cros_subdevices[i].mfd_cells,
cros_subdevices[i].num_cells);
if (retval)
dev_err(ec->dev,
"failed to add %s subdevice: %d\n",
cros_subdevices[i].mfd_cells->name,
retval);
}
}
/* Check whether this EC instance has RTC host command support */
if (cros_ec_check_features(ec, EC_FEATURE_RTC)) {
retval = mfd_add_devices(ec->dev, PLATFORM_DEVID_AUTO,
cros_ec_rtc_cells,
ARRAY_SIZE(cros_ec_rtc_cells),
NULL, 0, NULL);
if (retval)
dev_err(ec->dev,
"failed to add cros-ec-rtc device: %d\n",
retval);
}
/* Check whether this EC instance has the PD charge manager */
if (cros_ec_check_features(ec, EC_FEATURE_USB_PD)) {
retval = mfd_add_devices(ec->dev, PLATFORM_DEVID_AUTO,
cros_usbpd_charger_cells,
ARRAY_SIZE(cros_usbpd_charger_cells),
NULL, 0, NULL);
if (retval)
dev_err(ec->dev,
"failed to add cros-usbpd-charger device: %d\n",
retval);
}
/* We can now add the sysfs class, we know which parameter to show */
retval = cdev_device_add(&ec->cdev, &ec->class_dev);
if (retval) {
dev_err(dev, "cdev_device_add failed => %d\n", retval);
goto failed;
}
retval = mfd_add_devices(ec->dev, PLATFORM_DEVID_AUTO,
cros_ec_platform_cells,
ARRAY_SIZE(cros_ec_platform_cells),
NULL, 0, NULL);
/*
* The following subdevices cannot be detected by sending the
* EC_FEATURE_GET_CMD to the Embedded Controller device.
*/
retval = mfd_add_hotplug_devices(ec->dev, cros_ec_platform_cells,
ARRAY_SIZE(cros_ec_platform_cells));
if (retval)
dev_warn(ec->dev,
"failed to add cros-ec platform devices: %d\n",
@ -601,10 +427,8 @@ static int ec_device_probe(struct platform_device *pdev)
/* Check whether this EC instance has a VBC NVRAM */
node = ec->ec_dev->dev->of_node;
if (of_property_read_bool(node, "google,has-vbc-nvram")) {
retval = mfd_add_devices(ec->dev, PLATFORM_DEVID_AUTO,
cros_ec_vbc_cells,
ARRAY_SIZE(cros_ec_vbc_cells),
NULL, 0, NULL);
retval = mfd_add_hotplug_devices(ec->dev, cros_ec_vbc_cells,
ARRAY_SIZE(cros_ec_vbc_cells));
if (retval)
dev_warn(ec->dev, "failed to add VBC devices: %d\n",
retval);
@ -622,7 +446,6 @@ static int ec_device_remove(struct platform_device *pdev)
struct cros_ec_dev *ec = dev_get_drvdata(&pdev->dev);
mfd_remove_devices(ec->dev);
cdev_del(&ec->cdev);
device_unregister(&ec->class_dev);
return 0;
}
@ -645,7 +468,6 @@ static struct platform_driver cros_ec_dev_driver = {
static int __init cros_ec_dev_init(void)
{
int ret;
dev_t dev = 0;
ret = class_register(&cros_class);
if (ret) {
@ -653,14 +475,6 @@ static int __init cros_ec_dev_init(void)
return ret;
}
/* Get a range of minor numbers (starting with 0) to work with */
ret = alloc_chrdev_region(&dev, 0, CROS_MAX_DEV, CROS_EC_DEV_NAME);
if (ret < 0) {
pr_err(CROS_EC_DEV_NAME ": alloc_chrdev_region() failed\n");
goto failed_chrdevreg;
}
ec_major = MAJOR(dev);
/* Register the driver */
ret = platform_driver_register(&cros_ec_dev_driver);
if (ret < 0) {
@ -670,8 +484,6 @@ static int __init cros_ec_dev_init(void)
return 0;
failed_devreg:
unregister_chrdev_region(MKDEV(ec_major, 0), CROS_MAX_DEV);
failed_chrdevreg:
class_unregister(&cros_class);
return ret;
}
@ -679,7 +491,6 @@ failed_chrdevreg:
static void __exit cros_ec_dev_exit(void)
{
platform_driver_unregister(&cros_ec_dev_driver);
unregister_chrdev(ec_major, CROS_EC_DEV_NAME);
class_unregister(&cros_class);
}

View file

@ -1,35 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* cros_ec_dev - expose the Chrome OS Embedded Controller to userspace
*
* Copyright (C) 2014 Google, Inc.
*/
#ifndef _CROS_EC_DEV_H_
#define _CROS_EC_DEV_H_
#include <linux/ioctl.h>
#include <linux/types.h>
#include <linux/mfd/cros_ec.h>
#define CROS_EC_DEV_VERSION "1.0.0"
/**
* struct cros_ec_readmem - Struct used to read mapped memory.
* @offset: Within EC_LPC_ADDR_MEMMAP region.
* @bytes: Number of bytes to read. Zero means "read a string" (including '\0')
* At most only EC_MEMMAP_SIZE bytes can be read.
* @buffer: Where to store the result. The ioctl returns the number of bytes
* read or negative on error.
*/
struct cros_ec_readmem {
uint32_t offset;
uint32_t bytes;
uint8_t buffer[EC_MEMMAP_SIZE];
};
#define CROS_EC_DEV_IOC 0xEC
#define CROS_EC_DEV_IOCXCMD _IOWR(CROS_EC_DEV_IOC, 0, struct cros_ec_command)
#define CROS_EC_DEV_IOCRDMEM _IOWR(CROS_EC_DEV_IOC, 1, struct cros_ec_readmem)
#endif /* _CROS_EC_DEV_H_ */