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 /*---------------------------------------------------------------------------*/
166 typedef struct {
167  int8_t port;
168  int8_t pin;
169 } spi_pad_t;
170 typedef 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 /*---------------------------------------------------------------------------*/
181 static 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
205 void
206 spi_init(void)
207 {
208  spix_init(SPI_DEFAULT_INSTANCE);
209 }
210 #endif /* #ifdef SPI_DEFAULT_INSTANCE */
211 /*---------------------------------------------------------------------------*/
212 void
213 spix_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 /*---------------------------------------------------------------------------*/
276 void
277 spix_enable(uint8_t spi)
278 {
279  if(spi >= SSI_INSTANCE_COUNT) {
280  return;
281  }
282  REG(SYS_CTRL_RCGCSSI) |= (1 << spi);
283 }
284 /*---------------------------------------------------------------------------*/
285 void
286 spix_disable(uint8_t spi)
287 {
288  if(spi >= SSI_INSTANCE_COUNT) {
289  return;
290  }
291  REG(SYS_CTRL_RCGCSSI) &= ~(1 << spi);
292 }
293 /*---------------------------------------------------------------------------*/
294 void
295 spix_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 /*---------------------------------------------------------------------------*/
322 void
323 spix_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 /*---------------------------------------------------------------------------*/
353 void
354 spix_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 /** @} */
#define SSI_CR0_SPH
Serial clock phase (H)
Definition: ssi.h:154
void spix_init(uint8_t spi)
Initialize the SPI bus for the instance given.
Definition: spi-legacy.c:213
#define IOC_SSIRXD_SSI0
SSI0 RX.
Definition: ioc.h:129
Header file for the cc2538 System Control driver.
#define SSI_CC
Clock configuration.
Definition: ssi.h:77
#define GPIO_SET_PIN(PORT_BASE, PIN_MASK)
Set pins with PIN_MASK of port with PORT_BASE high.
Definition: gpio.h:106
#define SSI_CR0_SCR_M
Serial clock rate mask.
Definition: ssi.h:83
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
Header file for the cc2538 Synchronous Serial Interface.
Header file with register and macro declarations for the cc2538 GPIO module.
Header file with declarations for the I/O Control module.
Header file with register manipulation macro definitions.
#define SSI1_BASE
Base address for SSI1.
Definition: ssi.h:59
#define SSI_CR1
Control register 1.
Definition: ssi.h:68
#define IOC_OVERRIDE_DIS
Override Disabled.
Definition: ioc.h:226
#define SSI_CR0_SPO
Serial clock phase (O)
Definition: ssi.h:155
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 SSI0_BASE
Base address for SSI0.
Definition: ssi.h:58
#define GPIO_PIN_MASK(PIN)
Converts a pin number to a pin mask.
Definition: gpio.h:320
#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
void ioc_set_over(uint8_t port, uint8_t pin, uint8_t over)
Set Port:Pin override function.
Definition: ioc.c:54
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
Control register 0.
Definition: ssi.h:67
#define GPIO_SET_OUTPUT(PORT_BASE, PIN_MASK)
Set pins with PIN_MASK of port with PORT_BASE to output.
Definition: gpio.h:85
#define SSI_CR0_SCR_S
Serial clock rate shift.
Definition: ssi.h:84
Basic SPI macros
#define SYS_CTRL_RCGCSSI
SSI[1:0] clocks - active mode.
Definition: sys-ctrl.h:71
#define SSI_CR1_SSE
Synchronous serial port enable.
Definition: ssi.h:161
#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 IOC_SSIRXD_SSI1
SSI1 RX.
Definition: ioc.h:133
void spix_cs_init(uint8_t port, uint8_t pin)
Configure a GPIO to be the chip select pin.
Definition: spi-legacy.c:354
void ioc_set_sel(uint8_t port, uint8_t pin, uint8_t sel)
Function select for Port:Pin.
Definition: ioc.c:66
Default definitions of C compiler quirk work-arounds.
Header file for the cc2538 SPI driver, including macros for the implementation of the low-level SPI p...
#define GPIO_PORT_TO_BASE(PORT)
Converts a port number to the port base address.
Definition: gpio.h:328
void spix_enable(uint8_t spi)
Enables the SPI peripheral for the instance given.
Definition: spi-legacy.c:277