diff options
author | Marcel Ziswiler <marcel.ziswiler@toradex.com> | 2014-01-09 10:36:42 +0100 |
---|---|---|
committer | Marcel Ziswiler <marcel.ziswiler@toradex.com> | 2014-01-09 10:36:42 +0100 |
commit | 059910ca282917895bfcbc85595dcec8162b0134 (patch) | |
tree | 43a0de9e7483ab1aa1f053e8ff2c718fe986de63 /arch | |
parent | 4af247f6c360f222f583a7a773be6a204493caa1 (diff) |
colibri_vf61: fix/implement audio clocking
The PLL4 is exclusively used for audio clocking.
While at it implement CKO1 external clock pin.
Diffstat (limited to 'arch')
-rw-r--r-- | arch/arm/mach-mvf/clock.c | 210 | ||||
-rw-r--r-- | arch/arm/mach-mvf/crm_regs.h | 9 |
2 files changed, 194 insertions, 25 deletions
diff --git a/arch/arm/mach-mvf/clock.c b/arch/arm/mach-mvf/clock.c index 7074a1b708b1..127f0c1ef6a8 100644 --- a/arch/arm/mach-mvf/clock.c +++ b/arch/arm/mach-mvf/clock.c @@ -750,25 +750,26 @@ static struct clk pll3_sw_clk = { static unsigned long _clk_audio_video_get_rate(struct clk *clk) { unsigned int div, mfn, mfd; - unsigned long rate; unsigned int parent_rate = clk_get_rate(clk->parent); + unsigned long long ll; void __iomem *pllbase; - unsigned int test_div_sel, control3, post_div = 1; - if (clk == &pll4_audio_main_clk) pllbase = PLL4_AUDIO_BASE_ADDR; else pllbase = PLL6_VIDEO_BASE_ADDR; + /* Multiplication Factor Integer (MFI) */ div = __raw_readl(pllbase) & ANADIG_PLL_SYS_DIV_SELECT_MASK; + /* Multiplication Factor Numerator (MFN) */ mfn = __raw_readl(pllbase + PLL_NUM_DIV_OFFSET); + /* Multiplication Factor Denominator (MFD) */ mfd = __raw_readl(pllbase + PLL_DENOM_DIV_OFFSET); - rate = (parent_rate * div) + ((parent_rate / mfd) * mfn); - rate = rate / post_div; + ll = (unsigned long long)parent_rate * mfn; + do_div(ll, mfd); - return rate; + return (parent_rate * div) + ll; } static int _clk_audio_video_set_rate(struct clk *clk, unsigned long rate) @@ -908,6 +909,55 @@ static struct clk pll6_video_main_clk = { .set_parent = _clk_audio_video_set_parent, }; +static unsigned long _clk_pll4_audio_div_get_rate(struct clk *clk) +{ + u32 reg, div; + unsigned int parent_rate = clk_get_rate(clk->parent); + + reg = __raw_readl(MXC_CCM_CACRR); + div = (((reg & MXC_CCM_CACRR_PLL4_CLK_DIV_MASK) >> + MXC_CCM_CACRR_PLL4_CLK_DIV_OFFSET) + 1) * 2; + if (2 == div) + div = 1; + + return parent_rate / div; +} + +static int _clk_pll4_audio_div_set_rate(struct clk *clk, unsigned long rate) +{ + u32 reg, div; + unsigned int parent_rate = clk_get_rate(clk->parent); + + div = parent_rate / rate; + + /* Make sure rate is not greater than the maximum value for the clock. + * Also prevent a div of 0. + */ + if (0 == div) + div++; + + if (16 < div) + div = 16; + + div /= 2; + if (1 <= div) + div -= 1; + + reg = __raw_readl(MXC_CCM_CACRR); + reg &= ~MXC_CCM_CACRR_PLL4_CLK_DIV_MASK; + reg |= (div << MXC_CCM_CACRR_PLL4_CLK_DIV_OFFSET); + __raw_writel(reg, MXC_CCM_CACRR); + + return 0; +} + +static struct clk pll4_audio_div_clk = { + __INIT_CLK_DEBUG(pll4_audio_div_clk) + .parent = &pll4_audio_main_clk, + .set_rate = _clk_pll4_audio_div_set_rate, + .get_rate = _clk_pll4_audio_div_get_rate, +}; + static struct clk pll5_enet_main_clk = { __INIT_CLK_DEBUG(pll5_enet_main_clk) .parent = &osc_clk, @@ -1377,34 +1427,34 @@ static struct clk audio_external_clk = { .get_rate = get_audio_external_clock_rate, }; -static int _clk_sai2_set_parent(struct clk *clk, struct clk *parent) +static int _clk_sai0_set_parent(struct clk *clk, struct clk *parent) { int mux; u32 reg = __raw_readl(MXC_CCM_CSCMR1) - & ~MXC_CCM_CSCMR1_SAI2_CLK_SEL_MASK; + & ~MXC_CCM_CSCMR1_SAI0_CLK_SEL_MASK; mux = _get_mux6(parent, &audio_external_clk, NULL, - NULL, &pll4_audio_main_clk, NULL, NULL); + NULL /* spdif */, &pll4_audio_div_clk, NULL, NULL); - reg |= (mux << MXC_CCM_CSCMR1_SAI2_CLK_SEL_OFFSET); + reg |= (mux << MXC_CCM_CSCMR1_SAI0_CLK_SEL_OFFSET); __raw_writel(reg, MXC_CCM_CSCMR1); return 0; } -static unsigned long _clk_sai2_get_rate(struct clk *clk) +static unsigned long _clk_sai0_get_rate(struct clk *clk) { u32 reg, div; reg = __raw_readl(MXC_CCM_CSCDR1); - div = ((reg & MXC_CCM_CSCDR1_SAI2_DIV_MASK) >> - MXC_CCM_CSCDR1_SAI2_DIV_OFFSET) + 1; + div = ((reg & MXC_CCM_CSCDR1_SAI0_DIV_MASK) >> + MXC_CCM_CSCDR1_SAI0_DIV_OFFSET) + 1; return clk_get_rate(clk->parent) / div; } -static int _clk_sai2_set_rate(struct clk *clk, unsigned long rate) +static int _clk_sai0_set_rate(struct clk *clk, unsigned long rate) { u32 reg, div; u32 parent_rate = clk_get_rate(clk->parent); @@ -1416,31 +1466,30 @@ static int _clk_sai2_set_rate(struct clk *clk, unsigned long rate) return -EINVAL; reg = __raw_readl(MXC_CCM_CSCDR1); - reg &= ~MXC_CCM_CSCDR1_SAI2_DIV_MASK; - reg |= (div - 1) << MXC_CCM_CSCDR1_SAI2_DIV_OFFSET; - reg |= MXC_CCM_CSCDR1_SAI2_EN; + reg &= ~MXC_CCM_CSCDR1_SAI0_DIV_MASK; + reg |= (div - 1) << MXC_CCM_CSCDR1_SAI0_DIV_OFFSET; __raw_writel(reg, MXC_CCM_CSCDR1); return 0; } -static int _clk_sai2_enable(struct clk *clk) +static int _clk_sai0_enable(struct clk *clk) { u32 reg; reg = __raw_readl(MXC_CCM_CSCDR1); - reg |= MXC_CCM_CSCDR1_SAI2_EN; + reg |= MXC_CCM_CSCDR1_SAI0_EN; __raw_writel(reg, MXC_CCM_CSCDR1); return 0; } -static void _clk_sai2_disable(struct clk *clk) +static void _clk_sai0_disable(struct clk *clk) { u32 reg; reg = __raw_readl(MXC_CCM_CSCDR1); - reg &= ~MXC_CCM_CSCDR1_SAI2_EN; + reg &= ~MXC_CCM_CSCDR1_SAI0_EN; __raw_writel(reg, MXC_CCM_CSCDR1); return 0; @@ -1466,6 +1515,87 @@ static unsigned long _clk_sai_round_rate(struct clk *clk, return parent_rate / div; } +static struct clk sai0_clk = { + __INIT_CLK_DEBUG(sai0_clk) + .parent = &audio_external_clk, + .enable_reg = MXC_CCM_CCGR0, + .enable_shift = MXC_CCM_CCGRx_CG15_OFFSET, + .enable = _clk_sai0_enable, + .disable = _clk_sai0_disable, + .set_parent = _clk_sai0_set_parent, + .round_rate = _clk_sai_round_rate, + .set_rate = _clk_sai0_set_rate, + .get_rate = _clk_sai0_get_rate, +}; + +static int _clk_sai2_set_parent(struct clk *clk, struct clk *parent) +{ + int mux; + u32 reg = __raw_readl(MXC_CCM_CSCMR1) + & ~MXC_CCM_CSCMR1_SAI2_CLK_SEL_MASK; + + mux = _get_mux6(parent, &audio_external_clk, NULL, + NULL /* spdif */, &pll4_audio_div_clk, NULL, NULL); + + reg |= (mux << MXC_CCM_CSCMR1_SAI2_CLK_SEL_OFFSET); + + __raw_writel(reg, MXC_CCM_CSCMR1); + + return 0; +} + +static unsigned long _clk_sai2_get_rate(struct clk *clk) +{ + u32 reg, div; + + reg = __raw_readl(MXC_CCM_CSCDR1); + div = ((reg & MXC_CCM_CSCDR1_SAI2_DIV_MASK) >> + MXC_CCM_CSCDR1_SAI2_DIV_OFFSET) + 1; + + return clk_get_rate(clk->parent) / div; +} + +static int _clk_sai2_set_rate(struct clk *clk, unsigned long rate) +{ + u32 reg, div; + u32 parent_rate = clk_get_rate(clk->parent); + + div = parent_rate / rate; + if (div == 0) + div++; + if (((parent_rate / div) != rate) || (div > 16)) + return -EINVAL; + + reg = __raw_readl(MXC_CCM_CSCDR1); + reg &= ~MXC_CCM_CSCDR1_SAI2_DIV_MASK; + reg |= (div - 1) << MXC_CCM_CSCDR1_SAI2_DIV_OFFSET; + __raw_writel(reg, MXC_CCM_CSCDR1); + + return 0; +} + +static int _clk_sai2_enable(struct clk *clk) +{ + u32 reg; + + reg = __raw_readl(MXC_CCM_CSCDR1); + reg |= MXC_CCM_CSCDR1_SAI2_EN; + __raw_writel(reg, MXC_CCM_CSCDR1); + + return 0; +} + +static void _clk_sai2_disable(struct clk *clk) +{ + u32 reg; + + reg = __raw_readl(MXC_CCM_CSCDR1); + reg &= ~MXC_CCM_CSCDR1_SAI2_EN; + __raw_writel(reg, MXC_CCM_CSCDR1); + + return 0; +} + static struct clk sai2_clk = { __INIT_CLK_DEBUG(sai2_clk) .parent = &audio_external_clk, @@ -1510,15 +1640,32 @@ static int _clk_clko_set_parent(struct clk *clk, struct clk *parent) else if (parent == &pll6_video_main_clk) sel = 3; else if (parent == &pll4_audio_main_clk) + sel = 6; + else if (parent == &pll4_audio_div_clk) + sel = 7; + else if (parent == &pll4_audio_main_clk) sel = 15; else return -EINVAL; + + reg = __raw_readl(MXC_CCM_CCOSR) + & ~MXC_CCM_CCOSR_CKO1_SEL_MASK; + reg |= (sel << MXC_CCM_CCOSR_CKO1_SEL_OFFSET); + __raw_writel(reg, MXC_CCM_CCOSR); + return 0; } static unsigned long _clk_clko_get_rate(struct clk *clk) { - return 0; + u32 reg, div; + unsigned int parent_rate = clk_get_rate(clk->parent); + + reg = __raw_readl(MXC_CCM_CCOSR); + div = ((reg & MXC_CCM_CCOSR_CKO1_DIV_MASK) >> + MXC_CCM_CCOSR_CKO1_DIV_OFFSET) + 1; + + return parent_rate / div; } static int _clk_clko_set_rate(struct clk *clk, unsigned long rate) @@ -1529,9 +1676,14 @@ static int _clk_clko_set_rate(struct clk *clk, unsigned long rate) if (div == 0) div++; - if (((parent_rate / div) != rate) || (div > 8)) + if (((parent_rate / div) != rate) || (div > 16)) return -EINVAL; + reg = __raw_readl(MXC_CCM_CCOSR) + & ~MXC_CCM_CCOSR_CKO1_DIV_MASK; + reg |= ((div -1) << MXC_CCM_CCOSR_CKO1_DIV_OFFSET); + __raw_writel(reg, MXC_CCM_CCOSR); + return 0; } @@ -1577,7 +1729,9 @@ static int _clk_clko2_set_rate(struct clk *clk, unsigned long rate) static struct clk clko_clk = { __INIT_CLK_DEBUG(clko_clk) - .parent = &pll2_528_bus_main_clk, + .parent = &pll4_audio_div_clk, + .enable_reg = MXC_CCM_CCOSR, + .enable_shift = MXC_CCM_CCOSR_CKO1_EN_OFFSET, .enable = _clk_enable1, .disable = _clk_disable1, .set_parent = _clk_clko_set_parent, @@ -1880,6 +2034,7 @@ static struct clk_lookup lookups[] = { _REGISTER_CLOCK(NULL, "pll3_pfd3_308M", pll3_pfd3_308M), _REGISTER_CLOCK(NULL, "pll3_pfd4_320M", pll3_pfd4_320M), _REGISTER_CLOCK(NULL, "pll4", pll4_audio_main_clk), + _REGISTER_CLOCK(NULL, "pll4_div", pll4_audio_div_clk), _REGISTER_CLOCK(NULL, "pll5", pll6_video_main_clk), _REGISTER_CLOCK(NULL, "pll6", pll5_enet_main_clk), _REGISTER_CLOCK(NULL, "cpu_clk", cpu_clk), /* arm core clk */ @@ -1977,11 +2132,16 @@ int __init mvf_clocks_init(unsigned long ckil, unsigned long osc, // clk_set_parent(&dcu0_clk, &pll3_usb_otg_main_clk); #if !defined(CONFIG_COLIBRI_VF) clk_set_rate(&dcu0_clk, 113000000); + clk_set_parent(&sai2_clk, &audio_external_clk); #else clk_set_rate(&dcu0_clk, 452000000); // clk_set_rate(&dcu0_clk, 480000000); + clk_set_rate(&pll4_audio_div_clk, 147456000); + clk_set_parent(&sai0_clk, &pll4_audio_div_clk); + clk_set_parent(&sai2_clk, &pll4_audio_div_clk); + clk_set_rate(&sai0_clk, 147456000); + clk_enable(&sai0_clk); #endif - clk_set_parent(&sai2_clk, &audio_external_clk); clk_set_rate(&sai2_clk, 24576000); #if !defined(CONFIG_COLIBRI_VF) diff --git a/arch/arm/mach-mvf/crm_regs.h b/arch/arm/mach-mvf/crm_regs.h index 57fac5bbe973..51a898700baa 100644 --- a/arch/arm/mach-mvf/crm_regs.h +++ b/arch/arm/mach-mvf/crm_regs.h @@ -199,6 +199,7 @@ #define MXC_CCM_CLPCR (MXC_CCM_BASE + 0x2c) #define MXC_CCM_CISR (MXC_CCM_BASE + 0x30) #define MXC_CCM_CIMR (MXC_CCM_BASE + 0x34) +#define MXC_CCM_CCOSR (MXC_CCM_BASE + 0x38) #define MXC_CCM_CGPR (MXC_CCM_BASE + 0x3c) #define MXC_CCM_CCGR0 (MXC_CCM_BASE + 0x40) #define MXC_CCM_CCGR1 (MXC_CCM_BASE + 0x44) @@ -435,6 +436,14 @@ #define MXC_CCM_CIMR_LRF_PLL2 (1 << 1) #define MXC_CCM_CIMR_LRF_PLL1 (1) +/* CCOSR */ +#define MXC_CCM_CCOSR_CKO1_EN_OFFSET (10) +#define MXC_CCM_CCOSR_CKO1_DIV_MASK (0xF << 6) +#define MXC_CCM_CCOSR_CKO1_DIV_OFFSET (6) + +#define MXC_CCM_CCOSR_CKO1_SEL_MASK (0x3F << 0) +#define MXC_CCM_CCOSR_CKO1_SEL_OFFSET (0) + /* Define the bits in registers CGPR */ #define MXC_CCM_CGPR_EFUSE_PROG (1 << 4) #define MXC_CCM_CGPR_QSPI1_ACCZ (1 << 1) |