Linux preempt-rt

Check our new training course

Real-Time Linux with PREEMPT_RT

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

Bootlin logo

Elixir Cross Referencer

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

/**
 * @brief
 *
 * A common driver for STM32 pinmux. Each SoC must implement a SoC
 * specific part of the driver.
 */

#include <errno.h>

#include <kernel.h>
#include <device.h>
#include <soc.h>
#include <pinmux.h>
#include <gpio/gpio_stm32.h>
#include <clock_control/stm32_clock_control.h>
#include <pinmux/stm32/pinmux_stm32.h>

#include "pinmux.h"

#if defined(CONFIG_CLOCK_CONTROL_STM32_CUBE)
static const uint32_t ports_enable[STM32_PORTS_MAX] = {
	STM32_PERIPH_GPIOA,
	STM32_PERIPH_GPIOB,
	STM32_PERIPH_GPIOC,
	STM32_PERIPH_GPIOD,
#ifdef GPIOE_BASE
	STM32_PERIPH_GPIOE,
#endif
#ifdef GPIOF_BASE
	STM32_PERIPH_GPIOF,
#endif
#ifdef GPIOG_BASE
	STM32_PERIPH_GPIOG,
#endif
#ifdef GPIOH_BASE
	STM32_PERIPH_GPIOH,
#endif
};
#elif defined(CONFIG_SOC_SERIES_STM32F4X)
static const uint32_t ports_enable[STM32_PORTS_MAX] = {
	STM32F4X_CLOCK_ENABLE_GPIOA,
	STM32F4X_CLOCK_ENABLE_GPIOB,
	STM32F4X_CLOCK_ENABLE_GPIOC,
	STM32F4X_CLOCK_ENABLE_GPIOD,
	STM32F4X_CLOCK_ENABLE_GPIOE,
	STM32F4X_CLOCK_ENABLE_GPIOF,
	STM32F4X_CLOCK_ENABLE_GPIOG,
	STM32F4X_CLOCK_ENABLE_GPIOH,
};
#endif

/**
 * @brief enable IO port clock
 *
 * @param port I/O port ID
 * @param clk  optional clock device
 *
 * @return 0 on success, error otherwise
 */
static int enable_port(uint32_t port, struct device *clk)
{
	/* enable port clock */
	if (!clk) {
		clk = device_get_binding(STM32_CLOCK_CONTROL_NAME);
	}

	/* TODO: Merge this and move the port clock to the soc file */
#if defined(CONFIG_CLOCK_CONTROL_STM32_CUBE)
	struct stm32_pclken pclken;

	pclken.bus = STM32_CLOCK_BUS_GPIO;
	pclken.enr = ports_enable[port];

	return clock_control_on(clk, (clock_control_subsys_t *) &pclken);
#else
#if defined(CONFIG_SOC_SERIES_STM32F1X)
	clock_control_subsys_t subsys = stm32_get_port_clock(port);

	return clock_control_on(clk, subsys);
#elif CONFIG_SOC_SERIES_STM32F4X
	struct stm32f4x_pclken pclken;
	/* AHB1 bus for all the GPIO ports */
	pclken.bus = STM32F4X_CLOCK_BUS_AHB1;
	pclken.enr = ports_enable[port];

	return clock_control_on(clk, (clock_control_subsys_t *) &pclken);
#endif
#endif /* CONFIG_CLOCK_CONTROL_STM32_CUBE */
}

static int stm32_pin_configure(int pin, int func, int altf)
{
	/* determine IO port registers location */
	uint32_t offset = STM32_PORT(pin) * GPIO_REG_SIZE;
	uint8_t *port_base = (uint8_t *)(GPIO_PORTS_BASE + offset);

	/* not much here, on STM32F10x the alternate function is
	 * controller by setting up GPIO pins in specific mode.
	 */
	return stm32_gpio_configure((uint32_t *)port_base,
				    STM32_PIN(pin), func, altf);
}

/**
 * @brief pin setup
 *
 * @param pin  STM32PIN() encoded pin ID
 * @param func SoC specific function assignment
 * @param clk  optional clock device
 *
 * @return 0 on success, error otherwise
 */
int _pinmux_stm32_set(uint32_t pin, uint32_t func,
				struct device *clk)
{
	int config;

	/* make sure to enable port clock first */
	if (enable_port(STM32_PORT(pin), clk)) {
		return -EIO;
	}

	/* determine config for alternate function */
	config = stm32_get_pin_config(pin, func);

	return stm32_pin_configure(pin, config, func);
}

/**
 * @brief setup pins according to their assignments
 *
 * @param pinconf  board pin configuration array
 * @param pins     array size
 */
void stm32_setup_pins(const struct pin_config *pinconf,
		      size_t pins)
{
	struct device *clk;
	int i;

	clk = device_get_binding(STM32_CLOCK_CONTROL_NAME);

	for (i = 0; i < pins; i++) {
		_pinmux_stm32_set(pinconf[i].pin_num,
				  pinconf[i].mode, clk);
	}
}