1  /**
2  * @file       spimss.c
3  * @brief      This file contains the function implementations for the
4  *             Serial Peripheral Interface (SPIMSS) peripheral module.
5  */
6 
7 /* *****************************************************************************
8  * Copyright (C) 2017 Maxim Integrated Products, Inc., All Rights Reserved.
9  *
10  * Permission is hereby granted, free of charge, to any person obtaining a
11  * copy of this software and associated documentation files (the "Software"),
12  * to deal in the Software without restriction, including without limitation
13  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14  * and/or sell copies of the Software, and to permit persons to whom the
15  * Software is furnished to do so, subject to the following conditions:
16  *
17  * The above copyright notice and this permission notice shall be included
18  * in all copies or substantial portions of the Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
23  * IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
24  * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
25  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
26  * OTHER DEALINGS IN THE SOFTWARE.
27  *
28  * Except as contained in this notice, the name of Maxim Integrated
29  * Products, Inc. shall not be used except as stated in the Maxim Integrated
30  * Products, Inc. Branding Policy.
31  *
32  * The mere transfer of this software does not imply any licenses
33  * of trade secrets, proprietary technology, copyrights, patents,
34  * trademarks, maskwork rights, or any other form of intellectual
35  * property whatsoever. Maxim Integrated Products, Inc. retains all
36  * ownership rights.
37  *
38  * $Date: 2019-05-06 14:44:04 -0500 (Mon, 06 May 2019) $
39  * $Revision: 43157 $
40  *
41  **************************************************************************** */
42 
43 /* **** Includes **** */
44 #include <string.h>
45 #include <stdio.h>
46 #include <stdint.h>
47 #include "mxc_config.h"
48 #include "mxc_assert.h"
49 #include "mxc_sys.h"
50 #include "spimss.h"
51 #include "mxc_lock.h"
52 
53 /**
54  * @ingroup spimss
55  * @{
56  */
57 
58 /* **** Definitions **** */
59 
60 /* **** Globals **** */
61 typedef struct {
62     spimss_req_t *req;
63 } spimss_req_state_t;
64 
65 static spimss_req_state_t states[MXC_SPIMSS_INSTANCES];
66 
67 
68 /* **** Functions **** */
69 static int SPIMSS_TransSetup(mxc_spimss_regs_t *spi, spimss_req_t *req, int master);
70 static uint32_t SPIMSS_MasterTransHandler(mxc_spimss_regs_t *spi, spimss_req_t *req);
71 static uint32_t SPIMSS_TransHandler(mxc_spimss_regs_t *spi, spimss_req_t *req);
72 static uint32_t SPIMSS_SlaveTransHandler(mxc_spimss_regs_t *spi, spimss_req_t *req);
73 
74 /* ************************************************************************** */
SPIMSS_Init(mxc_spimss_regs_t * spi,unsigned mode,unsigned freq,const sys_cfg_spimss_t * sys_cfg)75 int SPIMSS_Init(mxc_spimss_regs_t *spi, unsigned mode, unsigned freq, const sys_cfg_spimss_t* sys_cfg)
76 {
77     int spi_num, error;
78     unsigned int spimss_clk;
79     unsigned int pol, pha;  // Polarity and phase of the clock (SPI mode)
80 
81     spi_num = MXC_SPIMSS_GET_IDX(spi);
82 
83     MXC_ASSERT(spi_num >= 0);
84 
85     if (mode > 3) {
86         return E_BAD_PARAM;
87     }
88 
89     if ((error = SYS_SPIMSS_Init(spi, sys_cfg)) != E_NO_ERROR) {
90         return error;
91     }
92 
93     states[spi_num].req = NULL;
94     spi->ctrl &=  ~(MXC_F_SPIMSS_CTRL_SPIEN);  // Keep the SPI Disabled (This is the SPI Start)
95 
96     // Check if frequency is too high
97     if (freq > PeripheralClock) {
98         return E_BAD_PARAM;
99     }
100 
101      // Set the bit rate
102     spimss_clk = PeripheralClock;
103     spi->brg = (spimss_clk / freq) >> 1;
104 
105     // Set the mode
106     pol = mode >> 1;  // Get the polarity out of the mode input value
107     pha = mode &  1;  // Get the phase out of the mode input value
108 
109     spi->ctrl = (spi->ctrl & ~(MXC_F_SPIMSS_CTRL_CLKPOL)) | (pol << MXC_F_SPIMSS_CTRL_CLKPOL_POS);  // polarity
110 
111     spi->ctrl = (spi->ctrl & ~(MXC_F_SPIMSS_CTRL_PHASE))  | (pha << MXC_F_SPIMSS_CTRL_PHASE_POS);   // phase
112 
113     spi->status &= ~(MXC_F_SPIMSS_STATUS_IRQ);
114 
115     return E_NO_ERROR;
116 }
117 /* ************************************************************************* */
SPIMSS_Shutdown(mxc_spimss_regs_t * spi)118 int SPIMSS_Shutdown(mxc_spimss_regs_t *spi)
119 {
120     int spi_num, err;
121     spimss_req_t *temp_req;
122 
123     // Disable and turn off the SPI transaction.
124     spi->ctrl = 0;    // Interrupts, SPI transaction all turned off
125     spi->status = 0;
126     spi->mod  = 0;
127 
128     // Reset FIFO counters
129     spi->dma &= ~(MXC_F_SPIMSS_DMA_RX_FIFO_CNT|MXC_F_SPIMSS_DMA_TX_FIFO_CNT);
130 
131     // Call all of the pending callbacks for this SPI
132     spi_num = MXC_SPIMSS_GET_IDX(spi);
133     if (states[spi_num].req != NULL) {
134 
135         // Save the request
136         temp_req = states[spi_num].req;
137 
138         // Unlock this SPI
139         mxc_free_lock((uint32_t*)&states[spi_num].req);
140 
141         // Callback if not NULL
142         if (temp_req->callback != NULL) {
143             temp_req->callback(temp_req, E_SHUTDOWN);
144         }
145     }
146 
147     spi->status = 0;
148 
149     // Clear system level configurations
150     if ((err = SYS_SPIMSS_Shutdown(spi)) != E_NO_ERROR) {
151         return err;
152     }
153 
154     return E_NO_ERROR;
155 }
156 
157 /* ************************************************************************** */
SPIMSS_TransSetup(mxc_spimss_regs_t * spi,spimss_req_t * req,int master)158 int SPIMSS_TransSetup(mxc_spimss_regs_t *spi, spimss_req_t *req, int master)
159 {
160     int spi_num;
161 
162     spi->ctrl &= ~(MXC_F_SPIMSS_CTRL_SPIEN);  // Make sure the Initiation
163 	                                          // of SPI Start is disabled.
164 
165     spi->mod |= MXC_F_SPIMSS_MOD_TX_LJ;       // Making sure data is left
166 	                                          // justified.
167 
168     if ((req->tx_data == NULL) && (req->rx_data == NULL)) {
169         return -1;
170     }
171 
172     spi_num = MXC_SPIMSS_GET_IDX(spi);
173     MXC_ASSERT(spi_num >= 0);
174 
175     if (req->len == 0) {
176         return 0;
177     }
178 
179     req->tx_num = 0;
180     req->rx_num = 0;
181 
182     if (mxc_get_lock((uint32_t*)&states[spi_num].req, (uint32_t)req) != E_NO_ERROR) {
183         return E_BUSY;
184     }
185 
186     if (master) { // Enable master mode
187         spi->ctrl |= MXC_F_SPIMSS_CTRL_MMEN;     // SPI configured as master.
188         spi->mod |= MXC_F_SPIMSS_CTRL_MMEN;       // SSEL pin is an output.
189     } else { // Enable slave mode
190         spi->ctrl &= ~(MXC_F_SPIMSS_CTRL_MMEN);  // SPI configured as slave.
191         spi->mod &= ~(MXC_F_SPIMSS_CTRL_MMEN);    // SSEL pin is an input.
192     }
193 
194     // Setup the character size
195 
196     if (req->bits <16) {
197         MXC_SETFIELD(spi->mod, MXC_F_SPIMSS_MOD_NUMBITS , req->bits <<  MXC_F_SPIMSS_MOD_NUMBITS_POS);
198 
199     } else {
200         MXC_SETFIELD(spi->mod, MXC_F_SPIMSS_MOD_NUMBITS , 0 <<  MXC_F_SPIMSS_MOD_NUMBITS_POS);
201 
202     }
203 
204     // Setup the slave select
205     spi->mod |= MXC_F_SPIMSS_MOD_SSV; // Assert a high on Slave Select,
206                                       // to get the line ready for active low later
207 
208     // Clear the TX and RX FIFO
209     spi->dma |= (MXC_F_SPIMSS_DMA_TX_FIFO_CLEAR | MXC_F_SPIMSS_DMA_RX_FIFO_CLEAR);
210 
211     return E_NO_ERROR;
212 }
213 
214 /* ************************************************************************** */
SPIMSS_Handler(mxc_spimss_regs_t * spi)215 void SPIMSS_Handler(mxc_spimss_regs_t *spi)  // From the IRQ
216 {
217     int spi_num;
218     uint32_t flags;
219     unsigned int int_enable;
220 
221     flags = spi->status;
222     spi->status = flags;
223     spi->status|= 0x80;    // clear interrupt
224 
225     spi_num = MXC_SPIMSS_GET_IDX(spi);
226 
227     int_enable = 0;
228     if (states[spi_num].req != NULL) {
229         if ((spi->ctrl  & MXC_F_SPIMSS_CTRL_MMEN) >> MXC_F_SPIMSS_CTRL_MMEN_POS) {
230             int_enable = SPIMSS_MasterTransHandler(spi, states[spi_num].req);
231 
232         } else {
233             int_enable = SPIMSS_SlaveTransHandler(spi, states[spi_num].req);
234         }
235     }
236 
237     if (int_enable==1) {
238         spi->ctrl |= (MXC_F_SPIMSS_CTRL_IRQE );
239 
240     }
241 }
242 
243 /* ************************************************************************** */
SPIMSS_MasterTrans(mxc_spimss_regs_t * spi,spimss_req_t * req)244 int SPIMSS_MasterTrans(mxc_spimss_regs_t *spi, spimss_req_t *req)
245 {
246     int error;
247 
248     if ((error = SPIMSS_TransSetup(spi, req, 1)) != E_NO_ERROR) {
249         return error;
250     }
251 
252     req->callback = NULL;
253 
254     spi->mod &= ~(MXC_F_SPIMSS_MOD_SSV);  // This will assert the Slave Select.
255     spi->ctrl |= MXC_F_SPIMSS_CTRL_SPIEN;  // Enable/Start SPI
256 
257     while (SPIMSS_MasterTransHandler(spi,req)!=0) {
258     }
259 
260     spi->mod |= MXC_F_SPIMSS_MOD_SSV;
261 
262     spi->ctrl &= ~(MXC_F_SPIMSS_CTRL_SPIEN); // Last of the SPIMSS value has been transmitted...
263                                              // stop the transmission...
264     return E_NO_ERROR;
265 }
266 
267 
268 /* ************************************************************************** */
SPIMSS_SlaveTrans(mxc_spimss_regs_t * spi,spimss_req_t * req)269 int SPIMSS_SlaveTrans(mxc_spimss_regs_t *spi, spimss_req_t *req)
270 {
271     int error;
272 
273     if ((error = SPIMSS_TransSetup(spi, req,0)) != E_NO_ERROR) {
274         return error;
275     }
276 
277     while (SPIMSS_SlaveTransHandler(spi,req)!=0) {
278         spi->ctrl |= MXC_F_SPIMSS_CTRL_SPIEN;  // Enable/Start SPI
279         while ((spi->status & MXC_F_SPIMSS_STATUS_TXST) == MXC_F_SPIMSS_STATUS_TXST) {}
280      }
281 
282     spi->ctrl &= ~(MXC_F_SPIMSS_CTRL_SPIEN);  // Last of the SPIMSS value has been transmitted...
283                                               // stop the transmission...
284     return E_NO_ERROR;
285 }
286 
287 /* ************************************************************************** */
SPIMSS_MasterTransAsync(mxc_spimss_regs_t * spi,spimss_req_t * req)288 int SPIMSS_MasterTransAsync(mxc_spimss_regs_t *spi, spimss_req_t *req)
289 {
290     int error;
291     uint8_t int_enable;
292     if ((error = SPIMSS_TransSetup(spi, req, 1) )!= E_NO_ERROR) {
293         return error;
294     }
295 
296     int_enable = SPIMSS_MasterTransHandler(spi,req);
297 
298     spi->mod ^= MXC_F_SPIMSS_MOD_SSV;      // This will assert the Slave Select.
299 
300     spi->ctrl |= MXC_F_SPIMSS_CTRL_SPIEN;  // Enable/Start SPI
301 
302     if (int_enable==1) {
303         spi->ctrl |= (MXC_F_SPIMSS_CTRL_IRQE | MXC_F_SPIMSS_CTRL_STR);
304     }
305 
306     return E_NO_ERROR;
307 }
308 
309 /* ************************************************************************** */
SPIMSS_SlaveTransAsync(mxc_spimss_regs_t * spi,spimss_req_t * req)310 int SPIMSS_SlaveTransAsync(mxc_spimss_regs_t *spi, spimss_req_t *req)
311 {
312     int error;
313     uint8_t int_enable;
314     if ((error = SPIMSS_TransSetup(spi, req, 0)) != E_NO_ERROR) {
315         return error;
316     }
317 
318     int_enable = SPIMSS_SlaveTransHandler(spi,req);
319 
320     spi->ctrl |= MXC_F_SPIMSS_CTRL_SPIEN;  // Enable/Start SPI
321 
322     if (int_enable==1) {                   // Trigger a SPI Interrupt
323         spi->ctrl |= (MXC_F_SPIMSS_CTRL_IRQE );
324     }
325 
326     return E_NO_ERROR;
327 }
328 
329 /* ************************************************************************** */
SPIMSS_MasterTransHandler(mxc_spimss_regs_t * spi,spimss_req_t * req)330 uint32_t SPIMSS_MasterTransHandler(mxc_spimss_regs_t *spi, spimss_req_t *req)
331 {
332     unsigned start_set = 0;
333     uint32_t retval;
334 
335     if (!start_set) {
336     start_set = 1;
337     retval = SPIMSS_TransHandler(spi,req);
338     }
339 
340     return retval;
341 }
342 
343 /* ************************************************************************** */
SPIMSS_SlaveTransHandler(mxc_spimss_regs_t * spi,spimss_req_t * req)344 uint32_t SPIMSS_SlaveTransHandler(mxc_spimss_regs_t *spi, spimss_req_t *req)
345 {
346     return SPIMSS_TransHandler(spi,req);
347 }
348 
349 /* ************************************************************************** */
SPIMSS_TransHandler(mxc_spimss_regs_t * spi,spimss_req_t * req)350 uint32_t SPIMSS_TransHandler(mxc_spimss_regs_t *spi, spimss_req_t *req)
351 {
352     unsigned tx_avail, rx_avail;
353     int remain, spi_num;
354     uint32_t int_en =0;
355     uint32_t length =req->len;
356 
357 
358     spi_num = MXC_SPIMSS_GET_IDX(spi);
359 
360     // Read the RX FIFO
361    if (req->rx_data != NULL) {
362         // Wait for there to be data in the RX FIFO
363         rx_avail = ((spi->dma & MXC_F_SPIMSS_DMA_RX_FIFO_CNT) >> MXC_F_SPIMSS_DMA_RX_FIFO_CNT_POS);
364         if ((length - req->rx_num) < rx_avail) {
365             rx_avail = (length - req->rx_num);
366         }
367 
368        // Read from the FIFO
369         while (rx_avail) {
370 
371             // Don't read less than 2 bytes if we are using greater than 8 bit characters
372             if (req->bits>8) {
373                 ((uint16_t*)req->rx_data)[req->rx_num++] = spi->data16;
374                 rx_avail -= 1;
375 
376             } else {
377                 ((uint8_t*)req->rx_data)[req->rx_num++] = spi->data8[0];
378                 rx_avail -= 1;
379             }
380             rx_avail = ((spi->dma & MXC_F_SPIMSS_DMA_RX_FIFO_CNT) >> MXC_F_SPIMSS_DMA_RX_FIFO_CNT_POS);
381             if ((length - req->rx_num) < rx_avail) {
382                 rx_avail = (length - req->rx_num);
383             }
384         }
385 
386         remain = length - req->rx_num;
387 
388         if (remain) {
389             if (remain > MXC_SPIMSS_FIFO_DEPTH) {
390                 spi->dma = ((spi->dma & ~MXC_F_SPIMSS_DMA_RX_FIFO_CNT) | ((2) << MXC_F_SPIMSS_DMA_RX_FIFO_CNT_POS));
391             } else {
392                 spi->dma = ((spi->dma & ~MXC_F_SPIMSS_DMA_RX_FIFO_CNT) | ((remain-1) << MXC_F_SPIMSS_DMA_RX_FIFO_CNT_POS));
393             }
394 
395             int_en = 1;
396         }
397 
398         // Break out if we've received all the bytes and we're not transmitting
399         if ((req->tx_data == NULL) && (req->rx_num == length)) {
400             spi->ctrl &= ~(MXC_F_SPIMSS_CTRL_IRQE | MXC_F_SPIMSS_CTRL_STR);
401             int_en = 0;
402             mxc_free_lock((uint32_t*)&states[spi_num].req);
403             // Callback if not NULL
404             if (req->callback != NULL) {
405                 req->callback(req, E_NO_ERROR);
406             }
407         }
408     }
409     // Note:- spi->dma shows the FIFO TX count and FIFO RX count in
410     // Words, while the calculation below is in bytes.
411     if (req->tx_data != NULL) {
412 
413         if (req->tx_num < length) {
414 
415             // Calculate how many bytes we can write to the FIFO (tx_avail holds that value)
416             tx_avail = MXC_SPIMSS_FIFO_DEPTH - (((spi->dma & MXC_F_SPIMSS_DMA_TX_FIFO_CNT) >> MXC_F_SPIMSS_DMA_TX_FIFO_CNT_POS));  // in bytes
417 
418             if ((length - req->tx_num) < tx_avail) {
419                 tx_avail = (length - req->tx_num);   // This is for the last spin
420             }
421             if (req->bits > 8) {
422                 tx_avail &= ~(unsigned)0x1;
423             }
424             // Write the FIFO
425             while (tx_avail) {
426                 if (req->bits >8) {
427                         spi->data16 = ((uint16_t*)req->tx_data)[req->tx_num++];
428 
429                         tx_avail -= 1;
430                 } else {
431                     spi->data8[0] = ((uint8_t*)req->tx_data)[req->tx_num++];
432                     tx_avail -=1;
433                 }
434 
435             }
436         }
437 
438         remain = length - req->tx_num;
439 
440         // If there are values remaining to be transmitted, this portion will get
441         // executed and int_en set, to indicate that this must spin and come back again...
442         if (remain) {
443             if (remain > MXC_SPIMSS_FIFO_DEPTH) {  //  more tx rounds will happen... Transfer the maximum,
444                 spi->dma = ((spi->dma & ~MXC_F_SPIMSS_DMA_TX_FIFO_CNT) | ((MXC_SPIMSS_FIFO_DEPTH) << MXC_F_SPIMSS_DMA_TX_FIFO_CNT_POS));
445             } else {  // only one more tx round will be done... Transfer whatever remains,
446                 spi->dma = ((spi->dma & ~MXC_F_SPIMSS_DMA_TX_FIFO_CNT) | ((remain) << MXC_F_SPIMSS_DMA_TX_FIFO_CNT_POS));
447             }
448             int_en = 1; // This will act as a trigger for the next round...
449         }
450 
451         // Break out if we've transmitted all the bytes and not receiving
452         if ((req->rx_data == NULL) && (req->tx_num == length)) {
453             spi->ctrl &= ~(MXC_F_SPIMSS_CTRL_IRQE | MXC_F_SPIMSS_CTRL_STR);
454             int_en = 0;
455             mxc_free_lock((uint32_t*)&states[spi_num].req);
456             // Callback if not NULL
457             if (req->callback != NULL) {
458                 req->callback(req, E_NO_ERROR);
459             }
460         }
461     }
462 
463 
464     // Break out once we've transmitted and received all of the data
465     if ((req->rx_num == length) && (req->tx_num == length)) {
466         spi->ctrl &= ~(MXC_F_SPIMSS_CTRL_IRQE | MXC_F_SPIMSS_CTRL_STR);
467         int_en = 0;
468         mxc_free_lock((uint32_t*)&states[spi_num].req);
469         // Callback if not NULL
470         if (req->callback != NULL) {
471             req->callback(req, E_NO_ERROR);
472         }
473     }
474 
475     return int_en;
476 }
477 
478 /* ************************************************************************* */
SPIMSS_AbortAsync(spimss_req_t * req)479 int SPIMSS_AbortAsync(spimss_req_t *req)
480 {
481     int spi_num;
482     mxc_spimss_regs_t *spi;
483 
484     // Check the input parameters
485     if (req == NULL) {
486         return E_BAD_PARAM;
487     }
488 
489     // Find the request, set to NULL
490     for (spi_num = 0; spi_num < MXC_SPIMSS_INSTANCES; spi_num++) {
491         if (req == states[spi_num].req) {
492 
493             spi = MXC_SPIMSS_GET_SPI(spi_num);
494 
495             // Disable interrupts, clear the flags
496             spi->ctrl &= ~(MXC_F_SPIMSS_CTRL_IRQE | MXC_F_SPIMSS_CTRL_STR);
497 
498             // Disable and turn off the SPI transaction.
499         	spi->ctrl &= ~(MXC_F_SPIMSS_CTRL_SPIEN);
500 
501             // Unlock this SPI
502             mxc_free_lock((uint32_t*)&states[spi_num].req);
503 
504             // Callback if not NULL
505             if (req->callback != NULL) {
506                 req->callback(req, E_ABORT);
507             }
508             return E_NO_ERROR;
509         }
510     }
511 
512     return E_BAD_PARAM;
513 }
514 /**@} end of group spimss */
515