* [U-Boot] [PATCH v2 3/6] tegra2: Add more clock functions
2011-09-21 22:40 [U-Boot] [PATCH v2 0/6] tegra2: Add more clock/pinmux functionality Simon Glass
2011-09-21 22:40 ` [U-Boot] [PATCH v2 1/6] tegra2: Rename CLOCK_PLL_ID to CLOCK_ID Simon Glass
2011-09-21 22:40 ` [U-Boot] [PATCH v2 2/6] tegra2: Clean up board code a little Simon Glass
@ 2011-09-21 22:40 ` Simon Glass
2011-09-21 22:40 ` [U-Boot] [PATCH v2 4/6] tegra2: Rename PIN_ to PINGRP_ Simon Glass
` (4 subsequent siblings)
7 siblings, 0 replies; 14+ messages in thread
From: Simon Glass @ 2011-09-21 22:40 UTC (permalink / raw)
To: u-boot
This adds most of the clock functions required by board and driver code:
-query and adjust peripheral clocks
-query and adjust PLLs
-reset and enable control
These functions are plumbed in as required.
Signed-off-by: Simon Glass <sjg@chromium.org>
Tested-by: Tom Warren <twarren@nvidia.com>
---
Changes in v2:
- Remove assert() which is now in common.h
arch/arm/cpu/armv7/tegra2/ap20.c | 3 +-
arch/arm/cpu/armv7/tegra2/clock.c | 821 +++++++++++++++++++++++++++-
arch/arm/include/asm/arch-tegra2/clk_rst.h | 84 +--
arch/arm/include/asm/arch-tegra2/clock.h | 108 ++++-
board/nvidia/common/board.c | 105 +---
drivers/mmc/tegra2_mmc.c | 92 +--
drivers/mmc/tegra2_mmc.h | 1 +
7 files changed, 999 insertions(+), 215 deletions(-)
diff --git a/arch/arm/cpu/armv7/tegra2/ap20.c b/arch/arm/cpu/armv7/tegra2/ap20.c
index 64d4c69..5cb4b1b 100644
--- a/arch/arm/cpu/armv7/tegra2/ap20.c
+++ b/arch/arm/cpu/armv7/tegra2/ap20.c
@@ -189,7 +189,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);
@@ -203,7 +202,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 5aa6482..03ac1e3 100644
--- a/arch/arm/cpu/armv7/tegra2/clock.c
+++ b/arch/arm/cpu/armv7/tegra2/clock.c
@@ -27,6 +27,371 @@
#include <asm/arch/timer.h>
#include <asm/arch/tegra2.h>
#include <common.h>
+#include <div64.h>
+
+/*
+ * 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)
+
+char pllp_valid = 1; /* PLLP is set up correctly */
+
+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
@@ -42,16 +407,21 @@ enum clock_osc_freq clock_get_osc_freq(void)
return (reg & OSC_FREQ_MASK) >> OSC_FREQ_SHIFT;
}
-unsigned long clock_start_pll(enum clock_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_id_isvalid(clkid));
- pll = &clkrst->crc_pll[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.
@@ -76,6 +446,294 @@ unsigned long clock_start_pll(enum clock_id clkid, u32 divm, u32 divn,
return timer_get_us() + CLOCK_PLL_STABLE_DELAY_US;
}
+/* return 1 if a peripheral ID is in range and valid */
+static int clock_periph_id_isvalid(enum periph_id id)
+{
+ if (id < PERIPH_ID_FIRST || id >= PERIPH_ID_COUNT)
+ printf("Peripheral id %d out of range\n", id);
+ else {
+ switch (id) {
+ case PERIPH_ID_RESERVED1:
+ case PERIPH_ID_RESERVED2:
+ case PERIPH_ID_RESERVED30:
+ case PERIPH_ID_RESERVED35:
+ case PERIPH_ID_RESERVED56:
+ case PERIPH_ID_RESERVED74:
+ case PERIPH_ID_RESERVED76:
+ case PERIPH_ID_RESERVED77:
+ case PERIPH_ID_RESERVED78:
+ case PERIPH_ID_RESERVED79:
+ case PERIPH_ID_RESERVED80:
+ case PERIPH_ID_RESERVED81:
+ case PERIPH_ID_RESERVED82:
+ case PERIPH_ID_RESERVED83:
+ printf("Peripheral id %d is reserved\n", id);
+ break;
+ default:
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* 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(clock_periph_id_isvalid(periph_id));
+ 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, unsigned source,
+ unsigned divisor)
+{
+ u32 *reg = get_periph_source_reg(periph_id);
+ u32 value;
+
+ value = readl(reg);
+
+ value &= ~OUT_CLK_SOURCE_MASK;
+ value |= source << OUT_CLK_SOURCE_SHIFT;
+
+ value &= ~OUT_CLK_DIVISOR_MASK;
+ value |= divisor << OUT_CLK_DIVISOR_SHIFT;
+
+ writel(value, reg);
+}
+
+void clock_ll_set_source(enum periph_id periph_id, unsigned source)
+{
+ u32 *reg = get_periph_source_reg(periph_id);
+
+ clrsetbits_le32(reg, OUT_CLK_SOURCE_MASK,
+ source << OUT_CLK_SOURCE_SHIFT);
+}
+
+/**
+ * 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
+ * @return divider which should be used
+ */
+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;
+}
+
+/**
+ * Given the parent's rate and the divider in 7.1 format, this works out the
+ * resulting peripheral clock rate.
+ *
+ * @param parent_rate clock rate of parent clock in Hz
+ * @param divider which should be used in 7.1 format
+ * @return effective clock rate of peripheral
+ */
+static unsigned long get_rate_from_divider(unsigned long parent_rate,
+ int divider)
+{
+ u64 rate;
+
+ rate = (u64)parent_rate * 2;
+ do_div(rate, divider + 2);
+ return rate;
+}
+
+unsigned long clock_get_periph_rate(enum periph_id periph_id,
+ enum clock_id parent)
+{
+ u32 *reg = get_periph_source_reg(periph_id);
+
+ return get_rate_from_divider(pll_rate[parent],
+ (readl(reg) & OUT_CLK_DIVISOR_MASK) >> OUT_CLK_DIVISOR_SHIFT);
+}
+
+/**
+ * Find the best available 7.1 format divisor given a parent clock rate and
+ * required child clock rate. This function assumes that a second-stage
+ * divisor is available which can divide by powers of 2 from 1 to 256.
+ *
+ * @param parent_rate clock rate of parent clock in Hz
+ * @param rate required clock rate for this clock
+ * @param extra_div value for the second-stage divisor (not set if this
+ * function returns -1.
+ * @return divider which should be used, or -1 if nothing is valid
+ *
+ */
+static int find_best_divider(unsigned long parent_rate, unsigned long rate,
+ int *extra_div)
+{
+ int shift;
+ int best_divider = -1;
+ int best_error = rate;
+
+ /* try dividers from 1 to 256 and find closest match */
+ for (shift = 0; shift <= 8 && best_error > 0; shift++) {
+ unsigned divided_parent = parent_rate >> shift;
+ int divider = clk_div7_1_get_divider(divided_parent, rate);
+ unsigned effective_rate = get_rate_from_divider(divided_parent,
+ divider);
+ int error = rate - effective_rate;
+
+ /* Given a valid divider, look for the lowest error */
+ if (divider != -1 && error < best_error) {
+ best_error = error;
+ *extra_div = 1 << shift;
+ best_divider = divider;
+ }
+ }
+
+ /* return what we found - *extra_div will already be set */
+ return best_divider;
+}
+
+/**
+ * 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;
+}
+
+/**
+ * Adjust peripheral PLL to use the given divider and source.
+ *
+ * @param periph_id peripheral to adjust
+ * @param parent Required parent clock (for source mux)
+ * @param divider Required divider in 7.1 format
+ * @return 0 if ok, -1 on error (requesting a parent clock which is not valid
+ * for this peripheral)
+ */
+static int adjust_periph_pll(enum periph_id periph_id,
+ enum clock_id parent, unsigned divider)
+{
+ u32 *reg = get_periph_source_reg(periph_id);
+ unsigned source;
+ int mux_bits;
+
+ clrsetbits_le32(reg, OUT_CLK_DIVISOR_MASK,
+ divider << OUT_CLK_DIVISOR_SHIFT);
+ udelay(1);
+
+ /* work out the source clock and set it */
+ source = get_periph_clock_source(periph_id, parent, &mux_bits);
+ if (source < 0)
+ return -1;
+ if (mux_bits == 4) {
+ clrsetbits_le32(reg, OUT_CLK_SOURCE4_MASK,
+ source << OUT_CLK_SOURCE4_SHIFT);
+ } else {
+ clrsetbits_le32(reg, OUT_CLK_SOURCE_MASK,
+ source << OUT_CLK_SOURCE_SHIFT);
+ }
+ udelay(2);
+ return 0;
+}
+
+unsigned clock_adjust_periph_pll_div(enum periph_id periph_id,
+ enum clock_id parent, unsigned rate, int *extra_div)
+{
+ unsigned effective_rate;
+ int divider;
+
+ if (extra_div)
+ divider = find_best_divider(pll_rate[parent], rate, extra_div);
+ else
+ divider = clk_div7_1_get_divider(pll_rate[parent], rate);
+ assert(divider >= 0);
+ if (adjust_periph_pll(periph_id, parent, divider))
+ return -1U;
+ debug("periph %d, rate=%d, reg=%p = %x\n", periph_id, rate,
+ get_periph_source_reg(periph_id),
+ readl(get_periph_source_reg(periph_id)));
+
+ /* Check what we ended up with. This shouldn't matter though */
+ effective_rate = clock_get_periph_rate(periph_id, parent);
+ if (extra_div)
+ effective_rate /= *extra_div;
+ if (rate != effective_rate)
+ debug("Requested clock rate %u not honored (got %u)\n",
+ rate, effective_rate);
+ return effective_rate;
+}
+
+unsigned clock_start_periph_pll(enum periph_id periph_id,
+ enum clock_id parent, unsigned rate)
+{
+ unsigned effective_rate;
+
+ reset_set_enable(periph_id, 1);
+ clock_enable(periph_id);
+
+ effective_rate = clock_adjust_periph_pll_div(periph_id, parent, rate,
+ NULL);
+
+ 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 =
@@ -148,3 +806,156 @@ void reset_cmplx_set_enable(int cpu, int which, int reset)
else
writel(mask, &clkrst->crc_cpu_cmplx_clr);
}
+
+unsigned clock_get_rate(enum clock_id clkid)
+{
+ struct clk_pll *pll;
+ u32 base;
+ u32 divm;
+ u64 parent_rate;
+ u64 rate;
+
+ parent_rate = osc_freq[clock_get_osc_freq()];
+ if (clkid == CLOCK_ID_OSC)
+ return parent_rate;
+
+ pll = get_pll(clkid);
+ base = readl(&pll->pll_base);
+
+ /* Oh for bf_unpack()... */
+ rate = parent_rate * ((base & PLL_DIVN_MASK) >> PLL_DIVN_SHIFT);
+ divm = (base & PLL_DIVM_MASK) >> PLL_DIVM_SHIFT;
+ if (clkid == CLOCK_ID_USB)
+ divm <<= (base & PLLU_VCO_FREQ_MASK) >> PLLU_VCO_FREQ_SHIFT;
+ else
+ divm <<= (base & PLL_DIVP_MASK) >> PLL_DIVP_SHIFT;
+ do_div(rate, divm);
+ return rate;
+}
+
+/**
+ * Set the output frequency you want for each PLL clock.
+ * PLL output frequencies are programmed by setting their N, M and P values.
+ * The governing equations are:
+ * VCO = (Fi / m) * n, Fo = VCO / (2^p)
+ * where Fo is the output frequency from the PLL.
+ * Example: Set the output frequency to 216Mhz(Fo) with 12Mhz OSC(Fi)
+ * 216Mhz = ((12Mhz / m) * n) / (2^p) so n=432,m=12,p=1
+ * Please see Tegra TRM section 5.3 to get the detail for PLL Programming
+ *
+ * @param n PLL feedback divider(DIVN)
+ * @param m PLL input divider(DIVN)
+ * @param p post divider(DIVP)
+ * @param cpcon base PLL charge pump(CPCON)
+ * @return 0 if ok, -1 on error (the requested PLL is incorrect and cannot
+ * be overriden), 1 if PLL is already correct
+ */
+static int clock_set_rate(enum clock_id clkid, u32 n, u32 m, u32 p, u32 cpcon)
+{
+ u32 base_reg;
+ u32 misc_reg;
+ struct clk_pll *pll;
+
+ pll = get_pll(clkid);
+
+ base_reg = readl(&pll->pll_base);
+
+ /* Set BYPASS, m, n and p to PLL_BASE */
+ base_reg &= ~PLL_DIVM_MASK;
+ base_reg |= m << PLL_DIVM_SHIFT;
+
+ base_reg &= ~PLL_DIVN_MASK;
+ base_reg |= n << PLL_DIVN_SHIFT;
+
+ base_reg &= ~PLL_DIVP_MASK;
+ base_reg |= p << PLL_DIVP_SHIFT;
+
+ if (clkid == CLOCK_ID_PERIPH) {
+ /*
+ * If the PLL is already set up, check that it is correct
+ * and record this info for clock_verify() to check.
+ */
+ if (base_reg & PLL_BASE_OVRRIDE_MASK) {
+ base_reg |= PLL_ENABLE_MASK;
+ if (base_reg != readl(&pll->pll_base))
+ pllp_valid = 0;
+ return pllp_valid ? 1 : -1;
+ }
+ base_reg |= PLL_BASE_OVRRIDE_MASK;
+ }
+
+ base_reg |= PLL_BYPASS_MASK;
+ writel(base_reg, &pll->pll_base);
+
+ /* Set cpcon to PLL_MISC */
+ misc_reg = readl(&pll->pll_misc);
+ misc_reg &= ~PLL_CPCON_MASK;
+ misc_reg |= cpcon << PLL_CPCON_SHIFT;
+ writel(misc_reg, &pll->pll_misc);
+
+ /* Enable PLL */
+ base_reg |= PLL_ENABLE_MASK;
+ writel(base_reg, &pll->pll_base);
+
+ /* Disable BYPASS */
+ base_reg &= ~PLL_BYPASS_MASK;
+ writel(base_reg, &pll->pll_base);
+
+ return 0;
+}
+
+int clock_verify(void)
+{
+ struct clk_pll *pll = get_pll(CLOCK_ID_PERIPH);
+ u32 reg = readl(&pll->pll_base);
+
+ if (!pllp_valid) {
+ printf("Warning: PLLP %x is not correct\n", reg);
+ return -1;
+ }
+ debug("PLLX %x is correct\n", reg);
+ return 0;
+}
+
+void clock_early_init(void)
+{
+ /*
+ * PLLP output frequency set to 216MHz
+ * PLLC output frequency set to 600Mhz
+ *
+ * TODO: Can we calculate these values instead of hard-coding?
+ */
+ switch (clock_get_osc_freq()) {
+ case CLOCK_OSC_FREQ_12_0: /* OSC is 12Mhz */
+ clock_set_rate(CLOCK_ID_PERIPH, 432, 12, 1, 8);
+ clock_set_rate(CLOCK_ID_CGENERAL, 600, 12, 0, 8);
+ break;
+
+ case CLOCK_OSC_FREQ_26_0: /* OSC is 26Mhz */
+ clock_set_rate(CLOCK_ID_PERIPH, 432, 26, 1, 8);
+ clock_set_rate(CLOCK_ID_CGENERAL, 600, 26, 0, 8);
+ break;
+
+ case CLOCK_OSC_FREQ_13_0:
+ case CLOCK_OSC_FREQ_19_2:
+ default:
+ /*
+ * These are not supported. It is too early to print a
+ * message and the UART likely won't work anyway due to the
+ * oscillator being wrong.
+ */
+ break;
+ }
+}
+
+void clock_init(void)
+{
+ pll_rate[CLOCK_ID_MEMORY] = clock_get_rate(CLOCK_ID_MEMORY);
+ pll_rate[CLOCK_ID_PERIPH] = clock_get_rate(CLOCK_ID_PERIPH);
+ pll_rate[CLOCK_ID_CGENERAL] = clock_get_rate(CLOCK_ID_CGENERAL);
+ pll_rate[CLOCK_ID_OSC] = clock_get_rate(CLOCK_ID_OSC);
+ pll_rate[CLOCK_ID_SFROM32KHZ] = 32768;
+ debug("Osc = %d\n", pll_rate[CLOCK_ID_OSC]);
+ 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 bd9d9ad..0b6e004 100644
--- a/arch/arm/include/asm/arch-tegra2/clk_rst.h
+++ b/arch/arm/include/asm/arch-tegra2/clk_rst.h
@@ -43,9 +43,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 {
@@ -79,65 +82,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 */
@@ -156,10 +104,13 @@ struct clk_rst_ctlr {
#define PLL_BASE_OVRRIDE_MASK (1U << 28)
#define PLL_DIVP_SHIFT 20
+#define PLL_DIVP_MASK (7U << PLL_DIVP_SHIFT)
#define PLL_DIVN_SHIFT 8
+#define PLL_DIVN_MASK (0x3ffU << PLL_DIVN_SHIFT)
#define PLL_DIVM_SHIFT 0
+#define PLL_DIVM_MASK (0x1f << PLL_DIVM_SHIFT)
/* CLK_RST_CONTROLLER_PLLx_MISC_0 */
#define PLL_CPCON_SHIFT 8
@@ -168,9 +119,20 @@ struct clk_rst_ctlr {
#define PLL_LFCON_SHIFT 4
#define PLLU_VCO_FREQ_SHIFT 20
+#define PLLU_VCO_FREQ_MASK (1U << PLLU_VCO_FREQ_SHIFT)
/* CLK_RST_CONTROLLER_OSC_CTRL_0 */
#define OSC_FREQ_SHIFT 30
#define OSC_FREQ_MASK (3U << OSC_FREQ_SHIFT)
+/* CLK_RST_CONTROLLER_CLK_SOURCE_x_OUT_0 */
+#define OUT_CLK_DIVISOR_SHIFT 0
+#define OUT_CLK_DIVISOR_MASK (255 << OUT_CLK_DIVISOR_SHIFT)
+
+#define OUT_CLK_SOURCE_SHIFT 30
+#define OUT_CLK_SOURCE_MASK (3U << OUT_CLK_SOURCE_SHIFT)
+
+#define OUT_CLK_SOURCE4_SHIFT 28
+#define OUT_CLK_SOURCE4_MASK (15U << OUT_CLK_SOURCE4_SHIFT)
+
#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 8adb23c..49e9904 100644
--- a/arch/arm/include/asm/arch-tegra2/clock.h
+++ b/arch/arm/include/asm/arch-tegra2/clock.h
@@ -51,7 +51,12 @@ enum clock_id {
CLOCK_ID_EPCI,
CLOCK_ID_SFROM32KHZ,
- CLOCK_ID_COUNT,
+ /* These are the base clocks (inputs to the Tegra SOC) */
+ CLOCK_ID_32KHZ,
+ CLOCK_ID_OSC,
+
+ CLOCK_ID_COUNT, /* number of clocks */
+ CLOCK_ID_NONE = -1,
};
/* The clocks supported by the hardware */
@@ -183,10 +188,6 @@ enum periph_id {
/* return 1 if a PLL ID is in range */
#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 && \
- (id) < PERIPH_ID_COUNT)
-
/* PLL stabilization delay in usec */
#define CLOCK_PLL_STABLE_DELAY_US 300
@@ -216,6 +217,13 @@ unsigned long clock_start_pll(enum clock_id id, u32 divm, u32 divn,
void clock_enable(enum periph_id clkid);
/*
+ * Disable a clock
+ *
+ * @param id clock id
+ */
+void clock_disable(enum periph_id clkid);
+
+/*
* Set whether a clock is enabled or disabled.
*
* @param id clock id
@@ -259,4 +267,94 @@ 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, unsigned 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, unsigned source,
+ unsigned divisor);
+
+/**
+ * Start a peripheral PLL clock@the given rate. This also resets the
+ * peripheral.
+ *
+ * @param periph_id peripheral to start
+ * @param parent PLL id of required parent clock
+ * @param rate Required clock rate in Hz
+ * @return rate selected in Hz, 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);
+
+/**
+ * Adjust peripheral PLL clock to the given rate. This does not reset the
+ * peripheral. If a second stage divisor is not available, pass NULL for
+ * extra_div. If it is available, then this parameter will return the
+ * divisor selected (which will be a power of 2 from 1 to 256).
+ *
+ * @param periph_id peripheral to start
+ * @param parent PLL id of required parent clock
+ * @param rate Required clock rate in Hz
+ * @param extra_div value for the second-stage divisor (NULL if one is
+ not available)
+ * @return rate selected in Hz, or -1U if something went wrong
+ */
+unsigned clock_adjust_periph_pll_div(enum periph_id periph_id,
+ enum clock_id parent, unsigned rate, int *extra_div);
+
+/**
+ * Returns the clock rate of a specified clock, in Hz.
+ *
+ * @param parent PLL id of clock to check
+ * @return rate of clock in Hz
+ */
+unsigned clock_get_rate(enum clock_id clkid);
+
+/*
+ * Checks that clocks are valid and prints a warning if not
+ *
+ * @return 0 if ok, -1 on error
+ */
+int clock_verify(void);
+
+/* Initialize the clocks */
+void clock_init(void);
+
+/* Initialize the PLLs */
+void clock_early_init(void);
+
#endif
diff --git a/board/nvidia/common/board.c b/board/nvidia/common/board.c
index 5e28b4c..9672c5e 100644
--- a/board/nvidia/common/board.c
+++ b/board/nvidia/common/board.c
@@ -52,62 +52,31 @@ int timer_init(void)
return 0;
}
-/*
- * Routine: clock_init_uart
- * Description: init the PLL and clock for the UART(s)
- */
-static void clock_init_uart(void)
+static void enable_uart(enum periph_id pid)
{
- struct clk_rst_ctlr *clkrst = (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE;
- struct clk_pll *pll = &clkrst->crc_pll[CLOCK_ID_PERIPH];
- u32 reg;
-
- reg = readl(&pll->pll_base);
- if (!(reg & PLL_BASE_OVRRIDE_MASK)) {
- /* Override pllp setup for 216MHz operation. */
- reg = PLL_BYPASS_MASK | PLL_BASE_OVRRIDE_MASK |
- (1 << PLL_DIVP_SHIFT) | (0xc << PLL_DIVM_SHIFT);
- reg |= (NVRM_PLLP_FIXED_FREQ_KHZ / 500) << PLL_DIVN_SHIFT;
- writel(reg, &pll->pll_base);
-
- reg |= PLL_ENABLE_MASK;
- writel(reg, &pll->pll_base);
-
- reg &= ~PLL_BYPASS_MASK;
- writel(reg, &pll->pll_base);
- }
-
-#if defined(CONFIG_TEGRA2_ENABLE_UARTA)
/* Assert UART reset and enable clock */
- reset_set_enable(PERIPH_ID_UART1, 1);
- clock_enable(PERIPH_ID_UART1);
-
- /* Enable pllp_out0 to UART */
- reg = readl(&clkrst->crc_clk_src_uarta);
- reg &= 0x3FFFFFFF; /* UARTA_CLK_SRC = 00, PLLP_OUT0 */
- writel(reg, &clkrst->crc_clk_src_uarta);
+ reset_set_enable(pid, 1);
+ clock_enable(pid);
+ clock_ll_set_source(pid, 0); /* UARTx_CLK_SRC = 00, PLLP_OUT0 */
/* wait for 2us */
udelay(2);
/* De-assert reset to UART */
- reset_set_enable(PERIPH_ID_UART1, 0);
+ reset_set_enable(pid, 0);
+}
+
+/*
+ * Routine: clock_init_uart
+ * Description: init the PLL and clock for the UART(s)
+ */
+static void clock_init_uart(void)
+{
+#if defined(CONFIG_TEGRA2_ENABLE_UARTA)
+ enable_uart(PERIPH_ID_UART1);
#endif /* CONFIG_TEGRA2_ENABLE_UARTA */
#if defined(CONFIG_TEGRA2_ENABLE_UARTD)
- /* Assert UART reset and enable clock */
- reset_set_enable(PERIPH_ID_UART4, 1);
- clock_enable(PERIPH_ID_UART4);
-
- /* Enable pllp_out0 to UART */
- reg = readl(&clkrst->crc_clk_src_uartd);
- reg &= 0x3FFFFFFF; /* UARTD_CLK_SRC = 00, PLLP_OUT0 */
- writel(reg, &clkrst->crc_clk_src_uartd);
-
- /* wait for 2us */
- udelay(2);
-
- /* De-assert reset to UART */
- reset_set_enable(PERIPH_ID_UART4, 0);
+ enable_uart(PERIPH_ID_UART4);
#endif /* CONFIG_TEGRA2_ENABLE_UARTD */
}
@@ -144,40 +113,8 @@ static void pin_mux_uart(void)
*/
static void clock_init_mmc(void)
{
- struct clk_rst_ctlr *clkrst = (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE;
- u32 reg;
-
- /* Do the SDMMC resets/clock enables */
- reset_set_enable(PERIPH_ID_SDMMC4, 1);
- clock_enable(PERIPH_ID_SDMMC4);
-
- /* Enable pllp_out0 to SDMMC4 */
- reg = readl(&clkrst->crc_clk_src_sdmmc4);
- reg &= 0x3FFFFF00; /* SDMMC4_CLK_SRC = 00, PLLP_OUT0 */
- reg |= (10 << 1); /* n-1, 11-1 shl 1 */
- writel(reg, &clkrst->crc_clk_src_sdmmc4);
-
- /*
- * As per the Tegra2 TRM, section 5.3.4:
- * 'Wait 2 us for the clock to flush through the pipe/logic'
- */
- udelay(2);
-
- reset_set_enable(PERIPH_ID_SDMMC4, 1);
-
- reset_set_enable(PERIPH_ID_SDMMC3, 1);
- clock_enable(PERIPH_ID_SDMMC3);
-
- /* Enable pllp_out0 to SDMMC4, set divisor to 11 for 20MHz */
- reg = readl(&clkrst->crc_clk_src_sdmmc3);
- reg &= 0x3FFFFF00; /* SDMMC3_CLK_SRC = 00, PLLP_OUT0 */
- reg |= (10 << 1); /* n-1, 11-1 shl 1 */
- writel(reg, &clkrst->crc_clk_src_sdmmc3);
-
- /* wait for 2us */
- udelay(2);
-
- reset_set_enable(PERIPH_ID_SDMMC3, 0);
+ clock_start_periph_pll(PERIPH_ID_SDMMC4, CLOCK_ID_PERIPH, 20000000);
+ clock_start_periph_pll(PERIPH_ID_SDMMC3, CLOCK_ID_PERIPH, 20000000);
}
/*
@@ -226,6 +163,9 @@ static void pin_mux_mmc(void)
*/
int board_init(void)
{
+ clock_init();
+ clock_verify();
+
/* boot param addr */
gd->bd->bi_boot_params = (NV_PA_SDRAM_BASE + 0x100);
@@ -268,6 +208,9 @@ int board_mmc_getcd(u8 *cd, struct mmc *mmc)
#ifdef CONFIG_BOARD_EARLY_INIT_F
int board_early_init_f(void)
{
+ /* Initialize essential common plls */
+ clock_early_init();
+
/* Initialize UART clocks */
clock_init_uart();
diff --git a/drivers/mmc/tegra2_mmc.c b/drivers/mmc/tegra2_mmc.c
index 8b6f829..a71785c 100644
--- a/drivers/mmc/tegra2_mmc.c
+++ b/drivers/mmc/tegra2_mmc.c
@@ -23,36 +23,46 @@
#include <mmc.h>
#include <asm/io.h>
#include <asm/arch/clk_rst.h>
+#include <asm/arch/clock.h>
#include "tegra2_mmc.h"
/* support 4 mmc hosts */
struct mmc mmc_dev[4];
struct mmc_host mmc_host[4];
-static inline struct tegra2_mmc *tegra2_get_base_mmc(int dev_index)
+
+/**
+ * Get the host address and peripheral ID for a device. Devices are numbered
+ * from 0 to 3.
+ *
+ * @param host Structure to fill in (base, reg, mmc_id)
+ * @param dev_index Device index (0-3)
+ */
+static void tegra2_get_setup(struct mmc_host *host, int dev_index)
{
- unsigned long offset;
debug("tegra2_get_base_mmc: dev_index = %d\n", dev_index);
switch (dev_index) {
- case 0:
- offset = TEGRA2_SDMMC4_BASE;
- break;
case 1:
- offset = TEGRA2_SDMMC3_BASE;
+ host->base = TEGRA2_SDMMC3_BASE;
+ host->mmc_id = PERIPH_ID_SDMMC3;
break;
case 2:
- offset = TEGRA2_SDMMC2_BASE;
+ host->base = TEGRA2_SDMMC2_BASE;
+ host->mmc_id = PERIPH_ID_SDMMC2;
break;
case 3:
- offset = TEGRA2_SDMMC1_BASE;
+ host->base = TEGRA2_SDMMC1_BASE;
+ host->mmc_id = PERIPH_ID_SDMMC1;
break;
+ case 0:
default:
- offset = TEGRA2_SDMMC4_BASE;
+ host->base = TEGRA2_SDMMC4_BASE;
+ host->mmc_id = PERIPH_ID_SDMMC4;
break;
}
- return (struct tegra2_mmc *)(offset);
+ host->reg = (struct tegra2_mmc *)host->base;
}
static void mmc_prepare_data(struct mmc_host *host, struct mmc_data *data)
@@ -274,62 +284,24 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
static void mmc_change_clock(struct mmc_host *host, uint clock)
{
- int div, hw_div;
+ int div;
unsigned short clk;
unsigned long timeout;
- unsigned int reg, hostbase;
- struct clk_rst_ctlr *clkrst = (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE;
+
debug(" mmc_change_clock called\n");
- /* Change Tegra2 SDMMCx clock divisor here */
- /* Source is 216MHz, PLLP_OUT0 */
+ /*
+ * Change Tegra2 SDMMCx clock divisor here. Source is 216MHz,
+ * PLLP_OUT0
+ */
if (clock == 0)
goto out;
-
- div = 1;
- if (clock <= 400000) {
- hw_div = ((9-1)<<1); /* Best match is 375KHz */
- div = 64;
- } else if (clock <= 20000000)
- hw_div = ((11-1)<<1); /* Best match is 19.6MHz */
- else if (clock <= 26000000)
- hw_div = ((9-1)<<1); /* Use 24MHz */
- else
- hw_div = ((4-1)<<1) + 1; /* 4.5 divisor for 48MHz */
-
- debug("mmc_change_clock: hw_div = %d, card clock div = %d\n",
- hw_div, div);
-
- /* Change SDMMCx divisor */
-
- hostbase = readl(&host->base);
- debug("mmc_change_clock: hostbase = %08X\n", hostbase);
-
- if (hostbase == TEGRA2_SDMMC1_BASE) {
- reg = readl(&clkrst->crc_clk_src_sdmmc1);
- reg &= 0xFFFFFF00; /* divisor (7.1) = 00 */
- reg |= hw_div; /* n-1 */
- writel(reg, &clkrst->crc_clk_src_sdmmc1);
- } else if (hostbase == TEGRA2_SDMMC2_BASE) {
- reg = readl(&clkrst->crc_clk_src_sdmmc2);
- reg &= 0xFFFFFF00; /* divisor (7.1) = 00 */
- reg |= hw_div; /* n-1 */
- writel(reg, &clkrst->crc_clk_src_sdmmc2);
- } else if (hostbase == TEGRA2_SDMMC3_BASE) {
- reg = readl(&clkrst->crc_clk_src_sdmmc3);
- reg &= 0xFFFFFF00; /* divisor (7.1) = 00 */
- reg |= hw_div; /* n-1 */
- writel(reg, &clkrst->crc_clk_src_sdmmc3);
- } else {
- reg = readl(&clkrst->crc_clk_src_sdmmc4);
- reg &= 0xFFFFFF00; /* divisor (7.1) = 00 */
- reg |= hw_div; /* n-1 */
- writel(reg, &clkrst->crc_clk_src_sdmmc4);
- }
+ clock_adjust_periph_pll_div(host->mmc_id, CLOCK_ID_PERIPH, clock,
+ &div);
+ debug("div = %d\n", div);
writew(0, &host->reg->clkcon);
- div >>= 1;
/*
* CLKCON
* SELFREQ[15:8] : base clock divided by value
@@ -337,6 +309,7 @@ static void mmc_change_clock(struct mmc_host *host, uint clock)
* STBLINTCLK[1] : Internal Clock Stable
* ENINTCLK[0] : Internal Clock Enable
*/
+ div >>= 1;
clk = (div << 8) | (1 << 0);
writew(clk, &host->reg->clkcon);
@@ -355,7 +328,6 @@ static void mmc_change_clock(struct mmc_host *host, uint clock)
writew(clk, &host->reg->clkcon);
debug("mmc_change_clock: clkcon = %08X\n", clk);
- debug("mmc_change_clock: CLK_SOURCE_SDMMCx = %08X\n", reg);
out:
host->clock = clock;
@@ -370,7 +342,6 @@ static void mmc_set_ios(struct mmc *mmc)
debug("bus_width: %x, clock: %d\n", mmc->bus_width, mmc->clock);
/* Change clock first */
-
mmc_change_clock(host, mmc->clock);
ctrl = readb(&host->reg->hostctl);
@@ -495,8 +466,7 @@ static int tegra2_mmc_initialize(int dev_index, int bus_width)
mmc->f_max = 48000000;
mmc_host[dev_index].clock = 0;
- mmc_host[dev_index].reg = tegra2_get_base_mmc(dev_index);
- mmc_host[dev_index].base = (unsigned int)mmc_host[dev_index].reg;
+ tegra2_get_setup(&mmc_host[dev_index], dev_index);
mmc_register(mmc);
return 0;
diff --git a/drivers/mmc/tegra2_mmc.h b/drivers/mmc/tegra2_mmc.h
index 4b80f9f..28698e0 100644
--- a/drivers/mmc/tegra2_mmc.h
+++ b/drivers/mmc/tegra2_mmc.h
@@ -73,6 +73,7 @@ struct mmc_host {
unsigned int version; /* SDHCI spec. version */
unsigned int clock; /* Current clock (MHz) */
unsigned int base; /* Base address, SDMMC1/2/3/4 */
+ enum periph_id mmc_id; /* Peripheral ID: PERIPH_ID_... */
};
int tegra2_mmc_init(int dev_index, int bus_width);
--
1.7.3.1
^ permalink raw reply related [flat|nested] 14+ messages in thread* [U-Boot] [PATCH v2 4/6] tegra2: Rename PIN_ to PINGRP_
2011-09-21 22:40 [U-Boot] [PATCH v2 0/6] tegra2: Add more clock/pinmux functionality Simon Glass
` (2 preceding siblings ...)
2011-09-21 22:40 ` [U-Boot] [PATCH v2 3/6] tegra2: Add more clock functions Simon Glass
@ 2011-09-21 22:40 ` Simon Glass
2011-09-21 22:40 ` [U-Boot] [PATCH v2 5/6] tegra2: Add more pinmux functions Simon Glass
` (3 subsequent siblings)
7 siblings, 0 replies; 14+ messages in thread
From: Simon Glass @ 2011-09-21 22:40 UTC (permalink / raw)
To: u-boot
The pin groupings are better named PINGRP, since on Tegra2 they refer to
multiple pins.
Sorry about this, but better to get it right now when there is only a small
amount of code affected.
Signed-off-by: Simon Glass <sjg@chromium.org>
Tested-by: Tom Warren <twarren@nvidia.com>
---
arch/arm/cpu/armv7/tegra2/pinmux.c | 6 +-
arch/arm/include/asm/arch-tegra2/pinmux.h | 272 ++++++++++++++--------------
board/nvidia/common/board.c | 18 +-
3 files changed, 148 insertions(+), 148 deletions(-)
diff --git a/arch/arm/cpu/armv7/tegra2/pinmux.c b/arch/arm/cpu/armv7/tegra2/pinmux.c
index 5594ab8..01a3d84 100644
--- a/arch/arm/cpu/armv7/tegra2/pinmux.c
+++ b/arch/arm/cpu/armv7/tegra2/pinmux.c
@@ -27,7 +27,7 @@
#include <common.h>
-void pinmux_set_tristate(enum pmux_pin pin, int enable)
+void pinmux_set_tristate(enum pmux_pingrp pin, int enable)
{
struct pmux_tri_ctlr *pmt = (struct pmux_tri_ctlr *)NV_PA_APB_MISC_BASE;
u32 *tri = &pmt->pmt_tri[TRISTATE_REG(pin)];
@@ -41,12 +41,12 @@ void pinmux_set_tristate(enum pmux_pin pin, int enable)
writel(reg, tri);
}
-void pinmux_tristate_enable(enum pmux_pin pin)
+void pinmux_tristate_enable(enum pmux_pingrp pin)
{
pinmux_set_tristate(pin, 1);
}
-void pinmux_tristate_disable(enum pmux_pin pin)
+void pinmux_tristate_disable(enum pmux_pingrp pin)
{
pinmux_set_tristate(pin, 0);
}
diff --git a/arch/arm/include/asm/arch-tegra2/pinmux.h b/arch/arm/include/asm/arch-tegra2/pinmux.h
index b8a4753..e8ef632 100644
--- a/arch/arm/include/asm/arch-tegra2/pinmux.h
+++ b/arch/arm/include/asm/arch-tegra2/pinmux.h
@@ -24,137 +24,137 @@
#ifndef _PINMUX_H_
#define _PINMUX_H_
-/* Pins which we can set to tristate or normal */
-enum pmux_pin {
+/* Pin groups which we can set to tristate or normal */
+enum pmux_pingrp {
/* APB_MISC_PP_TRISTATE_REG_A_0 */
- PIN_ATA,
- PIN_ATB,
- PIN_ATC,
- PIN_ATD,
- PIN_CDEV1,
- PIN_CDEV2,
- PIN_CSUS,
- PIN_DAP1,
-
- PIN_DAP2,
- PIN_DAP3,
- PIN_DAP4,
- PIN_DTA,
- PIN_DTB,
- PIN_DTC,
- PIN_DTD,
- PIN_DTE,
-
- PIN_GPU,
- PIN_GPV,
- PIN_I2CP,
- PIN_IRTX,
- PIN_IRRX,
- PIN_KBCB,
- PIN_KBCA,
- PIN_PMC,
-
- PIN_PTA,
- PIN_RM,
- PIN_KBCE,
- PIN_KBCF,
- PIN_GMA,
- PIN_GMC,
- PIN_SDMMC1,
- PIN_OWC,
+ PINGRP_ATA,
+ PINGRP_ATB,
+ PINGRP_ATC,
+ PINGRP_ATD,
+ PINGRP_CDEV1,
+ PINGRP_CDEV2,
+ PINGRP_CSUS,
+ PINGRP_DAP1,
+
+ PINGRP_DAP2,
+ PINGRP_DAP3,
+ PINGRP_DAP4,
+ PINGRP_DTA,
+ PINGRP_DTB,
+ PINGRP_DTC,
+ PINGRP_DTD,
+ PINGRP_DTE,
+
+ PINGRP_GPU,
+ PINGRP_GPV,
+ PINGRP_I2CP,
+ PINGRP_IRTX,
+ PINGRP_IRRX,
+ PINGRP_KBCB,
+ PINGRP_KBCA,
+ PINGRP_PMC,
+
+ PINGRP_PTA,
+ PINGRP_RM,
+ PINGRP_KBCE,
+ PINGRP_KBCF,
+ PINGRP_GMA,
+ PINGRP_GMC,
+ PINGRP_SDMMC1,
+ PINGRP_OWC,
/* 32: APB_MISC_PP_TRISTATE_REG_B_0 */
- PIN_GME,
- PIN_SDC,
- PIN_SDD,
- PIN_RESERVED0,
- PIN_SLXA,
- PIN_SLXC,
- PIN_SLXD,
- PIN_SLXK,
-
- PIN_SPDI,
- PIN_SPDO,
- PIN_SPIA,
- PIN_SPIB,
- PIN_SPIC,
- PIN_SPID,
- PIN_SPIE,
- PIN_SPIF,
-
- PIN_SPIG,
- PIN_SPIH,
- PIN_UAA,
- PIN_UAB,
- PIN_UAC,
- PIN_UAD,
- PIN_UCA,
- PIN_UCB,
-
- PIN_RESERVED1,
- PIN_ATE,
- PIN_KBCC,
- PIN_RESERVED2,
- PIN_RESERVED3,
- PIN_GMB,
- PIN_GMD,
- PIN_DDC,
+ PINGRP_GME,
+ PINGRP_SDC,
+ PINGRP_SDD,
+ PINGRP_RESERVED0,
+ PINGRP_SLXA,
+ PINGRP_SLXC,
+ PINGRP_SLXD,
+ PINGRP_SLXK,
+
+ PINGRP_SPDI,
+ PINGRP_SPDO,
+ PINGRP_SPIA,
+ PINGRP_SPIB,
+ PINGRP_SPIC,
+ PINGRP_SPID,
+ PINGRP_SPIE,
+ PINGRP_SPIF,
+
+ PINGRP_SPIG,
+ PINGRP_SPIH,
+ PINGRP_UAA,
+ PINGRP_UAB,
+ PINGRP_UAC,
+ PINGRP_UAD,
+ PINGRP_UCA,
+ PINGRP_UCB,
+
+ PINGRP_RESERVED1,
+ PINGRP_ATE,
+ PINGRP_KBCC,
+ PINGRP_RESERVED2,
+ PINGRP_RESERVED3,
+ PINGRP_GMB,
+ PINGRP_GMD,
+ PINGRP_DDC,
/* 64: APB_MISC_PP_TRISTATE_REG_C_0 */
- PIN_LD0,
- PIN_LD1,
- PIN_LD2,
- PIN_LD3,
- PIN_LD4,
- PIN_LD5,
- PIN_LD6,
- PIN_LD7,
-
- PIN_LD8,
- PIN_LD9,
- PIN_LD10,
- PIN_LD11,
- PIN_LD12,
- PIN_LD13,
- PIN_LD14,
- PIN_LD15,
-
- PIN_LD16,
- PIN_LD17,
- PIN_LHP0,
- PIN_LHP1,
- PIN_LHP2,
- PIN_LVP0,
- PIN_LVP1,
- PIN_HDINT,
-
- PIN_LM0,
- PIN_LM1,
- PIN_LVS,
- PIN_LSC0,
- PIN_LSC1,
- PIN_LSCK,
- PIN_LDC,
- PIN_LCSN,
+ PINGRP_LD0,
+ PINGRP_LD1,
+ PINGRP_LD2,
+ PINGRP_LD3,
+ PINGRP_LD4,
+ PINGRP_LD5,
+ PINGRP_LD6,
+ PINGRP_LD7,
+
+ PINGRP_LD8,
+ PINGRP_LD9,
+ PINGRP_LD10,
+ PINGRP_LD11,
+ PINGRP_LD12,
+ PINGRP_LD13,
+ PINGRP_LD14,
+ PINGRP_LD15,
+
+ PINGRP_LD16,
+ PINGRP_LD17,
+ PINGRP_LHP0,
+ PINGRP_LHP1,
+ PINGRP_LHP2,
+ PINGRP_LVP0,
+ PINGRP_LVP1,
+ PINGRP_HDINT,
+
+ PINGRP_LM0,
+ PINGRP_LM1,
+ PINGRP_LVS,
+ PINGRP_LSC0,
+ PINGRP_LSC1,
+ PINGRP_LSCK,
+ PINGRP_LDC,
+ PINGRP_LCSN,
/* 96: APB_MISC_PP_TRISTATE_REG_D_0 */
- PIN_LSPI,
- PIN_LSDA,
- PIN_LSDI,
- PIN_LPW0,
- PIN_LPW1,
- PIN_LPW2,
- PIN_LDI,
- PIN_LHS,
-
- PIN_LPP,
- PIN_RESERVED4,
- PIN_KBCD,
- PIN_GPU7,
- PIN_DTF,
- PIN_UDA,
- PIN_CRTP,
- PIN_SDB,
+ PINGRP_LSPI,
+ PINGRP_LSDA,
+ PINGRP_LSDI,
+ PINGRP_LPW0,
+ PINGRP_LPW1,
+ PINGRP_LPW2,
+ PINGRP_LDI,
+ PINGRP_LHS,
+
+ PINGRP_LPP,
+ PINGRP_RESERVED4,
+ PINGRP_KBCD,
+ PINGRP_GPU7,
+ PINGRP_DTF,
+ PINGRP_UDA,
+ PINGRP_CRTP,
+ PINGRP_SDB,
};
@@ -172,25 +172,25 @@ struct pmux_tri_ctlr {
uint pmt_reserved[22]; /* ABP_MISC_PP_ reserved offs 28-7C */
- uint pmt_ctl_a; /* _PIN_MUX_CTL_A_0, offset 80 */
- uint pmt_ctl_b; /* _PIN_MUX_CTL_B_0, offset 84 */
- uint pmt_ctl_c; /* _PIN_MUX_CTL_C_0, offset 88 */
- uint pmt_ctl_d; /* _PIN_MUX_CTL_D_0, offset 8C */
- uint pmt_ctl_e; /* _PIN_MUX_CTL_E_0, offset 90 */
- uint pmt_ctl_f; /* _PIN_MUX_CTL_F_0, offset 94 */
- uint pmt_ctl_g; /* _PIN_MUX_CTL_G_0, offset 98 */
+ uint pmt_ctl_a; /* _PINGRP_MUX_CTL_A_0, offset 80 */
+ uint pmt_ctl_b; /* _PINGRP_MUX_CTL_B_0, offset 84 */
+ uint pmt_ctl_c; /* _PINGRP_MUX_CTL_C_0, offset 88 */
+ uint pmt_ctl_d; /* _PINGRP_MUX_CTL_D_0, offset 8C */
+ uint pmt_ctl_e; /* _PINGRP_MUX_CTL_E_0, offset 90 */
+ uint pmt_ctl_f; /* _PINGRP_MUX_CTL_F_0, offset 94 */
+ uint pmt_ctl_g; /* _PINGRP_MUX_CTL_G_0, offset 98 */
};
-/* Converts a pin number to a tristate register: 0=A, 1=B, 2=C, 3=D */
+/* Converts a pin group to a tristate register: 0=A, 1=B, 2=C, 3=D */
#define TRISTATE_REG(id) ((id) >> 5)
/* Mask value for a tristate (within TRISTATE_REG(id)) */
#define TRISTATE_MASK(id) (1 << ((id) & 0x1f))
-/* Set a pin to tristate */
-void pinmux_tristate_enable(enum pmux_pin pin);
+/* Set a pin group to tristate */
+void pinmux_tristate_enable(enum pmux_pingrp pin);
-/* Set a pin to normal (non tristate) */
-void pinmux_tristate_disable(enum pmux_pin pin);
+/* Set a pin group to normal (non tristate) */
+void pinmux_tristate_disable(enum pmux_pingrp pin);
#endif /* PINMUX_H */
diff --git a/board/nvidia/common/board.c b/board/nvidia/common/board.c
index 9672c5e..35ff2ef 100644
--- a/board/nvidia/common/board.c
+++ b/board/nvidia/common/board.c
@@ -94,15 +94,15 @@ static void pin_mux_uart(void)
reg &= 0xFFF0FFFF; /* IRRX_/IRTX_SEL [19:16] = 00 UARTA */
writel(reg, &pmt->pmt_ctl_c);
- pinmux_tristate_disable(PIN_IRRX);
- pinmux_tristate_disable(PIN_IRTX);
+ pinmux_tristate_disable(PINGRP_IRRX);
+ pinmux_tristate_disable(PINGRP_IRTX);
#endif /* CONFIG_TEGRA2_ENABLE_UARTA */
#if defined(CONFIG_TEGRA2_ENABLE_UARTD)
reg = readl(&pmt->pmt_ctl_b);
reg &= 0xFFFFFFF3; /* GMC_SEL [3:2] = 00, UARTD */
writel(reg, &pmt->pmt_ctl_b);
- pinmux_tristate_disable(PIN_GMC);
+ pinmux_tristate_disable(PINGRP_GMC);
#endif /* CONFIG_TEGRA2_ENABLE_UARTD */
}
@@ -138,9 +138,9 @@ static void pin_mux_mmc(void)
reg |= (3 << 0); /* GME_SEL [1:0] = 11 SDIO4 */
writel(reg, &pmt->pmt_ctl_d);
- pinmux_tristate_disable(PIN_ATB);
- pinmux_tristate_disable(PIN_GMA);
- pinmux_tristate_disable(PIN_GME);
+ pinmux_tristate_disable(PINGRP_ATB);
+ pinmux_tristate_disable(PINGRP_GMA);
+ pinmux_tristate_disable(PINGRP_GME);
/* SDMMC3 */
/* SDIO3_CLK, SDIO3_CMD, SDIO3_DAT[3:0] */
@@ -151,9 +151,9 @@ static void pin_mux_mmc(void)
reg |= (2 << 14); /* SDD_SEL [15:14] = 01 SDIO3 */
writel(reg, &pmt->pmt_ctl_d);
- pinmux_tristate_disable(PIN_SDC);
- pinmux_tristate_disable(PIN_SDD);
- pinmux_tristate_disable(PIN_SDB);
+ pinmux_tristate_disable(PINGRP_SDC);
+ pinmux_tristate_disable(PINGRP_SDD);
+ pinmux_tristate_disable(PINGRP_SDB);
}
#endif
--
1.7.3.1
^ permalink raw reply related [flat|nested] 14+ messages in thread* [U-Boot] [PATCH v2 5/6] tegra2: Add more pinmux functions
2011-09-21 22:40 [U-Boot] [PATCH v2 0/6] tegra2: Add more clock/pinmux functionality Simon Glass
` (3 preceding siblings ...)
2011-09-21 22:40 ` [U-Boot] [PATCH v2 4/6] tegra2: Rename PIN_ to PINGRP_ Simon Glass
@ 2011-09-21 22:40 ` Simon Glass
2011-09-21 22:40 ` [U-Boot] [PATCH v2 6/6] tegra2: Enable MMC for Seaboard Simon Glass
` (2 subsequent siblings)
7 siblings, 0 replies; 14+ messages in thread
From: Simon Glass @ 2011-09-21 22:40 UTC (permalink / raw)
To: u-boot
This adds support for changing pinmux functions of pin groups. This is done
by defining a PMUX_FUNC_... enum which can be used to select the function for
each group using pinmux_set_func(). It is also possible to enable
pullup/pulldown, and the existing tristate functionality is retained.
Also provided is a means of configuring a list of pingroups by providing a
configuration table to pinmux_config_table().
Signed-off-by: Simon Glass <sjg@chromium.org>
Tested-by: Tom Warren <twarren@nvidia.com>
---
Changes in v2:
- Remove assert() which is now in common.h
arch/arm/cpu/armv7/tegra2/pinmux.c | 522 ++++++++++++++++++++++++++++-
arch/arm/include/asm/arch-tegra2/pinmux.h | 192 ++++++++++-
board/nvidia/common/board.c | 42 +--
3 files changed, 707 insertions(+), 49 deletions(-)
diff --git a/arch/arm/cpu/armv7/tegra2/pinmux.c b/arch/arm/cpu/armv7/tegra2/pinmux.c
index 01a3d84..b053f90 100644
--- a/arch/arm/cpu/armv7/tegra2/pinmux.c
+++ b/arch/arm/cpu/armv7/tegra2/pinmux.c
@@ -27,9 +27,463 @@
#include <common.h>
+/*
+ * This defines the order of the pin mux control bits in the registers. For
+ * some reason there is no correspendence between the tristate, pin mux and
+ * pullup/pulldown registers.
+ */
+enum pmux_ctlid {
+ /* 0: APB_MISC_PP_PIN_MUX_CTL_A_0 */
+ MUXCTL_UAA,
+ MUXCTL_UAB,
+ MUXCTL_UAC,
+ MUXCTL_UAD,
+ MUXCTL_UDA,
+ MUXCTL_RESERVED5,
+ MUXCTL_ATE,
+ MUXCTL_RM,
+
+ MUXCTL_ATB,
+ MUXCTL_RESERVED9,
+ MUXCTL_ATD,
+ MUXCTL_ATC,
+ MUXCTL_ATA,
+ MUXCTL_KBCF,
+ MUXCTL_KBCE,
+ MUXCTL_SDMMC1,
+
+ /* 16: APB_MISC_PP_PIN_MUX_CTL_B_0 */
+ MUXCTL_GMA,
+ MUXCTL_GMC,
+ MUXCTL_HDINT,
+ MUXCTL_SLXA,
+ MUXCTL_OWC,
+ MUXCTL_SLXC,
+ MUXCTL_SLXD,
+ MUXCTL_SLXK,
+
+ MUXCTL_UCA,
+ MUXCTL_UCB,
+ MUXCTL_DTA,
+ MUXCTL_DTB,
+ MUXCTL_RESERVED28,
+ MUXCTL_DTC,
+ MUXCTL_DTD,
+ MUXCTL_DTE,
+
+ /* 32: APB_MISC_PP_PIN_MUX_CTL_C_0 */
+ MUXCTL_DDC,
+ MUXCTL_CDEV1,
+ MUXCTL_CDEV2,
+ MUXCTL_CSUS,
+ MUXCTL_I2CP,
+ MUXCTL_KBCA,
+ MUXCTL_KBCB,
+ MUXCTL_KBCC,
+
+ MUXCTL_IRTX,
+ MUXCTL_IRRX,
+ MUXCTL_DAP1,
+ MUXCTL_DAP2,
+ MUXCTL_DAP3,
+ MUXCTL_DAP4,
+ MUXCTL_GMB,
+ MUXCTL_GMD,
+
+ /* 48: APB_MISC_PP_PIN_MUX_CTL_D_0 */
+ MUXCTL_GME,
+ MUXCTL_GPV,
+ MUXCTL_GPU,
+ MUXCTL_SPDO,
+ MUXCTL_SPDI,
+ MUXCTL_SDB,
+ MUXCTL_SDC,
+ MUXCTL_SDD,
+
+ MUXCTL_SPIH,
+ MUXCTL_SPIG,
+ MUXCTL_SPIF,
+ MUXCTL_SPIE,
+ MUXCTL_SPID,
+ MUXCTL_SPIC,
+ MUXCTL_SPIB,
+ MUXCTL_SPIA,
+
+ /* 64: APB_MISC_PP_PIN_MUX_CTL_E_0 */
+ MUXCTL_LPW0,
+ MUXCTL_LPW1,
+ MUXCTL_LPW2,
+ MUXCTL_LSDI,
+ MUXCTL_LSDA,
+ MUXCTL_LSPI,
+ MUXCTL_LCSN,
+ MUXCTL_LDC,
+
+ MUXCTL_LSCK,
+ MUXCTL_LSC0,
+ MUXCTL_LSC1,
+ MUXCTL_LHS,
+ MUXCTL_LVS,
+ MUXCTL_LM0,
+ MUXCTL_LM1,
+ MUXCTL_LVP0,
+
+ /* 80: APB_MISC_PP_PIN_MUX_CTL_F_0 */
+ MUXCTL_LD0,
+ MUXCTL_LD1,
+ MUXCTL_LD2,
+ MUXCTL_LD3,
+ MUXCTL_LD4,
+ MUXCTL_LD5,
+ MUXCTL_LD6,
+ MUXCTL_LD7,
+
+ MUXCTL_LD8,
+ MUXCTL_LD9,
+ MUXCTL_LD10,
+ MUXCTL_LD11,
+ MUXCTL_LD12,
+ MUXCTL_LD13,
+ MUXCTL_LD14,
+ MUXCTL_LD15,
+
+ /* 96: APB_MISC_PP_PIN_MUX_CTL_G_0 */
+ MUXCTL_LD16,
+ MUXCTL_LD17,
+ MUXCTL_LHP1,
+ MUXCTL_LHP2,
+ MUXCTL_LVP1,
+ MUXCTL_LHP0,
+ MUXCTL_RESERVED102,
+ MUXCTL_LPP,
+
+ MUXCTL_LDI,
+ MUXCTL_PMC,
+ MUXCTL_CRTP,
+ MUXCTL_PTA,
+ MUXCTL_RESERVED108,
+ MUXCTL_KBCD,
+ MUXCTL_GPU7,
+ MUXCTL_DTF,
+
+ MUXCTL_NONE = -1,
+};
+
+/*
+ * And this defines the order of the pullup/pulldown controls which are again
+ * in a different order
+ */
+enum pmux_pullid {
+ /* 0: APB_MISC_PP_PULLUPDOWN_REG_A_0 */
+ PUCTL_ATA,
+ PUCTL_ATB,
+ PUCTL_ATC,
+ PUCTL_ATD,
+ PUCTL_ATE,
+ PUCTL_DAP1,
+ PUCTL_DAP2,
+ PUCTL_DAP3,
+
+ PUCTL_DAP4,
+ PUCTL_DTA,
+ PUCTL_DTB,
+ PUCTL_DTC,
+ PUCTL_DTD,
+ PUCTL_DTE,
+ PUCTL_DTF,
+ PUCTL_GPV,
+
+ /* 16: APB_MISC_PP_PULLUPDOWN_REG_B_0 */
+ PUCTL_RM,
+ PUCTL_I2CP,
+ PUCTL_PTA,
+ PUCTL_GPU7,
+ PUCTL_KBCA,
+ PUCTL_KBCB,
+ PUCTL_KBCC,
+ PUCTL_KBCD,
+
+ PUCTL_SPDI,
+ PUCTL_SPDO,
+ PUCTL_GPSLXAU,
+ PUCTL_CRTP,
+ PUCTL_SLXC,
+ PUCTL_SLXD,
+ PUCTL_SLXK,
+
+ /* 32: APB_MISC_PP_PULLUPDOWN_REG_C_0 */
+ PUCTL_CDEV1,
+ PUCTL_CDEV2,
+ PUCTL_SPIA,
+ PUCTL_SPIB,
+ PUCTL_SPIC,
+ PUCTL_SPID,
+ PUCTL_SPIE,
+ PUCTL_SPIF,
+
+ PUCTL_SPIG,
+ PUCTL_SPIH,
+ PUCTL_IRTX,
+ PUCTL_IRRX,
+ PUCTL_GME,
+ PUCTL_RESERVED45,
+ PUCTL_XM2D,
+ PUCTL_XM2C,
+
+ /* 48: APB_MISC_PP_PULLUPDOWN_REG_D_0 */
+ PUCTL_UAA,
+ PUCTL_UAB,
+ PUCTL_UAC,
+ PUCTL_UAD,
+ PUCTL_UCA,
+ PUCTL_UCB,
+ PUCTL_LD17,
+ PUCTL_LD19_18,
+
+ PUCTL_LD21_20,
+ PUCTL_LD23_22,
+ PUCTL_LS,
+ PUCTL_LC,
+ PUCTL_CSUS,
+ PUCTL_DDRC,
+ PUCTL_SDC,
+ PUCTL_SDD,
+
+ /* 64: APB_MISC_PP_PULLUPDOWN_REG_E_0 */
+ PUCTL_KBCF,
+ PUCTL_KBCE,
+ PUCTL_PMCA,
+ PUCTL_PMCB,
+ PUCTL_PMCC,
+ PUCTL_PMCD,
+ PUCTL_PMCE,
+ PUCTL_CK32,
+
+ PUCTL_UDA,
+ PUCTL_SDMMC1,
+ PUCTL_GMA,
+ PUCTL_GMB,
+ PUCTL_GMC,
+ PUCTL_GMD,
+ PUCTL_DDC,
+ PUCTL_OWC,
+
+ PUCTL_NONE = -1
+};
+
+struct tegra_pingroup_desc {
+ const char *name;
+ enum pmux_func funcs[4];
+ enum pmux_func func_safe;
+ enum pmux_vddio vddio;
+ enum pmux_ctlid ctl_id;
+ enum pmux_pullid pull_id;
+};
+
+
+/* Converts a pmux_pingrp number to a tristate register: 0=A, 1=B, 2=C, 3=D */
+#define TRISTATE_REG(pmux_pingrp) ((pmux_pingrp) >> 5)
+
+/* Mask value for a tristate (within TRISTATE_REG(id)) */
+#define TRISTATE_MASK(pmux_pingrp) (1 << ((pmux_pingrp) & 0x1f))
+
+/* Converts a PUCTL id to a pull register: 0=A, 1=B...4=E */
+#define PULL_REG(pmux_pullid) ((pmux_pullid) >> 4)
+
+/* Converts a PUCTL id to a shift position */
+#define PULL_SHIFT(pmux_pullid) ((pmux_pullid << 1) & 0x1f)
+
+/* Converts a MUXCTL id to a ctl register: 0=A, 1=B...6=G */
+#define MUXCTL_REG(pmux_ctlid) ((pmux_ctlid) >> 4)
+
+/* Converts a MUXCTL id to a shift position */
+#define MUXCTL_SHIFT(pmux_ctlid) ((pmux_ctlid << 1) & 0x1f)
+
+/* Convenient macro for defining pin group properties */
+#define PINALL(pg_name, vdd, f0, f1, f2, f3, f_safe, mux, pupd) \
+ { \
+ .vddio = PMUX_VDDIO_ ## vdd, \
+ .funcs = { \
+ PMUX_FUNC_ ## f0, \
+ PMUX_FUNC_ ## f1, \
+ PMUX_FUNC_ ## f2, \
+ PMUX_FUNC_ ## f3, \
+ }, \
+ .func_safe = PMUX_FUNC_ ## f_safe, \
+ .ctl_id = mux, \
+ .pull_id = pupd \
+ }
+
+/* A normal pin group where the mux name and pull-up name match */
+#define PIN(pg_name, vdd, f0, f1, f2, f3, f_safe) \
+ PINALL(pg_name, vdd, f0, f1, f2, f3, f_safe, \
+ MUXCTL_ ## pg_name, PUCTL_ ## pg_name)
+
+/* A pin group where the pull-up name doesn't have a 1-1 mapping */
+#define PINP(pg_name, vdd, f0, f1, f2, f3, f_safe, pupd) \
+ PINALL(pg_name, vdd, f0, f1, f2, f3, f_safe, \
+ MUXCTL_ ## pg_name, PUCTL_ ## pupd)
+
+/* A pin group number which is not used */
+#define PIN_RESERVED \
+ PIN(NONE, NONE, NONE, NONE, NONE, NONE, NONE)
+
+const struct tegra_pingroup_desc tegra_soc_pingroups[PINGRP_COUNT] = {
+ PIN(ATA, NAND, IDE, NAND, GMI, RSVD, IDE),
+ PIN(ATB, NAND, IDE, NAND, GMI, SDIO4, IDE),
+ PIN(ATC, NAND, IDE, NAND, GMI, SDIO4, IDE),
+ PIN(ATD, NAND, IDE, NAND, GMI, SDIO4, IDE),
+ PIN(CDEV1, AUDIO, OSC, PLLA_OUT, PLLM_OUT1, AUDIO_SYNC, OSC),
+ PIN(CDEV2, AUDIO, OSC, AHB_CLK, APB_CLK, PLLP_OUT4, OSC),
+ PIN(CSUS, VI, PLLC_OUT1, PLLP_OUT2, PLLP_OUT3, VI_SENSOR_CLK,
+ PLLC_OUT1),
+ PIN(DAP1, AUDIO, DAP1, RSVD, GMI, SDIO2, DAP1),
+
+ PIN(DAP2, AUDIO, DAP2, TWC, RSVD, GMI, DAP2),
+ PIN(DAP3, BB, DAP3, RSVD, RSVD, RSVD, DAP3),
+ PIN(DAP4, UART, DAP4, RSVD, GMI, RSVD, DAP4),
+ PIN(DTA, VI, RSVD, SDIO2, VI, RSVD, RSVD4),
+ PIN(DTB, VI, RSVD, RSVD, VI, SPI1, RSVD1),
+ PIN(DTC, VI, RSVD, RSVD, VI, RSVD, RSVD1),
+ PIN(DTD, VI, RSVD, SDIO2, VI, RSVD, RSVD1),
+ PIN(DTE, VI, RSVD, RSVD, VI, SPI1, RSVD1),
+
+ PINP(GPU, UART, PWM, UARTA, GMI, RSVD, RSVD4,
+ GPSLXAU),
+ PIN(GPV, SD, PCIE, RSVD, RSVD, RSVD, PCIE),
+ PIN(I2CP, SYS, I2C, RSVD, RSVD, RSVD, RSVD4),
+ PIN(IRTX, UART, UARTA, UARTB, GMI, SPI4, UARTB),
+ PIN(IRRX, UART, UARTA, UARTB, GMI, SPI4, UARTB),
+ PIN(KBCB, SYS, KBC, NAND, SDIO2, MIO, KBC),
+ PIN(KBCA, SYS, KBC, NAND, SDIO2, EMC_TEST0_DLL, KBC),
+ PINP(PMC, SYS, PWR_ON, PWR_INTR, RSVD, RSVD, PWR_ON, NONE),
+
+ PIN(PTA, NAND, I2C2, HDMI, GMI, RSVD, RSVD4),
+ PIN(RM, UART, I2C, RSVD, RSVD, RSVD, RSVD4),
+ PIN(KBCE, SYS, KBC, NAND, OWR, RSVD, KBC),
+ PIN(KBCF, SYS, KBC, NAND, TRACE, MIO, KBC),
+ PIN(GMA, NAND, UARTE, SPI3, GMI, SDIO4, SPI3),
+ PIN(GMC, NAND, UARTD, SPI4, GMI, SFLASH, SPI4),
+ PIN(SDMMC1, BB, SDIO1, RSVD, UARTE, UARTA, RSVD2),
+ PIN(OWC, SYS, OWR, RSVD, RSVD, RSVD, OWR),
+
+ PIN(GME, NAND, RSVD, DAP5, GMI, SDIO4, GMI),
+ PIN(SDC, SD, PWM, TWC, SDIO3, SPI3, TWC),
+ PIN(SDD, SD, UARTA, PWM, SDIO3, SPI3, PWM),
+ PIN_RESERVED,
+ PINP(SLXA, SD, PCIE, SPI4, SDIO3, SPI2, PCIE, CRTP),
+ PIN(SLXC, SD, SPDIF, SPI4, SDIO3, SPI2, SPI4),
+ PIN(SLXD, SD, SPDIF, SPI4, SDIO3, SPI2, SPI4),
+ PIN(SLXK, SD, PCIE, SPI4, SDIO3, SPI2, PCIE),
+
+ PIN(SPDI, AUDIO, SPDIF, RSVD, I2C, SDIO2, RSVD2),
+ PIN(SPDO, AUDIO, SPDIF, RSVD, I2C, SDIO2, RSVD2),
+ PIN(SPIA, AUDIO, SPI1, SPI2, SPI3, GMI, GMI),
+ PIN(SPIB, AUDIO, SPI1, SPI2, SPI3, GMI, GMI),
+ PIN(SPIC, AUDIO, SPI1, SPI2, SPI3, GMI, GMI),
+ PIN(SPID, AUDIO, SPI2, SPI1, SPI2_ALT, GMI, GMI),
+ PIN(SPIE, AUDIO, SPI2, SPI1, SPI2_ALT, GMI, GMI),
+ PIN(SPIF, AUDIO, SPI3, SPI1, SPI2, RSVD, RSVD4),
+
+ PIN(SPIG, AUDIO, SPI3, SPI2, SPI2_ALT, I2C, SPI2_ALT),
+ PIN(SPIH, AUDIO, SPI3, SPI2, SPI2_ALT, I2C, SPI2_ALT),
+ PIN(UAA, BB, SPI3, MIPI_HS, UARTA, ULPI, MIPI_HS),
+ PIN(UAB, BB, SPI2, MIPI_HS, UARTA, ULPI, MIPI_HS),
+ PIN(UAC, BB, OWR, RSVD, RSVD, RSVD, RSVD4),
+ PIN(UAD, UART, IRDA, SPDIF, UARTA, SPI4, SPDIF),
+ PIN(UCA, UART, UARTC, RSVD, GMI, RSVD, RSVD4),
+ PIN(UCB, UART, UARTC, PWM, GMI, RSVD, RSVD4),
+
+ PIN_RESERVED,
+ PIN(ATE, NAND, IDE, NAND, GMI, RSVD, IDE),
+ PIN(KBCC, SYS, KBC, NAND, TRACE, EMC_TEST1_DLL, KBC),
+ PIN_RESERVED,
+ PIN_RESERVED,
+ PIN(GMB, NAND, IDE, NAND, GMI, GMI_INT, GMI),
+ PIN(GMD, NAND, RSVD, NAND, GMI, SFLASH, GMI),
+ PIN(DDC, LCD, I2C2, RSVD, RSVD, RSVD, RSVD4),
+
+ /* 64 */
+ PINP(LD0, LCD, DISPA, DISPB, XIO, RSVD, RSVD4, LD17),
+ PINP(LD1, LCD, DISPA, DISPB, XIO, RSVD, RSVD4, LD17),
+ PINP(LD2, LCD, DISPA, DISPB, XIO, RSVD, RSVD4, LD17),
+ PINP(LD3, LCD, DISPA, DISPB, XIO, RSVD, RSVD4, LD17),
+ PINP(LD4, LCD, DISPA, DISPB, XIO, RSVD, RSVD4, LD17),
+ PINP(LD5, LCD, DISPA, DISPB, XIO, RSVD, RSVD4, LD17),
+ PINP(LD6, LCD, DISPA, DISPB, XIO, RSVD, RSVD4, LD17),
+ PINP(LD7, LCD, DISPA, DISPB, XIO, RSVD, RSVD4, LD17),
+
+ PINP(LD8, LCD, DISPA, DISPB, XIO, RSVD, RSVD4, LD17),
+ PINP(LD9, LCD, DISPA, DISPB, XIO, RSVD, RSVD4, LD17),
+ PINP(LD10, LCD, DISPA, DISPB, XIO, RSVD, RSVD4, LD17),
+ PINP(LD11, LCD, DISPA, DISPB, XIO, RSVD, RSVD4, LD17),
+ PINP(LD12, LCD, DISPA, DISPB, XIO, RSVD, RSVD4, LD17),
+ PINP(LD13, LCD, DISPA, DISPB, XIO, RSVD, RSVD4, LD17),
+ PINP(LD14, LCD, DISPA, DISPB, XIO, RSVD, RSVD4, LD17),
+ PINP(LD15, LCD, DISPA, DISPB, XIO, RSVD, RSVD4, LD17),
+
+ PINP(LD16, LCD, DISPA, DISPB, XIO, RSVD, RSVD4, LD17),
+ PINP(LD17, LCD, DISPA, DISPB, RSVD, RSVD, RSVD4, LD17),
+ PINP(LHP0, LCD, DISPA, DISPB, RSVD, RSVD, RSVD4, LD21_20),
+ PINP(LHP1, LCD, DISPA, DISPB, RSVD, RSVD, RSVD4, LD19_18),
+ PINP(LHP2, LCD, DISPA, DISPB, RSVD, RSVD, RSVD4, LD19_18),
+ PINP(LVP0, LCD, DISPA, DISPB, RSVD, RSVD, RSVD4, LC),
+ PINP(LVP1, LCD, DISPA, DISPB, RSVD, RSVD, RSVD4, LD21_20),
+ PINP(HDINT, LCD, HDMI, RSVD, RSVD, RSVD, HDMI , LC),
+
+ PINP(LM0, LCD, DISPA, DISPB, SPI3, RSVD, RSVD4, LC),
+ PINP(LM1, LCD, DISPA, DISPB, RSVD, CRT, RSVD3, LC),
+ PINP(LVS, LCD, DISPA, DISPB, XIO, RSVD, RSVD4, LC),
+ PINP(LSC0, LCD, DISPA, DISPB, XIO, RSVD, RSVD4, LC),
+ PINP(LSC1, LCD, DISPA, DISPB, SPI3, HDMI, DISPA, LS),
+ PINP(LSCK, LCD, DISPA, DISPB, SPI3, HDMI, DISPA, LS),
+ PINP(LDC, LCD, DISPA, DISPB, RSVD, RSVD, RSVD4, LS),
+ PINP(LCSN, LCD, DISPA, DISPB, SPI3, RSVD, RSVD4, LS),
+
+ /* 96 */
+ PINP(LSPI, LCD, DISPA, DISPB, XIO, HDMI, DISPA, LC),
+ PINP(LSDA, LCD, DISPA, DISPB, SPI3, HDMI, DISPA, LS),
+ PINP(LSDI, LCD, DISPA, DISPB, SPI3, RSVD, DISPA, LS),
+ PINP(LPW0, LCD, DISPA, DISPB, SPI3, HDMI, DISPA, LS),
+ PINP(LPW1, LCD, DISPA, DISPB, RSVD, RSVD, RSVD4, LS),
+ PINP(LPW2, LCD, DISPA, DISPB, SPI3, HDMI, DISPA, LS),
+ PINP(LDI, LCD, DISPA, DISPB, RSVD, RSVD, RSVD4, LD23_22),
+ PINP(LHS, LCD, DISPA, DISPB, XIO, RSVD, RSVD4, LC),
+
+ PINP(LPP, LCD, DISPA, DISPB, RSVD, RSVD, RSVD4, LD23_22),
+ PIN_RESERVED,
+ PIN(KBCD, SYS, KBC, NAND, SDIO2, MIO, KBC),
+ PIN(GPU7, SYS, RTCK, RSVD, RSVD, RSVD, RTCK),
+ PIN(DTF, VI, I2C3, RSVD, VI, RSVD, RSVD4),
+ PIN(UDA, BB, SPI1, RSVD, UARTD, ULPI, RSVD2),
+ PIN(CRTP, LCD, CRT, RSVD, RSVD, RSVD, RSVD),
+ PINP(SDB, SD, UARTA, PWM, SDIO3, SPI2, PWM, NONE),
+
+ /* these pin groups only have pullup and pull down control */
+ PINALL(CK32, SYS, RSVD, RSVD, RSVD, RSVD, RSVD, MUXCTL_NONE,
+ PUCTL_NONE),
+ PINALL(DDRC, DDR, RSVD, RSVD, RSVD, RSVD, RSVD, MUXCTL_NONE,
+ PUCTL_NONE),
+ PINALL(PMCA, SYS, RSVD, RSVD, RSVD, RSVD, RSVD, MUXCTL_NONE,
+ PUCTL_NONE),
+ PINALL(PMCB, SYS, RSVD, RSVD, RSVD, RSVD, RSVD, MUXCTL_NONE,
+ PUCTL_NONE),
+ PINALL(PMCC, SYS, RSVD, RSVD, RSVD, RSVD, RSVD, MUXCTL_NONE,
+ PUCTL_NONE),
+ PINALL(PMCD, SYS, RSVD, RSVD, RSVD, RSVD, RSVD, MUXCTL_NONE,
+ PUCTL_NONE),
+ PINALL(PMCE, SYS, RSVD, RSVD, RSVD, RSVD, RSVD, MUXCTL_NONE,
+ PUCTL_NONE),
+ PINALL(XM2C, DDR, RSVD, RSVD, RSVD, RSVD, RSVD, MUXCTL_NONE,
+ PUCTL_NONE),
+ PINALL(XM2D, DDR, RSVD, RSVD, RSVD, RSVD, RSVD, MUXCTL_NONE,
+ PUCTL_NONE),
+};
+
void pinmux_set_tristate(enum pmux_pingrp pin, int enable)
{
- struct pmux_tri_ctlr *pmt = (struct pmux_tri_ctlr *)NV_PA_APB_MISC_BASE;
+ struct pmux_tri_ctlr *pmt =
+ (struct pmux_tri_ctlr *)NV_PA_APB_MISC_BASE;
u32 *tri = &pmt->pmt_tri[TRISTATE_REG(pin)];
u32 reg;
@@ -50,3 +504,69 @@ void pinmux_tristate_disable(enum pmux_pingrp pin)
{
pinmux_set_tristate(pin, 0);
}
+
+void pinmux_set_pullupdown(enum pmux_pingrp pin, enum pmux_pull pupd)
+{
+ struct pmux_tri_ctlr *pmt =
+ (struct pmux_tri_ctlr *)NV_PA_APB_MISC_BASE;
+ enum pmux_pullid pull_id = tegra_soc_pingroups[pin].pull_id;
+ u32 *pull = &pmt->pmt_pull[PULL_REG(pull_id)];
+ u32 mask_bit;
+ u32 reg;
+ mask_bit = PULL_SHIFT(pull_id);
+
+ reg = readl(pull);
+ reg &= ~(0x3 << mask_bit);
+ reg |= pupd << mask_bit;
+ writel(reg, pull);
+}
+
+void pinmux_set_func(enum pmux_pingrp pin, enum pmux_func func)
+{
+ struct pmux_tri_ctlr *pmt =
+ (struct pmux_tri_ctlr *)NV_PA_APB_MISC_BASE;
+ enum pmux_ctlid mux_id = tegra_soc_pingroups[pin].ctl_id;
+ u32 *muxctl = &pmt->pmt_ctl[MUXCTL_REG(mux_id)];
+ u32 mask_bit;
+ int i, mux = -1;
+ u32 reg;
+
+ assert(pmux_func_isvalid(func));
+
+ /* Handle special values */
+ if (func >= PMUX_FUNC_RSVD1) {
+ mux = (func - PMUX_FUNC_RSVD1) & 0x3;
+ } else {
+ /* Search for the appropriate function */
+ for (i = 0; i < 4; i++) {
+ if (tegra_soc_pingroups[pin].funcs[i] == func) {
+ mux = i;
+ break;
+ }
+ }
+ }
+ assert(mux != -1);
+
+ mask_bit = MUXCTL_SHIFT(mux_id);
+ reg = readl(muxctl);
+ reg &= ~(0x3 << mask_bit);
+ reg |= mux << mask_bit;
+ writel(reg, muxctl);
+}
+
+void pinmux_config_pingroup(struct pingroup_config *config)
+{
+ enum pmux_pingrp pin = config->pingroup;
+
+ pinmux_set_func(pin, config->func);
+ pinmux_set_pullupdown(pin, config->pull);
+ pinmux_set_tristate(pin, config->tristate);
+}
+
+void pinmux_config_table(struct pingroup_config *config, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ pinmux_config_pingroup(&config[i]);
+}
diff --git a/arch/arm/include/asm/arch-tegra2/pinmux.h b/arch/arm/include/asm/arch-tegra2/pinmux.h
index e8ef632..469d742 100644
--- a/arch/arm/include/asm/arch-tegra2/pinmux.h
+++ b/arch/arm/include/asm/arch-tegra2/pinmux.h
@@ -24,7 +24,14 @@
#ifndef _PINMUX_H_
#define _PINMUX_H_
-/* Pin groups which we can set to tristate or normal */
+/*
+ * Pin groups which we adjust. There are three basic attributes of each pin
+ * group which use this enum:
+ *
+ * - function
+ * - pullup / pulldown
+ * - tristate or normal
+ */
enum pmux_pingrp {
/* APB_MISC_PP_TRISTATE_REG_A_0 */
PINGRP_ATA,
@@ -155,37 +162,169 @@ enum pmux_pingrp {
PINGRP_UDA,
PINGRP_CRTP,
PINGRP_SDB,
+
+ /* these pin groups only have pullup and pull down control */
+ PINGRP_FIRST_NO_MUX,
+ PINGRP_CK32 = PINGRP_FIRST_NO_MUX,
+ PINGRP_DDRC,
+ PINGRP_PMCA,
+ PINGRP_PMCB,
+ PINGRP_PMCC,
+ PINGRP_PMCD,
+ PINGRP_PMCE,
+ PINGRP_XM2C,
+ PINGRP_XM2D,
+
+ PINGRP_COUNT,
+};
+
+/*
+ * Functions which can be assigned to each of the pin groups. The values here
+ * bear no relation to the values programmed into pinmux registers and are
+ * purely a convenience. The translation is done through a table search.
+ */
+enum pmux_func {
+ PMUX_FUNC_AHB_CLK,
+ PMUX_FUNC_APB_CLK,
+ PMUX_FUNC_AUDIO_SYNC,
+ PMUX_FUNC_CRT,
+ PMUX_FUNC_DAP1,
+ PMUX_FUNC_DAP2,
+ PMUX_FUNC_DAP3,
+ PMUX_FUNC_DAP4,
+ PMUX_FUNC_DAP5,
+ PMUX_FUNC_DISPA,
+ PMUX_FUNC_DISPB,
+ PMUX_FUNC_EMC_TEST0_DLL,
+ PMUX_FUNC_EMC_TEST1_DLL,
+ PMUX_FUNC_GMI,
+ PMUX_FUNC_GMI_INT,
+ PMUX_FUNC_HDMI,
+ PMUX_FUNC_I2C,
+ PMUX_FUNC_I2C2,
+ PMUX_FUNC_I2C3,
+ PMUX_FUNC_IDE,
+ PMUX_FUNC_IRDA,
+ PMUX_FUNC_KBC,
+ PMUX_FUNC_MIO,
+ PMUX_FUNC_MIPI_HS,
+ PMUX_FUNC_NAND,
+ PMUX_FUNC_OSC,
+ PMUX_FUNC_OWR,
+ PMUX_FUNC_PCIE,
+ PMUX_FUNC_PLLA_OUT,
+ PMUX_FUNC_PLLC_OUT1,
+ PMUX_FUNC_PLLM_OUT1,
+ PMUX_FUNC_PLLP_OUT2,
+ PMUX_FUNC_PLLP_OUT3,
+ PMUX_FUNC_PLLP_OUT4,
+ PMUX_FUNC_PWM,
+ PMUX_FUNC_PWR_INTR,
+ PMUX_FUNC_PWR_ON,
+ PMUX_FUNC_RTCK,
+ PMUX_FUNC_SDIO1,
+ PMUX_FUNC_SDIO2,
+ PMUX_FUNC_SDIO3,
+ PMUX_FUNC_SDIO4,
+ PMUX_FUNC_SFLASH,
+ PMUX_FUNC_SPDIF,
+ PMUX_FUNC_SPI1,
+ PMUX_FUNC_SPI2,
+ PMUX_FUNC_SPI2_ALT,
+ PMUX_FUNC_SPI3,
+ PMUX_FUNC_SPI4,
+ PMUX_FUNC_TRACE,
+ PMUX_FUNC_TWC,
+ PMUX_FUNC_UARTA,
+ PMUX_FUNC_UARTB,
+ PMUX_FUNC_UARTC,
+ PMUX_FUNC_UARTD,
+ PMUX_FUNC_UARTE,
+ PMUX_FUNC_ULPI,
+ PMUX_FUNC_VI,
+ PMUX_FUNC_VI_SENSOR_CLK,
+ PMUX_FUNC_XIO,
+ PMUX_FUNC_SAFE,
+
+ /* These don't have a name, but can be used in the table */
+ PMUX_FUNC_RSVD1,
+ PMUX_FUNC_RSVD2,
+ PMUX_FUNC_RSVD3,
+ PMUX_FUNC_RSVD4,
+ PMUX_FUNC_RSVD, /* Not valid and should not be used */
+
+ PMUX_FUNC_COUNT,
+
+ PMUX_FUNC_NONE = -1,
};
+/* return 1 if a pmux_func is in range */
+#define pmux_func_isvalid(func) ((func) >= 0 && (func) < PMUX_FUNC_COUNT && \
+ (func) != PMUX_FUNC_RSVD)
+
+/* The pullup/pulldown state of a pin group */
+enum pmux_pull {
+ PMUX_PULL_NORMAL = 0,
+ PMUX_PULL_DOWN,
+ PMUX_PULL_UP,
+};
+
+/* Defines whether a pin group is tristated or in normal operation */
+enum pmux_tristate {
+ PMUX_TRI_NORMAL = 0,
+ PMUX_TRI_TRISTATE = 1,
+};
-#define TEGRA_TRISTATE_REGS 4
+/* Available power domains used by pin groups */
+enum pmux_vddio {
+ PMUX_VDDIO_BB = 0,
+ PMUX_VDDIO_LCD,
+ PMUX_VDDIO_VI,
+ PMUX_VDDIO_UART,
+ PMUX_VDDIO_DDR,
+ PMUX_VDDIO_NAND,
+ PMUX_VDDIO_SYS,
+ PMUX_VDDIO_AUDIO,
+ PMUX_VDDIO_SD,
+
+ PMUX_VDDIO_NONE
+};
+
+enum {
+ PMUX_TRISTATE_REGS = 4,
+ PMUX_MUX_REGS = 7,
+ PMUX_PULL_REGS = 5,
+};
/* APB MISC Pin Mux and Tristate (APB_MISC_PP_) registers */
struct pmux_tri_ctlr {
uint pmt_reserved0; /* ABP_MISC_PP_ reserved offset 00 */
uint pmt_reserved1; /* ABP_MISC_PP_ reserved offset 04 */
- uint pmt_strap_opt_a; /* _STRAPPING_OPT_A_0, offset 08 */
+ uint pmt_strap_opt_a; /* _STRAPPING_OPT_A_0, offset 08 */
uint pmt_reserved2; /* ABP_MISC_PP_ reserved offset 0C */
uint pmt_reserved3; /* ABP_MISC_PP_ reserved offset 10 */
- uint pmt_tri[TEGRA_TRISTATE_REGS]; /* _TRI_STATE_REG_A/B/C/D_0 14-20 */
- uint pmt_cfg_ctl; /* _CONFIG_CTL_0, offset 24 */
+ uint pmt_tri[PMUX_TRISTATE_REGS];/* _TRI_STATE_REG_A/B/C/D_0 14-20 */
+ uint pmt_cfg_ctl; /* _CONFIG_CTL_0, offset 24 */
uint pmt_reserved[22]; /* ABP_MISC_PP_ reserved offs 28-7C */
- uint pmt_ctl_a; /* _PINGRP_MUX_CTL_A_0, offset 80 */
- uint pmt_ctl_b; /* _PINGRP_MUX_CTL_B_0, offset 84 */
- uint pmt_ctl_c; /* _PINGRP_MUX_CTL_C_0, offset 88 */
- uint pmt_ctl_d; /* _PINGRP_MUX_CTL_D_0, offset 8C */
- uint pmt_ctl_e; /* _PINGRP_MUX_CTL_E_0, offset 90 */
- uint pmt_ctl_f; /* _PINGRP_MUX_CTL_F_0, offset 94 */
- uint pmt_ctl_g; /* _PINGRP_MUX_CTL_G_0, offset 98 */
+ uint pmt_ctl[PMUX_MUX_REGS]; /* _PIN_MUX_CTL_A-G_0, offset 80 */
+ uint pmt_reserved4; /* ABP_MISC_PP_ reserved offset 9c */
+ uint pmt_pull[PMUX_PULL_REGS]; /* APB_MISC_PP_PULLUPDOWN_REG_A-E */
};
-/* Converts a pin group to a tristate register: 0=A, 1=B, 2=C, 3=D */
-#define TRISTATE_REG(id) ((id) >> 5)
-
-/* Mask value for a tristate (within TRISTATE_REG(id)) */
-#define TRISTATE_MASK(id) (1 << ((id) & 0x1f))
+/*
+ * This defines the configuration for a pin, including the function assigned,
+ * pull up/down settings and tristate settings. Having set up one of these
+ * you can call pinmux_config_pingroup() to configure a pin in one step. Also
+ * available is pinmux_config_table() to configure a list of pins.
+ */
+struct pingroup_config {
+ enum pmux_pingrp pingroup; /* pin group PINGRP_... */
+ enum pmux_func func; /* function to assign FUNC_... */
+ enum pmux_pull pull; /* pull up/down/normal PMUX_PULL_...*/
+ enum pmux_tristate tristate; /* tristate or normal PMUX_TRI_... */
+};
/* Set a pin group to tristate */
void pinmux_tristate_enable(enum pmux_pingrp pin);
@@ -193,4 +332,23 @@ void pinmux_tristate_enable(enum pmux_pingrp pin);
/* Set a pin group to normal (non tristate) */
void pinmux_tristate_disable(enum pmux_pingrp pin);
+/* Set the pull up/down feature for a pin group */
+void pinmux_set_pullupdown(enum pmux_pingrp pin, enum pmux_pull pupd);
+
+/* Set the mux function for a pin group */
+void pinmux_set_func(enum pmux_pingrp pin, enum pmux_func func);
+
+/* Set the complete configuration for a pin group */
+void pinmux_config_pingroup(struct pingroup_config *config);
+
+void pinmux_set_tristate(enum pmux_pingrp pin, int enable);
+
+/**
+ * Configuure a list of pin groups
+ *
+ * @param config List of config items
+ * @param len Number of config items in list
+ */
+void pinmux_config_table(struct pingroup_config *config, int len);
+
#endif /* PINMUX_H */
diff --git a/board/nvidia/common/board.c b/board/nvidia/common/board.c
index 35ff2ef..5ba7bda 100644
--- a/board/nvidia/common/board.c
+++ b/board/nvidia/common/board.c
@@ -86,21 +86,15 @@ static void clock_init_uart(void)
*/
static void pin_mux_uart(void)
{
- struct pmux_tri_ctlr *pmt = (struct pmux_tri_ctlr *)NV_PA_APB_MISC_BASE;
- u32 reg;
-
#if defined(CONFIG_TEGRA2_ENABLE_UARTA)
- reg = readl(&pmt->pmt_ctl_c);
- reg &= 0xFFF0FFFF; /* IRRX_/IRTX_SEL [19:16] = 00 UARTA */
- writel(reg, &pmt->pmt_ctl_c);
+ pinmux_set_func(PINGRP_IRRX, PMUX_FUNC_UARTA);
+ pinmux_set_func(PINGRP_IRTX, PMUX_FUNC_UARTA);
pinmux_tristate_disable(PINGRP_IRRX);
pinmux_tristate_disable(PINGRP_IRTX);
#endif /* CONFIG_TEGRA2_ENABLE_UARTA */
#if defined(CONFIG_TEGRA2_ENABLE_UARTD)
- reg = readl(&pmt->pmt_ctl_b);
- reg &= 0xFFFFFFF3; /* GMC_SEL [3:2] = 00, UARTD */
- writel(reg, &pmt->pmt_ctl_b);
+ pinmux_set_func(PINGRP_GMC, PMUX_FUNC_UARTD);
pinmux_tristate_disable(PINGRP_GMC);
#endif /* CONFIG_TEGRA2_ENABLE_UARTD */
@@ -123,33 +117,19 @@ static void clock_init_mmc(void)
*/
static void pin_mux_mmc(void)
{
- struct pmux_tri_ctlr *pmt = (struct pmux_tri_ctlr *)NV_PA_APB_MISC_BASE;
- u32 reg;
-
- /* SDMMC4 */
- /* config 2, x8 on 2nd set of pins */
- reg = readl(&pmt->pmt_ctl_a);
- reg |= (3 << 16); /* ATB_SEL [17:16] = 11 SDIO4 */
- writel(reg, &pmt->pmt_ctl_a);
- reg = readl(&pmt->pmt_ctl_b);
- reg |= (3 << 0); /* GMA_SEL [1:0] = 11 SDIO4 */
- writel(reg, &pmt->pmt_ctl_b);
- reg = readl(&pmt->pmt_ctl_d);
- reg |= (3 << 0); /* GME_SEL [1:0] = 11 SDIO4 */
- writel(reg, &pmt->pmt_ctl_d);
+ /* SDMMC4: config 3, x8 on 2nd set of pins */
+ pinmux_set_func(PINGRP_ATB, PMUX_FUNC_SDIO4);
+ pinmux_set_func(PINGRP_GMA, PMUX_FUNC_SDIO4);
+ pinmux_set_func(PINGRP_GME, PMUX_FUNC_SDIO4);
pinmux_tristate_disable(PINGRP_ATB);
pinmux_tristate_disable(PINGRP_GMA);
pinmux_tristate_disable(PINGRP_GME);
- /* SDMMC3 */
- /* SDIO3_CLK, SDIO3_CMD, SDIO3_DAT[3:0] */
- reg = readl(&pmt->pmt_ctl_d);
- reg &= 0xFFFF03FF;
- reg |= (2 << 10); /* SDB_SEL [11:10] = 01 SDIO3 */
- reg |= (2 << 12); /* SDC_SEL [13:12] = 01 SDIO3 */
- reg |= (2 << 14); /* SDD_SEL [15:14] = 01 SDIO3 */
- writel(reg, &pmt->pmt_ctl_d);
+ /* SDMMC3: SDIO3_CLK, SDIO3_CMD, SDIO3_DAT[3:0] */
+ pinmux_set_func(PINGRP_SDB, PMUX_FUNC_SDIO3);
+ pinmux_set_func(PINGRP_SDC, PMUX_FUNC_SDIO3);
+ pinmux_set_func(PINGRP_SDD, PMUX_FUNC_SDIO3);
pinmux_tristate_disable(PINGRP_SDC);
pinmux_tristate_disable(PINGRP_SDD);
--
1.7.3.1
^ permalink raw reply related [flat|nested] 14+ messages in thread