diff options
author | Simon Glass <sjg@chromium.org> | 2011-05-19 17:56:33 -0700 |
---|---|---|
committer | Simon Glass <sjg@chromium.org> | 2011-08-24 10:01:51 -0700 |
commit | f22424f2c7b085a88eb6b750c6acca1e2d133e9c (patch) | |
tree | 73b4d58fd2af0e64114f6b502a16cf6a33a02bd8 /arch | |
parent | a9177e1137f15505a0a5718e46167f43baede92b (diff) |
tegra2: Add clock and PLL control functions
This adds functions for setting up clocks to peripherals, by selecting
a parent clock and specifying a rate.
BUG=chromium-os:13228
TEST=Build, boot on Seaboard
Change-Id: I957723b5f0ef64244c16f44ae7cbd79abf06427d
Reviewed-on: http://gerrit.chromium.org/gerrit/1290
Reviewed-by: Simon Glass <sjg@chromium.org>
Tested-by: Simon Glass <sjg@chromium.org>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/arm/cpu/armv7/tegra2/ap20.c | 3 | ||||
-rw-r--r-- | arch/arm/cpu/armv7/tegra2/clock.c | 577 | ||||
-rw-r--r-- | arch/arm/include/asm/arch-tegra2/clk_rst.h | 75 | ||||
-rw-r--r-- | arch/arm/include/asm/arch-tegra2/clock.h | 86 |
4 files changed, 660 insertions, 81 deletions
diff --git a/arch/arm/cpu/armv7/tegra2/ap20.c b/arch/arm/cpu/armv7/tegra2/ap20.c index 3f365aebbac..74cd583d146 100644 --- a/arch/arm/cpu/armv7/tegra2/ap20.c +++ b/arch/arm/cpu/armv7/tegra2/ap20.c @@ -197,7 +197,6 @@ static void reset_A9_cpu(int reset) static void clock_enable_coresight(int enable) { - struct clk_rst_ctlr *clkrst = (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE; u32 rst, src; clock_set_enable(PERIPH_ID_CORESIGHT, enable); @@ -211,7 +210,7 @@ static void clock_enable_coresight(int enable) * (bits 7:0), so 00000001b == 1.5 (n+1 + .5) */ src = CLK_DIVIDER(NVBL_PLLP_KHZ, 144000); - writel(src, &clkrst->crc_clk_src_csite); + clock_ll_set_source_divisor(PERIPH_ID_CSI, 0, src); /* Unlock the CPU CoreSight interfaces */ rst = 0xC5ACCE55; diff --git a/arch/arm/cpu/armv7/tegra2/clock.c b/arch/arm/cpu/armv7/tegra2/clock.c index 1fad0d6769c..5efbd90917c 100644 --- a/arch/arm/cpu/armv7/tegra2/clock.c +++ b/arch/arm/cpu/armv7/tegra2/clock.c @@ -28,6 +28,7 @@ #include <asm/arch/timer.h> #include <asm/arch/tegra2.h> #include <common.h> +#include <div64.h> #ifdef DEBUG #define assert(x) \ @@ -38,6 +39,369 @@ #endif /* + * This is our record of the current clock rate of each clock. We don't + * fill all of these in since we are only really interested in clocks which + * we use as parents. + */ +static unsigned pll_rate[CLOCK_ID_COUNT]; + +/* + * The oscillator frequency is fixed to one of four set values. Based on this + * the other clocks are set up appropriately. + */ +static unsigned osc_freq[CLOCK_OSC_FREQ_COUNT] = { + 13000000, + 19200000, + 12000000, + 26000000, +}; + +/* + * Clock types that we can use as a source. The Tegra2 has muxes for the + * peripheral clocks, and in most cases there are four options for the clock + * source. This gives us a clock 'type' and exploits what commonality exists + * in the device. + * + * Letters are obvious, except for T which means CLK_M, and S which means the + * clock derived from 32KHz. Beware that CLK_M (also called OSC in the + * datasheet) and PLL_M are different things. The former is the basic + * clock supplied to the SOC from an external oscillator. The latter is the + * memory clock PLL. + * + * See definitions in clock_id in the header file. + */ +enum clock_type_id { + CLOCK_TYPE_AXPT, /* PLL_A, PLL_X, PLL_P, CLK_M */ + CLOCK_TYPE_MCPA, /* and so on */ + CLOCK_TYPE_MCPT, + CLOCK_TYPE_PCM, + CLOCK_TYPE_PCMT, + CLOCK_TYPE_PCXTS, + CLOCK_TYPE_PDCT, + + CLOCK_TYPE_COUNT, + CLOCK_TYPE_NONE = -1, /* invalid clock type */ +}; + +/* return 1 if a peripheral ID is in range */ +#define clock_type_id_isvalid(id) ((id) >= 0 && \ + (id) < CLOCK_TYPE_COUNT) + + +enum { + CLOCK_MAX_MUX = 4 /* number of source options for each clock */ +}; + +/* + * Clock source mux for each clock type. This just converts our enum into + * a list of mux sources for use by the code. Note that CLOCK_TYPE_PCXTS + * is special as it has 5 sources. Since it also has a different number of + * bits in its register for the source, we just handle it with a special + * case in the code. + */ +#define CLK(x) CLOCK_ID_ ## x +static enum clock_id clock_source[CLOCK_TYPE_COUNT] [CLOCK_MAX_MUX] = { + { CLK(AUDIO), CLK(XCPU), CLK(PERIPH), CLK(OSC) }, + { CLK(MEMORY), CLK(CGENERAL), CLK(PERIPH), CLK(AUDIO) }, + { CLK(MEMORY), CLK(CGENERAL), CLK(PERIPH), CLK(OSC) }, + { CLK(PERIPH), CLK(CGENERAL), CLK(MEMORY), CLK(NONE) }, + { CLK(PERIPH), CLK(CGENERAL), CLK(MEMORY), CLK(OSC) }, + { CLK(PERIPH), CLK(CGENERAL), CLK(XCPU), CLK(OSC) }, + { CLK(PERIPH), CLK(DISPLAY), CLK(CGENERAL), CLK(OSC) }, +}; + +/* + * Clock peripheral IDs which sadly don't match up with PERIPH_ID. This is + * not in the header file since it is for purely internal use - we want + * callers to use the PERIPH_ID for all access to peripheral clocks to avoid + * confusion bewteen PERIPH_ID_... and PERIPHC_... + * + * We don't call this CLOCK_PERIPH_ID or PERIPH_CLOCK_ID as it would just be + * confusing. + * + * Note to SOC vendors: perhaps define a unified numbering for peripherals and + * use it for reset, clock enable, clock source/divider and even pinmuxing + * if you can. + */ +enum periphc_internal_id { + /* 0x00 */ + PERIPHC_I2S1, + PERIPHC_I2S2, + PERIPHC_SPDIF_OUT, + PERIPHC_SPDIF_IN, + PERIPHC_PWM, + PERIPHC_SPI1, + PERIPHC_SPI2, + PERIPHC_SPI3, + + /* 0x08 */ + PERIPHC_XIO, + PERIPHC_I2C1, + PERIPHC_DVC_I2C, + PERIPHC_TWC, + PERIPHC_0c, + PERIPHC_10, /* PERIPHC_SPI1, what is this really? */ + PERIPHC_DISP1, + PERIPHC_DISP2, + + /* 0x10 */ + PERIPHC_CVE, + PERIPHC_IDE0, + PERIPHC_VI, + PERIPHC_1c, + PERIPHC_SDMMC1, + PERIPHC_SDMMC2, + PERIPHC_G3D, + PERIPHC_G2D, + + /* 0x18 */ + PERIPHC_NDFLASH, + PERIPHC_SDMMC4, + PERIPHC_VFIR, + PERIPHC_EPP, + PERIPHC_MPE, + PERIPHC_MIPI, + PERIPHC_UART1, + PERIPHC_UART2, + + /* 0x20 */ + PERIPHC_HOST1X, + PERIPHC_21, + PERIPHC_TVO, + PERIPHC_HDMI, + PERIPHC_24, + PERIPHC_TVDAC, + PERIPHC_I2C2, + PERIPHC_EMC, + + /* 0x28 */ + PERIPHC_UART3, + PERIPHC_29, + PERIPHC_VI_SENSOR, + PERIPHC_2b, + PERIPHC_2c, + PERIPHC_SPI4, + PERIPHC_I2C3, + PERIPHC_SDMMC3, + + /* 0x30 */ + PERIPHC_UART4, + PERIPHC_UART5, + PERIPHC_VDE, + PERIPHC_OWR, + PERIPHC_NOR, + PERIPHC_CSITE, + + PERIPHC_COUNT, + + PERIPHC_NONE = -1, +}; + +/* return 1 if a periphc_internal_id is in range */ +#define periphc_internal_id_isvalid(id) ((id) >= 0 && \ + (id) < PERIPHC_COUNT) + +/* + * Clock type for each peripheral clock source. We put the name in each + * record just so it is easy to match things up + */ +#define TYPE(name, type) type +static enum clock_type_id clock_periph_type[PERIPHC_COUNT] = { + /* 0x00 */ + TYPE(PERIPHC_I2S1, CLOCK_TYPE_AXPT), + TYPE(PERIPHC_I2S2, CLOCK_TYPE_AXPT), + TYPE(PERIPHC_SPDIF_OUT, CLOCK_TYPE_AXPT), + TYPE(PERIPHC_SPDIF_IN, CLOCK_TYPE_PCM), + TYPE(PERIPHC_PWM, CLOCK_TYPE_PCXTS), + TYPE(PERIPHC_SPI1, CLOCK_TYPE_PCMT), + TYPE(PERIPHC_SPI22, CLOCK_TYPE_PCMT), + TYPE(PERIPHC_SPI3, CLOCK_TYPE_PCMT), + + /* 0x08 */ + TYPE(PERIPHC_XIO, CLOCK_TYPE_PCMT), + TYPE(PERIPHC_I2C1, CLOCK_TYPE_PCMT), + TYPE(PERIPHC_DVC_I2C, CLOCK_TYPE_PCMT), + TYPE(PERIPHC_TWC, CLOCK_TYPE_PCMT), + TYPE(PERIPHC_NONE, CLOCK_TYPE_NONE), + TYPE(PERIPHC_SPI1, CLOCK_TYPE_PCMT), + TYPE(PERIPHC_DISP1, CLOCK_TYPE_PDCT), + TYPE(PERIPHC_DISP2, CLOCK_TYPE_PDCT), + + /* 0x10 */ + TYPE(PERIPHC_CVE, CLOCK_TYPE_PDCT), + TYPE(PERIPHC_IDE0, CLOCK_TYPE_PCMT), + TYPE(PERIPHC_VI, CLOCK_TYPE_MCPA), + TYPE(PERIPHC_NONE, CLOCK_TYPE_NONE), + TYPE(PERIPHC_SDMMC1, CLOCK_TYPE_PCMT), + TYPE(PERIPHC_SDMMC2, CLOCK_TYPE_PCMT), + TYPE(PERIPHC_G3D, CLOCK_TYPE_MCPA), + TYPE(PERIPHC_G2D, CLOCK_TYPE_MCPA), + + /* 0x18 */ + TYPE(PERIPHC_NDFLASH, CLOCK_TYPE_PCMT), + TYPE(PERIPHC_SDMMC4, CLOCK_TYPE_PCMT), + TYPE(PERIPHC_VFIR, CLOCK_TYPE_PCMT), + TYPE(PERIPHC_EPP, CLOCK_TYPE_MCPA), + TYPE(PERIPHC_MPE, CLOCK_TYPE_MCPA), + TYPE(PERIPHC_MIPI, CLOCK_TYPE_PCMT), + TYPE(PERIPHC_UART1, CLOCK_TYPE_PCMT), + TYPE(PERIPHC_UART2, CLOCK_TYPE_PCMT), + + /* 0x20 */ + TYPE(PERIPHC_HOST1X, CLOCK_TYPE_MCPA), + TYPE(PERIPHC_NONE, CLOCK_TYPE_NONE), + TYPE(PERIPHC_TVO, CLOCK_TYPE_PDCT), + TYPE(PERIPHC_HDMI, CLOCK_TYPE_PDCT), + TYPE(PERIPHC_NONE, CLOCK_TYPE_NONE), + TYPE(PERIPHC_TVDAC, CLOCK_TYPE_PDCT), + TYPE(PERIPHC_I2C2, CLOCK_TYPE_PCMT), + TYPE(PERIPHC_EMC, CLOCK_TYPE_MCPT), + + /* 0x28 */ + TYPE(PERIPHC_UART3, CLOCK_TYPE_PCMT), + TYPE(PERIPHC_NONE, CLOCK_TYPE_NONE), + TYPE(PERIPHC_VI, CLOCK_TYPE_MCPA), + TYPE(PERIPHC_NONE, CLOCK_TYPE_NONE), + TYPE(PERIPHC_NONE, CLOCK_TYPE_NONE), + TYPE(PERIPHC_SPI4, CLOCK_TYPE_PCMT), + TYPE(PERIPHC_I2C3, CLOCK_TYPE_PCMT), + TYPE(PERIPHC_SDMMC3, CLOCK_TYPE_PCMT), + + /* 0x30 */ + TYPE(PERIPHC_UART4, CLOCK_TYPE_PCMT), + TYPE(PERIPHC_UART5, CLOCK_TYPE_PCMT), + TYPE(PERIPHC_VDE, CLOCK_TYPE_PCMT), + TYPE(PERIPHC_OWR, CLOCK_TYPE_PCMT), + TYPE(PERIPHC_NOR, CLOCK_TYPE_PCMT), + TYPE(PERIPHC_CSITE, CLOCK_TYPE_PCMT), +}; + +/* + * This array translates a periph_id to a periphc_internal_id + * + * Not present/matched up: + * uint vi_sensor; _VI_SENSOR_0, 0x1A8 + * SPDIF - which is both 0x08 and 0x0c + * + */ +#define NONE(name) (-1) +#define OFFSET(name, value) PERIPHC_ ## name +static s8 periph_id_to_internal_id[PERIPH_ID_COUNT] = { + /* Low word: 31:0 */ + NONE(CPU), + NONE(RESERVED1), + NONE(RESERVED2), + NONE(AC97), + NONE(RTC), + NONE(TMR), + PERIPHC_UART1, + PERIPHC_UART2, /* and vfir 0x68 */ + + /* 0x08 */ + NONE(GPIO), + PERIPHC_SDMMC2, + NONE(SPDIF), /* 0x08 and 0x0c, unclear which to use */ + PERIPHC_I2S1, + PERIPHC_I2C1, + PERIPHC_NDFLASH, + PERIPHC_SDMMC1, + PERIPHC_SDMMC4, + + /* 0x10 */ + PERIPHC_TWC, + PERIPHC_PWM, + PERIPHC_I2S2, + PERIPHC_EPP, + PERIPHC_VI, + PERIPHC_G2D, + NONE(USBD), + NONE(ISP), + + /* 0x18 */ + PERIPHC_G3D, + PERIPHC_IDE0, + PERIPHC_DISP2, + PERIPHC_DISP1, + PERIPHC_HOST1X, + NONE(VCP), + NONE(RESERVED30), + NONE(CACHE2), + + /* Middle word: 63:32 */ + NONE(MEM), + NONE(AHBDMA), + NONE(APBDMA), + NONE(RESERVED35), + NONE(KBC), + NONE(STAT_MON), + NONE(PMC), + NONE(FUSE), + + /* 0x28 */ + NONE(KFUSE), + NONE(SBC1), /* SBC1, 0x34, is this SPI1? */ + PERIPHC_NOR, + PERIPHC_SPI1, + PERIPHC_SPI2, + PERIPHC_XIO, + PERIPHC_SPI3, + PERIPHC_DVC_I2C, + + /* 0x30 */ + NONE(DSI), + PERIPHC_TVO, /* also CVE 0x40 */ + PERIPHC_MIPI, + PERIPHC_HDMI, + PERIPHC_CSITE, + PERIPHC_TVDAC, + PERIPHC_I2C2, + PERIPHC_UART3, + + /* 0x38 */ + NONE(RESERVED56), + PERIPHC_EMC, + NONE(USB2), + NONE(USB3), + PERIPHC_MPE, + PERIPHC_VDE, + NONE(BSEA), + NONE(BSEV), + + /* Upper word 95:64 */ + NONE(SPEEDO), + PERIPHC_UART4, + PERIPHC_UART5, + PERIPHC_I2C3, + PERIPHC_SPI4, + PERIPHC_SDMMC3, + NONE(PCIE), + PERIPHC_OWR, + + /* 0x48 */ + NONE(AFI), + NONE(CORESIGHT), + NONE(RESERVED74), + NONE(AVPUCQ), + NONE(RESERVED76), + NONE(RESERVED77), + NONE(RESERVED78), + NONE(RESERVED79), + + /* 0x50 */ + NONE(RESERVED80), + NONE(RESERVED81), + NONE(RESERVED82), + NONE(RESERVED83), + NONE(IRAMA), + NONE(IRAMB), + NONE(IRAMC), + NONE(IRAMD), + + /* 0x58 */ + NONE(CRAM2), +}; + +/* * Get the oscillator frequency, from the corresponding hardware configuration * field. */ @@ -51,16 +415,21 @@ enum clock_osc_freq clock_get_osc_freq(void) return bf_unpack(OSC_FREQ, reg); } -unsigned long clock_start_pll(enum clock_pll_id clkid, u32 divm, u32 divn, - u32 divp, u32 cpcon, u32 lfcon) +/* Returns a pointer to the registers of the given pll */ +static struct clk_pll *get_pll(enum clock_id clkid) { 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]; + assert(clock_id_isvalid(clkid)); + return &clkrst->crc_pll[clkid]; +} + +unsigned long clock_start_pll(enum clock_id clkid, u32 divm, u32 divn, + u32 divp, u32 cpcon, u32 lfcon) +{ + struct clk_pll *pll = get_pll(clkid); + u32 data; /* * We cheat by treating all PLL (except PLLU) in the same fashion. @@ -89,6 +458,167 @@ unsigned long clock_start_pll(enum clock_pll_id clkid, u32 divm, u32 divn, return timer_get_future_us(CLOCK_PLL_STABLE_DELAY_US); } +/* Returns a pointer to the clock source register for a peripheral */ +static u32 *get_periph_source_reg(enum periph_id periph_id) +{ + struct clk_rst_ctlr *clkrst = + (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE; + enum periphc_internal_id internal_id; + + assert(periph_id >= PERIPH_ID_FIRST && periph_id < PERIPH_ID_COUNT); + internal_id = periph_id_to_internal_id[periph_id]; + assert(internal_id != -1); + return &clkrst->crc_clk_src[internal_id]; +} + +void clock_ll_set_source_divisor(enum periph_id periph_id, int source, + unsigned divisor) +{ + u32 *reg = get_periph_source_reg(periph_id); + u32 value; + + value = bf_pack(OUT_CLK_SOURCE, source) | + bf_pack(OUT_CLK_DIVISOR, divisor); + writel(value, reg); +} + +void clock_ll_set_source(enum periph_id periph_id, int source) +{ + u32 *reg = get_periph_source_reg(periph_id); + + bf_writel(OUT_CLK_SOURCE, source, reg); +} + +/** + * Given the parent's rate and the required rate for the children, this works + * out the peripheral clock divider to use, in 7.1 binary format. + * + * @param parent_rate clock rate of parent clock in Hz + * @param rate required clock rate for this clock + */ +static int clk_div7_1_get_divider(unsigned long parent_rate, unsigned long rate) +{ + u64 divider = parent_rate * 2; + + divider += rate - 1; + do_div(divider, rate); + + if ((s64)divider - 2 < 0) + return 0; + + if ((s64)divider - 2 > 255) + return -1; + + return divider - 2; +} + +unsigned long clock_get_periph_rate(enum periph_id periph_id, + enum clock_id parent) +{ + u32 *reg = get_periph_source_reg(periph_id); + u32 data = readl(reg); + u64 rate; + int divider; + + divider = bf_unpack(OUT_CLK_DIVISOR, data); + rate = (u64)pll_rate[parent] * 2 ; + do_div (rate, divider + 2); + return rate; +} + + +/* + * Given a peripheral ID and the required source clock, this returns which + * value should be programmed into the source mux for that peripheral. + * + * There is special code here to handle the one source type with 5 sources. + * + * @param periph_id peripheral to start + * @param source PLL id of required parent clock + * @param mux_bits Set to number of bits in mux register: 2 or 4 + * @return mux value (0-4, or -1 if not found) + */ +static int get_periph_clock_source(enum periph_id periph_id, + enum clock_id parent, int *mux_bits) +{ + enum clock_type_id type; + enum periphc_internal_id internal_id; + int mux; + + assert(clock_periph_id_isvalid(periph_id)); + + internal_id = periph_id_to_internal_id[periph_id]; + assert(periphc_internal_id_isvalid(internal_id)); + + type = clock_periph_type[internal_id]; + assert(clock_type_id_isvalid(type)); + + /* Special case here for the clock with a 4-bit source mux */ + if (type == CLOCK_TYPE_PCXTS) + *mux_bits = 4; + else + *mux_bits = 2; + + for (mux = 0; mux < CLOCK_MAX_MUX; mux++) + if (clock_source[type][mux] == parent) + return mux; + + /* + * Not found: it might be looking for the 'S' in CLOCK_TYPE_PCXTS + * which is not in our table. If not, then they are asking for a + * source which this peripheral can't access through its mux. + */ + assert(type == CLOCK_TYPE_PCXTS); + assert(parent == CLOCK_ID_SFROM32KHZ); + if (type == CLOCK_TYPE_PCXTS && parent == CLOCK_ID_SFROM32KHZ); + return 4; /* mux value for this clock */ + + /* if we get here, either us or the caller has made a mistake */ + printf("Caller requested bad clock: periph=%d, parent=%d\n", periph_id, + parent); + return -1; +} + +unsigned clock_start_periph_pll(enum periph_id periph_id, + enum clock_id parent, unsigned rate) +{ + u32 *reg = get_periph_source_reg(periph_id); + int divider; + uint effective_rate; + int source, mux_bits; + + reset_set_enable(periph_id, 1); + clock_enable(periph_id); + + divider = clk_div7_1_get_divider(pll_rate[parent], rate); + assert(divider >= 0); + + bf_writel(OUT_CLK_DIVISOR, divider, reg); + udelay(1); + + /* work out the source clock and set it*/ + source = get_periph_clock_source(periph_id, parent, &mux_bits); + if (source < 0) + return -1U; + if (mux_bits == 4) + bf_writel(OUT_CLK_SOURCE4, source, reg); + else + bf_writel(OUT_CLK_SOURCE, source, reg); + udelay(2); + + debug("periph %d, rate=%d, reg=%p = %x\n", periph_id, rate, + reg, readl(reg)); + + /* Check what we ended up with. This shouldn't matter though */ + effective_rate = clock_get_periph_rate(periph_id, parent); + if (rate != effective_rate) + printf("Requested clock rate %u not honored (got %u)\n", + rate, effective_rate); + + reset_set_enable(periph_id, 0); + return effective_rate; +} + void clock_set_enable(enum periph_id periph_id, int enable) { struct clk_rst_ctlr *clkrst = @@ -161,3 +691,38 @@ void reset_cmplx_set_enable(int cpu, int which, int reset) else writel(mask, &clkrst->crc_cpu_cmplx_clr); } + +/** + * Returns the frequency of the given PLL. This assumes that the PLL is fed + * from the main oscillator. + * + * @param clkid Clock to examine + * @return clock frequency in Hz + */ +static unsigned get_clock_freq(enum clock_id clkid) +{ + struct clk_pll *pll = get_pll(clkid); + u32 base = readl(&pll->pll_base); + u32 divm; + u64 parent_rate; + u64 rate; + + parent_rate = osc_freq[clock_get_osc_freq()]; + rate = parent_rate * bf_unpack(PLL_DIVN, base); + divm = bf_unpack(PLL_DIVM, base); + if (clkid == CLOCK_ID_USB) + divm <<= bf_unpack(PLLU_VCO_FREQ, base); + else + divm <<= bf_unpack(PLL_DIVP, base); + do_div(rate, divm); + return rate; +} + +void clock_init(void) +{ + pll_rate[CLOCK_ID_MEMORY] = get_clock_freq(CLOCK_ID_MEMORY); + pll_rate[CLOCK_ID_PERIPH] = get_clock_freq(CLOCK_ID_PERIPH); + pll_rate[CLOCK_ID_SFROM32KHZ] = 32768; + debug("PLLM = %d\n", pll_rate[CLOCK_ID_MEMORY]); + debug("PLLP = %d\n", pll_rate[CLOCK_ID_PERIPH]); +} diff --git a/arch/arm/include/asm/arch-tegra2/clk_rst.h b/arch/arm/include/asm/arch-tegra2/clk_rst.h index 09c8d14d0ea..e54c54f4410 100644 --- a/arch/arm/include/asm/arch-tegra2/clk_rst.h +++ b/arch/arm/include/asm/arch-tegra2/clk_rst.h @@ -44,9 +44,12 @@ struct clk_pll_simple { * structure for which we use clk_pll_simple. The reason for this non- * othogonal setup is not stated. */ -#define TEGRA_CLK_PLLS 6 -#define TEGRA_CLK_SIMPLE_PLLS 3 /* Number of simple PLLs */ -#define TEGRA_CLK_REGS 3 /* Number of clock enable registers */ +enum { + TEGRA_CLK_PLLS = 6, /* Number of normal PLLs */ + TEGRA_CLK_SIMPLE_PLLS = 3, /* Number of simple PLLs */ + TEGRA_CLK_REGS = 3, /* Number of clock enable registers */ + TEGRA_CLK_SOURCES = 64, /* Number of peripheral clock sources */ +}; /* Clock/Reset Controller (CLK_RST_CONTROLLER_) regs */ struct clk_rst_ctlr { @@ -80,65 +83,10 @@ struct clk_rst_ctlr { uint crc_reserved10; /* _reserved_10, 0xF8 */ uint crc_reserved11; /* _reserved_11, 0xFC */ - uint crc_clk_src_i2s1; /*_I2S1_0, 0x100 */ - uint crc_clk_src_i2s2; /*_I2S2_0, 0x104 */ - uint crc_clk_src_spdif_out; /*_SPDIF_OUT_0, 0x108 */ - uint crc_clk_src_spdif_in; /*_SPDIF_IN_0, 0x10C */ - uint crc_clk_src_pwm; /*_PWM_0, 0x110 */ - uint crc_clk_src_spi1; /*_SPI1_0, 0x114 */ - uint crc_clk_src_sbc2; /*_SBC2_0, 0x118 */ - uint crc_clk_src_sbc3; /*_SBC3_0, 0x11C */ - uint crc_clk_src_xio; /*_XIO_0, 0x120 */ - uint crc_clk_src_i2c1; /*_I2C1_0, 0x124 */ - uint crc_clk_src_dvc_i2c; /*_DVC_I2C_0, 0x128 */ - uint crc_clk_src_twc; /*_TWC_0, 0x12C */ - uint crc_reserved12; /* 0x130 */ - uint crc_clk_src_sbc1; /*_SBC1_0, 0x134 */ - uint crc_clk_src_disp1; /*_DISP1_0, 0x138 */ - uint crc_clk_src_disp2; /*_DISP2_0, 0x13C */ - uint crc_clk_src_cve; /*_CVE_0, 0x140 */ - uint crc_clk_src_ide; /*_IDE_0, 0x144 */ - uint crc_clk_src_vi; /*_VI_0, 0x148 */ - uint crc_reserved13; /* 0x14C */ - uint crc_clk_src_sdmmc1; /*_SDMMC1_0, 0x150 */ - uint crc_clk_src_sdmmc2; /*_SDMMC2_0, 0x154 */ - uint crc_clk_src_g3d; /*_G3D_0, 0x158 */ - uint crc_clk_src_g2d; /*_G2D_0, 0x15C */ - uint crc_clk_src_ndflash; /*_NDFLASH_0, 0x160 */ - uint crc_clk_src_sdmmc4; /*_SDMMC4_0, 0x164 */ - uint crc_clk_src_vfir; /*_VFIR_0, 0x168 */ - uint crc_clk_src_epp; /*_EPP_0, 0x16C */ - uint crc_clk_src_mp3; /*_MPE_0, 0x170 */ - uint crc_clk_src_mipi; /*_MIPI_0, 0x174 */ - uint crc_clk_src_uarta; /*_UARTA_0, 0x178 */ - uint crc_clk_src_uartb; /*_UARTB_0, 0x17C */ - uint crc_clk_src_host1x; /*_HOST1X_0, 0x180 */ - uint crc_reserved14; /* 0x184 */ - uint crc_clk_src_tvo; /*_TVO_0, 0x188 */ - uint crc_clk_src_hdmi; /*_HDMI_0, 0x18C */ - uint crc_reserved15; /* 0x190 */ - uint crc_clk_src_tvdac; /*_TVDAC_0, 0x194 */ - uint crc_clk_src_i2c2; /*_I2C2_0, 0x198 */ - uint crc_clk_src_emc; /*_EMC_0, 0x19C */ - uint crc_clk_src_uartc; /*_UARTC_0, 0x1A0 */ - uint crc_reserved16; /* 0x1A4 */ - uint crc_clk_src_vi_sensor; /*_VI_SENSOR_0, 0x1A8 */ - uint crc_reserved17; /* 0x1AC */ - uint crc_reserved18; /* 0x1B0 */ - uint crc_clk_src_sbc4; /*_SBC4_0, 0x1B4 */ - uint crc_clk_src_i2c3; /*_I2C3_0, 0x1B8 */ - uint crc_clk_src_sdmmc3; /*_SDMMC3_0, 0x1BC */ - uint crc_clk_src_uartd; /*_UARTD_0, 0x1C0 */ - uint crc_clk_src_uarte; /*_UARTE_0, 0x1C4 */ - uint crc_clk_src_vde; /*_VDE_0, 0x1C8 */ - uint crc_clk_src_owr; /*_OWR_0, 0x1CC */ - uint crc_clk_src_nor; /*_NOR_0, 0x1D0 */ - uint crc_clk_src_csite; /*_CSITE_0, 0x1D4 */ - uint crc_reserved19[9]; /* 0x1D8-1F8 */ - uint crc_clk_src_osc; /*_OSC_0, 0x1FC */ + uint crc_clk_src[TEGRA_CLK_SOURCES]; /*_I2S1_0... 0x100-1fc */ uint crc_reserved20[80]; /* 0x200-33C */ - uint crc_cpu_cmplx_set; /* _CPU_CMPLX_SET_0, 0x340 */ - uint crc_cpu_cmplx_clr; /* _CPU_CMPLX_CLR_0, 0x344 */ + uint crc_cpu_cmplx_set; /* _CPU_CMPLX_SET_0, 0x340 */ + uint crc_cpu_cmplx_clr; /* _CPU_CMPLX_CLR_0, 0x344 */ }; /* CLK_RST_CONTROLLER_CLK_CPU_CMPLX_0 */ @@ -162,4 +110,9 @@ struct clk_rst_ctlr { /* CLK_RST_CONTROLLER_OSC_CTRL_0 */ #define OSC_FREQ_RANGE 31:30 +/* CLK_RST_CONTROLLER_CLK_SOURCE_x_OUT_0 */ +#define OUT_CLK_DIVISOR_RANGE 7:0 +#define OUT_CLK_SOURCE_RANGE 31:30 +#define OUT_CLK_SOURCE4_RANGE 31:28 + #endif /* CLK_RST_H */ diff --git a/arch/arm/include/asm/arch-tegra2/clock.h b/arch/arm/include/asm/arch-tegra2/clock.h index 4ab9b0dbfea..eef82eb0b63 100644 --- a/arch/arm/include/asm/arch-tegra2/clock.h +++ b/arch/arm/include/asm/arch-tegra2/clock.h @@ -23,7 +23,6 @@ #ifndef _CLOCK_H - /* Set of oscillator frequencies supported in the internal API. */ enum clock_osc_freq { /* All in MHz, so 13_0 is 13.0MHz */ @@ -36,7 +35,7 @@ enum clock_osc_freq { }; /* The PLLs supported by the hardware */ -enum clock_pll_id { +enum clock_id { CLOCK_ID_FIRST, CLOCK_ID_CGENERAL = CLOCK_ID_FIRST, CLOCK_ID_MEMORY, @@ -51,7 +50,16 @@ enum clock_pll_id { CLOCK_ID_EPCI, CLOCK_ID_SFROM32KHZ, - CLOCK_ID_COUNT, + /* + * Not really a PLL, but a clock. Let's put these in here so we + * don't have two number ranges. + * TODO: rename all the CLOCK_PLL_ID_... to CLOCK_ID_... also. + */ + CLOCK_ID_32KHZ, + CLOCK_ID_OSC, + + CLOCK_ID_COUNT, /* number of PLLs */ + CLOCK_ID_NONE = -1, }; /* The clocks supported by the hardware */ @@ -80,7 +88,7 @@ enum periph_id { /* 16 */ PERIPH_ID_TWC, - PERIPH_ID_PWC, + PERIPH_ID_PWM, PERIPH_ID_I2S2, PERIPH_ID_EPP, PERIPH_ID_VI, @@ -181,8 +189,7 @@ enum periph_id { #define PERIPH_MASK(id) (1 << ((id) & 0x1f)) /* return 1 if a PLL ID is in range */ -#define clock_pll_id_isvalid(id) ((id) >= CLOCK_ID_FIRST && \ - (id) < CLOCK_ID_COUNT) +#define clock_id_isvalid(id) ((id) >= CLOCK_ID_FIRST && (id) < CLOCK_ID_COUNT) /* return 1 if a peripheral ID is in range */ #define clock_periph_id_isvalid(id) ((id) >= PERIPH_ID_FIRST && \ @@ -194,7 +201,7 @@ enum periph_id { /* return the current oscillator clock frequency */ enum clock_osc_freq clock_get_osc_freq(void); -/* +/** * Start PLL using the provided configuration parameters. * * @param id clock id @@ -206,7 +213,7 @@ enum clock_osc_freq clock_get_osc_freq(void); * * @returns monotonic time in us that the PLL will be stable */ -unsigned long clock_start_pll(enum clock_pll_id id, u32 divm, u32 divn, +unsigned long clock_start_pll(enum clock_id id, u32 divm, u32 divn, u32 divp, u32 cpcon, u32 lfcon); /* @@ -216,7 +223,7 @@ unsigned long clock_start_pll(enum clock_pll_id id, u32 divm, u32 divn, */ void clock_enable(enum periph_id clkid); -/* +/** * Set whether a clock is enabled or disabled. * * @param id clock id @@ -224,7 +231,7 @@ void clock_enable(enum periph_id clkid); */ void clock_set_enable(enum periph_id clkid, int enable); -/* +/** * Reset a peripheral. This puts it in reset, waits for a delay, then takes * it out of reset and waits for th delay again. * @@ -233,7 +240,7 @@ void clock_set_enable(enum periph_id clkid, int enable); */ void reset_periph(enum periph_id periph_id, int us_delay); -/* +/** * Put a peripheral into or out of reset. * * @param periph_id peripheral to reset @@ -251,7 +258,7 @@ enum crc_reset_id { crc_rst_debug = 1 << 4, }; -/* +/** * Put parts of the CPU complex into or out of reset.\ * * @param cpu cpu number (0 or 1 on Tegra2) @@ -260,5 +267,60 @@ enum crc_reset_id { */ void reset_cmplx_set_enable(int cpu, int which, int reset); +/** + * Set the source for a peripheral clock. This plus the divisor sets the + * clock rate. You need to look up the datasheet to see the meaning of the + * source parameter as it changes for each peripheral. + * + * Warning: This function is only for use pre-relocation. Please use + * clock_start_periph_pll() instead. + * + * @param periph_id peripheral to adjust + * @param source source clock (0, 1, 2 or 3) + */ +void clock_ll_set_source(enum periph_id periph_id, int source); + +/** + * Set the source and divisor for a peripheral clock. This sets the + * clock rate. You need to look up the datasheet to see the meaning of the + * source parameter as it changes for each peripheral. + * + * Warning: This function is only for use pre-relocation. Please use + * clock_start_periph_pll() instead. + * + * @param periph_id peripheral to adjust + * @param source source clock (0, 1, 2 or 3) + * @param divisor divisor value to use + */ +void clock_ll_set_source_divisor(enum periph_id periph_id, int source, + unsigned rate); + +/** + * Start a peripheral PLL clock at the given rate + * + * @param periph_id peripheral to start + * @param parent PLL id of required parent clock + * @param rate Required clock rate in Hz + * @return rate selected, or -1U if something went wrong + */ +unsigned clock_start_periph_pll(enum periph_id periph_id, + enum clock_id parent, unsigned rate); + +/** + * Returns the rate of a peripheral clock in Hz. Since the caller almost + * certainly knows the parent clock (having just set it) we require that + * this be passed in so we don't need to work it out. + * + * @param periph_id peripheral to start + * @param parent PLL id of parent clock (used to calculate rate, you + * must know this!) + * @return clock rate of peripheral in Hz + */ +unsigned long clock_get_periph_rate(enum periph_id periph_id, + enum clock_id parent); + +/* Initialize the clocks */ +void clock_init(void); + #endif |