|  | /* | 
|  | * 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); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void npcx_device_control_configure(const pinctrl_soc_pin_t *pin) | 
|  | { | 
|  | const struct npcx_dev_ctl *ctrl = (const struct npcx_dev_ctl *)&pin->cfg.dev_ctl; | 
|  | const uintptr_t scfg_base = npcx_pinctrl_cfg.base_scfg; | 
|  |  | 
|  | SET_FIELD(NPCX_DEV_CTL(scfg_base, ctrl->offest), | 
|  | FIELD(ctrl->field_offset, ctrl->field_size), | 
|  | ctrl->field_value); | 
|  | } | 
|  |  | 
|  | /* 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_DEVICE_CTRL) { | 
|  | /* Configure device's io characteristics */ | 
|  | npcx_device_control_configure(&pins[i]); | 
|  | } 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; | 
|  | } |