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:
parent
747127d505
commit
9fe20cb381
|
@ -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
|
||||
|
||||
source src/cpu/amd/Kconfig
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
################################################################################
|
||||
subdirs-y += amd
|
||||
subdirs-y += intel
|
||||
subdirs-y += samsung
|
||||
subdirs-y += via
|
||||
|
||||
################################################################################
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1,2 @@
|
|||
#romstage-y += soc.c
|
||||
romstage-y += spl_boot.c
|
|
@ -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());
|
||||
}
|
|
@ -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(®s->ch_cfg, SPI_CH_RST);
|
||||
clrbits_le32(®s->ch_cfg, SPI_CH_RST);
|
||||
writel(((todo * 8) / 32) | SPI_PACKET_CNT_EN, ®s->pkt_cnt);
|
||||
|
||||
while (in_bytes) {
|
||||
uint32_t spi_sts;
|
||||
int temp;
|
||||
|
||||
spi_sts = readl(®s->spi_sts);
|
||||
rx_lvl = ((spi_sts >> 15) & 0x7f);
|
||||
tx_lvl = ((spi_sts >> 6) & 0x7f);
|
||||
while (tx_lvl < 32 && out_bytes) {
|
||||
temp = 0xffffffff;
|
||||
writel(temp, ®s->tx_data);
|
||||
out_bytes -= 4;
|
||||
tx_lvl += 4;
|
||||
}
|
||||
while (rx_lvl >= 4 && in_bytes) {
|
||||
temp = readl(®s->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, ®s->pkt_cnt);
|
||||
/* set FB_CLK_SEL */
|
||||
writel(SPI_FB_DELAY_180, ®s->fb_clk);
|
||||
/* set CH_WIDTH and BUS_WIDTH as word */
|
||||
setbits_le32(®s->mode_cfg, SPI_MODE_CH_WIDTH_WORD |
|
||||
SPI_MODE_BUS_WIDTH_WORD);
|
||||
clrbits_le32(®s->ch_cfg, SPI_CH_CPOL_L); /* CPOL: active high */
|
||||
|
||||
/* clear rx and tx channel if set priveously */
|
||||
clrbits_le32(®s->ch_cfg, SPI_RX_CH_ON | SPI_TX_CH_ON);
|
||||
|
||||
setbits_le32(®s->swap_cfg, SPI_RX_SWAP_EN |
|
||||
SPI_RX_BYTE_SWAP |
|
||||
SPI_RX_HWORD_SWAP);
|
||||
|
||||
/* do a soft reset */
|
||||
setbits_le32(®s->ch_cfg, SPI_CH_RST);
|
||||
clrbits_le32(®s->ch_cfg, SPI_CH_RST);
|
||||
|
||||
/* now set rx and tx channel ON */
|
||||
setbits_le32(®s->ch_cfg, SPI_RX_CH_ON | SPI_TX_CH_ON | SPI_CH_HS_EN);
|
||||
clrbits_le32(®s->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, ®s->tx_data);
|
||||
|
||||
/* waiting for TX done */
|
||||
while (!(readl(®s->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(®s->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(®s->mode_cfg, SPI_MODE_CH_WIDTH_WORD |
|
||||
SPI_MODE_BUS_WIDTH_WORD);
|
||||
writel(0, ®s->swap_cfg);
|
||||
|
||||
/*
|
||||
* Flush spi tx, rx fifos and reset the SPI controller
|
||||
* and clear rx/tx channel
|
||||
*/
|
||||
clrsetbits_le32(®s->ch_cfg, SPI_CH_HS_EN, SPI_CH_RST);
|
||||
clrbits_le32(®s->ch_cfg, SPI_CH_RST);
|
||||
clrbits_le32(®s->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
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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)
|
||||
{
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -25,6 +25,10 @@
|
|||
#ifndef _SMDK5250_SETUP_H
|
||||
#define _SMDK5250_SETUP_H
|
||||
|
||||
struct exynos5_dmc;
|
||||
enum ddr_mode;
|
||||
struct exynos5_phy_control;
|
||||
|
||||
/* TZPC : Register Offsets */
|
||||
#define TZPC0_BASE 0x10100000
|
||||
#define TZPC1_BASE 0x10110000
|
||||
|
@ -683,7 +687,8 @@ enum {
|
|||
};
|
||||
|
||||
/* 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 */
|
||||
void cpu_info_init(void);
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
||||
#########################################################################
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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);
|
||||
}
|
Loading…
Reference in New Issue