diff options
author | Stefan Reinauer <stefan.reinauer@coreboot.org> | 2012-12-07 17:18:43 -0800 |
---|---|---|
committer | Ronald G. Minnich <rminnich@gmail.com> | 2012-12-08 06:48:03 +0100 |
commit | 9fe20cb3814df88f181648860102a9da249a4da1 (patch) | |
tree | 3623451d939c151754dcec0e4f87ec27b5a44614 | |
parent | 747127d50545c1fbd0dcc10baacc742d3151ddfe (diff) |
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)
33 files changed, 5688 insertions, 1 deletions
diff --git a/src/cpu/Kconfig b/src/cpu/Kconfig index 986c68afc4..383ba796a2 100644 --- a/src/cpu/Kconfig +++ b/src/cpu/Kconfig @@ -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 diff --git a/src/cpu/Makefile.inc b/src/cpu/Makefile.inc index 938a8dfef1..93b16ae3bd 100644 --- a/src/cpu/Makefile.inc +++ b/src/cpu/Makefile.inc @@ -3,6 +3,7 @@ ################################################################################ subdirs-y += amd subdirs-y += intel +subdirs-y += samsung subdirs-y += via ################################################################################ diff --git a/src/cpu/samsung/Kconfig b/src/cpu/samsung/Kconfig new file mode 100644 index 0000000000..3a14ab86e8 --- /dev/null +++ b/src/cpu/samsung/Kconfig @@ -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 diff --git a/src/cpu/samsung/Makefile.inc b/src/cpu/samsung/Makefile.inc new file mode 100644 index 0000000000..b6fcd4e9c0 --- /dev/null +++ b/src/cpu/samsung/Makefile.inc @@ -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 diff --git a/src/cpu/samsung/exynos5-common/Makefile.inc b/src/cpu/samsung/exynos5-common/Makefile.inc new file mode 100644 index 0000000000..cad6cb3de7 --- /dev/null +++ b/src/cpu/samsung/exynos5-common/Makefile.inc @@ -0,0 +1,2 @@ +#romstage-y += soc.c +romstage-y += spl_boot.c diff --git a/src/cpu/samsung/exynos5-common/soc.c b/src/cpu/samsung/exynos5-common/soc.c new file mode 100644 index 0000000000..e73fd338ba --- /dev/null +++ b/src/cpu/samsung/exynos5-common/soc.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()); +} diff --git a/src/cpu/samsung/exynos5-common/spl_boot.c b/src/cpu/samsung/exynos5-common/spl_boot.c new file mode 100644 index 0000000000..8ae051171e --- /dev/null +++ b/src/cpu/samsung/exynos5-common/spl_boot.c @@ -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 diff --git a/src/cpu/samsung/exynos5250/Kconfig b/src/cpu/samsung/exynos5250/Kconfig new file mode 100644 index 0000000000..8d4ba2dd0a --- /dev/null +++ b/src/cpu/samsung/exynos5250/Kconfig @@ -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 diff --git a/src/cpu/samsung/exynos5250/Makefile.inc b/src/cpu/samsung/exynos5250/Makefile.inc new file mode 100644 index 0000000000..556631a25a --- /dev/null +++ b/src/cpu/samsung/exynos5250/Makefile.inc @@ -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 diff --git a/src/cpu/samsung/exynos5250/ace_sha.c b/src/cpu/samsung/exynos5250/ace_sha.c new file mode 100644 index 0000000000..2715a03b2d --- /dev/null +++ b/src/cpu/samsung/exynos5250/ace_sha.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; +} diff --git a/src/cpu/samsung/exynos5250/clock.c b/src/cpu/samsung/exynos5250/clock.c new file mode 100644 index 0000000000..0250d77a28 --- /dev/null +++ b/src/cpu/samsung/exynos5250/clock.c @@ -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; +} diff --git a/src/cpu/samsung/exynos5250/clock_init.c b/src/cpu/samsung/exynos5250/clock_init.c new file mode 100644 index 0000000000..fd2a90f815 --- /dev/null +++ b/src/cpu/samsung/exynos5250/clock_init.c @@ -0,0 +1,1189 @@ +/* + * Clock setup 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 + */ + +#if 0 +#include <common.h> +#include <config.h> +//#include <fdtdec.h> +#include <arch/io.h> +#include <asm/arch/board.h> +#include <asm/arch/clk.h> +#include <asm/arch/clock.h> +#include <cpu/samsung/exynos5250/cpu.h> +#include <asm/arch/gpio.h> +#include <asm/arch-exynos/spl.h> +#endif +#include <delay.h> +#include <stdlib.h> +#include <types.h> +#include <system.h> + +#include <console/console.h> + +#include <cpu/samsung/exynos5-common/spl.h> +#include <cpu/samsung/exynos5250/clk.h> +#include <cpu/samsung/exynos5250/cpu.h> +#include <cpu/samsung/exynos5250/dmc.h> +#include <cpu/samsung/exynos5250/s5p-dp.h> +#include <cpu/samsung/s5p-common/clk.h> + +#include "clock_init.h" +#include "setup.h" + +//DECLARE_GLOBAL_DATA_PTR; + +struct arm_clk_ratios arm_clk_ratios[] = { + { + .arm_freq_mhz = 600, + + .apll_mdiv = 0xc8, + .apll_pdiv = 0x4, + .apll_sdiv = 0x1, + + .arm2_ratio = 0x0, + .apll_ratio = 0x1, + .pclk_dbg_ratio = 0x1, + .atb_ratio = 0x2, + .periph_ratio = 0x7, + .acp_ratio = 0x7, + .cpud_ratio = 0x1, + .arm_ratio = 0x0, + }, { + .arm_freq_mhz = 800, + + .apll_mdiv = 0x64, + .apll_pdiv = 0x3, + .apll_sdiv = 0x0, + + .arm2_ratio = 0x0, + .apll_ratio = 0x1, + .pclk_dbg_ratio = 0x1, + .atb_ratio = 0x3, + .periph_ratio = 0x7, + .acp_ratio = 0x7, + .cpud_ratio = 0x2, + .arm_ratio = 0x0, + }, { + .arm_freq_mhz = 1000, + + .apll_mdiv = 0x7d, + .apll_pdiv = 0x3, + .apll_sdiv = 0x0, + + .arm2_ratio = 0x0, + .apll_ratio = 0x1, + .pclk_dbg_ratio = 0x1, + .atb_ratio = 0x4, + .periph_ratio = 0x7, + .acp_ratio = 0x7, + .cpud_ratio = 0x2, + .arm_ratio = 0x0, + }, { + .arm_freq_mhz = 1200, + + .apll_mdiv = 0x96, + .apll_pdiv = 0x3, + .apll_sdiv = 0x0, + + .arm2_ratio = 0x0, + .apll_ratio = 0x3, + .pclk_dbg_ratio = 0x1, + .atb_ratio = 0x5, + .periph_ratio = 0x7, + .acp_ratio = 0x7, + .cpud_ratio = 0x3, + .arm_ratio = 0x0, + }, { + .arm_freq_mhz = 1400, + + .apll_mdiv = 0xaf, + .apll_pdiv = 0x3, + .apll_sdiv = 0x0, + + .arm2_ratio = 0x0, + .apll_ratio = 0x3, + .pclk_dbg_ratio = 0x1, + .atb_ratio = 0x6, + .periph_ratio = 0x7, + .acp_ratio = 0x7, + .cpud_ratio = 0x3, + .arm_ratio = 0x0, + }, { + .arm_freq_mhz = 1700, + + .apll_mdiv = 0x1a9, + .apll_pdiv = 0x6, + .apll_sdiv = 0x0, + + .arm2_ratio = 0x0, + .apll_ratio = 0x3, + .pclk_dbg_ratio = 0x1, + .atb_ratio = 0x6, + .periph_ratio = 0x7, + .acp_ratio = 0x7, + .cpud_ratio = 0x3, + .arm_ratio = 0x0, + } +}; + +struct mem_timings mem_timings[] = { + { + .mem_manuf = MEM_MANUF_ELPIDA, + .mem_type = DDR_MODE_DDR3, + .frequency_mhz = 800, + .mpll_mdiv = 0x64, + .mpll_pdiv = 0x3, + .mpll_sdiv = 0x0, + .cpll_mdiv = 0xde, + .cpll_pdiv = 0x4, + .cpll_sdiv = 0x2, + .gpll_mdiv = 0x215, + .gpll_pdiv = 0xc, + .gpll_sdiv = 0x1, + .epll_mdiv = 0x60, + .epll_pdiv = 0x3, + .epll_sdiv = 0x3, + .vpll_mdiv = 0x96, + .vpll_pdiv = 0x3, + .vpll_sdiv = 0x2, + + .bpll_mdiv = 0x64, + .bpll_pdiv = 0x3, + .bpll_sdiv = 0x0, + .use_bpll = 0, + .pclk_cdrex_ratio = 0x5, + .direct_cmd_msr = { + 0x00020018, 0x00030000, 0x00010042, 0x00000d70 + }, + .timing_ref = 0x000000bb, + .timing_row = 0x8c36660f, + .timing_data = 0x3630580b, + .timing_power = 0x41000a44, + .phy0_dqs = 0x08080808, + .phy1_dqs = 0x08080808, + .phy0_dq = 0x08080808, + .phy1_dq = 0x08080808, + .phy0_tFS = 0x4, + .phy1_tFS = 0x4, + .phy0_pulld_dqs = 0xf, + .phy1_pulld_dqs = 0xf, + + .lpddr3_ctrl_phy_reset = 0x1, + .ctrl_start_point = 0x10, + .ctrl_inc = 0x10, + .ctrl_start = 0x1, + .ctrl_dll_on = 0x1, + .ctrl_ref = 0x8, + + .ctrl_force = 0x1a, + .ctrl_rdlat = 0x0b, + .ctrl_bstlen = 0x08, + + .fp_resync = 0x8, + .iv_size = 0x7, + .dfi_init_start = 1, + .aref_en = 1, + + .rd_fetch = 0x3, + + .zq_mode_dds = 0x7, + .zq_mode_term = 0x1, + .zq_mode_noterm = 0, + + /* + * Dynamic Clock: Always Running + * Memory Burst length: 8 + * Number of chips: 1 + * Memory Bus width: 32 bit + * Memory Type: DDR3 + * Additional Latancy for PLL: 0 Cycle + */ + .memcontrol = DMC_MEMCONTROL_CLK_STOP_DISABLE | + DMC_MEMCONTROL_DPWRDN_DISABLE | + DMC_MEMCONTROL_DPWRDN_ACTIVE_PRECHARGE | + DMC_MEMCONTROL_TP_DISABLE | + DMC_MEMCONTROL_DSREF_ENABLE | + DMC_MEMCONTROL_ADD_LAT_PALL_CYCLE(0) | + DMC_MEMCONTROL_MEM_TYPE_DDR3 | + DMC_MEMCONTROL_MEM_WIDTH_32BIT | + DMC_MEMCONTROL_NUM_CHIP_1 | + DMC_MEMCONTROL_BL_8 | + DMC_MEMCONTROL_PZQ_DISABLE | + DMC_MEMCONTROL_MRR_BYTE_7_0, + .memconfig = DMC_MEMCONFIGx_CHIP_MAP_INTERLEAVED | + DMC_MEMCONFIGx_CHIP_COL_10 | + DMC_MEMCONFIGx_CHIP_ROW_15 | + DMC_MEMCONFIGx_CHIP_BANK_8, + .membaseconfig0 = DMC_MEMBASECONFIG_VAL(0x40), + .membaseconfig1 = DMC_MEMBASECONFIG_VAL(0x80), + .prechconfig_tp_cnt = 0xff, + .dpwrdn_cyc = 0xff, + .dsref_cyc = 0xffff, + .concontrol = DMC_CONCONTROL_DFI_INIT_START_DISABLE | + DMC_CONCONTROL_TIMEOUT_LEVEL0 | + DMC_CONCONTROL_RD_FETCH_DISABLE | + DMC_CONCONTROL_EMPTY_DISABLE | + DMC_CONCONTROL_AREF_EN_DISABLE | + DMC_CONCONTROL_IO_PD_CON_DISABLE, + .dmc_channels = 2, + .chips_per_channel = 2, + .chips_to_configure = 1, + .send_zq_init = 1, + .impedance = IMP_OUTPUT_DRV_30_OHM, + .gate_leveling_enable = 0, + }, { + .mem_manuf = MEM_MANUF_SAMSUNG, + .mem_type = DDR_MODE_DDR3, + .frequency_mhz = 800, + .mpll_mdiv = 0x64, + .mpll_pdiv = 0x3, + .mpll_sdiv = 0x0, + .cpll_mdiv = 0xde, + .cpll_pdiv = 0x4, + .cpll_sdiv = 0x2, + .gpll_mdiv = 0x215, + .gpll_pdiv = 0xc, + .gpll_sdiv = 0x1, + .epll_mdiv = 0x60, + .epll_pdiv = 0x3, + .epll_sdiv = 0x3, + .vpll_mdiv = 0x96, + .vpll_pdiv = 0x3, + .vpll_sdiv = 0x2, + + .bpll_mdiv = 0x64, + .bpll_pdiv = 0x3, + .bpll_sdiv = 0x0, + .use_bpll = 0, + .pclk_cdrex_ratio = 0x5, + .direct_cmd_msr = { + 0x00020018, 0x00030000, 0x00010000, 0x00000d70 + }, + .timing_ref = 0x000000bb, + .timing_row = 0x8c36660f, + .timing_data = 0x3630580b, + .timing_power = 0x41000a44, + .phy0_dqs = 0x08080808, + .phy1_dqs = 0x08080808, + .phy0_dq = 0x08080808, + .phy1_dq = 0x08080808, + .phy0_tFS = 0x8, + .phy1_tFS = 0x8, + .phy0_pulld_dqs = 0xf, + .phy1_pulld_dqs = 0xf, + + .lpddr3_ctrl_phy_reset = 0x1, + .ctrl_start_point = 0x10, + .ctrl_inc = 0x10, + .ctrl_start = 0x1, + .ctrl_dll_on = 0x1, + .ctrl_ref = 0x8, + + .ctrl_force = 0x1a, + .ctrl_rdlat = 0x0b, + .ctrl_bstlen = 0x08, + + .fp_resync = 0x8, + .iv_size = 0x7, + .dfi_init_start = 1, + .aref_en = 1, + + .rd_fetch = 0x3, + + .zq_mode_dds = 0x5, + .zq_mode_term = 0x1, + .zq_mode_noterm = 1, + + /* + * Dynamic Clock: Always Running + * Memory Burst length: 8 + * Number of chips: 1 + * Memory Bus width: 32 bit + * Memory Type: DDR3 + * Additional Latancy for PLL: 0 Cycle + */ + .memcontrol = DMC_MEMCONTROL_CLK_STOP_DISABLE | + DMC_MEMCONTROL_DPWRDN_DISABLE | + DMC_MEMCONTROL_DPWRDN_ACTIVE_PRECHARGE | + DMC_MEMCONTROL_TP_DISABLE | + DMC_MEMCONTROL_DSREF_ENABLE | + DMC_MEMCONTROL_ADD_LAT_PALL_CYCLE(0) | + DMC_MEMCONTROL_MEM_TYPE_DDR3 | + DMC_MEMCONTROL_MEM_WIDTH_32BIT | + DMC_MEMCONTROL_NUM_CHIP_1 | + DMC_MEMCONTROL_BL_8 | + DMC_MEMCONTROL_PZQ_DISABLE | + DMC_MEMCONTROL_MRR_BYTE_7_0, + .memconfig = DMC_MEMCONFIGx_CHIP_MAP_INTERLEAVED | + DMC_MEMCONFIGx_CHIP_COL_10 | + DMC_MEMCONFIGx_CHIP_ROW_15 | + DMC_MEMCONFIGx_CHIP_BANK_8, + .membaseconfig0 = DMC_MEMBASECONFIG_VAL(0x40), + .membaseconfig1 = DMC_MEMBASECONFIG_VAL(0x80), + .prechconfig_tp_cnt = 0xff, + .dpwrdn_cyc = 0xff, + .dsref_cyc = 0xffff, + .concontrol = DMC_CONCONTROL_DFI_INIT_START_DISABLE | + DMC_CONCONTROL_TIMEOUT_LEVEL0 | + DMC_CONCONTROL_RD_FETCH_DISABLE | + DMC_CONCONTROL_EMPTY_DISABLE | + DMC_CONCONTROL_AREF_EN_DISABLE | + DMC_CONCONTROL_IO_PD_CON_DISABLE, + .dmc_channels = 2, + .chips_per_channel = 2, + .chips_to_configure = 1, + .send_zq_init = 1, + .impedance = IMP_OUTPUT_DRV_40_OHM, + .gate_leveling_enable = 1, + }, + { + .mem_manuf = MEM_MANUF_ELPIDA, + .mem_type = DDR_MODE_DDR3, + .frequency_mhz = 780, + .mpll_mdiv = 0x64, + .mpll_pdiv = 0x3, + .mpll_sdiv = 0x0, + .cpll_mdiv = 0xde, + .cpll_pdiv = 0x4, + .cpll_sdiv = 0x2, + .gpll_mdiv = 0x215, + .gpll_pdiv = 0xc, + .gpll_sdiv = 0x1, + .epll_mdiv = 0x60, + .epll_pdiv = 0x3, + .epll_sdiv = 0x3, + .vpll_mdiv = 0x96, + .vpll_pdiv = 0x3, + .vpll_sdiv = 0x2, + + .bpll_mdiv = 0x82, + .bpll_pdiv = 0x4, + .bpll_sdiv = 0x0, + .use_bpll = 1, + .pclk_cdrex_ratio = 0x5, + .direct_cmd_msr = { + 0x00020018, 0x00030000, 0x00010042, 0x00000d70 + }, + .timing_ref = 0x000000bb, + .timing_row = 0x8c36660f, + .timing_data = 0x3630580b, + .timing_power = 0x41000a44, + .phy0_dqs = 0x08080808, + .phy1_dqs = 0x08080808, + .phy0_dq = 0x08080808, + .phy1_dq = 0x08080808, + .phy0_tFS = 0x4, + .phy1_tFS = 0x4, + .phy0_pulld_dqs = 0xf, + .phy1_pulld_dqs = 0xf, + + .lpddr3_ctrl_phy_reset = 0x1, + .ctrl_start_point = 0x10, + .ctrl_inc = 0x10, + .ctrl_start = 0x1, + .ctrl_dll_on = 0x1, + .ctrl_ref = 0x8, + + .ctrl_force = 0x1a, + .ctrl_rdlat = 0x0b, + .ctrl_bstlen = 0x08, + + .fp_resync = 0x8, + .iv_size = 0x7, + .dfi_init_start = 1, + .aref_en = 1, + + .rd_fetch = 0x3, + + .zq_mode_dds = 0x7, + .zq_mode_term = 0x1, + .zq_mode_noterm = 0, + + /* + * Dynamic Clock: Always Running + * Memory Burst length: 8 + * Number of chips: 1 + * Memory Bus width: 32 bit + * Memory Type: DDR3 + * Additional Latancy for PLL: 0 Cycle + */ + .memcontrol = DMC_MEMCONTROL_CLK_STOP_DISABLE | + DMC_MEMCONTROL_DPWRDN_DISABLE | + DMC_MEMCONTROL_DPWRDN_ACTIVE_PRECHARGE | + DMC_MEMCONTROL_TP_DISABLE | + DMC_MEMCONTROL_DSREF_ENABLE | + DMC_MEMCONTROL_ADD_LAT_PALL_CYCLE(0) | + DMC_MEMCONTROL_MEM_TYPE_DDR3 | + DMC_MEMCONTROL_MEM_WIDTH_32BIT | + DMC_MEMCONTROL_NUM_CHIP_1 | + DMC_MEMCONTROL_BL_8 | + DMC_MEMCONTROL_PZQ_DISABLE | + DMC_MEMCONTROL_MRR_BYTE_7_0, + .memconfig = DMC_MEMCONFIGx_CHIP_MAP_INTERLEAVED | + DMC_MEMCONFIGx_CHIP_COL_10 | + DMC_MEMCONFIGx_CHIP_ROW_15 | + DMC_MEMCONFIGx_CHIP_BANK_8, + .membaseconfig0 = DMC_MEMBASECONFIG_VAL(0x40), + .membaseconfig1 = DMC_MEMBASECONFIG_VAL(0x80), + .prechconfig_tp_cnt = 0xff, + .dpwrdn_cyc = 0xff, + .dsref_cyc = 0xffff, + .concontrol = DMC_CONCONTROL_DFI_INIT_START_DISABLE | + DMC_CONCONTROL_TIMEOUT_LEVEL0 | + DMC_CONCONTROL_RD_FETCH_DISABLE | + DMC_CONCONTROL_EMPTY_DISABLE | + DMC_CONCONTROL_AREF_EN_DISABLE | + DMC_CONCONTROL_IO_PD_CON_DISABLE, + .dmc_channels = 2, + .chips_per_channel = 2, + .chips_to_configure = 1, + .send_zq_init = 1, + .impedance = IMP_OUTPUT_DRV_30_OHM, + .gate_leveling_enable = 0, + }, { + .mem_manuf = MEM_MANUF_SAMSUNG, + .mem_type = DDR_MODE_DDR3, + .frequency_mhz = 780, + .mpll_mdiv = 0x64, + .mpll_pdiv = 0x3, + .mpll_sdiv = 0x0, + .cpll_mdiv = 0xde, + .cpll_pdiv = 0x4, + .cpll_sdiv = 0x2, + .gpll_mdiv = 0x215, + .gpll_pdiv = 0xc, + .gpll_sdiv = 0x1, + .epll_mdiv = 0x60, + .epll_pdiv = 0x3, + .epll_sdiv = 0x3, + .vpll_mdiv = 0x96, + .vpll_pdiv = 0x3, + .vpll_sdiv = 0x2, + + .bpll_mdiv = 0x82, + .bpll_pdiv = 0x4, + .bpll_sdiv = 0x0, + .use_bpll = 1, + .pclk_cdrex_ratio = 0x5, + .direct_cmd_msr = { + 0x00020018, 0x00030000, 0x00010000, 0x00000d70 + }, + .timing_ref = 0x000000bb, + .timing_row = 0x8c36660f, + .timing_data = 0x3630580b, + .timing_power = 0x41000a44, + .phy0_dqs = 0x08080808, + .phy1_dqs = 0x08080808, + .phy0_dq = 0x08080808, + .phy1_dq = 0x08080808, + .phy0_tFS = 0x8, + .phy1_tFS = 0x8, + .phy0_pulld_dqs = 0xf, + .phy1_pulld_dqs = 0xf, + + .lpddr3_ctrl_phy_reset = 0x1, + .ctrl_start_point = 0x10, + .ctrl_inc = 0x10, + .ctrl_start = 0x1, + .ctrl_dll_on = 0x1, + .ctrl_ref = 0x8, + + .ctrl_force = 0x1a, + .ctrl_rdlat = 0x0b, + .ctrl_bstlen = 0x08, + + .fp_resync = 0x8, + .iv_size = 0x7, + .dfi_init_start = 1, + .aref_en = 1, + + .rd_fetch = 0x3, + + .zq_mode_dds = 0x5, + .zq_mode_term = 0x1, + .zq_mode_noterm = 1, + + /* + * Dynamic Clock: Always Running + * Memory Burst length: 8 + * Number of chips: 1 + * Memory Bus width: 32 bit + * Memory Type: DDR3 + * Additional Latancy for PLL: 0 Cycle + */ + .memcontrol = DMC_MEMCONTROL_CLK_STOP_DISABLE | + DMC_MEMCONTROL_DPWRDN_DISABLE | + DMC_MEMCONTROL_DPWRDN_ACTIVE_PRECHARGE | + DMC_MEMCONTROL_TP_DISABLE | + DMC_MEMCONTROL_DSREF_ENABLE | + DMC_MEMCONTROL_ADD_LAT_PALL_CYCLE(0) | + DMC_MEMCONTROL_MEM_TYPE_DDR3 | + DMC_MEMCONTROL_MEM_WIDTH_32BIT | + DMC_MEMCONTROL_NUM_CHIP_1 | + DMC_MEMCONTROL_BL_8 | + DMC_MEMCONTROL_PZQ_DISABLE | + DMC_MEMCONTROL_MRR_BYTE_7_0, + .memconfig = DMC_MEMCONFIGx_CHIP_MAP_INTERLEAVED | + DMC_MEMCONFIGx_CHIP_COL_10 | + DMC_MEMCONFIGx_CHIP_ROW_15 | + DMC_MEMCONFIGx_CHIP_BANK_8, + .membaseconfig0 = DMC_MEMBASECONFIG_VAL(0x40), + .membaseconfig1 = DMC_MEMBASECONFIG_VAL(0x80), + .prechconfig_tp_cnt = 0xff, + .dpwrdn_cyc = 0xff, + .dsref_cyc = 0xffff, + .concontrol = DMC_CONCONTROL_DFI_INIT_START_DISABLE | + DMC_CONCONTROL_TIMEOUT_LEVEL0 | + DMC_CONCONTROL_RD_FETCH_DISABLE | + DMC_CONCONTROL_EMPTY_DISABLE | + DMC_CONCONTROL_AREF_EN_DISABLE | + DMC_CONCONTROL_IO_PD_CON_DISABLE, + .dmc_channels = 2, + .chips_per_channel = 2, + .chips_to_configure = 1, + .send_zq_init = 1, + .impedance = IMP_OUTPUT_DRV_40_OHM, + .gate_leveling_enable = 1, + } +}; + +/** + * Detect what memory is present based on board strappings + * + * Boards have various resistor stuff options that are supposed to match + * which SDRAM is present (and which revision of the board this is). This + * uses the resistor stuff options to figure out what memory manufacturer + * to use for matching in the memory tables. + * + * @return A MEM_MANUF_XXX constant, or -1 if an error occurred. + */ +/* + * FIXME(dhendrix): This unwinds into a mess of board-specific code. The + * board's romstage.c file should detect the memory type and pass in + * appropriate parameters to whatever calls this. + */ +#define BOARD_REV_ELPIDA_MEMORY 3 +#define BOARD_REV_SAMSUNG_MEMORY 4 +static int autodetect_memory(void) +{ +// int board_rev = board_get_revision(); + int board_rev = BOARD_REV_ELPIDA_MEMORY; + + if (board_rev == -1) + return -1; + + switch (board_rev) { + case BOARD_REV_SAMSUNG_MEMORY: + return MEM_MANUF_SAMSUNG; + case BOARD_REV_ELPIDA_MEMORY: + return MEM_MANUF_ELPIDA; + } + + return -1; +} + +#ifdef CONFIG_SPL_BUILD + +/** + * Get the required memory type and speed (SPL version). + * + * In SPL we have no device tree, so we use the machine parameters + */ +int clock_get_mem_selection(enum ddr_mode *mem_type, + unsigned *frequency_mhz, unsigned *arm_freq, + enum mem_manuf *mem_manuf) +{ + struct spl_machine_param *params; + + params = spl_get_machine_params(); + *mem_type = params->mem_type; + *frequency_mhz = params->frequency_mhz; + *arm_freq = params->arm_freq_mhz; + if (params->mem_manuf == MEM_MANUF_AUTODETECT) { + *mem_manuf = autodetect_memory(); + if (*mem_manuf == -1) + return -1; + } else { + *mem_manuf = params->mem_manuf; + } + + return 0; +} + +#else + +static const char *mem_types[DDR_MODE_COUNT] = { + "DDR2", "DDR3", "LPDDR2", "LPDDR3" +}; + +static const char *mem_manufs[MEM_MANUF_COUNT] = { + "autodetect", "Elpida", "Samsung" +}; + +int clock_get_mem_selection(enum ddr_mode *mem_type, + unsigned *frequency_mhz, unsigned *arm_freq, + enum mem_manuf *mem_manuf) +{ + const char *typestr; + int node, i; + + node = fdtdec_next_compatible(gd->fdt_blob, 0, + COMPAT_SAMSUNG_EXYNOS_DMC); + if (node < 0) + die("No memory information available in device tree"); + typestr = fdt_getprop(gd->fdt_blob, node, "mem-type", NULL); + for (i = 0; i < DDR_MODE_COUNT; i++) { + if (!stricmp(typestr, mem_types[i])) + break; + } + if (i == DDR_MODE_COUNT) + die("Invalid memory type in device tree"); + *mem_type = i; + + typestr = fdt_getprop(gd->fdt_blob, node, "mem-manuf", NULL); + for (i = 0; i < MEM_MANUF_COUNT; i++) { + if (!stricmp(typestr, mem_manufs[i])) + break; + } + if (i == MEM_MANUF_COUNT) + die("Invalid memory manufacturer in device tree"); + + if (i == MEM_MANUF_AUTODETECT) { + *mem_manuf = autodetect_memory(); + if (*mem_manuf == -1) + return -1; + } else { + *mem_manuf = i; + } + + *frequency_mhz = fdtdec_get_int(gd->fdt_blob, node, "clock-frequency", + 0); + if (!*frequency_mhz) + die("Invalid memory frequency in device tree"); + + *arm_freq = fdtdec_get_int(gd->fdt_blob, node, "arm-frequency", 0); + /* TODO: Remove all these panics/dies, and just return an error code */ + if (!*arm_freq) + die("Invalid ARM frequency in device tree"); + + return 0; +} + +const char *clock_get_mem_type_name(enum ddr_mode mem_type) +{ + if (mem_type >= 0 && mem_type < DDR_MODE_COUNT) + return mem_types[mem_type]; + + return "<unknown>"; +} + +const char *clock_get_mem_manuf_name(enum mem_manuf mem_manuf) +{ + if (mem_manuf >= 0 && mem_manuf < MEM_MANUF_COUNT) + return mem_manufs[mem_manuf]; + + return "<unknown>"; +} +#endif + +/* Get the ratios for setting ARM clock */ +struct arm_clk_ratios *get_arm_ratios(void); /* FIXME: silence compiler... */ +struct arm_clk_ratios *get_arm_ratios(void) +{ + struct arm_clk_ratios *arm_ratio; + enum ddr_mode mem_type; + enum mem_manuf mem_manuf; + unsigned frequency_mhz, arm_freq; + int i; + + /* TODO(sjg@chromium.org): Return NULL and have caller deal with it */ + if (clock_get_mem_selection(&mem_type, &frequency_mhz, + &arm_freq, &mem_manuf)) + ; + for (i = 0, arm_ratio = arm_clk_ratios; i < ARRAY_SIZE(arm_clk_ratios); + i++, arm_ratio++) { + if (arm_ratio->arm_freq_mhz == arm_freq) + return arm_ratio; + } + + die("get_arm_ratios: Failed to find ratio\n"); + return NULL; +} + +struct mem_timings *clock_get_mem_timings(void) +{ + struct mem_timings *mem; + enum ddr_mode mem_type; + enum mem_manuf mem_manuf; + unsigned frequency_mhz, arm_freq; + int i; + + if (!clock_get_mem_selection(&mem_type, &frequency_mhz, + &arm_freq, &mem_manuf)) { + for (i = 0, mem = mem_timings; i < ARRAY_SIZE(mem_timings); + i++, mem++) { + if (mem->mem_type == mem_type && + mem->frequency_mhz == frequency_mhz && + mem->mem_manuf == mem_manuf) + return mem; + } + } + /* TODO: Call panic() here */ + while (1) + ; + return NULL; +} + +void system_clock_init() +{ + struct exynos5_clock *clk = (struct exynos5_clock *)EXYNOS5_CLOCK_BASE; + struct exynos5_mct_regs *mct_regs = + (struct exynos5_mct_regs *)EXYNOS5_MULTI_CORE_TIMER_BASE; + struct mem_timings *mem; + struct arm_clk_ratios *arm_clk_ratio; + u32 val, tmp; + + /* Turn on the MCT as early as possible. */ + mct_regs->g_tcon |= (1 << 8); + + mem = clock_get_mem_timings(); + arm_clk_ratio = get_arm_ratios(); + + clrbits_le32(&clk->src_cpu, MUX_APLL_SEL_MASK); + do { + val = readl(&clk->mux_stat_cpu); + } while ((val | MUX_APLL_SEL_MASK) != val); + + clrbits_le32(&clk->src_core1, MUX_MPLL_SEL_MASK); + do { + val = readl(&clk->mux_stat_core1); + } while ((val | MUX_MPLL_SEL_MASK) != val); + + clrbits_le32(&clk->src_top2, MUX_CPLL_SEL_MASK); + clrbits_le32(&clk->src_top2, MUX_EPLL_SEL_MASK); + clrbits_le32(&clk->src_top2, MUX_VPLL_SEL_MASK); + clrbits_le32(&clk->src_top2, MUX_GPLL_SEL_MASK); + tmp = MUX_CPLL_SEL_MASK | MUX_EPLL_SEL_MASK | MUX_VPLL_SEL_MASK + | MUX_GPLL_SEL_MASK; + do { + val = readl(&clk->mux_stat_top2); + } while ((val | tmp) != val); + + clrbits_le32(&clk->src_cdrex, MUX_BPLL_SEL_MASK); + do { + val = readl(&clk->mux_stat_cdrex); + } while ((val | MUX_BPLL_SEL_MASK) != val); + + /* PLL locktime */ + writel(APLL_LOCK_VAL, &clk->apll_lock); + + writel(MPLL_LOCK_VAL, &clk->mpll_lock); + + writel(BPLL_LOCK_VAL, &clk->bpll_lock); + + writel(CPLL_LOCK_VAL, &clk->cpll_lock); + + writel(GPLL_LOCK_VAL, &clk->gpll_lock); + + writel(EPLL_LOCK_VAL, &clk->epll_lock); + + writel(VPLL_LOCK_VAL, &clk->vpll_lock); + + writel(CLK_REG_DISABLE, &clk->pll_div2_sel); + + writel(MUX_HPM_SEL_MASK, &clk->src_cpu); + do { + val = readl(&clk->mux_stat_cpu); + } while ((val | HPM_SEL_SCLK_MPLL) != val); + + val = arm_clk_ratio->arm2_ratio << 28 + | arm_clk_ratio->apll_ratio << 24 + | arm_clk_ratio->pclk_dbg_ratio << 20 + | arm_clk_ratio->atb_ratio << 16 + | arm_clk_ratio->periph_ratio << 12 + | arm_clk_ratio->acp_ratio << 8 + | arm_clk_ratio->cpud_ratio << 4 + | arm_clk_ratio->arm_ratio; + writel(val, &clk->div_cpu0); + do { + val = readl(&clk->div_stat_cpu0); + } while (0 != val); + + writel(CLK_DIV_CPU1_VAL, &clk->div_cpu1); + do { + val = readl(&clk->div_stat_cpu1); + } while (0 != val); + + /* Set APLL */ + writel(APLL_CON1_VAL, &clk->apll_con1); + val = set_pll(arm_clk_ratio->apll_mdiv, arm_clk_ratio->apll_pdiv, + arm_clk_ratio->apll_sdiv); + writel(val, &clk->apll_con0); + while ((readl(&clk->apll_con0) & APLL_CON0_LOCKED) == 0) + ; + + /* Set MPLL */ + writel(MPLL_CON1_VAL, &clk->mpll_con1); + val = set_pll(mem->mpll_mdiv, mem->mpll_pdiv, mem->mpll_sdiv); + writel(val, &clk->mpll_con0); + while ((readl(&clk->mpll_con0) & MPLL_CON0_LOCKED) == 0) + ; + + /* + * Configure MUX_MPLL_FOUT to choose the direct clock source + * path and avoid the fixed DIV/2 block to save power + */ + setbits_le32(&clk->pll_div2_sel, MUX_MPLL_FOUT_SEL); + + /* Set BPLL */ + if (mem->use_bpll) { + writel(BPLL_CON1_VAL, &clk->bpll_con1); + val = set_pll(mem->bpll_mdiv, mem->bpll_pdiv, mem->bpll_sdiv); + writel(val, &clk->bpll_con0); + while ((readl(&clk->bpll_con0) & BPLL_CON0_LOCKED) == 0) + ; + + setbits_le32(&clk->pll_div2_sel, MUX_BPLL_FOUT_SEL); + } + + /* Set CPLL */ + writel(CPLL_CON1_VAL, &clk->cpll_con1); + val = set_pll(mem->cpll_mdiv, mem->cpll_pdiv, mem->cpll_sdiv); + writel(val, &clk->cpll_con0); + while ((readl(&clk->cpll_con0) & CPLL_CON0_LOCKED) == 0) + ; + + /* Set GPLL */ + writel(GPLL_CON1_VAL, &clk->gpll_con1); + val = set_pll(mem->gpll_mdiv, mem->gpll_pdiv, mem->gpll_sdiv); + writel(val, &clk->gpll_con0); + while ((readl(&clk->gpll_con0) & GPLL_CON0_LOCKED) == 0) + ; + + /* Set EPLL */ + writel(EPLL_CON2_VAL, &clk->epll_con2); + writel(EPLL_CON1_VAL, &clk->epll_con1); + val = set_pll(mem->epll_mdiv, mem->epll_pdiv, mem->epll_sdiv); + writel(val, &clk->epll_con0); + while ((readl(&clk->epll_con0) & EPLL_CON0_LOCKED) == 0) + ; + + /* Set VPLL */ + writel(VPLL_CON2_VAL, &clk->vpll_con2); + writel(VPLL_CON1_VAL, &clk->vpll_con1); + val = set_pll(mem->vpll_mdiv, mem->vpll_pdiv, mem->vpll_sdiv); + writel(val, &clk->vpll_con0); + while ((readl(&clk->vpll_con0) & VPLL_CON0_LOCKED) == 0) + ; + + writel(CLK_SRC_CORE0_VAL, &clk->src_core0); + writel(CLK_DIV_CORE0_VAL, &clk->div_core0); + while (readl(&clk->div_stat_core0) != 0) + ; + + writel(CLK_DIV_CORE1_VAL, &clk->div_core1); + while (readl(&clk->div_stat_core1) != 0) + ; + + writel(CLK_DIV_SYSRGT_VAL, &clk->div_sysrgt); + while (readl(&clk->div_stat_sysrgt) != 0) + ; + + writel(CLK_DIV_ACP_VAL, &clk->div_acp); + while (readl(&clk->div_stat_acp) != 0) + ; + + writel(CLK_DIV_SYSLFT_VAL, &clk->div_syslft); + while (readl(&clk->div_stat_syslft) != 0) + ; + + writel(CLK_SRC_TOP0_VAL, &clk->src_top0); + writel(CLK_SRC_TOP1_VAL, &clk->src_top1); + writel(TOP2_VAL, &clk->src_top2); + writel(CLK_SRC_TOP3_VAL, &clk->src_top3); + + writel(CLK_DIV_TOP0_VAL, &clk->div_top0); + while (readl(&clk->div_stat_top0)) + ; + + writel(CLK_DIV_TOP1_VAL, &clk->div_top1); + while (readl(&clk->div_stat_top1)) + ; + + writel(CLK_SRC_LEX_VAL, &clk->src_lex); + while (1) { + val = readl(&clk->mux_stat_lex); + if (val == (val | 1)) + break; + } + + writel(CLK_DIV_LEX_VAL, &clk->div_lex); + while (readl(&clk->div_stat_lex)) + ; + + writel(CLK_DIV_R0X_VAL, &clk->div_r0x); + while (readl(&clk->div_stat_r0x)) + ; + + writel(CLK_DIV_R0X_VAL, &clk->div_r0x); + while (readl(&clk->div_stat_r0x)) + ; + + writel(CLK_DIV_R1X_VAL, &clk->div_r1x); + while (readl(&clk->div_stat_r1x)) + ; + + if (mem->use_bpll) { + writel(MUX_BPLL_SEL_MASK | MUX_MCLK_CDREX_SEL | + MUX_MCLK_DPHY_SEL, &clk->src_cdrex); + } else { + writel(CLK_REG_DISABLE, &clk->src_cdrex); + } + + writel(CLK_DIV_CDREX_VAL, &clk->div_cdrex); + while (readl(&clk->div_stat_cdrex)) + ; + + val = readl(&clk->src_cpu); + val |= CLK_SRC_CPU_VAL; + writel(val, &clk->src_cpu); + + val = readl(&clk->src_top2); + val |= CLK_SRC_TOP2_VAL; + writel(val, &clk->src_top2); + + val = readl(&clk->src_core1); + val |= CLK_SRC_CORE1_VAL; + writel(val, &clk->src_core1); + + writel(CLK_SRC_FSYS0_VAL, &clk->src_fsys); + writel(CLK_DIV_FSYS0_VAL, &clk->div_fsys0); + while (readl(&clk->div_stat_fsys0)) + ; + + writel(CLK_REG_DISABLE, &clk->clkout_cmu_cpu); + writel(CLK_REG_DISABLE, &clk->clkout_cmu_core); + writel(CLK_REG_DISABLE, &clk->clkout_cmu_acp); + writel(CLK_REG_DISABLE, &clk->clkout_cmu_top); + writel(CLK_REG_DISABLE, &clk->clkout_cmu_lex); + writel(CLK_REG_DISABLE, &clk->clkout_cmu_r0x); + writel(CLK_REG_DISABLE, &clk->clkout_cmu_r1x); + writel(CLK_REG_DISABLE, &clk->clkout_cmu_cdrex); + + writel(CLK_SRC_PERIC0_VAL, &clk->src_peric0); + writel(CLK_DIV_PERIC0_VAL, &clk->div_peric0); + + writel(CLK_SRC_PERIC1_VAL, &clk->src_peric1); + writel(CLK_DIV_PERIC1_VAL, &clk->div_peric1); + writel(CLK_DIV_PERIC2_VAL, &clk->div_peric2); + writel(SCLK_SRC_ISP_VAL, &clk->sclk_src_isp); + writel(SCLK_DIV_ISP_VAL, &clk->sclk_div_isp); + writel(CLK_DIV_ISP0_VAL, &clk->div_isp0); + writel(CLK_DIV_ISP1_VAL, &clk->div_isp1); + writel(CLK_DIV_ISP2_VAL, &clk->div_isp2); + + /* FIMD1 SRC CLK SELECTION */ + writel(CLK_SRC_DISP1_0_VAL, &clk->src_disp1_0); + + val = MMC2_PRE_RATIO_VAL << MMC2_PRE_RATIO_OFFSET + | MMC2_RATIO_VAL << MMC2_RATIO_OFFSET + | MMC3_PRE_RATIO_VAL << MMC3_PRE_RATIO_OFFSET + | MMC3_RATIO_VAL << MMC3_RATIO_OFFSET; + writel(val, &clk->div_fsys2); +} + +void clock_gate(void) +{ + struct exynos5_clock *clk = (struct exynos5_clock *)EXYNOS5_CLOCK_BASE; + + /* CLK_GATE_IP_SYSRGT */ + clrbits_le32(&clk->gate_ip_sysrgt, CLK_C2C_MASK); + + /* CLK_GATE_IP_ACP */ + clrbits_le32(&clk->gate_ip_acp, CLK_SMMUG2D_MASK | + CLK_SMMUSSS_MASK | + CLK_SMMUMDMA_MASK | + CLK_ID_REMAPPER_MASK | + CLK_G2D_MASK | + CLK_SSS_MASK | + CLK_MDMA_MASK | + CLK_SECJTAG_MASK); + + /* CLK_GATE_BUS_SYSLFT */ + clrbits_le32(&clk->gate_bus_syslft, CLK_EFCLK_MASK); + + /* CLK_GATE_IP_ISP0 */ + clrbits_le32(&clk->gate_ip_isp0, CLK_UART_ISP_MASK | + CLK_WDT_ISP_MASK | + CLK_PWM_ISP_MASK | + CLK_MTCADC_ISP_MASK | + CLK_I2C1_ISP_MASK | + CLK_I2C0_ISP_MASK | + CLK_MPWM_ISP_MASK | + CLK_MCUCTL_ISP_MASK | + CLK_INT_COMB_ISP_MASK | + CLK_SMMU_MCUISP_MASK | + CLK_SMMU_SCALERP_MASK | + CLK_SMMU_SCALERC_MASK | + CLK_SMMU_FD_MASK | + CLK_SMMU_DRC_MASK | + CLK_SMMU_ISP_MASK | + CLK_GICISP_MASK | + CLK_ARM9S_MASK | + CLK_MCUISP_MASK | + CLK_SCALERP_MASK | + CLK_SCALERC_MASK | + CLK_FD_MASK | + CLK_DRC_MASK | + CLK_ISP_MASK); + + /* CLK_GATE_IP_ISP1 */ + clrbits_le32(&clk->gate_ip_isp1, CLK_SPI1_ISP_MASK | + CLK_SPI0_ISP_MASK | + CLK_SMMU3DNR_MASK | + CLK_SMMUDIS1_MASK | + CLK_SMMUDIS0_MASK | + CLK_SMMUODC_MASK | + CLK_3DNR_MASK | + CLK_DIS_MASK | + CLK_ODC_MASK); + + /* CLK_GATE_SCLK_ISP */ + clrbits_le32(&clk->gate_sclk_isp, SCLK_MPWM_ISP_MASK); + + /* CLK_GATE_IP_GSCL */ + clrbits_le32(&clk->gate_ip_gscl, CLK_SMMUFIMC_LITE2_MASK | + CLK_SMMUFIMC_LITE1_MASK | + CLK_SMMUFIMC_LITE0_MASK | + CLK_SMMUGSCL3_MASK | + CLK_SMMUGSCL2_MASK | + CLK_SMMUGSCL1_MASK | + CLK_SMMUGSCL0_MASK | + CLK_GSCL_WRAP_B_MASK | + CLK_GSCL_WRAP_A_MASK | + CLK_CAMIF_TOP_MASK | + CLK_GSCL3_MASK | + CLK_GSCL2_MASK | + CLK_GSCL1_MASK | + CLK_GSCL0_MASK); + + /* CLK_GATE_IP_DISP1 */ + clrbits_le32(&clk->gate_ip_disp1, CLK_SMMUTVX_MASK | + CLK_ASYNCTVX_MASK | + CLK_HDMI_MASK | + CLK_MIXER_MASK | + CLK_DSIM1_MASK); + + /* CLK_GATE_IP_MFC */ + clrbits_le32(&clk->gate_ip_mfc, CLK_SMMUMFCR_MASK | + CLK_SMMUMFCL_MASK | + CLK_MFC_MASK); + + /* CLK_GATE_IP_GEN */ + clrbits_le32(&clk->gate_ip_gen, CLK_SMMUMDMA1_MASK | + CLK_SMMUJPEG_MASK | + CLK_SMMUROTATOR_MASK | + CLK_MDMA1_MASK | + CLK_JPEG_MASK | + CLK_ROTATOR_MASK); + + /* CLK_GATE_IP_FSYS */ + clrbits_le32(&clk->gate_ip_fsys, CLK_WDT_IOP_MASK | + CLK_SMMUMCU_IOP_MASK | + CLK_SATA_PHY_I2C_MASK | + CLK_SATA_PHY_CTRL_MASK | + CLK_MCUCTL_MASK | + CLK_NFCON_MASK | + CLK_SMMURTIC_MASK | + CLK_RTIC_MASK | + CLK_MIPI_HSI_MASK | + CLK_USBOTG_MASK | + CLK_SATA_MASK | + CLK_PDMA1_MASK | + CLK_PDMA0_MASK | + CLK_MCU_IOP_MASK); + + /* CLK_GATE_IP_PERIC */ + clrbits_le32(&clk->gate_ip_peric, CLK_HS_I2C3_MASK | + CLK_HS_I2C2_MASK | + CLK_HS_I2C1_MASK | + CLK_HS_I2C0_MASK | + CLK_AC97_MASK | + CLK_SPDIF_MASK | + CLK_PCM2_MASK | + CLK_PCM1_MASK | + CLK_I2S2_MASK | + CLK_SPI2_MASK | + CLK_SPI0_MASK); + + /* CLK_GATE_IP_PERIS */ + clrbits_le32(&clk->gate_ip_peris, CLK_RTC_MASK | + CLK_TZPC9_MASK | + CLK_TZPC8_MASK | + CLK_TZPC7_MASK | + CLK_TZPC6_MASK | + CLK_TZPC5_MASK | + CLK_TZPC4_MASK | + CLK_TZPC3_MASK | + CLK_TZPC2_MASK | + CLK_TZPC1_MASK | + CLK_TZPC0_MASK | + CLK_CHIPID_MASK); + + /* CLK_GATE_BLOCK */ + clrbits_le32(&clk->gate_block, CLK_ACP_MASK); + + /* CLK_GATE_IP_CDREX */ + clrbits_le32(&clk->gate_ip_cdrex, CLK_DPHY0_MASK | + CLK_DPHY1_MASK | + CLK_TZASC_DRBXR_MASK); + +} + +void clock_init_dp_clock(void) +{ + struct exynos5_clock *clk = (struct exynos5_clock *)EXYNOS5_CLOCK_BASE; + + /* DP clock enable */ + setbits_le32(&clk->gate_ip_disp1, CLK_GATE_DP1_ALLOW); + + /* We run DP at 267 Mhz */ + setbits_le32(&clk->div_disp1_0, CLK_DIV_DISP1_0_FIMD1); +} + +#ifdef CONFIG_SPL_BUILD +/* + * This is a custom implementation for the udelay(), as we do not the timer + * initialise during the SPL boot. We are assuming the cpu takes 3 instruction + * pre cycle. This is based on the implementation of sdelay() function. + */ +void udelay(unsigned usec) +{ + unsigned long count; + + /* TODO(alim.akhtar@samsung.com): Comment on why divided by 30000000 */ + count = usec * (get_pll_clk(APLL) / (3 * 10000000)); + sdelay(count); +} +#endif diff --git a/src/cpu/samsung/exynos5250/dmc_common.c b/src/cpu/samsung/exynos5250/dmc_common.c new file mode 100644 index 0000000000..35a13acc58 --- /dev/null +++ b/src/cpu/samsung/exynos5250/dmc_common.c @@ -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"); + } +} diff --git a/src/cpu/samsung/exynos5250/dmc_init_ddr3.c b/src/cpu/samsung/exynos5250/dmc_init_ddr3.c new file mode 100644 index 0000000000..6ef10bfc91 --- /dev/null +++ b/src/cpu/samsung/exynos5250/dmc_init_ddr3.c @@ -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; +} diff --git a/src/cpu/samsung/exynos5250/exynos_cache.c b/src/cpu/samsung/exynos5250/exynos_cache.c new file mode 100644 index 0000000000..ec858d1bc3 --- /dev/null +++ b/src/cpu/samsung/exynos5250/exynos_cache.c @@ -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) +{ +} diff --git a/src/cpu/samsung/exynos5250/lowlevel_init.S b/src/cpu/samsung/exynos5250/lowlevel_init.S new file mode 100644 index 0000000000..883db9dd7c --- /dev/null +++ b/src/cpu/samsung/exynos5250/lowlevel_init.S @@ -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 diff --git a/src/cpu/samsung/exynos5250/lowlevel_init_c.c b/src/cpu/samsung/exynos5250/lowlevel_init_c.c new file mode 100644 index 0000000000..2215fb7a95 --- /dev/null +++ b/src/cpu/samsung/exynos5250/lowlevel_init_c.c @@ -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; +} diff --git a/src/cpu/samsung/exynos5250/pinmux.c b/src/cpu/samsung/exynos5250/pinmux.c new file mode 100644 index 0000000000..becced24d5 --- /dev/null +++ b/src/cpu/samsung/exynos5250/pinmux.c @@ -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; +} diff --git a/src/cpu/samsung/exynos5250/power.c b/src/cpu/samsung/exynos5250/power.c new file mode 100644 index 0000000000..2000feebca --- /dev/null +++ b/src/cpu/samsung/exynos5250/power.c @@ -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); +} diff --git a/src/cpu/samsung/exynos5250/sata.c b/src/cpu/samsung/exynos5250/sata.c new file mode 100644 index 0000000000..b3e464ea31 --- /dev/null +++ b/src/cpu/samsung/exynos5250/sata.c @@ -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; +} diff --git a/src/cpu/samsung/exynos5250/setup.h b/src/cpu/samsung/exynos5250/setup.h index 5e5923202a..bb438c5fd6 100644 --- a/src/cpu/samsung/exynos5250/setup.h +++ b/src/cpu/samsung/exynos5250/setup.h @@ -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); diff --git a/src/cpu/samsung/exynos5250/soc.c b/src/cpu/samsung/exynos5250/soc.c new file mode 100644 index 0000000000..be28d8c38d --- /dev/null +++ b/src/cpu/samsung/exynos5250/soc.c @@ -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; +} diff --git a/src/cpu/samsung/exynos5250/spl.c b/src/cpu/samsung/exynos5250/spl.c new file mode 100644 index 0000000000..00b584a1d3 --- /dev/null +++ b/src/cpu/samsung/exynos5250/spl.c @@ -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 diff --git a/src/cpu/samsung/exynos5250/tzpc_init.c b/src/cpu/samsung/exynos5250/tzpc_init.c new file mode 100644 index 0000000000..370c2ed9c2 --- /dev/null +++ b/src/cpu/samsung/exynos5250/tzpc_init.c @@ -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); + } + } +} diff --git a/src/cpu/samsung/exynos5250/uart.c b/src/cpu/samsung/exynos5250/uart.c new file mode 100644 index 0000000000..3d43976b6d --- /dev/null +++ b/src/cpu/samsung/exynos5250/uart.c @@ -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 diff --git a/src/cpu/samsung/s5p-common/Makefile.inc b/src/cpu/samsung/s5p-common/Makefile.inc new file mode 100644 index 0000000000..9747f0d1ab --- /dev/null +++ b/src/cpu/samsung/s5p-common/Makefile.inc @@ -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 diff --git a/src/cpu/samsung/s5p-common/Makefile.uboot b/src/cpu/samsung/s5p-common/Makefile.uboot new file mode 100644 index 0000000000..f975f3f06c --- /dev/null +++ b/src/cpu/samsung/s5p-common/Makefile.uboot @@ -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 + +######################################################################### diff --git a/src/cpu/samsung/s5p-common/cpu_info.c b/src/cpu/samsung/s5p-common/cpu_info.c new file mode 100644 index 0000000000..6b4742ec21 --- /dev/null +++ b/src/cpu/samsung/s5p-common/cpu_info.c @@ -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 diff --git a/src/cpu/samsung/s5p-common/pwm.c b/src/cpu/samsung/s5p-common/pwm.c new file mode 100644 index 0000000000..53004204c6 --- /dev/null +++ b/src/cpu/samsung/s5p-common/pwm.c @@ -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; +} diff --git a/src/cpu/samsung/s5p-common/s5p_gpio.c b/src/cpu/samsung/s5p-common/s5p_gpio.c new file mode 100644 index 0000000000..50c451974c --- /dev/null +++ b/src/cpu/samsung/s5p-common/s5p_gpio.c @@ -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"); + } +} diff --git a/src/cpu/samsung/s5p-common/sromc.c b/src/cpu/samsung/s5p-common/sromc.c new file mode 100644 index 0000000000..091e8d18ab --- /dev/null +++ b/src/cpu/samsung/s5p-common/sromc.c @@ -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; +} diff --git a/src/cpu/samsung/s5p-common/timer.c b/src/cpu/samsung/s5p-common/timer.c new file mode 100644 index 0000000000..373c3741d2 --- /dev/null +++ b/src/cpu/samsung/s5p-common/timer.c @@ -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(); +} diff --git a/src/cpu/samsung/s5p-common/wdt.c b/src/cpu/samsung/s5p-common/wdt.c new file mode 100644 index 0000000000..94acc1e4ba --- /dev/null +++ b/src/cpu/samsung/s5p-common/wdt.c @@ -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); +} |