From ca5f1befb30dafbdf9f7e75d7de30e42ae86cd9f Mon Sep 17 00:00:00 2001 From: Hauke Petersen Date: Tue, 8 Nov 2016 18:17:17 +0100 Subject: [PATCH] cpu/cc2538+boards: adapted to SPI API changes - adapted the SPI driver - adapted all boards using the CPU --- boards/cc2538dk/include/periph_conf.h | 26 +- boards/openmote-cc2538/include/periph_conf.h | 23 +- boards/remote-pa/include/periph_conf.h | 39 +- boards/remote-reva/include/periph_conf.h | 40 +- boards/remote-revb/include/board.h | 2 +- boards/remote-revb/include/periph_conf.h | 28 +- cpu/cc2538/include/cc2538_ssi.h | 40 ++ cpu/cc2538/include/periph_cpu.h | 47 ++- cpu/cc2538/periph/spi.c | 380 ++++++------------- 9 files changed, 308 insertions(+), 317 deletions(-) diff --git a/boards/cc2538dk/include/periph_conf.h b/boards/cc2538dk/include/periph_conf.h index 55222f9df..d80a60b3b 100644 --- a/boards/cc2538dk/include/periph_conf.h +++ b/boards/cc2538dk/include/periph_conf.h @@ -58,7 +58,6 @@ static const timer_conf_t timer_config[] = { #define TIMER_IRQ_PRIO 1 /** @} */ - /** * @name UART configuration * @{ @@ -112,22 +111,35 @@ static const i2c_conf_t i2c_config[I2C_NUMOF] = { }; /** @} */ +/** + * @brief Pre-calculated clock divider values based on a CLOCK_CORECLOCK (32MHz) + * + * Calculated with (CPSR * (SCR + 1)) = (CLOCK_CORECLOCK / bus_freq), where + * CPSR and SCR cannot be larger than 255. + */ +static const spi_clk_conf_t spi_clk_config[] = { + { .cpsr = 10, .scr = 31 }, /* 100khz */ + { .cpsr = 1, .scr = 79 }, /* 400khz */ + { .cpsr = 1, .scr = 31 }, /* 1MHz */ + { .cpsr = 1, .scr = 6 }, /* ~4.5MHz */ + { .cpsr = 1, .scr = 2 } /* ~10.7MHz */ +}; + /** * @name SPI configuration * @{ */ -#define SPI_NUMOF 1 -#define SPI_0_EN 1 - -static const periph_spi_conf_t spi_config[SPI_NUMOF] = { +static const spi_conf_t spi_config[] = { { .dev = SSI0, .mosi_pin = GPIO_PA4, .miso_pin = GPIO_PA5, .sck_pin = GPIO_PA2, - .cs_pin = GPIO_PD0, - }, + .cs_pin = GPIO_PD0 + } }; + +#define SPI_NUMOF (sizeof(spi_config) / sizeof(spi_config[0])) /** @} */ /** diff --git a/boards/openmote-cc2538/include/periph_conf.h b/boards/openmote-cc2538/include/periph_conf.h index 845487b02..cd4b49f21 100644 --- a/boards/openmote-cc2538/include/periph_conf.h +++ b/boards/openmote-cc2538/include/periph_conf.h @@ -30,7 +30,7 @@ * @name Clock system configuration * @{ */ -#define CLOCK_CORECLOCK (32000000U) /* desired core clock frequency, 32MHz */ +#define CLOCK_CORECLOCK (32000000U) /* desired core clock frequency, 32MHz */ /** @} */ /** @@ -104,14 +104,25 @@ static const i2c_conf_t i2c_config[I2C_NUMOF] = { }; /** @} */ +/** + * @brief Pre-calculated clock divider values based on a CLOCK_CORECLOCK (32MHz) + * + * Calculated with (CPSR * (SCR + 1)) = (CLOCK_CORECLOCK / bus_freq), where + * CPSR and SCR cannot be larger than 255. + */ +static const spi_clk_conf_t spi_clk_config[] = { + { .cpsr = 10, .scr = 31 }, /* 100khz */ + { .cpsr = 1, .scr = 79 }, /* 400khz */ + { .cpsr = 1, .scr = 31 }, /* 1MHz */ + { .cpsr = 1, .scr = 6 }, /* ~4.5MHz */ + { .cpsr = 1, .scr = 2 } /* ~10.7MHz */ +}; + /** * @name SPI configuration * @{ */ -#define SPI_NUMOF 1 -#define SPI_0_EN 1 - -static const periph_spi_conf_t spi_config[SPI_NUMOF] = { +static const spi_conf_t spi_config[] = { { .dev = SSI0, .mosi_pin = GPIO_PA5, @@ -120,6 +131,8 @@ static const periph_spi_conf_t spi_config[SPI_NUMOF] = { .cs_pin = GPIO_PA3, }, }; + +#define SPI_NUMOF (sizeof(spi_config) / sizeof(spi_config[0])) /** @} */ /** diff --git a/boards/remote-pa/include/periph_conf.h b/boards/remote-pa/include/periph_conf.h index ee753928d..60c7b0e79 100644 --- a/boards/remote-pa/include/periph_conf.h +++ b/boards/remote-pa/include/periph_conf.h @@ -1,10 +1,10 @@ /* - * Copyright (C) 2014 Freie Universität Berlin - * Copyright (C) 2015 Zolertia SL + * Copyright (C) 2014-2016 Freie Universität Berlin + * 2015 Zolertia SL * - * 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. + * 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. */ /** @@ -15,7 +15,7 @@ * @brief Peripheral MCU configuration for the Re-Mote board prototype A * * @author Hauke Petersen - * Antonio Lignan + * @author Antonio Lignan */ #ifndef PERIPH_CONF_H @@ -70,29 +70,42 @@ static const i2c_conf_t i2c_config[I2C_NUMOF] = { }; /** @} */ +/** + * @brief Pre-calculated clock divider values based on a CLOCK_CORECLOCK (32MHz) + * + * Calculated with (CPSR * (SCR + 1)) = (CLOCK_CORECLOCK / bus_freq), where + * CPSR and SCR cannot be larger than 255. + */ +static const spi_clk_conf_t spi_clk_config[] = { + { .cpsr = 10, .scr = 31 }, /* 100khz */ + { .cpsr = 1, .scr = 79 }, /* 400khz */ + { .cpsr = 1, .scr = 31 }, /* 1MHz */ + { .cpsr = 1, .scr = 6 }, /* ~4.5MHz */ + { .cpsr = 1, .scr = 2 } /* ~10.7MHz */ +}; + /** * @name SPI configuration * @{ */ -#define SPI_NUMOF 2 -#define SPI_0_EN 1 -#define SPI_1_EN 1 - -static const periph_spi_conf_t spi_config[SPI_NUMOF] = { +static const spi_conf_t spi_config[] = { { .dev = SSI0, .mosi_pin = GPIO_PD0, .miso_pin = GPIO_PC4, .sck_pin = GPIO_PD1, - .cs_pin = GPIO_PD3, + .cs_pin = GPIO_PD3 }, { .dev = SSI1, .mosi_pin = GPIO_PC7, .miso_pin = GPIO_PA4, .sck_pin = GPIO_PB5, - }, + .cs_pin = GPIO_UNDEF + } }; + +#define SPI_NUMOF (sizeof(spi_config) / sizeof(spi_config[0])) /** @} */ #ifdef __cplusplus diff --git a/boards/remote-reva/include/periph_conf.h b/boards/remote-reva/include/periph_conf.h index 2dfd7b1b2..9381d3bf0 100644 --- a/boards/remote-reva/include/periph_conf.h +++ b/boards/remote-reva/include/periph_conf.h @@ -1,10 +1,10 @@ /* - * Copyright (C) 2014 Freie Universität Berlin - * Copyright (C) 2015 Zolertia SL + * Copyright (C) 2014-2016 Freie Universität Berlin + * 2015 Zolertia SL * - * 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. + * 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. */ /** @@ -15,7 +15,7 @@ * @brief Peripheral MCU configuration for the RE-Mote board revision A * * @author Hauke Petersen - * Antonio Lignan + * @author Antonio Lignan */ #ifndef PERIPH_CONF_H @@ -70,30 +70,42 @@ static const i2c_conf_t i2c_config[I2C_NUMOF] = { }; /** @} */ +/** + * @brief Pre-calculated clock divider values based on a CLOCK_CORECLOCK (32MHz) + * + * Calculated with (CPSR * (SCR + 1)) = (CLOCK_CORECLOCK / bus_freq), where + * CPSR and SCR cannot be larger than 255. + */ +static const spi_clk_conf_t spi_clk_config[] = { + { .cpsr = 10, .scr = 31 }, /* 100khz */ + { .cpsr = 1, .scr = 79 }, /* 400khz */ + { .cpsr = 1, .scr = 31 }, /* 1MHz */ + { .cpsr = 1, .scr = 6 }, /* ~4.5MHz */ + { .cpsr = 1, .scr = 2 } /* ~10.7MHz */ +}; + /** * @name SPI configuration * @{ */ -#define SPI_NUMOF 2 -#define SPI_0_EN 1 -#define SPI_1_EN 1 - -static const periph_spi_conf_t spi_config[SPI_NUMOF] = { +static const spi_conf_t spi_config[] = { { .dev = SSI0, .mosi_pin = GPIO_PB1, .miso_pin = GPIO_PB3, .sck_pin = GPIO_PB2, - .cs_pin = GPIO_PB5, + .cs_pin = GPIO_PB5 }, { .dev = SSI1, .mosi_pin = GPIO_PC5, .miso_pin = GPIO_PC6, .sck_pin = GPIO_PC4, - .cs_pin = GPIO_PA7, - }, + .cs_pin = GPIO_PA7 + } }; + +#define SPI_NUMOF (sizeof(spi_config) / sizeof(spi_config[0])) /** @} */ #ifdef __cplusplus diff --git a/boards/remote-revb/include/board.h b/boards/remote-revb/include/board.h index 7e1e212e9..9926c7290 100644 --- a/boards/remote-revb/include/board.h +++ b/boards/remote-revb/include/board.h @@ -114,7 +114,7 @@ * @name Onboard micro-sd slot pin definitions * @{ */ -#define SDCARD_SPI_PARAM_SPI SPI_1 +#define SDCARD_SPI_PARAM_SPI SPI_DEV(1) #define SDCARD_SPI_PARAM_CS GPIO_PIN(0,7) #define SDCARD_SPI_PARAM_CLK GPIO_PIN(2,4) #define SDCARD_SPI_PARAM_MOSI GPIO_PIN(2,5) diff --git a/boards/remote-revb/include/periph_conf.h b/boards/remote-revb/include/periph_conf.h index 4890135cc..04bb81cd9 100644 --- a/boards/remote-revb/include/periph_conf.h +++ b/boards/remote-revb/include/periph_conf.h @@ -70,30 +70,42 @@ static const i2c_conf_t i2c_config[I2C_NUMOF] = { }; /** @} */ +/** + * @brief Pre-calculated clock divider values based on a CLOCK_CORECLOCK (32MHz) + * + * Calculated with (CPSR * (SCR + 1)) = (CLOCK_CORECLOCK / bus_freq), where + * CPSR and SCR cannot be larger than 255. + */ +static const spi_clk_conf_t spi_clk_config[] = { + { .cpsr = 10, .scr = 31 }, /* 100khz */ + { .cpsr = 1, .scr = 79 }, /* 400khz */ + { .cpsr = 1, .scr = 31 }, /* 1MHz */ + { .cpsr = 1, .scr = 6 }, /* ~4.5MHz */ + { .cpsr = 1, .scr = 2 } /* ~10.7MHz */ +}; + /** * @name SPI configuration * @{ */ -#define SPI_NUMOF 2 -#define SPI_0_EN 1 -#define SPI_1_EN 1 - -static const periph_spi_conf_t spi_config[SPI_NUMOF] = { +static const spi_conf_t spi_config[] = { { .dev = SSI0, .mosi_pin = GPIO_PB1, .miso_pin = GPIO_PB3, .sck_pin = GPIO_PB2, - .cs_pin = GPIO_PB5, + .cs_pin = GPIO_PB5 }, { .dev = SSI1, .mosi_pin = GPIO_PC5, .miso_pin = GPIO_PC6, .sck_pin = GPIO_PC4, - .cs_pin = GPIO_PA7, - }, + .cs_pin = GPIO_PA7 + } }; + +#define SPI_NUMOF (sizeof(spi_config) / sizeof(spi_config[0])) /** @} */ #ifdef __cplusplus diff --git a/cpu/cc2538/include/cc2538_ssi.h b/cpu/cc2538/include/cc2538_ssi.h index 2dc08cff0..5fbdc004d 100644 --- a/cpu/cc2538/include/cc2538_ssi.h +++ b/cpu/cc2538/include/cc2538_ssi.h @@ -77,6 +77,46 @@ typedef struct { #define SSI0 ( (cc2538_ssi_t*)0x40008000 ) /**< SSI0 Instance */ #define SSI1 ( (cc2538_ssi_t*)0x40009000 ) /**< SSI1 Instance */ +/** + * @brief Define CR0 register bitfields + * @{ + */ +#define SSI_CR0_DSS(x) ((x - 1) << 0) +#define SSI_CR0_SPO (1 << 6) +#define SSI_CR0_SPH (1 << 7) +/** @} */ + +/** + * @brief Define CR1 register bitfields + * @{ + */ +#define SSI_CR1_LBM (1 << 0) +#define SSI_CR1_SSE (1 << 1) +#define SSI_CR1_MS (1 << 2) +#define SSI_CR1_SOD (1 << 3) +/** @} */ + +/** + * @brief Define SR register bitfields + * @{ + */ +#define SSI_SR_TFE (1 << 0) +#define SSI_SR_TNF (1 << 1) +#define SSI_SR_RNE (1 << 2) +#define SSI_SR_RFF (1 << 3) +#define SSI_SR_BSY (1 << 4) +/** @} */ + +/** + * @brief Define CC register bitfields + * @{ + */ +#define SSI_SS_PIOSC (1 << 0) +#define SSI_SS_DSEN (1 << 2) +#define SSI_SS_SYSDIV (0) +#define SSI_SS_IODIV (SSI_SS_PIOSC) +/** @} */ + #ifdef __cplusplus } /* end extern "C" */ #endif diff --git a/cpu/cc2538/include/periph_cpu.h b/cpu/cc2538/include/periph_cpu.h index 78f1194d2..a38ca522e 100644 --- a/cpu/cc2538/include/periph_cpu.h +++ b/cpu/cc2538/include/periph_cpu.h @@ -50,10 +50,20 @@ typedef uint32_t gpio_t; */ #define GPIO_PIN(port, pin) (gpio_t)(((uint32_t)GPIO_A + (port << 12)) | pin) +/** + * @brief I2C configuration options + */ +typedef struct { + gpio_t scl_pin; /**< pin used for SCL */ + gpio_t sda_pin; /**< pin used for SDA */ +} i2c_conf_t; + /** * @brief declare needed generic SPI functions * @{ */ +#define PERIPH_SPI_NEEDS_INIT_CS +#define PERIPH_SPI_NEEDS_TRANSFER_BYTE #define PERIPH_SPI_NEEDS_TRANSFER_REG #define PERIPH_SPI_NEEDS_TRANSFER_REGS /** @} */ @@ -74,12 +84,39 @@ typedef enum { /** @} */ /** - * @brief I2C configuration options + * @brief Override SPI mode settings + * @{ + */ +#define HAVE_SPI_MODE_T +typedef enum { + SPI_MODE_0 = 0, /**< CPOL=0, CPHA=0 */ + SPI_MODE_1 = (SSI_CR0_SPH), /**< CPOL=0, CPHA=1 */ + SPI_MODE_2 = (SSI_CR0_SPO), /**< CPOL=1, CPHA=0 */ + SPI_MODE_3 = (SSI_CR0_SPO | SSI_CR0_SPH) /**< CPOL=1, CPHA=1 */ +} spi_mode_t; +/** @ */ + +/** + * @brief Override SPI clock settings + * @{ + */ +#define HAVE_SPI_CLK_T +typedef enum { + SPI_CLK_100KHZ = 0, /**< drive the SPI bus with 100KHz */ + SPI_CLK_400KHZ = 1, /**< drive the SPI bus with 400KHz */ + SPI_CLK_1MHZ = 2, /**< drive the SPI bus with 1MHz */ + SPI_CLK_5MHZ = 3, /**< drive the SPI bus with 5MHz */ + SPI_CLK_10MHZ = 4 /**< drive the SPI bus with 10MHz */ +} spi_clk_t; +/** @} */ + +/** + * @brief Datafields for static SPI clock configuration values */ typedef struct { - gpio_t scl_pin; /**< pin used for SCL */ - gpio_t sda_pin; /**< pin used for SDA */ -} i2c_conf_t; + uint8_t cpsr; /**< CPSR clock divider */ + uint8_t scr; /**< SCR clock divider */ +} spi_clk_conf_t; /** * @brief SPI configuration data structure @@ -91,7 +128,7 @@ typedef struct { gpio_t miso_pin; /**< pin used for MISO */ gpio_t sck_pin; /**< pin used for SCK */ gpio_t cs_pin; /**< pin used for CS */ -} periph_spi_conf_t; +} spi_conf_t; /** @} */ /** diff --git a/cpu/cc2538/periph/spi.c b/cpu/cc2538/periph/spi.c index 75dd2cba5..73cd3836f 100644 --- a/cpu/cc2538/periph/spi.c +++ b/cpu/cc2538/periph/spi.c @@ -1,327 +1,179 @@ /* * 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. + * 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. */ /** - * @addtogroup driver_periph + * @addtogroup cpu_cc2538 * @{ * * @file * @brief Low-level SPI driver implementation * * @author Ian Martin + * @author Hauke Petersen * * @} */ -#include -#include - -#include "cc2538_ssi.h" - #include "cpu.h" #include "mutex.h" +#include "assert.h" #include "periph/spi.h" -#include "periph_conf.h" - -/* guard file in case no SPI device is defined */ -#if SPI_NUMOF - -/* clock sources for the SSI_CC register */ -#define CS_SYS_DIV 0 -#define CS_IO_DIV 1 - -#define SSI0_MASK (1 << 0) -#define SSI1_MASK (1 << 1) - -#ifndef SPI_DATA_BITS_NUMOF -#define SPI_DATA_BITS_NUMOF 8 -#endif - -#define spin_until(condition) while (!(condition)) thread_yield() /** - * @brief Array holding one pre-initialized mutex for each SPI device + * @brief Array holding one pre-initialized mutex for each SPI device */ -static mutex_t locks[SPI_NUMOF] = {MUTEX_INIT}; +static mutex_t locks[SPI_NUMOF]; -int spi_init_master(spi_t dev, spi_conf_t conf, spi_speed_t speed) +static inline cc2538_ssi_t *dev(spi_t bus) { - cc2538_ssi_t* ssi = spi_config[dev].dev; - - if ((unsigned int)dev >= SPI_NUMOF) { - return -1; - } - - /* power on the SPI device */ - spi_poweron(dev); - - /* configure SCK, MISO and MOSI pin */ - spi_conf_pins(dev); - - /* Disable the SSI and configure it for SPI master mode */ - ssi->CR1 = 0; - - /* 3. Configure the SSI clock source */ - ssi->CC = CS_SYS_DIV; - - /* 4. Configure the clock prescale divisor by writing the SSI_CPSR register. - * frequency of the SSIClk is defined by: SSIClk = SysClk / (CPSDVSR x (1 + SCR)) - */ - - const int32_t speed_lut[] = { - [SPI_SPEED_100KHZ] = 100000 /* Hz */, - [SPI_SPEED_400KHZ] = 400000 /* Hz */, - [SPI_SPEED_1MHZ ] = 1000000 /* Hz */, - [SPI_SPEED_5MHZ ] = 5000000 /* Hz */, - [SPI_SPEED_10MHZ ] = 10000000 /* Hz */, - }; - - int32_t SysClk = sys_clock_freq(); - int32_t f_desired = speed_lut[speed]; - int32_t f_actual; - int32_t err; - int32_t best_err = INT32_MAX; - int32_t div1; - int32_t div2; - int32_t best_div1 = 2; - int32_t best_div2 = 1; - - /* System clock is first divided by CPSDVSR, then by SCR */ - for (div1 = 2; div1 <= 254; div1++) { - div2 = SysClk; - int32_t denom = div1 * f_desired; - div2 += denom / 2; - div2 /= denom; - - if (div2 < 1) { - div2 = 1; - } - else if (div2 > 256) { - div2 = 256; - } - - f_actual = SysClk / (div1 * div2); - err = f_actual - f_desired; - if (err < 0) { - err = -err; - } - if (err <= best_err) { - best_div1 = div1; - best_div2 = div2; - best_err = err; - } - } - - ssi->CPSR = best_div1; /* CPSDVSR */ - ssi->CR0bits.SCR = best_div2 - 1; /* Serial clock rate (SCR) */ - - switch (conf) { - case SPI_CONF_FIRST_RISING: - ssi->CR0bits.SPO = 0; - ssi->CR0bits.SPH = 0; - break; - - case SPI_CONF_SECOND_RISING: - ssi->CR0bits.SPO = 0; - ssi->CR0bits.SPH = 1; - break; - - case SPI_CONF_FIRST_FALLING: - ssi->CR0bits.SPO = 1; - ssi->CR0bits.SPH = 0; - break; - - case SPI_CONF_SECOND_FALLING: - ssi->CR0bits.SPO = 1; - ssi->CR0bits.SPH = 1; - break; - } - - ssi->CR0bits.FRF = 0; /* SPI protocol mode */ - ssi->CR0bits.DSS = SPI_DATA_BITS_NUMOF - 1; /* The data size */ - - ssi->CR1bits.SSE = 1; + return spi_config[bus].dev; +} - return 0; +static inline void poweron(spi_t bus) +{ + SYS_CTRL_RCGCSSI |= (1 << bus); + SYS_CTRL_SCGCSSI |= (1 << bus); + SYS_CTRL_DCGCSSI |= (1 << bus); } -int spi_init_slave(spi_t dev, spi_conf_t conf, char(*cb)(char data)) +static inline void poweroff(spi_t bus) { - /* slave mode is not (yet) supported */ - return -1; + SYS_CTRL_RCGCSSI &= ~(1 << bus); + SYS_CTRL_SCGCSSI &= ~(1 << bus); + SYS_CTRL_DCGCSSI &= ~(1 << bus); } -int spi_conf_pins(spi_t dev) +void spi_init(spi_t bus) { - if ((unsigned int)dev >= SPI_NUMOF) { - return -1; - } + assert(bus <= SPI_NUMOF); + + /* 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_SS_IODIV; + /* and power off the bus again */ + poweroff(bus); + + /* trigger SPI pin configuration */ + spi_init_pins(bus); +} - switch ((uintptr_t)spi_config[dev].dev) { +void spi_init_pins(spi_t bus) +{ + switch ((uintptr_t)spi_config[bus].dev) { case (uintptr_t)SSI0: - IOC_PXX_SEL[spi_config[dev].mosi_pin] = SSI0_TXD; - IOC_PXX_SEL[spi_config[dev].sck_pin ] = SSI0_CLKOUT; - IOC_PXX_SEL[spi_config[dev].cs_pin ] = SSI0_FSSOUT; + IOC_PXX_SEL[spi_config[bus].mosi_pin] = SSI0_TXD; + IOC_PXX_SEL[spi_config[bus].sck_pin ] = SSI0_CLKOUT; + IOC_PXX_SEL[spi_config[bus].cs_pin ] = SSI0_FSSOUT; - IOC_SSIRXD_SSI0 = spi_config[dev].miso_pin; + IOC_SSIRXD_SSI0 = spi_config[bus].miso_pin; break; case (uintptr_t)SSI1: - IOC_PXX_SEL[spi_config[dev].mosi_pin] = SSI1_TXD; - IOC_PXX_SEL[spi_config[dev].sck_pin ] = SSI1_CLKOUT; - IOC_PXX_SEL[spi_config[dev].cs_pin ] = SSI1_FSSOUT; + IOC_PXX_SEL[spi_config[bus].mosi_pin] = SSI1_TXD; + IOC_PXX_SEL[spi_config[bus].sck_pin ] = SSI1_CLKOUT; + IOC_PXX_SEL[spi_config[bus].cs_pin ] = SSI1_FSSOUT; - IOC_SSIRXD_SSI1 = spi_config[dev].miso_pin; + IOC_SSIRXD_SSI1 = spi_config[bus].miso_pin; break; } - IOC_PXX_OVER[spi_config[dev].mosi_pin] = IOC_OVERRIDE_OE; - IOC_PXX_OVER[spi_config[dev].sck_pin ] = IOC_OVERRIDE_OE; - IOC_PXX_OVER[spi_config[dev].cs_pin ] = IOC_OVERRIDE_OE; - IOC_PXX_OVER[spi_config[dev].miso_pin] = IOC_OVERRIDE_DIS; + IOC_PXX_OVER[spi_config[bus].mosi_pin] = IOC_OVERRIDE_OE; + IOC_PXX_OVER[spi_config[bus].miso_pin] = IOC_OVERRIDE_DIS; + IOC_PXX_OVER[spi_config[bus].sck_pin ] = IOC_OVERRIDE_OE; + IOC_PXX_OVER[spi_config[bus].cs_pin ] = IOC_OVERRIDE_OE; - gpio_hardware_control(spi_config[dev].mosi_pin); - gpio_hardware_control(spi_config[dev].miso_pin); - gpio_hardware_control(spi_config[dev].sck_pin); - gpio_hardware_control(spi_config[dev].cs_pin); - - return 0; + gpio_hardware_control(spi_config[bus].mosi_pin); + gpio_hardware_control(spi_config[bus].miso_pin); + gpio_hardware_control(spi_config[bus].sck_pin); + gpio_hardware_control(spi_config[bus].cs_pin); } -int spi_acquire(spi_t dev) +int spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk) { - if ((unsigned int)dev >= SPI_NUMOF) { - return -1; - } - mutex_lock(&locks[dev]); - return 0; + /* 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; } -int spi_release(spi_t dev) +void spi_release(spi_t bus) { - if ((unsigned int)dev >= SPI_NUMOF) { - return -1; - } - mutex_unlock(&locks[dev]); - return 0; + /* disable and power off device */ + dev(bus)->CR1 = 0; + poweroff(bus); + /* and release lock... */ + mutex_unlock(&locks[bus]); } -static char ssi_flush_input(cc2538_ssi_t *ssi) +void spi_transfer_bytes(spi_t bus, spi_cs_t cs, bool cont, + const void *out, void *in, size_t len) { - char tmp = 0; - - while (ssi->SRbits.RNE) { - tmp = ssi->DR; - } + uint8_t *out_buf = (uint8_t *)out; + uint8_t *in_buf = (uint8_t *)in; - return tmp; -} - -int spi_transfer_byte(spi_t dev, char out, char *in) -{ - cc2538_ssi_t* ssi = spi_config[dev].dev; - char tmp; + assert(out_buf || in_buf); - ssi_flush_input(ssi); - - /* transmit byte */ - spin_until(ssi->SRbits.TNF); - ssi->DR = out; - - /* receive byte */ - spin_until(ssi->SRbits.RNE); - tmp = ssi->DR; - - if (in) { - *in = tmp; + if (cs != SPI_CS_UNDEF) { + gpio_clear((gpio_t)cs); } - return 1; -} - -int spi_transfer_bytes(spi_t dev, char *out, char *in, unsigned int length) -{ - cc2538_ssi_t* ssi = spi_config[dev].dev; - unsigned int tx_n = 0, rx_n = 0; - - if ((unsigned int)dev >= SPI_NUMOF) { - return -1; - } - - ssi_flush_input(ssi); - - /* transmit and receive bytes */ - while (tx_n < length) { - spin_until(ssi->SRbits.TNF || ssi->SRbits.RNE); - - if (ssi->SRbits.TNF) { - ssi->DR = out[tx_n]; - tx_n++; + if (!in_buf) { + for (size_t i = 0; i < len; i++) { + while (!(dev(bus)->SR & SSI_SR_TNF)) {} + dev(bus)->DR = out_buf[i]; } - else if (ssi->SRbits.RNE) { - assert(rx_n < length); - in[rx_n] = ssi->DR; - rx_n++; + /* flush RX FIFO */ + while (dev(bus)->SR & SSI_SR_RNE) { + dev(bus)->DR; } } - - /* receive remaining bytes */ - while (rx_n < length) { - spin_until(ssi->SRbits.RNE); - assert(rx_n < length); - in[rx_n] = ssi->DR; - rx_n++; + if (!out_buf) { + 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; + } } - - return rx_n; -} - -void spi_transmission_begin(spi_t dev, char reset_val) -{ - /* slave mode is not (yet) supported */ -} - -void spi_poweron(spi_t dev) -{ - switch ((uintptr_t)spi_config[dev].dev) { - case (uintptr_t)SSI0: - /* enable SSI0 in all three power modes */ - SYS_CTRL_RCGCSSI |= SSI0_MASK; - SYS_CTRL_SCGCSSI |= SSI0_MASK; - SYS_CTRL_DCGCSSI |= SSI0_MASK; - break; - - case (uintptr_t)SSI1: - /* enable SSI1 in all three power modes */ - SYS_CTRL_RCGCSSI |= SSI1_MASK; - SYS_CTRL_SCGCSSI |= SSI1_MASK; - SYS_CTRL_DCGCSSI |= SSI1_MASK; - break; + else { + size_t in_cnt = 0; + for (size_t i = 0; i < len; i++) { + while (!(dev(bus)->SR & SSI_SR_TNF)) {} + dev(bus)->DR = out_buf[i]; + 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; + } } -} - -void spi_poweroff(spi_t dev) -{ - switch ((uintptr_t)spi_config[dev].dev) { - case (uintptr_t)SSI0: - /* disable SSI0 in all three power modes */ - SYS_CTRL_RCGCSSI &= ~SSI0_MASK; - SYS_CTRL_SCGCSSI &= ~SSI0_MASK; - SYS_CTRL_DCGCSSI &= ~SSI0_MASK; - break; - case (uintptr_t)SSI1: - /* disable SSI1 in all three power modes */ - SYS_CTRL_RCGCSSI &= ~SSI1_MASK; - SYS_CTRL_SCGCSSI &= ~SSI1_MASK; - SYS_CTRL_DCGCSSI &= ~SSI1_MASK; - break; + if ((!cont) && (cs != SPI_CS_UNDEF)) { + gpio_set((gpio_t)cs); } } - -#endif /* SPI_NUMOF */