diff options
Diffstat (limited to 'arch/arm/cpu/armv7/tegra2')
-rw-r--r-- | arch/arm/cpu/armv7/tegra2/ap20.c | 5 | ||||
-rw-r--r-- | arch/arm/cpu/armv7/tegra2/clock.c | 825 | ||||
-rw-r--r-- | arch/arm/cpu/armv7/tegra2/pinmux.c | 528 |
3 files changed, 1344 insertions, 14 deletions
diff --git a/arch/arm/cpu/armv7/tegra2/ap20.c b/arch/arm/cpu/armv7/tegra2/ap20.c index dc5f984..5cb4b1b 100644 --- a/arch/arm/cpu/armv7/tegra2/ap20.c +++ b/arch/arm/cpu/armv7/tegra2/ap20.c @@ -36,7 +36,7 @@ u32 s_first_boot = 1; void init_pllx(void) { struct clk_rst_ctlr *clkrst = (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE; - struct clk_pll *pll = &clkrst->crc_pll[CLOCK_PLL_ID_XCPU]; + struct clk_pll *pll = &clkrst->crc_pll[CLOCK_ID_XCPU]; u32 reg; /* If PLLX is already enabled, just return */ @@ -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 0aaed7d..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_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. @@ -66,7 +436,7 @@ unsigned long clock_start_pll(enum clock_pll_id clkid, u32 divm, u32 divn, data = (divm << PLL_DIVM_SHIFT) | (divn << PLL_DIVN_SHIFT) | (0 << PLL_BYPASS_SHIFT) | (1 << PLL_ENABLE_SHIFT); - if (clkid == CLOCK_PLL_ID_USB) + if (clkid == CLOCK_ID_USB) data |= divp << PLLU_VCO_FREQ_SHIFT; else data |= divp << PLL_DIVP_SHIFT; @@ -76,6 +446,294 @@ unsigned long clock_start_pll(enum clock_pll_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/cpu/armv7/tegra2/pinmux.c b/arch/arm/cpu/armv7/tegra2/pinmux.c index 5594ab8..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> -void pinmux_set_tristate(enum pmux_pin pin, int enable) +/* + * 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; @@ -41,12 +495,78 @@ 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); } + +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]); +} |