diff --git a/drivers/can/Kconfig.mcan b/drivers/can/Kconfig.mcan index e5c00f9758b98..60017173159ca 100644 --- a/drivers/can/Kconfig.mcan +++ b/drivers/can/Kconfig.mcan @@ -7,6 +7,7 @@ config CAN_MCAN bool select CAN_HAS_CANFD select CAN_HAS_RX_TIMESTAMP + select IO32_MEM help Enable Bosch m_can driver. This driver supports the Bosch m_can IP. This IP is built into the diff --git a/drivers/can/can_mcan.c b/drivers/can/can_mcan.c index ac73684a51c4c..5efbbe4914ae6 100644 --- a/drivers/can/can_mcan.c +++ b/drivers/can/can_mcan.c @@ -21,21 +21,6 @@ LOG_MODULE_REGISTER(can_mcan, CONFIG_CAN_LOG_LEVEL); #define CAN_INIT_TIMEOUT (100) #define CAN_DIV_CEIL(val, div) (((val) + (div) - 1) / (div)) -static void memcpy32_volatile(volatile void *dst_, const volatile void *src_, - size_t len) -{ - volatile uint32_t *dst = dst_; - const volatile uint32_t *src = src_; - - __ASSERT(len % 4 == 0, "len must be a multiple of 4!"); - len /= sizeof(uint32_t); - - while (len--) { - *dst = *src; - ++dst; - ++src; - } -} static void memset32_volatile(volatile void *dst_, uint32_t val, size_t len) { @@ -625,7 +610,8 @@ static void can_mcan_get_message(const struct device *dev, sys_cache_data_invd_range((void *)&fifo[get_idx].hdr, sizeof(struct can_mcan_rx_fifo_hdr)); - memcpy32_volatile(&hdr, &fifo[get_idx].hdr, + + io32_memcpy(&hdr, &fifo[get_idx].hdr, sizeof(struct can_mcan_rx_fifo_hdr)); frame.dlc = hdr.dlc; @@ -670,7 +656,8 @@ static void can_mcan_get_message(const struct device *dev, /* Data needs to be written in 32 bit blocks! */ sys_cache_data_invd_range((void *)fifo[get_idx].data_32, ROUND_UP(data_length, sizeof(uint32_t))); - memcpy32_volatile(frame.data_32, fifo[get_idx].data_32, + + io32_memcpy(frame.data_32, fifo[get_idx].data_32, ROUND_UP(data_length, sizeof(uint32_t))); if ((frame.flags & CAN_FRAME_IDE) != 0) { @@ -892,8 +879,9 @@ int can_mcan_send(const struct device *dev, tx_hdr.std_id = frame->id & CAN_STD_ID_MASK; } - memcpy32_volatile(&msg_ram->tx_buffer[put_idx].hdr, &tx_hdr, sizeof(tx_hdr)); - memcpy32_volatile(msg_ram->tx_buffer[put_idx].data_32, frame->data_32, + BUILD_ASSERT(sizeof(msg_ram->tx_buffer[put_idx].hdr) == sizeof(tx_hdr)); + io32_memcpy(&msg_ram->tx_buffer[put_idx].hdr, &tx_hdr, sizeof(tx_hdr)); + io32_memcpy(msg_ram->tx_buffer[put_idx].data_32, frame->data_32, ROUND_UP(data_length, 4)); sys_cache_data_flush_range((void *)&msg_ram->tx_buffer[put_idx].hdr, sizeof(tx_hdr)); sys_cache_data_flush_range((void *)&msg_ram->tx_buffer[put_idx].data_32, @@ -966,7 +954,8 @@ int can_mcan_add_rx_filter_std(const struct device *dev, filter_element.sfce = filter_id & 0x01 ? CAN_MCAN_FCE_FIFO1 : CAN_MCAN_FCE_FIFO0; - memcpy32_volatile(&msg_ram->std_filt[filter_id], &filter_element, + BUILD_ASSERT(sizeof(msg_ram->std_filt[0]) == sizeof(filter_element)); + io32_memcpy(&msg_ram->std_filt[filter_id], &filter_element, sizeof(struct can_mcan_std_filter)); sys_cache_data_flush_range((void *)&msg_ram->std_filt[filter_id], sizeof(struct can_mcan_std_filter)); @@ -1030,7 +1019,7 @@ static int can_mcan_add_rx_filter_ext(const struct device *dev, filter_element.efce = filter_id & 0x01 ? CAN_MCAN_FCE_FIFO1 : CAN_MCAN_FCE_FIFO0; - memcpy32_volatile(&msg_ram->ext_filt[filter_id], &filter_element, + io32_memcpy(&msg_ram->ext_filt[filter_id], &filter_element, sizeof(struct can_mcan_ext_filter)); sys_cache_data_flush_range((void *)&msg_ram->ext_filt[filter_id], sizeof(struct can_mcan_ext_filter)); diff --git a/include/zephyr/sys/util.h b/include/zephyr/sys/util.h index fdc4532cf8cea..99956acb913a3 100644 --- a/include/zephyr/sys/util.h +++ b/include/zephyr/sys/util.h @@ -498,6 +498,26 @@ char *utf8_trunc(char *utf8_str); */ char *utf8_lcpy(char *dst, const char *src, size_t n); +/** + * @brief Copy src to dest word-wise + * + * This function copies src to dest 32 bits at a time. + * If one calls this function without src and dst being aligned + * on a 4-byte boundary or if n is not a multiple of 4, it will + * do nothing and return NULL. + * + * NOTE: This function uses __ASSERT to enforce the requirements + * of each parameter given below. + * + * @param dest Destination address (must be aligned to a 4-byte boundary) + * @param src Source address (must be aligned to a 4-byte boundary) + * @param n number of bytes to copy (must be a multiple of 4) + * + * @return destination pointer or NULL on error + */ +volatile void * +io32_memcpy(volatile void * dest, const volatile void * src, size_t n); + #ifdef __cplusplus } #endif diff --git a/lib/os/CMakeLists.txt b/lib/os/CMakeLists.txt index df75c47c00f97..57b011cc95f8c 100644 --- a/lib/os/CMakeLists.txt +++ b/lib/os/CMakeLists.txt @@ -64,6 +64,8 @@ zephyr_sources_ifdef(CONFIG_SYS_MEM_BLOCKS mem_blocks.c) zephyr_sources_ifdef(CONFIG_WINSTREAM winstream.c) +zephyr_sources_ifdef(CONFIG_IO32_MEM io32_mem.c) + zephyr_library_include_directories( ${ZEPHYR_BASE}/kernel/include ${ZEPHYR_BASE}/arch/${ARCH}/include diff --git a/lib/os/Kconfig b/lib/os/Kconfig index adb67a510701f..5f8903fe4d02d 100644 --- a/lib/os/Kconfig +++ b/lib/os/Kconfig @@ -172,6 +172,14 @@ config UTF8 Enable the utf8 API. The API implements functions to specifically handle UTF-8 encoded strings. +config IO32_MEM + bool "io32_memcpy()" + help + Enable io32_memcpy(). This function is useful when word-wise + copying is required, for example when copying from or to a + memory-mapped peripheral. May be expanded with memset and + similar in the future. + rsource "Kconfig.cbprintf" rsource "Kconfig.heap" diff --git a/lib/os/io32_mem.c b/lib/os/io32_mem.c new file mode 100644 index 0000000000000..e6d76623fb333 --- /dev/null +++ b/lib/os/io32_mem.c @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 Pete Dietl + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#define IS_ALIGNED(X) (!((uintptr_t)X & (sizeof(uint32_t) - 1))) + +volatile void * +io32_memcpy(volatile void * dst0, const volatile void * src0, size_t len) +{ + __ASSERT(len % 4 == 0, "len must be a multiple of 4!"); + + volatile uint32_t *dst = dst0; + const volatile uint32_t *src = src0; + + __ASSERT(IS_ALIGNED(dst), "dst0 must be aligned to a multiple of 4!"); + __ASSERT(IS_ALIGNED(src), "src0 must be aligned to a multiple of 4!"); + + if (!((len % 4 == 0) && IS_ALIGNED(src) && IS_ALIGNED(dst))) { + return NULL; + } + + len /= sizeof(uint32_t); + + while (len--) { + *dst = *src; + ++dst; + ++src; + } + + return dst0; +}