summaryrefslogtreecommitdiff
path: root/arch
diff options
context:
space:
mode:
authorSimon Glass <sjg@chromium.org>2011-05-19 17:56:33 -0700
committerSimon Glass <sjg@chromium.org>2011-08-24 10:01:51 -0700
commitf22424f2c7b085a88eb6b750c6acca1e2d133e9c (patch)
tree73b4d58fd2af0e64114f6b502a16cf6a33a02bd8 /arch
parenta9177e1137f15505a0a5718e46167f43baede92b (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.c3
-rw-r--r--arch/arm/cpu/armv7/tegra2/clock.c577
-rw-r--r--arch/arm/include/asm/arch-tegra2/clk_rst.h75
-rw-r--r--arch/arm/include/asm/arch-tegra2/clock.h86
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