aboutsummaryrefslogtreecommitdiff
path: root/src/ec/google/wilco
diff options
context:
space:
mode:
Diffstat (limited to 'src/ec/google/wilco')
-rw-r--r--src/ec/google/wilco/Kconfig47
-rw-r--r--src/ec/google/wilco/Makefile.inc5
-rw-r--r--src/ec/google/wilco/ec.h47
-rw-r--r--src/ec/google/wilco/mailbox.c257
4 files changed, 356 insertions, 0 deletions
diff --git a/src/ec/google/wilco/Kconfig b/src/ec/google/wilco/Kconfig
new file mode 100644
index 0000000000..e905d5ee21
--- /dev/null
+++ b/src/ec/google/wilco/Kconfig
@@ -0,0 +1,47 @@
+config EC_GOOGLE_WILCO
+ bool
+ default n
+ select EC_GOOGLE_COMMON_MEC
+ help
+ Google Wilco Embedded Controller interface.
+
+config EC_BASE_ACPI_DATA
+ hex
+ default 0x930
+ help
+ This option provides the 16-bit IO base address for the ACPI
+ data interface. This is the standard ACPI EC interface that
+ is used by the ACPI EC drivers in the OS.
+
+config EC_BASE_ACPI_COMMAND
+ hex
+ default 0x934
+ help
+ This option provides the 16-bit IO base address for the ACPI
+ EC command interface. This is the standard ACPI EC interface
+ that is used by the ACPI EC drivers in the OS.
+
+config EC_BASE_HOST_DATA
+ hex
+ default 0x940
+ help
+ This option provides the 16-bit IO base address for the host
+ data interface. This is the interface that is used to drive
+ the mailbox protocol.
+
+config EC_BASE_HOST_COMMAND
+ hex
+ default 0x944
+ help
+ This option provides the 16-bit IO base address for the host
+ command interface. This is the interface that is used to drive
+ the mailbox protocol.
+
+config EC_BASE_PACKET
+ hex
+ default 0x950
+ help
+ This option provides the 16-bit IO base address for the EC
+ mailbox interface data region. This data buffer is used along
+ with the host command and data registers to drive the EC
+ mailbox interface. This is also the MEC EMI base address.
diff --git a/src/ec/google/wilco/Makefile.inc b/src/ec/google/wilco/Makefile.inc
new file mode 100644
index 0000000000..6130f6f36a
--- /dev/null
+++ b/src/ec/google/wilco/Makefile.inc
@@ -0,0 +1,5 @@
+ifeq ($(CONFIG_EC_GOOGLE_WILCO),y)
+
+ramstage-y += mailbox.c
+
+endif
diff --git a/src/ec/google/wilco/ec.h b/src/ec/google/wilco/ec.h
new file mode 100644
index 0000000000..0ce6166322
--- /dev/null
+++ b/src/ec/google/wilco/ec.h
@@ -0,0 +1,47 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright 2018 Google LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef EC_GOOGLE_WILCO_EC_H
+#define EC_GOOGLE_WILCO_EC_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+/* Different supported message types */
+enum wilco_ec_msg_type {
+ WILCO_EC_MSG_RAW, /* Raw message, do not skip any data */
+ WILCO_EC_MSG_DEFAULT, /* Skip 1 byte of response data */
+ WILCO_EC_MSG_NO_RESPONSE, /* EC does not respond to command */
+};
+
+/**
+ * wilco_ec_mailbox
+ *
+ * Send a command request to the EC mailbox and receive the response.
+ *
+ * @type: Mailbox message type, see enum above
+ * @command: Command to execute
+ * @request_data: Request data buffer
+ * @request_size: Number of bytes in request data buffer (max 32)
+ * @response_data: Response data buffer
+ * @response_size: Number of bytes in response data buffer (max 32)
+ *
+ * @return number of bytes received, negative error code on failure
+ */
+int wilco_ec_mailbox(enum wilco_ec_msg_type type, uint8_t command,
+ const void *request_data, size_t request_size,
+ void *response_data, size_t response_size);
+
+#endif /* EC_GOOGLE_WILCO_EC_H */
diff --git a/src/ec/google/wilco/mailbox.c b/src/ec/google/wilco/mailbox.c
new file mode 100644
index 0000000000..1c38a5b880
--- /dev/null
+++ b/src/ec/google/wilco/mailbox.c
@@ -0,0 +1,257 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright 2018 Google LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <arch/io.h>
+#include <console/console.h>
+#include <delay.h>
+#include <ec/google/common/mec.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <timer.h>
+#include <types.h>
+
+#include "ec.h"
+
+/* Mailbox ID */
+#define EC_MAILBOX_ID 0x00f0
+
+/* Version of mailbox interface */
+#define EC_MAILBOX_VERSION 0
+
+/* Command to start mailbox transaction */
+#define EC_MAILBOX_START_COMMAND 0xda
+
+/* Version of EC protocol */
+#define EC_MAILBOX_PROTO_VERSION 3
+
+/* Max number of bytes in protocol data payload */
+#define EC_MAILBOX_DATA_SIZE 32
+
+/* Number of header bytes to be counted as data bytes */
+#define EC_MAILBOX_DATA_EXTRA 2
+
+/* Maximum timeout */
+#define EC_MAILBOX_TIMEOUT_MS MSECS_PER_SEC
+
+/* EC response flags */
+#define EC_CMDR_DATA BIT(0) /* Data ready for host to read */
+#define EC_CMDR_PENDING BIT(1) /* Write pending to EC */
+#define EC_CMDR_BUSY BIT(2) /* EC is busy processing a command */
+#define EC_CMDR_CMD BIT(3) /* Last host write was a command */
+
+/* Request to EC */
+struct wilco_ec_request {
+ uint8_t struct_version; /* version (=3) */
+ uint8_t checksum; /* sum of all bytes must be 0 */
+ uint16_t mailbox_id; /* mailbox identifier */
+ uint8_t mailbox_version; /* mailbox version (=0) */
+ uint8_t reserved1; /* unused (=0) */
+ uint16_t data_size; /* length (data + 2 bytes of header) */
+ uint8_t command; /* mailbox command */
+ uint8_t reserved2; /* unused (=0) */
+} __packed;
+
+/* Response from EC */
+struct wilco_ec_response {
+ uint8_t struct_version; /* version (=3) */
+ uint8_t checksum; /* sum of all bytes must be 0 */
+ uint16_t result; /* result code */
+ uint16_t data_size; /* length of data buffer (always 32) */
+ uint8_t reserved[3]; /* unused (=0) */
+ uint8_t data[EC_MAILBOX_DATA_SIZE];
+} __packed;
+
+struct wilco_ec_message {
+ uint8_t command; /* mailbox command code */
+ uint8_t result; /* request result */
+ size_t request_size; /* bytes to send to the EC */
+ size_t response_size; /* bytes expected from the EC */
+ enum wilco_ec_msg_type type; /* message type */
+ /*
+ * This data buffer will contain the request data when passed to
+ * wilco_ec_message() and will contain the response data on return.
+ */
+ uint8_t data[EC_MAILBOX_DATA_SIZE];
+};
+
+static bool wilco_ec_response_timed_out(void)
+{
+ uint8_t mask = EC_CMDR_PENDING | EC_CMDR_BUSY;
+ struct stopwatch sw;
+
+ stopwatch_init_msecs_expire(&sw, EC_MAILBOX_TIMEOUT_MS);
+
+ while (inb(CONFIG_EC_BASE_HOST_COMMAND) & mask) {
+ if (stopwatch_expired(&sw)) {
+ printk(BIOS_ERR, "%s: Command timeout\n", __func__);
+ return true; /* Timed out */
+ }
+ mdelay(1);
+ }
+
+ return false; /* Did not time out */
+}
+
+static uint8_t wilco_ec_checksum(void *data, size_t size)
+{
+ uint8_t *data_bytes = (uint8_t *)data;
+ uint8_t checksum = 0;
+ size_t i;
+
+ for (i = 0; i < size; i++)
+ checksum += data_bytes[i];
+
+ return checksum;
+}
+
+static void wilco_ec_prepare(struct wilco_ec_message *msg,
+ struct wilco_ec_request *rq)
+{
+ memset(rq, 0, sizeof(*rq));
+
+ /* Fill in request packet */
+ rq->struct_version = EC_MAILBOX_PROTO_VERSION;
+ rq->mailbox_id = EC_MAILBOX_ID;
+ rq->mailbox_version = EC_MAILBOX_VERSION;
+ rq->data_size = msg->request_size + EC_MAILBOX_DATA_EXTRA;
+ rq->command = msg->command;
+
+ /* Checksum header and data */
+ rq->checksum = wilco_ec_checksum(rq, sizeof(*rq));
+ rq->checksum += wilco_ec_checksum(msg->data, msg->request_size);
+ rq->checksum = -rq->checksum;
+}
+
+static int wilco_ec_transfer(struct wilco_ec_message *msg)
+{
+ struct wilco_ec_request rq;
+ struct wilco_ec_response rs;
+ uint8_t checksum;
+ size_t skip_size;
+
+ /* Prepare request packet */
+ wilco_ec_prepare(msg, &rq);
+
+ /* Write request header */
+ mec_io_bytes(MEC_IO_WRITE, CONFIG_EC_BASE_PACKET, 0, &rq, sizeof(rq));
+
+ /* Write request data */
+ mec_io_bytes(MEC_IO_WRITE, CONFIG_EC_BASE_PACKET, sizeof(rq),
+ msg->data, msg->request_size);
+
+ /* Start the command */
+ outb(EC_MAILBOX_START_COMMAND, CONFIG_EC_BASE_HOST_COMMAND);
+
+ /* Wait for it to complete */
+ if (wilco_ec_response_timed_out()) {
+ printk(BIOS_ERR, "%s: response timed out\n", __func__);
+ return -1;
+ }
+
+ /* Some commands will put the EC into a state where it cannot respond */
+ if (msg->type == WILCO_EC_MSG_NO_RESPONSE) {
+ printk(BIOS_DEBUG, "%s: EC does not respond to this command\n",
+ __func__);
+ return 0;
+ }
+
+ /* Check result */
+ msg->result = inb(CONFIG_EC_BASE_HOST_DATA);
+ if (msg->result != 0) {
+ printk(BIOS_ERR, "%s: bad response: 0x%02x\n",
+ __func__, msg->result);
+ return -1;
+ }
+
+ /* Read back response */
+ checksum = mec_io_bytes(MEC_IO_READ, CONFIG_EC_BASE_PACKET, 0,
+ &rs, sizeof(rs));
+ if (checksum) {
+ printk(BIOS_ERR, "%s: bad checksum %02x\n", __func__, checksum);
+ return -1;
+ }
+ msg->result = rs.result;
+
+ /* EC always returns EC_MAILBOX_DATA_SIZE bytes */
+ if (rs.data_size > EC_MAILBOX_DATA_SIZE) {
+ printk(BIOS_ERR, "%s: packet too long (%d bytes, expected %d)",
+ __func__, rs.data_size, EC_MAILBOX_DATA_SIZE);
+ return -1;
+ }
+
+ /* Skip response data bytes as requested */
+ skip_size = (msg->type == WILCO_EC_MSG_DEFAULT) ? 1 : 0;
+
+ if (msg->response_size > rs.data_size - skip_size) {
+ printk(BIOS_ERR, "%s: data too short (%lu bytes, expected %zu)",
+ __func__, rs.data_size - skip_size, msg->response_size);
+ return -1;
+ }
+
+ memcpy(msg->data, rs.data + skip_size, msg->response_size);
+
+ /* Return actual amount of data received */
+ return msg->response_size;
+}
+
+int wilco_ec_mailbox(enum wilco_ec_msg_type type, uint8_t command,
+ const void *request_data, size_t request_size,
+ void *response_data, size_t response_size)
+{
+ struct wilco_ec_message msg = {
+ .command = command,
+ .request_size = request_size,
+ .response_size = response_size,
+ .type = type,
+ };
+ int ret;
+
+ if (request_size > EC_MAILBOX_DATA_SIZE) {
+ printk(BIOS_ERR, "%s: provided request data too large: %zu\n",
+ __func__, request_size);
+ return -1;
+ }
+ if (response_size > EC_MAILBOX_DATA_SIZE) {
+ printk(BIOS_ERR, "%s: expected response data too large: %zu\n",
+ __func__, response_size);
+ return -1;
+ }
+ if (request_size && !request_data) {
+ printk(BIOS_ERR, "%s: request data missing\n", __func__);
+ return -1;
+ }
+ if (response_size && !response_data) {
+ printk(BIOS_ERR, "%s: request data missing\n", __func__);
+ return -1;
+ }
+
+ /* Copy request data if present */
+ if (request_size)
+ memcpy(msg.data, request_data, request_size);
+
+ /* Do the EC transfer */
+ ret = wilco_ec_transfer(&msg);
+
+ /* Copy response data if present */
+ if (ret > 0 && response_size)
+ memcpy(response_data, msg.data, response_size);
+
+ /* Return error if message result is non-zero */
+ if (ret >= 0 && msg.result)
+ ret = -1;
+
+ return ret;
+}