Skip to content

Silabs gpio driver #92824

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Aug 7, 2025
Merged

Conversation

fimohame
Copy link
Contributor

@fimohame fimohame commented Jul 8, 2025

The commit enables the gpio driver for EFR series 2 devices.

@fimohame fimohame closed this Jul 8, 2025
@fimohame fimohame reopened this Jul 8, 2025
@fimohame fimohame closed this Jul 8, 2025
@fimohame fimohame reopened this Jul 8, 2025
@fimohame fimohame closed this Jul 8, 2025
@fimohame fimohame reopened this Jul 8, 2025
@fimohame fimohame force-pushed the silabs_gpio_driver branch from 74b3e13 to eca5b4b Compare July 8, 2025 10:18
smalae
smalae previously requested changes Jul 8, 2025
@fimohame fimohame force-pushed the silabs_gpio_driver branch 3 times, most recently from 6c73c92 to e46e7bc Compare July 9, 2025 05:59
@fimohame fimohame requested a review from smalae July 9, 2025 06:04
@fimohame fimohame marked this pull request as ready for review July 10, 2025 08:05
Copy link
Contributor

@Martinhoff-maker Martinhoff-maker left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't gone through all the gpio_silabs.c files but in general:

  • We need to only use sl_hal_gpio functions. sl_gpio.h is already a driver in the HAL and it initializes things like clocks which we want to handle in the Zephyr driver.

  • We also need to have a new Kconfig symbol in zephyr/modules/hal_silabs/simplicity_sdk like "SILABS_SISDK_GPIO" and make changes to CMakeLists.txt accordingly. You can take inspiration from #92010

  • We should update the dtsi files for all Series 2 boards with this new GPIO driver and create an entry in the migration guide 4.3.

  • Don't forget to add a binding file for the driver.

Copy link
Contributor

@Martinhoff-maker Martinhoff-maker left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As general comment, as done in gpio_silabs_siwx91x.c, it could be good to have clear device name in general : is it the parent device ( the gpio controller) or the port device rather ? than just having "dev" names everywhere.

Adding PM would be perfect also (might be part of another PR)

@fimohame fimohame closed this Aug 6, 2025
@fimohame fimohame reopened this Aug 6, 2025
@fimohame fimohame requested a review from asmellby August 6, 2025 11:17
Copy link
Contributor

@asmellby asmellby left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks mostly good to me, a few minor comments.

} else {
ARRAY_FOR_EACH(data->irq_pin_map, i) {
if ((data->int_enabled_mask & BIT(i)) && (data->irq_pin_map[i] == pin)) {
sl_hal_gpio_disable_interrupts(BIT(i));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the purpose of disabling the interrupt in the "enable" branch?
If the purpose is to trick the configure function into reallocating the same interrupt number if an interrupt was already configured to this pin, I would instead just set int_no = i; here, since the configure function only tries to allocate if given the magic "unavailable" value.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addressed

/* bitmask of pins with interrupt enabled */
uint32_t int_enabled_mask;
/* maps interrupt line index to GPIO pin number, 0xFF if unused */
uint8_t irq_pin_map[16];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this be added to struct gpio_silabs_common_data instead?

I guess a const struct device * would need to be added to the port struct to point back to the common node, but that would still be more efficient (4*4 + 16 = 32 bytes instead of 4*16 = 64 bytes).

It would correspond to the hardware better, since two ports can't disagree on the irq-pin mapping, and it would also set us up better for adding em4wu interrupt support in the future.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addressed

@fimohame fimohame force-pushed the silabs_gpio_driver branch from 3bdb92d to 0b1a809 Compare August 6, 2025 13:20
@fimohame fimohame requested a review from asmellby August 6, 2025 13:22
@fimohame fimohame force-pushed the silabs_gpio_driver branch from 0b1a809 to a6f0d6e Compare August 6, 2025 13:32
}
const struct device *port_dev = data->ports[i];
struct gpio_silabs_port_data *port_data = port_dev->data;
struct gpio_silabs_common_data *common = port_data->common_dev->data;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be hoisted outside the for loop -- dev is already the gpio peripheral (same as port_data->common_dev).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addressed

@fimohame fimohame force-pushed the silabs_gpio_driver branch from a6f0d6e to 54200a9 Compare August 6, 2025 13:49
@fimohame fimohame requested a review from asmellby August 6, 2025 13:50
@fimohame fimohame force-pushed the silabs_gpio_driver branch from 54200a9 to df878a5 Compare August 6, 2025 18:11
@fimohame fimohame requested a review from asmellby August 6, 2025 18:13
@fimohame fimohame force-pushed the silabs_gpio_driver branch from df878a5 to 7fdbce6 Compare August 7, 2025 11:17
Added the gpio driver for EFR series 2 devices.

The SILABS_SISDK_GPIO symbol is added to enable
support for the new GPIO driver.
The SOC_GECKO_GPIO symbol is retained for now to
maintain compatibility with existing drivers and
will be removed in a subsequent commit.

Signed-off-by: S Mohamed Fiaz <[email protected]>
Updated Kconfig for EFR series 2 devices.

Signed-off-by: S Mohamed Fiaz <[email protected]>
Added the xg29_rb4412a.overlay for GPIO API 1-pin
This overlay sets the configuration
needed to validate the GPIO driver on the
xg29_rb4412a board during test execution.

Signed-off-by: S Mohamed Fiaz <[email protected]>
@fimohame fimohame force-pushed the silabs_gpio_driver branch from 7fdbce6 to fdfe2a7 Compare August 7, 2025 11:23
Copy link

sonarqubecloud bot commented Aug 7, 2025

@cfriedt cfriedt dismissed smalae’s stale review August 7, 2025 12:58

Changes seem to be addressed

@cfriedt cfriedt merged commit dd83b64 into zephyrproject-rtos:main Aug 7, 2025
30 checks passed
.clock = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(idx)), \
.clock_cfg = SILABS_DT_INST_CLOCK_CFG(idx), \
}; \
DEVICE_DT_INST_DEFINE(idx, gpio_silabs_common_init, NULL, &gpio_silabs_common_data_inst, \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest to be consistent. If you consider there can be several GPIO devices, we probably need one gpio_silabs_common_data per instance.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addressed

return 0; \
} \
static const struct gpio_silabs_port_config gpio_silabs_port_##n##_config = { \
.common = {.port_pin_mask = (gpio_port_pins_t)(-1)}, \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be enough: .common.port_pin_mask = (gpio_port_pins_t)(-1),

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addressed

static int gpio_silabs_port_##n##_init(const struct device *dev) \
{ \
struct gpio_silabs_port_data *data = dev->data; \
data->common_dev = DEVICE_DT_GET(DT_PARENT(n)); \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DEVICE_DT_GET(DT_PARENT(n)) can be located in gpio_silabs_port_config. Then, you won't have to define one function per port.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addressed

}; \
static struct gpio_silabs_port_data gpio_silabs_port_##n##_data; \
DEVICE_DT_DEFINE(n, gpio_silabs_port_##n##_init, NULL, &gpio_silabs_port_##n##_data, \
&gpio_silabs_port_##n##_config, PRE_KERNEL_1, CONFIG_GPIO_INIT_PRIORITY, \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We rather tend to place index value at the end of the names: gpio_silabs_port_init##n, gpio_silabs_port_data##n, etc...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addressed


config GPIO_SILABS_COMMON_INIT_PRIORITY
int "Common initialization priority"
depends on GPIO_SILABS
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You already said if GPIO_SILABS above.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addressed

};

static DEVICE_API(gpio, gpio_common_driver_api) = {
.manage_callback = gpio_silabs_port_manage_callback,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gpio_silabs_port_manage_callback() expect a gpio_silabs_port as parameter. It won't be the case here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addressed

}
const struct device *port_dev = data->ports[i];
struct gpio_silabs_port_data *port_data = port_dev->data;
uint32_t enabled_int = int_status & port_data->int_enabled_mask;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No declarations after statements.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addressed


#if GPIO_SILABS_COMMON_INIT_PRIORITY >= CONFIG_GPIO_INIT_PRIORITY
#error GPIO_SILABS_COMMON_INIT_PRIORITY must be less than CONFIG_GPIO_INIT_PRIORITY
#endif
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the GPIO_Ports are children of GPIO_Common, I believe Zephyr would raise an error, even without this check.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addressed

};

static inline void gpio_silabs_add_port(struct gpio_silabs_common_data *data,
const struct device *dev)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indentation

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addressed

compatible = "gpio-leds";
led0: led_0 {
gpios = <&gpioc 7 GPIO_ACTIVE_HIGH>;
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the untouched DTS does not work?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is clearing the port bit of the uart, even if the uart is port A, it will kill VCOM and then the uart output over USB

static int gpio_silabs_pin_configure(const struct device *dev, gpio_pin_t pin, gpio_flags_t flags)
{
const struct gpio_silabs_port_config *config = dev->config;
sl_gpio_t gpio = {.port = config->gpio_index, .pin = pin};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sl_gpio_t gpio = {
    .port = config->gpio_index,
    .pin = pin
};

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addressed

out = 0U;
} else {
pin_out = sl_hal_gpio_get_pin_output(&gpio);
out = pin_out;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably pin_out is here for bad reasons. Why not out = sl_hal_gpio_get_pin_output(&gpio)? or out = !!sl_hal_gpio_get_pin_output(&gpio)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addressed

const struct gpio_silabs_port_config *config = dev->config;
sl_gpio_t gpio = {.port = config->gpio_index, .pin = pin};
sl_gpio_mode_t mode;
uint16_t out = 0U;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why a uint16_t? sl_hal_gpio_set_pin_mode() takes a bool as input.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addressed

mode = SL_GPIO_MODE_INPUT_PULL;
out = 1U;
} else if (flags & GPIO_PULL_DOWN) {
mode = SL_GPIO_MODE_INPUT_PULL;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest to set value for out since it matters:

    out = false; // Pull-down

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addressed


#ifdef CONFIG_GPIO_GET_CONFIG
static int gpio_silabs_pin_get_config(const struct device *dev, gpio_pin_t pin,
gpio_flags_t *out_flags)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indentation

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addressed

common->irq_pin_map[int_no] = pin;

sl_hal_gpio_enable_interrupts(BIT(int_no));
WRITE_BIT(data->int_enabled_mask, int_no, true);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because we prefer to keep only one source of truth, I suggest to get rid of int_enabled_mask and used sl_hal_gpio_get_enabled_interrupts() instead.


if (mode == GPIO_INT_MODE_DISABLED) {
ARRAY_FOR_EACH(common->irq_pin_map, i) {
if ((data->int_enabled_mask & BIT(i)) && (common->irq_pin_map[i] == pin)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe irq_pin_map could be replaced by calls to sl_hal_gpio_get_external_interrupt_number() (this is prefered solution because we prefer a single source of trust).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When an interrupt occurs on any line, you need to examine the EXTIPINSEL* bitfield to determine which pin is associated with that interrupt line, and then construct the appropriate pin bitmask to pass to the callback dispatcher.

Currently, sl_hal_gpio does not provide a helper function for this task.

uint32_t int_status = GPIO->IF;

ARRAY_FOR_EACH(data->ports, i) {
if (!int_status) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This early exit make this function more complex to understand and it probably does not improve the performance.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addressed

.port_clear_bits_raw = gpio_silabs_port_clear_bits_raw,
.port_toggle_bits = gpio_silabs_port_toggle_bits,
.pin_interrupt_configure = gpio_silabs_pin_interrupt_configure,
.manage_callback = gpio_silabs_port_manage_callback,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: you can align the = signs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addressed

@jhedberg
Copy link
Member

jhedberg commented Aug 7, 2025

@fimohame looks like there were plenty of valid points raised by @jerome-pouiller which came a bit too late. Will you work on a follow-up PR to address those?

@fimohame
Copy link
Contributor Author

fimohame commented Aug 7, 2025

@jhedberg, Yes, I'm on it. Will be working on the follow-up PR

@fimohame
Copy link
Contributor Author

fimohame commented Aug 8, 2025

@jerome-pouiller @jhedberg @asmellby @Martinhoff-maker

I have addressed the comments in PR: #94251

Could you please review the changes?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants