Contiki-NG
spi-legacy.c
Go to the documentation of this file.
1/*
2 * Copyright (c) 2013, University of Michigan.
3 *
4 * Copyright (c) 2015, Weptech elektronik GmbH
5 * Author: Ulf Knoblich, ulf.knoblich@weptech.de
6 *
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33/**
34 * \addtogroup cc2538-spi
35 * @{
36 *
37 * \file
38 * Implementation of the cc2538 SPI peripheral driver
39 */
40#include "contiki.h"
41#include "reg.h"
42#include "dev/spi-arch-legacy.h"
43#include "sys/cc.h"
44#include "dev/ioc.h"
45#include "dev/sys-ctrl.h"
46#include "dev/spi-legacy.h"
47#include "dev/ssi.h"
48#include "dev/gpio.h"
49/*---------------------------------------------------------------------------*/
50/* Check port / pin settings for SPI0 and provide default values for spi_cfg */
51#ifndef SPI0_CLK_PORT
52#define SPI0_CLK_PORT (-1)
53#endif
54#ifndef SPI0_CLK_PIN
55#define SPI0_CLK_PIN (-1)
56#endif
57#if SPI0_CLK_PORT >= 0 && SPI0_CLK_PIN < 0 || \
58 SPI0_CLK_PORT < 0 && SPI0_CLK_PIN >= 0
59#error Both SPI0_CLK_PORT and SPI0_CLK_PIN must be valid or invalid
60#endif
61
62#ifndef SPI0_TX_PORT
63#define SPI0_TX_PORT (-1)
64#endif
65#ifndef SPI0_TX_PIN
66#define SPI0_TX_PIN (-1)
67#endif
68#if SPI0_TX_PORT >= 0 && SPI0_TX_PIN < 0 || \
69 SPI0_TX_PORT < 0 && SPI0_TX_PIN >= 0
70#error Both SPI0_TX_PORT and SPI0_TX_PIN must be valid or invalid
71#endif
72
73#ifndef SPI0_RX_PORT
74#define SPI0_RX_PORT (-1)
75#endif
76#ifndef SPI0_RX_PIN
77#define SPI0_RX_PIN (-1)
78#endif
79#if SPI0_RX_PORT >= 0 && SPI0_RX_PIN < 0 || \
80 SPI0_RX_PORT < 0 && SPI0_RX_PIN >= 0
81#error Both SPI0_RX_PORT and SPI0_RX_PIN must be valid or invalid
82#endif
83
84/* Here we check that either all or none of the ports are defined. As
85 we did already check that both ports + pins are either defined or
86 not for every pin, this means that we can check for an incomplete
87 configuration by only looking at the port defines */
88/* If some SPI0 pads are valid */
89#if SPI0_CLK_PORT >= 0 || SPI0_TX_PORT >= 0 || SPI0_RX_PORT >= 0
90/* but not all */
91#if SPI0_CLK_PORT < 0 || SPI0_TX_PORT < 0 || SPI0_RX_PORT < 0
92#error Some SPI0 pad definitions are invalid
93#endif
94#define SPI0_PADS_VALID
95#endif
96/*---------------------------------------------------------------------------*/
97/* Check port / pin settings for SPI1 and provide default values for spi_cfg */
98#ifndef SPI1_CLK_PORT
99#define SPI1_CLK_PORT (-1)
100#endif
101#ifndef SPI1_CLK_PIN
102#define SPI1_CLK_PIN (-1)
103#endif
104#if SPI1_CLK_PORT >= 0 && SPI1_CLK_PIN < 0 || \
105 SPI1_CLK_PORT < 0 && SPI1_CLK_PIN >= 0
106#error Both SPI1_CLK_PORT and SPI1_CLK_PIN must be valid or invalid
107#endif
108
109#ifndef SPI1_TX_PORT
110#define SPI1_TX_PORT (-1)
111#endif
112#ifndef SPI1_TX_PIN
113#define SPI1_TX_PIN (-1)
114#endif
115#if SPI1_TX_PORT >= 0 && SPI1_TX_PIN < 0 || \
116 SPI1_TX_PORT < 0 && SPI1_TX_PIN >= 0
117#error Both SPI1_TX_PORT and SPI1_TX_PIN must be valid or invalid
118#endif
119
120#ifndef SPI1_RX_PORT
121#define SPI1_RX_PORT (-1)
122#endif
123#ifndef SPI1_RX_PIN
124#define SPI1_RX_PIN (-1)
125#endif
126#if SPI1_RX_PORT >= 0 && SPI1_RX_PIN < 0 || \
127 SPI1_RX_PORT < 0 && SPI1_RX_PIN >= 0
128#error Both SPI1_RX_PORT and SPI1_RX_PIN must be valid or invalid
129#endif
130
131/* If some SPI1 pads are valid */
132#if SPI1_CLK_PORT >= 0 || SPI1_TX_PORT >= 0 || SPI1_RX_PORT >= 0
133/* but not all */
134#if SPI1_CLK_PORT < 0 || SPI1_TX_PORT < 0 || SPI1_RX_PORT < 0
135#error Some SPI1 pad definitions are invalid
136#endif
137#define SPI1_PADS_VALID
138#endif
139
140#ifdef SPI_DEFAULT_INSTANCE
141#if SPI_DEFAULT_INSTANCE == 0
142#ifndef SPI0_PADS_VALID
143#error SPI_DEFAULT_INSTANCE is set to SPI0, but its pads are not valid
144#endif
145#elif SPI_DEFAULT_INSTANCE == 1
146#ifndef SPI1_PADS_VALID
147#error SPI_DEFAULT_INSTANCE is set to SPI1, but its pads are not valid
148#endif
149#endif
150#endif
151
152#if (SPI0_CPRS_CPSDVSR & 1) == 1 || SPI0_CPRS_CPSDVSR < 2 || SPI0_CPRS_CPSDVSR > 254
153#error SPI0_CPRS_CPSDVSR must be an even number between 2 and 254
154#endif
155
156#if (SPI1_CPRS_CPSDVSR & 1) == 1 || SPI1_CPRS_CPSDVSR < 2 || SPI1_CPRS_CPSDVSR > 254
157#error SPI1_CPRS_CPSDVSR must be an even number between 2 and 254
158#endif
159/*---------------------------------------------------------------------------*/
160/*
161 * Clock source from which the baud clock is determined for the SSI, according
162 * to SSI_CC.CS.
163 */
164#define SSI_SYS_CLOCK SYS_CTRL_SYS_CLOCK
165/*---------------------------------------------------------------------------*/
166typedef struct {
167 int8_t port;
168 int8_t pin;
169} spi_pad_t;
170typedef struct {
171 uint32_t base;
172 uint32_t ioc_ssirxd_ssi;
173 uint32_t ioc_pxx_sel_ssi_clkout;
174 uint32_t ioc_pxx_sel_ssi_txd;
175 uint8_t ssi_cprs_cpsdvsr;
176 spi_pad_t clk;
177 spi_pad_t tx;
178 spi_pad_t rx;
179} spi_regs_t;
180/*---------------------------------------------------------------------------*/
181static const spi_regs_t spi_regs[SSI_INSTANCE_COUNT] = {
182 {
183 .base = SSI0_BASE,
184 .ioc_ssirxd_ssi = IOC_SSIRXD_SSI0,
185 .ioc_pxx_sel_ssi_clkout = IOC_PXX_SEL_SSI0_CLKOUT,
186 .ioc_pxx_sel_ssi_txd = IOC_PXX_SEL_SSI0_TXD,
187 .ssi_cprs_cpsdvsr = SPI0_CPRS_CPSDVSR,
188 .clk = { SPI0_CLK_PORT, SPI0_CLK_PIN },
189 .tx = { SPI0_TX_PORT, SPI0_TX_PIN },
190 .rx = { SPI0_RX_PORT, SPI0_RX_PIN }
191 }, {
192 .base = SSI1_BASE,
193 .ioc_ssirxd_ssi = IOC_SSIRXD_SSI1,
194 .ioc_pxx_sel_ssi_clkout = IOC_PXX_SEL_SSI1_CLKOUT,
195 .ioc_pxx_sel_ssi_txd = IOC_PXX_SEL_SSI1_TXD,
196 .ssi_cprs_cpsdvsr = SPI1_CPRS_CPSDVSR,
197 .clk = { SPI1_CLK_PORT, SPI1_CLK_PIN },
198 .tx = { SPI1_TX_PORT, SPI1_TX_PIN },
199 .rx = { SPI1_RX_PORT, SPI1_RX_PIN }
200 }
201};
202/*---------------------------------------------------------------------------*/
203/* Deprecated function call provided for compatibility reasons */
204#ifdef SPI_DEFAULT_INSTANCE
205void
206spi_init(void)
207{
208 spix_init(SPI_DEFAULT_INSTANCE);
209}
210#endif /* #ifdef SPI_DEFAULT_INSTANCE */
211/*---------------------------------------------------------------------------*/
212void
213spix_init(uint8_t spi)
214{
215 const spi_regs_t *regs;
216
217 if(spi >= SSI_INSTANCE_COUNT) {
218 return;
219 }
220
221 regs = &spi_regs[spi];
222
223 if(regs->clk.port < 0) {
224 /* Port / pin configuration invalid. We checked for completeness
225 above. If clk.port is < 0, this means that all other defines are
226 < 0 as well */
227 return;
228 }
229
230 spix_enable(spi);
231
232 /* Start by disabling the peripheral before configuring it */
233 REG(regs->base + SSI_CR1) = 0;
234
235 /* Set the system clock as the SSI clock */
236 REG(regs->base + SSI_CC) = 0;
237
238 /* Set the mux correctly to connect the SSI pins to the correct GPIO pins */
239 ioc_set_sel(regs->clk.port,
240 regs->clk.pin,
241 regs->ioc_pxx_sel_ssi_clkout);
242 ioc_set_sel(regs->tx.port,
243 regs->tx.pin,
244 regs->ioc_pxx_sel_ssi_txd);
245 REG(regs->ioc_ssirxd_ssi) = (regs->rx.port * 8) + regs->rx.pin;
246
247 /* Put all the SSI gpios into peripheral mode */
249 GPIO_PIN_MASK(regs->clk.pin));
251 GPIO_PIN_MASK(regs->tx.pin));
253 GPIO_PIN_MASK(regs->rx.pin));
254
255 /* Disable any pull ups or the like */
256 ioc_set_over(regs->clk.port, regs->clk.pin, IOC_OVERRIDE_DIS);
257 ioc_set_over(regs->tx.port, regs->tx.pin, IOC_OVERRIDE_DIS);
258 ioc_set_over(regs->rx.port, regs->rx.pin, IOC_OVERRIDE_DIS);
259
260 /* Configure the clock */
261 REG(regs->base + SSI_CPSR) = regs->ssi_cprs_cpsdvsr;
262
263 /*
264 * Configure the default SPI options.
265 * mode: Motorola frame format
266 * clock: High when idle
267 * data: Valid on rising edges of the clock
268 * bits: 8 byte data
269 */
270 REG(regs->base + SSI_CR0) = SSI_CR0_SPH | SSI_CR0_SPO | (0x07);
271
272 /* Enable the SSI */
273 REG(regs->base + SSI_CR1) |= SSI_CR1_SSE;
274}
275/*---------------------------------------------------------------------------*/
276void
277spix_enable(uint8_t spi)
278{
279 if(spi >= SSI_INSTANCE_COUNT) {
280 return;
281 }
282 REG(SYS_CTRL_RCGCSSI) |= (1 << spi);
283}
284/*---------------------------------------------------------------------------*/
285void
286spix_disable(uint8_t spi)
287{
288 if(spi >= SSI_INSTANCE_COUNT) {
289 return;
290 }
291 REG(SYS_CTRL_RCGCSSI) &= ~(1 << spi);
292}
293/*---------------------------------------------------------------------------*/
294void
295spix_set_mode(uint8_t spi,
296 uint32_t frame_format,
297 uint32_t clock_polarity,
298 uint32_t clock_phase,
299 uint32_t data_size)
300{
301 const spi_regs_t *regs;
302
303 if(spi >= SSI_INSTANCE_COUNT) {
304 return;
305 }
306
307 regs = &spi_regs[spi];
308
309 /* Disable the SSI peripheral to configure it */
310 REG(regs->base + SSI_CR1) = 0;
311
312 /* Configure the SSI options */
313 REG(regs->base + SSI_CR0) = clock_phase |
314 clock_polarity |
315 frame_format |
316 (data_size - 1);
317
318 /* Re-enable the SSI */
319 REG(regs->base + SSI_CR1) |= SSI_CR1_SSE;
320}
321/*---------------------------------------------------------------------------*/
322void
323spix_set_clock_freq(uint8_t spi, uint32_t freq)
324{
325 const spi_regs_t *regs;
326 uint64_t div;
327 uint32_t scr;
328
329 if(spi >= SSI_INSTANCE_COUNT) {
330 return;
331 }
332
333 regs = &spi_regs[spi];
334
335 /* Disable the SSI peripheral to configure it */
336 REG(regs->base + SSI_CR1) = 0;
337
338 /* Configure the SSI serial clock rate */
339 if(!freq) {
340 scr = 255;
341 } else {
342 div = (uint64_t)regs->ssi_cprs_cpsdvsr * freq;
343 scr = (SSI_SYS_CLOCK + div - 1) / div;
344 scr = MIN(MAX(scr, 1), 256) - 1;
345 }
346 REG(regs->base + SSI_CR0) = (REG(regs->base + SSI_CR0) & ~SSI_CR0_SCR_M) |
347 scr << SSI_CR0_SCR_S;
348
349 /* Re-enable the SSI */
350 REG(regs->base + SSI_CR1) |= SSI_CR1_SSE;
351}
352/*---------------------------------------------------------------------------*/
353void
354spix_cs_init(uint8_t port, uint8_t pin)
355{
357 GPIO_PIN_MASK(pin));
358 ioc_set_over(port, pin, IOC_OVERRIDE_DIS);
361}
362/** @} */
Default definitions of C compiler quirk work-arounds.
Header file with register and macro declarations for the cc2538 GPIO module.
#define GPIO_PIN_MASK(PIN)
Converts a pin number to a pin mask.
Definition: gpio.h:320
#define GPIO_PERIPHERAL_CONTROL(PORT_BASE, PIN_MASK)
Configure the pin to be under peripheral control with PIN_MASK of port with PORT_BASE.
Definition: gpio.h:250
#define GPIO_SOFTWARE_CONTROL(PORT_BASE, PIN_MASK)
Configure the pin to be software controlled with PIN_MASK of port with PORT_BASE.
Definition: gpio.h:258
#define GPIO_PORT_TO_BASE(PORT)
Converts a port number to the port base address.
Definition: gpio.h:328
#define GPIO_SET_PIN(PORT_BASE, PIN_MASK)
Set pins with PIN_MASK of port with PORT_BASE high.
Definition: gpio.h:106
#define GPIO_SET_OUTPUT(PORT_BASE, PIN_MASK)
Set pins with PIN_MASK of port with PORT_BASE to output.
Definition: gpio.h:85
void ioc_set_sel(uint8_t port, uint8_t pin, uint8_t sel)
Function select for Port:Pin.
Definition: ioc.c:66
#define IOC_SSIRXD_SSI0
SSI0 RX.
Definition: ioc.h:129
void ioc_set_over(uint8_t port, uint8_t pin, uint8_t over)
Set Port:Pin override function.
Definition: ioc.c:54
#define IOC_OVERRIDE_DIS
Override Disabled.
Definition: ioc.h:226
#define IOC_SSIRXD_SSI1
SSI1 RX.
Definition: ioc.h:133
#define SSI1_BASE
Base address for SSI1.
Definition: ssi.h:59
void spix_set_mode(uint8_t spi, uint32_t frame_format, uint32_t clock_polarity, uint32_t clock_phase, uint32_t data_size)
Configure the SPI data and clock polarity and the data size for the instance given.
Definition: spi-legacy.c:295
#define SSI_CR1
Control register 1.
Definition: ssi.h:68
#define SSI_CR0
Control register 0.
Definition: ssi.h:67
#define SSI_CC
Clock configuration.
Definition: ssi.h:77
void spix_enable(uint8_t spi)
Enables the SPI peripheral for the instance given.
Definition: spi-legacy.c:277
void spix_init(uint8_t spi)
Initialize the SPI bus for the instance given.
Definition: spi-legacy.c:213
void spix_cs_init(uint8_t port, uint8_t pin)
Configure a GPIO to be the chip select pin.
Definition: spi-legacy.c:354
#define SSI_CR0_SCR_S
Serial clock rate shift.
Definition: ssi.h:84
#define SSI_CR0_SPH
Serial clock phase (H)
Definition: ssi.h:154
#define SSI0_BASE
Base address for SSI0.
Definition: ssi.h:58
void spix_disable(uint8_t spi)
Disables the SPI peripheral for the instance given.
Definition: spi-legacy.c:286
#define SSI_CPSR
Clock divider.
Definition: ssi.h:71
void spix_set_clock_freq(uint8_t spi, uint32_t freq)
Sets the SPI clock frequency of the given SSI instance.
Definition: spi-legacy.c:323
#define SSI_CR0_SPO
Serial clock phase (O)
Definition: ssi.h:155
#define SSI_CR1_SSE
Synchronous serial port enable.
Definition: ssi.h:161
#define SYS_CTRL_RCGCSSI
SSI[1:0] clocks - active mode.
Definition: sys-ctrl.h:71
Header file with declarations for the I/O Control module.
Header file with register manipulation macro definitions.
Header file for the cc2538 SPI driver, including macros for the implementation of the low-level SPI p...
Basic SPI macros.
Header file for the cc2538 Synchronous Serial Interface.
Header file for the cc2538 System Control driver.