diff --git a/drivers/gpio/gpio_silabs.c b/drivers/gpio/gpio_silabs.c index b2935c9555185..a3b1f2fd7c636 100644 --- a/drivers/gpio/gpio_silabs.c +++ b/drivers/gpio/gpio_silabs.c @@ -22,8 +22,11 @@ LOG_MODULE_REGISTER(gpio_silabs, CONFIG_GPIO_LOG_LEVEL); #define GET_SILABS_GPIO_INDEX(node_id) \ (DT_REG_ADDR(node_id) - DT_REG_ADDR(DT_NODELABEL(gpioa))) / SILABS_GPIO_PORT_ADDR_SPACE_SIZE -#define NUMBER_OF_PORTS (SIZEOF_FIELD(GPIO_TypeDef, P) / SIZEOF_FIELD(GPIO_TypeDef, P[0])) -#define NUM_IRQ_LINES 16 +#define NUMBER_OF_PORTS (SIZEOF_FIELD(GPIO_TypeDef, P) / SIZEOF_FIELD(GPIO_TypeDef, P[0])) +#define NUM_IRQ_LINES 16 +#define MAX_EM4_IRQ_PER_PORT 3 +#define EM4WU_TO_INT(wu) ((wu) + NUM_IRQ_LINES) +#define INT_TO_EM4WU(int_no) ((int_no) - NUM_IRQ_LINES) struct gpio_silabs_common_config { /* IRQ configuration function */ @@ -39,6 +42,11 @@ struct gpio_silabs_common_data { const struct device *ports[NUMBER_OF_PORTS]; }; +struct gpio_silabs_em4wu_mapping { + uint8_t wu_no; + uint8_t pin; +}; + struct gpio_silabs_port_config { /* gpio_driver_config must be first */ struct gpio_driver_config common; @@ -46,6 +54,10 @@ struct gpio_silabs_port_config { sl_gpio_port_t gpio_index; /* pointer to common device */ const struct device *common_dev; + /* Number of valid EM4 wakeup interrupt mappings */ + int em4wu_pin_count; + /* EM4 wakeup interrupt mapping for GPIO pins */ + struct gpio_silabs_em4wu_mapping em4wu_pins[MAX_EM4_IRQ_PER_PORT]; }; struct gpio_silabs_port_data { @@ -253,6 +265,38 @@ static int interrupt_to_pin(int int_no) return ROUND_DOWN(int_no, 4) + FIELD_GET(0xF << (offset * 4), reg); } +static int gpio_silabs_pin_interrupt_configure_em4wu(sl_gpio_t *gpio, enum gpio_int_mode mode, + enum gpio_int_trig trig) +{ + int32_t em4wu_no = sl_hal_gpio_get_em4_interrupt_number(gpio); + int32_t int_no = EM4WU_TO_INT(em4wu_no); + + if (em4wu_no == SL_GPIO_INTERRUPT_UNAVAILABLE) { + LOG_ERR("Pin %u is not EM4 wakeup capable", gpio->pin); + return -EINVAL; + } + + if (mode != GPIO_INT_MODE_DISABLED) { + if (trig == GPIO_INT_TRIG_BOTH) { + LOG_ERR("EM4 wakeup interrupt on pin %u can only trigger on one edge", + gpio->pin); + return -ENOTSUP; + } + + sl_hal_gpio_configure_wakeup_em4_external_interrupt(gpio, em4wu_no, + trig == GPIO_INT_TRIG_HIGH); + } + + if (mode == GPIO_INT_MODE_DISABLED) { + sl_hal_gpio_disable_interrupts(BIT(int_no)); + sl_hal_gpio_disable_pin_em4_wakeup(BIT(int_no)); + } else { + sl_hal_gpio_enable_interrupts(BIT(int_no)); + } + + return 0; +} + static int gpio_silabs_pin_interrupt_configure(const struct device *dev, gpio_pin_t pin, enum gpio_int_mode mode, enum gpio_int_trig trig) { @@ -264,12 +308,20 @@ static int gpio_silabs_pin_interrupt_configure(const struct device *dev, gpio_pi sl_gpio_interrupt_flag_t flag = SL_GPIO_INTERRUPT_RISING_FALLING_EDGE; uint32_t enabled_interrupts; int32_t int_no = SL_GPIO_INTERRUPT_UNAVAILABLE; + bool em4_wakeup; + + em4_wakeup = ((trig & GPIO_INT_WAKEUP) == GPIO_INT_WAKEUP); + trig &= ~GPIO_INT_WAKEUP; if (mode == GPIO_INT_MODE_LEVEL) { LOG_ERR("Level interrupt not supported on pin %u", pin); return -ENOTSUP; } + if (em4_wakeup) { + return gpio_silabs_pin_interrupt_configure_em4wu(&gpio, mode, trig); + } + enabled_interrupts = sl_hal_gpio_get_enabled_interrupts(); for (int i = 0; i < NUM_IRQ_LINES; ++i) { @@ -315,6 +367,25 @@ static int gpio_silabs_port_manage_callback(const struct device *dev, struct gpi return gpio_manage_callback(&data->callbacks, cb, set); } +static void gpio_silabs_em4wu_interrupt_to_port_pin(struct gpio_silabs_common_data *data, + int int_no, int *port, int *pin) +{ + ARRAY_FOR_EACH(data->ports, p) { + const struct device *dev = data->ports[p]; + const struct gpio_silabs_port_config *config = dev->config; + + for (int i = 0; i < config->em4wu_pin_count; i++) { + const struct gpio_silabs_em4wu_mapping *em4wu = &config->em4wu_pins[i]; + + if (em4wu->wu_no == INT_TO_EM4WU(int_no)) { + *port = config->gpio_index; + *pin = em4wu->pin; + return; + } + } + } +} + static void gpio_silabs_common_isr(const struct device *dev) { struct gpio_silabs_common_data *data = dev->data; @@ -323,8 +394,15 @@ static void gpio_silabs_common_isr(const struct device *dev) while (pending) { int int_no = find_lsb_set(pending) - 1; - int port = interrupt_to_port(int_no); - int pin = interrupt_to_pin(int_no); + int port = -1; + int pin = -1; + + if (int_no >= NUM_IRQ_LINES) { + gpio_silabs_em4wu_interrupt_to_port_pin(data, int_no, &port, &pin); + } else { + port = interrupt_to_port(int_no); + pin = interrupt_to_pin(int_no); + } port_pin_masks[port] |= BIT(pin); sl_hal_gpio_clear_interrupts(BIT(int_no)); @@ -385,11 +463,19 @@ static int gpio_silabs_common_init(const struct device *dev) return 0; } +#define EM4_WAKEUP_PIN(node, prop, idx) \ + { \ + .wu_no = DT_PROP_BY_IDX(node, prop, idx), \ + .pin = DT_PROP_BY_IDX(node, silabs_wakeup_pins, idx), \ + }, + #define GPIO_PORT_INIT(n) \ static const struct gpio_silabs_port_config gpio_silabs_port_config_##n = { \ .common.port_pin_mask = (gpio_port_pins_t)(-1), \ .gpio_index = GET_SILABS_GPIO_INDEX(n), \ .common_dev = DEVICE_DT_GET(DT_PARENT(n)), \ + .em4wu_pin_count = DT_PROP_LEN(n, silabs_wakeup_ints), \ + .em4wu_pins = {DT_FOREACH_PROP_ELEM(n, silabs_wakeup_ints, EM4_WAKEUP_PIN)}, \ }; \ static struct gpio_silabs_port_data gpio_silabs_port_data_##n; \ DEVICE_DT_DEFINE(n, gpio_silabs_port_init, NULL, &gpio_silabs_port_data_##n, \ diff --git a/dts/arm/silabs/xg21/xg21.dtsi b/dts/arm/silabs/xg21/xg21.dtsi index 8d5c44bd9c9bc..3f0a56120f906 100644 --- a/dts/arm/silabs/xg21/xg21.dtsi +++ b/dts/arm/silabs/xg21/xg21.dtsi @@ -351,6 +351,8 @@ reg = <0x5003c000 0x30>; gpio-controller; #gpio-cells = <2>; + silabs,wakeup-ints = <0>; + silabs,wakeup-pins = <5>; status = "disabled"; }; @@ -359,6 +361,8 @@ reg = <0x5003c030 0x30>; gpio-controller; #gpio-cells = <2>; + silabs,wakeup-ints = <3>; + silabs,wakeup-pins = <1>; status = "disabled"; }; @@ -367,6 +371,8 @@ reg = <0x5003c060 0x30>; gpio-controller; #gpio-cells = <2>; + silabs,wakeup-ints = <6>, <7>; + silabs,wakeup-pins = <0>, <5>; status = "disabled"; }; @@ -375,6 +381,8 @@ reg = <0x5003c090 0x30>; gpio-controller; #gpio-cells = <2>; + silabs,wakeup-ints = <9>; + silabs,wakeup-pins = <2>; status = "disabled"; }; }; diff --git a/dts/arm/silabs/xg22/xg22.dtsi b/dts/arm/silabs/xg22/xg22.dtsi index 1aaa8db3869c1..c01e590abedc8 100644 --- a/dts/arm/silabs/xg22/xg22.dtsi +++ b/dts/arm/silabs/xg22/xg22.dtsi @@ -129,7 +129,7 @@ * The minimum residency and exit latency is * managed by sl_power_manager on S2 devices. */ - cpu-power-states = <&pstate_em1 &pstate_em2>; + cpu-power-states = <&pstate_em1 &pstate_em2 &pstate_em4>; #address-cells = <1>; #size-cells = <1>; @@ -158,6 +158,16 @@ compatible = "zephyr,power-state"; power-state-name = "suspend-to-idle"; }; + + /* + * EM4 is a shutdown state where all clocks are off. The device + * is reset on wakeup from EM4. + */ + pstate_em4: em4 { + compatible = "zephyr,power-state"; + power-state-name = "soft-off"; + status = "disabled"; + }; }; }; @@ -403,6 +413,8 @@ reg = <0x5003C000 0x30>; gpio-controller; #gpio-cells = <2>; + silabs,wakeup-ints = <0>; + silabs,wakeup-pins = <5>; status = "disabled"; }; @@ -411,6 +423,8 @@ reg = <0x5003C030 0x30>; gpio-controller; #gpio-cells = <2>; + silabs,wakeup-ints = <3>, <4>; + silabs,wakeup-pins = <1>, <3>; status = "disabled"; }; @@ -419,6 +433,8 @@ reg = <0x5003C060 0x30>; gpio-controller; #gpio-cells = <2>; + silabs,wakeup-ints = <6>, <7>, <8>; + silabs,wakeup-pins = <0>, <5>, <7>; status = "disabled"; }; @@ -427,6 +443,8 @@ reg = <0x5003C090 0x30>; gpio-controller; #gpio-cells = <2>; + silabs,wakeup-ints = <9>; + silabs,wakeup-pins = <2>; status = "disabled"; }; }; diff --git a/dts/arm/silabs/xg23/xg23.dtsi b/dts/arm/silabs/xg23/xg23.dtsi index 4feca87c7090f..c88517697ede2 100644 --- a/dts/arm/silabs/xg23/xg23.dtsi +++ b/dts/arm/silabs/xg23/xg23.dtsi @@ -149,7 +149,7 @@ device_type = "cpu"; compatible = "arm,cortex-m33"; reg = <0>; - cpu-power-states = <&pstate_em1 &pstate_em2>; + cpu-power-states = <&pstate_em1 &pstate_em2 &pstate_em4>; /* * The minimum residency and exit latency is * managed by sl_power_manager on S2 devices. @@ -182,6 +182,16 @@ compatible = "zephyr,power-state"; power-state-name = "suspend-to-idle"; }; + + /* + * EM4 is a shutdown state where all clocks are off. The device + * is reset on wakeup from EM4. + */ + pstate_em4: em4 { + compatible = "zephyr,power-state"; + power-state-name = "soft-off"; + status = "disabled"; + }; }; }; @@ -461,6 +471,8 @@ reg = <0x5003c030 0x30>; gpio-controller; #gpio-cells = <2>; + silabs,wakeup-ints = <0>; + silabs,wakeup-pins = <5>; status = "disabled"; }; @@ -469,6 +481,8 @@ reg = <0x5003c060 0x30>; gpio-controller; #gpio-cells = <2>; + silabs,wakeup-ints = <3>, <4>; + silabs,wakeup-pins = <1>, <3>; status = "disabled"; }; @@ -477,6 +491,8 @@ reg = <0x5003c090 0x30>; gpio-controller; #gpio-cells = <2>; + silabs,wakeup-ints = <6>, <7>, <8>; + silabs,wakeup-pins = <0>, <5>, <7>; status = "disabled"; }; @@ -485,6 +501,8 @@ reg = <0x5003c0C0 0x30>; gpio-controller; #gpio-cells = <2>; + silabs,wakeup-ints = <9>, <10>; + silabs,wakeup-pins = <2>, <5>; status = "disabled"; }; }; diff --git a/dts/arm/silabs/xg24/xg24.dtsi b/dts/arm/silabs/xg24/xg24.dtsi index 1cdab33bed43b..ffe4516b3fcd9 100644 --- a/dts/arm/silabs/xg24/xg24.dtsi +++ b/dts/arm/silabs/xg24/xg24.dtsi @@ -139,7 +139,7 @@ device_type = "cpu"; compatible = "arm,cortex-m33"; reg = <0>; - cpu-power-states = <&pstate_em1 &pstate_em2>; + cpu-power-states = <&pstate_em1 &pstate_em2 &pstate_em4>; /* * The minimum residency and exit latency is * managed by sl_power_manager on S2 devices. @@ -172,6 +172,16 @@ compatible = "zephyr,power-state"; power-state-name = "suspend-to-idle"; }; + + /* + * EM4 is a shutdown state where all clocks are off. The device + * is reset on wakeup from EM4. + */ + pstate_em4: em4 { + compatible = "zephyr,power-state"; + power-state-name = "soft-off"; + status = "disabled"; + }; }; }; @@ -431,6 +441,8 @@ reg = <0x5003c030 0x30>; gpio-controller; #gpio-cells = <2>; + silabs,wakeup-ints = <0>; + silabs,wakeup-pins = <5>; status = "disabled"; }; @@ -439,6 +451,8 @@ reg = <0x5003c060 0x30>; gpio-controller; #gpio-cells = <2>; + silabs,wakeup-ints = <3>, <4>; + silabs,wakeup-pins = <1>, <3>; status = "disabled"; }; @@ -447,6 +461,8 @@ reg = <0x5003c090 0x30>; gpio-controller; #gpio-cells = <2>; + silabs,wakeup-ints = <6>, <7>, <8>; + silabs,wakeup-pins = <0>, <5>, <7>; status = "disabled"; }; @@ -455,6 +471,8 @@ reg = <0x5003c0C0 0x30>; gpio-controller; #gpio-cells = <2>; + silabs,wakeup-ints = <9>, <10>; + silabs,wakeup-pins = <2>, <5>; status = "disabled"; }; }; diff --git a/dts/arm/silabs/xg27/xg27.dtsi b/dts/arm/silabs/xg27/xg27.dtsi index 023551e2db70d..c957f6a5eba96 100644 --- a/dts/arm/silabs/xg27/xg27.dtsi +++ b/dts/arm/silabs/xg27/xg27.dtsi @@ -138,7 +138,7 @@ * The minimum residency and exit latency is * managed by sl_power_manager on S2 devices. */ - cpu-power-states = <&pstate_em1 &pstate_em2>; + cpu-power-states = <&pstate_em1 &pstate_em2 &pstate_em4>; #address-cells = <1>; #size-cells = <1>; @@ -167,6 +167,16 @@ compatible = "zephyr,power-state"; power-state-name = "suspend-to-idle"; }; + + /* + * EM4 is a shutdown state where all clocks are off. The device + * is reset on wakeup from EM4. + */ + pstate_em4: em4 { + compatible = "zephyr,power-state"; + power-state-name = "soft-off"; + status = "disabled"; + }; }; }; @@ -412,6 +422,8 @@ reg = <0x5003C030 0x30>; gpio-controller; #gpio-cells = <2>; + silabs,wakeup-ints = <0>; + silabs,wakeup-pins = <5>; status = "disabled"; }; @@ -420,6 +432,8 @@ reg = <0x5003C060 0x30>; gpio-controller; #gpio-cells = <2>; + silabs,wakeup-ints = <3>, <4>; + silabs,wakeup-pins = <1>, <3>; status = "disabled"; }; @@ -428,6 +442,8 @@ reg = <0x5003C090 0x30>; gpio-controller; #gpio-cells = <2>; + silabs,wakeup-ints = <6>, <7>, <8>; + silabs,wakeup-pins = <0>, <5>, <7>; status = "disabled"; }; @@ -436,6 +452,8 @@ reg = <0x5003C0C0 0x30>; gpio-controller; #gpio-cells = <2>; + silabs,wakeup-ints = <9>; + silabs,wakeup-pins = <2>; status = "disabled"; }; }; diff --git a/dts/arm/silabs/xg28/xg28.dtsi b/dts/arm/silabs/xg28/xg28.dtsi index ed5bfa457ccbd..9e0e1033c732e 100644 --- a/dts/arm/silabs/xg28/xg28.dtsi +++ b/dts/arm/silabs/xg28/xg28.dtsi @@ -170,7 +170,7 @@ device_type = "cpu"; compatible = "arm,cortex-m33"; reg = <0>; - cpu-power-states = <&pstate_em1 &pstate_em2>; + cpu-power-states = <&pstate_em1 &pstate_em2 &pstate_em4>; /* * The minimum residency and exit latency is * managed by sl_power_manager on S2 devices. @@ -196,6 +196,16 @@ compatible = "zephyr,power-state"; power-state-name = "suspend-to-idle"; }; + + /* + * EM4 is a shutdown state where all clocks are off. The device + * is reset on wakeup from EM4. + */ + pstate_em4: em4 { + compatible = "zephyr,power-state"; + power-state-name = "soft-off"; + status = "disabled"; + }; }; }; @@ -475,6 +485,8 @@ reg = <0x5003c030 0x30>; gpio-controller; #gpio-cells = <2>; + silabs,wakeup-ints = <0>; + silabs,wakeup-pins = <5>; status = "disabled"; }; @@ -483,6 +495,8 @@ reg = <0x5003c060 0x30>; gpio-controller; #gpio-cells = <2>; + silabs,wakeup-ints = <3>, <4>; + silabs,wakeup-pins = <1>, <3>; status = "disabled"; }; @@ -491,6 +505,8 @@ reg = <0x5003c090 0x30>; gpio-controller; #gpio-cells = <2>; + silabs,wakeup-ints = <6>, <7>, <8>; + silabs,wakeup-pins = <0>, <5>, <7>; status = "disabled"; }; @@ -499,6 +515,8 @@ reg = <0x5003c0c0 0x30>; gpio-controller; #gpio-cells = <2>; + silabs,wakeup-ints = <9>, <10>; + silabs,wakeup-pins = <2>, <5>; status = "disabled"; }; }; diff --git a/dts/arm/silabs/xg29/xg29.dtsi b/dts/arm/silabs/xg29/xg29.dtsi index db155d3dac475..161b430cc50b9 100644 --- a/dts/arm/silabs/xg29/xg29.dtsi +++ b/dts/arm/silabs/xg29/xg29.dtsi @@ -146,7 +146,7 @@ device_type = "cpu"; compatible = "arm,cortex-m33"; reg = <0>; - cpu-power-states = <&pstate_em1 &pstate_em2>; + cpu-power-states = <&pstate_em1 &pstate_em2 &pstate_em4>; /* * The minimum residency and exit latency is * managed by sl_power_manager on S2 devices. @@ -179,6 +179,16 @@ compatible = "zephyr,power-state"; power-state-name = "suspend-to-idle"; }; + + /* + * EM4 is a shutdown state where all clocks are off. The device + * is reset on wakeup from EM4. + */ + pstate_em4: em4 { + compatible = "zephyr,power-state"; + power-state-name = "soft-off"; + status = "disabled"; + }; }; }; @@ -293,6 +303,8 @@ reg = <0x5003C030 0x30>; gpio-controller; #gpio-cells = <2>; + silabs,wakeup-ints = <0>; + silabs,wakeup-pins = <5>; status = "disabled"; }; @@ -301,6 +313,8 @@ reg = <0x5003C060 0x30>; gpio-controller; #gpio-cells = <2>; + silabs,wakeup-ints = <3>, <4>; + silabs,wakeup-pins = <1>, <3>; status = "disabled"; }; @@ -309,6 +323,8 @@ reg = <0x5003C090 0x30>; gpio-controller; #gpio-cells = <2>; + silabs,wakeup-ints = <6>, <7>, <8>; + silabs,wakeup-pins = <0>, <5>, <7>; status = "disabled"; }; @@ -317,6 +333,8 @@ reg = <0x5003C0C0 0x30>; gpio-controller; #gpio-cells = <2>; + silabs,wakeup-ints = <9>; + silabs,wakeup-pins = <2>; status = "disabled"; }; }; diff --git a/dts/bindings/gpio/silabs,gpio-port.yaml b/dts/bindings/gpio/silabs,gpio-port.yaml index 25aa379171bab..9462ef85b32ef 100644 --- a/dts/bindings/gpio/silabs,gpio-port.yaml +++ b/dts/bindings/gpio/silabs,gpio-port.yaml @@ -14,6 +14,18 @@ properties: "#gpio-cells": const: 2 + silabs,wakeup-ints: + type: array + description: | + List of EM4 wakeup interrupt numbers for this port. The corresponding entry + in `silabs,wakeup-pins` indicates the pin associated with the interrupt number. + + silabs,wakeup-pins: + type: array + description: | + List of EM4 wakeup capable pins for this port. The corresponding entry in + `silabs,wakeup-ints` indicates the interrupt number associated with the pin. + gpio-cells: - pin - flags