-
Notifications
You must be signed in to change notification settings - Fork 7.8k
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
Silabs gpio driver #92824
Conversation
74b3e13
to
eca5b4b
Compare
6c73c92
to
e46e7bc
Compare
There was a problem hiding this 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.
e46e7bc
to
165546d
Compare
165546d
to
aedd2cc
Compare
There was a problem hiding this 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)
There was a problem hiding this 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.
drivers/gpio/gpio_silabs.c
Outdated
} 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)); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
addressed
drivers/gpio/gpio_silabs.c
Outdated
/* 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]; |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
addressed
3bdb92d
to
0b1a809
Compare
0b1a809
to
a6f0d6e
Compare
drivers/gpio/gpio_silabs.c
Outdated
} | ||
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; |
There was a problem hiding this comment.
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
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
addressed
a6f0d6e
to
54200a9
Compare
54200a9
to
df878a5
Compare
df878a5
to
7fdbce6
Compare
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]>
7fdbce6
to
fdfe2a7
Compare
|
.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, \ |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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)}, \ |
There was a problem hiding this comment.
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),
There was a problem hiding this comment.
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)); \ |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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, \ |
There was a problem hiding this comment.
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...
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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, |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No declarations after statements.
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indentation
There was a problem hiding this comment.
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>; | ||
}; |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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}; |
There was a problem hiding this comment.
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
};
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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)
?
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indentation
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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)) { |
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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, |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
addressed
@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? |
@jhedberg, Yes, I'm on it. Will be working on the follow-up PR |
@jerome-pouiller @jhedberg @asmellby @Martinhoff-maker I have addressed the comments in PR: #94251 Could you please review the changes? |
The commit enables the gpio driver for EFR series 2 devices.