mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2025-12-25 14:33:52 +01:00
Rework SPI periph driver to use proper RIOT GPIO API functions.
Also cleanup header files by using vendor defines and remove
obsolete code. Further, adapt board config accordingly.
167 lines
4.6 KiB
C
167 lines
4.6 KiB
C
/*
|
|
* Copyright (C) 2015 Loci Controls Inc.
|
|
* 2016 Freie Universität Berlin
|
|
*
|
|
* This file is subject to the terms and conditions of the GNU Lesser
|
|
* General Public License v2.1. See the file LICENSE in the top level
|
|
* directory for more details.
|
|
*/
|
|
|
|
/**
|
|
* @ingroup cpu_cc2538
|
|
* @ingroup drivers_periph_spi
|
|
* @{
|
|
*
|
|
* @file
|
|
* @brief Low-level SPI driver implementation
|
|
*
|
|
* @author Ian Martin <ian@locicontrols.com>
|
|
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
|
|
*
|
|
* @}
|
|
*/
|
|
|
|
#include "vendor/hw_memmap.h"
|
|
#include "vendor/hw_ssi.h"
|
|
|
|
#include "cpu.h"
|
|
#include "mutex.h"
|
|
#include "assert.h"
|
|
#include "periph/spi.h"
|
|
|
|
/**
|
|
* @brief Array holding one pre-initialized mutex for each SPI device
|
|
*/
|
|
static mutex_t locks[SPI_NUMOF];
|
|
|
|
static inline cc2538_ssi_t *dev(spi_t bus)
|
|
{
|
|
/* .num is either 0 or 1, return respective base address */
|
|
return (spi_config[bus].num) ? (cc2538_ssi_t *)SSI1_BASE : (cc2538_ssi_t *)SSI0_BASE;
|
|
}
|
|
|
|
static inline void poweron(spi_t bus)
|
|
{
|
|
SYS_CTRL_RCGCSSI |= (1 << spi_config[bus].num);
|
|
SYS_CTRL_SCGCSSI |= (1 << spi_config[bus].num);
|
|
SYS_CTRL_DCGCSSI |= (1 << spi_config[bus].num);
|
|
}
|
|
|
|
static inline void poweroff(spi_t bus)
|
|
{
|
|
SYS_CTRL_RCGCSSI &= ~(1 << spi_config[bus].num);
|
|
SYS_CTRL_SCGCSSI &= ~(1 << spi_config[bus].num);
|
|
SYS_CTRL_DCGCSSI &= ~(1 << spi_config[bus].num);
|
|
}
|
|
|
|
void spi_init(spi_t bus)
|
|
{
|
|
assert(bus < SPI_NUMOF);
|
|
|
|
/* init mutex for given bus */
|
|
mutex_init(&locks[bus]);
|
|
/* temporarily power on the device */
|
|
poweron(bus);
|
|
/* configure device to be a master and disable SSI operation mode */
|
|
dev(bus)->CR1 = 0;
|
|
/* configure system clock as SSI clock source */
|
|
dev(bus)->CC = SSI_CC_CS_IODIV;
|
|
/* and power off the bus again */
|
|
poweroff(bus);
|
|
|
|
/* trigger SPI pin configuration */
|
|
spi_init_pins(bus);
|
|
}
|
|
|
|
void spi_init_pins(spi_t bus)
|
|
{
|
|
/* select values according to SPI device */
|
|
cc2538_ioc_sel_t txd = spi_config[bus].num ? SSI1_TXD : SSI0_TXD;
|
|
cc2538_ioc_sel_t clk = spi_config[bus].num ? SSI1_CLK_OUT : SSI0_CLK_OUT;
|
|
cc2538_ioc_sel_t fss = spi_config[bus].num ? SSI1_FSS_OUT : SSI0_FSS_OUT;
|
|
cc2538_ioc_pin_t rxd = spi_config[bus].num ? SSI1_RXD : SSI0_RXD;
|
|
/* init pin functions and multiplexing */
|
|
gpio_init_mux(spi_config[bus].mosi_pin, OVERRIDE_ENABLE, txd, GPIO_MUX_NONE);
|
|
gpio_init_mux(spi_config[bus].sck_pin, OVERRIDE_ENABLE, clk, GPIO_MUX_NONE);
|
|
gpio_init_mux(spi_config[bus].cs_pin, OVERRIDE_ENABLE, fss, GPIO_MUX_NONE);
|
|
gpio_init_mux(spi_config[bus].miso_pin, OVERRIDE_DISABLE, GPIO_MUX_NONE, rxd);
|
|
}
|
|
|
|
int spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk)
|
|
{
|
|
(void) cs;
|
|
/* lock the bus */
|
|
mutex_lock(&locks[bus]);
|
|
/* power on device */
|
|
poweron(bus);
|
|
/* configure SCR clock field, data-width and mode */
|
|
dev(bus)->CR0 = 0;
|
|
dev(bus)->CPSR = (spi_clk_config[clk].cpsr);
|
|
dev(bus)->CR0 = ((spi_clk_config[clk].scr << 8) | mode | SSI_CR0_DSS(8));
|
|
/* enable SSI device */
|
|
dev(bus)->CR1 = SSI_CR1_SSE;
|
|
|
|
return SPI_OK;
|
|
}
|
|
|
|
void spi_release(spi_t bus)
|
|
{
|
|
/* disable and power off device */
|
|
dev(bus)->CR1 = 0;
|
|
poweroff(bus);
|
|
/* and release lock... */
|
|
mutex_unlock(&locks[bus]);
|
|
}
|
|
|
|
void spi_transfer_bytes(spi_t bus, spi_cs_t cs, bool cont,
|
|
const void *out, void *in, size_t len)
|
|
{
|
|
const uint8_t *out_buf = out;
|
|
uint8_t *in_buf = in;
|
|
|
|
assert(out_buf || in_buf);
|
|
|
|
if (cs != SPI_CS_UNDEF) {
|
|
gpio_clear((gpio_t)cs);
|
|
}
|
|
|
|
if (!in_buf) {
|
|
for (size_t i = 0; i < len; i++) {
|
|
while (!(dev(bus)->SR & SSI_SR_TNF)) {}
|
|
dev(bus)->DR = out_buf[i];
|
|
}
|
|
/* flush RX FIFO while busy*/
|
|
while ((dev(bus)->SR & SSI_SR_BSY)) {
|
|
dev(bus)->DR;
|
|
}
|
|
}
|
|
else if (!out_buf) { /*TODO this case is currently untested */
|
|
size_t in_cnt = 0;
|
|
for (size_t i = 0; i < len; i++) {
|
|
while (!(dev(bus)->SR & SSI_SR_TNF)) {}
|
|
dev(bus)->DR = 0;
|
|
if (dev(bus)->SR & SSI_SR_RNE) {
|
|
in_buf[in_cnt++] = dev(bus)->DR;
|
|
}
|
|
}
|
|
/* get remaining bytes */
|
|
while (dev(bus)->SR & SSI_SR_RNE) {
|
|
in_buf[in_cnt++] = dev(bus)->DR;
|
|
}
|
|
}
|
|
else {
|
|
for (size_t i = 0; i < len; i++) {
|
|
while (!(dev(bus)->SR & SSI_SR_TNF)) {}
|
|
dev(bus)->DR = out_buf[i];
|
|
while (!(dev(bus)->SR & SSI_SR_RNE)){}
|
|
in_buf[i] = dev(bus)->DR;
|
|
}
|
|
/* wait until no more busy */
|
|
while ((dev(bus)->SR & SSI_SR_BSY)) {}
|
|
}
|
|
|
|
if ((!cont) && (cs != SPI_CS_UNDEF)) {
|
|
gpio_set((gpio_t)cs);
|
|
}
|
|
}
|