blob: 4fafbe146dff2575246435fe8ad5e518d6c78442 [file] [log] [blame]
/*
* Copyright (c) 2020 Nuvoton Technology Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/device.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/dt-bindings/pinctrl/npcx-pinctrl.h>
#include <zephyr/kernel.h>
#include <soc.h>
#include "soc_gpio.h"
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(pimux_npcx, LOG_LEVEL_ERR);
/* Driver config */
struct npcx_scfg_config {
/* scfg device base address */
uintptr_t base_scfg;
uintptr_t base_glue;
};
/*
* Get io list which default functionality are not IOs. Then switch them to
* GPIO in pin-mux init function.
*
* def_io_conf: def-io-conf-list {
* compatible = "nuvoton,npcx-pinctrl-def";
* pinctrl-0 = <&alt0_gpio_no_spip
* &alt0_gpio_no_fpip
* ...>;
* };
*/
static const struct npcx_alt def_alts[] =
NPCX_DT_IO_ALT_ITEMS_LIST(nuvoton_npcx_pinctrl_def, 0);
static const struct npcx_lvol def_lvols[] = NPCX_DT_IO_LVOL_ITEMS_DEF_LIST;
#if DT_HAS_COMPAT_STATUS_OKAY(nuvoton_npcx_pslctrl_def)
static const struct npcx_psl_in psl_in_confs[] = NPCX_DT_PSL_IN_ITEMS_LIST;
#endif
static const struct npcx_scfg_config npcx_scfg_cfg = {
.base_scfg = DT_REG_ADDR_BY_NAME(DT_NODELABEL(scfg), scfg),
.base_glue = DT_REG_ADDR_BY_NAME(DT_NODELABEL(scfg), glue),
};
/* Driver convenience defines */
#define HAL_SFCG_INST() (struct scfg_reg *)(npcx_scfg_cfg.base_scfg)
#define HAL_GLUE_INST() (struct glue_reg *)(npcx_scfg_cfg.base_glue)
/* PSL input detection mode is configured by bits 7:4 of PSL_CTS */
#define NPCX_PSL_CTS_MODE_BIT(bit) BIT(bit + 4)
/* PSL input assertion events are reported by bits 3:0 of PSL_CTS */
#define NPCX_PSL_CTS_EVENT_BIT(bit) BIT(bit)
/* Pin-control local functions */
static void npcx_pinctrl_alt_sel(const struct npcx_alt *alt, int alt_func)
{
const uint32_t scfg_base = npcx_scfg_cfg.base_scfg;
uint8_t alt_mask = BIT(alt->bit);
/*
* alt_fun == 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 (!!alt_func != !!alt->inverted) {
NPCX_DEVALT(scfg_base, alt->group) |= alt_mask;
} else {
NPCX_DEVALT(scfg_base, alt->group) &= ~alt_mask;
}
}
/* Platform specific pin-control functions */
void npcx_pinctrl_mux_configure(const struct npcx_alt *alts_list,
uint8_t alts_size, int altfunc)
{
int i;
for (i = 0; i < alts_size; i++, alts_list++) {
npcx_pinctrl_alt_sel(alts_list, altfunc);
}
}
void npcx_lvol_pads_configure(void)
{
const uint32_t scfg_base = npcx_scfg_cfg.base_scfg;
for (int i = 0; i < ARRAY_SIZE(def_lvols); i++) {
NPCX_LV_GPIO_CTL(scfg_base, def_lvols[i].ctrl)
|= BIT(def_lvols[i].bit);
LOG_DBG("IO%x%x turn on low-voltage", def_lvols[i].io_port,
def_lvols[i].io_bit);
}
}
void npcx_lvol_restore_io_pads(void)
{
for (int i = 0; i < ARRAY_SIZE(def_lvols); i++) {
npcx_gpio_enable_io_pads(
npcx_get_gpio_dev(def_lvols[i].io_port),
def_lvols[i].io_bit);
}
}
void npcx_lvol_suspend_io_pads(void)
{
for (int i = 0; i < ARRAY_SIZE(def_lvols); i++) {
npcx_gpio_disable_io_pads(
npcx_get_gpio_dev(def_lvols[i].io_port),
def_lvols[i].io_bit);
}
}
bool npcx_lvol_is_enabled(int port, int pin)
{
for (int i = 0; i < ARRAY_SIZE(def_lvols); i++) {
if (def_lvols[i].io_port == port &&
def_lvols[i].io_bit == pin) {
return true;
}
}
return false;
}
void npcx_pinctrl_i2c_port_sel(int controller, int port)
{
struct glue_reg *const inst_glue = HAL_GLUE_INST();
if (port != 0) {
inst_glue->SMB_SEL |= BIT(controller);
} else {
inst_glue->SMB_SEL &= ~BIT(controller);
}
}
int npcx_pinctrl_flash_write_protect_set(void)
{
struct scfg_reg *inst_scfg = HAL_SFCG_INST();
inst_scfg->DEV_CTL4 |= BIT(NPCX_DEV_CTL4_WP_IF);
if (!IS_BIT_SET(inst_scfg->DEV_CTL4, NPCX_DEV_CTL4_WP_IF)) {
return -EIO;
}
return 0;
}
bool npcx_pinctrl_flash_write_protect_is_set(void)
{
struct scfg_reg *inst_scfg = HAL_SFCG_INST();
return IS_BIT_SET(inst_scfg->DEV_CTL4, NPCX_DEV_CTL4_WP_IF);
}
void npcx_pinctrl_psl_output_set_inactive(void)
{
struct gpio_reg *const inst = (struct gpio_reg *)
NPCX_DT_PSL_OUT_CONTROLLER(0);
int pin = NPCX_DT_PSL_OUT_PIN(0);
/* Set PSL_OUT to inactive level by setting related bit of PDOUT */
inst->PDOUT |= BIT(pin);
}
#if DT_HAS_COMPAT_STATUS_OKAY(nuvoton_npcx_pslctrl_def)
static void npcx_pinctrl_psl_detect_mode_sel(uint32_t offset, bool edge_mode)
{
struct glue_reg *const inst_glue = HAL_GLUE_INST();
if (edge_mode) {
inst_glue->PSL_CTS |= NPCX_PSL_CTS_MODE_BIT(offset);
} else {
inst_glue->PSL_CTS &= ~NPCX_PSL_CTS_MODE_BIT(offset);
}
}
bool npcx_pinctrl_psl_input_asserted(uint32_t i)
{
struct glue_reg *const inst_glue = HAL_GLUE_INST();
if (i >= ARRAY_SIZE(psl_in_confs)) {
return false;
}
return IS_BIT_SET(inst_glue->PSL_CTS,
NPCX_PSL_CTS_EVENT_BIT(psl_in_confs[i].offset));
}
void npcx_pinctrl_psl_input_configure(void)
{
/* Configure detection type of PSL input pads */
for (int i = 0; i < ARRAY_SIZE(psl_in_confs); i++) {
/* Detection polarity select */
npcx_pinctrl_alt_sel(&psl_in_confs[i].polarity,
(psl_in_confs[i].flag & NPCX_PSL_ACTIVE_HIGH) != 0);
/* Detection mode select */
npcx_pinctrl_psl_detect_mode_sel(psl_in_confs[i].offset,
(psl_in_confs[i].flag & NPCX_PSL_MODE_EDGE) != 0);
}
/* Configure pin-mux for all PSL input pads from GPIO to PSL */
for (int i = 0; i < ARRAY_SIZE(psl_in_confs); i++) {
npcx_pinctrl_alt_sel(&psl_in_confs[i].pinctrl, 1);
}
}
#endif
void npcx_host_interface_sel(enum npcx_hif_type hif_type)
{
struct scfg_reg *inst_scfg = HAL_SFCG_INST();
SET_FIELD(inst_scfg->DEVCNT, NPCX_DEVCNT_HIF_TYP_SEL_FIELD, hif_type);
}
/* Pin-control driver registration */
static int npcx_scfg_init(const struct device *dev)
{
struct scfg_reg *inst_scfg = HAL_SFCG_INST();
/*
* Set bit 7 of DEVCNT again for npcx7 series. Please see Errata
* for more information. It will be fixed in next chip.
*/
if (IS_ENABLED(CONFIG_SOC_SERIES_NPCX7))
inst_scfg->DEVCNT |= BIT(7);
/* Change all pads whose default functionality isn't IO to GPIO */
npcx_pinctrl_mux_configure(def_alts, ARRAY_SIZE(def_alts), 0);
/* Configure default low-voltage pads */
npcx_lvol_pads_configure();
return 0;
}
SYS_INIT(npcx_scfg_init, PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);