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