WIP: Initial support for Samsung Exynos 5250 ARM CPU

Samsung SoC files, including Exynos5 (a Cortex-A15
implementation). Since this is an SoC we'll forego the x86-style
{north,south}bridge and cpu distinction. We may try to split some
stuff out before the final version if prudent.

Change-Id: Ie068e9dc3dd836c83d90e282b10d5202e7a4ba9b
Signed-off-by: David Hendricks <dhendrix@chromium.org>
Signed-off-by: Stefan Reinauer <reinauer@google.com>
Signed-off-by: Ronald G. Minnich <rminnich@gmail.com>
Reviewed-on: http://review.coreboot.org/2005
Tested-by: build bot (Jenkins)
This commit is contained in:
Stefan Reinauer 2012-12-07 17:18:43 -08:00 committed by Ronald G. Minnich
parent 747127d505
commit 9fe20cb381
33 changed files with 5688 additions and 1 deletions

View File

@ -1,3 +1,12 @@
# Warning: This file is included whether or not the if is here.
# The if controls how the evaluation occurs.
# (See also src/Kconfig)
if ARCH_ARMV7
source src/cpu/samsung/Kconfig
endif # ARCH_ARM
if ARCH_X86 if ARCH_X86
source src/cpu/amd/Kconfig source src/cpu/amd/Kconfig

View File

@ -3,6 +3,7 @@
################################################################################ ################################################################################
subdirs-y += amd subdirs-y += amd
subdirs-y += intel subdirs-y += intel
subdirs-y += samsung
subdirs-y += via subdirs-y += via
################################################################################ ################################################################################

46
src/cpu/samsung/Kconfig Normal file
View File

@ -0,0 +1,46 @@
config CPU_SAMSUNG_EXYNOS
bool
default n
config CPU_SAMSUNG_EXYNOS5
depends on ARCH_ARMV7
select CPU_SAMSUNG_EXYNOS
bool
default n
config SKIP_LOWLEVEL_INIT
bool "Skip low-level init"
default n
help
Certain functions (ie PLL init) and processor features may already be
handled by masked ROM code.
config IRAM_BOTTOM
hex
default 0x02020000
# FIXME(dhendrix): 0x02050000 was in the u-boot sources, but the docs say the
# iRAM range is 0x0202_0000 - 0x0207_7fff (352KB).
#config IRAM_TOP
# hex
# default 0x02050000
config IRAM_TOP
hex
default 0x02077fff
config SYS_INIT_SP_ADDR
hex
default 0x0204F800
config IRAM_STACK
hex
default SYS_INIT_SP_ADDR
# FIXME(dhendrix): what should this really be?
config XIP_ROM_SIZE
hex
default 0x20000
if CPU_SAMSUNG_EXYNOS5
source src/cpu/samsung/exynos5250/Kconfig
endif

View File

@ -0,0 +1,5 @@
subdirs-$(CONFIG_CPU_SAMSUNG_EXYNOS5) += exynos5-common
subdirs-$(CONFIG_CPU_SAMSUNG_EXYNOS5) += exynos5250
# S5P is a predecessor to Exynos
subdirs-$(CONFIG_CPU_SAMSUNG_EXYNOS) += s5p-common

View File

@ -0,0 +1,2 @@
#romstage-y += soc.c
romstage-y += spl_boot.c

View File

@ -0,0 +1,30 @@
/*
* Copyright (c) 2010 Samsung Electronics.
* Minkyu Kang <mk7.kang@samsung.com>
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <common.h>
#include <arch/io.h>
void reset_cpu(unsigned long addr)
{
writel(0x1, samsung_get_base_swreset());
}

View File

@ -0,0 +1,460 @@
/*
* Copyright (C) 2011 Samsung Electronics
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
/* FIXME(dhendrix): pulled in a lot of extra crap such as partition and string
libs*/
#include <assert.h>
#include <common.h>
#include <stdlib.h>
#include <string.h>
#include <console/console.h>
#include <console/loglevel.h>
//#include <asm/arch/board.h>
#include <config.h>
#include <spi.h>
#if 0
#include <asm/arch/clock.h>
#include <asm/arch-exynos/spi.h>
#include <asm/arch/pinmux.h>
#include <asm/arch/power.h>
#endif
#include <arch/hlt.h>
#include <cpu/samsung/exynos5250/clk.h>
#include <cpu/samsung/exynos5250/cpu.h>
#include <cpu/samsung/exynos5250/periph.h>
#include <cpu/samsung/exynos5250/pinmux.h>
#include <cpu/samsung/exynos5250/power.h>
#include <cpu/samsung/exynos5250/spi.h>
#include <cpu/samsung/exynos5-common/cpu.h>
#include <cpu/samsung/exynos5-common/exynos5-common.h>
//#include <asm/system.h>
#include <system.h>
#include <arch/io.h>
#include <arch/types.h>
/* FIXME(dhendrix): clean out u-boot global data stuff */
//DECLARE_GLOBAL_DATA_PTR;
#define OM_STAT (0x1f << 1)
/**
* Copy data from SD or MMC device to RAM.
*
* @param offset Block offset of the data
* @param nblock Number of blocks
* @param dst Destination address
* @return 1 = True or 0 = False
*/
typedef u32 (*mmc_copy_func_t)(u32 offset, u32 nblock, u32 dst);
/**
* Copy data from SPI flash to RAM.
*
* @param offset Block offset of the data
* @param nblock Number of blocks
* @param dst Destination address
* @return 1 = True or 0 = False
*/
typedef u32 (*spi_copy_func_t)(u32 offset, u32 nblock, u32 dst);
/**
* Copy data through USB.
*
* @return 1 = True or 0 = False
*/
typedef u32 (*usb_copy_func_t)(void);
/*
* Set/clear program flow prediction and return the previous state.
*/
static int config_branch_prediction(int set_cr_z)
{
unsigned int cr;
/* System Control Register: 11th bit Z Branch prediction enable */
cr = get_cr();
set_cr(set_cr_z ? cr | CR_Z : cr & ~CR_Z);
return cr & CR_Z;
}
#if 0
static void spi_rx_tx(struct exynos_spi *regs, int todo,
void *dinp, void const *doutp, int i)
{
uint *rxp = (uint *)(dinp + (i * (32 * 1024)));
int rx_lvl, tx_lvl;
uint out_bytes, in_bytes;
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) {
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;
}
}
}
#endif
/* FIXME(dhendrix): feels like exynos_spi_copy should go somewhere else... */
#if 0
/**
* Copy uboot from spi flash to RAM
*
* @parma uboot_size size of u-boot to copy
*/
static void exynos_spi_copy(unsigned int uboot_size)
{
int upto, todo;
int i;
// struct exynos_spi *regs = (struct exynos_spi *)samsung_get_base_spi1();
struct exynos_spi *regs = (struct exynos_spi *)0x12d30000;
clock_set_rate(PERIPH_ID_SPI1, 50000000); /* set spi clock to 50Mhz */
/* set the spi1 GPIO */
exynos_pinmux_config(PERIPH_ID_SPI1, PINMUX_FLAG_NONE);
/* 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 */
/* Send read instruction (0x3h) followed by a 24 bit addr */
writel((SF_READ_DATA_CMD << 24) | SPI_FLASH_UBOOT_POS, &regs->tx_data);
/* waiting for TX done */
while (!(readl(&regs->spi_sts) & SPI_ST_TX_DONE));
for (upto = 0, i = 0; upto < uboot_size; upto += todo, i++) {
todo = MIN(uboot_size - upto, (1 << 15));
spi_rx_tx(regs, todo, (void *)(CONFIG_SYS_TEXT_BASE),
(void *)(SPI_FLASH_UBOOT_POS), 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);
/*
* 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);
}
#endif
/* Copy U-Boot image to RAM */
static void copy_uboot_to_ram(void)
{
unsigned int sec_boot_check;
unsigned int uboot_size = CONFIG_COREBOOT_ROMSIZE_KB_4096;
int is_cr_z_set;
enum boot_mode boot_mode = BOOT_MODE_OM;
mmc_copy_func_t mmc_copy;
usb_copy_func_t usb_copy;
#if 0
uboot_size = exynos_get_uboot_size();
boot_mode = exynos_get_boot_device();
#endif
if (boot_mode == BOOT_MODE_OM) {
/* Read iRAM location to check for secondary USB boot mode */
sec_boot_check = readl(EXYNOS_IRAM_SECONDARY_BASE);
if (sec_boot_check == EXYNOS_USB_SECONDARY_BOOT)
boot_mode = BOOT_MODE_USB;
}
debug("U-Boot size %u\n", uboot_size);
if (boot_mode == BOOT_MODE_OM)
boot_mode = readl(EXYNOS_POWER_BASE) & OM_STAT;
switch (boot_mode) {
#if defined(CONFIG_EXYNOS_SPI_BOOT)
case BOOT_MODE_SERIAL:
/* let us our own function to copy u-boot from SF */
exynos_spi_copy(uboot_size);
break;
#endif
case BOOT_MODE_MMC:
mmc_copy = *(mmc_copy_func_t *)EXYNOS_COPY_MMC_FNPTR_ADDR;
assert(!(uboot_size & 511));
mmc_copy(BL2_START_OFFSET, uboot_size / 512,
CONFIG_SYS_TEXT_BASE);
break;
case BOOT_MODE_USB:
/*
* iROM needs program flow prediction to be disabled
* before copy from USB device to RAM
*/
is_cr_z_set = config_branch_prediction(0);
usb_copy = *(usb_copy_func_t *)
EXYNOS_COPY_USB_FNPTR_ADDR;
usb_copy();
config_branch_prediction(is_cr_z_set);
break;
default:
printk(BIOS_ERR, "Invalid boot mode selection\n");
hlt();
break;
}
debug("U-Boot copied\n");
}
#if 0
/**
* Set up the U-Boot global_data pointer
*
* This sets the address of the global data, and sets up basic values.
*
* @param gdp Value to give to gd
*/
static void setup_global_data(gd_t *gdp)
{
gd = gdp;
memzero((void *)gd, sizeof(gd_t));
gd->flags |= GD_FLG_RELOC;
gd->baudrate = CONFIG_BAUDRATE;
gd->have_console = 1;
}
#endif
#if 0
/* Tell the loaded U-Boot that it was loaded from SPL */
static void exynos5_set_spl_marker(void)
{
uint32_t *marker = (uint32_t *)CONFIG_SPL_MARKER;
*marker = EXYNOS5_SPL_MARKER;
}
#endif
/* Board-specific call to see if wakeup is allowed. */
static int __def_board_wakeup_permitted(void)
{
return 1;
}
int board_wakeup_permitted(void)
__attribute__((weak, alias("__def_board_wakeup_permitted")));
void board_init_f(unsigned long bootflag)
{
/*
* The gd struct is only needed for serial initialization. Since this
* function is called in SPL u-boot. We store the gd struct in the
* stack instead of the default memory region which may not be
* initialized.
*/
// __attribute__((aligned(8))) gd_t local_gd;
// __attribute__((noreturn)) void (*uboot)(void);
// exynos5_set_spl_marker();
// setup_global_data(&local_gd);
/*
* Init subsystems, and resume if required. For a normal boot this
* will set up the UART and display a message.
*/
if (lowlevel_init_subsystems()) {
if (!board_wakeup_permitted())
power_reset();
power_exit_wakeup();
}
// printk(BIOS_INFO, "\n\nU-Boot SPL, board rev %u\n", board_get_revision());
copy_uboot_to_ram();
/* Jump to U-Boot image */
// uboot = (void *)CONFIG_SYS_TEXT_BASE;
// uboot();
/* Never returns Here */
// printk(BIOS_ERR, "%s: u-boot jump failed", __func__);
printk(BIOS_INFO, "%s: we should not be here...", __func__);
hlt();
}
/* Place Holders */
void board_init_r(gd_t *id, ulong dest_addr)
{
/* Function attribute is no-return */
/* This Function never executes */
while (1)
;
}
//void save_boot_params(u32 r0, u32 r1, u32 r2, u32 r3) {}
#if 0
/*
* The following functions are required when linking console library to SPL.
*
* Enabling UART in SPL u-boot requires console library. But some
* functions we needed in the console library depends on a bunch
* of library in libgeneric, like lib/ctype.o, lib/div64.o, lib/string.o,
* and lib/vsprintf.o. Adding them makes the SPL u-boot too large and not
* fit into the expected size.
*
* So we mock these functions in SPL, i.e. vsprintf(), panic(), etc.,
* in order to cut its dependency.
*/
int vsprintf(char *buf, const char *fmt, va_list args)
{
char *str = buf, *s;
ulong u;
/*
* We won't implement all full functions of vsprintf().
* We only implement %s and %u, and ignore others and directly use
* the original format string as its result.
*/
while (*fmt) {
if (*fmt != '%') {
*str++ = *fmt++;
continue;
}
fmt++;
switch (*fmt) {
case '%':
*str++ = *fmt++;
break;
case 's':
fmt++;
s = va_arg(args, char *);
while (*s)
*str++ = *s++;
break;
case 'u':
fmt++;
u = va_arg(args, ulong);
s = simple_itoa(u);
while (*s)
*str++ = *s++;
break;
default:
/* Print the original string for unsupported formats */
*str++ = '%';
*str++ = *fmt++;
}
}
*str = '\0';
return str - buf;
}
#endif
#if 0
void panic(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
vprintf(fmt, args);
putc('\n');
va_end(args);
#if defined(CONFIG_PANIC_HANG)
hang();
#else
udelay(100000); /* allow messages to go out */
do_reset(NULL, 0, 0, NULL);
#endif
while (1)
;
}
#endif
#if 0
void __assert_fail(const char *assertion, const char *file, unsigned line,
const char *function)
{
/* This will not return */
panic("%s:%u: %s: Assertion `%s' failed.", file, line, function,
assertion);
}
#endif
#if 0
char *simple_itoa(ulong i)
{
/* 21 digits plus null terminator, good for 64-bit or smaller ints */
static char local[22] __attribute__((section(".data")));
char *p = &local[21];
*p-- = '\0';
do {
*p-- = '0' + i % 10;
i /= 10;
} while (i > 0);
return p + 1;
}
#endif

View File

@ -0,0 +1,28 @@
config EXYNOS_ACE_SHA
bool
default n
config SATA_AHCI
bool
default n
config SPL_BUILD
bool
default n
config SYS_TEXT_BASE
hex "Executable code section"
default 0x43e00000
config SYS_SDRAM_BASE
hex "SDRAM base address"
default 0x40000000
#FIXME(dhendrix, reinauer): re-visit this RAMBASE/RAMTOP stuff...
config RAMBASE
hex
default SYS_SDRAM_BASE
# according to stefan, this is RAMBASE + 1M.
config RAMTOP
hex
default 0x40100000

View File

@ -0,0 +1,32 @@
romstage-y += clock.c
romstage-y += clock_init.c
romstage-y += exynos_cache.c
romstage-y += lowlevel_init.S
romstage-y += lowlevel_init_c.c
romstage-y += pinmux.c
romstage-y += power.c
romstage-y += soc.c
romstage-y += uart.c
#ramstage-y += clock.c
#ramstage-y += clock_init.c
#ramstage-y += power.c
#ramstage-y += uart.c
##ramstage-y += spl.c
#ramstage-y += pinmux.c
##ramstage-y += tzpc_init.c
ramstage-y += clock.c
ramstage-y += clock_init.c
ramstage-y += exynos_cache.c
ramstage-y += lowlevel_init.S
ramstage-y += lowlevel_init_c.c
ramstage-y += pinmux.c
ramstage-y += power.c
ramstage-y += soc.c
ramstage-y += uart.c
#ramstage-$(CONFIG_EXYNOS_ACE_SHA) += ace_sha.c
#ramstage-$(CONFIG_SATA_AHCI) += sata.c
ramstage-$(CONFIG_SPL_BUILD) += lowlevel_init_c.c
ramstage-$(CONFIG_SPL_BUILD) += dmc_common.c
ramstage-$(CONFIG_SPL_BUILD) += dmc_init_ddr3.c

View File

@ -0,0 +1,118 @@
/*
* Advanced Crypto Engine - SHA Firmware
*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <common.h>
#include <asm/arch/ace_sha.h>
#include <asm/arch/ace_sfr.h>
/* Maximum input data size is 8 MB. Timeout observed for data size above 8MB */
#define TIMEOUT_MS 100
#define SHA1_DIGEST_LEN 20
#define SHA256_DIGEST_LEN 32
/* SHA1 value for the message of zero length */
static const unsigned char sha1_digest_emptymsg[SHA1_DIGEST_LEN] = {
0xDA, 0x39, 0xA3, 0xEE, 0x5E, 0x6B, 0x4B, 0x0D,
0x32, 0x55, 0xBF, 0xFF, 0x95, 0x60, 0x18, 0x90,
0xAF, 0xD8, 0x07, 0x09};
/* SHA256 value for the message of zero length */
static const unsigned char sha256_digest_emptymsg[SHA256_DIGEST_LEN] = {
0xE3, 0xB0, 0xC4, 0x42, 0x98, 0xFC, 0x1C, 0x14,
0x9A, 0xFB, 0xF4, 0xC8, 0x99, 0x6F, 0xB9, 0x24,
0x27, 0xAE, 0x41, 0xE4, 0x64, 0x9B, 0x93, 0x4C,
0xA4, 0x95, 0x99, 0x1B, 0x78, 0x52, 0xB8, 0x55};
int ace_sha_hash_digest(
unsigned char *pout, unsigned char *pbuf,
unsigned int buf_len, unsigned int hash_type)
{
unsigned int i, reg, len;
unsigned int *pdigest;
ulong start;
struct exynos_ace_sfr *ace_sha_reg =
(struct exynos_ace_sfr *) samsung_get_base_ace_sfr();
if (buf_len == 0) {
/* ACE H/W cannot compute hash value for empty string */
if (hash_type == ACE_SHA_TYPE_SHA1)
memcpy(pout, sha1_digest_emptymsg, SHA1_DIGEST_LEN);
else
memcpy(pout, sha256_digest_emptymsg, SHA256_DIGEST_LEN);
return 0;
}
/* Flush HRDMA */
writel(ACE_FC_HRDMACFLUSH_ON, &ace_sha_reg->fc_hrdmac);
writel(ACE_FC_HRDMACFLUSH_OFF, &ace_sha_reg->fc_hrdmac);
/* Set byte swap of data in */
writel(ACE_HASH_SWAPDI_ON | ACE_HASH_SWAPDO_ON | ACE_HASH_SWAPIV_ON,
&ace_sha_reg->hash_byteswap);
/* Select Hash input mux as external source */
reg = readl(&ace_sha_reg->fc_fifoctrl);
reg = (reg & ~ACE_FC_SELHASH_MASK) | ACE_FC_SELHASH_EXOUT;
writel(reg, &ace_sha_reg->fc_fifoctrl);
/* Set Hash as SHA1 or SHA256 and start Hash engine */
reg = (hash_type == ACE_SHA_TYPE_SHA1) ?
ACE_HASH_ENGSEL_SHA1HASH : ACE_HASH_ENGSEL_SHA256HASH;
reg |= ACE_HASH_STARTBIT_ON;
writel(reg, &ace_sha_reg->hash_control);
/* Enable FIFO mode */
writel(ACE_HASH_FIFO_ON, &ace_sha_reg->hash_fifo_mode);
/* Set message length */
writel(buf_len, &ace_sha_reg->hash_msgsize_low);
writel(0, &ace_sha_reg->hash_msgsize_high);
/* Set HRDMA */
writel((unsigned int)pbuf, &ace_sha_reg->fc_hrdmas);
writel(buf_len, &ace_sha_reg->fc_hrdmal);
start = get_timer(0);
while ((readl(&ace_sha_reg->hash_status) & ACE_HASH_MSGDONE_MASK) ==
ACE_HASH_MSGDONE_OFF) {
if (get_timer(start) > TIMEOUT_MS) {
debug("%s: Timeout waiting for ACE\n", __func__);
return -1;
}
}
/* Clear MSG_DONE bit */
writel(ACE_HASH_MSGDONE_ON, &ace_sha_reg->hash_status);
/* Read hash result */
pdigest = (unsigned int *)pout;
len = (hash_type == ACE_SHA_TYPE_SHA1) ? 5 : 8;
for (i = 0; i < len ; i++)
pdigest[i] = readl(&ace_sha_reg->hash_result[i]);
/* Clear HRDMA pending bit */
writel(ACE_FC_HRDMA, &ace_sha_reg->fc_intpend);
return 0;
}

View File

@ -0,0 +1,610 @@
/*
* Copyright (C) 2010 Samsung Electronics
* Minkyu Kang <mk7.kang@samsung.com>
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <common.h>
#include <console/console.h>
#include <stdlib.h>
//#include <fdtdec.h>
#include <arch/io.h>
//#include <asm/arch/clock.h>
//#include <asm/arch/clk.h>
#include <cpu/samsung/exynos5-common/clk.h>
#include <cpu/samsung/exynos5250/clk.h>
#include <cpu/samsung/exynos5250/cpu.h>
#include <cpu/samsung/exynos5250/periph.h>
#include <cpu/samsung/s5p-common/clk.h>
/* input clock of PLL: SMDK5250 has 24MHz input clock */
#define CONFIG_SYS_CLK_FREQ 24000000
/* 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 =
(struct exynos5_clock *)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 =
(struct exynos5_clock *)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 =
(struct exynos5_clock *)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;
}
/* exynos5: set the mmc clock */
void set_mmc_clk(int dev_index, unsigned int div)
{
struct exynos5_clock *clk =
(struct exynos5_clock *)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 = (unsigned int)&clk->div_fsys1;
} else {
addr = (unsigned int)&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 =
(struct exynos5_clock *)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:
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 =
(struct exynos5_clock *)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:
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;
debug("Input Rate is %u, Target is %u, Cap is %u\n", input_rate,
target_rate, cap);
assert(best_fine_scalar != NULL);
assert(main_scaler_bits <= fine_scalar_bits);
*best_fine_scalar = 1;
if (input_rate == 0 || target_rate == 0)
return -1;
if (target_rate >= input_rate)
return 1;
for (i = 1; i <= loops; i++) {
const unsigned int effective_div = MAX(MIN(input_rate / i /
target_rate, cap), 1);
const unsigned int effective_rate = input_rate / i /
effective_div;
const int error = target_rate - effective_rate;
debug("%d|effdiv:%u, effrate:%u, error:%d\n", i, effective_div,
effective_rate, error);
if (error >= 0 && error <= best_error) {
best_error = error;
best_main_scalar = i;
*best_fine_scalar = effective_div;
}
}
return best_main_scalar;
}
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) {
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:
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 =
(struct exynos5_clock *)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:
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;
}
#ifdef CONFIG_OF_CONTROL
int clock_decode_periph_id(const void *blob, int node)
{
enum periph_id id;
/*
* For now the peripheral ID is directly encoded. Once we have clock
* support in the fdt and properly in exynos U-Boot we may have
* another way of changing the clock.
*/
id = fdtdec_get_int(blob, node, "samsung,periph-id", -1);
assert(id != PERIPH_ID_NONE);
assert(id >= 0 && id < PERIPH_ID_COUNT);
return id;
}
#endif
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 =
(struct exynos5_clock *)samsung_get_base_clock();
epll_con = readl(&clk->epll_con0);
epll_con &= ~((EPLL_CON0_LOCK_DET_EN_MASK <<
EPLL_CON0_LOCK_DET_EN_SHIFT) |
EPLL_CON0_MDIV_MASK << EPLL_CON0_MDIV_SHIFT |
EPLL_CON0_PDIV_MASK << EPLL_CON0_PDIV_SHIFT |
EPLL_CON0_SDIV_MASK << EPLL_CON0_SDIV_SHIFT);
for (i = 0; i < ARRAY_SIZE(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 =
(struct exynos5_clock *)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 =
(struct exynos5_clock *)samsung_get_base_clock();
unsigned int div ;
if ((dst_frq == 0) || (src_frq == 0)) {
debug("%s: Invalid requency input for prescaler\n", __func__);
debug("src frq = %d des frq = %d ", src_frq, dst_frq);
return -1;
}
div = (src_frq / dst_frq);
if (div > AUDIO_1_RATIO_MASK) {
debug("%s: Frequency ratio is out of range\n", __func__);
debug("src frq = %d des frq = %d ", src_frq, dst_frq);
return -1;
}
clrsetbits_le32(&clk->div_peric4, AUDIO_1_RATIO_MASK,
(div & AUDIO_1_RATIO_MASK));
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,200 @@
/*
* Mem setup common file for different types of DDR present on SMDK5250 boards.
*
* Copyright (C) 2012 Samsung Electronics
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <assert.h>
#include <common.h>
#include <console/console.h>
#include <cpu/samsung/exynos5250/setup.h>
#include <cpu/samsung/exynos5-common/spl.h>
#include <system.h>
#include "clock_init.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) {
sdelay(100);
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) {
sdelay(100);
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.
*/
sdelay(0x10000);
/* Sending EMRS/MRS commands */
for (i = 0; i < MEM_TIMINGS_MSR_COUNT; i++) {
writel(mem->direct_cmd_msr[i] | mask,
&dmc->directcmd);
sdelay(0x10000);
}
if (mem->send_zq_init) {
/* Sending ZQINIT command */
writel(DIRECT_CMD_ZQINIT | mask,
&dmc->directcmd);
sdelay(10000);
}
}
}
}
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);
sdelay(0x10000);
}
}
}
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);
}
void mem_ctrl_init()
{
struct spl_machine_param *param = spl_get_machine_params();
struct mem_timings *mem;
int ret;
mem = clock_get_mem_timings();
/* If there are any other memory variant, add their init call below */
if (param->mem_type == DDR_MODE_DDR3) {
ret = ddr3_mem_ctrl_init(mem, param->mem_iv_size);
if (ret) {
printk(BIOS_ERR, "Memory controller init failed, err: %u", ret);
BUG();
}
} else {
die("Unknown memory type");
}
}

View File

@ -0,0 +1,249 @@
/*
* DDR3 mem setup file for SMDK5250 board based on EXYNOS5
*
* Copyright (C) 2012 Samsung Electronics
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <config.h>
#include <arch/io.h>
//#include "clock.h"
/* FIXME(dhendrix): untangle clock/clk ... */
#include <cpu/samsung/s5p-common/clock.h>
#include <system.h>
#include "clk.h"
#include "cpu.h"
#include "dmc.h"
#include "setup.h"
#include "clock_init.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);
/*
* 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);
}
int ddr3_mem_ctrl_init(struct mem_timings *mem, unsigned long mem_iv_size)
{
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;
reset_phy_ctrl();
/* Set Impedance Output Driver */
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);
writel(val, &phy0_ctrl->phy_con39);
writel(val, &phy1_ctrl->phy_con39);
/* Set Read Latency and Burst Length for PHY0 and PHY1 */
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 */
if (dmc_config_zq(mem, phy0_ctrl, phy1_ctrl))
return SETUP_ERR_ZQ_CALIBRATION_FAILURE;
/* DQ Signal */
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 */
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 */
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 */
writel(mem->iv_size, &dmc->ivcontrol);
/* Set DMC MEMCONTROL register */
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 */
writel(mem->prechconfig_tp_cnt << PRECHCONFIG_TP_CNT_SHIFT,
&dmc->prechconfig);
/* Power Down mode Configuration */
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
*/
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 */
dmc_config_prech(mem, dmc);
/* Send NOP, MRS and ZQINIT commands */
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
*/
sdelay(100);
i--;
}
if (!i)
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 */
dmc_config_prech(mem, dmc);
writel(mem->memcontrol, &dmc->memcontrol);
/* Set DMC Concontrol and enable auto-refresh counter */
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,90 @@
/*
* Copyright (c) 2012 Samsung Electronics.
* Arun Mankuzhi <arun.m@samsung.com>
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <common.h>
#include <system.h>
#include <armv7.h>
enum l2_cache_params {
CACHE_TAG_RAM_SETUP = (1<<9),
CACHE_DATA_RAM_SETUP = (1<<5),
CACHE_TAG_RAM_LATENCY = (2<<6),
CACHE_DATA_RAM_LATENCY = (2<<0)
};
/* FIXME(dhendrix): maybe move this to a romstage-specific file? */
#ifdef __PRE_RAM__
void enable_caches(void)
{
/* Enable D-cache. I-cache is already enabled in start.S */
dcache_enable();
}
#endif
/*
* Set L2 cache parameters
*/
static void exynos5_set_l2cache_params(void)
{
unsigned int val = 0;
asm volatile("mrc p15, 1, %0, c9, c0, 2\n" : "=r"(val));
val |= CACHE_TAG_RAM_SETUP |
CACHE_DATA_RAM_SETUP |
CACHE_TAG_RAM_LATENCY |
CACHE_DATA_RAM_LATENCY;
asm volatile("mcr p15, 1, %0, c9, c0, 2\n" : : "r"(val));
}
/*
* Sets L2 cache related parameters before enabling data cache
*/
void v7_outer_cache_enable(void)
{
exynos5_set_l2cache_params();
}
/* stubs so we don't need weak symbols in cache_v7.c */
void v7_outer_cache_disable(void)
{
}
void v7_outer_cache_flush_all(void)
{
}
void v7_outer_cache_inval_all(void)
{
}
void v7_outer_cache_flush_range(u32 start, u32 end)
{
}
void v7_outer_cache_inval_range(u32 start, u32 end)
{
}

View File

@ -0,0 +1,32 @@
/*
* Lowlevel setup for SMDK5250 board based on S5PC520
*
* Copyright (C) 2012 Samsung Electronics
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
.globl lowlevel_init
lowlevel_init:
/*
* Set the stack pointer, although it will be overwriten by the caller
* It seems we will not boot if this function is empty.
*/
ldr sp, =CONFIG_IRAM_STACK
mov pc, lr

View File

@ -0,0 +1,120 @@
/*
* Lowlevel setup for SMDK5250 board based on S5PC520
*
* Copyright (C) 2012 Samsung Electronics
* Copyright (c) 2012 The Chromium OS Authors.
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <common.h>
#include <config.h>
#include <cpu/samsung/exynos5-common/exynos5-common.h>
#include <cpu/samsung/exynos5-common/spl.h>
#include <cpu/samsung/exynos5250/clock_init.h>
#include <cpu/samsung/exynos5250/cpu.h>
#include <cpu/samsung/exynos5250/dmc.h>
#include <cpu/samsung/exynos5250/pinmux.h>
#include <cpu/samsung/exynos5250/power.h>
#include <cpu/samsung/exynos5250/setup.h>
#include <cpu/samsung/exynos5250/tzpc.h>
#include "setup.h"
void do_barriers(void); /* FIXME: make gcc shut up about "no previous prototype" */
void do_barriers(void)
{
/*
* The reason we don't write out the instructions dsb/isb/sev:
* While ARM Cortex-A8 supports ARM v7 instruction set (-march=armv7a),
* we compile with -march=armv5 to allow more compilers to work.
* For U-Boot code this has no performance impact.
*/
__asm__ __volatile__(
#if defined(__thumb__)
".hword 0xF3BF, 0x8F4F\n" /* dsb; darn -march=armv5 */
".hword 0xF3BF, 0x8F6F\n" /* isb; darn -march=armv5 */
".hword 0xBF40\n" /* sev; darn -march=armv5 */
#else
".word 0xF57FF04F\n" /* dsb; darn -march=armv5 */
".word 0xF57FF06F\n" /* isb; darn -march=armv5 */
".word 0xE320F004\n" /* sev; darn -march=armv5 */
#endif
);
}
/* These are the things we can do during low-level init */
enum {
DO_WAKEUP = 1 << 0,
DO_UART = 1 << 1,
DO_CLOCKS = 1 << 2,
DO_POWER = 1 << 3,
};
int lowlevel_init_subsystems(void)
{
uint32_t reset_status;
int actions = 0;
do_barriers();
/* Setup cpu info which is needed to select correct register offsets */
cpu_info_init();
reset_status = power_read_reset_status();
switch (reset_status) {
case S5P_CHECK_SLEEP:
actions = DO_CLOCKS | DO_WAKEUP;
break;
case S5P_CHECK_DIDLE:
case S5P_CHECK_LPA:
actions = DO_WAKEUP;
default:
/* This is a normal boot (not a wake from sleep) */
actions = DO_UART | DO_CLOCKS | DO_POWER;
}
if (actions & DO_POWER)
power_init();
if (actions & DO_CLOCKS)
system_clock_init();
if (actions & DO_UART) {
/* Set up serial UART so we can printf() */
// exynos_pinmux_config(EXYNOS_UART, PINMUX_FLAG_NONE);
/* FIXME(dhendrix): add a function for mapping
CONFIG_CONSOLE_SERIAL_UART_ADDRESS to PERIPH_ID_UARTn */
exynos_pinmux_config(PERIPH_ID_UART3, PINMUX_FLAG_NONE);
/* FIXME(dhendrix): serial_init() does not seem to
actually do anything !?!? */
// serial_init();
init_timer(); /* FIXME(dhendrix): was timer_init() */
}
/* FIXME(dhendrix): place this somewhere for ramstage... */
#if 0
if (actions & DO_CLOCKS) {
mem_ctrl_init();
tzpc_init();
}
#endif
return actions & DO_WAKEUP;
}

View File

@ -0,0 +1,303 @@
/*
* Copyright (c) 2012 Samsung Electronics.
* Abhilash Kesavan <a.kesavan@samsung.com>
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <common.h>
#include <arch/gpio.h>
#include <cpu/samsung/exynos5250/gpio.h>
#include <cpu/samsung/exynos5250/cpu.h>
#include <cpu/samsung/exynos5250/pinmux.h>
#include <cpu/samsung/exynos5-common/sromc.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, EXYNOS_GPIO_PULL_NONE);
gpio_cfg_pin(i, EXYNOS_GPIO_FUNC(0x2));
}
break;
case PERIPH_ID_SDMMC0:
case PERIPH_ID_SDMMC1:
case PERIPH_ID_SDMMC2:
case PERIPH_ID_SDMMC3:
pin = EXYNOS_GPIO_FUNC(0x2);
pin_ext = EXYNOS_GPIO_FUNC(0x2);
drv = EXYNOS_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.
*/
debug("SDMMC3 not supported yet");
return -1;
}
if ((flags & PINMUX_FLAG_8BIT_MODE) && !start_ext) {
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,
EXYNOS_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, EXYNOS_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, EXYNOS_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),
EXYNOS_GPIO_FUNC(2));
gpio_cfg_pin(GPIO_Y04, EXYNOS_GPIO_FUNC(2));
gpio_cfg_pin(GPIO_Y05, EXYNOS_GPIO_FUNC(2));
for (i = 2; i < 4; i++)
gpio_cfg_pin(GPIO_Y10 + i, EXYNOS_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, EXYNOS_GPIO_FUNC(2));
gpio_set_pull(GPIO_Y30 + i, EXYNOS_GPIO_PULL_UP);
gpio_cfg_pin(GPIO_Y50 + i, EXYNOS_GPIO_FUNC(2));
gpio_set_pull(GPIO_Y50 + i, EXYNOS_GPIO_PULL_UP);
if (flags & PINMUX_FLAG_16BIT) {
gpio_cfg_pin(GPIO_Y60 + i, EXYNOS_GPIO_FUNC(2));
gpio_set_pull(GPIO_Y60 + i,
EXYNOS_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, EXYNOS_GPIO_FUNC(cfg));
break;
}
case PERIPH_ID_SPI4:
for (i = 0; i < 2; i++)
gpio_cfg_pin(GPIO_F02 + i, EXYNOS_GPIO_FUNC(0x4));
for (i = 2; i < 4; i++)
gpio_cfg_pin(GPIO_E02 + i, EXYNOS_GPIO_FUNC(0x4));
break;
case PERIPH_ID_BACKLIGHT:
gpio_cfg_pin(GPIO_B20, EXYNOS_GPIO_OUTPUT);
gpio_set_value(GPIO_B20, 1);
break;
case PERIPH_ID_LCD:
gpio_cfg_pin(GPIO_Y25, EXYNOS_GPIO_OUTPUT);
gpio_set_value(GPIO_Y25, 1);
gpio_cfg_pin(GPIO_X15, EXYNOS_GPIO_OUTPUT);
gpio_set_value(GPIO_X15, 1);
gpio_cfg_pin(GPIO_X30, EXYNOS_GPIO_OUTPUT);
gpio_set_value(GPIO_X30, 1);
break;
case PERIPH_ID_I2C0:
gpio_cfg_pin(GPIO_B30, EXYNOS_GPIO_FUNC(0x2));
gpio_cfg_pin(GPIO_B31, EXYNOS_GPIO_FUNC(0x2));
gpio_set_pull(GPIO_B30, EXYNOS_GPIO_PULL_NONE);
gpio_set_pull(GPIO_B31, EXYNOS_GPIO_PULL_NONE);
break;
case PERIPH_ID_I2C1:
gpio_cfg_pin(GPIO_B32, EXYNOS_GPIO_FUNC(0x2));
gpio_cfg_pin(GPIO_B33, EXYNOS_GPIO_FUNC(0x2));
gpio_set_pull(GPIO_B32, EXYNOS_GPIO_PULL_NONE);
gpio_set_pull(GPIO_B33, EXYNOS_GPIO_PULL_NONE);
break;
case PERIPH_ID_I2C2:
gpio_cfg_pin(GPIO_A06, EXYNOS_GPIO_FUNC(0x3));
gpio_cfg_pin(GPIO_A07, EXYNOS_GPIO_FUNC(0x3));
gpio_set_pull(GPIO_A06, EXYNOS_GPIO_PULL_NONE);
gpio_set_pull(GPIO_A07, EXYNOS_GPIO_PULL_NONE);
break;
case PERIPH_ID_I2C3:
gpio_cfg_pin(GPIO_A12, EXYNOS_GPIO_FUNC(0x3));
gpio_cfg_pin(GPIO_A13, EXYNOS_GPIO_FUNC(0x3));
gpio_set_pull(GPIO_A12, EXYNOS_GPIO_PULL_NONE);
gpio_set_pull(GPIO_A13, EXYNOS_GPIO_PULL_NONE);
break;
case PERIPH_ID_I2C4:
gpio_cfg_pin(GPIO_A20, EXYNOS_GPIO_FUNC(0x3));
gpio_cfg_pin(GPIO_A21, EXYNOS_GPIO_FUNC(0x3));
gpio_set_pull(GPIO_A20, EXYNOS_GPIO_PULL_NONE);
gpio_set_pull(GPIO_A21, EXYNOS_GPIO_PULL_NONE);
break;
case PERIPH_ID_I2C5:
gpio_cfg_pin(GPIO_A22, EXYNOS_GPIO_FUNC(0x3));
gpio_cfg_pin(GPIO_A23, EXYNOS_GPIO_FUNC(0x3));
gpio_set_pull(GPIO_A22, EXYNOS_GPIO_PULL_NONE);
gpio_set_pull(GPIO_A23, EXYNOS_GPIO_PULL_NONE);
break;
case PERIPH_ID_I2C6:
gpio_cfg_pin(GPIO_B13, EXYNOS_GPIO_FUNC(0x4));
gpio_cfg_pin(GPIO_B14, EXYNOS_GPIO_FUNC(0x4));
break;
case PERIPH_ID_I2C7:
gpio_cfg_pin(GPIO_B22, EXYNOS_GPIO_FUNC(0x3));
gpio_cfg_pin(GPIO_B23, EXYNOS_GPIO_FUNC(0x3));
gpio_set_pull(GPIO_B22, EXYNOS_GPIO_PULL_NONE);
gpio_set_pull(GPIO_B23, EXYNOS_GPIO_PULL_NONE);
break;
case PERIPH_ID_DPHPD:
/* Set Hotplug detect for DP */
gpio_cfg_pin(GPIO_X07, EXYNOS_GPIO_FUNC(0x3));
/*
* Hotplug detect should have an external pullup; disable the
* internal pulldown so they don't fight.
*/
gpio_set_pull(GPIO_X07, EXYNOS_GPIO_PULL_NONE);
break;
case PERIPH_ID_I2S1:
for (i = 0; i < 5; i++)
gpio_cfg_pin(GPIO_B00 + i, EXYNOS_GPIO_FUNC(0x02));
break;
default:
debug("%s: invalid peripheral %d", __func__, peripheral);
return -1;
}
return 0;
}

View File

@ -0,0 +1,202 @@
/*
* Power setup code for EXYNOS5
*
* Copyright (C) 2012 Samsung Electronics
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <common.h>
#include <arch/io.h>
#include <console/console.h>
#include <cpu/samsung/exynos5250/cpu.h>
#include <cpu/samsung/exynos5250/power.h>
#include <cpu/samsung/exynos5250/sysreg.h>
#include <cpu/samsung/exynos5-common/spl.h>
#include <device/i2c.h>
#include <device/power/max77686.h>
static void ps_hold_setup(void)
{
struct exynos5_power *power =
(struct exynos5_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 =
(struct exynos5_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 =
(struct exynos5_power *)samsung_get_base_power();
clrbits_le32(&power->ps_hold_ctrl, POWER_PS_HOLD_CONTROL_DATA_HIGH);
hang();
}
void power_enable_dp_phy(void)
{
struct exynos5_power *power =
(struct exynos5_power *)samsung_get_base_power();
setbits_le32(&power->dptx_phy_control, DPTX_PHY_ENABLE);
}
void power_enable_usb_phy(void)
{
struct exynos5_sysreg *sysreg =
(struct exynos5_sysreg *)samsung_get_base_sysreg();
struct exynos5_power *power =
(struct exynos5_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) {
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 =
(struct exynos5_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 =
(struct exynos5_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 =
(struct exynos5_power *)samsung_get_base_power();
return power->inform1;
}
void power_exit_wakeup(void)
{
struct exynos5_power *power =
(struct exynos5_power *)samsung_get_base_power();
typedef void (*resume_func)(void);
((resume_func)power->inform0)();
}
/**
* Initialize the pmic voltages to power up the system
* This also calls i2c_init so that we can program the pmic
*
* REG_ENABLE = 0, needed to set the buck/ldo enable bit ON
*
* @return Return 0 if ok, else -1
*/
int power_init(void)
{
int error = 0;
/* FIXME(dhendrix): not necessary for initial bringup... */
#if 0
#ifdef CONFIG_SPL_BUILD
struct spl_machine_param *param = spl_get_machine_params();
/* Set the i2c register address base so i2c works before FDT */
i2c_set_early_reg(param->i2c_base);
#endif
#endif
ps_hold_setup();
/* FIXME(dhendrix): not necessary for initial bringup... */
#if 0
/* init the i2c so that we can program pmic chip */
i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);
/*
* We're using CR1616 coin cell battery that is non-rechargeable
* battery. But, BBCHOSTEN bit of the BBAT Charger Register in
* MAX77686 is enabled by default for charging coin cell.
*
* Also, we cannot meet the coin cell reverse current spec. in UL
* standard if BBCHOSTEN bit is enabled.
*
* Disable Coin BATT Charging
*/
error = max77686_disable_backup_batt();
error |= max77686_volsetting(PMIC_BUCK2, CONFIG_VDD_ARM_MV,
REG_ENABLE, MAX77686_MV);
error |= max77686_volsetting(PMIC_BUCK3, CONFIG_VDD_INT_UV,
REG_ENABLE, MAX77686_UV);
error |= max77686_volsetting(PMIC_BUCK1, CONFIG_VDD_MIF_MV,
REG_ENABLE, MAX77686_MV);
error |= max77686_volsetting(PMIC_BUCK4, CONFIG_VDD_G3D_MV,
REG_ENABLE, MAX77686_MV);
error |= max77686_volsetting(PMIC_LDO2, CONFIG_VDD_LDO2_MV,
REG_ENABLE, MAX77686_MV);
error |= max77686_volsetting(PMIC_LDO3, CONFIG_VDD_LDO3_MV,
REG_ENABLE, MAX77686_MV);
error |= max77686_volsetting(PMIC_LDO5, CONFIG_VDD_LDO5_MV,
REG_ENABLE, MAX77686_MV);
error |= max77686_volsetting(PMIC_LDO10, CONFIG_VDD_LDO10_MV,
REG_ENABLE, MAX77686_MV);
#endif
if (error != 0)
printk(BIOS_ERR, "power init failed\n");
return error;
}
void power_enable_xclkout(void)
{
struct exynos5_power *power =
(struct exynos5_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,431 @@
/*
* Copyright (c) 2012 The Chromium OS Authors.
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <ahci.h>
#include <common.h>
#include <fdtdec.h>
#include <scsi.h>
#include <asm/arch-exynos5/sata.h>
#include <asm/arch/pinmux.h>
#include <asm/errno.h>
#include <asm/gpio.h>
#include <asm/types.h>
#define SATA_AHCI_AXI 0x122f0000
#define SATA_PHCTRL_APB 0x12170000
#define SATA_PHY_I2C_ABP 0x121d0000
#define EXYNOS5_SATA_PHY_CONTROL (0x10040000 + 0x724)
#define S5P_PMU_SATA_PHY_CONTROL_EN 0x1
void * const phy_ctrl = (void *)SATA_PHCTRL_APB;
void * const phy_i2c_base = (void *)SATA_PHY_I2C_ABP;
typedef unsigned char bool;
#define true 1
#define false 0
#define SATA_TIME_LIMIT 10000
#define SATA_PHY_I2C_SLAVE_ADDRS 0x70
#define SATA_RESET 0x4
#define RESET_CMN_RST_N (1 << 1)
#define LINK_RESET 0xF0000
#define SATA_MODE0 0x10
#define SATA_CTRL0 0x14
#define CTRL0_P0_PHY_CALIBRATED_SEL (1 << 9)
#define CTRL0_P0_PHY_CALIBRATED (1 << 8)
#define SATA_PHSATA_CTRLM 0xE0
#define PHCTRLM_REF_RATE (1 << 1)
#define PHCTRLM_HIGH_SPEED (1 << 0)
#define SATA_PHSATA_STATM 0xF0
#define PHSTATM_PLL_LOCKED (1 << 0)
/********************** I2C**************/
#define SATA_I2C_CON 0x00
#define SATA_I2C_STAT 0x04
#define SATA_I2C_ADDR 0x08
#define SATA_I2C_DS 0x0C
#define SATA_I2C_LC 0x10
/* I2CCON reg */
#define CON_ACKEN (1 << 7)
#define CON_CLK512 (1 << 6)
#define CON_CLK16 (~CON_CLK512)
#define CON_INTEN (1 << 5)
#define CON_INTPND (1 << 4)
#define CON_TXCLK_PS (0xF)
/* I2CSTAT reg */
#define STAT_MSTT (0x3 << 6)
#define STAT_BSYST (1 << 5)
#define STAT_RTEN (1 << 4)
#define STAT_LAST (1 << 0)
#define LC_FLTR_EN (1 << 2)
#define SATA_PHY_CON_RESET 0xF003F
#define SCLK_SATA_FREQ (66 * MHZ)
enum {
SATA_GENERATION1,
SATA_GENERATION2,
SATA_GENERATION3,
};
static bool sata_is_reg(void __iomem *base, u32 reg, u32 checkbit, u32 Status)
{
if ((__raw_readl(base + reg) & checkbit) == Status)
return true;
else
return false;
}
static bool wait_for_reg_status(void __iomem *base, u32 reg, u32 checkbit,
u32 Status)
{
u32 time_limit_cnt = 0;
while (!sata_is_reg(base, reg, checkbit, Status)) {
if (time_limit_cnt == SATA_TIME_LIMIT) {
return false;
}
udelay(1000);
time_limit_cnt++;
}
return true;
}
static void sata_set_gen(u8 gen)
{
__raw_writel(gen, phy_ctrl + SATA_MODE0);
}
/* Address :I2C Address */
static void sata_i2c_write_addrs(u8 data)
{
__raw_writeb((data & 0xFE), phy_i2c_base + SATA_I2C_DS);
}
static void sata_i2c_write_data(u8 data)
{
__raw_writeb((data), phy_i2c_base + SATA_I2C_DS);
}
static void sata_i2c_start(void)
{
u32 val;
val = __raw_readl(phy_i2c_base + SATA_I2C_STAT);
val |= STAT_BSYST;
__raw_writel(val, phy_i2c_base + SATA_I2C_STAT);
}
static void sata_i2c_stop(void)
{
u32 val;
val = __raw_readl(phy_i2c_base + SATA_I2C_STAT);
val &= ~STAT_BSYST;
__raw_writel(val, phy_i2c_base + SATA_I2C_STAT);
}
static bool sata_i2c_get_int_status(void)
{
if ((__raw_readl(phy_i2c_base + SATA_I2C_CON)) & CON_INTPND)
return true;
else
return false;
}
static bool sata_i2c_is_tx_ack(void)
{
if ((__raw_readl(phy_i2c_base + SATA_I2C_STAT)) & STAT_LAST)
return false;
else
return true;
}
static bool sata_i2c_is_bus_ready(void)
{
if ((__raw_readl(phy_i2c_base + SATA_I2C_STAT)) & STAT_BSYST)
return false;
else
return true;
}
static bool sata_i2c_wait_for_busready(u32 time_out)
{
while (--time_out) {
if (sata_i2c_is_bus_ready())
return true;
udelay(100);
}
return false;
}
static bool sata_i2c_wait_for_tx_ack(u32 time_out)
{
while (--time_out) {
if (sata_i2c_get_int_status()) {
if (sata_i2c_is_tx_ack())
return true;
}
udelay(100);
}
return false;
}
static void sata_i2c_clear_int_status(void)
{
u32 val;
val = __raw_readl(phy_i2c_base + SATA_I2C_CON);
val &= ~CON_INTPND;
__raw_writel(val, phy_i2c_base + SATA_I2C_CON);
}
static void sata_i2c_set_ack_gen(bool enable)
{
u32 val;
if (enable) {
val = (__raw_readl(phy_i2c_base + SATA_I2C_CON)) | CON_ACKEN;
__raw_writel(val, phy_i2c_base + SATA_I2C_CON);
} else {
val = __raw_readl(phy_i2c_base + SATA_I2C_CON);
val &= ~CON_ACKEN;
__raw_writel(val, phy_i2c_base + SATA_I2C_CON);
}
}
static void sata_i2c_set_master_tx(void)
{
u32 val;
/* Disable I2C */
val = __raw_readl(phy_i2c_base + SATA_I2C_STAT);
val &= ~STAT_RTEN;
__raw_writel(val, phy_i2c_base + SATA_I2C_STAT);
/* Clear Mode */
val = __raw_readl(phy_i2c_base + SATA_I2C_STAT);
val &= ~STAT_MSTT;
__raw_writel(val, phy_i2c_base + SATA_I2C_STAT);
sata_i2c_clear_int_status();
/* interrupt disable */
val = __raw_readl(phy_i2c_base + SATA_I2C_CON);
val &= ~CON_INTEN;
__raw_writel(val, phy_i2c_base + SATA_I2C_CON);
/* Master, Send mode */
val = __raw_readl(phy_i2c_base + SATA_I2C_STAT);
val |= STAT_MSTT;
__raw_writel(val, phy_i2c_base + SATA_I2C_STAT);
/* interrupt enable */
val = __raw_readl(phy_i2c_base + SATA_I2C_CON);
val |= CON_INTEN;
__raw_writel(val, phy_i2c_base + SATA_I2C_CON);
/* Enable I2C */
val = __raw_readl(phy_i2c_base + SATA_I2C_STAT);
val |= STAT_RTEN;
__raw_writel(val, phy_i2c_base + SATA_I2C_STAT);
}
static void sata_i2c_init(void)
{
u32 val;
val = __raw_readl(phy_i2c_base + SATA_I2C_CON);
val &= CON_CLK16;
__raw_writel(val, phy_i2c_base + SATA_I2C_CON);
val = __raw_readl(phy_i2c_base + SATA_I2C_CON);
val &= ~(CON_TXCLK_PS);
__raw_writel(val, phy_i2c_base + SATA_I2C_CON);
val = __raw_readl(phy_i2c_base + SATA_I2C_CON);
val |= (2 & CON_TXCLK_PS);
__raw_writel(val, phy_i2c_base + SATA_I2C_CON);
val = __raw_readl(phy_i2c_base + SATA_I2C_LC);
val &= ~(LC_FLTR_EN);
__raw_writel(val, phy_i2c_base + SATA_I2C_LC);
sata_i2c_set_ack_gen(false);
}
static bool sata_i2c_send(u8 slave_addrs, u8 addrs, u8 ucData)
{
s32 ret = 0;
if (!sata_i2c_wait_for_busready(SATA_TIME_LIMIT))
return false;
sata_i2c_init();
sata_i2c_set_master_tx();
__raw_writel(SATA_PHY_CON_RESET, phy_ctrl + SATA_RESET);
sata_i2c_write_addrs(slave_addrs);
sata_i2c_start();
if (!sata_i2c_wait_for_tx_ack(SATA_TIME_LIMIT)) {
ret = false;
goto STOP;
}
sata_i2c_write_data(addrs);
sata_i2c_clear_int_status();
if (!sata_i2c_wait_for_tx_ack(SATA_TIME_LIMIT)) {
ret = false;
goto STOP;
}
sata_i2c_write_data(ucData);
sata_i2c_clear_int_status();
if (!sata_i2c_wait_for_tx_ack(SATA_TIME_LIMIT)) {
ret = false;
goto STOP;
}
ret = true;
STOP:
sata_i2c_stop();
sata_i2c_clear_int_status();
sata_i2c_wait_for_busready(SATA_TIME_LIMIT);
return ret;
}
static bool ahci_phy_init(void __iomem *mmio)
{
u8 uCount, i = 0;
/* 0x3A for 40bit I/F */
u8 reg_addrs[] = {0x22, 0x21, 0x3A};
/* 0x0B for 40bit I/F */
u8 default_setting_value[] = {0x30, 0x4f, 0x0B};
uCount = sizeof(reg_addrs)/sizeof(u8);
while (i < uCount) {
if (!sata_i2c_send(SATA_PHY_I2C_SLAVE_ADDRS, reg_addrs[i],
default_setting_value[i]))
return false;
i++;
}
return true;
}
static int exynos5_ahci_init(void __iomem *mmio)
{
int val, ret;
__raw_writel(S5P_PMU_SATA_PHY_CONTROL_EN, EXYNOS5_SATA_PHY_CONTROL);
val = 0;
__raw_writel(val, phy_ctrl + SATA_RESET);
val = __raw_readl(phy_ctrl + SATA_RESET);
val |= 0x3D;
__raw_writel(val, phy_ctrl + SATA_RESET);
val = __raw_readl(phy_ctrl + SATA_RESET);
val |= LINK_RESET;
__raw_writel(val, phy_ctrl + SATA_RESET);
val = __raw_readl(phy_ctrl + SATA_RESET);
val |= RESET_CMN_RST_N;
__raw_writel(val, phy_ctrl + SATA_RESET);
val = __raw_readl(phy_ctrl + SATA_PHSATA_CTRLM);
val &= ~PHCTRLM_REF_RATE;
__raw_writel(val, phy_ctrl + SATA_PHSATA_CTRLM);
/* High speed enable for Gen3 */
val = __raw_readl(phy_ctrl + SATA_PHSATA_CTRLM);
val |= PHCTRLM_HIGH_SPEED;
__raw_writel(val, phy_ctrl + SATA_PHSATA_CTRLM);
/* Port0 is available */
__raw_writel(0x1, mmio + HOST_PORTS_IMPL);
ret = ahci_phy_init(mmio);
val = __raw_readl(phy_ctrl + SATA_CTRL0);
val |= CTRL0_P0_PHY_CALIBRATED_SEL|CTRL0_P0_PHY_CALIBRATED;
__raw_writel(val, phy_ctrl + SATA_CTRL0);
sata_set_gen(SATA_GENERATION3);
/* release cmu reset */
val = __raw_readl(phy_ctrl + SATA_RESET);
val &= ~RESET_CMN_RST_N;
__raw_writel(val, phy_ctrl + SATA_RESET);
val = __raw_readl(phy_ctrl + SATA_RESET);
val |= RESET_CMN_RST_N;
__raw_writel(val, phy_ctrl + SATA_RESET);
if (wait_for_reg_status(phy_ctrl, SATA_PHSATA_STATM,
PHSTATM_PLL_LOCKED, 1)) {
return ret;
}
return 0;
}
static int exynos5_sata_enable_power(const void *blob)
{
int node;
struct fdt_gpio_state gpio;
node = fdtdec_next_compatible(blob, 0, COMPAT_GOOGLE_SATA);
if (node >= 0 &&
fdtdec_decode_gpio(blob, node, "enable-gpios", &gpio) == 0) {
gpio_cfg_pin(gpio.gpio, EXYNOS_GPIO_OUTPUT);
gpio_set_value(gpio.gpio, 1);
return 0;
}
return -ENODEV;
}
static void exynos5_enable_clock_gates(void)
{
/* Turn on all SATA clock gates & DMA gates. */
const unsigned cmu_toppart = 0x10020000;
const unsigned addr = cmu_toppart + 0x944;
const unsigned sata_clocks = (1 << 25) | (1 << 24) | (1 << 6);
const unsigned dma_clocks = (2 << 1) | (1 << 1);
const unsigned clk_gate_ip_fsys = readl(addr);
writel(clk_gate_ip_fsys | sata_clocks | dma_clocks, addr);
}
int exynos5_sata_init(const void *blob)
{
if (exynos5_sata_enable_power(blob) == 0) {
exynos5_enable_clock_gates();
if (exynos5_ahci_init((void *)SATA_AHCI_AXI)) {
ahci_init(SATA_AHCI_AXI);
scsi_scan(1);
return 0;
}
}
return -ENODEV;
}

View File

@ -25,6 +25,10 @@
#ifndef _SMDK5250_SETUP_H #ifndef _SMDK5250_SETUP_H
#define _SMDK5250_SETUP_H #define _SMDK5250_SETUP_H
struct exynos5_dmc;
enum ddr_mode;
struct exynos5_phy_control;
/* TZPC : Register Offsets */ /* TZPC : Register Offsets */
#define TZPC0_BASE 0x10100000 #define TZPC0_BASE 0x10100000
#define TZPC1_BASE 0x10110000 #define TZPC1_BASE 0x10110000
@ -683,7 +687,8 @@ enum {
}; };
/* Functions common between LPDDR2 and DDR3 */ /* Functions common between LPDDR2 and DDR3 */
void sdelay(unsigned long); /* FIXME(dhendrix): conflicts with arch system.h version of sdelay()... */
//void sdelay(unsigned long);
/* CPU info initialization code */ /* CPU info initialization code */
void cpu_info_init(void); void cpu_info_init(void);

View File

@ -0,0 +1,47 @@
/*
* This file is part of the coreboot project.
*
* Copyright (C) 2011 The Chromium OS Authors. All rights reserved.
*
* 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 <cpu/samsung/exynos5250/cpu.h>
#include <cpu/samsung/exynos5250/periph.h>
#include <cpu/samsung/exynos5250/uart.h>
enum periph_id exynos5_get_periph_id(unsigned base_addr)
{
enum periph_id id = PERIPH_ID_NONE;
switch (base_addr) {
case EXYNOS5_UART0_BASE:
id = PERIPH_ID_UART0;
break;
case EXYNOS5_UART1_BASE:
id = PERIPH_ID_UART1;
break;
case EXYNOS5_UART2_BASE:
id = PERIPH_ID_UART2;
break;
case EXYNOS5_UART3_BASE:
id = PERIPH_ID_UART3;
break;
default:
break;
}
return id;
}

View File

@ -0,0 +1,41 @@
/*
* 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; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
/* FIXME(dhendrix): file unneeded? */
#if 0
#include <asm/types.h>
#include <asm/arch-exynos/cpu.h>
#include <asm/arch-exynos/spl.h>
/* Get the u-boot size from the SPL parameter table */
unsigned int exynos_get_uboot_size(void)
{
struct spl_machine_param *param = spl_get_machine_params();
return param->uboot_size;
}
/* Get the boot device from the SPL parameter table */
enum boot_mode exynos_get_boot_device(void)
{
struct spl_machine_param *param = spl_get_machine_params();
return param->boot_source;
}
#endif

View File

@ -0,0 +1,57 @@
/*
* Lowlevel setup for SMDK5250 board based on S5PC520
*
* Copyright (C) 2012 Samsung Electronics
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <cpu/samsung/exynos5250/cpu.h>
#include <asm/arch/dmc.h>
#include <asm/arch/tzpc.h>
#include"setup.h"
/* Setting TZPC[TrustZone Protection Controller] */
void tzpc_init(void)
{
struct exynos5_tzpc *tzpc;
unsigned int addr;
for (addr = TZPC0_BASE; addr <= TZPC9_BASE; addr += TZPC_BASE_OFFSET) {
tzpc = (struct exynos5_tzpc *)addr;
if (addr == TZPC0_BASE)
writel(R0SIZE, &tzpc->r0size);
writel(DECPROTXSET, &tzpc->decprot0set);
writel(DECPROTXSET, &tzpc->decprot1set);
if (addr == TZPC9_BASE) {
/* TODO: Add comment here describing the numerical values
* used below.
*/
writel(0xf0, &tzpc->decprot2set);
writel(0x50, &tzpc->decprot3set);
} else {
writel(DECPROTXSET, &tzpc->decprot2set);
writel(DECPROTXSET, &tzpc->decprot3set);
}
}
}

View File

@ -0,0 +1,235 @@
/*
* (C) Copyright 2009 SAMSUNG Electronics
* Minkyu Kang <mk7.kang@samsung.com>
* Heungjun Kim <riverful.kim@samsung.com>
*
* based on drivers/serial/s3c64xx.c
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
//#include <common.h>
//#include <linux/compiler.h>
#include <uart.h>
#include <arch/io.h>
//#include <asm/arch-exynos/spl.h>
//#include <asm/global_data.h>
//#include <fdtdec.h>
//#include <serial.h>
#include <console/console.h> /* for __console definition */
#include <cpu/samsung/exynos5-common/exynos5-common.h>
#include <cpu/samsung/exynos5250/clk.h>
#include <cpu/samsung/exynos5250/uart.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;
#if 0
/* Information about a serial port */
struct fdt_serial {
u32 base_addr; /* address of registers in physical memory */
u8 port_id; /* uart port number */
u8 enabled; /* 1 if enabled, 0 if disabled */
} config = {
-1U
};
#endif
#if 0
static inline struct s5p_uart *s5p_get_base_uart(int dev_index)
{
/* FIXME: there should be an assertion here if dev_index is >3 */
return (struct s5p_uart *)(EXYNOS5_UART0_BASE + (0x10000 * dev_index));
}
#endif
/*
* 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 *const uart = s5p_get_base_uart(dev_index);
struct s5p_uart *uart = (struct s5p_uart *)base_port;
u32 uclk;
u32 baudrate = CONFIG_TTYS0_BAUD;
u32 val;
enum periph_id periph;
periph = exynos5_get_periph_id(base_port);
uclk = clock_get_periph_rate(periph);
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 exynos5250... not entirely sure
* where/if we need to worry about it here
*/
#if 0
if (s5p_uart_divslot())
writew(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 *const uart = s5p_get_base_uart(dev_index);
struct s5p_uart *uart = (struct s5p_uart *)base_port;
/* 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 *const uart = s5p_get_base_uart(dev_index);
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 *const uart = s5p_get_base_uart(dev_index);
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.
*/
/* FIXME: ordering of arguments for coreboot v. u-boot for tx_byte */
//static void exynos5_tx_byte(const char c, const int dev_index)
static void exynos5_uart_tx_byte(unsigned char data)
{
// struct s5p_uart *const uart = s5p_get_base_uart(dev_index);
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);
}
#ifndef __PRE_RAM__
static const struct console_driver exynos5_uart_console __console = {
//static const struct console_driver exynos5_uart_console __console = {
#if 0
void (*init)(void);
void (*tx_byte)(unsigned char byte);
void (*tx_flush)(void);
unsigned char (*rx_byte)(void);
int (*tst_byte)(void);
#endif
.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,
};
#else
/* for romstage_console... */
//void (*uart_init)(void) = exynos5_init_dev;
//unsigned char (*uart_rx_byte)(unsigned base_port) = exynos5_uart_rx_byte;
//void (*uart_tx_byte)(unsigned base_port, unsigned char data) = exynos5_uart_tx_byte;
/* FIXME: trivial wrappers */
void uart_init()
{
exynos5_init_dev();
}
unsigned char uart_rx_byte()
{
return exynos5_uart_rx_byte();
}
void uart_tx_byte(unsigned char data)
{
exynos5_uart_tx_byte(data);
}
#endif

View File

@ -0,0 +1,12 @@
romstage-y += cpu_info.c
romstage-y += pwm.c # needed by timer.c
romstage-y += s5p_gpio.c
romstage-y += timer.c
#romstage-y += sromc.c
#romstage-y += wdt.c
ramstage-y += cpu_info.c
ramstage-y += pwm.c # needed by timer.c
ramstage-y += timer.c
ramstage-y += s5p_gpio.c

View File

@ -0,0 +1,49 @@
#
# Copyright (C) 2009 Samsung Electronics
# Minkyu Kang <mk7.kang@samsung.com>
#
# See file CREDITS for list of people who contributed to this
# project.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of
# the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston,
# MA 02111-1307 USA
#
include $(TOPDIR)/config.mk
LIB = $(obj)libs5p-common.o
COBJS-y += cpu_info.o
COBJS-y += timer.o
COBJS-y += sromc.o
COBJS-y += wdt.o
COBJS-$(CONFIG_PWM) += pwm.o
SRCS := $(SOBJS:.o=.S) $(COBJS:.o=.c)
OBJS := $(addprefix $(obj),$(COBJS-y) $(SOBJS))
all: $(obj).depend $(LIB)
$(LIB): $(OBJS)
$(call cmd_link_o_target, $(OBJS))
#########################################################################
# defines $(obj).depend target
include $(SRCTREE)/rules.mk
sinclude $(obj).depend
#########################################################################

View File

@ -0,0 +1,128 @@
/*
* Copyright (C) 2009 Samsung Electronics
* Minkyu Kang <mk7.kang@samsung.com>
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <common.h>
#if 0
#include <asm/io.h>
#include <asm/arch/clk.h>
#include <asm/arch/clock.h>
#include <asm/arch/dmc.h>
#endif
#include <arch/io.h>
#include <cpu/samsung/s5p-common/clk.h>
#include <cpu/samsung/s5p-common/clock.h>
#include <cpu/samsung/s5p-common/cpu.h>
#include <cpu/samsung/exynos5250/dmc.h>
#include <cpu/samsung/exynos5-common/cpu.h> /* for EXYNOS_PRO_ID */
/* FIXME(dhendrix): consolidate samsung ID code/#defines to a common location */
#include <cpu/samsung/exynos5250/setup.h> /* cpu_info_init() prototype */
/*
* The following CPU infos are initialized in lowlevel_init(). They should be
* put in the .data section. Otherwise, a compile will put them in the .bss
* section since they don't have initial values. The relocation code which
* runs after lowlevel_init() will reset them to zero.
*/
unsigned int s5p_cpu_id __attribute__((section(".data")));
unsigned int s5p_cpu_rev __attribute__((section(".data")));
void cpu_info_init(void)
{
s5p_set_cpu_id();
}
int s5p_get_cpu_id(void)
{
return s5p_cpu_id;
}
int s5p_get_cpu_rev(void)
{
return s5p_cpu_rev;
}
void s5p_set_cpu_id(void)
{
s5p_cpu_id = readl(EXYNOS_PRO_ID);
s5p_cpu_id = (0xC000 | ((s5p_cpu_id & 0x00FFF000) >> 12));
/*
* 0xC200: EXYNOS4210 EVT0
* 0xC210: EXYNOS4210 EVT1
*/
if (s5p_cpu_id == 0xC200) {
s5p_cpu_id |= 0x10;
s5p_cpu_rev = 0;
} else if (s5p_cpu_id == 0xC210) {
s5p_cpu_rev = 1;
}
}
#ifdef CONFIG_DISPLAY_CPUINFO
int print_cpuinfo(void)
{
char buf[32];
printf("CPU: S5P%X @ %sMHz\n",
s5p_cpu_id, strmhz(buf, get_arm_clk()));
return 0;
}
#endif
#ifndef CONFIG_SPL_BUILD
void board_show_dram(ulong size)
{
enum ddr_mode mem_type;
unsigned frequency_mhz;
unsigned arm_freq;
enum mem_manuf mem_manuf;
char buf[32];
int ret;
/* Get settings from the fdt */
ret = clock_get_mem_selection(&mem_type, &frequency_mhz,
&arm_freq, &mem_manuf);
if (ret)
panic("Invalid DRAM information");
puts("DRAM: ");
print_size(size, " ");
printf("%s %s @ %sMHz",
clock_get_mem_manuf_name(mem_manuf),
clock_get_mem_type_name(mem_type),
strmhz(buf, frequency_mhz));
putc('\n');
}
#endif
#ifdef CONFIG_ARCH_CPU_INIT
int arch_cpu_init(void)
{
cpu_info_init();
return 0;
}
#endif

View File

@ -0,0 +1,205 @@
/*
* Copyright (C) 2011 Samsung Electronics
*
* Donghwa Lee <dh09.lee@samsung.com>
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <common.h>
//#include <pwm.h>
#include <arch/io.h>
//#include <arch/pwm.h>
//#include <arch/clk.h>
/* FIXME(dhendrix): this is a godawful mess of similar-but-different includes... */
#include <cpu/samsung/exynos5-common/clk.h>
#include <cpu/samsung/exynos5250/cpu.h>
#include <cpu/samsung/exynos5250/periph.h>
#include <cpu/samsung/s5p-common/pwm.h>
#include <cpu/samsung/s5p-common/clk.h>
//#include <arch/periph.h>
int pwm_enable(int pwm_id)
{
const struct s5p_timer *pwm =
(struct s5p_timer *)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 =
(struct s5p_timer *)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 =
(struct s5p_timer *)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 =
(struct s5p_timer *)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;
// return -ERANGE;
if (duty_ns > period_ns)
return -1;
// return -EINVAL;
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 =
(struct s5p_timer *)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,490 @@
/*
* (C) Copyright 2009 Samsung Electronics
* Minkyu Kang <mk7.kang@samsung.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
/* FIXME(dhendrix): fix this up so it doesn't require a bunch of #ifdefs... */
#include <common.h>
//#include <arch/io.h>
#include <arch/gpio.h>
#include <console/console.h>
#include <cpu/samsung/s5p-common/gpio.h>
#include <cpu/samsung/exynos5-common/gpio.h>
#include <cpu/samsung/exynos5250/gpio.h> /* FIXME: for gpio_decode_number prototype */
#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 */
};
#ifdef CONFIG_CPU_SAMSUNG_EXYNOS5
#include <cpu/samsung/exynos5250/cpu.h>
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 },
};
#define HAVE_GENERIC_GPIO
#elif defined(CONFIG_CPU_SAMSUNG_EXYNOS4)
static const struct gpio_info gpio_data[EXYNOS_GPIO_NUM_PARTS] = {
{ EXYNOS4_GPIO_PART1_BASE, GPIO_MAX_PORT_PART_1 },
{ EXYNOS4_GPIO_PART2_BASE, GPIO_MAX_PORT_PART_2 },
{ EXYNOS4_GPIO_PART3_BASE, GPIO_MAX_PORT_PART_3 },
};
#define HAVE_GENERIC_GPIO
#endif
/* This macro gets gpio pin offset from 0..7 */
#define GPIO_BIT(x) ((x) & 0x7)
//#ifdef HAVE_GENERIC_GPIO
static struct s5p_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 s5p_gpio_bank *bank;
bank = (struct s5p_gpio_bank *)data->reg_addr;
bank += (gpio - upto) / GPIO_PER_BANK;
return bank;
}
}
#ifndef CONFIG_SPL_BUILD
assert(gpio < GPIO_MAX_PORT); /* ...which it will not be */
#endif
return NULL;
}
//#endif
/* TODO: Deprecation this interface in favour of asm-generic/gpio.h */
void s5p_gpio_cfg_pin(struct s5p_gpio_bank *bank, int gpio, int cfg)
{
unsigned int value;
value = readl(&bank->con);
value &= ~CON_MASK(gpio);
value |= CON_SFR(gpio, cfg);
writel(value, &bank->con);
}
void s5p_gpio_direction_output(struct s5p_gpio_bank *bank, int gpio, int en)
{
unsigned int value;
s5p_gpio_cfg_pin(bank, gpio, EXYNOS_GPIO_OUTPUT);
value = readl(&bank->dat);
value &= ~DAT_MASK(gpio);
if (en)
value |= DAT_SET(gpio);
writel(value, &bank->dat);
}
void s5p_gpio_direction_input(struct s5p_gpio_bank *bank, int gpio)
{
s5p_gpio_cfg_pin(bank, gpio, EXYNOS_GPIO_INPUT);
}
void s5p_gpio_set_value(struct s5p_gpio_bank *bank, int gpio, int en)
{
unsigned int value;
value = readl(&bank->dat);
value &= ~DAT_MASK(gpio);
if (en)
value |= DAT_SET(gpio);
writel(value, &bank->dat);
}
unsigned int s5p_gpio_get_value(struct s5p_gpio_bank *bank, int gpio)
{
unsigned int value;
value = readl(&bank->dat);
return !!(value & DAT_MASK(gpio));
}
void s5p_gpio_set_pull(struct s5p_gpio_bank *bank, int gpio, int mode)
{
unsigned int value;
value = readl(&bank->pull);
value &= ~PULL_MASK(gpio);
switch (mode) {
case EXYNOS_GPIO_PULL_DOWN:
case EXYNOS_GPIO_PULL_UP:
value |= PULL_MODE(gpio, mode);
break;
default:
break;
}
writel(value, &bank->pull);
}
void s5p_gpio_set_drv(struct s5p_gpio_bank *bank, int gpio, int mode)
{
unsigned int value;
value = readl(&bank->drv);
value &= ~DRV_MASK(gpio);
switch (mode) {
case EXYNOS_GPIO_DRV_1X:
case EXYNOS_GPIO_DRV_2X:
case EXYNOS_GPIO_DRV_3X:
case EXYNOS_GPIO_DRV_4X:
value |= DRV_SET(gpio, mode);
break;
default:
return;
}
writel(value, &bank->drv);
}
void s5p_gpio_set_rate(struct s5p_gpio_bank *bank, int gpio, int mode)
{
unsigned int value;
value = readl(&bank->drv);
value &= ~RATE_MASK(gpio);
switch (mode) {
case EXYNOS_GPIO_DRV_FAST:
case EXYNOS_GPIO_DRV_SLOW:
value |= RATE_SET(gpio);
break;
default:
return;
}
writel(value, &bank->drv);
}
/* Common GPIO API - only available on Exynos5 */
/* FIXME(dhendrix): If this stuff is really only applicable to exynos5,
move it to a more sensible location. */
#ifdef HAVE_GENERIC_GPIO
void gpio_cfg_pin(int gpio, int cfg)
{
unsigned int value;
struct s5p_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 s5p_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 s5p_gpio_bank *bank = gpio_get_bank(gpio);
value = readl(&bank->pull);
value &= ~PULL_MASK(GPIO_BIT(gpio));
switch (mode) {
case EXYNOS_GPIO_PULL_DOWN:
case EXYNOS_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 s5p_gpio_bank *bank = gpio_get_bank(gpio);
value = readl(&bank->drv);
value &= ~DRV_MASK(GPIO_BIT(gpio));
switch (mode) {
case EXYNOS_GPIO_DRV_1X:
case EXYNOS_GPIO_DRV_2X:
case EXYNOS_GPIO_DRV_3X:
case EXYNOS_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 s5p_gpio_bank *bank = gpio_get_bank(gpio);
value = readl(&bank->drv);
value &= ~RATE_MASK(GPIO_BIT(gpio));
switch (mode) {
case EXYNOS_GPIO_DRV_FAST:
case EXYNOS_GPIO_DRV_SLOW:
value |= RATE_SET(GPIO_BIT(gpio));
break;
default:
return;
}
writel(value, &bank->drv);
}
int gpio_request(unsigned gpio, const char *label)
{
return 0;
}
int gpio_free(unsigned gpio)
{
return 0;
}
int gpio_direction_input(unsigned gpio)
{
gpio_cfg_pin(gpio, EXYNOS_GPIO_INPUT);
return 0;
}
int gpio_direction_output(unsigned gpio, int value)
{
unsigned int val;
struct s5p_gpio_bank *bank = gpio_get_bank(gpio);
gpio_cfg_pin(gpio, EXYNOS_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 s5p_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 s5p_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;
}
#else
static int s5p_gpio_get_pin(unsigned gpio)
{
return gpio % GPIO_PER_BANK;
}
/*
* If we have the old-style GPIO numbering setup, use these functions
* which don't necessary provide sequentially increasing GPIO numbers.
*/
static struct s5p_gpio_bank *s5p_gpio_get_bank(unsigned gpio)
{
int bank = gpio / GPIO_PER_BANK;
bank *= sizeof(struct s5p_gpio_bank);
return (struct s5p_gpio_bank *) (s5p_gpio_base(gpio) + bank);
}
int gpio_request(unsigned gpio, const char *label)
{
return 0;
}
int gpio_free(unsigned gpio)
{
return 0;
}
int gpio_direction_input(unsigned gpio)
{
s5p_gpio_direction_input(s5p_gpio_get_bank(gpio),
s5p_gpio_get_pin(gpio));
return 0;
}
int gpio_direction_output(unsigned gpio, int value)
{
s5p_gpio_direction_output(s5p_gpio_get_bank(gpio),
s5p_gpio_get_pin(gpio), value);
return 0;
}
int gpio_get_value(unsigned gpio)
{
return (int) s5p_gpio_get_value(s5p_gpio_get_bank(gpio),
s5p_gpio_get_pin(gpio));
}
int gpio_set_value(unsigned gpio, int value)
{
s5p_gpio_set_value(s5p_gpio_get_bank(gpio),
s5p_gpio_get_pin(gpio), value);
return 0;
}
#endif /* HAVE_GENERIC_GPIO */
/*
* 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
/* FIXME(dhendrix): this should probably go to a more generic location */
int gpio_decode_number(unsigned gpio_list[], int count)
{
int result = 0;
int multiplier = 1;
int value, high, low;
int gpio, i;
for (i = 0; i < count; i++) {
gpio = gpio_list[i];
if (gpio >= GPIO_MAX_PORT)
return -1;
gpio_direction_input(gpio);
gpio_set_pull(gpio, EXYNOS_GPIO_PULL_UP);
udelay(GPIO_DELAY_US);
high = gpio_get_value(gpio);
gpio_set_pull(gpio, EXYNOS_GPIO_PULL_DOWN);
udelay(GPIO_DELAY_US);
low = gpio_get_value(gpio);
if (high && low) /* external pullup */
value = 2;
else if (!high && !low) /* external pulldown */
value = 1;
else /* floating */
value = 0;
/*
* 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 == 2)
gpio_set_pull(gpio, EXYNOS_GPIO_PULL_UP);
result += value * multiplier;
multiplier *= 3;
}
return result;
}
static const char *get_cfg_name(int cfg)
{
static char name[8];
if (cfg == EXYNOS_GPIO_INPUT)
return "input";
else if (cfg == EXYNOS_GPIO_OUTPUT)
return "output";
printk(BIOS_INFO, "func %d", cfg);
// sprintf(name, "func %d", cfg);
return name;
}
/*
* 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: %s", gpio, get_cfg_name(cfg));
if (cfg == EXYNOS_GPIO_INPUT || cfg == EXYNOS_GPIO_OUTPUT)
printk(BIOS_INFO, ", value = %d", gpio_get_value(gpio));
printk(BIOS_INFO, "\n");
}
}

View File

@ -0,0 +1,49 @@
/*
* Copyright (C) 2010 Samsung Electronics
* Naveen Krishna Ch <ch.naveen@samsung.com>
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <common.h>
#include <asm/io.h>
#include <asm/arch/sromc.h>
/*
* s5p_config_sromc() - select the proper SROMC Bank and configure the
* band width control and bank control registers
* srom_bank - SROM
* srom_bw_conf - SMC Band witdh reg configuration value
* srom_bc_conf - SMC Bank Control reg configuration value
*/
void s5p_config_sromc(u32 srom_bank, u32 srom_bw_conf, u32 srom_bc_conf)
{
u32 tmp;
struct s5p_sromc *srom =
(struct s5p_sromc *)samsung_get_base_sromc();
/* Configure SMC_BW register to handle proper SROMC bank */
tmp = srom->bw;
tmp &= ~(0xF << (srom_bank * 4));
tmp |= srom_bw_conf;
srom->bw = tmp;
/* Configure SMC_BC register */
srom->bc[srom_bank] = srom_bc_conf;
}

View File

@ -0,0 +1,153 @@
/*
* Copyright (C) 2009 Samsung Electronics
* Heungjun Kim <riverful.kim@samsung.com>
* Inki Dae <inki.dae@samsung.com>
* Minkyu Kang <mk7.kang@samsung.com>
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <common.h>
#include <arch/io.h>
//#include <cpu/samsung/exynos5250/pwm.h>
//#include <cpu/samsung/exynos5250/clk.h>
#include <cpu/samsung/s5p-common/pwm.h>
#include <cpu/samsung/s5p-common/clk.h>
#include <cpu/samsung/exynos5250/cpu.h>
#include <cpu/samsung/exynos5-common/exynos5-common.h>
//#include <pwm.h>
//DECLARE_GLOBAL_DATA_PTR;
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 (struct s5p_timer *)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);
}
int 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);
#ifndef CONFIG_SPL_BUILD
/* Use this as the current monotonic time in us */
//gd->timer_reset_value = 0;
timer_reset_value = 0;
/* Use this as the last timer value we saw */
//gd->lastinc = timer_get_us_down();
lastinc = timer_get_us_down();
#endif
}
return 0;
}
/*
* timer without interrupts
*/
unsigned long get_timer(unsigned long base)
{
ulong 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.
*/
#if 0
gd->timer_reset_value += gd->lastinc - now;
gd->lastinc = now;
/* Divide by 1000 to convert from us to ms */
return gd->timer_reset_value / 1000 - base;
#endif
timer_reset_value += lastinc - now;
lastinc = now;
/* Divide by 1000 to convert from us to ms */
return timer_reset_value / 1000 - base;
}
unsigned long timer_get_us(void)
{
struct s5p_timer *const timer = s5p_get_base_timer();
unsigned long now_downward_us = readl(&timer->tcnto4);
/*
* Note that this timer counts downward. The pre-SPL process (BL1)
* takes about 100ms, so add this in here.
*/
return CONFIG_SPL_TIME_US - now_downward_us;
}
/* delay x useconds */
void __udelay(unsigned long usec)
{
unsigned long count_value;
count_value = timer_get_us_down();
while ((int)(count_value - timer_get_us_down()) < (int)usec)
;
}
/*
* This function is derived from PowerPC code (read timebase as long long).
* On ARM it just returns the timer value.
*/
unsigned long long get_ticks(void)
{
return get_timer(0);
}
/*
* This function is derived from PowerPC code (timebase clock frequency).
* On ARM it returns the number of timer ticks per second.
*/
unsigned long get_tbclk(void)
{
return CONFIG_SYS_HZ;
}
unsigned long timer_get_boot_us(void)
{
return timer_get_us();
}

View File

@ -0,0 +1,59 @@
/*
* Copyright (C) 2012 Samsung Electronics
* Minkyu Kang <mk7.kang@samsung.com>
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <common.h>
#include <asm/io.h>
#include <asm/arch/watchdog.h>
#define PRESCALER_VAL 255
void wdt_stop(void)
{
struct s5p_watchdog *wdt =
(struct s5p_watchdog *)samsung_get_base_watchdog();
unsigned int wtcon;
wtcon = readl(&wdt->wtcon);
wtcon &= ~(WTCON_EN | WTCON_INT | WTCON_RESET);
writel(wtcon, &wdt->wtcon);
}
void wdt_start(unsigned int timeout)
{
struct s5p_watchdog *wdt =
(struct s5p_watchdog *)samsung_get_base_watchdog();
unsigned int wtcon;
wdt_stop();
wtcon = readl(&wdt->wtcon);
wtcon |= (WTCON_EN | WTCON_CLK(WTCON_CLK_128));
wtcon &= ~WTCON_INT;
wtcon |= WTCON_RESET;
wtcon |= WTCON_PRESCALER(PRESCALER_VAL);
writel(timeout, &wdt->wtdat);
writel(timeout, &wdt->wtcnt);
writel(wtcon, &wdt->wtcon);
}