Merge tag 'tag-chrome-platform-for-v5.2' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/chrome-platform/linux

Pull chrome platform updates from Benson Leung:
 "CrOS EC:
   - Add EC host command support using rpmsg
   - Add new CrOS USB PD logging driver
   - Transfer spi messages at high priority
   - Add support to trace CrOS EC commands
   - Minor fixes and cleanups in protocol and debugfs

  Wilco EC:
   - Standardize Wilco EC mailbox interface
   - Add h1_gpio status to debugfs"

* tag 'tag-chrome-platform-for-v5.2' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/chrome-platform/linux:
  platform/chrome: cros_ec_proto: Add trace event to trace EC commands
  platform/chrome: cros_ec_debugfs: Use cros_ec_cmd_xfer_status helper
  platform/chrome: cros_ec: Add EC host command support using rpmsg
  platform/chrome: wilco_ec: Add h1_gpio status to debugfs
  platform/chrome: wilco_ec: Standardize mailbox interface
  platform/chrome: cros_ec_proto: check for NULL transfer function
  platform/chrome: Add CrOS USB PD logging driver
  platform/chrome: cros_ec_spi: Transfer messages at high priority
  platform/chrome: cros_ec_debugfs: no need to check return value of debugfs_create functions
  platform/chrome: cros_ec_debugfs: Remove dev_warn when console log is not supported
This commit is contained in:
Linus Torvalds
2019-05-12 07:00:21 -04:00
14 changed files with 984 additions and 183 deletions

View File

@@ -59,6 +59,18 @@ config CROS_EC_I2C
a checksum. Failing accesses will be retried three times to
improve reliability.
config CROS_EC_RPMSG
tristate "ChromeOS Embedded Controller (rpmsg)"
depends on MFD_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
checksum. Also since there's no addition EC-to-host interrupt, this
use a byte in message to distinguish host event from host command.
To compile this driver as a module, choose M here: the
module will be called cros_ec_rpmsg.
config CROS_EC_SPI
tristate "ChromeOS Embedded Controller (SPI)"
depends on MFD_CROS_EC && SPI
@@ -152,6 +164,18 @@ config CROS_EC_SYSFS
To compile this driver as a module, choose M here: the
module will be called cros_ec_sysfs.
config CROS_USBPD_LOGGER
tristate "Logging driver for USB PD charger"
depends on CHARGER_CROS_USBPD
default y
select RTC_LIB
help
This option enables support for logging event data for the USB PD charger
available in the Embedded Controller on ChromeOS systems.
To compile this driver as a module, choose M here: the
module will be called cros_usbpd_logger.
source "drivers/platform/chrome/wilco_ec/Kconfig"
endif # CHROMEOS_PLATFORMS

View File

@@ -1,18 +1,23 @@
# SPDX-License-Identifier: GPL-2.0
# tell define_trace.h where to find the cros ec trace header
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_I2C) += cros_ec_i2c.o
obj-$(CONFIG_CROS_EC_RPMSG) += cros_ec_rpmsg.o
obj-$(CONFIG_CROS_EC_SPI) += cros_ec_spi.o
cros_ec_lpcs-objs := cros_ec_lpc.o cros_ec_lpc_reg.o
cros_ec_lpcs-$(CONFIG_CROS_EC_LPC_MEC) += cros_ec_lpc_mec.o
obj-$(CONFIG_CROS_EC_LPC) += cros_ec_lpcs.o
obj-$(CONFIG_CROS_EC_PROTO) += cros_ec_proto.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_LIGHTBAR) += cros_ec_lightbar.o
obj-$(CONFIG_CROS_EC_VBC) += cros_ec_vbc.o
obj-$(CONFIG_CROS_EC_DEBUGFS) += cros_ec_debugfs.o
obj-$(CONFIG_CROS_EC_SYSFS) += cros_ec_sysfs.o
obj-$(CONFIG_CROS_USBPD_LOGGER) += cros_usbpd_logger.o
obj-$(CONFIG_WILCO_EC) += wilco_ec/

View File

@@ -72,15 +72,9 @@ static void cros_ec_console_log_work(struct work_struct *__work)
int buf_space;
int ret;
ret = cros_ec_cmd_xfer(ec->ec_dev, &snapshot_msg);
if (ret < 0) {
dev_err(ec->dev, "EC communication failed\n");
ret = cros_ec_cmd_xfer_status(ec->ec_dev, &snapshot_msg);
if (ret < 0)
goto resched;
}
if (snapshot_msg.result != EC_RES_SUCCESS) {
dev_err(ec->dev, "EC failed to snapshot the console log\n");
goto resched;
}
/* Loop until we have read everything, or there's an error. */
mutex_lock(&debug_info->log_mutex);
@@ -95,16 +89,10 @@ static void cros_ec_console_log_work(struct work_struct *__work)
memset(read_params, '\0', sizeof(*read_params));
read_params->subcmd = CONSOLE_READ_RECENT;
ret = cros_ec_cmd_xfer(ec->ec_dev, debug_info->read_msg);
if (ret < 0) {
dev_err(ec->dev, "EC communication failed\n");
ret = cros_ec_cmd_xfer_status(ec->ec_dev,
debug_info->read_msg);
if (ret < 0)
break;
}
if (debug_info->read_msg->result != EC_RES_SUCCESS) {
dev_err(ec->dev,
"EC failed to read the console log\n");
break;
}
/* If the buffer is empty, we're done here. */
if (ret == 0 || ec_buffer[0] == '\0')
@@ -290,9 +278,8 @@ static int ec_read_version_supported(struct cros_ec_dev *ec)
params->cmd = EC_CMD_CONSOLE_READ;
response = (struct ec_response_get_cmd_versions *)msg->data;
ret = cros_ec_cmd_xfer(ec->ec_dev, msg) >= 0 &&
msg->result == EC_RES_SUCCESS &&
(response->version_mask & EC_VER_MASK(1));
ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg) >= 0 &&
response->version_mask & EC_VER_MASK(1);
kfree(msg);
@@ -306,11 +293,12 @@ static int cros_ec_create_console_log(struct cros_ec_debugfs *debug_info)
int read_params_size;
int read_response_size;
if (!ec_read_version_supported(ec)) {
dev_warn(ec->dev,
"device does not support reading the console log\n");
/*
* If the console log feature is not supported return silently and
* don't create the console_log entry.
*/
if (!ec_read_version_supported(ec))
return 0;
}
buf = devm_kzalloc(ec->dev, LOG_SIZE, GFP_KERNEL);
if (!buf)
@@ -336,12 +324,8 @@ static int cros_ec_create_console_log(struct cros_ec_debugfs *debug_info)
mutex_init(&debug_info->log_mutex);
init_waitqueue_head(&debug_info->log_wq);
if (!debugfs_create_file("console_log",
S_IFREG | 0444,
debug_info->dir,
debug_info,
&cros_ec_console_log_fops))
return -ENOMEM;
debugfs_create_file("console_log", S_IFREG | 0444, debug_info->dir,
debug_info, &cros_ec_console_log_fops);
INIT_DELAYED_WORK(&debug_info->log_poll_work,
cros_ec_console_log_work);
@@ -375,9 +359,8 @@ static int cros_ec_create_panicinfo(struct cros_ec_debugfs *debug_info)
msg->command = EC_CMD_GET_PANIC_INFO;
msg->insize = insize;
ret = cros_ec_cmd_xfer(ec_dev, msg);
ret = cros_ec_cmd_xfer_status(ec_dev, msg);
if (ret < 0) {
dev_warn(debug_info->ec->dev, "Cannot read panicinfo.\n");
ret = 0;
goto free;
}
@@ -389,13 +372,8 @@ static int cros_ec_create_panicinfo(struct cros_ec_debugfs *debug_info)
debug_info->panicinfo_blob.data = msg->data;
debug_info->panicinfo_blob.size = ret;
if (!debugfs_create_blob("panicinfo",
S_IFREG | 0444,
debug_info->dir,
&debug_info->panicinfo_blob)) {
ret = -ENOMEM;
goto free;
}
debugfs_create_blob("panicinfo", S_IFREG | 0444, debug_info->dir,
&debug_info->panicinfo_blob);
return 0;
@@ -404,15 +382,6 @@ free:
return ret;
}
static int cros_ec_create_pdinfo(struct cros_ec_debugfs *debug_info)
{
if (!debugfs_create_file("pdinfo", 0444, debug_info->dir, debug_info,
&cros_ec_pdinfo_fops))
return -ENOMEM;
return 0;
}
static int cros_ec_debugfs_probe(struct platform_device *pd)
{
struct cros_ec_dev *ec = dev_get_drvdata(pd->dev.parent);
@@ -427,8 +396,6 @@ static int cros_ec_debugfs_probe(struct platform_device *pd)
debug_info->ec = ec;
debug_info->dir = debugfs_create_dir(name, NULL);
if (!debug_info->dir)
return -ENOMEM;
ret = cros_ec_create_panicinfo(debug_info);
if (ret)
@@ -438,9 +405,8 @@ static int cros_ec_debugfs_probe(struct platform_device *pd)
if (ret)
goto remove_debugfs;
ret = cros_ec_create_pdinfo(debug_info);
if (ret)
goto remove_log;
debugfs_create_file("pdinfo", 0444, debug_info->dir, debug_info,
&cros_ec_pdinfo_fops);
ec->debug_info = debug_info;
@@ -448,8 +414,6 @@ static int cros_ec_debugfs_probe(struct platform_device *pd)
return 0;
remove_log:
cros_ec_cleanup_console_log(debug_info);
remove_debugfs:
debugfs_remove_recursive(debug_info->dir);
return ret;

View File

@@ -10,6 +10,8 @@
#include <linux/slab.h>
#include <asm/unaligned.h>
#include "cros_ec_trace.h"
#define EC_COMMAND_RETRIES 50
static int prepare_packet(struct cros_ec_device *ec_dev,
@@ -51,11 +53,24 @@ static int send_command(struct cros_ec_device *ec_dev,
int ret;
int (*xfer_fxn)(struct cros_ec_device *ec, struct cros_ec_command *msg);
trace_cros_ec_cmd(msg);
if (ec_dev->proto_version > 2)
xfer_fxn = ec_dev->pkt_xfer;
else
xfer_fxn = ec_dev->cmd_xfer;
if (!xfer_fxn) {
/*
* This error can happen if a communication error happened and
* the EC is trying to use protocol v2, on an underlying
* communication mechanism that does not support v2.
*/
dev_err_once(ec_dev->dev,
"missing EC transfer API, cannot send command\n");
return -EIO;
}
ret = (*xfer_fxn)(ec_dev, msg);
if (msg->result == EC_RES_IN_PROGRESS) {
int i;

View File

@@ -0,0 +1,258 @@
// SPDX-License-Identifier: GPL-2.0
//
// Copyright 2018 Google LLC.
#include <linux/completion.h>
#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_device.h>
#include <linux/rpmsg.h>
#include <linux/slab.h>
#define EC_MSG_TIMEOUT_MS 200
#define HOST_COMMAND_MARK 1
#define HOST_EVENT_MARK 2
/**
* struct cros_ec_rpmsg_response - rpmsg message format from from EC.
*
* @type: The type of message, should be either HOST_COMMAND_MARK or
* HOST_EVENT_MARK, representing that the message is a response to
* host command, or a host event.
* @data: ec_host_response for host command.
*/
struct cros_ec_rpmsg_response {
u8 type;
u8 data[] __aligned(4);
};
/**
* struct cros_ec_rpmsg - information about a EC over rpmsg.
*
* @rpdev: rpmsg device we are connected to
* @xfer_ack: completion for host command transfer.
* @host_event_work: Work struct for pending host event.
*/
struct cros_ec_rpmsg {
struct rpmsg_device *rpdev;
struct completion xfer_ack;
struct work_struct host_event_work;
};
/**
* cros_ec_cmd_xfer_rpmsg - Transfer a message over rpmsg and receive the reply
*
* @ec_dev: ChromeOS EC device
* @ec_msg: Message to transfer
*
* This is only used for old EC proto version, and is not supported for this
* driver.
*
* Return: -EINVAL
*/
static int cros_ec_cmd_xfer_rpmsg(struct cros_ec_device *ec_dev,
struct cros_ec_command *ec_msg)
{
return -EINVAL;
}
/**
* cros_ec_pkt_xfer_rpmsg - Transfer a packet over rpmsg and receive the reply
*
* @ec_dev: ChromeOS EC device
* @ec_msg: Message to transfer
*
* Return: number of bytes of the reply on success or negative error code.
*/
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;
int ret;
u8 sum;
int i;
ec_msg->result = 0;
len = cros_ec_prepare_tx(ec_dev, ec_msg);
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);
if (ret) {
dev_err(ec_dev->dev, "rpmsg send failed\n");
return ret;
}
timeout = msecs_to_jiffies(EC_MSG_TIMEOUT_MS);
ret = wait_for_completion_timeout(&ec_rpmsg->xfer_ack, timeout);
if (!ret) {
dev_err(ec_dev->dev, "rpmsg send timeout\n");
return -EIO;
}
/* check response error code */
response = (struct ec_host_response *)ec_dev->din;
ec_msg->result = response->result;
ret = cros_ec_check_result(ec_dev, ec_msg);
if (ret)
goto exit;
if (response->data_len > ec_msg->insize) {
dev_err(ec_dev->dev, "packet too long (%d bytes, expected %d)",
response->data_len, ec_msg->insize);
ret = -EMSGSIZE;
goto exit;
}
/* copy response packet payload and compute checksum */
memcpy(ec_msg->data, ec_dev->din + sizeof(*response),
response->data_len);
sum = 0;
for (i = 0; i < sizeof(*response) + response->data_len; i++)
sum += ec_dev->din[i];
if (sum) {
dev_err(ec_dev->dev, "bad packet checksum, calculated %x\n",
sum);
ret = -EBADMSG;
goto exit;
}
ret = response->data_len;
exit:
if (ec_msg->command == EC_CMD_REBOOT_EC)
msleep(EC_REBOOT_DELAY_MS);
return ret;
}
static void
cros_ec_rpmsg_host_event_function(struct work_struct *host_event_work)
{
struct cros_ec_rpmsg *ec_rpmsg = container_of(host_event_work,
struct cros_ec_rpmsg,
host_event_work);
struct cros_ec_device *ec_dev = dev_get_drvdata(&ec_rpmsg->rpdev->dev);
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);
}
static int cros_ec_rpmsg_callback(struct rpmsg_device *rpdev, void *data,
int len, void *priv, u32 src)
{
struct cros_ec_device *ec_dev = dev_get_drvdata(&rpdev->dev);
struct cros_ec_rpmsg *ec_rpmsg = ec_dev->priv;
struct cros_ec_rpmsg_response *resp;
if (!len) {
dev_warn(ec_dev->dev, "rpmsg received empty response");
return -EINVAL;
}
resp = data;
len -= offsetof(struct cros_ec_rpmsg_response, data);
if (resp->type == HOST_COMMAND_MARK) {
if (len > ec_dev->din_size) {
dev_warn(ec_dev->dev,
"received length %d > din_size %d, truncating",
len, ec_dev->din_size);
len = ec_dev->din_size;
}
memcpy(ec_dev->din, resp->data, len);
complete(&ec_rpmsg->xfer_ack);
} else if (resp->type == HOST_EVENT_MARK) {
schedule_work(&ec_rpmsg->host_event_work);
} else {
dev_warn(ec_dev->dev, "rpmsg received invalid type = %d",
resp->type);
return -EINVAL;
}
return 0;
}
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;
ec_dev = devm_kzalloc(dev, sizeof(*ec_dev), GFP_KERNEL);
if (!ec_dev)
return -ENOMEM;
ec_rpmsg = devm_kzalloc(dev, sizeof(*ec_rpmsg), GFP_KERNEL);
if (!ec_rpmsg)
return -ENOMEM;
ec_dev->dev = dev;
ec_dev->priv = ec_rpmsg;
ec_dev->cmd_xfer = cros_ec_cmd_xfer_rpmsg;
ec_dev->pkt_xfer = cros_ec_pkt_xfer_rpmsg;
ec_dev->phys_name = dev_name(&rpdev->dev);
ec_dev->din_size = sizeof(struct ec_host_response) +
sizeof(struct ec_response_get_protocol_info);
ec_dev->dout_size = sizeof(struct ec_host_request);
dev_set_drvdata(dev, ec_dev);
ec_rpmsg->rpdev = rpdev;
init_completion(&ec_rpmsg->xfer_ack);
INIT_WORK(&ec_rpmsg->host_event_work,
cros_ec_rpmsg_host_event_function);
return cros_ec_register(ec_dev);
}
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;
cancel_work_sync(&ec_rpmsg->host_event_work);
}
static const struct of_device_id cros_ec_rpmsg_of_match[] = {
{ .compatible = "google,cros-ec-rpmsg", },
{ }
};
MODULE_DEVICE_TABLE(of, cros_ec_rpmsg_of_match);
static struct rpmsg_driver cros_ec_driver_rpmsg = {
.drv = {
.name = "cros-ec-rpmsg",
.of_match_table = cros_ec_rpmsg_of_match,
},
.probe = cros_ec_rpmsg_probe,
.remove = cros_ec_rpmsg_remove,
.callback = cros_ec_rpmsg_callback,
};
module_rpmsg_driver(cros_ec_driver_rpmsg);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("ChromeOS EC multi function device (rpmsg)");

View File

@@ -75,6 +75,27 @@ struct cros_ec_spi {
unsigned int end_of_msg_delay;
};
typedef int (*cros_ec_xfer_fn_t) (struct cros_ec_device *ec_dev,
struct cros_ec_command *ec_msg);
/**
* struct cros_ec_xfer_work_params - params for our high priority workers
*
* @work: The work_struct needed to queue work
* @fn: The function to use to transfer
* @ec_dev: ChromeOS EC device
* @ec_msg: Message to transfer
* @ret: The return value of the function
*/
struct cros_ec_xfer_work_params {
struct work_struct work;
cros_ec_xfer_fn_t fn;
struct cros_ec_device *ec_dev;
struct cros_ec_command *ec_msg;
int ret;
};
static void debug_packet(struct device *dev, const char *name, u8 *ptr,
int len)
{
@@ -350,13 +371,13 @@ static int cros_ec_spi_receive_response(struct cros_ec_device *ec_dev,
}
/**
* cros_ec_pkt_xfer_spi - Transfer a packet over SPI and receive the reply
* do_cros_ec_pkt_xfer_spi - Transfer a packet over SPI and receive the reply
*
* @ec_dev: ChromeOS EC device
* @ec_msg: Message to transfer
*/
static int cros_ec_pkt_xfer_spi(struct cros_ec_device *ec_dev,
struct cros_ec_command *ec_msg)
static int do_cros_ec_pkt_xfer_spi(struct cros_ec_device *ec_dev,
struct cros_ec_command *ec_msg)
{
struct ec_host_response *response;
struct cros_ec_spi *ec_spi = ec_dev->priv;
@@ -493,13 +514,13 @@ exit:
}
/**
* cros_ec_cmd_xfer_spi - Transfer a message over SPI and receive the reply
* do_cros_ec_cmd_xfer_spi - Transfer a message over SPI and receive the reply
*
* @ec_dev: ChromeOS EC device
* @ec_msg: Message to transfer
*/
static int cros_ec_cmd_xfer_spi(struct cros_ec_device *ec_dev,
struct cros_ec_command *ec_msg)
static int do_cros_ec_cmd_xfer_spi(struct cros_ec_device *ec_dev,
struct cros_ec_command *ec_msg)
{
struct cros_ec_spi *ec_spi = ec_dev->priv;
struct spi_transfer trans;
@@ -611,6 +632,53 @@ exit:
return ret;
}
static void cros_ec_xfer_high_pri_work(struct work_struct *work)
{
struct cros_ec_xfer_work_params *params;
params = container_of(work, struct cros_ec_xfer_work_params, work);
params->ret = params->fn(params->ec_dev, params->ec_msg);
}
static int cros_ec_xfer_high_pri(struct cros_ec_device *ec_dev,
struct cros_ec_command *ec_msg,
cros_ec_xfer_fn_t fn)
{
struct cros_ec_xfer_work_params params;
INIT_WORK_ONSTACK(&params.work, cros_ec_xfer_high_pri_work);
params.ec_dev = ec_dev;
params.ec_msg = ec_msg;
params.fn = fn;
/*
* This looks a bit ridiculous. Why do the work on a
* different thread if we're just going to block waiting for
* the thread to finish? The key here is that the thread is
* running at high priority but the calling context might not
* be. We need to be at high priority to avoid getting
* context switched out for too long and the EC giving up on
* the transfer.
*/
queue_work(system_highpri_wq, &params.work);
flush_work(&params.work);
destroy_work_on_stack(&params.work);
return params.ret;
}
static int cros_ec_pkt_xfer_spi(struct cros_ec_device *ec_dev,
struct cros_ec_command *ec_msg)
{
return cros_ec_xfer_high_pri(ec_dev, ec_msg, do_cros_ec_pkt_xfer_spi);
}
static int cros_ec_cmd_xfer_spi(struct cros_ec_device *ec_dev,
struct cros_ec_command *ec_msg)
{
return cros_ec_xfer_high_pri(ec_dev, ec_msg, do_cros_ec_cmd_xfer_spi);
}
static void cros_ec_spi_dt_probe(struct cros_ec_spi *ec_spi, struct device *dev)
{
struct device_node *np = dev->of_node;

View File

@@ -0,0 +1,124 @@
// SPDX-License-Identifier: GPL-2.0
// Trace events for the ChromeOS Embedded Controller
//
// Copyright 2019 Google LLC.
#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
#define EC_CMDS \
TRACE_SYMBOL(EC_CMD_PROTO_VERSION), \
TRACE_SYMBOL(EC_CMD_HELLO), \
TRACE_SYMBOL(EC_CMD_GET_VERSION), \
TRACE_SYMBOL(EC_CMD_READ_TEST), \
TRACE_SYMBOL(EC_CMD_GET_BUILD_INFO), \
TRACE_SYMBOL(EC_CMD_GET_CHIP_INFO), \
TRACE_SYMBOL(EC_CMD_GET_BOARD_VERSION), \
TRACE_SYMBOL(EC_CMD_READ_MEMMAP), \
TRACE_SYMBOL(EC_CMD_GET_CMD_VERSIONS), \
TRACE_SYMBOL(EC_CMD_GET_COMMS_STATUS), \
TRACE_SYMBOL(EC_CMD_TEST_PROTOCOL), \
TRACE_SYMBOL(EC_CMD_GET_PROTOCOL_INFO), \
TRACE_SYMBOL(EC_CMD_GSV_PAUSE_IN_S5), \
TRACE_SYMBOL(EC_CMD_GET_FEATURES), \
TRACE_SYMBOL(EC_CMD_FLASH_INFO), \
TRACE_SYMBOL(EC_CMD_FLASH_READ), \
TRACE_SYMBOL(EC_CMD_FLASH_WRITE), \
TRACE_SYMBOL(EC_CMD_FLASH_ERASE), \
TRACE_SYMBOL(EC_CMD_FLASH_PROTECT), \
TRACE_SYMBOL(EC_CMD_FLASH_REGION_INFO), \
TRACE_SYMBOL(EC_CMD_VBNV_CONTEXT), \
TRACE_SYMBOL(EC_CMD_PWM_GET_FAN_TARGET_RPM), \
TRACE_SYMBOL(EC_CMD_PWM_SET_FAN_TARGET_RPM), \
TRACE_SYMBOL(EC_CMD_PWM_GET_KEYBOARD_BACKLIGHT), \
TRACE_SYMBOL(EC_CMD_PWM_SET_KEYBOARD_BACKLIGHT), \
TRACE_SYMBOL(EC_CMD_PWM_SET_FAN_DUTY), \
TRACE_SYMBOL(EC_CMD_PWM_SET_DUTY), \
TRACE_SYMBOL(EC_CMD_PWM_GET_DUTY), \
TRACE_SYMBOL(EC_CMD_LIGHTBAR_CMD), \
TRACE_SYMBOL(EC_CMD_LED_CONTROL), \
TRACE_SYMBOL(EC_CMD_VBOOT_HASH), \
TRACE_SYMBOL(EC_CMD_MOTION_SENSE_CMD), \
TRACE_SYMBOL(EC_CMD_USB_CHARGE_SET_MODE), \
TRACE_SYMBOL(EC_CMD_PSTORE_INFO), \
TRACE_SYMBOL(EC_CMD_PSTORE_READ), \
TRACE_SYMBOL(EC_CMD_PSTORE_WRITE), \
TRACE_SYMBOL(EC_CMD_RTC_GET_VALUE), \
TRACE_SYMBOL(EC_CMD_RTC_GET_ALARM), \
TRACE_SYMBOL(EC_CMD_RTC_SET_VALUE), \
TRACE_SYMBOL(EC_CMD_RTC_SET_ALARM), \
TRACE_SYMBOL(EC_CMD_PORT80_LAST_BOOT), \
TRACE_SYMBOL(EC_CMD_PORT80_READ), \
TRACE_SYMBOL(EC_CMD_THERMAL_SET_THRESHOLD), \
TRACE_SYMBOL(EC_CMD_THERMAL_GET_THRESHOLD), \
TRACE_SYMBOL(EC_CMD_THERMAL_AUTO_FAN_CTRL), \
TRACE_SYMBOL(EC_CMD_TMP006_GET_CALIBRATION), \
TRACE_SYMBOL(EC_CMD_TMP006_SET_CALIBRATION), \
TRACE_SYMBOL(EC_CMD_TMP006_GET_RAW), \
TRACE_SYMBOL(EC_CMD_MKBP_STATE), \
TRACE_SYMBOL(EC_CMD_MKBP_INFO), \
TRACE_SYMBOL(EC_CMD_MKBP_SIMULATE_KEY), \
TRACE_SYMBOL(EC_CMD_MKBP_SET_CONFIG), \
TRACE_SYMBOL(EC_CMD_MKBP_GET_CONFIG), \
TRACE_SYMBOL(EC_CMD_KEYSCAN_SEQ_CTRL), \
TRACE_SYMBOL(EC_CMD_GET_NEXT_EVENT), \
TRACE_SYMBOL(EC_CMD_TEMP_SENSOR_GET_INFO), \
TRACE_SYMBOL(EC_CMD_HOST_EVENT_GET_B), \
TRACE_SYMBOL(EC_CMD_HOST_EVENT_GET_SMI_MASK), \
TRACE_SYMBOL(EC_CMD_HOST_EVENT_GET_SCI_MASK), \
TRACE_SYMBOL(EC_CMD_HOST_EVENT_GET_WAKE_MASK), \
TRACE_SYMBOL(EC_CMD_HOST_EVENT_SET_SMI_MASK), \
TRACE_SYMBOL(EC_CMD_HOST_EVENT_SET_SCI_MASK), \
TRACE_SYMBOL(EC_CMD_HOST_EVENT_CLEAR), \
TRACE_SYMBOL(EC_CMD_HOST_EVENT_SET_WAKE_MASK), \
TRACE_SYMBOL(EC_CMD_HOST_EVENT_CLEAR_B), \
TRACE_SYMBOL(EC_CMD_SWITCH_ENABLE_BKLIGHT), \
TRACE_SYMBOL(EC_CMD_SWITCH_ENABLE_WIRELESS), \
TRACE_SYMBOL(EC_CMD_GPIO_SET), \
TRACE_SYMBOL(EC_CMD_GPIO_GET), \
TRACE_SYMBOL(EC_CMD_I2C_READ), \
TRACE_SYMBOL(EC_CMD_I2C_WRITE), \
TRACE_SYMBOL(EC_CMD_CHARGE_CONTROL), \
TRACE_SYMBOL(EC_CMD_CONSOLE_SNAPSHOT), \
TRACE_SYMBOL(EC_CMD_CONSOLE_READ), \
TRACE_SYMBOL(EC_CMD_BATTERY_CUT_OFF), \
TRACE_SYMBOL(EC_CMD_USB_MUX), \
TRACE_SYMBOL(EC_CMD_LDO_SET), \
TRACE_SYMBOL(EC_CMD_LDO_GET), \
TRACE_SYMBOL(EC_CMD_POWER_INFO), \
TRACE_SYMBOL(EC_CMD_I2C_PASSTHRU), \
TRACE_SYMBOL(EC_CMD_HANG_DETECT), \
TRACE_SYMBOL(EC_CMD_CHARGE_STATE), \
TRACE_SYMBOL(EC_CMD_CHARGE_CURRENT_LIMIT), \
TRACE_SYMBOL(EC_CMD_EXTERNAL_POWER_LIMIT), \
TRACE_SYMBOL(EC_CMD_HOST_SLEEP_EVENT), \
TRACE_SYMBOL(EC_CMD_SB_READ_WORD), \
TRACE_SYMBOL(EC_CMD_SB_WRITE_WORD), \
TRACE_SYMBOL(EC_CMD_SB_READ_BLOCK), \
TRACE_SYMBOL(EC_CMD_SB_WRITE_BLOCK), \
TRACE_SYMBOL(EC_CMD_BATTERY_VENDOR_PARAM), \
TRACE_SYMBOL(EC_CMD_CODEC_I2S), \
TRACE_SYMBOL(EC_CMD_REBOOT_EC), \
TRACE_SYMBOL(EC_CMD_GET_PANIC_INFO), \
TRACE_SYMBOL(EC_CMD_ACPI_READ), \
TRACE_SYMBOL(EC_CMD_ACPI_WRITE), \
TRACE_SYMBOL(EC_CMD_ACPI_QUERY_EVENT), \
TRACE_SYMBOL(EC_CMD_CEC_WRITE_MSG), \
TRACE_SYMBOL(EC_CMD_CEC_SET), \
TRACE_SYMBOL(EC_CMD_CEC_GET), \
TRACE_SYMBOL(EC_CMD_REBOOT), \
TRACE_SYMBOL(EC_CMD_RESEND_RESPONSE), \
TRACE_SYMBOL(EC_CMD_VERSION0), \
TRACE_SYMBOL(EC_CMD_PD_EXCHANGE_STATUS), \
TRACE_SYMBOL(EC_CMD_USB_PD_CONTROL), \
TRACE_SYMBOL(EC_CMD_USB_PD_PORTS), \
TRACE_SYMBOL(EC_CMD_USB_PD_POWER_INFO), \
TRACE_SYMBOL(EC_CMD_CHARGE_PORT_COUNT), \
TRACE_SYMBOL(EC_CMD_USB_PD_DISCOVERY), \
TRACE_SYMBOL(EC_CMD_PD_CHARGE_PORT_OVERRIDE), \
TRACE_SYMBOL(EC_CMD_PD_GET_LOG_ENTRY), \
TRACE_SYMBOL(EC_CMD_USB_PD_MUX_INFO)
#define CREATE_TRACE_POINTS
#include "cros_ec_trace.h"

View File

@@ -0,0 +1,51 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Trace events for the ChromeOS Embedded Controller
*
* Copyright 2019 Google LLC.
*/
#undef TRACE_SYSTEM
#define TRACE_SYSTEM cros_ec
#if !defined(_CROS_EC_TRACE_H_) || defined(TRACE_HEADER_MULTI_READ)
#define _CROS_EC_TRACE_H_
#include <linux/types.h>
#include <linux/mfd/cros_ec.h>
#include <linux/tracepoint.h>
DECLARE_EVENT_CLASS(cros_ec_cmd_class,
TP_PROTO(struct cros_ec_command *cmd),
TP_ARGS(cmd),
TP_STRUCT__entry(
__field(uint32_t, version)
__field(uint32_t, command)
),
TP_fast_assign(
__entry->version = cmd->version;
__entry->command = cmd->command;
),
TP_printk("version: %u, command: %s", __entry->version,
__print_symbolic(__entry->command, EC_CMDS))
);
DEFINE_EVENT(cros_ec_cmd_class, cros_ec_cmd,
TP_PROTO(struct cros_ec_command *cmd),
TP_ARGS(cmd)
);
#endif /* _CROS_EC_TRACE_H_ */
/* this part must be outside header guard */
#undef TRACE_INCLUDE_PATH
#define TRACE_INCLUDE_PATH .
#undef TRACE_INCLUDE_FILE
#define TRACE_INCLUDE_FILE cros_ec_trace
#include <trace/define_trace.h>

View File

@@ -0,0 +1,262 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Logging driver for ChromeOS EC based USBPD Charger.
*
* Copyright 2018 Google LLC.
*/
#include <linux/ktime.h>
#include <linux/math64.h>
#include <linux/mfd/cros_ec.h>
#include <linux/mfd/cros_ec_commands.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/rtc.h>
#define DRV_NAME "cros-usbpd-logger"
#define CROS_USBPD_MAX_LOG_ENTRIES 30
#define CROS_USBPD_LOG_UPDATE_DELAY msecs_to_jiffies(60000)
#define CROS_USBPD_DATA_SIZE 16
#define CROS_USBPD_LOG_RESP_SIZE (sizeof(struct ec_response_pd_log) + \
CROS_USBPD_DATA_SIZE)
#define CROS_USBPD_BUFFER_SIZE (sizeof(struct cros_ec_command) + \
CROS_USBPD_LOG_RESP_SIZE)
/* Buffer for building the PDLOG string */
#define BUF_SIZE 80
struct logger_data {
struct device *dev;
struct cros_ec_dev *ec_dev;
u8 ec_buffer[CROS_USBPD_BUFFER_SIZE];
struct delayed_work log_work;
struct workqueue_struct *log_workqueue;
};
static const char * const chg_type_names[] = {
"None", "PD", "Type-C", "Proprietary", "DCP", "CDP", "SDP",
"Other", "VBUS"
};
static const char * const role_names[] = {
"Disconnected", "SRC", "SNK", "SNK (not charging)"
};
static const char * const fault_names[] = {
"---", "OCP", "fast OCP", "OVP", "Discharge"
};
static int append_str(char *buf, int pos, const char *fmt, ...)
{
va_list args;
int i;
va_start(args, fmt);
i = vsnprintf(buf + pos, BUF_SIZE - pos, fmt, args);
va_end(args);
return i;
}
static struct ec_response_pd_log *ec_get_log_entry(struct logger_data *logger)
{
struct cros_ec_dev *ec_dev = logger->ec_dev;
struct cros_ec_command *msg;
int ret;
msg = (struct cros_ec_command *)logger->ec_buffer;
msg->command = ec_dev->cmd_offset + EC_CMD_PD_GET_LOG_ENTRY;
msg->insize = CROS_USBPD_LOG_RESP_SIZE;
ret = cros_ec_cmd_xfer_status(ec_dev->ec_dev, msg);
if (ret < 0)
return ERR_PTR(ret);
return (struct ec_response_pd_log *)msg->data;
}
static void cros_usbpd_print_log_entry(struct ec_response_pd_log *r,
ktime_t tstamp)
{
const char *fault, *role, *chg_type;
struct usb_chg_measures *meas;
struct mcdp_info *minfo;
int role_idx, type_idx;
char buf[BUF_SIZE + 1];
struct rtc_time rt;
int len = 0;
s32 rem;
int i;
/* The timestamp is the number of 1024th of seconds in the past */
tstamp = ktime_sub_us(tstamp, r->timestamp << PD_LOG_TIMESTAMP_SHIFT);
rt = rtc_ktime_to_tm(tstamp);
switch (r->type) {
case PD_EVENT_MCU_CHARGE:
if (r->data & CHARGE_FLAGS_OVERRIDE)
len += append_str(buf, len, "override ");
if (r->data & CHARGE_FLAGS_DELAYED_OVERRIDE)
len += append_str(buf, len, "pending_override ");
role_idx = r->data & CHARGE_FLAGS_ROLE_MASK;
role = role_idx < ARRAY_SIZE(role_names) ?
role_names[role_idx] : "Unknown";
type_idx = (r->data & CHARGE_FLAGS_TYPE_MASK)
>> CHARGE_FLAGS_TYPE_SHIFT;
chg_type = type_idx < ARRAY_SIZE(chg_type_names) ?
chg_type_names[type_idx] : "???";
if (role_idx == USB_PD_PORT_POWER_DISCONNECTED ||
role_idx == USB_PD_PORT_POWER_SOURCE) {
len += append_str(buf, len, "%s", role);
break;
}
meas = (struct usb_chg_measures *)r->payload;
len += append_str(buf, len, "%s %s %s %dmV max %dmV / %dmA",
role, r->data & CHARGE_FLAGS_DUAL_ROLE ?
"DRP" : "Charger",
chg_type, meas->voltage_now,
meas->voltage_max, meas->current_max);
break;
case PD_EVENT_ACC_RW_FAIL:
len += append_str(buf, len, "RW signature check failed");
break;
case PD_EVENT_PS_FAULT:
fault = r->data < ARRAY_SIZE(fault_names) ? fault_names[r->data]
: "???";
len += append_str(buf, len, "Power supply fault: %s", fault);
break;
case PD_EVENT_VIDEO_DP_MODE:
len += append_str(buf, len, "DP mode %sabled", r->data == 1 ?
"en" : "dis");
break;
case PD_EVENT_VIDEO_CODEC:
minfo = (struct mcdp_info *)r->payload;
len += append_str(buf, len, "HDMI info: family:%04x chipid:%04x ",
MCDP_FAMILY(minfo->family),
MCDP_CHIPID(minfo->chipid));
len += append_str(buf, len, "irom:%d.%d.%d fw:%d.%d.%d",
minfo->irom.major, minfo->irom.minor,
minfo->irom.build, minfo->fw.major,
minfo->fw.minor, minfo->fw.build);
break;
default:
len += append_str(buf, len, "Event %02x (%04x) [", r->type,
r->data);
for (i = 0; i < PD_LOG_SIZE(r->size_port); i++)
len += append_str(buf, len, "%02x ", r->payload[i]);
len += append_str(buf, len, "]");
break;
}
div_s64_rem(ktime_to_ms(tstamp), MSEC_PER_SEC, &rem);
pr_info("PDLOG %d/%02d/%02d %02d:%02d:%02d.%03d P%d %s\n",
rt.tm_year + 1900, rt.tm_mon + 1, rt.tm_mday,
rt.tm_hour, rt.tm_min, rt.tm_sec, rem,
PD_LOG_PORT(r->size_port), buf);
}
static void cros_usbpd_log_check(struct work_struct *work)
{
struct logger_data *logger = container_of(to_delayed_work(work),
struct logger_data,
log_work);
struct device *dev = logger->dev;
struct ec_response_pd_log *r;
int entries = 0;
ktime_t now;
while (entries++ < CROS_USBPD_MAX_LOG_ENTRIES) {
r = ec_get_log_entry(logger);
now = ktime_get_real();
if (IS_ERR(r)) {
dev_dbg(dev, "Cannot get PD log %ld\n", PTR_ERR(r));
break;
}
if (r->type == PD_EVENT_NO_ENTRY)
break;
cros_usbpd_print_log_entry(r, now);
}
queue_delayed_work(logger->log_workqueue, &logger->log_work,
CROS_USBPD_LOG_UPDATE_DELAY);
}
static int cros_usbpd_logger_probe(struct platform_device *pd)
{
struct cros_ec_dev *ec_dev = dev_get_drvdata(pd->dev.parent);
struct device *dev = &pd->dev;
struct logger_data *logger;
logger = devm_kzalloc(dev, sizeof(*logger), GFP_KERNEL);
if (!logger)
return -ENOMEM;
logger->dev = dev;
logger->ec_dev = ec_dev;
platform_set_drvdata(pd, logger);
/* Retrieve PD event logs periodically */
INIT_DELAYED_WORK(&logger->log_work, cros_usbpd_log_check);
logger->log_workqueue = create_singlethread_workqueue("cros_usbpd_log");
queue_delayed_work(logger->log_workqueue, &logger->log_work,
CROS_USBPD_LOG_UPDATE_DELAY);
return 0;
}
static int cros_usbpd_logger_remove(struct platform_device *pd)
{
struct logger_data *logger = platform_get_drvdata(pd);
cancel_delayed_work_sync(&logger->log_work);
return 0;
}
static int __maybe_unused cros_usbpd_logger_resume(struct device *dev)
{
struct logger_data *logger = dev_get_drvdata(dev);
queue_delayed_work(logger->log_workqueue, &logger->log_work,
CROS_USBPD_LOG_UPDATE_DELAY);
return 0;
}
static int __maybe_unused cros_usbpd_logger_suspend(struct device *dev)
{
struct logger_data *logger = dev_get_drvdata(dev);
cancel_delayed_work_sync(&logger->log_work);
return 0;
}
static SIMPLE_DEV_PM_OPS(cros_usbpd_logger_pm_ops, cros_usbpd_logger_suspend,
cros_usbpd_logger_resume);
static struct platform_driver cros_usbpd_logger_driver = {
.driver = {
.name = DRV_NAME,
.pm = &cros_usbpd_logger_pm_ops,
},
.probe = cros_usbpd_logger_probe,
.remove = cros_usbpd_logger_remove,
};
module_platform_driver(cros_usbpd_logger_driver);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Logging driver for ChromeOS EC USBPD Charger.");
MODULE_ALIAS("platform:" DRV_NAME);

View File

@@ -4,31 +4,7 @@
*
* Copyright 2019 Google LLC
*
* There is only one attribute used for debugging, called raw.
* You can write a hexadecimal sentence to raw, and that series of bytes
* will be sent to the EC. Then, you can read the bytes of response
* by reading from raw.
*
* For writing:
* Bytes 0-1 indicate the message type:
* 00 F0 = Execute Legacy Command
* 00 F2 = Read/Write NVRAM Property
* Byte 2 provides the command code
* Bytes 3+ consist of the data passed in the request
*
* When referencing the EC interface spec, byte 2 corresponds to MBOX[0],
* byte 3 corresponds to MBOX[1], etc.
*
* At least three bytes are required, for the msg type and command,
* with additional bytes optional for additional data.
*
* Example:
* // Request EC info type 3 (EC firmware build date)
* $ echo 00 f0 38 00 03 00 > raw
* // View the result. The decoded ASCII result "12/21/18" is
* // included after the raw hex.
* $ cat raw
* 00 31 32 2f 32 31 2f 31 38 00 38 00 01 00 2f 00 .12/21/18.8...
* See Documentation/ABI/testing/debugfs-wilco-ec for usage.
*/
#include <linux/ctype.h>
@@ -136,18 +112,15 @@ static ssize_t raw_write(struct file *file, const char __user *user_buf,
ret = parse_hex_sentence(buf, kcount, request_data, TYPE_AND_DATA_SIZE);
if (ret < 0)
return ret;
/* Need at least two bytes for message type and one for command */
/* Need at least two bytes for message type and one byte of data */
if (ret < 3)
return -EINVAL;
/* Clear response data buffer */
memset(debug_info->raw_data, '\0', EC_MAILBOX_DATA_SIZE_EXTENDED);
msg.type = request_data[0] << 8 | request_data[1];
msg.flags = WILCO_EC_FLAG_RAW;
msg.command = request_data[2];
msg.request_data = ret > 3 ? request_data + 3 : 0;
msg.request_size = ret - 3;
msg.flags = 0;
msg.request_data = request_data + 2;
msg.request_size = ret - 2;
memset(debug_info->raw_data, 0, sizeof(debug_info->raw_data));
msg.response_data = debug_info->raw_data;
msg.response_size = EC_MAILBOX_DATA_SIZE;
@@ -174,7 +147,8 @@ static ssize_t raw_read(struct file *file, char __user *user_buf, size_t count,
fmt_len = hex_dump_to_buffer(debug_info->raw_data,
debug_info->response_size,
16, 1, debug_info->formatted_data,
FORMATTED_BUFFER_SIZE, true);
sizeof(debug_info->formatted_data),
true);
/* Only return response the first time it is read */
debug_info->response_size = 0;
}
@@ -190,6 +164,51 @@ static const struct file_operations fops_raw = {
.llseek = no_llseek,
};
#define CMD_KB_CHROME 0x88
#define SUB_CMD_H1_GPIO 0x0A
struct h1_gpio_status_request {
u8 cmd; /* Always CMD_KB_CHROME */
u8 reserved;
u8 sub_cmd; /* Always SUB_CMD_H1_GPIO */
} __packed;
struct hi_gpio_status_response {
u8 status; /* 0 if allowed */
u8 val; /* BIT(0)=ENTRY_TO_FACT_MODE, BIT(1)=SPI_CHROME_SEL */
} __packed;
static int h1_gpio_get(void *arg, u64 *val)
{
struct wilco_ec_device *ec = arg;
struct h1_gpio_status_request rq;
struct hi_gpio_status_response rs;
struct wilco_ec_message msg;
int ret;
memset(&rq, 0, sizeof(rq));
rq.cmd = CMD_KB_CHROME;
rq.sub_cmd = SUB_CMD_H1_GPIO;
memset(&msg, 0, sizeof(msg));
msg.type = WILCO_EC_MSG_LEGACY;
msg.request_data = &rq;
msg.request_size = sizeof(rq);
msg.response_data = &rs;
msg.response_size = sizeof(rs);
ret = wilco_ec_mailbox(ec, &msg);
if (ret < 0)
return ret;
if (rs.status)
return -EIO;
*val = rs.val;
return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(fops_h1_gpio, h1_gpio_get, NULL, "0x%02llx\n");
/**
* wilco_ec_debugfs_probe() - Create the debugfs node
* @pdev: The platform device, probably created in core.c
@@ -211,6 +230,8 @@ static int wilco_ec_debugfs_probe(struct platform_device *pdev)
if (!debug_info->dir)
return 0;
debugfs_create_file("raw", 0644, debug_info->dir, NULL, &fops_raw);
debugfs_create_file("h1_gpio", 0444, debug_info->dir, ec,
&fops_h1_gpio);
return 0;
}

View File

@@ -92,21 +92,10 @@ static void wilco_ec_prepare(struct wilco_ec_message *msg,
struct wilco_ec_request *rq)
{
memset(rq, 0, sizeof(*rq));
/* Handle messages without trimming bytes from the request */
if (msg->request_size && msg->flags & WILCO_EC_FLAG_RAW_REQUEST) {
rq->reserved_raw = *(u8 *)msg->request_data;
msg->request_size--;
memmove(msg->request_data, msg->request_data + 1,
msg->request_size);
}
/* Fill in request packet */
rq->struct_version = EC_MAILBOX_PROTO_VERSION;
rq->mailbox_id = msg->type;
rq->mailbox_version = EC_MAILBOX_VERSION;
rq->data_size = msg->request_size + EC_MAILBOX_DATA_EXTRA;
rq->command = msg->command;
rq->data_size = msg->request_size;
/* Checksum header and data */
rq->checksum = wilco_ec_checksum(rq, sizeof(*rq));
@@ -159,6 +148,12 @@ static int wilco_ec_transfer(struct wilco_ec_device *ec,
return -EIO;
}
/*
* The EC always returns either EC_MAILBOX_DATA_SIZE or
* EC_MAILBOX_DATA_SIZE_EXTENDED bytes of data, so we need to
* calculate the checksum on **all** of this data, even if we
* won't use all of it.
*/
if (msg->flags & WILCO_EC_FLAG_EXTENDED_DATA)
size = EC_MAILBOX_DATA_SIZE_EXTENDED;
else
@@ -173,33 +168,26 @@ static int wilco_ec_transfer(struct wilco_ec_device *ec,
return -EBADMSG;
}
/* Check that the EC reported success */
msg->result = rs->result;
if (msg->result) {
dev_dbg(ec->dev, "bad response: 0x%02x\n", msg->result);
if (rs->result) {
dev_dbg(ec->dev, "EC reported failure: 0x%02x\n", rs->result);
return -EBADMSG;
}
/* Check the returned data size, skipping the header */
if (rs->data_size != size) {
dev_dbg(ec->dev, "unexpected packet size (%u != %zu)",
rs->data_size, size);
return -EMSGSIZE;
}
/* Skip 1 response data byte unless specified */
size = (msg->flags & WILCO_EC_FLAG_RAW_RESPONSE) ? 0 : 1;
if ((ssize_t) rs->data_size - size < msg->response_size) {
dev_dbg(ec->dev, "response data too short (%zd < %zu)",
(ssize_t) rs->data_size - size, msg->response_size);
if (rs->data_size < msg->response_size) {
dev_dbg(ec->dev, "EC didn't return enough data (%u < %zu)",
rs->data_size, msg->response_size);
return -EMSGSIZE;
}
/* Ignore response data bytes as requested */
memcpy(msg->response_data, rs->data + size, msg->response_size);
memcpy(msg->response_data, rs->data, msg->response_size);
/* Return actual amount of data received */
return msg->response_size;
return rs->data_size;
}
/**
@@ -207,10 +195,12 @@ static int wilco_ec_transfer(struct wilco_ec_device *ec,
* @ec: EC device.
* @msg: EC message data for request and response.
*
* On entry msg->type, msg->flags, msg->command, msg->request_size,
* msg->response_size, and msg->request_data should all be filled in.
* On entry msg->type, msg->request_size, and msg->request_data should all be
* filled in. If desired, msg->flags can be set.
*
* On exit msg->result and msg->response_data will be filled.
* If a response is expected, msg->response_size should be set, and
* msg->response_data should point to a buffer with enough space. On exit
* msg->response_data will be filled.
*
* Return: number of bytes received or negative error code on failure.
*/
@@ -219,9 +209,8 @@ int wilco_ec_mailbox(struct wilco_ec_device *ec, struct wilco_ec_message *msg)
struct wilco_ec_request *rq;
int ret;
dev_dbg(ec->dev, "cmd=%02x type=%04x flags=%02x rslen=%zu rqlen=%zu\n",
msg->command, msg->type, msg->flags, msg->response_size,
msg->request_size);
dev_dbg(ec->dev, "type=%04x flags=%02x rslen=%zu rqlen=%zu\n",
msg->type, msg->flags, msg->response_size, msg->request_size);
mutex_lock(&ec->mailbox_lock);
/* Prepare request packet */