Received: by 2002:ad5:474a:0:0:0:0:0 with SMTP id i10csp6908212imu; Thu, 31 Jan 2019 01:32:34 -0800 (PST) X-Google-Smtp-Source: ALg8bN62YKphZNGzKWHP2ggay/qWYuemXVYsEb5L0ZmB+h8apRWIggKpJeueaqK+LNqVwchpULVq X-Received: by 2002:a63:5f50:: with SMTP id t77mr30784397pgb.76.1548927154709; Thu, 31 Jan 2019 01:32:34 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1548927154; cv=none; d=google.com; s=arc-20160816; b=MM62oDDiDgi8IzOANnc5o6Zb/85xIcktBNKEy7JDBWctAmr/wpQ0H63bDLvTzdX2MF MAiU9RSUiHY+EcM8CIWTEAMB7NtcOuRf1sRINZYhobGVP/x3iruEg/WaP44vZBDYh29v S9PfsU0PD3hADYJdYvyWeEk8w2C305QCwKaEeaoCnLFBOqF0f9aF2HPxEMCf/ASXIzu0 x3RmIzd3/LMpF7wWSRaZNpKAkJDJ86eVwtDk7DIsOJ9P/ji4rC1YZm6gIqkR97F3T3YK rKjKIEVLXSij9BHMIYQQNIAvEQ2gw2LbOwq2cgyeiIRD0HxkPof4dMLsP8i+WIU98nNH rxRA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:to:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:from :dkim-signature; bh=BakVyP3L/pLlOufG/DBli2IVC5MfnYy8IdEPPT2ki98=; b=vz5+bfWPDq78aXQZhUTRVbkKsK0zzc8yRxHmDOefdTqMhqhJysyzsm2T7Ukn31QUUg ZbyQyjP4eAIR70qu91WjagjjKmuVma3UM4lG37EjsGMkSbza39eUrSgRM+8AGP2Oh3FQ We74RtCVKDHZmi2Yf9nwm6m7PUBz7VqAtbPgKy1VUefTe6bwvEhODznmJC0uPeOD5sGH NQ/7BPFUcy46/ikVOfQlOC0NPZK3HHi4USoLzqGFwdfKeE8mzDofyXiENNttc/bJtRcR /gref5RyVjD39e3E3n0I/xaPAosQqtR128xIhyE93Ov+KMYlSTP2N3ZvhDx3ngsk88f+ cJQw== ARC-Authentication-Results: i=1; mx.google.com; dkim=fail header.i=@chromium.org header.s=google header.b="J1LXG/sb"; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=chromium.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id b24si3521844pgg.288.2019.01.31.01.32.19; Thu, 31 Jan 2019 01:32:34 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=fail header.i=@chromium.org header.s=google header.b="J1LXG/sb"; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=chromium.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1731525AbfAaJcC (ORCPT + 99 others); Thu, 31 Jan 2019 04:32:02 -0500 Received: from mail-pf1-f195.google.com ([209.85.210.195]:39143 "EHLO mail-pf1-f195.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1731267AbfAaJcB (ORCPT ); Thu, 31 Jan 2019 04:32:01 -0500 Received: by mail-pf1-f195.google.com with SMTP id r136so1219321pfc.6 for ; Thu, 31 Jan 2019 01:32:00 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=BakVyP3L/pLlOufG/DBli2IVC5MfnYy8IdEPPT2ki98=; b=J1LXG/sbD4fVUqOkOev+apV7oKclyTtt0L/VoirZEHXA+7ublxyH0bMOGT3zKof9FZ 1liIKKuwSGCrXSPpzQ6sBAasaRLACIkadXNiDIYpcdWfOTvkkDgCYQxXtHlTNzkGrxnj xi4VJxiBD2nA9ozpdzz9yHec+Z47MUHkhg018= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=BakVyP3L/pLlOufG/DBli2IVC5MfnYy8IdEPPT2ki98=; b=EpBfr/0caslSAzY9Q3XQcdLV9xo1b2KnAF9+3HJPfeILalRlaOWWuMJNKuERRCc62Y MPcT8tUI95D534mv3pc/Qn71mR3a5zgl/HWtJw26GRy/rZdQNq8Fw4o1iyj6HN1O6ZJt JxQaG4fiu9mbJetJeaK8LHPQGE7Qqf1H7MyJ/LswMsbUYTd6EZljSmygFarcJFKvUy9A ngDoRdQ8jW+rB9bpPGX9XRA4xEJK2VwDYUXczbsRNMxg6Z1qbhIjpvJs3oHl6qOiafSC W4e20NT8DCUs+mpe/NMwyCBq69nIHrODyMcgbewGcFtLPYlr3sNIbSvXnfWf7Ptskrb6 ngFg== X-Gm-Message-State: AJcUukfwXW/vCFiUL6JjNb5GyEpJ0MWjAeGtYZ7QSRDI3ZytszhvVElk zJF0ou8CCZK85izUSSABmMxdKg== X-Received: by 2002:a63:e101:: with SMTP id z1mr30938070pgh.310.1548927120366; Thu, 31 Jan 2019 01:32:00 -0800 (PST) Received: from pihsun-z840.tpe.corp.google.com ([2401:fa00:1:10:7889:7a43:f899:134c]) by smtp.googlemail.com with ESMTPSA id w184sm4531197pgd.34.2019.01.31.01.31.58 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 31 Jan 2019 01:31:59 -0800 (PST) From: Pi-Hsun Shih Cc: Pi-Hsun Shih , Erin Lo , Enric Balletbo Serra , Guenter Roeck , Benson Leung , Enric Balletbo i Serra , linux-kernel@vger.kernel.org (open list) Subject: [PATCH v4 4/6] mfd: add EC host command support using rpmsg. Date: Thu, 31 Jan 2019 17:31:29 +0800 Message-Id: <20190131093131.245531-5-pihsun@chromium.org> X-Mailer: git-send-email 2.20.1.611.gfbb209baf1-goog In-Reply-To: <20190131093131.245531-1-pihsun@chromium.org> References: <20190131093131.245531-1-pihsun@chromium.org> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit To: unlisted-recipients:; (no To-header on input) Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Add EC host command / host event support through rpmsg. Cc: Enric Balletbo Serra Cc: Guenter Roeck Signed-off-by: Pi-Hsun Shih --- This patch depends on https://lore.kernel.org/patchwork/patch/1024376/ Changes from v3: - Add host event support by adding an extra bytes at the start of IPC message to indicate the type of the message (host event or host command), since there's no additional irq that can be used for host event. Changes from v2: - Wait for ipi ack instead of depends on the behavior in mtk-rpmsg. Changes from v1: - Code format fix based on feedback for cros_ec_rpmsg.c. - Extract feature detection for SCP into separate patch (Patch 6). --- drivers/platform/chrome/Kconfig | 9 + drivers/platform/chrome/Makefile | 1 + drivers/platform/chrome/cros_ec_rpmsg.c | 240 ++++++++++++++++++++++++ 3 files changed, 250 insertions(+) create mode 100644 drivers/platform/chrome/cros_ec_rpmsg.c diff --git a/drivers/platform/chrome/Kconfig b/drivers/platform/chrome/Kconfig index 16b1615958aa2d..f18b4586a7ad5d 100644 --- a/drivers/platform/chrome/Kconfig +++ b/drivers/platform/chrome/Kconfig @@ -72,6 +72,15 @@ config CROS_EC_SPI response time cannot be guaranteed, we support ignoring 'pre-amble' bytes before the response actually starts. +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. + config CROS_EC_LPC tristate "ChromeOS Embedded Controller (LPC)" depends on MFD_CROS_EC && ACPI && (X86 || COMPILE_TEST) diff --git a/drivers/platform/chrome/Makefile b/drivers/platform/chrome/Makefile index cd591bf872bbe9..3e3190af2b50f4 100644 --- a/drivers/platform/chrome/Makefile +++ b/drivers/platform/chrome/Makefile @@ -8,6 +8,7 @@ cros_ec_ctl-objs := cros_ec_sysfs.o cros_ec_lightbar.o \ obj-$(CONFIG_CROS_EC_CTL) += cros_ec_ctl.o obj-$(CONFIG_CROS_EC_I2C) += cros_ec_i2c.o obj-$(CONFIG_CROS_EC_SPI) += cros_ec_spi.o +obj-$(CONFIG_CROS_EC_RPMSG) += cros_ec_rpmsg.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 diff --git a/drivers/platform/chrome/cros_ec_rpmsg.c b/drivers/platform/chrome/cros_ec_rpmsg.c new file mode 100644 index 00000000000000..51ba64305b487b --- /dev/null +++ b/drivers/platform/chrome/cros_ec_rpmsg.c @@ -0,0 +1,240 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright 2018 Google LLC. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define EC_MSG_TIMEOUT_MS 200 +#define HOST_COMMAND_MARK 1 +#define HOST_EVENT_MARK 2 + +struct cros_ec_rpmsg_response { + uint8_t type; + uint8_t data[]; +}; + +struct cros_ec_rpmsg { + struct rpmsg_device *rpdev; + + wait_queue_head_t xfer_ack_wq; + bool xfer_acked; + + struct work_struct host_event_work; +}; + +/** + * cros_ec_cmd_xfer_rpmsg - Transfer a message over rpmsg and receive the reply + * + * This is only used for old EC proto version, and is not supported for this + * driver. + * + * @ec_dev: ChromeOS EC device + * @ec_msg: Message to transfer + */ +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 + */ +static int cros_ec_pkt_xfer_rpmsg(struct cros_ec_device *ec_dev, + struct cros_ec_command *ec_msg) +{ + struct ec_host_response *response; + struct cros_ec_rpmsg *ec_rpmsg = ec_dev->priv; + struct rpmsg_device *rpdev = ec_rpmsg->rpdev; + int len; + u8 sum; + int ret; + int i; + unsigned long timeout; + + ec_msg->result = 0; + len = cros_ec_prepare_tx(ec_dev, ec_msg); + dev_dbg(ec_dev->dev, "prepared, len=%d\n", len); + + ec_rpmsg->xfer_acked = false; + 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_event_timeout(ec_rpmsg->xfer_ack_wq, ec_rpmsg->xfer_acked, + 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--; + 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); + ec_rpmsg->xfer_acked = true; + wake_up(&ec_rpmsg->xfer_ack_wq); + } 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_device *ec_dev; + struct cros_ec_rpmsg *ec_rpmsg; + int ret; + + 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_waitqueue_head(&ec_rpmsg->xfer_ack_wq); + INIT_WORK(&ec_rpmsg->host_event_work, + cros_ec_rpmsg_host_event_function); + + ret = cros_ec_register(ec_dev); + if (ret) { + dev_err(dev, "cannot register EC\n"); + return ret; + } + + return 0; +} + +static const struct rpmsg_device_id cros_ec_rpmsg_device_id[] = { + { .name = "cros-ec-rpmsg", }, + { } +}; +MODULE_DEVICE_TABLE(rpmsg, cros_ec_rpmsg_device_id); + +static struct rpmsg_driver cros_ec_driver_rpmsg = { + .drv.name = KBUILD_MODNAME, + .id_table = cros_ec_rpmsg_device_id, + .probe = cros_ec_rpmsg_probe, + .callback = cros_ec_rpmsg_callback, +}; + +module_rpmsg_driver(cros_ec_driver_rpmsg); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("ChromeOS EC multi function device (rpmsg)"); -- 2.20.1.611.gfbb209baf1-goog