From 1e73a1ba06b7879de3b03c7cefec9135bf73fcf5 Mon Sep 17 00:00:00 2001 From: Anton Staaf Date: Wed, 9 Jul 2014 10:58:55 -0700 Subject: GPIO: Reduce code duplication in STM32 gpio driver Previously the F0 and L variants had almost identical driver files and the F variant shared about half of its driver. This refactor moves the shared code into gpio.c and gpio-f0-l.c, the latter is for code shared between the F0 and L variants. Signed-off-by: Anton Staaf BRANCH=none TEST=make buildall -j Followed by manual testing of interrupt on change and UART functionality on STM32F0 based discovery board. Change-Id: I920babd1861548272af2857c8bd3e4f9dac4985c Reviewed-on: https://chromium-review.googlesource.com/207986 Tested-by: Anton Staaf Reviewed-by: Randall Spangler Commit-Queue: Anton Staaf --- chip/stm32/build.mk | 2 +- chip/stm32/gpio-f0-l.c | 123 +++++++++++++++++++++++++ chip/stm32/gpio-stm32f.c | 141 ++++------------------------ chip/stm32/gpio-stm32f0.c | 226 ++------------------------------------------- chip/stm32/gpio-stm32l.c | 228 +++------------------------------------------- chip/stm32/gpio.c | 125 +++++++++++++++++++++++++ include/gpio.h | 16 ++++ 7 files changed, 303 insertions(+), 558 deletions(-) create mode 100644 chip/stm32/gpio-f0-l.c create mode 100644 chip/stm32/gpio.c diff --git a/chip/stm32/build.mk b/chip/stm32/build.mk index 1700fb190d..da9a54f363 100644 --- a/chip/stm32/build.mk +++ b/chip/stm32/build.mk @@ -28,7 +28,7 @@ chip-y+=jtag-$(CHIP_FAMILY).o clock-$(CHIP_FAMILY).o chip-$(CONFIG_SPI)+=spi.o chip-$(CONFIG_SPI_MASTER_PORT)+=spi_master.o chip-$(CONFIG_SW_CRC)+=crc.o -chip-$(CONFIG_COMMON_GPIO)+=gpio-$(CHIP_FAMILY).o +chip-$(CONFIG_COMMON_GPIO)+=gpio.o gpio-$(CHIP_FAMILY).o chip-$(CONFIG_COMMON_TIMER)+=hwtimer$(TIMER_TYPE).o chip-$(CONFIG_I2C)+=i2c-$(CHIP_FAMILY).o chip-$(CONFIG_WATCHDOG)+=watchdog.o diff --git a/chip/stm32/gpio-f0-l.c b/chip/stm32/gpio-f0-l.c new file mode 100644 index 0000000000..d3369d9d89 --- /dev/null +++ b/chip/stm32/gpio-f0-l.c @@ -0,0 +1,123 @@ +/* Copyright (c) 2014 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* + * GPIO module for Chrome EC + * + * These functions are shared by the STM32F0 and STM32L variants. + */ + +#include "common.h" +#include "gpio.h" +#include "registers.h" +#include "util.h" + +static uint32_t expand_to_2bit_mask(uint32_t mask) +{ + uint32_t mask_out = 0; + while (mask) { + int bit = get_next_bit(&mask); + mask_out |= 3 << (bit * 2); + } + return mask_out; +} + +void gpio_set_flags_by_mask(uint32_t port, uint32_t mask, uint32_t flags) +{ + /* Bitmask for registers with 2 bits per GPIO pin */ + const uint32_t mask2 = expand_to_2bit_mask(mask); + uint32_t val; + + /* Set up pullup / pulldown */ + val = STM32_GPIO_PUPDR(port) & ~mask2; + if (flags & GPIO_PULL_UP) + val |= 0x55555555 & mask2; /* Pull Up = 01 */ + else if (flags & GPIO_PULL_DOWN) + val |= 0xaaaaaaaa & mask2; /* Pull Down = 10 */ + STM32_GPIO_PUPDR(port) = val; + + /* + * Select open drain first, so that we don't glitch the signal when + * changing the line to an output. + */ + if (flags & GPIO_OPEN_DRAIN) + STM32_GPIO_OTYPER(port) |= mask; + + val = STM32_GPIO_MODER(port) & ~mask2; + if (flags & GPIO_OUTPUT) { + /* + * Set pin level first to avoid glitching. This is harmless on + * STM32L because the set/reset register isn't connected to the + * output drivers until the pin is made an output. + */ + if (flags & GPIO_HIGH) + STM32_GPIO_BSRR(port) = mask; + else if (flags & GPIO_LOW) + STM32_GPIO_BSRR(port) = mask << 16; + + /* General purpose, MODE = 01 */ + val |= 0x55555555 & mask2; + STM32_GPIO_MODER(port) = val; + + } else if (flags & GPIO_ANALOG) { + /* Analog, MODE=11 */ + val |= 0xFFFFFFFF & mask2; + STM32_GPIO_MODER(port) = val; + } else if (flags & GPIO_INPUT) { + /* Input, MODE=00 */ + STM32_GPIO_MODER(port) = val; + } + + /* Set up interrupts if necessary */ + ASSERT(!(flags & (GPIO_INT_F_LOW | GPIO_INT_F_HIGH))); + if (flags & GPIO_INT_F_RISING) + STM32_EXTI_RTSR |= mask; + if (flags & GPIO_INT_F_FALLING) + STM32_EXTI_FTSR |= mask; + /* Interrupt is enabled by gpio_enable_interrupt() */ +} + +void gpio_set_alternate_function(uint32_t port, uint32_t mask, int func) +{ + int bit; + uint32_t half; + uint32_t afr; + uint32_t moder = STM32_GPIO_MODER(port); + + if (func < 0) { + /* Return to normal GPIO function, defaulting to input. */ + while (mask) { + bit = get_next_bit(&mask); + moder &= ~(0x3 << (bit * 2)); + } + STM32_GPIO_MODER(port) = moder; + return; + } + + /* Low half of the GPIO bank */ + half = mask & 0xff; + afr = STM32_GPIO_AFRL(port); + while (half) { + bit = get_next_bit(&half); + afr &= ~(0xf << (bit * 4)); + afr |= func << (bit * 4); + moder &= ~(0x3 << (bit * 2 + 0)); + moder |= 0x2 << (bit * 2 + 0); + } + STM32_GPIO_AFRL(port) = afr; + + /* High half of the GPIO bank */ + half = (mask >> 8) & 0xff; + afr = STM32_GPIO_AFRH(port); + while (half) { + bit = get_next_bit(&half); + afr &= ~(0xf << (bit * 4)); + afr |= func << (bit * 4); + moder &= ~(0x3 << (bit * 2 + 16)); + moder |= 0x2 << (bit * 2 + 16); + } + STM32_GPIO_AFRH(port) = afr; + STM32_GPIO_MODER(port) = moder; +} diff --git a/chip/stm32/gpio-stm32f.c b/chip/stm32/gpio-stm32f.c index 8b6344fdad..147f461528 100644 --- a/chip/stm32/gpio-stm32f.c +++ b/chip/stm32/gpio-stm32f.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved. +/* Copyright (c) 2014 The Chromium OS Authors. All rights reserved. * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ @@ -6,32 +6,12 @@ /* GPIO module for Chrome EC */ #include "common.h" -#include "console.h" #include "gpio.h" #include "hooks.h" #include "registers.h" #include "task.h" #include "util.h" -/* Console output macros */ -#define CPUTS(outstr) cputs(CC_GPIO, outstr) -#define CPRINTS(format, args...) cprints(CC_GPIO, format, ## args) - -/* - * Special precautions must be taken in order to avoid accidentally rebooting - * the AP if we are warm rebooting the EC such as during sysjump. - */ -static int is_warm_boot; - -/* For each EXTI bit, record which GPIO entry is using it */ -static const struct gpio_info *exti_events[16]; - -struct port_config { - uint32_t addr; - uint32_t mode; - uint32_t cnf; -}; - /** * Helper function for generating bitmasks for STM32 GPIO config registers */ @@ -118,53 +98,32 @@ void gpio_set_flags_by_mask(uint32_t port, uint32_t pmask, uint32_t flags) /* Interrupt is enabled by gpio_enable_interrupt() */ } -void gpio_set_alternate_function(uint32_t port, uint32_t mask, int func) +int gpio_is_reboot_warm(void) { - /* TODO(crosbug.com/p/21618): implement me! */ + return (STM32_RCC_APB1ENR & 1); } -void gpio_pre_init(void) +void gpio_enable_clocks(void) { - const struct gpio_info *g = gpio_list; - int i; - - if (STM32_RCC_APB1ENR & 1) { - /* This is a warm reboot : TIM2 is already active */ - is_warm_boot = 1; - } else { - /* - * Enable all GPIOs clocks - * - * TODO(crosbug.com/p/23770): only enable the banks we need to, - * and support disabling some of them in low-power idle. - */ + /* + * Enable all GPIOs clocks + * + * TODO(crosbug.com/p/23770): only enable the banks we need to, + * and support disabling some of them in low-power idle. + */ #ifdef CHIP_VARIANT_STM32TS60 - STM32_RCC_APB2ENR |= 0x7fd; + STM32_RCC_APB2ENR |= 0x7fd; #else - STM32_RCC_APB2ENR |= 0x1fd; + STM32_RCC_APB2ENR |= 0x1fd; #endif - } - - /* Set all GPIOs to defaults */ - for (i = 0; i < GPIO_COUNT; i++, g++) { - int flags = g->flags; - - if (flags & GPIO_DEFAULT) - continue; - - /* - * If this is a warm reboot, don't set the output levels or - * we'll shut off the AP. - */ - if (is_warm_boot) - flags &= ~(GPIO_LOW | GPIO_HIGH); +} - /* Set up GPIO based on flags */ - gpio_set_flags(i, flags); - } +void gpio_set_alternate_function(uint32_t port, uint32_t mask, int func) +{ + /* TODO(crosbug.com/p/21618): implement me! */ } -void gpio_init(void) +static void gpio_init(void) { /* Enable IRQs now that pins are set up */ task_enable_irq(STM32_IRQ_EXTI0); @@ -177,72 +136,6 @@ void gpio_init(void) } DECLARE_HOOK(HOOK_INIT, gpio_init, HOOK_PRIO_DEFAULT); -uint16_t *gpio_get_level_reg(enum gpio_signal signal, uint32_t *mask) -{ - *mask = gpio_list[signal].mask; - return (uint16_t *)&STM32_GPIO_IDR(gpio_list[signal].port); -} - - -test_mockable int gpio_get_level(enum gpio_signal signal) -{ - return !!(STM32_GPIO_IDR(gpio_list[signal].port) & - gpio_list[signal].mask); -} - - -void gpio_set_level(enum gpio_signal signal, int value) -{ - STM32_GPIO_BSRR(gpio_list[signal].port) = - gpio_list[signal].mask << (value ? 0 : 16); -} - -int gpio_enable_interrupt(enum gpio_signal signal) -{ - const struct gpio_info *g = gpio_list + signal; - uint32_t bit, group, shift, bank; - - /* Fail if not implemented or no interrupt handler */ - if (!g->mask || !g->irq_handler) - return EC_ERROR_INVAL; - - bit = 31 - __builtin_clz(g->mask); - - if (exti_events[bit]) { - CPRINTS("Overriding %s with %s on EXTI%d", - exti_events[bit]->name, g->name, bit); - } - exti_events[bit] = g; - - group = bit / 4; - shift = (bit % 4) * 4; - bank = (g->port - STM32_GPIOA_BASE) / 0x400; - STM32_AFIO_EXTICR(group) = (STM32_AFIO_EXTICR(group) & - ~(0xF << shift)) | (bank << shift); - STM32_EXTI_IMR |= g->mask; - - return EC_SUCCESS; -} - -/*****************************************************************************/ -/* Interrupt handler */ - -void gpio_interrupt(void) -{ - int bit; - const struct gpio_info *g; - /* process only GPIO EXTINTs (EXTINT0..15) not other EXTINTs */ - uint32_t pending = STM32_EXTI_PR & 0xFFFF; - - STM32_EXTI_PR = pending; - - while (pending) { - bit = get_next_bit(&pending); - g = exti_events[bit]; - if (g && g->irq_handler) - g->irq_handler(g - gpio_list); - } -} DECLARE_IRQ(STM32_IRQ_EXTI0, gpio_interrupt, 1); DECLARE_IRQ(STM32_IRQ_EXTI1, gpio_interrupt, 1); DECLARE_IRQ(STM32_IRQ_EXTI2, gpio_interrupt, 1); diff --git a/chip/stm32/gpio-stm32f0.c b/chip/stm32/gpio-stm32f0.c index 5cbb61993e..58a28a25f1 100644 --- a/chip/stm32/gpio-stm32f0.c +++ b/chip/stm32/gpio-stm32f0.c @@ -6,124 +6,26 @@ /* GPIO module for Chrome EC */ #include "common.h" -#include "console.h" #include "gpio.h" #include "hooks.h" #include "registers.h" #include "task.h" #include "util.h" -/* Console output macros */ -#define CPUTS(outstr) cputs(CC_GPIO, outstr) -#define CPRINTS(format, args...) cprints(CC_GPIO, format, ## args) - -/* For each EXTI bit, record which GPIO entry is using it */ -static const struct gpio_info *exti_events[16]; - -static uint32_t expand_to_2bit_mask(uint32_t mask) +int gpio_is_reboot_warm(void) { - uint32_t mask_out = 0; - while (mask) { - int bit = get_next_bit(&mask); - mask_out |= 3 << (bit * 2); - } - return mask_out; + return ((STM32_RCC_AHBENR & 0x7e0000) == 0x7e0000); } -void gpio_set_flags_by_mask(uint32_t port, uint32_t mask, uint32_t flags) +void gpio_enable_clocks(void) { - /* Bitmask for registers with 2 bits per GPIO pin */ - const uint32_t mask2 = expand_to_2bit_mask(mask); - uint32_t val; - - /* Set up pullup / pulldown */ - val = STM32_GPIO_PUPDR(port) & ~mask2; - if (flags & GPIO_PULL_UP) - val |= 0x55555555 & mask2; /* Pull Up = 01 */ - else if (flags & GPIO_PULL_DOWN) - val |= 0xaaaaaaaa & mask2; /* Pull Down = 10 */ - STM32_GPIO_PUPDR(port) = val; - /* - * Select open drain first, so that we don't glitch the signal when - * changing the line to an output. + * Enable all GPIOs clocks + * + * TODO(crosbug.com/p/23770): only enable the banks we need to, + * and support disabling some of them in low-power idle. */ - if (flags & GPIO_OPEN_DRAIN) - STM32_GPIO_OTYPER(port) |= mask; - - val = STM32_GPIO_MODER(port) & ~mask2; - if (flags & GPIO_OUTPUT) { - /* - * Set pin level first to avoid glitching. This is harmless on - * STM32L because the set/reset register isn't connected to the - * output drivers until the pin is made an output. - */ - if (flags & GPIO_HIGH) - STM32_GPIO_BSRR(port) = mask; - else if (flags & GPIO_LOW) - STM32_GPIO_BSRR(port) = mask << 16; - - /* General purpose, MODE = 01 */ - val |= 0x55555555 & mask2; - STM32_GPIO_MODER(port) = val; - - } else if (flags & GPIO_ANALOG) { - /* Analog, MODE=11 */ - val |= 0xFFFFFFFF & mask2; - STM32_GPIO_MODER(port) = val; - } else if (flags & GPIO_INPUT) { - /* Input, MODE=00 */ - STM32_GPIO_MODER(port) = val; - } - - /* Set up interrupts if necessary */ - ASSERT(!(flags & (GPIO_INT_F_LOW | GPIO_INT_F_HIGH))); - if (flags & GPIO_INT_F_RISING) - STM32_EXTI_RTSR |= mask; - if (flags & GPIO_INT_F_FALLING) - STM32_EXTI_FTSR |= mask; - /* Interrupt is enabled by gpio_enable_interrupt() */ -} - -void gpio_pre_init(void) -{ - const struct gpio_info *g = gpio_list; - int is_warm = 0; - int i; - - /* Required to configure external IRQ lines (SYSCFG_EXTICRn) */ - STM32_RCC_APB2ENR |= 1 << 0; - - if ((STM32_RCC_AHBENR & 0x7e0000) == 0x7e0000) { - /* This is a warm reboot */ - is_warm = 1; - } else { - /* - * Enable all GPIOs clocks - * - * TODO(crosbug.com/p/23770): only enable the banks we need to, - * and support disabling some of them in low-power idle. - */ - STM32_RCC_AHBENR |= 0x7e0000; - } - - /* Set all GPIOs to defaults */ - for (i = 0; i < GPIO_COUNT; i++, g++) { - int flags = g->flags; - - if (flags & GPIO_DEFAULT) - continue; - - /* - * If this is a warm reboot, don't set the output levels or - * we'll shut off the AP. - */ - if (is_warm) - flags &= ~(GPIO_LOW | GPIO_HIGH); - - /* Set up GPIO based on flags */ - gpio_set_flags_by_mask(g->port, g->mask, flags); - } + STM32_RCC_AHBENR |= 0x7e0000; } static void gpio_init(void) @@ -135,116 +37,8 @@ static void gpio_init(void) } DECLARE_HOOK(HOOK_INIT, gpio_init, HOOK_PRIO_DEFAULT); -void gpio_set_alternate_function(uint32_t port, uint32_t mask, int func) -{ - int bit; - uint8_t half; - uint32_t afr; - uint32_t moder = STM32_GPIO_MODER(port); - - if (func < 0) { - /* Return to normal GPIO function, defaulting to input. */ - while (mask) { - bit = get_next_bit(&mask); - moder &= ~(0x3 << (bit * 2)); - } - STM32_GPIO_MODER(port) = moder; - return; - } - - /* Low half of the GPIO bank */ - half = mask & 0xff; - afr = STM32_GPIO_AFRL(port); - while (half) { - bit = 31 - __builtin_clz(half); - afr &= ~(0xf << (bit * 4)); - afr |= func << (bit * 4); - moder &= ~(0x3 << (bit * 2 + 0)); - moder |= 0x2 << (bit * 2 + 0); - half &= ~(1 << bit); - } - STM32_GPIO_AFRL(port) = afr; - - /* High half of the GPIO bank */ - half = mask >> 8; - afr = STM32_GPIO_AFRH(port); - while (half) { - bit = 31 - __builtin_clz(half); - afr &= ~(0xf << (bit * 4)); - afr |= func << (bit * 4); - moder &= ~(0x3 << (bit * 2 + 16)); - moder |= 0x2 << (bit * 2 + 16); - half &= ~(1 << bit); - } - STM32_GPIO_AFRH(port) = afr; - STM32_GPIO_MODER(port) = moder; -} - -test_mockable int gpio_get_level(enum gpio_signal signal) -{ - return !!(STM32_GPIO_IDR(gpio_list[signal].port) & - gpio_list[signal].mask); -} - -uint16_t *gpio_get_level_reg(enum gpio_signal signal, uint32_t *mask) -{ - *mask = gpio_list[signal].mask; - return (uint16_t *)&STM32_GPIO_IDR(gpio_list[signal].port); -} - -void gpio_set_level(enum gpio_signal signal, int value) -{ - STM32_GPIO_BSRR(gpio_list[signal].port) = - gpio_list[signal].mask << (value ? 0 : 16); -} - -int gpio_enable_interrupt(enum gpio_signal signal) -{ - const struct gpio_info *g = gpio_list + signal; - uint32_t bit, group, shift, bank; - - /* Fail if not implemented or no interrupt handler */ - if (!g->mask || !g->irq_handler) - return EC_ERROR_INVAL; - - bit = 31 - __builtin_clz(g->mask); - - if (exti_events[bit]) { - CPRINTS("Overriding %s with %s on EXTI%d", - exti_events[bit]->name, g->name, bit); - } - exti_events[bit] = g; - - group = bit / 4; - shift = (bit % 4) * 4; - bank = (g->port - STM32_GPIOA_BASE) / 0x400; - STM32_SYSCFG_EXTICR(group) = (STM32_SYSCFG_EXTICR(group) & - ~(0xF << shift)) | (bank << shift); - STM32_EXTI_IMR |= g->mask; - - return EC_SUCCESS; -} - -/*****************************************************************************/ -/* Interrupt handler */ - -void gpio_interrupt(void) -{ - int bit; - const struct gpio_info *g; - /* process only GPIO EXTINTs (EXTINT0..15) not other EXTINTs */ - uint32_t pending = STM32_EXTI_PR & 0xFFFF; - - STM32_EXTI_PR = pending; - - while (pending) { - bit = 31 - __builtin_clz(pending); - g = exti_events[bit]; - if (g && g->irq_handler) - g->irq_handler(g - gpio_list); - pending &= ~(1 << bit); - } -} DECLARE_IRQ(STM32_IRQ_EXTI0_1, gpio_interrupt, 1); DECLARE_IRQ(STM32_IRQ_EXTI2_3, gpio_interrupt, 1); DECLARE_IRQ(STM32_IRQ_EXTI4_15, gpio_interrupt, 1); + +#include "gpio-f0-l.c" diff --git a/chip/stm32/gpio-stm32l.c b/chip/stm32/gpio-stm32l.c index b6cb2bf71f..44282fc5c0 100644 --- a/chip/stm32/gpio-stm32l.c +++ b/chip/stm32/gpio-stm32l.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved. +/* Copyright (c) 2014 The Chromium OS Authors. All rights reserved. * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ @@ -6,124 +6,26 @@ /* GPIO module for Chrome EC */ #include "common.h" -#include "console.h" #include "gpio.h" #include "hooks.h" #include "registers.h" #include "task.h" #include "util.h" -/* Console output macros */ -#define CPUTS(outstr) cputs(CC_GPIO, outstr) -#define CPRINTS(format, args...) cprints(CC_GPIO, format, ## args) - -/* For each EXTI bit, record which GPIO entry is using it */ -static const struct gpio_info *exti_events[16]; - -static uint32_t expand_to_2bit_mask(uint32_t mask) +int gpio_is_reboot_warm(void) { - uint32_t mask_out = 0; - while (mask) { - int bit = get_next_bit(&mask); - mask_out |= 3 << (bit * 2); - } - return mask_out; + return ((STM32_RCC_AHBENR & 0x3f) == 0x3f); } -void gpio_set_flags_by_mask(uint32_t port, uint32_t mask, uint32_t flags) +void gpio_enable_clocks(void) { - /* Bitmask for registers with 2 bits per GPIO pin */ - const uint32_t mask2 = expand_to_2bit_mask(mask); - uint32_t val; - - /* Set up pullup / pulldown */ - val = STM32_GPIO_PUPDR(port) & ~mask2; - if (flags & GPIO_PULL_UP) - val |= 0x55555555 & mask2; /* Pull Up = 01 */ - else if (flags & GPIO_PULL_DOWN) - val |= 0xaaaaaaaa & mask2; /* Pull Down = 10 */ - STM32_GPIO_PUPDR(port) = val; - /* - * Select open drain first, so that we don't glitch the signal when - * changing the line to an output. + * Enable all GPIOs clocks + * + * TODO(crosbug.com/p/23770): only enable the banks we need to, + * and support disabling some of them in low-power idle. */ - if (flags & GPIO_OPEN_DRAIN) - STM32_GPIO_OTYPER(port) |= mask; - - val = STM32_GPIO_MODER(port) & ~mask2; - if (flags & GPIO_OUTPUT) { - /* - * Set pin level first to avoid glitching. This is harmless on - * STM32L because the set/reset register isn't connected to the - * output drivers until the pin is made an output. - */ - if (flags & GPIO_HIGH) - STM32_GPIO_BSRR(port) = mask; - else if (flags & GPIO_LOW) - STM32_GPIO_BSRR(port) = mask << 16; - - /* General purpose, MODE = 01 */ - val |= 0x55555555 & mask2; - STM32_GPIO_MODER(port) = val; - - } else if (flags & GPIO_ANALOG) { - /* Analog, MODE=11 */ - val |= 0xFFFFFFFF & mask2; - STM32_GPIO_MODER(port) = val; - } else if (flags & GPIO_INPUT) { - /* Input, MODE=00 */ - STM32_GPIO_MODER(port) = val; - } - - /* Set up interrupts if necessary */ - ASSERT(!(flags & (GPIO_INT_F_LOW | GPIO_INT_F_HIGH))); - if (flags & GPIO_INT_F_RISING) - STM32_EXTI_RTSR |= mask; - if (flags & GPIO_INT_F_FALLING) - STM32_EXTI_FTSR |= mask; - /* Interrupt is enabled by gpio_enable_interrupt() */ -} - -void gpio_pre_init(void) -{ - const struct gpio_info *g = gpio_list; - int is_warm = 0; - int i; - - /* Required to configure external IRQ lines (SYSCFG_EXTICRn) */ - STM32_RCC_APB2ENR |= 1 << 0; - - if ((STM32_RCC_AHBENR & 0x3f) == 0x3f) { - /* This is a warm reboot */ - is_warm = 1; - } else { - /* - * Enable all GPIOs clocks - * - * TODO(crosbug.com/p/23770): only enable the banks we need to, - * and support disabling some of them in low-power idle. - */ - STM32_RCC_AHBENR |= 0x3f; - } - - /* Set all GPIOs to defaults */ - for (i = 0; i < GPIO_COUNT; i++, g++) { - int flags = g->flags; - - if (flags & GPIO_DEFAULT) - continue; - - /* - * If this is a warm reboot, don't set the output levels or - * we'll shut off the AP. - */ - if (is_warm) - flags &= ~(GPIO_LOW | GPIO_HIGH); - - /* Set up GPIO based on flags */ - gpio_set_flags_by_mask(g->port, g->mask, flags); - } + STM32_RCC_AHBENR |= 0x3f; } static void gpio_init(void) @@ -139,116 +41,6 @@ static void gpio_init(void) } DECLARE_HOOK(HOOK_INIT, gpio_init, HOOK_PRIO_DEFAULT); -void gpio_set_alternate_function(uint32_t port, uint32_t mask, int func) -{ - int bit; - uint8_t half; - uint32_t afr; - uint32_t moder = STM32_GPIO_MODER(port); - - if (func < 0) { - /* Return to normal GPIO function, defaulting to input. */ - while (mask) { - bit = get_next_bit(&mask); - moder &= ~(0x3 << (bit * 2)); - } - STM32_GPIO_MODER(port) = moder; - return; - } - - /* Low half of the GPIO bank */ - half = mask & 0xff; - afr = STM32_GPIO_AFRL(port); - while (half) { - bit = 31 - __builtin_clz(half); - afr &= ~(0xf << (bit * 4)); - afr |= func << (bit * 4); - moder &= ~(0x3 << (bit * 2 + 0)); - moder |= 0x2 << (bit * 2 + 0); - half &= ~(1 << bit); - } - STM32_GPIO_AFRL(port) = afr; - - /* High half of the GPIO bank */ - half = mask >> 8; - afr = STM32_GPIO_AFRH(port); - while (half) { - bit = 31 - __builtin_clz(half); - afr &= ~(0xf << (bit * 4)); - afr |= func << (bit * 4); - moder &= ~(0x3 << (bit * 2 + 16)); - moder |= 0x2 << (bit * 2 + 16); - half &= ~(1 << bit); - } - STM32_GPIO_AFRH(port) = afr; - STM32_GPIO_MODER(port) = moder; -} - -test_mockable int gpio_get_level(enum gpio_signal signal) -{ - return !!(STM32_GPIO_IDR(gpio_list[signal].port) & - gpio_list[signal].mask); -} - -uint16_t *gpio_get_level_reg(enum gpio_signal signal, uint32_t *mask) -{ - *mask = gpio_list[signal].mask; - return (uint16_t *)&STM32_GPIO_IDR(gpio_list[signal].port); -} - -void gpio_set_level(enum gpio_signal signal, int value) -{ - STM32_GPIO_BSRR(gpio_list[signal].port) = - gpio_list[signal].mask << (value ? 0 : 16); -} - -int gpio_enable_interrupt(enum gpio_signal signal) -{ - const struct gpio_info *g = gpio_list + signal; - uint32_t bit, group, shift, bank; - - /* Fail if not implemented or no interrupt handler */ - if (!g->mask || !g->irq_handler) - return EC_ERROR_INVAL; - - bit = 31 - __builtin_clz(g->mask); - - if (exti_events[bit]) { - CPRINTS("Overriding %s with %s on EXTI%d", - exti_events[bit]->name, g->name, bit); - } - exti_events[bit] = g; - - group = bit / 4; - shift = (bit % 4) * 4; - bank = (g->port - STM32_GPIOA_BASE) / 0x400; - STM32_SYSCFG_EXTICR(group) = (STM32_SYSCFG_EXTICR(group) & - ~(0xF << shift)) | (bank << shift); - STM32_EXTI_IMR |= g->mask; - - return EC_SUCCESS; -} - -/*****************************************************************************/ -/* Interrupt handler */ - -void gpio_interrupt(void) -{ - int bit; - const struct gpio_info *g; - /* process only GPIO EXTINTs (EXTINT0..15) not other EXTINTs */ - uint32_t pending = STM32_EXTI_PR & 0xFFFF; - - STM32_EXTI_PR = pending; - - while (pending) { - bit = 31 - __builtin_clz(pending); - g = exti_events[bit]; - if (g && g->irq_handler) - g->irq_handler(g - gpio_list); - pending &= ~(1 << bit); - } -} DECLARE_IRQ(STM32_IRQ_EXTI0, gpio_interrupt, 1); DECLARE_IRQ(STM32_IRQ_EXTI1, gpio_interrupt, 1); DECLARE_IRQ(STM32_IRQ_EXTI2, gpio_interrupt, 1); @@ -256,3 +48,5 @@ DECLARE_IRQ(STM32_IRQ_EXTI3, gpio_interrupt, 1); DECLARE_IRQ(STM32_IRQ_EXTI4, gpio_interrupt, 1); DECLARE_IRQ(STM32_IRQ_EXTI9_5, gpio_interrupt, 1); DECLARE_IRQ(STM32_IRQ_EXTI15_10, gpio_interrupt, 1); + +#include "gpio-f0-l.c" diff --git a/chip/stm32/gpio.c b/chip/stm32/gpio.c new file mode 100644 index 0000000000..df65aba533 --- /dev/null +++ b/chip/stm32/gpio.c @@ -0,0 +1,125 @@ +/* Copyright (c) 2014 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* GPIO module for Chrome EC */ + +#include "common.h" +#include "console.h" +#include "gpio.h" +#include "hooks.h" +#include "registers.h" +#include "task.h" +#include "util.h" + +/* Console output macros */ +#define CPRINTS(format, args...) cprints(CC_GPIO, format, ## args) + +/* For each EXTI bit, record which GPIO entry is using it */ +static const struct gpio_info *exti_events[16]; + +void gpio_pre_init(void) +{ + const struct gpio_info *g = gpio_list; + int is_warm = gpio_is_reboot_warm(); + int i; + + /* Required to configure external IRQ lines (SYSCFG_EXTICRn) */ + STM32_RCC_APB2ENR |= 1 << 0; + + if (!is_warm) + gpio_enable_clocks(); + + /* Set all GPIOs to defaults */ + for (i = 0; i < GPIO_COUNT; i++, g++) { + int flags = g->flags; + + if (flags & GPIO_DEFAULT) + continue; + + /* + * If this is a warm reboot, don't set the output levels or + * we'll shut off the AP. + */ + if (is_warm) + flags &= ~(GPIO_LOW | GPIO_HIGH); + + /* Set up GPIO based on flags */ + gpio_set_flags_by_mask(g->port, g->mask, flags); + } +} + +test_mockable int gpio_get_level(enum gpio_signal signal) +{ + return !!(STM32_GPIO_IDR(gpio_list[signal].port) & + gpio_list[signal].mask); +} + +uint16_t *gpio_get_level_reg(enum gpio_signal signal, uint32_t *mask) +{ + *mask = gpio_list[signal].mask; + return (uint16_t *)&STM32_GPIO_IDR(gpio_list[signal].port); +} + +void gpio_set_level(enum gpio_signal signal, int value) +{ + STM32_GPIO_BSRR(gpio_list[signal].port) = + gpio_list[signal].mask << (value ? 0 : 16); +} + +int gpio_enable_interrupt(enum gpio_signal signal) +{ + const struct gpio_info *g = gpio_list + signal; + uint32_t bit, group, shift, bank; + + /* Fail if not implemented or no interrupt handler */ + if (!g->mask || !g->irq_handler) + return EC_ERROR_INVAL; + + bit = 31 - __builtin_clz(g->mask); + + if (exti_events[bit]) { + CPRINTS("Overriding %s with %s on EXTI%d", + exti_events[bit]->name, g->name, bit); + } + exti_events[bit] = g; + + group = bit / 4; + shift = (bit % 4) * 4; + bank = (g->port - STM32_GPIOA_BASE) / 0x400; + +#if defined(CHIP_FAMILY_STM32F) + STM32_AFIO_EXTICR(group) = (STM32_AFIO_EXTICR(group) & + ~(0xF << shift)) | (bank << shift); +#elif defined(CHIP_FAMILY_STM32F0) || defined(CHIP_FAMILY_STM32L) + STM32_SYSCFG_EXTICR(group) = (STM32_SYSCFG_EXTICR(group) & + ~(0xF << shift)) | (bank << shift); +#else +#error "Unsupported chip family" +#endif + + STM32_EXTI_IMR |= g->mask; + + return EC_SUCCESS; +} + +/*****************************************************************************/ +/* Interrupt handler */ + +void gpio_interrupt(void) +{ + int bit; + const struct gpio_info *g; + /* process only GPIO EXTINTs (EXTINT0..15) not other EXTINTs */ + uint32_t pending = STM32_EXTI_PR & 0xFFFF; + + STM32_EXTI_PR = pending; + + while (pending) { + bit = get_next_bit(&pending); + g = exti_events[bit]; + if (g && g->irq_handler) + g->irq_handler(g - gpio_list); + } +} diff --git a/include/gpio.h b/include/gpio.h index 093d1566f2..3f76d43bfd 100644 --- a/include/gpio.h +++ b/include/gpio.h @@ -201,4 +201,20 @@ void gpio_set_flags_by_mask(uint32_t port, uint32_t mask, uint32_t flags); */ void gpio_set_alternate_function(uint32_t port, uint32_t mask, int func); +/** + * Return true if the EC is warm booting. + * + * This function is used by the GPIO implementation and should not be called + * outside of that context. + */ +int gpio_is_reboot_warm(void); + +/** + * Enable GPIO peripheral clocks. + * + * This function is used by the GPIO implementation and should not be called + * outside of that context. + */ +void gpio_enable_clocks(void); + #endif /* __CROS_EC_GPIO_H */ -- cgit v1.2.1