| /* 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_chip.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; |
| } |
| |
| int gpio_get_flags_by_mask(uint32_t port, uint32_t mask) |
| { |
| uint32_t flags = 0; |
| uint32_t val = 0; |
| const uint32_t mask2 = expand_to_2bit_mask(mask); |
| |
| /* Only one bit must be set. */ |
| if ((mask != (mask & -mask)) || (mask == 0)) |
| return 0; |
| |
| /* Check output type. */ |
| val = STM32_GPIO_PUPDR(port) & mask2; |
| if (val == (0x55555555 & mask2)) |
| flags |= GPIO_PULL_UP; |
| if (val == (0xaaaaaaaa & mask2)) |
| flags |= GPIO_PULL_DOWN; |
| |
| if (STM32_GPIO_OTYPER(port) & mask) |
| flags |= GPIO_OPEN_DRAIN; |
| |
| /* Check mode. */ |
| val = STM32_GPIO_MODER(port) & mask2; |
| if (val == (0x55555555 & mask2)) |
| flags |= GPIO_OUTPUT; |
| if (val == (0xFFFFFFFF & mask2)) |
| flags |= GPIO_ANALOG; |
| if (val == (0x0 & mask2)) |
| flags |= GPIO_INPUT; |
| if (val == (0xaaaaaaaa & mask2)) |
| flags |= GPIO_ALTERNATE; |
| |
| if (flags & GPIO_OUTPUT) { |
| if (STM32_GPIO_ODR(port) & mask) |
| flags |= GPIO_HIGH; |
| else |
| flags |= GPIO_LOW; |
| } |
| |
| |
| if (STM32_EXTI_RTSR & mask) |
| flags |= GPIO_INT_F_RISING; |
| if (STM32_EXTI_RTSR & mask) |
| flags |= GPIO_INT_F_RISING; |
| |
| return flags; |
| } |
| |
| 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; |
| else |
| 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; |
| } else if (flags & GPIO_ALTERNATE) { |
| /* Alternate, MODE=10 */ |
| val |= 0xaaaaaaaa & mask2; |
| 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; |
| } |