Linux Audio

Check our new training course

Loading...
/*
 * Copyright (c) Linaro Limited.
 *
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @brief
 *
 * Based on reference manual:
 *   RM0368 Reference manual STM32F401xB/C and STM32F401xD/E
 *   advanced ARM ® -based 32-bit MCUs
 *
 * Chapter 8: General-purpose I/Os (GPIOs)
 */

#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 STM32F4X_PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
	case STM32F4X_PIN_CONFIG_BIAS_PULL_UP:
	case STM32F4X_PIN_CONFIG_BIAS_PULL_DOWN:
		return 0x0;
	case STM32F4X_PIN_CONFIG_DRIVE_PUSH_PULL:
	case STM32F4X_PIN_CONFIG_DRIVE_PUSH_UP:
	case STM32F4X_PIN_CONFIG_DRIVE_PUSH_DOWN:
	case STM32F4X_PIN_CONFIG_DRIVE_OPEN_DRAIN:
	case STM32F4X_PIN_CONFIG_DRIVE_OPEN_UP:
	case STM32F4X_PIN_CONFIG_DRIVE_OPEN_DOWN:
		return 0x1;
	case STM32F4X_PIN_CONFIG_AF_PUSH_PULL:
	case STM32F4X_PIN_CONFIG_AF_PUSH_UP:
	case STM32F4X_PIN_CONFIG_AF_PUSH_DOWN:
	case STM32F4X_PIN_CONFIG_AF_OPEN_DRAIN:
	case STM32F4X_PIN_CONFIG_AF_OPEN_UP:
	case STM32F4X_PIN_CONFIG_AF_OPEN_DOWN:
		return 0x2;
	case STM32F4X_PIN_CONFIG_ANALOG:
		return 0x3;
	}

	return 0;
}

/**
 * @brief map pin function to OTYPE register value
 */
static uint32_t __func_to_otype(int func)
{
	switch (func) {
	case STM32F4X_PIN_CONFIG_DRIVE_OPEN_DRAIN:
	case STM32F4X_PIN_CONFIG_DRIVE_OPEN_UP:
	case STM32F4X_PIN_CONFIG_DRIVE_OPEN_DOWN:
	case STM32F4X_PIN_CONFIG_AF_OPEN_DRAIN:
	case STM32F4X_PIN_CONFIG_AF_OPEN_UP:
	case STM32F4X_PIN_CONFIG_AF_OPEN_DOWN:
		return 0x1;
	}

	return 0;
}

/**
 * @brief map pin function to OSPEED register value
 */
static uint32_t __func_to_ospeed(int func)
{
	switch (func) {
	case STM32F4X_PIN_CONFIG_DRIVE_PUSH_PULL:
	case STM32F4X_PIN_CONFIG_DRIVE_PUSH_UP:
	case STM32F4X_PIN_CONFIG_DRIVE_PUSH_DOWN:
	case STM32F4X_PIN_CONFIG_DRIVE_OPEN_DRAIN:
	case STM32F4X_PIN_CONFIG_DRIVE_OPEN_UP:
	case STM32F4X_PIN_CONFIG_DRIVE_OPEN_DOWN:
	case STM32F4X_PIN_CONFIG_AF_PUSH_PULL:
	case STM32F4X_PIN_CONFIG_AF_PUSH_UP:
	case STM32F4X_PIN_CONFIG_AF_PUSH_DOWN:
	case STM32F4X_PIN_CONFIG_AF_OPEN_DRAIN:
	case STM32F4X_PIN_CONFIG_AF_OPEN_UP:
	case STM32F4X_PIN_CONFIG_AF_OPEN_DOWN:
		/* Force fast speed by default */
		return 0x2;
	}

	return 0;
}

/**
 * @brief map pin function to PUPD register value
 */
static uint32_t __func_to_pupd(int func)
{
	switch (func) {
	case STM32F4X_PIN_CONFIG_DRIVE_PUSH_PULL:
	case STM32F4X_PIN_CONFIG_DRIVE_OPEN_DRAIN:
	case STM32F4X_PIN_CONFIG_AF_PUSH_PULL:
	case STM32F4X_PIN_CONFIG_AF_OPEN_DRAIN:
	case STM32F4X_PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
	case STM32F4X_PIN_CONFIG_ANALOG:
		return 0x0;
	case STM32F4X_PIN_CONFIG_DRIVE_PUSH_UP:
	case STM32F4X_PIN_CONFIG_DRIVE_OPEN_UP:
	case STM32F4X_PIN_CONFIG_AF_PUSH_UP:
	case STM32F4X_PIN_CONFIG_AF_OPEN_UP:
	case STM32F4X_PIN_CONFIG_BIAS_PULL_UP:
		return 0x1;
	case STM32F4X_PIN_CONFIG_DRIVE_PUSH_DOWN:
	case STM32F4X_PIN_CONFIG_DRIVE_OPEN_DOWN:
	case STM32F4X_PIN_CONFIG_AF_PUSH_DOWN:
	case STM32F4X_PIN_CONFIG_AF_OPEN_DOWN:
	case STM32F4X_PIN_CONFIG_BIAS_PULL_DOWN:
		return 0x2;
	}

	return 0;
}


int stm32_gpio_flags_to_conf(int flags, int *pincfg)
{
	int direction = flags & GPIO_DIR_MASK;
	int pud = flags & GPIO_PUD_MASK;

	if (!pincfg) {
		return -EINVAL;
	}

	if (direction == GPIO_DIR_OUT) {
		if (pud == GPIO_PUD_PULL_UP) {
			*pincfg = STM32F4X_PIN_CONFIG_DRIVE_PUSH_UP;
		} else if (pud == GPIO_PUD_PULL_DOWN) {
			*pincfg = STM32F4X_PIN_CONFIG_DRIVE_PUSH_DOWN;
		} else {
			*pincfg = STM32F4X_PIN_CONFIG_DRIVE_PUSH_PULL;
		}
	} else if (direction == GPIO_DIR_IN) {
		if (pud == GPIO_PUD_PULL_UP) {
			*pincfg = STM32F4X_PIN_CONFIG_BIAS_PULL_UP;
		} else if (pud == GPIO_PUD_PULL_DOWN) {
			*pincfg = STM32F4X_PIN_CONFIG_BIAS_PULL_DOWN;
		} else {
			*pincfg = STM32F4X_PIN_CONFIG_BIAS_HIGH_IMPEDANCE;
		}
	} else {
		return -ENOTSUP;
	}

	return 0;
}

int stm32_gpio_configure(uint32_t *base_addr, int pin, int conf, int altf)
{
	volatile struct stm32f4x_gpio *gpio =
		(struct stm32f4x_gpio *)(base_addr);
	uint32_t mode = __func_to_mode(conf);
	uint32_t otype = __func_to_otype(conf);
	uint32_t ospeed = __func_to_ospeed(conf);
	uint32_t pupd = __func_to_pupd(conf);
	uint32_t tmpreg = 0;

	/* TODO: validate if indeed alternate */
	if (altf) {
		/* Set the alternate function */
		tmpreg = gpio->afr[pin >> 0x3];
		tmpreg &= ~(0xf << ((pin & 0x07) * 4));
		tmpreg |= (altf << ((pin & 0x07) * 4));
		gpio->afr[pin >> 0x3] = tmpreg;
	}

	/* Set the IO direction mode */
	tmpreg = gpio->mode;
	tmpreg &= ~(0x3 << (pin * 2));
	tmpreg |= (mode << (pin * 2));
	gpio->mode = tmpreg;

	if (otype) {
		tmpreg = gpio->otype;
		tmpreg &= ~(0x1 << pin);
		tmpreg |= (otype << pin);
		gpio->otype = tmpreg;
	}

	if (ospeed) {
		tmpreg = gpio->ospeed;
		tmpreg &= ~(0x3 << (pin * 2));
		tmpreg |= (ospeed << (pin * 2));
		gpio->ospeed = tmpreg;
	}

	tmpreg = gpio->pupdr;
	tmpreg &= ~(0x3 << (pin * 2));
	tmpreg |= (pupd << (pin * 2));
	gpio->pupdr = tmpreg;

	return 0;
}

int stm32_gpio_set(uint32_t *base, int pin, int value)
{
	struct stm32f4x_gpio *gpio = (struct stm32f4x_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 stm32f4x_gpio *gpio = (struct stm32f4x_gpio *)base;

	return (gpio->idr >> pin) & 0x1;
}

int stm32_gpio_enable_int(int port, int pin)
{
	volatile struct stm32f4x_syscfg *syscfg =
		(struct stm32f4x_syscfg *)SYSCFG_BASE;
	volatile union syscfg_exticr *exticr;
	struct device *clk = device_get_binding(STM32_CLOCK_CONTROL_NAME);
	struct stm32f4x_pclken pclken = {
		.bus = STM32F4X_CLOCK_BUS_APB2,
		.enr = STM32F4X_CLOCK_ENABLE_SYSCFG
	};
	int shift = 0;

	/* Enable SYSCFG clock */
	clock_control_on(clk, (clock_control_subsys_t *) &pclken);

	if (pin <= 3) {
		exticr = &syscfg->exticr1;
	} else if (pin <= 7) {
		exticr = &syscfg->exticr2;
	} else if (pin <= 11) {
		exticr = &syscfg->exticr3;
	} else if (pin <= 15) {
		exticr = &syscfg->exticr4;
	} else {
		return -EINVAL;
	}

	shift = 4 * (pin % 4);

	exticr->val &= ~(0xf << shift);
	exticr->val |= port << shift;

	return 0;
}