/*
 * Copyright (c) 2022-2023, Gerson Fernando Budke <nandojve@gmail.com>
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <zephyr/drivers/pinctrl.h>
#include <zephyr/drivers/clock_control/atmel_sam_pmc.h>
#include <soc_gpio.h>

/** Utility macro that expands to the GPIO port address if it exists */
#define SAM_PORT_ADDR_OR_NONE(nodelabel)					\
	IF_ENABLED(DT_NODE_EXISTS(DT_NODELABEL(nodelabel)),			\
		   (DT_REG_ADDR(DT_NODELABEL(nodelabel)),))

/** Utility macro that expands to the GPIO Peripheral ID if it exists */
#define SAM_PORT_CLOCKS_OR_NONE(nodelabel)					\
	IF_ENABLED(DT_NODE_EXISTS(DT_NODELABEL(nodelabel)),			\
		   (SAM_DT_CLOCK_PMC_CFG(0, DT_NODELABEL(nodelabel)),))

/** SAM port addresses */
static const uint32_t sam_port_addrs[] = {
#ifdef ID_GPIO
	SAM_PORT_ADDR_OR_NONE(gpioa)
	SAM_PORT_ADDR_OR_NONE(gpiob)
	SAM_PORT_ADDR_OR_NONE(gpioc)
#else
	SAM_PORT_ADDR_OR_NONE(pioa)
	SAM_PORT_ADDR_OR_NONE(piob)
	SAM_PORT_ADDR_OR_NONE(pioc)
	SAM_PORT_ADDR_OR_NONE(piod)
	SAM_PORT_ADDR_OR_NONE(pioe)
	SAM_PORT_ADDR_OR_NONE(piof)
#endif
};

/** SAM port clocks */
static const struct atmel_sam_pmc_config sam_port_clocks[] = {
#ifdef ID_GPIO
	SAM_PORT_CLOCKS_OR_NONE(gpioa)
	SAM_PORT_CLOCKS_OR_NONE(gpiob)
	SAM_PORT_CLOCKS_OR_NONE(gpioc)
#else
	SAM_PORT_CLOCKS_OR_NONE(pioa)
	SAM_PORT_CLOCKS_OR_NONE(piob)
	SAM_PORT_CLOCKS_OR_NONE(pioc)
	SAM_PORT_CLOCKS_OR_NONE(piod)
	SAM_PORT_CLOCKS_OR_NONE(pioe)
	SAM_PORT_CLOCKS_OR_NONE(piof)
#endif
};

static void pinctrl_configure_pin(pinctrl_soc_pin_t pin)
{
	struct soc_gpio_pin soc_pin;
	uint8_t  port_idx, port_func;

	port_idx = SAM_PINMUX_PORT_GET(pin);
	__ASSERT_NO_MSG(port_idx < ARRAY_SIZE(sam_port_addrs));
	port_func = SAM_PINMUX_FUNC_GET(pin);

#ifdef ID_GPIO
	soc_pin.regs = (Gpio *) sam_port_addrs[port_idx];
#else
	soc_pin.regs = (Pio *) sam_port_addrs[port_idx];
#endif
	soc_pin.periph_id = sam_port_clocks[port_idx].peripheral_id;
	soc_pin.mask = 1 << SAM_PINMUX_PIN_GET(pin);
	soc_pin.flags = SAM_PINCTRL_FLAGS_GET(pin) << SOC_GPIO_FLAGS_POS;

	if (port_func == SAM_PINMUX_FUNC_periph) {
		soc_pin.flags |= (SAM_PINMUX_PERIPH_GET(pin)
				  << SOC_GPIO_FUNC_POS);
	}

	soc_gpio_configure(&soc_pin);
}

int pinctrl_configure_pins(const pinctrl_soc_pin_t *pins, uint8_t pin_cnt,
			   uintptr_t reg)
{
	ARG_UNUSED(reg);

	for (uint8_t i = 0U; i < pin_cnt; i++) {
		pinctrl_configure_pin(*pins++);
	}

	return 0;
}
