Contiki 3.x
spi.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 "spi-arch.h"
43 #include "dev/ioc.h"
44 #include "dev/sys-ctrl.h"
45 #include "dev/spi.h"
46 #include "dev/ssi.h"
47 #include "dev/gpio.h"
48 /*---------------------------------------------------------------------------*/
49 /* Check port / pin settings for SPI0 and provide default values for spi_cfg */
50 #ifndef SPI0_CLK_PORT
51 #define SPI0_CLK_PORT (-1)
52 #endif
53 #ifndef SPI0_CLK_PIN
54 #define SPI0_CLK_PIN (-1)
55 #endif
56 #if SPI0_CLK_PORT >= 0 && SPI0_CLK_PIN < 0 || \
57  SPI0_CLK_PORT < 0 && SPI0_CLK_PIN >= 0
58 #error Both SPI0_CLK_PORT and SPI0_CLK_PIN must be valid or invalid
59 #endif
60 
61 #ifndef SPI0_TX_PORT
62 #define SPI0_TX_PORT (-1)
63 #endif
64 #ifndef SPI0_TX_PIN
65 #define SPI0_TX_PIN (-1)
66 #endif
67 #if SPI0_TX_PORT >= 0 && SPI0_TX_PIN < 0 || \
68  SPI0_TX_PORT < 0 && SPI0_TX_PIN >= 0
69 #error Both SPI0_TX_PORT and SPI0_TX_PIN must be valid or invalid
70 #endif
71 
72 #ifndef SPI0_RX_PORT
73 #define SPI0_RX_PORT (-1)
74 #endif
75 #ifndef SPI0_RX_PIN
76 #define SPI0_RX_PIN (-1)
77 #endif
78 #if SPI0_RX_PORT >= 0 && SPI0_RX_PIN < 0 || \
79  SPI0_RX_PORT < 0 && SPI0_RX_PIN >= 0
80 #error Both SPI0_RX_PORT and SPI0_RX_PIN must be valid or invalid
81 #endif
82 
83 /* Here we check that either all or none of the ports are defined. As
84  we did already check that both ports + pins are either defined or
85  not for every pin, this means that we can check for an incomplete
86  configuration by only looking at the port defines */
87 /* If some SPI0 pads are valid */
88 #if SPI0_CLK_PORT >= 0 || SPI0_TX_PORT >= 0 || SPI0_RX_PORT >= 0
89 /* but not all */
90 #if SPI0_CLK_PORT < 0 || SPI0_TX_PORT < 0 || SPI0_RX_PORT < 0
91 #error Some SPI0 pad definitions are invalid
92 #endif
93 #define SPI0_PADS_VALID
94 #endif
95 /*---------------------------------------------------------------------------*/
96 /* Check port / pin settings for SPI1 and provide default values for spi_cfg */
97 #ifndef SPI1_CLK_PORT
98 #define SPI1_CLK_PORT (-1)
99 #endif
100 #ifndef SPI1_CLK_PIN
101 #define SPI1_CLK_PIN (-1)
102 #endif
103 #if SPI1_CLK_PORT >= 0 && SPI1_CLK_PIN < 0 || \
104  SPI1_CLK_PORT < 0 && SPI1_CLK_PIN >= 0
105 #error Both SPI1_CLK_PORT and SPI1_CLK_PIN must be valid or invalid
106 #endif
107 
108 #ifndef SPI1_TX_PORT
109 #define SPI1_TX_PORT (-1)
110 #endif
111 #ifndef SPI1_TX_PIN
112 #define SPI1_TX_PIN (-1)
113 #endif
114 #if SPI1_TX_PORT >= 0 && SPI1_TX_PIN < 0 || \
115  SPI1_TX_PORT < 0 && SPI1_TX_PIN >= 0
116 #error Both SPI1_TX_PORT and SPI1_TX_PIN must be valid or invalid
117 #endif
118 
119 #ifndef SPI1_RX_PORT
120 #define SPI1_RX_PORT (-1)
121 #endif
122 #ifndef SPI1_RX_PIN
123 #define SPI1_RX_PIN (-1)
124 #endif
125 #if SPI1_RX_PORT >= 0 && SPI1_RX_PIN < 0 || \
126  SPI1_RX_PORT < 0 && SPI1_RX_PIN >= 0
127 #error Both SPI1_RX_PORT and SPI1_RX_PIN must be valid or invalid
128 #endif
129 
130 /* If some SPI1 pads are valid */
131 #if SPI1_CLK_PORT >= 0 || SPI1_TX_PORT >= 0 || SPI1_RX_PORT >= 0
132 /* but not all */
133 #if SPI1_CLK_PORT < 0 || SPI1_TX_PORT < 0 || SPI1_RX_PORT < 0
134 #error Some SPI1 pad definitions are invalid
135 #endif
136 #define SPI1_PADS_VALID
137 #endif
138 
139 #ifdef SPI_DEFAULT_INSTANCE
140 #if SPI_DEFAULT_INSTANCE == 0
141 #ifndef SPI0_PADS_VALID
142 #error SPI_DEFAULT_INSTANCE is set to SPI0, but its pads are not valid
143 #endif
144 #elif SPI_DEFAULT_INSTANCE == 1
145 #ifndef SPI1_PADS_VALID
146 #error SPI_DEFAULT_INSTANCE is set to SPI1, but its pads are not valid
147 #endif
148 #endif
149 #endif
150 
151 #if (SPI0_CPRS_CPSDVSR & 1) == 1 || SPI0_CPRS_CPSDVSR < 2 || SPI0_CPRS_CPSDVSR > 254
152 #error SPI0_CPRS_CPSDVSR must be an even number between 2 and 254
153 #endif
154 
155 #if (SPI1_CPRS_CPSDVSR & 1) == 1 || SPI1_CPRS_CPSDVSR < 2 || SPI1_CPRS_CPSDVSR > 254
156 #error SPI1_CPRS_CPSDVSR must be an even number between 2 and 254
157 #endif
158 
159 /*---------------------------------------------------------------------------*/
160 typedef struct {
161  int8_t port;
162  int8_t pin;
163 } spi_pad_t;
164 typedef struct {
165  uint32_t base;
166  uint32_t ioc_ssirxd_ssi;
167  uint32_t ioc_pxx_sel_ssi_clkout;
168  uint32_t ioc_pxx_sel_ssi_txd;
169  uint8_t ssi_cprs_cpsdvsr;
170  spi_pad_t clk;
171  spi_pad_t tx;
172  spi_pad_t rx;
173 } spi_regs_t;
174 /*---------------------------------------------------------------------------*/
175 static const spi_regs_t spi_regs[SSI_INSTANCE_COUNT] = {
176  {
177  .base = SSI0_BASE,
178  .ioc_ssirxd_ssi = IOC_SSIRXD_SSI0,
179  .ioc_pxx_sel_ssi_clkout = IOC_PXX_SEL_SSI0_CLKOUT,
180  .ioc_pxx_sel_ssi_txd = IOC_PXX_SEL_SSI0_TXD,
181  .ssi_cprs_cpsdvsr = SPI0_CPRS_CPSDVSR,
182  .clk = { SPI0_CLK_PORT, SPI0_CLK_PIN },
183  .tx = { SPI0_TX_PORT, SPI0_TX_PIN },
184  .rx = { SPI0_RX_PORT, SPI0_RX_PIN }
185  }, {
186  .base = SSI1_BASE,
187  .ioc_ssirxd_ssi = IOC_SSIRXD_SSI1,
188  .ioc_pxx_sel_ssi_clkout = IOC_PXX_SEL_SSI1_CLKOUT,
189  .ioc_pxx_sel_ssi_txd = IOC_PXX_SEL_SSI1_TXD,
190  .ssi_cprs_cpsdvsr = SPI1_CPRS_CPSDVSR,
191  .clk = { SPI1_CLK_PORT, SPI1_CLK_PIN },
192  .tx = { SPI1_TX_PORT, SPI1_TX_PIN },
193  .rx = { SPI1_RX_PORT, SPI1_RX_PIN }
194  }
195 };
196 /*---------------------------------------------------------------------------*/
197 /* Deprecated function call provided for compatibility reasons */
198 #ifdef SPI_DEFAULT_INSTANCE
199 void
200 spi_init(void)
201 {
202  spix_init(SPI_DEFAULT_INSTANCE);
203 }
204 #endif /* #ifdef SPI_DEFAULT_INSTANCE */
205 /*---------------------------------------------------------------------------*/
206 void
207 spix_init(uint8_t spi)
208 {
209  const spi_regs_t *regs;
210 
211  if(spi >= SSI_INSTANCE_COUNT) {
212  return;
213  }
214 
215  regs = &spi_regs[spi];
216 
217  if(regs->clk.port < 0) {
218  /* Port / pin configuration invalid. We checked for completeness
219  above. If clk.port is < 0, this means that all other defines are
220  < 0 as well */
221  return;
222  }
223 
224  spix_enable(spi);
225 
226  /* Start by disabling the peripheral before configuring it */
227  REG(regs->base + SSI_CR1) = 0;
228 
229  /* Set the system clock as the SSI clock */
230  REG(regs->base + SSI_CC) = 0;
231 
232  /* Set the mux correctly to connect the SSI pins to the correct GPIO pins */
233  ioc_set_sel(regs->clk.port,
234  regs->clk.pin,
235  regs->ioc_pxx_sel_ssi_clkout);
236  ioc_set_sel(regs->tx.port,
237  regs->tx.pin,
238  regs->ioc_pxx_sel_ssi_txd);
239  REG(regs->ioc_ssirxd_ssi) = (regs->rx.port * 8) + regs->rx.pin;
240 
241  /* Put all the SSI gpios into peripheral mode */
243  GPIO_PIN_MASK(regs->clk.pin));
245  GPIO_PIN_MASK(regs->tx.pin));
247  GPIO_PIN_MASK(regs->rx.pin));
248 
249  /* Disable any pull ups or the like */
250  ioc_set_over(regs->clk.port, regs->clk.pin, IOC_OVERRIDE_OE);
251  ioc_set_over(regs->tx.port, regs->tx.pin, IOC_OVERRIDE_OE);
252  // Set a pull up so RX doesn't float
253  ioc_set_over(regs->rx.port, regs->rx.pin, IOC_OVERRIDE_DIS);
254  ioc_set_over(regs->rx.port, regs->rx.pin, IOC_OVERRIDE_PUE);
255 
256  /* Configure the clock */
257  REG(regs->base + SSI_CPSR) = regs->ssi_cprs_cpsdvsr;
258 
259  /*
260  * Configure the default SPI options.
261  * mode: Motorola frame format
262  * clock: High when idle
263  * data: Valid on rising edges of the clock
264  * bits: 8 byte data
265  */
266  REG(regs->base + SSI_CR0) = SSI_CR0_SPH | SSI_CR0_SPO | (0x07);
267 
268  /* Enable the SSI */
269  REG(regs->base + SSI_CR1) |= SSI_CR1_SSE;
270 }
271 /*---------------------------------------------------------------------------*/
272 void
273 spix_enable(uint8_t spi)
274 {
275  if(spi >= SSI_INSTANCE_COUNT) {
276  return;
277  }
278  REG(SYS_CTRL_RCGCSSI) |= (1 << spi);
279 }
280 /*---------------------------------------------------------------------------*/
281 void
282 spix_disable(uint8_t spi)
283 {
284  if(spi >= SSI_INSTANCE_COUNT) {
285  return;
286  }
287  REG(SYS_CTRL_RCGCSSI) &= ~(1 << spi);
288 }
289 /*---------------------------------------------------------------------------*/
290 void
291 spix_set_mode(uint8_t spi,
292  uint32_t frame_format,
293  uint32_t clock_polarity,
294  uint32_t clock_phase,
295  uint32_t data_size)
296 {
297  const spi_regs_t *regs;
298 
299  if(spi >= SSI_INSTANCE_COUNT) {
300  return;
301  }
302 
303  regs = &spi_regs[spi];
304 
305  /* Disable the SSI peripheral to configure it */
306  REG(regs->base + SSI_CR1) = 0;
307 
308  /* Configure the SSI options */
309  REG(regs->base + SSI_CR0) = clock_phase |
310  clock_polarity |
311  frame_format |
312  (data_size - 1);
313 
314  /* Re-enable the SSI */
315  REG(regs->base + SSI_CR1) |= SSI_CR1_SSE;
316 }
317 /*---------------------------------------------------------------------------*/
318 void
319 spix_cs_init(uint8_t port, uint8_t pin)
320 {
322  GPIO_PIN_MASK(pin));
323  ioc_set_over(port, pin, IOC_OVERRIDE_DIS);
326 }
327 /** @} */
#define SSI_CC
Clock configuration.
Definition: ssi.h:75
#define GPIO_PIN_MASK(PIN)
Converts a pin number to a pin mask.
Definition: gpio.h:321
#define SSI_CR0
Control register 0.
Definition: ssi.h:65
Header file with register manipulation macro definitions.
#define SSI_CPSR
Clock divider.
Definition: ssi.h:69
#define IOC_OVERRIDE_PUE
Pull Up Enable.
Definition: ioc.h:223
#define IOC_OVERRIDE_DIS
Override Disabled.
Definition: ioc.h:226
Header file for the cc2538 SPI driver, including macros for the implementation of the low-level SPI p...
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.c:291
#define SYS_CTRL_RCGCSSI
SSI[1:0] clocks - active mode.
Definition: sys-ctrl.h:71
#define IOC_OVERRIDE_OE
Output Enable.
Definition: ioc.h:222
void spix_init(uint8_t spi)
Initialize the SPI bus for the instance given.
Definition: spi.c:207
void ioc_set_sel(uint8_t port, uint8_t pin, uint8_t sel)
Function select for Port:Pin.
Definition: ioc.c:60
#define SSI0_BASE
Base address for SSI0.
Definition: ssi.h:58
#define GPIO_SET_PIN(PORT_BASE, PIN_MASK)
Set pins with PIN_MASK of port with PORT_BASE high.
Definition: gpio.h:107
#define SSI_CR0_SPH
Serial clock phase (H)
Definition: ssi.h:152
void spix_cs_init(uint8_t port, uint8_t pin)
Configure a GPIO to be the chip select pin.
Definition: spi.c:319
#define SSI1_BASE
Base address for SSI1.
Definition: ssi.h:59
#define GPIO_SET_OUTPUT(PORT_BASE, PIN_MASK)
Set pins with PIN_MASK of port with PORT_BASE to output.
Definition: gpio.h:100
#define IOC_SSIRXD_SSI0
SSI0 RX.
Definition: ioc.h:129
#define SSI_CR0_SPO
Serial clock phase (O)
Definition: ssi.h:153
Header file with declarations for the I/O Control module.
#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:259
#define GPIO_PORT_TO_BASE(PORT)
Converts a port number to the port base address.
Definition: gpio.h:329
Header file for the cc2538 System Control driver.
#define SSI_CR1_SSE
Synchronous serial port enable.
Definition: ssi.h:159
Header file for the cc2538 Synchronous Serial Interface.
#define SSI_CR1
Control register 1.
Definition: ssi.h:66
#define IOC_SSIRXD_SSI1
SSI1 RX.
Definition: ioc.h:133
void spix_disable(uint8_t spi)
Disables the SPI peripheral for the instance given.
Definition: spi.c:282
#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:251
void ioc_set_over(uint8_t port, uint8_t pin, uint8_t over)
Set Port:Pin override function.
Definition: ioc.c:54
void spix_enable(uint8_t spi)
Enables the SPI peripheral for the instance given.
Definition: spi.c:273