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 */