Skip to content

Commit

Permalink
drivers: watchdog: Add Raspberry Pi Pico watchdog driver
Browse files Browse the repository at this point in the history
This adds basic support for the watchdog timer on the RP2040 MCU and
Raspberry Pi Pico development board

Signed-off-by: Jamie McCrae <spam@helper3000.net>
  • Loading branch information
thedjnK authored and carlescufi committed Jul 6, 2022
1 parent 927d6b6 commit a20eb66
Show file tree
Hide file tree
Showing 8 changed files with 234 additions and 0 deletions.
1 change: 1 addition & 0 deletions CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,7 @@
/drivers/watchdog/wdt_ite_it8xxx2.c @RuibinChang
/drivers/watchdog/Kconfig.it8xxx2 @RuibinChang
/drivers/watchdog/wdt_counter.c @nordic-krch
/drivers/watchdog/*rpi_pico* @thedjnK
/drivers/wifi/ @rlubos @tbursztyka @pfalcon
/drivers/wifi/esp_at/ @mniestroj
/drivers/wifi/eswifi/ @loicpoulain @nandojve
Expand Down
11 changes: 11 additions & 0 deletions boards/arm/rpi_pico/rpi_pico.dts
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,15 @@
};
};

xtal_clk: xtal-clk {
compatible = "fixed-clock";
clock-frequency = <12000000>;
#clock-cells = <0>;
};

aliases {
led0 = &led0;
watchdog0 = &wdt0;
};
};

Expand Down Expand Up @@ -85,6 +92,10 @@
pinctrl-names = "default";
};

&wdt0 {
status = "okay";
};

zephyr_udc0: &usbd {
status = "okay";
};
1 change: 1 addition & 0 deletions drivers/watchdog/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ zephyr_library_sources_ifdef(CONFIG_WDT_MCUX_WDOG32 wdt_mcux_wdog32.c)
zephyr_library_sources_ifdef(CONFIG_WDT_MCUX_WWDT wdt_mcux_wwdt.c)
zephyr_library_sources_ifdef(CONFIG_WDT_NPCX wdt_npcx.c)
zephyr_library_sources_ifdef(CONFIG_WDT_NRFX wdt_nrfx.c)
zephyr_library_sources_ifdef(CONFIG_WDT_RPI_PICO wdt_rpi_pico.c)
zephyr_library_sources_ifdef(CONFIG_WDT_SAM wdt_sam.c)
zephyr_library_sources_ifdef(CONFIG_WDT_SAM0 wdt_sam0.c)
zephyr_library_sources_ifdef(CONFIG_WDT_SIFIVE wdt_sifive.c)
Expand Down
3 changes: 3 additions & 0 deletions drivers/watchdog/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,7 @@ source "drivers/watchdog/Kconfig.npcx"
source "drivers/watchdog/Kconfig.cc32xx"

source "drivers/watchdog/Kconfig.it8xxx2"

source "drivers/watchdog/Kconfig.rpi_pico"

endif
20 changes: 20 additions & 0 deletions drivers/watchdog/Kconfig.rpi_pico
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Copyright (c) 2022, Jamie McCrae
# SPDX-License-Identifier: Apache-2.0

DT_COMPAT_RASPBERRYPI_PICO_WATCHDOG := raspberrypi,pico-watchdog

config WDT_RPI_PICO
bool "Raspberry Pi Pico Watchdog driver"
default $(dt_compat_enabled,$(DT_COMPAT_RASPBERRYPI_PICO_WATCHDOG))
depends on SOC_FAMILY_RPI_PICO

config WDT_RPI_PICO_INITIAL_TIMEOUT
int "Default watchdog timeout in us"
depends on WDT_RPI_PICO
default 8388607
range 1 8388607
help
Sets the default watchdog timeout at start-up, the feed function must
be called every interval prior to this time elapsing to prevent a
reboot of the module. The default is just over 8 seconds, which is the
largest timeout possible.
171 changes: 171 additions & 0 deletions drivers/watchdog/wdt_rpi_pico.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
/*
* Copyright (c) 2022, Jamie McCrae
*
* SPDX-License-Identifier: Apache-2.0
*/

#define DT_DRV_COMPAT raspberrypi_pico_watchdog

#include <hardware/watchdog.h>
#include <hardware/structs/psm.h>
#include <zephyr/drivers/watchdog.h>

#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(wdt_rpi_pico, CONFIG_WDT_LOG_LEVEL);

/* Maximum watchdog time is halved due to errata RP2040-E1 */
#define RPI_PICO_MAX_WDT_TIME (0xffffff / 2)
#define RPI_PICO_WDT_TIME_MULTIPLICATION_FACTOR 2

/* Watchdog requires a 1MHz clock source, divided from the crystal oscillator */
#define RPI_PICO_XTAL_FREQ_WDT_TICK_DIVISOR 1000000

struct wdt_rpi_pico_data {
uint8_t reset_type;
uint32_t load;
bool enabled;
};

struct wdt_rpi_pico_config {
uint32_t xtal_frequency;
};

static int wdt_rpi_pico_setup(const struct device *dev, uint8_t options)
{
const struct wdt_rpi_pico_config *config = dev->config;
struct wdt_rpi_pico_data *data = dev->data;

if ((options & WDT_OPT_PAUSE_IN_SLEEP) == 1) {
return -ENOTSUP;
}

hw_clear_bits(&watchdog_hw->ctrl, WATCHDOG_CTRL_ENABLE_BITS);

psm_hw->wdsel = 0;

/* TODO: Handle individual core reset when SMP support for RP2040 is added */
if (data->reset_type == WDT_FLAG_RESET_SOC) {
hw_set_bits(&psm_hw->wdsel, PSM_WDSEL_BITS);
} else if (data->reset_type == WDT_FLAG_RESET_CPU_CORE) {
hw_set_bits(&psm_hw->wdsel, PSM_WDSEL_PROC0_BITS);
}

if ((options & WDT_OPT_PAUSE_HALTED_BY_DBG) == 0) {
hw_clear_bits(&watchdog_hw->ctrl,
(WATCHDOG_CTRL_PAUSE_JTAG_BITS | WATCHDOG_CTRL_PAUSE_DBG0_BITS |
WATCHDOG_CTRL_PAUSE_DBG1_BITS));
} else {
hw_set_bits(&watchdog_hw->ctrl,
(WATCHDOG_CTRL_PAUSE_JTAG_BITS | WATCHDOG_CTRL_PAUSE_DBG0_BITS |
WATCHDOG_CTRL_PAUSE_DBG1_BITS));
}

watchdog_hw->load = data->load;

/* Zero out the scratch registers so that the module reboots at the
* default program counter
*/
watchdog_hw->scratch[4] = 0;
watchdog_hw->scratch[5] = 0;
watchdog_hw->scratch[6] = 0;
watchdog_hw->scratch[7] = 0;

hw_set_bits(&watchdog_hw->ctrl, WATCHDOG_CTRL_ENABLE_BITS);

data->enabled = true;

watchdog_hw->tick = (config->xtal_frequency / RPI_PICO_XTAL_FREQ_WDT_TICK_DIVISOR) |
WATCHDOG_TICK_ENABLE_BITS;

return 0;
}

static int wdt_rpi_pico_disable(const struct device *dev)
{
struct wdt_rpi_pico_data *data = dev->data;

if (data->enabled == false) {
return -EFAULT;
}

hw_clear_bits(&watchdog_hw->ctrl, WATCHDOG_CTRL_ENABLE_BITS);

data->enabled = false;

return 0;
}

static int wdt_rpi_pico_install_timeout(const struct device *dev, const struct wdt_timeout_cfg *cfg)
{
struct wdt_rpi_pico_data *data = dev->data;

if (cfg->window.min != 0U || cfg->window.max == 0U) {
return -EINVAL;
} else if (cfg->window.max > RPI_PICO_MAX_WDT_TIME) {
return -EINVAL;
} else if (cfg->callback != NULL) {
return -ENOTSUP;
} else if ((cfg->flags & WDT_FLAG_RESET_MASK) == WDT_FLAG_RESET_NONE) {
/* The RP2040 does technically support this mode, but requires
* a program counter and stack pointer value to be set,
* therefore do not allow configuring in this mode
*/
return -EINVAL;
}

data->load = (cfg->window.max * RPI_PICO_WDT_TIME_MULTIPLICATION_FACTOR);
data->reset_type = (cfg->flags & WDT_FLAG_RESET_MASK);

return 0;
}

static int wdt_rpi_pico_feed(const struct device *dev, int channel_id)
{
struct wdt_rpi_pico_data *data = dev->data;

if (channel_id != 0) {
/* There is only one input to the watchdog */
return -EINVAL;
}

if (data->enabled == false) {
/* Watchdog is not running so does not need to be fed */
return -EINVAL;
}

watchdog_hw->load = data->load;

return 0;
}

static int wdt_rpi_pico_init(const struct device *dev)
{
#ifndef CONFIG_WDT_DISABLE_AT_BOOT
return wdt_rpi_pico_setup(dev, WDT_OPT_PAUSE_HALTED_BY_DBG);
#endif

return 0;
}

static const struct wdt_driver_api wdt_rpi_pico_driver_api = {
.setup = wdt_rpi_pico_setup,
.disable = wdt_rpi_pico_disable,
.install_timeout = wdt_rpi_pico_install_timeout,
.feed = wdt_rpi_pico_feed,
};

#define WDT_RPI_PICO_WDT_DEVICE(idx) \
static const struct wdt_rpi_pico_config wdt_##idx##_config = { \
.xtal_frequency = DT_INST_PROP_BY_PHANDLE(idx, clocks, clock_frequency) \
}; \
static struct wdt_rpi_pico_data wdt_##idx##_data = { \
.reset_type = WDT_FLAG_RESET_SOC, \
.load = (CONFIG_WDT_RPI_PICO_INITIAL_TIMEOUT * \
RPI_PICO_WDT_TIME_MULTIPLICATION_FACTOR), \
.enabled = false \
}; \
DEVICE_DT_DEFINE(DT_NODELABEL(wdt##idx), wdt_rpi_pico_init, NULL, &wdt_##idx##_data, \
&wdt_##idx##_config, PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \
&wdt_rpi_pico_driver_api)

DT_INST_FOREACH_STATUS_OKAY(WDT_RPI_PICO_WDT_DEVICE);
8 changes: 8 additions & 0 deletions dts/arm/rpi_pico/rp2040.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,14 @@
status = "disabled";
};

wdt0: watchdog@40058000 {
compatible = "raspberrypi,pico-watchdog";
reg = <0x40058000 DT_SIZE_K(4)>;
clocks = <&xtal_clk>;
label = "WDT_0";
status = "disabled";
};

usbd: usbd@50100000 {
compatible = "raspberrypi,pico-usbd";
reg = <0x50100000 0x10000>;
Expand Down
19 changes: 19 additions & 0 deletions dts/bindings/watchdog/raspberrypi,pico-watchdog.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Copyright (c) 2022, Jamie McCrae
# SPDX-License-Identifier: Apache-2.0

description: Raspberry Pi Pico Watchdog

compatible: "raspberrypi,pico-watchdog"

include: base.yaml

properties:
reg:
required: true

label:
required: true

clocks:
required: true
description: Crystal oscillator source clock

0 comments on commit a20eb66

Please sign in to comment.