167 lines
4.6 KiB
C
167 lines
4.6 KiB
C
/*
|
|
* 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 <zephyr/drivers/pinctrl.h>
|
|
#include <zephyr/dt-bindings/pinctrl/mchp-xec-pinctrl.h>
|
|
#include <mec_gpio_api.h>
|
|
|
|
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;
|
|
}
|