summaryrefslogtreecommitdiff
path: root/chip/g/sps.c
diff options
context:
space:
mode:
Diffstat (limited to 'chip/g/sps.c')
-rw-r--r--chip/g/sps.c110
1 files changed, 84 insertions, 26 deletions
diff --git a/chip/g/sps.c b/chip/g/sps.c
index a25e379bb6..76b4d4d462 100644
--- a/chip/g/sps.c
+++ b/chip/g/sps.c
@@ -5,6 +5,7 @@
#include "common.h"
#include "console.h"
+#include "gpio.h"
#include "hooks.h"
#include "pmu.h"
#include "registers.h"
@@ -44,7 +45,7 @@
/*
* Hardware pointers use one extra bit, which means that indexing FIFO and
- * values written into the pointers have to have dfferent sizes. Tracked under
+ * values written into the pointers have to have different sizes. Tracked under
* http://b/20894690
*/
#define SPS_FIFO_PTR_MASK ((SPS_FIFO_MASK << 1) | 1)
@@ -59,6 +60,9 @@ static uint32_t sps_tx_count, sps_rx_count, tx_empty_count, max_rx_batch;
#define CPUTS(outstr) cputs(CC_SPS, outstr)
#define CPRINTS(format, args...) cprints(CC_SPS, format, ## args)
+/* Flag indicating if there has been any data received while CS was asserted. */
+static uint8_t seen_data;
+
void sps_tx_status(uint8_t byte)
{
GREG32(SPS, DUMMY_WORD) = byte;
@@ -118,7 +122,7 @@ int sps_transmit(uint8_t *data, size_t data_size)
/*
* CR50 SPS controller does not allow byte
* accesses for writes into the FIFO, so read
- * modify/write is requred. Tracked uder
+ * modify/write is required. Tracked under
* http://b/20894727
*/
bit_shift = 8 * (wptr & 3);
@@ -151,7 +155,7 @@ int sps_transmit(uint8_t *data, size_t data_size)
/*
* Start TX if necessary. This happens after FIFO is primed, which
- * helps aleviate TX underrun problems but introduces delay before
+ * helps alleviate TX underrun problems but introduces delay before
* data starts coming out.
*/
if (!GREAD_FIELD(SPS, FIFO_CTRL, TXFIFO_EN))
@@ -161,6 +165,15 @@ int sps_transmit(uint8_t *data, size_t data_size)
return bytes_sent;
}
+static int sps_cs_asserted(void)
+{
+ /*
+ * Read the current value on the SPS CS line and return the iversion
+ * of it (CS is active low).
+ */
+ return !GREAD_FIELD(SPS, VAL, CSB);
+}
+
/** Configure the data transmission format
*
* @param mode Clock polarity and phase mode (0 - 3)
@@ -181,6 +194,20 @@ static void sps_configure(enum sps_mode mode, enum spi_clock_mode clk_mode,
/* xfer 0xff when tx fifo is empty */
GREG32(SPS, DUMMY_WORD) = GC_SPS_DUMMY_WORD_DEFAULT;
+ if (sps_cs_asserted()) {
+ /*
+ * Reset while the external controller is mid SPI
+ * transaction.
+ */
+ ccprintf("%s: reset while CS active\n", __func__);
+ /*
+ * Wait for external controller to deassert CS before
+ * continuing.
+ */
+ while (sps_cs_asserted())
+ ;
+ }
+
/* [5,4,3] [2,1,0]
* RX{DIS, EN, RST} TX{DIS, EN, RST}
*/
@@ -197,6 +224,8 @@ static void sps_configure(enum sps_mode mode, enum spi_clock_mode clk_mode,
GWRITE_FIELD(SPS, ICTRL, RXFIFO_LVL, 1);
+ seen_data = 0;
+
/* Use CS_DEASSERT to retrieve all remaining bytes from RX FIFO. */
GWRITE_FIELD(SPS, ISTATE_CLR, CS_DEASSERT, 1);
GWRITE_FIELD(SPS, ICTRL, CS_DEASSERT, 1);
@@ -211,8 +240,11 @@ static rx_handler_f sps_rx_handler;
int sps_register_rx_handler(enum sps_mode mode, rx_handler_f rx_handler,
unsigned rx_fifo_threshold)
{
- if (sps_rx_handler)
- return -1;
+ task_disable_irq(GC_IRQNUM_SPS0_RXFIFO_LVL_INTR);
+ task_disable_irq(GC_IRQNUM_SPS0_CS_DEASSERT_INTR);
+
+ if (!rx_handler)
+ return 0;
if (!rx_fifo_threshold)
rx_fifo_threshold = 8; /* This is a sensible default. */
@@ -225,25 +257,13 @@ int sps_register_rx_handler(enum sps_mode mode, rx_handler_f rx_handler,
return 0;
}
-int sps_unregister_rx_handler(void)
-{
- if (!sps_rx_handler)
- return -1;
-
- task_disable_irq(GC_IRQNUM_SPS0_RXFIFO_LVL_INTR);
- task_disable_irq(GC_IRQNUM_SPS0_CS_DEASSERT_INTR);
-
- sps_rx_handler = NULL;
- return 0;
-}
-
static void sps_init(void)
{
/*
* Check to see if slave SPI interface is required by the board before
* initializing it. If SPI option is not set, then just return.
*/
- if (!(system_get_board_properties() & BOARD_SLAVE_CONFIG_SPI))
+ if (!board_tpm_uses_spi())
return;
pmu_clock_en(PERIPH_SPS);
@@ -302,7 +322,7 @@ static void sps_advance_rx(int port, int data_size)
/*
* Actual receive interrupt processing function. Invokes the callback passing
* it a pointer to the linear space in the RX FIFO and the number of bytes
- * availabe at that address.
+ * available at that address.
*
* If RX fifo is wrapping around, the callback will be called twice with two
* flat pointers.
@@ -322,6 +342,7 @@ static void sps_rx_interrupt(uint32_t port, int cs_deasserted)
if (!data_size)
break;
+ seen_data = 1;
sps_rx_count += data_size;
if (sps_rx_handler)
@@ -333,13 +354,52 @@ static void sps_rx_interrupt(uint32_t port, int cs_deasserted)
sps_advance_rx(port, data_size);
}
- if (cs_deasserted)
- sps_rx_handler(NULL, 0, 1);
+ if (cs_deasserted) {
+ if (seen_data) {
+ /*
+ * SPI does not provide inherent flow control. Let's
+ * use this pin to signal the AP that the device has
+ * finished processing received data.
+ */
+
+ sps_rx_handler(NULL, 0, 1);
+ gpio_set_level(GPIO_INT_AP_L, 0);
+ gpio_set_level(GPIO_INT_AP_L, 1);
+ seen_data = 0;
+ }
+ }
}
static void sps_cs_deassert_interrupt(uint32_t port)
{
/* Make sure the receive FIFO is drained. */
+
+ if (sps_cs_asserted()) {
+ /*
+ * we must have been slow, this is the next CS assertion after
+ * the 'wake up' pulse, but we have not processed the wake up
+ * interrupt yet.
+ *
+ * There would be no other out of order CS assertions, as all
+ * the 'real' ones (as opposed to the wake up pulses) are
+ * confirmed by the H1 pulsing the AP interrupt line
+ */
+
+ /*
+ * Make sure we react to the next deassertion when it
+ * happens.
+ */
+ GWRITE_FIELD(SPS, ISTATE_CLR, CS_DEASSERT, 1);
+ GWRITE_FIELD(SPS, FIFO_CTRL, TXFIFO_EN, 0);
+ if (sps_cs_asserted())
+ return;
+
+ /*
+ * The CS went away while we were processing this interrupt,
+ * this was the 'real' CS, need to process data.
+ */
+ }
+
sps_rx_interrupt(port, 1);
GWRITE_FIELD(SPS, ISTATE_CLR, CS_DEASSERT, 1);
GWRITE_FIELD(SPS, FIFO_CTRL, TXFIFO_EN, 0);
@@ -375,7 +435,7 @@ DECLARE_IRQ(GC_IRQNUM_SPS0_RXFIFO_LVL_INTR, _sps0_interrupt, 1);
*/
/*
- * Receive callback implemets a simple state machine, it could be in one of
+ * Receive callback implements a simple state machine, it could be in one of
* three states: not started, receiving frame, frame finished.
*/
@@ -416,7 +476,7 @@ static void sps_receive_callback(uint8_t *data, size_t data_size, int cs_status)
rx_state = spstrx_receiving;
else
/*
- * If we won't be able to receve this much, enter the
+ * If we won't be able to receive this much, enter the
* 'frame finished' state.
*/
rx_state = spstrx_finished;
@@ -495,7 +555,7 @@ static int command_sps(int argc, char **argv)
/*
* Wait for receive state machine to transition out of 'frame
- * finised' state.
+ * finished' state.
*/
while (rx_state == spstrx_finished) {
watchdog_reload();
@@ -503,8 +563,6 @@ static int command_sps(int argc, char **argv)
}
}
- sps_unregister_rx_handler();
-
ccprintf("Processed %d frames\n", count - 1);
ccprintf("rx count %d, tx count %d, tx_empty %d, max rx batch %d\n",
sps_rx_count, sps_tx_count,