pit: Create an exynos5420 directory which is nearly a copy of exynos5250.

This change creates an exynos5420 directory with code that will eventually
implement support for the exynos5420 cpu from Samsung. Currently it's a copy
of the exynos5250 directory with the name changed. There are going to be some
problems where headers in src/cpu/samsung/exynos-common include headers in the
exynos5250 directory directly.

Change-Id: Ia8d7244310d32499238bbc171c0c668ec48178e1
Signed-off-by: Gabe Black <gabeblack@chromium.org>
Reviewed-on: http://review.coreboot.org/3644
Tested-by: build bot (Jenkins)
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
This commit is contained in:
Gabe Black 2013-05-16 05:45:57 -07:00 committed by Stefan Reinauer
parent dc006c1db4
commit 607c0b6d63
48 changed files with 9749 additions and 4 deletions

View File

@ -1,4 +1,4 @@
config CPU_SAMSUNG_EXYNOS5
config CPU_SAMSUNG_EXYNOS5250
depends on ARCH_ARMV7
select HAVE_MONOTONIC_TIMER
select HAVE_UART_SPECIAL
@ -6,6 +6,18 @@ config CPU_SAMSUNG_EXYNOS5
bool
default n
if CPU_SAMSUNG_EXYNOS5
config CPU_SAMSUNG_EXYNOS5420
depends on ARCH_ARMV7
select HAVE_MONOTONIC_TIMER
select HAVE_UART_SPECIAL
select DEFAULT_EARLY_CONSOLE
bool
default n
if CPU_SAMSUNG_EXYNOS5250
source src/cpu/samsung/exynos5250/Kconfig
endif
if CPU_SAMSUNG_EXYNOS5420
source src/cpu/samsung/exynos5420/Kconfig
endif

View File

@ -1 +1,2 @@
subdirs-$(CONFIG_CPU_SAMSUNG_EXYNOS5) += exynos5250
subdirs-$(CONFIG_CPU_SAMSUNG_EXYNOS5250) += exynos5250
subdirs-$(CONFIG_CPU_SAMSUNG_EXYNOS5420) += exynos5420

View File

@ -0,0 +1,133 @@
config BOOTBLOCK_CPU_INIT
string
default "cpu/samsung/exynos5420/bootblock.c"
help
CPU/SoC-specific bootblock code. This is useful if the
bootblock must load microcode or copy data from ROM before
searching for the bootblock.
config EXYNOS_ACE_SHA
bool
default n
config BL1_SIZE_KB
int
default 8
# Example SRAM/iRAM map for Exynos5420 platform:
#
# 0x0202_0000: vendor-provided BL1
# 0x0202_3400: bootblock, assume up to 32KB in size
# 0x0203_0000: romstage, assume up to 128KB in size.
# 0x0207_8000: stack pointer
config BOOTBLOCK_BASE
hex
default 0x02023400
config ROMSTAGE_BASE
hex
default 0x02030000
config ROMSTAGE_SIZE
hex
default 0x10000
# Stack may reside in either IRAM or DRAM. We will define it to live
# at the top of IRAM for now.
#
# Stack grows downward, push operation stores register contents in
# consecutive memory locations ending just below SP
config STACK_TOP
hex
default 0x02078000
config STACK_BOTTOM
hex
default 0x02077000
config STACK_SIZE
hex
default 0x1000
config CBFS_ROM_OFFSET
# Calculated by BL1 + max bootblock size.
hex "offset of CBFS data in ROM"
default 0x0A000
# TODO Change this to some better address not overlapping bootblock when
# cbfstool supports creating header in arbitrary location.
config CBFS_HEADER_ROM_OFFSET
hex "offset of master CBFS header in ROM"
default 0x2040
# TODO We may probably move this to board-specific implementation files instead
# of KConfig values.
config CBFS_CACHE_ADDRESS
hex "memory address to put CBFS cache data"
default 0x02060000
config CBFS_CACHE_SIZE
hex "size of CBFS cache data"
default 0x000017000
# FIXME: This is for copying SPI content into SRAM temporarily and
# will be removed when we have the SPI streaming driver implemented.
config SPI_IMAGE_HACK
hex
default 0x02060000
# FIXME: other magic numbers that should probably go away
config XIP_ROM_SIZE
hex
default ROMSTAGE_SIZE
config SYS_SDRAM_BASE
hex
default 0x40000000
config SYS_TEXT_BASE
hex
default 0x43e00000
config COREBOOT_TABLES_SIZE
hex
default 0x4000000
choice CONSOLE_SERIAL_UART_CHOICES
prompt "Serial Console UART"
default CONSOLE_SERIAL_UART3
depends on CONSOLE_SERIAL_UART
config CONSOLE_SERIAL_UART0
bool "UART0"
help
Serial console on UART0
config CONSOLE_SERIAL_UART1
bool "UART1"
help
Serial console on UART1
config CONSOLE_SERIAL_UART2
bool "UART2"
help
Serial console on UART2
config CONSOLE_SERIAL_UART3
bool "UART3"
help
Serial console on UART3
endchoice
config CONSOLE_SERIAL_UART_ADDRESS
hex
depends on CONSOLE_SERIAL_UART
default 0x12c00000 if CONSOLE_SERIAL_UART0
default 0x12c10000 if CONSOLE_SERIAL_UART1
default 0x12c20000 if CONSOLE_SERIAL_UART2
default 0x12c30000 if CONSOLE_SERIAL_UART3
help
Map the UART names to the respective MMIO address.

View File

@ -0,0 +1,55 @@
# Run an intermediate step when producing coreboot.rom
# that adds additional components to the final firmware
# image outside of CBFS
INTERMEDIATE += exynos5420_add_bl1
bootblock-y += spi.c
bootblock-y += pinmux.c mct.c power.c
# Clock is required for UART
bootblock-$(CONFIG_EARLY_CONSOLE) += clock_init.c
bootblock-$(CONFIG_EARLY_CONSOLE) += clock.c
bootblock-$(CONFIG_EARLY_CONSOLE) += monotonic_timer.c
bootblock-$(CONFIG_EARLY_CONSOLE) += uart.c
bootblock-y += wakeup.c
bootblock-y += gpio.c
bootblock-$(CONFIG_EARLY_CONSOLE) += pwm.c
bootblock-$(CONFIG_EARLY_CONSOLE) += timer.c
romstage-y += spi.c
romstage-y += clock.c
romstage-y += clock_init.c
romstage-y += pinmux.c # required by s3c24x0_i2c and uart.
romstage-y += dmc_common.c
romstage-y += dmc_init_ddr3.c
romstage-y += power.c
romstage-y += mct.c
romstage-y += monotonic_timer.c
romstage-$(CONFIG_EARLY_CONSOLE) += uart.c
romstage-y += wakeup.c
romstage-y += pwm.c # needed by timer.c
romstage-y += gpio.c
romstage-y += timer.c
romstage-y += i2c.c
#romstage-y += wdt.c
ramstage-y += spi.c
ramstage-y += clock.c
ramstage-y += clock_init.c
ramstage-y += pinmux.c
ramstage-y += power.c
ramstage-$(CONFIG_CONSOLE_SERIAL_UART) += uart.c
ramstage-y += cpu.c
ramstage-y += tmu.c
ramstage-y += mct.c
ramstage-y += monotonic_timer.c
ramstage-y += pwm.c # needed by timer.c
ramstage-y += timer.c
ramstage-y += gpio.c
ramstage-y += i2c.c
ramstage-y += dp-reg.c
ramstage-y += fb.c
exynos5420_add_bl1: $(obj)/coreboot.pre
printf " DD Adding Samsung Exynos5420 BL1\n"
dd if=3rdparty/cpu/samsung/exynos5420/bl1.bin \
of=$(obj)/coreboot.pre conv=notrunc >/dev/null 2>&1

View File

@ -0,0 +1,43 @@
/*
* This file is part of the coreboot project.
*
* Copyright 2013 Google Inc.
*
* 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; version 2 of the License.
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <console/console.h>
#include "clk.h"
#include "wakeup.h"
void bootblock_cpu_init(void);
void bootblock_cpu_init(void)
{
/* kick off the multi-core timer.
* We want to do this as early as we can.
*/
mct_start();
if (get_wakeup_state() == WAKEUP_DIRECT) {
wakeup();
/* Never returns. */
}
/* For most ARM systems, we have to initialize firmware media source
* (ex, SPI, SD/MMC, or eMMC) now; but for Exynos platform, that is
* already handled by iROM so there's no need to setup again.
*/
console_init();
}

View File

@ -0,0 +1,40 @@
/*
* This file is part of the coreboot project.
*
* Copyright 2013 Google Inc.
*
* 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; version 2 of the License.
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef CPU_SAMSUNG_EXYNOS5420_H
#define CPU_SAMSUNG_EXYNOS5420_H
struct cpu_samsung_exynos5420_config {
/* special magic numbers! */
int clkval_f;
int upper_margin;
int lower_margin;
int vsync;
int left_margin;
int right_margin;
int hsync;
int xres;
int yres;
int bpp;
u32 lcdbase;
};
#endif /* CPU_SAMSUNG_EXYNOS5420_H */

View File

@ -0,0 +1,624 @@
/*
* This file is part of the coreboot project.
*
* Copyright 2013 Google Inc.
* Copyright (C) 2012 Samsung Electronics
*
* 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; version 2 of the License.
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef CPU_SAMSUNG_EXYNOS5420_CLK_H
#define CPU_SAMSUNG_EXYNOS5420_CLK_H
#include <stdint.h>
enum periph_id;
#define APLL 0
#define MPLL 1
#define EPLL 2
#define HPLL 3
#define VPLL 4
#define BPLL 5
enum pll_src_bit {
SRC_MPLL = 6,
SRC_EPLL,
SRC_VPLL,
};
/* *
* This structure is to store the src bit, div bit and prediv bit
* positions of the peripheral clocks of the src and div registers
*/
struct clk_bit_info {
s8 src_bit; /* offset in register to clock source field */
s8 n_src_bits; /* number of bits in 'src_bit' field */
s8 div_bit;
s8 prediv_bit;
};
unsigned long get_pll_clk(int pllreg);
unsigned long get_arm_clk(void);
unsigned long get_pwm_clk(void);
unsigned long get_uart_clk(int dev_index);
void set_mmc_clk(int dev_index, unsigned int div);
/**
* get the clk frequency of the required peripherial
*
* @param peripherial Peripherial id
*
* @return frequency of the peripherial clk
*/
unsigned long clock_get_periph_rate(enum periph_id peripheral);
#include "pinmux.h"
#define MCT_ADDRESS 0x101c0000
#define MCT_HZ 24000000
/*
* Set mshci controller instances clock drivder
*
* @param enum periph_id instance of the mshci controller
*
* Return 0 if ok else -1
*/
int clock_set_mshci(enum periph_id peripheral);
/*
* Sets the epll clockrate
*
* @param rate Required clock rate to the presacaler in Hz
*
* Return 0 if ok else -1
*/
int clock_epll_set_rate(unsigned long rate);
/*
* selects the clk source for I2S MCLK
*/
void clock_select_i2s_clk_source(void);
/*
* Set prescaler division based on input and output frequency
* for i2s audio clock
*
* @param src_frq Source frequency in Hz
* @param dst_frq Required MCLK frequency in Hz
*
* Return 0 if ok else -1
*/
int clock_set_i2s_clk_prescaler(unsigned int src_frq, unsigned int dst_frq);
struct exynos5_clock {
unsigned int apll_lock; /* base + 0 */
unsigned char res1[0xfc];
unsigned int apll_con0;
unsigned int apll_con1;
unsigned char res2[0xf8];
unsigned int src_cpu;
unsigned char res3[0x1fc];
unsigned int mux_stat_cpu;
unsigned char res4[0xfc];
unsigned int div_cpu0;
unsigned int div_cpu1;
unsigned char res5[0xf8];
unsigned int div_stat_cpu0;
unsigned int div_stat_cpu1;
unsigned char res6[0x1f8];
unsigned int gate_sclk_cpu;
unsigned char res7[0x1fc];
unsigned int clkout_cmu_cpu;
unsigned int clkout_cmu_cpu_div_stat;
unsigned char res8[0x5f8];
unsigned int armclk_stopctrl; /* base + 0x1000 */
unsigned int atclk_stopctrl;
unsigned char res9[0x8];
unsigned int parityfail_status;
unsigned int parityfail_clear;
unsigned char res10[0x8];
unsigned int pwr_ctrl;
unsigned int pwr_ctr2;
unsigned char res11[0xd8];
unsigned int apll_con0_l8;
unsigned int apll_con0_l7;
unsigned int apll_con0_l6;
unsigned int apll_con0_l5;
unsigned int apll_con0_l4;
unsigned int apll_con0_l3;
unsigned int apll_con0_l2;
unsigned int apll_con0_l1;
unsigned int iem_control;
unsigned char res12[0xdc];
unsigned int apll_con1_l8;
unsigned int apll_con1_l7;
unsigned int apll_con1_l6;
unsigned int apll_con1_l5;
unsigned int apll_con1_l4;
unsigned int apll_con1_l3;
unsigned int apll_con1_l2;
unsigned int apll_con1_l1;
unsigned char res13[0xe0];
unsigned int div_iem_l8;
unsigned int div_iem_l7;
unsigned int div_iem_l6;
unsigned int div_iem_l5;
unsigned int div_iem_l4;
unsigned int div_iem_l3;
unsigned int div_iem_l2;
unsigned int div_iem_l1;
unsigned char res14[0x2ce0];
unsigned int mpll_lock; /* base + 0x4000 */
unsigned char res15[0xfc];
unsigned int mpll_con0;
unsigned int mpll_con1;
unsigned char res16[0xf8];
unsigned int src_core0;
unsigned int src_core1;
unsigned char res17[0xf8];
unsigned int src_mask_core;
unsigned char res18[0x100];
unsigned int mux_stat_core1;
unsigned char res19[0xf8];
unsigned int div_core0;
unsigned int div_core1;
unsigned int div_sysrgt;
unsigned char res20[0xf4];
unsigned int div_stat_core0;
unsigned int div_stat_core1;
unsigned int div_stat_sysrgt;
unsigned char res21[0x2f4];
unsigned int gate_ip_core;
unsigned int gate_ip_sysrgt;
unsigned char res22[0xf8];
unsigned int clkout_cmu_core;
unsigned int clkout_cmu_core_div_stat;
unsigned char res23[0x5f8];
unsigned int dcgidx_map0; /* base + 0x5000 */
unsigned int dcgidx_map1;
unsigned int dcgidx_map2;
unsigned char res24[0x14];
unsigned int dcgperf_map0;
unsigned int dcgperf_map1;
unsigned char res25[0x18];
unsigned int dvcidx_map;
unsigned char res26[0x1c];
unsigned int freq_cpu;
unsigned int freq_dpm;
unsigned char res27[0x18];
unsigned int dvsemclk_en;
unsigned int maxperf;
unsigned char res28[0x3478];
unsigned int div_acp; /* base + 0x8500 */
unsigned char res29[0xfc];
unsigned int div_stat_acp;
unsigned char res30[0x1fc];
unsigned int gate_ip_acp;
unsigned char res31a[0xfc];
unsigned int div_syslft;
unsigned char res31b[0xc];
unsigned int div_stat_syslft;
unsigned char res31c[0xc];
unsigned int gate_bus_syslft;
unsigned char res31d[0xdc];
unsigned int clkout_cmu_acp;
unsigned int clkout_cmu_acp_div_stat;
unsigned char res32[0x38f8];
unsigned int div_isp0; /* base + 0xc300 */
unsigned int div_isp1;
unsigned int div_isp2;
unsigned char res33[0xf4];
unsigned int div_stat_isp0; /* base + 0xc400 */
unsigned int div_stat_isp1;
unsigned int div_stat_isp2;
unsigned char res34[0x3f4];
unsigned int gate_ip_isp0; /* base + 0xc800 */
unsigned int gate_ip_isp1;
unsigned char res35[0xf8];
unsigned int gate_sclk_isp;
unsigned char res36[0xc];
unsigned int mcuisp_pwr_ctrl;
unsigned char res37[0xec];
unsigned int clkout_cmu_isp;
unsigned int clkout_cmu_isp_div_stat;
unsigned char res38[0x3618];
unsigned int cpll_lock; /* base + 0x10020 */
unsigned char res39[0xc];
unsigned int epll_lock;
unsigned char res40[0xc];
unsigned int vpll_lock;
unsigned char res41a[0xc];
unsigned int gpll_lock;
unsigned char res41b[0xcc];
unsigned int cpll_con0;
unsigned int cpll_con1;
unsigned char res42[0x8];
unsigned int epll_con0;
unsigned int epll_con1;
unsigned int epll_con2;
unsigned char res43[0x4];
unsigned int vpll_con0;
unsigned int vpll_con1;
unsigned int vpll_con2;
unsigned char res44a[0x4];
unsigned int gpll_con0;
unsigned int gpll_con1;
unsigned char res44b[0xb8];
unsigned int src_top0;
unsigned int src_top1;
unsigned int src_top2;
unsigned int src_top3;
unsigned int src_gscl;
unsigned int src_disp0_0;
unsigned int src_disp0_1;
unsigned int src_disp1_0;
unsigned int src_disp1_1;
unsigned char res46[0xc];
unsigned int src_mau;
unsigned int src_fsys;
unsigned char res47[0x8];
unsigned int src_peric0;
unsigned int src_peric1;
unsigned char res48[0x18];
unsigned int sclk_src_isp;
unsigned char res49[0x9c];
unsigned int src_mask_top;
unsigned char res50[0xc];
unsigned int src_mask_gscl;
unsigned int src_mask_disp0_0;
unsigned int src_mask_disp0_1;
unsigned int src_mask_disp1_0;
unsigned int src_mask_disp1_1;
unsigned int src_mask_maudio;
unsigned char res52[0x8];
unsigned int src_mask_fsys;
unsigned char res53[0xc];
unsigned int src_mask_peric0;
unsigned int src_mask_peric1;
unsigned char res54[0x18];
unsigned int src_mask_isp;
unsigned char res55[0x9c];
unsigned int mux_stat_top0;
unsigned int mux_stat_top1;
unsigned int mux_stat_top2;
unsigned int mux_stat_top3;
unsigned char res56[0xf0];
unsigned int div_top0;
unsigned int div_top1;
unsigned char res57[0x8];
unsigned int div_gscl;
unsigned int div_disp0_0;
unsigned int div_disp0_1;
unsigned int div_disp1_0;
unsigned int div_disp1_1;
unsigned char res59[0x8];
unsigned int div_gen;
unsigned char res60[0x4];
unsigned int div_mau;
unsigned int div_fsys0;
unsigned int div_fsys1;
unsigned int div_fsys2;
unsigned int div_fsys3;
unsigned int div_peric0;
unsigned int div_peric1;
unsigned int div_peric2;
unsigned int div_peric3;
unsigned int div_peric4;
unsigned int div_peric5;
unsigned char res61[0x10];
unsigned int sclk_div_isp;
unsigned char res62[0xc];
unsigned int div2_ratio0;
unsigned int div2_ratio1;
unsigned char res63[0x8];
unsigned int div4_ratio;
unsigned char res64[0x6c];
unsigned int div_stat_top0;
unsigned int div_stat_top1;
unsigned char res65[0x8];
unsigned int div_stat_gscl;
unsigned int div_stat_disp0_0;
unsigned int div_stat_disp0_1;
unsigned int div_stat_disp1_0;
unsigned int div_stat_disp1_1;
unsigned char res67[0x8];
unsigned int div_stat_gen;
unsigned char res68[0x4];
unsigned int div_stat_maudio;
unsigned int div_stat_fsys0;
unsigned int div_stat_fsys1;
unsigned int div_stat_fsys2;
unsigned int div_stat_fsys3;
unsigned int div_stat_peric0;
unsigned int div_stat_peric1;
unsigned int div_stat_peric2;
unsigned int div_stat_peric3;
unsigned int div_stat_peric4;
unsigned int div_stat_peric5;
unsigned char res69[0x10];
unsigned int sclk_div_stat_isp;
unsigned char res70[0xc];
unsigned int div2_stat0;
unsigned int div2_stat1;
unsigned char res71[0x8];
unsigned int div4_stat;
unsigned char res72[0x180];
unsigned int gate_top_sclk_disp0;
unsigned int gate_top_sclk_disp1;
unsigned int gate_top_sclk_gen;
unsigned char res74[0xc];
unsigned int gate_top_sclk_mau;
unsigned int gate_top_sclk_fsys;
unsigned char res75[0xc];
unsigned int gate_top_sclk_peric;
unsigned char res76[0x1c];
unsigned int gate_top_sclk_isp;
unsigned char res77[0xac];
unsigned int gate_ip_gscl;
unsigned int gate_ip_disp0;
unsigned int gate_ip_disp1;
unsigned int gate_ip_mfc;
unsigned int gate_ip_g3d;
unsigned int gate_ip_gen;
unsigned char res79[0xc];
unsigned int gate_ip_fsys;
unsigned char res80[0x4];
unsigned int gate_ip_gps;
unsigned int gate_ip_peric;
unsigned char res81[0xc];
unsigned int gate_ip_peris;
unsigned char res82[0x1c];
unsigned int gate_block;
unsigned char res83[0x7c];
unsigned int clkout_cmu_top;
unsigned int clkout_cmu_top_div_stat;
unsigned char res84[0x37f8];
unsigned int src_lex; /* base + 0x14200 */
unsigned char res85[0x1fc];
unsigned int mux_stat_lex;
unsigned char res85b[0xfc];
unsigned int div_lex;
unsigned char res86[0xfc];
unsigned int div_stat_lex;
unsigned char res87[0x1fc];
unsigned int gate_ip_lex;
unsigned char res88[0x1fc];
unsigned int clkout_cmu_lex;
unsigned int clkout_cmu_lex_div_stat;
unsigned char res89[0x3af8];
unsigned int div_r0x; /* base + 0x18500 */
unsigned char res90[0xfc];
unsigned int div_stat_r0x;
unsigned char res91[0x1fc];
unsigned int gate_ip_r0x;
unsigned char res92[0x1fc];
unsigned int clkout_cmu_r0x;
unsigned int clkout_cmu_r0x_div_stat;
unsigned char res94[0x3af8];
unsigned int div_r1x; /* base + 0x1c500 */
unsigned char res95[0xfc];
unsigned int div_stat_r1x;
unsigned char res96[0x1fc];
unsigned int gate_ip_r1x;
unsigned char res97[0x1fc];
unsigned int clkout_cmu_r1x;
unsigned int clkout_cmu_r1x_div_stat;
unsigned char res98[0x3608];
unsigned int bpll_lock; /* base + 0x2000c */
unsigned char res99[0xfc];
unsigned int bpll_con0;
unsigned int bpll_con1;
unsigned char res100[0xe8];
unsigned int src_cdrex;
unsigned char res101[0x1fc];
unsigned int mux_stat_cdrex;
unsigned char res102[0xfc];
unsigned int div_cdrex;
unsigned int div_cdrex2;
unsigned char res103[0xf8];
unsigned int div_stat_cdrex;
unsigned char res104[0x2fc];
unsigned int gate_ip_cdrex;
unsigned char res105[0xc];
unsigned int c2c_monitor;
unsigned int dmc_pwr_ctrl;
unsigned char res106[0x4];
unsigned int drex2_pause;
unsigned char res107[0xe0];
unsigned int clkout_cmu_cdrex;
unsigned int clkout_cmu_cdrex_div_stat;
unsigned char res108[0x8];
unsigned int lpddr3phy_ctrl;
unsigned char res109a[0xc];
unsigned int lpddr3phy_con3;
unsigned int pll_div2_sel;
unsigned char res109b[0xf5e4];
};
struct exynos5_mct_regs {
uint32_t mct_cfg;
uint8_t reserved0[0xfc];
uint32_t g_cnt_l;
uint32_t g_cnt_u;
uint8_t reserved1[0x8];
uint32_t g_cnt_wstat;
uint8_t reserved2[0xec];
uint32_t g_comp0_l;
uint32_t g_comp0_u;
uint32_t g_comp0_addr_incr;
uint8_t reserved3[0x4];
uint32_t g_comp1_l;
uint32_t g_comp1_u;
uint32_t g_comp1_addr_incr;
uint8_t reserved4[0x4];
uint32_t g_comp2_l;
uint32_t g_comp2_u;
uint32_t g_comp2_addr_incr;
uint8_t reserved5[0x4];
uint32_t g_comp3_l;
uint32_t g_comp3_u;
uint32_t g_comp3_addr_incr;
uint8_t reserved6[0x4];
uint32_t g_tcon;
uint32_t g_int_cstat;
uint32_t g_int_enb;
uint32_t g_wstat;
uint8_t reserved7[0xb0];
uint32_t l0_tcntb;
uint32_t l0_tcnto;
uint32_t l0_icntb;
uint32_t l0_icnto;
uint32_t l0_frcntb;
uint32_t l0_frcnto;
uint8_t reserved8[0x8];
uint32_t l0_tcon;
uint8_t reserved9[0xc];
uint32_t l0_int_cstat;
uint32_t l0_int_enb;
uint8_t reserved10[0x8];
uint32_t l0_wstat;
uint8_t reserved11[0xbc];
uint32_t l1_tcntb;
uint32_t l1_tcnto;
uint32_t l1_icntb;
uint32_t l1_icnto;
uint32_t l1_frcntb;
uint32_t l1_frcnto;
uint8_t reserved12[0x8];
uint32_t l1_tcon;
uint8_t reserved13[0xc];
uint32_t l1_int_cstat;
uint32_t l1_int_enb;
uint8_t reserved14[0x8];
uint32_t l1_wstat;
};
#define EXYNOS5_EPLLCON0_LOCKED_SHIFT 29 /* EPLL Locked bit position*/
#define EPLL_SRC_CLOCK 24000000 /*24 MHz Cristal Input */
#define TIMEOUT_EPLL_LOCK 1000
#define AUDIO_0_RATIO_MASK 0x0f
#define AUDIO_1_RATIO_MASK 0x0f
#define CLK_SRC_PERIC1 0x254
#define AUDIO1_SEL_MASK 0xf
#define CLK_SRC_AUDIOCDCLK1 0x0
#define CLK_SRC_XXTI 0x1
#define CLK_SRC_SCLK_EPLL 0x7
/* CON0 bit-fields */
#define EPLL_CON0_MDIV_MASK 0x1ff
#define EPLL_CON0_PDIV_MASK 0x3f
#define EPLL_CON0_SDIV_MASK 0x7
#define EPLL_CON0_LOCKED_SHIFT 29
#define EPLL_CON0_MDIV_SHIFT 16
#define EPLL_CON0_PDIV_SHIFT 8
#define EPLL_CON0_SDIV_SHIFT 0
#define EPLL_CON0_LOCK_DET_EN_SHIFT 28
#define EPLL_CON0_LOCK_DET_EN_MASK 1
/* structure for epll configuration used in audio clock configuration */
struct st_epll_con_val {
unsigned int freq_out; /* frequency out */
unsigned int en_lock_det; /* enable lock detect */
unsigned int m_div; /* m divider value */
unsigned int p_div; /* p divider value */
unsigned int s_div; /* s divider value */
unsigned int k_dsm; /* k value of delta signal modulator */
};
/**
* Low-level function to set the clock pre-ratio for a peripheral
*
* @param periph_id Peripheral ID of peripheral to change
* @param divisor New divisor for this peripheral's clock
*/
void clock_ll_set_pre_ratio(enum periph_id periph_id, unsigned divisor);
/**
* Low-level function to set the clock ratio for a peripheral
*
* @param periph_id Peripheral ID of peripheral to change
* @param divisor New divisor for this peripheral's clock
*/
void clock_ll_set_ratio(enum periph_id periph_id, unsigned divisor);
/**
* Low-level function that selects the best clock scalars for a given rate and
* sets up the given peripheral's clock accordingly.
*
* @param periph_id Peripheral ID of peripheral to change
* @param rate Desired clock rate in Hz
*
* @return zero on success, negative on error
*/
int clock_set_rate(enum periph_id periph_id, unsigned int rate);
/* Clock gate unused IP */
void clock_gate(void);
void mct_start(void);
uint64_t mct_raw_value(void);
#include "dmc.h"
/* These are the ratio's for configuring ARM clock */
struct arm_clk_ratios {
unsigned int arm_freq_mhz; /* Frequency of ARM core in MHz */
unsigned int apll_mdiv;
unsigned int apll_pdiv;
unsigned int apll_sdiv;
unsigned int arm2_ratio;
unsigned int apll_ratio;
unsigned int pclk_dbg_ratio;
unsigned int atb_ratio;
unsigned int periph_ratio;
unsigned int acp_ratio;
unsigned int cpud_ratio;
unsigned int arm_ratio;
};
/**
* Get the clock ratios for CPU configuration
*
* @return pointer to the clock ratios that we should use
*/
struct arm_clk_ratios *get_arm_clk_ratios(void);
/*
* Initialize clock for the device
*/
struct mem_timings;
void system_clock_init(struct mem_timings *mem,
struct arm_clk_ratios *arm_clk_ratio);
#endif

View File

@ -0,0 +1,691 @@
/*
* This file is part of the coreboot project.
*
* Copyright (C) 2010 Samsung Electronics
*
* 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; version 2 of the License.
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <console/console.h>
#include <stdlib.h>
#include <assert.h>
#include <arch/io.h>
#include "timer.h"
#include "clk.h"
#include "cpu.h"
/* input clock of PLL: SMDK5420 has 24MHz input clock */
#define CONFIG_SYS_CLK_FREQ 24000000
static struct arm_clk_ratios arm_clk_ratios[] = {
{
.arm_freq_mhz = 600,
.apll_mdiv = 0xc8,
.apll_pdiv = 0x4,
.apll_sdiv = 0x1,
.arm2_ratio = 0x0,
.apll_ratio = 0x1,
.pclk_dbg_ratio = 0x1,
.atb_ratio = 0x2,
.periph_ratio = 0x7,
.acp_ratio = 0x7,
.cpud_ratio = 0x1,
.arm_ratio = 0x0,
}, {
.arm_freq_mhz = 800,
.apll_mdiv = 0x64,
.apll_pdiv = 0x3,
.apll_sdiv = 0x0,
.arm2_ratio = 0x0,
.apll_ratio = 0x1,
.pclk_dbg_ratio = 0x1,
.atb_ratio = 0x3,
.periph_ratio = 0x7,
.acp_ratio = 0x7,
.cpud_ratio = 0x2,
.arm_ratio = 0x0,
}, {
.arm_freq_mhz = 1000,
.apll_mdiv = 0x7d,
.apll_pdiv = 0x3,
.apll_sdiv = 0x0,
.arm2_ratio = 0x0,
.apll_ratio = 0x1,
.pclk_dbg_ratio = 0x1,
.atb_ratio = 0x4,
.periph_ratio = 0x7,
.acp_ratio = 0x7,
.cpud_ratio = 0x2,
.arm_ratio = 0x0,
}, {
.arm_freq_mhz = 1200,
.apll_mdiv = 0x96,
.apll_pdiv = 0x3,
.apll_sdiv = 0x0,
.arm2_ratio = 0x0,
.apll_ratio = 0x3,
.pclk_dbg_ratio = 0x1,
.atb_ratio = 0x5,
.periph_ratio = 0x7,
.acp_ratio = 0x7,
.cpud_ratio = 0x3,
.arm_ratio = 0x0,
}, {
.arm_freq_mhz = 1400,
.apll_mdiv = 0xaf,
.apll_pdiv = 0x3,
.apll_sdiv = 0x0,
.arm2_ratio = 0x0,
.apll_ratio = 0x3,
.pclk_dbg_ratio = 0x1,
.atb_ratio = 0x6,
.periph_ratio = 0x7,
.acp_ratio = 0x7,
.cpud_ratio = 0x3,
.arm_ratio = 0x0,
}, {
.arm_freq_mhz = 1700,
.apll_mdiv = 0x1a9,
.apll_pdiv = 0x6,
.apll_sdiv = 0x0,
.arm2_ratio = 0x0,
.apll_ratio = 0x3,
.pclk_dbg_ratio = 0x1,
.atb_ratio = 0x6,
.periph_ratio = 0x7,
.acp_ratio = 0x7,
.cpud_ratio = 0x3,
.arm_ratio = 0x0,
}
};
/* src_bit div_bit prediv_bit */
static struct clk_bit_info clk_bit_info[PERIPH_ID_COUNT] = {
{0, 4, 0, -1},
{4, 4, 4, -1},
{8, 4, 8, -1},
{12, 4, 12, -1},
{0, 4, 0, 8},
{4, 4, 16, 24},
{8, 4, 0, 8},
{12, 4, 16, 24},
{-1, -1, -1, -1},
{16, 4, 0, 8}, /* PERIPH_ID_SROMC */
{20, 4, 16, 24},
{24, 4, 0, 8},
{0, 4, 0, 4},
{4, 4, 12, 16},
{-1, 4, -1, -1},
{-1, 4, -1, -1},
{-1, 4, 24, 0},
{-1, 4, 24, 0},
{-1, 4, 24, 0},
{-1, 4, 24, 0},
{-1, 4, 24, 0},
{-1, 4, 24, 0},
{-1, 4, 24, 0},
{-1, 4, 24, 0},
{24, 4, 0, -1},
{24, 4, 0, -1},
{24, 4, 0, -1},
{24, 4, 0, -1},
{24, 4, 0, -1},
{-1, -1, -1, -1},
{-1, -1, -1, -1},
{-1, -1, -1, -1}, /* PERIPH_ID_I2S1 */
{24, 1, 20, -1}, /* PERIPH_ID_SATA */
};
/* Epll Clock division values to achive different frequency output */
static struct st_epll_con_val 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 }
};
/* exynos5: return pll clock frequency */
unsigned long get_pll_clk(int pllreg)
{
struct exynos5_clock *clk =
samsung_get_base_clock();
unsigned long r, m, p, s, k = 0, mask, fout;
unsigned int freq;
switch (pllreg) {
case APLL:
r = readl(&clk->apll_con0);
break;
case BPLL:
r = readl(&clk->bpll_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:
printk(BIOS_DEBUG, "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]
*/
if (pllreg == APLL || pllreg == BPLL || pllreg == MPLL)
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 {
/* FOUT = MDIV * FIN / (PDIV * 2^SDIV) */
fout = m * (freq / (p * (1 << s)));
}
return fout;
}
unsigned long clock_get_periph_rate(enum periph_id peripheral)
{
struct exynos5_clock *clk =
samsung_get_base_clock();
struct clk_bit_info *bit_info = &clk_bit_info[peripheral];
unsigned long sclk, sub_clk;
unsigned int src, div, sub_div;
switch (peripheral) {
case PERIPH_ID_UART0:
case PERIPH_ID_UART1:
case PERIPH_ID_UART2:
case PERIPH_ID_UART3:
src = readl(&clk->src_peric0);
div = readl(&clk->div_peric0);
break;
case PERIPH_ID_PWM0:
case PERIPH_ID_PWM1:
case PERIPH_ID_PWM2:
case PERIPH_ID_PWM3:
case PERIPH_ID_PWM4:
src = readl(&clk->src_peric0);
div = readl(&clk->div_peric3);
break;
case PERIPH_ID_SPI0:
case PERIPH_ID_SPI1:
src = readl(&clk->src_peric1);
div = readl(&clk->div_peric1);
break;
case PERIPH_ID_SPI2:
src = readl(&clk->src_peric1);
div = readl(&clk->div_peric2);
break;
case PERIPH_ID_SPI3:
case PERIPH_ID_SPI4:
src = readl(&clk->sclk_src_isp);
div = readl(&clk->sclk_div_isp);
break;
case PERIPH_ID_SATA:
src = readl(&clk->src_fsys);
div = readl(&clk->div_fsys0);
break;
case PERIPH_ID_SDMMC0:
case PERIPH_ID_SDMMC1:
case PERIPH_ID_SDMMC2:
case PERIPH_ID_SDMMC3:
src = readl(&clk->src_fsys);
div = readl(&clk->div_fsys1);
break;
case PERIPH_ID_I2C0:
case PERIPH_ID_I2C1:
case PERIPH_ID_I2C2:
case PERIPH_ID_I2C3:
case PERIPH_ID_I2C4:
case PERIPH_ID_I2C5:
case PERIPH_ID_I2C6:
case PERIPH_ID_I2C7:
sclk = get_pll_clk(MPLL);
sub_div = ((readl(&clk->div_top1) >> bit_info->div_bit) & 0x7) + 1;
div = ((readl(&clk->div_top0) >> bit_info->prediv_bit) & 0x7) + 1;
return (sclk / sub_div) / div;
default:
printk(BIOS_DEBUG, "%s: invalid peripheral %d", __func__, peripheral);
return -1;
};
src = (src >> bit_info->src_bit) & ((1 << bit_info->n_src_bits) - 1);
if (peripheral == PERIPH_ID_SATA) {
if (src)
sclk = get_pll_clk(BPLL);
else
sclk = get_pll_clk(MPLL);
} else {
if (src == SRC_MPLL)
sclk = get_pll_clk(MPLL);
else if (src == SRC_EPLL)
sclk = get_pll_clk(EPLL);
else if (src == SRC_VPLL)
sclk = get_pll_clk(VPLL);
else
return 0;
}
sub_div = (div >> bit_info->div_bit) & 0xf;
sub_clk = sclk / (sub_div + 1);
if (peripheral == PERIPH_ID_SDMMC0 || peripheral == PERIPH_ID_SDMMC2) {
div = (div >> bit_info->prediv_bit) & 0xff;
return sub_clk / (div + 1);
}
return sub_clk;
}
/* exynos5: return ARM clock frequency */
unsigned long get_arm_clk(void)
{
struct exynos5_clock *clk =
samsung_get_base_clock();
unsigned long div;
unsigned long armclk;
unsigned int arm_ratio;
unsigned int arm2_ratio;
div = readl(&clk->div_cpu0);
/* ARM_RATIO: [2:0], ARM2_RATIO: [30:28] */
arm_ratio = (div >> 0) & 0x7;
arm2_ratio = (div >> 28) & 0x7;
armclk = get_pll_clk(APLL) / (arm_ratio + 1);
armclk /= (arm2_ratio + 1);
return armclk;
}
struct arm_clk_ratios *get_arm_clk_ratios(void)
{
struct arm_clk_ratios *arm_ratio;
unsigned long arm_freq = 1700; /* FIXME: use get_arm_clk() */
int i;
for (i = 0, arm_ratio = arm_clk_ratios; i < ARRAY_SIZE(arm_clk_ratios);
i++, arm_ratio++) {
if (arm_ratio->arm_freq_mhz == arm_freq)
return arm_ratio;
}
return NULL;
}
/* exynos5: set the mmc clock */
void set_mmc_clk(int dev_index, unsigned int div)
{
struct exynos5_clock *clk =
samsung_get_base_clock();
unsigned int *addr;
unsigned int val;
/*
* CLK_DIV_FSYS1
* MMC0_PRE_RATIO [15:8], MMC1_PRE_RATIO [31:24]
* CLK_DIV_FSYS2
* MMC2_PRE_RATIO [15:8], MMC3_PRE_RATIO [31:24]
*/
if (dev_index < 2) {
addr = &clk->div_fsys1;
} else {
addr = &clk->div_fsys2;
dev_index -= 2;
}
val = readl(addr);
val &= ~(0xff << ((dev_index << 4) + 8));
val |= (div & 0xff) << ((dev_index << 4) + 8);
writel(val, addr);
}
void clock_ll_set_pre_ratio(enum periph_id periph_id, unsigned divisor)
{
struct exynos5_clock *clk =
samsung_get_base_clock();
unsigned shift;
unsigned mask = 0xff;
u32 *reg;
/*
* For now we only handle a very small subset of peipherals here.
* Others will need to (and do) mangle the clock registers
* themselves, At some point it is hoped that this function can work
* from a table or calculated register offset / mask. For now this
* is at least better than spreading clock control code around
* U-Boot.
*/
switch (periph_id) {
case PERIPH_ID_SPI0:
reg = &clk->div_peric1;
shift = 8;
break;
case PERIPH_ID_SPI1:
reg = &clk->div_peric1;
shift = 24;
break;
case PERIPH_ID_SPI2:
reg = &clk->div_peric2;
shift = 8;
break;
case PERIPH_ID_SPI3:
reg = &clk->sclk_div_isp;
shift = 4;
break;
case PERIPH_ID_SPI4:
reg = &clk->sclk_div_isp;
shift = 16;
break;
default:
printk(BIOS_DEBUG, "%s: Unsupported peripheral ID %d\n", __func__,
periph_id);
return;
}
clrsetbits_le32(reg, mask << shift, (divisor & mask) << shift);
}
void clock_ll_set_ratio(enum periph_id periph_id, unsigned divisor)
{
struct exynos5_clock *clk =
samsung_get_base_clock();
unsigned shift;
unsigned mask = 0xff;
u32 *reg;
switch (periph_id) {
case PERIPH_ID_SPI0:
reg = &clk->div_peric1;
shift = 0;
break;
case PERIPH_ID_SPI1:
reg = &clk->div_peric1;
shift = 16;
break;
case PERIPH_ID_SPI2:
reg = &clk->div_peric2;
shift = 0;
break;
case PERIPH_ID_SPI3:
reg = &clk->sclk_div_isp;
shift = 0;
break;
case PERIPH_ID_SPI4:
reg = &clk->sclk_div_isp;
shift = 12;
break;
default:
printk(BIOS_DEBUG, "%s: Unsupported peripheral ID %d\n", __func__,
periph_id);
return;
}
clrsetbits_le32(reg, mask << shift, (divisor & mask) << shift);
}
/**
* 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;
printk(BIOS_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;
printk(BIOS_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;
}
int clock_set_rate(enum periph_id periph_id, unsigned int rate)
{
int main;
unsigned int fine;
switch (periph_id) {
case PERIPH_ID_SPI0:
case PERIPH_ID_SPI1:
case PERIPH_ID_SPI2:
case PERIPH_ID_SPI3:
case PERIPH_ID_SPI4:
main = clock_calc_best_scalar(4, 8, 400000000, rate, &fine);
if (main < 0) {
printk(BIOS_DEBUG, "%s: Cannot set clock rate for periph %d",
__func__, periph_id);
return -1;
}
clock_ll_set_ratio(periph_id, main - 1);
clock_ll_set_pre_ratio(periph_id, fine - 1);
break;
default:
printk(BIOS_DEBUG, "%s: Unsupported peripheral ID %d\n", __func__,
periph_id);
return -1;
}
return 0;
}
int clock_set_mshci(enum periph_id peripheral)
{
struct exynos5_clock *clk =
samsung_get_base_clock();
u32 *addr;
unsigned int clock;
unsigned int tmp;
unsigned int i;
/* get mpll clock */
clock = get_pll_clk(MPLL) / 1000000;
/*
* CLK_DIV_FSYS1
* MMC0_PRE_RATIO [15:8], MMC0_RATIO [3:0]
* CLK_DIV_FSYS2
* MMC2_PRE_RATIO [15:8], MMC2_RATIO [3:0]
*/
switch (peripheral) {
case PERIPH_ID_SDMMC0:
addr = &clk->div_fsys1;
break;
case PERIPH_ID_SDMMC2:
addr = &clk->div_fsys2;
break;
default:
printk(BIOS_DEBUG, "invalid peripheral\n");
return -1;
}
tmp = readl(addr) & ~0xff0f;
for (i = 0; i <= 0xf; i++) {
if ((clock / (i + 1)) <= 400) {
writel(tmp | i << 0, addr);
break;
}
}
return 0;
}
int clock_epll_set_rate(unsigned long rate)
{
unsigned int epll_con, epll_con_k;
unsigned int i;
unsigned int lockcnt;
unsigned int start;
struct exynos5_clock *clk =
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(epll_div); i++) {
if (epll_div[i].freq_out == rate)
break;
}
if (i == ARRAY_SIZE(epll_div))
return -1;
epll_con_k = epll_div[i].k_dsm << 0;
epll_con |= epll_div[i].en_lock_det << EPLL_CON0_LOCK_DET_EN_SHIFT;
epll_con |= epll_div[i].m_div << EPLL_CON0_MDIV_SHIFT;
epll_con |= epll_div[i].p_div << EPLL_CON0_PDIV_SHIFT;
epll_con |= 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 * 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) {
printk(BIOS_DEBUG, "%s: Timeout waiting for EPLL lock\n", __func__);
return -1;
}
}
return 0;
}
void clock_select_i2s_clk_source(void)
{
struct exynos5_clock *clk =
samsung_get_base_clock();
clrsetbits_le32(&clk->src_peric1, AUDIO1_SEL_MASK,
(CLK_SRC_SCLK_EPLL));
}
int clock_set_i2s_clk_prescaler(unsigned int src_frq, unsigned int dst_frq)
{
struct exynos5_clock *clk =
samsung_get_base_clock();
unsigned int div ;
if ((dst_frq == 0) || (src_frq == 0)) {
printk(BIOS_DEBUG, "%s: Invalid requency input for prescaler\n", __func__);
printk(BIOS_DEBUG, "src frq = %d des frq = %d ", src_frq, dst_frq);
return -1;
}
div = (src_frq / dst_frq);
if (div > AUDIO_1_RATIO_MASK) {
printk(BIOS_DEBUG, "%s: Frequency ratio is out of range\n", __func__);
printk(BIOS_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;
}

View File

@ -0,0 +1,445 @@
/*
* This file is part of the coreboot project.
*
* Copyright (C) 2012 Samsung Electronics
*
* 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; version 2 of the License.
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/* Clock setup for SMDK5420 board based on EXYNOS5 */
#include <console/console.h>
#include <delay.h>
#include "clk.h"
#include "cpu.h"
#include "dp.h"
#include "setup.h"
void system_clock_init(struct mem_timings *mem,
struct arm_clk_ratios *arm_clk_ratio)
{
struct exynos5_clock *clk = (struct exynos5_clock *)EXYNOS5_CLOCK_BASE;
struct exynos5_mct_regs *mct_regs =
(struct exynos5_mct_regs *)EXYNOS5_MULTI_CORE_TIMER_BASE;
u32 val, tmp;
/* Turn on the MCT as early as possible. */
mct_regs->g_tcon |= (1 << 8);
clrbits_le32(&clk->src_cpu, MUX_APLL_SEL_MASK);
do {
val = readl(&clk->mux_stat_cpu);
} while ((val | MUX_APLL_SEL_MASK) != val);
clrbits_le32(&clk->src_core1, MUX_MPLL_SEL_MASK);
do {
val = readl(&clk->mux_stat_core1);
} while ((val | MUX_MPLL_SEL_MASK) != val);
clrbits_le32(&clk->src_top2, MUX_CPLL_SEL_MASK);
clrbits_le32(&clk->src_top2, MUX_EPLL_SEL_MASK);
clrbits_le32(&clk->src_top2, MUX_VPLL_SEL_MASK);
clrbits_le32(&clk->src_top2, MUX_GPLL_SEL_MASK);
tmp = MUX_CPLL_SEL_MASK | MUX_EPLL_SEL_MASK | MUX_VPLL_SEL_MASK
| MUX_GPLL_SEL_MASK;
do {
val = readl(&clk->mux_stat_top2);
} while ((val | tmp) != val);
clrbits_le32(&clk->src_cdrex, MUX_BPLL_SEL_MASK);
do {
val = readl(&clk->mux_stat_cdrex);
} while ((val | MUX_BPLL_SEL_MASK) != val);
/* PLL locktime */
writel(APLL_LOCK_VAL, &clk->apll_lock);
writel(MPLL_LOCK_VAL, &clk->mpll_lock);
writel(BPLL_LOCK_VAL, &clk->bpll_lock);
writel(CPLL_LOCK_VAL, &clk->cpll_lock);
writel(GPLL_LOCK_VAL, &clk->gpll_lock);
writel(EPLL_LOCK_VAL, &clk->epll_lock);
writel(VPLL_LOCK_VAL, &clk->vpll_lock);
writel(CLK_REG_DISABLE, &clk->pll_div2_sel);
writel(MUX_HPM_SEL_MASK, &clk->src_cpu);
do {
val = readl(&clk->mux_stat_cpu);
} while ((val | HPM_SEL_SCLK_MPLL) != val);
val = arm_clk_ratio->arm2_ratio << 28
| arm_clk_ratio->apll_ratio << 24
| arm_clk_ratio->pclk_dbg_ratio << 20
| arm_clk_ratio->atb_ratio << 16
| arm_clk_ratio->periph_ratio << 12
| arm_clk_ratio->acp_ratio << 8
| arm_clk_ratio->cpud_ratio << 4
| arm_clk_ratio->arm_ratio;
writel(val, &clk->div_cpu0);
do {
val = readl(&clk->div_stat_cpu0);
} while (0 != val);
writel(CLK_DIV_CPU1_VAL, &clk->div_cpu1);
do {
val = readl(&clk->div_stat_cpu1);
} while (0 != val);
/* Set APLL */
writel(APLL_CON1_VAL, &clk->apll_con1);
val = set_pll(arm_clk_ratio->apll_mdiv, arm_clk_ratio->apll_pdiv,
arm_clk_ratio->apll_sdiv);
writel(val, &clk->apll_con0);
while ((readl(&clk->apll_con0) & APLL_CON0_LOCKED) == 0)
;
/* Set MPLL */
writel(MPLL_CON1_VAL, &clk->mpll_con1);
val = set_pll(mem->mpll_mdiv, mem->mpll_pdiv, mem->mpll_sdiv);
writel(val, &clk->mpll_con0);
while ((readl(&clk->mpll_con0) & MPLL_CON0_LOCKED) == 0)
;
/*
* Configure MUX_MPLL_FOUT to choose the direct clock source
* path and avoid the fixed DIV/2 block to save power
*/
setbits_le32(&clk->pll_div2_sel, MUX_MPLL_FOUT_SEL);
/* Set BPLL */
if (mem->use_bpll) {
writel(BPLL_CON1_VAL, &clk->bpll_con1);
val = set_pll(mem->bpll_mdiv, mem->bpll_pdiv, mem->bpll_sdiv);
writel(val, &clk->bpll_con0);
while ((readl(&clk->bpll_con0) & BPLL_CON0_LOCKED) == 0)
;
setbits_le32(&clk->pll_div2_sel, MUX_BPLL_FOUT_SEL);
}
/* Set CPLL */
writel(CPLL_CON1_VAL, &clk->cpll_con1);
val = set_pll(mem->cpll_mdiv, mem->cpll_pdiv, mem->cpll_sdiv);
writel(val, &clk->cpll_con0);
while ((readl(&clk->cpll_con0) & CPLL_CON0_LOCKED) == 0)
;
/* Set GPLL */
writel(GPLL_CON1_VAL, &clk->gpll_con1);
val = set_pll(mem->gpll_mdiv, mem->gpll_pdiv, mem->gpll_sdiv);
writel(val, &clk->gpll_con0);
while ((readl(&clk->gpll_con0) & GPLL_CON0_LOCKED) == 0)
;
/* Set EPLL */
writel(EPLL_CON2_VAL, &clk->epll_con2);
writel(EPLL_CON1_VAL, &clk->epll_con1);
val = set_pll(mem->epll_mdiv, mem->epll_pdiv, mem->epll_sdiv);
writel(val, &clk->epll_con0);
while ((readl(&clk->epll_con0) & EPLL_CON0_LOCKED) == 0)
;
/* Set VPLL */
writel(VPLL_CON2_VAL, &clk->vpll_con2);
writel(VPLL_CON1_VAL, &clk->vpll_con1);
val = set_pll(mem->vpll_mdiv, mem->vpll_pdiv, mem->vpll_sdiv);
writel(val, &clk->vpll_con0);
while ((readl(&clk->vpll_con0) & VPLL_CON0_LOCKED) == 0)
;
writel(CLK_SRC_CORE0_VAL, &clk->src_core0);
writel(CLK_DIV_CORE0_VAL, &clk->div_core0);
while (readl(&clk->div_stat_core0) != 0)
;
writel(CLK_DIV_CORE1_VAL, &clk->div_core1);
while (readl(&clk->div_stat_core1) != 0)
;
writel(CLK_DIV_SYSRGT_VAL, &clk->div_sysrgt);
while (readl(&clk->div_stat_sysrgt) != 0)
;
writel(CLK_DIV_ACP_VAL, &clk->div_acp);
while (readl(&clk->div_stat_acp) != 0)
;
writel(CLK_DIV_SYSLFT_VAL, &clk->div_syslft);
while (readl(&clk->div_stat_syslft) != 0)
;
writel(CLK_SRC_TOP0_VAL, &clk->src_top0);
writel(CLK_SRC_TOP1_VAL, &clk->src_top1);
writel(TOP2_VAL, &clk->src_top2);
writel(CLK_SRC_TOP3_VAL, &clk->src_top3);
writel(CLK_DIV_TOP0_VAL, &clk->div_top0);
while (readl(&clk->div_stat_top0))
;
writel(CLK_DIV_TOP1_VAL, &clk->div_top1);
while (readl(&clk->div_stat_top1))
;
writel(CLK_SRC_LEX_VAL, &clk->src_lex);
while (1) {
val = readl(&clk->mux_stat_lex);
if (val == (val | 1))
break;
}
writel(CLK_DIV_LEX_VAL, &clk->div_lex);
while (readl(&clk->div_stat_lex))
;
writel(CLK_DIV_R0X_VAL, &clk->div_r0x);
while (readl(&clk->div_stat_r0x))
;
writel(CLK_DIV_R0X_VAL, &clk->div_r0x);
while (readl(&clk->div_stat_r0x))
;
writel(CLK_DIV_R1X_VAL, &clk->div_r1x);
while (readl(&clk->div_stat_r1x))
;
if (mem->use_bpll) {
writel(MUX_BPLL_SEL_MASK | MUX_MCLK_CDREX_SEL |
MUX_MCLK_DPHY_SEL, &clk->src_cdrex);
} else {
writel(CLK_REG_DISABLE, &clk->src_cdrex);
}
writel(CLK_DIV_CDREX_VAL, &clk->div_cdrex);
while (readl(&clk->div_stat_cdrex))
;
val = readl(&clk->src_cpu);
val |= CLK_SRC_CPU_VAL;
writel(val, &clk->src_cpu);
val = readl(&clk->src_top2);
val |= CLK_SRC_TOP2_VAL;
writel(val, &clk->src_top2);
val = readl(&clk->src_core1);
val |= CLK_SRC_CORE1_VAL;
writel(val, &clk->src_core1);
writel(CLK_SRC_FSYS0_VAL, &clk->src_fsys);
writel(CLK_DIV_FSYS0_VAL, &clk->div_fsys0);
while (readl(&clk->div_stat_fsys0))
;
writel(CLK_REG_DISABLE, &clk->clkout_cmu_cpu);
writel(CLK_REG_DISABLE, &clk->clkout_cmu_core);
writel(CLK_REG_DISABLE, &clk->clkout_cmu_acp);
writel(CLK_REG_DISABLE, &clk->clkout_cmu_top);
writel(CLK_REG_DISABLE, &clk->clkout_cmu_lex);
writel(CLK_REG_DISABLE, &clk->clkout_cmu_r0x);
writel(CLK_REG_DISABLE, &clk->clkout_cmu_r1x);
writel(CLK_REG_DISABLE, &clk->clkout_cmu_cdrex);
writel(CLK_SRC_PERIC0_VAL, &clk->src_peric0);
writel(CLK_DIV_PERIC0_VAL, &clk->div_peric0);
writel(CLK_SRC_PERIC1_VAL, &clk->src_peric1);
writel(CLK_DIV_PERIC1_VAL, &clk->div_peric1);
writel(CLK_DIV_PERIC2_VAL, &clk->div_peric2);
writel(SCLK_SRC_ISP_VAL, &clk->sclk_src_isp);
writel(SCLK_DIV_ISP_VAL, &clk->sclk_div_isp);
writel(CLK_DIV_ISP0_VAL, &clk->div_isp0);
writel(CLK_DIV_ISP1_VAL, &clk->div_isp1);
writel(CLK_DIV_ISP2_VAL, &clk->div_isp2);
/* FIMD1 SRC CLK SELECTION */
writel(CLK_SRC_DISP1_0_VAL, &clk->src_disp1_0);
val = MMC2_PRE_RATIO_VAL << MMC2_PRE_RATIO_OFFSET
| MMC2_RATIO_VAL << MMC2_RATIO_OFFSET
| MMC3_PRE_RATIO_VAL << MMC3_PRE_RATIO_OFFSET
| MMC3_RATIO_VAL << MMC3_RATIO_OFFSET;
writel(val, &clk->div_fsys2);
}
void clock_gate(void)
{
struct exynos5_clock *clk = (struct exynos5_clock *)EXYNOS5_CLOCK_BASE;
/* CLK_GATE_IP_SYSRGT */
clrbits_le32(&clk->gate_ip_sysrgt, CLK_C2C_MASK);
/* CLK_GATE_IP_ACP */
clrbits_le32(&clk->gate_ip_acp, CLK_SMMUG2D_MASK |
CLK_SMMUSSS_MASK |
CLK_SMMUMDMA_MASK |
CLK_ID_REMAPPER_MASK |
CLK_G2D_MASK |
CLK_SSS_MASK |
CLK_MDMA_MASK |
CLK_SECJTAG_MASK);
/* CLK_GATE_BUS_SYSLFT */
clrbits_le32(&clk->gate_bus_syslft, CLK_EFCLK_MASK);
/* CLK_GATE_IP_ISP0 */
clrbits_le32(&clk->gate_ip_isp0, CLK_UART_ISP_MASK |
CLK_WDT_ISP_MASK |
CLK_PWM_ISP_MASK |
CLK_MTCADC_ISP_MASK |
CLK_I2C1_ISP_MASK |
CLK_I2C0_ISP_MASK |
CLK_MPWM_ISP_MASK |
CLK_MCUCTL_ISP_MASK |
CLK_INT_COMB_ISP_MASK |
CLK_SMMU_MCUISP_MASK |
CLK_SMMU_SCALERP_MASK |
CLK_SMMU_SCALERC_MASK |
CLK_SMMU_FD_MASK |
CLK_SMMU_DRC_MASK |
CLK_SMMU_ISP_MASK |
CLK_GICISP_MASK |
CLK_ARM9S_MASK |
CLK_MCUISP_MASK |
CLK_SCALERP_MASK |
CLK_SCALERC_MASK |
CLK_FD_MASK |
CLK_DRC_MASK |
CLK_ISP_MASK);
/* CLK_GATE_IP_ISP1 */
clrbits_le32(&clk->gate_ip_isp1, CLK_SPI1_ISP_MASK |
CLK_SPI0_ISP_MASK |
CLK_SMMU3DNR_MASK |
CLK_SMMUDIS1_MASK |
CLK_SMMUDIS0_MASK |
CLK_SMMUODC_MASK |
CLK_3DNR_MASK |
CLK_DIS_MASK |
CLK_ODC_MASK);
/* CLK_GATE_SCLK_ISP */
clrbits_le32(&clk->gate_sclk_isp, SCLK_MPWM_ISP_MASK);
/* CLK_GATE_IP_GSCL */
clrbits_le32(&clk->gate_ip_gscl, CLK_SMMUFIMC_LITE2_MASK |
CLK_SMMUFIMC_LITE1_MASK |
CLK_SMMUFIMC_LITE0_MASK |
CLK_SMMUGSCL3_MASK |
CLK_SMMUGSCL2_MASK |
CLK_SMMUGSCL1_MASK |
CLK_SMMUGSCL0_MASK |
CLK_GSCL_WRAP_B_MASK |
CLK_GSCL_WRAP_A_MASK |
CLK_CAMIF_TOP_MASK |
CLK_GSCL3_MASK |
CLK_GSCL2_MASK |
CLK_GSCL1_MASK |
CLK_GSCL0_MASK);
/* CLK_GATE_IP_DISP1 */
clrbits_le32(&clk->gate_ip_disp1, CLK_SMMUTVX_MASK |
CLK_ASYNCTVX_MASK |
CLK_HDMI_MASK |
CLK_MIXER_MASK |
CLK_DSIM1_MASK);
/* CLK_GATE_IP_MFC */
clrbits_le32(&clk->gate_ip_mfc, CLK_SMMUMFCR_MASK |
CLK_SMMUMFCL_MASK |
CLK_MFC_MASK);
/* CLK_GATE_IP_GEN */
clrbits_le32(&clk->gate_ip_gen, CLK_SMMUMDMA1_MASK |
CLK_SMMUJPEG_MASK |
CLK_SMMUROTATOR_MASK |
CLK_MDMA1_MASK |
CLK_JPEG_MASK |
CLK_ROTATOR_MASK);
/* CLK_GATE_IP_FSYS */
clrbits_le32(&clk->gate_ip_fsys, CLK_WDT_IOP_MASK |
CLK_SMMUMCU_IOP_MASK |
CLK_SATA_PHY_I2C_MASK |
CLK_SATA_PHY_CTRL_MASK |
CLK_MCUCTL_MASK |
CLK_NFCON_MASK |
CLK_SMMURTIC_MASK |
CLK_RTIC_MASK |
CLK_MIPI_HSI_MASK |
CLK_USBOTG_MASK |
CLK_SATA_MASK |
CLK_PDMA1_MASK |
CLK_PDMA0_MASK |
CLK_MCU_IOP_MASK);
/* CLK_GATE_IP_PERIC */
clrbits_le32(&clk->gate_ip_peric, CLK_HS_I2C3_MASK |
CLK_HS_I2C2_MASK |
CLK_HS_I2C1_MASK |
CLK_HS_I2C0_MASK |
CLK_AC97_MASK |
CLK_SPDIF_MASK |
CLK_PCM2_MASK |
CLK_PCM1_MASK |
CLK_I2S2_MASK |
CLK_SPI2_MASK |
CLK_SPI0_MASK);
/*
* CLK_GATE_IP_PERIS
* Note: Keep CHIPID_APBIF ungated to ensure reading the product ID
* register (PRO_ID) works correctly when the OS kernel determines
* which chip it is running on.
*/
clrbits_le32(&clk->gate_ip_peris, CLK_RTC_MASK |
CLK_TZPC9_MASK |
CLK_TZPC8_MASK |
CLK_TZPC7_MASK |
CLK_TZPC6_MASK |
CLK_TZPC5_MASK |
CLK_TZPC4_MASK |
CLK_TZPC3_MASK |
CLK_TZPC2_MASK |
CLK_TZPC1_MASK |
CLK_TZPC0_MASK);
/* CLK_GATE_BLOCK */
clrbits_le32(&clk->gate_block, CLK_ACP_MASK);
/* CLK_GATE_IP_CDREX */
clrbits_le32(&clk->gate_ip_cdrex, CLK_DPHY0_MASK |
CLK_DPHY1_MASK |
CLK_TZASC_DRBXR_MASK);
}
void clock_init_dp_clock(void)
{
struct exynos5_clock *clk = (struct exynos5_clock *)EXYNOS5_CLOCK_BASE;
/* DP clock enable */
setbits_le32(&clk->gate_ip_disp1, CLK_GATE_DP1_ALLOW);
/* We run DP at 267 Mhz */
setbits_le32(&clk->div_disp1_0, CLK_DIV_DISP1_0_FIMD1);
}

View File

@ -0,0 +1,181 @@
/*
* This file is part of the coreboot project.
*
* Copyright 2013 Google Inc.
*
* 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; version 2 of the License.
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
#include <delay.h>
#include <console/console.h>
#include <device/device.h>
#include <cbmem.h>
#include <arch/cache.h>
#include "fimd.h"
#include "dp-core.h"
#include "cpu.h"
#include "clk.h"
#include "chip.h"
#define RAM_BASE_KB (CONFIG_SYS_SDRAM_BASE >> 10)
#define RAM_SIZE_KB (CONFIG_DRAM_SIZE_MB << 10UL)
static unsigned int cpu_id;
static unsigned int cpu_rev;
static void set_cpu_id(void)
{
cpu_id = readl((void *)EXYNOS_PRO_ID);
cpu_id = (0xC000 | ((cpu_id & 0x00FFF000) >> 12));
/*
* 0xC200: EXYNOS4210 EVT0
* 0xC210: EXYNOS4210 EVT1
*/
if (cpu_id == 0xC200) {
cpu_id |= 0x10;
cpu_rev = 0;
} else if (cpu_id == 0xC210) {
cpu_rev = 1;
}
}
/* we distinguish a display port device from a raw graphics device
* because there are dramatic differences in startup depending on
* graphics usage. To make startup fast and easier to understand and
* debug we explicitly name this common case. The alternate approach,
* involving lots of machine and callbacks, is hard to debug and
* verify.
*/
static void exynos_displayport_init(device_t dev)
{
int ret;
struct cpu_samsung_exynos5420_config *conf = dev->chip_info;
/* put these on the stack. If, at some point, we want to move
* this code to a pre-ram stage, it will be much easier.
*/
vidinfo_t vi;
struct exynos5_fimd_panel panel;
unsigned long int fb_size;
u32 lcdbase;
memset(&vi, 0, sizeof(vi));
memset(&panel, 0, sizeof(panel));
panel.is_dp = 1; /* Display I/F is eDP */
/* while it is true that we did a memset to zero,
* we leave some 'set to zero' entries here to make
* it clear what's going on. Graphics is confusing.
*/
panel.is_mipi = 0;
panel.fixvclk = 0;
panel.ivclk = 0;
panel.clkval_f = conf->clkval_f;
panel.upper_margin = conf->upper_margin;
panel.lower_margin = conf->lower_margin;
panel.vsync = conf->vsync;
panel.left_margin = conf->left_margin;
panel.right_margin = conf->right_margin;
panel.hsync = conf->hsync;
panel.xres = conf->xres;
panel.yres = conf->yres;
vi.vl_col = conf->xres;
vi.vl_row = conf->yres;
vi.vl_bpix = conf->bpp;
/*
* The size is a magic number from hardware. Allocate enough for the
* frame buffer and color map.
*/
fb_size = conf->xres * conf->yres * (conf->bpp / 8);
lcdbase = (uintptr_t)cbmem_add(CBMEM_ID_CONSOLE, fb_size + 64*KiB);
printk(BIOS_SPEW, "LCD colormap base is %p\n", (void *)(lcdbase));
mmio_resource(dev, 0, lcdbase/KiB, 64);
vi.cmap = (void *)lcdbase;
/*
* We need to clean and invalidate the framebuffer region and disable
* caching as well. We assume that our dcache <--> memory address
* space is identity-mapped in 1MB chunks, so align accordingly.
*
* Note: We may want to do something clever to ensure the framebuffer
* region is aligned such that we don't change dcache policy for other
* stuff inadvertantly.
*
* FIXME: Is disabling/re-enabling the MMU entirely necessary?
*/
uint32_t lower = ALIGN_DOWN(lcdbase, MiB);
uint32_t upper = ALIGN_UP(lcdbase + fb_size + 64*KiB, MiB);
dcache_clean_invalidate_by_mva(lower, upper - lower);
dcache_mmu_disable();
mmu_config_range(lower/MiB, (upper - lower)/MiB, DCACHE_OFF);
dcache_mmu_enable();
lcdbase += 64*KiB;
mmio_resource(dev, 1, lcdbase/KiB, (fb_size + KiB - 1)/KiB);
printk(BIOS_DEBUG,
"Initializing Exynos VGA, base %p\n", (void *)lcdbase);
memset((void *)lcdbase, 0, fb_size); /* clear the framebuffer */
ret = lcd_ctrl_init(&vi, &panel, (void *)lcdbase);
}
static void cpu_init(device_t dev)
{
exynos_displayport_init(dev);
ram_resource(dev, 0, RAM_BASE_KB, RAM_SIZE_KB);
set_cpu_id();
printk(BIOS_INFO, "CPU: S5P%X @ %ldMHz\n",
cpu_id, get_arm_clk() / (1024*1024));
}
static void cpu_noop(device_t dev)
{
}
static struct device_operations cpu_ops = {
.read_resources = cpu_noop,
.set_resources = cpu_noop,
.enable_resources = cpu_init,
.init = cpu_noop,
.scan_bus = 0,
};
static void enable_exynos5420_dev(device_t dev)
{
dev->ops = &cpu_ops;
}
struct chip_operations cpu_samsung_exynos5420_ops = {
CHIP_NAME("CPU Samsung Exynos 5420")
.enable_dev = enable_exynos5420_dev,
};
void exynos5420_config_l2_cache(void)
{
uint32_t val;
/*
* Bit 9 - L2 tag RAM setup (1 cycle)
* Bits 8:6 - L2 tag RAM latency (3 cycles)
* Bit 5 - L2 data RAM setup (1 cycle)
* Bits 2:0 - L2 data RAM latency (3 cycles)
*/
val = (1 << 9) | (0x2 << 6) | (1 << 5) | (0x2);
write_l2ctlr(val);
}

View File

@ -0,0 +1,130 @@
/*
* This file is part of the coreboot project.
*
* Copyright (C) 2010 Samsung Electronics
*
* 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; version 2 of the License.
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef CPU_SAMSUNG_EXYNOS5420_CPU_H
#define CPU_SAMSUNG_EXYNOS5420_CPU_H
#include <arch/io.h>
#define DEVICE_NOT_AVAILABLE 0
#define EXYNOS_PRO_ID 0x10000000
/* Address of address of function that copys data from SD or MMC */
#define EXYNOS_COPY_MMC_FNPTR_ADDR 0x02020030
/* Address of address of function that copys data from SPI */
#define EXYNOS_COPY_SPI_FNPTR_ADDR 0x02020058
/* Address of address of function that copys data through USB */
#define EXYNOS_COPY_USB_FNPTR_ADDR 0x02020070
/* Boot mode values */
#define EXYNOS_USB_SECONDARY_BOOT 0xfeed0002
#define EXYNOS_IRAM_SECONDARY_BASE 0x02020018
#define EXYNOS_I2C_SPACING 0x10000
/* EXYNOS5 */
#define EXYNOS5_GPIO_PART6_BASE 0x03860000 /* Z<6:0> */
#define EXYNOS5_PRO_ID 0x10000000
#define EXYNOS5_CLOCK_BASE 0x10010000
#define EXYNOS5_POWER_BASE 0x10040000
#define EXYNOS5_SWRESET 0x10040400
#define EXYNOS5_SYSREG_BASE 0x10050000
#define EXYNOS5_TZPC1_DECPROT1SET 0x10110810
#define EXYNOS5_MULTI_CORE_TIMER_BASE 0x101C0000
#define EXYNOS5_WATCHDOG_BASE 0x101D0000
#define EXYNOS5_ACE_SFR_BASE 0x10830000
#define EXYNOS5_DMC_PHY0_BASE 0x10C00000
#define EXYNOS5_DMC_PHY1_BASE 0x10C10000
#define EXYNOS5_GPIO_PART4_BASE 0x10D10000 /* V00..V37 */
#define EXYNOS5_GPIO_PART5_BASE 0x10D100C0 /* V40..V47 */
#define EXYNOS5_DMC_CTRL_BASE 0x10DD0000
#define EXYNOS5_GPIO_PART1_BASE 0x11400000 /* A00..Y67 */
#define EXYNOS5_GPIO_PART2_BASE 0x11400c00 /* X00..X37 */
#define EXYNOS5_USB_HOST_EHCI_BASE 0x12110000
#define EXYNOS5_USBPHY_BASE 0x12130000
#define EXYNOS5_USBOTG_BASE 0x12140000
#define EXYNOS5_MMC_BASE 0x12200000
#define EXYNOS5_MSHC_BASE 0x12240000
#define EXYNOS5_SROMC_BASE 0x12250000
#define EXYNOS5_UART_BASE 0x12C00000
#define EXYNOS5_SPI1_BASE 0x12D30000
#define EXYNOS5_I2C_BASE 0x12C60000
#define EXYNOS5_SPI_BASE 0x12D20000
#define EXYNOS5_PWMTIMER_BASE 0x12DD0000
#define EXYNOS5_SPI_ISP_BASE 0x131A0000
#define EXYNOS5_I2S_BASE 0x12D60000
#define EXYNOS5_GPIO_PART3_BASE 0x13400000 /* E00..H17 */
#define EXYNOS5_FIMD_BASE 0x14400000
#define EXYNOS5_DISP1_CTRL_BASE 0x14420000
#define EXYNOS5_MIPI_DSI1_BASE 0x14500000
#define EXYNOS5_ADC_BASE DEVICE_NOT_AVAILABLE
#define EXYNOS5_MODEM_BASE DEVICE_NOT_AVAILABLE
/* Compatibility defines */
#define EXYNOS_POWER_BASE EXYNOS5_POWER_BASE
/* Marker values stored at the bottom of IRAM stack by SPL */
#define EXYNOS5_SPL_MARKER 0xb004f1a9 /* hexspeak word: bootflag */
/* Distance between each Trust Zone PC register set */
#define TZPC_BASE_OFFSET 0x10000
#define samsung_get_base_adc() ((struct exynos5_adc *)EXYNOS5_ADC_BASE)
#define samsung_get_base_clock() ((struct exynos5_clock *)EXYNOS5_CLOCK_BASE)
#define samsung_get_base_ace_sfr() ((struct exynos5_ace_sfr *)EXYNOS5_ACE_SFR_BASE)
#define samsung_get_base_dsim() ((struct exynos5_dsim *)EXYNOS5_MIPI_DSI1_BASE)
#define samsung_get_base_disp_ctrl() ((struct exynos5_disp_ctrl *)EXYNOS5_DISP1_CTRL_BASE)
#define samsung_get_base_fimd() ((struct exynos5_fimd *)EXYNOS5_FIMD_BASE)
#define samsung_get_base_pro_id() ((struct exynos5_pro_id *)EXYNOS5_PRO_ID)
#define samsung_get_base_mmc() ((struct exynos5_mmc *)EXYNOS5_MMC_BASE)
#define samsung_get_base_mshci() ((struct exynos5_mshci *)EXYNOS5_MSHC_BASE)
#define samsung_get_base_modem() ((struct exynos5_modem *)EXYNOS5_MODEM_BASE)
#define samsung_get_base_sromc() ((struct exynos5_sromc *)EXYNOS5_SROMC_BASE)
#define samsung_get_base_swreset() ((struct exynos5_swreset *)EXYNOS5_SWRESET)
#define samsung_get_base_sysreg() ((struct exynos5_sysreg *)EXYNOS5_SYSREG_BASE)
#define samsung_get_base_timer() ((struct s5p_timer *)EXYNOS5_PWMTIMER_BASE)
#define samsung_get_base_uart() ((struct exynos5_uart *)EXYNOS5_UART_BASE)
#define samsung_get_base_usb_phy() ((struct exynos5_usb_phy *)EXYNOS5_USBPHY_BASE)
#define samsung_get_base_usb_otg() ((struct exynos5_usb_otg *)EXYNOS5_USBOTG_BASE)
#define samsung_get_base_watchdog() ((struct exynos5_watchdog *)EXYNOS5_WATCHDOG_BASE)
#define samsung_get_base_power() ((struct exynos5_power *)EXYNOS5_POWER_BASE)
#define samsung_get_base_i2s() ((struct exynos5_i2s *)EXYNOS5_I2S_BASE)
#define samsung_get_base_spi1() ((struct exynos5_spi1 *)EXYNOS5_SPI1_BASE)
#define samsung_get_base_i2c() ((struct exynos5_i2c *)EXYNOS5_I2C_BASE)
#define samsung_get_base_spi() ((struct exynos5_spi *)EXYNOS5_SPI_BASE)
#define samsung_get_base_spi_isp() ((struct exynos5_spi_isp *)EXYNOS5_SPI_ISP_BASE)
#define EXYNOS5_SPI_NUM_CONTROLLERS 5
#define EXYNOS_I2C_MAX_CONTROLLERS 8
void exynos5420_config_l2_cache(void);
extern struct tmu_info exynos5420_tmu_info;
#endif /* _EXYNOS5420_CPU_H */

View File

@ -0,0 +1,343 @@
/*
* This file is part of the coreboot 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; version 2 of the License.
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef CPU_SAMSUNG_EXYNOS5420_DMC_H
#define CPU_SAMSUNG_EXYNOS5420_DMC_H
#ifndef __ASSEMBLER__
struct exynos5_dmc {
unsigned int concontrol;
unsigned int memcontrol;
unsigned int memconfig0;
unsigned int memconfig1;
unsigned int directcmd;
unsigned int prechconfig;
unsigned int phycontrol0;
unsigned char res1[0xc];
unsigned int pwrdnconfig;
unsigned int timingpzq;
unsigned int timingref;
unsigned int timingrow;
unsigned int timingdata;
unsigned int timingpower;
unsigned int phystatus;
unsigned char res2[0x4];
unsigned int chipstatus_ch0;
unsigned int chipstatus_ch1;
unsigned char res3[0x4];
unsigned int mrstatus;
unsigned char res4[0x8];
unsigned int qoscontrol0;
unsigned char resr5[0x4];
unsigned int qoscontrol1;
unsigned char res6[0x4];
unsigned int qoscontrol2;
unsigned char res7[0x4];
unsigned int qoscontrol3;
unsigned char res8[0x4];
unsigned int qoscontrol4;
unsigned char res9[0x4];
unsigned int qoscontrol5;
unsigned char res10[0x4];
unsigned int qoscontrol6;
unsigned char res11[0x4];
unsigned int qoscontrol7;
unsigned char res12[0x4];
unsigned int qoscontrol8;
unsigned char res13[0x4];
unsigned int qoscontrol9;
unsigned char res14[0x4];
unsigned int qoscontrol10;
unsigned char res15[0x4];
unsigned int qoscontrol11;
unsigned char res16[0x4];
unsigned int qoscontrol12;
unsigned char res17[0x4];
unsigned int qoscontrol13;
unsigned char res18[0x4];
unsigned int qoscontrol14;
unsigned char res19[0x4];
unsigned int qoscontrol15;
unsigned char res20[0x14];
unsigned int ivcontrol;
unsigned int wrtra_config;
unsigned int rdlvl_config;
unsigned char res21[0x8];
unsigned int brbrsvconfig;
unsigned int brbqosconfig;
unsigned int membaseconfig0;
unsigned int membaseconfig1;
unsigned char res22[0xc];
unsigned int wrlvl_config;
unsigned char res23[0xc];
unsigned int perevcontrol;
unsigned int perev0config;
unsigned int perev1config;
unsigned int perev2config;
unsigned int perev3config;
unsigned char res24[0xdebc];
unsigned int pmnc_ppc_a;
unsigned char res25[0xc];
unsigned int cntens_ppc_a;
unsigned char res26[0xc];
unsigned int cntenc_ppc_a;
unsigned char res27[0xc];
unsigned int intens_ppc_a;
unsigned char res28[0xc];
unsigned int intenc_ppc_a;
unsigned char res29[0xc];
unsigned int flag_ppc_a;
unsigned char res30[0xac];
unsigned int ccnt_ppc_a;
unsigned char res31[0xc];
unsigned int pmcnt0_ppc_a;
unsigned char res32[0xc];
unsigned int pmcnt1_ppc_a;
unsigned char res33[0xc];
unsigned int pmcnt2_ppc_a;
unsigned char res34[0xc];
unsigned int pmcnt3_ppc_a;
};
struct exynos5_phy_control {
unsigned int phy_con0;
unsigned int phy_con1;
unsigned int phy_con2;
unsigned int phy_con3;
unsigned int phy_con4;
unsigned char res1[4];
unsigned int phy_con6;
unsigned char res2[4];
unsigned int phy_con8;
unsigned int phy_con9;
unsigned int phy_con10;
unsigned char res3[4];
unsigned int phy_con12;
unsigned int phy_con13;
unsigned int phy_con14;
unsigned int phy_con15;
unsigned int phy_con16;
unsigned char res4[4];
unsigned int phy_con17;
unsigned int phy_con18;
unsigned int phy_con19;
unsigned int phy_con20;
unsigned int phy_con21;
unsigned int phy_con22;
unsigned int phy_con23;
unsigned int phy_con24;
unsigned int phy_con25;
unsigned int phy_con26;
unsigned int phy_con27;
unsigned int phy_con28;
unsigned int phy_con29;
unsigned int phy_con30;
unsigned int phy_con31;
unsigned int phy_con32;
unsigned int phy_con33;
unsigned int phy_con34;
unsigned int phy_con35;
unsigned int phy_con36;
unsigned int phy_con37;
unsigned int phy_con38;
unsigned int phy_con39;
unsigned int phy_con40;
unsigned int phy_con41;
unsigned int phy_con42;
};
enum ddr_mode {
DDR_MODE_DDR2,
DDR_MODE_DDR3,
DDR_MODE_LPDDR2,
DDR_MODE_LPDDR3,
DDR_MODE_COUNT,
};
/* For reasons unknown, people are in the habit of taking a 32-bit
* field with 2 possible values and packing it with, say, 2 bits. A
* non-robust encoding, using only 2 bits of a 32-bit field, is
* incredibly difficult to deal with when things go wrong, because
* there are a lot of things that get expressed as 0, 1, or 2. If
* you're scanning with jtag or dumping memory it is really hard to
* tell when you've hit the beginning of the struct. So, let's be a
* bit smart here. First, while it's common to let the enum count
* entries for you, when there are two of them, we can do the
* counting. And, let's set the values to something we can easily scan
* for in memory. Since '1' and '2' are rather common, we pick
* something that's actually of some value when things go wrong. This
* setup motivated by a use case: something's going wrong and having a
* manuf name of '1' or '2' is completely useless!
*/
enum mem_manuf {
MEM_MANUF_AUTODETECT,
MEM_MANUF_ELPIDA = 0xe7b1da,
MEM_MANUF_SAMSUNG = 0x5a5096,
MEM_MANUF_COUNT = 2, // fancy that.
};
enum {
MEM_TIMINGS_MSR_COUNT = 4,
};
#define DMC_INTERLEAVE_SIZE 0x1f
/* CONCONTROL register fields */
#define CONCONTROL_DFI_INIT_START_SHIFT 28
#define CONCONTROL_RD_FETCH_SHIFT 12
#define CONCONTROL_RD_FETCH_MASK (0x7 << CONCONTROL_RD_FETCH_SHIFT)
#define CONCONTROL_AREF_EN_SHIFT 5
/* PRECHCONFIG register field */
#define PRECHCONFIG_TP_CNT_SHIFT 24
/* PWRDNCONFIG register field */
#define PWRDNCONFIG_DPWRDN_CYC_SHIFT 0
#define PWRDNCONFIG_DSREF_CYC_SHIFT 16
/* PHY_CON0 register fields */
#define PHY_CON0_T_WRRDCMD_SHIFT 17
#define PHY_CON0_T_WRRDCMD_MASK (0x7 << PHY_CON0_T_WRRDCMD_SHIFT)
#define PHY_CON0_CTRL_DDR_MODE_SHIFT 11
/* PHY_CON1 register fields */
#define PHY_CON1_RDLVL_RDDATA_ADJ_SHIFT 0
/* PHY_CON12 register fields */
#define PHY_CON12_CTRL_START_POINT_SHIFT 24
#define PHY_CON12_CTRL_INC_SHIFT 16
#define PHY_CON12_CTRL_FORCE_SHIFT 8
#define PHY_CON12_CTRL_START_SHIFT 6
#define PHY_CON12_CTRL_START_MASK (1 << PHY_CON12_CTRL_START_SHIFT)
#define PHY_CON12_CTRL_DLL_ON_SHIFT 5
#define PHY_CON12_CTRL_DLL_ON_MASK (1 << PHY_CON12_CTRL_DLL_ON_SHIFT)
#define PHY_CON12_CTRL_REF_SHIFT 1
/* PHY_CON16 register fields */
#define PHY_CON16_ZQ_MODE_DDS_SHIFT 24
#define PHY_CON16_ZQ_MODE_DDS_MASK (0x7 << PHY_CON16_ZQ_MODE_DDS_SHIFT)
#define PHY_CON16_ZQ_MODE_TERM_SHIFT 21
#define PHY_CON16_ZQ_MODE_TERM_MASK (0x7 << PHY_CON16_ZQ_MODE_TERM_SHIFT)
#define PHY_CON16_ZQ_MODE_NOTERM_MASK (1 << 19)
/* PHY_CON42 register fields */
#define PHY_CON42_CTRL_BSTLEN_SHIFT 8
#define PHY_CON42_CTRL_BSTLEN_MASK (0xff << PHY_CON42_CTRL_BSTLEN_SHIFT)
#define PHY_CON42_CTRL_RDLAT_SHIFT 0
#define PHY_CON42_CTRL_RDLAT_MASK (0x1f << PHY_CON42_CTRL_RDLAT_SHIFT)
/* These are the memory timings for a particular memory type and speed */
struct mem_timings {
enum mem_manuf mem_manuf; /* Memory manufacturer */
enum ddr_mode mem_type; /* Memory type */
unsigned int frequency_mhz; /* Frequency of memory in MHz */
/* Here follow the timing parameters for the selected memory */
uint8_t apll_mdiv;
uint8_t apll_pdiv;
uint8_t apll_sdiv;
uint8_t mpll_mdiv;
uint8_t mpll_pdiv;
uint8_t mpll_sdiv;
uint8_t cpll_mdiv;
uint8_t cpll_pdiv;
uint8_t cpll_sdiv;
uint8_t gpll_pdiv;
uint16_t gpll_mdiv;
uint8_t gpll_sdiv;
uint8_t epll_mdiv;
uint8_t epll_pdiv;
uint8_t epll_sdiv;
uint8_t vpll_mdiv;
uint8_t vpll_pdiv;
uint8_t vpll_sdiv;
uint8_t bpll_mdiv;
uint8_t bpll_pdiv;
uint8_t bpll_sdiv;
uint8_t use_bpll; /* 1 to use BPLL for cdrex, 0 to use MPLL */
uint8_t pclk_cdrex_ratio;
unsigned int direct_cmd_msr[MEM_TIMINGS_MSR_COUNT];
unsigned int timing_ref;
unsigned int timing_row;
unsigned int timing_data;
unsigned int timing_power;
/* DQS, DQ, DEBUG offsets */
unsigned int phy0_dqs;
unsigned int phy1_dqs;
unsigned int phy0_dq;
unsigned int phy1_dq;
uint8_t phy0_tFS;
uint8_t phy1_tFS;
uint8_t phy0_pulld_dqs;
uint8_t phy1_pulld_dqs;
uint8_t lpddr3_ctrl_phy_reset;
uint8_t ctrl_start_point;
uint8_t ctrl_inc;
uint8_t ctrl_start;
uint8_t ctrl_dll_on;
uint8_t ctrl_ref;
uint8_t ctrl_force;
uint8_t ctrl_rdlat;
uint8_t ctrl_bstlen;
uint8_t fp_resync;
uint8_t iv_size;
uint8_t dfi_init_start;
uint8_t aref_en;
uint8_t rd_fetch;
uint8_t zq_mode_dds;
uint8_t zq_mode_term;
uint8_t zq_mode_noterm; /* 1 to allow termination disable */
unsigned int memcontrol;
unsigned int memconfig;
unsigned int membaseconfig0;
unsigned int membaseconfig1;
unsigned int prechconfig_tp_cnt;
unsigned int dpwrdn_cyc;
unsigned int dsref_cyc;
unsigned int concontrol;
/* Channel and Chip Selection */
uint8_t dmc_channels; /* number of memory channels */
uint8_t chips_per_channel; /* number of chips per channel */
uint8_t chips_to_configure; /* number of chips to configure */
uint8_t send_zq_init; /* 1 to send this command */
unsigned int impedance; /* drive strength impedeance */
uint8_t gate_leveling_enable; /* check gate leveling is enabled */
};
/**
* Get the correct memory timings for our selected memory type and speed.
*
* @return pointer to the memory timings that we should use
*/
struct mem_timings *get_mem_timings(void);
#endif
#endif

View File

@ -0,0 +1,183 @@
/*
* This file is part of the coreboot project.
*
* Copyright (C) 2012 Samsung Electronics
*
* 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; version 2 of the License.
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/* Mem setup common file for different types of DDR present on SMDK5420 boards.
*/
#include <console/console.h>
#include <arch/io.h>
#include <delay.h>
#include "setup.h"
#include "dmc.h"
#include "clk.h"
#include "setup.h"
#define ZQ_INIT_TIMEOUT 10000
int dmc_config_zq(struct mem_timings *mem,
struct exynos5_phy_control *phy0_ctrl,
struct exynos5_phy_control *phy1_ctrl)
{
unsigned long val = 0;
int i;
/*
* ZQ Calibration:
* Select Driver Strength,
* long calibration for manual calibration
*/
val = PHY_CON16_RESET_VAL;
val |= mem->zq_mode_dds << PHY_CON16_ZQ_MODE_DDS_SHIFT;
val |= mem->zq_mode_term << PHY_CON16_ZQ_MODE_TERM_SHIFT;
val |= ZQ_CLK_DIV_EN;
writel(val, &phy0_ctrl->phy_con16);
writel(val, &phy1_ctrl->phy_con16);
/* Disable termination */
if (mem->zq_mode_noterm)
val |= PHY_CON16_ZQ_MODE_NOTERM_MASK;
writel(val, &phy0_ctrl->phy_con16);
writel(val, &phy1_ctrl->phy_con16);
/* ZQ_MANUAL_START: Enable */
val |= ZQ_MANUAL_STR;
writel(val, &phy0_ctrl->phy_con16);
writel(val, &phy1_ctrl->phy_con16);
/* ZQ_MANUAL_START: Disable */
val &= ~ZQ_MANUAL_STR;
/*
* Since we are manaully calibrating the ZQ values,
* we are looping for the ZQ_init to complete.
*/
i = ZQ_INIT_TIMEOUT;
while ((readl(&phy0_ctrl->phy_con17) & ZQ_DONE) != ZQ_DONE && i > 0) {
udelay(1);
i--;
}
if (!i)
return -1;
writel(val, &phy0_ctrl->phy_con16);
i = ZQ_INIT_TIMEOUT;
while ((readl(&phy1_ctrl->phy_con17) & ZQ_DONE) != ZQ_DONE && i > 0) {
udelay(1);
i--;
}
if (!i)
return -1;
writel(val, &phy1_ctrl->phy_con16);
return 0;
}
void update_reset_dll(struct exynos5_dmc *dmc, enum ddr_mode mode)
{
unsigned long val;
if (mode == DDR_MODE_DDR3) {
val = MEM_TERM_EN | PHY_TERM_EN | DMC_CTRL_SHGATE;
writel(val, &dmc->phycontrol0);
}
/* Update DLL Information: Force DLL Resyncronization */
val = readl(&dmc->phycontrol0);
val |= FP_RSYNC;
writel(val, &dmc->phycontrol0);
/* Reset Force DLL Resyncronization */
val = readl(&dmc->phycontrol0);
val &= ~FP_RSYNC;
writel(val, &dmc->phycontrol0);
}
void dmc_config_mrs(struct mem_timings *mem, struct exynos5_dmc *dmc)
{
int channel, chip;
for (channel = 0; channel < mem->dmc_channels; channel++) {
unsigned long mask;
mask = channel << DIRECT_CMD_CHANNEL_SHIFT;
for (chip = 0; chip < mem->chips_to_configure; chip++) {
int i;
mask |= chip << DIRECT_CMD_CHIP_SHIFT;
/* Sending NOP command */
writel(DIRECT_CMD_NOP | mask, &dmc->directcmd);
/*
* TODO(alim.akhtar@samsung.com): Do we need these
* delays? This one and the next were not there for
* DDR3.
*/
udelay(100);
/* Sending EMRS/MRS commands */
for (i = 0; i < MEM_TIMINGS_MSR_COUNT; i++) {
writel(mem->direct_cmd_msr[i] | mask,
&dmc->directcmd);
udelay(100);
}
if (mem->send_zq_init) {
/* Sending ZQINIT command */
writel(DIRECT_CMD_ZQINIT | mask,
&dmc->directcmd);
/*
* FIXME: This was originally sdelay(10000)
* in the imported u-boot code. That may have
* been meant to be sdelay(0x10000) since that
* was used elsewhere in this function. Either
* way seems to work, though.
*/
udelay(12);
}
}
}
}
void dmc_config_prech(struct mem_timings *mem, struct exynos5_dmc *dmc)
{
int channel, chip;
for (channel = 0; channel < mem->dmc_channels; channel++) {
unsigned long mask;
mask = channel << DIRECT_CMD_CHANNEL_SHIFT;
for (chip = 0; chip < mem->chips_per_channel; chip++) {
mask |= chip << DIRECT_CMD_CHIP_SHIFT;
/* PALL (all banks precharge) CMD */
writel(DIRECT_CMD_PALL | mask, &dmc->directcmd);
udelay(100);
}
}
}
void dmc_config_memory(struct mem_timings *mem, struct exynos5_dmc *dmc)
{
writel(mem->memconfig, &dmc->memconfig0);
writel(mem->memconfig, &dmc->memconfig1);
writel(DMC_MEMBASECONFIG0_VAL, &dmc->membaseconfig0);
writel(DMC_MEMBASECONFIG1_VAL, &dmc->membaseconfig1);
}

View File

@ -0,0 +1,277 @@
/*
* This file is part of the coreboot project.
*
* Copyright (C) 2012 Samsung Electronics
*
* 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; version 2 of the License.
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/* DDR3 mem setup file for SMDK5420 board based on EXYNOS5 */
#include <console/console.h>
#include <delay.h>
#include <arch/io.h>
#include "clk.h"
#include "cpu.h"
#include "dmc.h"
#include "setup.h"
#define RDLVL_COMPLETE_TIMEOUT 10000
static void reset_phy_ctrl(void)
{
struct exynos5_clock *clk = (struct exynos5_clock *)EXYNOS5_CLOCK_BASE;
writel(LPDDR3PHY_CTRL_PHY_RESET_OFF, &clk->lpddr3phy_ctrl);
writel(LPDDR3PHY_CTRL_PHY_RESET, &clk->lpddr3phy_ctrl);
#if 0
/*
* For proper memory initialization there should be a minimum delay of
* 500us after the LPDDR3PHY_CTRL_PHY_RESET signal.
* The below value is an approximate value whose calculation in done
* considering that sdelay takes 2 instruction for every 1 delay cycle.
* And assuming each instruction takes 1 clock cycle i.e 1/(1.7 Ghz)sec
* So for 500 usec, the number of delay cycle should be
* (500 * 10^-6) * (1.7 * 10^9) / 2 = 425000
*
* TODO(hatim.rv@samsung.com): Implement the delay using timer/counter
*/
sdelay(425000);
#endif
udelay(500);
}
int ddr3_mem_ctrl_init(struct mem_timings *mem, unsigned long mem_iv_size,
int mem_reset)
{
unsigned int val;
struct exynos5_phy_control *phy0_ctrl, *phy1_ctrl;
struct exynos5_dmc *dmc;
int i;
phy0_ctrl = (struct exynos5_phy_control *)EXYNOS5_DMC_PHY0_BASE;
phy1_ctrl = (struct exynos5_phy_control *)EXYNOS5_DMC_PHY1_BASE;
dmc = (struct exynos5_dmc *)EXYNOS5_DMC_CTRL_BASE;
if (mem_reset) {
printk(BIOS_SPEW, "%s: reset phy: ", __func__);
reset_phy_ctrl();
printk(BIOS_SPEW, "done\n");
} else {
printk(BIOS_SPEW, "%s: skip mem_reset.\n", __func__);
}
/* Set Impedance Output Driver */
printk(BIOS_SPEW, "ddr3_mem_ctrl_init: Set Impedance Output Driver\n");
printk(BIOS_SPEW, "ddr3_mem_ctrl_init: mem->impedance 0x%x\n",
mem->impedance);
val = (mem->impedance << CA_CK_DRVR_DS_OFFSET) |
(mem->impedance << CA_CKE_DRVR_DS_OFFSET) |
(mem->impedance << CA_CS_DRVR_DS_OFFSET) |
(mem->impedance << CA_ADR_DRVR_DS_OFFSET);
printk(BIOS_SPEW, "ddr3_mem_ctrl_init: val 0x%x\n", val);
writel(val, &phy0_ctrl->phy_con39);
writel(val, &phy1_ctrl->phy_con39);
/* Set Read Latency and Burst Length for PHY0 and PHY1 */
printk(BIOS_SPEW, "ddr3_mem_ctrl_init: "
"Set Read Latency and Burst Length for PHY0 and PHY1\n");
val = (mem->ctrl_bstlen << PHY_CON42_CTRL_BSTLEN_SHIFT) |
(mem->ctrl_rdlat << PHY_CON42_CTRL_RDLAT_SHIFT);
writel(val, &phy0_ctrl->phy_con42);
writel(val, &phy1_ctrl->phy_con42);
/* ZQ Calibration */
printk(BIOS_SPEW, "ddr3_mem_ctrl_init: ZQ Calibration\n");
if (dmc_config_zq(mem, phy0_ctrl, phy1_ctrl))
return SETUP_ERR_ZQ_CALIBRATION_FAILURE;
/* DQ Signal */
printk(BIOS_SPEW, "ddr3_mem_ctrl_init: DQ Signal\n");
writel(mem->phy0_pulld_dqs, &phy0_ctrl->phy_con14);
writel(mem->phy1_pulld_dqs, &phy1_ctrl->phy_con14);
writel(mem->concontrol | (mem->rd_fetch << CONCONTROL_RD_FETCH_SHIFT)
| (mem->dfi_init_start << CONCONTROL_DFI_INIT_START_SHIFT),
&dmc->concontrol);
update_reset_dll(dmc, DDR_MODE_DDR3);
/* DQS Signal */
printk(BIOS_SPEW, "ddr3_mem_ctrl_init: DQS Signal\n");
writel(mem->phy0_dqs, &phy0_ctrl->phy_con4);
writel(mem->phy1_dqs, &phy1_ctrl->phy_con4);
writel(mem->phy0_dq, &phy0_ctrl->phy_con6);
writel(mem->phy1_dq, &phy1_ctrl->phy_con6);
writel(mem->phy0_tFS, &phy0_ctrl->phy_con10);
writel(mem->phy1_tFS, &phy1_ctrl->phy_con10);
val = (mem->ctrl_start_point << PHY_CON12_CTRL_START_POINT_SHIFT) |
(mem->ctrl_inc << PHY_CON12_CTRL_INC_SHIFT) |
(mem->ctrl_dll_on << PHY_CON12_CTRL_DLL_ON_SHIFT) |
(mem->ctrl_ref << PHY_CON12_CTRL_REF_SHIFT);
writel(val, &phy0_ctrl->phy_con12);
writel(val, &phy1_ctrl->phy_con12);
/* Start DLL locking */
printk(BIOS_SPEW, "ddr3_mem_ctrl_init: Start DLL Locking\n");
writel(val | (mem->ctrl_start << PHY_CON12_CTRL_START_SHIFT),
&phy0_ctrl->phy_con12);
writel(val | (mem->ctrl_start << PHY_CON12_CTRL_START_SHIFT),
&phy1_ctrl->phy_con12);
update_reset_dll(dmc, DDR_MODE_DDR3);
writel(mem->concontrol | (mem->rd_fetch << CONCONTROL_RD_FETCH_SHIFT),
&dmc->concontrol);
/* Memory Channel Inteleaving Size */
printk(BIOS_SPEW, "ddr3_mem_ctrl_init: "
"Memory Channel Inteleaving Size\n");
writel(mem->iv_size, &dmc->ivcontrol);
/* Set DMC MEMCONTROL register */
printk(BIOS_SPEW, "ddr3_mem_ctrl_init: Set DMC MEMCONTROL register\n");
val = mem->memcontrol & ~DMC_MEMCONTROL_DSREF_ENABLE;
writel(val, &dmc->memcontrol);
writel(mem->memconfig, &dmc->memconfig0);
writel(mem->memconfig, &dmc->memconfig1);
writel(mem->membaseconfig0, &dmc->membaseconfig0);
writel(mem->membaseconfig1, &dmc->membaseconfig1);
/* Precharge Configuration */
printk(BIOS_SPEW, "ddr3_mem_ctrl_init: Precharge Configuration\n");
writel(mem->prechconfig_tp_cnt << PRECHCONFIG_TP_CNT_SHIFT,
&dmc->prechconfig);
/* Power Down mode Configuration */
printk(BIOS_SPEW, "ddr3_mem_ctrl_init: "
"Power Down mode Configuraation\n");
writel(mem->dpwrdn_cyc << PWRDNCONFIG_DPWRDN_CYC_SHIFT |
mem->dsref_cyc << PWRDNCONFIG_DSREF_CYC_SHIFT,
&dmc->pwrdnconfig);
/* TimingRow, TimingData, TimingPower and Timingaref
* values as per Memory AC parameters
*/
printk(BIOS_SPEW, "ddr3_mem_ctrl_init: "
"TimingRow, TimingData, TimingPower and Timingaref\n");
writel(mem->timing_ref, &dmc->timingref);
writel(mem->timing_row, &dmc->timingrow);
writel(mem->timing_data, &dmc->timingdata);
writel(mem->timing_power, &dmc->timingpower);
/* Send PALL command */
printk(BIOS_SPEW, "ddr3_mem_ctrl_init: Send PALL Command\n");
dmc_config_prech(mem, dmc);
/* Send NOP, MRS and ZQINIT commands */
printk(BIOS_SPEW, "ddr3_mem_ctrl_init: Send NOP, MRS, and ZQINIT\n");
dmc_config_mrs(mem, dmc);
if (mem->gate_leveling_enable) {
val = PHY_CON0_RESET_VAL;
val |= P0_CMD_EN;
writel(val, &phy0_ctrl->phy_con0);
writel(val, &phy1_ctrl->phy_con0);
val = PHY_CON2_RESET_VAL;
val |= INIT_DESKEW_EN;
writel(val, &phy0_ctrl->phy_con2);
writel(val, &phy1_ctrl->phy_con2);
val = PHY_CON0_RESET_VAL;
val |= P0_CMD_EN;
val |= BYTE_RDLVL_EN;
writel(val, &phy0_ctrl->phy_con0);
writel(val, &phy1_ctrl->phy_con0);
val = (mem->ctrl_start_point <<
PHY_CON12_CTRL_START_POINT_SHIFT) |
(mem->ctrl_inc << PHY_CON12_CTRL_INC_SHIFT) |
(mem->ctrl_force << PHY_CON12_CTRL_FORCE_SHIFT) |
(mem->ctrl_start << PHY_CON12_CTRL_START_SHIFT) |
(mem->ctrl_ref << PHY_CON12_CTRL_REF_SHIFT);
writel(val, &phy0_ctrl->phy_con12);
writel(val, &phy1_ctrl->phy_con12);
val = PHY_CON2_RESET_VAL;
val |= INIT_DESKEW_EN;
val |= RDLVL_GATE_EN;
writel(val, &phy0_ctrl->phy_con2);
writel(val, &phy1_ctrl->phy_con2);
val = PHY_CON0_RESET_VAL;
val |= P0_CMD_EN;
val |= BYTE_RDLVL_EN;
val |= CTRL_SHGATE;
writel(val, &phy0_ctrl->phy_con0);
writel(val, &phy1_ctrl->phy_con0);
val = PHY_CON1_RESET_VAL;
val &= ~(CTRL_GATEDURADJ_MASK);
writel(val, &phy0_ctrl->phy_con1);
writel(val, &phy1_ctrl->phy_con1);
writel(CTRL_RDLVL_GATE_ENABLE, &dmc->rdlvl_config);
i = RDLVL_COMPLETE_TIMEOUT;
while ((readl(&dmc->phystatus) &
(RDLVL_COMPLETE_CHO | RDLVL_COMPLETE_CH1)) !=
(RDLVL_COMPLETE_CHO | RDLVL_COMPLETE_CH1) && i > 0) {
/*
* TODO(waihong): Comment on how long this take to
* timeout
*/
udelay(1);
i--;
}
if (!i){
printk(BIOS_SPEW, "Timeout on RDLVL. No DRAM.\n");
return SETUP_ERR_RDLV_COMPLETE_TIMEOUT;
}
writel(CTRL_RDLVL_GATE_DISABLE, &dmc->rdlvl_config);
writel(0, &phy0_ctrl->phy_con14);
writel(0, &phy1_ctrl->phy_con14);
val = (mem->ctrl_start_point <<
PHY_CON12_CTRL_START_POINT_SHIFT) |
(mem->ctrl_inc << PHY_CON12_CTRL_INC_SHIFT) |
(mem->ctrl_force << PHY_CON12_CTRL_FORCE_SHIFT) |
(mem->ctrl_start << PHY_CON12_CTRL_START_SHIFT) |
(mem->ctrl_dll_on << PHY_CON12_CTRL_DLL_ON_SHIFT) |
(mem->ctrl_ref << PHY_CON12_CTRL_REF_SHIFT);
writel(val, &phy0_ctrl->phy_con12);
writel(val, &phy1_ctrl->phy_con12);
update_reset_dll(dmc, DDR_MODE_DDR3);
}
/* Send PALL command */
printk(BIOS_SPEW, "ddr3_mem_ctrl_init: Send PALL Command\n");
dmc_config_prech(mem, dmc);
writel(mem->memcontrol, &dmc->memcontrol);
/* Set DMC Concontrol and enable auto-refresh counter */
printk(BIOS_SPEW, "ddr3_mem_ctrl_init: "
"Set DMC Concontrol and enable auto-refresh counter\n");
writel(mem->concontrol | (mem->rd_fetch << CONCONTROL_RD_FETCH_SHIFT)
| (mem->aref_en << CONCONTROL_AREF_EN_SHIFT), &dmc->concontrol);
return 0;
}

View File

@ -0,0 +1,268 @@
/*
* This file is part of the coreboot project.
*
* Copyright 2013 Google Inc.
* Copyright (C) 2012 Samsung Electronics
*
* 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; version 2 of the License.
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/* Header file for Samsung DP (Display Port) interface driver. */
#ifndef CPU_SAMSUNG_EXYNOS5420_DP_CORE_H
#define CPU_SAMSUNG_EXYNOS5420_DP_CORE_H
#define STREAM_ON_TIMEOUT 100
#define PLL_LOCK_TIMEOUT 10
#define DP_INIT_TRIES 10
#define MAX_CR_LOOP 5
#define MAX_EQ_LOOP 4
/* Link tare type */
enum link_rate {
LINK_RATE_1_62GBPS = 0x06,
LINK_RATE_2_70GBPS = 0x0a
};
/* Number of lanes supported */
enum link_lane_count {
LANE_COUNT1 = 1,
LANE_COUNT2 = 2,
LANE_COUNT4 = 4
};
/* Pre emphasis level */
enum pre_emphasis_level {
PRE_EMPHASIS_LEVEL_0,
PRE_EMPHASIS_LEVEL_1,
PRE_EMPHASIS_LEVEL_2,
PRE_EMPHASIS_LEVEL_3,
};
/* Type of color space */
enum color_space {
COLOR_RGB,
COLOR_YCBCR422,
COLOR_YCBCR444
};
/* Video input Bit Per Color */
enum color_depth {
COLOR_6,
COLOR_8,
COLOR_10,
COLOR_12
};
/* Type of YCbCr coefficient */
enum color_coefficient {
COLOR_YCBCR601,
COLOR_YCBCR709
};
/* Color range */
enum dynamic_range {
VESA,
CEA
};
/* Status of PLL clock */
enum pll_status {
PLL_UNLOCKED,
PLL_LOCKED
};
/* To choose type of m_value */
enum clock_recovery_m_value_type {
CALCULATED_M,
REGISTER_M
};
struct video_info {
enum color_space color_space;
enum dynamic_range dynamic_range;
enum color_coefficient ycbcr_coeff;
enum color_depth color_depth;
enum link_rate link_rate;
enum link_lane_count lane_count;
char *name;
unsigned int h_sync_polarity:1;
unsigned int v_sync_polarity:1;
unsigned int interlaced:1;
};
struct link_train {
u8 link_rate;
u8 lane_count;
};
struct s5p_dp_device {
unsigned int irq;
struct exynos5_dp *base;
struct video_info *video_info;
struct link_train link_train;
};
/* this struct is used by mainboards to pass mode info to the driver */
typedef struct vidinfo {
u16 vl_col;
u16 vl_row;
u8 vl_bpix;
u16 *cmap;
} vidinfo_t;
/* s5p_dp_reg.c */
/*
* Reset DP module
*
* param dp pointer to main s5p-dp structure
*/
void s5p_dp_reset(struct s5p_dp_device *dp);
/*
* Initialize DP to recieve video stream
*
* param dp pointer to main s5p-dp structure
*/
void s5p_dp_init_video(struct s5p_dp_device *dp);
/*
* Check whether PLL is locked
*
* param dp pointer to main s5p-dp structure
* return Lock status
*/
unsigned int s5p_dp_get_pll_lock_status(struct s5p_dp_device *dp);
/*
* Initialize analog functions of DP
*
* param dp pointer to main s5p-dp structure
* return 0 on success
*/
int s5p_dp_init_analog_func(struct s5p_dp_device *dp);
/*
* Initialize DP for AUX transaction
*
* param dp pointer to main s5p-dp structure
*/
void s5p_dp_init_aux(struct s5p_dp_device *dp);
/*
* Start an AUX transaction.
*
* param dp pointer to main s5p-dp structure
*/
int s5p_dp_start_aux_transaction(struct s5p_dp_device *dp);
/*
* Write a byte to DPCD register
*
* param dp pointer to main s5p-dp structure
* param reg_addr DPCD register to be written
* param data byte data to be written
* return write status
*/
int s5p_dp_write_byte_to_dpcd(struct s5p_dp_device *dp,
unsigned int reg_addr,
unsigned char data);
/*
* Read a byte from DPCD register
*
* param dp pointer to main s5p-dp structure
* param reg_addr DPCD register to read
* param data read byte data
* return read status
*/
int s5p_dp_read_byte_from_dpcd(struct s5p_dp_device *dp,
unsigned int reg_addr,
unsigned char *data);
/*
* Initialize DP video functions
*
* param dp pointer to main s5p-dp structure
*/
//void s5p_dp_init_video(struct s5p_dp_device *dp);
/*
* Set color parameters for display
*
* param dp pointer to main s5p-dp structure
* param color_depth Video input Bit Per Color
* param color_space Colorimetric format of input video
* param dynamic_range VESA range or CEA range
* param coeff YCbCr Coefficients of input video
*/
void s5p_dp_set_video_color_format(struct s5p_dp_device *dp,
unsigned int color_depth,
unsigned int color_space,
unsigned int dynamic_range,
unsigned int coeff);
/*
* Check whether video clock is on
*
* param dp pointer to main s5p-dp structure
* return clock status
*/
int s5p_dp_is_slave_video_stream_clock_on(struct s5p_dp_device *dp);
/*
* Check whether video clock is on
*
* param dp pointer to main s5p-dp structure
* param type clock_recovery_m_value_type
* param m_value to caluculate m_vid value
* param n_value to caluculate n_vid value
*/
void s5p_dp_set_video_cr_mn(struct s5p_dp_device *dp,
enum clock_recovery_m_value_type type,
unsigned int m_value,
unsigned int n_value);
/*
* Set DP to video slave mode thereby enabling video master
*
* param dp pointer to main s5p-dp structure
*/
void s5p_dp_enable_video_master(struct s5p_dp_device *dp);
/*
* Check whether video stream is on
*
* param dp pointer to main s5p-dp structure
* return video stream status
*/
int s5p_dp_is_video_stream_on(struct s5p_dp_device *dp);
/*
* Configure DP in slave mode
*
* param dp pointer to main s5p-dp structure
* param video_info pointer to main video_info structure.
*/
void s5p_dp_config_video_slave_mode(struct s5p_dp_device *dp,
struct video_info *video_info);
/*
* Wait unitl HW link training done
*
* param dp pointer to main s5p-dp structure
*/
void s5p_dp_wait_hw_link_training_done(struct s5p_dp_device *dp);
/* startup and init */
struct exynos5_fimd_panel;
void fb_init(vidinfo_t *panel_info, void *lcdbase,
struct exynos5_fimd_panel *pd);
int dp_controller_init(struct s5p_dp_device *dp_device);
int lcd_ctrl_init(vidinfo_t *panel_info,
struct exynos5_fimd_panel *panel_data, void *lcdbase);
#endif /* CPU_SAMSUNG_EXYNOS5420_DP_CORE_H */

View File

@ -0,0 +1,495 @@
/*
* This file is part of the coreboot project.
*
* Copyright (C) 2012 Samsung Electronics
*
* 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; version 2 of the License.
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/* Samsung DP (Display port) register interface driver. */
#include <console/console.h>
#include <arch/io.h>
#include <delay.h>
#include "timer.h"
#include "clk.h"
#include "cpu.h"
#include "periph.h"
#include "dp.h"
#include "fimd.h"
#include "dp-core.h"
void s5p_dp_reset(struct s5p_dp_device *dp)
{
u32 reg;
struct exynos5_dp *base = dp->base;
writel(RESET_DP_TX, &base->dp_tx_sw_reset);
/* Stop Video */
clrbits_le32(&base->video_ctl_1, VIDEO_EN);
clrbits_le32(&base->video_ctl_1, HDCP_VIDEO_MUTE);
reg = MASTER_VID_FUNC_EN_N | SLAVE_VID_FUNC_EN_N |
AUD_FIFO_FUNC_EN_N | AUD_FUNC_EN_N |
HDCP_FUNC_EN_N | SW_FUNC_EN_N;
writel(reg, &base->func_en_1);
reg = SSC_FUNC_EN_N | AUX_FUNC_EN_N |
SERDES_FIFO_FUNC_EN_N |
LS_CLK_DOMAIN_FUNC_EN_N;
writel(reg, &base->func_en_2);
udelay(20);
reg = LANE3_MAP_LOGIC_LANE_3 | LANE2_MAP_LOGIC_LANE_2 |
LANE1_MAP_LOGIC_LANE_1 | LANE0_MAP_LOGIC_LANE_0;
writel(reg, &base->lane_map);
writel(0x0, &base->sys_ctl_1);
writel(0x40, &base->sys_ctl_2);
writel(0x0, &base->sys_ctl_3);
writel(0x0, &base->sys_ctl_4);
writel(0x0, &base->pkt_send_ctl);
writel(0x0, &base->dp_hdcp_ctl);
writel(0x5e, &base->dp_hpd_deglitch_l);
writel(0x1a, &base->dp_hpd_deglitch_h);
writel(0x10, &base->dp_debug_ctl);
writel(0x0, &base->dp_phy_test);
writel(0x0, &base->dp_video_fifo_thrd);
writel(0x20, &base->dp_audio_margin);
writel(0x4, &base->m_vid_gen_filter_th);
writel(0x2, &base->m_aud_gen_filter_th);
writel(0x00000101, &base->soc_general_ctl);
/* Set Analog Parameters */
writel(0x10, &base->analog_ctl_1);
writel(0x0C, &base->analog_ctl_2);
writel(0x85, &base->analog_ctl_3);
writel(0x66, &base->pll_filter_ctl_1);
writel(0x0, &base->tx_amp_tuning_ctl);
/* Set interrupt pin assertion polarity as high */
writel(INT_POL0 | INT_POL1, &base->int_ctl);
/* Clear pending regisers */
writel(0xff, &base->common_int_sta_1);
writel(0x4f, &base->common_int_sta_2);
writel(0xe0, &base->common_int_sta_3);
writel(0xe7, &base->common_int_sta_4);
writel(0x63, &base->dp_int_sta);
/* 0:mask,1: unmask */
writel(0x00, &base->common_int_mask_1);
writel(0x00, &base->common_int_mask_2);
writel(0x00, &base->common_int_mask_3);
writel(0x00, &base->common_int_mask_4);
writel(0x00, &base->int_sta_mask);
}
unsigned int s5p_dp_get_pll_lock_status(struct s5p_dp_device *dp)
{
u32 reg;
reg = readl(&dp->base->dp_debug_ctl);
if (reg & PLL_LOCK)
return PLL_LOCKED;
else
return PLL_UNLOCKED;
}
int s5p_dp_init_analog_func(struct s5p_dp_device *dp)
{
u32 reg;
u32 start;
struct exynos5_dp *base = dp->base;
writel(0x00, &base->dp_phy_pd);
reg = PLL_LOCK_CHG;
writel(reg, &base->common_int_sta_1);
clrbits_le32(&base->dp_debug_ctl, (F_PLL_LOCK | PLL_LOCK_CTRL));
/* Power up PLL */
if (s5p_dp_get_pll_lock_status(dp) == PLL_UNLOCKED) {
clrbits_le32(&base->dp_pll_ctl, DP_PLL_PD);
start = get_timer(0);
while (s5p_dp_get_pll_lock_status(dp) == PLL_UNLOCKED) {
if (get_timer(start) > PLL_LOCK_TIMEOUT) {
printk(BIOS_ERR, "%s: PLL is not locked\n",
__func__);
return -1;
}
}
}
/* Enable Serdes FIFO function and Link symbol clock domain module */
clrbits_le32(&base->func_en_2, (SERDES_FIFO_FUNC_EN_N |
LS_CLK_DOMAIN_FUNC_EN_N | AUX_FUNC_EN_N));
return 0;
}
void s5p_dp_init_aux(struct s5p_dp_device *dp)
{
u32 reg;
struct exynos5_dp *base = dp->base;
/* Clear inerrupts related to AUX channel */
reg = RPLY_RECEIV | AUX_ERR;
writel(reg, &base->dp_int_sta);
/* Disable AUX channel module */
setbits_le32(&base->func_en_2, AUX_FUNC_EN_N);
/* Disable AUX transaction H/W retry */
reg = (3 & AUX_BIT_PERIOD_MASK) << AUX_BIT_PERIOD_SHIFT;
reg |= (0 & AUX_HW_RETRY_COUNT_MASK) << AUX_HW_RETRY_COUNT_SHIFT;
reg |= (AUX_HW_RETRY_INTERVAL_600_US << AUX_HW_RETRY_INTERVAL_SHIFT);
writel(reg, &base->aux_hw_retry_ctl) ;
/* Receive AUX Channel DEFER commands equal to DEFFER_COUNT*64 */
reg = DEFER_CTRL_EN;
reg |= (1 & DEFER_COUNT_MASK) << DEFER_COUNT_SHIFT;
writel(reg, &base->aux_ch_defer_dtl);
/* Enable AUX channel module */
clrbits_le32(&base->func_en_2, AUX_FUNC_EN_N);
}
int s5p_dp_start_aux_transaction(struct s5p_dp_device *dp)
{
int reg;
struct exynos5_dp *base = dp->base;
/* Enable AUX CH operation */
setbits_le32(&base->aux_ch_ctl_2, AUX_EN);
/* Is AUX CH command reply received? */
reg = readl(&base->dp_int_sta);
while (!(reg & RPLY_RECEIV))
reg = readl(&base->dp_int_sta);
/* Clear interrupt source for AUX CH command reply */
writel(RPLY_RECEIV, &base->dp_int_sta);
/* Clear interrupt source for AUX CH access error */
reg = readl(&base->dp_int_sta);
if (reg & AUX_ERR) {
printk(BIOS_ERR, "%s: AUX_ERR encountered, dp_int_sta: "
"0x%02x\n", __func__, reg);
writel(AUX_ERR, &base->dp_int_sta);
return -1;
}
/* Check AUX CH error access status */
reg = readl(&base->dp_int_sta);
if ((reg & AUX_STATUS_MASK) != 0) {
printk(BIOS_ERR, "AUX CH error happens: %d\n\n",
reg & AUX_STATUS_MASK);
return -1;
}
return 0;
}
int s5p_dp_write_byte_to_dpcd(struct s5p_dp_device *dp,
unsigned int reg_addr,
unsigned char data)
{
u32 reg;
int i;
int retval;
struct exynos5_dp *base = dp->base;
for (i = 0; i < MAX_AUX_RETRY_COUNT; i++) {
/* Clear AUX CH data buffer */
writel(BUF_CLR, &base->buf_data_ctl);
/* Select DPCD device address */
reg = reg_addr >> AUX_ADDR_7_0_SHIFT;
reg &= AUX_ADDR_7_0_MASK;
writel(reg, &base->aux_addr_7_0);
reg = reg_addr >> AUX_ADDR_15_8_SHIFT;
reg &= AUX_ADDR_15_8_MASK;
writel(reg, &base->aux_addr_15_8);
reg = reg_addr >> AUX_ADDR_19_16_SHIFT;
reg &= AUX_ADDR_19_16_MASK;
writel(reg, &base->aux_addr_19_16);
/* Write data buffer */
reg = (unsigned int)data;
writel(reg, &base->buf_data_0);
/*
* Set DisplayPort transaction and write 1 byte
* If bit 3 is 1, DisplayPort transaction.
* If Bit 3 is 0, I2C transaction.
*/
reg = AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_WRITE;
writel(reg, &base->aux_ch_ctl_1);
/* Start AUX transaction */
retval = s5p_dp_start_aux_transaction(dp);
if (retval == 0)
break;
else
printk(BIOS_DEBUG, "Aux Transaction fail!\n");
}
return retval;
}
int s5p_dp_read_byte_from_dpcd(struct s5p_dp_device *dp,
unsigned int reg_addr,
unsigned char *data)
{
u32 reg;
int i;
int retval;
struct exynos5_dp *base = dp->base;
for (i = 0; i < MAX_AUX_RETRY_COUNT; i++) {
/* Clear AUX CH data buffer */
writel(BUF_CLR, &base->buf_data_ctl);
/* Select DPCD device address */
reg = reg_addr >> AUX_ADDR_7_0_SHIFT;
reg &= AUX_ADDR_7_0_MASK;
writel(reg, &base->aux_addr_7_0);
reg = reg_addr >> AUX_ADDR_15_8_SHIFT;
reg &= AUX_ADDR_15_8_MASK;
writel(reg, &base->aux_addr_15_8);
reg = reg_addr >> AUX_ADDR_19_16_SHIFT;
reg &= AUX_ADDR_19_16_MASK;
writel(reg, &base->aux_addr_19_16);
/*
* Set DisplayPort transaction and read 1 byte
* If bit 3 is 1, DisplayPort transaction.
* If Bit 3 is 0, I2C transaction.
*/
reg = AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_READ;
writel(reg, &base->aux_ch_ctl_1);
/* Start AUX transaction */
retval = s5p_dp_start_aux_transaction(dp);
if (retval == 0)
break;
else
printk(BIOS_DEBUG, "Aux Transaction fail!\n");
}
/* Read data buffer */
if (!retval) {
reg = readl(&base->buf_data_0);
*data = (unsigned char)(reg & 0xff);
}
return retval;
}
void s5p_dp_init_video(struct s5p_dp_device *dp)
{
u32 reg;
struct exynos5_dp *base = dp->base;
reg = VSYNC_DET | VID_FORMAT_CHG | VID_CLK_CHG;
writel(reg, &base->common_int_sta_1);
reg = 0x0;
writel(reg, &base->sys_ctl_1);
reg = (4 & CHA_CRI_MASK) << CHA_CRI_SHIFT;
reg |= CHA_CTRL;
writel(reg, &base->sys_ctl_2);
reg = 0x0;
writel(reg, &base->sys_ctl_3);
}
void s5p_dp_set_video_color_format(struct s5p_dp_device *dp,
unsigned int color_depth,
unsigned int color_space,
unsigned int dynamic_range,
unsigned int coeff)
{
u32 reg;
struct exynos5_dp *base = dp->base;
/* Configure the input color depth, color space, dynamic range */
reg = (dynamic_range << IN_D_RANGE_SHIFT) |
(color_depth << IN_BPC_SHIFT) |
(color_space << IN_COLOR_F_SHIFT);
writel(reg, &base->video_ctl_2);
/* Set Input Color YCbCr Coefficients to ITU601 or ITU709 */
reg = readl(&base->video_ctl_3);
reg &= ~IN_YC_COEFFI_MASK;
if (coeff)
reg |= IN_YC_COEFFI_ITU709;
else
reg |= IN_YC_COEFFI_ITU601;
writel(reg, &base->video_ctl_3);
}
int s5p_dp_is_slave_video_stream_clock_on(struct s5p_dp_device *dp)
{
u32 reg;
struct exynos5_dp *base = dp->base;
reg = readl(&base->sys_ctl_1);
writel(reg, &base->sys_ctl_1);
reg = readl(&base->sys_ctl_1);
if (!(reg & DET_STA))
return -1;
reg = readl(&base->sys_ctl_2);
writel(reg, &base->sys_ctl_2);
reg = readl(&base->sys_ctl_2);
if (reg & CHA_STA) {
printk(BIOS_DEBUG, "Input stream clk is changing\n");
return -1;
}
return 0;
}
void s5p_dp_set_video_cr_mn(struct s5p_dp_device *dp,
enum clock_recovery_m_value_type type,
unsigned int m_value,
unsigned int n_value)
{
u32 reg;
struct exynos5_dp *base = dp->base;
if (type == REGISTER_M) {
setbits_le32(&base->sys_ctl_4, FIX_M_VID);
reg = m_value >> M_VID_0_VALUE_SHIFT;
writel(reg, &base->m_vid_0);
reg = (m_value >> M_VID_1_VALUE_SHIFT);
writel(reg, &base->m_vid_1);
reg = (m_value >> M_VID_2_VALUE_SHIFT);
writel(reg, &base->m_vid_2);
reg = n_value >> N_VID_0_VALUE_SHIFT;
writel(reg, &base->n_vid_0);
reg = (n_value >> N_VID_1_VALUE_SHIFT);
writel(reg, &base->n_vid_1);
reg = (n_value >> N_VID_2_VALUE_SHIFT);
writel(reg, &base->n_vid_2);
} else {
clrbits_le32(&base->sys_ctl_4, FIX_M_VID);
writel(0x00, &base->n_vid_0);
writel(0x80, &base->n_vid_1);
writel(0x00, &base->n_vid_2);
}
}
void s5p_dp_enable_video_master(struct s5p_dp_device *dp)
{
u32 reg;
struct exynos5_dp *base = dp->base;
reg = readl(&base->soc_general_ctl);
reg &= ~VIDEO_MODE_MASK;
reg |= VIDEO_MODE_SLAVE_MODE;
writel(reg, &base->soc_general_ctl);
}
int s5p_dp_is_video_stream_on(struct s5p_dp_device *dp)
{
u32 reg, i = 0;
u32 start;
struct exynos5_dp *base = dp->base;
/* Wait for 4 VSYNC_DET interrupts */
start = get_timer(0);
do {
reg = readl(&base->common_int_sta_1);
if (reg & VSYNC_DET) {
i++;
writel(reg | VSYNC_DET, &base->common_int_sta_1);
}
if (i == 4)
break;
} while (get_timer(start) <= STREAM_ON_TIMEOUT);
if (i != 4) {
printk(BIOS_DEBUG, "s5p_dp_is_video_stream_on timeout\n");
return -1;
}
return 0;
}
void s5p_dp_config_video_slave_mode(struct s5p_dp_device *dp,
struct video_info *video_info)
{
u32 reg;
struct exynos5_dp *base = dp->base;
reg = readl(&base->func_en_1);
reg &= ~(MASTER_VID_FUNC_EN_N|SLAVE_VID_FUNC_EN_N);
reg |= MASTER_VID_FUNC_EN_N;
writel(reg, &base->func_en_1);
reg = readl(&base->video_ctl_10);
reg &= ~INTERACE_SCAN_CFG;
reg |= (video_info->interlaced << 2);
writel(reg, &base->video_ctl_10);
reg = readl(&base->video_ctl_10);
reg &= ~VSYNC_POLARITY_CFG;
reg |= (video_info->v_sync_polarity << 1);
writel(reg, &base->video_ctl_10);
reg = readl(&base->video_ctl_10);
reg &= ~HSYNC_POLARITY_CFG;
reg |= (video_info->h_sync_polarity << 0);
writel(reg, &base->video_ctl_10);
reg = AUDIO_MODE_SPDIF_MODE | VIDEO_MODE_SLAVE_MODE;
writel(reg, &base->soc_general_ctl);
}
void s5p_dp_wait_hw_link_training_done(struct s5p_dp_device *dp)
{
u32 reg;
struct exynos5_dp *base = dp->base;
reg = readl(&base->dp_hw_link_training);
while (reg & HW_TRAINING_EN)
reg = readl(&base->dp_hw_link_training);
}

View File

@ -0,0 +1,497 @@
/*
* This file is part of the coreboot project.
*
* Copyright 2013 Google Inc.
* Copyright (C) 2012 Samsung Electronics
*
* 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; version 2 of the License.
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/* Register map for Exynos5 DP */
#ifndef CPU_SAMSUNG_EXYNOS5420_DP_H
#define CPU_SAMSUNG_EXYNOS5420_DP_H
/* DSIM register map */
struct exynos5_dp {
u8 res1[0x10];
u32 dp_tx_version;
u32 dp_tx_sw_reset;
u32 func_en_1;
u32 func_en_2;
u32 video_ctl_1;
u32 video_ctl_2;
u32 video_ctl_3;
u32 video_ctl_4;
u32 clr_blue_cb;
u32 clr_green_y;
u32 clr_red_cr;
u32 video_ctl_8;
u8 res2[0x4];
u32 video_ctl_10;
u32 total_line_l;
u32 total_line_h;
u32 active_line_l;
u32 active_line_h;
u32 v_f_porch;
u32 vsync;
u32 v_b_porch;
u32 total_pixel_l;
u32 total_pixel_h;
u32 active_pixel_l;
u32 active_pixel_h;
u32 h_f_porch_l;
u32 h_f_porch_h;
u32 hsync_l;
u32 hysnc_h;
u32 h_b_porch_l;
u32 h_b_porch_h;
u32 vid_status;
u32 total_line_sta_l;
u32 total_line_sta_h;
u32 active_line_sta_l;
u32 active_line_sta_h;
u32 v_f_porch_sta;
u32 vsync_sta;
u32 v_b_porch_sta;
u32 total_pixel_sta_l;
u32 total_pixel_sta_h;
u32 active_pixel_sta_l;
u32 active_pixel_sta_h;
u32 h_f_porch_sta_l;
u32 h_f_porch_sta_h;
u32 hsync_sta_l;
u32 hsync_sta_h;
u32 h_b_porch_sta_l;
u32 h_b_porch__sta_h;
u8 res3[0x288];
u32 lane_map;
u8 res4[0x10];
u32 analog_ctl_1;
u32 analog_ctl_2;
u32 analog_ctl_3;
u32 pll_filter_ctl_1;
u32 tx_amp_tuning_ctl;
u8 res5[0xc];
u32 aux_hw_retry_ctl;
u8 res6[0x2c];
u32 int_state;
u32 common_int_sta_1;
u32 common_int_sta_2;
u32 common_int_sta_3;
u32 common_int_sta_4;
u8 res7[0x8];
u32 dp_int_sta;
u32 common_int_mask_1;
u32 common_int_mask_2;
u32 common_int_mask_3;
u32 common_int_mask_4;
u8 res8[0x08];
u32 int_sta_mask;
u32 int_ctl;
u8 res9[0x200];
u32 sys_ctl_1;
u32 sys_ctl_2;
u32 sys_ctl_3;
u32 sys_ctl_4;
u32 dp_vid_ctl;
u8 res10[0x2c];
u32 pkt_send_ctl;
u8 res11[0x4];
u32 dp_hdcp_ctl;
u8 res12[0x34];
u32 link_bw_set;
u32 lane_count_set;
u32 dp_training_ptn_set;
u32 ln0_link_trn_ctl;
u32 ln1_link_trn_ctl;
u32 ln2_link_trn_ctl;
u32 ln3_link_trn_ctl;
u32 dp_dn_spread;
u32 dp_hw_link_training;
u8 res13[0x1c];
u32 dp_debug_ctl;
u32 dp_hpd_deglitch_l;
u32 dp_hpd_deglitch_h;
u8 res14[0x14];
u32 dp_link_debug_ctl;
u8 res15[0x1c];
u32 m_vid_0;
u32 m_vid_1;
u32 m_vid_2;
u32 n_vid_0;
u32 n_vid_1;
u32 n_vid_2;
u32 m_vid_mon;
u32 dp_pll_ctl;
u32 dp_phy_pd;
u32 dp_phy_test;
u8 res16[0x8];
u32 dp_video_fifo_thrd;
u8 res17[0x8];
u32 dp_audio_margin;
u32 dp_dn_spread_ctl_1;
u32 dp_dn_spread_ctl_2;
u8 res18[0x18];
u32 dp_m_cal_ctl;
u32 m_vid_gen_filter_th;
u8 res19[0x14];
u32 m_aud_gen_filter_th;
u32 aux_ch_sta;
u32 aux_err_num;
u32 aux_ch_defer_dtl;
u32 aux_rx_comm;
u32 buf_data_ctl;
u32 aux_ch_ctl_1;
u32 aux_addr_7_0;
u32 aux_addr_15_8;
u32 aux_addr_19_16;
u32 aux_ch_ctl_2;
u8 res20[0x18];
u32 buf_data_0;
u8 res21[0x3c];
u32 soc_general_ctl;
};
/* DP_TX_SW_RESET */
#define RESET_DP_TX (1 << 0)
/* DP_FUNC_EN_1 */
#define MASTER_VID_FUNC_EN_N (1 << 7)
#define SLAVE_VID_FUNC_EN_N (1 << 5)
#define AUD_FIFO_FUNC_EN_N (1 << 4)
#define AUD_FUNC_EN_N (1 << 3)
#define HDCP_FUNC_EN_N (1 << 2)
#define CRC_FUNC_EN_N (1 << 1)
#define SW_FUNC_EN_N (1 << 0)
/* DP_FUNC_EN_2 */
#define SSC_FUNC_EN_N (1 << 7)
#define AUX_FUNC_EN_N (1 << 2)
#define SERDES_FIFO_FUNC_EN_N (1 << 1)
#define LS_CLK_DOMAIN_FUNC_EN_N (1 << 0)
/* DP_VIDEO_CTL_1 */
#define VIDEO_EN (1 << 7)
#define HDCP_VIDEO_MUTE (1 << 6)
/* DP_VIDEO_CTL_1 */
#define IN_D_RANGE_MASK (1 << 7)
#define IN_D_RANGE_SHIFT (7)
#define IN_D_RANGE_CEA (1 << 7)
#define IN_D_RANGE_VESA (0 << 7)
#define IN_BPC_MASK (7 << 4)
#define IN_BPC_SHIFT (4)
#define IN_BPC_12_BITS (3 << 4)
#define IN_BPC_10_BITS (2 << 4)
#define IN_BPC_8_BITS (1 << 4)
#define IN_BPC_6_BITS (0 << 4)
#define IN_COLOR_F_MASK (3 << 0)
#define IN_COLOR_F_SHIFT (0)
#define IN_COLOR_F_YCBCR444 (2 << 0)
#define IN_COLOR_F_YCBCR422 (1 << 0)
#define IN_COLOR_F_RGB (0 << 0)
/* DP_VIDEO_CTL_3 */
#define IN_YC_COEFFI_MASK (1 << 7)
#define IN_YC_COEFFI_SHIFT (7)
#define IN_YC_COEFFI_ITU709 (1 << 7)
#define IN_YC_COEFFI_ITU601 (0 << 7)
#define VID_CHK_UPDATE_TYPE_MASK (1 << 4)
#define VID_CHK_UPDATE_TYPE_SHIFT (4)
#define VID_CHK_UPDATE_TYPE_1 (1 << 4)
#define VID_CHK_UPDATE_TYPE_0 (0 << 4)
/* DP_VIDEO_CTL_10 */
#define FORMAT_SEL (1 << 4)
#define INTERACE_SCAN_CFG (1 << 2)
#define VSYNC_POLARITY_CFG (1 << 1)
#define HSYNC_POLARITY_CFG (1 << 0)
/* DP_LANE_MAP */
#define LANE3_MAP_LOGIC_LANE_0 (0 << 6)
#define LANE3_MAP_LOGIC_LANE_1 (1 << 6)
#define LANE3_MAP_LOGIC_LANE_2 (2 << 6)
#define LANE3_MAP_LOGIC_LANE_3 (3 << 6)
#define LANE2_MAP_LOGIC_LANE_0 (0 << 4)
#define LANE2_MAP_LOGIC_LANE_1 (1 << 4)
#define LANE2_MAP_LOGIC_LANE_2 (2 << 4)
#define LANE2_MAP_LOGIC_LANE_3 (3 << 4)
#define LANE1_MAP_LOGIC_LANE_0 (0 << 2)
#define LANE1_MAP_LOGIC_LANE_1 (1 << 2)
#define LANE1_MAP_LOGIC_LANE_2 (2 << 2)
#define LANE1_MAP_LOGIC_LANE_3 (3 << 2)
#define LANE0_MAP_LOGIC_LANE_0 (0 << 0)
#define LANE0_MAP_LOGIC_LANE_1 (1 << 0)
#define LANE0_MAP_LOGIC_LANE_2 (2 << 0)
#define LANE0_MAP_LOGIC_LANE_3 (3 << 0)
/* DP_AUX_HW_RETRY_CTL */
#define AUX_BIT_PERIOD_SHIFT 8
#define AUX_BIT_PERIOD_MASK 7
#define AUX_HW_RETRY_INTERVAL_SHIFT 3
#define AUX_HW_RETRY_INTERVAL_600_US 0
#define AUX_HW_RETRY_INTERVAL_800_US 1
#define AUX_HW_RETRY_INTERVAL_1000_US 2
#define AUX_HW_RETRY_INTERVAL_1800_US 3
#define AUX_HW_RETRY_COUNT_SHIFT 0
#define AUX_HW_RETRY_COUNT_MASK 7
/* DP_COMMON_INT_STA_1 */
#define VSYNC_DET (1 << 7)
#define PLL_LOCK_CHG (1 << 6)
#define SPDIF_ERR (1 << 5)
#define SPDIF_UNSTBL (1 << 4)
#define VID_FORMAT_CHG (1 << 3)
#define AUD_CLK_CHG (1 << 2)
#define VID_CLK_CHG (1 << 1)
#define SW_INT (1 << 0)
/* DP_COMMON_INT_STA_2 */
#define ENC_EN_CHG (1 << 6)
#define HW_BKSV_RDY (1 << 3)
#define HW_SHA_DONE (1 << 2)
#define HW_AUTH_STATE_CHG (1 << 1)
#define HW_AUTH_DONE (1 << 0)
/* DP_COMMON_INT_STA_3 */
#define AFIFO_UNDER (1 << 7)
#define AFIFO_OVER (1 << 6)
#define R0_CHK_FLAG (1 << 5)
/* DP_COMMON_INT_STA_4 */
#define PSR_ACTIVE (1 << 7)
#define PSR_INACTIVE (1 << 6)
#define SPDIF_BI_PHASE_ERR (1 << 5)
#define HOTPLUG_CHG (1 << 2)
#define HPD_LOST (1 << 1)
#define PLUG (1 << 0)
/* DP_INT_STA */
#define INT_HPD (1 << 6)
#define HW_TRAINING_FINISH (1 << 5)
#define RPLY_RECEIV (1 << 1)
#define AUX_ERR (1 << 0)
/* DP_INT_CTL */
#define INT_POL0 (1 << 0)
#define INT_POL1 (1 << 1)
#define SOFT_INT_CTRL (1 << 2)
/* DP_SYS_CTL_1 */
#define DET_STA (1 << 2)
#define FORCE_DET (1 << 1)
#define DET_CTRL (1 << 0)
/* DP_SYS_CTL_2 */
#define CHA_CRI_SHIFT 4
#define CHA_CRI_MASK 0xf
#define CHA_STA (1 << 2)
#define FORCE_CHA (1 << 1)
#define CHA_CTRL (1 << 0)
/* DP_SYS_CTL_3 */
#define HPD_STATUS (1 << 6)
#define F_HPD (1 << 5)
#define HPD_CTRL (1 << 4)
#define HDCP_RDY (1 << 3)
#define STRM_VALID (1 << 2)
#define F_VALID (1 << 1)
#define VALID_CTRL (1 << 0)
/* DP_SYS_CTL_4 */
#define FIX_M_AUD (1 << 4)
#define ENHANCED (1 << 3)
#define FIX_M_VID (1 << 2)
#define M_VID_UPDATE_CTRL (3 << 0)
/* DP_TRAINING_PTN_SET */
#define SCRAMBLER_TYPE (1 << 9)
#define HW_LINK_TRAINING_PATTERN (1 << 8)
#define SCRAMBLING_DISABLE (1 << 5)
#define SCRAMBLING_ENABLE (0 << 5)
#define LINK_QUAL_PATTERN_SET_MASK (3 << 2)
#define LINK_QUAL_PATTERN_SET_PRBS7 (3 << 2)
#define LINK_QUAL_PATTERN_SET_D10_2 (1 << 2)
#define LINK_QUAL_PATTERN_SET_DISABLE (0 << 2)
#define SW_TRAINING_PATTERN_SET_MASK (3 << 0)
#define SW_TRAINING_PATTERN_SET_PTN2 (2 << 0)
#define SW_TRAINING_PATTERN_SET_PTN1 (1 << 0)
#define SW_TRAINING_PATTERN_SET_NORMAL (0 << 0)
/* DP_LN0_LINK_TRAINING_CTL */
#define PRE_EMPHASIS_SET_SHIFT (3)
/* DP_DEBUG_CTL */
#define PLL_LOCK (1 << 4)
#define F_PLL_LOCK (1 << 3)
#define PLL_LOCK_CTRL (1 << 2)
#define PN_INV (1 << 0)
/* DP_M_VID */
#define M_VID_0_VALUE_SHIFT 0
#define M_VID_1_VALUE_SHIFT 8
#define M_VID_2_VALUE_SHIFT 16
/* DP_M_VID */
#define N_VID_0_VALUE_SHIFT 0
#define N_VID_1_VALUE_SHIFT 8
#define N_VID_2_VALUE_SHIFT 16
/* DP_PLL_CTL */
#define DP_PLL_PD (1 << 7)
#define DP_PLL_RESET (1 << 6)
#define DP_PLL_LOOP_BIT_DEFAULT (1 << 4)
#define DP_PLL_REF_BIT_1_1250V (5 << 0)
#define DP_PLL_REF_BIT_1_2500V (7 << 0)
/* DP_PHY_PD */
#define DP_PHY_PD (1 << 5)
#define AUX_PD (1 << 4)
#define CH3_PD (1 << 3)
#define CH2_PD (1 << 2)
#define CH1_PD (1 << 1)
#define CH0_PD (1 << 0)
/* DP_PHY_TEST */
#define MACRO_RST (1 << 5)
#define CH1_TEST (1 << 1)
#define CH0_TEST (1 << 0)
/* DP_AUX_CH_STA */
#define AUX_BUSY (1 << 4)
#define AUX_STATUS_MASK (0xf << 0)
/* DP_AUX_CH_DEFER_CTL */
#define DEFER_CTRL_EN (1 << 7)
#define DEFER_COUNT_SHIFT 0
#define DEFER_COUNT_MASK 0x7f
/* DP_AUX_RX_COMM */
#define AUX_RX_COMM_I2C_DEFER (2 << 2)
#define AUX_RX_COMM_AUX_DEFER (2 << 0)
/* DP_BUFFER_DATA_CTL */
#define BUF_CLR (1 << 7)
/* Maximum number of tries for Aux Transaction */
#define MAX_AUX_RETRY_COUNT 10
/* DP_AUX_CH_CTL_1 */
#define AUX_LENGTH_SHIFT 4
#define AUX_LENGTH_MASK 0xf
#define AUX_TX_COMM_MASK (0xf << 0)
#define AUX_TX_COMM_DP_TRANSACTION (1 << 3)
#define AUX_TX_COMM_I2C_TRANSACTION (0 << 3)
#define AUX_TX_COMM_MOT (1 << 2)
#define AUX_TX_COMM_WRITE (0 << 0)
#define AUX_TX_COMM_READ (1 << 0)
/* DP_AUX_ADDR_7_0 */
#define AUX_ADDR_7_0_SHIFT 0
#define AUX_ADDR_7_0_MASK 0xff
/* DP_AUX_ADDR_15_8 */
#define AUX_ADDR_15_8_SHIFT 8
#define AUX_ADDR_15_8_MASK 0xff
/* DP_AUX_ADDR_19_16 */
#define AUX_ADDR_19_16_SHIFT 16
#define AUX_ADDR_19_16_MASK 0x0f
/* DP_AUX_CH_CTL_2 */
#define ADDR_ONLY (1 << 1)
#define AUX_EN (1 << 0)
/* DP_SOC_GENERAL_CTL */
#define AUDIO_MODE_SPDIF_MODE (1 << 8)
#define AUDIO_MODE_MASTER_MODE (0 << 8)
#define MASTER_VIDEO_INTERLACE_EN (1 << 4)
#define VIDEO_MASTER_CLK_SEL (1 << 2)
#define VIDEO_MASTER_MODE_EN (1 << 1)
#define VIDEO_MODE_MASK (1 << 0)
#define VIDEO_MODE_SLAVE_MODE (1 << 0)
#define VIDEO_MODE_MASTER_MODE (0 << 0)
#define HW_TRAINING_ERROR_CODE (7<<4)
#define HW_TRAINING_EN (1<<0)
/* I2C EDID Chip ID, Slave Address */
#define I2C_EDID_DEVICE_ADDR 0x50
#define I2C_E_EDID_DEVICE_ADDR 0x30
#define EDID_BLOCK_LENGTH 0x80
#define EDID_HEADER_PATTERN 0x00
#define EDID_EXTENSION_FLAG 0x7e
#define EDID_CHECKSUM 0x7f
/* Definition for DPCD Register */
#define DPCD_ADDR_DPCD_REV 0x0000
#define DPCD_ADDR_MAX_LINK_RATE 0x0001
#define DPCD_ADDR_MAX_LANE_COUNT 0x0002
#define DPCD_ADDR_LINK_BW_SET 0x0100
#define DPCD_ADDR_LANE_COUNT_SET 0x0101
#define DPCD_ADDR_TRAINING_PATTERN_SET 0x0102
#define DPCD_ADDR_TRAINING_LANE0_SET 0x0103
#define DPCD_ADDR_LANE0_1_STATUS 0x0202
#define DPCD_ADDR_LANE_ALIGN__STATUS_UPDATED 0x0204
#define DPCD_ADDR_ADJUST_REQUEST_LANE0_1 0x0206
#define DPCD_ADDR_ADJUST_REQUEST_LANE2_3 0x0207
#define DPCD_ADDR_TEST_REQUEST 0x0218
#define DPCD_ADDR_TEST_RESPONSE 0x0260
#define DPCD_ADDR_TEST_EDID_CHECKSUM 0x0261
#define DPCD_ADDR_SINK_POWER_STATE 0x0600
/* DPCD_ADDR_MAX_LANE_COUNT */
#define DPCD_MAX_LANE_COUNT_MASK 0x1f
/* DPCD_ADDR_LANE_COUNT_SET */
#define DPCD_ENHANCED_FRAME_EN (1 << 7)
#define DPCD_LANE_COUNT_SET_MASK 0x1f
/* DPCD_ADDR_TRAINING_PATTERN_SET */
#define DPCD_SCRAMBLING_DISABLED (1 << 5)
#define DPCD_SCRAMBLING_ENABLED (0 << 5)
#define DPCD_TRAINING_PATTERN_2 (2 << 0)
#define DPCD_TRAINING_PATTERN_1 (1 << 0)
#define DPCD_TRAINING_PATTERN_DISABLED (0 << 0)
/* DPCD_ADDR_LANE0_1_STATUS */
#define DPCD_LANE_SYMBOL_LOCKED (1 << 2)
#define DPCD_LANE_CHANNEL_EQ_DONE (1 << 1)
#define DPCD_LANE_CR_DONE (1 << 0)
#define DPCD_CHANNEL_EQ_BITS (DPCD_LANE_CR_DONE | \
DPCD_LANE_CHANNEL_EQ_DONE | \
DPCD_LANE_SYMBOL_LOCKED)
/* DPCD_ADDR_LANE_ALIGN__STATUS_UPDATED */
#define DPCD_LINK_STATUS_UPDATED (1 << 7)
#define DPCD_DOWNSTREAM_PORT_STATUS_CHANGED (1 << 6)
#define DPCD_INTERLANE_ALIGN_DONE (1 << 0)
/* DPCD_ADDR_TEST_REQUEST */
#define DPCD_TEST_EDID_READ (1 << 2)
/* DPCD_ADDR_TEST_RESPONSE */
#define DPCD_TEST_EDID_CHECKSUM_WRITE (1 << 2)
/* DPCD_ADDR_SINK_POWER_STATE */
#define DPCD_SET_POWER_STATE_D0 (1 << 0)
#define DPCD_SET_POWER_STATE_D4 (2 << 0)
/* Allow DP Gating clock and set FIMD source to 267 Mhz for DP */
void clock_init_dp_clock(void);
#endif

View File

@ -0,0 +1,109 @@
/*
* This file is part of the coreboot project.
*
* Copyright (C) 2012 Samsung Electronics
*
* 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; version 2 of the License.
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/* Register map for Exynos5 MIPI-DSIM */
#ifndef CPU_SAMSUNG_EXYNOS5420_DSIM_H
#define CPU_SAMSUNG_EXYNOS5420_DSIM_H
/* DSIM register map */
struct exynos5_dsim {
unsigned int status;
unsigned int swrst;
unsigned int clkctrl;
unsigned int timeout;
unsigned int config;
unsigned int escmode;
unsigned int mdresol;
unsigned int mvporch;
unsigned int mhporch;
unsigned int msync;
unsigned int sdresol;
unsigned int intsrc;
unsigned int intmsk;
unsigned int pkthdr;
unsigned int payload;
unsigned int rxfifo;
unsigned int res1;
unsigned int fifoctrl;
unsigned int res2;
unsigned int pllctrl;
unsigned int plltmr;
unsigned int phyacchr;
unsigned int phyacchr1;
};
#define ENABLE 1
#define DISABLE 0
#define DSIM_SWRST (1 << 0)
#define NUM_OF_DAT_LANE_IS_FOUR (3 << 5)
#define DATA_LANE_0_EN (1 << 0)
#define DATA_LANE_1_EN (1 << 1)
#define DATA_LANE_2_EN (1 << 2)
#define DATA_LANE_3_EN (1 << 3)
#define CLK_LANE_EN (1 << 4)
#define ENABLE_ALL_DATA_LANE DATA_LANE_0_EN | \
DATA_LANE_1_EN | \
DATA_LANE_2_EN | \
DATA_LANE_3_EN
#define MAIN_PIX_FORMAT_OFFSET 12
#define RGB_565_16_BIT 0x4
#define VIDEO_MODE (1 << 25)
#define BURST_MODE (1 << 26)
#define DSIM_PHYACCHR_AFC_EN (1 << 14)
#define DSIM_PHYACCHR_AFC_CTL_OFFSET 5
#define DSIM_PLLCTRL_PMS_OFFSET 1
#define DSIM_FREQ_BAND_OFFSET 24
#define LANE_ESC_CLK_EN_ALL (0x1f << 19)
#define BYTE_CLK_EN (1 << 24)
#define DSIM_ESC_CLK_EN (1 << 28)
#define TXREQUEST_HS_CLK_ON (1 << 31)
#define LP_MODE_ENABLE (1 << 7)
#define STOP_STATE_CNT_OFFSET 21
#define MAIN_VBP_OFFSET 0
#define STABLE_VFP_OFFSET 16
#define CMD_ALLOW_OFFSET 28
#define MAIN_HBP_OFFSET 0
#define MAIN_HFP_OFFSET 16
#define MAIN_HSA_OFFSET 0
#define MAIN_VSA_OFFSET 22
#define MAIN_STANDBY (1 << 31)
#define MAIN_VRESOL_OFFSET 16
#define MAIN_HRESOL_OFFSET 0
#define SFR_FIFO_EMPTY (1 << 29)
#define DSIM_PLL_EN_SHIFT (1 << 23)
#define PLL_STABLE (1 << 31)
#define DSIM_STOP_STATE_DAT(x) (((x) & 0xf) << 0)
#define DSIM_STOP_STATE_CLK (1 << 8)
#define DSIM_TX_READY_HS_CLK (1 << 10)
#endif

View File

@ -0,0 +1,597 @@
/*
* This file is part of the coreboot project.
*
* Copyright 2013 Google Inc.
* Copyright (C) 2012 Samsung Electronics
*
* 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; version 2 of the License.
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/* LCD driver for Exynos */
#include <arch/io.h>
#include <stdlib.h>
#include <string.h>
#include <timer.h>
#include <delay.h>
#include <console/console.h>
#include "timer.h"
#include "cpu.h"
#include "power.h"
#include "sysreg.h"
#include <drivers/maxim/max77686/max77686.h>
#include "device/i2c.h"
#include "i2c.h"
#include "fimd.h"
#include "dp.h"
#include "dp-core.h"
/*
* Here is the rough outline of how we bring up the display:
* 1. Upon power-on Sink generates a hot plug detection pulse thru HPD
* 2. Source determines video mode by reading DPCD receiver capability field
* (DPCD 00000h to 0000Dh) including eDP CP capability register (DPCD
* 0000Dh).
* 3. Sink replies DPCD receiver capability field.
* 4. Source starts EDID read thru I2C-over-AUX.
* 5. Sink replies EDID thru I2C-over-AUX.
* 6. Source determines link configuration, such as MAX_LINK_RATE and
* MAX_LANE_COUNT. Source also determines which type of eDP Authentication
* method to use and writes DPCD link configuration field (DPCD 00100h to
* 0010Ah) including eDP configuration set (DPCD 0010Ah).
* 7. Source starts link training. Sink does clock recovery and equalization.
* 8. Source reads DPCD link status field (DPCD 00200h to 0020Bh).
* 9. Sink replies DPCD link status field. If main link is not stable, Source
* repeats Step 7.
* 10. Source sends MSA (Main Stream Attribute) data. Sink extracts video
* parameters and recovers stream clock.
* 11. Source sends video data.
*/
/* To help debug any init errors here, define a list of possible errors */
enum {
ERR_PLL_NOT_UNLOCKED = 2,
ERR_VIDEO_CLOCK_BAD,
ERR_VIDEO_STREAM_BAD,
ERR_DPCD_READ_ERROR1, /* 5 */
ERR_DPCD_WRITE_ERROR1,
ERR_DPCD_READ_ERROR2,
ERR_DPCD_WRITE_ERROR2,
ERR_INVALID_LANE,
ERR_PLL_NOT_LOCKED, /* 10 */
ERR_PRE_EMPHASIS_LEVELS,
ERR_LINK_RATE_ABNORMAL,
ERR_MAX_LANE_COUNT_ABNORMAL,
ERR_LINK_TRAINING_FAILURE,
ERR_MISSING_DP_BASE, /* 15 */
ERR_NO_FDT_NODE,
};
/* ok, this is stupid, but we're going to leave the variables in here until we
* know it works. One cleanup task at a time.
*/
enum stage_t {
STAGE_START = 0,
STAGE_LCD_VDD,
STAGE_BRIDGE_SETUP,
STAGE_BRIDGE_INIT,
STAGE_BRIDGE_RESET,
STAGE_HOTPLUG,
STAGE_DP_CONTROLLER,
STAGE_BACKLIGHT_VDD,
STAGE_BACKLIGHT_PWM,
STAGE_BACKLIGHT_EN,
STAGE_DONE,
};
int lcd_line_length;
int lcd_color_fg;
int lcd_color_bg;
void *lcd_console_address; /* Start of console buffer */
short console_col;
short console_row;
/* Bypass FIMD of DISP1_BLK */
static void fimd_bypass(void)
{
struct exynos5_sysreg *sysreg = samsung_get_base_sysreg();
setbits_le32(&sysreg->disp1blk_cfg, FIMDBYPASS_DISP1);
sysreg->disp1blk_cfg &= ~FIMDBYPASS_DISP1;
}
/* Calculate the size of Framebuffer from the resolution */
static u32 calc_fbsize(vidinfo_t *panel_info)
{
/* They had PAGE_SIZE here instead of 4096.
* but that's a totally arbitrary number -- everything nowadays
* has lots of page sizes.
* So keep it obvious.
*/
return ALIGN((panel_info->vl_col * panel_info->vl_row *
((1<<panel_info->vl_bpix) / 8)), 4096);
}
/*
* Initialize display controller.
*
* @param lcdbase pointer to the base address of framebuffer.
* @pd pointer to the main panel_data structure
*/
void fb_init(vidinfo_t *panel_info, void *lcdbase,
struct exynos5_fimd_panel *pd)
{
unsigned int val;
u32 fbsize;
struct exynos5_fimd *fimd = samsung_get_base_fimd();
struct exynos5_disp_ctrl *disp_ctrl = samsung_get_base_disp_ctrl();
writel(pd->ivclk | pd->fixvclk, &disp_ctrl->vidcon1);
val = ENVID_ON | ENVID_F_ON | (pd->clkval_f << CLKVAL_F_OFFSET);
writel(val, &fimd->vidcon0);
val = (pd->vsync << VSYNC_PULSE_WIDTH_OFFSET) |
(pd->lower_margin << V_FRONT_PORCH_OFFSET) |
(pd->upper_margin << V_BACK_PORCH_OFFSET);
writel(val, &disp_ctrl->vidtcon0);
val = (pd->hsync << HSYNC_PULSE_WIDTH_OFFSET) |
(pd->right_margin << H_FRONT_PORCH_OFFSET) |
(pd->left_margin << H_BACK_PORCH_OFFSET);
writel(val, &disp_ctrl->vidtcon1);
val = ((pd->xres - 1) << HOZVAL_OFFSET) |
((pd->yres - 1) << LINEVAL_OFFSET);
writel(val, &disp_ctrl->vidtcon2);
writel((unsigned int)lcdbase, &fimd->vidw00add0b0);
fbsize = calc_fbsize(panel_info);
writel((unsigned int)lcdbase + fbsize, &fimd->vidw00add1b0);
writel(pd->xres * 2, &fimd->vidw00add2);
val = ((pd->xres - 1) << OSD_RIGHTBOTX_F_OFFSET);
val |= ((pd->yres - 1) << OSD_RIGHTBOTY_F_OFFSET);
writel(val, &fimd->vidosd0b);
writel(pd->xres * pd->yres, &fimd->vidosd0c);
setbits_le32(&fimd->shadowcon, CHANNEL0_EN);
val = BPPMODE_F_RGB_16BIT_565 << BPPMODE_F_OFFSET;
val |= ENWIN_F_ENABLE | HALF_WORD_SWAP_EN;
writel(val, &fimd->wincon0);
/* DPCLKCON_ENABLE */
writel(1 << 1, &fimd->dpclkcon);
}
#ifdef UNUSED_CODE
void exynos_fimd_disable(void)
{
struct exynos5_fimd *fimd = samsung_get_base_fimd();
writel(0, &fimd->wincon0);
clrbits_le32(&fimd->shadowcon, CHANNEL0_EN);
}
#endif
/*
* Configure DP in slave mode and wait for video stream.
*
* param dp pointer to main s5p-dp structure
* param video_info pointer to main video_info structure.
* return status
*/
static int s5p_dp_config_video(struct s5p_dp_device *dp,
struct video_info *video_info)
{
int timeout = 0;
struct exynos5_dp *base = dp->base;
struct mono_time start, current, end;
s5p_dp_config_video_slave_mode(dp, video_info);
s5p_dp_set_video_color_format(dp, video_info->color_depth,
video_info->color_space,
video_info->dynamic_range,
video_info->ycbcr_coeff);
if (s5p_dp_get_pll_lock_status(dp) == PLL_UNLOCKED) {
printk(BIOS_DEBUG, "PLL is not locked yet.\n");
return -ERR_PLL_NOT_UNLOCKED;
}
timer_monotonic_get(&start);
end = current = start;
mono_time_add_usecs(&end, STREAM_ON_TIMEOUT * USECS_PER_MSEC);
do {
if (s5p_dp_is_slave_video_stream_clock_on(dp) == 0) {
timeout++;
break;
}
timer_monotonic_get(&current);
} while (mono_time_before(&current, &end));
if (!timeout) {
printk(BIOS_ERR, "Video Clock Not ok after %ldus.\n",
mono_time_diff_microseconds(&start, &end));
return -ERR_VIDEO_CLOCK_BAD;
}
/* Set to use the register calculated M/N video */
s5p_dp_set_video_cr_mn(dp, CALCULATED_M, 0, 0);
clrbits_le32(&base->video_ctl_10, FORMAT_SEL);
/* Disable video mute */
clrbits_le32(&base->video_ctl_1, HDCP_VIDEO_MUTE);
/* Configure video slave mode */
s5p_dp_enable_video_master(dp);
/* Enable video */
setbits_le32(&base->video_ctl_1, VIDEO_EN);
timeout = s5p_dp_is_video_stream_on(dp);
if (timeout) {
printk(BIOS_DEBUG, "Video Stream Not on\n");
return -ERR_VIDEO_STREAM_BAD;
}
return 0;
}
/*
* Set DP to enhanced mode. We use this for EVT1
* param dp pointer to main s5p-dp structure
* return status
*/
static int s5p_dp_enable_rx_to_enhanced_mode(struct s5p_dp_device *dp)
{
u8 data;
if (s5p_dp_read_byte_from_dpcd(dp, DPCD_ADDR_LANE_COUNT_SET, &data)) {
printk(BIOS_DEBUG, "DPCD read error\n");
return -ERR_DPCD_READ_ERROR1;
}
if (s5p_dp_write_byte_to_dpcd(dp, DPCD_ADDR_LANE_COUNT_SET,
DPCD_ENHANCED_FRAME_EN |
(data & DPCD_LANE_COUNT_SET_MASK))) {
printk(BIOS_DEBUG, "DPCD write error\n");
return -ERR_DPCD_WRITE_ERROR1;
}
return 0;
}
/*
* Enable scrambles mode. We use this for EVT1
* param dp pointer to main s5p-dp structure
* return status
*/
static int s5p_dp_enable_scramble(struct s5p_dp_device *dp)
{
u8 data;
struct exynos5_dp *base = dp->base;
clrbits_le32(&base->dp_training_ptn_set, SCRAMBLING_DISABLE);
if (s5p_dp_read_byte_from_dpcd(dp, DPCD_ADDR_TRAINING_PATTERN_SET,
&data)) {
printk(BIOS_DEBUG, "DPCD read error\n");
return -ERR_DPCD_READ_ERROR2;
}
if (s5p_dp_write_byte_to_dpcd(dp, DPCD_ADDR_TRAINING_PATTERN_SET,
(u8)(data & ~DPCD_SCRAMBLING_DISABLED))) {
printk(BIOS_DEBUG, "DPCD write error\n");
return -ERR_DPCD_WRITE_ERROR2;
}
return 0;
}
/*
* Reset DP and prepare DP for init training
* param dp pointer to main s5p-dp structure
*/
static int s5p_dp_init_dp(struct s5p_dp_device *dp)
{
int ret, i;
struct exynos5_dp *base = dp->base;
for (i = 0; i < DP_INIT_TRIES; i++) {
s5p_dp_reset(dp);
/* SW defined function Normal operation */
clrbits_le32(&base->func_en_1, SW_FUNC_EN_N);
ret = s5p_dp_init_analog_func(dp);
if (!ret)
break;
udelay(5000);
printk(BIOS_DEBUG, "LCD retry init, attempt=%d ret=%d\n", i, ret);
}
if (i == DP_INIT_TRIES) {
printk(BIOS_DEBUG, "LCD initialization failed, ret=%d\n", ret);
return ret;
}
s5p_dp_init_aux(dp);
return ret;
}
/*
* Set pre-emphasis level
* param dp pointer to main s5p-dp structure
* param pre_emphasis pre-emphasis level
* param lane lane number(0 - 3)
* return status
*/
static int s5p_dp_set_lane_lane_pre_emphasis(struct s5p_dp_device *dp,
int pre_emphasis, int lane)
{
u32 reg;
struct exynos5_dp *base = dp->base;
reg = pre_emphasis << PRE_EMPHASIS_SET_SHIFT;
switch (lane) {
case 0:
writel(reg, &base->ln0_link_trn_ctl);
break;
case 1:
writel(reg, &base->ln1_link_trn_ctl);
break;
case 2:
writel(reg, &base->ln2_link_trn_ctl);
break;
case 3:
writel(reg, &base->ln3_link_trn_ctl);
break;
default:
printk(BIOS_DEBUG, "%s: Invalid lane %d\n", __func__, lane);
return -ERR_INVALID_LANE;
}
return 0;
}
/*
* Read supported bandwidth type
* param dp pointer to main s5p-dp structure
* param bandwidth pointer to variable holding bandwidth type
*/
static void s5p_dp_get_max_rx_bandwidth(struct s5p_dp_device *dp,
u8 *bandwidth)
{
u8 data;
/*
* For DP rev.1.1, Maximum link rate of Main Link lanes
* 0x06 = 1.62 Gbps, 0x0a = 2.7 Gbps
*/
s5p_dp_read_byte_from_dpcd(dp, DPCD_ADDR_MAX_LINK_RATE, &data);
*bandwidth = data;
}
/*
* Reset DP and prepare DP for init training
* param dp pointer to main s5p-dp structure
* param lane_count pointer to variable holding no of lanes
*/
static void s5p_dp_get_max_rx_lane_count(struct s5p_dp_device *dp,
u8 *lane_count)
{
u8 data;
/*
* For DP rev.1.1, Maximum number of Main Link lanes
* 0x01 = 1 lane, 0x02 = 2 lanes, 0x04 = 4 lanes
*/
s5p_dp_read_byte_from_dpcd(dp, DPCD_ADDR_MAX_LANE_COUNT, &data);
*lane_count = data & DPCD_MAX_LANE_COUNT_MASK;
}
/*
* DP H/w Link Training. Set DPCD link rate and bandwidth.
* param dp pointer to main s5p-dp structure
* param max_lane No of lanes
* param max_rate bandwidth
* return status
*/
static int s5p_dp_hw_link_training(struct s5p_dp_device *dp,
unsigned int max_lane,
unsigned int max_rate)
{
int pll_is_locked = 0;
u32 data;
u32 start;
int lane;
struct exynos5_dp *base = dp->base;
/* Stop Video */
clrbits_le32(&base->video_ctl_1, VIDEO_EN);
start = get_timer(0);
while ((pll_is_locked = s5p_dp_get_pll_lock_status(dp)) == PLL_UNLOCKED) {
if (get_timer(start) > PLL_LOCK_TIMEOUT) {
/* Ignore this error, and try to continue */
printk(BIOS_ERR, "PLL is not locked yet.\n");
break;
}
}
printk(BIOS_SPEW, "PLL is %slocked\n",
pll_is_locked == PLL_LOCKED ? "": "not ");
/* Reset Macro */
setbits_le32(&base->dp_phy_test, MACRO_RST);
/* 10 us is the minimum reset time. */
udelay(10);
clrbits_le32(&base->dp_phy_test, MACRO_RST);
/* Set TX pre-emphasis to minimum */
for (lane = 0; lane < max_lane; lane++)
if (s5p_dp_set_lane_lane_pre_emphasis(dp,
PRE_EMPHASIS_LEVEL_0, lane)) {
printk(BIOS_DEBUG, "Unable to set pre emphasis level\n");
return -ERR_PRE_EMPHASIS_LEVELS;
}
/* All DP analog module power up */
writel(0x00, &base->dp_phy_pd);
/* Initialize by reading RX's DPCD */
s5p_dp_get_max_rx_bandwidth(dp, &dp->link_train.link_rate);
s5p_dp_get_max_rx_lane_count(dp, &dp->link_train.lane_count);
printk(BIOS_SPEW, "%s: rate 0x%x, lane_count %d\n", __func__,
dp->link_train.link_rate, dp->link_train.lane_count);
if ((dp->link_train.link_rate != LINK_RATE_1_62GBPS) &&
(dp->link_train.link_rate != LINK_RATE_2_70GBPS)) {
printk(BIOS_DEBUG, "Rx Max Link Rate is abnormal :%x !\n",
dp->link_train.link_rate);
/* Not Retrying */
return -ERR_LINK_RATE_ABNORMAL;
}
if (dp->link_train.lane_count == 0) {
printk(BIOS_DEBUG, "Rx Max Lane count is abnormal :%x !\n",
dp->link_train.lane_count);
/* Not retrying */
return -ERR_MAX_LANE_COUNT_ABNORMAL;
}
/* Setup TX lane count & rate */
if (dp->link_train.lane_count > max_lane)
dp->link_train.lane_count = max_lane;
if (dp->link_train.link_rate > max_rate)
dp->link_train.link_rate = max_rate;
/* Set link rate and count as you want to establish*/
writel(dp->link_train.lane_count, &base->lane_count_set);
writel(dp->link_train.link_rate, &base->link_bw_set);
/* Set sink to D0 (Sink Not Ready) mode. */
s5p_dp_write_byte_to_dpcd(dp, DPCD_ADDR_SINK_POWER_STATE,
DPCD_SET_POWER_STATE_D0);
/* Start HW link training */
writel(HW_TRAINING_EN, &base->dp_hw_link_training);
/* Wait until HW link training done */
s5p_dp_wait_hw_link_training_done(dp);
/* Get hardware link training status */
data = readl(&base->dp_hw_link_training);
printk(BIOS_SPEW, "hardware link training status: 0x%08x\n", data);
if (data != 0) {
printk(BIOS_ERR, " H/W link training failure: 0x%x\n", data);
return -ERR_LINK_TRAINING_FAILURE;
}
/* Get Link Bandwidth */
data = readl(&base->link_bw_set);
dp->link_train.link_rate = data;
data = readl(&base->lane_count_set);
dp->link_train.lane_count = data;
printk(BIOS_SPEW, "Done training: Link bandwidth: 0x%x, lane_count: %d\n",
dp->link_train.link_rate, data);
return 0;
}
/*
* Initialize DP display
*/
int dp_controller_init(struct s5p_dp_device *dp_device)
{
int ret;
struct s5p_dp_device *dp = dp_device;
struct exynos5_dp *base;
clock_init_dp_clock();
power_enable_dp_phy();
ret = s5p_dp_init_dp(dp);
if (ret) {
printk(BIOS_ERR, "%s: Could not initialize dp\n", __func__);
return ret;
}
ret = s5p_dp_hw_link_training(dp, dp->video_info->lane_count,
dp->video_info->link_rate);
if (ret) {
printk(BIOS_ERR, "unable to do link train\n");
return ret;
}
/* Minimum delay after H/w Link training */
udelay(1000);
ret = s5p_dp_enable_scramble(dp);
if (ret) {
printk(BIOS_ERR, "unable to set scramble mode\n");
return ret;
}
ret = s5p_dp_enable_rx_to_enhanced_mode(dp);
if (ret) {
printk(BIOS_ERR, "unable to set enhanced mode\n");
return ret;
}
base = dp->base;
/* Enable enhanced mode */
setbits_le32(&base->sys_ctl_4, ENHANCED);
writel(dp->link_train.lane_count, &base->lane_count_set);
writel(dp->link_train.link_rate, &base->link_bw_set);
s5p_dp_init_video(dp);
ret = s5p_dp_config_video(dp, dp->video_info);
if (ret) {
printk(BIOS_ERR, "unable to config video\n");
return ret;
}
return 0;
}
/**
* Init the LCD controller
*
* @param lcdbase Base address of LCD frame buffer
* @return 0 if ok, -ve error code on error
*/
int lcd_ctrl_init(vidinfo_t *panel_info,
struct exynos5_fimd_panel *panel_data, void *lcdbase)
{
int ret = 0;
fimd_bypass();
fb_init(panel_info, lcdbase, panel_data);
return ret;
}

View File

@ -0,0 +1,138 @@
/*
* This file is part of the coreboot project.
*
* Copyright (C) 2012 Samsung Electronics
*
* 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; version 2 of the License.
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/* Register map for Exynos5 FIMD */
#ifndef CPU_SAMSUNG_EXYNOS5420_FIMD_H
#define CPU_SAMSUNG_EXYNOS5420_FIMD_H
/* FIMD register map */
struct exynos5_fimd {
/* This is an incomplete list. Add registers as and when required */
unsigned int vidcon0;
unsigned char res1[0x1c];
unsigned int wincon0;
unsigned int wincon1;
unsigned int wincon2;
unsigned int wincon3;
unsigned int wincon4;
unsigned int shadowcon;
unsigned char res2[0x8];
unsigned int vidosd0a;
unsigned int vidosd0b;
unsigned int vidosd0c;
unsigned char res3[0x54];
unsigned int vidw00add0b0;
unsigned char res4[0x2c];
unsigned int vidw00add1b0;
unsigned char res5[0x2c];
unsigned int vidw00add2;
unsigned char res6[0x3c];
unsigned int w1keycon0;
unsigned int w1keycon1;
unsigned int w2keycon0;
unsigned int w2keycon1;
unsigned int w3keycon0;
unsigned int w3keycon1;
unsigned int w4keycon0;
unsigned int w4keycon1;
unsigned char res7[0x20];
unsigned int win0map;
unsigned char res8[0xdc];
unsigned int blendcon;
unsigned char res9[0x18];
unsigned int dpclkcon;
};
#define W0_SHADOW_PROTECT (0x1 << 10)
#define COMPKEY_F 0xffffff
#define ENVID_F_ON (0x1 << 0)
#define ENVID_ON (0x1 << 1)
#define CLKVAL_F 0xb
#define CLKVAL_F_OFFSET 6
/*
* Structure containing display panel specific data for FIMD
*/
struct exynos5_fimd_panel {
unsigned int is_dp:1; /* Display Panel interface is eDP */
unsigned int is_mipi:1; /* Display Panel interface is MIPI */
unsigned int fixvclk:2; /* VCLK hold scheme at data underflow */
/*
* Polarity of the VCLK active edge
* 0-falling
* 1-rising
*/
unsigned int ivclk:1;
unsigned int clkval_f; /* Divider to create pixel clock */
unsigned int upper_margin; /* Vertical Backporch */
unsigned int lower_margin; /* Vertical frontporch */
unsigned int vsync; /* Vertical Sync Pulse Width */
unsigned int left_margin; /* Horizantal Backporch */
unsigned int right_margin; /* Horizontal Frontporch */
unsigned int hsync; /* Horizontal Sync Pulse Width */
unsigned int xres; /* X Resolution */
unsigned int yres; /* Y Resopultion */
};
/* LCDIF Register Map */
struct exynos5_disp_ctrl {
unsigned int vidout_con;
unsigned int vidcon1;
unsigned char res1[0x8];
unsigned int vidtcon0;
unsigned int vidtcon1;
unsigned int vidtcon2;
unsigned int vidtcon3;
unsigned char res2[0x184];
unsigned int trigcon;
};
#define VCLK_RISING_EDGE (1 << 7)
#define VCLK_RUNNING (1 << 9)
#define CHANNEL0_EN (1 << 0)
#define VSYNC_PULSE_WIDTH_VAL 0x3
#define VSYNC_PULSE_WIDTH_OFFSET 0
#define V_FRONT_PORCH_VAL 0x3
#define V_FRONT_PORCH_OFFSET 8
#define V_BACK_PORCH_VAL 0x3
#define V_BACK_PORCH_OFFSET 16
#define HSYNC_PULSE_WIDTH_VAL 0x3
#define HSYNC_PULSE_WIDTH_OFFSET 0
#define H_FRONT_PORCH_VAL 0x3
#define H_FRONT_PORCH_OFFSET 8
#define H_BACK_PORCH_VAL 0x3
#define H_BACK_PORCH_OFFSET 16
#define HOZVAL_OFFSET 0
#define LINEVAL_OFFSET 11
#define BPPMODE_F_RGB_16BIT_565 0x5
#define BPPMODE_F_OFFSET 2
#define ENWIN_F_ENABLE (1 << 0)
#define HALF_WORD_SWAP_EN (1 << 16)
#define OSD_RIGHTBOTX_F_OFFSET 11
#define OSD_RIGHTBOTY_F_OFFSET 0
#endif

View File

@ -0,0 +1,279 @@
/*
* This file is part of the coreboot project.
*
* Copyright (C) 2009 Samsung Electronics
*
* 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; version 2 of the License.
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <console/console.h>
#include <string.h>
#include <delay.h>
#include <assert.h>
#include "gpio.h"
#include "cpu.h"
#define CON_MASK(x) (0xf << ((x) << 2))
#define CON_SFR(x, v) ((v) << ((x) << 2))
#define DAT_MASK(x) (0x1 << (x))
#define DAT_SET(x) (0x1 << (x))
#define PULL_MASK(x) (0x3 << ((x) << 1))
#define PULL_MODE(x, v) ((v) << ((x) << 1))
#define DRV_MASK(x) (0x3 << ((x) << 1))
#define DRV_SET(x, m) ((m) << ((x) << 1))
#define RATE_MASK(x) (0x1 << (x + 16))
#define RATE_SET(x) (0x1 << (x + 16))
struct gpio_info {
unsigned int reg_addr; /* Address of register for this part */
unsigned int max_gpio; /* Maximum GPIO in this part */
};
static const struct gpio_info gpio_data[EXYNOS_GPIO_NUM_PARTS] = {
{ EXYNOS5_GPIO_PART1_BASE, GPIO_MAX_PORT_PART_1 },
{ EXYNOS5_GPIO_PART2_BASE, GPIO_MAX_PORT_PART_2 },
{ EXYNOS5_GPIO_PART3_BASE, GPIO_MAX_PORT_PART_3 },
{ EXYNOS5_GPIO_PART4_BASE, GPIO_MAX_PORT_PART_4 },
{ EXYNOS5_GPIO_PART5_BASE, GPIO_MAX_PORT_PART_5 },
{ EXYNOS5_GPIO_PART6_BASE, GPIO_MAX_PORT },
};
/* This macro gets gpio pin offset from 0..7 */
#define GPIO_BIT(x) ((x) & 0x7)
static struct gpio_bank *gpio_get_bank(unsigned int gpio)
{
const struct gpio_info *data;
unsigned int upto;
int i;
for (i = upto = 0, data = gpio_data; i < EXYNOS_GPIO_NUM_PARTS;
i++, upto = data->max_gpio, data++) {
if (gpio < data->max_gpio) {
struct gpio_bank *bank;
bank = (struct gpio_bank *)data->reg_addr;
bank += (gpio - upto) / GPIO_PER_BANK;
return bank;
}
}
ASSERT(gpio < GPIO_MAX_PORT); /* ...which it will not be */
return NULL;
}
/* Common GPIO API - only available on Exynos5 */
void gpio_cfg_pin(int gpio, int cfg)
{
unsigned int value;
struct gpio_bank *bank = gpio_get_bank(gpio);
value = readl(&bank->con);
value &= ~CON_MASK(GPIO_BIT(gpio));
value |= CON_SFR(GPIO_BIT(gpio), cfg);
writel(value, &bank->con);
}
static int gpio_get_cfg(int gpio)
{
struct gpio_bank *bank = gpio_get_bank(gpio);
int shift = GPIO_BIT(gpio) << 2;
return (readl(&bank->con) & CON_MASK(GPIO_BIT(gpio))) >> shift;
}
void gpio_set_pull(int gpio, int mode)
{
unsigned int value;
struct gpio_bank *bank = gpio_get_bank(gpio);
value = readl(&bank->pull);
value &= ~PULL_MASK(GPIO_BIT(gpio));
switch (mode) {
case GPIO_PULL_DOWN:
case GPIO_PULL_UP:
value |= PULL_MODE(GPIO_BIT(gpio), mode);
break;
default:
break;
}
writel(value, &bank->pull);
}
void gpio_set_drv(int gpio, int mode)
{
unsigned int value;
struct gpio_bank *bank = gpio_get_bank(gpio);
value = readl(&bank->drv);
value &= ~DRV_MASK(GPIO_BIT(gpio));
switch (mode) {
case GPIO_DRV_1X:
case GPIO_DRV_2X:
case GPIO_DRV_3X:
case GPIO_DRV_4X:
value |= DRV_SET(GPIO_BIT(gpio), mode);
break;
default:
return;
}
writel(value, &bank->drv);
}
void gpio_set_rate(int gpio, int mode)
{
unsigned int value;
struct gpio_bank *bank = gpio_get_bank(gpio);
value = readl(&bank->drv);
value &= ~RATE_MASK(GPIO_BIT(gpio));
switch (mode) {
case GPIO_DRV_FAST:
case GPIO_DRV_SLOW:
value |= RATE_SET(GPIO_BIT(gpio));
break;
default:
return;
}
writel(value, &bank->drv);
}
int gpio_direction_input(unsigned gpio)
{
gpio_cfg_pin(gpio, GPIO_INPUT);
return 0;
}
int gpio_direction_output(unsigned gpio, int value)
{
unsigned int val;
struct gpio_bank *bank = gpio_get_bank(gpio);
gpio_cfg_pin(gpio, GPIO_OUTPUT);
val = readl(&bank->dat);
val &= ~DAT_MASK(GPIO_BIT(gpio));
if (value)
val |= DAT_SET(GPIO_BIT(gpio));
writel(val, &bank->dat);
return 0;
}
int gpio_get_value(unsigned gpio)
{
unsigned int value;
struct gpio_bank *bank = gpio_get_bank(gpio);
value = readl(&bank->dat);
return !!(value & DAT_MASK(GPIO_BIT(gpio)));
}
int gpio_set_value(unsigned gpio, int value)
{
unsigned int val;
struct gpio_bank *bank = gpio_get_bank(gpio);
val = readl(&bank->dat);
val &= ~DAT_MASK(GPIO_BIT(gpio));
if (value)
val |= DAT_SET(GPIO_BIT(gpio));
writel(val, &bank->dat);
return 0;
}
/*
* Add a delay here to give the lines time to settle
* TODO(sjg): 1us does not always work, 2 is stable, so use 5 to be safe
* Come back to this and sort out what the datasheet says
*/
#define GPIO_DELAY_US 5
#ifndef __BOOT_BLOCK__
/*
* FIXME(dhendrix): These functions use udelay, which has dependencies on
* pwm code and timer code. These aren't necessary for the bootblock and
* bloat the image significantly.
*/
int gpio_read_mvl3(unsigned gpio)
{
int high, low;
enum mvl3 value;
if (gpio >= GPIO_MAX_PORT)
return -1;
gpio_direction_input(gpio);
gpio_set_pull(gpio, GPIO_PULL_UP);
udelay(GPIO_DELAY_US);
high = gpio_get_value(gpio);
gpio_set_pull(gpio, GPIO_PULL_DOWN);
udelay(GPIO_DELAY_US);
low = gpio_get_value(gpio);
if (high && low) /* external pullup */
value = LOGIC_1;
else if (!high && !low) /* external pulldown */
value = LOGIC_0;
else /* floating */
value = LOGIC_Z;
/*
* Check if line is externally pulled high and
* configure the internal pullup to match. For
* floating and pulldowns, the GPIO is already
* configured with an internal pulldown from the
* above test.
*/
if (value == LOGIC_1)
gpio_set_pull(gpio, GPIO_PULL_UP);
return value;
}
#endif /* __BOOT_BLOCK__ */
/*
* Display Exynos GPIO information
*/
void gpio_info(void)
{
unsigned gpio;
for (gpio = 0; gpio < GPIO_MAX_PORT; gpio++) {
int cfg = gpio_get_cfg(gpio);
printk(BIOS_INFO, "GPIO_%-3d: ", gpio);
if (cfg == GPIO_INPUT)
printk(BIOS_INFO, "input");
else if (cfg == GPIO_OUTPUT)
printk(BIOS_INFO, "output");
else
printk(BIOS_INFO, "func %d", cfg);
if (cfg == GPIO_INPUT || cfg == GPIO_OUTPUT)
printk(BIOS_INFO, ", value = %d", gpio_get_value(gpio));
printk(BIOS_INFO, "\n");
}
}

View File

@ -0,0 +1,578 @@
/*
* This file is part of the coreboot project.
*
* Copyright (C) 2010 Samsung Electronics
*
* 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; version 2 of the License.
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef CPU_SAMSUNG_EXYNOS5420_GPIO_H
#define CPU_SAMSUNG_EXYNOS5420_GPIO_H
struct gpio_bank {
unsigned int con;
unsigned int dat;
unsigned int pull;
unsigned int drv;
unsigned int pdn_con;
unsigned int pdn_pull;
unsigned char res1[8];
};
/* GPIO pins per bank */
#define GPIO_PER_BANK 8
/* Pin configurations */
#define GPIO_INPUT 0x0
#define GPIO_OUTPUT 0x1
#define GPIO_IRQ 0xf
#define GPIO_FUNC(x) (x)
/* Pull mode */
#define GPIO_PULL_NONE 0x0
#define GPIO_PULL_DOWN 0x1
#define GPIO_PULL_UP 0x3
/* Drive Strength level */
#define GPIO_DRV_1X 0x0
#define GPIO_DRV_3X 0x1
#define GPIO_DRV_2X 0x2
#define GPIO_DRV_4X 0x3
#define GPIO_DRV_FAST 0x0
#define GPIO_DRV_SLOW 0x1
#define EXYNOS5_GPIO_BASE0 0x11400000
#define EXYNOS5_GPIO_BASE1 0x13400000
#define EXYNOS5_GPIO_BASE2 0x10d10000
#define EXYNOS5_GPIO_BASE3 0x03860000
enum exynos5_gpio_port {
/*
* Ordered by base address + offset.
* ETC registers are special, thus not included.
*/
/* base == EXYNOS_GPIO_BASE0 */
EXYNOS5_GPA0 = EXYNOS5_GPIO_BASE0 + 0x0000,
EXYNOS5_GPA1 = EXYNOS5_GPIO_BASE0 + 0x0020,
EXYNOS5_GPA2 = EXYNOS5_GPIO_BASE0 + 0x0040,
EXYNOS5_GPB0 = EXYNOS5_GPIO_BASE0 + 0x0060,
EXYNOS5_GPB1 = EXYNOS5_GPIO_BASE0 + 0x0080,
EXYNOS5_GPB2 = EXYNOS5_GPIO_BASE0 + 0x00a0,
EXYNOS5_GPB3 = EXYNOS5_GPIO_BASE0 + 0x00c0,
EXYNOS5_GPC0 = EXYNOS5_GPIO_BASE0 + 0x00e0,
EXYNOS5_GPC1 = EXYNOS5_GPIO_BASE0 + 0x0100,
EXYNOS5_GPC2 = EXYNOS5_GPIO_BASE0 + 0x0120,
EXYNOS5_GPC3 = EXYNOS5_GPIO_BASE0 + 0x0140,
EXYNOS5_GPD0 = EXYNOS5_GPIO_BASE0 + 0x0160,
EXYNOS5_GPD1 = EXYNOS5_GPIO_BASE0 + 0x0180,
EXYNOS5_GPY0 = EXYNOS5_GPIO_BASE0 + 0x01a0,
EXYNOS5_GPY1 = EXYNOS5_GPIO_BASE0 + 0x01c0,
EXYNOS5_GPY2 = EXYNOS5_GPIO_BASE0 + 0x01e0,
EXYNOS5_GPY3 = EXYNOS5_GPIO_BASE0 + 0x0200,
EXYNOS5_GPY4 = EXYNOS5_GPIO_BASE0 + 0x0220,
EXYNOS5_GPY5 = EXYNOS5_GPIO_BASE0 + 0x0240,
EXYNOS5_GPY6 = EXYNOS5_GPIO_BASE0 + 0x0260,
EXYNOS5_GPX0 = EXYNOS5_GPIO_BASE0 + 0x0c00,
EXYNOS5_GPX1 = EXYNOS5_GPIO_BASE0 + 0x0c20,
EXYNOS5_GPX2 = EXYNOS5_GPIO_BASE0 + 0x0c40,
EXYNOS5_GPX3 = EXYNOS5_GPIO_BASE0 + 0x0c60,
/* base == EXYNOS_GPIO_BASE1 */
EXYNOS5_GPE0 = EXYNOS5_GPIO_BASE1 + 0x0000,
EXYNOS5_GPE1 = EXYNOS5_GPIO_BASE1 + 0x0020,
EXYNOS5_GPF0 = EXYNOS5_GPIO_BASE1 + 0x0040,
EXYNOS5_GPF1 = EXYNOS5_GPIO_BASE1 + 0x0060,
EXYNOS5_GPG0 = EXYNOS5_GPIO_BASE1 + 0x0080,
EXYNOS5_GPG1 = EXYNOS5_GPIO_BASE1 + 0x00a0,
EXYNOS5_GPG2 = EXYNOS5_GPIO_BASE1 + 0x00c0,
EXYNOS5_GPH0 = EXYNOS5_GPIO_BASE1 + 0x00e0,
EXYNOS5_GPH1 = EXYNOS5_GPIO_BASE1 + 0x0100,
/* base == EXYNOS_GPIO_BASE2 */
EXYNOS5_GPV0 = EXYNOS5_GPIO_BASE2 + 0x0000,
EXYNOS5_GPV1 = EXYNOS5_GPIO_BASE2 + 0x0020,
EXYNOS5_GPV2 = EXYNOS5_GPIO_BASE2 + 0x0060,
EXYNOS5_GPV3 = EXYNOS5_GPIO_BASE2 + 0x0080,
EXYNOS5_GPV4 = EXYNOS5_GPIO_BASE2 + 0x00c0,
/* base == EXYNOS_GPIO_BASE3 */
EXYNOS5_GPZ = EXYNOS5_GPIO_BASE3 + 0x0000,
};
enum {
/* GPIO banks are split into this many parts */
EXYNOS_GPIO_NUM_PARTS = 6
};
/* A list of valid GPIO numbers for the asm-generic/gpio.h interface */
enum exynos5_gpio_pin {
/* GPIO_PART1_STARTS */
GPIO_A00,
GPIO_A01,
GPIO_A02,
GPIO_A03,
GPIO_A04,
GPIO_A05,
GPIO_A06,
GPIO_A07,
GPIO_A10,
GPIO_A11,
GPIO_A12,
GPIO_A13,
GPIO_A14,
GPIO_A15,
GPIO_A16,
GPIO_A17,
GPIO_A20,
GPIO_A21,
GPIO_A22,
GPIO_A23,
GPIO_A24,
GPIO_A25,
GPIO_A26,
GPIO_A27,
GPIO_B00, /* 0x18 */
GPIO_B01,
GPIO_B02,
GPIO_B03,
GPIO_B04,
GPIO_B05,
GPIO_B06,
GPIO_B07,
GPIO_B10,
GPIO_B11,
GPIO_B12,
GPIO_B13,
GPIO_B14,
GPIO_B15,
GPIO_B16,
GPIO_B17,
GPIO_B20,
GPIO_B21,
GPIO_B22,
GPIO_B23,
GPIO_B24,
GPIO_B25,
GPIO_B26,
GPIO_B27,
GPIO_B30,
GPIO_B31,
GPIO_B32,
GPIO_B33,
GPIO_B34,
GPIO_B35,
GPIO_B36,
GPIO_B37,
GPIO_C00, /* 0x38 */
GPIO_C01,
GPIO_C02,
GPIO_C03,
GPIO_C04,
GPIO_C05,
GPIO_C06,
GPIO_C07,
GPIO_C10,
GPIO_C11,
GPIO_C12,
GPIO_C13,
GPIO_C14,
GPIO_C15,
GPIO_C16,
GPIO_C17,
GPIO_C20,
GPIO_C21,
GPIO_C22,
GPIO_C23,
GPIO_C24,
GPIO_C25,
GPIO_C26,
GPIO_C27,
GPIO_C30,
GPIO_C31,
GPIO_C32,
GPIO_C33,
GPIO_C34,
GPIO_C35,
GPIO_C36,
GPIO_C37,
GPIO_D00, /* 0x58 */
GPIO_D01,
GPIO_D02,
GPIO_D03,
GPIO_D04,
GPIO_D05,
GPIO_D06,
GPIO_D07,
GPIO_D10,
GPIO_D11,
GPIO_D12,
GPIO_D13,
GPIO_D14,
GPIO_D15,
GPIO_D16,
GPIO_D17,
GPIO_Y00, /* 0x68 */
GPIO_Y01,
GPIO_Y02,
GPIO_Y03,
GPIO_Y04,
GPIO_Y05,
GPIO_Y06,
GPIO_Y07,
GPIO_Y10,
GPIO_Y11,
GPIO_Y12,
GPIO_Y13,
GPIO_Y14,
GPIO_Y15,
GPIO_Y16,
GPIO_Y17,
GPIO_Y20,
GPIO_Y21,
GPIO_Y22,
GPIO_Y23,
GPIO_Y24,
GPIO_Y25,
GPIO_Y26,
GPIO_Y27,
GPIO_Y30,
GPIO_Y31,
GPIO_Y32,
GPIO_Y33,
GPIO_Y34,
GPIO_Y35,
GPIO_Y36,
GPIO_Y37,
GPIO_Y40,
GPIO_Y41,
GPIO_Y42,
GPIO_Y43,
GPIO_Y44,
GPIO_Y45,
GPIO_Y46,
GPIO_Y47,
GPIO_Y50,
GPIO_Y51,
GPIO_Y52,
GPIO_Y53,
GPIO_Y54,
GPIO_Y55,
GPIO_Y56,
GPIO_Y57,
GPIO_Y60,
GPIO_Y61,
GPIO_Y62,
GPIO_Y63,
GPIO_Y64,
GPIO_Y65,
GPIO_Y66,
GPIO_Y67,
/* GPIO_PART2_STARTS */
GPIO_MAX_PORT_PART_1,
GPIO_X00 = GPIO_MAX_PORT_PART_1, /* 0xa0 */
GPIO_X01,
GPIO_X02,
GPIO_X03,
GPIO_X04,
GPIO_X05,
GPIO_X06,
GPIO_X07,
GPIO_X10,
GPIO_X11,
GPIO_X12,
GPIO_X13,
GPIO_X14,
GPIO_X15,
GPIO_X16,
GPIO_X17,
GPIO_X20,
GPIO_X21,
GPIO_X22,
GPIO_X23,
GPIO_X24,
GPIO_X25,
GPIO_X26,
GPIO_X27,
GPIO_X30,
GPIO_X31,
GPIO_X32,
GPIO_X33,
GPIO_X34,
GPIO_X35,
GPIO_X36,
GPIO_X37,
/* GPIO_PART3_STARTS */
GPIO_MAX_PORT_PART_2,
GPIO_E00 = GPIO_MAX_PORT_PART_2, /* 0xc0 */
GPIO_E01,
GPIO_E02,
GPIO_E03,
GPIO_E04,
GPIO_E05,
GPIO_E06,
GPIO_E07,
GPIO_E10,
GPIO_E11,
GPIO_E12,
GPIO_E13,
GPIO_E14,
GPIO_E15,
GPIO_E16,
GPIO_E17,
GPIO_F00, /* 0xd0 */
GPIO_F01,
GPIO_F02,
GPIO_F03,
GPIO_F04,
GPIO_F05,
GPIO_F06,
GPIO_F07,
GPIO_F10,
GPIO_F11,
GPIO_F12,
GPIO_F13,
GPIO_F14,
GPIO_F15,
GPIO_F16,
GPIO_F17,
GPIO_G00,
GPIO_G01,
GPIO_G02,
GPIO_G03,
GPIO_G04,
GPIO_G05,
GPIO_G06,
GPIO_G07,
GPIO_G10,
GPIO_G11,
GPIO_G12,
GPIO_G13,
GPIO_G14,
GPIO_G15,
GPIO_G16,
GPIO_G17,
GPIO_G20,
GPIO_G21,
GPIO_G22,
GPIO_G23,
GPIO_G24,
GPIO_G25,
GPIO_G26,
GPIO_G27,
GPIO_H00,
GPIO_H01,
GPIO_H02,
GPIO_H03,
GPIO_H04,
GPIO_H05,
GPIO_H06,
GPIO_H07,
GPIO_H10,
GPIO_H11,
GPIO_H12,
GPIO_H13,
GPIO_H14,
GPIO_H15,
GPIO_H16,
GPIO_H17,
/* GPIO_PART4_STARTS */
GPIO_MAX_PORT_PART_3,
GPIO_V00 = GPIO_MAX_PORT_PART_3,
GPIO_V01,
GPIO_V02,
GPIO_V03,
GPIO_V04,
GPIO_V05,
GPIO_V06,
GPIO_V07,
GPIO_V10,
GPIO_V11,
GPIO_V12,
GPIO_V13,
GPIO_V14,
GPIO_V15,
GPIO_V16,
GPIO_V17,
GPIO_V20,
GPIO_V21,
GPIO_V22,
GPIO_V23,
GPIO_V24,
GPIO_V25,
GPIO_V26,
GPIO_V27,
GPIO_V30,
GPIO_V31,
GPIO_V32,
GPIO_V33,
GPIO_V34,
GPIO_V35,
GPIO_V36,
GPIO_V37,
/* GPIO_PART5_STARTS */
GPIO_MAX_PORT_PART_4,
GPIO_V40 = GPIO_MAX_PORT_PART_4,
GPIO_V41,
GPIO_V42,
GPIO_V43,
GPIO_V44,
GPIO_V45,
GPIO_V46,
GPIO_V47,
/* GPIO_PART6_STARTS */
GPIO_MAX_PORT_PART_5,
GPIO_Z0 = GPIO_MAX_PORT_PART_5,
GPIO_Z1,
GPIO_Z2,
GPIO_Z3,
GPIO_Z4,
GPIO_Z5,
GPIO_Z6,
GPIO_MAX_PORT
};
/**
* Set GPIO pin configuration.
*
* @param gpio GPIO pin
* @param cfg Either GPIO_INPUT, GPIO_OUTPUT, or GPIO_IRQ
*/
void gpio_cfg_pin(int gpio, int cfg);
/**
* Set GPIO pull mode.
*
* @param gpio GPIO pin
* @param mode Either GPIO_PULL_DOWN or GPIO_PULL_UP
*/
void gpio_set_pull(int gpio, int mode);
/**
* Set GPIO drive strength level.
*
* @param gpio GPIO pin
* @param mode Either GPIO_DRV_1X, GPIO_DRV_2X, GPIO_DRV_3X, or GPIO_DRV_4X
*/
void gpio_set_drv(int gpio, int mode);
/**
* Set GPIO drive rate.
*
* @param gpio GPIO pin
* @param mode Either GPIO_DRV_FAST or GPIO_DRV_SLOW
*/
void gpio_set_rate(int gpio, int mode);
/*
* reads only a single GPIO
*
* @param gpio GPIO to read
* @return -1 if the value cannot be determined. Otherwise returns
* the corresponding MVL3 enum value.
*/
int gpio_read_mvl3(unsigned gpio);
void gpio_info(void);
/*
* Generic GPIO API for U-Boot
*
* GPIOs are numbered from 0 to GPIO_COUNT-1 which value is defined
* by the SOC/architecture.
*
* Each GPIO can be an input or output. If an input then its value can
* be read as 0 or 1. If an output then its value can be set to 0 or 1.
* If you try to write an input then the value is undefined. If you try
* to read an output, barring something very unusual, you will get
* back the value of the output that you previously set.
*
* In some cases the operation may fail, for example if the GPIO number
* is out of range, or the GPIO is not available because its pin is
* being used by another function. In that case, functions may return
* an error value of -1.
*/
/**
* Make a GPIO an input.
*
* @param gpio GPIO number
* @return 0 if ok, -1 on error
*/
int gpio_direction_input(unsigned gpio);
/**
* Make a GPIO an output, and set its value.
*
* @param gpio GPIO number
* @param value GPIO value (0 for low or 1 for high)
* @return 0 if ok, -1 on error
*/
int gpio_direction_output(unsigned gpio, int value);
/**
* Get a GPIO's value. This will work whether the GPIO is an input
* or an output.
*
* @param gpio GPIO number
* @return 0 if low, 1 if high, -1 on error
*/
int gpio_get_value(unsigned gpio);
/**
* Set an output GPIO's value. The GPIO must already be an output or
* this function may have no effect.
*
* @param gpio GPIO number
* @param value GPIO value (0 for low or 1 for high)
* @return 0 if ok, -1 on error
*/
int gpio_set_value(unsigned gpio, int value);
/*
* Many-value logic (3 states). This can be used for inputs whereby presence
* of external pull-up or pull-down resistors can be added to overcome internal
* pull-ups/pull-downs and force a single value.
*
* Thus, external pull resistors can force a 0 or 1 and if the value changes
* along with internal pull-up/down enable then the input is floating.
*
* Vpd | Vpu | MVL
* -----------------
* 0 | 0 | 0
* -----------------
* 0 | 1 | Z <-- floating input will follow internal pull up/down
* -----------------
* 1 | 1 | 1
*/
enum mvl3 {
LOGIC_0,
LOGIC_1,
LOGIC_Z, /* high impedence / tri-stated / floating */
};
#endif /* CPU_SAMSUNG_EXYNOS5420_GPIO_H */

View File

@ -0,0 +1,405 @@
/*
* This file is part of the coreboot project.
*
* (C) Copyright 2002
* David Mueller, ELSOFT AG, d.mueller@elsoft.ch
*
* 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; version 2 of the License.
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <console/console.h>
#include <delay.h>
#include <arch/io.h>
#include <device/i2c.h>
#include "clk.h"
#include "i2c.h"
#include "pinmux.h"
#define I2C_WRITE 0
#define I2C_READ 1
#define I2C_OK 0
#define I2C_NOK 1
#define I2C_NACK 2
#define I2C_NOK_LA 3 /* Lost arbitration */
#define I2C_NOK_TOUT 4 /* time out */
#define I2CSTAT_BSY 0x20 /* Busy bit */
#define I2CSTAT_NACK 0x01 /* Nack bit */
#define I2CCON_ACKGEN 0x80 /* Acknowledge generation */
#define I2CCON_IRPND 0x10 /* Interrupt pending bit */
#define I2C_MODE_MT 0xC0 /* Master Transmit Mode */
#define I2C_MODE_MR 0x80 /* Master Receive Mode */
#define I2C_START_STOP 0x20 /* START / STOP */
#define I2C_TXRX_ENA 0x10 /* I2C Tx/Rx enable */
/* The timeouts we live by */
enum {
I2C_XFER_TIMEOUT_MS = 35, /* xfer to complete */
I2C_INIT_TIMEOUT_MS = 1000, /* bus free on init */
I2C_IDLE_TIMEOUT_MS = 100, /* waiting for bus idle */
I2C_STOP_TIMEOUT_US = 200, /* waiting for stop events */
};
static struct s3c24x0_i2c_bus i2c_buses[] = {
{
.bus_num = 0,
.regs = (struct s3c24x0_i2c *)0x12c60000,
.periph_id = PERIPH_ID_I2C0,
},
{
.bus_num = 1,
.regs = (struct s3c24x0_i2c *)0x12c70000,
.periph_id = PERIPH_ID_I2C1,
},
{
.bus_num = 2,
.regs = (struct s3c24x0_i2c *)0x12c80000,
.periph_id = PERIPH_ID_I2C2,
},
{
.bus_num = 3,
.regs = (struct s3c24x0_i2c *)0x12c90000,
.periph_id = PERIPH_ID_I2C3,
},
{
.bus_num = 4,
.regs = (struct s3c24x0_i2c *)0x12ca0000,
.periph_id = PERIPH_ID_I2C4,
},
{
.bus_num = 5,
.regs = (struct s3c24x0_i2c *)0x12cb0000,
.periph_id = PERIPH_ID_I2C5,
},
{
.bus_num = 6,
.regs = (struct s3c24x0_i2c *)0x12cc0000,
.periph_id = PERIPH_ID_I2C6,
},
{
.bus_num = 7,
.regs = (struct s3c24x0_i2c *)0x12cd0000,
.periph_id = PERIPH_ID_I2C7,
},
};
static int WaitForXfer(struct s3c24x0_i2c *i2c)
{
int i;
i = I2C_XFER_TIMEOUT_MS * 20;
while (!(readl(&i2c->iiccon) & I2CCON_IRPND)) {
if (i == 0) {
printk(BIOS_ERR, "%s: i2c xfer timeout\n", __func__);
return I2C_NOK_TOUT;
}
udelay(50);
i--;
}
return I2C_OK;
}
static int IsACK(struct s3c24x0_i2c *i2c)
{
return !(readl(&i2c->iicstat) & I2CSTAT_NACK);
}
static void ReadWriteByte(struct s3c24x0_i2c *i2c)
{
uint32_t x;
x = readl(&i2c->iiccon);
writel(x & ~I2CCON_IRPND, &i2c->iiccon);
}
static void i2c_ch_init(struct s3c24x0_i2c_bus *bus, int speed, int slaveadd)
{
unsigned long freq, pres = 16, div;
unsigned long val;
freq = clock_get_periph_rate(bus->periph_id);
/* calculate prescaler and divisor values */
if ((freq / pres / (16 + 1)) > speed)
/* set prescaler to 512 */
pres = 512;
div = 0;
while ((freq / pres / (div + 1)) > speed)
div++;
/* set prescaler, divisor according to freq, also set ACKGEN, IRQ */
val = (div & 0x0F) | 0xA0 | ((pres == 512) ? 0x40 : 0);
writel(val, &bus->regs->iiccon);
/* init to SLAVE RECEIVE mode and clear I2CADDn */
writel(0, &bus->regs->iicstat);
writel(slaveadd, &bus->regs->iicadd);
/* program Master Transmit (and implicit STOP) */
writel(I2C_MODE_MT | I2C_TXRX_ENA, &bus->regs->iicstat);
}
/*
* MULTI BUS I2C support
*/
static void i2c_bus_init(struct s3c24x0_i2c_bus *bus, int speed, int slaveadd)
{
exynos_pinmux_config(bus->periph_id, 0);
i2c_ch_init(bus, speed, slaveadd);
}
/*
* Verify the whether I2C ACK was received or not
*
* @param i2c pointer to I2C register base
* @param buf array of data
* @param len length of data
* return I2C_OK when transmission done
* I2C_NACK otherwise
*/
static int i2c_send_verify(struct s3c24x0_i2c *i2c, unsigned char buf[],
unsigned char len)
{
int i, result = I2C_OK;
if (IsACK(i2c)) {
for (i = 0; (i < len) && (result == I2C_OK); i++) {
writel(buf[i], &i2c->iicds);
ReadWriteByte(i2c);
result = WaitForXfer(i2c);
if (result == I2C_OK && !IsACK(i2c))
result = I2C_NACK;
}
} else {
result = I2C_NACK;
}
return result;
}
void i2c_init(unsigned bus_num, int speed, int slaveadd)
{
struct s3c24x0_i2c_bus *i2c;
int i;
i2c = &i2c_buses[bus_num];
i2c_bus_init(i2c, speed, slaveadd);
/* wait for some time to give previous transfer a chance to finish */
i = I2C_INIT_TIMEOUT_MS * 20;
while ((readl(&i2c->regs->iicstat) & I2CSTAT_BSY) && (i > 0)) {
udelay(50);
i--;
}
i2c_ch_init(i2c, speed, slaveadd);
}
/*
* Send a STOP event and wait for it to have completed
*
* @param mode If it is a master transmitter or receiver
* @return I2C_OK if the line became idle before timeout I2C_NOK_TOUT otherwise
*/
static int i2c_send_stop(struct s3c24x0_i2c *i2c, int mode)
{
int timeout;
/* Setting the STOP event to fire */
writel(mode | I2C_TXRX_ENA, &i2c->iicstat);
ReadWriteByte(i2c);
/* Wait for the STOP to send and the bus to go idle */
for (timeout = I2C_STOP_TIMEOUT_US; timeout > 0; timeout -= 5) {
if (!(readl(&i2c->iicstat) & I2CSTAT_BSY))
return I2C_OK;
udelay(5);
}
return I2C_NOK_TOUT;
}
/*
* cmd_type is 0 for write, 1 for read.
*
* addr_len can take any value from 0-255, it is only limited
* by the char, we could make it larger if needed. If it is
* 0 we skip the address write cycle.
*/
static int i2c_transfer(struct s3c24x0_i2c *i2c,
unsigned char cmd_type,
unsigned char chip,
unsigned char addr[],
unsigned char addr_len,
unsigned char data[],
unsigned short data_len)
{
int i, result, stop_bit_result;
uint32_t x;
if (data == 0 || data_len == 0) {
/* Don't support data transfer of no length or to address 0 */
printk(BIOS_ERR, "i2c_transfer: bad call\n");
return I2C_NOK;
}
/* Check I2C bus idle */
i = I2C_IDLE_TIMEOUT_MS * 20;
while ((readl(&i2c->iicstat) & I2CSTAT_BSY) && (i > 0)) {
udelay(50);
i--;
}
if (readl(&i2c->iicstat) & I2CSTAT_BSY) {
printk(BIOS_ERR, "%s: bus busy\n", __func__);
return I2C_NOK_TOUT;
}
x = readl(&i2c->iiccon);
writel(x | I2CCON_ACKGEN, &i2c->iiccon);
if (addr && addr_len) {
writel(chip, &i2c->iicds);
/* send START */
writel(I2C_MODE_MT | I2C_TXRX_ENA | I2C_START_STOP,
&i2c->iicstat);
if (WaitForXfer(i2c) == I2C_OK)
result = i2c_send_verify(i2c, addr, addr_len);
else
result = I2C_NACK;
} else
result = I2C_NACK;
switch (cmd_type) {
case I2C_WRITE:
if (result == I2C_OK)
result = i2c_send_verify(i2c, data, data_len);
else {
writel(chip, &i2c->iicds);
/* send START */
writel(I2C_MODE_MT | I2C_TXRX_ENA | I2C_START_STOP,
&i2c->iicstat);
if (WaitForXfer(i2c) == I2C_OK)
result = i2c_send_verify(i2c, data, data_len);
}
if (result == I2C_OK)
result = WaitForXfer(i2c);
stop_bit_result = i2c_send_stop(i2c, I2C_MODE_MT);
break;
case I2C_READ:
{
int was_ok = (result == I2C_OK);
writel(chip, &i2c->iicds);
/* resend START */
writel(I2C_MODE_MR | I2C_TXRX_ENA |
I2C_START_STOP, &i2c->iicstat);
ReadWriteByte(i2c);
result = WaitForXfer(i2c);
if (was_ok || IsACK(i2c)) {
i = 0;
while ((i < data_len) && (result == I2C_OK)) {
/* disable ACK for final READ */
if (i == data_len - 1) {
x = readl(&i2c->iiccon) & ~I2CCON_ACKGEN;
writel(x, &i2c->iiccon);
}
ReadWriteByte(i2c);
result = WaitForXfer(i2c);
data[i] = readl(&i2c->iicds);
i++;
}
} else {
result = I2C_NACK;
}
stop_bit_result = i2c_send_stop(i2c, I2C_MODE_MR);
break;
}
default:
printk(BIOS_ERR, "i2c_transfer: bad call\n");
result = stop_bit_result = I2C_NOK;
break;
}
/*
* If the transmission went fine, then only the stop bit was left to
* fail. Otherwise, the real failure we're interested in came before
* that, during the actual transmission.
*/
return (result == I2C_OK) ? stop_bit_result : result;
}
int i2c_read(unsigned bus, unsigned chip, unsigned addr,
unsigned alen, unsigned char *buf, unsigned len)
{
struct s3c24x0_i2c_bus *i2c;
unsigned char xaddr[4];
int ret;
if (alen > 4) {
printk(BIOS_ERR, "I2C read: addr len %d not supported\n", alen);
return 1;
}
if (alen > 0) {
xaddr[0] = (addr >> 24) & 0xFF;
xaddr[1] = (addr >> 16) & 0xFF;
xaddr[2] = (addr >> 8) & 0xFF;
xaddr[3] = addr & 0xFF;
}
i2c = &i2c_buses[bus];
ret = i2c_transfer(i2c->regs, I2C_READ, chip << 1, &xaddr[4 - alen],
alen, buf, len);
if (ret) {
printk(BIOS_ERR, "I2c read: failed %d\n", ret);
return 1;
}
return 0;
}
int i2c_write(unsigned bus, unsigned chip, unsigned addr,
unsigned alen, unsigned char *buf, unsigned len)
{
struct s3c24x0_i2c_bus *i2c;
unsigned char xaddr[4];
int ret;
if (alen > 4) {
printk(BIOS_ERR, "I2C write: addr len %d not supported\n",
alen);
return 1;
}
if (alen > 0) {
xaddr[0] = (addr >> 24) & 0xFF;
xaddr[1] = (addr >> 16) & 0xFF;
xaddr[2] = (addr >> 8) & 0xFF;
xaddr[3] = addr & 0xFF;
}
i2c = &i2c_buses[bus];
ret = i2c_transfer(i2c->regs, I2C_WRITE, chip << 1, &xaddr[4 - alen],
alen, buf, len);
return ret != 0;
}

View File

@ -0,0 +1,41 @@
/*
* This file is part of the coreboot project.
*
* Copyright (C) 2012 Samsung Electronics
*
* 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; version 2 of the License.
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef CPU_SAMSUNG_EXYNOS5420_I2C_H
#define CPU_SAMSUNG_EXYNOS5420_I2C_H
#include "periph.h"
struct s3c24x0_i2c {
u32 iiccon;
u32 iicstat;
u32 iicadd;
u32 iicds;
u32 iiclc;
};
struct s3c24x0_i2c_bus {
int bus_num;
struct s3c24x0_i2c *regs;
enum periph_id periph_id;
};
void i2c_init(unsigned bus, int speed, int slaveadd);
#endif /* CPU_SAMSUNG_EXYNOS5420_I2C_H */

View File

@ -0,0 +1,142 @@
/*
* This file is part of the coreboot project.
*
* Copyright (C) 2012 Samsung Electronics
*
* 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; version 2 of the License.
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/* Taken from the kernel code */
#ifndef CPU_SAMSUNG_EXYNOS5420_I2S_REGS_H
#define CPU_SAMSUNG_EXYNOS5420_I2S_REGS_H
#define I2SCON 0x0
#define I2SMOD 0x4
#define I2SFIC 0x8
#define I2SPSR 0xc
#define I2STXD 0x10
#define I2SRXD 0x14
#define I2SFICS 0x18
#define I2STXDS 0x1c
#define I2SAHB 0x20
#define I2SSTR0 0x24
#define I2SSIZE 0x28
#define I2STRNCNT 0x2c
#define I2SLVL0ADDR 0x30
#define I2SLVL1ADDR 0x34
#define I2SLVL2ADDR 0x38
#define I2SLVL3ADDR 0x3c
#define CON_RSTCLR (1 << 31)
#define CON_FRXOFSTATUS (1 << 26)
#define CON_FRXORINTEN (1 << 25)
#define CON_FTXSURSTAT (1 << 24)
#define CON_FTXSURINTEN (1 << 23)
#define CON_TXSDMA_PAUSE (1 << 20)
#define CON_TXSDMA_ACTIVE (1 << 18)
#define CON_FTXURSTATUS (1 << 17)
#define CON_FTXURINTEN (1 << 16)
#define CON_TXFIFO2_EMPTY (1 << 15)
#define CON_TXFIFO1_EMPTY (1 << 14)
#define CON_TXFIFO2_FULL (1 << 13)
#define CON_TXFIFO1_FULL (1 << 12)
#define CON_LRINDEX (1 << 11)
#define CON_TXFIFO_EMPTY (1 << 10)
#define CON_RXFIFO_EMPTY (1 << 9)
#define CON_TXFIFO_FULL (1 << 8)
#define CON_RXFIFO_FULL (1 << 7)
#define CON_TXDMA_PAUSE (1 << 6)
#define CON_RXDMA_PAUSE (1 << 5)
#define CON_TXCH_PAUSE (1 << 4)
#define CON_RXCH_PAUSE (1 << 3)
#define CON_TXDMA_ACTIVE (1 << 2)
#define CON_RXDMA_ACTIVE (1 << 1)
#define CON_ACTIVE (1 << 0)
#define MOD_OPCLK_CDCLK_OUT (0 << 30)
#define MOD_OPCLK_CDCLK_IN (1 << 30)
#define MOD_OPCLK_BCLK_OUT (2 << 30)
#define MOD_OPCLK_PCLK (3 << 30)
#define MOD_OPCLK_MASK (3 << 30)
#define MOD_TXS_IDMA (1 << 28) /* Sec_TXFIFO use I-DMA */
#define MOD_BLCS_SHIFT 26
#define MOD_BLCS_16BIT (0 << MOD_BLCS_SHIFT)
#define MOD_BLCS_8BIT (1 << MOD_BLCS_SHIFT)
#define MOD_BLCS_24BIT (2 << MOD_BLCS_SHIFT)
#define MOD_BLCS_MASK (3 << MOD_BLCS_SHIFT)
#define MOD_BLCP_SHIFT 24
#define MOD_BLCP_16BIT (0 << MOD_BLCP_SHIFT)
#define MOD_BLCP_8BIT (1 << MOD_BLCP_SHIFT)
#define MOD_BLCP_24BIT (2 << MOD_BLCP_SHIFT)
#define MOD_BLCP_MASK (3 << MOD_BLCP_SHIFT)
#define MOD_C2DD_HHALF (1 << 21) /* Discard Higher-half */
#define MOD_C2DD_LHALF (1 << 20) /* Discard Lower-half */
#define MOD_C1DD_HHALF (1 << 19)
#define MOD_C1DD_LHALF (1 << 18)
#define MOD_DC2_EN (1 << 17)
#define MOD_DC1_EN (1 << 16)
#define MOD_BLC_16BIT (0 << 13)
#define MOD_BLC_8BIT (1 << 13)
#define MOD_BLC_24BIT (2 << 13)
#define MOD_BLC_MASK (3 << 13)
#define MOD_IMS_SYSMUX (1 << 10)
#define MOD_SLAVE (1 << 11)
#define MOD_TXONLY (0 << 8)
#define MOD_RXONLY (1 << 8)
#define MOD_TXRX (2 << 8)
#define MOD_MASK (3 << 8)
#define MOD_LR_LLOW (0 << 7)
#define MOD_LR_RLOW (1 << 7)
#define MOD_SDF_IIS (0 << 5)
#define MOD_SDF_MSB (1 << 5)
#define MOD_SDF_LSB (2 << 5)
#define MOD_SDF_MASK (3 << 5)
#define MOD_RCLK_256FS (0 << 3)
#define MOD_RCLK_512FS (1 << 3)
#define MOD_RCLK_384FS (2 << 3)
#define MOD_RCLK_768FS (3 << 3)
#define MOD_RCLK_MASK (3 << 3)
#define MOD_BCLK_32FS (0 << 1)
#define MOD_BCLK_48FS (1 << 1)
#define MOD_BCLK_16FS (2 << 1)
#define MOD_BCLK_24FS (3 << 1)
#define MOD_BCLK_MASK (3 << 1)
#define MOD_8BIT (1 << 0)
#define MOD_CDCLKCON (1 << 12)
#define PSR_PSREN (1 << 15)
#define FIC_TXFLUSH (1 << 15)
#define FIC_RXFLUSH (1 << 7)
#define AHB_INTENLVL0 (1 << 24)
#define AHB_LVL0INT (1 << 20)
#define AHB_CLRLVL0INT (1 << 16)
#define AHB_DMARLD (1 << 5)
#define AHB_INTMASK (1 << 3)
#define AHB_DMAEN (1 << 0)
#define AHB_LVLINTMASK (0xf << 20)
#define I2SSIZE_TRNMSK (0xffff)
#define I2SSIZE_SHIFT (16)
#endif /* CPU_SAMSUNG_EXYNOS5420_I2S_REGS_H */

View File

@ -0,0 +1,104 @@
/*
* This file is part of the coreboot project.
*
* Copyright 2012 Google Inc.
*
* 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; version 2 of the License.
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdint.h>
#include <arch/io.h>
#include "clk.h"
struct __attribute__((packed)) mct_regs
{
uint32_t mct_cfg;
uint8_t reserved0[0xfc];
uint32_t g_cnt_l;
uint32_t g_cnt_u;
uint8_t reserved1[0x8];
uint32_t g_cnt_wstat;
uint8_t reserved2[0xec];
uint32_t g_comp0_l;
uint32_t g_comp0_u;
uint32_t g_comp0_addr_incr;
uint8_t reserved3[0x4];
uint32_t g_comp1_l;
uint32_t g_comp1_u;
uint32_t g_comp1_addr_incr;
uint8_t reserved4[0x4];
uint32_t g_comp2_l;
uint32_t g_comp2_u;
uint32_t g_comp2_addr_incr;
uint8_t reserved5[0x4];
uint32_t g_comp3_l;
uint32_t g_comp3_u;
uint32_t g_comp3_addr_incr;
uint8_t reserved6[0x4];
uint32_t g_tcon;
uint32_t g_int_cstat;
uint32_t g_int_enb;
uint32_t g_wstat;
uint8_t reserved7[0xb0];
uint32_t l0_tcntb;
uint32_t l0_tcnto;
uint32_t l0_icntb;
uint32_t l0_icnto;
uint32_t l0_frcntb;
uint32_t l0_frcnto;
uint8_t reserved8[0x8];
uint32_t l0_tcon;
uint8_t reserved9[0xc];
uint32_t l0_int_cstat;
uint32_t l0_int_enb;
uint8_t reserved10[0x8];
uint32_t l0_wstat;
uint8_t reserved11[0xbc];
uint32_t l1_tcntb;
uint32_t l1_tcnto;
uint32_t l1_icntb;
uint32_t l1_icnto;
uint32_t l1_frcntb;
uint32_t l1_frcnto;
uint8_t reserved12[0x8];
uint32_t l1_tcon;
uint8_t reserved13[0xc];
uint32_t l1_int_cstat;
uint32_t l1_int_enb;
uint8_t reserved14[0x8];
uint32_t l1_wstat;
};
static int enabled = 0;
static struct mct_regs *const mct =
(struct mct_regs *)MCT_ADDRESS;
uint64_t mct_raw_value(void)
{
if (!enabled) {
writel(readl(&mct->g_tcon) | (0x1 << 8), &mct->g_tcon);
enabled = 1;
}
uint64_t upper = readl(&mct->g_cnt_u);
uint64_t lower = readl(&mct->g_cnt_l);
return (upper << 32) | lower;
}
void mct_start(void)
{
writel(readl(&mct->g_tcon) | (0x1 << 8), &mct->g_tcon);
enabled = 1;
}

View File

@ -0,0 +1,57 @@
/*
* This file is part of the coreboot project.
*
* Copyright 2013 Google Inc.
*
* 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; version 2 of the License.
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdint.h>
#include <delay.h>
#include <timer.h>
#include "clk.h"
static struct monotonic_counter {
int initialized;
struct mono_time time;
uint64_t last_value;
} mono_counter;
static const uint32_t clocks_per_usec = MCT_HZ/1000000;
void timer_monotonic_get(struct mono_time *mt)
{
uint64_t current_tick;
uint64_t usecs_elapsed;
if (!mono_counter.initialized) {
init_timer();
mono_counter.last_value = mct_raw_value();
mono_counter.initialized = 1;
}
current_tick = mct_raw_value();
usecs_elapsed = (current_tick - mono_counter.last_value) /
clocks_per_usec;
/* Update current time and tick values only if a full tick occurred. */
if (usecs_elapsed) {
mono_time_add_usecs(&mono_counter.time, usecs_elapsed);
mono_counter.last_value = current_tick;
}
/* Save result. */
*mt = mono_counter.time;
}

View File

@ -0,0 +1,69 @@
/*
* This file is part of the coreboot project.
*
* Copyright 2012 Google Inc.
*
* 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; version 2 of the License.
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef CPU_SAMSUNG_EXYNOS5420_PERIPH_H
#define CPU_SAMSUNG_EXYNOS5420_PERIPH_H
/*
* Peripherals requiring clock/pinmux configuration. List will
* grow with support for more devices getting added.
*
* At present the order is arbitrary - we may be able to take advantage
* of some orthogonality later.
*/
enum periph_id {
PERIPH_ID_UART0,
PERIPH_ID_UART1,
PERIPH_ID_UART2,
PERIPH_ID_UART3,
PERIPH_ID_SDMMC0,
PERIPH_ID_SDMMC1,
PERIPH_ID_SDMMC2,
PERIPH_ID_SDMMC3,
PERIPH_ID_SROMC = 9,
PERIPH_ID_SPI0,
PERIPH_ID_SPI1,
PERIPH_ID_SPI2,
PERIPH_ID_SPI3,
PERIPH_ID_SPI4,
PERIPH_ID_LCD,
PERIPH_ID_BACKLIGHT,
PERIPH_ID_I2C0,
PERIPH_ID_I2C1,
PERIPH_ID_I2C2,
PERIPH_ID_I2C3,
PERIPH_ID_I2C4,
PERIPH_ID_I2C5,
PERIPH_ID_I2C6,
PERIPH_ID_I2C7,
PERIPH_ID_DPHPD, /* eDP hot plug detect */
PERIPH_ID_PWM0,
PERIPH_ID_PWM1,
PERIPH_ID_PWM2,
PERIPH_ID_PWM3,
PERIPH_ID_PWM4,
PERIPH_ID_I2S1,
PERIPH_ID_SATA,
PERIPH_ID_COUNT,
PERIPH_ID_NONE = -1,
};
#endif

View File

@ -0,0 +1,298 @@
/*
* This file is part of the coreboot project.
*
* Copyright (C) 2012 Samsung Electronics
*
* 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; version 2 of the License.
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <console/console.h>
#include <assert.h>
#include "gpio.h"
#include "cpu.h"
#include "pinmux.h"
int exynos_pinmux_config(enum periph_id peripheral, int flags)
{
int i, start, count, start_ext, pin_ext, pin, drv;
switch (peripheral) {
case PERIPH_ID_UART0:
case PERIPH_ID_UART1:
case PERIPH_ID_UART2:
case PERIPH_ID_UART3:
switch (peripheral) {
default:
case PERIPH_ID_UART0:
start = GPIO_A00; count = 4;
break;
case PERIPH_ID_UART1:
start = GPIO_A04; count = 4;
break;
case PERIPH_ID_UART2:
start = GPIO_A10; count = 4;
break;
case PERIPH_ID_UART3:
start = GPIO_A14; count = 2;
break;
}
for (i = start; i < start + count; i++) {
gpio_set_pull(i, GPIO_PULL_NONE);
gpio_cfg_pin(i, GPIO_FUNC(0x2));
}
break;
case PERIPH_ID_SDMMC0:
case PERIPH_ID_SDMMC1:
case PERIPH_ID_SDMMC2:
case PERIPH_ID_SDMMC3:
pin = GPIO_FUNC(0x2);
pin_ext = GPIO_FUNC(0x2);
drv = GPIO_DRV_4X;
switch (peripheral) {
default:
case PERIPH_ID_SDMMC0:
start = GPIO_C00;
start_ext = GPIO_C10;
break;
case PERIPH_ID_SDMMC1:
start = GPIO_C20;
start_ext = 0;
break;
case PERIPH_ID_SDMMC2:
start = GPIO_C30;
/*
* TODO: (alim.akhtar@samsung.com)
* add support for 8 bit mode (needs to be a per-board
* option, so in the FDT).
*/
start_ext = 0;
break;
case PERIPH_ID_SDMMC3:
/*
* TODO: Need to add defintions for GPC4 before
* enabling this.
*/
printk(BIOS_DEBUG, "SDMMC3 not supported yet");
return -1;
}
if ((flags & PINMUX_FLAG_8BIT_MODE) && !start_ext) {
printk(BIOS_DEBUG, "SDMMC device %d does not support 8bit mode",
peripheral);
return -1;
}
if (flags & PINMUX_FLAG_8BIT_MODE) {
ASSERT(peripheral == PERIPH_ID_SDMMC0);
for (i = 0; i <= 3; i++) {
gpio_cfg_pin(start_ext + i, pin_ext);
gpio_set_pull(start_ext + i,
GPIO_PULL_UP);
gpio_set_drv(start_ext + i, drv);
}
}
for (i = 0; i < 2; i++) {
gpio_cfg_pin(start + i, pin);
gpio_set_pull(start + i, GPIO_PULL_NONE);
gpio_set_drv(start + i, drv);
}
for (i = 2; i <= 6; i++) {
gpio_cfg_pin(start + i, pin);
gpio_set_pull(start + i, GPIO_PULL_UP);
gpio_set_drv(start + i, drv);
}
break;
case PERIPH_ID_SROMC:
/*
* SROM:CS1 and EBI
*
* GPY0[0] SROM_CSn[0]
* GPY0[1] SROM_CSn[1](2)
* GPY0[2] SROM_CSn[2]
* GPY0[3] SROM_CSn[3]
* GPY0[4] EBI_OEn(2)
* GPY0[5] EBI_EEn(2)
*
* GPY1[0] EBI_BEn[0](2)
* GPY1[1] EBI_BEn[1](2)
* GPY1[2] SROM_WAIT(2)
* GPY1[3] EBI_DATA_RDn(2)
*/
gpio_cfg_pin(GPIO_Y00 + (flags & PINMUX_FLAG_BANK),
GPIO_FUNC(2));
gpio_cfg_pin(GPIO_Y04, GPIO_FUNC(2));
gpio_cfg_pin(GPIO_Y05, GPIO_FUNC(2));
for (i = 2; i < 4; i++)
gpio_cfg_pin(GPIO_Y10 + i, GPIO_FUNC(2));
/*
* EBI: 8 Addrss Lines
*
* GPY3[0] EBI_ADDR[0](2)
* GPY3[1] EBI_ADDR[1](2)
* GPY3[2] EBI_ADDR[2](2)
* GPY3[3] EBI_ADDR[3](2)
* GPY3[4] EBI_ADDR[4](2)
* GPY3[5] EBI_ADDR[5](2)
* GPY3[6] EBI_ADDR[6](2)
* GPY3[7] EBI_ADDR[7](2)
*
* EBI: 16 Data Lines
*
* GPY5[0] EBI_DATA[0](2)
* GPY5[1] EBI_DATA[1](2)
* GPY5[2] EBI_DATA[2](2)
* GPY5[3] EBI_DATA[3](2)
* GPY5[4] EBI_DATA[4](2)
* GPY5[5] EBI_DATA[5](2)
* GPY5[6] EBI_DATA[6](2)
* GPY5[7] EBI_DATA[7](2)
*
* GPY6[0] EBI_DATA[8](2)
* GPY6[1] EBI_DATA[9](2)
* GPY6[2] EBI_DATA[10](2)
* GPY6[3] EBI_DATA[11](2)
* GPY6[4] EBI_DATA[12](2)
* GPY6[5] EBI_DATA[13](2)
* GPY6[6] EBI_DATA[14](2)
* GPY6[7] EBI_DATA[15](2)
*/
for (i = 0; i < 8; i++) {
gpio_cfg_pin(GPIO_Y30 + i, GPIO_FUNC(2));
gpio_set_pull(GPIO_Y30 + i, GPIO_PULL_UP);
gpio_cfg_pin(GPIO_Y50 + i, GPIO_FUNC(2));
gpio_set_pull(GPIO_Y50 + i, GPIO_PULL_UP);
if (flags & PINMUX_FLAG_16BIT) {
gpio_cfg_pin(GPIO_Y60 + i, GPIO_FUNC(2));
gpio_set_pull(GPIO_Y60 + i,
GPIO_PULL_UP);
}
}
break;
case PERIPH_ID_SPI0:
case PERIPH_ID_SPI1:
case PERIPH_ID_SPI2:
case PERIPH_ID_SPI3: {
int cfg;
switch (peripheral) {
default:
case PERIPH_ID_SPI0:
start = GPIO_A20;
cfg = 0x2;
break;
case PERIPH_ID_SPI1:
start = GPIO_A24;
cfg = 0x2;
break;
case PERIPH_ID_SPI2:
start = GPIO_B11;
cfg = 0x5;
break;
case PERIPH_ID_SPI3:
start = GPIO_E00;
cfg = 0x2;
break;
}
for (i = 0; i < 4; i++)
gpio_cfg_pin(start + i, GPIO_FUNC(cfg));
break;
}
case PERIPH_ID_SPI4:
for (i = 0; i < 2; i++)
gpio_cfg_pin(GPIO_F02 + i, GPIO_FUNC(0x4));
for (i = 2; i < 4; i++)
gpio_cfg_pin(GPIO_E02 + i, GPIO_FUNC(0x4));
break;
case PERIPH_ID_BACKLIGHT:
gpio_cfg_pin(GPIO_B20, GPIO_OUTPUT);
gpio_set_value(GPIO_B20, 1);
break;
case PERIPH_ID_LCD:
gpio_cfg_pin(GPIO_Y25, GPIO_OUTPUT);
gpio_set_value(GPIO_Y25, 1);
gpio_cfg_pin(GPIO_X15, GPIO_OUTPUT);
gpio_set_value(GPIO_X15, 1);
gpio_cfg_pin(GPIO_X30, GPIO_OUTPUT);
gpio_set_value(GPIO_X30, 1);
break;
case PERIPH_ID_I2C0:
gpio_cfg_pin(GPIO_B30, GPIO_FUNC(0x2));
gpio_cfg_pin(GPIO_B31, GPIO_FUNC(0x2));
gpio_set_pull(GPIO_B30, GPIO_PULL_NONE);
gpio_set_pull(GPIO_B31, GPIO_PULL_NONE);
break;
case PERIPH_ID_I2C1:
gpio_cfg_pin(GPIO_B32, GPIO_FUNC(0x2));
gpio_cfg_pin(GPIO_B33, GPIO_FUNC(0x2));
gpio_set_pull(GPIO_B32, GPIO_PULL_NONE);
gpio_set_pull(GPIO_B33, GPIO_PULL_NONE);
break;
case PERIPH_ID_I2C2:
gpio_cfg_pin(GPIO_A06, GPIO_FUNC(0x3));
gpio_cfg_pin(GPIO_A07, GPIO_FUNC(0x3));
gpio_set_pull(GPIO_A06, GPIO_PULL_NONE);
gpio_set_pull(GPIO_A07, GPIO_PULL_NONE);
break;
case PERIPH_ID_I2C3:
gpio_cfg_pin(GPIO_A12, GPIO_FUNC(0x3));
gpio_cfg_pin(GPIO_A13, GPIO_FUNC(0x3));
gpio_set_pull(GPIO_A12, GPIO_PULL_NONE);
gpio_set_pull(GPIO_A13, GPIO_PULL_NONE);
break;
case PERIPH_ID_I2C4:
gpio_cfg_pin(GPIO_A20, GPIO_FUNC(0x3));
gpio_cfg_pin(GPIO_A21, GPIO_FUNC(0x3));
gpio_set_pull(GPIO_A20, GPIO_PULL_NONE);
gpio_set_pull(GPIO_A21, GPIO_PULL_NONE);
break;
case PERIPH_ID_I2C5:
gpio_cfg_pin(GPIO_A22, GPIO_FUNC(0x3));
gpio_cfg_pin(GPIO_A23, GPIO_FUNC(0x3));
gpio_set_pull(GPIO_A22, GPIO_PULL_NONE);
gpio_set_pull(GPIO_A23, GPIO_PULL_NONE);
break;
case PERIPH_ID_I2C6:
gpio_cfg_pin(GPIO_B13, GPIO_FUNC(0x4));
gpio_cfg_pin(GPIO_B14, GPIO_FUNC(0x4));
break;
case PERIPH_ID_I2C7:
gpio_cfg_pin(GPIO_B22, GPIO_FUNC(0x3));
gpio_cfg_pin(GPIO_B23, GPIO_FUNC(0x3));
gpio_set_pull(GPIO_B22, GPIO_PULL_NONE);
gpio_set_pull(GPIO_B23, GPIO_PULL_NONE);
break;
case PERIPH_ID_DPHPD:
/* Set Hotplug detect for DP */
gpio_cfg_pin(GPIO_X07, GPIO_FUNC(0x3));
/*
* Hotplug detect should have an external pullup; disable the
* internal pulldown so they don't fight.
*/
gpio_set_pull(GPIO_X07, GPIO_PULL_NONE);
break;
case PERIPH_ID_I2S1:
for (i = 0; i < 5; i++)
gpio_cfg_pin(GPIO_B00 + i, GPIO_FUNC(0x02));
break;
default:
printk(BIOS_DEBUG, "%s: invalid peripheral %d", __func__, peripheral);
return -1;
}
return 0;
}

View File

@ -0,0 +1,55 @@
/*
* This file is part of the coreboot project.
*
* Copyright (C) 2012 Samsung Electronics
*
* 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; version 2 of the License.
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef CPU_SAMSUNG_EXYNOS5420_PINMUX_H
#define CPU_SAMSUNG_EXYNOS5420_PINMUX_H
#include "periph.h"
enum {
PINMUX_FLAG_NONE = 0x00000000,
/* Flags for eMMC */
PINMUX_FLAG_8BIT_MODE = 1 << 0, /* SDMMC 8-bit mode */
/*
* Flags for SPI.
*/
PINMUX_FLAG_SLAVE_MODE = 1 << 0, /* Slave mode */
/* Flags for SROM controller */
PINMUX_FLAG_BANK = 3 << 0, /* bank number (0-3) */
PINMUX_FLAG_16BIT = 1 << 2, /* 16-bit width */
};
/**
* Configures the pinmux for a particular peripheral.
*
* Each gpio can be configured in many different ways (4 bits on exynos)
* such as "input", "output", "special function", "external interrupt"
* etc. This function will configure the peripheral pinmux along with
* pull-up/down and drive strength.
*
* @param peripheral peripheral to be configured
* @param flags configure flags
* @return 0 if ok, -1 on error (e.g. unsupported peripheral)
*/
int exynos_pinmux_config(enum periph_id peripheral, int flags);
#endif

View File

@ -0,0 +1,138 @@
/*
* This file is part of the coreboot project.
*
* Copyright (C) 2012 Samsung Electronics
*
* 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; version 2 of the License.
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/* Power setup code for EXYNOS5 */
#include <console/console.h>
#include <arch/io.h>
#include <arch/hlt.h>
#include "cpu.h"
#include "power.h"
#include "sysreg.h"
static void ps_hold_setup(void)
{
struct exynos5_power *power =
samsung_get_base_power();
/* Set PS-Hold high */
setbits_le32(&power->ps_hold_ctrl, POWER_PS_HOLD_CONTROL_DATA_HIGH);
}
void power_reset(void)
{
struct exynos5_power *power =
samsung_get_base_power();
/* Clear inform1 so there's no change we think we've got a wake reset */
power->inform1 = 0;
setbits_le32(&power->sw_reset, 1);
}
/* This function never returns */
void power_shutdown(void)
{
struct exynos5_power *power =
samsung_get_base_power();
clrbits_le32(&power->ps_hold_ctrl, POWER_PS_HOLD_CONTROL_DATA_HIGH);
hlt();
}
void power_enable_dp_phy(void)
{
struct exynos5_power *power =
samsung_get_base_power();
setbits_le32(&power->dptx_phy_control, DPTX_PHY_ENABLE);
}
void power_enable_usb_phy(void)
{
struct exynos5_sysreg *sysreg =
samsung_get_base_sysreg();
struct exynos5_power *power =
samsung_get_base_power();
unsigned int phy_cfg;
/* Setting USB20PHY_CONFIG register to USB 2.0 HOST link */
phy_cfg = readl(&sysreg->usb20_phy_cfg);
if (phy_cfg & USB20_PHY_CFG_EN) {
printk(BIOS_DEBUG, "USB 2.0 HOST link already selected\n");
} else {
phy_cfg |= USB20_PHY_CFG_EN;
writel(phy_cfg, &sysreg->usb20_phy_cfg);
}
/* Enabling USBHOST_PHY */
setbits_le32(&power->usb_host_phy_ctrl, POWER_USB_HOST_PHY_CTRL_EN);
}
void power_disable_usb_phy(void)
{
struct exynos5_power *power =
samsung_get_base_power();
/* Disabling USBHost_PHY */
clrbits_le32(&power->usb_host_phy_ctrl, POWER_USB_HOST_PHY_CTRL_EN);
}
void power_enable_hw_thermal_trip(void)
{
struct exynos5_power *power =
samsung_get_base_power();
/* Enable HW thermal trip */
setbits_le32(&power->ps_hold_ctrl, POWER_ENABLE_HW_TRIP);
}
uint32_t power_read_reset_status(void)
{
struct exynos5_power *power =
samsung_get_base_power();
return power->inform1;
}
void power_exit_wakeup(void)
{
struct exynos5_power *power =
samsung_get_base_power();
typedef void (*resume_func)(void);
((resume_func)power->inform0)();
}
int power_init(void)
{
ps_hold_setup();
return 0;
}
void power_enable_xclkout(void)
{
struct exynos5_power *power =
samsung_get_base_power();
/* use xxti for xclk out */
clrsetbits_le32(&power->pmu_debug, PMU_DEBUG_CLKOUT_SEL_MASK,
PMU_DEBUG_XXTI);
}

View File

@ -0,0 +1,90 @@
/*
* This file is part of the coreboot project.
*
* Copyright (C) 2012 Samsung Electronics
*
* 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; version 2 of the License.
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/* Register map for Exynos5 PMU */
#ifndef CPU_SAMSUNG_EXYNOS5420_POWER_H
#define CPU_SAMSUNG_EXYNOS5420_POWER_H
/* Enable HW thermal trip with PS_HOLD_CONTROL register ENABLE_HW_TRIP bit */
void power_enable_hw_thermal_trip(void);
#define MIPI_PHY1_CONTROL_ENABLE (1 << 0)
#define MIPI_PHY1_CONTROL_M_RESETN (1 << 2)
#define POWER_USB_HOST_PHY_CTRL_EN (1 << 0)
#define POWER_PS_HOLD_CONTROL_DATA_HIGH (1 << 8)
#define POWER_ENABLE_HW_TRIP (1UL << 31)
#define DPTX_PHY_ENABLE (1 << 0)
/* PMU_DEBUG bits [12:8] = 0x1000 selects XXTI clock source */
#define PMU_DEBUG_XXTI 0x1000
/* Mask bit[12:8] for xxti clock selection */
#define PMU_DEBUG_CLKOUT_SEL_MASK 0x1f00
/* Power Management Unit register map */
struct exynos5_power {
/* Add registers as and when required */
uint8_t reserved1[0x0400];
uint32_t sw_reset; /* 0x0400 */
uint8_t reserved2[0x0304];
uint32_t usb_host_phy_ctrl; /* 0x0708 */
uint8_t reserved3[0x8];
uint32_t mipi_phy1_control; /* 0x0714 */
uint8_t reserved4[0x8];
uint32_t dptx_phy_control; /* 0x0720 */
uint8_t reserved5[0xdc];
uint32_t inform0; /* 0x0800 */
uint32_t inform1; /* 0x0804 */
uint8_t reserved6[0x1f8];
uint32_t pmu_debug; /* 0x0A00*/
uint8_t reserved7[0x2908];
uint32_t ps_hold_ctrl; /* 0x330c */
} __attribute__ ((__packed__));
/**
* Perform a software reset.
*/
void power_reset(void);
/**
* Power off the system; it should never return.
*/
void power_shutdown(void);
/* Enable DPTX PHY */
void power_enable_dp_phy(void);
void power_enable_usb_phy(void);
void power_disable_usb_phy(void);
/* Initialize the pmic voltages to power up the system */
int power_init(void);
/* Read the reset status. */
uint32_t power_read_reset_status(void);
/* Read the resume function and call it. */
void power_exit_wakeup(void);
/* pmu debug is used for xclkout, enable xclkout with source as XXTI */
void power_enable_xclkout(void);
#endif

View File

@ -0,0 +1,191 @@
/*
* This file is part of the coreboot project.
*
* Copyright (C) 2011 Samsung Electronics
*
* 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; version 2 of the License.
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <arch/io.h>
#include "clk.h"
#include "cpu.h"
#include "periph.h"
#include "pwm.h"
int pwm_enable(int pwm_id)
{
const struct s5p_timer *pwm =
samsung_get_base_timer();
unsigned long tcon;
tcon = readl(&pwm->tcon);
tcon |= TCON_START(pwm_id);
writel(tcon, &pwm->tcon);
return 0;
}
int pwm_check_enabled(int pwm_id)
{
const struct s5p_timer *pwm =
samsung_get_base_timer();
const unsigned long tcon = readl(&pwm->tcon);
return tcon & TCON_START(pwm_id);
}
void pwm_disable(int pwm_id)
{
const struct s5p_timer *pwm =
samsung_get_base_timer();
unsigned long tcon;
tcon = readl(&pwm->tcon);
tcon &= ~TCON_START(pwm_id);
writel(tcon, &pwm->tcon);
}
static unsigned long pwm_calc_tin(int pwm_id, unsigned long freq)
{
unsigned long tin_parent_rate;
unsigned int div;
tin_parent_rate = clock_get_periph_rate(PERIPH_ID_PWM0);
for (div = 2; div <= 16; div *= 2) {
if ((tin_parent_rate / (div << 16)) < freq)
return tin_parent_rate / div;
}
return tin_parent_rate / 16;
}
#define NS_IN_SEC 1000000000UL
int pwm_config(int pwm_id, int duty_ns, int period_ns)
{
const struct s5p_timer *pwm =
samsung_get_base_timer();
unsigned int offset;
unsigned long tin_rate;
unsigned long tin_ns;
unsigned long frequency;
unsigned long tcon;
unsigned long tcnt;
unsigned long tcmp;
/*
* We currently avoid using 64bit arithmetic by using the
* fact that anything faster than 1GHz is easily representable
* by 32bits.
*/
if (period_ns > NS_IN_SEC || duty_ns > NS_IN_SEC || period_ns == 0)
return -1;
if (duty_ns > period_ns)
return -1;
frequency = NS_IN_SEC / period_ns;
/* Check to see if we are changing the clock rate of the PWM */
tin_rate = pwm_calc_tin(pwm_id, frequency);
tin_ns = NS_IN_SEC / tin_rate;
tcnt = period_ns / tin_ns;
/* Note, counters count down */
tcmp = duty_ns / tin_ns;
tcmp = tcnt - tcmp;
/* Update the PWM register block. */
offset = pwm_id * 3;
if (pwm_id < 4) {
writel(tcnt, &pwm->tcntb0 + offset);
writel(tcmp, &pwm->tcmpb0 + offset);
}
tcon = readl(&pwm->tcon);
tcon |= TCON_UPDATE(pwm_id);
if (pwm_id < 4)
tcon |= TCON_AUTO_RELOAD(pwm_id);
else
tcon |= TCON4_AUTO_RELOAD;
writel(tcon, &pwm->tcon);
tcon &= ~TCON_UPDATE(pwm_id);
writel(tcon, &pwm->tcon);
return 0;
}
int pwm_init(int pwm_id, int div, int invert)
{
u32 val;
const struct s5p_timer *pwm =
samsung_get_base_timer();
unsigned long ticks_per_period;
unsigned int offset, prescaler;
/*
* Timer Freq(HZ) =
* PWM_CLK / { (prescaler_value + 1) * (divider_value) }
*/
val = readl(&pwm->tcfg0);
if (pwm_id < 2) {
prescaler = PRESCALER_0;
val &= ~0xff;
val |= (prescaler & 0xff);
} else {
prescaler = PRESCALER_1;
val &= ~(0xff << 8);
val |= (prescaler & 0xff) << 8;
}
writel(val, &pwm->tcfg0);
val = readl(&pwm->tcfg1);
val &= ~(0xf << MUX_DIV_SHIFT(pwm_id));
val |= (div & 0xf) << MUX_DIV_SHIFT(pwm_id);
writel(val, &pwm->tcfg1);
if (pwm_id == 4) {
/*
* TODO(sjg): Use this as a countdown timer for now. We count
* down from the maximum value to 0, then reset.
*/
ticks_per_period = -1UL;
} else {
const unsigned long pwm_hz = 1000;
unsigned long timer_rate_hz = clock_get_periph_rate(
PERIPH_ID_PWM0) / ((prescaler + 1) * (1 << div));
ticks_per_period = timer_rate_hz / pwm_hz;
}
/* set count value */
offset = pwm_id * 3;
writel(ticks_per_period, &pwm->tcntb0 + offset);
val = readl(&pwm->tcon) & ~(0xf << TCON_OFFSET(pwm_id));
if (invert && (pwm_id < 4))
val |= TCON_INVERTER(pwm_id);
writel(val, &pwm->tcon);
pwm_enable(pwm_id);
return 0;
}

View File

@ -0,0 +1,70 @@
/*
* This file is part of the coreboot project.
*
* Copyright (C) 2009 Samsung Electronics
*
* 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; version 2 of the License.
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef CPU_SAMSUNG_EXYNOS5420_PWM_H
#define CPU_SAMSUNG_EXYNOS5420_PWM_H
#define PRESCALER_0 (8 - 1) /* prescaler of timer 0, 1 */
#define PRESCALER_1 (16 - 1) /* prescaler of timer 2, 3, 4 */
/* Divider MUX */
#define MUX_DIV_1 0 /* 1/1 period */
#define MUX_DIV_2 1 /* 1/2 period */
#define MUX_DIV_4 2 /* 1/4 period */
#define MUX_DIV_8 3 /* 1/8 period */
#define MUX_DIV_16 4 /* 1/16 period */
#define MUX_DIV_SHIFT(x) (x * 4)
#define TCON_OFFSET(x) ((x + 1) * (!!x) << 2)
#define TCON_START(x) (1 << TCON_OFFSET(x))
#define TCON_UPDATE(x) (1 << (TCON_OFFSET(x) + 1))
#define TCON_INVERTER(x) (1 << (TCON_OFFSET(x) + 2))
#define TCON_AUTO_RELOAD(x) (1 << (TCON_OFFSET(x) + 3))
#define TCON4_AUTO_RELOAD (1 << 22)
struct s5p_timer {
unsigned int tcfg0;
unsigned int tcfg1;
unsigned int tcon;
unsigned int tcntb0;
unsigned int tcmpb0;
unsigned int tcnto0;
unsigned int tcntb1;
unsigned int tcmpb1;
unsigned int tcnto1;
unsigned int tcntb2;
unsigned int tcmpb2;
unsigned int tcnto2;
unsigned int tcntb3;
unsigned int tcmpb3;
unsigned int tcnto3;
unsigned int tcntb4;
unsigned int tcnto4;
unsigned int tintcstat;
};
int pwm_config(int pwm_id, int duty_ns, int period_ns);
int pwm_check_enabled(int pwm_id);
void pwm_disable(int pwm_id);
int pwm_enable(int pwm_id);
int pwm_init(int pwm_id, int div, int invert);
#endif

View File

@ -0,0 +1,26 @@
/*
* This file is part of the coreboot project.
*
* Copyright 2013 Google Inc.
*
* 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; version 2 of the License.
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <reset.h>
#include <arch/io.h>
void soft_reset(void)
{
writel(0x1, samsung_get_base_swreset());
}

View File

@ -0,0 +1,753 @@
/*
* This file is part of the coreboot project.
*
* Copyright (C) 2012 Samsung Electronics
*
* 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; version 2 of the License.
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/* Machine Specific Values for SMDK5420 board based on Exynos5 */
#ifndef CPU_SAMSUNG_EXYNOS5420_SETUP_H
#define CPU_SAMSUNG_EXYNOS5420_SETUP_H
struct exynos5_dmc;
enum ddr_mode;
struct exynos5_phy_control;
/* TZPC : Register Offsets */
#define TZPC0_BASE 0x10100000
#define TZPC1_BASE 0x10110000
#define TZPC2_BASE 0x10120000
#define TZPC3_BASE 0x10130000
#define TZPC4_BASE 0x10140000
#define TZPC5_BASE 0x10150000
#define TZPC6_BASE 0x10160000
#define TZPC7_BASE 0x10170000
#define TZPC8_BASE 0x10180000
#define TZPC9_BASE 0x10190000
/* APLL_CON1 */
#define APLL_CON1_VAL (0x00203800)
/* MPLL_CON1 */
#define MPLL_CON1_VAL (0x00203800)
/* CPLL_CON1 */
#define CPLL_CON1_VAL (0x00203800)
/* GPLL_CON1 */
#define GPLL_CON1_VAL (0x00203800)
/* EPLL_CON1, CON2 */
#define EPLL_CON1_VAL 0x00000000
#define EPLL_CON2_VAL 0x00000080
/* VPLL_CON1, CON2 */
#define VPLL_CON1_VAL 0x00000000
#define VPLL_CON2_VAL 0x00000080
/* BPLL_CON1 */
#define BPLL_CON1_VAL 0x00203800
/* Set PLL */
#define set_pll(mdiv, pdiv, sdiv) (1<<31 | mdiv<<16 | pdiv<<8 | sdiv)
/* CLK_SRC_CPU */
/* 0 = MOUTAPLL, 1 = SCLKMPLL */
#define MUX_HPM_SEL 0
#define MUX_CPU_SEL 0
#define MUX_APLL_SEL 1
#define CLK_SRC_CPU_VAL ((MUX_HPM_SEL << 20) \
| (MUX_CPU_SEL << 16) \
| (MUX_APLL_SEL))
/* MEMCONTROL register bit fields */
#define DMC_MEMCONTROL_CLK_STOP_DISABLE (0 << 0)
#define DMC_MEMCONTROL_DPWRDN_DISABLE (0 << 1)
#define DMC_MEMCONTROL_DPWRDN_ACTIVE_PRECHARGE (0 << 2)
#define DMC_MEMCONTROL_TP_DISABLE (0 << 4)
#define DMC_MEMCONTROL_DSREF_DISABLE (0 << 5)
#define DMC_MEMCONTROL_DSREF_ENABLE (1 << 5)
#define DMC_MEMCONTROL_ADD_LAT_PALL_CYCLE(x) (x << 6)
#define DMC_MEMCONTROL_MEM_TYPE_LPDDR3 (7 << 8)
#define DMC_MEMCONTROL_MEM_TYPE_DDR3 (6 << 8)
#define DMC_MEMCONTROL_MEM_TYPE_LPDDR2 (5 << 8)
#define DMC_MEMCONTROL_MEM_WIDTH_32BIT (2 << 12)
#define DMC_MEMCONTROL_NUM_CHIP_1 (0 << 16)
#define DMC_MEMCONTROL_NUM_CHIP_2 (1 << 16)
#define DMC_MEMCONTROL_BL_8 (3 << 20)
#define DMC_MEMCONTROL_BL_4 (2 << 20)
#define DMC_MEMCONTROL_PZQ_DISABLE (0 << 24)
#define DMC_MEMCONTROL_MRR_BYTE_7_0 (0 << 25)
#define DMC_MEMCONTROL_MRR_BYTE_15_8 (1 << 25)
#define DMC_MEMCONTROL_MRR_BYTE_23_16 (2 << 25)
#define DMC_MEMCONTROL_MRR_BYTE_31_24 (3 << 25)
/* MEMCONFIG0 register bit fields */
#define DMC_MEMCONFIGx_CHIP_MAP_INTERLEAVED (1 << 12)
#define DMC_MEMCONFIGx_CHIP_COL_10 (3 << 8)
#define DMC_MEMCONFIGx_CHIP_ROW_14 (2 << 4)
#define DMC_MEMCONFIGx_CHIP_ROW_15 (3 << 4)
#define DMC_MEMCONFIGx_CHIP_BANK_8 (3 << 0)
#define DMC_MEMBASECONFIGx_CHIP_BASE(x) (x << 16)
#define DMC_MEMBASECONFIGx_CHIP_MASK(x) (x << 0)
#define DMC_MEMBASECONFIG_VAL(x) ( \
DMC_MEMBASECONFIGx_CHIP_BASE(x) | \
DMC_MEMBASECONFIGx_CHIP_MASK(0x780) \
)
#define DMC_MEMBASECONFIG0_VAL DMC_MEMBASECONFIG_VAL(0x40)
#define DMC_MEMBASECONFIG1_VAL DMC_MEMBASECONFIG_VAL(0x80)
#define DMC_PRECHCONFIG_VAL 0xFF000000
#define DMC_PWRDNCONFIG_VAL 0xFFFF00FF
#define DMC_CONCONTROL_RESET_VAL 0x0FFF0000
#define DFI_INIT_START (1 << 28)
#define EMPTY (1 << 8)
#define AREF_EN (1 << 5)
#define DFI_INIT_COMPLETE_CHO (1 << 2)
#define DFI_INIT_COMPLETE_CH1 (1 << 3)
#define RDLVL_COMPLETE_CHO (1 << 14)
#define RDLVL_COMPLETE_CH1 (1 << 15)
#define CLK_STOP_EN (1 << 0)
#define DPWRDN_EN (1 << 1)
#define DSREF_EN (1 << 5)
/* COJCONTROL register bit fields */
#define DMC_CONCONTROL_IO_PD_CON_DISABLE (0 << 3)
#define DMC_CONCONTROL_AREF_EN_DISABLE (0 << 5)
#define DMC_CONCONTROL_EMPTY_DISABLE (0 << 8)
#define DMC_CONCONTROL_EMPTY_ENABLE (1 << 8)
#define DMC_CONCONTROL_RD_FETCH_DISABLE (0x0 << 12)
#define DMC_CONCONTROL_TIMEOUT_LEVEL0 (0xFFF << 16)
#define DMC_CONCONTROL_DFI_INIT_START_DISABLE (0 << 28)
/* CLK_DIV_CPU0_VAL */
#define CLK_DIV_CPU0_VAL ((ARM2_RATIO << 28) \
| (APLL_RATIO << 24) \
| (PCLK_DBG_RATIO << 20) \
| (ATB_RATIO << 16) \
| (PERIPH_RATIO << 12) \
| (ACP_RATIO << 8) \
| (CPUD_RATIO << 4) \
| (ARM_RATIO))
/* CLK_FSYS */
#define CLK_SRC_FSYS0_VAL 0x66666
#define CLK_DIV_FSYS0_VAL 0x0BB00000
/* CLK_DIV_CPU1 */
#define HPM_RATIO 0x2
#define COPY_RATIO 0x0
/* CLK_DIV_CPU1 = 0x00000003 */
#define CLK_DIV_CPU1_VAL ((HPM_RATIO << 4) \
| (COPY_RATIO))
/* CLK_SRC_CORE0 */
#define CLK_SRC_CORE0_VAL 0x00000000
/* CLK_SRC_CORE1 */
#define CLK_SRC_CORE1_VAL 0x100
/* CLK_DIV_CORE0 */
#define CLK_DIV_CORE0_VAL 0x00120000
/* CLK_DIV_CORE1 */
#define CLK_DIV_CORE1_VAL 0x07070700
/* CLK_DIV_SYSRGT */
#define CLK_DIV_SYSRGT_VAL 0x00000111
/* CLK_DIV_ACP */
#define CLK_DIV_ACP_VAL 0x12
/* CLK_DIV_SYSLFT */
#define CLK_DIV_SYSLFT_VAL 0x00000311
/* CLK_SRC_CDREX */
#define CLK_SRC_CDREX_VAL 0x1
/* CLK_DIV_CDREX */
#define MCLK_CDREX2_RATIO 0x0
#define ACLK_EFCON_RATIO 0x1
#define MCLK_DPHY_RATIO 0x1
#define MCLK_CDREX_RATIO 0x1
#define ACLK_C2C_200_RATIO 0x1
#define C2C_CLK_400_RATIO 0x1
#define PCLK_CDREX_RATIO 0x1
#define ACLK_CDREX_RATIO 0x1
#define CLK_DIV_CDREX_VAL ((MCLK_DPHY_RATIO << 24) \
| (C2C_CLK_400_RATIO << 6) \
| (PCLK_CDREX_RATIO << 4) \
| (ACLK_CDREX_RATIO))
/* CLK_SRC_TOP0 */
#define MUX_ACLK_300_GSCL_SEL 0x0
#define MUX_ACLK_300_GSCL_MID_SEL 0x0
#define MUX_ACLK_400_G3D_MID_SEL 0x0
#define MUX_ACLK_333_SEL 0x0
#define MUX_ACLK_300_DISP1_SEL 0x0
#define MUX_ACLK_300_DISP1_MID_SEL 0x0
#define MUX_ACLK_200_SEL 0x0
#define MUX_ACLK_166_SEL 0x0
#define CLK_SRC_TOP0_VAL ((MUX_ACLK_300_GSCL_SEL << 25) \
| (MUX_ACLK_300_GSCL_MID_SEL << 24) \
| (MUX_ACLK_400_G3D_MID_SEL << 20) \
| (MUX_ACLK_333_SEL << 16) \
| (MUX_ACLK_300_DISP1_SEL << 15) \
| (MUX_ACLK_300_DISP1_MID_SEL << 14) \
| (MUX_ACLK_200_SEL << 12) \
| (MUX_ACLK_166_SEL << 8))
/* CLK_SRC_TOP1 */
#define MUX_ACLK_400_G3D_SEL 0x1
#define MUX_ACLK_400_ISP_SEL 0x0
#define MUX_ACLK_400_IOP_SEL 0x0
#define MUX_ACLK_MIPI_HSI_TXBASE_SEL 0x0
#define MUX_ACLK_300_GSCL_MID1_SEL 0x0
#define MUX_ACLK_300_DISP1_MID1_SEL 0x0
#define CLK_SRC_TOP1_VAL ((MUX_ACLK_400_G3D_SEL << 28) \
|(MUX_ACLK_400_ISP_SEL << 24) \
|(MUX_ACLK_400_IOP_SEL << 20) \
|(MUX_ACLK_MIPI_HSI_TXBASE_SEL << 16) \
|(MUX_ACLK_300_GSCL_MID1_SEL << 12) \
|(MUX_ACLK_300_DISP1_MID1_SEL << 8))
/* CLK_SRC_TOP2 */
#define MUX_GPLL_SEL 0x1
#define MUX_BPLL_USER_SEL 0x0
#define MUX_MPLL_USER_SEL 0x0
#define MUX_VPLL_SEL 0x1
#define MUX_EPLL_SEL 0x1
#define MUX_CPLL_SEL 0x1
#define VPLLSRC_SEL 0x0
#define CLK_SRC_TOP2_VAL ((MUX_GPLL_SEL << 28) \
| (MUX_BPLL_USER_SEL << 24) \
| (MUX_MPLL_USER_SEL << 20) \
| (MUX_VPLL_SEL << 16) \
| (MUX_EPLL_SEL << 12) \
| (MUX_CPLL_SEL << 8) \
| (VPLLSRC_SEL))
/* CLK_SRC_TOP3 */
#define MUX_ACLK_333_SUB_SEL 0x1
#define MUX_ACLK_400_SUB_SEL 0x1
#define MUX_ACLK_266_ISP_SUB_SEL 0x1
#define MUX_ACLK_266_GPS_SUB_SEL 0x0
#define MUX_ACLK_300_GSCL_SUB_SEL 0x1
#define MUX_ACLK_266_GSCL_SUB_SEL 0x1
#define MUX_ACLK_300_DISP1_SUB_SEL 0x1
#define MUX_ACLK_200_DISP1_SUB_SEL 0x1
#define CLK_SRC_TOP3_VAL ((MUX_ACLK_333_SUB_SEL << 24) \
| (MUX_ACLK_400_SUB_SEL << 20) \
| (MUX_ACLK_266_ISP_SUB_SEL << 16) \
| (MUX_ACLK_266_GPS_SUB_SEL << 12) \
| (MUX_ACLK_300_GSCL_SUB_SEL << 10) \
| (MUX_ACLK_266_GSCL_SUB_SEL << 8) \
| (MUX_ACLK_300_DISP1_SUB_SEL << 6) \
| (MUX_ACLK_200_DISP1_SUB_SEL << 4))
/* CLK_DIV_TOP0 */
#define ACLK_300_DISP1_RATIO 0x2
#define ACLK_400_G3D_RATIO 0x0
#define ACLK_333_RATIO 0x0
#define ACLK_266_RATIO 0x2
#define ACLK_200_RATIO 0x3
#define ACLK_166_RATIO 0x1
#define ACLK_133_RATIO 0x1
#define ACLK_66_RATIO 0x5
#define CLK_DIV_TOP0_VAL ((ACLK_300_DISP1_RATIO << 28) \
| (ACLK_400_G3D_RATIO << 24) \
| (ACLK_333_RATIO << 20) \
| (ACLK_266_RATIO << 16) \
| (ACLK_200_RATIO << 12) \
| (ACLK_166_RATIO << 8) \
| (ACLK_133_RATIO << 4) \
| (ACLK_66_RATIO))
/* CLK_DIV_TOP1 */
#define ACLK_MIPI_HSI_TX_BASE_RATIO 0x3
#define ACLK_66_PRE_RATIO 0x1
#define ACLK_400_ISP_RATIO 0x1
#define ACLK_400_IOP_RATIO 0x1
#define ACLK_300_GSCL_RATIO 0x2
#define CLK_DIV_TOP1_VAL ((ACLK_MIPI_HSI_TX_BASE_RATIO << 28) \
| (ACLK_66_PRE_RATIO << 24) \
| (ACLK_400_ISP_RATIO << 20) \
| (ACLK_400_IOP_RATIO << 16) \
| (ACLK_300_GSCL_RATIO << 12))
/* APLL_LOCK */
#define APLL_LOCK_VAL (0x546)
/* MPLL_LOCK */
#define MPLL_LOCK_VAL (0x546)
/* CPLL_LOCK */
#define CPLL_LOCK_VAL (0x546)
/* GPLL_LOCK */
#define GPLL_LOCK_VAL (0x546)
/* EPLL_LOCK */
#define EPLL_LOCK_VAL (0x3A98)
/* VPLL_LOCK */
#define VPLL_LOCK_VAL (0x3A98)
/* BPLL_LOCK */
#define BPLL_LOCK_VAL (0x546)
#define MUX_MCLK_CDREX_SEL (1 << 4)
#define MUX_MCLK_DPHY_SEL (1 << 8)
#define MUX_APLL_SEL_MASK (1 << 0)
#define MUX_MPLL_FOUT_SEL (1 << 4)
#define MUX_BPLL_FOUT_SEL (1 << 0)
#define MUX_MPLL_SEL_MASK (1 << 8)
#define MPLL_SEL_MOUT_MPLLFOUT (2 << 8)
#define MUX_CPLL_SEL_MASK (1 << 8)
#define MUX_EPLL_SEL_MASK (1 << 12)
#define MUX_VPLL_SEL_MASK (1 << 16)
#define MUX_GPLL_SEL_MASK (1 << 28)
#define MUX_BPLL_SEL_MASK (1 << 0)
#define MUX_HPM_SEL_MASK (1 << 20)
#define HPM_SEL_SCLK_MPLL (1 << 21)
#define APLL_CON0_LOCKED (1 << 29)
#define MPLL_CON0_LOCKED (1 << 29)
#define BPLL_CON0_LOCKED (1 << 29)
#define CPLL_CON0_LOCKED (1 << 29)
#define EPLL_CON0_LOCKED (1 << 29)
#define GPLL_CON0_LOCKED (1 << 29)
#define VPLL_CON0_LOCKED (1 << 29)
#define CLK_REG_DISABLE 0x0
#define TOP2_VAL 0x0110000
/* CLK_SRC_PERIC0 */
#define PWM_SEL 6
#define UART3_SEL 6
#define UART2_SEL 6
#define UART1_SEL 6
#define UART0_SEL 6
/* SRC_CLOCK = SCLK_MPLL */
#define CLK_SRC_PERIC0_VAL ((PWM_SEL << 24) \
| (UART3_SEL << 12) \
| (UART2_SEL << 8) \
| (UART1_SEL << 4) \
| (UART0_SEL))
/* CLK_SRC_PERIC1 */
/* SRC_CLOCK = SCLK_MPLL */
#define SPI0_SEL 6
#define SPI1_SEL 6
#define SPI2_SEL 6
#define CLK_SRC_PERIC1_VAL ((SPI2_SEL << 24) \
| (SPI1_SEL << 20) \
| (SPI0_SEL << 16))
/* SCLK_SRC_ISP - set SPI0/1 to 6 = SCLK_MPLL_USER */
#define SPI0_ISP_SEL 6
#define SPI1_ISP_SEL 6
#define SCLK_SRC_ISP_VAL (SPI1_ISP_SEL << 4) \
| (SPI0_ISP_SEL << 0)
/* SCLK_DIV_ISP - set SPI0/1 to 0xf = divide by 16 */
#define SPI0_ISP_RATIO 0xf
#define SPI1_ISP_RATIO 0xf
#define SCLK_DIV_ISP_VAL (SPI1_ISP_RATIO << 12) \
| (SPI0_ISP_RATIO << 0)
/* CLK_DIV_PERIL0 */
#define UART5_RATIO 7
#define UART4_RATIO 7
#define UART3_RATIO 7
#define UART2_RATIO 7
#define UART1_RATIO 7
#define UART0_RATIO 7
#define CLK_DIV_PERIC0_VAL ((UART3_RATIO << 12) \
| (UART2_RATIO << 8) \
| (UART1_RATIO << 4) \
| (UART0_RATIO))
/* CLK_DIV_PERIC1 */
#define SPI1_RATIO 0x7
#define SPI0_RATIO 0xf
#define SPI1_SUB_RATIO 0x0
#define SPI0_SUB_RATIO 0x0
#define CLK_DIV_PERIC1_VAL ((SPI1_SUB_RATIO << 24) \
| ((SPI1_RATIO << 16) \
| (SPI0_SUB_RATIO << 8) \
| (SPI0_RATIO << 0)))
/* CLK_DIV_PERIC2 */
#define SPI2_RATIO 0xf
#define SPI2_SUB_RATIO 0x0
#define CLK_DIV_PERIC2_VAL ((SPI2_SUB_RATIO << 8) \
| (SPI2_RATIO << 0))
/* CLK_DIV_FSYS2 */
#define MMC2_RATIO_MASK 0xf
#define MMC2_RATIO_VAL 0x3
#define MMC2_RATIO_OFFSET 0
#define MMC2_PRE_RATIO_MASK 0xff
#define MMC2_PRE_RATIO_VAL 0x9
#define MMC2_PRE_RATIO_OFFSET 8
#define MMC3_RATIO_MASK 0xf
#define MMC3_RATIO_VAL 0x1
#define MMC3_RATIO_OFFSET 16
#define MMC3_PRE_RATIO_MASK 0xff
#define MMC3_PRE_RATIO_VAL 0x0
#define MMC3_PRE_RATIO_OFFSET 24
/* CLK_SRC_LEX */
#define CLK_SRC_LEX_VAL 0x0
/* CLK_DIV_LEX */
#define CLK_DIV_LEX_VAL 0x10
/* CLK_DIV_R0X */
#define CLK_DIV_R0X_VAL 0x10
/* CLK_DIV_L0X */
#define CLK_DIV_R1X_VAL 0x10
/* CLK_DIV_ISP0 */
#define CLK_DIV_ISP0_VAL 0x31
/* CLK_DIV_ISP1 */
#define CLK_DIV_ISP1_VAL 0x0
/* CLK_DIV_ISP2 */
#define CLK_DIV_ISP2_VAL 0x1
/* CLK_SRC_DISP1_0 */
#define CLK_SRC_DISP1_0_VAL 0x6
/*
* DIV_DISP1_0
* For DP, divisor should be 2
*/
#define CLK_DIV_DISP1_0_FIMD1 (2 << 0)
/* CLK_GATE_IP_DISP1 */
#define CLK_GATE_DP1_ALLOW (1 << 4)
/* CLK_GATE_IP_SYSRGT */
#define CLK_C2C_MASK (1 << 1)
/* CLK_GATE_IP_ACP */
#define CLK_SMMUG2D_MASK (1 << 7)
#define CLK_SMMUSSS_MASK (1 << 6)
#define CLK_SMMUMDMA_MASK (1 << 5)
#define CLK_ID_REMAPPER_MASK (1 << 4)
#define CLK_G2D_MASK (1 << 3)
#define CLK_SSS_MASK (1 << 2)
#define CLK_MDMA_MASK (1 << 1)
#define CLK_SECJTAG_MASK (1 << 0)
/* CLK_GATE_BUS_SYSLFT */
#define CLK_EFCLK_MASK (1 << 16)
/* CLK_GATE_IP_ISP0 */
#define CLK_UART_ISP_MASK (1 << 31)
#define CLK_WDT_ISP_MASK (1 << 30)
#define CLK_PWM_ISP_MASK (1 << 28)
#define CLK_MTCADC_ISP_MASK (1 << 27)
#define CLK_I2C1_ISP_MASK (1 << 26)
#define CLK_I2C0_ISP_MASK (1 << 25)
#define CLK_MPWM_ISP_MASK (1 << 24)
#define CLK_MCUCTL_ISP_MASK (1 << 23)
#define CLK_INT_COMB_ISP_MASK (1 << 22)
#define CLK_SMMU_MCUISP_MASK (1 << 13)
#define CLK_SMMU_SCALERP_MASK (1 << 12)
#define CLK_SMMU_SCALERC_MASK (1 << 11)
#define CLK_SMMU_FD_MASK (1 << 10)
#define CLK_SMMU_DRC_MASK (1 << 9)
#define CLK_SMMU_ISP_MASK (1 << 8)
#define CLK_GICISP_MASK (1 << 7)
#define CLK_ARM9S_MASK (1 << 6)
#define CLK_MCUISP_MASK (1 << 5)
#define CLK_SCALERP_MASK (1 << 4)
#define CLK_SCALERC_MASK (1 << 3)
#define CLK_FD_MASK (1 << 2)
#define CLK_DRC_MASK (1 << 1)
#define CLK_ISP_MASK (1 << 0)
/* CLK_GATE_IP_ISP1 */
#define CLK_SPI1_ISP_MASK (1 << 13)
#define CLK_SPI0_ISP_MASK (1 << 12)
#define CLK_SMMU3DNR_MASK (1 << 7)
#define CLK_SMMUDIS1_MASK (1 << 6)
#define CLK_SMMUDIS0_MASK (1 << 5)
#define CLK_SMMUODC_MASK (1 << 4)
#define CLK_3DNR_MASK (1 << 2)
#define CLK_DIS_MASK (1 << 1)
#define CLK_ODC_MASK (1 << 0)
/* CLK_GATE_IP_GSCL */
#define CLK_SMMUFIMC_LITE2_MASK (1 << 20)
#define CLK_SMMUFIMC_LITE1_MASK (1 << 12)
#define CLK_SMMUFIMC_LITE0_MASK (1 << 11)
#define CLK_SMMUGSCL3_MASK (1 << 10)
#define CLK_SMMUGSCL2_MASK (1 << 9)
#define CLK_SMMUGSCL1_MASK (1 << 8)
#define CLK_SMMUGSCL0_MASK (1 << 7)
#define CLK_GSCL_WRAP_B_MASK (1 << 6)
#define CLK_GSCL_WRAP_A_MASK (1 << 5)
#define CLK_CAMIF_TOP_MASK (1 << 4)
#define CLK_GSCL3_MASK (1 << 3)
#define CLK_GSCL2_MASK (1 << 2)
#define CLK_GSCL1_MASK (1 << 1)
#define CLK_GSCL0_MASK (1 << 0)
/* CLK_GATE_IP_MFC */
#define CLK_SMMUMFCR_MASK (1 << 2)
#define CLK_SMMUMFCL_MASK (1 << 1)
#define CLK_MFC_MASK (1 << 0)
#define SCLK_MPWM_ISP_MASK (1 << 0)
/* CLK_GATE_IP_DISP1 */
#define CLK_SMMUTVX_MASK (1 << 9)
#define CLK_ASYNCTVX_MASK (1 << 7)
#define CLK_HDMI_MASK (1 << 6)
#define CLK_MIXER_MASK (1 << 5)
#define CLK_DSIM1_MASK (1 << 3)
/* CLK_GATE_IP_GEN */
#define CLK_SMMUMDMA1_MASK (1 << 9)
#define CLK_SMMUJPEG_MASK (1 << 7)
#define CLK_SMMUROTATOR_MASK (1 << 6)
#define CLK_MDMA1_MASK (1 << 4)
#define CLK_JPEG_MASK (1 << 2)
#define CLK_ROTATOR_MASK (1 << 1)
/* CLK_GATE_IP_FSYS */
#define CLK_WDT_IOP_MASK (1 << 30)
#define CLK_SMMUMCU_IOP_MASK (1 << 26)
#define CLK_SATA_PHY_I2C_MASK (1 << 25)
#define CLK_SATA_PHY_CTRL_MASK (1 << 24)
#define CLK_MCUCTL_MASK (1 << 23)
#define CLK_NFCON_MASK (1 << 22)
#define CLK_SMMURTIC_MASK (1 << 11)
#define CLK_RTIC_MASK (1 << 9)
#define CLK_MIPI_HSI_MASK (1 << 8)
#define CLK_USBOTG_MASK (1 << 7)
#define CLK_SATA_MASK (1 << 6)
#define CLK_PDMA1_MASK (1 << 2)
#define CLK_PDMA0_MASK (1 << 1)
#define CLK_MCU_IOP_MASK (1 << 0)
/* CLK_GATE_IP_PERIC */
#define CLK_HS_I2C3_MASK (1 << 31)
#define CLK_HS_I2C2_MASK (1 << 30)
#define CLK_HS_I2C1_MASK (1 << 29)
#define CLK_HS_I2C0_MASK (1 << 28)
#define CLK_AC97_MASK (1 << 27)
#define CLK_SPDIF_MASK (1 << 26)
#define CLK_PCM2_MASK (1 << 23)
#define CLK_PCM1_MASK (1 << 22)
#define CLK_I2S2_MASK (1 << 21)
#define CLK_I2S1_MASK (1 << 20)
#define CLK_SPI2_MASK (1 << 18)
#define CLK_SPI0_MASK (1 << 16)
#define CLK_I2CHDMI_MASK (1 << 14)
#define CLK_I2C7_MASK (1 << 13)
#define CLK_I2C6_MASK (1 << 12)
#define CLK_I2C5_MASK (1 << 11)
#define CLK_I2C4_MASK (1 << 10)
#define CLK_I2C3_MASK (1 << 9)
#define CLK_I2C2_MASK (1 << 8)
#define CLK_I2C1_MASK (1 << 7)
#define CLK_I2C0_MASK (1 << 6)
/* CLK_GATE_IP_PERIS */
#define CLK_RTC_MASK (1 << 20)
#define CLK_TZPC9_MASK (1 << 15)
#define CLK_TZPC8_MASK (1 << 14)
#define CLK_TZPC7_MASK (1 << 13)
#define CLK_TZPC6_MASK (1 << 12)
#define CLK_TZPC5_MASK (1 << 11)
#define CLK_TZPC4_MASK (1 << 10)
#define CLK_TZPC3_MASK (1 << 9)
#define CLK_TZPC2_MASK (1 << 8)
#define CLK_TZPC1_MASK (1 << 7)
#define CLK_TZPC0_MASK (1 << 6)
#define CLK_CHIPID_MASK (1 << 0)
/* CLK_GATE_BLOCK */
#define CLK_ACP_MASK (1 << 7)
/* CLK_GATE_IP_CDREX */
#define CLK_TZASC_DRBXW_MASK (1 << 23)
#define CLK_TZASC_DRBXR_MASK (1 << 22)
#define CLK_TZASC_XLBXW_MASK (1 << 21)
#define CLK_TZASC_XLBXR_MASK (1 << 20)
#define CLK_TZASC_XR1BXW_MASK (1 << 19)
#define CLK_TZASC_XR1BXR_MASK (1 << 18)
#define CLK_DPHY1_MASK (1 << 5)
#define CLK_DPHY0_MASK (1 << 4)
/*
* TZPC Register Value :
* R0SIZE: 0x0 : Size of secured ram
*/
#define R0SIZE 0x0
/*
* TZPC Decode Protection Register Value :
* DECPROTXSET: 0xFF : Set Decode region to non-secure
*/
#define DECPROTXSET 0xFF
#define LPDDR3PHY_CTRL_PHY_RESET (1 << 0)
#define LPDDR3PHY_CTRL_PHY_RESET_OFF (0 << 0)
#define PHY_CON0_RESET_VAL 0x17020a40
#define P0_CMD_EN (1 << 14)
#define BYTE_RDLVL_EN (1 << 13)
#define CTRL_SHGATE (1 << 8)
#define PHY_CON1_RESET_VAL 0x09210100
#define CTRL_GATEDURADJ_MASK (0xf << 20)
#define PHY_CON2_RESET_VAL 0x00010004
#define INIT_DESKEW_EN (1 << 6)
#define RDLVL_GATE_EN (1 << 24)
/*ZQ Configurations */
#define PHY_CON16_RESET_VAL 0x08000304
#define ZQ_CLK_DIV_EN (1 << 18)
#define ZQ_MANUAL_STR (1 << 1)
#define ZQ_DONE (1 << 0)
#define CTRL_RDLVL_GATE_ENABLE 1
#define CTRL_RDLVL_GATE_DISABLE 1
/* Direct Command */
#define DIRECT_CMD_NOP 0x07000000
#define DIRECT_CMD_PALL 0x01000000
#define DIRECT_CMD_ZQINIT 0x0a000000
#define DIRECT_CMD_CHANNEL_SHIFT 28
#define DIRECT_CMD_CHIP_SHIFT 20
/* DMC PHY Control0 register */
#define PHY_CONTROL0_RESET_VAL 0x0
#define MEM_TERM_EN (1 << 31) /* Termination enable for memory */
#define PHY_TERM_EN (1 << 30) /* Termination enable for PHY */
#define DMC_CTRL_SHGATE (1 << 29) /* Duration of DQS gating signal */
#define FP_RSYNC (1 << 3) /* Force DLL resyncronization */
/* Driver strength for CK, CKE, CS & CA */
#define IMP_OUTPUT_DRV_40_OHM 0x5
#define IMP_OUTPUT_DRV_30_OHM 0x7
#define CA_CK_DRVR_DS_OFFSET 9
#define CA_CKE_DRVR_DS_OFFSET 6
#define CA_CS_DRVR_DS_OFFSET 3
#define CA_ADR_DRVR_DS_OFFSET 0
#define PHY_CON42_CTRL_BSTLEN_SHIFT 8
#define PHY_CON42_CTRL_RDLAT_SHIFT 0
struct mem_timings;
/* Errors that we can encourter in low-level setup */
enum {
SETUP_ERR_OK,
SETUP_ERR_RDLV_COMPLETE_TIMEOUT = -1,
SETUP_ERR_ZQ_CALIBRATION_FAILURE = -2,
};
/* Functions common between LPDDR2 and DDR3 */
/* CPU info initialization code */
void cpu_info_init(void);
void mem_ctrl_init(void);
/*
* Memory variant specific initialization code
*
* @param mem Memory timings for this memory type.
* @param mem_iv_size Memory interleaving size is a configurable parameter
* which the DMC uses to decide how to split a memory
* chunk into smaller chunks to support concurrent
* accesses; may vary across boards.
* @param mem_reset Reset memory during initialization.
* @return 0 if ok, SETUP_ERR_... if there is a problem
*/
int ddr3_mem_ctrl_init(struct mem_timings *mem, unsigned long mem_iv_size,
int mem_reset);
/*
* Configure ZQ I/O interface
*
* @param mem Memory timings for this memory type.
* @param phy0_ctrl Pointer to struct containing PHY0 control reg
* @param phy1_ctrl Pointer to struct containing PHY1 control reg
* @return 0 if ok, -1 on error
*/
int dmc_config_zq(struct mem_timings *mem,
struct exynos5_phy_control *phy0_ctrl,
struct exynos5_phy_control *phy1_ctrl);
/*
* Send NOP and MRS/EMRS Direct commands
*
* @param mem Memory timings for this memory type.
* @param dmc Pointer to struct of DMC registers
*/
void dmc_config_mrs(struct mem_timings *mem, struct exynos5_dmc *dmc);
/*
* Send PALL Direct commands
*
* @param mem Memory timings for this memory type.
* @param dmc Pointer to struct of DMC registers
*/
void dmc_config_prech(struct mem_timings *mem, struct exynos5_dmc *dmc);
/*
* Configure the memconfig and membaseconfig registers
*
* @param mem Memory timings for this memory type.
* @param exynos5_dmc Pointer to struct of DMC registers
*/
void dmc_config_memory(struct mem_timings *mem, struct exynos5_dmc *dmc);
/* Set the PS-Hold drive value */
void ps_hold_setup(void);
/*
* Reset the DLL. This function is common between DDR3 and LPDDR2.
* However, the reset value is different. So we are passing a flag
* ddr_mode to distinguish between LPDDR2 and DDR3.
*
* @param exynos5_dmc Pointer to struct of DMC registers
* @param ddr_mode Type of DDR memory
*/
void update_reset_dll(struct exynos5_dmc *, enum ddr_mode);
#endif

View File

@ -0,0 +1,222 @@
/*
* This file is part of the coreboot project.
*
* Copyright (C) 2011 Samsung Electronics
* Copyright 2013 Google Inc.
*
* 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; version 2 of the License.
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <console/console.h>
#include <arch/io.h>
#include <stdlib.h>
#include <assert.h>
#include "gpio.h"
#include "clk.h"
#include "spi.h"
#define OM_STAT (0x1f << 1)
#define EXYNOS_BASE_SPI1 ((void *)0x12d30000)
#if defined(CONFIG_DEBUG_SPI) && CONFIG_DEBUG_SPI
# define DEBUG_SPI(x,...) printk(BIOS_DEBUG, "EXYNOS_SPI: " x)
#else
# define DEBUG_SPI(x,...)
#endif
static void exynos_spi_rx_tx(struct exynos_spi *regs, int todo,
void *dinp, void const *doutp, int i)
{
int rx_lvl, tx_lvl;
unsigned int *rxp = (unsigned int *)(dinp + (i * (32 * 1024)));
unsigned int out_bytes, in_bytes;
// TODO In currrent implementation, every read/write must be aligned to
// 4 bytes, otherwise you may get timeout or other unexpected results.
ASSERT(todo % 4 == 0);
out_bytes = in_bytes = todo;
setbits_le32(&regs->ch_cfg, SPI_CH_RST);
clrbits_le32(&regs->ch_cfg, SPI_CH_RST);
writel(((todo * 8) / 32) | SPI_PACKET_CNT_EN, &regs->pkt_cnt);
while (in_bytes) {
uint32_t spi_sts;
int temp;
spi_sts = readl(&regs->spi_sts);
rx_lvl = ((spi_sts >> 15) & 0x7f);
tx_lvl = ((spi_sts >> 6) & 0x7f);
while (tx_lvl < 32 && out_bytes) {
// TODO The "writing" (tx) is not supported now; that's
// why we write garbage to keep driving FIFO clock.
temp = 0xffffffff;
writel(temp, &regs->tx_data);
out_bytes -= 4;
tx_lvl += 4;
}
while (rx_lvl >= 4 && in_bytes) {
temp = readl(&regs->rx_data);
if (rxp)
*rxp++ = temp;
in_bytes -= 4;
rx_lvl -= 4;
}
}
}
/* set up SPI channel */
int exynos_spi_open(struct exynos_spi *regs)
{
/* set the spi1 GPIO */
/* set pktcnt and enable it */
writel(4 | SPI_PACKET_CNT_EN, &regs->pkt_cnt);
/* set FB_CLK_SEL */
writel(SPI_FB_DELAY_180, &regs->fb_clk);
/* set CH_WIDTH and BUS_WIDTH as word */
setbits_le32(&regs->mode_cfg,
SPI_MODE_CH_WIDTH_WORD | SPI_MODE_BUS_WIDTH_WORD);
clrbits_le32(&regs->ch_cfg, SPI_CH_CPOL_L); /* CPOL: active high */
/* clear rx and tx channel if set priveously */
clrbits_le32(&regs->ch_cfg, SPI_RX_CH_ON | SPI_TX_CH_ON);
setbits_le32(&regs->swap_cfg,
SPI_RX_SWAP_EN | SPI_RX_BYTE_SWAP | SPI_RX_HWORD_SWAP);
/* do a soft reset */
setbits_le32(&regs->ch_cfg, SPI_CH_RST);
clrbits_le32(&regs->ch_cfg, SPI_CH_RST);
/* now set rx and tx channel ON */
setbits_le32(&regs->ch_cfg, SPI_RX_CH_ON | SPI_TX_CH_ON | SPI_CH_HS_EN);
clrbits_le32(&regs->cs_reg, SPI_SLAVE_SIG_INACT); /* CS low */
return 0;
}
int exynos_spi_read(struct exynos_spi *regs, void *dest, u32 len, u32 off)
{
int upto, todo;
int i;
/* Send read instruction (0x3h) followed by a 24 bit addr */
writel((SF_READ_DATA_CMD << 24) | off, &regs->tx_data);
/* waiting for TX done */
while (!(readl(&regs->spi_sts) & SPI_ST_TX_DONE));
for (upto = 0, i = 0; upto < len; upto += todo, i++) {
todo = MIN(len - upto, (1 << 15));
exynos_spi_rx_tx(regs, todo, dest, (void *)(off), i);
}
setbits_le32(&regs->cs_reg, SPI_SLAVE_SIG_INACT);/* make the CS high */
/*
* Let put controller mode to BYTE as
* SPI driver does not support WORD mode yet
*/
clrbits_le32(&regs->mode_cfg,
SPI_MODE_CH_WIDTH_WORD | SPI_MODE_BUS_WIDTH_WORD);
writel(0, &regs->swap_cfg);
return len;
}
int exynos_spi_close(struct exynos_spi *regs)
{
/*
* Flush spi tx, rx fifos and reset the SPI controller
* and clear rx/tx channel
*/
clrsetbits_le32(&regs->ch_cfg, SPI_CH_HS_EN, SPI_CH_RST);
clrbits_le32(&regs->ch_cfg, SPI_CH_RST);
clrbits_le32(&regs->ch_cfg, SPI_TX_CH_ON | SPI_RX_CH_ON);
return 0;
}
// SPI as CBFS media.
struct exynos_spi_media {
struct exynos_spi *regs;
struct cbfs_simple_buffer buffer;
};
static int exynos_spi_cbfs_open(struct cbfs_media *media) {
struct exynos_spi_media *spi = (struct exynos_spi_media*)media->context;
DEBUG_SPI("exynos_spi_cbfs_open\n");
return exynos_spi_open(spi->regs);
}
static int exynos_spi_cbfs_close(struct cbfs_media *media) {
struct exynos_spi_media *spi = (struct exynos_spi_media*)media->context;
DEBUG_SPI("exynos_spi_cbfs_close\n");
return exynos_spi_close(spi->regs);
}
static size_t exynos_spi_cbfs_read(struct cbfs_media *media, void *dest,
size_t offset, size_t count) {
struct exynos_spi_media *spi = (struct exynos_spi_media*)media->context;
int bytes;
DEBUG_SPI("exynos_spi_cbfs_read(%u)\n", count);
bytes = exynos_spi_read(spi->regs, dest, count, offset);
// Flush and re-open the device.
exynos_spi_close(spi->regs);
exynos_spi_open(spi->regs);
return bytes;
}
static void *exynos_spi_cbfs_map(struct cbfs_media *media, size_t offset,
size_t count) {
struct exynos_spi_media *spi = (struct exynos_spi_media*)media->context;
DEBUG_SPI("exynos_spi_cbfs_map\n");
// See exynos_spi_rx_tx for I/O alignment limitation.
if (count % 4)
count += 4 - (count % 4);
return cbfs_simple_buffer_map(&spi->buffer, media, offset, count);
}
static void *exynos_spi_cbfs_unmap(struct cbfs_media *media,
const void *address) {
struct exynos_spi_media *spi = (struct exynos_spi_media*)media->context;
DEBUG_SPI("exynos_spi_cbfs_unmap\n");
return cbfs_simple_buffer_unmap(&spi->buffer, address);
}
int initialize_exynos_spi_cbfs_media(struct cbfs_media *media,
void *buffer_address,
size_t buffer_size) {
// TODO Replace static variable to support multiple streams.
static struct exynos_spi_media context;
DEBUG_SPI("initialize_exynos_spi_cbfs_media\n");
context.regs = EXYNOS_BASE_SPI1;
context.buffer.allocated = context.buffer.last_allocate = 0;
context.buffer.buffer = buffer_address;
context.buffer.size = buffer_size;
media->context = (void*)&context;
media->open = exynos_spi_cbfs_open;
media->close = exynos_spi_cbfs_close;
media->read = exynos_spi_cbfs_read;
media->map = exynos_spi_cbfs_map;
media->unmap = exynos_spi_cbfs_unmap;
return 0;
}
int init_default_cbfs_media(struct cbfs_media *media) {
return initialize_exynos_spi_cbfs_media(
media,
(void*)CONFIG_CBFS_CACHE_ADDRESS,
CONFIG_CBFS_CACHE_SIZE);
}

View File

@ -0,0 +1,98 @@
/*
* This file is part of the coreboot project.
*
* Copyright (C) 2012 Samsung Electronics
*
* 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; version 2 of the License.
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef CPU_SAMSUNG_EXYNOS5420_SPI_H
#define CPU_SAMSUNG_EXYNOS5420_SPI_H
/* This driver serves as a CBFS media source. */
#include <cbfs.h>
/* SPI peripheral register map; padded to 64KB */
struct exynos_spi {
unsigned int ch_cfg; /* 0x00 */
unsigned char reserved0[4];
unsigned int mode_cfg; /* 0x08 */
unsigned int cs_reg; /* 0x0c */
unsigned char reserved1[4];
unsigned int spi_sts; /* 0x14 */
unsigned int tx_data; /* 0x18 */
unsigned int rx_data; /* 0x1c */
unsigned int pkt_cnt; /* 0x20 */
unsigned char reserved2[4];
unsigned int swap_cfg; /* 0x28 */
unsigned int fb_clk; /* 0x2c */
unsigned char padding[0xffd0];
};
#define EXYNOS_SPI_MAX_FREQ 50000000
#define SPI_TIMEOUT_MS 10
#define SF_READ_DATA_CMD 0x3
/* SPI_CHCFG */
#define SPI_CH_HS_EN (1 << 6)
#define SPI_CH_RST (1 << 5)
#define SPI_SLAVE_MODE (1 << 4)
#define SPI_CH_CPOL_L (1 << 3)
#define SPI_CH_CPHA_B (1 << 2)
#define SPI_RX_CH_ON (1 << 1)
#define SPI_TX_CH_ON (1 << 0)
/* SPI_MODECFG */
#define SPI_MODE_CH_WIDTH_WORD (0x2 << 29)
#define SPI_MODE_BUS_WIDTH_WORD (0x2 << 17)
/* SPI_CSREG */
#define SPI_SLAVE_SIG_INACT (1 << 0)
/* SPI_STS */
#define SPI_ST_TX_DONE (1 << 25)
#define SPI_FIFO_LVL_MASK 0x1ff
#define SPI_TX_LVL_OFFSET 6
#define SPI_RX_LVL_OFFSET 15
/* Feedback Delay */
#define SPI_CLK_BYPASS (0 << 0)
#define SPI_FB_DELAY_90 (1 << 0)
#define SPI_FB_DELAY_180 (2 << 0)
#define SPI_FB_DELAY_270 (3 << 0)
/* Packet Count */
#define SPI_PACKET_CNT_EN (1 << 16)
/* Swap config */
#define SPI_TX_SWAP_EN (1 << 0)
#define SPI_TX_BYTE_SWAP (1 << 2)
#define SPI_TX_HWORD_SWAP (1 << 3)
#define SPI_TX_BYTE_SWAP (1 << 2)
#define SPI_RX_SWAP_EN (1 << 4)
#define SPI_RX_BYTE_SWAP (1 << 6)
#define SPI_RX_HWORD_SWAP (1 << 7)
/* API */
int exynos_spi_open(struct exynos_spi *regs);
int exynos_spi_read(struct exynos_spi *regs, void *dest, u32 len, u32 off);
int exynos_spi_close(struct exynos_spi *regs);
/* Serve as CBFS media source */
int initialize_exynos_spi_cbfs_media(struct cbfs_media *media,
void *buffer_address,
size_t buffer_size);
#endif

View File

@ -0,0 +1,37 @@
/*
* This file is part of the coreboot project.
*
* Copyright (c) 2012 Samsung Electronics Co., Ltd.
*
* 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; version 2 of the License.
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/* Register map for Exynos5 sysreg */
#ifndef CPU_SAMSUNG_EXYNOS5420_SYSREG_H
#define CPU_SAMSUNG_EXYNOS5420_SYSREG_H
/* sysreg map */
struct exynos5_sysreg {
/* Add registers as and when required */
unsigned char res1[0x214];
unsigned int disp1blk_cfg;
unsigned char res2[0x18];
unsigned int usb20_phy_cfg;
};
#define FIMDBYPASS_DISP1 (1 << 15)
#define USB20_PHY_CFG_EN (1 << 0)
#endif

View File

@ -0,0 +1,110 @@
/*
* This file is part of the coreboot project.
*
* Copyright (C) 2009 Samsung Electronics
*
* 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; version 2 of the License.
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <console/console.h>
#include <arch/io.h>
#include <timer.h>
#include <delay.h>
#include "timer.h"
#include "pwm.h"
#include "clk.h"
#include "cpu.h"
static unsigned long long timer_reset_value;
static unsigned long lastinc;
/* macro to read the 16 bit timer */
static inline struct s5p_timer *s5p_get_base_timer(void)
{
return samsung_get_base_timer();
}
/**
* Read the countdown timer.
*
* This operates at 1MHz and counts downwards. It will wrap about every
* hour (2^32 microseconds).
*
* @return current value of timer
*/
static unsigned long timer_get_us_down(void)
{
struct s5p_timer *const timer = s5p_get_base_timer();
return readl(&timer->tcnto4);
}
void init_timer(void)
{
/* Timer may have been enabled in SPL */
if (!pwm_check_enabled(4)) {
/* PWM Timer 4 */
pwm_init(4, MUX_DIV_4, 0);
pwm_config(4, 100000, 100000);
pwm_enable(4);
/* Use this as the current monotonic time in us */
timer_reset_value = 0;
/* Use this as the last timer value we saw */
lastinc = timer_get_us_down();
}
}
/*
* timer without interrupts
*/
unsigned long get_timer(unsigned long base)
{
unsigned long now = timer_get_us_down();
/*
* Increment the time by the amount elapsed since the last read.
* The timer may have wrapped around, but it makes no difference to
* our arithmetic here.
*/
timer_reset_value += lastinc - now;
lastinc = now;
/* Divide by 1000 to convert from us to ms */
return timer_reset_value / 1000 - base;
}
/* delay x useconds */
void udelay(unsigned usec)
{
struct mono_time current, end;
timer_monotonic_get(&current);
end = current;
mono_time_add_usecs(&end, usec);
if (mono_time_after(&current, &end)) {
printk(BIOS_EMERG, "udelay: 0x%08x is impossibly large\n",
usec);
/* There's not much we can do if usec is too big. Use a long,
* paranoid delay value and hope for the best... */
end = current;
mono_time_add_usecs(&end, USECS_PER_SEC);
}
while (mono_time_before(&current, &end))
timer_monotonic_get(&current);
}

View File

@ -0,0 +1,25 @@
/*
* This file is part of the coreboot project.
*
* Copyright 2013 Google Inc.
*
* 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; version 2 of the License.
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef CPU_SAMSUNG_EXYNOS5420_TIMER_H
#define CPU_SAMSUNG_EXYNOS5420_TIMER_H
unsigned long get_timer(unsigned long base);
#endif

View File

@ -0,0 +1,215 @@
/*
* This file is part of the coreboot project.
*
* Copyright (C) 2012 Samsung Electronics
* Copyright 2013 Google Inc.
*
* 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; version 2 of the License.
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/* EXYNOS - Thermal Management Unit */
#include <console/console.h>
#include <arch/io.h>
#include "power.h"
#include "tmu.h"
#define TRIMINFO_RELOAD 1
#define CORE_EN 1
#define THERM_TRIP_EN (1 << 12)
#define INTEN_RISE0 1
#define INTEN_RISE1 (1 << 4)
#define INTEN_RISE2 (1 << 8)
#define INTEN_FALL0 (1 << 16)
#define INTEN_FALL1 (1 << 20)
#define INTEN_FALL2 (1 << 24)
#define TRIM_INFO_MASK 0xff
#define INTCLEAR_RISE0 1
#define INTCLEAR_RISE1 (1 << 4)
#define INTCLEAR_RISE2 (1 << 8)
#define INTCLEAR_FALL0 (1 << 16)
#define INTCLEAR_FALL1 (1 << 20)
#define INTCLEAR_FALL2 (1 << 24)
#define INTCLEARALL (INTCLEAR_RISE0 | INTCLEAR_RISE1 | \
INTCLEAR_RISE2 | INTCLEAR_FALL0 | \
INTCLEAR_FALL1 | INTCLEAR_FALL2)
struct tmu_info exynos5420_tmu_info = {
.tmu_base = 0x10060000,
.tmu_mux = 6,
.data = {
.ts = {
.min_val = 25,
.max_val = 125,
.start_warning = 95,
.start_tripping = 105,
.hardware_tripping = 110,
},
.efuse_min_value = 40,
.efuse_value = 55,
.efuse_max_value = 100,
.slope = 0x10008802,
},
.dc_value = 25,
};
/*
* After reading temperature code from register, compensating
* its value and calculating celsius temperatue,
* get current temperatue.
*
* @return current temperature of the chip as sensed by TMU
*/
static int get_cur_temp(struct tmu_info *info)
{
int cur_temp;
struct tmu_reg *reg = (struct tmu_reg *)info->tmu_base;
/* Temperature code range between min 25 and max 125 */
cur_temp = readl(&reg->current_temp) & 0xff;
/* Calibrate current temperature */
if (cur_temp)
cur_temp = cur_temp - info->te1 + info->dc_value;
return cur_temp;
}
/*
* Monitors status of the TMU device and exynos temperature
*
* @info TMU info
* @temp pointer to the current temperature value
* @return enum tmu_status_t value, code indicating event to execute
*/
enum tmu_status_t tmu_monitor(struct tmu_info *info, int *temp)
{
if (info->tmu_state == TMU_STATUS_INIT)
return -1;
int cur_temp;
struct tmu_data *data = &info->data;
/* Read current temperature of the SOC */
cur_temp = get_cur_temp(info);
*temp = cur_temp;
/* Temperature code lies between min 25 and max 125 */
if (cur_temp >= data->ts.start_tripping &&
cur_temp <= data->ts.max_val)
return TMU_STATUS_TRIPPED;
else if (cur_temp >= data->ts.start_warning)
return TMU_STATUS_WARNING;
else if (cur_temp < data->ts.start_warning &&
cur_temp >= data->ts.min_val)
return TMU_STATUS_NORMAL;
/* Temperature code does not lie between min 25 and max 125 */
else {
info->tmu_state = TMU_STATUS_INIT;
printk(BIOS_DEBUG, "EXYNOS_TMU: Thermal reading failed\n");
return -1;
}
return 0;
}
/*
* Calibrate and calculate threshold values and
* enable interrupt levels
*
* @param info pointer to the tmu_info struct
*/
static void tmu_setup_parameters(struct tmu_info *info)
{
unsigned int te_temp, con;
unsigned int warning_code, trip_code, hwtrip_code;
unsigned int cooling_temp;
unsigned int rising_value;
struct tmu_data *data = &info->data;
struct tmu_reg *reg = (struct tmu_reg *)info->tmu_base;
/* Must reload for using efuse value at EXYNOS */
writel(TRIMINFO_RELOAD, &reg->triminfo_control);
/* Get the compensation parameter */
te_temp = readl(&reg->triminfo);
info->te1 = te_temp & TRIM_INFO_MASK;
info->te2 = ((te_temp >> 8) & TRIM_INFO_MASK);
if ((data->efuse_min_value > info->te1) ||
(info->te1 > data->efuse_max_value)
|| (info->te2 != 0))
info->te1 = data->efuse_value;
/* Get RISING & FALLING Threshold value */
warning_code = data->ts.start_warning
+ info->te1 - info->dc_value;
trip_code = data->ts.start_tripping
+ info->te1 - info->dc_value;
hwtrip_code = data->ts.hardware_tripping
+ info->te1 - info->dc_value;
cooling_temp = 0;
rising_value = ((warning_code << 8) |
(trip_code << 16) |
(hwtrip_code << 24));
/* Set interrupt level */
writel(rising_value, &reg->threshold_temp_rise);
writel(cooling_temp, &reg->threshold_temp_fall);
/*
* Need to init all register settings after getting parameter info
* [28:23] vref [11:8] slope - Tuning parameter
*
* WARNING: this slope value writes into many bits in the tmu_control
* register, with the default FDT value of 268470274 (0x10008802)
* we are using this essentially sets the default register setting
* from the TRM for tmu_control.
* TODO(bhthompson): rewrite this code such that we are not performing
* a hard wipe of tmu_control and re verify functionality.
*/
writel(data->slope, &reg->tmu_control);
writel(INTCLEARALL, &reg->intclear);
/* TMU core enable */
con = readl(&reg->tmu_control);
con |= (info->tmu_mux << 20) | THERM_TRIP_EN | CORE_EN;
writel(con, &reg->tmu_control);
/* Enable HW thermal trip */
power_enable_hw_thermal_trip();
/* LEV1 LEV2 interrupt enable */
writel(INTEN_RISE1 | INTEN_RISE2, &reg->inten);
}
/*
* Initialize TMU device
*
* @return int value, 0 for success
*/
int tmu_init(struct tmu_info *info)
{
info->tmu_state = TMU_STATUS_INIT;
tmu_setup_parameters(info);
info->tmu_state = TMU_STATUS_NORMAL;
return 0;
}

View File

@ -0,0 +1,134 @@
/*
* This file is part of the coreboot project.
*
* Copyright (c) 2012 Samsung Electronics Co., Ltd.
*
* 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; version 2 of the License.
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/* EXYNOS - Thermal Management Unit */
#ifndef CPU_SAMSUNG_EXYNOS5420_TMU_H
#define CPU_SAMSUNG_EXYNOS5420_TMU_H
struct tmu_reg {
unsigned triminfo;
unsigned rsvd1;
unsigned rsvd2;
unsigned rsvd3;
unsigned rsvd4;
unsigned triminfo_control;
unsigned rsvd5;
unsigned rsvd6;
unsigned tmu_control;
unsigned rsvd7;
unsigned tmu_status;
unsigned sampling_internal;
unsigned counter_value0;
unsigned counter_value1;
unsigned rsvd8;
unsigned rsvd9;
unsigned current_temp;
unsigned rsvd10;
unsigned rsvd11;
unsigned rsvd12;
unsigned threshold_temp_rise;
unsigned threshold_temp_fall;
unsigned rsvd13;
unsigned rsvd14;
unsigned past_temp3_0;
unsigned past_temp7_4;
unsigned past_temp11_8;
unsigned past_temp15_12;
unsigned inten;
unsigned intstat;
unsigned intclear;
unsigned rsvd15;
unsigned emul_con;
};
enum tmu_status_t {
TMU_STATUS_INIT = 0,
TMU_STATUS_NORMAL,
TMU_STATUS_WARNING,
TMU_STATUS_TRIPPED,
};
/* Tmeperature threshold values for various thermal events */
struct temperature_params {
/* minimum value in temperature code range */
unsigned int min_val;
/* maximum value in temperature code range */
unsigned int max_val;
/* temperature threshold to start warning */
unsigned int start_warning;
/* temperature threshold CPU tripping */
unsigned int start_tripping;
/* temperature threshold for HW tripping */
unsigned int hardware_tripping;
};
/* Pre-defined values and thresholds for calibration of current temperature */
struct tmu_data {
/* pre-defined temperature thresholds */
struct temperature_params ts;
/* pre-defined efuse range minimum value */
unsigned int efuse_min_value;
/* pre-defined efuse value for temperature calibration */
unsigned int efuse_value;
/* pre-defined efuse range maximum value */
unsigned int efuse_max_value;
/* current temperature sensing slope */
unsigned int slope;
};
/* TMU device specific details and status */
struct tmu_info {
/* base Address for the TMU */
unsigned tmu_base;
/* mux Address for the TMU */
int tmu_mux;
/* pre-defined values for calibration and thresholds */
struct tmu_data data;
/* value required for triminfo_25 calibration */
unsigned int te1;
/* value required for triminfo_85 calibration */
unsigned int te2;
/* TMU DC value for threshold calculation */
int dc_value;
/* enum value indicating status of the TMU */
int tmu_state;
};
extern struct tmu_info *tmu_info;
/*
* Monitors status of the TMU device and exynos temperature
*
* @info pointer to TMU info struct
* @temp pointer to the current temperature value
* @return enum tmu_status_t value, code indicating event to execute
* and -1 on error
*/
enum tmu_status_t tmu_monitor(struct tmu_info *info, int *temp);
/*
* Initialize TMU device
*
* @info pointer to TMU info struct
* @return int value, 0 for success
*/
int tmu_init(struct tmu_info *info);
#endif /* CPU_SAMSUNG_EXYNOS5420_TMU_H */

View File

@ -0,0 +1,200 @@
/*
* This file is part of the coreboot project.
*
* Copyright (C) 2009 Samsung Electronics
*
* 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; version 2 of the License.
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <console/console.h> /* for __console definition */
#include <uart.h>
#include <arch/io.h>
#include "uart.h"
#include "clk.h"
#include "cpu.h"
#define RX_FIFO_COUNT_MASK 0xff
#define RX_FIFO_FULL_MASK (1 << 8)
#define TX_FIFO_FULL_MASK (1 << 24)
/* FIXME(dhendrix): exynos5 has 4 UARTs and its functions in u-boot take a
base_port argument. However console_driver functions do not. */
static uint32_t base_port = CONFIG_CONSOLE_SERIAL_UART_ADDRESS;
/*
* The coefficient, used to calculate the baudrate on S5P UARTs is
* calculated as
* C = UBRDIV * 16 + number_of_set_bits_in_UDIVSLOT
* however, section 31.6.11 of the datasheet doesn't recomment using 1 for 1,
* 3 for 2, ... (2^n - 1) for n, instead, they suggest using these constants:
*/
static const int udivslot[] = {
0,
0x0080,
0x0808,
0x0888,
0x2222,
0x4924,
0x4a52,
0x54aa,
0x5555,
0xd555,
0xd5d5,
0xddd5,
0xdddd,
0xdfdd,
0xdfdf,
0xffdf,
};
static void serial_setbrg_dev(void)
{
struct s5p_uart *uart = (struct s5p_uart *)base_port;
u32 uclk;
u32 baudrate = CONFIG_TTYS0_BAUD;
u32 val;
// All UARTs share the same clock.
uclk = clock_get_periph_rate(PERIPH_ID_UART3);
val = uclk / baudrate;
writel(val / 16 - 1, &uart->ubrdiv);
/*
* FIXME(dhendrix): the original uart.h had a "br_rest" value which
* does not seem relevant to the exynos5420... not entirely sure
* where/if we need to worry about it here
*/
#if 0
if (s5p_uart_divslot())
writel(udivslot[val % 16], &uart->rest.slot);
else
writeb(val % 16, &uart->rest.value);
#endif
}
/*
* Initialise the serial port with the given baudrate. The settings
* are always 8 data bits, no parity, 1 stop bit, no start bits.
*/
static void exynos5_init_dev(void)
{
struct s5p_uart *uart = (struct s5p_uart *)base_port;
// TODO initialize with correct peripheral id by base_port.
exynos_pinmux_config(PERIPH_ID_UART3, PINMUX_FLAG_NONE);
/* enable FIFOs */
writel(0x1, &uart->ufcon);
writel(0, &uart->umcon);
/* 8N1 */
writel(0x3, &uart->ulcon);
/* No interrupts, no DMA, pure polling */
writel(0x245, &uart->ucon);
serial_setbrg_dev();
}
static int exynos5_uart_err_check(int op)
{
struct s5p_uart *uart = (struct s5p_uart *)base_port;
unsigned int mask;
/*
* UERSTAT
* Break Detect [3]
* Frame Err [2] : receive operation
* Parity Err [1] : receive operation
* Overrun Err [0] : receive operation
*/
if (op)
mask = 0x8;
else
mask = 0xf;
return readl(&uart->uerstat) & mask;
}
/*
* Read a single byte from the serial port. Returns 1 on success, 0
* otherwise. When the function is succesfull, the character read is
* written into its argument c.
*/
static unsigned char exynos5_uart_rx_byte(void)
{
struct s5p_uart *uart = (struct s5p_uart *)base_port;
/* wait for character to arrive */
while (!(readl(&uart->ufstat) & (RX_FIFO_COUNT_MASK |
RX_FIFO_FULL_MASK))) {
if (exynos5_uart_err_check(0))
return 0;
}
return readb(&uart->urxh) & 0xff;
}
/*
* Output a single byte to the serial port.
*/
static void exynos5_uart_tx_byte(unsigned char data)
{
struct s5p_uart *uart = (struct s5p_uart *)base_port;
/* wait for room in the tx FIFO */
while ((readl(&uart->ufstat) & TX_FIFO_FULL_MASK)) {
if (exynos5_uart_err_check(1))
return;
}
writeb(data, &uart->utxh);
}
#if !defined(__PRE_RAM__)
static const struct console_driver exynos5_uart_console __console = {
.init = exynos5_init_dev,
.tx_byte = exynos5_uart_tx_byte,
// .tx_flush = exynos5_uart_tx_flush,
.rx_byte = exynos5_uart_rx_byte,
// .tst_byte = exynos5_uart_tst_byte,
};
uint32_t uartmem_getbaseaddr(void)
{
return base_port;
}
#else
void uart_init(void)
{
exynos5_init_dev();
}
unsigned char uart_rx_byte(void)
{
return exynos5_uart_rx_byte();
}
void uart_tx_byte(unsigned char data)
{
exynos5_uart_tx_byte(data);
}
void uart_tx_flush(void)
{
}
#endif

View File

@ -0,0 +1,54 @@
/*
* This file is part of the coreboot project.
*
* Copyright 2012 Google Inc.
* Copyright (C) 2009 Samsung Electronics
*
* 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; version 2 of the License.
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef CPU_SAMSUNG_EXYNOS5420_UART_H
#define CPU_SAMSUNG_EXYNOS5420_UART_H
#define EXYNOS5_UART0_BASE 0x12c00000
#define EXYNOS5_UART1_BASE 0x12c10000
#define EXYNOS5_UART2_BASE 0x12c20000
#define EXYNOS5_UART3_BASE 0x12c30000
#define EXYNOS5_ISP_UART_BASE 0x13190000
/* baudrate rest value */
union br_rest {
unsigned short slot; /* udivslot */
unsigned char value; /* ufracval */
};
struct s5p_uart {
unsigned int ulcon;
unsigned int ucon;
unsigned int ufcon;
unsigned int umcon;
unsigned int utrstat;
unsigned int uerstat;
unsigned int ufstat;
unsigned int umstat;
unsigned char utxh;
unsigned char res1[3];
unsigned char urxh;
unsigned char res2[3];
unsigned int ubrdiv;
union br_rest rest;
unsigned char res3[0xffd0];
};
#endif

View File

@ -0,0 +1,50 @@
/*
* This file is part of the coreboot project.
*
* Copyright 2013 Google Inc.
*
* 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; version 2 of the License.
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <console/console.h>
#include "power.h"
#include "wakeup.h"
void wakeup(void)
{
if (wakeup_need_reset())
power_reset();
power_init(); /* Ensure ps_hold_setup() for early wakeup. */
power_exit_wakeup();
/* Should never return. */
die("Failed to wake up.\n");
}
int get_wakeup_state(void)
{
uint32_t status = power_read_reset_status();
/* DIDLE/LPA can be resumed without clock reset (ex, bootblock),
* and SLEEP requires resetting clock (should be done in ROM stage).
*/
if (status == S5P_CHECK_DIDLE || status == S5P_CHECK_LPA)
return WAKEUP_DIRECT;
if (status == S5P_CHECK_SLEEP)
return WAKEUP_NEED_CLOCK_RESET;
return IS_NOT_WAKEUP;
}

View File

@ -0,0 +1,42 @@
/*
* This file is part of the coreboot project.
*
* Copyright 2013 Google Inc.
*
* 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; version 2 of the License.
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef CPU_SAMSUNG_EXYNOS5420_WAKEUP_H
#define CPU_SAMSUNG_EXYNOS5420_WAKEUP_H
/* Power Down Modes */
#define S5P_CHECK_SLEEP 0x00000BAD
#define S5P_CHECK_DIDLE 0xBAD00000
#define S5P_CHECK_LPA 0xABAD0000
enum {
// A normal boot (not suspend/resume)
IS_NOT_WAKEUP,
// A wake up event that can be resumed any time
WAKEUP_DIRECT,
// A wake up event that must be resumed only after
// clock and memory controllers are re-initialized
WAKEUP_NEED_CLOCK_RESET,
};
int wakeup_need_reset(void);
int get_wakeup_state(void);
void wakeup(void);
#endif /* CPU_SAMSUNG_EXYNOS5420_WAKEUP_H */

View File

@ -22,7 +22,7 @@ if BOARD_GOOGLE_SNOW
config BOARD_SPECIFIC_OPTIONS # dummy
def_bool y
select ARCH_ARMV7
select CPU_SAMSUNG_EXYNOS5
select CPU_SAMSUNG_EXYNOS5250
select HAVE_UART_MEMORY_MAPPED
select EC_GOOGLE_CHROMEEC
select EC_GOOGLE_CHROMEEC_I2C