zephyr/drivers/pinctrl/pinctrl_mchp_mec5.c
Scott Worley 4fa5fc3b4c drivers: pinctrl: mec5: Microchip MEC5 HAL based pinctrl driver
Add a pinctrl driver for Microchip MEC5 HAL based chips.
The driver removes the YAML enum "no change" property
value from the driver strength and slew rate properties.
Update the shared header file in mec soc common folder to
use a different Z_PINCTRL_STATE_PINCFG_INIT for MEC5.
Modifications to legacy MEC172x XEC PINCTRL will be in
a future PR.

Signed-off-by: Scott Worley <scott.worley@microchip.com>
2024-10-24 14:07:31 +02:00

166 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;
}