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