From 5b25fbc3dd462722a5256a450bfdd64e60416785 Mon Sep 17 00:00:00 2001 From: Chekhov Ma Date: Wed, 18 Sep 2024 13:56:50 +0800 Subject: [PATCH 1/6] drivers: gpio: retire pca95xx driver pca95xx driver can be replaced by pca_series driver, which covers a larger number of devices. Signed-off-by: Chekhov Ma --- .../mec15xxevb_assy6853.dts | 3 +- .../mec172xevb_assy6906.dts | 3 +- drivers/gpio/CMakeLists.txt | 1 - drivers/gpio/Kconfig | 1 - drivers/gpio/Kconfig.pca95xx | 27 - drivers/gpio/gpio_pca95xx.c | 877 ------------------ dts/bindings/gpio/nxp,pca95xx.yaml | 28 - .../mec15xxevb_assy6853/i2c_api/src/main.c | 2 +- .../mec15xxevb_assy6853/i2c_api/testcase.yaml | 2 +- .../mec172xevb_assy6906/i2c_api/src/main.c | 2 +- .../mec172xevb_assy6906/i2c_api/testcase.yaml | 2 +- tests/drivers/build_all/gpio/app.overlay | 6 +- tests/drivers/build_all/gpio/prj.conf | 1 - 13 files changed, 11 insertions(+), 944 deletions(-) delete mode 100644 drivers/gpio/Kconfig.pca95xx delete mode 100644 drivers/gpio/gpio_pca95xx.c delete mode 100644 dts/bindings/gpio/nxp,pca95xx.yaml diff --git a/boards/microchip/mec15xxevb_assy6853/mec15xxevb_assy6853.dts b/boards/microchip/mec15xxevb_assy6853/mec15xxevb_assy6853.dts index 11daa0efbe3736..2ba2c9e1d94fbd 100644 --- a/boards/microchip/mec15xxevb_assy6853/mec15xxevb_assy6853.dts +++ b/boards/microchip/mec15xxevb_assy6853/mec15xxevb_assy6853.dts @@ -96,7 +96,7 @@ pinctrl-names = "default"; pca9555@26 { - compatible = "nxp,pca95xx"; + compatible = "nxp,pca9555"; /* Depends on JP53 for device address. * Pin 1-2 = A0, pin 3-4 = A1, pin 5-6 = A2. @@ -106,6 +106,7 @@ * resulting in device address 0x26. */ reg = <0x26>; + ngpios = <16>; gpio-controller; #gpio-cells = <2>; diff --git a/boards/microchip/mec172xevb_assy6906/mec172xevb_assy6906.dts b/boards/microchip/mec172xevb_assy6906/mec172xevb_assy6906.dts index c0427d03d3deda..43584ff9a30b87 100644 --- a/boards/microchip/mec172xevb_assy6906/mec172xevb_assy6906.dts +++ b/boards/microchip/mec172xevb_assy6906/mec172xevb_assy6906.dts @@ -150,7 +150,7 @@ pinctrl-names = "default"; pca9555@26 { - compatible = "nxp,pca95xx"; + compatible = "nxp,pca9555"; /* Depends on JP53 for device address. * Pin 1-2 = A0, pin 3-4 = A1, pin 5-6 = A2. @@ -160,6 +160,7 @@ * resulting in device address 0x26. */ reg = <0x26>; + ngpios = <16>; gpio-controller; #gpio-cells = <2>; diff --git a/drivers/gpio/CMakeLists.txt b/drivers/gpio/CMakeLists.txt index 10b72060d64b21..82cdbc1cae46cd 100644 --- a/drivers/gpio/CMakeLists.txt +++ b/drivers/gpio/CMakeLists.txt @@ -64,7 +64,6 @@ zephyr_library_sources_ifdef(CONFIG_GPIO_NUMAKER gpio_numaker.c) zephyr_library_sources_ifdef(CONFIG_GPIO_NUMICRO gpio_numicro.c) zephyr_library_sources_ifdef(CONFIG_GPIO_NXP_S32 gpio_nxp_s32.c) zephyr_library_sources_ifdef(CONFIG_GPIO_PCA953X gpio_pca953x.c) -zephyr_library_sources_ifdef(CONFIG_GPIO_PCA95XX gpio_pca95xx.c) zephyr_library_sources_ifdef(CONFIG_GPIO_PCAL64XXA gpio_pcal64xxa.c) zephyr_library_sources_ifdef(CONFIG_GPIO_PCA_SERIES gpio_pca_series.c) zephyr_library_sources_ifdef(CONFIG_GPIO_PCF857X gpio_pcf857x.c) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 81b20ea68c6404..91ad2010d31806 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -151,7 +151,6 @@ source "drivers/gpio/Kconfig.numaker" source "drivers/gpio/Kconfig.numicro" source "drivers/gpio/Kconfig.nxp_s32" source "drivers/gpio/Kconfig.pca953x" -source "drivers/gpio/Kconfig.pca95xx" source "drivers/gpio/Kconfig.pca_series" source "drivers/gpio/Kconfig.pcal64xxa" source "drivers/gpio/Kconfig.pcf857x" diff --git a/drivers/gpio/Kconfig.pca95xx b/drivers/gpio/Kconfig.pca95xx deleted file mode 100644 index 91383a1a400cac..00000000000000 --- a/drivers/gpio/Kconfig.pca95xx +++ /dev/null @@ -1,27 +0,0 @@ -# PCA95XX GPIO configuration options - -# Copyright (c) 2016 Intel Corporation -# SPDX-License-Identifier: Apache-2.0 - -menuconfig GPIO_PCA95XX - bool "PCA95XX I2C-based GPIO chip" - default y - depends on DT_HAS_NXP_PCA95XX_ENABLED - depends on I2C - help - Enable driver for PCA95XX I2C-based GPIO chip. - -config GPIO_PCA95XX_INIT_PRIORITY - int "Init priority" - default 70 - depends on GPIO_PCA95XX - help - Device driver initialization priority. - -config GPIO_PCA95XX_INTERRUPT - bool "Interrupt enable" - depends on GPIO_PCA95XX - help - Enable interrupt support in PCA95XX driver. - Note that the PCA95XX cannot reliably detect - short-pulse interrupts due to its design. diff --git a/drivers/gpio/gpio_pca95xx.c b/drivers/gpio/gpio_pca95xx.c deleted file mode 100644 index 2cfffe7e198f26..00000000000000 --- a/drivers/gpio/gpio_pca95xx.c +++ /dev/null @@ -1,877 +0,0 @@ -/* - * Copyright (c) 2015 Intel Corporation. - * Copyright (c) 2020 Norbit ODM AS - * Copyright 2022 NXP - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#define DT_DRV_COMPAT nxp_pca95xx - -/** - * @file Driver for PCA95XX and PCAL95XX I2C-based GPIO driver. - */ - -#include - -#include -#include -#include -#include -#include -#include -#include - -#include - -#define LOG_LEVEL CONFIG_GPIO_LOG_LEVEL -#include -LOG_MODULE_REGISTER(gpio_pca95xx); - -#if CONFIG_LITTLE_ENDIAN -#define LOW_BYTE_LE16_IDX 0 -#define HIGH_BYTE_LE16_IDX 1 -#else -#define LOW_BYTE_LE16_IDX 1 -#define HIGH_BYTE_LE16_IDX 0 -#endif - -/* Register definitions */ -#define REG_INPUT_PORT0 0x00 -#define REG_INPUT_PORT1 0x01 -#define REG_OUTPUT_PORT0 0x02 -#define REG_OUTPUT_PORT1 0x03 -#define REG_POL_INV_PORT0 0x04 -#define REG_POL_INV_PORT1 0x05 -#define REG_CONF_PORT0 0x06 -#define REG_CONF_PORT1 0x07 -#define REG_OUT_DRV_STRENGTH_PORT0_L 0x40 -#define REG_OUT_DRV_STRENGTH_PORT0_H 0x41 -#define REG_OUT_DRV_STRENGTH_PORT1_L 0x42 -#define REG_OUT_DRV_STRENGTH_PORT1_H 0x43 -#define REG_INPUT_LATCH_PORT0 0x44 -#define REG_INPUT_LATCH_PORT1 0x45 -#define REG_PUD_EN_PORT0 0x46 -#define REG_PUD_EN_PORT1 0x47 -#define REG_PUD_SEL_PORT0 0x48 -#define REG_PUD_SEL_PORT1 0x49 -#define REG_INT_MASK_PORT0 0x4A -#define REG_INT_MASK_PORT1 0x4B -#define REG_INT_STATUS_PORT0 0x4C -#define REG_INT_STATUS_PORT1 0x4D -#define REG_OUTPUT_PORT_CONF 0x4F - -/* Driver flags */ -#define PCA_HAS_PUD BIT(0) -#define PCA_HAS_INTERRUPT BIT(1) -#define PCA_HAS_INTERRUPT_MASK_REG BIT(2) - -/** Configuration data */ -struct gpio_pca95xx_config { - /* gpio_driver_config needs to be first */ - struct gpio_driver_config common; - struct i2c_dt_spec bus; - uint8_t capabilities; -#ifdef CONFIG_GPIO_PCA95XX_INTERRUPT - struct gpio_dt_spec int_gpio; -#endif -}; - -/** Runtime driver data */ -struct gpio_pca95xx_drv_data { - /* gpio_driver_data needs to be first */ - struct gpio_driver_data common; - - struct { - uint16_t input; - uint16_t output; - uint16_t dir; - uint16_t pud_en; - uint16_t pud_sel; - uint16_t int_mask; - uint16_t input_latch; - } reg_cache; - - struct k_sem lock; - -#ifdef CONFIG_GPIO_PCA95XX_INTERRUPT - /* Self-reference to the driver instance */ - const struct device *instance; - - /* port ISR callback routine address */ - sys_slist_t callbacks; - - /* interrupt triggering pin masks */ - struct { - uint16_t edge_rising; - uint16_t edge_falling; - uint16_t level_high; - uint16_t level_low; - } interrupts; - - struct gpio_callback gpio_callback; - - struct k_work interrupt_worker; - - bool interrupt_active; -#endif -}; - -static int read_port_reg(const struct device *dev, uint8_t reg, uint8_t pin, - uint16_t *cache, uint16_t *buf) -{ - const struct gpio_pca95xx_config * const config = dev->config; - uint8_t b_buf; - int ret; - - if (pin >= 8) { - reg++; - } - - ret = i2c_reg_read_byte_dt(&config->bus, reg, &b_buf); - if (ret != 0) { - LOG_ERR("PCA95XX[0x%X]: error reading register 0x%X (%d)", - config->bus.addr, reg, ret); - return ret; - } - - if (pin < 8) { - ((uint8_t *)cache)[LOW_BYTE_LE16_IDX] = b_buf; - } else { - ((uint8_t *)cache)[HIGH_BYTE_LE16_IDX] = b_buf; - } - - *buf = *cache; - - LOG_DBG("PCA95XX[0x%X]: Read: REG[0x%X] = 0x%X", - config->bus.addr, reg, b_buf); - - return 0; -} - -/** - * @brief Read both port 0 and port 1 registers of certain register function. - * - * Given the register in reg, read the pair of port 0 and port 1. - * - * @param dev Device struct of the PCA95XX. - * @param reg Register to read (the PORT0 of the pair of registers). - * @param cache Pointer to the cache to be updated after successful read. - * @param buf Buffer to read data into. - * - * @return 0 if successful, failed otherwise. - */ -static int read_port_regs(const struct device *dev, uint8_t reg, - uint16_t *cache, uint16_t *buf) -{ - const struct gpio_pca95xx_config * const config = dev->config; - uint16_t port_data, value; - int ret; - - ret = i2c_burst_read_dt(&config->bus, reg, (uint8_t *)&port_data, - sizeof(port_data)); - if (ret != 0) { - LOG_ERR("PCA95XX[0x%X]: error reading register 0x%X (%d)", - config->bus.addr, reg, ret); - return ret; - } - - value = sys_le16_to_cpu(port_data); - *cache = value; - *buf = value; - - LOG_DBG("PCA95XX[0x%X]: Read: REG[0x%X] = 0x%X, REG[0x%X] = 0x%X", - config->bus.addr, reg, (*buf & 0xFF), (reg + 1), (*buf >> 8)); - - return 0; -} - - -static int write_port_reg(const struct device *dev, uint8_t reg, uint8_t pin, - uint16_t *cache, uint16_t value) -{ - const struct gpio_pca95xx_config * const config = dev->config; - uint8_t buf[2]; - int ret; - - if (pin < 8) { - buf[1] = value; - } else { - buf[1] = value >> 8; - reg++; - } - buf[0] = reg; - - LOG_DBG("PCA95XX[0x%X]: Write: REG[0x%X] = 0x%X", config->bus.addr, - reg, buf[1]); - - ret = i2c_write_dt(&config->bus, buf, sizeof(buf)); - if (ret == 0) { - *cache = value; - } else { - LOG_ERR("PCA95XX[0x%X]: error writing to register 0x%X " - "(%d)", config->bus.addr, reg, ret); - } - - return ret; -} - -/** - * @brief Write both port 0 and port 1 registers of certain register function. - * - * Given the register in reg, write the pair of port 0 and port 1. - * - * @param dev Device struct of the PCA95XX. - * @param reg Register to write into (the PORT0 of the pair of registers). - * @param cache Pointer to the cache to be updated after successful write. - * @param value New value to set. - * - * @return 0 if successful, failed otherwise. - */ -static int write_port_regs(const struct device *dev, uint8_t reg, - uint16_t *cache, uint16_t value) -{ - const struct gpio_pca95xx_config * const config = dev->config; - uint8_t buf[3]; - int ret; - - LOG_DBG("PCA95XX[0x%X]: Write: REG[0x%X] = 0x%X, REG[0x%X] = " - "0x%X", config->bus.addr, reg, (value & 0xFF), - (reg + 1), (value >> 8)); - - buf[0] = reg; - sys_put_le16(value, &buf[1]); - - ret = i2c_write_dt(&config->bus, buf, sizeof(buf)); - if (ret == 0) { - *cache = value; - } else { - LOG_ERR("PCA95XX[0x%X]: error writing to register 0x%X " - "(%d)", config->bus.addr, reg, ret); - } - - return ret; -} - -static inline int update_input_reg(const struct device *dev, uint8_t pin, - uint16_t *buf) -{ - struct gpio_pca95xx_drv_data * const drv_data = - (struct gpio_pca95xx_drv_data * const)dev->data; - - return read_port_reg(dev, REG_INPUT_PORT0, pin, - &drv_data->reg_cache.input, buf); -} - -static inline int update_input_regs(const struct device *dev, uint16_t *buf) -{ - struct gpio_pca95xx_drv_data * const drv_data = - (struct gpio_pca95xx_drv_data * const)dev->data; - - return read_port_regs(dev, REG_INPUT_PORT0, - &drv_data->reg_cache.input, buf); -} - -static inline int update_output_reg(const struct device *dev, uint8_t pin, - uint16_t value) -{ - struct gpio_pca95xx_drv_data * const drv_data = - (struct gpio_pca95xx_drv_data * const)dev->data; - - return write_port_reg(dev, REG_OUTPUT_PORT0, pin, - &drv_data->reg_cache.output, value); -} - -static inline int update_output_regs(const struct device *dev, uint16_t value) -{ - struct gpio_pca95xx_drv_data * const drv_data = - (struct gpio_pca95xx_drv_data * const)dev->data; - - return write_port_regs(dev, REG_OUTPUT_PORT0, - &drv_data->reg_cache.output, value); -} - -static inline int update_direction_reg(const struct device *dev, uint8_t pin, - uint16_t value) -{ - struct gpio_pca95xx_drv_data * const drv_data = - (struct gpio_pca95xx_drv_data * const)dev->data; - - return write_port_reg(dev, REG_CONF_PORT0, pin, - &drv_data->reg_cache.dir, value); -} - -static inline int update_pul_sel_reg(const struct device *dev, uint8_t pin, - uint16_t value) -{ - struct gpio_pca95xx_drv_data * const drv_data = - (struct gpio_pca95xx_drv_data * const)dev->data; - - return write_port_reg(dev, REG_PUD_SEL_PORT0, pin, - &drv_data->reg_cache.pud_sel, value); -} - -static inline int update_pul_en_reg(const struct device *dev, uint8_t pin, - uint8_t value) -{ - struct gpio_pca95xx_drv_data * const drv_data = - (struct gpio_pca95xx_drv_data * const)dev->data; - - return write_port_reg(dev, REG_PUD_EN_PORT0, pin, - &drv_data->reg_cache.pud_en, value); -} - -#ifdef CONFIG_GPIO_PCA95XX_INTERRUPT -static inline int update_int_mask_reg(const struct device *dev, uint8_t pin, - uint16_t value) -{ - struct gpio_pca95xx_drv_data * const drv_data = - (struct gpio_pca95xx_drv_data * const)dev->data; - - /* If the interrupt mask is present, so is the input latch */ - write_port_reg(dev, REG_INPUT_LATCH_PORT0, pin, &drv_data->reg_cache.input_latch, ~value); - - return write_port_reg(dev, REG_INT_MASK_PORT0, pin, - &drv_data->reg_cache.int_mask, value); -} -#endif /* CONFIG_GPIO_PCA95XX_INTERRUPT */ - -/** - * @brief Setup the pin direction (input or output) - * - * @param dev Device struct of the PCA95XX - * @param pin The pin number - * @param flags Flags of pin or port - * - * @return 0 if successful, failed otherwise - */ -static int setup_pin_dir(const struct device *dev, uint32_t pin, int flags) -{ - struct gpio_pca95xx_drv_data * const drv_data = - (struct gpio_pca95xx_drv_data * const)dev->data; - uint16_t reg_dir = drv_data->reg_cache.dir; - uint16_t reg_out = drv_data->reg_cache.output; - int ret; - - /* For each pin, 0 == output, 1 == input */ - if ((flags & GPIO_OUTPUT) != 0U) { - if ((flags & GPIO_OUTPUT_INIT_HIGH) != 0U) { - reg_out |= BIT(pin); - } else if ((flags & GPIO_OUTPUT_INIT_LOW) != 0U) { - reg_out &= ~BIT(pin); - } - ret = update_output_reg(dev, pin, reg_out); - if (ret != 0) { - return ret; - } - reg_dir &= ~BIT(pin); - } else { - reg_dir |= BIT(pin); - } - - ret = update_direction_reg(dev, pin, reg_dir); - - return ret; -} - -/** - * @brief Setup the pin pull up/pull down status - * - * @param dev Device struct of the PCA95XX - * @param pin The pin number - * @param flags Flags of pin or port - * - * @return 0 if successful, failed otherwise - */ -static int setup_pin_pullupdown(const struct device *dev, uint32_t pin, - int flags) -{ - const struct gpio_pca95xx_config * const config = dev->config; - struct gpio_pca95xx_drv_data * const drv_data = - (struct gpio_pca95xx_drv_data * const)dev->data; - uint16_t reg_pud; - int ret; - - if ((config->capabilities & PCA_HAS_PUD) == 0) { - /* Chip does not support pull up/pull down */ - if ((flags & (GPIO_PULL_UP | GPIO_PULL_DOWN)) != 0U) { - return -ENOTSUP; - } - - /* If both GPIO_PULL_UP and GPIO_PULL_DOWN are not set, - * we should disable them in hardware. But need to skip - * if the chip does not support pull up/pull down. - */ - return 0; - } - - /* If disabling pull up/down, there is no need to set the selection - * register. Just go straight to disabling. - */ - if ((flags & (GPIO_PULL_UP | GPIO_PULL_DOWN)) != 0U) { - /* Setup pin pull up or pull down */ - reg_pud = drv_data->reg_cache.pud_sel; - - /* pull down == 0, pull up == 1 */ - WRITE_BIT(reg_pud, pin, (flags & GPIO_PULL_UP) != 0U); - - ret = update_pul_sel_reg(dev, pin, reg_pud); - if (ret) { - return ret; - } - } - - /* enable/disable pull up/down */ - reg_pud = drv_data->reg_cache.pud_en; - - WRITE_BIT(reg_pud, pin, - (flags & (GPIO_PULL_UP | GPIO_PULL_DOWN)) != 0U); - - ret = update_pul_en_reg(dev, pin, reg_pud); - - return ret; -} - -/** - * @brief Configure pin or port - * - * @param dev Device struct of the PCA95XX - * @param pin The pin number - * @param flags Flags of pin or port - * - * @return 0 if successful, failed otherwise - */ -static int gpio_pca95xx_config(const struct device *dev, - gpio_pin_t pin, gpio_flags_t flags) -{ - int ret; - struct gpio_pca95xx_drv_data * const drv_data = - (struct gpio_pca95xx_drv_data * const)dev->data; - -#if (CONFIG_GPIO_LOG_LEVEL >= LOG_LEVEL_DEBUG) - const struct gpio_pca95xx_config * const config = dev->config; -#endif - - /* Does not support disconnected pin */ - if ((flags & (GPIO_INPUT | GPIO_OUTPUT)) == GPIO_DISCONNECTED) { - return -ENOTSUP; - } - - /* Open-drain support is per port, not per pin. - * So can't really support the API as-is. - */ - if ((flags & GPIO_SINGLE_ENDED) != 0U) { - return -ENOTSUP; - } - - /* Can't do I2C bus operations from an ISR */ - if (k_is_in_isr()) { - return -EWOULDBLOCK; - } - - k_sem_take(&drv_data->lock, K_FOREVER); - - ret = setup_pin_dir(dev, pin, flags); - if (ret) { - LOG_ERR("PCA95XX[0x%X]: error setting pin direction (%d)", - config->bus.addr, ret); - goto done; - } - - ret = setup_pin_pullupdown(dev, pin, flags); - if (ret) { - LOG_ERR("PCA95XX[0x%X]: error setting pin pull up/down " - "(%d)", config->bus.addr, ret); - goto done; - } - -done: - k_sem_give(&drv_data->lock); - return ret; -} - -static int gpio_pca95xx_port_get_raw(const struct device *dev, uint32_t *value) -{ - struct gpio_pca95xx_drv_data * const drv_data = - (struct gpio_pca95xx_drv_data * const)dev->data; - uint16_t buf; - int ret; - - /* Can't do I2C bus operations from an ISR */ - if (k_is_in_isr()) { - return -EWOULDBLOCK; - } - - k_sem_take(&drv_data->lock, K_FOREVER); - - ret = update_input_regs(dev, &buf); - if (ret != 0) { - goto done; - } - - *value = buf; - -done: - k_sem_give(&drv_data->lock); - return ret; -} - -static int gpio_pca95xx_port_set_masked_raw(const struct device *dev, - uint32_t mask, uint32_t value) -{ - struct gpio_pca95xx_drv_data * const drv_data = - (struct gpio_pca95xx_drv_data * const)dev->data; - uint16_t reg_out; - int ret; - - /* Can't do I2C bus operations from an ISR */ - if (k_is_in_isr()) { - return -EWOULDBLOCK; - } - - k_sem_take(&drv_data->lock, K_FOREVER); - - reg_out = drv_data->reg_cache.output; - reg_out = (reg_out & ~mask) | (mask & value); - - ret = update_output_regs(dev, reg_out); - - k_sem_give(&drv_data->lock); - - return ret; -} - -static int gpio_pca95xx_port_set_bits_raw(const struct device *dev, - uint32_t mask) -{ - return gpio_pca95xx_port_set_masked_raw(dev, mask, mask); -} - -static int gpio_pca95xx_port_clear_bits_raw(const struct device *dev, - uint32_t mask) -{ - return gpio_pca95xx_port_set_masked_raw(dev, mask, 0); -} - -static int gpio_pca95xx_port_toggle_bits(const struct device *dev, - uint32_t mask) -{ - struct gpio_pca95xx_drv_data * const drv_data = - (struct gpio_pca95xx_drv_data * const)dev->data; - uint16_t reg_out; - int ret; - - /* Can't do I2C bus operations from an ISR */ - if (k_is_in_isr()) { - return -EWOULDBLOCK; - } - - k_sem_take(&drv_data->lock, K_FOREVER); - - reg_out = drv_data->reg_cache.output; - reg_out ^= mask; - - ret = update_output_regs(dev, reg_out); - - k_sem_give(&drv_data->lock); - - return ret; -} - -#ifdef CONFIG_GPIO_PCA95XX_INTERRUPT -static void get_triggered_it(struct gpio_pca95xx_drv_data *drv_data, - uint16_t *trig_edge, uint16_t *trig_level) -{ - uint16_t input_cache, changed_pins, input_new; - int ret; - - input_cache = drv_data->reg_cache.input; - ret = update_input_regs(drv_data->instance, &input_new); - if (ret == 0) { - changed_pins = (input_cache ^ input_new); - - *trig_edge |= (changed_pins & input_new & - drv_data->interrupts.edge_rising); - *trig_edge |= (changed_pins & input_cache & - drv_data->interrupts.edge_falling); - *trig_level |= (input_new & drv_data->interrupts.level_high); - *trig_level |= (~input_new & drv_data->interrupts.level_low); - } -} - -static void gpio_pca95xx_interrupt_worker(struct k_work *work) -{ - struct gpio_pca95xx_drv_data * const drv_data = CONTAINER_OF( - work, struct gpio_pca95xx_drv_data, interrupt_worker); - const struct gpio_pca95xx_config * const config = drv_data->instance->config; - uint16_t trig_edge = 0, trig_level = 0; - uint32_t triggered_int = 0; - - k_sem_take(&drv_data->lock, K_FOREVER); - - /* Note: PCA Interrupt status is cleared by reading inputs */ - if (config->capabilities & PCA_HAS_INTERRUPT_MASK_REG) { - /* gpio latched read values, to be compared to cached ones */ - get_triggered_it(drv_data, &trig_edge, &trig_level); - } - /* gpio unlatched read values, in case signal has flipped again */ - get_triggered_it(drv_data, &trig_edge, &trig_level); - - triggered_int = (trig_edge | trig_level); - - k_sem_give(&drv_data->lock); - - if (triggered_int != 0) { - gpio_fire_callbacks(&drv_data->callbacks, drv_data->instance, - triggered_int); - } - - /* Emulate level triggering */ - if (trig_level != 0) { - /* Reschedule worker */ - k_work_submit(&drv_data->interrupt_worker); - } -} - -static void gpio_pca95xx_interrupt_callback(const struct device *dev, - struct gpio_callback *cb, - gpio_port_pins_t pins) -{ - struct gpio_pca95xx_drv_data * const drv_data = - CONTAINER_OF(cb, struct gpio_pca95xx_drv_data, gpio_callback); - - ARG_UNUSED(pins); - - /* Cannot read PCA95xx registers from ISR context, queue worker */ - k_work_submit(&drv_data->interrupt_worker); -} - -static int gpio_pca95xx_pin_interrupt_configure(const struct device *dev, - gpio_pin_t pin, - enum gpio_int_mode mode, - enum gpio_int_trig trig) -{ - int ret = 0; - const struct gpio_pca95xx_config * const config = dev->config; - struct gpio_pca95xx_drv_data * const drv_data = - (struct gpio_pca95xx_drv_data * const)dev->data; - uint16_t reg; - bool enabled, edge, level, active; - - /* Check if GPIO port supports interrupts */ - if ((config->capabilities & PCA_HAS_INTERRUPT) == 0U) { - return -ENOTSUP; - } - - /* Check for an invalid pin number */ - if (BIT(pin) > config->common.port_pin_mask) { - return -EINVAL; - } - - /* Check configured pin direction */ - if ((mode != GPIO_INT_MODE_DISABLED) && - (BIT(pin) & drv_data->reg_cache.dir) != BIT(pin)) { - LOG_ERR("PCA95XX[0x%X]: output pin cannot trigger interrupt", - config->bus.addr); - return -ENOTSUP; - } - - k_sem_take(&drv_data->lock, K_FOREVER); - - /* Check if GPIO port has an interrupt mask register */ - if (config->capabilities & PCA_HAS_INTERRUPT_MASK_REG) { - uint16_t reg_out; - - reg_out = drv_data->reg_cache.int_mask; - WRITE_BIT(reg_out, pin, (mode == GPIO_INT_MODE_DISABLED)); - - ret = update_int_mask_reg(dev, pin, reg_out); - if (ret != 0) { - LOG_ERR("PCA95XX[0x%X]: failed to update int mask (%d)", - config->bus.addr, ret); - goto err; - } - } - - /* Update interrupt masks */ - enabled = ((mode & GPIO_INT_MODE_DISABLED) == 0U); - edge = (mode == GPIO_INT_MODE_EDGE); - level = (mode == GPIO_INT_MODE_LEVEL); - WRITE_BIT(drv_data->interrupts.edge_rising, pin, (enabled && - edge && ((trig & GPIO_INT_TRIG_HIGH) == GPIO_INT_TRIG_HIGH))); - WRITE_BIT(drv_data->interrupts.edge_falling, pin, (enabled && - edge && ((trig & GPIO_INT_TRIG_LOW) == GPIO_INT_TRIG_LOW))); - WRITE_BIT(drv_data->interrupts.level_high, pin, (enabled && - level && ((trig & GPIO_INT_TRIG_HIGH) == GPIO_INT_TRIG_HIGH))); - WRITE_BIT(drv_data->interrupts.level_low, pin, (enabled && - level && ((trig & GPIO_INT_TRIG_LOW) == GPIO_INT_TRIG_LOW))); - - active = ((drv_data->interrupts.edge_rising || - drv_data->interrupts.edge_falling || - drv_data->interrupts.level_high || - drv_data->interrupts.level_low) > 0); - - /* Enable / disable interrupt as needed */ - if (active != drv_data->interrupt_active) { - ret = gpio_pin_interrupt_configure_dt( - &config->int_gpio, active ? - GPIO_INT_EDGE_TO_ACTIVE : - GPIO_INT_MODE_DISABLED); - if (ret != 0) { - LOG_ERR("PCA95XX[0x%X]: failed to configure interrupt " - "on pin %d (%d)", config->bus.addr, - config->int_gpio.pin, ret); - goto err; - } - drv_data->interrupt_active = active; - - if (active) { - /* Read current status to reset any - * active signal on INT line - */ - update_input_regs(dev, ®); - } - } - -err: - k_sem_give(&drv_data->lock); - return ret; -} - -static int gpio_pca95xx_manage_callback(const struct device *dev, - struct gpio_callback *callback, - bool set) -{ - const struct gpio_pca95xx_config * const config = dev->config; - struct gpio_pca95xx_drv_data * const drv_data = - (struct gpio_pca95xx_drv_data * const)dev->data; - - if ((config->capabilities & PCA_HAS_INTERRUPT) == 0U) { - return -ENOTSUP; - } - - k_sem_take(&drv_data->lock, K_FOREVER); - - gpio_manage_callback(&drv_data->callbacks, callback, set); - - k_sem_give(&drv_data->lock); - return 0; -} -#endif /* CONFIG_GPIO_PCA95XX_INTERRUPT */ - -static const struct gpio_driver_api gpio_pca95xx_drv_api_funcs = { - .pin_configure = gpio_pca95xx_config, - .port_get_raw = gpio_pca95xx_port_get_raw, - .port_set_masked_raw = gpio_pca95xx_port_set_masked_raw, - .port_set_bits_raw = gpio_pca95xx_port_set_bits_raw, - .port_clear_bits_raw = gpio_pca95xx_port_clear_bits_raw, - .port_toggle_bits = gpio_pca95xx_port_toggle_bits, -#ifdef CONFIG_GPIO_PCA95XX_INTERRUPT - .pin_interrupt_configure = gpio_pca95xx_pin_interrupt_configure, - .manage_callback = gpio_pca95xx_manage_callback, -#endif -}; - -/** - * @brief Initialization function of PCA95XX - * - * @param dev Device struct - * @return 0 if successful, failed otherwise. - */ -static int gpio_pca95xx_init(const struct device *dev) -{ - const struct gpio_pca95xx_config * const config = dev->config; - struct gpio_pca95xx_drv_data * const drv_data = - (struct gpio_pca95xx_drv_data * const)dev->data; - - if (!device_is_ready(config->bus.bus)) { - return -ENODEV; - } - - k_sem_init(&drv_data->lock, 1, 1); - -#ifdef CONFIG_GPIO_PCA95XX_INTERRUPT - /* Check if GPIO port supports interrupts */ - if ((config->capabilities & PCA_HAS_INTERRUPT) != 0) { - int ret; - - /* Store self-reference for interrupt handling */ - drv_data->instance = dev; - - /* Prepare interrupt worker */ - k_work_init(&drv_data->interrupt_worker, - gpio_pca95xx_interrupt_worker); - - /* Configure GPIO interrupt pin */ - if (!gpio_is_ready_dt(&config->int_gpio)) { - LOG_ERR("PCA95XX[0x%X]: interrupt GPIO not ready", - config->bus.addr); - return -ENODEV; - } - - ret = gpio_pin_configure_dt(&config->int_gpio, GPIO_INPUT); - if (ret != 0) { - LOG_ERR("PCA95XX[0x%X]: failed to configure interrupt" - " pin %d (%d)", config->bus.addr, - config->int_gpio.pin, ret); - return ret; - } - - /* Prepare GPIO callback for interrupt pin */ - gpio_init_callback(&drv_data->gpio_callback, - gpio_pca95xx_interrupt_callback, - BIT(config->int_gpio.pin)); - ret = gpio_add_callback(config->int_gpio.port, &drv_data->gpio_callback); - if (ret != 0) { - LOG_ERR("PCA95XX[0x%X]: failed to add interrupt callback for" - " pin %d (%d)", config->bus.addr, - config->int_gpio.pin, ret); - return ret; - } - } -#endif - - return 0; -} - -#define GPIO_PCA95XX_DEVICE_INSTANCE(inst) \ -static const struct gpio_pca95xx_config gpio_pca95xx_##inst##_cfg = { \ - .common = { \ - .port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(inst), \ - }, \ - .bus = I2C_DT_SPEC_INST_GET(inst), \ - .capabilities = \ - (DT_INST_PROP(inst, has_pud) ? PCA_HAS_PUD : 0) | \ - IF_ENABLED(CONFIG_GPIO_PCA95XX_INTERRUPT, ( \ - (DT_INST_NODE_HAS_PROP(inst, interrupt_gpios) ? \ - PCA_HAS_INTERRUPT : 0) | \ - (DT_INST_PROP(inst, has_interrupt_mask_reg) ? \ - PCA_HAS_INTERRUPT_MASK_REG : 0) | \ - )) \ - 0, \ - IF_ENABLED(CONFIG_GPIO_PCA95XX_INTERRUPT, \ - (.int_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, interrupt_gpios, {}),)) \ -}; \ - \ -static struct gpio_pca95xx_drv_data gpio_pca95xx_##inst##_drvdata = { \ - .reg_cache.input = 0x0, \ - .reg_cache.output = 0xFFFF, \ - .reg_cache.dir = 0xFFFF, \ - .reg_cache.pud_en = 0x0, \ - .reg_cache.pud_sel = 0xFFFF, \ - .reg_cache.int_mask = 0xFFFF, \ - .reg_cache.input_latch = 0x0, \ - IF_ENABLED(CONFIG_GPIO_PCA95XX_INTERRUPT, ( \ - .interrupt_active = false, \ - )) \ -}; \ - \ -DEVICE_DT_INST_DEFINE(inst, \ - gpio_pca95xx_init, \ - NULL, \ - &gpio_pca95xx_##inst##_drvdata, \ - &gpio_pca95xx_##inst##_cfg, \ - POST_KERNEL, CONFIG_GPIO_PCA95XX_INIT_PRIORITY, \ - &gpio_pca95xx_drv_api_funcs); - -DT_INST_FOREACH_STATUS_OKAY(GPIO_PCA95XX_DEVICE_INSTANCE) diff --git a/dts/bindings/gpio/nxp,pca95xx.yaml b/dts/bindings/gpio/nxp,pca95xx.yaml deleted file mode 100644 index 449d52ac47b07c..00000000000000 --- a/dts/bindings/gpio/nxp,pca95xx.yaml +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (c) 2019 Intel Corporation -# SPDX-License-Identifier: Apache-2.0 - -description: PCA95xx series I2C-based GPIO expander - -compatible: "nxp,pca95xx" - -include: [gpio-controller.yaml, i2c-device.yaml] - -properties: - has-pud: - type: boolean - description: Supports pull-up/pull-down - - has-interrupt-mask-reg: - type: boolean - description: Has Interrupt mask register (PCAL95xx) - - interrupt-gpios: - type: phandle-array - description: Interrupt GPIO pin (active-low open-drain) - - "#gpio-cells": - const: 2 - -gpio-cells: - - pin - - flags diff --git a/tests/boards/mec15xxevb_assy6853/i2c_api/src/main.c b/tests/boards/mec15xxevb_assy6853/i2c_api/src/main.c index 7143f98c889cb7..774086f8103b03 100644 --- a/tests/boards/mec15xxevb_assy6853/i2c_api/src/main.c +++ b/tests/boards/mec15xxevb_assy6853/i2c_api/src/main.c @@ -29,7 +29,7 @@ ZTEST(i2c, test_i2c_pca95xx) uint32_t i2c_cfg = I2C_SPEED_SET(I2C_SPEED_STANDARD) | I2C_MODE_CONTROLLER; /* get i2c device */ - const struct i2c_dt_spec i2c = I2C_DT_SPEC_GET(DT_COMPAT_GET_ANY_STATUS_OKAY(nxp_pca95xx)); + const struct i2c_dt_spec i2c = I2C_DT_SPEC_GET(DT_COMPAT_GET_ANY_STATUS_OKAY(nxp_pca9555)); zassert_true(device_is_ready(i2c.bus), "I2C controller device is not ready"); diff --git a/tests/boards/mec15xxevb_assy6853/i2c_api/testcase.yaml b/tests/boards/mec15xxevb_assy6853/i2c_api/testcase.yaml index 038e4dded3bb3e..c594717882faa4 100644 --- a/tests/boards/mec15xxevb_assy6853/i2c_api/testcase.yaml +++ b/tests/boards/mec15xxevb_assy6853/i2c_api/testcase.yaml @@ -4,5 +4,5 @@ tests: tags: - drivers - i2c - filter: dt_compat_enabled("nxp,pca95xx") + filter: dt_compat_enabled("nxp,pca9555") platform_allow: mec15xxevb_assy6853 diff --git a/tests/boards/mec172xevb_assy6906/i2c_api/src/main.c b/tests/boards/mec172xevb_assy6906/i2c_api/src/main.c index 5bc99d40cbe485..7caf33481b963b 100644 --- a/tests/boards/mec172xevb_assy6906/i2c_api/src/main.c +++ b/tests/boards/mec172xevb_assy6906/i2c_api/src/main.c @@ -29,7 +29,7 @@ ZTEST(boards_mec172x_pca95xx, test_i2c_pca95xx) uint32_t i2c_cfg = I2C_SPEED_SET(I2C_SPEED_STANDARD) | I2C_MODE_CONTROLLER; /* get i2c device */ - const struct i2c_dt_spec i2c = I2C_DT_SPEC_GET(DT_COMPAT_GET_ANY_STATUS_OKAY(nxp_pca95xx)); + const struct i2c_dt_spec i2c = I2C_DT_SPEC_GET(DT_COMPAT_GET_ANY_STATUS_OKAY(nxp_pca9555)); zassert_true(device_is_ready(i2c.bus), "I2C controller device is not ready"); diff --git a/tests/boards/mec172xevb_assy6906/i2c_api/testcase.yaml b/tests/boards/mec172xevb_assy6906/i2c_api/testcase.yaml index 8deaed213edec0..d772f66f4ff673 100644 --- a/tests/boards/mec172xevb_assy6906/i2c_api/testcase.yaml +++ b/tests/boards/mec172xevb_assy6906/i2c_api/testcase.yaml @@ -4,5 +4,5 @@ tests: tags: - drivers - i2c - filter: dt_compat_enabled("nxp,pca95xx") + filter: dt_compat_enabled("nxp,pca9555") platform_allow: mec172xevb_assy6906 diff --git a/tests/drivers/build_all/gpio/app.overlay b/tests/drivers/build_all/gpio/app.overlay index c834f27621f455..837e667a9c8e04 100644 --- a/tests/drivers/build_all/gpio/app.overlay +++ b/tests/drivers/build_all/gpio/app.overlay @@ -105,13 +105,13 @@ int-gpios = <&test_gpio 0 0>; }; - test_i2c_pca95xx: pca95xx@3 { - compatible = "nxp,pca95xx"; + test_i2c_pca95xx: pca9555@3 { + compatible = "nxp,pca9555"; reg = <0x03>; gpio-controller; #gpio-cells = <2>; ngpios = <16>; - interrupt-gpios = <&test_gpio 0 0>; + int-gpios = <&test_gpio 0 0>; }; test_i2c_pcf8575: pcf8575@4 { diff --git a/tests/drivers/build_all/gpio/prj.conf b/tests/drivers/build_all/gpio/prj.conf index cd3185f68456f2..a7ade43d4c1cf2 100644 --- a/tests/drivers/build_all/gpio/prj.conf +++ b/tests/drivers/build_all/gpio/prj.conf @@ -2,7 +2,6 @@ CONFIG_TEST=y CONFIG_GPIO=y CONFIG_TEST_USERSPACE=y CONFIG_I2C=y -CONFIG_GPIO_PCA95XX_INTERRUPT=y CONFIG_SPI=y CONFIG_MFD=y CONFIG_MFD_INIT_PRIORITY=80 From a5d3582400ac051a28f649a424a8a68a9d9f6ba2 Mon Sep 17 00:00:00 2001 From: Chekhov Ma Date: Wed, 11 Sep 2024 17:56:01 +0800 Subject: [PATCH 2/6] drivers: gpio: retire pca953x driver pca953x driver can be replaced by pca_series driver, which covers a larger number of devices. Signed-off-by: Chekhov Ma --- .../bl5340_dvk_nrf5340_cpuapp_common.dtsi | 4 +- boards/ezurio/bt610/Kconfig.defconfig | 2 +- boards/ezurio/bt610/bt610.dts | 4 +- drivers/gpio/CMakeLists.txt | 1 - drivers/gpio/Kconfig | 1 - drivers/gpio/Kconfig.pca953x | 23 - drivers/gpio/gpio_pca953x.c | 508 ------------------ dts/bindings/gpio/ti,tca9538.yaml | 43 -- tests/drivers/build_all/gpio/app.overlay | 9 - 9 files changed, 5 insertions(+), 590 deletions(-) delete mode 100644 drivers/gpio/Kconfig.pca953x delete mode 100644 drivers/gpio/gpio_pca953x.c delete mode 100644 dts/bindings/gpio/ti,tca9538.yaml diff --git a/boards/ezurio/bl5340_dvk/bl5340_dvk_nrf5340_cpuapp_common.dtsi b/boards/ezurio/bl5340_dvk/bl5340_dvk_nrf5340_cpuapp_common.dtsi index c6986c7c878088..418d85745562cd 100644 --- a/boards/ezurio/bl5340_dvk/bl5340_dvk_nrf5340_cpuapp_common.dtsi +++ b/boards/ezurio/bl5340_dvk/bl5340_dvk_nrf5340_cpuapp_common.dtsi @@ -212,12 +212,12 @@ }; gpio_exp0: tca9538@70 { - compatible = "ti,tca9538"; + compatible = "nxp,pca9538"; reg = <0x70>; gpio-controller; #gpio-cells = <2>; ngpios = <8>; - nint-gpios = <&gpio1 6 GPIO_ACTIVE_LOW>; + int-gpios = <&gpio1 6 GPIO_ACTIVE_LOW>; }; }; diff --git a/boards/ezurio/bt610/Kconfig.defconfig b/boards/ezurio/bt610/Kconfig.defconfig index 9a82770655ef07..eecd3f2b0c0d0e 100644 --- a/boards/ezurio/bt610/Kconfig.defconfig +++ b/boards/ezurio/bt610/Kconfig.defconfig @@ -9,6 +9,6 @@ config BT_CTLR default BT config I2C - default $(dt_compat_on_bus,$(DT_COMPAT_TI_TCA9538),i2c) + default $(dt_compat_on_bus,$(DT_COMPAT_NXP_PCA9538),i2c) endif # BOARD_BT610 diff --git a/boards/ezurio/bt610/bt610.dts b/boards/ezurio/bt610/bt610.dts index abf34ed11fc76c..35aa2287fabf5b 100644 --- a/boards/ezurio/bt610/bt610.dts +++ b/boards/ezurio/bt610/bt610.dts @@ -187,12 +187,12 @@ pinctrl-1 = <&i2c0_sleep>; pinctrl-names = "default", "sleep"; gpio_exp0: tca9538@70 { - compatible = "ti,tca9538"; + compatible = "nxp,pca9538"; reg = <0x70>; gpio-controller; #gpio-cells = <2>; ngpios = <8>; - nint-gpios = <&gpio1 14 GPIO_ACTIVE_LOW>; + int-gpios = <&gpio1 14 GPIO_ACTIVE_LOW>; }; }; diff --git a/drivers/gpio/CMakeLists.txt b/drivers/gpio/CMakeLists.txt index 82cdbc1cae46cd..28147793f13664 100644 --- a/drivers/gpio/CMakeLists.txt +++ b/drivers/gpio/CMakeLists.txt @@ -63,7 +63,6 @@ zephyr_library_sources_ifdef(CONFIG_GPIO_NRFX gpio_nrfx.c) zephyr_library_sources_ifdef(CONFIG_GPIO_NUMAKER gpio_numaker.c) zephyr_library_sources_ifdef(CONFIG_GPIO_NUMICRO gpio_numicro.c) zephyr_library_sources_ifdef(CONFIG_GPIO_NXP_S32 gpio_nxp_s32.c) -zephyr_library_sources_ifdef(CONFIG_GPIO_PCA953X gpio_pca953x.c) zephyr_library_sources_ifdef(CONFIG_GPIO_PCAL64XXA gpio_pcal64xxa.c) zephyr_library_sources_ifdef(CONFIG_GPIO_PCA_SERIES gpio_pca_series.c) zephyr_library_sources_ifdef(CONFIG_GPIO_PCF857X gpio_pcf857x.c) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 91ad2010d31806..794d856661c5ba 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -150,7 +150,6 @@ source "drivers/gpio/Kconfig.nrfx" source "drivers/gpio/Kconfig.numaker" source "drivers/gpio/Kconfig.numicro" source "drivers/gpio/Kconfig.nxp_s32" -source "drivers/gpio/Kconfig.pca953x" source "drivers/gpio/Kconfig.pca_series" source "drivers/gpio/Kconfig.pcal64xxa" source "drivers/gpio/Kconfig.pcf857x" diff --git a/drivers/gpio/Kconfig.pca953x b/drivers/gpio/Kconfig.pca953x deleted file mode 100644 index 0bcb54cb8e5ac6..00000000000000 --- a/drivers/gpio/Kconfig.pca953x +++ /dev/null @@ -1,23 +0,0 @@ -# PCA953X GPIO configuration options - -# Copyright (c) 2018 Aapo Vienamo -# Copyright (c) 2021 Laird Connectivity -# SPDX-License-Identifier: Apache-2.0 - -menuconfig GPIO_PCA953X - bool "PCA953X I2C GPIO chip" - default y - depends on DT_HAS_TI_TCA9538_ENABLED - select I2C - help - Enable driver for PCA953X I2C GPIO chip. - -if GPIO_PCA953X - -config GPIO_PCA953X_INIT_PRIORITY - int "Init priority" - default 70 - help - Device driver initialization priority. - -endif # GPIO_PCA953X diff --git a/drivers/gpio/gpio_pca953x.c b/drivers/gpio/gpio_pca953x.c deleted file mode 100644 index 81898e83382b40..00000000000000 --- a/drivers/gpio/gpio_pca953x.c +++ /dev/null @@ -1,508 +0,0 @@ -/* - * Copyright (c) 2018 Peter Bigot Consulting, LLC - * Copyright (c) 2018 Aapo Vienamo - * Copyright (c) 2019 Nordic Semiconductor ASA - * Copyright (c) 2019 Vestas Wind Systems A/S - * Copyright (c) 2020 ZedBlox Ltd. - * Copyright (c) 2021 Laird Connectivity - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -LOG_MODULE_REGISTER(pca953x, CONFIG_GPIO_LOG_LEVEL); - -#include - -/* PCA953X Register addresses */ -#define PCA953X_INPUT_PORT 0x00 -#define PCA953X_OUTPUT_PORT 0x01 -#define PCA953X_CONFIGURATION 0x03 -#define REG_INPUT_LATCH_PORT0 0x42 -#define REG_INT_MASK_PORT0 0x45 - -/* Number of pins supported by the device */ -#define NUM_PINS 8 - -/* Max to select all pins supported on the device. */ -#define ALL_PINS ((uint8_t)BIT_MASK(NUM_PINS)) - -/** Cache of the output configuration and data of the pins. */ -struct pca953x_pin_state { - uint8_t dir; - uint8_t input; - uint8_t output; -}; - -struct pca953x_irq_state { - uint8_t rising; - uint8_t falling; -}; - -/** Runtime driver data */ -struct pca953x_drv_data { - /* gpio_driver_data needs to be first */ - struct gpio_driver_data common; - struct pca953x_pin_state pin_state; - struct k_sem lock; - struct gpio_callback gpio_cb; - struct k_work work; - struct pca953x_irq_state irq_state; - const struct device *dev; - /* user ISR cb */ - sys_slist_t cb; -}; - -/** Configuration data */ -struct pca953x_config { - /* gpio_driver_config needs to be first */ - struct gpio_driver_config common; - struct i2c_dt_spec i2c; - const struct gpio_dt_spec gpio_int; - bool interrupt_enabled; - int interrupt_mask; - int input_latch; -}; - -/** - * @brief Gets the state of input pins of the PCA953X I/O Port and - * stores in driver data struct. - * - * @param dev Pointer to the device structure for the driver instance. - * - * @retval 0 If successful. - * @retval Negative value for error code. - */ -static int update_input(const struct device *dev) -{ - const struct pca953x_config *cfg = dev->config; - struct pca953x_drv_data *drv_data = dev->data; - uint8_t input_states; - int rc = 0; - - rc = i2c_reg_read_byte_dt(&cfg->i2c, PCA953X_INPUT_PORT, &input_states); - - if (rc == 0) { - drv_data->pin_state.input = input_states; - } - return rc; -} - -/** - * @brief Handles interrupt triggered by the interrupt pin of PCA953X I/O Port. - * - * If nint_gpios is configured in device tree then this will be triggered each - * time a gpio configured as an input changes state. The gpio input states are - * read in this function which clears the interrupt. - * - * @param dev Pointer to the device structure for the driver instance. - */ -static void gpio_pca953x_handle_interrupt(const struct device *dev) -{ - struct pca953x_drv_data *drv_data = dev->data; - struct pca953x_irq_state *irq_state = &drv_data->irq_state; - int rc; - uint8_t previous_state; - uint8_t current_state; - uint8_t transitioned_pins; - uint8_t interrupt_status = 0; - - k_sem_take(&drv_data->lock, K_FOREVER); - - /* Any interrupts enabled? */ - if (!irq_state->rising && !irq_state->falling) { - rc = -EINVAL; - goto out; - } - - /* Store previous input state then read new value */ - previous_state = drv_data->pin_state.input; - rc = update_input(dev); - if (rc != 0) { - goto out; - } - - /* Find out which input pins have changed state */ - current_state = drv_data->pin_state.input; - transitioned_pins = previous_state ^ current_state; - - /* Mask gpio transactions with rising/falling edge interrupt config */ - interrupt_status = (irq_state->rising & transitioned_pins & - current_state); - interrupt_status |= (irq_state->falling & transitioned_pins & - previous_state); - -out: - k_sem_give(&drv_data->lock); - - if ((rc == 0) && (interrupt_status)) { - gpio_fire_callbacks(&drv_data->cb, dev, interrupt_status); - } -} - -/** - * @brief Work handler for PCA953X interrupt - * - * @param work Work struct that contains pointer to interrupt handler function - */ -static void gpio_pca953x_work_handler(struct k_work *work) -{ - struct pca953x_drv_data *drv_data = - CONTAINER_OF(work, struct pca953x_drv_data, work); - - gpio_pca953x_handle_interrupt(drv_data->dev); -} - -/** - * @brief ISR for interrupt pin of PCA953X - * - * @param dev Pointer to the device structure for the driver instance. - * @param gpio_cb Pointer to callback function struct - * @param pins Bitmask of pins that triggered interrupt - */ -static void gpio_pca953x_init_cb(const struct device *dev, - struct gpio_callback *gpio_cb, uint32_t pins) -{ - struct pca953x_drv_data *drv_data = - CONTAINER_OF(gpio_cb, struct pca953x_drv_data, gpio_cb); - - ARG_UNUSED(pins); - - k_work_submit(&drv_data->work); -} - -static int gpio_pca953x_config(const struct device *dev, gpio_pin_t pin, - gpio_flags_t flags) -{ - const struct pca953x_config *cfg = dev->config; - struct pca953x_drv_data *drv_data = dev->data; - struct pca953x_pin_state *pins = &drv_data->pin_state; - int rc = 0; - bool data_first = false; - - /* Can't do I2C bus operations from an ISR */ - if (k_is_in_isr()) { - return -EWOULDBLOCK; - } - - /* Single Ended lines (Open drain and open source) not supported */ - if ((flags & GPIO_SINGLE_ENDED) != 0) { - return -ENOTSUP; - } - - /* The PCA953X has no internal pull up support */ - if (((flags & GPIO_PULL_UP) != 0) || ((flags & GPIO_PULL_DOWN) != 0)) { - return -ENOTSUP; - } - - /* Simultaneous input & output mode not supported */ - if (((flags & GPIO_INPUT) != 0) && ((flags & GPIO_OUTPUT) != 0)) { - return -ENOTSUP; - } - - k_sem_take(&drv_data->lock, K_FOREVER); - - /* Ensure either Output or Input is specified */ - if ((flags & GPIO_OUTPUT) != 0) { - pins->dir &= ~BIT(pin); - if ((flags & GPIO_OUTPUT_INIT_LOW) != 0) { - pins->output &= ~BIT(pin); - data_first = true; - } else if ((flags & GPIO_OUTPUT_INIT_HIGH) != 0) { - pins->output |= BIT(pin); - data_first = true; - } - } else if ((flags & GPIO_INPUT) != 0) { - pins->dir |= BIT(pin); - } else { - rc = -ENOTSUP; - goto out; - } - - /* Set output values */ - if (data_first) { - rc = i2c_reg_write_byte_dt(&cfg->i2c, PCA953X_OUTPUT_PORT, pins->output); - } - - if (rc == 0) { - /* Set pin directions */ - rc = i2c_reg_write_byte_dt(&cfg->i2c, PCA953X_CONFIGURATION, pins->dir); - } - - if (rc == 0) { - /* Refresh input status */ - rc = update_input(dev); - } - -out: - k_sem_give(&drv_data->lock); - return rc; -} - -static int gpio_pca953x_port_read(const struct device *dev, - gpio_port_value_t *value) -{ - const struct pca953x_config *cfg = dev->config; - struct pca953x_drv_data *drv_data = dev->data; - uint8_t input_pin_data; - int rc = 0; - - /* Can't do I2C bus operations from an ISR */ - if (k_is_in_isr()) { - return -EWOULDBLOCK; - } - - k_sem_take(&drv_data->lock, K_FOREVER); - - /* Read Input Register */ - rc = i2c_reg_read_byte_dt(&cfg->i2c, PCA953X_INPUT_PORT, &input_pin_data); - - LOG_DBG("read %x got %d", input_pin_data, rc); - - if (rc == 0) { - drv_data->pin_state.input = input_pin_data; - *value = (gpio_port_value_t)(drv_data->pin_state.input); - } - - k_sem_give(&drv_data->lock); - return rc; -} - -static int gpio_pca953x_port_write(const struct device *dev, - gpio_port_pins_t mask, - gpio_port_value_t value, - gpio_port_value_t toggle) -{ - const struct pca953x_config *cfg = dev->config; - struct pca953x_drv_data *drv_data = dev->data; - uint8_t *outp = &drv_data->pin_state.output; - int rc; - uint8_t orig_out; - uint8_t out; - - /* Can't do I2C bus operations from an ISR */ - if (k_is_in_isr()) { - return -EWOULDBLOCK; - } - - k_sem_take(&drv_data->lock, K_FOREVER); - - orig_out = *outp; - out = ((orig_out & ~mask) | (value & mask)) ^ toggle; - - rc = i2c_reg_write_byte_dt(&cfg->i2c, PCA953X_OUTPUT_PORT, out); - - if (rc == 0) { - *outp = out; - } - - k_sem_give(&drv_data->lock); - - LOG_DBG("write %x msk %08x val %08x => %x: %d", orig_out, mask, - value, out, rc); - - return rc; -} - -static int gpio_pca953x_port_set_masked(const struct device *dev, - gpio_port_pins_t mask, - gpio_port_value_t value) -{ - return gpio_pca953x_port_write(dev, mask, value, 0); -} - -static int gpio_pca953x_port_set_bits(const struct device *dev, - gpio_port_pins_t pins) -{ - return gpio_pca953x_port_write(dev, pins, pins, 0); -} - -static int gpio_pca953x_port_clear_bits(const struct device *dev, - gpio_port_pins_t pins) -{ - return gpio_pca953x_port_write(dev, pins, 0, 0); -} - -static int gpio_pca953x_port_toggle_bits(const struct device *dev, - gpio_port_pins_t pins) -{ - return gpio_pca953x_port_write(dev, 0, 0, pins); -} - -static int gpio_pca953x_pin_interrupt_configure(const struct device *dev, - gpio_pin_t pin, - enum gpio_int_mode mode, - enum gpio_int_trig trig) -{ - const struct pca953x_config *cfg = dev->config; - struct pca953x_drv_data *drv_data = dev->data; - struct pca953x_irq_state *irq = &drv_data->irq_state; - - if (!cfg->interrupt_enabled) { - return -ENOTSUP; - } - /* Device does not support level-triggered interrupts. */ - if (mode == GPIO_INT_MODE_LEVEL) { - return -ENOTSUP; - } - - k_sem_take(&drv_data->lock, K_FOREVER); - - if (mode == GPIO_INT_MODE_DISABLED) { - irq->falling &= ~BIT(pin); - irq->rising &= ~BIT(pin); - } else { /* GPIO_INT_MODE_EDGE */ - if (trig == GPIO_INT_TRIG_BOTH) { - irq->falling |= BIT(pin); - irq->rising |= BIT(pin); - } else if (trig == GPIO_INT_TRIG_LOW) { - irq->falling |= BIT(pin); - irq->rising &= ~BIT(pin); - } else if (trig == GPIO_INT_TRIG_HIGH) { - irq->falling &= ~BIT(pin); - irq->rising |= BIT(pin); - } - } - - k_sem_give(&drv_data->lock); - - return 0; -} - -static int gpio_pca953x_manage_callback(const struct device *dev, - struct gpio_callback *callback, - bool set) -{ - struct pca953x_drv_data *data = dev->data; - - return gpio_manage_callback(&data->cb, callback, set); -} - -/** - * @brief Initialization function of PCA953X - * - * This sets initial input/ output configuration and output states. - * The interrupt is configured if this is enabled. - * - * @param dev Device struct - * @return 0 if successful, failed otherwise. - */ -static int gpio_pca953x_init(const struct device *dev) -{ - const struct pca953x_config *cfg = dev->config; - struct pca953x_drv_data *drv_data = dev->data; - int rc = 0; - - if (!device_is_ready(cfg->i2c.bus)) { - LOG_ERR("I2C bus device not found"); - goto out; - } - - /* Do an initial read, this clears the interrupt pin and sets - * up the initial value of the pin state input data. - */ - rc = update_input(dev); - if (rc) { - goto out; - } - - if (cfg->interrupt_enabled) { - if (!gpio_is_ready_dt(&cfg->gpio_int)) { - LOG_ERR("Cannot get pointer to gpio interrupt device"); - rc = -EINVAL; - goto out; - } - - drv_data->dev = dev; - - k_work_init(&drv_data->work, gpio_pca953x_work_handler); - - rc = gpio_pin_configure_dt(&cfg->gpio_int, GPIO_INPUT); - if (rc) { - goto out; - } - - rc = gpio_pin_interrupt_configure_dt(&cfg->gpio_int, - GPIO_INT_EDGE_TO_ACTIVE); - if (rc) { - goto out; - } - - gpio_init_callback(&drv_data->gpio_cb, - gpio_pca953x_init_cb, - BIT(cfg->gpio_int.pin)); - - rc = gpio_add_callback(cfg->gpio_int.port, - &drv_data->gpio_cb); - - if (rc) { - goto out; - } - - /* This may not present on all variants of device */ - if (cfg->input_latch > -1) { - rc = i2c_reg_write_byte_dt(&cfg->i2c, REG_INPUT_LATCH_PORT0, - cfg->input_latch); - } - if (cfg->interrupt_mask > -1) { - rc = i2c_reg_write_byte_dt(&cfg->i2c, REG_INT_MASK_PORT0, - cfg->interrupt_mask); - } - } -out: - if (rc) { - LOG_ERR("%s init failed: %d", dev->name, rc); - } else { - LOG_INF("%s init ok", dev->name); - } - return rc; -} - -static const struct gpio_driver_api api_table = { - .pin_configure = gpio_pca953x_config, - .port_get_raw = gpio_pca953x_port_read, - .port_set_masked_raw = gpio_pca953x_port_set_masked, - .port_set_bits_raw = gpio_pca953x_port_set_bits, - .port_clear_bits_raw = gpio_pca953x_port_clear_bits, - .port_toggle_bits = gpio_pca953x_port_toggle_bits, - .pin_interrupt_configure = gpio_pca953x_pin_interrupt_configure, - .manage_callback = gpio_pca953x_manage_callback, -}; - -#define GPIO_PCA953X_INIT(n) \ - static const struct pca953x_config pca953x_cfg_##n = { \ - .i2c = I2C_DT_SPEC_INST_GET(n), \ - .common = { \ - .port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(n), \ - }, \ - .interrupt_enabled = DT_INST_NODE_HAS_PROP(n, nint_gpios), \ - .gpio_int = GPIO_DT_SPEC_INST_GET_OR(n, nint_gpios, {0}), \ - .interrupt_mask = DT_INST_PROP_OR(n, interrupt_mask, -1), \ - .input_latch = DT_INST_PROP_OR(n, input_latch, -1), \ - }; \ - \ - static struct pca953x_drv_data pca953x_drvdata_##n = { \ - .lock = Z_SEM_INITIALIZER(pca953x_drvdata_##n.lock, 1, 1), \ - .pin_state.dir = ALL_PINS, \ - .pin_state.output = ALL_PINS, \ - }; \ - DEVICE_DT_INST_DEFINE(n, \ - gpio_pca953x_init, \ - NULL, \ - &pca953x_drvdata_##n, \ - &pca953x_cfg_##n, \ - POST_KERNEL, \ - CONFIG_GPIO_PCA953X_INIT_PRIORITY, \ - &api_table); - -#define DT_DRV_COMPAT ti_tca9538 -DT_INST_FOREACH_STATUS_OKAY(GPIO_PCA953X_INIT) diff --git a/dts/bindings/gpio/ti,tca9538.yaml b/dts/bindings/gpio/ti,tca9538.yaml deleted file mode 100644 index 9200a9a16061a3..00000000000000 --- a/dts/bindings/gpio/ti,tca9538.yaml +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) 2018, Aapo Vienamo -# Copyright (c) 2021, Laird Connectivity -# SPDX-License-Identifier: Apache-2.0 - -description: TCA9538 GPIO node - -compatible: "ti,tca9538" - -include: [i2c-device.yaml, gpio-controller.yaml] - -properties: - "#gpio-cells": - const: 2 - - ngpios: - required: true - const: 8 - description: | - Number of GPIOs available on port expander. - - nint-gpios: - type: phandle-array - description: | - Connection for the NINT signal. This signal is active-low when - produced by tca9538 GPIO node. - - input-latch: - type: int - description: | - Input latch register bit is 0 by default and the input pin state - is not latched. When input latch register bit is 1 and the input - pin state is latched. - - interrupt-mask: - type: int - description: | - Interrupt mask register is set to logic 1 by default without - enabling interrupts. Setting corresponding mask bits to logic - 0 to enable the interrupts. - -gpio-cells: - - pin - - flags diff --git a/tests/drivers/build_all/gpio/app.overlay b/tests/drivers/build_all/gpio/app.overlay index 837e667a9c8e04..37662234d5dfc7 100644 --- a/tests/drivers/build_all/gpio/app.overlay +++ b/tests/drivers/build_all/gpio/app.overlay @@ -132,15 +132,6 @@ int-gpios = <&test_gpio 0 0>; }; - test_i2c_pca953x: pca953x@6 { - compatible = "ti,tca9538"; - reg = <0x06>; - gpio-controller; - #gpio-cells = <2>; - ngpios = <8>; - nint-gpios = <&test_gpio 0 0>; - }; - test_i2c_mcp23017: mcp23017@7 { compatible = "microchip,mcp23017"; reg = <0x07>; From c32921c4b66e11e158309a3e42846b7789237f9d Mon Sep 17 00:00:00 2001 From: Chekhov Ma Date: Thu, 19 Sep 2024 15:37:02 +0800 Subject: [PATCH 3/6] drivers: gpio: pca_series: add support for pca957x add support for pca9574 and pca9575 devices Signed-off-by: Chekhov Ma --- drivers/gpio/Kconfig.pca_series | 1 + drivers/gpio/gpio_pca_series.c | 140 +++++++++++++++++++++++ dts/bindings/gpio/nxp,pca9574.yaml | 10 ++ dts/bindings/gpio/nxp,pca9575.yaml | 10 ++ tests/drivers/build_all/gpio/app.overlay | 11 +- 5 files changed, 171 insertions(+), 1 deletion(-) create mode 100644 dts/bindings/gpio/nxp,pca9574.yaml create mode 100644 dts/bindings/gpio/nxp,pca9575.yaml diff --git a/drivers/gpio/Kconfig.pca_series b/drivers/gpio/Kconfig.pca_series index 95387ee3892ea6..a8bf5363ae2b58 100644 --- a/drivers/gpio/Kconfig.pca_series +++ b/drivers/gpio/Kconfig.pca_series @@ -8,6 +8,7 @@ menuconfig GPIO_PCA_SERIES default y 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_PCA9574_ENABLED || DT_HAS_NXP_PCA9575_ENABLED || \ DT_HAS_NXP_PCAL6524_ENABLED || DT_HAS_NXP_PCAL6534_ENABLED depends on I2C help diff --git a/drivers/gpio/gpio_pca_series.c b/drivers/gpio/gpio_pca_series.c index 6d7744e4eb2a73..b8db9952edb507 100644 --- a/drivers/gpio/gpio_pca_series.c +++ b/drivers/gpio/gpio_pca_series.c @@ -47,6 +47,8 @@ enum gpio_pca_series_part_no { PCA_PART_NO_PCA9539, PCA_PART_NO_PCA9554, PCA_PART_NO_PCA9555, + PCA_PART_NO_PCA9574, + PCA_PART_NO_PCA9575, PCA_PART_NO_PCAL6524, PCA_PART_NO_PCAL6534, }; @@ -61,6 +63,8 @@ const char *const gpio_pca_series_part_name[] = { "pca9539", "pca9554", "pca9555", + "pca9574", + "pca9575", "pcal6524", "pcal6534", }; @@ -2045,6 +2049,134 @@ const struct gpio_pca_series_part_config gpio_pca_series_part_cfg_pca9555 = { #endif /* CONFIG_GPIO_PCA_SERIES_CACHE_ALL */ }; +/** + * @brief implement pca957x driver + * + * @note flags = PCA_HAS_PULL + * | PCA_HAS_INT_MASK + * + * api set : standard + * ngpios : 8, 16 + * part_no : pca9574 pca9575 + */ +#define GPIO_PCA_SERIES_FLAG_TYPE_1 (PCA_HAS_PULL | PCA_HAS_INT_MASK) + +#ifdef CONFIG_GPIO_PCA_SERIES_CACHE_ALL +/** + * cache map for flag = PCA_HAS_PULL + * | PCA_HAS_INT_MASK + */ +static const uint8_t gpio_pca_series_cache_map_pca957x[] = { + 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*/ + 0x02, /** pull_enable if PCA_HAS_PULL */ + 0x03, /** pull_select if PCA_HAS_PULL */ + PCA_REG_INVALID, /** input_status if PCA_HAS_OUT_CONFIG, non-cacheable */ + 0x04, /** 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 + 0x05, /** 1b_input_history if PCA_HAS_LATCH and not PCA_HAS_INT_EXTEND */ + 0x06, /** 1b_interrupt_rise if PCA_HAS_LATCH and not PCA_HAS_INT_EXTEND */ + 0x07, /** 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_pca9574[] = { + 0x00, /** input_port if not PCA_HAS_OUT_CONFIG, non-cacheable */ + 0x05, /** output_port */ +/* 0x01, polarity_inversion (unused, omitted) */ + 0x04, /** configuration */ + PCA_REG_INVALID, /** 2b_output_drive_strength if PCA_HAS_LATCH*/ + PCA_REG_INVALID, /** input_latch if PCA_HAS_LATCH*/ + 0x02, /** pull_enable if PCA_HAS_PULL */ + 0x03, /** 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 + 0x06, /** interrupt_mask if PCA_HAS_INT_MASK, + * non-cacheable if not PCA_HAS_INT_EXTEND + */ + 0x07, /** 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_PCA9574 (1U) +#define GPIO_PCA_FLAG_PCA_PART_NO_PCA9574 GPIO_PCA_SERIES_FLAG_TYPE_1 +#define GPIO_PCA_PART_CFG_PCA_PART_NO_PCA9574 (&gpio_pca_series_part_cfg_pca9574) + +const struct gpio_pca_series_part_config gpio_pca_series_part_cfg_pca9574 = { + .port_no = GPIO_PCA_PORT_NO_PCA_PART_NO_PCA9574, + .flags = GPIO_PCA_FLAG_PCA_PART_NO_PCA9574, + .regs = gpio_pca_series_reg_pca9574, +#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_PCA9574), +# endif /* GPIO_NXP_PCA_SERIES_DEBUG */ + .cache_map = gpio_pca_series_cache_map_pca957x, +#endif /* CONFIG_GPIO_PCA_SERIES_CACHE_ALL */ +}; + +static const uint8_t gpio_pca_series_reg_pca9575[] = { + 0x00, /** input_port if not PCA_HAS_OUT_CONFIG, non-cacheable */ + 0x0a, /** output_port */ +/* 0x02, polarity_inversion (unused, omitted) */ + 0x08, /** configuration */ + PCA_REG_INVALID, /** 2b_output_drive_strength if PCA_HAS_LATCH*/ + PCA_REG_INVALID, /** input_latch if PCA_HAS_LATCH*/ + 0x04, /** pull_enable if PCA_HAS_PULL */ + 0x06, /** 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 + 0x0c, /** interrupt_mask if PCA_HAS_INT_MASK, + * non-cacheable if not PCA_HAS_INT_EXTEND + */ + 0x0e, /** 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_PCA9575 (2U) +#define GPIO_PCA_FLAG_PCA_PART_NO_PCA9575 GPIO_PCA_SERIES_FLAG_TYPE_1 +#define GPIO_PCA_PART_CFG_PCA_PART_NO_PCA9575 (&gpio_pca_series_part_cfg_pca9575) + +const struct gpio_pca_series_part_config gpio_pca_series_part_cfg_pca9575 = { + .port_no = GPIO_PCA_PORT_NO_PCA_PART_NO_PCA9575, + .flags = GPIO_PCA_FLAG_PCA_PART_NO_PCA9575, + .regs = gpio_pca_series_reg_pca9575, +#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_PCA9575), +# endif /* GPIO_NXP_PCA_SERIES_DEBUG */ + .cache_map = gpio_pca_series_cache_map_pca957x, +#endif /* CONFIG_GPIO_PCA_SERIES_CACHE_ALL */ +}; + /** * @brief implement pcal65xx driver * @@ -2228,6 +2360,14 @@ DT_INST_FOREACH_STATUS_OKAY_VARGS(GPIO_PCA_SERIES_DEVICE_INSTANCE, PCA_PART_NO_P #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_pca9574 +DT_INST_FOREACH_STATUS_OKAY_VARGS(GPIO_PCA_SERIES_DEVICE_INSTANCE, PCA_PART_NO_PCA9574) + +#undef DT_DRV_COMPAT +#define DT_DRV_COMPAT nxp_pca9575 +DT_INST_FOREACH_STATUS_OKAY_VARGS(GPIO_PCA_SERIES_DEVICE_INSTANCE, PCA_PART_NO_PCA9575) + #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,pca9574.yaml b/dts/bindings/gpio/nxp,pca9574.yaml new file mode 100644 index 00000000000000..3cca4cc4939eb3 --- /dev/null +++ b/dts/bindings/gpio/nxp,pca9574.yaml @@ -0,0 +1,10 @@ +# Copyright 2024 NXP +# SPDX-License-Identifier: Apache-2.0 +description: NXP PCA9574 I2C GPIO expander node +compatible: nxp,pca9574 +include: nxp,pca_series.yaml + +properties: + ngpios: + required: true + const: 8 diff --git a/dts/bindings/gpio/nxp,pca9575.yaml b/dts/bindings/gpio/nxp,pca9575.yaml new file mode 100644 index 00000000000000..bd86005bf4efe9 --- /dev/null +++ b/dts/bindings/gpio/nxp,pca9575.yaml @@ -0,0 +1,10 @@ +# Copyright 2024 NXP +# SPDX-License-Identifier: Apache-2.0 +description: NXP PCA9575 I2C GPIO expander node +compatible: nxp,pca9575 +include: nxp,pca_series.yaml + +properties: + ngpios: + required: true + const: 16 diff --git a/tests/drivers/build_all/gpio/app.overlay b/tests/drivers/build_all/gpio/app.overlay index 37662234d5dfc7..916766d69ad85e 100644 --- a/tests/drivers/build_all/gpio/app.overlay +++ b/tests/drivers/build_all/gpio/app.overlay @@ -105,7 +105,7 @@ int-gpios = <&test_gpio 0 0>; }; - test_i2c_pca95xx: pca9555@3 { + test_i2c_pca9555: pca9555@3 { compatible = "nxp,pca9555"; reg = <0x03>; gpio-controller; @@ -114,6 +114,15 @@ int-gpios = <&test_gpio 0 0>; }; + test_i2c_pca9575: pca9575@4 { + compatible = "nxp,pca9575"; + reg = <0x04>; + gpio-controller; + #gpio-cells = <2>; + ngpios = <16>; + int-gpios = <&test_gpio 0 0>; + }; + test_i2c_pcf8575: pcf8575@4 { compatible = "nxp,pcf857x"; reg = <0x04>; From 0bd22ddaa2681bd5011dd5817751f8d6160507e9 Mon Sep 17 00:00:00 2001 From: Chekhov Ma Date: Thu, 19 Sep 2024 14:36:24 +0800 Subject: [PATCH 4/6] drivers: gpio: pca_series: add support for pcal953x and pcal64xxa add support for pca9538, pcal9539, pcal6408a and pcal6416a devices Signed-off-by: Chekhov Ma --- drivers/gpio/Kconfig.pca_series | 2 + drivers/gpio/gpio_pca_series.c | 200 ++++++++++++++++++++++++++ dts/bindings/gpio/nxp,pca_series.yaml | 1 + dts/bindings/gpio/nxp,pcal6408a.yaml | 10 +- dts/bindings/gpio/nxp,pcal6416a.yaml | 10 +- dts/bindings/gpio/nxp,pcal9538.yaml | 10 ++ dts/bindings/gpio/nxp,pcal9539.yaml | 10 ++ 7 files changed, 231 insertions(+), 12 deletions(-) create mode 100644 dts/bindings/gpio/nxp,pcal9538.yaml create mode 100644 dts/bindings/gpio/nxp,pcal9539.yaml diff --git a/drivers/gpio/Kconfig.pca_series b/drivers/gpio/Kconfig.pca_series index a8bf5363ae2b58..5a1493cd7616d6 100644 --- a/drivers/gpio/Kconfig.pca_series +++ b/drivers/gpio/Kconfig.pca_series @@ -9,6 +9,8 @@ menuconfig GPIO_PCA_SERIES 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_PCA9574_ENABLED || DT_HAS_NXP_PCA9575_ENABLED || \ + DT_HAS_NXP_PCAL9538_ENABLED || DT_HAS_NXP_PCAL9539_ENABLED || \ + DT_HAS_NXP_PCAL6408A_ENABLED || DT_HAS_NXP_PCAL6416A_ENABLED || \ DT_HAS_NXP_PCAL6524_ENABLED || DT_HAS_NXP_PCAL6534_ENABLED depends on I2C help diff --git a/drivers/gpio/gpio_pca_series.c b/drivers/gpio/gpio_pca_series.c index b8db9952edb507..653e30c1311615 100644 --- a/drivers/gpio/gpio_pca_series.c +++ b/drivers/gpio/gpio_pca_series.c @@ -49,6 +49,10 @@ enum gpio_pca_series_part_no { PCA_PART_NO_PCA9555, PCA_PART_NO_PCA9574, PCA_PART_NO_PCA9575, + PCA_PART_NO_PCAL9538, + PCA_PART_NO_PCAL9539, + PCA_PART_NO_PCAL6408A, + PCA_PART_NO_PCAL6416A, PCA_PART_NO_PCAL6524, PCA_PART_NO_PCAL6534, }; @@ -65,6 +69,10 @@ const char *const gpio_pca_series_part_name[] = { "pca9555", "pca9574", "pca9575", + "pcal9538", + "pcal9539", + "pcal6408a", + "pcal6416a", "pcal6524", "pcal6534", }; @@ -2177,6 +2185,182 @@ const struct gpio_pca_series_part_config gpio_pca_series_part_cfg_pca9575 = { #endif /* CONFIG_GPIO_PCA_SERIES_CACHE_ALL */ }; +/** + * @brief implement pcal953x and pcal64xxa driver + * + * @note flags = PCA_HAS_LATCH + * | PCA_HAS_PULL + * | PCA_HAS_INT_MASK + * + * api set : standard + * + * ngpios : 8, 16; + * part_no : pcal9534 pcal9538 pcal6408a + * pcal9535 pcal9539 pcal6416a + */ +#define GPIO_PCA_SERIES_FLAG_TYPE_2 (PCA_HAS_LATCH | PCA_HAS_PULL | PCA_HAS_INT_MASK) + +#ifdef CONFIG_GPIO_PCA_SERIES_CACHE_ALL +/** + * cache map for flag = PCA_HAS_LATCH + * | PCA_HAS_PULL + * | PCA_HAS_INT_MASK + */ +static const uint8_t gpio_pca_series_cache_map_pcal953x[] = { + PCA_REG_INVALID, /** input_port if not PCA_HAS_OUT_CONFIG, non-cacheable */ + 0x00, /** output_port */ +/* 0x02, polarity_inversion (unused, omitted) */ + 0x01, /** configuration */ + 0x02, /** 2b_output_drive_strength if PCA_HAS_LATCH*/ + 0x04, /** input_latch if PCA_HAS_LATCH*/ + 0x05, /** pull_enable if PCA_HAS_PULL */ + 0x06, /** 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 + 0x07, /** 1b_input_history if PCA_HAS_LATCH and not PCA_HAS_INT_EXTEND */ + 0x08, /** 1b_interrupt_rise if PCA_HAS_LATCH and not PCA_HAS_INT_EXTEND */ + 0x09, /** 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_pcal9538[] = { + 0x00, /** input_port if not PCA_HAS_OUT_CONFIG, non-cacheable */ + 0x01, /** output_port */ +/* 0x02, polarity_inversion (unused, omitted) */ + 0x03, /** configuration */ + 0x40, /** 2b_output_drive_strength if PCA_HAS_LATCH*/ + 0x42, /** input_latch if PCA_HAS_LATCH*/ + 0x43, /** pull_enable if PCA_HAS_PULL */ + 0x44, /** 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 + 0x45, /** interrupt_mask if PCA_HAS_INT_MASK, + * non-cacheable if not PCA_HAS_INT_EXTEND + */ + 0x46, /** 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_PCAL9538 (1U) +#define GPIO_PCA_FLAG_PCA_PART_NO_PCAL9538 GPIO_PCA_SERIES_FLAG_TYPE_2 +#define GPIO_PCA_PART_CFG_PCA_PART_NO_PCAL9538 (&gpio_pca_series_part_cfg_pcal9538) + +const struct gpio_pca_series_part_config gpio_pca_series_part_cfg_pcal9538 = { + .port_no = GPIO_PCA_PORT_NO_PCA_PART_NO_PCAL9538, + .flags = GPIO_PCA_FLAG_PCA_PART_NO_PCAL9538, + .regs = gpio_pca_series_reg_pcal9538, +#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_PCAL9538), +# endif /* GPIO_NXP_PCA_SERIES_DEBUG */ + .cache_map = gpio_pca_series_cache_map_pcal953x, +#endif /* CONFIG_GPIO_PCA_SERIES_CACHE_ALL */ +}; + +/** + * pcal6408a share the same register layout with pcal9538, with + * additional voltage level translation capability. + * no difference from driver perspective. + */ + +#define GPIO_PCA_PORT_NO_PCA_PART_NO_PCAL6408A GPIO_PCA_PORT_NO_PCA_PART_NO_PCAL9538 +#define GPIO_PCA_FLAG_PCA_PART_NO_PCAL6408A GPIO_PCA_FLAG_PCA_PART_NO_PCAL9538 +#define GPIO_PCA_PART_CFG_PCA_PART_NO_PCAL6408A (&gpio_pca_series_part_cfg_pcal6408a) + +const struct gpio_pca_series_part_config gpio_pca_series_part_cfg_pcal6408a = { + .port_no = GPIO_PCA_PORT_NO_PCA_PART_NO_PCAL6408A, + .flags = GPIO_PCA_FLAG_PCA_PART_NO_PCAL6408A, + .regs = gpio_pca_series_reg_pcal9538, +#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_PCAL6408A), +# endif /* GPIO_NXP_PCA_SERIES_DEBUG */ + .cache_map = gpio_pca_series_cache_map_pcal953x, +#endif /* CONFIG_GPIO_PCA_SERIES_CACHE_ALL */ +}; + +static const uint8_t gpio_pca_series_reg_pcal9539[] = { + 0x00, /** input_port if not PCA_HAS_OUT_CONFIG, non-cacheable */ + 0x02, /** output_port */ +/* 0x04, polarity_inversion (unused, omitted) */ + 0x06, /** configuration */ + 0x40, /** 2b_output_drive_strength if PCA_HAS_LATCH*/ + 0x44, /** input_latch if PCA_HAS_LATCH*/ + 0x46, /** pull_enable if PCA_HAS_PULL */ + 0x48, /** 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 + 0x4a, /** interrupt_mask if PCA_HAS_INT_MASK, + * non-cacheable if not PCA_HAS_INT_EXTEND + */ + 0x4c, /** 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_PCAL9539 (2U) +#define GPIO_PCA_FLAG_PCA_PART_NO_PCAL9539 GPIO_PCA_SERIES_FLAG_TYPE_2 +#define GPIO_PCA_PART_CFG_PCA_PART_NO_PCAL9539 (&gpio_pca_series_part_cfg_pcal9539) + +const struct gpio_pca_series_part_config gpio_pca_series_part_cfg_pcal9539 = { + .port_no = GPIO_PCA_PORT_NO_PCA_PART_NO_PCAL9539, + .flags = GPIO_PCA_FLAG_PCA_PART_NO_PCAL9539, + .regs = gpio_pca_series_reg_pcal9539, +#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_PCAL9539), +# endif /* GPIO_NXP_PCA_SERIES_DEBUG */ + .cache_map = gpio_pca_series_cache_map_pcal953x, +#endif /* CONFIG_GPIO_PCA_SERIES_CACHE_ALL */ +}; + +/** + * pcal6416a share the same register layout with pcal9539, with + * additional voltage level translation capability. + * no difference from driver perspective. + */ + +#define GPIO_PCA_PORT_NO_PCA_PART_NO_PCAL6416A GPIO_PCA_PORT_NO_PCA_PART_NO_PCAL9539 +#define GPIO_PCA_FLAG_PCA_PART_NO_PCAL6416A GPIO_PCA_FLAG_PCA_PART_NO_PCAL9539 +#define GPIO_PCA_PART_CFG_PCA_PART_NO_PCAL6416A (&gpio_pca_series_part_cfg_pcal6416a) + +const struct gpio_pca_series_part_config gpio_pca_series_part_cfg_pcal6416a = { + .port_no = GPIO_PCA_PORT_NO_PCA_PART_NO_PCAL6416A, + .flags = GPIO_PCA_FLAG_PCA_PART_NO_PCAL6416A, + .regs = gpio_pca_series_reg_pcal9539, +#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_PCAL6416A), +# endif /* GPIO_NXP_PCA_SERIES_DEBUG */ + .cache_map = gpio_pca_series_cache_map_pcal953x, +#endif /* CONFIG_GPIO_PCA_SERIES_CACHE_ALL */ +}; + /** * @brief implement pcal65xx driver * @@ -2368,6 +2552,22 @@ DT_INST_FOREACH_STATUS_OKAY_VARGS(GPIO_PCA_SERIES_DEVICE_INSTANCE, PCA_PART_NO_P #define DT_DRV_COMPAT nxp_pca9575 DT_INST_FOREACH_STATUS_OKAY_VARGS(GPIO_PCA_SERIES_DEVICE_INSTANCE, PCA_PART_NO_PCA9575) +#undef DT_DRV_COMPAT +#define DT_DRV_COMPAT nxp_pcal9538 +DT_INST_FOREACH_STATUS_OKAY_VARGS(GPIO_PCA_SERIES_DEVICE_INSTANCE, PCA_PART_NO_PCAL9538) + +#undef DT_DRV_COMPAT +#define DT_DRV_COMPAT nxp_pcal9539 +DT_INST_FOREACH_STATUS_OKAY_VARGS(GPIO_PCA_SERIES_DEVICE_INSTANCE, PCA_PART_NO_PCAL9539) + +#undef DT_DRV_COMPAT +#define DT_DRV_COMPAT nxp_pcal6408a +DT_INST_FOREACH_STATUS_OKAY_VARGS(GPIO_PCA_SERIES_DEVICE_INSTANCE, PCA_PART_NO_PCAL6408A) + +#undef DT_DRV_COMPAT +#define DT_DRV_COMPAT nxp_pcal6416a +DT_INST_FOREACH_STATUS_OKAY_VARGS(GPIO_PCA_SERIES_DEVICE_INSTANCE, PCA_PART_NO_PCAL6416A) + #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,pca_series.yaml b/dts/bindings/gpio/nxp,pca_series.yaml index a37fa281e3b729..533c14e26a7c78 100644 --- a/dts/bindings/gpio/nxp,pca_series.yaml +++ b/dts/bindings/gpio/nxp,pca_series.yaml @@ -7,6 +7,7 @@ include: [gpio-controller.yaml, i2c-device.yaml] # Currently supported device: # pca9538 pca9539 +# pca9554 pca9555 # pca9574 pca9575 # pcal9538 pcal9539 # pcal6408a pcal6416a diff --git a/dts/bindings/gpio/nxp,pcal6408a.yaml b/dts/bindings/gpio/nxp,pcal6408a.yaml index 4acf381614784c..1f68325c07b061 100644 --- a/dts/bindings/gpio/nxp,pcal6408a.yaml +++ b/dts/bindings/gpio/nxp,pcal6408a.yaml @@ -1,12 +1,10 @@ # Copyright (c) 2021 Nordic Semiconductor ASA # Copyright (c) 2023 SILA Embedded Solutions GmbH +# Copyright 2024 NXP # SPDX-License-Identifier: Apache-2.0 - -description: PCAL6408A 8-bit I2C-based I/O expander - -compatible: "nxp,pcal6408a" - -include: nxp,pcal64xxa-base.yaml +description: NXP PCAL6408A I2C GPIO expander node +compatible: nxp,pcal6408a +include: nxp,pca_series.yaml properties: ngpios: diff --git a/dts/bindings/gpio/nxp,pcal6416a.yaml b/dts/bindings/gpio/nxp,pcal6416a.yaml index 7717ca838308d1..c9dd2d7f9b71ba 100644 --- a/dts/bindings/gpio/nxp,pcal6416a.yaml +++ b/dts/bindings/gpio/nxp,pcal6416a.yaml @@ -1,11 +1,9 @@ # Copyright (c) 2023 SILA Embedded Solutions GmbH +# Copyright 2024 NXP # SPDX-License-Identifier: Apache-2.0 - -description: PCAL6416A 16-bit I2C-based I/O expander - -compatible: "nxp,pcal6416a" - -include: nxp,pcal64xxa-base.yaml +description: NXP PCAL6416A I2C GPIO expander node +compatible: nxp,pcal6416a +include: nxp,pca_series.yaml properties: ngpios: diff --git a/dts/bindings/gpio/nxp,pcal9538.yaml b/dts/bindings/gpio/nxp,pcal9538.yaml new file mode 100644 index 00000000000000..46f0ae43f5555b --- /dev/null +++ b/dts/bindings/gpio/nxp,pcal9538.yaml @@ -0,0 +1,10 @@ +# Copyright 2024 NXP +# SPDX-License-Identifier: Apache-2.0 +description: NXP PCAL9538 I2C GPIO expander node +compatible: nxp,pcal9538 +include: nxp,pca_series.yaml + +properties: + ngpios: + required: true + const: 8 diff --git a/dts/bindings/gpio/nxp,pcal9539.yaml b/dts/bindings/gpio/nxp,pcal9539.yaml new file mode 100644 index 00000000000000..0cc82662ca1882 --- /dev/null +++ b/dts/bindings/gpio/nxp,pcal9539.yaml @@ -0,0 +1,10 @@ +# Copyright 2024 NXP +# SPDX-License-Identifier: Apache-2.0 +description: NXP PCAL9539 I2C GPIO expander node +compatible: nxp,pcal9539 +include: nxp,pca_series.yaml + +properties: + ngpios: + required: true + const: 16 From a014789f141b61d92841ed31b47a4dd04957faf8 Mon Sep 17 00:00:00 2001 From: Chekhov Ma Date: Thu, 19 Sep 2024 14:10:58 +0800 Subject: [PATCH 5/6] drivers: gpio: retire pcal64xxa driver pcal64xxa driver can be replaced by pca_series driver, which covers a larger number of devices. Signed-off-by: Chekhov Ma --- drivers/gpio/CMakeLists.txt | 1 - drivers/gpio/Kconfig | 1 - drivers/gpio/Kconfig.pcal64xxa | 20 - drivers/gpio/gpio_pcal64xxa.c | 1168 --------------------- dts/bindings/gpio/nxp,pcal64xxa-base.yaml | 32 - 5 files changed, 1222 deletions(-) delete mode 100644 drivers/gpio/Kconfig.pcal64xxa delete mode 100644 drivers/gpio/gpio_pcal64xxa.c delete mode 100644 dts/bindings/gpio/nxp,pcal64xxa-base.yaml diff --git a/drivers/gpio/CMakeLists.txt b/drivers/gpio/CMakeLists.txt index 28147793f13664..d6d58fe9a322fd 100644 --- a/drivers/gpio/CMakeLists.txt +++ b/drivers/gpio/CMakeLists.txt @@ -63,7 +63,6 @@ zephyr_library_sources_ifdef(CONFIG_GPIO_NRFX gpio_nrfx.c) zephyr_library_sources_ifdef(CONFIG_GPIO_NUMAKER gpio_numaker.c) zephyr_library_sources_ifdef(CONFIG_GPIO_NUMICRO gpio_numicro.c) zephyr_library_sources_ifdef(CONFIG_GPIO_NXP_S32 gpio_nxp_s32.c) -zephyr_library_sources_ifdef(CONFIG_GPIO_PCAL64XXA gpio_pcal64xxa.c) zephyr_library_sources_ifdef(CONFIG_GPIO_PCA_SERIES gpio_pca_series.c) zephyr_library_sources_ifdef(CONFIG_GPIO_PCF857X gpio_pcf857x.c) zephyr_library_sources_ifdef(CONFIG_GPIO_PSOC6 gpio_psoc6.c) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 794d856661c5ba..686eb365c9a915 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -151,7 +151,6 @@ source "drivers/gpio/Kconfig.numaker" source "drivers/gpio/Kconfig.numicro" source "drivers/gpio/Kconfig.nxp_s32" source "drivers/gpio/Kconfig.pca_series" -source "drivers/gpio/Kconfig.pcal64xxa" source "drivers/gpio/Kconfig.pcf857x" source "drivers/gpio/Kconfig.psoc6" source "drivers/gpio/Kconfig.rcar" diff --git a/drivers/gpio/Kconfig.pcal64xxa b/drivers/gpio/Kconfig.pcal64xxa deleted file mode 100644 index c04b3d694069b0..00000000000000 --- a/drivers/gpio/Kconfig.pcal64xxa +++ /dev/null @@ -1,20 +0,0 @@ -# PCAL6408a GPIO configuration options - -# Copyright (c) 2021 Nordic Semiconductor ASA -# Copyright (c) 2023 SILA Embedded Solutions GmbH -# SPDX-License-Identifier: Apache-2.0 - -menuconfig GPIO_PCAL64XXA - bool "PCAL64XXA I2C GPIO chip" - default y - depends on DT_HAS_NXP_PCAL6408A_ENABLED || DT_HAS_NXP_PCAL6416A_ENABLED - select I2C - help - Enable driver for PCAL64XXA I2C GPIO chip. - -config GPIO_PCAL64XXA_INIT_PRIORITY - int "Init priority" - default 70 - depends on GPIO_PCAL64XXA - help - Device driver initialization priority. diff --git a/drivers/gpio/gpio_pcal64xxa.c b/drivers/gpio/gpio_pcal64xxa.c deleted file mode 100644 index b9b5a8383e3592..00000000000000 --- a/drivers/gpio/gpio_pcal64xxa.c +++ /dev/null @@ -1,1168 +0,0 @@ -/* - * Copyright (c) 2021 Nordic Semiconductor ASA - * Copyright (c) 2024 SILA Embedded Solutions GmbH - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -#include -#include -#include -#include -#include - -LOG_MODULE_REGISTER(pcal64xxa, CONFIG_GPIO_LOG_LEVEL); - -enum pcal6408a_register { - PCAL6408A_REG_INPUT_PORT = 0x00, - PCAL6408A_REG_OUTPUT_PORT = 0x01, - PCAL6408A_REG_POLARITY_INVERSION = 0x02, - PCAL6408A_REG_CONFIGURATION = 0x03, - PCAL6408A_REG_OUTPUT_DRIVE_STRENGTH_0 = 0x40, - PCAL6408A_REG_OUTPUT_DRIVE_STRENGTH_1 = 0x41, - PCAL6408A_REG_INPUT_LATCH = 0x42, - PCAL6408A_REG_PULL_UP_DOWN_ENABLE = 0x43, - PCAL6408A_REG_PULL_UP_DOWN_SELECT = 0x44, - PCAL6408A_REG_INTERRUPT_MASK = 0x45, - PCAL6408A_REG_INTERRUPT_STATUS = 0x46, - PCAL6408A_REG_OUTPUT_PORT_CONFIGURATION = 0x4f, -}; - -enum pcal6416a_register { - PCAL6416A_REG_INPUT_PORT_0 = 0x00, - PCAL6416A_REG_INPUT_PORT_1 = 0x01, - PCAL6416A_REG_OUTPUT_PORT_0 = 0x02, - PCAL6416A_REG_OUTPUT_PORT_1 = 0x03, - PCAL6416A_REG_POLARITY_INVERSION_0 = 0x04, - PCAL6416A_REG_POLARITY_INVERSION_1 = 0x05, - PCAL6416A_REG_CONFIGURATION_0 = 0x06, - PCAL6416A_REG_CONFIGURATION_1 = 0x07, - PCAL6416A_REG_OUTPUT_DRIVE_STRENGTH_0_0 = 0x40, - PCAL6416A_REG_OUTPUT_DRIVE_STRENGTH_0_1 = 0x41, - PCAL6416A_REG_OUTPUT_DRIVE_STRENGTH_1_0 = 0x42, - PCAL6416A_REG_OUTPUT_DRIVE_STRENGTH_1_1 = 0x43, - PCAL6416A_REG_INPUT_LATCH_0 = 0x44, - PCAL6416A_REG_INPUT_LATCH_1 = 0x45, - PCAL6416A_REG_PULL_UP_DOWN_ENABLE_0 = 0x46, - PCAL6416A_REG_PULL_UP_DOWN_ENABLE_1 = 0x47, - PCAL6416A_REG_PULL_UP_DOWN_SELECT_0 = 0x48, - PCAL6416A_REG_PULL_UP_DOWN_SELECT_1 = 0x49, - PCAL6416A_REG_INTERRUPT_MASK_0 = 0x4A, - PCAL6416A_REG_INTERRUPT_MASK_1 = 0x4B, - PCAL6416A_REG_INTERRUPT_STATUS_0 = 0x4C, - PCAL6416A_REG_INTERRUPT_STATUS_1 = 0x4D, - PCAL6416A_REG_OUTPUT_PORT_CONFIGURATION = 0x4F, -}; - -#if DT_HAS_COMPAT_STATUS_OKAY(nxp_pcal6416a) -typedef uint16_t pcal64xxa_data_t; -#define PCAL64XXA_INIT_HIGH UINT16_MAX -#define PRIpcal_data "04" PRIx16 -#elif DT_HAS_COMPAT_STATUS_OKAY(nxp_pcal6408a) -typedef uint8_t pcal64xxa_data_t; -#define PCAL64XXA_INIT_HIGH UINT8_MAX -#define PRIpcal_data "02" PRIx8 -#else -#error "Cannot determine the internal data type size" -#endif - -struct pcal64xxa_pins_cfg { - pcal64xxa_data_t configured_as_inputs; - pcal64xxa_data_t outputs_high; - pcal64xxa_data_t pull_ups_selected; - pcal64xxa_data_t pulls_enabled; -}; - -struct pcal64xxa_triggers { - pcal64xxa_data_t masked; - pcal64xxa_data_t dual_edge; - pcal64xxa_data_t on_low; -}; - -struct pcal64xxa_drv_data { - /* gpio_driver_data needs to be first */ - struct gpio_driver_data common; - - sys_slist_t callbacks; - struct k_sem lock; - struct k_work work; - const struct device *dev; - struct gpio_callback int_gpio_cb; - struct pcal64xxa_pins_cfg pins_cfg; - struct pcal64xxa_triggers triggers; - pcal64xxa_data_t input_port_last; -}; - -typedef int (*pcal64xxa_pins_cfg_apply)(const struct i2c_dt_spec *i2c, - const struct pcal64xxa_pins_cfg *pins_cfg); -typedef int (*pcal64xxa_pins_cfg_read)(const struct i2c_dt_spec *i2c, - struct pcal64xxa_pins_cfg *pins_cfg); -typedef int (*pcal64xxa_triggers_apply)(const struct i2c_dt_spec *i2c, - const struct pcal64xxa_triggers *triggers); -typedef int (*pcal64xxa_reset_state_apply)(const struct i2c_dt_spec *i2c); -typedef int (*pcal64xxa_inputs_read)(const struct i2c_dt_spec *i2c, pcal64xxa_data_t *int_sources, - pcal64xxa_data_t *input_port); -typedef int (*pcal64xxa_outputs_write)(const struct i2c_dt_spec *i2c, pcal64xxa_data_t outputs); - -struct pcal64xxa_chip_api { - pcal64xxa_pins_cfg_apply pins_cfg_apply; - pcal64xxa_triggers_apply triggers_apply; - pcal64xxa_inputs_read inputs_read; - pcal64xxa_outputs_write outputs_write; - pcal64xxa_reset_state_apply reset_state_apply; - pcal64xxa_pins_cfg_read pins_cfg_read; -}; - -struct pcal64xxa_drv_cfg { - /* gpio_driver_config needs to be first */ - struct gpio_driver_config common; - - struct i2c_dt_spec i2c; - uint8_t ngpios; - const struct gpio_dt_spec gpio_reset; - const struct gpio_dt_spec gpio_interrupt; - const struct pcal64xxa_chip_api *chip_api; - bool automatic_reset; -}; - -static int pcal64xxa_pin_configure(const struct device *dev, gpio_pin_t pin, gpio_flags_t flags) -{ - struct pcal64xxa_drv_data *drv_data = dev->data; - const struct pcal64xxa_drv_cfg *drv_cfg = dev->config; - struct pcal64xxa_pins_cfg pins_cfg; - gpio_flags_t flags_io; - int rc; - - LOG_DBG("%s: configure pin %i with flags 0x%08X", dev->name, pin, flags); - - /* This device does not support open-source outputs, and open-drain - * outputs can be only configured port-wise. - */ - if ((flags & GPIO_SINGLE_ENDED) != 0) { - return -ENOTSUP; - } - - /* Pins in this device can be either inputs or outputs and cannot be - * completely disconnected. - */ - flags_io = (flags & (GPIO_INPUT | GPIO_OUTPUT)); - if (flags_io == (GPIO_INPUT | GPIO_OUTPUT) || flags_io == GPIO_DISCONNECTED) { - return -ENOTSUP; - } - - if (k_is_in_isr()) { - return -EWOULDBLOCK; - } - - k_sem_take(&drv_data->lock, K_FOREVER); - - pins_cfg = drv_data->pins_cfg; - - if ((flags & (GPIO_PULL_UP | GPIO_PULL_DOWN)) != 0) { - if ((flags & GPIO_PULL_UP) != 0) { - pins_cfg.pull_ups_selected |= BIT(pin); - } else { - pins_cfg.pull_ups_selected &= ~BIT(pin); - } - - pins_cfg.pulls_enabled |= BIT(pin); - } else { - pins_cfg.pulls_enabled &= ~BIT(pin); - } - - if ((flags & GPIO_OUTPUT) != 0) { - if ((flags & GPIO_OUTPUT_INIT_LOW) != 0) { - pins_cfg.outputs_high &= ~BIT(pin); - } else if ((flags & GPIO_OUTPUT_INIT_HIGH) != 0) { - pins_cfg.outputs_high |= BIT(pin); - } - - pins_cfg.configured_as_inputs &= ~BIT(pin); - } else { - pins_cfg.configured_as_inputs |= BIT(pin); - } - - rc = drv_cfg->chip_api->pins_cfg_apply(&drv_cfg->i2c, &pins_cfg); - if (rc == 0) { - drv_data->pins_cfg = pins_cfg; - } else { - LOG_ERR("%s: failed to apply pin config", dev->name); - } - - k_sem_give(&drv_data->lock); - - return rc; -} - -static int pcal64xxa_process_input(const struct device *dev, gpio_port_value_t *value) -{ - const struct pcal64xxa_drv_cfg *drv_cfg = dev->config; - struct pcal64xxa_drv_data *drv_data = dev->data; - int rc; - pcal64xxa_data_t int_sources; - pcal64xxa_data_t input_port; - - k_sem_take(&drv_data->lock, K_FOREVER); - - rc = drv_cfg->chip_api->inputs_read(&drv_cfg->i2c, &int_sources, &input_port); - - if (rc != 0) { - LOG_ERR("%s: failed to read inputs", dev->name); - k_sem_give(&drv_data->lock); - return rc; - } - - if (value) { - *value = input_port; - } - - /* It may happen that some inputs change their states between above - * reads of the interrupt status and input port registers. Such changes - * will not be noted in `int_sources`, thus to correctly detect them, - * the current state of inputs needs to be additionally compared with - * the one read last time, and any differences need to be added to - * `int_sources`. - */ - int_sources |= ((input_port ^ drv_data->input_port_last) & ~drv_data->triggers.masked); - - drv_data->input_port_last = input_port; - - if (int_sources) { - pcal64xxa_data_t dual_edge_triggers = drv_data->triggers.dual_edge; - pcal64xxa_data_t falling_edge_triggers = - ~dual_edge_triggers & drv_data->triggers.on_low; - pcal64xxa_data_t fired_triggers = 0; - - /* For dual edge triggers, react to all state changes. */ - fired_triggers |= (int_sources & dual_edge_triggers); - /* For single edge triggers, fire callbacks only for the pins - * that transitioned to their configured target state (0 for - * falling edges, 1 otherwise, hence the XOR operation below). - */ - fired_triggers |= ((input_port & int_sources) ^ falling_edge_triggers); - - /* Give back semaphore before the callback to make the same - * driver available again for the callback. - */ - k_sem_give(&drv_data->lock); - - gpio_fire_callbacks(&drv_data->callbacks, dev, fired_triggers); - } else { - k_sem_give(&drv_data->lock); - } - - return 0; -} - -static void pcal64xxa_work_handler(struct k_work *work) -{ - struct pcal64xxa_drv_data *drv_data = CONTAINER_OF(work, struct pcal64xxa_drv_data, work); - - (void)pcal64xxa_process_input(drv_data->dev, NULL); -} - -static void pcal64xxa_int_gpio_handler(const struct device *dev, struct gpio_callback *gpio_cb, - uint32_t pins) -{ - ARG_UNUSED(dev); - ARG_UNUSED(pins); - - struct pcal64xxa_drv_data *drv_data = - CONTAINER_OF(gpio_cb, struct pcal64xxa_drv_data, int_gpio_cb); - - k_work_submit(&drv_data->work); -} - -static int pcal64xxa_port_get_raw(const struct device *dev, gpio_port_value_t *value) -{ - int rc; - - if (k_is_in_isr()) { - return -EWOULDBLOCK; - } - - /* Reading of the input port also clears the generated interrupt, - * thus the configured callbacks must be fired also here if needed. - */ - rc = pcal64xxa_process_input(dev, value); - - return rc; -} - -static int pcal64xxa_port_set_raw(const struct device *dev, pcal64xxa_data_t mask, - pcal64xxa_data_t value, pcal64xxa_data_t toggle) -{ - const struct pcal64xxa_drv_cfg *drv_cfg = dev->config; - struct pcal64xxa_drv_data *drv_data = dev->data; - int rc; - pcal64xxa_data_t output; - - LOG_DBG("%s: setting port with mask 0x%" PRIpcal_data " with value 0x%" PRIpcal_data - " and toggle 0x%" PRIpcal_data, - dev->name, mask, value, toggle); - - if (k_is_in_isr()) { - return -EWOULDBLOCK; - } - - k_sem_take(&drv_data->lock, K_FOREVER); - - output = (drv_data->pins_cfg.outputs_high & ~mask); - output |= (value & mask); - output ^= toggle; - /* - * No need to limit `out` to only pins configured as outputs, - * as the chip anyway ignores all other bits in the register. - */ - rc = drv_cfg->chip_api->outputs_write(&drv_cfg->i2c, output); - if (rc == 0) { - drv_data->pins_cfg.outputs_high = output; - } - - k_sem_give(&drv_data->lock); - - if (rc != 0) { - LOG_ERR("%s: failed to write output port: %d", dev->name, rc); - return -EIO; - } - - return 0; -} - -static int pcal64xxa_port_set_masked_raw(const struct device *dev, gpio_port_pins_t mask, - gpio_port_value_t value) -{ - return pcal64xxa_port_set_raw(dev, (pcal64xxa_data_t)mask, (pcal64xxa_data_t)value, 0); -} - -static int pcal64xxa_port_set_bits_raw(const struct device *dev, gpio_port_pins_t pins) -{ - return pcal64xxa_port_set_raw(dev, (pcal64xxa_data_t)pins, (pcal64xxa_data_t)pins, 0); -} - -static int pcal64xxa_port_clear_bits_raw(const struct device *dev, gpio_port_pins_t pins) -{ - return pcal64xxa_port_set_raw(dev, (pcal64xxa_data_t)pins, 0, 0); -} - -static int pcal64xxa_port_toggle_bits(const struct device *dev, gpio_port_pins_t pins) -{ - return pcal64xxa_port_set_raw(dev, 0, 0, (pcal64xxa_data_t)pins); -} - -static int pcal64xxa_pin_interrupt_configure(const struct device *dev, gpio_pin_t pin, - enum gpio_int_mode mode, enum gpio_int_trig trig) -{ - const struct pcal64xxa_drv_cfg *drv_cfg = dev->config; - struct pcal64xxa_drv_data *drv_data = dev->data; - struct pcal64xxa_triggers triggers; - int rc; - - LOG_DBG("%s: configure interrupt for pin %i", dev->name, pin); - - if (drv_cfg->gpio_interrupt.port == NULL) { - return -ENOTSUP; - } - - /* This device supports only edge-triggered interrupts. */ - if (mode == GPIO_INT_MODE_LEVEL) { - return -ENOTSUP; - } - - if (k_is_in_isr()) { - return -EWOULDBLOCK; - } - - k_sem_take(&drv_data->lock, K_FOREVER); - - triggers = drv_data->triggers; - - if (mode == GPIO_INT_MODE_DISABLED) { - triggers.masked |= BIT(pin); - } else { - triggers.masked &= ~BIT(pin); - } - - if (trig == GPIO_INT_TRIG_BOTH) { - triggers.dual_edge |= BIT(pin); - } else { - triggers.dual_edge &= ~BIT(pin); - - if (trig == GPIO_INT_TRIG_LOW) { - triggers.on_low |= BIT(pin); - } else { - triggers.on_low &= ~BIT(pin); - } - } - - rc = drv_cfg->chip_api->triggers_apply(&drv_cfg->i2c, &triggers); - if (rc == 0) { - drv_data->triggers = triggers; - } else { - LOG_ERR("%s: failed to apply triggers", dev->name); - } - - k_sem_give(&drv_data->lock); - - return rc; -} - -static int pcal64xxa_manage_callback(const struct device *dev, struct gpio_callback *callback, - bool set) -{ - struct pcal64xxa_drv_data *drv_data = dev->data; - - return gpio_manage_callback(&drv_data->callbacks, callback, set); -} - -static int pcal64xxa_i2c_write(const struct i2c_dt_spec *i2c, uint8_t register_address, - uint8_t value) -{ - int rc; - - LOG_DBG("writing to register 0x%02X value 0x%02X", register_address, value); - rc = i2c_reg_write_byte_dt(i2c, register_address, value); - - if (rc != 0) { - LOG_ERR("unable to write to register 0x%02X, error %i", register_address, rc); - } - - return rc; -} - -static int pcal64xxa_i2c_read(const struct i2c_dt_spec *i2c, uint8_t register_address, - uint8_t *value) -{ - int rc; - - rc = i2c_reg_read_byte_dt(i2c, register_address, value); - LOG_DBG("reading from register 0x%02X value 0x%02X", register_address, *value); - - if (rc != 0) { - LOG_ERR("unable to read from register 0x%02X, error %i", register_address, rc); - } - - return rc; -} - -#if DT_HAS_COMPAT_STATUS_OKAY(nxp_pcal6408a) -static int pcal6408a_pins_cfg_apply(const struct i2c_dt_spec *i2c, - const struct pcal64xxa_pins_cfg *pins_cfg) -{ - int rc; - - rc = pcal64xxa_i2c_write(i2c, PCAL6408A_REG_PULL_UP_DOWN_SELECT, - (uint8_t)pins_cfg->pull_ups_selected); - if (rc != 0) { - return -EIO; - } - - rc = pcal64xxa_i2c_write(i2c, PCAL6408A_REG_PULL_UP_DOWN_ENABLE, - (uint8_t)pins_cfg->pulls_enabled); - if (rc != 0) { - return -EIO; - } - - rc = pcal64xxa_i2c_write(i2c, PCAL6408A_REG_OUTPUT_PORT, (uint8_t)pins_cfg->outputs_high); - if (rc != 0) { - return -EIO; - } - - rc = pcal64xxa_i2c_write(i2c, PCAL6408A_REG_CONFIGURATION, - (uint8_t)pins_cfg->configured_as_inputs); - if (rc != 0) { - return -EIO; - } - - return 0; -} - -static int pcal6408a_pins_cfg_read(const struct i2c_dt_spec *i2c, - struct pcal64xxa_pins_cfg *pins_cfg) -{ - int rc; - uint8_t value; - - rc = pcal64xxa_i2c_read(i2c, PCAL6408A_REG_PULL_UP_DOWN_SELECT, &value); - if (rc != 0) { - return -EIO; - } - - pins_cfg->pull_ups_selected = value; - - rc = pcal64xxa_i2c_read(i2c, PCAL6408A_REG_PULL_UP_DOWN_ENABLE, &value); - if (rc != 0) { - return -EIO; - } - - pins_cfg->pulls_enabled = value; - - rc = pcal64xxa_i2c_read(i2c, PCAL6408A_REG_OUTPUT_PORT, &value); - if (rc != 0) { - return -EIO; - } - - pins_cfg->outputs_high = value; - - rc = pcal64xxa_i2c_read(i2c, PCAL6408A_REG_CONFIGURATION, &value); - if (rc != 0) { - return -EIO; - } - - pins_cfg->configured_as_inputs = value; - - return 0; -} - -static int pcal6408a_inputs_read(const struct i2c_dt_spec *i2c, pcal64xxa_data_t *int_sources, - pcal64xxa_data_t *input_port) -{ - int rc; - uint8_t value; - - rc = pcal64xxa_i2c_read(i2c, PCAL6408A_REG_INTERRUPT_STATUS, &value); - if (rc != 0) { - return -EIO; - } - - *int_sources = value; - - /* This read also clears the generated interrupt if any. */ - rc = pcal64xxa_i2c_read(i2c, PCAL6408A_REG_INPUT_PORT, &value); - if (rc != 0) { - return -EIO; - } - - *input_port = value; - - return 0; -} - -static int pcal6408a_outputs_write(const struct i2c_dt_spec *i2c, pcal64xxa_data_t outputs) -{ - int rc; - - /* - * No need to limit `out` to only pins configured as outputs, - * as the chip anyway ignores all other bits in the register. - */ - rc = pcal64xxa_i2c_write(i2c, PCAL6408A_REG_OUTPUT_PORT, (uint8_t)outputs); - - if (rc != 0) { - LOG_ERR("failed to write output port: %d", rc); - return -EIO; - } - - return 0; -} - -static int pcal6408a_triggers_apply(const struct i2c_dt_spec *i2c, - const struct pcal64xxa_triggers *triggers) -{ - int rc; - uint8_t input_latch = ~triggers->masked; - uint8_t interrupt_mask = triggers->masked; - - rc = pcal64xxa_i2c_write(i2c, PCAL6408A_REG_INPUT_LATCH, (uint8_t)input_latch); - if (rc != 0) { - LOG_ERR("failed to configure input latch: %d", rc); - return -EIO; - } - - rc = pcal64xxa_i2c_write(i2c, PCAL6408A_REG_INTERRUPT_MASK, (uint8_t)interrupt_mask); - if (rc != 0) { - LOG_ERR("failed to configure interrupt mask: %d", rc); - return -EIO; - } - - return 0; -} - -static int pcal6408a_reset_state_apply(const struct i2c_dt_spec *i2c) -{ - int rc; - static const uint8_t reset_state[][2] = { - {PCAL6408A_REG_POLARITY_INVERSION, 0}, - {PCAL6408A_REG_OUTPUT_DRIVE_STRENGTH_0, 0xff}, - {PCAL6408A_REG_OUTPUT_DRIVE_STRENGTH_1, 0xff}, - {PCAL6408A_REG_OUTPUT_PORT_CONFIGURATION, 0}, - }; - - for (int i = 0; i < ARRAY_SIZE(reset_state); ++i) { - rc = pcal64xxa_i2c_write(i2c, reset_state[i][0], reset_state[i][1]); - if (rc != 0) { - LOG_ERR("failed to reset register %02x: %d", reset_state[i][0], rc); - return -EIO; - } - } - - return 0; -} - -static const struct pcal64xxa_chip_api pcal6408a_chip_api = { - .pins_cfg_apply = pcal6408a_pins_cfg_apply, - .triggers_apply = pcal6408a_triggers_apply, - .inputs_read = pcal6408a_inputs_read, - .outputs_write = pcal6408a_outputs_write, - .reset_state_apply = pcal6408a_reset_state_apply, - .pins_cfg_read = pcal6408a_pins_cfg_read, -}; -#endif /* DT_HAS_COMPAT_STATUS_OKAY(nxp_pcal6408a) */ - -#if DT_HAS_COMPAT_STATUS_OKAY(nxp_pcal6416a) -static int pcal6416a_pins_cfg_apply(const struct i2c_dt_spec *i2c, - const struct pcal64xxa_pins_cfg *pins_cfg) -{ - int rc; - - rc = pcal64xxa_i2c_write(i2c, PCAL6416A_REG_PULL_UP_DOWN_SELECT_0, - (uint8_t)pins_cfg->pull_ups_selected); - if (rc != 0) { - return -EIO; - } - - rc = pcal64xxa_i2c_write(i2c, PCAL6416A_REG_PULL_UP_DOWN_SELECT_1, - (uint8_t)(pins_cfg->pull_ups_selected >> 8)); - if (rc != 0) { - return -EIO; - } - - rc = pcal64xxa_i2c_write(i2c, PCAL6416A_REG_PULL_UP_DOWN_ENABLE_0, - (uint8_t)pins_cfg->pulls_enabled); - if (rc != 0) { - return -EIO; - } - - rc = pcal64xxa_i2c_write(i2c, PCAL6416A_REG_PULL_UP_DOWN_ENABLE_1, - (uint8_t)(pins_cfg->pulls_enabled >> 8)); - if (rc != 0) { - return -EIO; - } - - rc = pcal64xxa_i2c_write(i2c, PCAL6416A_REG_OUTPUT_PORT_0, (uint8_t)pins_cfg->outputs_high); - if (rc != 0) { - return -EIO; - } - - rc = pcal64xxa_i2c_write(i2c, PCAL6416A_REG_OUTPUT_PORT_1, - (uint8_t)(pins_cfg->outputs_high >> 8)); - if (rc != 0) { - return -EIO; - } - - rc = pcal64xxa_i2c_write(i2c, PCAL6416A_REG_CONFIGURATION_0, - (uint8_t)pins_cfg->configured_as_inputs); - if (rc != 0) { - return -EIO; - } - - rc = pcal64xxa_i2c_write(i2c, PCAL6416A_REG_CONFIGURATION_1, - (uint8_t)(pins_cfg->configured_as_inputs >> 8)); - if (rc != 0) { - return -EIO; - } - - return 0; -} - -static int pcal6416a_pins_cfg_read(const struct i2c_dt_spec *i2c, - struct pcal64xxa_pins_cfg *pins_cfg) -{ - int rc; - uint8_t value_low; - uint8_t value_high; - - rc = pcal64xxa_i2c_read(i2c, PCAL6416A_REG_PULL_UP_DOWN_SELECT_0, &value_low); - if (rc != 0) { - return -EIO; - } - - rc = pcal64xxa_i2c_read(i2c, PCAL6416A_REG_PULL_UP_DOWN_SELECT_1, &value_high); - if (rc != 0) { - return -EIO; - } - - pins_cfg->pull_ups_selected = value_high << 8 | value_low; - - rc = pcal64xxa_i2c_read(i2c, PCAL6416A_REG_PULL_UP_DOWN_ENABLE_0, &value_low); - if (rc != 0) { - return -EIO; - } - - rc = pcal64xxa_i2c_read(i2c, PCAL6416A_REG_PULL_UP_DOWN_ENABLE_1, &value_high); - if (rc != 0) { - return -EIO; - } - - pins_cfg->pulls_enabled = value_high << 8 | value_low; - - rc = pcal64xxa_i2c_read(i2c, PCAL6416A_REG_OUTPUT_PORT_0, &value_low); - if (rc != 0) { - return -EIO; - } - - rc = pcal64xxa_i2c_read(i2c, PCAL6416A_REG_OUTPUT_PORT_1, &value_high); - if (rc != 0) { - return -EIO; - } - - pins_cfg->outputs_high = value_high << 8 | value_low; - - rc = pcal64xxa_i2c_read(i2c, PCAL6416A_REG_CONFIGURATION_0, &value_low); - if (rc != 0) { - return -EIO; - } - - rc = pcal64xxa_i2c_read(i2c, PCAL6416A_REG_CONFIGURATION_1, &value_high); - if (rc != 0) { - return -EIO; - } - - pins_cfg->configured_as_inputs = value_high << 8 | value_low; - - return 0; -} - -static int pcal6416a_inputs_read(const struct i2c_dt_spec *i2c, pcal64xxa_data_t *int_sources, - pcal64xxa_data_t *input_port) -{ - int rc; - uint8_t value_low; - uint8_t value_high; - - rc = pcal64xxa_i2c_read(i2c, PCAL6416A_REG_INTERRUPT_STATUS_0, &value_low); - if (rc != 0) { - return -EIO; - } - - rc = pcal64xxa_i2c_read(i2c, PCAL6416A_REG_INTERRUPT_STATUS_1, &value_high); - if (rc != 0) { - return -EIO; - } - - *int_sources = value_low | (value_high << 8); - - /* This read also clears the generated interrupt if any. */ - rc = pcal64xxa_i2c_read(i2c, PCAL6416A_REG_INPUT_PORT_0, &value_low); - if (rc != 0) { - return -EIO; - } - - rc = pcal64xxa_i2c_read(i2c, PCAL6416A_REG_INPUT_PORT_1, &value_high); - if (rc != 0) { - LOG_ERR("failed to read input port: %d", rc); - return -EIO; - } - - *input_port = value_low | (value_high << 8); - - return 0; -} - -static int pcal6416a_outputs_write(const struct i2c_dt_spec *i2c, pcal64xxa_data_t outputs) -{ - int rc; - - /* - * No need to limit `out` to only pins configured as outputs, - * as the chip anyway ignores all other bits in the register. - */ - rc = pcal64xxa_i2c_write(i2c, PCAL6416A_REG_OUTPUT_PORT_0, (uint8_t)outputs); - - if (rc != 0) { - LOG_ERR("failed to write output port: %d", rc); - return -EIO; - } - - rc = pcal64xxa_i2c_write(i2c, PCAL6416A_REG_OUTPUT_PORT_1, (uint8_t)(outputs >> 8)); - - if (rc != 0) { - LOG_ERR("failed to write output port: %d", rc); - return -EIO; - } - - return 0; -} - -static int pcal6416a_triggers_apply(const struct i2c_dt_spec *i2c, - const struct pcal64xxa_triggers *triggers) -{ - int rc; - pcal64xxa_data_t input_latch = ~triggers->masked; - pcal64xxa_data_t interrupt_mask = triggers->masked; - - rc = pcal64xxa_i2c_write(i2c, PCAL6416A_REG_INPUT_LATCH_0, (uint8_t)input_latch); - if (rc != 0) { - LOG_ERR("failed to configure input latch: %d", rc); - return -EIO; - } - - rc = pcal64xxa_i2c_write(i2c, PCAL6416A_REG_INPUT_LATCH_1, (uint8_t)(input_latch >> 8)); - if (rc != 0) { - LOG_ERR("failed to configure input latch: %d", rc); - return -EIO; - } - - rc = pcal64xxa_i2c_write(i2c, PCAL6416A_REG_INTERRUPT_MASK_0, (uint8_t)interrupt_mask); - if (rc != 0) { - LOG_ERR("failed to configure interrupt mask: %d", rc); - return -EIO; - } - - rc = pcal64xxa_i2c_write(i2c, PCAL6416A_REG_INTERRUPT_MASK_1, - (uint8_t)(interrupt_mask >> 8)); - if (rc != 0) { - LOG_ERR("failed to configure interrupt mask: %d", rc); - return -EIO; - } - - return 0; -} - -static int pcal6416a_reset_state_apply(const struct i2c_dt_spec *i2c) -{ - int rc; - static const uint8_t reset_state[][2] = { - {PCAL6416A_REG_POLARITY_INVERSION_0, 0}, - {PCAL6416A_REG_POLARITY_INVERSION_1, 0}, - {PCAL6416A_REG_OUTPUT_DRIVE_STRENGTH_0_0, 0xff}, - {PCAL6416A_REG_OUTPUT_DRIVE_STRENGTH_0_1, 0xff}, - {PCAL6416A_REG_OUTPUT_DRIVE_STRENGTH_1_0, 0xff}, - {PCAL6416A_REG_OUTPUT_DRIVE_STRENGTH_1_1, 0xff}, - {PCAL6416A_REG_OUTPUT_PORT_CONFIGURATION, 0}, - }; - - for (int i = 0; i < ARRAY_SIZE(reset_state); ++i) { - rc = pcal64xxa_i2c_write(i2c, reset_state[i][0], reset_state[i][1]); - if (rc != 0) { - LOG_ERR("failed to reset register %02x: %d", reset_state[i][0], rc); - return -EIO; - } - } - - return 0; -} - -static const struct pcal64xxa_chip_api pcal6416a_chip_api = { - .pins_cfg_apply = pcal6416a_pins_cfg_apply, - .triggers_apply = pcal6416a_triggers_apply, - .inputs_read = pcal6416a_inputs_read, - .outputs_write = pcal6416a_outputs_write, - .reset_state_apply = pcal6416a_reset_state_apply, - .pins_cfg_read = pcal6416a_pins_cfg_read, -}; -#endif /* DT_HAS_COMPAT_STATUS_OKAY(nxp_pcal6416a) */ - -static int pcal64xxa_apply_initial_state(const struct device *dev) -{ - const struct pcal64xxa_drv_cfg *drv_cfg = dev->config; - struct pcal64xxa_drv_data *drv_data = dev->data; - const struct pcal64xxa_pins_cfg initial_pins_cfg = { - .configured_as_inputs = PCAL64XXA_INIT_HIGH, - .outputs_high = 0, - .pull_ups_selected = 0, - .pulls_enabled = 0, - }; - int rc; - - LOG_DBG("%s: apply initial state", dev->name); - - /* If the RESET line is available, use it to reset the expander. - * Otherwise, write reset values to registers that are not used by - * this driver. - */ - if (drv_cfg->gpio_reset.port != NULL) { - if (!gpio_is_ready_dt(&drv_cfg->gpio_reset)) { - LOG_ERR("%s: reset gpio device is not ready", dev->name); - return -ENODEV; - } - - LOG_DBG("%s: trigger reset", dev->name); - rc = gpio_pin_configure_dt(&drv_cfg->gpio_reset, GPIO_OUTPUT_ACTIVE); - if (rc != 0) { - LOG_ERR("%s: failed to configure RESET line: %d", dev->name, rc); - return -EIO; - } - - /* RESET signal needs to be active for a minimum of 30 ns. */ - k_busy_wait(1); - - rc = gpio_pin_set_dt(&drv_cfg->gpio_reset, 0); - if (rc != 0) { - LOG_ERR("%s: failed to deactivate RESET line: %d", dev->name, rc); - return -EIO; - } - - /* Give the expander at least 200 ns to recover after reset. */ - k_busy_wait(1); - } else { - rc = drv_cfg->chip_api->reset_state_apply(&drv_cfg->i2c); - - if (rc != 0) { - LOG_ERR("%s: failed to apply reset state", dev->name); - return rc; - } - } - - /* Set initial configuration of the pins. */ - rc = drv_cfg->chip_api->pins_cfg_apply(&drv_cfg->i2c, &initial_pins_cfg); - if (rc != 0) { - LOG_ERR("%s: failed to apply pin config", dev->name); - return rc; - } - - drv_data->pins_cfg = initial_pins_cfg; - - return 0; -} - -static int pcal64xxa_read_state_from_registers(const struct device *dev) -{ - const struct pcal64xxa_drv_cfg *drv_cfg = dev->config; - struct pcal64xxa_drv_data *drv_data = dev->data; - int rc; - - LOG_DBG("%s: use retained state", dev->name); - - /* Read current configuration of the pins. */ - rc = drv_cfg->chip_api->pins_cfg_read(&drv_cfg->i2c, &drv_data->pins_cfg); - if (rc != 0) { - LOG_ERR("%s: failed to apply pin config", dev->name); - return rc; - } - - return 0; -} - -static int pcal64xxa_apply_initial_triggers(const struct device *dev) -{ - const struct pcal64xxa_drv_cfg *drv_cfg = dev->config; - struct pcal64xxa_drv_data *drv_data = dev->data; - const struct pcal64xxa_triggers initial_triggers = { - .masked = PCAL64XXA_INIT_HIGH, - }; - int rc; - - /* Set initial state of the interrupt related registers. */ - rc = drv_cfg->chip_api->triggers_apply(&drv_cfg->i2c, &initial_triggers); - if (rc != 0) { - LOG_ERR("%s: failed to apply triggers", dev->name); - return rc; - } - - drv_data->triggers = initial_triggers; - - return 0; -} - -static int pcal64xxa_read_initial_inputs(const struct device *dev) -{ - const struct pcal64xxa_drv_cfg *drv_cfg = dev->config; - struct pcal64xxa_drv_data *drv_data = dev->data; - pcal64xxa_data_t int_sources; - int rc; - - /* Read initial state of the input port register. */ - rc = drv_cfg->chip_api->inputs_read(&drv_cfg->i2c, &int_sources, - &drv_data->input_port_last); - if (rc != 0) { - LOG_ERR("%s: failed to read inputs", dev->name); - return rc; - } - - return 0; -} - -static int pcal64xxa_reset_unlocked(const struct device *dev) -{ - int rc; - - rc = pcal64xxa_apply_initial_state(dev); - if (rc != 0) { - return rc; - } - - rc = pcal64xxa_apply_initial_triggers(dev); - if (rc != 0) { - return rc; - } - - rc = pcal64xxa_read_initial_inputs(dev); - if (rc != 0) { - return rc; - } - - return 0; -} - -int pcal64xxa_reset(const struct device *dev) -{ - struct pcal64xxa_drv_data *drv_data = dev->data; - int rc; - - k_sem_take(&drv_data->lock, K_FOREVER); - rc = pcal64xxa_reset_unlocked(dev); - k_sem_give(&drv_data->lock); - - return rc; -} - -int pcal64xxa_init(const struct device *dev) -{ - const struct pcal64xxa_drv_cfg *drv_cfg = dev->config; - struct pcal64xxa_drv_data *drv_data = dev->data; - int rc; - - LOG_DBG("%s: initializing PCAL64XXA", dev->name); - - if (drv_cfg->ngpios != 8U && drv_cfg->ngpios != 16U) { - LOG_ERR("%s: Invalid value ngpios=%u. Expected 8 or 16!", - dev->name, drv_cfg->ngpios); - return -EINVAL; - } - - /* - * executing the is ready check on i2c_bus_dev instead of on i2c.bus - * to avoid a const warning - */ - if (!i2c_is_ready_dt(&drv_cfg->i2c)) { - LOG_ERR("%s: %s is not ready", dev->name, drv_cfg->i2c.bus->name); - return -ENODEV; - } - - /* If the INT line is available, configure the callback for it. */ - if (drv_cfg->gpio_interrupt.port != NULL) { - if (!gpio_is_ready_dt(&drv_cfg->gpio_interrupt)) { - LOG_ERR("%s: interrupt gpio device is not ready", dev->name); - return -ENODEV; - } - - rc = gpio_pin_configure_dt(&drv_cfg->gpio_interrupt, GPIO_INPUT); - if (rc != 0) { - LOG_ERR("%s: failed to configure INT line: %d", dev->name, rc); - return -EIO; - } - - rc = gpio_pin_interrupt_configure_dt(&drv_cfg->gpio_interrupt, - GPIO_INT_EDGE_TO_ACTIVE); - if (rc != 0) { - LOG_ERR("%s: failed to configure INT interrupt: %d", dev->name, rc); - return -EIO; - } - - gpio_init_callback(&drv_data->int_gpio_cb, pcal64xxa_int_gpio_handler, - BIT(drv_cfg->gpio_interrupt.pin)); - rc = gpio_add_callback(drv_cfg->gpio_interrupt.port, &drv_data->int_gpio_cb); - if (rc != 0) { - LOG_ERR("%s: failed to add INT callback: %d", dev->name, rc); - return -EIO; - } - } - - if (drv_cfg->automatic_reset) { - rc = pcal64xxa_apply_initial_state(dev); - if (rc != 0) { - return rc; - } - } else { - rc = pcal64xxa_read_state_from_registers(dev); - if (rc != 0) { - return rc; - } - } - - rc = pcal64xxa_apply_initial_triggers(dev); - if (rc != 0) { - return rc; - } - - rc = pcal64xxa_read_initial_inputs(dev); - if (rc != 0) { - return rc; - } - - /* Device configured, unlock it so that it can be used. */ - k_sem_give(&drv_data->lock); - - return 0; -} - -#define PCAL64XXA_INIT_INT_GPIO_FIELDS(idx) \ - COND_CODE_1(DT_INST_NODE_HAS_PROP(idx, int_gpios), \ - (GPIO_DT_SPEC_GET_BY_IDX(DT_DRV_INST(idx), int_gpios, 0)), ({0})) - -#define PCAL64XXA_INIT_RESET_GPIO_FIELDS(idx) \ - COND_CODE_1(DT_INST_NODE_HAS_PROP(idx, reset_gpios), \ - (GPIO_DT_SPEC_GET_BY_IDX(DT_DRV_INST(idx), reset_gpios, 0)), ({0})) - -#define PCAL64XXA_AUTOMATIC_RESET(idx) !(DT_INST_PROP(idx, no_auto_reset)) - -#define GPIO_PCAL6408A_INST(idx) \ - static const struct gpio_driver_api pcal6408a_drv_api##idx = { \ - .pin_configure = pcal64xxa_pin_configure, \ - .port_get_raw = pcal64xxa_port_get_raw, \ - .port_set_masked_raw = pcal64xxa_port_set_masked_raw, \ - .port_set_bits_raw = pcal64xxa_port_set_bits_raw, \ - .port_clear_bits_raw = pcal64xxa_port_clear_bits_raw, \ - .port_toggle_bits = pcal64xxa_port_toggle_bits, \ - .pin_interrupt_configure = pcal64xxa_pin_interrupt_configure, \ - .manage_callback = pcal64xxa_manage_callback, \ - }; \ - static const struct pcal64xxa_drv_cfg pcal6408a_cfg##idx = { \ - .common = { \ - .port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(idx), \ - }, \ - .i2c = I2C_DT_SPEC_INST_GET(idx), \ - .ngpios = DT_INST_PROP(idx, ngpios), \ - .gpio_interrupt = PCAL64XXA_INIT_INT_GPIO_FIELDS(idx), \ - .gpio_reset = PCAL64XXA_INIT_RESET_GPIO_FIELDS(idx), \ - .chip_api = &pcal6408a_chip_api, \ - .automatic_reset = PCAL64XXA_AUTOMATIC_RESET(idx), \ - }; \ - static struct pcal64xxa_drv_data pcal6408a_data##idx = { \ - .lock = Z_SEM_INITIALIZER(pcal6408a_data##idx.lock, 1, 1), \ - .work = Z_WORK_INITIALIZER(pcal64xxa_work_handler), \ - .dev = DEVICE_DT_INST_GET(idx), \ - }; \ - DEVICE_DT_INST_DEFINE(idx, pcal64xxa_init, NULL, &pcal6408a_data##idx, \ - &pcal6408a_cfg##idx, POST_KERNEL, \ - CONFIG_GPIO_PCAL64XXA_INIT_PRIORITY, &pcal6408a_drv_api##idx); - -#define DT_DRV_COMPAT nxp_pcal6408a -DT_INST_FOREACH_STATUS_OKAY(GPIO_PCAL6408A_INST) - -#define GPIO_PCAL6416A_INST(idx) \ - static const struct gpio_driver_api pcal6416a_drv_api##idx = { \ - .pin_configure = pcal64xxa_pin_configure, \ - .port_get_raw = pcal64xxa_port_get_raw, \ - .port_set_masked_raw = pcal64xxa_port_set_masked_raw, \ - .port_set_bits_raw = pcal64xxa_port_set_bits_raw, \ - .port_clear_bits_raw = pcal64xxa_port_clear_bits_raw, \ - .port_toggle_bits = pcal64xxa_port_toggle_bits, \ - .pin_interrupt_configure = pcal64xxa_pin_interrupt_configure, \ - .manage_callback = pcal64xxa_manage_callback, \ - }; \ - static const struct pcal64xxa_drv_cfg pcal6416a_cfg##idx = { \ - .common = { \ - .port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(idx), \ - }, \ - .i2c = I2C_DT_SPEC_INST_GET(idx), \ - .ngpios = DT_INST_PROP(idx, ngpios), \ - .gpio_interrupt = PCAL64XXA_INIT_INT_GPIO_FIELDS(idx), \ - .gpio_reset = PCAL64XXA_INIT_RESET_GPIO_FIELDS(idx), \ - .chip_api = &pcal6416a_chip_api, \ - .automatic_reset = PCAL64XXA_AUTOMATIC_RESET(idx), \ - }; \ - static struct pcal64xxa_drv_data pcal6416a_data##idx = { \ - .lock = Z_SEM_INITIALIZER(pcal6416a_data##idx.lock, 1, 1), \ - .work = Z_WORK_INITIALIZER(pcal64xxa_work_handler), \ - .dev = DEVICE_DT_INST_GET(idx), \ - }; \ - DEVICE_DT_INST_DEFINE(idx, pcal64xxa_init, NULL, &pcal6416a_data##idx, \ - &pcal6416a_cfg##idx, POST_KERNEL, \ - CONFIG_GPIO_PCAL64XXA_INIT_PRIORITY, &pcal6416a_drv_api##idx); - -#undef DT_DRV_COMPAT -#define DT_DRV_COMPAT nxp_pcal6416a -DT_INST_FOREACH_STATUS_OKAY(GPIO_PCAL6416A_INST) diff --git a/dts/bindings/gpio/nxp,pcal64xxa-base.yaml b/dts/bindings/gpio/nxp,pcal64xxa-base.yaml deleted file mode 100644 index da5827e1e1badd..00000000000000 --- a/dts/bindings/gpio/nxp,pcal64xxa-base.yaml +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright (c) 2021 Nordic Semiconductor ASA -# Copyright (c) 2023 SILA Embedded Solutions GmbH -# SPDX-License-Identifier: Apache-2.0 - -include: [i2c-device.yaml, gpio-controller.yaml] - -properties: - int-gpios: - type: phandle-array - description: | - GPIO connected to the controller INT pin. This pin is active-low. - - reset-gpios: - type: phandle-array - description: | - GPIO connected to the controller RESET pin. This pin is active-low. - - no-auto-reset: - type: boolean - description: | - This flag disables the automatic reset, which allows the implementation - of for instance an external state retention of the port expander. If - this flag is set it must be ensured that for an actually uninitialized - port expander the manual reset is triggered via the exposed reset - function, specific to the PCAL64XXA. - - "#gpio-cells": - const: 2 - -gpio-cells: - - pin - - flags From 302051941e6fcaa714b515326255df41191d5d32 Mon Sep 17 00:00:00 2001 From: Chekhov Ma Date: Tue, 24 Sep 2024 10:55:58 +0800 Subject: [PATCH 6/6] doc: release: 4.1: highlight pca_series driver replacing a few drivers pca_series driver replaces pca953x, pca95xx and pcal64xxa drivers. Signed-off-by: Chekhov Ma --- doc/releases/release-notes-4.1.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/doc/releases/release-notes-4.1.rst b/doc/releases/release-notes-4.1.rst index dff3f100c2c51d..55160fc122ac4b 100644 --- a/doc/releases/release-notes-4.1.rst +++ b/doc/releases/release-notes-4.1.rst @@ -113,6 +113,15 @@ Drivers and Sensors * GPIO + * pca_series: modified ``pca_series`` driver to unify support to tca and pca series I2C-based + gpio expanders. + + * pca953x: removed and replaced with an API equivalent driver ``pca_series``. + + * pca95xx: removed and replaced with an API equivalent driver ``pca_series``. + + * pcal64xxa: removed and replaced with an API equivalent driver ``pca_series``. + * Hardware info * I2C