Contiki-NG
spi-arch.c
1/*
2 * Copyright (c) 2016-2017, Yanzi Networks.
3 * Copyright (c) 2018, University of Bristol.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of the copyright holder nor the names of its
15 * contributors may be used to endorse or promote products derived
16 * from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
29 * OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include <stdint.h>
33#include "contiki.h"
34#include "reg.h"
35#include "dev/spi.h"
36#include "gpio-hal-arch.h"
37#include "sys/cc.h"
38#include "ioc.h"
39#include "sys-ctrl.h"
40#include "ssi.h"
41#include "gpio.h"
42#include "sys/log.h"
43#include "sys/mutex.h"
44/*---------------------------------------------------------------------------*/
45/* Log configuration */
46#define LOG_MODULE "spi-hal-arch"
47#define LOG_LEVEL LOG_LEVEL_NONE
48/*---------------------------------------------------------------------------*/
49/* Default values for the clock rate divider */
50#ifdef SPI_ARCH_CONF_SPI0_CPRS_CPSDVSR
51#define SPI_ARCH_SPI0_CPRS_CPSDVSR SPI_ARCH_CONF_SPI0_CPRS_CPSDVSR
52#else
53#define SPI_ARCH_SPI0_CPRS_CPSDVSR 2
54#endif
55
56#ifdef SPI_ARCH_CONF_SPI1_CPRS_CPSDVSR
57#define SPI_ARCH_SPI1_CPRS_CPSDVSR SPI_ARCH_CONF_SPI1_CPRS_CPSDVSR
58#else
59#define SPI_ARCH_SPI1_CPRS_CPSDVSR 2
60#endif
61
62#if (SPI_ARCH_SPI0_CPRS_CPSDVSR & 1) == 1 || \
63 SPI_ARCH_SPI0_CPRS_CPSDVSR < 2 || \
64 SPI_ARCH_SPI0_CPRS_CPSDVSR > 254
65#error SPI_ARCH_SPI0_CPRS_CPSDVSR must be an even number between 2 and 254
66#endif
67
68#if (SPI_ARCH_SPI1_CPRS_CPSDVSR & 1) == 1 || \
69 SPI_ARCH_SPI1_CPRS_CPSDVSR < 2 || \
70 SPI_ARCH_SPI1_CPRS_CPSDVSR > 254
71#error SPI_ARCH_SPI1_CPRS_CPSDVSR must be an even number between 2 and 254
72#endif
73/*---------------------------------------------------------------------------*/
74/* CS set and clear macros */
75#define SPIX_CS_CLR(port, pin) GPIO_CLR_PIN(GPIO_PORT_TO_BASE(port), GPIO_PIN_MASK(pin))
76#define SPIX_CS_SET(port, pin) GPIO_SET_PIN(GPIO_PORT_TO_BASE(port), GPIO_PIN_MASK(pin))
77/*---------------------------------------------------------------------------*/
78/*
79 * Clock source from which the baud clock is determined for the SSI, according
80 * to SSI_CC.CS.
81 */
82#define SSI_SYS_CLOCK SYS_CTRL_SYS_CLOCK
83/*---------------------------------------------------------------------------*/
84typedef struct {
85 uint32_t base;
86 uint32_t ioc_ssirxd_ssi;
87 uint32_t ioc_pxx_sel_ssi_clkout;
88 uint32_t ioc_pxx_sel_ssi_txd;
89 uint8_t ssi_cprs_cpsdvsr;
90} spi_regs_t;
91/*---------------------------------------------------------------------------*/
92static const spi_regs_t spi_regs[SSI_INSTANCE_COUNT] = {
93 {
94 .base = SSI0_BASE,
95 .ioc_ssirxd_ssi = IOC_SSIRXD_SSI0,
96 .ioc_pxx_sel_ssi_clkout = IOC_PXX_SEL_SSI0_CLKOUT,
97 .ioc_pxx_sel_ssi_txd = IOC_PXX_SEL_SSI0_TXD,
98 .ssi_cprs_cpsdvsr = SPI_ARCH_SPI0_CPRS_CPSDVSR,
99 }, {
100 .base = SSI1_BASE,
101 .ioc_ssirxd_ssi = IOC_SSIRXD_SSI1,
102 .ioc_pxx_sel_ssi_clkout = IOC_PXX_SEL_SSI1_CLKOUT,
103 .ioc_pxx_sel_ssi_txd = IOC_PXX_SEL_SSI1_TXD,
104 .ssi_cprs_cpsdvsr = SPI_ARCH_SPI1_CPRS_CPSDVSR,
105 }
106};
107
108typedef struct spi_locks_s {
109 mutex_t lock;
110 const spi_device_t *owner;
111} spi_locks_t;
112
113/* One lock per SPI controller */
114spi_locks_t board_spi_locks_spi[SPI_CONTROLLER_COUNT] = { { MUTEX_STATUS_UNLOCKED, NULL } };
115
116/*---------------------------------------------------------------------------*/
117static void
118spix_wait_tx_ready(const spi_device_t *dev)
119{
120 /* Infinite loop until SR_TNF - Transmit FIFO Not Full */
121 while(!(REG(spi_regs[dev->spi_controller].base + SSI_SR) & SSI_SR_TNF));
122}
123/*---------------------------------------------------------------------------*/
124static int
125spix_read_buf(const spi_device_t *dev)
126{
127 return REG(spi_regs[dev->spi_controller].base + SSI_DR);
128}
129/*---------------------------------------------------------------------------*/
130static void
131spix_write_buf(const spi_device_t *dev, int data)
132{
133 REG(spi_regs[dev->spi_controller].base + SSI_DR) = data;
134}
135/*---------------------------------------------------------------------------*/
136static void
137spix_wait_eotx(const spi_device_t *dev)
138{
139 /* wait until not busy */
140 while(REG(spi_regs[dev->spi_controller].base + SSI_SR) & SSI_SR_BSY);
141}
142/*---------------------------------------------------------------------------*/
143static void
144spix_wait_eorx(const spi_device_t *dev)
145{
146 /* wait as long as receive is empty */
147 while(!(REG(spi_regs[dev->spi_controller].base + SSI_SR) & SSI_SR_RNE));
148}
149/*---------------------------------------------------------------------------*/
150bool
152{
153 if(board_spi_locks_spi[dev->spi_controller].owner == dev) {
154 return true;
155 }
156
157 return false;
158}
159/*---------------------------------------------------------------------------*/
160bool
162{
163 if(board_spi_locks_spi[dev->spi_controller].lock == MUTEX_STATUS_LOCKED) {
164 return true;
165 }
166
167 return false;
168}
169/*---------------------------------------------------------------------------*/
172{
173 const spi_regs_t *regs;
174 uint32_t scr;
175 uint64_t div;
176
177 uint32_t cs_port = PIN_TO_PORT(dev->pin_spi_cs);
178 uint32_t cs_pin = PIN_TO_NUM(dev->pin_spi_cs);
179
180 uint32_t clk_port = PIN_TO_PORT(dev->pin_spi_sck);
181 uint32_t clk_pin = PIN_TO_NUM(dev->pin_spi_sck);
182
183 uint32_t miso_port = PIN_TO_PORT(dev->pin_spi_miso);
184 uint32_t miso_pin = PIN_TO_NUM(dev->pin_spi_miso);
185
186 uint32_t mosi_port = PIN_TO_PORT(dev->pin_spi_mosi);
187 uint32_t mosi_pin = PIN_TO_NUM(dev->pin_spi_mosi);
188
189 uint32_t mode = 0;
190
191 /* lock the SPI bus */
192 if(mutex_try_lock(&board_spi_locks_spi[dev->spi_controller].lock) == false) {
193 return SPI_DEV_STATUS_BUS_LOCKED;
194 }
195
196 board_spi_locks_spi[dev->spi_controller].owner = dev;
197
198 /* Set SPI phase */
199 if(dev->spi_pha != 0) {
200 mode = mode | SSI_CR0_SPH;
201 }
202
203 /* Set SPI polarity */
204 if(dev->spi_pol != 0) {
205 mode = mode | SSI_CR0_SPO;
206 }
207
208 /* CS pin configuration */
210 GPIO_PIN_MASK(cs_pin));
211 ioc_set_over(cs_port, cs_pin, IOC_OVERRIDE_DIS);
214
215 regs = &spi_regs[dev->spi_controller];
216
217 /* SSI Enable */
218 REG(SYS_CTRL_RCGCSSI) |= (1 << dev->spi_controller);
219
220 /* Start by disabling the peripheral before configuring it */
221 REG(regs->base + SSI_CR1) = 0;
222
223 /* Set the system clock as the SSI clock */
224 REG(regs->base + SSI_CC) = 0;
225
226 /* Set the mux correctly to connect the SSI pins to the correct GPIO pins */
227 ioc_set_sel(clk_port, clk_pin, regs->ioc_pxx_sel_ssi_clkout);
228 ioc_set_sel(mosi_port, mosi_pin, regs->ioc_pxx_sel_ssi_txd);
229 REG(regs->ioc_ssirxd_ssi) = dev->pin_spi_miso;
230
231 /* Put all the SSI gpios into peripheral mode */
233 GPIO_PIN_MASK(clk_pin));
235 GPIO_PIN_MASK(mosi_pin));
237 GPIO_PIN_MASK(miso_pin));
238
239 /* Disable any pull ups or the like */
240 ioc_set_over(clk_port, clk_pin, IOC_OVERRIDE_DIS);
241 ioc_set_over(mosi_port, mosi_pin, IOC_OVERRIDE_DIS);
242 ioc_set_over(miso_port, miso_pin, IOC_OVERRIDE_DIS);
243
244 /* Configure the clock */
245 REG(regs->base + SSI_CPSR) = regs->ssi_cprs_cpsdvsr;
246
247 /* Configure the mode */
248 REG(regs->base + SSI_CR0) = mode | (0x07);
249
250 /* Configure the SSI serial clock rate */
251 if(!dev->spi_bit_rate) {
252 scr = 255;
253 } else {
254 div = (uint64_t)regs->ssi_cprs_cpsdvsr * dev->spi_bit_rate;
255 scr = (SSI_SYS_CLOCK + div - 1) / div;
256 scr = MIN(MAX(scr, 1), 256) - 1;
257 }
258 REG(regs->base + SSI_CR0) = (REG(regs->base + SSI_CR0) & ~SSI_CR0_SCR_M) |
259 scr << SSI_CR0_SCR_S;
260
261 /* Enable the SSI */
262 REG(regs->base + SSI_CR1) |= SSI_CR1_SSE;
263
264 return SPI_DEV_STATUS_OK;
265}
266/*---------------------------------------------------------------------------*/
269{
270 if(!spi_arch_has_lock(dev)) {
271 return SPI_DEV_STATUS_BUS_NOT_OWNED;
272 }
273
274 /* Disable SSI */
275 REG(SYS_CTRL_RCGCSSI) &= ~(1 << dev->spi_controller);
276
277 /* Unlock the SPI bus */
278 board_spi_locks_spi[dev->spi_controller].owner = NULL;
279 mutex_unlock(&board_spi_locks_spi[dev->spi_controller].lock);
280
281 return SPI_DEV_STATUS_OK;
282}
283/*---------------------------------------------------------------------------*/
284/* Assumes that checking dev and bus is not NULL before calling this */
287 const uint8_t *write_buf, int wlen,
288 uint8_t *inbuf, int rlen, int ignore_len)
289{
290 int i;
291 int totlen;
292 uint8_t c;
293
294 if(!spi_arch_has_lock(dev)) {
295 return SPI_DEV_STATUS_BUS_NOT_OWNED;
296 }
297
298 LOG_DBG("SPI: transfer (r:%d,w:%d) ", rlen, wlen);
299
300 if(write_buf == NULL && wlen > 0) {
301 return SPI_DEV_STATUS_EINVAL;
302 }
303 if(inbuf == NULL && rlen > 0) {
304 return SPI_DEV_STATUS_EINVAL;
305 }
306
307 totlen = MAX(rlen + ignore_len, wlen);
308
309 if(totlen == 0) {
310 /* Nothing to do */
311 return SPI_DEV_STATUS_OK;
312 }
313
314 LOG_DBG_("%c%c%c: %u ", rlen > 0 ? 'R' : '-', wlen > 0 ? 'W' : '-',
315 ignore_len > 0 ? 'S' : '-', totlen);
316
317 for(i = 0; i < totlen; i++) {
318 spix_wait_tx_ready(dev);
319 c = i < wlen ? write_buf[i] : 0;
320 spix_write_buf(dev, c);
321 LOG_DBG_("%c%02x->", i < rlen ? ' ' : '#', c);
322 spix_wait_eotx(dev);
323 spix_wait_eorx(dev);
324 c = spix_read_buf(dev);
325 if(i < rlen) {
326 inbuf[i] = c;
327 }
328 LOG_DBG_("%02x", c);
329 }
330 LOG_DBG_("\n");
331
332 return SPI_DEV_STATUS_OK;
333}
334/*---------------------------------------------------------------------------*/
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 SSI_DR
Access the TX and RX FIFO.
Definition: ssi.h:69
#define SSI1_BASE
Base address for SSI1.
Definition: ssi.h:59
#define SSI_SR_BSY
Busy bit.
Definition: ssi.h:163
#define SSI_CR1
Control register 1.
Definition: ssi.h:68
#define SSI_SR_TNF
Transmit FIFO not full.
Definition: ssi.h:166
#define SSI_SR
Meta information about FIFO.
Definition: ssi.h:70
#define SSI_CR0
Control register 0.
Definition: ssi.h:67
#define SSI_CC
Clock configuration.
Definition: ssi.h:77
#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
#define SSI_SR_RNE
Receive FIFO not empty.
Definition: ssi.h:165
#define SSI_CPSR
Clock divider.
Definition: ssi.h:71
#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
#define mutex_try_lock(m)
Try to lock a mutex.
Definition: mutex.h:91
#define mutex_unlock(m)
Unlock a previously acquired mutex.
Definition: mutex.h:103
spi_status_t spi_arch_close_and_unlock(const spi_device_t *dev)
Closes and unlocks an SPI controller.
Definition: spi-arch.c:268
spi_status_t spi_arch_transfer(const spi_device_t *dev, const uint8_t *write_buf, int wlen, uint8_t *inbuf, int rlen, int ignore_len)
Performs an SPI transfer.
Definition: spi-arch.c:286
spi_status_t
SPI return codes.
Definition: spi.h:80
spi_status_t spi_arch_lock_and_open(const spi_device_t *dev)
Locks and opens an SPI controller to the configuration specified.
Definition: spi-arch.c:171
bool spi_arch_has_lock(const spi_device_t *dev)
Checks if a device has locked an SPI controller.
Definition: spi-arch.c:151
bool spi_arch_is_bus_locked(const spi_device_t *dev)
Checks if an SPI controller is locked by any device.
Definition: spi-arch.c:161
Header file with declarations for the I/O Control module.
Header file for the logging system.
Header file with register manipulation macro definitions.
Header file for the SPI HAL.
Header file for the cc2538 Synchronous Serial Interface.
SPI Device Configuration.
Definition: spi.h:100
Header file for the cc2538 System Control driver.