Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Fork of mbed-dev by
targets/TARGET_NUVOTON/TARGET_NUC472/spi_api.c@161:2cc1468da177, 2017-03-30 (annotated)
- Committer:
- <>
- Date:
- Thu Mar 30 13:45:57 2017 +0100
- Revision:
- 161:2cc1468da177
- Parent:
- 153:fa9ff456f731
This updates the lib to the mbed lib v139
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
<> | 149:156823d33999 | 1 | /* mbed Microcontroller Library |
<> | 149:156823d33999 | 2 | * Copyright (c) 2015-2016 Nuvoton |
<> | 149:156823d33999 | 3 | * |
<> | 149:156823d33999 | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
<> | 149:156823d33999 | 5 | * you may not use this file except in compliance with the License. |
<> | 149:156823d33999 | 6 | * You may obtain a copy of the License at |
<> | 149:156823d33999 | 7 | * |
<> | 149:156823d33999 | 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
<> | 149:156823d33999 | 9 | * |
<> | 149:156823d33999 | 10 | * Unless required by applicable law or agreed to in writing, software |
<> | 149:156823d33999 | 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
<> | 149:156823d33999 | 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
<> | 149:156823d33999 | 13 | * See the License for the specific language governing permissions and |
<> | 149:156823d33999 | 14 | * limitations under the License. |
<> | 149:156823d33999 | 15 | */ |
<> | 149:156823d33999 | 16 | |
<> | 149:156823d33999 | 17 | #include "spi_api.h" |
<> | 149:156823d33999 | 18 | |
<> | 149:156823d33999 | 19 | #if DEVICE_SPI |
<> | 149:156823d33999 | 20 | |
<> | 149:156823d33999 | 21 | #include "cmsis.h" |
<> | 149:156823d33999 | 22 | #include "pinmap.h" |
<> | 149:156823d33999 | 23 | #include "PeripheralPins.h" |
<> | 149:156823d33999 | 24 | #include "nu_modutil.h" |
<> | 149:156823d33999 | 25 | #include "nu_miscutil.h" |
<> | 149:156823d33999 | 26 | #include "nu_bitutil.h" |
<> | 149:156823d33999 | 27 | |
<> | 149:156823d33999 | 28 | #if DEVICE_SPI_ASYNCH |
<> | 149:156823d33999 | 29 | #include "dma_api.h" |
<> | 149:156823d33999 | 30 | #include "dma.h" |
<> | 149:156823d33999 | 31 | #endif |
<> | 149:156823d33999 | 32 | |
<> | 149:156823d33999 | 33 | #define NU_SPI_FRAME_MIN 8 |
<> | 149:156823d33999 | 34 | #define NU_SPI_FRAME_MAX 32 |
<> | 149:156823d33999 | 35 | #define NU_SPI_FIFO_DEPTH 8 |
<> | 149:156823d33999 | 36 | |
<> | 149:156823d33999 | 37 | struct nu_spi_var { |
<> | 149:156823d33999 | 38 | #if DEVICE_SPI_ASYNCH |
<> | 149:156823d33999 | 39 | uint8_t pdma_perp_tx; |
<> | 149:156823d33999 | 40 | uint8_t pdma_perp_rx; |
<> | 149:156823d33999 | 41 | #endif |
<> | 149:156823d33999 | 42 | }; |
<> | 149:156823d33999 | 43 | |
<> | 149:156823d33999 | 44 | static struct nu_spi_var spi0_var = { |
<> | 149:156823d33999 | 45 | #if DEVICE_SPI_ASYNCH |
<> | 149:156823d33999 | 46 | .pdma_perp_tx = PDMA_SPI0_TX, |
<> | 149:156823d33999 | 47 | .pdma_perp_rx = PDMA_SPI0_RX |
<> | 149:156823d33999 | 48 | #endif |
<> | 149:156823d33999 | 49 | }; |
<> | 149:156823d33999 | 50 | static struct nu_spi_var spi1_var = { |
<> | 149:156823d33999 | 51 | #if DEVICE_SPI_ASYNCH |
<> | 149:156823d33999 | 52 | .pdma_perp_tx = PDMA_SPI1_TX, |
<> | 149:156823d33999 | 53 | .pdma_perp_rx = PDMA_SPI1_RX |
<> | 149:156823d33999 | 54 | #endif |
<> | 149:156823d33999 | 55 | }; |
<> | 149:156823d33999 | 56 | static struct nu_spi_var spi2_var = { |
<> | 149:156823d33999 | 57 | #if DEVICE_SPI_ASYNCH |
<> | 149:156823d33999 | 58 | .pdma_perp_tx = PDMA_SPI2_TX, |
<> | 149:156823d33999 | 59 | .pdma_perp_rx = PDMA_SPI2_RX |
<> | 149:156823d33999 | 60 | #endif |
<> | 149:156823d33999 | 61 | }; |
<> | 149:156823d33999 | 62 | static struct nu_spi_var spi3_var = { |
<> | 149:156823d33999 | 63 | #if DEVICE_SPI_ASYNCH |
<> | 149:156823d33999 | 64 | .pdma_perp_tx = PDMA_SPI3_TX, |
<> | 149:156823d33999 | 65 | .pdma_perp_rx = PDMA_SPI3_RX |
<> | 149:156823d33999 | 66 | #endif |
<> | 149:156823d33999 | 67 | }; |
<> | 149:156823d33999 | 68 | |
<> | 149:156823d33999 | 69 | #if DEVICE_SPI_ASYNCH |
<> | 149:156823d33999 | 70 | static void spi_enable_vector_interrupt(spi_t *obj, uint32_t handler, uint8_t enable); |
<> | 149:156823d33999 | 71 | static void spi_master_enable_interrupt(spi_t *obj, uint8_t enable); |
<> | 149:156823d33999 | 72 | static uint32_t spi_master_write_asynch(spi_t *obj, uint32_t tx_limit); |
<> | 149:156823d33999 | 73 | static uint32_t spi_master_read_asynch(spi_t *obj); |
<> | 149:156823d33999 | 74 | static uint32_t spi_event_check(spi_t *obj); |
<> | 149:156823d33999 | 75 | static void spi_enable_event(spi_t *obj, uint32_t event, uint8_t enable); |
<> | 149:156823d33999 | 76 | static void spi_buffer_set(spi_t *obj, const void *tx, size_t tx_length, void *rx, size_t rx_length); |
<> | 149:156823d33999 | 77 | static void spi_check_dma_usage(DMAUsage *dma_usage, int *dma_ch_tx, int *dma_ch_rx); |
<> | 149:156823d33999 | 78 | static uint8_t spi_get_data_width(spi_t *obj); |
<> | 149:156823d33999 | 79 | static int spi_is_tx_complete(spi_t *obj); |
<> | 149:156823d33999 | 80 | static int spi_is_rx_complete(spi_t *obj); |
<> | 149:156823d33999 | 81 | static int spi_writeable(spi_t * obj); |
<> | 149:156823d33999 | 82 | static int spi_readable(spi_t * obj); |
<> | 149:156823d33999 | 83 | static void spi_dma_handler_tx(uint32_t id, uint32_t event_dma); |
<> | 149:156823d33999 | 84 | static void spi_dma_handler_rx(uint32_t id, uint32_t event_dma); |
<> | 149:156823d33999 | 85 | #endif |
<> | 149:156823d33999 | 86 | |
<> | 149:156823d33999 | 87 | static uint32_t spi_modinit_mask = 0; |
<> | 149:156823d33999 | 88 | |
<> | 149:156823d33999 | 89 | static const struct nu_modinit_s spi_modinit_tab[] = { |
<> | 149:156823d33999 | 90 | {SPI_0, SPI0_MODULE, 0, 0, SPI0_RST, SPI0_IRQn, &spi0_var}, |
<> | 149:156823d33999 | 91 | {SPI_1, SPI1_MODULE, 0, 0, SPI1_RST, SPI1_IRQn, &spi1_var}, |
<> | 149:156823d33999 | 92 | {SPI_2, SPI2_MODULE, 0, 0, SPI2_RST, SPI2_IRQn, &spi2_var}, |
<> | 149:156823d33999 | 93 | {SPI_3, SPI3_MODULE, 0, 0, SPI3_RST, SPI3_IRQn, &spi3_var}, |
<> | 149:156823d33999 | 94 | |
<> | 149:156823d33999 | 95 | {NC, 0, 0, 0, 0, (IRQn_Type) 0, NULL} |
<> | 149:156823d33999 | 96 | }; |
<> | 149:156823d33999 | 97 | |
<> | 149:156823d33999 | 98 | void spi_init(spi_t *obj, PinName mosi, PinName miso, PinName sclk, PinName ssel) { |
<> | 149:156823d33999 | 99 | // Determine which SPI_x the pins are used for |
<> | 149:156823d33999 | 100 | uint32_t spi_mosi = pinmap_peripheral(mosi, PinMap_SPI_MOSI); |
<> | 149:156823d33999 | 101 | uint32_t spi_miso = pinmap_peripheral(miso, PinMap_SPI_MISO); |
<> | 149:156823d33999 | 102 | uint32_t spi_sclk = pinmap_peripheral(sclk, PinMap_SPI_SCLK); |
<> | 149:156823d33999 | 103 | uint32_t spi_ssel = pinmap_peripheral(ssel, PinMap_SPI_SSEL); |
<> | 149:156823d33999 | 104 | uint32_t spi_data = pinmap_merge(spi_mosi, spi_miso); |
<> | 149:156823d33999 | 105 | uint32_t spi_cntl = pinmap_merge(spi_sclk, spi_ssel); |
<> | 149:156823d33999 | 106 | obj->spi.spi = (SPIName) pinmap_merge(spi_data, spi_cntl); |
<> | 149:156823d33999 | 107 | MBED_ASSERT((int)obj->spi.spi != NC); |
<> | 149:156823d33999 | 108 | |
<> | 149:156823d33999 | 109 | const struct nu_modinit_s *modinit = get_modinit(obj->spi.spi, spi_modinit_tab); |
<> | 149:156823d33999 | 110 | MBED_ASSERT(modinit != NULL); |
<> | 149:156823d33999 | 111 | MBED_ASSERT(modinit->modname == obj->spi.spi); |
<> | 149:156823d33999 | 112 | |
<> | 149:156823d33999 | 113 | // Reset this module |
<> | 149:156823d33999 | 114 | SYS_ResetModule(modinit->rsetidx); |
<> | 149:156823d33999 | 115 | |
<> | 149:156823d33999 | 116 | // Enable IP clock |
<> | 149:156823d33999 | 117 | CLK_EnableModuleClock(modinit->clkidx); |
<> | 149:156823d33999 | 118 | |
<> | 149:156823d33999 | 119 | //SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi); |
<> | 149:156823d33999 | 120 | |
<> | 149:156823d33999 | 121 | pinmap_pinout(mosi, PinMap_SPI_MOSI); |
<> | 149:156823d33999 | 122 | pinmap_pinout(miso, PinMap_SPI_MISO); |
<> | 149:156823d33999 | 123 | pinmap_pinout(sclk, PinMap_SPI_SCLK); |
<> | 149:156823d33999 | 124 | pinmap_pinout(ssel, PinMap_SPI_SSEL); |
<> | 149:156823d33999 | 125 | |
<> | 149:156823d33999 | 126 | obj->spi.pin_mosi = mosi; |
<> | 149:156823d33999 | 127 | obj->spi.pin_miso = miso; |
<> | 149:156823d33999 | 128 | obj->spi.pin_sclk = sclk; |
<> | 149:156823d33999 | 129 | obj->spi.pin_ssel = ssel; |
<> | 149:156823d33999 | 130 | |
<> | 149:156823d33999 | 131 | // Configure the SPI data format and frequency |
<> | 149:156823d33999 | 132 | //spi_format(obj, 8, 0, SPI_MSB); // 8 bits, mode 0 |
<> | 149:156823d33999 | 133 | //spi_frequency(obj, 1000000); |
<> | 149:156823d33999 | 134 | |
<> | 149:156823d33999 | 135 | #if DEVICE_SPI_ASYNCH |
<> | 149:156823d33999 | 136 | obj->spi.dma_usage = DMA_USAGE_NEVER; |
<> | 149:156823d33999 | 137 | obj->spi.event = 0; |
<> | 149:156823d33999 | 138 | obj->spi.dma_chn_id_tx = DMA_ERROR_OUT_OF_CHANNELS; |
<> | 149:156823d33999 | 139 | obj->spi.dma_chn_id_rx = DMA_ERROR_OUT_OF_CHANNELS; |
<> | 149:156823d33999 | 140 | #endif |
<> | 149:156823d33999 | 141 | |
<> | 149:156823d33999 | 142 | // Mark this module to be inited. |
<> | 149:156823d33999 | 143 | int i = modinit - spi_modinit_tab; |
<> | 149:156823d33999 | 144 | spi_modinit_mask |= 1 << i; |
<> | 149:156823d33999 | 145 | } |
<> | 149:156823d33999 | 146 | |
<> | 149:156823d33999 | 147 | void spi_free(spi_t *obj) |
<> | 149:156823d33999 | 148 | { |
<> | 149:156823d33999 | 149 | #if DEVICE_SPI_ASYNCH |
<> | 149:156823d33999 | 150 | if (obj->spi.dma_chn_id_tx != DMA_ERROR_OUT_OF_CHANNELS) { |
<> | 149:156823d33999 | 151 | dma_channel_free(obj->spi.dma_chn_id_tx); |
<> | 149:156823d33999 | 152 | obj->spi.dma_chn_id_tx = DMA_ERROR_OUT_OF_CHANNELS; |
<> | 149:156823d33999 | 153 | } |
<> | 149:156823d33999 | 154 | if (obj->spi.dma_chn_id_rx != DMA_ERROR_OUT_OF_CHANNELS) { |
<> | 149:156823d33999 | 155 | dma_channel_free(obj->spi.dma_chn_id_rx); |
<> | 149:156823d33999 | 156 | obj->spi.dma_chn_id_rx = DMA_ERROR_OUT_OF_CHANNELS; |
<> | 149:156823d33999 | 157 | } |
<> | 149:156823d33999 | 158 | #endif |
<> | 149:156823d33999 | 159 | |
<> | 149:156823d33999 | 160 | SPI_Close((SPI_T *) NU_MODBASE(obj->spi.spi)); |
<> | 149:156823d33999 | 161 | |
<> | 149:156823d33999 | 162 | const struct nu_modinit_s *modinit = get_modinit(obj->spi.spi, spi_modinit_tab); |
<> | 149:156823d33999 | 163 | MBED_ASSERT(modinit != NULL); |
<> | 149:156823d33999 | 164 | MBED_ASSERT(modinit->modname == obj->spi.spi); |
<> | 149:156823d33999 | 165 | |
<> | 149:156823d33999 | 166 | SPI_DisableInt(((SPI_T *) NU_MODBASE(obj->spi.spi)), (SPI_FIFO_RXOVIEN_MASK | SPI_FIFO_RXTHIEN_MASK | SPI_FIFO_TXTHIEN_MASK)); |
<> | 149:156823d33999 | 167 | NVIC_DisableIRQ(modinit->irq_n); |
<> | 149:156823d33999 | 168 | |
<> | 149:156823d33999 | 169 | // Disable IP clock |
<> | 149:156823d33999 | 170 | CLK_DisableModuleClock(modinit->clkidx); |
<> | 149:156823d33999 | 171 | |
<> | 149:156823d33999 | 172 | //((struct nu_spi_var *) modinit->var)->obj = NULL; |
<> | 149:156823d33999 | 173 | |
<> | 149:156823d33999 | 174 | // Mark this module to be deinited. |
<> | 149:156823d33999 | 175 | int i = modinit - spi_modinit_tab; |
<> | 149:156823d33999 | 176 | spi_modinit_mask &= ~(1 << i); |
<> | 149:156823d33999 | 177 | } |
<> | 149:156823d33999 | 178 | void spi_format(spi_t *obj, int bits, int mode, int slave) |
<> | 149:156823d33999 | 179 | { |
<> | 149:156823d33999 | 180 | MBED_ASSERT(bits >= NU_SPI_FRAME_MIN && bits <= NU_SPI_FRAME_MAX); |
<> | 149:156823d33999 | 181 | |
<> | 149:156823d33999 | 182 | SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi); |
<> | 149:156823d33999 | 183 | |
<> | 149:156823d33999 | 184 | // NOTE 1: All configurations should be ready before enabling SPI peripheral. |
<> | 149:156823d33999 | 185 | // NOTE 2: Re-configuration is allowed only as SPI peripheral is idle. |
<> | 149:156823d33999 | 186 | while (SPI_IS_BUSY(spi_base)); |
<> | 149:156823d33999 | 187 | SPI_DISABLE(spi_base); |
<> | 149:156823d33999 | 188 | |
<> | 149:156823d33999 | 189 | SPI_Open(spi_base, |
<> | 149:156823d33999 | 190 | slave ? SPI_SLAVE : SPI_MASTER, |
<> | 149:156823d33999 | 191 | (mode == 0) ? SPI_MODE_0 : (mode == 1) ? SPI_MODE_1 : (mode == 2) ? SPI_MODE_2 : SPI_MODE_3, |
<> | 149:156823d33999 | 192 | bits, |
<> | 149:156823d33999 | 193 | SPI_GetBusClock(spi_base)); |
<> | 149:156823d33999 | 194 | // NOTE: Hardcode to be MSB first. |
<> | 149:156823d33999 | 195 | SPI_SET_MSB_FIRST(spi_base); |
<> | 149:156823d33999 | 196 | |
<> | 149:156823d33999 | 197 | if (! slave) { |
<> | 149:156823d33999 | 198 | // Master |
<> | 149:156823d33999 | 199 | if (obj->spi.pin_ssel != NC) { |
<> | 149:156823d33999 | 200 | // Configure SS as low active. |
<> | 149:156823d33999 | 201 | SPI_EnableAutoSS(spi_base, SPI_SS0, SPI_SS_ACTIVE_LOW); |
<> | 149:156823d33999 | 202 | // NOTE: In NUC472 series, all SPI SS pins are SS0, so we can hardcode SS0 here. |
<> | 149:156823d33999 | 203 | } |
<> | 149:156823d33999 | 204 | else { |
<> | 149:156823d33999 | 205 | SPI_DisableAutoSS(spi_base); |
<> | 149:156823d33999 | 206 | } |
<> | 149:156823d33999 | 207 | } |
<> | 149:156823d33999 | 208 | else { |
<> | 149:156823d33999 | 209 | // Slave |
<> | 149:156823d33999 | 210 | // Configure SS as low active. |
<> | 149:156823d33999 | 211 | spi_base->SSCTL &= ~SPI_SSCTL_SSACTPOL_Msk; |
<> | 149:156823d33999 | 212 | // NOTE: SPI_SS0 is defined as the slave select input in Slave mode. |
<> | 149:156823d33999 | 213 | } |
<> | 149:156823d33999 | 214 | } |
<> | 149:156823d33999 | 215 | |
<> | 149:156823d33999 | 216 | void spi_frequency(spi_t *obj, int hz) |
<> | 149:156823d33999 | 217 | { |
<> | 149:156823d33999 | 218 | SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi); |
<> | 149:156823d33999 | 219 | |
<> | 149:156823d33999 | 220 | while (SPI_IS_BUSY(spi_base)); |
<> | 149:156823d33999 | 221 | SPI_DISABLE(spi_base); |
<> | 149:156823d33999 | 222 | |
<> | 149:156823d33999 | 223 | SPI_SetBusClock((SPI_T *) NU_MODBASE(obj->spi.spi), hz); |
<> | 149:156823d33999 | 224 | } |
<> | 149:156823d33999 | 225 | |
<> | 149:156823d33999 | 226 | |
<> | 149:156823d33999 | 227 | int spi_master_write(spi_t *obj, int value) |
<> | 149:156823d33999 | 228 | { |
<> | 149:156823d33999 | 229 | SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi); |
<> | 149:156823d33999 | 230 | |
<> | 149:156823d33999 | 231 | // NOTE: Data in receive FIFO can be read out via ICE. |
<> | 149:156823d33999 | 232 | SPI_ENABLE(spi_base); |
<> | 149:156823d33999 | 233 | |
<> | 149:156823d33999 | 234 | // Wait for tx buffer empty |
<> | 149:156823d33999 | 235 | while(! spi_writeable(obj)); |
<> | 149:156823d33999 | 236 | SPI_WRITE_TX(spi_base, value); |
<> | 149:156823d33999 | 237 | |
<> | 149:156823d33999 | 238 | // Wait for rx buffer full |
<> | 149:156823d33999 | 239 | while (! spi_readable(obj)); |
<> | 149:156823d33999 | 240 | int value2 = SPI_READ_RX(spi_base); |
<> | 149:156823d33999 | 241 | |
<> | 149:156823d33999 | 242 | SPI_DISABLE(spi_base); |
<> | 149:156823d33999 | 243 | |
<> | 149:156823d33999 | 244 | return value2; |
<> | 149:156823d33999 | 245 | } |
<> | 149:156823d33999 | 246 | |
<> | 149:156823d33999 | 247 | #if DEVICE_SPISLAVE |
<> | 149:156823d33999 | 248 | int spi_slave_receive(spi_t *obj) |
<> | 149:156823d33999 | 249 | { |
<> | 149:156823d33999 | 250 | SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi); |
<> | 149:156823d33999 | 251 | |
<> | 149:156823d33999 | 252 | SPI_ENABLE(spi_base); |
<> | 149:156823d33999 | 253 | |
<> | 149:156823d33999 | 254 | return spi_readable(obj); |
<> | 149:156823d33999 | 255 | }; |
<> | 149:156823d33999 | 256 | |
<> | 149:156823d33999 | 257 | int spi_slave_read(spi_t *obj) |
<> | 149:156823d33999 | 258 | { |
<> | 149:156823d33999 | 259 | SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi); |
<> | 149:156823d33999 | 260 | |
<> | 149:156823d33999 | 261 | SPI_ENABLE(spi_base); |
<> | 149:156823d33999 | 262 | |
<> | 149:156823d33999 | 263 | // Wait for rx buffer full |
<> | 149:156823d33999 | 264 | while (! spi_readable(obj)); |
<> | 149:156823d33999 | 265 | int value = SPI_READ_RX(spi_base); |
<> | 149:156823d33999 | 266 | return value; |
<> | 149:156823d33999 | 267 | } |
<> | 149:156823d33999 | 268 | |
<> | 149:156823d33999 | 269 | void spi_slave_write(spi_t *obj, int value) |
<> | 149:156823d33999 | 270 | { |
<> | 149:156823d33999 | 271 | SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi); |
<> | 149:156823d33999 | 272 | |
<> | 149:156823d33999 | 273 | SPI_ENABLE(spi_base); |
<> | 149:156823d33999 | 274 | |
<> | 149:156823d33999 | 275 | // Wait for tx buffer empty |
<> | 149:156823d33999 | 276 | while(! spi_writeable(obj)); |
<> | 149:156823d33999 | 277 | SPI_WRITE_TX(spi_base, value); |
<> | 149:156823d33999 | 278 | } |
<> | 149:156823d33999 | 279 | #endif |
<> | 149:156823d33999 | 280 | |
<> | 149:156823d33999 | 281 | #if DEVICE_SPI_ASYNCH |
<> | 149:156823d33999 | 282 | void spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx, size_t rx_length, uint8_t bit_width, uint32_t handler, uint32_t event, DMAUsage hint) |
<> | 149:156823d33999 | 283 | { |
<> | 149:156823d33999 | 284 | //MBED_ASSERT(bits >= NU_SPI_FRAME_MIN && bits <= NU_SPI_FRAME_MAX); |
<> | 149:156823d33999 | 285 | SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi); |
<> | 149:156823d33999 | 286 | SPI_SET_DATA_WIDTH(spi_base, bit_width); |
<> | 149:156823d33999 | 287 | |
<> | 149:156823d33999 | 288 | obj->spi.dma_usage = hint; |
<> | 149:156823d33999 | 289 | spi_check_dma_usage(&obj->spi.dma_usage, &obj->spi.dma_chn_id_tx, &obj->spi.dma_chn_id_rx); |
<> | 149:156823d33999 | 290 | uint32_t data_width = spi_get_data_width(obj); |
<> | 149:156823d33999 | 291 | // Conditions to go DMA way: |
<> | 149:156823d33999 | 292 | // (1) No DMA support for non-8 multiple data width. |
<> | 149:156823d33999 | 293 | // (2) tx length >= rx length. Otherwise, as tx DMA is done, no bus activity for remaining rx. |
<> | 149:156823d33999 | 294 | if ((data_width % 8) || |
<> | 149:156823d33999 | 295 | (tx_length < rx_length)) { |
<> | 149:156823d33999 | 296 | obj->spi.dma_usage = DMA_USAGE_NEVER; |
<> | 149:156823d33999 | 297 | dma_channel_free(obj->spi.dma_chn_id_tx); |
<> | 149:156823d33999 | 298 | obj->spi.dma_chn_id_tx = DMA_ERROR_OUT_OF_CHANNELS; |
<> | 149:156823d33999 | 299 | dma_channel_free(obj->spi.dma_chn_id_rx); |
<> | 149:156823d33999 | 300 | obj->spi.dma_chn_id_rx = DMA_ERROR_OUT_OF_CHANNELS; |
<> | 149:156823d33999 | 301 | } |
<> | 149:156823d33999 | 302 | |
<> | 149:156823d33999 | 303 | // SPI IRQ is necessary for both interrupt way and DMA way |
<> | 149:156823d33999 | 304 | spi_enable_event(obj, event, 1); |
<> | 149:156823d33999 | 305 | spi_buffer_set(obj, tx, tx_length, rx, rx_length); |
<> | 149:156823d33999 | 306 | |
<> | 149:156823d33999 | 307 | SPI_ENABLE(spi_base); |
<> | 149:156823d33999 | 308 | |
<> | 149:156823d33999 | 309 | if (obj->spi.dma_usage == DMA_USAGE_NEVER) { |
<> | 149:156823d33999 | 310 | // Interrupt way |
<> | 149:156823d33999 | 311 | spi_master_write_asynch(obj, NU_SPI_FIFO_DEPTH / 2); |
<> | 149:156823d33999 | 312 | spi_enable_vector_interrupt(obj, handler, 1); |
<> | 149:156823d33999 | 313 | spi_master_enable_interrupt(obj, 1); |
<> | 149:156823d33999 | 314 | } else { |
<> | 149:156823d33999 | 315 | // DMA way |
<> | 149:156823d33999 | 316 | const struct nu_modinit_s *modinit = get_modinit(obj->spi.spi, spi_modinit_tab); |
<> | 149:156823d33999 | 317 | MBED_ASSERT(modinit != NULL); |
<> | 149:156823d33999 | 318 | MBED_ASSERT(modinit->modname == obj->spi.spi); |
<> | 149:156823d33999 | 319 | |
<> | 161:2cc1468da177 | 320 | PDMA_T *pdma_base = dma_modbase(); |
<> | 161:2cc1468da177 | 321 | |
<> | 149:156823d33999 | 322 | // Configure tx DMA |
<> | 161:2cc1468da177 | 323 | pdma_base->CHCTL |= 1 << obj->spi.dma_chn_id_tx; // Enable this DMA channel |
<> | 149:156823d33999 | 324 | PDMA_SetTransferMode(obj->spi.dma_chn_id_tx, |
<> | 149:156823d33999 | 325 | ((struct nu_spi_var *) modinit->var)->pdma_perp_tx, // Peripheral connected to this PDMA |
<> | 149:156823d33999 | 326 | 0, // Scatter-gather disabled |
<> | 149:156823d33999 | 327 | 0); // Scatter-gather descriptor address |
<> | 149:156823d33999 | 328 | PDMA_SetTransferCnt(obj->spi.dma_chn_id_tx, |
<> | 149:156823d33999 | 329 | (data_width == 8) ? PDMA_WIDTH_8 : (data_width == 16) ? PDMA_WIDTH_16 : PDMA_WIDTH_32, |
<> | 149:156823d33999 | 330 | tx_length); |
<> | 149:156823d33999 | 331 | PDMA_SetTransferAddr(obj->spi.dma_chn_id_tx, |
<> | 149:156823d33999 | 332 | ((uint32_t) tx) + (data_width / 8) * tx_length, // NOTE: End of source address |
<> | 149:156823d33999 | 333 | PDMA_SAR_INC, // Source address incremental |
<> | 149:156823d33999 | 334 | (uint32_t) &spi_base->TX, // Destination address |
<> | 149:156823d33999 | 335 | PDMA_DAR_FIX); // Destination address fixed |
<> | 149:156823d33999 | 336 | PDMA_SetBurstType(obj->spi.dma_chn_id_tx, |
<> | 149:156823d33999 | 337 | PDMA_REQ_SINGLE, // Single mode |
<> | 149:156823d33999 | 338 | 0); // Burst size |
<> | 149:156823d33999 | 339 | PDMA_EnableInt(obj->spi.dma_chn_id_tx, |
<> | 149:156823d33999 | 340 | 0); // Interrupt type. No use here |
<> | 149:156823d33999 | 341 | // Register DMA event handler |
<> | 149:156823d33999 | 342 | dma_set_handler(obj->spi.dma_chn_id_tx, (uint32_t) spi_dma_handler_tx, (uint32_t) obj, DMA_EVENT_ALL); |
<> | 149:156823d33999 | 343 | |
<> | 149:156823d33999 | 344 | // Configure rx DMA |
<> | 161:2cc1468da177 | 345 | pdma_base->CHCTL |= 1 << obj->spi.dma_chn_id_rx; // Enable this DMA channel |
<> | 149:156823d33999 | 346 | PDMA_SetTransferMode(obj->spi.dma_chn_id_rx, |
<> | 149:156823d33999 | 347 | ((struct nu_spi_var *) modinit->var)->pdma_perp_rx, // Peripheral connected to this PDMA |
<> | 149:156823d33999 | 348 | 0, // Scatter-gather disabled |
<> | 149:156823d33999 | 349 | 0); // Scatter-gather descriptor address |
<> | 149:156823d33999 | 350 | PDMA_SetTransferCnt(obj->spi.dma_chn_id_rx, |
<> | 149:156823d33999 | 351 | (data_width == 8) ? PDMA_WIDTH_8 : (data_width == 16) ? PDMA_WIDTH_16 : PDMA_WIDTH_32, |
<> | 149:156823d33999 | 352 | rx_length); |
<> | 149:156823d33999 | 353 | PDMA_SetTransferAddr(obj->spi.dma_chn_id_rx, |
<> | 149:156823d33999 | 354 | (uint32_t) &spi_base->RX, // Source address |
<> | 149:156823d33999 | 355 | PDMA_SAR_FIX, // Source address fixed |
<> | 149:156823d33999 | 356 | ((uint32_t) rx) + (data_width / 8) * rx_length, // NOTE: End of destination address |
<> | 149:156823d33999 | 357 | PDMA_DAR_INC); // Destination address incremental |
<> | 149:156823d33999 | 358 | PDMA_SetBurstType(obj->spi.dma_chn_id_rx, |
<> | 149:156823d33999 | 359 | PDMA_REQ_SINGLE, // Single mode |
<> | 149:156823d33999 | 360 | 0); // Burst size |
<> | 149:156823d33999 | 361 | PDMA_EnableInt(obj->spi.dma_chn_id_rx, |
<> | 149:156823d33999 | 362 | 0); // Interrupt type. No use here |
<> | 149:156823d33999 | 363 | // Register DMA event handler |
<> | 149:156823d33999 | 364 | dma_set_handler(obj->spi.dma_chn_id_rx, (uint32_t) spi_dma_handler_rx, (uint32_t) obj, DMA_EVENT_ALL); |
<> | 149:156823d33999 | 365 | |
<> | 149:156823d33999 | 366 | // Start tx/rx DMA transfer |
<> | 149:156823d33999 | 367 | spi_enable_vector_interrupt(obj, handler, 1); |
<> | 149:156823d33999 | 368 | // NOTE: It is safer to start rx DMA first and then tx DMA. Otherwise, receive FIFO is subject to overflow by tx DMA. |
<> | 149:156823d33999 | 369 | SPI_TRIGGER_RX_PDMA(((SPI_T *) NU_MODBASE(obj->spi.spi))); |
<> | 149:156823d33999 | 370 | SPI_TRIGGER_TX_PDMA(((SPI_T *) NU_MODBASE(obj->spi.spi))); |
<> | 149:156823d33999 | 371 | spi_master_enable_interrupt(obj, 1); |
<> | 149:156823d33999 | 372 | } |
<> | 149:156823d33999 | 373 | } |
<> | 149:156823d33999 | 374 | |
<> | 149:156823d33999 | 375 | /** |
<> | 149:156823d33999 | 376 | * Abort an SPI transfer |
<> | 149:156823d33999 | 377 | * This is a helper function for event handling. When any of the events listed occurs, the HAL will abort any ongoing |
<> | 149:156823d33999 | 378 | * transfers |
<> | 149:156823d33999 | 379 | * @param[in] obj The SPI peripheral to stop |
<> | 149:156823d33999 | 380 | */ |
<> | 149:156823d33999 | 381 | void spi_abort_asynch(spi_t *obj) |
<> | 149:156823d33999 | 382 | { |
<> | 149:156823d33999 | 383 | SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi); |
<> | 161:2cc1468da177 | 384 | PDMA_T *pdma_base = dma_modbase(); |
<> | 149:156823d33999 | 385 | |
<> | 149:156823d33999 | 386 | if (obj->spi.dma_usage != DMA_USAGE_NEVER) { |
<> | 149:156823d33999 | 387 | // Receive FIFO Overrun in case of tx length > rx length on DMA way |
<> | 149:156823d33999 | 388 | if (spi_base->STATUS & SPI_STATUS_RXOVIF_Msk) { |
<> | 149:156823d33999 | 389 | spi_base->STATUS = SPI_STATUS_RXOVIF_Msk; |
<> | 149:156823d33999 | 390 | } |
<> | 149:156823d33999 | 391 | |
<> | 149:156823d33999 | 392 | if (obj->spi.dma_chn_id_tx != DMA_ERROR_OUT_OF_CHANNELS) { |
<> | 149:156823d33999 | 393 | PDMA_DisableInt(obj->spi.dma_chn_id_tx, 0); |
<> | 149:156823d33999 | 394 | // FIXME: Next PDMA transfer will fail with PDMA_STOP() called. Cause is unknown. |
<> | 149:156823d33999 | 395 | //PDMA_STOP(obj->spi.dma_chn_id_tx); |
<> | 161:2cc1468da177 | 396 | pdma_base->CHCTL &= ~(1 << obj->spi.dma_chn_id_tx); |
<> | 149:156823d33999 | 397 | } |
<> | 149:156823d33999 | 398 | SPI_DISABLE_TX_PDMA(((SPI_T *) NU_MODBASE(obj->spi.spi))); |
<> | 149:156823d33999 | 399 | |
<> | 149:156823d33999 | 400 | if (obj->spi.dma_chn_id_rx != DMA_ERROR_OUT_OF_CHANNELS) { |
<> | 149:156823d33999 | 401 | PDMA_DisableInt(obj->spi.dma_chn_id_rx, 0); |
<> | 149:156823d33999 | 402 | // FIXME: Next PDMA transfer will fail with PDMA_STOP() called. Cause is unknown. |
<> | 149:156823d33999 | 403 | //PDMA_STOP(obj->spi.dma_chn_id_rx); |
<> | 161:2cc1468da177 | 404 | pdma_base->CHCTL &= ~(1 << obj->spi.dma_chn_id_rx); |
<> | 149:156823d33999 | 405 | } |
<> | 149:156823d33999 | 406 | SPI_DISABLE_RX_PDMA(((SPI_T *) NU_MODBASE(obj->spi.spi))); |
<> | 149:156823d33999 | 407 | } |
<> | 149:156823d33999 | 408 | |
<> | 149:156823d33999 | 409 | // Necessary for both interrupt way and DMA way |
<> | 149:156823d33999 | 410 | spi_enable_vector_interrupt(obj, 0, 0); |
<> | 149:156823d33999 | 411 | spi_master_enable_interrupt(obj, 0); |
<> | 149:156823d33999 | 412 | |
<> | 149:156823d33999 | 413 | // FIXME: SPI H/W may get out of state without the busy check. |
<> | 149:156823d33999 | 414 | while (SPI_IS_BUSY(spi_base)); |
<> | 149:156823d33999 | 415 | SPI_DISABLE(spi_base); |
<> | 149:156823d33999 | 416 | |
<> | 149:156823d33999 | 417 | SPI_ClearRxFIFO(spi_base); |
<> | 149:156823d33999 | 418 | SPI_ClearTxFIFO(spi_base); |
<> | 149:156823d33999 | 419 | } |
<> | 149:156823d33999 | 420 | |
<> | 149:156823d33999 | 421 | /** |
<> | 149:156823d33999 | 422 | * Handle the SPI interrupt |
<> | 149:156823d33999 | 423 | * Read frames until the RX FIFO is empty. Write at most as many frames as were read. This way, |
<> | 149:156823d33999 | 424 | * it is unlikely that the RX FIFO will overflow. |
<> | 149:156823d33999 | 425 | * @param[in] obj The SPI peripheral that generated the interrupt |
<> | 149:156823d33999 | 426 | * @return |
<> | 149:156823d33999 | 427 | */ |
<> | 149:156823d33999 | 428 | uint32_t spi_irq_handler_asynch(spi_t *obj) |
<> | 149:156823d33999 | 429 | { |
<> | 149:156823d33999 | 430 | // Check for SPI events |
<> | 149:156823d33999 | 431 | uint32_t event = spi_event_check(obj); |
<> | 149:156823d33999 | 432 | if (event) { |
<> | 149:156823d33999 | 433 | spi_abort_asynch(obj); |
<> | 149:156823d33999 | 434 | } |
<> | 149:156823d33999 | 435 | |
<> | 149:156823d33999 | 436 | return (obj->spi.event & event) | ((event & SPI_EVENT_COMPLETE) ? SPI_EVENT_INTERNAL_TRANSFER_COMPLETE : 0); |
<> | 149:156823d33999 | 437 | } |
<> | 149:156823d33999 | 438 | |
<> | 149:156823d33999 | 439 | uint8_t spi_active(spi_t *obj) |
<> | 149:156823d33999 | 440 | { |
<> | 149:156823d33999 | 441 | SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi); |
<> | 149:156823d33999 | 442 | // FIXME |
<> | 149:156823d33999 | 443 | /* |
<> | 149:156823d33999 | 444 | if ((obj->rx_buff.buffer && obj->rx_buff.pos < obj->rx_buff.length) |
<> | 149:156823d33999 | 445 | || (obj->tx_buff.buffer && obj->tx_buff.pos < obj->tx_buff.length) ){ |
<> | 149:156823d33999 | 446 | return 1; |
<> | 149:156823d33999 | 447 | } else { |
<> | 149:156823d33999 | 448 | // interrupts are disabled, all transaction have been completed |
<> | 149:156823d33999 | 449 | // TODO: checking rx fifo, it reports data eventhough RFDF is not set |
<> | 149:156823d33999 | 450 | return DSPI_HAL_GetIntMode(obj->spi.address, kDspiRxFifoDrainRequest); |
<> | 149:156823d33999 | 451 | }*/ |
<> | 149:156823d33999 | 452 | |
<> | 149:156823d33999 | 453 | //return SPI_IS_BUSY(spi_base); |
<> | 149:156823d33999 | 454 | return (spi_base->CTL & SPI_CTL_SPIEN_Msk); |
<> | 149:156823d33999 | 455 | } |
<> | 149:156823d33999 | 456 | |
<> | 149:156823d33999 | 457 | int spi_allow_powerdown(void) |
<> | 149:156823d33999 | 458 | { |
<> | 149:156823d33999 | 459 | uint32_t modinit_mask = spi_modinit_mask; |
<> | 149:156823d33999 | 460 | while (modinit_mask) { |
<> | 149:156823d33999 | 461 | int spi_idx = nu_ctz(modinit_mask); |
<> | 149:156823d33999 | 462 | const struct nu_modinit_s *modinit = spi_modinit_tab + spi_idx; |
<> | 149:156823d33999 | 463 | if (modinit->modname != NC) { |
<> | 149:156823d33999 | 464 | SPI_T *spi_base = (SPI_T *) NU_MODBASE(modinit->modname); |
<> | 149:156823d33999 | 465 | // Disallow entering power-down mode if SPI transfer is enabled. |
<> | 149:156823d33999 | 466 | if (spi_base->CTL & SPI_CTL_SPIEN_Msk) { |
<> | 149:156823d33999 | 467 | return 0; |
<> | 149:156823d33999 | 468 | } |
<> | 149:156823d33999 | 469 | } |
<> | 149:156823d33999 | 470 | modinit_mask &= ~(1 << spi_idx); |
<> | 149:156823d33999 | 471 | } |
<> | 149:156823d33999 | 472 | |
<> | 149:156823d33999 | 473 | return 1; |
<> | 149:156823d33999 | 474 | } |
<> | 149:156823d33999 | 475 | |
<> | 149:156823d33999 | 476 | static int spi_writeable(spi_t * obj) |
<> | 149:156823d33999 | 477 | { |
<> | 149:156823d33999 | 478 | // Receive FIFO must not be full to avoid receive FIFO overflow on next transmit/receive |
<> | 149:156823d33999 | 479 | //return (! SPI_GET_TX_FIFO_FULL_FLAG(((SPI_T *) NU_MODBASE(obj->spi.spi)))) && (SPI_GET_RX_FIFO_COUNT(((SPI_T *) NU_MODBASE(obj->spi.spi))) < NU_SPI_FIFO_DEPTH); |
<> | 149:156823d33999 | 480 | return (! SPI_GET_TX_FIFO_FULL_FLAG(((SPI_T *) NU_MODBASE(obj->spi.spi)))); |
<> | 149:156823d33999 | 481 | } |
<> | 149:156823d33999 | 482 | |
<> | 149:156823d33999 | 483 | static int spi_readable(spi_t * obj) |
<> | 149:156823d33999 | 484 | { |
<> | 149:156823d33999 | 485 | return ! SPI_GET_RX_FIFO_EMPTY_FLAG(((SPI_T *) NU_MODBASE(obj->spi.spi))); |
<> | 149:156823d33999 | 486 | } |
<> | 149:156823d33999 | 487 | |
<> | 149:156823d33999 | 488 | static void spi_enable_event(spi_t *obj, uint32_t event, uint8_t enable) |
<> | 149:156823d33999 | 489 | { |
<> | 149:156823d33999 | 490 | obj->spi.event &= ~SPI_EVENT_ALL; |
<> | 149:156823d33999 | 491 | obj->spi.event |= (event & SPI_EVENT_ALL); |
<> | 149:156823d33999 | 492 | if (event & SPI_EVENT_RX_OVERFLOW) { |
<> | 149:156823d33999 | 493 | SPI_EnableInt((SPI_T *) NU_MODBASE(obj->spi.spi), SPI_FIFO_RXOVIEN_MASK); |
<> | 149:156823d33999 | 494 | } |
<> | 149:156823d33999 | 495 | } |
<> | 149:156823d33999 | 496 | |
<> | 149:156823d33999 | 497 | static void spi_enable_vector_interrupt(spi_t *obj, uint32_t handler, uint8_t enable) |
<> | 149:156823d33999 | 498 | { |
<> | 149:156823d33999 | 499 | const struct nu_modinit_s *modinit = get_modinit(obj->spi.spi, spi_modinit_tab); |
<> | 149:156823d33999 | 500 | MBED_ASSERT(modinit != NULL); |
<> | 149:156823d33999 | 501 | MBED_ASSERT(modinit->modname == obj->spi.spi); |
<> | 149:156823d33999 | 502 | |
<> | 149:156823d33999 | 503 | if (enable) { |
<> | 149:156823d33999 | 504 | NVIC_SetVector(modinit->irq_n, handler); |
<> | 149:156823d33999 | 505 | NVIC_EnableIRQ(modinit->irq_n); |
<> | 149:156823d33999 | 506 | } |
<> | 149:156823d33999 | 507 | else { |
<> | 149:156823d33999 | 508 | //NVIC_SetVector(modinit->irq_n, handler); |
<> | 149:156823d33999 | 509 | NVIC_DisableIRQ(modinit->irq_n); |
<> | 149:156823d33999 | 510 | } |
<> | 149:156823d33999 | 511 | } |
<> | 149:156823d33999 | 512 | |
<> | 149:156823d33999 | 513 | static void spi_master_enable_interrupt(spi_t *obj, uint8_t enable) |
<> | 149:156823d33999 | 514 | { |
<> | 149:156823d33999 | 515 | SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi); |
<> | 149:156823d33999 | 516 | |
<> | 149:156823d33999 | 517 | if (enable) { |
<> | 149:156823d33999 | 518 | SPI_SetFIFOThreshold(spi_base, 4, 4); |
<> | 149:156823d33999 | 519 | //SPI_SET_SUSPEND_CYCLE(spi_base, 4); |
<> | 149:156823d33999 | 520 | // Enable tx/rx FIFO threshold interrupt |
<> | 149:156823d33999 | 521 | SPI_EnableInt(spi_base, SPI_FIFO_RXTHIEN_MASK | SPI_FIFO_TXTHIEN_MASK); |
<> | 149:156823d33999 | 522 | } |
<> | 149:156823d33999 | 523 | else { |
<> | 149:156823d33999 | 524 | SPI_DisableInt(spi_base, SPI_FIFO_RXTHIEN_MASK | SPI_FIFO_TXTHIEN_MASK); |
<> | 149:156823d33999 | 525 | } |
<> | 149:156823d33999 | 526 | } |
<> | 149:156823d33999 | 527 | |
<> | 149:156823d33999 | 528 | static uint32_t spi_event_check(spi_t *obj) |
<> | 149:156823d33999 | 529 | { |
<> | 149:156823d33999 | 530 | SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi); |
<> | 149:156823d33999 | 531 | uint32_t event = 0; |
<> | 149:156823d33999 | 532 | |
<> | 149:156823d33999 | 533 | if (obj->spi.dma_usage == DMA_USAGE_NEVER) { |
<> | 149:156823d33999 | 534 | uint32_t n_rec = spi_master_read_asynch(obj); |
<> | 149:156823d33999 | 535 | spi_master_write_asynch(obj, n_rec); |
<> | 149:156823d33999 | 536 | } |
<> | 149:156823d33999 | 537 | |
<> | 149:156823d33999 | 538 | if (spi_is_tx_complete(obj) && spi_is_rx_complete(obj)) { |
<> | 149:156823d33999 | 539 | event |= SPI_EVENT_COMPLETE; |
<> | 149:156823d33999 | 540 | } |
<> | 149:156823d33999 | 541 | |
<> | 149:156823d33999 | 542 | // Receive FIFO Overrun |
<> | 149:156823d33999 | 543 | if (spi_base->STATUS & SPI_STATUS_RXOVIF_Msk) { |
<> | 149:156823d33999 | 544 | spi_base->STATUS = SPI_STATUS_RXOVIF_Msk; |
<> | 149:156823d33999 | 545 | // In case of tx length > rx length on DMA way |
<> | 149:156823d33999 | 546 | if (obj->spi.dma_usage == DMA_USAGE_NEVER) { |
<> | 149:156823d33999 | 547 | event |= SPI_EVENT_RX_OVERFLOW; |
<> | 149:156823d33999 | 548 | } |
<> | 149:156823d33999 | 549 | } |
<> | 149:156823d33999 | 550 | |
<> | 149:156823d33999 | 551 | // Receive Time-Out |
<> | 149:156823d33999 | 552 | if (spi_base->STATUS & SPI_STATUS_RXTOIF_Msk) { |
<> | 149:156823d33999 | 553 | spi_base->STATUS = SPI_STATUS_RXTOIF_Msk; |
<> | 149:156823d33999 | 554 | //event |= SPI_EVENT_ERROR; |
<> | 149:156823d33999 | 555 | } |
<> | 149:156823d33999 | 556 | // Transmit FIFO Under-Run |
<> | 149:156823d33999 | 557 | if (spi_base->STATUS & SPI_STATUS_TXUFIF_Msk) { |
<> | 149:156823d33999 | 558 | spi_base->STATUS = SPI_STATUS_TXUFIF_Msk; |
<> | 149:156823d33999 | 559 | event |= SPI_EVENT_ERROR; |
<> | 149:156823d33999 | 560 | } |
<> | 149:156823d33999 | 561 | |
<> | 149:156823d33999 | 562 | return event; |
<> | 149:156823d33999 | 563 | } |
<> | 149:156823d33999 | 564 | |
<> | 149:156823d33999 | 565 | /** |
<> | 149:156823d33999 | 566 | * Send words from the SPI TX buffer until the send limit is reached or the TX FIFO is full |
<> | 149:156823d33999 | 567 | * tx_limit is provided to ensure that the number of SPI frames (words) in flight can be managed. |
<> | 149:156823d33999 | 568 | * @param[in] obj The SPI object on which to operate |
<> | 149:156823d33999 | 569 | * @param[in] tx_limit The maximum number of words to send |
<> | 149:156823d33999 | 570 | * @return The number of SPI words that have been transfered |
<> | 149:156823d33999 | 571 | */ |
<> | 149:156823d33999 | 572 | static uint32_t spi_master_write_asynch(spi_t *obj, uint32_t tx_limit) |
<> | 149:156823d33999 | 573 | { |
<> | 149:156823d33999 | 574 | uint32_t n_words = 0; |
<> | 149:156823d33999 | 575 | uint32_t tx_rmn = obj->tx_buff.length - obj->tx_buff.pos; |
<> | 149:156823d33999 | 576 | uint32_t rx_rmn = obj->rx_buff.length - obj->rx_buff.pos; |
<> | 149:156823d33999 | 577 | uint32_t max_tx = NU_MAX(tx_rmn, rx_rmn); |
<> | 149:156823d33999 | 578 | max_tx = NU_MIN(max_tx, tx_limit); |
<> | 149:156823d33999 | 579 | uint8_t data_width = spi_get_data_width(obj); |
<> | 149:156823d33999 | 580 | uint8_t bytes_per_word = (data_width + 7) / 8; |
<> | 149:156823d33999 | 581 | uint8_t *tx = (uint8_t *)(obj->tx_buff.buffer) + bytes_per_word * obj->tx_buff.pos; |
<> | 149:156823d33999 | 582 | SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi); |
<> | 149:156823d33999 | 583 | |
<> | 149:156823d33999 | 584 | while ((n_words < max_tx) && spi_writeable(obj)) { |
<> | 149:156823d33999 | 585 | if (spi_is_tx_complete(obj)) { |
<> | 149:156823d33999 | 586 | // Transmit dummy as transmit buffer is empty |
<> | 149:156823d33999 | 587 | SPI_WRITE_TX(spi_base, 0); |
<> | 149:156823d33999 | 588 | } |
<> | 149:156823d33999 | 589 | else { |
<> | 149:156823d33999 | 590 | switch (bytes_per_word) { |
<> | 149:156823d33999 | 591 | case 4: |
<> | 149:156823d33999 | 592 | SPI_WRITE_TX(spi_base, nu_get32_le(tx)); |
<> | 149:156823d33999 | 593 | tx += 4; |
<> | 149:156823d33999 | 594 | break; |
<> | 149:156823d33999 | 595 | case 2: |
<> | 149:156823d33999 | 596 | SPI_WRITE_TX(spi_base, nu_get16_le(tx)); |
<> | 149:156823d33999 | 597 | tx += 2; |
<> | 149:156823d33999 | 598 | break; |
<> | 149:156823d33999 | 599 | case 1: |
<> | 149:156823d33999 | 600 | SPI_WRITE_TX(spi_base, *((uint8_t *) tx)); |
<> | 149:156823d33999 | 601 | tx += 1; |
<> | 149:156823d33999 | 602 | break; |
<> | 149:156823d33999 | 603 | } |
<> | 149:156823d33999 | 604 | |
<> | 149:156823d33999 | 605 | obj->tx_buff.pos ++; |
<> | 149:156823d33999 | 606 | } |
<> | 149:156823d33999 | 607 | n_words ++; |
<> | 149:156823d33999 | 608 | } |
<> | 149:156823d33999 | 609 | |
<> | 149:156823d33999 | 610 | //Return the number of words that have been sent |
<> | 149:156823d33999 | 611 | return n_words; |
<> | 149:156823d33999 | 612 | } |
<> | 149:156823d33999 | 613 | |
<> | 149:156823d33999 | 614 | /** |
<> | 149:156823d33999 | 615 | * Read SPI words out of the RX FIFO |
<> | 149:156823d33999 | 616 | * Continues reading words out of the RX FIFO until the following condition is met: |
<> | 149:156823d33999 | 617 | * o There are no more words in the FIFO |
<> | 149:156823d33999 | 618 | * OR BOTH OF: |
<> | 149:156823d33999 | 619 | * o At least as many words as the TX buffer have been received |
<> | 149:156823d33999 | 620 | * o At least as many words as the RX buffer have been received |
<> | 149:156823d33999 | 621 | * This way, RX overflows are not generated when the TX buffer size exceeds the RX buffer size |
<> | 149:156823d33999 | 622 | * @param[in] obj The SPI object on which to operate |
<> | 149:156823d33999 | 623 | * @return Returns the number of words extracted from the RX FIFO |
<> | 149:156823d33999 | 624 | */ |
<> | 149:156823d33999 | 625 | static uint32_t spi_master_read_asynch(spi_t *obj) |
<> | 149:156823d33999 | 626 | { |
<> | 149:156823d33999 | 627 | uint32_t n_words = 0; |
<> | 149:156823d33999 | 628 | uint32_t tx_rmn = obj->tx_buff.length - obj->tx_buff.pos; |
<> | 149:156823d33999 | 629 | uint32_t rx_rmn = obj->rx_buff.length - obj->rx_buff.pos; |
<> | 149:156823d33999 | 630 | uint32_t max_rx = NU_MAX(tx_rmn, rx_rmn); |
<> | 149:156823d33999 | 631 | uint8_t data_width = spi_get_data_width(obj); |
<> | 149:156823d33999 | 632 | uint8_t bytes_per_word = (data_width + 7) / 8; |
<> | 149:156823d33999 | 633 | uint8_t *rx = (uint8_t *)(obj->rx_buff.buffer) + bytes_per_word * obj->rx_buff.pos; |
<> | 149:156823d33999 | 634 | SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi); |
<> | 149:156823d33999 | 635 | |
<> | 149:156823d33999 | 636 | while ((n_words < max_rx) && spi_readable(obj)) { |
<> | 149:156823d33999 | 637 | if (spi_is_rx_complete(obj)) { |
<> | 149:156823d33999 | 638 | // Disregard as receive buffer is full |
<> | 149:156823d33999 | 639 | SPI_READ_RX(spi_base); |
<> | 149:156823d33999 | 640 | } |
<> | 149:156823d33999 | 641 | else { |
<> | 149:156823d33999 | 642 | switch (bytes_per_word) { |
<> | 149:156823d33999 | 643 | case 4: { |
<> | 149:156823d33999 | 644 | uint32_t val = SPI_READ_RX(spi_base); |
<> | 149:156823d33999 | 645 | nu_set32_le(rx, val); |
<> | 149:156823d33999 | 646 | rx += 4; |
<> | 149:156823d33999 | 647 | break; |
<> | 149:156823d33999 | 648 | } |
<> | 149:156823d33999 | 649 | case 2: { |
<> | 149:156823d33999 | 650 | uint16_t val = SPI_READ_RX(spi_base); |
<> | 149:156823d33999 | 651 | nu_set16_le(rx, val); |
<> | 149:156823d33999 | 652 | rx += 2; |
<> | 149:156823d33999 | 653 | break; |
<> | 149:156823d33999 | 654 | } |
<> | 149:156823d33999 | 655 | case 1: |
<> | 149:156823d33999 | 656 | *rx ++ = SPI_READ_RX(spi_base); |
<> | 149:156823d33999 | 657 | break; |
<> | 149:156823d33999 | 658 | } |
<> | 149:156823d33999 | 659 | |
<> | 149:156823d33999 | 660 | obj->rx_buff.pos ++; |
<> | 149:156823d33999 | 661 | } |
<> | 149:156823d33999 | 662 | n_words ++; |
<> | 149:156823d33999 | 663 | } |
<> | 149:156823d33999 | 664 | |
<> | 149:156823d33999 | 665 | // Return the number of words received |
<> | 149:156823d33999 | 666 | return n_words; |
<> | 149:156823d33999 | 667 | } |
<> | 149:156823d33999 | 668 | |
<> | 149:156823d33999 | 669 | static void spi_buffer_set(spi_t *obj, const void *tx, size_t tx_length, void *rx, size_t rx_length) |
<> | 149:156823d33999 | 670 | { |
<> | 149:156823d33999 | 671 | obj->tx_buff.buffer = (void *) tx; |
<> | 149:156823d33999 | 672 | obj->tx_buff.length = tx_length; |
<> | 149:156823d33999 | 673 | obj->tx_buff.pos = 0; |
<> | 149:156823d33999 | 674 | obj->tx_buff.width = spi_get_data_width(obj); |
<> | 149:156823d33999 | 675 | obj->rx_buff.buffer = rx; |
<> | 149:156823d33999 | 676 | obj->rx_buff.length = rx_length; |
<> | 149:156823d33999 | 677 | obj->rx_buff.pos = 0; |
<> | 149:156823d33999 | 678 | obj->rx_buff.width = spi_get_data_width(obj); |
<> | 149:156823d33999 | 679 | } |
<> | 149:156823d33999 | 680 | |
<> | 149:156823d33999 | 681 | static void spi_check_dma_usage(DMAUsage *dma_usage, int *dma_ch_tx, int *dma_ch_rx) |
<> | 149:156823d33999 | 682 | { |
<> | 149:156823d33999 | 683 | if (*dma_usage != DMA_USAGE_NEVER) { |
<> | 149:156823d33999 | 684 | if (*dma_ch_tx == DMA_ERROR_OUT_OF_CHANNELS) { |
<> | 149:156823d33999 | 685 | *dma_ch_tx = dma_channel_allocate(DMA_CAP_NONE); |
<> | 149:156823d33999 | 686 | } |
<> | 149:156823d33999 | 687 | if (*dma_ch_rx == DMA_ERROR_OUT_OF_CHANNELS) { |
<> | 149:156823d33999 | 688 | *dma_ch_rx = dma_channel_allocate(DMA_CAP_NONE); |
<> | 149:156823d33999 | 689 | } |
<> | 149:156823d33999 | 690 | |
<> | 149:156823d33999 | 691 | if (*dma_ch_tx == DMA_ERROR_OUT_OF_CHANNELS || *dma_ch_rx == DMA_ERROR_OUT_OF_CHANNELS) { |
<> | 149:156823d33999 | 692 | *dma_usage = DMA_USAGE_NEVER; |
<> | 149:156823d33999 | 693 | } |
<> | 149:156823d33999 | 694 | } |
<> | 149:156823d33999 | 695 | |
<> | 149:156823d33999 | 696 | if (*dma_usage == DMA_USAGE_NEVER) { |
<> | 149:156823d33999 | 697 | dma_channel_free(*dma_ch_tx); |
<> | 149:156823d33999 | 698 | *dma_ch_tx = DMA_ERROR_OUT_OF_CHANNELS; |
<> | 149:156823d33999 | 699 | dma_channel_free(*dma_ch_rx); |
<> | 149:156823d33999 | 700 | *dma_ch_rx = DMA_ERROR_OUT_OF_CHANNELS; |
<> | 149:156823d33999 | 701 | } |
<> | 149:156823d33999 | 702 | } |
<> | 149:156823d33999 | 703 | |
<> | 149:156823d33999 | 704 | static uint8_t spi_get_data_width(spi_t *obj) |
<> | 149:156823d33999 | 705 | { |
<> | 149:156823d33999 | 706 | SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi); |
<> | 149:156823d33999 | 707 | |
<> | 153:fa9ff456f731 | 708 | uint32_t data_width = ((spi_base->CTL & SPI_CTL_DWIDTH_Msk) >> SPI_CTL_DWIDTH_Pos); |
<> | 153:fa9ff456f731 | 709 | if (data_width == 0) { |
<> | 153:fa9ff456f731 | 710 | data_width = 32; |
<> | 153:fa9ff456f731 | 711 | } |
<> | 153:fa9ff456f731 | 712 | |
<> | 153:fa9ff456f731 | 713 | return data_width; |
<> | 149:156823d33999 | 714 | } |
<> | 149:156823d33999 | 715 | |
<> | 149:156823d33999 | 716 | static int spi_is_tx_complete(spi_t *obj) |
<> | 149:156823d33999 | 717 | { |
<> | 149:156823d33999 | 718 | // ???: Exclude tx fifo empty check due to no such interrupt on DMA way |
<> | 149:156823d33999 | 719 | return (obj->tx_buff.pos == obj->tx_buff.length); |
<> | 149:156823d33999 | 720 | //return (obj->tx_buff.pos == obj->tx_buff.length && SPI_GET_TX_FIFO_EMPTY_FLAG(((SPI_T *) NU_MODBASE(obj->spi.spi)))); |
<> | 149:156823d33999 | 721 | } |
<> | 149:156823d33999 | 722 | |
<> | 149:156823d33999 | 723 | static int spi_is_rx_complete(spi_t *obj) |
<> | 149:156823d33999 | 724 | { |
<> | 149:156823d33999 | 725 | return (obj->rx_buff.pos == obj->rx_buff.length); |
<> | 149:156823d33999 | 726 | } |
<> | 149:156823d33999 | 727 | |
<> | 149:156823d33999 | 728 | static void spi_dma_handler_tx(uint32_t id, uint32_t event_dma) |
<> | 149:156823d33999 | 729 | { |
<> | 149:156823d33999 | 730 | spi_t *obj = (spi_t *) id; |
<> | 149:156823d33999 | 731 | |
<> | 149:156823d33999 | 732 | // FIXME: Pass this error to caller |
<> | 149:156823d33999 | 733 | if (event_dma & DMA_EVENT_ABORT) { |
<> | 149:156823d33999 | 734 | } |
<> | 149:156823d33999 | 735 | // Expect SPI IRQ will catch this transfer done event |
<> | 149:156823d33999 | 736 | if (event_dma & DMA_EVENT_TRANSFER_DONE) { |
<> | 149:156823d33999 | 737 | obj->tx_buff.pos = obj->tx_buff.length; |
<> | 149:156823d33999 | 738 | } |
<> | 149:156823d33999 | 739 | // FIXME: Pass this error to caller |
<> | 149:156823d33999 | 740 | if (event_dma & DMA_EVENT_TIMEOUT) { |
<> | 149:156823d33999 | 741 | } |
<> | 149:156823d33999 | 742 | |
<> | 149:156823d33999 | 743 | const struct nu_modinit_s *modinit = get_modinit(obj->spi.spi, spi_modinit_tab); |
<> | 149:156823d33999 | 744 | MBED_ASSERT(modinit != NULL); |
<> | 149:156823d33999 | 745 | MBED_ASSERT(modinit->modname == obj->spi.spi); |
<> | 149:156823d33999 | 746 | |
<> | 149:156823d33999 | 747 | void (*vec)(void) = (void (*)(void)) NVIC_GetVector(modinit->irq_n); |
<> | 149:156823d33999 | 748 | vec(); |
<> | 149:156823d33999 | 749 | } |
<> | 149:156823d33999 | 750 | |
<> | 149:156823d33999 | 751 | static void spi_dma_handler_rx(uint32_t id, uint32_t event_dma) |
<> | 149:156823d33999 | 752 | { |
<> | 149:156823d33999 | 753 | spi_t *obj = (spi_t *) id; |
<> | 149:156823d33999 | 754 | |
<> | 149:156823d33999 | 755 | // FIXME: Pass this error to caller |
<> | 149:156823d33999 | 756 | if (event_dma & DMA_EVENT_ABORT) { |
<> | 149:156823d33999 | 757 | } |
<> | 149:156823d33999 | 758 | // Expect SPI IRQ will catch this transfer done event |
<> | 149:156823d33999 | 759 | if (event_dma & DMA_EVENT_TRANSFER_DONE) { |
<> | 149:156823d33999 | 760 | obj->rx_buff.pos = obj->rx_buff.length; |
<> | 149:156823d33999 | 761 | } |
<> | 149:156823d33999 | 762 | // FIXME: Pass this error to caller |
<> | 149:156823d33999 | 763 | if (event_dma & DMA_EVENT_TIMEOUT) { |
<> | 149:156823d33999 | 764 | } |
<> | 149:156823d33999 | 765 | |
<> | 149:156823d33999 | 766 | const struct nu_modinit_s *modinit = get_modinit(obj->spi.spi, spi_modinit_tab); |
<> | 149:156823d33999 | 767 | MBED_ASSERT(modinit != NULL); |
<> | 149:156823d33999 | 768 | MBED_ASSERT(modinit->modname == obj->spi.spi); |
<> | 149:156823d33999 | 769 | |
<> | 149:156823d33999 | 770 | void (*vec)(void) = (void (*)(void)) NVIC_GetVector(modinit->irq_n); |
<> | 149:156823d33999 | 771 | vec(); |
<> | 149:156823d33999 | 772 | } |
<> | 149:156823d33999 | 773 | |
<> | 149:156823d33999 | 774 | #endif |
<> | 149:156823d33999 | 775 | |
<> | 149:156823d33999 | 776 | #endif |