/** * cros_ec_cmd_xfer_spi - Transfer a message over SPI and receive the reply * * @ec_dev: ChromeOS EC device * @ec_msg: Message to transfer */ static int 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; int sum; int ret = 0, final_ret; /* * We have the shared ec_dev buffer plus we do lots of separate spi_sync * calls, so we need to make sure only one person is using this at a * time. */ mutex_lock(&ec_spi->lock); len = cros_ec_prepare_tx(ec_dev, ec_msg); dev_dbg(ec_dev->dev, "prepared, len=%d\n", len); /* If it's too soon to do another transaction, wait */ if (ec_spi->last_transfer_ns) { unsigned long delay; /* The delay completed so far */ delay = ktime_get_ns() - ec_spi->last_transfer_ns; if (delay < EC_SPI_RECOVERY_TIME_NS) ndelay(EC_SPI_RECOVERY_TIME_NS - delay); } /* Transmit phase - send our message */ debug_packet(ec_dev->dev, "out", ec_dev->dout, len); memset(&trans, 0, sizeof(trans)); trans.tx_buf = ec_dev->dout; trans.len = len; trans.cs_change = 1; spi_message_init(&msg); spi_message_add_tail(&trans, &msg); ret = spi_sync(ec_spi->spi, &msg); /* Get the response */ if (!ret) { ret = cros_ec_spi_receive_response(ec_dev, ec_msg->insize + EC_MSG_TX_PROTO_BYTES); } else { dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret); } /* * 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_usecs = ec_spi->end_of_msg_delay; spi_message_add_tail(&trans, &msg); final_ret = spi_sync(ec_spi->spi, &msg); ec_spi->last_transfer_ns = ktime_get_ns(); if (!ret) ret = final_ret; if (ret < 0) { dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret); goto exit; } ptr = ec_dev->din; /* check response error code */ ec_msg->result = ptr[0]; ret = cros_ec_check_result(ec_dev, ec_msg); if (ret) goto exit; 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; goto exit; } /* copy response packet payload and compute checksum */ for (i = 0; i < len; i++) { sum += ptr[i + 2]; if (ec_msg->insize) ec_msg->indata[i] = ptr[i + 2]; } sum &= 0xff; debug_packet(ec_dev->dev, "in", ptr, len + 3); if (sum != ptr[len + 2]) { dev_err(ec_dev->dev, "bad packet checksum, expected %02x, got %02x\n", sum, ptr[len + 2]); ret = -EBADMSG; goto exit; } ret = len; exit: mutex_unlock(&ec_spi->lock); return ret; }
/** * cros_ec_cmd_xfer_spi - Transfer a message over SPI and receive the reply * * @ec_dev: ChromeOS EC device * @ec_msg: Message to transfer */ static int 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; int sum; int ret = 0, final_ret; len = cros_ec_prepare_tx(ec_dev, ec_msg); dev_dbg(ec_dev->dev, "prepared, len=%d\n", len); /* If it's too soon to do another transaction, wait */ if (ec_spi->last_transfer_ns) { unsigned long delay; /* The delay completed so far */ 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) { ret = -ENOMEM; goto exit; } /* Transmit phase - send our message */ debug_packet(ec_dev->dev, "out", ec_dev->dout, len); memset(&trans, 0, sizeof(trans)); trans.tx_buf = ec_dev->dout; trans.rx_buf = rx_buf; trans.len = len; trans.cs_change = 1; spi_message_init(&msg); spi_message_add_tail(&trans, &msg); ret = spi_sync(ec_spi->spi, &msg); /* Get the response */ if (!ret) { /* Verify that EC can process command */ for (i = 0; i < len; i++) { switch (rx_buf[i]) { case EC_SPI_PAST_END: case EC_SPI_RX_BAD_DATA: case EC_SPI_NOT_READY: ret = -EAGAIN; ec_msg->result = EC_RES_IN_PROGRESS; default: break; } if (ret) break; } if (!ret) ret = cros_ec_spi_receive_response(ec_dev, ec_msg->insize + EC_MSG_TX_PROTO_BYTES); } else { dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret); } final_ret = terminate_request(ec_dev); if (!ret) ret = final_ret; if (ret < 0) goto exit; ptr = ec_dev->din; /* check response error code */ ec_msg->result = ptr[0]; ret = cros_ec_check_result(ec_dev, ec_msg); if (ret) goto exit; 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; goto exit; } /* 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; debug_packet(ec_dev->dev, "in", ptr, len + 3); if (sum != ptr[len + 2]) { dev_err(ec_dev->dev, "bad packet checksum, expected %02x, got %02x\n", sum, ptr[len + 2]); ret = -EBADMSG; goto exit; } ret = len; exit: kfree(rx_buf); if (ec_msg->command == EC_CMD_REBOOT_EC) msleep(EC_REBOOT_DELAY_MS); return ret; }
/** * cros_ec_command_spi_xfer - Transfer a message over SPI and receive the reply * * @ec_dev: ChromeOS EC device * @ec_msg: Message to transfer */ static int cros_ec_command_spi_xfer(struct cros_ec_device *ec_dev, struct cros_ec_msg *ec_msg) { struct cros_ec_spi *ec_spi = ec_dev->priv; struct spi_transfer trans; struct spi_message msg; int i, len; u8 *ptr; int sum; int ret = 0, final_ret; struct timespec ts; len = cros_ec_prepare_tx(ec_dev, ec_msg); dev_dbg(ec_dev->dev, "prepared, len=%d\n", len); /* If it's too soon to do another transaction, wait */ if (ec_spi->last_transfer_ns) { struct timespec ts; unsigned long delay; /* The delay completed so far */ ktime_get_ts(&ts); delay = timespec_to_ns(&ts) - ec_spi->last_transfer_ns; if (delay < EC_SPI_RECOVERY_TIME_NS) ndelay(delay); } /* Transmit phase - send our message */ debug_packet(ec_dev->dev, "out", ec_dev->dout, len); memset(&trans, '\0', sizeof(trans)); trans.tx_buf = ec_dev->dout; trans.len = len; trans.cs_change = 1; spi_message_init(&msg); spi_message_add_tail(&trans, &msg); ret = spi_sync(ec_spi->spi, &msg); /* Get the response */ if (!ret) { ret = cros_ec_spi_receive_response(ec_dev, ec_msg->in_len + EC_MSG_TX_PROTO_BYTES); } else { dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret); } /* turn off CS */ spi_message_init(&msg); final_ret = spi_sync(ec_spi->spi, &msg); ktime_get_ts(&ts); ec_spi->last_transfer_ns = timespec_to_ns(&ts); if (!ret) ret = final_ret; if (ret < 0) { dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret); return ret; } /* check response error code */ ptr = ec_dev->din; if (ptr[0]) { dev_warn(ec_dev->dev, "command 0x%02x returned an error %d\n", ec_msg->cmd, ptr[0]); debug_packet(ec_dev->dev, "in_err", ptr, len); return -EINVAL; } len = ptr[1]; sum = ptr[0] + ptr[1]; if (len > ec_msg->in_len) { dev_err(ec_dev->dev, "packet too long (%d bytes, expected %d)", len, ec_msg->in_len); return -ENOSPC; } /* copy response packet payload and compute checksum */ for (i = 0; i < len; i++) { sum += ptr[i + 2]; if (ec_msg->in_len) ec_msg->in_buf[i] = ptr[i + 2]; } sum &= 0xff; debug_packet(ec_dev->dev, "in", ptr, len + 3); if (sum != ptr[len + 2]) { dev_err(ec_dev->dev, "bad packet checksum, expected %02x, got %02x\n", sum, ptr[len + 2]); return -EBADMSG; } return 0; }
/** * cros_ec_pkt_xfer_spi - Transfer a packet over SPI and receive the reply * * @ec_dev: ChromeOS EC device * @ec_msg: Message to transfer */ static int cros_ec_pkt_xfer_spi(struct cros_ec_device *ec_dev, struct cros_ec_command *ec_msg) { struct ec_host_request *request; 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; int ret = 0, final_ret; len = cros_ec_prepare_tx(ec_dev, ec_msg); request = (struct ec_host_request *)ec_dev->dout; dev_dbg(ec_dev->dev, "prepared, len=%d\n", len); /* If it's too soon to do another transaction, wait */ if (ec_spi->last_transfer_ns) { unsigned long delay; /* The delay completed so far */ 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) { ret = -ENOMEM; goto exit; } /* * 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_usecs = ec_spi->start_of_msg_delay; spi_message_add_tail(&trans_delay, &msg); } /* Transmit phase - send our message */ memset(&trans, 0, sizeof(trans)); trans.tx_buf = ec_dev->dout; trans.rx_buf = rx_buf; trans.len = len; trans.cs_change = 1; spi_message_add_tail(&trans, &msg); ret = spi_sync(ec_spi->spi, &msg); /* Get the response */ if (!ret) { /* Verify that EC can process command */ for (i = 0; i < len; i++) { switch (rx_buf[i]) { case EC_SPI_PAST_END: case EC_SPI_RX_BAD_DATA: case EC_SPI_NOT_READY: ret = -EAGAIN; ec_msg->result = EC_RES_IN_PROGRESS; default: break; } if (ret) break; } if (!ret) ret = cros_ec_spi_receive_packet(ec_dev, ec_msg->insize + sizeof(*response)); } else { dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret); } final_ret = terminate_request(ec_dev); if (!ret) ret = final_ret; if (ret < 0) goto exit; ptr = ec_dev->din; /* check response error code */ response = (struct ec_host_response *)ptr; ec_msg->result = response->result; ret = cros_ec_check_result(ec_dev, ec_msg); if (ret) goto exit; 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; goto exit; } 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; goto exit; } ret = len; exit: kfree(rx_buf); if (ec_msg->command == EC_CMD_REBOOT_EC) msleep(EC_REBOOT_DELAY_MS); return ret; }
static int cros_ec_pkt_xfer_lpc(struct cros_ec_device *ec, struct cros_ec_command *msg) { struct ec_host_request *request; struct ec_host_response response; u8 sum; int ret = 0; u8 *dout; ret = cros_ec_prepare_tx(ec, msg); /* Write buffer */ cros_ec_lpc_write_bytes(EC_LPC_ADDR_HOST_PACKET, ret, ec->dout); request = (struct ec_host_request *)ec->dout; /* Here we go */ sum = EC_COMMAND_PROTOCOL_3; cros_ec_lpc_write_bytes(EC_LPC_ADDR_HOST_CMD, 1, &sum); if (ec_response_timed_out()) { dev_warn(ec->dev, "EC responsed timed out\n"); ret = -EIO; goto done; } /* Check result */ msg->result = cros_ec_lpc_read_bytes(EC_LPC_ADDR_HOST_DATA, 1, &sum); ret = cros_ec_check_result(ec, msg); if (ret) goto done; /* Read back response */ dout = (u8 *)&response; sum = cros_ec_lpc_read_bytes(EC_LPC_ADDR_HOST_PACKET, sizeof(response), dout); msg->result = response.result; if (response.data_len > msg->insize) { dev_err(ec->dev, "packet too long (%d bytes, expected %d)", response.data_len, msg->insize); ret = -EMSGSIZE; goto done; } /* Read response and process checksum */ sum += cros_ec_lpc_read_bytes(EC_LPC_ADDR_HOST_PACKET + sizeof(response), response.data_len, msg->data); if (sum) { dev_err(ec->dev, "bad packet checksum %02x\n", response.checksum); ret = -EBADMSG; goto done; } /* Return actual amount of data received */ ret = response.data_len; done: return ret; }