mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2025-12-27 18:25:05 +00:00
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>
This commit is contained in:
committed by
chrome-internal-fetch
parent
950894b27e
commit
1e73a1ba06
@@ -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
|
||||
|
||||
123
chip/stm32/gpio-f0-l.c
Normal file
123
chip/stm32/gpio-f0-l.c
Normal file
@@ -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;
|
||||
}
|
||||
@@ -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() */
|
||||
}
|
||||
|
||||
int gpio_is_reboot_warm(void)
|
||||
{
|
||||
return (STM32_RCC_APB1ENR & 1);
|
||||
}
|
||||
|
||||
void gpio_enable_clocks(void)
|
||||
{
|
||||
/*
|
||||
* 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;
|
||||
#else
|
||||
STM32_RCC_APB2ENR |= 0x1fd;
|
||||
#endif
|
||||
}
|
||||
|
||||
void gpio_set_alternate_function(uint32_t port, uint32_t mask, int func)
|
||||
{
|
||||
/* TODO(crosbug.com/p/21618): implement me! */
|
||||
}
|
||||
|
||||
void gpio_pre_init(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.
|
||||
*/
|
||||
#ifdef CHIP_VARIANT_STM32TS60
|
||||
STM32_RCC_APB2ENR |= 0x7fd;
|
||||
#else
|
||||
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_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);
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
125
chip/stm32/gpio.c
Normal file
125
chip/stm32/gpio.c
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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 */
|
||||
|
||||
Reference in New Issue
Block a user