Open Model Railroad Network (OpenMRN)
Loading...
Searching...
No Matches
CC32xxSPI.cxx
Go to the documentation of this file.
1
34#include "CC32xxSPI.hxx"
35
36#include <algorithm>
37#include <unistd.h>
38
39#include "inc/hw_gpio.h"
40#include "inc/hw_types.h"
41#include "inc/hw_memmap.h"
42#include "inc/hw_ints.h"
43#include "inc/hw_udma.h"
44#include "driverlib/gpio.h"
45#include "driverlib/rom.h"
46#include "driverlib/rom_map.h"
47#include "driverlib/spi.h"
48#include "driverlib/interrupt.h"
49#include "driverlib/prcm.h"
50
52static CC32xxSPI *instances_[1] = {NULL};
53
56
57constexpr uint32_t CC32xxSPI::dmaRxConfig_[];
58constexpr uint32_t CC32xxSPI::dmaTxConfig_[];
59constexpr uint32_t CC32xxSPI::dmaNullConfig_[];
60
72CC32xxSPI::CC32xxSPI(const char *name, unsigned long base, uint32_t interrupt,
73 ChipSelectMethod cs_assert, ChipSelectMethod cs_deassert,
74 OSMutex *bus_lock, size_t dma_threshold,
75 uint32_t dma_channel_index_tx,
76 uint32_t dma_channel_index_rx)
77 : SPI(name, cs_assert, cs_deassert, bus_lock)
78 , dmaHandle_(nullptr)
79 , base_(base)
80 , interrupt_(interrupt)
81 , dmaThreshold_(dma_threshold)
82 , dmaChannelIndexTx_(dma_channel_index_tx)
83 , dmaChannelIndexRx_(dma_channel_index_rx)
84{
85 switch (base_)
86 {
87 default:
88 HASSERT(0);
89 case GSPI_BASE:
90 MAP_PRCMPeripheralClkEnable(PRCM_GSPI, PRCM_RUN_MODE_CLK);
91 MAP_PRCMPeripheralReset(PRCM_GSPI);
92 clock_ = MAP_PRCMPeripheralClockGet(PRCM_GSPI);
93 UDMACC32XX_init();
94 instances_[0] = this;
95 sem_ = &sem[0];
96 break;
97 }
98
100}
101
106{
107 unsigned long new_mode;
108 unsigned long bits_per_word;
109 unsigned long fifo_level;
110
111 switch (mode_)
112 {
113 default:
114 case SPI_MODE_0:
115 new_mode = SPI_SUB_MODE_0;
116 break;
117 case SPI_MODE_1:
118 new_mode = SPI_SUB_MODE_1;
119 break;
120 case SPI_MODE_2:
121 new_mode = SPI_SUB_MODE_2;
122 break;
123 case SPI_MODE_3:
124 new_mode = SPI_SUB_MODE_3;
125 break;
126 }
127
128 switch (bitsPerWord)
129 {
130 default:
131 bitsPerWord = 8;
132 // fall through
133 case 8:
134 bits_per_word = SPI_WL_8;
135 fifo_level = sizeof(uint8_t);
136 break;
137 case 16:
138 bits_per_word = SPI_WL_16;
139 fifo_level = sizeof(uint16_t);
140 break;
141 case 32:
142 bits_per_word = SPI_WL_32;
143 fifo_level = sizeof(uint32_t);
144 break;
145 }
146
147 /* The SPI peripheral only supoprts certain frequencies and driverlib does
148 * not properly round down. We can do some math to make sure that driverlib
149 * makes the correct selection.
150 */
151 if (speedHz > (clock_ / 2))
152 {
153 speedHz = clock_ / 2;
154 }
155 else if ((clock_ % speedHz) != 0)
156 {
157 speedHz = clock_ / ((clock_ / speedHz) + 1);
158 }
159
160 MAP_SPIDisable(base_);
161 MAP_SPIReset(base_);
162 MAP_SPIConfigSetExpClk(base_, clock_, speedHz, SPI_MODE_MASTER, new_mode,
163 (SPI_3PIN_MODE | SPI_TURBO_ON | bits_per_word));
164 MAP_SPIFIFOEnable(base_, SPI_RX_FIFO | SPI_TX_FIFO);
165 MAP_SPIFIFOLevelSet(base_, fifo_level, fifo_level);
166 MAP_IntPrioritySet(interrupt_, configKERNEL_INTERRUPT_PRIORITY);
167 MAP_IntEnable(interrupt_);
168 MAP_SPIEnable(base_);
169
170 // these values are used to quickly program instance local configuration
171 // settings
172 spiChctrl_ = HWREG(base_ + MCSPI_O_CH0CTRL);
173 spiChconf_ = HWREG(base_ + MCSPI_O_CH0CONF);
174 spiXferlevel_ = HWREG(base_ + MCSPI_O_XFERLEVEL);
175
176 return 0;
177}
178
182__attribute__((optimize("-O3")))
183void CC32xxSPI::config_dma(struct spi_ioc_transfer *msg)
184{
185 static uint32_t scratch_buffer __attribute__((aligned(4))) = 0;
186
187 /* use DMA */
188 void *buf;
189 uint32_t channel_control_options;
190 unsigned config_index;
191 unsigned items;
192
193 /* set instance specific configuration */
194 set_configuration();
195
201 switch (bitsPerWord)
202 {
203 default:
204 case 8:
205 config_index = 0;
206 items = msg->len / sizeof(uint8_t);
207 break;
208 case 16:
209 config_index = 1;
210 items = msg->len / sizeof(uint16_t);
211 break;
212 case 32:
213 config_index = 2;
214 items = msg->len / sizeof(uint32_t);
215 break;
216 }
217
219 HASSERT(items <= MAX_DMA_TRANSFER_AMOUNT);
220
221 if (msg->tx_buf)
222 {
223 channel_control_options = dmaTxConfig_[config_index];
224 buf = (void*)msg->tx_buf;
225 }
226 else
227 {
228 channel_control_options = dmaNullConfig_[config_index];
229 buf = &scratch_buffer;
230 }
231
232 /* Setup the TX transfer characteristics & buffers */
233 uDMAChannelControlSet(dmaChannelIndexTx_ | UDMA_PRI_SELECT,
234 channel_control_options);
235#if 0
236 // This disabled section is provided as a driverlib reference to the
237 // faster enabled (#else) version inline.
238 uDMAChannelAttributeDisable(dmaChannelIndexTx_, UDMA_ATTR_ALTSELECT);
239#else
240 HWREG(UDMA_BASE + UDMA_O_ALTCLR) = 1 << (dmaChannelIndexTx_ & 0x1f);
241#endif
242 uDMAChannelTransferSet(dmaChannelIndexTx_ | UDMA_PRI_SELECT,
243 UDMA_MODE_BASIC, buf, (void*)(base_ + MCSPI_O_TX0),
244 items);
245
246 if (msg->rx_buf)
247 {
248 channel_control_options = dmaRxConfig_[config_index];
249 buf = (void*)msg->rx_buf;
250 }
251 else
252 {
253 channel_control_options = dmaNullConfig_[config_index];
254 buf = &scratch_buffer;
255 }
256
257 /* Setup the RX transfer characteristics & buffers */
258 uDMAChannelControlSet(dmaChannelIndexRx_ | UDMA_PRI_SELECT,
259 channel_control_options);
260#if 0
261 // This disabled section is provided as a driverlib reference to the
262 // faster enabled (#else) version inline.
263 uDMAChannelAttributeDisable(dmaChannelIndexRx_, UDMA_ATTR_ALTSELECT);
264#else
265 HWREG(UDMA_BASE + UDMA_O_ALTCLR) = 1 << (dmaChannelIndexRx_ & 0x1f);
266#endif
267 uDMAChannelTransferSet(dmaChannelIndexRx_ | UDMA_PRI_SELECT,
268 UDMA_MODE_BASIC, (void*)(base_ + MCSPI_O_RX0), buf,
269 items);
270
271 {
272 AtomicHolder h(this);
273
274 uDMAChannelAssign(dmaChannelIndexRx_);
275 uDMAChannelAssign(dmaChannelIndexTx_);
276
277 SPIDmaEnable(base_, SPI_RX_DMA | SPI_TX_DMA);
278 SPIIntClear(base_, SPI_INT_DMARX);
279 SPIIntEnable(base_, SPI_INT_DMARX);
280
281 /* Enable channels & start DMA transfers */
282 uDMAChannelEnable(dmaChannelIndexTx_);
283 uDMAChannelEnable(dmaChannelIndexRx_);
284 }
285
286 sem_->wait();
287}
288
291__attribute__((optimize("-O3")))
292void CC32xxSPI::interrupt_handler()
293{
294 // Note: There can be more than one CC32xxSPI instance sharing a single
295 // bus. However, the fact that in this case the base_ address and
296 // sem_ reference will also be shared, we can exploit the fact that
297 // the "this" pointer may not directly correspond to the instance
298 // that is waiting using sem_->wait().
299
300 if (SPIIntStatus(base_, true) & SPI_INT_DMATX)
301 {
302 SPIIntDisable(base_, SPI_INT_DMATX);
303 SPIIntClear(base_, SPI_INT_DMATX);
304 }
305 if (uDMAChannelIsEnabled(dmaChannelIndexRx_))
306 {
307 /* DMA has not completed if the channel is still enabled */
308 return;
309 }
310
311 SPIDmaDisable(base_, SPI_RX_DMA | SPI_TX_DMA);
312 SPIIntDisable(base_, SPI_INT_DMARX);
313
314 int woken = 0;
315 sem_->post_from_isr(&woken);
316 os_isr_exit_yield_test(woken);
317}
318
319extern "C" {
322__attribute__((optimize("-O3")))
327} // extern C
OSSem sem[1]
One semaphore required per instance pointer.
Definition CC32xxSPI.cxx:55
static CC32xxSPI * instances_[1]
Instance pointers help us get context from the interrupt handler(s)
Definition CC32xxSPI.cxx:52
void spi0_interrupt_handler(void)
SPI0 interrupt handler.
See OSMutexLock in os/OS.hxx.
Definition Atomic.hxx:153
Specialization of Serial SPI driver for CC32xx devices.
Definition CC32xxSPI.hxx:54
static constexpr uint32_t dmaRxConfig_[]
This lookup table is used to configure the DMA channels for the appropriate (8bit,...
static constexpr uint32_t dmaTxConfig_[]
This lookup table is used to configure the DMA channels for the appropriate (8bit,...
static constexpr uint32_t dmaNullConfig_[]
This lookup table is used to configure the DMA channels for the appropriate (8bit,...
unsigned long clock_
clock rate supplied to the module
CC32xxSPI()
Default constructor.
uint32_t spiChctrl_
instance local copy of configuration
void interrupt_handler()
handle an interrupt.
OSSem * sem_
reference to the semaphore belonging to this bus
uint32_t spiXferlevel_
instance local copy of configuration
uint32_t spiChconf_
instance local copy of configuration
unsigned long interrupt_
interrupt of this device
int update_configuration() override
Update the configuration of the bus.
unsigned long base_
base address of this device
mode_t mode_
File open mode, such as O_NONBLOCK.
Definition Devtab.hxx:590
This class provides a mutex API.
Definition OS.hxx:427
This class provides a counting semaphore API.
Definition OS.hxx:243
Private data for an SPI device.
Definition SPI.hxx:53
uint32_t speedHz
Max default speed in Hz.
Definition SPI.hxx:190
uint8_t bitsPerWord
number of bits per word transaction
Definition SPI.hxx:193
#define HASSERT(x)
Checks that the value of expression x is true, else terminates the current process.
Definition macros.h:138