1 /*
2 * Copyright (c) 2015-2018, Texas Instruments Incorporated
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * * Neither the name of Texas Instruments Incorporated nor the names of
17 * its contributors may be used to endorse or promote products derived
18 * from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
22 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
27 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
29 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
30 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32 #include <stdbool.h>
33 #include <stddef.h>
34 #include <stdint.h>
35
36 #include <ti/devices/cc32xx/inc/hw_mcspi.h>
37 #include <ti/devices/cc32xx/inc/hw_types.h>
38 #include <ti/devices/cc32xx/inc/hw_memmap.h>
39 #include <ti/devices/cc32xx/inc/hw_udma.h>
40 #include <ti/devices/cc32xx/inc/hw_ocp_shared.h>
41 #include <ti/devices/cc32xx/driverlib/rom.h>
42 #include <ti/devices/cc32xx/driverlib/rom_map.h>
43 #include <ti/devices/cc32xx/driverlib/prcm.h>
44 #include <ti/devices/cc32xx/driverlib/spi.h>
45 #include <ti/devices/cc32xx/driverlib/udma.h>
46
47 #include <ti/drivers/dma/UDMACC32XX.h>
48 #include <ti/drivers/dpl/HwiP.h>
49 #include <ti/drivers/dpl/SemaphoreP.h>
50 #include <ti/drivers/Power.h>
51 #include <ti/drivers/power/PowerCC32XX.h>
52 #include <ti/drivers/spi/SPICC32XXDMA.h>
53
54 #define MAX_DMA_TRANSFER_AMOUNT (1024)
55
56 #define PAD_CONFIG_BASE (OCP_SHARED_BASE + OCP_SHARED_O_GPIO_PAD_CONFIG_0)
57 #define PAD_RESET_STATE 0xC61
58
59 void SPICC32XXDMA_close(SPI_Handle handle);
60 int_fast16_t SPICC32XXDMA_control(SPI_Handle handle, uint_fast16_t cmd,
61 void *arg);
62 void SPICC32XXDMA_init(SPI_Handle handle);
63 SPI_Handle SPICC32XXDMA_open(SPI_Handle handle, SPI_Params *params);
64 bool SPICC32XXDMA_transfer(SPI_Handle handle, SPI_Transaction *transaction);
65 void SPICC32XXDMA_transferCancel(SPI_Handle handle);
66
67 /* SPI function table for SPICC32XXDMA implementation */
68 const SPI_FxnTable SPICC32XXDMA_fxnTable = {
69 SPICC32XXDMA_close,
70 SPICC32XXDMA_control,
71 SPICC32XXDMA_init,
72 SPICC32XXDMA_open,
73 SPICC32XXDMA_transfer,
74 SPICC32XXDMA_transferCancel
75 };
76
77 static const uint32_t mode[] = {
78 SPI_MODE_MASTER,
79 SPI_MODE_SLAVE
80 };
81
82 /*
83 * This lookup table is used to configure the DMA channels for the appropriate
84 * (8bit, 16bit or 32bit) transfer sizes.
85 * Table for an SPI DMA RX channel.
86 */
87 static const uint32_t dmaRxConfig[] = {
88 UDMA_SIZE_8 | UDMA_SRC_INC_NONE | UDMA_DST_INC_8 | UDMA_ARB_1,
89 UDMA_SIZE_16 | UDMA_SRC_INC_NONE | UDMA_DST_INC_16 | UDMA_ARB_1,
90 UDMA_SIZE_32 | UDMA_SRC_INC_NONE | UDMA_DST_INC_32 | UDMA_ARB_1
91 };
92
93 /*
94 * This lookup table is used to configure the DMA channels for the appropriate
95 * (8bit, 16bit or 32bit) transfer sizes.
96 * Table for an SPI DMA TX channel
97 */
98 static const uint32_t dmaTxConfig[] = {
99 UDMA_SIZE_8 | UDMA_SRC_INC_8 | UDMA_DST_INC_NONE | UDMA_ARB_1,
100 UDMA_SIZE_16 | UDMA_SRC_INC_16 | UDMA_DST_INC_NONE | UDMA_ARB_1,
101 UDMA_SIZE_32 | UDMA_SRC_INC_32 | UDMA_DST_INC_NONE | UDMA_ARB_1
102 };
103
104 /*
105 * This lookup table is used to configure the DMA channels for the appropriate
106 * (8bit, 16bit or 32bit) transfer sizes when either txBuf or rxBuf are NULL.
107 */
108 static const uint32_t dmaNullConfig[] = {
109 UDMA_SIZE_8 | UDMA_SRC_INC_NONE | UDMA_DST_INC_NONE | UDMA_ARB_1,
110 UDMA_SIZE_16 | UDMA_SRC_INC_NONE | UDMA_DST_INC_NONE | UDMA_ARB_1,
111 UDMA_SIZE_32 | UDMA_SRC_INC_NONE | UDMA_DST_INC_NONE | UDMA_ARB_1
112 };
113
114 /*
115 * ======== blockingTransferCallback ========
116 */
blockingTransferCallback(SPI_Handle handle,SPI_Transaction * transaction)117 static void blockingTransferCallback(SPI_Handle handle,
118 SPI_Transaction *transaction)
119 {
120 SPICC32XXDMA_Object *object = handle->object;
121
122 SemaphoreP_post(object->transferComplete);
123 }
124
125 /*
126 * ======== configDMA ========
127 * This functions configures the transmit and receive DMA channels for a given
128 * SPI_Handle and SPI_Transaction
129 */
configDMA(SPICC32XXDMA_Object * object,SPICC32XXDMA_HWAttrsV1 const * hwAttrs,SPI_Transaction * transaction)130 static void configDMA(SPICC32XXDMA_Object *object,
131 SPICC32XXDMA_HWAttrsV1 const *hwAttrs, SPI_Transaction *transaction)
132 {
133 uintptr_t key;
134 void *buf;
135 uint32_t channelControlOptions;
136 uint8_t dataFrameSizeInBytes;
137 uint8_t optionsIndex;
138
139 /* DMA options used vary according to data size. */
140 if (object->dataSize < 9) {
141 optionsIndex = 0;
142 dataFrameSizeInBytes = sizeof(uint8_t);
143 }
144 else if (object->dataSize < 17) {
145 optionsIndex = 1;
146 dataFrameSizeInBytes = sizeof(uint16_t);;
147 }
148 else {
149 optionsIndex = 2;
150 dataFrameSizeInBytes = sizeof(uint32_t);
151 }
152
153 /*
154 * The DMA has a max transfer amount of 1024. If the transaction is
155 * greater; we must transfer it in chunks. object->amtDataXferred has
156 * how much data has already been sent.
157 */
158 if ((transaction->count - object->amtDataXferred) > MAX_DMA_TRANSFER_AMOUNT) {
159 object->currentXferAmt = MAX_DMA_TRANSFER_AMOUNT;
160 }
161 else {
162 object->currentXferAmt = (transaction->count - object->amtDataXferred);
163 }
164
165 if (transaction->txBuf) {
166 channelControlOptions = dmaTxConfig[optionsIndex];
167 /*
168 * Add an offset for the amount of data transfered. The offset is
169 * calculated by: object->amtDataXferred * (dataFrameSizeInBytes).
170 * This accounts for 8, 16 or 32-bit sized transfers.
171 */
172 buf = (void *) ((uint32_t) transaction->txBuf +
173 ((uint32_t) object->amtDataXferred * dataFrameSizeInBytes));
174 }
175 else {
176 channelControlOptions = dmaNullConfig[optionsIndex];
177 *hwAttrs->scratchBufPtr = hwAttrs->defaultTxBufValue;
178 buf = hwAttrs->scratchBufPtr;
179 }
180
181 /* Setup the TX transfer characteristics & buffers */
182 MAP_uDMAChannelControlSet(hwAttrs->txChannelIndex | UDMA_PRI_SELECT,
183 channelControlOptions);
184 MAP_uDMAChannelAttributeDisable(hwAttrs->txChannelIndex,
185 UDMA_ATTR_ALTSELECT);
186 MAP_uDMAChannelTransferSet(hwAttrs->txChannelIndex | UDMA_PRI_SELECT,
187 UDMA_MODE_BASIC, buf, (void *) (hwAttrs->baseAddr + MCSPI_O_TX0),
188 object->currentXferAmt);
189
190 if (transaction->rxBuf) {
191 channelControlOptions = dmaRxConfig[optionsIndex];
192 /*
193 * Add an offset for the amount of data transfered. The offset is
194 * calculated by: object->amtDataXferred * (dataFrameSizeInBytes).
195 * This accounts for 8 or 16-bit sized transfers.
196 */
197 buf = (void *) ((uint32_t) transaction->rxBuf +
198 ((uint32_t) object->amtDataXferred * dataFrameSizeInBytes));
199 }
200 else {
201 channelControlOptions = dmaNullConfig[optionsIndex];
202 buf = hwAttrs->scratchBufPtr;
203 }
204
205 /* Setup the RX transfer characteristics & buffers */
206 MAP_uDMAChannelControlSet(hwAttrs->rxChannelIndex | UDMA_PRI_SELECT,
207 channelControlOptions);
208 MAP_uDMAChannelAttributeDisable(hwAttrs->rxChannelIndex,
209 UDMA_ATTR_ALTSELECT);
210 MAP_uDMAChannelTransferSet(hwAttrs->rxChannelIndex | UDMA_PRI_SELECT,
211 UDMA_MODE_BASIC, (void *) (hwAttrs->baseAddr + MCSPI_O_RX0), buf,
212 object->currentXferAmt);
213
214 /* A lock is needed because we are accessing shared uDMA memory */
215 key = HwiP_disable();
216
217 /*
218 * DMA channels 30 and 31 are connected to the SPI peripheral by default.
219 * If someone hasn't remapped them to something else already, we remap
220 * them to SW.
221 */
222 if (!(HWREG(UDMA_BASE + UDMA_O_CHMAP3) & UDMA_CHMAP3_CH30SEL_M)) {
223 MAP_uDMAChannelAssign(UDMA_CH30_SW);
224 }
225 if (!(HWREG(UDMA_BASE + UDMA_O_CHMAP3) & UDMA_CHMAP3_CH31SEL_M)) {
226 MAP_uDMAChannelAssign(UDMA_CH31_SW);
227 }
228
229 /* Assign the requested DMA channels */
230 MAP_uDMAChannelAssign(hwAttrs->rxChannelIndex);
231 MAP_uDMAChannelAssign(hwAttrs->txChannelIndex);
232
233 /* Enable DMA to generate interrupt on SPI peripheral */
234 MAP_SPIDmaEnable(hwAttrs->baseAddr, SPI_RX_DMA | SPI_TX_DMA);
235 MAP_SPIIntClear(hwAttrs->baseAddr, SPI_INT_DMARX);
236 MAP_SPIIntEnable(hwAttrs->baseAddr, SPI_INT_DMARX);
237 MAP_SPIWordCountSet(hwAttrs->baseAddr, object->currentXferAmt);
238
239 /* Enable channels & start DMA transfers */
240 MAP_uDMAChannelEnable(hwAttrs->txChannelIndex);
241 MAP_uDMAChannelEnable(hwAttrs->rxChannelIndex);
242
243 HwiP_restore(key);
244
245 MAP_SPIEnable(hwAttrs->baseAddr);
246 MAP_SPICSEnable(hwAttrs->baseAddr);
247 }
248
249 /*
250 * ======== getDmaRemainingXfers ========
251 */
getDmaRemainingXfers(SPICC32XXDMA_HWAttrsV1 const * hwAttrs)252 static inline uint32_t getDmaRemainingXfers(SPICC32XXDMA_HWAttrsV1 const *hwAttrs) {
253 uint32_t controlWord;
254 tDMAControlTable *controlTable;
255
256 controlTable = MAP_uDMAControlBaseGet();
257 controlWord = controlTable[(hwAttrs->rxChannelIndex & 0x3f)].ulControl;
258
259 return (((controlWord & UDMA_CHCTL_XFERSIZE_M) >> 4) + 1);
260 }
261
262 /*
263 * ======== getPowerMgrId ========
264 */
getPowerMgrId(uint32_t baseAddr)265 static uint16_t getPowerMgrId(uint32_t baseAddr)
266 {
267 switch (baseAddr) {
268 case GSPI_BASE:
269 return (PowerCC32XX_PERIPH_GSPI);
270 case LSPI_BASE:
271 return (PowerCC32XX_PERIPH_LSPI);
272 default:
273 return (~0);
274 }
275 }
276
277 /*
278 * ======== initHw ========
279 */
initHw(SPICC32XXDMA_Object * object,SPICC32XXDMA_HWAttrsV1 const * hwAttrs)280 static void initHw(SPICC32XXDMA_Object *object,
281 SPICC32XXDMA_HWAttrsV1 const *hwAttrs)
282 {
283 /*
284 * SPI peripheral should remain disabled until a transfer is requested.
285 * This is done to prevent the RX FIFO from gathering data from other
286 * transfers.
287 */
288 MAP_SPICSDisable(hwAttrs->baseAddr);
289 MAP_SPIDisable(hwAttrs->baseAddr);
290 MAP_SPIReset(hwAttrs->baseAddr);
291
292 MAP_SPIConfigSetExpClk(hwAttrs->baseAddr,
293 MAP_PRCMPeripheralClockGet(hwAttrs->spiPRCM), object->bitRate,
294 mode[object->spiMode], object->frameFormat,
295 (hwAttrs->csControl | hwAttrs->pinMode | hwAttrs->turboMode |
296 hwAttrs->csPolarity | ((object->dataSize - 1) << 7)));
297
298 MAP_SPIFIFOEnable(hwAttrs->baseAddr, SPI_RX_FIFO | SPI_TX_FIFO);
299 MAP_SPIFIFOLevelSet(hwAttrs->baseAddr, object->txFifoTrigger,
300 object->rxFifoTrigger);
301 }
302
303 /*
304 * ======== postNotifyFxn ========
305 */
postNotifyFxn(unsigned int eventType,uintptr_t eventArg,uintptr_t clientArg)306 static int postNotifyFxn(unsigned int eventType, uintptr_t eventArg,
307 uintptr_t clientArg)
308 {
309 SPICC32XXDMA_Object *object = ((SPI_Handle) clientArg)->object;
310 SPICC32XXDMA_HWAttrsV1 const *hwAttrs = ((SPI_Handle) clientArg)->hwAttrs;
311
312 initHw(object, hwAttrs);
313
314 return (Power_NOTIFYDONE);
315 }
316
317 /*
318 * ======== spiHwiFxn ========
319 */
spiHwiFxn(uintptr_t arg)320 static void spiHwiFxn(uintptr_t arg)
321 {
322 uint32_t intFlags;
323 SPI_Transaction *msg;
324 SPICC32XXDMA_Object *object = ((SPI_Handle)arg)->object;
325 SPICC32XXDMA_HWAttrsV1 const *hwAttrs = ((SPI_Handle)arg)->hwAttrs;
326
327 /*
328 * Although the DMATX interrupt is not used by this driver, it seems like
329 * it is still triggering DMA interrupts. The code below will clear &
330 * disable the interrupt thus reducing the amount of spurious interrupts.
331 */
332 intFlags = MAP_SPIIntStatus(hwAttrs->baseAddr, false);
333 if (intFlags & SPI_INT_DMATX) {
334 MAP_SPIIntDisable(hwAttrs->baseAddr, SPI_INT_DMATX);
335 MAP_SPIIntClear(hwAttrs->baseAddr, SPI_INT_DMATX);
336 }
337
338 if (MAP_uDMAChannelIsEnabled(hwAttrs->rxChannelIndex)) {
339 /* DMA has not completed if the channel is still enabled */
340 return;
341 }
342
343 /* RX DMA channel has completed; disable peripheral */
344 MAP_SPIDmaDisable(hwAttrs->baseAddr, SPI_RX_DMA | SPI_TX_DMA);
345 MAP_SPIIntDisable(hwAttrs->baseAddr, SPI_INT_DMARX);
346 MAP_SPIIntClear(hwAttrs->baseAddr, SPI_INT_DMARX);
347 MAP_SPICSDisable(hwAttrs->baseAddr);
348 MAP_SPIDisable(hwAttrs->baseAddr);
349
350 if (object->transaction->count - object->amtDataXferred >
351 MAX_DMA_TRANSFER_AMOUNT) {
352 /* Data still remaining, configure another DMA transfer */
353 object->amtDataXferred += object->currentXferAmt;
354
355 configDMA(object, hwAttrs, object->transaction);
356 }
357 else {
358 /* All data sent; set status, perform callback & return */
359 object->transaction->status = SPI_TRANSFER_COMPLETED;
360
361 /* Release constraint since transaction is done */
362 Power_releaseConstraint(PowerCC32XX_DISALLOW_LPDS);
363
364 /*
365 * Use a temporary transaction pointer in case the callback function
366 * attempts to perform another SPI_transfer call
367 */
368 msg = object->transaction;
369
370 /* Indicate we are done with this transfer */
371 object->transaction = NULL;
372
373 object->transferCallbackFxn((SPI_Handle) arg, msg);
374 }
375 }
376
377 /*
378 * ======== spiPollingTransfer ========
379 */
spiPollingTransfer(SPICC32XXDMA_Object * object,SPICC32XXDMA_HWAttrsV1 const * hwAttrs,SPI_Transaction * transaction)380 static inline void spiPollingTransfer(SPICC32XXDMA_Object *object,
381 SPICC32XXDMA_HWAttrsV1 const *hwAttrs, SPI_Transaction *transaction)
382 {
383 uint8_t increment;
384 uint32_t dummyBuffer;
385 size_t transferCount;
386 void *rxBuf;
387 void *txBuf;
388
389 if (transaction->rxBuf) {
390 rxBuf = transaction->rxBuf;
391 }
392 else {
393 rxBuf = hwAttrs->scratchBufPtr;
394 }
395
396 if (transaction->txBuf) {
397 txBuf = transaction->txBuf;
398 }
399 else {
400 *hwAttrs->scratchBufPtr = hwAttrs->defaultTxBufValue;
401 txBuf = hwAttrs->scratchBufPtr;
402 }
403
404 if (object->dataSize < 9) {
405 increment = sizeof(uint8_t);
406 }
407 else if (object->dataSize < 17) {
408 increment = sizeof(uint16_t);
409 }
410 else {
411 increment = sizeof(uint32_t);
412 }
413
414 transferCount = transaction->count;
415
416 /*
417 * Start the polling transfer - we MUST set word count to 0; not doing so
418 * will raise spurious RX interrupts flags (though interrupts are not
419 * enabled).
420 */
421 MAP_SPIWordCountSet(hwAttrs->baseAddr, 0);
422 MAP_SPIEnable(hwAttrs->baseAddr);
423 MAP_SPICSEnable(hwAttrs->baseAddr);
424
425 while (transferCount--) {
426 if (object->dataSize < 9) {
427 MAP_SPIDataPut(hwAttrs->baseAddr, *((uint8_t *) txBuf));
428 MAP_SPIDataGet(hwAttrs->baseAddr, (unsigned long *)&dummyBuffer);
429 *((uint8_t *) rxBuf) = (uint8_t) dummyBuffer;
430 }
431 else if (object->dataSize < 17) {
432 MAP_SPIDataPut(hwAttrs->baseAddr, *((uint16_t *) txBuf));
433 MAP_SPIDataGet(hwAttrs->baseAddr, (unsigned long *) &dummyBuffer);
434 *((uint16_t *) rxBuf) = (uint16_t) dummyBuffer;
435 }
436 else {
437 MAP_SPIDataPut(hwAttrs->baseAddr, *((uint32_t *) txBuf));
438 MAP_SPIDataGet(hwAttrs->baseAddr, (unsigned long * ) rxBuf);
439 }
440
441 /* Only increment source & destination if buffers were provided */
442 if (transaction->rxBuf) {
443 rxBuf = (void *) (((uint32_t) rxBuf) + increment);
444 }
445 if (transaction->txBuf) {
446 txBuf = (void *) (((uint32_t) txBuf) + increment);
447 }
448 }
449
450 MAP_SPICSDisable(hwAttrs->baseAddr);
451 MAP_SPIDisable(hwAttrs->baseAddr);
452 }
453
454 /*
455 * ======== SPICC32XXDMA_close ========
456 */
SPICC32XXDMA_close(SPI_Handle handle)457 void SPICC32XXDMA_close(SPI_Handle handle)
458 {
459 uint32_t padRegister;
460 SPICC32XXDMA_Object *object = handle->object;
461 SPICC32XXDMA_HWAttrsV1 const *hwAttrs = handle->hwAttrs;
462
463 MAP_SPICSDisable(hwAttrs->baseAddr);
464 MAP_SPIDisable(hwAttrs->baseAddr);
465 MAP_SPIFIFODisable(hwAttrs->baseAddr, SPI_RX_FIFO | SPI_TX_FIFO);
466
467 /* Release power dependency on SPI. */
468 Power_releaseDependency(getPowerMgrId(hwAttrs->baseAddr));
469 Power_unregisterNotify(&(object->notifyObj));
470
471 if (object->hwiHandle) {
472 HwiP_delete(object->hwiHandle);
473 object->hwiHandle = NULL;
474 }
475 if (object->transferComplete) {
476 SemaphoreP_delete(object->transferComplete);
477 object->transferComplete = NULL;
478 }
479
480 if (object->dmaHandle) {
481 UDMACC32XX_close(object->dmaHandle);
482 object->dmaHandle = NULL;
483 }
484
485 /* Restore pin pads to their reset states */
486 if (hwAttrs->mosiPin != SPICC32XXDMA_PIN_NO_CONFIG) {
487 padRegister = (PinToPadGet((hwAttrs->mosiPin) & 0xff)<<2)
488 + PAD_CONFIG_BASE;
489 HWREG(padRegister) = PAD_RESET_STATE;
490 }
491 if (hwAttrs->misoPin != SPICC32XXDMA_PIN_NO_CONFIG) {
492 padRegister = (PinToPadGet((hwAttrs->misoPin) & 0xff)<<2)
493 + PAD_CONFIG_BASE;
494 HWREG(padRegister) = PAD_RESET_STATE;
495 }
496 if (hwAttrs->clkPin != SPICC32XXDMA_PIN_NO_CONFIG) {
497 padRegister = (PinToPadGet((hwAttrs->clkPin) & 0xff)<<2)
498 + PAD_CONFIG_BASE;
499 HWREG(padRegister) = PAD_RESET_STATE;
500 }
501 if ((hwAttrs->pinMode == SPI_4PIN_MODE) &&
502 (hwAttrs->csPin != SPICC32XXDMA_PIN_NO_CONFIG)) {
503 padRegister = (PinToPadGet((hwAttrs->csPin) & 0xff)<<2)
504 + PAD_CONFIG_BASE;
505 HWREG(padRegister) = PAD_RESET_STATE;
506 }
507
508 object->isOpen = false;
509 }
510
511 /*
512 * ======== SPICC32XXDMA_control ========
513 */
SPICC32XXDMA_control(SPI_Handle handle,uint_fast16_t cmd,void * arg)514 int_fast16_t SPICC32XXDMA_control(SPI_Handle handle, uint_fast16_t cmd, void *arg)
515 {
516 return (SPI_STATUS_UNDEFINEDCMD);
517 }
518
519 /*
520 * ======== SPICC32XXDMA_init ========
521 */
SPICC32XXDMA_init(SPI_Handle handle)522 void SPICC32XXDMA_init(SPI_Handle handle)
523 {
524 UDMACC32XX_init();
525 }
526
527 /*
528 * ======== SPICC32XXDMA_open ========
529 */
SPICC32XXDMA_open(SPI_Handle handle,SPI_Params * params)530 SPI_Handle SPICC32XXDMA_open(SPI_Handle handle, SPI_Params *params)
531 {
532 uintptr_t key;
533 uint16_t pin;
534 uint16_t mode;
535 uint8_t powerMgrId;
536 HwiP_Params hwiParams;
537 SPICC32XXDMA_Object *object = handle->object;
538 SPICC32XXDMA_HWAttrsV1 const *hwAttrs = handle->hwAttrs;
539
540 key = HwiP_disable();
541
542 if (object->isOpen) {
543 HwiP_restore(key);
544
545 return (NULL);
546 }
547 object->isOpen = true;
548
549 HwiP_restore(key);
550
551 /* SPI_TI & SPI_MW are not supported */
552 if (params->frameFormat == SPI_TI || params->frameFormat == SPI_MW) {
553 object->isOpen = false;
554
555 return (NULL);
556 }
557
558 /* Register power dependency - i.e. power up and enable clock for SPI. */
559 powerMgrId = getPowerMgrId(hwAttrs->baseAddr);
560 if (powerMgrId > PowerCC32XX_NUMRESOURCES) {
561 object->isOpen = false;
562
563 return (NULL);
564 }
565 Power_setDependency(powerMgrId);
566 Power_registerNotify(&(object->notifyObj), PowerCC32XX_AWAKE_LPDS,
567 postNotifyFxn, (uintptr_t) handle);
568
569 /* Configure the pins */
570 if (hwAttrs->mosiPin != SPICC32XXDMA_PIN_NO_CONFIG) {
571 pin = (hwAttrs->mosiPin) & 0xff;
572 mode = (hwAttrs->mosiPin >> 8) & 0xff;
573 MAP_PinTypeSPI((unsigned long) pin, (unsigned long) mode);
574 }
575
576 if (hwAttrs->misoPin != SPICC32XXDMA_PIN_NO_CONFIG) {
577 pin = (hwAttrs->misoPin) & 0xff;
578 mode = (hwAttrs->misoPin >> 8) & 0xff;
579 MAP_PinTypeSPI((unsigned long) pin, (unsigned long) mode);
580 }
581
582 if (hwAttrs->clkPin != SPICC32XXDMA_PIN_NO_CONFIG) {
583 pin = (hwAttrs->clkPin) & 0xff;
584 mode = (hwAttrs->clkPin >> 8) & 0xff;
585 MAP_PinTypeSPI((unsigned long) pin, (unsigned long) mode);
586 }
587
588 if (hwAttrs->pinMode == SPI_4PIN_MODE) {
589 if (hwAttrs->csPin != SPICC32XXDMA_PIN_NO_CONFIG) {
590 pin = (hwAttrs->csPin) & 0xff;
591 mode = (hwAttrs->csPin >> 8) & 0xff;
592 MAP_PinTypeSPI((unsigned long) pin, (unsigned long) mode);
593 }
594 }
595
596 object->dmaHandle = UDMACC32XX_open();
597 if (object->dmaHandle == NULL) {
598 SPICC32XXDMA_close(handle);
599
600 return (NULL);
601 }
602
603 HwiP_Params_init(&hwiParams);
604 hwiParams.arg = (uintptr_t) handle;
605 hwiParams.priority = hwAttrs->intPriority;
606 object->hwiHandle = HwiP_create(hwAttrs->intNum, spiHwiFxn, &hwiParams);
607 if (object->hwiHandle == NULL) {
608 SPICC32XXDMA_close(handle);
609
610 return (NULL);
611 }
612
613 if (params->transferMode == SPI_MODE_BLOCKING) {
614 /*
615 * Create a semaphore to block task execution for the duration of the
616 * SPI transfer
617 */
618 object->transferComplete = SemaphoreP_createBinary(0);
619 if (object->transferComplete == NULL) {
620 SPICC32XXDMA_close(handle);
621
622 return (NULL);
623 }
624
625 object->transferCallbackFxn = blockingTransferCallback;
626 }
627 else {
628 if (params->transferCallbackFxn == NULL) {
629 SPICC32XXDMA_close(handle);
630
631 return (NULL);
632 }
633
634 object->transferCallbackFxn = params->transferCallbackFxn;
635 }
636
637 object->bitRate = params->bitRate;
638 object->dataSize = params->dataSize;
639 object->frameFormat = params->frameFormat;
640 object->spiMode = params->mode;
641 object->transaction = NULL;
642 object->transferMode = params->transferMode;
643 object->transferTimeout = params->transferTimeout;
644
645 /* SPI FIFO trigger sizes vary based on data frame size */
646 if (object->dataSize < 9) {
647 object->rxFifoTrigger = sizeof(uint8_t);
648 object->txFifoTrigger = sizeof(uint8_t);
649 }
650 else if (object->dataSize < 17) {
651 object->rxFifoTrigger = sizeof(uint16_t);
652 object->txFifoTrigger = sizeof(uint16_t);
653 }
654 else {
655 object->rxFifoTrigger = sizeof(uint32_t);
656 object->txFifoTrigger = sizeof(uint32_t);
657 }
658
659 initHw(object, hwAttrs);
660
661 return (handle);
662 }
663
664 /*
665 * ======== SPICC32XXDMA_transfer ========
666 */
SPICC32XXDMA_transfer(SPI_Handle handle,SPI_Transaction * transaction)667 bool SPICC32XXDMA_transfer(SPI_Handle handle, SPI_Transaction *transaction)
668 {
669 uintptr_t key;
670 uint8_t alignMask;
671 bool buffersAligned;
672 SPICC32XXDMA_Object *object = handle->object;
673 SPICC32XXDMA_HWAttrsV1 const *hwAttrs = handle->hwAttrs;
674
675 if ((transaction->count == 0) ||
676 (transaction->rxBuf == NULL && transaction->txBuf == NULL) ||
677 (hwAttrs->scratchBufPtr == NULL && (transaction->rxBuf == NULL ||
678 transaction->txBuf == NULL))) {
679 return (false);
680 }
681
682 key = HwiP_disable();
683
684 /*
685 * alignMask is used to determine if the RX/TX buffers addresses are
686 * aligned to the data frame size.
687 */
688 alignMask = (object->rxFifoTrigger - 1);
689 buffersAligned = ((((uint32_t) transaction->rxBuf & alignMask) == 0) &&
690 (((uint32_t) transaction->txBuf & alignMask) == 0));
691
692 if (object->transaction) {
693 HwiP_restore(key);
694
695 return (false);
696 }
697 else {
698 object->transaction = transaction;
699 object->transaction->status = SPI_TRANSFER_STARTED;
700 object->amtDataXferred = 0;
701 object->currentXferAmt = 0;
702 }
703
704 HwiP_restore(key);
705
706 /* Set constraints to guarantee transaction */
707 Power_setConstraint(PowerCC32XX_DISALLOW_LPDS);
708
709 /* Polling transfer if BLOCKING mode & transaction->count < threshold */
710 if ((object->transferMode == SPI_MODE_BLOCKING &&
711 transaction->count < hwAttrs->minDmaTransferSize) || !buffersAligned) {
712 spiPollingTransfer(object, hwAttrs, transaction);
713
714 /* Transaction completed; set status & mark SPI ready */
715 object->transaction->status = SPI_TRANSFER_COMPLETED;
716 object->transaction = NULL;
717
718 Power_releaseConstraint(PowerCC32XX_DISALLOW_LPDS);
719 }
720 else {
721 /* Perform a DMA backed SPI transfer */
722 configDMA(object, hwAttrs, transaction);
723
724 if (object->transferMode == SPI_MODE_BLOCKING) {
725 if (SemaphoreP_pend(object->transferComplete,
726 object->transferTimeout) != SemaphoreP_OK) {
727 /* Timeout occurred; cancel the transfer */
728 object->transaction->status = SPI_TRANSFER_FAILED;
729 SPICC32XXDMA_transferCancel(handle);
730
731 /*
732 * TransferCancel() performs callback which posts
733 * transferComplete semaphore. This call consumes this extra post.
734 */
735 SemaphoreP_pend(object->transferComplete, SemaphoreP_NO_WAIT);
736
737 return (false);
738 }
739 }
740 }
741
742 return (true);
743 }
744
745 /*
746 * ======== SPICC32XXDMA_transferCancel ========
747 */
SPICC32XXDMA_transferCancel(SPI_Handle handle)748 void SPICC32XXDMA_transferCancel(SPI_Handle handle)
749 {
750 uintptr_t key;
751 SPI_Transaction *msg;
752 SPICC32XXDMA_Object *object = handle->object;
753 SPICC32XXDMA_HWAttrsV1 const *hwAttrs = handle->hwAttrs;
754
755 /*
756 * There are 2 use cases in which to call transferCancel():
757 * 1. The driver is in CALLBACK mode.
758 * 2. The driver is in BLOCKING mode & there has been a transfer timeout.
759 */
760 if (object->transferMode == SPI_MODE_CALLBACK ||
761 object->transaction->status == SPI_TRANSFER_FAILED) {
762
763 key = HwiP_disable();
764
765 if (object->transaction == NULL || object->cancelInProgress) {
766 HwiP_restore(key);
767
768 return;
769 }
770 object->cancelInProgress = true;
771
772 /* Prevent interrupt from occurring while canceling the transfer */
773 HwiP_disableInterrupt(hwAttrs->intNum);
774 HwiP_clearInterrupt(hwAttrs->intNum);
775
776 /* Clear DMA configuration */
777 MAP_uDMAChannelDisable(hwAttrs->rxChannelIndex);
778 MAP_uDMAChannelDisable(hwAttrs->txChannelIndex);
779
780 MAP_SPIIntDisable(hwAttrs->baseAddr, SPI_INT_DMARX);
781 MAP_SPIIntClear(hwAttrs->baseAddr, SPI_INT_DMARX);
782 MAP_SPIDmaDisable(hwAttrs->baseAddr, SPI_RX_DMA | SPI_TX_DMA);
783
784 HwiP_restore(key);
785
786 /*
787 * Disables peripheral, clears all registers & reinitializes it to
788 * parameters used in SPI_open()
789 */
790 initHw(object, hwAttrs);
791
792 HwiP_enableInterrupt(hwAttrs->intNum);
793
794 /*
795 * Calculate amount of data which has already been sent & store
796 * it in transaction->count
797 */
798 object->transaction->count = object->amtDataXferred +
799 (object->currentXferAmt - getDmaRemainingXfers(hwAttrs));
800
801 /* Set status CANCELED if we did not cancel due to timeout */
802 if (object->transaction->status == SPI_TRANSFER_STARTED) {
803 object->transaction->status = SPI_TRANSFER_CANCELED;
804 }
805
806 /* Release constraint set during transaction */
807 Power_releaseConstraint(PowerCC32XX_DISALLOW_LPDS);
808
809 /*
810 * Use a temporary transaction pointer in case the callback function
811 * attempts to perform another SPI_transfer call
812 */
813 msg = object->transaction;
814
815 /* Indicate we are done with this transfer */
816 object->transaction = NULL;
817 object->cancelInProgress = false;
818 object->transferCallbackFxn(handle, msg);
819 }
820 }
821