blob: 1c3349d9a13deebed83454280dd16814b64e9455 [file] [log] [blame]
/*
* Copyright 2023 Cypress Semiconductor Corporation (an Infineon company) or
* an affiliate of Cypress Semiconductor Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT infineon_cat1_watchdog
#include "cy_wdt.h"
#include "cy_sysclk.h"
#include <zephyr/devicetree.h>
#include <zephyr/drivers/watchdog.h>
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(wdt_infineon_cat1, CONFIG_WDT_LOG_LEVEL);
#define IFX_CAT1_WDT_IS_IRQ_EN DT_NODE_HAS_PROP(DT_DRV_INST(0), interrupts)
typedef struct {
/* Minimum period in milliseconds that can be represented with this
* many ignored bits
*/
uint32_t min_period_ms;
/* Timeout threshold in milliseconds from which to round up to
* the minimum period
*/
uint32_t round_threshold_ms;
} wdt_ignore_bits_data_t;
#if defined(CY_IP_MXS40SRSS) || defined(CY_IP_MXS40SSRSS)
#define ifx_wdt_lock() Cy_WDT_Lock()
#define ifx_wdt_unlock() Cy_WDT_Unlock()
#else
#define ifx_wdt_lock()
#define ifx_wdt_unlock()
#endif
#if defined(SRSS_NUM_WDT_A_BITS)
#define IFX_WDT_MATCH_BITS (SRSS_NUM_WDT_A_BITS)
#elif defined(COMPONENT_CAT1A)
#if defined(CY_IP_MXS40SRSS) && (CY_IP_MXS40SRSS_VERSION < 2)
#define IFX_WDT_MATCH_BITS (16)
#endif
#elif defined(COMPONENT_CAT1B)
#define IFX_WDT_MATCH_BITS (16)
#else
#error Unhandled device type
#endif
/* match range: 0 -> 2^(IFX_WDT_MATCH_BITS - ignore_bits)
* ignore_bits range: 0 -> (IFX_WDT_MATCH_BITS - 4) (Bottom four bits cannot be ignored)
* WDT Reset Period (timeout_ms) = CLK_DURATION * (2 * 2^(IFX_WDT_MATCH_BITS - ignore_bits) + match)
* Max WDT Reset Period = 3 * (2^IFX_WDT_MATCH_BITS) * CLK_DURATION
*/
#if defined(CY_IP_MXS40SRSS)
/* ILO, PILO, BAK all run at 32768 Hz - Period is ~0.030518 ms */
#define IFX_WDT_MAX_TIMEOUT_MS 6000
#define IFX_WDT_MAX_IGNORE_BITS 12
/* ILO Frequency = 32768 Hz, ILO Period = 1 / 32768 Hz = .030518 ms */
static const wdt_ignore_bits_data_t ifx_wdt_ignore_data[] = {
{4000, 3001}, /* 0 bit(s): min period: 4000ms, max period: 6000ms, round up from 3001+ms */
{2000, 1501}, /* 1 bit(s): min period: 2000ms, max period: 3000ms, round up from 1501+ms */
{1000, 751}, /* 2 bit(s): min period: 1000ms, max period: 1500ms, round up from 751+ms */
{500, 376}, /* 3 bit(s): min period: 500ms, max period: 750ms, round up from 376+ms */
{250, 188}, /* 4 bit(s): min period: 250ms, max period: 375ms, round up from 188+ms */
{125, 94}, /* 5 bit(s): min period: 125ms, max period: 187ms, round up from 94+ms */
{63, 47}, /* 6 bit(s): min period: 63ms, max period: 93ms, round up from 47+ms */
{32, 24}, /* 7 bit(s): min period: 32ms, max period: 46ms, round up from 24+ms */
{16, 12}, /* 8 bit(s): min period: 16ms, max period: 23ms, round up from 12+ms */
{8, 6}, /* 9 bit(s): min period: 8ms, max period: 11ms, round up from 6+ms */
{4, 3}, /* 10 bit(s): min period: 4ms, max period: 5ms, round up from 3+ms */
{2, 2}, /* 11 bit(s): min period: 2ms, max period: 2ms, round up from 2+ms */
{1, 1}, /* 12 bit(s): min period: 1ms, max period: 1ms, round up from 1+ms */
};
#elif defined(CY_IP_MXS40SSRSS) && (IFX_WDT_MATCH_BITS == 22)
/* ILO Frequency = 32768 Hz, ILO Period = 1 / 32768 Hz = .030518 ms */
#define IFX_WDT_MAX_TIMEOUT_MS 384000
#define IFX_WDT_MAX_IGNORE_BITS (IFX_WDT_MATCH_BITS - 4)
static const wdt_ignore_bits_data_t ifx_wdt_ignore_data[] = {
/* 0 bit(s): min period: 256000ms, max period: 384000ms, round up from 192001+ms */
{256000, 192001},
/* 1 bit(s): min period: 128000ms, max period: 192000ms, round up from 96001+ms */
{128000, 96001},
/* 2 bit(s): min period: 64000ms, max period: 96000ms, round up from 48001+ms */
{64000, 48001},
/* 3 bit(s): min period: 32000ms, max period: 48000ms, round up from 24001+ms */
{32000, 24001},
/* 4 bit(s): min period: 16000ms, max period: 24000ms, round up from 12001+ms */
{16000, 12001},
/* 5 bit(s): min period:8000ms, max period: 12000ms, round up from 6001+ms */
{8000, 6001},
/* 6 bit(s): min period:4000ms, max period:6000ms, round up from 3001+ms */
{4000, 3001},
/* 7 bit(s): min period:2000ms, max period:3000ms, round up from 1501+ms */
{2000, 1501},
/* 8 bit(s): min period:1000ms, max period:1500ms, round up from 751+ms */
{1000, 751},
/* 9 bit(s): min period: 500ms, max period: 750ms, round up from 376+ms */
{500, 376},
/* 10 bit(s): min period: 250ms, max period: 375ms, round up from 188+ms */
{250, 188},
/* 11 bit(s): min period: 125ms, max period: 187ms, round up from 94+ms */
{125, 94},
/* 12 bit(s): min period: 63ms, max period: 93ms, round up from 47+ms */
{63, 47},
/* 13 bit(s): min period: 32ms, max period: 46ms, round up from 24+ms */
{32, 24},
/* 14 bit(s): min period: 16ms, max period: 23ms, round up from 12+ms */
{16, 12},
/* 15 bit(s): min period: 8ms, max period: 11ms, round up from 6+ms */
{8, 6},
/* 16 bit(s): min period: 4ms, max period: 5ms, round up from 3+ms */
{4, 3},
/* 17 bit(s): min period: 2ms, max period: 2ms, round up from 2+ms */
{2, 2},
/* 18 bit(s): min period: 1ms, max period: 1ms, round up from 1+ms */
{1, 1},
};
#elif defined(CY_IP_MXS40SSRSS) && (IFX_WDT_MATCH_BITS == 32)
/* ILO Frequency = 32768 Hz, ILO Period = 1 / 32768 Hz = .030518 ms */
#define IFX_WDT_MAX_TIMEOUT_MS 393211435
#define IFX_WDT_MAX_IGNORE_BITS (IFX_WDT_MATCH_BITS - 4)
static const wdt_ignore_bits_data_t ifx_wdt_ignore_data[] = {
/* 0 bit(s): min period: 262147000ms, max period: 393221000ms, round up from 196610001+ms */
{262147000, 196610001},
/* 1 bit(s): min period: 131073000ms, max period: 196610000ms, round up from 98305001+ms */
{131073000, 98305001},
/* 2 bit(s): min period: 65537000ms, max period: 98305000ms, round up from 49152001+ms */
{65537000, 49152001},
/* 3 bit(s): min period: 32768000ms, max period: 49152000ms, round up from 24576001+ms */
{32768000, 24576001},
/* 4 bit(s): min period: 16384000ms, max period: 24576000ms, round up from 12288001+ms */
{16384000, 12288001},
/* 5 bit(s): min period: 8192000ms, max period: 12288000ms, round up from 6144001+ms */
{8192000, 6144001},
/* 6 bit(s): min period: 4096000ms, max period: 6144000ms, round up from 3072001+ms */
{4096000, 3072001},
/* 7 bit(s): min period: 2048000ms, max period: 3072000ms, round up from 1536001+ms */
{2048000, 1536001},
/* 8 bit(s): min period: 1024000ms, max period: 1536000ms, round up from 768001+ms */
{1024000, 768001},
/* 9 bit(s): min period: 512000ms, max period: 768000ms, round up from 384001+ms */
{512000, 384001},
/* 10 bit(s): min period: 256000ms, max period: 384000ms, round up from 192001+ms */
{256000, 192001},
/* 11 bit(s): min period: 128000ms, max period: 192000ms, round up from 96001+ms */
{128000, 96001},
/* 12 bit(s): min period: 64000ms, max period: 96000ms, round up from 48001+ms */
{64000, 48001},
/* 13 bit(s): min period: 32000ms, max period: 48000ms, round up from 24001+ms */
{32000, 24001},
/* 14 bit(s): min period: 16000ms, max period: 24000ms, round up from 12001+ms */
{16000, 12001},
/* 15 bit(s): min period: 8000ms, max period: 12000ms, round up from 6001+ms */
{8000, 6001},
/* 16 bit(s): min period: 4000ms, max period: 6000ms, round up from 3001+ms */
{4000, 3001},
/* 17 bit(s): min period: 2000ms, max period: 3000ms, round up from 1501+ms */
{2000, 1501},
/* 18 bit(s): min period: 1000ms, max period: 1500ms, round up from 751+ms */
{1000, 751},
/* 19 bit(s): min period: 500ms, max period: 750ms, round up from 376+ms */
{500, 376},
/* 20 bit(s): min period: 250ms, max period: 375ms, round up from 188+ms */
{250, 188},
/* 21 bit(s): min period: 125ms, max period: 187ms, round up from 94+ms */
{125, 94},
/* 22 bit(s): min period: 63ms, max period: 93ms, round up from 47+ms */
{63, 47},
/* 23 bit(s): min period: 32ms, max period: 46ms, round up from 24+ms */
{32, 24},
/* 24 bit(s): min period: 16ms, max period: 23ms, round up from 12+ms */
{16, 12},
/* 25 bit(s): min period: 8ms, max period: 11ms, round up from 6+ms */
{8, 6},
/* 26 bit(s): min period: 4ms, max period: 5ms, round up from 3+ms */
{4, 3},
/* 27 bit(s): min period: 2ms, max period: 2ms, round up from 2+ms */
{2, 2},
/* 28 bit(s): min period: 1ms, max period: 1ms, round up from 1+ms */
{1, 1},
};
#endif
struct ifx_cat1_wdt_data {
bool wdt_initialized;
uint32_t wdt_initial_timeout_ms;
uint32_t wdt_rounded_timeout_ms;
uint32_t wdt_ignore_bits;
#ifdef IFX_CAT1_WDT_IS_IRQ_EN
wdt_callback_t callback;
#endif /* IFX_CAT1_WDT_IS_IRQ_EN */
uint32_t timeout;
bool timeout_installed;
};
static struct ifx_cat1_wdt_data wdt_data;
#define IFX_DETERMINE_MATCH_BITS(bits) ((IFX_WDT_MAX_IGNORE_BITS) - (bits))
#define IFX_GET_COUNT_FROM_MATCH_BITS(bits) (2UL << IFX_DETERMINE_MATCH_BITS(bits))
__STATIC_INLINE uint32_t ifx_wdt_timeout_to_match(uint32_t timeout_ms, uint32_t ignore_bits)
{
uint32_t wrap_count_for_ignore_bits = (IFX_GET_COUNT_FROM_MATCH_BITS(ignore_bits));
uint32_t timeout_count = ((timeout_ms * CY_SYSCLK_ILO_FREQ) / 1000UL);
/* handle multiple possible wraps of WDT counter */
timeout_count = ((timeout_count + Cy_WDT_GetCount()) % wrap_count_for_ignore_bits);
return timeout_count;
}
/* Rounds up *timeout_ms if it's outside of the valid timeout range (ifx_wdt_ignore_data) */
__STATIC_INLINE uint32_t ifx_wdt_timeout_to_ignore_bits(uint32_t *timeout_ms)
{
for (uint32_t i = 0; i <= IFX_WDT_MAX_IGNORE_BITS; i++) {
if (*timeout_ms >= ifx_wdt_ignore_data[i].round_threshold_ms) {
if (*timeout_ms < ifx_wdt_ignore_data[i].min_period_ms) {
*timeout_ms = ifx_wdt_ignore_data[i].min_period_ms;
}
return i;
}
}
return IFX_WDT_MAX_IGNORE_BITS; /* Ideally should never reach this */
}
#ifdef IFX_CAT1_WDT_IS_IRQ_EN
static void ifx_cat1_wdt_isr_handler(const struct device *dev)
{
struct ifx_cat1_wdt_data *dev_data = dev->data;
if (dev_data->callback) {
dev_data->callback(dev, 0);
}
Cy_WDT_MaskInterrupt();
}
#endif /* IFX_CAT1_WDT_IS_IRQ_EN */
static int ifx_cat1_wdt_setup(const struct device *dev, uint8_t options)
{
struct ifx_cat1_wdt_data *dev_data = dev->data;
/* Initialize the WDT */
if ((dev_data->timeout == 0) || (dev_data->timeout > IFX_WDT_MAX_TIMEOUT_MS)) {
return -ENOTSUP;
}
if (dev_data->wdt_initialized) {
return -EBUSY;
}
/* Unlock and disable before doing other work */
ifx_wdt_unlock();
Cy_WDT_Disable();
Cy_WDT_ClearInterrupt();
Cy_WDT_MaskInterrupt();
dev_data->wdt_initial_timeout_ms = dev_data->timeout;
#if defined(CY_IP_MXS40SRSS) && (CY_IP_MXS40SRSS_VERSION >= 2)
Cy_WDT_SetUpperLimit(ifx_wdt_timeout_to_match(dev_data->wdt_initial_timeout_ms));
Cy_WDT_SetUpperAction(CY_WDT_LOW_UPPER_LIMIT_ACTION_RESET);
#else
dev_data->wdt_ignore_bits = ifx_wdt_timeout_to_ignore_bits(&dev_data->timeout);
dev_data->wdt_rounded_timeout_ms = dev_data->timeout;
#if defined(SRSS_NUM_WDT_A_BITS) && (SRSS_NUM_WDT_A_BITS == 22)
/* Cy_WDT_SetMatchBits configures the bit position above which the bits will be ignored for
* match, while ifx_wdt_timeout_to_ignore_bits returns number of timer MSB to ignore, so
* conversion is needed.
*/
Cy_WDT_SetMatchBits(IFX_DETERMINE_MATCH_BITS(dev_data->wdt_ignore_bits));
#else
Cy_WDT_SetIgnoreBits(dev_data->wdt_ignore_bits);
#endif
#if defined(COMPONENT_CAT1) && (CY_WDT_DRV_VERSION_MAJOR > 1) && (CY_WDT_DRV_VERSION_MINOR > 6)
/* Reset counter every time - large current counts in WDT can cause problems on some boards
*/
Cy_WDT_ResetCounter();
/* Twice, as reading back after 1 reset gives same value as before single reset */
Cy_WDT_ResetCounter();
#endif
Cy_WDT_SetMatch(ifx_wdt_timeout_to_match(dev_data->wdt_rounded_timeout_ms,
dev_data->wdt_ignore_bits));
#endif
Cy_WDT_Enable();
ifx_wdt_lock();
dev_data->wdt_initialized = true;
if (!Cy_WDT_IsEnabled()) {
return -ENOMSG;
}
#ifdef IFX_CAT1_WDT_IS_IRQ_EN
if (dev_data->callback) {
Cy_WDT_UnmaskInterrupt();
irq_enable(DT_INST_IRQN(0));
}
#endif /* IFX_CAT1_WDT_IS_IRQ_EN */
return 0;
}
static int ifx_cat1_wdt_disable(const struct device *dev)
{
struct ifx_cat1_wdt_data *dev_data = dev->data;
#ifdef IFX_CAT1_WDT_IS_IRQ_EN
Cy_WDT_MaskInterrupt();
irq_disable(DT_INST_IRQN(0));
#endif /* IFX_CAT1_WDT_IS_IRQ_EN */
ifx_wdt_unlock();
Cy_WDT_Disable();
ifx_wdt_lock();
dev_data->wdt_initialized = false;
return 0;
}
static int ifx_cat1_wdt_install_timeout(const struct device *dev, const struct wdt_timeout_cfg *cfg)
{
struct ifx_cat1_wdt_data *dev_data = dev->data;
if (dev_data->timeout_installed) {
LOG_ERR("No more timeouts can be installed");
return -ENOMEM;
}
if (cfg->flags) {
LOG_WRN("Watchdog behavior is not configurable.");
}
if (cfg->callback) {
#ifndef IFX_CAT1_WDT_IS_IRQ_EN
LOG_WRN("Interrupt is not configured, can't set a callback.");
#else
dev_data->callback = cfg->callback;
#endif /* IFX_CAT1_WDT_IS_IRQ_EN */
}
/* window watchdog not supported */
if (cfg->window.min != 0U || cfg->window.max == 0U) {
return -EINVAL;
}
dev_data->timeout = cfg->window.max;
return 0;
}
static int ifx_cat1_wdt_feed(const struct device *dev, int channel_id)
{
struct ifx_cat1_wdt_data *data = dev->data;
/* Only channel 0 is supported */
if (channel_id) {
return -EINVAL;
}
ifx_wdt_unlock();
Cy_WDT_ClearWatchdog(); /* Clear to prevent reset from WDT */
Cy_WDT_SetMatch(
ifx_wdt_timeout_to_match(data->wdt_rounded_timeout_ms, data->wdt_ignore_bits));
ifx_wdt_lock();
return 0;
}
static int ifx_cat1_wdt_init(const struct device *dev)
{
#ifdef IFX_CAT1_WDT_IS_IRQ_EN
/* Connect WDT interrupt to ISR */
IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority), ifx_cat1_wdt_isr_handler,
DEVICE_DT_INST_GET(0), 0);
#endif /* IFX_CAT1_WDT_IS_IRQ_EN */
return 0;
}
static DEVICE_API(wdt, ifx_cat1_wdt_api) = {
.setup = ifx_cat1_wdt_setup,
.disable = ifx_cat1_wdt_disable,
.install_timeout = ifx_cat1_wdt_install_timeout,
.feed = ifx_cat1_wdt_feed,
};
DEVICE_DT_INST_DEFINE(0, ifx_cat1_wdt_init, NULL, &wdt_data, NULL, POST_KERNEL,
CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &ifx_cat1_wdt_api);