/* The header byte, which follows the preamble */ #define EC_MSG_HEADER 0xec
/* * Number of EC preamble bytes we read at a time. Since it takes * about 400-500us for the EC to respond there is not a lot of * point in tuning this. If the EC could respond faster then * we could increase this so that might expect the preamble and * message to occur in a single transaction. However, the maximum * SPI transfer size is 256 bytes, so at 5MHz we need a response * time of perhaps <320us (200 bytes / 1600 bits).
*/ #define EC_MSG_PREAMBLE_COUNT 32
/* * Allow for a long time for the EC to respond. We support i2c * tunneling and support fairly long messages for the tunnel (249 * bytes long at the moment). If we're talking to a 100 kHz device * on the other end and need to transfer ~256 bytes, then we need: * 10 us/bit * ~10 bits/byte * ~256 bytes = ~25ms * * We'll wait 8 times that to handle clock stretching and other * paranoia. Note that some battery gas gauge ICs claim to have a * clock stretch of 144ms in rare situations. That's incentive for * not directly passing i2c through, but it's too late for that for * existing hardware. * * It's pretty unlikely that we'll really see a 249 byte tunnel in * anything other than testing. If this was more common we might * consider having slow commands like this require a GET_STATUS * wait loop. The 'flash write' command would be another candidate * for this, clocking in at 2-3ms.
*/ #define EC_MSG_DEADLINE_MS 200
/* * Time between raising the SPI chip select (for the end of a * transaction) and dropping it again (for the next transaction). * If we go too fast, the EC will miss the transaction. We know that we * need at least 70 us with the 16 MHz STM32 EC, so go with 200 us to be * safe.
*/ #define EC_SPI_RECOVERY_TIME_NS (200 * 1000)
/** * struct cros_ec_spi - information about a SPI-connected EC * * @spi: SPI device we are connected to * @last_transfer_ns: time that we last finished a transfer. * @start_of_msg_delay: used to set the delay_usecs on the spi_transfer that * is sent when we want to turn on CS at the start of a transaction. * @end_of_msg_delay: used to set the delay_usecs on the spi_transfer that * is sent when we want to turn off CS at the end of a transaction. * @high_pri_worker: Used to schedule high priority work.
*/ struct cros_ec_spi { struct spi_device *spi;
s64 last_transfer_ns; unsignedint start_of_msg_delay; unsignedint end_of_msg_delay; struct kthread_worker *high_pri_worker;
};
/** * struct cros_ec_xfer_work_params - params for our high priority workers * * @work: The work_struct needed to queue work * @fn: The function to use to transfer * @ec_dev: ChromeOS EC device * @ec_msg: Message to transfer * @ret: The return value of the function
*/
/* * Turn off CS, possibly adding a delay to ensure the rising edge * doesn't come too soon after the end of the data.
*/
spi_message_init(&msg);
memset(&trans, 0, sizeof(trans));
trans.delay.value = ec_spi->end_of_msg_delay;
trans.delay.unit = SPI_DELAY_UNIT_USECS;
spi_message_add_tail(&trans, &msg);
/** * receive_n_bytes - receive n bytes from the EC. * * Assumes buf is a pointer into the ec_dev->din buffer * * @ec_dev: ChromeOS EC device. * @buf: Pointer to the buffer receiving the data. * @n: Number of bytes received.
*/ staticint receive_n_bytes(struct cros_ec_device *ec_dev, u8 *buf, int n)
{ struct cros_ec_spi *ec_spi = ec_dev->priv; struct spi_transfer trans; struct spi_message msg; int ret;
if (buf - ec_dev->din + n > ec_dev->din_size) return -EINVAL;
spi_message_init(&msg);
spi_message_add_tail(&trans, &msg);
ret = spi_sync_locked(ec_spi->spi, &msg); if (ret < 0)
dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret);
return ret;
}
/** * cros_ec_spi_receive_packet - Receive a packet from the EC. * * This function has two phases: reading the preamble bytes (since if we read * data from the EC before it is ready to send, we just get preamble) and * reading the actual message. * * The received data is placed into ec_dev->din. * * @ec_dev: ChromeOS EC device * @need_len: Number of message bytes we need to read
*/ staticint cros_ec_spi_receive_packet(struct cros_ec_device *ec_dev, int need_len)
{ struct ec_host_response *response;
u8 *ptr, *end; int ret; unsignedlong deadline; int todo;
if (ec_dev->din_size < EC_MSG_PREAMBLE_COUNT) return -EINVAL;
/* Receive data until we see the header byte */
deadline = jiffies + msecs_to_jiffies(EC_MSG_DEADLINE_MS); while (true) { unsignedlong start_jiffies = jiffies;
ret = receive_n_bytes(ec_dev,
ec_dev->din,
EC_MSG_PREAMBLE_COUNT); if (ret < 0) return ret;
ptr = ec_dev->din; for (end = ptr + EC_MSG_PREAMBLE_COUNT; ptr != end; ptr++) { if (*ptr == EC_SPI_FRAME_START) {
dev_dbg(ec_dev->dev, "msg found at %zd\n",
ptr - ec_dev->din); break;
}
} if (ptr != end) break;
/* * Use the time at the start of the loop as a timeout. This * gives us one last shot at getting the transfer and is useful * in case we got context switched out for a while.
*/ if (time_after(start_jiffies, deadline)) {
dev_warn(ec_dev->dev, "EC failed to respond in time\n"); return -ETIMEDOUT;
}
}
/* * ptr now points to the header byte. Copy any valid data to the * start of our buffer
*/
todo = end - ++ptr;
todo = min(todo, need_len);
memmove(ec_dev->din, ptr, todo);
ptr = ec_dev->din + todo;
dev_dbg(ec_dev->dev, "need %d, got %d bytes from preamble\n",
need_len, todo);
need_len -= todo;
/* If the entire response struct wasn't read, get the rest of it. */ if (todo < sizeof(*response)) {
ret = receive_n_bytes(ec_dev, ptr, sizeof(*response) - todo); if (ret < 0) return -EBADMSG;
ptr += (sizeof(*response) - todo);
todo = sizeof(*response);
}
/* Abort if data_len is too large. */ if (response->data_len > ec_dev->din_size) return -EMSGSIZE;
/* Receive data until we have it all */ while (need_len > 0) { /* * We can't support transfers larger than the SPI FIFO size * unless we have DMA. We don't have DMA on the ISP SPI ports * for Exynos. We need a way of asking SPI driver for * maximum-supported transfer size.
*/
todo = min(need_len, 256);
dev_dbg(ec_dev->dev, "loop, todo=%d, need_len=%d, ptr=%zd\n",
todo, need_len, ptr - ec_dev->din);
ret = receive_n_bytes(ec_dev, ptr, todo); if (ret < 0) return ret;
/** * cros_ec_spi_receive_response - Receive a response from the EC. * * This function has two phases: reading the preamble bytes (since if we read * data from the EC before it is ready to send, we just get preamble) and * reading the actual message. * * The received data is placed into ec_dev->din. * * @ec_dev: ChromeOS EC device * @need_len: Number of message bytes we need to read
*/ staticint cros_ec_spi_receive_response(struct cros_ec_device *ec_dev, int need_len)
{
u8 *ptr, *end; int ret; unsignedlong deadline; int todo;
if (ec_dev->din_size < EC_MSG_PREAMBLE_COUNT) return -EINVAL;
/* Receive data until we see the header byte */
deadline = jiffies + msecs_to_jiffies(EC_MSG_DEADLINE_MS); while (true) { unsignedlong start_jiffies = jiffies;
ret = receive_n_bytes(ec_dev,
ec_dev->din,
EC_MSG_PREAMBLE_COUNT); if (ret < 0) return ret;
ptr = ec_dev->din; for (end = ptr + EC_MSG_PREAMBLE_COUNT; ptr != end; ptr++) { if (*ptr == EC_SPI_FRAME_START) {
dev_dbg(ec_dev->dev, "msg found at %zd\n",
ptr - ec_dev->din); break;
}
} if (ptr != end) break;
/* * Use the time at the start of the loop as a timeout. This * gives us one last shot at getting the transfer and is useful * in case we got context switched out for a while.
*/ if (time_after(start_jiffies, deadline)) {
dev_warn(ec_dev->dev, "EC failed to respond in time\n"); return -ETIMEDOUT;
}
}
/* * ptr now points to the header byte. Copy any valid data to the * start of our buffer
*/
todo = end - ++ptr;
todo = min(todo, need_len);
memmove(ec_dev->din, ptr, todo);
ptr = ec_dev->din + todo;
dev_dbg(ec_dev->dev, "need %d, got %d bytes from preamble\n",
need_len, todo);
need_len -= todo;
/* Receive data until we have it all */ while (need_len > 0) { /* * We can't support transfers larger than the SPI FIFO size * unless we have DMA. We don't have DMA on the ISP SPI ports * for Exynos. We need a way of asking SPI driver for * maximum-supported transfer size.
*/
todo = min(need_len, 256);
dev_dbg(ec_dev->dev, "loop, todo=%d, need_len=%d, ptr=%zd\n",
todo, need_len, ptr - ec_dev->din);
ret = receive_n_bytes(ec_dev, ptr, todo); if (ret < 0) return ret;
/** * do_cros_ec_pkt_xfer_spi - Transfer a packet over SPI and receive the reply * * @ec_dev: ChromeOS EC device * @ec_msg: Message to transfer
*/ staticint do_cros_ec_pkt_xfer_spi(struct cros_ec_device *ec_dev, struct cros_ec_command *ec_msg)
{ struct ec_host_response *response; struct cros_ec_spi *ec_spi = ec_dev->priv; struct spi_transfer trans, trans_delay; struct spi_message msg; int i, len;
u8 *ptr;
u8 *rx_buf;
u8 sum;
u8 rx_byte; int ret = 0, final_ret; unsignedlong delay;
len = cros_ec_prepare_tx(ec_dev, ec_msg); if (len < 0) return len;
dev_dbg(ec_dev->dev, "prepared, len=%d\n", len);
/* If it's too soon to do another transaction, wait */
delay = ktime_get_ns() - ec_spi->last_transfer_ns; if (delay < EC_SPI_RECOVERY_TIME_NS)
ndelay(EC_SPI_RECOVERY_TIME_NS - delay);
rx_buf = kzalloc(len, GFP_KERNEL); if (!rx_buf) return -ENOMEM;
spi_bus_lock(ec_spi->spi->controller);
/* * Leave a gap between CS assertion and clocking of data to allow the * EC time to wakeup.
*/
spi_message_init(&msg); if (ec_spi->start_of_msg_delay) {
memset(&trans_delay, 0, sizeof(trans_delay));
trans_delay.delay.value = ec_spi->start_of_msg_delay;
trans_delay.delay.unit = SPI_DELAY_UNIT_USECS;
spi_message_add_tail(&trans_delay, &msg);
}
/* Get the response */ if (!ret) { /* Verify that EC can process command */ for (i = 0; i < len; i++) {
rx_byte = rx_buf[i]; /* * Seeing the PAST_END, RX_BAD_DATA, or NOT_READY * markers are all signs that the EC didn't fully * receive our command. e.g., if the EC is flashing * itself, it can't respond to any commands and instead * clocks out EC_SPI_PAST_END from its SPI hardware * buffer. Similar occurrences can happen if the AP is * too slow to clock out data after asserting CS -- the * EC will abort and fill its buffer with * EC_SPI_RX_BAD_DATA. * * In all cases, these errors should be safe to retry. * Report -EAGAIN and let the caller decide what to do * about that.
*/ if (rx_byte == EC_SPI_PAST_END ||
rx_byte == EC_SPI_RX_BAD_DATA ||
rx_byte == EC_SPI_NOT_READY) {
ret = -EAGAIN; break;
}
}
}
if (!ret)
ret = cros_ec_spi_receive_packet(ec_dev,
ec_msg->insize + sizeof(*response)); elseif (ret != -EAGAIN)
dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret);
ret = cros_ec_check_result(ec_dev, ec_msg); if (ret) gotoexit;
len = response->data_len;
sum = 0; if (len > ec_msg->insize) {
dev_err(ec_dev->dev, "packet too long (%d bytes, expected %d)",
len, ec_msg->insize);
ret = -EMSGSIZE; gotoexit;
}
for (i = 0; i < sizeof(*response); i++)
sum += ptr[i];
/* copy response packet payload and compute checksum */
memcpy(ec_msg->data, ptr + sizeof(*response), len); for (i = 0; i < len; i++)
sum += ec_msg->data[i];
if (sum) {
dev_err(ec_dev->dev, "bad packet checksum, calculated %x\n",
sum);
ret = -EBADMSG; gotoexit;
}
ret = len; exit:
kfree(rx_buf); if (ec_msg->command == EC_CMD_REBOOT_EC)
msleep(EC_REBOOT_DELAY_MS);
return ret;
}
/** * do_cros_ec_cmd_xfer_spi - Transfer a message over SPI and receive the reply * * @ec_dev: ChromeOS EC device * @ec_msg: Message to transfer
*/ staticint do_cros_ec_cmd_xfer_spi(struct cros_ec_device *ec_dev, struct cros_ec_command *ec_msg)
{ struct cros_ec_spi *ec_spi = ec_dev->priv; struct spi_transfer trans; struct spi_message msg; int i, len;
u8 *ptr;
u8 *rx_buf;
u8 rx_byte; int sum; int ret = 0, final_ret; unsignedlong delay;
len = cros_ec_prepare_tx(ec_dev, ec_msg); if (len < 0) return len;
dev_dbg(ec_dev->dev, "prepared, len=%d\n", len);
/* If it's too soon to do another transaction, wait */
delay = ktime_get_ns() - ec_spi->last_transfer_ns; if (delay < EC_SPI_RECOVERY_TIME_NS)
ndelay(EC_SPI_RECOVERY_TIME_NS - delay);
rx_buf = kzalloc(len, GFP_KERNEL); if (!rx_buf) return -ENOMEM;
/* Get the response */ if (!ret) { /* Verify that EC can process command */ for (i = 0; i < len; i++) {
rx_byte = rx_buf[i]; /* See comments in cros_ec_pkt_xfer_spi() */ if (rx_byte == EC_SPI_PAST_END ||
rx_byte == EC_SPI_RX_BAD_DATA ||
rx_byte == EC_SPI_NOT_READY) {
ret = -EAGAIN; break;
}
}
}
if (!ret)
ret = cros_ec_spi_receive_response(ec_dev,
ec_msg->insize + EC_MSG_TX_PROTO_BYTES); elseif (ret != -EAGAIN)
dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret);
final_ret = terminate_request(ec_dev);
spi_bus_unlock(ec_spi->spi->controller);
if (!ret)
ret = final_ret; if (ret < 0) gotoexit;
ptr = ec_dev->din;
/* check response error code */
ec_msg->result = ptr[0];
ret = cros_ec_check_result(ec_dev, ec_msg); if (ret) gotoexit;
len = ptr[1];
sum = ptr[0] + ptr[1]; if (len > ec_msg->insize) {
dev_err(ec_dev->dev, "packet too long (%d bytes, expected %d)",
len, ec_msg->insize);
ret = -ENOSPC; gotoexit;
}
/* copy response packet payload and compute checksum */ for (i = 0; i < len; i++) {
sum += ptr[i + 2]; if (ec_msg->insize)
ec_msg->data[i] = ptr[i + 2];
}
sum &= 0xff;
/* * This looks a bit ridiculous. Why do the work on a * different thread if we're just going to block waiting for * the thread to finish? The key here is that the thread is * running at high priority but the calling context might not * be. We need to be at high priority to avoid getting * context switched out for too long and the EC giving up on * the transfer.
*/
kthread_queue_work(ec_spi->high_pri_worker, ¶ms.work);
kthread_flush_work(¶ms.work);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.