Home | History | Annotate | Download | only in stm32
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include <errno.h>
     18 #include <string.h>
     19 
     20 #include <gpio.h>
     21 #include <spi.h>
     22 #include <spi_priv.h>
     23 #include <util.h>
     24 #include <atomicBitset.h>
     25 #include <atomic.h>
     26 #include <platform.h>
     27 
     28 #include <plat/cmsis.h>
     29 #include <plat/dma.h>
     30 #include <plat/gpio.h>
     31 #include <plat/pwr.h>
     32 #include <plat/exti.h>
     33 #include <plat/syscfg.h>
     34 #include <plat/spi.h>
     35 #include <plat/plat.h>
     36 
     37 #define SPI_CR1_CPHA                (1 << 0)
     38 #define SPI_CR1_CPOL                (1 << 1)
     39 #define SPI_CR1_MSTR                (1 << 2)
     40 
     41 #define SPI_CR1_BR(x)               ((LOG2_CEIL(x) - 1) << 3)
     42 #define SPI_CR1_BR_MIN              2
     43 #define SPI_CR1_BR_MAX              256
     44 #define SPI_CR1_BR_MASK             (0x7 << 3)
     45 
     46 #define SPI_CR1_SPE                 (1 << 6)
     47 #define SPI_CR1_LSBFIRST            (1 << 7)
     48 #define SPI_CR1_SSI                 (1 << 8)
     49 #define SPI_CR1_SSM                 (1 << 9)
     50 #define SPI_CR1_RXONLY              (1 << 10)
     51 #define SPI_CR1_DFF                 (1 << 11)
     52 #define SPI_CR1_BIDIOE              (1 << 14)
     53 #define SPI_CR1_BIDIMODE            (1 << 15)
     54 
     55 #define SPI_CR2_TXEIE               (1 << 7)
     56 #define SPI_CR2_RXNEIE              (1 << 6)
     57 #define SPI_CR2_ERRIE               (1 << 5)
     58 #define SPI_CR2_TXDMAEN             (1 << 1)
     59 #define SPI_CR2_RXDMAEN             (1 << 0)
     60 #define SPI_CR2_INT_MASK            (SPI_CR2_TXEIE | SPI_CR2_RXNEIE | SPI_CR2_ERRIE)
     61 
     62 #define SPI_CR2_SSOE                (1 << 2)
     63 
     64 #define SPI_SR_RXNE                 (1 << 0)
     65 #define SPI_SR_TXE                  (1 << 1)
     66 #define SPI_SR_BSY                  (1 << 7)
     67 
     68 struct StmSpi {
     69     volatile uint32_t CR1;
     70     volatile uint32_t CR2;
     71     volatile uint32_t SR;
     72     volatile uint32_t DR;
     73     volatile uint32_t CRCPR;
     74     volatile uint32_t RXCRCR;
     75     volatile uint32_t TXCRCR;
     76     volatile uint32_t I2SCFGR;
     77     volatile uint32_t I2SPR;
     78 };
     79 
     80 struct StmSpiState {
     81     uint8_t bitsPerWord;
     82     uint8_t xferEnable;
     83 
     84     uint16_t rxWord;
     85     uint16_t txWord;
     86 
     87     bool rxDone;
     88     bool txDone;
     89 
     90     struct ChainedIsr isrNss;
     91 
     92     bool nssChange;
     93 };
     94 
     95 struct StmSpiCfg {
     96     struct StmSpi *regs;
     97 
     98     uint32_t clockBus;
     99     uint32_t clockUnit;
    100 
    101     IRQn_Type irq;
    102 
    103     uint8_t dmaBus;
    104 };
    105 
    106 struct StmSpiDev {
    107     struct SpiDevice *base;
    108     const struct StmSpiCfg *cfg;
    109     const struct StmSpiBoardCfg *board;
    110     struct StmSpiState state;
    111 
    112     struct Gpio *miso;
    113     struct Gpio *mosi;
    114     struct Gpio *sck;
    115     struct Gpio *nss;
    116 };
    117 
    118 static inline struct Gpio *stmSpiGpioInit(uint32_t gpioNum, enum StmGpioSpeed speed, enum StmGpioAltFunc func)
    119 {
    120     struct Gpio *gpio = gpioRequest(gpioNum);
    121 
    122     if (gpio)
    123         gpioConfigAlt(gpio, speed, GPIO_PULL_NONE, GPIO_OUT_PUSH_PULL, func);
    124 
    125     return gpio;
    126 }
    127 
    128 static inline void stmSpiDataPullMode(struct StmSpiDev *pdev, enum StmGpioSpeed dataSpeed, enum GpioPullMode dataPull)
    129 {
    130     gpioConfigAlt(pdev->miso, dataSpeed, dataPull, GPIO_OUT_PUSH_PULL, pdev->board->gpioFunc);
    131     gpioConfigAlt(pdev->mosi, dataSpeed, dataPull, GPIO_OUT_PUSH_PULL, pdev->board->gpioFunc);
    132 }
    133 
    134 static inline void stmSpiSckPullMode(struct StmSpiDev *pdev, enum StmGpioSpeed sckSpeed, enum GpioPullMode sckPull)
    135 {
    136     gpioConfigAlt(pdev->sck, sckSpeed, sckPull, GPIO_OUT_PUSH_PULL, pdev->board->gpioFunc);
    137 }
    138 
    139 static inline void stmSpiStartDma(struct StmSpiDev *pdev,
    140         const struct StmSpiDmaCfg *dmaCfg, const void *buf, uint8_t bitsPerWord,
    141         bool minc, size_t size, DmaCallbackF callback, bool rx)
    142 {
    143     struct StmSpi *regs = pdev->cfg->regs;
    144     struct dmaMode mode;
    145 
    146     memset(&mode, 0, sizeof(mode));
    147 
    148     if (bitsPerWord == 8) {
    149         mode.psize = DMA_SIZE_8_BITS;
    150         mode.msize = DMA_SIZE_8_BITS;
    151     } else {
    152         mode.psize = DMA_SIZE_16_BITS;
    153         mode.msize = DMA_SIZE_16_BITS;
    154     }
    155     mode.priority = DMA_PRIORITY_HIGH;
    156     mode.direction = rx ? DMA_DIRECTION_PERIPH_TO_MEM :
    157             DMA_DIRECTION_MEM_TO_PERIPH;
    158     mode.periphAddr = (uintptr_t)&regs->DR;
    159     mode.minc = minc;
    160     mode.channel = dmaCfg->channel;
    161 
    162     dmaStart(pdev->cfg->dmaBus, dmaCfg->stream, buf, size, &mode, callback,
    163             pdev);
    164 }
    165 
    166 static inline int stmSpiEnable(struct StmSpiDev *pdev,
    167         const struct SpiMode *mode, bool master)
    168 {
    169     struct StmSpi *regs = pdev->cfg->regs;
    170     struct StmSpiState *state = &pdev->state;
    171 
    172     if (mode->bitsPerWord != 8 &&
    173             mode->bitsPerWord != 16)
    174         return -EINVAL;
    175 
    176     unsigned int div;
    177     if (master) {
    178         if (!mode->speed)
    179             return -EINVAL;
    180 
    181         uint32_t pclk = pwrGetBusSpeed(pdev->cfg->clockBus);
    182         div = pclk / mode->speed;
    183         if (div > SPI_CR1_BR_MAX)
    184             return -EINVAL;
    185         else if (div < SPI_CR1_BR_MIN)
    186             div = SPI_CR1_BR_MIN;
    187     }
    188 
    189     atomicWriteByte(&state->xferEnable, false);
    190 
    191     state->txWord = mode->txWord;
    192     state->bitsPerWord = mode->bitsPerWord;
    193 
    194     pwrUnitClock(pdev->cfg->clockBus, pdev->cfg->clockUnit, true);
    195 
    196     if (master) {
    197         regs->CR1 &= ~SPI_CR1_BR_MASK;
    198         regs->CR1 |= SPI_CR1_BR(div);
    199     }
    200 
    201     if (mode->cpol == SPI_CPOL_IDLE_LO)
    202         regs->CR1 &= ~SPI_CR1_CPOL;
    203     else
    204         regs->CR1 |= SPI_CR1_CPOL;
    205 
    206     if (mode->cpha == SPI_CPHA_LEADING_EDGE)
    207         regs->CR1 &= ~SPI_CR1_CPHA;
    208     else
    209         regs->CR1 |= SPI_CR1_CPHA;
    210 
    211     if (mode->bitsPerWord == 8)
    212         regs->CR1 &= ~SPI_CR1_DFF;
    213     else
    214         regs->CR1 |= SPI_CR1_DFF;
    215 
    216     if (mode->format == SPI_FORMAT_MSB_FIRST)
    217         regs->CR1 &= ~SPI_CR1_LSBFIRST;
    218     else
    219         regs->CR1 |= SPI_CR1_LSBFIRST;
    220 
    221     if (master)
    222         regs->CR1 |= SPI_CR1_SSI | SPI_CR1_SSM | SPI_CR1_MSTR;
    223     else
    224         regs->CR1 &= ~(SPI_CR1_SSM | SPI_CR1_MSTR);
    225 
    226     return 0;
    227 }
    228 
    229 static int stmSpiMasterStartSync(struct SpiDevice *dev, spi_cs_t cs,
    230         const struct SpiMode *mode)
    231 {
    232     struct StmSpiDev *pdev = dev->pdata;
    233 
    234     int err = stmSpiEnable(pdev, mode, true);
    235     if (err < 0)
    236         return err;
    237 
    238     stmSpiDataPullMode(pdev, pdev->board->gpioSpeed, pdev->board->gpioPull);
    239     stmSpiSckPullMode(pdev, pdev->board->gpioSpeed, mode->cpol ? GPIO_PULL_UP : GPIO_PULL_DOWN);
    240 
    241     if (!pdev->nss)
    242         pdev->nss = gpioRequest(cs);
    243     if (!pdev->nss)
    244         return -ENODEV;
    245     gpioConfigOutput(pdev->nss, pdev->board->gpioSpeed, pdev->board->gpioPull, GPIO_OUT_PUSH_PULL, 1);
    246 
    247     return 0;
    248 }
    249 
    250 static int stmSpiSlaveStartSync(struct SpiDevice *dev,
    251         const struct SpiMode *mode)
    252 {
    253     struct StmSpiDev *pdev = dev->pdata;
    254 
    255     stmSpiDataPullMode(pdev, pdev->board->gpioSpeed, GPIO_PULL_NONE);
    256     stmSpiSckPullMode(pdev, pdev->board->gpioSpeed, GPIO_PULL_NONE);
    257 
    258     if (!pdev->nss)
    259         pdev->nss = stmSpiGpioInit(pdev->board->gpioNss, pdev->board->gpioSpeed, pdev->board->gpioFunc);
    260     if (!pdev->nss)
    261         return -ENODEV;
    262 
    263     return stmSpiEnable(pdev, mode, false);
    264 }
    265 
    266 static inline bool stmSpiIsMaster(struct StmSpiDev *pdev)
    267 {
    268     struct StmSpi *regs = pdev->cfg->regs;
    269     return !!(regs->CR1 & SPI_CR1_MSTR);
    270 }
    271 
    272 static void stmSpiDone(struct StmSpiDev *pdev, int err)
    273 {
    274     struct StmSpi *regs = pdev->cfg->regs;
    275     struct StmSpiState *state = &pdev->state;
    276 
    277     if (pdev->board->sleepDev >= 0)
    278         platReleaseDevInSleepMode(pdev->board->sleepDev);
    279 
    280     while (regs->SR & SPI_SR_BSY)
    281         ;
    282 
    283     if (stmSpiIsMaster(pdev)) {
    284         if (state->nssChange && pdev->nss)
    285             gpioSet(pdev->nss, 1);
    286         spiMasterRxTxDone(pdev->base, err);
    287     } else {
    288         regs->CR2 = SPI_CR2_TXEIE;
    289         spiSlaveRxTxDone(pdev->base, err);
    290     }
    291 }
    292 
    293 static void stmSpiRxDone(void *cookie, uint16_t bytesLeft, int err)
    294 {
    295     struct StmSpiDev *pdev = cookie;
    296     struct StmSpi *regs = pdev->cfg->regs;
    297     struct StmSpiState *state = &pdev->state;
    298 
    299     regs->CR2 &= ~SPI_CR2_RXDMAEN;
    300     state->rxDone = true;
    301 
    302     if (state->txDone) {
    303         atomicWriteByte(&state->xferEnable, false);
    304         stmSpiDone(pdev, err);
    305     }
    306 }
    307 
    308 static void stmSpiTxDone(void *cookie, uint16_t bytesLeft, int err)
    309 {
    310     struct StmSpiDev *pdev = cookie;
    311     struct StmSpi *regs = pdev->cfg->regs;
    312     struct StmSpiState *state = &pdev->state;
    313 
    314     regs->CR2 &= ~SPI_CR2_TXDMAEN;
    315     state->txDone = true;
    316 
    317     if (state->rxDone) {
    318         atomicWriteByte(&state->xferEnable, false);
    319         stmSpiDone(pdev, err);
    320     }
    321 }
    322 
    323 static int stmSpiRxTx(struct SpiDevice *dev, void *rxBuf, const void *txBuf,
    324         size_t size, const struct SpiMode *mode)
    325 {
    326     struct StmSpiDev *pdev = dev->pdata;
    327     struct StmSpi *regs = pdev->cfg->regs;
    328     struct StmSpiState *state = &pdev->state;
    329     bool rxMinc = true, txMinc = true;
    330     uint32_t cr2 = SPI_CR2_TXDMAEN;
    331 
    332     if (atomicXchgByte(&state->xferEnable, true) == true)
    333         return -EBUSY;
    334 
    335     if (stmSpiIsMaster(pdev) && pdev->nss)
    336         gpioSet(pdev->nss, 0);
    337 
    338     state->rxDone = false;
    339     state->txDone = false;
    340     state->nssChange = mode->nssChange;
    341 
    342     /* In master mode, if RX is ignored at any point, then turning it on
    343      * later may cause the SPI/DMA controllers to "receive" a stale byte
    344      * sitting in a FIFO somewhere (even when their respective registers say
    345      * their FIFOs are empty, and even if the SPI FIFO is explicitly cleared).
    346      * Work around this by DMAing bytes we don't care about into a throwaway
    347      * 1-word buffer.
    348      *
    349      * In slave mode, this specific WAR sometimes causes bigger problems
    350      * (the first byte TXed is sometimes dropped or corrupted).  Slave mode
    351      * has its own WARs below.
    352      */
    353     if (!rxBuf && stmSpiIsMaster(pdev)) {
    354         rxBuf = &state->rxWord;
    355         rxMinc = false;
    356     }
    357 
    358     if (rxBuf) {
    359         stmSpiStartDma(pdev, &pdev->board->dmaRx, rxBuf, mode->bitsPerWord,
    360                 rxMinc, size, stmSpiRxDone, true);
    361         cr2 |= SPI_CR2_RXDMAEN;
    362     } else {
    363         state->rxDone = true;
    364     }
    365 
    366     if (!txBuf) {
    367         txBuf = &state->txWord;
    368         txMinc = false;
    369     }
    370     stmSpiStartDma(pdev, &pdev->board->dmaTx, txBuf, mode->bitsPerWord, txMinc,
    371             size, stmSpiTxDone, false);
    372 
    373     /* Ensure the TXE and RXNE bits are cleared; otherwise the DMA controller
    374      * may "receive" the byte sitting in the SPI controller's FIFO right now,
    375      * or drop/corrupt the first TX byte.  Timing is crucial here, so do it
    376      * right before enabling DMA.
    377      */
    378     if (!stmSpiIsMaster(pdev)) {
    379         regs->CR2 &= ~SPI_CR2_TXEIE;
    380         NVIC_ClearPendingIRQ(pdev->cfg->irq);
    381 
    382         if (regs->SR & SPI_SR_RXNE)
    383             (void)regs->DR;
    384 
    385         if (regs->SR & SPI_SR_TXE)
    386             regs->DR = mode->txWord;
    387     }
    388 
    389     if (pdev->board->sleepDev >= 0)
    390         platRequestDevInSleepMode(pdev->board->sleepDev, 12);
    391 
    392     regs->CR2 = cr2;
    393     regs->CR1 |= SPI_CR1_SPE;
    394 
    395 
    396     return 0;
    397 }
    398 
    399 static int stmSpiSlaveIdle(struct SpiDevice *dev, const struct SpiMode *mode)
    400 {
    401     struct StmSpiDev *pdev = dev->pdata;
    402     struct StmSpi *regs = pdev->cfg->regs;
    403     struct StmSpiState *state = &pdev->state;
    404 
    405     if (atomicXchgByte(&state->xferEnable, true) == true)
    406         return -EBUSY;
    407 
    408     regs->CR2 = SPI_CR2_TXEIE;
    409     regs->CR1 |= SPI_CR1_SPE;
    410 
    411     atomicXchgByte(&state->xferEnable, false);
    412     return 0;
    413 }
    414 
    415 static inline void stmSpiDisable(struct SpiDevice *dev, bool master)
    416 {
    417     struct StmSpiDev *pdev = dev->pdata;
    418     struct StmSpi *regs = pdev->cfg->regs;
    419 
    420     while (regs->SR & SPI_SR_BSY)
    421         ;
    422 
    423     if (master) {
    424         stmSpiSckPullMode(pdev, pdev->board->gpioSpeed, pdev->board->gpioPull);
    425     }
    426 
    427     regs->CR2 &= ~(SPI_CR2_RXDMAEN | SPI_CR2_TXDMAEN | SPI_CR2_TXEIE);
    428     regs->CR1 &= ~SPI_CR1_SPE;
    429     pwrUnitClock(pdev->cfg->clockBus, pdev->cfg->clockUnit, false);
    430 }
    431 
    432 static int stmSpiMasterStopSync(struct SpiDevice *dev)
    433 {
    434     struct StmSpiDev *pdev = dev->pdata;
    435 
    436     if (pdev->nss) {
    437         gpioSet(pdev->nss, 1);
    438         gpioRelease(pdev->nss);
    439     }
    440 
    441     stmSpiDisable(dev, true);
    442     pdev->nss = NULL;
    443     return 0;
    444 }
    445 
    446 static int stmSpiSlaveStopSync(struct SpiDevice *dev)
    447 {
    448     struct StmSpiDev *pdev = dev->pdata;
    449 
    450     if (pdev->nss)
    451         gpioRelease(pdev->nss);
    452 
    453     stmSpiDisable(dev, false);
    454     pdev->nss = NULL;
    455     return 0;
    456 }
    457 
    458 static bool stmSpiExtiIsr(struct ChainedIsr *isr)
    459 {
    460     struct StmSpiState *state = container_of(isr, struct StmSpiState, isrNss);
    461     struct StmSpiDev *pdev = container_of(state, struct StmSpiDev, state);
    462 
    463     if (pdev->nss && !extiIsPendingGpio(pdev->nss))
    464         return false;
    465 
    466     spiSlaveCsInactive(pdev->base);
    467     if (pdev->nss)
    468         extiClearPendingGpio(pdev->nss);
    469     return true;
    470 }
    471 
    472 static void stmSpiSlaveSetCsInterrupt(struct SpiDevice *dev, bool enabled)
    473 {
    474     struct StmSpiDev *pdev = dev->pdata;
    475     struct ChainedIsr *isr = &pdev->state.isrNss;
    476 
    477     if (enabled) {
    478         isr->func = stmSpiExtiIsr;
    479 
    480         if (pdev->nss) {
    481             syscfgSetExtiPort(pdev->nss);
    482             extiEnableIntGpio(pdev->nss, EXTI_TRIGGER_RISING);
    483         }
    484         extiChainIsr(pdev->board->irqNss, isr);
    485     } else {
    486         extiUnchainIsr(pdev->board->irqNss, isr);
    487         if (pdev->nss)
    488             extiDisableIntGpio(pdev->nss);
    489     }
    490 }
    491 
    492 static bool stmSpiSlaveCsIsActive(struct SpiDevice *dev)
    493 {
    494     struct StmSpiDev *pdev = dev->pdata;
    495     return pdev->nss && !gpioGet(pdev->nss);
    496 }
    497 
    498 static inline void stmSpiTxe(struct StmSpiDev *pdev)
    499 {
    500     struct StmSpi *regs = pdev->cfg->regs;
    501 
    502     /**
    503      * n.b.: if nothing handles the TXE interrupt in slave mode, the SPI
    504      * controller will just keep reading the existing value from DR anytime it
    505      * needs data
    506      */
    507     regs->DR = pdev->state.txWord;
    508     regs->CR2 &= ~SPI_CR2_TXEIE;
    509 }
    510 
    511 static void stmSpiIsr(struct StmSpiDev *pdev)
    512 {
    513     struct StmSpi *regs = pdev->cfg->regs;
    514 
    515     if (regs->SR & SPI_SR_TXE) {
    516         stmSpiTxe(pdev);
    517     }
    518 
    519     /* TODO: error conditions */
    520 }
    521 
    522 static int stmSpiRelease(struct SpiDevice *dev)
    523 {
    524     struct StmSpiDev *pdev = dev->pdata;
    525 
    526     NVIC_DisableIRQ(pdev->cfg->irq);
    527 
    528     pdev->base = NULL;
    529     return 0;
    530 }
    531 
    532 #define DECLARE_IRQ_HANDLER(_n)             \
    533     void SPI##_n##_IRQHandler();            \
    534     void SPI##_n##_IRQHandler()             \
    535     {                                       \
    536         stmSpiIsr(&mStmSpiDevs[_n - 1]); \
    537     }
    538 
    539 const struct SpiDevice_ops mStmSpiOps = {
    540     .masterStartSync = stmSpiMasterStartSync,
    541     .masterRxTx = stmSpiRxTx,
    542     .masterStopSync = stmSpiMasterStopSync,
    543 
    544     .slaveStartSync = stmSpiSlaveStartSync,
    545     .slaveIdle = stmSpiSlaveIdle,
    546     .slaveRxTx = stmSpiRxTx,
    547     .slaveStopSync = stmSpiSlaveStopSync,
    548 
    549     .slaveSetCsInterrupt = stmSpiSlaveSetCsInterrupt,
    550     .slaveCsIsActive = stmSpiSlaveCsIsActive,
    551 
    552     .release = stmSpiRelease,
    553 };
    554 
    555 static const struct StmSpiCfg mStmSpiCfgs[] = {
    556     [0] = {
    557         .regs = (struct StmSpi *)SPI1_BASE,
    558 
    559         .clockBus = PERIPH_BUS_APB2,
    560         .clockUnit = PERIPH_APB2_SPI1,
    561 
    562         .irq = SPI1_IRQn,
    563 
    564         .dmaBus = SPI1_DMA_BUS,
    565     },
    566     [1] = {
    567         .regs = (struct StmSpi *)SPI2_BASE,
    568 
    569         .clockBus = PERIPH_BUS_APB1,
    570         .clockUnit = PERIPH_APB1_SPI2,
    571 
    572         .irq = SPI2_IRQn,
    573 
    574         .dmaBus = SPI2_DMA_BUS,
    575     },
    576     [2] = {
    577         .regs = (struct StmSpi *)SPI3_BASE,
    578 
    579         .clockBus = PERIPH_BUS_APB1,
    580         .clockUnit = PERIPH_APB1_SPI3,
    581 
    582         .irq = SPI3_IRQn,
    583 
    584         .dmaBus = SPI3_DMA_BUS,
    585     },
    586 };
    587 
    588 static struct StmSpiDev mStmSpiDevs[ARRAY_SIZE(mStmSpiCfgs)];
    589 DECLARE_IRQ_HANDLER(1)
    590 DECLARE_IRQ_HANDLER(2)
    591 DECLARE_IRQ_HANDLER(3)
    592 
    593 static void stmSpiInit(struct StmSpiDev *pdev, const struct StmSpiCfg *cfg,
    594         const struct StmSpiBoardCfg *board, struct SpiDevice *dev)
    595 {
    596     pdev->miso = stmSpiGpioInit(board->gpioMiso, board->gpioSpeed, board->gpioFunc);
    597     pdev->mosi = stmSpiGpioInit(board->gpioMosi, board->gpioSpeed, board->gpioFunc);
    598     pdev->sck = stmSpiGpioInit(board->gpioSclk, board->gpioSpeed, board->gpioFunc);
    599 
    600     NVIC_EnableIRQ(cfg->irq);
    601 
    602     pdev->base = dev;
    603     pdev->cfg = cfg;
    604     pdev->board = board;
    605 }
    606 
    607 int spiRequest(struct SpiDevice *dev, uint8_t busId)
    608 {
    609     if (busId >= ARRAY_SIZE(mStmSpiDevs))
    610         return -ENODEV;
    611 
    612     const struct StmSpiBoardCfg *board = boardStmSpiCfg(busId);
    613     if (!board)
    614         return -ENODEV;
    615 
    616     struct StmSpiDev *pdev = &mStmSpiDevs[busId];
    617     const struct StmSpiCfg *cfg = &mStmSpiCfgs[busId];
    618     if (!pdev->base)
    619         stmSpiInit(pdev, cfg, board, dev);
    620 
    621     memset(&pdev->state, 0, sizeof(pdev->state));
    622     dev->ops = &mStmSpiOps;
    623     dev->pdata = pdev;
    624     return 0;
    625 }
    626 
    627 const enum IRQn spiRxIrq(uint8_t busId)
    628 {
    629     if (busId >= ARRAY_SIZE(mStmSpiDevs))
    630         return -ENODEV;
    631 
    632     struct StmSpiDev *pdev = &mStmSpiDevs[busId];
    633 
    634     return dmaIrq(pdev->cfg->dmaBus, pdev->board->dmaRx.stream);
    635 }
    636 
    637 const enum IRQn spiTxIrq(uint8_t busId)
    638 {
    639     if (busId >= ARRAY_SIZE(mStmSpiDevs))
    640         return -ENODEV;
    641 
    642     struct StmSpiDev *pdev = &mStmSpiDevs[busId];
    643 
    644     return dmaIrq(pdev->cfg->dmaBus, pdev->board->dmaTx.stream);
    645 }
    646