You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
195 lines
5.6 KiB
195 lines
5.6 KiB
/* |
|
* 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 ((BIT(group) & NPCX_DEVALT_LK_GROUP_MASK) != 0) { |
|
return true; |
|
} |
|
|
|
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 defined(CONFIG_PINCTRL_NPCX_EX) |
|
if (pin->flags.psl_in_mode == NPCX_PSL_IN_MODE_EDGE) { |
|
inst_glue->PSL_CTS3 |= BIT(psl_in->port); |
|
} else { |
|
inst_glue->PSL_CTS3 &= ~BIT(psl_in->port); |
|
} |
|
|
|
/* Clear event bits */ |
|
inst_glue->PSL_CTS |= BIT(psl_in->port); |
|
inst_glue->PSL_IN_POS |= BIT(psl_in->port); |
|
inst_glue->PSL_IN_NEG |= BIT(psl_in->port); |
|
#else |
|
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); |
|
} |
|
#endif /* CONFIG_PINCTRL_NPCX_EX */ |
|
} |
|
|
|
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; |
|
}
|
|
|