zephyr/drivers/pinctrl/pinctrl_mci_io_mux.c

175 lines
5.3 KiB
C

/*
* Copyright 2022 NXP
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/drivers/pinctrl.h>
#include <soc.h>
static MCI_IO_MUX_Type *mci_iomux =
(MCI_IO_MUX_Type *)DT_REG_ADDR(DT_NODELABEL(pinctrl));
static SOCCIU_Type *soc_ctrl =
(SOCCIU_Type *)DT_REG_ADDR(DT_NODELABEL(soc_ctrl));
static AON_SOC_CIU_Type *aon_soc_ciu =
(AON_SOC_CIU_Type *)DT_REG_ADDR(DT_NODELABEL(aon_soc_ctrl));
/*
* GPIO mux option definitions. Stored as a static array, because
* these mux options are needed to clear pin mux settings to
* a known good state before selecting a new alternate function.
*/
static uint64_t gpio_muxes[] = {IOMUX_GPIO_OPS};
/*
* Helper function to handle setting pin properties,
* such as pin bias and slew rate
*/
static void configure_pin_props(uint32_t pin_mux, uint8_t gpio_idx)
{
uint32_t mask, set;
volatile uint32_t *pull_reg = &soc_ctrl->PAD_PU_PD_EN0;
volatile uint32_t *slew_reg = &soc_ctrl->SR_CONFIG0;
volatile uint32_t *sleep_force_en = &soc_ctrl->PAD_SLP_EN0;
volatile uint32_t *sleep_force_val = &soc_ctrl->PAD_SLP_VAL0;
/* GPIO 22-27 use always on configuration registers */
if (gpio_idx > 21 && gpio_idx < 28) {
pull_reg = (&aon_soc_ciu->PAD_PU_PD_EN1 - 1);
slew_reg = (&aon_soc_ciu->SR_CONFIG1 - 1);
sleep_force_en = &aon_soc_ciu->PAD_SLP_EN0;
sleep_force_val = &aon_soc_ciu->PAD_SLP_VAL0;
}
/* Calculate register offset for pull and slew regs.
* Use bit shifting as opposed to division
*/
pull_reg += (gpio_idx >> 4);
slew_reg += (gpio_idx >> 4);
sleep_force_en += (gpio_idx >> 5);
sleep_force_val += (gpio_idx >> 5);
/* Set pull-up/pull-down */
/* Use mask and bitshift here as opposed to modulo and multiplication.
* equivalent to ((gpio_idx % 16) * 2)
*/
mask = 0x3 << ((gpio_idx & 0xF) << 1);
set = IOMUX_PAD_GET_PULL(pin_mux) << ((gpio_idx & 0xF) << 1);
*pull_reg = (*pull_reg & ~mask) | set;
/* Set slew rate */
set = IOMUX_PAD_GET_SLEW(pin_mux) << ((gpio_idx & 0xF) << 1);
*slew_reg = (*slew_reg & ~mask) | set;
/* Set sleep force enable bit */
mask = (0x1 << (gpio_idx & 0x1F));
set = (IOMUX_PAD_GET_SLEEP_FORCE_EN(pin_mux) << (gpio_idx & 0x1F));
*sleep_force_en = (*sleep_force_en & ~mask) | set;
set = (IOMUX_PAD_GET_SLEEP_FORCE_VAL(pin_mux) << (gpio_idx & 0x1F));
*sleep_force_val = (*sleep_force_val & ~mask) | set;
}
static void select_gpio_mode(uint8_t gpio_idx)
{
uint64_t gpio_setting = gpio_muxes[gpio_idx];
volatile uint32_t *flexcomm_reg = &mci_iomux->FC0;
/* Clear flexcomm settings */
flexcomm_reg += IOMUX_GET_FLEXCOMM_CLR_IDX(gpio_setting);
*flexcomm_reg &= ~IOMUX_GET_FLEXCOMM_CLR_MASK(gpio_setting);
/* Clear fsel settings */
mci_iomux->FSEL &= ~IOMUX_GET_FSEL_CLR_MASK(gpio_setting);
/* Clear CTimer in/out, if required */
if (IOMUX_GET_SCTIMER_IN_CLR_ENABLE(gpio_setting)) {
mci_iomux->C_TIMER_IN &=
~(0x1 << IOMUX_GET_CTIMER_CLR_OFFSET(gpio_setting));
mci_iomux->C_TIMER_OUT &=
~(0x1 << IOMUX_GET_CTIMER_CLR_OFFSET(gpio_setting));
}
/* Clear SCTimer in/out, if required */
if (IOMUX_GET_SCTIMER_IN_CLR_ENABLE(gpio_setting)) {
mci_iomux->SC_TIMER &=
~(0x1 << IOMUX_GET_SCTIMER_IN_CLR_OFFSET(gpio_setting));
}
if (IOMUX_GET_SCTIMER_OUT_CLR_ENABLE(gpio_setting)) {
mci_iomux->SC_TIMER &=
~(0x1 << (IOMUX_GET_SCTIMER_OUT_CLR_OFFSET(gpio_setting) + 16));
}
/* Clear security gpio enable */
mci_iomux->S_GPIO &= ~(0x1 << (gpio_idx - 32));
}
int pinctrl_configure_pins(const pinctrl_soc_pin_t *pins, uint8_t pin_cnt,
uintptr_t reg)
{
volatile uint32_t *flexcomm_reg;
volatile uint32_t *iomux_en_reg;
for (uint8_t i = 0; i < pin_cnt; i++) {
flexcomm_reg = &mci_iomux->FC0;
iomux_en_reg = &soc_ctrl->MCI_IOMUX_EN0;
uint32_t pin_mux = pins[i];
uint8_t gpio_idx = IOMUX_GET_GPIO_IDX(pin_mux);
uint8_t type = IOMUX_GET_TYPE(pin_mux);
/*
* Before selecting an alternate function, we must clear any
* conflicting pin configuration. We do this by resetting the
* pin to a gpio configuration, then selecting the alternate
* function.
*/
select_gpio_mode(gpio_idx);
switch (type) {
case IOMUX_FLEXCOMM:
flexcomm_reg += IOMUX_GET_FLEXCOMM_IDX(pin_mux);
*flexcomm_reg |=
(0x1 << IOMUX_GET_FLEXCOMM_BIT(pin_mux));
break;
case IOMUX_FSEL:
mci_iomux->FSEL |=
(0x1 << IOMUX_GET_FSEL_BIT(pin_mux));
break;
case IOMUX_CTIMER_IN:
mci_iomux->C_TIMER_IN |=
(0x1 << IOMUX_GET_CTIMER_BIT(pin_mux));
break;
case IOMUX_CTIMER_OUT:
mci_iomux->C_TIMER_OUT |=
(0x1 << IOMUX_GET_CTIMER_BIT(pin_mux));
break;
case IOMUX_SCTIMER_IN:
mci_iomux->SC_TIMER |=
(0x1 << IOMUX_GET_SCTIMER_BIT(pin_mux));
break;
case IOMUX_SCTIMER_OUT:
mci_iomux->SC_TIMER |=
(0x1 << (IOMUX_GET_SCTIMER_BIT(pin_mux) + 16));
break;
case IOMUX_SGPIO:
mci_iomux->S_GPIO |= (0x1 << (gpio_idx - 32));
break;
case IOMUX_GPIO:
if (gpio_idx > 32) {
mci_iomux->GPIO_GRP1 |= (0x1 << (gpio_idx - 32));
} else {
mci_iomux->GPIO_GRP0 |= (0x1 << gpio_idx);
}
break;
case IOMUX_AON:
/* No selection bits should be set */
break;
default:
/* Unsupported type passed */
return -ENOTSUP;
}
configure_pin_props(pin_mux, gpio_idx);
/* Now, enable pin controller access to this pin */
if (gpio_idx > 21 && gpio_idx < 28) {
/* GPIO 22-27 use always on soc controller */
iomux_en_reg = &aon_soc_ciu->MCI_IOMUX_EN0;
}
iomux_en_reg += (gpio_idx >> 5);
*iomux_en_reg |= (0x1 << (gpio_idx & 0x1F));
}
return 0;
}