From 5a6c36960b87d183d4300228ec28580e0cb96ade Mon Sep 17 00:00:00 2001 From: Chekhov Ma Date: Thu, 19 Sep 2024 14:10:58 +0800 Subject: [PATCH] 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 a75710417b0795c..000b0edadc1213e 100644 --- a/drivers/gpio/CMakeLists.txt +++ b/drivers/gpio/CMakeLists.txt @@ -61,7 +61,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 a2370b2f6c2bd2d..692099cfb3de053 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -149,7 +149,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 c04b3d694069b03..000000000000000 --- 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 b9b5a8383e3592b..000000000000000 --- 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 da5827e1e1badd9..000000000000000 --- 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