summaryrefslogtreecommitdiff
path: root/common/i2cs_tpm.c
diff options
context:
space:
mode:
authorVadim Bendebury <vbendeb@google.com>2016-09-13 16:42:12 -0700
committerchrome-bot <chrome-bot@chromium.org>2016-09-15 13:42:16 -0700
commitd50b7699b57b47b6ac3414d6c5b747ca9e1dd0e9 (patch)
tree43449f428339c75634cb170ffbcf3160d8bf283c /common/i2cs_tpm.c
parent76f2f81a9297b99215d9f499fe8f0e593fd2eda7 (diff)
downloadchrome-ec-d50b7699b57b47b6ac3414d6c5b747ca9e1dd0e9.tar.gz
g: generate AP_INT_L pulse after i2cs processing finished
The g i2c slave controller does not support clock stretching, so it is necessary to flow control the AP by some other means. Luckily there is an interrupt line which g can toggle and the AP can watch. This patch adds generating a pulse on the AP interrupt line once g finished processing the i2c transaction. In case of the read transaction the pulse is generated after the data to read is put in the i2cs transmit buffer. BRANCH=none BUG=chrome-os-partner:57338 TEST=with this patch and the AP firmware synchronizing on the interrupt pulse, the TPM initialization succeeds in coreboot and depthcharge. Change-Id: I16c09b59b7d772624baa9d1f5258aaff26f91ff9 Signed-off-by: Vadim Bendebury <vbendeb@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/385256 Reviewed-by: Scott Collyer <scollyer@chromium.org>
Diffstat (limited to 'common/i2cs_tpm.c')
-rw-r--r--common/i2cs_tpm.c159
1 files changed, 89 insertions, 70 deletions
diff --git a/common/i2cs_tpm.c b/common/i2cs_tpm.c
index b466e10e27..b9ab003b16 100644
--- a/common/i2cs_tpm.c
+++ b/common/i2cs_tpm.c
@@ -5,6 +5,7 @@
#include "common.h"
#include "console.h"
+#include "gpio.h"
#include "hooks.h"
#include "i2cs.h"
#include "registers.h"
@@ -77,6 +78,84 @@ static const struct i2c_tpm_reg_map i2c_to_tpm[] = {
{0xf, 0, 0xf90}, /* TPM_FW_VER */
};
+static void process_read_access(uint16_t reg_size,
+ uint16_t tpm_reg, uint8_t *data)
+{
+ int i;
+ uint8_t reg_value[4];
+
+ /*
+ * The master wants to read the register, read the value and pass it
+ * to the controller.
+ */
+ if (reg_size == 1 || reg_size == 4) {
+ /* Always read regsize number of bytes */
+ tpm_register_get(tpm_reg, reg_value, reg_size);
+ for (i = 0; i < reg_size; i++)
+ i2cs_post_read_data(reg_value[i]);
+ return;
+ }
+
+ /*
+ * FIFO accesses do not require endianness conversion, but to find out
+ * how many bytes to read we need to consult the burst size field of
+ * the tpm status register.
+ */
+ reg_size = tpm_get_burst_size();
+
+ /*
+ * For TPM fifo reads, if there is already data pending in the I2CS hw
+ * fifo, then don't read any more TPM fifo data until the I2CS hw fifo
+ * has been fully drained.
+ *
+ * The Host will only read only enough data to extract the full TPM
+ * message length. However, Cr50 will fill the I2CS hw fifo with
+ * 'burstsize' amount of bytes. The 2nd fifo access for a given TPM
+ * repsonse by the Host will extract the queued up data. Following
+ * this, the Host will then read 'burstcount' amount of data for
+ * subsequent fifo accesses until the response has been fully read.
+ */
+ if (i2cs_get_read_fifo_buffer_depth())
+ /* Data is already in the queue, just return */
+ return;
+
+ /*
+ * Now, this is a hack, but we are short on SRAM, so let's reuse the
+ * receive buffer for the FIFO data sotrage. We know that the ISR has
+ * a 64 byte buffer were it moves received data.
+ */
+ /* Back pointer up by one to point to beginning of buffer */
+ data -= 1;
+ tpm_register_get(tpm_reg, data, reg_size);
+ /* Transfer TPM fifo data to the I2CS HW fifo */
+ i2cs_post_read_fill_fifo(data, reg_size);
+}
+
+static void process_write_access(uint16_t reg_size, uint16_t tpm_reg,
+ uint8_t *data, size_t i2cs_data_size)
+{
+ /* This is an actual write request. */
+
+ /*
+ * If reg_size is 0, then this is a fifo register write. Send the stream
+ * down directly
+ */
+ if (reg_size == 0) {
+ tpm_register_put(tpm_reg, data, i2cs_data_size);
+ return;
+ }
+
+ if (i2cs_data_size != reg_size) {
+ CPRINTF("%s: data size mismatch for reg 0x%x "
+ "(rx %d, need %d)\n", __func__, tpm_reg,
+ i2cs_data_size, reg_size);
+ return;
+ }
+
+ /* Write the data to the appropriate TPM register */
+ tpm_register_put(tpm_reg, data, reg_size);
+}
+
static void wr_complete_handler(void *i2cs_data, size_t i2cs_data_size)
{
size_t i;
@@ -120,79 +199,19 @@ static void wr_complete_handler(void *i2cs_data, size_t i2cs_data_size)
i2cs_data_size--;
data++;
- if (!i2cs_data_size) {
- uint8_t reg_value[4];
-
- /*
- * The master wants to read the register, read the value and
- * pass it to the controller.
- */
- if (reg_size == 1 || reg_size == 4) {
- /* Always read regsize number of bytes */
- tpm_register_get(tpm_reg, reg_value, reg_size);
- for (i = 0; i < reg_size; i++)
- i2cs_post_read_data(reg_value[i]);
- return;
- }
-
- /*
- * FIFO accesses do not require endianness conversion, but to
- * find out how many bytes to read we need to consult the
- * burst size field of the tpm status register.
- */
- reg_size = tpm_get_burst_size();
-
- /* For TPM fifo reads, if there is already data pending in the
- * I2CS hw fifo, then don't read any more TPM fifo data until
- * the I2CS hw fifo has been fully drained.
- *
- * The Host will only read only enough data to extract the full
- * TPM message length. However, Cr50 will fill the I2CS hw fifo
- * with 'burstsize' amount of bytes. The 2nd fifo access for a
- * given TPM repsonse by the Host will extract the queued up
- * data. Following this, the Host will then read 'burstcount'
- * amount of data for subsequent fifo accesses until the
- * response has been fully read.
- *
- */
- if (i2cs_get_read_fifo_buffer_depth())
- /* Data is already in the queue, just return */
- return;
-
- /*
- * Now, this is a hack, but we are short on SRAM, so let's
- * reuse the receive buffer for the FIFO data sotrage. We know
- * that the ISR has a 64 byte buffer were it moves received
- * data.
- */
- /* Back pointer up by one to point to beginning of buffer */
- data -= 1;
- tpm_register_get(tpm_reg, data, reg_size);
- /* Transfer TPM fifo data to the I2CS HW fifo */
- i2cs_post_read_fill_fifo(data, reg_size);
- return;
- }
-
- /* This is an actual write request. */
+ if (!i2cs_data_size)
+ process_read_access(reg_size, tpm_reg, data);
+ else
+ process_write_access(reg_size, tpm_reg,
+ data, i2cs_data_size);
/*
- * If reg_size is 0, then this is a fifo register write. Send the stream
- * down directly
+ * Since cr50 does not provide i2c clock stretching, we need some
+ * onther means of flow controlling the host. Let's generate a pulse
+ * on the AP interrupt line for that.
*/
- if (reg_size == 0) {
- tpm_register_put(tpm_reg, data, i2cs_data_size);
- return;
- }
-
- if (i2cs_data_size != reg_size) {
- CPRINTF("%s: data size mismatch for reg 0x%x "
- "(rx %d, need %d)\n", __func__, tpm_reg,
- i2cs_data_size, reg_size);
- return;
- }
-
- /* Write the data to the appropriate TPM register */
- tpm_register_put(tpm_reg, data, reg_size);
+ gpio_set_level(GPIO_INT_AP_L, 0);
+ gpio_set_level(GPIO_INT_AP_L, 1);
}
static void i2cs_tpm_init(void)