Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cpu/stm32/periph_spi: Fix /CS handling #20084

Merged
merged 4 commits into from
Nov 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions cpu/stm32/include/periph_cpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,36 @@ typedef struct {
#define USBDEV_NUM_ENDPOINTS 8
#endif

/* unify names across STM32 families */
#ifdef SPI_CR1_CPHA_Msk
# define STM32_SPI_CPHA_Msk SPI_CR1_CPHA_Msk
#endif
#ifdef SPI_CFG2_CPHA_Msk
# define STM32_SPI_CPHA_Msk SPI_CFG2_CPHA_Msk
#endif
#ifdef SPI_CR1_CPOL_Msk
# define STM32_SPI_CPOL_Msk SPI_CR1_CPOL_Msk
#endif
#ifdef SPI_CFG2_CPOL_Msk
# define STM32_SPI_CPOL_Msk SPI_CFG2_CPOL_Msk
#endif

/**
* @name Override the SPI mode values
*
* As the mode is set in bit 3 and 2 of the configuration register, we put the
* correct configuration there
* @{
*/
#define HAVE_SPI_MODE_T
typedef enum {
SPI_MODE_0 = 0, /**< CPOL=0, CPHA=0 */
SPI_MODE_1 = STM32_SPI_CPHA_Msk, /**< CPOL=0, CPHA=1 */
SPI_MODE_2 = STM32_SPI_CPOL_Msk, /**< CPOL=1, CPHA=0 */
SPI_MODE_3 = STM32_SPI_CPOL_Msk | STM32_SPI_CPHA_Msk, /**< CPOL=1, CPHA=0 */
} spi_mode_t;
/** @} */

#endif /* !DOXYGEN */

#ifdef __cplusplus
Expand Down
84 changes: 37 additions & 47 deletions cpu/stm32/periph/spi.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@

#include <assert.h>

#include "bitarithm.h"
#include "cpu.h"
#include "mutex.h"
#include "periph/gpio.h"
#include "periph/spi.h"
Expand Down Expand Up @@ -65,9 +63,9 @@ static mutex_t locks[SPI_NUMOF];
static uint32_t clocks[SPI_NUMOF];

/**
* @brief Clock divider cache
* @brief Clock prescaler cache
*/
static uint8_t dividers[SPI_NUMOF];
static uint8_t prescalers[SPI_NUMOF];

static inline SPI_TypeDef *dev(spi_t bus)
{
Expand All @@ -81,33 +79,24 @@ static inline bool _use_dma(const spi_conf_t *conf)
}
#endif

/**
* @brief Multiplier for clock divider calculations
*
* Makes the divider calculation fixed point
*/
#define SPI_APB_CLOCK_SHIFT (4U)
#define SPI_APB_CLOCK_MULT (1U << SPI_APB_CLOCK_SHIFT)

static uint8_t _get_clkdiv(const spi_conf_t *conf, uint32_t clock)
static uint8_t _get_prescaler(const spi_conf_t *conf, uint32_t clock)
{
uint32_t bus_clock = periph_apb_clk(conf->apbbus);
/* Shift bus_clock with SPI_APB_CLOCK_SHIFT to create a fixed point int */
uint32_t div = (bus_clock << SPI_APB_CLOCK_SHIFT) / (2 * clock);
DEBUG("[spi] clock: divider: %"PRIu32"\n", div);
/* Test if the divider is 2 or smaller, keeping the fixed point in mind */
if (div <= SPI_APB_CLOCK_MULT) {
return 0;
}
/* determine MSB and compensate back for the fixed point int shift */
uint8_t rounded_div = bitarithm_msb(div) - SPI_APB_CLOCK_SHIFT;
/* Determine if rounded_div is not a power of 2 */
if ((div & (div - 1)) != 0) {
/* increment by 1 to ensure that the clock speed at most the
* requested clock speed */
rounded_div++;
}
return rounded_div > BR_MAX ? BR_MAX : rounded_div;

uint8_t prescaler = 0;
uint32_t prescaled_clock = bus_clock >> 1;
const uint8_t prescaler_max = SPI_CR1_BR_Msk >> SPI_CR1_BR_Pos;
for (; (prescaled_clock > clock) && (prescaler < prescaler_max); prescaler++) {
prescaled_clock >>= 1;
}

/* If the callers asks for an SPI frequency of at most x, bad things will
* happen if this cannot be met. So let's have a blown assertion
* rather than runtime failures that require a logic analyzer to
* debug. */
assert(prescaled_clock <= clock);

return prescaler;
}

void spi_init(spi_t bus)
Expand Down Expand Up @@ -235,30 +224,30 @@ void spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk)
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);
prescalers[bus] = _get_prescaler(&spi_config[bus], clk);
clocks[bus] = clk;
}
uint8_t br = dividers[bus];
uint8_t br = prescalers[bus];

DEBUG("[spi] acquire: requested clock: %"PRIu32", resulting clock: %"PRIu32
" BR divider: %u\n",
DEBUG("[spi] acquire: requested clock: %" PRIu32
" Hz, resulting clock: %" PRIu32 " Hz, BR prescaler: %u\n",
clk,
periph_apb_clk(spi_config[bus].apbbus)/(1 << (br + 1)),
br);
periph_apb_clk(spi_config[bus].apbbus) >> (br + 1),
(unsigned)br);

uint16_t cr1_settings = ((br << BR_SHIFT) | mode | SPI_CR1_MSTR);
uint16_t cr1 = ((br << BR_SHIFT) | mode | SPI_CR1_MSTR | SPI_CR1_SPE);
/* Settings to add to CR2 in addition to SPI_CR2_SETTINGS */
uint16_t cr2_extra_settings = 0;
uint16_t cr2 = SPI_CR2_SETTINGS;
if (cs != SPI_HWCS_MASK) {
cr1_settings |= (SPI_CR1_SSM | SPI_CR1_SSI);
cr1 |= (SPI_CR1_SSM | SPI_CR1_SSI);
}
else {
cr2_extra_settings = (SPI_CR2_SSOE);
cr2 = (SPI_CR2_SSOE);
}

#ifdef MODULE_PERIPH_DMA
if (_use_dma(&spi_config[bus])) {
cr2_extra_settings |= SPI_CR2_TXDMAEN | SPI_CR2_RXDMAEN;
cr2 |= SPI_CR2_TXDMAEN | SPI_CR2_RXDMAEN;

dma_acquire(spi_config[bus].tx_dma);
dma_setup(spi_config[bus].tx_dma,
Expand All @@ -277,11 +266,8 @@ void spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk)
0);
}
#endif
dev(bus)->CR1 = cr1_settings;
/* Only modify CR2 if needed */
if (cr2_extra_settings) {
dev(bus)->CR2 = (SPI_CR2_SETTINGS | cr2_extra_settings);
}
dev(bus)->CR1 = cr1;
dev(bus)->CR2 = cr2;
}

void spi_release(spi_t bus)
Expand Down Expand Up @@ -396,10 +382,12 @@ void spi_transfer_bytes(spi_t bus, spi_cs_t cs, bool cont,
assert(out || in);

/* active the given chip select line */
dev(bus)->CR1 |= (SPI_CR1_SPE); /* this pulls the HW CS line low */
if ((cs != SPI_HWCS_MASK) && gpio_is_valid(cs)) {
gpio_clear((gpio_t)cs);
}
else {
dev(bus)->CR2 |= SPI_CR2_SSOE;
}

#ifdef MODULE_PERIPH_DMA
if (_use_dma(&spi_config[bus])) {
Expand All @@ -414,9 +402,11 @@ void spi_transfer_bytes(spi_t bus, spi_cs_t cs, bool cont,

/* release the chip select if not specified differently */
if ((!cont) && gpio_is_valid(cs)) {
dev(bus)->CR1 &= ~(SPI_CR1_SPE); /* pull HW CS line high */
if (cs != SPI_HWCS_MASK) {
gpio_set((gpio_t)cs);
}
else {
dev(bus)->CR2 &= ~(SPI_CR2_SSOE);
}
}
}