summaryrefslogtreecommitdiff
path: root/chip/g/usb_spi_v2.c
diff options
context:
space:
mode:
Diffstat (limited to 'chip/g/usb_spi_v2.c')
-rw-r--r--chip/g/usb_spi_v2.c189
1 files changed, 157 insertions, 32 deletions
diff --git a/chip/g/usb_spi_v2.c b/chip/g/usb_spi_v2.c
index 89a492c0c1..abc234c708 100644
--- a/chip/g/usb_spi_v2.c
+++ b/chip/g/usb_spi_v2.c
@@ -5,10 +5,12 @@
#include "ccd_config.h"
#include "common.h"
-#include "link_defs.h"
#include "gpio.h"
+#include "link_defs.h"
#include "registers.h"
+#include "shared_mem.h"
#include "spi.h"
+#include "spi_controller.h"
#include "spi_flash.h"
#include "timer.h"
#include "usb_descriptor.h"
@@ -298,18 +300,34 @@ static uint16_t usb_spi_read_packet(struct usb_spi_config const *config)
queue_count(config->consumer.queue));
}
-static void usb_spi_write_packet(struct usb_spi_config const *config,
- uint8_t count)
+/*
+ * Put a packet on USB TX queue.
+ *
+ * Return EC_SUCCESS if there was enough room on the queue or EC_ERROR_TIMEOUT
+ * if the queue is full for longer than 500 ms
+ */
+static enum ec_error_list usb_spi_write_packet(
+ struct usb_spi_config const *config,
+ uint8_t count)
{
+#define QUEUE_BACKOFF_TIMEOUT_MS 1
+#define MAX_QUEUE_WAIT_MS 500
+ int i = (MAX_QUEUE_WAIT_MS/QUEUE_BACKOFF_TIMEOUT_MS);
+
/*
* Experiments show that while reading a 16M flash time spent waiting
* is less than 30 ms, which is negligible in this case, as the AP is
* held in reset.
*/
- while (queue_space(config->tx_queue) < count)
+ while (queue_space(config->tx_queue) < count) {
+ if (i-- == 0)
+ return EC_ERROR_TIMEOUT;
msleep(1);
+ }
QUEUE_ADD_UNITS(config->tx_queue, config->buffer, count);
+
+ return EC_SUCCESS;
}
enum packet_id_type {
@@ -376,6 +394,7 @@ union raiden_cmd_hdr {
#define RESP_PAYLOAD_SIZE \
(USB_MAX_PACKET_SIZE - sizeof(struct raiden_response_hdr))
+BUILD_ASSERT(START_PAYLOAD_SIZE < SPI_BUF_SIZE);
/*
* Hardcoded values used to communicate to the host maximum sizes of read and
* write transactions.
@@ -387,10 +406,14 @@ union raiden_cmd_hdr {
#define USB_SPI_MAX_WRITE_COUNT 270
/*
* Flashrom deducts 5 from this value, remaining 2040 bytes size results in 34
- * USB packets of 4 byte header and 60 bytes data.
+ * USB packets of 4 byte header and 60 bytes data, getting the PDU size close
+ * to 2K. Decreasing this size would decrease flashrom read efficiency,
+ * increasing it would put more pressure on the heap size.
*/
#define USB_SPI_MAX_READ_COUNT (5 + 34 * RESP_PAYLOAD_SIZE)
+BUILD_ASSERT(USB_SPI_MAX_READ_COUNT < (CONFIG_SHAREDMEM_MINIMUM_SIZE/2));
+
/* Hadrdcoded response to the config request packet. */
static const struct {
uint16_t rsp_id;
@@ -419,6 +442,51 @@ static uint8_t report_error(void *packet, uint16_t error,
return sizeof(*rsp);
}
+/*
+ * Send buffer containing data read from AP flash to the host.
+ *
+ * The buffer is sent as a V2 PDU, split into as many USB packets as
+ * necessary.
+ */
+static void ship_pdu(const struct usb_spi_config *config, const uint8_t *buf,
+ size_t size)
+{
+ struct raiden_response_hdr *rsp =
+ (struct raiden_response_hdr *)config->buffer;
+ size_t sent_so_far = 0;
+
+ do {
+ size_t this_count = size - sent_so_far;
+
+ if (this_count > RESP_PAYLOAD_SIZE)
+ this_count = RESP_PAYLOAD_SIZE;
+
+ rsp->packet_id = sent_so_far ?
+ USB_SPI_PKT_ID_RSP_TRANSFER_CONTINUE :
+ USB_SPI_PKT_ID_RSP_TRANSFER_START;
+ rsp->value = sent_so_far;
+
+ memcpy(rsp + 1, buf + sent_so_far, this_count);
+ if (usb_spi_write_packet(config, sizeof(*rsp) + this_count)
+ != EC_SUCCESS) {
+ CPRINTS("%s: USB stuck, breaking out", __func__);
+ return;
+ }
+ sent_so_far += this_count;
+
+ } while (sent_so_far != size);
+}
+
+/*
+ * Most recent SPI transaction request received from the host, used when it is
+ * necessary to restart the transaction.
+ */
+static struct {
+ uint16_t count; /* Actual size of the request packet. */
+ union raiden_cmd_hdr hdr;
+ uint8_t payload[6]; /* Enough for any SPI read command. */
+} last_cmd;
+
/* Return length of the last response packet to send back to the host. */
static uint8_t process_raiden_packet(const struct usb_spi_config *config,
size_t count)
@@ -442,18 +510,43 @@ static uint8_t process_raiden_packet(const struct usb_spi_config *config,
packet_id = packet->s.packet_id;
switch (state->raiden_state) {
- case RAIDEN_IDLE:
+ case RAIDEN_IDLE: {
+ char *read_buf;
+ size_t read_limit;
+
if ((packet_id == USB_SPI_PKT_ID_CMD_GET_USB_SPI_CONFIG) &&
(count == sizeof(uint16_t))) {
+ last_cmd.count = 0;
memcpy(config->buffer, &config_rsp, sizeof(config_rsp));
return sizeof(config_rsp);
}
+ /*
+ * If this is a restart request and the most recent command is
+ * available - use the saved command to start over.
+ */
+ if ((packet_id == USB_SPI_PKT_ID_CMD_RESTART_RESPONSE) &&
+ last_cmd.count) {
+ packet = &last_cmd.hdr;
+ packet_id = last_cmd.hdr.s.packet_id;
+ count = last_cmd.count;
+ CPRINTS("%s: will restart the response id %d count %d",
+ __func__, packet_id, count);
+ }
+
if ((packet_id != USB_SPI_PKT_ID_CMD_TRANSFER_START) ||
(count < sizeof(struct raiden_cmd_start_hdr)))
return report_error(config->buffer,
USB_SPI_RUNT_PACKET, state);
+ /* Preserve the request in case host asks to restart. */
+ if (count <= (sizeof(last_cmd) - sizeof(last_cmd.count))) {
+ memcpy(&last_cmd.hdr, packet, count);
+ last_cmd.count = count;
+ } else {
+ last_cmd.count = 0;
+ }
+
/* This is request to start a new transaction. */
state->total_write_count = packet->s.write_count;
total_read_count = packet->s.read_count;
@@ -496,34 +589,63 @@ static uint8_t process_raiden_packet(const struct usb_spi_config *config,
else
this_write_count = state->total_write_count;
+ /* Allocate buffer to store the entire requested read size. */
+ if (total_read_count) {
+ if (shared_mem_acquire(total_read_count, &read_buf) !=
+ EC_SUCCESS) {
+ ccprintf("%s: Failed to allocate %zd bytes!\n",
+ __func__, total_read_count);
+ return report_error(config->buffer,
+ USB_SPI_UNKNOWN_ERROR,
+ state);
+ }
+ }
+
+ last_sub_transaction = false;
+
+ /*
+ * This will send the write portion and read the entire read
+ * portion in multiple SPI subtransactions.
+ *
+ * In case the write PDU is split into multiple USB packets
+ * this will send the first packet and change state to
+ * RAIDEN_WRITING, where it will process the rest of USB
+ * packets of this PDU.
+ */
while (this_write_count || (read_so_far != total_read_count)) {
+
this_read_count = total_read_count - read_so_far;
+
/*
- * Need to decide if CS needs to be deasserted in this
- * iteration. It would be the case if both final read
- * and write counts fit into USB packet.
+ * If there is a write portion, read limit needs to be
+ * reduced.
*/
- if ((this_read_count > RESP_PAYLOAD_SIZE) ||
- (state->total_write_count > START_PAYLOAD_SIZE)) {
- last_sub_transaction = false;
-
- /* Limit read count by USB packet capacity. */
- if (this_read_count > RESP_PAYLOAD_SIZE)
- this_read_count = RESP_PAYLOAD_SIZE;
- } else {
+ read_limit = SPI_BUF_SIZE - this_write_count;
+
+ /* Limit read count by SPI driver capacity. */
+ if (this_read_count > read_limit)
+ this_read_count = read_limit;
+
+ /*
+ * This transaction is over when we have read as much
+ * as requested (which could be zero), AND the entire
+ * write portion fit into the USB payload.
+ */
+ if (((this_read_count + read_so_far) ==
+ total_read_count) &&
+ (state->total_write_count <= START_PAYLOAD_SIZE))
last_sub_transaction = true;
- }
res = usb_spi_map_error(spi_sub_transaction(
SPI_FLASH_DEVICE,
config->buffer +
sizeof(struct raiden_cmd_start_hdr),
- this_write_count,
- config->buffer +
- sizeof(struct raiden_response_hdr),
+ this_write_count, read_buf + read_so_far,
this_read_count, last_sub_transaction));
if (res) {
+ if (total_read_count)
+ shared_mem_release(read_buf);
rsp->value = res;
rsp->packet_id =
USB_SPI_PKT_ID_RSP_TRANSFER_START;
@@ -544,11 +666,14 @@ static uint8_t process_raiden_packet(const struct usb_spi_config *config,
return 0;
}
- if (last_sub_transaction)
+ if (last_sub_transaction) {
+ if (total_read_count) {
+ ship_pdu(config, read_buf,
+ total_read_count);
+ shared_mem_release(read_buf);
+ }
break;
-
- usb_spi_write_packet(config,
- this_read_count + sizeof(*rsp));
+ }
/*
* Make sure this does not keep the loop going, we
@@ -557,14 +682,15 @@ static uint8_t process_raiden_packet(const struct usb_spi_config *config,
this_write_count = 0;
}
- if (this_read_count)
- return this_read_count + sizeof(*rsp);
-
- if (state->total_write_count &&
- (state->total_write_count == this_write_count))
+ /*
+ * If this was a pure write transaction and it fit into one
+ * USB packet - send a response to the host.
+ */
+ if (!total_read_count && state->total_write_count &&
+ (state->raiden_state == RAIDEN_IDLE))
return sizeof(*rsp);
return 0;
-
+ }
case RAIDEN_WRITING:
if ((packet_id != USB_SPI_PKT_ID_CMD_TRANSFER_CONTINUE) ||
(count <= sizeof(struct raiden_cmd_continue_hdr)))
@@ -690,4 +816,3 @@ void usb_spi_enable(struct usb_spi_config const *config, int enabled)
hook_call_deferred(config->deferred, 0);
}
-