From 24903a6bd082bea5d7fb9ce030c72d5feff23a1b Mon Sep 17 00:00:00 2001 From: chenxing Date: Tue, 7 May 2013 10:02:28 +0800 Subject: [PATCH] rk3188 plus: add delay line support --- arch/arm/mach-rk3188/Makefile | 1 + arch/arm/mach-rk3188/clock_data.c | 25 ++++- arch/arm/mach-rk3188/delayline.c | 161 ++++++++++++++++++++++++++++++ 3 files changed, 183 insertions(+), 4 deletions(-) create mode 100644 arch/arm/mach-rk3188/delayline.c diff --git a/arch/arm/mach-rk3188/Makefile b/arch/arm/mach-rk3188/Makefile index f747a9eae6db..141483d89965 100755 --- a/arch/arm/mach-rk3188/Makefile +++ b/arch/arm/mach-rk3188/Makefile @@ -18,6 +18,7 @@ obj-$(CONFIG_CPU_IDLE) += ../mach-rk30/cpuidle.o obj-$(CONFIG_CPU_FREQ) += cpufreq.o obj-$(CONFIG_DVFS) += dvfs.o obj-y += board.o +obj-y += delayline.o board-$(CONFIG_MACH_RK3188_FPGA) += board-rk3188-fpga.o board-$(CONFIG_MACH_RK3188_TB) += ../mach-rk30/board-rk3168-tb.o diff --git a/arch/arm/mach-rk3188/clock_data.c b/arch/arm/mach-rk3188/clock_data.c index 3f9d2420b96e..4abed8976425 100755 --- a/arch/arm/mach-rk3188/clock_data.c +++ b/arch/arm/mach-rk3188/clock_data.c @@ -683,7 +683,6 @@ static unsigned long plus_pll_clk_recalc(u32 pll_id, unsigned long parent_rate) } static unsigned long plus_plls_clk_recalc(struct clk *clk) { - DVFS_DBG("%s: for rk3188 plus\n", __func__); return plus_pll_clk_recalc(clk->pll->id, clk->parent->rate); } @@ -767,7 +766,6 @@ static int plus_gpll_clk_set_rate(struct clk *c, unsigned long rate) { struct _pll_data *pll_data = c->pll; struct pll_clk_set *clk_set = (struct pll_clk_set *)pll_data->table; - DVFS_DBG("%s: for rk3188 plus\n", __func__); while(clk_set->rate) { if (clk_set->rate == rate) { @@ -911,7 +909,6 @@ static int plus_cpll_clk_set_rate(struct clk *c, unsigned long rate) struct pll_clk_set *clk_set = (struct pll_clk_set *)pll_data->table; struct pll_clk_set temp_clk_set; u32 clk_nr, clk_nf, clk_no; - DVFS_DBG("%s: for rk3188 plus\n", __func__); while(clk_set->rate) { if (clk_set->rate == rate) { @@ -1113,7 +1110,6 @@ static int plus_arm_pll_clk_set_rate(struct clk *clk, unsigned long rate) u32 pll_id = clk->pll->id; u32 temp_div; u32 old_aclk_div = 0, new_aclk_div; - DVFS_DBG("%s: for rk3188 plus\n", __func__); ps = arm_pll_clk_get_best_pll_set(rate, (struct apll_clk_set *)clk->pll->table); @@ -1571,6 +1567,26 @@ static struct clk atclk_cpu = { .gate_idx = CLK_GATE_ATCLK_CPU, }; +/* + * This clock's base is 0x20009000, not RK30_CRU_BASE + * .set_rate/.set_parent/.recalc/.clksel_con/.parents + * will be assignment in 's prob function + * + * static struct clk *clk_delayline_parents[2] = {&xin24m, &aclk_cpu}; + * + * */ +static struct clk clk_delayline = { + .name = "clk_delayline", + .parent = &xin24m, + .recalc = NULL, + .set_rate = NULL, + .set_parent = NULL, + .get_parent = NULL, + .clksel_con = 0xffff, + CRU_DIV_SET(0xf, 0, 16), + CRU_SRC_SET(1, 4), +}; + /* GPU setting */ static int clk_gpu_set_rate(struct clk *clk, unsigned long rate) { @@ -2802,6 +2818,7 @@ static struct clk_lookup clks[] = { CLK(NULL, "atclk_cpu", &atclk_cpu), CLK(NULL, "hclk_cpu", &hclk_cpu), CLK(NULL, "ahb2apb_cpu", &ahb2apb_cpu), + CLK(NULL, "delayline", &clk_delayline), CLK(NULL, "gpu", &aclk_gpu), diff --git a/arch/arm/mach-rk3188/delayline.c b/arch/arm/mach-rk3188/delayline.c new file mode 100644 index 000000000000..a16d4dd0def4 --- /dev/null +++ b/arch/arm/mach-rk3188/delayline.c @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2013 ROCKCHIP, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define KHZ (1000UL) +#define MHZ (1000UL * 1000UL) +#define RK30_DELAYLINE_BASE (RK30_TIMER0_BASE + 0x1000) +#define delayline_readl(offset) readl_relaxed(RK30_DELAYLINE_BASE + offset) +#define delayline_writel(val, offset) writel_relaxed(val, RK30_DELAYLINE_BASE + offset) + +#define get_delayline_bits(con, mask, shift)\ + ((delayline_readl((con)) >> (shift)) & (mask)) +#define set_delayline_bits(val, mask, shift, con)\ + delayline_writel(((delayline_readl(con) & (~(mask << shift))) | (val << shift)), con) +#define set_delayline_bits_w_msk(val, mask, shift, con)\ + delayline_writel(((mask) << (shift + 16)) | ((val) << (shift)), (con)) + +#define DELAYLINE_CON0 (0x0000) +#define DELAYLINE_CON1 (0x0004) +#define DELAYLINE_STATUS (0x0008) + +#if 1 +#define DELAYLINE_DBG(fmt, args...) printk(KERN_DEBUG "DELAYLINE_DBG:\t"fmt, ##args) +#else +#define DELAYLINE_DBG(fmt, args...) do {} while(0) +#endif + +#define DELAYLINE_ERR(fmt, args...) printk("DELAYLINE_ERR:\t"fmt, ##args) + +static struct clk *clk_delayline = NULL, *clk_xin24m = NULL, *clk_aclk_cpu = NULL; +static struct clk *clk_delayline_parents[2]; + +static unsigned long clk_delayline_recalc_freediv(struct clk *clk) +{ + u32 div = get_delayline_bits(clk->clksel_con, clk->div_mask, clk->div_shift) + 1; + unsigned long rate = clk->parent->rate / div; + + DELAYLINE_DBG("%s new clock rate is %lu (div %u)\n", clk->name, rate, div); + return rate; +} + +static int clk_delayline_set_rate_freediv(struct clk *clk, unsigned long rate) +{ + u32 div; + for (div = 0; div < clk->div_max; div++) { + u32 new_rate = clk->parent->rate / (div + 1); + if (new_rate <= rate) { + set_delayline_bits(div, clk->div_mask, clk->div_shift, clk->clksel_con); + DELAYLINE_DBG("clksel_set_rate_freediv for clock %s to rate %ld (div %d)\n", + clk->name, rate, div + 1); + return 0; + } + } + return -ENOENT; +} + +static int clk_delayline_set_parent(struct clk *clk, struct clk *parent) +{ + u32 i; + if (unlikely(!clk->parents)) + return -EINVAL; + for (i = 0; (i < clk->parents_num); i++) { + if (clk->parents[i] != parent) + continue; + set_delayline_bits(i, clk->src_mask, clk->src_shift, clk->clksel_con); + return 0; + } + return -EINVAL; +} + +static struct clk *clk_delayline_get_parent(struct clk *clk) { + return clk->parents[(delayline_readl(clk->clksel_con) >> clk->src_shift) & clk->src_mask]; +} + +int rk3188_get_delayline_value(void) +{ + struct clk *clk = clk_delayline; + clk->set_parent(clk, clk_aclk_cpu); + clk->set_rate(clk, 75 * MHZ); + + set_delayline_bits(0x4, 0xff, 8, DELAYLINE_CON1); + set_delayline_bits(0xff, 0xff, 0, DELAYLINE_CON1); + set_delayline_bits(0x1, 0x1, 5, clk->clksel_con); + mdelay(1); + set_delayline_bits(0x0, 0x1, 5, clk->clksel_con); + mdelay(1); + + while(get_delayline_bits(DELAYLINE_STATUS, 0x1, 8) == 0); + + return delayline_readl(DELAYLINE_STATUS) & 0xff; +} +EXPORT_SYMBOL(rk3188_get_delayline_value); + +static int __init rk3188_delayline_init(void) +{ + DELAYLINE_DBG("%s......\n", __func__); + + clk_delayline = clk_get(NULL, "delayline"); + if (IS_ERR_OR_NULL(clk_delayline)) { + DELAYLINE_ERR("%s: can not get clock 'delay_line'\n" + "\tplease check 'mach-rk3188/clock_data.c' had 'clk_delayline' or not\n", __func__); + return -EINVAL; + } + + clk_xin24m = clk_get(NULL, "xin24m"); + if (IS_ERR_OR_NULL(clk_xin24m)) { + DELAYLINE_ERR("%s: can not get parent clock 'xin24m'\n", __func__); + return -EINVAL; + } + clk_delayline_parents[0] = clk_xin24m; + + clk_aclk_cpu = clk_get(NULL, "aclk_cpu"); + if (IS_ERR_OR_NULL(clk_aclk_cpu)) { + DELAYLINE_ERR("%s: can not get parent clock 'aclk_cpu'\n", __func__); + return -EINVAL; + } + clk_delayline_parents[1] = clk_aclk_cpu; + + clk_delayline->clksel_con = DELAYLINE_CON0; + clk_delayline->recalc = clk_delayline_recalc_freediv; + clk_delayline->set_rate = clk_delayline_set_rate_freediv; + clk_delayline->set_parent = clk_delayline_set_parent; + clk_delayline->get_parent = clk_delayline_get_parent; + clk_delayline->parents = clk_delayline_parents; + clk_delayline->parents_num = ARRAY_SIZE(clk_delayline_parents); + + clk_delayline->parent = clk_delayline->get_parent(clk_delayline); + clk_delayline->rate = clk_delayline->recalc(clk_delayline); +#if 1 + DELAYLINE_DBG("clksel_con=%08x\n", clk_delayline->clksel_con); + DELAYLINE_DBG("current parent=%s\n", clk_delayline->parent->name); + DELAYLINE_DBG("parents[0]=%s\n", clk_delayline->parents[0]->name); + DELAYLINE_DBG("parents[1]=%s\n", clk_delayline->parents[1]->name); + DELAYLINE_DBG("parents_num=%d\n", clk_delayline->parents_num); +#endif + return 0; +} + +late_initcall(rk3188_delayline_init); + +MODULE_DESCRIPTION("Driver for delayline"); +MODULE_AUTHOR("chenxing, chenxing@rock-chips.com"); +MODULE_LICENSE("GPL"); -- 2.34.1