From 91316a879a0746ab9d681c1d83d69a9e81f0f1b8 Mon Sep 17 00:00:00 2001 From: Gerson Fernando Budke Date: Sat, 23 Jan 2021 18:25:59 -0300 Subject: [PATCH] cpu/atxmega/periph/spi: Add spi driver Introduce SPI driver. Signed-off-by: Gerson Fernando Budke --- cpu/atxmega/Makefile.features | 1 + cpu/atxmega/include/periph_cpu.h | 53 +++++++++++ cpu/atxmega/periph/spi.c | 158 +++++++++++++++++++++++++++++++ tests/driver_atwinc15x0/Makefile | 2 +- 4 files changed, 213 insertions(+), 1 deletion(-) create mode 100644 cpu/atxmega/periph/spi.c diff --git a/cpu/atxmega/Makefile.features b/cpu/atxmega/Makefile.features index 337ccbe070..48ace1cc10 100644 --- a/cpu/atxmega/Makefile.features +++ b/cpu/atxmega/Makefile.features @@ -9,5 +9,6 @@ FEATURES_PROVIDED += periph_gpio periph_gpio_irq FEATURES_PROVIDED += periph_i2c FEATURES_PROVIDED += periph_nvm FEATURES_PROVIDED += periph_pm +FEATURES_PROVIDED += periph_spi FEATURES_PROVIDED += periph_timer periph_timer_periodic FEATURES_PROVIDED += periph_uart diff --git a/cpu/atxmega/include/periph_cpu.h b/cpu/atxmega/include/periph_cpu.h index 765e3da030..901a2326f1 100644 --- a/cpu/atxmega/include/periph_cpu.h +++ b/cpu/atxmega/include/periph_cpu.h @@ -301,6 +301,59 @@ typedef struct { cpu_int_lvl_t int_lvl; /**< Serial Interrupt Level */ } i2c_conf_t; +/** + * @brief Enable common 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 +/** @} */ + +/** + * @brief Define global value for undefined SPI device + * @{ + */ +#define SPI_UNDEF (UCHAR_MAX) +/** @} */ + +/** + * @brief Define spi_t data type to save data + * @{ + */ +#define HAVE_SPI_T +typedef uint8_t spi_t; +/** @} */ + +/** + * @brief SPI device configuration + * @{ + */ +typedef struct { + SPI_t *dev; /**< pointer to the used SPI device */ + pwr_reduction_t pwr; /**< Power Management */ + gpio_t sck_pin; /**< pin used for SCK */ + gpio_t miso_pin; /**< pin used for MISO */ + gpio_t mosi_pin; /**< pin used for MOSI */ + gpio_t ss_pin; /**< pin used for SS line */ +} spi_conf_t; +/** @} */ + +/** + * @brief Available SPI clock speeds + * @{ + */ +#define HAVE_SPI_CLK_T +typedef enum { + SPI_CLK_100KHZ = 100000U, /**< drive the SPI bus with 100KHz */ + SPI_CLK_400KHZ = 400000U, /**< drive the SPI bus with 400KHz */ + SPI_CLK_1MHZ = 1000000U, /**< drive the SPI bus with 1MHz */ + SPI_CLK_5MHZ = 5000000U, /**< drive the SPI bus with 5MHz */ + SPI_CLK_10MHZ = 10000000U, /**< drive the SPI bus with 10MHz */ +} spi_clk_t; +/** @} */ + #ifdef __cplusplus } #endif diff --git a/cpu/atxmega/periph/spi.c b/cpu/atxmega/periph/spi.c new file mode 100644 index 0000000000..cc0d57dfc7 --- /dev/null +++ b/cpu/atxmega/periph/spi.c @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2021 Gerson Fernando Budke + * + * 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_atxmega + * @ingroup cpu_atxmega_periph + * @{ + * + * @file + * @brief Low-level SPI driver implementation + * + * @author Gerson Fernando Budke + * + * @} + */ + +#include "cpu.h" +#include "cpu_pm.h" +#include "mutex.h" +#include "periph/spi.h" +#include "pm_layered.h" + +#define ENABLE_DEBUG 0 +#include "debug.h" + +static void _print_buffer(const char* s, const uint8_t* buffer, uint16_t len) +{ + uint16_t i; + + if (buffer == NULL) { + DEBUG("%s - no buffer\n", s); + return; + } + + DEBUG("%s\n", s); + + for (i = 0; i < len; i++) { + if ((i % 16) == 0 && i != 0) { + DEBUG("\n"); + } + else if ((i % 8) == 0) { + DEBUG(" "); + } + + DEBUG("%02x ", buffer[i]); + } + + DEBUG("\n"); +} + +/** + * @brief Allocation device locks + */ +static mutex_t locks[SPI_NUMOF]; + +/** + * @brief Get the pointer to the base register of the given SPI device + * + * @param[in] dev SPI device identifier + * + * @return base register address + */ +static inline SPI_t* dev(spi_t bus) +{ + return (SPI_t*)spi_config[bus].dev; +} + +void spi_init(spi_t bus) +{ + mutex_init(&locks[bus]); + spi_init_pins(bus); + + DEBUG("init\n"); +} + +void spi_init_pins(spi_t bus) +{ + /* SS pin always should be output even when it is not used */ + spi_init_cs(bus, spi_config[bus].ss_pin); + gpio_init(spi_config[bus].sck_pin, GPIO_OUT); + gpio_init(spi_config[bus].miso_pin, GPIO_IN); + gpio_init(spi_config[bus].mosi_pin, GPIO_OUT); +} + +int spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk) +{ + (void)cs; + (void)clk; + + DEBUG("acquire\n"); + + pm_block(3); + mutex_lock(&locks[bus]); + pm_periph_enable(spi_config[bus].pwr); + + dev(bus)->CTRL = SPI_CLK2X_bm + | SPI_ENABLE_bm + | SPI_MASTER_bm + | (mode << SPI_MODE_gp) + | SPI_PRESCALER0_bm; + + (void)dev(bus)->STATUS; + (void)dev(bus)->DATA; + + return SPI_OK; +} + +void spi_release(spi_t bus) +{ + dev(bus)->CTRL &= ~SPI_ENABLE_bm; + pm_periph_disable(spi_config[bus].pwr); + mutex_unlock(&locks[bus]); + pm_unblock(3); + + DEBUG("release\n"); +} + +void spi_transfer_bytes(spi_t bus, spi_cs_t cs, bool cont, + const void *out, void *in, size_t len) +{ + uint8_t *out_buf = (uint8_t *)out; + uint8_t *in_buf = (uint8_t *)in; + + if (IS_ACTIVE(ENABLE_DEBUG)) { + _print_buffer("bytes - out", out, len); + } + + if (cs != SPI_CS_UNDEF) { + gpio_clear((gpio_t) cs); + } + + for (size_t i = 0; i < len; i++) { + uint8_t tmp = (out_buf) ? out_buf[i] : 0; + + dev(bus)->DATA = tmp; + + while (!(dev(bus)->STATUS & SPI_IF_bm)){} + + tmp = dev(bus)->DATA; + + if (in_buf) { + in_buf[i] = tmp; + } + } + + if ((!cont) && (cs != SPI_CS_UNDEF)) { + gpio_set((gpio_t) cs); + } + + if (IS_ACTIVE(ENABLE_DEBUG)) { + _print_buffer("bytes - in", in, len); + } +} diff --git a/tests/driver_atwinc15x0/Makefile b/tests/driver_atwinc15x0/Makefile index 0542cb1cdb..cbc8738aaa 100644 --- a/tests/driver_atwinc15x0/Makefile +++ b/tests/driver_atwinc15x0/Makefile @@ -2,6 +2,6 @@ USEMODULE = atwinc15x0 # msp430-gcc doesn't support -Wno-discarded-qualifiers -FEATURES_BLACKLIST += arch_msp430 +FEATURES_BLACKLIST += arch_msp430 cpu_core_atxmega include ../driver_netdev_common/Makefile.netdev.mk