mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2026-01-07 16:11:43 +00:00
If shared_mem_acquire() failed, we should not call shared_mem_release(). BRANCH=none BUG=chrome-os-partner:34703 TEST="Compiled" Change-Id: I5179f8b75b13451a63eb3209c9156066231aa12d Signed-off-by: Sheng-Liang Song <ssl@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/236392 Reviewed-by: David Hendricks <dhendrix@chromium.org> Reviewed-by: Alexandru Stan <amstan@chromium.org> Reviewed-by: Randall Spangler <rspangler@chromium.org>
217 lines
4.6 KiB
C
217 lines
4.6 KiB
C
/*
|
|
* Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
* found in the LICENSE file.
|
|
*
|
|
* SPI master driver.
|
|
*/
|
|
|
|
#include "common.h"
|
|
#include "dma.h"
|
|
#include "gpio.h"
|
|
#include "shared_mem.h"
|
|
#include "spi.h"
|
|
#include "timer.h"
|
|
#include "util.h"
|
|
|
|
#define SPI_REG_ADDR CONCAT3(STM32_SPI, CONFIG_SPI_MASTER_PORT, _BASE)
|
|
#define SPI_REG ((stm32_spi_regs_t *)SPI_REG_ADDR)
|
|
#define DMAC_SPI_TX CONCAT3(STM32_DMAC_SPI, CONFIG_SPI_MASTER_PORT, _TX)
|
|
#define DMAC_SPI_RX CONCAT3(STM32_DMAC_SPI, CONFIG_SPI_MASTER_PORT, _RX)
|
|
|
|
#define SPI_TRANSACTION_TIMEOUT_USEC (800 * MSEC)
|
|
|
|
/* Default DMA channel options */
|
|
static const struct dma_option dma_tx_option = {
|
|
DMAC_SPI_TX, (void *)&SPI_REG->dr,
|
|
STM32_DMA_CCR_MSIZE_8_BIT | STM32_DMA_CCR_PSIZE_8_BIT
|
|
};
|
|
|
|
static const struct dma_option dma_rx_option = {
|
|
DMAC_SPI_RX, (void *)&SPI_REG->dr,
|
|
STM32_DMA_CCR_MSIZE_8_BIT | STM32_DMA_CCR_PSIZE_8_BIT
|
|
};
|
|
|
|
static uint8_t spi_enabled;
|
|
|
|
/**
|
|
* Initialize SPI module, registers, and clocks
|
|
*/
|
|
static int spi_master_initialize(void)
|
|
{
|
|
stm32_spi_regs_t *spi = SPI_REG;
|
|
|
|
/*
|
|
* Set SPI master, baud rate, and software slave control.
|
|
* Set SPI clock rate to DIV2R = 24 MHz
|
|
* */
|
|
spi->cr1 = STM32_SPI_CR1_MSTR | STM32_SPI_CR1_SSM | STM32_SPI_CR1_SSI;
|
|
|
|
/*
|
|
* Configure 8-bit datasize, set FRXTH, enable DMA,
|
|
* and enable NSS output
|
|
*/
|
|
spi->cr2 = STM32_SPI_CR2_TXDMAEN | STM32_SPI_CR2_RXDMAEN |
|
|
STM32_SPI_CR2_FRXTH | STM32_SPI_CR2_DATASIZE(8);
|
|
|
|
/* Enable SPI */
|
|
spi->cr1 |= STM32_SPI_CR1_SPE;
|
|
|
|
/* Drive SS high */
|
|
gpio_set_level(CONFIG_SPI_CS_GPIO, 1);
|
|
|
|
/* Set flag */
|
|
spi_enabled = 1;
|
|
|
|
return EC_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* Shutdown SPI module
|
|
*/
|
|
static int spi_master_shutdown(void)
|
|
{
|
|
int rv = EC_SUCCESS;
|
|
stm32_spi_regs_t *spi = SPI_REG;
|
|
char dummy __attribute__((unused));
|
|
|
|
/* Set flag */
|
|
spi_enabled = 0;
|
|
|
|
/* Disable DMA streams */
|
|
dma_disable(dma_tx_option.channel);
|
|
dma_disable(dma_rx_option.channel);
|
|
|
|
/* Disable SPI */
|
|
spi->cr1 &= ~STM32_SPI_CR1_SPE;
|
|
|
|
/* Read until FRLVL[1:0] is empty */
|
|
while (spi->sr & STM32_SPI_SR_FTLVL)
|
|
dummy = spi->dr;
|
|
|
|
/* Disable DMA buffers */
|
|
spi->cr2 &= ~(STM32_SPI_CR2_TXDMAEN | STM32_SPI_CR2_RXDMAEN);
|
|
|
|
return rv;
|
|
}
|
|
|
|
int spi_enable(int enable)
|
|
{
|
|
if (enable == spi_enabled)
|
|
return EC_SUCCESS;
|
|
if (enable)
|
|
return spi_master_initialize();
|
|
else
|
|
return spi_master_shutdown();
|
|
}
|
|
|
|
static int spi_dma_start(const uint8_t *txdata, uint8_t *rxdata, int len)
|
|
{
|
|
stm32_dma_chan_t *txdma;
|
|
|
|
/* Set up RX DMA */
|
|
dma_start_rx(&dma_rx_option, len, rxdata);
|
|
|
|
/* Set up TX DMA */
|
|
txdma = dma_get_channel(dma_tx_option.channel);
|
|
dma_prepare_tx(&dma_tx_option, len, txdata);
|
|
dma_go(txdma);
|
|
|
|
return EC_SUCCESS;
|
|
}
|
|
|
|
static int spi_dma_wait(void)
|
|
{
|
|
timestamp_t timeout;
|
|
stm32_spi_regs_t *spi = SPI_REG;
|
|
int rv = EC_SUCCESS;
|
|
|
|
/* Wait for DMA transmission to complete */
|
|
rv = dma_wait(dma_tx_option.channel);
|
|
if (rv)
|
|
return rv;
|
|
|
|
timeout.val = get_time().val + SPI_TRANSACTION_TIMEOUT_USEC;
|
|
/* Wait for FIFO empty and BSY bit clear to indicate completion */
|
|
while ((spi->sr & STM32_SPI_SR_FTLVL) || (spi->sr & STM32_SPI_SR_BSY))
|
|
if (get_time().val > timeout.val)
|
|
return EC_ERROR_TIMEOUT;
|
|
|
|
/* Disable TX DMA */
|
|
dma_disable(dma_tx_option.channel);
|
|
|
|
/* Wait for DMA reception to complete */
|
|
rv = dma_wait(dma_rx_option.channel);
|
|
if (rv)
|
|
return rv;
|
|
|
|
timeout.val = get_time().val + SPI_TRANSACTION_TIMEOUT_USEC;
|
|
/* Wait for FRLVL[1:0] to indicate FIFO empty */
|
|
while (spi->sr & STM32_SPI_SR_FRLVL)
|
|
if (get_time().val > timeout.val)
|
|
return EC_ERROR_TIMEOUT;
|
|
|
|
/* Disable RX DMA */
|
|
dma_disable(dma_rx_option.channel);
|
|
|
|
return rv;
|
|
}
|
|
|
|
int spi_transaction_async(const uint8_t *txdata, int txlen,
|
|
uint8_t *rxdata, int rxlen)
|
|
{
|
|
int rv = EC_SUCCESS;
|
|
stm32_spi_regs_t *spi = SPI_REG;
|
|
char *buf;
|
|
|
|
rv = shared_mem_acquire(MAX(txlen, rxlen), &buf);
|
|
if (rv != EC_SUCCESS)
|
|
return rv;
|
|
|
|
/* Drive SS low */
|
|
gpio_set_level(CONFIG_SPI_CS_GPIO, 0);
|
|
|
|
/* Clear out the FIFO. */
|
|
while (spi->sr & STM32_SPI_SR_FRLVL)
|
|
(void) (uint8_t) spi->dr;
|
|
|
|
rv = spi_dma_start(txdata, buf, txlen);
|
|
if (rv != EC_SUCCESS)
|
|
goto err_free;
|
|
|
|
rv = spi_dma_wait();
|
|
if (rv != EC_SUCCESS)
|
|
goto err_free;
|
|
|
|
if (rxlen) {
|
|
rv = spi_dma_start(buf, rxdata, rxlen);
|
|
if (rv != EC_SUCCESS)
|
|
goto err_free;
|
|
}
|
|
|
|
err_free:
|
|
shared_mem_release(buf);
|
|
return rv;
|
|
}
|
|
|
|
int spi_transaction_flush(void)
|
|
{
|
|
int rv = spi_dma_wait();
|
|
|
|
/* Drive SS high */
|
|
gpio_set_level(CONFIG_SPI_CS_GPIO, 1);
|
|
|
|
return rv;
|
|
}
|
|
|
|
int spi_transaction(const uint8_t *txdata, int txlen,
|
|
uint8_t *rxdata, int rxlen)
|
|
{
|
|
int rv;
|
|
|
|
rv = spi_transaction_async(txdata, txlen, rxdata, rxlen);
|
|
rv |= spi_transaction_flush();
|
|
|
|
return rv;
|
|
}
|