Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:23:38

0001 /* SPDX-License-Identifier: BSD-2-Clause */
0002 
0003 /**
0004  * @file
0005  *
0006  * @ingroup RTEMSBSPsARMSTM32H7
0007  *
0008  * @brief This source file contains the shared SPI support code.
0009  */
0010 
0011 /*
0012  * Copyright (C) 2024 On-Line Applications Research (OAR) Corporation
0013  *
0014  * Redistribution and use in source and binary forms, with or without
0015  * modification, are permitted provided that the following conditions
0016  * are met:
0017  * 1. Redistributions of source code must retain the above copyright
0018  *    notice, this list of conditions and the following disclaimer.
0019  * 2. Redistributions in binary form must reproduce the above copyright
0020  *    notice, this list of conditions and the following disclaimer in the
0021  *    documentation and/or other materials provided with the distribution.
0022  *
0023  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0024  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0025  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0026  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
0027  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
0028  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
0029  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
0030  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
0031  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
0032  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
0033  * POSSIBILITY OF SUCH DAMAGE.
0034  */
0035 
0036 #ifdef HAVE_CONFIG_H
0037 #include "config.h"
0038 #endif
0039 
0040 #include <bsp.h>
0041 #include <bsp/fatal.h>
0042 #include <inttypes.h>
0043 #include <rtems/bspIo.h>
0044 #include <rtems/sysinit.h>
0045 #include <stdio.h>
0046 #include <stm32h7xx_hal_dma.h>
0047 #include <stm32h7xx_hal_spi.h>
0048 
0049 #include <stm32h7/hal.h>
0050 #include <rtems/score/prioritybitmapimpl.h>
0051 
0052 #define STM32H7_SPI_COMPLETE 0
0053 #define STM32H7_SPI_ERROR 1
0054 #define SPI_TIMEOUT_MS 100
0055 
0056 /* NULLs are included for disabled devices to preserve index */
0057 static stm32h7_spi_context * const stm32h7_spi_instances[] = {
0058 #ifdef STM32H7_SPI1_ENABLE
0059  #ifdef SPI1
0060   &stm32h7_spi1_instance,
0061  #else
0062   #error SPI1 configured, but not available
0063  #endif
0064 #else
0065   NULL,
0066 #endif
0067 
0068 #ifdef STM32H7_SPI2_ENABLE
0069  #ifdef SPI2
0070   &stm32h7_spi2_instance,
0071  #else
0072   #error SPI2 configured, but not available
0073  #endif
0074 #else
0075   NULL,
0076 #endif
0077 
0078 #ifdef STM32H7_SPI3_ENABLE
0079  #ifdef SPI3
0080   &stm32h7_spi3_instance,
0081  #else
0082   #error SPI3 configured, but not available
0083  #endif
0084 #else
0085   NULL,
0086 #endif
0087 
0088 #ifdef STM32H7_SPI4_ENABLE
0089  #ifdef SPI4
0090   &stm32h7_spi4_instance,
0091  #else
0092   #error SPI4 configured, but not available
0093  #endif
0094 #else
0095   NULL,
0096 #endif
0097 
0098 #ifdef STM32H7_SPI5_ENABLE
0099  #ifdef SPI5
0100   &stm32h7_spi5_instance,
0101  #else
0102   #error SPI5 configured, but not available
0103  #endif
0104 #else
0105   NULL,
0106 #endif
0107 
0108 #ifdef STM32H7_SPI6_ENABLE
0109  #ifdef SPI6
0110   &stm32h7_spi6_instance,
0111  #else
0112   #error SPI6 configured, but not available
0113  #endif
0114 #else
0115   NULL,
0116 #endif
0117 
0118   /* NULL is included for consistent use of commas above */
0119   NULL
0120 };
0121 
0122 static int stm32h7_spi_set_prescaler(stm32h7_spi_context *ctx, uint32_t speed_hz)
0123 {
0124   uint32_t prescaler_mask = SPI_BAUDRATEPRESCALER_256;
0125 
0126   /* check speed against max divider (2 is implicit in max_speed_hz) */
0127   if (speed_hz < (ctx->bus.max_speed_hz / 128)) {
0128     /* clock rate request too low */
0129     return 1;
0130   }
0131 
0132   if (speed_hz > ctx->bus.max_speed_hz) {
0133     ctx->spi.Instance->CFG1 &= ~prescaler_mask;
0134     ctx->spi.Instance->CFG1 |= SPI_BAUDRATEPRESCALER_2;
0135   } else {
0136     uint32_t divider = 2 * ctx->bus.max_speed_hz / speed_hz;
0137     uint32_t remainder = (2 * ctx->bus.max_speed_hz) % speed_hz;
0138     uint32_t prescaler_value;
0139     if (divider > 256) {
0140       /* not able to divide enough to accomodate clock rate request */
0141       return 1;
0142     }
0143     /* prescaler values with scale factor N are (log2(N)-1) << 24 */
0144     prescaler_value = 7 - _Bitfield_Leading_zeros[divider & 0xff];
0145     if (remainder) {
0146       prescaler_value++;
0147     }
0148     if (prescaler_value > SPI_BAUDRATEPRESCALER_256 >> 24) {
0149       /* not able to divide enough to accomodate clock rate request */
0150       return 1;
0151     }
0152     prescaler_value <<= 28;
0153     ctx->spi.Instance->CFG1 &= ~prescaler_mask;
0154     ctx->spi.Instance->CFG1 |= prescaler_value;
0155   }
0156 
0157   return 0;
0158 }
0159 
0160 static int stm32h7_spi_set_bpw(stm32h7_spi_context *ctx, uint32_t bits_per_word)
0161 {
0162   uint32_t bits_per_word_mask = SPI_DATASIZE_32BIT;
0163 
0164   if (bits_per_word < 4 || bits_per_word > 32) {
0165     return 1;
0166   }
0167 
0168   /*
0169    * bits per word starts at 4 bpw with register value 3 and counts up to 32 bpw
0170    * with register value 0x1F (31)
0171    */
0172   ctx->spi.Instance->CFG1 &= ~bits_per_word_mask;
0173   ctx->spi.Instance->CFG1 |= (bits_per_word - 1);
0174 
0175   return 0;
0176 }
0177 
0178 static void stm32h7_spi_set_mode(stm32h7_spi_context *ctx, uint32_t mode)
0179 {
0180   uint32_t mode_mask = SPI_POLARITY_HIGH | SPI_PHASE_2EDGE;
0181   ctx->spi.Instance->CFG2 &= ~mode_mask;
0182   if (mode & SPI_CPOL) {
0183     ctx->spi.Instance->CFG2 |= SPI_POLARITY_HIGH;
0184   }
0185   if (mode & SPI_CPHA) {
0186     ctx->spi.Instance->CFG2 |= SPI_PHASE_2EDGE;
0187   }
0188 
0189 }
0190 
0191 static int stm32h7_spi_setup(spi_bus *base)
0192 {
0193   stm32h7_spi_context *ctx = RTEMS_CONTAINER_OF(base, stm32h7_spi_context, bus);
0194 
0195   if (stm32h7_spi_set_prescaler(ctx, ctx->bus.speed_hz)) {
0196     return -EINVAL;
0197   }
0198 
0199   if (stm32h7_spi_set_bpw(ctx, ctx->bus.bits_per_word)) {
0200     return -EINVAL;
0201   }
0202 
0203   stm32h7_spi_set_mode(ctx, ctx->bus.mode);
0204 
0205   return 0;
0206 }
0207 
0208 static void stm32h7_spi_destroy(spi_bus *base)
0209 {
0210   stm32h7_spi_context *ctx = RTEMS_CONTAINER_OF(base, stm32h7_spi_context, bus);
0211 
0212 #ifdef STM32H7_SPI_USE_INTERRUPTS
0213   rtems_binary_semaphore_destroy(&ctx->sem);
0214 #endif
0215   HAL_SPI_DeInit(&ctx->spi);
0216 
0217   spi_bus_destroy(base);
0218 }
0219 
0220 static int stm32h7_spi_get_chip_select(
0221   stm32h7_spi_context *ctx,
0222   uint8_t cs,
0223   GPIO_TypeDef **gpio,
0224   uint16_t *pin)
0225 {
0226   const stm32h7_gpio_config *cs_gpio;
0227   if (cs >= STM32H7_NUM_SOFT_CS) {
0228     return 1;
0229   }
0230   cs_gpio = &ctx->config->cs_gpio[cs];
0231   if (cs_gpio->regs == NULL || cs_gpio->config.Pin == 0) {
0232     /* The requested chip select is not configured */
0233     return 1;
0234   }
0235   *gpio = cs_gpio->regs;
0236   *pin = cs_gpio->config.Pin;
0237   return 0;
0238 }
0239 
0240 static int stm32h7_spi_apply_premessage_settings(
0241   stm32h7_spi_context *ctx,
0242   const spi_ioc_transfer *msg
0243 )
0244 {
0245   uint32_t mode_width = SPI_TX_DUAL | SPI_TX_QUAD | SPI_RX_DUAL | SPI_RX_QUAD;
0246 
0247   if (msg->rx_nbits > 1 || msg->tx_nbits > 1 || (msg->mode & mode_width)) {
0248     /* This device does not support dual or quad SPI */
0249     return 1;
0250   }
0251 
0252   if (stm32h7_spi_set_prescaler(ctx, msg->speed_hz)) {
0253     return 1;
0254   }
0255 
0256   if (stm32h7_spi_set_bpw(ctx, msg->bits_per_word)) {
0257     return 1;
0258   }
0259 
0260   stm32h7_spi_set_mode(ctx, msg->mode);
0261 
0262   GPIO_TypeDef *gpio_block = NULL;
0263   uint16_t gpio_pin = 0;
0264   if (stm32h7_spi_get_chip_select(ctx, msg->cs, &gpio_block, &gpio_pin)) {
0265     /* Selected GPIO pin not available */
0266     return 1;
0267   }
0268   /* pull chip select low to activate selected device */
0269   HAL_GPIO_WritePin(gpio_block, gpio_pin, GPIO_PIN_RESET);
0270   return 0;
0271 }
0272 
0273 static void stm32h7_spi_apply_postmessage_settings(
0274   stm32h7_spi_context *ctx,
0275   const spi_ioc_transfer *msg,
0276   bool final
0277 )
0278 {
0279   usleep(msg->delay_usecs);
0280   if (msg->cs_change || final) {
0281     GPIO_TypeDef *gpio_block = NULL;
0282     uint16_t gpio_pin = 0;
0283 
0284     /*
0285      * It shouldn't be possible for this to fail since it was already checked in
0286      * the premessage application
0287      */
0288     (void) stm32h7_spi_get_chip_select(ctx, msg->cs, &gpio_block, &gpio_pin);
0289     /* bring chip select high */
0290     HAL_GPIO_WritePin(gpio_block, gpio_pin, GPIO_PIN_SET);
0291   }
0292 }
0293 
0294 static int stm32h7_spi_transfer_wait(stm32h7_spi_context *ctx, uint32_t timeout_ms)
0295 {
0296 #ifdef STM32H7_SPI_USE_INTERRUPTS
0297   int status;
0298   uint32_t timeout_ticks = timeout_ms;
0299 
0300   timeout_ticks /= rtems_configuration_get_milliseconds_per_tick();
0301 
0302   status = rtems_binary_semaphore_wait_timed_ticks(&ctx->sem, timeout_ticks);
0303   if (status != 0) {
0304     return -ETIME;
0305   }
0306   if (ctx->error == STM32H7_SPI_ERROR) {
0307     return -EIO;
0308   }
0309 #else
0310   (void) timeout_ms;
0311 #endif
0312   return 0;
0313 }
0314 
0315 static int hal_status_to_errno(HAL_StatusTypeDef status)
0316 {
0317   _Assert(status != HAL_OK);
0318 
0319   switch (status) {
0320   case HAL_BUSY:
0321     return -EBUSY;
0322   case HAL_ERROR:
0323     return -EINVAL;
0324   case HAL_TIMEOUT:
0325     return -ETIME;
0326   case HAL_OK:
0327     return -EIO;
0328   }
0329 
0330   return -EIO;
0331 }
0332 
0333 static int stm32h7_spi_txrx(
0334   stm32h7_spi_context *ctx,
0335   const uint8_t *pTxData,
0336   uint8_t *pRxData,
0337   uint16_t Size
0338 )
0339 {
0340   HAL_StatusTypeDef status;
0341 
0342 #ifdef STM32H7_SPI_USE_INTERRUPTS
0343   status = HAL_SPI_TransmitReceive_IT(&ctx->spi, pTxData, pRxData, Size);
0344 #else
0345   status = HAL_SPI_TransmitReceive(
0346     &ctx->spi, pTxData, pRxData, Size, SPI_TIMEOUT_MS
0347   );
0348 #endif
0349   if (status != HAL_OK) {
0350     return hal_status_to_errno(status);
0351   }
0352 
0353   return stm32h7_spi_transfer_wait(ctx, SPI_TIMEOUT_MS);
0354 }
0355 
0356 static int stm32h7_spi_tx(
0357   stm32h7_spi_context *ctx,
0358   const uint8_t *pData,
0359   uint16_t Size
0360 )
0361 {
0362   HAL_StatusTypeDef status;
0363 
0364 #ifdef STM32H7_SPI_USE_INTERRUPTS
0365   status = HAL_SPI_Transmit_IT(&ctx->spi, pData, Size);
0366 #else
0367   status = HAL_SPI_Transmit(&ctx->spi, pData, Size, SPI_TIMEOUT_MS);
0368 #endif
0369   if (status != HAL_OK) {
0370     return hal_status_to_errno(status);
0371   }
0372 
0373   return stm32h7_spi_transfer_wait(ctx, SPI_TIMEOUT_MS);
0374 }
0375 
0376 static int stm32h7_spi_rx(
0377   stm32h7_spi_context *ctx,
0378   uint8_t *pData,
0379   uint16_t Size
0380 )
0381 {
0382   HAL_StatusTypeDef status;
0383 
0384 #ifdef STM32H7_SPI_USE_INTERRUPTS
0385   status = HAL_SPI_Receive_IT(&ctx->spi, pData, Size);
0386 #else
0387   status = HAL_SPI_Receive(&ctx->spi, pData, Size, SPI_TIMEOUT_MS);
0388 #endif
0389   if (status != HAL_OK) {
0390     return hal_status_to_errno(status);
0391   }
0392 
0393   return stm32h7_spi_transfer_wait(ctx, SPI_TIMEOUT_MS);
0394 }
0395 
0396 static int stm32h7_spi_transfer(
0397   spi_bus *base,
0398   const spi_ioc_transfer *msgs,
0399   uint32_t msg_count
0400 )
0401 {
0402   stm32h7_spi_context *ctx = RTEMS_CONTAINER_OF(base, stm32h7_spi_context, bus);
0403 
0404   for (int i = 0; i < msg_count; i++) {
0405     const spi_ioc_transfer *msg = &msgs[i];
0406 
0407     if (stm32h7_spi_apply_premessage_settings(ctx, msg)) {
0408       return -EINVAL;
0409     }
0410     /* perform transfer */
0411     if (msg->tx_buf != NULL && msg->rx_buf != NULL) {
0412       int ret = stm32h7_spi_txrx(ctx, msg->tx_buf, msg->rx_buf, msg->len);
0413       if (ret != 0) {
0414         return ret;
0415       }
0416     } else if (msg->tx_buf != NULL) {
0417       int ret = stm32h7_spi_tx(ctx, msg->tx_buf, msg->len);
0418       if (ret != 0) {
0419         return ret;
0420       }
0421     } else if (msg->rx_buf != NULL) {
0422       int ret = stm32h7_spi_rx(ctx, msg->rx_buf, msg->len);
0423       if (ret != 0) {
0424         return ret;
0425       }
0426     }
0427     /* set final to true on last iteration */
0428     stm32h7_spi_apply_postmessage_settings(ctx, msg, i == msg_count);
0429   }
0430 
0431   return 0;
0432 }
0433 
0434 void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *spi)
0435 {
0436 #ifdef STM32H7_SPI_USE_INTERRUPTS
0437   stm32h7_spi_context *ctx = RTEMS_CONTAINER_OF(spi, stm32h7_spi_context, spi);
0438 
0439   ctx->error = STM32H7_SPI_ERROR;
0440   rtems_binary_semaphore_post(&ctx->sem);
0441 #endif
0442 }
0443 
0444 void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *spi)
0445 {
0446 #ifdef STM32H7_SPI_USE_INTERRUPTS
0447   stm32h7_spi_context *ctx = RTEMS_CONTAINER_OF(spi, stm32h7_spi_context, spi);
0448 
0449   ctx->error = STM32H7_SPI_COMPLETE;
0450   rtems_binary_semaphore_post(&ctx->sem);
0451 #endif
0452 }
0453 
0454 void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *spi)
0455 {
0456 #ifdef STM32H7_SPI_USE_INTERRUPTS
0457   stm32h7_spi_context *ctx = RTEMS_CONTAINER_OF(spi, stm32h7_spi_context, spi);
0458 
0459   ctx->error = STM32H7_SPI_COMPLETE;
0460   rtems_binary_semaphore_post(&ctx->sem);
0461 #endif
0462 }
0463 
0464 void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *spi)
0465 {
0466 #ifdef STM32H7_SPI_USE_INTERRUPTS
0467   stm32h7_spi_context *ctx = RTEMS_CONTAINER_OF(spi, stm32h7_spi_context, spi);
0468 
0469   ctx->error = STM32H7_SPI_COMPLETE;
0470   rtems_binary_semaphore_post(&ctx->sem);
0471 #endif
0472 }
0473 
0474 #ifdef STM32H7_SPI_USE_INTERRUPTS
0475 static void stm32h7_spi_irq_handler(void *arg)
0476 {
0477   stm32h7_spi_context *ctx = arg;
0478 
0479   HAL_SPI_IRQHandler(&ctx->spi);
0480 }
0481 #endif
0482 
0483 static int stm32h7_register_spi_device(
0484   stm32h7_spi_context *ctx,
0485   uint8_t device_index
0486 )
0487 {
0488   char path[sizeof("/dev/spiXXX")];
0489   int rv;
0490 #ifdef STM32H7_SPI_USE_INTERRUPTS
0491   rtems_status_code sc;
0492 #endif
0493   spi_bus *bus = &ctx->bus;
0494 
0495   rv = spi_bus_init(bus);
0496   if (rv) {
0497     return rv;
0498   }
0499 
0500   bus->transfer = stm32h7_spi_transfer;
0501   bus->destroy = stm32h7_spi_destroy;
0502   bus->setup = stm32h7_spi_setup;
0503   /*
0504    * Max speed for these peripherals is 150MHz, but other clock limitations
0505    * determined by the BSP clock configuration bring that down. The minimum
0506    * required SPI internal divider is 2 which should be accounted for in the
0507    * configuration's max_speed_hz parameter.
0508    */
0509   bus->max_speed_hz = ctx->config->max_speed_hz;
0510   /*
0511    * The stm32h7 SPI peripherals support a single hardware chip select which is
0512    * not required to be routed to a pin by the configuration since peripheral
0513    * drivers using the SPI bus will often need to use GPIO to enable
0514    * peripherals. Since any hardware chip select pin can also be used as GPIO,
0515    * all chip selects are used in GPIO mode.
0516    */
0517   bus->speed_hz = bus->max_speed_hz;
0518   bus->cs_change = 0;
0519   bus->cs = 0;
0520   bus->bits_per_word = ctx->spi.Init.DataSize + 1;
0521   bus->lsb_first = false;
0522   if (ctx->spi.Init.FirstBit == SPI_FIRSTBIT_LSB) {
0523     bus->lsb_first = true;
0524   }
0525   bus->mode = 0;
0526   if (ctx->spi.Init.CLKPolarity == SPI_POLARITY_HIGH) {
0527     bus->mode |= SPI_CPOL;
0528   }
0529   if (ctx->spi.Init.CLKPhase == SPI_PHASE_2EDGE) {
0530     bus->mode |= SPI_CPHA;
0531   }
0532 
0533   if (HAL_SPI_Init(&ctx->spi)) {
0534     return 1;
0535   }
0536 
0537 #ifdef STM32H7_SPI_USE_INTERRUPTS
0538   /* Configure interrupt */
0539   rtems_interrupt_entry_initialize(
0540     &ctx->spi_irq_entry,
0541     stm32h7_spi_irq_handler,
0542     ctx,
0543     "SPI"
0544   );
0545   sc = rtems_interrupt_entry_install(
0546     ctx->irq,
0547     RTEMS_INTERRUPT_SHARED,
0548     &ctx->spi_irq_entry
0549   );
0550   if (sc != RTEMS_SUCCESSFUL) {
0551     return false;
0552   }
0553   rtems_binary_semaphore_init(&ctx->sem, "STM32H7 SPI");
0554 #endif
0555 
0556   snprintf(path, sizeof(path), "/dev/spi%" PRIu8, device_index);
0557   rv = spi_bus_register(bus, path);
0558   if (rv) {
0559     return rv;
0560   }
0561 
0562   return 0;
0563 }
0564 
0565 void stm32h7_register_spi_devices(void)
0566 {
0567   int i;
0568 
0569   for (i = 0; i < (RTEMS_ARRAY_SIZE(stm32h7_spi_instances)); i++) {
0570     if (stm32h7_spi_instances[i] == NULL) {
0571       continue;
0572     }
0573     if (stm32h7_register_spi_device(stm32h7_spi_instances[i], i)) {
0574       bsp_fatal(STM32H7_FATAL_MMU_CANNOT_REGISTER_SPI);
0575     }
0576   }
0577 }