Commit 47782361 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'tag-chrome-platform-for-v5.2' of...

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
parents 8148c17b 58a2109f
What: /sys/kernel/debug/wilco_ec/h1_gpio
Date: April 2019
KernelVersion: 5.2
Description:
As part of Chrome OS's FAFT (Fully Automated Firmware Testing)
tests, we need to ensure that the H1 chip is properly setting
some GPIO lines. The h1_gpio attribute exposes the state
of the lines:
- ENTRY_TO_FACT_MODE in BIT(0)
- SPI_CHROME_SEL in BIT(1)
Output will formatted with "0x%02x\n".
What: /sys/kernel/debug/wilco_ec/raw
Date: January 2019
KernelVersion: 5.1
Description:
Write and read raw mailbox commands to the EC.
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
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.
At least three bytes are required, for the msg type and command,
with additional bytes optional for additional data.
For writing, bytes 0-1 indicate the message type, one of enum
wilco_ec_msg_type. Byte 2+ consist of the data passed in the
request, starting at MBOX[0]
At least three bytes are required for writing, two for the type
and at least a single byte of data. Only the first
EC_MAILBOX_DATA_SIZE bytes of MBOX will be used.
Example:
// Request EC info type 3 (EC firmware build date)
$ echo 00 f0 38 00 03 00 > raw
// Corresponds with sending type 0x00f0 with
// MBOX = [38, 00, 03, 00]
$ echo 00 f0 38 00 03 00 > /sys/kernel/debug/wilco_ec/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...
// Corresponds with MBOX = [00, 00, 31, 32, 2f, 32, 31, 38, ...]
$ cat /sys/kernel/debug/wilco_ec/raw
00 00 31 32 2f 32 31 2f 31 38 00 38 00 01 00 2f 00 ..12/21/18.8...
Note that the first 32 bytes of the received MBOX[] will be
printed, even if some of the data is junk. It is up to you to
know how many of the first bytes of data are the actual
response.
......@@ -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
# 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/
......@@ -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");
goto resched;
}
if (snapshot_msg.result != EC_RES_SUCCESS) {
dev_err(ec->dev, "EC failed to snapshot the console log\n");
ret = cros_ec_cmd_xfer_status(ec->ec_dev, &snapshot_msg);
if (ret < 0)
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");
break;
}
if (debug_info->read_msg->result != EC_RES_SUCCESS) {
dev_err(ec->dev,
"EC failed to read the console log\n");
ret = cros_ec_cmd_xfer_status(ec->ec_dev,
debug_info->read_msg);
if (ret < 0)
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 @@ static int cros_ec_create_panicinfo(struct cros_ec_debugfs *debug_info)
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;
......
......@@ -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;
......
// 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)");
......@@ -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 @@ static int cros_ec_pkt_xfer_spi(struct cros_ec_device *ec_dev,
}
/**
* 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 @@ static int cros_ec_cmd_xfer_spi(struct cros_ec_device *ec_dev,
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;
......
// 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"
/* 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>
// 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);
......@@ -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;
}
......
......@@ -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 */
......
......@@ -21,8 +21,20 @@
#define EC_CMOS_TOD_WRITE 0x02
#define EC_CMOS_TOD_READ 0x08
/* Message sent to the EC to request the current time. */
struct ec_rtc_read_request {
u8 command;
u8 reserved;
u8 param;
} __packed;
static struct ec_rtc_read_request read_rq = {
.command = EC_COMMAND_CMOS,
.param = EC_CMOS_TOD_READ,
};
/**
* struct ec_rtc_read - Format of RTC returned by EC.
* struct ec_rtc_read_response - Format of RTC returned by EC.
* @reserved: Unused byte
* @second: Second value (0..59)
* @minute: Minute value (0..59)
* @hour: Hour value (0..23)
......@@ -33,7 +45,8 @@
*
* All values are presented in binary (not BCD).
*/
struct ec_rtc_read {
struct ec_rtc_read_response {
u8 reserved;
u8 second;
u8 minute;
u8 hour;
......@@ -44,8 +57,10 @@ struct ec_rtc_read {
} __packed;
/**
* struct ec_rtc_write - Format of RTC sent to the EC.
* @param: EC_CMOS_TOD_WRITE
* struct ec_rtc_write_request - Format of RTC sent to the EC.
* @command: Always EC_COMMAND_CMOS
* @reserved: Unused byte
* @param: Always EC_CMOS_TOD_WRITE
* @century: Century value (full year / 100)
* @year: Year value (full year % 100)
* @month: Month value (1..12)
......@@ -57,7 +72,9 @@ struct ec_rtc_read {
*
* All values are presented in BCD.
*/
struct ec_rtc_write {
struct ec_rtc_write_request {
u8 command;
u8 reserved;
u8 param;
u8 century;
u8 year;
......@@ -72,19 +89,17 @@ struct ec_rtc_write {
static int wilco_ec_rtc_read(struct device *dev, struct rtc_time *tm)
{
struct wilco_ec_device *ec = dev_get_drvdata(dev->parent);
u8 param = EC_CMOS_TOD_READ;
struct ec_rtc_read rtc;
struct wilco_ec_message msg = {
.type = WILCO_EC_MSG_LEGACY,
.flags = WILCO_EC_FLAG_RAW_RESPONSE,
.command = EC_COMMAND_CMOS,
.request_data = &param,
.request_size = sizeof(param),
.response_data = &rtc,
.response_size = sizeof(rtc),
};
struct ec_rtc_read_response rtc;
struct wilco_ec_message msg;
int ret;
memset(&msg, 0, sizeof(msg));
msg.type = WILCO_EC_MSG_LEGACY;
msg.request_data = &read_rq;
msg.request_size = sizeof(read_rq);
msg.response_data = &rtc;
msg.response_size = sizeof(rtc);
ret = wilco_ec_mailbox(ec, &msg);
if (ret < 0)
return ret;
......@@ -106,14 +121,8 @@ static int wilco_ec_rtc_read(struct device *dev, struct rtc_time *tm)
static int wilco_ec_rtc_write(struct device *dev, struct rtc_time *tm)
{
struct wilco_ec_device *ec = dev_get_drvdata(dev->parent);
struct ec_rtc_write rtc;
struct wilco_ec_message msg = {
.type = WILCO_EC_MSG_LEGACY,
.flags = WILCO_EC_FLAG_RAW_RESPONSE,
.command = EC_COMMAND_CMOS,
.request_data = &rtc,
.request_size = sizeof(rtc),
};
struct ec_rtc_write_request rtc;
struct wilco_ec_message msg;
int year = tm->tm_year + 1900;
/*
* Convert from 0=Sunday to 0=Saturday for the EC
......@@ -123,6 +132,7 @@ static int wilco_ec_rtc_write(struct device *dev, struct rtc_time *tm)
int wday = tm->tm_wday == 6 ? 0 : tm->tm_wday + 1;
int ret;
rtc.command = EC_COMMAND_CMOS;
rtc.param = EC_CMOS_TOD_WRITE;
rtc.century = bin2bcd(year / 100);
rtc.year = bin2bcd(year % 100);
......@@ -133,6 +143,11 @@ static int wilco_ec_rtc_write(struct device *dev, struct rtc_time *tm)
rtc.second = bin2bcd(tm->tm_sec);
rtc.weekday = bin2bcd(wday);
memset(&msg, 0, sizeof(msg));
msg.type = WILCO_EC_MSG_LEGACY;
msg.request_data = &rtc;
msg.request_size = sizeof(rtc);
ret = wilco_ec_mailbox(ec, &msg);
if (ret < 0)
return ret;
......
......@@ -14,10 +14,6 @@
/* Message flags for using the mailbox() interface */
#define WILCO_EC_FLAG_NO_RESPONSE BIT(0) /* EC does not respond */
#define WILCO_EC_FLAG_EXTENDED_DATA BIT(1) /* EC returns 256 data bytes */
#define WILCO_EC_FLAG_RAW_REQUEST BIT(2) /* Do not trim request data */
#define WILCO_EC_FLAG_RAW_RESPONSE BIT(3) /* Do not trim response data */
#define WILCO_EC_FLAG_RAW (WILCO_EC_FLAG_RAW_REQUEST | \
WILCO_EC_FLAG_RAW_RESPONSE)
/* Normal commands have a maximum 32 bytes of data */
#define EC_MAILBOX_DATA_SIZE 32
......@@ -56,10 +52,7 @@ struct wilco_ec_device {
* @mailbox_id: Mailbox identifier, specifies the command set.
* @mailbox_version: Mailbox interface version %EC_MAILBOX_VERSION
* @reserved: Set to zero.
* @data_size: Length of request, data + last 2 bytes of the header.
* @command: Mailbox command code, unique for each mailbox_id set.
* @reserved_raw: Set to zero for most commands, but is used by
* some command types and for raw commands.
* @data_size: Length of following data.
*/
struct wilco_ec_request {
u8 struct_version;
......@@ -68,8 +61,6 @@ struct wilco_ec_request {
u8 mailbox_version;
u8 reserved;
u16 data_size;
u8 command;
u8 reserved_raw;
} __packed;
/**
......@@ -79,8 +70,6 @@ struct wilco_ec_request {
* @result: Result code from the EC. Non-zero indicates an error.
* @data_size: Length of the response data buffer.
* @reserved: Set to zero.
* @mbox0: EC returned data at offset 0 is unused (always 0) so this byte
* is treated as part of the header instead of the data.
* @data: Response data buffer. Max size is %EC_MAILBOX_DATA_SIZE_EXTENDED.
*/
struct wilco_ec_response {
......@@ -89,7 +78,6 @@ struct wilco_ec_response {
u16 result;
u16 data_size;
u8 reserved[2];
u8 mbox0;
u8 data[0];
} __packed;
......@@ -111,21 +99,15 @@ enum wilco_ec_msg_type {
* struct wilco_ec_message - Request and response message.
* @type: Mailbox message type.
* @flags: Message flags, e.g. %WILCO_EC_FLAG_NO_RESPONSE.
* @command: Mailbox command code.
* @result: Result code from the EC. Non-zero indicates an error.
* @request_size: Number of bytes to send to the EC.
* @request_data: Buffer containing the request data.
* @response_size: Number of bytes expected from the EC.
* This is 32 by default and 256 if the flag
* is set for %WILCO_EC_FLAG_EXTENDED_DATA
* @response_size: Number of bytes to read from EC.
* @response_data: Buffer containing the response data, should be
* response_size bytes and allocated by caller.
*/
struct wilco_ec_message {
enum wilco_ec_msg_type type;
u8 flags;
u8 command;
u8 result;
size_t request_size;
void *request_data;
size_t response_size;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment