1 /******************************************************************************
2  *  Filename:       spi.c
3  *
4  *  Description:    Driver for the Serial Peripheral Interface (SPI).
5  *
6  *  Copyright (c) 2023 Texas Instruments Incorporated
7  *
8  *  Redistribution and use in source and binary forms, with or without
9  *  modification, are permitted provided that the following conditions are met:
10  *
11  *  1) Redistributions of source code must retain the above copyright notice,
12  *     this list of conditions and the following disclaimer.
13  *
14  *  2) Redistributions in binary form must reproduce the above copyright notice,
15  *     this list of conditions and the following disclaimer in the documentation
16  *     and/or other materials provided with the distribution.
17  *
18  *  3) Neither the name of the copyright holder nor the names of its
19  *     contributors may be used to endorse or promote products derived from this
20  *     software without specific prior written permission.
21  *
22  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23  *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
26  *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29  *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30  *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31  *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  *  POSSIBILITY OF SUCH DAMAGE.
33  *
34  ******************************************************************************/
35 
36 #include "spi.h"
37 
38 // DSAMPLE default value limits based bit rate
39 #define DSAMPLE_MED_BITRATE  4000000U
40 #define DSAMPLE_HIGH_BITRATE 8000000U
41 
42 // Maximum serial clock divider value
43 #define SCR_MAXIMUM 0x000003FFU
44 
45 //*****************************************************************************
46 //
47 // Configures the serial peripheral port
48 //
49 //*****************************************************************************
SPIConfig(uint32_t baseAddr,uint32_t freq,uint32_t format,uint32_t mode,uint32_t bitRate,uint32_t dataSize,uint32_t dsample)50 static void SPIConfig(uint32_t baseAddr,
51                       uint32_t freq,
52                       uint32_t format,
53                       uint32_t mode,
54                       uint32_t bitRate,
55                       uint32_t dataSize,
56                       uint32_t dsample)
57 {
58     uint16_t scr;
59     uint32_t ratio;
60 
61     // Get existing settings
62     uint32_t reg  = HWREG(baseAddr + SPI_O_CTL0);
63     // Create mask for settings to modify
64     uint32_t mask = (SPI_CTL0_DSS_M | SPI_CTL0_FRF_M | SPI_CTL0_SPO_M | SPI_CTL0_SPH_M);
65 
66     // Convert and mask data size to HW register format
67     dataSize = (SPI_CTL0_DSS_M & (dataSize - 1));
68 
69     // Apply updated register
70     HWREG(baseAddr + SPI_O_CTL0) = (reg & ~mask) | format | dataSize;
71 
72     // Set controller/peripheral mode and MSB first for shift reg
73     HWREG(baseAddr + SPI_O_CTL1) = mode | SPI_CTL1_MSB_MSB;
74 
75     // Get existing settings
76     reg = HWREG(baseAddr + SPI_O_CLKCFG1);
77 
78     // Create a mask for settings to modify
79     mask = (SPI_CLKCFG1_DSAMPLE_M | SPI_CLKCFG1_SCR_M);
80 
81     // Calculate scr variable
82     ratio = freq / (2U * bitRate);
83     if ((ratio > 0U) && (ratio <= SCR_MAXIMUM))
84     {
85         scr = (uint16_t)(ratio - 1U);
86     }
87     else
88     {
89         scr = 0U;
90     }
91 
92     // Set clock divider
93     HWREG(baseAddr + SPI_O_CLKCFG1) = (reg & ~mask) | dsample | scr;
94 }
95 
SPIConfigSetExpClk(uint32_t base,uint32_t spiClk,uint32_t protocol,uint32_t mode,uint32_t bitRate,uint32_t dataWidth)96 void SPIConfigSetExpClk(uint32_t base,
97                         uint32_t spiClk,
98                         uint32_t protocol,
99                         uint32_t mode,
100                         uint32_t bitRate,
101                         uint32_t dataWidth)
102 {
103     uint32_t dsample = 0U;
104 
105     ASSERT(SPIBaseValid(base));
106 
107     if (bitRate >= DSAMPLE_MED_BITRATE)
108     {
109         dsample = 1U << SPI_CLKCFG1_DSAMPLE_S;
110     }
111     else if (bitRate >= DSAMPLE_HIGH_BITRATE)
112     {
113         dsample = 2U << SPI_CLKCFG1_DSAMPLE_S;
114     }
115 
116     SPIConfig(base, spiClk, protocol, mode, bitRate, dataWidth, dsample);
117 }
118 
119 //*****************************************************************************
120 //
121 // Puts a data element into the SPI transmit FIFO
122 //
123 //*****************************************************************************
SPIPutDataNonBlocking(uint32_t base,uint32_t data)124 int32_t SPIPutDataNonBlocking(uint32_t base, uint32_t data)
125 {
126     ASSERT(SPIBaseValid(base));
127 
128     // Check for space to write.
129     if (HWREG(base + SPI_O_STA) & SPI_STA_TNF_NOT_FULL)
130     {
131         // Write the data to the SPI TX FIFO.
132         HWREG(base + SPI_O_TXDATA) = data;
133         return (1);
134     }
135     else
136     {
137         return (0);
138     }
139 }
140 
141 //*****************************************************************************
142 //
143 // Puts a data element into the SPI transmit FIFO
144 //
145 //*****************************************************************************
SPIPutData(uint32_t base,uint32_t data)146 void SPIPutData(uint32_t base, uint32_t data)
147 {
148     ASSERT(SPIBaseValid(base));
149 
150     // Check for space to write.
151     while (!(HWREG(base + SPI_O_STA) & SPI_STA_TNF_NOT_FULL)) {}
152 
153     // Write the data to the SPI TX FIFO.
154     HWREG(base + SPI_O_TXDATA) = data;
155 }
156 
157 //*****************************************************************************
158 //
159 // Gets a data element from the SPI receive FIFO
160 //
161 //*****************************************************************************
SPIGetData(uint32_t base,uint32_t * data)162 void SPIGetData(uint32_t base, uint32_t *data)
163 {
164     ASSERT(SPIBaseValid(base));
165 
166     // Wait until there is data to be read.
167     while (HWREG(base + SPI_O_STA) & SPI_STA_RFE_EMPTY) {}
168 
169     // Read data from SPI RX FIFO.
170     *data = HWREG(base + SPI_O_RXDATA);
171 }
172 
173 //*****************************************************************************
174 //
175 // Gets a data element from the SPI receive FIFO
176 //
177 //*****************************************************************************
SPIGetDataNonBlocking(uint32_t base,uint32_t * data)178 int32_t SPIGetDataNonBlocking(uint32_t base, uint32_t *data)
179 {
180     // Check the arguments
181     ASSERT(SPIBaseValid(base));
182 
183     // Check for data to read
184     if (!(HWREG(base + SPI_O_STA) & SPI_STA_RFE_EMPTY))
185     {
186         *data = HWREG(base + SPI_O_RXDATA);
187         return (1);
188     }
189     else
190     {
191         return (0);
192     }
193 }
194 
195 //*****************************************************************************
196 //
197 // Registers an interrupt handler for the serial peripheral port
198 //
199 //*****************************************************************************
SPIRegisterInt(uint32_t base,void (* pfnHandler)(void))200 void SPIRegisterInt(uint32_t base, void (*pfnHandler)(void))
201 {
202     // Check the arguments
203     ASSERT(SPIBaseValid(base));
204 
205     // Register the interrupt handler
206     IntRegister((uint32_t)INT_SPI0_COMB, pfnHandler);
207 
208     // Enable the synchronous serial port interrupt
209     IntEnable((uint32_t)INT_SPI0_COMB);
210 }
211 
212 //*****************************************************************************
213 //
214 // Unregisters an interrupt handler for the serial peripheral port
215 //
216 //*****************************************************************************
SPIUnregisterInt(uint32_t base)217 void SPIUnregisterInt(uint32_t base)
218 {
219     // Check the arguments
220     ASSERT(SPIBaseValid(base));
221 
222     // Disable the interrupt
223     IntDisable((uint32_t)INT_SPI0_COMB);
224 
225     // Unregister the interrupt handler
226     IntUnregister((uint32_t)INT_SPI0_COMB);
227 }
228