/* Copyright 2015 The Chromium OS Authors. All rights reserved. * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "common.h" #include "console.h" #include "hooks.h" #include "host_command.h" #include "sps.h" #include "util.h" /* * This implements EC host commands over the SPI bus, using the Cr50 SPS (SPI * slave) controller. * * Host commands are communicated using software flow-control, because most of * the embedded controllers either aren't fast enough or don't have any support * for hardware flow-control. * * Every SPI transaction is bi-directional, so when the AP sends commands to * the EC, a default "dummy" byte is returned at the same time. The EC * preconfigures that default response byte to indicate its status (ready, * busy, waiting for more input, etc). Once the AP has sent a complete command * message, it continues clocking bytes to the EC (which the EC ignores) and * just looks at the response byte that comes back. Once the EC has parsed the * command and is ready to reply, it sends a "start of frame" byte, followed by * the actual response. The AP continues to read and ignore bytes from the EC * until it sees the start of frame byte, and then it knows that the EC's * response is starting with the next byte. Refer to include/ec_commands.h for * more details. */ /* Console output macros */ #define CPUTS(outstr) cputs(CC_HOSTCMD, outstr) #define CPRINTS(format, args...) cprints(CC_HOSTCMD, format, ## args) #define CPRINTF(format, args...) cprintf(CC_HOSTCMD, format, ## args) /* * Incoming messages are collected here until they're ready to process. The * buffer will start with a struct ec_host_request, followed by whatever data * is sent by the master. */ #define RXBUF_MAX 512 /* chosen arbitrarily */ static uint8_t rxbuf[RXBUF_MAX]; static unsigned int rxbuf_count; /* num bytes received */ static unsigned int rxbuf_needed; /* num bytes we'd like */ /* * Outgoing messages are shoved in here. We need a preamble byte to mark the * start of the data stream before the data itself. */ #define TXBUF_MAX 512 /* chosen arbitrarily */ static uint8_t txbuf[1 + TXBUF_MAX]; static enum spi_state { /* SPI not enabled (initial state, and when chipset is off) */ SPI_STATE_DISABLED = 0, /* Ready to receive next request */ SPI_STATE_READY_TO_RX, /* Receiving request */ SPI_STATE_RECEIVING_HEADER, SPI_STATE_RECEIVING_BODY, /* Processing request */ SPI_STATE_PROCESSING, /* Sending response */ SPI_STATE_SENDING, /* * Received bad data - transaction started before we were ready, or * packet header from the master didn't parse properly. Ignoring * received data. */ SPI_STATE_RX_BAD, } state; /* If the chip select is disabled, don't transmit response */ static int discard_response; /* Callback to send our response to the master */ static void cb_send_response_packet(struct host_packet *pkt) { uint8_t *bufptr; size_t data_size = pkt->response_size; /* Chip Select was deasserted before we got here */ if (discard_response) { discard_response = 0; state = SPI_STATE_READY_TO_RX; sps_tx_status(EC_SPI_RX_READY); return; } /* * The pkt->response will start at txbuf[1], which means that * pkt->response_size doesn't include the preamble byte. We need to * initialize it and start sending from there. */ txbuf[0] = EC_SPI_FRAME_START; data_size++; bufptr = txbuf; state = SPI_STATE_SENDING; /* There's probably still a race condition somewhere... */ while (data_size && state == SPI_STATE_SENDING) { size_t cnt = sps_transmit(bufptr, data_size); data_size -= cnt; bufptr += cnt; } /* Clock out the end-of-packet marker when we're done. */ sps_tx_status(EC_SPI_PAST_END); } static int req_header_looks_good(const struct ec_host_request *req) { return (req->struct_version == EC_HOST_REQUEST_VERSION) && (req->reserved == 0) && (sizeof(*req) + req->data_len <= RXBUF_MAX); } /* RX FIFO handler (runs in interrupt context) */ static void hc_rx_handler(uint8_t *data, size_t data_size, int cs_disabled) { struct ec_host_request *req = (struct ec_host_request *)rxbuf; static struct host_packet rx_packet; size_t i; if (cs_disabled) { if (state == SPI_STATE_PROCESSING) { /* * A task is preparing a response, but the master has * stopped caring. Set a flag so when the response is * ready we'll just throw it away and reset everything. */ discard_response = 1; return; } /* Otherwise, just go back to waiting for new input */ state = SPI_STATE_READY_TO_RX; sps_tx_status(EC_SPI_RX_READY); return; } /* No data == nothing to do */ if (!data_size) return; switch (state) { case SPI_STATE_READY_TO_RX: /* Starting a new RX transaction. */ rxbuf_count = 0; state = SPI_STATE_RECEIVING_HEADER; sps_tx_status(EC_SPI_RECEIVING); /* Need a header first (proto v3 only) */ rxbuf_needed = sizeof(*req); break; case SPI_STATE_RECEIVING_HEADER: case SPI_STATE_RECEIVING_BODY: /* still gathering bytes */ break; case SPI_STATE_DISABLED: /* * The master started a transaction but we weren't ready for it. * Tell it we weren't ready, and ignore the incoming data until * the master gives up. */ CPRINTS("SPI not ready (in state %d)", state); sps_tx_status(EC_SPI_NOT_READY); state = SPI_STATE_RX_BAD; return; default: /* Anything else doesn't need us to look at the input */ return; } /* Collect incoming bytes */ if (rxbuf_count + data_size > RXBUF_MAX) goto spi_event_error; memcpy(rxbuf + rxbuf_count, data, data_size); rxbuf_count += data_size; /* Wait until we have enough */ if (rxbuf_count < rxbuf_needed) return; switch (state) { case SPI_STATE_RECEIVING_HEADER: /* Got the header - is it okay? */ if (!req_header_looks_good(req)) goto spi_event_error; /* Yep, now need the body */ state = SPI_STATE_RECEIVING_BODY; rxbuf_needed += req->data_len; /* Still need more bytes? */ if (rxbuf_count < rxbuf_needed) return; /* FALLTHROUGH */ case SPI_STATE_RECEIVING_BODY: /* We have all the data we need. */ state = SPI_STATE_PROCESSING; sps_tx_status(EC_SPI_PROCESSING); /* Hand it off to the host command task for processing */ rx_packet.send_response = cb_send_response_packet; rx_packet.request = rxbuf; rx_packet.request_temp = NULL; rx_packet.request_max = RXBUF_MAX; rx_packet.request_size = rxbuf_count; rx_packet.response = txbuf + 1; /* skip preamble byte */ rx_packet.response_max = TXBUF_MAX; rx_packet.response_size = 0; rx_packet.driver_result = EC_RES_SUCCESS; host_packet_receive(&rx_packet); break; default: /* shouldn't get here */ goto spi_event_error; } return; spi_event_error: /* Error, timeout, or protocol we can't handle. Ignore data. */ sps_tx_status(EC_SPI_RX_BAD_DATA); state = SPI_STATE_RX_BAD; CPRINTS("SPI RX BAD DATA"); CPRINTF("[rxbuf (%d): ", rxbuf_count); for (i = 0; i < rxbuf_count; i++) CPRINTF("%02x ", rxbuf[i]); CPRINTF("]\n"); } static void sps_hc_enable(void) { /* I'm not listening, la la la la la ... */ sps_tx_status(EC_SPI_NOT_READY); /* We are no longer actively processing a transaction */ state = SPI_STATE_DISABLED; /* Ready to receive */ sps_register_rx_handler(SPS_GENERIC_MODE, hc_rx_handler, 0); /* Here we go */ discard_response = 0; state = SPI_STATE_READY_TO_RX; sps_tx_status(EC_SPI_RX_READY); } static void sps_hc_disable(void) { sps_unregister_rx_handler(); /* We are no longer actively processing a transaction */ state = SPI_STATE_DISABLED; } static int sps_get_protocol_info(struct host_cmd_handler_args *args) { struct ec_response_get_protocol_info *r = args->response; r->protocol_versions = (1 << 3); r->max_request_packet_size = RXBUF_MAX; r->max_response_packet_size = TXBUF_MAX; r->flags = 0; args->response_size = sizeof(*r); return EC_SUCCESS; } DECLARE_HOST_COMMAND(EC_CMD_GET_PROTOCOL_INFO, sps_get_protocol_info, EC_VER_MASK(0)); static int command_sps(int argc, char **argv) { if (argc > 1) { if (0 != strcasecmp(argv[1], "off")) return EC_ERROR_PARAM1; sps_hc_disable(); ccprintf("SPS host commands disabled\n"); return EC_SUCCESS; } sps_hc_enable(); ccprintf("SPS host commands enabled\n"); return EC_SUCCESS; } DECLARE_CONSOLE_COMMAND(spshc, command_sps, "[off]", "Enable (default) or disable SPS host commands", NULL);