diff options
author | Duncan Laurie <dlaurie@google.com> | 2018-10-15 02:00:39 +0000 |
---|---|---|
committer | Duncan Laurie <dlaurie@chromium.org> | 2018-10-31 18:29:00 +0000 |
commit | 21dde8b25f610c02a8c71341a6579e9f850b690b (patch) | |
tree | 04e4d427411a4a0f0ccc58119ffb5e404ca6614f /src/ec/google | |
parent | 370123e1a3cd3992ba4fb21813d3b96f6ce0376d (diff) |
ec/google/wilco: Add Wilco EC mailbox interface
The Google "Wilco" Embedded Controller is a new embedded controller that
will be used in some future devices. The mailbox interface is simliar
to the existing Chromium EC protocol version 3, but not close enough
that it was convenient to re-use the full Chrome EC driver.
This commit adds the basic mailbox interface for ramstage which will be
used by future commits to send varous mailbox commands during the boot
process. The IO base addresses for the mailbox interface are defined in
Kconfig so they can be changed by the mainboard if needed.
Change-Id: I8520dadfa982c9d14357cf2aa644e255cef425c2
Signed-off-by: Duncan Laurie <dlaurie@google.com>
Reviewed-on: https://review.coreboot.org/29113
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Aaron Durbin <adurbin@chromium.org>
Diffstat (limited to 'src/ec/google')
-rw-r--r-- | src/ec/google/wilco/Kconfig | 47 | ||||
-rw-r--r-- | src/ec/google/wilco/Makefile.inc | 5 | ||||
-rw-r--r-- | src/ec/google/wilco/ec.h | 47 | ||||
-rw-r--r-- | src/ec/google/wilco/mailbox.c | 257 |
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; +} |