-
Notifications
You must be signed in to change notification settings - Fork 6.7k
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
drivers: watchdog: Watchdog API redesign [RFC] #1260
Conversation
include/watchdog.h
Outdated
* timeouts - pointer to an array storing timeouts values in microseconds | ||
* for all enabled channels. If watchdog supports multiple channels | ||
* but only one counter reload value then the first value from the | ||
* array will be taken. |
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 still wonder if such high resolution for watchdog is required. Maybe it would be sufficient to use u16_t
and milliseconds.
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.
Kernel timer API is using milliseconds so indeed we would be more consistent sticking with this unit. Also watchdogs often work with slow / imprecise clock, microsecond resolution is not required.
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.
My opinion is just the opposite, let use [1 us] resolution. Usually WDT is clocker from several dozen or several hundred KHZ - so resolution is always muh more than [1ms]. Regards precisin: On nRF5x it is drive form 32768 Hz and it can be the crystal oscillator, so it has precise timing. I believe some other SoC has similar capabilities - why to lock these capabilities. I see sense in keeping 1us resolution.
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.
Watchdog clocks are designed to be reliable not precise. Typically these are a very imprecise RC clock sources, often as a fallback when a crystal fails. But even if we had a 32768 crystal oscillator only microsecond precision is not required. We won't use watchdog to measure time. Resolution of watchdog is strictly connected with the system tick. It will never be smaller than a tick length. Watchdog timeout value lays typically between ~10ms to some seconds. It should be as small as possible but has to include all the uncertainties: inaccuracy of the source clock, scheduler. There is always safety margin added to the timeout value and it will never lay in microseconds range. If Zephyr allowed to set system tick to value smaller than 1 ms then the watchdog should do it too but it the same way kernel API does. Let's keep APIs consistent.
include/watchdog.h
Outdated
u32_t timeout; | ||
enum wdt_mode mode; | ||
void (*interrupt_fn)(struct device *dev); | ||
u32_t *timeouts; |
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.
For me 1 us is the good resolution. Of course the realized timeout value for timeout requested is depend on the hardware. Is there any way to get the real timeout? Is it expected to be retrieved by get_config call?
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.
timeouts with an 's'? [edit] Missed the comment detailing this attribute...
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.
@nvlsianpu No, there is no way to get the real timeout. Driver implementation should approximate to the closest possible value or maybe to the closest higher value? get_config
will be probably removed as no one can find any use case for it right now.
Maybe *timeouts table should be in/out parameter, after leaving the function it would store the real set values?
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.
@m-kru Works for me as well.
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.
Thanks @m-kru for working on the new watchdog API. It certainly needs a refreshment.
In addition to my comments below I have one extra remark. As captured by this API a watchdog timer needs to be reloaded before a fixed amount of time has elapsed. However another common feature of a watchdog module is to provide a minimum time before which the watchdog timer is not allowed to be reloaded. I.e. the watchdog timer can only be reloaded in a time window with predefined minimum and maximum values. This is not supported by this API.
Could you please give an example of a SoC with multi-channel watchdog?
drivers/watchdog/Kconfig
Outdated
@@ -1,23 +1,38 @@ | |||
# Kconfig - Watchdog configuration options | |||
# | |||
# | |||
# Copyright (c) 2015 Intel Corporation |
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.
The file was modified, we probably should not remove the old copyright, feel free to add new.
drivers/watchdog/Kconfig
Outdated
menuconfig WATCHDOG | ||
bool | ||
prompt "Watchdog Support" | ||
default 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.
What was the reason to remove this line?
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.
@mnkp Lines with default n
are not required. n
is the default where there is no default
line. There is not need to make file size greater than necessary.
include/watchdog.h
Outdated
* @brief Watchdog operational mode. | ||
*/ | ||
#define WDT_OP_MODE_RESET (0) | ||
#define WDT_OP_MODE_INTERRUPT_RESET BIT(0) |
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.
A better name would be WDT_MODE_CALLBACK. The callback typically will, but actually does not have to trigger a reset.
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.
@mnkp Thanks, I will improve that.
include/watchdog.h
Outdated
*/ | ||
#define WDT_NO_PAUSE_HALTED_BY_DBG (0) | ||
#define WDT_PAUSE_HALTED_BY_DBG BIT(2) | ||
#define WDT_PAUSE_HALTED_BY_DBG_MASK (1 << 2) |
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.
In Zephyr's API we typically provide a bit field option to change API's default behavior. I.e. The only required define here is WDT_PAUSE_HALTED_BY_DBG. The other two WDT_NO_PAUSE_HALTED_BY_DBG and WDT_PAUSE_HALTED_BY_DBG_MASK can be removed. This will make it consistent with the rest of the API. Doxygen comment should be updated accordingly.
include/watchdog.h
Outdated
* timeouts - pointer to an array storing timeouts values in microseconds | ||
* for all enabled channels. If watchdog supports multiple channels | ||
* but only one counter reload value then the first value from the | ||
* array will be taken. |
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.
Kernel timer API is using milliseconds so indeed we would be more consistent sticking with this unit. Also watchdogs often work with slow / imprecise clock, microsecond resolution is not required.
include/watchdog.h
Outdated
enum wdt_mode mode; | ||
void (*interrupt_fn)(struct device *dev); | ||
u32_t *timeouts; | ||
void (*interrupt_fn)(struct device *dev, u8_t channel); |
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 could provide a typedef for this function as in
/** Watchdog callback */
typedef void (*wdt_callback_t)(struct device *dev, u8_t channel);
this is sometimes required / useful when writing a driver.
include/watchdog.h
Outdated
}; | ||
|
||
typedef void (*wdt_api_enable)(struct device *dev); | ||
typedef void (*wdt_api_disable)(struct device *dev); | ||
typedef int (*wdt_api_enable)(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.
SPI API does not but most of the others Zephyr's APIs add '_t' at the end of the function name in typedef. This seems reasonable. Another clean and used practice is to skip typedef completely and place definitions directly in the struct as in:
struct wdt_driver_api {
int (*enable)(struct device *dev);
...
};
In this case typedef is actually not required/useful.
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.
Actually there is no strict rules with adding a '_t' or not, or declaring the functions signatures into the struct directly.
Up to you.
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 for the typedef: driver APIs are indeed inconsistent but Zephyr Kernel API is not. It is always using '_t' at the end of the function name in typedef.
include/watchdog.h
Outdated
static inline void wdt_enable(struct device *dev) | ||
/* | ||
* @brief Enable watchdog 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.
Description of function parameters is missing, add them with @ param. Here and in all the remaining functions.
include/watchdog.h
Outdated
* WDT configuration struct. | ||
* @brief Watchdog configuration struct. | ||
* | ||
* timeouts - pointer to an array storing timeouts values in microseconds |
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.
From the description of 'timeouts' it's not clear what is the length of the array. I think we should mention explicitly that it's num_of_channels field.
include/watchdog.h
Outdated
/** | ||
* @brief Get configuration of watchdog. | ||
*/ | ||
static inline void wdt_get_config(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.
I wonder if we need this function at all. It forces the device driver to store internally the whole struct wdt_config. Also it is not clear what should be done with config.timeouts array. In struct wdt_config we store only the pointer. It does not have to point to a static variable.
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.
Indeed, not sure this one is useful, it comes from legacy API after all. What would be the use case for this function?
include/watchdog.h
Outdated
*/ | ||
#define WDT_NO_PAUSE_HALTED_BY_DBG (0) | ||
#define WDT_PAUSE_HALTED_BY_DBG BIT(2) | ||
#define WDT_PAUSE_HALTED_BY_DBG_MASK (1 << 2) |
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.
1 << 2 is what BIT(2) does. I guess you want to change that and use BIT_MASK() macro? See misc/util.h
include/watchdog.h
Outdated
void (*interrupt_fn)(struct device *dev); | ||
u32_t *timeouts; | ||
void (*interrupt_fn)(struct device *dev, u8_t channel); | ||
u8_t operation; |
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 agree, operation makes sense in SPI due to SPI wording (that field in SPI is affecting how the controller works then).
include/watchdog.h
Outdated
* @retval -EFAULT if watchdog instance has not been configured yet. | ||
* @retval -EBUSY if watchdog instance has been already enabled. | ||
*/ | ||
static inline int wdt_enable(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.
isn't wdt_enable/wdt_disable something related to PM? (actual implementation in wdt_qmsi seems to ack that - qmsi, against which the legacy wdt api was designed from.)
Looks like you can remove these 2 functions, up to PM to handle that.
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.
Very good point. I believe we need to remove these functions. However wdt_configure function should provide means for disabling the watchdog. While Nordic watchdogs need to be explicitly started in case of other vendors watchdog will typically be active after reset.
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.
Not all wdt controller seem to be possible to disable once started, at least not through the controller itself. And disabling through wdt_configure would look weird.
Maybe we need these 2 after all, and the PM function would redirect to them if enabled.
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.
Considering the fact that we don't need to pass any parameters when we want to disable the watchdog it may be indeed a cleaner / more user friendly design if we have a dedicated wdt_disable function rather than passing some flag to wdt_configure. But we likely won't need wdt_enable.
include/watchdog.h
Outdated
/** | ||
* @brief Get configuration of watchdog. | ||
*/ | ||
static inline void wdt_get_config(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.
Indeed, not sure this one is useful, it comes from legacy API after all. What would be the use case for this function?
include/watchdog.h
Outdated
u32_t timeout; | ||
enum wdt_mode mode; | ||
void (*interrupt_fn)(struct device *dev); | ||
u32_t *timeouts; |
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.
timeouts with an 's'? [edit] Missed the comment detailing this attribute...
include/watchdog.h
Outdated
u32_t *timeouts; | ||
void (*interrupt_fn)(struct device *dev, u8_t channel); | ||
u8_t operation; | ||
u8_t num_of_channels; |
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.
The pb with channels here means the user has to know how many channel the targeted hardware can handle: 1, 4, ... ? From that: it will be an issue is the code using wdt API is meant to be portable.
Imo, it should be transparent, and fully handled in the driver:
- if the hw supports only 1 config it's easy
- if the hw supports n channels (usually not a huge number of these, right? something like 4, 8...), the driver would maintain an array of pointers of wdt_config of channels element. And use an empty "slot" in this array to follow a call of wdt_set_config().
-> in both case, is a subsequent wdt_set_config() cannot be applied do to lack of channels, it would just return -EBUSY.
the config that would trigger the wdt interruption, could be passe in the callback (the relevant wdt_config pointer, that is), same in wdt_reload.
This would hide fully this channel issue.
Now, you may want a function to uninstall a configuration already in use. I guess it depends on the controller (some might not be reconfigured? then it would return -ENOTSUPP)
include/watchdog.h
Outdated
* pointer is NULL. | ||
*/ | ||
static inline int wdt_set_config(struct device *dev, | ||
struct wdt_config *config) |
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.
btw, why not renaming that one to wdt_configure()? That's a taste issue though.
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.
+1
include/watchdog.h
Outdated
}; | ||
|
||
typedef void (*wdt_api_enable)(struct device *dev); | ||
typedef void (*wdt_api_disable)(struct device *dev); | ||
typedef int (*wdt_api_enable)(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.
Actually there is no strict rules with adding a '_t' or not, or declaring the functions signatures into the struct directly.
Up to you.
Thanks for taking a look at the API. The cycles-vs-timeout thing is really welcome. It would be nice to set and get values that are retained during a reset caused by the watchdog. Some devices support this (Quark and ESP32 AFAIK). This is useful specially for testing purposes. The ESP32 watchdog offers a way to determine the boot reason, as well, but I'm not sure if this fits in a generic watchdog interface. |
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 like that version
include/watchdog.h
Outdated
wdt_window window; | ||
void *handle; | ||
wdt_callback_t callback; | ||
struct wdt_timeout_cfg *next; |
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.
nice addition
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.
Nice indeed :)
include/watchdog.h
Outdated
void (*interrupt_fn)(struct device *dev); | ||
struct wdt_timeout_cfg { | ||
wdt_window window; | ||
void *handle; |
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.
Is this handle filled in by the caller or by the driver? (maybe a little bit more doc about it to clarify would be needed then)
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.
@tbursztyka It is handle filled by the caller. As there was no agreement what should be the handle I have left that decision to user. It can point to thread id, config structure or whatever.
You are right the documentation needs improvement.
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.
Ah, if that is handled by the user then maybe it's not needed. The user owns the wdt_timeout_cfg pointer: it could be used as the handle then. Could save a pointer of memory.
(and it lets the possibility for the user to use that pointer in a smart way through CONTAINER_OF() and then access some private data of its own)
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.
@tbursztyka using pointer to wdt_timeout_cfg as a handle is a really bad idea. As I argued before we don't save anything, in contrary we waste sizeof(wdt_timeout_cfg) RAM for every stage or every channel. It's better if wdt_timeout_cfg variable lives shortly on a stack when we call wdt_install_timeout() function. No need to force it to be static. Not mentioning the fact that having pointer to wdt_timeout_cfg as a handle would further complicate this API. It's already pretty complex.
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.
True, missed that point.
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.
@mnkp Having all wdt_timeout_cfg as const and stored in flash would not waste RAM, isn't it?
I am just asking, I am also not a big fan of storing wdt_timtout_cfg pointer as handle.
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.
Having all wdt_timeout_cfg as const and stored in flash would not waste RAM, isn't it?
That's correct.
include/watchdog.h
Outdated
* @retval -ENOMEM if no more timeouts can be installed. | ||
* @retval -EINVAL if any of the window timeout value is out of possible range. | ||
* @retval -ENOEXEC if WDT_MODE_CALLBACK is set but interrupt function | ||
* pointer is NULL. |
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 don't know it should be a terminal error here. Isn't it so the nrf5 wdt for instance can be set to trigger an interrupt on underflow/error (which could call a cb then) but will still reset anyway?
Maybe just a SYS_LOG_WARN() in this case would be just fine (to tell the dev that maybe he is doing something unwanted)
include/watchdog.h
Outdated
enum wdt_mode mode; | ||
void (*interrupt_fn)(struct device *dev); | ||
struct wdt_timeout_cfg { | ||
wdt_window window; |
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.
Documentation: I think It is worth to mention that In case a device doesn't support the window feature the window.down value is ignored or (better) must be equal to 0. It should be told unambiguously.
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.
Good work! I do have some extra comments though.
include/watchdog.h
Outdated
*/ | ||
struct wdt_window { | ||
u16_t up; | ||
u16_t 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.
Let's change the field names to: min, max. When we are talking about timeout window having the minimum and maximum timeout value is more straightforward; up, down is confusing.
include/watchdog.h
Outdated
* WDT configuration struct. | ||
* @brief Watchdog timeout configuration struct. | ||
* | ||
* @param window - timing parameters of watchdog timeout. |
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 changed to
@param window timing parameters of watchdog timeout.
i.e. we should get rid of '-'. Here and in a few other places. It will not look good when rendered by Doxygen.
include/watchdog.h
Outdated
*/ | ||
struct wdt_window { | ||
u16_t up; | ||
u16_t 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.
We should use the same type as Kernel API, that is s32_t to keep APIs consistent. Also u16_t is limited to 65.5 seconds, for some applications that may be too short.
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.
@mnkp Could you explain why signed is used in kernel API. Using signed here, where negative values makes no sense is at least weird for me.
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.
Good question. I'm afraid I cannot give any sensible answer here. Maybe someone else?
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.
isn't it so because there is no garanty the timer will exactly be stopped at the right tick, so the remaining time might be below 0 (which then is a stop condition for the timer as well as if it would be 0)
include/watchdog.h
Outdated
u32_t timeout; | ||
enum wdt_mode mode; | ||
void (*interrupt_fn)(struct device *dev); | ||
struct wdt_timeout_cfg { |
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 structure is missing a way to specify timeout action for multistage watchdogs. I.e. we should be able to support the following scenarios.
Scenario 1:
- stage 1 -> run callback_1
- stage 2 -> run callback_2
- stage 3 -> perform SoC reset
Scenario 2:
- stage 1 -> run callback_1
- stage 2 -> run callback_2
- stage 3 -> run callback_3
We may also want to ensure that it's possible to extend this API in the future (if it ever becomes a requirement) to support Scenario 3:
- stage 1 -> run callback_1
- stage 2 -> run callback_2
- stage 3 -> perform CPU core reset
- stage 4 -> perform SoC reset
It seems like WDT_MODE_CALLBACK should belong to this struct and not to wdt_cfg. Also maybe it would make sense to replace WDT_MODE_CALLBACK with WDT_MODE_SOC_RESET? If we provide a valid (not NULL) callback pointer it means we want the watchdog driver to run the callback (no need to re-confirm this by explicit WDT_MODE_CALLBACK) and we provide WDT_MODE_SOC_RESET flag if we want a reset to be performed. We would support this way all different watchdog hardware:
- Atmels SAM family - single channel, single stage watchdog. If callback is configured the reset will not be performed.
- NXP K64 family - single channel, single stage watchdog. If callback is configured it will be executed, reset will always be performed 256 bus clock cycles later.
- Espressif ESP32 family - single channel, multiple stage watchdog. Every stage can be independently configured to either execute a callback or trigger a reset.
- Nordic nRF51 series - multi-channel, single stage watchdog. If callback is configured it will be executed, reset will always be performed 2 slow clock cycles later.
E.g. if we try to configure NXP K64 watchdog without providing WDT_MODE_SOC_RESET flag the call will return ENOTSUP.
We may also choose not to use WDT_MODE_SOC_RESET flag and rely on the default SoC behavior. In this case however user application will not be able to determine if the watchdog will or will not perform a reset when it configures a callback. To get around this we may provide mechanism to retrieve watchdog capabilities.
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.
@mnkp I was also thinking about that. The question is if that API should be so robust or maybe we should left configuring the behavior of each stage in Kconfig.SoC. Suppose that WDT_MODE_CALLBACK is not set so there generally will be some kind of reset.
What kind of reset and after what stage could be configured in Kconfig. Then driver would only check if configuring timeouts is consistent with options set in Kconfig. For example if following option is desired:
- stage 1 -> run callback_1
- stage 2 -> run callback_2
- stage 3 -> perform SoC reset
the driver would return some error code if not enough wdt_timeout_cfg
are supplied.
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.
There are a few problems with using Kconfig:
- It will make the API opaque. User will have difficulty determining what is happening.
- it will not be very portable. Either every driver will implement their own set of Kconfig options or we try to come up with a common set of options defined in drivers/watchdog/Kconfig. The former is not portable, in the latter case we won't be able to map the options between different driver instances. Unless we put it in DTS but it's not straightforward. I think this should be considered the last resort.
include/watchdog.h
Outdated
void (*interrupt_fn)(struct device *dev); | ||
struct wdt_timeout_cfg { | ||
wdt_window window; | ||
void *handle; |
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.
@tbursztyka using pointer to wdt_timeout_cfg as a handle is a really bad idea. As I argued before we don't save anything, in contrary we waste sizeof(wdt_timeout_cfg) RAM for every stage or every channel. It's better if wdt_timeout_cfg variable lives shortly on a stack when we call wdt_install_timeout() function. No need to force it to be static. Not mentioning the fact that having pointer to wdt_timeout_cfg as a handle would further complicate this API. It's already pretty complex.
include/watchdog.h
Outdated
* @retval -EFAULT if watchdog instance has not been configured yet. | ||
* @retval -EBUSY if watchdog instance has been already started. | ||
*/ | ||
static inline int wdt_start(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.
This function is Nordic nRF51 specific. Typically a watchdog is started/stopped when it is configured. Also virtually all watchdogs are started upon reset so having an explicit wdt_start function is confusing. Watchdogs can only be reconfigured. The function can be removed.
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.
hum depends actually. On some SoC you can really stop wdt running via clock controller, and make it running again the same way. That does not seem nrf specific to me.
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.
K64 subfamily can be explicitly stopped through the WDOG_EN bit in the watchdog status and control register. nRF5 family can be explicitly started but can't be explicitly stopped. I would left both wdt_start
and wdt_stop
and return -ENOTSUP if SoC does not support called functionality.
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.
@tbursztyka, @m-kru you are both right and I understand the reasoning behind having the two functions but I still would like to argue that we are better without them.
There are some watchdogs which can be stopped and then restarted but this is a niche behavior. If we get rid of wdt_start we will still support watchdogs which allow to be stopped and restarted with wdt_stop, wdt_configure combo. Rather than having
wdt_stop()
wdt_install_timeout()
...
wdt_install_timeout()
wdt_configure()
wdt_start()
we would have
wdt_stop()
wdt_install_timeout()
...
wdt_install_timeout()
wdt_configure()
wdt_configure() and wdt_start() would have to be used in pairs anyway.
When writing an application we generally shouldn't concern ourselves with the exact driver implementation but the way API was designed. So we would need to use wdt_configure() and wdt_start() also for watchdogs that can't be re-started.
To sum up:
- We don't loose any functionality if we remove this function.
- For majority of the users familiar with the way their watchdog works the API will be more straightforward to use.
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.
@mnkp Would wdt_configure()
automatically start watchdog or should it be configurable (I think it should automatically start it as there is rather no sense in configuring watchdog and not enabling it)?
Btw. I have realized that in API RFC there is no word about uninstalling timeouts. Should we provide such functionality via separate function, or maybe wdt_stop()
should automatically clear all installed timeouts in addition to stopping watchdog? (If yes I think it should be renamed to wdt_disable()
).
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.
wdt_configure() does not start the watchdog, it installs the controller's "behavior".
about loosing installed timeouts, I guess it depends on controllers again.
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 need only one function to configure and start the watchdog. Maybe we can come up with a different name? The only alternative that comes to my mind is wdt_control. Anything else?
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.
wdt_setup()
?
@tbursztyka @mnkp Is there any agreement if there should be one or two functions for configuring and starting watchdog, cause I am a bit lost with that issue right now?
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.
well, maybe mixing up controller's config and starting is a good idea. wdt_start() was not bad, just remove wdt_config and put the parameter into wdt_start?
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.
wdt_start() is not a good name for this function as most of the watchdog modules will already be running when the function is called. User seeing wdt_start() function and not calling it will be right to assume that the watchdog will stay in the disabled state. That's incorrect.
I think wdt_setup()
is a really good name as it reflects well what the function is doing.
include/watchdog.h
Outdated
/** | ||
* @brief Pause watchdog timer when CPU is halted by the debugger. | ||
*/ | ||
#define WDT_PAUSE_HALTED_BY_DBG BIT(2) |
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.
Currently this API has only one field, namely 'options' which requires bit-field defines. But this may change in the future in case the API is extended. E.g. we still need to provide a function to determine the boot reason as requested by @lpereira. To avoid confusion I propose to proceed above defines with WDT_OPT_ to make clear they refer to the options field. It's a common practice and it will make it consistent with I2S API. I.e. WDT_PAUSE_HALTED_BY_DBG should become WDT_OPT_PAUSE_HALTED_BY_DBG
I have one more comment. Currently watchdog API is missing from the official Zephyr API Documentation. I think this PR should also update one of the files in doc/api/. I'm however not sure what's the best section to put it. We should also run doxygen to verify that all doc strings are rendered as expected. I couldn't find the official documentation explaining how it should be done. As far as I remember one has to run
from Zephyr's home directory. There may be some dependencies which need to be installed. |
$ pip3 install --user -r scripts/requirements.txt that will install all deps needed to build documentation |
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.
Style & API design whinery.
include/watchdog.h
Outdated
/** | ||
* @brief Pause watchdog timer when CPU is in sleep state. | ||
*/ | ||
#define WDT_PAUSE_IN_SLEEP BIT(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.
Devil's advocacy: why is this an option? Under what circumstance would you want a driver to fail to automatically pause a watchdog during sleep?
Maybe what we really want is a flag set by the driver that tells the user "watchdog does NOT automatically pause for sleep, you have to do it yourself!" ?
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.
@andyross I am sorry, I am not sure what you mean, since when driver controls the user not the other way.
It is straightforward I guess, you either want watchdog counter to stop counting when CPU is in sleep state or you want it to keep counting, it depends on the application.
include/watchdog.h
Outdated
WDT_2_30_CYCLES, | ||
WDT_2_31_CYCLES | ||
struct wdt_cfg { | ||
u8_t options; |
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.
Sorry, I don't get this one. Why is this a struct without named fields? If you want a simple way to pass three flags, then just pass a flags word directly and not a pointer to a configuration struct. If you're going to go through the trouble to make a struct, we should at least get typesafety out of the deal with e.g. single-bit fields named "callback_en", "pause_in_sleep" and "pause_in_debug" or whatever.
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.
Thanks, I am aware of that, I will improve it in next iteration.
include/watchdog.h
Outdated
* installed so far. | ||
*/ | ||
static inline int wdt_install_timeout(struct device *dev, | ||
struct wdt_timeout_cfg *cfg) |
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.
Similar whine: wdt_timeout_cfg has only two fields. Forcing the user to fill them out on the stack and pass a pointer isn't actually saving anything (in fact I'd expect code size would be rather larger) vs. just passing them as "min" and "max" arguments to the function right here. Seems needlessly complicated to me.
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.
@andyross wdt_timeout_cfg
has 4 fields (or 5 I do not know how to count structure inside structure).
include/watchdog.h
Outdated
|
||
/* | ||
* This is the default, and will be the way until the new API below | ||
* will be enforced everywhere. |
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 exactly is the plan for this?
We have been carrying around "spi" and "spi_legacy" for quite some time, and the legacy API is still the default..
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.
@andrewboie Well, I guess the reason is that no one feels responsible for updating the drivers.
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.
@andrewboie Well, I guess the reason is that no one feels responsible for updating the drivers.
When changing API changes we can't expect drivers to be changed over-night to an API that is still in review. If we just change the APIs to support new IPs and h without looking at the existing drivers we might end up with drivers that can't be moved to the new API easily, looking at existing drivers should be part of the change, or there should be at least a plan.
For starters, we need to have a list of issues added for each driver that needs to be changed, preferably with some instructions for driver owner explaining what changed and what to look for when moving to the new API.
Do we really have to create this _legacy header? Can't we change the API inline deprecating the old APIs? I know it was done for SPI, does not mean it is needed for every API change we do? Maybe it is easier with _legacy.h when implementing the API, but adds lots of complexity and confusion and old API are kept alive and are not deprecated proper.
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.
Can't we change the API inline deprecating the old APIs?
@nashif
You mean making one watchdog.h file and adding deprecated suffix/prefix to old stuff?
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.
yes, that has been the right way to change APIs. Not sure if that is possible at all, but at least it gives us an easier migration path without introducing the LEGACY craft.
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.
__deprecated should not fail the build, this was changed some time ago.
re naming, yes, that will be tricky. Right now however moving everything to be legacy, i.e. renaming driver names to _legacy and declaring this as the new API without having a single driver use it is not helping. Without actually looking at the existing drivers and providing example of how to migrate and working with the driver owners to move to the new API this will be a problem.
We do not want to end up with an API that favours one HW and ignores the others, and that was the case we had initially given that the API was put together to address the first WD driver we introduced to Zephyr.
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 agree, a driver implementing this new API in the same PR would be nice.
Renaming existing drivers with _legacy is not needed, only the dependency in Kconfig is.
In case of spi, responding to @andrewboie, the legacy is the default because of qmsi and the fact that the related hw was our reference platform. I guess, knowing these hw fate now, we can make the new API the default. That said, even new platform like nrf5 is still made on top of old API.
We need to push driver maintainers to go with new APIs asap.
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'm also much in favor of using __deprecated rather than going the LEGACY way. The former is cleaner, with __deprecated user receives very precise warnings at compile time pointing out things that need to be changed.
That said, wdt_reload() is indeed a problem as we've changed the function prototype. We would have to come up with a new name. The two quite popular among different watchdog driver implementations are wdt_kick() and wdt_feed().
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.
not as good as wdt_reload() but well, if we need a new, let's have a new name.
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.
wdt_feed() is better. We do not want to get sued by defenders of animal rights.
https://github.com/zephyrproject-rtos/zephyr/blob/master/doc/README.rst tell how to setup and build documentation |
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.
Hope we are close to the finish with this API.
include/watchdog.h
Outdated
* timeouts then min value must be equal to 0. | ||
* | ||
* @param min Lower limit of watchdog reload timeout in microseconds. | ||
* @param max Upper limit of watchdog reload timeout in microseconds. |
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 have come back to microseconds and 32 bit values. The question is if there is any need to return actual set timeout values. For example the resolution of watchdog timer is 60 us and super supply 100 us. I think there is no need because users usually estimate timeout values and this estimation is rather coarse. Maybe we should only add information to the documentation that these values are always rounded up and they may vary depending on platform?
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 I stated before I am in favor of using the same time unit as Zephyr kernel does. So here is one vote against the change to microseconds. I'll shortly remind my reasoning:
Watchdog clocks are designed to be reliable not precise. Typically these are a very imprecise RC clock sources, often as a fallback when a crystal fails. But even if we had a 32768 crystal oscillator only microsecond precision is not required. We won't use watchdog to measure time. Resolution of watchdog is strictly connected with the system tick. It will never be smaller than a tick length. Watchdog timeout value lays typically between ~10ms to some seconds. It should be as small as possible but has to include all the uncertainties: inaccuracy of the source clock, scheduler. There is always safety margin added to the timeout value and it will never lay in microseconds range. If Zephyr allowed to set system tick to value smaller than 1 ms then the watchdog should do it too but it the same way kernel API does. Let's keep APIs consistent.
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.
The question is if there is any need to return actual set timeout values.
For now we should be fine without providing this information. If there is a need in the future the watchdog API can be extended with a new function. We could also encourage developers to add SYS_LOG_INF in wdt_setup function to print out timing information.
Maybe we should only add information to the documentation that these values are always rounded up
+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.
SYS_LOG_INFO with real timeout is great idea - as it is indeed debug related functionality. So I would be up to the will of MCU ports maintainers.
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.
Is adding a @note
to the documentation of wdt_setup
that it is welcomed to print actual timeouts using SYS_LOG_INFO
an abuse?
include/watchdog.h
Outdated
/** CPU core and peripherals reset */ | ||
#define WDT_FLAG_RESET_CPU_PER BIT(2) | ||
/** Global system reset */ | ||
#define WDT_FLAG_RESET_SOC (BIT(2) | BIT(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.
I do not know how to make clean group doxygen comment for these macros. I have tried different ways from doxygen manual however none on them worked with our auto generated html documentation.
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.
OK. I found a few issues here that will take a few commits to make work. The way we were including the auto-generated docs used a :content-only: directive that on the surface looks OK, has the side-effect of causing all the doxygen headings to be removed and the API material listed in alphabetic order (instead of separating the functions from the typedefs, for example). Removing the :content-only: fixed that, but resulted in some extra headings and layout issues (that required a CSS tweak). When all this gets merged, you can write this section like this to make it come out as a "group doxygen comment" (note the addition of the @name and the start/stop group markers @{ and @}:
/**
* @name Watchdog Reset Behavior
* If none of the reset flags is specified there will be no reset after timeout.
* @{
*/
/** CPU core reset */
#define WDT_FLAG_RESET_CPU>-----BIT(1)
/** CPU core and peripherals reset */
#define WDT_FLAG_RESET_CPU_PER>-BIT(2)
/** Global system reset */
#define WDT_FLAG_RESET_SOC>-----(BIT(2) | BIT(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.
Ready to use this documentation grouping syntax now.
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 are certainly close to finish with this API. It's time to drop RFC from the commit title.
include/watchdog.h
Outdated
|
||
/* | ||
* This is the default, and will be the way until the new API below | ||
* will be enforced everywhere. |
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'm also much in favor of using __deprecated rather than going the LEGACY way. The former is cleaner, with __deprecated user receives very precise warnings at compile time pointing out things that need to be changed.
That said, wdt_reload() is indeed a problem as we've changed the function prototype. We would have to come up with a new name. The two quite popular among different watchdog driver implementations are wdt_kick() and wdt_feed().
include/watchdog.h
Outdated
* @brief Run callback function on watchdog timeout. If any of reset flags is | ||
* set this callback is called before reset. | ||
*/ | ||
#define WDT_FLAG_RUN_CALLBACK BIT(0) |
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.
Now that we have WDT_FLAG_RESET_* flags this one could be removed. We would only need to mention in the description of the callback parameter that passing NULL means that no callback will be run.
If WDT_FLAG_RUN_CALLBACK flag stays the driver will have to check if it is consistent with the value of the callback parameter and return an error if not. That's an extra code. By removing the flag we reduce code size and simplify the API.
include/watchdog.h
Outdated
/** CPU core and peripherals reset */ | ||
#define WDT_FLAG_RESET_CPU_PER BIT(2) | ||
/** Global system reset */ | ||
#define WDT_FLAG_RESET_SOC (BIT(2) | BIT(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.
I believe we need the following two defines only:
#define WDT_FLAG_RESET_CPU BIT(1)
#define WDT_FLAG_RESET_SOC BIT(2)
We probably should be more explicit and rename the first one to WDT_FLAG_RESET_CPU_CORE.
Also, as I mentioned until we re-write our device drivers to take into account the reset source users should not really use WDT_FLAG_RESET_CPU_CORE as it is not supported. So for now we could leave it out completely. This flag can be added later when there is a project which needs it and comes up with a sensible device driver design that supports the flag.
include/watchdog.h
Outdated
* timeouts then min value must be equal to 0. | ||
* | ||
* @param min Lower limit of watchdog reload timeout in microseconds. | ||
* @param max Upper limit of watchdog reload timeout in microseconds. |
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 I stated before I am in favor of using the same time unit as Zephyr kernel does. So here is one vote against the change to microseconds. I'll shortly remind my reasoning:
Watchdog clocks are designed to be reliable not precise. Typically these are a very imprecise RC clock sources, often as a fallback when a crystal fails. But even if we had a 32768 crystal oscillator only microsecond precision is not required. We won't use watchdog to measure time. Resolution of watchdog is strictly connected with the system tick. It will never be smaller than a tick length. Watchdog timeout value lays typically between ~10ms to some seconds. It should be as small as possible but has to include all the uncertainties: inaccuracy of the source clock, scheduler. There is always safety margin added to the timeout value and it will never lay in microseconds range. If Zephyr allowed to set system tick to value smaller than 1 ms then the watchdog should do it too but it the same way kernel API does. Let's keep APIs consistent.
include/watchdog.h
Outdated
* timeouts then min value must be equal to 0. | ||
* | ||
* @param min Lower limit of watchdog reload timeout in microseconds. | ||
* @param max Upper limit of watchdog reload timeout in microseconds. |
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.
The question is if there is any need to return actual set timeout values.
For now we should be fine without providing this information. If there is a need in the future the watchdog API can be extended with a new function. We could also encourage developers to add SYS_LOG_INF in wdt_setup function to print out timing information.
Maybe we should only add information to the documentation that these values are always rounded up
+1
include/watchdog.h
Outdated
* @retval -ENOTSUP If any of the set options is not supported. | ||
* @retval -EBUSY If watchdog instance has been already setup. | ||
*/ | ||
static inline int wdt_set_up(struct device *dev, u8_t options) |
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 looks like a correct English grammar but set-up function has always been called setup. Maybe we should stick with the convention and change it to wdt_setup
?
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.
From a grammar point of view, "setup" is a noun (or adjective) while "set up" is a verb (phrase), so you can "set up a chess board", or you can "go to a setup screen", or you can "pay a setup fee" (generally not used with a hyphen.) Alas, @mnkp is probably right with the convention of using "setup" rather than "set_up" in function names even if it's an action.
include/watchdog.h
Outdated
struct wdt_config *config) | ||
/** | ||
* @brief Install new timeout. | ||
* |
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.
Watchdogs are typically started on reset, many can be configured only once. This means that there can only be one function which can perform actual hardware changes and it has to be wdt_setup(). We should state here that wdt_install_timeout() will not perform any actual changes, not until wdt_setup() is called (even if description of wdt_setup() already suggests such behavior).
include/watchdog.h
Outdated
* @brief Disable watchdog instance. | ||
* | ||
* This function stops watchdog instance and automatically uninstalls all | ||
* timeouts. To set up watchdog again it is required to install timeouts. |
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.
* timeouts.
*
* To set up watchdog again it is required to install timeouts and call
* wdt_setup function. Not all watchdogs can be re-started once they are
* disabled.
include/watchdog.h
Outdated
* @retval -EBUSY If timeout can not be installed while watchdog has already | ||
* been setup. | ||
* @retval -ENOMEM If no more timeouts can be installed. | ||
* @retval -EFAULT If timeout with given handle has already been installed. |
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 should allow using the same handle for multistage watchdogs. Only for multi-channel watchdogs it would be a problem.
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.
@mnkp I have added following sentence to wdt_timeout_cfg
structure documentation.
It is allowed to use the same handle for multistage timeouts.
Is it enough or do you want to mention about that also in the documentation of wdt_install_timeout()
function?
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's probably best not to repeat the information twice but I think that the -EFAULT error description should mention that it applies to multi-channel watchdogs only.
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.
Sorry, I did not consider this case carefully enough. For the most popular single channel, single stage watchdogs we also shouldn't allow to re-install the timeout (since we don't allow to re-install timeouts for multi-channel watchdogs). So the sentence should better be changed to something like "Multistage watchdogs are allowed to use the same handle for different stages" or "This limitation does not apply to multistage watchdogs".
2 WARNING, write SYS_LOG_WRN in addition to previous level | ||
3 INFO, write SYS_LOG_INF in addition to previous levels | ||
4 DEBUG, write SYS_LOG_DBG in addition to previous levels | ||
|
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 propose to add WDT_DISABLE_AT_BOOT option to Kconfig to allow disabling watchdog at boot. Useful at the initial stage of the project development as well as for many sample applications where we don't explicitly handle watchdogs.
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.
@mnkp Should it depend on WATCHDOG? It would be funny if you enable WATCHDOG to disable it :).
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.
True, but actually it should depend on WATCHDOG. If the watchdog subsystem is not enabled we can't disable it.
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.
@mnkp You think it should be default y
or default n
?
By the way which one is correct: at boot or on boot?
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 think it should be default n
. The board defconfig file can still choose to set it to default y
if deemed required.
Later we should only ensure that all the applications in samples/ directory that don't explicitly handle watchdog have WDT_DISABLE_AT_BOOT
option enabled.
doc/api/timer_counter_interfaces.rst
Outdated
|
||
.. doxygengroup:: watchdog_interface | ||
:project: Zephyr | ||
:content-only: |
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.
delete this line (:contents-only:)
include/watchdog.h
Outdated
#define WDT_FLAG_RUN_CALLBACK BIT(0) | ||
|
||
/** | ||
* @brief Watchdog reset behavior. |
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.
recommend using the grouping syntax noted below; the doc infrastructure needed has been merged now. As this is written these comments only apply to the first define (WDT_FLAG_RESET_CPU)
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.
a few recommended edits
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.
@dbkinder parts of the documentation were re-written, it would be great if you could double check the style. We're not native English speakers so you are the only hope.
include/watchdog.h
Outdated
{ | ||
const struct wdt_driver_api *api = dev->driver_api; | ||
|
||
api->enable(dev); | ||
} | ||
|
||
static inline void wdt_disable(struct device *dev) | ||
static inline void __deprecated wdt_disable(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.
@m-kru did you try to compile this file? I believe it would result in an error as wdt_disable
function is defined twice. To solve this issue it should be enough to remove the second definition, lines 269-274. struct wdt_driver_api
also needs an update.
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.
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.
In both new and old API wdt_disable
function has the same functionality and the same prototype (almost). The only difference is the function return value: int in the new API, void in the old one.
I propose to keep wdt_disable
as it is. You'll need to remove the second definition, lines 269-274 and additionally modify all existing watchdog drivers to ensure wdt_disable
returns int. I believe this is the easiest solution. Unless I overlooked something?
Your current proposal is invalid, it does not parse in compiler as reported by Shippable. There are two more compile time errors which need to be fixed.
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.
@mnkp I am aware of that, I just was not sure how to solve this function name issue. I will update PR today.
include/watchdog.h
Outdated
* @retval -EBUSY If timeout can not be installed while watchdog has already | ||
* been setup. | ||
* @retval -ENOMEM If no more timeouts can be installed. | ||
* @retval -EFAULT If timeout with given handle has already been installed. |
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.
Sorry, I did not consider this case carefully enough. For the most popular single channel, single stage watchdogs we also shouldn't allow to re-install the timeout (since we don't allow to re-install timeouts for multi-channel watchdogs). So the sentence should better be changed to something like "Multistage watchdogs are allowed to use the same handle for different stages" or "This limitation does not apply to multistage watchdogs".
include/watchdog.h
Outdated
/** | ||
* @brief Disable watchdog instance. | ||
* | ||
* This function stops watchdog instance and automatically uninstalls all |
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 we should write "This function disables watchdog instance"? In this case user will not have to consider if there is any difference between stopping and disabling the watchdog.
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.
agree! But use:
This function disables the watchdog instance...
include/watchdog.h
Outdated
* @param dev Pointer to the device structure for the driver instance. | ||
* @param options Bit field with following parts: | ||
* | ||
* pause in sleep [ 0 ] - pause in CPU sleep |
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.
"- pause when CPU is in sleep state" ?
Or we could simply say:
@param options Configuration options as defined by the WDT_OPT_* constants.
include/watchdog.h
Outdated
*/ | ||
/** CPU core reset */ | ||
#define WDT_FLAG_RESET_CPU_CORE BIT(0) | ||
/** Global system reset */ |
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 would write "Global SoC reset" or "Global chip reset". System reset suggests that also other board peripherals (on the PCB, external to SoC) will be reset. However this is SoC/board specific, doesn't have to be the case.
include/watchdog.h
Outdated
/** | ||
* @brief Watchdog reset flag bit field mask. | ||
*/ | ||
#define WDT_FLAG_RESET_MASK (0x3) |
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 we encode every reset type as a separate flag/bit (we use BIT macro) I think we shouldn't define WDT_FLAG_RESET_MASK. However, if we do want to treat WDT_FLAG_RESET_* as a multi-bit bit field we should define it as follows:
#define WDT_FLAG_RESET_SHIFT (0)
#define WDT_FLAG_RESET_MASK (0x3 << WDT_FLAG_RESET_SHIFT)
#define WDT_FLAG_RESET_NONE (0 << WDT_FLAG_RESET_SHIFT)
#define WDT_FLAG_RESET_CPU_CORE (1 << WDT_FLAG_RESET_SHIFT)
#define WDT_FLAG_RESET_SOC (2 << WDT_FLAG_RESET_SHIFT)
to be consistent with remaining Zephys APIs.
* @note If specified values can not be precisely set they are always | ||
* rounded up. | ||
*/ | ||
struct wdt_window { |
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.
There was a suggestion before to flatten this whole structure directly into struct wdt_timeout_cfg
to make it easier for the user to define timeout configuration. That may be not a bad idea. Did we make a final decision? Anyone against/for such a change?
include/watchdog.h
Outdated
* | ||
* This function is used for configuring global watchdog settings that | ||
* affect all timeouts. It should be called after installing timeouts. | ||
* After successful return all installed timeouts are valid. |
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.
are valid and have to be serviced periodically by calling wdt_feed function.
include/watchdog.h
Outdated
} | ||
|
||
/** | ||
* @brief Reload specified watchdog timeout. |
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.
"Feed specified watchdog timeout." would sound strange, but using 'reload' interchangeably with 'feed' may also be non obvious. Wouldn't 'service' be a better choice. As in "Service specified watchdog timeout."?
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.
But the function name is wdt_feed so "feed" is consistent (and appears in many other places)
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.
So let's stay with "feed". Thanks @dbkinder for the review!
include/watchdog.h
Outdated
* @brief Reload specified watchdog timeout. | ||
* | ||
* @param dev Pointer to the device structure for the driver instance. | ||
* @param handle Handle of timeout that should be fed. |
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.
And this could be changed to "Handle of timeout that should be serviced."
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.
Don't want to "service" sometimes and "feed" others, so given the name of the function is "feed", I'd think you want to keep to using "feed".
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.
Thanks for clarification. While "feeding the watchdog" sounds natural I was not sure if "feeding the watchdog timeout" is also OK, i.e. I was not sure if "feed" can be used in a context it was used here.
include/watchdog.h
Outdated
/** | ||
* @brief Watchdog timeout window. | ||
* | ||
* Each installed timeout needs feeding in specified time window, otherwise |
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.
better would read:
... needs feeding within the specified ...
@@ -1,23 +1,39 @@ | |||
/* | |||
* Copyright (c) 2015 Intel Corporation. | |||
* Copyright (c) 2017 Nordic Semiconductor ASA |
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 should not remove existing copyrights, just add the new copyright on top please.
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.
Minor English nitpicks
include/watchdog.h
Outdated
* @param dev Pointer to the device structure for the driver instance. | ||
* @param options Configuration options as defined by the WDT_OPT_* constants | ||
* | ||
* @retval 0 If success. |
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.
English nitpick: should be "If successful".
include/watchdog.h
Outdated
* | ||
* @param dev Pointer to the device structure for the driver instance. | ||
* | ||
* @retval 0 If success. |
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.
Same here ("if successful").
include/watchdog.h
Outdated
* @param dev Pointer to the device structure for the driver instance. | ||
* @param cfg Pointer to timeout configuration structure. | ||
* | ||
* @retval 0 If success. |
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.
Same here ("if successful").
include/watchdog.h
Outdated
* @param dev Pointer to the device structure for the driver instance. | ||
* @param handle Handle of timeout that should be fed. | ||
* | ||
* @retval 0 If success. |
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.
Same here ("if successful").
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.
do not rename the current test (wdt_deprecated), leave it as is and it should be fixed to use the new APIs and probably made more generic, right now it is very qmsi specific. |
New API enables setting watchdog timeout in the unit of microseconds. It is possible to configure watchdog timer behavior in CPU sleep state as well as when it is halted by the debugger. It supports watchdogs with multiple reload channels. Jira: ZEP-2564 Signed-off-by: Michał Kruszewski <[email protected]>
At this point, I feel like this is now 1.11, any concerns related to pushing to 1.11? |
This PR is all but ready. As far as I can tell all issues have already been addressed, only not yet approved by respective reviewers. It would be great if everyone followed up on the issues they reported. The status of the PR would be clearer. That said it probably doesn't make sense to merge this PR in 1.10 if there are no drivers which implement it. If no one reports new issues we should merge this PR as soon as the window for 1.11 release is open. This will give us enough time to update the watchdog drivers. Or should the process be different? There is also a question on how we should split the work. Shall we create an issue for every watchdog driver that needs to be converted to the new API so any developer interested in working on the task can take ownership of it? |
…ct-rtos#1286) This includes base configuration common to all platforms. Fixes zephyrproject-rtos#1260. Signed-off-by: Geoff Gustafson <[email protected]>
This PR is all but ready, shouldn't we merge it? It will give us some time to update watchdog drivers before 1.11 release window is closed. It would be shame to leave it hanging after so much discussion and time invested. |
yes, agree. There are still a few unaddressed comments, but those are minor IMO. Will put this on the top of the list for next week. |
* window differs from windows for alarms installed so far. | ||
*/ | ||
static inline int wdt_install_timeout(struct device *dev, | ||
struct wdt_timeout_cfg *cfg) |
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.
Is there a reason why the second parameter is not a pointer to a constant structure? It seems that this function is not supposed to modify anything in the pointed content. And currently one cannot use a constant structure (e.g. stored in flash) without doing ugly typecasting.
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.
Good point. It certainly makes sense to make it const. We've overlooked it.
3 INFO, write SYS_LOG_INF in addition to previous levels | ||
4 DEBUG, write SYS_LOG_DBG in addition to previous levels | ||
|
||
config HAVE_WDT_MULTISTAGE |
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 there any SoC which has it already?
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.
ESP32 supports 4 watchdog stages.
Looks like this is not getting into 1.11 (it's in pre-release freeze already). Changing milestone to 1.12. |
this was superseded by another PR |
New API enables setting watchdog timeout in the unit of microseconds.
It is possible to configure watchdog timer behavior in CPU sleep state
as well as when it is halted by the debugger.
It supports watchdogs with multiple reload channels.
Fixes #3994
Signed-off-by: Michał Kruszewski [email protected]