| /* |
| * Copyright (c) 2016 Open-RnD Sp. z o.o. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /** |
| * @brief |
| * |
| * Based on reference manual: |
| * STM32F101xx, STM32F102xx, STM32F103xx, STM32F105xx and STM32F107xx |
| * advanced ARM ® -based 32-bit MCUs |
| * |
| * Chapter 9: General-purpose and alternate-function I/Os |
| * (GPIOs and AFIOs) |
| */ |
| |
| #include <errno.h> |
| |
| #include <device.h> |
| #include "soc.h" |
| #include "soc_registers.h" |
| #include <gpio.h> |
| #include <gpio/gpio_stm32.h> |
| |
| /** |
| * @brief map pin function to MODE register value |
| */ |
| static uint32_t __func_to_mode(int func) |
| { |
| switch (func) { |
| case STM32F10X_PIN_CONFIG_ANALOG: |
| case STM32F10X_PIN_CONFIG_BIAS_HIGH_IMPEDANCE: |
| case STM32F10X_PIN_CONFIG_BIAS_PULL_UP: |
| case STM32F10X_PIN_CONFIG_BIAS_PULL_DOWN: |
| return 0; |
| case STM32F10X_PIN_CONFIG_DRIVE_OPEN_DRAIN: |
| case STM32F10X_PIN_CONFIG_DRIVE_PUSH_PULL: |
| case STM32F10X_PIN_CONFIG_AF_PUSH_PULL: |
| case STM32F10X_PIN_CONFIG_AF_OPEN_DRAIN: |
| return 0x1; |
| } |
| return 0; |
| } |
| |
| /** |
| * @brief map pin function to CNF register value |
| */ |
| static uint32_t __func_to_cnf(int func) |
| { |
| switch (func) { |
| case STM32F10X_PIN_CONFIG_ANALOG: |
| return 0x0; |
| case STM32F10X_PIN_CONFIG_BIAS_HIGH_IMPEDANCE: |
| return 0x1; |
| case STM32F10X_PIN_CONFIG_BIAS_PULL_UP: |
| case STM32F10X_PIN_CONFIG_BIAS_PULL_DOWN: |
| return 0x2; |
| case STM32F10X_PIN_CONFIG_DRIVE_PUSH_PULL: |
| return 0x0; |
| case STM32F10X_PIN_CONFIG_DRIVE_OPEN_DRAIN: |
| return 0x1; |
| case STM32F10X_PIN_CONFIG_AF_PUSH_PULL: |
| return 0x2; |
| case STM32F10X_PIN_CONFIG_AF_OPEN_DRAIN: |
| return 0x3; |
| } |
| return 0; |
| } |
| |
| int stm32_gpio_flags_to_conf(int flags, int *pincfg) |
| { |
| int direction = flags & GPIO_DIR_MASK; |
| |
| if (!pincfg) { |
| return -EINVAL; |
| } |
| |
| if (direction == GPIO_DIR_OUT) { |
| *pincfg = STM32F10X_PIN_CONFIG_DRIVE_PUSH_PULL; |
| } else if (direction == GPIO_DIR_IN) { |
| int pud = flags & GPIO_PUD_MASK; |
| |
| /* pull-{up,down} maybe? */ |
| if (pud == GPIO_PUD_PULL_UP) { |
| *pincfg = STM32F10X_PIN_CONFIG_BIAS_PULL_UP; |
| } else if (pud == GPIO_PUD_PULL_DOWN) { |
| *pincfg = STM32F10X_PIN_CONFIG_BIAS_PULL_DOWN; |
| } else { |
| /* floating */ |
| *pincfg = STM32F10X_PIN_CONFIG_BIAS_HIGH_IMPEDANCE; |
| } |
| } else { |
| return -ENOTSUP; |
| } |
| |
| return 0; |
| } |
| |
| int stm32_gpio_configure(uint32_t *base_addr, int pin, int conf) |
| { |
| volatile struct stm32f10x_gpio *gpio = |
| (struct stm32f10x_gpio *)(base_addr); |
| int cnf, mode; |
| int crpin = pin; |
| |
| /* pins are configured in CRL (0-7) and CRH (8-15) |
| * registers |
| */ |
| volatile uint32_t *reg = &gpio->crl; |
| |
| if (crpin > 7) { |
| reg = &gpio->crh; |
| crpin -= 8; |
| } |
| |
| /* each port is configured by 2 registers: |
| * CNFy[1:0]: Port x configuration bits |
| * MODEy[1:0]: Port x mode bits |
| * |
| * memory layout is repeated for every port: |
| * | CNF | MODE | |
| * | [0:1] | [0:1] | |
| */ |
| cnf = __func_to_cnf(conf); |
| mode = __func_to_mode(conf); |
| |
| /* clear bits */ |
| *reg &= ~(0xf << (crpin * 4)); |
| /* set bits */ |
| *reg |= (cnf << (crpin * 4 + 2) | mode << (crpin * 4)); |
| |
| /* input mode - 0x1 */ |
| if (conf == STM32F10X_PIN_CONFIG_BIAS_PULL_UP) { |
| /* enable pull up */ |
| gpio->odr |= 1 << pin; |
| } else if (conf == STM32F10X_PIN_CONFIG_BIAS_PULL_DOWN) { |
| /* or pull down */ |
| gpio->odr &= ~(1 << pin); |
| } |
| |
| return 0; |
| } |
| |
| int stm32_gpio_set(uint32_t *base, int pin, int value) |
| { |
| struct stm32f10x_gpio *gpio = (struct stm32f10x_gpio *)base; |
| |
| int pval = 1 << (pin & 0xf); |
| |
| if (value) { |
| gpio->odr |= pval; |
| } else { |
| gpio->odr &= ~pval; |
| } |
| |
| return 0; |
| } |
| |
| int stm32_gpio_get(uint32_t *base, int pin) |
| { |
| struct stm32f10x_gpio *gpio = (struct stm32f10x_gpio *)base; |
| |
| return (gpio->idr >> pin) & 0x1; |
| } |
| |
| int stm32_gpio_enable_int(int port, int pin) |
| { |
| volatile struct stm32f10x_afio *afio = |
| (struct stm32f10x_afio *)AFIO_BASE; |
| volatile union __afio_exticr *exticr; |
| int shift = 0; |
| |
| if (pin <= 3) { |
| exticr = &afio->exticr1; |
| } else if (pin <= 7) { |
| exticr = &afio->exticr2; |
| } else if (pin <= 11) { |
| exticr = &afio->exticr3; |
| } else if (pin <= 15) { |
| exticr = &afio->exticr4; |
| } else { |
| return -EINVAL; |
| } |
| |
| shift = 4 * (pin % 4); |
| |
| exticr->val &= ~(0xf << shift); |
| exticr->val |= port << shift; |
| |
| return 0; |
| } |