Linux Audio

Check our new training course

Embedded Linux Audio

Check our new training course
with Creative Commons CC-BY-SA
lecture materials

Bootlin logo

Elixir Cross Referencer

Loading...
/*
 * Copyright (c) 2016 Open-RnD Sp. z o.o.
 *
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @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) {
		/* Pin is configured as an output */
		*pincfg = STM32F10X_PIN_CONFIG_DRIVE_PUSH_PULL;
	} else {
		/* Pin is configured as an input */
		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;
		}
	}

	return 0;
}

int stm32_gpio_configure(uint32_t *base_addr, int pin, int conf, int altf)
{
	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;

	ARG_UNUSED(altf);

	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;
}