summaryrefslogtreecommitdiff
path: root/bsp/apm32/libraries/Drivers/drv_wdt.c
blob: dcd55d77132c2a97f1c0b24abbf6d177a17de878 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
/*
 * Copyright (c) 2006-2023, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author            Notes
 * 2022-03-04     stevetong459      first version
 * 2022-12-26     luobeihai         add APM32F0 series MCU support
 * 2023-03-28     luobeihai         add APM32E1/S1 series MCU support
 */

#include <board.h>
#include <sys/time.h>

#ifdef RT_USING_WDT

#define DBG_TAG             "drv.wdt"
#define DBG_LVL             DBG_INFO
#include <rtdbg.h>

#ifndef LSI_VALUE
    #define LSI_VALUE            ((uint32_t)40000)
#endif

#define DRV_WDT_TIME_OUT      0xFFFF

typedef struct
{
    struct rt_watchdog_device wdt;
    rt_uint32_t min_threshold;
    rt_uint32_t max_threshold;
    rt_uint32_t current_threshold;
} apm32_wdt_t;

static apm32_wdt_t wdt_config;

static rt_err_t apm32_iwdt_init(rt_watchdog_t *wdt)
{
    rt_uint32_t counter = 0;

    RCM_EnableLSI();

    while (!RCM_ReadStatusFlag(RCM_FLAG_LSIRDY))
    {
        if (++counter > DRV_WDT_TIME_OUT)
        {
            LOG_E("LSI clock open failed.");
            return -RT_ERROR;
        }
    }

    wdt_config.min_threshold = 1;
    wdt_config.max_threshold = (0xfff << 8) / LSI_VALUE;
    LOG_I("threshold section [%u, %d]", \
          wdt_config.min_threshold,
          wdt_config.max_threshold);

#if defined(SOC_SERIES_APM32F0)
    while (IWDT_ReadStatusFlag(IWDT_FLAG_DIVU))
#elif defined(SOC_SERIES_APM32F1) || defined(SOC_SERIES_APM32E1) || defined(SOC_SERIES_APM32S1) \
    || defined(SOC_SERIES_APM32F4)
    while (IWDT_ReadStatusFlag(IWDT_FLAG_PSCU))
#endif
    {
        if (++counter > DRV_WDT_TIME_OUT)
        {
            LOG_E("watchdog prescaler init failed.");
            return -RT_ERROR;
        }
    }

    IWDT_EnableWriteAccess();

#if defined(SOC_SERIES_APM32F0)
    IWDT_ConfigDivider(IWDT_DIV_256);
#elif defined(SOC_SERIES_APM32F1) || defined(SOC_SERIES_APM32E1) || defined(SOC_SERIES_APM32S1) \
    || defined(SOC_SERIES_APM32F4)
    IWDT_ConfigDivider(IWDT_DIVIDER_256);
#endif

    IWDT_DisableWriteAccess();

    return RT_EOK;
}

/**
 * @brief    This function will control watchdog device.
 *
 * @param    wdt is a pointer to i2c config class.
 *
 * @return   RT_EOK indicates successful , other value indicates failed.
 */
static rt_err_t apm32_iwdt_control(rt_watchdog_t *wdt, int cmd, void *arg)
{
    volatile rt_uint32_t param, counter = 0;

    switch (cmd)
    {
    case RT_DEVICE_CTRL_WDT_KEEPALIVE:
        IWDT_Refresh();
        break;
    case RT_DEVICE_CTRL_WDT_SET_TIMEOUT:
        param = *(rt_uint32_t *) arg;
        if ((param > wdt_config.max_threshold) || \
                (param < wdt_config.min_threshold))
        {
            LOG_E("invalid param@%u.", param);
            return -RT_ERROR;
        }
        else
        {
            wdt_config.current_threshold = param;
        }
        while (IWDT_ReadStatusFlag(IWDT_FLAG_CNTU))
        {
            if (++counter > DRV_WDT_TIME_OUT)
            {
                LOG_E("Update watchdog reload value complete.");
                return -RT_ERROR;
            }
        }
        IWDT_Refresh();
        IWDT_EnableWriteAccess();
        IWDT_ConfigReload(param * LSI_VALUE >> 8);
        IWDT_DisableWriteAccess();
        break;
    case RT_DEVICE_CTRL_WDT_GET_TIMEOUT:
        *(rt_uint32_t *)arg = wdt_config.current_threshold;
        break;
    case RT_DEVICE_CTRL_WDT_START:
        IWDT_Enable();
        IWDT_Refresh();
        break;
    default:
        LOG_W("This command is not supported.");
        return -RT_ERROR;
    }

    return RT_EOK;
}

static struct rt_watchdog_ops apm32_wdt_ops =
{
    apm32_iwdt_init,
    apm32_iwdt_control,
};

static int rt_hw_wdt_init(void)
{
    wdt_config.wdt.ops = &apm32_wdt_ops;
    /* register watchdog device */
    if (rt_hw_watchdog_register(&wdt_config.wdt, "wdt", \
                                RT_DEVICE_FLAG_DEACTIVATE, RT_NULL) != RT_EOK)
    {
        LOG_E("wdt device register failed.");
        return -RT_ERROR;
    }
    LOG_D("wdt device register success.");

    return RT_EOK;
}
INIT_BOARD_EXPORT(rt_hw_wdt_init);

#endif