| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491 |
- /**
- * Copyright (c) 2019 Fuzhou Rockchip Electronics Co., Ltd
- *
- * SPDX-License-Identifier: Apache-2.0
- ******************************************************************************
- * @file drv_clock.c
- * @version V0.1
- * @brief cru clock interface
- *
- * Change Logs:
- * Date Author Notes
- * 2019-07-11 Elaine.Zhang first implementation
- *
- ******************************************************************************
- */
- /** @addtogroup RKBSP_Driver_Reference
- * @{
- */
- /** @addtogroup Clock
- * @{
- */
- /** @defgroup Clock_How_To_Use How To Use
- * @{
- The Clock driver use to configure clock frequency, enable/disable clock output, clock reset, power on/off power domain,
- it can be used in the following three scenarios:
- - **Configure clock frequency**:
- - The device set clock rate by clk_set_rate(eCLOCK_Name clk_id, uint32_t rate);
- - The device get clock rate by clk_get_rate(eCLOCK_Name clk_id);
- - **Enable/disable clock output**:
- - The device get clock by get_clk_gate_from_id(int clk_id);
- - The device set clock enable/disable by clk_enable(struct clk_gate *gate) or clk_disable(struct clk_gate *gate);
- - **Power on/off power domain**:
- - The device get pd by get_pd_from_id(int pd_id);
- - The device power on/off pd by pd_power(struct pd *power, int on);
- @} */
- #include <rtdevice.h>
- #include <rtthread.h>
- #if defined(RT_USING_CRU)
- #include "hal_base.h"
- #include "drv_clock.h"
- static const struct clk_init *g_clk_init = RT_NULL;
- static rt_slist_t clk_gate_list;
- static struct rt_mutex clk_lock;
- static struct rt_mutex gate_lock;
- #if defined(RT_USING_PMU)
- static struct rt_mutex pd_lock;
- static rt_slist_t pd_list;
- #endif
- /********************* Public Function Definition ****************************/
- /** @defgroup CLOCK_Public_Functions Public Functions
- * @{
- */
- /**
- * @brief clk set enable.
- * @param gate: get_clk_gate_from_id.
- * @retval -RT_EINVAL: struct gate is invalid argument
- * @retval -RT_ERROR: clk enable failed.
- */
- rt_err_t clk_enable(struct clk_gate *gate)
- {
- rt_err_t error = RT_EOK;
- HAL_Status ret;
- if (!gate)
- {
- return -RT_EINVAL;
- }
- rt_mutex_take(&gate_lock, RT_WAITING_FOREVER);
- if (gate->enable_count == 0)
- {
- ret = HAL_CRU_ClkEnable(gate->gate_id);
- if (ret != HAL_OK)
- error = -RT_ERROR;
- }
- gate->enable_count++;
- rt_mutex_release(&gate_lock);
- return error;
- }
- /**
- * @brief clk set disable.
- * @param gate: get_clk_gate_from_id.
- * @retval -RT_EINVAL: struct gate is invalid argument
- * @retval -RT_ERROR: clk disable failed.
- */
- rt_err_t clk_disable(struct clk_gate *gate)
- {
- rt_err_t error = RT_EOK;
- HAL_Status ret;
- if (!gate)
- {
- return -RT_EINVAL;
- }
- rt_mutex_take(&gate_lock, RT_WAITING_FOREVER);
- if (gate->enable_count == 0)
- {
- rt_kprintf("It may be wrong to used, make enable first.(gate_id = %d)\n", __func__, gate->gate_id);
- goto out;
- }
- if (--gate->enable_count > 0)
- {
- goto out;
- }
- ret = HAL_CRU_ClkDisable(gate->gate_id);
- if (ret != HAL_OK)
- error = -RT_ERROR;
- out:
- rt_mutex_release(&gate_lock);
- return error;
- }
- /**
- * @brief clk is enabled.
- * @param gate: get_clk_gate_from_id.
- * @retval 0: clk is disabled
- * @retval 1: clk is enabled
- */
- int clk_is_enabled(struct clk_gate *gate)
- {
- if (!gate)
- {
- return 0;
- }
- return HAL_CRU_ClkIsEnabled(gate->gate_id);
- }
- /**
- * @brief get clk gate by id.
- * @param gate_id: clk gate id.
- * @return struct of type clk_gate
- */
- struct clk_gate *get_clk_gate_from_id(int gate_id)
- {
- struct clk_gate *clk_gate;
- rt_mutex_take(&gate_lock, RT_WAITING_FOREVER);
- rt_slist_for_each_entry(clk_gate, &clk_gate_list, node)
- {
- if (clk_gate->gate_id == gate_id)
- {
- goto out;
- }
- }
- clk_gate = rt_calloc(1, sizeof(struct clk_gate));
- clk_gate->gate_id = gate_id;
- clk_gate->enable_count = 0;
- rt_slist_insert(&clk_gate_list, &clk_gate->node);
- out:
- clk_gate->ref_count++;
- rt_mutex_release(&gate_lock);
- return clk_gate;
- }
- /**
- * @brief put clk gate.
- * @param gate: get_clk_gate_from_id.
- */
- void put_clk_gate(struct clk_gate *gate)
- {
- if (!gate)
- return;
- rt_mutex_take(&gate_lock, RT_WAITING_FOREVER);
- if (--gate->ref_count > 0)
- {
- goto out;
- }
- rt_slist_remove(&clk_gate_list, &gate->node);
- rt_free(gate);
- out:
- rt_mutex_release(&gate_lock);
- }
- /**
- * @brief clk get rate.
- * @param clk_id: clk id.
- * @return the return value of HAL_CRU_ClkGetFreq, which returns the frequency value in unit hz.
- */
- uint32_t clk_get_rate(eCLOCK_Name clk_id)
- {
- uint32_t rate;
- rt_mutex_take(&clk_lock, RT_WAITING_FOREVER);
- rate = HAL_CRU_ClkGetFreq(clk_id);
- rt_mutex_release(&clk_lock);
- return rate;
- }
- /**
- * @brief clk set rate.
- * @param clk_id: clk id.
- * @param rate: frequency value hz.
- * @retval RT_EOK: clk set successful
- * @retval HAL_OK: HAL_CRU_ClkSetFreq set frequency successfully
- * @retval HAL_ERROR: HAL_CRU_ClkSetFreq set frequency failed
- * @retval HAL_INVAL: HAL_CRU_ClkSetFreq set frequency unsupported
- */
- rt_err_t clk_set_rate(eCLOCK_Name clk_id, uint32_t rate)
- {
- rt_err_t error = RT_EOK;
- if (rate == clk_get_rate(clk_id))
- return error;
- rt_mutex_take(&clk_lock, RT_WAITING_FOREVER);
- error = HAL_CRU_ClkSetFreq(clk_id, rate);
- rt_mutex_release(&clk_lock);
- return error;
- }
- #if defined(RT_USING_PMU)
- /**
- * @brief pd power on.
- * @param power: get_pd_from_id.
- * @retval -RT_EINVAL: struct pd is invalid argument
- * @retval -RT_ERROR: pd power on failed.
- * @retval RT_EOK: pd power on success.
- */
- rt_err_t pd_on(struct pd *power)
- {
- rt_err_t error = RT_EOK;
- HAL_Status ret;
- if (!power)
- {
- return -RT_EINVAL;
- }
- rt_mutex_take(&pd_lock, RT_WAITING_FOREVER);
- if (power->enable_count == 0)
- {
- ret = HAL_PD_On(power->pd_id);
- if (ret != HAL_OK)
- error = -RT_ERROR;
- }
- power->enable_count++;
- rt_mutex_release(&pd_lock);
- return error;
- }
- /**
- * @brief pd power off.
- * @param power: get_pd_from_id.
- * @retval -RT_EINVAL: struct pd is invalid argument
- * @retval -RT_ERROR: pd power off failed.
- * @retval RT_EOK: pd power off success.
- */
- rt_err_t pd_off(struct pd *power)
- {
- rt_err_t error = RT_EOK;
- HAL_Status ret;
- if (!power)
- {
- return -RT_EINVAL;
- }
- rt_mutex_take(&pd_lock, RT_WAITING_FOREVER);
- if (--power->enable_count > 0)
- {
- goto out;
- }
- ret = HAL_PD_Off(power->pd_id);
- if (ret != HAL_OK)
- error = -RT_ERROR;
- out:
- rt_mutex_release(&pd_lock);
- return error;
- }
- /**
- * @brief get pd by id.
- * @param pd_id: pd id.
- * @return struct of type pd
- */
- struct pd *get_pd_from_id(ePD_Id pd_id)
- {
- struct pd *pd;
- if (!pd_id)
- return NULL;
- rt_mutex_take(&pd_lock, RT_WAITING_FOREVER);
- rt_slist_for_each_entry(pd, &pd_list, node)
- {
- if (pd->pd_id == pd_id)
- {
- goto out;
- }
- }
- pd = rt_calloc(1, sizeof(struct pd));
- pd->pd_id = pd_id;
- pd->enable_count = 0;
- rt_slist_insert(&pd_list, &pd->node);
- out:
- pd->ref_count++;
- rt_mutex_release(&pd_lock);
- return pd;
- }
- /**
- * @brief put pd.
- * @param power: get_pd_from_id.
- */
- void put_pd(struct pd *power)
- {
- if (!power)
- return;
- rt_mutex_take(&pd_lock, RT_WAITING_FOREVER);
- if (--power->ref_count > 0)
- {
- goto out;
- }
- rt_slist_remove(&pd_list, &power->node);
- rt_free(power);
- out:
- rt_mutex_release(&pd_lock);
- }
- #endif
- /**
- * @brief clock dev init.
- * @return RT_EOK
- */
- int clock_dev_init(void)
- {
- if (rt_mutex_init(&(clk_lock), "clkLock", RT_IPC_FLAG_FIFO) != RT_EOK)
- {
- RT_ASSERT(0);
- }
- if (rt_mutex_init(&(gate_lock), "gateLock", RT_IPC_FLAG_FIFO) != RT_EOK)
- {
- RT_ASSERT(0);
- }
- rt_slist_init(&clk_gate_list);
- #if defined(RT_USING_PMU)
- if (rt_mutex_init(&(pd_lock), "pdLock", RT_IPC_FLAG_FIFO) != RT_EOK)
- {
- RT_ASSERT(0);
- }
- rt_slist_init(&pd_list);
- #endif
- return RT_EOK;
- }
- INIT_BOARD_EXPORT(clock_dev_init);
- /**
- * @brief clock init frequency.
- * @param clk_inits: some need init clks.
- * @param clk_dump: if need printf clk get freq after setting.
- */
- void clk_init(const struct clk_init *clk_inits, bool clk_dump)
- {
- const struct clk_init *clks = clk_inits;
- while (clks->name)
- {
- if (clks->init_rate)
- {
- HAL_CRU_ClkSetFreq(clks->clk_id, clks->init_rate);
- }
- if (clk_dump)
- rt_kprintf("%s: %s = %d\n", __func__, clks->name, HAL_CRU_ClkGetFreq(clks->clk_id));
- clks++;
- }
- g_clk_init = clk_inits;
- }
- /**
- * @brief clock disable unused.
- * @param clks_unused: disable some not needed clks.
- */
- void clk_disable_unused(const struct clk_unused *clks_unused)
- {
- const struct clk_unused *clks = clks_unused;
- while (clks->gate_val)
- {
- if (clks->is_pmucru)
- {
- #if defined(CRU_PMU_CLKGATE_CON0_OFFSET)
- CRU->PMU_CLKGATE_CON[clks->gate_con] = clks->gate_val;
- #endif
- }
- else
- {
- CRU->CRU_CLKGATE_CON[clks->gate_con] = clks->gate_val;
- }
- clks++;
- }
- }
- #if defined(RT_USING_CRU_DUMP)
- /**
- * @brief clock dump frequency, dump cru registers, used for debug.
- */
- static void clk_dump(void)
- {
- const struct clk_init *clks = g_clk_init;
- int i;
- if (clks)
- {
- while (clks->name)
- {
- rt_kprintf("%s: %s[%x] = %d\n", __func__, clks->name, clks->clk_id,
- HAL_CRU_ClkGetFreq(clks->clk_id));
- clks++;
- }
- }
- for (i = 0; i < HAL_ARRAY_SIZE(CRU->CRU_CLKSEL_CON); i++)
- {
- rt_kprintf("%s: cru_sel_con[%d] = %lx\n", __func__, i, CRU->CRU_CLKSEL_CON[i]);
- }
- for (i = 0; i < HAL_ARRAY_SIZE(CRU->CRU_CLKGATE_CON); i++)
- {
- rt_kprintf("%s: cru_gate_con[%d] = %lx\n", __func__, i, CRU->CRU_CLKGATE_CON[i]);
- }
- for (i = 0; i < HAL_ARRAY_SIZE(CRU->CRU_SOFTRST_CON); i++)
- {
- rt_kprintf("%s: cru_softreset_con[%d] = %lx\n", __func__, i, CRU->CRU_SOFTRST_CON[i]);
- }
- }
- #ifdef RT_USING_FINSH
- #include <finsh.h>
- MSH_CMD_EXPORT(clk_dump, cru drive test. e.g: clk_dump);
- #endif
- #endif
- /** @} */
- #endif
- /** @} */
- /** @} */
|