• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 Bestechnic (Shanghai) Co., Ltd. All rights reserved.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 #include "plat_types.h"
16 #include "plat_addr_map.h"
17 #include "cmsis.h"
18 #include "cmsis_nvic.h"
19 #include "hal_timer.h"
20 #include "hal_trace.h"
21 #include "hal_uart.h"
22 #include "hal_wdt.h"
23 
24 #if defined(CHIP_BEST3001) || defined(CHIP_BEST3003) || defined(CHIP_BEST3005) || defined(CHIP_BEST1400) || defined(CHIP_BEST1402)
25 #define CLOCK_SYNC_WORKAROUND
26 #endif
27 
28 #define SLOW_TIMER_VAL_DELTA        1
29 #define FAST_TIMER_VAL_DELTA        20
30 
31 /* wdt controller */
32 /* reg address */
33 /* default timeout in seconds */
34 #define DEFAULT_TIMEOUT                     60
35 
36 #define WDT_RATE                            CONFIG_SYSTICK_HZ
37 
38 /* watchdog register offsets and masks */
39 #define WDTLOAD_REG_OFFSET                  0x000
40 #define WDTLOAD_LOAD_MIN                    0x00000001
41 #define WDTLOAD_LOAD_MAX                    0xFFFFFFFF
42 
43 #define WDTVALUE_REG_OFFSET                 0x004
44 
45 #define WDTCONTROL_REG_OFFSET               0x008
46 #define WDTCONTROL_REG_INT_ENABLE           (1 << 0)
47 #define WDTCONTROL_REG_RESET_ENABLE         (1 << 1)
48 
49 #define WDTINTCLR_REG_OFFSET                0x00C
50 
51 #define WDTRIS_REG_OFFSET                   0x010
52 #define WDTRIS_REG_INT_MASK                 (1 << 0)
53 
54 #define WDTMIS_REG_OFFSET                   0x014
55 #define WDTMIS_REG_INT_MASK                 (1 << 0)
56 
57 #define WDTLOCK_REG_OFFSET                  0xC00
58 #define WDTLOCK_REG_UNLOCK                  0x1ACCE551
59 #define WDTLOCK_REG_LOCK                    0x00000001
60 
61 /* read write */
62 #define wdtip_write32(v,b,a) \
63     (*((volatile unsigned int *)(b+a)) = v)
64 #define wdtip_read32(b,a) \
65     (*((volatile unsigned int *)(b+a)))
66 
67 #if 1
68 typedef void (*HAL_WDT_IRQ_HANDLER)(void);
69 
70 struct HAL_WDT_CTX {
71     unsigned int load_val;
72 };
73 
74 struct HAL_WDT_CTX hal_wdt_ctx[HAL_WDT_ID_NUM];
75 static void _wdt1_irq_handler(void);
76 HAL_WDT_IRQ_HANDLER hal_wdt_irq_handler[HAL_WDT_ID_NUM] =
77 {
78     _wdt1_irq_handler,
79 };
80 HAL_WDT_IRQ_CALLBACK hal_wdt_irq_callback[HAL_WDT_ID_NUM];
81 
_wdt1_irq_handler(void)82 static void _wdt1_irq_handler(void)
83 {
84     if (hal_wdt_irq_callback[HAL_WDT_ID_0] != 0) {
85         hal_wdt_irq_callback[HAL_WDT_ID_0](HAL_WDT_ID_0, HAL_WDT_EVENT_FIRE);
86     }
87 }
88 
_wdt_get_base(enum HAL_WDT_ID_T id)89 static unsigned int _wdt_get_base(enum HAL_WDT_ID_T id)
90 {
91     switch(id) {
92         default:
93         case HAL_WDT_ID_0:
94             return WDT_BASE;
95             break;
96     }
97 
98     return 0;
99 }
100 
_wdt_load(enum HAL_WDT_ID_T id)101 static void _wdt_load(enum HAL_WDT_ID_T id)
102 {
103     unsigned int reg_base = 0;
104     struct HAL_WDT_CTX *wdt = &hal_wdt_ctx[id];
105     uint32_t lock;
106 
107     reg_base = _wdt_get_base(id);
108 
109     lock = int_lock();
110 
111     wdtip_write32(WDTLOCK_REG_UNLOCK, reg_base, WDTLOCK_REG_OFFSET);
112 
113 #ifdef CLOCK_SYNC_WORKAROUND
114     uint32_t val;
115 
116     do {
117         wdtip_write32(wdt->load_val, reg_base, WDTLOAD_REG_OFFSET);
118         val = wdtip_read32(reg_base, WDTVALUE_REG_OFFSET);
119     } while ((wdt->load_val < val) || (wdt->load_val > val + SLOW_TIMER_VAL_DELTA));
120 #else
121     wdtip_write32(wdt->load_val, reg_base, WDTLOAD_REG_OFFSET);
122 #endif
123 
124     wdtip_write32(WDTLOCK_REG_LOCK, reg_base, WDTLOCK_REG_OFFSET);
125 
126     int_unlock(lock);
127 
128     /* Flush posted writes. */
129     wdtip_read32(reg_base, WDTLOCK_REG_OFFSET);
130 }
131 
_wdt_config(enum HAL_WDT_ID_T id)132 static void _wdt_config(enum HAL_WDT_ID_T id)
133 {
134     unsigned int reg_base = 0;
135     uint32_t lock;
136 
137     reg_base = _wdt_get_base(id);
138 
139     lock = int_lock();
140     wdtip_write32(WDTLOCK_REG_UNLOCK, reg_base, WDTLOCK_REG_OFFSET);
141     wdtip_write32(WDTRIS_REG_INT_MASK, reg_base, WDTINTCLR_REG_OFFSET);
142     wdtip_write32(WDTCONTROL_REG_INT_ENABLE | WDTCONTROL_REG_RESET_ENABLE, reg_base, WDTCONTROL_REG_OFFSET);
143     wdtip_write32(WDTLOCK_REG_LOCK, reg_base, WDTLOCK_REG_OFFSET);
144     int_unlock(lock);
145 
146     /* Flush posted writes. */
147     wdtip_read32(reg_base, WDTLOCK_REG_OFFSET);
148 }
149 
150 static int wdt_start;
151 
152 /* mandatory operations */
hal_wdt_start(enum HAL_WDT_ID_T id)153 int hal_wdt_start(enum HAL_WDT_ID_T id)
154 {
155     _wdt_load(id);
156     _wdt_config(id);
157     wdt_start = 1;
158     return 0;
159 }
hal_wdt_stop(enum HAL_WDT_ID_T id)160 int hal_wdt_stop(enum HAL_WDT_ID_T id)
161 {
162     unsigned int reg_base = 0;
163     uint32_t lock;
164 
165     reg_base = _wdt_get_base(id);
166 
167     lock = int_lock();
168     wdtip_write32(WDTLOCK_REG_UNLOCK, reg_base, WDTLOCK_REG_OFFSET);
169     wdtip_write32(0, reg_base, WDTCONTROL_REG_OFFSET);
170     wdtip_write32(WDTLOCK_REG_LOCK, reg_base, WDTLOCK_REG_OFFSET);
171     int_unlock(lock);
172 
173     /* Flush posted writes. */
174     wdtip_read32(reg_base, WDTLOCK_REG_OFFSET);
175 
176     wdt_start = 0;
177     return 0;
178 }
179 
180 /* optional operations */
hal_wdt_ping(enum HAL_WDT_ID_T id)181 int hal_wdt_ping(enum HAL_WDT_ID_T id)
182 {
183     if (wdt_start) {
184         _wdt_load(id);
185     }
186 
187     return 0;
188 }
hal_wdt_set_timeout(enum HAL_WDT_ID_T id,unsigned int timeout)189 int hal_wdt_set_timeout(enum HAL_WDT_ID_T id, unsigned int timeout)
190 {
191     uint64_t load;
192     struct HAL_WDT_CTX *wdt = &hal_wdt_ctx[id];
193 
194     /*
195      * sp805 runs counter with given value twice, after the end of first
196      * counter it gives an interrupt and then starts counter again. If
197      * interrupt already occurred then it resets the system. This is why
198      * load is half of what should be required.
199      */
200     load = WDT_RATE * timeout - 1;
201 
202     load = (load > WDTLOAD_LOAD_MAX) ? WDTLOAD_LOAD_MAX: load;
203     load = (load < WDTLOAD_LOAD_MIN) ? WDTLOAD_LOAD_MIN: load;
204 
205     wdt->load_val = load;
206 
207     return 0;
208 }
hal_wdt_get_timeleft(enum HAL_WDT_ID_T id)209 unsigned int hal_wdt_get_timeleft(enum HAL_WDT_ID_T id)
210 {
211     uint64_t load;
212     unsigned int reg_base = 0;
213     struct HAL_WDT_CTX *wdt = &hal_wdt_ctx[id];
214 
215     reg_base = _wdt_get_base(id);
216 
217     load = wdtip_read32(reg_base, WDTVALUE_REG_OFFSET);
218 
219     /*If the interrupt is inactive then time left is WDTValue + WDTLoad. */
220     if (!(wdtip_read32(reg_base, WDTRIS_REG_OFFSET) & WDTRIS_REG_INT_MASK))
221         load += wdt->load_val + 1;
222 
223     return load/WDT_RATE;
224 }
225 
hal_wdt_set_irq_callback(enum HAL_WDT_ID_T id,HAL_WDT_IRQ_CALLBACK cb)226 void hal_wdt_set_irq_callback(enum HAL_WDT_ID_T id, HAL_WDT_IRQ_CALLBACK cb)
227 {
228     switch(id) {
229         default:
230         case HAL_WDT_ID_0:
231             NVIC_SetVector(WDT_IRQn, (uint32_t)hal_wdt_irq_handler[id]);
232             NVIC_SetPriority(WDT_IRQn, IRQ_PRIORITY_REALTIME);
233             NVIC_ClearPendingIRQ(WDT_IRQn);
234             NVIC_EnableIRQ(WDT_IRQn);
235         break;
236     }
237 
238     hal_wdt_irq_callback[id] = cb;
239 }
240 
241 #endif
242