altos/stm32f1: Grab both TX/RX DMA mutexes while doing I2C
authorKeith Packard <keithp@keithp.com>
Sat, 2 Mar 2024 23:23:57 +0000 (16:23 -0700)
committerKeith Packard <keithp@keithp.com>
Sat, 2 Mar 2024 23:23:57 +0000 (16:23 -0700)
The I2C engine appears to trigger an extra RX DMA transaction which
scrambles anything sharing the same DMA units.

Signed-off-by: Keith Packard <keithp@keithp.com>
src/stm32f1/ao_arch_funcs.h
src/stm32f1/ao_dma_stm.c
src/stm32f1/ao_i2c_stm.c

index 3a83f382faca4c3a75c73ee1cc2a6411877b0874..7004eb460ea0c18a8d9e98bcb53e81dc390e2342 100644 (file)
@@ -350,6 +350,12 @@ ao_dma_set_transfer(uint8_t                index,
                    uint16_t            count,
                    uint32_t            ccr);
 
+void
+ao_dma_mutex_get(uint8_t index);
+
+void
+ao_dma_mutex_put(uint8_t index);
+
 void
 ao_dma_set_isr(uint8_t index, void (*isr)(int index));
 
index d3162d5ba85c163261e4e3bc815cf4f02398c496..45bb88f31609c882487ac2e2eb0219ecbe9b6480 100644 (file)
@@ -66,6 +66,27 @@ void stm_dma1_channel7_isr(void) { ao_dma_isr(STM_DMA_INDEX(7)); }
 static uint8_t ao_dma_active;
 #endif
 
+void
+ao_dma_mutex_get(uint8_t index)
+{
+       if (ao_dma_allocated[index]) {
+               if (ao_dma_mutex[index])
+                       ao_panic(AO_PANIC_DMA);
+               ao_dma_mutex[index] = 0xff;
+       } else
+               ao_mutex_get(&ao_dma_mutex[index]);
+}
+
+void
+ao_dma_mutex_put(uint8_t index)
+{
+       if (ao_dma_allocated[index])
+               ao_dma_mutex[index] = 0;
+       else
+               ao_mutex_put(&ao_dma_mutex[index]);
+}
+
+
 void
 ao_dma_set_transfer(uint8_t            index,
                    volatile void       *peripheral,
index e84f594bb19cc7a290de5e866dea00e8a82d653b..a61d3eb5f037c3c20c4324ebbd7321c485b128ca 100644 (file)
@@ -224,11 +224,11 @@ ao_i2c_send(void *block, uint16_t len, uint8_t index, uint8_t stop)
 {
        struct stm_i2c  *stm_i2c = ao_i2c_stm_info[index].stm_i2c;
        uint8_t         tx_dma_index = ao_i2c_stm_info[index].tx_dma_index;
+       uint8_t         rx_dma_index = ao_i2c_stm_info[index].rx_dma_index;
 
        /* Clear any pending ADDR bit */
        (void) stm_i2c->sr2;
        ao_i2c_wait_addr(index);
-       stm_i2c->cr2 = AO_STM_I2C_CR2 | (1 << STM_I2C_CR2_DMAEN);
        ao_dma_set_transfer(tx_dma_index,
                            &stm_i2c->dr,
                            block,
@@ -241,13 +241,14 @@ ao_i2c_send(void *block, uint16_t len, uint8_t index, uint8_t stop)
                            (0 << STM_DMA_CCR_PINC) |
                            (0 << STM_DMA_CCR_CIRC) |
                            (STM_DMA_CCR_DIR_MEM_TO_PER << STM_DMA_CCR_DIR));
+       ao_dma_mutex_get(rx_dma_index);
+       stm_i2c->cr2 = AO_STM_I2C_CR2 | (1 << STM_I2C_CR2_DMAEN);
 
        ao_dma_start(tx_dma_index);
        ao_arch_block_interrupts();
        while (!ao_dma_done[tx_dma_index])
                if (ao_sleep_for(&ao_dma_done[tx_dma_index], 1 + len))
                        break;
-       ao_dma_done_transfer(tx_dma_index);
        stm_i2c->cr2 = AO_STM_I2C_CR2 | (1 << STM_I2C_CR2_ITEVTEN) | (1 << STM_I2C_CR2_ITERREN);
        while ((stm_i2c->sr1 & (1 << STM_I2C_SR1_BTF)) == 0)
                if (ao_sleep_for(&ao_i2c_state[index], 1 + len))
@@ -258,6 +259,8 @@ ao_i2c_send(void *block, uint16_t len, uint8_t index, uint8_t stop)
                stm_i2c->cr1 = AO_STM_I2C_CR1 | (1 << STM_I2C_CR1_STOP);
                ao_i2c_wait_stop(index);
        }
+       ao_dma_mutex_put(rx_dma_index);
+       ao_dma_done_transfer(tx_dma_index);
        return true;
 }
 
@@ -311,7 +314,9 @@ ao_i2c_recv(void *block, uint16_t len, uint8_t index, uint8_t stop)
                ao_arch_release_interrupts();
                ret = ao_i2c_recv_len[index] == 0;
        } else {
+               uint8_t         tx_dma_index = ao_i2c_stm_info[index].tx_dma_index;
                uint8_t         rx_dma_index = ao_i2c_stm_info[index].rx_dma_index;
+               ao_dma_mutex_get(tx_dma_index);
                ao_dma_set_transfer(rx_dma_index,
                                    &stm_i2c->dr,
                                    block,
@@ -349,8 +354,9 @@ ao_i2c_recv(void *block, uint16_t len, uint8_t index, uint8_t stop)
                                break;
                ao_arch_release_interrupts();
                ret = ao_dma_done[rx_dma_index];
-               ao_dma_done_transfer(rx_dma_index);
                stm_i2c->cr1 = AO_STM_I2C_CR1 | (1 << STM_I2C_CR1_STOP);
+               ao_dma_done_transfer(rx_dma_index);
+               ao_dma_mutex_put(tx_dma_index);
        }
        if (stop)
                ao_i2c_wait_stop(index);