mirror of
https://github.com/torvalds/linux.git
synced 2024-11-10 14:11:52 +00:00
chrome platform changes for v5.2
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 -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEE6gYDF28Li+nEiKLaHwn1ewov5lgFAlzV61YACgkQHwn1ewov 5lgEFw//S7GVmBrFxcqu5wAjF1CW+mOGi3y6nVuTAHanWG/hJGWT+itOwsdDp6c9 TggYgohprz64JAZOPqPCTonV/qbxgsfKrSQRxFDtHH4F1iEUF46fnlsULDKi8VwM Qzj4g4d//ePsOwHOsYVrbJRU2qKyF6Rm2hpOxKfI9u2Dv5fxLFu6fxUhrSq1Inr6 U67j7pxBwOnBtN2A6hMKHZaOUVkSNYT6azSPO3Z2YH0aky2Baxw/LPoRnbCNhwUQ iyneX5+K0wpCz2fpnBF/QSh1QBACeyfrO6HHA+flfaejhShaWttrS36Gar+sdHFN p6eeR1CoEJZbRY79Eetj8Cv5Be1ivVG/SC5JF4O1apAAn87wXLI6AaLG/03ul0vc KOkcjrXMxISRlAUr+OKD0rg3Uo2oI0ht70XMT9DDsCRNDoVHvkDQJNdkWrKq+E1c xL4YeLofZpcEN+Oe/WnwUZtYUdY3qcWs+C4hV+h0L0Ke5xir25DEUfF3j3J/uK2B JEgkTpH8j6YjbGAErBPkTxWt5HE3oWtkK4moPlrfPKfxoSo2eRDvqz68qHsgIn8p WBM+FSr+dQ7qyYDigMKrFSesiBpwCBI4lIgPxkvTxqbubaoZcsABHm3BUGjykXII E5z2qsgRnDrB+uGGDkTvDoR0Kr3U0hGlag7u/N61H86PoiMLUig= =NF5N -----END PGP SIGNATURE----- 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:
commit
47782361ac
@ -1,23 +1,46 @@
|
||||
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
|
||||
|
@ -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/
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
258
drivers/platform/chrome/cros_ec_rpmsg.c
Normal file
258
drivers/platform/chrome/cros_ec_rpmsg.c
Normal 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)");
|
@ -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(¶ms.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, ¶ms.work);
|
||||
flush_work(¶ms.work);
|
||||
destroy_work_on_stack(¶ms.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;
|
||||
|
124
drivers/platform/chrome/cros_ec_trace.c
Normal file
124
drivers/platform/chrome/cros_ec_trace.c
Normal 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"
|
51
drivers/platform/chrome/cros_ec_trace.h
Normal file
51
drivers/platform/chrome/cros_ec_trace.h
Normal 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>
|
262
drivers/platform/chrome/cros_usbpd_logger.c
Normal file
262
drivers/platform/chrome/cros_usbpd_logger.c
Normal 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);
|
@ -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 = ¶m,
|
||||
.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;
|
||||
|
Loading…
Reference in New Issue
Block a user