From 20e5b7fdd07d31981c908d2d06b48ebfeee6ad21 Mon Sep 17 00:00:00 2001 From: Florian Vaussard Date: Mon, 5 Sep 2016 16:54:49 +0200 Subject: [PATCH] gpio: stm32: Use atomic set/reset in stm32_gpio_set() The current implementation of stm32_gpio_set() uses the GPIO output data register to change the state of individual GPIOs. The generated assembler needs at least 3 instructions: load / modify / store. This opens a small race window, for example if a thread and an interrupt both try to change the state of the same GPIO bank. Use the GPIO bit set/reset register to perform the atomic change without locking. This also has the benefit of a more optimised implementation, which can be useful for GPIO-intensive work. Compare the new version: 08000c98 : 8000c98: f001 010f and.w r1, r1, #15 8000c9c: 2301 movs r3, #1 8000c9e: b902 cbnz r2, 8000ca2 8000ca0: 3110 adds r1, #16 8000ca2: 408b lsls r3, r1 8000ca4: 6183 str r3, [r0, #24] 8000ca6: 2000 movs r0, #0 8000ca8: 4770 bx lr and the old one: 08000c98 : 8000c98: 2301 movs r3, #1 8000c9a: f001 010f and.w r1, r1, #15 8000c9e: fa03 f101 lsl.w r1, r3, r1 8000ca2: 6943 ldr r3, [r0, #20] 8000ca4: b10a cbz r2, 8000caa 8000ca6: 4319 orrs r1, r3 8000ca8: e001 b.n 8000cae 8000caa: ea23 0101 bic.w r1, r3, r1 8000cae: 6141 str r1, [r0, #20] 8000cb0: 2000 movs r0, #0 8000cb2: 4770 bx lr Change-Id: Ie5800d1c345016028d1b9a099f5d74cac35f592a Signed-off-by: Florian Vaussard --- arch/arm/soc/st_stm32/stm32f4/soc_gpio.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/arm/soc/st_stm32/stm32f4/soc_gpio.c b/arch/arm/soc/st_stm32/stm32f4/soc_gpio.c index 03c9bfe8f54a24..875fdcee7b5520 100644 --- a/arch/arm/soc/st_stm32/stm32f4/soc_gpio.c +++ b/arch/arm/soc/st_stm32/stm32f4/soc_gpio.c @@ -210,12 +210,12 @@ int stm32_gpio_set(uint32_t *base, int pin, int value) { struct stm32f4x_gpio *gpio = (struct stm32f4x_gpio *)base; - int pval = 1 << (pin & 0xf); - if (value) { - gpio->odr |= pval; + /* atomic set */ + gpio->bsr = (1 << (pin & 0x0f)); } else { - gpio->odr &= ~pval; + /* atomic reset */ + gpio->bsr = (1 << ((pin & 0x0f) + 0x10)); } return 0;