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/wilco/mailbox.c | |
parent | 370123e1a3cd3992ba4fb21813d3b96f6ce0376d (diff) | |
download | coreboot-21dde8b25f610c02a8c71341a6579e9f850b690b.tar.xz |
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/wilco/mailbox.c')
-rw-r--r-- | src/ec/google/wilco/mailbox.c | 257 |
1 files changed, 257 insertions, 0 deletions
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; +} |