Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 90 additions & 4 deletions drivers/gpio/gpio_silabs.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand All @@ -39,13 +42,22 @@ 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;
/* index of the GPIO port */
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 {
Expand Down Expand Up @@ -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)
{
Expand All @@ -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) {
Expand Down Expand Up @@ -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;
Expand All @@ -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));
Expand Down Expand Up @@ -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, \
Expand Down
8 changes: 8 additions & 0 deletions dts/arm/silabs/xg21/xg21.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,8 @@
reg = <0x5003c000 0x30>;
gpio-controller;
#gpio-cells = <2>;
silabs,wakeup-ints = <0>;
silabs,wakeup-pins = <5>;
status = "disabled";
};

Expand All @@ -359,6 +361,8 @@
reg = <0x5003c030 0x30>;
gpio-controller;
#gpio-cells = <2>;
silabs,wakeup-ints = <3>;
silabs,wakeup-pins = <1>;
status = "disabled";
};

Expand All @@ -367,6 +371,8 @@
reg = <0x5003c060 0x30>;
gpio-controller;
#gpio-cells = <2>;
silabs,wakeup-ints = <6>, <7>;
silabs,wakeup-pins = <0>, <5>;
status = "disabled";
};

Expand All @@ -375,6 +381,8 @@
reg = <0x5003c090 0x30>;
gpio-controller;
#gpio-cells = <2>;
silabs,wakeup-ints = <9>;
silabs,wakeup-pins = <2>;
status = "disabled";
};
};
Expand Down
20 changes: 19 additions & 1 deletion dts/arm/silabs/xg22/xg22.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -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>;

Expand Down Expand Up @@ -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";
};
};
};

Expand Down Expand Up @@ -403,6 +413,8 @@
reg = <0x5003C000 0x30>;
gpio-controller;
#gpio-cells = <2>;
silabs,wakeup-ints = <0>;
silabs,wakeup-pins = <5>;
status = "disabled";
};

Expand All @@ -411,6 +423,8 @@
reg = <0x5003C030 0x30>;
gpio-controller;
#gpio-cells = <2>;
silabs,wakeup-ints = <3>, <4>;
silabs,wakeup-pins = <1>, <3>;
status = "disabled";
};

Expand All @@ -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";
};

Expand All @@ -427,6 +443,8 @@
reg = <0x5003C090 0x30>;
gpio-controller;
#gpio-cells = <2>;
silabs,wakeup-ints = <9>;
silabs,wakeup-pins = <2>;
status = "disabled";
};
};
Expand Down
20 changes: 19 additions & 1 deletion dts/arm/silabs/xg23/xg23.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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";
};
};
};

Expand Down Expand Up @@ -461,6 +471,8 @@
reg = <0x5003c030 0x30>;
gpio-controller;
#gpio-cells = <2>;
silabs,wakeup-ints = <0>;
silabs,wakeup-pins = <5>;
status = "disabled";
};

Expand All @@ -469,6 +481,8 @@
reg = <0x5003c060 0x30>;
gpio-controller;
#gpio-cells = <2>;
silabs,wakeup-ints = <3>, <4>;
silabs,wakeup-pins = <1>, <3>;
status = "disabled";
};

Expand All @@ -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";
};

Expand All @@ -485,6 +501,8 @@
reg = <0x5003c0C0 0x30>;
gpio-controller;
#gpio-cells = <2>;
silabs,wakeup-ints = <9>, <10>;
silabs,wakeup-pins = <2>, <5>;
status = "disabled";
};
};
Expand Down
20 changes: 19 additions & 1 deletion dts/arm/silabs/xg24/xg24.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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";
};
};
};

Expand Down Expand Up @@ -431,6 +441,8 @@
reg = <0x5003c030 0x30>;
gpio-controller;
#gpio-cells = <2>;
silabs,wakeup-ints = <0>;
silabs,wakeup-pins = <5>;
status = "disabled";
};

Expand All @@ -439,6 +451,8 @@
reg = <0x5003c060 0x30>;
gpio-controller;
#gpio-cells = <2>;
silabs,wakeup-ints = <3>, <4>;
silabs,wakeup-pins = <1>, <3>;
status = "disabled";
};

Expand All @@ -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";
};

Expand All @@ -455,6 +471,8 @@
reg = <0x5003c0C0 0x30>;
gpio-controller;
#gpio-cells = <2>;
silabs,wakeup-ints = <9>, <10>;
silabs,wakeup-pins = <2>, <5>;
status = "disabled";
};
};
Expand Down
Loading