From 1aa75d3c3955b5b3cba73613131d6b5017bada74 Mon Sep 17 00:00:00 2001 From: Stoyan Bogdanov Date: Thu, 4 Apr 2024 16:57:29 +0300 Subject: [PATCH] drivers: gpio: max22190: Add MAX22190 octal input with diagnostics Add max22190 gpio driver with input functionality, since device support only input with output. Implemented diagnostic funcionality for all 8 channels which include various check to over/under voltage and wire break. Filtering configuration is done from devicetree on per channel bases and is configured on chip start. In case of some fault condition occure FAULT pin drive LOW which prop to FAULT registers to be read. Data is stored in data structure for furter analizes and ERR message is prited in console. Signed-off-by: Stoyan Bogdanov --- drivers/gpio/CMakeLists.txt | 1 + drivers/gpio/Kconfig | 2 + drivers/gpio/Kconfig.max22190 | 20 + drivers/gpio/gpio_max22190.c | 646 +++++++++++++++++++++++ dts/bindings/gpio/adi,max22190-gpio.yaml | 103 ++++ 5 files changed, 772 insertions(+) create mode 100644 drivers/gpio/Kconfig.max22190 create mode 100644 drivers/gpio/gpio_max22190.c create mode 100644 dts/bindings/gpio/adi,max22190-gpio.yaml diff --git a/drivers/gpio/CMakeLists.txt b/drivers/gpio/CMakeLists.txt index 2143d019cb7a0d4..1bab587a9dc75b8 100644 --- a/drivers/gpio/CMakeLists.txt +++ b/drivers/gpio/CMakeLists.txt @@ -92,6 +92,7 @@ zephyr_library_sources_ifdef(CONFIG_GPIO_RENESAS_RA gpio_renesas_ra.c) zephyr_library_sources_ifdef(CONFIG_GPIO_ENE_KB1200 gpio_ene_kb1200.c) zephyr_library_sources_ifdef(CONFIG_GPIO_RZT2M gpio_rzt2m.c) zephyr_library_sources_ifdef(CONFIG_GPIO_AMBIQ gpio_ambiq.c) +zephyr_library_sources_ifdef(CONFIG_GPIO_MAX22190 gpio_max22190.c) if (CONFIG_GPIO_EMUL_SDL) zephyr_library_sources(gpio_emul_sdl.c) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 2ca6da678c3099b..3dba4f512a50fbf 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -242,4 +242,6 @@ source "drivers/gpio/Kconfig.rzt2m" source "drivers/gpio/Kconfig.ambiq" +source "drivers/gpio/Kconfig.max22190" + endif # GPIO diff --git a/drivers/gpio/Kconfig.max22190 b/drivers/gpio/Kconfig.max22190 new file mode 100644 index 000000000000000..2ffec1cbe02d71b --- /dev/null +++ b/drivers/gpio/Kconfig.max22190 @@ -0,0 +1,20 @@ +# Copyright (c) 2010-2024 Analog Devices Inc. +# Copyright (c) 2024 BayLibre SAS +# SPDX-License-Identifier: Apache-2.0 + +# MAX22190 GPIO configuration options + +menuconfig GPIO_MAX22190 + bool "MAX22190 GPIO driver" + default y + depends on DT_HAS_ADI_MAX22190_GPIO_ENABLED + help + Enabe MAX22190 Octal industrial digital + input with diagnostics + +config GPIO_MAX22190_INIT_PRIORITY + int "Driver init priority" + default 99 + depends on GPIO_MAX22190 + help + Device driver initialization priority. diff --git a/drivers/gpio/gpio_max22190.c b/drivers/gpio/gpio_max22190.c new file mode 100644 index 000000000000000..69d1af46fbca43d --- /dev/null +++ b/drivers/gpio/gpio_max22190.c @@ -0,0 +1,646 @@ +/* + * Copyright (c) 2010-2024 Analog Devices Inc. + * Copyright (c) 2024 Baylibre SAS + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT adi_max22190_gpio + +#include +#include +#include +#include + +#define LOG_LEVEL CONFIG_GPIO_LOG_LEVEL +#include + +LOG_MODULE_REGISTER(gpio_max22190); + +#include + +#if DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) == 0 + #warning "GPIO MAX22190 driver enabled without any devices" +#endif + +#define MAX22190_ENABLE 1 +#define MAX22190_DISABLE 0 + +#define MAX22190_READ 0 +#define MAX22190_WRITE 1 + +#define MAX22190_MAX_PKT_SIZE 3 +#define MAX22190_CHANNELS 8 +#define MAX22190_FAULT2_ENABLES 5 + +#define MAX22190_WB_REG 0x0 +#define MAX22190_DI_REG 0x2 +#define MAX22190_FAULT1_REG 0x4 +#define MAX22190_FILTER_IN_REG(x) (0x6 + (2 * (x))) +#define MAX22190_CFG_REG 0x18 +#define MAX22190_IN_EN_REG 0x1A +#define MAX22190_FAULT2_REG 0x1C +#define MAX22190_FAULT2_EN_REG 0x1E +#define MAX22190_GPO_REG 0x22 +#define MAX22190_FAULT1_EN_REG 0x24 +#define MAX22190_NOP_REG 0x26 + +#define MAX22190_CH_STATE_MASK(x) BIT(x) +#define MAX22190_DELAY_MASK GENMASK(2, 0) +#define MAX22190_FBP_MASK BIT(3) +#define MAX22190_WBE_MASK BIT(4) +#define MAX22190_RW_MASK BIT(7) +#define MAX22190_ADDR_MASK GENMASK(6, 0) +#define MAX22190_ALARM_MASK GENMASK(4, 3) +#define MAX22190_POR_MASK BIT(6) + +#define MAX22190_FAULT_MASK(x) BIT(x) +#define MAX22190_FAULT2_WBE_MASK BIT(4) + +#define MAX22190_FAULT2_EN_MASK GENMASK(5, 0) + +#define MAX22190_CFG_REFDI_MASK BIT(0) +#define MAX22190_CFG_CLRF_MASK BIT(3) +#define MAX22190_CFG_24VF_MASK BIT(4) + +#define PRINT_ERR_BIT(bit1, bit2) if ((bit1) & (bit2)) LOG_ERR("[%s] %d", #bit1, bit1) + +#define max22190_clean_por(dev) max22190_reg_update(dev, MAX22190_FAULT1_REG, MAX22190_POR_MASK,\ + FIELD_PREP(MAX22190_POR_MASK, 0)) + +enum max22190_ch_state { + MAX22190_CH_OFF, + MAX22190_CH_ON +}; + +enum max22190_ch_wb_state { + MAX22190_CH_NO_WB_BREAK, + MAX22190_CH_WB_COND_DET +}; + +enum max22190_mode { + MAX22190_MODE_0, + MAX22190_MODE_1, + MAX22190_MODE_2, + MAX22190_MODE_3, +}; + +typedef struct _max22190_fault1 { + uint8_t max22190_WBG:1; /* BIT 0 */ + uint8_t max22190_24VM:1; + uint8_t max22190_24VL:1; + uint8_t max22190_ALRMT1:1; + uint8_t max22190_ALRMT2:1; + uint8_t max22190_FAULT2:1; + uint8_t max22190_POR:1; + uint8_t max22190_CRC:1; /* BIT 7 */ +} max22190_fault1; + +typedef struct _max22190_fault1_en { + uint8_t max22190_WBGE:1; /* BIT 0 */ + uint8_t max22190_24VME:1; + uint8_t max22190_24VLE:1; + uint8_t max22190_ALRMT1E:1; + uint8_t max22190_ALRMT2E:1; + uint8_t max22190_FAULT2E:1; + uint8_t max22190_PORE:1; + uint8_t max22190_CRCE:1; /* BIT 7 */ +} max22190_fault1_en; + +typedef struct _max22190_fault2 { + uint8_t max22190_RFWBS:1; /* BIT 0 */ + uint8_t max22190_RFWBO:1; + uint8_t max22190_RFDIS:1; + uint8_t max22190_RFDIO:1; + uint8_t max22190_OTSHDN:1; + uint8_t max22190_FAULT8CK:1; + uint8_t max22190_DUMMY:2; /* BIT 7 */ +} max22190_fault2; + +typedef struct _max22190_fault2_en { + uint8_t max22190_RFWBSE:1; /* BIT 0 */ + uint8_t max22190_RFWBOE:1; + uint8_t max22190_RFDISE:1; + uint8_t max22190_RFDIOE:1; + uint8_t max22190_OTSHDNE:1; + uint8_t max22190_FAULT8CKE:1; + uint8_t max22190_DUMMY:2; /* BIT 7 */ +} max22190_fault2_en; + +typedef struct _max22190_cfg { + uint8_t max22190_DUMMY1:3; /* BIT 0 */ + uint8_t max22190_24VF:1; + uint8_t max22190_CLRF:1; + uint8_t max22190_DUMMY2:2; + uint8_t max22190_REFDI_SH_EN:1; /* BIT 7 */ +} max22190_cfg; + +typedef struct _max22190_filter { + uint8_t max22190_DELAY:3; /* BIT 0 */ + uint8_t max22190_FBP:1; + uint8_t max22190_WBE:1; + uint8_t max22190_DUMMY:2; /* BIT 7 */ +} max22190_filter; + +enum max22190_delay { + MAX22190_DELAY_50US, + MAX22190_DELAY_100US, + MAX22190_DELAY_400US, + MAX22190_DELAY_800US, + MAX22190_DELAY_1800US, + MAX22190_DELAY_3200US, + MAX22190_DELAY_12800US, + MAX22190_DELAY_20000US +}; + +struct max22190_config { + struct spi_dt_spec spi; + struct gpio_dt_spec fault_gpio; + struct gpio_dt_spec ready_gpio; + struct gpio_dt_spec latch_gpio; + max22190_filter filter[8]; + bool crc_en; + enum max22190_mode mode; + uint8_t pkt_size; +}; + +struct max22190_data { + struct gpio_driver_data common; + enum max22190_ch_state channels[MAX22190_CHANNELS]; + enum max22190_ch_wb_state wb[MAX22190_CHANNELS]; + max22190_cfg cfg; + max22190_fault1 fault1; + max22190_fault1_en fault1_en; + max22190_fault2 fault2; + max22190_fault2_en fault2_en; +}; + +/* + * @brief Compute the CRC5 value for MAX22190 + * @param data - Data array to calculate CRC for. + * @return CRC result. + */ +static uint8_t max22190_crc(uint8_t *data) +{ + int length = 19; + uint8_t crc_step, tmp; + uint8_t crc_init = 0x7; + uint8_t crc_poly = 0x35; + int i; + + /* + * This is the C custom implementation of CRC function for MAX22190, and + * can be found here: + * https://www.analog.com/en/design-notes/guidelines-to-implement-crc-algorithm.html + */ + uint32_t datainput = (uint32_t)((data[0] << 16) + (data[1] << 8) + data[2]); + + datainput = (datainput & 0xFFFFE0) + crc_init; + + tmp = (uint8_t)((datainput & 0xFC0000) >> 18); + + if ((tmp & 0x20) == 0x20) + crc_step = (uint8_t)(tmp ^ crc_poly); + else + crc_step = tmp; + + for (i = 0; i < length - 1; i++) { + tmp = (uint8_t)(((crc_step & 0x1F) << 1) + + ((datainput >> (length - 2 - i)) & 0x01)); + + if ((tmp & 0x20) == 0x20) + crc_step = (uint8_t)(tmp ^ crc_poly); + else + crc_step = tmp; + } + + return (uint8_t)(crc_step & 0x1F); +} + +/* + * @brief Update chan WB state in max22190_data + * + * @param dev - MAX22190 device. + * @param val - value to be set. + */ +static void max22190_update_wb_stat(const struct device *dev, uint8_t val) +{ + struct max22190_data *data = dev->data; + + for (int ch_n = 0; ch_n < 8; ch_n++) { + data->wb[ch_n] = (val >> ch_n) & 0x1; + } +} + +/* + * @brief Update chan IN state in max22190_data + * + * @param dev - MAX22190 device. + * @param val - value to be set. + */ +static void max22190_update_in_stat(const struct device *dev, uint8_t val) +{ + struct max22190_data *data = dev->data; + + for (int ch_n = 0; ch_n < 8; ch_n++) { + data->channels[ch_n] = (val >> ch_n) & 0x1; + } +} + +/* + * @brief Register write function for MAX22190 + * + * @param dev - MAX22190 device config. + * @param addr - Register value to which data is written. + * @param val - Value which is to be written to requested register. + * @return 0 in case of success, negative error code otherwise. + */ +static int max22190_reg_transceive(const struct device *dev, uint8_t addr, uint8_t val, uint8_t rw) +{ + uint8_t crc; + int ret; + + uint8_t local_rx_buff[MAX22190_MAX_PKT_SIZE] = {0}; + uint8_t local_tx_buff[MAX22190_MAX_PKT_SIZE] = {0}; + + const struct max22190_config *config = dev->config; + + struct spi_buf tx_buf = { + .buf = &local_tx_buff, + .len = config->pkt_size, + }; + const struct spi_buf_set tx = {.buffers = &tx_buf, .count = 1}; + + struct spi_buf rx_buf = { + .buf = &local_rx_buff, + .len = config->pkt_size, + }; + const struct spi_buf_set rx = {.buffers = &rx_buf, .count = 1}; + + if (config->crc_en & 0) { + rx_buf.len++; + } + + local_tx_buff[0] = FIELD_PREP(MAX22190_ADDR_MASK, addr) | + FIELD_PREP(MAX22190_RW_MASK, rw & 0x1); + local_tx_buff[1] = val; + + /* If CRC enabled calculate it */ + if (config->crc_en) { + local_tx_buff[2] = max22190_crc(&local_tx_buff[0]); + } + + /* write cmd & read resp at once */ + ret = spi_transceive_dt(&config->spi, &tx, &rx); + + if (ret) { + LOG_ERR("Err spi_transcieve_dt [%d]\n", ret); + return ret; + } + + /* if CRC enabled check readed */ + if (config->crc_en & 0) { + crc = max22190_crc(&local_rx_buff[0]); + if (crc != (local_rx_buff[2] & 0x1F)) { + LOG_ERR("READ CRC ERR (%d)-(%d)\n", crc, (local_rx_buff[2] & 0x1F)); + return -EINVAL; + } + } + + /* always (R/W) get DI reg in first byte */ + max22190_update_in_stat(dev, local_rx_buff[0]); + + /* in case of writing register we get as second byte WB reg */ + if (rw == MAX22190_WRITE) { + max22190_update_wb_stat(dev, local_rx_buff[1]); + } else { + /* in case of READ second byte is value we are looking for */ + ret = local_rx_buff[1]; + } + + return ret; +} + +/* + * @brief Register update function for MAX22190 + * + * @param dev - MAX22190 device. + * @param addr - Register valueto wich data is updated. + * @param mask - Corresponding mask to the data that will be updated. + * @param val - Updated value to be written in the register at update. + * @return 0 in case of success, negative error code otherwise. + */ +static int max22190_reg_update(const struct device *dev, uint8_t addr, uint8_t mask, uint8_t val) +{ + int ret; + uint32_t reg_val = 0; + + ret = max22190_reg_transceive(dev, addr, 0, MAX22190_READ); + + reg_val = ret; + reg_val &= ~mask; + reg_val |= mask & val; + + return max22190_reg_transceive(dev, addr, reg_val, MAX22190_WRITE); +} + +/* + * @brief Check FAULT1 and FAULT2 + * + * @param dev - MAX22190 device + */ +static void max22190_fault_check(const struct device *dev) +{ + /* FAULT1 */ + struct max22190_data *data = dev->data; + + uint8_t _fault1 = max22190_reg_transceive(dev, MAX22190_FAULT1_REG, 0, + MAX22190_READ); + + if (_fault1) { + memcpy(&data->fault1, &_fault1, 1); + + /* FAULT1_EN */ + uint8_t _fault1_en = max22190_reg_transceive(dev, MAX22190_FAULT1_EN_REG, 0, + MAX22190_READ); + + memcpy(&data->fault1_en, &_fault1_en, 1); + + PRINT_ERR_BIT(data->fault1.max22190_CRC, + data->fault1_en.max22190_CRCE); + PRINT_ERR_BIT(data->fault1.max22190_POR, + data->fault1_en.max22190_PORE); + PRINT_ERR_BIT(data->fault1.max22190_FAULT2, + data->fault1_en.max22190_FAULT2E); + PRINT_ERR_BIT(data->fault1.max22190_ALRMT2, + data->fault1_en.max22190_ALRMT2E); + PRINT_ERR_BIT(data->fault1.max22190_ALRMT1, + data->fault1_en.max22190_ALRMT1E); + PRINT_ERR_BIT(data->fault1.max22190_24VL, + data->fault1_en.max22190_24VLE); + PRINT_ERR_BIT(data->fault1.max22190_24VM, + data->fault1_en.max22190_24VME); + PRINT_ERR_BIT(data->fault1.max22190_WBG, + data->fault1_en.max22190_WBGE); + + if (data->fault1.max22190_WBG & data->fault1_en.max22190_WBGE) { + uint8_t wb_val = max22190_reg_transceive(dev, MAX22190_WB_REG, 0, + MAX22190_READ); + max22190_update_wb_stat(dev, (uint8_t)wb_val); + } + + if (data->fault1.max22190_FAULT2) { + /* FAULT2 */ + uint8_t _fault2 = max22190_reg_transceive(dev, MAX22190_FAULT2_REG, 0, + MAX22190_READ); + + memcpy(&data->fault2, &_fault2, 1); + + /* FAULT2_EN */ + uint8_t _fault2_en = max22190_reg_transceive(dev, MAX22190_FAULT2_EN_REG, 0, + MAX22190_READ); + + memcpy(&data->fault2_en, &_fault2_en, 1); + PRINT_ERR_BIT(data->fault2.max22190_RFWBS, + data->fault2_en.max22190_RFWBSE); + PRINT_ERR_BIT(data->fault2.max22190_RFWBO, + data->fault2_en.max22190_RFWBOE); + PRINT_ERR_BIT(data->fault2.max22190_RFDIS, + data->fault2_en.max22190_RFDISE); + PRINT_ERR_BIT(data->fault2.max22190_RFDIO, + data->fault2_en.max22190_RFDIOE); + PRINT_ERR_BIT(data->fault2.max22190_OTSHDN, + data->fault2_en.max22190_OTSHDNE); + PRINT_ERR_BIT(data->fault2.max22190_FAULT8CK, + data->fault2_en.max22190_FAULT8CKE); + } + } +} + +static void max22190_state_get(const struct device *dev) +{ + const struct max22190_config *config = dev->config; + + if (gpio_pin_get_dt(&config->fault_gpio)) + max22190_fault_check(dev); + + /* We are reading WB reg because on first byte will be clocked out DI reg + * on second byte we will ge WB value. + */ + uint8_t wb_val = max22190_reg_transceive(dev, MAX22190_WB_REG, 0, MAX22190_READ); + + max22190_update_wb_stat(dev, (uint8_t)wb_val); +} + +static int gpio_max22190_config(const struct device *dev, gpio_pin_t pin, gpio_flags_t flags) +{ + int err = 0; + + if ((flags & (GPIO_INPUT | GPIO_OUTPUT)) == GPIO_DISCONNECTED) { + return -ENOTSUP; + } + + if ((flags & GPIO_SINGLE_ENDED) != 0) { + return -ENOTSUP; + } + + if ((flags & (GPIO_PULL_UP | GPIO_PULL_DOWN)) != 0) { + return -ENOTSUP; + } + + if (flags & GPIO_INT_ENABLE) { + return -ENOTSUP; + } + + switch (flags & GPIO_DIR_MASK) { + case GPIO_INPUT: + LOG_INF("Nothing to do, only INPUT supported"); + break; + default: + LOG_ERR("On MAX22190 only input option is available!"); + return -ENOTSUP; + } + + return err; +} + +static void max22190_filter_set(const struct device *dev) +{ + const struct max22190_config *config = dev->config; + uint8_t reg = 0; + + for (int ch_n = 0 ; ch_n < 8 ; ch_n++) { + memcpy(®, &config->filter[ch_n], 1); + max22190_reg_transceive(dev, MAX22190_FILTER_IN_REG(ch_n), reg, + MAX22190_WRITE); + } +} + +static int max22190_fault_set(const struct device *dev) +{ + const struct max22190_data *data = dev->data; + uint8_t reg = 0; + int ret = 0; + + memcpy(®, &data->fault1_en, 1); + ret = max22190_reg_transceive(dev, MAX22190_FAULT1_EN_REG, reg, MAX22190_WRITE); + if (ret) + goto exit_fault_set; + + memcpy(®, &data->fault1, 1); + ret = max22190_reg_transceive(dev, MAX22190_FAULT1_REG, reg, MAX22190_WRITE); + if (ret) + goto exit_fault_set; + + memcpy(®, &data->fault2_en, 1); + ret = max22190_reg_transceive(dev, MAX22190_FAULT2_EN_REG, reg, MAX22190_WRITE); + if (ret) + goto exit_fault_set; + + memcpy(®, &data->fault2, 1); + ret = max22190_reg_transceive(dev, MAX22190_FAULT2_REG, reg, MAX22190_WRITE); + if (ret) + goto exit_fault_set; + +exit_fault_set: + LOG_ERR("Err spi_transcieve_dt [%d]\n", ret); + return ret; +} + +static int gpio_max22190_port_get_raw(const struct device *dev, gpio_port_value_t *value) +{ + const struct max22190_data *data = dev->data; + + max22190_state_get(dev); + *value = 0; + for (int ch_n = 0; ch_n < 8; ch_n++) { + /* IN ch state */ + *value |= data->channels[ch_n] << ch_n; + } + + return 0; +} + +static int gpio_max22190_init(const struct device *dev) +{ + const struct max22190_config *config = dev->config; + struct max22190_data *data = dev->data; + + LOG_DBG("GPIO MAX22190 init IN\n"); + + int err = 0; + + if (!spi_is_ready_dt(&config->spi)) { + LOG_ERR("SPI bus is not ready\n"); + return -ENODEV; + } + + /* setup READY gpio - normal low */ + if (!gpio_is_ready_dt(&config->ready_gpio)) { + LOG_ERR("READY GPIO device not ready"); + return -ENODEV; + } + + err = gpio_pin_configure_dt(&config->ready_gpio, GPIO_INPUT); + if (err < 0) { + LOG_ERR("Failed to configure reset GPIO"); + return err; + } + + /* setup FAULT gpio - normal high */ + if (!gpio_is_ready_dt(&config->fault_gpio)) { + LOG_ERR("FAULT GPIO device not ready"); + return -ENODEV; + } + + err = gpio_pin_configure_dt(&config->fault_gpio, GPIO_INPUT | GPIO_PULL_UP); + if (err < 0) { + LOG_ERR("Failed to configure DC GPIO"); + return err; + } + + /* setup LATCH gpio - normal high */ + if (!gpio_is_ready_dt(&config->latch_gpio)) { + LOG_ERR("LATCH GPIO device not ready"); + return -ENODEV; + } + + err = gpio_pin_configure_dt(&config->latch_gpio, GPIO_OUTPUT_INACTIVE); + if (err < 0) { + LOG_ERR("Failed to configure busy GPIO"); + return err; + } + + for (int i = 0 ; i < 8 ; i++) { + LOG_DBG("IN%d WBE [%d] FBP[%d] DELAY[%d]\n", i, config->filter[i].max22190_WBE, + config->filter[i].max22190_FBP, config->filter[i].max22190_DELAY); + } + + LOG_DBG(" > MAX22190 MODE: %x", config->mode); + LOG_DBG(" > MAX22190 PKT SIZE: %dbits (%dbytes)", config->pkt_size * 8, config->pkt_size); + LOG_DBG(" > MAX22190 CRC: %s", config->crc_en ? "enable" : "disable"); + + data->fault1_en.max22190_WBGE = 1; + data->fault1_en.max22190_PORE = 1; + + /* Set all FAULT and FAULT_EN regs */ + max22190_fault_set(dev); + + /* Set channels filters */ + max22190_filter_set(dev); + + /* POR bit need to be cleared after start */ + max22190_clean_por(dev); + + LOG_DBG("GPIO MAX22190 init OUT\n"); + return 0; +} + +static const struct gpio_driver_api gpio_max22190_api = { + .pin_configure = gpio_max22190_config, + .port_get_raw = gpio_max22190_port_get_raw, +}; + +/* Assign appropriate value for FILTER delay*/ +#define MAX22190_FILTER_SET_DELAY(delay) delay == 20000 ? MAX22190_DELAY_20000US : \ + delay == 12800 ? MAX22190_DELAY_12800US : \ + delay == 3200 ? MAX22190_DELAY_3200US : \ + delay == 1600 ? MAX22190_DELAY_1800US : \ + delay == 800 ? MAX22190_DELAY_800US : \ + delay == 400 ? MAX22190_DELAY_400US : \ + delay == 100 ? MAX22190_DELAY_100US : \ + delay == 50 ? MAX22190_DELAY_50US : \ + MAX22190_DELAY_20000US + +/* Set FILTERx reg */ +#define MAX22190_FILTER_BY_IDX(id, idx) { \ + .max22190_DELAY = MAX22190_FILTER_SET_DELAY(DT_INST_PROP_BY_IDX(id, filter_delays, idx)),\ + .max22190_FBP = DT_INST_PROP_BY_IDX(id, filter_fbps, idx), \ + .max22190_WBE = DT_INST_PROP_BY_IDX(id, filter_wbes, idx), \ +} + +#define GPIO_MAX22190_DEVICE(id) \ + static const struct max22190_config max22190_##id##_cfg = { \ + .spi = SPI_DT_SPEC_INST_GET(id, SPI_OP_MODE_MASTER | \ + SPI_WORD_SET(8U), 0U), \ + .ready_gpio = GPIO_DT_SPEC_INST_GET(id, drdy_gpios), \ + .fault_gpio = GPIO_DT_SPEC_INST_GET(id, fault_gpios), \ + .latch_gpio = GPIO_DT_SPEC_INST_GET(id, latch_gpios), \ + .mode = DT_INST_PROP(id, max22190_mode), \ + .crc_en = !(DT_INST_PROP(id, max22190_mode) & 0x1), \ + .pkt_size = !(DT_INST_PROP(id, max22190_mode) & 0x1) ? 3 : 2, \ + .filter = { \ + MAX22190_FILTER_BY_IDX(id, 0), MAX22190_FILTER_BY_IDX(id, 1), \ + MAX22190_FILTER_BY_IDX(id, 2), MAX22190_FILTER_BY_IDX(id, 3), \ + MAX22190_FILTER_BY_IDX(id, 4), MAX22190_FILTER_BY_IDX(id, 5), \ + MAX22190_FILTER_BY_IDX(id, 6), MAX22190_FILTER_BY_IDX(id, 7), \ + }, \ + }; \ + \ + static struct max22190_data max22190_##id##_data; \ + \ + DEVICE_DT_INST_DEFINE(id, &gpio_max22190_init, NULL, &max22190_##id##_data, \ + &max22190_##id##_cfg, POST_KERNEL, \ + CONFIG_GPIO_MAX22190_INIT_PRIORITY, &gpio_max22190_api); + +DT_INST_FOREACH_STATUS_OKAY(GPIO_MAX22190_DEVICE) diff --git a/dts/bindings/gpio/adi,max22190-gpio.yaml b/dts/bindings/gpio/adi,max22190-gpio.yaml new file mode 100644 index 000000000000000..f73ad823d0a3a52 --- /dev/null +++ b/dts/bindings/gpio/adi,max22190-gpio.yaml @@ -0,0 +1,103 @@ +# Copyright (c) 2010-2024 Analog Devices Inc. +# Copyright (c) 2024 BayLibre SAS +# SPDX-License-Identifier: Apache-2.0 + +description: | + ADI MAX22190 octal industrial Input with advanced diagnostic + capabilities like Wire break and over/under volatage. + Fiter configuration could be done on per channel bases, which + mean WB functionality and fiters could be set.Reffering to: + filter-wbes = for wire break + To ebale wire break set 1 else 0. Same principal is followed for: + filter-fbps and filter-dealys. + Sample binding + &sdp_spi { + status = "okay"; + pinctrl-names = "default"; + max22190_gpio0: max22190_gpio@0 { + compatible = "adi,max22190-gpio"; + reg = <0>; + + spi-max-frequency = <1000000>; + status = "okay"; + + gpio-controller; + #gpio-cells = <2>; + ngpios = <8>; + status = "okay"; + + max22190-mode = <1>; // modes range from 0-4 + + drdy-gpios = <&gpioj 12 GPIO_ACTIVE_LOW>; /* SDP-GPIO5 - PMOD-PIN8 */ + fault-gpios = <&gpioc 11 GPIO_ACTIVE_LOW>; /* SDP-SERIAL_INT - PMOD-PIN7 */ + latch-gpios = <&gpioj 13 GPIO_ACTIVE_LOW>; /* SDP-GPIO6 - PMOD-PIN9 */ + + filter-wbes = <1 0 0 0 0 0 0 0>; // wirebreak WBEx + filter-fbps = <0 0 0 0 0 0 0 0>; // programmable filter INx + filter-delays = <50 100 400 800 1600 3200 12800 20000>; // 1000 us == 1ms + }; + }; + +compatible: "adi,max22190-gpio" + +properties: + "#gpio-cells": + const: 2 + ngpios: + type: int + required: true + const: 8 + description: Number of gpios supported + drdy-gpios: + description: Ready pin which show when chip is ready + type: phandle-array + fault-gpios: + description: | + Fault pin indicates when there is Fault state in either FAULT1 or FAULT2 + bothe of which are cleaned on read once problem is not persistent + type: phandle-array + latch-gpios: + description: | + Latch the data so it could be read (partially duplicate CS) + type: phandle-array + max22190-mode: + type: int + default: 1 + required: true + enum: + - 0 + - 1 + - 2 + - 3 + description: | + max22190 mode is configured from M0 and M1 pins with + pull up or down resistors. + filter-wbes: + type: array + default: [1, 1, 1, 1, 1, 1, 1, 1] + description: | + WBE bit in all Filter registers statnd for wire break enable on each + channel, so to enable WB functionality set 1. + If WB on specific channel is disabled , FAULT will not be rised in case + wire is cut. + channels indentation start from CH0...CH7 + filter-fbps: + type: array + default: [1, 1, 1, 1, 1, 1, 1, 1] + description: | + Enable or disable filter + - 1 mean bypass + - 0 mean filter is used + channels indentation start from CH0...CH7 + filter-delays: + type: array + default: [20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000] + description: | + Used to setup filter delay. Values are set in uS. Default value is 20000us = 20ms + channels indentation start from CH0...CH7 + +gpio-cells: + - pin + - flags + +include: [gpio-controller.yaml, spi-device.yaml]