| /* |
| * Copyright (c) 2020 ITE Corporation. All Rights Reserved. |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| /** |
| * @brief PINMUX driver for the IT8xxx2 |
| */ |
| |
| #include <errno.h> |
| #include <device.h> |
| #include <drivers/pinmux.h> |
| #include <soc.h> |
| #include <dt-bindings/pinctrl/it8xxx2-pinctrl.h> |
| |
| #define DT_DRV_COMPAT ite_it8xxx2_pinmux |
| |
| #include <logging/log.h> |
| LOG_MODULE_REGISTER(pinmux_ite_it8xxx2, LOG_LEVEL_ERR); |
| |
| struct pinmux_it8xxx2_config { |
| /* gpio port control register (byte mapping to pin) */ |
| uintptr_t reg_gpcr; |
| /* function 3 general control register */ |
| uintptr_t func3_gcr[8]; |
| /* function 4 general control register */ |
| uintptr_t func4_gcr[8]; |
| /* function 3 enable mask */ |
| uint8_t func3_en_mask[8]; |
| /* function 4 enable mask */ |
| uint8_t func4_en_mask[8]; |
| }; |
| |
| #define DEV_CFG(dev) \ |
| ((const struct pinmux_it8xxx2_config * const) \ |
| (dev)->config) |
| |
| static int pinmux_it8xxx2_set(const struct device *dev, |
| uint32_t pin, uint32_t func) |
| { |
| const struct pinmux_it8xxx2_config *pinmux_config = DEV_CFG(dev); |
| |
| volatile uint8_t *reg_gpcr = |
| (uint8_t *)(pinmux_config->reg_gpcr + pin); |
| volatile uint8_t *reg_func3_gcr = |
| (uint8_t *)(pinmux_config->func3_gcr[pin]); |
| volatile uint8_t *reg_func4_gcr = |
| (uint8_t *)(pinmux_config->func4_gcr[pin]); |
| |
| if (pin >= IT8XXX2_PINMUX_PINS) { |
| return -EINVAL; |
| } |
| |
| /* Common settings for alternate function. */ |
| *reg_gpcr &= ~(GPCR_PORT_PIN_MODE_INPUT | |
| GPCR_PORT_PIN_MODE_OUTPUT); |
| |
| switch (func) { |
| case IT8XXX2_PINMUX_FUNC_1: |
| /* Func1: Alternate function has been set above. */ |
| break; |
| case IT8XXX2_PINMUX_FUNC_2: |
| /* Func2: WUI function: turn the pin into an input */ |
| *reg_gpcr |= GPCR_PORT_PIN_MODE_INPUT; |
| break; |
| case IT8XXX2_PINMUX_FUNC_3: |
| /* |
| * Func3: In addition to the alternate setting above, |
| * Func3 also need to set the general control. |
| */ |
| *reg_func3_gcr |= pinmux_config->func3_en_mask[pin]; |
| break; |
| case IT8XXX2_PINMUX_FUNC_4: |
| /* |
| * Func4: In addition to the alternate setting above, |
| * Func4 also need to set the general control. |
| */ |
| *reg_func4_gcr |= pinmux_config->func4_en_mask[pin]; |
| break; |
| default: |
| LOG_ERR("This function is not supported"); |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| static int pinmux_it8xxx2_get(const struct device *dev, |
| uint32_t pin, uint32_t *func) |
| { |
| const struct pinmux_it8xxx2_config *pinmux_config = DEV_CFG(dev); |
| |
| volatile uint8_t *reg_gpcr = |
| (uint8_t *)(pinmux_config->reg_gpcr + pin); |
| |
| if (pin >= IT8XXX2_PINMUX_PINS || func == NULL) { |
| return -EINVAL; |
| } |
| |
| *func = (*reg_gpcr & (GPCR_PORT_PIN_MODE_INPUT | |
| GPCR_PORT_PIN_MODE_OUTPUT)) == GPCR_PORT_PIN_MODE_INPUT ? |
| IT8XXX2_PINMUX_FUNC_2 : IT8XXX2_PINMUX_FUNC_1; |
| |
| /* TODO: IT8XXX2_PINMUX_FUNC_3 & IT8XXX2_PINMUX_FUNC_4 */ |
| |
| return 0; |
| } |
| |
| static int pinmux_it8xxx2_pullup(const struct device *dev, |
| uint32_t pin, uint8_t func) |
| { |
| const struct pinmux_it8xxx2_config *pinmux_config = DEV_CFG(dev); |
| |
| volatile uint8_t *reg_gpcr = |
| (uint8_t *)(pinmux_config->reg_gpcr + pin); |
| |
| if (func == PINMUX_PULLUP_ENABLE) { |
| *reg_gpcr = (*reg_gpcr | GPCR_PORT_PIN_MODE_PULLUP) & |
| ~GPCR_PORT_PIN_MODE_PULLDOWN; |
| } else if (func == PINMUX_PULLUP_DISABLE) { |
| *reg_gpcr &= ~(GPCR_PORT_PIN_MODE_PULLUP | |
| GPCR_PORT_PIN_MODE_PULLDOWN); |
| } else { |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| static int pinmux_it8xxx2_input(const struct device *dev, |
| uint32_t pin, uint8_t func) |
| { |
| const struct pinmux_it8xxx2_config *pinmux_config = DEV_CFG(dev); |
| |
| volatile uint8_t *reg_gpcr = |
| (uint8_t *)(pinmux_config->reg_gpcr + pin); |
| |
| *reg_gpcr &= ~(GPCR_PORT_PIN_MODE_INPUT | |
| GPCR_PORT_PIN_MODE_OUTPUT); |
| |
| if (func == PINMUX_INPUT_ENABLED) { |
| *reg_gpcr |= GPCR_PORT_PIN_MODE_INPUT; |
| } else if (func == PINMUX_OUTPUT_ENABLED) { |
| *reg_gpcr |= GPCR_PORT_PIN_MODE_OUTPUT; |
| } else { |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| static int pinmux_it8xxx2_init(const struct device *dev) |
| { |
| ARG_UNUSED(dev); |
| |
| /* |
| * 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. |
| */ |
| IT8XXX2_GPIO_GCR &= ~(BIT(1) | BIT(2)); |
| |
| /* |
| * TODO: If SMBUS3 swaps from H group to F group, we have to |
| * set SMB3PSEL = 1 in PMER3 register. |
| */ |
| |
| /* |
| * TODO: If UART2 swaps from bit2:1 to bit6:5 in H group, we |
| * have to set UART1PSEL = 1 in UART1PMR register. |
| */ |
| |
| return 0; |
| } |
| |
| static const struct pinmux_driver_api pinmux_it8xxx2_driver_api = { |
| .set = pinmux_it8xxx2_set, |
| .get = pinmux_it8xxx2_get, |
| .pullup = pinmux_it8xxx2_pullup, |
| .input = pinmux_it8xxx2_input, |
| }; |
| |
| #define PINMUX_ITE_INIT(inst) \ |
| static const struct pinmux_it8xxx2_config pinmux_it8xxx2_cfg_##inst = { \ |
| .reg_gpcr = DT_INST_REG_ADDR(inst), \ |
| .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), \ |
| }; \ |
| \ |
| DEVICE_DT_INST_DEFINE(inst, \ |
| &pinmux_it8xxx2_init, \ |
| NULL, NULL, &pinmux_it8xxx2_cfg_##inst, \ |
| PRE_KERNEL_1, \ |
| CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \ |
| &pinmux_it8xxx2_driver_api); |
| |
| DT_INST_FOREACH_STATUS_OKAY(PINMUX_ITE_INIT) |