From 5253eb1692500fb7aa01f47b454742023699f034 Mon Sep 17 00:00:00 2001 From: Chekhov Ma Date: Mon, 2 Sep 2024 14:00:10 +0800 Subject: [PATCH] drivers: gpio: extend pca_series driver to pca953x and pca955x This commit extends pca_series gpio driver to devices pca9538, pca9539, pca9554 and pca9555. Signed-off-by: Chekhov Ma --- drivers/gpio/Kconfig.pca_series | 4 +- drivers/gpio/gpio_pca_series.c | 479 ++++++++++++++++++++++++++++- dts/bindings/gpio/nxp,pca9538.yaml | 10 + dts/bindings/gpio/nxp,pca9539.yaml | 10 + dts/bindings/gpio/nxp,pca9554.yaml | 10 + dts/bindings/gpio/nxp,pca9555.yaml | 10 + 6 files changed, 519 insertions(+), 4 deletions(-) create mode 100644 dts/bindings/gpio/nxp,pca9538.yaml create mode 100644 dts/bindings/gpio/nxp,pca9539.yaml create mode 100644 dts/bindings/gpio/nxp,pca9554.yaml create mode 100644 dts/bindings/gpio/nxp,pca9555.yaml diff --git a/drivers/gpio/Kconfig.pca_series b/drivers/gpio/Kconfig.pca_series index bf2a369f0a0..95387ee3892 100644 --- a/drivers/gpio/Kconfig.pca_series +++ b/drivers/gpio/Kconfig.pca_series @@ -6,7 +6,9 @@ menuconfig GPIO_PCA_SERIES bool "PCA_SERIES I2C-based GPIO chip" default y - depends on DT_HAS_NXP_PCAL6524_ENABLED || DT_HAS_NXP_PCAL6534_ENABLED + depends on DT_HAS_NXP_PCA9538_ENABLED || DT_HAS_NXP_PCA9539_ENABLED || \ + DT_HAS_NXP_PCA9554_ENABLED || DT_HAS_NXP_PCA9555_ENABLED || \ + DT_HAS_NXP_PCAL6524_ENABLED || DT_HAS_NXP_PCAL6534_ENABLED depends on I2C help Enable driver for PCA_SERIES I2C-based GPIO chip. diff --git a/drivers/gpio/gpio_pca_series.c b/drivers/gpio/gpio_pca_series.c index 741b9db2961..23f9505b0c5 100644 --- a/drivers/gpio/gpio_pca_series.c +++ b/drivers/gpio/gpio_pca_series.c @@ -43,6 +43,10 @@ LOG_MODULE_REGISTER(gpio_pca_series); * @brief part number definition. */ enum gpio_pca_series_part_no { + PCA_PART_NO_PCA9538, + PCA_PART_NO_PCA9539, + PCA_PART_NO_PCA9554, + PCA_PART_NO_PCA9555, PCA_PART_NO_PCAL6524, PCA_PART_NO_PCAL6534, }; @@ -53,6 +57,10 @@ enum gpio_pca_series_part_no { * @note must be consistent in order with @ref enum gpio_pca_series_part_no */ const char *const gpio_pca_series_part_name[] = { + "pca9538", + "pca9539", + "pca9554", + "pca9555", "pcal6524", "pcal6534", }; @@ -839,6 +847,12 @@ void gpio_pca_series_cache_test(const struct device *dev) * { */ +#ifdef CONFIG_GPIO_PCA_SERIES_INTERRUPT +/** Forward declaration */ +static inline void gpio_pca_series_interrupt_handler_standard( + const struct device *dev, gpio_port_value_t *input_value); +#endif /* CONFIG_GPIO_PCA_SERIES_INTERRUPT */ + /** * @brief configure gpio port. * @@ -990,6 +1004,52 @@ out: return ret; } +/** + * @brief read gpio port + * + * @note read input_port register will clear interrupt masks + * on supported devices. This API is used for part no + * without PCA_HAS_INT_EXTEND capability. + * + * @param dev + * @param value + * @return int 0 if success + * -EWOULDBLOCK if called from ISR context + */ +static int gpio_pca_series_port_read_standard( + const struct device *dev, gpio_port_value_t *value) +{ + struct gpio_pca_series_data *data = dev->data; + uint32_t input_data; + int ret = 0; + + /* Can't do I2C bus operations from an ISR */ + if (k_is_in_isr()) { + return -EWOULDBLOCK; + } + + LOG_DBG("dev %s standard_read", dev->name); + +#ifdef CONFIG_GPIO_PCA_SERIES_INTERRUPT + gpio_pca_series_interrupt_handler_standard(dev, value); + ARG_UNUSED(data); +#else + k_sem_take(&data->lock, K_FOREVER); + + /* Read Input Register */ + ret = gpio_pca_series_reg_read(dev, + PCA_REG_TYPE_1B_INPUT_PORT, (uint8_t *)value); + if (ret) { + LOG_ERR("port read error %d", ret); + } + k_sem_give(&data->lock); +#endif /* CONFIG_GPIO_PCA_SERIES_INTERRUPT */ + + LOG_DBG("dev %s standard_read return %d result 0x%8.8x", + dev->name, ret, input_data); + return ret; +} + /** * @brief read gpio port * @@ -1114,6 +1174,133 @@ static int gpio_pca_series_port_toggle_bits(const struct device *dev, gpio_port_ } #ifdef CONFIG_GPIO_PCA_SERIES_INTERRUPT +/** + * @brief Configure interrupt for device with software-compared interrupt edge + * + * @note This version is used by devices that does not have interrupt edge config + * (aka PCA_HAS_INT_EXTEND), and relies on software to check the edge. + * This applies to all pca(l)9xxx and pcal64xxa devices. + * This will also configure interrupt mask register if the device has it. + * + * @param dev + * @param pin + * @param mode + * @param trig + * @return int + */ +static int gpio_pca_series_pin_interrupt_configure_standard( + const struct device *dev, gpio_pin_t pin, enum gpio_int_mode mode, + enum gpio_int_trig trig) +{ + const struct gpio_pca_series_config *cfg = dev->config; + struct gpio_pca_series_data *data = dev->data; + uint32_t int_rise, int_fall; + uint32_t int_mask, input_latch; + int ret = 0; + + if (cfg->gpio_int.port == NULL) { + return -ENOTSUP; + } + /* Device does not support level-triggered interrupts. */ + if (mode == GPIO_INT_MODE_LEVEL) { + return -ENOTSUP; + } + if (k_is_in_isr()) { + return -EWOULDBLOCK; + } + + k_sem_take(&data->lock, K_FOREVER); + + /** + * TODO: Only write 1 byte. + * + * The config api only configures 1 pin, so only need to write the + * modified byte. Need to create new register & cache api to provide + * single byte access. + * This applies to: pin_configure, pin_interrupt_configure + */ + + /** get current interrupt config */ +#ifdef CONFIG_GPIO_PCA_SERIES_CACHE_ALL + /** read from cache even if this register is not present on device */ + ret = gpio_pca_series_reg_cache_read(dev, PCA_REG_TYPE_1B_INTERRUPT_RISE, + (uint8_t *)&int_rise); + if (ret) { + goto out; + } + /** read from cache even if this register is not present on device */ + ret = gpio_pca_series_reg_cache_read(dev, PCA_REG_TYPE_1B_INTERRUPT_FALL, + (uint8_t *)&int_fall); + if (ret) { + goto out; + } +#else + int_rise = gpio_pca_series_reg_cache_mini_get(dev)->int_rise; + int_fall = gpio_pca_series_reg_cache_mini_get(dev)->int_fall; +#endif /* CONFIG_GPIO_PCA_SERIES_CACHE_ALL */ + + if (mode == GPIO_INT_MODE_DISABLED) { + int_fall &= ~BIT(pin); + int_rise &= ~BIT(pin); + } else { + if (trig == GPIO_INT_TRIG_BOTH) { + int_fall |= BIT(pin); + int_rise |= BIT(pin); + } else if (trig == GPIO_INT_TRIG_LOW) { + int_fall |= BIT(pin); + int_rise &= ~BIT(pin); + } else if (trig == GPIO_INT_TRIG_HIGH) { + int_fall &= ~BIT(pin); + int_rise |= BIT(pin); + } + } + + int_mask = int_fall | int_rise; + input_latch = ~int_mask; + +#ifdef CONFIG_GPIO_PCA_SERIES_CACHE_ALL + /** read from cache even if this register is not present on device */ + ret = gpio_pca_series_reg_cache_update(dev, PCA_REG_TYPE_1B_INTERRUPT_RISE, + (uint8_t *)&int_rise); + if (ret) { + goto out; + } + /** read from cache even if this register is not present on device */ + ret = gpio_pca_series_reg_cache_update(dev, PCA_REG_TYPE_1B_INTERRUPT_FALL, + (uint8_t *)&int_fall); + if (ret) { + goto out; + } +#else + gpio_pca_series_reg_cache_mini_get(dev)->int_rise = int_rise; + gpio_pca_series_reg_cache_mini_get(dev)->int_fall = int_fall; +#endif /* CONFIG_GPIO_PCA_SERIES_CACHE_ALL */ + + /** enable latch if available, so we do not lost interrupt */ + if (cfg->part_cfg->flags & PCA_HAS_LATCH) { + ret = gpio_pca_series_reg_write(dev, PCA_REG_TYPE_1B_INPUT_LATCH, + (uint8_t *)&input_latch); + if (ret) { + goto out; + } + } + /** update interrupt mask register if available */ + if (cfg->part_cfg->flags & PCA_HAS_INT_MASK) { + ret = gpio_pca_series_reg_write(dev, PCA_REG_TYPE_1B_INTERRUPT_MASK, + (uint8_t *)&int_mask); + if (ret) { + goto out; + } + } + +out: + k_sem_give(&data->lock); + + if (ret) { + LOG_ERR("int config(s) error %d", ret); + } + return ret; +} /** * @brief Configure interrupt for device with hardware-selected interrupt edge @@ -1241,6 +1428,79 @@ static int gpio_pca_series_manage_callback(const struct device *dev, return gpio_manage_callback(&data->callbacks, callback, set); } +static void gpio_pca_series_interrupt_handler_standard(const struct device *dev, + gpio_port_value_t *input_value) +{ + struct gpio_pca_series_data *data = dev->data; + int ret = 0; + uint32_t input_old, int_rise, int_fall; + uint32_t input; + uint32_t transitioned_pins; + uint32_t int_status = 0; + + k_sem_take(&data->lock, K_FOREVER); + +#ifdef CONFIG_GPIO_PCA_SERIES_CACHE_ALL + /** read from cache even if this register is not present on device */ + ret = gpio_pca_series_reg_cache_read(dev, PCA_REG_TYPE_1B_INPUT_HISTORY, + (uint8_t *)&input_old); + if (ret) { + goto out; + } + /** read from cache even if this register is not present on device */ + ret = gpio_pca_series_reg_cache_read(dev, PCA_REG_TYPE_1B_INTERRUPT_RISE, + (uint8_t *)&int_rise); + if (ret) { + goto out; + } + /** read from cache even if this register is not present on device */ + ret = gpio_pca_series_reg_cache_read(dev, PCA_REG_TYPE_1B_INTERRUPT_FALL, + (uint8_t *)&int_fall); + if (ret) { + goto out; + } +#else + input_old = gpio_pca_series_reg_cache_mini_get(dev)->input_old; + int_rise = gpio_pca_series_reg_cache_mini_get(dev)->int_rise; + int_fall = gpio_pca_series_reg_cache_mini_get(dev)->int_fall; +#endif /* CONFIG_GPIO_PCA_SERIES_CACHE_ALL */ + + /** check if any interrupt enabled */ + if ((!int_rise) && (!int_fall)) { + goto out; + } + + /** read current input value, and clear status if reg is present */ + ret = gpio_pca_series_reg_read(dev, PCA_REG_TYPE_1B_INPUT_PORT, (uint8_t *)&input); + if (ret) { + goto out; + } + /** compare input to input_old to get transitioned_pins */ + transitioned_pins = input_old ^ input; + + /** Mask gpio transactions with rising/falling edge interrupt config */ + int_status = (int_rise & transitioned_pins & input) + | (int_fall & transitioned_pins & (~input)); + + /** update current input to cache */ +#ifdef CONFIG_GPIO_PCA_SERIES_CACHE_ALL + ret = gpio_pca_series_reg_cache_update(dev, PCA_REG_TYPE_1B_INPUT_HISTORY, + (uint8_t *)&input); +#else + gpio_pca_series_reg_cache_mini_get(dev)->input_old = input; +#endif /* CONFIG_GPIO_PCA_SERIES_CACHE_ALL */ + +out: + k_sem_give(&data->lock); + + if (input_value) { + *input_value = input; + } + if ((ret == 0) && (int_status)) { + gpio_fire_callbacks(&data->callbacks, dev, int_status); + } +} + static void gpio_pca_series_interrupt_handler_extended(const struct device *dev) { const struct gpio_pca_series_config *cfg = dev->config; @@ -1292,6 +1552,15 @@ out: } } +static void gpio_pca_series_interrupt_worker_standard(struct k_work *work) +{ + struct gpio_pca_series_data *data = + CONTAINER_OF(work, struct gpio_pca_series_data, int_work); + const struct device *dev = data->self; + + gpio_pca_series_interrupt_handler_standard(dev, NULL); +} + static void gpio_pca_series_interrupt_worker_extended(struct k_work *work) { struct gpio_pca_series_data *data = @@ -1322,6 +1591,19 @@ static void gpio_pca_series_gpio_int_handler(const struct device *dev, * gpio_pca_zephyr_gpio_api */ +static const struct gpio_driver_api gpio_pca_series_api_funcs_standard = { + .pin_configure = gpio_pca_series_pin_configure, + .port_get_raw = gpio_pca_series_port_read_standard, + .port_set_masked_raw = gpio_pca_series_port_set_masked, + .port_set_bits_raw = gpio_pca_series_port_set_bits, + .port_clear_bits_raw = gpio_pca_series_port_clear_bits, + .port_toggle_bits = gpio_pca_series_port_toggle_bits, +#ifdef CONFIG_GPIO_PCA_SERIES_INTERRUPT + .pin_interrupt_configure = gpio_pca_series_pin_interrupt_configure_standard, + .manage_callback = gpio_pca_series_manage_callback, +#endif +}; + static const struct gpio_driver_api gpio_pca_series_api_funcs_extended = { .pin_configure = gpio_pca_series_pin_configure, .port_get_raw = gpio_pca_series_port_read_extended, /* special version used */ @@ -1381,8 +1663,14 @@ static int gpio_pca_series_init(const struct device *dev) /** save dev pointer */ data->self = dev; - /** init work obj */ - k_work_init(&data->int_work, gpio_pca_series_interrupt_worker_extended); + /** check the flags and init work obj */ + const uint8_t check_flags = (PCA_HAS_LATCH | PCA_HAS_INT_MASK | PCA_HAS_INT_EXTEND); + + if ((cfg->part_cfg->flags & check_flags) == check_flags) { + k_work_init(&data->int_work, gpio_pca_series_interrupt_worker_extended); + } else { + k_work_init(&data->int_work, gpio_pca_series_interrupt_worker_standard); + } /** Interrupt pin connected, enable interrupt */ if (cfg->gpio_int.port != NULL) { @@ -1433,7 +1721,7 @@ out_bus: #define GPIO_PCA_GET_API_BY_PART_NO(part_no) ( \ (part_no == PCA_PART_NO_PCAL6524) ? &gpio_pca_series_api_funcs_extended : \ (part_no == PCA_PART_NO_PCAL6534) ? &gpio_pca_series_api_funcs_extended : \ - NULL \ + &gpio_pca_series_api_funcs_standard \ ) #define GPIO_PCA_GET_PORT_NO_CFG_BY_PART_NO(part_no) (GPIO_PCA_PORT_NO_##part_no) #define GPIO_PCA_GET_PART_FLAG_BY_PART_NO(part_no) (GPIO_PCA_FLAG_##part_no) @@ -1539,6 +1827,175 @@ out_bus: #endif /* CONFIG_GPIO_PCA_SERIES_INTERRUPT */ #endif /* CONFIG_GPIO_PCA_SERIES_CACHE_ALL */ +/** + * @brief implement pca953x driver + * + * @note flags = 0U; + * + * api set : standard + * ngpios : 8, 16 + * part_no : pca9534 pca9538 pca9535 pca9539 + */ +#define GPIO_PCA_SERIES_FLAG_TYPE_0 (0U) + +#ifdef CONFIG_GPIO_PCA_SERIES_CACHE_ALL +/** + * cache map for flag = 0U + */ +static const uint8_t gpio_pca_series_cache_map_pca953x[] = { + PCA_REG_INVALID, /** input_port if not PCA_HAS_OUT_CONFIG, non-cacheable */ + 0x00, /** output_port */ +/* 0x02, polarity_inversion (unused, omitted) */ + 0x01, /** configuration */ + PCA_REG_INVALID, /** 2b_output_drive_strength if PCA_HAS_LATCH*/ + PCA_REG_INVALID, /** input_latch if PCA_HAS_LATCH*/ + PCA_REG_INVALID, /** pull_enable if PCA_HAS_PULL */ + PCA_REG_INVALID, /** pull_select if PCA_HAS_PULL */ + PCA_REG_INVALID, /** input_status if PCA_HAS_OUT_CONFIG, non-cacheable */ + PCA_REG_INVALID, /** output_config if PCA_HAS_OUT_CONFIG */ +#ifdef CONFIG_GPIO_PCA_SERIES_INTERRUPT + PCA_REG_INVALID, /** interrupt_mask if PCA_HAS_INT_MASK, + * non-cacheable if not PCA_HAS_INT_EXTEND + */ + PCA_REG_INVALID, /** int_status if PCA_HAS_INT_MASK, non-cacheable */ + PCA_REG_INVALID, /** 2b_interrupt_edge if PCA_HAS_INT_EXTEND */ + PCA_REG_INVALID, /** interrupt_clear if PCA_HAS_INT_EXTEND, non-cacheable */ +# ifdef CONFIG_GPIO_PCA_SERIES_CACHE_ALL + 0x02, /** 1b_input_history if PCA_HAS_LATCH and not PCA_HAS_INT_EXTEND */ + 0x03, /** 1b_interrupt_rise if PCA_HAS_LATCH and not PCA_HAS_INT_EXTEND */ + 0x04, /** 1b_interrupt_fall if PCA_HAS_LATCH and not PCA_HAS_INT_EXTEND */ +# endif /* CONFIG_GPIO_PCA_SERIES_CACHE_ALL */ +#endif /* CONFIG_GPIO_PCA_SERIES_INTERRUPT */ +}; +#endif /* CONFIG_GPIO_PCA_SERIES_CACHE_ALL */ + +static const uint8_t gpio_pca_series_reg_pca9538[] = { + 0x00, /** input_port if not PCA_HAS_OUT_CONFIG, non-cacheable */ + 0x01, /** output_port */ +/* 0x02, polarity_inversion (unused, omitted) */ + 0x03, /** configuration */ + PCA_REG_INVALID, /** 2b_output_drive_strength if PCA_HAS_LATCH*/ + PCA_REG_INVALID, /** input_latch if PCA_HAS_LATCH*/ + PCA_REG_INVALID, /** pull_enable if PCA_HAS_PULL */ + PCA_REG_INVALID, /** pull_select if PCA_HAS_PULL */ + PCA_REG_INVALID, /** input_status if PCA_HAS_OUT_CONFIG, non-cacheable */ + PCA_REG_INVALID, /** output_config if PCA_HAS_OUT_CONFIG */ +#ifdef CONFIG_GPIO_PCA_SERIES_INTERRUPT + PCA_REG_INVALID, /** interrupt_mask if PCA_HAS_INT_MASK, + * non-cacheable if not PCA_HAS_INT_EXTEND + */ + PCA_REG_INVALID, /** int_status if PCA_HAS_INT_MASK */ + PCA_REG_INVALID, /** 2b_interrupt_edge if PCA_HAS_INT_EXTEND */ + PCA_REG_INVALID, /** interrupt_clear if PCA_HAS_INT_EXTEND, non-cacheable */ +# ifdef CONFIG_GPIO_PCA_SERIES_CACHE_ALL + PCA_REG_INVALID, /** 1b_input_history if PCA_HAS_LATCH and not PCA_HAS_INT_EXTEND */ + PCA_REG_INVALID, /** 1b_interrupt_rise if PCA_HAS_LATCH and not PCA_HAS_INT_EXTEND */ + PCA_REG_INVALID, /** 1b_interrupt_fall if PCA_HAS_LATCH and not PCA_HAS_INT_EXTEND */ +# endif /* CONFIG_GPIO_PCA_SERIES_CACHE_ALL */ +#endif /* CONFIG_GPIO_PCA_SERIES_INTERRUPT */ +}; + +#define GPIO_PCA_PORT_NO_PCA_PART_NO_PCA9538 (1U) +#define GPIO_PCA_FLAG_PCA_PART_NO_PCA9538 GPIO_PCA_SERIES_FLAG_TYPE_0 +#define GPIO_PCA_PART_CFG_PCA_PART_NO_PCA9538 (&gpio_pca_series_part_cfg_pca9538) + +const struct gpio_pca_series_part_config gpio_pca_series_part_cfg_pca9538 = { + .port_no = GPIO_PCA_PORT_NO_PCA_PART_NO_PCA9538, + .flags = GPIO_PCA_FLAG_PCA_PART_NO_PCA9538, + .regs = gpio_pca_series_reg_pca9538, +#ifdef CONFIG_GPIO_PCA_SERIES_CACHE_ALL +# ifdef GPIO_NXP_PCA_SERIES_DEBUG + .cache_size = GPIO_PCA_GET_CACHE_SIZE_BY_PART_NO(PCA_PART_NO_PCA9538), +# endif /* GPIO_NXP_PCA_SERIES_DEBUG */ + .cache_map = gpio_pca_series_cache_map_pca953x, +#endif /* CONFIG_GPIO_PCA_SERIES_CACHE_ALL */ +}; + +/** + * pca9555 share the same register layout with pca9539, with + * RESET pin repurposed to another address strapping pin. + * no difference from driver perspective. + */ + +#define GPIO_PCA_PORT_NO_PCA_PART_NO_PCA9554 GPIO_PCA_PORT_NO_PCA_PART_NO_PCA9538 +#define GPIO_PCA_FLAG_PCA_PART_NO_PCA9554 GPIO_PCA_FLAG_PCA_PART_NO_PCA9538 +#define GPIO_PCA_PART_CFG_PCA_PART_NO_PCA9554 (&gpio_pca_series_part_cfg_pca9554) + +const struct gpio_pca_series_part_config gpio_pca_series_part_cfg_pca9554 = { + .port_no = GPIO_PCA_PORT_NO_PCA_PART_NO_PCA9554, + .flags = GPIO_PCA_FLAG_PCA_PART_NO_PCA9554, + .regs = gpio_pca_series_reg_pca9538, +#ifdef CONFIG_GPIO_PCA_SERIES_CACHE_ALL +# ifdef GPIO_NXP_PCA_SERIES_DEBUG + .cache_size = GPIO_PCA_GET_CACHE_SIZE_BY_PART_NO(PCA_PART_NO_PCA9554), +# endif /* GPIO_NXP_PCA_SERIES_DEBUG */ + .cache_map = gpio_pca_series_cache_map_pca953x, +#endif /* CONFIG_GPIO_PCA_SERIES_CACHE_ALL */ +}; + +static const uint8_t gpio_pca_series_reg_pca9539[] = { + 0x00, /** input_port if not PCA_HAS_OUT_CONFIG, non-cacheable */ + 0x02, /** output_port */ +/* 0x04, polarity_inversion (unused, omitted) */ + 0x06, /** configuration */ + PCA_REG_INVALID, /** 2b_output_drive_strength if PCA_HAS_LATCH*/ + PCA_REG_INVALID, /** input_latch if PCA_HAS_LATCH*/ + PCA_REG_INVALID, /** pull_enable if PCA_HAS_PULL */ + PCA_REG_INVALID, /** pull_select if PCA_HAS_PULL */ + PCA_REG_INVALID, /** input_status if PCA_HAS_OUT_CONFIG, non-cacheable */ + PCA_REG_INVALID, /** output_config if PCA_HAS_OUT_CONFIG */ +#ifdef CONFIG_GPIO_PCA_SERIES_INTERRUPT + PCA_REG_INVALID, /** interrupt_mask if PCA_HAS_INT_MASK, + * non-cacheable if not PCA_HAS_INT_EXTEND + */ + PCA_REG_INVALID, /** int_status if PCA_HAS_INT_MASK */ + PCA_REG_INVALID, /** 2b_interrupt_edge if PCA_HAS_INT_EXTEND */ + PCA_REG_INVALID, /** interrupt_clear if PCA_HAS_INT_EXTEND, non-cacheable */ +# ifdef CONFIG_GPIO_PCA_SERIES_CACHE_ALL + PCA_REG_INVALID, /** 1b_input_history if PCA_HAS_LATCH and not PCA_HAS_INT_EXTEND */ + PCA_REG_INVALID, /** 1b_interrupt_rise if PCA_HAS_LATCH and not PCA_HAS_INT_EXTEND */ + PCA_REG_INVALID, /** 1b_interrupt_fall if PCA_HAS_LATCH and not PCA_HAS_INT_EXTEND */ +# endif /* CONFIG_GPIO_PCA_SERIES_CACHE_ALL */ +#endif /* CONFIG_GPIO_PCA_SERIES_INTERRUPT */ +}; + +#define GPIO_PCA_PORT_NO_PCA_PART_NO_PCA9539 (2U) +#define GPIO_PCA_FLAG_PCA_PART_NO_PCA9539 GPIO_PCA_SERIES_FLAG_TYPE_0 +#define GPIO_PCA_PART_CFG_PCA_PART_NO_PCA9539 (&gpio_pca_series_part_cfg_pca9539) + +const struct gpio_pca_series_part_config gpio_pca_series_part_cfg_pca9539 = { + .port_no = GPIO_PCA_PORT_NO_PCA_PART_NO_PCA9539, + .flags = GPIO_PCA_FLAG_PCA_PART_NO_PCA9539, + .regs = gpio_pca_series_reg_pca9539, +#ifdef CONFIG_GPIO_PCA_SERIES_CACHE_ALL +# ifdef GPIO_NXP_PCA_SERIES_DEBUG + .cache_size = GPIO_PCA_GET_CACHE_SIZE_BY_PART_NO(PCA_PART_NO_PCA9539), +# endif /* GPIO_NXP_PCA_SERIES_DEBUG */ + .cache_map = gpio_pca_series_cache_map_pca953x, +#endif /* CONFIG_GPIO_PCA_SERIES_CACHE_ALL */ +}; + +/** + * pca9555 share the same register layout with pca9539, with + * RESET pin repurposed to another address strapping pin. + * no difference from driver perspective. + */ + +#define GPIO_PCA_PORT_NO_PCA_PART_NO_PCA9555 GPIO_PCA_PORT_NO_PCA_PART_NO_PCA9539 +#define GPIO_PCA_FLAG_PCA_PART_NO_PCA9555 GPIO_PCA_FLAG_PCA_PART_NO_PCA9539 +#define GPIO_PCA_PART_CFG_PCA_PART_NO_PCA9555 (&gpio_pca_series_part_cfg_pca9555) + +const struct gpio_pca_series_part_config gpio_pca_series_part_cfg_pca9555 = { + .port_no = GPIO_PCA_PORT_NO_PCA_PART_NO_PCA9555, + .flags = GPIO_PCA_FLAG_PCA_PART_NO_PCA9555, + .regs = gpio_pca_series_reg_pca9539, +#ifdef CONFIG_GPIO_PCA_SERIES_CACHE_ALL +# ifdef GPIO_NXP_PCA_SERIES_DEBUG + .cache_size = GPIO_PCA_GET_CACHE_SIZE_BY_PART_NO(PCA_PART_NO_PCA9555), +# endif /* GPIO_NXP_PCA_SERIES_DEBUG */ + .cache_map = gpio_pca_series_cache_map_pca953x, +#endif /* CONFIG_GPIO_PCA_SERIES_CACHE_ALL */ +}; /** * @brief implement pcal65xx driver @@ -1707,6 +2164,22 @@ const struct gpio_pca_series_part_config gpio_pca_series_part_cfg_pcal6534 = { GPIO_PCA_GET_API_BY_PART_NO(part_no)); +#undef DT_DRV_COMPAT +#define DT_DRV_COMPAT nxp_pca9538 +DT_INST_FOREACH_STATUS_OKAY_VARGS(GPIO_PCA_SERIES_DEVICE_INSTANCE, PCA_PART_NO_PCA9538) + +#undef DT_DRV_COMPAT +#define DT_DRV_COMPAT nxp_pca9539 +DT_INST_FOREACH_STATUS_OKAY_VARGS(GPIO_PCA_SERIES_DEVICE_INSTANCE, PCA_PART_NO_PCA9539) + +#undef DT_DRV_COMPAT +#define DT_DRV_COMPAT nxp_pca9554 +DT_INST_FOREACH_STATUS_OKAY_VARGS(GPIO_PCA_SERIES_DEVICE_INSTANCE, PCA_PART_NO_PCA9554) + +#undef DT_DRV_COMPAT +#define DT_DRV_COMPAT nxp_pca9555 +DT_INST_FOREACH_STATUS_OKAY_VARGS(GPIO_PCA_SERIES_DEVICE_INSTANCE, PCA_PART_NO_PCA9555) + #undef DT_DRV_COMPAT #define DT_DRV_COMPAT nxp_pcal6524 DT_INST_FOREACH_STATUS_OKAY_VARGS(GPIO_PCA_SERIES_DEVICE_INSTANCE, PCA_PART_NO_PCAL6524) diff --git a/dts/bindings/gpio/nxp,pca9538.yaml b/dts/bindings/gpio/nxp,pca9538.yaml new file mode 100644 index 00000000000..5933b2841d3 --- /dev/null +++ b/dts/bindings/gpio/nxp,pca9538.yaml @@ -0,0 +1,10 @@ +# Copyright 2024 NXP +# SPDX-License-Identifier: Apache-2.0 +description: NXP PCA9538 I2C GPIO expander node +compatible: nxp,pca9538 +include: nxp,pca_series.yaml + +properties: + ngpios: + required: true + const: 8 diff --git a/dts/bindings/gpio/nxp,pca9539.yaml b/dts/bindings/gpio/nxp,pca9539.yaml new file mode 100644 index 00000000000..42101ba30f0 --- /dev/null +++ b/dts/bindings/gpio/nxp,pca9539.yaml @@ -0,0 +1,10 @@ +# Copyright 2024 NXP +# SPDX-License-Identifier: Apache-2.0 +description: NXP PCA9539 I2C GPIO expander node +compatible: nxp,pca9539 +include: nxp,pca_series.yaml + +properties: + ngpios: + required: true + const: 16 diff --git a/dts/bindings/gpio/nxp,pca9554.yaml b/dts/bindings/gpio/nxp,pca9554.yaml new file mode 100644 index 00000000000..aa4ff0c65dc --- /dev/null +++ b/dts/bindings/gpio/nxp,pca9554.yaml @@ -0,0 +1,10 @@ +# Copyright 2024 NXP +# SPDX-License-Identifier: Apache-2.0 +description: NXP PCA9554 I2C GPIO expander node +compatible: nxp,pca9554 +include: nxp,pca_series.yaml + +properties: + ngpios: + required: true + const: 8 diff --git a/dts/bindings/gpio/nxp,pca9555.yaml b/dts/bindings/gpio/nxp,pca9555.yaml new file mode 100644 index 00000000000..5902b2bacf7 --- /dev/null +++ b/dts/bindings/gpio/nxp,pca9555.yaml @@ -0,0 +1,10 @@ +# Copyright 2024 NXP +# SPDX-License-Identifier: Apache-2.0 +description: NXP PCA9555 I2C GPIO expander node +compatible: nxp,pca9555 +include: nxp,pca_series.yaml + +properties: + ngpios: + required: true + const: 16