From b4ba2be8dc2711ad2219186e89b4e10648d52af9 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 30 Aug 2011 06:23:13 +0000 Subject: Tegra2: Add more clock support This adds functions to enable/disable clocks and reset to on-chip peripherals. Signed-off-by: Simon Glass --- arch/arm/cpu/armv7/tegra2/Makefile | 2 +- arch/arm/cpu/armv7/tegra2/ap20.c | 52 ++++-------- arch/arm/cpu/armv7/tegra2/clock.c | 158 +++++++++++++++++++++++++++++++++++++ 3 files changed, 176 insertions(+), 36 deletions(-) create mode 100644 arch/arm/cpu/armv7/tegra2/clock.c (limited to 'arch/arm/cpu/armv7') diff --git a/arch/arm/cpu/armv7/tegra2/Makefile b/arch/arm/cpu/armv7/tegra2/Makefile index f1ea915..b35764c 100644 --- a/arch/arm/cpu/armv7/tegra2/Makefile +++ b/arch/arm/cpu/armv7/tegra2/Makefile @@ -28,7 +28,7 @@ include $(TOPDIR)/config.mk LIB = $(obj)lib$(SOC).o SOBJS := lowlevel_init.o -COBJS := ap20.o board.o sys_info.o timer.o +COBJS := ap20.o board.o clock.o sys_info.o timer.o SRCS := $(SOBJS:.o=.S) $(COBJS:.o=.c) OBJS := $(addprefix $(obj),$(COBJS) $(SOBJS)) diff --git a/arch/arm/cpu/armv7/tegra2/ap20.c b/arch/arm/cpu/armv7/tegra2/ap20.c index 60dd5df..e3832e2 100644 --- a/arch/arm/cpu/armv7/tegra2/ap20.c +++ b/arch/arm/cpu/armv7/tegra2/ap20.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -35,33 +36,34 @@ u32 s_first_boot = 1; void init_pllx(void) { struct clk_rst_ctlr *clkrst = (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE; + struct clk_pll *pll = &clkrst->crc_pll[CLOCK_PLL_ID_XCPU]; u32 reg; /* If PLLX is already enabled, just return */ - reg = readl(&clkrst->crc_pllx_base); + reg = readl(&pll->pll_base); if (reg & PLL_ENABLE) return; /* Set PLLX_MISC */ reg = CPCON; /* CPCON[11:8] = 0001 */ - writel(reg, &clkrst->crc_pllx_misc); + writel(reg, &pll->pll_misc); /* Use 12MHz clock here */ - reg = (PLL_BYPASS | PLL_DIVM); + reg = (PLL_BYPASS | PLL_DIVM_VALUE); reg |= (1000 << 8); /* DIVN = 0x3E8 */ - writel(reg, &clkrst->crc_pllx_base); + writel(reg, &pll->pll_base); reg |= PLL_ENABLE; - writel(reg, &clkrst->crc_pllx_base); + writel(reg, &pll->pll_base); reg &= ~PLL_BYPASS; - writel(reg, &clkrst->crc_pllx_base); + writel(reg, &pll->pll_base); } static void enable_cpu_clock(int enable) { struct clk_rst_ctlr *clkrst = (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE; - u32 reg, clk; + u32 clk; /* * NOTE: @@ -83,10 +85,6 @@ static void enable_cpu_clock(int enable) writel(SUPER_CCLK_DIVIDER, &clkrst->crc_super_cclk_div); } - /* Fetch the register containing the main CPU complex clock enable */ - reg = readl(&clkrst->crc_clk_out_enb_l); - reg |= CLK_ENB_CPU; - /* * Read the register containing the individual CPU clock enables and * always stop the clock to CPU 1. @@ -103,7 +101,8 @@ static void enable_cpu_clock(int enable) } writel(clk, &clkrst->crc_clk_cpu_cmplx); - writel(reg, &clkrst->crc_clk_out_enb_l); + + clock_enable(PERIPH_ID_CPU); } static int is_cpu_powered(void) @@ -179,7 +178,7 @@ static void enable_cpu_power_rail(void) static void reset_A9_cpu(int reset) { struct clk_rst_ctlr *clkrst = (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE; - u32 reg, cpu; + u32 cpu; /* * NOTE: Regardless of whether the request is to hold the CPU in reset @@ -193,44 +192,27 @@ static void reset_A9_cpu(int reset) cpu = SET_DBGRESET1 | SET_DERESET1 | SET_CPURESET1; writel(cpu, &clkrst->crc_cpu_cmplx_set); - reg = readl(&clkrst->crc_rst_dev_l); if (reset) { /* Now place CPU0 into reset */ cpu |= SET_DBGRESET0 | SET_DERESET0 | SET_CPURESET0; writel(cpu, &clkrst->crc_cpu_cmplx_set); - - /* Enable master CPU reset */ - reg |= SWR_CPU_RST; } else { /* Take CPU0 out of reset */ cpu = CLR_DBGRESET0 | CLR_DERESET0 | CLR_CPURESET0; writel(cpu, &clkrst->crc_cpu_cmplx_clr); - - /* Disable master CPU reset */ - reg &= ~SWR_CPU_RST; } - writel(reg, &clkrst->crc_rst_dev_l); + /* Enable/Disable master CPU reset */ + reset_set_enable(PERIPH_ID_CPU, reset); } static void clock_enable_coresight(int enable) { struct clk_rst_ctlr *clkrst = (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE; - u32 rst, clk, src; - - rst = readl(&clkrst->crc_rst_dev_u); - clk = readl(&clkrst->crc_clk_out_enb_u); - - if (enable) { - rst &= ~SWR_CSITE_RST; - clk |= CLK_ENB_CSITE; - } else { - rst |= SWR_CSITE_RST; - clk &= ~CLK_ENB_CSITE; - } + u32 rst, src; - writel(clk, &clkrst->crc_clk_out_enb_u); - writel(rst, &clkrst->crc_rst_dev_u); + clock_set_enable(PERIPH_ID_CORESIGHT, enable); + reset_set_enable(PERIPH_ID_CORESIGHT, !enable); if (enable) { /* diff --git a/arch/arm/cpu/armv7/tegra2/clock.c b/arch/arm/cpu/armv7/tegra2/clock.c new file mode 100644 index 0000000..67eed14 --- /dev/null +++ b/arch/arm/cpu/armv7/tegra2/clock.c @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2011 The Chromium OS Authors. + * See file CREDITS for list of people who contributed to this + * project. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/* Tegra2 Clock control functions */ + +#include +#include +#include +#include +#include +#include + +#ifdef DEBUG +#define assert(x) \ + ({ if (!(x)) printf("Assertion failure '%s' %s line %d\n", \ + #x, __FILE__, __LINE__); }) +#else +#define assert(x) +#endif + +/* + * Get the oscillator frequency, from the corresponding hardware configuration + * field. + */ +enum clock_osc_freq clock_get_osc_freq(void) +{ + struct clk_rst_ctlr *clkrst = + (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE; + u32 reg; + + reg = readl(&clkrst->crc_osc_ctrl); + return (reg & OSC_FREQ_MASK) >> OSC_FREQ_SHIFT; +} + +unsigned long clock_start_pll(enum clock_pll_id clkid, u32 divm, u32 divn, + u32 divp, u32 cpcon, u32 lfcon) +{ + struct clk_rst_ctlr *clkrst = + (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE; + u32 data; + struct clk_pll *pll; + + assert(clock_pll_id_isvalid(clkid)); + pll = &clkrst->crc_pll[clkid]; + + /* + * We cheat by treating all PLL (except PLLU) in the same fashion. + * This works only because: + * - same fields are always mapped at same offsets, except DCCON + * - DCCON is always 0, doesn't conflict + * - M,N, P of PLLP values are ignored for PLLP + */ + data = (cpcon << PLL_CPCON_SHIFT) | (lfcon << PLL_LFCON_SHIFT); + writel(data, &pll->pll_misc); + + data = (divm << PLL_DIVM_SHIFT) | (divn << PLL_DIVN_SHIFT) | + (0 << PLL_BYPASS_SHIFT) | (1 << PLL_ENABLE_SHIFT); + + if (clkid == CLOCK_PLL_ID_USB) + data |= divp << PLLU_VCO_FREQ_SHIFT; + else + data |= divp << PLL_DIVP_SHIFT; + writel(data, &pll->pll_base); + + /* calculate the stable time */ + return timer_get_us() + CLOCK_PLL_STABLE_DELAY_US; +} + +void clock_set_enable(enum periph_id periph_id, int enable) +{ + struct clk_rst_ctlr *clkrst = + (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE; + u32 *clk = &clkrst->crc_clk_out_enb[PERIPH_REG(periph_id)]; + u32 reg; + + /* Enable/disable the clock to this peripheral */ + assert(clock_periph_id_isvalid(periph_id)); + reg = readl(clk); + if (enable) + reg |= PERIPH_MASK(periph_id); + else + reg &= ~PERIPH_MASK(periph_id); + writel(reg, clk); +} + +void clock_enable(enum periph_id clkid) +{ + clock_set_enable(clkid, 1); +} + +void clock_disable(enum periph_id clkid) +{ + clock_set_enable(clkid, 0); +} + +void reset_set_enable(enum periph_id periph_id, int enable) +{ + struct clk_rst_ctlr *clkrst = + (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE; + u32 *reset = &clkrst->crc_rst_dev[PERIPH_REG(periph_id)]; + u32 reg; + + /* Enable/disable reset to the peripheral */ + assert(clock_periph_id_isvalid(periph_id)); + reg = readl(reset); + if (enable) + reg |= PERIPH_MASK(periph_id); + else + reg &= ~PERIPH_MASK(periph_id); + writel(reg, reset); +} + +void reset_periph(enum periph_id periph_id, int us_delay) +{ + /* Put peripheral into reset */ + reset_set_enable(periph_id, 1); + udelay(us_delay); + + /* Remove reset */ + reset_set_enable(periph_id, 0); + + udelay(us_delay); +} + +void reset_cmplx_set_enable(int cpu, int which, int reset) +{ + struct clk_rst_ctlr *clkrst = + (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE; + u32 mask; + + /* Form the mask, which depends on the cpu chosen. Tegra2 has 2 */ + assert(cpu >= 0 && cpu < 2); + mask = which << cpu; + + /* either enable or disable those reset for that CPU */ + if (reset) + writel(mask, &clkrst->crc_cpu_cmplx_set); + else + writel(mask, &clkrst->crc_cpu_cmplx_clr); +} -- cgit v1.1