| /* |
| * Copyright (c) 2022 Nuvoton Technology Corporation. |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| #include <assert.h> |
| #include <zephyr/drivers/pinctrl.h> |
| #include <soc.h> |
| |
| /* Driver config */ |
| struct npcx_pinctrl_config { |
| /* Device base address used for pinctrl driver */ |
| uintptr_t base_scfg; |
| uintptr_t base_glue; |
| }; |
| |
| static const struct npcx_pinctrl_config npcx_pinctrl_cfg = { |
| .base_scfg = NPCX_SCFG_REG_ADDR, |
| .base_glue = NPCX_GLUE_REG_ADDR, |
| }; |
| |
| /* PWM pinctrl config */ |
| struct npcx_pwm_pinctrl_config { |
| uintptr_t base; |
| int channel; |
| }; |
| |
| #define NPCX_PWM_PINCTRL_CFG_INIT(node_id) \ |
| { \ |
| .base = DT_REG_ADDR(node_id), \ |
| .channel = DT_PROP(node_id, pwm_channel), \ |
| }, |
| |
| static const struct npcx_pwm_pinctrl_config pwm_pinctrl_cfg[] = { |
| DT_FOREACH_STATUS_OKAY(nuvoton_npcx_pwm, NPCX_PWM_PINCTRL_CFG_INIT) |
| }; |
| |
| /* Pin-control local functions for peripheral devices */ |
| static bool npcx_periph_pinmux_has_lock(int group) |
| { |
| #if defined(CONFIG_SOC_SERIES_NPCX7) |
| if (group == 0x00 || (group >= 0x02 && group <= 0x04) || group == 0x06 || |
| group == 0x0b || group == 0x0f) { |
| return true; |
| } |
| #elif defined(CONFIG_SOC_SERIES_NPCX9) |
| if (group == 0x00 || (group >= 0x02 && group <= 0x06) || group == 0x0b || |
| group == 0x0d || (group >= 0x0f && group <= 0x12)) { |
| return true; |
| } |
| #endif |
| return false; |
| } |
| |
| static void npcx_periph_pinmux_configure(const struct npcx_periph *alt, bool is_alternate, |
| bool is_locked) |
| { |
| const uintptr_t scfg_base = npcx_pinctrl_cfg.base_scfg; |
| uint8_t alt_mask = BIT(alt->bit); |
| |
| /* |
| * is_alternate == 0 means select GPIO, otherwise Alternate Func. |
| * inverted == 0: |
| * Set devalt bit to select Alternate Func. |
| * inverted == 1: |
| * Clear devalt bit to select Alternate Func. |
| */ |
| if (is_alternate != alt->inverted) { |
| NPCX_DEVALT(scfg_base, alt->group) |= alt_mask; |
| } else { |
| NPCX_DEVALT(scfg_base, alt->group) &= ~alt_mask; |
| } |
| |
| if (is_locked && npcx_periph_pinmux_has_lock(alt->group)) { |
| NPCX_DEVALT_LK(scfg_base, alt->group) |= alt_mask; |
| } |
| } |
| |
| static void npcx_periph_pupd_configure(const struct npcx_periph *pupd, |
| enum npcx_io_bias_type type) |
| { |
| const uintptr_t scfg_base = npcx_pinctrl_cfg.base_scfg; |
| |
| if (type == NPCX_BIAS_TYPE_NONE) { |
| NPCX_PUPD_EN(scfg_base, pupd->group) &= ~BIT(pupd->bit); |
| } else { |
| NPCX_PUPD_EN(scfg_base, pupd->group) |= BIT(pupd->bit); |
| } |
| } |
| |
| static void npcx_periph_pwm_drive_mode_configure(const struct npcx_periph *periph, |
| bool is_od) |
| { |
| uintptr_t reg = 0; |
| |
| /* Find selected pwm module which enables open-drain prop. */ |
| for (int i = 0; i < ARRAY_SIZE(pwm_pinctrl_cfg); i++) { |
| if (periph->group == pwm_pinctrl_cfg[i].channel) { |
| reg = pwm_pinctrl_cfg[i].base; |
| break; |
| } |
| } |
| |
| if (reg == 0) { |
| return; |
| } |
| |
| struct pwm_reg *const inst = (struct pwm_reg *)(reg); |
| |
| if (is_od) { |
| inst->PWMCTLEX |= BIT(NPCX_PWMCTLEX_OD_OUT); |
| } else { |
| inst->PWMCTLEX &= ~BIT(NPCX_PWMCTLEX_OD_OUT); |
| } |
| } |
| |
| static void npcx_periph_configure(const pinctrl_soc_pin_t *pin, uintptr_t reg) |
| { |
| if (pin->cfg.periph.type == NPCX_PINCTRL_TYPE_PERIPH_PINMUX) { |
| /* Configure peripheral device's pinmux functionality */ |
| npcx_periph_pinmux_configure(&pin->cfg.periph, |
| !pin->flags.pinmux_gpio, |
| pin->flags.pinmux_lock); |
| } else if (pin->cfg.periph.type == NPCX_PINCTRL_TYPE_PERIPH_PUPD) { |
| /* Configure peripheral device's internal PU/PD */ |
| npcx_periph_pupd_configure(&pin->cfg.periph, |
| pin->flags.io_bias_type); |
| } else if (pin->cfg.periph.type == NPCX_PINCTRL_TYPE_PERIPH_DRIVE) { |
| /* Configure peripheral device's drive mode. (Only PWM pads support it) */ |
| npcx_periph_pwm_drive_mode_configure(&pin->cfg.periph, |
| pin->flags.io_drive_type == NPCX_DRIVE_TYPE_OPEN_DRAIN); |
| } |
| } |
| |
| static void npcx_psl_input_detection_configure(const pinctrl_soc_pin_t *pin) |
| { |
| struct glue_reg *inst_glue = (struct glue_reg *)(npcx_pinctrl_cfg.base_glue); |
| const uintptr_t scfg_base = npcx_pinctrl_cfg.base_scfg; |
| const struct npcx_psl_input *psl_in = (const struct npcx_psl_input *)&pin->cfg.psl_in; |
| |
| /* Configure detection polarity of PSL input pads */ |
| if (pin->flags.psl_in_polarity == NPCX_PSL_IN_POL_HIGH) { |
| NPCX_DEVALT(scfg_base, psl_in->pol_group) |= BIT(psl_in->pol_bit); |
| } else { |
| NPCX_DEVALT(scfg_base, psl_in->pol_group) &= ~BIT(psl_in->pol_bit); |
| } |
| |
| /* Configure detection mode of PSL input pads */ |
| if (pin->flags.psl_in_mode == NPCX_PSL_IN_MODE_EDGE) { |
| inst_glue->PSL_CTS |= NPCX_PSL_CTS_MODE_BIT(psl_in->port); |
| } else { |
| inst_glue->PSL_CTS &= ~NPCX_PSL_CTS_MODE_BIT(psl_in->port); |
| } |
| } |
| |
| /* Pinctrl API implementation */ |
| int pinctrl_configure_pins(const pinctrl_soc_pin_t *pins, uint8_t pin_cnt, |
| uintptr_t reg) |
| { |
| ARG_UNUSED(reg); |
| |
| /* Configure all peripheral devices' properties here. */ |
| for (uint8_t i = 0; i < pin_cnt; i++) { |
| if (pins[i].flags.type == NPCX_PINCTRL_TYPE_PERIPH) { |
| /* Configure peripheral device's pinmux functionality */ |
| npcx_periph_configure(&pins[i], reg); |
| } else if (pins[i].flags.type == NPCX_PINCTRL_TYPE_PSL_IN) { |
| /* Configure SPL input's detection mode */ |
| npcx_psl_input_detection_configure(&pins[i]); |
| } else { |
| return -ENOTSUP; |
| } |
| } |
| |
| return 0; |
| } |