diff --git a/boards/st/stm32l4r9i_disco/stm32l4r9i_disco.dts b/boards/st/stm32l4r9i_disco/stm32l4r9i_disco.dts index 27cfdb2f0a7ad..2e6175dd171e0 100644 --- a/boards/st/stm32l4r9i_disco/stm32l4r9i_disco.dts +++ b/boards/st/stm32l4r9i_disco/stm32l4r9i_disco.dts @@ -22,7 +22,10 @@ leds { compatible = "gpio-leds"; - /* N.B. LD1 (orange) is not wired to MCU */ + orange_led: led_1 { + gpios = <&mfx 0 GPIO_ACTIVE_LOW>; + label = "User LD1"; + }; green_led: led_2 { gpios = <&gpioh 4 GPIO_ACTIVE_LOW>; label = "User LD2"; @@ -36,11 +39,36 @@ gpios = <&gpioc 13 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>; zephyr,code = ; }; + joy_up: joystick_up { + label = "joystick up"; + gpios = <&mfx 1 (GPIO_ACTIVE_HIGH | GPIO_PULL_UP)>; + zephyr,code = ; + }; + joy_down: joystick_down { + label = "joystick down"; + gpios = <&mfx 2 (GPIO_ACTIVE_HIGH | GPIO_PULL_UP)>; + zephyr,code = ; + }; + joy_right: joystick_right { + label = "joystick right"; + gpios = <&mfx 3 (GPIO_ACTIVE_HIGH | GPIO_PULL_UP)>; + zephyr,code = ; + }; + joy_left: joystick_left { + label = "joystick left"; + gpios = <&mfx 4 (GPIO_ACTIVE_HIGH | GPIO_PULL_UP)>; + zephyr,code = ; + }; }; aliases { led0 = &green_led; + led1 = &orange_led; sw0 = &joy_sel; + sw1 = &joy_up; + sw2 = &joy_down; + sw3 = &joy_right; + sw4 = &joy_left; die-temp0 = &die_temp; volt-sensor0 = &vref; volt-sensor1 = &vbat; @@ -155,6 +183,15 @@ pinctrl-0 = <&i2c1_scl_pb6 &i2c1_sda_pg13>; pinctrl-names = "default"; clock-frequency = ; + + mfx: gpio@42 { + compatible = "st,mfxstm32l152"; + reg = <0x42>; + ngpios = <24>; + int-gpios = <&gpioi 1 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>; + gpio-controller; + #gpio-cells = <2>; + }; }; &i2c3 { diff --git a/drivers/gpio/CMakeLists.txt b/drivers/gpio/CMakeLists.txt index d88cb2b87a908..e921cfca9221a 100644 --- a/drivers/gpio/CMakeLists.txt +++ b/drivers/gpio/CMakeLists.txt @@ -55,6 +55,7 @@ zephyr_library_sources_ifdef(CONFIG_GPIO_MCUX gpio_mcux.c) zephyr_library_sources_ifdef(CONFIG_GPIO_MCUX_IGPIO gpio_mcux_igpio.c) zephyr_library_sources_ifdef(CONFIG_GPIO_MCUX_LPC gpio_mcux_lpc.c) zephyr_library_sources_ifdef(CONFIG_GPIO_MCUX_RGPIO gpio_mcux_rgpio.c) +zephyr_library_sources_ifdef(CONFIG_GPIO_MFXSTM32L152 gpio_mfxstm32l152.c) zephyr_library_sources_ifdef(CONFIG_GPIO_MMIO32 gpio_mmio32.c) zephyr_library_sources_ifdef(CONFIG_GPIO_NCT38XX gpio_nct38xx.c) zephyr_library_sources_ifdef(CONFIG_GPIO_NCT38XX gpio_nct38xx_port.c) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 6ca6216a04642..63de725f663f3 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -143,6 +143,7 @@ source "drivers/gpio/Kconfig.mcux_igpio" source "drivers/gpio/Kconfig.mcux_lpc" source "drivers/gpio/Kconfig.mcux_rgpio" source "drivers/gpio/Kconfig.mec5" +source "drivers/gpio/Kconfig.mfxstm32l152" source "drivers/gpio/Kconfig.mmio32" source "drivers/gpio/Kconfig.nct38xx" source "drivers/gpio/Kconfig.neorv32" diff --git a/drivers/gpio/Kconfig.mfxstm32l152 b/drivers/gpio/Kconfig.mfxstm32l152 new file mode 100644 index 0000000000000..ac9c649f0ca90 --- /dev/null +++ b/drivers/gpio/Kconfig.mfxstm32l152 @@ -0,0 +1,19 @@ +# MFXSTM32L152 GPIO configuration options + +# Copyright (c) 2024 STMicroelectronics +# SPDX-License-Identifier: Apache-2.0 + +menuconfig GPIO_MFXSTM32L152 + bool "MFXSTM32L152 I2C-based GPIO chip" + default y + depends on DT_HAS_ST_MFXSTM32L152_ENABLED + select I2C + help + Enable driver for MFXSTM32L152 I2C-based GPIO chip. + +config GPIO_MFXSTM32L152_INIT_PRIORITY + int "Init priority" + default 70 + depends on GPIO_MFXSTM32L152 + help + Device driver initialization priority. diff --git a/drivers/gpio/gpio_mfxstm32l152.c b/drivers/gpio/gpio_mfxstm32l152.c new file mode 100644 index 0000000000000..ede0740df8d47 --- /dev/null +++ b/drivers/gpio/gpio_mfxstm32l152.c @@ -0,0 +1,639 @@ +/* + * Copyright (c) 2024 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT st_mfxstm32l152 + +/** + * @file Driver for ST MFXstm32l152 I2C-based GPIO driver. + */ +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define LOG_LEVEL CONFIG_GPIO_LOG_LEVEL +#include +LOG_MODULE_REGISTER(mfxstm32l152); + +/* Register definitions */ +#define REG_ID 0x00 /* const 0x7b */ +#define REG_GPIO_IRQ_PEND 0x0c /* GPIO irq pending */ +#define REG_GPIO_STATE 0x10 /* GPIO state */ +#define REG_SYS_CTRL 0x40 /* System control */ +#define REG_SYS_IRQ_MODE 0x41 /* System irq mode */ +#define SYS_IRQ_MODE_OPEN_DRAIN (0 << 0) +#define SYS_IRQ_MODE_PUSH_PULL (1 << 0) +#define SYS_IRQ_MODE_POL_LOW (0 << 1) +#define SYS_IRQ_MODE_POL_HIGH (1 << 1) +#define REG_SYS_IRQ_EN 0x42 /* System irq enable */ +#define REG_GPIO_IRQ_EN 0x48 /* GPIO irq enable */ +#define REG_GPIO_IRQ_EVT 0x4c /* GPIO irq event */ +#define REG_GPIO_IRQ_TYPE 0x50 /* GPIO irq type */ +#define REG_GPIO_IRQ_ACK 0x54 /* GPIO irq ack */ +#define REG_GPIO_DIR 0x60 /* GPIO direction control */ +#define REG_GPIO_PUPD 0x68 /* GPIO pull-up/pull-down control */ +#define REG_GPIO_SET 0x6c /* GPIO set control */ +#define REG_GPIO_CLR 0x70 /* GPIO clear control */ + +#define MFXSTM32L152_ID 0x7b + +/** Configuration data */ +struct mfxstm32l152_drv_cfg { + /* gpio_driver_config needs to be first */ + struct gpio_driver_config common; + + /** Master I2C DT specification */ + struct i2c_dt_spec i2c_spec; + struct gpio_dt_spec int_gpio; +}; + +/** Cache of the pins configuration */ +struct mfxstm32l152_pins_state { + uint32_t direction; + uint32_t pupd; + uint32_t irq_enabled; +}; + +/** Runtime driver data */ +struct mfxstm32l152_drv_data { + /* gpio_driver_data needs to be first */ + struct gpio_driver_data common; + + /** Driver lock */ + struct k_sem lock; + + sys_slist_t callbacks; + struct k_work work; + + const struct device *dev; + struct gpio_callback int_gpio_cb; + struct mfxstm32l152_pins_state pins_state; +}; + +/** + * @brief read a register value from the MFX + * + * @param dev Pointer to the device structure for the driver instance. + * @param reg Address of the register + * @param buf Pointer to the place to store the value + * + * @retval 0 if successful. + * @retval Negative value for error code. + */ +static int read_reg(const struct device *dev, uint8_t reg, uint8_t *buf) +{ + const struct mfxstm32l152_drv_cfg *const config = dev->config; + uint8_t value; + int ret; + + ret = i2c_burst_read_dt(&config->i2c_spec, reg, (uint8_t *)&value, 1); + if (ret != 0) { + LOG_ERR("%s: error reading register 0x%X (%d)", dev->name, reg, ret); + return ret; + } + + *buf = value; + LOG_DBG("%s: Read: REG[0x%X] = 0x%X", dev->name, reg, value); + + return 0; +} + +/** + * @brief write a register of the MFX + * + * @param dev Pointer to the device structure for the driver instance. + * @param reg Address of the register to be written + * @param value Value to be written into the register + * + * @retval 0 if successful. + * @retval Negative value for error code. + */ +static int write_reg(const struct device *dev, uint8_t reg, uint8_t value) +{ + const struct mfxstm32l152_drv_cfg *const config = dev->config; + uint8_t buf[2]; + int ret; + + LOG_DBG("%s: Write: REG[0x%X] = 0x%X", dev->name, reg, value); + + buf[0] = reg; + buf[1] = value; + ret = i2c_write_dt(&config->i2c_spec, buf, sizeof(buf)); + if (ret != 0) { + LOG_ERR("%s: error writing to register 0x%X (%d)", dev->name, reg, ret); + } + + return ret; +} + +/** + * @brief Gets the state of a specified block of 3 registers from the MFX + * + * @param dev Pointer to the device structure for the driver instance. + * @param reg Address of the first of 3 registers to be read. + * @param buf Pointer to the buffer to output the register. + * + * @retval 0 if successful. + * @retval Negative value for error code. + */ +static int read_port_regs(const struct device *dev, uint8_t reg, uint32_t *buf) +{ + const struct mfxstm32l152_drv_cfg *const config = dev->config; + uint32_t port_data, value; + int ret; + + ret = i2c_burst_read_dt(&config->i2c_spec, reg, (uint8_t *)&port_data, 3); + if (ret != 0) { + LOG_ERR("%s: error reading register 0x%X (%d)", dev->name, reg, ret); + return ret; + } + + value = sys_le24_to_cpu(port_data); + *buf = value; + LOG_DBG("%s: Read: REG[0x%X] = 0x%X, REG[0x%X] = 0x%X, REG[0x%X] = 0x%X", dev->name, reg, + (*buf & 0xFF), (reg + 1), ((*buf >> 8) & 0xFF), (reg + 2), ((*buf >> 16) & 0xFF)); + + return 0; +} + +/** + * @brief writes to a specified block of 3 registers into the MFX + * + * @param dev Pointer to the device structure for the driver instance. + * @param reg Address of the first of 3 registers to be written. + * @param value The pin value to be written into the registers. + * + * @retval 0 if successful. + * @retval Negative value for error code. + */ +static int write_port_regs(const struct device *dev, uint8_t reg, uint32_t value) +{ + const struct mfxstm32l152_drv_cfg *const config = dev->config; + uint8_t buf[4]; + int ret; + + LOG_DBG("%s: Write: REG[0x%X] = 0x%X, REG[0x%X] = 0x%X, REG[0x%X] = 0x%X", dev->name, reg, + (value & 0xFF), (reg + 1), ((value >> 8) & 0xFF), (reg + 2), + ((value >> 16) & 0xFF)); + + buf[0] = reg; + sys_put_le24(value, &buf[1]); + ret = i2c_write_dt(&config->i2c_spec, buf, sizeof(buf)); + if (ret != 0) { + LOG_ERR("%s: error writing to register 0x%X (%d)", dev->name, reg, ret); + } + + return ret; +} + +/** + * @brief Handles interrupt triggered by the interrupt pin of MFXSTM32L152. + * + * If int_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 mfxstm32l152_handle_interrupt(const struct device *dev) +{ + struct mfxstm32l152_drv_data *drv_data = dev->data; + uint32_t irq_status; + int ret; + + k_sem_take(&drv_data->lock, K_FOREVER); + + /* Any interrupts enabled? */ + if (!drv_data->pins_state.irq_enabled) { + k_sem_give(&drv_data->lock); + return; + } + + /* Check pending irq status */ + ret = read_port_regs(dev, REG_GPIO_IRQ_PEND, &irq_status); + if (ret != 0) { + k_sem_give(&drv_data->lock); + return; + } + + if (irq_status == 0) { + k_sem_give(&drv_data->lock); + return; + } + + /* Ack everything */ + ret = write_port_regs(dev, REG_GPIO_IRQ_ACK, irq_status); + if (ret != 0) { + k_sem_give(&drv_data->lock); + return; + } + + k_sem_give(&drv_data->lock); + + gpio_fire_callbacks(&drv_data->callbacks, dev, irq_status); +} + +/** + * @brief Work handler for MFXSTM32L152 interrupt + * + * @param work Work struct that contains pointer to interrupt handler function + */ +static void mfxstm32l152_work_handler(struct k_work *work) +{ + struct mfxstm32l152_drv_data *drv_data = + CONTAINER_OF(work, struct mfxstm32l152_drv_data, work); + + mfxstm32l152_handle_interrupt(drv_data->dev); +} + +/** + * @brief ISR for interrupt pin of MFXSTM32L152 + * + * @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 mfxstm32l152_int_gpio_handler(const struct device *dev, struct gpio_callback *gpio_cb, + uint32_t pins) +{ + ARG_UNUSED(dev); + ARG_UNUSED(pins); + + struct mfxstm32l152_drv_data *drv_data = + CONTAINER_OF(gpio_cb, struct mfxstm32l152_drv_data, int_gpio_cb); + + k_work_submit(&drv_data->work); +} + +static int set_pin_dir_mode(const struct device *dev, gpio_pin_t pin, gpio_flags_t flags) +{ + struct mfxstm32l152_drv_data *const drvdata = + (struct mfxstm32l152_drv_data *const)dev->data; + uint32_t *dir_cache = &drvdata->pins_state.direction; + uint32_t *mode_cache = &drvdata->pins_state.pupd; + bool need_update = false; + uint32_t dir, mode; + int ret = 0; + + /* In case of configure in output mode first set initial state */ + if ((flags & GPIO_OUTPUT) != 0U) { + if ((flags & GPIO_OUTPUT_INIT_HIGH) != 0U) { + ret = write_port_regs(dev, REG_GPIO_SET, BIT(pin)); + if (ret != 0) { + goto out; + } + } else if ((flags & GPIO_OUTPUT_INIT_LOW) != 0U) { + ret = write_port_regs(dev, REG_GPIO_CLR, BIT(pin)); + if (ret != 0) { + goto out; + } + } + } + + /* Configure direction */ + if ((flags & GPIO_OUTPUT) && ((*mode_cache & BIT(pin)) == 0)) { + dir = *dir_cache | BIT(pin); + need_update = true; + } else if ((flags & GPIO_INPUT) && ((*mode_cache & BIT(pin)) != 0)) { + dir = *dir_cache & ~BIT(pin); + need_update = true; + } + if (need_update) { + ret = write_port_regs(dev, REG_GPIO_DIR, dir); + if (ret != 0) { + goto out; + } + *dir_cache = dir; + } + + /* In case of input mode, configure PullUp/ PullDown */ + need_update = false; + if ((flags & GPIO_INPUT) != 0U) { + if ((flags & GPIO_PULL_UP) && ((*mode_cache & BIT(pin)) == 0)) { + mode = *mode_cache | BIT(pin); + need_update = true; + } else if ((flags & GPIO_PULL_DOWN) && ((*mode_cache & BIT(pin)) != 0)) { + mode = *mode_cache & ~BIT(pin); + need_update = true; + } + } + if (need_update) { + ret = write_port_regs(dev, REG_GPIO_PUPD, mode); + if (ret != 0) { + goto out; + } + *mode_cache = mode; + } + +out: + return ret; +} + +static int mfxstm32l152_configure(const struct device *dev, gpio_pin_t pin, gpio_flags_t flags) +{ + struct mfxstm32l152_drv_data *const drvdata = + (struct mfxstm32l152_drv_data *const)dev->data; + int ret; + + /* No support for disconnected pin, single ended and simultaneous input / output */ + if ((flags & (GPIO_INPUT | GPIO_OUTPUT)) == GPIO_DISCONNECTED || + (flags & GPIO_SINGLE_ENDED) != 0 || + (((flags & GPIO_INPUT) != 0) && ((flags & GPIO_OUTPUT) != 0))) { + return -ENOTSUP; + } + + /* Can't do I2C bus operations from an ISR */ + if (k_is_in_isr()) { + return -EWOULDBLOCK; + } + + k_sem_take(&drvdata->lock, K_FOREVER); + + ret = set_pin_dir_mode(dev, pin, flags); + if (ret != 0) { + LOG_ERR("%s: error setting pin direction and mode (%d)", dev->name, ret); + } + + k_sem_give(&drvdata->lock); + + return ret; +} + +static int mfxstm32l152_port_get_raw(const struct device *dev, uint32_t *value) +{ + struct mfxstm32l152_drv_data *const drvdata = + (struct mfxstm32l152_drv_data *const)dev->data; + uint32_t reg_value; + int ret; + + /* Can't do I2C bus operations from an ISR */ + if (k_is_in_isr()) { + return -EWOULDBLOCK; + } + + k_sem_take(&drvdata->lock, K_FOREVER); + ret = read_port_regs(dev, REG_GPIO_STATE, ®_value); + k_sem_give(&drvdata->lock); + + if (ret == 0) { + *value = reg_value; + } + + return ret; +} + +static int mfxstm32l152_port_set_bits_raw(const struct device *dev, uint32_t mask) +{ + struct mfxstm32l152_drv_data *const drvdata = + (struct mfxstm32l152_drv_data *const)dev->data; + int ret; + + /* Can't do I2C bus operations from an ISR */ + if (k_is_in_isr()) { + return -EWOULDBLOCK; + } + + k_sem_take(&drvdata->lock, K_FOREVER); + ret = write_port_regs(dev, REG_GPIO_SET, mask); + k_sem_give(&drvdata->lock); + + return ret; +} + +static int mfxstm32l152_port_clear_bits_raw(const struct device *dev, uint32_t mask) +{ + struct mfxstm32l152_drv_data *const drvdata = + (struct mfxstm32l152_drv_data *const)dev->data; + int ret; + + /* Can't do I2C bus operations from an ISR */ + if (k_is_in_isr()) { + return -EWOULDBLOCK; + } + + k_sem_take(&drvdata->lock, K_FOREVER); + ret = write_port_regs(dev, REG_GPIO_CLR, mask); + k_sem_give(&drvdata->lock); + + return ret; +} + +static int mfxstm32l152_pin_interrupt_configure(const struct device *dev, gpio_pin_t pin, + enum gpio_int_mode mode, enum gpio_int_trig trig) +{ + struct mfxstm32l152_drv_data *drv_data = dev->data; + uint32_t irq_event, irq_type; + int ret; + + k_sem_take(&drv_data->lock, K_FOREVER); + + if (mode == GPIO_INT_MODE_DISABLED) { + drv_data->pins_state.irq_enabled &= ~BIT(pin); + ret = write_port_regs(dev, REG_GPIO_IRQ_EN, drv_data->pins_state.irq_enabled); + if (ret != 0) { + k_sem_give(&drv_data->lock); + return ret; + } + + if (drv_data->pins_state.irq_enabled == 0) { + ret = write_reg(dev, REG_SYS_IRQ_EN, 0); + k_sem_give(&drv_data->lock); + return ret; + } + + k_sem_give(&drv_data->lock); + + return ret; + } + + /* Set mode (EDGE / LEVEL */ + ret = read_port_regs(dev, REG_GPIO_IRQ_EVT, &irq_event); + if (ret != 0) { + k_sem_give(&drv_data->lock); + return ret; + } + + if (mode == GPIO_INT_MODE_EDGE) { + irq_event |= BIT(pin); + } else { + irq_event &= ~BIT(pin); + } + + ret = write_port_regs(dev, REG_GPIO_IRQ_EVT, irq_event); + if (ret != 0) { + k_sem_give(&drv_data->lock); + return ret; + } + + /* Set High / Rising or Low / Falling */ + ret = read_port_regs(dev, REG_GPIO_IRQ_TYPE, &irq_type); + if (ret != 0) { + k_sem_give(&drv_data->lock); + return ret; + } + + /* We cannot handle BOTH edge so if BOTH is asked, we set it as HIGH */ + if (trig == GPIO_INT_TRIG_HIGH || trig == GPIO_INT_TRIG_BOTH) { + irq_type |= BIT(pin); + } else { + irq_type &= ~BIT(pin); + } + + ret = write_port_regs(dev, REG_GPIO_IRQ_TYPE, irq_type); + if (ret != 0) { + k_sem_give(&drv_data->lock); + return ret; + } + + /* Enable the interrupt */ + drv_data->pins_state.irq_enabled |= BIT(pin); + ret = write_port_regs(dev, REG_GPIO_IRQ_EN, drv_data->pins_state.irq_enabled); + if (ret != 0) { + k_sem_give(&drv_data->lock); + return ret; + } + + ret = write_reg(dev, REG_SYS_IRQ_EN, 1); + + k_sem_give(&drv_data->lock); + + return ret; +} + +static int mfxstm32l152_manage_callback(const struct device *dev, struct gpio_callback *callback, + bool set) +{ + struct mfxstm32l152_drv_data *drv_data = dev->data; + + return gpio_manage_callback(&drv_data->callbacks, callback, set); +} + +static int mfxstm32l152_init(const struct device *dev) +{ + struct mfxstm32l152_drv_data *const drvdata = + (struct mfxstm32l152_drv_data *const)dev->data; + const struct mfxstm32l152_drv_cfg *drv_cfg = dev->config; + uint8_t chip_id, int_pin = 0; + int ret; + + if (!device_is_ready(drv_cfg->i2c_spec.bus)) { + LOG_ERR("I2C device not found"); + return -ENODEV; + } + + k_sem_init(&drvdata->lock, 1, 1); + + ret = read_reg(dev, REG_ID, &chip_id); + if (ret != 0) { + LOG_ERR("%s: Unable to read Chip ID", dev->name); + return ret; + } + + if (chip_id != MFXSTM32L152_ID) { + LOG_ERR("%s: Invalid Chip ID", dev->name); + return -EINVAL; + } + + ret = read_port_regs(dev, REG_GPIO_DIR, &drvdata->pins_state.direction); + if (ret != 0) { + LOG_ERR("%s: Unable to read initial directions", dev->name); + return ret; + } + + ret = read_port_regs(dev, REG_GPIO_PUPD, &drvdata->pins_state.pupd); + if (ret != 0) { + LOG_ERR("%s: Unable to read initial directions", dev->name); + return ret; + } + + ret = write_reg(dev, REG_SYS_CTRL, 0x01); + if (ret != 0) { + LOG_ERR("%s: Failed to enable GPIO", dev->name); + return ret; + } + + /* If the INT line is available, configure the callback for it. */ + if (drv_cfg->int_gpio.port) { + if (!gpio_is_ready_dt(&drv_cfg->int_gpio)) { + LOG_ERR("Cannot get pointer to gpio interrupt device %s init failed", + dev->name); + return -EINVAL; + } + + drvdata->dev = dev; + + k_work_init(&drvdata->work, mfxstm32l152_work_handler); + + ret = gpio_pin_configure_dt(&drv_cfg->int_gpio, GPIO_INPUT); + if (ret != 0) { + LOG_ERR("%s init failed: %d", dev->name, ret); + return ret; + } + + ret = gpio_pin_interrupt_configure_dt(&drv_cfg->int_gpio, GPIO_INT_EDGE_TO_ACTIVE); + if (ret != 0) { + LOG_ERR("%s init failed: %d", dev->name, ret); + return ret; + } + + gpio_init_callback(&drvdata->int_gpio_cb, mfxstm32l152_int_gpio_handler, + BIT(drv_cfg->int_gpio.pin)); + + ret = gpio_add_callback(drv_cfg->int_gpio.port, &drvdata->int_gpio_cb); + if (ret != 0) { + LOG_ERR("%s init failed: %d", dev->name, ret); + return ret; + } + + /* Configure the INT_OUT pin based on int_gpio dt_flags */ + if ((drv_cfg->int_gpio.dt_flags & GPIO_OPEN_DRAIN) != 0) { + int_pin |= SYS_IRQ_MODE_OPEN_DRAIN; + } else { + int_pin |= SYS_IRQ_MODE_PUSH_PULL; + } + if ((drv_cfg->int_gpio.dt_flags & GPIO_ACTIVE_LOW) != 0) { + int_pin |= SYS_IRQ_MODE_POL_LOW; + } else { + int_pin |= SYS_IRQ_MODE_POL_HIGH; + } + + ret = write_reg(dev, REG_SYS_IRQ_MODE, int_pin); + } + + return ret; +} + +static const struct gpio_driver_api mfxstm32l152_drv_api = { + .pin_configure = mfxstm32l152_configure, + .port_get_raw = mfxstm32l152_port_get_raw, + .port_set_masked_raw = NULL, + .port_set_bits_raw = mfxstm32l152_port_set_bits_raw, + .port_clear_bits_raw = mfxstm32l152_port_clear_bits_raw, + .port_toggle_bits = NULL, + .pin_interrupt_configure = mfxstm32l152_pin_interrupt_configure, + .manage_callback = mfxstm32l152_manage_callback, +}; + +#define MFXSTM32L152_INIT(inst) \ + static struct mfxstm32l152_drv_cfg mfxstm32l152_##inst##_config = { \ + .common = {.port_pin_mask = 0x0fff}, \ + .i2c_spec = I2C_DT_SPEC_INST_GET(inst), \ + .int_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, int_gpios, {0}), \ + }; \ + \ + static struct mfxstm32l152_drv_data mfxstm32l152_##inst##_drv_data; \ + \ + DEVICE_DT_INST_DEFINE(inst, mfxstm32l152_init, NULL, &mfxstm32l152_##inst##_drv_data, \ + &mfxstm32l152_##inst##_config, POST_KERNEL, \ + CONFIG_GPIO_MFXSTM32L152_INIT_PRIORITY, &mfxstm32l152_drv_api); + +DT_INST_FOREACH_STATUS_OKAY(MFXSTM32L152_INIT) diff --git a/dts/bindings/gpio/st,mfxstm32l152.yaml b/dts/bindings/gpio/st,mfxstm32l152.yaml new file mode 100644 index 0000000000000..b01137043c307 --- /dev/null +++ b/dts/bindings/gpio/st,mfxstm32l152.yaml @@ -0,0 +1,22 @@ +# Copyright (c) 2024 STMicroelectronics +# SPDX-License-Identifier: Apache-2.0 + +description: MFXSTM32L152 I2C-based GPIO expander + +compatible: "st,mfxstm32l152" + +include: [gpio-controller.yaml, i2c-device.yaml] + +properties: + int-gpios: + type: phandle-array + description: | + GPIO connected to the controller INT pin. This pin is active-low + and open-drain. + + "#gpio-cells": + const: 2 + +gpio-cells: + - pin + - flags