int spi_slave_send_response_async(struct spi_comm_packet *resp) { int size = resp->size + SPI_PACKET_HEADER_SIZE; stm32_spi_regs_t *spi = STM32_SPI1_REGS; if (size > SPI_PACKET_MAX_SIZE) return EC_ERROR_OVERFLOW; if (out_msg != (uint8_t *)resp) memcpy(out_msg, resp, size); master_slave_sync(100); if (spi->sr & STM32_SPI_SR_RXNE) in_msg[0] = spi->dr; spi->dr = out_msg[0]; /* Set N_CHG (master SPI_NSS) to high */ STM32_GPIO_BSRR(GPIO_A) = 1 << 1; while (!(spi->sr & STM32_SPI_SR_RXNE)) ; in_msg[0] = spi->dr; dma_clear_isr(STM32_DMAC_SPI1_TX); dma_clear_isr(STM32_DMAC_SPI1_RX); dma_start_rx(&dma_rx_option, size - 1, in_msg); dma_prepare_tx(&dma_tx_option, size - 1, out_msg + 1); dma_go(dma_get_channel(STM32_DMAC_SPI1_TX)); master_slave_sync(5); return EC_SUCCESS; }
static int spi_master_read_write_byte(uint8_t *in_buf, uint8_t *out_buf, int sz) { int ret; dma_start_rx(&dma_rx_option, sz, in_buf); dma_prepare_tx(&dma_tx_option, sz, out_buf); dma_go(dma_get_channel(STM32_DMAC_SPI1_TX)); ret = dma_wait(STM32_DMAC_SPI1_TX); ret |= dma_wait(STM32_DMAC_SPI1_RX); dma_disable(STM32_DMAC_SPI1_TX); dma_disable(STM32_DMAC_SPI1_RX); dma_clear_isr(STM32_DMAC_SPI1_TX); dma_clear_isr(STM32_DMAC_SPI1_RX); return ret; }
static void spi_nss_interrupt(void) { const struct spi_comm_packet *cmd = (const struct spi_comm_packet *)in_msg; stm32_spi_regs_t *spi = STM32_SPI1_REGS; if (spi->sr & STM32_SPI_SR_RXNE) in_msg[0] = spi->dr; master_slave_sync(5); /* Read in the packet size */ while (!(spi->sr & STM32_SPI_SR_RXNE)) ; in_msg[0] = spi->dr; /* Read in the rest of the packet */ dma_clear_isr(STM32_DMAC_SPI1_RX); dma_start_rx(&dma_rx_option, in_msg[0] + SPI_PACKET_HEADER_SIZE - 1, in_msg + 1); dma_prepare_tx(&dma_tx_option, in_msg[0] + SPI_PACKET_HEADER_SIZE - 1, out_msg); dma_go(dma_get_channel(STM32_DMAC_SPI1_TX)); master_slave_sync(5); if (dma_wait(STM32_DMAC_SPI1_RX) != EC_SUCCESS) { debug_printf("SPI: Incomplete packet\n"); spi_slave_nack(); return; } if (spi->sr & STM32_SPI_SR_CRCERR) { debug_printf("SPI: CRC mismatch\n"); spi_slave_nack(); return; } if (cmd->cmd_sts == TS_CMD_HELLO) spi_slave_hello_back(cmd); else if (cmd->cmd_sts == TS_CMD_FULL_SCAN) touch_scan_slave_start(); else spi_slave_nack(); }
static int i2c_write_raw_slave(int port, void *buf, int len) { stm32_dma_chan_t *chan; int rv; /* we don't want to race with TxE interrupt event */ disable_i2c_interrupt(port); /* Configuring DMA1 channel DMAC_SLAVE_TX */ enable_ack(port); chan = dma_get_channel(DMAC_SLAVE_TX); dma_prepare_tx(dma_tx_option + port, len, buf); /* Start the DMA */ dma_go(chan); /* Configuring i2c to use DMA */ STM32_I2C_CR2(port) |= (1 << 11); if (in_interrupt_context()) { /* Poll for the transmission complete flag */ dma_wait(DMAC_SLAVE_TX); dma_clear_isr(DMAC_SLAVE_TX); } else { /* Wait for the transmission complete Interrupt */ dma_enable_tc_interrupt(DMAC_SLAVE_TX); rv = task_wait_event(DMA_TRANSFER_TIMEOUT_US); dma_disable_tc_interrupt(DMAC_SLAVE_TX); if (!(rv & TASK_EVENT_WAKE)) { CPRINTS("Slave timeout, resetting i2c"); i2c_init_port(port); } } dma_disable(DMAC_SLAVE_TX); STM32_I2C_CR2(port) &= ~(1 << 11); enable_i2c_interrupt(port); return len; }
/** * Called to send a response back to the host. * * Some commands can continue for a while. This function is called by * host_command when it completes. * */ static void spi_send_response_packet(struct host_packet *pkt) { stm32_dma_chan_t *txdma; /* * If we're not processing, then the AP has already terminated the * transaction, and won't be listening for a response. */ if (state != SPI_STATE_PROCESSING) return; /* state == SPI_STATE_PROCESSING */ /* Append our past-end byte, which we reserved space for. */ ((uint8_t *)pkt->response)[pkt->response_size + 0] = EC_SPI_PAST_END; #ifdef CHIP_FAMILY_STM32F0 /* Make sure we are going to be outputting it properly when the DMA * ends due to the TX FIFO bug on the F0. See crbug.com/31390 */ ((uint8_t *)pkt->response)[pkt->response_size + 1] = EC_SPI_PAST_END; ((uint8_t *)pkt->response)[pkt->response_size + 2] = EC_SPI_PAST_END; ((uint8_t *)pkt->response)[pkt->response_size + 3] = EC_SPI_PAST_END; #endif /* Transmit the reply */ txdma = dma_get_channel(STM32_DMAC_SPI1_TX); dma_prepare_tx(&dma_tx_option, sizeof(out_preamble) + pkt->response_size + EC_SPI_PAST_END_LENGTH, out_msg); dma_go(txdma); /* * Before the state is set to SENDING, any CS de-assertion would * set setup_transaction_later to 1. */ state = SPI_STATE_SENDING; check_setup_transaction_later(); }
int spi_master_wait_response_async(void) { stm32_spi_regs_t *spi = STM32_SPI1_REGS; int size; master_slave_sync(40); if (wait_for_signal(GPIO_A, 1 << 0, 1, 40 * MSEC)) goto err_wait_resp_async; /* Discard potential garbage in SPI DR */ if (spi->sr & STM32_SPI_SR_RXNE) in_msg[0] = spi->dr; /* Get the packet size */ spi->dr = DUMMY_DATA; while (!(spi->sr & STM32_SPI_SR_RXNE)) ; in_msg[0] = spi->dr; size = in_msg[0] + SPI_PACKET_HEADER_SIZE; master_slave_sync(5); dma_clear_isr(STM32_DMAC_SPI1_TX); dma_clear_isr(STM32_DMAC_SPI1_RX); /* Get the rest of the packet*/ dma_start_rx(&dma_rx_option, size - 1, in_msg + 1); dma_prepare_tx(&dma_tx_option, size - 1, out_msg); dma_go(dma_get_channel(STM32_DMAC_SPI1_TX)); return EC_SUCCESS; err_wait_resp_async: /* Set CS1 (slave SPI_NSS) to high */ STM32_GPIO_BSRR(GPIO_A) = 1 << 6; return EC_ERROR_TIMEOUT; }
/** * Send a reply on a given port. * * The format of a reply is as per the command interface, with a number of * preamble bytes before it. * * The format of a reply is a sequence of bytes: * * <hdr> <status> <len> <msg bytes> <sum> [<preamble byte>...] * * The hdr byte is just a tag to indicate that the real message follows. It * signals the end of any preamble required by the interface. * * The length is the entire packet size, including the header, length bytes, * message payload, checksum, and postamble byte. * * The preamble is at least 2 bytes, but can be longer if the STM takes ages * to react to the incoming message. Since we send our first byte as the AP * sends us the command, we clearly can't send anything sensible for that * byte. The second byte must be written to the output register just when the * command byte is ready (I think), so we can't do anything there either. * Any processing we do may increase this delay. That's the reason for the * preamble. * * It is interesting to note that it seems to be possible to run the SPI * interface faster than the CPU clock with this approach. * * We keep an eye on the NSS line - if this goes high then the transaction is * over so there is no point in trying to send the reply. * * @param txdma TX DMA channel to send on * @param status Status result to send * @param msg_ptr Message payload to send, which normally starts * SPI_PROTO2_OFFSET bytes into out_msg * @param msg_len Number of message bytes to send */ static void reply(stm32_dma_chan_t *txdma, enum ec_status status, char *msg_ptr, int msg_len) { char *msg = out_msg; int need_copy = msg_ptr != msg + SPI_PROTO2_OFFSET; int sum, i; ASSERT(msg_len + SPI_PROTO2_OVERHEAD <= sizeof(out_msg)); /* Add our header bytes - the first one might not actually be sent */ msg[0] = EC_SPI_PROCESSING; msg[1] = EC_SPI_FRAME_START; msg[2] = status; msg[3] = msg_len & 0xff; /* * Calculate the checksum; includes the status and message length bytes * but not the pad and framing bytes since those are stripped by the AP * driver. */ sum = status + msg_len; for (i = 0; i < msg_len; i++) { int ch = msg_ptr[i]; sum += ch; if (need_copy) msg[i + SPI_PROTO2_OFFSET] = ch; } /* Add the checksum and get ready to send */ msg[SPI_PROTO2_OFFSET + msg_len] = sum & 0xff; msg[SPI_PROTO2_OFFSET + msg_len + 1] = EC_SPI_PAST_END; dma_prepare_tx(&dma_tx_option, msg_len + SPI_PROTO2_OVERHEAD, msg); /* Kick off the DMA to send the data */ dma_go(txdma); }