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 <subbu147@gmail.com>
Signed-off-by: Kumar Gala <kumar.gala@linaro.org>
This commit is contained in:
Subramanian Meenakshi Sundaram 2018-10-24 22:16:25 -07:00 committed by Maureen Helm
commit 4e1179bba5
16 changed files with 294 additions and 29 deletions

View file

@ -7,6 +7,7 @@
compatible = "arduino,due", "atmel,sam3x8e", "atmel,sam3x"; compatible = "arduino,due", "atmel,sam3x8e", "atmel,sam3x";
aliases { aliases {
wdog = &wdog;
uart-0 = &uart0; uart-0 = &uart0;
i2c-0 = &i2c0; i2c-0 = &i2c0;
i2c-1 = &i2c1; i2c-1 = &i2c1;
@ -19,6 +20,11 @@
}; };
}; };
&wdog {
status = "ok";
};
&i2c0 { &i2c0 {
status = "ok"; status = "ok";
}; };

View file

@ -7,3 +7,5 @@ flash: 512
toolchain: toolchain:
- zephyr - zephyr
- gnuarmemb - gnuarmemb
supported:
- watchdog

View file

@ -49,6 +49,8 @@ The arduino_due board configuration supports the following hardware features:
+-----------+------------+----------------------+ +-----------+------------+----------------------+
| I2C | on-chip | i2c | | I2C | on-chip | i2c |
+-----------+------------+----------------------+ +-----------+------------+----------------------+
| Watchdog | on-chip | watchdog |
+-----------+------------+----------------------+
Other hardware features are not currently supported by the Zephyr kernel. Other hardware features are not currently supported by the Zephyr kernel.
See `Arduino Due website`_ and `Atmel SAM3X8E Datasheet`_ for a complete See `Arduino Due website`_ and `Atmel SAM3X8E Datasheet`_ for a complete

View file

@ -47,6 +47,8 @@ features:
+-----------+------------+-------------------------------------+ +-----------+------------+-------------------------------------+
| GPIO | on-chip | gpio | | GPIO | on-chip | gpio |
+-----------+------------+-------------------------------------+ +-----------+------------+-------------------------------------+
| Watchdog | on-chip | watchdog |
+-----------+------------+-------------------------------------+
Other hardware features are not currently supported by Zephyr. Other hardware features are not currently supported by Zephyr.

View file

@ -17,6 +17,7 @@
led0 = &yellow_led_1; led0 = &yellow_led_1;
led1 = &yellow_led_2; led1 = &yellow_led_2;
sw0 = &user_button; sw0 = &user_button;
wdog = &wdog;
}; };
chosen { chosen {
@ -60,3 +61,7 @@
current-speed = <115200>; current-speed = <115200>;
status = "ok"; status = "ok";
}; };
&wdog {
status = "ok";
};

View file

@ -7,3 +7,4 @@ toolchain:
- gnuarmemb - gnuarmemb
supported: supported:
- gpio - gpio
- watchdog

View file

@ -19,6 +19,7 @@
i2c-2 = &i2c2; i2c-2 = &i2c2;
led0 = &green_led; led0 = &green_led;
sw0 = &sw0_user_button; sw0 = &sw0_user_button;
wdog = &wdog;
}; };
chosen { chosen {
@ -74,6 +75,10 @@
status = "ok"; status = "ok";
}; };
&wdog {
status = "ok";
};
&flash0 { &flash0 {
/* /*
* If chosen's zephyr,code-partition * If chosen's zephyr,code-partition

View file

@ -10,3 +10,4 @@ supported:
- adc - adc
- gpio - gpio
- spi - spi
- watchdog

View file

@ -10,9 +10,11 @@
* Note: * Note:
* - Once the watchdog disable bit is set, it cannot be cleared till next * - Once the watchdog disable bit is set, it cannot be cleared till next
* power reset, i.e, the watchdog cannot be started once stopped. * power reset, i.e, the watchdog cannot be started once stopped.
* - Since the MCU boots with WDT enabled, only basic feature (disable) is * - Since the MCU boots with WDT enabled,the CONFIG_WDT_SAM_DISABLE_AT_BOOT
* currently implemented for systems that don't need watchdog functionality. * 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 <watchdog.h> #include <watchdog.h>
@ -22,76 +24,231 @@
#include <logging/log.h> #include <logging/log.h>
LOG_MODULE_REGISTER(wdt_sam); LOG_MODULE_REGISTER(wdt_sam);
#define SAM_PRESCALAR 128
#define WDT_MAX_VALUE 4095
/* Device constant configuration parameters */ /* Device constant configuration parameters */
struct wdt_sam_dev_cfg { struct wdt_sam_dev_cfg {
Wdt *regs; 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) \ #define DEV_CFG(dev) \
((const struct wdt_sam_dev_cfg *const)(dev)->config->config_info) ((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) static int wdt_sam_disable(struct device *dev)
{ {
Wdt *const wdt = DEV_CFG(dev)->regs; 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; wdt->WDT_MR |= WDT_MR_WDDIS;
data->mode_set = true;
return 0; 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;
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 int wdt_sam_install_timeout(struct device *dev,
const struct wdt_timeout_cfg *cfg)
{
u32_t wdt_mode = 0;
int timeout_value;
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; return -ENOTSUP;
} }
static void wdt_sam_get_config(struct device *dev, struct wdt_config *config) data->mode = wdt_mode |
{ WDT_MR_WDV(timeout_value) |
ARG_UNUSED(dev); WDT_MR_WDD(timeout_value);
ARG_UNUSED(config);
LOG_ERR("Function not implemented!"); 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 = { static const struct wdt_driver_api wdt_sam_api = {
.enable = wdt_sam_enable, .setup = wdt_sam_setup,
.disable = wdt_sam_disable, .disable = wdt_sam_disable,
.get_config = wdt_sam_get_config, .install_timeout = wdt_sam_install_timeout,
.set_config = wdt_sam_set_config, .feed = wdt_sam_feed,
.reload = wdt_sam_reload
}; };
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) static int wdt_sam_init(struct device *dev)
{ {
#ifdef CONFIG_WDT_SAM_DISABLE_AT_BOOT #ifdef CONFIG_WDT_SAM_DISABLE_AT_BOOT
wdt_sam_disable(dev); wdt_sam_disable(dev);
#endif #endif
wdt_sam_irq_config();
return 0; 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, DEVICE_AND_API_INIT(wdt_sam, CONFIG_WDT_0_NAME, wdt_sam_init,
NULL, &wdt_sam_config, &wdt_sam_data, &wdt_sam_cfg, PRE_KERNEL_1,
PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &wdt_sam_api);
&wdt_sam_api);

View file

@ -32,6 +32,15 @@
}; };
soc { 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 { i2c0: i2c@4008C000 {
compatible = "atmel,sam-i2c-twi"; compatible = "atmel,sam-i2c-twi";
clock-frequency = <I2C_BITRATE_STANDARD>; clock-frequency = <I2C_BITRATE_STANDARD>;

View file

@ -31,6 +31,14 @@
}; };
soc { 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 { i2c0: i2c@40018000 {
compatible = "atmel,sam-i2c-twi"; compatible = "atmel,sam-i2c-twi";
clock-frequency = <I2C_BITRATE_STANDARD>; clock-frequency = <I2C_BITRATE_STANDARD>;

View file

@ -32,6 +32,15 @@
}; };
soc { 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 { i2c0: i2c@40018000 {
compatible = "atmel,sam-i2c-twihs"; compatible = "atmel,sam-i2c-twihs";
clock-frequency = <I2C_BITRATE_STANDARD>; clock-frequency = <I2C_BITRATE_STANDARD>;

View file

@ -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
...

View file

@ -35,4 +35,9 @@
#define CONFIG_USART_SAM_PORT_3_NAME ATMEL_SAM_USART_400A4000_LABEL #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_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 */ /* End of SoC Level DTS fixup file */

View file

@ -50,4 +50,8 @@
#define CONFIG_USART_SAM_PORT_1_NAME ATMEL_SAM_USART_40028000_LABEL #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_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 */ /* End of SoC Level DTS fixup file */

View file

@ -115,4 +115,9 @@
#define CONFIG_ADC_1_NAME ATMEL_SAM_AFEC_40064000_LABEL #define CONFIG_ADC_1_NAME ATMEL_SAM_AFEC_40064000_LABEL
#define CONFIG_ADC_1_PERIPHERAL_ID ATMEL_SAM_AFEC_40064000_PERIPHERAL_ID #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 */ /* End of SoC Level DTS fixup file */