1 /**
2   * Copyright (c) 2019 Fuzhou Rockchip Electronics Co., Ltd
3   *
4   * SPDX-License-Identifier: Apache-2.0
5   ******************************************************************************
6   * @file    drv_clock.c
7   * @version V0.1
8   * @brief   cru clock interface
9   *
10   * Change Logs:
11   * Date           Author          Notes
12   * 2019-07-11     Elaine.Zhang      first implementation
13   *
14   ******************************************************************************
15   */
16 
17 /** @addtogroup RKBSP_Driver_Reference
18 *  @{
19 */
20 
21 /** @addtogroup Clock
22  *  @{
23  */
24 
25 /** @defgroup Clock_How_To_Use How To Use
26  *  @{
27 
28 The Clock driver use to configure clock frequency, enable/disable clock output, clock reset, power on/off power domain,
29  it can be used in the following three scenarios:
30 
31 - **Configure clock frequency**:
32     - The device set clock rate by clk_set_rate(eCLOCK_Name clk_id, uint32_t rate);
33     - The device get clock rate by clk_get_rate(eCLOCK_Name clk_id);
34 
35 - **Enable/disable clock output**:
36     - The device get clock by get_clk_gate_from_id(int clk_id);
37     - The device set clock enable/disable by clk_enable(struct clk_gate *gate) or clk_disable(struct clk_gate *gate);
38 
39 - **Power on/off power domain**:
40     - The device get pd by get_pd_from_id(int pd_id);
41     - The device power on/off pd by pd_power(struct pd *power, int on);
42 
43  @} */
44 
45 #include <rtdevice.h>
46 #include <rtthread.h>
47 
48 #if defined(RT_USING_CRU)
49 
50 #include "hal_base.h"
51 #include "drv_clock.h"
52 
53 static const struct clk_init *g_clk_init = RT_NULL;
54 
55 static rt_slist_t clk_gate_list;
56 
57 static struct rt_mutex clk_lock;
58 static struct rt_mutex gate_lock;
59 #if defined(RT_USING_PMU)
60 static struct rt_mutex pd_lock;
61 static rt_slist_t pd_list;
62 #endif
63 
64 /********************* Public Function Definition ****************************/
65 
66 /** @defgroup CLOCK_Public_Functions Public Functions
67  *  @{
68  */
69 
70 /**
71  * @brief  clk set enable.
72  * @param  gate: get_clk_gate_from_id.
73  * @retval -RT_EINVAL: struct gate is invalid argument
74  * @retval -RT_ERROR: clk enable failed.
75  */
clk_enable(struct clk_gate * gate)76 rt_err_t clk_enable(struct clk_gate *gate)
77 {
78     rt_err_t error = RT_EOK;
79     HAL_Status ret;
80 
81     if (!gate)
82     {
83         return -RT_EINVAL;
84     }
85 
86     rt_mutex_take(&gate_lock, RT_WAITING_FOREVER);
87 
88     if (gate->enable_count == 0)
89     {
90         ret = HAL_CRU_ClkEnable(gate->gate_id);
91         if (ret != HAL_OK)
92             error = -RT_ERROR;
93     }
94     gate->enable_count++;
95 
96     rt_mutex_release(&gate_lock);
97 
98     return error;
99 }
100 
101 /**
102  * @brief  clk set disable.
103  * @param  gate: get_clk_gate_from_id.
104  * @retval -RT_EINVAL: struct gate is invalid argument
105  * @retval -RT_ERROR: clk disable failed.
106  */
clk_disable(struct clk_gate * gate)107 rt_err_t clk_disable(struct clk_gate *gate)
108 {
109     rt_err_t error = RT_EOK;
110     HAL_Status ret;
111 
112     if (!gate)
113     {
114         return -RT_EINVAL;
115     }
116 
117     rt_mutex_take(&gate_lock, RT_WAITING_FOREVER);
118 
119     if (gate->enable_count == 0)
120     {
121         rt_kprintf("It may be wrong to used, make enable first.(gate_id = %d)\n", __func__, gate->gate_id);
122         goto out;
123     }
124 
125     if (--gate->enable_count > 0)
126     {
127         goto out;
128     }
129     ret = HAL_CRU_ClkDisable(gate->gate_id);
130     if (ret != HAL_OK)
131         error = -RT_ERROR;
132 
133 out:
134     rt_mutex_release(&gate_lock);
135 
136     return error;
137 }
138 
139 /**
140  * @brief  clk is enabled.
141  * @param gate: get_clk_gate_from_id.
142  * @retval 0: clk is disabled
143  * @retval 1: clk is enabled
144  */
clk_is_enabled(struct clk_gate * gate)145 int clk_is_enabled(struct clk_gate *gate)
146 {
147     if (!gate)
148     {
149         return 0;
150     }
151 
152     return HAL_CRU_ClkIsEnabled(gate->gate_id);
153 }
154 
155 /**
156  * @brief  get clk gate by id.
157  * @param gate_id: clk gate id.
158  * @return struct of type clk_gate
159  */
get_clk_gate_from_id(int gate_id)160 struct clk_gate *get_clk_gate_from_id(int gate_id)
161 {
162     struct clk_gate *clk_gate;
163 
164     rt_mutex_take(&gate_lock, RT_WAITING_FOREVER);
165 
166     rt_slist_for_each_entry(clk_gate, &clk_gate_list, node)
167     {
168         if (clk_gate->gate_id == gate_id)
169         {
170             goto out;
171         }
172     }
173 
174     clk_gate = rt_calloc(1, sizeof(struct clk_gate));
175     clk_gate->gate_id = gate_id;
176     clk_gate->enable_count = 0;
177     rt_slist_insert(&clk_gate_list, &clk_gate->node);
178 
179 out:
180     clk_gate->ref_count++;
181     rt_mutex_release(&gate_lock);
182 
183     return clk_gate;
184 }
185 
186 /**
187  * @brief  put clk gate.
188  * @param gate: get_clk_gate_from_id.
189  */
put_clk_gate(struct clk_gate * gate)190 void put_clk_gate(struct clk_gate *gate)
191 {
192     if (!gate)
193         return;
194 
195     rt_mutex_take(&gate_lock, RT_WAITING_FOREVER);
196 
197     if (--gate->ref_count > 0)
198     {
199         goto out;
200     }
201     rt_slist_remove(&clk_gate_list, &gate->node);
202     rt_free(gate);
203 
204 out:
205     rt_mutex_release(&gate_lock);
206 }
207 
208 /**
209  * @brief  clk get rate.
210  * @param clk_id: clk id.
211  * @return the return value of HAL_CRU_ClkGetFreq, which returns the frequency value in unit hz.
212  */
clk_get_rate(eCLOCK_Name clk_id)213 uint32_t clk_get_rate(eCLOCK_Name clk_id)
214 {
215     uint32_t rate;
216 
217     rt_mutex_take(&clk_lock, RT_WAITING_FOREVER);
218 
219     rate = HAL_CRU_ClkGetFreq(clk_id);
220 
221     rt_mutex_release(&clk_lock);
222 
223     return rate;
224 }
225 
226 /**
227  * @brief  clk set rate.
228  * @param clk_id: clk id.
229  * @param rate: frequency value hz.
230  * @retval RT_EOK: clk set successful
231  * @retval HAL_OK: HAL_CRU_ClkSetFreq set frequency successfully
232  * @retval HAL_ERROR: HAL_CRU_ClkSetFreq set frequency failed
233  * @retval HAL_INVAL: HAL_CRU_ClkSetFreq set frequency unsupported
234  */
clk_set_rate(eCLOCK_Name clk_id,uint32_t rate)235 rt_err_t clk_set_rate(eCLOCK_Name clk_id, uint32_t rate)
236 {
237     rt_err_t error = RT_EOK;
238 
239     if (rate == clk_get_rate(clk_id))
240         return error;
241 
242     rt_mutex_take(&clk_lock, RT_WAITING_FOREVER);
243 
244     error = HAL_CRU_ClkSetFreq(clk_id, rate);
245 
246     rt_mutex_release(&clk_lock);
247 
248     return error;
249 }
250 
251 #if defined(RT_USING_PMU)
252 
253 /**
254  * @brief  pd power on.
255  * @param power: get_pd_from_id.
256  * @retval -RT_EINVAL: struct pd is invalid argument
257  * @retval -RT_ERROR: pd power on failed.
258  * @retval RT_EOK: pd power on success.
259  */
pd_on(struct pd * power)260 rt_err_t pd_on(struct pd *power)
261 {
262     rt_err_t error = RT_EOK;
263     HAL_Status ret;
264 
265     if (!power)
266     {
267         return -RT_EINVAL;
268     }
269 
270     rt_mutex_take(&pd_lock, RT_WAITING_FOREVER);
271 
272     if (power->enable_count == 0)
273     {
274         ret = HAL_PD_On(power->pd_id);
275         if (ret != HAL_OK)
276             error = -RT_ERROR;
277     }
278     power->enable_count++;
279     rt_mutex_release(&pd_lock);
280 
281     return error;
282 }
283 
284 /**
285  * @brief  pd power off.
286  * @param power: get_pd_from_id.
287  * @retval -RT_EINVAL: struct pd is invalid argument
288  * @retval -RT_ERROR: pd power off failed.
289  * @retval RT_EOK: pd power off success.
290  */
pd_off(struct pd * power)291 rt_err_t pd_off(struct pd *power)
292 {
293     rt_err_t error = RT_EOK;
294     HAL_Status ret;
295 
296     if (!power)
297     {
298         return -RT_EINVAL;
299     }
300 
301     rt_mutex_take(&pd_lock, RT_WAITING_FOREVER);
302 
303     if (--power->enable_count > 0)
304     {
305         goto out;
306     }
307     ret = HAL_PD_Off(power->pd_id);
308     if (ret != HAL_OK)
309         error = -RT_ERROR;
310 
311 out:
312     rt_mutex_release(&pd_lock);
313 
314     return error;
315 }
316 
317 /**
318  * @brief  get pd by id.
319  * @param pd_id: pd id.
320  * @return struct of type pd
321  */
get_pd_from_id(ePD_Id pd_id)322 struct pd *get_pd_from_id(ePD_Id pd_id)
323 {
324     struct pd *pd;
325 
326     if (!pd_id)
327         return NULL;
328 
329     rt_mutex_take(&pd_lock, RT_WAITING_FOREVER);
330 
331     rt_slist_for_each_entry(pd, &pd_list, node)
332     {
333         if (pd->pd_id == pd_id)
334         {
335             goto out;
336         }
337     }
338     pd = rt_calloc(1, sizeof(struct pd));
339     pd->pd_id = pd_id;
340     pd->enable_count = 0;
341     rt_slist_insert(&pd_list, &pd->node);
342 
343 out:
344     pd->ref_count++;
345     rt_mutex_release(&pd_lock);
346 
347     return pd;
348 }
349 
350 /**
351  * @brief  put pd.
352  * @param power: get_pd_from_id.
353  */
put_pd(struct pd * power)354 void put_pd(struct pd *power)
355 {
356     if (!power)
357         return;
358 
359     rt_mutex_take(&pd_lock, RT_WAITING_FOREVER);
360 
361     if (--power->ref_count > 0)
362     {
363         goto out;
364     }
365     rt_slist_remove(&pd_list, &power->node);
366     rt_free(power);
367 
368 out:
369     rt_mutex_release(&pd_lock);
370 }
371 #endif
372 
373 /**
374  * @brief  clock dev init.
375  * @return RT_EOK
376  */
clock_dev_init(void)377 int clock_dev_init(void)
378 {
379     if (rt_mutex_init(&(clk_lock), "clkLock", RT_IPC_FLAG_FIFO) != RT_EOK)
380     {
381         RT_ASSERT(0);
382     }
383     if (rt_mutex_init(&(gate_lock), "gateLock", RT_IPC_FLAG_FIFO) != RT_EOK)
384     {
385         RT_ASSERT(0);
386     }
387     rt_slist_init(&clk_gate_list);
388 #if defined(RT_USING_PMU)
389     if (rt_mutex_init(&(pd_lock), "pdLock", RT_IPC_FLAG_FIFO) != RT_EOK)
390     {
391         RT_ASSERT(0);
392     }
393     rt_slist_init(&pd_list);
394 #endif
395 
396     return RT_EOK;
397 }
398 INIT_BOARD_EXPORT(clock_dev_init);
399 
400 /**
401  * @brief  clock init frequency.
402  * @param clk_inits: some need init clks.
403  * @param clk_dump: if need printf clk get freq after setting.
404  */
clk_init(const struct clk_init * clk_inits,bool clk_dump)405 void clk_init(const struct clk_init *clk_inits, bool clk_dump)
406 {
407     const struct clk_init *clks = clk_inits;
408 
409     while (clks->name)
410     {
411         if (clks->init_rate)
412         {
413             HAL_CRU_ClkSetFreq(clks->clk_id, clks->init_rate);
414         }
415         if (clk_dump)
416             rt_kprintf("%s: %s = %d\n", __func__, clks->name, HAL_CRU_ClkGetFreq(clks->clk_id));
417         clks++;
418     }
419     g_clk_init = clk_inits;
420 }
421 
422 /**
423  * @brief  clock disable unused.
424  * @param clks_unused: disable some not needed clks.
425  */
clk_disable_unused(const struct clk_unused * clks_unused)426 void clk_disable_unused(const struct clk_unused *clks_unused)
427 {
428     const struct clk_unused *clks = clks_unused;
429 
430     while (clks->gate_val)
431     {
432         if (clks->is_pmucru)
433         {
434 #if defined(CRU_PMU_CLKGATE_CON0_OFFSET)
435             CRU->PMU_CLKGATE_CON[clks->gate_con] = clks->gate_val;
436 #endif
437         }
438         else
439         {
440             CRU->CRU_CLKGATE_CON[clks->gate_con] = clks->gate_val;
441         }
442         clks++;
443     }
444 }
445 
446 #if defined(RT_USING_CRU_DUMP)
447 
448 /**
449  * @brief  clock dump frequency, dump cru registers, used for debug.
450  */
clk_dump(void)451 static void clk_dump(void)
452 {
453     const struct clk_init *clks = g_clk_init;
454     int i;
455 
456     if (clks)
457     {
458         while (clks->name)
459         {
460             rt_kprintf("%s: %s[%x] = %d\n", __func__, clks->name, clks->clk_id,
461                        HAL_CRU_ClkGetFreq(clks->clk_id));
462             clks++;
463         }
464     }
465     for (i = 0; i < HAL_ARRAY_SIZE(CRU->CRU_CLKSEL_CON); i++)
466     {
467         rt_kprintf("%s: cru_sel_con[%d] = %lx\n", __func__, i, CRU->CRU_CLKSEL_CON[i]);
468     }
469     for (i = 0; i < HAL_ARRAY_SIZE(CRU->CRU_CLKGATE_CON); i++)
470     {
471         rt_kprintf("%s: cru_gate_con[%d] = %lx\n", __func__, i, CRU->CRU_CLKGATE_CON[i]);
472     }
473     for (i = 0; i < HAL_ARRAY_SIZE(CRU->CRU_SOFTRST_CON); i++)
474     {
475         rt_kprintf("%s: cru_softreset_con[%d] = %lx\n", __func__, i, CRU->CRU_SOFTRST_CON[i]);
476     }
477 }
478 
479 #ifdef RT_USING_FINSH
480 #include <finsh.h>
481 MSH_CMD_EXPORT(clk_dump, cru drive test. e.g: clk_dump);
482 #endif
483 #endif
484 
485 /** @} */
486 
487 #endif
488 
489 /** @} */
490 
491 /** @} */
492