mbed-os

Fork of mbed-os by erkin yucel

Committer:
elessair
Date:
Sun Oct 23 15:10:02 2016 +0000
Revision:
0:f269e3021894
Initial commit

Who changed what in which revision?

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