From efd44b941bc6518ea4cfbfad2ab5026b1c7595d2 Mon Sep 17 00:00:00 2001 From: =?utf8?q?=E9=BB=84=E6=B6=9B?= Date: Thu, 2 Dec 2010 16:14:15 +0800 Subject: [PATCH] rk29: clock: add common init, add uart/i2s set rate support --- arch/arm/mach-rk29/clock.c | 402 +++++++++++++++++++++++++++++++------ 1 file changed, 341 insertions(+), 61 deletions(-) diff --git a/arch/arm/mach-rk29/clock.c b/arch/arm/mach-rk29/clock.c index 6ba71e9602c5..8c2b35dee4e8 100755 --- a/arch/arm/mach-rk29/clock.c +++ b/arch/arm/mach-rk29/clock.c @@ -32,10 +32,11 @@ #include /* CRU PLL CON */ -#define PLL_BAND (0x01 << 16) +#define PLL_HIGH_BAND (0x01 << 16) +#define PLL_LOW_BAND (0x00 << 16) #define PLL_PD (0x01 << 15) -#define PLL_CLKR(i) ((((i) - 1) << & 0x1f) << 10) +#define PLL_CLKR(i) ((((i) - 1) & 0x1f) << 10) #define PLL_NR(v) ((((v) >> 10) & 0x1f) + 1) #define PLL_CLKF(i) ((((i) - 1) & 0x7f) << 3) @@ -82,6 +83,8 @@ #define cru_writel(v, offset) writel(v, RK29_CRU_BASE + offset) #define cru_writel_force(v, offset) do { u32 _v = v; u32 _count = 5; do { cru_writel(_v, offset); } while (cru_readl(offset) != _v && _count--); } while (0) /* huangtao: when write CRU_xPLL_CON, first time may failed, so try again. unknown why. */ +#define regfile_readl(offset) readl(RK29_GRF_BASE + offset) + struct clk { struct list_head node; const char *name; @@ -108,9 +111,10 @@ struct clk { struct clk **parents; }; -int ddr_disabled = 0; - -static void __clk_disable(struct clk *clk); +static int clk_enable_nolock(struct clk *clk); +static void clk_disable_nolock(struct clk *clk); +static int clk_set_rate_nolock(struct clk *clk, unsigned long rate); +static int clk_set_parent_nolock(struct clk *clk, struct clk *parent); static void __clk_reparent(struct clk *child, struct clk *parent); static void __propagate_rate(struct clk *tclk); @@ -267,6 +271,34 @@ static struct clk otgphy1_clkin = { }; +static void delay_500ns(void) +{ + int delay = 2000; + while (delay--) + barrier(); +} + + +#define PERIPH_PLL_IDX 0 +#define CODEC_PLL_IDX 1 +#define ARM_PLL_IDX 2 +#define DDR_PLL_IDX 3 + +#define GRF_SOC_CON0 0xbc +static void pll_wait_lock(int pll_idx, int delay) +{ + u32 bit = 0x2000000u << pll_idx; + while (delay > 0) { + if (regfile_readl(GRF_SOC_CON0) & bit) + break; + delay--; + } + if (delay == 0) { + pr_warning("wait pll bit 0x%x time out!\n", bit); + } +} + + static unsigned long arm_pll_clk_recalc(struct clk *clk) { unsigned long rate; @@ -345,8 +377,8 @@ static unsigned long periph_pll_clk_recalc(struct clk *clk) { unsigned long rate; - if ((cru_readl(CRU_MODE_CON) & CRU_CODEC_MODE_MASK) == CRU_CODEC_MODE_NORMAL) { - u32 v = cru_readl(CRU_CPLL_CON); + if ((cru_readl(CRU_MODE_CON) & CRU_PERIPH_MODE_MASK) == CRU_PERIPH_MODE_NORMAL) { + u32 v = cru_readl(CRU_PPLL_CON); u64 rate64 = (u64) clk->parent->rate * PLL_NF(v); do_div(rate64, PLL_NR(v)); rate = rate64 >> PLL_NO_SHIFT(v); @@ -359,10 +391,41 @@ static unsigned long periph_pll_clk_recalc(struct clk *clk) return rate; } +static int periph_pll_clk_set_rate(struct clk *clk, unsigned long rate) +{ + /* 624M: high-band, NR=1, NF=26, NO=1 */ + u32 v = PLL_HIGH_BAND | PLL_CLKR(1) | PLL_CLKF(26) | PLL_NO_1; + + /* enter slow mode */ + cru_writel((cru_readl(CRU_MODE_CON) & ~CRU_PERIPH_MODE_MASK) | CRU_PERIPH_MODE_SLOW, CRU_MODE_CON); + + pll_wait_lock(PERIPH_PLL_IDX, 2400000); + + /* power down */ + cru_writel(cru_readl(CRU_PPLL_CON) | PLL_PD, CRU_PPLL_CON); + + delay_500ns(); + + cru_writel(v | PLL_PD, CRU_PPLL_CON); + + delay_500ns(); + + /* power up */ + cru_writel(v, CRU_PPLL_CON); + + pll_wait_lock(PERIPH_PLL_IDX, 2400000); + + /* enter normal mode */ + cru_writel((cru_readl(CRU_MODE_CON) & ~CRU_PERIPH_MODE_MASK) | CRU_PERIPH_MODE_NORMAL, CRU_MODE_CON); + + return 0; +} + static struct clk periph_pll_clk = { .name = "periph_pll", .parent = &xin24m, .recalc = periph_pll_clk_recalc, + .set_rate = periph_pll_clk_set_rate, }; @@ -547,10 +610,54 @@ static struct clk clk_spdif_div = { .parents = clk_i2s_div_parents, }; +static int clk_i2s_frac_div_set_rate(struct clk *clk, unsigned long rate) +{ + u16 numerator, denominator; + + switch (rate) { + case 8192000: /* 624*128/9750 */ + numerator = 128; + denominator = 9750; + break; + case 11289600: /* 624*294/16250 */ + numerator = 294; + denominator = 16250; + break; + case 12288000: /* 624*64/3250 */ + numerator = 64; + denominator = 3250; + break; + case 22579200: /* 624*294/8125 */ + numerator = 294; + denominator = 8125; + break; + case 24576000: /* 624*64/1625 */ + numerator = 64; + denominator = 1625; + break; + case 45158400: /* 624*588/8125 */ + numerator = 588; + denominator = 8125; + break; + case 49152000: /* 624*128/1625 */ + numerator = 128; + denominator = 1625; + break; + default: + return -ENOENT; + } + + pr_debug("set clock %s to rate %ld (%d/%d)\n", clk->name, rate, numerator, denominator); + cru_writel((u32)numerator << 16 | denominator, clk->clksel_con); + + return 0; +} + static struct clk clk_i2s0_frac_div = { .name = "i2s0_frac_div", .parent = &clk_i2s0_div, .recalc = clksel_recalc_frac, + .set_rate = clk_i2s_frac_div_set_rate, .clksel_con = CRU_CLKSEL3_CON, }; @@ -558,6 +665,7 @@ static struct clk clk_i2s1_frac_div = { .name = "i2s1_frac_div", .parent = &clk_i2s1_div, .recalc = clksel_recalc_frac, + .set_rate = clk_i2s_frac_div_set_rate, .clksel_con = CRU_CLKSEL4_CON, }; @@ -565,13 +673,34 @@ static struct clk clk_spdif_frac_div = { .name = "spdif_frac_div", .parent = &clk_spdif_div, .recalc = clksel_recalc_frac, + .set_rate = clk_i2s_frac_div_set_rate, .clksel_con = CRU_CLKSEL5_CON, }; +static int i2s_set_rate(struct clk *clk, unsigned long rate) +{ + int ret; + struct clk *parent; + + if (rate == 12000000) { + parent = &clk_12m; + } else { + parent = clk->parents[1]; /* frac div */ + ret = clk_set_rate_nolock(parent, rate); + if (ret) + return ret; + } + if (clk->parent != parent) + clk_set_parent_nolock(clk, parent); + + return ret; +} + static struct clk *clk_i2s0_parents[4] = { &clk_i2s0_div, &clk_i2s0_frac_div, &clk_12m, &xin24m }; static struct clk clk_i2s0 = { .name = "i2s0", + .set_rate = i2s_set_rate, .clksel_con = CRU_CLKSEL2_CON, .clksel_parent_mask = 3, .clksel_parent_shift = 8, @@ -582,6 +711,7 @@ static struct clk *clk_i2s1_parents[4] = { &clk_i2s1_div, &clk_i2s1_frac_div, &c static struct clk clk_i2s1 = { .name = "i2s1", + .set_rate = i2s_set_rate, .clksel_con = CRU_CLKSEL2_CON, .clksel_parent_mask = 3, .clksel_parent_shift = 18, @@ -592,6 +722,7 @@ static struct clk *clk_spdif_parents[4] = { &clk_spdif_div, &clk_spdif_frac_div, static struct clk clk_spdif = { .name = "spdif", + .set_rate = i2s_set_rate, .clksel_con = CRU_CLKSEL2_CON, .clksel_parent_mask = 3, .clksel_parent_shift = 28, @@ -754,6 +885,96 @@ static struct clk clk_ddr = { }; +static int clk_uart_set_rate(struct clk *clk, unsigned long rate) +{ + int ret; + struct clk *parent; + struct clk *clk_div = clk->parents[0]; + + switch (rate) { + case 24000000: /* 1.5M/0.5M/50/75/150/200/300/600/1200/2400 */ + parent = clk->parents[2]; /* xin24m */ + break; + case 48000000: /* 3M */ + case 16000000: /* 1M */ + case 8125*16: /* 4800 */ + parent = clk_div; + break; + default: + parent = clk->parents[1]; /* frac div */ + /* reset div to 1 */ + ret = clk_set_rate_nolock(clk_div, clk_div->parent->rate); + if (ret) + return ret; + break; + } + + if (parent->set_rate) { + ret = clk_set_rate_nolock(parent, rate); + if (ret) + return ret; + } + + if (clk->parent != parent) + clk_set_parent_nolock(clk, parent); + + return 0; +} + +static int clk_uart_frac_div_set_rate(struct clk *clk, unsigned long rate) +{ + u16 numerator, denominator; + + switch (rate) { + case 9600*16: + numerator = 2; + denominator = 8125; + break; + case 19200*16: + numerator = 4; + denominator = 8125; + break; + case 38400*16: + numerator = 8; + denominator = 8125; + break; + case 57600*16: + numerator = 12; + denominator = 8125; + break; + case 115200*16: + numerator = 24; + denominator = 8125; + break; + case 230400*16: + numerator = 48; + denominator = 8125; + break; + case 460800*16: + numerator = 96; + denominator = 8125; + break; + case 576000*16: + numerator = 24; + denominator = 1625; + break; + case 921600*16: + numerator = 192; + denominator = 8125; + break; + case 1152000*16: + numerator = 48; + denominator = 1625; + break; + default: + return -ENOENT; + } + + pr_debug("set clock %s to rate %ld (%d/%d)\n", clk->name, rate, numerator, denominator); + cru_writel((u32)numerator << 16 | denominator, clk->clksel_con); + + return 0; +} static struct clk *clk_uart_src_parents[8] = { &periph_pll_clk, &ddr_pll_clk, &codec_pll_clk, &arm_pll_clk, &otgphy0_clkin, &otgphy1_clkin }; static struct clk clk_uart01_src = { @@ -778,6 +999,7 @@ static struct clk clk_uart0_frac_div = { .name = "uart0_frac_div", .parent = &clk_uart0_div, .recalc = clksel_recalc_frac, + .set_rate = clk_uart_frac_div_set_rate, .clksel_con = CRU_CLKSEL10_CON, }; @@ -786,6 +1008,7 @@ static struct clk *clk_uart0_parents[4] = { &clk_uart0_div, &clk_uart0_frac_div, static struct clk clk_uart0 = { .name = "uart0", .mode = gate_mode, + .set_rate = clk_uart_set_rate, .gate_idx = CLK_GATE_UART0, .clksel_con = CRU_CLKSEL8_CON, .clksel_parent_mask = 3, @@ -807,6 +1030,7 @@ static struct clk clk_uart1_frac_div = { .name = "uart1_frac_div", .parent = &clk_uart1_div, .recalc = clksel_recalc_frac, + .set_rate = clk_uart_frac_div_set_rate, .clksel_con = CRU_CLKSEL11_CON, }; @@ -815,6 +1039,7 @@ static struct clk *clk_uart1_parents[4] = { &clk_uart1_div, &clk_uart1_frac_div, static struct clk clk_uart1 = { .name = "uart1", .mode = gate_mode, + .set_rate = clk_uart_set_rate, .gate_idx = CLK_GATE_UART1, .clksel_con = CRU_CLKSEL8_CON, .clksel_parent_mask = 3, @@ -844,6 +1069,7 @@ static struct clk clk_uart2_frac_div = { .name = "uart2_frac_div", .parent = &clk_uart2_div, .recalc = clksel_recalc_frac, + .set_rate = clk_uart_frac_div_set_rate, .clksel_con = CRU_CLKSEL12_CON, }; @@ -852,6 +1078,7 @@ static struct clk *clk_uart2_parents[4] = { &clk_uart2_div, &clk_uart2_frac_div, static struct clk clk_uart2 = { .name = "uart2", .mode = gate_mode, + .set_rate = clk_uart_set_rate, .gate_idx = CLK_GATE_UART2, .clksel_con = CRU_CLKSEL9_CON, .clksel_parent_mask = 3, @@ -873,6 +1100,7 @@ static struct clk clk_uart3_frac_div = { .name = "uart3_frac_div", .parent = &clk_uart3_div, .recalc = clksel_recalc_frac, + .set_rate = clk_uart_frac_div_set_rate, .clksel_con = CRU_CLKSEL13_CON, }; @@ -881,6 +1109,7 @@ static struct clk *clk_uart3_parents[4] = { &clk_uart3_div, &clk_uart3_frac_div, static struct clk clk_uart3 = { .name = "uart3", .mode = gate_mode, + .set_rate = clk_uart_set_rate, .gate_idx = CLK_GATE_UART3, .clksel_con = CRU_CLKSEL9_CON, .clksel_parent_mask = 3, @@ -1265,13 +1494,13 @@ static DEFINE_SPINLOCK(clockfw_lock); #define LOCK() do { WARN_ON(in_irq()); if (!irqs_disabled()) spin_lock_bh(&clockfw_lock); } while (0) #define UNLOCK() do { if (!irqs_disabled()) spin_unlock_bh(&clockfw_lock); } while (0) -static int __clk_enable(struct clk *clk) +static int clk_enable_nolock(struct clk *clk) { int ret = 0; if (clk->usecount == 0) { if (clk->parent) { - ret = __clk_enable(clk->parent); + ret = clk_enable_nolock(clk->parent); if (ret) return ret; } @@ -1280,7 +1509,7 @@ static int __clk_enable(struct clk *clk) ret = clk->mode(clk, 1); if (ret) { if (clk->parent) - __clk_disable(clk->parent); + clk_disable_nolock(clk->parent); return ret; } } @@ -1299,21 +1528,27 @@ int clk_enable(struct clk *clk) return -EINVAL; LOCK(); - ret = __clk_enable(clk); + ret = clk_enable_nolock(clk); UNLOCK(); return ret; } EXPORT_SYMBOL(clk_enable); -static void __clk_disable(struct clk *clk) +static void clk_disable_nolock(struct clk *clk) { + if (clk->usecount == 0) { + printk(KERN_ERR "Trying disable clock %s with 0 usecount\n", clk->name); + WARN_ON(1); + return; + } + if (--clk->usecount == 0) { if (clk->mode) clk->mode(clk, 0); pr_debug("%s disabled\n", clk->name); if (clk->parent) - __clk_disable(clk->parent); + clk_disable_nolock(clk->parent); } } @@ -1323,15 +1558,7 @@ void clk_disable(struct clk *clk) return; LOCK(); - if (clk->usecount == 0) { - printk(KERN_ERR "Trying disable clock %s with 0 usecount\n", clk->name); - WARN_ON(1); - goto out; - } - - __clk_disable(clk); - -out: + clk_disable_nolock(clk); UNLOCK(); } EXPORT_SYMBOL(clk_disable); @@ -1350,7 +1577,7 @@ EXPORT_SYMBOL(clk_get_rate); *-------------------------------------------------------------------------*/ /* Given a clock and a rate apply a clock specific rounding function */ -static long __clk_round_rate(struct clk *clk, unsigned long rate) +static long clk_round_rate_nolock(struct clk *clk, unsigned long rate) { if (clk->round_rate) return clk->round_rate(clk, rate); @@ -1369,39 +1596,50 @@ long clk_round_rate(struct clk *clk, unsigned long rate) return ret; LOCK(); - ret = __clk_round_rate(clk, rate); + ret = clk_round_rate_nolock(clk, rate); UNLOCK(); return ret; } EXPORT_SYMBOL(clk_round_rate); -/* Set the clock rate for a clock source */ -static int __clk_set_rate(struct clk *clk, unsigned long rate) +static void __clk_recalc(struct clk *clk) { - int ret = -EINVAL; + if (unlikely(clk->flags & RATE_FIXED)) + return; + if (clk->recalc) + clk->rate = clk->recalc(clk); + else if (clk->parent) + clk->rate = clk->parent->rate; + pr_debug("%s new clock rate is %lu\n", clk->name, clk->rate); +} + +static int clk_set_rate_nolock(struct clk *clk, unsigned long rate) +{ + int ret; + + if (rate == clk->rate) + return 0; pr_debug("set_rate for clock %s to rate %ld\n", clk->name, rate); if (clk->flags & CONFIG_PARTICIPANT) return -EINVAL; - if (clk->set_rate) - ret = clk->set_rate(clk, rate); + if (!clk->set_rate) + return -EINVAL; - return ret; -} + ret = clk->set_rate(clk, rate); -static void __clk_recalc(struct clk *clk) -{ - if (unlikely(clk->flags & RATE_FIXED)) - return; - if (clk->recalc) - clk->rate = clk->recalc(clk); - else if (clk->parent) - clk->rate = clk->parent->rate; + if (ret == 0) { + __clk_recalc(clk); + __propagate_rate(clk); + } + + return ret; } +/* Set the clock rate for a clock source */ int clk_set_rate(struct clk *clk, unsigned long rate) { int ret = -EINVAL; @@ -1410,21 +1648,45 @@ int clk_set_rate(struct clk *clk, unsigned long rate) return ret; LOCK(); - if (rate == clk->rate) { - ret = 0; - goto out; - } - ret = __clk_set_rate(clk, rate); + ret = clk_set_rate_nolock(clk, rate); + UNLOCK(); + + return ret; +} +EXPORT_SYMBOL(clk_set_rate); + +static int clk_set_parent_nolock(struct clk *clk, struct clk *parent) +{ + int ret; + int enabled = clk->usecount > 0; + struct clk *old_parent = clk->parent; + + if (clk->parent == parent) + return 0; + + /* if clk is already enabled, enable new parent first and disable old parent later. */ + if (enabled) + clk_enable_nolock(parent); + + if (clk->set_parent) + ret = clk->set_parent(clk, parent); + else + ret = clksel_set_parent(clk, parent); + if (ret == 0) { + /* OK */ + __clk_reparent(clk, parent); __clk_recalc(clk); __propagate_rate(clk); + if (enabled) + clk_disable_nolock(old_parent); + } else { + if (enabled) + clk_disable_nolock(parent); } -out: - UNLOCK(); return ret; } -EXPORT_SYMBOL(clk_set_rate); int clk_set_parent(struct clk *clk, struct clk *parent) { @@ -1437,17 +1699,9 @@ int clk_set_parent(struct clk *clk, struct clk *parent) return ret; LOCK(); - if (clk->usecount == 0) { - if (clk->set_parent) - ret = clk->set_parent(clk, parent); - else - ret = clksel_set_parent(clk, parent); - if (ret == 0) { - __clk_reparent(clk, parent); - __clk_recalc(clk); - __propagate_rate(clk); - } - } else + if (clk->usecount == 0) + ret = clk_set_parent_nolock(clk, parent); + else ret = -EBUSY; UNLOCK(); @@ -1493,7 +1747,7 @@ static LIST_HEAD(root_clks); * clock's .recalc is set correctly, should also propagate their rates. * Called at init. */ -static void recalculate_root_clocks(void) +static void clk_recalculate_root_clocks_nolock(void) { struct clk *clkp; @@ -1506,7 +1760,7 @@ static void recalculate_root_clocks(void) void clk_recalculate_root_clocks(void) { LOCK(); - recalculate_root_clocks(); + clk_recalculate_root_clocks_nolock(); UNLOCK(); } @@ -1563,6 +1817,30 @@ static void clk_enable_init_clocks(void) } } +static void rk29_clock_common_init(void) +{ + /* periph pll */ + clk_set_rate_nolock(&periph_pll_clk, 624000000); + clk_set_parent_nolock(&aclk_periph, &periph_pll_clk); + clk_set_parent_nolock(&clk_uhost, &periph_pll_clk); // default + clk_set_rate_nolock(&clk_uhost, 48000000); + clk_set_parent_nolock(&clk_i2s0_div, &periph_pll_clk); // default + clk_set_parent_nolock(&clk_i2s1_div, &periph_pll_clk); // default + clk_set_parent_nolock(&clk_spdif_div, &periph_pll_clk); // default + clk_set_parent_nolock(&clk_spi_src, &periph_pll_clk); // default + clk_set_parent_nolock(&clk_sdmmc_src, &periph_pll_clk); + clk_set_parent_nolock(&clk_uart01_src, &periph_pll_clk); // default + clk_set_parent_nolock(&clk_uart23_src, &periph_pll_clk); // default + clk_set_parent_nolock(&dclk_lcdc_div, &periph_pll_clk); + clk_set_parent_nolock(&aclk_lcdc, &periph_pll_clk); + clk_set_parent_nolock(&aclk_vepu, &periph_pll_clk); // default + clk_set_parent_nolock(&aclk_vdpu, &periph_pll_clk); // default + clk_set_parent_nolock(&clk_gpu, &periph_pll_clk); // default + clk_set_parent_nolock(&aclk_gpu, &periph_pll_clk); // default + clk_set_parent_nolock(&clk_mac_ref_div, &periph_pll_clk); + clk_set_parent_nolock(&clk_hsadc_div, &periph_pll_clk); +} + void __init rk29_clock_init(void) { struct clk_lookup *lk; @@ -1575,7 +1853,9 @@ void __init rk29_clock_init(void) clk_register(lk->clk); } - recalculate_root_clocks(); + clk_recalculate_root_clocks_nolock(); + + rk29_clock_common_init(); printk(KERN_INFO "Clocking rate (apll/dpll/cpll/ppll/core/aclk/hclk/pclk): %ld/%ld/%ld/%ld/%ld/%ld/%ld/%ld MHz\n", arm_pll_clk.rate / 1000000, ddr_pll_clk.rate / 1000000, codec_pll_clk.rate / 1000000, periph_pll_clk.rate / 1000000, -- 2.34.1