1 /*
2  * Copyright (C) 2017-2019 Alibaba Group Holding Limited
3  */
4 
5 
6 /******************************************************************************
7  * @file     dw_timer.c
8  * @brief    CSI Source File for timer Driver
9  * @version  V1.0
10  * @date     02. June 2017
11  * @vendor   csky
12  * @name     dw-timer
13  * @ip_id    0x111000010
14  * @model    timer
15  * @tag      DRV_DW_TIMER_TAG
16  ******************************************************************************/
17 
18 #include <csi_config.h>
19 #include <drv_irq.h>
20 #include <drv_timer.h>
21 #include <dw_timer.h>
22 #include <soc.h>
23 #include <csi_core.h>
24 
25 #define ERR_TIMER(errno) (CSI_DRV_ERRNO_TIMER_BASE | errno)
26 
27 #define TIMER_NULL_PARAM_CHK(para)  HANDLE_PARAM_CHK(para, ERR_TIMER(DRV_ERROR_PARAMETER))
28 
29 typedef struct {
30 #ifdef CONFIG_LPM
31     uint8_t timer_power_status;
32     uint32_t timer_regs_saved[2];
33 #endif
34     uint8_t idx;
35     uint64_t base;
36     uint32_t irq;
37     timer_event_cb_t cb_event;
38     uint32_t timeout;                  ///< the set time (us)
39     uint32_t timeout_flag;
40 } dw_timer_priv_t;
41 
42 extern int32_t target_get_timer_count(void);
43 extern int32_t target_get_timer(int32_t idx, uint64_t *base, uint32_t *irq, void **handler);
44 
45 static dw_timer_priv_t timer_instance[CONFIG_TIMER_NUM];
46 /**
47   \brief      Make all the timers in the idle state.
48   \param[in]  pointer to timer register base
49 */
timer_deactive_control(dw_timer_reg_t * addr)50 static void timer_deactive_control(dw_timer_reg_t *addr)
51 {
52     /* stop the corresponding timer */
53     addr->TxControl &= ~DW_TIMER_TXCONTROL_ENABLE;
54     /* Disable interrupt. */
55     addr->TxControl |= DW_TIMER_TXCONTROL_INTMASK;
56 }
57 
dw_timer_irqhandler(int idx)58 void dw_timer_irqhandler(int idx)
59 {
60     dw_timer_priv_t *timer_priv = &timer_instance[idx];
61     timer_priv->timeout_flag = 1;
62 
63     dw_timer_reg_t *addr = (dw_timer_reg_t *)(timer_priv->base);
64 
65     addr->TxEOI;
66 
67     if (timer_priv->cb_event) {
68         return timer_priv->cb_event(idx, TIMER_EVENT_TIMEOUT);
69     }
70 
71 }
72 
73 #ifdef CONFIG_LPM
manage_clock(timer_handle_t handle,uint8_t enable)74 static void manage_clock(timer_handle_t handle, uint8_t enable)
75 {
76     if (handle == &timer_instance[0] || handle == &timer_instance[1]) {
77         drv_clock_manager_config(CLOCK_MANAGER_TIM, enable);
78     } else if (handle == &timer_instance[3] || handle == &timer_instance[2]) {
79         drv_clock_manager_config(CLOCK_MANAGER_TIM1, enable);
80     }
81 }
82 
do_prepare_sleep_action(timer_handle_t handle)83 static void do_prepare_sleep_action(timer_handle_t handle)
84 {
85     dw_timer_priv_t *timer_priv = (dw_timer_priv_t *)handle;
86     uint64_t *tbase = (uint64_t *)(timer_priv->base);
87     registers_save(timer_priv->timer_regs_saved, tbase, 1);
88     registers_save(&timer_priv->timer_regs_saved[1], tbase + 2, 1);
89 }
90 
do_wakeup_sleep_action(timer_handle_t handle)91 static void do_wakeup_sleep_action(timer_handle_t handle)
92 {
93     dw_timer_priv_t *timer_priv = (dw_timer_priv_t *)handle;
94     uint64_t *tbase = (uint64_t *)(timer_priv->base);
95     registers_restore(tbase, timer_priv->timer_regs_saved, 1);
96     registers_restore(tbase + 2, &timer_priv->timer_regs_saved[1], 1);
97 }
98 #endif
99 
100 /**
101   \brief       Initialize TIMER Interface. 1. Initializes the resources needed for the TIMER interface 2.registers event callback function
102   \param[in]   idx  instance timer index
103   \param[in]   cb_event  Pointer to \ref timer_event_cb_t
104   \return      pointer to timer instance
105 */
csi_timer_initialize(int32_t idx,timer_event_cb_t cb_event)106 timer_handle_t csi_timer_initialize(int32_t idx, timer_event_cb_t cb_event)
107 {
108     if (idx < 0 || idx >= CONFIG_TIMER_NUM) {
109         return NULL;
110     }
111 
112     uint64_t base = 0u;
113     uint32_t irq = 0u;
114     void *handler;
115 
116     int32_t real_idx = target_get_timer(idx, &base, &irq, &handler);
117 
118     if (real_idx != idx) {
119         return NULL;
120     }
121 
122     dw_timer_priv_t *timer_priv = &timer_instance[idx];
123     timer_priv->base = base;
124     timer_priv->irq  = irq;
125     timer_priv->idx = idx;
126 
127     dw_timer_reg_t *addr = (dw_timer_reg_t *)(timer_priv->base);
128     timer_priv->timeout = DW_TIMER_INIT_DEFAULT_VALUE;
129 
130 #ifdef CONFIG_LPM
131     csi_timer_power_control(timer_priv, DRV_POWER_FULL);
132 #endif
133 
134     timer_deactive_control(addr);
135     timer_priv->cb_event = cb_event;
136 
137     if (cb_event != NULL) {
138         drv_irq_register(timer_priv->irq, handler);
139         drv_irq_enable(timer_priv->irq);
140     }
141 
142     return (timer_handle_t)timer_priv;
143 }
144 
145 /**
146   \brief       De-initialize TIMER Interface. stops operation and releases the software resources used by the interface
147   \param[in]   handle timer handle to operate.
148   \return      error code
149 */
csi_timer_uninitialize(timer_handle_t handle)150 int32_t csi_timer_uninitialize(timer_handle_t handle)
151 {
152     TIMER_NULL_PARAM_CHK(handle);
153 
154     dw_timer_priv_t *timer_priv = (dw_timer_priv_t *)handle;
155     dw_timer_reg_t *addr = (dw_timer_reg_t *)(timer_priv->base);
156 
157     timer_deactive_control(addr);
158     timer_priv->cb_event = NULL;
159 
160     drv_irq_disable(timer_priv->irq);
161     drv_irq_unregister(timer_priv->irq);
162 
163 #ifdef CONFIG_LPM
164     csi_timer_power_control(timer_priv, DRV_POWER_OFF);
165 #endif
166 
167     return 0;
168 }
169 
csi_timer_power_control(timer_handle_t handle,csi_power_stat_e state)170 int32_t csi_timer_power_control(timer_handle_t handle, csi_power_stat_e state)
171 {
172     TIMER_NULL_PARAM_CHK(handle);
173 #ifdef CONFIG_LPM
174     power_cb_t callback = {
175         .wakeup = do_wakeup_sleep_action,
176         .sleep = do_prepare_sleep_action,
177         .manage_clock = manage_clock
178     };
179     return drv_soc_power_control(handle, state, &callback);
180 #else
181     return ERR_TIMER(DRV_ERROR_UNSUPPORTED);
182 #endif
183 }
184 
185 /**
186   \brief       config timer mode.
187   \param[in]   handle timer handle to operate.
188   \param[in]   mode      \ref timer_mode_e
189   \return      error code
190 */
csi_timer_config(timer_handle_t handle,timer_mode_e mode)191 int32_t csi_timer_config(timer_handle_t handle, timer_mode_e mode)
192 {
193     TIMER_NULL_PARAM_CHK(handle);
194 
195     dw_timer_priv_t *timer_priv = handle;
196     dw_timer_reg_t *addr = (dw_timer_reg_t *)(timer_priv->base);
197 
198     switch (mode) {
199         case TIMER_MODE_FREE_RUNNING:
200             addr->TxControl &= ~DW_TIMER_TXCONTROL_MODE;
201             break;
202 
203         case TIMER_MODE_RELOAD:
204             addr->TxControl |= DW_TIMER_TXCONTROL_MODE;
205             break;
206 
207         default:
208             return ERR_TIMER(DRV_ERROR_PARAMETER);
209     }
210 
211     addr->TxControl |= (DW_TIMER_TXCONTROL_TRIGGER);
212 
213     return 0;
214 }
215 
216 /**
217   \brief       Set timer.
218   \param[in]   instance  timer instance to operate.
219   \param[in]   timeout the timeout value in microseconds(us).
220   \return      error code
221 */
csi_timer_set_timeout(timer_handle_t handle,uint32_t timeout)222 int32_t csi_timer_set_timeout(timer_handle_t handle, uint32_t timeout)
223 {
224     TIMER_NULL_PARAM_CHK(handle);
225 
226     dw_timer_priv_t *timer_priv = handle;
227     timer_priv->timeout = timeout;
228     return 0;
229 }
230 
231 /**
232   \brief       Start timer.
233   \param[in]   handle timer handle to operate.
234   \return      error code
235 */
236 
csi_timer_start(timer_handle_t handle)237 int32_t csi_timer_start(timer_handle_t handle)
238 {
239     TIMER_NULL_PARAM_CHK(handle);
240 
241     dw_timer_priv_t *timer_priv = handle;
242 
243     timer_priv->timeout_flag = 0;
244 
245     uint32_t min_us = drv_get_timer_freq(timer_priv->idx) / 1000000;
246     uint32_t load;
247 
248     if (((timer_priv->timeout * drv_get_timer_freq(timer_priv->idx)) / 1000000) > 0xffffffff) {
249         return ERR_TIMER(DRV_ERROR_PARAMETER);
250     }
251 
252     if (min_us) {
253         load = (uint32_t)(timer_priv->timeout * min_us);
254     } else {
255         load = (uint32_t)(((uint64_t)(timer_priv->timeout) * drv_get_timer_freq(timer_priv->idx)) / 1000000);
256     }
257 
258     dw_timer_reg_t *addr = (dw_timer_reg_t *)(timer_priv->base);
259 
260     if (timer_priv->timeout == 0) {
261         addr->TxLoadCount = 0xffffffff;                           /* load time(us) */
262     } else {
263         if ((addr->TxControl | 0x2) == 0x2) {
264             addr->TxLoadCount = 0xffffffff;                           /* load time(us) */
265         } else {
266             addr->TxLoadCount = load;                           /* load time(us) */
267         }
268     }
269 
270 
271     addr->TxControl &= ~DW_TIMER_TXCONTROL_ENABLE;      /* disable the timer */
272     addr->TxControl |= DW_TIMER_TXCONTROL_ENABLE;       /* enable the corresponding timer */
273     addr->TxControl &= ~DW_TIMER_TXCONTROL_INTMASK;     /* enable interrupt */
274 
275     /* avoid timer bug on yunvoice soc */
276 #ifdef CONFIG_CHIP_YUNVOICE
277 
278     if (addr->TxCurrentValue > addr->TxLoadCount) {
279         addr->TxControl &= ~DW_TIMER_TXCONTROL_ENABLE;      /* disable the timer */
280         addr->TxControl |= DW_TIMER_TXCONTROL_ENABLE;       /* enable the corresponding timer */
281         addr->TxControl &= ~DW_TIMER_TXCONTROL_INTMASK;     /* enable interrupt */
282     }
283 
284 #endif
285 
286     return 0;
287 }
288 
289 /**
290   \brief       Stop timer.
291   \param[in]   handle timer handle to operate.
292   \return      error code
293 */
csi_timer_stop(timer_handle_t handle)294 int32_t csi_timer_stop(timer_handle_t handle)
295 {
296     TIMER_NULL_PARAM_CHK(handle);
297 
298     dw_timer_priv_t *timer_priv = handle;
299     dw_timer_reg_t *addr = (dw_timer_reg_t *)(timer_priv->base);
300 
301     addr->TxControl |= DW_TIMER_TXCONTROL_INTMASK;      /* enable interrupt */
302     addr->TxControl &= ~DW_TIMER_TXCONTROL_ENABLE;      /* disable the timer */
303 
304     return 0;
305 }
306 
307 /**
308   \brief       suspend timer.
309   \param[in]   instance  timer instance to operate.
310   \return      error code
311 */
csi_timer_suspend(timer_handle_t handle)312 int32_t csi_timer_suspend(timer_handle_t handle)
313 {
314     TIMER_NULL_PARAM_CHK(handle);
315 
316     return ERR_TIMER(DRV_ERROR_UNSUPPORTED);
317 }
318 
319 /**
320   \brief       resume timer.
321   \param[in]   handle timer handle to operate.
322   \return      error code
323 */
csi_timer_resume(timer_handle_t handle)324 int32_t csi_timer_resume(timer_handle_t handle)
325 {
326     TIMER_NULL_PARAM_CHK(handle);
327 
328     dw_timer_priv_t *timer_priv = handle;
329     dw_timer_reg_t *addr = (dw_timer_reg_t *)(timer_priv->base);
330 
331     addr->TxControl &= ~DW_TIMER_TXCONTROL_ENABLE;      /* stop the corresponding timer */
332     addr->TxControl &= DW_TIMER_TXCONTROL_ENABLE;       /* restart the corresponding timer */
333 
334     return 0;
335 }
336 
337 /**
338   \brief       get timer current value
339   \param[in]   handle timer handle to operate.
340   \param[out]   value     timer current value
341   \return      error code
342 */
csi_timer_get_current_value(timer_handle_t handle,uint32_t * value)343 int32_t csi_timer_get_current_value(timer_handle_t handle, uint32_t *value)
344 {
345     TIMER_NULL_PARAM_CHK(handle);
346     TIMER_NULL_PARAM_CHK(value);
347 
348     dw_timer_priv_t *timer_priv = handle;
349     dw_timer_reg_t *addr = (dw_timer_reg_t *)(timer_priv->base);
350 
351     *value = addr->TxCurrentValue;
352     return 0;
353 }
354 
355 /**
356   \brief       Get TIMER status.
357   \param[in]   handle timer handle to operate.
358   \return      TIMER status \ref timer_status_t
359 */
csi_timer_get_status(timer_handle_t handle)360 timer_status_t csi_timer_get_status(timer_handle_t handle)
361 {
362     timer_status_t timer_status = {0};
363 
364     if (handle == NULL) {
365         return timer_status;
366     }
367 
368     dw_timer_priv_t *timer_priv = handle;
369     dw_timer_reg_t *addr = (dw_timer_reg_t *)(timer_priv->base);
370 
371     if (addr->TxControl & DW_TIMER_TXCONTROL_ENABLE) {
372         timer_status.active = 1;
373     }
374 
375     if (timer_priv->timeout_flag == 1) {
376         timer_status.timeout = 1;
377     }
378 
379     return timer_status;
380 }
381 
382 /**
383   \brief       get timer reload value
384   \param[in]   handle timer handle to operate.
385   \param[out]   value    timer reload value
386   \return      error code
387 */
csi_timer_get_load_value(timer_handle_t handle,uint32_t * value)388 int32_t csi_timer_get_load_value(timer_handle_t handle, uint32_t *value)
389 {
390     TIMER_NULL_PARAM_CHK(handle);
391     TIMER_NULL_PARAM_CHK(value);
392 
393     dw_timer_priv_t *timer_priv = handle;
394     dw_timer_reg_t *addr = (dw_timer_reg_t *)(timer_priv->base);
395 
396     *value = addr->TxLoadCount;
397     return 0;
398 }
399 
400