diff --git a/boards/arm/npcm400f_evb/fun_def_list.h b/boards/arm/npcm400f_evb/fun_def_list.h index 52dd2ee0b03948..4a4fcf4042b4a3 100644 --- a/boards/arm/npcm400f_evb/fun_def_list.h +++ b/boards/arm/npcm400f_evb/fun_def_list.h @@ -84,3 +84,11 @@ FUN_DEFINE(DT_NODELABEL(pinctrl_i3c4_default), I3C5_SCL, I3C5_SDA) #if DT_NODE_HAS_STATUS(DT_NODELABEL(i3c5), okay) && CONFIG_I3C_NPCM4XX FUN_DEFINE(DT_NODELABEL(pinctrl_i3c5_default), I3C6_SCL, I3C6_SDA) #endif + +#if DT_NODE_HAS_STATUS(DT_NODELABEL(sgpio0), okay) && CONFIG_SGPIO_NPCM4XX +FUN_DEFINE(DT_NODELABEL(pinctrl_sgpio0_default), IOX1_LDSH, IOX1_DOUT, IOX1_DIN, IOX1_SCLK) +#endif + +#if DT_NODE_HAS_STATUS(DT_NODELABEL(sgpio1), okay) && CONFIG_SGPIO_NPCM4XX +FUN_DEFINE(DT_NODELABEL(pinctrl_sgpio1_default), IOX2_LDSH, IOX2_DOUT, IOX2_DIN, IOX2_SCLK) +#endif diff --git a/boards/arm/npcm400f_evb/npcm400f_evb.dts b/boards/arm/npcm400f_evb/npcm400f_evb.dts index 110aacfa6a2803..fcf3b3e50428fd 100644 --- a/boards/arm/npcm400f_evb/npcm400f_evb.dts +++ b/boards/arm/npcm400f_evb/npcm400f_evb.dts @@ -232,3 +232,329 @@ * }; */ }; + +&sgpio0 { + status = "disabled"; + sgpio0_0: sgpio0_0 { + #gpio-cells = <2>; + gpio-controller; + ngpios = <8>; + interrupt-controller; + #interrupt-cells = <2>; + label = "SGPIO0_0"; + pin-offset = <0>; + }; + + sgpio0_1: sgpio0_1 { + #gpio-cells = <2>; + gpio-controller; + ngpios = <8>; + interrupt-controller; + #interrupt-cells = <2>; + label = "SGPIO0_1"; + pin-offset = <8>; + }; + + sgpio0_2: sgpio0_2 { + #gpio-cells = <2>; + gpio-controller; + ngpios = <8>; + interrupt-controller; + #interrupt-cells = <2>; + label = "SGPIO0_2"; + pin-offset = <16>; + }; + + sgpio0_3: sgpio0_3 { + #gpio-cells = <2>; + gpio-controller; + ngpios = <8>; + interrupt-controller; + #interrupt-cells = <2>; + label = "SGPIO0_3"; + pin-offset = <24>; + }; + + sgpio0_4: sgpio0_4 { + #gpio-cells = <2>; + gpio-controller; + ngpios = <8>; + interrupt-controller; + #interrupt-cells = <2>; + label = "SGPIO0_4"; + pin-offset = <32>; + }; + + sgpio0_5: sgpio0_5 { + #gpio-cells = <2>; + gpio-controller; + ngpios = <8>; + interrupt-controller; + #interrupt-cells = <2>; + label = "SGPIO0_5"; + pin-offset = <40>; + }; + + sgpio0_6: sgpio0_6 { + #gpio-cells = <2>; + gpio-controller; + ngpios = <8>; + interrupt-controller; + #interrupt-cells = <2>; + label = "SGPIO0_6"; + pin-offset = <48>; + }; + + sgpio0_7: sgpio0_7 { + #gpio-cells = <2>; + gpio-controller; + ngpios = <8>; + interrupt-controller; + #interrupt-cells = <2>; + label = "SGPIO0_7"; + pin-offset = <56>; + }; + + sgpio0_8: sgpio0_8 { + #gpio-cells = <2>; + gpio-controller; + ngpios = <8>; + interrupt-controller; + #interrupt-cells = <2>; + label = "SGPIO0_8"; + pin-offset = <64>; + }; + + sgpio0_9: sgpio0_9 { + #gpio-cells = <2>; + gpio-controller; + ngpios = <8>; + interrupt-controller; + #interrupt-cells = <2>; + label = "SGPIO0_9"; + pin-offset = <72>; + }; + + sgpio0_a: sgpio0_a { + #gpio-cells = <2>; + gpio-controller; + ngpios = <8>; + interrupt-controller; + #interrupt-cells = <2>; + label = "SGPIO0_a"; + pin-offset = <80>; + }; + + sgpio0_b: sgpio0_b { + #gpio-cells = <2>; + gpio-controller; + ngpios = <8>; + interrupt-controller; + #interrupt-cells = <2>; + label = "SGPIO0_b"; + pin-offset = <88>; + }; + + sgpio0_c: sgpio0_c { + #gpio-cells = <2>; + gpio-controller; + ngpios = <8>; + interrupt-controller; + #interrupt-cells = <2>; + label = "SGPIO0_c"; + pin-offset = <96>; + }; + + sgpio0_d: sgpio0_d { + #gpio-cells = <2>; + gpio-controller; + ngpios = <8>; + interrupt-controller; + #interrupt-cells = <2>; + label = "SGPIO0_d"; + pin-offset = <104>; + }; + + sgpio0_e: sgpio0_e { + #gpio-cells = <2>; + gpio-controller; + ngpios = <8>; + interrupt-controller; + #interrupt-cells = <2>; + label = "SGPIO0_e"; + pin-offset = <112>; + }; + + sgpio0_f: sgpio0_f { + #gpio-cells = <2>; + gpio-controller; + ngpios = <8>; + interrupt-controller; + #interrupt-cells = <2>; + label = "SGPIO0_f"; + pin-offset = <120>; + }; +}; + +&sgpio1 { + status = "disabled"; + sgpio1_0: sgpio1_0 { + #gpio-cells = <2>; + gpio-controller; + ngpios = <8>; + interrupt-controller; + #interrupt-cells = <2>; + label = "SGPIO1_0"; + pin-offset = <0>; + }; + + sgpio1_1: sgpio1_1 { + #gpio-cells = <2>; + gpio-controller; + ngpios = <8>; + interrupt-controller; + #interrupt-cells = <2>; + label = "SGPIO1_1"; + pin-offset = <8>; + }; + + sgpio1_2: sgpio1_2 { + #gpio-cells = <2>; + gpio-controller; + ngpios = <8>; + interrupt-controller; + #interrupt-cells = <2>; + label = "SGPIO1_2"; + pin-offset = <16>; + }; + + sgpio1_3: sgpio1_3 { + #gpio-cells = <2>; + gpio-controller; + ngpios = <8>; + interrupt-controller; + #interrupt-cells = <2>; + label = "SGPIO1_3"; + pin-offset = <24>; + }; + + sgpio1_4: sgpio1_4 { + #gpio-cells = <2>; + gpio-controller; + ngpios = <8>; + interrupt-controller; + #interrupt-cells = <2>; + label = "SGPIO1_4"; + pin-offset = <32>; + }; + + sgpio1_5: sgpio1_5 { + #gpio-cells = <2>; + gpio-controller; + ngpios = <8>; + interrupt-controller; + #interrupt-cells = <2>; + label = "SGPIO1_5"; + pin-offset = <40>; + }; + + sgpio1_6: sgpio1_6 { + #gpio-cells = <2>; + gpio-controller; + ngpios = <8>; + interrupt-controller; + #interrupt-cells = <2>; + label = "SGPIO1_6"; + pin-offset = <48>; + }; + + sgpio1_7: sgpio1_7 { + #gpio-cells = <2>; + gpio-controller; + ngpios = <8>; + interrupt-controller; + #interrupt-cells = <2>; + label = "SGPIO1_7"; + pin-offset = <56>; + }; + + sgpio1_8: sgpio1_8 { + #gpio-cells = <2>; + gpio-controller; + ngpios = <8>; + interrupt-controller; + #interrupt-cells = <2>; + label = "SGPIO1_8"; + pin-offset = <64>; + }; + + sgpio1_9: sgpio1_9 { + #gpio-cells = <2>; + gpio-controller; + ngpios = <8>; + interrupt-controller; + #interrupt-cells = <2>; + label = "SGPIO1_9"; + pin-offset = <72>; + }; + + sgpio1_a: sgpio1_a { + #gpio-cells = <2>; + gpio-controller; + ngpios = <8>; + interrupt-controller; + #interrupt-cells = <2>; + label = "SGPIO1_a"; + pin-offset = <80>; + }; + + sgpio1_b: sgpio1_b { + #gpio-cells = <2>; + gpio-controller; + ngpios = <8>; + interrupt-controller; + #interrupt-cells = <2>; + label = "SGPIO1_b"; + pin-offset = <88>; + }; + + sgpio1_c: sgpio1_c { + #gpio-cells = <2>; + gpio-controller; + ngpios = <8>; + interrupt-controller; + #interrupt-cells = <2>; + label = "SGPIO1_c"; + pin-offset = <96>; + }; + + sgpio1_d: sgpio1_d { + #gpio-cells = <2>; + gpio-controller; + ngpios = <8>; + interrupt-controller; + #interrupt-cells = <2>; + label = "SGPIO1_d"; + pin-offset = <104>; + }; + + sgpio1_e: sgpio1_e { + #gpio-cells = <2>; + gpio-controller; + ngpios = <8>; + interrupt-controller; + #interrupt-cells = <2>; + label = "SGPIO1_e"; + pin-offset = <112>; + }; + + sgpio1_f: sgpio1_f { + #gpio-cells = <2>; + gpio-controller; + ngpios = <8>; + interrupt-controller; + #interrupt-cells = <2>; + label = "SGPIO1_f"; + pin-offset = <120>; + }; +}; diff --git a/boards/arm/npcm400f_evb/npcm400f_evb_defconfig b/boards/arm/npcm400f_evb/npcm400f_evb_defconfig index 6cfb973b3b7524..f20c49568920ec 100644 --- a/boards/arm/npcm400f_evb/npcm400f_evb_defconfig +++ b/boards/arm/npcm400f_evb/npcm400f_evb_defconfig @@ -102,3 +102,6 @@ CONFIG_USB_DC_NPCM4XX=y CONFIG_JTAG=y CONFIG_JTAG_SHELL=y CONFIG_JTAG_NPCM4XX=y + +# SGPIO Driver +CONFIG_SGPIO_NPCM4XX=n diff --git a/drivers/gpio/CMakeLists.txt b/drivers/gpio/CMakeLists.txt index ac6fb25fa8457c..46c16661ccb1a7 100644 --- a/drivers/gpio/CMakeLists.txt +++ b/drivers/gpio/CMakeLists.txt @@ -36,6 +36,7 @@ zephyr_library_sources_ifdef(CONFIG_GPIO_LPC11U6X gpio_lpc11u6x.c) zephyr_library_sources_ifdef(CONFIG_GPIO_XLNX_AXI gpio_xlnx_axi.c) zephyr_library_sources_ifdef(CONFIG_GPIO_NPCX gpio_npcx.c) zephyr_library_sources_ifdef(CONFIG_GPIO_NPCM4XX gpio_npcm4xx.c) +zephyr_library_sources_ifdef(CONFIG_SGPIO_NPCM4XX gpio_npcm4xx_sgpio.c) zephyr_library_sources_ifdef(CONFIG_GPIO_EMUL gpio_emul.c) zephyr_library_sources_ifdef(CONFIG_GPIO_PSOC6 gpio_psoc6.c) zephyr_library_sources_ifdef(CONFIG_GPIO_PCAL6408A gpio_pcal6408a.c) diff --git a/drivers/gpio/Kconfig.npcm4xx b/drivers/gpio/Kconfig.npcm4xx index 744393fb483491..1da88b1b3abbac 100644 --- a/drivers/gpio/Kconfig.npcm4xx +++ b/drivers/gpio/Kconfig.npcm4xx @@ -10,3 +10,18 @@ config GPIO_NPCM4XX This option enables the GPIO driver for NPCM4XX family of processors. Say y if you wish to use GPIO on NPCM4XX MCU. + +config SGPIO_NPCM4XX + bool "Nuvoton NPCM4XX sgpio driver" + depends on SOC_FAMILY_NPCM4XX + help + This option enables the SGPIO driver for NPCM4XX family of + processors. + Say y if you wish to use SGPIO on NPCM4XX MCU. + +config GPIO_NPCM4XX_SGPIO_INIT_PRIORITY + int "Init priority" + depends on SGPIO_NPCM4XX + default 60 + help + Device driver initialization priority. diff --git a/drivers/gpio/gpio_npcm4xx_sgpio.c b/drivers/gpio/gpio_npcm4xx_sgpio.c new file mode 100644 index 00000000000000..9382133a5a1d5f --- /dev/null +++ b/drivers/gpio/gpio_npcm4xx_sgpio.c @@ -0,0 +1,585 @@ +/* + * Copyright (c) 2023 Nuvoton Technology Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT nuvoton_npcm4xx_sgpio + +#include +#include +#include +#include +#include + +#include "gpio_utils.h" +#include "soc_gpio.h" +#include "soc_miwu.h" +#include "sig_id.h" + +#include +LOG_MODULE_REGISTER(sgpio_npcm4xx); + +typedef void (*sgpio_irq_config_func_t)(const struct device *dev); + +#define IXOEVCFG_MASK 0x3 +#define IXOEVCFG_BOTH 0x3 +#define IXOEVCFG_FALLING 0x2 +#define IXOEVCFG_RISING 0x1 + +static const uint8_t npcm4xx_CLK_SEL[] = { + 0x00, 0x05, 0x06, 0x07, 0x0C, 0x0D +}; + +static const int npcm4xx_SFT_CLK[] = { + 1024, 32, 16, 8, 4, 3 +}; + +/* Driver config */ +struct device_array { + const struct device *dev; +}; + +struct sgpio_npcm4xx_parent_config { + /* SGPIO controller base address */ + struct sgpio_reg *base; + struct device_array *child_dev; + uint32_t child_num; + /* clock configuration */ + struct npcm4xx_clk_cfg clk_cfg; + sgpio_irq_config_func_t irq_conf_func; + struct k_spinlock lock; + uint32_t sgpio_freq; + uint8_t nin_sgpio; + uint8_t nout_sgpio; + uint8_t in_port; + uint8_t out_port; +}; + +struct sgpio_npcm4xx_config { + /* gpio_driver_config needs to be first */ + struct gpio_driver_config common; + /* Parent device handler for shared resource */ + const struct device *parent; + /* SGPIO controller base address */ + struct sgpio_reg *base; + uint8_t pin_offset; +}; + +/* Driver data */ +struct sgpio_npcm4xx_data { + /* gpio_driver_data needs to be first */ + struct gpio_driver_data common; + /* list of callbacks */ + sys_slist_t cb; +}; + +struct npcm4xx_scfg_config { + /* scfg device base address */ + uintptr_t base_scfg; +}; + +static const struct npcm4xx_scfg_config npcm4xx_scfg_cfg = { + .base_scfg = DT_REG_ADDR_BY_NAME(DT_NODELABEL(scfg), scfg), +}; + +/* Driver convenience defines */ +#define DRV_PARENT_CFG(dev) ((struct sgpio_npcm4xx_parent_config *)(dev)->config) + +#define DRV_CONFIG(dev) ((struct sgpio_npcm4xx_config *)(dev)->config) + +#define DRV_DATA(dev) ((struct sgpio_npcm4xx_data *)(dev)->data) + +#define HAL_PARENT_INSTANCE(dev) (struct sgpio_reg *)(DRV_PARENT_CFG(dev)->base) + +#define HAL_INSTANCE(dev) (struct sgpio_reg *)(DRV_CONFIG(dev)->base) + +#define HAL_SFCG_INST() (struct scfg_reg *)(npcm4xx_scfg_cfg.base_scfg) + + +static void npcm_sgpio_setup_enable(const struct device *parent, bool enable) +{ + struct sgpio_reg *const inst = HAL_PARENT_INSTANCE(parent); + uint8_t reg = 0; + + reg = inst->IOXCTS; + reg = reg & ~NPCM4XX_IOXCTS_RD_MODE_MASK; + reg |= NPCM4XX_IOXCTS_RD_MODE_CONTINUOUS; + + if (enable) { + reg |= NPCM4XX_IOXCTS_IOXIF_EN; + inst->IOXCTS = reg; + } else { + reg &= ~NPCM4XX_IOXCTS_IOXIF_EN; + inst->IOXCTS = reg; + } +} + +static int npcm_sgpio_init_port(const struct device *parent) +{ + uint8_t in_port, out_port, set_port, reg; + struct sgpio_npcm4xx_parent_config *const config = DRV_PARENT_CFG(parent); + struct sgpio_reg *const inst = HAL_PARENT_INSTANCE(parent); + + in_port = config->nin_sgpio / 8; + if((config->nin_sgpio % 8) > 0 ) + in_port++; + + out_port = config->nout_sgpio / 8; + if((config->nout_sgpio % 8) > 0 ) + out_port++; + + config->in_port = in_port; + config->out_port = out_port; + + set_port = ((out_port & 0xf) << 4) | (in_port & 0xf); + inst->IOXCFG2 = set_port; + reg = inst->IOXCFG2; + + if(reg == set_port) + return 0; + + return -1; +} + +static int sgpio_npcm4xx_port_get_raw(const struct device *dev, + gpio_port_value_t *value) +{ + struct sgpio_reg *inst = HAL_INSTANCE(dev); + + uint8_t pin_offset = DRV_CONFIG(dev)->pin_offset - MAX_NR_HW_SGPIO; + uint32_t group_idx = pin_offset >> 3; + + // Input an sgpio port which is not an input one. + if(pin_offset < 0) { + LOG_ERR("%s Invalid pinoffset #%d", __func__, DRV_CONFIG(dev)->pin_offset); + return -EINVAL; + } + + *value = (gpio_port_value_t)NPCM4XX_XDIN((uintptr_t)inst, group_idx); + return 0; +} + +static int sgpio_npcm4xx_port_set_masked_raw(const struct device *dev, + gpio_port_pins_t mask, + gpio_port_value_t value) +{ + struct sgpio_reg *inst = HAL_INSTANCE(dev); + uint8_t pin_offset = DRV_CONFIG(dev)->pin_offset; + uint32_t group_idx = pin_offset >> 3; + uint8_t out = NPCM4XX_XDOUT((uintptr_t)inst, group_idx); + uint8_t pin; + bool pin_valid = false; + + // Input an sgpio port which is not an output one. + if(pin_offset >= MAX_NR_HW_SGPIO) { + LOG_ERR("%s Invalid pinoffset #%d", __func__, DRV_CONFIG(dev)->pin_offset); + return -EINVAL; + } + + // The max pin number is 7 in one sgpio port since it's 0-based. + if(mask >= BIT(8)) { + LOG_ERR("%s Invalid mask 0x%x", __func__, mask); + return -EINVAL; + } + + // find the pin which is at MSB + for(pin = 7; pin >= 0; pin--) { + if(mask & BIT(pin)) { + pin_valid = true; + break; + } + } + + if(!pin_valid) { + LOG_ERR("%s Invalid pin #%d", __func__, pin); + return -EINVAL; + } + + // Input a pin number which is greater than the output pin number configured in the device tree. + if((pin + pin_offset) >= DRV_PARENT_CFG(DRV_CONFIG(dev)->parent)->nout_sgpio) { + LOG_ERR("%s Invalid sgpio pin #%d", __func__, (pin + pin_offset)); + return -EINVAL; + } + + NPCM4XX_XDOUT((uintptr_t)inst, group_idx) = ((out & ~mask) | (value & mask)); + + return 0; +} + +static int sgpio_npcm4xx_port_set_bits_raw(const struct device *dev, + gpio_port_value_t mask) +{ + return sgpio_npcm4xx_port_set_masked_raw(dev, mask, mask); +} + +static int sgpio_npcm4xx_port_clear_bits_raw(const struct device *dev, + gpio_port_value_t mask) +{ + struct sgpio_reg *inst = HAL_INSTANCE(dev); + uint8_t pin_offset = DRV_CONFIG(dev)->pin_offset; + uint32_t group_idx = pin_offset >> 3; + uint8_t pin; + bool pin_valid = false; + + // Input an sgpio port which is not an output one. + if(pin_offset >= MAX_NR_HW_SGPIO) { + LOG_ERR("%s Invalid pinoffset #%d", __func__, DRV_CONFIG(dev)->pin_offset); + return -EINVAL; + } + + // The max pin number is 7 in one sgpio port since it's 0-based. + if(mask >= BIT(8)) { + LOG_ERR("%s Invalid mask 0x%x", __func__, mask); + return -EINVAL; + } + + // find the pin which is at MSB + for(pin = 7; pin >= 0; pin--) { + if(mask & BIT(pin)) { + pin_valid = true; + break; + } + } + + if(!pin_valid) { + LOG_ERR("%s Invalid pin #%d", __func__, pin); + return -EINVAL; + } + + // Input a pin number which is greater than the output pin number configured in the device tree. + if((pin + pin_offset) >= DRV_PARENT_CFG(DRV_CONFIG(dev)->parent)->nout_sgpio) { + LOG_ERR("%s Invalid sgpio pin #%d", __func__, (pin + pin_offset)); + return -EINVAL; + } + + /* Clear raw bits of SGPIO output registers */ + NPCM4XX_XDOUT((uintptr_t)inst, group_idx) &= ~mask; + + return 0; +} + +/* GPIO api functions */ +static int sgpio_npcm4xx_config(const struct device *dev, + gpio_pin_t pin, gpio_flags_t flags) +{ + if((DRV_CONFIG(dev)->common.port_pin_mask & (gpio_port_pins_t)BIT(pin)) == 0) + return -ENOTSUP; + + /* Don't support simultaneous in/out mode */ + if (((flags & GPIO_INPUT) != 0) && ((flags & GPIO_OUTPUT) != 0)) + return -ENOTSUP; + + /* Don't support "open source" mode */ + if (((flags & GPIO_SINGLE_ENDED) != 0) && + ((flags & GPIO_LINE_OPEN_DRAIN) == 0)) + return -ENOTSUP; + + /* Does not support pull-up/pull-down */ + if ((flags & (GPIO_PULL_UP | GPIO_PULL_DOWN)) != 0U) + return -ENOTSUP; + + /* Set level 0:low 1:high */ + if ((flags & GPIO_OUTPUT_INIT_HIGH) != 0) + sgpio_npcm4xx_port_set_bits_raw(dev, BIT(pin)); + else if ((flags & GPIO_OUTPUT_INIT_LOW) != 0) + sgpio_npcm4xx_port_clear_bits_raw(dev, BIT(pin)); + + return 0; +} + +static int sgpio_npcm4xx_port_toggle_bits(const struct device *dev, + gpio_port_value_t mask) +{ + struct sgpio_reg *inst = HAL_INSTANCE(dev); + uint8_t pin_offset = DRV_CONFIG(dev)->pin_offset; + uint32_t group_idx = pin_offset >> 3; + uint8_t pin; + bool pin_valid = false; + + // Input an sgpio port which is not an output one. + if(pin_offset >= MAX_NR_HW_SGPIO) { + LOG_ERR("%s Invalid pinoffset #%d", __func__, DRV_CONFIG(dev)->pin_offset); + return -EINVAL; + } + + // The max pin number is 7 in one sgpio port since it's 0-based. + if(mask >= BIT(8)) { + LOG_ERR("%s Invalid mask 0x%x", __func__, mask); + return -EINVAL; + } + + // find the pin which is at MSB + for(pin = 7; pin >= 0; pin--) { + if(mask & BIT(pin)) { + pin_valid = true; + break; + } + } + + if(!pin_valid) { + LOG_ERR("%s Invalid pin #%d", __func__, pin); + return -EINVAL; + } + + // Input a pin number which is greater than the output pin number configured in the device tree. + if((pin + pin_offset) >= DRV_PARENT_CFG(DRV_CONFIG(dev)->parent)->nout_sgpio) { + LOG_ERR("%s Invalid sgpio pin #%d", __func__, (pin + pin_offset)); + return -EINVAL; + } + + /* Toggle raw bits of SGPIO output registers */ + NPCM4XX_XDOUT((uintptr_t)inst, group_idx) ^= mask; + + return 0; +} + +static int sgpio_npcm4xx_pin_interrupt_configure(const struct device *dev, + gpio_pin_t pin, + enum gpio_int_mode mode, + enum gpio_int_trig trig) +{ + struct sgpio_reg *inst = HAL_INSTANCE(dev); + uint8_t pin_offset = DRV_CONFIG(dev)->pin_offset - MAX_NR_HW_SGPIO; + uint32_t group_idx; + uint16_t reg, val; + + // Input an sgpio port which is not an input one. + if(pin_offset < 0) { + LOG_ERR("%s Invalid pinoffset #%d", __func__, DRV_CONFIG(dev)->pin_offset); + return -EINVAL; + } + + if (pin >= 8) { + LOG_ERR("Invalid sgpio pin #%d", pin); + return -EINVAL; + } + + // Input a pin number which is greater than the input pin number configured in the device tree. + if((pin + pin_offset) >= DRV_PARENT_CFG(DRV_CONFIG(dev)->parent)->nin_sgpio) { + LOG_ERR("%s Invalid sgpio pin #%d", __func__, (pin + pin_offset)); + return -EINVAL; + } + + group_idx = pin_offset >> 3; + + /* Configure and enable interrupt? */ + if (mode != GPIO_INT_MODE_DISABLED) { + if (mode == GPIO_INT_MODE_LEVEL) { + if (trig == GPIO_INT_TRIG_LOW) { + val = IXOEVCFG_FALLING; + } else if (trig == GPIO_INT_TRIG_HIGH) { + val = IXOEVCFG_RISING; + } else { + return -ENOTSUP; + } + } else { + if (trig == GPIO_INT_TRIG_LOW) { + val = IXOEVCFG_FALLING; + } else if (trig == GPIO_INT_TRIG_HIGH) { + val = IXOEVCFG_RISING; + } else { + val = IXOEVCFG_BOTH; + } + } + } else + val = 0; + + + npcm_sgpio_setup_enable(DRV_CONFIG(dev)->parent, false); + reg = NPCM4XX_XEVCFG((uintptr_t)inst, group_idx); + reg &= ~(IXOEVCFG_MASK << (pin * 2)); + reg |= val << (pin * 2); + NPCM4XX_XEVCFG((uintptr_t)inst, group_idx) = reg; + npcm_sgpio_setup_enable(DRV_CONFIG(dev)->parent, true); + + return 0; +} + +static int sgpio_npcm4xx_manage_callback(const struct device *dev, + struct gpio_callback *callback, bool set) +{ + struct sgpio_npcm4xx_data *const data = DRV_DATA(dev); + + return gpio_manage_callback(&data->cb, callback, set); +} + +/* GPIO driver registration */ +static const struct gpio_driver_api sgpio_npcm4xx_driver = { + .pin_configure = sgpio_npcm4xx_config, + .port_get_raw = sgpio_npcm4xx_port_get_raw, + .port_set_masked_raw = sgpio_npcm4xx_port_set_masked_raw, + .port_set_bits_raw = sgpio_npcm4xx_port_set_bits_raw, + .port_clear_bits_raw = sgpio_npcm4xx_port_clear_bits_raw, + .port_toggle_bits = sgpio_npcm4xx_port_toggle_bits, + .pin_interrupt_configure = sgpio_npcm4xx_pin_interrupt_configure, + .manage_callback = sgpio_npcm4xx_manage_callback, +}; + +static void sgpio_npcm4xx_isr(const void *arg) +{ + const struct sgpio_npcm4xx_parent_config *cfg; + const struct device *parent = arg; + struct sgpio_npcm4xx_data *data; + struct sgpio_reg *inst; + const struct device *dev; + uint8_t gpio_pin, int_pendding; + uint8_t index, group_idx; + + cfg = DRV_PARENT_CFG(parent); + for (index = 8; index < cfg->child_num; index++) { + dev = cfg->child_dev[index].dev; + inst = HAL_INSTANCE(dev); + data = DRV_DATA(dev); + group_idx = (DRV_CONFIG(dev)->pin_offset - MAX_NR_HW_SGPIO) >> 3; + int_pendding = NPCM4XX_XEVSTS((uintptr_t)inst, group_idx); + gpio_pin = 0; + while (int_pendding) { + if (int_pendding & 0x1) { + gpio_fire_callbacks(&data->cb, dev, BIT(gpio_pin)); + NPCM4XX_XEVSTS((uintptr_t)inst, group_idx) &= BIT(gpio_pin); + } + gpio_pin++; + int_pendding >>= 1; + } + } +} + +int sgpio_npcm4xx_init(const struct device *dev) +{ + return 0; +} + +int sgpio_npcm4xx_parent_init(const struct device *parent) +{ + struct sgpio_npcm4xx_parent_config *const config = DRV_PARENT_CFG(parent); + struct sgpio_reg *const inst = HAL_PARENT_INSTANCE(parent); + const struct device *const clk_dev = + device_get_binding(NPCM4XX_CLK_CTRL_NAME); + struct scfg_reg *inst_scfg = HAL_SFCG_INST(); + uint32_t clk, val; + uint8_t i, size, tmp; + int ret; + + if((config->nin_sgpio > MAX_NR_HW_SGPIO) || (config->nout_sgpio > MAX_NR_HW_SGPIO)) { + LOG_ERR("Number of GPIOs exceeds the maximum of %d: input: %d output: %d\n", + MAX_NR_HW_SGPIO, config->nin_sgpio, config->nout_sgpio); + return -1; + } + + /* Turn on device clock first and get source clock freq. */ + ret = clock_control_on(clk_dev, (clock_control_subsys_t *) + &config->clk_cfg); + if (ret < 0) { + LOG_ERR("Turn on SGPIO clock fail %d", ret); + return ret; + } + + ret = clock_control_get_rate(clk_dev, (clock_control_subsys_t *) + &config->clk_cfg, &clk); + if (ret < 0) { + LOG_ERR("Get SGPIO clock rate error %d", ret); + return ret; + } + + size = ARRAY_SIZE(npcm4xx_CLK_SEL); + tmp = (inst->IOXCFG1) & ~NPCM4XX_IOXCFG1_SFT_CLK_MASK; + + for(i = 0; i < size; i++) { + val = clk / npcm4xx_SFT_CLK[i]; + if ((config->sgpio_freq < val) && (i != 0)) { + inst->IOXCFG1 = tmp | (npcm4xx_CLK_SEL[i-1]); + break; + } else if ((i == (size-1)) && (config->sgpio_freq > val)) { + inst->IOXCFG1 = tmp | (npcm4xx_CLK_SEL[i]); + } + } + + ret = npcm_sgpio_init_port(parent); + if(ret) { + LOG_ERR("SGPIO init port fails"); + return ret; + } + + npcm_sgpio_setup_enable(parent, false); + + /* Disable IRQ and clear Interrupt status registers for all SGPIO Pins. */ + for(i = 0; i < NPCM4XX_SGPIO_PORT_PIN_NUM; i++) { + NPCM4XX_XEVCFG((uintptr_t)inst, i) = 0x0000; + NPCM4XX_XEVSTS((uintptr_t)inst, i) = 0xff; + } + + config->irq_conf_func(parent); + NPCM4XX_DEVALT(inst_scfg, NPCM4XX_DEVALT6A_OFFSET) |= (BIT(NPCM4XX_DEVALT6A_SIOX1_PU_EN) | BIT(NPCM4XX_DEVALT6A_SIOX2_PU_EN)); + npcm_sgpio_setup_enable(parent, true); + + + return 0; +} + +struct device_cont { + const struct sgpio_npcm4xx_config *cfg; + struct sgpio_npcm4xx_data *data; +}; + + +#define NPCM4XX_SGPIO_IRQ_CONFIG_FUNC_DECL(inst) \ + static void sgpio_npcm4xx_irq_config_##inst(const struct device *dev) +#define NPCM4XX_SGPIO_IRQ_CONFIG_FUNC_INIT(inst) .irq_conf_func = sgpio_npcm4xx_irq_config_##inst, +#define NPCM4XX_SGPIO_IRQ_CONFIG_FUNC(inst) \ + static void sgpio_npcm4xx_irq_config_##inst(const struct device *dev) \ + { \ + IRQ_CONNECT(DT_INST_IRQN(inst), DT_INST_IRQ(inst, priority), sgpio_npcm4xx_isr, \ + DEVICE_DT_INST_GET(inst), 0); \ + irq_enable(DT_INST_IRQN(inst)); \ + } + +#define SGPIO_ENUM(node_id) node_id, +#define SGPIO_NPCM4XX_DEV_DATA(node_id) {}, +#define SGPIO_NPCM4XX_DEV_CFG(node_id) { \ + .common = { \ + .port_pin_mask = \ + GPIO_PORT_PIN_MASK_FROM_DT_NODE(node_id) \ + }, \ + .parent = DEVICE_DT_GET(DT_PARENT(node_id)), \ + .base = (struct sgpio_reg *)DT_REG_ADDR(DT_PARENT(node_id)), \ + .pin_offset = DT_PROP(node_id, pin_offset), \ +}, +#define SGPIO_NPCM4XX_DT_DEFINE(node_id) \ + DEVICE_DT_DEFINE(node_id, sgpio_npcm4xx_init, NULL, &DT_PARENT(node_id).data[node_id], \ + &DT_PARENT(node_id).cfg[node_id], POST_KERNEL, 0, &sgpio_npcm4xx_driver); + +#define SGPIO_NPCM4XX_DEV_DECLARE(node_id) { .dev = DEVICE_DT_GET(node_id) }, + +#define NPCM4XX_SGPIO_DEVICE_INIT(inst) \ + NPCM4XX_SGPIO_IRQ_CONFIG_FUNC_DECL(inst); \ + \ + static struct device_array child_dev_##inst[] = { DT_FOREACH_CHILD( \ + DT_DRV_INST(inst), SGPIO_NPCM4XX_DEV_DECLARE) }; \ + static const struct sgpio_npcm4xx_parent_config sgpio_npcm4xx_parent_cfg_##inst = { \ + .base = (struct sgpio_reg *)DT_INST_REG_ADDR(inst), \ + NPCM4XX_SGPIO_IRQ_CONFIG_FUNC_INIT(inst) \ + .clk_cfg = NPCM4XX_DT_CLK_CFG_ITEM(inst), \ + .nin_sgpio = DT_INST_PROP(inst, nuvoton_input_ngpios), \ + .nout_sgpio = DT_INST_PROP(inst, nuvoton_output_ngpios), \ + .sgpio_freq = DT_INST_PROP(inst, nuvoton_bus_freq), \ + .child_dev = child_dev_##inst, \ + .child_num = ARRAY_SIZE(child_dev_##inst), \ + }; \ + DEVICE_DT_INST_DEFINE(inst, sgpio_npcm4xx_parent_init, NULL, NULL, \ + &sgpio_npcm4xx_parent_cfg_##inst, POST_KERNEL, \ + CONFIG_GPIO_NPCM4XX_SGPIO_INIT_PRIORITY, NULL); \ + static const struct sgpio_npcm4xx_config sgpio_npcm4xx_cfg_##inst[] = { DT_FOREACH_CHILD( \ + DT_DRV_INST(inst), SGPIO_NPCM4XX_DEV_CFG) }; \ + static struct sgpio_npcm4xx_data sgpio_npcm4xx_data_##inst[] = { DT_FOREACH_CHILD( \ + DT_DRV_INST(inst), SGPIO_NPCM4XX_DEV_DATA) }; \ + static const struct device_cont DT_DRV_INST(inst) = { \ + .cfg = sgpio_npcm4xx_cfg_##inst, \ + .data = sgpio_npcm4xx_data_##inst, \ + }; \ + enum { DT_FOREACH_CHILD(DT_DRV_INST(inst), SGPIO_ENUM) }; \ + DT_FOREACH_CHILD(DT_DRV_INST(inst), SGPIO_NPCM4XX_DT_DEFINE) \ + \ +NPCM4XX_SGPIO_IRQ_CONFIG_FUNC(inst) + +DT_INST_FOREACH_STATUS_OKAY(NPCM4XX_SGPIO_DEVICE_INIT) diff --git a/dts/arm/nuvoton/npcm4xx.dtsi b/dts/arm/nuvoton/npcm4xx.dtsi index 3f144760658826..8c8d99f5fa9dc0 100644 --- a/dts/arm/nuvoton/npcm4xx.dtsi +++ b/dts/arm/nuvoton/npcm4xx.dtsi @@ -516,6 +516,32 @@ status = "disabled"; }; + sgpio0: sgpio@4001d000 { + compatible = "nuvoton,npcm4xx-sgpio"; + reg = <0x4001d000 0x100>; + interrupts = <80 3>; + clocks = <&pcc NPCM4XX_CLOCK_BUS_APB3 NPCM4XX_PWDWN_CTL7 7>; + pinctrl-0 = <&pinctrl_sgpio0_default>; + nuvoton,input-ngpios = <64>; + nuvoton,output-ngpios = <64>; + nuvoton,bus-freq = <8000000>; + label = "SGPIO0"; + status = "disabled"; + }; + + sgpio1: sgpio@4001d100 { + compatible = "nuvoton,npcm4xx-sgpio"; + reg = <0x4001d100 0x100>; + interrupts = <81 3>; + clocks = <&pcc NPCM4XX_CLOCK_BUS_APB3 NPCM4XX_PWDWN_CTL7 6>; + pinctrl-0 = <&pinctrl_sgpio1_default>; + nuvoton,input-ngpios = <64>; + nuvoton,output-ngpios = <64>; + nuvoton,bus-freq = <8000000>; + label = "SGPIO1"; + status = "disabled"; + }; + }; soc-if { diff --git a/dts/arm/nuvoton/npcm4xx/npcm4xx-pinctrl.dtsi b/dts/arm/nuvoton/npcm4xx/npcm4xx-pinctrl.dtsi index 99a0c4c637193a..fee4b2167b9fc5 100644 --- a/dts/arm/nuvoton/npcm4xx/npcm4xx-pinctrl.dtsi +++ b/dts/arm/nuvoton/npcm4xx/npcm4xx-pinctrl.dtsi @@ -39,4 +39,6 @@ pinctrl_i3c5_default: i3c5_default {}; pinctrl_usbd_phy_iclk: usbd_phy_iclk {}; pinctrl_usbd_phy_xclk: usbd_phy_xclk {}; + pinctrl_sgpio0_default: sgpio0_default {}; + pinctrl_sgpio1_default: sgpio1_default {}; }; diff --git a/dts/bindings/gpio/nuvoton,npcm4xx-sgpio.yaml b/dts/bindings/gpio/nuvoton,npcm4xx-sgpio.yaml new file mode 100644 index 00000000000000..fc792080cc1e6a --- /dev/null +++ b/dts/bindings/gpio/nuvoton,npcm4xx-sgpio.yaml @@ -0,0 +1,53 @@ +# Copyright (c) 2023 Nuvoton Technology Corporation. +# SPDX-License-Identifier: Apache-2.0 + + +description: Nuvoton, NPCM4XX-SGPIO node + +compatible: "nuvoton,npcm4xx-sgpio" + +include: [base.yaml, nuvoton-pinctrl.yaml] + +properties: + reg: + required: true + + label: + required: true + + nuvoton,bus-freq: + type: int + default: 8000000 + description: + SGPIO clock frequency. + + nuvoton,input-ngpios: + type: int + required: true + + nuvoton,output-ngpios: + type: int + required: true + +child-binding: + description: GPIO child node + include: gpio-controller.yaml + properties: + label: + type: string + required: true + + ngpios: + required: true + + pin-offset: + type: int + required: true + description: Pin offset of this GPIO entry + + "#gpio-cells": + const: 2 + + gpio-cells: + - pin + - flags diff --git a/soc/arm/npcm4xx/common/reg/reg_def.h b/soc/arm/npcm4xx/common/reg/reg_def.h index fe95d5cda7bd97..7ddf282a28b8ea 100644 --- a/soc/arm/npcm4xx/common/reg/reg_def.h +++ b/soc/arm/npcm4xx/common/reg/reg_def.h @@ -180,6 +180,7 @@ struct scfg_reg { #define NPCM4XX_DEVALT_OFFSET(n) (0x010 + (n)) #define NPCM4XX_DEVALT(base, n) (*(volatile uint8_t *)(base + \ NPCM4XX_DEVALT_OFFSET(n))) +#define NPCM4XX_DEVALT6A_OFFSET 0x5A /* SCFG register fields */ #define NPCM4XX_DEVCNT_F_SPI_TRIS 6 @@ -210,6 +211,9 @@ struct scfg_reg { #define NPCM4XX_DEVALT10_CRGPIO_SELECT_SL_POWER 1 #define NPCM4XX_DEVALTCX_GPIO_PULL_EN 7 +#define NPCM4XX_DEVALT6A_SIOX1_PU_EN 2 +#define NPCM4XX_DEVALT6A_SIOX2_PU_EN 3 + #define SCFG_BASE_ADDR (0x400C3000) /* @@ -2560,4 +2564,103 @@ struct pdma_reg { __IO uint32_t REQSEL12_15; }; + +/* + * SERIAL I/O EXPANSION INTERFACE (SIOX) device registers + */ +struct sgpio_reg { + /* SIOn (n=1,2) */ + /* 0x00: I/O Expansion Data Out n Register 0 */ + volatile uint8_t XDOUT0; + /* 0x01: I/O Expansion Data Out n Register 1 */ + volatile uint8_t XDOUT1; + /* 0x02: I/O Expansion Data Out n Register 2 */ + volatile uint8_t XDOUT2; + /* 0x03: I/O Expansion Data Out n Register 3 */ + volatile uint8_t XDOUT3; + /* 0x04: I/O Expansion Data Out n Register 4 */ + volatile uint8_t XDOUT4; + /* 0x05: I/O Expansion Data Out n Register 5 */ + volatile uint8_t XDOUT5; + /* 0x06: I/O Expansion Data Out n Register 6 */ + volatile uint8_t XDOUT6; + /* 0x07: I/O Expansion Data Out n Register 7 */ + volatile uint8_t XDOUT7; + /* 0x08: I/O Expansion Data In n Register 0 */ + volatile uint8_t XDIN0; + /* 0x09: I/O Expansion Data In n Register 1 */ + volatile uint8_t XDIN1; + /* 0x0A: I/O Expansion Data In n Register 2 */ + volatile uint8_t XDIN2; + /* 0x0B: I/O Expansion Data In n Register 3 */ + volatile uint8_t XDIN3; + /* 0x0C: I/O Expansion Data In n Register 4 */ + volatile uint8_t XDIN4; + /* 0x0D: I/O Expansion Data In n Register 5 */ + volatile uint8_t XDIN5; + /* 0x0E: I/O Expansion Data In n Register 6 */ + volatile uint8_t XDIN6; + /* 0x0F: I/O Expansion Data In n Register 7 */ + volatile uint8_t XDIN7; + /* 0x10: I/O Expansion Event Configuration n Register 0 */ + volatile uint16_t XEVCFG0; + /* 0x12: I/O Expansion Event Configuration n Register 1 */ + volatile uint16_t XEVCFG1; + /* 0x14: I/O Expansion Event Configuration n Register 2 */ + volatile uint16_t XEVCFG2; + /* 0x16: I/O Expansion Event Configuration n Register 3 */ + volatile uint16_t XEVCFG3; + /* 0x18: I/O Expansion Event Configuration n Register 4 */ + volatile uint16_t XEVCFG4; + /* 0x1A: I/O Expansion Event Configuration n Register 5 */ + volatile uint16_t XEVCFG5; + /* 0x1C: I/O Expansion Event Configuration n Register 6 */ + volatile uint16_t XEVCFG6; + /* 0x1E: I/O Expansion Event Configuration n Register 7 */ + volatile uint16_t XEVCFG7; + /* 0x20: I/O Expansion Event Status n Register 0 */ + volatile uint8_t XEVSTS0; + /* 0x21: I/O Expansion Event Status n Register 1 */ + volatile uint8_t XEVSTS1; + /* 0x22: I/O Expansion Event Status n Register 2 */ + volatile uint8_t XEVSTS2; + /* 0x23: I/O Expansion Event Status n Register 3 */ + volatile uint8_t XEVSTS3; + /* 0x24: I/O Expansion Event Status n Register 4 */ + volatile uint8_t XEVSTS4; + /* 0x25: I/O Expansion Event Status n Register 5 */ + volatile uint8_t XEVSTS5; + /* 0x26: I/O Expansion Event Status n Register 6 */ + volatile uint8_t XEVSTS6; + /* 0x27: I/O Expansion Event Status n Register 7 */ + volatile uint8_t XEVSTS7; + /* 0x28: I/O Expansion Control and Status Register */ + volatile uint8_t IOXCTS; + volatile uint8_t reserved1; + /* 0x2A: I/O Expansion Configuration 1 Register */ + volatile uint8_t IOXCFG1; + /* 0x2B: I/O Expansion Configuration 2 Register */ + volatile uint8_t IOXCFG2; +}; + +#define NPCM4XX_SGPIO_PORT_PIN_NUM 8U +#define MAX_NR_HW_SGPIO 64 +#define NPCM4XX_IOXCTS_RD_MODE_MASK 0x6 +#define NPCM4XX_IOXCTS_RD_MODE_CONTINUOUS BIT(2) +#define NPCM4XX_IOXCTS_IOXIF_EN BIT(7) +#define NPCM4XX_IOXCFG1_SFT_CLK_MASK 0xF + +#define NPCM4XX_XDOUT_OFFSET(n) (0x00 + (n)) +#define NPCM4XX_XDIN_OFFSET(n) (0x08 + (n)) +#define NPCM4XX_XEVCFG_OFFSET(n) (0x10 + ((n) * 2L)) +#define NPCM4XX_XEVSTS_OFFSET(n) (0x20 + (n)) + +#define NPCM4XX_XDOUT(base, n) (*(volatile uint8_t*)(base + \ + NPCM4XX_XDOUT_OFFSET(n))) +#define NPCM4XX_XDIN(base, n) (*(volatile uint8_t*)(base + \ + NPCM4XX_XDIN_OFFSET(n))) +#define NPCM4XX_XEVCFG(base, n) (*(volatile uint16_t*)(base + \ + NPCM4XX_XEVCFG_OFFSET(n))) +#define NPCM4XX_XEVSTS(base, n) (*(volatile uint8_t*)(base + \ + NPCM4XX_XEVSTS_OFFSET(n))) #endif /* _NUVOTON_NPCM4XX_REG_DEF_H */ diff --git a/soc/arm/npcm4xx/npcm400f/sig_def_list.h b/soc/arm/npcm4xx/npcm400f/sig_def_list.h index a39d6c542699e6..48c8b597f95857 100644 --- a/soc/arm/npcm4xx/npcm400f/sig_def_list.h +++ b/soc/arm/npcm4xx/npcm400f/sig_def_list.h @@ -180,3 +180,25 @@ SIG_DEFINE(I3C6_SCL, A5, SIG_DESC_CLEAR(0x17, 3), SIG_DESC_SET(0x17, 2)) /* DEVALT7.4=0, DEVALT5.4=0, DEVPD3.4=1 */ SIG_DEFINE(I3C6_SDA, B5, SIG_DESC_CLEAR(0x17, 4), SIG_DESC_CLEAR(0x15, 4), SIG_DESC_SET(0x7B, 4)) #endif + +#if DT_NODE_HAS_STATUS(DT_NODELABEL(sgpio0), okay) && CONFIG_SGPIO_NPCM4XX +/* DEVALT6A.0=1 */ +SIG_DEFINE(IOX1_LDSH, C11, SIG_DESC_SET(0x6A, 0)) +/* DEVALT6A.0=1 */ +SIG_DEFINE(IOX1_DOUT, B11, SIG_DESC_SET(0x6A, 0)) +/* DEVALT34.7=0, DEVALT6A.0=1 */ +SIG_DEFINE(IOX1_DIN, B7, SIG_DESC_CLEAR(0x34, 7), SIG_DESC_SET(0x6A, 0)) +/* DEVALT34.7=0, DEVALT6A.0=1 */ +SIG_DEFINE(IOX1_SCLK, A7, SIG_DESC_CLEAR(0x34, 7), SIG_DESC_SET(0x6A, 0)) +#endif + +#if DT_NODE_HAS_STATUS(DT_NODELABEL(sgpio1), okay) && CONFIG_SGPIO_NPCM4XX +/* DEVALT4.6=0, DEVALT6A.1=1 */ +SIG_DEFINE(IOX2_LDSH, C5, SIG_DESC_CLEAR(0x14, 6), SIG_DESC_SET(0x6A, 1)) +/* DEVALT3.4=0, DEVALT6A.1=1*/ +SIG_DEFINE(IOX2_DOUT, D5, SIG_DESC_CLEAR(0x13, 4), SIG_DESC_SET(0x6A, 1)) +/* DEVALT4.7=0, DEVALT6A.1=1 */ +SIG_DEFINE(IOX2_DIN, D4, SIG_DESC_CLEAR(0x14, 7), SIG_DESC_SET(0x6A, 1)) +/* DEVALT3.5=0, DEVALT6A.1=1 */ +SIG_DEFINE(IOX2_SCLK, C4, SIG_DESC_CLEAR(0x13, 5), SIG_DESC_SET(0x6A, 1)) +#endif