summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnton Staaf <robotboy@chromium.org>2014-07-09 10:58:55 -0700
committerchrome-internal-fetch <chrome-internal-fetch@google.com>2014-07-17 00:39:47 +0000
commit1e73a1ba06b7879de3b03c7cefec9135bf73fcf5 (patch)
treeb505dc075465c02b383062e0f6ed15c8a4d2acc7
parent950894b27e7c97af15738e56e0ecd2fd8abd4874 (diff)
downloadchrome-ec-1e73a1ba06b7879de3b03c7cefec9135bf73fcf5.tar.gz
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 <robotboy@chromium.org> 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 <robotboy@chromium.org> Reviewed-by: Randall Spangler <rspangler@chromium.org> Commit-Queue: Anton Staaf <robotboy@chromium.org>
-rw-r--r--chip/stm32/build.mk2
-rw-r--r--chip/stm32/gpio-f0-l.c123
-rw-r--r--chip/stm32/gpio-stm32f.c141
-rw-r--r--chip/stm32/gpio-stm32f0.c226
-rw-r--r--chip/stm32/gpio-stm32l.c228
-rw-r--r--chip/stm32/gpio.c125
-rw-r--r--include/gpio.h16
7 files changed, 303 insertions, 558 deletions
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 */