rt-thread-official/bsp/rockchip/common/drivers/drv_clock.c

492 lines
10 KiB
C

/**
* 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
/** @} */
/** @} */