diff options
Diffstat (limited to 'arch/arm/cpu')
23 files changed, 1333 insertions, 159 deletions
diff --git a/arch/arm/cpu/arm1136/config.mk b/arch/arm/cpu/arm1136/config.mk index efee0d1dca..9092d914f6 100644 --- a/arch/arm/cpu/arm1136/config.mk +++ b/arch/arm/cpu/arm1136/config.mk @@ -31,3 +31,6 @@ PLATFORM_CPPFLAGS += -march=armv5 # ========================================================================= PF_RELFLAGS_SLB_AT := $(call cc-option,-mshort-load-bytes,$(call cc-option,-malignment-traps,)) PLATFORM_RELFLAGS += $(PF_RELFLAGS_SLB_AT) +ifdef CONFIG_SPL_BUILD +ALL-y += $(OBJTREE)/SPL +endif diff --git a/arch/arm/cpu/arm1136/cpu.c b/arch/arm/cpu/arm1136/cpu.c index b98e3d9fac..32a4c244c5 100644 --- a/arch/arm/cpu/arm1136/cpu.c +++ b/arch/arm/cpu/arm1136/cpu.c @@ -141,16 +141,6 @@ void flush_cache(unsigned long start, unsigned long size) flush_dcache_range(start, start + size); } -void enable_caches(void) -{ -#ifndef CONFIG_SYS_ICACHE_OFF - icache_enable(); -#endif -#ifndef CONFIG_SYS_DCACHE_OFF - dcache_enable(); -#endif -} - #else /* #ifndef CONFIG_SYS_DCACHE_OFF */ void invalidate_dcache_all(void) { @@ -172,3 +162,15 @@ void flush_cache(unsigned long start, unsigned long size) { } #endif /* #ifndef CONFIG_SYS_DCACHE_OFF */ + +#if !defined(CONFIG_SYS_ICACHE_OFF) || !defined(CONFIG_SYS_DCACHE_OFF) +void enable_caches(void) +{ +#ifndef CONFIG_SYS_ICACHE_OFF + icache_enable(); +#endif +#ifndef CONFIG_SYS_DCACHE_OFF + dcache_enable(); +#endif +} +#endif diff --git a/arch/arm/cpu/arm1136/mx35/Makefile b/arch/arm/cpu/arm1136/mx35/Makefile index 469397ca43..f4ababbe5b 100644 --- a/arch/arm/cpu/arm1136/mx35/Makefile +++ b/arch/arm/cpu/arm1136/mx35/Makefile @@ -30,6 +30,7 @@ LIB = $(obj)lib$(SOC).o COBJS += generic.o COBJS += timer.o COBJS += iomux.o +COBJS += mx35_sdram.o SRCS := $(SOBJS:.o=.S) $(COBJS:.o=.c) OBJS := $(addprefix $(obj),$(SOBJS) $(COBJS)) diff --git a/arch/arm/cpu/arm1136/mx35/generic.c b/arch/arm/cpu/arm1136/mx35/generic.c index 41e9639d9c..98aa4d15bc 100644 --- a/arch/arm/cpu/arm1136/mx35/generic.c +++ b/arch/arm/cpu/arm1136/mx35/generic.c @@ -35,6 +35,7 @@ #include <fsl_esdhc.h> #endif #include <netdev.h> +#include <spl.h> #define CLK_CODE(arm, ahb, sel) (((arm) << 16) + ((ahb) << 8) + (sel)) #define CLK_CODE_ARM(c) (((c) >> 16) & 0xFF) @@ -492,3 +493,77 @@ void reset_cpu(ulong addr) struct wdog_regs *wdog = (struct wdog_regs *)WDOG_BASE_ADDR; writew(4, &wdog->wcr); } + +#define RCSR_MEM_CTL_WEIM 0 +#define RCSR_MEM_CTL_NAND 1 +#define RCSR_MEM_CTL_ATA 2 +#define RCSR_MEM_CTL_EXPANSION 3 +#define RCSR_MEM_TYPE_NOR 0 +#define RCSR_MEM_TYPE_ONENAND 2 +#define RCSR_MEM_TYPE_SD 0 +#define RCSR_MEM_TYPE_I2C 2 +#define RCSR_MEM_TYPE_SPI 3 + +u32 spl_boot_device(void) +{ + struct ccm_regs *ccm = + (struct ccm_regs *)IMX_CCM_BASE; + + u32 rcsr = readl(&ccm->rcsr); + u32 mem_type, mem_ctl; + + /* In external mode, no boot device is returned */ + if ((rcsr >> 10) & 0x03) + return BOOT_DEVICE_NONE; + + mem_ctl = (rcsr >> 25) & 0x03; + mem_type = (rcsr >> 23) & 0x03; + + switch (mem_ctl) { + case RCSR_MEM_CTL_WEIM: + switch (mem_type) { + case RCSR_MEM_TYPE_NOR: + return BOOT_DEVICE_NOR; + case RCSR_MEM_TYPE_ONENAND: + return BOOT_DEVICE_ONE_NAND; + default: + return BOOT_DEVICE_NONE; + } + case RCSR_MEM_CTL_NAND: + return BOOT_DEVICE_NAND; + case RCSR_MEM_CTL_EXPANSION: + switch (mem_type) { + case RCSR_MEM_TYPE_SD: + return BOOT_DEVICE_MMC1; + case RCSR_MEM_TYPE_I2C: + return BOOT_DEVICE_I2C; + case RCSR_MEM_TYPE_SPI: + return BOOT_DEVICE_SPI; + default: + return BOOT_DEVICE_NONE; + } + } + + return BOOT_DEVICE_NONE; +} + +#ifdef CONFIG_SPL_BUILD +u32 spl_boot_mode(void) +{ + switch (spl_boot_device()) { + case BOOT_DEVICE_MMC1: +#ifdef CONFIG_SPL_FAT_SUPPORT + return MMCSD_MODE_FAT; +#else + return MMCSD_MODE_RAW; +#endif + break; + case BOOT_DEVICE_NAND: + return 0; + break; + default: + puts("spl: ERROR: unsupported device\n"); + hang(); + } +} +#endif diff --git a/arch/arm/cpu/arm1136/mx35/mx35_sdram.c b/arch/arm/cpu/arm1136/mx35/mx35_sdram.c new file mode 100644 index 0000000000..f7e682c8c5 --- /dev/null +++ b/arch/arm/cpu/arm1136/mx35/mx35_sdram.c @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2012, Stefano Babic <sbabic@denx.de> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <asm/io.h> +#include <asm/errno.h> +#include <asm/arch/imx-regs.h> +#include <linux/types.h> +#include <asm/arch/sys_proto.h> + +#define ESDCTL_DDR2_EMR2 0x04000000 +#define ESDCTL_DDR2_EMR3 0x06000000 +#define ESDCTL_PRECHARGE 0x00000400 +#define ESDCTL_DDR2_EN_DLL 0x02000400 +#define ESDCTL_DDR2_RESET_DLL 0x00000333 +#define ESDCTL_DDR2_MR 0x00000233 +#define ESDCTL_DDR2_OCD_DEFAULT 0x02000780 + +enum { + SMODE_NORMAL = 0, + SMODE_PRECHARGE, + SMODE_AUTO_REFRESH, + SMODE_LOAD_REG, + SMODE_MANUAL_REFRESH +}; + +#define set_mode(x, en, m) (x | (en << 31) | (m << 28)) + +static inline void dram_wait(unsigned int count) +{ + volatile unsigned int wait = count; + + while (wait--) + ; + +} + +void mx3_setup_sdram_bank(u32 start_address, u32 ddr2_config, + u32 row, u32 col, u32 dsize, u32 refresh) +{ + struct esdc_regs *esdc = (struct esdc_regs *)ESDCTL_BASE_ADDR; + u32 *cfg_reg, *ctl_reg; + u32 val; + u32 ctlval; + + switch (start_address) { + case CSD0_BASE_ADDR: + cfg_reg = &esdc->esdcfg0; + ctl_reg = &esdc->esdctl0; + break; + case CSD1_BASE_ADDR: + cfg_reg = &esdc->esdcfg1; + ctl_reg = &esdc->esdctl1; + break; + default: + return; + } + + /* The MX35 supports 11 up to 14 rows */ + if (row < 11 || row > 14 || col < 8 || col > 10) + return; + ctlval = (row - 11) << 24 | (col - 8) << 20 | (dsize << 16); + + /* Initialize MISC register for DDR2 */ + val = ESDC_MISC_RST | ESDC_MISC_MDDR_EN | ESDC_MISC_MDDR_DL_RST | + ESDC_MISC_DDR_EN | ESDC_MISC_DDR2_EN; + writel(val, &esdc->esdmisc); + val &= ~(ESDC_MISC_RST | ESDC_MISC_MDDR_DL_RST); + writel(val, &esdc->esdmisc); + + /* + * according to DDR2 specs, wait a while before + * the PRECHARGE_ALL command + */ + dram_wait(0x20000); + + /* Load DDR2 config and timing */ + writel(ddr2_config, cfg_reg); + + /* Precharge ALL */ + writel(set_mode(ctlval, 1, SMODE_PRECHARGE), + ctl_reg); + writel(0xda, start_address + ESDCTL_PRECHARGE); + + /* Load mode */ + writel(set_mode(ctlval, 1, SMODE_LOAD_REG), + ctl_reg); + writeb(0xda, start_address + ESDCTL_DDR2_EMR2); /* EMRS2 */ + writeb(0xda, start_address + ESDCTL_DDR2_EMR3); /* EMRS3 */ + writeb(0xda, start_address + ESDCTL_DDR2_EN_DLL); /* Enable DLL */ + writeb(0xda, start_address + ESDCTL_DDR2_RESET_DLL); /* Reset DLL */ + + /* Precharge ALL */ + writel(set_mode(ctlval, 1, SMODE_PRECHARGE), + ctl_reg); + writel(0xda, start_address + ESDCTL_PRECHARGE); + + /* Set mode auto refresh : at least two refresh are required */ + writel(set_mode(ctlval, 1, SMODE_AUTO_REFRESH), + ctl_reg); + writel(0xda, start_address); + writel(0xda, start_address); + + writel(set_mode(ctlval, 1, SMODE_LOAD_REG), + ctl_reg); + writeb(0xda, start_address + ESDCTL_DDR2_MR); + writeb(0xda, start_address + ESDCTL_DDR2_OCD_DEFAULT); + + /* OCD mode exit */ + writeb(0xda, start_address + ESDCTL_DDR2_EN_DLL); /* Enable DLL */ + + /* Set normal mode */ + writel(set_mode(ctlval, 1, SMODE_NORMAL) | refresh, + ctl_reg); + + dram_wait(0x20000); + + /* Do not set delay lines, only for MDDR */ +} diff --git a/arch/arm/cpu/arm1136/start.S b/arch/arm/cpu/arm1136/start.S index 3752af9ddd..5d3b4c2299 100644 --- a/arch/arm/cpu/arm1136/start.S +++ b/arch/arm/cpu/arm1136/start.S @@ -100,6 +100,10 @@ _TEXT_BASE: _bss_start_ofs: .word __bss_start - _start +.global _image_copy_end_ofs +_image_copy_end_ofs: + .word __image_copy_end - _start + .globl _bss_end_ofs _bss_end_ofs: .word __bss_end__ - _start @@ -193,7 +197,7 @@ stack_setup: moveq r9, #0 /* no relocation. relocation offset(r9) = 0 */ beq clear_bss /* skip relocation */ mov r1, r6 /* r1 <- scratch for copy_loop */ - ldr r3, _bss_start_ofs + ldr r3, _image_copy_end_ofs add r2, r0, r3 /* r2 <- source end address */ copy_loop: @@ -241,15 +245,28 @@ fixnext: add r2, r2, #8 /* each rel.dyn entry is 8 bytes */ cmp r2, r3 blo fixloop + b clear_bss + +_rel_dyn_start_ofs: + .word __rel_dyn_start - _start +_rel_dyn_end_ofs: + .word __rel_dyn_end - _start +_dynsym_start_ofs: + .word __dynsym_start - _start #endif clear_bss: -#ifndef CONFIG_SPL_BUILD +#ifdef CONFIG_SPL_BUILD + /* No relocation for SPL */ + ldr r0, =__bss_start + ldr r1, =__bss_end__ +#else ldr r0, _bss_start_ofs ldr r1, _bss_end_ofs mov r4, r6 /* reloc addr */ add r0, r0, r4 add r1, r1, r4 +#endif mov r2, #0x00000000 /* clear */ clbss_l:cmp r0, r1 /* clear loop... */ @@ -258,7 +275,6 @@ clbss_l:cmp r0, r1 /* clear loop... */ add r0, r0, #4 b clbss_l clbss_e: -#endif /* #ifndef CONFIG_SPL_BUILD */ /* * We are done. Do not return, instead branch to second part of board @@ -273,7 +289,7 @@ _nand_boot_ofs: #else jump_2_ram: ldr r0, _board_init_r_ofs - ldr r1, _TEXT_BASE + adr r1, _start add lr, r0, r1 add lr, lr, r9 /* setup parameters for board_init_r */ @@ -286,13 +302,6 @@ _board_init_r_ofs: .word board_init_r - _start #endif -_rel_dyn_start_ofs: - .word __rel_dyn_start - _start -_rel_dyn_end_ofs: - .word __rel_dyn_end - _start -_dynsym_start_ofs: - .word __dynsym_start - _start - /* ************************************************************************* * diff --git a/arch/arm/cpu/arm1136/u-boot-spl.lds b/arch/arm/cpu/arm1136/u-boot-spl.lds new file mode 100644 index 0000000000..a0462ab97a --- /dev/null +++ b/arch/arm/cpu/arm1136/u-boot-spl.lds @@ -0,0 +1,62 @@ +/* + * (C) Copyright 2002 + * Gary Jennejohn, DENX Software Engineering, <garyj@denx.de> + * + * (C) Copyright 2010 + * Texas Instruments, <www.ti.com> + * Aneesh V <aneesh@ti.com> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +MEMORY { .sram : ORIGIN = CONFIG_SPL_TEXT_BASE,\ + LENGTH = CONFIG_SPL_MAX_SIZE } +MEMORY { .sdram : ORIGIN = CONFIG_SPL_BSS_START_ADDR, \ + LENGTH = CONFIG_SPL_BSS_MAX_SIZE } + +OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") +OUTPUT_ARCH(arm) +ENTRY(_start) +SECTIONS +{ + .text : + { + __start = .; + arch/arm/cpu/arm1136/start.o (.text) + *(.text*) + } >.sram + + . = ALIGN(4); + .rodata : { *(SORT_BY_ALIGNMENT(.rodata*)) } >.sram + + . = ALIGN(4); + .data : { *(SORT_BY_ALIGNMENT(.data*)) } >.sram + . = ALIGN(4); + __image_copy_end = .; + _end = .; + + .bss : + { + . = ALIGN(4); + __bss_start = .; + *(.bss*) + . = ALIGN(4); + __bss_end__ = .; + } >.sdram +} diff --git a/arch/arm/cpu/arm1176/s3c64xx/Makefile b/arch/arm/cpu/arm1176/s3c64xx/Makefile index 0785b194c5..266a0739ce 100644 --- a/arch/arm/cpu/arm1176/s3c64xx/Makefile +++ b/arch/arm/cpu/arm1176/s3c64xx/Makefile @@ -31,7 +31,7 @@ LIB = $(obj)lib$(SOC).o SOBJS = reset.o COBJS-$(CONFIG_S3C6400) += cpu_init.o speed.o -COBJS-y += timer.o +COBJS-y += timer.o init.o OBJS := $(addprefix $(obj),$(SOBJS) $(COBJS-y)) diff --git a/arch/arm/cpu/arm1176/s3c64xx/init.c b/arch/arm/cpu/arm1176/s3c64xx/init.c new file mode 100644 index 0000000000..f113d8ed41 --- /dev/null +++ b/arch/arm/cpu/arm1176/s3c64xx/init.c @@ -0,0 +1,26 @@ +/* + * (C) Copyright 2012 Ashok Kumar Reddy Kourla + * ashokkourla2000@gmail.com + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include<common.h> + +int arch_cpu_init(void) +{ + icache_enable(); + + return 0; +} diff --git a/arch/arm/cpu/armv7/cache_v7.c b/arch/arm/cpu/armv7/cache_v7.c index 1b4e808a79..5f6d0396f3 100644 --- a/arch/arm/cpu/armv7/cache_v7.c +++ b/arch/arm/cpu/armv7/cache_v7.c @@ -297,6 +297,12 @@ void arm_init_before_mmu(void) v7_inval_tlb(); } +void mmu_page_table_flush(unsigned long start, unsigned long stop) +{ + flush_dcache_range(start, stop); + v7_inval_tlb(); +} + /* * Flush range from all levels of d-cache/unified-cache used: * Affects the range [start, start + size - 1] @@ -329,6 +335,11 @@ void arm_init_before_mmu(void) void flush_cache(unsigned long start, unsigned long size) { } + +void mmu_page_table_flush(unsigned long start, unsigned long stop) +{ +} + #endif /* #ifndef CONFIG_SYS_DCACHE_OFF */ #ifndef CONFIG_SYS_ICACHE_OFF diff --git a/arch/arm/cpu/armv7/exynos/clock.c b/arch/arm/cpu/armv7/exynos/clock.c index 21e45d2e82..c6c66814e5 100644 --- a/arch/arm/cpu/armv7/exynos/clock.c +++ b/arch/arm/cpu/armv7/exynos/clock.c @@ -25,42 +25,32 @@ #include <asm/io.h> #include <asm/arch/clock.h> #include <asm/arch/clk.h> - -/* exynos4: return pll clock frequency */ -static unsigned long exynos4_get_pll_clk(int pllreg) +#include <asm/arch/periph.h> + +/* Epll Clock division values to achive different frequency output */ +static struct set_epll_con_val exynos5_epll_div[] = { + { 192000000, 0, 48, 3, 1, 0 }, + { 180000000, 0, 45, 3, 1, 0 }, + { 73728000, 1, 73, 3, 3, 47710 }, + { 67737600, 1, 90, 4, 3, 20762 }, + { 49152000, 0, 49, 3, 3, 9961 }, + { 45158400, 0, 45, 3, 3, 10381 }, + { 180633600, 0, 45, 3, 1, 10381 } +}; + +/* exynos: return pll clock frequency */ +static int exynos_get_pll_clk(int pllreg, unsigned int r, unsigned int k) { - struct exynos4_clock *clk = - (struct exynos4_clock *)samsung_get_base_clock(); - unsigned long r, m, p, s, k = 0, mask, fout; + unsigned long m, p, s = 0, mask, fout; unsigned int freq; - - switch (pllreg) { - case APLL: - r = readl(&clk->apll_con0); - break; - case MPLL: - r = readl(&clk->mpll_con0); - break; - case EPLL: - r = readl(&clk->epll_con0); - k = readl(&clk->epll_con1); - break; - case VPLL: - r = readl(&clk->vpll_con0); - k = readl(&clk->vpll_con1); - break; - default: - printf("Unsupported PLL (%d)\n", pllreg); - return 0; - } - /* * APLL_CON: MIDV [25:16] * MPLL_CON: MIDV [25:16] * EPLL_CON: MIDV [24:16] * VPLL_CON: MIDV [24:16] + * BPLL_CON: MIDV [25:16]: Exynos5 */ - if (pllreg == APLL || pllreg == MPLL) + if (pllreg == APLL || pllreg == MPLL || pllreg == BPLL) mask = 0x3ff; else mask = 0x1ff; @@ -92,13 +82,43 @@ static unsigned long exynos4_get_pll_clk(int pllreg) return fout; } +/* exynos4: return pll clock frequency */ +static unsigned long exynos4_get_pll_clk(int pllreg) +{ + struct exynos4_clock *clk = + (struct exynos4_clock *)samsung_get_base_clock(); + unsigned long r, k = 0; + + switch (pllreg) { + case APLL: + r = readl(&clk->apll_con0); + break; + case MPLL: + r = readl(&clk->mpll_con0); + break; + case EPLL: + r = readl(&clk->epll_con0); + k = readl(&clk->epll_con1); + break; + case VPLL: + r = readl(&clk->vpll_con0); + k = readl(&clk->vpll_con1); + break; + default: + printf("Unsupported PLL (%d)\n", pllreg); + return 0; + } + + return exynos_get_pll_clk(pllreg, r, k); +} + /* exynos5: return pll clock frequency */ static unsigned long exynos5_get_pll_clk(int pllreg) { struct exynos5_clock *clk = (struct exynos5_clock *)samsung_get_base_clock(); - unsigned long r, m, p, s, k = 0, mask, fout; - unsigned int freq, pll_div2_sel, fout_sel; + unsigned long r, k = 0, fout; + unsigned int pll_div2_sel, fout_sel; switch (pllreg) { case APLL: @@ -123,41 +143,7 @@ static unsigned long exynos5_get_pll_clk(int pllreg) return 0; } - /* - * APLL_CON: MIDV [25:16] - * MPLL_CON: MIDV [25:16] - * EPLL_CON: MIDV [24:16] - * VPLL_CON: MIDV [24:16] - * BPLL_CON: MIDV [25:16] - */ - if (pllreg == APLL || pllreg == MPLL || pllreg == BPLL) - mask = 0x3ff; - else - mask = 0x1ff; - - m = (r >> 16) & mask; - - /* PDIV [13:8] */ - p = (r >> 8) & 0x3f; - /* SDIV [2:0] */ - s = r & 0x7; - - freq = CONFIG_SYS_CLK_FREQ; - - if (pllreg == EPLL) { - k = k & 0xffff; - /* FOUT = (MDIV + K / 65536) * FIN / (PDIV * 2^SDIV) */ - fout = (m + k / 65536) * (freq / (p * (1 << s))); - } else if (pllreg == VPLL) { - k = k & 0xfff; - /* FOUT = (MDIV + K / 1024) * FIN / (PDIV * 2^SDIV) */ - fout = (m + k / 1024) * (freq / (p * (1 << s))); - } else { - if (s < 1) - s = 1; - /* FOUT = MDIV * FIN / (PDIV * 2^(SDIV - 1)) */ - fout = m * (freq / (p * (1 << (s - 1)))); - } + fout = exynos_get_pll_clk(pllreg, r, k); /* According to the user manual, in EVT1 MPLL and BPLL always gives * 1.6GHz clock, so divide by 2 to get 800MHz MPLL clock.*/ @@ -732,6 +718,208 @@ static unsigned long exynos5_get_i2c_clk(void) return aclk_66; } +int exynos5_set_epll_clk(unsigned long rate) +{ + unsigned int epll_con, epll_con_k; + unsigned int i; + unsigned int lockcnt; + unsigned int start; + struct exynos5_clock *clk = + (struct exynos5_clock *)samsung_get_base_clock(); + + epll_con = readl(&clk->epll_con0); + epll_con &= ~((EPLL_CON0_LOCK_DET_EN_MASK << + EPLL_CON0_LOCK_DET_EN_SHIFT) | + EPLL_CON0_MDIV_MASK << EPLL_CON0_MDIV_SHIFT | + EPLL_CON0_PDIV_MASK << EPLL_CON0_PDIV_SHIFT | + EPLL_CON0_SDIV_MASK << EPLL_CON0_SDIV_SHIFT); + + for (i = 0; i < ARRAY_SIZE(exynos5_epll_div); i++) { + if (exynos5_epll_div[i].freq_out == rate) + break; + } + + if (i == ARRAY_SIZE(exynos5_epll_div)) + return -1; + + epll_con_k = exynos5_epll_div[i].k_dsm << 0; + epll_con |= exynos5_epll_div[i].en_lock_det << + EPLL_CON0_LOCK_DET_EN_SHIFT; + epll_con |= exynos5_epll_div[i].m_div << EPLL_CON0_MDIV_SHIFT; + epll_con |= exynos5_epll_div[i].p_div << EPLL_CON0_PDIV_SHIFT; + epll_con |= exynos5_epll_div[i].s_div << EPLL_CON0_SDIV_SHIFT; + + /* + * Required period ( in cycles) to genarate a stable clock output. + * The maximum clock time can be up to 3000 * PDIV cycles of PLLs + * frequency input (as per spec) + */ + lockcnt = 3000 * exynos5_epll_div[i].p_div; + + writel(lockcnt, &clk->epll_lock); + writel(epll_con, &clk->epll_con0); + writel(epll_con_k, &clk->epll_con1); + + start = get_timer(0); + + while (!(readl(&clk->epll_con0) & + (0x1 << EXYNOS5_EPLLCON0_LOCKED_SHIFT))) { + if (get_timer(start) > TIMEOUT_EPLL_LOCK) { + debug("%s: Timeout waiting for EPLL lock\n", __func__); + return -1; + } + } + return 0; +} + +void exynos5_set_i2s_clk_source(void) +{ + struct exynos5_clock *clk = + (struct exynos5_clock *)samsung_get_base_clock(); + + clrsetbits_le32(&clk->src_peric1, AUDIO1_SEL_MASK, + (CLK_SRC_SCLK_EPLL)); +} + +int exynos5_set_i2s_clk_prescaler(unsigned int src_frq, + unsigned int dst_frq) +{ + struct exynos5_clock *clk = + (struct exynos5_clock *)samsung_get_base_clock(); + unsigned int div; + + if ((dst_frq == 0) || (src_frq == 0)) { + debug("%s: Invalid requency input for prescaler\n", __func__); + debug("src frq = %d des frq = %d ", src_frq, dst_frq); + return -1; + } + + div = (src_frq / dst_frq); + if (div > AUDIO_1_RATIO_MASK) { + debug("%s: Frequency ratio is out of range\n", __func__); + debug("src frq = %d des frq = %d ", src_frq, dst_frq); + return -1; + } + clrsetbits_le32(&clk->div_peric4, AUDIO_1_RATIO_MASK, + (div & AUDIO_1_RATIO_MASK)); + return 0; +} + +/** + * Linearly searches for the most accurate main and fine stage clock scalars + * (divisors) for a specified target frequency and scalar bit sizes by checking + * all multiples of main_scalar_bits values. Will always return scalars up to or + * slower than target. + * + * @param main_scalar_bits Number of main scalar bits, must be > 0 and < 32 + * @param fine_scalar_bits Number of fine scalar bits, must be > 0 and < 32 + * @param input_freq Clock frequency to be scaled in Hz + * @param target_freq Desired clock frequency in Hz + * @param best_fine_scalar Pointer to store the fine stage divisor + * + * @return best_main_scalar Main scalar for desired frequency or -1 if none + * found + */ +static int clock_calc_best_scalar(unsigned int main_scaler_bits, + unsigned int fine_scalar_bits, unsigned int input_rate, + unsigned int target_rate, unsigned int *best_fine_scalar) +{ + int i; + int best_main_scalar = -1; + unsigned int best_error = target_rate; + const unsigned int cap = (1 << fine_scalar_bits) - 1; + const unsigned int loops = 1 << main_scaler_bits; + + debug("Input Rate is %u, Target is %u, Cap is %u\n", input_rate, + target_rate, cap); + + assert(best_fine_scalar != NULL); + assert(main_scaler_bits <= fine_scalar_bits); + + *best_fine_scalar = 1; + + if (input_rate == 0 || target_rate == 0) + return -1; + + if (target_rate >= input_rate) + return 1; + + for (i = 1; i <= loops; i++) { + const unsigned int effective_div = max(min(input_rate / i / + target_rate, cap), 1); + const unsigned int effective_rate = input_rate / i / + effective_div; + const int error = target_rate - effective_rate; + + debug("%d|effdiv:%u, effrate:%u, error:%d\n", i, effective_div, + effective_rate, error); + + if (error >= 0 && error <= best_error) { + best_error = error; + best_main_scalar = i; + *best_fine_scalar = effective_div; + } + } + + return best_main_scalar; +} + +static int exynos5_set_spi_clk(enum periph_id periph_id, + unsigned int rate) +{ + struct exynos5_clock *clk = + (struct exynos5_clock *)samsung_get_base_clock(); + int main; + unsigned int fine; + unsigned shift, pre_shift; + unsigned mask = 0xff; + u32 *reg; + + main = clock_calc_best_scalar(4, 8, 400000000, rate, &fine); + if (main < 0) { + debug("%s: Cannot set clock rate for periph %d", + __func__, periph_id); + return -1; + } + main = main - 1; + fine = fine - 1; + + switch (periph_id) { + case PERIPH_ID_SPI0: + reg = &clk->div_peric1; + shift = 0; + pre_shift = 8; + break; + case PERIPH_ID_SPI1: + reg = &clk->div_peric1; + shift = 16; + pre_shift = 24; + break; + case PERIPH_ID_SPI2: + reg = &clk->div_peric2; + shift = 0; + pre_shift = 8; + break; + case PERIPH_ID_SPI3: + reg = &clk->sclk_div_isp; + shift = 0; + pre_shift = 4; + break; + case PERIPH_ID_SPI4: + reg = &clk->sclk_div_isp; + shift = 12; + pre_shift = 16; + break; + default: + debug("%s: Unsupported peripheral ID %d\n", __func__, + periph_id); + return -1; + } + clrsetbits_le32(reg, mask << shift, (main & mask) << shift); + clrsetbits_le32(reg, mask << pre_shift, (fine & mask) << pre_shift); + + return 0; + static unsigned long exynos4_get_i2c_clk(void) { struct exynos4_clock *clk = @@ -820,3 +1008,34 @@ void set_mipi_clk(void) if (cpu_is_exynos4()) exynos4_set_mipi_clk(); } + +int set_spi_clk(int periph_id, unsigned int rate) +{ + if (cpu_is_exynos5()) + return exynos5_set_spi_clk(periph_id, rate); + else + return 0; +} + +int set_i2s_clk_prescaler(unsigned int src_frq, unsigned int dst_frq) +{ + + if (cpu_is_exynos5()) + return exynos5_set_i2s_clk_prescaler(src_frq, dst_frq); + else + return 0; +} + +void set_i2s_clk_source(void) +{ + if (cpu_is_exynos5()) + exynos5_set_i2s_clk_source(); +} + +int set_epll_clk(unsigned long rate) +{ + if (cpu_is_exynos5()) + return exynos5_set_epll_clk(rate); + else + return 0; +} diff --git a/arch/arm/cpu/armv7/exynos/pinmux.c b/arch/arm/cpu/armv7/exynos/pinmux.c index 44ce0726e9..b479965743 100644 --- a/arch/arm/cpu/armv7/exynos/pinmux.c +++ b/arch/arm/cpu/armv7/exynos/pinmux.c @@ -112,6 +112,7 @@ static int exynos5_mmc_config(int peripheral, int flags) s5p_gpio_set_pull(bank, i, GPIO_PULL_UP); s5p_gpio_set_drv(bank, i, GPIO_DRV_4X); } + return 0; } @@ -230,6 +231,59 @@ static void exynos5_i2c_config(int peripheral, int flags) } } +static void exynos5_i2s_config(int peripheral) +{ + int i; + struct exynos5_gpio_part1 *gpio1 = + (struct exynos5_gpio_part1 *) samsung_get_base_gpio_part1(); + + for (i = 0; i < 5; i++) + s5p_gpio_cfg_pin(&gpio1->b0, i, GPIO_FUNC(0x02)); +} + +void exynos5_spi_config(int peripheral) +{ + int cfg = 0, pin = 0, i; + struct s5p_gpio_bank *bank = NULL; + struct exynos5_gpio_part1 *gpio1 = + (struct exynos5_gpio_part1 *) samsung_get_base_gpio_part1(); + struct exynos5_gpio_part2 *gpio2 = + (struct exynos5_gpio_part2 *) samsung_get_base_gpio_part2(); + + switch (peripheral) { + case PERIPH_ID_SPI0: + bank = &gpio1->a2; + cfg = GPIO_FUNC(0x2); + pin = 0; + break; + case PERIPH_ID_SPI1: + bank = &gpio1->a2; + cfg = GPIO_FUNC(0x2); + pin = 4; + break; + case PERIPH_ID_SPI2: + bank = &gpio1->b1; + cfg = GPIO_FUNC(0x5); + pin = 1; + break; + case PERIPH_ID_SPI3: + bank = &gpio2->f1; + cfg = GPIO_FUNC(0x2); + pin = 0; + break; + case PERIPH_ID_SPI4: + for (i = 0; i < 2; i++) { + s5p_gpio_cfg_pin(&gpio2->f0, i + 2, GPIO_FUNC(0x4)); + s5p_gpio_cfg_pin(&gpio2->e0, i + 4, GPIO_FUNC(0x4)); + } + break; + } + if (peripheral != PERIPH_ID_SPI4) { + for (i = pin; i < pin + 4; i++) + s5p_gpio_cfg_pin(bank, i, cfg); + } +} + static int exynos5_pinmux_config(int peripheral, int flags) { switch (peripheral) { @@ -257,6 +311,72 @@ static int exynos5_pinmux_config(int peripheral, int flags) case PERIPH_ID_I2C7: exynos5_i2c_config(peripheral, flags); break; + case PERIPH_ID_I2S1: + exynos5_i2s_config(peripheral); + break; + case PERIPH_ID_SPI0: + case PERIPH_ID_SPI1: + case PERIPH_ID_SPI2: + case PERIPH_ID_SPI3: + case PERIPH_ID_SPI4: + exynos5_spi_config(peripheral); + break; + default: + debug("%s: invalid peripheral %d", __func__, peripheral); + return -1; + } + + return 0; +} + +static int exynos4_mmc_config(int peripheral, int flags) +{ + struct exynos4_gpio_part2 *gpio2 = + (struct exynos4_gpio_part2 *)samsung_get_base_gpio_part2(); + struct s5p_gpio_bank *bank, *bank_ext; + int i; + + switch (peripheral) { + case PERIPH_ID_SDMMC0: + bank = &gpio2->k0; + bank_ext = &gpio2->k1; + break; + case PERIPH_ID_SDMMC2: + bank = &gpio2->k2; + bank_ext = &gpio2->k3; + break; + default: + return -1; + } + for (i = 0; i < 7; i++) { + if (i == 2) + continue; + s5p_gpio_cfg_pin(bank, i, GPIO_FUNC(0x2)); + s5p_gpio_set_pull(bank, i, GPIO_PULL_NONE); + s5p_gpio_set_drv(bank, i, GPIO_DRV_4X); + } + if (flags & PINMUX_FLAG_8BIT_MODE) { + for (i = 3; i < 7; i++) { + s5p_gpio_cfg_pin(bank_ext, i, GPIO_FUNC(0x3)); + s5p_gpio_set_pull(bank_ext, i, GPIO_PULL_NONE); + s5p_gpio_set_drv(bank_ext, i, GPIO_DRV_4X); + } + } + + return 0; +} + +static int exynos4_pinmux_config(int peripheral, int flags) +{ + switch (peripheral) { + case PERIPH_ID_SDMMC0: + case PERIPH_ID_SDMMC2: + return exynos4_mmc_config(peripheral, flags); + case PERIPH_ID_SDMMC1: + case PERIPH_ID_SDMMC3: + case PERIPH_ID_SDMMC4: + printf("SDMMC device %d not implemented\n", peripheral); + return -1; default: debug("%s: invalid peripheral %d", __func__, peripheral); return -1; diff --git a/arch/arm/cpu/armv7/mx5/clock.c b/arch/arm/cpu/armv7/mx5/clock.c index 1c9223fa07..76c2c529a8 100644 --- a/arch/arm/cpu/armv7/mx5/clock.c +++ b/arch/arm/cpu/armv7/mx5/clock.c @@ -928,7 +928,9 @@ int do_mx5_showclocks(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) printf("IPG %8d kHz\n", mxc_get_clock(MXC_IPG_CLK) / 1000); printf("IPG PERCLK %8d kHz\n", mxc_get_clock(MXC_IPG_PERCLK) / 1000); printf("DDR %8d kHz\n", mxc_get_clock(MXC_DDR_CLK) / 1000); - +#ifdef CONFIG_MXC_SPI + printf("CSPI %8d kHz\n", mxc_get_clock(MXC_CSPI_CLK) / 1000); +#endif return 0; } diff --git a/arch/arm/cpu/armv7/mx5/lowlevel_init.S b/arch/arm/cpu/armv7/mx5/lowlevel_init.S index 29ec95797b..6d9396a976 100644 --- a/arch/arm/cpu/armv7/mx5/lowlevel_init.S +++ b/arch/arm/cpu/armv7/mx5/lowlevel_init.S @@ -396,7 +396,7 @@ ENTRY(lowlevel_init) mov r10, lr mov r4, #0 /* Fix R4 to 0 */ -#if defined(CONFIG_MX51) +#if defined(CONFIG_SYS_MAIN_PWR_ON) ldr r0, =GPIO1_BASE_ADDR ldr r1, [r0, #0x0] orr r1, r1, #1 << 23 diff --git a/arch/arm/cpu/armv7/mx6/clock.c b/arch/arm/cpu/armv7/mx6/clock.c index a01d96f48e..a50db70b19 100644 --- a/arch/arm/cpu/armv7/mx6/clock.c +++ b/arch/arm/cpu/armv7/mx6/clock.c @@ -404,7 +404,9 @@ int do_mx6_showclocks(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) printf("\n"); printf("IPG %8d kHz\n", mxc_get_clock(MXC_IPG_CLK) / 1000); printf("UART %8d kHz\n", mxc_get_clock(MXC_UART_CLK) / 1000); +#ifdef CONFIG_MXC_SPI printf("CSPI %8d kHz\n", mxc_get_clock(MXC_CSPI_CLK) / 1000); +#endif printf("AHB %8d kHz\n", mxc_get_clock(MXC_AHB_CLK) / 1000); printf("AXI %8d kHz\n", mxc_get_clock(MXC_AXI_CLK) / 1000); printf("DDR %8d kHz\n", mxc_get_clock(MXC_DDR_CLK) / 1000); diff --git a/arch/arm/cpu/armv7/mx6/soc.c b/arch/arm/cpu/armv7/mx6/soc.c index bc65767e7d..a8aad5dd0a 100644 --- a/arch/arm/cpu/armv7/mx6/soc.c +++ b/arch/arm/cpu/armv7/mx6/soc.c @@ -31,17 +31,33 @@ #include <asm/arch/sys_proto.h> #include <asm/imx-common/boot_mode.h> +struct scu_regs { + u32 ctrl; + u32 config; + u32 status; + u32 invalidate; + u32 fpga_rev; +}; + u32 get_cpu_rev(void) { struct anatop_regs *anatop = (struct anatop_regs *)ANATOP_BASE_ADDR; - int reg = readl(&anatop->digprog); - - /* Read mx6 variant: quad, dual or solo */ - int system_rev = (reg >> 4) & 0xFF000; - /* Read mx6 silicon revision */ - system_rev |= (reg & 0xFF) + 0x10; - - return system_rev; + u32 reg = readl(&anatop->digprog_sololite); + u32 type = ((reg >> 16) & 0xff); + + if (type != MXC_CPU_MX6SL) { + reg = readl(&anatop->digprog); + type = ((reg >> 16) & 0xff); + if (type == MXC_CPU_MX6DL) { + struct scu_regs *scu = (struct scu_regs *)SCU_BASE_ADDR; + u32 cfg = readl(&scu->config) & 3; + + if (!cfg) + type = MXC_CPU_MX6SOLO; + } + } + reg &= 0xff; /* mx6 silicon revision */ + return (type << 12) | (reg + 0x10); } void init_aips(void) diff --git a/arch/arm/cpu/armv7/s5p-common/Makefile b/arch/arm/cpu/armv7/s5p-common/Makefile index f975f3f06c..17053995bd 100644 --- a/arch/arm/cpu/armv7/s5p-common/Makefile +++ b/arch/arm/cpu/armv7/s5p-common/Makefile @@ -28,7 +28,6 @@ LIB = $(obj)libs5p-common.o COBJS-y += cpu_info.o COBJS-y += timer.o COBJS-y += sromc.o -COBJS-y += wdt.o COBJS-$(CONFIG_PWM) += pwm.o SRCS := $(SOBJS:.o=.S) $(COBJS:.o=.c) diff --git a/arch/arm/cpu/armv7/s5p-common/wdt.c b/arch/arm/cpu/armv7/s5p-common/wdt.c deleted file mode 100644 index 94acc1e4ba..0000000000 --- a/arch/arm/cpu/armv7/s5p-common/wdt.c +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2012 Samsung Electronics - * Minkyu Kang <mk7.kang@samsung.com> - * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, - * MA 02111-1307 USA - */ - -#include <common.h> -#include <asm/io.h> -#include <asm/arch/watchdog.h> - -#define PRESCALER_VAL 255 - -void wdt_stop(void) -{ - struct s5p_watchdog *wdt = - (struct s5p_watchdog *)samsung_get_base_watchdog(); - unsigned int wtcon; - - wtcon = readl(&wdt->wtcon); - wtcon &= ~(WTCON_EN | WTCON_INT | WTCON_RESET); - - writel(wtcon, &wdt->wtcon); -} - -void wdt_start(unsigned int timeout) -{ - struct s5p_watchdog *wdt = - (struct s5p_watchdog *)samsung_get_base_watchdog(); - unsigned int wtcon; - - wdt_stop(); - - wtcon = readl(&wdt->wtcon); - wtcon |= (WTCON_EN | WTCON_CLK(WTCON_CLK_128)); - wtcon &= ~WTCON_INT; - wtcon |= WTCON_RESET; - wtcon |= WTCON_PRESCALER(PRESCALER_VAL); - - writel(timeout, &wdt->wtdat); - writel(timeout, &wdt->wtcnt); - writel(wtcon, &wdt->wtcon); -} diff --git a/arch/arm/cpu/armv7/tegra20/Makefile b/arch/arm/cpu/armv7/tegra20/Makefile index 09a0314d0d..54ed8c48b4 100644 --- a/arch/arm/cpu/armv7/tegra20/Makefile +++ b/arch/arm/cpu/armv7/tegra20/Makefile @@ -28,6 +28,8 @@ include $(TOPDIR)/config.mk LIB = $(obj)lib$(SOC).o COBJS-$(CONFIG_USB_EHCI_TEGRA) += usb.o +COBJS-$(CONFIG_PWM_TEGRA) += pwm.o +COBJS-$(CONFIG_VIDEO_TEGRA) += display.o COBJS := $(COBJS-y) SRCS := $(COBJS:.o=.c) diff --git a/arch/arm/cpu/armv7/tegra20/display.c b/arch/arm/cpu/armv7/tegra20/display.c new file mode 100644 index 0000000000..031f9a850a --- /dev/null +++ b/arch/arm/cpu/armv7/tegra20/display.c @@ -0,0 +1,409 @@ +/* + * (C) Copyright 2010 + * NVIDIA Corporation <www.nvidia.com> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/arch/clock.h> +#include <asm/arch/tegra.h> +#include <asm/arch/display.h> +#include <asm/arch/dc.h> +#include <asm/arch-tegra/clk_rst.h> +#include <asm/arch-tegra/timer.h> + +static struct fdt_disp_config config; + +static void update_window(struct dc_ctlr *dc, struct disp_ctl_win *win) +{ + unsigned h_dda, v_dda; + unsigned long val; + + val = readl(&dc->cmd.disp_win_header); + val |= WINDOW_A_SELECT; + writel(val, &dc->cmd.disp_win_header); + + writel(win->fmt, &dc->win.color_depth); + + clrsetbits_le32(&dc->win.byte_swap, BYTE_SWAP_MASK, + BYTE_SWAP_NOSWAP << BYTE_SWAP_SHIFT); + + val = win->out_x << H_POSITION_SHIFT; + val |= win->out_y << V_POSITION_SHIFT; + writel(val, &dc->win.pos); + + val = win->out_w << H_SIZE_SHIFT; + val |= win->out_h << V_SIZE_SHIFT; + writel(val, &dc->win.size); + + val = (win->w * win->bpp / 8) << H_PRESCALED_SIZE_SHIFT; + val |= win->h << V_PRESCALED_SIZE_SHIFT; + writel(val, &dc->win.prescaled_size); + + writel(0, &dc->win.h_initial_dda); + writel(0, &dc->win.v_initial_dda); + + h_dda = (win->w * 0x1000) / max(win->out_w - 1, 1); + v_dda = (win->h * 0x1000) / max(win->out_h - 1, 1); + + val = h_dda << H_DDA_INC_SHIFT; + val |= v_dda << V_DDA_INC_SHIFT; + writel(val, &dc->win.dda_increment); + + writel(win->stride, &dc->win.line_stride); + writel(0, &dc->win.buf_stride); + + val = WIN_ENABLE; + if (win->bpp < 24) + val |= COLOR_EXPAND; + writel(val, &dc->win.win_opt); + + writel((unsigned long)win->phys_addr, &dc->winbuf.start_addr); + writel(win->x, &dc->winbuf.addr_h_offset); + writel(win->y, &dc->winbuf.addr_v_offset); + + writel(0xff00, &dc->win.blend_nokey); + writel(0xff00, &dc->win.blend_1win); + + val = GENERAL_ACT_REQ | WIN_A_ACT_REQ; + val |= GENERAL_UPDATE | WIN_A_UPDATE; + writel(val, &dc->cmd.state_ctrl); +} + +static void write_pair(struct fdt_disp_config *config, int item, u32 *reg) +{ + writel(config->horiz_timing[item] | + (config->vert_timing[item] << 16), reg); +} + +static int update_display_mode(struct dc_disp_reg *disp, + struct fdt_disp_config *config) +{ + unsigned long val; + unsigned long rate; + unsigned long div; + + writel(0x0, &disp->disp_timing_opt); + write_pair(config, FDT_LCD_TIMING_REF_TO_SYNC, &disp->ref_to_sync); + write_pair(config, FDT_LCD_TIMING_SYNC_WIDTH, &disp->sync_width); + write_pair(config, FDT_LCD_TIMING_BACK_PORCH, &disp->back_porch); + write_pair(config, FDT_LCD_TIMING_FRONT_PORCH, &disp->front_porch); + + writel(config->width | (config->height << 16), &disp->disp_active); + + val = DE_SELECT_ACTIVE << DE_SELECT_SHIFT; + val |= DE_CONTROL_NORMAL << DE_CONTROL_SHIFT; + writel(val, &disp->data_enable_opt); + + val = DATA_FORMAT_DF1P1C << DATA_FORMAT_SHIFT; + val |= DATA_ALIGNMENT_MSB << DATA_ALIGNMENT_SHIFT; + val |= DATA_ORDER_RED_BLUE << DATA_ORDER_SHIFT; + writel(val, &disp->disp_interface_ctrl); + + /* + * The pixel clock divider is in 7.1 format (where the bottom bit + * represents 0.5). Here we calculate the divider needed to get from + * the display clock (typically 600MHz) to the pixel clock. We round + * up or down as requried. + */ + rate = clock_get_periph_rate(PERIPH_ID_DISP1, CLOCK_ID_CGENERAL); + div = ((rate * 2 + config->pixel_clock / 2) / config->pixel_clock) - 2; + debug("Display clock %lu, divider %lu\n", rate, div); + + writel(0x00010001, &disp->shift_clk_opt); + + val = PIXEL_CLK_DIVIDER_PCD1 << PIXEL_CLK_DIVIDER_SHIFT; + val |= div << SHIFT_CLK_DIVIDER_SHIFT; + writel(val, &disp->disp_clk_ctrl); + + return 0; +} + +/* Start up the display and turn on power to PWMs */ +static void basic_init(struct dc_cmd_reg *cmd) +{ + u32 val; + + writel(0x00000100, &cmd->gen_incr_syncpt_ctrl); + writel(0x0000011a, &cmd->cont_syncpt_vsync); + writel(0x00000000, &cmd->int_type); + writel(0x00000000, &cmd->int_polarity); + writel(0x00000000, &cmd->int_mask); + writel(0x00000000, &cmd->int_enb); + + val = PW0_ENABLE | PW1_ENABLE | PW2_ENABLE; + val |= PW3_ENABLE | PW4_ENABLE | PM0_ENABLE; + val |= PM1_ENABLE; + writel(val, &cmd->disp_pow_ctrl); + + val = readl(&cmd->disp_cmd); + val |= CTRL_MODE_C_DISPLAY << CTRL_MODE_SHIFT; + writel(val, &cmd->disp_cmd); +} + +static void basic_init_timer(struct dc_disp_reg *disp) +{ + writel(0x00000020, &disp->mem_high_pri); + writel(0x00000001, &disp->mem_high_pri_timer); +} + +static const u32 rgb_enb_tab[PIN_REG_COUNT] = { + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; + +static const u32 rgb_polarity_tab[PIN_REG_COUNT] = { + 0x00000000, + 0x01000000, + 0x00000000, + 0x00000000, +}; + +static const u32 rgb_data_tab[PIN_REG_COUNT] = { + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; + +static const u32 rgb_sel_tab[PIN_OUTPUT_SEL_COUNT] = { + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00210222, + 0x00002200, + 0x00020000, +}; + +static void rgb_enable(struct dc_com_reg *com) +{ + int i; + + for (i = 0; i < PIN_REG_COUNT; i++) { + writel(rgb_enb_tab[i], &com->pin_output_enb[i]); + writel(rgb_polarity_tab[i], &com->pin_output_polarity[i]); + writel(rgb_data_tab[i], &com->pin_output_data[i]); + } + + for (i = 0; i < PIN_OUTPUT_SEL_COUNT; i++) + writel(rgb_sel_tab[i], &com->pin_output_sel[i]); +} + +int setup_window(struct disp_ctl_win *win, struct fdt_disp_config *config) +{ + win->x = 0; + win->y = 0; + win->w = config->width; + win->h = config->height; + win->out_x = 0; + win->out_y = 0; + win->out_w = config->width; + win->out_h = config->height; + win->phys_addr = config->frame_buffer; + win->stride = config->width * (1 << config->log2_bpp) / 8; + debug("%s: depth = %d\n", __func__, config->log2_bpp); + switch (config->log2_bpp) { + case 5: + case 24: + win->fmt = COLOR_DEPTH_R8G8B8A8; + win->bpp = 32; + break; + case 4: + win->fmt = COLOR_DEPTH_B5G6R5; + win->bpp = 16; + break; + + default: + debug("Unsupported LCD bit depth"); + return -1; + } + + return 0; +} + +struct fdt_disp_config *tegra_display_get_config(void) +{ + return config.valid ? &config : NULL; +} + +static void debug_timing(const char *name, unsigned int timing[]) +{ +#ifdef DEBUG + int i; + + debug("%s timing: ", name); + for (i = 0; i < FDT_LCD_TIMING_COUNT; i++) + debug("%d ", timing[i]); + debug("\n"); +#endif +} + +/** + * Decode panel information from the fdt, according to a standard binding + * + * @param blob fdt blob + * @param node offset of fdt node to read from + * @param config structure to store fdt config into + * @return 0 if ok, -ve on error + */ +static int tegra_decode_panel(const void *blob, int node, + struct fdt_disp_config *config) +{ + int front, back, ref; + + config->width = fdtdec_get_int(blob, node, "xres", -1); + config->height = fdtdec_get_int(blob, node, "yres", -1); + config->pixel_clock = fdtdec_get_int(blob, node, "clock", 0); + if (!config->pixel_clock || config->width == -1 || + config->height == -1) { + debug("%s: Pixel parameters missing\n", __func__); + return -FDT_ERR_NOTFOUND; + } + + back = fdtdec_get_int(blob, node, "left-margin", -1); + front = fdtdec_get_int(blob, node, "right-margin", -1); + ref = fdtdec_get_int(blob, node, "hsync-len", -1); + if ((back | front | ref) == -1) { + debug("%s: Horizontal parameters missing\n", __func__); + return -FDT_ERR_NOTFOUND; + } + + /* Use a ref-to-sync of 1 always, and take this from the front porch */ + config->horiz_timing[FDT_LCD_TIMING_REF_TO_SYNC] = 1; + config->horiz_timing[FDT_LCD_TIMING_SYNC_WIDTH] = ref; + config->horiz_timing[FDT_LCD_TIMING_BACK_PORCH] = back; + config->horiz_timing[FDT_LCD_TIMING_FRONT_PORCH] = front - + config->horiz_timing[FDT_LCD_TIMING_REF_TO_SYNC]; + debug_timing("horiz", config->horiz_timing); + + back = fdtdec_get_int(blob, node, "upper-margin", -1); + front = fdtdec_get_int(blob, node, "lower-margin", -1); + ref = fdtdec_get_int(blob, node, "vsync-len", -1); + if ((back | front | ref) == -1) { + debug("%s: Vertical parameters missing\n", __func__); + return -FDT_ERR_NOTFOUND; + } + + config->vert_timing[FDT_LCD_TIMING_REF_TO_SYNC] = 1; + config->vert_timing[FDT_LCD_TIMING_SYNC_WIDTH] = ref; + config->vert_timing[FDT_LCD_TIMING_BACK_PORCH] = back; + config->vert_timing[FDT_LCD_TIMING_FRONT_PORCH] = front - + config->vert_timing[FDT_LCD_TIMING_REF_TO_SYNC]; + debug_timing("vert", config->vert_timing); + + return 0; +} + +/** + * Decode the display controller information from the fdt. + * + * @param blob fdt blob + * @param config structure to store fdt config into + * @return 0 if ok, -ve on error + */ +static int tegra_display_decode_config(const void *blob, + struct fdt_disp_config *config) +{ + int node, rgb; + int bpp, bit; + + /* TODO: Support multiple controllers */ + node = fdtdec_next_compatible(blob, 0, COMPAT_NVIDIA_TEGRA20_DC); + if (node < 0) { + debug("%s: Cannot find display controller node in fdt\n", + __func__); + return node; + } + config->disp = (struct disp_ctlr *)fdtdec_get_addr(blob, node, "reg"); + if (!config->disp) { + debug("%s: No display controller address\n", __func__); + return -1; + } + + rgb = fdt_subnode_offset(blob, node, "rgb"); + + config->panel_node = fdtdec_lookup_phandle(blob, rgb, "nvidia,panel"); + if (!config->panel_node < 0) { + debug("%s: Cannot find panel information\n", __func__); + return -1; + } + + if (tegra_decode_panel(blob, config->panel_node, config)) { + debug("%s: Failed to decode panel information\n", __func__); + return -1; + } + + bpp = fdtdec_get_int(blob, config->panel_node, "nvidia,bits-per-pixel", + -1); + bit = ffs(bpp) - 1; + if (bpp == (1 << bit)) + config->log2_bpp = bit; + else + config->log2_bpp = bpp; + if (bpp == -1) { + debug("%s: Pixel bpp parameters missing\n", __func__); + return -FDT_ERR_NOTFOUND; + } + config->bpp = bpp; + + config->valid = 1; /* we have a valid configuration */ + + return 0; +} + +int tegra_display_probe(const void *blob, void *default_lcd_base) +{ + struct disp_ctl_win window; + struct dc_ctlr *dc; + + if (tegra_display_decode_config(blob, &config)) + return -1; + + config.frame_buffer = (u32)default_lcd_base; + + dc = (struct dc_ctlr *)config.disp; + + /* + * A header file for clock constants was NAKed upstream. + * TODO: Put this into the FDT and fdt_lcd struct when we have clock + * support there + */ + clock_start_periph_pll(PERIPH_ID_HOST1X, CLOCK_ID_PERIPH, + 144 * 1000000); + clock_start_periph_pll(PERIPH_ID_DISP1, CLOCK_ID_CGENERAL, + 600 * 1000000); + basic_init(&dc->cmd); + basic_init_timer(&dc->disp); + rgb_enable(&dc->com); + + if (config.pixel_clock) + update_display_mode(&dc->disp, &config); + + if (setup_window(&window, &config)) + return -1; + + update_window(dc, &window); + + return 0; +} diff --git a/arch/arm/cpu/armv7/tegra20/pwm.c b/arch/arm/cpu/armv7/tegra20/pwm.c new file mode 100644 index 0000000000..b655c5cd06 --- /dev/null +++ b/arch/arm/cpu/armv7/tegra20/pwm.c @@ -0,0 +1,101 @@ +/* + * Tegra2 pulse width frequency modulator definitions + * + * Copyright (c) 2011 The Chromium OS Authors. + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <fdtdec.h> +#include <asm/io.h> +#include <asm/arch/clock.h> +#include <asm/arch/pwm.h> + +struct pwm_info { + struct pwm_ctlr *pwm; /* Registers for our pwm controller */ + int pwm_node; /* PWM device tree node */ +} local; + +void pwm_enable(unsigned channel, int rate, int pulse_width, int freq_divider) +{ + u32 reg; + + assert(channel < PWM_NUM_CHANNELS); + + /* TODO: Can we use clock_adjust_periph_pll_div() here? */ + clock_start_periph_pll(PERIPH_ID_PWM, CLOCK_ID_SFROM32KHZ, rate); + + reg = PWM_ENABLE_MASK; + reg |= pulse_width << PWM_WIDTH_SHIFT; + reg |= freq_divider << PWM_DIVIDER_SHIFT; + writel(reg, &local.pwm[channel].control); + debug("%s: channel=%d, rate=%d\n", __func__, channel, rate); +} + +int pwm_request(const void *blob, int node, const char *prop_name) +{ + int pwm_node; + u32 data[3]; + + if (fdtdec_get_int_array(blob, node, prop_name, data, + ARRAY_SIZE(data))) { + debug("%s: Cannot decode PWM property '%s'\n", __func__, + prop_name); + return -1; + } + + pwm_node = fdt_node_offset_by_phandle(blob, data[0]); + if (pwm_node != local.pwm_node) { + debug("%s: PWM property '%s' phandle %d not recognised" + "- expecting %d\n", __func__, prop_name, data[0], + local.pwm_node); + return -1; + } + if (data[1] >= PWM_NUM_CHANNELS) { + debug("%s: PWM property '%s': invalid channel %u\n", __func__, + prop_name, data[1]); + return -1; + } + + /* + * TODO: We could maintain a list of requests, but it might not be + * worth it for U-Boot. + */ + return data[1]; +} + +int pwm_init(const void *blob) +{ + local.pwm_node = fdtdec_next_compatible(blob, 0, + COMPAT_NVIDIA_TEGRA20_PWM); + if (local.pwm_node < 0) { + debug("%s: Cannot find device tree node\n", __func__); + return -1; + } + + local.pwm = (struct pwm_ctlr *)fdtdec_get_addr(blob, local.pwm_node, + "reg"); + if (local.pwm == (struct pwm_ctlr *)FDT_ADDR_T_NONE) { + debug("%s: Cannot find pwm reg address\n", __func__); + return -1; + } + debug("Tegra PWM at %p, node %d\n", local.pwm, local.pwm_node); + + return 0; +} diff --git a/arch/arm/cpu/tegra20-common/funcmux.c b/arch/arm/cpu/tegra20-common/funcmux.c index 00b8029eba..ece7ad9ec9 100644 --- a/arch/arm/cpu/tegra20-common/funcmux.c +++ b/arch/arm/cpu/tegra20-common/funcmux.c @@ -25,6 +25,30 @@ #include <asm/arch/funcmux.h> #include <asm/arch/pinmux.h> +/* + * The PINMUX macro is used to set up pinmux tables. + */ +#define PINMUX(grp, mux, pupd, tri) \ + {PINGRP_##grp, PMUX_FUNC_##mux, PMUX_PULL_##pupd, PMUX_TRI_##tri} + +static const struct pingroup_config disp1_default[] = { + PINMUX(LDI, DISPA, NORMAL, NORMAL), + PINMUX(LHP0, DISPA, NORMAL, NORMAL), + PINMUX(LHP1, DISPA, NORMAL, NORMAL), + PINMUX(LHP2, DISPA, NORMAL, NORMAL), + PINMUX(LHS, DISPA, NORMAL, NORMAL), + PINMUX(LM0, RSVD4, NORMAL, NORMAL), + PINMUX(LPP, DISPA, NORMAL, NORMAL), + PINMUX(LPW0, DISPA, NORMAL, NORMAL), + PINMUX(LPW2, DISPA, NORMAL, NORMAL), + PINMUX(LSC0, DISPA, NORMAL, NORMAL), + PINMUX(LSPI, DISPA, NORMAL, NORMAL), + PINMUX(LVP1, DISPA, NORMAL, NORMAL), + PINMUX(LVS, DISPA, NORMAL, NORMAL), + PINMUX(SLXD, SPDIF, NORMAL, NORMAL), +}; + + int funcmux_select(enum periph_id id, int config) { int bad_config = config != FUNCMUX_DEFAULT; @@ -257,6 +281,19 @@ int funcmux_select(enum periph_id id, int config) break; } break; + case PERIPH_ID_DISP1: + if (config == FUNCMUX_DEFAULT) { + int i; + + for (i = PINGRP_LD0; i <= PINGRP_LD17; i++) { + pinmux_set_func(i, PMUX_FUNC_DISPA); + pinmux_tristate_disable(i); + pinmux_set_pullupdown(i, PMUX_PULL_NORMAL); + } + pinmux_config_table(disp1_default, + ARRAY_SIZE(disp1_default)); + } + break; default: debug("%s: invalid periph_id %d", __func__, id); diff --git a/arch/arm/cpu/tegra20-common/pinmux.c b/arch/arm/cpu/tegra20-common/pinmux.c index 08b83055db..a2a09169e5 100644 --- a/arch/arm/cpu/tegra20-common/pinmux.c +++ b/arch/arm/cpu/tegra20-common/pinmux.c @@ -554,7 +554,7 @@ void pinmux_set_func(enum pmux_pingrp pin, enum pmux_func func) writel(reg, muxctl); } -void pinmux_config_pingroup(struct pingroup_config *config) +void pinmux_config_pingroup(const struct pingroup_config *config) { enum pmux_pingrp pin = config->pingroup; @@ -563,7 +563,7 @@ void pinmux_config_pingroup(struct pingroup_config *config) pinmux_set_tristate(pin, config->tristate); } -void pinmux_config_table(struct pingroup_config *config, int len) +void pinmux_config_table(const struct pingroup_config *config, int len) { int i; |