From 4e1179bba55da78d462b47c9b744b12bdab7ee57 Mon Sep 17 00:00:00 2001 From: Subramanian Meenakshi Sundaram Date: Wed, 24 Oct 2018 22:16:25 -0700 Subject: [PATCH] drivers: watchdog: Adding watchdog support for sam SOC Adding watchdog driver for sam SoC. The current implemntation just diables the watchdog on device boot. This PR adds the following support for SAM3X, SAM4S and SAME70 1. Activate processor reset 2. Activate all reset 3. Generate interrupt on watchdog timer expiration. Signed-off-by: Subramanian Meenakshi Sundaram Signed-off-by: Kumar Gala --- boards/arm/arduino_due/arduino_due.dts | 6 + boards/arm/arduino_due/arduino_due.yaml | 2 + boards/arm/arduino_due/doc/board.rst | 2 + .../arm/sam4s_xplained/doc/sam4s_xplained.rst | 2 + boards/arm/sam4s_xplained/sam4s_xplained.dts | 5 + boards/arm/sam4s_xplained/sam4s_xplained.yaml | 1 + .../arm/sam_e70_xplained/sam_e70_xplained.dts | 5 + .../sam_e70_xplained/sam_e70_xplained.yaml | 1 + drivers/watchdog/wdt_sam.c | 215 +++++++++++++++--- dts/arm/atmel/sam3x.dtsi | 9 + dts/arm/atmel/sam4s.dtsi | 8 + dts/arm/atmel/same70.dtsi | 9 + dts/bindings/watchdog/atmel,sam-watchdog.yaml | 44 ++++ soc/arm/atmel_sam/sam3x/dts_fixup.h | 5 + soc/arm/atmel_sam/sam4s/dts_fixup.h | 4 + soc/arm/atmel_sam/same70/dts_fixup.h | 5 + 16 files changed, 294 insertions(+), 29 deletions(-) create mode 100644 dts/bindings/watchdog/atmel,sam-watchdog.yaml diff --git a/boards/arm/arduino_due/arduino_due.dts b/boards/arm/arduino_due/arduino_due.dts index 3dccb75a8af..946da58b030 100644 --- a/boards/arm/arduino_due/arduino_due.dts +++ b/boards/arm/arduino_due/arduino_due.dts @@ -7,6 +7,7 @@ compatible = "arduino,due", "atmel,sam3x8e", "atmel,sam3x"; aliases { + wdog = &wdog; uart-0 = &uart0; i2c-0 = &i2c0; i2c-1 = &i2c1; @@ -19,6 +20,11 @@ }; }; +&wdog { + status = "ok"; +}; + + &i2c0 { status = "ok"; }; diff --git a/boards/arm/arduino_due/arduino_due.yaml b/boards/arm/arduino_due/arduino_due.yaml index ceb80a1f4ed..8daadb6d924 100644 --- a/boards/arm/arduino_due/arduino_due.yaml +++ b/boards/arm/arduino_due/arduino_due.yaml @@ -7,3 +7,5 @@ flash: 512 toolchain: - zephyr - gnuarmemb +supported: + - watchdog diff --git a/boards/arm/arduino_due/doc/board.rst b/boards/arm/arduino_due/doc/board.rst index bb5387dcfe6..ba47ca85f9b 100644 --- a/boards/arm/arduino_due/doc/board.rst +++ b/boards/arm/arduino_due/doc/board.rst @@ -49,6 +49,8 @@ The arduino_due board configuration supports the following hardware features: +-----------+------------+----------------------+ | I2C | on-chip | i2c | +-----------+------------+----------------------+ +| Watchdog | on-chip | watchdog | ++-----------+------------+----------------------+ Other hardware features are not currently supported by the Zephyr kernel. See `Arduino Due website`_ and `Atmel SAM3X8E Datasheet`_ for a complete diff --git a/boards/arm/sam4s_xplained/doc/sam4s_xplained.rst b/boards/arm/sam4s_xplained/doc/sam4s_xplained.rst index 238fcd2a8a8..c73303481f4 100644 --- a/boards/arm/sam4s_xplained/doc/sam4s_xplained.rst +++ b/boards/arm/sam4s_xplained/doc/sam4s_xplained.rst @@ -47,6 +47,8 @@ features: +-----------+------------+-------------------------------------+ | GPIO | on-chip | gpio | +-----------+------------+-------------------------------------+ +| Watchdog | on-chip | watchdog | ++-----------+------------+-------------------------------------+ Other hardware features are not currently supported by Zephyr. diff --git a/boards/arm/sam4s_xplained/sam4s_xplained.dts b/boards/arm/sam4s_xplained/sam4s_xplained.dts index 10ee9bb98cf..b36bdcffa43 100644 --- a/boards/arm/sam4s_xplained/sam4s_xplained.dts +++ b/boards/arm/sam4s_xplained/sam4s_xplained.dts @@ -17,6 +17,7 @@ led0 = &yellow_led_1; led1 = &yellow_led_2; sw0 = &user_button; + wdog = &wdog; }; chosen { @@ -60,3 +61,7 @@ current-speed = <115200>; status = "ok"; }; + +&wdog { + status = "ok"; +}; diff --git a/boards/arm/sam4s_xplained/sam4s_xplained.yaml b/boards/arm/sam4s_xplained/sam4s_xplained.yaml index f216bfc1f12..db988dca81a 100644 --- a/boards/arm/sam4s_xplained/sam4s_xplained.yaml +++ b/boards/arm/sam4s_xplained/sam4s_xplained.yaml @@ -7,3 +7,4 @@ toolchain: - gnuarmemb supported: - gpio + - watchdog diff --git a/boards/arm/sam_e70_xplained/sam_e70_xplained.dts b/boards/arm/sam_e70_xplained/sam_e70_xplained.dts index 0e5f5e54a6a..2535710d251 100644 --- a/boards/arm/sam_e70_xplained/sam_e70_xplained.dts +++ b/boards/arm/sam_e70_xplained/sam_e70_xplained.dts @@ -19,6 +19,7 @@ i2c-2 = &i2c2; led0 = &green_led; sw0 = &sw0_user_button; + wdog = &wdog; }; chosen { @@ -74,6 +75,10 @@ status = "ok"; }; +&wdog { + status = "ok"; +}; + &flash0 { /* * If chosen's zephyr,code-partition diff --git a/boards/arm/sam_e70_xplained/sam_e70_xplained.yaml b/boards/arm/sam_e70_xplained/sam_e70_xplained.yaml index 65dca15791d..523f6e002eb 100644 --- a/boards/arm/sam_e70_xplained/sam_e70_xplained.yaml +++ b/boards/arm/sam_e70_xplained/sam_e70_xplained.yaml @@ -10,3 +10,4 @@ supported: - adc - gpio - spi + - watchdog diff --git a/drivers/watchdog/wdt_sam.c b/drivers/watchdog/wdt_sam.c index 652da097282..c0e749400c2 100644 --- a/drivers/watchdog/wdt_sam.c +++ b/drivers/watchdog/wdt_sam.c @@ -10,9 +10,11 @@ * Note: * - Once the watchdog disable bit is set, it cannot be cleared till next * power reset, i.e, the watchdog cannot be started once stopped. - * - Since the MCU boots with WDT enabled, only basic feature (disable) is - * currently implemented for systems that don't need watchdog functionality. - * + * - Since the MCU boots with WDT enabled,the CONFIG_WDT_SAM_DISABLE_AT_BOOT + * is set default at boot and watchdog module is disabled in the MCU for + * systems that don't need watchdog functionality. + * - If the application needs to use the watchdog in the system, then + * CONFIG_WDT_SAM_DISABLE_AT_BOOT must be unset in the app's config file */ #include @@ -22,76 +24,231 @@ #include LOG_MODULE_REGISTER(wdt_sam); +#define SAM_PRESCALAR 128 +#define WDT_MAX_VALUE 4095 + /* Device constant configuration parameters */ struct wdt_sam_dev_cfg { Wdt *regs; }; +static struct device DEVICE_NAME_GET(wdt_sam); + +struct wdt_sam_dev_data { + wdt_callback_t cb; + u32_t mode; + bool timeout_valid; + bool mode_set; +}; + +static struct wdt_sam_dev_data wdt_sam_data = { 0 }; + #define DEV_CFG(dev) \ ((const struct wdt_sam_dev_cfg *const)(dev)->config->config_info) -static void wdt_sam_enable(struct device *dev) +static void wdt_sam_isr(struct device *dev) { - ARG_UNUSED(dev); + u32_t wdt_sr; + Wdt *const wdt = DEV_CFG(dev)->regs; + struct wdt_sam_dev_data *data = dev->driver_data; - LOG_ERR("Function not implemented!"); + /* Clear status bit to acknowledge interrupt by dummy read. */ + wdt_sr = wdt->WDT_SR; + + data->cb(dev, 0); +} + +/** + * @brief Calculates the watchdog counter value (WDV) + * to be installed in the watchdog timer + * + * @param timeout Timeout value in milliseconds. + * @param slow clock on board in Hz. + */ +int wdt_sam_convert_timeout(u32_t timeout, u32_t sclk) +{ + u32_t max, min; + + timeout = timeout * 1000; + min = (SAM_PRESCALAR * 1000000) / sclk; + max = min * WDT_MAX_VALUE; + if ((timeout < min) || (timeout > max)) { + LOG_ERR("Invalid timeout value allowed range:" + "%d ms to %d ms", min / 1000, max / 1000); + return -EINVAL; + } + + return WDT_MR_WDV(timeout / min); } static int wdt_sam_disable(struct device *dev) { Wdt *const wdt = DEV_CFG(dev)->regs; + struct wdt_sam_dev_data *data = dev->driver_data; + /* since Watchdog mode register is 'write-once', we can't disable if + * someone has already set the mode register + */ + if (data->mode_set) { + return -EPERM; + } + + /* do we handle -EFAULT here */ + + /* Watchdog Mode register is 'write-once' only register. + * Once disabled, it cannot be enabled until the device is reset + */ wdt->WDT_MR |= WDT_MR_WDDIS; + data->mode_set = true; return 0; } -static int wdt_sam_set_config(struct device *dev, struct wdt_config *config) +static int wdt_sam_setup(struct device *dev, u8_t options) { - ARG_UNUSED(dev); - ARG_UNUSED(config); - LOG_ERR("Function not implemented!"); + Wdt *const wdt = DEV_CFG(dev)->regs; + struct wdt_sam_dev_data *data = dev->driver_data; - return -ENOTSUP; + if (!data->timeout_valid) { + LOG_ERR("No valid timeouts installed"); + return -EINVAL; + } + + /* since Watchdog mode register is 'write-once', we can't set if + * someone has already set the mode register + */ + if (data->mode_set) { + return -EPERM; + } + + if ((options & WDT_OPT_PAUSE_IN_SLEEP) == WDT_OPT_PAUSE_IN_SLEEP) { + data->mode |= WDT_MR_WDIDLEHLT; + } + + if ((options & WDT_OPT_PAUSE_HALTED_BY_DBG) == + WDT_OPT_PAUSE_HALTED_BY_DBG) { + data->mode |= WDT_MR_WDDBGHLT; + } + + wdt->WDT_MR = data->mode; + data->mode_set = true; + + return 0; } -static void wdt_sam_get_config(struct device *dev, struct wdt_config *config) +static int wdt_sam_install_timeout(struct device *dev, + const struct wdt_timeout_cfg *cfg) { - ARG_UNUSED(dev); - ARG_UNUSED(config); + u32_t wdt_mode = 0; + int timeout_value; - LOG_ERR("Function not implemented!"); + struct wdt_sam_dev_data *data = dev->driver_data; + + if (data->timeout_valid) { + LOG_ERR("No more timeouts can be installed"); + return -ENOMEM; + } + + if (cfg->window.min != 0) { + return -EINVAL; + } + + /* + * Convert time to cycles. SAM3X SoC doesn't supports window + * timeout config. So the api expects the timeout to be filled + * in the max field of the timeout config. + */ + timeout_value = wdt_sam_convert_timeout(cfg->window.max, + (u32_t) CHIP_FREQ_XTAL_32K); + + if (timeout_value < 0) { + return -EINVAL; + } + + switch (cfg->flags) { + case WDT_FLAG_RESET_SOC: + /*A Watchdog fault (underflow or error) activates all resets */ + wdt_mode = WDT_MR_WDRSTEN; /* WDT reset enable */ + break; + + case WDT_FLAG_RESET_NONE: + /* A Watchdog fault (underflow or error) asserts interrupt. */ + if (cfg->callback) { + wdt_mode = WDT_MR_WDFIEN; /* WDT fault interrupt. */ + data->cb = cfg->callback; + } else { + LOG_ERR("Invalid(NULL) ISR callback passed\n"); + return -EINVAL; + } + break; + + /* Processor only reset mode not available in same70 series */ +#ifdef WDT_MR_WDRPROC + case WDT_FLAG_RESET_CPU_CORE: + /*A Watchdog fault activates the processor reset*/ + LOG_DBG("Configuring reset CPU only mode\n"); + wdt_mode = WDT_MR_WDRSTEN | /* WDT reset enable */ + WDT_MR_WDRPROC; /* WDT reset processor only*/ + break; +#endif + default: + LOG_ERR("Unsupported watchdog config Flag\n"); + return -ENOTSUP; + } + + data->mode = wdt_mode | + WDT_MR_WDV(timeout_value) | + WDT_MR_WDD(timeout_value); + + data->timeout_valid = true; + + return 0; } -static void wdt_sam_reload(struct device *dev) +static int wdt_sam_feed(struct device *dev, int channel_id) { - ARG_UNUSED(dev); + /* + * On watchdog restart the Watchdog counter is immediately + * reloaded/feeded with the 12-bit watchdog counter + * value from WDT_MR and restarted + */ + Wdt *const wdt = DEV_CFG(dev)->regs; - LOG_ERR("Function not implemented!"); + wdt->WDT_CR |= WDT_CR_KEY_PASSWD | WDT_CR_WDRSTT; + + return 0; } static const struct wdt_driver_api wdt_sam_api = { - .enable = wdt_sam_enable, + .setup = wdt_sam_setup, .disable = wdt_sam_disable, - .get_config = wdt_sam_get_config, - .set_config = wdt_sam_set_config, - .reload = wdt_sam_reload + .install_timeout = wdt_sam_install_timeout, + .feed = wdt_sam_feed, }; +static const struct wdt_sam_dev_cfg wdt_sam_cfg = { + .regs = (Wdt *)CONFIG_WDT_SAM_BASE_ADDRESS, +}; + +static void wdt_sam_irq_config(void) +{ + IRQ_CONNECT(CONFIG_WDT_SAM_IRQ, + CONFIG_WDT_SAM_IRQ_PRIORITY, wdt_sam_isr, + DEVICE_GET(wdt_sam), 0); + irq_enable(CONFIG_WDT_SAM_IRQ); +} + static int wdt_sam_init(struct device *dev) { #ifdef CONFIG_WDT_SAM_DISABLE_AT_BOOT wdt_sam_disable(dev); #endif + + wdt_sam_irq_config(); return 0; } -static const struct wdt_sam_dev_cfg wdt_sam_config = { - .regs = WDT -}; - DEVICE_AND_API_INIT(wdt_sam, CONFIG_WDT_0_NAME, wdt_sam_init, - NULL, &wdt_sam_config, - PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, - &wdt_sam_api); + &wdt_sam_data, &wdt_sam_cfg, PRE_KERNEL_1, + CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &wdt_sam_api); diff --git a/dts/arm/atmel/sam3x.dtsi b/dts/arm/atmel/sam3x.dtsi index 67fff9aed59..e2e95a7844c 100644 --- a/dts/arm/atmel/sam3x.dtsi +++ b/dts/arm/atmel/sam3x.dtsi @@ -32,6 +32,15 @@ }; soc { + wdog: watchdog@400E1A50 { + compatible = "atmel,sam-watchdog"; + reg = <0x400E1A50 0xc>; + interrupts = <4 0>; + peripheral-id = <4>; + label = "WATCHDOG_0"; + status = "disabled"; + }; + i2c0: i2c@4008C000 { compatible = "atmel,sam-i2c-twi"; clock-frequency = ; diff --git a/dts/arm/atmel/sam4s.dtsi b/dts/arm/atmel/sam4s.dtsi index 7b30cb25cb7..634e01425a4 100644 --- a/dts/arm/atmel/sam4s.dtsi +++ b/dts/arm/atmel/sam4s.dtsi @@ -31,6 +31,14 @@ }; soc { + wdog: watchdog@400E1450 { + compatible = "atmel,sam-watchdog"; + reg = <0x400E1450 0xc>; + interrupts = <4 0>; + peripheral-id = <4>; + label = "WATCHDOG_0"; + status = "disabled"; + }; i2c0: i2c@40018000 { compatible = "atmel,sam-i2c-twi"; clock-frequency = ; diff --git a/dts/arm/atmel/same70.dtsi b/dts/arm/atmel/same70.dtsi index 39814755603..8a730409dc2 100644 --- a/dts/arm/atmel/same70.dtsi +++ b/dts/arm/atmel/same70.dtsi @@ -32,6 +32,15 @@ }; soc { + wdog: watchdog@400E1850 { + compatible = "atmel,sam-watchdog"; + reg = <0x400E1850 0xc>; + interrupts = <4 0>; + peripheral-id = <4>; + label = "WATCHDOG_0"; + status = "disabled"; + }; + i2c0: i2c@40018000 { compatible = "atmel,sam-i2c-twihs"; clock-frequency = ; diff --git a/dts/bindings/watchdog/atmel,sam-watchdog.yaml b/dts/bindings/watchdog/atmel,sam-watchdog.yaml new file mode 100644 index 00000000000..4ef68510a32 --- /dev/null +++ b/dts/bindings/watchdog/atmel,sam-watchdog.yaml @@ -0,0 +1,44 @@ +# +# Copyright (c) 2018, Atmel Corporation +# +# SPDX-License-Identifier: Apache-2.0 +# +--- +title: Atmel SAM watchdog driver +id: atmel,sam-watchdog +version: 0.1 + +description: > + This is a representation of the SAM0 watchdog + +properties: + compatible: + type: string + category: required + description: compatible strings + constraint: "atmel,sam-watchdog" + + reg: + type: int + description: mmio register space + generation: define + category: required + + label: + type: string + category: required + description: Human readable string describing the device (used by Zephyr for API name) + generation: define + + interrupts: + type: array + category: required + description: required interrupts + generation: define + + peripheral-id: + type: int + description: peripheral ID + generation: define + category: required +... diff --git a/soc/arm/atmel_sam/sam3x/dts_fixup.h b/soc/arm/atmel_sam/sam3x/dts_fixup.h index 8212a75d756..10929433636 100644 --- a/soc/arm/atmel_sam/sam3x/dts_fixup.h +++ b/soc/arm/atmel_sam/sam3x/dts_fixup.h @@ -35,4 +35,9 @@ #define CONFIG_USART_SAM_PORT_3_NAME ATMEL_SAM_USART_400A4000_LABEL #define CONFIG_USART_SAM_PORT_3_BAUD_RATE ATMEL_SAM_USART_400A4000_CURRENT_SPEED +#define CONFIG_WDT_SAM_IRQ ATMEL_SAM_WATCHDOG_400E1A50_IRQ_0 +#define CONFIG_WDT_SAM_IRQ_PRIORITY ATMEL_SAM_WATCHDOG_400E1A50_IRQ_0_PRIORITY +#define CONFIG_WDT_SAM_LABEL ATMEL_SAM_WATCHDOG_400E1A50_LABEL +#define CONFIG_WDT_SAM_BASE_ADDRESS ATMEL_SAM_WATCHDOG_400E1A50_BASE_ADDRESS + /* End of SoC Level DTS fixup file */ diff --git a/soc/arm/atmel_sam/sam4s/dts_fixup.h b/soc/arm/atmel_sam/sam4s/dts_fixup.h index 74962fd0369..98c219cc2c7 100644 --- a/soc/arm/atmel_sam/sam4s/dts_fixup.h +++ b/soc/arm/atmel_sam/sam4s/dts_fixup.h @@ -50,4 +50,8 @@ #define CONFIG_USART_SAM_PORT_1_NAME ATMEL_SAM_USART_40028000_LABEL #define CONFIG_USART_SAM_PORT_1_BAUD_RATE ATMEL_SAM_USART_40028000_CURRENT_SPEED +#define CONFIG_WDT_SAM_IRQ ATMEL_SAM_WATCHDOG_400E1450_IRQ_0 +#define CONFIG_WDT_SAM_IRQ_PRIORITY ATMEL_SAM_WATCHDOG_400E1450_IRQ_0_PRIORITY +#define CONFIG_WDT_SAM_LABEL ATMEL_SAM_WATCHDOG_400E1450_LABEL +#define CONFIG_WDT_SAM_BASE_ADDRESS ATMEL_SAM_WATCHDOG_400E1450_BASE_ADDRESS /* End of SoC Level DTS fixup file */ diff --git a/soc/arm/atmel_sam/same70/dts_fixup.h b/soc/arm/atmel_sam/same70/dts_fixup.h index 54313e01934..1659e6828b3 100644 --- a/soc/arm/atmel_sam/same70/dts_fixup.h +++ b/soc/arm/atmel_sam/same70/dts_fixup.h @@ -115,4 +115,9 @@ #define CONFIG_ADC_1_NAME ATMEL_SAM_AFEC_40064000_LABEL #define CONFIG_ADC_1_PERIPHERAL_ID ATMEL_SAM_AFEC_40064000_PERIPHERAL_ID +#define CONFIG_WDT_SAM_IRQ ATMEL_SAM_WATCHDOG_400E1850_IRQ_0 +#define CONFIG_WDT_SAM_IRQ_PRIORITY ATMEL_SAM_WATCHDOG_400E1850_IRQ_0_PRIORITY +#define CONFIG_WDT_SAM_LABEL ATMEL_SAM_WATCHDOG_400E1850_LABEL +#define CONFIG_WDT_SAM_BASE_ADDRESS ATMEL_SAM_WATCHDOG_400E1850_BASE_ADDRESS + /* End of SoC Level DTS fixup file */