blob: 3c8af74baf30c829c400b43b2a857ea5a822e2c9 [file] [log] [blame] [edit]
/*
* Copyright (c) 2022 ITE Corporation. All Rights Reserved.
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT ite_it8xxx2_pinctrl_func
#include <zephyr/drivers/pinctrl.h>
#include <zephyr/logging/log.h>
#include <chip_chipregs.h>
LOG_MODULE_REGISTER(pinctrl_ite_it8xxx2, LOG_LEVEL_ERR);
#define GPIO_IT8XXX2_REG_BASE \
((struct gpio_it8xxx2_regs *)DT_REG_ADDR(DT_NODELABEL(gpiogcr)))
#define GPIO_GROUP_MEMBERS 8
struct pinctrl_it8xxx2_gpio {
/* gpio port control register (byte mapping to pin) */
uint8_t *reg_gpcr;
/* function 3 general control register */
uintptr_t func3_gcr[GPIO_GROUP_MEMBERS];
/* function 3 enable mask */
uint8_t func3_en_mask[GPIO_GROUP_MEMBERS];
/* function 4 general control register */
uintptr_t func4_gcr[GPIO_GROUP_MEMBERS];
/* function 4 enable mask */
uint8_t func4_en_mask[GPIO_GROUP_MEMBERS];
/* Input voltage selection */
uintptr_t volt_sel[GPIO_GROUP_MEMBERS];
/* Input voltage selection mask */
uint8_t volt_sel_mask[GPIO_GROUP_MEMBERS];
};
struct pinctrl_it8xxx2_ksi_kso {
/*
* KSI[7:0]/KSO[15:8]/KSO[7:0] port gpio control register
* (bit mapping to pin)
*/
uint8_t *reg_gctrl;
/* KSI[7:0]/KSO[15:8]/KSO[7:0] port control register */
uint8_t *reg_ctrl;
/*
* KSO push-pull/open-drain bit of KSO[15:0] control register
* (this bit apply to all pins)
*/
int pp_od_mask;
/*
* KSI/KSO pullup bit of KSI[7:0]/KSO[15:0] control register
* (this bit apply to all pins)
*/
int pullup_mask;
};
struct pinctrl_it8xxx2_config {
bool gpio_group;
union {
struct pinctrl_it8xxx2_gpio gpio;
struct pinctrl_it8xxx2_ksi_kso ksi_kso;
};
};
static int pinctrl_it8xxx2_set(const pinctrl_soc_pin_t *pins)
{
const struct pinctrl_it8xxx2_config *pinctrl_config = pins->pinctrls->config;
const struct pinctrl_it8xxx2_gpio *gpio = &(pinctrl_config->gpio);
uint32_t pincfg = pins->pincfg;
uint8_t pin = pins->pin;
volatile uint8_t *reg_gpcr = (uint8_t *)gpio->reg_gpcr + pin;
volatile uint8_t *reg_volt_sel = (uint8_t *)(gpio->volt_sel[pin]);
/* Setting pull-up or pull-down. */
switch (IT8XXX2_DT_PINCFG_PUPDR(pincfg)) {
case IT8XXX2_PULL_PIN_DEFAULT:
/* No pull-up or pull-down */
*reg_gpcr &= ~(GPCR_PORT_PIN_MODE_PULLUP |
GPCR_PORT_PIN_MODE_PULLDOWN);
break;
case IT8XXX2_PULL_UP:
*reg_gpcr = (*reg_gpcr | GPCR_PORT_PIN_MODE_PULLUP) &
~GPCR_PORT_PIN_MODE_PULLDOWN;
break;
case IT8XXX2_PULL_DOWN:
*reg_gpcr = (*reg_gpcr | GPCR_PORT_PIN_MODE_PULLDOWN) &
~GPCR_PORT_PIN_MODE_PULLUP;
break;
default:
LOG_ERR("This pull level is not supported.");
return -EINVAL;
}
/*
* Since not all GPIOs support voltage selection, configure voltage
* selection register only if it is present.
*/
if (reg_volt_sel != NULL) {
/* Setting voltage 3.3V or 1.8V. */
switch (IT8XXX2_DT_PINCFG_VOLTAGE(pincfg)) {
case IT8XXX2_VOLTAGE_3V3:
/* Input voltage selection 3.3V. */
*reg_volt_sel &= ~gpio->volt_sel_mask[pin];
break;
case IT8XXX2_VOLTAGE_1V8:
__ASSERT(!(IT8XXX2_DT_PINCFG_PUPDR(pincfg)
== IT8XXX2_PULL_UP),
"Don't enable internal pullup if 1.8V voltage is used");
/* Input voltage selection 1.8V. */
*reg_volt_sel |= gpio->volt_sel_mask[pin];
break;
default:
LOG_ERR("The voltage selection is not supported");
return -EINVAL;
}
}
/* Setting tri-state mode. */
if (IT8XXX2_DT_PINCFG_IMPEDANCE(pincfg)) {
*reg_gpcr |= (GPCR_PORT_PIN_MODE_PULLUP |
GPCR_PORT_PIN_MODE_PULLDOWN);
}
return 0;
}
static int pinctrl_gpio_it8xxx2_configure_pins(const pinctrl_soc_pin_t *pins)
{
const struct pinctrl_it8xxx2_config *pinctrl_config = pins->pinctrls->config;
const struct pinctrl_it8xxx2_gpio *gpio = &(pinctrl_config->gpio);
uint8_t pin = pins->pin;
volatile uint8_t *reg_gpcr = (uint8_t *)gpio->reg_gpcr + pin;
volatile uint8_t *reg_func3_gcr = (uint8_t *)(gpio->func3_gcr[pin]);
volatile uint8_t *reg_func4_gcr = (uint8_t *)(gpio->func4_gcr[pin]);
/* Handle PIN configuration. */
if (pinctrl_it8xxx2_set(pins)) {
LOG_ERR("Pin configuration is invalid.");
return -EINVAL;
}
/*
* If pincfg is input, we don't need to handle
* alternate function.
*/
if (IT8XXX2_DT_PINCFG_INPUT(pins->pincfg)) {
*reg_gpcr = (*reg_gpcr | GPCR_PORT_PIN_MODE_INPUT) &
~GPCR_PORT_PIN_MODE_OUTPUT;
return 0;
}
/*
* Handle alternate function.
*/
/* Common settings for alternate function. */
*reg_gpcr &= ~(GPCR_PORT_PIN_MODE_INPUT |
GPCR_PORT_PIN_MODE_OUTPUT);
switch (pins->alt_func) {
case IT8XXX2_ALT_FUNC_1:
/* Func1: Alternate function has been set above. */
break;
case IT8XXX2_ALT_FUNC_2:
/* Func2: WUI function: turn the pin into an input */
*reg_gpcr |= GPCR_PORT_PIN_MODE_INPUT;
break;
case IT8XXX2_ALT_FUNC_3:
/*
* Func3: In addition to the alternate setting above,
* Func3 also need to set the general control.
*/
*reg_func3_gcr |= gpio->func3_en_mask[pin];
break;
case IT8XXX2_ALT_FUNC_4:
/*
* Func4: In addition to the alternate setting above,
* Func4 also need to set the general control.
*/
*reg_func4_gcr |= gpio->func4_en_mask[pin];
break;
case IT8XXX2_ALT_DEFAULT:
*reg_gpcr = (*reg_gpcr | GPCR_PORT_PIN_MODE_INPUT) &
~GPCR_PORT_PIN_MODE_OUTPUT;
*reg_func3_gcr &= ~gpio->func3_en_mask[pin];
*reg_func4_gcr &= ~gpio->func4_en_mask[pin];
break;
default:
LOG_ERR("This function is not supported.");
return -EINVAL;
}
return 0;
}
static int pinctrl_kscan_it8xxx2_set(const pinctrl_soc_pin_t *pins)
{
const struct pinctrl_it8xxx2_config *pinctrl_config = pins->pinctrls->config;
const struct pinctrl_it8xxx2_ksi_kso *ksi_kso = &(pinctrl_config->ksi_kso);
volatile uint8_t *reg_ctrl = ksi_kso->reg_ctrl;
uint8_t pullup_mask = ksi_kso->pullup_mask;
uint8_t pp_od_mask = ksi_kso->pp_od_mask;
uint32_t pincfg = pins->pincfg;
/*
* Enable or disable internal pull-up (this bit apply to all pins):
* If KSI[7:0]/KSO[15:0] is in KBS mode , setting 1 enables the internal
* pull-up (KSO[17:16] setting internal pull-up by GPIO port GPCR register).
* If KSI[7:0]/KSO[15:0] is in GPIO mode, then this bit is always disabled.
*/
switch (IT8XXX2_DT_PINCFG_PULLUP(pincfg)) {
case IT8XXX2_PULL_PIN_DEFAULT:
/* Disable internal pulll-up */
*reg_ctrl &= ~pullup_mask;
break;
case IT8XXX2_PULL_UP:
*reg_ctrl |= pullup_mask;
break;
default:
LOG_ERR("This pull level is not supported.");
return -EINVAL;
}
/*
* Set push-pull or open-drain mode (this bit apply to all pins):
* KSI[7:0] doesn't support push-pull and open-drain settings in kbs mode.
* If KSO[17:0] is in KBS mode, setting 1 selects open-drain mode,
* setting 0 selects push-pull mode.
* If KSO[15:0] is in GPIO mode, then this bit is always disabled.
*/
if (pp_od_mask != NO_FUNC) {
switch (IT8XXX2_DT_PINCFG_PP_OD(pincfg)) {
case IT8XXX2_PUSH_PULL:
*reg_ctrl &= ~pp_od_mask;
break;
case IT8XXX2_OPEN_DRAIN:
*reg_ctrl |= pp_od_mask;
break;
default:
LOG_ERR("This pull mode is not supported.");
return -EINVAL;
}
}
return 0;
}
static int pinctrl_kscan_it8xxx2_configure_pins(const pinctrl_soc_pin_t *pins)
{
const struct pinctrl_it8xxx2_config *pinctrl_config = pins->pinctrls->config;
const struct pinctrl_it8xxx2_ksi_kso *ksi_kso = &(pinctrl_config->ksi_kso);
/* Set a pin of KSI[7:0]/KSO[15:0] to pullup, push-pull/open-drain */
if (pinctrl_kscan_it8xxx2_set(pins)) {
return -EINVAL;
}
#ifdef CONFIG_SOC_IT8XXX2_REG_SET_V1
uint8_t pin_mask = BIT(pins->pin);
volatile uint8_t *reg_gctrl = ksi_kso->reg_gctrl;
switch (pins->alt_func) {
case IT8XXX2_ALT_FUNC_1:
/* Set a pin of KSI[7:0]/KSO[15:0] to kbs mode */
*reg_gctrl &= ~pin_mask;
break;
case IT8XXX2_ALT_DEFAULT:
/* Set a pin of KSI[7:0]/KSO[15:0] to gpio mode */
*reg_gctrl |= pin_mask;
break;
#elif CONFIG_SOC_IT8XXX2_REG_SET_V2
uint8_t pin = pins->pin;
volatile uint8_t *reg_gctrl = ksi_kso->reg_gctrl + pin;
switch (pins->alt_func) {
case IT8XXX2_ALT_FUNC_1:
/* Set a pin of KSI[7:0]/KSO[15:0] to kbs mode */
*reg_gctrl &= ~(GPCR_PORT_PIN_MODE_INPUT |
GPCR_PORT_PIN_MODE_OUTPUT);
break;
case IT8XXX2_ALT_DEFAULT:
/* Set a pin of KSI[7:0]/KSO[15:0] to gpio mode */
*reg_gctrl = (*reg_gctrl | GPCR_PORT_PIN_MODE_INPUT) &
~GPCR_PORT_PIN_MODE_OUTPUT;
break;
#endif
default:
LOG_ERR("Alternate function not supported");
return -ENOTSUP;
}
return 0;
}
int pinctrl_configure_pins(const pinctrl_soc_pin_t *pins, uint8_t pin_cnt,
uintptr_t reg)
{
ARG_UNUSED(reg);
const struct pinctrl_it8xxx2_config *pinctrl_config;
int status;
for (uint8_t i = 0U; i < pin_cnt; i++) {
pinctrl_config = pins[i].pinctrls->config;
if (pinctrl_config->gpio_group) {
status = pinctrl_gpio_it8xxx2_configure_pins(&pins[i]);
} else {
status = pinctrl_kscan_it8xxx2_configure_pins(&pins[i]);
}
if (status < 0) {
LOG_ERR("%s pin%d configuration is invalid.",
pins[i].pinctrls->name, pins[i].pin);
return status;
}
}
return 0;
}
static int pinctrl_it8xxx2_init(const struct device *dev)
{
struct gpio_it8xxx2_regs *const gpio_base = GPIO_IT8XXX2_REG_BASE;
/*
* The default value of LPCRSTEN is bit2:1 = 10b(GPD2) in GCR.
* If LPC reset is enabled on GPB7, we have to clear bit2:1
* to 00b.
*/
gpio_base->GPIO_GCR &= ~IT8XXX2_GPIO_LPCRSTEN;
/*
* TODO: If UART2 swaps from bit2:1 to bit6:5 in H group, we
* have to set UART1PSEL = 1 in UART1PMR register.
*/
#ifdef CONFIG_SOC_IT8XXX2_REG_SET_V2
/*
* Swap the default I2C2 SMCLK2/SMDAT2 pins from GPC7/GPD0 to GPF6/GPF7,
* and I2C3 SMCLK3/SMDAT3 pins from GPB2/GPB5 to GPH1/GPH2,
* and I2C5 SMCLK5/SMDAT5 pins from GPE1/GPE2 to GPA4/GPA5,
*/
gpio_base->GPIO_GCR7 &= ~(IT8XXX2_GPIO_SMB2PS |
IT8XXX2_GPIO_SMB3PS |
IT8XXX2_GPIO_SMB5PS);
#endif
return 0;
}
#define INIT_UNION_CONFIG(inst) \
COND_CODE_1(DT_INST_PROP(inst, gpio_group), \
(.gpio = { \
.reg_gpcr = (uint8_t *)DT_INST_REG_ADDR_BY_IDX(inst, 0), \
.func3_gcr = DT_INST_PROP(inst, func3_gcr), \
.func3_en_mask = DT_INST_PROP(inst, func3_en_mask), \
.func4_gcr = DT_INST_PROP(inst, func4_gcr), \
.func4_en_mask = DT_INST_PROP(inst, func4_en_mask), \
.volt_sel = DT_INST_PROP(inst, volt_sel), \
.volt_sel_mask = DT_INST_PROP(inst, volt_sel_mask), \
}), \
(.ksi_kso = { \
.reg_gctrl = (uint8_t *)DT_INST_REG_ADDR_BY_IDX(inst, 0), \
.reg_ctrl = (uint8_t *)DT_INST_REG_ADDR_BY_IDX(inst, 1), \
.pp_od_mask = (uint8_t)DT_INST_PROP(inst, pp_od_mask), \
.pullup_mask = (uint8_t)DT_INST_PROP(inst, pullup_mask), \
}) \
)
#define PINCTRL_ITE_INIT(inst) \
static const struct pinctrl_it8xxx2_config pinctrl_it8xxx2_cfg_##inst = { \
.gpio_group = DT_INST_PROP(inst, gpio_group), \
{ \
INIT_UNION_CONFIG(inst) \
} \
}; \
\
DEVICE_DT_INST_DEFINE(inst, &pinctrl_it8xxx2_init, \
NULL, \
NULL, \
&pinctrl_it8xxx2_cfg_##inst, \
PRE_KERNEL_1, \
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \
NULL);
DT_INST_FOREACH_STATUS_OKAY(PINCTRL_ITE_INIT)