/* * Copyright (c) 2016 Open-RnD Sp. z o.o. * Copyright (c) 2021 Linaro Limited * Copyright (c) 2021 Nordic Semiconductor ASA * Copyright (c) 2024 Microchip Technology Inc. * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT microchip_mec5_pinctrl #include #include #include static const struct mec_gpio_props cfg1[] = { {MEC_GPIO_OSEL_PROP_ID, MEC_GPIO_PROP_OSEL_CTRL}, {MEC_GPIO_INPAD_DIS_PROP_ID, MEC_GPIO_PROP_INPAD_EN}, }; /* DT enable booleans take precedence over disable booleans. * We initially clear alternate output disable allowing us to set output state * in the control register. Hardware sets output state bit in both control and * parallel output register bits. Alternate output disable only controls which * register bit is writable by the EC. We also clear the input pad disable * bit because we need the input pin state and we don't know if the requested * alternate function is input or bi-directional. * Note 1: hardware allows input and output to be simultaneously enabled. * Note 2: hardware interrupt detection is only on the input path. */ static int mec5_config_pin(uint32_t pinmux, uint32_t altf) { uint32_t conf = pinmux; uint32_t pin = 0, temp = 0; int ret = 0; size_t idx = 0; struct mec_gpio_props cfg2[12]; ret = mec_hal_gpio_pin_num(MCHP_XEC_PINMUX_PORT(pinmux), MCHP_XEC_PINMUX_PIN(pinmux), &pin); if (ret) { return -EINVAL; } ret = mec_hal_gpio_set_props(pin, cfg1, ARRAY_SIZE(cfg1)); if (ret) { return -EIO; } /* slew rate */ temp = (conf >> MCHP_XEC_SLEW_RATE_POS) & MCHP_XEC_SLEW_RATE_MSK0; if (temp != MCHP_XEC_SLEW_RATE_MSK0) { cfg2[idx].prop = MEC_GPIO_SLEW_RATE_ID; cfg2[idx].val = (uint8_t)MEC_GPIO_SLEW_RATE_SLOW; if (temp == MCHP_XEC_SLEW_RATE_FAST0) { cfg2[idx].val = (uint8_t)MEC_GPIO_SLEW_RATE_FAST; } idx++; } /* drive strength */ temp = (conf >> MCHP_XEC_DRV_STR_POS) & MCHP_XEC_DRV_STR_MSK0; if (temp != MCHP_XEC_DRV_STR_MSK0) { cfg2[idx].prop = MEC_GPIO_DRV_STR_ID; cfg2[idx].val = (uint8_t)(temp - 1u); idx++; } /* Touch internal pull-up/pull-down? */ cfg2[idx].prop = MEC_GPIO_PUD_PROP_ID; if (conf & BIT(MCHP_XEC_NO_PUD_POS)) { cfg2[idx++].val = MEC_GPIO_PROP_NO_PUD; } else if (conf & BIT(MCHP_XEC_PU_POS)) { cfg2[idx++].val = MEC_GPIO_PROP_PULL_UP; } else if (conf & BIT(MCHP_XEC_PD_POS)) { cfg2[idx++].val = MEC_GPIO_PROP_PULL_DN; } /* Touch output enable. We always enable input */ if (conf & (BIT(MCHP_XEC_OUT_DIS_POS) | BIT(MCHP_XEC_OUT_EN_POS))) { cfg2[idx].prop = MEC_GPIO_DIR_PROP_ID; cfg2[idx].val = MEC_GPIO_PROP_DIR_IN; if (conf & BIT(MCHP_XEC_OUT_EN_POS)) { cfg2[idx].val = MEC_GPIO_PROP_DIR_OUT; } idx++; } /* Touch output state? Bit can be set even if the direction is input only */ if (conf & (BIT(MCHP_XEC_OUT_LO_POS) | BIT(MCHP_XEC_OUT_HI_POS))) { cfg2[idx].prop = MEC_GPIO_CTRL_OUT_VAL_ID; cfg2[idx].val = 0u; if (conf & BIT(MCHP_XEC_OUT_HI_POS)) { cfg2[idx].val = 1u; } idx++; } /* Touch output buffer type? */ if (conf & (BIT(MCHP_XEC_PUSH_PULL_POS) | BIT(MCHP_XEC_OPEN_DRAIN_POS))) { cfg2[idx].prop = MEC_GPIO_OBUFT_PROP_ID; cfg2[idx].val = MEC_GPIO_PROP_PUSH_PULL; if (conf & BIT(MCHP_XEC_OPEN_DRAIN_POS)) { cfg2[idx].val = MEC_GPIO_PROP_OPEN_DRAIN; } idx++; } /* Always touch power gate */ cfg2[idx].prop = MEC_GPIO_PWRGT_PROP_ID; cfg2[idx].val = MEC_GPIO_PROP_PWRGT_VTR; if (conf & BIT(MCHP_XEC_PIN_LOW_POWER_POS)) { cfg2[idx].val = MEC_GPIO_PROP_PWRGT_OFF; } idx++; /* Always touch MUX (alternate function) */ cfg2[idx].prop = MEC_GPIO_MUX_PROP_ID; cfg2[idx].val = (uint8_t)altf; idx++; /* Always touch invert of alternate function. Need another bit to avoid touching */ cfg2[idx].prop = MEC_GPIO_FUNC_POL_PROP_ID; cfg2[idx].val = MEC_GPIO_PROP_FUNC_OUT_NON_INV; if (conf & BIT(MCHP_XEC_FUNC_INV_POS)) { cfg2[idx].val = MEC_GPIO_PROP_FUNC_OUT_INV; } idx++; /* HW sets output state set in control & parallel regs */ ret = mec_hal_gpio_set_props(pin, cfg2, idx); if (ret) { return -EIO; } /* make output state in control read-only in control and read-write in parallel reg */ ret = mec_hal_gpio_set_property(pin, MEC_GPIO_OSEL_PROP_ID, MEC_GPIO_PROP_OSEL_PAROUT); if (ret) { return -EIO; } return 0; } int pinctrl_configure_pins(const pinctrl_soc_pin_t *pins, uint8_t pin_cnt, uintptr_t reg) { uint32_t pinmux, func; int ret; ARG_UNUSED(reg); for (uint8_t i = 0U; i < pin_cnt; i++) { pinmux = pins[i]; func = MCHP_XEC_PINMUX_FUNC(pinmux); if (func >= MCHP_AFMAX) { return -EINVAL; } ret = mec5_config_pin(pinmux, func); if (ret < 0) { return ret; } } return 0; }