diff options
| -rw-r--r-- | arch/arm/cpu/armv7/am33xx/Makefile | 1 | ||||
| -rw-r--r-- | arch/arm/cpu/armv7/am33xx/clock.c | 273 | ||||
| -rw-r--r-- | arch/arm/include/asm/arch-am33xx/clock.h | 24 | ||||
| -rw-r--r-- | arch/arm/include/asm/arch-am33xx/clocks_am33xx.h | 55 | 
4 files changed, 353 insertions, 0 deletions
| diff --git a/arch/arm/cpu/armv7/am33xx/Makefile b/arch/arm/cpu/armv7/am33xx/Makefile index 498df785a..fc25587fa 100644 --- a/arch/arm/cpu/armv7/am33xx/Makefile +++ b/arch/arm/cpu/armv7/am33xx/Makefile @@ -18,6 +18,7 @@ LIB	= $(obj)lib$(SOC).o  SOBJS	:= lowlevel_init.o +COBJS	+= clock.o  COBJS	+= sys_info.o  SRCS	:= $(SOBJS:.o=.S) $(COBJS:.o=.c) diff --git a/arch/arm/cpu/armv7/am33xx/clock.c b/arch/arm/cpu/armv7/am33xx/clock.c new file mode 100644 index 000000000..4ca6c4534 --- /dev/null +++ b/arch/arm/cpu/armv7/am33xx/clock.c @@ -0,0 +1,273 @@ +/* + * clock.c + * + * clocks for AM33XX based boards + * + * Copyright (C) 2011, Texas Instruments, Incorporated - http://www.ti.com/ + * + * 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, or (at your option) any later version. + * + * 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 <common.h> +#include <asm/arch/cpu.h> +#include <asm/arch/clock.h> +#include <asm/arch/hardware.h> +#include <asm/io.h> + +#define PRCM_MOD_EN		0x2 +#define PRCM_FORCE_WAKEUP	0x2 + +#define PRCM_EMIF_CLK_ACTIVITY	BIT(2) +#define PRCM_L3_GCLK_ACTIVITY	BIT(4) + +#define PLL_BYPASS_MODE		0x4 +#define ST_MN_BYPASS		0x00000100 +#define ST_DPLL_CLK		0x00000001 +#define CLK_SEL_MASK		0x7ffff +#define CLK_DIV_MASK		0x1f +#define CLK_DIV2_MASK		0x7f +#define CLK_SEL_SHIFT		0x8 +#define CLK_MODE_SEL		0x7 +#define CLK_MODE_MASK		0xfffffff8 +#define CLK_DIV_SEL		0xFFFFFFE0 + + +const struct cm_perpll *cmper = (struct cm_perpll *)CM_PER; +const struct cm_wkuppll *cmwkup = (struct cm_wkuppll *)CM_WKUP; +const struct cm_dpll *cmdpll = (struct cm_dpll *)CM_DPLL; + +static void enable_interface_clocks(void) +{ +	/* Enable all the Interconnect Modules */ +	writel(PRCM_MOD_EN, &cmper->l3clkctrl); +	while (readl(&cmper->l3clkctrl) != PRCM_MOD_EN) +		; + +	writel(PRCM_MOD_EN, &cmper->l4lsclkctrl); +	while (readl(&cmper->l4lsclkctrl) != PRCM_MOD_EN) +		; + +	writel(PRCM_MOD_EN, &cmper->l4fwclkctrl); +	while (readl(&cmper->l4fwclkctrl) != PRCM_MOD_EN) +		; + +	writel(PRCM_MOD_EN, &cmwkup->wkl4wkclkctrl); +	while (readl(&cmwkup->wkl4wkclkctrl) != PRCM_MOD_EN) +		; + +	writel(PRCM_MOD_EN, &cmper->l3instrclkctrl); +	while (readl(&cmper->l3instrclkctrl) != PRCM_MOD_EN) +		; + +	writel(PRCM_MOD_EN, &cmper->l4hsclkctrl); +	while (readl(&cmper->l4hsclkctrl) != PRCM_MOD_EN) +		; +} + +/* + * Force power domain wake up transition + * Ensure that the corresponding interface clock is active before + * using the peripheral + */ +static void power_domain_wkup_transition(void) +{ +	writel(PRCM_FORCE_WAKEUP, &cmper->l3clkstctrl); +	writel(PRCM_FORCE_WAKEUP, &cmper->l4lsclkstctrl); +	writel(PRCM_FORCE_WAKEUP, &cmwkup->wkclkstctrl); +	writel(PRCM_FORCE_WAKEUP, &cmper->l4fwclkstctrl); +	writel(PRCM_FORCE_WAKEUP, &cmper->l3sclkstctrl); +} + +/* + * Enable the peripheral clock for required peripherals + */ +static void enable_per_clocks(void) +{ +	/* Enable the control module though RBL would have done it*/ +	writel(PRCM_MOD_EN, &cmwkup->wkctrlclkctrl); +	while (readl(&cmwkup->wkctrlclkctrl) != PRCM_MOD_EN) +		; + +	/* Enable the module clock */ +	writel(PRCM_MOD_EN, &cmper->timer2clkctrl); +	while (readl(&cmper->timer2clkctrl) != PRCM_MOD_EN) +		; + +	/* UART0 */ +	writel(PRCM_MOD_EN, &cmwkup->wkup_uart0ctrl); +	while (readl(&cmwkup->wkup_uart0ctrl) != PRCM_MOD_EN) +		; +} + +static void mpu_pll_config(void) +{ +	u32 clkmode, clksel, div_m2; + +	clkmode = readl(&cmwkup->clkmoddpllmpu); +	clksel = readl(&cmwkup->clkseldpllmpu); +	div_m2 = readl(&cmwkup->divm2dpllmpu); + +	/* Set the PLL to bypass Mode */ +	writel(PLL_BYPASS_MODE, &cmwkup->clkmoddpllmpu); +	while (readl(&cmwkup->idlestdpllmpu) != ST_MN_BYPASS) +		; + +	clksel = clksel & (~CLK_SEL_MASK); +	clksel = clksel | ((MPUPLL_M << CLK_SEL_SHIFT) | MPUPLL_N); +	writel(clksel, &cmwkup->clkseldpllmpu); + +	div_m2 = div_m2 & ~CLK_DIV_MASK; +	div_m2 = div_m2 | MPUPLL_M2; +	writel(div_m2, &cmwkup->divm2dpllmpu); + +	clkmode = clkmode | CLK_MODE_SEL; +	writel(clkmode, &cmwkup->clkmoddpllmpu); + +	while (readl(&cmwkup->idlestdpllmpu) != ST_DPLL_CLK) +		; +} + +static void core_pll_config(void) +{ +	u32 clkmode, clksel, div_m4, div_m5, div_m6; + +	clkmode = readl(&cmwkup->clkmoddpllcore); +	clksel = readl(&cmwkup->clkseldpllcore); +	div_m4 = readl(&cmwkup->divm4dpllcore); +	div_m5 = readl(&cmwkup->divm5dpllcore); +	div_m6 = readl(&cmwkup->divm6dpllcore); + +	/* Set the PLL to bypass Mode */ +	writel(PLL_BYPASS_MODE, &cmwkup->clkmoddpllcore); + +	while (readl(&cmwkup->idlestdpllcore) != ST_MN_BYPASS) +		; + +	clksel = clksel & (~CLK_SEL_MASK); +	clksel = clksel | ((COREPLL_M << CLK_SEL_SHIFT) | COREPLL_N); +	writel(clksel, &cmwkup->clkseldpllcore); + +	div_m4 = div_m4 & ~CLK_DIV_MASK; +	div_m4 = div_m4 | COREPLL_M4; +	writel(div_m4, &cmwkup->divm4dpllcore); + +	div_m5 = div_m5 & ~CLK_DIV_MASK; +	div_m5 = div_m5 | COREPLL_M5; +	writel(div_m5, &cmwkup->divm5dpllcore); + +	div_m6 = div_m6 & ~CLK_DIV_MASK; +	div_m6 = div_m6 | COREPLL_M6; +	writel(div_m6, &cmwkup->divm6dpllcore); + +	clkmode = clkmode | CLK_MODE_SEL; +	writel(clkmode, &cmwkup->clkmoddpllcore); + +	while (readl(&cmwkup->idlestdpllcore) != ST_DPLL_CLK) +		; +} + +static void per_pll_config(void) +{ +	u32 clkmode, clksel, div_m2; + +	clkmode = readl(&cmwkup->clkmoddpllper); +	clksel = readl(&cmwkup->clkseldpllper); +	div_m2 = readl(&cmwkup->divm2dpllper); + +	/* Set the PLL to bypass Mode */ +	writel(PLL_BYPASS_MODE, &cmwkup->clkmoddpllper); + +	while (readl(&cmwkup->idlestdpllper) != ST_MN_BYPASS) +		; + +	clksel = clksel & (~CLK_SEL_MASK); +	clksel = clksel | ((PERPLL_M << CLK_SEL_SHIFT) | PERPLL_N); +	writel(clksel, &cmwkup->clkseldpllper); + +	div_m2 = div_m2 & ~CLK_DIV2_MASK; +	div_m2 = div_m2 | PERPLL_M2; +	writel(div_m2, &cmwkup->divm2dpllper); + +	clkmode = clkmode | CLK_MODE_SEL; +	writel(clkmode, &cmwkup->clkmoddpllper); + +	while (readl(&cmwkup->idlestdpllper) != ST_DPLL_CLK) +		; +} + +static void ddr_pll_config(void) +{ +	u32 clkmode, clksel, div_m2; + +	clkmode = readl(&cmwkup->clkmoddpllddr); +	clksel = readl(&cmwkup->clkseldpllddr); +	div_m2 = readl(&cmwkup->divm2dpllddr); + +	/* Set the PLL to bypass Mode */ +	clkmode = (clkmode & CLK_MODE_MASK) | PLL_BYPASS_MODE; +	writel(clkmode, &cmwkup->clkmoddpllddr); + +	/* Wait till bypass mode is enabled */ +	while ((readl(&cmwkup->idlestdpllddr) & ST_MN_BYPASS) +				!= ST_MN_BYPASS) +		; + +	clksel = clksel & (~CLK_SEL_MASK); +	clksel = clksel | ((DDRPLL_M << CLK_SEL_SHIFT) | DDRPLL_N); +	writel(clksel, &cmwkup->clkseldpllddr); + +	div_m2 = div_m2 & CLK_DIV_SEL; +	div_m2 = div_m2 | DDRPLL_M2; +	writel(div_m2, &cmwkup->divm2dpllddr); + +	clkmode = (clkmode & CLK_MODE_MASK) | CLK_MODE_SEL; +	writel(clkmode, &cmwkup->clkmoddpllddr); + +	/* Wait till dpll is locked */ +	while ((readl(&cmwkup->idlestdpllddr) & ST_DPLL_CLK) != ST_DPLL_CLK) +		; +} + +void enable_emif_clocks(void) +{ +	/* Enable the  EMIF_FW Functional clock */ +	writel(PRCM_MOD_EN, &cmper->emiffwclkctrl); +	/* Enable EMIF0 Clock */ +	writel(PRCM_MOD_EN, &cmper->emifclkctrl); +	/* Poll for emif_gclk  & L3_G clock  are active */ +	while ((readl(&cmper->l3clkstctrl) & (PRCM_EMIF_CLK_ACTIVITY | +			PRCM_L3_GCLK_ACTIVITY)) != (PRCM_EMIF_CLK_ACTIVITY | +			PRCM_L3_GCLK_ACTIVITY)) +		; +	/* Poll if module is functional */ +	while ((readl(&cmper->emifclkctrl)) != PRCM_MOD_EN) +		; +} + +/* + * Configure the PLL/PRCM for necessary peripherals + */ +void pll_init() +{ +	mpu_pll_config(); +	core_pll_config(); +	per_pll_config(); +	ddr_pll_config(); + +	/* Enable the required interconnect clocks */ +	enable_interface_clocks(); + +	/* Power domain wake up transition */ +	power_domain_wkup_transition(); + +	/* Enable the required peripherals */ +	enable_per_clocks(); +} diff --git a/arch/arm/include/asm/arch-am33xx/clock.h b/arch/arm/include/asm/arch-am33xx/clock.h new file mode 100644 index 000000000..872ff820a --- /dev/null +++ b/arch/arm/include/asm/arch-am33xx/clock.h @@ -0,0 +1,24 @@ +/* + * clock.h + * + * clock header + * + * Copyright (C) 2011, Texas Instruments, Incorporated - http://www.ti.com/ + * + * 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, or (at your option) any later version. + * + * 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. + */ + +#ifndef _CLOCKS_H_ +#define _CLOCKS_H_ + +#include <asm/arch/clocks_am33xx.h> + +#endif diff --git a/arch/arm/include/asm/arch-am33xx/clocks_am33xx.h b/arch/arm/include/asm/arch-am33xx/clocks_am33xx.h new file mode 100644 index 000000000..abc5b6b41 --- /dev/null +++ b/arch/arm/include/asm/arch-am33xx/clocks_am33xx.h @@ -0,0 +1,55 @@ +/* + * clocks_am33xx.h + * + * AM33xx clock define + * + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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, or (at your option) any later version. + * + * 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. + */ + +#ifndef _CLOCKS_AM33XX_H_ +#define _CLOCKS_AM33XX_H_ + +#define OSC	24 + +/* MAIN PLL Fdll = 550 MHZ, */ +#define MPUPLL_M	550 +#define MPUPLL_N	23 +#define MPUPLL_M2	1 + +/* Core PLL Fdll = 1 GHZ, */ +#define COREPLL_M	1000 +#define COREPLL_N	23 + +#define COREPLL_M4	10	/* CORE_CLKOUTM4 = 200 MHZ */ +#define COREPLL_M5	8	/* CORE_CLKOUTM5 = 250 MHZ */ +#define COREPLL_M6	4	/* CORE_CLKOUTM6 = 500 MHZ */ + +/* + * USB PHY clock is 960 MHZ. Since, this comes directly from Fdll, Fdll + * frequency needs to be set to 960 MHZ. Hence, + * For clkout = 192 MHZ, Fdll = 960 MHZ, divider values are given below + */ +#define PERPLL_M	960 +#define PERPLL_N	23 +#define PERPLL_M2	5 + +/* DDR Freq is 266 MHZ for now */ +/* Set Fdll = 400 MHZ , Fdll = M * 2 * CLKINP/ N + 1; clkout = Fdll /(2 * M2) */ +#define DDRPLL_M	266 +#define DDRPLL_N	23 +#define DDRPLL_M2	1 + +extern void pll_init(void); +extern void enable_emif_clocks(void); + +#endif	/* endif _CLOCKS_AM33XX_H_ */ |