From a20eb667420dbf6d4fd869ac150e7e07fa75fefa Mon Sep 17 00:00:00 2001 From: Jamie McCrae Date: Wed, 2 Mar 2022 22:47:25 +0000 Subject: [PATCH] drivers: watchdog: Add Raspberry Pi Pico watchdog driver This adds basic support for the watchdog timer on the RP2040 MCU and Raspberry Pi Pico development board Signed-off-by: Jamie McCrae --- CODEOWNERS | 1 + boards/arm/rpi_pico/rpi_pico.dts | 11 ++ drivers/watchdog/CMakeLists.txt | 1 + drivers/watchdog/Kconfig | 3 + drivers/watchdog/Kconfig.rpi_pico | 20 ++ drivers/watchdog/wdt_rpi_pico.c | 171 ++++++++++++++++++ dts/arm/rpi_pico/rp2040.dtsi | 8 + .../watchdog/raspberrypi,pico-watchdog.yaml | 19 ++ 8 files changed, 234 insertions(+) create mode 100644 drivers/watchdog/Kconfig.rpi_pico create mode 100644 drivers/watchdog/wdt_rpi_pico.c create mode 100644 dts/bindings/watchdog/raspberrypi,pico-watchdog.yaml diff --git a/CODEOWNERS b/CODEOWNERS index cecf7e62727e68..a3157e1cca78d7 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -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 diff --git a/boards/arm/rpi_pico/rpi_pico.dts b/boards/arm/rpi_pico/rpi_pico.dts index 2270dc37165d2d..4dcc03c13b7c31 100644 --- a/boards/arm/rpi_pico/rpi_pico.dts +++ b/boards/arm/rpi_pico/rpi_pico.dts @@ -28,8 +28,15 @@ }; }; + xtal_clk: xtal-clk { + compatible = "fixed-clock"; + clock-frequency = <12000000>; + #clock-cells = <0>; + }; + aliases { led0 = &led0; + watchdog0 = &wdt0; }; }; @@ -85,6 +92,10 @@ pinctrl-names = "default"; }; +&wdt0 { + status = "okay"; +}; + zephyr_udc0: &usbd { status = "okay"; }; diff --git a/drivers/watchdog/CMakeLists.txt b/drivers/watchdog/CMakeLists.txt index ea28a15b5dc53d..2056cc0b6f3c2a 100644 --- a/drivers/watchdog/CMakeLists.txt +++ b/drivers/watchdog/CMakeLists.txt @@ -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) diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index cc646cd83bf9e5..543a5deb38509d 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -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 diff --git a/drivers/watchdog/Kconfig.rpi_pico b/drivers/watchdog/Kconfig.rpi_pico new file mode 100644 index 00000000000000..a3636f06af0aa6 --- /dev/null +++ b/drivers/watchdog/Kconfig.rpi_pico @@ -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. diff --git a/drivers/watchdog/wdt_rpi_pico.c b/drivers/watchdog/wdt_rpi_pico.c new file mode 100644 index 00000000000000..4f82627b5e8d9e --- /dev/null +++ b/drivers/watchdog/wdt_rpi_pico.c @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2022, Jamie McCrae + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT raspberrypi_pico_watchdog + +#include +#include +#include + +#include +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); diff --git a/dts/arm/rpi_pico/rp2040.dtsi b/dts/arm/rpi_pico/rp2040.dtsi index 9b78248b769ed3..639fb3dbd0785e 100644 --- a/dts/arm/rpi_pico/rp2040.dtsi +++ b/dts/arm/rpi_pico/rp2040.dtsi @@ -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>; diff --git a/dts/bindings/watchdog/raspberrypi,pico-watchdog.yaml b/dts/bindings/watchdog/raspberrypi,pico-watchdog.yaml new file mode 100644 index 00000000000000..cb93ee9d563a5b --- /dev/null +++ b/dts/bindings/watchdog/raspberrypi,pico-watchdog.yaml @@ -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