From 4465c29d15cb49516bbc90d935593d460b80de63 Mon Sep 17 00:00:00 2001 From: Hugues Larrive Date: Fri, 13 Aug 2021 00:20:26 +0200 Subject: [PATCH] periph/spi: printing and testing SPI clock rates tests/periph_spi: printing and testing SPI clock rates drivers/periph_spi: change API of spi_acquire (from #15904) drivers/periph_spi: add the `bus` parameter to spi_get_*() This was necessary for implementations where multiple devices can have different clock sources. This broke the macros SPI_CLK_* that were reverted to an enum. periph/spi: adapted to the new API Arbitrary speed support was added to all implementations where it was missing. --- .github/workflows/tools-buildtest.yml | 2 - .../common/kw41z/include/periph_conf_common.h | 44 ---- boards/frdm-k22f/include/periph_conf.h | 105 ++++---- boards/frdm-k64f/include/periph_conf.h | 43 +--- boards/mulle/include/periph_conf.h | 39 --- .../openlabs-kw41z-mini/include/periph_conf.h | 39 --- boards/pba-d-01-kw2x/include/periph_conf.h | 39 --- cpu/atmega_common/include/periph_cpu_common.h | 23 +- cpu/atmega_common/periph/spi.c | 70 ++++- cpu/atxmega/include/periph_cpu.h | 14 - cpu/atxmega/periph/spi.c | 62 ++++- cpu/cc2538/include/periph_cpu.h | 39 --- cpu/cc2538/periph/spi.c | 43 +++- cpu/efm32/include/periph_cpu.h | 16 +- cpu/efm32/periph/spi.c | 68 +++++ cpu/esp32/periph/spi.c | 93 ++++--- cpu/esp8266/periph/spi.c | 87 ++++--- cpu/fe310/periph/spi.c | 45 ++-- cpu/kinetis/dist/calc_spi_scalers/Makefile | 12 - .../dist/calc_spi_scalers/calc_spi_scalers.c | 242 ------------------ cpu/kinetis/doc.txt | 17 +- cpu/kinetis/periph/spi.c | 166 +++++++++++- cpu/lm4f120/include/periph_cpu.h | 19 +- cpu/lm4f120/periph/spi.c | 41 +++ cpu/lpc23xx/include/periph_cpu.h | 16 -- cpu/lpc23xx/periph/spi.c | 52 +++- cpu/msp430fxyz/include/periph_cpu.h | 14 - cpu/msp430fxyz/periph/spi.c | 44 +++- cpu/native/include/periph_cpu.h | 22 -- cpu/native/periph/spidev_linux.c | 20 ++ cpu/nrf51/periph/spi.c | 46 ++++ cpu/nrf5x_common/include/periph_cpu_common.h | 14 - cpu/nrf5x_common/periph/spi_nrf52_nrf9160.c | 50 +++- cpu/qn908x/include/periph_cpu.h | 19 -- cpu/qn908x/periph/spi.c | 57 +++-- cpu/sam0_common/include/periph_cpu_common.h | 14 - cpu/sam0_common/periph/spi.c | 87 +++++-- cpu/sam3/include/periph_cpu.h | 14 - cpu/sam3/periph/spi.c | 39 ++- cpu/stm32/include/periph/cpu_spi.h | 19 -- cpu/stm32/periph/spi.c | 42 ++- drivers/include/periph/spi.h | 68 ++++- drivers/include/soft_spi.h | 49 +++- drivers/soft_spi/soft_spi.c | 14 + pkg/driver_bme680/contrib/bme680_hal.c | 18 +- pkg/driver_sx126x/contrib/driver_sx126x_hal.c | 29 ++- pkg/u8g2/contrib/u8x8_riotos.c | 25 +- pkg/ucglib/contrib/ucg_riotos.c | 26 +- pkg/uwb-dw1000/hal/uwb_dw1000_spi.c | 6 +- sys/arduino/SPI.cpp | 25 +- sys/arduino/include/spiport.hpp | 18 +- tests/drivers/nrf24l01p_lowlevel/main.c | 4 +- tests/drivers/soft_spi/main.c | 13 +- tests/periph/spi/main.c | 110 ++++++-- 54 files changed, 1278 insertions(+), 1064 deletions(-) delete mode 100644 cpu/kinetis/dist/calc_spi_scalers/Makefile delete mode 100644 cpu/kinetis/dist/calc_spi_scalers/calc_spi_scalers.c diff --git a/.github/workflows/tools-buildtest.yml b/.github/workflows/tools-buildtest.yml index 35bc9e8dded2..39012cf1c4d9 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 6eafe086951a..2d4fce31e10c 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 a4f590888770..4e082b0512a8 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 bc94d95423b8..818f8e5ab6a3 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 220ea1de7ea6..4be533fc8828 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 062a193d08fd..9ba8e19e0828 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 0bf510be4841..7d5b78c46be6 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 5d2b04f24024..792064fd72f3 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,13 @@ 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 { + 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 cac04cfdeb40..c6625331f5d0 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,6 +22,7 @@ * @author Hauke Petersen * @author Dimitri Nahm * @author Thomas Perrot + * @author Hugues Larrive * * @} */ @@ -29,17 +31,49 @@ #include "cpu.h" #include "mutex.h" #include "periph/spi.h" +#include "macros/units.h" + +#define ENABLE_DEBUG 0 +#include "debug.h" + +static mutex_t lock = MUTEX_INIT; /** * @brief Extract BR0, BR1 and SPI2X bits from speed value * @{ */ -#define CLK_MASK (0x3) -#define S2X_SHIFT (2) +/* Helper function to compute a right shift value corresponding to + * dividers */ +static uint8_t _clk_shift(uint32_t clk) +{ + /* 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; clk << 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,6 +112,30 @@ void spi_init_pins(spi_t bus) #endif } +spi_clk_t spi_get_clk(spi_t bus, uint32_t freq) +{ + (void)bus; + /* bound divider from 2 to 128 */ + if (freq > CLOCK_CORECLOCK / 2) { + freq = CLOCK_CORECLOCK / 2; + } + assert(freq >= CLOCK_CORECLOCK / 128); + + uint8_t shift = _clk_shift(freq); + return (spi_clk_t){ + .spi2x = ~shift & 1, + .spr = shift >> 1 + }; +} + +uint32_t spi_get_freq(spi_t bus, spi_clk_t clk) +{ + (void)bus; + 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; @@ -89,8 +147,8 @@ void spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk) 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 440f09266a9b..13fcce5561fb 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 690bf73717d2..7ef9371cbddd 100644 --- a/cpu/atxmega/periph/spi.c +++ b/cpu/atxmega/periph/spi.c @@ -28,6 +28,42 @@ #define ENABLE_DEBUG 0 #include "debug.h" +/** + * @brief Extract BR0, BR1 and SPI2X bits from speed value + * @{ + */ +/* Helper function to compute a right shift value corresponding to + * dividers */ +static uint8_t _clk_shift(uint32_t clk) +{ + /* 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; clk << 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,6 +123,27 @@ 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 from 2 to 128 */ + if (freq > CLOCK_CORECLOCK / 2) { + freq = CLOCK_CORECLOCK / 2; + } + assert(freq >= CLOCK_CORECLOCK / 128); + + uint8_t shift = _clk_shift(freq); + return ((~shift & 1) << 7) | (shift >> 1); +} + +uint32_t spi_get_freq(spi_t bus, spi_clk_t clk) +{ + (void)bus; + uint8_t shift = ((~clk & 0x80) >> 7) | (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; @@ -98,11 +155,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; (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 2ded24080c27..d72902d4c61c 100644 --- a/cpu/cc2538/include/periph_cpu.h +++ b/cpu/cc2538/include/periph_cpu.h @@ -247,47 +247,8 @@ typedef enum { SPI_MODE_3 = (SSI_CR0_SPO | SSI_CR0_SPH) /**< CPOL=1, CPHA=1 */ } spi_mode_t; /** @ */ - -/** - * @name Override SPI clock settings - * @{ - */ -#define HAVE_SPI_CLK_T -typedef enum { - SPI_CLK_100KHZ = 0, /**< drive the SPI bus with 100KHz */ - SPI_CLK_400KHZ = 1, /**< drive the SPI bus with 400KHz */ - SPI_CLK_1MHZ = 2, /**< drive the SPI bus with 1MHz */ - SPI_CLK_5MHZ = 3, /**< drive the SPI bus with 5MHz */ - SPI_CLK_10MHZ = 4 /**< drive the SPI bus with 10MHz */ -} spi_clk_t; -/** @} */ #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 edd67d97a6f4..bdb0e7d8c1ba 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 * * @} */ @@ -33,6 +35,9 @@ #define ENABLE_DEBUG 0 #include "debug.h" +/* DIV_UP is division which rounds up instead of down */ +#define SPI_DIV_UP(a, b) (((a) + ((b) - 1)) / (b)) + /** * @brief Array holding one pre-initialized mutex for each SPI device */ @@ -94,19 +99,53 @@ 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 from 2 to 65024 (254 * (255 + 1)) */ + if (freq > CLOCK_CORECLOCK / 2) { + freq = CLOCK_CORECLOCK / 2; + } + assert(freq >= CLOCK_CORECLOCK / 65024); + + uint8_t cpsr = 2, scr = 0; + uint32_t divider = SPI_DIV_UP(CLOCK_CORECLOCK, freq); + if (divider % 2) { + divider++; + } + while (divider / cpsr > 256) { + cpsr += 2; + } + scr = divider / cpsr - 1; + + return (cpsr << 8) | scr; +} + +uint32_t spi_get_freq(spi_t bus, spi_clk_t clk) +{ + (void)bus; + return CLOCK_CORECLOCK / ((clk >> 8) * ((clk & 0xff) + 1)); +} + void spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk) { assert((unsigned)bus < SPI_NUMOF); DEBUG("%s: bus=%u\n", __FUNCTION__, bus); (void)cs; + assert((unsigned)bus < SPI_NUMOF); /* 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 >> 8; + dev(bus)->CR0 = ((clk & 0xff) << 8) | 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 b60018246dc1..3ce0ae9c3c72 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 1b63a8e3fa03..29ab3ef76179 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,6 +18,7 @@ * @author Ryan Kurte * @author Bas Stottelaar * @author Christian Amsüss + * @author Hugues Larrive * @} */ @@ -35,6 +37,9 @@ #include "em_cmu.h" #include "em_usart.h" +/* DIV_UP is division which rounds up instead of down */ +#define SPI_DIV_UP(a, b) (((a) + ((b) - 1)) / (b)) + static mutex_t spi_lock[SPI_NUMOF]; void spi_init(spi_t bus) @@ -56,6 +61,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 from 2 to 65536 */ + if (freq > (CLOCK_CORECLOCK - 1) / 2) { + freq = (CLOCK_CORECLOCK - 1) / 2; + } + assert(freq >= SPI_DIV_UP(CLOCK_CORECLOCK, 65536)); + + /* ================================================================= + * 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_DIV_UP(ref_freq, (2 * (1 + (clkdiv >> 8)))); +} + +uint32_t spi_get_freq(spi_t bus, spi_clk_t clk) +{ + (void)bus; + return clk; +} + #define GET_PIN(x) (x & 0xf) #define GET_PORT(x) (x >> 4) #define USART_NUM(ref) ((ref == USART0) ? 0 : -1) diff --git a/cpu/esp32/periph/spi.c b/cpu/esp32/periph/spi.c index b2187824869d..b924c518a6f1 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 * * @} */ @@ -64,7 +66,7 @@ struct _spi_bus_t { 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 */ + uint32_t freq_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,7 @@ 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, + .freq_last = 0, .mode_last = UINT8_MAX, }, #endif @@ -86,7 +88,7 @@ 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, + .freq_last = 0, .mode_last = UINT8_MAX, }, #endif @@ -263,6 +265,56 @@ 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 + */ + + /* check whether timing has to be recalculated (time consuming) */ + if (freq != _spi[bus].freq_last) { + uint32_t apb_clk = rtc_clk_apb_freq_get(); + uint32_t clk_reg; + + if (apb_clk / 5 < freq) { + LOG_TAG_ERROR("spi", "APB clock rate (%"PRIu32" Hz) has to be at " + "least 5 times SPI clock rate (%d Hz)\n", + apb_clk, freq); + assert(false); + } + + /* duty cycle is measured in is 1/256th, 50% = 128 */ + int _freq = spi_ll_master_cal_clock(apb_clk, freq, + 128, &clk_reg); + + _spi[bus].freq_last = freq; + _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: freq=%d eff=%d " + "reg=%08"PRIx32"\n", + __func__, bus, freq, _freq, clk_reg); + + return clk_reg; + } + else { + return _spi[bus].timing.clock_reg; + } +} + +uint32_t IRAM_ATTR spi_get_freq(spi_t bus, spi_clk_t clk) +{ + (void)bus; + uint32_t apb_clk = rtc_clk_apb_freq_get(); + uint16_t spi_clkdiv_pre = (clk >> 18) & 0x1fff; + uint8_t spi_clkcnt_N = (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); @@ -299,39 +351,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); #if defined(CPU_FAM_ESP32C3) || defined(CPU_FAM_ESP32S3) /* diff --git a/cpu/esp8266/periph/spi.c b/cpu/esp8266/periph/spi.c index 4782593e9310..67779bd96c0a 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 * * @} */ @@ -44,6 +46,19 @@ #define SPI_BLOCK_SIZE 64 /* number of bytes per SPI transfer */ +/* DIV_UP is division which rounds up instead of down */ +#define SPI_DIV_UP(a, b) (((a) + ((b) - 1)) / (b)) + +/* 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 */ @@ -221,6 +236,35 @@ 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_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); + + /* bound divider from 2 to 8192 */ + if (freq > source_clock) { + freq = source_clock; + } + assert(freq >= SPI_DIV_UP(source_clock, 8192)); + return SPI_DIV_UP(source_clock, freq) - 1; +} + +uint32_t IRAM_ATTR spi_get_freq(spi_t bus, spi_clk_t clk) +{ + (void)bus; + return (SPI_CLK_SRC_FREQ / (clk + 1) / (CONST_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); @@ -257,40 +301,9 @@ 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 */ - } - - /* register values are set to deviders-1 */ - spi_clkdiv_pre--; - spi_clkcnt_N--; + DEBUG("%s spi_clkdiv_pre=%u spi_clkcnt_N=%u\n", + __func__, clk, CONST_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; @@ -298,10 +311,10 @@ void IRAM_ATTR spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t cl _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.clkdiv_pre = clk; + _spi[bus].regs->clock.clkcnt_n = CONST_SPI_CLKCNT_N; + _spi[bus].regs->clock.clkcnt_h = (CONST_SPI_CLKCNT_N + 1) / 2 - 1; + _spi[bus].regs->clock.clkcnt_l = CONST_SPI_CLKCNT_N; 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 f79c6b3346c4..d7a20abb2f96 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 * * @} */ @@ -34,21 +36,9 @@ #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 +52,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,6 +90,31 @@ 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 = cpu_freq() / 2; + + /* bound divider from 1 to 4096 */ + if (freq > source_clock) { + freq = source_clock; + } + assert(freq >= SPI_DIV_UP(source_clock, 4096)); + return SPI_DIV_UP(source_clock, freq) - 1; +} + +uint32_t spi_get_freq(spi_t bus, spi_clk_t clk) +{ + (void)bus; + return cpu_freq() / (2 * (clk + 1)); +} + void spi_acquire(spi_t dev, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk) { (void)cs; @@ -111,7 +122,7 @@ void spi_acquire(spi_t dev, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk) mutex_lock(&lock); - _REG32(spi_config[dev].addr, SPI_REG_SCKDIV) = _spi_clks_config[clk]; + _REG32(spi_config[dev].addr, SPI_REG_SCKDIV) = clk; _REG32(spi_config[dev].addr, SPI_REG_SCKMODE) = mode; } diff --git a/cpu/kinetis/dist/calc_spi_scalers/Makefile b/cpu/kinetis/dist/calc_spi_scalers/Makefile deleted file mode 100644 index 1ada319e2da4..000000000000 --- 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 2b1c19883209..000000000000 --- 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 343443684c3d..412a96f39442 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 193277f01d3f..cd59f31ef628 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 * * @} */ @@ -32,10 +34,14 @@ #include "mutex.h" #include "periph/gpio.h" #include "periph/spi.h" +#include "bitarithm.h" #define ENABLE_DEBUG 0 #include "debug.h" +/* DIV_UP is division which rounds up instead of down */ +#define SPI_DIV_UP(a, b) (((a) + ((b) - 1)) / (b)) + /** * @brief We use this mask to differentiate between SPI_HWCS() and GPIO_PIN() */ @@ -53,7 +59,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 +80,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 +126,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 +151,162 @@ 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 = SPI_DIV_UP(CLOCK_BUSCLOCK, freq); + uint32_t divider = 4, scaler = 2; + uint8_t prescaler = 2, pbr = 0, br, pdt, dt; + + /* bound divider from 4 to 229376 */ + if (freq > CLOCK_BUSCLOCK / 4) { + freq = CLOCK_BUSCLOCK / 4; + } + assert(freq >= SPI_DIV_UP(CLOCK_BUSCLOCK, 229376)); + + /* 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_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); +} + +uint32_t spi_get_freq(spi_t bus, spi_clk_t clk) +{ + (void)bus; + + 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 >> 16 & 0x3]; + int br = br_scalers[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); + /* 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); } void spi_release(spi_t bus) diff --git a/cpu/lm4f120/include/periph_cpu.h b/cpu/lm4f120/include/periph_cpu.h index 9d39286d8ba0..6da14ce49b9b 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 319c896c183a..da7b74941b75 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 * * @} */ @@ -30,6 +32,9 @@ #define ENABLE_DEBUG 0 #include "debug.h" +/* DIV_UP is division which rounds up instead of down */ +#define SPI_DIV_UP(a, b) (((a) + ((b) - 1)) / (b)) + /** * @brief Array holding one pre-initialized mutex for each SPI device */ @@ -67,12 +72,48 @@ 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 from 2 to 65024 (254 * (1 + 255)) */ + if (freq > sys_ctl_clock / 2) { + freq = sys_ctl_clock / 2; + } + assert(freq >= sys_ctl_clock / 65024); + + uint8_t cpsdvsr = 2, scr = 0; + uint32_t divider = SPI_DIV_UP(sys_ctl_clock, freq); + if (divider % 2) { + divider++; + } + while (divider / cpsdvsr > 256) { + cpsdvsr += 2; + } + scr = divider / cpsdvsr - 1; + + return SPI_DIV_UP(sys_ctl_clock, (cpsdvsr * (1 + scr))); +} + +uint32_t spi_get_freq(spi_t bus, spi_clk_t clk) +{ + (void)bus; + return 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); + /* lock bus */ mutex_lock(&locks[bus]); + /* enable clock for SSI */ ROM_SysCtlPeripheralEnable(spi_confs[bus].ssi_sysctl); diff --git a/cpu/lpc23xx/include/periph_cpu.h b/cpu/lpc23xx/include/periph_cpu.h index bb2429ed23d3..c9b36a81348a 100644 --- a/cpu/lpc23xx/include/periph_cpu.h +++ b/cpu/lpc23xx/include/periph_cpu.h @@ -144,22 +144,6 @@ typedef struct { #define PERIPH_SPI_NEEDS_TRANSFER_REGS /** @} */ -#ifndef DOXYGEN -/** - * @brief Override SPI clock speed values - * @{ - */ -#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 */ -} spi_clk_t; -/** @} */ -#endif /* ndef 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 4cf79e6a97e6..d192bd0bcd68 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,18 +23,23 @@ * * @author Kaspar Schleiser * @author Hauke Petersen + * @author Hugues Larrive * * @} */ +#include + #include "cpu.h" #include "mutex.h" -#include "assert.h" #include "periph/spi.h" #define ENABLE_DEBUG 0 #include "debug.h" +/* DIV_UP is division which rounds up instead of down */ +#define SPI_DIV_UP(a, b) (((a) + ((b) - 1)) / (b)) + /** * @brief Get the pointer to the base register of the given SPI device * @@ -95,6 +101,47 @@ 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 from 2 to 2032 */ + if (freq > source_clock) { + freq = source_clock; + } + assert(freq >= SPI_DIV_UP(source_clock, 2032)); + + /* result must be at most the requested frequency */ + freq = CLOCK_CORECLOCK / SPI_DIV_UP(CLOCK_CORECLOCK, freq); + + lpc23xx_pclk_scale(CLOCK_CORECLOCK, freq, &pclksel, &cpsr); + + return (pclksel<<10) | cpsr; +} + +uint32_t spi_get_freq(spi_t bus, spi_clk_t clk) +{ + (void)bus; + + uint32_t pclksel, pclkdiv, cpsr; + + pclksel = clk >> 8; + cpsr = clk & 0xff; + + switch (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 / cpsr; +} + void spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk) { (void)cs; (void)mode; @@ -116,7 +163,8 @@ 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); + pclksel = clk >> 8; + cpsr = clk & 0xff; switch ((uint32_t)dev) { case SSP0_BASE_ADDR: diff --git a/cpu/msp430fxyz/include/periph_cpu.h b/cpu/msp430fxyz/include/periph_cpu.h index d00643c433aa..a9897ba524d8 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 648dabb6ed14..757a6fcf448f 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 * * @} */ @@ -29,6 +31,9 @@ #include "mutex.h" #include "periph/spi.h" +/* DIV_UP is division which rounds up instead of down */ +#define SPI_DIV_UP(a, b) (((a) + ((b) - 1)) / (b)) + /** * @brief Mutex for locking the SPI device */ @@ -66,24 +71,45 @@ 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 + /* CLOCK_CMCLK / 2..65535 */ + /* bound divider from 2 to 65535 */ + if (freq > CLOCK_CMCLK / 2) { + freq = CLOCK_CMCLK / 2; + } +#else + /* CLOCK_CMCLK / 1..65535 */ + /* bound divider from 1 to 65535 */ + if (freq > CLOCK_CMCLK) { + freq = CLOCK_CMCLK; + } +#endif + assert(freq >= SPI_DIV_UP(CLOCK_CMCLK, 65535)); + return CLOCK_CMCLK / freq; +} + +uint32_t spi_get_freq(spi_t bus, spi_clk_t clk) +{ + (void)bus; + return CLOCK_CMCLK / 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); /* 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; + SPI_BASE->BR1 = (uint8_t)(clk >> 8); /* configure bus mode */ #ifndef SPI_USE_USCI diff --git a/cpu/native/include/periph_cpu.h b/cpu/native/include/periph_cpu.h index 668c877aee06..90a71bfa0061 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 7cb94cd28ca4..c1d62457f31d 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,6 +147,24 @@ 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 freq; +} + +uint32_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; +} + 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); diff --git a/cpu/nrf51/periph/spi.c b/cpu/nrf51/periph/spi.c index c3efd1e99d6b..2ab6cf2b2f4f 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,6 +62,50 @@ 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_FREQUENCY_FREQUENCY_M8; + } + if (freq >= MHZ(4)) { + return SPI_FREQUENCY_FREQUENCY_M4; + } + if (freq >= MHZ(2)) { + return SPI_FREQUENCY_FREQUENCY_M2; + } + if (freq >= MHZ(1)) { + return SPI_FREQUENCY_FREQUENCY_M1; + } + if (freq >= 500000) { + return SPI_FREQUENCY_FREQUENCY_K500; + } + if (freq >= 250000) { + return SPI_FREQUENCY_FREQUENCY_K250; + } + if (freq >= 125000) { + return SPI_FREQUENCY_FREQUENCY_K125; + } + assert(0); +} + +uint32_t spi_get_freq(spi_t bus, spi_clk_t clk) +{ + (void)bus; + + switch (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 250000; + } +} + void spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk) { (void)cs; diff --git a/cpu/nrf5x_common/include/periph_cpu_common.h b/cpu/nrf5x_common/include/periph_cpu_common.h index e2c5a81df70a..f9e31ab21d3c 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 e14e8d706245..a75e4c23aa1c 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,6 +183,50 @@ 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_FREQUENCY_FREQUENCY_M8; + } + if (freq >= MHZ(4)) { + return SPI_FREQUENCY_FREQUENCY_M4; + } + if (freq >= MHZ(2)) { + return SPI_FREQUENCY_FREQUENCY_M2; + } + if (freq >= MHZ(1)) { + return SPI_FREQUENCY_FREQUENCY_M1; + } + if (freq >= 500000) { + return SPI_FREQUENCY_FREQUENCY_K500; + } + if (freq >= 250000) { + return SPI_FREQUENCY_FREQUENCY_K250; + } + if (freq >= 125000) { + return SPI_FREQUENCY_FREQUENCY_K125; + } + assert(0); +} + +uint32_t spi_get_freq(spi_t bus, spi_clk_t clk) +{ + (void)bus; + + switch (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 250000; + } +} + void spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk) { (void)cs; diff --git a/cpu/qn908x/include/periph_cpu.h b/cpu/qn908x/include/periph_cpu.h index e7854b36dd35..40a3581d555c 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 ff4f114212a3..eed0aa40c1df 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 * * @} */ @@ -36,6 +38,9 @@ #define ENABLE_DEBUG 0 #include "debug.h" +/* DIV_UP is division which rounds up instead of down */ +#define SPI_DIV_UP(a, b) (((a) + ((b) - 1)) / (b)) + typedef struct { uint8_t *in; /**< The RX buffer pointer or NULL if unused. */ uint32_t in_len; /**< The remaining bytes to receive or 0 if unused. */ @@ -76,30 +81,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,6 +169,32 @@ 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 from 1 to 65536 */ + if (freq > bus_freq) { + freq = bus_freq; + } + assert(freq >= SPI_DIV_UP(bus_freq, 65536)); + + uint32_t divider = SPI_DIV_UP(bus_freq, freq); + + /* The value stored in DIV is always (divider - 1), meaning that a value of + * 0 divides by 1. */ + return (uint16_t)(divider - 1); +} + +uint32_t spi_get_freq(spi_t bus, spi_clk_t clk) +{ + (void)bus; + return CLOCK_GetFreq(kCLOCK_BusClk) / (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); @@ -198,7 +205,7 @@ void spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk) /* 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; 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/sam0_common/include/periph_cpu_common.h b/cpu/sam0_common/include/periph_cpu_common.h index ee425d144c2e..99285830f37f 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 ebed25f335d9..1b81ad927b5e 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 * * @} */ @@ -38,6 +40,9 @@ #define ENABLE_DEBUG 0 #include "debug.h" +/* DIV_UP is division which rounds up instead of down */ +#define SPI_DIV_UP(a, b) (((a) + ((b) - 1)) / (b)) + /** * @brief Threshold under which polling transfers are used instead of DMA * TODO: determine at run-time based on SPI clock @@ -183,9 +188,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 +200,45 @@ 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(spi_clk_t clk) { /* 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 CLOCK_CORECLOCK > (2 * clk) + ? SPI_DIV_UP(CLOCK_CORECLOCK, clk) + : 1; +} +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) | _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 +258,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,7 +284,7 @@ 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, spi_clk_t clk) { /* clock can't be higher than source clock */ uint32_t gclk_src = sam0_gclk_freq(spi_config[bus].gclk_src); @@ -278,13 +293,15 @@ static void _spi_acquire(spi_t bus, spi_mode_t mode, spi_clk_t clk) } /* 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 sam0_gclk_freq(spi_config[bus].gclk_src) > (2 * clk) + ? SPI_DIV_UP(sam0_gclk_freq(spi_config[bus].gclk_src), (2 * clk)) - 1 + : 0; +} +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 +313,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 || dev(bus)->CTRLA.reg != ctrla) { /* disable the device */ _disable(dev(bus)); - dev(bus)->BAUD.reg = baud; + dev(bus)->BAUD.reg = clk; dev(bus)->CTRLA.reg = ctrla; /* no synchronization needed here, the enable synchronization below * acts as a write-synchronization for both registers */ @@ -408,6 +425,34 @@ 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); + } + assert(baud < 256); + return baud; +} + +uint32_t spi_get_freq(spi_t bus, spi_clk_t clk) +{ + if (_is_qspi(bus)) { + /* SCK = MCK / (BAUD + 1) + * but assume SCK = MCK / BAUD as in _qspi_baud() */ + return CLOCK_CORECLOCK / clk; + } else { + /* fbaud = fref / 2 / (BAUD + 1) */ + return sam0_gclk_freq(spi_config[bus].gclk_src) / 2 / ++clk; + } +} + void spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk) { (void)cs; diff --git a/cpu/sam3/include/periph_cpu.h b/cpu/sam3/include/periph_cpu.h index 973460a4be10..013e4f46e16f 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 e542519dabbd..fbe2a647b01a 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 * * @} */ @@ -33,6 +35,9 @@ #define ENABLE_DEBUG 0 #include "debug.h" +/* DIV_UP is division which rounds up instead of down */ +#define SPI_DIV_UP(a, b) (((a) + ((b) - 1)) / (b)) + /** * @brief Array holding one pre-initialized mutex for each SPI device */ @@ -63,6 +68,26 @@ 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 divider from 1 to 255 */ + if (freq > CLOCK_CORECLOCK) { + freq = CLOCK_CORECLOCK; + } + assert(freq >= SPI_DIV_UP(CLOCK_CORECLOCK, 255)); + return SPI_DIV_UP(CLOCK_CORECLOCK, freq); +} + +uint32_t spi_get_freq(spi_t bus, spi_clk_t clk) +{ + (void)bus; + return CLOCK_CORECLOCK / clk; +} + void spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk) { (void)cs; @@ -73,7 +98,7 @@ void spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk) /* 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) | mode); dev(bus)->SPI_MR = (SPI_MR_MSTR | SPI_MR_MODFDIS); dev(bus)->SPI_CR = SPI_CR_SPIEN; } @@ -101,7 +126,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 +141,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 2ead5233e00e..592b2cafae1e 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 47e324eb9562..6a291159eda6 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; @@ -107,7 +99,7 @@ static uint8_t _get_clkdiv(const spi_conf_t *conf, uint32_t clock) * requested clock speed */ rounded_div++; } - return rounded_div > BR_MAX ? BR_MAX : rounded_div; + return rounded_div; } void spi_init(spi_t bus) @@ -221,6 +213,20 @@ 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) +{ + /* f_pclk / (1 << (BR + 1)) + * BR = 0..7 */ + uint8_t br = _get_clkdiv(&spi_config[bus], freq); + assert(br <= BR_MAX); + return br; +} + +uint32_t spi_get_freq(spi_t bus, spi_clk_t clk) +{ + return periph_apb_clk(spi_config[bus].apbbus) / (1 << (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); @@ -234,19 +240,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 << BR_SHIFT) | 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 5c88a7db076f..b1c1bd054fc3 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,15 @@ typedef uint_fast8_t spi_t; typedef gpio_t spi_cs_t; #endif +/** + * @brief Opaque type that contains a SPI clock configuration + * + * Use @ref spi_get_clk to obtain this value. + */ +#ifndef HAVE_SPI_CLK_T +typedef uword_t spi_clk_t; +#endif + /** * @brief Status codes used by the SPI driver interface * @@ -172,16 +185,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 +348,49 @@ 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 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 + */ +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 + * + * @return The exact frequency in Hertz matching the clock configuration + * + * @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. + */ +uint32_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 69860561b7f2..d61c5af4ef3a 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 83f67b53be48..826c30ac6562 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 fd977c393b45..e0770b902b09 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 4e9c2ea2656f..41531e5c4914 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 0730f9da01e8..df26b27727f4 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 043e573535f0..d4807d4b4521 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 be8bc8833d7d..18e856ba0c2a 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 d4f263c0605d..46ec305f9785 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 e5fe7d2c0ab1..20c75eef050a 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 459367f678a4..c1116fe7db5d 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 2423587e590e..ab85033a1cd7 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/main.c b/tests/periph/spi/main.c index b390239ea3c9..cc63fcd8e3e9 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,19 @@ 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); } /* parse chip select port and pin */ @@ -215,8 +236,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 +552,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 +593,53 @@ int cmd_spi_gpio(int argc, char **argv) } #endif +int cmd_get_freq(int argc, char **argv) +{ + int dev; + uint32_t clk; + + if (argc < 3) { + printf("usage: %s \n", argv[0]); + _dev_help(); + puts("\tclk: 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) + clk = atol(argv[2]); +#else + clk = atoi(argv[2]); +#endif + + printf("Requested frequency of %"PRIu32 + " Hz on SPI_DEV(%i) results in %"PRIu32" Hz\n", + clk, dev, spi_get_freq(SPI_DEV(dev), spi_get_clk(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 } };