646 lines
17 KiB
C
646 lines
17 KiB
C
/*
|
|
* Copyright (C) 2016 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
|
|
#include <gpio.h>
|
|
#include <spi.h>
|
|
#include <spi_priv.h>
|
|
#include <util.h>
|
|
#include <atomicBitset.h>
|
|
#include <atomic.h>
|
|
#include <platform.h>
|
|
|
|
#include <plat/cmsis.h>
|
|
#include <plat/dma.h>
|
|
#include <plat/gpio.h>
|
|
#include <plat/pwr.h>
|
|
#include <plat/exti.h>
|
|
#include <plat/syscfg.h>
|
|
#include <plat/spi.h>
|
|
#include <plat/plat.h>
|
|
|
|
#define SPI_CR1_CPHA (1 << 0)
|
|
#define SPI_CR1_CPOL (1 << 1)
|
|
#define SPI_CR1_MSTR (1 << 2)
|
|
|
|
#define SPI_CR1_BR(x) ((LOG2_CEIL(x) - 1) << 3)
|
|
#define SPI_CR1_BR_MIN 2
|
|
#define SPI_CR1_BR_MAX 256
|
|
#define SPI_CR1_BR_MASK (0x7 << 3)
|
|
|
|
#define SPI_CR1_SPE (1 << 6)
|
|
#define SPI_CR1_LSBFIRST (1 << 7)
|
|
#define SPI_CR1_SSI (1 << 8)
|
|
#define SPI_CR1_SSM (1 << 9)
|
|
#define SPI_CR1_RXONLY (1 << 10)
|
|
#define SPI_CR1_DFF (1 << 11)
|
|
#define SPI_CR1_BIDIOE (1 << 14)
|
|
#define SPI_CR1_BIDIMODE (1 << 15)
|
|
|
|
#define SPI_CR2_TXEIE (1 << 7)
|
|
#define SPI_CR2_RXNEIE (1 << 6)
|
|
#define SPI_CR2_ERRIE (1 << 5)
|
|
#define SPI_CR2_TXDMAEN (1 << 1)
|
|
#define SPI_CR2_RXDMAEN (1 << 0)
|
|
#define SPI_CR2_INT_MASK (SPI_CR2_TXEIE | SPI_CR2_RXNEIE | SPI_CR2_ERRIE)
|
|
|
|
#define SPI_CR2_SSOE (1 << 2)
|
|
|
|
#define SPI_SR_RXNE (1 << 0)
|
|
#define SPI_SR_TXE (1 << 1)
|
|
#define SPI_SR_BSY (1 << 7)
|
|
|
|
struct StmSpi {
|
|
volatile uint32_t CR1;
|
|
volatile uint32_t CR2;
|
|
volatile uint32_t SR;
|
|
volatile uint32_t DR;
|
|
volatile uint32_t CRCPR;
|
|
volatile uint32_t RXCRCR;
|
|
volatile uint32_t TXCRCR;
|
|
volatile uint32_t I2SCFGR;
|
|
volatile uint32_t I2SPR;
|
|
};
|
|
|
|
struct StmSpiState {
|
|
uint8_t bitsPerWord;
|
|
uint8_t xferEnable;
|
|
|
|
uint16_t rxWord;
|
|
uint16_t txWord;
|
|
|
|
bool rxDone;
|
|
bool txDone;
|
|
|
|
struct ChainedIsr isrNss;
|
|
|
|
bool nssChange;
|
|
};
|
|
|
|
struct StmSpiCfg {
|
|
struct StmSpi *regs;
|
|
|
|
uint32_t clockBus;
|
|
uint32_t clockUnit;
|
|
|
|
IRQn_Type irq;
|
|
|
|
uint8_t dmaBus;
|
|
};
|
|
|
|
struct StmSpiDev {
|
|
struct SpiDevice *base;
|
|
const struct StmSpiCfg *cfg;
|
|
const struct StmSpiBoardCfg *board;
|
|
struct StmSpiState state;
|
|
|
|
struct Gpio *miso;
|
|
struct Gpio *mosi;
|
|
struct Gpio *sck;
|
|
struct Gpio *nss;
|
|
};
|
|
|
|
static inline struct Gpio *stmSpiGpioInit(uint32_t gpioNum, enum StmGpioSpeed speed, enum StmGpioAltFunc func)
|
|
{
|
|
struct Gpio *gpio = gpioRequest(gpioNum);
|
|
|
|
if (gpio)
|
|
gpioConfigAlt(gpio, speed, GPIO_PULL_NONE, GPIO_OUT_PUSH_PULL, func);
|
|
|
|
return gpio;
|
|
}
|
|
|
|
static inline void stmSpiDataPullMode(struct StmSpiDev *pdev, enum StmGpioSpeed dataSpeed, enum GpioPullMode dataPull)
|
|
{
|
|
gpioConfigAlt(pdev->miso, dataSpeed, dataPull, GPIO_OUT_PUSH_PULL, pdev->board->gpioFunc);
|
|
gpioConfigAlt(pdev->mosi, dataSpeed, dataPull, GPIO_OUT_PUSH_PULL, pdev->board->gpioFunc);
|
|
}
|
|
|
|
static inline void stmSpiSckPullMode(struct StmSpiDev *pdev, enum StmGpioSpeed sckSpeed, enum GpioPullMode sckPull)
|
|
{
|
|
gpioConfigAlt(pdev->sck, sckSpeed, sckPull, GPIO_OUT_PUSH_PULL, pdev->board->gpioFunc);
|
|
}
|
|
|
|
static inline void stmSpiStartDma(struct StmSpiDev *pdev,
|
|
const struct StmSpiDmaCfg *dmaCfg, const void *buf, uint8_t bitsPerWord,
|
|
bool minc, size_t size, DmaCallbackF callback, bool rx)
|
|
{
|
|
struct StmSpi *regs = pdev->cfg->regs;
|
|
struct dmaMode mode;
|
|
|
|
memset(&mode, 0, sizeof(mode));
|
|
|
|
if (bitsPerWord == 8) {
|
|
mode.psize = DMA_SIZE_8_BITS;
|
|
mode.msize = DMA_SIZE_8_BITS;
|
|
} else {
|
|
mode.psize = DMA_SIZE_16_BITS;
|
|
mode.msize = DMA_SIZE_16_BITS;
|
|
}
|
|
mode.priority = DMA_PRIORITY_HIGH;
|
|
mode.direction = rx ? DMA_DIRECTION_PERIPH_TO_MEM :
|
|
DMA_DIRECTION_MEM_TO_PERIPH;
|
|
mode.periphAddr = (uintptr_t)®s->DR;
|
|
mode.minc = minc;
|
|
mode.channel = dmaCfg->channel;
|
|
|
|
dmaStart(pdev->cfg->dmaBus, dmaCfg->stream, buf, size, &mode, callback,
|
|
pdev);
|
|
}
|
|
|
|
static inline int stmSpiEnable(struct StmSpiDev *pdev,
|
|
const struct SpiMode *mode, bool master)
|
|
{
|
|
struct StmSpi *regs = pdev->cfg->regs;
|
|
struct StmSpiState *state = &pdev->state;
|
|
|
|
if (mode->bitsPerWord != 8 &&
|
|
mode->bitsPerWord != 16)
|
|
return -EINVAL;
|
|
|
|
unsigned int div;
|
|
if (master) {
|
|
if (!mode->speed)
|
|
return -EINVAL;
|
|
|
|
uint32_t pclk = pwrGetBusSpeed(pdev->cfg->clockBus);
|
|
div = pclk / mode->speed;
|
|
if (div > SPI_CR1_BR_MAX)
|
|
return -EINVAL;
|
|
else if (div < SPI_CR1_BR_MIN)
|
|
div = SPI_CR1_BR_MIN;
|
|
}
|
|
|
|
atomicWriteByte(&state->xferEnable, false);
|
|
|
|
state->txWord = mode->txWord;
|
|
state->bitsPerWord = mode->bitsPerWord;
|
|
|
|
pwrUnitClock(pdev->cfg->clockBus, pdev->cfg->clockUnit, true);
|
|
|
|
if (master) {
|
|
regs->CR1 &= ~SPI_CR1_BR_MASK;
|
|
regs->CR1 |= SPI_CR1_BR(div);
|
|
}
|
|
|
|
if (mode->cpol == SPI_CPOL_IDLE_LO)
|
|
regs->CR1 &= ~SPI_CR1_CPOL;
|
|
else
|
|
regs->CR1 |= SPI_CR1_CPOL;
|
|
|
|
if (mode->cpha == SPI_CPHA_LEADING_EDGE)
|
|
regs->CR1 &= ~SPI_CR1_CPHA;
|
|
else
|
|
regs->CR1 |= SPI_CR1_CPHA;
|
|
|
|
if (mode->bitsPerWord == 8)
|
|
regs->CR1 &= ~SPI_CR1_DFF;
|
|
else
|
|
regs->CR1 |= SPI_CR1_DFF;
|
|
|
|
if (mode->format == SPI_FORMAT_MSB_FIRST)
|
|
regs->CR1 &= ~SPI_CR1_LSBFIRST;
|
|
else
|
|
regs->CR1 |= SPI_CR1_LSBFIRST;
|
|
|
|
if (master)
|
|
regs->CR1 |= SPI_CR1_SSI | SPI_CR1_SSM | SPI_CR1_MSTR;
|
|
else
|
|
regs->CR1 &= ~(SPI_CR1_SSM | SPI_CR1_MSTR);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int stmSpiMasterStartSync(struct SpiDevice *dev, spi_cs_t cs,
|
|
const struct SpiMode *mode)
|
|
{
|
|
struct StmSpiDev *pdev = dev->pdata;
|
|
|
|
int err = stmSpiEnable(pdev, mode, true);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
stmSpiDataPullMode(pdev, pdev->board->gpioSpeed, pdev->board->gpioPull);
|
|
stmSpiSckPullMode(pdev, pdev->board->gpioSpeed, mode->cpol ? GPIO_PULL_UP : GPIO_PULL_DOWN);
|
|
|
|
if (!pdev->nss)
|
|
pdev->nss = gpioRequest(cs);
|
|
if (!pdev->nss)
|
|
return -ENODEV;
|
|
gpioConfigOutput(pdev->nss, pdev->board->gpioSpeed, pdev->board->gpioPull, GPIO_OUT_PUSH_PULL, 1);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int stmSpiSlaveStartSync(struct SpiDevice *dev,
|
|
const struct SpiMode *mode)
|
|
{
|
|
struct StmSpiDev *pdev = dev->pdata;
|
|
|
|
stmSpiDataPullMode(pdev, pdev->board->gpioSpeed, GPIO_PULL_NONE);
|
|
stmSpiSckPullMode(pdev, pdev->board->gpioSpeed, GPIO_PULL_NONE);
|
|
|
|
if (!pdev->nss)
|
|
pdev->nss = stmSpiGpioInit(pdev->board->gpioNss, pdev->board->gpioSpeed, pdev->board->gpioFunc);
|
|
if (!pdev->nss)
|
|
return -ENODEV;
|
|
|
|
return stmSpiEnable(pdev, mode, false);
|
|
}
|
|
|
|
static inline bool stmSpiIsMaster(struct StmSpiDev *pdev)
|
|
{
|
|
struct StmSpi *regs = pdev->cfg->regs;
|
|
return !!(regs->CR1 & SPI_CR1_MSTR);
|
|
}
|
|
|
|
static void stmSpiDone(struct StmSpiDev *pdev, int err)
|
|
{
|
|
struct StmSpi *regs = pdev->cfg->regs;
|
|
struct StmSpiState *state = &pdev->state;
|
|
|
|
if (pdev->board->sleepDev >= 0)
|
|
platReleaseDevInSleepMode(pdev->board->sleepDev);
|
|
|
|
while (regs->SR & SPI_SR_BSY)
|
|
;
|
|
|
|
if (stmSpiIsMaster(pdev)) {
|
|
if (state->nssChange && pdev->nss)
|
|
gpioSet(pdev->nss, 1);
|
|
spiMasterRxTxDone(pdev->base, err);
|
|
} else {
|
|
regs->CR2 = SPI_CR2_TXEIE;
|
|
spiSlaveRxTxDone(pdev->base, err);
|
|
}
|
|
}
|
|
|
|
static void stmSpiRxDone(void *cookie, uint16_t bytesLeft, int err)
|
|
{
|
|
struct StmSpiDev *pdev = cookie;
|
|
struct StmSpi *regs = pdev->cfg->regs;
|
|
struct StmSpiState *state = &pdev->state;
|
|
|
|
regs->CR2 &= ~SPI_CR2_RXDMAEN;
|
|
state->rxDone = true;
|
|
|
|
if (state->txDone) {
|
|
atomicWriteByte(&state->xferEnable, false);
|
|
stmSpiDone(pdev, err);
|
|
}
|
|
}
|
|
|
|
static void stmSpiTxDone(void *cookie, uint16_t bytesLeft, int err)
|
|
{
|
|
struct StmSpiDev *pdev = cookie;
|
|
struct StmSpi *regs = pdev->cfg->regs;
|
|
struct StmSpiState *state = &pdev->state;
|
|
|
|
regs->CR2 &= ~SPI_CR2_TXDMAEN;
|
|
state->txDone = true;
|
|
|
|
if (state->rxDone) {
|
|
atomicWriteByte(&state->xferEnable, false);
|
|
stmSpiDone(pdev, err);
|
|
}
|
|
}
|
|
|
|
static int stmSpiRxTx(struct SpiDevice *dev, void *rxBuf, const void *txBuf,
|
|
size_t size, const struct SpiMode *mode)
|
|
{
|
|
struct StmSpiDev *pdev = dev->pdata;
|
|
struct StmSpi *regs = pdev->cfg->regs;
|
|
struct StmSpiState *state = &pdev->state;
|
|
bool rxMinc = true, txMinc = true;
|
|
uint32_t cr2 = SPI_CR2_TXDMAEN;
|
|
|
|
if (atomicXchgByte(&state->xferEnable, true) == true)
|
|
return -EBUSY;
|
|
|
|
if (stmSpiIsMaster(pdev) && pdev->nss)
|
|
gpioSet(pdev->nss, 0);
|
|
|
|
state->rxDone = false;
|
|
state->txDone = false;
|
|
state->nssChange = mode->nssChange;
|
|
|
|
/* In master mode, if RX is ignored at any point, then turning it on
|
|
* later may cause the SPI/DMA controllers to "receive" a stale byte
|
|
* sitting in a FIFO somewhere (even when their respective registers say
|
|
* their FIFOs are empty, and even if the SPI FIFO is explicitly cleared).
|
|
* Work around this by DMAing bytes we don't care about into a throwaway
|
|
* 1-word buffer.
|
|
*
|
|
* In slave mode, this specific WAR sometimes causes bigger problems
|
|
* (the first byte TXed is sometimes dropped or corrupted). Slave mode
|
|
* has its own WARs below.
|
|
*/
|
|
if (!rxBuf && stmSpiIsMaster(pdev)) {
|
|
rxBuf = &state->rxWord;
|
|
rxMinc = false;
|
|
}
|
|
|
|
if (rxBuf) {
|
|
stmSpiStartDma(pdev, &pdev->board->dmaRx, rxBuf, mode->bitsPerWord,
|
|
rxMinc, size, stmSpiRxDone, true);
|
|
cr2 |= SPI_CR2_RXDMAEN;
|
|
} else {
|
|
state->rxDone = true;
|
|
}
|
|
|
|
if (!txBuf) {
|
|
txBuf = &state->txWord;
|
|
txMinc = false;
|
|
}
|
|
stmSpiStartDma(pdev, &pdev->board->dmaTx, txBuf, mode->bitsPerWord, txMinc,
|
|
size, stmSpiTxDone, false);
|
|
|
|
/* Ensure the TXE and RXNE bits are cleared; otherwise the DMA controller
|
|
* may "receive" the byte sitting in the SPI controller's FIFO right now,
|
|
* or drop/corrupt the first TX byte. Timing is crucial here, so do it
|
|
* right before enabling DMA.
|
|
*/
|
|
if (!stmSpiIsMaster(pdev)) {
|
|
regs->CR2 &= ~SPI_CR2_TXEIE;
|
|
NVIC_ClearPendingIRQ(pdev->cfg->irq);
|
|
|
|
if (regs->SR & SPI_SR_RXNE)
|
|
(void)regs->DR;
|
|
|
|
if (regs->SR & SPI_SR_TXE)
|
|
regs->DR = mode->txWord;
|
|
}
|
|
|
|
if (pdev->board->sleepDev >= 0)
|
|
platRequestDevInSleepMode(pdev->board->sleepDev, 12);
|
|
|
|
regs->CR2 = cr2;
|
|
regs->CR1 |= SPI_CR1_SPE;
|
|
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int stmSpiSlaveIdle(struct SpiDevice *dev, const struct SpiMode *mode)
|
|
{
|
|
struct StmSpiDev *pdev = dev->pdata;
|
|
struct StmSpi *regs = pdev->cfg->regs;
|
|
struct StmSpiState *state = &pdev->state;
|
|
|
|
if (atomicXchgByte(&state->xferEnable, true) == true)
|
|
return -EBUSY;
|
|
|
|
regs->CR2 = SPI_CR2_TXEIE;
|
|
regs->CR1 |= SPI_CR1_SPE;
|
|
|
|
atomicXchgByte(&state->xferEnable, false);
|
|
return 0;
|
|
}
|
|
|
|
static inline void stmSpiDisable(struct SpiDevice *dev, bool master)
|
|
{
|
|
struct StmSpiDev *pdev = dev->pdata;
|
|
struct StmSpi *regs = pdev->cfg->regs;
|
|
|
|
while (regs->SR & SPI_SR_BSY)
|
|
;
|
|
|
|
if (master) {
|
|
stmSpiSckPullMode(pdev, pdev->board->gpioSpeed, pdev->board->gpioPull);
|
|
}
|
|
|
|
regs->CR2 &= ~(SPI_CR2_RXDMAEN | SPI_CR2_TXDMAEN | SPI_CR2_TXEIE);
|
|
regs->CR1 &= ~SPI_CR1_SPE;
|
|
pwrUnitClock(pdev->cfg->clockBus, pdev->cfg->clockUnit, false);
|
|
}
|
|
|
|
static int stmSpiMasterStopSync(struct SpiDevice *dev)
|
|
{
|
|
struct StmSpiDev *pdev = dev->pdata;
|
|
|
|
if (pdev->nss) {
|
|
gpioSet(pdev->nss, 1);
|
|
gpioRelease(pdev->nss);
|
|
}
|
|
|
|
stmSpiDisable(dev, true);
|
|
pdev->nss = NULL;
|
|
return 0;
|
|
}
|
|
|
|
static int stmSpiSlaveStopSync(struct SpiDevice *dev)
|
|
{
|
|
struct StmSpiDev *pdev = dev->pdata;
|
|
|
|
if (pdev->nss)
|
|
gpioRelease(pdev->nss);
|
|
|
|
stmSpiDisable(dev, false);
|
|
pdev->nss = NULL;
|
|
return 0;
|
|
}
|
|
|
|
static bool stmSpiExtiIsr(struct ChainedIsr *isr)
|
|
{
|
|
struct StmSpiState *state = container_of(isr, struct StmSpiState, isrNss);
|
|
struct StmSpiDev *pdev = container_of(state, struct StmSpiDev, state);
|
|
|
|
if (pdev->nss && !extiIsPendingGpio(pdev->nss))
|
|
return false;
|
|
|
|
spiSlaveCsInactive(pdev->base);
|
|
if (pdev->nss)
|
|
extiClearPendingGpio(pdev->nss);
|
|
return true;
|
|
}
|
|
|
|
static void stmSpiSlaveSetCsInterrupt(struct SpiDevice *dev, bool enabled)
|
|
{
|
|
struct StmSpiDev *pdev = dev->pdata;
|
|
struct ChainedIsr *isr = &pdev->state.isrNss;
|
|
|
|
if (enabled) {
|
|
isr->func = stmSpiExtiIsr;
|
|
|
|
if (pdev->nss) {
|
|
syscfgSetExtiPort(pdev->nss);
|
|
extiEnableIntGpio(pdev->nss, EXTI_TRIGGER_RISING);
|
|
}
|
|
extiChainIsr(pdev->board->irqNss, isr);
|
|
} else {
|
|
extiUnchainIsr(pdev->board->irqNss, isr);
|
|
if (pdev->nss)
|
|
extiDisableIntGpio(pdev->nss);
|
|
}
|
|
}
|
|
|
|
static bool stmSpiSlaveCsIsActive(struct SpiDevice *dev)
|
|
{
|
|
struct StmSpiDev *pdev = dev->pdata;
|
|
return pdev->nss && !gpioGet(pdev->nss);
|
|
}
|
|
|
|
static inline void stmSpiTxe(struct StmSpiDev *pdev)
|
|
{
|
|
struct StmSpi *regs = pdev->cfg->regs;
|
|
|
|
/**
|
|
* n.b.: if nothing handles the TXE interrupt in slave mode, the SPI
|
|
* controller will just keep reading the existing value from DR anytime it
|
|
* needs data
|
|
*/
|
|
regs->DR = pdev->state.txWord;
|
|
regs->CR2 &= ~SPI_CR2_TXEIE;
|
|
}
|
|
|
|
static void stmSpiIsr(struct StmSpiDev *pdev)
|
|
{
|
|
struct StmSpi *regs = pdev->cfg->regs;
|
|
|
|
if (regs->SR & SPI_SR_TXE) {
|
|
stmSpiTxe(pdev);
|
|
}
|
|
|
|
/* TODO: error conditions */
|
|
}
|
|
|
|
static int stmSpiRelease(struct SpiDevice *dev)
|
|
{
|
|
struct StmSpiDev *pdev = dev->pdata;
|
|
|
|
NVIC_DisableIRQ(pdev->cfg->irq);
|
|
|
|
pdev->base = NULL;
|
|
return 0;
|
|
}
|
|
|
|
#define DECLARE_IRQ_HANDLER(_n) \
|
|
void SPI##_n##_IRQHandler(); \
|
|
void SPI##_n##_IRQHandler() \
|
|
{ \
|
|
stmSpiIsr(&mStmSpiDevs[_n - 1]); \
|
|
}
|
|
|
|
const struct SpiDevice_ops mStmSpiOps = {
|
|
.masterStartSync = stmSpiMasterStartSync,
|
|
.masterRxTx = stmSpiRxTx,
|
|
.masterStopSync = stmSpiMasterStopSync,
|
|
|
|
.slaveStartSync = stmSpiSlaveStartSync,
|
|
.slaveIdle = stmSpiSlaveIdle,
|
|
.slaveRxTx = stmSpiRxTx,
|
|
.slaveStopSync = stmSpiSlaveStopSync,
|
|
|
|
.slaveSetCsInterrupt = stmSpiSlaveSetCsInterrupt,
|
|
.slaveCsIsActive = stmSpiSlaveCsIsActive,
|
|
|
|
.release = stmSpiRelease,
|
|
};
|
|
|
|
static const struct StmSpiCfg mStmSpiCfgs[] = {
|
|
[0] = {
|
|
.regs = (struct StmSpi *)SPI1_BASE,
|
|
|
|
.clockBus = PERIPH_BUS_APB2,
|
|
.clockUnit = PERIPH_APB2_SPI1,
|
|
|
|
.irq = SPI1_IRQn,
|
|
|
|
.dmaBus = SPI1_DMA_BUS,
|
|
},
|
|
[1] = {
|
|
.regs = (struct StmSpi *)SPI2_BASE,
|
|
|
|
.clockBus = PERIPH_BUS_APB1,
|
|
.clockUnit = PERIPH_APB1_SPI2,
|
|
|
|
.irq = SPI2_IRQn,
|
|
|
|
.dmaBus = SPI2_DMA_BUS,
|
|
},
|
|
[2] = {
|
|
.regs = (struct StmSpi *)SPI3_BASE,
|
|
|
|
.clockBus = PERIPH_BUS_APB1,
|
|
.clockUnit = PERIPH_APB1_SPI3,
|
|
|
|
.irq = SPI3_IRQn,
|
|
|
|
.dmaBus = SPI3_DMA_BUS,
|
|
},
|
|
};
|
|
|
|
static struct StmSpiDev mStmSpiDevs[ARRAY_SIZE(mStmSpiCfgs)];
|
|
DECLARE_IRQ_HANDLER(1)
|
|
DECLARE_IRQ_HANDLER(2)
|
|
DECLARE_IRQ_HANDLER(3)
|
|
|
|
static void stmSpiInit(struct StmSpiDev *pdev, const struct StmSpiCfg *cfg,
|
|
const struct StmSpiBoardCfg *board, struct SpiDevice *dev)
|
|
{
|
|
pdev->miso = stmSpiGpioInit(board->gpioMiso, board->gpioSpeed, board->gpioFunc);
|
|
pdev->mosi = stmSpiGpioInit(board->gpioMosi, board->gpioSpeed, board->gpioFunc);
|
|
pdev->sck = stmSpiGpioInit(board->gpioSclk, board->gpioSpeed, board->gpioFunc);
|
|
|
|
NVIC_EnableIRQ(cfg->irq);
|
|
|
|
pdev->base = dev;
|
|
pdev->cfg = cfg;
|
|
pdev->board = board;
|
|
}
|
|
|
|
int spiRequest(struct SpiDevice *dev, uint8_t busId)
|
|
{
|
|
if (busId >= ARRAY_SIZE(mStmSpiDevs))
|
|
return -ENODEV;
|
|
|
|
const struct StmSpiBoardCfg *board = boardStmSpiCfg(busId);
|
|
if (!board)
|
|
return -ENODEV;
|
|
|
|
struct StmSpiDev *pdev = &mStmSpiDevs[busId];
|
|
const struct StmSpiCfg *cfg = &mStmSpiCfgs[busId];
|
|
if (!pdev->base)
|
|
stmSpiInit(pdev, cfg, board, dev);
|
|
|
|
memset(&pdev->state, 0, sizeof(pdev->state));
|
|
dev->ops = &mStmSpiOps;
|
|
dev->pdata = pdev;
|
|
return 0;
|
|
}
|
|
|
|
const enum IRQn spiRxIrq(uint8_t busId)
|
|
{
|
|
if (busId >= ARRAY_SIZE(mStmSpiDevs))
|
|
return -ENODEV;
|
|
|
|
struct StmSpiDev *pdev = &mStmSpiDevs[busId];
|
|
|
|
return dmaIrq(pdev->cfg->dmaBus, pdev->board->dmaRx.stream);
|
|
}
|
|
|
|
const enum IRQn spiTxIrq(uint8_t busId)
|
|
{
|
|
if (busId >= ARRAY_SIZE(mStmSpiDevs))
|
|
return -ENODEV;
|
|
|
|
struct StmSpiDev *pdev = &mStmSpiDevs[busId];
|
|
|
|
return dmaIrq(pdev->cfg->dmaBus, pdev->board->dmaTx.stream);
|
|
}
|