diff --git a/arch/arm/src/stm32wl5/Kconfig b/arch/arm/src/stm32wl5/Kconfig index 6e26709bf1..99e5a6cd68 100644 --- a/arch/arm/src/stm32wl5/Kconfig +++ b/arch/arm/src/stm32wl5/Kconfig @@ -201,6 +201,17 @@ config STM32WL5_SPI1 depends on STM32WL5_HAVE_SPI1 select STM32WL5_SPI +comment "AHB3 Peripherals" + +config STM32WL5_IPCC + bool "IPCC" + select IPCC + default n + ---help--- + IPCC - Inter Processor Communication Controller. A very simple + character device stream driver to exchange data between + CM0 and CM4. + endmenu # STM32WL5 Peripheral Support @@ -300,4 +311,99 @@ config STM32WL5_SPI2S2_DMA_BUFFER endmenu # SPI Configuration +menu "IPCC Configuration" + depends on STM32WL5_IPCC + +config STM32WL5_IPCC_CHAN1_RX_SIZE + int "Channel 1 RX size" + default 256 + ---help--- + Size of the receive buffer. Another CPU will write to this + buffer and currently running CPU will read from it. + +config STM32WL5_IPCC_CHAN1_TX_SIZE + int "Channel 1 TX size" + default 256 + ---help--- + Size of the send buffer. Another CPU will read from this + buffer and currently running CPU will write to it. + +config STM32WL5_IPCC_CHAN2 + bool "Enable channel 2" + default n + +if STM32WL5_IPCC_CHAN2 + +config STM32WL5_IPCC_CHAN2_RX_SIZE + int "Channel 2 RX size" + default 256 + +config STM32WL5_IPCC_CHAN2_TX_SIZE + int "Channel 2 TX size" + default 256 + +config STM32WL5_IPCC_CHAN3 + bool "Enable channel 3" + default n + +if STM32WL5_IPCC_CHAN3 + +config STM32WL5_IPCC_CHAN3_RX_SIZE + int "Channel 3 RX size" + default 256 + +config STM32WL5_IPCC_CHAN3_TX_SIZE + int "Channel 3 TX size" + default 256 + +config STM32WL5_IPCC_CHAN4 + bool "Enable channel 4" + default n + +if STM32WL5_IPCC_CHAN4 + +config STM32WL5_IPCC_CHAN4_RX_SIZE + int "Channel 4 RX size" + default 256 + +config STM32WL5_IPCC_CHAN4_TX_SIZE + int "Channel 4 TX size" + default 256 + +config STM32WL5_IPCC_CHAN5 + bool "Enable channel 5" + default n + +if STM32WL5_IPCC_CHAN5 + +config STM32WL5_IPCC_CHAN5_RX_SIZE + int "Channel 5 RX size" + default 256 + +config STM32WL5_IPCC_CHAN5_TX_SIZE + int "Channel 5 TX size" + default 256 + +config STM32WL5_IPCC_CHAN6 + bool "Enable channel 6" + default n + +if STM32WL5_IPCC_CHAN6 + +config STM32WL5_IPCC_CHAN6_RX_SIZE + int "Channel 6 RX size" + default 256 + +config STM32WL5_IPCC_CHAN6_TX_SIZE + int "Channel 6 TX size" + default 256 + +endif # STM32WL5_IPCC_CHAN2 +endif # STM32WL5_IPCC_CHAN3 +endif # STM32WL5_IPCC_CHAN4 +endif # STM32WL5_IPCC_CHAN5 +endif # STM32WL5_IPCC_CHAN6 + +endmenu # IPCC Configuration + endif # ARCH_CHIP_STM32WL5 diff --git a/arch/arm/src/stm32wl5/Make.defs b/arch/arm/src/stm32wl5/Make.defs index 9397b10f63..a4a5ea5ca7 100644 --- a/arch/arm/src/stm32wl5/Make.defs +++ b/arch/arm/src/stm32wl5/Make.defs @@ -33,3 +33,7 @@ CHIP_CSRCS += stm32wl5_serial.c stm32wl5_start.c stm32wl5_waste.c stm32wl5_uid.c CHIP_CSRCS += stm32wl5_lse.c stm32wl5_lsi.c stm32wl5_idle.c CHIP_CSRCS += stm32wl5_pwr.c stm32wl5_tim.c stm32wl5_flash.c stm32wl5_timerisr.c CHIP_CSRCS += stm32wl5_spi.c + +CSRCS-$(CONFIG_STM32WL5_IPCC) = stm32wl5_ipcc.c + +CHIP_CSRCS += $(CSRCS-y) diff --git a/arch/arm/src/stm32wl5/hardware/stm32wl5_ipcc.h b/arch/arm/src/stm32wl5/hardware/stm32wl5_ipcc.h new file mode 100644 index 0000000000..47466253c1 --- /dev/null +++ b/arch/arm/src/stm32wl5/hardware/stm32wl5_ipcc.h @@ -0,0 +1,79 @@ +/**************************************************************************** + * arch/arm/src/stm32wl5/hardware/stm32wl5_ipcc.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_ARM_SRC_STM32WL5_HARDWARE_STM32WL5_IPCC_H +#define __ARCH_ARM_SRC_STM32WL5_HARDWARE_STM32WL5_IPCC_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include "chip.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define STM32WL5_IPCC_CPU1_OFFSET 0x00 +#define STM32WL5_IPCC_CPU2_OFFSET 0x10 + +/* Register Offsets *********************************************************/ + +#define STM32WL5_IPCC_CR_OFFSET 0x00 /* IPCC control register */ +#define STM32WL5_IPCC_MR_OFFSET 0x04 /* IPCC mask register */ +#define STM32WL5_IPCC_SCR_OFFSET 0x08 /* IPCC status set clear register */ +#define STM32WL5_IPCC_CTOCSR_OFFSET 0x0c /* IPCC processor to processor status register */ + +/* Register Addresses *******************************************************/ + +#define STM32WL5_IPCC_C1CR (STM32WL5_IPCC_BASE+STM32WL5_IPCC_CR_OFFSET+STM32WL5_IPCC_CPU1_OFFSET) +#define STM32WL5_IPCC_C1MR (STM32WL5_IPCC_BASE+STM32WL5_IPCC_MR_OFFSET+STM32WL5_IPCC_CPU1_OFFSET) +#define STM32WL5_IPCC_C1SCR (STM32WL5_IPCC_BASE+STM32WL5_IPCC_SCR_OFFSET+STM32WL5_IPCC_CPU1_OFFSET) +#define STM32WL5_IPCC_C1TOC2SR (STM32WL5_IPCC_BASE+STM32WL5_IPCC_CTOCSR_OFFSET+STM32WL5_IPCC_CPU1_OFFSET) +#define STM32WL5_IPCC_C2CR (STM32WL5_IPCC_BASE+STM32WL5_IPCC_CR_OFFSET+STM32WL5_IPCC_CPU2_OFFSET) +#define STM32WL5_IPCC_C2MR (STM32WL5_IPCC_BASE+STM32WL5_IPCC_MR_OFFSET+STM32WL5_IPCC_CPU2_OFFSET) +#define STM32WL5_IPCC_C2SCR (STM32WL5_IPCC_BASE+STM32WL5_IPCC_SCR_OFFSET+STM32WL5_IPCC_CPU2_OFFSET) +#define STM32WL5_IPCC_C2TOC1SR (STM32WL5_IPCC_BASE+STM32WL5_IPCC_CTOCSR_OFFSET+STM32WL5_IPCC_CPU2_OFFSET) + +/* Register Bitfield Definitions ********************************************/ + +#define STM32WL5_IPCC_TX_SHIFT (16) /* TX shift for all registers */ + +/* IPCC control register */ + +#define STM32WL5_IPCC_CR_RXOIE (1 << 0) /* Bit 0: Receive channel occupied interrupt enable */ +#define STM32WL5_IPCC_CR_TXFIE (1 << 16) /* Bit 16: Transmit channel free interrupt enable */ + +/* IPCC mask register */ + +#define STM32WL5_IPCC_MR_CHNOM(n) (1 << (n)) /* Bit 0..5: Receive channel n occupied interrupt enable, Channels 0..5 */ +#define STM32WL5_IPCC_MR_CHNFM(n) (1 << (16 + (n))) /* Bit 16..21: Transmit channel n free interrupt enable, Channels 0..5 */ + +/* IPCC status set clear register */ + +#define STM32WL5_IPCC_SCR_CHNC(n) (1 << (n)) /* Bit 0..5: Receive channel n status bit clear, Channels 0..5 */ +#define STM32WL5_IPCC_SCR_CHNS(n) (1 << (16 + (n))) /* Bit 16..21: Transmit channel n status bit set, Channels 0..5 */ + +/* IPCC processor to processor status register */ + +#define STM32WL5_IPCC_CTOCSR_CHNF(n) (1 << (n)) /* Bit 0..5: Channel n occupied, Channels 0..5 */ + +#endif /* __ARCH_ARM_SRC_STM32WL5_HARDWARE_STM32WL5_IPCC_H */ diff --git a/arch/arm/src/stm32wl5/stm32wl5_allocateheap.c b/arch/arm/src/stm32wl5/stm32wl5_allocateheap.c index 06d01bf888..44d13c8880 100644 --- a/arch/arm/src/stm32wl5/stm32wl5_allocateheap.c +++ b/arch/arm/src/stm32wl5/stm32wl5_allocateheap.c @@ -41,6 +41,7 @@ #include "arm_internal.h" #include "arm_internal.h" #include "stm32wl5_mpuinit.h" +#include "stm32wl5_ipcc.h" /**************************************************************************** * Pre-processor Definitions @@ -69,7 +70,11 @@ /* Set the range of SRAM2 as well, requires a second memory region */ -#define SRAM2_START STM32WL5_SRAM2_BASE +#ifdef CONFIG_IPCC +# define SRAM2_START IPCC_END +#else +# define SRAM2_START STM32WL5_SRAM2_BASE +#endif #define SRAM2_END (SRAM2_START + STM32WL5_SRAM2_SIZE) /* Some sanity checking. If multiple memory regions are defined, verify diff --git a/arch/arm/src/stm32wl5/stm32wl5_ipcc.c b/arch/arm/src/stm32wl5/stm32wl5_ipcc.c new file mode 100644 index 0000000000..8f4565e5c0 --- /dev/null +++ b/arch/arm/src/stm32wl5/stm32wl5_ipcc.c @@ -0,0 +1,812 @@ +/**************************************************************************** + * arch/arm/src/stm32wl5/stm32wl5_ipcc.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include "hardware/stm32wl5_ipcc.h" +#include "stm32wl5.h" +#include "stm32wl5_ipcc.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* This structure describes tx or rx of single channel in memory */ + +struct stm32wl5_ipcc_chan_mem_s +{ + unsigned len; /* Number of valid bytes in data[] */ + char data[]; /* Data in IPCC memory */ +}; + +/* Internal stm32wl5 ipcc structure describing channel state. */ + +struct stm32wl5_ipcc_s +{ + /* Pointer to API connecting upper and lower half of the driver */ + + struct ipcc_lower_s *ipcc; + + /* Physical memory address where second CPU will write data for us, + * we will be reading from this memory. + */ + + char *rxmem; + + /* Maximum length of data that rxmem can hold. It is size of the + * reserved space for rxmem minus sizeof(stm32wl5_ipcc_chan_mem_s.len) + */ + + unsigned rxlen; + +#if CONFIG_IPCC_BUFFERED + /* Number of bytes copied from IPCC memory to buffer. Can be less than + * stm32wl5_ipcc_chan_mem_s.len after copy operation when buffer is full. + * Value can persist between multiple ISR and stm32wl5_ipcc_buffer_data() + * calls, until all data from IPCC memory is successfully buffered. + */ + + unsigned rxcopied; +#endif + + /* Physical memory address where we will write data for the second + * CPU to read. + */ + + char *txmem; + + /* Maximum length of data that txmem can hold. It is size of the + * reserved space for txmem minus sizeof(stm32wl5_ipcc_chan_mem_s.len) + */ + + unsigned txlen; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static ssize_t stm32wl5_ipcc_read(struct ipcc_lower_s *ipcc, + char *buffer, size_t buflen); +static ssize_t stm32wl5_ipcc_write(struct ipcc_lower_s *ipcc, + const char *buffer, size_t buflen); +static ssize_t stm32wl5_ipcc_buffer_data(struct ipcc_lower_s *ipcc, + struct circbuf_s *rxbuf); +static ssize_t stm32wl5_ipcc_copy_to_buffer(int chan, + struct circbuf_s *rxbuf); +static int stm32wl5_ipcc_rx_isr(int irq, void *context, void *arg); +static int stm32wl5_ipcc_tx_isr(int irq, void *context, void *arg); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +struct stm32wl5_ipcc_s g_ipccpriv[IPCC_NCHAN] = +{ + /* Channel 1 is always enabled when IPCC is enabled */ + + { + .rxmem = (char *)(IPCC_CHAN1_START), + .rxlen = IPCC_CHAN1_RX_SIZE - sizeof(unsigned), + .txmem = (char *)(IPCC_CHAN1_START + IPCC_CHAN1_RX_SIZE), + .txlen = IPCC_CHAN1_TX_SIZE - sizeof(unsigned) + } + +#if IPCC_CHAN2 + , + { + .rxmem = (char *)(IPCC_CHAN2_START), + .rxlen = IPCC_CHAN2_RX_SIZE - sizeof(unsigned), + .txmem = (char *)(IPCC_CHAN2_START + IPCC_CHAN2_RX_SIZE), + .txlen = IPCC_CHAN2_TX_SIZE - sizeof(unsigned) + } +#endif + +#if IPCC_CHAN3 + , + { + .rxmem = (char *)(IPCC_CHAN3_START), + .rxlen = IPCC_CHAN3_RX_SIZE - sizeof(unsigned), + .txmem = (char *)(IPCC_CHAN3_START + IPCC_CHAN3_RX_SIZE), + .txlen = IPCC_CHAN3_TX_SIZE - sizeof(unsigned) + } +#endif + +#if IPCC_CHAN4 + , + { + .rxmem = (char *)(IPCC_CHAN4_START), + .rxlen = IPCC_CHAN4_RX_SIZE - sizeof(unsigned), + .txmem = (char *)(IPCC_CHAN4_START + IPCC_CHAN4_RX_SIZE), + .txlen = IPCC_CHAN4_TX_SIZE - sizeof(unsigned) + } +#endif + +#if IPCC_CHAN5 + , + { + .rxmem = (char *)(IPCC_CHAN5_START), + .rxlen = IPCC_CHAN5_RX_SIZE - sizeof(unsigned), + .txmem = (char *)(IPCC_CHAN5_START + IPCC_CHAN5_RX_SIZE), + .txlen = IPCC_CHAN5_TX_SIZE - sizeof(unsigned) + } +#endif + +#if IPCC_CHAN6 + , + { + .rxmem = (char *)(IPCC_CHAN6_START), + .rxlen = IPCC_CHAN6_RX_SIZE - sizeof(unsigned), + .txmem = (char *)(IPCC_CHAN6_START + IPCC_CHAN6_RX_SIZE), + .txlen = IPCC_CHAN6_TX_SIZE - sizeof(unsigned) + } +#endif +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32wl5_ipcc_tx_isr + * + * Description: + * IPCC TX interrupt service routine. This interrupt is called when + * second CPU read all data from IPCC TX memory and we are free to + * write new data to that memory. + * + * For buffered IPCC, we will immediately write data from buffer to + * IPCC memory and notify second CPU. Also blocked writer will be + * notified that there is free space on tx buffer. + * + * For unbuffered IPCC, we won't copy anything but only notify blocked + * writers that IPCC TX memory is free. + * + * Input Parameters: + * irq - Number of IRQ that generated interrupt + * context - Interrupt register state save info (architecture-specific) + * arg - user data (not used by us) + * + * Returned Value: + * Always OK + * + * Assumptions/Limitations: + * + ****************************************************************************/ + +static int stm32wl5_ipcc_tx_isr(int irq, void *context, void *arg) +{ + int chan; + size_t nwritten; + uint32_t mr; + uint32_t sr; + uint32_t status; + struct stm32wl5_ipcc_s *priv; + struct stm32wl5_ipcc_chan_mem_s *txmem; + + (void)context; + (void)arg; + (void)irq; + + mr = getreg32(STM32WL5_IPCC_C1MR) >> STM32WL5_IPCC_TX_SHIFT; + sr = getreg32(STM32WL5_IPCC_C1TOC2SR); + + /* Consider only channels that have tx memory free and are unmasked */ + + status = sr | mr; + + /* Check which channels have data ready to read */ + + for (chan = 0; chan != IPCC_NCHAN; chan++) + { + if (status & (1 << chan)) + { + /* Transmit on channel is either masked or TX memory is not + * ready to write (second CPU is still read data from it, + * CHnF == 1). + */ + + continue; + } + + /* Get internal data structure for current chan */ + + priv = &g_ipccpriv[chan]; + txmem = (struct stm32wl5_ipcc_chan_mem_s *)priv->txmem; + +#if CONFIG_IPCC_BUFFERED + /* Copy as much as we can into IPCC memory, circbuf won't copy + * more than there is in the buffer. + */ + + nwritten = circbuf_read(&priv->ipcc->txbuf, txmem->data, priv->txlen); + + /* Did we write anything to TX memory? */ + + if (nwritten) + { + /* Yes, tell another CPU that data is available to read */ + + txmem->len = nwritten; + modifyreg32(STM32WL5_IPCC_C1SCR, 0, STM32WL5_IPCC_SCR_CHNS(chan)); + } +#else /* CONFIG_IPCC_BUFFERED */ + /* In unbuffered operations we only notify blocked writers, these + * writers will write to IPCC memory directly. + */ +#endif /* CONFIG_IPCC_BUFFERED */ + + /* Wake up all blocked writers that there is free space available + * in IPCC memory (or txbuffer) to write. + */ + + ipcc_txfree_notify(priv->ipcc->upper); + + if (circbuf_used(&priv->ipcc->txbuf) == 0) + { + /* Mask tx free interrupt - if tx buffer is empty and we + * did not write anything and we don't mask interrupt, we + * will be constantly interrupted by tx free irq. + */ + + modifyreg32(STM32WL5_IPCC_C1MR, 0, STM32WL5_IPCC_MR_CHNFM(chan)); + } + } + + return OK; +} + +/**************************************************************************** + * Name: stm32wl5_ipcc_write + * + * Description: + * Function writes buffer to IPCC memory that will be later read by + * second CPU. + * + * Input Parameters: + * ipcc - ipcc channel instance to write to + * buffer - data to write to IPCC memory + * buflen - number of bytes requested to write + * + * Returned Value: + * Number of bytes that has been successfully written, or 0 when no + * bytes could be written for any reason. + * + * Assumptions/Limitations: + * + ****************************************************************************/ + +static ssize_t stm32wl5_ipcc_write(struct ipcc_lower_s *ipcc, + const char *buffer, size_t buflen) +{ + size_t to_copy; + struct stm32wl5_ipcc_s *priv; + struct stm32wl5_ipcc_chan_mem_s *txmem; + uint32_t sr; + + sr = getreg32(STM32WL5_IPCC_C1TOC2SR); + + if ((sr & (1 << ipcc->chan))) + { + /* CHnF == 1 means that channel is occupied and second CPU can read + * data from it. In any case second CPU did not yet read all data + * and we should not write to that memory. Unmask TX interrupt + * so we are notified when we can write to memory. + */ + + modifyreg32(STM32WL5_IPCC_C1MR, STM32WL5_IPCC_MR_CHNFM(ipcc->chan), 0); + return 0; + } + + priv = &g_ipccpriv[ipcc->chan]; + txmem = (struct stm32wl5_ipcc_chan_mem_s *)priv->txmem; + + /* Disable TX interrupt since we will modify shared data */ + + up_disable_irq(STM32WL5_IRQ_IPCC_C1_TX_IT); + + /* Copy as much as we can into IPCC memory */ + + to_copy = buflen > priv->txlen ? priv->txlen : buflen; + memcpy(txmem->data, buffer, to_copy); + txmem->len = to_copy; + + /* Tell another CPU that data is available to read */ + + modifyreg32(STM32WL5_IPCC_C1SCR, 0, STM32WL5_IPCC_SCR_CHNS(ipcc->chan)); + + /* Reenable interrupts */ + + modifyreg32(STM32WL5_IPCC_C1MR, STM32WL5_IPCC_MR_CHNFM(ipcc->chan), 0); + up_enable_irq(STM32WL5_IRQ_IPCC_C1_TX_IT); + + /* Return number of successfully copied bytes to IPCC memory */ + + return to_copy; +} + +/**************************************************************************** + * Name: stm32wl5_ipcc_rx_isr + * + * Description: + * Interrupt service routine - this function is called when another CPU + * has copied data to IPCC memory. Function will wake up blocked readers. + * + * If buffering is enabled, function will also try to copy all data from + * IPCC memory to buffer. If buffer gets full, we will copy as many bytes + * as we can fit into buffer, internally save how many bytes we managed + * to copy and set overflow flag to 1. + * + * Input Parameters: + * irq - Number of IRQ that generated interrupt + * context - Interrupt register state save info (architecture-specific) + * arg - user data (not used by us) + * + * Returned Value: + * Always OK + * + * Assumptions/Limitations: + * + ****************************************************************************/ + +static int stm32wl5_ipcc_rx_isr(int irq, void *context, void *arg) +{ + int chan; + ssize_t nread; + uint32_t mr; + uint32_t sr; + uint32_t status; + struct stm32wl5_ipcc_s *priv; + + (void)context; + (void)arg; + (void)irq; + + mr = getreg32(STM32WL5_IPCC_C1MR); + sr = getreg32(STM32WL5_IPCC_C2TOC1SR); + + /* Consider only channels that have data in rx memory and are unmasked */ + + status = sr & ~mr; + + /* Check which channels have data ready to read */ + + for (chan = 0; chan != IPCC_NCHAN; chan++) + { + if (!(status & (1 << chan))) + { + /* Receive on channel is either masked or there is no data + * ready for us to read (CHnF == 0) + */ + + continue; + } + + /* Get internal data structure for current chan */ + + priv = &g_ipccpriv[chan]; + +#if CONFIG_IPCC_BUFFERED + nread = stm32wl5_ipcc_copy_to_buffer(chan, &priv->ipcc->rxbuf); +#else /* CONFIG_IPCC_BUFFERED */ + /* In unbuffered operations we only notify blocked readers, these + * readers will read from IPCC memory directly. + */ +#endif /* CONFIG_IPCC_BUFFERED */ + + if (nread) + { + /* Wake up all blocked readers that there is data + * available to read + */ + + ipcc_rxfree_notify(priv->ipcc->upper); + } + } + + return OK; +} + +/**************************************************************************** + * Name: stm32wl5_ipcc_read + * + * Description: + * Function will copy requests number of bytes to buffer. If there is not + * enough data in IPCC memory, less bytes than requests will be copied. + * Buflen does not have to be bigger than IPCC memory - function can be + * called multiple times and only new data will be transfered. If we don't + * have control over IPCC memory (CHnF is 0 - second CPU is writing data + * to memory) then it's assumed no data is there to read and 0 is returned. + * + * Input Parameters: + * ipcc - ipcc channel struct + * buffer - location where data shall be copied + * buflen - size of buffer and number of requested bytes to copy + * + * Returned Value: + * Number of bytes that have been successfully copied to buffer (which + * may be less than buflen), or 0 when there was no data to be copied. + * + * Assumptions/Limitations: + * + ****************************************************************************/ + +static ssize_t stm32wl5_ipcc_read(struct ipcc_lower_s *ipcc, + char *buffer, size_t buflen) +{ + size_t to_copy; + uint32_t sr; + struct stm32wl5_ipcc_s *priv; + struct stm32wl5_ipcc_chan_mem_s *rxmem; + + sr = getreg32(STM32WL5_IPCC_C2TOC1SR); + + if (!(sr & (1 << ipcc->chan))) + { + /* CHnF == 0 means that channel is free and second CPU can write + * data to it. In any case data is not yet ready to read - so no + * data can be read. + */ + + return 0; + } + + priv = &g_ipccpriv[ipcc->chan]; + rxmem = (struct stm32wl5_ipcc_chan_mem_s *)priv->rxmem; + + /* Disable RX interrupt since we will modify shared data */ + + up_disable_irq(STM32WL5_IRQ_IPCC_C1_RX_IT); + + /* This function may be called multiple times to get only part + * of data from IPCC memory, ie. There are 8 bytes of data in + * IPCC memory and upper half calls this function 4 times, each + * time only reading 2 bytes. Check how many bytes we can copy + */ + + to_copy = rxmem->len - priv->rxcopied; + to_copy = to_copy > buflen ? buflen : to_copy; + + /* Copy as much data to upper half as possible */ + + memcpy(buffer, rxmem->data + priv->rxcopied, to_copy); + priv->rxcopied += to_copy; + if (priv->rxcopied == priv->rxlen) + { + /* We have copied all data from IPCC memory */ + + priv->rxcopied = 0; + + /* Tell another CPU that IPCC rx buffer is free to be populated */ + + modifyreg32(STM32WL5_IPCC_C1SCR, 0, + STM32WL5_IPCC_SCR_CHNC(ipcc->chan)); + } + + /* Reenable interrupt */ + + up_enable_irq(STM32WL5_IRQ_IPCC_C1_RX_IT); + + return to_copy; +} + +/**************************************************************************** + * Name: stm32wl5_ipcc_copy_to_buffer + * + * Description: + * Copies as much bytes from channel as possible to rxbuf circ buffer. + * + * Input Parameters: + * chan - channel number to get data from + * rxbuf - circural buffer to copy data to + * + * Returned Value: + * Number of bytes copied to rxbuf + * + * Assumptions/Limitations: + * This is helper function, it does not perform any locking. It may + * be called from interrupt. + * + ****************************************************************************/ + +static ssize_t stm32wl5_ipcc_copy_to_buffer(int chan, + struct circbuf_s *rxbuf) +{ + size_t to_copy; + size_t rxbuf_space; + struct stm32wl5_ipcc_s *priv; + struct stm32wl5_ipcc_chan_mem_s *rxmem; + uint32_t sr; + + sr = getreg32(STM32WL5_IPCC_C2TOC1SR); + + if (!(sr & (1 << chan))) + { + /* CHnF == 0 means that channel is free and second CPU can write + * data to it. In any case data is not yet ready to read - so no + * data can be read. + */ + + return 0; + } + + priv = &g_ipccpriv[chan]; + rxmem = (struct stm32wl5_ipcc_chan_mem_s *)priv->rxmem; + + /* If buffer is full, it's possible we did not copy everything from + * IPCC memory to buffer in previous interrupt. Then when another + * channel triggers interrupt we may only need to copy what is left + * in IPCC memory to buffer. + */ + + to_copy = rxmem->len - priv->rxcopied; + rxbuf_space = circbuf_space(rxbuf); + + if (to_copy > rxbuf_space) + { + /* we can't fit all data into buffer, copy as much as + * possible and set overflow flag to 1, to tell upper + * half that there is still data in IPCC memory. + */ + + to_copy = rxbuf_space; + priv->ipcc->overflow = 1; + + /* Also disable RX interrupt. If data is still in the ipcc + * memory and we don't set rxbuffer as free, we will + * immediately get another RX interrupt once we leave + * this one. + */ + + modifyreg32(STM32WL5_IPCC_C1MR, 0, STM32WL5_IPCC_MR_CHNOM(chan)); + } + + /* Buffer data. This function cannot really fail us if we + * pass valid input parameters. + */ + + circbuf_write(&priv->ipcc->rxbuf, rxmem->data + priv->rxcopied, to_copy); + + /* Increment number of bytes that has been buffered */ + + if ((priv->rxcopied += to_copy) == rxmem->len) + { + /* We have buffered all data from IPCC memory */ + + priv->rxcopied = 0; + priv->ipcc->overflow = 0; + + /* Tell another CPU that IPCC rx buffer is free to be populated */ + + modifyreg32(STM32WL5_IPCC_C1SCR, 0, STM32WL5_IPCC_SCR_CHNC(chan)); + + /* Unmask RX interrupt to know when second CPU sends us a message */ + + modifyreg32(STM32WL5_IPCC_C1MR, STM32WL5_IPCC_MR_CHNOM(chan), 0); + } + + return to_copy; +} + +/**************************************************************************** + * Name: stm32wl5_ipcc_buffer_data + * + * Description: + * Copies as many bytes as possible from ipcc channel to rxbuf. + * + * Input Parameters: + * ipcc - ipcc channel to copy data from + * rxbuf - circural buffer to copy data to + * + * Returned Value: + * Number of successfully buffered bytes. + * + * Assumptions/Limitations: + * + ****************************************************************************/ + +static ssize_t stm32wl5_ipcc_buffer_data(struct ipcc_lower_s *ipcc, + struct circbuf_s *rxbuf) +{ + int ret; + + /* Disable RX interrupt since we will modify shared data */ + + up_disable_irq(STM32WL5_IRQ_IPCC_C1_RX_IT); + + /* Copy data to buffer */ + + ret = stm32wl5_ipcc_copy_to_buffer(ipcc->chan, rxbuf); + + /* Reenable interrupt */ + + up_enable_irq(STM32WL5_IRQ_IPCC_C1_RX_IT); + + /* Return number of bytes that were successfully buffered */ + + return ret; +} + +/**************************************************************************** + * Name: stm32wl5_ipcc_write_notify + * + * Description: + * This function is called when there is new data on circ buffer. + * All copy operations from buffer to ipcc memory is done in interrupt, + * so we simply unmask the interrupt. Interrupt will arrive once ipcc + * TX memory is free to be written. + * + * Input Parameters: + * ipcc - ipcc channel + * + * Returned Value: + * Always OK + * + ****************************************************************************/ + +static ssize_t stm32wl5_ipcc_write_notify(struct ipcc_lower_s *ipcc) +{ + modifyreg32(STM32WL5_IPCC_C1MR, STM32WL5_IPCC_MR_CHNFM(ipcc->chan), 0); + return 0; +} + +/**************************************************************************** + * Name: stm32wl5_ipcc_cleanup + * + * Description: + * Cleans up resources initialized by stm32wl5_ipcc_init(). This will + * free() ipcc pointer! + * + * Input Parameters: + * ipcc - ipcc channel to cleanup + * + * Returned Value: + * Always OK + * + ****************************************************************************/ + +static int stm32wl5_ipcc_cleanup(FAR struct ipcc_lower_s *ipcc) +{ + DEBUGASSERT(ipcc); + DEBUGASSERT(ipcc->chan <= IPCC_NCHAN); + + /* Mask interrupts for given channel */ + + modifyreg32(STM32WL5_IPCC_C1MR, 1, STM32WL5_IPCC_MR_CHNFM(ipcc->chan)); + modifyreg32(STM32WL5_IPCC_C1MR, 1, STM32WL5_IPCC_MR_CHNOM(ipcc->chan)); + + /* Free allocated ipcc memory */ + + kmm_free(ipcc); + + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32wl5_ipcc_init + * + * Description: + * Function initializes runtime options for IPCC. This function is called + * by upper half of the IPCC driver from ipcc_register(). This function + * is called once for every registered channel - so it can be called + * multiple times with different input parameters during system bring up. + * + * Input Parameters: + * chan - channel to initialize + * + * Returned Value: + * Structure to link lower and upper halfs of the driver, or NULL on + * initialization failure. + * + * Assumptions/Limitations: + * + ****************************************************************************/ + +struct ipcc_lower_s *stm32wl5_ipcc_init(int chan) +{ + int ret; + static int ipcc_fti; + struct ipcc_lower_s *ipcc; + + DEBUGASSERT(ipcc); + DEBUGASSERT(chan <= IPCC_NCHAN); + + if ((ipcc = kmm_zalloc(sizeof(*ipcc))) == NULL) + { + return NULL; + } + + /* Link internal stm32wl5 ipcc struct with character device driver + * via ipcc_lower struct. + */ + + g_ipccpriv[chan].ipcc = ipcc; + + /* Give upper half driver pointers to mcu specific functions that + * upper half needs to call to work properly. + */ + + ipcc->ops.read = stm32wl5_ipcc_read; + ipcc->ops.write = stm32wl5_ipcc_write; + ipcc->ops.buffer_data = stm32wl5_ipcc_buffer_data; + ipcc->ops.write_notify = stm32wl5_ipcc_write_notify; + ipcc->ops.cleanup = stm32wl5_ipcc_cleanup; + + ipcc->chan = chan; + + /* Unmask channel interrupt */ + + modifyreg32(STM32WL5_IPCC_C1MR, STM32WL5_IPCC_MR_CHNFM(chan), 0); + modifyreg32(STM32WL5_IPCC_C1MR, STM32WL5_IPCC_MR_CHNOM(chan), 0); + + if (ipcc_fti) + { + return ipcc; + } + + /* For the first time initialization we also need to attach rx/tx + * interrupt functions + */ + + ret = irq_attach(STM32WL5_IRQ_IPCC_C1_RX_IT, stm32wl5_ipcc_rx_isr, NULL); + if (ret) + { + kmm_free(ipcc); + return NULL; + } + + ret = irq_attach(STM32WL5_IRQ_IPCC_C1_TX_IT, stm32wl5_ipcc_tx_isr, NULL); + if (ret) + { + kmm_free(ipcc); + return NULL; + } + + /* Enable interrupts when: + * - we receive data from CPU2 + * - CPU2 has read message from us and TX memory is free to be used again + */ + + putreg32(STM32WL5_IPCC_CR_RXOIE | STM32WL5_IPCC_CR_TXFIE, + STM32WL5_IPCC_C1CR); + + up_enable_irq(STM32WL5_IRQ_IPCC_C1_RX_IT); + up_enable_irq(STM32WL5_IRQ_IPCC_C1_TX_IT); + + ipcc_fti = 1; + + return ipcc; +} diff --git a/arch/arm/src/stm32wl5/stm32wl5_ipcc.h b/arch/arm/src/stm32wl5/stm32wl5_ipcc.h new file mode 100644 index 0000000000..2d2a221fa0 --- /dev/null +++ b/arch/arm/src/stm32wl5/stm32wl5_ipcc.h @@ -0,0 +1,145 @@ +/**************************************************************************** + * arch/arm/src/stm32wl5/stm32wl5_ipcc.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_ARM_SRC_STM32WL5_IPCC_H +#define __ARCH_ARM_SRC_STM32WL5_IPCC_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include "hardware/stm32wl5_memorymap.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Channels are sequential, that is, if channel 3 is enabled that means + * channel 2 and 1 are also enabled. Kconfig makes sure there is no + * situation where channel 3 is enabled while channel 2 is not. + */ + +/* channel 1 configuration **************************************************/ + +#define IPCC_CHAN1_RX_SIZE (CONFIG_STM32WL5_IPCC_CHAN1_RX_SIZE) +#define IPCC_CHAN1_TX_SIZE (CONFIG_STM32WL5_IPCC_CHAN1_TX_SIZE) +#define IPCC_CHAN1_START (IPCC_START) +#define IPCC_CHAN1_SIZE (IPCC_CHAN1_RX_SIZE + IPCC_CHAN1_TX_SIZE) +#define IPCC_CHAN1 (1) + +/* channel 2 configuration **************************************************/ + +#if defined(CONFIG_STM32WL5_IPCC_CHAN2) +# define IPCC_CHAN2_RX_SIZE (CONFIG_STM32WL5_IPCC_CHAN2_RX_SIZE) +# define IPCC_CHAN2_TX_SIZE (CONFIG_STM32WL5_IPCC_CHAN2_TX_SIZE) +# define IPCC_CHAN2_START (IPCC_CHAN1_START + IPCC_CHAN1_SIZE) +# define IPCC_CHAN2_SIZE (IPCC_CHAN2_RX_SIZE + IPCC_CHAN2_TX_SIZE) +# define IPCC_CHAN2 (1) +#else +# define IPCC_CHAN2_SIZE (0) +# define IPCC_CHAN2 (0) +#endif + +/* channel 3 configuration **************************************************/ + +#if defined(CONFIG_STM32WL5_IPCC_CHAN3) +# define IPCC_CHAN3_RX_SIZE (CONFIG_STM32WL5_IPCC_CHAN3_RX_SIZE) +# define IPCC_CHAN3_TX_SIZE (CONFIG_STM32WL5_IPCC_CHAN3_TX_SIZE) +# define IPCC_CHAN3_START (IPCC_CHAN2_START + IPCC_CHAN2_SIZE) +# define IPCC_CHAN3_SIZE (IPCC_CHAN3_RX_SIZE + IPCC_CHAN3_TX_SIZE) +# define IPCC_CHAN3 (1) +#else +# define IPCC_CHAN3_SIZE (0) +# define IPCC_CHAN3 (0) +#endif + +/* channel 4 configuration **************************************************/ + +#if defined(CONFIG_STM32WL5_IPCC_CHAN4) +# define IPCC_CHAN4_RX_SIZE (CONFIG_STM32WL5_IPCC_CHAN4_RX_SIZE) +# define IPCC_CHAN4_TX_SIZE (CONFIG_STM32WL5_IPCC_CHAN4_TX_SIZE) +# define IPCC_CHAN4_START (IPCC_CHAN3_START + IPCC_CHAN3_SIZE) +# define IPCC_CHAN4_SIZE (IPCC_CHAN4_RX_SIZE + IPCC_CHAN4_TX_SIZE) +# define IPCC_CHAN4 (1) +#else +# define IPCC_CHAN4_SIZE (0) +# define IPCC_CHAN4 (0) +#endif + +/* channel 5 configuration **************************************************/ + +#if defined(CONFIG_STM32WL5_IPCC_CHAN5) +# define IPCC_CHAN5_RX_SIZE (CONFIG_STM32WL5_IPCC_CHAN5_RX_SIZE) +# define IPCC_CHAN5_TX_SIZE (CONFIG_STM32WL5_IPCC_CHAN5_TX_SIZE) +# define IPCC_CHAN5_START (IPCC_CHAN4_START + IPCC_CHAN4_SIZE) +# define IPCC_CHAN5_SIZE (IPCC_CHAN5_RX_SIZE + IPCC_CHAN5_TX_SIZE) +# define IPCC_CHAN5 (1) +#else +# define IPCC_CHAN5_SIZE (0) +# define IPCC_CHAN5 (0) +#endif + +/* channel 6 configuration **************************************************/ + +#if defined(CONFIG_STM32WL5_IPCC_CHAN6) +# define IPCC_CHAN6_RX_SIZE (CONFIG_STM32WL5_IPCC_CHAN6_RX_SIZE) +# define IPCC_CHAN6_TX_SIZE (CONFIG_STM32WL5_IPCC_CHAN6_TX_SIZE) +# define IPCC_CHAN6_START (IPCC_CHAN5_START + IPCC_CHAN5_SIZE) +# define IPCC_CHAN6_SIZE (IPCC_CHAN6_RX_SIZE + IPCC_CHAN6_TX_SIZE) +# define IPCC_CHAN6 (1) +#else +# define IPCC_CHAN6_SIZE (0) +# define IPCC_CHAN6 (0) +#endif + +/* ipcc general configuration ***********************************************/ + +/* Memory layout is: + * + * +----------+ + * | CHAN1_RX | CHAN1_START (IPCC_START) + * | CHAN1_TX | CHAN1_START + IPCC_CHAN1_RX_SIZE + * +----------+ + * | CHAN2_RX | CHAN2_START + * | CHAN2_TX | CHAN2_START + IPCC_CHAN2_RX_SIZE + * | . | + * | . | + * | . | + * | CHAN6_RX | CHAN6_START + * | CHAN6_TX | CHAN6_START + IPCC_CHAN6_RX_SIZE + * +----------+ + */ + +/* IPCC needs continous memory of known address that is shared + * between CPUs. Because of that we reserve memory at beginning + * of SRAM2. SRAM2 region will be right after IPCC reserved memory + */ + +#define IPCC_START STM32WL5_SRAM2_BASE +#define IPCC_NCHAN (IPCC_CHAN1 + IPCC_CHAN2 + IPCC_CHAN3 + \ + IPCC_CHAN4 + IPCC_CHAN5 + IPCC_CHAN6) +#define IPCC_END (IPCC_START + IPCC_CHAN1_SIZE + IPCC_CHAN2_SIZE + \ + IPCC_CHAN3_SIZE + IPCC_CHAN4_SIZE + \ + IPCC_CHAN5_SIZE + IPCC_CHAN6_SIZE) + +struct ipcc_lower_s *stm32wl5_ipcc_init(int chan); + +#endif /* __ARCH_ARM_SRC_STM32WL5_IPCC_H */ diff --git a/arch/arm/src/stm32wl5/stm32wl5_rcc.c b/arch/arm/src/stm32wl5/stm32wl5_rcc.c index 51b03ca2d8..380d14a50b 100644 --- a/arch/arm/src/stm32wl5/stm32wl5_rcc.c +++ b/arch/arm/src/stm32wl5/stm32wl5_rcc.c @@ -294,6 +294,12 @@ static inline void stm32wl5_rcc_enableahb3(void) regval |= RCC_AHB3ENR_FLASHEN; #endif +#ifdef CONFIG_STM32WL5_IPCC + /* IPCC interface clock enable */ + + regval |= RCC_AHB3ENR_IPCCEN; +#endif + putreg32(regval, STM32WL5_RCC_AHB3ENR); /* Enable peripherals */ }