diff --git a/.github/workflows/tools-buildtest.yml b/.github/workflows/tools-buildtest.yml index 35bc9e8dded2a..39012cf1c4d97 100644 --- a/.github/workflows/tools-buildtest.yml +++ b/.github/workflows/tools-buildtest.yml @@ -28,8 +28,6 @@ jobs: path: dist/tools - name: bossa-nrf52 path: dist/tools - - name: calc_spi_scalers - path: cpu/kinetis/dist - name: clk_conf path: cpu/stm32/dist - name: edbg diff --git a/boards/common/kw41z/include/periph_conf_common.h b/boards/common/kw41z/include/periph_conf_common.h index 6eafe086951a4..2d4fce31e10ce 100644 --- a/boards/common/kw41z/include/periph_conf_common.h +++ b/boards/common/kw41z/include/periph_conf_common.h @@ -122,50 +122,6 @@ static const uart_conf_t uart_config[] = { #define LPUART_0_SRC 3 /** @} */ -/** - * @name SPI clock configuration - * - * Clock configuration values based on the configured 16Mhz module clock. - * - * Auto-generated by: - * cpu/kinetis/dist/calc_spi_scalers/calc_spi_scalers.c - * -* @{ -*/ -static const uint32_t spi_clk_config[] = { - ( - SPI_CTAR_PBR(2) | SPI_CTAR_BR(5) | /* -> 100000Hz */ - SPI_CTAR_PCSSCK(2) | SPI_CTAR_CSSCK(4) | - SPI_CTAR_PASC(2) | SPI_CTAR_ASC(4) | - SPI_CTAR_PDT(2) | SPI_CTAR_DT(4) - ), - ( - SPI_CTAR_PBR(2) | SPI_CTAR_BR(3) | /* -> 400000Hz */ - SPI_CTAR_PCSSCK(2) | SPI_CTAR_CSSCK(2) | - SPI_CTAR_PASC(2) | SPI_CTAR_ASC(2) | - SPI_CTAR_PDT(2) | SPI_CTAR_DT(2) - ), - ( - SPI_CTAR_PBR(0) | SPI_CTAR_BR(3) | /* -> 1000000Hz */ - SPI_CTAR_PCSSCK(0) | SPI_CTAR_CSSCK(3) | - SPI_CTAR_PASC(0) | SPI_CTAR_ASC(3) | - SPI_CTAR_PDT(0) | SPI_CTAR_DT(3) - ), - ( - SPI_CTAR_PBR(0) | SPI_CTAR_BR(0) | /* -> 4000000Hz */ - SPI_CTAR_PCSSCK(0) | SPI_CTAR_CSSCK(1) | - SPI_CTAR_PASC(0) | SPI_CTAR_ASC(1) | - SPI_CTAR_PDT(0) | SPI_CTAR_DT(1) - ), - ( - SPI_CTAR_PBR(0) | SPI_CTAR_BR(0) | /* -> 4000000Hz */ - SPI_CTAR_PCSSCK(0) | SPI_CTAR_CSSCK(0) | - SPI_CTAR_PASC(0) | SPI_CTAR_ASC(0) | - SPI_CTAR_PDT(0) | SPI_CTAR_DT(0) - ) -}; -/** @} */ - /** * @name Random Number Generator configuration * @{ diff --git a/boards/frdm-k22f/include/periph_conf.h b/boards/frdm-k22f/include/periph_conf.h index a4f5908887704..4e082b0512a84 100644 --- a/boards/frdm-k22f/include/periph_conf.h +++ b/boards/frdm-k22f/include/periph_conf.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2017 Eistec AB + * 2021-2023 Hugues Larrive * * 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 @@ -14,6 +15,7 @@ * @name Peripheral MCU configuration for the FRDM-K22F * * @author Joakim Nohlgård + * @author Hugues Larrive */ #ifndef PERIPH_CONF_H @@ -141,22 +143,54 @@ static const uart_conf_t uart_config[] = { */ static const adc_conf_t adc_config[] = { /* dev, pin, channel */ - [ 0] = { .dev = ADC0, .pin = GPIO_UNDEF , .chan = 0, .avg = ADC_AVG_MAX }, /* ADC0_DP0 */ - [ 1] = { .dev = ADC0, .pin = GPIO_UNDEF , .chan = 19, .avg = ADC_AVG_MAX }, /* ADC0_DM0 */ - [ 2] = { .dev = ADC1, .pin = GPIO_UNDEF , .chan = 0, .avg = ADC_AVG_MAX }, /* ADC1_DP0 */ - [ 3] = { .dev = ADC1, .pin = GPIO_UNDEF , .chan = 19, .avg = ADC_AVG_MAX }, /* ADC1_DM0 */ - [ 4] = { .dev = ADC0, .pin = GPIO_PIN(PORT_B, 0), .chan = 8, .avg = ADC_AVG_MAX }, /* PTB0 (Arduino A0) */ - [ 5] = { .dev = ADC0, .pin = GPIO_PIN(PORT_B, 1), .chan = 9, .avg = ADC_AVG_MAX }, /* PTB1 (Arduino A1) */ - [ 6] = { .dev = ADC0, .pin = GPIO_PIN(PORT_C, 1), .chan = 15, .avg = ADC_AVG_MAX }, /* PTC1 (Arduino A2) */ - [ 7] = { .dev = ADC0, .pin = GPIO_PIN(PORT_C, 2), .chan = 4, .avg = ADC_AVG_MAX }, /* PTC2 (Arduino A3) */ + [0] = { /* ADC0_DP0 */ + .dev = ADC0, .pin = GPIO_UNDEF, + .chan = 0, .avg = ADC_AVG_MAX + }, + [1] = { /* ADC0_DM0 */ + .dev = ADC0, .pin = GPIO_UNDEF, + .chan = 19, .avg = ADC_AVG_MAX + }, + [2] = { /* ADC1_DP0 */ + .dev = ADC1, .pin = GPIO_UNDEF, + .chan = 0, .avg = ADC_AVG_MAX + }, + [3] = { /* ADC1_DM0 */ + .dev = ADC1, .pin = GPIO_UNDEF, + .chan = 19, .avg = ADC_AVG_MAX + }, + [4] = { /* PTB0 (Arduino A0) */ + .dev = ADC0, .pin = GPIO_PIN(PORT_B, 0), + .chan = 8, .avg = ADC_AVG_MAX + }, + [5] = { /* PTB1 (Arduino A1) */ + .dev = ADC0, .pin = GPIO_PIN(PORT_B, 1), + .chan = 9, .avg = ADC_AVG_MAX + }, + [6] = { /* PTC1 (Arduino A2) */ + .dev = ADC0, .pin = GPIO_PIN(PORT_C, 1), + .chan = 15, .avg = ADC_AVG_MAX + }, + [7] = { /* PTC2 (Arduino A3) */ + .dev = ADC0, .pin = GPIO_PIN(PORT_C, 2), + .chan = 4, .avg = ADC_AVG_MAX + }, /* internal: temperature sensor */ - /* The temperature sensor has a very high output impedance, it must not be - * sampled using hardware averaging, or the sampled values will be garbage */ - [ 8] = { .dev = ADC0, .pin = GPIO_UNDEF, .chan = 26, .avg = ADC_AVG_NONE }, + /* The temperature sensor has a very high output impedance, it must + * not be sampled using hardware averaging, or the sampled values + * will be garbage */ + [8] = { + .dev = ADC0, .pin = GPIO_UNDEF, + .chan = 26, .avg = ADC_AVG_NONE + }, /* internal: band gap */ - /* Note: the band gap buffer uses a bit of current and is turned off by default, - * Set PMC->REGSC |= PMC_REGSC_BGBE_MASK before reading or the input will be floating */ - [ 9] = { .dev = ADC0, .pin = GPIO_UNDEF, .chan = 27, .avg = ADC_AVG_MAX }, + /* Note: the band gap buffer uses a bit of current and is turned off + * by default, set PMC->REGSC |= PMC_REGSC_BGBE_MASK before reading + * or the input will be floating */ + [9] = { + .dev = ADC0, .pin = GPIO_UNDEF, + .chan = 27, .avg = ADC_AVG_MAX + }, }; #define ADC_NUMOF ARRAY_SIZE(adc_config) @@ -191,47 +225,8 @@ static const pwm_conf_t pwm_config[] = { /** * @name SPI configuration - * - * Clock configuration values based on the configured 48Mhz module clock. - * - * Auto-generated by: - * cpu/kinetis/dist/calc_spi_scalers/calc_spi_scalers.c - * -* @{ -*/ -static const uint32_t spi_clk_config[] = { - ( - SPI_CTAR_PBR(0) | SPI_CTAR_BR(8) | /* -> 93750Hz */ - SPI_CTAR_PCSSCK(0) | SPI_CTAR_CSSCK(8) | - SPI_CTAR_PASC(0) | SPI_CTAR_ASC(8) | - SPI_CTAR_PDT(0) | SPI_CTAR_DT(8) - ), - ( - SPI_CTAR_PBR(0) | SPI_CTAR_BR(6) | /* -> 375000Hz */ - SPI_CTAR_PCSSCK(0) | SPI_CTAR_CSSCK(6) | - SPI_CTAR_PASC(0) | SPI_CTAR_ASC(6) | - SPI_CTAR_PDT(0) | SPI_CTAR_DT(6) - ), - ( - SPI_CTAR_PBR(1) | SPI_CTAR_BR(4) | /* -> 1000000Hz */ - SPI_CTAR_PCSSCK(1) | SPI_CTAR_CSSCK(3) | - SPI_CTAR_PASC(1) | SPI_CTAR_ASC(3) | - SPI_CTAR_PDT(1) | SPI_CTAR_DT(3) - ), - ( - SPI_CTAR_PBR(2) | SPI_CTAR_BR(0) | /* -> 4800000Hz */ - SPI_CTAR_PCSSCK(2) | SPI_CTAR_CSSCK(0) | - SPI_CTAR_PASC(2) | SPI_CTAR_ASC(0) | - SPI_CTAR_PDT(2) | SPI_CTAR_DT(0) - ), - ( - SPI_CTAR_PBR(1) | SPI_CTAR_BR(0) | /* -> 8000000Hz */ - SPI_CTAR_PCSSCK(1) | SPI_CTAR_CSSCK(0) | - SPI_CTAR_PASC(1) | SPI_CTAR_ASC(0) | - SPI_CTAR_PDT(1) | SPI_CTAR_DT(0) - ) -}; - + * @{ + */ static const spi_conf_t spi_config[] = { { .dev = SPI0, diff --git a/boards/frdm-k64f/include/periph_conf.h b/boards/frdm-k64f/include/periph_conf.h index bc94d95423b84..818f8e5ab6a3b 100644 --- a/boards/frdm-k64f/include/periph_conf.h +++ b/boards/frdm-k64f/include/periph_conf.h @@ -188,47 +188,8 @@ static const pwm_conf_t pwm_config[] = { /** * @name SPI configuration - * - * Clock configuration values based on the configured 30Mhz module clock. - * - * Auto-generated by: - * cpu/kinetis/dist/calc_spi_scalers/calc_spi_scalers.c - * -* @{ -*/ -static const uint32_t spi_clk_config[] = { - ( - SPI_CTAR_PBR(2) | SPI_CTAR_BR(6) | /* -> 93750Hz */ - SPI_CTAR_PCSSCK(2) | SPI_CTAR_CSSCK(5) | - SPI_CTAR_PASC(2) | SPI_CTAR_ASC(5) | - SPI_CTAR_PDT(2) | SPI_CTAR_DT(5) - ), - ( - SPI_CTAR_PBR(2) | SPI_CTAR_BR(4) | /* -> 375000Hz */ - SPI_CTAR_PCSSCK(2) | SPI_CTAR_CSSCK(3) | - SPI_CTAR_PASC(2) | SPI_CTAR_ASC(3) | - SPI_CTAR_PDT(2) | SPI_CTAR_DT(3) - ), - ( - SPI_CTAR_PBR(2) | SPI_CTAR_BR(2) | /* -> 1000000Hz */ - SPI_CTAR_PCSSCK(0) | SPI_CTAR_CSSCK(4) | - SPI_CTAR_PASC(0) | SPI_CTAR_ASC(4) | - SPI_CTAR_PDT(0) | SPI_CTAR_DT(4) - ), - ( - SPI_CTAR_PBR(1) | SPI_CTAR_BR(0) | /* -> 5000000Hz */ - SPI_CTAR_PCSSCK(1) | SPI_CTAR_CSSCK(0) | - SPI_CTAR_PASC(1) | SPI_CTAR_ASC(0) | - SPI_CTAR_PDT(1) | SPI_CTAR_DT(0) - ), - ( - SPI_CTAR_PBR(0) | SPI_CTAR_BR(0) | /* -> 7500000Hz */ - SPI_CTAR_PCSSCK(0) | SPI_CTAR_CSSCK(1) | - SPI_CTAR_PASC(0) | SPI_CTAR_ASC(1) | - SPI_CTAR_PDT(0) | SPI_CTAR_DT(1) - ) -}; - + * @{ + */ static const spi_conf_t spi_config[] = { { .dev = SPI0, diff --git a/boards/mulle/include/periph_conf.h b/boards/mulle/include/periph_conf.h index 220ea1de7ea67..4be533fc8828f 100644 --- a/boards/mulle/include/periph_conf.h +++ b/boards/mulle/include/periph_conf.h @@ -260,47 +260,8 @@ static const pwm_conf_t pwm_config[] = { /** * @name SPI configuration - * - * Clock configuration values based on the configured 47988736Hz module clock. - * - * Auto-generated by: - * cpu/kinetis/dist/calc_spi_scalers/calc_spi_scalers.c - * * @{ */ -static const uint32_t spi_clk_config[] = { - ( - SPI_CTAR_PBR(0) | SPI_CTAR_BR(8) | /* -> 93728Hz */ - SPI_CTAR_PCSSCK(0) | SPI_CTAR_CSSCK(8) | - SPI_CTAR_PASC(0) | SPI_CTAR_ASC(8) | - SPI_CTAR_PDT(0) | SPI_CTAR_DT(8) - ), - ( - SPI_CTAR_PBR(0) | SPI_CTAR_BR(6) | /* -> 374912Hz */ - SPI_CTAR_PCSSCK(0) | SPI_CTAR_CSSCK(6) | - SPI_CTAR_PASC(0) | SPI_CTAR_ASC(6) | - SPI_CTAR_PDT(0) | SPI_CTAR_DT(6) - ), - ( - SPI_CTAR_PBR(1) | SPI_CTAR_BR(4) | /* -> 999765Hz */ - SPI_CTAR_PCSSCK(1) | SPI_CTAR_CSSCK(3) | - SPI_CTAR_PASC(1) | SPI_CTAR_ASC(3) | - SPI_CTAR_PDT(1) | SPI_CTAR_DT(3) - ), - ( - SPI_CTAR_PBR(2) | SPI_CTAR_BR(0) | /* -> 4798873Hz */ - SPI_CTAR_PCSSCK(2) | SPI_CTAR_CSSCK(0) | - SPI_CTAR_PASC(2) | SPI_CTAR_ASC(0) | - SPI_CTAR_PDT(2) | SPI_CTAR_DT(0) - ), - ( - SPI_CTAR_PBR(1) | SPI_CTAR_BR(0) | /* -> 7998122Hz */ - SPI_CTAR_PCSSCK(1) | SPI_CTAR_CSSCK(0) | - SPI_CTAR_PASC(1) | SPI_CTAR_ASC(0) | - SPI_CTAR_PDT(1) | SPI_CTAR_DT(0) - ) -}; - static const spi_conf_t spi_config[] = { { .dev = SPI0, diff --git a/boards/openlabs-kw41z-mini/include/periph_conf.h b/boards/openlabs-kw41z-mini/include/periph_conf.h index 062a193d08fd3..9ba8e19e0828b 100644 --- a/boards/openlabs-kw41z-mini/include/periph_conf.h +++ b/boards/openlabs-kw41z-mini/include/periph_conf.h @@ -267,47 +267,8 @@ static const pwm_conf_t pwm_config[] = { /** * @name SPI configuration - * - * Clock configuration values based on the configured 16Mhz module clock. - * - * Auto-generated by: - * cpu/kinetis/dist/calc_spi_scalers/calc_spi_scalers.c - * * @{ */ -static const uint32_t spi_clk_config[] = { - ( - SPI_CTAR_PBR(2) | SPI_CTAR_BR(5) | /* -> 100000Hz */ - SPI_CTAR_PCSSCK(2) | SPI_CTAR_CSSCK(4) | - SPI_CTAR_PASC(2) | SPI_CTAR_ASC(4) | - SPI_CTAR_PDT(2) | SPI_CTAR_DT(4) - ), - ( - SPI_CTAR_PBR(2) | SPI_CTAR_BR(3) | /* -> 400000Hz */ - SPI_CTAR_PCSSCK(2) | SPI_CTAR_CSSCK(2) | - SPI_CTAR_PASC(2) | SPI_CTAR_ASC(2) | - SPI_CTAR_PDT(2) | SPI_CTAR_DT(2) - ), - ( - SPI_CTAR_PBR(0) | SPI_CTAR_BR(3) | /* -> 1000000Hz */ - SPI_CTAR_PCSSCK(0) | SPI_CTAR_CSSCK(3) | - SPI_CTAR_PASC(0) | SPI_CTAR_ASC(3) | - SPI_CTAR_PDT(0) | SPI_CTAR_DT(3) - ), - ( - SPI_CTAR_PBR(0) | SPI_CTAR_BR(0) | /* -> 4000000Hz */ - SPI_CTAR_PCSSCK(0) | SPI_CTAR_CSSCK(1) | - SPI_CTAR_PASC(0) | SPI_CTAR_ASC(1) | - SPI_CTAR_PDT(0) | SPI_CTAR_DT(1) - ), - ( - SPI_CTAR_PBR(0) | SPI_CTAR_BR(0) | /* -> 4000000Hz */ - SPI_CTAR_PCSSCK(0) | SPI_CTAR_CSSCK(0) | - SPI_CTAR_PASC(0) | SPI_CTAR_ASC(0) | - SPI_CTAR_PDT(0) | SPI_CTAR_DT(0) - ) -}; - static const spi_conf_t spi_config[] = { { .dev = SPI0, diff --git a/boards/pba-d-01-kw2x/include/periph_conf.h b/boards/pba-d-01-kw2x/include/periph_conf.h index 0bf510be4841f..7d5b78c46be65 100644 --- a/boards/pba-d-01-kw2x/include/periph_conf.h +++ b/boards/pba-d-01-kw2x/include/periph_conf.h @@ -174,47 +174,8 @@ static const pwm_conf_t pwm_config[] = { /** * @name SPI device configuration - * - * Clock configuration values based on the configured 48Mhz module clock. - * - * Auto-generated by: - * cpu/kinetis/dist/calc_spi_scalers/calc_spi_scalers.c - * * @{ */ -static const uint32_t spi_clk_config[] = { - ( - SPI_CTAR_PBR(0) | SPI_CTAR_BR(8) | /* -> 93750Hz */ - SPI_CTAR_PCSSCK(0) | SPI_CTAR_CSSCK(8) | - SPI_CTAR_PASC(0) | SPI_CTAR_ASC(8) | - SPI_CTAR_PDT(0) | SPI_CTAR_DT(8) - ), - ( - SPI_CTAR_PBR(0) | SPI_CTAR_BR(6) | /* -> 375000Hz */ - SPI_CTAR_PCSSCK(0) | SPI_CTAR_CSSCK(6) | - SPI_CTAR_PASC(0) | SPI_CTAR_ASC(6) | - SPI_CTAR_PDT(0) | SPI_CTAR_DT(6) - ), - ( - SPI_CTAR_PBR(1) | SPI_CTAR_BR(4) | /* -> 1000000Hz */ - SPI_CTAR_PCSSCK(1) | SPI_CTAR_CSSCK(3) | - SPI_CTAR_PASC(1) | SPI_CTAR_ASC(3) | - SPI_CTAR_PDT(1) | SPI_CTAR_DT(3) - ), - ( - SPI_CTAR_PBR(2) | SPI_CTAR_BR(0) | /* -> 4800000Hz */ - SPI_CTAR_PCSSCK(2) | SPI_CTAR_CSSCK(0) | - SPI_CTAR_PASC(2) | SPI_CTAR_ASC(0) | - SPI_CTAR_PDT(2) | SPI_CTAR_DT(0) - ), - ( - SPI_CTAR_PBR(1) | SPI_CTAR_BR(0) | /* -> 8000000Hz */ - SPI_CTAR_PCSSCK(1) | SPI_CTAR_CSSCK(0) | - SPI_CTAR_PASC(1) | SPI_CTAR_ASC(0) | - SPI_CTAR_PDT(1) | SPI_CTAR_DT(0) - ) -}; - static const spi_conf_t spi_config[] = { { .dev = SPI0, diff --git a/cpu/atmega_common/include/periph_cpu_common.h b/cpu/atmega_common/include/periph_cpu_common.h index 5d2b04f24024b..1aed2217cc0b7 100644 --- a/cpu/atmega_common/include/periph_cpu_common.h +++ b/cpu/atmega_common/include/periph_cpu_common.h @@ -2,6 +2,7 @@ * Copyright (C) 2015 HAW Hamburg * 2016 Freie Universität Berlin * 2016 INRIA + * 2021-2023 Hugues Larrive * * 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 @@ -18,6 +19,7 @@ * @author René Herthel * @author Hauke Petersen * @author Francisco Acosta + * @author Hugues Larrive */ #ifndef PERIPH_CPU_COMMON_H @@ -254,26 +256,14 @@ typedef enum { /** @} */ /** - * @brief SPI speed selection macro - * - * We encode the speed in bits 2, 1, and 0, where bit0 and bit1 hold the SPCR - * prescaler bits, while bit2 holds the SPI2X bit. - */ -#define SPI_CLK_SEL(s2x, pr1, pr0) ((s2x << 2) | (pr1 << 1) | pr0) - -/** - * @name Override SPI speed values - * - * We assume a master clock speed of 16MHz here. + * @brief Override SPI clock configuration * @{ */ #define HAVE_SPI_CLK_T -typedef enum { - SPI_CLK_100KHZ = SPI_CLK_SEL(0, 1, 1), /**< 16/128 -> 125KHz */ - SPI_CLK_400KHZ = SPI_CLK_SEL(1, 1, 0), /**< 16/32 -> 500KHz */ - SPI_CLK_1MHZ = SPI_CLK_SEL(0, 0, 1), /**< 16/16 -> 1MHz */ - SPI_CLK_5MHZ = SPI_CLK_SEL(0, 0, 0), /**< 16/4 -> 4MHz */ - SPI_CLK_10MHZ = SPI_CLK_SEL(1, 0, 0) /**< 16/2 -> 8MHz */ +typedef struct { + int err; + uint8_t spi2x; + uint8_t spr; } spi_clk_t; /** @} */ #endif /* ifndef DOXYGEN */ diff --git a/cpu/atmega_common/periph/spi.c b/cpu/atmega_common/periph/spi.c index cac04cfdeb409..e521cbd1fdaaf 100644 --- a/cpu/atmega_common/periph/spi.c +++ b/cpu/atmega_common/periph/spi.c @@ -3,6 +3,7 @@ * 2016 Freie Universität Berlin * 2017 Hamburg University of Applied Sciences * 2017 Thomas Perrot + * 2021-2023 Hugues Larrive * * 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 @@ -21,25 +22,58 @@ * @author Hauke Petersen * @author Dimitri Nahm * @author Thomas Perrot + * @author Hugues Larrive * * @} */ #include #include "cpu.h" +#include "macros/math.h" +#include "macros/units.h" #include "mutex.h" #include "periph/spi.h" +#define ENABLE_DEBUG 0 +#include "debug.h" + +static mutex_t lock = MUTEX_INIT; + /** - * @brief Extract BR0, BR1 and SPI2X bits from speed value + * @brief Helper function to compute a right shift value corresponding + * to dividers * @{ */ -#define CLK_MASK (0x3) -#define S2X_SHIFT (2) +static uint8_t _clk_shift(uint32_t freq) +{ + /* Atmega datasheets give the following table: + * SPI2X SPR1 SPR0 SCK Frequency + * 0 0 0 Fosc/4 + * 0 0 1 Fosc/16 + * 0 1 0 Fosc/64 + * 0 1 1 Fosc/128 + * 1 0 0 Fosc/2 + * 1 0 1 Fosc/8 + * 1 1 0 Fosc/32 + * 1 1 1 Fosc/64 + * We can easily sort it by dividers by inverting the SPI2X bit and + * taking it as LSB: + * Divider SPR1 SPR0 ~SPI2X shift + * 2 0 0 0 0 + * 4 0 0 1 1 + * 8 0 1 0 2 + * 16 0 1 1 3 + * 32 1 0 0 4 + * 64 1 0 1 5 + * 64 1 1 0 6 + * 128 1 1 1 7 */ + uint8_t shift; + for (shift = 0; freq << shift < CLOCK_CORECLOCK / 2; + shift = ++shift > 5 ? shift + 1 : shift) {} + return shift; +} /** @} */ -static mutex_t lock = MUTEX_INIT; - void spi_init(spi_t bus) { assert(bus == 0); @@ -78,19 +112,45 @@ void spi_init_pins(spi_t bus) #endif } +spi_clk_t spi_get_clk(spi_t bus, uint32_t freq) +{ + (void)bus; + /* bound divider to 128 */ + if(freq < DIV_ROUND_UP(CLOCK_CORECLOCK, 128)) { + return (spi_clk_t){ .err = -EDOM }; + } + + uint8_t shift = _clk_shift(freq); + return (spi_clk_t){ + .spi2x = ~shift & 1, + .spr = shift >> 1 + }; +} + +int32_t spi_get_freq(spi_t bus, spi_clk_t clk) +{ + (void)bus; + if (clk.err) { return -EINVAL; } + uint8_t shift = (~clk.spi2x & 1) | (clk.spr << 1); + return shift > 5 ? + CLOCK_CORECLOCK >> shift : (CLOCK_CORECLOCK / 2) >> shift; +} + void spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk) { (void)bus; (void)cs; + assert(bus == SPI_DEV(0)); + if (clk.err) { return; } /* lock the bus and power on the SPI peripheral */ mutex_lock(&lock); power_spi_enable(); /* configure as master, with given mode and clock */ - SPSR = (clk >> S2X_SHIFT); - SPCR = ((1 << SPE) | (1 << MSTR) | mode | (clk & CLK_MASK)); + SPSR = clk.spi2x; + SPCR = (1 << SPE | 1 << MSTR | mode | clk.spr); /* clear interrupt flag by reading SPSR and data register by reading SPDR */ (void)SPSR; diff --git a/cpu/atxmega/include/periph_cpu.h b/cpu/atxmega/include/periph_cpu.h index 440f09266a9b0..13fcce5561fba 100644 --- a/cpu/atxmega/include/periph_cpu.h +++ b/cpu/atxmega/include/periph_cpu.h @@ -340,20 +340,6 @@ typedef struct { } 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; -/** @} */ - #if defined(__AVR_ATxmega64A1__) || \ defined(__AVR_ATxmega128A1__) || \ defined(__AVR_ATxmega64A1U__) || \ diff --git a/cpu/atxmega/periph/spi.c b/cpu/atxmega/periph/spi.c index 690bf73717d24..9e62db5d7898e 100644 --- a/cpu/atxmega/periph/spi.c +++ b/cpu/atxmega/periph/spi.c @@ -21,6 +21,7 @@ #include "cpu.h" #include "cpu_pm.h" +#include "macros/math.h" #include "mutex.h" #include "periph/spi.h" #include "pm_layered.h" @@ -28,6 +29,41 @@ #define ENABLE_DEBUG 0 #include "debug.h" +/** + * @brief Helper function to compute a right shift value corresponding + * to dividers + * @{ + */ +static uint8_t _clk_shift(uint32_t freq) +{ + /* Atmega datasheets give the following table: + * SPI2X SPR1 SPR0 SCK Frequency + * 0 0 0 Fosc/4 + * 0 0 1 Fosc/16 + * 0 1 0 Fosc/64 + * 0 1 1 Fosc/128 + * 1 0 0 Fosc/2 + * 1 0 1 Fosc/8 + * 1 1 0 Fosc/32 + * 1 1 1 Fosc/64 + * We can easily sort it by dividers by inverting the SPI2X bit and + * taking it as LSB: + * Divider SPR1 SPR0 ~SPI2X shift + * 2 0 0 0 0 + * 4 0 0 1 1 + * 8 0 1 0 2 + * 16 0 1 1 3 + * 32 1 0 0 4 + * 64 1 0 1 5 + * 64 1 1 0 6 + * 128 1 1 1 7 */ + uint8_t shift; + for (shift = 0; freq << shift < CLOCK_CORECLOCK / 2; + shift = ++shift > 5 ? shift + 1 : shift) {} + return shift; +} +/** @} */ + static void _print_buffer(const char* s, const uint8_t* buffer, uint16_t len) { uint16_t i; @@ -87,10 +123,35 @@ void spi_init_pins(spi_t bus) gpio_init(spi_config[bus].mosi_pin, GPIO_OUT); } +spi_clk_t spi_get_clk(spi_t bus, uint32_t freq) +{ + (void)bus; + /* bound divider to 128 */ + if(freq < DIV_ROUND_UP(CLOCK_CORECLOCK, 128)) { + return (spi_clk_t){ .err = -EDOM }; + } + + uint8_t shift = _clk_shift(freq); + return (spi_clk_t){ + .clk = ((~shift & 1) << 7) | (shift >> 1) + }; +} + +int32_t spi_get_freq(spi_t bus, spi_clk_t clk) +{ + (void)bus; + if (clk.err) { return -EINVAL; } + uint8_t shift = ((~clk.clk & 0x80) >> 7) | (clk.clk << 1); + return shift > 5 ? + CLOCK_CORECLOCK >> shift : (CLOCK_CORECLOCK / 2) >> shift; +} + void spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk) { (void)cs; - (void)clk; + + assert(bus < SPI_NUMOF); + if (clk.err) { return; } DEBUG("acquire\n"); @@ -98,11 +159,10 @@ void spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk) mutex_lock(&locks[bus]); pm_periph_enable(spi_config[bus].pwr); - dev(bus)->CTRL = SPI_CLK2X_bm - | SPI_ENABLE_bm + dev(bus)->CTRL = SPI_ENABLE_bm | SPI_MASTER_bm | (mode << SPI_MODE_gp) - | SPI_PRESCALER0_bm; + | clk.clk; (void)dev(bus)->STATUS; (void)dev(bus)->DATA; diff --git a/cpu/cc2538/include/periph_cpu.h b/cpu/cc2538/include/periph_cpu.h index 2ded24080c27e..66774eacc03d1 100644 --- a/cpu/cc2538/include/periph_cpu.h +++ b/cpu/cc2538/include/periph_cpu.h @@ -1,6 +1,7 @@ /* * Copyright (C) 2015-2016 Freie Universität Berlin * 2017 HAW Hamburg + * 2021-2023 Hugues Larrive * * 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 @@ -16,6 +17,7 @@ * * @author Hauke Petersen * @author Sebastian Meiling + * @author Hugues Larrive */ #ifndef PERIPH_CPU_H @@ -249,45 +251,18 @@ typedef enum { /** @ */ /** - * @name Override SPI clock settings + * @brief Override SPI clock configuration * @{ */ #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 */ +typedef struct { + int err; + uint32_t cpsr; + uint32_t scr; } spi_clk_t; /** @} */ #endif /* ndef DOXYGEN */ -/** - * @brief Datafields for static SPI clock configuration values - */ -typedef struct { - uint8_t cpsr; /**< CPSR clock divider */ - uint8_t scr; /**< SCR clock divider */ -} spi_clk_conf_t; - -#ifndef BOARD_HAS_SPI_CLK_CONF -/** - * @brief Pre-calculated clock divider values based on a CLOCK_CORECLOCK (32MHz) - * - * SPI bus frequency = CLOCK_CORECLOCK / (CPSR * (SCR + 1)), with - * CPSR = 2..254 and even, - * SCR = 0..255 - */ -static const spi_clk_conf_t spi_clk_config[] = { - { .cpsr = 64, .scr = 4 }, /* 100khz */ - { .cpsr = 16, .scr = 4 }, /* 400khz */ - { .cpsr = 32, .scr = 0 }, /* 1.0MHz */ - { .cpsr = 2, .scr = 2 }, /* 5.3MHz */ - { .cpsr = 2, .scr = 1 } /* 8.0MHz */ -}; -#endif /* BOARD_HAS_SPI_CLK_CONF */ - /** * @name SPI configuration data structure * @{ diff --git a/cpu/cc2538/periph/spi.c b/cpu/cc2538/periph/spi.c index edd67d97a6f4b..2673fa37452ce 100644 --- a/cpu/cc2538/periph/spi.c +++ b/cpu/cc2538/periph/spi.c @@ -1,6 +1,7 @@ /* * Copyright (C) 2015 Loci Controls Inc. * 2016 Freie Universität Berlin + * 2021-2023 Hugues Larrive * * 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 @@ -17,6 +18,7 @@ * * @author Ian Martin * @author Hauke Petersen + * @author Hugues Larrive * * @} */ @@ -27,6 +29,7 @@ #include "vendor/hw_ssi.h" #include "cpu.h" +#include "macros/math.h" #include "mutex.h" #include "periph/spi.h" @@ -94,19 +97,58 @@ void spi_init_pins(spi_t bus) gpio_init_mux(spi_config[bus].miso_pin, OVERRIDE_DISABLE, GPIO_MUX_NONE, rxd); } +spi_clk_t spi_get_clk(spi_t bus, uint32_t freq) +{ + (void)bus; + + /* SPI bus frequency = CLOCK_CORECLOCK / (CPSR * (SCR + 1)), with + * CPSR = 2..254 and even, + * SCR = 0..255 */ + + /* bound divider to 65024 (254 * (255 + 1)) */ + if (freq < DIV_ROUND_UP(CLOCK_CORECLOCK, 65024)) { + return (spi_clk_t){ .err = -EDOM }; + } + + uint8_t cpsr = 2, scr = 0; + uint32_t divider = DIV_ROUND_UP(CLOCK_CORECLOCK, freq); + if (divider % 2) { + divider++; + } + while (divider / cpsr > 256) { + cpsr += 2; + } + scr = divider / cpsr - 1; + + return (spi_clk_t){ + .cpsr = cpsr, + .scr = (scr << SSI_CR0_SCR_S) + }; +} + +int32_t spi_get_freq(spi_t bus, spi_clk_t clk) +{ + (void)bus; + if (clk.err) { return -EINVAL; } + return CLOCK_CORECLOCK / (clk.cpsr * ((clk.scr >> SSI_CR0_SCR_S) + 1)); +} + void spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk) { + (void)cs; + assert((unsigned)bus < SPI_NUMOF); + if (clk.err) { return; } + DEBUG("%s: bus=%u\n", __FUNCTION__, bus); - (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)); + dev(bus)->CPSR = clk.cpsr; + dev(bus)->CR0 = clk.scr | mode | SSI_CR0_DSS(8); /* enable SSI device */ dev(bus)->CR1 = SSI_CR1_SSE; } diff --git a/cpu/efm32/include/periph_cpu.h b/cpu/efm32/include/periph_cpu.h index b60018246dc13..3ce0ae9c3c725 100644 --- a/cpu/efm32/include/periph_cpu.h +++ b/cpu/efm32/include/periph_cpu.h @@ -465,7 +465,7 @@ typedef struct { #ifndef DOXYGEN /** - * @brief Override SPI clocks. + * @brief Override SPI modes. * @{ */ #define HAVE_SPI_MODE_T @@ -476,20 +476,6 @@ typedef enum { SPI_MODE_3 = usartClockMode3 } spi_mode_t; /** @} */ - -/** - * @brief Define a set of pre-defined SPI clock speeds. - * @{ - */ -#define HAVE_SPI_CLK_T -typedef enum { - SPI_CLK_100KHZ = 100000, /**< drive the SPI bus with 100KHz */ - SPI_CLK_400KHZ = 400000, /**< drive the SPI bus with 400KHz */ - SPI_CLK_1MHZ = 1000000, /**< drive the SPI bus with 1MHz */ - SPI_CLK_5MHZ = 5000000, /**< drive the SPI bus with 5MHz */ - SPI_CLK_10MHZ = 10000000 /**< drive the SPI bus with 10MHz */ -} spi_clk_t; -/** @} */ #endif /* ndef DOXYGEN */ /** diff --git a/cpu/efm32/periph/spi.c b/cpu/efm32/periph/spi.c index 1b63a8e3fa030..2b48fa344b257 100644 --- a/cpu/efm32/periph/spi.c +++ b/cpu/efm32/periph/spi.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2014-2016 Freie Universität Berlin + * 2021-2023 Hugues Larrive * * 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 @@ -17,15 +18,17 @@ * @author Ryan Kurte * @author Bas Stottelaar * @author Christian Amsüss + * @author Hugues Larrive * @} */ #include #include "cpu.h" +#include "macros/math.h" +#include "mutex.h" #include "sched.h" #include "thread.h" -#include "mutex.h" #include "periph_conf.h" #include "periph/gpio.h" @@ -56,6 +59,69 @@ void spi_init_pins(spi_t bus) gpio_init(spi_config[bus].miso_pin, GPIO_IN_PD); } +spi_clk_t spi_get_clk(spi_t bus, uint32_t freq) +{ + (void)bus; + + /* bound divider to 65536 */ + if (freq < DIV_ROUND_UP(CLOCK_CORECLOCK, 65536)) { + return (spi_clk_t){ .err = -EDOM }; + } + + /* ================================================================= + * from USART_BaudrateSyncSet() of the gecko SDK + */ + uint32_t ref_freq, clkdiv; + + /* Prevent dividing by 0. */ + EFM_ASSERT(freq); + + /* + * CLKDIV in synchronous mode is given by: + * + * CLKDIV = 256 * (fHFPERCLK/(2 * br) - 1) + */ + + /* HFPERCLK/HFPERBCLK used to clock all USART/UART peripheral modules. */ +#if defined(_SILICON_LABS_32B_SERIES_2) + ref_freq = CMU_ClockFreqGet(cmuClock_PCLK); +#else +#if defined(_CMU_HFPERPRESCB_MASK) + if (dev == USART2) { + ref_freq = CMU_ClockFreqGet(cmuClock_HFPERB); + } + else { + ref_freq = CMU_ClockFreqGet(cmuClock_HFPER); + } +#else + ref_freq = CMU_ClockFreqGet(cmuClock_HFPER); +#endif +#endif + + clkdiv = (ref_freq - 1) / (2 * freq); + clkdiv = clkdiv << 8; + + /* Verify that resulting clock divider is within limits. */ + EFM_ASSERT(!(clkdiv & ~_USART_CLKDIV_MASK)); + + /* ================================================================= + * + * The manual say that `the clock division factor have a 15-bit + * integral part and a 5-bit fractional part` but only the integral + * part was used in the gecko SDK. + */ + + /* br = fHFPERCLK/(2 x (1 + USARTn_CLKDIV / 256)) */ + return (spi_clk_t){ .clk = DIV_ROUND_UP(ref_freq, (2 * (1 + (clkdiv >> 8)))) }; +} + +int32_t spi_get_freq(spi_t bus, spi_clk_t clk) +{ + (void)bus; + if (clk.err) { return -EINVAL; } + return clk.clk; +} + #define GET_PIN(x) (x & 0xf) #define GET_PORT(x) (x >> 4) #define USART_NUM(ref) ((ref == USART0) ? 0 : -1) @@ -64,6 +130,7 @@ void spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk) { (void)cs; assert((unsigned)bus < SPI_NUMOF); + if (clk.err) { return; } mutex_lock(&spi_lock[bus]); @@ -75,7 +142,7 @@ void spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk) USART_InitSync_TypeDef init = USART_INITSYNC_DEFAULT; - init.baudrate = (uint32_t)clk; + init.baudrate = (uint32_t)clk.clk; init.clockMode = (USART_ClockMode_TypeDef)mode; init.msbf = true; diff --git a/cpu/esp32/include/periph_cpu.h b/cpu/esp32/include/periph_cpu.h index fe2aaeea3139e..a7c068f26fb7f 100644 --- a/cpu/esp32/include/periph_cpu.h +++ b/cpu/esp32/include/periph_cpu.h @@ -680,20 +680,6 @@ typedef struct { */ #ifndef DOXYGEN -/** - * @brief Override SPI clock speed values - * @{ - */ -#define HAVE_SPI_CLK_T -typedef enum { - SPI_CLK_100KHZ = 100000, /**< drive the SPI bus with 100KHz */ - SPI_CLK_400KHZ = 400000, /**< drive the SPI bus with 400KHz */ - SPI_CLK_1MHZ = 1000000, /**< drive the SPI bus with 1MHz */ - SPI_CLK_5MHZ = 5000000, /**< drive the SPI bus with 5MHz */ - SPI_CLK_10MHZ = 10000000 /**< drive the SPI bus with 10MHz */ -} spi_clk_t; -/** @} */ - /** * @brief SPI pin getters * @{ diff --git a/cpu/esp32/periph/spi.c b/cpu/esp32/periph/spi.c index b2187824869d0..24ab40b189673 100644 --- a/cpu/esp32/periph/spi.c +++ b/cpu/esp32/periph/spi.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2022 Gunar Schorcht + * 2021-2023 Hugues Larrive * * 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 @@ -22,6 +23,7 @@ * - DMA transfer * * @author Gunar Schorcht + * @author Hugues Larrive * * @} */ @@ -34,8 +36,9 @@ #include "cpu.h" #include "gpio_arch.h" +#include "macros/math.h" #include "mutex.h" -#include "periph/spi.h" +/* periph/spi.h must be included after soc/rtc.h since it include macros/units.h */ #include "syscalls.h" #include "driver/periph_ctrl.h" @@ -43,12 +46,13 @@ #include "esp_rom_gpio.h" #include "hal/spi_hal.h" #include "hal/spi_types.h" -#include "soc/rtc.h" +#include "soc/rtc.h" /* define an unwanted MHZ macro */ #include "esp_idf_api/gpio.h" #undef MHZ -#include "macros/units.h" +/* periph/spi.h already include macros/units.h which define riot's MHZ macro */ +#include "periph/spi.h" #define ENABLE_DEBUG 0 #include "debug.h" @@ -63,8 +67,6 @@ struct _spi_bus_t { mutex_t lock; /* mutex for each SPI interface */ spi_host_device_t hostid; /* SPI hostid as used by ESP-IDF */ const spi_signal_conn_t *periph; /* SPI peripheral descriptor */ - spi_hal_timing_conf_t timing; /* calculated SPI timing parameters */ - spi_clk_t clk_last; /* SPI clock speed used last time in Hz */ uint8_t mode_last; /* SPI mode used last time */ bool pins_initialized; /* SPI pins initialized */ }; @@ -76,7 +78,6 @@ static struct _spi_bus_t _spi[] = { .lock = MUTEX_INIT_LOCKED, .hostid = spi_config[0].ctrl, .periph = &spi_periph_signal[spi_config[0].ctrl], - .clk_last = 0, .mode_last = UINT8_MAX, }, #endif @@ -86,7 +87,6 @@ static struct _spi_bus_t _spi[] = { .lock = MUTEX_INIT_LOCKED, .hostid = spi_config[1].ctrl, .periph = &spi_periph_signal[spi_config[1].ctrl], - .clk_last = 0, .mode_last = UINT8_MAX, }, #endif @@ -128,7 +128,7 @@ void IRAM_ATTR spi_init(spi_t bus) spi_ll_set_tx_lsbfirst(_spi[bus].periph->hw, false); /* acquire and release to set default parameters */ - spi_acquire(bus, GPIO_UNDEF, SPI_MODE_0, SPI_CLK_100KHZ); + spi_acquire(bus, GPIO_UNDEF, SPI_MODE_0, spi_get_clk(bus, SPI_CLK_100KHZ)); spi_release(bus); return; @@ -263,11 +263,61 @@ void spi_deinit_pins(spi_t bus) mutex_lock(&_spi[bus].lock); } +spi_clk_t IRAM_ATTR spi_get_clk(spi_t bus, uint32_t freq) +{ + (void)bus; + /* + * set SPI clock + * see ESP32 Technical Reference, Section 7.8 SPI_CLOCK_REG + * https://www.espressif.com/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf + */ + + uint32_t apb_clk = rtc_clk_apb_freq_get(); + uint32_t clk_reg; + + /* Section 7.4 GP­SPI Clock Control: + * f_spi = f_apb / ((spi_clkcnt_n + 1) * (cpi__clkdiv_pre + 1)) + * spi_clkdiv_pre 0..8191 (13 bit) + * spi_clkcnt_N 1..63 (6 bit) + */ + if (freq > apb_clk / 5) { + LOG_TAG_ERROR("spi", "APB clock rate (%"PRIu32" Hz) has to be at " + "least 5 times SPI clock rate (%"PRIu32" Hz)\n", + apb_clk, freq); + freq = apb_clk / 5; + } + else if (freq < DIV_ROUND_UP(apb_clk, (8192 * 64))) { + /* As apb_clk / (8192 * 64) result in very low frequency (153 Hz for + * an apb_clk frequency of 80 MHz), this should never happen... */ + return (spi_clk_t){ .err = -EDOM }; + } + + /* duty cycle is measured in is 1/256th, 50% = 128 */ + uint32_t _freq = spi_ll_master_cal_clock(apb_clk, freq, 128, &clk_reg); + + DEBUG("%s bus %d: SPI clock frequency: freq=%"PRIu32" eff=%"PRIu32" " + "reg=%08"PRIx32"\n", + __func__, bus, freq, _freq, clk_reg); + + return (spi_clk_t){ .clk = clk_reg }; +} + +int32_t IRAM_ATTR spi_get_freq(spi_t bus, spi_clk_t clk) +{ + (void)bus; + if (clk.err) { return -EINVAL; } + uint32_t apb_clk = rtc_clk_apb_freq_get(); + uint16_t spi_clkdiv_pre = (clk.clk >> 18) & 0x1fff; + uint8_t spi_clkcnt_N = (clk.clk >> 12) & 0x3f; + return (apb_clk / (spi_clkdiv_pre + 1) / (spi_clkcnt_N +1)); +} + void IRAM_ATTR spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk) { - DEBUG("%s bus=%u cs=%u mode=%u clk=%u\n", __func__, bus, cs, mode, clk); + DEBUG("%s bus=%u cs=%u mode=%u clk=%"PRIu32"\n", __func__, bus, cs, mode, clk.clk); assert(bus < SPI_NUMOF); + if (clk.err) { return; } /* if parameter cs is GPIO_UNDEF, the default CS pin is used */ cs = (cs == GPIO_UNDEF) ? spi_config[bus].cs : cs; @@ -299,39 +349,8 @@ void IRAM_ATTR spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t cl spi_ll_set_miso_delay(_spi[bus].periph->hw, delay_mode, 0); spi_ll_set_mosi_delay(_spi[bus].periph->hw, 0, 0); - /* - * set SPI clock - * see ESP32 Technical Reference, Section 7.8 SPI_CLOCK_REG - * https://www.espressif.com/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf - */ - - /* check whether timing has to be recalculated (time consuming) */ - if (clk != _spi[bus].clk_last) { - uint32_t apb_clk = rtc_clk_apb_freq_get(); - uint32_t clk_reg; - - if (apb_clk / 5 < clk) { - LOG_TAG_ERROR("spi", "APB clock rate (%"PRIu32" Hz) has to be at " - "least 5 times SPI clock rate (%d Hz)\n", - apb_clk, clk); - assert(false); - } - - /* duty cycle is measured in is 1/256th, 50% = 128 */ - int _clk = spi_ll_master_cal_clock(apb_clk, clk, - 128, &clk_reg); - - _spi[bus].clk_last = clk; - _spi[bus].timing.clock_reg = clk_reg; - _spi[bus].timing.timing_miso_delay = 0; - _spi[bus].timing.timing_dummy = 0; - - DEBUG("%s bus %d: SPI clock frequency: clk=%d eff=%d " - "reg=%08"PRIx32"\n", - __func__, bus, clk, _clk, clk_reg); - } - spi_ll_master_set_clock_by_reg(_spi[bus].periph->hw, - &_spi[bus].timing.clock_reg); + /* set SPI clock */ + spi_ll_master_set_clock_by_reg(_spi[bus].periph->hw, &clk.clk); #if defined(CPU_FAM_ESP32C3) || defined(CPU_FAM_ESP32S3) /* @@ -354,7 +373,6 @@ void IRAM_ATTR spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t cl #else #error Platform implementation is missing #endif - } void IRAM_ATTR spi_release(spi_t bus) diff --git a/cpu/esp8266/periph/spi.c b/cpu/esp8266/periph/spi.c index 4782593e93101..f1b61188c8b0c 100644 --- a/cpu/esp8266/periph/spi.c +++ b/cpu/esp8266/periph/spi.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2022 Gunar Schorcht + * 2021-2023 Hugues Larrive * * 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 @@ -15,6 +16,7 @@ * @brief Low-level SPI driver implementation for ESP8266 * * @author Gunar Schorcht + * @author Hugues Larrive * * @} */ @@ -26,9 +28,10 @@ #include "log.h" #include "cpu.h" +#include "macros/math.h" +#include "macros/units.h" #include "mutex.h" #include "periph/spi.h" -#include "macros/units.h" #include "esp_attr.h" #include "gpio_arch.h" @@ -44,6 +47,16 @@ #define SPI_BLOCK_SIZE 64 /* number of bytes per SPI transfer */ +/* baud_rate = 80MHz / (spi_clkdiv_pre + 1) / (spi_clkcnt_N + 1) */ +#if CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ == 2 +#define SPI_CLK_SRC_FREQ 2000000 +#elif CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ == 40 +#define SPI_CLK_SRC_FREQ 40000000 +#else +#define SPI_CLK_SRC_FREQ 80000000 +#endif +#define CONST_SPI_CLKCNT_N 1 + /** structure which describes all properties of one SPI bus */ struct _spi_bus_t { spi_dev_t* regs; /* pointer to register data struct of the SPI device */ @@ -133,7 +146,7 @@ static void IRAM_ATTR _spi_init_internal(spi_t bus) _spi[bus].regs->ctrl.fastrd_mode = 0; /* acquire and release to set default parameters */ - spi_acquire(bus, GPIO_UNDEF, SPI_MODE_0, SPI_CLK_1MHZ); + spi_acquire(bus, GPIO_UNDEF, SPI_MODE_0, spi_get_clk(bus, SPI_CLK_1MHZ)); spi_release(bus); } @@ -221,11 +234,67 @@ int spi_init_cs(spi_t bus, spi_cs_t cs) return SPI_OK; } +spi_clk_t IRAM_ATTR spi_get_clk(spi_t bus, uint32_t freq) +{ + (void)bus; + /* set SPI clock + * see ESP8266 Technical Reference Appendix 2 - SPI registers + * https://www.espressif.com/sites/default/files/documentation/esp8266-technical_reference_en.pdf + * + * spi_clk_equ_sysclk In the master mode: + * 1: spi_clk is equal to 80MHz, + * 0: spi_clk is divided from 80 MHz clock. + * spi_clkdiv_pre 0..8191 (13 bit) + * spi_clkcnt_N 1..63 (6 bit) + * baud_rate = 80MHz / (spi_clkdiv_pre + 1) / (spi_clkcnt_N + 1) + * 80 MHz / 8192 / 2 = 4882 Hz so we can use a constant spi_clkcnt_N of 1. + */ + + uint32_t source_clock = SPI_CLK_SRC_FREQ / (CONST_SPI_CLKCNT_N + 1); + spi_dev_t spi_regs; + + /* bound divider to 8192 */ + if (freq < DIV_ROUND_UP(source_clock, 8192)) { + return (spi_clk_t){ .err = -EDOM }; + } + + if (freq >= SPI_CLK_SRC_FREQ) { + spi_regs.clock.clk_equ_sysclk = 1; + } + else { + spi_regs.clock.clkcnt_l = CONST_SPI_CLKCNT_N; + spi_regs.clock.clkcnt_h = (CONST_SPI_CLKCNT_N + 1) / 2 - 1; + spi_regs.clock.clkcnt_n = CONST_SPI_CLKCNT_N; + spi_regs.clock.clkdiv_pre = DIV_ROUND_UP(source_clock, freq) - 1; + } + + return (spi_clk_t){ .clk = spi_regs.clock.val }; +} + +int32_t IRAM_ATTR spi_get_freq(spi_t bus, spi_clk_t clk) +{ + (void)bus; + if (clk.err) { return -EINVAL; } + spi_dev_t spi_regs; + spi_regs.clock.val = clk.clk; + if (spi_regs.clock.clk_equ_sysclk) { + return SPI_CLK_SRC_FREQ; + } + else { + /* baud_rate = sysclk / (spi_clkdiv_pre + 1) / (spi_clkcnt_N + 1) */ + uint32_t sysclk = SPI_CLK_SRC_FREQ; + uint32_t clkdiv_pre = spi_regs.clock.clkdiv_pre; + uint32_t clkcnt_n = spi_regs.clock.clkcnt_n; + return (sysclk / (clkdiv_pre + 1) / (clkcnt_n + 1)); + } +} + void IRAM_ATTR spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk) { - DEBUG("%s bus=%u cs=%u mode=%u clk=%u\n", __func__, bus, cs, mode, clk); + DEBUG("%s bus=%u cs=%u mode=%u clk=%x\n", __func__, bus, cs, mode, clk.clk); assert(bus < SPI_NUMOF); + if (clk.err) { return; } /* call initialization of the SPI interface if it is not initialized yet */ if (!_spi[bus].initialized) { @@ -257,51 +326,11 @@ void IRAM_ATTR spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t cl _spi[bus].regs->ctrl2.mosi_delay_mode = 0; _spi[bus].regs->ctrl2.mosi_delay_num = 0; - /* set SPI clock - * see ESP8266 Technical Reference Appendix 2 - SPI registers - * https://www.espressif.com/sites/default/files/documentation/esp8266-technical_reference_en.pdf - */ - - uint32_t spi_clkdiv_pre; - uint32_t spi_clkcnt_N; - - switch (clk) { - case SPI_CLK_10MHZ: spi_clkdiv_pre = 2; /* predivides 80 MHz to 40 MHz */ - spi_clkcnt_N = 4; /* 4 cycles results into 10 MHz */ - break; - case SPI_CLK_5MHZ: spi_clkdiv_pre = 2; /* predivides 80 MHz to 40 MHz */ - spi_clkcnt_N = 8; /* 8 cycles results into 5 MHz */ - break; - case SPI_CLK_1MHZ: spi_clkdiv_pre = 2; /* predivides 80 MHz to 40 MHz */ - spi_clkcnt_N = 40; /* 40 cycles results into 1 MHz */ - break; - case SPI_CLK_400KHZ: spi_clkdiv_pre = 20; /* predivides 80 MHz to 4 MHz */ - spi_clkcnt_N = 10; /* 10 cycles results into 400 kHz */ - break; - case SPI_CLK_100KHZ: spi_clkdiv_pre = 20; /* predivides 80 MHz to 4 MHz */ - spi_clkcnt_N = 40; /* 20 cycles results into 100 kHz */ - break; - default: spi_clkdiv_pre = 20; /* predivides 80 MHz to 4 MHz */ - spi_clkcnt_N = 40; /* 20 cycles results into 100 kHz */ + /* set SPI clock configuration */ + if (!(clk.clk & 0x80000000)) { + IOMUX.CONF &= ~IOMUX_CONF_SPI1_CLOCK_EQU_SYS_CLOCK; } - - /* register values are set to deviders-1 */ - spi_clkdiv_pre--; - spi_clkcnt_N--; - - DEBUG("%s spi_clkdiv_prev=%u spi_clkcnt_N=%u\n", - __func__, spi_clkdiv_pre, spi_clkcnt_N); - - IOMUX.CONF &= ~IOMUX_CONF_SPI1_CLOCK_EQU_SYS_CLOCK; - - /* SPI clock is derived from APB clock by dividers */ - _spi[bus].regs->clock.clk_equ_sysclk = 0; - - /* set SPI clock dividers */ - _spi[bus].regs->clock.clkdiv_pre = spi_clkdiv_pre; - _spi[bus].regs->clock.clkcnt_n = spi_clkcnt_N; - _spi[bus].regs->clock.clkcnt_h = (spi_clkcnt_N+1)/2-1; - _spi[bus].regs->clock.clkcnt_l = spi_clkcnt_N; + _spi[bus].regs->clock.val = (uint32_t)clk.clk; DEBUG("%s bus %d: SPI_CLOCK_REG=%08x\n", __func__, bus, _spi[bus].regs->clock.val); diff --git a/cpu/fe310/periph/spi.c b/cpu/fe310/periph/spi.c index f79c6b3346c43..301e2e42f86e8 100644 --- a/cpu/fe310/periph/spi.c +++ b/cpu/fe310/periph/spi.c @@ -1,6 +1,7 @@ /* * Copyright (C) 2019 Tristan Bruns * 2019 Inria + * 2021-2023 Hugues Larrive * * 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 @@ -18,6 +19,7 @@ * * @author Tristan Bruns * @author Alexandre Abadie + * @author Hugues Larrive * * @} */ @@ -26,6 +28,7 @@ #include "clk.h" #include "cpu.h" +#include "macros/math.h" #include "mutex.h" #include "periph/spi.h" @@ -34,21 +37,6 @@ #define ENABLE_DEBUG 0 #include "debug.h" -#define SPI_CLK_NUMOF ARRAY_SIZE(_spi_clks) - -/* DIV_UP is division which rounds up instead of down */ -#define SPI_DIV_UP(a, b) (((a) + ((b) - 1)) / (b)) - -static const uint32_t _spi_clks[] = { - 100000, - 400000, - 1000000, - 5000000, - 10000000, -}; - -static uint32_t _spi_clks_config[SPI_CLK_NUMOF] = { 0 }; - /** * @brief Allocation device locks */ @@ -62,10 +50,6 @@ void spi_init(spi_t dev) /* initialize the buses lock */ mutex_init(&lock); - for (uint8_t i = 0; i < SPI_CLK_NUMOF; ++i) { - _spi_clks_config[i] = SPI_DIV_UP(coreclk(), 2 * _spi_clks[i]) - 1; - } - /* trigger pin initialization */ spi_init_pins(dev); @@ -104,14 +88,40 @@ int spi_init_cs(spi_t dev, spi_cs_t cs) return SPI_OK; } +spi_clk_t spi_get_clk(spi_t bus, uint32_t freq) +{ + (void)bus; + /* fsck = fin / (2 * (div + 1)) + * div 0..4095 (12 bit) + * + * div = fin / 2 / fsck - 1 + */ + + uint32_t source_clock = coreclk() / 2; + + /* bound divider to 4096 */ + if (freq < DIV_ROUND_UP(source_clock, 4096)) { + return (spi_clk_t){ .err = -EDOM }; + } + return (spi_clk_t){ .clk = DIV_ROUND_UP(source_clock, freq) - 1 }; +} + +int32_t spi_get_freq(spi_t bus, spi_clk_t clk) +{ + (void)bus; + if (clk.err) { return -EINVAL; } + return coreclk() / (2 * (clk.clk + 1)); +} + void spi_acquire(spi_t dev, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk) { (void)cs; assert(dev < SPI_NUMOF); + if (clk.err) { return; } mutex_lock(&lock); - _REG32(spi_config[dev].addr, SPI_REG_SCKDIV) = _spi_clks_config[clk]; + _REG32(spi_config[dev].addr, SPI_REG_SCKDIV) = clk.clk; _REG32(spi_config[dev].addr, SPI_REG_SCKMODE) = mode; } diff --git a/cpu/gd32v/include/periph_cpu.h b/cpu/gd32v/include/periph_cpu.h index 17431ea199682..b1da6f35b9499 100644 --- a/cpu/gd32v/include/periph_cpu.h +++ b/cpu/gd32v/include/periph_cpu.h @@ -321,25 +321,6 @@ typedef uint32_t spi_cs_t; #define PERIPH_SPI_NEEDS_TRANSFER_REGS /** @} */ -/** - * @brief Override SPI clock speed values - * @{ - */ -#define HAVE_SPI_CLK_T -enum { - SPI_CLK_100KHZ = KHZ(100), /**< drive the SPI bus with 100KHz */ - SPI_CLK_400KHZ = KHZ(400), /**< drive the SPI bus with 400KHz */ - SPI_CLK_1MHZ = MHZ(1), /**< drive the SPI bus with 1MHz */ - SPI_CLK_5MHZ = MHZ(5), /**< drive the SPI bus with 5MHz */ - SPI_CLK_10MHZ = MHZ(10), /**< drive the SPI bus with 10MHz */ -}; - -/** - * @brief SPI clock type - */ -typedef uint32_t spi_clk_t; -/** @} */ - /** * @brief Structure for SPI configuration data */ diff --git a/cpu/gd32v/periph/spi.c b/cpu/gd32v/periph/spi.c index 95689f27107dd..cb8497581f439 100644 --- a/cpu/gd32v/periph/spi.c +++ b/cpu/gd32v/periph/spi.c @@ -3,6 +3,7 @@ * 2014-2017 Freie Universität Berlin * 2016-2017 OTA keys S.A. * 2023 Gunar Schorcht + * 2023 Hugues Larrive * * 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 @@ -25,6 +26,7 @@ * @author Joakim Nohlgård * @author Thomas Eichinger * @author Gunar Schorcht + * @author Hugues Larrive * * @} */ @@ -54,50 +56,12 @@ */ static mutex_t locks[SPI_NUMOF]; -/** - * @brief Clock configuration cache - */ -static uint32_t clocks[SPI_NUMOF]; - -/** - * @brief Clock divider cache - */ -static uint8_t dividers[SPI_NUMOF]; static inline SPI_Type *dev(spi_t bus) { return spi_config[bus].dev; } -/** - * @brief Multiplier for clock divider calculations - * - * Makes the divider calculation fixed point - */ -#define SPI_APB_CLOCK_SHIFT (4U) -#define SPI_APB_CLOCK_MULT (1U << SPI_APB_CLOCK_SHIFT) - -static uint8_t _get_clkdiv(const spi_conf_t *conf, uint32_t clock) -{ - uint32_t bus_clock = periph_apb_clk(conf->apbbus); - /* Shift bus_clock with SPI_APB_CLOCK_SHIFT to create a fixed point int */ - uint32_t div = (bus_clock << SPI_APB_CLOCK_SHIFT) / (2 * clock); - DEBUG("[spi] clock: divider: %"PRIu32"\n", div); - /* Test if the divider is 2 or smaller, keeping the fixed point in mind */ - if (div <= SPI_APB_CLOCK_MULT) { - return 0; - } - /* determine MSB and compensate back for the fixed point int shift */ - uint8_t rounded_div = bitarithm_msb(div) - SPI_APB_CLOCK_SHIFT; - /* Determine if rounded_div is not a power of 2 */ - if ((div & (div - 1)) != 0) { - /* increment by 1 to ensure that the clock speed at most the - * requested clock speed */ - rounded_div++; - } - return rounded_div > BR_MAX ? BR_MAX : rounded_div; -} - void spi_init(spi_t bus) { assert(bus < SPI_NUMOF); @@ -179,9 +143,32 @@ int spi_init_with_gpio_mode(spi_t bus, const spi_gpio_mode_t* mode) } #endif +spi_clk_t spi_get_clk(spi_t bus, uint32_t freq) +{ + /* freq = f_pclk / (1 << (BR + 1)) + * BR = 0..7 */ + uint8_t br = 0; + while (periph_apb_clk(spi_config[bus].apbbus) / (1 << (br + 1)) > freq) { + br++; + } + /* bound divider to 256 */ + if (br > BR_MAX) { + return (spi_clk_t){ .err = -EDOM }; + } + return (spi_clk_t){ .clk = (br << BR_SHIFT) }; +} + +int32_t spi_get_freq(spi_t bus, spi_clk_t clk) +{ + if (clk.err) { return -EINVAL; } + return periph_apb_clk(spi_config[bus].apbbus) + / (1 << ((clk.clk >> BR_SHIFT) + 1)); +} + void spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk) { assert((unsigned)bus < SPI_NUMOF); + if (clk.err) { return; } /* lock bus */ mutex_lock(&locks[bus]); @@ -192,19 +179,7 @@ void spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk) /* enable SPI device clock */ periph_clk_en(spi_config[bus].apbbus, spi_config[bus].rcumask); /* enable device */ - if (clk != clocks[bus]) { - dividers[bus] = _get_clkdiv(&spi_config[bus], clk); - clocks[bus] = clk; - } - uint8_t br = dividers[bus]; - - DEBUG("[spi] acquire: requested clock: %"PRIu32", resulting clock: %"PRIu32 - " BR divider: %u\n", - clk, - periph_apb_clk(spi_config[bus].apbbus)/(1 << (br + 1)), - br); - - uint16_t ctl0_settings = ((br << BR_SHIFT) | mode | SPI0_CTL0_MSTMOD_Msk); + uint16_t ctl0_settings = clk.clk | mode | SPI0_CTL0_MSTMOD_Msk; /* Settings to add to CTL1 in addition to SPI_CTL1_SETTINGS */ uint16_t ctl1_extra_settings = 0; if (cs != SPI_HWCS_MASK) { diff --git a/cpu/kinetis/dist/calc_spi_scalers/Makefile b/cpu/kinetis/dist/calc_spi_scalers/Makefile deleted file mode 100644 index 1ada319e2da48..0000000000000 --- a/cpu/kinetis/dist/calc_spi_scalers/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -NAME = calc_spi_scalers -CC = gcc -CFLAGS = -std=c99 -Wall -SRC = $(wildcard *.c) - -.PHONY: all clean - -all: - $(CC) $(CFLAGS) -o $(NAME) $(SRC) - -clean: - rm -f $(NAME) diff --git a/cpu/kinetis/dist/calc_spi_scalers/calc_spi_scalers.c b/cpu/kinetis/dist/calc_spi_scalers/calc_spi_scalers.c deleted file mode 100644 index 2b1c19883209e..0000000000000 --- a/cpu/kinetis/dist/calc_spi_scalers/calc_spi_scalers.c +++ /dev/null @@ -1,242 +0,0 @@ -/* - * Copyright (C) 2014 Hamburg University of Applied Sciences - * 2014 PHYTEC Messtechnik GmbH - * 2015 Eistec AB - * 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. - */ - -/** - * @brief SPI bus scaler computation - * - * This helper tool calculates the needed SPI scaler values for a given APB bus - * clock speed. The result of the computation must be placed in a board's - * periph_conf.h for quick reference by the SPI drivers. - * - * @author Peter Kietzmann - * @author Johann Fischer - * @author Joakim Nohlgård - * @author Hauke Petersen - * - * @} - */ - -#include -#include -#include - -#define ARRAY_SIZE(a) (sizeof((a)) / sizeof((a)[0])) - -/** - * @brief Targeted SPI bus speed values (pre-defined by RIOT) - */ -static uint32_t targets[] = { 100000, 400000, 1000000, 5000000, 10000000 }; - -/** - * @brief Helper function for finding optimal baud rate scalers. - * - * Find the prescaler and scaler settings that will yield a clock frequency - * as close as possible (but not above) the target frequency, given the module - * runs at module_clock Hz. - * - * Hardware properties (Baud rate configuration): - * Possible prescalers: 2, 3, 5, 7 - * Possible scalers: 2, 4, 6 (sic!), 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768 - * - * SCK baud rate = (f_BUS/PBR) x [(1+DBR)/BR] - * - * where PBR is the prescaler, BR is the scaler, DBR is the Double BaudRate bit. - * - * @note We are not using the DBR bit because it may affect the SCK duty cycle. - * - * @param module_clock Module clock frequency (e.g. F_BUS) - * @param target_clock Desired baud rate - * @param closest_prescaler pointer where to write the optimal prescaler index. - * @param closest_scaler pointer where to write the optimal scaler index. - * - * @return The actual achieved frequency on success - * @return Less than 0 on error. - */ - -static long find_closest_baudrate_scalers(const uint32_t module_clock, const long target_clock, - uint8_t *closest_prescaler, uint8_t *closest_scaler) -{ - uint8_t i; - uint8_t k; - long freq; - static const uint8_t num_scalers = 16; - static const uint8_t num_prescalers = 4; - static const int br_scalers[16] = { - 2, 4, 6, 8, 16, 32, 64, 128, - 256, 512, 1024, 2048, 4096, 8192, 16384, 32768 - }; - static const int br_prescalers[4] = {2, 3, 5, 7}; - - long closest_frequency = -1; - - /* Test all combinations until we arrive close to the target clock */ - for (i = 0; i < num_prescalers; ++i) { - for (k = 0; k < num_scalers; ++k) { - freq = module_clock / (br_scalers[k] * br_prescalers[i]); - - if (freq <= target_clock) { - /* Found closest lower frequency at this prescaler setting, - * compare to the best result */ - if (closest_frequency < freq) { - closest_frequency = freq; - *closest_scaler = k; - *closest_prescaler = i; - } - - break; - } - } - } - - if (closest_frequency < 0) { - /* Error, no solution found, this line is never reachable with current - * hardware settings unless a _very_ low target clock is requested. - * (scaler_max * prescaler_max) = 229376 => target_min@100MHz = 435 Hz*/ - return -1; - } - - return closest_frequency; -} - -/** - * @brief Helper function for finding optimal delay scalers. - * - * Find the prescaler and scaler settings that will yield a delay timing - * as close as possible (but not shorter than) the target delay, given the - * module runs at module_clock Hz. - * - * Hardware properties (delay configuration): - * Possible prescalers: 1, 3, 5, 7 - * Possible scalers: 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536 - * - * delay = (1/f_BUS) x prescaler x scaler - * - * Because we want to do this using only integers, the target_freq parameter is - * the reciprocal of the delay time. - * - * @param module_clock Module clock frequency (e.g. F_BUS) - * @param target_freq Reciprocal (i.e. 1/t [Hz], frequency) of the desired delay time. - * @param closest_prescaler pointer where to write the optimal prescaler index. - * @param closest_scaler pointer where to write the optimal scaler index. - * - * @return The actual achieved frequency on success - * @return Less than 0 on error. - */ -static long find_closest_delay_scalers(const uint32_t module_clock, const long target_freq, - uint8_t *closest_prescaler, uint8_t *closest_scaler) -{ - uint8_t i; - uint8_t k; - long freq; - int prescaler; - int scaler; - static const uint8_t num_scalers = 16; - static const uint8_t num_prescalers = 4; - - long closest_frequency = -1; - - /* Test all combinations until we arrive close to the target clock */ - for (i = 0; i < num_prescalers; ++i) { - for (k = 0; k < num_scalers; ++k) { - prescaler = (i * 2) + 1; - scaler = (1 << (k + 1)); /* 2^(k+1) */ - freq = module_clock / (prescaler * scaler); - - if (freq <= target_freq) { - /* Found closest lower frequency at this prescaler setting, - * compare to the best result */ - if (closest_frequency < freq) { - closest_frequency = freq; - *closest_scaler = k; - *closest_prescaler = i; - } - - break; - } - } - } - - if (closest_frequency < 0) { - /* Error, no solution found, this line is never reachable with current - * hardware settings unless a _very_ low target clock is requested. - * (scaler_max * prescaler_max) = 458752 */ - return -1; - } - - return closest_frequency; -} - -int main(int argc, char **argv) -{ - uint32_t modclk; - int i; - - if (argc != 2) { - printf("usage: %s \n", argv[0]); - return 1; - } - - modclk = atoi(argv[1]); - if (modclk == 0) { - printf("error: invalid input value\n"); - return 1; - } - - printf("\nCalculating SPI clock scalers for a module clock of: %iHz\n\n", - (int)modclk); - - puts("static const uint32_t spi_clk_config[] = {"); - - for (i = 0; i < ARRAY_SIZE(targets); i++) { - uint8_t tmp, ptmp; - long res; - /* bus clock */ - res = find_closest_baudrate_scalers(modclk, targets[i], &ptmp, &tmp); - if (res < 0) { - puts("error: no applicable bus clock scalers could be found!"); - return 1; - } - puts(" ("); - printf(" SPI_CTAR_PBR(%i) | SPI_CTAR_BR(%i) | /* -> %iHz */\n", - (int)ptmp, (int)tmp, (int)res); - - /* t_csc: chip select to fist clock signal delay */ - if (find_closest_delay_scalers(modclk, targets[i], &ptmp, &tmp) < 0) { - puts("error: no applicable delay values for t_csc found\n"); - return 1; - } - printf(" SPI_CTAR_PCSSCK(%i) | SPI_CTAR_CSSCK(%i) |\n", (int)ptmp, (int)tmp); - - /* t_asc: delay after last clock signal to release of chip select */ - if (find_closest_delay_scalers(modclk, targets[i], &ptmp, &tmp) < 0) { - puts("error: no applicable delay values for t_asc found\n"); - return 1; - } - printf(" SPI_CTAR_PASC(%i) | SPI_CTAR_ASC(%i) |\n", (int)ptmp, (int)tmp); - - /* t_psc: delay between release and next assertion of chip select */ - if (find_closest_delay_scalers(modclk, targets[i], &ptmp, &tmp) < 0) { - puts("error: no applicable delay values for t_csc found\n"); - return 1; - } - printf(" SPI_CTAR_PDT(%i) | SPI_CTAR_DT(%i)\n", (int)ptmp, (int)tmp); - - if (i == ARRAY_SIZE(targets) - 1) { - puts(" )"); - } - else { - puts(" ),"); - } - } - puts("};"); - - return 0; -} diff --git a/cpu/kinetis/doc.txt b/cpu/kinetis/doc.txt index 343443684c3d7..412a96f394428 100644 --- a/cpu/kinetis/doc.txt +++ b/cpu/kinetis/doc.txt @@ -135,14 +135,15 @@ Optional settings to configure internal load capacitors (see reference manual): The SPI baud rate and other timings are generated from the bus clock via prescalers, the hardware module allows for very detailed timing configuration, -but a tool exists to generate a standard timing configuration for any given -module clock frequency. The timing configuration tool is found in -cpu/kinetis/dist/calc_spi_scalers +and a standard timing configuration is generated by the spi_get_clk() function +for any given frequency. Finer tuning of timings than the RIOT SPI API is capable of is supported by -modifying the generated configuration. See the reference manual for your -Kinetis CPU (Chapter: "SPI module, Functional description, Module baud rate and -clock delay generation") for a description of each delay. +modifying the generated configuration (spi_clk_t) which correspond to the +PBR, BR, PCSSCK, CSSCK, PASC, ASC, PDT, DT bit-fields of the CTAR register. See +the reference manual for your Kinetis CPU (Chapter: "SPI module, Functional +description, Module baud rate and clock delay generation") for a description of +each delay. The SPI driver supports using GPIO pins for chip select, as an alternative to using hardware chip select. The pins specified in spi_config[x].pin_cs[y] are @@ -158,10 +159,6 @@ because of the additional overhead of calling gpio_set/clear at every transfer. ### SPI configuration example (for periph_conf.h): ### - static const uint32_t spi_clk_config[] = { - - Use cpu/kinetis/dist/calc_spi_scalers to generate the timing configuration - - }; - static const spi_conf_t spi_config[] = { { .dev = SPI0, diff --git a/cpu/kinetis/periph/spi.c b/cpu/kinetis/periph/spi.c index 193277f01d3f4..71b9910c2c22b 100644 --- a/cpu/kinetis/periph/spi.c +++ b/cpu/kinetis/periph/spi.c @@ -3,6 +3,7 @@ * 2014 PHYTEC Messtechnik GmbH * 2015 Eistec AB * 2016 Freie Universität Berlin + * 2021-2023 Hugues Larrive * * 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 @@ -22,6 +23,7 @@ * @author Johann Fischer * @author Joakim Nohlgård * @author Hauke Petersen + * @author Hugues Larrive * * @} */ @@ -29,9 +31,11 @@ #include #include "cpu.h" +#include "macros/math.h" #include "mutex.h" #include "periph/gpio.h" #include "periph/spi.h" +#include "bitarithm.h" #define ENABLE_DEBUG 0 #include "debug.h" @@ -53,7 +57,7 @@ static inline SPI_Type *dev(spi_t bus) static inline void poweron(spi_t bus) { - switch((uint32_t)dev(bus)) { + switch ((uint32_t)dev(bus)) { case (uint32_t)SPI0: case (uint32_t)SPI1: SIM->SCGC6 |= (spi_config[bus].simmask); @@ -74,7 +78,7 @@ static inline void poweroff(spi_t bus) /* Disable the module */ dev(bus)->MCR |= SPI_MCR_MDIS_MASK; - switch((uint32_t)dev(bus)) { + switch ((uint32_t)dev(bus)) { case (uint32_t)SPI0: case (uint32_t)SPI1: SIM->SCGC6 &= ~(spi_config[bus].simmask); @@ -120,7 +124,7 @@ void spi_init_pins(spi_t bus) { gpio_init_port(spi_config[bus].pin_miso, spi_config[bus].pcr); gpio_init_port(spi_config[bus].pin_mosi, spi_config[bus].pcr); - gpio_init_port(spi_config[bus].pin_clk , spi_config[bus].pcr); + gpio_init_port(spi_config[bus].pin_clk, spi_config[bus].pcr); } int spi_init_cs(spi_t bus, spi_cs_t cs) @@ -145,10 +149,164 @@ int spi_init_cs(spi_t bus, spi_cs_t cs) return SPI_OK; } +spi_clk_t spi_get_clk(spi_t bus, uint32_t freq) +{ + (void)bus; + /* Baud rate: + * + * Possible prescalers: 2, 3, 5, 7 + * Possible scalers: 2, 4, 6, 8, 16, 32, 64, 128, 256, 512, 1024, + * 2048, 4096, 8192, 16384, 32768 + * + * SCK baud rate = CLOCK_BUSCLOCK / (PBR * BR) + * + * Delays: + * + * Possible prescalers: 1, 3, 5, 7 + * Possible scalers: 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, + * 4096, 8192, 16384, 32768, 65536 + * + * tCSC = (1 / CLOCK_BUSCLOCK) * PCSSCK * CSSCK + * tASC = (1 / CLOCK_BUSCLOCK) * PASC * ASC + * tDT = (1 / CLOCK_BUSCLOCK) * PDT * DT + */ + + const uint32_t target_divider = DIV_ROUND_UP(CLOCK_BUSCLOCK, freq); + uint32_t divider = 4, scaler = 2; + uint8_t prescaler = 2, pbr = 0, br, pdt, dt; + + /* bound divider to 229376 */ + if (freq < DIV_ROUND_UP(CLOCK_BUSCLOCK, 229376)) { + return (spi_clk_t){ .err = -EDOM }; + } + + /* Baudrate scalers + * + * Sorting out dividers: + * previous previous + * prescaler prescaler scaler divider + * 7 -> 2 * (0.5 * 4) = 2 * 2 = 4 + * 2 -> 5 * (2 / 2) = 5 * 1 = 5 scaler underflow + * 5 -> 3 * (1 * 2) = 3 * 2 = 6 + * 3 -> 7 * (2 / 2) = 7 * 1 = 7 scaler underflow + * 7 -> 2 * (1 * 4) = 2 * 4 = 8 + * 2 -> 5 * (4 / 2) = 5 * 2 = 10 + * 5 -> 3 * (2 * 2) = 3 * 4 = 12 + * 3 -> 7 * (4 / 2) = 7 * 2 = 14 + * 7 -> 2 * (2 * 4) = 2 * 8 = 16 + * first exception for scaler 6: 3 * 6 = 18 + * 2 -> 5 * (8 / 2) = 5 * 4 = 20 + * 5 -> 3 * (4 * 2) = 3 * 8 = 24 + * 3 -> 7 * (8 / 2) = 7 * 4 = 28 + * second exception for scaler 6: 5 * 6 = 30 + * 7 -> 2 * (4 * 4) = 2 * 16 = 32 + * 2 -> 5 * (16 / 2) = 5 * 8 = 40 + * third exception for scaler 6: 7 * 6 = 42 + * 5 -> 3 * (8 * 2) = 3 * 16 = 48 + * from now on, the scaler will never go back below 8 + * prescaler 3 -> 7 scaler /= 2 + * prescaler 7 -> 2 scaler *= 4 + * prescaler 2 -> 5 scaler /= 2 + * prescaler 5 -> 3 scaler *= 2 + * | | + * 3 -> 7 * (32768 / 2) = 7 * 16384 = 114688 + * 7 -> 2 * (16384 * 4) = 2 * 65536 = 131072 scaler overflow + * 2 -> 5 * (65536 / 2) = 5 * 32768 = 163840 + * 5 -> 3 * (32768 * 2) = 3 * 65536 = 196608 scaler overflow + * 3 -> 7 * (65536 / 2) = 7 * 32768 = 229376 + */ + while (divider < target_divider) { + switch (divider) { + /* do not underflow the scaler */ + case 4: prescaler = 3; break; + case 6: prescaler = 2; scaler = 4; break; + /* first exception for the scaler 6 */ + case 16: prescaler = 3; scaler = 6; break; + case 18: prescaler = 5; scaler = 4; break; + /* second exception for the scaler 6 */ + case 28: prescaler = 5; scaler = 6; break; + case 30: prescaler = 2; scaler = 16; break; + /* third exception for the scaler 6 */ + case 40: prescaler = 7; scaler = 6; break; + case 42: prescaler = 3; scaler = 16; break; + /* do not overflow the scaler */ + case 114688: prescaler = 5; scaler = 32768; break; + case 163840: prescaler = 7; scaler = 32768; break; + /* default progress */ + default: switch (prescaler) { + case 2: prescaler = 5; scaler /= 2; break; + case 5: prescaler = 3; scaler *= 2; break; + case 3: prescaler = 7; scaler /= 2; break; + case 7: prescaler = 2; scaler *= 4; break; + } + break; + } + divider = prescaler * scaler; + } + /* convert prescaler to pbr */ + switch (prescaler) { + case 2: pbr = 0; break; + case 3: pbr = 1; break; + case 5: pbr = 2; break; + case 7: pbr = 3; break; + } + /* convert scaler to br */ + br = scaler < 6 ? bitarithm_msb(scaler) - 1 : bitarithm_msb(scaler); + + /* Delay scalers + * + * We will use delays of at least one clock period. + * + * The delay scalers computation is practically the same as the + * baudrate scalers so we will reuse the previous results. + * + * There is the scaler 65536 but we will not handle it as this would + * lead into delays less than one clock period. + */ + switch (divider) { + /* handle the 3 exceptions (there is not the scaler 6) */ + case 18: pdt = 2; dt = 1; break; /* -> 20 */ + case 30: pdt = 0; dt = 4; break; /* -> 32 */ + case 42: pdt = 1; dt = 3; break; /* -> 48 */ + /* add 1 to br when pbr is 0 (the first prescaler is 1 instead of 2) */ + default: pdt = pbr; dt = pbr ? br : br + 1; break; + } + if (br > 2) { + /* there is not the scaler 6 */ + --dt; + } + + return (spi_clk_t){ + .clk = SPI_CTAR_PBR(pbr) | SPI_CTAR_BR(br) | + SPI_CTAR_PCSSCK(pdt) | SPI_CTAR_CSSCK(dt) | + SPI_CTAR_PASC(pdt) | SPI_CTAR_ASC(dt) | + SPI_CTAR_PDT(pdt) | SPI_CTAR_DT(dt) + }; +} + +int32_t spi_get_freq(spi_t bus, spi_clk_t clk) +{ + (void)bus; + if (clk.err) { return -EINVAL; } + + static const int br_scalers[16] = { + 2, 4, 6, 8, 16, 32, 64, 128, + 256, 512, 1024, 2048, 4096, 8192, 16384, 32768 + }; + static const int br_prescalers[4] = {2, 3, 5, 7}; + + int pbr = br_prescalers[clk.clk >> 16 & 0x3]; + int br = br_scalers[clk.clk & 0xf]; + + return CLOCK_BUSCLOCK / (pbr * br); +} + void spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk) { (void)cs; assert((unsigned)bus < SPI_NUMOF); + if (clk.err) { return; } + /* lock and power on the bus */ mutex_lock(&locks[bus]); poweron(bus); @@ -157,7 +315,7 @@ void spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk) dev(bus)->MCR &= ~(SPI_MCR_HALT_MASK | SPI_MCR_MDIS_MASK); /* configure clock and mode */ - dev(bus)->CTAR[0] = (mode | SPI_CTAR_FMSZ(7) | spi_clk_config[clk]); + dev(bus)->CTAR[0] = (mode | SPI_CTAR_FMSZ(7) | clk.clk); } void spi_release(spi_t bus) diff --git a/cpu/lm4f120/include/periph_cpu.h b/cpu/lm4f120/include/periph_cpu.h index 9d39286d8ba01..6da14ce49b9b7 100644 --- a/cpu/lm4f120/include/periph_cpu.h +++ b/cpu/lm4f120/include/periph_cpu.h @@ -1,6 +1,7 @@ /* * Copyright (C) 2015 Rakendra Thapa - * Copyright (C) 2017 Marc Poulhiès + * 2017 Marc Poulhiès + * 2021-2023 Hugues Larrive * * 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 @@ -16,6 +17,7 @@ * * @author Rakendra Thapa * @author Marc Poulhiès + * @author Hugues Larrive */ #ifndef PERIPH_CPU_H @@ -154,21 +156,6 @@ typedef struct { /** @} */ #ifndef DOXYGEN -/** - * @brief Override SPI clock speed values - * @{ - */ -#define HAVE_SPI_CLK_T 1 -typedef enum { - SPI_CLK_100KHZ = 100000, /**< drive the SPI bus with 100KHz */ - SPI_CLK_400KHZ = 400000, /**< drive the SPI bus with 400KHz */ - SPI_CLK_1MHZ = 1000000, /**< drive the SPI bus with 1MHz */ - SPI_CLK_4MHZ = 4000000, /**< drive the SPI bus with 4MHz */ - SPI_CLK_5MHZ = 5000000, /**< drive the SPI bus with 5MHz */ - SPI_CLK_10MHZ = 10000000, /**< drive the SPI bus with 10MHz */ -} spi_clk_t; -/** @} */ - /** * @brief Override SPI mode settings * @{ diff --git a/cpu/lm4f120/periph/spi.c b/cpu/lm4f120/periph/spi.c index 319c896c183ae..3001ae8cce240 100644 --- a/cpu/lm4f120/periph/spi.c +++ b/cpu/lm4f120/periph/spi.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2017 Marc Poulhiès + * 2021-2023 Hugues Larrive * * 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 @@ -14,6 +15,7 @@ * @brief Low-level SPI driver implementation * * @author Marc Poulhiès + * @author Hugues Larrive * * @} */ @@ -21,6 +23,7 @@ #include #include "cpu.h" +#include "macros/math.h" #include "mutex.h" #include "periph/gpio.h" #include "periph/spi.h" @@ -67,12 +70,51 @@ void spi_init_pins(spi_t bus) ROM_GPIOPinTypeSSI(spi_confs[bus].gpio_port, spi_confs[bus].pins.mask); } +spi_clk_t spi_get_clk(spi_t bus, uint32_t freq) +{ + (void)bus; + /* SSIClk = SysClk / (CPSDVSR * (1 + SCR)), with + * CPSDVSR = 2..254 and even, + * SCR = 0..255 */ + + uint32_t sys_ctl_clock = ROM_SysCtlClockGet(); + + /* bound divider to 65024 (254 * (1 + 255)) */ + if (freq >= DIV_ROUND_UP(sys_ctl_clock, 65024)) { + return (spi_clk_t){ .err = -EDOM }; + } + + uint8_t cpsdvsr = 2, scr = 0; + uint32_t divider = DIV_ROUND_UP(sys_ctl_clock, freq); + if (divider % 2) { + divider++; + } + while (divider / cpsdvsr > 256) { + cpsdvsr += 2; + } + scr = divider / cpsdvsr - 1; + + return (spi_clk_t){ + .clk = DIV_ROUND_UP(sys_ctl_clock, (cpsdvsr * (1 + scr))) + }; +} + +int32_t spi_get_freq(spi_t bus, spi_clk_t clk) +{ + (void)bus; + if (clk.err) { return -EINVAL; } + return clk.clk; +} + void spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk) { (void)cs; assert((unsigned)bus < SPI_NUMOF); + if (clk.err) { return; } + /* lock bus */ mutex_lock(&locks[bus]); + /* enable clock for SSI */ ROM_SysCtlPeripheralEnable(spi_confs[bus].ssi_sysctl); @@ -80,7 +122,7 @@ void spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk) ROM_SSIConfigSetExpClk(spi_confs[bus].ssi_base, ROM_SysCtlClockGet(), mode, SSI_MODE_MASTER, - clk, + clk.clk, 8); ROM_SSIEnable(spi_confs[bus].ssi_base); diff --git a/cpu/lpc23xx/include/periph_cpu.h b/cpu/lpc23xx/include/periph_cpu.h index bb2429ed23d31..a54bf113bae1b 100644 --- a/cpu/lpc23xx/include/periph_cpu.h +++ b/cpu/lpc23xx/include/periph_cpu.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2015 Kaspar Schleiser + * 2021-2023 Hugues Larrive * * 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 @@ -14,6 +15,7 @@ * @brief CPU specific definitions for internal peripheral handling * * @author Kaspar Schleiser + * @author Hugues Larrive */ #ifndef PERIPH_CPU_H @@ -146,19 +148,16 @@ typedef struct { #ifndef DOXYGEN /** - * @brief Override SPI clock speed values + * @brief Override SPI clock configuration * @{ */ #define HAVE_SPI_CLK_T -typedef enum { - SPI_CLK_100KHZ = 100, /**< drive the SPI bus with 100KHz */ - SPI_CLK_400KHZ = 400, /**< drive the SPI bus with 400KHz */ - SPI_CLK_1MHZ = 1000, /**< drive the SPI bus with 1MHz */ - SPI_CLK_5MHZ = 5000, /**< drive the SPI bus with 5MHz */ - SPI_CLK_10MHZ = 10000 /**< drive the SPI bus with 10MHz */ +typedef struct { + int err; + uint32_t pclksel; + uint32_t cpsr; } spi_clk_t; -/** @} */ -#endif /* ndef DOXYGEN */ +#endif /* ifndef DOXYGEN */ /** * @brief DAC configuration, valid for all boards using this CPU diff --git a/cpu/lpc23xx/periph/spi.c b/cpu/lpc23xx/periph/spi.c index 4cf79e6a97e6c..f703b4bfd1cdf 100644 --- a/cpu/lpc23xx/periph/spi.c +++ b/cpu/lpc23xx/periph/spi.c @@ -1,6 +1,7 @@ /* * Copyright (C) 2015 Kaspar Schleiser * 2016 Freie Universität Berlin + * 2021-2023 Hugues Larrive * * 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 @@ -22,13 +23,16 @@ * * @author Kaspar Schleiser * @author Hauke Petersen + * @author Hugues Larrive * * @} */ +#include + #include "cpu.h" +#include "macros/math.h" #include "mutex.h" -#include "assert.h" #include "periph/spi.h" #define ENABLE_DEBUG 0 @@ -95,14 +99,50 @@ void spi_init_pins(spi_t bus) *(&PINSEL0 + cfg->pinsel_clk) |= cfg->pinsel_msk_clk; } +spi_clk_t spi_get_clk(spi_t bus, uint32_t freq) +{ + (void)bus; + + /* CLOCK_CORECLOCK / (1 << 0..3) / 2..254 and even */ + + uint32_t pclksel, cpsr, source_clock = CLOCK_CORECLOCK / 2; + + /* bound divider to 2032 */ + if (freq >= DIV_ROUND_UP(source_clock, 2032)) { + return (spi_clk_t){ .err = -EDOM }; + } + + /* result must be at most the requested frequency */ + freq = CLOCK_CORECLOCK / DIV_ROUND_UP(CLOCK_CORECLOCK, freq); + + lpc23xx_pclk_scale(CLOCK_CORECLOCK, freq, &pclksel, &cpsr); + + return (spi_clk_t){ .pclksel = pclksel, .cpsr = cpsr }; +} + +int32_t spi_get_freq(spi_t bus, spi_clk_t clk) +{ + (void)bus; + if (clk.err) { return -EINVAL; } + + uint32_t pclkdiv; + + switch (clk.pclksel) { + case 0: pclkdiv = 4; break; + case 1: pclkdiv = 1; break; + case 2: pclkdiv = 2; break; + case 3: pclkdiv = 8; break; + default: pclkdiv = 4; break; + } + return CLOCK_CORECLOCK / pclkdiv / clk.cpsr; +} + void spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk) { (void)cs; (void)mode; assert((unsigned)bus < SPI_NUMOF); assert(mode == SPI_MODE_0); - - uint32_t pclksel; - uint32_t cpsr; + if (clk.err) { return; } lpc23xx_spi_t *dev = get_dev(bus); @@ -116,20 +156,17 @@ void spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk) dev->CR0 = 7; /* configure bus clock */ - lpc23xx_pclk_scale(CLOCK_CORECLOCK / 1000, (uint32_t)clk, &pclksel, &cpsr); - switch ((uint32_t)dev) { case SSP0_BASE_ADDR: PCLKSEL1 &= ~(BIT10 | BIT11); /* CCLK to PCLK divider*/ - PCLKSEL1 |= pclksel << 10; + PCLKSEL1 |= clk.pclksel << 10; break; case SSP1_BASE_ADDR: PCLKSEL0 &= ~(BIT20 | BIT21); /* CCLK to PCLK divider*/ - PCLKSEL0 |= pclksel << 20; + PCLKSEL0 |= clk.pclksel << 20; break; } - - dev->CPSR = cpsr; + dev->CPSR = clk.cpsr; /* enable the bus */ dev->CR1 |= BIT1; diff --git a/cpu/msp430fxyz/include/periph_cpu.h b/cpu/msp430fxyz/include/periph_cpu.h index d00643c433aa9..a9897ba524d84 100644 --- a/cpu/msp430fxyz/include/periph_cpu.h +++ b/cpu/msp430fxyz/include/periph_cpu.h @@ -88,20 +88,6 @@ typedef enum { } spi_mode_t; #endif /** @} */ - -/** - * @brief Override SPI clock speed selection values - * @{ - */ -#define HAVE_SPI_CLK_T -typedef enum { - SPI_CLK_100KHZ = 100000, /**< 100KHz */ - SPI_CLK_400KHZ = 400000, /**< 400KHz */ - SPI_CLK_1MHZ = 1000000, /**< 1MHz */ - SPI_CLK_5MHZ = 5000000, /**< 5MHz */ - SPI_CLK_10MHZ = 0, /**< not supported */ -} spi_clk_t; -/** @} */ #endif /* ndef DOXYGEN */ /** diff --git a/cpu/msp430fxyz/periph/spi.c b/cpu/msp430fxyz/periph/spi.c index 648dabb6ed14e..f7639f1b0096d 100644 --- a/cpu/msp430fxyz/periph/spi.c +++ b/cpu/msp430fxyz/periph/spi.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2015-2016 Freie Universität Berlin + * 2021-2023 Hugues Larrive * * 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 @@ -19,6 +20,7 @@ * devices - one used as UART and one as SPI. * * @author Hauke Petersen + * @author Hugues Larrive * * @} */ @@ -26,6 +28,7 @@ #include #include "cpu.h" +#include "macros/math.h" #include "mutex.h" #include "periph/spi.h" @@ -66,32 +69,63 @@ void spi_init_pins(spi_t bus) gpio_periph_mode(SPI_PIN_CLK, true); } +spi_clk_t spi_get_clk(spi_t bus, uint32_t freq) +{ + (void)bus; + +#ifndef SPI_USE_USCI + /* USART: Baud rate = BRCLK / UxBR (User's quide section 19.2.5) + * The maximum baud rate that can be generated in master mode is BRCLK/2 */ + /* SMCLK / 2..65535 */ + /* bound divider from 2 to 65535 */ + if (freq > msp430_fxyz_submain_clock_freq() / 2) { + freq = msp430_fxyz_submain_clock_freq() / 2; + } +#else + /* USCI: FBitClock = fBRCLK / UCBRx (User's quide section 16.3.6), + * The maximum bit clock that can be generated in master mode is BRCLK. */ + /* SMCLK / 1..65535 */ + /* bound divider from 1 to 65535 */ + if (freq > msp430_fxyz_submain_clock_freq()) { + freq = msp430_fxyz_submain_clock_freq(); + } +#endif + else if (freq < DIV_ROUND_UP(msp430_fxyz_submain_clock_freq(), 65535)) { + return (spi_clk_t){ .err = -EDOM }; + } + return (spi_clk_t){ + .clk = DIV_ROUND_UP(msp430_fxyz_submain_clock_freq(), freq) + }; +} + +int32_t spi_get_freq(spi_t bus, spi_clk_t clk) +{ + (void)bus; + if (clk.err) { return -EINVAL; } + return msp430_fxyz_submain_clock_freq() / clk.clk; +} + void spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk) { (void)bus; (void)cs; assert((unsigned)bus < SPI_NUMOF); - assert(clk != SPI_CLK_10MHZ); + if (clk.err) { return; } /* lock the bus */ mutex_lock(&spi_lock); - /* calculate baudrate */ - uint32_t br = msp430_fxyz_submain_clock_freq() / clk; - /* make sure the is not smaller then 2 */ - if (br < 2) { - br = 2; - } - SPI_BASE->BR0 = (uint8_t)br; - SPI_BASE->BR1 = (uint8_t)(br >> 8); + /* set baudrate */ + SPI_BASE->BR0 = (uint8_t)clk.clk; + SPI_BASE->BR1 = (uint8_t)(clk.clk >> 8); /* configure bus mode */ -#ifndef SPI_USE_USCI +#ifndef SPI_USE_USCI /* USART */ /* configure mode */ SPI_BASE->TCTL = (USART_TCTL_SSEL_SMCLK | USART_TCTL_STC | mode); /* release from software reset */ SPI_BASE->CTL &= ~(USART_CTL_SWRST); -#else +#else /* USCI */ /* configure mode */ SPI_BASE->CTL0 = (USCI_SPI_CTL0_UCSYNC | USCI_SPI_CTL0_MST| USCI_SPI_CTL0_MODE_0 | USCI_SPI_CTL0_MSB | mode); diff --git a/cpu/native/include/periph_cpu.h b/cpu/native/include/periph_cpu.h index 668c877aee067..90a71bfa0061c 100644 --- a/cpu/native/include/periph_cpu.h +++ b/cpu/native/include/periph_cpu.h @@ -135,28 +135,6 @@ typedef enum { * @brief Use the common `transfer_regs` SPI function */ #define PERIPH_SPI_NEEDS_TRANSFER_REGS - -#ifndef DOXYGEN -/** - * @brief Use a custom clock speed type - */ -#define HAVE_SPI_CLK_T -/** - * @brief SPI clock speed values - * - * The Linux userspace driver takes values in Hertz, which values are available - * can only be determined at runtime. - * @{ - */ -typedef enum { - SPI_CLK_100KHZ = (100000U), - SPI_CLK_400KHZ = (400000U), - SPI_CLK_1MHZ = (1000000U), - SPI_CLK_5MHZ = (5000000U), - SPI_CLK_10MHZ = (10000000U) -} spi_clk_t; -/** @} */ -#endif /* ndef DOXYGEN */ #endif /* MODULE_PERIPH_SPI | DOXYGEN */ /** diff --git a/cpu/native/periph/spidev_linux.c b/cpu/native/periph/spidev_linux.c index 7cb94cd28ca46..6c315980cd364 100644 --- a/cpu/native/periph/spidev_linux.c +++ b/cpu/native/periph/spidev_linux.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2019 Frank Hessel + * 2021-2023 Hugues Larrive * * 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 @@ -15,6 +16,7 @@ * @brief Implementation of SPI access from Linux User Space * * @author Frank Hessel + * @author Hugues Larrive * @} */ @@ -145,9 +147,27 @@ void spidev_linux_teardown(void) } } +spi_clk_t spi_get_clk(spi_t bus, uint32_t freq) +{ + (void)bus; + /* The Linux userspace driver takes values in Hertz, which values + * are available can only be determined at runtime so we let the + * value pass through. */ + return (spi_clk_t){ .clk = freq }; +} + +int32_t spi_get_freq(spi_t bus, spi_clk_t clk) +{ + (void)bus; + /* From https://www.kernel.org/doc/html/v5.15-rc1/spi/spidev.html + * There’s currently no way to report the actual bit rate used to + * shift data to/from a given device. */ + return clk.clk; +} + void spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk) { - DEBUG("spi_acquire(%u, %u, 0x%02x, %d)\n", bus, cs, mode, clk); + DEBUG("spi_acquire(%u, %u, 0x%02x, %d)\n", bus, cs, mode, clk.clk); assert((unsigned)bus < SPI_NUMOF); mutex_lock(&(device_state[bus].lock)); @@ -262,7 +282,7 @@ void spi_release(spi_t bus) static int spi_set_params(int fd, bool hwcs, spi_mode_t mode, spi_clk_t clk) { uint8_t spi_mode = mode | (hwcs ? 0 : SPI_NO_CS); - uint32_t ioctl_clk = clk; + uint32_t ioctl_clk = clk.clk; if (real_ioctl(fd, SPI_IOC_WR_MODE, &spi_mode) < 0) { return SPI_NOMODE; diff --git a/cpu/nrf51/periph/spi.c b/cpu/nrf51/periph/spi.c index c3efd1e99d6b3..7658b67e2a146 100644 --- a/cpu/nrf51/periph/spi.c +++ b/cpu/nrf51/periph/spi.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2014-2016 Freie Universität Berlin + * 2021-2023 Hugues Larrive * * 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 @@ -17,6 +18,7 @@ * @author Hauke Petersen * @author Frank Holtz * @author Jan Wagner + * @author Hugues Larrive * * @} */ @@ -60,17 +62,63 @@ void spi_init_pins(spi_t bus) SPI_MISOSEL = spi_config[bus].miso; } +spi_clk_t spi_get_clk(spi_t bus, uint32_t freq) +{ + (void)bus; + + if (freq >= MHZ(8)) { + return (spi_clk_t){ .clk = SPI_FREQUENCY_FREQUENCY_M8 }; + } + if (freq >= MHZ(4)) { + return (spi_clk_t){ .clk = SPI_FREQUENCY_FREQUENCY_M4 }; + } + if (freq >= MHZ(2)) { + return (spi_clk_t){ .clk = SPI_FREQUENCY_FREQUENCY_M2 }; + } + if (freq >= MHZ(1)) { + return (spi_clk_t){ .clk = SPI_FREQUENCY_FREQUENCY_M1 }; + } + if (freq >= 500000) { + return (spi_clk_t){ .clk = SPI_FREQUENCY_FREQUENCY_K500 }; + } + if (freq >= 250000) { + return (spi_clk_t){ .clk = SPI_FREQUENCY_FREQUENCY_K250 }; + } + if (freq >= 125000) { + return (spi_clk_t){ .clk = SPI_FREQUENCY_FREQUENCY_K125 }; + } + return (spi_clk_t){ .err = -EDOM }; +} + +int32_t spi_get_freq(spi_t bus, spi_clk_t clk) +{ + (void)bus; + if (clk.err) { return -EINVAL; } + + switch (clk.clk) { + case SPI_FREQUENCY_FREQUENCY_K125: return 125000; + case SPI_FREQUENCY_FREQUENCY_K250: return 250000; + case SPI_FREQUENCY_FREQUENCY_K500: return 500000; + case SPI_FREQUENCY_FREQUENCY_M1: return MHZ(1); + case SPI_FREQUENCY_FREQUENCY_M2: return MHZ(2); + case SPI_FREQUENCY_FREQUENCY_M4: return MHZ(4); + case SPI_FREQUENCY_FREQUENCY_M8: return MHZ(8); + default: return -EINVAL; + } +} + void spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk) { (void)cs; assert((unsigned)bus < SPI_NUMOF); + if (clk.err) { return; } mutex_lock(&locks[bus]); /* power on the bus (NRF51 only) */ dev(bus)->POWER = 1; /* configure bus */ dev(bus)->CONFIG = mode; - dev(bus)->FREQUENCY = clk; + dev(bus)->FREQUENCY = clk.clk; /* enable the bus */ dev(bus)->ENABLE = 1; } diff --git a/cpu/nrf5x_common/include/periph_cpu_common.h b/cpu/nrf5x_common/include/periph_cpu_common.h index e2c5a81df70a8..f9e31ab21d3c9 100644 --- a/cpu/nrf5x_common/include/periph_cpu_common.h +++ b/cpu/nrf5x_common/include/periph_cpu_common.h @@ -218,20 +218,6 @@ typedef enum { SPI_MODE_3 = (SPI_CONFIG_CPOL_Msk | SPI_CONFIG_CPHA_Msk) /**< CPOL=1, CPHA=1 */ } spi_mode_t; /** @} */ - -/** - * @brief Override SPI clock values - * @{ - */ -#define HAVE_SPI_CLK_T -typedef enum { - SPI_CLK_100KHZ = SPI_FREQUENCY_FREQUENCY_K125, /**< 100KHz */ - SPI_CLK_400KHZ = SPI_FREQUENCY_FREQUENCY_K500, /**< 400KHz */ - SPI_CLK_1MHZ = SPI_FREQUENCY_FREQUENCY_M1, /**< 1MHz */ - SPI_CLK_5MHZ = SPI_FREQUENCY_FREQUENCY_M4, /**< 5MHz */ - SPI_CLK_10MHZ = SPI_FREQUENCY_FREQUENCY_M8 /**< 10MHz */ -} spi_clk_t; -/** @} */ #endif /* ndef CPU_FAM_NRF9160 */ #endif /* ndef DOXYGEN */ diff --git a/cpu/nrf5x_common/periph/spi_nrf52_nrf9160.c b/cpu/nrf5x_common/periph/spi_nrf52_nrf9160.c index e14e8d706245d..788c6efc5ff99 100644 --- a/cpu/nrf5x_common/periph/spi_nrf52_nrf9160.c +++ b/cpu/nrf5x_common/periph/spi_nrf52_nrf9160.c @@ -1,7 +1,8 @@ /* * Copyright (C) 2014-2016 Freie Universität Berlin - * Copyright (C) 2020 Inria - * Copyright (C) 2020 Koen Zandberg + * 2020 Inria + * 2020 Koen Zandberg + * 2021-2023 Hugues Larrive * * 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 @@ -20,6 +21,7 @@ * @author Frank Holtz * @author Jan Wagner * @author Koen Zandberg + * @author Hugues Larrive * * @} */ @@ -181,10 +183,53 @@ void spi_init_pins(spi_t bus) spi_twi_irq_register_spi(dev(bus), spi_isr_handler, (void *)(uintptr_t)bus); } +spi_clk_t spi_get_clk(spi_t bus, uint32_t freq) +{ + (void)bus; + + if (freq >= MHZ(8)) { + return (spi_clk_t){ .clk = SPIM_FREQUENCY_FREQUENCY_M8 }; + } + else if (freq >= MHZ(4)) { + return (spi_clk_t){ .clk = SPIM_FREQUENCY_FREQUENCY_M4 }; + } + else if (freq >= MHZ(2)) { + return (spi_clk_t){ .clk = SPIM_FREQUENCY_FREQUENCY_M2 }; + } + else if (freq >= MHZ(1)) { + return (spi_clk_t){ .clk = SPIM_FREQUENCY_FREQUENCY_M1 }; + } + else if (freq >= 500000) { + return (spi_clk_t){ .clk = SPIM_FREQUENCY_FREQUENCY_K500 }; + } + else if (freq >= 250000) { + return (spi_clk_t){ .clk = SPIM_FREQUENCY_FREQUENCY_K250 }; + } + return (spi_clk_t){ .err = -EDOM }; +} + +int32_t spi_get_freq(spi_t bus, spi_clk_t clk) +{ + (void)bus; + if (clk.err) { return -EINVAL; } + + switch (clk.clk) { + case SPIM_FREQUENCY_FREQUENCY_K125: return 125000; + case SPIM_FREQUENCY_FREQUENCY_K250: return 250000; + case SPIM_FREQUENCY_FREQUENCY_K500: return 500000; + case SPIM_FREQUENCY_FREQUENCY_M1: return MHZ(1); + case SPIM_FREQUENCY_FREQUENCY_M2: return MHZ(2); + case SPIM_FREQUENCY_FREQUENCY_M4: return MHZ(4); + case SPIM_FREQUENCY_FREQUENCY_M8: return MHZ(8); + default: return -EINVAL; + } +} + void spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk) { (void)cs; assert((unsigned)bus < SPI_NUMOF); + if (clk.err) { return; } if (IS_USED(MODULE_PERIPH_SPI_RECONFIGURE)) { mutex_lock(&locks[bus]); @@ -195,7 +240,7 @@ void spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk) /* configure bus */ dev(bus)->CONFIG = mode; - dev(bus)->FREQUENCY = clk; + dev(bus)->FREQUENCY = clk.clk; /* enable the bus */ dev(bus)->ENABLE = SPIM_ENABLE_ENABLE_Enabled; } diff --git a/cpu/nrf9160/include/periph_cpu.h b/cpu/nrf9160/include/periph_cpu.h index f5fbbb72fe5cb..903dd7a45c257 100644 --- a/cpu/nrf9160/include/periph_cpu.h +++ b/cpu/nrf9160/include/periph_cpu.h @@ -167,19 +167,6 @@ typedef enum { } spi_mode_t; /** @} */ -/** - * @brief Override SPI clock values - * @{ - */ -#define HAVE_SPI_CLK_T -typedef enum { - SPI_CLK_100KHZ = SPIM_FREQUENCY_FREQUENCY_K125, /**< 100KHz */ - SPI_CLK_400KHZ = SPIM_FREQUENCY_FREQUENCY_K500, /**< 400KHz */ - SPI_CLK_1MHZ = SPIM_FREQUENCY_FREQUENCY_M1, /**< 1MHz */ - SPI_CLK_5MHZ = SPIM_FREQUENCY_FREQUENCY_M4, /**< 5MHz */ - SPI_CLK_10MHZ = SPIM_FREQUENCY_FREQUENCY_M8 /**< 10MHz */ -} spi_clk_t; -/** @} */ #endif /* ndef DOXYGEN */ /** diff --git a/cpu/qn908x/include/periph_cpu.h b/cpu/qn908x/include/periph_cpu.h index e7854b36dd353..40a3581d555cb 100644 --- a/cpu/qn908x/include/periph_cpu.h +++ b/cpu/qn908x/include/periph_cpu.h @@ -431,25 +431,6 @@ typedef enum { } spi_mode_t; /** @} */ -/** - * @name Override SPI speed values - * - * The speed is configured at run time based on the AHB clock speed using an - * arbitrary divider between /1 and /65536. The standard macro values just map - * to the frequency in Hz. The maximum possible speed is 32 MHz assuming a - * core clock and AHB bus clock of 32 MHz. - * @{ - */ -#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; -/** @} */ - /** * @brief SPI pin getters * @{ diff --git a/cpu/qn908x/periph/spi.c b/cpu/qn908x/periph/spi.c index ff4f114212a30..c53d90f8a1eea 100644 --- a/cpu/qn908x/periph/spi.c +++ b/cpu/qn908x/periph/spi.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2020 iosabi + * 2021-2023 Hugues Larrive * * 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 @@ -16,6 +17,7 @@ * @brief Low-level SPI driver implementation * * @author iosabi + * @author Hugues Larrive * * @} */ @@ -23,6 +25,7 @@ #include #include "bitarithm.h" +#include "macros/math.h" #include "mutex.h" #include "cpu.h" @@ -76,30 +79,6 @@ static const uint32_t _spi_func5_mask_fc2 = (1u << 4) | /* FC2_COPI */ (1u << 5); /* FC2_CIPO */ -/** - * @brief Set the clock divided for the target frequency. - */ -static void _spi_controller_set_speed(SPI_Type *spi_bus, uint32_t speed_hz) -{ - /* The SPI clock source is based on the FLEXCOMM clock with a simple - * frequency divider between /1 and /65536. */ - const uint32_t bus_freq = CLOCK_GetFreq(kCLOCK_BusClk); - uint32_t divider = (bus_freq + speed_hz / 2) / speed_hz; - - if (divider == 0) { - divider = 1; - } - else if (divider > (1u << 16)) { - divider = 1u << 16; - } - DEBUG("[spi] clock requested: %" PRIu32 " Hz, actual: %" PRIu32 - " Hz, divider: /%" PRIu32 "\n", speed_hz, bus_freq / divider, - divider); - /* The value stored in DIV is always (divider - 1), meaning that a value of - * 0 divides by 1. */ - spi_bus->DIV = divider - 1; -} - void spi_init(spi_t bus) { assert(bus < SPI_NUMOF); @@ -188,17 +167,45 @@ void spi_deinit_pins(spi_t bus) } #endif /* MODULE_PERIPH_SPI_RECONFIGURE */ +spi_clk_t spi_get_clk(spi_t bus, uint32_t freq) +{ + (void)bus; + /* The SPI clock source is based on the FLEXCOMM clock with a simple + * frequency divider between /1 and /65536. */ + const uint32_t bus_freq = CLOCK_GetFreq(kCLOCK_BusClk); + + /* bound divider to 65536 */ + if (freq < DIV_ROUND_UP(bus_freq, 65536)) { + return (spi_clk_t){ .err = -EDOM }; + } + + uint32_t divider = DIV_ROUND_UP(bus_freq, freq); + + /* The value stored in DIV is always (divider - 1), meaning that a value of + * 0 divides by 1. */ + return (spi_clk_t){ .clk = (uint16_t)(divider - 1) }; +} + +int32_t spi_get_freq(spi_t bus, spi_clk_t clk) +{ + (void)bus; + if (clk.err) { return -EINVAL; } + return CLOCK_GetFreq(kCLOCK_BusClk) / (clk.clk + 1); +} + void spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk) { assert((unsigned)bus < SPI_NUMOF); assert((mode & ~(SPI_CFG_CPHA_MASK | SPI_CFG_CPOL_MASK)) == 0); + if (clk.err) { return; } + const spi_conf_t *const conf = &spi_config[bus]; mutex_lock(&locks[bus]); /* Set SPI clock speed. This silently chooses the closest frequency, no * matter how far it is from the requested one. */ - _spi_controller_set_speed(conf->dev, clk); + conf->dev->DIV = clk.clk; DEBUG("[spi] acquire: mode CPHA=%d CPOL=%d, cs=0x%" PRIx32 "\n", !!(mode & SPI_CFG_CPHA_MASK), !!(mode & SPI_CFG_CPOL_MASK), diff --git a/cpu/rpx0xx/include/periph_cpu.h b/cpu/rpx0xx/include/periph_cpu.h index a977e0eedad53..51b1582a46798 100644 --- a/cpu/rpx0xx/include/periph_cpu.h +++ b/cpu/rpx0xx/include/periph_cpu.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2021 Otto-von-Guericke-Universität Magdeburg + * 2023 Hugues Larrive * * 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 @@ -15,6 +16,7 @@ * * @author Fabian Hüßler * @author Marian Buschsieweke + * @author Hugues Larrive */ #ifndef PERIPH_CPU_H @@ -751,24 +753,19 @@ void rosc_stop(void); /** @} */ +#ifndef DOXYGEN /** - * @brief Override SPI clock speed values + * @brief Override SPI clock configuration * @{ */ #define HAVE_SPI_CLK_T -enum { - SPI_CLK_100KHZ = KHZ(100), /**< drive the SPI bus with 100KHz */ - SPI_CLK_400KHZ = KHZ(400), /**< drive the SPI bus with 400KHz */ - SPI_CLK_1MHZ = MHZ(1), /**< drive the SPI bus with 1MHz */ - SPI_CLK_5MHZ = MHZ(5), /**< drive the SPI bus with 5MHz */ - SPI_CLK_10MHZ = MHZ(10), /**< drive the SPI bus with 10MHz */ -}; - -/** - * @brief SPI clock type - */ -typedef uint32_t spi_clk_t; +typedef struct { + int err; + uint8_t cpsdvsr; + uint8_t scr; +} spi_clk_t; /** @} */ +#endif /* ndef DOXYGEN */ /** * @brief Configuration details for an SPI interface needed by the RPX0XX peripheral diff --git a/cpu/rpx0xx/periph/spi.c b/cpu/rpx0xx/periph/spi.c index da174a9185fd0..c5f347248d4de 100644 --- a/cpu/rpx0xx/periph/spi.c +++ b/cpu/rpx0xx/periph/spi.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2023 Frank Engelhardt + * 2023 Hugues Larrive * * 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 @@ -14,6 +15,7 @@ * @brief Implementation of SPI. * * @author Frank Engelhardt + * @author Hugues Larrive * * @} */ @@ -21,6 +23,7 @@ #include "assert.h" #include "bitarithm.h" #include "cpu.h" +#include "macros/math.h" #include "mutex.h" #include "periph/gpio.h" #include "periph/spi.h" @@ -33,16 +36,6 @@ */ static mutex_t locks[SPI_NUMOF]; -/** - * @brief Save the clock prescaler values for faster bus acquisition. - */ -typedef struct { - spi_clk_t clk; - uint8_t cpsdvsr; - uint8_t scr; -} _pl022_clk_t; -static _pl022_clk_t pl022_clk[SPI_NUMOF]; - gpio_t spi_pin_clk(spi_t spi) { assert((unsigned)spi < SPI_NUMOF); @@ -90,8 +83,6 @@ void spi_init(spi_t spi) mutex_init(&locks[spi]); /* trigger pin initialization */ spi_init_pins(spi); - /* clock prescaler values must be calculated */ - pl022_clk[spi].clk = 0xff; } void spi_init_pins(spi_t spi) @@ -215,9 +206,15 @@ static inline void _check_best_clk_result(uint16_t target_dvsr, } } -static void _calc_pl022_clk(_pl022_clk_t *pl022_clk, spi_clk_t clk) +spi_clk_t spi_get_clk(spi_t bus, uint32_t freq) { - uint16_t dvsr = CLOCK_PERIPH / clk; + (void)bus; + /* bound dividerto 65024 (254 * 256) */ + if (freq < DIV_ROUND_UP(CLOCK_PERIPH, 65024)) { + return (spi_clk_t){ .err = -EDOM }; + } + + uint16_t dvsr = DIV_ROUND_UP(CLOCK_PERIPH, freq); /* The divisor must be split into two 8-bit divisors, cpsdvsr and scr, * dvsr = cpsdvsr*scr. cpsdvsr must be an even number greater than 0. */ uint8_t cpsdvsr = 2, best_cpsdvsr = 2; @@ -243,26 +240,36 @@ static void _calc_pl022_clk(_pl022_clk_t *pl022_clk, spi_clk_t clk) &best_scr, &best_cpsdvsr); } } - uint32_t resulting_clk_hz = CLOCK_PERIPH / (best_cpsdvsr * best_scr); - - pl022_clk->clk = clk; - pl022_clk->cpsdvsr = best_cpsdvsr; /* For scr, +1 is added internally. */ - pl022_clk->scr = (best_scr - 1); + best_scr--; + + uint32_t resulting_clk_hz = CLOCK_PERIPH / (best_cpsdvsr * (best_scr + 1)); DEBUG("[rpx0xx] Values for spi clock divider registers CPSDVSR=%" PRIu16 ", SCR=%" PRIu16 ". Resulting clock is %" PRIu32 " Hz.\n", - best_cpsdvsr, best_scr - 1, resulting_clk_hz); + best_cpsdvsr, best_scr, resulting_clk_hz); + + return (spi_clk_t){ + .cpsdvsr = best_cpsdvsr, + .scr = best_scr + }; +} + +int32_t spi_get_freq(spi_t bus, spi_clk_t clk) +{ + (void)bus; + if (clk.err) { return -EINVAL; } + return CLOCK_PERIPH / (clk.cpsdvsr * clk.scr); } void spi_acquire(spi_t spi, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk) { DEBUG("[rpx0xx] Call spi_acquire(spi=%" PRIuFAST8 ", cs=%" PRIu32 - ", mode=%" PRIu16 ", clk=%" PRIu32 ")\n", spi, cs, mode, clk); + ", mode=%" PRIu16 ", clk.cpsdvsr=%" PRIu8 ", clk.scr=%" PRIu8 ")\n", + spi, cs, mode, clk.cpsdvsr, clk.scr); - assert((unsigned)spi < SPI_NUMOF); - assert(clk == SPI_CLK_100KHZ || clk == SPI_CLK_10MHZ || clk == SPI_CLK_1MHZ - || clk == SPI_CLK_400KHZ || clk == SPI_CLK_5MHZ); (void)cs; + assert((unsigned)spi < SPI_NUMOF); + if (clk.err) { return; } /* lock bus */ mutex_lock(&locks[spi]); @@ -298,14 +305,11 @@ void spi_acquire(spi_t spi, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk) } /* set clock speed */ - if (clk != pl022_clk[spi].clk) { - _calc_pl022_clk(&pl022_clk[spi], clk); - } io_reg_write_dont_corrupt(&dev->SSPCPSR, - pl022_clk[spi].cpsdvsr << SPI0_SSPCPSR_CPSDVSR_Pos, + clk.cpsdvsr << SPI0_SSPCPSR_CPSDVSR_Pos, SPI0_SSPCPSR_CPSDVSR_Msk); io_reg_write_dont_corrupt(&dev->SSPCR0, - pl022_clk[spi].scr << SPI0_SSPCR0_SCR_Pos, + clk.scr << SPI0_SSPCR0_SCR_Pos, SPI0_SSPCR0_SCR_Msk); /* enable SPI */ diff --git a/cpu/sam0_common/include/periph_cpu_common.h b/cpu/sam0_common/include/periph_cpu_common.h index ee425d144c2e5..99285830f37f3 100644 --- a/cpu/sam0_common/include/periph_cpu_common.h +++ b/cpu/sam0_common/include/periph_cpu_common.h @@ -368,20 +368,6 @@ typedef enum { } spi_mode_t; /** @} */ -/** - * @brief Override SPI clock speed values - * @{ - */ -#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; -/** @} */ - /** * @brief SPI pin getters * @{ diff --git a/cpu/sam0_common/periph/spi.c b/cpu/sam0_common/periph/spi.c index ebed25f335d9d..4e35632ffba59 100644 --- a/cpu/sam0_common/periph/spi.c +++ b/cpu/sam0_common/periph/spi.c @@ -2,6 +2,7 @@ * Copyright (C) 2014-2016 Freie Universität Berlin * 2015 Kaspar Schleiser * 2015 FreshTemp, LLC. + * 2021-2023 Hugues Larrive * 2022 SSV Software Systems GmbH * * This file is subject to the terms and conditions of the GNU Lesser @@ -24,6 +25,7 @@ * @author Kaspar Schleiser * @author Benjamin Valentin * @author Juergen Fitschen + * @author Hugues Larrive * * @} */ @@ -31,6 +33,7 @@ #include #include "cpu.h" +#include "macros/math.h" #include "mutex.h" #include "periph/spi.h" #include "pm_layered.h" @@ -183,9 +186,9 @@ static inline void _init_dma(spi_t bus, const volatile void *reg_rx, volatile vo * @brief QSPI peripheral in SPI mode * @{ */ -#ifdef QSPI static void _init_qspi(spi_t bus) { +#ifdef QSPI /* reset the peripheral */ QSPI->CTRLA.bit.SWRST = 1; @@ -195,32 +198,43 @@ static void _init_qspi(spi_t bus) /* set up DMA channels */ _init_dma(bus, &QSPI->RXDATA.reg, &QSPI->TXDATA.reg); +#else + (void)bus; +#endif } -static void _qspi_acquire(spi_mode_t mode, spi_clk_t clk) +static inline uint32_t _qspi_baud(uint32_t freq) { /* datasheet says SCK = MCK / (BAUD + 1) */ /* but BAUD = 0 does not work, assume SCK = MCK / BAUD */ - uint32_t baud = CLOCK_CORECLOCK > (2 * clk) - ? (CLOCK_CORECLOCK + clk - 1) / clk - : 1; + return DIV_ROUND_UP(CLOCK_CORECLOCK, freq); +} +static void _qspi_acquire(spi_mode_t mode, spi_clk_t clk) +{ +#ifdef QSPI /* bit order is reversed from SERCOM SPI */ uint32_t _mode = (mode >> 1) | (mode << 1); _mode &= 0x3; QSPI->CTRLA.bit.ENABLE = 1; - QSPI->BAUD.reg = QSPI_BAUD_BAUD(baud) | _mode; + QSPI->BAUD.reg = QSPI_BAUD_BAUD(clk.clk) | _mode; +#else + (void)mode; (void)clk; +#endif } static inline void _qspi_release(void) { +#ifdef QSPI QSPI->CTRLA.bit.ENABLE = 0; +#endif } static void _qspi_blocking_transfer(const void *out, void *in, size_t len) { +#ifdef QSPI const uint8_t *out_buf = out; uint8_t *in_buf = in; @@ -240,13 +254,10 @@ static void _qspi_blocking_transfer(const void *out, void *in, size_t len) in_buf[i] = tmp; } } -} -#else /* !QSPI */ -void _init_qspi(spi_t bus); -void _qspi_acquire(spi_mode_t mode, spi_clk_t clk); -void _qspi_release(void); -void _qspi_blocking_transfer(const void *out, void *in, size_t len); +#else + (void)out; (void)in; (void)len; #endif +} /** @} */ /** @@ -269,22 +280,17 @@ static void _init_spi(spi_t bus, SercomSpi *dev) _init_dma(bus, &dev->DATA.reg, &dev->DATA.reg); } -static void _spi_acquire(spi_t bus, spi_mode_t mode, spi_clk_t clk) +static inline uint32_t _spi_baud(spi_t bus, uint32_t freq) { - /* clock can't be higher than source clock */ uint32_t gclk_src = sam0_gclk_freq(spi_config[bus].gclk_src); - if (clk > gclk_src) { - clk = gclk_src; - } - /* configure bus clock, in synchronous mode its calculated from - * BAUD.reg = (f_ref / (2 * f_bus) - 1) - * with f_ref := CLOCK_CORECLOCK as defined by the board - * to mitigate the rounding error due to integer arithmetic, the - * equation is modified to - * BAUD.reg = ((f_ref + f_bus) / (2 * f_bus) - 1) */ - const uint8_t baud = (gclk_src + clk) / (2 * clk) - 1; + * BAUD.reg = f_ref / (2 * f_bus) - 1 + * with f_ref := CLOCK_CORECLOCK as defined by the board */ + return DIV_ROUND_UP(gclk_src, (2 * freq)) - 1; +} +static void _spi_acquire(spi_t bus, spi_mode_t mode, spi_clk_t clk) +{ /* configure device to be master and set mode and pads, * * NOTE: we could configure the pads already during spi_init, but for @@ -296,11 +302,11 @@ static void _spi_acquire(spi_t bus, spi_mode_t mode, spi_clk_t clk) | (mode << SERCOM_SPI_CTRLA_CPHA_Pos); /* first configuration or reconfiguration after altered device usage */ - if (dev(bus)->BAUD.reg != baud || dev(bus)->CTRLA.reg != ctrla) { + if (dev(bus)->BAUD.reg != clk.clk || dev(bus)->CTRLA.reg != ctrla) { /* disable the device */ _disable(dev(bus)); - dev(bus)->BAUD.reg = baud; + dev(bus)->BAUD.reg = clk.clk; dev(bus)->CTRLA.reg = ctrla; /* no synchronization needed here, the enable synchronization below * acts as a write-synchronization for both registers */ @@ -408,10 +414,42 @@ void spi_deinit_pins(spi_t bus) gpio_disable_mux(spi_config[bus].mosi_pin); } +spi_clk_t spi_get_clk(spi_t bus, uint32_t freq) +{ + uint32_t baud; + if (_is_qspi(bus)) { + /* BAUD = MCK / freq - 1 + * 8 bit: 0..255 */ + baud = _qspi_baud(freq); + } else { + /* BAUD = fref / (2 * fbaud) - 1 + * 8 bit: 0..255 */ + baud = _spi_baud(bus, freq); + } + if (baud > 255) { + return (spi_clk_t){ .err = -EDOM }; + } + return (spi_clk_t){ .clk = baud }; +} + +int32_t spi_get_freq(spi_t bus, spi_clk_t clk) +{ + if (clk.err) { return -EINVAL; } + if (_is_qspi(bus)) { + /* SCK = MCK / (BAUD + 1) + * but assume SCK = MCK / BAUD as in _qspi_baud() */ + return CLOCK_CORECLOCK / clk.clk; + } else { + /* fbaud = fref / 2 / (BAUD + 1) */ + return sam0_gclk_freq(spi_config[bus].gclk_src) / 2 / ++clk.clk; + } +} + void spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk) { (void)cs; assert((unsigned)bus < SPI_NUMOF); + if (clk.err) { return; } /* get exclusive access to the device */ mutex_lock(&locks[bus]); diff --git a/cpu/sam3/include/periph_cpu.h b/cpu/sam3/include/periph_cpu.h index 973460a4be10b..013e4f46e16fd 100644 --- a/cpu/sam3/include/periph_cpu.h +++ b/cpu/sam3/include/periph_cpu.h @@ -159,20 +159,6 @@ typedef enum { SPI_MODE_3 = (SPI_CSR_CPOL) /**< CPOL=1, CPHA=1 */ } spi_mode_t; /** @} */ - -/** - * @brief Override default SPI clock values - * @{ - */ -#define HAVE_SPI_CLK_T -typedef enum { - SPI_CLK_100KHZ = (100000), /**< 100KHz */ - SPI_CLK_400KHZ = (400000), /**< 400KHz */ - SPI_CLK_1MHZ = (1000000), /**< 1MHz */ - SPI_CLK_5MHZ = (5000000), /**< 5MHz */ - SPI_CLK_10MHZ = (10000000) /**< 10MHz */ -} spi_clk_t; -/** @} */ #endif /* ndef DOXYGEN */ #ifndef DOXYGEN diff --git a/cpu/sam3/periph/spi.c b/cpu/sam3/periph/spi.c index e542519dabbda..354d070494029 100644 --- a/cpu/sam3/periph/spi.c +++ b/cpu/sam3/periph/spi.c @@ -1,11 +1,12 @@ /* -* Copyright (C) 2014 Hamburg University of Applied Sciences -* 2016-2017 Freie Universität Berlin + * Copyright (C) 2014 Hamburg University of Applied Sciences + * 2016-2017 Freie Universität Berlin + * 2021-2023 Hugues Larrive * * 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_sam3 @@ -19,6 +20,7 @@ * @author Peter Kietzmann * @author Hauke Petersen * @author Joakim Nohlgård + * @author Hugues Larrive * * @} */ @@ -26,6 +28,7 @@ #include #include "cpu.h" +#include "macros/math.h" #include "mutex.h" #include "periph/gpio.h" #include "periph/spi.h" @@ -63,17 +66,38 @@ void spi_init_pins(spi_t bus) gpio_init_mux(spi_config[bus].miso, spi_config[bus].mux); } +spi_clk_t spi_get_clk(spi_t bus, uint32_t freq) +{ + (void)bus; + /* SPCK Baudrate = MCK / SCBR + * SCBR = 1..255 */ + + /* bound dividerto 255 */ + if (freq < DIV_ROUND_UP(CLOCK_CORECLOCK, 255)) { + return (spi_clk_t){ .err = -EDOM }; + } + return (spi_clk_t){ .clk = DIV_ROUND_UP(CLOCK_CORECLOCK, freq) }; +} + +int32_t spi_get_freq(spi_t bus, spi_clk_t clk) +{ + (void)bus; + if (clk.err) { return -EINVAL; } + return CLOCK_CORECLOCK / clk.clk; +} + void spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk) { (void)cs; assert((unsigned)bus < SPI_NUMOF); + if (clk.err) { return; } /* lock bus */ mutex_lock(&locks[bus]); /* enable SPI device clock */ PMC->PMC_PCER0 |= (1 << spi_config[bus].id); /* set mode and speed */ - dev(bus)->SPI_CSR[0] = (SPI_CSR_SCBR(CLOCK_CORECLOCK / clk) | mode); + dev(bus)->SPI_CSR[0] = (SPI_CSR_SCBR(clk.clk) | mode); dev(bus)->SPI_MR = (SPI_MR_MSTR | SPI_MR_MODFDIS); dev(bus)->SPI_CR = SPI_CR_SPIEN; } @@ -101,7 +125,7 @@ void spi_transfer_bytes(spi_t bus, spi_cs_t cs, bool cont, if (!in_buf) { for (size_t i = 0; i < len; i++) { - while(!(dev(bus)->SPI_SR & SPI_SR_TDRE)) {} + while (!(dev(bus)->SPI_SR & SPI_SR_TDRE)) {} dev(bus)->SPI_TDR = out_buf[i]; } while (!(dev(bus)->SPI_SR & SPI_SR_RDRF)) {} @@ -116,9 +140,9 @@ void spi_transfer_bytes(spi_t bus, spi_cs_t cs, bool cont, } else { for (size_t i = 0; i < len; i++) { - while (!(dev(bus)->SPI_SR & SPI_SR_TDRE)); + while (!(dev(bus)->SPI_SR & SPI_SR_TDRE)) {} dev(bus)->SPI_TDR = out_buf[i]; - while (!(dev(bus)->SPI_SR & SPI_SR_RDRF)); + while (!(dev(bus)->SPI_SR & SPI_SR_RDRF)) {} in_buf[i] = dev(bus)->SPI_RDR; } } diff --git a/cpu/stm32/include/periph/cpu_spi.h b/cpu/stm32/include/periph/cpu_spi.h index 2ead5233e00e6..592b2cafae1ed 100644 --- a/cpu/stm32/include/periph/cpu_spi.h +++ b/cpu/stm32/include/periph/cpu_spi.h @@ -75,25 +75,6 @@ typedef uint32_t spi_cs_t; #define PERIPH_SPI_NEEDS_TRANSFER_REGS /** @} */ -/** - * @brief Override SPI clock speed values - * @{ - */ -#define HAVE_SPI_CLK_T -enum { - SPI_CLK_100KHZ = KHZ(100), /**< drive the SPI bus with 100KHz */ - SPI_CLK_400KHZ = KHZ(400), /**< drive the SPI bus with 400KHz */ - SPI_CLK_1MHZ = MHZ(1), /**< drive the SPI bus with 1MHz */ - SPI_CLK_5MHZ = MHZ(5), /**< drive the SPI bus with 5MHz */ - SPI_CLK_10MHZ = MHZ(10), /**< drive the SPI bus with 10MHz */ -}; - -/** - * @brief SPI clock type - */ -typedef uint32_t spi_clk_t; -/** @} */ - /** * @brief Structure for SPI configuration data */ diff --git a/cpu/stm32/periph/spi.c b/cpu/stm32/periph/spi.c index 47e324eb95624..660fa42c1e321 100644 --- a/cpu/stm32/periph/spi.c +++ b/cpu/stm32/periph/spi.c @@ -2,6 +2,7 @@ * Copyright (C) 2014 Hamburg University of Applied Sciences * 2014-2017 Freie Universität Berlin * 2016-2017 OTA keys S.A. + * 2021-2023 Hugues Larrive * * 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 @@ -22,6 +23,7 @@ * @author Vincent Dupont * @author Joakim Nohlgård * @author Thomas Eichinger + * @author Hugues Larrive * * @} */ @@ -59,16 +61,6 @@ */ static mutex_t locks[SPI_NUMOF]; -/** - * @brief Clock configuration cache - */ -static uint32_t clocks[SPI_NUMOF]; - -/** - * @brief Clock divider cache - */ -static uint8_t dividers[SPI_NUMOF]; - static inline SPI_TypeDef *dev(spi_t bus) { return spi_config[bus].dev; @@ -81,35 +73,6 @@ static inline bool _use_dma(const spi_conf_t *conf) } #endif -/** - * @brief Multiplier for clock divider calculations - * - * Makes the divider calculation fixed point - */ -#define SPI_APB_CLOCK_SHIFT (4U) -#define SPI_APB_CLOCK_MULT (1U << SPI_APB_CLOCK_SHIFT) - -static uint8_t _get_clkdiv(const spi_conf_t *conf, uint32_t clock) -{ - uint32_t bus_clock = periph_apb_clk(conf->apbbus); - /* Shift bus_clock with SPI_APB_CLOCK_SHIFT to create a fixed point int */ - uint32_t div = (bus_clock << SPI_APB_CLOCK_SHIFT) / (2 * clock); - DEBUG("[spi] clock: divider: %"PRIu32"\n", div); - /* Test if the divider is 2 or smaller, keeping the fixed point in mind */ - if (div <= SPI_APB_CLOCK_MULT) { - return 0; - } - /* determine MSB and compensate back for the fixed point int shift */ - uint8_t rounded_div = bitarithm_msb(div) - SPI_APB_CLOCK_SHIFT; - /* Determine if rounded_div is not a power of 2 */ - if ((div & (div - 1)) != 0) { - /* increment by 1 to ensure that the clock speed at most the - * requested clock speed */ - rounded_div++; - } - return rounded_div > BR_MAX ? BR_MAX : rounded_div; -} - void spi_init(spi_t bus) { assert(bus < SPI_NUMOF); @@ -221,9 +184,32 @@ int spi_init_with_gpio_mode(spi_t bus, const spi_gpio_mode_t* mode) } #endif +spi_clk_t spi_get_clk(spi_t bus, uint32_t freq) +{ + /* freq = f_pclk / (1 << (BR + 1)) + * BR = 0..7 */ + uint8_t br = 0; + while (periph_apb_clk(spi_config[bus].apbbus) / (1 << (br + 1)) > freq) { + br++; + } + /* bound divider to 256 */ + if (br > BR_MAX) { + return (spi_clk_t){ .err = -EDOM }; + } + return (spi_clk_t){ .clk = (br << BR_SHIFT) }; +} + +int32_t spi_get_freq(spi_t bus, spi_clk_t clk) +{ + if (clk.err) { return -EINVAL; } + return periph_apb_clk(spi_config[bus].apbbus) + / (1 << ((clk.clk >> BR_SHIFT) + 1)); +} + void spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk) { assert((unsigned)bus < SPI_NUMOF); + if (clk.err) { return; } /* lock bus */ mutex_lock(&locks[bus]); @@ -234,19 +220,7 @@ void spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk) /* enable SPI device clock */ periph_clk_en(spi_config[bus].apbbus, spi_config[bus].rccmask); /* enable device */ - if (clk != clocks[bus]) { - dividers[bus] = _get_clkdiv(&spi_config[bus], clk); - clocks[bus] = clk; - } - uint8_t br = dividers[bus]; - - DEBUG("[spi] acquire: requested clock: %"PRIu32", resulting clock: %"PRIu32 - " BR divider: %u\n", - clk, - periph_apb_clk(spi_config[bus].apbbus)/(1 << (br + 1)), - br); - - uint16_t cr1_settings = ((br << BR_SHIFT) | mode | SPI_CR1_MSTR); + uint16_t cr1_settings = (clk.clk | mode | SPI_CR1_MSTR); /* Settings to add to CR2 in addition to SPI_CR2_SETTINGS */ uint16_t cr2_extra_settings = 0; if (cs != SPI_HWCS_MASK) { diff --git a/drivers/include/periph/spi.h b/drivers/include/periph/spi.h index 5c88a7db076f4..0d7e88f2285c4 100644 --- a/drivers/include/periph/spi.h +++ b/drivers/include/periph/spi.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2014-2016 Freie Universität Berlin + * 2021-2023 Hugues Larrive * * 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 @@ -61,6 +62,7 @@ * @brief Low-level SPI peripheral driver interface definition * * @author Hauke Petersen + * @author Hugues Larrive */ #ifndef PERIPH_SPI_H @@ -72,9 +74,11 @@ #include #include -#include "periph_cpu.h" -#include "periph_conf.h" +#include "architecture.h" +#include "macros/units.h" #include "periph/gpio.h" +#include "periph_conf.h" +#include "periph_cpu.h" #ifdef __cplusplus extern "C" { @@ -127,6 +131,19 @@ typedef uint_fast8_t spi_t; typedef gpio_t spi_cs_t; #endif +/** + * @brief Structure representing the return value of @ref spi_get_clk function + * + * This structure encapsulates the return code and the SPI clock configuration. + */ +#ifndef HAVE_SPI_CLK_T +typedef struct { + int err; /**< Error code indicating the success or failure of the + * operation */ + uword_t clk; /**< SPI clock configuration */ +} spi_clk_t; +#endif + /** * @brief Status codes used by the SPI driver interface * @@ -172,16 +189,18 @@ typedef enum { * The actual speed of the bus can vary to some extend, as the combination of * CPU clock and available prescaler values on certain platforms may not make * the exact values possible. + * + * @deprecated Use numeric values instead + * @{ */ -#ifndef HAVE_SPI_CLK_T -typedef enum { - SPI_CLK_100KHZ = 0, /**< drive the SPI bus with 100KHz */ - SPI_CLK_400KHZ, /**< drive the SPI bus with 400KHz */ - SPI_CLK_1MHZ, /**< drive the SPI bus with 1MHz */ - SPI_CLK_5MHZ, /**< drive the SPI bus with 5MHz */ - SPI_CLK_10MHZ /**< drive the SPI bus with 10MHz */ -} spi_clk_t; -#endif +enum { + SPI_CLK_100KHZ = KHZ(100), /**< drive the SPI bus with 100KHz */ + SPI_CLK_400KHZ = KHZ(400), /**< drive the SPI bus with 400KHz */ + SPI_CLK_1MHZ = MHZ(1), /**< drive the SPI bus with 1MHz */ + SPI_CLK_5MHZ = MHZ(5), /**< drive the SPI bus with 5MHz */ + SPI_CLK_10MHZ = MHZ(10), /**< drive the SPI bus with 10MHz */ +}; +/** @} */ /** * @brief Basic initialization of the given SPI bus @@ -333,22 +352,62 @@ typedef struct { int spi_init_with_gpio_mode(spi_t bus, const spi_gpio_mode_t* mode); #endif +/** + * @brief Get the @ref spi_clk_t value that best matches the given frequency + * in Hertz + * + * @param[in] bus SPI device to get a clock configuration for + * @param[in] freq The desired frequency in Hertz + * + * @return The SPI clock configuration that is as close to, but not higher than + * the specified frequency. If the requested frequency is too low + * (below the lower limit), it returns an spi_clk_t structure with + * errno set to -EDOM indicating an invalid argument. If the requested + * frequency is too high (above the upper limit), it returns the + * closest clock configuration available that is within the valid + * range. + */ +spi_clk_t spi_get_clk(spi_t bus, uint32_t freq); + +/** + * @brief Get the actual frequency Hertz corresponding to the given + * clock config + * + * @param[in] bus SPI device which the clock configuration was + * made for + * @param[in] clk The clock configuration to get the corresponding + * frequency from + * + * @retval > 0 The exact frequency in Hertz matching the clock + * configuration. + * @retval -EINVAL spi_get_clk() failed to get a valid clock configuration + * so .errno was set in clk data structure. + * + * @note In most cases `spi_get_freq(spi_get_clk(x)) != x` will be true, + * since `spi_get_clk()` will return only the closest match, which will + * rarely be an exact match. + */ +int32_t spi_get_freq(spi_t bus, spi_clk_t clk); + /** * @brief Start a new SPI transaction * * Starting a new SPI transaction will get exclusive access to the SPI bus * and configure it according to the given values. If another SPI transaction * is active when this function is called, this function will block until the - * other transaction is complete (spi_relase was called). + * other transaction is complete (@ref spi_release was called). * * @param[in] bus SPI device to access * @param[in] cs chip select pin/line to use, set to SPI_CS_UNDEF if chip * select should not be handled by the SPI driver * @param[in] mode mode to use for the new transaction - * @param[in] clk bus clock speed to use for the transaction + * @param[in] clk opaque clock configuration obtain from @ref spi_get_clk * * @pre All parameters are valid and supported, otherwise an assertion blows * up (if assertions are enabled). + * + * @post Exclusive access to the SPI bus is guaranteed until @ref spi_release + * is called. */ void spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk); diff --git a/drivers/include/soft_spi.h b/drivers/include/soft_spi.h index 69860561b7f27..d61c5af4ef3a2 100644 --- a/drivers/include/soft_spi.h +++ b/drivers/include/soft_spi.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2017 Hamburg University of Applied Sciences + * 2021-2023 Hugues Larrive * * 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 @@ -23,6 +24,7 @@ * @brief Software SPI port descriptor definition * * @author Markus Blechschmidt + * @author Hugues Larrive */ #ifndef SOFT_SPI_H @@ -74,6 +76,13 @@ typedef unsigned int soft_spi_t; */ typedef gpio_t soft_spi_cs_t; +/** + * @brief Opaque type that contains a SPI clock configuration + * + * Use @ref soft_spi_get_clk to obtain this value. + */ +typedef uword_t soft_spi_clk_t; + /** * @brief Status codes used by the SPI driver interface */ @@ -114,12 +123,17 @@ typedef enum { * The actual speed of the bus varies between CPUs and depends on the speed * of the processing. The values of the enum entries represent the approximate * delay between two clock edges. + * + * @deprecated Use numeric values instead */ -typedef enum { - SOFT_SPI_CLK_100KHZ = 5, /**< drive the SPI bus with less than 100kHz */ - SOFT_SPI_CLK_1MHZ = 1, /**< drive the SPI bus with less than 1MHz */ - SOFT_SPI_CLK_DEFAULT = 0, /**< drive the SPI bus with maximum speed possible */ -} soft_spi_clk_t; +enum { + SOFT_SPI_CLK_100KHZ = KHZ(100), /**< drive the SPI bus with less + * than 100kHz */ + SOFT_SPI_CLK_1MHZ = MHZ(1), /**< drive the SPI bus with less + * than 400kHz */ + SOFT_SPI_CLK_DEFAULT = (MHZ(500) + 1), /**< drive the SPI bus with + * maximum speed possible */ +}; /** * @brief Software SPI port descriptor @@ -174,6 +188,31 @@ void soft_spi_init_pins(soft_spi_t bus); */ int soft_spi_init_cs(soft_spi_t bus, soft_spi_cs_t cs); +/** + * @brief Get the @ref soft_spi_clk_t value that best matches the given frequency in Hertz + * + * @param[in] bus SPI device to get a clock configuration for + * @param[in] freq Get the clock configuration best matching this frequency in Hertz + * + * @return The opaque clock configuration that is as close to but not higher than the frequency + * given in @p freq + */ +soft_spi_clk_t soft_spi_get_clk(soft_spi_t bus, uint32_t freq); + +/** + * @brief Get the actual frequency Hertz corresponding to the given clock config + * + * @param[in] bus SPI device which the clock configuration was made for + * @param[in] clk The clock configuration to get the corresponding frequency from + * + * @return The exact frequency in Hertz matching the clock configuration + * + * @note In most cases `soft_spi_get_freq(soft_spi_get_clk(x)) != x` will be true, + * since `soft_spi_get_clk()` will return only the closest match, which will + * rarely be an exact match. + */ +uint32_t soft_spi_get_freq(soft_spi_t bus, soft_spi_clk_t clk); + /** * @brief Start a new SPI transaction * diff --git a/drivers/soft_spi/soft_spi.c b/drivers/soft_spi/soft_spi.c index 83f67b53be485..826c30ac65623 100644 --- a/drivers/soft_spi/soft_spi.c +++ b/drivers/soft_spi/soft_spi.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2017 Hamburg University of Applied Sciences + * 2021-2023 Hugues Larrive * * 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 @@ -15,6 +16,7 @@ * * @author Markus Blechschmidt * @author Peter Kietzmann + * @author Hugues Larrive */ #include @@ -110,6 +112,18 @@ static inline int soft_spi_mode_is_valid(soft_spi_mode_t mode) return 1; } +soft_spi_clk_t soft_spi_get_clk(soft_spi_t bus, uint32_t freq) +{ + (void)bus; + return MHZ(500) / freq; +} + +uint32_t soft_spi_get_freq(soft_spi_t bus, soft_spi_clk_t clk) +{ + (void)bus; + return MHZ(500) / clk; +} + void soft_spi_acquire(soft_spi_t bus, soft_spi_cs_t cs, soft_spi_mode_t mode, soft_spi_clk_t clk) { (void)cs; diff --git a/pkg/driver_bme680/contrib/bme680_hal.c b/pkg/driver_bme680/contrib/bme680_hal.c index fd977c393b45d..e0770b902b09e 100644 --- a/pkg/driver_bme680/contrib/bme680_hal.c +++ b/pkg/driver_bme680/contrib/bme680_hal.c @@ -1,6 +1,7 @@ /* * Copyright (C) 2019 Mesotic SAS * 2020 Gunar Schorcht + * 2021-2023 Hugues Larrive * * 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 @@ -17,6 +18,7 @@ * * @author Dylan Laduranty * @author Gunar Schorcht + * @author Hugues Larrive */ #include @@ -80,6 +82,18 @@ int8_t bme680_i2c_write_hal(uint8_t dev_id, uint8_t reg_addr, #endif /* MODULE_PERIPH_I2C */ #ifdef MODULE_PERIPH_SPI +static uint32_t freq_cache; +static spi_clk_t clk_cache; +static inline spi_clk_t spi_clk_cache(spi_t bus, uint32_t freq) +{ + if (freq != freq_cache) { + freq_cache = freq; + clk_cache = spi_get_clk(bus, freq); + } + + return clk_cache; +} + int8_t bme680_spi_read_hal(uint8_t dev_id, uint8_t reg_addr, uint8_t *data, uint16_t len) { @@ -89,7 +103,7 @@ int8_t bme680_spi_read_hal(uint8_t dev_id, uint8_t reg_addr, unsigned int cpsr = irq_disable(); gpio_clear(intf->nss_pin); - spi_acquire(intf->dev, SPI_CS_UNDEF, BME680_SPI_MODE, BME680_SPI_SPEED); + spi_acquire(intf->dev, SPI_CS_UNDEF, BME680_SPI_MODE, spi_clk_cache(intf->dev, BME680_SPI_SPEED)); spi_transfer_regs(intf->dev, SPI_CS_UNDEF, reg_addr, NULL, data, len); gpio_set(intf->nss_pin); @@ -107,7 +121,7 @@ int8_t bme680_spi_write_hal(uint8_t dev_id, uint8_t reg_addr, unsigned int cpsr = irq_disable(); gpio_clear(intf->nss_pin); - spi_acquire(intf->dev, SPI_CS_UNDEF, BME680_SPI_MODE, BME680_SPI_SPEED); + spi_acquire(intf->dev, SPI_CS_UNDEF, BME680_SPI_MODE, spi_clk_cache(intf->dev, BME680_SPI_SPEED)); spi_transfer_regs(intf->dev, SPI_CS_UNDEF, reg_addr, data, NULL, len); gpio_set(intf->nss_pin); diff --git a/pkg/driver_sx126x/contrib/driver_sx126x_hal.c b/pkg/driver_sx126x/contrib/driver_sx126x_hal.c index 4e9c2ea2656fd..41531e5c49143 100644 --- a/pkg/driver_sx126x/contrib/driver_sx126x_hal.c +++ b/pkg/driver_sx126x/contrib/driver_sx126x_hal.c @@ -1,6 +1,7 @@ /* * Copyright (C) 2021 Inria * 2021 Freie Universität Berlin + * 2021-2023 Hugues Larrive * * 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 @@ -16,6 +17,7 @@ * * @author Alexandre Abadie * @author Akshai M + * @author Hugues Larrive * * @} */ @@ -57,6 +59,18 @@ static uint8_t sx126x_radio_wait_until_ready(sx126x_t *dev) } #endif +static uint32_t freq_cache; +static spi_clk_t clk_cache; +static inline spi_clk_t spi_clk_cache(spi_t bus, uint32_t freq) +{ + if (freq != freq_cache) { + freq_cache = freq; + clk_cache = spi_get_clk(bus, freq); + } + + return clk_cache; +} + sx126x_hal_status_t sx126x_hal_write(const void *context, const uint8_t *command, const uint16_t command_length, const uint8_t *data, const uint16_t data_length) @@ -68,7 +82,8 @@ sx126x_hal_status_t sx126x_hal_write(const void *context, if (sx126x_is_stm32wl(dev)) { #if IS_USED(MODULE_SX126X_STM32WL) sx126x_radio_wait_until_ready(dev); - spi_acquire(dev->params->spi, SPI_CS_UNDEF, SX126X_SPI_MODE, SX126X_SPI_SPEED); + spi_acquire(dev->params->spi, SPI_CS_UNDEF, SX126X_SPI_MODE, + spi_clk_cache(dev->params->spi, SX126X_SPI_SPEED)); /* Check if radio is set to sleep or `RxDutyCycle` mode */ if (command[0] == 0x84 || command[0] == 0x94) { @@ -95,7 +110,8 @@ sx126x_hal_status_t sx126x_hal_write(const void *context, /* wait for the device to not be busy anymore */ while (gpio_read(dev->params->busy_pin)) {} - spi_acquire(dev->params->spi, SPI_CS_UNDEF, SX126X_SPI_MODE, SX126X_SPI_SPEED); + spi_acquire(dev->params->spi, SPI_CS_UNDEF, SX126X_SPI_MODE, + spi_clk_cache(dev->params->spi, SX126X_SPI_SPEED)); spi_transfer_bytes(dev->params->spi, dev->params->nss_pin, data_length != 0, command, NULL, command_length); if (data_length) { @@ -118,7 +134,8 @@ sx126x_hal_status_t sx126x_hal_read(const void *context, #if IS_USED(MODULE_SX126X_STM32WL) sx126x_radio_wait_until_ready(dev); - spi_acquire(dev->params->spi, SPI_CS_UNDEF, SX126X_SPI_MODE, SX126X_SPI_SPEED); + spi_acquire(dev->params->spi, SPI_CS_UNDEF, SX126X_SPI_MODE, + spi_clk_cache(dev->params->spi, SX126X_SPI_SPEED)); /* Pull NSS low */ PWR->SUBGHZSPICR &= ~PWR_SUBGHZSPICR_NSS; spi_transfer_bytes(dev->params->spi, SPI_CS_UNDEF, true, command, NULL, \ @@ -133,7 +150,8 @@ sx126x_hal_status_t sx126x_hal_read(const void *context, /* wait for the device to not be busy anymore */ while (gpio_read(dev->params->busy_pin)) {} - spi_acquire(dev->params->spi, SPI_CS_UNDEF, SX126X_SPI_MODE, SX126X_SPI_SPEED); + spi_acquire(dev->params->spi, SPI_CS_UNDEF, SX126X_SPI_MODE, + spi_clk_cache(dev->params->spi, SX126X_SPI_SPEED)); spi_transfer_bytes(dev->params->spi, dev->params->nss_pin, true, \ command, NULL, command_length); spi_transfer_bytes(dev->params->spi, dev->params->nss_pin, false, \ @@ -204,7 +222,8 @@ sx126x_hal_status_t sx126x_hal_wakeup(const void *context) #endif } else { - spi_acquire(dev->params->spi, SPI_CS_UNDEF, SX126X_SPI_MODE, SX126X_SPI_SPEED); + spi_acquire(dev->params->spi, SPI_CS_UNDEF, SX126X_SPI_MODE, + spi_clk_cache(dev->params->spi, SX126X_SPI_SPEED)); gpio_clear(dev->params->nss_pin); gpio_set(dev->params->nss_pin); spi_release(dev->params->spi); diff --git a/pkg/u8g2/contrib/u8x8_riotos.c b/pkg/u8g2/contrib/u8x8_riotos.c index 0730f9da01e8a..df26b27727f4c 100644 --- a/pkg/u8g2/contrib/u8x8_riotos.c +++ b/pkg/u8g2/contrib/u8x8_riotos.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2016-2018 Bas Stottelaar + * 2021-2023 Hugues Larrive * * 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 @@ -14,6 +15,7 @@ * @brief U8g2 driver for interacting with RIOT-OS peripherals * * @author Bas Stottelaar + * @author Hugues Larrive * * @} */ @@ -34,24 +36,17 @@ #endif #ifdef MODULE_PERIPH_SPI -static spi_clk_t u8x8_pulse_width_to_spi_speed(uint32_t pulse_width) +static spi_clk_t spi_clk_cache(spi_t bus, uint32_t period_ns) { - const uint32_t cycle_time = 2 * pulse_width; + static uint32_t period_ns_cache; + static spi_clk_t clk_cache; - if (cycle_time < 100) { - return SPI_CLK_10MHZ; - } - else if (cycle_time < 200) { - return SPI_CLK_5MHZ; - } - else if (cycle_time < 1000) { - return SPI_CLK_1MHZ; - } - else if (cycle_time < 2500) { - return SPI_CLK_400KHZ; + if (period_ns != period_ns_cache) { + period_ns_cache = period_ns; + clk_cache = spi_get_clk(bus, MHZ(1000) / period_ns); } - return SPI_CLK_100KHZ; + return clk_cache; } static spi_mode_t u8x8_spi_mode_to_spi_conf(uint32_t spi_mode) @@ -151,7 +146,7 @@ uint8_t u8x8_byte_hw_spi_riotos(u8x8_t *u8g2, uint8_t msg, uint8_t arg_int, void case U8X8_MSG_BYTE_START_TRANSFER: spi_acquire(dev, GPIO_UNDEF, u8x8_spi_mode_to_spi_conf(u8g2->display_info->spi_mode), - u8x8_pulse_width_to_spi_speed(u8g2->display_info->sck_pulse_width_ns)); + spi_clk_cache(dev, u8g2->display_info->sck_pulse_width_ns)); u8x8_gpio_SetCS(u8g2, u8g2->display_info->chip_enable_level); u8g2->gpio_and_delay_cb(u8g2, U8X8_MSG_DELAY_NANO, u8g2->display_info->post_chip_enable_wait_ns, NULL); diff --git a/pkg/ucglib/contrib/ucg_riotos.c b/pkg/ucglib/contrib/ucg_riotos.c index 043e573535f00..d4807d4b45212 100644 --- a/pkg/ucglib/contrib/ucg_riotos.c +++ b/pkg/ucglib/contrib/ucg_riotos.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2018 Bas Stottelaar + * 2021-2023 Hugues Larrive * * 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 @@ -14,6 +15,7 @@ * @brief Ucglib driver to interact with RIOT-OS drivers. * * @author Bas Stottelaar + * @author Hugues Larrive * * @} */ @@ -31,22 +33,17 @@ #include "periph/gpio.h" #ifdef MODULE_PERIPH_SPI -static spi_clk_t ucg_serial_clk_speed_to_spi_speed(uint32_t serial_clk_speed) +static spi_clk_t spi_clk_cache(spi_t bus, uint32_t period_ns) { - if (serial_clk_speed < 100) { - return SPI_CLK_10MHZ; - } - else if (serial_clk_speed < 200) { - return SPI_CLK_5MHZ; - } - else if (serial_clk_speed < 1000) { - return SPI_CLK_1MHZ; - } - else if (serial_clk_speed < 2500) { - return SPI_CLK_400KHZ; + static uint32_t period_ns_cache; + static spi_clk_t clk_cache; + + if (period_ns != period_ns_cache) { + period_ns_cache = period_ns; + clk_cache = spi_get_clk(bus, MHZ(1000) / period_ns); } - return SPI_CLK_100KHZ; + return clk_cache; } #endif /* MODULE_PERIPH_SPI */ @@ -90,10 +87,11 @@ int16_t ucg_com_hw_spi_riotos(ucg_t *ucg, int16_t msg, uint16_t arg, uint8_t *da /* setup SPI */ spi_init_pins(dev); + /* correct alignment of data can be assumed, as in pkg callers use * ucg_com_info_t to allocate memory */ ucg_com_info_t *info = (void *)(uintptr_t)data; - spi_clk_t speed = ucg_serial_clk_speed_to_spi_speed(info->serial_clk_speed); + spi_clk_t speed = spi_clk_cache(dev, info->serial_clk_speed); spi_acquire(dev, GPIO_UNDEF, SPI_MODE_0, speed); break; diff --git a/pkg/uwb-dw1000/hal/uwb_dw1000_spi.c b/pkg/uwb-dw1000/hal/uwb_dw1000_spi.c index be8bc8833d7d5..18e856ba0c2a9 100644 --- a/pkg/uwb-dw1000/hal/uwb_dw1000_spi.c +++ b/pkg/uwb-dw1000/hal/uwb_dw1000_spi.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2020 Inria + * 2021-2023 Hugues Larrive * * 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 @@ -14,13 +15,14 @@ * @brief SPI abstraction layer implementation * * @author Francisco Molina + * @author Hugues Larrive * @} */ #include "hal/hal_spi.h" #include "periph/spi.h" -static uint32_t spi_clk[SPI_NUMOF] = { SPI_CLK_1MHZ }; +static uint32_t spi_clk[SPI_NUMOF]; static uint32_t spi_mode[SPI_NUMOF] = { SPI_MODE_0 }; int hal_spi_set_txrx_cb(int spi_num, hal_spi_txrx_cb txrx_cb, void *arg) @@ -41,7 +43,7 @@ int hal_spi_init(int spi_num, void *cfg, uint8_t spi_type) int hal_spi_config(int spi_num, struct hal_spi_settings *settings) { - spi_clk[spi_num] = settings->baudrate; + spi_clk[spi_num] = spi_get_clk(spi_num, settings->baudrate); spi_mode[spi_num] = settings->data_mode; return 0; } diff --git a/sys/arduino/SPI.cpp b/sys/arduino/SPI.cpp index d4f263c0605df..46ec305f97853 100644 --- a/sys/arduino/SPI.cpp +++ b/sys/arduino/SPI.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2019 Otto-von-Guericke-Universität Magdeburg + * 2021-2023 Hugues Larrive * * 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 @@ -14,6 +15,7 @@ * @brief Implementation of the Arduino 'SPI' interface * * @author Marian Buschsieweke + * @author Hugues Larrive * * @} */ @@ -30,13 +32,6 @@ SPISettings::SPISettings(uint32_t clock_hz, uint8_t bitOrder, uint8_t dataMode) { (void)bitOrder; - static const spi_clk_t clocks[] = { - SPI_CLK_10MHZ, SPI_CLK_5MHZ, SPI_CLK_1MHZ, SPI_CLK_400KHZ - }; - static const uint32_t steps [] = { - 10000000, 5000000, 1000000, 400000 - }; - assert(bitOrder == MSBFIRST); switch(dataMode) { default: @@ -54,14 +49,7 @@ SPISettings::SPISettings(uint32_t clock_hz, uint8_t bitOrder, uint8_t dataMode) break; } - for (uint8_t i = 0; i < ARRAY_SIZE(steps); i++) { - if (clock_hz >= steps[i]) { - clock = clocks[i]; - return; - } - } - - clock = SPI_CLK_100KHZ; + clock = spi_get_clk(SPI_DEV(0), clock_hz); } SPIClass::SPIClass(spi_t spi_dev) @@ -133,12 +121,7 @@ void SPIClass::setDataMode(uint8_t dataMode) void SPIClass::setClockDivider(uint8_t divider) { - static const spi_clk_t clocks[] = { - SPI_CLK_5MHZ, SPI_CLK_1MHZ, SPI_CLK_400KHZ, SPI_CLK_100KHZ - }; - - assert(divider < ARRAY_SIZE(clocks)); - settings.clock = clocks[divider]; + settings.clock = spi_get_clk(SPI_DEV(0), divider); } SPIClass SPI(SPI_DEV(ARDUINO_SPI_INTERFACE)); diff --git a/sys/arduino/include/spiport.hpp b/sys/arduino/include/spiport.hpp index e5fe7d2c0ab11..20c75eef050a0 100644 --- a/sys/arduino/include/spiport.hpp +++ b/sys/arduino/include/spiport.hpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2019 Otto-von-Guericke-Universität Magdeburg + * 2021-2023 Hugues Larrive * * 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 @@ -14,6 +15,7 @@ * @brief Definition of the Arduino 'SPI' interface * * @author Marian Buschsieweke + * @author Hugues Larrive */ #ifndef SPIPORT_H @@ -42,13 +44,13 @@ * a frequency greater than the one it would have on Arduinos. * @{ */ -#define SPI_CLOCK_DIV2 (0) /**< Best match for 8 MHz: 5 MHz */ -#define SPI_CLOCK_DIV4 (1) /**< Best match for 4 MHz: 1 MHz */ -#define SPI_CLOCK_DIV8 (1) /**< Best match for 2 MHz: 1 MHz */ -#define SPI_CLOCK_DIV16 (1) /**< Best match for 1 MHz: 1 MHz */ -#define SPI_CLOCK_DIV32 (2) /**< Best match for 500 kHz: 400 kHz */ -#define SPI_CLOCK_DIV64 (3) /**< Best match for 250 kHZ: 100 kHz */ -#define SPI_CLOCK_DIV128 (3) /**< Best match for 125 kHz: 100 kHz */ +#define SPI_CLOCK_DIV2 MHZ(8) /**< 16 MHz / 2 */ +#define SPI_CLOCK_DIV4 MHZ(4) /**< 16 MHz / 4 */ +#define SPI_CLOCK_DIV8 MHZ(2) /**< 16 MHz / 8 */ +#define SPI_CLOCK_DIV16 MHZ(1) /**< 16 MHz / 16 */ +#define SPI_CLOCK_DIV32 KHZ(500) /**< 16 MHz / 32 */ +#define SPI_CLOCK_DIV64 KHZ(250) /**< 16 MHz / 64 */ +#define SPI_CLOCK_DIV128 KHZ(125) /**< 16 MHz / 128 */ /** @} */ /** @@ -84,7 +86,7 @@ class SPISettings { /** * @brief Create a new SPI settings instance with default settings */ - SPISettings() : SPISettings(4000000, MSBFIRST, SPI_MODE0) { } + SPISettings() : SPISettings(MHZ(4), MSBFIRST, SPI_MODE0) { } friend class SPIClass; }; diff --git a/tests/drivers/nrf24l01p_lowlevel/main.c b/tests/drivers/nrf24l01p_lowlevel/main.c index 459367f678a4b..c1116fe7db5dc 100644 --- a/tests/drivers/nrf24l01p_lowlevel/main.c +++ b/tests/drivers/nrf24l01p_lowlevel/main.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2014 Hamburg University of Applied Sciences + * 2021-2023 Hugues Larrive * * 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 @@ -14,6 +15,7 @@ * @brief Test application for nrf24l01p lowlevel functions * * @author Peter Kietzmann + * @author Hugues Larrive * * @} */ @@ -258,7 +260,7 @@ int cmd_print_regs(int argc, char **argv) printf("################## Print Registers ###################\n"); - spi_acquire(SPI_PORT, CS_PIN, SPI_MODE_0, SPI_CLK_400KHZ); + spi_acquire(SPI_PORT, CS_PIN, SPI_MODE_0, spi_get_clk(SPI_PORT, KHZ(400))); puts("REG_CONFIG: "); print_register(REG_CONFIG, 1); diff --git a/tests/drivers/soft_spi/main.c b/tests/drivers/soft_spi/main.c index 2423587e590ef..ab85033a1cd74 100644 --- a/tests/drivers/soft_spi/main.c +++ b/tests/drivers/soft_spi/main.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2017 Hamburg University of Applied Sciences + * 2021-2023 Hugues Larrive * * 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 @@ -16,6 +17,7 @@ * * @author Markus Blechschmidt * @author Peter Kietzmann + * @author Hugues Larrive * * @} */ @@ -33,6 +35,7 @@ int main(void) soft_spi_t soft_spi = TEST_SOFT_SPI_DEV; soft_spi_cs_t cs = TEST_CS_PIN; + soft_spi_clk_t clk = soft_spi_get_clk(soft_spi, SOFT_SPI_CLK_100KHZ); /* Initialize software SPI device */ soft_spi_init(soft_spi); @@ -45,24 +48,24 @@ int main(void) } puts("Send 0xa5 in all four modes"); - soft_spi_acquire(soft_spi, cs, SOFT_SPI_MODE_0, SOFT_SPI_CLK_100KHZ); + soft_spi_acquire(soft_spi, cs, SOFT_SPI_MODE_0, clk); soft_spi_transfer_byte(soft_spi, cs, false, 0xa5); soft_spi_release(soft_spi); - soft_spi_acquire(soft_spi, cs, SOFT_SPI_MODE_1, SOFT_SPI_CLK_100KHZ); + soft_spi_acquire(soft_spi, cs, SOFT_SPI_MODE_1, clk); soft_spi_transfer_byte(soft_spi, cs, false, 0xa5); soft_spi_release(soft_spi); - soft_spi_acquire(soft_spi, cs, SOFT_SPI_MODE_2, SOFT_SPI_CLK_100KHZ); + soft_spi_acquire(soft_spi, cs, SOFT_SPI_MODE_2, clk); soft_spi_transfer_byte(soft_spi, cs, false, 0xa5); soft_spi_release(soft_spi); - soft_spi_acquire(soft_spi, cs, SOFT_SPI_MODE_3, SOFT_SPI_CLK_100KHZ); + soft_spi_acquire(soft_spi, cs, SOFT_SPI_MODE_3, clk); soft_spi_transfer_byte(soft_spi, cs, false, 0xa5); soft_spi_release(soft_spi); printf("Send %s\n",string); - soft_spi_acquire(soft_spi, cs, SOFT_SPI_MODE_0, SOFT_SPI_CLK_100KHZ); + soft_spi_acquire(soft_spi, cs, SOFT_SPI_MODE_0, clk); soft_spi_transfer_bytes(soft_spi, cs, false, string, NULL, sizeof string); soft_spi_release(soft_spi); diff --git a/tests/periph/spi/Makefile b/tests/periph/spi/Makefile index 96892a362ce10..685476602c88a 100644 --- a/tests/periph/spi/Makefile +++ b/tests/periph/spi/Makefile @@ -1,7 +1,7 @@ BOARD ?= samr21-xpro include ../Makefile.periph_common -LOW_MEMORY_BOARDS := samd10-xmini +LOW_MEMORY_BOARDS := samd10-xmini atmega1284p FEATURES_REQUIRED += periph_spi FEATURES_OPTIONAL += periph_spi_on_qspi diff --git a/tests/periph/spi/main.c b/tests/periph/spi/main.c index b390239ea3c98..39615e2257e17 100644 --- a/tests/periph/spi/main.c +++ b/tests/periph/spi/main.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2014 Freie Universität Berlin + * 2021-2023 Hugues Larrive * * 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 @@ -16,6 +17,7 @@ * This implementation covers both, master and slave configurations. * * @author Hauke Petersen + * @author Hugues Larrive * * @} */ @@ -118,27 +120,44 @@ void print_bytes(char* title, uint8_t* data, size_t len) printf("\n\n"); } +static void _dev_help(void) +{ + puts("\tdev:"); + for (int i = 0; i < (int)SPI_NUMOF; i++) { + printf("\t\t%i: SPI_DEV(%i)\n", i, i); + } +} + +static int _check_dev(int dev) +{ + if (dev < 0 || dev >= (int)SPI_NUMOF) { + puts("error: invalid SPI device specified"); + return 1; + } + return 0; +} + int cmd_init(int argc, char **argv) { - int dev, mode, clk, port, pin, tmp; + int dev, mode, port, pin, tmp; + uint32_t clk; if (argc < 4) { printf("usage: %s [cs port] [cs pin]\n", argv[0]); - puts("\tdev:"); - for (int i = 0; i < (int)SPI_NUMOF; i++) { - printf("\t\t%i: SPI_DEV(%i)\n", i, i); - } + _dev_help(); puts("\tmode:"); puts("\t\t0: POL:0, PHASE:0 - on first rising edge"); puts("\t\t1: POL:0, PHASE:1 - on second rising edge"); puts("\t\t2: POL:1, PHASE:0 - on first falling edge"); puts("\t\t3: POL:1, PHASE:1 - on second falling edge"); puts("\tclk:"); - puts("\t\t0: 100 KHz"); - puts("\t\t1: 400 KHz"); - puts("\t\t2: 1 MHz"); - puts("\t\t3: 5 MHz"); - puts("\t\t4: 10 MHz"); + puts("\t\t0: SPI_CLK_100KHZ*"); + puts("\t\t1: SPI_CLK_400KHZ*"); + puts("\t\t2: SPI_CLK_1MHZ*"); + puts("\t\t3: SPI_CLK_5MHZ*"); + puts("\t\t4: SPI_CLK_10MHZ*"); + puts("\t\t * Deprecated: use arbitrary value insteed"); + puts("\t\tn: arbitrary value in Hz"); puts("\tcs port:"); puts("\t\tPort of the CS pin, set to -1 for hardware chip select"); puts("\tcs pin:"); @@ -149,8 +168,7 @@ int cmd_init(int argc, char **argv) /* parse the given SPI device */ dev = atoi(argv[1]); - if (dev < 0 || dev >= (int)SPI_NUMOF) { - puts("error: invalid SPI device specified"); + if (_check_dev(dev)) { return 1; } spiconf.dev = SPI_DEV(dev); @@ -168,16 +186,23 @@ int cmd_init(int argc, char **argv) } /* parse the targeted clock speed */ +#if (ARCHITECTURE_WORD_BITS == 8) + clk = atol(argv[3]); +#else clk = atoi(argv[3]); +#endif switch (clk) { - case 0: spiconf.clk = SPI_CLK_100KHZ; break; - case 1: spiconf.clk = SPI_CLK_400KHZ; break; - case 2: spiconf.clk = SPI_CLK_1MHZ; break; - case 3: spiconf.clk = SPI_CLK_5MHZ; break; - case 4: spiconf.clk = SPI_CLK_10MHZ; break; + case 0: spiconf.clk = spi_get_clk(SPI_DEV(dev), SPI_CLK_100KHZ); break; + case 1: spiconf.clk = spi_get_clk(SPI_DEV(dev), SPI_CLK_400KHZ); break; + case 2: spiconf.clk = spi_get_clk(SPI_DEV(dev), SPI_CLK_1MHZ); break; + case 3: spiconf.clk = spi_get_clk(SPI_DEV(dev), SPI_CLK_5MHZ); break; + case 4: spiconf.clk = spi_get_clk(SPI_DEV(dev), SPI_CLK_10MHZ); break; default: - puts("error: invalid bus speed specified"); - return 1; + spiconf.clk = spi_get_clk(SPI_DEV(dev), clk); + } + if (spiconf.clk.err) { + puts("error: requested frequency too low"); + return 1; } /* parse chip select port and pin */ @@ -215,8 +240,10 @@ int cmd_init(int argc, char **argv) puts("cannot test configuration without assert()ions enabled"); } else { - printf("Trying to initialize SPI_DEV(%i): mode: %i, clk: %i, cs_port: %i, cs_pin: %i\n", - dev, mode, clk, port, pin); + printf("Trying to initialize SPI_DEV(%i): mode: %i, " + "clk: %"PRIu32"(%"PRIu32" Hz), cs_port: %i, cs_pin: %i\n", + dev, mode, + clk, spi_get_freq(SPI_DEV(dev), spiconf.clk), port, pin); puts("(if below the program crashes with a failed assertion, then it means the" " configuration is not supported)"); spi_acquire(spiconf.dev, spiconf.cs, spiconf.mode, spiconf.clk); @@ -529,6 +556,9 @@ int cmd_spi_gpio(int argc, char **argv) /* parse the given SPI device */ if (argc > 1) { dev = atoi(argv[1]); + if (_check_dev(dev)) { + return 1; + } } if (dev < 0 || dev >= (int)SPI_NUMOF) { @@ -567,13 +597,59 @@ int cmd_spi_gpio(int argc, char **argv) } #endif +int cmd_get_freq(int argc, char **argv) +{ + int dev; + uint32_t freq; + spi_clk_t clk; + + if (argc < 3) { + printf("usage: %s \n", argv[0]); + _dev_help(); + puts("\tfreq: arbitrary value in Hz"); + return 1; + } + + /* parse the given SPI device */ + dev = atoi(argv[1]); + if (_check_dev(dev)) { + return 1; + } + + /* parse the targeted clock speed */ +#if (ARCHITECTURE_WORD_BITS == 8) + freq = atol(argv[2]); +#else + freq = atoi(argv[2]); +#endif + + clk = spi_get_clk(SPI_DEV(dev), freq); + if (clk.err) { + puts("error: requested frequency too low"); + return 1; + } + printf("Requested frequency of %"PRIu32 + " Hz on SPI_DEV(%i) results in %"PRIu32" Hz\n", + freq, dev, spi_get_freq(SPI_DEV(dev), clk)); + return 0; +} + static const shell_command_t shell_commands[] = { { "init", "Setup a particular SPI configuration", cmd_init }, { "send", "Transfer string to slave", cmd_transfer }, { "bench", "Runs some benchmarks", cmd_bench }, #ifdef MODULE_PERIPH_SPI_RECONFIGURE - { "spi_gpio", "Re-configures MISO & MOSI pins to GPIO mode and back.", cmd_spi_gpio }, + { + "spi_gpio", + "Re-configures MISO & MOSI pins to GPIO mode and back.", + cmd_spi_gpio + }, #endif + { + "get_freq", + "Get the actual frequency in Hz corresponding to the given clock config", + cmd_get_freq + }, { NULL, NULL, NULL } };