mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-10 12:57:06 +09:00
rk3188: add clock support
This commit is contained in:
1369
arch/arm/boot/dts/rk3188-clocks.dtsi
Normal file
1369
arch/arm/boot/dts/rk3188-clocks.dtsi
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,7 @@
|
||||
/dts-v1/;
|
||||
|
||||
#include "rk3188.dtsi"
|
||||
#include "rk3188-clocks.dtsi"
|
||||
|
||||
/ {
|
||||
memory {
|
||||
|
||||
@@ -20,6 +20,7 @@ obj-$(CONFIG_ARCH_U300) += clk-u300.o
|
||||
obj-$(CONFIG_COMMON_CLK_VERSATILE) += versatile/
|
||||
obj-$(CONFIG_ARCH_PRIMA2) += clk-prima2.o
|
||||
obj-$(CONFIG_PLAT_ORION) += mvebu/
|
||||
obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/
|
||||
ifeq ($(CONFIG_COMMON_CLK), y)
|
||||
obj-$(CONFIG_ARCH_MMP) += mmp/
|
||||
endif
|
||||
|
||||
@@ -220,6 +220,7 @@ static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
val = readl(divider->reg);
|
||||
val &= ~(div_mask(divider) << divider->shift);
|
||||
val |= value << divider->shift;
|
||||
val |= (div_mask(divider) << (divider->shift + 16));
|
||||
writel(val, divider->reg);
|
||||
|
||||
if (divider->lock)
|
||||
|
||||
@@ -89,6 +89,8 @@ static int clk_mux_set_parent(struct clk_hw *hw, u8 index)
|
||||
val = readl(mux->reg);
|
||||
val &= ~(mux->mask << mux->shift);
|
||||
val |= index << mux->shift;
|
||||
val |= (mux->mask << (mux->shift + 16));
|
||||
|
||||
writel(val, mux->reg);
|
||||
|
||||
if (mux->lock)
|
||||
|
||||
2
drivers/clk/rockchip/Makefile
Normal file
2
drivers/clk/rockchip/Makefile
Normal file
@@ -0,0 +1,2 @@
|
||||
obj-y += clk.o
|
||||
obj-y += clk-ops.o
|
||||
814
drivers/clk/rockchip/clk-ops.c
Normal file
814
drivers/clk/rockchip/clk-ops.c
Normal file
@@ -0,0 +1,814 @@
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/clk-private.h>
|
||||
#include "clk-ops.h"
|
||||
#include <linux/delay.h>
|
||||
|
||||
/* mux_ops */
|
||||
struct clk_ops_table rk_clk_mux_ops_table[] = {
|
||||
{.index = CLKOPS_TABLE_END},
|
||||
};
|
||||
|
||||
#define to_clk_divider(_hw) container_of(_hw, struct clk_divider, hw)
|
||||
#define div_mask(d) ((1 << ((d)->width)) - 1)
|
||||
|
||||
#define MHZ (1000 * 1000)
|
||||
static u32 clk_gcd(u32 numerator, u32 denominator)
|
||||
{
|
||||
u32 a, b;
|
||||
|
||||
if (!numerator || !denominator)
|
||||
return 0;
|
||||
if (numerator > denominator) {
|
||||
a = numerator;
|
||||
b = denominator;
|
||||
} else {
|
||||
a = denominator;
|
||||
b = numerator;
|
||||
}
|
||||
while (b != 0) {
|
||||
int r = b;
|
||||
b = a % b;
|
||||
a = r;
|
||||
}
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
static int clk_fracdiv_get_config(unsigned long rate_out, unsigned long rate,
|
||||
u32 *numerator, u32 *denominator)
|
||||
{
|
||||
u32 gcd_val;
|
||||
gcd_val = clk_gcd(rate, rate_out);
|
||||
clk_debug("%s: frac_get_seting rate=%lu, parent=%lu, gcd=%d\n",
|
||||
__func__, rate_out, rate, gcd_val);
|
||||
|
||||
if (!gcd_val) {
|
||||
clk_err("gcd=0, i2s frac div is not be supported\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*numerator = rate_out / gcd_val;
|
||||
*denominator = rate / gcd_val;
|
||||
|
||||
clk_debug("%s: frac_get_seting numerator=%d, denominator=%d, times=%d\n",
|
||||
__func__, *numerator, *denominator,
|
||||
*denominator / *numerator);
|
||||
|
||||
if (*numerator > 0xffff || *denominator > 0xffff ||
|
||||
(*denominator / (*numerator)) < 20) {
|
||||
clk_err("can't get a available nume and deno\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
static int clk_fracdiv_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
u32 numerator, denominator;
|
||||
struct clk_divider *div = to_clk_divider(hw);
|
||||
|
||||
struct clk *clk_parent = hw->clk->parent;
|
||||
if(clk_fracdiv_get_config(rate, parent_rate,
|
||||
&numerator, &denominator) == 0) {
|
||||
|
||||
clk_parent->ops->set_rate(clk_parent->hw,
|
||||
clk_parent->parent->rate,
|
||||
clk_parent->parent->rate);
|
||||
writel(numerator << 16 | denominator, div->reg);
|
||||
clk_err("%s set rate=%lu,is ok\n", hw->clk->name, rate);
|
||||
|
||||
} else {
|
||||
clk_err("clk_frac_div can't get rate=%lu,%s\n",
|
||||
rate, hw->clk->name);
|
||||
return -ENOENT;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long clk_fracdiv_recalc(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
unsigned long rate;
|
||||
u64 rate64;
|
||||
struct clk_divider *div = to_clk_divider(hw);
|
||||
u32 numerator, denominator, reg_val;
|
||||
reg_val = readl(div->reg);
|
||||
if (reg_val == 0)
|
||||
return parent_rate;
|
||||
numerator = reg_val >> 16;
|
||||
denominator = reg_val & 0xFFFF;
|
||||
rate64 = (u64)parent_rate * numerator;
|
||||
do_div(rate64, denominator);
|
||||
rate = rate64;
|
||||
clk_debug("%s: %s new clock rate is %lu, prate %lu (frac %u/%u)\n",
|
||||
__func__, hw->clk->name, rate, parent_rate,
|
||||
numerator, denominator);
|
||||
return rate;
|
||||
}
|
||||
static long clk_fracdiv_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *prate)
|
||||
{
|
||||
return rate;
|
||||
}
|
||||
/*************************************************************************/
|
||||
/* rate_ops */
|
||||
#define PARENTS_NUM_MAX 3
|
||||
/*
|
||||
* get the best rate from array of available rates, regarding rate which is smaller than
|
||||
* and most close to the set_rate as the best.
|
||||
*/
|
||||
static long get_best_rate(unsigned long array[],unsigned int num, int *n, long rate)
|
||||
{
|
||||
int i = 0;
|
||||
unsigned long best_rate = 0;
|
||||
|
||||
for(i = 0; i < num; i++){
|
||||
if(array[i] == rate){
|
||||
*n = i;
|
||||
return array[i];
|
||||
}else if((array[i] < rate) && (array[i] > best_rate)){
|
||||
best_rate = array[i];
|
||||
*n = i;
|
||||
}
|
||||
}
|
||||
|
||||
if(best_rate == 0){
|
||||
clk_err("NOT array rate is <= %lu\n", rate);
|
||||
}else{
|
||||
clk_debug("get the best available rate,but it != %lu you want to set!\n", rate);
|
||||
}
|
||||
|
||||
return best_rate;
|
||||
}
|
||||
|
||||
static struct clk *clk_get_best_parent(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned int *div_out)
|
||||
{
|
||||
struct clk *clk = hw->clk;
|
||||
u32 div[PARENTS_NUM_MAX] = {0};
|
||||
unsigned long new_rate[PARENTS_NUM_MAX] = {0};
|
||||
unsigned long best_rate;
|
||||
u32 i;
|
||||
|
||||
memset(div, 0, sizeof(div));
|
||||
memset(new_rate, 0, sizeof(new_rate));
|
||||
|
||||
if(clk->rate == rate)
|
||||
return clk->parent;
|
||||
|
||||
for(i = 0; i < clk->num_parents; i++) {
|
||||
new_rate[i] = clk_divider_ops.round_rate(hw, rate,
|
||||
&(clk->parents[i]->rate));
|
||||
div[i] = (clk->parents[i]->rate)/new_rate[i];
|
||||
if(new_rate[i] == rate) {
|
||||
*div_out = div[i];
|
||||
return clk->parents[i];
|
||||
}
|
||||
}
|
||||
|
||||
best_rate = get_best_rate(new_rate, PARENTS_NUM_MAX, &i, rate);
|
||||
if(best_rate == 0){
|
||||
clk_err("NOT rate is good!\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*div_out = div[i];
|
||||
|
||||
return clk->parents[i];
|
||||
}
|
||||
|
||||
static long clk_div_round_rate_autosel_parents(struct clk_hw *hw,
|
||||
unsigned long rate, unsigned long *prate)
|
||||
{
|
||||
struct clk *clk = hw->clk;
|
||||
struct clk *new_parent;
|
||||
int new_div;
|
||||
|
||||
if(clk->rate == rate)
|
||||
return rate;
|
||||
|
||||
new_parent = clk_get_best_parent(hw, rate, &new_div);
|
||||
if(!new_parent || (new_div <= 0)){
|
||||
clk_err("%s: clk %s could not get new_parent or new_div\n",
|
||||
__func__,clk->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return (new_parent->rate)/new_div;
|
||||
}
|
||||
|
||||
|
||||
static int clk_div_set_rate_autosel_parents(struct clk_hw *hw,
|
||||
unsigned long rate, unsigned long parent_rate)
|
||||
{
|
||||
//struct clk_divider *divider = to_clk_divider(hw);
|
||||
struct clk *clk = hw->clk;
|
||||
struct clk *new_parent;
|
||||
unsigned int new_div,old_div;
|
||||
unsigned long new_rate;
|
||||
int ret = 0;
|
||||
u8 index;
|
||||
int i;
|
||||
|
||||
if(clk->rate == rate)
|
||||
goto out;
|
||||
|
||||
new_parent = clk_get_best_parent(hw, rate, &new_div);
|
||||
if(!new_parent || (new_div == 0)){
|
||||
clk_err("%s: clk %s could not get new_parent or get "
|
||||
"new_div = 0\n", __func__,clk->name);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
old_div = (clk->parent->rate)/(clk->rate);
|
||||
|
||||
clk_debug("%s:%d: %s: %lu\n", __func__, __LINE__,
|
||||
clk->parent->name, new_parent->rate);
|
||||
if(new_div > old_div){
|
||||
new_rate = (clk->parent->rate)/new_div;
|
||||
ret = clk_divider_ops.set_rate(hw, new_rate,
|
||||
(clk->parent->rate));
|
||||
if(ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if(clk->parent != new_parent){
|
||||
for(i=0; i<clk->num_parents; i++){
|
||||
if(new_parent == clk->parents[i]){
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* ret = clk->ops->set_parent(clk->hw, index);
|
||||
* if(ret)
|
||||
* goto out;
|
||||
*/
|
||||
clk_set_parent(clk, new_parent);
|
||||
clk->ops->recalc_rate(clk->hw, clk->parent->rate);
|
||||
}
|
||||
|
||||
if(new_div <= old_div){
|
||||
new_rate = (clk->parent->rate)/new_div;
|
||||
ret = clk_divider_ops.set_rate(hw, new_rate,
|
||||
(clk->parent->rate));
|
||||
if(ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned long clk_divider_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
return clk_divider_ops.recalc_rate(hw, hw->clk->parent->rate);
|
||||
}
|
||||
|
||||
const struct clk_ops clkops_rate_auto_parent = {
|
||||
.recalc_rate = clk_divider_recalc_rate,
|
||||
.round_rate = clk_div_round_rate_autosel_parents,
|
||||
.set_rate = clk_div_set_rate_autosel_parents,
|
||||
};
|
||||
|
||||
static long clk_div_round_rate_even(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *prate)
|
||||
{
|
||||
int i = 0;
|
||||
struct clk_divider *divider =to_clk_divider(hw);
|
||||
int max_div = 1 << divider->width;
|
||||
|
||||
for (i = 1; i < max_div; i++) {
|
||||
if (i > 1 && (i % 2 != 0))
|
||||
continue;
|
||||
if (rate >= *prate / i)
|
||||
return *prate / i;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int clk_div_set_rate_even(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
return clk_divider_ops.set_rate(hw, rate, hw->clk->parent->rate);
|
||||
}
|
||||
|
||||
const struct clk_ops clkops_rate_evendiv = {
|
||||
.recalc_rate = clk_divider_recalc_rate,
|
||||
.round_rate = clk_div_round_rate_even,
|
||||
.set_rate = clk_div_set_rate_even,
|
||||
};
|
||||
|
||||
static long dclk_lcdc_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *prate)
|
||||
{
|
||||
long ret = 0;
|
||||
if (rate == 27 * MHZ) {
|
||||
ret = clk_div_round_rate_autosel_parents(hw, rate, prate);
|
||||
} else {
|
||||
ret = clk_div_round_rate_autosel_parents(hw, rate, prate);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dclk_lcdc_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
return clk_div_set_rate_autosel_parents(hw, rate, parent_rate);
|
||||
}
|
||||
|
||||
const struct clk_ops clkops_rate_dclk_lcdc = {
|
||||
.recalc_rate = clk_divider_recalc_rate,
|
||||
.round_rate = dclk_lcdc_round_rate,
|
||||
.set_rate = dclk_lcdc_set_rate,
|
||||
};
|
||||
|
||||
#define CIF_OUT_SRC_DIV (0x0)
|
||||
#define CIF_OUT_SRC_24M (0x1)
|
||||
|
||||
static unsigned long cif_out_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
return hw->clk->parent->rate;
|
||||
}
|
||||
|
||||
static long cif_out_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *prate)
|
||||
{
|
||||
struct clk *clk = hw->clk;
|
||||
struct clk *parent;
|
||||
|
||||
if (rate == clk->parents[CIF_OUT_SRC_24M]->rate) {
|
||||
return rate;
|
||||
} else {
|
||||
parent = clk->parents[CIF_OUT_SRC_DIV];
|
||||
return parent->ops->round_rate(parent->hw, rate,
|
||||
&(parent->parent->rate));
|
||||
}
|
||||
}
|
||||
|
||||
static int cif_out_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct clk *clk = hw->clk;
|
||||
struct clk *parent;
|
||||
int ret = 0;
|
||||
|
||||
if (rate == clk->parents[CIF_OUT_SRC_24M]->rate) {
|
||||
parent = clk->parents[CIF_OUT_SRC_24M];
|
||||
} else {
|
||||
parent = clk->parents[CIF_OUT_SRC_DIV];
|
||||
ret = parent->ops->set_rate(parent->hw, rate,
|
||||
parent->parent->rate);
|
||||
if (ret)
|
||||
goto out;
|
||||
else
|
||||
parent->rate = rate;
|
||||
}
|
||||
|
||||
if(clk->parent != parent){
|
||||
ret = clk_set_parent(clk, parent);
|
||||
#if 0
|
||||
for(i=0; i<clk->num_parents; i++){
|
||||
if(parent == clk->parents[i]){
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ret = clk->ops->set_parent(clk->hw, index);
|
||||
#endif
|
||||
if(ret)
|
||||
goto out;
|
||||
}
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
const struct clk_ops clkops_rate_cif_out = {
|
||||
.recalc_rate = cif_out_recalc_rate,
|
||||
.round_rate = cif_out_round_rate,
|
||||
.set_rate = cif_out_set_rate,
|
||||
};
|
||||
|
||||
static int clk_i2s_fracdiv_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
u32 numerator, denominator;
|
||||
struct clk *clk_parent;
|
||||
int i = 10;
|
||||
struct clk_divider *div = to_clk_divider(hw);
|
||||
|
||||
clk_parent = hw->clk->parent;
|
||||
if(clk_fracdiv_get_config(rate, parent_rate,
|
||||
&numerator, &denominator) == 0) {
|
||||
|
||||
clk_parent->ops->set_rate(clk_parent->hw,
|
||||
clk_parent->parent->rate,
|
||||
clk_parent->parent->rate);
|
||||
while (i--) {
|
||||
writel((numerator - 1) << 16 | denominator, div->reg);
|
||||
mdelay(1);
|
||||
writel(numerator << 16 | denominator, div->reg);
|
||||
mdelay(1);
|
||||
}
|
||||
clk_err("%s set rate=%lu,is ok\n", hw->clk->name, rate);
|
||||
|
||||
} else {
|
||||
clk_err("%s: can't get rate=%lu,%s\n", __func__, rate, hw->clk->name);
|
||||
return -ENOENT;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct clk_ops clkops_rate_i2s_frac = {
|
||||
.recalc_rate = clk_fracdiv_recalc,
|
||||
.round_rate = clk_fracdiv_round_rate,
|
||||
.set_rate = clk_i2s_fracdiv_set_rate,
|
||||
};
|
||||
static unsigned long clk_i2s_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
return hw->clk->parent->rate;
|
||||
}
|
||||
|
||||
static long clk_i2s_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *prate)
|
||||
{
|
||||
return rate;
|
||||
}
|
||||
|
||||
#define I2S_SRC_DIV (0x0)
|
||||
#define I2S_SRC_FRAC (0x1)
|
||||
#define I2S_SRC_12M (0x2)
|
||||
static int clk_i2s_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
u8 p_index = 0;
|
||||
struct clk *parent_tmp, *parent;
|
||||
struct clk *clk = hw->clk;
|
||||
|
||||
|
||||
if (rate == clk->parents[I2S_SRC_12M]->rate) {
|
||||
parent = clk->parents[I2S_SRC_12M];
|
||||
p_index = I2S_SRC_12M;
|
||||
goto set_parent;
|
||||
}
|
||||
|
||||
parent_tmp = clk->parents[I2S_SRC_DIV];
|
||||
|
||||
if(parent_tmp->ops->round_rate(parent_tmp->hw, rate,
|
||||
&parent_tmp->parent->rate) == rate) {
|
||||
parent = clk->parents[I2S_SRC_DIV];
|
||||
p_index = I2S_SRC_DIV;
|
||||
goto set;
|
||||
}
|
||||
|
||||
parent = clk->parents[I2S_SRC_FRAC];
|
||||
p_index = I2S_SRC_FRAC;
|
||||
//ret = clk_set_rate(parent_tmp, parent_tmp->parent->rate);
|
||||
ret = parent_tmp->ops->set_rate(parent_tmp->hw,
|
||||
parent_tmp->parent->rate,
|
||||
parent_tmp->parent->rate);
|
||||
parent_tmp->rate = parent_tmp->ops->recalc_rate(parent_tmp->hw,
|
||||
parent_tmp->parent->rate);
|
||||
//ret = parent->ops->set_rate(parent->hw, rate, parent->parent->rate);
|
||||
if (ret) {
|
||||
clk_debug("%s set rate%lu err\n", clk->name, rate);
|
||||
return ret;
|
||||
}
|
||||
|
||||
set:
|
||||
clk_debug(" %s set rate=%lu parent %s(old %s)\n",
|
||||
clk->name, rate, parent->name, clk->parent->name);
|
||||
|
||||
ret = clk_set_rate(parent, rate);
|
||||
//ret = parent->ops->set_rate(parent->hw, rate, parent->parent->rate);
|
||||
if (ret) {
|
||||
clk_debug("%s set rate%lu err\n", clk->name, rate);
|
||||
return ret;
|
||||
}
|
||||
|
||||
set_parent:
|
||||
clk_debug("%s: set parent\n", __func__);
|
||||
if (clk->parent != parent) {
|
||||
ret = clk_set_parent(clk, parent);
|
||||
/*
|
||||
* clk->ops->set_parent(hw, p_index);
|
||||
*/
|
||||
if (ret) {
|
||||
clk_debug("%s can't get rate%lu,reparent err\n",
|
||||
clk->name, rate);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
const struct clk_ops clkops_rate_i2s = {
|
||||
.recalc_rate = clk_i2s_recalc_rate,
|
||||
.round_rate = clk_i2s_round_rate,
|
||||
.set_rate = clk_i2s_set_rate,
|
||||
};
|
||||
const struct clk_ops clkops_rate_hsadc_frac = {
|
||||
.recalc_rate = clk_fracdiv_recalc,
|
||||
.round_rate = clk_fracdiv_round_rate,
|
||||
.set_rate = clk_fracdiv_set_rate,
|
||||
};
|
||||
|
||||
|
||||
const struct clk_ops clkops_rate_uart_frac = {
|
||||
.recalc_rate = clk_fracdiv_recalc,
|
||||
.round_rate = clk_fracdiv_round_rate,
|
||||
.set_rate = clk_fracdiv_set_rate,
|
||||
};
|
||||
|
||||
static unsigned long clk_uart_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
return hw->clk->parent->rate;
|
||||
|
||||
}
|
||||
static long clk_uart_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *prate)
|
||||
{
|
||||
return rate;
|
||||
}
|
||||
|
||||
#define UART_SRC_DIV (0x0)
|
||||
#define UART_SRC_FRAC (0x1)
|
||||
#define UART_SRC_24M (0x2)
|
||||
static int clk_uart_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
u8 p_index = 0;
|
||||
struct clk *parent_tmp, *parent;
|
||||
struct clk *clk = hw->clk;
|
||||
|
||||
|
||||
if (rate == clk->parents[UART_SRC_24M]->rate) {
|
||||
parent = clk->parents[UART_SRC_24M];
|
||||
p_index = UART_SRC_24M;
|
||||
goto set_parent;
|
||||
}
|
||||
|
||||
parent_tmp = clk->parents[UART_SRC_DIV];
|
||||
|
||||
if(parent_tmp->ops->round_rate(parent_tmp->hw, rate,
|
||||
&parent_tmp->parent->rate) == rate) {
|
||||
parent = clk->parents[UART_SRC_DIV];
|
||||
p_index = UART_SRC_DIV;
|
||||
goto set;
|
||||
}
|
||||
|
||||
parent = clk->parents[UART_SRC_FRAC];
|
||||
p_index = UART_SRC_FRAC;
|
||||
/*
|
||||
* ret = clk_set_rate(parent_tmp, parent_tmp->parent->rate);
|
||||
*/
|
||||
ret = parent_tmp->ops->set_rate(parent_tmp->hw,
|
||||
parent_tmp->parent->rate,
|
||||
parent_tmp->parent->rate);
|
||||
parent_tmp->rate = parent_tmp->ops->recalc_rate(parent_tmp->hw,
|
||||
parent_tmp->parent->rate);
|
||||
//ret = parent->ops->set_rate(parent->hw, rate, parent->parent->rate);
|
||||
if (ret) {
|
||||
clk_debug("%s set rate%lu err\n", clk->name, rate);
|
||||
return ret;
|
||||
}
|
||||
|
||||
set:
|
||||
clk_debug(" %s set rate=%lu parent %s(old %s)\n",
|
||||
clk->name, rate, parent->name, clk->parent->name);
|
||||
|
||||
ret = clk_set_rate(parent, rate);
|
||||
//ret = parent->ops->set_rate(parent->hw, rate, parent->parent->rate);
|
||||
if (ret) {
|
||||
clk_debug("%s set rate%lu err\n", clk->name, rate);
|
||||
return ret;
|
||||
}
|
||||
|
||||
set_parent:
|
||||
clk_debug("%s: set parent\n", __func__);
|
||||
if (clk->parent != parent) {
|
||||
ret = clk_set_parent(clk, parent);
|
||||
/*
|
||||
* clk->ops->set_parent(hw, p_index);
|
||||
*/
|
||||
if (ret) {
|
||||
clk_debug("%s can't get rate%lu,reparent err\n",
|
||||
clk->name, rate);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
const struct clk_ops clkops_rate_uart = {
|
||||
.recalc_rate = clk_uart_recalc_rate,
|
||||
.round_rate = clk_uart_round_rate,
|
||||
.set_rate = clk_uart_set_rate,
|
||||
};
|
||||
|
||||
|
||||
static unsigned long clk_hsadc_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
return hw->clk->parent->rate;
|
||||
|
||||
}
|
||||
static long clk_hsadc_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *prate)
|
||||
{
|
||||
return rate;
|
||||
}
|
||||
|
||||
#define HSADC_SRC_DIV (0x0)
|
||||
#define HSADC_SRC_FRAC (0x1)
|
||||
#define HSADC_SRC_EXT (0x2)
|
||||
static int clk_hsadc_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
u8 p_index = 0;
|
||||
struct clk *parent_tmp, *parent;
|
||||
struct clk *clk = hw->clk;
|
||||
|
||||
|
||||
if (rate == clk->parents[HSADC_SRC_EXT]->rate) {
|
||||
parent = clk->parents[HSADC_SRC_EXT];
|
||||
p_index = HSADC_SRC_EXT;
|
||||
goto set_parent;
|
||||
}
|
||||
|
||||
parent_tmp = clk->parents[HSADC_SRC_DIV];
|
||||
|
||||
if(parent_tmp->ops->round_rate(parent_tmp->hw, rate,
|
||||
&parent_tmp->parent->rate) == rate) {
|
||||
parent = clk->parents[HSADC_SRC_DIV];
|
||||
p_index = HSADC_SRC_DIV;
|
||||
goto set;
|
||||
}
|
||||
|
||||
parent = clk->parents[HSADC_SRC_FRAC];
|
||||
p_index = HSADC_SRC_FRAC;
|
||||
/*
|
||||
* ret = clk_set_rate(parent_tmp, parent_tmp->parent->rate);
|
||||
*/
|
||||
ret = parent_tmp->ops->set_rate(parent_tmp->hw,
|
||||
parent_tmp->parent->rate,
|
||||
parent_tmp->parent->rate);
|
||||
parent_tmp->rate = parent_tmp->ops->recalc_rate(parent_tmp->hw,
|
||||
parent_tmp->parent->rate);
|
||||
//ret = parent->ops->set_rate(parent->hw, rate, parent->parent->rate);
|
||||
if (ret) {
|
||||
clk_debug("%s set rate%lu err\n", clk->name, rate);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
set:
|
||||
clk_debug(" %s set rate=%lu parent %s(old %s)\n",
|
||||
clk->name, rate, parent->name, clk->parent->name);
|
||||
|
||||
ret = clk_set_rate(parent, rate);
|
||||
//ret = parent->ops->set_rate(parent->hw, rate, parent->parent->rate);
|
||||
if (ret) {
|
||||
clk_debug("%s set rate%lu err\n", clk->name, rate);
|
||||
return ret;
|
||||
}
|
||||
|
||||
set_parent:
|
||||
clk_debug("%s: set parent\n", __func__);
|
||||
if (clk->parent != parent) {
|
||||
ret = clk_set_parent(clk, parent);
|
||||
/*
|
||||
* clk->ops->set_parent(hw, p_index);
|
||||
*/
|
||||
if (ret) {
|
||||
clk_debug("%s can't get rate%lu,reparent err\n",
|
||||
clk->name, rate);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
const struct clk_ops clkops_rate_hsadc = {
|
||||
.recalc_rate = clk_hsadc_recalc_rate,
|
||||
.round_rate = clk_hsadc_round_rate,
|
||||
.set_rate = clk_hsadc_set_rate,
|
||||
};
|
||||
|
||||
static unsigned long clk_mac_ref_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
return hw->clk->parent->rate;
|
||||
|
||||
}
|
||||
static long clk_mac_ref_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *prate)
|
||||
{
|
||||
return rate;
|
||||
}
|
||||
|
||||
#define MAC_SRC_DIV (0x0)
|
||||
#define RMII_CLKIN (0x1)
|
||||
static int clk_mac_ref_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
u8 p_index = 0;
|
||||
struct clk *parent;
|
||||
struct clk *clk = hw->clk;
|
||||
|
||||
clk_debug("%s: rate %lu\n", __func__, rate);
|
||||
|
||||
if (rate == clk->parents[RMII_CLKIN]->rate) {
|
||||
parent = clk->parents[RMII_CLKIN];
|
||||
p_index = RMII_CLKIN;
|
||||
goto set_parent;
|
||||
}
|
||||
|
||||
parent = clk->parents[MAC_SRC_DIV];
|
||||
p_index = MAC_SRC_DIV;
|
||||
|
||||
clk_debug(" %s set rate=%lu parent %s(old %s)\n",
|
||||
clk->name, rate, parent->name, clk->parent->name);
|
||||
|
||||
/*
|
||||
* ret = clk_set_rate(parent, rate);
|
||||
*/
|
||||
ret = parent->ops->set_rate(parent->hw,
|
||||
rate,
|
||||
parent->parent->rate);
|
||||
parent->rate = parent->ops->recalc_rate(parent->hw,
|
||||
parent->parent->rate);
|
||||
//ret = parent->ops->set_rate(parent->hw, rate, parent->parent->rate);
|
||||
if (ret) {
|
||||
clk_debug("%s set rate%lu err\n", clk->name, rate);
|
||||
return ret;
|
||||
}
|
||||
|
||||
set_parent:
|
||||
clk_debug("%s: set parent\n", __func__);
|
||||
if (clk->parent != parent) {
|
||||
ret = clk_set_parent(clk, parent);
|
||||
/*
|
||||
* clk->ops->set_parent(hw, p_index);
|
||||
*/
|
||||
if (ret) {
|
||||
clk_debug("%s can't get rate%lu,reparent err\n",
|
||||
clk->name, rate);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
const struct clk_ops clkops_rate_mac_ref = {
|
||||
.recalc_rate = clk_mac_ref_recalc_rate,
|
||||
.round_rate = clk_mac_ref_round_rate,
|
||||
.set_rate = clk_mac_ref_set_rate,
|
||||
};
|
||||
|
||||
|
||||
struct clk_ops_table rk_clkops_rate_table[] = {
|
||||
{.index = CLKOPS_RATE_MUX_DIV, .clk_ops = &clkops_rate_auto_parent},
|
||||
{.index = CLKOPS_RATE_EVENDIV, .clk_ops = &clkops_rate_evendiv},
|
||||
{.index = CLKOPS_RATE_DCLK_LCDC, .clk_ops = &clkops_rate_dclk_lcdc},
|
||||
{.index = CLKOPS_RATE_CIFOUT, .clk_ops = &clkops_rate_cif_out},
|
||||
{.index = CLKOPS_RATE_I2S_FRAC, .clk_ops = &clkops_rate_i2s_frac},
|
||||
{.index = CLKOPS_RATE_I2S, .clk_ops = &clkops_rate_i2s},
|
||||
{.index = CLKOPS_RATE_HSADC_FRAC, .clk_ops = &clkops_rate_hsadc_frac},
|
||||
{.index = CLKOPS_RATE_UART_FRAC, .clk_ops = &clkops_rate_uart_frac},
|
||||
{.index = CLKOPS_RATE_UART, .clk_ops = &clkops_rate_uart},
|
||||
{.index = CLKOPS_RATE_HSADC, .clk_ops = &clkops_rate_hsadc},
|
||||
{.index = CLKOPS_RATE_MAC_REF, .clk_ops = &clkops_rate_mac_ref},
|
||||
|
||||
|
||||
{.index = CLKOPS_TABLE_END},
|
||||
};
|
||||
const struct clk_ops *rk_get_clkops(u32 idx)
|
||||
{
|
||||
return rk_clkops_rate_table[idx].clk_ops;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rk_get_clkops);
|
||||
19
drivers/clk/rockchip/clk-ops.h
Normal file
19
drivers/clk/rockchip/clk-ops.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#ifndef __RK_CLK_OPS_H
|
||||
#define __RK_CLK_OPS_H
|
||||
#include "clkops-dtsi.h"
|
||||
struct clk_ops_table {
|
||||
unsigned int index;
|
||||
const struct clk_ops *clk_ops;
|
||||
};
|
||||
const struct clk_ops *rk_get_clkops(u32 idx);
|
||||
//#define RKCLK_DEBUG
|
||||
|
||||
#if defined(RKCLK_DEBUG)
|
||||
#define clk_debug(fmt, args...) printk(KERN_INFO "rkclk: "fmt, ##args)
|
||||
#else
|
||||
#define clk_debug(fmt, args...) do {} while(0)
|
||||
#endif
|
||||
|
||||
#define clk_err(fmt, args...) printk(KERN_ERR "rkclk: "fmt, ##args)
|
||||
|
||||
#endif /* __RK_CLKOPS_H */
|
||||
924
drivers/clk/rockchip/clk.c
Normal file
924
drivers/clk/rockchip/clk.c
Normal file
@@ -0,0 +1,924 @@
|
||||
/*
|
||||
* Copyright (C) 2013 ROCKCHIP, Inc.
|
||||
* Author: chenxing <chenxing@rock-chips.com>
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include "clk-ops.h"
|
||||
#include <linux/clk-private.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
static DEFINE_SPINLOCK(clk_lock);
|
||||
|
||||
struct rkclk_divmap_table {
|
||||
u32 reg_val;
|
||||
u32 div_val;
|
||||
};
|
||||
|
||||
struct rkclk_divinfo {
|
||||
struct clk_divider *div;
|
||||
void __iomem *addr;
|
||||
u32 shift;
|
||||
u32 width;
|
||||
u32 div_type;
|
||||
u32 max_div;
|
||||
u32 fixed_div_val;
|
||||
u32 clkops_idx;
|
||||
const char *clk_name;
|
||||
const char *parent_name;
|
||||
struct clk_div_table *div_table;
|
||||
struct list_head node;
|
||||
};
|
||||
|
||||
struct rkclk_muxinfo {
|
||||
struct clk_mux *mux;
|
||||
void __iomem *addr;
|
||||
u32 shift;
|
||||
u32 width;
|
||||
u32 parent_num;
|
||||
u32 clkops_idx;
|
||||
const char *clk_name;
|
||||
const char **parent_names;
|
||||
struct list_head node;
|
||||
};
|
||||
|
||||
struct rkclk_fracinfo {
|
||||
struct clk_hw hw;
|
||||
void __iomem *addr;
|
||||
u32 shift;
|
||||
u32 width;
|
||||
u32 frac_type;
|
||||
u32 clkops_idx;
|
||||
const char *clk_name;
|
||||
const char *parent_name;
|
||||
struct list_head node;
|
||||
};
|
||||
|
||||
struct rkclk_gateinfo {
|
||||
struct clk_gate *gate;
|
||||
void __iomem *addr;
|
||||
u32 shift;
|
||||
u32 clkops_idx;
|
||||
const char *clk_name;
|
||||
const char *parent_name;
|
||||
};
|
||||
|
||||
struct rkclk_pllinfo {
|
||||
struct clk_hw hw;
|
||||
struct clk_ops *ops;
|
||||
void __iomem *addr;
|
||||
u32 width;
|
||||
const char *clk_name;
|
||||
const char *parent_name;
|
||||
/*
|
||||
* const char **clkout_names;
|
||||
*/
|
||||
struct list_head node;
|
||||
};
|
||||
|
||||
struct rkclk {
|
||||
const char *clk_name;
|
||||
u32 clk_type;
|
||||
/*
|
||||
* store nodes creat this rkclk
|
||||
* */
|
||||
struct device_node *np;
|
||||
struct rkclk_pllinfo *pll_info;
|
||||
struct rkclk_muxinfo *mux_info;
|
||||
struct rkclk_divinfo *div_info;
|
||||
struct rkclk_fracinfo *frac_info;
|
||||
struct rkclk_gateinfo *gate_info;
|
||||
struct list_head node;
|
||||
};
|
||||
|
||||
LIST_HEAD(rk_clks);
|
||||
void __iomem *reg_start = 0;
|
||||
#define RKCLK_PLL_TYPE (1 << 0)
|
||||
#define RKCLK_MUX_TYPE (1 << 1)
|
||||
#define RKCLK_DIV_TYPE (1 << 2)
|
||||
#define RKCLK_FRAC_TYPE (1 << 3)
|
||||
#define RKCLK_GATE_TYPE (1 << 4)
|
||||
|
||||
#define CLK_GATE_SET_TO_DISABLE BIT(0)
|
||||
#define CLK_GATE_HIWORD_MASK BIT(1)
|
||||
static int rkclk_init_muxinfo(struct device_node *np,
|
||||
struct rkclk_muxinfo *mux, void __iomem *addr)
|
||||
{
|
||||
int cnt, i, ret = 0;
|
||||
u8 found = 0;
|
||||
struct rkclk *rkclk;
|
||||
|
||||
mux = kzalloc(sizeof(struct rkclk_muxinfo), GFP_KERNEL);
|
||||
if (!mux)
|
||||
return -ENOMEM;
|
||||
/*
|
||||
* Get control bit addr
|
||||
*/
|
||||
ret = of_property_read_u32_index(np, "rockchip,bits", 0, &mux->shift);
|
||||
if (ret != 0)
|
||||
return -EINVAL;
|
||||
ret = of_property_read_u32(np, "rockchip,clkops-idx", &mux->clkops_idx);
|
||||
if (ret != 0)
|
||||
mux->clkops_idx = CLKOPS_TABLE_END;
|
||||
|
||||
ret = of_property_read_u32_index(np, "rockchip,bits", 1, &mux->width);
|
||||
if (ret != 0)
|
||||
return -EINVAL;
|
||||
mux->addr = addr;
|
||||
|
||||
ret = of_property_read_string(np, "clock-output-names", &mux->clk_name);
|
||||
if (ret != 0)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Get parents' cnt
|
||||
*/
|
||||
cnt = of_count_phandle_with_args(np, "clocks", "#clock-cells");
|
||||
if (cnt< 0)
|
||||
return -EINVAL;
|
||||
|
||||
mux->parent_num = cnt;
|
||||
mux->parent_names = kzalloc(cnt * sizeof(char *), GFP_KERNEL);
|
||||
|
||||
clk_debug("%s: parent cnt = %d\n", __func__, cnt);
|
||||
for (i = 0; i < cnt ; i++) {
|
||||
|
||||
mux->parent_names[i] = of_clk_get_parent_name(np, i);
|
||||
}
|
||||
|
||||
found = 0;
|
||||
list_for_each_entry(rkclk, &rk_clks, node) {
|
||||
if (strcmp(mux->clk_name, rkclk->clk_name) == 0) {
|
||||
if (rkclk->mux_info != NULL)
|
||||
clk_err("%s(%d): This clk(%s) has been used\n",
|
||||
__func__, __LINE__, mux->clk_name);
|
||||
clk_debug("%s: find match %s\n", __func__, rkclk->clk_name);
|
||||
found = 1;
|
||||
rkclk->mux_info = mux;
|
||||
rkclk->clk_type |= RKCLK_MUX_TYPE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
rkclk = kzalloc(sizeof(struct rkclk), GFP_KERNEL);
|
||||
rkclk->clk_name = mux->clk_name;
|
||||
rkclk->mux_info = mux;
|
||||
rkclk->clk_type |= RKCLK_MUX_TYPE;
|
||||
rkclk->np = np;
|
||||
clk_debug("%s: creat %s\n", __func__, rkclk->clk_name);
|
||||
|
||||
list_add_tail(&rkclk->node, &rk_clks);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
static int rkclk_init_divinfo(struct device_node *np,
|
||||
struct rkclk_divinfo *div, void __iomem *addr)
|
||||
{
|
||||
int cnt = 0, i = 0, ret = 0;
|
||||
struct rkclk *rkclk;
|
||||
u8 found = 0;
|
||||
|
||||
div = kzalloc(sizeof(struct rkclk_divinfo), GFP_KERNEL);
|
||||
if (!div)
|
||||
return -ENOMEM;
|
||||
|
||||
of_property_read_u32_index(np, "rockchip,bits", 0, &div->shift);
|
||||
of_property_read_u32_index(np, "rockchip,bits", 1, &div->width);
|
||||
div->addr = addr;
|
||||
|
||||
of_property_read_u32(np, "rockchip,div-type", &div->div_type);
|
||||
ret = of_property_read_u32(np, "rockchip,clkops-idx", &div->clkops_idx);
|
||||
if (ret != 0)
|
||||
div->clkops_idx = CLKOPS_TABLE_END;
|
||||
|
||||
cnt = of_property_count_strings(np, "clock-output-names");
|
||||
if (cnt <= 0)
|
||||
div->clk_name = of_clk_get_parent_name(np, 0);
|
||||
else {
|
||||
ret = of_property_read_string(np, "clock-output-names", &div->clk_name);
|
||||
if (ret != 0)
|
||||
return -EINVAL;
|
||||
div->parent_name = of_clk_get_parent_name(np, 0);
|
||||
}
|
||||
|
||||
switch (div->div_type) {
|
||||
case CLK_DIVIDER_PLUS_ONE:
|
||||
case CLK_DIVIDER_ONE_BASED:
|
||||
case CLK_DIVIDER_POWER_OF_TWO:
|
||||
break;
|
||||
case CLK_DIVIDER_FIXED:
|
||||
of_property_read_u32_index(np, "rockchip,div-relations", 0,
|
||||
&div->fixed_div_val);
|
||||
clk_debug("%s:%s fixed_div = %d\n", __func__,
|
||||
div->clk_name, div->fixed_div_val);
|
||||
break;
|
||||
case CLK_DIVIDER_USER_DEFINE:
|
||||
of_get_property(np, "rockchip,div-relations", &cnt);
|
||||
cnt /= 4 * 2;
|
||||
div->div_table = kzalloc(cnt * sizeof(struct clk_div_table),
|
||||
GFP_KERNEL);
|
||||
|
||||
for (i = 0; i < cnt; i++) {
|
||||
of_property_read_u32_index(np, "rockchip,div-relations", i * 2,
|
||||
&div->div_table[i].val);
|
||||
of_property_read_u32_index(np, "rockchip,div-relations", i * 2 + 1,
|
||||
&div->div_table[i].div);
|
||||
clk_debug("\tGet div table %d: val=%d, div=%d\n",
|
||||
i, div->div_table[i].val,
|
||||
div->div_table[i].div);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
clk_err("%s: %s: unknown rockchip,div-type, please check dtsi\n",
|
||||
__func__, div->clk_name);
|
||||
break;
|
||||
}
|
||||
|
||||
found = 0;
|
||||
list_for_each_entry(rkclk, &rk_clks, node) {
|
||||
if (strcmp(div->clk_name, rkclk->clk_name) == 0) {
|
||||
if (rkclk->div_info != NULL)
|
||||
clk_err("%s(Line %d): This clk(%s) has been used\n",
|
||||
__func__, __LINE__, rkclk->clk_name);
|
||||
clk_debug("%s: find match %s\n", __func__, rkclk->clk_name);
|
||||
found = 1;
|
||||
rkclk->div_info = div;
|
||||
rkclk->clk_type |= RKCLK_DIV_TYPE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
rkclk = kzalloc(sizeof(struct rkclk), GFP_KERNEL);
|
||||
rkclk->clk_name = div->clk_name;
|
||||
rkclk->div_info = div;
|
||||
rkclk->clk_type |= RKCLK_DIV_TYPE;
|
||||
rkclk->np = np;
|
||||
clk_debug("%s: creat %s\n", __func__, rkclk->clk_name);
|
||||
|
||||
list_add_tail(&rkclk->node, &rk_clks);
|
||||
}
|
||||
return 0;
|
||||
|
||||
|
||||
}
|
||||
static int rkclk_init_fracinfo(struct device_node *np,
|
||||
struct rkclk_fracinfo *frac, void __iomem *addr)
|
||||
{
|
||||
struct rkclk *rkclk;
|
||||
u8 found = 0;
|
||||
int ret = 0;
|
||||
|
||||
frac = kzalloc(sizeof(struct rkclk_fracinfo), GFP_KERNEL);
|
||||
if (!frac)
|
||||
return -ENOMEM;
|
||||
|
||||
of_property_read_u32_index(np, "rockchip,bits", 0, &frac->shift);
|
||||
of_property_read_u32_index(np, "rockchip,bits", 1, &frac->width);
|
||||
frac->addr = addr;
|
||||
|
||||
ret = of_property_read_u32(np, "rockchip,clkops-idx", &frac->clkops_idx);
|
||||
if (ret != 0)
|
||||
frac->clkops_idx = CLKOPS_TABLE_END;
|
||||
|
||||
frac->parent_name = of_clk_get_parent_name(np, 0);
|
||||
ret = of_property_read_string(np, "clock-output-names", &frac->clk_name);
|
||||
if (ret != 0)
|
||||
return -EINVAL;
|
||||
|
||||
found = 0;
|
||||
list_for_each_entry(rkclk, &rk_clks, node) {
|
||||
if (strcmp(frac->clk_name, rkclk->clk_name) == 0) {
|
||||
if (rkclk->frac_info != NULL)
|
||||
clk_err("%s(%d): This clk(%s) has been used\n",
|
||||
__func__, __LINE__, frac->clk_name);
|
||||
clk_debug("%s: find match %s\n", __func__, rkclk->clk_name);
|
||||
found = 1;
|
||||
rkclk->frac_info = frac;
|
||||
rkclk->clk_type |= RKCLK_FRAC_TYPE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
rkclk = kzalloc(sizeof(struct rkclk), GFP_KERNEL);
|
||||
rkclk->clk_name = frac->clk_name;
|
||||
rkclk->frac_info = frac;
|
||||
rkclk->clk_type |= RKCLK_FRAC_TYPE;
|
||||
rkclk->np = np;
|
||||
clk_debug("%s: creat %s\n", __func__, rkclk->clk_name);
|
||||
|
||||
list_add_tail(&rkclk->node, &rk_clks);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init rkclk_init_selcon(struct device_node *np)
|
||||
{
|
||||
struct device_node *node_con, *node;
|
||||
void __iomem *reg = 0;
|
||||
|
||||
struct rkclk_divinfo *divinfo;
|
||||
struct rkclk_muxinfo *muxinfo;
|
||||
struct rkclk_fracinfo *fracinfo;
|
||||
|
||||
for_each_available_child_of_node(np, node_con) {
|
||||
|
||||
reg = of_iomap(node_con, 0);
|
||||
|
||||
for_each_available_child_of_node(node_con, node) {
|
||||
|
||||
if (of_device_is_compatible(node, "rockchip,rk3188-div-con"))
|
||||
rkclk_init_divinfo(node, divinfo, reg);
|
||||
|
||||
else if (of_device_is_compatible(node, "rockchip,rk3188-mux-con"))
|
||||
rkclk_init_muxinfo(node, muxinfo, reg);
|
||||
|
||||
else if (of_device_is_compatible(node, "rockchip,rk3188-frac-con"))
|
||||
rkclk_init_fracinfo(node, fracinfo, reg);
|
||||
|
||||
else if (of_device_is_compatible(node, "rockchip,rk3188-inv-con"))
|
||||
clk_debug("INV clk\n");
|
||||
|
||||
else
|
||||
clk_err("%s: unknown controler type, plz check dtsi "
|
||||
"or add type support\n", __func__);
|
||||
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init rkclk_init_gatecon(struct device_node *np)
|
||||
{
|
||||
struct clk_onecell_data *clk_data;
|
||||
struct device_node *node;
|
||||
const char *clk_parent;
|
||||
const char *clk_name;
|
||||
void __iomem *reg;
|
||||
void __iomem *reg_idx;
|
||||
int flags;
|
||||
int cnt;
|
||||
int reg_bit;
|
||||
int clkflags = CLK_SET_RATE_PARENT;
|
||||
int i;
|
||||
struct rkclk_gateinfo *gateinfo;
|
||||
u8 found = 0;
|
||||
struct rkclk *rkclk;
|
||||
|
||||
for_each_available_child_of_node(np, node) {
|
||||
cnt = of_property_count_strings(node, "clock-output-names");
|
||||
if (cnt < 0) {
|
||||
clk_err("%s: error in clock-output-names %d\n",
|
||||
__func__, cnt);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cnt == 0) {
|
||||
pr_info("%s: nothing to do\n", __func__);
|
||||
continue;
|
||||
}
|
||||
|
||||
reg = of_iomap(node, 0);
|
||||
|
||||
clk_data = kzalloc(sizeof(struct clk_onecell_data), GFP_KERNEL);
|
||||
if (!clk_data)
|
||||
return -ENOMEM;
|
||||
|
||||
clk_data->clks = kzalloc(cnt * sizeof(struct clk *), GFP_KERNEL);
|
||||
if (!clk_data->clks) {
|
||||
kfree(clk_data);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
flags = CLK_GATE_HIWORD_MASK | CLK_GATE_SET_TO_DISABLE;
|
||||
|
||||
for (i = 0; i < cnt; i++) {
|
||||
of_property_read_string_index(node, "clock-output-names",
|
||||
i, &clk_name);
|
||||
|
||||
/* ignore empty slots */
|
||||
if (!strcmp("reserved", clk_name))
|
||||
continue;
|
||||
|
||||
clk_parent = of_clk_get_parent_name(node, i);
|
||||
|
||||
clkflags |= CLK_IGNORE_UNUSED;
|
||||
|
||||
reg_idx = reg + (4 * (i / 16));
|
||||
reg_bit = (i % 16);
|
||||
|
||||
gateinfo = kzalloc(sizeof(struct rkclk_gateinfo), GFP_KERNEL);
|
||||
gateinfo->clk_name = clk_name;
|
||||
gateinfo->parent_name = clk_parent;
|
||||
gateinfo->addr = reg;
|
||||
gateinfo->shift = reg_bit;
|
||||
found = 0;
|
||||
list_for_each_entry(rkclk, &rk_clks, node) {
|
||||
if (strcmp(clk_name, rkclk->clk_name) == 0) {
|
||||
if (rkclk->gate_info != NULL)
|
||||
clk_err("%s(%d): This clk(%s) has been used\n",
|
||||
__func__, __LINE__, clk_name);
|
||||
clk_debug("%s: find match %s\n", __func__, rkclk->clk_name);
|
||||
found = 1;
|
||||
rkclk->gate_info = gateinfo;
|
||||
rkclk->clk_type |= RKCLK_GATE_TYPE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
rkclk = kzalloc(sizeof(struct rkclk), GFP_KERNEL);
|
||||
rkclk->clk_name = gateinfo->clk_name;
|
||||
rkclk->gate_info = gateinfo;
|
||||
rkclk->clk_type |= RKCLK_GATE_TYPE;
|
||||
rkclk->np = np;
|
||||
clk_debug("%s: creat %s\n", __func__, rkclk->clk_name);
|
||||
|
||||
list_add_tail(&rkclk->node, &rk_clks);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
static int __init rkclk_init_pllcon(struct device_node *np)
|
||||
{
|
||||
struct rkclk_pllinfo *pllinfo;
|
||||
struct device_node *node;
|
||||
struct rkclk *rkclk;
|
||||
void __iomem *reg;
|
||||
int i = 0;
|
||||
int ret = 0, clknum = 0;
|
||||
u8 found = 0;
|
||||
|
||||
for_each_available_child_of_node(np, node) {
|
||||
clknum = of_property_count_strings(node, "clock-output-names");
|
||||
if (clknum < 0) {
|
||||
clk_err("%s: error in get clock-output-names numbers = %d\n",
|
||||
__func__, clknum);
|
||||
return -EINVAL;
|
||||
}
|
||||
reg = of_iomap(node, 0);
|
||||
if (reg_start == 0)
|
||||
reg_start = reg;
|
||||
for (i = 0; i < clknum; i++) {
|
||||
pllinfo = kzalloc(sizeof(struct rkclk_pllinfo), GFP_KERNEL);
|
||||
if (!pllinfo)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* Get pll parent name
|
||||
*/
|
||||
pllinfo->parent_name = of_clk_get_parent_name(node, i);
|
||||
|
||||
/*
|
||||
* Get pll output name
|
||||
*/
|
||||
of_property_read_string_index(node, "clock-output-names",
|
||||
i, &pllinfo->clk_name);
|
||||
|
||||
pllinfo->addr = reg;
|
||||
|
||||
ret = of_property_read_u32_index(node, "reg", 1, &pllinfo->width);
|
||||
if (ret != 0) {
|
||||
clk_err("%s: cat not get reg info\n", __func__);
|
||||
}
|
||||
|
||||
clk_debug("%s: parent=%s, pllname=%s, reg =%08x, cnt=%d\n", __func__,
|
||||
pllinfo->parent_name, pllinfo->clk_name,
|
||||
(u32)pllinfo->addr, pllinfo->width);
|
||||
|
||||
found = 0;
|
||||
list_for_each_entry(rkclk, &rk_clks, node) {
|
||||
if (strcmp(pllinfo->clk_name, rkclk->clk_name) == 0) {
|
||||
if (rkclk->pll_info != NULL)
|
||||
clk_err("%s(%d): This clk(%s) has been used\n",
|
||||
__func__, __LINE__, pllinfo->clk_name);
|
||||
clk_debug("%s: find match %s\n", __func__, rkclk->clk_name);
|
||||
found = 1;
|
||||
rkclk->pll_info = pllinfo;
|
||||
rkclk->clk_type |= RKCLK_PLL_TYPE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
rkclk = kzalloc(sizeof(struct rkclk), GFP_KERNEL);
|
||||
rkclk->clk_name = pllinfo->clk_name;
|
||||
rkclk->pll_info = pllinfo;
|
||||
rkclk->clk_type |= RKCLK_PLL_TYPE;
|
||||
rkclk->np = np;
|
||||
|
||||
list_add_tail(&rkclk->node, &rk_clks);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define MHZ (1000 * 1000)
|
||||
static unsigned long clk_pll_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
clk_debug("%s\n", __func__);
|
||||
if (strncmp(hw->clk->name, "clk_apll", sizeof("clk_apll")) == 0) {
|
||||
return 600 * MHZ;
|
||||
} else if (strncmp(hw->clk->name, "clk_dpll", sizeof("clk_dpll")) == 0) {
|
||||
return 300 * MHZ;
|
||||
}else if (strncmp(hw->clk->name, "clk_cpll", sizeof("clk_cpll")) == 0) {
|
||||
return 132 * MHZ;
|
||||
}else if (strncmp(hw->clk->name, "clk_gpll", sizeof("clk_gpll")) == 0) {
|
||||
return 891 * MHZ;
|
||||
} else
|
||||
clk_err("Unknown PLL\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
static long clk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *prate)
|
||||
{
|
||||
clk_debug("%s\n", __func__);
|
||||
return rate;
|
||||
}
|
||||
static int clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
clk_debug("%s\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
const struct clk_ops clk_pll_ops = {
|
||||
.recalc_rate = clk_pll_recalc_rate,
|
||||
.round_rate = clk_pll_round_rate,
|
||||
.set_rate = clk_pll_set_rate,
|
||||
};
|
||||
static unsigned long clk_frac_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
return parent_rate;
|
||||
}
|
||||
static long clk_frac_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *prate)
|
||||
{
|
||||
return rate;
|
||||
}
|
||||
static int clk_frac_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
const struct clk_ops clk_frac_ops = {
|
||||
.recalc_rate = clk_frac_recalc_rate,
|
||||
.round_rate = clk_frac_round_rate,
|
||||
.set_rate = clk_frac_set_rate,
|
||||
};
|
||||
static unsigned long clk_div_special_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
return parent_rate;
|
||||
}
|
||||
static long clk_div_special_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *prate)
|
||||
{
|
||||
return rate;
|
||||
}
|
||||
static int clk_div_special_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
// For fixed div clks and For user defined div clk
|
||||
const struct clk_ops clk_div_special_ops = {
|
||||
.recalc_rate = clk_div_special_recalc_rate,
|
||||
.round_rate = clk_div_special_round_rate,
|
||||
.set_rate = clk_div_special_set_rate,
|
||||
};
|
||||
static int rkclk_register(struct rkclk *rkclk)
|
||||
{
|
||||
struct clk_mux *mux = NULL;
|
||||
struct clk_divider *div = NULL;
|
||||
struct clk_gate *gate = NULL;
|
||||
|
||||
const struct clk_ops *rate_ops = NULL;
|
||||
const struct clk_ops *mux_ops = NULL;
|
||||
|
||||
struct clk *clk = NULL;
|
||||
const char **parent_names = NULL;
|
||||
struct clk_hw *rate_hw;
|
||||
int parent_num;
|
||||
struct device_node *node = rkclk->np;
|
||||
/* Single clk */
|
||||
clk_debug("%s: %s clk_type=%x\n", __func__,
|
||||
rkclk->clk_name, rkclk->clk_type);
|
||||
if (rkclk->clk_type & RKCLK_PLL_TYPE) {
|
||||
div = kzalloc(sizeof(struct clk_divider), GFP_KERNEL);
|
||||
rate_ops = &clk_pll_ops;
|
||||
div->reg = rkclk->pll_info->addr;
|
||||
div->shift = 0;
|
||||
div->width = rkclk->pll_info->width;
|
||||
rate_hw = &div->hw;
|
||||
|
||||
parent_num = 1;
|
||||
parent_names = &rkclk->pll_info->parent_name;
|
||||
|
||||
} else if (rkclk->clk_type & RKCLK_FRAC_TYPE) {
|
||||
div = kzalloc(sizeof(struct clk_divider), GFP_KERNEL);
|
||||
if (rkclk->frac_info->clkops_idx != CLKOPS_TABLE_END)
|
||||
rate_ops = rk_get_clkops(rkclk->frac_info->clkops_idx);
|
||||
else
|
||||
rate_ops = &clk_frac_ops;
|
||||
div->reg = rkclk->frac_info->addr;
|
||||
div->shift = (u8)rkclk->frac_info->shift;
|
||||
div->width = rkclk->frac_info->width;
|
||||
rate_hw = &div->hw;
|
||||
|
||||
parent_num = 1;
|
||||
parent_names = &rkclk->frac_info->parent_name;
|
||||
|
||||
} else if (rkclk->clk_type & RKCLK_DIV_TYPE) {
|
||||
div = kzalloc(sizeof(struct clk_divider), GFP_KERNEL);
|
||||
if (rkclk->div_info->clkops_idx != CLKOPS_TABLE_END)
|
||||
rate_ops = rk_get_clkops(rkclk->div_info->clkops_idx);
|
||||
else
|
||||
rate_ops = &clk_divider_ops;
|
||||
div->reg = rkclk->div_info->addr;
|
||||
div->shift = (u8)rkclk->div_info->shift;
|
||||
div->width = rkclk->div_info->width;
|
||||
div->flags = rkclk->div_info->div_type;
|
||||
rate_hw = &div->hw;
|
||||
if (rkclk->div_info->div_table)
|
||||
div->table = rkclk->div_info->div_table;
|
||||
|
||||
parent_num = 1;
|
||||
parent_names = &rkclk->div_info->parent_name;
|
||||
if (rkclk->clk_type != (rkclk->clk_type & CLK_DIVIDER_MASK)) {
|
||||
// FIXME: fixed div add here
|
||||
clk_err("%s: %d, unknown clk_type=%x\n",
|
||||
__func__, __LINE__, rkclk->clk_type);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (rkclk->clk_type & RKCLK_MUX_TYPE) {
|
||||
mux = kzalloc(sizeof(struct clk_mux), GFP_KERNEL);
|
||||
mux->reg = rkclk->mux_info->addr;
|
||||
mux->shift = (u8)rkclk->mux_info->shift;
|
||||
mux->mask = (1 << rkclk->mux_info->width) - 1;
|
||||
mux->flags = 0;
|
||||
mux_ops = &clk_mux_ops;
|
||||
if (rkclk->mux_info->clkops_idx != CLKOPS_TABLE_END) {
|
||||
rate_hw = kzalloc(sizeof(struct clk_hw), GFP_KERNEL);
|
||||
rate_ops = rk_get_clkops(rkclk->mux_info->clkops_idx);
|
||||
}
|
||||
|
||||
parent_num = rkclk->mux_info->parent_num;
|
||||
parent_names = rkclk->mux_info->parent_names;
|
||||
}
|
||||
|
||||
if (rkclk->clk_type & RKCLK_GATE_TYPE) {
|
||||
gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL);
|
||||
gate->reg = rkclk->gate_info->addr;
|
||||
gate->bit_idx = rkclk->gate_info->shift;
|
||||
gate->flags = CLK_GATE_HIWORD_MASK | CLK_GATE_SET_TO_DISABLE;
|
||||
|
||||
}
|
||||
|
||||
// FIXME: flag(CLK_IGNORE_UNUSED) may need an input argument
|
||||
if (rkclk->clk_type == RKCLK_MUX_TYPE
|
||||
&& rkclk->mux_info->clkops_idx == CLKOPS_TABLE_END) {
|
||||
clk = clk_register_mux(NULL, rkclk->clk_name,
|
||||
rkclk->mux_info->parent_names,
|
||||
(u8)rkclk->mux_info->parent_num,
|
||||
CLK_SET_RATE_PARENT,
|
||||
mux->reg, mux->shift, mux->mask,
|
||||
0, &clk_lock);
|
||||
} else if (rkclk->clk_type == RKCLK_DIV_TYPE) {
|
||||
clk = clk_register_divider(NULL, rkclk->clk_name,
|
||||
rkclk->div_info->parent_name,
|
||||
CLK_SET_RATE_PARENT, div->reg, div->shift,
|
||||
div->width, div->flags, &clk_lock);
|
||||
} else if (rkclk->clk_type == RKCLK_GATE_TYPE) {
|
||||
clk = clk_register_gate(NULL, rkclk->clk_name,
|
||||
rkclk->gate_info->parent_name,
|
||||
CLK_IGNORE_UNUSED, gate->reg,
|
||||
gate->bit_idx,
|
||||
gate->flags, &clk_lock);
|
||||
} else {
|
||||
int i = 0;
|
||||
clk_debug("%s: composite clk(\"%s\") parents:\n",
|
||||
__func__, rkclk->clk_name);
|
||||
|
||||
for (i = 0; i < parent_num; i++) {
|
||||
clk_debug("\t\t%s: parent[%d]=%s\n", __func__,
|
||||
i, parent_names[i]);
|
||||
}
|
||||
clk = clk_register_composite(NULL, rkclk->clk_name,
|
||||
parent_names, parent_num,
|
||||
mux ? &mux->hw : NULL, mux ? mux_ops : NULL,
|
||||
rate_hw, rate_ops,
|
||||
gate ? &gate->hw : NULL, gate ? &clk_gate_ops : NULL,
|
||||
CLK_IGNORE_UNUSED);
|
||||
}
|
||||
if (clk) {
|
||||
of_clk_add_provider(node, of_clk_src_simple_get, clk);
|
||||
clk_register_clkdev(clk, rkclk->clk_name, NULL);
|
||||
} else {
|
||||
clk_err("%s: clk(\"%s\") register clk error\n",
|
||||
__func__, rkclk->clk_name);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct test_table {
|
||||
const char *name;
|
||||
u32 rate;
|
||||
};
|
||||
struct test_table t_table[] = {
|
||||
{.name = "clk_gpu", .rate = 297000000},
|
||||
{.name = "dclk_lcdc0", .rate = 297000000},
|
||||
{.name = "clk_i2s", .rate = 11289600},
|
||||
{.name = "clk_spdif", .rate = 11289600},
|
||||
{.name = "clk_sdmmc", .rate = 50000000},
|
||||
{.name = "clk_emmc", .rate = 50000000},
|
||||
{.name = "clk_sdio", .rate = 50000000},
|
||||
{.name = "clk_uart0", .rate = 12288000},
|
||||
{.name = "clk_hsadc", .rate = 12288000},
|
||||
{.name = "clk_mac", .rate = 50000000},
|
||||
{.name = "clk_cif0", .rate = 12000000},
|
||||
{.name = "aclk_lcdc0", .rate = 297000000},
|
||||
};
|
||||
|
||||
#ifdef RKCLK_DEBUG
|
||||
void rk_clk_test(void)
|
||||
{
|
||||
const char *clk_name;
|
||||
struct clk *clk;
|
||||
u32 rate = 0;
|
||||
|
||||
u32 i = 0, j = 0;
|
||||
for (j = 0; j < ARRAY_SIZE(t_table); j++) {
|
||||
clk_name = t_table[j].name;
|
||||
rate = t_table[j].rate;
|
||||
|
||||
clk = clk_get(NULL, clk_name);
|
||||
if (IS_ERR(clk)) {
|
||||
clk_err("%s: clk(\"%s\") \tclk_get error\n",
|
||||
__func__, clk_name);
|
||||
} else
|
||||
clk_debug("%s: clk(\"%s\") \tclk_get success\n",
|
||||
__func__, __clk_get_name(clk));
|
||||
|
||||
/* TEST: clk_set_rate */
|
||||
if (clk->ops->set_rate) {
|
||||
if (0 != clk_set_rate(clk, rate)) {
|
||||
clk_err("%s: clk(\"%s\") \tclk_set_rate error\n",
|
||||
__func__, clk_name);
|
||||
} else {
|
||||
clk_debug("%s: clk(\"%s\") \tclk_set_rate success\n",
|
||||
__func__, __clk_get_name(clk));
|
||||
}
|
||||
} else {
|
||||
clk_debug("%s: clk(\"%s\") have no set ops\n",
|
||||
__func__, clk->name);
|
||||
}
|
||||
|
||||
for (i = 0; i * 4 <= 0xf4; i++) {
|
||||
if (i % 4 == 0)
|
||||
printk("\n%s: \t[0x%08x]: ",
|
||||
__func__, 0x20000000 + i * 4);
|
||||
printk("%08x ", readl(reg_start + i * 4));
|
||||
}
|
||||
printk("\n\n");
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rk_clk_test);
|
||||
#else
|
||||
void rk_clk_test(void){};
|
||||
EXPORT_SYMBOL_GPL(rk_clk_test);
|
||||
#endif
|
||||
extern void clk_dump_tree(void);
|
||||
static void __init rkclk_init(struct device_node *np)
|
||||
{
|
||||
struct device_node *node;
|
||||
struct rkclk *rkclk;
|
||||
|
||||
for_each_available_child_of_node(np, node) {
|
||||
|
||||
if (!ERR_PTR(of_property_match_string(node,
|
||||
"compatible",
|
||||
"fixed-clock"))) {
|
||||
continue;
|
||||
|
||||
} else if (!ERR_PTR(of_property_match_string(node,
|
||||
"compatible",
|
||||
"rockchip,rk-pll-cons"))) {
|
||||
if (ERR_PTR(rkclk_init_pllcon(node))) {
|
||||
clk_err("%s: init pll clk err\n", __func__);
|
||||
return ;
|
||||
}
|
||||
|
||||
} else if (!ERR_PTR(of_property_match_string(node,
|
||||
"compatible",
|
||||
"rockchip,rk-sel-cons"))) {
|
||||
if (ERR_PTR(rkclk_init_selcon(node))) {
|
||||
clk_err("%s: init sel cons err\n", __func__);
|
||||
return ;
|
||||
}
|
||||
|
||||
} else if (!ERR_PTR(of_property_match_string(node,
|
||||
"compatible",
|
||||
"rockchip,rk-gate-cons"))) {
|
||||
if (ERR_PTR(rkclk_init_gatecon(node))) {
|
||||
clk_err("%s: init gate cons err\n", __func__);
|
||||
return ;
|
||||
}
|
||||
|
||||
} else {
|
||||
clk_err("%s: unknown\n", __func__);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
#if 0
|
||||
list_for_each_entry(rkclk, &rk_clks, node) {
|
||||
int i;
|
||||
clk_debug("%s: clkname = %s; type=%d\n",
|
||||
__func__, rkclk->clk_name,
|
||||
rkclk->clk_type);
|
||||
if (rkclk->pll_info) {
|
||||
clk_debug("\t\tpll: name=%s, parent=%s\n",
|
||||
rkclk->pll_info->clk_name,
|
||||
rkclk->pll_info->parent_name);
|
||||
}
|
||||
if (rkclk->mux_info) {
|
||||
for (i = 0; i < rkclk->mux_info->parent_num; i++)
|
||||
clk_debug("\t\tmux name=%s, parent: %s\n",
|
||||
rkclk->mux_info->clk_name,
|
||||
rkclk->mux_info->parent_names[i]);
|
||||
}
|
||||
if (rkclk->div_info) {
|
||||
clk_debug("\t\tdiv name=%s\n",
|
||||
rkclk->div_info->clk_name);
|
||||
}
|
||||
if (rkclk->frac_info) {
|
||||
clk_debug("\t\tfrac name=%s\n",
|
||||
rkclk->frac_info->clk_name);
|
||||
}
|
||||
if (rkclk->gate_info) {
|
||||
clk_debug("\t\tgate name=%s, \taddr=%08x, \tshift=%d\n",
|
||||
rkclk->gate_info->clk_name,
|
||||
(u32)rkclk->gate_info->addr,
|
||||
rkclk->gate_info->shift);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
list_for_each_entry(rkclk, &rk_clks, node) {
|
||||
rkclk_register(rkclk);
|
||||
}
|
||||
/* check clock parents init */
|
||||
list_for_each_entry(rkclk, &rk_clks, node) {
|
||||
|
||||
struct clk *clk;
|
||||
int i = 0;
|
||||
const char *clk_name = rkclk->clk_name;
|
||||
clk = clk_get(NULL, clk_name);
|
||||
if (IS_ERR(clk)) {
|
||||
clk_err("%s: clk(\"%s\") \tclk_get error\n",
|
||||
__func__, clk_name);
|
||||
continue;
|
||||
} else {
|
||||
clk_debug("%s: clk(\"%s\") \tclk_get success\n",
|
||||
__func__, __clk_get_name(clk));
|
||||
}
|
||||
|
||||
if (clk->parents) {
|
||||
for (i = 0; i < clk->num_parents; i++) {
|
||||
if (clk->parents[i])
|
||||
clk_debug("\t\tclk(\"%s\"): init parent:%s\n",
|
||||
clk->name,
|
||||
clk->parents[i]->name);
|
||||
else {
|
||||
clk->parents[i] = clk_get(NULL, clk->parent_names[i]);
|
||||
clk_debug("\t\tclk(\"%s\"): init parent:%s\n",
|
||||
clk->name,
|
||||
clk->parents[i]->name);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
clk_debug("\t\tNOT A MUX CLK, parent num=%d\n", clk->num_parents);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CLK_OF_DECLARE(rk_clocks, "rockchip,rk-clock-regs", rkclk_init);
|
||||
29
drivers/clk/rockchip/clkops-dtsi.h
Normal file
29
drivers/clk/rockchip/clkops-dtsi.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#ifndef __RK_CLKOPS_DTSI_H
|
||||
#define __RK_CLKOPS_DTSI_H
|
||||
|
||||
/* rate_ops index */
|
||||
#define CLKOPS_RATE_MUX_DIV 0
|
||||
#define CLKOPS_RATE_EVENDIV 1
|
||||
#define CLKOPS_RATE_DCLK_LCDC 2
|
||||
#define CLKOPS_RATE_CIFOUT 3
|
||||
#define CLKOPS_RATE_I2S_FRAC 4
|
||||
#define CLKOPS_RATE_I2S 5
|
||||
#define CLKOPS_RATE_HSADC_FRAC 6
|
||||
#define CLKOPS_RATE_UART_FRAC 7
|
||||
#define CLKOPS_RATE_UART 8
|
||||
#define CLKOPS_RATE_HSADC 9
|
||||
#define CLKOPS_RATE_MAC_REF 10
|
||||
#define CLKOPS_TABLE_END ~0
|
||||
|
||||
#ifndef BIT
|
||||
#define BIT(nr) (1 << (nr))
|
||||
#endif
|
||||
#define CLK_DIVIDER_PLUS_ONE (0)
|
||||
#define CLK_DIVIDER_ONE_BASED BIT(0)
|
||||
#define CLK_DIVIDER_POWER_OF_TWO BIT(1)
|
||||
#define CLK_DIVIDER_ALLOW_ZERO BIT(2)
|
||||
#define CLK_DIVIDER_FIXED BIT(3)
|
||||
#define CLK_DIVIDER_USER_DEFINE BIT(4)
|
||||
/* CLK_DIVIDER_MAX defined the bits been used above */
|
||||
#define CLK_DIVIDER_MASK (0x1F)
|
||||
#endif /* __RK_CLKOPS_H */
|
||||
Reference in New Issue
Block a user