diff options
Diffstat (limited to 'chip/g/sps.c')
-rw-r--r-- | chip/g/sps.c | 110 |
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, |