diff options
Diffstat (limited to 'src/soc')
101 files changed, 22819 insertions, 0 deletions
diff --git a/src/soc/Kconfig b/src/soc/Kconfig index 152e206374..5903a57bcb 100644 --- a/src/soc/Kconfig +++ b/src/soc/Kconfig @@ -1,2 +1,3 @@ source src/soc/intel/Kconfig source src/soc/nvidia/Kconfig +source src/soc/samsung/Kconfig
\ No newline at end of file diff --git a/src/soc/Makefile.inc b/src/soc/Makefile.inc index d419309105..b36d5be853 100644 --- a/src/soc/Makefile.inc +++ b/src/soc/Makefile.inc @@ -3,3 +3,4 @@ ################################################################################ subdirs-y += intel subdirs-y += nvidia +subdirs-y += samsung diff --git a/src/soc/samsung/Kconfig b/src/soc/samsung/Kconfig new file mode 100644 index 0000000000..9241d27bbf --- /dev/null +++ b/src/soc/samsung/Kconfig @@ -0,0 +1,2 @@ +source src/soc/samsung/exynos5250/Kconfig +source src/soc/samsung/exynos5420/Kconfig diff --git a/src/soc/samsung/Makefile.inc b/src/soc/samsung/Makefile.inc new file mode 100644 index 0000000000..496b5f7be3 --- /dev/null +++ b/src/soc/samsung/Makefile.inc @@ -0,0 +1,2 @@ +subdirs-$(CONFIG_CPU_SAMSUNG_EXYNOS5250) += exynos5250 +subdirs-$(CONFIG_CPU_SAMSUNG_EXYNOS5420) += exynos5420 diff --git a/src/soc/samsung/exynos5250/Kconfig b/src/soc/samsung/exynos5250/Kconfig new file mode 100644 index 0000000000..2b4ad3995b --- /dev/null +++ b/src/soc/samsung/exynos5250/Kconfig @@ -0,0 +1,89 @@ +config CPU_SAMSUNG_EXYNOS5250 + select ARCH_BOOTBLOCK_ARMV7 + select ARCH_ROMSTAGE_ARMV7 + select ARCH_RAMSTAGE_ARMV7 + select CPU_HAS_BOOTBLOCK_INIT + select HAVE_MONOTONIC_TIMER + select HAVE_UART_SPECIAL + select DYNAMIC_CBMEM + bool + default n + +if CPU_SAMSUNG_EXYNOS5250 + +# ROM image layout. +# +# 0x0000: vendor-provided BL1 (8k). +# 0x2000: bootblock +# 0x2010-0x2090: reserved for CBFS master header. +# 0xA000: Free for CBFS data. + +config BOOTBLOCK_ROM_OFFSET + hex + default 0x2000 + +config CBFS_HEADER_ROM_OFFSET + hex "offset of master CBFS header in ROM" + default 0x2010 + +config CBFS_ROM_OFFSET + # Calculated by BOOTBLOCK_ROM_OFFSET + max bootblock size. + hex "offset of CBFS data in ROM" + default 0x0A000 + +config SYS_SDRAM_BASE + hex + default 0x40000000 + +# Example SRAM/iRAM map for Exynos5250 platform: +# +# 0x0202_0000: vendor-provided BL1 +# 0x0202_3400: bootblock, assume up to 32KB in size +# 0x0203_0000: romstage, assume up to 128KB in size. +# 0x0207_8000: stack pointer + +config BOOTBLOCK_BASE + hex + default 0x02023400 + +config ROMSTAGE_BASE + hex + default 0x02030000 + +config RAMSTAGE_BASE + hex + default SYS_SDRAM_BASE + +# Stack may reside in either IRAM or DRAM. We will define it to live +# at the top of IRAM for now. +# +# Stack grows downward, push operation stores register contents in +# consecutive memory locations ending just below SP +config STACK_TOP + hex + default 0x02078000 + +config STACK_BOTTOM + hex + default 0x02074000 + +config STACK_SIZE + hex + default 0x4000 + +# TODO We may probably move this to board-specific implementation files instead +# of KConfig values. +config CBFS_CACHE_ADDRESS + hex "memory address to put CBFS cache data" + default 0x0205c000 + +config CBFS_CACHE_SIZE + hex "size of CBFS cache data" + default 0x00018000 + +# TTB needs to be aligned to 16KB. +config TTB_BUFFER + hex "memory address of the TTB buffer" + default 0x02058000 + +endif diff --git a/src/soc/samsung/exynos5250/Makefile.inc b/src/soc/samsung/exynos5250/Makefile.inc new file mode 100644 index 0000000000..a15bc9c6a3 --- /dev/null +++ b/src/soc/samsung/exynos5250/Makefile.inc @@ -0,0 +1,62 @@ +bootblock-y += spi.c alternate_cbfs.c +bootblock-y += bootblock.c +bootblock-y += pinmux.c mct.c power.c +# Clock is required for UART +bootblock-$(CONFIG_BOOTBLOCK_CONSOLE) += clock_init.c +bootblock-$(CONFIG_BOOTBLOCK_CONSOLE) += clock.c +bootblock-$(CONFIG_BOOTBLOCK_CONSOLE) += monotonic_timer.c +ifeq ($(CONFIG_DRIVERS_UART),y) +bootblock-$(CONFIG_BOOTBLOCK_CONSOLE) += uart.c +endif +bootblock-y += wakeup.c +bootblock-y += gpio.c +bootblock-$(CONFIG_BOOTBLOCK_CONSOLE) += timer.c + +romstage-y += spi.c alternate_cbfs.c +romstage-y += clock.c +romstage-y += clock_init.c +romstage-y += pinmux.c # required by s3c24x0_i2c and uart. +romstage-y += dmc_common.c +romstage-y += dmc_init_ddr3.c +romstage-y += power.c +romstage-y += mct.c +romstage-y += monotonic_timer.c +ifeq ($(CONFIG_DRIVERS_UART),y) +romstage-y += uart.c +endif +romstage-y += wakeup.c +romstage-y += gpio.c +romstage-y += timer.c +romstage-y += trustzone.c +romstage-y += i2c.c +#romstage-y += wdt.c +romstage-y += cbmem.c + +ramstage-y += spi.c alternate_cbfs.c +ramstage-y += clock.c +ramstage-y += clock_init.c +ramstage-y += pinmux.c +ramstage-y += power.c +ramstage-$(CONFIG_DRIVERS_UART) += uart.c +ramstage-y += cpu.c +ramstage-y += tmu.c +ramstage-y += mct.c +ramstage-y += monotonic_timer.c +ramstage-y += timer.c +ramstage-y += gpio.c +ramstage-y += i2c.c +ramstage-y += dp-reg.c +ramstage-y += fb.c +ramstage-y += usb.c +ramstage-y += cbmem.c + +# Run an intermediate step when producing coreboot.rom +# that adds additional components to the final firmware +# image outside of CBFS +.PHONY: exynos5250_add_bl1 +$(obj)/coreboot.rom: exynos5250_add_bl1 +exynos5250_add_bl1: $(obj)/coreboot.pre + printf " DD Adding Samsung Exynos5250 BL1\n" + # TODO(hungte) Change this 'cpu' to soc when build scripts are changed. + dd if=3rdparty/cpu/samsung/exynos5250/bl1.bin \ + of=$(obj)/coreboot.pre conv=notrunc >/dev/null 2>&1 diff --git a/src/soc/samsung/exynos5250/alternate_cbfs.c b/src/soc/samsung/exynos5250/alternate_cbfs.c new file mode 100644 index 0000000000..10b33f0c2b --- /dev/null +++ b/src/soc/samsung/exynos5250/alternate_cbfs.c @@ -0,0 +1,176 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2013 Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#include <assert.h> +#include <cbfs.h> /* This driver serves as a CBFS media source. */ +#include <stdlib.h> +#include <string.h> +#include <console/console.h> +#include "alternate_cbfs.h" +#include "power.h" +#include "spi.h" + +/* This allows USB A-A firmware upload from a compatible host in four parts: + * The first two are the bare BL1 and the Coreboot boot block, which are just + * written to their respective loading addresses. These transfers are initiated + * by the IROM / BL1, so this code has nothing to do with them. + * + * The third transfer is a valid CBFS image that contains only the romstage, + * and must be small enough to fit into alternate_cbfs_size[__BOOT_BLOCK__] in + * IRAM. It is loaded when this function gets called in the boot block, and + * the normal CBFS code extracts the romstage from it. + * + * The fourth transfer is also a CBFS image, but can be of arbitrary size and + * should contain all available stages/payloads/etc. It is loaded when this + * function is called a second time at the end of the romstage, and copied to + * alternate_cbfs_buffer[!__BOOT_BLOCK__] in DRAM. It will reside there for the + * rest of the firmware's lifetime and all subsequent stages (which will not + * have __PRE_RAM__ defined) can just directly reference it there. + */ +static int usb_cbfs_open(struct cbfs_media *media) +{ +#ifdef __PRE_RAM__ + static int first_run = 1; + int (*irom_load_usb)(void) = *irom_load_image_from_usb_ptr; + + if (!first_run) + return 0; + + if (!irom_load_usb()) { + printk(BIOS_EMERG, "Unable to load CBFS image via USB!\n"); + return -1; + } + + /* + * We need to trust the host/irom to copy the image to our + * alternate_cbfs_buffer address... there is no way to control or even + * check the transfer size or target address from our side. + */ + + printk(BIOS_DEBUG, "USB A-A transfer successful, CBFS image should now" + " be at %p\n", alternate_cbfs_buffer); + first_run = 0; +#endif + return 0; +} + +/* + * SDMMC works very similar to USB A-A: we copy the CBFS image into memory + * and read it from there. While SDMMC would also allow direct block by block + * on-demand reading, we might run into problems if we call back into the IROM + * in very late boot stages (e.g. after initializing/changing MMC clocks)... so + * this seems like a safer approach. It also makes it easy to pass our image + * down to payloads. + */ +static int sdmmc_cbfs_open(struct cbfs_media *media) +{ +#ifdef __PRE_RAM__ + /* + * In the bootblock, we just copy the small part that fits in the buffer + * and hope that it's enough (since the romstage is currently always the + * first component in the image, this should work out). In the romstage, + * we copy until our buffer is full (currently 12M) to avoid the pain of + * figuring out the true image size from in here. Since this is mainly a + * developer/debug boot mode, those shortcomings should be bearable. + */ + const u32 count = alternate_cbfs_size / 512; + static int first_run = 1; + int (*irom_load_sdmmc)(u32 start, u32 count, void *dst) = + *irom_sdmmc_read_blocks_ptr; + + if (!first_run) + return 0; + + if (!irom_load_sdmmc(1, count, alternate_cbfs_buffer)) { + printk(BIOS_EMERG, "Unable to load CBFS image from SDMMC!\n"); + return -1; + } + + printk(BIOS_DEBUG, "SDMMC read successful, CBFS image should now be" + " at %p\n", alternate_cbfs_buffer); + first_run = 0; +#endif + return 0; +} + +static int alternate_cbfs_close(struct cbfs_media *media) { return 0; } + +static size_t alternate_cbfs_read(struct cbfs_media *media, void *dest, + size_t offset, size_t count) +{ + ASSERT(offset + count < alternate_cbfs_size); + memcpy(dest, alternate_cbfs_buffer + offset, count); + return count; +} + +static void *alternate_cbfs_map(struct cbfs_media *media, size_t offset, + size_t count) +{ + ASSERT(offset + count < alternate_cbfs_size); + return alternate_cbfs_buffer + offset; +} + +static void *alternate_cbfs_unmap(struct cbfs_media *media, + const void *buffer) { return 0; } + +static int initialize_exynos_sdmmc_cbfs_media(struct cbfs_media *media) +{ + printk(BIOS_DEBUG, "Using Exynos alternate boot mode SDMMC\n"); + + media->open = sdmmc_cbfs_open; + media->close = alternate_cbfs_close; + media->read = alternate_cbfs_read; + media->map = alternate_cbfs_map; + media->unmap = alternate_cbfs_unmap; + + return 0; +} + +static int initialize_exynos_usb_cbfs_media(struct cbfs_media *media) +{ + printk(BIOS_DEBUG, "Using Exynos alternate boot mode USB A-A\n"); + + media->open = usb_cbfs_open; + media->close = alternate_cbfs_close; + media->read = alternate_cbfs_read; + media->map = alternate_cbfs_map; + media->unmap = alternate_cbfs_unmap; + + return 0; +} + +int init_default_cbfs_media(struct cbfs_media *media) +{ + if (*iram_secondary_base == SECONDARY_BASE_BOOT_USB) + return initialize_exynos_usb_cbfs_media(media); + + switch (exynos_power->om_stat & OM_STAT_MASK) { + case OM_STAT_SDMMC: + return initialize_exynos_sdmmc_cbfs_media(media); + case OM_STAT_SPI: + return initialize_exynos_spi_cbfs_media(media, + (void*)CONFIG_CBFS_CACHE_ADDRESS, + CONFIG_CBFS_CACHE_SIZE); + default: + printk(BIOS_EMERG, "Exynos OM_STAT value 0x%x not supported!\n", + exynos_power->om_stat); + return 0; + } +} diff --git a/src/soc/samsung/exynos5250/alternate_cbfs.h b/src/soc/samsung/exynos5250/alternate_cbfs.h new file mode 100644 index 0000000000..72a30acc84 --- /dev/null +++ b/src/soc/samsung/exynos5250/alternate_cbfs.h @@ -0,0 +1,51 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2013 Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef CPU_SAMSUNG_EXYNOS5250_ALTERNATE_CBFS_H +#define CPU_SAMSUNG_EXYNOS5250_ALTERNATE_CBFS_H + +/* These are pointers to function pointers. Double indirection! */ +static void * * const irom_sdmmc_read_blocks_ptr = (void * *)0x02020030; +static void * * const irom_msh_read_from_fifo_emmc_ptr = (void * *)0x02020044; +static void * * const irom_msh_end_boot_op_emmc_ptr = (void * *)0x02020048; +static void * * const irom_spi_sf_read_ptr = (void * *)0x02020058; +static void * * const irom_load_image_from_usb_ptr = (void * *)0x02020070; + +#define SECONDARY_BASE_BOOT_USB 0xfeed0002 +static u32 * const iram_secondary_base = (u32 *)0x02020018; + +/* Values pulled from U-Boot, I think the manual is wrong here (for SPI) */ +#define OM_STAT_SDMMC 0x4 +#define OM_STAT_EMMC 0x8 +#define OM_STAT_SPI 0x14 +#define OM_STAT_MASK 0x7f + +#if defined(__BOOT_BLOCK__) + /* A small space in IRAM to hold the romstage-only image */ + static void * const alternate_cbfs_buffer = + (void *)CONFIG_CBFS_CACHE_ADDRESS; + static size_t const alternate_cbfs_size = CONFIG_CBFS_CACHE_SIZE; +#else + /* Just put this anywhere in RAM that's far enough from anything else */ + /* TODO: Find a better way to "reserve" this region? */ + static void * const alternate_cbfs_buffer = (void *)0x77400000; + static size_t const alternate_cbfs_size = 0xc00000; +#endif + +#endif diff --git a/src/soc/samsung/exynos5250/bootblock.c b/src/soc/samsung/exynos5250/bootblock.c new file mode 100644 index 0000000000..f524399e91 --- /dev/null +++ b/src/soc/samsung/exynos5250/bootblock.c @@ -0,0 +1,41 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2013 Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <bootblock_common.h> + +#include "clk.h" +#include "wakeup.h" + +void bootblock_cpu_init(void) +{ + /* kick off the multi-core timer. + * We want to do this as early as we can. + */ + mct_start(); + + if (get_wakeup_state() == WAKEUP_DIRECT) { + wakeup(); + /* Never returns. */ + } + + /* For most ARM systems, we have to initialize firmware media source + * (ex, SPI, SD/MMC, or eMMC) now; but for Exynos platform, that is + * already handled by iROM so there's no need to setup again. + */ +} diff --git a/src/soc/samsung/exynos5250/cbmem.c b/src/soc/samsung/exynos5250/cbmem.c new file mode 100644 index 0000000000..465032013b --- /dev/null +++ b/src/soc/samsung/exynos5250/cbmem.c @@ -0,0 +1,27 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2013 Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <stddef.h> +#include <cbmem.h> +#include "cpu.h" + +void *cbmem_top(void) +{ + return (void *)(get_fb_base_kb() * KiB); +} diff --git a/src/soc/samsung/exynos5250/chip.h b/src/soc/samsung/exynos5250/chip.h new file mode 100644 index 0000000000..242bd21c70 --- /dev/null +++ b/src/soc/samsung/exynos5250/chip.h @@ -0,0 +1,45 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2013 Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef CPU_SAMSUNG_EXYNOS5250_H +#define CPU_SAMSUNG_EXYNOS5250_H + +#include "gpio.h" + +struct soc_samsung_exynos5250_config { + /* special magic numbers! */ + int clkval_f; + int upper_margin; + int lower_margin; + int vsync; + int left_margin; + int right_margin; + int hsync; + + int xres; + int yres; + int framebuffer_bits_per_pixel; + + int usb_vbus_gpio; + int usb_hsic_gpio; + + u32 lcdbase; +}; + +#endif /* CPU_SAMSUNG_EXYNOS5250_H */ diff --git a/src/soc/samsung/exynos5250/clk.h b/src/soc/samsung/exynos5250/clk.h new file mode 100644 index 0000000000..f09ac41643 --- /dev/null +++ b/src/soc/samsung/exynos5250/clk.h @@ -0,0 +1,629 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2013 Google Inc. + * Copyright (C) 2012 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef CPU_SAMSUNG_EXYNOS5250_CLK_H +#define CPU_SAMSUNG_EXYNOS5250_CLK_H + +#include <stdint.h> + +#include "cpu.h" + +enum periph_id; + +#define APLL 0 +#define MPLL 1 +#define EPLL 2 +#define HPLL 3 +#define VPLL 4 +#define BPLL 5 + +enum pll_src_bit { + SRC_MPLL = 6, + SRC_EPLL, + SRC_VPLL, +}; + +/* * + * This structure is to store the src bit, div bit and prediv bit + * positions of the peripheral clocks of the src and div registers + */ +struct clk_bit_info { + s8 src_bit; /* offset in register to clock source field */ + s8 n_src_bits; /* number of bits in 'src_bit' field */ + s8 div_bit; + s8 prediv_bit; +}; + +unsigned long get_pll_clk(int pllreg); +unsigned long get_arm_clk(void); +unsigned long get_pwm_clk(void); +unsigned long get_uart_clk(int dev_index); +void set_mmc_clk(int dev_index, unsigned int div); + +/** + * get the clk frequency of the required peripherial + * + * @param peripherial Peripherial id + * + * @return frequency of the peripherial clk + */ +unsigned long clock_get_periph_rate(enum periph_id peripheral); + +#include "pinmux.h" + + +#define MCT_HZ 24000000 + +/* + * Set mshci controller instances clock divider + * + * @param enum periph_id instance of the mshci controller + * + * Return 0 if ok else -1 + */ +int clock_set_mshci(enum periph_id peripheral); + +/* + * Sets the epll clockrate + * + * @param rate Required clock rate to the presacaler in Hz + * + * Return 0 if ok else -1 + */ +int clock_epll_set_rate(unsigned long rate); + +/* + * selects the clk source for I2S MCLK + */ +void clock_select_i2s_clk_source(void); + +/* + * Set prescaler division based on input and output frequency + * for i2s audio clock + * + * @param src_frq Source frequency in Hz + * @param dst_frq Required MCLK frequency in Hz + * + * Return 0 if ok else -1 + */ +int clock_set_i2s_clk_prescaler(unsigned int src_frq, unsigned int dst_frq); + +struct exynos5_clock { + unsigned int apll_lock; /* base + 0 */ + unsigned char res1[0xfc]; + unsigned int apll_con0; + unsigned int apll_con1; + unsigned char res2[0xf8]; + unsigned int src_cpu; + unsigned char res3[0x1fc]; + unsigned int mux_stat_cpu; + unsigned char res4[0xfc]; + unsigned int div_cpu0; + unsigned int div_cpu1; + unsigned char res5[0xf8]; + unsigned int div_stat_cpu0; + unsigned int div_stat_cpu1; + unsigned char res6[0x1f8]; + unsigned int gate_sclk_cpu; + unsigned char res7[0x1fc]; + unsigned int clkout_cmu_cpu; + unsigned int clkout_cmu_cpu_div_stat; + unsigned char res8[0x5f8]; + + unsigned int armclk_stopctrl; /* base + 0x1000 */ + unsigned int atclk_stopctrl; + unsigned char res9[0x8]; + unsigned int parityfail_status; + unsigned int parityfail_clear; + unsigned char res10[0x8]; + unsigned int pwr_ctrl; + unsigned int pwr_ctr2; + unsigned char res11[0xd8]; + unsigned int apll_con0_l8; + unsigned int apll_con0_l7; + unsigned int apll_con0_l6; + unsigned int apll_con0_l5; + unsigned int apll_con0_l4; + unsigned int apll_con0_l3; + unsigned int apll_con0_l2; + unsigned int apll_con0_l1; + unsigned int iem_control; + unsigned char res12[0xdc]; + unsigned int apll_con1_l8; + unsigned int apll_con1_l7; + unsigned int apll_con1_l6; + unsigned int apll_con1_l5; + unsigned int apll_con1_l4; + unsigned int apll_con1_l3; + unsigned int apll_con1_l2; + unsigned int apll_con1_l1; + unsigned char res13[0xe0]; + unsigned int div_iem_l8; + unsigned int div_iem_l7; + unsigned int div_iem_l6; + unsigned int div_iem_l5; + unsigned int div_iem_l4; + unsigned int div_iem_l3; + unsigned int div_iem_l2; + unsigned int div_iem_l1; + unsigned char res14[0x2ce0]; + + unsigned int mpll_lock; /* base + 0x4000 */ + unsigned char res15[0xfc]; + unsigned int mpll_con0; + unsigned int mpll_con1; + unsigned char res16[0xf8]; + unsigned int src_core0; + unsigned int src_core1; + unsigned char res17[0xf8]; + unsigned int src_mask_core; + unsigned char res18[0x100]; + unsigned int mux_stat_core1; + unsigned char res19[0xf8]; + unsigned int div_core0; + unsigned int div_core1; + unsigned int div_sysrgt; + unsigned char res20[0xf4]; + unsigned int div_stat_core0; + unsigned int div_stat_core1; + unsigned int div_stat_sysrgt; + unsigned char res21[0x2f4]; + unsigned int gate_ip_core; + unsigned int gate_ip_sysrgt; + unsigned char res22[0xf8]; + unsigned int clkout_cmu_core; + unsigned int clkout_cmu_core_div_stat; + unsigned char res23[0x5f8]; + + unsigned int dcgidx_map0; /* base + 0x5000 */ + unsigned int dcgidx_map1; + unsigned int dcgidx_map2; + unsigned char res24[0x14]; + unsigned int dcgperf_map0; + unsigned int dcgperf_map1; + unsigned char res25[0x18]; + unsigned int dvcidx_map; + unsigned char res26[0x1c]; + unsigned int freq_cpu; + unsigned int freq_dpm; + unsigned char res27[0x18]; + unsigned int dvsemclk_en; + unsigned int maxperf; + unsigned char res28[0x3478]; + + unsigned int div_acp; /* base + 0x8500 */ + unsigned char res29[0xfc]; + unsigned int div_stat_acp; + unsigned char res30[0x1fc]; + unsigned int gate_ip_acp; + unsigned char res31a[0xfc]; + unsigned int div_syslft; + unsigned char res31b[0xc]; + unsigned int div_stat_syslft; + unsigned char res31c[0xc]; + unsigned int gate_bus_syslft; + unsigned char res31d[0xdc]; + unsigned int clkout_cmu_acp; + unsigned int clkout_cmu_acp_div_stat; + unsigned char res32[0x38f8]; + + unsigned int div_isp0; /* base + 0xc300 */ + unsigned int div_isp1; + unsigned int div_isp2; + unsigned char res33[0xf4]; + + unsigned int div_stat_isp0; /* base + 0xc400 */ + unsigned int div_stat_isp1; + unsigned int div_stat_isp2; + unsigned char res34[0x3f4]; + + unsigned int gate_ip_isp0; /* base + 0xc800 */ + unsigned int gate_ip_isp1; + unsigned char res35[0xf8]; + unsigned int gate_sclk_isp; + unsigned char res36[0xc]; + unsigned int mcuisp_pwr_ctrl; + unsigned char res37[0xec]; + unsigned int clkout_cmu_isp; + unsigned int clkout_cmu_isp_div_stat; + unsigned char res38[0x3618]; + + unsigned int cpll_lock; /* base + 0x10020 */ + unsigned char res39[0xc]; + unsigned int epll_lock; + unsigned char res40[0xc]; + unsigned int vpll_lock; + unsigned char res41a[0xc]; + unsigned int gpll_lock; + unsigned char res41b[0xcc]; + unsigned int cpll_con0; + unsigned int cpll_con1; + unsigned char res42[0x8]; + unsigned int epll_con0; + unsigned int epll_con1; + unsigned int epll_con2; + unsigned char res43[0x4]; + unsigned int vpll_con0; + unsigned int vpll_con1; + unsigned int vpll_con2; + unsigned char res44a[0x4]; + unsigned int gpll_con0; + unsigned int gpll_con1; + unsigned char res44b[0xb8]; + unsigned int src_top0; + unsigned int src_top1; + unsigned int src_top2; + unsigned int src_top3; + unsigned int src_gscl; + unsigned int src_disp0_0; + unsigned int src_disp0_1; + unsigned int src_disp1_0; + unsigned int src_disp1_1; + unsigned char res46[0xc]; + unsigned int src_mau; + unsigned int src_fsys; + unsigned char res47[0x8]; + unsigned int src_peric0; + unsigned int src_peric1; + unsigned char res48[0x18]; + unsigned int sclk_src_isp; + unsigned char res49[0x9c]; + unsigned int src_mask_top; + unsigned char res50[0xc]; + unsigned int src_mask_gscl; + unsigned int src_mask_disp0_0; + unsigned int src_mask_disp0_1; + unsigned int src_mask_disp1_0; + unsigned int src_mask_disp1_1; + unsigned int src_mask_maudio; + unsigned char res52[0x8]; + unsigned int src_mask_fsys; + unsigned char res53[0xc]; + unsigned int src_mask_peric0; + unsigned int src_mask_peric1; + unsigned char res54[0x18]; + unsigned int src_mask_isp; + unsigned char res55[0x9c]; + unsigned int mux_stat_top0; + unsigned int mux_stat_top1; + unsigned int mux_stat_top2; + unsigned int mux_stat_top3; + unsigned char res56[0xf0]; + unsigned int div_top0; + unsigned int div_top1; + unsigned char res57[0x8]; + unsigned int div_gscl; + unsigned int div_disp0_0; + unsigned int div_disp0_1; + unsigned int div_disp1_0; + unsigned int div_disp1_1; + unsigned char res59[0x8]; + unsigned int div_gen; + unsigned char res60[0x4]; + unsigned int div_mau; + unsigned int div_fsys0; + unsigned int div_fsys1; + unsigned int div_fsys2; + unsigned int div_fsys3; + unsigned int div_peric0; + unsigned int div_peric1; + unsigned int div_peric2; + unsigned int div_peric3; + unsigned int div_peric4; + unsigned int div_peric5; + unsigned char res61[0x10]; + unsigned int sclk_div_isp; + unsigned char res62[0xc]; + unsigned int div2_ratio0; + unsigned int div2_ratio1; + unsigned char res63[0x8]; + unsigned int div4_ratio; + unsigned char res64[0x6c]; + unsigned int div_stat_top0; + unsigned int div_stat_top1; + unsigned char res65[0x8]; + unsigned int div_stat_gscl; + unsigned int div_stat_disp0_0; + unsigned int div_stat_disp0_1; + unsigned int div_stat_disp1_0; + unsigned int div_stat_disp1_1; + unsigned char res67[0x8]; + unsigned int div_stat_gen; + unsigned char res68[0x4]; + unsigned int div_stat_maudio; + unsigned int div_stat_fsys0; + unsigned int div_stat_fsys1; + unsigned int div_stat_fsys2; + unsigned int div_stat_fsys3; + unsigned int div_stat_peric0; + unsigned int div_stat_peric1; + unsigned int div_stat_peric2; + unsigned int div_stat_peric3; + unsigned int div_stat_peric4; + unsigned int div_stat_peric5; + unsigned char res69[0x10]; + unsigned int sclk_div_stat_isp; + unsigned char res70[0xc]; + unsigned int div2_stat0; + unsigned int div2_stat1; + unsigned char res71[0x8]; + unsigned int div4_stat; + unsigned char res72[0x180]; + unsigned int gate_top_sclk_disp0; + unsigned int gate_top_sclk_disp1; + unsigned int gate_top_sclk_gen; + unsigned char res74[0xc]; + unsigned int gate_top_sclk_mau; + unsigned int gate_top_sclk_fsys; + unsigned char res75[0xc]; + unsigned int gate_top_sclk_peric; + unsigned char res76[0x1c]; + unsigned int gate_top_sclk_isp; + unsigned char res77[0xac]; + unsigned int gate_ip_gscl; + unsigned int gate_ip_disp0; + unsigned int gate_ip_disp1; + unsigned int gate_ip_mfc; + unsigned int gate_ip_g3d; + unsigned int gate_ip_gen; + unsigned char res79[0xc]; + unsigned int gate_ip_fsys; + unsigned char res80[0x4]; + unsigned int gate_ip_gps; + unsigned int gate_ip_peric; + unsigned char res81[0xc]; + unsigned int gate_ip_peris; + unsigned char res82[0x1c]; + unsigned int gate_block; + unsigned char res83[0x7c]; + unsigned int clkout_cmu_top; + unsigned int clkout_cmu_top_div_stat; + unsigned char res84[0x37f8]; + + unsigned int src_lex; /* base + 0x14200 */ + unsigned char res85[0x1fc]; + unsigned int mux_stat_lex; + unsigned char res85b[0xfc]; + unsigned int div_lex; + unsigned char res86[0xfc]; + unsigned int div_stat_lex; + unsigned char res87[0x1fc]; + unsigned int gate_ip_lex; + unsigned char res88[0x1fc]; + unsigned int clkout_cmu_lex; + unsigned int clkout_cmu_lex_div_stat; + unsigned char res89[0x3af8]; + + unsigned int div_r0x; /* base + 0x18500 */ + unsigned char res90[0xfc]; + unsigned int div_stat_r0x; + unsigned char res91[0x1fc]; + unsigned int gate_ip_r0x; + unsigned char res92[0x1fc]; + unsigned int clkout_cmu_r0x; + unsigned int clkout_cmu_r0x_div_stat; + unsigned char res94[0x3af8]; + + unsigned int div_r1x; /* base + 0x1c500 */ + unsigned char res95[0xfc]; + unsigned int div_stat_r1x; + unsigned char res96[0x1fc]; + unsigned int gate_ip_r1x; + unsigned char res97[0x1fc]; + unsigned int clkout_cmu_r1x; + unsigned int clkout_cmu_r1x_div_stat; + unsigned char res98[0x3608]; + + unsigned int bpll_lock; /* base + 0x2000c */ + unsigned char res99[0xfc]; + unsigned int bpll_con0; + unsigned int bpll_con1; + unsigned char res100[0xe8]; + unsigned int src_cdrex; + unsigned char res101[0x1fc]; + unsigned int mux_stat_cdrex; + unsigned char res102[0xfc]; + unsigned int div_cdrex; + unsigned int div_cdrex2; + unsigned char res103[0xf8]; + unsigned int div_stat_cdrex; + unsigned char res104[0x2fc]; + unsigned int gate_ip_cdrex; + unsigned char res105[0xc]; + unsigned int c2c_monitor; + unsigned int dmc_pwr_ctrl; + unsigned char res106[0x4]; + unsigned int drex2_pause; + unsigned char res107[0xe0]; + unsigned int clkout_cmu_cdrex; + unsigned int clkout_cmu_cdrex_div_stat; + unsigned char res108[0x8]; + unsigned int lpddr3phy_ctrl; + unsigned char res109a[0xc]; + unsigned int lpddr3phy_con3; + unsigned int pll_div2_sel; + unsigned char res109b[0xf5e4]; +}; + +static struct exynos5_clock * const exynos_clock = (void *)EXYNOS5_CLOCK_BASE; + +struct exynos5_mct { + uint32_t mct_cfg; + uint8_t reserved0[0xfc]; + uint32_t g_cnt_l; + uint32_t g_cnt_u; + uint8_t reserved1[0x8]; + uint32_t g_cnt_wstat; + uint8_t reserved2[0xec]; + uint32_t g_comp0_l; + uint32_t g_comp0_u; + uint32_t g_comp0_addr_incr; + uint8_t reserved3[0x4]; + uint32_t g_comp1_l; + uint32_t g_comp1_u; + uint32_t g_comp1_addr_incr; + uint8_t reserved4[0x4]; + uint32_t g_comp2_l; + uint32_t g_comp2_u; + uint32_t g_comp2_addr_incr; + uint8_t reserved5[0x4]; + uint32_t g_comp3_l; + uint32_t g_comp3_u; + uint32_t g_comp3_addr_incr; + uint8_t reserved6[0x4]; + uint32_t g_tcon; + uint32_t g_int_cstat; + uint32_t g_int_enb; + uint32_t g_wstat; + uint8_t reserved7[0xb0]; + uint32_t l0_tcntb; + uint32_t l0_tcnto; + uint32_t l0_icntb; + uint32_t l0_icnto; + uint32_t l0_frcntb; + uint32_t l0_frcnto; + uint8_t reserved8[0x8]; + uint32_t l0_tcon; + uint8_t reserved9[0xc]; + uint32_t l0_int_cstat; + uint32_t l0_int_enb; + uint8_t reserved10[0x8]; + uint32_t l0_wstat; + uint8_t reserved11[0xbc]; + uint32_t l1_tcntb; + uint32_t l1_tcnto; + uint32_t l1_icntb; + uint32_t l1_icnto; + uint32_t l1_frcntb; + uint32_t l1_frcnto; + uint8_t reserved12[0x8]; + uint32_t l1_tcon; + uint8_t reserved13[0xc]; + uint32_t l1_int_cstat; + uint32_t l1_int_enb; + uint8_t reserved14[0x8]; + uint32_t l1_wstat; +}; + +static struct exynos5_mct * const exynos_mct = + (void *)EXYNOS5_MULTI_CORE_TIMER_BASE; + +#define EXYNOS5_EPLLCON0_LOCKED_SHIFT 29 /* EPLL Locked bit position*/ +#define EPLL_SRC_CLOCK 24000000 /*24 MHz Crystal Input */ +#define TIMEOUT_EPLL_LOCK 1000 + +#define AUDIO_0_RATIO_MASK 0x0f +#define AUDIO_1_RATIO_MASK 0x0f + +#define CLK_SRC_PERIC1 0x254 +#define AUDIO1_SEL_MASK 0xf +#define CLK_SRC_AUDIOCDCLK1 0x0 +#define CLK_SRC_XXTI 0x1 +#define CLK_SRC_SCLK_EPLL 0x7 + +/* CON0 bit-fields */ +#define EPLL_CON0_MDIV_MASK 0x1ff +#define EPLL_CON0_PDIV_MASK 0x3f +#define EPLL_CON0_SDIV_MASK 0x7 +#define EPLL_CON0_LOCKED_SHIFT 29 +#define EPLL_CON0_MDIV_SHIFT 16 +#define EPLL_CON0_PDIV_SHIFT 8 +#define EPLL_CON0_SDIV_SHIFT 0 +#define EPLL_CON0_LOCK_DET_EN_SHIFT 28 +#define EPLL_CON0_LOCK_DET_EN_MASK 1 + +/* structure for epll configuration used in audio clock configuration */ +struct st_epll_con_val { + unsigned int freq_out; /* frequency out */ + unsigned int en_lock_det; /* enable lock detect */ + unsigned int m_div; /* m divider value */ + unsigned int p_div; /* p divider value */ + unsigned int s_div; /* s divider value */ + unsigned int k_dsm; /* k value of delta signal modulator */ +}; + +/** + * Low-level function to set the clock pre-ratio for a peripheral + * + * @param periph_id Peripheral ID of peripheral to change + * @param divisor New divisor for this peripheral's clock + */ +void clock_ll_set_pre_ratio(enum periph_id periph_id, unsigned divisor); + +/** + * Low-level function to set the clock ratio for a peripheral + * + * @param periph_id Peripheral ID of peripheral to change + * @param divisor New divisor for this peripheral's clock + */ +void clock_ll_set_ratio(enum periph_id periph_id, unsigned divisor); + +/** + * Low-level function that selects the best clock scalars for a given rate and + * sets up the given peripheral's clock accordingly. + * + * @param periph_id Peripheral ID of peripheral to change + * @param rate Desired clock rate in Hz + * + * @return zero on success, negative on error + */ +int clock_set_rate(enum periph_id periph_id, unsigned int rate); + +/* Clock gate unused IP */ +void clock_gate(void); + +void mct_start(void); +uint64_t mct_raw_value(void); + +#include "dmc.h" + +/* These are the ratio's for configuring ARM clock */ +struct arm_clk_ratios { + unsigned int arm_freq_mhz; /* Frequency of ARM core in MHz */ + + unsigned int apll_mdiv; + unsigned int apll_pdiv; + unsigned int apll_sdiv; + + unsigned int arm2_ratio; + unsigned int apll_ratio; + unsigned int pclk_dbg_ratio; + unsigned int atb_ratio; + unsigned int periph_ratio; + unsigned int acp_ratio; + unsigned int cpud_ratio; + unsigned int arm_ratio; +}; + +/** + * Get the clock ratios for CPU configuration + * + * @return pointer to the clock ratios that we should use + */ +struct arm_clk_ratios *get_arm_clk_ratios(void); + +/* + * Initialize clock for the device + */ +struct mem_timings; +void system_clock_init(struct mem_timings *mem, + struct arm_clk_ratios *arm_clk_ratio); + +#endif diff --git a/src/soc/samsung/exynos5250/clock.c b/src/soc/samsung/exynos5250/clock.c new file mode 100644 index 0000000000..8a731be2a3 --- /dev/null +++ b/src/soc/samsung/exynos5250/clock.c @@ -0,0 +1,677 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2010 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <assert.h> +#include <stdlib.h> +#include <arch/io.h> +#include <console/console.h> +#include "clk.h" +#include "periph.h" +#include "timer.h" + +/* input clock of PLL: SMDK5250 has 24MHz input clock */ +#define CONFIG_SYS_CLK_FREQ 24000000 + +static struct arm_clk_ratios arm_clk_ratios[] = { + { + .arm_freq_mhz = 600, + + .apll_mdiv = 0xc8, + .apll_pdiv = 0x4, + .apll_sdiv = 0x1, + + .arm2_ratio = 0x0, + .apll_ratio = 0x1, + .pclk_dbg_ratio = 0x1, + .atb_ratio = 0x2, + .periph_ratio = 0x7, + .acp_ratio = 0x7, + .cpud_ratio = 0x1, + .arm_ratio = 0x0, + }, { + .arm_freq_mhz = 800, + + .apll_mdiv = 0x64, + .apll_pdiv = 0x3, + .apll_sdiv = 0x0, + + .arm2_ratio = 0x0, + .apll_ratio = 0x1, + .pclk_dbg_ratio = 0x1, + .atb_ratio = 0x3, + .periph_ratio = 0x7, + .acp_ratio = 0x7, + .cpud_ratio = 0x2, + .arm_ratio = 0x0, + }, { + .arm_freq_mhz = 1000, + + .apll_mdiv = 0x7d, + .apll_pdiv = 0x3, + .apll_sdiv = 0x0, + + .arm2_ratio = 0x0, + .apll_ratio = 0x1, + .pclk_dbg_ratio = 0x1, + .atb_ratio = 0x4, + .periph_ratio = 0x7, + .acp_ratio = 0x7, + .cpud_ratio = 0x2, + .arm_ratio = 0x0, + }, { + .arm_freq_mhz = 1200, + + .apll_mdiv = 0x96, + .apll_pdiv = 0x3, + .apll_sdiv = 0x0, + + .arm2_ratio = 0x0, + .apll_ratio = 0x3, + .pclk_dbg_ratio = 0x1, + .atb_ratio = 0x5, + .periph_ratio = 0x7, + .acp_ratio = 0x7, + .cpud_ratio = 0x3, + .arm_ratio = 0x0, + }, { + .arm_freq_mhz = 1400, + + .apll_mdiv = 0xaf, + .apll_pdiv = 0x3, + .apll_sdiv = 0x0, + + .arm2_ratio = 0x0, + .apll_ratio = 0x3, + .pclk_dbg_ratio = 0x1, + .atb_ratio = 0x6, + .periph_ratio = 0x7, + .acp_ratio = 0x7, + .cpud_ratio = 0x3, + .arm_ratio = 0x0, + }, { + .arm_freq_mhz = 1700, + + .apll_mdiv = 0x1a9, + .apll_pdiv = 0x6, + .apll_sdiv = 0x0, + + .arm2_ratio = 0x0, + .apll_ratio = 0x3, + .pclk_dbg_ratio = 0x1, + .atb_ratio = 0x6, + .periph_ratio = 0x7, + .acp_ratio = 0x7, + .cpud_ratio = 0x3, + .arm_ratio = 0x0, + } +}; + +/* src_bit div_bit prediv_bit */ +static struct clk_bit_info clk_bit_info[PERIPH_ID_COUNT] = { + {0, 4, 0, -1}, + {4, 4, 4, -1}, + {8, 4, 8, -1}, + {12, 4, 12, -1}, + {0, 4, 0, 8}, + {4, 4, 16, 24}, + {8, 4, 0, 8}, + {12, 4, 16, 24}, + {-1, -1, -1, -1}, + {16, 4, 0, 8}, /* PERIPH_ID_SROMC */ + {20, 4, 16, 24}, + {24, 4, 0, 8}, + {0, 4, 0, 4}, + {4, 4, 12, 16}, + {-1, 4, -1, -1}, + {-1, 4, -1, -1}, + {-1, 4, 24, 0}, + {-1, 4, 24, 0}, + {-1, 4, 24, 0}, + {-1, 4, 24, 0}, + {-1, 4, 24, 0}, + {-1, 4, 24, 0}, + {-1, 4, 24, 0}, + {-1, 4, 24, 0}, + {24, 4, 0, -1}, + {24, 4, 0, -1}, + {24, 4, 0, -1}, + {24, 4, 0, -1}, + {24, 4, 0, -1}, + {-1, -1, -1, -1}, + {-1, -1, -1, -1}, + {-1, -1, -1, -1}, /* PERIPH_ID_I2S1 */ + {24, 1, 20, -1}, /* PERIPH_ID_SATA */ +}; + +/* Epll Clock division values to achieve 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) +{ + unsigned long r, m, p, s, k = 0, mask, fout; + unsigned int freq; + + switch (pllreg) { + case APLL: + r = readl(&exynos_clock->apll_con0); + break; + case BPLL: + r = readl(&exynos_clock->bpll_con0); + break; + case MPLL: + r = readl(&exynos_clock->mpll_con0); + break; + case EPLL: + r = readl(&exynos_clock->epll_con0); + k = readl(&exynos_clock->epll_con1); + break; + case VPLL: + r = readl(&exynos_clock->vpll_con0); + k = readl(&exynos_clock->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 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(&exynos_clock->src_peric0); + div = readl(&exynos_clock->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(&exynos_clock->src_peric0); + div = readl(&exynos_clock->div_peric3); + break; + case PERIPH_ID_SPI0: + case PERIPH_ID_SPI1: + src = readl(&exynos_clock->src_peric1); + div = readl(&exynos_clock->div_peric1); + break; + case PERIPH_ID_SPI2: + src = readl(&exynos_clock->src_peric1); + div = readl(&exynos_clock->div_peric2); + break; + case PERIPH_ID_SPI3: + case PERIPH_ID_SPI4: + src = readl(&exynos_clock->sclk_src_isp); + div = readl(&exynos_clock->sclk_div_isp); + break; + case PERIPH_ID_SATA: + src = readl(&exynos_clock->src_fsys); + div = readl(&exynos_clock->div_fsys0); + break; + case PERIPH_ID_SDMMC0: + case PERIPH_ID_SDMMC1: + case PERIPH_ID_SDMMC2: + case PERIPH_ID_SDMMC3: + src = readl(&exynos_clock->src_fsys); + div = readl(&exynos_clock->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(&exynos_clock->div_top1) + >> bit_info->div_bit) & 0x7) + 1; + div = ((readl(&exynos_clock->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) +{ + unsigned long div; + unsigned long armclk; + unsigned int arm_ratio; + unsigned int arm2_ratio; + + div = readl(&exynos_clock->div_cpu0); + + /* ARM_RATIO: [2:0], ARM2_RATIO: [30:28] */ + arm_ratio = (div >> 0) & 0x7; + arm2_ratio = (div >> 28) & 0x7; + + armclk = get_pll_clk(APLL) / (arm_ratio + 1); + armclk /= (arm2_ratio + 1); + + return armclk; +} + +struct arm_clk_ratios *get_arm_clk_ratios(void) +{ + struct arm_clk_ratios *arm_ratio; + unsigned long arm_freq = 1700; /* FIXME: use get_arm_clk() */ + int i; + + for (i = 0, arm_ratio = arm_clk_ratios; i < ARRAY_SIZE(arm_clk_ratios); + i++, arm_ratio++) { + if (arm_ratio->arm_freq_mhz == arm_freq) + return arm_ratio; + } + + return NULL; +} + +/* exynos5: set the mmc clock */ +void set_mmc_clk(int dev_index, unsigned int div) +{ + 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 = &exynos_clock->div_fsys1; + } else { + addr = &exynos_clock->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) +{ + unsigned shift; + unsigned mask = 0xff; + u32 *reg; + + /* + * For now we only handle a very small subset of peripherals 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 = &exynos_clock->div_peric1; + shift = 8; + break; + case PERIPH_ID_SPI1: + reg = &exynos_clock->div_peric1; + shift = 24; + break; + case PERIPH_ID_SPI2: + reg = &exynos_clock->div_peric2; + shift = 8; + break; + case PERIPH_ID_SPI3: + reg = &exynos_clock->sclk_div_isp; + shift = 4; + break; + case PERIPH_ID_SPI4: + reg = &exynos_clock->sclk_div_isp; + shift = 16; + break; + default: + printk(BIOS_DEBUG, "%s: Unsupported peripheral ID %d\n", __func__, + periph_id); + return; + } + clrsetbits_le32(reg, mask << shift, (divisor & mask) << shift); +} + +void clock_ll_set_ratio(enum periph_id periph_id, unsigned divisor) +{ + unsigned shift; + unsigned mask = 0xff; + u32 *reg; + + switch (periph_id) { + case PERIPH_ID_SPI0: + reg = &exynos_clock->div_peric1; + shift = 0; + break; + case PERIPH_ID_SPI1: + reg = &exynos_clock->div_peric1; + shift = 16; + break; + case PERIPH_ID_SPI2: + reg = &exynos_clock->div_peric2; + shift = 0; + break; + case PERIPH_ID_SPI3: + reg = &exynos_clock->sclk_div_isp; + shift = 0; + break; + case PERIPH_ID_SPI4: + reg = &exynos_clock->sclk_div_isp; + shift = 12; + break; + default: + printk(BIOS_DEBUG, "%s: Unsupported peripheral ID %d\n", __func__, + periph_id); + return; + } + clrsetbits_le32(reg, mask << shift, (divisor & mask) << shift); +} + +/** + * Linearly searches for the most accurate main and fine stage clock scalars + * (divisors) for a specified target frequency and scalar bit sizes by checking + * all multiples of main_scalar_bits values. Will always return scalars up to or + * slower than target. + * + * @param main_scalar_bits Number of main scalar bits, must be > 0 and < 32 + * @param fine_scalar_bits Number of fine scalar bits, must be > 0 and < 32 + * @param input_freq Clock frequency to be scaled in Hz + * @param target_freq Desired clock frequency in Hz + * @param best_fine_scalar Pointer to store the fine stage divisor + * + * @return best_main_scalar Main scalar for desired frequency or -1 if none + * found + */ +static int clock_calc_best_scalar(unsigned int main_scaler_bits, + unsigned int fine_scalar_bits, unsigned int input_rate, + unsigned int target_rate, unsigned int *best_fine_scalar) +{ + int i; + int best_main_scalar = -1; + unsigned int best_error = target_rate; + const unsigned int cap = (1 << fine_scalar_bits) - 1; + const unsigned int loops = 1 << main_scaler_bits; + + printk(BIOS_DEBUG, "Input Rate is %u, Target is %u, Cap is %u\n", input_rate, + target_rate, cap); + + ASSERT(best_fine_scalar != NULL); + ASSERT(main_scaler_bits <= fine_scalar_bits); + + *best_fine_scalar = 1; + + if (input_rate == 0 || target_rate == 0) + return -1; + + if (target_rate >= input_rate) + return 1; + + for (i = 1; i <= loops; i++) { + const unsigned int effective_div = MAX(MIN(input_rate / i / + target_rate, cap), 1); + const unsigned int effective_rate = input_rate / i / + effective_div; + const int error = target_rate - effective_rate; + + printk(BIOS_DEBUG, "%d|effdiv:%u, effrate:%u, error:%d\n", i, effective_div, + effective_rate, error); + + if (error >= 0 && error <= best_error) { + best_error = error; + best_main_scalar = i; + *best_fine_scalar = effective_div; + } + } + + return best_main_scalar; +} + +int clock_set_rate(enum periph_id periph_id, unsigned int rate) +{ + int main_scalar; + 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_scalar = clock_calc_best_scalar(4, 8, 400000000, rate, &fine); + if (main_scalar < 0) { + printk(BIOS_DEBUG, "%s: Cannot set clock rate for periph %d", + __func__, periph_id); + return -1; + } + clock_ll_set_ratio(periph_id, main_scalar - 1); + clock_ll_set_pre_ratio(periph_id, fine - 1); + break; + default: + printk(BIOS_DEBUG, "%s: Unsupported peripheral ID %d\n", __func__, + periph_id); + return -1; + } + + return 0; +} + +int clock_set_mshci(enum periph_id peripheral) +{ + 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 = &exynos_clock->div_fsys1; + break; + case PERIPH_ID_SDMMC2: + addr = &exynos_clock->div_fsys2; + break; + default: + printk(BIOS_DEBUG, "invalid peripheral\n"); + return -1; + } + tmp = readl(addr) & ~0xff0f; + for (i = 0; i <= 0xf; i++) { + if ((clock / (i + 1)) <= 400) { + writel(tmp | i << 0, addr); + break; + } + } + return 0; +} + +int clock_epll_set_rate(unsigned long rate) +{ + unsigned int epll_con, epll_con_k; + unsigned int i; + unsigned int lockcnt; + struct mono_time current, end; + + epll_con = readl(&exynos_clock->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 generate 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, &exynos_clock->epll_lock); + writel(epll_con, &exynos_clock->epll_con0); + writel(epll_con_k, &exynos_clock->epll_con1); + + timer_monotonic_get(¤t); + end = current; + mono_time_add_msecs(&end, TIMEOUT_EPLL_LOCK); + + while (!(readl(&exynos_clock->epll_con0) & + (0x1 << EXYNOS5_EPLLCON0_LOCKED_SHIFT))) { + if (mono_time_after(¤t, &end)) { + printk(BIOS_DEBUG, + "%s: Timeout waiting for EPLL lock\n", + __func__); + return -1; + } + timer_monotonic_get(¤t); + } + + return 0; +} + +void clock_select_i2s_clk_source(void) +{ + clrsetbits_le32(&exynos_clock->src_peric1, AUDIO1_SEL_MASK, + (CLK_SRC_SCLK_EPLL)); +} + +int clock_set_i2s_clk_prescaler(unsigned int src_frq, unsigned int dst_frq) +{ + unsigned int div ; + + if ((dst_frq == 0) || (src_frq == 0)) { + printk(BIOS_DEBUG, "%s: Invalid frequency input for prescaler\n", __func__); + printk(BIOS_DEBUG, "src frq = %d des frq = %d ", src_frq, dst_frq); + return -1; + } + + div = (src_frq / dst_frq); + if (div > AUDIO_1_RATIO_MASK) { + printk(BIOS_DEBUG, "%s: Frequency ratio is out of range\n", __func__); + printk(BIOS_DEBUG, "src frq = %d des frq = %d ", src_frq, dst_frq); + return -1; + } + clrsetbits_le32(&exynos_clock->div_peric4, AUDIO_1_RATIO_MASK, + (div & AUDIO_1_RATIO_MASK)); + return 0; +} diff --git a/src/soc/samsung/exynos5250/clock_init.c b/src/soc/samsung/exynos5250/clock_init.c new file mode 100644 index 0000000000..c8cf3babfb --- /dev/null +++ b/src/soc/samsung/exynos5250/clock_init.c @@ -0,0 +1,442 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2012 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* Clock setup for SMDK5250 board based on EXYNOS5 */ + +#include <delay.h> +#include <console/console.h> +#include "clk.h" +#include "dp.h" +#include "setup.h" + +void system_clock_init(struct mem_timings *mem, + struct arm_clk_ratios *arm_clk_ratio) +{ + u32 val, tmp; + + /* Turn on the MCT as early as possible. */ + exynos_mct->g_tcon |= (1 << 8); + + clrbits_le32(&exynos_clock->src_cpu, MUX_APLL_SEL_MASK); + do { + val = readl(&exynos_clock->mux_stat_cpu); + } while ((val | MUX_APLL_SEL_MASK) != val); + + clrbits_le32(&exynos_clock->src_core1, MUX_MPLL_SEL_MASK); + do { + val = readl(&exynos_clock->mux_stat_core1); + } while ((val | MUX_MPLL_SEL_MASK) != val); + + clrbits_le32(&exynos_clock->src_top2, MUX_CPLL_SEL_MASK); + clrbits_le32(&exynos_clock->src_top2, MUX_EPLL_SEL_MASK); + clrbits_le32(&exynos_clock->src_top2, MUX_VPLL_SEL_MASK); + clrbits_le32(&exynos_clock->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(&exynos_clock->mux_stat_top2); + } while ((val | tmp) != val); + + clrbits_le32(&exynos_clock->src_cdrex, MUX_BPLL_SEL_MASK); + do { + val = readl(&exynos_clock->mux_stat_cdrex); + } while ((val | MUX_BPLL_SEL_MASK) != val); + + /* PLL locktime */ + writel(APLL_LOCK_VAL, &exynos_clock->apll_lock); + + writel(MPLL_LOCK_VAL, &exynos_clock->mpll_lock); + + writel(BPLL_LOCK_VAL, &exynos_clock->bpll_lock); + + writel(CPLL_LOCK_VAL, &exynos_clock->cpll_lock); + + writel(GPLL_LOCK_VAL, &exynos_clock->gpll_lock); + + writel(EPLL_LOCK_VAL, &exynos_clock->epll_lock); + + writel(VPLL_LOCK_VAL, &exynos_clock->vpll_lock); + + writel(CLK_REG_DISABLE, &exynos_clock->pll_div2_sel); + + writel(MUX_HPM_SEL_MASK, &exynos_clock->src_cpu); + do { + val = readl(&exynos_clock->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, &exynos_clock->div_cpu0); + do { + val = readl(&exynos_clock->div_stat_cpu0); + } while (0 != val); + + writel(CLK_DIV_CPU1_VAL, &exynos_clock->div_cpu1); + do { + val = readl(&exynos_clock->div_stat_cpu1); + } while (0 != val); + + /* switch A15 clock source to OSC clock before changing APLL */ + clrbits_le32(&exynos_clock->src_cpu, APLL_FOUT); + + /* Set APLL */ + writel(APLL_CON1_VAL, &exynos_clock->apll_con1); + val = set_pll(arm_clk_ratio->apll_mdiv, arm_clk_ratio->apll_pdiv, + arm_clk_ratio->apll_sdiv); + writel(val, &exynos_clock->apll_con0); + while ((readl(&exynos_clock->apll_con0) & APLL_CON0_LOCKED) == 0) + ; + + /* now it is safe to switch to APLL */ + setbits_le32(&exynos_clock->src_cpu, APLL_FOUT); + + /* Set MPLL */ + writel(MPLL_CON1_VAL, &exynos_clock->mpll_con1); + val = set_pll(mem->mpll_mdiv, mem->mpll_pdiv, mem->mpll_sdiv); + writel(val, &exynos_clock->mpll_con0); + while ((readl(&exynos_clock->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(&exynos_clock->pll_div2_sel, MUX_MPLL_FOUT_SEL); + + /* Set BPLL */ + if (mem->use_bpll) { + writel(BPLL_CON1_VAL, &exynos_clock->bpll_con1); + val = set_pll(mem->bpll_mdiv, mem->bpll_pdiv, mem->bpll_sdiv); + writel(val, &exynos_clock->bpll_con0); + while ((readl(&exynos_clock->bpll_con0) & BPLL_CON0_LOCKED) == 0) + ; + + setbits_le32(&exynos_clock->pll_div2_sel, MUX_BPLL_FOUT_SEL); + } + + /* Set CPLL */ + writel(CPLL_CON1_VAL, &exynos_clock->cpll_con1); + val = set_pll(mem->cpll_mdiv, mem->cpll_pdiv, mem->cpll_sdiv); + writel(val, &exynos_clock->cpll_con0); + while ((readl(&exynos_clock->cpll_con0) & CPLL_CON0_LOCKED) == 0) + ; + + /* Set GPLL */ + writel(GPLL_CON1_VAL, &exynos_clock->gpll_con1); + val = set_pll(mem->gpll_mdiv, mem->gpll_pdiv, mem->gpll_sdiv); + writel(val, &exynos_clock->gpll_con0); + while ((readl(&exynos_clock->gpll_con0) & GPLL_CON0_LOCKED) == 0) + ; + + /* Set EPLL */ + writel(EPLL_CON2_VAL, &exynos_clock->epll_con2); + writel(EPLL_CON1_VAL, &exynos_clock->epll_con1); + val = set_pll(mem->epll_mdiv, mem->epll_pdiv, mem->epll_sdiv); + writel(val, &exynos_clock->epll_con0); + while ((readl(&exynos_clock->epll_con0) & EPLL_CON0_LOCKED) == 0) + ; + + /* Set VPLL */ + writel(VPLL_CON2_VAL, &exynos_clock->vpll_con2); + writel(VPLL_CON1_VAL, &exynos_clock->vpll_con1); + val = set_pll(mem->vpll_mdiv, mem->vpll_pdiv, mem->vpll_sdiv); + writel(val, &exynos_clock->vpll_con0); + while ((readl(&exynos_clock->vpll_con0) & VPLL_CON0_LOCKED) == 0) + ; + + writel(CLK_SRC_CORE0_VAL, &exynos_clock->src_core0); + writel(CLK_DIV_CORE0_VAL, &exynos_clock->div_core0); + while (readl(&exynos_clock->div_stat_core0) != 0) + ; + + writel(CLK_DIV_CORE1_VAL, &exynos_clock->div_core1); + while (readl(&exynos_clock->div_stat_core1) != 0) + ; + + writel(CLK_DIV_SYSRGT_VAL, &exynos_clock->div_sysrgt); + while (readl(&exynos_clock->div_stat_sysrgt) != 0) + ; + + writel(CLK_DIV_ACP_VAL, &exynos_clock->div_acp); + while (readl(&exynos_clock->div_stat_acp) != 0) + ; + + writel(CLK_DIV_SYSLFT_VAL, &exynos_clock->div_syslft); + while (readl(&exynos_clock->div_stat_syslft) != 0) + ; + + writel(CLK_SRC_TOP0_VAL, &exynos_clock->src_top0); + writel(CLK_SRC_TOP1_VAL, &exynos_clock->src_top1); + writel(TOP2_VAL, &exynos_clock->src_top2); + writel(CLK_SRC_TOP3_VAL, &exynos_clock->src_top3); + + writel(CLK_DIV_TOP0_VAL, &exynos_clock->div_top0); + while (readl(&exynos_clock->div_stat_top0)) + ; + + writel(CLK_DIV_TOP1_VAL, &exynos_clock->div_top1); + while (readl(&exynos_clock->div_stat_top1)) + ; + + writel(CLK_SRC_LEX_VAL, &exynos_clock->src_lex); + while (1) { + val = readl(&exynos_clock->mux_stat_lex); + if (val == (val | 1)) + break; + } + + writel(CLK_DIV_LEX_VAL, &exynos_clock->div_lex); + while (readl(&exynos_clock->div_stat_lex)) + ; + + writel(CLK_DIV_R0X_VAL, &exynos_clock->div_r0x); + while (readl(&exynos_clock->div_stat_r0x)) + ; + + writel(CLK_DIV_R0X_VAL, &exynos_clock->div_r0x); + while (readl(&exynos_clock->div_stat_r0x)) + ; + + writel(CLK_DIV_R1X_VAL, &exynos_clock->div_r1x); + while (readl(&exynos_clock->div_stat_r1x)) + ; + + if (mem->use_bpll) { + writel(MUX_BPLL_SEL_MASK | MUX_MCLK_CDREX_SEL | + MUX_MCLK_DPHY_SEL, &exynos_clock->src_cdrex); + } else { + writel(CLK_REG_DISABLE, &exynos_clock->src_cdrex); + } + + writel(CLK_DIV_CDREX_VAL, &exynos_clock->div_cdrex); + while (readl(&exynos_clock->div_stat_cdrex)) + ; + + val = readl(&exynos_clock->src_cpu); + val |= CLK_SRC_CPU_VAL; + writel(val, &exynos_clock->src_cpu); + + val = readl(&exynos_clock->src_top2); + val |= CLK_SRC_TOP2_VAL; + writel(val, &exynos_clock->src_top2); + + val = readl(&exynos_clock->src_core1); + val |= CLK_SRC_CORE1_VAL; + writel(val, &exynos_clock->src_core1); + + writel(CLK_SRC_FSYS0_VAL, &exynos_clock->src_fsys); + writel(CLK_DIV_FSYS0_VAL, &exynos_clock->div_fsys0); + while (readl(&exynos_clock->div_stat_fsys0)) + ; + + writel(CLK_REG_DISABLE, &exynos_clock->clkout_cmu_cpu); + writel(CLK_REG_DISABLE, &exynos_clock->clkout_cmu_core); + writel(CLK_REG_DISABLE, &exynos_clock->clkout_cmu_acp); + writel(CLK_REG_DISABLE, &exynos_clock->clkout_cmu_top); + writel(CLK_REG_DISABLE, &exynos_clock->clkout_cmu_lex); + writel(CLK_REG_DISABLE, &exynos_clock->clkout_cmu_r0x); + writel(CLK_REG_DISABLE, &exynos_clock->clkout_cmu_r1x); + writel(CLK_REG_DISABLE, &exynos_clock->clkout_cmu_cdrex); + + writel(CLK_SRC_PERIC0_VAL, &exynos_clock->src_peric0); + writel(CLK_DIV_PERIC0_VAL, &exynos_clock->div_peric0); + + writel(CLK_SRC_PERIC1_VAL, &exynos_clock->src_peric1); + writel(CLK_DIV_PERIC1_VAL, &exynos_clock->div_peric1); + writel(CLK_DIV_PERIC2_VAL, &exynos_clock->div_peric2); + writel(SCLK_SRC_ISP_VAL, &exynos_clock->sclk_src_isp); + writel(SCLK_DIV_ISP_VAL, &exynos_clock->sclk_div_isp); + writel(CLK_DIV_ISP0_VAL, &exynos_clock->div_isp0); + writel(CLK_DIV_ISP1_VAL, &exynos_clock->div_isp1); + writel(CLK_DIV_ISP2_VAL, &exynos_clock->div_isp2); + + /* FIMD1 SRC CLK SELECTION */ + writel(CLK_SRC_DISP1_0_VAL, &exynos_clock->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, &exynos_clock->div_fsys2); +} + +void clock_gate(void) +{ + /* CLK_GATE_IP_SYSRGT */ + clrbits_le32(&exynos_clock->gate_ip_sysrgt, CLK_C2C_MASK); + + /* CLK_GATE_IP_ACP */ + clrbits_le32(&exynos_clock->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(&exynos_clock->gate_bus_syslft, CLK_EFCLK_MASK); + + /* CLK_GATE_IP_ISP0 */ + clrbits_le32(&exynos_clock->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(&exynos_clock->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(&exynos_clock->gate_sclk_isp, SCLK_MPWM_ISP_MASK); + + /* CLK_GATE_IP_GSCL */ + clrbits_le32(&exynos_clock->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(&exynos_clock->gate_ip_disp1, CLK_SMMUTVX_MASK | + CLK_ASYNCTVX_MASK | + CLK_HDMI_MASK | + CLK_MIXER_MASK | + CLK_DSIM1_MASK); + + /* CLK_GATE_IP_MFC */ + clrbits_le32(&exynos_clock->gate_ip_mfc, CLK_SMMUMFCR_MASK | + CLK_SMMUMFCL_MASK | + CLK_MFC_MASK); + + /* CLK_GATE_IP_GEN */ + clrbits_le32(&exynos_clock->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(&exynos_clock->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(&exynos_clock->gate_ip_peric, CLK_HS_I2C3_MASK | + CLK_HS_I2C2_MASK | + CLK_HS_I2C1_MASK | + CLK_HS_I2C0_MASK | + CLK_AC97_MASK | + CLK_SPDIF_MASK | + CLK_PCM2_MASK | + CLK_PCM1_MASK | + CLK_I2S2_MASK | + CLK_SPI2_MASK | + CLK_SPI0_MASK); + + /* + * CLK_GATE_IP_PERIS + * Note: Keep CHIPID_APBIF ungated to ensure reading the product ID + * register (PRO_ID) works correctly when the OS kernel determines + * which chip it is running on. + */ + clrbits_le32(&exynos_clock->gate_ip_peris, CLK_RTC_MASK | + CLK_TZPC9_MASK | + CLK_TZPC8_MASK | + CLK_TZPC7_MASK | + CLK_TZPC6_MASK | + CLK_TZPC5_MASK | + CLK_TZPC4_MASK | + CLK_TZPC3_MASK | + CLK_TZPC2_MASK | + CLK_TZPC1_MASK | + CLK_TZPC0_MASK); + + /* CLK_GATE_BLOCK */ + clrbits_le32(&exynos_clock->gate_block, CLK_ACP_MASK); + + /* CLK_GATE_IP_CDREX */ + clrbits_le32(&exynos_clock->gate_ip_cdrex, CLK_DPHY0_MASK | + CLK_DPHY1_MASK | + CLK_TZASC_DRBXR_MASK); + +} + +void clock_init_dp_clock(void) +{ + /* DP clock enable */ + setbits_le32(&exynos_clock->gate_ip_disp1, CLK_GATE_DP1_ALLOW); + + /* We run DP at 267 Mhz */ + setbits_le32(&exynos_clock->div_disp1_0, CLK_DIV_DISP1_0_FIMD1); +} diff --git a/src/soc/samsung/exynos5250/cpu.c b/src/soc/samsung/exynos5250/cpu.c new file mode 100644 index 0000000000..6b3ee8bc0b --- /dev/null +++ b/src/soc/samsung/exynos5250/cpu.c @@ -0,0 +1,167 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2013 Google Inc. + * Copyright (C) 2012 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <stdlib.h> +#include <string.h> +#include <stddef.h> +#include <delay.h> +#include <console/console.h> +#include <device/device.h> +#include <cbmem.h> +#include <arch/cache.h> +#include "fimd.h" +#include "dp-core.h" +#include "cpu.h" +#include "clk.h" +#include "chip.h" + +static unsigned int cpu_id; +static unsigned int cpu_rev; + +static void set_cpu_id(void) +{ + cpu_id = readl((void *)EXYNOS5_PRO_ID); + cpu_id = (0xC000 | ((cpu_id & 0x00FFF000) >> 12)); + + /* + * 0xC200: EXYNOS4210 EVT0 + * 0xC210: EXYNOS4210 EVT1 + */ + if (cpu_id == 0xC200) { + cpu_id |= 0x10; + cpu_rev = 0; + } else if (cpu_id == 0xC210) { + cpu_rev = 1; + } +} + +/* we distinguish a display port device from a raw graphics device + * because there are dramatic differences in startup depending on + * graphics usage. To make startup fast and easier to understand and + * debug we explicitly name this common case. The alternate approach, + * involving lots of machine and callbacks, is hard to debug and + * verify. + */ +static void exynos_displayport_init(device_t dev, u32 lcdbase, + unsigned long fb_size) +{ + int ret; + struct soc_samsung_exynos5250_config *conf = dev->chip_info; + /* put these on the stack. If, at some point, we want to move + * this code to a pre-ram stage, it will be much easier. + */ + struct exynos5_fimd_panel panel; + memset(&panel, 0, sizeof(panel)); + + panel.is_dp = 1; /* Display I/F is eDP */ + /* while it is true that we did a memset to zero, + * we leave some 'set to zero' entries here to make + * it clear what's going on. Graphics is confusing. + */ + panel.is_mipi = 0; + panel.fixvclk = 0; + panel.ivclk = 0; + panel.clkval_f = conf->clkval_f; + panel.upper_margin = conf->upper_margin; + panel.lower_margin = conf->lower_margin; + panel.vsync = conf->vsync; + panel.left_margin = conf->left_margin; + panel.right_margin = conf->right_margin; + panel.hsync = conf->hsync; + panel.xres = conf->xres; + panel.yres = conf->yres; + + printk(BIOS_SPEW, "LCD framebuffer @%p\n", (void *)(lcdbase)); + memset((void *)lcdbase, 0, fb_size); /* clear the framebuffer */ + + /* + * We need to clean and invalidate the framebuffer region and disable + * caching as well. We assume that our dcache <--> memory address + * space is identity-mapped in 1MB chunks, so align accordingly. + * + * Note: We may want to do something clever to ensure the framebuffer + * region is aligned such that we don't change dcache policy for other + * stuff inadvertently. + */ + uint32_t lower = ALIGN_DOWN(lcdbase, MiB); + uint32_t upper = ALIGN_UP(lcdbase + fb_size, MiB); + + dcache_clean_invalidate_by_mva(lower, upper - lower); + mmu_config_range(lower / MiB, (upper - lower) / MiB, DCACHE_OFF); + + printk(BIOS_DEBUG, "Initializing Exynos LCD.\n"); + + ret = lcd_ctrl_init(fb_size, &panel, (void *)lcdbase); +} + +static void cpu_enable(device_t dev) +{ + unsigned long fb_size = FB_SIZE_KB * KiB; + u32 lcdbase = get_fb_base_kb() * KiB; + + ram_resource(dev, 0, RAM_BASE_KB, RAM_SIZE_KB - FB_SIZE_KB); + mmio_resource(dev, 1, lcdbase / KiB, CEIL_DIV(fb_size, KiB)); + + exynos_displayport_init(dev, lcdbase, fb_size); + + set_cpu_id(); +} + +static void cpu_init(device_t dev) +{ + printk(BIOS_INFO, "CPU: S5P%X @ %ldMHz\n", + cpu_id, get_arm_clk() / (1024*1024)); +} + +static void cpu_noop(device_t dev) +{ +} + +static struct device_operations cpu_ops = { + .read_resources = cpu_noop, + .set_resources = cpu_noop, + .enable_resources = cpu_enable, + .init = cpu_init, + .scan_bus = 0, +}; + +static void enable_exynos5250_dev(device_t dev) +{ + dev->ops = &cpu_ops; +} + +struct chip_operations cpu_samsung_exynos5250_ops = { + CHIP_NAME("CPU Samsung Exynos 5250") + .enable_dev = enable_exynos5250_dev, +}; + +void exynos5250_config_l2_cache(void) +{ + uint32_t val; + + /* + * Bit 9 - L2 tag RAM setup (1 cycle) + * Bits 8:6 - L2 tag RAM latency (3 cycles) + * Bit 5 - L2 data RAM setup (1 cycle) + * Bits 2:0 - L2 data RAM latency (3 cycles) + */ + val = (1 << 9) | (0x2 << 6) | (1 << 5) | (0x2); + write_l2ctlr(val); +} diff --git a/src/soc/samsung/exynos5250/cpu.h b/src/soc/samsung/exynos5250/cpu.h new file mode 100644 index 0000000000..10f9ef4d21 --- /dev/null +++ b/src/soc/samsung/exynos5250/cpu.h @@ -0,0 +1,87 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2010 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef CPU_SAMSUNG_EXYNOS5250_CPU_H +#define CPU_SAMSUNG_EXYNOS5250_CPU_H + +#include <arch/io.h> + +/* Base address registers */ +#define EXYNOS5_GPIO_PART6_BASE 0x03860000 /* Z<6:0> */ +#define EXYNOS5_PRO_ID 0x10000000 +#define EXYNOS5_CLOCK_BASE 0x10010000 +#define EXYNOS5_POWER_BASE 0x10040000 +#define EXYNOS5_SYSREG_BASE 0x10050000 +#define EXYNOS5_TZPC1_DECPROT1SET 0x10110810 +#define EXYNOS5_MULTI_CORE_TIMER_BASE 0x101C0000 +#define EXYNOS5_WATCHDOG_BASE 0x101D0000 +#define EXYNOS5_ACE_SFR_BASE 0x10830000 +#define EXYNOS5_DMC_PHY0_BASE 0x10C00000 +#define EXYNOS5_DMC_PHY1_BASE 0x10C10000 +#define EXYNOS5_GPIO_PART4_BASE 0x10D10000 /* V00..V37 */ +#define EXYNOS5_GPIO_PART5_BASE 0x10D100C0 /* V40..V47 */ +#define EXYNOS5_DMC_CTRL_BASE 0x10DD0000 +#define EXYNOS5_GPIO_PART1_BASE 0x11400000 /* A00..Y67 */ +#define EXYNOS5_GPIO_PART2_BASE 0x11400c00 /* X00..X37 */ +#define EXYNOS5_USB_DRD_XHCI_BASE 0x12000000 +#define EXYNOS5_USB_DRD_PHY_BASE 0x12100000 +#define EXYNOS5_USB_DRD_DWC3_BASE 0x1200C100 +#define EXYNOS5_USB_HOST_EHCI_BASE 0x12110000 +#define EXYNOS5_USB_HOST_PHY_BASE 0x12130000 +#define EXYNOS5_MMC_BASE 0x12200000 +#define EXYNOS5_MSHC_BASE 0x12240000 +#define EXYNOS5_SROMC_BASE 0x12250000 +#define EXYNOS5_UART0_BASE 0x12C00000 +#define EXYNOS5_UART1_BASE 0x12C10000 +#define EXYNOS5_UART2_BASE 0x12C20000 +#define EXYNOS5_UART3_BASE 0x12C30000 +#define EXYNOS5_I2C_BASE 0x12C60000 +#define EXYNOS5_SPI0_BASE 0x12D20000 +#define EXYNOS5_SPI1_BASE 0x12D30000 +#define EXYNOS5_I2S_BASE 0x12D60000 +#define EXYNOS5_UART_ISP_BASE 0x13190000 +#define EXYNOS5_SPI_ISP_BASE 0x131A0000 +#define EXYNOS5_GPIO_PART3_BASE 0x13400000 /* E00..H17 */ +#define EXYNOS5_FIMD_BASE 0x14400000 +#define EXYNOS5_DISP1_CTRL_BASE 0x14420000 +#define EXYNOS5_MIPI_DSI1_BASE 0x14500000 +#define EXYNOS5_DP0_BASE 0x14510000 +#define EXYNOS5_DP1_BASE 0x145B0000 + +/* Marker values stored at the bottom of IRAM stack by SPL */ +#define EXYNOS5_SPL_MARKER 0xb004f1a9 /* hexspeak word: bootflag */ + +#define EXYNOS5_SPI_NUM_CONTROLLERS 5 +#define EXYNOS_I2C_MAX_CONTROLLERS 8 + +void exynos5250_config_l2_cache(void); + +extern struct tmu_info exynos5250_tmu_info; + +/* TODO clean up defines. */ +#define FB_SIZE_KB 4096 +#define RAM_BASE_KB (CONFIG_SYS_SDRAM_BASE >> 10) +#define RAM_SIZE_KB (CONFIG_DRAM_SIZE_MB << 10UL) + +static inline u32 get_fb_base_kb(void) +{ + return RAM_BASE_KB + RAM_SIZE_KB - FB_SIZE_KB; +} + +#endif /* _EXYNOS5250_CPU_H */ diff --git a/src/soc/samsung/exynos5250/dmc.h b/src/soc/samsung/exynos5250/dmc.h new file mode 100644 index 0000000000..6388316c5e --- /dev/null +++ b/src/soc/samsung/exynos5250/dmc.h @@ -0,0 +1,353 @@ +/* + * This file is part of the coreboot project. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef CPU_SAMSUNG_EXYNOS5250_DMC_H +#define CPU_SAMSUNG_EXYNOS5250_DMC_H + +#ifndef __ASSEMBLER__ + +#include "cpu.h" + +struct exynos5_dmc { + unsigned int concontrol; + unsigned int memcontrol; + unsigned int memconfig0; + unsigned int memconfig1; + unsigned int directcmd; + unsigned int prechconfig; + unsigned int phycontrol0; + unsigned char res1[0xc]; + unsigned int pwrdnconfig; + unsigned int timingpzq; + unsigned int timingref; + unsigned int timingrow; + unsigned int timingdata; + unsigned int timingpower; + unsigned int phystatus; + unsigned char res2[0x4]; + unsigned int chipstatus_ch0; + unsigned int chipstatus_ch1; + unsigned char res3[0x4]; + unsigned int mrstatus; + unsigned char res4[0x8]; + unsigned int qoscontrol0; + unsigned char resr5[0x4]; + unsigned int qoscontrol1; + unsigned char res6[0x4]; + unsigned int qoscontrol2; + unsigned char res7[0x4]; + unsigned int qoscontrol3; + unsigned char res8[0x4]; + unsigned int qoscontrol4; + unsigned char res9[0x4]; + unsigned int qoscontrol5; + unsigned char res10[0x4]; + unsigned int qoscontrol6; + unsigned char res11[0x4]; + unsigned int qoscontrol7; + unsigned char res12[0x4]; + unsigned int qoscontrol8; + unsigned char res13[0x4]; + unsigned int qoscontrol9; + unsigned char res14[0x4]; + unsigned int qoscontrol10; + unsigned char res15[0x4]; + unsigned int qoscontrol11; + unsigned char res16[0x4]; + unsigned int qoscontrol12; + unsigned char res17[0x4]; + unsigned int qoscontrol13; + unsigned char res18[0x4]; + unsigned int qoscontrol14; + unsigned char res19[0x4]; + unsigned int qoscontrol15; + unsigned char res20[0x14]; + unsigned int ivcontrol; + unsigned int wrtra_config; + unsigned int rdlvl_config; + unsigned char res21[0x8]; + unsigned int brbrsvconfig; + unsigned int brbqosconfig; + unsigned int membaseconfig0; + unsigned int membaseconfig1; + unsigned char res22[0xc]; + unsigned int wrlvl_config; + unsigned char res23[0xc]; + unsigned int perevcontrol; + unsigned int perev0config; + unsigned int perev1config; + unsigned int perev2config; + unsigned int perev3config; + unsigned char res24[0xdebc]; + unsigned int pmnc_ppc_a; + unsigned char res25[0xc]; + unsigned int cntens_ppc_a; + unsigned char res26[0xc]; + unsigned int cntenc_ppc_a; + unsigned char res27[0xc]; + unsigned int intens_ppc_a; + unsigned char res28[0xc]; + unsigned int intenc_ppc_a; + unsigned char res29[0xc]; + unsigned int flag_ppc_a; + unsigned char res30[0xac]; + unsigned int ccnt_ppc_a; + unsigned char res31[0xc]; + unsigned int pmcnt0_ppc_a; + unsigned char res32[0xc]; + unsigned int pmcnt1_ppc_a; + unsigned char res33[0xc]; + unsigned int pmcnt2_ppc_a; + unsigned char res34[0xc]; + unsigned int pmcnt3_ppc_a; +}; + +static struct exynos5_dmc * const exynos_dmc = (void *)EXYNOS5_DMC_CTRL_BASE; + +struct exynos5_phy_control { + unsigned int phy_con0; + unsigned int phy_con1; + unsigned int phy_con2; + unsigned int phy_con3; + unsigned int phy_con4; + unsigned char res1[4]; + unsigned int phy_con6; + unsigned char res2[4]; + unsigned int phy_con8; + unsigned int phy_con9; + unsigned int phy_con10; + unsigned char res3[4]; + unsigned int phy_con12; + unsigned int phy_con13; + unsigned int phy_con14; + unsigned int phy_con15; + unsigned int phy_con16; + unsigned char res4[4]; + unsigned int phy_con17; + unsigned int phy_con18; + unsigned int phy_con19; + unsigned int phy_con20; + unsigned int phy_con21; + unsigned int phy_con22; + unsigned int phy_con23; + unsigned int phy_con24; + unsigned int phy_con25; + unsigned int phy_con26; + unsigned int phy_con27; + unsigned int phy_con28; + unsigned int phy_con29; + unsigned int phy_con30; + unsigned int phy_con31; + unsigned int phy_con32; + unsigned int phy_con33; + unsigned int phy_con34; + unsigned int phy_con35; + unsigned int phy_con36; + unsigned int phy_con37; + unsigned int phy_con38; + unsigned int phy_con39; + unsigned int phy_con40; + unsigned int phy_con41; + unsigned int phy_con42; +}; + +static struct exynos5_phy_control * const exynos_phy0_control = + (void *)EXYNOS5_DMC_PHY0_BASE; +static struct exynos5_phy_control * const exynos_phy1_control = + (void *)EXYNOS5_DMC_PHY1_BASE; + +enum ddr_mode { + DDR_MODE_DDR2, + DDR_MODE_DDR3, + DDR_MODE_LPDDR2, + DDR_MODE_LPDDR3, + + DDR_MODE_COUNT, +}; + +/* For reasons unknown, people are in the habit of taking a 32-bit + * field with 2 possible values and packing it with, say, 2 bits. A + * non-robust encoding, using only 2 bits of a 32-bit field, is + * incredibly difficult to deal with when things go wrong, because + * there are a lot of things that get expressed as 0, 1, or 2. If + * you're scanning with jtag or dumping memory it is really hard to + * tell when you've hit the beginning of the struct. So, let's be a + * bit smart here. First, while it's common to let the enum count + * entries for you, when there are two of them, we can do the + * counting. And, let's set the values to something we can easily scan + * for in memory. Since '1' and '2' are rather common, we pick + * something that's actually of some value when things go wrong. This + * setup motivated by a use case: something's going wrong and having a + * manuf name of '1' or '2' is completely useless! + */ +enum mem_manuf { + MEM_MANUF_AUTODETECT, + MEM_MANUF_ELPIDA = 0xe7b1da, + MEM_MANUF_SAMSUNG = 0x5a5096, + + MEM_MANUF_COUNT = 2, // fancy that. +}; + +enum { + MEM_TIMINGS_MSR_COUNT = 4, +}; + +#define DMC_INTERLEAVE_SIZE 0x1f + +/* CONCONTROL register fields */ +#define CONCONTROL_DFI_INIT_START_SHIFT 28 +#define CONCONTROL_RD_FETCH_SHIFT 12 +#define CONCONTROL_RD_FETCH_MASK (0x7 << CONCONTROL_RD_FETCH_SHIFT) +#define CONCONTROL_AREF_EN_SHIFT 5 + +/* PRECHCONFIG register field */ +#define PRECHCONFIG_TP_CNT_SHIFT 24 + +/* PWRDNCONFIG register field */ +#define PWRDNCONFIG_DPWRDN_CYC_SHIFT 0 +#define PWRDNCONFIG_DSREF_CYC_SHIFT 16 + +/* PHY_CON0 register fields */ +#define PHY_CON0_T_WRRDCMD_SHIFT 17 +#define PHY_CON0_T_WRRDCMD_MASK (0x7 << PHY_CON0_T_WRRDCMD_SHIFT) +#define PHY_CON0_CTRL_DDR_MODE_SHIFT 11 + +/* PHY_CON1 register fields */ +#define PHY_CON1_RDLVL_RDDATA_ADJ_SHIFT 0 + +/* PHY_CON12 register fields */ +#define PHY_CON12_CTRL_START_POINT_SHIFT 24 +#define PHY_CON12_CTRL_INC_SHIFT 16 +#define PHY_CON12_CTRL_FORCE_SHIFT 8 +#define PHY_CON12_CTRL_START_SHIFT 6 +#define PHY_CON12_CTRL_START_MASK (1 << PHY_CON12_CTRL_START_SHIFT) +#define PHY_CON12_CTRL_DLL_ON_SHIFT 5 +#define PHY_CON12_CTRL_DLL_ON_MASK (1 << PHY_CON12_CTRL_DLL_ON_SHIFT) +#define PHY_CON12_CTRL_REF_SHIFT 1 + +/* PHY_CON16 register fields */ +#define PHY_CON16_ZQ_MODE_DDS_SHIFT 24 +#define PHY_CON16_ZQ_MODE_DDS_MASK (0x7 << PHY_CON16_ZQ_MODE_DDS_SHIFT) + +#define PHY_CON16_ZQ_MODE_TERM_SHIFT 21 +#define PHY_CON16_ZQ_MODE_TERM_MASK (0x7 << PHY_CON16_ZQ_MODE_TERM_SHIFT) + +#define PHY_CON16_ZQ_MODE_NOTERM_MASK (1 << 19) + +/* PHY_CON42 register fields */ +#define PHY_CON42_CTRL_BSTLEN_SHIFT 8 +#define PHY_CON42_CTRL_BSTLEN_MASK (0xff << PHY_CON42_CTRL_BSTLEN_SHIFT) + +#define PHY_CON42_CTRL_RDLAT_SHIFT 0 +#define PHY_CON42_CTRL_RDLAT_MASK (0x1f << PHY_CON42_CTRL_RDLAT_SHIFT) + +/* These are the memory timings for a particular memory type and speed */ +struct mem_timings { + enum mem_manuf mem_manuf; /* Memory manufacturer */ + enum ddr_mode mem_type; /* Memory type */ + unsigned int frequency_mhz; /* Frequency of memory in MHz */ + + /* Here follow the timing parameters for the selected memory */ + uint8_t apll_mdiv; + uint8_t apll_pdiv; + uint8_t apll_sdiv; + uint8_t mpll_mdiv; + uint8_t mpll_pdiv; + uint8_t mpll_sdiv; + uint8_t cpll_mdiv; + uint8_t cpll_pdiv; + uint8_t cpll_sdiv; + uint8_t gpll_pdiv; + uint16_t gpll_mdiv; + uint8_t gpll_sdiv; + uint8_t epll_mdiv; + uint8_t epll_pdiv; + uint8_t epll_sdiv; + uint8_t vpll_mdiv; + uint8_t vpll_pdiv; + uint8_t vpll_sdiv; + uint8_t bpll_mdiv; + uint8_t bpll_pdiv; + uint8_t bpll_sdiv; + uint8_t use_bpll; /* 1 to use BPLL for cdrex, 0 to use MPLL */ + uint8_t pclk_cdrex_ratio; + unsigned int direct_cmd_msr[MEM_TIMINGS_MSR_COUNT]; + + unsigned int timing_ref; + unsigned int timing_row; + unsigned int timing_data; + unsigned int timing_power; + + /* DQS, DQ, DEBUG offsets */ + unsigned int phy0_dqs; + unsigned int phy1_dqs; + unsigned int phy0_dq; + unsigned int phy1_dq; + uint8_t phy0_tFS; + uint8_t phy1_tFS; + uint8_t phy0_pulld_dqs; + uint8_t phy1_pulld_dqs; + + uint8_t lpddr3_ctrl_phy_reset; + uint8_t ctrl_start_point; + uint8_t ctrl_inc; + uint8_t ctrl_start; + uint8_t ctrl_dll_on; + uint8_t ctrl_ref; + + uint8_t ctrl_force; + uint8_t ctrl_rdlat; + uint8_t ctrl_bstlen; + + uint8_t fp_resync; + uint8_t iv_size; + uint8_t dfi_init_start; + uint8_t aref_en; + + uint8_t rd_fetch; + + uint8_t zq_mode_dds; + uint8_t zq_mode_term; + uint8_t zq_mode_noterm; /* 1 to allow termination disable */ + + unsigned int memcontrol; + unsigned int memconfig; + + unsigned int membaseconfig0; + unsigned int membaseconfig1; + unsigned int prechconfig_tp_cnt; + unsigned int dpwrdn_cyc; + unsigned int dsref_cyc; + unsigned int concontrol; + /* Channel and Chip Selection */ + uint8_t dmc_channels; /* number of memory channels */ + uint8_t chips_per_channel; /* number of chips per channel */ + uint8_t chips_to_configure; /* number of chips to configure */ + uint8_t send_zq_init; /* 1 to send this command */ + unsigned int impedance; /* drive strength impedance */ + uint8_t gate_leveling_enable; /* check gate leveling is enabled */ +}; + +/** + * Get the correct memory timings for our selected memory type and speed. + * + * @return pointer to the memory timings that we should use + */ +struct mem_timings *get_mem_timings(void); + +#endif +#endif diff --git a/src/soc/samsung/exynos5250/dmc_common.c b/src/soc/samsung/exynos5250/dmc_common.c new file mode 100644 index 0000000000..624097b12b --- /dev/null +++ b/src/soc/samsung/exynos5250/dmc_common.c @@ -0,0 +1,182 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2012 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* Mem setup common file for different types of DDR present on SMDK5250 boards. + */ + +#include <console/console.h> +#include <arch/io.h> +#include <delay.h> +#include "setup.h" +#include "dmc.h" +#include "clk.h" +#include "setup.h" + +#define ZQ_INIT_TIMEOUT 10000 + +int dmc_config_zq(struct mem_timings *mem, + struct exynos5_phy_control *phy0_ctrl, + struct exynos5_phy_control *phy1_ctrl) +{ + unsigned long val = 0; + int i; + + /* + * ZQ Calibration: + * Select Driver Strength, + * long calibration for manual calibration + */ + val = PHY_CON16_RESET_VAL; + val |= mem->zq_mode_dds << PHY_CON16_ZQ_MODE_DDS_SHIFT; + val |= mem->zq_mode_term << PHY_CON16_ZQ_MODE_TERM_SHIFT; + val |= ZQ_CLK_DIV_EN; + writel(val, &phy0_ctrl->phy_con16); + writel(val, &phy1_ctrl->phy_con16); + + /* Disable termination */ + if (mem->zq_mode_noterm) + val |= PHY_CON16_ZQ_MODE_NOTERM_MASK; + writel(val, &phy0_ctrl->phy_con16); + writel(val, &phy1_ctrl->phy_con16); + + /* ZQ_MANUAL_START: Enable */ + val |= ZQ_MANUAL_STR; + writel(val, &phy0_ctrl->phy_con16); + writel(val, &phy1_ctrl->phy_con16); + + /* ZQ_MANUAL_START: Disable */ + val &= ~ZQ_MANUAL_STR; + + /* + * Since we are manually calibrating the ZQ values, + * we are looping for the ZQ_init to complete. + */ + i = ZQ_INIT_TIMEOUT; + while ((readl(&phy0_ctrl->phy_con17) & ZQ_DONE) != ZQ_DONE && i > 0) { + udelay(1); + i--; + } + if (!i) + return -1; + writel(val, &phy0_ctrl->phy_con16); + + i = ZQ_INIT_TIMEOUT; + while ((readl(&phy1_ctrl->phy_con17) & ZQ_DONE) != ZQ_DONE && i > 0) { + udelay(1); + i--; + } + if (!i) + return -1; + writel(val, &phy1_ctrl->phy_con16); + + return 0; +} + +void update_reset_dll(struct exynos5_dmc *dmc, enum ddr_mode mode) +{ + unsigned long val; + + if (mode == DDR_MODE_DDR3) { + val = MEM_TERM_EN | PHY_TERM_EN | DMC_CTRL_SHGATE; + writel(val, &dmc->phycontrol0); + } + + /* Update DLL Information: Force DLL Resynchronization */ + val = readl(&dmc->phycontrol0); + val |= FP_RSYNC; + writel(val, &dmc->phycontrol0); + + /* Reset Force DLL Resynchronization */ + val = readl(&dmc->phycontrol0); + val &= ~FP_RSYNC; + writel(val, &dmc->phycontrol0); +} + +void dmc_config_mrs(struct mem_timings *mem, struct exynos5_dmc *dmc) +{ + int channel, chip; + + for (channel = 0; channel < mem->dmc_channels; channel++) { + unsigned long mask; + + mask = channel << DIRECT_CMD_CHANNEL_SHIFT; + for (chip = 0; chip < mem->chips_to_configure; chip++) { + int i; + + mask |= chip << DIRECT_CMD_CHIP_SHIFT; + + /* Sending NOP command */ + writel(DIRECT_CMD_NOP | mask, &dmc->directcmd); + + /* + * TODO(alim.akhtar@samsung.com): Do we need these + * delays? This one and the next were not there for + * DDR3. + */ + udelay(100); + + /* Sending EMRS/MRS commands */ + for (i = 0; i < MEM_TIMINGS_MSR_COUNT; i++) { + writel(mem->direct_cmd_msr[i] | mask, + &dmc->directcmd); + udelay(100); + } + + if (mem->send_zq_init) { + /* Sending ZQINIT command */ + writel(DIRECT_CMD_ZQINIT | mask, + &dmc->directcmd); + /* + * FIXME: This was originally sdelay(10000) + * in the imported u-boot code. That may have + * been meant to be sdelay(0x10000) since that + * was used elsewhere in this function. Either + * way seems to work, though. + */ + udelay(12); + } + } + } +} + +void dmc_config_prech(struct mem_timings *mem, struct exynos5_dmc *dmc) +{ + int channel, chip; + + for (channel = 0; channel < mem->dmc_channels; channel++) { + unsigned long mask; + + mask = channel << DIRECT_CMD_CHANNEL_SHIFT; + for (chip = 0; chip < mem->chips_per_channel; chip++) { + mask |= chip << DIRECT_CMD_CHIP_SHIFT; + + /* PALL (all banks precharge) CMD */ + writel(DIRECT_CMD_PALL | mask, &dmc->directcmd); + udelay(100); + } + } +} + +void dmc_config_memory(struct mem_timings *mem, struct exynos5_dmc *dmc) +{ + writel(mem->memconfig, &dmc->memconfig0); + writel(mem->memconfig, &dmc->memconfig1); + writel(DMC_MEMBASECONFIG0_VAL, &dmc->membaseconfig0); + writel(DMC_MEMBASECONFIG1_VAL, &dmc->membaseconfig1); +} diff --git a/src/soc/samsung/exynos5250/dmc_init_ddr3.c b/src/soc/samsung/exynos5250/dmc_init_ddr3.c new file mode 100644 index 0000000000..89a7b613e7 --- /dev/null +++ b/src/soc/samsung/exynos5250/dmc_init_ddr3.c @@ -0,0 +1,250 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2012 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* DDR3 mem setup file for SMDK5250 board based on EXYNOS5 */ + +#include <delay.h> +#include <arch/io.h> +#include <console/console.h> +#include "clk.h" +#include "cpu.h" +#include "dmc.h" +#include "setup.h" + +#define RDLVL_COMPLETE_TIMEOUT 10000 + +static void reset_phy_ctrl(void) +{ + writel(LPDDR3PHY_CTRL_PHY_RESET_ENABLE, &exynos_clock->lpddr3phy_ctrl); + writel(LPDDR3PHY_CTRL_PHY_RESET_DISABLE, &exynos_clock->lpddr3phy_ctrl); + +#if 0 + /* + * For proper memory initialization there should be a minimum delay of + * 500us after the LPDDR3PHY_CTRL_PHY_RESET signal. + * The below value is an approximate value whose calculation in done + * considering that sdelay takes 2 instruction for every 1 delay cycle. + * And assuming each instruction takes 1 clock cycle i.e 1/(1.7 Ghz)sec + * So for 500 usec, the number of delay cycle should be + * (500 * 10^-6) * (1.7 * 10^9) / 2 = 425000 + * + * TODO(hatim.rv@samsung.com): Implement the delay using timer/counter + */ + sdelay(425000); +#endif + udelay(500); +} + +int ddr3_mem_ctrl_init(struct mem_timings *mem, unsigned long mem_iv_size, + int mem_reset) +{ + unsigned int val; + int i; + + if (mem_reset) + 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, &exynos_phy0_control->phy_con39); + writel(val, &exynos_phy1_control->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, &exynos_phy0_control->phy_con42); + writel(val, &exynos_phy1_control->phy_con42); + + /* ZQ Calibration */ + if (dmc_config_zq(mem, exynos_phy0_control, exynos_phy1_control)){ + printk(BIOS_EMERG, "DRAM ZQ CALIBRATION FAILURE\n"); + return SETUP_ERR_ZQ_CALIBRATION_FAILURE; + } + + /* DQ Signal */ + writel(mem->phy0_pulld_dqs, &exynos_phy0_control->phy_con14); + writel(mem->phy1_pulld_dqs, &exynos_phy1_control->phy_con14); + + writel(mem->concontrol | (mem->rd_fetch << CONCONTROL_RD_FETCH_SHIFT) + | (mem->dfi_init_start << CONCONTROL_DFI_INIT_START_SHIFT), + &exynos_dmc->concontrol); + + update_reset_dll(exynos_dmc, DDR_MODE_DDR3); + + /* DQS Signal */ + writel(mem->phy0_dqs, &exynos_phy0_control->phy_con4); + writel(mem->phy1_dqs, &exynos_phy1_control->phy_con4); + + writel(mem->phy0_dq, &exynos_phy0_control->phy_con6); + writel(mem->phy1_dq, &exynos_phy1_control->phy_con6); + + writel(mem->phy0_tFS, &exynos_phy0_control->phy_con10); + writel(mem->phy1_tFS, &exynos_phy1_control->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, &exynos_phy0_control->phy_con12); + writel(val, &exynos_phy1_control->phy_con12); + + /* Start DLL locking */ + writel(val | (mem->ctrl_start << PHY_CON12_CTRL_START_SHIFT), + &exynos_phy0_control->phy_con12); + writel(val | (mem->ctrl_start << PHY_CON12_CTRL_START_SHIFT), + &exynos_phy1_control->phy_con12); + + update_reset_dll(exynos_dmc, DDR_MODE_DDR3); + + writel(mem->concontrol | (mem->rd_fetch << CONCONTROL_RD_FETCH_SHIFT), + &exynos_dmc->concontrol); + + /* Memory Channel Inteleaving Size */ + writel(mem->iv_size, &exynos_dmc->ivcontrol); + + /* Set DMC MEMCONTROL register */ + val = mem->memcontrol & ~DMC_MEMCONTROL_DSREF_ENABLE; + writel(val, &exynos_dmc->memcontrol); + + writel(mem->memconfig, &exynos_dmc->memconfig0); + writel(mem->memconfig, &exynos_dmc->memconfig1); + writel(mem->membaseconfig0, &exynos_dmc->membaseconfig0); + writel(mem->membaseconfig1, &exynos_dmc->membaseconfig1); + + /* Precharge Configuration */ + writel(mem->prechconfig_tp_cnt << PRECHCONFIG_TP_CNT_SHIFT, + &exynos_dmc->prechconfig); + + /* Power Down mode Configuration */ + writel(mem->dpwrdn_cyc << PWRDNCONFIG_DPWRDN_CYC_SHIFT | + mem->dsref_cyc << PWRDNCONFIG_DSREF_CYC_SHIFT, + &exynos_dmc->pwrdnconfig); + + /* TimingRow, TimingData, TimingPower and Timingaref + * values as per Memory AC parameters + */ + writel(mem->timing_ref, &exynos_dmc->timingref); + writel(mem->timing_row, &exynos_dmc->timingrow); + writel(mem->timing_data, &exynos_dmc->timingdata); + writel(mem->timing_power, &exynos_dmc->timingpower); + + /* Send PALL command */ + dmc_config_prech(mem, exynos_dmc); + + if (mem_reset) { + /* Send NOP, MRS and ZQINIT commands. + * Sending MRS command will reset the DRAM. We should not be + * reseting the DRAM after resume, this will lead to memory + * corruption as DRAM content is lost after DRAM reset + */ + dmc_config_mrs(mem, exynos_dmc); + } + + if (mem->gate_leveling_enable) { + val = PHY_CON0_RESET_VAL; + val |= P0_CMD_EN; + writel(val, &exynos_phy0_control->phy_con0); + writel(val, &exynos_phy1_control->phy_con0); + + val = PHY_CON2_RESET_VAL; + val |= INIT_DESKEW_EN; + writel(val, &exynos_phy0_control->phy_con2); + writel(val, &exynos_phy1_control->phy_con2); + + val = PHY_CON0_RESET_VAL; + val |= P0_CMD_EN; + val |= BYTE_RDLVL_EN; + writel(val, &exynos_phy0_control->phy_con0); + writel(val, &exynos_phy1_control->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, &exynos_phy0_control->phy_con12); + writel(val, &exynos_phy1_control->phy_con12); + + val = PHY_CON2_RESET_VAL; + val |= INIT_DESKEW_EN; + val |= RDLVL_GATE_EN; + writel(val, &exynos_phy0_control->phy_con2); + writel(val, &exynos_phy1_control->phy_con2); + + val = PHY_CON0_RESET_VAL; + val |= P0_CMD_EN; + val |= BYTE_RDLVL_EN; + val |= CTRL_SHGATE; + writel(val, &exynos_phy0_control->phy_con0); + writel(val, &exynos_phy1_control->phy_con0); + + val = PHY_CON1_RESET_VAL; + val &= ~(CTRL_GATEDURADJ_MASK); + writel(val, &exynos_phy0_control->phy_con1); + writel(val, &exynos_phy1_control->phy_con1); + + writel(CTRL_RDLVL_GATE_ENABLE, &exynos_dmc->rdlvl_config); + i = RDLVL_COMPLETE_TIMEOUT; + while ((readl(&exynos_dmc->phystatus) & + (RDLVL_COMPLETE_CHO | RDLVL_COMPLETE_CH1)) != + (RDLVL_COMPLETE_CHO | RDLVL_COMPLETE_CH1) && i > 0) { + /* + * TODO(waihong): Comment on how long this take to + * timeout + */ + udelay(1); + i--; + } + if (!i){ + printk(BIOS_EMERG, "Timeout on RDLVL. No DRAM.\n"); + return SETUP_ERR_RDLV_COMPLETE_TIMEOUT; + } + writel(CTRL_RDLVL_GATE_DISABLE, &exynos_dmc->rdlvl_config); + + writel(0, &exynos_phy0_control->phy_con14); + writel(0, &exynos_phy1_control->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, &exynos_phy0_control->phy_con12); + writel(val, &exynos_phy1_control->phy_con12); + + update_reset_dll(exynos_dmc, DDR_MODE_DDR3); + } + + /* Send PALL command */ + dmc_config_prech(mem, exynos_dmc); + + writel(mem->memcontrol, &exynos_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), + &exynos_dmc->concontrol); + return 0; +} diff --git a/src/soc/samsung/exynos5250/dp-core.h b/src/soc/samsung/exynos5250/dp-core.h new file mode 100644 index 0000000000..ab7e7e4b32 --- /dev/null +++ b/src/soc/samsung/exynos5250/dp-core.h @@ -0,0 +1,260 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2013 Google Inc. + * Copyright (C) 2012 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* Header file for Samsung DP (Display Port) interface driver. */ + +#ifndef CPU_SAMSUNG_EXYNOS5250_DP_CORE_H +#define CPU_SAMSUNG_EXYNOS5250_DP_CORE_H + +#define STREAM_ON_TIMEOUT 100 +#define PLL_LOCK_TIMEOUT 10 +#define DP_INIT_TRIES 10 +#define MAX_CR_LOOP 5 +#define MAX_EQ_LOOP 4 + +/* Link rate type */ +enum link_rate { + LINK_RATE_1_62GBPS = 0x06, + LINK_RATE_2_70GBPS = 0x0a +}; + +/* Number of lanes supported */ +enum link_lane_count { + LANE_COUNT1 = 1, + LANE_COUNT2 = 2, + LANE_COUNT4 = 4 +}; + +/* Pre emphasis level */ +enum pre_emphasis_level { + PRE_EMPHASIS_LEVEL_0, + PRE_EMPHASIS_LEVEL_1, + PRE_EMPHASIS_LEVEL_2, + PRE_EMPHASIS_LEVEL_3, +}; + +/* Type of color space */ +enum color_space { + COLOR_RGB, + COLOR_YCBCR422, + COLOR_YCBCR444 +}; + +/* Video input Bit Per Color */ +enum color_depth { + COLOR_6, + COLOR_8, + COLOR_10, + COLOR_12 +}; + +/* Type of YCbCr coefficient */ +enum color_coefficient { + COLOR_YCBCR601, + COLOR_YCBCR709 +}; + +/* Color range */ +enum dynamic_range { + VESA, + CEA +}; + +/* Status of PLL clock */ +enum pll_status { + PLL_UNLOCKED, + PLL_LOCKED +}; + +/* To choose type of m_value */ +enum clock_recovery_m_value_type { + CALCULATED_M, + REGISTER_M +}; + +struct video_info { + enum color_space color_space; + enum dynamic_range dynamic_range; + enum color_coefficient ycbcr_coeff; + enum color_depth color_depth; + + enum link_rate link_rate; + enum link_lane_count lane_count; + + char *name; + + unsigned int h_sync_polarity:1; + unsigned int v_sync_polarity:1; + unsigned int interlaced:1; +}; + +struct link_train { + u8 link_rate; + u8 lane_count; +}; + +struct s5p_dp_device { + unsigned int irq; + struct exynos5_dp *base; + struct video_info *video_info; + struct link_train link_train; +}; + +/* s5p_dp_reg.c */ + +/* + * Reset DP module + * + * param dp pointer to main s5p-dp structure + */ +void s5p_dp_reset(struct s5p_dp_device *dp); +/* + * Initialize DP to receive video stream + * + * param dp pointer to main s5p-dp structure + */ +void s5p_dp_init_video(struct s5p_dp_device *dp); +/* + * Check whether PLL is locked + * + * param dp pointer to main s5p-dp structure + * return Lock status + */ +unsigned int s5p_dp_get_pll_lock_status(struct s5p_dp_device *dp); +/* + * Initialize analog functions of DP + * + * param dp pointer to main s5p-dp structure + * return 0 on success + */ +int s5p_dp_init_analog_func(struct s5p_dp_device *dp); +/* + * Initialize DP for AUX transaction + * + * param dp pointer to main s5p-dp structure + */ +void s5p_dp_init_aux(struct s5p_dp_device *dp); + +/* + * Start an AUX transaction. + * + * param dp pointer to main s5p-dp structure + */ +int s5p_dp_start_aux_transaction(struct s5p_dp_device *dp); + +/* + * Write a byte to DPCD register + * + * param dp pointer to main s5p-dp structure + * param reg_addr DPCD register to be written + * param data byte data to be written + * return write status + */ +int s5p_dp_write_byte_to_dpcd(struct s5p_dp_device *dp, + unsigned int reg_addr, + unsigned char data); +/* + * Read a byte from DPCD register + * + * param dp pointer to main s5p-dp structure + * param reg_addr DPCD register to read + * param data read byte data + * return read status + */ +int s5p_dp_read_byte_from_dpcd(struct s5p_dp_device *dp, + unsigned int reg_addr, + unsigned char *data); +/* + * Initialize DP video functions + * + * param dp pointer to main s5p-dp structure + */ +//void s5p_dp_init_video(struct s5p_dp_device *dp); + +/* + * Set color parameters for display + * + * param dp pointer to main s5p-dp structure + * param color_depth Video input Bit Per Color + * param color_space Colorimetric format of input video + * param dynamic_range VESA range or CEA range + * param coeff YCbCr Coefficients of input video + */ +void s5p_dp_set_video_color_format(struct s5p_dp_device *dp, + unsigned int color_depth, + unsigned int color_space, + unsigned int dynamic_range, + unsigned int coeff); +/* + * Check whether video clock is on + * + * param dp pointer to main s5p-dp structure + * return clock status + */ +int s5p_dp_is_slave_video_stream_clock_on(struct s5p_dp_device *dp); +/* + * Check whether video clock is on + * + * param dp pointer to main s5p-dp structure + * param type clock_recovery_m_value_type + * param m_value to calculate m_vid value + * param n_value to calculate n_vid value + */ +void s5p_dp_set_video_cr_mn(struct s5p_dp_device *dp, + enum clock_recovery_m_value_type type, + unsigned int m_value, + unsigned int n_value); +/* + * Set DP to video slave mode thereby enabling video master + * + * param dp pointer to main s5p-dp structure + */ +void s5p_dp_enable_video_master(struct s5p_dp_device *dp); +/* + * Check whether video stream is on + * + * param dp pointer to main s5p-dp structure + * return video stream status + */ +int s5p_dp_is_video_stream_on(struct s5p_dp_device *dp); +/* + * Configure DP in slave mode + * + * param dp pointer to main s5p-dp structure + * param video_info pointer to main video_info structure. + */ +void s5p_dp_config_video_slave_mode(struct s5p_dp_device *dp, + struct video_info *video_info); + +/* + * Wait unitl HW link training done + * + * param dp pointer to main s5p-dp structure + */ +void s5p_dp_wait_hw_link_training_done(struct s5p_dp_device *dp); + +/* startup and init */ +struct exynos5_fimd_panel; +void fb_init(unsigned long int fb_size, void *lcdbase, + struct exynos5_fimd_panel *pd); +int dp_controller_init(struct s5p_dp_device *dp_device); +int lcd_ctrl_init(unsigned long int fb_size, + struct exynos5_fimd_panel *panel_data, void *lcdbase); +#endif /* CPU_SAMSUNG_EXYNOS5250_DP_CORE_H */ diff --git a/src/soc/samsung/exynos5250/dp-reg.c b/src/soc/samsung/exynos5250/dp-reg.c new file mode 100644 index 0000000000..1cc70887ce --- /dev/null +++ b/src/soc/samsung/exynos5250/dp-reg.c @@ -0,0 +1,503 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2012 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* Samsung DP (Display port) register interface driver. */ + +#include <console/console.h> +#include <arch/io.h> +#include <delay.h> +#include "timer.h" +#include "clk.h" +#include "cpu.h" +#include "periph.h" +#include "dp.h" +#include "fimd.h" +#include "dp-core.h" + +void s5p_dp_reset(struct s5p_dp_device *dp) +{ + u32 reg; + struct exynos5_dp *base = dp->base; + + writel(RESET_DP_TX, &base->dp_tx_sw_reset); + + /* Stop Video */ + clrbits_le32(&base->video_ctl_1, VIDEO_EN); + clrbits_le32(&base->video_ctl_1, HDCP_VIDEO_MUTE); + + reg = MASTER_VID_FUNC_EN_N | SLAVE_VID_FUNC_EN_N | + AUD_FIFO_FUNC_EN_N | AUD_FUNC_EN_N | + HDCP_FUNC_EN_N | SW_FUNC_EN_N; + writel(reg, &base->func_en_1); + + reg = SSC_FUNC_EN_N | AUX_FUNC_EN_N | + SERDES_FIFO_FUNC_EN_N | + LS_CLK_DOMAIN_FUNC_EN_N; + writel(reg, &base->func_en_2); + + udelay(20); + + reg = LANE3_MAP_LOGIC_LANE_3 | LANE2_MAP_LOGIC_LANE_2 | + LANE1_MAP_LOGIC_LANE_1 | LANE0_MAP_LOGIC_LANE_0; + + writel(reg, &base->lane_map); + + writel(0x0, &base->sys_ctl_1); + writel(0x40, &base->sys_ctl_2); + writel(0x0, &base->sys_ctl_3); + writel(0x0, &base->sys_ctl_4); + + writel(0x0, &base->pkt_send_ctl); + writel(0x0, &base->dp_hdcp_ctl); + + writel(0x5e, &base->dp_hpd_deglitch_l); + writel(0x1a, &base->dp_hpd_deglitch_h); + + writel(0x10, &base->dp_debug_ctl); + + writel(0x0, &base->dp_phy_test); + + writel(0x0, &base->dp_video_fifo_thrd); + writel(0x20, &base->dp_audio_margin); + + writel(0x4, &base->m_vid_gen_filter_th); + writel(0x2, &base->m_aud_gen_filter_th); + + writel(0x00000101, &base->soc_general_ctl); + + /* Set Analog Parameters */ + writel(0x10, &base->analog_ctl_1); + writel(0x0C, &base->analog_ctl_2); + writel(0x85, &base->analog_ctl_3); + writel(0x66, &base->pll_filter_ctl_1); + writel(0x0, &base->tx_amp_tuning_ctl); + + /* Set interrupt pin assertion polarity as high */ + writel(INT_POL0 | INT_POL1, &base->int_ctl); + + /* Clear pending registers */ + writel(0xff, &base->common_int_sta_1); + writel(0x4f, &base->common_int_sta_2); + writel(0xe0, &base->common_int_sta_3); + writel(0xe7, &base->common_int_sta_4); + writel(0x63, &base->dp_int_sta); + + /* 0:mask,1: unmask */ + writel(0x00, &base->common_int_mask_1); + writel(0x00, &base->common_int_mask_2); + writel(0x00, &base->common_int_mask_3); + writel(0x00, &base->common_int_mask_4); + writel(0x00, &base->int_sta_mask); +} + +unsigned int s5p_dp_get_pll_lock_status(struct s5p_dp_device *dp) +{ + u32 reg; + + reg = readl(&dp->base->dp_debug_ctl); + if (reg & PLL_LOCK) + return PLL_LOCKED; + else + return PLL_UNLOCKED; +} + +int s5p_dp_init_analog_func(struct s5p_dp_device *dp) +{ + u32 reg; + struct mono_time current, end; + struct exynos5_dp *base = dp->base; + + writel(0x00, &base->dp_phy_pd); + + reg = PLL_LOCK_CHG; + writel(reg, &base->common_int_sta_1); + + clrbits_le32(&base->dp_debug_ctl, (F_PLL_LOCK | PLL_LOCK_CTRL)); + + /* Power up PLL */ + if (s5p_dp_get_pll_lock_status(dp) == PLL_UNLOCKED) { + + clrbits_le32(&base->dp_pll_ctl, DP_PLL_PD); + + timer_monotonic_get(¤t); + end = current; + mono_time_add_msecs(&end, PLL_LOCK_TIMEOUT); + + while (s5p_dp_get_pll_lock_status(dp) == PLL_UNLOCKED) { + if (mono_time_after(¤t, &end)) { + printk(BIOS_ERR, "%s: PLL is not locked\n", + __func__); + return -1; + } + timer_monotonic_get(¤t); + } + } + + /* Enable Serdes FIFO function and Link symbol clock domain module */ + clrbits_le32(&base->func_en_2, (SERDES_FIFO_FUNC_EN_N | + LS_CLK_DOMAIN_FUNC_EN_N | AUX_FUNC_EN_N)); + return 0; +} + +void s5p_dp_init_aux(struct s5p_dp_device *dp) +{ + u32 reg; + struct exynos5_dp *base = dp->base; + + /* Clear interrupts related to AUX channel */ + reg = RPLY_RECEIV | AUX_ERR; + writel(reg, &base->dp_int_sta); + + /* Disable AUX channel module */ + setbits_le32(&base->func_en_2, AUX_FUNC_EN_N); + + /* Disable AUX transaction H/W retry */ + reg = (3 & AUX_BIT_PERIOD_MASK) << AUX_BIT_PERIOD_SHIFT; + reg |= (0 & AUX_HW_RETRY_COUNT_MASK) << AUX_HW_RETRY_COUNT_SHIFT; + reg |= (AUX_HW_RETRY_INTERVAL_600_US << AUX_HW_RETRY_INTERVAL_SHIFT); + writel(reg, &base->aux_hw_retry_ctl) ; + + /* Receive AUX Channel DEFER commands equal to DEFFER_COUNT*64 */ + reg = DEFER_CTRL_EN; + reg |= (1 & DEFER_COUNT_MASK) << DEFER_COUNT_SHIFT; + writel(reg, &base->aux_ch_defer_dtl); + + /* Enable AUX channel module */ + clrbits_le32(&base->func_en_2, AUX_FUNC_EN_N); +} + +int s5p_dp_start_aux_transaction(struct s5p_dp_device *dp) +{ + int reg; + struct exynos5_dp *base = dp->base; + + /* Enable AUX CH operation */ + setbits_le32(&base->aux_ch_ctl_2, AUX_EN); + + /* Is AUX CH command reply received? */ + reg = readl(&base->dp_int_sta); + while (!(reg & RPLY_RECEIV)) + reg = readl(&base->dp_int_sta); + + /* Clear interrupt source for AUX CH command reply */ + writel(RPLY_RECEIV, &base->dp_int_sta); + + /* Clear interrupt source for AUX CH access error */ + reg = readl(&base->dp_int_sta); + if (reg & AUX_ERR) { + printk(BIOS_ERR, "%s: AUX_ERR encountered, dp_int_sta: " + "0x%02x\n", __func__, reg); + writel(AUX_ERR, &base->dp_int_sta); + return -1; + } + + /* Check AUX CH error access status */ + reg = readl(&base->dp_int_sta); + if ((reg & AUX_STATUS_MASK) != 0) { + printk(BIOS_ERR, "AUX CH error happens: %d\n\n", + reg & AUX_STATUS_MASK); + return -1; + } + + return 0; +} + +int s5p_dp_write_byte_to_dpcd(struct s5p_dp_device *dp, + unsigned int reg_addr, + unsigned char data) +{ + u32 reg; + int i; + int retval; + struct exynos5_dp *base = dp->base; + + for (i = 0; i < MAX_AUX_RETRY_COUNT; i++) { + /* Clear AUX CH data buffer */ + writel(BUF_CLR, &base->buf_data_ctl); + + /* Select DPCD device address */ + reg = reg_addr >> AUX_ADDR_7_0_SHIFT; + reg &= AUX_ADDR_7_0_MASK; + writel(reg, &base->aux_addr_7_0); + reg = reg_addr >> AUX_ADDR_15_8_SHIFT; + reg &= AUX_ADDR_15_8_MASK; + writel(reg, &base->aux_addr_15_8); + reg = reg_addr >> AUX_ADDR_19_16_SHIFT; + reg &= AUX_ADDR_19_16_MASK; + writel(reg, &base->aux_addr_19_16); + + /* Write data buffer */ + reg = (unsigned int)data; + writel(reg, &base->buf_data_0); + + /* + * Set DisplayPort transaction and write 1 byte + * If bit 3 is 1, DisplayPort transaction. + * If Bit 3 is 0, I2C transaction. + */ + reg = AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_WRITE; + writel(reg, &base->aux_ch_ctl_1); + + /* Start AUX transaction */ + retval = s5p_dp_start_aux_transaction(dp); + if (retval == 0) + break; + else + printk(BIOS_DEBUG, "Aux Transaction fail!\n"); + } + + return retval; +} + +int s5p_dp_read_byte_from_dpcd(struct s5p_dp_device *dp, + unsigned int reg_addr, + unsigned char *data) +{ + u32 reg; + int i; + int retval; + struct exynos5_dp *base = dp->base; + + for (i = 0; i < MAX_AUX_RETRY_COUNT; i++) { + /* Clear AUX CH data buffer */ + writel(BUF_CLR, &base->buf_data_ctl); + + /* Select DPCD device address */ + reg = reg_addr >> AUX_ADDR_7_0_SHIFT; + reg &= AUX_ADDR_7_0_MASK; + writel(reg, &base->aux_addr_7_0); + reg = reg_addr >> AUX_ADDR_15_8_SHIFT; + reg &= AUX_ADDR_15_8_MASK; + writel(reg, &base->aux_addr_15_8); + reg = reg_addr >> AUX_ADDR_19_16_SHIFT; + reg &= AUX_ADDR_19_16_MASK; + writel(reg, &base->aux_addr_19_16); + + /* + * Set DisplayPort transaction and read 1 byte + * If bit 3 is 1, DisplayPort transaction. + * If Bit 3 is 0, I2C transaction. + */ + reg = AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_READ; + writel(reg, &base->aux_ch_ctl_1); + + /* Start AUX transaction */ + retval = s5p_dp_start_aux_transaction(dp); + if (retval == 0) + break; + else + printk(BIOS_DEBUG, "Aux Transaction fail!\n"); + } + + /* Read data buffer */ + if (!retval) { + reg = readl(&base->buf_data_0); + *data = (unsigned char)(reg & 0xff); + } + + return retval; +} + +void s5p_dp_init_video(struct s5p_dp_device *dp) +{ + u32 reg; + struct exynos5_dp *base = dp->base; + + reg = VSYNC_DET | VID_FORMAT_CHG | VID_CLK_CHG; + writel(reg, &base->common_int_sta_1); + + reg = 0x0; + writel(reg, &base->sys_ctl_1); + + reg = (4 & CHA_CRI_MASK) << CHA_CRI_SHIFT; + reg |= CHA_CTRL; + writel(reg, &base->sys_ctl_2); + + reg = 0x0; + writel(reg, &base->sys_ctl_3); +} + +void s5p_dp_set_video_color_format(struct s5p_dp_device *dp, + unsigned int color_depth, + unsigned int color_space, + unsigned int dynamic_range, + unsigned int coeff) +{ + u32 reg; + struct exynos5_dp *base = dp->base; + + /* Configure the input color depth, color space, dynamic range */ + reg = (dynamic_range << IN_D_RANGE_SHIFT) | + (color_depth << IN_BPC_SHIFT) | + (color_space << IN_COLOR_F_SHIFT); + writel(reg, &base->video_ctl_2); + + /* Set Input Color YCbCr Coefficients to ITU601 or ITU709 */ + reg = readl(&base->video_ctl_3); + reg &= ~IN_YC_COEFFI_MASK; + if (coeff) + reg |= IN_YC_COEFFI_ITU709; + else + reg |= IN_YC_COEFFI_ITU601; + writel(reg, &base->video_ctl_3); +} + +int s5p_dp_is_slave_video_stream_clock_on(struct s5p_dp_device *dp) +{ + u32 reg; + struct exynos5_dp *base = dp->base; + + reg = readl(&base->sys_ctl_1); + writel(reg, &base->sys_ctl_1); + + reg = readl(&base->sys_ctl_1); + + if (!(reg & DET_STA)) + return -1; + + reg = readl(&base->sys_ctl_2); + writel(reg, &base->sys_ctl_2); + + reg = readl(&base->sys_ctl_2); + + if (reg & CHA_STA) { + printk(BIOS_DEBUG, "Input stream clk is changing\n"); + return -1; + } + + return 0; +} + +void s5p_dp_set_video_cr_mn(struct s5p_dp_device *dp, + enum clock_recovery_m_value_type type, + unsigned int m_value, + unsigned int n_value) +{ + u32 reg; + struct exynos5_dp *base = dp->base; + + if (type == REGISTER_M) { + setbits_le32(&base->sys_ctl_4, FIX_M_VID); + + reg = m_value >> M_VID_0_VALUE_SHIFT; + writel(reg, &base->m_vid_0); + + reg = (m_value >> M_VID_1_VALUE_SHIFT); + writel(reg, &base->m_vid_1); + + reg = (m_value >> M_VID_2_VALUE_SHIFT); + writel(reg, &base->m_vid_2); + + reg = n_value >> N_VID_0_VALUE_SHIFT; + writel(reg, &base->n_vid_0); + + reg = (n_value >> N_VID_1_VALUE_SHIFT); + writel(reg, &base->n_vid_1); + + reg = (n_value >> N_VID_2_VALUE_SHIFT); + writel(reg, &base->n_vid_2); + } else { + clrbits_le32(&base->sys_ctl_4, FIX_M_VID); + + writel(0x00, &base->n_vid_0); + writel(0x80, &base->n_vid_1); + writel(0x00, &base->n_vid_2); + } +} + +void s5p_dp_enable_video_master(struct s5p_dp_device *dp) +{ + u32 reg; + struct exynos5_dp *base = dp->base; + + reg = readl(&base->soc_general_ctl); + reg &= ~VIDEO_MODE_MASK; + reg |= VIDEO_MODE_SLAVE_MODE; + writel(reg, &base->soc_general_ctl); +} + +int s5p_dp_is_video_stream_on(struct s5p_dp_device *dp) +{ + u32 reg, i = 0; + struct mono_time current, end; + struct exynos5_dp *base = dp->base; + + /* Wait for 4 VSYNC_DET interrupts */ + timer_monotonic_get(¤t); + end = current; + mono_time_add_msecs(&end, STREAM_ON_TIMEOUT); + + do { + reg = readl(&base->common_int_sta_1); + if (reg & VSYNC_DET) { + i++; + writel(reg | VSYNC_DET, &base->common_int_sta_1); + } + if (i == 4) + break; + timer_monotonic_get(¤t); + } while (mono_time_before(¤t, &end)); + + if (i != 4) { + printk(BIOS_DEBUG, "s5p_dp_is_video_stream_on timeout\n"); + return -1; + } + + return 0; +} + +void s5p_dp_config_video_slave_mode(struct s5p_dp_device *dp, + struct video_info *video_info) +{ + u32 reg; + struct exynos5_dp *base = dp->base; + + reg = readl(&base->func_en_1); + reg &= ~(MASTER_VID_FUNC_EN_N|SLAVE_VID_FUNC_EN_N); + reg |= MASTER_VID_FUNC_EN_N; + writel(reg, &base->func_en_1); + + reg = readl(&base->video_ctl_10); + reg &= ~INTERACE_SCAN_CFG; + reg |= (video_info->interlaced << 2); + writel(reg, &base->video_ctl_10); + + reg = readl(&base->video_ctl_10); + reg &= ~VSYNC_POLARITY_CFG; + reg |= (video_info->v_sync_polarity << 1); + writel(reg, &base->video_ctl_10); + + reg = readl(&base->video_ctl_10); + reg &= ~HSYNC_POLARITY_CFG; + reg |= (video_info->h_sync_polarity << 0); + writel(reg, &base->video_ctl_10); + + reg = AUDIO_MODE_SPDIF_MODE | VIDEO_MODE_SLAVE_MODE; + writel(reg, &base->soc_general_ctl); +} + +void s5p_dp_wait_hw_link_training_done(struct s5p_dp_device *dp) +{ + u32 reg; + struct exynos5_dp *base = dp->base; + + reg = readl(&base->dp_hw_link_training); + while (reg & HW_TRAINING_EN) + reg = readl(&base->dp_hw_link_training); +} diff --git a/src/soc/samsung/exynos5250/dp.h b/src/soc/samsung/exynos5250/dp.h new file mode 100644 index 0000000000..b1533c74f7 --- /dev/null +++ b/src/soc/samsung/exynos5250/dp.h @@ -0,0 +1,501 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2013 Google Inc. + * Copyright (C) 2012 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* Register map for Exynos5 DP */ + +#ifndef CPU_SAMSUNG_EXYNOS5250_DP_H +#define CPU_SAMSUNG_EXYNOS5250_DP_H + +/* DSIM register map */ +struct exynos5_dp { + u8 res1[0x10]; + u32 dp_tx_version; + u32 dp_tx_sw_reset; + u32 func_en_1; + u32 func_en_2; + u32 video_ctl_1; + u32 video_ctl_2; + u32 video_ctl_3; + u32 video_ctl_4; + u32 clr_blue_cb; + u32 clr_green_y; + u32 clr_red_cr; + u32 video_ctl_8; + u8 res2[0x4]; + u32 video_ctl_10; + u32 total_line_l; + u32 total_line_h; + u32 active_line_l; + u32 active_line_h; + u32 v_f_porch; + u32 vsync; + u32 v_b_porch; + u32 total_pixel_l; + u32 total_pixel_h; + u32 active_pixel_l; + u32 active_pixel_h; + u32 h_f_porch_l; + u32 h_f_porch_h; + u32 hsync_l; + u32 hysnc_h; + u32 h_b_porch_l; + u32 h_b_porch_h; + u32 vid_status; + u32 total_line_sta_l; + u32 total_line_sta_h; + u32 active_line_sta_l; + u32 active_line_sta_h; + u32 v_f_porch_sta; + u32 vsync_sta; + u32 v_b_porch_sta; + u32 total_pixel_sta_l; + u32 total_pixel_sta_h; + u32 active_pixel_sta_l; + u32 active_pixel_sta_h; + u32 h_f_porch_sta_l; + u32 h_f_porch_sta_h; + u32 hsync_sta_l; + u32 hsync_sta_h; + u32 h_b_porch_sta_l; + u32 h_b_porch__sta_h; + u8 res3[0x288]; + u32 lane_map; + u8 res4[0x10]; + u32 analog_ctl_1; + u32 analog_ctl_2; + u32 analog_ctl_3; + u32 pll_filter_ctl_1; + u32 tx_amp_tuning_ctl; + u8 res5[0xc]; + u32 aux_hw_retry_ctl; + u8 res6[0x2c]; + u32 int_state; + u32 common_int_sta_1; + u32 common_int_sta_2; + u32 common_int_sta_3; + u32 common_int_sta_4; + u8 res7[0x8]; + u32 dp_int_sta; + u32 common_int_mask_1; + u32 common_int_mask_2; + u32 common_int_mask_3; + u32 common_int_mask_4; + u8 res8[0x08]; + u32 int_sta_mask; + u32 int_ctl; + u8 res9[0x200]; + u32 sys_ctl_1; + u32 sys_ctl_2; + u32 sys_ctl_3; + u32 sys_ctl_4; + u32 dp_vid_ctl; + u8 res10[0x2c]; + u32 pkt_send_ctl; + u8 res11[0x4]; + u32 dp_hdcp_ctl; + u8 res12[0x34]; + u32 link_bw_set; + u32 lane_count_set; + u32 dp_training_ptn_set; + u32 ln0_link_trn_ctl; + u32 ln1_link_trn_ctl; + u32 ln2_link_trn_ctl; + u32 ln3_link_trn_ctl; + u32 dp_dn_spread; + u32 dp_hw_link_training; + u8 res13[0x1c]; + u32 dp_debug_ctl; + u32 dp_hpd_deglitch_l; + u32 dp_hpd_deglitch_h; + u8 res14[0x14]; + u32 dp_link_debug_ctl; + u8 res15[0x1c]; + u32 m_vid_0; + u32 m_vid_1; + u32 m_vid_2; + u32 n_vid_0; + u32 n_vid_1; + u32 n_vid_2; + u32 m_vid_mon; + u32 dp_pll_ctl; + u32 dp_phy_pd; + u32 dp_phy_test; + u8 res16[0x8]; + u32 dp_video_fifo_thrd; + u8 res17[0x8]; + u32 dp_audio_margin; + u32 dp_dn_spread_ctl_1; + u32 dp_dn_spread_ctl_2; + u8 res18[0x18]; + u32 dp_m_cal_ctl; + u32 m_vid_gen_filter_th; + u8 res19[0x14]; + u32 m_aud_gen_filter_th; + u32 aux_ch_sta; + u32 aux_err_num; + u32 aux_ch_defer_dtl; + u32 aux_rx_comm; + u32 buf_data_ctl; + u32 aux_ch_ctl_1; + u32 aux_addr_7_0; + u32 aux_addr_15_8; + u32 aux_addr_19_16; + u32 aux_ch_ctl_2; + u8 res20[0x18]; + u32 buf_data_0; + u8 res21[0x3c]; + u32 soc_general_ctl; +}; + +static struct exynos5_dp * const exynos_dp0 = (void *)EXYNOS5_DP0_BASE; +static struct exynos5_dp * const exynos_dp1 = (void *)EXYNOS5_DP1_BASE; + +/* DP_TX_SW_RESET */ +#define RESET_DP_TX (1 << 0) + +/* DP_FUNC_EN_1 */ +#define MASTER_VID_FUNC_EN_N (1 << 7) +#define SLAVE_VID_FUNC_EN_N (1 << 5) +#define AUD_FIFO_FUNC_EN_N (1 << 4) +#define AUD_FUNC_EN_N (1 << 3) +#define HDCP_FUNC_EN_N (1 << 2) +#define CRC_FUNC_EN_N (1 << 1) +#define SW_FUNC_EN_N (1 << 0) + +/* DP_FUNC_EN_2 */ +#define SSC_FUNC_EN_N (1 << 7) +#define AUX_FUNC_EN_N (1 << 2) +#define SERDES_FIFO_FUNC_EN_N (1 << 1) +#define LS_CLK_DOMAIN_FUNC_EN_N (1 << 0) + +/* DP_VIDEO_CTL_1 */ +#define VIDEO_EN (1 << 7) +#define HDCP_VIDEO_MUTE (1 << 6) + +/* DP_VIDEO_CTL_1 */ +#define IN_D_RANGE_MASK (1 << 7) +#define IN_D_RANGE_SHIFT (7) +#define IN_D_RANGE_CEA (1 << 7) +#define IN_D_RANGE_VESA (0 << 7) +#define IN_BPC_MASK (7 << 4) +#define IN_BPC_SHIFT (4) +#define IN_BPC_12_BITS (3 << 4) +#define IN_BPC_10_BITS (2 << 4) +#define IN_BPC_8_BITS (1 << 4) +#define IN_BPC_6_BITS (0 << 4) +#define IN_COLOR_F_MASK (3 << 0) +#define IN_COLOR_F_SHIFT (0) +#define IN_COLOR_F_YCBCR444 (2 << 0) +#define IN_COLOR_F_YCBCR422 (1 << 0) +#define IN_COLOR_F_RGB (0 << 0) + +/* DP_VIDEO_CTL_3 */ +#define IN_YC_COEFFI_MASK (1 << 7) +#define IN_YC_COEFFI_SHIFT (7) +#define IN_YC_COEFFI_ITU709 (1 << 7) +#define IN_YC_COEFFI_ITU601 (0 << 7) +#define VID_CHK_UPDATE_TYPE_MASK (1 << 4) +#define VID_CHK_UPDATE_TYPE_SHIFT (4) +#define VID_CHK_UPDATE_TYPE_1 (1 << 4) +#define VID_CHK_UPDATE_TYPE_0 (0 << 4) + +/* DP_VIDEO_CTL_10 */ +#define FORMAT_SEL (1 << 4) +#define INTERACE_SCAN_CFG (1 << 2) +#define VSYNC_POLARITY_CFG (1 << 1) +#define HSYNC_POLARITY_CFG (1 << 0) + +/* DP_LANE_MAP */ +#define LANE3_MAP_LOGIC_LANE_0 (0 << 6) +#define LANE3_MAP_LOGIC_LANE_1 (1 << 6) +#define LANE3_MAP_LOGIC_LANE_2 (2 << 6) +#define LANE3_MAP_LOGIC_LANE_3 (3 << 6) +#define LANE2_MAP_LOGIC_LANE_0 (0 << 4) +#define LANE2_MAP_LOGIC_LANE_1 (1 << 4) +#define LANE2_MAP_LOGIC_LANE_2 (2 << 4) +#define LANE2_MAP_LOGIC_LANE_3 (3 << 4) +#define LANE1_MAP_LOGIC_LANE_0 (0 << 2) +#define LANE1_MAP_LOGIC_LANE_1 (1 << 2) +#define LANE1_MAP_LOGIC_LANE_2 (2 << 2) +#define LANE1_MAP_LOGIC_LANE_3 (3 << 2) +#define LANE0_MAP_LOGIC_LANE_0 (0 << 0) +#define LANE0_MAP_LOGIC_LANE_1 (1 << 0) +#define LANE0_MAP_LOGIC_LANE_2 (2 << 0) +#define LANE0_MAP_LOGIC_LANE_3 (3 << 0) + +/* DP_AUX_HW_RETRY_CTL */ +#define AUX_BIT_PERIOD_SHIFT 8 +#define AUX_BIT_PERIOD_MASK 7 + +#define AUX_HW_RETRY_INTERVAL_SHIFT 3 +#define AUX_HW_RETRY_INTERVAL_600_US 0 +#define AUX_HW_RETRY_INTERVAL_800_US 1 +#define AUX_HW_RETRY_INTERVAL_1000_US 2 +#define AUX_HW_RETRY_INTERVAL_1800_US 3 +#define AUX_HW_RETRY_COUNT_SHIFT 0 +#define AUX_HW_RETRY_COUNT_MASK 7 + +/* DP_COMMON_INT_STA_1 */ +#define VSYNC_DET (1 << 7) +#define PLL_LOCK_CHG (1 << 6) +#define SPDIF_ERR (1 << 5) +#define SPDIF_UNSTBL (1 << 4) +#define VID_FORMAT_CHG (1 << 3) +#define AUD_CLK_CHG (1 << 2) +#define VID_CLK_CHG (1 << 1) +#define SW_INT (1 << 0) + +/* DP_COMMON_INT_STA_2 */ +#define ENC_EN_CHG (1 << 6) +#define HW_BKSV_RDY (1 << 3) +#define HW_SHA_DONE (1 << 2) +#define HW_AUTH_STATE_CHG (1 << 1) +#define HW_AUTH_DONE (1 << 0) + +/* DP_COMMON_INT_STA_3 */ +#define AFIFO_UNDER (1 << 7) +#define AFIFO_OVER (1 << 6) +#define R0_CHK_FLAG (1 << 5) + +/* DP_COMMON_INT_STA_4 */ +#define PSR_ACTIVE (1 << 7) +#define PSR_INACTIVE (1 << 6) +#define SPDIF_BI_PHASE_ERR (1 << 5) +#define HOTPLUG_CHG (1 << 2) +#define HPD_LOST (1 << 1) +#define PLUG (1 << 0) + +/* DP_INT_STA */ +#define INT_HPD (1 << 6) +#define HW_TRAINING_FINISH (1 << 5) +#define RPLY_RECEIV (1 << 1) +#define AUX_ERR (1 << 0) + +/* DP_INT_CTL */ +#define INT_POL0 (1 << 0) +#define INT_POL1 (1 << 1) +#define SOFT_INT_CTRL (1 << 2) + +/* DP_SYS_CTL_1 */ +#define DET_STA (1 << 2) +#define FORCE_DET (1 << 1) +#define DET_CTRL (1 << 0) + +/* DP_SYS_CTL_2 */ +#define CHA_CRI_SHIFT 4 +#define CHA_CRI_MASK 0xf +#define CHA_STA (1 << 2) +#define FORCE_CHA (1 << 1) +#define CHA_CTRL (1 << 0) + +/* DP_SYS_CTL_3 */ +#define HPD_STATUS (1 << 6) +#define F_HPD (1 << 5) +#define HPD_CTRL (1 << 4) +#define HDCP_RDY (1 << 3) +#define STRM_VALID (1 << 2) +#define F_VALID (1 << 1) +#define VALID_CTRL (1 << 0) + +/* DP_SYS_CTL_4 */ +#define FIX_M_AUD (1 << 4) +#define ENHANCED (1 << 3) +#define FIX_M_VID (1 << 2) +#define M_VID_UPDATE_CTRL (3 << 0) + +/* DP_TRAINING_PTN_SET */ +#define SCRAMBLER_TYPE (1 << 9) +#define HW_LINK_TRAINING_PATTERN (1 << 8) +#define SCRAMBLING_DISABLE (1 << 5) +#define SCRAMBLING_ENABLE (0 << 5) +#define LINK_QUAL_PATTERN_SET_MASK (3 << 2) +#define LINK_QUAL_PATTERN_SET_PRBS7 (3 << 2) +#define LINK_QUAL_PATTERN_SET_D10_2 (1 << 2) +#define LINK_QUAL_PATTERN_SET_DISABLE (0 << 2) +#define SW_TRAINING_PATTERN_SET_MASK (3 << 0) +#define SW_TRAINING_PATTERN_SET_PTN2 (2 << 0) +#define SW_TRAINING_PATTERN_SET_PTN1 (1 << 0) +#define SW_TRAINING_PATTERN_SET_NORMAL (0 << 0) + +/* DP_LN0_LINK_TRAINING_CTL */ +#define PRE_EMPHASIS_SET_SHIFT (3) + +/* DP_DEBUG_CTL */ +#define PLL_LOCK (1 << 4) +#define F_PLL_LOCK (1 << 3) +#define PLL_LOCK_CTRL (1 << 2) +#define PN_INV (1 << 0) + +/* DP_M_VID */ +#define M_VID_0_VALUE_SHIFT 0 +#define M_VID_1_VALUE_SHIFT 8 +#define M_VID_2_VALUE_SHIFT 16 + +/* DP_M_VID */ +#define N_VID_0_VALUE_SHIFT 0 +#define N_VID_1_VALUE_SHIFT 8 +#define N_VID_2_VALUE_SHIFT 16 + +/* DP_PLL_CTL */ +#define DP_PLL_PD (1 << 7) +#define DP_PLL_RESET (1 << 6) +#define DP_PLL_LOOP_BIT_DEFAULT (1 << 4) +#define DP_PLL_REF_BIT_1_1250V (5 << 0) +#define DP_PLL_REF_BIT_1_2500V (7 << 0) + +/* DP_PHY_PD */ +#define DP_PHY_PD (1 << 5) +#define AUX_PD (1 << 4) +#define CH3_PD (1 << 3) +#define CH2_PD (1 << 2) +#define CH1_PD (1 << 1) +#define CH0_PD (1 << 0) + +/* DP_PHY_TEST */ +#define MACRO_RST (1 << 5) +#define CH1_TEST (1 << 1) +#define CH0_TEST (1 << 0) + +/* DP_AUX_CH_STA */ +#define AUX_BUSY (1 << 4) +#define AUX_STATUS_MASK (0xf << 0) + +/* DP_AUX_CH_DEFER_CTL */ +#define DEFER_CTRL_EN (1 << 7) +#define DEFER_COUNT_SHIFT 0 +#define DEFER_COUNT_MASK 0x7f + +/* DP_AUX_RX_COMM */ +#define AUX_RX_COMM_I2C_DEFER (2 << 2) +#define AUX_RX_COMM_AUX_DEFER (2 << 0) + +/* DP_BUFFER_DATA_CTL */ +#define BUF_CLR (1 << 7) + +/* Maximum number of tries for Aux Transaction */ +#define MAX_AUX_RETRY_COUNT 10 + +/* DP_AUX_CH_CTL_1 */ +#define AUX_LENGTH_SHIFT 4 +#define AUX_LENGTH_MASK 0xf + +#define AUX_TX_COMM_MASK (0xf << 0) +#define AUX_TX_COMM_DP_TRANSACTION (1 << 3) +#define AUX_TX_COMM_I2C_TRANSACTION (0 << 3) +#define AUX_TX_COMM_MOT (1 << 2) +#define AUX_TX_COMM_WRITE (0 << 0) +#define AUX_TX_COMM_READ (1 << 0) + +/* DP_AUX_ADDR_7_0 */ +#define AUX_ADDR_7_0_SHIFT 0 +#define AUX_ADDR_7_0_MASK 0xff + +/* DP_AUX_ADDR_15_8 */ +#define AUX_ADDR_15_8_SHIFT 8 +#define AUX_ADDR_15_8_MASK 0xff + +/* DP_AUX_ADDR_19_16 */ +#define AUX_ADDR_19_16_SHIFT 16 +#define AUX_ADDR_19_16_MASK 0x0f + +/* DP_AUX_CH_CTL_2 */ +#define ADDR_ONLY (1 << 1) +#define AUX_EN (1 << 0) + +/* DP_SOC_GENERAL_CTL */ +#define AUDIO_MODE_SPDIF_MODE (1 << 8) +#define AUDIO_MODE_MASTER_MODE (0 << 8) +#define MASTER_VIDEO_INTERLACE_EN (1 << 4) +#define VIDEO_MASTER_CLK_SEL (1 << 2) +#define VIDEO_MASTER_MODE_EN (1 << 1) +#define VIDEO_MODE_MASK (1 << 0) +#define VIDEO_MODE_SLAVE_MODE (1 << 0) +#define VIDEO_MODE_MASTER_MODE (0 << 0) + +#define HW_TRAINING_ERROR_CODE (7<<4) +#define HW_TRAINING_EN (1<<0) + +/* I2C EDID Chip ID, Slave Address */ +#define I2C_EDID_DEVICE_ADDR 0x50 +#define I2C_E_EDID_DEVICE_ADDR 0x30 + +#define EDID_BLOCK_LENGTH 0x80 +#define EDID_HEADER_PATTERN 0x00 +#define EDID_EXTENSION_FLAG 0x7e +#define EDID_CHECKSUM 0x7f + +/* Definition for DPCD Register */ +#define DPCD_ADDR_DPCD_REV 0x0000 +#define DPCD_ADDR_MAX_LINK_RATE 0x0001 +#define DPCD_ADDR_MAX_LANE_COUNT 0x0002 +#define DPCD_ADDR_LINK_BW_SET 0x0100 +#define DPCD_ADDR_LANE_COUNT_SET 0x0101 +#define DPCD_ADDR_TRAINING_PATTERN_SET 0x0102 +#define DPCD_ADDR_TRAINING_LANE0_SET 0x0103 +#define DPCD_ADDR_LANE0_1_STATUS 0x0202 +#define DPCD_ADDR_LANE_ALIGN__STATUS_UPDATED 0x0204 +#define DPCD_ADDR_ADJUST_REQUEST_LANE0_1 0x0206 +#define DPCD_ADDR_ADJUST_REQUEST_LANE2_3 0x0207 +#define DPCD_ADDR_TEST_REQUEST 0x0218 +#define DPCD_ADDR_TEST_RESPONSE 0x0260 +#define DPCD_ADDR_TEST_EDID_CHECKSUM 0x0261 +#define DPCD_ADDR_SINK_POWER_STATE 0x0600 + +/* DPCD_ADDR_MAX_LANE_COUNT */ +#define DPCD_MAX_LANE_COUNT_MASK 0x1f + +/* DPCD_ADDR_LANE_COUNT_SET */ +#define DPCD_ENHANCED_FRAME_EN (1 << 7) +#define DPCD_LANE_COUNT_SET_MASK 0x1f + +/* DPCD_ADDR_TRAINING_PATTERN_SET */ +#define DPCD_SCRAMBLING_DISABLED (1 << 5) +#define DPCD_SCRAMBLING_ENABLED (0 << 5) +#define DPCD_TRAINING_PATTERN_2 (2 << 0) +#define DPCD_TRAINING_PATTERN_1 (1 << 0) +#define DPCD_TRAINING_PATTERN_DISABLED (0 << 0) + +/* DPCD_ADDR_LANE0_1_STATUS */ +#define DPCD_LANE_SYMBOL_LOCKED (1 << 2) +#define DPCD_LANE_CHANNEL_EQ_DONE (1 << 1) +#define DPCD_LANE_CR_DONE (1 << 0) +#define DPCD_CHANNEL_EQ_BITS (DPCD_LANE_CR_DONE | \ + DPCD_LANE_CHANNEL_EQ_DONE | \ + DPCD_LANE_SYMBOL_LOCKED) + +/* DPCD_ADDR_LANE_ALIGN__STATUS_UPDATED */ +#define DPCD_LINK_STATUS_UPDATED (1 << 7) +#define DPCD_DOWNSTREAM_PORT_STATUS_CHANGED (1 << 6) +#define DPCD_INTERLANE_ALIGN_DONE (1 << 0) + +/* DPCD_ADDR_TEST_REQUEST */ +#define DPCD_TEST_EDID_READ (1 << 2) + +/* DPCD_ADDR_TEST_RESPONSE */ +#define DPCD_TEST_EDID_CHECKSUM_WRITE (1 << 2) + +/* DPCD_ADDR_SINK_POWER_STATE */ +#define DPCD_SET_POWER_STATE_D0 (1 << 0) +#define DPCD_SET_POWER_STATE_D4 (2 << 0) + +/* Allow DP Gating clock and set FIMD source to 267 Mhz for DP */ +void clock_init_dp_clock(void); + +#endif diff --git a/src/soc/samsung/exynos5250/dsim.h b/src/soc/samsung/exynos5250/dsim.h new file mode 100644 index 0000000000..b9245d31e6 --- /dev/null +++ b/src/soc/samsung/exynos5250/dsim.h @@ -0,0 +1,109 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2012 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* Register map for Exynos5 MIPI-DSIM */ + +#ifndef CPU_SAMSUNG_EXYNOS5250_DSIM_H +#define CPU_SAMSUNG_EXYNOS5250_DSIM_H + +/* DSIM register map */ +struct exynos5_dsim { + unsigned int status; + unsigned int swrst; + unsigned int clkctrl; + unsigned int timeout; + unsigned int config; + unsigned int escmode; + unsigned int mdresol; + unsigned int mvporch; + unsigned int mhporch; + unsigned int msync; + unsigned int sdresol; + unsigned int intsrc; + unsigned int intmsk; + unsigned int pkthdr; + unsigned int payload; + unsigned int rxfifo; + unsigned int res1; + unsigned int fifoctrl; + unsigned int res2; + unsigned int pllctrl; + unsigned int plltmr; + unsigned int phyacchr; + unsigned int phyacchr1; +}; + +#define ENABLE 1 +#define DISABLE 0 + +#define DSIM_SWRST (1 << 0) +#define NUM_OF_DAT_LANE_IS_FOUR (3 << 5) +#define DATA_LANE_0_EN (1 << 0) +#define DATA_LANE_1_EN (1 << 1) +#define DATA_LANE_2_EN (1 << 2) +#define DATA_LANE_3_EN (1 << 3) +#define CLK_LANE_EN (1 << 4) +#define ENABLE_ALL_DATA_LANE DATA_LANE_0_EN | \ + DATA_LANE_1_EN | \ + DATA_LANE_2_EN | \ + DATA_LANE_3_EN +#define MAIN_PIX_FORMAT_OFFSET 12 +#define RGB_565_16_BIT 0x4 +#define VIDEO_MODE (1 << 25) +#define BURST_MODE (1 << 26) + + +#define DSIM_PHYACCHR_AFC_EN (1 << 14) +#define DSIM_PHYACCHR_AFC_CTL_OFFSET 5 + +#define DSIM_PLLCTRL_PMS_OFFSET 1 +#define DSIM_FREQ_BAND_OFFSET 24 + +#define LANE_ESC_CLK_EN_ALL (0x1f << 19) +#define BYTE_CLK_EN (1 << 24) +#define DSIM_ESC_CLK_EN (1 << 28) +#define TXREQUEST_HS_CLK_ON (1 << 31) + +#define LP_MODE_ENABLE (1 << 7) +#define STOP_STATE_CNT_OFFSET 21 + +#define MAIN_VBP_OFFSET 0 +#define STABLE_VFP_OFFSET 16 +#define CMD_ALLOW_OFFSET 28 + +#define MAIN_HBP_OFFSET 0 +#define MAIN_HFP_OFFSET 16 + +#define MAIN_HSA_OFFSET 0 +#define MAIN_VSA_OFFSET 22 + +#define MAIN_STANDBY (1 << 31) +#define MAIN_VRESOL_OFFSET 16 +#define MAIN_HRESOL_OFFSET 0 + +#define SFR_FIFO_EMPTY (1 << 29) + +#define DSIM_PLL_EN_SHIFT (1 << 23) +#define PLL_STABLE (1 << 31) + +#define DSIM_STOP_STATE_DAT(x) (((x) & 0xf) << 0) +#define DSIM_STOP_STATE_CLK (1 << 8) +#define DSIM_TX_READY_HS_CLK (1 << 10) + +#endif diff --git a/src/soc/samsung/exynos5250/fb.c b/src/soc/samsung/exynos5250/fb.c new file mode 100644 index 0000000000..080be49250 --- /dev/null +++ b/src/soc/samsung/exynos5250/fb.c @@ -0,0 +1,578 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2013 Google Inc. + * Copyright (C) 2012 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* LCD driver for Exynos */ + +#include <delay.h> +#include <stdlib.h> +#include <string.h> +#include <timer.h> +#include <arch/io.h> +#include <console/console.h> +#include "power.h" +#include "sysreg.h" + +#include "dp.h" +#include "dp-core.h" +#include "fimd.h" +#include "i2c.h" + +/* + * Here is the rough outline of how we bring up the display: + * 1. Upon power-on Sink generates a hot plug detection pulse thru HPD + * 2. Source determines video mode by reading DPCD receiver capability field + * (DPCD 00000h to 0000Dh) including eDP CP capability register (DPCD + * 0000Dh). + * 3. Sink replies DPCD receiver capability field. + * 4. Source starts EDID read thru I2C-over-AUX. + * 5. Sink replies EDID thru I2C-over-AUX. + * 6. Source determines link configuration, such as MAX_LINK_RATE and + * MAX_LANE_COUNT. Source also determines which type of eDP Authentication + * method to use and writes DPCD link configuration field (DPCD 00100h to + * 0010Ah) including eDP configuration set (DPCD 0010Ah). + * 7. Source starts link training. Sink does clock recovery and equalization. + * 8. Source reads DPCD link status field (DPCD 00200h to 0020Bh). + * 9. Sink replies DPCD link status field. If main link is not stable, Source + * repeats Step 7. + * 10. Source sends MSA (Main Stream Attribute) data. Sink extracts video + * parameters and recovers stream clock. + * 11. Source sends video data. + */ + +/* To help debug any init errors here, define a list of possible errors */ +enum { + ERR_PLL_NOT_UNLOCKED = 2, + ERR_VIDEO_CLOCK_BAD, + ERR_VIDEO_STREAM_BAD, + ERR_DPCD_READ_ERROR1, /* 5 */ + + ERR_DPCD_WRITE_ERROR1, + ERR_DPCD_READ_ERROR2, + ERR_DPCD_WRITE_ERROR2, + ERR_INVALID_LANE, + ERR_PLL_NOT_LOCKED, /* 10 */ + + ERR_PRE_EMPHASIS_LEVELS, + ERR_LINK_RATE_ABNORMAL, + ERR_MAX_LANE_COUNT_ABNORMAL, + ERR_LINK_TRAINING_FAILURE, + ERR_MISSING_DP_BASE, /* 15 */ + + ERR_NO_FDT_NODE, +}; +/* ok, this is stupid, but we're going to leave the variables in here until we + * know it works. One cleanup task at a time. + */ +enum stage_t { + STAGE_START = 0, + STAGE_LCD_VDD, + STAGE_BRIDGE_SETUP, + STAGE_BRIDGE_INIT, + STAGE_BRIDGE_RESET, + STAGE_HOTPLUG, + STAGE_DP_CONTROLLER, + STAGE_BACKLIGHT_VDD, + STAGE_BACKLIGHT_PWM, + STAGE_BACKLIGHT_EN, + STAGE_DONE, +}; + +int lcd_line_length; +int lcd_color_fg; +int lcd_color_bg; + +void *lcd_console_address; /* Start of console buffer */ + +short console_col; +short console_row; + +/* Bypass FIMD of DISP1_BLK */ +static void fimd_bypass(void) +{ + setbits_le32(&exynos_sysreg->disp1blk_cfg, FIMDBYPASS_DISP1); + exynos_sysreg->disp1blk_cfg &= ~FIMDBYPASS_DISP1; +} + +/* + * Initialize display controller. + * + * @param lcdbase pointer to the base address of framebuffer. + * @pd pointer to the main panel_data structure + */ +void fb_init(unsigned long int fb_size, void *lcdbase, + struct exynos5_fimd_panel *pd) +{ + unsigned int val; + + fb_size = ALIGN(fb_size, 4096); + + writel(pd->ivclk | pd->fixvclk, &exynos_disp_ctrl->vidcon1); + val = ENVID_ON | ENVID_F_ON | (pd->clkval_f << CLKVAL_F_OFFSET); + writel(val, &exynos_fimd->vidcon0); + + val = (pd->vsync << VSYNC_PULSE_WIDTH_OFFSET) | + (pd->lower_margin << V_FRONT_PORCH_OFFSET) | + (pd->upper_margin << V_BACK_PORCH_OFFSET); + writel(val, &exynos_disp_ctrl->vidtcon0); + + val = (pd->hsync << HSYNC_PULSE_WIDTH_OFFSET) | + (pd->right_margin << H_FRONT_PORCH_OFFSET) | + (pd->left_margin << H_BACK_PORCH_OFFSET); + writel(val, &exynos_disp_ctrl->vidtcon1); + + val = ((pd->xres - 1) << HOZVAL_OFFSET) | + ((pd->yres - 1) << LINEVAL_OFFSET); + writel(val, &exynos_disp_ctrl->vidtcon2); + + writel((unsigned int)lcdbase, &exynos_fimd->vidw00add0b0); + writel((unsigned int)lcdbase + fb_size, &exynos_fimd->vidw00add1b0); + + writel(pd->xres * 2, &exynos_fimd->vidw00add2); + + val = ((pd->xres - 1) << OSD_RIGHTBOTX_F_OFFSET); + val |= ((pd->yres - 1) << OSD_RIGHTBOTY_F_OFFSET); + writel(val, &exynos_fimd->vidosd0b); + writel(pd->xres * pd->yres, &exynos_fimd->vidosd0c); + + setbits_le32(&exynos_fimd->shadowcon, CHANNEL0_EN); + + val = BPPMODE_F_RGB_16BIT_565 << BPPMODE_F_OFFSET; + val |= ENWIN_F_ENABLE | HALF_WORD_SWAP_EN; + writel(val, &exynos_fimd->wincon0); + + /* DPCLKCON_ENABLE */ + writel(1 << 1, &exynos_fimd->dpclkcon); +} + +#ifdef UNUSED_CODE +void exynos_fimd_disable(void) +{ + writel(0, &exynos_fimd->wincon0); + clrbits_le32(&exynos_fimd->shadowcon, CHANNEL0_EN); +} +#endif + +/* + * Configure DP in slave mode and wait for video stream. + * + * param dp pointer to main s5p-dp structure + * param video_info pointer to main video_info structure. + * return status + */ +static int s5p_dp_config_video(struct s5p_dp_device *dp, + struct video_info *video_info) +{ + int timeout = 0; + struct exynos5_dp *base = dp->base; + struct mono_time start, current, end; + s5p_dp_config_video_slave_mode(dp, video_info); + + s5p_dp_set_video_color_format(dp, video_info->color_depth, + video_info->color_space, + video_info->dynamic_range, + video_info->ycbcr_coeff); + + if (s5p_dp_get_pll_lock_status(dp) == PLL_UNLOCKED) { + printk(BIOS_DEBUG, "PLL is not locked yet.\n"); + return -ERR_PLL_NOT_UNLOCKED; + } + + timer_monotonic_get(&start); + end = current = start; + mono_time_add_usecs(&end, STREAM_ON_TIMEOUT * USECS_PER_MSEC); + do { + if (s5p_dp_is_slave_video_stream_clock_on(dp) == 0) { + timeout++; + break; + } + timer_monotonic_get(¤t); + } while (mono_time_before(¤t, &end)); + + if (!timeout) { + printk(BIOS_ERR, "Video Clock Not ok after %ldus.\n", + mono_time_diff_microseconds(&start, &end)); + return -ERR_VIDEO_CLOCK_BAD; + } + + /* Set to use the register calculated M/N video */ + s5p_dp_set_video_cr_mn(dp, CALCULATED_M, 0, 0); + + clrbits_le32(&base->video_ctl_10, FORMAT_SEL); + + /* Disable video mute */ + clrbits_le32(&base->video_ctl_1, HDCP_VIDEO_MUTE); + + /* Configure video slave mode */ + s5p_dp_enable_video_master(dp); + + /* Enable video */ + setbits_le32(&base->video_ctl_1, VIDEO_EN); + timeout = s5p_dp_is_video_stream_on(dp); + + if (timeout) { + printk(BIOS_DEBUG, "Video Stream Not on\n"); + return -ERR_VIDEO_STREAM_BAD; + } + + return 0; +} + +/* + * Set DP to enhanced mode. We use this for EVT1 + * param dp pointer to main s5p-dp structure + * return status + */ +static int s5p_dp_enable_rx_to_enhanced_mode(struct s5p_dp_device *dp) +{ + u8 data; + + if (s5p_dp_read_byte_from_dpcd(dp, DPCD_ADDR_LANE_COUNT_SET, &data)) { + printk(BIOS_DEBUG, "DPCD read error\n"); + return -ERR_DPCD_READ_ERROR1; + } + if (s5p_dp_write_byte_to_dpcd(dp, DPCD_ADDR_LANE_COUNT_SET, + DPCD_ENHANCED_FRAME_EN | + (data & DPCD_LANE_COUNT_SET_MASK))) { + printk(BIOS_DEBUG, "DPCD write error\n"); + return -ERR_DPCD_WRITE_ERROR1; + } + + return 0; +} + +/* + * Enable scrambles mode. We use this for EVT1 + * param dp pointer to main s5p-dp structure + * return status + */ +static int s5p_dp_enable_scramble(struct s5p_dp_device *dp) +{ + u8 data; + struct exynos5_dp *base = dp->base; + + clrbits_le32(&base->dp_training_ptn_set, SCRAMBLING_DISABLE); + + if (s5p_dp_read_byte_from_dpcd(dp, DPCD_ADDR_TRAINING_PATTERN_SET, + &data)) { + printk(BIOS_DEBUG, "DPCD read error\n"); + return -ERR_DPCD_READ_ERROR2; + } + + if (s5p_dp_write_byte_to_dpcd(dp, DPCD_ADDR_TRAINING_PATTERN_SET, + (u8)(data & ~DPCD_SCRAMBLING_DISABLED))) { + printk(BIOS_DEBUG, "DPCD write error\n"); + return -ERR_DPCD_WRITE_ERROR2; + } + + return 0; +} + +/* + * Reset DP and prepare DP for init training + * param dp pointer to main s5p-dp structure + */ +static int s5p_dp_init_dp(struct s5p_dp_device *dp) +{ + int ret, i; + struct exynos5_dp *base = dp->base; + + for (i = 0; i < DP_INIT_TRIES; i++) { + s5p_dp_reset(dp); + + /* SW defined function Normal operation */ + clrbits_le32(&base->func_en_1, SW_FUNC_EN_N); + + ret = s5p_dp_init_analog_func(dp); + if (!ret) + break; + + udelay(5000); + printk(BIOS_DEBUG, "LCD retry init, attempt=%d ret=%d\n", i, ret); + } + if (i == DP_INIT_TRIES) { + printk(BIOS_DEBUG, "LCD initialization failed, ret=%d\n", ret); + return ret; + } + + s5p_dp_init_aux(dp); + + return ret; +} + +/* + * Set pre-emphasis level + * param dp pointer to main s5p-dp structure + * param pre_emphasis pre-emphasis level + * param lane lane number(0 - 3) + * return status + */ +static int s5p_dp_set_lane_lane_pre_emphasis(struct s5p_dp_device *dp, + int pre_emphasis, int lane) +{ + u32 reg; + struct exynos5_dp *base = dp->base; + + reg = pre_emphasis << PRE_EMPHASIS_SET_SHIFT; + switch (lane) { + case 0: + writel(reg, &base->ln0_link_trn_ctl); + break; + case 1: + writel(reg, &base->ln1_link_trn_ctl); + break; + + case 2: + writel(reg, &base->ln2_link_trn_ctl); + break; + + case 3: + writel(reg, &base->ln3_link_trn_ctl); + break; + default: + printk(BIOS_DEBUG, "%s: Invalid lane %d\n", __func__, lane); + return -ERR_INVALID_LANE; + } + return 0; +} + +/* + * Read supported bandwidth type + * param dp pointer to main s5p-dp structure + * param bandwidth pointer to variable holding bandwidth type + */ +static void s5p_dp_get_max_rx_bandwidth(struct s5p_dp_device *dp, + u8 *bandwidth) +{ + u8 data; + + /* + * For DP rev.1.1, Maximum link rate of Main Link lanes + * 0x06 = 1.62 Gbps, 0x0a = 2.7 Gbps + */ + s5p_dp_read_byte_from_dpcd(dp, DPCD_ADDR_MAX_LINK_RATE, &data); + *bandwidth = data; +} + +/* + * Reset DP and prepare DP for init training + * param dp pointer to main s5p-dp structure + * param lane_count pointer to variable holding no of lanes + */ +static void s5p_dp_get_max_rx_lane_count(struct s5p_dp_device *dp, + u8 *lane_count) +{ + u8 data; + + /* + * For DP rev.1.1, Maximum number of Main Link lanes + * 0x01 = 1 lane, 0x02 = 2 lanes, 0x04 = 4 lanes + */ + s5p_dp_read_byte_from_dpcd(dp, DPCD_ADDR_MAX_LANE_COUNT, &data); + *lane_count = data & DPCD_MAX_LANE_COUNT_MASK; +} + +/* + * DP H/w Link Training. Set DPCD link rate and bandwidth. + * param dp pointer to main s5p-dp structure + * param max_lane No of lanes + * param max_rate bandwidth + * return status + */ +static int s5p_dp_hw_link_training(struct s5p_dp_device *dp, + unsigned int max_lane, + unsigned int max_rate) +{ + int pll_is_locked = 0; + u32 data; + int lane; + struct mono_time current, end; + struct exynos5_dp *base = dp->base; + + /* Stop Video */ + clrbits_le32(&base->video_ctl_1, VIDEO_EN); + + timer_monotonic_get(¤t); + end = current; + mono_time_add_msecs(&end, PLL_LOCK_TIMEOUT); + + while ((pll_is_locked = s5p_dp_get_pll_lock_status(dp)) == PLL_UNLOCKED) { + if (mono_time_after(¤t, &end)) { + /* Ignore this error, and try to continue */ + printk(BIOS_ERR, "PLL is not locked yet.\n"); + break; + } + timer_monotonic_get(¤t); + } + printk(BIOS_SPEW, "PLL is %slocked\n", + pll_is_locked == PLL_LOCKED ? "": "not "); + /* Reset Macro */ + setbits_le32(&base->dp_phy_test, MACRO_RST); + + /* 10 us is the minimum reset time. */ + udelay(10); + + clrbits_le32(&base->dp_phy_test, MACRO_RST); + + /* Set TX pre-emphasis to minimum */ + for (lane = 0; lane < max_lane; lane++) + if (s5p_dp_set_lane_lane_pre_emphasis(dp, + PRE_EMPHASIS_LEVEL_0, lane)) { + printk(BIOS_DEBUG, "Unable to set pre emphasis level\n"); + return -ERR_PRE_EMPHASIS_LEVELS; + } + + /* All DP analog module power up */ + writel(0x00, &base->dp_phy_pd); + + /* Initialize by reading RX's DPCD */ + s5p_dp_get_max_rx_bandwidth(dp, &dp->link_train.link_rate); + s5p_dp_get_max_rx_lane_count(dp, &dp->link_train.lane_count); + + printk(BIOS_SPEW, "%s: rate 0x%x, lane_count %d\n", __func__, + dp->link_train.link_rate, dp->link_train.lane_count); + + if ((dp->link_train.link_rate != LINK_RATE_1_62GBPS) && + (dp->link_train.link_rate != LINK_RATE_2_70GBPS)) { + printk(BIOS_DEBUG, "Rx Max Link Rate is abnormal :%x !\n", + dp->link_train.link_rate); + /* Not Retrying */ + return -ERR_LINK_RATE_ABNORMAL; + } + + if (dp->link_train.lane_count == 0) { + printk(BIOS_DEBUG, "Rx Max Lane count is abnormal :%x !\n", + dp->link_train.lane_count); + /* Not retrying */ + return -ERR_MAX_LANE_COUNT_ABNORMAL; + } + + /* Setup TX lane count & rate */ + if (dp->link_train.lane_count > max_lane) + dp->link_train.lane_count = max_lane; + if (dp->link_train.link_rate > max_rate) + dp->link_train.link_rate = max_rate; + + /* Set link rate and count as you want to establish*/ + writel(dp->link_train.lane_count, &base->lane_count_set); + writel(dp->link_train.link_rate, &base->link_bw_set); + + /* Set sink to D0 (Sink Not Ready) mode. */ + s5p_dp_write_byte_to_dpcd(dp, DPCD_ADDR_SINK_POWER_STATE, + DPCD_SET_POWER_STATE_D0); + + /* Start HW link training */ + writel(HW_TRAINING_EN, &base->dp_hw_link_training); + + /* Wait until HW link training done */ + s5p_dp_wait_hw_link_training_done(dp); + + /* Get hardware link training status */ + data = readl(&base->dp_hw_link_training); + printk(BIOS_SPEW, "hardware link training status: 0x%08x\n", data); + if (data != 0) { + printk(BIOS_ERR, " H/W link training failure: 0x%x\n", data); + return -ERR_LINK_TRAINING_FAILURE; + } + + /* Get Link Bandwidth */ + data = readl(&base->link_bw_set); + + dp->link_train.link_rate = data; + + data = readl(&base->lane_count_set); + dp->link_train.lane_count = data; + printk(BIOS_SPEW, "Done training: Link bandwidth: 0x%x, lane_count: %d\n", + dp->link_train.link_rate, data); + + return 0; +} + +/* + * Initialize DP display + */ +int dp_controller_init(struct s5p_dp_device *dp_device) +{ + int ret; + struct s5p_dp_device *dp = dp_device; + struct exynos5_dp *base; + + clock_init_dp_clock(); + + power_enable_dp_phy(); + ret = s5p_dp_init_dp(dp); + if (ret) { + printk(BIOS_ERR, "%s: Could not initialize dp\n", __func__); + return ret; + } + + ret = s5p_dp_hw_link_training(dp, dp->video_info->lane_count, + dp->video_info->link_rate); + if (ret) { + printk(BIOS_ERR, "unable to do link train\n"); + return ret; + } + /* Minimum delay after H/w Link training */ + udelay(1000); + + ret = s5p_dp_enable_scramble(dp); + if (ret) { + printk(BIOS_ERR, "unable to set scramble mode\n"); + return ret; + } + + ret = s5p_dp_enable_rx_to_enhanced_mode(dp); + if (ret) { + printk(BIOS_ERR, "unable to set enhanced mode\n"); + return ret; + } + + + base = dp->base; + /* Enable enhanced mode */ + setbits_le32(&base->sys_ctl_4, ENHANCED); + + writel(dp->link_train.lane_count, &base->lane_count_set); + writel(dp->link_train.link_rate, &base->link_bw_set); + + s5p_dp_init_video(dp); + ret = s5p_dp_config_video(dp, dp->video_info); + if (ret) { + printk(BIOS_ERR, "unable to config video\n"); + return ret; + } + + return 0; +} + +/** + * Init the LCD controller + * + * @param lcdbase Base address of LCD frame buffer + * @return 0 if ok, -ve error code on error + */ +int lcd_ctrl_init(unsigned long int fb_size, + struct exynos5_fimd_panel *panel_data, void *lcdbase) +{ + int ret = 0; + + fimd_bypass(); + fb_init(fb_size, lcdbase, panel_data); + return ret; +} diff --git a/src/soc/samsung/exynos5250/fimd.h b/src/soc/samsung/exynos5250/fimd.h new file mode 100644 index 0000000000..178fb73d08 --- /dev/null +++ b/src/soc/samsung/exynos5250/fimd.h @@ -0,0 +1,145 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2012 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* Register map for Exynos5 FIMD */ + +#ifndef CPU_SAMSUNG_EXYNOS5250_FIMD_H +#define CPU_SAMSUNG_EXYNOS5250_FIMD_H + +#include "cpu.h" + +/* FIMD register map */ +struct exynos5_fimd { + /* This is an incomplete list. Add registers as and when required */ + unsigned int vidcon0; + unsigned char res1[0x1c]; + unsigned int wincon0; + unsigned int wincon1; + unsigned int wincon2; + unsigned int wincon3; + unsigned int wincon4; + unsigned int shadowcon; + unsigned char res2[0x8]; + unsigned int vidosd0a; + unsigned int vidosd0b; + unsigned int vidosd0c; + unsigned char res3[0x54]; + unsigned int vidw00add0b0; + unsigned char res4[0x2c]; + unsigned int vidw00add1b0; + unsigned char res5[0x2c]; + unsigned int vidw00add2; + unsigned char res6[0x3c]; + unsigned int w1keycon0; + unsigned int w1keycon1; + unsigned int w2keycon0; + unsigned int w2keycon1; + unsigned int w3keycon0; + unsigned int w3keycon1; + unsigned int w4keycon0; + unsigned int w4keycon1; + unsigned char res7[0x20]; + unsigned int win0map; + unsigned char res8[0xdc]; + unsigned int blendcon; + unsigned char res9[0x18]; + unsigned int dpclkcon; +}; + +static struct exynos5_fimd * const exynos_fimd = (void *)EXYNOS5_FIMD_BASE; + +#define W0_SHADOW_PROTECT (0x1 << 10) +#define COMPKEY_F 0xffffff +#define ENVID_F_ON (0x1 << 0) +#define ENVID_ON (0x1 << 1) +#define CLKVAL_F 0xb +#define CLKVAL_F_OFFSET 6 + +/* + * Structure containing display panel specific data for FIMD + */ +struct exynos5_fimd_panel { + unsigned int is_dp:1; /* Display Panel interface is eDP */ + unsigned int is_mipi:1; /* Display Panel interface is MIPI */ + unsigned int fixvclk:2; /* VCLK hold scheme at data underflow */ + + /* + * Polarity of the VCLK active edge + * 0-falling + * 1-rising + */ + unsigned int ivclk:1; + unsigned int clkval_f; /* Divider to create pixel clock */ + + unsigned int upper_margin; /* Vertical Backporch */ + unsigned int lower_margin; /* Vertical frontporch */ + unsigned int vsync; /* Vertical Sync Pulse Width */ + unsigned int left_margin; /* Horizontal Backporch */ + unsigned int right_margin; /* Horizontal Frontporch */ + unsigned int hsync; /* Horizontal Sync Pulse Width */ + unsigned int xres; /* X Resolution */ + unsigned int yres; /* Y Resolution */ +}; + +/* LCDIF Register Map */ +struct exynos5_disp_ctrl { + unsigned int vidout_con; + unsigned int vidcon1; + unsigned char res1[0x8]; + unsigned int vidtcon0; + unsigned int vidtcon1; + unsigned int vidtcon2; + unsigned int vidtcon3; + unsigned char res2[0x184]; + unsigned int trigcon; +}; + +static struct exynos5_disp_ctrl * const exynos_disp_ctrl = + (void *)EXYNOS5_DISP1_CTRL_BASE; + +#define VCLK_RISING_EDGE (1 << 7) +#define VCLK_RUNNING (1 << 9) + +#define CHANNEL0_EN (1 << 0) + +#define VSYNC_PULSE_WIDTH_VAL 0x3 +#define VSYNC_PULSE_WIDTH_OFFSET 0 +#define V_FRONT_PORCH_VAL 0x3 +#define V_FRONT_PORCH_OFFSET 8 +#define V_BACK_PORCH_VAL 0x3 +#define V_BACK_PORCH_OFFSET 16 + +#define HSYNC_PULSE_WIDTH_VAL 0x3 +#define HSYNC_PULSE_WIDTH_OFFSET 0 +#define H_FRONT_PORCH_VAL 0x3 +#define H_FRONT_PORCH_OFFSET 8 +#define H_BACK_PORCH_VAL 0x3 +#define H_BACK_PORCH_OFFSET 16 + +#define HOZVAL_OFFSET 0 +#define LINEVAL_OFFSET 11 + +#define BPPMODE_F_RGB_16BIT_565 0x5 +#define BPPMODE_F_OFFSET 2 +#define ENWIN_F_ENABLE (1 << 0) +#define HALF_WORD_SWAP_EN (1 << 16) + +#define OSD_RIGHTBOTX_F_OFFSET 11 +#define OSD_RIGHTBOTY_F_OFFSET 0 +#endif diff --git a/src/soc/samsung/exynos5250/gpio.c b/src/soc/samsung/exynos5250/gpio.c new file mode 100644 index 0000000000..2a93328a70 --- /dev/null +++ b/src/soc/samsung/exynos5250/gpio.c @@ -0,0 +1,279 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2009 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <console/console.h> +#include <string.h> +#include <delay.h> +#include <assert.h> +#include "gpio.h" +#include "cpu.h" + +#define CON_MASK(x) (0xf << ((x) << 2)) +#define CON_SFR(x, v) ((v) << ((x) << 2)) + +#define DAT_MASK(x) (0x1 << (x)) +#define DAT_SET(x) (0x1 << (x)) + +#define PULL_MASK(x) (0x3 << ((x) << 1)) +#define PULL_MODE(x, v) ((v) << ((x) << 1)) + +#define DRV_MASK(x) (0x3 << ((x) << 1)) +#define DRV_SET(x, m) ((m) << ((x) << 1)) +#define RATE_MASK(x) (0x1 << (x + 16)) +#define RATE_SET(x) (0x1 << (x + 16)) + +struct gpio_info { + unsigned int reg_addr; /* Address of register for this part */ + unsigned int max_gpio; /* Maximum GPIO in this part */ +}; + +static const struct gpio_info gpio_data[EXYNOS_GPIO_NUM_PARTS] = { + { EXYNOS5_GPIO_PART1_BASE, GPIO_MAX_PORT_PART_1 }, + { EXYNOS5_GPIO_PART2_BASE, GPIO_MAX_PORT_PART_2 }, + { EXYNOS5_GPIO_PART3_BASE, GPIO_MAX_PORT_PART_3 }, + { EXYNOS5_GPIO_PART4_BASE, GPIO_MAX_PORT_PART_4 }, + { EXYNOS5_GPIO_PART5_BASE, GPIO_MAX_PORT_PART_5 }, + { EXYNOS5_GPIO_PART6_BASE, GPIO_MAX_PORT }, +}; + +/* This macro gets gpio pin offset from 0..7 */ +#define GPIO_BIT(x) ((x) & 0x7) + +static struct gpio_bank *gpio_get_bank(unsigned int gpio) +{ + const struct gpio_info *data; + unsigned int upto; + int i; + + for (i = upto = 0, data = gpio_data; i < EXYNOS_GPIO_NUM_PARTS; + i++, upto = data->max_gpio, data++) { + if (gpio < data->max_gpio) { + struct gpio_bank *bank; + + bank = (struct gpio_bank *)data->reg_addr; + bank += (gpio - upto) / GPIO_PER_BANK; + return bank; + } + } + + ASSERT(gpio < GPIO_MAX_PORT); /* ...which it will not be */ + return NULL; +} + +/* Common GPIO API - only available on Exynos5 */ +void gpio_cfg_pin(int gpio, int cfg) +{ + unsigned int value; + struct gpio_bank *bank = gpio_get_bank(gpio); + + value = readl(&bank->con); + value &= ~CON_MASK(GPIO_BIT(gpio)); + value |= CON_SFR(GPIO_BIT(gpio), cfg); + writel(value, &bank->con); +} + +static int gpio_get_cfg(int gpio) +{ + struct gpio_bank *bank = gpio_get_bank(gpio); + int shift = GPIO_BIT(gpio) << 2; + + return (readl(&bank->con) & CON_MASK(GPIO_BIT(gpio))) >> shift; +} + +void gpio_set_pull(int gpio, int mode) +{ + unsigned int value; + struct gpio_bank *bank = gpio_get_bank(gpio); + + value = readl(&bank->pull); + value &= ~PULL_MASK(GPIO_BIT(gpio)); + + switch (mode) { + case GPIO_PULL_DOWN: + case GPIO_PULL_UP: + value |= PULL_MODE(GPIO_BIT(gpio), mode); + break; + default: + break; + } + + writel(value, &bank->pull); +} + +void gpio_set_drv(int gpio, int mode) +{ + unsigned int value; + struct gpio_bank *bank = gpio_get_bank(gpio); + + value = readl(&bank->drv); + value &= ~DRV_MASK(GPIO_BIT(gpio)); + + switch (mode) { + case GPIO_DRV_1X: + case GPIO_DRV_2X: + case GPIO_DRV_3X: + case GPIO_DRV_4X: + value |= DRV_SET(GPIO_BIT(gpio), mode); + break; + default: + return; + } + + writel(value, &bank->drv); +} + +void gpio_set_rate(int gpio, int mode) +{ + unsigned int value; + struct gpio_bank *bank = gpio_get_bank(gpio); + + value = readl(&bank->drv); + value &= ~RATE_MASK(GPIO_BIT(gpio)); + + switch (mode) { + case GPIO_DRV_FAST: + case GPIO_DRV_SLOW: + value |= RATE_SET(GPIO_BIT(gpio)); + break; + default: + return; + } + + writel(value, &bank->drv); +} + +int gpio_direction_input(unsigned gpio) +{ + gpio_cfg_pin(gpio, GPIO_INPUT); + + return 0; +} + +int gpio_direction_output(unsigned gpio, int value) +{ + unsigned int val; + struct gpio_bank *bank = gpio_get_bank(gpio); + + val = readl(&bank->dat); + val &= ~DAT_MASK(GPIO_BIT(gpio)); + if (value) + val |= DAT_SET(GPIO_BIT(gpio)); + writel(val, &bank->dat); + + gpio_cfg_pin(gpio, GPIO_OUTPUT); + + return 0; +} + +int gpio_get_value(unsigned gpio) +{ + unsigned int value; + struct gpio_bank *bank = gpio_get_bank(gpio); + + value = readl(&bank->dat); + return !!(value & DAT_MASK(GPIO_BIT(gpio))); +} + +int gpio_set_value(unsigned gpio, int value) +{ + unsigned int val; + struct gpio_bank *bank = gpio_get_bank(gpio); + + val = readl(&bank->dat); + val &= ~DAT_MASK(GPIO_BIT(gpio)); + if (value) + val |= DAT_SET(GPIO_BIT(gpio)); + writel(val, &bank->dat); + + return 0; +} + +/* + * Add a delay here to give the lines time to settle + * TODO(sjg): 1us does not always work, 2 is stable, so use 5 to be safe + * Come back to this and sort out what the datasheet says + */ +#define GPIO_DELAY_US 5 + +#ifndef __BOOT_BLOCK__ +/* + * FIXME(dhendrix): These functions use udelay, which has dependencies on + * pwm code and timer code. These aren't necessary for the bootblock and + * bloat the image significantly. + */ +int gpio_read_mvl3(unsigned gpio) +{ + int high, low; + enum mvl3 value; + + if (gpio >= GPIO_MAX_PORT) + return -1; + + gpio_direction_input(gpio); + gpio_set_pull(gpio, GPIO_PULL_UP); + udelay(GPIO_DELAY_US); + high = gpio_get_value(gpio); + gpio_set_pull(gpio, GPIO_PULL_DOWN); + udelay(GPIO_DELAY_US); + low = gpio_get_value(gpio); + + if (high && low) /* external pullup */ + value = LOGIC_1; + else if (!high && !low) /* external pulldown */ + value = LOGIC_0; + else /* floating */ + value = LOGIC_Z; + + /* + * Check if line is externally pulled high and + * configure the internal pullup to match. For + * floating and pulldowns, the GPIO is already + * configured with an internal pulldown from the + * above test. + */ + if (value == LOGIC_1) + gpio_set_pull(gpio, GPIO_PULL_UP); + + return value; +} +#endif /* __BOOT_BLOCK__ */ + +/* + * Display Exynos GPIO information + */ +void gpio_info(void) +{ + unsigned gpio; + + for (gpio = 0; gpio < GPIO_MAX_PORT; gpio++) { + int cfg = gpio_get_cfg(gpio); + + printk(BIOS_INFO, "GPIO_%-3d: ", gpio); + if (cfg == GPIO_INPUT) + printk(BIOS_INFO, "input"); + else if (cfg == GPIO_OUTPUT) + printk(BIOS_INFO, "output"); + else + printk(BIOS_INFO, "func %d", cfg); + + if (cfg == GPIO_INPUT || cfg == GPIO_OUTPUT) + printk(BIOS_INFO, ", value = %d", gpio_get_value(gpio)); + printk(BIOS_INFO, "\n"); + } +} diff --git a/src/soc/samsung/exynos5250/gpio.h b/src/soc/samsung/exynos5250/gpio.h new file mode 100644 index 0000000000..a8f22f7df0 --- /dev/null +++ b/src/soc/samsung/exynos5250/gpio.h @@ -0,0 +1,567 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2010 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef CPU_SAMSUNG_EXYNOS5250_GPIO_H +#define CPU_SAMSUNG_EXYNOS5250_GPIO_H + +#include "cpu.h" + +struct gpio_bank { + unsigned int con; + unsigned int dat; + unsigned int pull; + unsigned int drv; + unsigned int pdn_con; + unsigned int pdn_pull; + unsigned char res1[8]; +}; + +/* GPIO pins per bank */ +#define GPIO_PER_BANK 8 + +/* Pin configurations */ +#define GPIO_INPUT 0x0 +#define GPIO_OUTPUT 0x1 +#define GPIO_IRQ 0xf +#define GPIO_FUNC(x) (x) + +/* Pull mode */ +#define GPIO_PULL_NONE 0x0 +#define GPIO_PULL_DOWN 0x1 +#define GPIO_PULL_UP 0x3 + +/* Drive Strength level */ +#define GPIO_DRV_1X 0x0 +#define GPIO_DRV_3X 0x1 +#define GPIO_DRV_2X 0x2 +#define GPIO_DRV_4X 0x3 +#define GPIO_DRV_FAST 0x0 +#define GPIO_DRV_SLOW 0x1 + +enum exynos5_gpio_port { + EXYNOS5_GPA0 = EXYNOS5_GPIO_PART1_BASE + 0x0000, + EXYNOS5_GPA1 = EXYNOS5_GPIO_PART1_BASE + 0x0020, + EXYNOS5_GPA2 = EXYNOS5_GPIO_PART1_BASE + 0x0040, + + EXYNOS5_GPB0 = EXYNOS5_GPIO_PART1_BASE + 0x0060, + EXYNOS5_GPB1 = EXYNOS5_GPIO_PART1_BASE + 0x0080, + EXYNOS5_GPB2 = EXYNOS5_GPIO_PART1_BASE + 0x00a0, + EXYNOS5_GPB3 = EXYNOS5_GPIO_PART1_BASE + 0x00c0, + + EXYNOS5_GPC0 = EXYNOS5_GPIO_PART1_BASE + 0x00e0, + EXYNOS5_GPC1 = EXYNOS5_GPIO_PART1_BASE + 0x0100, + EXYNOS5_GPC2 = EXYNOS5_GPIO_PART1_BASE + 0x0120, + EXYNOS5_GPC3 = EXYNOS5_GPIO_PART1_BASE + 0x0140, + + EXYNOS5_GPD0 = EXYNOS5_GPIO_PART1_BASE + 0x0160, + EXYNOS5_GPD1 = EXYNOS5_GPIO_PART1_BASE + 0x0180, + + EXYNOS5_GPY0 = EXYNOS5_GPIO_PART1_BASE + 0x01a0, + EXYNOS5_GPY1 = EXYNOS5_GPIO_PART1_BASE + 0x01c0, + EXYNOS5_GPY2 = EXYNOS5_GPIO_PART1_BASE + 0x01e0, + EXYNOS5_GPY3 = EXYNOS5_GPIO_PART1_BASE + 0x0200, + EXYNOS5_GPY4 = EXYNOS5_GPIO_PART1_BASE + 0x0220, + EXYNOS5_GPY5 = EXYNOS5_GPIO_PART1_BASE + 0x0240, + EXYNOS5_GPY6 = EXYNOS5_GPIO_PART1_BASE + 0x0260, + + EXYNOS5_GPX0 = EXYNOS5_GPIO_PART2_BASE + 0x0000, + EXYNOS5_GPX1 = EXYNOS5_GPIO_PART2_BASE + 0x0020, + EXYNOS5_GPX2 = EXYNOS5_GPIO_PART2_BASE + 0x0040, + EXYNOS5_GPX3 = EXYNOS5_GPIO_PART2_BASE + 0x0060, + + EXYNOS5_GPE0 = EXYNOS5_GPIO_PART3_BASE + 0x0000, + EXYNOS5_GPE1 = EXYNOS5_GPIO_PART3_BASE + 0x0020, + + EXYNOS5_GPF0 = EXYNOS5_GPIO_PART3_BASE + 0x0040, + EXYNOS5_GPF1 = EXYNOS5_GPIO_PART3_BASE + 0x0060, + + EXYNOS5_GPG0 = EXYNOS5_GPIO_PART3_BASE + 0x0080, + EXYNOS5_GPG1 = EXYNOS5_GPIO_PART3_BASE + 0x00a0, + EXYNOS5_GPG2 = EXYNOS5_GPIO_PART3_BASE + 0x00c0, + + EXYNOS5_GPH0 = EXYNOS5_GPIO_PART3_BASE + 0x00e0, + EXYNOS5_GPH1 = EXYNOS5_GPIO_PART3_BASE + 0x0100, + + EXYNOS5_GPV0 = EXYNOS5_GPIO_PART4_BASE + 0x0000, + EXYNOS5_GPV1 = EXYNOS5_GPIO_PART4_BASE + 0x0020, + EXYNOS5_GPV2 = EXYNOS5_GPIO_PART4_BASE + 0x0060, + EXYNOS5_GPV3 = EXYNOS5_GPIO_PART4_BASE + 0x0080, + + EXYNOS5_GPV4 = EXYNOS5_GPIO_PART5_BASE + 0x0000, + + EXYNOS5_GPZ = EXYNOS5_GPIO_PART6_BASE + 0x0000, +}; + +enum { + /* GPIO banks are split into this many parts */ + EXYNOS_GPIO_NUM_PARTS = 6 +}; + +/* A list of valid GPIO numbers for the asm-generic/gpio.h interface */ +enum exynos5_gpio_pin { + /* GPIO_PART1_STARTS */ + GPIO_A00, + GPIO_A01, + GPIO_A02, + GPIO_A03, + GPIO_A04, + GPIO_A05, + GPIO_A06, + GPIO_A07, + GPIO_A10, + GPIO_A11, + GPIO_A12, + GPIO_A13, + GPIO_A14, + GPIO_A15, + GPIO_A16, + GPIO_A17, + GPIO_A20, + GPIO_A21, + GPIO_A22, + GPIO_A23, + GPIO_A24, + GPIO_A25, + GPIO_A26, + GPIO_A27, + GPIO_B00, /* 0x18 */ + GPIO_B01, + GPIO_B02, + GPIO_B03, + GPIO_B04, + GPIO_B05, + GPIO_B06, + GPIO_B07, + GPIO_B10, + GPIO_B11, + GPIO_B12, + GPIO_B13, + GPIO_B14, + GPIO_B15, + GPIO_B16, + GPIO_B17, + GPIO_B20, + GPIO_B21, + GPIO_B22, + GPIO_B23, + GPIO_B24, + GPIO_B25, + GPIO_B26, + GPIO_B27, + GPIO_B30, + GPIO_B31, + GPIO_B32, + GPIO_B33, + GPIO_B34, + GPIO_B35, + GPIO_B36, + GPIO_B37, + GPIO_C00, /* 0x38 */ + GPIO_C01, + GPIO_C02, + GPIO_C03, + GPIO_C04, + GPIO_C05, + GPIO_C06, + GPIO_C07, + GPIO_C10, + GPIO_C11, + GPIO_C12, + GPIO_C13, + GPIO_C14, + GPIO_C15, + GPIO_C16, + GPIO_C17, + GPIO_C20, + GPIO_C21, + GPIO_C22, + GPIO_C23, + GPIO_C24, + GPIO_C25, + GPIO_C26, + GPIO_C27, + GPIO_C30, + GPIO_C31, + GPIO_C32, + GPIO_C33, + GPIO_C34, + GPIO_C35, + GPIO_C36, + GPIO_C37, + GPIO_D00, /* 0x58 */ + GPIO_D01, + GPIO_D02, + GPIO_D03, + GPIO_D04, + GPIO_D05, + GPIO_D06, + GPIO_D07, + GPIO_D10, + GPIO_D11, + GPIO_D12, + GPIO_D13, + GPIO_D14, + GPIO_D15, + GPIO_D16, + GPIO_D17, + GPIO_Y00, /* 0x68 */ + GPIO_Y01, + GPIO_Y02, + GPIO_Y03, + GPIO_Y04, + GPIO_Y05, + GPIO_Y06, + GPIO_Y07, + GPIO_Y10, + GPIO_Y11, + GPIO_Y12, + GPIO_Y13, + GPIO_Y14, + GPIO_Y15, + GPIO_Y16, + GPIO_Y17, + GPIO_Y20, + GPIO_Y21, + GPIO_Y22, + GPIO_Y23, + GPIO_Y24, + GPIO_Y25, + GPIO_Y26, + GPIO_Y27, + GPIO_Y30, + GPIO_Y31, + GPIO_Y32, + GPIO_Y33, + GPIO_Y34, + GPIO_Y35, + GPIO_Y36, + GPIO_Y37, + GPIO_Y40, + GPIO_Y41, + GPIO_Y42, + GPIO_Y43, + GPIO_Y44, + GPIO_Y45, + GPIO_Y46, + GPIO_Y47, + GPIO_Y50, + GPIO_Y51, + GPIO_Y52, + GPIO_Y53, + GPIO_Y54, + GPIO_Y55, + GPIO_Y56, + GPIO_Y57, + GPIO_Y60, + GPIO_Y61, + GPIO_Y62, + GPIO_Y63, + GPIO_Y64, + GPIO_Y65, + GPIO_Y66, + GPIO_Y67, + + /* GPIO_PART2_STARTS */ + GPIO_MAX_PORT_PART_1, + GPIO_X00 = GPIO_MAX_PORT_PART_1, /* 0xa0 */ + GPIO_X01, + GPIO_X02, + GPIO_X03, + GPIO_X04, + GPIO_X05, + GPIO_X06, + GPIO_X07, + GPIO_X10, + GPIO_X11, + GPIO_X12, + GPIO_X13, + GPIO_X14, + GPIO_X15, + GPIO_X16, + GPIO_X17, + GPIO_X20, + GPIO_X21, + GPIO_X22, + GPIO_X23, + GPIO_X24, + GPIO_X25, + GPIO_X26, + GPIO_X27, + GPIO_X30, + GPIO_X31, + GPIO_X32, + GPIO_X33, + GPIO_X34, + GPIO_X35, + GPIO_X36, + GPIO_X37, + + /* GPIO_PART3_STARTS */ + GPIO_MAX_PORT_PART_2, + GPIO_E00 = GPIO_MAX_PORT_PART_2, /* 0xc0 */ + GPIO_E01, + GPIO_E02, + GPIO_E03, + GPIO_E04, + GPIO_E05, + GPIO_E06, + GPIO_E07, + GPIO_E10, + GPIO_E11, + GPIO_E12, + GPIO_E13, + GPIO_E14, + GPIO_E15, + GPIO_E16, + GPIO_E17, + GPIO_F00, /* 0xd0 */ + GPIO_F01, + GPIO_F02, + GPIO_F03, + GPIO_F04, + GPIO_F05, + GPIO_F06, + GPIO_F07, + GPIO_F10, + GPIO_F11, + GPIO_F12, + GPIO_F13, + GPIO_F14, + GPIO_F15, + GPIO_F16, + GPIO_F17, + GPIO_G00, + GPIO_G01, + GPIO_G02, + GPIO_G03, + GPIO_G04, + GPIO_G05, + GPIO_G06, + GPIO_G07, + GPIO_G10, + GPIO_G11, + GPIO_G12, + GPIO_G13, + GPIO_G14, + GPIO_G15, + GPIO_G16, + GPIO_G17, + GPIO_G20, + GPIO_G21, + GPIO_G22, + GPIO_G23, + GPIO_G24, + GPIO_G25, + GPIO_G26, + GPIO_G27, + GPIO_H00, + GPIO_H01, + GPIO_H02, + GPIO_H03, + GPIO_H04, + GPIO_H05, + GPIO_H06, + GPIO_H07, + GPIO_H10, + GPIO_H11, + GPIO_H12, + GPIO_H13, + GPIO_H14, + GPIO_H15, + GPIO_H16, + GPIO_H17, + + /* GPIO_PART4_STARTS */ + GPIO_MAX_PORT_PART_3, + GPIO_V00 = GPIO_MAX_PORT_PART_3, + GPIO_V01, + GPIO_V02, + GPIO_V03, + GPIO_V04, + GPIO_V05, + GPIO_V06, + GPIO_V07, + GPIO_V10, + GPIO_V11, + GPIO_V12, + GPIO_V13, + GPIO_V14, + GPIO_V15, + GPIO_V16, + GPIO_V17, + GPIO_V20, + GPIO_V21, + GPIO_V22, + GPIO_V23, + GPIO_V24, + GPIO_V25, + GPIO_V26, + GPIO_V27, + GPIO_V30, + GPIO_V31, + GPIO_V32, + GPIO_V33, + GPIO_V34, + GPIO_V35, + GPIO_V36, + GPIO_V37, + + /* GPIO_PART5_STARTS */ + GPIO_MAX_PORT_PART_4, + GPIO_V40 = GPIO_MAX_PORT_PART_4, + GPIO_V41, + GPIO_V42, + GPIO_V43, + GPIO_V44, + GPIO_V45, + GPIO_V46, + GPIO_V47, + + /* GPIO_PART6_STARTS */ + GPIO_MAX_PORT_PART_5, + GPIO_Z0 = GPIO_MAX_PORT_PART_5, + GPIO_Z1, + GPIO_Z2, + GPIO_Z3, + GPIO_Z4, + GPIO_Z5, + GPIO_Z6, + GPIO_MAX_PORT +}; + +/** + * Set GPIO pin configuration. + * + * @param gpio GPIO pin + * @param cfg Either GPIO_INPUT, GPIO_OUTPUT, or GPIO_IRQ + */ +void gpio_cfg_pin(int gpio, int cfg); + +/** + * Set GPIO pull mode. + * + * @param gpio GPIO pin + * @param mode Either GPIO_PULL_DOWN or GPIO_PULL_UP + */ +void gpio_set_pull(int gpio, int mode); + +/** + * Set GPIO drive strength level. + * + * @param gpio GPIO pin + * @param mode Either GPIO_DRV_1X, GPIO_DRV_2X, GPIO_DRV_3X, or GPIO_DRV_4X + */ +void gpio_set_drv(int gpio, int mode); + +/** + * Set GPIO drive rate. + * + * @param gpio GPIO pin + * @param mode Either GPIO_DRV_FAST or GPIO_DRV_SLOW + */ +void gpio_set_rate(int gpio, int mode); + +/* + * reads only a single GPIO + * + * @param gpio GPIO to read + * @return -1 if the value cannot be determined. Otherwise returns + * the corresponding MVL3 enum value. + */ +int gpio_read_mvl3(unsigned gpio); + +void gpio_info(void); + +/* + * Generic GPIO API for U-Boot + * + * GPIOs are numbered from 0 to GPIO_COUNT-1 which value is defined + * by the SOC/architecture. + * + * Each GPIO can be an input or output. If an input then its value can + * be read as 0 or 1. If an output then its value can be set to 0 or 1. + * If you try to write an input then the value is undefined. If you try + * to read an output, barring something very unusual, you will get + * back the value of the output that you previously set. + * + * In some cases the operation may fail, for example if the GPIO number + * is out of range, or the GPIO is not available because its pin is + * being used by another function. In that case, functions may return + * an error value of -1. + */ + +/** + * Make a GPIO an input. + * + * @param gpio GPIO number + * @return 0 if ok, -1 on error + */ +int gpio_direction_input(unsigned gpio); + +/** + * Make a GPIO an output, and set its value. + * + * @param gpio GPIO number + * @param value GPIO value (0 for low or 1 for high) + * @return 0 if ok, -1 on error + */ +int gpio_direction_output(unsigned gpio, int value); + +/** + * Get a GPIO's value. This will work whether the GPIO is an input + * or an output. + * + * @param gpio GPIO number + * @return 0 if low, 1 if high, -1 on error + */ +int gpio_get_value(unsigned gpio); + +/** + * Set an output GPIO's value. The GPIO must already be an output or + * this function may have no effect. + * + * @param gpio GPIO number + * @param value GPIO value (0 for low or 1 for high) + * @return 0 if ok, -1 on error + */ +int gpio_set_value(unsigned gpio, int value); + +/* + * Many-value logic (3 states). This can be used for inputs whereby presence + * of external pull-up or pull-down resistors can be added to overcome internal + * pull-ups/pull-downs and force a single value. + * + * Thus, external pull resistors can force a 0 or 1 and if the value changes + * along with internal pull-up/down enable then the input is floating. + * + * Vpd | Vpu | MVL + * ----------------- + * 0 | 0 | 0 + * ----------------- + * 0 | 1 | Z <-- floating input will follow internal pull up/down + * ----------------- + * 1 | 1 | 1 + */ +enum mvl3 { + LOGIC_0, + LOGIC_1, + LOGIC_Z, /* high impedence / tri-stated / floating */ +}; + +#endif /* CPU_SAMSUNG_EXYNOS5250_GPIO_H */ diff --git a/src/soc/samsung/exynos5250/i2c.c b/src/soc/samsung/exynos5250/i2c.c new file mode 100644 index 0000000000..e83ab57014 --- /dev/null +++ b/src/soc/samsung/exynos5250/i2c.c @@ -0,0 +1,403 @@ +/* + * This file is part of the coreboot project. + * + * (C) Copyright 2002 + * David Mueller, ELSOFT AG, d.mueller@elsoft.ch + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <console/console.h> +#include <delay.h> +#include <arch/io.h> +#include <device/i2c.h> +#include "clk.h" +#include "i2c.h" + +#define I2C_WRITE 0 +#define I2C_READ 1 + +#define I2C_OK 0 +#define I2C_NOK 1 +#define I2C_NACK 2 +#define I2C_NOK_LA 3 /* Lost arbitration */ +#define I2C_NOK_TOUT 4 /* time out */ + +#define I2CSTAT_BSY 0x20 /* Busy bit */ +#define I2CSTAT_NACK 0x01 /* Nack bit */ +#define I2CCON_ACKGEN 0x80 /* Acknowledge generation */ +#define I2CCON_IRPND 0x10 /* Interrupt pending bit */ +#define I2C_MODE_MT 0xC0 /* Master Transmit Mode */ +#define I2C_MODE_MR 0x80 /* Master Receive Mode */ +#define I2C_START_STOP 0x20 /* START / STOP */ +#define I2C_TXRX_ENA 0x10 /* I2C Tx/Rx enable */ + +/* The timeouts we live by */ +enum { + I2C_XFER_TIMEOUT_MS = 35, /* xfer to complete */ + I2C_INIT_TIMEOUT_MS = 1000, /* bus free on init */ + I2C_IDLE_TIMEOUT_MS = 100, /* waiting for bus idle */ + I2C_STOP_TIMEOUT_US = 200, /* waiting for stop events */ +}; + +static struct s3c24x0_i2c_bus i2c_buses[] = { + { + .bus_num = 0, + .regs = (struct s3c24x0_i2c *)0x12c60000, + .periph_id = PERIPH_ID_I2C0, + }, + { + .bus_num = 1, + .regs = (struct s3c24x0_i2c *)0x12c70000, + .periph_id = PERIPH_ID_I2C1, + }, + { + .bus_num = 2, + .regs = (struct s3c24x0_i2c *)0x12c80000, + .periph_id = PERIPH_ID_I2C2, + }, + { + .bus_num = 3, + .regs = (struct s3c24x0_i2c *)0x12c90000, + .periph_id = PERIPH_ID_I2C3, + }, + { + .bus_num = 4, + .regs = (struct s3c24x0_i2c *)0x12ca0000, + .periph_id = PERIPH_ID_I2C4, + }, + { + .bus_num = 5, + .regs = (struct s3c24x0_i2c *)0x12cb0000, + .periph_id = PERIPH_ID_I2C5, + }, + { + .bus_num = 6, + .regs = (struct s3c24x0_i2c *)0x12cc0000, + .periph_id = PERIPH_ID_I2C6, + }, + { + .bus_num = 7, + .regs = (struct s3c24x0_i2c *)0x12cd0000, + .periph_id = PERIPH_ID_I2C7, + }, +}; + +static int WaitForXfer(struct s3c24x0_i2c *i2c) +{ + int i; + + i = I2C_XFER_TIMEOUT_MS * 20; + while (!(readl(&i2c->iiccon) & I2CCON_IRPND)) { + if (i == 0) { + printk(BIOS_ERR, "%s: i2c xfer timeout\n", __func__); + return I2C_NOK_TOUT; + } + udelay(50); + i--; + } + + return I2C_OK; +} + +static int IsACK(struct s3c24x0_i2c *i2c) +{ + return !(readl(&i2c->iicstat) & I2CSTAT_NACK); +} + +static void ReadWriteByte(struct s3c24x0_i2c *i2c) +{ + uint32_t x; + + x = readl(&i2c->iiccon); + writel(x & ~I2CCON_IRPND, &i2c->iiccon); +} + +static void i2c_ch_init(struct s3c24x0_i2c_bus *bus, int speed, int slaveadd) +{ + unsigned long freq, pres = 16, div; + unsigned long val; + + freq = clock_get_periph_rate(bus->periph_id); + /* calculate prescaler and divisor values */ + if ((freq / pres / (16 + 1)) > speed) + /* set prescaler to 512 */ + pres = 512; + + div = 0; + + while ((freq / pres / (div + 1)) > speed) + div++; + + /* set prescaler, divisor according to freq, also set ACKGEN, IRQ */ + val = (div & 0x0F) | 0xA0 | ((pres == 512) ? 0x40 : 0); + writel(val, &bus->regs->iiccon); + + /* init to SLAVE RECEIVE mode and clear I2CADDn */ + writel(0, &bus->regs->iicstat); + writel(slaveadd, &bus->regs->iicadd); + /* program Master Transmit (and implicit STOP) */ + writel(I2C_MODE_MT | I2C_TXRX_ENA, &bus->regs->iicstat); +} + +/* + * MULTI BUS I2C support + */ +static void i2c_bus_init(struct s3c24x0_i2c_bus *bus, int speed, int slaveadd) +{ + i2c_ch_init(bus, speed, slaveadd); +} + +/* + * Verify the whether I2C ACK was received or not + * + * @param i2c pointer to I2C register base + * @param buf array of data + * @param len length of data + * return I2C_OK when transmission done + * I2C_NACK otherwise + */ +static int i2c_send_verify(struct s3c24x0_i2c *i2c, unsigned char buf[], + unsigned char len) +{ + int i, result = I2C_OK; + + if (IsACK(i2c)) { + for (i = 0; (i < len) && (result == I2C_OK); i++) { + writel(buf[i], &i2c->iicds); + ReadWriteByte(i2c); + result = WaitForXfer(i2c); + if (result == I2C_OK && !IsACK(i2c)) + result = I2C_NACK; + } + } else { + result = I2C_NACK; + } + + return result; +} + +void i2c_init(unsigned bus_num, int speed, int slaveadd) +{ + struct s3c24x0_i2c_bus *i2c; + int i; + + i2c = &i2c_buses[bus_num]; + i2c_bus_init(i2c, speed, slaveadd); + + /* wait for some time to give previous transfer a chance to finish */ + i = I2C_INIT_TIMEOUT_MS * 20; + while ((readl(&i2c->regs->iicstat) & I2CSTAT_BSY) && (i > 0)) { + udelay(50); + i--; + } + + i2c_ch_init(i2c, speed, slaveadd); +} + +/* + * Send a STOP event and wait for it to have completed + * + * @param mode If it is a master transmitter or receiver + * @return I2C_OK if the line became idle before timeout I2C_NOK_TOUT otherwise + */ +static int i2c_send_stop(struct s3c24x0_i2c *i2c, int mode) +{ + int timeout; + + /* Setting the STOP event to fire */ + writel(mode | I2C_TXRX_ENA, &i2c->iicstat); + ReadWriteByte(i2c); + + /* Wait for the STOP to send and the bus to go idle */ + for (timeout = I2C_STOP_TIMEOUT_US; timeout > 0; timeout -= 5) { + if (!(readl(&i2c->iicstat) & I2CSTAT_BSY)) + return I2C_OK; + udelay(5); + } + + return I2C_NOK_TOUT; +} + +/* + * cmd_type is 0 for write, 1 for read. + * + * addr_len can take any value from 0-255, it is only limited + * by the char, we could make it larger if needed. If it is + * 0 we skip the address write cycle. + */ +static int i2c_transfer(struct s3c24x0_i2c *i2c, + unsigned char cmd_type, + unsigned char chip, + unsigned char addr[], + unsigned char addr_len, + unsigned char data[], + unsigned short data_len) +{ + int i, result, stop_bit_result; + uint32_t x; + + if (data == 0 || data_len == 0) { + /* Don't support data transfer of no length or to address 0 */ + printk(BIOS_ERR, "i2c_transfer: bad call\n"); + return I2C_NOK; + } + + /* Check I2C bus idle */ + i = I2C_IDLE_TIMEOUT_MS * 20; + while ((readl(&i2c->iicstat) & I2CSTAT_BSY) && (i > 0)) { + udelay(50); + i--; + } + + if (readl(&i2c->iicstat) & I2CSTAT_BSY) { + printk(BIOS_ERR, "%s: bus busy\n", __func__); + return I2C_NOK_TOUT; + } + + x = readl(&i2c->iiccon); + writel(x | I2CCON_ACKGEN, &i2c->iiccon); + + if (addr && addr_len) { + writel(chip, &i2c->iicds); + /* send START */ + writel(I2C_MODE_MT | I2C_TXRX_ENA | I2C_START_STOP, + &i2c->iicstat); + if (WaitForXfer(i2c) == I2C_OK) + result = i2c_send_verify(i2c, addr, addr_len); + else + result = I2C_NACK; + } else + result = I2C_NACK; + + switch (cmd_type) { + case I2C_WRITE: + if (result == I2C_OK) + result = i2c_send_verify(i2c, data, data_len); + else { + writel(chip, &i2c->iicds); + /* send START */ + writel(I2C_MODE_MT | I2C_TXRX_ENA | I2C_START_STOP, + &i2c->iicstat); + if (WaitForXfer(i2c) == I2C_OK) + result = i2c_send_verify(i2c, data, data_len); + } + + if (result == I2C_OK) + result = WaitForXfer(i2c); + + stop_bit_result = i2c_send_stop(i2c, I2C_MODE_MT); + break; + + case I2C_READ: + { + int was_ok = (result == I2C_OK); + + writel(chip, &i2c->iicds); + /* resend START */ + writel(I2C_MODE_MR | I2C_TXRX_ENA | + I2C_START_STOP, &i2c->iicstat); + ReadWriteByte(i2c); + result = WaitForXfer(i2c); + + if (was_ok || IsACK(i2c)) { + i = 0; + while ((i < data_len) && (result == I2C_OK)) { + /* disable ACK for final READ */ + if (i == data_len - 1) { + x = readl(&i2c->iiccon) & ~I2CCON_ACKGEN; + writel(x, &i2c->iiccon); + } + ReadWriteByte(i2c); + result = WaitForXfer(i2c); + data[i] = readl(&i2c->iicds); + i++; + } + } else { + result = I2C_NACK; + } + + stop_bit_result = i2c_send_stop(i2c, I2C_MODE_MR); + break; + } + + default: + printk(BIOS_ERR, "i2c_transfer: bad call\n"); + result = stop_bit_result = I2C_NOK; + break; + } + + /* + * If the transmission went fine, then only the stop bit was left to + * fail. Otherwise, the real failure we're interested in came before + * that, during the actual transmission. + */ + return (result == I2C_OK) ? stop_bit_result : result; +} + +int i2c_read(unsigned bus, unsigned chip, unsigned addr, + unsigned alen, uint8_t *buf, unsigned len) +{ + struct s3c24x0_i2c_bus *i2c; + unsigned char xaddr[4]; + int ret; + + if (alen > 4) { + printk(BIOS_ERR, "I2C read: addr len %d not supported\n", alen); + return 1; + } + + if (alen > 0) { + xaddr[0] = (addr >> 24) & 0xFF; + xaddr[1] = (addr >> 16) & 0xFF; + xaddr[2] = (addr >> 8) & 0xFF; + xaddr[3] = addr & 0xFF; + } + + i2c = &i2c_buses[bus]; + ret = i2c_transfer(i2c->regs, I2C_READ, chip << 1, &xaddr[4 - alen], + alen, buf, len); + if (ret) { + printk(BIOS_ERR, "I2c read: failed %d\n", ret); + return 1; + } + return 0; +} + +int i2c_write(unsigned bus, unsigned chip, unsigned addr, + unsigned alen, const uint8_t *buf, unsigned len) +{ + struct s3c24x0_i2c_bus *i2c; + unsigned char xaddr[4]; + int ret; + + if (alen > 4) { + printk(BIOS_ERR, "I2C write: addr len %d not supported\n", + alen); + return 1; + } + + if (alen > 0) { + xaddr[0] = (addr >> 24) & 0xFF; + xaddr[1] = (addr >> 16) & 0xFF; + xaddr[2] = (addr >> 8) & 0xFF; + xaddr[3] = addr & 0xFF; + } + + i2c = &i2c_buses[bus]; + ret = i2c_transfer(i2c->regs, I2C_WRITE, chip << 1, &xaddr[4 - alen], + alen, (void *)buf, len); + + return ret != 0; +} diff --git a/src/soc/samsung/exynos5250/i2c.h b/src/soc/samsung/exynos5250/i2c.h new file mode 100644 index 0000000000..a1d8bc1dcd --- /dev/null +++ b/src/soc/samsung/exynos5250/i2c.h @@ -0,0 +1,41 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2012 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef CPU_SAMSUNG_EXYNOS5250_I2C_H +#define CPU_SAMSUNG_EXYNOS5250_I2C_H + +#include "periph.h" + +struct s3c24x0_i2c { + u32 iiccon; + u32 iicstat; + u32 iicadd; + u32 iicds; + u32 iiclc; +}; + +struct s3c24x0_i2c_bus { + int bus_num; + struct s3c24x0_i2c *regs; + enum periph_id periph_id; +}; + +void i2c_init(unsigned bus, int speed, int slaveadd); + +#endif /* CPU_SAMSUNG_EXYNOS5250_I2C_H */ diff --git a/src/soc/samsung/exynos5250/i2s-regs.h b/src/soc/samsung/exynos5250/i2s-regs.h new file mode 100644 index 0000000000..fabd914180 --- /dev/null +++ b/src/soc/samsung/exynos5250/i2s-regs.h @@ -0,0 +1,142 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2012 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* Taken from the kernel code */ + +#ifndef CPU_SAMSUNG_EXYNOS5250_I2S_REGS_H +#define CPU_SAMSUNG_EXYNOS5250_I2S_REGS_H + +#define I2SCON 0x0 +#define I2SMOD 0x4 +#define I2SFIC 0x8 +#define I2SPSR 0xc +#define I2STXD 0x10 +#define I2SRXD 0x14 +#define I2SFICS 0x18 +#define I2STXDS 0x1c +#define I2SAHB 0x20 +#define I2SSTR0 0x24 +#define I2SSIZE 0x28 +#define I2STRNCNT 0x2c +#define I2SLVL0ADDR 0x30 +#define I2SLVL1ADDR 0x34 +#define I2SLVL2ADDR 0x38 +#define I2SLVL3ADDR 0x3c + +#define CON_RSTCLR (1 << 31) +#define CON_FRXOFSTATUS (1 << 26) +#define CON_FRXORINTEN (1 << 25) +#define CON_FTXSURSTAT (1 << 24) +#define CON_FTXSURINTEN (1 << 23) +#define CON_TXSDMA_PAUSE (1 << 20) +#define CON_TXSDMA_ACTIVE (1 << 18) + +#define CON_FTXURSTATUS (1 << 17) +#define CON_FTXURINTEN (1 << 16) +#define CON_TXFIFO2_EMPTY (1 << 15) +#define CON_TXFIFO1_EMPTY (1 << 14) +#define CON_TXFIFO2_FULL (1 << 13) +#define CON_TXFIFO1_FULL (1 << 12) + +#define CON_LRINDEX (1 << 11) +#define CON_TXFIFO_EMPTY (1 << 10) +#define CON_RXFIFO_EMPTY (1 << 9) +#define CON_TXFIFO_FULL (1 << 8) +#define CON_RXFIFO_FULL (1 << 7) +#define CON_TXDMA_PAUSE (1 << 6) +#define CON_RXDMA_PAUSE (1 << 5) +#define CON_TXCH_PAUSE (1 << 4) +#define CON_RXCH_PAUSE (1 << 3) +#define CON_TXDMA_ACTIVE (1 << 2) +#define CON_RXDMA_ACTIVE (1 << 1) +#define CON_ACTIVE (1 << 0) + +#define MOD_OPCLK_CDCLK_OUT (0 << 30) +#define MOD_OPCLK_CDCLK_IN (1 << 30) +#define MOD_OPCLK_BCLK_OUT (2 << 30) +#define MOD_OPCLK_PCLK (3 << 30) +#define MOD_OPCLK_MASK (3 << 30) +#define MOD_TXS_IDMA (1 << 28) /* Sec_TXFIFO use I-DMA */ + +#define MOD_BLCS_SHIFT 26 +#define MOD_BLCS_16BIT (0 << MOD_BLCS_SHIFT) +#define MOD_BLCS_8BIT (1 << MOD_BLCS_SHIFT) +#define MOD_BLCS_24BIT (2 << MOD_BLCS_SHIFT) +#define MOD_BLCS_MASK (3 << MOD_BLCS_SHIFT) + +#define MOD_BLCP_SHIFT 24 +#define MOD_BLCP_16BIT (0 << MOD_BLCP_SHIFT) +#define MOD_BLCP_8BIT (1 << MOD_BLCP_SHIFT) +#define MOD_BLCP_24BIT (2 << MOD_BLCP_SHIFT) +#define MOD_BLCP_MASK (3 << MOD_BLCP_SHIFT) + +#define MOD_C2DD_HHALF (1 << 21) /* Discard Higher-half */ +#define MOD_C2DD_LHALF (1 << 20) /* Discard Lower-half */ +#define MOD_C1DD_HHALF (1 << 19) +#define MOD_C1DD_LHALF (1 << 18) +#define MOD_DC2_EN (1 << 17) +#define MOD_DC1_EN (1 << 16) +#define MOD_BLC_16BIT (0 << 13) +#define MOD_BLC_8BIT (1 << 13) +#define MOD_BLC_24BIT (2 << 13) +#define MOD_BLC_MASK (3 << 13) + +#define MOD_IMS_SYSMUX (1 << 10) +#define MOD_SLAVE (1 << 11) +#define MOD_TXONLY (0 << 8) +#define MOD_RXONLY (1 << 8) +#define MOD_TXRX (2 << 8) +#define MOD_MASK (3 << 8) +#define MOD_LR_LLOW (0 << 7) +#define MOD_LR_RLOW (1 << 7) +#define MOD_SDF_IIS (0 << 5) +#define MOD_SDF_MSB (1 << 5) +#define MOD_SDF_LSB (2 << 5) +#define MOD_SDF_MASK (3 << 5) +#define MOD_RCLK_256FS (0 << 3) +#define MOD_RCLK_512FS (1 << 3) +#define MOD_RCLK_384FS (2 << 3) +#define MOD_RCLK_768FS (3 << 3) +#define MOD_RCLK_MASK (3 << 3) +#define MOD_BCLK_32FS (0 << 1) +#define MOD_BCLK_48FS (1 << 1) +#define MOD_BCLK_16FS (2 << 1) +#define MOD_BCLK_24FS (3 << 1) +#define MOD_BCLK_MASK (3 << 1) +#define MOD_8BIT (1 << 0) + +#define MOD_CDCLKCON (1 << 12) + +#define PSR_PSREN (1 << 15) + +#define FIC_TXFLUSH (1 << 15) +#define FIC_RXFLUSH (1 << 7) + +#define AHB_INTENLVL0 (1 << 24) +#define AHB_LVL0INT (1 << 20) +#define AHB_CLRLVL0INT (1 << 16) +#define AHB_DMARLD (1 << 5) +#define AHB_INTMASK (1 << 3) +#define AHB_DMAEN (1 << 0) +#define AHB_LVLINTMASK (0xf << 20) + +#define I2SSIZE_TRNMSK (0xffff) +#define I2SSIZE_SHIFT (16) + +#endif /* CPU_SAMSUNG_EXYNOS5250_I2S_REGS_H */ diff --git a/src/soc/samsung/exynos5250/mct.c b/src/soc/samsung/exynos5250/mct.c new file mode 100644 index 0000000000..bbb90e49bb --- /dev/null +++ b/src/soc/samsung/exynos5250/mct.c @@ -0,0 +1,36 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2012 Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <stdint.h> +#include <arch/io.h> +#include "clk.h" + +uint64_t mct_raw_value(void) +{ + uint64_t upper = readl(&exynos_mct->g_cnt_u); + uint64_t lower = readl(&exynos_mct->g_cnt_l); + + return (upper << 32) | lower; +} + +void mct_start(void) +{ + writel(readl(&exynos_mct->g_tcon) | (0x1 << 8), + &exynos_mct->g_tcon); +} diff --git a/src/soc/samsung/exynos5250/monotonic_timer.c b/src/soc/samsung/exynos5250/monotonic_timer.c new file mode 100644 index 0000000000..89ac416eb1 --- /dev/null +++ b/src/soc/samsung/exynos5250/monotonic_timer.c @@ -0,0 +1,34 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2013 Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <stdint.h> +#include <timer.h> + +#include "clk.h" + +static const uint32_t clocks_per_usec = MCT_HZ/1000000; + +void timer_monotonic_get(struct mono_time *mt) +{ + /* We don't have to call mct_start() here + * because it was already called in the bootblock + */ + + mono_time_set_usecs(mt, mct_raw_value() / clocks_per_usec); +} diff --git a/src/soc/samsung/exynos5250/periph.h b/src/soc/samsung/exynos5250/periph.h new file mode 100644 index 0000000000..7d8bf623f6 --- /dev/null +++ b/src/soc/samsung/exynos5250/periph.h @@ -0,0 +1,69 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2012 Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef CPU_SAMSUNG_EXYNOS5250_PERIPH_H +#define CPU_SAMSUNG_EXYNOS5250_PERIPH_H + +/* + * Peripherals requiring clock/pinmux configuration. List will + * grow with support for more devices getting added. + * + * At present the order is arbitrary - we may be able to take advantage + * of some orthogonality later. + */ +enum periph_id { + PERIPH_ID_UART0, + PERIPH_ID_UART1, + PERIPH_ID_UART2, + PERIPH_ID_UART3, + PERIPH_ID_SDMMC0, + PERIPH_ID_SDMMC1, + PERIPH_ID_SDMMC2, + PERIPH_ID_SDMMC3, + + PERIPH_ID_SROMC = 9, + PERIPH_ID_SPI0, + PERIPH_ID_SPI1, + PERIPH_ID_SPI2, + PERIPH_ID_SPI3, + PERIPH_ID_SPI4, + PERIPH_ID_LCD, + PERIPH_ID_BACKLIGHT, + PERIPH_ID_I2C0, + PERIPH_ID_I2C1, + PERIPH_ID_I2C2, + PERIPH_ID_I2C3, + PERIPH_ID_I2C4, + PERIPH_ID_I2C5, + PERIPH_ID_I2C6, + PERIPH_ID_I2C7, + PERIPH_ID_DPHPD, /* eDP hot plug detect */ + PERIPH_ID_PWM0, + PERIPH_ID_PWM1, + PERIPH_ID_PWM2, + PERIPH_ID_PWM3, + PERIPH_ID_PWM4, + PERIPH_ID_I2S1, + PERIPH_ID_SATA, + + PERIPH_ID_COUNT, + PERIPH_ID_NONE = -1, +}; + +#endif diff --git a/src/soc/samsung/exynos5250/pinmux.c b/src/soc/samsung/exynos5250/pinmux.c new file mode 100644 index 0000000000..dc9590f20f --- /dev/null +++ b/src/soc/samsung/exynos5250/pinmux.c @@ -0,0 +1,267 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2012 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <console/console.h> +#include <assert.h> +#include "gpio.h" +#include "pinmux.h" + +static void exynos_pinmux_uart(int start, int count) +{ + int i; + + for (i = start; i < start + count; i++) { + gpio_set_pull(i, GPIO_PULL_NONE); + gpio_cfg_pin(i, GPIO_FUNC(0x2)); + } +} + +void exynos_pinmux_uart0(void) +{ + exynos_pinmux_uart(GPIO_A00, 4); +} + +void exynos_pinmux_uart1(void) +{ + exynos_pinmux_uart(GPIO_D00, 4); +} + +void exynos_pinmux_uart2(void) +{ + exynos_pinmux_uart(GPIO_A10, 4); +} + +void exynos_pinmux_uart3(void) +{ + exynos_pinmux_uart(GPIO_A14, 2); +} + +static void exynos_pinmux_sdmmc(int start, int start_ext) +{ + int i; + + if (start_ext) { + for (i = 0; i <= 3; i++) { + gpio_cfg_pin(start_ext + i, GPIO_FUNC(0x2)); + gpio_set_pull(start_ext + i, GPIO_PULL_UP); + gpio_set_drv(start_ext + i, GPIO_DRV_4X); + } + } + for (i = 0; i < 2; i++) { + gpio_cfg_pin(start + i, GPIO_FUNC(0x2)); + gpio_set_pull(start + i, GPIO_PULL_NONE); + gpio_set_drv(start + i, GPIO_DRV_4X); + } + for (i = 2; i <= 6; i++) { + gpio_cfg_pin(start + i, GPIO_FUNC(0x2)); + gpio_set_pull(start + i, GPIO_PULL_UP); + gpio_set_drv(start + i, GPIO_DRV_4X); + } +} + +void exynos_pinmux_sdmmc0(void) +{ + exynos_pinmux_sdmmc(GPIO_C00, GPIO_C10); +} + +void exynos_pinmux_sdmmc1(void) +{ + exynos_pinmux_sdmmc(GPIO_C20, 0); +} + +void exynos_pinmux_sdmmc2(void) +{ + exynos_pinmux_sdmmc(GPIO_C30, 0); +} + +void exynos_pinmux_sdmmc3(void) +{ + /* + * TODO: Need to add definitions for GPC4 before + * enabling this. + */ + printk(BIOS_DEBUG, "SDMMC3 not supported yet"); +} + +void exynos_pinmux_sromc(int bank, int sixteen_bit) +{ + int i; + + if (bank > 3) { + printk(BIOS_DEBUG, "Unsupported sromc bank %d.\n", bank); + return; + } + + gpio_cfg_pin(GPIO_Y00 + bank, GPIO_FUNC(2)); + gpio_cfg_pin(GPIO_Y04, GPIO_FUNC(2)); + gpio_cfg_pin(GPIO_Y05, GPIO_FUNC(2)); + + for (i = 2; i < 4; i++) + gpio_cfg_pin(GPIO_Y10 + i, GPIO_FUNC(2)); + + for (i = 0; i < 8; i++) { + gpio_cfg_pin(GPIO_Y30 + i, GPIO_FUNC(2)); + gpio_set_pull(GPIO_Y30 + i, GPIO_PULL_UP); + + gpio_cfg_pin(GPIO_Y50 + i, GPIO_FUNC(2)); + gpio_set_pull(GPIO_Y50 + i, GPIO_PULL_UP); + + if (sixteen_bit) { + gpio_cfg_pin(GPIO_Y60 + i, GPIO_FUNC(2)); + gpio_set_pull(GPIO_Y60 + i, GPIO_PULL_UP); + } + } +} + +static void exynos_pinmux_spi(int start, int cfg) +{ + int i; + + for (i = 0; i < 4; i++) { + gpio_cfg_pin(start + i, GPIO_FUNC(cfg)); + gpio_set_pull(start + i, GPIO_PULL_NONE); + gpio_set_drv(start + i, GPIO_DRV_3X); + } +} + +void exynos_pinmux_spi0(void) +{ + exynos_pinmux_spi(GPIO_A20, 0x2); +} + +void exynos_pinmux_spi1(void) +{ + exynos_pinmux_spi(GPIO_A24, 0x2); +} + +void exynos_pinmux_spi2(void) +{ + exynos_pinmux_spi(GPIO_B11, 0x5); +} + +void exynos_pinmux_spi3(void) +{ + exynos_pinmux_spi(GPIO_E00, 0x2); +} + +void exynos_pinmux_spi4(void) +{ + int i; + + for (i = 0; i < 2; i++) + gpio_cfg_pin(GPIO_F02 + i, GPIO_FUNC(0x4)); + for (i = 2; i < 4; i++) + gpio_cfg_pin(GPIO_E02 + i, GPIO_FUNC(0x4)); +} + +void exynos_pinmux_backlight(void) +{ + gpio_cfg_pin(GPIO_B20, GPIO_OUTPUT); + gpio_set_value(GPIO_B20, 1); +} + +void exynos_pinmux_lcd(void) +{ + gpio_cfg_pin(GPIO_Y25, GPIO_OUTPUT); + gpio_set_value(GPIO_Y25, 1); + gpio_cfg_pin(GPIO_X15, GPIO_OUTPUT); + gpio_set_value(GPIO_X15, 1); + gpio_cfg_pin(GPIO_X30, GPIO_OUTPUT); + gpio_set_value(GPIO_X30, 1); +} + +static void exynos_pinmux_i2c(int start, int func) +{ + gpio_cfg_pin(start, GPIO_FUNC(func)); + gpio_cfg_pin(start + 1, GPIO_FUNC(func)); + gpio_set_pull(start, GPIO_PULL_NONE); + gpio_set_pull(start + 1, GPIO_PULL_NONE); +} + +void exynos_pinmux_i2c0(void) +{ + exynos_pinmux_i2c(GPIO_B30, 0x2); +} + +void exynos_pinmux_i2c1(void) +{ + exynos_pinmux_i2c(GPIO_B32, 0x2); +} + +void exynos_pinmux_i2c2(void) +{ + exynos_pinmux_i2c(GPIO_A06, 0x3); +} + +void exynos_pinmux_i2c3(void) +{ + exynos_pinmux_i2c(GPIO_A12, 0x3); +} + +void exynos_pinmux_i2c4(void) +{ + exynos_pinmux_i2c(GPIO_A20, 0x3); +} + +void exynos_pinmux_i2c5(void) +{ + exynos_pinmux_i2c(GPIO_A22, 0x3); +} + +void exynos_pinmux_i2c6(void) +{ + exynos_pinmux_i2c(GPIO_B13, 0x4); +} + +void exynos_pinmux_i2c7(void) +{ + exynos_pinmux_i2c(GPIO_B22, 0x3); +} + +void exynos_pinmux_dphpd(void) +{ + /* Set Hotplug detect for DP */ + gpio_cfg_pin(GPIO_X07, GPIO_FUNC(0x3)); + + /* + * Hotplug detect should have an external pullup; disable the + * internal pulldown so they don't fight. + */ + gpio_set_pull(GPIO_X07, GPIO_PULL_NONE); +} + +void exynos_pinmux_i2s0(void) +{ + int i; + + for (i = 0; i < 5; i++) { + gpio_cfg_pin(GPIO_Z0 + i, GPIO_FUNC(0x02)); + gpio_set_pull(GPIO_Z0 + i, GPIO_PULL_NONE); + } +} + +void exynos_pinmux_i2s1(void) +{ + int i; + + for (i = 0; i < 5; i++) { + gpio_cfg_pin(GPIO_B00 + i, GPIO_FUNC(0x02)); + gpio_set_pull(GPIO_B00 + i, GPIO_PULL_NONE); + } +} diff --git a/src/soc/samsung/exynos5250/pinmux.h b/src/soc/samsung/exynos5250/pinmux.h new file mode 100644 index 0000000000..227a2807e3 --- /dev/null +++ b/src/soc/samsung/exynos5250/pinmux.h @@ -0,0 +1,58 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2012 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef CPU_SAMSUNG_EXYNOS5250_PINMUX_H +#define CPU_SAMSUNG_EXYNOS5250_PINMUX_H + +void exynos_pinmux_uart0(void); +void exynos_pinmux_uart1(void); +void exynos_pinmux_uart2(void); +void exynos_pinmux_uart3(void); + +void exynos_pinmux_sdmmc0(void); +void exynos_pinmux_sdmmc1(void); +void exynos_pinmux_sdmmc2(void); +void exynos_pinmux_sdmmc3(void); + +void exynos_pinmux_sromc(int bank, int sixteen_bit); + +void exynos_pinmux_spi0(void); +void exynos_pinmux_spi1(void); +void exynos_pinmux_spi2(void); +void exynos_pinmux_spi3(void); +void exynos_pinmux_spi4(void); + +void exynos_pinmux_backlight(void); +void exynos_pinmux_lcd(void); + +void exynos_pinmux_i2c0(void); +void exynos_pinmux_i2c1(void); +void exynos_pinmux_i2c2(void); +void exynos_pinmux_i2c3(void); +void exynos_pinmux_i2c4(void); +void exynos_pinmux_i2c5(void); +void exynos_pinmux_i2c6(void); +void exynos_pinmux_i2c7(void); + +void exynos_pinmux_dphpd(void); + +void exynos_pinmux_i2s0(void); +void exynos_pinmux_i2s1(void); + +#endif diff --git a/src/soc/samsung/exynos5250/power.c b/src/soc/samsung/exynos5250/power.c new file mode 100644 index 0000000000..c9d620a958 --- /dev/null +++ b/src/soc/samsung/exynos5250/power.c @@ -0,0 +1,90 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2012 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* Power setup code for EXYNOS5 */ + +#include <arch/io.h> +#include <arch/hlt.h> +#include <console/console.h> +#include "power.h" + +static void ps_hold_setup(void) +{ + /* Set PS-Hold high */ + setbits_le32(&exynos_power->ps_hold_ctrl, + POWER_PS_HOLD_CONTROL_DATA_HIGH); +} + +void power_reset(void) +{ + /* Clear inform1 so there's no change we think we've got a wake reset */ + exynos_power->inform1 = 0; + + setbits_le32(&exynos_power->sw_reset, 1); +} + +/* This function never returns */ +void power_shutdown(void) +{ + clrbits_le32(&exynos_power->ps_hold_ctrl, + POWER_PS_HOLD_CONTROL_DATA_HIGH); + + hlt(); +} + +void power_enable_dp_phy(void) +{ + setbits_le32(&exynos_power->dptx_phy_control, DPTX_PHY_ENABLE); +} + +void power_enable_hw_thermal_trip(void) +{ + /* Enable HW thermal trip */ + setbits_le32(&exynos_power->ps_hold_ctrl, POWER_ENABLE_HW_TRIP); +} + +uint32_t power_read_reset_status(void) +{ + return exynos_power->inform1; +} + +void power_exit_wakeup(void) +{ + typedef void (*resume_func)(void); + + ((resume_func)exynos_power->inform0)(); +} + +int power_init(void) +{ + ps_hold_setup(); + return 0; +} + +void power_enable_xclkout(void) +{ + /* use xxti for xclk out */ + clrsetbits_le32(&exynos_power->pmu_debug, PMU_DEBUG_CLKOUT_SEL_MASK, + PMU_DEBUG_XXTI); +} + +void power_release_uart_retention(void) +{ + writel(1 << 28, &exynos_power->padret_uart_opt); +} diff --git a/src/soc/samsung/exynos5250/power.h b/src/soc/samsung/exynos5250/power.h new file mode 100644 index 0000000000..5ea73c7ef8 --- /dev/null +++ b/src/soc/samsung/exynos5250/power.h @@ -0,0 +1,99 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2012 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* Register map for Exynos5 PMU */ + +#ifndef CPU_SAMSUNG_EXYNOS5250_POWER_H +#define CPU_SAMSUNG_EXYNOS5250_POWER_H + +#include "cpu.h" + +/* Enable HW thermal trip with PS_HOLD_CONTROL register ENABLE_HW_TRIP bit */ +void power_enable_hw_thermal_trip(void); + +#define MIPI_PHY1_CONTROL_ENABLE (1 << 0) +#define MIPI_PHY1_CONTROL_M_RESETN (1 << 2) + +#define POWER_USB_PHY_CTRL_EN (1 << 0) +#define POWER_PS_HOLD_CONTROL_DATA_HIGH (1 << 8) +#define POWER_ENABLE_HW_TRIP (1UL << 31) + +#define DPTX_PHY_ENABLE (1 << 0) + +/* PMU_DEBUG bits [12:8] = 0x1000 selects XXTI clock source */ +#define PMU_DEBUG_XXTI 0x1000 +/* Mask bit[12:8] for xxti clock selection */ +#define PMU_DEBUG_CLKOUT_SEL_MASK 0x1f00 + +/* Power Management Unit register map */ +struct exynos5_power { + /* Add registers as and when required */ + uint32_t om_stat; /* 0x0000 */ + uint8_t reserved1[0x03fc]; + uint32_t sw_reset; /* 0x0400 */ + uint8_t reserved2[0x0300]; + uint32_t usb_drd_phy_ctrl; /* 0x0704 */ + uint32_t usb_host_phy_ctrl; /* 0x0708 */ + uint8_t reserved3[0x8]; + uint32_t mipi_phy1_control; /* 0x0714 */ + uint8_t reserved4[0x8]; + uint32_t dptx_phy_control; /* 0x0720 */ + uint8_t reserved5[0xdc]; + uint32_t inform0; /* 0x0800 */ + uint32_t inform1; /* 0x0804 */ + uint8_t reserved6[0x1f8]; + uint32_t pmu_debug; /* 0x0A00*/ + uint8_t reserved7[0x2728]; + uint32_t padret_uart_opt; /* 0x3128 */ + uint8_t reserved8[0x1e0]; + uint32_t ps_hold_ctrl; /* 0x330c */ +} __attribute__ ((__packed__)); + +static struct exynos5_power * const exynos_power = (void*)EXYNOS5_POWER_BASE; + +/** + * Perform a software reset. + */ +void power_reset(void); + +/** + * Power off the system; it should never return. + */ +void power_shutdown(void); + +/* Enable DPTX PHY */ +void power_enable_dp_phy(void); + +/* Initialize the pmic voltages to power up the system */ +int power_init(void); + +/* Read the reset status. */ +uint32_t power_read_reset_status(void); + +/* Read the resume function and call it. */ +void power_exit_wakeup(void); + +/* pmu debug is used for xclkout, enable xclkout with source as XXTI */ +void power_enable_xclkout(void); + +/* Release UART retention on resume (only for debugging, may conflict with + * kernel). */ +void power_release_uart_retention(void); + +#endif diff --git a/src/soc/samsung/exynos5250/setup.h b/src/soc/samsung/exynos5250/setup.h new file mode 100644 index 0000000000..c65747b775 --- /dev/null +++ b/src/soc/samsung/exynos5250/setup.h @@ -0,0 +1,755 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2012 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* Machine Specific Values for SMDK5250 board based on Exynos5 */ + +#ifndef CPU_SAMSUNG_EXYNOS5250_SETUP_H +#define CPU_SAMSUNG_EXYNOS5250_SETUP_H + +struct exynos5_dmc; +enum ddr_mode; +struct exynos5_phy_control; + +/* TZPC : Register Offsets */ +#define TZPC0_BASE 0x10100000 +#define TZPC1_BASE 0x10110000 +#define TZPC2_BASE 0x10120000 +#define TZPC3_BASE 0x10130000 +#define TZPC4_BASE 0x10140000 +#define TZPC5_BASE 0x10150000 +#define TZPC6_BASE 0x10160000 +#define TZPC7_BASE 0x10170000 +#define TZPC8_BASE 0x10180000 +#define TZPC9_BASE 0x10190000 + +#define APLL_FOUT (1 << 0) + +/* APLL_CON1 */ +#define APLL_CON1_VAL (0x00203800) + +/* MPLL_CON1 */ +#define MPLL_CON1_VAL (0x00203800) + +/* CPLL_CON1 */ +#define CPLL_CON1_VAL (0x00203800) + +/* GPLL_CON1 */ +#define GPLL_CON1_VAL (0x00203800) + +/* EPLL_CON1, CON2 */ +#define EPLL_CON1_VAL 0x00000000 +#define EPLL_CON2_VAL 0x00000080 + +/* VPLL_CON1, CON2 */ +#define VPLL_CON1_VAL 0x00000000 +#define VPLL_CON2_VAL 0x00000080 + +/* BPLL_CON1 */ +#define BPLL_CON1_VAL 0x00203800 + +/* Set PLL */ +#define set_pll(mdiv, pdiv, sdiv) (1<<31 | mdiv<<16 | pdiv<<8 | sdiv) + +/* CLK_SRC_CPU */ +/* 0 = MOUTAPLL, 1 = SCLKMPLL */ +#define MUX_HPM_SEL 0 +#define MUX_CPU_SEL 0 +#define MUX_APLL_SEL 1 + +#define CLK_SRC_CPU_VAL ((MUX_HPM_SEL << 20) \ + | (MUX_CPU_SEL << 16) \ + | (MUX_APLL_SEL)) + +/* MEMCONTROL register bit fields */ +#define DMC_MEMCONTROL_CLK_STOP_DISABLE (0 << 0) +#define DMC_MEMCONTROL_DPWRDN_DISABLE (0 << 1) +#define DMC_MEMCONTROL_DPWRDN_ACTIVE_PRECHARGE (0 << 2) +#define DMC_MEMCONTROL_TP_DISABLE (0 << 4) +#define DMC_MEMCONTROL_DSREF_DISABLE (0 << 5) +#define DMC_MEMCONTROL_DSREF_ENABLE (1 << 5) +#define DMC_MEMCONTROL_ADD_LAT_PALL_CYCLE(x) (x << 6) + +#define DMC_MEMCONTROL_MEM_TYPE_LPDDR3 (7 << 8) +#define DMC_MEMCONTROL_MEM_TYPE_DDR3 (6 << 8) +#define DMC_MEMCONTROL_MEM_TYPE_LPDDR2 (5 << 8) + +#define DMC_MEMCONTROL_MEM_WIDTH_32BIT (2 << 12) + +#define DMC_MEMCONTROL_NUM_CHIP_1 (0 << 16) +#define DMC_MEMCONTROL_NUM_CHIP_2 (1 << 16) + +#define DMC_MEMCONTROL_BL_8 (3 << 20) +#define DMC_MEMCONTROL_BL_4 (2 << 20) + +#define DMC_MEMCONTROL_PZQ_DISABLE (0 << 24) + +#define DMC_MEMCONTROL_MRR_BYTE_7_0 (0 << 25) +#define DMC_MEMCONTROL_MRR_BYTE_15_8 (1 << 25) +#define DMC_MEMCONTROL_MRR_BYTE_23_16 (2 << 25) +#define DMC_MEMCONTROL_MRR_BYTE_31_24 (3 << 25) + +/* MEMCONFIG0 register bit fields */ +#define DMC_MEMCONFIGx_CHIP_MAP_INTERLEAVED (1 << 12) +#define DMC_MEMCONFIGx_CHIP_COL_10 (3 << 8) +#define DMC_MEMCONFIGx_CHIP_ROW_14 (2 << 4) +#define DMC_MEMCONFIGx_CHIP_ROW_15 (3 << 4) +#define DMC_MEMCONFIGx_CHIP_BANK_8 (3 << 0) + +#define DMC_MEMBASECONFIGx_CHIP_BASE(x) (x << 16) +#define DMC_MEMBASECONFIGx_CHIP_MASK(x) (x << 0) +#define DMC_MEMBASECONFIG_VAL(x) ( \ + DMC_MEMBASECONFIGx_CHIP_BASE(x) | \ + DMC_MEMBASECONFIGx_CHIP_MASK(0x780) \ +) + +#define DMC_MEMBASECONFIG0_VAL DMC_MEMBASECONFIG_VAL(0x40) +#define DMC_MEMBASECONFIG1_VAL DMC_MEMBASECONFIG_VAL(0x80) + +#define DMC_PRECHCONFIG_VAL 0xFF000000 +#define DMC_PWRDNCONFIG_VAL 0xFFFF00FF + +#define DMC_CONCONTROL_RESET_VAL 0x0FFF0000 +#define DFI_INIT_START (1 << 28) +#define EMPTY (1 << 8) +#define AREF_EN (1 << 5) + +#define DFI_INIT_COMPLETE_CHO (1 << 2) +#define DFI_INIT_COMPLETE_CH1 (1 << 3) + +#define RDLVL_COMPLETE_CHO (1 << 14) +#define RDLVL_COMPLETE_CH1 (1 << 15) + +#define CLK_STOP_EN (1 << 0) +#define DPWRDN_EN (1 << 1) +#define DSREF_EN (1 << 5) + +/* COJCONTROL register bit fields */ +#define DMC_CONCONTROL_IO_PD_CON_DISABLE (0 << 3) +#define DMC_CONCONTROL_AREF_EN_DISABLE (0 << 5) +#define DMC_CONCONTROL_EMPTY_DISABLE (0 << 8) +#define DMC_CONCONTROL_EMPTY_ENABLE (1 << 8) +#define DMC_CONCONTROL_RD_FETCH_DISABLE (0x0 << 12) +#define DMC_CONCONTROL_TIMEOUT_LEVEL0 (0xFFF << 16) +#define DMC_CONCONTROL_DFI_INIT_START_DISABLE (0 << 28) + +/* CLK_DIV_CPU0_VAL */ +#define CLK_DIV_CPU0_VAL ((ARM2_RATIO << 28) \ + | (APLL_RATIO << 24) \ + | (PCLK_DBG_RATIO << 20) \ + | (ATB_RATIO << 16) \ + | (PERIPH_RATIO << 12) \ + | (ACP_RATIO << 8) \ + | (CPUD_RATIO << 4) \ + | (ARM_RATIO)) + + +/* CLK_FSYS */ +#define CLK_SRC_FSYS0_VAL 0x66666 +#define CLK_DIV_FSYS0_VAL 0x0BB00000 + +/* CLK_DIV_CPU1 */ +#define HPM_RATIO 0x2 +#define COPY_RATIO 0x0 + +/* CLK_DIV_CPU1 = 0x00000003 */ +#define CLK_DIV_CPU1_VAL ((HPM_RATIO << 4) \ + | (COPY_RATIO)) + +/* CLK_SRC_CORE0 */ +#define CLK_SRC_CORE0_VAL 0x00000000 + +/* CLK_SRC_CORE1 */ +#define CLK_SRC_CORE1_VAL 0x100 + +/* CLK_DIV_CORE0 */ +#define CLK_DIV_CORE0_VAL 0x00120000 + +/* CLK_DIV_CORE1 */ +#define CLK_DIV_CORE1_VAL 0x07070700 + +/* CLK_DIV_SYSRGT */ +#define CLK_DIV_SYSRGT_VAL 0x00000111 + +/* CLK_DIV_ACP */ +#define CLK_DIV_ACP_VAL 0x12 + +/* CLK_DIV_SYSLFT */ +#define CLK_DIV_SYSLFT_VAL 0x00000311 + +/* CLK_SRC_CDREX */ +#define CLK_SRC_CDREX_VAL 0x1 + +/* CLK_DIV_CDREX */ +#define MCLK_CDREX2_RATIO 0x0 +#define ACLK_EFCON_RATIO 0x1 +#define MCLK_DPHY_RATIO 0x1 +#define MCLK_CDREX_RATIO 0x1 +#define ACLK_C2C_200_RATIO 0x1 +#define C2C_CLK_400_RATIO 0x1 +#define PCLK_CDREX_RATIO 0x1 +#define ACLK_CDREX_RATIO 0x1 + +#define CLK_DIV_CDREX_VAL ((MCLK_DPHY_RATIO << 24) \ + | (C2C_CLK_400_RATIO << 6) \ + | (PCLK_CDREX_RATIO << 4) \ + | (ACLK_CDREX_RATIO)) + +/* CLK_SRC_TOP0 */ +#define MUX_ACLK_300_GSCL_SEL 0x0 +#define MUX_ACLK_300_GSCL_MID_SEL 0x0 +#define MUX_ACLK_400_G3D_MID_SEL 0x0 +#define MUX_ACLK_333_SEL 0x0 +#define MUX_ACLK_300_DISP1_SEL 0x0 +#define MUX_ACLK_300_DISP1_MID_SEL 0x0 +#define MUX_ACLK_200_SEL 0x0 +#define MUX_ACLK_166_SEL 0x0 +#define CLK_SRC_TOP0_VAL ((MUX_ACLK_300_GSCL_SEL << 25) \ + | (MUX_ACLK_300_GSCL_MID_SEL << 24) \ + | (MUX_ACLK_400_G3D_MID_SEL << 20) \ + | (MUX_ACLK_333_SEL << 16) \ + | (MUX_ACLK_300_DISP1_SEL << 15) \ + | (MUX_ACLK_300_DISP1_MID_SEL << 14) \ + | (MUX_ACLK_200_SEL << 12) \ + | (MUX_ACLK_166_SEL << 8)) + +/* CLK_SRC_TOP1 */ +#define MUX_ACLK_400_G3D_SEL 0x1 +#define MUX_ACLK_400_ISP_SEL 0x0 +#define MUX_ACLK_400_IOP_SEL 0x0 +#define MUX_ACLK_MIPI_HSI_TXBASE_SEL 0x0 +#define MUX_ACLK_300_GSCL_MID1_SEL 0x0 +#define MUX_ACLK_300_DISP1_MID1_SEL 0x0 +#define CLK_SRC_TOP1_VAL ((MUX_ACLK_400_G3D_SEL << 28) \ + |(MUX_ACLK_400_ISP_SEL << 24) \ + |(MUX_ACLK_400_IOP_SEL << 20) \ + |(MUX_ACLK_MIPI_HSI_TXBASE_SEL << 16) \ + |(MUX_ACLK_300_GSCL_MID1_SEL << 12) \ + |(MUX_ACLK_300_DISP1_MID1_SEL << 8)) + +/* CLK_SRC_TOP2 */ +#define MUX_GPLL_SEL 0x1 +#define MUX_BPLL_USER_SEL 0x0 +#define MUX_MPLL_USER_SEL 0x0 +#define MUX_VPLL_SEL 0x1 +#define MUX_EPLL_SEL 0x1 +#define MUX_CPLL_SEL 0x1 +#define VPLLSRC_SEL 0x0 +#define CLK_SRC_TOP2_VAL ((MUX_GPLL_SEL << 28) \ + | (MUX_BPLL_USER_SEL << 24) \ + | (MUX_MPLL_USER_SEL << 20) \ + | (MUX_VPLL_SEL << 16) \ + | (MUX_EPLL_SEL << 12) \ + | (MUX_CPLL_SEL << 8) \ + | (VPLLSRC_SEL)) +/* CLK_SRC_TOP3 */ +#define MUX_ACLK_333_SUB_SEL 0x1 +#define MUX_ACLK_400_SUB_SEL 0x1 +#define MUX_ACLK_266_ISP_SUB_SEL 0x1 +#define MUX_ACLK_266_GPS_SUB_SEL 0x0 +#define MUX_ACLK_300_GSCL_SUB_SEL 0x1 +#define MUX_ACLK_266_GSCL_SUB_SEL 0x1 +#define MUX_ACLK_300_DISP1_SUB_SEL 0x1 +#define MUX_ACLK_200_DISP1_SUB_SEL 0x1 +#define CLK_SRC_TOP3_VAL ((MUX_ACLK_333_SUB_SEL << 24) \ + | (MUX_ACLK_400_SUB_SEL << 20) \ + | (MUX_ACLK_266_ISP_SUB_SEL << 16) \ + | (MUX_ACLK_266_GPS_SUB_SEL << 12) \ + | (MUX_ACLK_300_GSCL_SUB_SEL << 10) \ + | (MUX_ACLK_266_GSCL_SUB_SEL << 8) \ + | (MUX_ACLK_300_DISP1_SUB_SEL << 6) \ + | (MUX_ACLK_200_DISP1_SUB_SEL << 4)) + +/* CLK_DIV_TOP0 */ +#define ACLK_300_DISP1_RATIO 0x2 +#define ACLK_400_G3D_RATIO 0x0 +#define ACLK_333_RATIO 0x0 +#define ACLK_266_RATIO 0x2 +#define ACLK_200_RATIO 0x3 +#define ACLK_166_RATIO 0x1 +#define ACLK_133_RATIO 0x1 +#define ACLK_66_RATIO 0x5 + +#define CLK_DIV_TOP0_VAL ((ACLK_300_DISP1_RATIO << 28) \ + | (ACLK_400_G3D_RATIO << 24) \ + | (ACLK_333_RATIO << 20) \ + | (ACLK_266_RATIO << 16) \ + | (ACLK_200_RATIO << 12) \ + | (ACLK_166_RATIO << 8) \ + | (ACLK_133_RATIO << 4) \ + | (ACLK_66_RATIO)) + +/* CLK_DIV_TOP1 */ +#define ACLK_MIPI_HSI_TX_BASE_RATIO 0x3 +#define ACLK_66_PRE_RATIO 0x1 +#define ACLK_400_ISP_RATIO 0x1 +#define ACLK_400_IOP_RATIO 0x1 +#define ACLK_300_GSCL_RATIO 0x2 + +#define CLK_DIV_TOP1_VAL ((ACLK_MIPI_HSI_TX_BASE_RATIO << 28) \ + | (ACLK_66_PRE_RATIO << 24) \ + | (ACLK_400_ISP_RATIO << 20) \ + | (ACLK_400_IOP_RATIO << 16) \ + | (ACLK_300_GSCL_RATIO << 12)) + +/* APLL_LOCK */ +#define APLL_LOCK_VAL (0x546) +/* MPLL_LOCK */ +#define MPLL_LOCK_VAL (0x546) +/* CPLL_LOCK */ +#define CPLL_LOCK_VAL (0x546) +/* GPLL_LOCK */ +#define GPLL_LOCK_VAL (0x546) +/* EPLL_LOCK */ +#define EPLL_LOCK_VAL (0x3A98) +/* VPLL_LOCK */ +#define VPLL_LOCK_VAL (0x3A98) +/* BPLL_LOCK */ +#define BPLL_LOCK_VAL (0x546) + +#define MUX_MCLK_CDREX_SEL (1 << 4) +#define MUX_MCLK_DPHY_SEL (1 << 8) + +#define MUX_APLL_SEL_MASK (1 << 0) +#define MUX_MPLL_FOUT_SEL (1 << 4) +#define MUX_BPLL_FOUT_SEL (1 << 0) +#define MUX_MPLL_SEL_MASK (1 << 8) +#define MPLL_SEL_MOUT_MPLLFOUT (2 << 8) +#define MUX_CPLL_SEL_MASK (1 << 8) +#define MUX_EPLL_SEL_MASK (1 << 12) +#define MUX_VPLL_SEL_MASK (1 << 16) +#define MUX_GPLL_SEL_MASK (1 << 28) +#define MUX_BPLL_SEL_MASK (1 << 0) +#define MUX_HPM_SEL_MASK (1 << 20) +#define HPM_SEL_SCLK_MPLL (1 << 21) +#define APLL_CON0_LOCKED (1 << 29) +#define MPLL_CON0_LOCKED (1 << 29) +#define BPLL_CON0_LOCKED (1 << 29) +#define CPLL_CON0_LOCKED (1 << 29) +#define EPLL_CON0_LOCKED (1 << 29) +#define GPLL_CON0_LOCKED (1 << 29) +#define VPLL_CON0_LOCKED (1 << 29) +#define CLK_REG_DISABLE 0x0 +#define TOP2_VAL 0x0110000 + +/* CLK_SRC_PERIC0 */ +#define PWM_SEL 6 +#define UART3_SEL 6 +#define UART2_SEL 6 +#define UART1_SEL 6 +#define UART0_SEL 6 +/* SRC_CLOCK = SCLK_MPLL */ +#define CLK_SRC_PERIC0_VAL ((PWM_SEL << 24) \ + | (UART3_SEL << 12) \ + | (UART2_SEL << 8) \ + | (UART1_SEL << 4) \ + | (UART0_SEL)) + +/* CLK_SRC_PERIC1 */ +/* SRC_CLOCK = SCLK_MPLL */ +#define SPI0_SEL 6 +#define SPI1_SEL 6 +#define SPI2_SEL 6 +#define CLK_SRC_PERIC1_VAL ((SPI2_SEL << 24) \ + | (SPI1_SEL << 20) \ + | (SPI0_SEL << 16)) + +/* SCLK_SRC_ISP - set SPI0/1 to 6 = SCLK_MPLL_USER */ +#define SPI0_ISP_SEL 6 +#define SPI1_ISP_SEL 6 +#define SCLK_SRC_ISP_VAL (SPI1_ISP_SEL << 4) \ + | (SPI0_ISP_SEL << 0) + +/* SCLK_DIV_ISP - set SPI0/1 to 0xf = divide by 16 */ +#define SPI0_ISP_RATIO 0xf +#define SPI1_ISP_RATIO 0xf +#define SCLK_DIV_ISP_VAL (SPI1_ISP_RATIO << 12) \ + | (SPI0_ISP_RATIO << 0) + +/* CLK_DIV_PERIL0 */ +#define UART5_RATIO 7 +#define UART4_RATIO 7 +#define UART3_RATIO 7 +#define UART2_RATIO 7 +#define UART1_RATIO 7 +#define UART0_RATIO 7 + +#define CLK_DIV_PERIC0_VAL ((UART3_RATIO << 12) \ + | (UART2_RATIO << 8) \ + | (UART1_RATIO << 4) \ + | (UART0_RATIO)) +/* CLK_DIV_PERIC1 */ +#define SPI1_RATIO 0x7 +#define SPI0_RATIO 0xf +#define SPI1_SUB_RATIO 0x0 +#define SPI0_SUB_RATIO 0x0 +#define CLK_DIV_PERIC1_VAL ((SPI1_SUB_RATIO << 24) \ + | ((SPI1_RATIO << 16) \ + | (SPI0_SUB_RATIO << 8) \ + | (SPI0_RATIO << 0))) + +/* CLK_DIV_PERIC2 */ +#define SPI2_RATIO 0xf +#define SPI2_SUB_RATIO 0x0 +#define CLK_DIV_PERIC2_VAL ((SPI2_SUB_RATIO << 8) \ + | (SPI2_RATIO << 0)) +/* CLK_DIV_FSYS2 */ +#define MMC2_RATIO_MASK 0xf +#define MMC2_RATIO_VAL 0x3 +#define MMC2_RATIO_OFFSET 0 + +#define MMC2_PRE_RATIO_MASK 0xff +#define MMC2_PRE_RATIO_VAL 0x9 +#define MMC2_PRE_RATIO_OFFSET 8 + +#define MMC3_RATIO_MASK 0xf +#define MMC3_RATIO_VAL 0x1 +#define MMC3_RATIO_OFFSET 16 + +#define MMC3_PRE_RATIO_MASK 0xff +#define MMC3_PRE_RATIO_VAL 0x0 +#define MMC3_PRE_RATIO_OFFSET 24 + +/* CLK_SRC_LEX */ +#define CLK_SRC_LEX_VAL 0x0 + +/* CLK_DIV_LEX */ +#define CLK_DIV_LEX_VAL 0x10 + +/* CLK_DIV_R0X */ +#define CLK_DIV_R0X_VAL 0x10 + +/* CLK_DIV_L0X */ +#define CLK_DIV_R1X_VAL 0x10 + +/* CLK_DIV_ISP0 */ +#define CLK_DIV_ISP0_VAL 0x31 + +/* CLK_DIV_ISP1 */ +#define CLK_DIV_ISP1_VAL 0x0 + +/* CLK_DIV_ISP2 */ +#define CLK_DIV_ISP2_VAL 0x1 + +/* CLK_SRC_DISP1_0 */ +#define CLK_SRC_DISP1_0_VAL 0x6 + +/* + * DIV_DISP1_0 + * For DP, divisor should be 2 + */ +#define CLK_DIV_DISP1_0_FIMD1 (2 << 0) + +/* CLK_GATE_IP_DISP1 */ +#define CLK_GATE_DP1_ALLOW (1 << 4) + +/* CLK_GATE_IP_SYSRGT */ +#define CLK_C2C_MASK (1 << 1) + +/* CLK_GATE_IP_ACP */ +#define CLK_SMMUG2D_MASK (1 << 7) +#define CLK_SMMUSSS_MASK (1 << 6) +#define CLK_SMMUMDMA_MASK (1 << 5) +#define CLK_ID_REMAPPER_MASK (1 << 4) +#define CLK_G2D_MASK (1 << 3) +#define CLK_SSS_MASK (1 << 2) +#define CLK_MDMA_MASK (1 << 1) +#define CLK_SECJTAG_MASK (1 << 0) + +/* CLK_GATE_BUS_SYSLFT */ +#define CLK_EFCLK_MASK (1 << 16) + +/* CLK_GATE_IP_ISP0 */ +#define CLK_UART_ISP_MASK (1 << 31) +#define CLK_WDT_ISP_MASK (1 << 30) +#define CLK_PWM_ISP_MASK (1 << 28) +#define CLK_MTCADC_ISP_MASK (1 << 27) +#define CLK_I2C1_ISP_MASK (1 << 26) +#define CLK_I2C0_ISP_MASK (1 << 25) +#define CLK_MPWM_ISP_MASK (1 << 24) +#define CLK_MCUCTL_ISP_MASK (1 << 23) +#define CLK_INT_COMB_ISP_MASK (1 << 22) +#define CLK_SMMU_MCUISP_MASK (1 << 13) +#define CLK_SMMU_SCALERP_MASK (1 << 12) +#define CLK_SMMU_SCALERC_MASK (1 << 11) +#define CLK_SMMU_FD_MASK (1 << 10) +#define CLK_SMMU_DRC_MASK (1 << 9) +#define CLK_SMMU_ISP_MASK (1 << 8) +#define CLK_GICISP_MASK (1 << 7) +#define CLK_ARM9S_MASK (1 << 6) +#define CLK_MCUISP_MASK (1 << 5) +#define CLK_SCALERP_MASK (1 << 4) +#define CLK_SCALERC_MASK (1 << 3) +#define CLK_FD_MASK (1 << 2) +#define CLK_DRC_MASK (1 << 1) +#define CLK_ISP_MASK (1 << 0) + +/* CLK_GATE_IP_ISP1 */ +#define CLK_SPI1_ISP_MASK (1 << 13) +#define CLK_SPI0_ISP_MASK (1 << 12) +#define CLK_SMMU3DNR_MASK (1 << 7) +#define CLK_SMMUDIS1_MASK (1 << 6) +#define CLK_SMMUDIS0_MASK (1 << 5) +#define CLK_SMMUODC_MASK (1 << 4) +#define CLK_3DNR_MASK (1 << 2) +#define CLK_DIS_MASK (1 << 1) +#define CLK_ODC_MASK (1 << 0) + +/* CLK_GATE_IP_GSCL */ +#define CLK_SMMUFIMC_LITE2_MASK (1 << 20) +#define CLK_SMMUFIMC_LITE1_MASK (1 << 12) +#define CLK_SMMUFIMC_LITE0_MASK (1 << 11) +#define CLK_SMMUGSCL3_MASK (1 << 10) +#define CLK_SMMUGSCL2_MASK (1 << 9) +#define CLK_SMMUGSCL1_MASK (1 << 8) +#define CLK_SMMUGSCL0_MASK (1 << 7) +#define CLK_GSCL_WRAP_B_MASK (1 << 6) +#define CLK_GSCL_WRAP_A_MASK (1 << 5) +#define CLK_CAMIF_TOP_MASK (1 << 4) +#define CLK_GSCL3_MASK (1 << 3) +#define CLK_GSCL2_MASK (1 << 2) +#define CLK_GSCL1_MASK (1 << 1) +#define CLK_GSCL0_MASK (1 << 0) + +/* CLK_GATE_IP_MFC */ +#define CLK_SMMUMFCR_MASK (1 << 2) +#define CLK_SMMUMFCL_MASK (1 << 1) +#define CLK_MFC_MASK (1 << 0) + +#define SCLK_MPWM_ISP_MASK (1 << 0) + +/* CLK_GATE_IP_DISP1 */ +#define CLK_SMMUTVX_MASK (1 << 9) +#define CLK_ASYNCTVX_MASK (1 << 7) +#define CLK_HDMI_MASK (1 << 6) +#define CLK_MIXER_MASK (1 << 5) +#define CLK_DSIM1_MASK (1 << 3) + +/* CLK_GATE_IP_GEN */ +#define CLK_SMMUMDMA1_MASK (1 << 9) +#define CLK_SMMUJPEG_MASK (1 << 7) +#define CLK_SMMUROTATOR_MASK (1 << 6) +#define CLK_MDMA1_MASK (1 << 4) +#define CLK_JPEG_MASK (1 << 2) +#define CLK_ROTATOR_MASK (1 << 1) + +/* CLK_GATE_IP_FSYS */ +#define CLK_WDT_IOP_MASK (1 << 30) +#define CLK_SMMUMCU_IOP_MASK (1 << 26) +#define CLK_SATA_PHY_I2C_MASK (1 << 25) +#define CLK_SATA_PHY_CTRL_MASK (1 << 24) +#define CLK_MCUCTL_MASK (1 << 23) +#define CLK_NFCON_MASK (1 << 22) +#define CLK_SMMURTIC_MASK (1 << 11) +#define CLK_RTIC_MASK (1 << 9) +#define CLK_MIPI_HSI_MASK (1 << 8) +#define CLK_USBOTG_MASK (1 << 7) +#define CLK_SATA_MASK (1 << 6) +#define CLK_PDMA1_MASK (1 << 2) +#define CLK_PDMA0_MASK (1 << 1) +#define CLK_MCU_IOP_MASK (1 << 0) + +/* CLK_GATE_IP_PERIC */ +#define CLK_HS_I2C3_MASK (1 << 31) +#define CLK_HS_I2C2_MASK (1 << 30) +#define CLK_HS_I2C1_MASK (1 << 29) +#define CLK_HS_I2C0_MASK (1 << 28) +#define CLK_AC97_MASK (1 << 27) +#define CLK_SPDIF_MASK (1 << 26) +#define CLK_PCM2_MASK (1 << 23) +#define CLK_PCM1_MASK (1 << 22) +#define CLK_I2S2_MASK (1 << 21) +#define CLK_I2S1_MASK (1 << 20) +#define CLK_SPI2_MASK (1 << 18) +#define CLK_SPI0_MASK (1 << 16) +#define CLK_I2CHDMI_MASK (1 << 14) +#define CLK_I2C7_MASK (1 << 13) +#define CLK_I2C6_MASK (1 << 12) +#define CLK_I2C5_MASK (1 << 11) +#define CLK_I2C4_MASK (1 << 10) +#define CLK_I2C3_MASK (1 << 9) +#define CLK_I2C2_MASK (1 << 8) +#define CLK_I2C1_MASK (1 << 7) +#define CLK_I2C0_MASK (1 << 6) + +/* CLK_GATE_IP_PERIS */ +#define CLK_RTC_MASK (1 << 20) +#define CLK_TZPC9_MASK (1 << 15) +#define CLK_TZPC8_MASK (1 << 14) +#define CLK_TZPC7_MASK (1 << 13) +#define CLK_TZPC6_MASK (1 << 12) +#define CLK_TZPC5_MASK (1 << 11) +#define CLK_TZPC4_MASK (1 << 10) +#define CLK_TZPC3_MASK (1 << 9) +#define CLK_TZPC2_MASK (1 << 8) +#define CLK_TZPC1_MASK (1 << 7) +#define CLK_TZPC0_MASK (1 << 6) +#define CLK_CHIPID_MASK (1 << 0) + +/* CLK_GATE_BLOCK */ +#define CLK_ACP_MASK (1 << 7) + +/* CLK_GATE_IP_CDREX */ +#define CLK_TZASC_DRBXW_MASK (1 << 23) +#define CLK_TZASC_DRBXR_MASK (1 << 22) +#define CLK_TZASC_XLBXW_MASK (1 << 21) +#define CLK_TZASC_XLBXR_MASK (1 << 20) +#define CLK_TZASC_XR1BXW_MASK (1 << 19) +#define CLK_TZASC_XR1BXR_MASK (1 << 18) +#define CLK_DPHY1_MASK (1 << 5) +#define CLK_DPHY0_MASK (1 << 4) + +/* + * TZPC Register Value : + * R0SIZE: 0x0 : Size of secured ram + */ +#define R0SIZE 0x0 + +/* + * TZPC Decode Protection Register Value : + * DECPROTXSET: 0xFF : Set Decode region to non-secure + */ +#define DECPROTXSET 0xFF + +#define LPDDR3PHY_CTRL_PHY_RESET_DISABLE (1 << 0) +#define LPDDR3PHY_CTRL_PHY_RESET_ENABLE (0 << 0 ) + +#define PHY_CON0_RESET_VAL 0x17020a40 +#define P0_CMD_EN (1 << 14) +#define BYTE_RDLVL_EN (1 << 13) +#define CTRL_SHGATE (1 << 8) + +#define PHY_CON1_RESET_VAL 0x09210100 +#define CTRL_GATEDURADJ_MASK (0xf << 20) + +#define PHY_CON2_RESET_VAL 0x00010004 +#define INIT_DESKEW_EN (1 << 6) +#define RDLVL_GATE_EN (1 << 24) + +/*ZQ Configurations */ +#define PHY_CON16_RESET_VAL 0x08000304 + +#define ZQ_CLK_DIV_EN (1 << 18) +#define ZQ_MANUAL_STR (1 << 1) +#define ZQ_DONE (1 << 0) + +#define CTRL_RDLVL_GATE_ENABLE 1 +#define CTRL_RDLVL_GATE_DISABLE 1 + +/* Direct Command */ +#define DIRECT_CMD_NOP 0x07000000 +#define DIRECT_CMD_PALL 0x01000000 +#define DIRECT_CMD_ZQINIT 0x0a000000 +#define DIRECT_CMD_CHANNEL_SHIFT 28 +#define DIRECT_CMD_CHIP_SHIFT 20 + +/* DMC PHY Control0 register */ +#define PHY_CONTROL0_RESET_VAL 0x0 +#define MEM_TERM_EN (1 << 31) /* Termination enable for memory */ +#define PHY_TERM_EN (1 << 30) /* Termination enable for PHY */ +#define DMC_CTRL_SHGATE (1 << 29) /* Duration of DQS gating signal */ +#define FP_RSYNC (1 << 3) /* Force DLL resynchronization */ + +/* Driver strength for CK, CKE, CS & CA */ +#define IMP_OUTPUT_DRV_40_OHM 0x5 +#define IMP_OUTPUT_DRV_30_OHM 0x7 +#define CA_CK_DRVR_DS_OFFSET 9 +#define CA_CKE_DRVR_DS_OFFSET 6 +#define CA_CS_DRVR_DS_OFFSET 3 +#define CA_ADR_DRVR_DS_OFFSET 0 + +#define PHY_CON42_CTRL_BSTLEN_SHIFT 8 +#define PHY_CON42_CTRL_RDLAT_SHIFT 0 + +struct mem_timings; + +/* Errors that we can encounter in low-level setup */ +enum { + SETUP_ERR_OK, + SETUP_ERR_RDLV_COMPLETE_TIMEOUT = -1, + SETUP_ERR_ZQ_CALIBRATION_FAILURE = -2, +}; + +/* Functions common between LPDDR2 and DDR3 */ + +/* CPU info initialization code */ +void cpu_info_init(void); + +void mem_ctrl_init(void); +/* + * Memory variant specific initialization code + * + * @param mem Memory timings for this memory type. + * @param mem_iv_size Memory interleaving size is a configurable parameter + * which the DMC uses to decide how to split a memory + * chunk into smaller chunks to support concurrent + * accesses; may vary across boards. + * @param mem_reset Reset memory during initialization. + * @return 0 if ok, SETUP_ERR_... if there is a problem + */ +int ddr3_mem_ctrl_init(struct mem_timings *mem, unsigned long mem_iv_size, + int mem_reset); + +/* + * Configure ZQ I/O interface + * + * @param mem Memory timings for this memory type. + * @param phy0_ctrl Pointer to struct containing PHY0 control reg + * @param phy1_ctrl Pointer to struct containing PHY1 control reg + * @return 0 if ok, -1 on error + */ +int dmc_config_zq(struct mem_timings *mem, + struct exynos5_phy_control *phy0_ctrl, + struct exynos5_phy_control *phy1_ctrl); + +/* + * Send NOP and MRS/EMRS Direct commands + * + * @param mem Memory timings for this memory type. + * @param dmc Pointer to struct of DMC registers + */ +void dmc_config_mrs(struct mem_timings *mem, struct exynos5_dmc *dmc); + +/* + * Send PALL Direct commands + * + * @param mem Memory timings for this memory type. + * @param dmc Pointer to struct of DMC registers + */ +void dmc_config_prech(struct mem_timings *mem, struct exynos5_dmc *dmc); + +/* + * Configure the memconfig and membaseconfig registers + * + * @param mem Memory timings for this memory type. + * @param exynos5_dmc Pointer to struct of DMC registers + */ +void dmc_config_memory(struct mem_timings *mem, struct exynos5_dmc *dmc); + +/* Set the PS-Hold drive value */ +void ps_hold_setup(void); +/* + * Reset the DLL. This function is common between DDR3 and LPDDR2. + * However, the reset value is different. So we are passing a flag + * ddr_mode to distinguish between LPDDR2 and DDR3. + * + * @param exynos5_dmc Pointer to struct of DMC registers + * @param ddr_mode Type of DDR memory + */ +void update_reset_dll(struct exynos5_dmc *, enum ddr_mode); +#endif diff --git a/src/soc/samsung/exynos5250/spi.c b/src/soc/samsung/exynos5250/spi.c new file mode 100644 index 0000000000..33ec6984db --- /dev/null +++ b/src/soc/samsung/exynos5250/spi.c @@ -0,0 +1,213 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2011 Samsung Electronics + * Copyright 2013 Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <assert.h> +#include <stdlib.h> +#include <arch/io.h> +#include <console/console.h> +#include "clk.h" +#include "gpio.h" +#include "spi.h" + +#if defined(CONFIG_DEBUG_SPI) && CONFIG_DEBUG_SPI +# define DEBUG_SPI(x,...) printk(BIOS_DEBUG, "EXYNOS_SPI: " x) +#else +# define DEBUG_SPI(x,...) +#endif + +static void exynos_spi_rx_tx(struct exynos_spi *regs, int todo, + void *dinp, void const *doutp, int i) +{ + int rx_lvl, tx_lvl; + unsigned int *rxp = (unsigned int *)(dinp + (i * (32 * 1024))); + unsigned int out_bytes, in_bytes; + + // TODO In current implementation, every read/write must be aligned to + // 4 bytes, otherwise you may get timeout or other unexpected results. + ASSERT(todo % 4 == 0); + + out_bytes = in_bytes = todo; + setbits_le32(®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) { + // TODO The "writing" (tx) is not supported now; that's + // why we write garbage to keep driving FIFO clock. + 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; + } + } +} + +/* set up SPI channel */ +int exynos_spi_open(struct exynos_spi *regs) +{ + /* set the spi1 GPIO */ + + /* set pktcnt and enable it */ + writel(4 | SPI_PACKET_CNT_EN, ®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 previously */ + 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); + return 0; +} + +int exynos_spi_read(struct exynos_spi *regs, void *dest, u32 len, u32 off) +{ + int upto, todo; + int i; + 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) | off, ®s->tx_data); + + /* waiting for TX done */ + while (!(readl(®s->spi_sts) & SPI_ST_TX_DONE)); + + for (upto = 0, i = 0; upto < len; upto += todo, i++) { + todo = MIN(len - upto, (1 << 15)); + exynos_spi_rx_tx(regs, todo, dest, (void *)(off), i); + } + + setbits_le32(®s->cs_reg, SPI_SLAVE_SIG_INACT);/* make the CS high */ + + return len; +} + +int exynos_spi_close(struct exynos_spi *regs) +{ + /* + * 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); + return 0; +} + +// SPI as CBFS media. +struct exynos_spi_media { + struct exynos_spi *regs; + struct cbfs_simple_buffer buffer; +}; + +static int exynos_spi_cbfs_open(struct cbfs_media *media) { + struct exynos_spi_media *spi = (struct exynos_spi_media*)media->context; + DEBUG_SPI("exynos_spi_cbfs_open\n"); + return exynos_spi_open(spi->regs); +} + +static int exynos_spi_cbfs_close(struct cbfs_media *media) { + struct exynos_spi_media *spi = (struct exynos_spi_media*)media->context; + DEBUG_SPI("exynos_spi_cbfs_close\n"); + return exynos_spi_close(spi->regs); +} + +static size_t exynos_spi_cbfs_read(struct cbfs_media *media, void *dest, + size_t offset, size_t count) { + struct exynos_spi_media *spi = (struct exynos_spi_media*)media->context; + int bytes; + DEBUG_SPI("exynos_spi_cbfs_read(%u)\n", count); + bytes = exynos_spi_read(spi->regs, dest, count, offset); + // Flush and re-open the device. + exynos_spi_close(spi->regs); + exynos_spi_open(spi->regs); + return bytes; +} + +static void *exynos_spi_cbfs_map(struct cbfs_media *media, size_t offset, + size_t count) { + struct exynos_spi_media *spi = (struct exynos_spi_media*)media->context; + DEBUG_SPI("exynos_spi_cbfs_map\n"); + // See exynos_spi_rx_tx for I/O alignment limitation. + if (count % 4) + count += 4 - (count % 4); + return cbfs_simple_buffer_map(&spi->buffer, media, offset, count); +} + +static void *exynos_spi_cbfs_unmap(struct cbfs_media *media, + const void *address) { + struct exynos_spi_media *spi = (struct exynos_spi_media*)media->context; + DEBUG_SPI("exynos_spi_cbfs_unmap\n"); + return cbfs_simple_buffer_unmap(&spi->buffer, address); +} + +int initialize_exynos_spi_cbfs_media(struct cbfs_media *media, + void *buffer_address, + size_t buffer_size) { + // TODO Replace static variable to support multiple streams. + static struct exynos_spi_media context; + DEBUG_SPI("initialize_exynos_spi_cbfs_media\n"); + + context.regs = (void*)EXYNOS5_SPI1_BASE; + context.buffer.allocated = context.buffer.last_allocate = 0; + context.buffer.buffer = buffer_address; + context.buffer.size = buffer_size; + media->context = (void*)&context; + media->open = exynos_spi_cbfs_open; + media->close = exynos_spi_cbfs_close; + media->read = exynos_spi_cbfs_read; + media->map = exynos_spi_cbfs_map; + media->unmap = exynos_spi_cbfs_unmap; + + return 0; +} diff --git a/src/soc/samsung/exynos5250/spi.h b/src/soc/samsung/exynos5250/spi.h new file mode 100644 index 0000000000..7ca311444c --- /dev/null +++ b/src/soc/samsung/exynos5250/spi.h @@ -0,0 +1,98 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2012 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef CPU_SAMSUNG_EXYNOS5250_SPI_H +#define CPU_SAMSUNG_EXYNOS5250_SPI_H + +/* This driver serves as a CBFS media source. */ +#include <cbfs.h> + +/* SPI peripheral register map; padded to 64KB */ +struct exynos_spi { + unsigned int ch_cfg; /* 0x00 */ + unsigned char reserved0[4]; + unsigned int mode_cfg; /* 0x08 */ + unsigned int cs_reg; /* 0x0c */ + unsigned char reserved1[4]; + unsigned int spi_sts; /* 0x14 */ + unsigned int tx_data; /* 0x18 */ + unsigned int rx_data; /* 0x1c */ + unsigned int pkt_cnt; /* 0x20 */ + unsigned char reserved2[4]; + unsigned int swap_cfg; /* 0x28 */ + unsigned int fb_clk; /* 0x2c */ + unsigned char padding[0xffd0]; +}; + +#define EXYNOS_SPI_MAX_FREQ 50000000 + +#define SPI_TIMEOUT_MS 10 + +#define SF_READ_DATA_CMD 0x3 + +/* SPI_CHCFG */ +#define SPI_CH_HS_EN (1 << 6) +#define SPI_CH_RST (1 << 5) +#define SPI_SLAVE_MODE (1 << 4) +#define SPI_CH_CPOL_L (1 << 3) +#define SPI_CH_CPHA_B (1 << 2) +#define SPI_RX_CH_ON (1 << 1) +#define SPI_TX_CH_ON (1 << 0) + +/* SPI_MODECFG */ +#define SPI_MODE_CH_WIDTH_WORD (0x2 << 29) +#define SPI_MODE_BUS_WIDTH_WORD (0x2 << 17) + +/* SPI_CSREG */ +#define SPI_SLAVE_SIG_INACT (1 << 0) + +/* SPI_STS */ +#define SPI_ST_TX_DONE (1 << 25) +#define SPI_FIFO_LVL_MASK 0x1ff +#define SPI_TX_LVL_OFFSET 6 +#define SPI_RX_LVL_OFFSET 15 + +/* Feedback Delay */ +#define SPI_CLK_BYPASS (0 << 0) +#define SPI_FB_DELAY_90 (1 << 0) +#define SPI_FB_DELAY_180 (2 << 0) +#define SPI_FB_DELAY_270 (3 << 0) + +/* Packet Count */ +#define SPI_PACKET_CNT_EN (1 << 16) + +/* Swap config */ +#define SPI_TX_SWAP_EN (1 << 0) +#define SPI_TX_BYTE_SWAP (1 << 2) +#define SPI_TX_HWORD_SWAP (1 << 3) +#define SPI_TX_BYTE_SWAP (1 << 2) +#define SPI_RX_SWAP_EN (1 << 4) +#define SPI_RX_BYTE_SWAP (1 << 6) +#define SPI_RX_HWORD_SWAP (1 << 7) + +/* API */ +int exynos_spi_open(struct exynos_spi *regs); +int exynos_spi_read(struct exynos_spi *regs, void *dest, u32 len, u32 off); +int exynos_spi_close(struct exynos_spi *regs); + +/* Serve as CBFS media source */ +int initialize_exynos_spi_cbfs_media(struct cbfs_media *media, + void *buffer_address, + size_t buffer_size); +#endif diff --git a/src/soc/samsung/exynos5250/sysreg.h b/src/soc/samsung/exynos5250/sysreg.h new file mode 100644 index 0000000000..1362177a33 --- /dev/null +++ b/src/soc/samsung/exynos5250/sysreg.h @@ -0,0 +1,42 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* Register map for Exynos5 sysreg */ + +#ifndef CPU_SAMSUNG_EXYNOS5250_SYSREG_H +#define CPU_SAMSUNG_EXYNOS5250_SYSREG_H + +#include "cpu.h" + +/* sysreg map */ +struct exynos5_sysreg { + /* Add registers as and when required */ + unsigned char res1[0x214]; + unsigned int disp1blk_cfg; + unsigned char res2[0x18]; + unsigned int usb20_phy_cfg; +}; + +static struct exynos5_sysreg * const exynos_sysreg = + (void *)EXYNOS5_SYSREG_BASE; + +#define FIMDBYPASS_DISP1 (1 << 15) +#define USB20_PHY_CFG_EN (1 << 0) + +#endif diff --git a/src/soc/samsung/exynos5250/timer.c b/src/soc/samsung/exynos5250/timer.c new file mode 100644 index 0000000000..5d402bc4bc --- /dev/null +++ b/src/soc/samsung/exynos5250/timer.c @@ -0,0 +1,53 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2009 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <console/console.h> +#include <timer.h> +#include <delay.h> + +#include "clk.h" + +void init_timer(void) +{ + /* Nothing to do because we manually + * call mct_start() in the bootblock + */ +} + +/* delay x useconds */ +void udelay(unsigned usec) +{ + struct mono_time current, end; + + timer_monotonic_get(¤t); + end = current; + mono_time_add_usecs(&end, usec); + + if (mono_time_after(¤t, &end)) { + printk(BIOS_EMERG, "udelay: 0x%08x is impossibly large\n", + usec); + /* There's not much we can do if usec is too big. Use a long, + * paranoid delay value and hope for the best... */ + end = current; + mono_time_add_usecs(&end, USECS_PER_SEC); + } + + while (mono_time_before(¤t, &end)) + timer_monotonic_get(¤t); +} diff --git a/src/soc/samsung/exynos5250/tmu.c b/src/soc/samsung/exynos5250/tmu.c new file mode 100644 index 0000000000..1b5e9c2b64 --- /dev/null +++ b/src/soc/samsung/exynos5250/tmu.c @@ -0,0 +1,215 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2012 Samsung Electronics + * Copyright 2013 Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* EXYNOS - Thermal Management Unit */ + +#include <console/console.h> +#include <arch/io.h> +#include "power.h" +#include "tmu.h" + +#define TRIMINFO_RELOAD 1 +#define CORE_EN 1 +#define THERM_TRIP_EN (1 << 12) + +#define INTEN_RISE0 1 +#define INTEN_RISE1 (1 << 4) +#define INTEN_RISE2 (1 << 8) +#define INTEN_FALL0 (1 << 16) +#define INTEN_FALL1 (1 << 20) +#define INTEN_FALL2 (1 << 24) + +#define TRIM_INFO_MASK 0xff + +#define INTCLEAR_RISE0 1 +#define INTCLEAR_RISE1 (1 << 4) +#define INTCLEAR_RISE2 (1 << 8) +#define INTCLEAR_FALL0 (1 << 16) +#define INTCLEAR_FALL1 (1 << 20) +#define INTCLEAR_FALL2 (1 << 24) +#define INTCLEARALL (INTCLEAR_RISE0 | INTCLEAR_RISE1 | \ + INTCLEAR_RISE2 | INTCLEAR_FALL0 | \ + INTCLEAR_FALL1 | INTCLEAR_FALL2) + +struct tmu_info exynos5250_tmu_info = { + .tmu_base = 0x10060000, + .tmu_mux = 6, + .data = { + .ts = { + .min_val = 25, + .max_val = 125, + .start_warning = 95, + .start_tripping = 105, + .hardware_tripping = 110, + }, + .efuse_min_value = 40, + .efuse_value = 55, + .efuse_max_value = 100, + .slope = 0x10008802, + }, + .dc_value = 25, +}; + +/* + * After reading temperature code from register, compensating + * its value and calculating celsius temperature, + * get current temperature. + * + * @return current temperature of the chip as sensed by TMU + */ +static int get_cur_temp(struct tmu_info *info) +{ + int cur_temp; + struct tmu_reg *reg = (struct tmu_reg *)info->tmu_base; + + /* Temperature code range between min 25 and max 125 */ + cur_temp = readl(®->current_temp) & 0xff; + + /* Calibrate current temperature */ + if (cur_temp) + cur_temp = cur_temp - info->te1 + info->dc_value; + + return cur_temp; +} + +/* + * Monitors status of the TMU device and exynos temperature + * + * @info TMU info + * @temp pointer to the current temperature value + * @return enum tmu_status_t value, code indicating event to execute + */ +enum tmu_status_t tmu_monitor(struct tmu_info *info, int *temp) +{ + if (info->tmu_state == TMU_STATUS_INIT) + return -1; + + int cur_temp; + struct tmu_data *data = &info->data; + + /* Read current temperature of the SOC */ + cur_temp = get_cur_temp(info); + *temp = cur_temp; + + /* Temperature code lies between min 25 and max 125 */ + if (cur_temp >= data->ts.start_tripping && + cur_temp <= data->ts.max_val) + return TMU_STATUS_TRIPPED; + else if (cur_temp >= data->ts.start_warning) + return TMU_STATUS_WARNING; + else if (cur_temp < data->ts.start_warning && + cur_temp >= data->ts.min_val) + return TMU_STATUS_NORMAL; + /* Temperature code does not lie between min 25 and max 125 */ + else { + info->tmu_state = TMU_STATUS_INIT; + printk(BIOS_DEBUG, "EXYNOS_TMU: Thermal reading failed\n"); + return -1; + } + return 0; +} + +/* + * Calibrate and calculate threshold values and + * enable interrupt levels + * + * @param info pointer to the tmu_info struct + */ +static void tmu_setup_parameters(struct tmu_info *info) +{ + unsigned int te_temp, con; + unsigned int warning_code, trip_code, hwtrip_code; + unsigned int cooling_temp; + unsigned int rising_value; + struct tmu_data *data = &info->data; + struct tmu_reg *reg = (struct tmu_reg *)info->tmu_base; + + /* Must reload for using efuse value at EXYNOS */ + writel(TRIMINFO_RELOAD, ®->triminfo_control); + + /* Get the compensation parameter */ + te_temp = readl(®->triminfo); + info->te1 = te_temp & TRIM_INFO_MASK; + info->te2 = ((te_temp >> 8) & TRIM_INFO_MASK); + + if ((data->efuse_min_value > info->te1) || + (info->te1 > data->efuse_max_value) + || (info->te2 != 0)) + info->te1 = data->efuse_value; + + /* Get RISING & FALLING Threshold value */ + warning_code = data->ts.start_warning + + info->te1 - info->dc_value; + trip_code = data->ts.start_tripping + + info->te1 - info->dc_value; + hwtrip_code = data->ts.hardware_tripping + + info->te1 - info->dc_value; + + cooling_temp = 0; + + rising_value = ((warning_code << 8) | + (trip_code << 16) | + (hwtrip_code << 24)); + + /* Set interrupt level */ + writel(rising_value, ®->threshold_temp_rise); + writel(cooling_temp, ®->threshold_temp_fall); + + /* + * Need to init all register settings after getting parameter info + * [28:23] vref [11:8] slope - Tuning parameter + * + * WARNING: this slope value writes into many bits in the tmu_control + * register, with the default FDT value of 268470274 (0x10008802) + * we are using this essentially sets the default register setting + * from the TRM for tmu_control. + * TODO(bhthompson): rewrite this code such that we are not performing + * a hard wipe of tmu_control and re verify functionality. + */ + writel(data->slope, ®->tmu_control); + + writel(INTCLEARALL, ®->intclear); + /* TMU core enable */ + con = readl(®->tmu_control); + con |= (info->tmu_mux << 20) | THERM_TRIP_EN | CORE_EN; + + writel(con, ®->tmu_control); + + /* Enable HW thermal trip */ + power_enable_hw_thermal_trip(); + + /* LEV1 LEV2 interrupt enable */ + writel(INTEN_RISE1 | INTEN_RISE2, ®->inten); +} + +/* + * Initialize TMU device + * + * @return int value, 0 for success + */ +int tmu_init(struct tmu_info *info) +{ + info->tmu_state = TMU_STATUS_INIT; + + tmu_setup_parameters(info); + info->tmu_state = TMU_STATUS_NORMAL; + + return 0; +} diff --git a/src/soc/samsung/exynos5250/tmu.h b/src/soc/samsung/exynos5250/tmu.h new file mode 100644 index 0000000000..bda4bd7dd8 --- /dev/null +++ b/src/soc/samsung/exynos5250/tmu.h @@ -0,0 +1,134 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* EXYNOS - Thermal Management Unit */ + +#ifndef CPU_SAMSUNG_EXYNOS5250_TMU_H +#define CPU_SAMSUNG_EXYNOS5250_TMU_H + +struct tmu_reg { + unsigned triminfo; + unsigned rsvd1; + unsigned rsvd2; + unsigned rsvd3; + unsigned rsvd4; + unsigned triminfo_control; + unsigned rsvd5; + unsigned rsvd6; + unsigned tmu_control; + unsigned rsvd7; + unsigned tmu_status; + unsigned sampling_internal; + unsigned counter_value0; + unsigned counter_value1; + unsigned rsvd8; + unsigned rsvd9; + unsigned current_temp; + unsigned rsvd10; + unsigned rsvd11; + unsigned rsvd12; + unsigned threshold_temp_rise; + unsigned threshold_temp_fall; + unsigned rsvd13; + unsigned rsvd14; + unsigned past_temp3_0; + unsigned past_temp7_4; + unsigned past_temp11_8; + unsigned past_temp15_12; + unsigned inten; + unsigned intstat; + unsigned intclear; + unsigned rsvd15; + unsigned emul_con; +}; + +enum tmu_status_t { + TMU_STATUS_INIT = 0, + TMU_STATUS_NORMAL, + TMU_STATUS_WARNING, + TMU_STATUS_TRIPPED, +}; + +/* Temperature threshold values for various thermal events */ +struct temperature_params { + /* minimum value in temperature code range */ + unsigned int min_val; + /* maximum value in temperature code range */ + unsigned int max_val; + /* temperature threshold to start warning */ + unsigned int start_warning; + /* temperature threshold CPU tripping */ + unsigned int start_tripping; + /* temperature threshold for HW tripping */ + unsigned int hardware_tripping; +}; + +/* Pre-defined values and thresholds for calibration of current temperature */ +struct tmu_data { + /* pre-defined temperature thresholds */ + struct temperature_params ts; + /* pre-defined efuse range minimum value */ + unsigned int efuse_min_value; + /* pre-defined efuse value for temperature calibration */ + unsigned int efuse_value; + /* pre-defined efuse range maximum value */ + unsigned int efuse_max_value; + /* current temperature sensing slope */ + unsigned int slope; +}; + +/* TMU device specific details and status */ +struct tmu_info { + /* base Address for the TMU */ + unsigned tmu_base; + /* mux Address for the TMU */ + int tmu_mux; + /* pre-defined values for calibration and thresholds */ + struct tmu_data data; + /* value required for triminfo_25 calibration */ + unsigned int te1; + /* value required for triminfo_85 calibration */ + unsigned int te2; + /* TMU DC value for threshold calculation */ + int dc_value; + /* enum value indicating status of the TMU */ + int tmu_state; +}; + +extern struct tmu_info *tmu_info; + +/* + * Monitors status of the TMU device and exynos temperature + * + * @info pointer to TMU info struct + * @temp pointer to the current temperature value + * @return enum tmu_status_t value, code indicating event to execute + * and -1 on error + */ +enum tmu_status_t tmu_monitor(struct tmu_info *info, int *temp); + +/* + * Initialize TMU device + * + * @info pointer to TMU info struct + * @return int value, 0 for success + */ +int tmu_init(struct tmu_info *info); + +#endif /* CPU_SAMSUNG_EXYNOS5250_TMU_H */ diff --git a/src/soc/samsung/exynos5250/trustzone.c b/src/soc/samsung/exynos5250/trustzone.c new file mode 100644 index 0000000000..d67eb33228 --- /dev/null +++ b/src/soc/samsung/exynos5250/trustzone.c @@ -0,0 +1,44 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2013 Google Inc. + * Copyright (C) 2012 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <arch/io.h> +#include "trustzone.h" + +/* Setting TZPC[TrustZone Protection Controller] */ +void trustzone_init(void) +{ + struct exynos_tzpc *tzpc; + unsigned int addr; + + for (addr = TZPC0_BASE; addr <= TZPC9_BASE; addr += TZPC_BASE_OFFSET) { + tzpc = (struct exynos_tzpc *)addr; + + if (addr == TZPC0_BASE) + writel(R0SIZE, &tzpc->r0size); + + writel(DECPROTXSET, &tzpc->decprot0set); + writel(DECPROTXSET, &tzpc->decprot1set); + + if (addr != TZPC9_BASE) { + writel(DECPROTXSET, &tzpc->decprot2set); + writel(DECPROTXSET, &tzpc->decprot3set); + } + } +} diff --git a/src/soc/samsung/exynos5250/trustzone.h b/src/soc/samsung/exynos5250/trustzone.h new file mode 100644 index 0000000000..7b4af9b1f1 --- /dev/null +++ b/src/soc/samsung/exynos5250/trustzone.h @@ -0,0 +1,81 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2013 Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef CPU_SAMSUNG_EXYNOS5250_TRUSTZONE_H +#define CPU_SAMSUNG_EXYNOS5250_TRUSTZONE_H + +#include <stdint.h> + +/* Distance between each Trust Zone PC register set */ +#define TZPC_BASE_OFFSET 0x10000 +/* TZPC : Register Offsets */ +#define TZPC0_BASE 0x10100000 +#define TZPC1_BASE 0x10110000 +#define TZPC2_BASE 0x10120000 +#define TZPC3_BASE 0x10130000 +#define TZPC4_BASE 0x10140000 +#define TZPC5_BASE 0x10150000 +#define TZPC6_BASE 0x10160000 +#define TZPC7_BASE 0x10170000 +#define TZPC8_BASE 0x10180000 +#define TZPC9_BASE 0x10190000 +#define TZPC10_BASE 0x100E0000 +#define TZPC11_BASE 0x100F0000 + +/* + * TZPC Register Value : + * R0SIZE: 0x0 : Size of secured ram + */ +#define R0SIZE 0x0 + +/* + * TZPC Decode Protection Register Value : + * DECPROTXSET: 0xFF : Set Decode region to non-secure + */ +#define DECPROTXSET 0xFF + +struct exynos_tzpc { + u32 r0size; + u8 res1[0x7FC]; + u32 decprot0stat; + u32 decprot0set; + u32 decprot0clr; + u32 decprot1stat; + u32 decprot1set; + u32 decprot1clr; + u32 decprot2stat; + u32 decprot2set; + u32 decprot2clr; + u32 decprot3stat; + u32 decprot3set; + u32 decprot3clr; + u8 res2[0x7B0]; + u32 periphid0; + u32 periphid1; + u32 periphid2; + u32 periphid3; + u32 pcellid0; + u32 pcellid1; + u32 pcellid2; + u32 pcellid3; +}; + +void trustzone_init(void); + +#endif /* CPU_SAMSUNG_EXYNOS5250_TRUSTZONE_H */ diff --git a/src/soc/samsung/exynos5250/uart.c b/src/soc/samsung/exynos5250/uart.c new file mode 100644 index 0000000000..cc97291807 --- /dev/null +++ b/src/soc/samsung/exynos5250/uart.c @@ -0,0 +1,203 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2009 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <types.h> +#include <console/uart.h> +#include <arch/io.h> +#include <boot/coreboot_tables.h> +#include "uart.h" +#include "clk.h" +#include "cpu.h" +#include "periph.h" +#include "uart.h" + +#define RX_FIFO_COUNT_MASK 0xff +#define RX_FIFO_FULL_MASK (1 << 8) +#define TX_FIFO_FULL_MASK (1 << 24) + + +/* + * 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 recommend 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(struct s5p_uart *uart) +{ + u32 uclk; + u32 val; + + // All UARTs share the same clock. + uclk = clock_get_periph_rate(PERIPH_ID_UART3); + val = uclk / default_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()) + writel(udivslot[val % 16], &uart->rest.slot); + else + writeb(val % 16, &uart->rest.value); +#endif +} + +/* + * Initialise the serial port with the given baudrate. The settings + * are always 8 data bits, no parity, 1 stop bit, no start bits. + */ +static void exynos5_init_dev(struct s5p_uart *uart) +{ + // TODO initialize with correct peripheral id by base_port. + exynos_pinmux_uart3(); + + /* 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(uart); +} + +static int exynos5_uart_err_check(struct s5p_uart *uart, int op) +{ + 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 successful, the character read is + * written into its argument c. + */ +static unsigned char exynos5_uart_rx_byte(struct s5p_uart *uart) +{ + /* wait for character to arrive */ + while (!(readl(&uart->ufstat) & (RX_FIFO_COUNT_MASK | + RX_FIFO_FULL_MASK))) { + if (exynos5_uart_err_check(uart, 0)) + return 0; + } + + return readb(&uart->urxh) & 0xff; +} + +/* + * Output a single byte to the serial port. + */ +static void exynos5_uart_tx_byte(struct s5p_uart *uart, unsigned char data) +{ + /* wait for room in the tx FIFO */ + while ((readl(&uart->ufstat) & TX_FIFO_FULL_MASK)) { + if (exynos5_uart_err_check(uart, 1)) + return; + } + + writeb(data, &uart->utxh); +} + +static void exynos5_uart_tx_flush(struct s5p_uart *uart) +{ + while (readl(&uart->ufstat) & 0x1ff0000); +} + +unsigned int uart_platform_base(int idx) +{ + if (idx < 4) + return 0x12c00000 + idx * 0x10000; + else + return 0; +} + +void uart_init(int idx) +{ + struct s5p_uart *uart = uart_platform_baseptr(idx); + exynos5_init_dev(uart); +} + +unsigned char uart_rx_byte(int idx) +{ + struct s5p_uart *uart = uart_platform_baseptr(idx); + return exynos5_uart_rx_byte(uart); +} + +void uart_tx_byte(int idx, unsigned char data) +{ + struct s5p_uart *uart = uart_platform_baseptr(idx); + exynos5_uart_tx_byte(uart, data); +} + +void uart_tx_flush(int idx) +{ + struct s5p_uart *uart = uart_platform_baseptr(idx); + exynos5_uart_tx_flush(uart); +} + +#ifndef __PRE_RAM__ +void uart_fill_lb(void *data) +{ + struct lb_serial serial; + serial.type = LB_SERIAL_TYPE_MEMORY_MAPPED; + serial.baseaddr = uart_platform_base(CONFIG_UART_FOR_CONSOLE); + serial.baud = default_baudrate(); + lb_add_serial(&serial, data); + + lb_add_console(LB_TAG_CONSOLE_SERIAL8250MEM, data); +} +#endif diff --git a/src/soc/samsung/exynos5250/uart.h b/src/soc/samsung/exynos5250/uart.h new file mode 100644 index 0000000000..d324a2f93d --- /dev/null +++ b/src/soc/samsung/exynos5250/uart.h @@ -0,0 +1,48 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2012 Google Inc. + * Copyright (C) 2009 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef CPU_SAMSUNG_EXYNOS5250_UART_H +#define CPU_SAMSUNG_EXYNOS5250_UART_H + +/* baudrate rest value */ +union br_rest { + unsigned short slot; /* udivslot */ + unsigned char value; /* ufracval */ +}; + +struct s5p_uart { + unsigned int ulcon; + unsigned int ucon; + unsigned int ufcon; + unsigned int umcon; + unsigned int utrstat; + unsigned int uerstat; + unsigned int ufstat; + unsigned int umstat; + unsigned char utxh; + unsigned char res1[3]; + unsigned char urxh; + unsigned char res2[3]; + unsigned int ubrdiv; + union br_rest rest; + unsigned char res3[0xffd0]; +}; + +#endif diff --git a/src/soc/samsung/exynos5250/usb.c b/src/soc/samsung/exynos5250/usb.c new file mode 100644 index 0000000000..76da1dd56e --- /dev/null +++ b/src/soc/samsung/exynos5250/usb.c @@ -0,0 +1,198 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2012 Samsung Electronics + * Copyright 2013 Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <delay.h> +#include <arch/io.h> +#include <console/console.h> +#include <device/device.h> +#include "gpio.h" +#include "power.h" +#include "sysreg.h" +#include "usb.h" + +static void reset_dwc3(struct exynos5_usb_drd_dwc3 *dwc3) +{ + setbits_le32(&dwc3->ctl, 0x1 << 11); /* core soft reset */ + setbits_le32(&dwc3->usb3pipectl, 0x1 << 31); /* PHY soft reset */ + setbits_le32(&dwc3->usb2phycfg, 0x1 << 31); /* PHY soft reset */ +} + +void reset_usb_drd_dwc3() +{ + printk(BIOS_DEBUG, "Starting DWC3 reset for USB DRD\n"); + reset_dwc3(exynos_usb_drd_dwc3); +} + +static void setup_dwc3(struct exynos5_usb_drd_dwc3 *dwc3) +{ + if (!(dwc3->ctl & 0x1 << 11) || + !(dwc3->usb3pipectl & 0x1 << 31) || + !(dwc3->usb2phycfg & 0x1 << 31)) { + printk(BIOS_ERR, "DWC3 at %p not in reset (you need to call " + "reset_usb_drd_dwc3() first)!\n", dwc3); + } + + /* Set relevant registers to default values (clearing all reset bits) */ + + writel(0x1 << 24 | /* activate PHY low power states */ + 0x4 << 19 | /* low power delay value */ + 0x1 << 18 | /* activate PHY low power delay */ + 0x1 << 17 | /* enable SuperSpeed PHY suspend */ + 0x1 << 1 | /* default Tx deemphasis value */ + 0, &dwc3->usb3pipectl); + + /* Configure PHY clock turnaround for 8-bit UTMI+, disable suspend */ + writel(0x9 << 10 | /* PHY clock turnaround for 8-bit UTMI+ */ + 0x1 << 8 | /* enable PHY sleep in L1 */ + 0x1 << 6 | /* enable PHY suspend */ + 0, &dwc3->usb2phycfg); + + writel(0x5dc << 19 | /* suspend clock scale for 24MHz */ + 0x1 << 16 | /* retry SS three times (bugfix from U-Boot) */ + 0x1 << 12 | /* port capability HOST */ + 0, &dwc3->ctl); +} + +void setup_usb_drd_dwc3() +{ + setup_dwc3(exynos_usb_drd_dwc3); + printk(BIOS_DEBUG, "DWC3 setup for USB DRD finished\n"); +} + +static void setup_drd_phy(struct exynos5_usb_drd_phy *phy) +{ + /* Set all PHY registers to default values */ + + /* XHCI Version 1.0, Frame Length adjustment 30 MHz */ + setbits_le32(&phy->linksystem, 0x1 << 27 | 0x20 << 1); + + /* Disable OTG, ID0 and DRVVBUS, do not force sleep/suspend */ + writel(1 << 6, &phy->utmi); + + writel(0x88 << 23 | /* spread spectrum refclk selector */ + 0x1 << 20 | /* enable spread spectrum */ + 0x1 << 19 | /* enable prescaler refclk */ + 0x68 << 11 | /* multiplier for 24MHz refclk */ + 0x5 << 5 | /* select 24MHz refclk (weird, from U-Boot) */ + 0x1 << 4 | /* power supply in normal operating mode */ + 0x3 << 2 | /* use external refclk (undocumented on 5420?)*/ + 0x1 << 1 | /* force port reset */ + 0x1 << 0 | /* normal operating mode */ + 0, &phy->clkrst); + + writel(0x9 << 26 | /* LOS level */ + 0x3 << 22 | /* TX VREF tune */ + 0x1 << 20 | /* TX rise tune */ + 0x1 << 18 | /* TX res tune */ + 0x3 << 13 | /* TX HS X Vtune */ + 0x3 << 9 | /* TX FS/LS tune */ + 0x3 << 6 | /* SQRX tune */ + 0x4 << 3 | /* OTG tune */ + 0x4 << 0 | /* comp disc tune */ + 0, &phy->param0); + + writel(0x7f << 19 | /* reserved */ + 0x7f << 12 | /* Tx launch amplitude */ + 0x20 << 6 | /* Tx deemphasis 6dB */ + 0x1c << 0 | /* Tx deemphasis 3.5dB (value from U-Boot) */ + 0, &phy->param1); + + /* disable all test features */ + writel(0, &phy->test); + + /* UTMI clock select? ("must be 0x1") */ + writel(0x1 << 2, &phy->utmiclksel); + + /* Samsung magic, undocumented (from U-Boot) */ + writel(0x0, &phy->resume); + + udelay(10); + clrbits_le32(&phy->clkrst, 0x1 << 1); /* deassert port reset */ +} + +void setup_usb_drd_phy() +{ + printk(BIOS_DEBUG, "Powering up USB DRD PHY\n"); + setbits_le32(&exynos_power->usb_drd_phy_ctrl, POWER_USB_PHY_CTRL_EN); + setup_drd_phy(exynos_usb_drd_phy); +} + +void setup_usb_host_phy(int hsic_gpio) +{ + unsigned int hostphy_ctrl0; + + setbits_le32(&exynos_sysreg->usb20_phy_cfg, USB20_PHY_CFG_EN); + setbits_le32(&exynos_power->usb_host_phy_ctrl, POWER_USB_PHY_CTRL_EN); + + printk(BIOS_DEBUG, "Powering up USB HOST PHY (%s HSIC)\n", + hsic_gpio ? "with" : "without"); + + hostphy_ctrl0 = readl(&exynos_usb_host_phy->usbphyctrl0); + hostphy_ctrl0 &= ~(HOST_CTRL0_FSEL_MASK | + HOST_CTRL0_COMMONON_N | + /* HOST Phy setting */ + HOST_CTRL0_PHYSWRST | + HOST_CTRL0_PHYSWRSTALL | + HOST_CTRL0_SIDDQ | + HOST_CTRL0_FORCESUSPEND | + HOST_CTRL0_FORCESLEEP); + hostphy_ctrl0 |= (/* Setting up the ref freq */ + CLK_24MHZ << 16 | + /* HOST Phy setting */ + HOST_CTRL0_LINKSWRST | + HOST_CTRL0_UTMISWRST); + writel(hostphy_ctrl0, &exynos_usb_host_phy->usbphyctrl0); + udelay(10); + clrbits_le32(&exynos_usb_host_phy->usbphyctrl0, + HOST_CTRL0_LINKSWRST | + HOST_CTRL0_UTMISWRST); + udelay(20); + + /* EHCI Ctrl setting */ + setbits_le32(&exynos_usb_host_phy->ehcictrl, + EHCICTRL_ENAINCRXALIGN | + EHCICTRL_ENAINCR4 | + EHCICTRL_ENAINCR8 | + EHCICTRL_ENAINCR16); + + /* HSIC USB Hub initialization. */ + if (hsic_gpio) { + gpio_direction_output(hsic_gpio, 0); + udelay(100); + gpio_direction_output(hsic_gpio, 1); + udelay(5000); + + clrbits_le32(&exynos_usb_host_phy->hsicphyctrl1, + HOST_CTRL0_SIDDQ | + HOST_CTRL0_FORCESLEEP | + HOST_CTRL0_FORCESUSPEND); + setbits_le32(&exynos_usb_host_phy->hsicphyctrl1, + HOST_CTRL0_PHYSWRST); + udelay(10); + clrbits_le32(&exynos_usb_host_phy->hsicphyctrl1, + HOST_CTRL0_PHYSWRST); + } + + /* At this point we need to wait for 50ms before talking to + * the USB controller (PHY clock and power setup time) + * By the time we are actually in the payload, these 50ms + * will have passed. + */ +} diff --git a/src/soc/samsung/exynos5250/usb.h b/src/soc/samsung/exynos5250/usb.h new file mode 100644 index 0000000000..ad617da96c --- /dev/null +++ b/src/soc/samsung/exynos5250/usb.h @@ -0,0 +1,137 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2012 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef CPU_SAMSUNG_EXYNOS5250_USB_H +#define CPU_SAMSUNG_EXYNOS5250_USB_H + +#include "cpu.h" + +#define CLK_24MHZ 5 + +#define HOST_CTRL0_PHYSWRSTALL (1 << 31) +#define HOST_CTRL0_COMMONON_N (1 << 9) +#define HOST_CTRL0_SIDDQ (1 << 6) +#define HOST_CTRL0_FORCESLEEP (1 << 5) +#define HOST_CTRL0_FORCESUSPEND (1 << 4) +#define HOST_CTRL0_WORDINTERFACE (1 << 3) +#define HOST_CTRL0_UTMISWRST (1 << 2) +#define HOST_CTRL0_LINKSWRST (1 << 1) +#define HOST_CTRL0_PHYSWRST (1 << 0) + +#define HOST_CTRL0_FSEL_MASK (7 << 16) + +#define EHCICTRL_ENAINCRXALIGN (1 << 29) +#define EHCICTRL_ENAINCR4 (1 << 28) +#define EHCICTRL_ENAINCR8 (1 << 27) +#define EHCICTRL_ENAINCR16 (1 << 26) + +/* Register map for PHY control */ +struct exynos5_usb_host_phy { + uint32_t usbphyctrl0; + uint32_t usbphytune0; + uint8_t reserved1[8]; + uint32_t hsicphyctrl1; + uint32_t hsicphytune1; + uint8_t reserved2[8]; + uint32_t hsicphyctrl2; + uint32_t hsicphytune2; + uint8_t reserved3[8]; + uint32_t ehcictrl; + uint32_t ohcictrl; + uint32_t usbotgsys; + uint8_t reserved4[4]; + uint32_t usbotgtune; +}; + +static struct exynos5_usb_host_phy * const exynos_usb_host_phy = + (void *)EXYNOS5_USB_HOST_PHY_BASE; + +struct exynos5_usb_drd_phy { + uint8_t reserved1[4]; + uint32_t linksystem; + uint32_t utmi; + uint32_t pipe; + uint32_t clkrst; + uint32_t reg0; + uint32_t reg1; + uint32_t param0; + uint32_t param1; + uint32_t term; + uint32_t test; + uint32_t adp; + uint32_t utmiclksel; + uint32_t resume; + uint8_t reserved2[8]; + uint32_t linkhcbelt; + uint32_t linkport; +}; + +static struct exynos5_usb_drd_phy * const exynos_usb_drd_phy = + (void *)EXYNOS5_USB_DRD_PHY_BASE; + +struct exynos5_usb_drd_dwc3 { + uint32_t sbuscfg0; + uint32_t sbuscfg1; + uint32_t txthrcfg; + uint32_t rxthrcfg; + uint32_t ctl; + uint32_t evten; + uint32_t sts; + uint8_t reserved0[4]; + uint32_t snpsid; + uint32_t gpio; + uint32_t uid; + uint32_t uctl; + uint64_t buserraddr; + uint64_t prtbimap; + uint8_t reserved1[32]; + uint32_t dbgfifospace; + uint32_t dbgltssm; + uint32_t dbglnmcc; + uint32_t dbgbmu; + uint32_t dbglspmux; + uint32_t dbglsp; + uint32_t dbgepinfo0; + uint32_t dbgepinfo1; + uint64_t prtbimap_hs; + uint64_t prtbimap_fs; + uint8_t reserved2[112]; + uint32_t usb2phycfg; + uint8_t reserved3[60]; + uint32_t usb2i2cctl; + uint8_t reserved4[60]; + uint32_t usb2phyacc; + uint8_t reserved5[60]; + uint32_t usb3pipectl; + uint8_t reserved6[60]; +}; + +static struct exynos5_usb_drd_dwc3 * const exynos_usb_drd_dwc3 = + (void *)EXYNOS5_USB_DRD_DWC3_BASE; + +/* Leave hsic_gpio at 0 to not enable HSIC. */ +void setup_usb_host_phy(int hsic_gpio); + +void setup_usb_drd_phy(void); + +/* Call reset_ before setup_, ensure at least 100ms pass in between. */ +void reset_usb_drd_dwc3(void); +void setup_usb_drd_dwc3(void); + +#endif diff --git a/src/soc/samsung/exynos5250/wakeup.c b/src/soc/samsung/exynos5250/wakeup.c new file mode 100644 index 0000000000..b7161cf935 --- /dev/null +++ b/src/soc/samsung/exynos5250/wakeup.c @@ -0,0 +1,55 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2013 Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <console/console.h> +#include "power.h" +#include "wakeup.h" + +void wakeup(void) +{ + if (wakeup_need_reset()) + power_reset(); + + power_init(); /* Ensure ps_hold_setup() for early wakeup. */ + power_exit_wakeup(); + /* Should never return. If we do, reset. */ + power_reset(); +} + +int get_wakeup_state(void) +{ + uint32_t status = power_read_reset_status(); + + /* DIDLE/LPA can be resumed without clock reset (ex, bootblock), + * and SLEEP requires resetting clock (should be done in ROM stage). + */ + + if (status == S5P_CHECK_DIDLE || status == S5P_CHECK_LPA) + return WAKEUP_DIRECT; + + if (status == S5P_CHECK_SLEEP) + return WAKEUP_NEED_CLOCK_RESET; + + return IS_NOT_WAKEUP; +} + +void wakeup_enable_uart(void) +{ + power_release_uart_retention(); +} diff --git a/src/soc/samsung/exynos5250/wakeup.h b/src/soc/samsung/exynos5250/wakeup.h new file mode 100644 index 0000000000..690c6a3262 --- /dev/null +++ b/src/soc/samsung/exynos5250/wakeup.h @@ -0,0 +1,43 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2013 Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef CPU_SAMSUNG_EXYNOS5250_WAKEUP_H +#define CPU_SAMSUNG_EXYNOS5250_WAKEUP_H + +/* Power Down Modes */ +#define S5P_CHECK_SLEEP 0x00000BAD +#define S5P_CHECK_DIDLE 0xBAD00000 +#define S5P_CHECK_LPA 0xABAD0000 + +enum { + // A normal boot (not suspend/resume) + IS_NOT_WAKEUP, + // A wake up event that can be resumed any time + WAKEUP_DIRECT, + // A wake up event that must be resumed only after + // clock and memory controllers are re-initialized + WAKEUP_NEED_CLOCK_RESET, +}; + +int wakeup_need_reset(void); +int get_wakeup_state(void); +void wakeup(void); +void wakeup_enable_uart(void); + +#endif /* CPU_SAMSUNG_EXYNOS5250_WAKEUP_H */ diff --git a/src/soc/samsung/exynos5420/Kconfig b/src/soc/samsung/exynos5420/Kconfig new file mode 100644 index 0000000000..a0d6c9882c --- /dev/null +++ b/src/soc/samsung/exynos5420/Kconfig @@ -0,0 +1,110 @@ +config CPU_SAMSUNG_EXYNOS5420 + select ARCH_BOOTBLOCK_ARMV7 + select ARCH_ROMSTAGE_ARMV7 + select ARCH_RAMSTAGE_ARMV7 + select CPU_HAS_BOOTBLOCK_INIT + select HAVE_MONOTONIC_TIMER + select HAVE_UART_SPECIAL + select RELOCATABLE_MODULES + select DYNAMIC_CBMEM + bool + default n + +if CPU_SAMSUNG_EXYNOS5420 + +# ROM image layout. +# +# 0x0000: vendor-provided BL1 (8k). +# 0x2000: variable length bootblock checksum header +# 0x2010: bootblock +# 0x2020-0x20A0: reserved for CBFS master header. +# 0xA000: Free for CBFS data. + +config BOOTBLOCK_ROM_OFFSET + hex + default 0x2010 + +config CBFS_HEADER_ROM_OFFSET + hex "offset of master CBFS header in ROM" + default 0x2020 + +config CBFS_ROM_OFFSET + # Calculated by BOOTBLOCK_ROM_OFFSET + max bootblock size. + hex "offset of CBFS data in ROM" + default 0x0A000 + +config SYS_SDRAM_BASE + hex + default 0x20000000 + +# Example SRAM/iRAM map for Exynos5420 platform: +# +# 0x0202_0000: vendor-provided BL1 +# 0x0202_4400: variable length bootblock checksum header. +# 0x0202_4410: bootblock, assume up to 32KB in size +# 0x0203_0000: romstage, assume up to 128KB in size. +# 0x0205_8000: TTB buffer. +# 0x0205_c000: cache for CBFS data. +# 0x0206_f000: stack bottom +# 0x0207_3000: stack pointer +# 0x0207_3000: shared (with kernel) page for cpu & secondary core states. +# the shared data is currently only <0x50 bytes so we can share +# this page with stack. + +config BOOTBLOCK_BASE + hex + default 0x02024410 + +config ROMSTAGE_BASE + hex + default 0x02030000 + +config RAMSTAGE_BASE + hex + default SYS_SDRAM_BASE + +# Stack may reside in either IRAM or DRAM. We will define it to live +# at the top of IRAM for now. +# +# Stack grows downward, push operation stores register contents in +# consecutive memory locations ending just below SP. +# The setup in the exynos 5420 is a new one for coreboot. We have got +# the bootblock, romstage, and ramstage sharing the same stack space. +# The SRAM is always there and having a known-good stack memory +# makes for a more reliable setup. +# Thus, in this case: +# STACK_TOP: highest stack address in SRAM +# STACK_BOTTOM: lowest stack address in SRAM +# STACK_SIZE: as in standard coreboot usage, size of thread stacks in ramstage +# ROMSTAGE_STACK_SIZE: size of the single stack in romstage + +config STACK_TOP + hex + default 0x02073000 + +config STACK_BOTTOM + hex + default 0x0206f000 + +# STACK_SIZE is for the ramstage core and thread stacks. +# It must be a power of 2, to make the cpu_info computation work, +# and cpu_info needs to work to make SMP startup and threads work. +config STACK_SIZE + hex + default 0x0800 + +# TODO We may probably move this to board-specific implementation files instead +# of KConfig values. +config CBFS_CACHE_ADDRESS + hex "memory address to put CBFS cache data" + default 0x0205c000 + +config CBFS_CACHE_SIZE + hex "size of CBFS cache data" + default 0x00013000 + +config TTB_BUFFER + hex "memory address of the TTB buffer" + default 0x02058000 + +endif diff --git a/src/soc/samsung/exynos5420/Makefile.inc b/src/soc/samsung/exynos5420/Makefile.inc new file mode 100644 index 0000000000..ef77b1b728 --- /dev/null +++ b/src/soc/samsung/exynos5420/Makefile.inc @@ -0,0 +1,62 @@ +bootblock-y += spi.c alternate_cbfs.c +bootblock-y += bootblock.c +bootblock-y += pinmux.c mct.c power.c +# Clock is required for UART +bootblock-$(CONFIG_BOOTBLOCK_CONSOLE) += clock_init.c +bootblock-$(CONFIG_BOOTBLOCK_CONSOLE) += clock.c +bootblock-$(CONFIG_BOOTBLOCK_CONSOLE) += monotonic_timer.c +ifeq ($(CONFIG_DRIVERS_UART),y) +bootblock-$(CONFIG_BOOTBLOCK_CONSOLE) += uart.c +endif +bootblock-y += wakeup.c +bootblock-y += gpio.c +bootblock-$(CONFIG_BOOTBLOCK_CONSOLE) += timer.c + +romstage-y += spi.c alternate_cbfs.c +romstage-y += smp.c +romstage-y += clock.c +romstage-y += clock_init.c +romstage-y += pinmux.c # required by s3c24x0_i2c and uart. +romstage-y += dmc_common.c +romstage-y += dmc_init_ddr3.c +romstage-y += power.c +romstage-y += mct.c +romstage-y += monotonic_timer.c +ifeq ($(CONFIG_DRIVERS_UART),y) +romstage-y += uart.c +endif +romstage-y += wakeup.c +romstage-y += gpio.c +romstage-y += timer.c +romstage-y += i2c.c +#romstage-y += wdt.c +romstage-y += cbmem.c +romstage-y += trustzone.c + +ramstage-y += spi.c alternate_cbfs.c +ramstage-y += clock.c +ramstage-y += clock_init.c +ramstage-y += pinmux.c +ramstage-y += power.c +ramstage-$(CONFIG_DRIVERS_UART) += uart.c +ramstage-y += cpu.c +ramstage-y += tmu.c +ramstage-y += mct.c +ramstage-y += monotonic_timer.c +ramstage-y += timer.c +ramstage-y += gpio.c +ramstage-y += i2c.c +ramstage-y += dp.c dp_lowlevel.c fimd.c +ramstage-y += usb.c +ramstage-y += cbmem.c + +# Run an intermediate step when producing coreboot.rom +# that adds additional components to the final firmware +# image outside of CBFS +.PHONY: exynos5420_add_bl1 +$(obj)/coreboot.rom: exynos5420_add_bl1 +exynos5420_add_bl1: $(obj)/coreboot.pre + printf " DD Adding Samsung Exynos5420 BL1\n" + # TODO(hungte) Change this 'cpu' to soc when build scripts are changed. + dd if=3rdparty/cpu/samsung/exynos5420/bl1.bin \ + of=$(obj)/coreboot.pre conv=notrunc >/dev/null 2>&1 diff --git a/src/soc/samsung/exynos5420/alternate_cbfs.c b/src/soc/samsung/exynos5420/alternate_cbfs.c new file mode 100644 index 0000000000..d19098b948 --- /dev/null +++ b/src/soc/samsung/exynos5420/alternate_cbfs.c @@ -0,0 +1,183 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2013 Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#include <assert.h> +#include <cbfs.h> /* This driver serves as a CBFS media source. */ +#include <stdlib.h> +#include <string.h> +#include <arch/cache.h> +#include <console/console.h> +#include "alternate_cbfs.h" +#include "power.h" +#include "spi.h" + +/* This allows USB A-A firmware upload from a compatible host in four parts: + * The first two are the bare BL1 and the Coreboot boot block, which are just + * written to their respective loading addresses. These transfers are initiated + * by the IROM / BL1, so this code has nothing to do with them. + * + * The third transfer is a valid CBFS image that contains only the romstage, + * and must be small enough to fit into alternate_cbfs_size[__BOOT_BLOCK__] in + * IRAM. It is loaded when this function gets called in the boot block, and + * the normal CBFS code extracts the romstage from it. + * + * The fourth transfer is also a CBFS image, but can be of arbitrary size and + * should contain all available stages/payloads/etc. It is loaded when this + * function is called a second time at the end of the romstage, and copied to + * alternate_cbfs_buffer[!__BOOT_BLOCK__] in DRAM. It will reside there for the + * rest of the firmware's lifetime and all subsequent stages (which will not + * have __PRE_RAM__ defined) can just directly reference it there. + */ +static int usb_cbfs_open(struct cbfs_media *media) +{ +#ifdef __PRE_RAM__ + static int first_run = 1; + int (*irom_load_usb)(void) = *irom_load_image_from_usb_ptr; + + if (!first_run) + return 0; + + dcache_mmu_disable(); + if (!irom_load_usb()) { + dcache_mmu_enable(); + printk(BIOS_EMERG, "Unable to load CBFS image via USB!\n"); + return -1; + } + dcache_mmu_enable(); + + /* + * We need to trust the host/irom to copy the image to our + * alternate_cbfs_buffer address... there is no way to control or even + * check the transfer size or target address from our side. + */ + + printk(BIOS_DEBUG, "USB A-A transfer successful, CBFS image should now" + " be at %p\n", alternate_cbfs_buffer); + first_run = 0; +#endif + return 0; +} + +/* + * SDMMC works very similar to USB A-A: we copy the CBFS image into memory + * and read it from there. While SDMMC would also allow direct block by block + * on-demand reading, we might run into problems if we call back into the IROM + * in very late boot stages (e.g. after initializing/changing MMC clocks)... so + * this seems like a safer approach. It also makes it easy to pass our image + * down to payloads. + */ +static int sdmmc_cbfs_open(struct cbfs_media *media) +{ +#ifdef __PRE_RAM__ + /* + * In the bootblock, we just copy the small part that fits in the buffer + * and hope that it's enough (since the romstage is currently always the + * first component in the image, this should work out). In the romstage, + * we copy until our buffer is full (currently 12M) to avoid the pain of + * figuring out the true image size from in here. Since this is mainly a + * developer/debug boot mode, those shortcomings should be bearable. + */ + const u32 count = alternate_cbfs_size / 512; + static int first_run = 1; + int (*irom_load_sdmmc)(u32 start, u32 count, void *dst) = + *irom_sdmmc_read_blocks_ptr; + + if (!first_run) + return 0; + + dcache_mmu_disable(); + if (!irom_load_sdmmc(1, count, alternate_cbfs_buffer)) { + dcache_mmu_enable(); + printk(BIOS_EMERG, "Unable to load CBFS image from SDMMC!\n"); + return -1; + } + dcache_mmu_enable(); + + printk(BIOS_DEBUG, "SDMMC read successful, CBFS image should now be" + " at %p\n", alternate_cbfs_buffer); + first_run = 0; +#endif + return 0; +} + +static int alternate_cbfs_close(struct cbfs_media *media) { return 0; } + +static size_t alternate_cbfs_read(struct cbfs_media *media, void *dest, + size_t offset, size_t count) +{ + ASSERT(offset + count < alternate_cbfs_size); + memcpy(dest, alternate_cbfs_buffer + offset, count); + return count; +} + +static void *alternate_cbfs_map(struct cbfs_media *media, size_t offset, + size_t count) +{ + ASSERT(offset + count < alternate_cbfs_size); + return alternate_cbfs_buffer + offset; +} + +static void *alternate_cbfs_unmap(struct cbfs_media *media, + const void *buffer) { return 0; } + +static int initialize_exynos_sdmmc_cbfs_media(struct cbfs_media *media) +{ + printk(BIOS_DEBUG, "Using Exynos alternate boot mode SDMMC\n"); + + media->open = sdmmc_cbfs_open; + media->close = alternate_cbfs_close; + media->read = alternate_cbfs_read; + media->map = alternate_cbfs_map; + media->unmap = alternate_cbfs_unmap; + + return 0; +} + +static int initialize_exynos_usb_cbfs_media(struct cbfs_media *media) +{ + printk(BIOS_DEBUG, "Using Exynos alternate boot mode USB A-A\n"); + + media->open = usb_cbfs_open; + media->close = alternate_cbfs_close; + media->read = alternate_cbfs_read; + media->map = alternate_cbfs_map; + media->unmap = alternate_cbfs_unmap; + + return 0; +} + +int init_default_cbfs_media(struct cbfs_media *media) +{ + if (*iram_secondary_base == SECONDARY_BASE_BOOT_USB) + return initialize_exynos_usb_cbfs_media(media); + + switch (exynos_power->om_stat & OM_STAT_MASK) { + case OM_STAT_SDMMC: + return initialize_exynos_sdmmc_cbfs_media(media); + case OM_STAT_SPI: + return initialize_exynos_spi_cbfs_media(media, + (void*)CONFIG_CBFS_CACHE_ADDRESS, + CONFIG_CBFS_CACHE_SIZE); + default: + printk(BIOS_EMERG, "Exynos OM_STAT value 0x%x not supported!\n", + exynos_power->om_stat); + return 0; + } +} diff --git a/src/soc/samsung/exynos5420/alternate_cbfs.h b/src/soc/samsung/exynos5420/alternate_cbfs.h new file mode 100644 index 0000000000..af7751d4ee --- /dev/null +++ b/src/soc/samsung/exynos5420/alternate_cbfs.h @@ -0,0 +1,51 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2013 Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef CPU_SAMSUNG_EXYNOS5420_ALTERNATE_CBFS_H +#define CPU_SAMSUNG_EXYNOS5420_ALTERNATE_CBFS_H + +/* These are pointers to function pointers. Double indirection! */ +static void * * const irom_sdmmc_read_blocks_ptr = (void * *)0x02020030; +static void * * const irom_msh_read_from_fifo_emmc_ptr = (void * *)0x02020044; +static void * * const irom_msh_end_boot_op_emmc_ptr = (void * *)0x02020048; +static void * * const irom_spi_sf_read_ptr = (void * *)0x02020058; +static void * * const irom_load_image_from_usb_ptr = (void * *)0x02020070; + +#define SECONDARY_BASE_BOOT_USB 0xfeed0002 +static u32 * const iram_secondary_base = (u32 *)0x02020018; + +/* Values pulled from U-Boot, I think the manual is wrong here (for SPI) */ +#define OM_STAT_SDMMC 0x4 +#define OM_STAT_EMMC 0x8 +#define OM_STAT_SPI 0x14 +#define OM_STAT_MASK 0x7f + +#if defined(__BOOT_BLOCK__) + /* A small space in IRAM to hold the romstage-only image */ + static void * const alternate_cbfs_buffer = + (void *)CONFIG_CBFS_CACHE_ADDRESS; + static size_t const alternate_cbfs_size = CONFIG_CBFS_CACHE_SIZE; +#else + /* Just put this anywhere in RAM that's far enough from anything else */ + /* TODO: Find a better way to "reserve" this region? */ + static void * const alternate_cbfs_buffer = (void *)0x77400000; + static size_t const alternate_cbfs_size = 0xc00000; +#endif + +#endif /* CPU_SAMSUNG_EXYNOS5420_ALTERNATE_CBFS_H */ diff --git a/src/soc/samsung/exynos5420/bootblock.c b/src/soc/samsung/exynos5420/bootblock.c new file mode 100644 index 0000000000..5d2d2b73ca --- /dev/null +++ b/src/soc/samsung/exynos5420/bootblock.c @@ -0,0 +1,55 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2013 Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <bootblock_common.h> +#include <arch/cache.h> + +#include "clk.h" +#include "wakeup.h" +#include "cpu.h" + +/* convenient shorthand (in MB) */ +#define SRAM_START (EXYNOS5_SRAM_BASE >> 20) +#define SRAM_SIZE 1 +#define SRAM_END (SRAM_START + SRAM_SIZE) /* plus one... */ + +void bootblock_cpu_init(void) +{ + /* kick off the multi-core timer. + * We want to do this as early as we can. + */ + mct_start(); + + if (get_wakeup_state() == WAKEUP_DIRECT) { + wakeup(); + /* Never returns. */ + } + + /* set up dcache and MMU */ + mmu_init(); + mmu_disable_range(0, SRAM_START); + mmu_config_range(SRAM_START, SRAM_SIZE, DCACHE_WRITEBACK); + mmu_config_range(SRAM_END, 4096 - SRAM_END, DCACHE_OFF); + dcache_mmu_enable(); + + /* For most ARM systems, we have to initialize firmware media source + * (ex, SPI, SD/MMC, or eMMC) now; but for Exynos platform, that is + * already handled by iROM so there's no need to setup again. + */ +} diff --git a/src/soc/samsung/exynos5420/cbmem.c b/src/soc/samsung/exynos5420/cbmem.c new file mode 100644 index 0000000000..465032013b --- /dev/null +++ b/src/soc/samsung/exynos5420/cbmem.c @@ -0,0 +1,27 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2013 Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <stddef.h> +#include <cbmem.h> +#include "cpu.h" + +void *cbmem_top(void) +{ + return (void *)(get_fb_base_kb() * KiB); +} diff --git a/src/soc/samsung/exynos5420/chip.h b/src/soc/samsung/exynos5420/chip.h new file mode 100644 index 0000000000..65bcacb1fd --- /dev/null +++ b/src/soc/samsung/exynos5420/chip.h @@ -0,0 +1,45 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2013 Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef CPU_SAMSUNG_EXYNOS5420_H +#define CPU_SAMSUNG_EXYNOS5420_H + +#include "gpio.h" + +struct soc_samsung_exynos5420_config { + /* special magic numbers! */ + int clkval_f; + int upper_margin; + int lower_margin; + int vsync; + int left_margin; + int right_margin; + int hsync; + + int xres; + int yres; + int framebuffer_bits_per_pixel; + + int usb_vbus_gpio; + int usb_hsic_gpio; + + u32 lcdbase; +}; + +#endif /* CPU_SAMSUNG_EXYNOS5420_H */ diff --git a/src/soc/samsung/exynos5420/clk.h b/src/soc/samsung/exynos5420/clk.h new file mode 100644 index 0000000000..83d4ad6320 --- /dev/null +++ b/src/soc/samsung/exynos5420/clk.h @@ -0,0 +1,764 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2013 Google Inc. + * Copyright (C) 2012 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef CPU_SAMSUNG_EXYNOS5420_CLK_H +#define CPU_SAMSUNG_EXYNOS5420_CLK_H + +#include <stdint.h> + +#include "cpu.h" + +enum periph_id; + +/* This master list of PLLs is ordered arbitrarily. */ +#define APLL 0 +#define MPLL 1 +#define EPLL 2 +#define HPLL 3 +#define VPLL 4 +#define BPLL 5 +#define RPLL 6 +#define SPLL 7 +#define CPLL 8 +#define DPLL 9 +#define IPLL 10 + +unsigned long get_pll_clk(int pllreg); +unsigned long get_arm_clk(void); +unsigned long get_pwm_clk(void); +unsigned long get_uart_clk(int dev_index); +void set_mmc_clk(int dev_index, unsigned int div); + +/** + * get the clk frequency of the required peripherial + * + * @param peripherial Peripherial id + * + * @return frequency of the peripherial clk + */ +unsigned long clock_get_periph_rate(enum periph_id peripheral); + +#include "pinmux.h" + + +#define MCT_HZ 24000000 + +/* + * Set mshci controller instances clock drivder + * + * @param enum periph_id instance of the mshci controller + * + * Return 0 if ok else -1 + */ +int clock_set_mshci(enum periph_id peripheral); + +/* + * Set dwmci controller instances clock drivder + * + * @param enum periph_id instance of the dwmci controller + * + * Return 0 if ok else -1 + */ +int clock_set_dwmci(enum periph_id peripheral); + +/* + * Sets the epll clockrate + * + * @param rate Required clock rate to the presacaler in Hz + * + * Return 0 if ok else -1 + */ +int clock_epll_set_rate(unsigned long rate); + +/* + * selects the clk source for I2S MCLK + */ +void clock_select_i2s_clk_source(void); + +/* + * Set prescaler division based on input and output frequency + * for i2s audio clock + * + * @param src_frq Source frequency in Hz + * @param dst_frq Required MCLK frequency in Hz + * + * Return 0 if ok else -1 + */ +int clock_set_i2s_clk_prescaler(unsigned int src_frq, unsigned int dst_frq); + +struct exynos5420_clock { + uint32_t apll_lock; /* 0x10010000 */ + uint8_t res1[0xfc]; + uint32_t apll_con0; + uint32_t apll_con1; + uint8_t res2[0xf8]; + uint32_t clk_src_cpu; + uint8_t res3[0x1fc]; + uint32_t clk_mux_stat_cpu; + uint8_t res4[0xfc]; + uint32_t clk_div_cpu0; /* 0x10010500 */ + uint32_t clk_div_cpu1; + uint8_t res5[0xf8]; + uint32_t clk_div_stat_cpu0; + uint32_t clk_div_stat_cpu1; + uint8_t res6[0xf8]; + uint32_t clk_gate_bus_cpu; + uint8_t res7[0xfc]; + uint32_t clk_gate_sclk_cpu; + uint8_t res8[0x1fc]; + uint32_t clkout_cmu_cpu; /* 0x10010a00 */ + uint32_t clkout_cmu_cpu_div_stat; + uint8_t res9[0x5f8]; + uint32_t armclk_stopctrl; + uint8_t res10[0x4]; + uint32_t arm_ema_ctrl; + uint32_t arm_ema_status; + uint8_t res11[0x10]; + uint32_t pwr_ctrl; + uint32_t pwr_ctrl2; + uint8_t res12[0xd8]; + uint32_t apll_con0_l8; /* 0x1001100 */ + uint32_t apll_con0_l7; + uint32_t apll_con0_l6; + uint32_t apll_con0_l5; + uint32_t apll_con0_l4; + uint32_t apll_con0_l3; + uint32_t apll_con0_l2; + uint32_t apll_con0_l1; + uint32_t iem_control; + uint8_t res13[0xdc]; + uint32_t apll_con1_l8; /* 0x10011200 */ + uint32_t apll_con1_l7; + uint32_t apll_con1_l6; + uint32_t apll_con1_l5; + uint32_t apll_con1_l4; + uint32_t apll_con1_l3; + uint32_t apll_con1_l2; + uint32_t apll_con1_l1; + uint8_t res14[0xe0]; + uint32_t clkdiv_iem_l8; + uint32_t clkdiv_iem_l7; /* 0x10011304 */ + uint32_t clkdiv_iem_l6; + uint32_t clkdiv_iem_l5; + uint32_t clkdiv_iem_l4; + uint32_t clkdiv_iem_l3; + uint32_t clkdiv_iem_l2; + uint32_t clkdiv_iem_l1; + uint8_t res15[0xe0]; + uint32_t l2_status; + uint8_t res16[0x0c]; + uint32_t cpu_status; /* 0x10011410 */ + uint8_t res17[0x0c]; + uint32_t ptm_status; + uint8_t res18[0xbdc]; + uint32_t cmu_cpu_spare0; + uint32_t cmu_cpu_spare1; + uint32_t cmu_cpu_spare2; + uint32_t cmu_cpu_spare3; + uint32_t cmu_cpu_spare4; + uint8_t res19[0x1fdc]; + uint32_t cmu_cpu_version; + uint8_t res20[0x20c]; + uint32_t clk_src_cperi0; /* 0x10014200 */ + uint32_t clk_src_cperi1; + uint8_t res21[0xf8]; + uint32_t clk_src_mask_cperi; + uint8_t res22[0x100]; + uint32_t clk_mux_stat_cperi1; + uint8_t res23[0xfc]; + uint32_t clk_div_cperi1; + uint8_t res24[0xfc]; + uint32_t clk_div_stat_cperi1; + uint8_t res25[0xf8]; + uint32_t clk_gate_bus_cperi0; /* 0x10014700 */ + uint32_t clk_gate_bus_cperi1; + uint8_t res26[0xf8]; + uint32_t clk_gate_sclk_cperi; + uint8_t res27[0xfc]; + uint32_t clk_gate_ip_cperi; + uint8_t res28[0xfc]; + uint32_t clkout_cmu_cperi; + uint32_t clkout_cmu_cperi_div_stat; + uint8_t res29[0x5f8]; + uint32_t dcgidx_map0; /* 0x10015000 */ + uint32_t dcgidx_map1; + uint32_t dcgidx_map2; + uint8_t res30[0x14]; + uint32_t dcgperf_map0; + uint32_t dcgperf_map1; + uint8_t res31[0x18]; + uint32_t dvcidx_map; + uint8_t res32[0x1c]; + uint32_t freq_cpu; + uint32_t freq_dpm; + uint8_t res33[0x18]; + uint32_t dvsemclk_en; /* 0x10015080 */ + uint32_t maxperf; + uint8_t res34[0x2e78]; + uint32_t cmu_cperi_spare0; + uint32_t cmu_cperi_spare1; + uint32_t cmu_cperi_spare2; + uint32_t cmu_cperi_spare3; + uint32_t cmu_cperi_spare4; + uint32_t cmu_cperi_spare5; + uint32_t cmu_cperi_spare6; + uint32_t cmu_cperi_spare7; + uint32_t cmu_cperi_spare8; + uint8_t res35[0xcc]; + uint32_t cmu_cperi_version; /* 0x10017ff0 */ + uint8_t res36[0x50c]; + uint32_t clk_div_g2d; + uint8_t res37[0xfc]; + uint32_t clk_div_stat_g2d; + uint8_t res38[0xfc]; + uint32_t clk_gate_bus_g2d; + uint8_t res39[0xfc]; + uint32_t clk_gate_ip_g2d; + uint8_t res40[0x1fc]; + uint32_t clkout_cmu_g2d; + uint32_t clkout_cmu_g2d_div_stat;/* 0x10018a04 */ + uint8_t res41[0xf8]; + uint32_t cmu_g2d_spare0; + uint32_t cmu_g2d_spare1; + uint32_t cmu_g2d_spare2; + uint32_t cmu_g2d_spare3; + uint32_t cmu_g2d_spare4; + uint8_t res42[0x34dc]; + uint32_t cmu_g2d_version; + uint8_t res43[0x30c]; + uint32_t clk_div_cmu_isp0; + uint32_t clk_div_cmu_isp1; + uint32_t clk_div_isp2; /* 0x1001c308 */ + uint8_t res44[0xf4]; + uint32_t clk_div_stat_cmu_isp0; + uint32_t clk_div_stat_cmu_isp1; + uint32_t clk_div_stat_isp2; + uint8_t res45[0x2f4]; + uint32_t clk_gate_bus_isp0; + uint32_t clk_gate_bus_isp1; + uint32_t clk_gate_bus_isp2; + uint32_t clk_gate_bus_isp3; + uint8_t res46[0xf0]; + uint32_t clk_gate_ip_isp0; + uint32_t clk_gate_ip_isp1; + uint8_t res47[0xf8]; + uint32_t clk_gate_sclk_isp; + uint8_t res48[0x0c]; + uint32_t mcuisp_pwr_ctrl; /* 0x1001c910 */ + uint8_t res49[0x0ec]; + uint32_t clkout_cmu_isp; + uint32_t clkout_cmu_isp_div_stat; + uint8_t res50[0xf8]; + uint32_t cmu_isp_spare0; + uint32_t cmu_isp_spare1; + uint32_t cmu_isp_spare2; + uint32_t cmu_isp_spare3; + uint8_t res51[0x34e0]; + uint32_t cmu_isp_version; + uint8_t res52[0x2c]; + uint32_t cpll_lock; /* 10020020 */ + uint8_t res53[0xc]; + uint32_t dpll_lock; + uint8_t res54[0xc]; + uint32_t epll_lock; + uint8_t res55[0xc]; + uint32_t rpll_lock; + uint8_t res56[0xc]; + uint32_t ipll_lock; + uint8_t res57[0xc]; + uint32_t spll_lock; + uint8_t res58[0xc]; + uint32_t vpll_lock; + uint8_t res59[0xc]; + uint32_t mpll_lock; + uint8_t res60[0x8c]; + uint32_t cpll_con0; /* 10020120 */ + uint32_t cpll_con1; + uint32_t dpll_con0; + uint32_t dpll_con1; + uint32_t epll_con0; + uint32_t epll_con1; + uint32_t epll_con2; + uint8_t res601[0x4]; + uint32_t rpll_con0; + uint32_t rpll_con1; + uint32_t rpll_con2; + uint8_t res602[0x4]; + uint32_t ipll_con0; + uint32_t ipll_con1; + uint8_t res61[0x8]; + uint32_t spll_con0; + uint32_t spll_con1; + uint8_t res62[0x8]; + uint32_t vpll_con0; + uint32_t vpll_con1; + uint8_t res63[0x8]; + uint32_t mpll_con0; + uint32_t mpll_con1; + uint8_t res64[0x78]; + uint32_t clk_src_top0; /* 0x10020200 */ + uint32_t clk_src_top1; + uint32_t clk_src_top2; + uint32_t clk_src_top3; + uint32_t clk_src_top4; + uint32_t clk_src_top5; + uint32_t clk_src_top6; + uint32_t clk_src_top7; + uint8_t res65[0xc]; + uint32_t clk_src_disp10; /* 0x1002022c */ + uint8_t res66[0x10]; + uint32_t clk_src_mau; + uint32_t clk_src_fsys; + uint8_t res67[0x8]; + uint32_t clk_src_peric0; + uint32_t clk_src_peric1; + uint8_t res68[0x18]; + uint32_t clk_src_isp; + uint8_t res69[0x0c]; + uint32_t clk_src_top10; + uint32_t clk_src_top11; + uint32_t clk_src_top12; + uint8_t res70[0x74]; + uint32_t clk_src_mask_top0; + uint32_t clk_src_mask_top1; + uint32_t clk_src_mask_top2; + uint8_t res71[0x10]; + uint32_t clk_src_mask_top7; + uint8_t res72[0xc]; + uint32_t clk_src_mask_disp10; /* 0x1002032c */ + uint8_t res73[0x4]; + uint32_t clk_src_mask_mau; + uint8_t res74[0x8]; + uint32_t clk_src_mask_fsys; + uint8_t res75[0xc]; + uint32_t clk_src_mask_peric0; + uint32_t clk_src_mask_peric1; + uint8_t res76[0x18]; + uint32_t clk_src_mask_isp; + uint8_t res77[0x8c]; + uint32_t clk_mux_stat_top0; /* 0x10020400 */ + uint32_t clk_mux_stat_top1; + uint32_t clk_mux_stat_top2; + uint32_t clk_mux_stat_top3; + uint32_t clk_mux_stat_top4; + uint32_t clk_mux_stat_top5; + uint32_t clk_mux_stat_top6; + uint32_t clk_mux_stat_top7; + uint8_t res78[0x60]; + uint32_t clk_mux_stat_top10; + uint32_t clk_mux_stat_top11; + uint32_t clk_mux_stat_top12; + uint8_t res79[0x74]; + uint32_t clk_div_top0; /* 0x10020500 */ + uint32_t clk_div_top1; + uint32_t clk_div_top2; + uint8_t res80[0x20]; + uint32_t clk_div_disp10; + uint8_t res81[0x14]; + uint32_t clk_div_mau; + uint32_t clk_div_fsys0; + uint32_t clk_div_fsys1; + uint32_t clk_div_fsys2; + uint8_t res82[0x4]; + uint32_t clk_div_peric0; + uint32_t clk_div_peric1; + uint32_t clk_div_peric2; + uint32_t clk_div_peric3; + uint32_t clk_div_peric4; /* 0x10020568 */ + uint8_t res83[0x14]; + uint32_t clk_div_isp0; + uint32_t clk_div_isp1; + uint8_t res84[0x8]; + uint32_t clkdiv2_ratio; + uint8_t res850[0xc]; + uint32_t clkdiv4_ratio; + uint8_t res85[0x5c]; + uint32_t clk_div_stat_top0; + uint32_t clk_div_stat_top1; + uint32_t clk_div_stat_top2; + uint8_t res86[0x20]; + uint32_t clk_div_stat_disp10; + uint8_t res87[0x14]; + uint32_t clk_div_stat_mau; /* 0x10020644 */ + uint32_t clk_div_stat_fsys0; + uint32_t clk_div_stat_fsys1; + uint32_t clk_div_stat_fsys2; + uint8_t res88[0x4]; + uint32_t clk_div_stat_peric0; + uint32_t clk_div_stat_peric1; + uint32_t clk_div_stat_peric2; + uint32_t clk_div_stat_peric3; + uint32_t clk_div_stat_peric4; + uint8_t res89[0x14]; + uint32_t clk_div_stat_isp0; + uint32_t clk_div_stat_isp1; + uint8_t res90[0x8]; + uint32_t clkdiv2_stat0; + uint8_t res91[0xc]; + uint32_t clkdiv4_stat; + uint8_t res92[0x5c]; + uint32_t clk_gate_bus_top; /* 0x10020700 */ + uint8_t res93[0xc]; + uint32_t clk_gate_bus_gscl0; + uint8_t res94[0xc]; + uint32_t clk_gate_bus_gscl1; + uint8_t res95[0x4]; + uint32_t clk_gate_bus_disp1; + uint8_t res96[0x4]; + uint32_t clk_gate_bus_wcore; + uint32_t clk_gate_bus_mfc; + uint32_t clk_gate_bus_g3d; + uint32_t clk_gate_bus_gen; + uint32_t clk_gate_bus_fsys0; + uint32_t clk_gate_bus_fsys1; + uint32_t clk_gate_bus_fsys2; + uint32_t clk_gate_bus_mscl; + uint32_t clk_gate_bus_peric; + uint32_t clk_gate_bus_peric1; + uint8_t res97[0x8]; + uint32_t clk_gate_bus_peris0; + uint32_t clk_gate_bus_peris1; /* 0x10020764 */ + uint8_t res98[0x8]; + uint32_t clk_gate_bus_noc; + uint8_t res99[0xac]; + uint32_t clk_gate_top_sclk_gscl; + uint8_t res1000[0x4]; + uint32_t clk_gate_top_sclk_disp1; + uint8_t res100[0x10]; + uint32_t clk_gate_top_sclk_mau; + uint32_t clk_gate_top_sclk_fsys; + uint8_t res101[0xc]; + uint32_t clk_gate_top_sclk_peric; + uint8_t res102[0xc]; + uint32_t clk_gate_top_sclk_cperi; + uint8_t res103[0xc]; + uint32_t clk_gate_top_sclk_isp; + uint8_t res104[0x9c]; + uint32_t clk_gate_ip_gscl0; + uint8_t res105[0xc]; + uint32_t clk_gate_ip_gscl1; + uint8_t res106[0x4]; + uint32_t clk_gate_ip_disp1; + uint32_t clk_gate_ip_mfc; + uint32_t clk_gate_ip_g3d; + uint32_t clk_gate_ip_gen; /* 0x10020934 */ + uint8_t res107[0xc]; + uint32_t clk_gate_ip_fsys; + uint8_t res108[0x8]; + uint32_t clk_gate_ip_peric; + uint8_t res109[0xc]; + uint32_t clk_gate_ip_peris; + uint8_t res110[0xc]; + uint32_t clk_gate_ip_mscl; + uint8_t res111[0xc]; + uint32_t clk_gate_ip_block; + uint8_t res112[0xc]; + uint32_t bypass; + uint8_t res113[0x6c]; + uint32_t clkout_cmu_top; + uint32_t clkout_cmu_top_div_stat; + uint8_t res114[0xf8]; + uint32_t clkout_top_spare0; + uint32_t clkout_top_spare1; + uint32_t clkout_top_spare2; + uint32_t clkout_top_spare3; + uint8_t res115[0x34e0]; + uint32_t clkout_top_version; + uint8_t res116[0xc01c]; + uint32_t bpll_lock; /* 0x10030010 */ + uint8_t res117[0xfc]; + uint32_t bpll_con0; + uint32_t bpll_con1; + uint8_t res118[0xe8]; + uint32_t clk_src_cdrex; + uint8_t res119[0x1fc]; + uint32_t clk_mux_stat_cdrex; + uint8_t res120[0xfc]; + uint32_t clk_div_cdrex0; + uint32_t clk_div_cdrex1; + uint8_t res121[0xf8]; + uint32_t clk_div_stat_cdrex; + uint8_t res1211[0xfc]; + uint32_t clk_gate_bus_cdrex; + uint32_t clk_gate_bus_cdrex1; + uint8_t res122[0x1f8]; + uint32_t clk_gate_ip_cdrex; + uint8_t res123[0x10]; + uint32_t dmc_freq_ctrl; /* 0x10030914 */ + uint8_t res124[0x4]; + uint32_t pause; + uint32_t ddrphy_lock_ctrl; + uint8_t res125[0xdc]; + uint32_t clkout_cmu_cdrex; + uint32_t clkout_cmu_cdrex_div_stat; + uint8_t res126[0x8]; + uint32_t lpddr3phy_ctrl; + uint32_t lpddr3phy_con0; + uint32_t lpddr3phy_con1; + uint32_t lpddr3phy_con2; + uint32_t lpddr3phy_con3; + uint32_t lpddr3phy_con4; + uint32_t lpddr3phy_con5; /* 0x10030a28 */ + uint32_t pll_div2_sel; + uint8_t res127[0xd0]; + uint32_t cmu_cdrex_spare0; + uint32_t cmu_cdrex_spare1; + uint32_t cmu_cdrex_spare2; + uint32_t cmu_cdrex_spare3; + uint32_t cmu_cdrex_spare4; + uint8_t res128[0x34dc]; + uint32_t cmu_cdrex_version; /* 0x10033ff0 */ + uint8_t res129[0x400c]; + uint32_t kpll_lock; + uint8_t res130[0xfc]; + uint32_t kpll_con0; + uint32_t kpll_con1; + uint8_t res131[0xf8]; + uint32_t clk_src_kfc; + uint8_t res132[0x1fc]; + uint32_t clk_mux_stat_kfc; /* 0x10038400 */ + uint8_t res133[0xfc]; + uint32_t clk_div_kfc0; + uint8_t res134[0xfc]; + uint32_t clk_div_stat_kfc0; + uint8_t res135[0xfc]; + uint32_t clk_gate_bus_cpu_kfc; + uint8_t res136[0xfc]; + uint32_t clk_gate_sclk_cpu_kfc; + uint8_t res137[0x1fc]; + uint32_t clkout_cmu_kfc; + uint32_t clkout_cmu_kfc_div_stat;/* 0x10038a04 */ + uint8_t res138[0x5f8]; + uint32_t armclk_stopctrl_kfc; + uint8_t res139[0x4]; + uint32_t armclk_ema_ctrl_kfc; + uint32_t armclk_ema_status_kfc; + uint8_t res140[0x10]; + uint32_t pwr_ctrl_kfc; + uint32_t pwr_ctrl2_kfc; + uint8_t res141[0xd8]; + uint32_t kpll_con0_l8; + uint32_t kpll_con0_l7; + uint32_t kpll_con0_l6; + uint32_t kpll_con0_l5; + uint32_t kpll_con0_l4; + uint32_t kpll_con0_l3; + uint32_t kpll_con0_l2; + uint32_t kpll_con0_l1; + uint32_t iem_control_kfc; /* 0x10039120 */ + uint8_t res142[0xdc]; + uint32_t kpll_con1_l8; + uint32_t kpll_con1_l7; + uint32_t kpll_con1_l6; + uint32_t kpll_con1_l5; + uint32_t kpll_con1_l4; + uint32_t kpll_con1_l3; + uint32_t kpll_con1_l2; + uint32_t kpll_con1_l1; + uint8_t res143[0xe0]; + uint32_t clkdiv_iem_l8_kfc; /* 0x10039300 */ + uint32_t clkdiv_iem_l7_kfc; + uint32_t clkdiv_iem_l6_kfc; + uint32_t clkdiv_iem_l5_kfc; + uint32_t clkdiv_iem_l4_kfc; + uint32_t clkdiv_iem_l3_kfc; + uint32_t clkdiv_iem_l2_kfc; + uint32_t clkdiv_iem_l1_kfc; + uint8_t res144[0xe0]; + uint32_t l2_status_kfc; + uint8_t res145[0xc]; + uint32_t cpu_status_kfc; /* 0x10039410 */ + uint8_t res146[0xc]; + uint32_t ptm_status_kfc; + uint8_t res147[0xbdc]; + uint32_t cmu_kfc_spare0; + uint32_t cmu_kfc_spare1; + uint32_t cmu_kfc_spare2; + uint32_t cmu_kfc_spare3; + uint32_t cmu_kfc_spare4; + uint8_t res148[0x1fdc]; + uint32_t cmu_kfc_version; /* 0x1003bff0 */ +}; + +static struct exynos5420_clock * const exynos_clock = + (void *)EXYNOS5_CLOCK_BASE; + +struct exynos5_mct { + uint32_t mct_cfg; + uint8_t reserved0[0xfc]; + uint32_t g_cnt_l; + uint32_t g_cnt_u; + uint8_t reserved1[0x8]; + uint32_t g_cnt_wstat; + uint8_t reserved2[0xec]; + uint32_t g_comp0_l; + uint32_t g_comp0_u; + uint32_t g_comp0_addr_incr; + uint8_t reserved3[0x4]; + uint32_t g_comp1_l; + uint32_t g_comp1_u; + uint32_t g_comp1_addr_incr; + uint8_t reserved4[0x4]; + uint32_t g_comp2_l; + uint32_t g_comp2_u; + uint32_t g_comp2_addr_incr; + uint8_t reserved5[0x4]; + uint32_t g_comp3_l; + uint32_t g_comp3_u; + uint32_t g_comp3_addr_incr; + uint8_t reserved6[0x4]; + uint32_t g_tcon; + uint32_t g_int_cstat; + uint32_t g_int_enb; + uint32_t g_wstat; + uint8_t reserved7[0xb0]; + uint32_t l0_tcntb; + uint32_t l0_tcnto; + uint32_t l0_icntb; + uint32_t l0_icnto; + uint32_t l0_frcntb; + uint32_t l0_frcnto; + uint8_t reserved8[0x8]; + uint32_t l0_tcon; + uint8_t reserved9[0xc]; + uint32_t l0_int_cstat; + uint32_t l0_int_enb; + uint8_t reserved10[0x8]; + uint32_t l0_wstat; + uint8_t reserved11[0xbc]; + uint32_t l1_tcntb; + uint32_t l1_tcnto; + uint32_t l1_icntb; + uint32_t l1_icnto; + uint32_t l1_frcntb; + uint32_t l1_frcnto; + uint8_t reserved12[0x8]; + uint32_t l1_tcon; + uint8_t reserved13[0xc]; + uint32_t l1_int_cstat; + uint32_t l1_int_enb; + uint8_t reserved14[0x8]; + uint32_t l1_wstat; +}; + +static struct exynos5_mct * const exynos_mct = + (void *)EXYNOS5_MULTI_CORE_TIMER_BASE; + +#define EXYNOS5_EPLLCON0_LOCKED_SHIFT 29 /* EPLL Locked bit position*/ +#define EPLL_SRC_CLOCK 24000000 /*24 MHz Cristal Input */ +#define TIMEOUT_EPLL_LOCK 1000 + +#define AUDIO_0_RATIO_MASK 0x0f +#define AUDIO_1_RATIO_MASK 0x0f + +#define CLK_SRC_PERIC1 0x254 +#define AUDIO1_SEL_MASK 0xf +#define CLK_SRC_AUDIOCDCLK1 0x0 +#define CLK_SRC_XXTI 0x1 +#define CLK_SRC_SCLK_EPLL 0x7 + +/* CON0 bit-fields */ +#define EPLL_CON0_MDIV_MASK 0x1ff +#define EPLL_CON0_PDIV_MASK 0x3f +#define EPLL_CON0_SDIV_MASK 0x7 +#define EPLL_CON0_LOCKED_SHIFT 29 +#define EPLL_CON0_MDIV_SHIFT 16 +#define EPLL_CON0_PDIV_SHIFT 8 +#define EPLL_CON0_SDIV_SHIFT 0 +#define EPLL_CON0_LOCK_DET_EN_SHIFT 28 +#define EPLL_CON0_LOCK_DET_EN_MASK 1 + +/* structure for epll configuration used in audio clock configuration */ +struct st_epll_con_val { + unsigned int freq_out; /* frequency out */ + unsigned int en_lock_det; /* enable lock detect */ + unsigned int m_div; /* m divider value */ + unsigned int p_div; /* p divider value */ + unsigned int s_div; /* s divider value */ + unsigned int k_dsm; /* k value of delta signal modulator */ +}; + +/** + * Low-level function to set the clock pre-ratio for a peripheral + * + * @param periph_id Peripheral ID of peripheral to change + * @param divisor New divisor for this peripheral's clock + */ +void clock_ll_set_pre_ratio(enum periph_id periph_id, unsigned divisor); + +/** + * Low-level function to set the clock ratio for a peripheral + * + * @param periph_id Peripheral ID of peripheral to change + * @param divisor New divisor for this peripheral's clock + */ +void clock_ll_set_ratio(enum periph_id periph_id, unsigned divisor); + +/** + * Low-level function that selects the best clock scalars for a given rate and + * sets up the given peripheral's clock accordingly. + * + * @param periph_id Peripheral ID of peripheral to change + * @param rate Desired clock rate in Hz + * + * @return zero on success, negative on error + */ +int clock_set_rate(enum periph_id periph_id, unsigned int rate); + +/* Clock gate unused IP */ +void clock_gate(void); + +void mct_start(void); +uint64_t mct_raw_value(void); + +#include "dmc.h" + +/* These are the ratio's for configuring ARM clock */ +struct arm_clk_ratios { + unsigned int arm_freq_mhz; /* Frequency of ARM core in MHz */ + + unsigned int apll_mdiv; + unsigned int apll_pdiv; + unsigned int apll_sdiv; + + unsigned int arm2_ratio; + unsigned int apll_ratio; + unsigned int pclk_dbg_ratio; + unsigned int atb_ratio; + unsigned int periph_ratio; + unsigned int acp_ratio; + unsigned int cpud_ratio; + unsigned int arm_ratio; +}; + +/** + * Get the clock ratios for CPU configuration + * + * @return pointer to the clock ratios that we should use + */ +struct arm_clk_ratios *get_arm_clk_ratios(void); + +/* + * Initialize clock for the device + */ +struct mem_timings; +void system_clock_init(void); + +#endif diff --git a/src/soc/samsung/exynos5420/clock.c b/src/soc/samsung/exynos5420/clock.c new file mode 100644 index 0000000000..7043310a43 --- /dev/null +++ b/src/soc/samsung/exynos5420/clock.c @@ -0,0 +1,639 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2010 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <assert.h> +#include <stdlib.h> +#include <timer.h> +#include <arch/io.h> +#include <console/console.h> +#include "clk.h" +#include "periph.h" + +/* input clock of PLL: SMDK5420 has 24MHz input clock */ +#define CONFIG_SYS_CLK_FREQ 24000000 + +/* 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) +{ + unsigned long r, m, p, s, k = 0, mask, fout; + unsigned int freq; + + switch (pllreg) { + case APLL: + r = readl(&exynos_clock->apll_con0); + break; + case MPLL: + r = readl(&exynos_clock->mpll_con0); + break; + case EPLL: + r = readl(&exynos_clock->epll_con0); + k = readl(&exynos_clock->epll_con1); + break; + case VPLL: + r = readl(&exynos_clock->vpll_con0); + k = readl(&exynos_clock->vpll_con1); + break; + case BPLL: + r = readl(&exynos_clock->bpll_con0); + break; + case RPLL: + r = readl(&exynos_clock->rpll_con0); + k = readl(&exynos_clock->rpll_con1); + break; + case SPLL: + r = readl(&exynos_clock->spll_con0); + break; + case CPLL: + r = readl(&exynos_clock->cpll_con0); + break; + case DPLL: + r = readl(&exynos_clock->dpll_con0); + 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 || + pllreg == SPLL) + 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 || pllreg == RPLL) { + 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; +} + +enum peripheral_clock_select { + PERIPH_SRC_CPLL = 1, + PERIPH_SRC_DPLL = 2, + PERIPH_SRC_MPLL = 3, + PERIPH_SRC_SPLL = 4, + PERIPH_SRC_IPLL = 5, + PERIPH_SRC_EPLL = 6, + PERIPH_SRC_RPLL = 7, +}; + +static int clock_select_to_pll(enum peripheral_clock_select sel) +{ + int pll; + + switch (sel) { + case PERIPH_SRC_CPLL: + pll = CPLL; + break; + case PERIPH_SRC_DPLL: + pll = DPLL; + break; + case PERIPH_SRC_MPLL: + pll = MPLL; + break; + case PERIPH_SRC_SPLL: + pll = SPLL; + break; + case PERIPH_SRC_IPLL: + pll = IPLL; + break; + case PERIPH_SRC_EPLL: + pll = EPLL; + break; + case PERIPH_SRC_RPLL: + pll = RPLL; + break; + default: + pll = -1; + break; + } + + return pll; +} + +unsigned long clock_get_periph_rate(enum periph_id peripheral) +{ + unsigned long sclk; + unsigned int src, div; + + switch (peripheral) { + case PERIPH_ID_UART0: + src = (readl(&exynos_clock->clk_src_peric0) >> 4) & 0x7; + div = (readl(&exynos_clock->clk_div_peric0) >> 8) & 0xf; + break; + case PERIPH_ID_UART1: + src = (readl(&exynos_clock->clk_src_peric0) >> 8) & 0x7; + div = (readl(&exynos_clock->clk_div_peric0) >> 12) & 0xf; + break; + case PERIPH_ID_UART2: + src = (readl(&exynos_clock->clk_src_peric0) >> 12) & 0x7; + div = (readl(&exynos_clock->clk_div_peric0) >> 16) & 0xf; + break; + case PERIPH_ID_UART3: + src = (readl(&exynos_clock->clk_src_peric0) >> 16) & 0x7; + div = (readl(&exynos_clock->clk_div_peric0) >> 20) & 0xf; + break; + case PERIPH_ID_PWM0: + case PERIPH_ID_PWM1: + case PERIPH_ID_PWM2: + case PERIPH_ID_PWM3: + case PERIPH_ID_PWM4: + src = (readl(&exynos_clock->clk_src_peric0) >> 24) & 0x7; + div = (readl(&exynos_clock->clk_div_peric0) >> 28) & 0x7; + break; + case PERIPH_ID_SPI0: + src = (readl(&exynos_clock->clk_src_peric1) >> 20) & 0x7; + div = (readl(&exynos_clock->clk_div_peric1) >> 20) & 0xf; + break; + case PERIPH_ID_SPI1: + src = (readl(&exynos_clock->clk_src_peric1) >> 24) & 0x7; + div = (readl(&exynos_clock->clk_div_peric1) >> 24) & 0xf; + break; + case PERIPH_ID_SPI2: + src = (readl(&exynos_clock->clk_src_peric1) >> 28) & 0x7; + div = (readl(&exynos_clock->clk_div_peric1) >> 28) & 0xf; + break; + case PERIPH_ID_SPI3: /* aka SPI0_ISP */ + src = (readl(&exynos_clock->clk_src_isp) >> 16) & 0x7; + div = (readl(&exynos_clock->clk_div_isp0) >> 0) & 0x7; + break; + case PERIPH_ID_SPI4: /* aka SPI1_ISP */ + src = (readl(&exynos_clock->clk_src_isp) >> 12) & 0x7; + div = (readl(&exynos_clock->clk_div_isp1) >> 4) & 0x7; + 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: + case PERIPH_ID_I2C8: + case PERIPH_ID_I2C9: + case PERIPH_ID_I2C10: + /* + * I2C block parent clock selection is different from other + * peripherals, so we handle it all here. + * TODO: Add a helper function like with the peripheral clock + * select fields? + */ + src = (readl(&exynos_clock->clk_src_top1) >> 8) & 0x3; + if (src == 0x0) + src = CPLL; + else if (src == 0x1) + src = DPLL; + else if (src == 0x2) + src = MPLL; + else + return -1; + + sclk = get_pll_clk(src); + div = ((readl(&exynos_clock->clk_div_top1) >> 8) & 0x3f) + 1; + return sclk / div; + default: + printk(BIOS_DEBUG, "%s: invalid peripheral %d", + __func__, peripheral); + return -1; + }; + + src = clock_select_to_pll(src); + if (src < 0) { + printk(BIOS_DEBUG, "%s: cannot determine source PLL", __func__); + return -1; + } + + sclk = get_pll_clk(src); + + return sclk / (div + 1); +} + +/* exynos5: return ARM clock frequency */ +unsigned long get_arm_clk(void) +{ + unsigned long div; + unsigned long armclk; + unsigned int arm_ratio; + unsigned int arm2_ratio; + + div = readl(&exynos_clock->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: get the mmc clock */ +static unsigned long get_mmc_clk(int dev_index) +{ + unsigned long uclk, sclk; + unsigned int sel, ratio; + int shift = 0; + + sel = readl(&exynos_clock->clk_src_fsys); + sel = (sel >> ((dev_index * 4) + 8)) & 0x7; + + if (sel == 0x3) + sclk = get_pll_clk(MPLL); + else if (sel == 0x6) + sclk = get_pll_clk(EPLL); + else + return 0; + + ratio = readl(&exynos_clock->clk_div_fsys1); + + shift = dev_index * 10; + + ratio = (ratio >> shift) & 0x3ff; + uclk = (sclk / (ratio + 1)); + printk(BIOS_DEBUG, "%s(%d): %lu\n", __func__, dev_index, uclk); + + return uclk; +} + +/* exynos5: set the mmc clock */ +void set_mmc_clk(int dev_index, unsigned int div) +{ + void *addr; + unsigned int val, shift; + + addr = &exynos_clock->clk_div_fsys1; + shift = dev_index * 10; + + val = readl(addr); + val &= ~(0x3ff << shift); + val |= (div & 0x3ff) << shift; + writel(val, addr); +} + +/* Set DW MMC Controller clock */ +int clock_set_dwmci(enum periph_id peripheral) +{ + /* Request MMC clock value to 52MHz. */ + const unsigned long freq = 52000000; + unsigned long sdclkin, cclkin; + int device_index = (int)peripheral - (int)PERIPH_ID_SDMMC0; + + ASSERT(device_index >= 0 && device_index < 4); + sdclkin = get_mmc_clk(device_index); + if (!sdclkin) { + return -1; + } + + /* The SDCLKIN is divided insided controller by the DIVRATIO field in + * CLKSEL register, so we must calculate clock value as + * cclk_in = SDCLKIN / (DIVRATIO + 1) + * Currently the RIVRATIO must be 3 for MMC0 and MMC2 on Exynos5420 + * (and must be configured in payload). + */ + if (device_index == 0 || device_index == 2){ + int divratio = 3; + sdclkin /= (divratio + 1); + } + printk(BIOS_DEBUG, "%s(%d): sdclkin: %ld\n", __func__, device_index, sdclkin); + + cclkin = CEIL_DIV(sdclkin, freq); + set_mmc_clk(device_index, cclkin); + return 0; +} + +void clock_ll_set_pre_ratio(enum periph_id periph_id, unsigned divisor) +{ + 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 = &exynos_clock->clk_div_peric4; + shift = 8; + break; + case PERIPH_ID_SPI1: + reg = &exynos_clock->clk_div_peric4; + shift = 16; + break; + case PERIPH_ID_SPI2: + reg = &exynos_clock->clk_div_peric4; + shift = 24; + break; + case PERIPH_ID_SPI3: + reg = &exynos_clock->clk_div_isp1; + shift = 0; + break; + case PERIPH_ID_SPI4: + reg = &exynos_clock->clk_div_isp1; + shift = 8; + break; + default: + printk(BIOS_DEBUG, "%s: Unsupported peripheral ID %d\n", __func__, + periph_id); + return; + } + clrsetbits_le32(reg, mask << shift, (divisor & mask) << shift); +} + +void clock_ll_set_ratio(enum periph_id periph_id, unsigned divisor) +{ + unsigned shift; + unsigned mask = 0xf; + u32 *reg; + + switch (periph_id) { + case PERIPH_ID_SPI0: + reg = &exynos_clock->clk_div_peric1; + shift = 20; + break; + case PERIPH_ID_SPI1: + reg = &exynos_clock->clk_div_peric1; + shift = 24; + break; + case PERIPH_ID_SPI2: + reg = &exynos_clock->clk_div_peric1; + shift = 28; + break; + case PERIPH_ID_SPI3: + reg = &exynos_clock->clk_div_isp1; + shift = 16; + break; + case PERIPH_ID_SPI4: + reg = &exynos_clock->clk_div_isp1; + shift = 20; + break; + default: + printk(BIOS_DEBUG, "%s: Unsupported peripheral ID %d\n", __func__, + periph_id); + return; + } + clrsetbits_le32(reg, mask << shift, (divisor & mask) << shift); +} + +/** + * Linearly searches for the most accurate main and fine stage clock scalars + * (divisors) for a specified target frequency and scalar bit sizes by checking + * all multiples of main_scalar_bits values. Will always return scalars up to or + * slower than target. + * + * @param main_scalar_bits Number of main scalar bits, must be > 0 and < 32 + * @param fine_scalar_bits Number of fine scalar bits, must be > 0 and < 32 + * @param input_freq Clock frequency to be scaled in Hz + * @param target_freq Desired clock frequency in Hz + * @param best_fine_scalar Pointer to store the fine stage divisor + * + * @return best_main_scalar Main scalar for desired frequency or -1 if none + * found + */ +static int clock_calc_best_scalar(unsigned int main_scaler_bits, + unsigned int fine_scalar_bits, unsigned int input_rate, + unsigned int target_rate, unsigned int *best_fine_scalar) +{ + int i; + int best_main_scalar = -1; + unsigned int best_error = target_rate; + const unsigned int cap = (1 << fine_scalar_bits) - 1; + const unsigned int loops = 1 << main_scaler_bits; + + printk(BIOS_DEBUG, "Input Rate is %u, Target is %u, Cap is %u\n", input_rate, + target_rate, cap); + + ASSERT(best_fine_scalar != NULL); + ASSERT(main_scaler_bits <= fine_scalar_bits); + + *best_fine_scalar = 1; + + if (input_rate == 0 || target_rate == 0) + return -1; + + if (target_rate >= input_rate) + return 1; + + for (i = 1; i <= loops; i++) { + const unsigned int effective_div = MAX(MIN(input_rate / i / + target_rate, cap), 1); + const unsigned int effective_rate = input_rate / i / + effective_div; + const int error = target_rate - effective_rate; + + printk(BIOS_DEBUG, "%d|effdiv:%u, effrate:%u, error:%d\n", i, effective_div, + effective_rate, error); + + if (error >= 0 && error <= best_error) { + best_error = error; + best_main_scalar = i; + *best_fine_scalar = effective_div; + } + } + + return best_main_scalar; +} + +int clock_set_rate(enum periph_id periph_id, unsigned int rate) +{ + int main_scalar; + 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_scalar = clock_calc_best_scalar(4, 8, 400000000, rate, &fine); + if (main_scalar < 0) { + printk(BIOS_DEBUG, "%s: Cannot set clock rate for periph %d", + __func__, periph_id); + return -1; + } + clock_ll_set_ratio(periph_id, main_scalar - 1); + clock_ll_set_pre_ratio(periph_id, fine - 1); + break; + default: + printk(BIOS_DEBUG, "%s: Unsupported peripheral ID %d\n", __func__, + periph_id); + return -1; + } + + return 0; +} + +int clock_set_mshci(enum periph_id peripheral) +{ + 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 = &exynos_clock->clk_div_fsys1; + break; + case PERIPH_ID_SDMMC2: + addr = &exynos_clock->clk_div_fsys2; + break; + default: + printk(BIOS_DEBUG, "invalid peripheral\n"); + return -1; + } + tmp = readl(addr) & ~0xff0f; + for (i = 0; i <= 0xf; i++) { + if ((clock / (i + 1)) <= 400) { + writel(tmp | i << 0, addr); + break; + } + } + return 0; +} + +int clock_epll_set_rate(unsigned long rate) +{ + unsigned int epll_con, epll_con_k; + unsigned int i; + unsigned int lockcnt; + struct mono_time current, end; + + epll_con = readl(&exynos_clock->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, &exynos_clock->epll_lock); + writel(epll_con, &exynos_clock->epll_con0); + writel(epll_con_k, &exynos_clock->epll_con1); + + timer_monotonic_get(¤t); + end = current; + mono_time_add_msecs(&end, TIMEOUT_EPLL_LOCK); + + while (!(readl(&exynos_clock->epll_con0) & + (0x1 << EXYNOS5_EPLLCON0_LOCKED_SHIFT))) { + if (mono_time_after(¤t, &end)) { + printk(BIOS_DEBUG, "%s: Timeout waiting for EPLL lock\n", __func__); + return -1; + } + timer_monotonic_get(¤t); + } + + return 0; +} + +void clock_select_i2s_clk_source(void) +{ + clrsetbits_le32(&exynos_clock->clk_src_peric1, AUDIO1_SEL_MASK, + (CLK_SRC_SCLK_EPLL)); +} + +int clock_set_i2s_clk_prescaler(unsigned int src_frq, unsigned int dst_frq) +{ + unsigned int div ; + + if ((dst_frq == 0) || (src_frq == 0)) { + printk(BIOS_DEBUG, "%s: Invalid requency input for prescaler\n", __func__); + printk(BIOS_DEBUG, "src frq = %d des frq = %d ", src_frq, dst_frq); + return -1; + } + + div = (src_frq / dst_frq); + if (div > AUDIO_1_RATIO_MASK) { + printk(BIOS_DEBUG, "%s: Frequency ratio is out of range\n", __func__); + printk(BIOS_DEBUG, "src frq = %d des frq = %d ", src_frq, dst_frq); + return -1; + } + clrsetbits_le32(&exynos_clock->clk_div_peric4, AUDIO_1_RATIO_MASK, + (div & AUDIO_1_RATIO_MASK)); + return 0; +} diff --git a/src/soc/samsung/exynos5420/clock_init.c b/src/soc/samsung/exynos5420/clock_init.c new file mode 100644 index 0000000000..cfac01e307 --- /dev/null +++ b/src/soc/samsung/exynos5420/clock_init.c @@ -0,0 +1,223 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2012 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* Clock setup for SMDK5420 board based on EXYNOS5 */ + +#include <delay.h> +#include <console/console.h> +#include "clk.h" +#include "cpu.h" +#include "dp.h" +#include "dmc.h" +#include "setup.h" + +void system_clock_init(void) +{ + u32 val; + + /* Turn on the MCT as early as possible. */ + exynos_mct->g_tcon |= (1 << 8); + + /* PLL locktime */ + writel(APLL_LOCK_VAL, &exynos_clock->apll_lock); + writel(MPLL_LOCK_VAL, &exynos_clock->mpll_lock); + writel(BPLL_LOCK_VAL, &exynos_clock->bpll_lock); + writel(CPLL_LOCK_VAL, &exynos_clock->cpll_lock); + writel(DPLL_LOCK_VAL, &exynos_clock->dpll_lock); + writel(EPLL_LOCK_VAL, &exynos_clock->epll_lock); + writel(VPLL_LOCK_VAL, &exynos_clock->vpll_lock); + writel(IPLL_LOCK_VAL, &exynos_clock->ipll_lock); + writel(SPLL_LOCK_VAL, &exynos_clock->spll_lock); + writel(KPLL_LOCK_VAL, &exynos_clock->kpll_lock); + writel(RPLL_LOCK_VAL, &exynos_clock->rpll_lock); + + setbits_le32(&exynos_clock->clk_src_cpu, MUX_HPM_SEL_MASK); + + writel(0, &exynos_clock->clk_src_top6); + + writel(0, &exynos_clock->clk_src_cdrex); + writel(SRC_KFC_HPM_SEL, &exynos_clock->clk_src_kfc); + writel(HPM_RATIO, &exynos_clock->clk_div_cpu1); + writel(CLK_DIV_CPU0_VAL, &exynos_clock->clk_div_cpu0); + + /* switch A15 clock source to OSC clock before changing APLL */ + clrbits_le32(&exynos_clock->clk_src_cpu, APLL_FOUT); + + /* Set APLL */ + writel(APLL_CON1_VAL, &exynos_clock->apll_con1); + val = set_pll(225, 3, 0); /* FOUT=1800MHz */ + writel(val, &exynos_clock->apll_con0); + while ((readl(&exynos_clock->apll_con0) & PLL_LOCKED) == 0) + ; + + /* now it is safe to switch to APLL */ + setbits_le32(&exynos_clock->clk_src_cpu, APLL_FOUT); + + writel(SRC_KFC_HPM_SEL, &exynos_clock->clk_src_kfc); + writel(CLK_DIV_KFC_VAL, &exynos_clock->clk_div_kfc0); + + /* switch A7 clock source to OSC clock before changing KPLL */ + clrbits_le32(&exynos_clock->clk_src_kfc, KPLL_FOUT); + + /* Set KPLL*/ + writel(KPLL_CON1_VAL, &exynos_clock->kpll_con1); + val = set_pll(0x190, 0x4, 0x2); + writel(val, &exynos_clock->kpll_con0); + while ((readl(&exynos_clock->kpll_con0) & PLL_LOCKED) == 0) + ; + + /* now it is safe to switch to KPLL */ + setbits_le32(&exynos_clock->clk_src_kfc, KPLL_FOUT); + + /* Set MPLL */ + writel(MPLL_CON1_VAL, &exynos_clock->mpll_con1); + val = set_pll(0xc8, 0x3, 0x1); + writel(val, &exynos_clock->mpll_con0); + while ((readl(&exynos_clock->mpll_con0) & PLL_LOCKED) == 0) + ; + + /* Set DPLL */ + writel(DPLL_CON1_VAL, &exynos_clock->dpll_con1); + val = set_pll(0x190, 0x4, 0x2); + writel(val, &exynos_clock->dpll_con0); + while ((readl(&exynos_clock->dpll_con0) & PLL_LOCKED) == 0) + ; + + /* Set EPLL */ + writel(EPLL_CON2_VAL, &exynos_clock->epll_con2); + writel(EPLL_CON1_VAL, &exynos_clock->epll_con1); + val = set_pll(0x64, 0x2, 0x1); + writel(val, &exynos_clock->epll_con0); + while ((readl(&exynos_clock->epll_con0) & PLL_LOCKED) == 0) + ; + + /* Set CPLL */ + writel(CPLL_CON1_VAL, &exynos_clock->cpll_con1); + val = set_pll(0xde, 0x4, 0x1); + writel(val, &exynos_clock->cpll_con0); + while ((readl(&exynos_clock->cpll_con0) & PLL_LOCKED) == 0) + ; + + /* Set IPLL */ + writel(IPLL_CON1_VAL, &exynos_clock->ipll_con1); + val = set_pll(0xB9, 0x3, 0x2); + writel(val, &exynos_clock->ipll_con0); + while ((readl(&exynos_clock->ipll_con0) & PLL_LOCKED) == 0) + ; + + /* Set VPLL */ + writel(VPLL_CON1_VAL, &exynos_clock->vpll_con1); + val = set_pll(0xd7, 0x3, 0x2); + writel(val, &exynos_clock->vpll_con0); + while ((readl(&exynos_clock->vpll_con0) & PLL_LOCKED) == 0) + ; + + /* Set BPLL */ + writel(BPLL_CON1_VAL, &exynos_clock->bpll_con1); + val = set_pll(0xc8, 0x3, 0x1); + writel(val, &exynos_clock->bpll_con0); + while ((readl(&exynos_clock->bpll_con0) & PLL_LOCKED) == 0) + ; + + /* Set SPLL */ + writel(SPLL_CON1_VAL, &exynos_clock->spll_con1); + val = set_pll(200, 0x3, 0x2); /* 400MHz */ + writel(val, &exynos_clock->spll_con0); + while ((readl(&exynos_clock->spll_con0) & PLL_LOCKED) == 0) + ; + + /* We use RPLL as the source for FIMD video stream clock */ + writel(RPLL_CON1_VAL, &exynos_clock->rpll_con1); + writel(RPLL_CON2_VAL, &exynos_clock->rpll_con2); + /* computed by gabe from first principles; u-boot is probably + * wrong again + */ + val = set_pll(0xa0, 0x3, 0x2); + writel(val, &exynos_clock->rpll_con0); + /* note: this is a meaningless exercise. The hardware lock + * detection does not work. So this just spins for some + * time and is done. NO indication of success should attach + * to this or any other spin on a con0 value. + */ + while ((readl(&exynos_clock->rpll_con0) & PLL_LOCKED) == 0) + ; + + writel(CLK_DIV_CDREX0_VAL, &exynos_clock->clk_div_cdrex0); + writel(CLK_DIV_CDREX1_VAL, &exynos_clock->clk_div_cdrex1); + + writel(CLK_SRC_TOP0_VAL, &exynos_clock->clk_src_top0); + writel(CLK_SRC_TOP1_VAL, &exynos_clock->clk_src_top1); + writel(CLK_SRC_TOP2_VAL, &exynos_clock->clk_src_top2); + writel(CLK_SRC_TOP7_VAL, &exynos_clock->clk_src_top7); + + writel(CLK_DIV_TOP0_VAL, &exynos_clock->clk_div_top0); + writel(CLK_DIV_TOP1_VAL, &exynos_clock->clk_div_top1); + writel(CLK_DIV_TOP2_VAL, &exynos_clock->clk_div_top2); + + writel(0, &exynos_clock->clk_src_top10); + writel(0, &exynos_clock->clk_src_top11); + writel(0, &exynos_clock->clk_src_top12); + + writel(CLK_SRC_TOP3_VAL, &exynos_clock->clk_src_top3); + writel(CLK_SRC_TOP4_VAL, &exynos_clock->clk_src_top4); + writel(CLK_SRC_TOP5_VAL, &exynos_clock->clk_src_top5); + + /* DISP1 BLK CLK SELECTION */ + writel(CLK_SRC_DISP1_0_VAL, &exynos_clock->clk_src_disp10); + writel(CLK_DIV_DISP1_0_VAL, &exynos_clock->clk_div_disp10); + + /* AUDIO BLK */ + writel(AUDIO0_SEL_EPLL, &exynos_clock->clk_src_mau); + writel(DIV_MAU_VAL, &exynos_clock->clk_div_mau); + + /* FSYS */ + writel(CLK_SRC_FSYS0_VAL, &exynos_clock->clk_src_fsys); + writel(CLK_DIV_FSYS0_VAL, &exynos_clock->clk_div_fsys0); + writel(CLK_DIV_FSYS1_VAL, &exynos_clock->clk_div_fsys1); + writel(CLK_DIV_FSYS2_VAL, &exynos_clock->clk_div_fsys2); + + writel(CLK_SRC_ISP_VAL, &exynos_clock->clk_src_isp); + writel(CLK_DIV_ISP0_VAL, &exynos_clock->clk_div_isp0); + writel(CLK_DIV_ISP1_VAL, &exynos_clock->clk_div_isp1); + + writel(CLK_SRC_PERIC0_VAL, &exynos_clock->clk_src_peric0); + writel(CLK_SRC_PERIC1_VAL, &exynos_clock->clk_src_peric1); + + writel(CLK_DIV_PERIC0_VAL, &exynos_clock->clk_div_peric0); + writel(CLK_DIV_PERIC1_VAL, &exynos_clock->clk_div_peric1); + writel(CLK_DIV_PERIC2_VAL, &exynos_clock->clk_div_peric2); + writel(CLK_DIV_PERIC3_VAL, &exynos_clock->clk_div_peric3); + writel(CLK_DIV_PERIC4_VAL, &exynos_clock->clk_div_peric4); + + writel(CLK_DIV_CPERI1_VAL, &exynos_clock->clk_div_cperi1); + + writel(CLK_DIV2_RATIO, &exynos_clock->clkdiv2_ratio); + writel(CLK_DIV4_RATIO, &exynos_clock->clkdiv4_ratio); + writel(CLK_DIV_G2D, &exynos_clock->clk_div_g2d); + + writel(CLK_SRC_CPU_VAL, &exynos_clock->clk_src_cpu); + writel(CLK_SRC_TOP6_VAL, &exynos_clock->clk_src_top6); + writel(CLK_SRC_CDREX_VAL, &exynos_clock->clk_src_cdrex); + writel(CLK_SRC_KFC_VAL, &exynos_clock->clk_src_kfc); +} + +void clock_gate(void) +{ + /* Not implemented for now. */ +} diff --git a/src/soc/samsung/exynos5420/cpu.c b/src/soc/samsung/exynos5420/cpu.c new file mode 100644 index 0000000000..3f915f0460 --- /dev/null +++ b/src/soc/samsung/exynos5420/cpu.c @@ -0,0 +1,183 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2013 Google Inc. + * Copyright (C) 2012 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <stdlib.h> +#include <string.h> +#include <stddef.h> +#include <delay.h> +#include <console/console.h> +#include <device/device.h> +#include <cbmem.h> +#include <arch/cache.h> +#include "dp.h" +#include "fimd.h" +#include "cpu.h" +#include "clk.h" +#include "chip.h" + +#include <ec/google/chromeec/ec.h> + +static unsigned int cpu_id; +static unsigned int cpu_rev; + +static void set_cpu_id(void) +{ + u32 pro_id = (read32((void *)EXYNOS5_PRO_ID) & 0x00FFF000) >> 12; + + switch (pro_id) { + case 0x200: + /* Exynos4210 EVT0 */ + cpu_id = 0x4210; + cpu_rev = 0; + break; + case 0x210: + /* Exynos4210 EVT1 */ + cpu_id = 0x4210; + break; + case 0x412: + /* Exynos4412 */ + cpu_id = 0x4412; + break; + case 0x520: + /* Exynos5250 */ + cpu_id = 0x5250; + break; + case 0x420: + /* Exynos5420 */ + cpu_id = 0x5420; + break; + } +} + +/* we distinguish a display port device from a raw graphics device + * because there are dramatic differences in startup depending on + * graphics usage. To make startup fast and easier to understand and + * debug we explicitly name this common case. The alternate approach, + * involving lots of machine and callbacks, is hard to debug and + * verify. + */ +static void exynos_displayport_init(device_t dev, u32 lcdbase, + unsigned long fb_size) +{ + struct soc_samsung_exynos5420_config *conf = dev->chip_info; + /* put these on the stack. If, at some point, we want to move + * this code to a pre-ram stage, it will be much easier. + */ + struct exynos5_fimd_panel panel; + memset(&panel, 0, sizeof(panel)); + + panel.is_dp = 1; /* Display I/F is eDP */ + /* while it is true that we did a memset to zero, + * we leave some 'set to zero' entries here to make + * it clear what's going on. Graphics is confusing. + */ + panel.is_mipi = 0; + panel.fixvclk = 0; + panel.ivclk = 0; + panel.clkval_f = conf->clkval_f; + panel.upper_margin = conf->upper_margin; + panel.lower_margin = conf->lower_margin; + panel.vsync = conf->vsync; + panel.left_margin = conf->left_margin; + panel.right_margin = conf->right_margin; + panel.hsync = conf->hsync; + panel.xres = conf->xres; + panel.yres = conf->yres; + + printk(BIOS_SPEW, "LCD framebuffer @%p\n", (void *)(lcdbase)); + memset((void *)lcdbase, 0, fb_size); /* clear the framebuffer */ + + /* + * We need to clean and invalidate the framebuffer region and disable + * caching as well. We assume that our dcache <--> memory address + * space is identity-mapped in 1MB chunks, so align accordingly. + * + * Note: We may want to do something clever to ensure the framebuffer + * region is aligned such that we don't change dcache policy for other + * stuff inadvertantly. + */ + uint32_t lower = ALIGN_DOWN(lcdbase, MiB); + uint32_t upper = ALIGN_UP(lcdbase + fb_size, MiB); + + dcache_clean_invalidate_by_mva(lower, upper - lower); + mmu_config_range(lower / MiB, (upper - lower) / MiB, DCACHE_OFF); + + mmio_resource(dev, 1, lcdbase/KiB, CEIL_DIV(fb_size, KiB)); +} + +static void tps65090_thru_ec_fet_disable(int index) +{ + uint8_t value = 0; + + if (google_chromeec_i2c_xfer(0x48, 0xe + index, 1, &value, 1, 0)) { + printk(BIOS_ERR, + "Error sending i2c pass through command to EC.\n"); + return; + } +} + +static void cpu_enable(device_t dev) +{ + unsigned long fb_size = FB_SIZE_KB * KiB; + u32 lcdbase = get_fb_base_kb() * KiB; + + ram_resource(dev, 0, RAM_BASE_KB, RAM_SIZE_KB - FB_SIZE_KB); + mmio_resource(dev, 1, lcdbase / KiB, CEIL_DIV(fb_size, KiB)); + + /* + * Disable LCD FETs before we do anything with the display. + * FIXME(dhendrix): This is a gross hack and should be done + * elsewhere (romstage?). + */ + tps65090_thru_ec_fet_disable(1); + tps65090_thru_ec_fet_disable(6); + + exynos_displayport_init(dev, lcdbase, fb_size); + + set_cpu_id(); +} + +static void cpu_init(device_t dev) +{ + printk(BIOS_INFO, "CPU: S5P%X @ %ldMHz\n", + cpu_id, get_arm_clk() / 1000000); +} + +static void cpu_noop(device_t dev) +{ +} + +static struct device_operations cpu_ops = { + .read_resources = cpu_noop, + .set_resources = cpu_noop, + .enable_resources = cpu_enable, + .init = cpu_init, + .scan_bus = 0, +}; + +static void enable_exynos5420_dev(device_t dev) +{ + dev->ops = &cpu_ops; +} + +struct chip_operations cpu_samsung_exynos5420_ops = { + CHIP_NAME("CPU Samsung Exynos 5420") + .enable_dev = enable_exynos5420_dev, +}; diff --git a/src/soc/samsung/exynos5420/cpu.h b/src/soc/samsung/exynos5420/cpu.h new file mode 100644 index 0000000000..5b5c731bf0 --- /dev/null +++ b/src/soc/samsung/exynos5420/cpu.h @@ -0,0 +1,98 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2010 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef CPU_SAMSUNG_EXYNOS5420_CPU_H +#define CPU_SAMSUNG_EXYNOS5420_CPU_H + +#include <arch/io.h> + +#define EXYNOS5_SRAM_BASE 0x02020000 + +/* Base address registers */ +#define EXYNOS5420_GPIO_PART6_BASE 0x03860000 /* Z0 */ +#define EXYNOS5_PRO_ID 0x10000000 +#define EXYNOS5_CLOCK_BASE 0x10010000 +#define EXYNOS5_POWER_BASE 0x10040000 +#define EXYNOS5_SYSREG_BASE 0x10050000 +#define EXYNOS5_TZPC1_DECPROT1SET 0x10110810 +#define EXYNOS5_MULTI_CORE_TIMER_BASE 0x101C0000 +#define EXYNOS5_WATCHDOG_BASE 0x101D0000 +#define EXYNOS5_ACE_SFR_BASE 0x10830000 +#define EXYNOS5_DMC_PHY0_BASE 0x10C00000 +#define EXYNOS5_DMC_PHY1_BASE 0x10C10000 +#define EXYNOS5420_DMC_DREXI_0 0x10C20000 +#define EXYNOS5420_DMC_DREXI_1 0x10C30000 +#define EXYNOS5420_DMC_TZASC_0 0x10D40000 +#define EXYNOS5420_DMC_TZASC_1 0x10D50000 +#define EXYNOS5420_USB_DRD0_XHCI_BASE 0x12000000 +#define EXYNOS5420_USB_DRD0_DWC3_BASE 0x1200C100 +#define EXYNOS5420_USB_DRD0_PHY_BASE 0x12100000 +#define EXYNOS5_USB_HOST_EHCI_BASE 0x12110000 +#define EXYNOS5_USB_HOST_PHY_BASE 0x12130000 +#define EXYNOS5_MMC_BASE 0x12200000 +#define EXYNOS5_MSHC_BASE 0x12240000 +#define EXYNOS5_SROMC_BASE 0x12250000 +#define EXYNOS5420_USB_DRD1_XHCI_BASE 0x12400000 +#define EXYNOS5420_USB_DRD1_DWC3_BASE 0x1240C100 +#define EXYNOS5420_USB_DRD1_PHY_BASE 0x12500000 +#define EXYNOS5_UART0_BASE 0x12C00000 +#define EXYNOS5_UART1_BASE 0x12C10000 +#define EXYNOS5_UART2_BASE 0x12C20000 +#define EXYNOS5_UART3_BASE 0x12C30000 +#define EXYNOS5_I2C_BASE 0x12C60000 +#define EXYNOS5_SPI0_BASE 0x12D20000 +#define EXYNOS5_SPI1_BASE 0x12D30000 +#define EXYNOS5_SPI2_BASE 0x12D40000 +#define EXYNOS5_I2S_BASE 0x12D60000 +#define EXYNOS5420_I2C_8910_BASE 0x12E00000 +#define EXYNOS5_UART_ISP_BASE 0x13190000 +#define EXYNOS5_SPI_ISP_BASE 0x131A0000 +#define EXYNOS5420_GPIO_PART1_BASE 0x13400000 /* Y0 */ +#define EXYNOS5420_GPIO_PART2_BASE 0x13400C00 /* X0..3 */ +#define EXYNOS5420_GPIO_PART3_BASE 0x13410000 /* C0..4, D1, Y0..6 */ +#define EXYNOS5420_GPIO_PART4_BASE 0x14000000 /* E0..1, F0..1, G0..2, J4 */ +#define EXYNOS5420_GPIO_PART5_BASE 0x14010000 /* A0..2, B0..4, H0 */ +#define EXYNOS5420_MIPI_DSIM_BASE 0x14500000 +#define EXYNOS5_DP0_BASE 0x14510000 +#define EXYNOS5_DP1_BASE 0x145B0000 + +/* Marker values stored at the bottom of IRAM stack by SPL */ +#define EXYNOS5_SPL_MARKER 0xb004f1a9 /* hexspeak word: bootflag */ + +#define RST_FLAG_VAL 0xfcba0d10 + +#define EXYNOS5_SPI_NUM_CONTROLLERS 5 +#define EXYNOS_I2C_MAX_CONTROLLERS 8 + +extern struct tmu_info exynos5420_tmu_info; + +/* TODO clean up defines. */ +#define FB_SIZE_KB 4096 +#define RAM_BASE_KB (CONFIG_SYS_SDRAM_BASE >> 10) +#define RAM_SIZE_KB (CONFIG_DRAM_SIZE_MB << 10UL) + +static inline u32 get_fb_base_kb(void) +{ + return RAM_BASE_KB + RAM_SIZE_KB - FB_SIZE_KB; +} + +/* Procedures to setup Exynos5420 CPU */ +void exynos5420_config_smp(void); + +#endif /* _EXYNOS5420_CPU_H */ diff --git a/src/soc/samsung/exynos5420/dmc.h b/src/soc/samsung/exynos5420/dmc.h new file mode 100644 index 0000000000..fa7482ae89 --- /dev/null +++ b/src/soc/samsung/exynos5420/dmc.h @@ -0,0 +1,412 @@ +/* + * This file is part of the coreboot project. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef CPU_SAMSUNG_EXYNOS5420_DMC_H +#define CPU_SAMSUNG_EXYNOS5420_DMC_H + +#define DMC_INTERLEAVE_SIZE 0x1f + +#define PAD_RETENTION_DRAM_COREBLK_VAL 0x10000000 + +/* CONCONTROL register fields */ +#define CONCONTROL_DFI_INIT_START_SHIFT 28 +#define CONCONTROL_RD_FETCH_SHIFT 12 +#define CONCONTROL_RD_FETCH_MASK (0x7 << CONCONTROL_RD_FETCH_SHIFT) +#define CONCONTROL_AREF_EN_SHIFT 5 + +/* PRECHCONFIG register field */ +#define PRECHCONFIG_TP_CNT_SHIFT 24 + +/* PWRDNCONFIG register field */ +#define PWRDNCONFIG_DPWRDN_CYC_SHIFT 0 +#define PWRDNCONFIG_DSREF_CYC_SHIFT 16 + +/* PHY_CON0 register fields */ +#define PHY_CON0_T_WRRDCMD_SHIFT 17 +#define PHY_CON0_T_WRRDCMD_MASK (0x7 << PHY_CON0_T_WRRDCMD_SHIFT) +#define PHY_CON0_CTRL_DDR_MODE_MASK 0x3 +#define PHY_CON0_CTRL_DDR_MODE_SHIFT 11 + +/* PHY_CON1 register fields */ +#define PHY_CON1_RDLVL_RDDATA_ADJ_SHIFT 0 + +/* PHY_CON12 register fields */ +#define PHY_CON12_CTRL_START_POINT_SHIFT 24 +#define PHY_CON12_CTRL_INC_SHIFT 16 +#define PHY_CON12_CTRL_FORCE_SHIFT 8 +#define PHY_CON12_CTRL_START_SHIFT 6 +#define PHY_CON12_CTRL_START_MASK (1 << PHY_CON12_CTRL_START_SHIFT) +#define PHY_CON12_CTRL_DLL_ON_SHIFT 5 +#define PHY_CON12_CTRL_DLL_ON_MASK (1 << PHY_CON12_CTRL_DLL_ON_SHIFT) +#define PHY_CON12_CTRL_REF_SHIFT 1 + +/* PHY_CON16 register fields */ +#define PHY_CON16_ZQ_MODE_DDS_SHIFT 24 +#define PHY_CON16_ZQ_MODE_DDS_MASK (0x7 << PHY_CON16_ZQ_MODE_DDS_SHIFT) + +#define PHY_CON16_ZQ_MODE_TERM_SHIFT 21 +#define PHY_CON16_ZQ_MODE_TERM_MASK (0x7 << PHY_CON16_ZQ_MODE_TERM_SHIFT) + +#define PHY_CON16_ZQ_MODE_NOTERM_MASK (1 << 19) + +/* PHY_CON42 register fields */ +#define PHY_CON42_CTRL_BSTLEN_SHIFT 8 +#define PHY_CON42_CTRL_BSTLEN_MASK (0xff << PHY_CON42_CTRL_BSTLEN_SHIFT) + +#define PHY_CON42_CTRL_RDLAT_SHIFT 0 +#define PHY_CON42_CTRL_RDLAT_MASK (0x1f << PHY_CON42_CTRL_RDLAT_SHIFT) + +#ifndef __ASSEMBLER__ + +#include "cpu.h" + +struct exynos5_dmc { + uint32_t concontrol; + uint32_t memcontrol; + uint32_t cgcontrol; + uint32_t memconfig1; + uint32_t directcmd; + uint32_t prechconfig0; + uint32_t phycontrol0; + uint32_t prechconfig1; + uint8_t res1[0x8]; + uint32_t pwrdnconfig; /* 0x0028*/ + uint32_t timingpzq; + uint32_t timingref; + uint32_t timingrow; + uint32_t timingdata; + uint32_t timingpower; + uint32_t phystatus; + uint8_t res2[0x4]; + uint32_t chipstatus_ch0; /* 0x0048 */ + uint32_t chipstatus_ch1; + uint8_t res3[0x4]; + uint32_t mrstatus; + uint8_t res4[0x8]; + uint32_t qoscontrol0; /* 0x0060 */ + uint8_t resr5[0x4]; + uint32_t qoscontrol1; + uint8_t res6[0x4]; + uint32_t qoscontrol2; + uint8_t res7[0x4]; + uint32_t qoscontrol3; + uint8_t res8[0x4]; + uint32_t qoscontrol4; + uint8_t res9[0x4]; + uint32_t qoscontrol5; + uint8_t res10[0x4]; + uint32_t qoscontrol6; + uint8_t res11[0x4]; + uint32_t qoscontrol7; + uint8_t res12[0x4]; + uint32_t qoscontrol8; + uint8_t res13[0x4]; + uint32_t qoscontrol9; + uint8_t res14[0x4]; + uint32_t qoscontrol10; + uint8_t res15[0x4]; + uint32_t qoscontrol11; + uint8_t res16[0x4]; + uint32_t qoscontrol12; + uint8_t res17[0x4]; + uint32_t qoscontrol13; + uint8_t res18[0x4]; + uint32_t qoscontrol14; + uint8_t res19[0x4]; + uint32_t qoscontrol15; + uint8_t res20[0x4]; + uint32_t timing_set_sw; /* 0x00e0 */ + uint32_t timingrow1; + uint32_t timingdata1; + uint32_t timingpower1; + uint32_t ivcontrol; + uint32_t wrtra_config; + uint32_t rdlvl_config; + uint8_t res21[0x4]; + uint32_t brbrsvcontrol; /* 0x0100*/ + uint32_t brbrsvconfig; + uint32_t brbqosconfig; + uint32_t membaseconfig0; + uint32_t membaseconfig1; /* 0x0110 */ + uint8_t res22[0xc]; + uint32_t wrlvl_config0; /* 0x0120 */ + uint32_t wrlvl_config1; + uint32_t wrlvl_status; + uint8_t res23[0x4]; + uint32_t perevcontrol; /* 0x0130 */ + uint32_t perev0config; + uint32_t perev1config; + uint32_t perev2config; + uint32_t perev3config; + uint8_t res22a[0xc]; + uint32_t ctrl_io_rdata_ch0; + uint32_t ctrl_io_rdata_ch1; + uint8_t res23a[0x8]; + uint32_t cacal_config0; + uint32_t cacal_config1; + uint32_t cacal_status; + uint8_t res24[0x94]; + uint32_t emergent_config0; /* 0x0200 */ + uint32_t emergent_config1; + uint8_t res25[0x8]; + uint32_t bp_control0; + uint32_t bp_control0_r; + uint32_t bp_control0_w; + uint8_t res26[0x4]; + uint32_t bp_control1; + uint32_t bp_control1_r; + uint32_t bp_control1_w; + uint8_t res27[0x4]; + uint32_t bp_control2; + uint32_t bp_control2_r; + uint32_t bp_control2_w; + uint8_t res28[0x4]; + uint32_t bp_control3; + uint32_t bp_control3_r; + uint32_t bp_control3_w; + uint8_t res29[0xb4]; + uint32_t winconfig_odt_w; /* 0x0300 */ + uint8_t res30[0x4]; + uint32_t winconfig_ctrl_read; + uint32_t winconfig_ctrl_gate; + uint8_t res31[0xdcf0]; + uint32_t pmnc_ppc; + uint8_t res32[0xc]; + uint32_t cntens_ppc; + uint8_t res33[0xc]; + uint32_t cntenc_ppc; + uint8_t res34[0xc]; + uint32_t intens_ppc; + uint8_t res35[0xc]; + uint32_t intenc_ppc; + uint8_t res36[0xc]; + uint32_t flag_ppc; /* 0xe050 */ + uint8_t res37[0xac]; + uint32_t ccnt_ppc; + uint8_t res38[0xc]; + uint32_t pmcnt0_ppc; + uint8_t res39[0xc]; + uint32_t pmcnt1_ppc; + uint8_t res40[0xc]; + uint32_t pmcnt2_ppc; + uint8_t res41[0xc]; + uint32_t pmcnt3_ppc; /* 0xe140 */ +} __attribute__((packed)); + +static struct exynos5_dmc * const exynos_drex0 = (void *)EXYNOS5420_DMC_DREXI_0; +static struct exynos5_dmc * const exynos_drex1 = (void *)EXYNOS5420_DMC_DREXI_1; + +struct exynos5_phy_control { + uint32_t phy_con0; + uint32_t phy_con1; + uint32_t phy_con2; + uint32_t phy_con3; + uint32_t phy_con4; + uint8_t res1[4]; + uint32_t phy_con6; + uint8_t res2[4]; + uint32_t phy_con8; + uint32_t phy_con9; + uint32_t phy_con10; + uint8_t res3[4]; + uint32_t phy_con12; + uint32_t phy_con13; + uint32_t phy_con14; + uint32_t phy_con15; + uint32_t phy_con16; + uint8_t res4[4]; + uint32_t phy_con17; + uint32_t phy_con18; + uint32_t phy_con19; + uint32_t phy_con20; + uint32_t phy_con21; + uint32_t phy_con22; + uint32_t phy_con23; + uint32_t phy_con24; + uint32_t phy_con25; + uint32_t phy_con26; + uint32_t phy_con27; + uint32_t phy_con28; + uint32_t phy_con29; + uint32_t phy_con30; + uint32_t phy_con31; + uint32_t phy_con32; + uint32_t phy_con33; + uint32_t phy_con34; + uint32_t phy_con35; + uint32_t phy_con36; + uint32_t phy_con37; + uint32_t phy_con38; + uint32_t phy_con39; + uint32_t phy_con40; + uint32_t phy_con41; + uint32_t phy_con42; +} __attribute__((packed)); + +static struct exynos5_phy_control * const exynos_phy0_control = + (void *)EXYNOS5_DMC_PHY0_BASE; +static struct exynos5_phy_control * const exynos_phy1_control = + (void *)EXYNOS5_DMC_PHY1_BASE; + +struct exynos5_tzasc { + uint8_t res1[0xf00]; + uint32_t membaseconfig0; + uint32_t membaseconfig1; + uint8_t res2[0x8]; + uint32_t memconfig0; + uint32_t memconfig1; +} __attribute__((packed)); + +static struct exynos5_tzasc * const exynos_tzasc0 = + (void *)EXYNOS5420_DMC_TZASC_0; +static struct exynos5_tzasc * const exynos_tzasc1 = + (void *)EXYNOS5420_DMC_TZASC_1; + +enum ddr_mode { + /* This is in order of ctrl_ddr_mode values. Do not change. */ + DDR_MODE_DDR2 = 0x0, + DDR_MODE_DDR3 = 0x1, + DDR_MODE_LPDDR2 = 0x2, + DDR_MODE_LPDDR3 = 0x3, + + DDR_MODE_COUNT, +}; + +/* For reasons unknown, people are in the habit of taking a 32-bit + * field with 2 possible values and packing it with, say, 2 bits. A + * non-robust encoding, using only 2 bits of a 32-bit field, is + * incredibly difficult to deal with when things go wrong, because + * there are a lot of things that get expressed as 0, 1, or 2. If + * you're scanning with jtag or dumping memory it is really hard to + * tell when you've hit the beginning of the struct. So, let's be a + * bit smart here. First, while it's common to let the enum count + * entries for you, when there are two of them, we can do the + * counting. And, let's set the values to something we can easily scan + * for in memory. Since '1' and '2' are rather common, we pick + * something that's actually of some value when things go wrong. This + * setup motivated by a use case: something's going wrong and having a + * manuf name of '1' or '2' is completely useless! + */ +enum mem_manuf { + MEM_MANUF_AUTODETECT, + MEM_MANUF_ELPIDA = 0xe7b1da, + MEM_MANUF_SAMSUNG = 0x5a5096, + + MEM_MANUF_COUNT = 2, // fancy that. +}; + +enum { + MEM_TIMINGS_MSR_COUNT = 5, +}; + + +/* These are the memory timings for a particular memory type and speed */ +struct mem_timings { + enum mem_manuf mem_manuf; /* Memory manufacturer */ + enum ddr_mode mem_type; /* Memory type */ + unsigned int frequency_mhz; /* Frequency of memory in MHz */ + + /* Here follow the timing parameters for the selected memory */ + uint8_t apll_mdiv; + uint8_t apll_pdiv; + uint8_t apll_sdiv; + uint8_t mpll_mdiv; + uint8_t mpll_pdiv; + uint8_t mpll_sdiv; + uint8_t cpll_mdiv; + uint8_t cpll_pdiv; + uint8_t cpll_sdiv; + uint8_t gpll_pdiv; + uint16_t gpll_mdiv; + uint8_t gpll_sdiv; + uint8_t epll_mdiv; + uint8_t epll_pdiv; + uint8_t epll_sdiv; + uint8_t vpll_mdiv; + uint8_t vpll_pdiv; + uint8_t vpll_sdiv; + uint8_t bpll_mdiv; + uint8_t bpll_pdiv; + uint8_t bpll_sdiv; + uint8_t use_bpll; /* 1 to use BPLL for cdrex, 0 to use MPLL */ + uint8_t pclk_cdrex_ratio; + unsigned int direct_cmd_msr[MEM_TIMINGS_MSR_COUNT]; + + unsigned int timing_ref; + unsigned int timing_row; + unsigned int timing_data; + unsigned int timing_power; + + /* DQS, DQ, DEBUG offsets */ + unsigned int phy0_dqs; + unsigned int phy1_dqs; + unsigned int phy0_dq; + unsigned int phy1_dq; + uint8_t phy0_tFS; + uint8_t phy1_tFS; + uint8_t phy0_pulld_dqs; + uint8_t phy1_pulld_dqs; + + uint8_t lpddr3_ctrl_phy_reset; + uint8_t ctrl_start_point; + uint8_t ctrl_inc; + uint8_t ctrl_start; + uint8_t ctrl_dll_on; + uint8_t ctrl_ref; + + uint8_t ctrl_force; + uint8_t ctrl_rdlat; + uint8_t ctrl_bstlen; + + uint8_t fp_resync; + uint8_t iv_size; + uint8_t dfi_init_start; + uint8_t aref_en; + + uint8_t rd_fetch; + + uint8_t zq_mode_dds; + uint8_t zq_mode_term; + uint8_t zq_mode_noterm; /* 1 to allow termination disable */ + + unsigned int memcontrol; + unsigned int memconfig; + + unsigned int membaseconfig0; + unsigned int membaseconfig1; + unsigned int prechconfig_tp_cnt; + unsigned int dpwrdn_cyc; + unsigned int dsref_cyc; + unsigned int concontrol; + /* Channel and Chip Selection */ + uint8_t dmc_channels; /* number of memory channels */ + uint8_t chips_per_channel; /* number of chips per channel */ + uint8_t chips_to_configure; /* number of chips to configure */ + uint8_t send_zq_init; /* 1 to send this command */ + unsigned int impedance; /* drive strength impedeance */ + uint8_t gate_leveling_enable; /* check gate leveling is enabled */ +}; + +/** + * Get the correct memory timings for our selected memory type and speed. + * + * @return pointer to the memory timings that we should use + */ +struct mem_timings *get_mem_timings(void); + +#endif +#endif diff --git a/src/soc/samsung/exynos5420/dmc_common.c b/src/soc/samsung/exynos5420/dmc_common.c new file mode 100644 index 0000000000..433312eb3e --- /dev/null +++ b/src/soc/samsung/exynos5420/dmc_common.c @@ -0,0 +1,173 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2012 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* Mem setup common file for different types of DDR present on SMDK5420 boards. + */ + +#include <console/console.h> +#include <arch/io.h> +#include <delay.h> +#include "dmc.h" +#include "setup.h" +#include "clk.h" + +#define ZQ_INIT_TIMEOUT 10000 + +int dmc_config_zq(struct mem_timings *mem, + struct exynos5_phy_control *phy0_ctrl, + struct exynos5_phy_control *phy1_ctrl) +{ + unsigned long val = 0; + int i; + + /* + * ZQ Calibration: + * Select Driver Strength, + * long calibration for manual calibration + */ + val = PHY_CON16_RESET_VAL; + val |= mem->zq_mode_dds << PHY_CON16_ZQ_MODE_DDS_SHIFT; + val |= mem->zq_mode_term << PHY_CON16_ZQ_MODE_TERM_SHIFT; + val |= ZQ_CLK_DIV_EN; + writel(val, &phy0_ctrl->phy_con16); + writel(val, &phy1_ctrl->phy_con16); + + /* Disable termination */ + if (mem->zq_mode_noterm) + val |= PHY_CON16_ZQ_MODE_NOTERM_MASK; + writel(val, &phy0_ctrl->phy_con16); + writel(val, &phy1_ctrl->phy_con16); + + /* ZQ_MANUAL_START: Enable */ + val |= ZQ_MANUAL_STR; + writel(val, &phy0_ctrl->phy_con16); + writel(val, &phy1_ctrl->phy_con16); + + /* ZQ_MANUAL_START: Disable */ + val &= ~ZQ_MANUAL_STR; + + /* + * Since we are manaully calibrating the ZQ values, + * we are looping for the ZQ_init to complete. + */ + i = ZQ_INIT_TIMEOUT; + while ((readl(&phy0_ctrl->phy_con17) & ZQ_DONE) != ZQ_DONE && i > 0) { + udelay(1); + i--; + } + if (!i) + return -1; + writel(val, &phy0_ctrl->phy_con16); + + i = ZQ_INIT_TIMEOUT; + while ((readl(&phy1_ctrl->phy_con17) & ZQ_DONE) != ZQ_DONE && i > 0) { + udelay(1); + i--; + } + if (!i) + return -1; + writel(val, &phy1_ctrl->phy_con16); + + return 0; +} + +void update_reset_dll(struct exynos5_dmc *dmc, enum ddr_mode mode) +{ + unsigned long val; + + if (mode == DDR_MODE_DDR3) { + val = MEM_TERM_EN | PHY_TERM_EN | DMC_CTRL_SHGATE; + writel(val, &dmc->phycontrol0); + } + + /* Update DLL Information: Force DLL Resyncronization */ + val = readl(&dmc->phycontrol0); + val |= FP_RSYNC; + writel(val, &dmc->phycontrol0); + + /* Reset Force DLL Resyncronization */ + val = readl(&dmc->phycontrol0); + val &= ~FP_RSYNC; + writel(val, &dmc->phycontrol0); +} + +void dmc_config_mrs(struct mem_timings *mem, struct exynos5_dmc *dmc) +{ + int channel, chip; + + for (channel = 0; channel < mem->dmc_channels; channel++) { + unsigned long mask; + + mask = channel << DIRECT_CMD_CHANNEL_SHIFT; + for (chip = 0; chip < mem->chips_to_configure; chip++) { + int i; + + mask |= chip << DIRECT_CMD_CHIP_SHIFT; + + /* Sending NOP command */ + writel(DIRECT_CMD_NOP | mask, &dmc->directcmd); + + /* + * TODO(alim.akhtar@samsung.com): Do we need these + * delays? This one and the next were not there for + * DDR3. + */ + udelay(100); + + /* Sending EMRS/MRS commands */ + for (i = 0; i < MEM_TIMINGS_MSR_COUNT; i++) { + writel(mem->direct_cmd_msr[i] | mask, + &dmc->directcmd); + udelay(100); + } + + if (mem->send_zq_init) { + /* Sending ZQINIT command */ + writel(DIRECT_CMD_ZQINIT | mask, + &dmc->directcmd); + /* + * FIXME: This was originally sdelay(10000) + * in the imported u-boot code. That may have + * been meant to be sdelay(0x10000) since that + * was used elsewhere in this function. Either + * way seems to work, though. + */ + udelay(12); + } + } + } +} + +void dmc_config_prech(struct mem_timings *mem, struct exynos5_dmc *dmc) +{ + int channel, chip; + + for (channel = 0; channel < mem->dmc_channels; channel++) { + unsigned long mask; + + mask = channel << DIRECT_CMD_CHANNEL_SHIFT; + for (chip = 0; chip < mem->chips_per_channel; chip++) { + mask |= chip << DIRECT_CMD_CHIP_SHIFT; + + /* PALL (all banks precharge) CMD */ + writel(DIRECT_CMD_PALL | mask, &dmc->directcmd); + udelay(100); + } + } +} diff --git a/src/soc/samsung/exynos5420/dmc_init_ddr3.c b/src/soc/samsung/exynos5420/dmc_init_ddr3.c new file mode 100644 index 0000000000..4acf7d13f3 --- /dev/null +++ b/src/soc/samsung/exynos5420/dmc_init_ddr3.c @@ -0,0 +1,350 @@ +/* + * This file is part of the coreboot project. + * + * DDR3 mem setup file for EXYNOS5 based board + * + * 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 <delay.h> +#include <arch/io.h> +#include <console/console.h> +#include "clk.h" +#include "dmc.h" +#include "power.h" +#include "setup.h" + +#define TIMEOUT 10000 + +/* 'reset' field is currently ignored. */ + +int ddr3_mem_ctrl_init(struct mem_timings *mem, int interleave_size, int reset) +{ + u32 val, nLockR, nLockW_phy0, nLockW_phy1; + int i, chip; + + /* Enable PAUSE for DREX */ + setbits_le32(&exynos_clock->pause, ENABLE_BIT); + + /* Enable BYPASS mode */ + setbits_le32(&exynos_clock->bpll_con1, BYPASS_EN); + + writel(MUX_BPLL_SEL_FOUTBPLL, &exynos_clock->clk_src_cdrex); + do { + val = readl(&exynos_clock->clk_mux_stat_cdrex); + val &= BPLL_SEL_MASK; + } while (val != FOUTBPLL); + + clrbits_le32(&exynos_clock->bpll_con1, BYPASS_EN); + + /* Specify the DDR memory type as DDR3 */ + val = readl(&exynos_phy0_control->phy_con0); + val &= ~(PHY_CON0_CTRL_DDR_MODE_MASK << PHY_CON0_CTRL_DDR_MODE_SHIFT); + val |= (mem->mem_type << PHY_CON0_CTRL_DDR_MODE_SHIFT); + writel(val, &exynos_phy0_control->phy_con0); + + val = readl(&exynos_phy1_control->phy_con0); + val &= ~(PHY_CON0_CTRL_DDR_MODE_MASK << PHY_CON0_CTRL_DDR_MODE_SHIFT); + val |= (mem->mem_type << PHY_CON0_CTRL_DDR_MODE_SHIFT); + writel(val, &exynos_phy1_control->phy_con0); + + /* 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, &exynos_phy0_control->phy_con42); + writel(val, &exynos_phy1_control->phy_con42); + + val = readl(&exynos_phy0_control->phy_con26); + val &= ~(T_WRDATA_EN_MASK << T_WRDATA_EN_OFFSET); + val |= (T_WRDATA_EN_DDR3 << T_WRDATA_EN_OFFSET); + writel(val, &exynos_phy0_control->phy_con26); + + val = readl(&exynos_phy1_control->phy_con26); + val &= ~(T_WRDATA_EN_MASK << T_WRDATA_EN_OFFSET); + val |= (T_WRDATA_EN_DDR3 << T_WRDATA_EN_OFFSET); + writel(val, &exynos_phy1_control->phy_con26); + + /* Set Driver strength for CK, CKE, CS & CA to 0x7 + * Set Driver strength for Data Slice 0~3 to 0x6 + */ + val = (0x7 << CA_CK_DRVR_DS_OFFSET) | (0x7 << CA_CKE_DRVR_DS_OFFSET) | + (0x7 << CA_CS_DRVR_DS_OFFSET) | (0x7 << CA_ADR_DRVR_DS_OFFSET); + val |= (0x7 << DA_3_DS_OFFSET) | (0x7 << DA_2_DS_OFFSET) | + (0x7 << DA_1_DS_OFFSET) | (0x7 << DA_0_DS_OFFSET); + writel(val, &exynos_phy0_control->phy_con39); + writel(val, &exynos_phy1_control->phy_con39); + + /* ZQ Calibration */ + if (dmc_config_zq(mem, exynos_phy0_control, exynos_phy1_control)) + return SETUP_ERR_ZQ_CALIBRATION_FAILURE; + + clrbits_le32(&exynos_phy0_control->phy_con16, ZQ_CLK_DIV_EN); + clrbits_le32(&exynos_phy1_control->phy_con16, ZQ_CLK_DIV_EN); + + /* DQ Signal */ + val = readl(&exynos_phy0_control->phy_con14); + val |= mem->phy0_pulld_dqs; + writel(val, &exynos_phy0_control->phy_con14); + val = readl(&exynos_phy1_control->phy_con14); + val |= mem->phy1_pulld_dqs; + writel(val, &exynos_phy1_control->phy_con14); + + val = MEM_TERM_EN | PHY_TERM_EN; + writel(val, &exynos_drex0->phycontrol0); + writel(val, &exynos_drex1->phycontrol0); + + writel(mem->concontrol | + (mem->dfi_init_start << CONCONTROL_DFI_INIT_START_SHIFT) | + (mem->rd_fetch << CONCONTROL_RD_FETCH_SHIFT), + &exynos_drex0->concontrol); + writel(mem->concontrol | + (mem->dfi_init_start << CONCONTROL_DFI_INIT_START_SHIFT) | + (mem->rd_fetch << CONCONTROL_RD_FETCH_SHIFT), + &exynos_drex1->concontrol); + + do { + val = readl(&exynos_drex0->phystatus); + } while ((val & DFI_INIT_COMPLETE) != DFI_INIT_COMPLETE); + do { + val = readl(&exynos_drex1->phystatus); + } while ((val & DFI_INIT_COMPLETE) != DFI_INIT_COMPLETE); + + clrbits_le32(&exynos_drex0->concontrol, DFI_INIT_START); + clrbits_le32(&exynos_drex1->concontrol, DFI_INIT_START); + + update_reset_dll(exynos_drex0, mem->mem_type); + update_reset_dll(exynos_drex1, mem->mem_type); + + /* MEMBASECONFIG0 (CS0) */ + writel(mem->membaseconfig0, &exynos_tzasc0->membaseconfig0); + writel(mem->membaseconfig0, &exynos_tzasc1->membaseconfig0); + + /* MEMBASECONFIG1 (CS1) */ + if (mem->chips_per_channel == 2) { + writel(mem->membaseconfig1, &exynos_tzasc0->membaseconfig1); + writel(mem->membaseconfig1, &exynos_tzasc1->membaseconfig1); + } + + /* Memory Channel Inteleaving Size + * Exynos5420 Channel interleaving = 128 bytes + */ + /* MEMCONFIG0/1 */ + writel(mem->memconfig, &exynos_tzasc0->memconfig0); + writel(mem->memconfig, &exynos_tzasc1->memconfig0); + writel(mem->memconfig, &exynos_tzasc0->memconfig1); + writel(mem->memconfig, &exynos_tzasc1->memconfig1); + + /* Precharge Configuration */ + writel(mem->prechconfig_tp_cnt << PRECHCONFIG_TP_CNT_SHIFT, + &exynos_drex0->prechconfig0); + writel(mem->prechconfig_tp_cnt << PRECHCONFIG_TP_CNT_SHIFT, + &exynos_drex1->prechconfig0); + + /* TimingRow, TimingData, TimingPower and Timingaref + * values as per Memory AC parameters + */ + writel(mem->timing_ref, &exynos_drex0->timingref); + writel(mem->timing_ref, &exynos_drex1->timingref); + writel(mem->timing_row, &exynos_drex0->timingrow); + writel(mem->timing_row, &exynos_drex1->timingrow); + writel(mem->timing_data, &exynos_drex0->timingdata); + writel(mem->timing_data, &exynos_drex1->timingdata); + writel(mem->timing_power, &exynos_drex0->timingpower); + writel(mem->timing_power, &exynos_drex1->timingpower); + + if (reset) { + /* Send NOP, MRS and ZQINIT commands. + * Sending MRS command will reset the DRAM. We should not be + * reseting the DRAM after resume, this will lead to memory + * corruption as DRAM content is lost after DRAM reset. + */ + dmc_config_mrs(mem, exynos_drex0); + dmc_config_mrs(mem, exynos_drex1); + } else { + u32 ret; + + /* + * During Suspend-Resume & S/W-Reset, as soon as PMU releases + * pad retention, CKE goes high. This causes memory contents + * not to be retained during DRAM initialization. Therfore, + * there is a new control register(0x100431e8[28]) which lets us + * release pad retention and retain the memory content until the + * initialization is complete. + */ + write32(PAD_RETENTION_DRAM_COREBLK_VAL, + &exynos_power->padret_dram_cblk_opt); + do { + ret = read32(&exynos_power->padret_dram_status); + } while (ret != 0x1); + + /* + * CKE PAD retention disables DRAM self-refresh mode. + * Send auto refresh command for DRAM refresh. + */ + for (i = 0; i < 128; i++) { + for (chip = 0; chip < mem->chips_to_configure; chip++) { + writel(DIRECT_CMD_REFA | + (chip << DIRECT_CMD_CHIP_SHIFT), + &exynos_drex0->directcmd); + writel(DIRECT_CMD_REFA | + (chip << DIRECT_CMD_CHIP_SHIFT), + &exynos_drex1->directcmd); + } + } + } + + if (mem->gate_leveling_enable) { + + writel(PHY_CON0_RESET_VAL, &exynos_phy0_control->phy_con0); + writel(PHY_CON0_RESET_VAL, &exynos_phy1_control->phy_con0); + + setbits_le32(&exynos_phy0_control->phy_con0, P0_CMD_EN); + setbits_le32(&exynos_phy1_control->phy_con0, P0_CMD_EN); + + val = PHY_CON2_RESET_VAL; + val |= INIT_DESKEW_EN; + writel(val, &exynos_phy0_control->phy_con2); + writel(val, &exynos_phy1_control->phy_con2); + + val = readl(&exynos_phy0_control->phy_con1); + val |= (RDLVL_PASS_ADJ_VAL << RDLVL_PASS_ADJ_OFFSET); + writel(val, &exynos_phy0_control->phy_con1); + + val = readl(&exynos_phy1_control->phy_con1); + val |= (RDLVL_PASS_ADJ_VAL << RDLVL_PASS_ADJ_OFFSET); + writel(val, &exynos_phy1_control->phy_con1); + + nLockR = readl(&exynos_phy0_control->phy_con13); + nLockW_phy0 = (nLockR & CTRL_LOCK_COARSE_MASK) >> 2; + nLockR = readl(&exynos_phy0_control->phy_con12); + nLockR &= ~CTRL_DLL_ON; + nLockR |= nLockW_phy0; + writel(nLockR, &exynos_phy0_control->phy_con12); + + nLockR = readl(&exynos_phy1_control->phy_con13); + nLockW_phy1 = (nLockR & CTRL_LOCK_COARSE_MASK) >> 2; + nLockR = readl(&exynos_phy1_control->phy_con12); + nLockR &= ~CTRL_DLL_ON; + nLockR |= nLockW_phy1; + writel(nLockR, &exynos_phy1_control->phy_con12); + + val = (0x3 << DIRECT_CMD_BANK_SHIFT) | 0x4; + for (chip = 0; chip < mem->chips_to_configure; chip++) { + writel(val | (chip << DIRECT_CMD_CHIP_SHIFT), + &exynos_drex0->directcmd); + writel(val | (chip << DIRECT_CMD_CHIP_SHIFT), + &exynos_drex1->directcmd); + } + + setbits_le32(&exynos_phy0_control->phy_con2, RDLVL_GATE_EN); + setbits_le32(&exynos_phy1_control->phy_con2, RDLVL_GATE_EN); + + setbits_le32(&exynos_phy0_control->phy_con0, CTRL_SHGATE); + setbits_le32(&exynos_phy1_control->phy_con0, CTRL_SHGATE); + + val = readl(&exynos_phy0_control->phy_con1); + val &= ~(CTRL_GATEDURADJ_MASK); + writel(val, &exynos_phy0_control->phy_con1); + + val = readl(&exynos_phy1_control->phy_con1); + val &= ~(CTRL_GATEDURADJ_MASK); + writel(val, &exynos_phy1_control->phy_con1); + + writel(CTRL_RDLVL_GATE_ENABLE, &exynos_drex0->rdlvl_config); + i = TIMEOUT; + while (((readl(&exynos_drex0->phystatus) & RDLVL_COMPLETE_CHO) + != RDLVL_COMPLETE_CHO) && (i > 0)) { + /* + * TODO(waihong): Comment on how long this take to + * timeout + */ + udelay(1); + i--; + } + if (!i) + return SETUP_ERR_RDLV_COMPLETE_TIMEOUT; + writel(CTRL_RDLVL_GATE_DISABLE, &exynos_drex0->rdlvl_config); + + writel(CTRL_RDLVL_GATE_ENABLE, &exynos_drex1->rdlvl_config); + i = TIMEOUT; + while (((readl(&exynos_drex1->phystatus) & RDLVL_COMPLETE_CHO) + != RDLVL_COMPLETE_CHO) && (i > 0)) { + /* + * TODO(waihong): Comment on how long this take to + * timeout + */ + udelay(1); + i--; + } + if (!i) + return SETUP_ERR_RDLV_COMPLETE_TIMEOUT; + writel(CTRL_RDLVL_GATE_DISABLE, &exynos_drex1->rdlvl_config); + + writel(0, &exynos_phy0_control->phy_con14); + writel(0, &exynos_phy1_control->phy_con14); + + val = (0x3 << DIRECT_CMD_BANK_SHIFT); + for (chip = 0; chip < mem->chips_to_configure; chip++) { + writel(val | (chip << DIRECT_CMD_CHIP_SHIFT), + &exynos_drex0->directcmd); + writel(val | (chip << DIRECT_CMD_CHIP_SHIFT), + &exynos_drex1->directcmd); + } + + /* Common Settings for Leveling */ + val = PHY_CON12_RESET_VAL; + writel((val + nLockW_phy0), &exynos_phy0_control->phy_con12); + writel((val + nLockW_phy1), &exynos_phy1_control->phy_con12); + + setbits_le32(&exynos_phy0_control->phy_con2, DLL_DESKEW_EN); + setbits_le32(&exynos_phy1_control->phy_con2, DLL_DESKEW_EN); + } + + /* Send PALL command */ + dmc_config_prech(mem, exynos_drex0); + dmc_config_prech(mem, exynos_drex1); + + writel(mem->memcontrol, &exynos_drex0->memcontrol); + writel(mem->memcontrol, &exynos_drex1->memcontrol); + + /* + * Set DMC Concontrol: Enable auto-refresh counter, provide + * read data fetch cycles and enable DREX auto set powerdown + * for input buffer of I/O in none read memory state. + */ + writel(mem->concontrol | (mem->aref_en << CONCONTROL_AREF_EN_SHIFT) | + (mem->rd_fetch << CONCONTROL_RD_FETCH_SHIFT)| + DMC_CONCONTROL_IO_PD_CON(0x2), + &exynos_drex0->concontrol); + writel(mem->concontrol | (mem->aref_en << CONCONTROL_AREF_EN_SHIFT) | + (mem->rd_fetch << CONCONTROL_RD_FETCH_SHIFT)| + DMC_CONCONTROL_IO_PD_CON(0x2), + &exynos_drex1->concontrol); + + /* Enable Clock Gating Control for DMC + * this saves around 25 mw dmc power as compared to the power + * consumption without these bits enabled + */ + setbits_le32(&exynos_drex0->cgcontrol, DMC_INTERNAL_CG); + setbits_le32(&exynos_drex1->cgcontrol, DMC_INTERNAL_CG); + + return 0; +} diff --git a/src/soc/samsung/exynos5420/dp.c b/src/soc/samsung/exynos5420/dp.c new file mode 100644 index 0000000000..b147035b64 --- /dev/null +++ b/src/soc/samsung/exynos5420/dp.c @@ -0,0 +1,910 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * + * Author: Donghwa Lee <dh09.lee@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 + */ + +#include <arch/io.h> +#include <stdlib.h> +#include <string.h> +#include <timer.h> +#include <delay.h> +#include <console/console.h> +#include <lib.h> +#include "timer.h" +#include "power.h" +#include "sysreg.h" + +#include "dp.h" +#include "fimd.h" +#include "i2c.h" + +/* + * Here is the rough outline of how we bring up the display: + * 1. Upon power-on Sink generates a hot plug detection pulse thru HPD + * 2. Source determines video mode by reading DPCD receiver capability field + * (DPCD 00000h to 0000Dh) including eDP CP capability register (DPCD + * 0000Dh). + * 3. Sink replies DPCD receiver capability field. + * 4. Source starts EDID read thru I2C-over-AUX. + * 5. Sink replies EDID thru I2C-over-AUX. + * 6. Source determines link configuration, such as MAX_LINK_RATE and + * MAX_LANE_COUNT. Source also determines which type of eDP Authentication + * method to use and writes DPCD link configuration field (DPCD 00100h to + * 0010Ah) including eDP configuration set (DPCD 0010Ah). + * 7. Source starts link training. Sink does clock recovery and equalization. + * 8. Source reads DPCD link status field (DPCD 00200h to 0020Bh). + * 9. Sink replies DPCD link status field. If main link is not stable, Source + * repeats Step 7. + * 10. Source sends MSA (Main Stream Attribute) data. Sink extracts video + * parameters and recovers stream clock. + * 11. Source sends video data. + */ + + +static int exynos_dp_init_dp(void) +{ + int ret; + exynos_dp_reset(); + + /* SW defined function Normal operation */ + exynos_dp_enable_sw_func(DP_ENABLE); + + ret = exynos_dp_init_analog_func(); + if (ret != EXYNOS_DP_SUCCESS) + return ret; + + exynos_dp_init_hpd(); + exynos_dp_init_aux(); + + return ret; +} + +static unsigned char exynos_dp_calc_edid_check_sum(unsigned char *edid_data) +{ + int i; + unsigned char sum = 0; + + for (i = 0; i < EDID_BLOCK_LENGTH; i++) + sum = sum + edid_data[i]; + + return sum; +} + +static unsigned int exynos_dp_read_edid(void) +{ + unsigned char edid[EDID_BLOCK_LENGTH * 2]; + unsigned int extend_block = 0; + unsigned char sum; + unsigned char test_vector; + int retval = 0; + + /* + * EDID device address is 0x50. + * However, if necessary, you must have set upper address + * into E-EDID in I2C device, 0x30. + */ + + /* Read Extension Flag, Number of 128-byte EDID extension blocks */ + if (exynos_dp_read_byte_from_i2c + (I2C_EDID_DEVICE_ADDR, EDID_EXTENSION_FLAG, &extend_block)) + return -1; + + if (extend_block > 0) { + /* Read EDID data */ + retval = exynos_dp_read_bytes_from_i2c(I2C_EDID_DEVICE_ADDR, + EDID_HEADER_PATTERN, + EDID_BLOCK_LENGTH, + &edid[EDID_HEADER_PATTERN]); + + if (retval != 0) { + printk(BIOS_ERR, "DP EDID Read failed!\n"); + return -1; + } + sum = exynos_dp_calc_edid_check_sum(edid); + if (sum != 0) { + printk(BIOS_ERR, "DP EDID bad checksum!\n"); + return -1; + } + /* Read additional EDID data */ + retval = exynos_dp_read_bytes_from_i2c(I2C_EDID_DEVICE_ADDR, + EDID_BLOCK_LENGTH, + EDID_BLOCK_LENGTH, + &edid[EDID_BLOCK_LENGTH]); + if (retval != 0) { + printk(BIOS_ERR, "DP EDID Read failed!\n"); + return -1; + } + sum = exynos_dp_calc_edid_check_sum(&edid[EDID_BLOCK_LENGTH]); + if (sum != 0) { + printk(BIOS_ERR, "DP EDID bad checksum!\n"); + return -1; + } + exynos_dp_read_byte_from_dpcd(DPCD_TEST_REQUEST, + &test_vector); + if (test_vector & DPCD_TEST_EDID_READ) { + exynos_dp_write_byte_to_dpcd(DPCD_TEST_EDID_CHECKSUM, + edid[EDID_BLOCK_LENGTH + EDID_CHECKSUM]); + exynos_dp_write_byte_to_dpcd(DPCD_TEST_RESPONSE, + DPCD_TEST_EDID_CHECKSUM_WRITE); + } + } else { + /* Read EDID data */ + retval = exynos_dp_read_bytes_from_i2c(I2C_EDID_DEVICE_ADDR, + EDID_HEADER_PATTERN, + EDID_BLOCK_LENGTH, + &edid[EDID_HEADER_PATTERN]); + + if (retval != 0) { + printk(BIOS_ERR, "DP EDID Read failed!\n"); + return -1; + } + sum = exynos_dp_calc_edid_check_sum(edid); + if (sum != 0) { + printk(BIOS_ERR, "DP EDID bad checksum!\n"); + return -1; + } + + exynos_dp_read_byte_from_dpcd(DPCD_TEST_REQUEST, + &test_vector); + if (test_vector & DPCD_TEST_EDID_READ) { + exynos_dp_write_byte_to_dpcd(DPCD_TEST_EDID_CHECKSUM, + edid[EDID_CHECKSUM]); + exynos_dp_write_byte_to_dpcd(DPCD_TEST_RESPONSE, + DPCD_TEST_EDID_CHECKSUM_WRITE); + } + + } + + return 0; +} + +static unsigned int exynos_dp_handle_edid(struct edp_device_info *edp_info) +{ + unsigned char buf[12]; + unsigned int ret; + unsigned char temp; + unsigned char retry_cnt; + unsigned char dpcd_rev[16]; + unsigned char lane_bw[16]; + unsigned char lane_cnt[16]; + + memset(dpcd_rev, 0, sizeof(dpcd_rev)); + memset(lane_bw, 0, sizeof(lane_bw)); + memset(lane_cnt, 0, sizeof(lane_cnt)); + memset(buf, 0, sizeof(buf)); + + retry_cnt = 5; + while (retry_cnt) { + /* Read DPCD 0x0000-0x000b */ + ret = exynos_dp_read_bytes_from_dpcd(DPCD_DPCD_REV, 12, + buf); + if (ret != EXYNOS_DP_SUCCESS) { + if (retry_cnt == 0) { + printk(BIOS_ERR, "DP read_byte_from_dpcd() failed\n"); + return ret; + } + retry_cnt--; + } else + break; + } + /* */ + temp = buf[DPCD_DPCD_REV]; + if (temp == DP_DPCD_REV_10 || temp == DP_DPCD_REV_11) + edp_info->dpcd_rev = temp; + else { + printk(BIOS_ERR, "DP Wrong DPCD Rev : %x\n", temp); + return -1; + } + temp = buf[DPCD_MAX_LINK_RATE]; + if (temp == DP_LANE_BW_1_62 || temp == DP_LANE_BW_2_70) + edp_info->lane_bw = temp; + else { + printk(BIOS_ERR, "DP Wrong MAX LINK RATE : %x\n", temp); + return -1; + } + /*Refer VESA Display Port Stnadard Ver1.1a Page 120 */ + if (edp_info->dpcd_rev == DP_DPCD_REV_11) { + temp = buf[DPCD_MAX_LANE_COUNT] & 0x1f; + if (buf[DPCD_MAX_LANE_COUNT] & 0x80) + edp_info->dpcd_efc = 1; + else + edp_info->dpcd_efc = 0; + } else { + temp = buf[DPCD_MAX_LANE_COUNT]; + edp_info->dpcd_efc = 0; + } + + if (temp == DP_LANE_CNT_1 || temp == DP_LANE_CNT_2 || + temp == DP_LANE_CNT_4) { + edp_info->lane_cnt = temp; + } else { + printk(BIOS_ERR, "DP Wrong MAX LANE COUNT : %x\n", temp); + return -1; + } + + if (edp_info->raw_edid){ + ret = EXYNOS_DP_SUCCESS; + printk(BIOS_SPEW, "EDID compiled in, skipping read\n"); + } else { + ret = exynos_dp_read_edid(); + if (ret != EXYNOS_DP_SUCCESS) { + printk(BIOS_ERR, "DP exynos_dp_read_edid() failed\n"); + return -1; + } + } + + return ret; +} + +static void exynos_dp_init_training(void) +{ + /* + * MACRO_RST must be applied after the PLL_LOCK to avoid + * the DP inter pair skew issue for at least 10 us + */ + exynos_dp_reset_macro(); + + /* All DP analog module power up */ + exynos_dp_set_analog_power_down(POWER_ALL, 0); +} + +static unsigned int exynos_dp_link_start(struct edp_device_info *edp_info) +{ + unsigned char buf[5]; + unsigned int ret; + + edp_info->lt_info.lt_status = DP_LT_CR; + edp_info->lt_info.ep_loop = 0; + edp_info->lt_info.cr_loop[0] = 0; + edp_info->lt_info.cr_loop[1] = 0; + edp_info->lt_info.cr_loop[2] = 0; + edp_info->lt_info.cr_loop[3] = 0; + + /* Set sink to D0 (Sink Not Ready) mode. */ + ret = exynos_dp_write_byte_to_dpcd(DPCD_SINK_POWER_STATE, + DPCD_SET_POWER_STATE_D0); + if (ret != EXYNOS_DP_SUCCESS) { + printk(BIOS_ERR, "DP write_dpcd_byte failed\n"); + return ret; + } + + /* Set link rate and count as you want to establish*/ + exynos_dp_set_link_bandwidth(edp_info->lane_bw); + exynos_dp_set_lane_count(edp_info->lane_cnt); + + /* Setup RX configuration */ + buf[0] = edp_info->lane_bw; + buf[1] = edp_info->lane_cnt; + + ret = exynos_dp_write_bytes_to_dpcd(DPCD_LINK_BW_SET, 2, + buf); + if (ret != EXYNOS_DP_SUCCESS) { + printk(BIOS_ERR, "DP write_dpcd_byte failed\n"); + return ret; + } + + exynos_dp_set_lane_pre_emphasis(PRE_EMPHASIS_LEVEL_0, + edp_info->lane_cnt); + + /* Set training pattern 1 */ + exynos_dp_set_training_pattern(TRAINING_PTN1); + + /* Set RX training pattern */ + buf[0] = DPCD_SCRAMBLING_DISABLED | DPCD_TRAINING_PATTERN_1; + + buf[1] = DPCD_PRE_EMPHASIS_SET_PATTERN_2_LEVEL_0 | + DPCD_VOLTAGE_SWING_SET_PATTERN_1_LEVEL_0; + buf[2] = DPCD_PRE_EMPHASIS_SET_PATTERN_2_LEVEL_0 | + DPCD_VOLTAGE_SWING_SET_PATTERN_1_LEVEL_0; + buf[3] = DPCD_PRE_EMPHASIS_SET_PATTERN_2_LEVEL_0 | + DPCD_VOLTAGE_SWING_SET_PATTERN_1_LEVEL_0; + buf[4] = DPCD_PRE_EMPHASIS_SET_PATTERN_2_LEVEL_0 | + DPCD_VOLTAGE_SWING_SET_PATTERN_1_LEVEL_0; + + ret = exynos_dp_write_bytes_to_dpcd(DPCD_TRAINING_PATTERN_SET, + 5, buf); + if (ret != EXYNOS_DP_SUCCESS) { + printk(BIOS_ERR, "DP write_dpcd_byte failed\n"); + return ret; + } + return ret; +} + +static unsigned int exynos_dp_training_pattern_dis(void) +{ + unsigned int ret; + + exynos_dp_set_training_pattern(DP_NONE); + + ret = exynos_dp_write_byte_to_dpcd(DPCD_TRAINING_PATTERN_SET, + DPCD_TRAINING_PATTERN_DISABLED); + if (ret != EXYNOS_DP_SUCCESS) { + printk(BIOS_ERR, "DP requst_link_traninig_req failed\n"); + return -1; + } + + return ret; +} + +static unsigned int exynos_dp_enable_rx_to_enhanced_mode(unsigned char enable) +{ + unsigned char data; + unsigned int ret; + + ret = exynos_dp_read_byte_from_dpcd(DPCD_LANE_COUNT_SET, + &data); + if (ret != EXYNOS_DP_SUCCESS) { + printk(BIOS_ERR, "DP read_from_dpcd failed\n"); + return -1; + } + + if (enable) + data = DPCD_ENHANCED_FRAME_EN | DPCD_LN_COUNT_SET(data); + else + data = DPCD_LN_COUNT_SET(data); + + ret = exynos_dp_write_byte_to_dpcd(DPCD_LANE_COUNT_SET, + data); + if (ret != EXYNOS_DP_SUCCESS) { + printk(BIOS_ERR, "DP write_to_dpcd failed\n"); + return -1; + + } + + return ret; +} + +static unsigned int exynos_dp_set_enhanced_mode(unsigned char enhance_mode) +{ + unsigned int ret; + + ret = exynos_dp_enable_rx_to_enhanced_mode(enhance_mode); + if (ret != EXYNOS_DP_SUCCESS) { + printk(BIOS_ERR, "DP rx_enhance_mode failed\n"); + return -1; + } + + exynos_dp_enable_enhanced_mode(enhance_mode); + + return ret; +} + +static int exynos_dp_read_dpcd_lane_stat(struct edp_device_info *edp_info, + unsigned char *status) +{ + unsigned int ret, i; + unsigned char buf[2]; + unsigned char lane_stat[DP_LANE_CNT_4] = {0,}; + const unsigned char shift_val[] = {0, 4, 0, 4}; + + ret = exynos_dp_read_bytes_from_dpcd(DPCD_LANE0_1_STATUS, 2, buf); + if (ret != EXYNOS_DP_SUCCESS) { + printk(BIOS_ERR, "DP read lane status failed\n"); + return ret; + } + + for (i = 0; i < edp_info->lane_cnt; i++) { + lane_stat[i] = (buf[(i / 2)] >> shift_val[i]) & 0x0f; + if (lane_stat[0] != lane_stat[i]) { + printk(BIOS_ERR, "Wrong lane status\n"); + return -1; + } + } + + *status = lane_stat[0]; + + return ret; +} + +static unsigned int exynos_dp_read_dpcd_adj_req(unsigned char lane_num, + unsigned char *sw, unsigned char *em) +{ + const unsigned char shift_val[] = {0, 4, 0, 4}; + unsigned int ret; + unsigned char buf; + unsigned int dpcd_addr; + + /*lane_num value is used as arry index, so this range 0 ~ 3 */ + dpcd_addr = DPCD_ADJUST_REQUEST_LANE0_1 + (lane_num / 2); + + ret = exynos_dp_read_byte_from_dpcd(dpcd_addr, &buf); + if (ret != EXYNOS_DP_SUCCESS) { + printk(BIOS_ERR, "DP read adjust request failed\n"); + return -1; + } + + *sw = ((buf >> shift_val[lane_num]) & 0x03); + *em = ((buf >> shift_val[lane_num]) & 0x0c) >> 2; + + return ret; +} + +static int exynos_dp_equalizer_err_link(struct edp_device_info *edp_info) +{ + int ret; + + ret = exynos_dp_training_pattern_dis(); + if (ret != EXYNOS_DP_SUCCESS) { + printk(BIOS_ERR, "DP training_patter_disable() failed\n"); + edp_info->lt_info.lt_status = DP_LT_FAIL; + } + + ret = exynos_dp_set_enhanced_mode(edp_info->dpcd_efc); + if (ret != EXYNOS_DP_SUCCESS) { + printk(BIOS_ERR, "DP set_enhanced_mode() failed\n"); + edp_info->lt_info.lt_status = DP_LT_FAIL; + } + + return ret; +} + +static int exynos_dp_reduce_link_rate(struct edp_device_info *edp_info) +{ + int ret; + + if (edp_info->lane_bw == DP_LANE_BW_2_70) { + edp_info->lane_bw = DP_LANE_BW_1_62; + printk(BIOS_ERR, "DP Change lane bw to 1.62Gbps\n"); + edp_info->lt_info.lt_status = DP_LT_START; + ret = EXYNOS_DP_SUCCESS; + } else { + ret = exynos_dp_training_pattern_dis(); + if (ret != EXYNOS_DP_SUCCESS) + printk(BIOS_ERR, "DP training_patter_disable() failed\n"); + + ret = exynos_dp_set_enhanced_mode(edp_info->dpcd_efc); + if (ret != EXYNOS_DP_SUCCESS) + printk(BIOS_ERR, "DP set_enhanced_mode() failed\n"); + + edp_info->lt_info.lt_status = DP_LT_FAIL; + } + + return ret; +} + +static unsigned int exynos_dp_process_clock_recovery(struct edp_device_info + *edp_info) +{ + unsigned int ret; + unsigned char lane_stat; + unsigned char lt_ctl_val[DP_LANE_CNT_4] = {0, }; + unsigned int i; + unsigned char adj_req_sw; + unsigned char adj_req_em; + unsigned char buf[5]; + + mdelay(1); + + ret = exynos_dp_read_dpcd_lane_stat(edp_info, &lane_stat); + if (ret != EXYNOS_DP_SUCCESS) { + printk(BIOS_ERR, "DP read lane status failed\n"); + edp_info->lt_info.lt_status = DP_LT_FAIL; + return ret; + } + + if (lane_stat & DP_LANE_STAT_CR_DONE) { + printk(BIOS_DEBUG,"DP clock Recovery training succeed\n"); + exynos_dp_set_training_pattern(TRAINING_PTN2); + + for (i = 0; i < edp_info->lane_cnt; i++) { + ret = exynos_dp_read_dpcd_adj_req(i, &adj_req_sw, + &adj_req_em); + if (ret != EXYNOS_DP_SUCCESS) { + edp_info->lt_info.lt_status = DP_LT_FAIL; + return ret; + } + + lt_ctl_val[i] = 0; + lt_ctl_val[i] = adj_req_em << 3 | adj_req_sw; + + if ((adj_req_sw == VOLTAGE_LEVEL_3) + || (adj_req_em == PRE_EMPHASIS_LEVEL_3)) { + lt_ctl_val[i] |= MAX_DRIVE_CURRENT_REACH_3 | + MAX_PRE_EMPHASIS_REACH_3; + } + exynos_dp_set_lanex_pre_emphasis(lt_ctl_val[i], i); + } + + buf[0] = DPCD_SCRAMBLING_DISABLED | DPCD_TRAINING_PATTERN_2; + buf[1] = lt_ctl_val[0]; + buf[2] = lt_ctl_val[1]; + buf[3] = lt_ctl_val[2]; + buf[4] = lt_ctl_val[3]; + + ret = exynos_dp_write_bytes_to_dpcd( + DPCD_TRAINING_PATTERN_SET, 5, buf); + if (ret != EXYNOS_DP_SUCCESS) { + printk(BIOS_ERR, "DP write training pattern1 failed\n"); + edp_info->lt_info.lt_status = DP_LT_FAIL; + return ret; + } else + edp_info->lt_info.lt_status = DP_LT_ET; + } else { + for (i = 0; i < edp_info->lane_cnt; i++) { + lt_ctl_val[i] = exynos_dp_get_lanex_pre_emphasis(i); + ret = exynos_dp_read_dpcd_adj_req(i, + &adj_req_sw, &adj_req_em); + if (ret != EXYNOS_DP_SUCCESS) { + printk(BIOS_ERR, "DP read adj req failed\n"); + edp_info->lt_info.lt_status = DP_LT_FAIL; + return ret; + } + + if ((adj_req_sw == VOLTAGE_LEVEL_3) || + (adj_req_em == PRE_EMPHASIS_LEVEL_3)) + ret = exynos_dp_reduce_link_rate(edp_info); + + if ((DRIVE_CURRENT_SET_0_GET(lt_ctl_val[i]) == + adj_req_sw) && + (PRE_EMPHASIS_SET_0_GET(lt_ctl_val[i]) == + adj_req_em)) { + edp_info->lt_info.cr_loop[i]++; + if (edp_info->lt_info.cr_loop[i] == MAX_CR_LOOP) + ret = exynos_dp_reduce_link_rate( + edp_info); + } + + lt_ctl_val[i] = 0; + lt_ctl_val[i] = adj_req_em << 3 | adj_req_sw; + + if ((adj_req_sw == VOLTAGE_LEVEL_3) || + (adj_req_em == PRE_EMPHASIS_LEVEL_3)) { + lt_ctl_val[i] |= MAX_DRIVE_CURRENT_REACH_3 | + MAX_PRE_EMPHASIS_REACH_3; + } + exynos_dp_set_lanex_pre_emphasis(lt_ctl_val[i], i); + } + + ret = exynos_dp_write_bytes_to_dpcd( + DPCD_TRAINING_LANE0_SET, 4, lt_ctl_val); + if (ret != EXYNOS_DP_SUCCESS) { + printk(BIOS_ERR, "DP write training pattern2 failed\n"); + edp_info->lt_info.lt_status = DP_LT_FAIL; + return ret; + } + } + + return ret; +} + +static unsigned int exynos_dp_process_equalizer_training(struct edp_device_info + *edp_info) +{ + unsigned int ret; + unsigned char lane_stat, adj_req_sw, adj_req_em, i; + unsigned char lt_ctl_val[DP_LANE_CNT_4] = {0,}; + unsigned char interlane_aligned = 0; + unsigned char f_bw; + unsigned char f_lane_cnt; + unsigned char sink_stat; + + mdelay(1); + + ret = exynos_dp_read_dpcd_lane_stat(edp_info, &lane_stat); + if (ret != EXYNOS_DP_SUCCESS) { + printk(BIOS_ERR, "DP read lane status failed\n"); + edp_info->lt_info.lt_status = DP_LT_FAIL; + return ret; + } + + printk(BIOS_DEBUG,"DP lane stat : %x\n", lane_stat); + + if (lane_stat & DP_LANE_STAT_CR_DONE) { + printk(BIOS_DEBUG, "DP_LANE_STAT_CR_DONE ok\n"); + ret = exynos_dp_read_byte_from_dpcd(DPCD_LN_ALIGN_UPDATED, + &sink_stat); + if (ret != EXYNOS_DP_SUCCESS) { + edp_info->lt_info.lt_status = DP_LT_FAIL; + printk(BIOS_ERR, "DP read DPCD_LN_ALIGN_UPDATED failed\n"); + return ret; + } + + interlane_aligned = (sink_stat & DPCD_INTERLANE_ALIGN_DONE); + printk(BIOS_DEBUG, "interlane_aligned: %d\n", interlane_aligned); + printk(BIOS_DEBUG, "Check %d lanes\n", edp_info->lane_cnt); + + for (i = 0; i < edp_info->lane_cnt; i++) { + ret = exynos_dp_read_dpcd_adj_req(i, + &adj_req_sw, &adj_req_em); + if (ret != EXYNOS_DP_SUCCESS) { + printk(BIOS_ERR, "DP read adj req 1 failed\n"); + edp_info->lt_info.lt_status = DP_LT_FAIL; + + return ret; + } + + lt_ctl_val[i] = 0; + lt_ctl_val[i] = adj_req_em << 3 | adj_req_sw; + + if ((adj_req_sw == VOLTAGE_LEVEL_3) || + (adj_req_em == PRE_EMPHASIS_LEVEL_3)) { + lt_ctl_val[i] |= MAX_DRIVE_CURRENT_REACH_3; + lt_ctl_val[i] |= MAX_PRE_EMPHASIS_REACH_3; + } + } + + if (((lane_stat&DP_LANE_STAT_CE_DONE) && + (lane_stat&DP_LANE_STAT_SYM_LOCK)) + && (interlane_aligned == DPCD_INTERLANE_ALIGN_DONE)) { + printk(BIOS_DEBUG,"DP Equalizer training succeed\n"); + + f_bw = exynos_dp_get_link_bandwidth(); + f_lane_cnt = exynos_dp_get_lane_count(); + + printk(BIOS_DEBUG,"DP final BandWidth : %x\n", f_bw); + printk(BIOS_DEBUG,"DP final Lane Count : %x\n", f_lane_cnt); + + edp_info->lt_info.lt_status = DP_LT_FINISHED; + + exynos_dp_equalizer_err_link(edp_info); + + } else { + edp_info->lt_info.ep_loop++; + + if (edp_info->lt_info.ep_loop > MAX_EQ_LOOP) { + if (edp_info->lane_bw == DP_LANE_BW_2_70) { + ret = exynos_dp_reduce_link_rate( + edp_info); + } else { + edp_info->lt_info.lt_status = + DP_LT_FAIL; + exynos_dp_equalizer_err_link(edp_info); + } + } else { + for (i = 0; i < edp_info->lane_cnt; i++) + exynos_dp_set_lanex_pre_emphasis( + lt_ctl_val[i], i); + + ret = exynos_dp_write_bytes_to_dpcd( + DPCD_TRAINING_LANE0_SET, + 4, lt_ctl_val); + if (ret != EXYNOS_DP_SUCCESS) { + printk(BIOS_ERR, "DP set lt pattern failed\n"); + edp_info->lt_info.lt_status = + DP_LT_FAIL; + exynos_dp_equalizer_err_link(edp_info); + } + } + } + } else if (edp_info->lane_bw == DP_LANE_BW_2_70) { + ret = exynos_dp_reduce_link_rate(edp_info); + } else { + edp_info->lt_info.lt_status = DP_LT_FAIL; + exynos_dp_equalizer_err_link(edp_info); + } + + return ret; +} + +static unsigned int exynos_dp_sw_link_training(struct edp_device_info *edp_info) +{ + /* the C compiler is almost smart enough to know this gets set. + * But not quite. + */ + unsigned int ret = 0; + int training_finished; + + /* Turn off unnecessary lane */ + if (edp_info->lane_cnt == 1) + exynos_dp_set_analog_power_down(CH1_BLOCK, 1); + + training_finished = 0; + + edp_info->lt_info.lt_status = DP_LT_START; + + /* Process here */ + while (!training_finished) { + switch (edp_info->lt_info.lt_status) { + case DP_LT_START: + ret = exynos_dp_link_start(edp_info); + if (ret != EXYNOS_DP_SUCCESS) { + printk(BIOS_ERR, "DP LT:link start failed\n"); + training_finished = 1; + } + break; + case DP_LT_CR: + ret = exynos_dp_process_clock_recovery(edp_info); + if (ret != EXYNOS_DP_SUCCESS) { + printk(BIOS_ERR, "DP LT:clock recovery failed\n"); + training_finished = 1; + } + break; + case DP_LT_ET: + ret = exynos_dp_process_equalizer_training(edp_info); + if (ret != EXYNOS_DP_SUCCESS) { + printk(BIOS_ERR, "DP LT:equalizer training failed\n"); + training_finished = 1; + } + break; + case DP_LT_FINISHED: + training_finished = 1; + break; + case DP_LT_FAIL: + printk(BIOS_ERR,"DP: %s: DP_LT_FAIL: failed\n", __func__); + training_finished = 1; + ret = -1; + } + } + + return ret; +} + +static unsigned int exynos_dp_set_link_train(struct edp_device_info *edp_info) +{ + unsigned int ret; + + exynos_dp_init_training(); + + ret = exynos_dp_sw_link_training(edp_info); + if (ret != EXYNOS_DP_SUCCESS) + printk(BIOS_ERR, "DP dp_sw_link_traning() failed\n"); + + return ret; +} + +static void exynos_dp_enable_scramble(unsigned int enable) +{ + unsigned char data; + + if (enable) { + exynos_dp_enable_scrambling(DP_ENABLE); + + exynos_dp_read_byte_from_dpcd(DPCD_TRAINING_PATTERN_SET, + &data); + exynos_dp_write_byte_to_dpcd(DPCD_TRAINING_PATTERN_SET, + (u8)(data & ~DPCD_SCRAMBLING_DISABLED)); + } else { + exynos_dp_enable_scrambling(DP_DISABLE); + exynos_dp_read_byte_from_dpcd(DPCD_TRAINING_PATTERN_SET, + &data); + exynos_dp_write_byte_to_dpcd(DPCD_TRAINING_PATTERN_SET, + (u8)(data | DPCD_SCRAMBLING_DISABLED)); + } +} + +static unsigned int exynos_dp_config_video(struct edp_device_info *edp_info) +{ + unsigned int ret = 0; + unsigned int retry_cnt; + + mdelay(1); + + if (edp_info->video_info.master_mode) { + printk(BIOS_ERR, + "DP does not support master mode: bailing out\n"); + return -1; + } else { + /* debug slave */ + exynos_dp_config_video_slave_mode(&edp_info->video_info); + } + + exynos_dp_set_video_color_format(&edp_info->video_info); + + ret = exynos_dp_get_pll_lock_status(); + if (ret != PLL_LOCKED) { + printk(BIOS_ERR, "DP PLL is not locked yet\n"); + return -1; + } + + if (edp_info->video_info.master_mode == 0) { + retry_cnt = 10; + while (retry_cnt) { + ret = exynos_dp_is_slave_video_stream_clock_on(); + if (ret != EXYNOS_DP_SUCCESS) { + if (retry_cnt == 0) { + printk(BIOS_ERR, "DP stream_clock_on failed\n"); + return ret; + } + retry_cnt--; + mdelay(1); + } else { + printk(BIOS_DEBUG, "DP stream_clock succeeds\n"); + break; + } + } + } + + /* Set to use the register calculated M/N video */ + exynos_dp_set_video_cr_mn(CALCULATED_M, 0, 0); + + /* For video bist, Video timing must be generated by register + * not clear if we still need this. We could take it out and it + * might appear to work, then fail strangely. + */ + exynos_dp_set_video_timing_mode(VIDEO_TIMING_FROM_CAPTURE); + + /* we need to be sure this is off. */ + exynos_dp_disable_video_bist(); + + /* Disable video mute */ + exynos_dp_enable_video_mute(DP_DISABLE); + + /* Configure video Master or Slave mode */ + exynos_dp_enable_video_master(edp_info->video_info.master_mode); + + /* Enable video */ + exynos_dp_start_video(); + + if (edp_info->video_info.master_mode == 0) { + retry_cnt = 500; + while (retry_cnt) { + ret = exynos_dp_is_video_stream_on(); + if (ret != EXYNOS_DP_SUCCESS) { + retry_cnt--; + if (retry_cnt == 0) { + printk(BIOS_ERR, "DP Timeout of video stream\n"); + } + } else { + printk(BIOS_DEBUG, "DP video stream is on\n"); + break; + } + /* this is a cheap operation, involving some register + * reads, and no AUX channel IO. A ms. delay is fine. + */ + mdelay(1); + } + } + + return ret; +} + +int exynos_init_dp(struct edp_device_info *edp_info) +{ + unsigned int ret; + + + dp_phy_control(1); + + ret = exynos_dp_init_dp(); + if (ret != EXYNOS_DP_SUCCESS) { + printk(BIOS_ERR, "DP exynos_dp_init_dp() failed\n"); + return ret; + } + + ret = exynos_dp_handle_edid(edp_info); + if (ret != EXYNOS_DP_SUCCESS) { + printk(BIOS_ERR, "EDP handle_edid fail\n"); + return ret; + } + + ret = exynos_dp_set_link_train(edp_info); + if (ret != EXYNOS_DP_SUCCESS) { + printk(BIOS_ERR, "DP link training fail\n"); + return ret; + } + printk(BIOS_DEBUG, "EDP link training ok\n"); + + exynos_dp_enable_scramble(DP_ENABLE); + exynos_dp_enable_rx_to_enhanced_mode(DP_ENABLE); + exynos_dp_enable_enhanced_mode(DP_ENABLE); + + exynos_dp_set_link_bandwidth(edp_info->lane_bw); + exynos_dp_set_lane_count(edp_info->lane_cnt); + + exynos_dp_init_video(); + ret = exynos_dp_config_video(edp_info); + + if (ret != EXYNOS_DP_SUCCESS) { + printk(BIOS_ERR, "Exynos DP init failed\n"); + return ret; + } + printk(BIOS_DEBUG, "Exynos DP init done\n"); + + return ret; +} diff --git a/src/soc/samsung/exynos5420/dp.h b/src/soc/samsung/exynos5420/dp.h new file mode 100644 index 0000000000..9131b0853c --- /dev/null +++ b/src/soc/samsung/exynos5420/dp.h @@ -0,0 +1,1430 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2013 Google Inc. + * Copyright (C) 2012 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* Register map for Exynos5 DP */ + +#ifndef CPU_SAMSUNG_EXYNOS5420_DP_H +#define CPU_SAMSUNG_EXYNOS5420_DP_H + +#include "cpu.h" + +/* DSIM register map */ +struct exynos_dp { + u8 res1[0x10]; + u32 tx_version; + u32 tx_sw_reset; + u32 func_en1; + u32 func_en2; + u32 video_ctl1; + u32 video_ctl2; + u32 video_ctl3; + u32 video_ctl4; + u32 color_blue_cb; + u32 color_green_y; + u32 color_red_cr; + u32 video_ctl8; + u8 res2[0x4]; + u32 video_ctl10; + u32 total_ln_cfg_l; + u32 total_ln_cfg_h; + u32 active_ln_cfg_l; + u32 active_ln_cfg_h; + u32 vfp_cfg; + u32 vsw_cfg; + u32 vbp_cfg; + u32 total_pix_cfg_l; + u32 total_pix_cfg_h; + u32 active_pix_cfg_l; + u32 active_pix_cfg_h; + u32 hfp_cfg_l; + u32 hfp_cfg_h; + u32 hsw_cfg_l; + u32 hsw_cfg_h; + u32 hbp_cfg_l; + u32 hbp_cfg_h; + u32 video_status; + u32 total_ln_sta_l; + u32 total_ln_sta_h; + u32 active_ln_sta_l; + u32 active_ln_sta_h; + + u32 vfp_sta; + u32 vsw_sta; + u32 vbp_sta; + + u32 total_pix_sta_l; + u32 total_pix_sta_h; + u32 active_pix_sta_l; + u32 active_pix_sta_h; + + u32 hfp_sta_l; + u32 hfp_sta_h; + u32 hsw_sta_l; + u32 hsw_sta_h; + u32 hbp_sta_l; + u32 hbp_sta_h; + + u8 res3[0x288]; + + u32 lane_map; + u8 res4[0x10]; + u32 analog_ctl1; + u32 analog_ctl2; + u32 analog_ctl3; + + u32 pll_filter_ctl1; + u32 amp_tuning_ctl; + u8 res5[0xc]; + + u32 aux_hw_retry_ctl; + u8 res6[0x2c]; + u32 int_state; + u32 common_int_sta1; + u32 common_int_sta2; + u32 common_int_sta3; + u32 common_int_sta4; + u8 res7[0x8]; + + u32 int_sta; + u8 res8[0x1c]; + u32 int_ctl; + u8 res9[0x200]; + u32 sys_ctl1; + u32 sys_ctl2; + u32 sys_ctl3; + u32 sys_ctl4; + u32 vid_ctl; + u8 res10[0x2c]; + u32 pkt_send_ctl; + u8 res[0x4]; + u32 hdcp_ctl; + u8 res11[0x34]; + u32 link_bw_set; + + u32 lane_count_set; + u32 training_ptn_set; + u32 ln0_link_training_ctl; + u32 ln1_link_training_ctl; + u32 ln2_link_training_ctl; + u32 ln3_link_training_ctl; + u32 dn_spread_ctl; + u32 hw_link_training_ctl; + u8 res12[0x1c]; + + u32 debug_ctl; + u32 hpd_deglitch_l; + u32 hpd_deglitch_h; + + u8 res13[0x14]; + u32 link_debug_ctl; + + u8 res14[0x1c]; + + u32 m_vid0; + u32 m_vid1; + u32 m_vid2; + u32 n_vid0; + u32 n_vid1; + u32 n_vid2; + u32 m_vid_mon; + u32 pll_ctl; + u32 phy_pd; + u32 phy_test; + u8 res15[0x8]; + + u32 video_fifo_thrd; + u8 res16[0x8]; + u32 audio_margin; + + u32 dn_spread_ctl1; + u32 dn_spread_ctl2; + u8 res17[0x18]; + u32 m_cal_ctl; + u32 m_vid_gen_filter_th; + u8 res18[0x10]; + u32 m_aud_gen_filter_th; + u8 res50[0x4]; + + u32 aux_ch_sta; + u32 aux_err_num; + u32 aux_ch_defer_ctl; + u32 aux_rx_comm; + u32 buffer_data_ctl; + + u32 aux_ch_ctl1; + u32 aux_addr_7_0; + u32 aux_addr_15_8; + u32 aux_addr_19_16; + u32 aux_ch_ctl2; + u8 res19[0x18]; + u32 buf_data0; + u8 res20[0x3c]; + + u32 soc_general_ctl; + u8 res21[0x8c]; + u32 crc_con; + u32 crc_result; + u8 res22[0x8]; + + u32 common_int_mask1; + u32 common_int_mask2; + u32 common_int_mask3; + u32 common_int_mask4; + u32 int_sta_mask1; + u32 int_sta_mask2; + u32 int_sta_mask3; + u32 int_sta_mask4; + u32 int_sta_mask; + u32 crc_result2; + u32 scrambler_reset_cnt; + + u32 pn_inv; + u32 psr_config; + u32 psr_command0; + u32 psr_command1; + u32 psr_crc_mon0; + u32 psr_crc_mon1; + + u8 res24[0x30]; + u32 phy_bist_ctrl; + u8 res25[0xc]; + u32 phy_ctrl; + u8 res26[0x1c]; + u32 test_pattern_gen_en; + u32 test_pattern_gen_ctrl; +}; + +static struct exynos_dp * const exynos_dp0 = (void *)EXYNOS5_DP0_BASE; +static struct exynos_dp * const exynos_dp1 = (void *)EXYNOS5_DP1_BASE; + +/* For DP VIDEO CTL 1 */ +#define VIDEO_EN_MASK (0x01 << 7) +#define VIDEO_MUTE_MASK (0x01 << 6) + +/* For DP VIDEO CTL 4 */ +#define VIDEO_BIST_MASK (0x1 << 3) + +/* EXYNOS_DP_ANALOG_CTL_1 */ +#define SEL_BG_NEW_BANDGAP (0x0 << 6) +#define SEL_BG_INTERNAL_RESISTOR (0x1 << 6) +#define TX_TERMINAL_CTRL_73_OHM (0x0 << 4) +#define TX_TERMINAL_CTRL_61_OHM (0x1 << 4) +#define TX_TERMINAL_CTRL_50_OHM (0x2 << 4) +#define TX_TERMINAL_CTRL_45_OHM (0x3 << 4) +#define SWING_A_30PER_G_INCREASE (0x1 << 3) +#define SWING_A_30PER_G_NORMAL (0x0 << 3) + +/* EXYNOS_DP_ANALOG_CTL_2 */ +#define CPREG_BLEED (0x1 << 4) +#define SEL_24M (0x1 << 3) +#define TX_DVDD_BIT_1_0000V (0x3 << 0) +#define TX_DVDD_BIT_1_0625V (0x4 << 0) +#define TX_DVDD_BIT_1_1250V (0x5 << 0) + +/* EXYNOS_DP_ANALOG_CTL_3 */ +#define DRIVE_DVDD_BIT_1_0000V (0x3 << 5) +#define DRIVE_DVDD_BIT_1_0625V (0x4 << 5) +#define DRIVE_DVDD_BIT_1_1250V (0x5 << 5) +#define SEL_CURRENT_DEFAULT (0x0 << 3) +#define VCO_BIT_000_MICRO (0x0 << 0) +#define VCO_BIT_200_MICRO (0x1 << 0) +#define VCO_BIT_300_MICRO (0x2 << 0) +#define VCO_BIT_400_MICRO (0x3 << 0) +#define VCO_BIT_500_MICRO (0x4 << 0) +#define VCO_BIT_600_MICRO (0x5 << 0) +#define VCO_BIT_700_MICRO (0x6 << 0) +#define VCO_BIT_900_MICRO (0x7 << 0) + +/* EXYNOS_DP_PLL_FILTER_CTL_1 */ +#define PD_RING_OSC (0x1 << 6) +#define AUX_TERMINAL_CTRL_52_OHM (0x3 << 4) +#define AUX_TERMINAL_CTRL_69_OHM (0x2 << 4) +#define AUX_TERMINAL_CTRL_102_OHM (0x1 << 4) +#define AUX_TERMINAL_CTRL_200_OHM (0x0 << 4) +#define TX_CUR1_1X (0x0 << 2) +#define TX_CUR1_2X (0x1 << 2) +#define TX_CUR1_3X (0x2 << 2) +#define TX_CUR_1_MA (0x0 << 0) +#define TX_CUR_2_MA (0x1 << 0) +#define TX_CUR_3_MA (0x2 << 0) +#define TX_CUR_4_MA (0x3 << 0) + +/* EXYNOS_DP_PLL_FILTER_CTL_2 */ +#define CH3_AMP_0_MV (0x3 << 12) +#define CH2_AMP_0_MV (0x3 << 8) +#define CH1_AMP_0_MV (0x3 << 4) +#define CH0_AMP_0_MV (0x3 << 0) + +/* EXYNOS_DP_PLL_CTL */ +#define DP_PLL_PD (0x1 << 7) +#define DP_PLL_RESET (0x1 << 6) +#define DP_PLL_LOOP_BIT_DEFAULT (0x1 << 4) +#define DP_PLL_REF_BIT_1_1250V (0x5 << 0) +#define DP_PLL_REF_BIT_1_2500V (0x7 << 0) + +/* EXYNOS_DP_INT_CTL */ +#define SOFT_INT_CTRL (0x1 << 2) +#define INT_POL (0x1 << 0) + +/* DP TX SW RESET */ +#define RESET_DP_TX (0x01 << 0) + +/* DP FUNC_EN_1 */ +#define MASTER_VID_FUNC_EN_N (0x1 << 7) +#define SLAVE_VID_FUNC_EN_N (0x1 << 5) +#define AUD_FIFO_FUNC_EN_N (0x1 << 4) +#define AUD_FUNC_EN_N (0x1 << 3) +#define HDCP_FUNC_EN_N (0x1 << 2) +#define CRC_FUNC_EN_N (0x1 << 1) +#define SW_FUNC_EN_N (0x1 << 0) + +/* DP FUNC_EN_2 */ +#define SSC_FUNC_EN_N (0x1 << 7) +#define AUX_FUNC_EN_N (0x1 << 2) +#define SERDES_FIFO_FUNC_EN_N (0x1 << 1) +#define LS_CLK_DOMAIN_FUNC_EN_N (0x1 << 0) + +/* EXYNOS_DP_PHY_PD */ +#define PHY_PD (0x1 << 5) +#define AUX_PD (0x1 << 4) +#define CH3_PD (0x1 << 3) +#define CH2_PD (0x1 << 2) +#define CH1_PD (0x1 << 1) +#define CH0_PD (0x1 << 0) + +/* EXYNOS_DP_COMMON_INT_STA_1 */ +#define VSYNC_DET (0x1 << 7) +#define PLL_LOCK_CHG (0x1 << 6) +#define SPDIF_ERR (0x1 << 5) +#define SPDIF_UNSTBL (0x1 << 4) +#define VID_FORMAT_CHG (0x1 << 3) +#define AUD_CLK_CHG (0x1 << 2) +#define VID_CLK_CHG (0x1 << 1) +#define SW_INT (0x1 << 0) + +/* EXYNOS_DP_DEBUG_CTL */ +#define PLL_LOCK (0x1 << 4) +#define F_PLL_LOCK (0x1 << 3) +#define PLL_LOCK_CTRL (0x1 << 2) + +/* EXYNOS_DP_FUNC_EN_2 */ +#define SSC_FUNC_EN_N (0x1 << 7) +#define AUX_FUNC_EN_N (0x1 << 2) +#define SERDES_FIFO_FUNC_EN_N (0x1 << 1) +#define LS_CLK_DOMAIN_FUNC_EN_N (0x1 << 0) + +/* EXYNOS_DP_COMMON_INT_STA_4 */ +#define PSR_ACTIVE (0x1 << 7) +#define PSR_INACTIVE (0x1 << 6) +#define SPDIF_BI_PHASE_ERR (0x1 << 5) +#define HOTPLUG_CHG (0x1 << 2) +#define HPD_LOST (0x1 << 1) +#define PLUG (0x1 << 0) + +/* EXYNOS_DP_INT_STA */ +#define INT_HPD (0x1 << 6) +#define HW_TRAINING_FINISH (0x1 << 5) +#define RPLY_RECEIV (0x1 << 1) +#define AUX_ERR (0x1 << 0) + +/* EXYNOS_DP_SYS_CTL_3 */ +#define HPD_STATUS (0x1 << 6) +#define F_HPD (0x1 << 5) +#define HPD_CTRL (0x1 << 4) +#define HDCP_RDY (0x1 << 3) +#define STRM_VALID (0x1 << 2) +#define F_VALID (0x1 << 1) +#define VALID_CTRL (0x1 << 0) + +/* EXYNOS_DP_AUX_HW_RETRY_CTL */ +#define AUX_BIT_PERIOD_EXPECTED_DELAY(x) (((x) & 0x7) << 8) +#define AUX_HW_RETRY_INTERVAL_MASK (0x3 << 3) +#define AUX_HW_RETRY_INTERVAL_600_MICROSECONDS (0x0 << 3) +#define AUX_HW_RETRY_INTERVAL_800_MICROSECONDS (0x1 << 3) +#define AUX_HW_RETRY_INTERVAL_1000_MICROSECONDS (0x2 << 3) +#define AUX_HW_RETRY_INTERVAL_1800_MICROSECONDS (0x3 << 3) +#define AUX_HW_RETRY_COUNT_SEL(x) (((x) & 0x7) << 0) + +/* EXYNOS_DP_AUX_CH_DEFER_CTL */ +#define DEFER_CTRL_EN (0x1 << 7) +#define DEFER_COUNT(x) (((x) & 0x7f) << 0) + +#define COMMON_INT_MASK_1 (0) +#define COMMON_INT_MASK_2 (0) +#define COMMON_INT_MASK_3 (0) +#define COMMON_INT_MASK_4 (0) +#define INT_STA_MASK (0) + +/* EXYNOS_DP_BUFFER_DATA_CTL */ +#define BUF_CLR (0x1 << 7) +#define BUF_DATA_COUNT(x) (((x) & 0x1f) << 0) + +/* EXYNOS_DP_AUX_ADDR_7_0 */ +#define AUX_ADDR_7_0(x) (((x) >> 0) & 0xff) + +/* EXYNOS_DP_AUX_ADDR_15_8 */ +#define AUX_ADDR_15_8(x) (((x) >> 8) & 0xff) + +/* EXYNOS_DP_AUX_ADDR_19_16 */ +#define AUX_ADDR_19_16(x) (((x) >> 16) & 0x0f) + +/* EXYNOS_DP_AUX_CH_CTL_1 */ +#define AUX_LENGTH(x) (((x - 1) & 0xf) << 4) +#define AUX_TX_COMM_MASK (0xf << 0) +#define AUX_TX_COMM_DP_TRANSACTION (0x1 << 3) +#define AUX_TX_COMM_I2C_TRANSACTION (0x0 << 3) +#define AUX_TX_COMM_MOT (0x1 << 2) +#define AUX_TX_COMM_WRITE (0x0 << 0) +#define AUX_TX_COMM_READ (0x1 << 0) + +/* EXYNOS_DP_AUX_CH_CTL_2 */ +#define ADDR_ONLY (0x1 << 1) +#define AUX_EN (0x1 << 0) + +/* EXYNOS_DP_AUX_CH_STA */ +#define AUX_BUSY (0x1 << 4) +#define AUX_STATUS_MASK (0xf << 0) + +/* EXYNOS_DP_AUX_RX_COMM */ +#define AUX_RX_COMM_I2C_DEFER (0x2 << 2) +#define AUX_RX_COMM_AUX_DEFER (0x2 << 0) + +/* EXYNOS_DP_PHY_TEST */ +#define MACRO_RST (0x1 << 5) +#define CH1_TEST (0x1 << 1) +#define CH0_TEST (0x1 << 0) + +/* EXYNOS_DP_TRAINING_PTN_SET */ +#define SCRAMBLER_TYPE (0x1 << 9) +#define HW_LINK_TRAINING_PATTERN (0x1 << 8) +#define SCRAMBLING_DISABLE (0x1 << 5) +#define SCRAMBLING_ENABLE (0x0 << 5) +#define LINK_QUAL_PATTERN_SET_MASK (0x3 << 2) +#define LINK_QUAL_PATTERN_SET_PRBS7 (0x3 << 2) +#define LINK_QUAL_PATTERN_SET_D10_2 (0x1 << 2) +#define LINK_QUAL_PATTERN_SET_DISABLE (0x0 << 2) +#define SW_TRAINING_PATTERN_SET_MASK (0x3 << 0) +#define SW_TRAINING_PATTERN_SET_PTN2 (0x2 << 0) +#define SW_TRAINING_PATTERN_SET_PTN1 (0x1 << 0) +#define SW_TRAINING_PATTERN_SET_NORMAL (0x0 << 0) + +/* EXYNOS_DP_TOTAL_LINE_CFG */ +#define TOTAL_LINE_CFG_L(x) ((x) & 0xff) +#define TOTAL_LINE_CFG_H(x) ((((x) >> 8)) & 0xff) +#define ACTIVE_LINE_CFG_L(x) ((x) & 0xff) +#define ACTIVE_LINE_CFG_H(x) (((x) >> 8) & 0xff) +#define TOTAL_PIXEL_CFG_L(x) ((x) & 0xff) +#define TOTAL_PIXEL_CFG_H(x) ((((x) >> 8)) & 0xff) +#define ACTIVE_PIXEL_CFG_L(x) ((x) & 0xff) +#define ACTIVE_PIXEL_CFG_H(x) ((((x) >> 8)) & 0xff) + +#define H_F_PORCH_CFG_L(x) ((x) & 0xff) +#define H_F_PORCH_CFG_H(x) ((((x) >> 8)) & 0xff) +#define H_SYNC_PORCH_CFG_L(x) ((x) & 0xff) +#define H_SYNC_PORCH_CFG_H(x) ((((x) >> 8)) & 0xff) +#define H_B_PORCH_CFG_L(x) ((x) & 0xff) +#define H_B_PORCH_CFG_H(x) ((((x) >> 8)) & 0xff) + +/* EXYNOS_DP_LN0_LINK_TRAINING_CTL */ +#define MAX_PRE_EMPHASIS_REACH_0 (0x1 << 5) +#define PRE_EMPHASIS_SET_0_SET(x) (((x) & 0x3) << 3) +#define PRE_EMPHASIS_SET_0_GET(x) (((x) >> 3) & 0x3) +#define PRE_EMPHASIS_SET_0_MASK (0x3 << 3) +#define PRE_EMPHASIS_SET_0_SHIFT (3) +#define PRE_EMPHASIS_SET_0_LEVEL_3 (0x3 << 3) +#define PRE_EMPHASIS_SET_0_LEVEL_2 (0x2 << 3) +#define PRE_EMPHASIS_SET_0_LEVEL_1 (0x1 << 3) +#define PRE_EMPHASIS_SET_0_LEVEL_0 (0x0 << 3) +#define MAX_DRIVE_CURRENT_REACH_0 (0x1 << 2) +#define DRIVE_CURRENT_SET_0_MASK (0x3 << 0) +#define DRIVE_CURRENT_SET_0_SET(x) (((x) & 0x3) << 0) +#define DRIVE_CURRENT_SET_0_GET(x) (((x) >> 0) & 0x3) +#define DRIVE_CURRENT_SET_0_LEVEL_3 (0x3 << 0) +#define DRIVE_CURRENT_SET_0_LEVEL_2 (0x2 << 0) +#define DRIVE_CURRENT_SET_0_LEVEL_1 (0x1 << 0) +#define DRIVE_CURRENT_SET_0_LEVEL_0 (0x0 << 0) + +/* EXYNOS_DP_LN1_LINK_TRAINING_CTL */ +#define MAX_PRE_EMPHASIS_REACH_1 (0x1 << 5) +#define PRE_EMPHASIS_SET_1_SET(x) (((x) & 0x3) << 3) +#define PRE_EMPHASIS_SET_1_GET(x) (((x) >> 3) & 0x3) +#define PRE_EMPHASIS_SET_1_MASK (0x3 << 3) +#define PRE_EMPHASIS_SET_1_SHIFT (3) +#define PRE_EMPHASIS_SET_1_LEVEL_3 (0x3 << 3) +#define PRE_EMPHASIS_SET_1_LEVEL_2 (0x2 << 3) +#define PRE_EMPHASIS_SET_1_LEVEL_1 (0x1 << 3) +#define PRE_EMPHASIS_SET_1_LEVEL_0 (0x0 << 3) +#define MAX_DRIVE_CURRENT_REACH_1 (0x1 << 2) +#define DRIVE_CURRENT_SET_1_MASK (0x3 << 0) +#define DRIVE_CURRENT_SET_1_SET(x) (((x) & 0x3) << 0) +#define DRIVE_CURRENT_SET_1_GET(x) (((x) >> 0) & 0x3) +#define DRIVE_CURRENT_SET_1_LEVEL_3 (0x3 << 0) +#define DRIVE_CURRENT_SET_1_LEVEL_2 (0x2 << 0) +#define DRIVE_CURRENT_SET_1_LEVEL_1 (0x1 << 0) +#define DRIVE_CURRENT_SET_1_LEVEL_0 (0x0 << 0) + +/* EXYNOS_DP_LN2_LINK_TRAINING_CTL */ +#define MAX_PRE_EMPHASIS_REACH_2 (0x1 << 5) +#define PRE_EMPHASIS_SET_2_SET(x) (((x) & 0x3) << 3) +#define PRE_EMPHASIS_SET_2_GET(x) (((x) >> 3) & 0x3) +#define PRE_EMPHASIS_SET_2_MASK (0x3 << 3) +#define PRE_EMPHASIS_SET_2_SHIFT (3) +#define PRE_EMPHASIS_SET_2_LEVEL_3 (0x3 << 3) +#define PRE_EMPHASIS_SET_2_LEVEL_2 (0x2 << 3) +#define PRE_EMPHASIS_SET_2_LEVEL_1 (0x1 << 3) +#define PRE_EMPHASIS_SET_2_LEVEL_0 (0x0 << 3) +#define MAX_DRIVE_CURRENT_REACH_2 (0x1 << 2) +#define DRIVE_CURRENT_SET_2_MASK (0x3 << 0) +#define DRIVE_CURRENT_SET_2_SET(x) (((x) & 0x3) << 0) +#define DRIVE_CURRENT_SET_2_GET(x) (((x) >> 0) & 0x3) +#define DRIVE_CURRENT_SET_2_LEVEL_3 (0x3 << 0) +#define DRIVE_CURRENT_SET_2_LEVEL_2 (0x2 << 0) +#define DRIVE_CURRENT_SET_2_LEVEL_1 (0x1 << 0) +#define DRIVE_CURRENT_SET_2_LEVEL_0 (0x0 << 0) + +/* EXYNOS_DP_LN3_LINK_TRAINING_CTL */ +#define MAX_PRE_EMPHASIS_REACH_3 (0x1 << 5) +#define PRE_EMPHASIS_SET_3_SET(x) (((x) & 0x3) << 3) +#define PRE_EMPHASIS_SET_3_GET(x) (((x) >> 3) & 0x3) +#define PRE_EMPHASIS_SET_3_MASK (0x3 << 3) +#define PRE_EMPHASIS_SET_3_SHIFT (3) +#define PRE_EMPHASIS_SET_3_LEVEL_3 (0x3 << 3) +#define PRE_EMPHASIS_SET_3_LEVEL_2 (0x2 << 3) +#define PRE_EMPHASIS_SET_3_LEVEL_1 (0x1 << 3) +#define PRE_EMPHASIS_SET_3_LEVEL_0 (0x0 << 3) +#define MAX_DRIVE_CURRENT_REACH_3 (0x1 << 2) +#define DRIVE_CURRENT_SET_3_MASK (0x3 << 0) +#define DRIVE_CURRENT_SET_3_SET(x) (((x) & 0x3) << 0) +#define DRIVE_CURRENT_SET_3_GET(x) (((x) >> 0) & 0x3) +#define DRIVE_CURRENT_SET_3_LEVEL_3 (0x3 << 0) +#define DRIVE_CURRENT_SET_3_LEVEL_2 (0x2 << 0) +#define DRIVE_CURRENT_SET_3_LEVEL_1 (0x1 << 0) +#define DRIVE_CURRENT_SET_3_LEVEL_0 (0x0 << 0) + +/* EXYNOS_DP_VIDEO_CTL_10 */ +#define FORMAT_SEL (0x1 << 4) +#define INTERACE_SCAN_CFG (0x1 << 2) +#define INTERACE_SCAN_CFG_SHIFT (2) +#define VSYNC_POLARITY_CFG (0x1 << 1) +#define V_S_POLARITY_CFG_SHIFT (1) +#define HSYNC_POLARITY_CFG (0x1 << 0) +#define H_S_POLARITY_CFG_SHIFT (0) + +/* EXYNOS_DP_SOC_GENERAL_CTL */ +#define AUDIO_MODE_SPDIF_MODE (0x1 << 8) +#define AUDIO_MODE_MASTER_MODE (0x0 << 8) +#define MASTER_VIDEO_INTERLACE_EN (0x1 << 4) +#define VIDEO_MASTER_CLK_SEL (0x1 << 2) +#define VIDEO_MASTER_MODE_EN (0x1 << 1) +#define VIDEO_MODE_MASK (0x1 << 0) +#define VIDEO_MODE_SLAVE_MODE (0x1 << 0) +#define VIDEO_MODE_MASTER_MODE (0x0 << 0) + +/* EXYNOS_DP_VIDEO_CTL_1 */ +#define VIDEO_EN (0x1 << 7) +#define HDCP_VIDEO_MUTE (0x1 << 6) + +/* EXYNOS_DP_VIDEO_CTL_2 */ +#define IN_D_RANGE_MASK (0x1 << 7) +#define IN_D_RANGE_SHIFT (7) +#define IN_D_RANGE_CEA (0x1 << 7) +#define IN_D_RANGE_VESA (0x0 << 7) +#define IN_BPC_MASK (0x7 << 4) +#define IN_BPC_SHIFT (4) +#define IN_BPC_12_BITS (0x3 << 4) +#define IN_BPC_10_BITS (0x2 << 4) +#define IN_BPC_8_BITS (0x1 << 4) +#define IN_BPC_6_BITS (0x0 << 4) +#define IN_COLOR_F_MASK (0x3 << 0) +#define IN_COLOR_F_SHIFT (0) +#define IN_COLOR_F_YCBCR444 (0x2 << 0) +#define IN_COLOR_F_YCBCR422 (0x1 << 0) +#define IN_COLOR_F_RGB (0x0 << 0) + +/* EXYNOS_DP_VIDEO_CTL_3 */ +#define IN_YC_COEFFI_MASK (0x1 << 7) +#define IN_YC_COEFFI_SHIFT (7) +#define IN_YC_COEFFI_ITU709 (0x1 << 7) +#define IN_YC_COEFFI_ITU601 (0x0 << 7) +#define VID_CHK_UPDATE_TYPE_MASK (0x1 << 4) +#define VID_CHK_UPDATE_TYPE_SHIFT (4) +#define VID_CHK_UPDATE_TYPE_1 (0x1 << 4) +#define VID_CHK_UPDATE_TYPE_0 (0x0 << 4) + +/* EXYNOS_DP_TEST_PATTERN_GEN_EN */ +#define TEST_PATTERN_GEN_EN (0x1 << 0) +#define TEST_PATTERN_GEN_DIS (0x0 << 0) + +/* EXYNOS_DP_TEST_PATTERN_GEN_CTRL */ +#define TEST_PATTERN_MODE_COLOR_SQUARE (0x3 << 0) +#define TEST_PATTERN_MODE_BALCK_WHITE_V_LINES (0x2 << 0) +#define TEST_PATTERN_MODE_COLOR_RAMP (0x1 << 0) + +/* EXYNOS_DP_VIDEO_CTL_4 */ +#define BIST_EN (0x1 << 3) +#define BIST_WIDTH_MASK (0x1 << 2) +#define BIST_WIDTH_BAR_32_PIXEL (0x0 << 2) +#define BIST_WIDTH_BAR_64_PIXEL (0x1 << 2) +#define BIST_TYPE_MASK (0x3 << 0) +#define BIST_TYPE_COLOR_BAR (0x0 << 0) +#define BIST_TYPE_WHITE_GRAY_BLACK_BAR (0x1 << 0) +#define BIST_TYPE_MOBILE_WHITE_BAR (0x2 << 0) + +/* EXYNOS_DP_SYS_CTL_1 */ +#define DET_STA (0x1 << 2) +#define FORCE_DET (0x1 << 1) +#define DET_CTRL (0x1 << 0) + +/* EXYNOS_DP_SYS_CTL_2 */ +#define CHA_CRI(x) (((x) & 0xf) << 4) +#define CHA_STA (0x1 << 2) +#define FORCE_CHA (0x1 << 1) +#define CHA_CTRL (0x1 << 0) + +/* EXYNOS_DP_SYS_CTL_3 */ +#define HPD_STATUS (0x1 << 6) +#define F_HPD (0x1 << 5) +#define HPD_CTRL (0x1 << 4) +#define HDCP_RDY (0x1 << 3) +#define STRM_VALID (0x1 << 2) +#define F_VALID (0x1 << 1) +#define VALID_CTRL (0x1 << 0) + +/* EXYNOS_DP_SYS_CTL_4 */ +#define FIX_M_AUD (0x1 << 4) +#define ENHANCED (0x1 << 3) +#define FIX_M_VID (0x1 << 2) +#define M_VID_UPDATE_CTRL (0x3 << 0) + +/* EXYNOS_M_VID_X */ +#define M_VID0_CFG(x) ((x) & 0xff) +#define M_VID1_CFG(x) (((x) >> 8) & 0xff) +#define M_VID2_CFG(x) (((x) >> 16) & 0xff) + +/* EXYNOS_M_VID_X */ +#define N_VID0_CFG(x) ((x) & 0xff) +#define N_VID1_CFG(x) (((x) >> 8) & 0xff) +#define N_VID2_CFG(x) (((x) >> 16) & 0xff) + +/* DPCD_TRAINING_PATTERN_SET */ +#define DPCD_SCRAMBLING_DISABLED (0x1 << 5) +#define DPCD_SCRAMBLING_ENABLED (0x0 << 5) +#define DPCD_TRAINING_PATTERN_2 (0x2 << 0) +#define DPCD_TRAINING_PATTERN_1 (0x1 << 0) +#define DPCD_TRAINING_PATTERN_DISABLED (0x0 << 0) + +/* Definition for DPCD Register */ +#define DPCD_DPCD_REV (0x0000) +#define DPCD_MAX_LINK_RATE (0x0001) +#define DPCD_MAX_LANE_COUNT (0x0002) +#define DPCD_LINK_BW_SET (0x0100) +#define DPCD_LANE_COUNT_SET (0x0101) +#define DPCD_TRAINING_PATTERN_SET (0x0102) +#define DPCD_TRAINING_LANE0_SET (0x0103) +#define DPCD_LANE0_1_STATUS (0x0202) +#define DPCD_LN_ALIGN_UPDATED (0x0204) +#define DPCD_ADJUST_REQUEST_LANE0_1 (0x0206) +#define DPCD_ADJUST_REQUEST_LANE2_3 (0x0207) +#define DPCD_TEST_REQUEST (0x0218) +#define DPCD_TEST_RESPONSE (0x0260) +#define DPCD_TEST_EDID_CHECKSUM (0x0261) +#define DPCD_SINK_POWER_STATE (0x0600) + +/* DPCD_TEST_REQUEST */ +#define DPCD_TEST_EDID_READ (0x1 << 2) + +/* DPCD_TEST_RESPONSE */ +#define DPCD_TEST_EDID_CHECKSUM_WRITE (0x1 << 2) + +/* DPCD_SINK_POWER_STATE */ +#define DPCD_SET_POWER_STATE_D0 (0x1 << 0) +#define DPCD_SET_POWER_STATE_D4 (0x2 << 0) + +/* I2C EDID Chip ID, Slave Address */ +#define I2C_EDID_DEVICE_ADDR (0x50) +#define I2C_E_EDID_DEVICE_ADDR (0x30) +#define EDID_BLOCK_LENGTH (0x80) +#define EDID_HEADER_PATTERN (0x00) +#define EDID_EXTENSION_FLAG (0x7e) +#define EDID_CHECKSUM (0x7f) + +/* DPCD_LANE0_1_STATUS */ +#define DPCD_LANE1_SYMBOL_LOCKED (0x1 << 6) +#define DPCD_LANE1_CHANNEL_EQ_DONE (0x1 << 5) +#define DPCD_LANE1_CR_DONE (0x1 << 4) +#define DPCD_LANE0_SYMBOL_LOCKED (0x1 << 2) +#define DPCD_LANE0_CHANNEL_EQ_DONE (0x1 << 1) +#define DPCD_LANE0_CR_DONE (0x1 << 0) + +/* DPCD_ADJUST_REQUEST_LANE0_1 */ +#define DPCD_PRE_EMPHASIS_LANE1_MASK (0x3 << 6) +#define DPCD_PRE_EMPHASIS_LANE1(x) (((x) >> 6) & 0x3) +#define DPCD_PRE_EMPHASIS_LANE1_LEVEL_3 (0x3 << 6) +#define DPCD_PRE_EMPHASIS_LANE1_LEVEL_2 (0x2 << 6) +#define DPCD_PRE_EMPHASIS_LANE1_LEVEL_1 (0x1 << 6) +#define DPCD_PRE_EMPHASIS_LANE1_LEVEL_0 (0x0 << 6) +#define DPCD_VOLTAGE_SWING_LANE1_MASK (0x3 << 4) +#define DPCD_VOLTAGE_SWING_LANE1(x) (((x) >> 4) & 0x3) +#define DPCD_VOLTAGE_SWING_LANE1_LEVEL_3 (0x3 << 4) +#define DPCD_VOLTAGE_SWING_LANE1_LEVEL_2 (0x2 << 4) +#define DPCD_VOLTAGE_SWING_LANE1_LEVEL_1 (0x1 << 4) +#define DPCD_VOLTAGE_SWING_LANE1_LEVEL_0 (0x0 << 4) +#define DPCD_PRE_EMPHASIS_LANE0_MASK (0x3 << 2) +#define DPCD_PRE_EMPHASIS_LANE0(x) (((x) >> 2) & 0x3) +#define DPCD_PRE_EMPHASIS_LANE0_LEVEL_3 (0x3 << 2) +#define DPCD_PRE_EMPHASIS_LANE0_LEVEL_2 (0x2 << 2) +#define DPCD_PRE_EMPHASIS_LANE0_LEVEL_1 (0x1 << 2) +#define DPCD_PRE_EMPHASIS_LANE0_LEVEL_0 (0x0 << 2) +#define DPCD_VOLTAGE_SWING_LANE0_MASK (0x3 << 0) +#define DPCD_VOLTAGE_SWING_LANE0(x) (((x) >> 0) & 0x3) +#define DPCD_VOLTAGE_SWING_LANE0_LEVEL_3 (0x3 << 0) +#define DPCD_VOLTAGE_SWING_LANE0_LEVEL_2 (0x2 << 0) +#define DPCD_VOLTAGE_SWING_LANE0_LEVEL_1 (0x1 << 0) +#define DPCD_VOLTAGE_SWING_LANE0_LEVEL_0 (0x0 << 0) + +/* DPCD_ADJUST_REQUEST_LANE2_3 */ +#define DPCD_PRE_EMPHASIS_LANE2_MASK (0x3 << 6) +#define DPCD_PRE_EMPHASIS_LANE2(x) (((x) >> 6) & 0x3) +#define DPCD_PRE_EMPHASIS_LANE2_LEVEL_3 (0x3 << 6) +#define DPCD_PRE_EMPHASIS_LANE2_LEVEL_2 (0x2 << 6) +#define DPCD_PRE_EMPHASIS_LANE2_LEVEL_1 (0x1 << 6) +#define DPCD_PRE_EMPHASIS_LANE2_LEVEL_0 (0x0 << 6) +#define DPCD_VOLTAGE_SWING_LANE2_MASK (0x3 << 4) +#define DPCD_VOLTAGE_SWING_LANE2(x) (((x) >> 4) & 0x3) +#define DPCD_VOLTAGE_SWING_LANE2_LEVEL_3 (0x3 << 4) +#define DPCD_VOLTAGE_SWING_LANE2_LEVEL_2 (0x2 << 4) +#define DPCD_VOLTAGE_SWING_LANE2_LEVEL_1 (0x1 << 4) +#define DPCD_VOLTAGE_SWING_LANE2_LEVEL_0 (0x0 << 4) +#define DPCD_PRE_EMPHASIS_LANE3_MASK (0x3 << 2) +#define DPCD_PRE_EMPHASIS_LANE3(x) (((x) >> 2) & 0x3) +#define DPCD_PRE_EMPHASIS_LANE3_LEVEL_3 (0x3 << 2) +#define DPCD_PRE_EMPHASIS_LANE3_LEVEL_2 (0x2 << 2) +#define DPCD_PRE_EMPHASIS_LANE3_LEVEL_1 (0x1 << 2) +#define DPCD_PRE_EMPHASIS_LANE3_LEVEL_0 (0x0 << 2) +#define DPCD_VOLTAGE_SWING_LANE3_MASK (0x3 << 0) +#define DPCD_VOLTAGE_SWING_LANE3(x) (((x) >> 0) & 0x3) +#define DPCD_VOLTAGE_SWING_LANE3_LEVEL_3 (0x3 << 0) +#define DPCD_VOLTAGE_SWING_LANE3_LEVEL_2 (0x2 << 0) +#define DPCD_VOLTAGE_SWING_LANE3_LEVEL_1 (0x1 << 0) +#define DPCD_VOLTAGE_SWING_LANE3_LEVEL_0 (0x0 << 0) + +/* DPCD_LANE_COUNT_SET */ +#define DPCD_ENHANCED_FRAME_EN (0x1 << 7) +#define DPCD_LN_COUNT_SET(x) ((x) & 0x1f) + +/* DPCD_LANE_ALIGN__STATUS_UPDATED */ +#define DPCD_LINK_STATUS_UPDATED (0x1 << 7) +#define DPCD_DOWNSTREAM_PORT_STATUS_CHANGED (0x1 << 6) +#define DPCD_INTERLANE_ALIGN_DONE (0x1 << 0) + +/* DPCD_TRAINING_LANE0_SET */ +#define DPCD_PRE_EMPHASIS_SET_PATTERN_2_LEVEL_3 (0x3 << 3) +#define DPCD_PRE_EMPHASIS_SET_PATTERN_2_LEVEL_2 (0x2 << 3) +#define DPCD_PRE_EMPHASIS_SET_PATTERN_2_LEVEL_1 (0x1 << 3) +#define DPCD_PRE_EMPHASIS_SET_PATTERN_2_LEVEL_0 (0x0 << 3) +#define DPCD_VOLTAGE_SWING_SET_PATTERN_1_LEVEL_3 (0x3 << 0) +#define DPCD_VOLTAGE_SWING_SET_PATTERN_1_LEVEL_2 (0x2 << 0) +#define DPCD_VOLTAGE_SWING_SET_PATTERN_1_LEVEL_1 (0x1 << 0) +#define DPCD_VOLTAGE_SWING_SET_PATTERN_1_LEVEL_0 (0x0 << 0) + +#define DPCD_REQ_ADJ_SWING (0x00) +#define DPCD_REQ_ADJ_EMPHASIS (0x01) + +#define DP_LANE_STAT_CR_DONE (0x01 << 0) +#define DP_LANE_STAT_CE_DONE (0x01 << 1) +#define DP_LANE_STAT_SYM_LOCK (0x01 << 2) + +struct exynos_fb { + u32 vidcon0; + u32 vidcon1; + u32 vidcon2; + u32 vidcon3; + u32 vidtcon0; + u32 vidtcon1; + u32 vidtcon2; + u32 vidtcon3; + u32 wincon0; + u32 wincon1; + u32 wincon2; + u32 wincon3; + u32 wincon4; + + u32 winshmap; + u32 res1; + + u32 winchmap2; + u32 vidosd0a; + u32 vidosd0b; + u32 vidosd0c; + u32 res2; + + u32 vidosd1a; + u32 vidosd1b; + u32 vidosd1c; + u32 vidosd1d; + + u32 vidosd2a; + u32 vidosd2b; + u32 vidosd2c; + u32 vidosd2d; + + u32 vidosd3a; + u32 vidosd3b; + u32 vidosd3c; + u32 res3; + + u32 vidosd4a; + u32 vidosd4b; + u32 vidosd4c; + u32 res4[5]; + + u32 vidw00add0b0; + u32 vidw00add0b1; + u32 vidw01add0b0; + u32 vidw01add0b1; + + u32 vidw02add0b0; + u32 vidw02add0b1; + u32 vidw03add0b0; + u32 vidw03add0b1; + u32 vidw04add0b0; + u32 vidw04add0b1; + u32 res5[2]; + + u32 vidw00add1b0; + u32 vidw00add1b1; + u32 vidw01add1b0; + u32 vidw01add1b1; + + u32 vidw02add1b0; + u32 vidw02add1b1; + u32 vidw03add1b0; + u32 vidw03add1b1; + + u32 vidw04add1b0; + u32 vidw04add1b1; + u32 res7[2]; + + u32 vidw00add2; + u32 vidw01add2; + u32 vidw02add2; + u32 vidw03add2; + u32 vidw04add2; + u32 res8[7]; + + u32 vidintcon0; + u32 vidintcon1; + u32 res9[1]; + + u32 w1keycon0; + u32 w1keycon1; + u32 w2keycon0; + u32 w2keycon1; + u32 w3keycon0; + u32 w3keycon1; + u32 w4keycon0; + u32 w4keycon1; + + u32 w1keyalpha; + u32 w2keyalpha; + u32 w3keyalpha; + u32 w4keyalpha; + + u32 dithmode; + u32 res10[2]; + + u32 win0map; + u32 win1map; + u32 win2map; + u32 win3map; + u32 win4map; + u32 res11[1]; + + u32 wpalcon_h; + u32 wpalcon_l; + + u32 trigcon; + u32 res12[2]; + + u32 i80ifcona0; + u32 i80ifcona1; + u32 i80ifconb0; + u32 i80ifconb1; + + u32 colorgaincon; + u32 res13[2]; + + u32 ldi_cmdcon0; + u32 ldi_cmdcon1; + u32 res14[1]; + + /* To be updated */ + + u8 res15[156]; + u32 dualrgb; + u8 res16[16]; + u32 dp_mie_clkcon; +}; + + +/* LCD IF register offset */ +#define EXYNOS5_LCD_IF_BASE_OFFSET 0x20000 + +static inline u32 exynos_fimd_get_base_offset(void) +{ + return EXYNOS5_LCD_IF_BASE_OFFSET/4; +} + +/* + * Register offsets +*/ +#define EXYNOS_WINCON(x) (x) +#define EXYNOS_VIDOSD(x) (x * 4) +#define EXYNOS_BUFFER_OFFSET(x) (x * 2) +#define EXYNOS_BUFFER_SIZE(x) (x) + +/* + * Bit Definitions +*/ + +/* VIDCON0 */ +#define EXYNOS_VIDCON0_DSI_DISABLE (0 << 30) +#define EXYNOS_VIDCON0_DSI_ENABLE (1 << 30) +#define EXYNOS_VIDCON0_SCAN_PROGRESSIVE (0 << 29) +#define EXYNOS_VIDCON0_SCAN_INTERLACE (1 << 29) +#define EXYNOS_VIDCON0_SCAN_MASK (1 << 29) +#define EXYNOS_VIDCON0_VIDOUT_RGB (0 << 26) +#define EXYNOS_VIDCON0_VIDOUT_ITU (1 << 26) +#define EXYNOS_VIDCON0_VIDOUT_I80LDI0 (2 << 26) +#define EXYNOS_VIDCON0_VIDOUT_I80LDI1 (3 << 26) +#define EXYNOS_VIDCON0_VIDOUT_WB_RGB (4 << 26) +#define EXYNOS_VIDCON0_VIDOUT_WB_I80LDI0 (6 << 26) +#define EXYNOS_VIDCON0_VIDOUT_WB_I80LDI1 (7 << 26) +#define EXYNOS_VIDCON0_VIDOUT_MASK (7 << 26) +#define EXYNOS_VIDCON0_PNRMODE_RGB_P (0 << 17) +#define EXYNOS_VIDCON0_PNRMODE_BGR_P (1 << 17) +#define EXYNOS_VIDCON0_PNRMODE_RGB_S (2 << 17) +#define EXYNOS_VIDCON0_PNRMODE_BGR_S (3 << 17) +#define EXYNOS_VIDCON0_PNRMODE_MASK (3 << 17) +#define EXYNOS_VIDCON0_PNRMODE_SHIFT (17) +#define EXYNOS_VIDCON0_CLKVALUP_ALWAYS (0 << 16) +#define EXYNOS_VIDCON0_CLKVALUP_START_FRAME (1 << 16) +#define EXYNOS_VIDCON0_CLKVALUP_MASK (1 << 16) +#define EXYNOS_VIDCON0_CLKVAL_F(x) (((x) & 0xff) << 6) +#define EXYNOS_VIDCON0_VCLKEN_NORMAL (0 << 5) +#define EXYNOS_VIDCON0_VCLKEN_FREERUN (1 << 5) +#define EXYNOS_VIDCON0_VCLKEN_MASK (1 << 5) +#define EXYNOS_VIDCON0_CLKDIR_DIRECTED (0 << 4) +#define EXYNOS_VIDCON0_CLKDIR_DIVIDED (1 << 4) +#define EXYNOS_VIDCON0_CLKDIR_MASK (1 << 4) +#define EXYNOS_VIDCON0_CLKSEL_HCLK (0 << 2) +#define EXYNOS_VIDCON0_CLKSEL_SCLK (1 << 2) +#define EXYNOS_VIDCON0_CLKSEL_MASK (1 << 2) +#define EXYNOS_VIDCON0_ENVID_ENABLE (1 << 1) +#define EXYNOS_VIDCON0_ENVID_DISABLE (0 << 1) +#define EXYNOS_VIDCON0_ENVID_F_ENABLE (1 << 0) +#define EXYNOS_VIDCON0_ENVID_F_DISABLE (0 << 0) + +/* VIDCON1 */ +#define EXYNOS_VIDCON1_IVCLK_FALLING_EDGE (0 << 7) +#define EXYNOS_VIDCON1_IVCLK_RISING_EDGE (1 << 7) +#define EXYNOS_VIDCON1_IHSYNC_NORMAL (0 << 6) +#define EXYNOS_VIDCON1_IHSYNC_INVERT (1 << 6) +#define EXYNOS_VIDCON1_IVSYNC_NORMAL (0 << 5) +#define EXYNOS_VIDCON1_IVSYNC_INVERT (1 << 5) +#define EXYNOS_VIDCON1_IVDEN_NORMAL (0 << 4) +#define EXYNOS_VIDCON1_IVDEN_INVERT (1 << 4) + +/* VIDCON2 */ +#define EXYNOS_VIDCON2_EN601_DISABLE (0 << 23) +#define EXYNOS_VIDCON2_EN601_ENABLE (1 << 23) +#define EXYNOS_VIDCON2_EN601_MASK (1 << 23) +#define EXYNOS_VIDCON2_WB_DISABLE (0 << 15) +#define EXYNOS_VIDCON2_WB_ENABLE (1 << 15) +#define EXYNOS_VIDCON2_WB_MASK (1 << 15) +#define EXYNOS_VIDCON2_TVFORMATSEL_HW (0 << 14) +#define EXYNOS_VIDCON2_TVFORMATSEL_SW (1 << 14) +#define EXYNOS_VIDCON2_TVFORMATSEL_MASK (1 << 14) +#define EXYNOS_VIDCON2_TVFORMATSEL_YUV422 (1 << 12) +#define EXYNOS_VIDCON2_TVFORMATSEL_YUV444 (2 << 12) +#define EXYNOS_VIDCON2_TVFORMATSEL_YUV_MASK (3 << 12) +#define EXYNOS_VIDCON2_ORGYUV_YCBCR (0 << 8) +#define EXYNOS_VIDCON2_ORGYUV_CBCRY (1 << 8) +#define EXYNOS_VIDCON2_ORGYUV_MASK (1 << 8) +#define EXYNOS_VIDCON2_YUVORD_CBCR (0 << 7) +#define EXYNOS_VIDCON2_YUVORD_CRCB (1 << 7) +#define EXYNOS_VIDCON2_YUVORD_MASK (1 << 7) + +/* PRTCON */ +#define EXYNOS_PRTCON_UPDATABLE (0 << 11) +#define EXYNOS_PRTCON_PROTECT (1 << 11) + +/* VIDTCON0 */ +#define EXYNOS_VIDTCON0_VBPDE(x) (((x) & 0xff) << 24) +#define EXYNOS_VIDTCON0_VBPD(x) (((x) & 0xff) << 16) +#define EXYNOS_VIDTCON0_VFPD(x) (((x) & 0xff) << 8) +#define EXYNOS_VIDTCON0_VSPW(x) (((x) & 0xff) << 0) + +/* VIDTCON1 */ +#define EXYNOS_VIDTCON1_VFPDE(x) (((x) & 0xff) << 24) +#define EXYNOS_VIDTCON1_HBPD(x) (((x) & 0xff) << 16) +#define EXYNOS_VIDTCON1_HFPD(x) (((x) & 0xff) << 8) +#define EXYNOS_VIDTCON1_HSPW(x) (((x) & 0xff) << 0) + +/* VIDTCON2 */ +#define EXYNOS_VIDTCON2_LINEVAL(x) (((x) & 0x7ff) << 11) +#define EXYNOS_VIDTCON2_HOZVAL(x) (((x) & 0x7ff) << 0) +#define EXYNOS_VIDTCON2_LINEVAL_E(x) ((((x) & 0x800) >> 11) << 23) +#define EXYNOS_VIDTCON2_HOZVAL_E(x) ((((x) & 0x800) >> 11) << 22) + +/* Window 0~4 Control - WINCONx */ +#define EXYNOS_WINCON_DATAPATH_DMA (0 << 22) +#define EXYNOS_WINCON_DATAPATH_LOCAL (1 << 22) +#define EXYNOS_WINCON_DATAPATH_MASK (1 << 22) +#define EXYNOS_WINCON_BUFSEL_0 (0 << 20) +#define EXYNOS_WINCON_BUFSEL_1 (1 << 20) +#define EXYNOS_WINCON_BUFSEL_MASK (1 << 20) +#define EXYNOS_WINCON_BUFSEL_SHIFT (20) +#define EXYNOS_WINCON_BUFAUTO_DISABLE (0 << 19) +#define EXYNOS_WINCON_BUFAUTO_ENABLE (1 << 19) +#define EXYNOS_WINCON_BUFAUTO_MASK (1 << 19) +#define EXYNOS_WINCON_BITSWP_DISABLE (0 << 18) +#define EXYNOS_WINCON_BITSWP_ENABLE (1 << 18) +#define EXYNOS_WINCON_BITSWP_SHIFT (18) +#define EXYNOS_WINCON_BYTESWP_DISABLE (0 << 17) +#define EXYNOS_WINCON_BYTESWP_ENABLE (1 << 17) +#define EXYNOS_WINCON_BYTESWP_SHIFT (17) +#define EXYNOS_WINCON_HAWSWP_DISABLE (0 << 16) +#define EXYNOS_WINCON_HAWSWP_ENABLE (1 << 16) +#define EXYNOS_WINCON_HAWSWP_SHIFT (16) +#define EXYNOS_WINCON_WSWP_DISABLE (0 << 15) +#define EXYNOS_WINCON_WSWP_ENABLE (1 << 15) +#define EXYNOS_WINCON_WSWP_SHIFT (15) +#define EXYNOS_WINCON_INRGB_RGB (0 << 13) +#define EXYNOS_WINCON_INRGB_YUV (1 << 13) +#define EXYNOS_WINCON_INRGB_MASK (1 << 13) +#define EXYNOS_WINCON_BURSTLEN_16WORD (0 << 9) +#define EXYNOS_WINCON_BURSTLEN_8WORD (1 << 9) +#define EXYNOS_WINCON_BURSTLEN_4WORD (2 << 9) +#define EXYNOS_WINCON_BURSTLEN_MASK (3 << 9) +#define EXYNOS_WINCON_ALPHA_MULTI_DISABLE (0 << 7) +#define EXYNOS_WINCON_ALPHA_MULTI_ENABLE (1 << 7) +#define EXYNOS_WINCON_BLD_PLANE (0 << 6) +#define EXYNOS_WINCON_BLD_PIXEL (1 << 6) +#define EXYNOS_WINCON_BLD_MASK (1 << 6) +#define EXYNOS_WINCON_BPPMODE_1BPP (0 << 2) +#define EXYNOS_WINCON_BPPMODE_2BPP (1 << 2) +#define EXYNOS_WINCON_BPPMODE_4BPP (2 << 2) +#define EXYNOS_WINCON_BPPMODE_8BPP_PAL (3 << 2) +#define EXYNOS_WINCON_BPPMODE_8BPP (4 << 2) +#define EXYNOS_WINCON_BPPMODE_16BPP_565 (5 << 2) +#define EXYNOS_WINCON_BPPMODE_16BPP_A555 (6 << 2) +#define EXYNOS_WINCON_BPPMODE_18BPP_666 (8 << 2) +#define EXYNOS_WINCON_BPPMODE_18BPP_A665 (9 << 2) +#define EXYNOS_WINCON_BPPMODE_24BPP_888 (0xb << 2) +#define EXYNOS_WINCON_BPPMODE_24BPP_A887 (0xc << 2) +#define EXYNOS_WINCON_BPPMODE_32BPP (0xd << 2) +#define EXYNOS_WINCON_BPPMODE_16BPP_A444 (0xe << 2) +#define EXYNOS_WINCON_BPPMODE_15BPP_555 (0xf << 2) +#define EXYNOS_WINCON_BPPMODE_MASK (0xf << 2) +#define EXYNOS_WINCON_BPPMODE_SHIFT (2) +#define EXYNOS_WINCON_ALPHA0_SEL (0 << 1) +#define EXYNOS_WINCON_ALPHA1_SEL (1 << 1) +#define EXYNOS_WINCON_ALPHA_SEL_MASK (1 << 1) +#define EXYNOS_WINCON_ENWIN_DISABLE (0 << 0) +#define EXYNOS_WINCON_ENWIN_ENABLE (1 << 0) + +/* WINCON1 special */ +#define EXYNOS_WINCON1_VP_DISABLE (0 << 24) +#define EXYNOS_WINCON1_VP_ENABLE (1 << 24) +#define EXYNOS_WINCON1_LOCALSEL_FIMC1 (0 << 23) +#define EXYNOS_WINCON1_LOCALSEL_VP (1 << 23) +#define EXYNOS_WINCON1_LOCALSEL_MASK (1 << 23) + +/* WINSHMAP */ +#define EXYNOS_WINSHMAP_PROTECT(x) (((x) & 0x1f) << 10) +#define EXYNOS_WINSHMAP_CH_ENABLE(x) (1 << (x)) +#define EXYNOS_WINSHMAP_CH_DISABLE(x) (1 << (x)) +#define EXYNOS_WINSHMAP_LOCAL_ENABLE(x) (0x20 << (x)) +#define EXYNOS_WINSHMAP_LOCAL_DISABLE(x) (0x20 << (x)) + +/* VIDOSDxA, VIDOSDxB */ +#define EXYNOS_VIDOSD_LEFT_X(x) (((x) & 0x7ff) << 11) +#define EXYNOS_VIDOSD_TOP_Y(x) (((x) & 0x7ff) << 0) +#define EXYNOS_VIDOSD_RIGHT_X(x) (((x) & 0x7ff) << 11) +#define EXYNOS_VIDOSD_BOTTOM_Y(x) (((x) & 0x7ff) << 0) +#define EXYNOS_VIDOSD_RIGHT_X_E(x) (((x) & 0x1) << 23) +#define EXYNOS_VIDOSD_BOTTOM_Y_E(x) (((x) & 0x1) << 22) + +/* VIDOSD0C, VIDOSDxD */ +#define EXYNOS_VIDOSD_SIZE(x) (((x) & 0xffffff) << 0) + +/* VIDOSDxC (1~4) */ +#define EXYNOS_VIDOSD_ALPHA0_R(x) (((x) & 0xf) << 20) +#define EXYNOS_VIDOSD_ALPHA0_G(x) (((x) & 0xf) << 16) +#define EXYNOS_VIDOSD_ALPHA0_B(x) (((x) & 0xf) << 12) +#define EXYNOS_VIDOSD_ALPHA1_R(x) (((x) & 0xf) << 8) +#define EXYNOS_VIDOSD_ALPHA1_G(x) (((x) & 0xf) << 4) +#define EXYNOS_VIDOSD_ALPHA1_B(x) (((x) & 0xf) << 0) +#define EXYNOS_VIDOSD_ALPHA0_SHIFT (12) +#define EXYNOS_VIDOSD_ALPHA1_SHIFT (0) + +/* Start Address */ +#define EXYNOS_VIDADDR_START_VBANK(x) (((x) & 0xff) << 24) +#define EXYNOS_VIDADDR_START_VBASEU(x) (((x) & 0xffffff) << 0) + +/* End Address */ +#define EXYNOS_VIDADDR_END_VBASEL(x) (((x) & 0xffffff) << 0) + +/* Buffer Size */ +#define EXYNOS_VIDADDR_OFFSIZE(x) (((x) & 0x1fff) << 13) +#define EXYNOS_VIDADDR_PAGEWIDTH(x) (((x) & 0x1fff) << 0) +#define EXYNOS_VIDADDR_OFFSIZE_E(x) ((((x) & 0x2000) >> 13) << 27) +#define EXYNOS_VIDADDR_PAGEWIDTH_E(x) ((((x) & 0x2000) >> 13) << 26) + +/* WIN Color Map */ +#define EXYNOS_WINMAP_COLOR(x) ((x) & 0xffffff) + +/* VIDINTCON0 */ +#define EXYNOS_VIDINTCON0_SYSMAINCON_DISABLE (0 << 19) +#define EXYNOS_VIDINTCON0_SYSMAINCON_ENABLE (1 << 19) +#define EXYNOS_VIDINTCON0_SYSSUBCON_DISABLE (0 << 18) +#define EXYNOS_VIDINTCON0_SYSSUBCON_ENABLE (1 << 18) +#define EXYNOS_VIDINTCON0_SYSIFDONE_DISABLE (0 << 17) +#define EXYNOS_VIDINTCON0_SYSIFDONE_ENABLE (1 << 17) +#define EXYNOS_VIDINTCON0_FRAMESEL0_BACK (0 << 15) +#define EXYNOS_VIDINTCON0_FRAMESEL0_VSYNC (1 << 15) +#define EXYNOS_VIDINTCON0_FRAMESEL0_ACTIVE (2 << 15) +#define EXYNOS_VIDINTCON0_FRAMESEL0_FRONT (3 << 15) +#define EXYNOS_VIDINTCON0_FRAMESEL0_MASK (3 << 15) +#define EXYNOS_VIDINTCON0_FRAMESEL1_NONE (0 << 13) +#define EXYNOS_VIDINTCON0_FRAMESEL1_BACK (1 << 13) +#define EXYNOS_VIDINTCON0_FRAMESEL1_VSYNC (2 << 13) +#define EXYNOS_VIDINTCON0_FRAMESEL1_FRONT (3 << 13) +#define EXYNOS_VIDINTCON0_INTFRMEN_DISABLE (0 << 12) +#define EXYNOS_VIDINTCON0_INTFRMEN_ENABLE (1 << 12) +#define EXYNOS_VIDINTCON0_FIFOSEL_WIN4 (1 << 11) +#define EXYNOS_VIDINTCON0_FIFOSEL_WIN3 (1 << 10) +#define EXYNOS_VIDINTCON0_FIFOSEL_WIN2 (1 << 9) +#define EXYNOS_VIDINTCON0_FIFOSEL_WIN1 (1 << 6) +#define EXYNOS_VIDINTCON0_FIFOSEL_WIN0 (1 << 5) +#define EXYNOS_VIDINTCON0_FIFOSEL_ALL (0x73 << 5) +#define EXYNOS_VIDINTCON0_FIFOSEL_MASK (0x73 << 5) +#define EXYNOS_VIDINTCON0_FIFOLEVEL_25 (0 << 2) +#define EXYNOS_VIDINTCON0_FIFOLEVEL_50 (1 << 2) +#define EXYNOS_VIDINTCON0_FIFOLEVEL_75 (2 << 2) +#define EXYNOS_VIDINTCON0_FIFOLEVEL_EMPTY (3 << 2) +#define EXYNOS_VIDINTCON0_FIFOLEVEL_FULL (4 << 2) +#define EXYNOS_VIDINTCON0_FIFOLEVEL_MASK (7 << 2) +#define EXYNOS_VIDINTCON0_INTFIFO_DISABLE (0 << 1) +#define EXYNOS_VIDINTCON0_INTFIFO_ENABLE (1 << 1) +#define EXYNOS_VIDINTCON0_INT_DISABLE (0 << 0) +#define EXYNOS_VIDINTCON0_INT_ENABLE (1 << 0) +#define EXYNOS_VIDINTCON0_INT_MASK (1 << 0) + +/* VIDINTCON1 */ +#define EXYNOS_VIDINTCON1_INTVPPEND (1 << 5) +#define EXYNOS_VIDINTCON1_INTI80PEND (1 << 2) +#define EXYNOS_VIDINTCON1_INTFRMPEND (1 << 1) +#define EXYNOS_VIDINTCON1_INTFIFOPEND (1 << 0) + +/* WINMAP */ +#define EXYNOS_WINMAP_ENABLE (1 << 24) + +/* WxKEYCON0 (1~4) */ +#define EXYNOS_KEYCON0_KEYBLEN_DISABLE (0 << 26) +#define EXYNOS_KEYCON0_KEYBLEN_ENABLE (1 << 26) +#define EXYNOS_KEYCON0_KEY_DISABLE (0 << 25) +#define EXYNOS_KEYCON0_KEY_ENABLE (1 << 25) +#define EXYNOS_KEYCON0_DIRCON_MATCH_FG (0 << 24) +#define EXYNOS_KEYCON0_DIRCON_MATCH_BG (1 << 24) +#define EXYNOS_KEYCON0_COMPKEY(x) (((x) & 0xffffff) << 0) + +/* WxKEYCON1 (1~4) */ +#define EXYNOS_KEYCON1_COLVAL(x) (((x) & 0xffffff) << 0) + +/* DUALRGB */ +#define EXYNOS_DUALRGB_BYPASS_SINGLE (0x00 << 0) +#define EXYNOS_DUALRGB_BYPASS_DUAL (0x01 << 0) +#define EXYNOS_DUALRGB_MIE_DUAL (0x10 << 0) +#define EXYNOS_DUALRGB_MIE_SINGLE (0x11 << 0) +#define EXYNOS_DUALRGB_LINESPLIT (0x0 << 2) +#define EXYNOS_DUALRGB_FRAMESPLIT (0x1 << 2) +#define EXYNOS_DUALRGB_SUB_CNT(x) ((x & 0xfff) << 4) +#define EXYNOS_DUALRGB_VDEN_EN_DISABLE (0x0 << 16) +#define EXYNOS_DUALRGB_VDEN_EN_ENABLE (0x1 << 16) +#define EXYNOS_DUALRGB_MAIN_CNT(x) ((x & 0xfff) << 18) + +/* I80IFCONA0 and I80IFCONA1 */ +#define EXYNOS_LCD_CS_SETUP(x) (((x) & 0xf) << 16) +#define EXYNOS_LCD_WR_SETUP(x) (((x) & 0xf) << 12) +#define EXYNOS_LCD_WR_ACT(x) (((x) & 0xf) << 8) +#define EXYNOS_LCD_WR_HOLD(x) (((x) & 0xf) << 4) +#define EXYNOS_RSPOL_LOW (0 << 2) +#define EXYNOS_RSPOL_HIGH (1 << 2) +#define EXYNOS_I80IFEN_DISABLE (0 << 0) +#define EXYNOS_I80IFEN_ENABLE (1 << 0) + +/* TRIGCON */ +#define EXYNOS_I80SOFT_TRIG_EN (1 << 0) +#define EXYNOS_I80START_TRIG (1 << 1) +#define EXYNOS_I80STATUS_TRIG_DONE (1 << 2) + +/* DP_MIE_CLKCON */ +#define EXYNOS_DP_MIE_DISABLE (0 << 0) +#define EXYNOS_DP_CLK_ENABLE (1 << 1) +#define EXYNOS_MIE_CLK_ENABLE (3 << 0) + +#define DP_TIMEOUT_LOOP_COUNT 1000 +#define MAX_CR_LOOP 5 +#define MAX_EQ_LOOP 4 + +#define EXYNOS_DP_SUCCESS 0 + +enum { + DP_DISABLE, + DP_ENABLE, +}; + +struct edp_disp_info { + char *name; + unsigned int h_total; + unsigned int h_res; + unsigned int h_sync_width; + unsigned int h_back_porch; + unsigned int h_front_porch; + unsigned int v_total; + unsigned int v_res; + unsigned int v_sync_width; + unsigned int v_back_porch; + unsigned int v_front_porch; + unsigned int v_sync_rate; +}; + +struct edp_link_train_info { + unsigned int lt_status; + unsigned int ep_loop; + unsigned int cr_loop[4]; +}; + +struct edp_video_info { + unsigned int master_mode; + unsigned int bist_mode; + unsigned int bist_pattern; + unsigned int h_sync_polarity; + unsigned int v_sync_polarity; + unsigned int interlaced; + unsigned int color_space; + unsigned int dynamic_range; + unsigned int ycbcr_coeff; + unsigned int color_depth; +}; + +struct edp_device_info { + struct edp_disp_info disp_info; + struct edp_link_train_info lt_info; + struct edp_video_info video_info; + + /*below info get from panel during training*/ + u8 lane_bw; + u8 lane_cnt; + u8 dpcd_rev; + /*support enhanced frame cap */ + u8 dpcd_efc; + u8 *raw_edid; +}; + +enum analog_power_block { + AUX_BLOCK, + CH0_BLOCK, + CH1_BLOCK, + CH2_BLOCK, + CH3_BLOCK, + ANALOG_TOTAL, + POWER_ALL +}; + +enum pll_status { + PLL_UNLOCKED = 0, + PLL_LOCKED +}; + +enum { + COLOR_RGB, + COLOR_YCBCR422, + COLOR_YCBCR444 +}; + +enum { + VESA, + CEA +}; + +enum { + COLOR_YCBCR601, + COLOR_YCBCR709 +}; + +enum { + COLOR_6, + COLOR_8, + COLOR_10, + COLOR_12 +}; + +enum { + DP_LANE_BW_1_62 = 0x06, + DP_LANE_BW_2_70 = 0x0a, +}; + +enum { + DP_LANE_CNT_1 = 1, + DP_LANE_CNT_2 = 2, + DP_LANE_CNT_4 = 4, +}; + +enum { + DP_DPCD_REV_10 = 0x10, + DP_DPCD_REV_11 = 0x11, +}; + +enum { + DP_LT_NONE, + DP_LT_START, + DP_LT_CR, + DP_LT_ET, + DP_LT_FINISHED, + DP_LT_FAIL, +}; + +enum { + PRE_EMPHASIS_LEVEL_0, + PRE_EMPHASIS_LEVEL_1, + PRE_EMPHASIS_LEVEL_2, + PRE_EMPHASIS_LEVEL_3, +}; + +enum { + PRBS7, + D10_2, + TRAINING_PTN1, + TRAINING_PTN2, + DP_NONE +}; + +enum { + VOLTAGE_LEVEL_0, + VOLTAGE_LEVEL_1, + VOLTAGE_LEVEL_2, + VOLTAGE_LEVEL_3, +}; + +enum pattern_type { + NO_PATTERN, + COLOR_RAMP, + BALCK_WHITE_V_LINES, + COLOR_SQUARE, + INVALID_PATTERN, + COLORBAR_32, + COLORBAR_64, + WHITE_GRAY_BALCKBAR_32, + WHITE_GRAY_BALCKBAR_64, + MOBILE_WHITEBAR_32, + MOBILE_WHITEBAR_64 +}; + +enum { + CALCULATED_M, + REGISTER_M +}; + +enum { + VIDEO_TIMING_FROM_CAPTURE, + VIDEO_TIMING_FROM_REGISTER +}; + + +struct exynos_dp_platform_data { + struct edp_device_info *edp_dev_info; +}; + + +int exynos_init_dp(struct edp_device_info *edp_info); + +void exynos_set_dp_platform_data(struct exynos_dp_platform_data *pd); + +void exynos_dp_disable_video_bist(void); +void exynos_dp_enable_video_mute(unsigned int enable); +void exynos_dp_reset(void); +void exynos_dp_enable_sw_func(unsigned int enable); +unsigned int exynos_dp_set_analog_power_down(unsigned int block, u32 enable); +unsigned int exynos_dp_get_pll_lock_status(void); +int exynos_dp_init_analog_func(void); +void exynos_dp_init_hpd(void); +void exynos_dp_init_aux(void); +void exynos_dp_config_interrupt(void); +unsigned int exynos_dp_get_plug_in_status(void); +unsigned int exynos_dp_detect_hpd(void); +unsigned int exynos_dp_start_aux_transaction(void); +unsigned int exynos_dp_write_byte_to_dpcd(u32 reg_addr, + u8 data); +unsigned int exynos_dp_read_byte_from_dpcd(u32 reg_addr, + u8 *data); +unsigned int exynos_dp_write_bytes_to_dpcd(u32 reg_addr, + unsigned int count, + u8 data[]); +u32 exynos_dp_read_bytes_from_dpcd( unsigned int reg_addr, + unsigned int count, + u8 data[]); +int exynos_dp_select_i2c_device( u32 device_addr, + u32 reg_addr); +int exynos_dp_read_byte_from_i2c(u32 device_addr, + u32 reg_addr, unsigned int *data); +int exynos_dp_read_bytes_from_i2c(u32 device_addr, + u32 reg_addr, unsigned int count, + u8 edid[]); +void exynos_dp_reset_macro(void); +void exynos_dp_set_link_bandwidth(u8 bwtype); +u8 exynos_dp_get_link_bandwidth(void); +void exynos_dp_set_lane_count(u8 count); +unsigned int exynos_dp_get_lane_count(void); +u8 exynos_dp_get_lanex_pre_emphasis(u8 lanecnt); +void exynos_dp_set_lane_pre_emphasis(unsigned int level, + u8 lanecnt); +void exynos_dp_set_lanex_pre_emphasis(u8 request_val, + u8 lanecnt); +void exynos_dp_set_training_pattern(unsigned int pattern); +void exynos_dp_enable_enhanced_mode(u8 enable); +void exynos_dp_enable_scrambling(unsigned int enable); +int exynos_dp_init_video(void); +void exynos_dp_config_video_slave_mode(struct edp_video_info *video_info); +void exynos_dp_set_video_color_format(struct edp_video_info *video_info); +int exynos_dp_config_video_bist(struct edp_device_info *edp_info); +unsigned int exynos_dp_is_slave_video_stream_clock_on(void); +void exynos_dp_set_video_cr_mn(unsigned int type, unsigned int m_value, + unsigned int n_value); +void exynos_dp_set_video_timing_mode(unsigned int type); +void exynos_dp_enable_video_master(unsigned int enable); +void exynos_dp_start_video(void); +unsigned int exynos_dp_is_video_stream_on(void); +void exynos_dp_set_base_addr(void); +void dp_phy_control(unsigned int enable); + +#endif diff --git a/src/soc/samsung/exynos5420/dp_lowlevel.c b/src/soc/samsung/exynos5420/dp_lowlevel.c new file mode 100644 index 0000000000..2b2418ec21 --- /dev/null +++ b/src/soc/samsung/exynos5420/dp_lowlevel.c @@ -0,0 +1,1192 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * + * Author: Donghwa Lee <dh09.lee@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 + */ + +#include <delay.h> +#include <stdlib.h> +#include <string.h> +#include <timer.h> +#include <arch/io.h> +#include <console/console.h> +#include "dp.h" +#include "fimd.h" +#include "i2c.h" +#include "power.h" +#include "sysreg.h" +#include "timer.h" + +/* FIXME: I think the DP controller shouldn't be hardcoded here... */ +static struct exynos_dp * const dp_regs = (void *)EXYNOS5_DP1_BASE; + +/* for debugging, it's nice to get control on a per-file basis. + * I had a bit of a discussion with myself (boring!) about + * how to do this and for the moment this is the easiest way. + * These debugging statements allowed me to find the final bugs. + */ + +#if 0 +static inline void fwadl(unsigned long l,void *v) { + writel(l, v); + printk(BIOS_SPEW, "W %p %p\n", v, (void *)l); +} +#define lwrite32(a,b) fwadl((unsigned long)(a), (void *)(b)) + +static inline unsigned long fradl(void *v) { + unsigned long l = readl(v); + printk(BIOS_SPEW, "R %p %p\n", v, (void *)l); + return l; +} + +#define lread32(a) fradl((void *)(a)) +#else +#define lwrite32(a,b) write32((unsigned long)(a), (void *)(b)) +#define lread32(a) read32((void *)(a)) +#endif + +static void exynos_dp_enable_video_input(u32 enable) +{ + u32 reg; + + reg = lread32(&dp_regs->video_ctl1); + reg &= ~VIDEO_EN_MASK; + + /* enable video input*/ + if (enable) + reg |= VIDEO_EN_MASK; + + lwrite32(reg, &dp_regs->video_ctl1); + + return; +} + +void exynos_dp_disable_video_bist(void) +{ + u32 reg; + reg = lread32(&dp_regs->video_ctl4); + reg &= ~VIDEO_BIST_MASK; + lwrite32(reg, &dp_regs->video_ctl4); +} + +void exynos_dp_enable_video_mute(unsigned int enable) +{ + u32 reg; + + reg = lread32(&dp_regs->video_ctl1); + reg &= ~(VIDEO_MUTE_MASK); + if (enable) + reg |= VIDEO_MUTE_MASK; + + lwrite32(reg, &dp_regs->video_ctl1); + + return; +} + + +static void exynos_dp_init_analog_param(void) +{ + u32 reg; + + /* + * Set termination + * Normal bandgap, Normal swing, Tx terminal registor 61 ohm + * 24M Phy clock, TX digital logic power is 100:1.0625V + */ + reg = SEL_BG_NEW_BANDGAP | TX_TERMINAL_CTRL_61_OHM | + SWING_A_30PER_G_NORMAL; + lwrite32(reg, &dp_regs->analog_ctl1); + + reg = SEL_24M | TX_DVDD_BIT_1_0625V; + lwrite32(reg, &dp_regs->analog_ctl2); + + /* + * Set power source for internal clk driver to 1.0625v. + * Select current reference of TX driver current to 00:Ipp/2+Ic/2. + * Set VCO range of PLL +- 0uA + */ + reg = DRIVE_DVDD_BIT_1_0625V | SEL_CURRENT_DEFAULT | VCO_BIT_000_MICRO; + lwrite32(reg, &dp_regs->analog_ctl3); + + /* + * Set AUX TX terminal resistor to 102 ohm + * Set AUX channel amplitude control + */ + reg = PD_RING_OSC | AUX_TERMINAL_CTRL_52_OHM | TX_CUR1_2X | TX_CUR_4_MA; + lwrite32(reg, &dp_regs->pll_filter_ctl1); + + /* + * PLL loop filter bandwidth + * For 2.7Gbps: 175KHz, For 1.62Gbps: 234KHz + * PLL digital power select: 1.2500V + */ + reg = CH3_AMP_0_MV | CH2_AMP_0_MV | CH1_AMP_0_MV | CH0_AMP_0_MV; + + lwrite32(reg, &dp_regs->amp_tuning_ctl); + + /* + * PLL loop filter bandwidth + * For 2.7Gbps: 175KHz, For 1.62Gbps: 234KHz + * PLL digital power select: 1.1250V + */ + reg = DP_PLL_LOOP_BIT_DEFAULT | DP_PLL_REF_BIT_1_1250V; + lwrite32(reg, &dp_regs->pll_ctl); +} + +static void exynos_dp_init_interrupt(void) +{ + /* Set interrupt registers to initial states */ + + /* + * Disable interrupt + * INT pin assertion polarity. It must be configured + * correctly according to ICU setting. + * 1 = assert high, 0 = assert low + */ + lwrite32(INT_POL, &dp_regs->int_ctl); + + /* Clear pending regisers */ + lwrite32(0xff, &dp_regs->common_int_sta1); + lwrite32(0xff, &dp_regs->common_int_sta2); + lwrite32(0xff, &dp_regs->common_int_sta3); + lwrite32(0xff, &dp_regs->common_int_sta4); + lwrite32(0xff, &dp_regs->int_sta); + + /* 0:mask,1: unmask */ + lwrite32(0x00, &dp_regs->int_sta_mask1); + lwrite32(0x00, &dp_regs->int_sta_mask2); + lwrite32(0x00, &dp_regs->int_sta_mask3); + lwrite32(0x00, &dp_regs->int_sta_mask4); + lwrite32(0x00, &dp_regs->int_sta_mask); +} + +void exynos_dp_reset(void) +{ + u32 reg_func_1; + + /*dp tx sw reset*/ + lwrite32(RESET_DP_TX, &dp_regs->tx_sw_reset); + + exynos_dp_enable_video_input(DP_DISABLE); + exynos_dp_disable_video_bist(); + exynos_dp_enable_video_mute(DP_DISABLE); + + /* software reset */ + reg_func_1 = MASTER_VID_FUNC_EN_N | SLAVE_VID_FUNC_EN_N | + AUD_FIFO_FUNC_EN_N | AUD_FUNC_EN_N | + HDCP_FUNC_EN_N | SW_FUNC_EN_N; + + lwrite32(reg_func_1, &dp_regs->func_en1); + lwrite32(reg_func_1, &dp_regs->func_en2); + + mdelay(1); + + exynos_dp_init_analog_param(); + exynos_dp_init_interrupt(); + + return; +} + +void exynos_dp_enable_sw_func(unsigned int enable) +{ + u32 reg; + + reg = lread32(&dp_regs->func_en1); + reg &= ~(SW_FUNC_EN_N); + + if (!enable) + reg |= SW_FUNC_EN_N; + + lwrite32(reg, &dp_regs->func_en1); + + return; +} + +unsigned int exynos_dp_set_analog_power_down(unsigned int block, u32 enable) +{ + u32 reg; + + reg = lread32(&dp_regs->phy_pd); + switch (block) { + case AUX_BLOCK: + reg &= ~(AUX_PD); + if (enable) + reg |= AUX_PD; + break; + case CH0_BLOCK: + reg &= ~(CH0_PD); + if (enable) + reg |= CH0_PD; + break; + case CH1_BLOCK: + reg &= ~(CH1_PD); + if (enable) + reg |= CH1_PD; + break; + case CH2_BLOCK: + reg &= ~(CH2_PD); + if (enable) + reg |= CH2_PD; + break; + case CH3_BLOCK: + reg &= ~(CH3_PD); + if (enable) + reg |= CH3_PD; + break; + case ANALOG_TOTAL: + reg &= ~PHY_PD; + if (enable) + reg |= PHY_PD; + break; + case POWER_ALL: + reg &= ~(PHY_PD | AUX_PD | CH0_PD | CH1_PD | CH2_PD | + CH3_PD); + if (enable) + reg |= (PHY_PD | AUX_PD | CH0_PD | CH1_PD | + CH2_PD | CH3_PD); + break; + default: + printk(BIOS_ERR, "DP undefined block number : %d\n", block); + return -1; + } + + lwrite32(reg, &dp_regs->phy_pd); + + return 0; +} + +unsigned int exynos_dp_get_pll_lock_status(void) +{ + u32 reg; + + reg = lread32(&dp_regs->debug_ctl); + + if (reg & PLL_LOCK) + return PLL_LOCKED; + else + return PLL_UNLOCKED; +} + +static void exynos_dp_set_pll_power(unsigned int enable) +{ + u32 reg; + + reg = lread32(&dp_regs->pll_ctl); + reg &= ~(DP_PLL_PD); + + if (!enable) + reg |= DP_PLL_PD; + + lwrite32(reg, &dp_regs->pll_ctl); +} + +int exynos_dp_init_analog_func(void) +{ + int ret = EXYNOS_DP_SUCCESS; + unsigned int retry_cnt = 10; + u32 reg; + + /*Power On All Analog block */ + exynos_dp_set_analog_power_down(POWER_ALL, DP_DISABLE); + + reg = PLL_LOCK_CHG; + lwrite32(reg, &dp_regs->common_int_sta1); + + reg = lread32(&dp_regs->debug_ctl); + reg &= ~(F_PLL_LOCK | PLL_LOCK_CTRL); + lwrite32(reg, &dp_regs->debug_ctl); + + /*Assert DP PLL Reset*/ + reg = lread32(&dp_regs->pll_ctl); + reg |= DP_PLL_RESET; + lwrite32(reg, &dp_regs->pll_ctl); + + mdelay(1); + + /*Deassert DP PLL Reset*/ + reg = lread32(&dp_regs->pll_ctl); + reg &= ~(DP_PLL_RESET); + lwrite32(reg, &dp_regs->pll_ctl); + + exynos_dp_set_pll_power(DP_ENABLE); + + while (exynos_dp_get_pll_lock_status() == PLL_UNLOCKED) { + mdelay(1); + retry_cnt--; + if (retry_cnt == 0) { + printk(BIOS_ERR, "DP dp's pll lock failed : retry : %d\n", + retry_cnt); + return -1; + } + } + + printk(BIOS_DEBUG, "dp's pll lock success(%d)\n", retry_cnt); + + /* Enable Serdes FIFO function and Link symbol clock domain module */ + reg = lread32(&dp_regs->func_en2); + reg &= ~(SERDES_FIFO_FUNC_EN_N | LS_CLK_DOMAIN_FUNC_EN_N + | AUX_FUNC_EN_N); + lwrite32(reg, &dp_regs->func_en2); + + return ret; +} + +void exynos_dp_init_hpd(void) +{ + u32 reg; + + /* Clear interrupts releated to Hot Plug Dectect */ + reg = HOTPLUG_CHG | HPD_LOST | PLUG; + lwrite32(reg, &dp_regs->common_int_sta4); + + reg = INT_HPD; + lwrite32(reg, &dp_regs->int_sta); + + reg = lread32(&dp_regs->sys_ctl3); + reg &= ~(F_HPD | HPD_CTRL); + lwrite32(reg, &dp_regs->sys_ctl3); + + return; +} + +static inline void exynos_dp_reset_aux(void) +{ + u32 reg; + + /* Disable AUX channel module */ + reg = lread32(&dp_regs->func_en2); + reg |= AUX_FUNC_EN_N; + lwrite32(reg, &dp_regs->func_en2); + + return; +} + +void exynos_dp_init_aux(void) +{ + u32 reg; + + /* Clear inerrupts related to AUX channel */ + reg = RPLY_RECEIV | AUX_ERR; + lwrite32(reg, &dp_regs->int_sta); + + exynos_dp_reset_aux(); + + /* Disable AUX transaction H/W retry */ + reg = AUX_BIT_PERIOD_EXPECTED_DELAY(3) | AUX_HW_RETRY_COUNT_SEL(3)| + AUX_HW_RETRY_INTERVAL_600_MICROSECONDS; + lwrite32(reg, &dp_regs->aux_hw_retry_ctl); + + /* Receive AUX Channel DEFER commands equal to DEFFER_COUNT*64 */ + reg = DEFER_CTRL_EN | DEFER_COUNT(1); + lwrite32(reg, &dp_regs->aux_ch_defer_ctl); + + /* Enable AUX channel module */ + reg = lread32(&dp_regs->func_en2); + reg &= ~AUX_FUNC_EN_N; + lwrite32(reg, &dp_regs->func_en2); + + return; +} + +void exynos_dp_config_interrupt(void) +{ + u32 reg; + + /* 0: mask, 1: unmask */ + reg = COMMON_INT_MASK_1; + lwrite32(reg, &dp_regs->common_int_mask1); + + reg = COMMON_INT_MASK_2; + lwrite32(reg, &dp_regs->common_int_mask2); + + reg = COMMON_INT_MASK_3; + lwrite32(reg, &dp_regs->common_int_mask3); + + reg = COMMON_INT_MASK_4; + lwrite32(reg, &dp_regs->common_int_mask4); + + reg = INT_STA_MASK; + lwrite32(reg, &dp_regs->int_sta_mask); + + return; +} + +unsigned int exynos_dp_get_plug_in_status(void) +{ + u32 reg; + + reg = lread32(&dp_regs->sys_ctl3); + if (reg & HPD_STATUS) + return 0; + + return -1; +} + +unsigned int exynos_dp_detect_hpd(void) +{ + int timeout_loop = DP_TIMEOUT_LOOP_COUNT; + + mdelay(2); + + while (exynos_dp_get_plug_in_status() != 0) { + if (timeout_loop == 0) + return -1; + mdelay(1); + timeout_loop--; + } + + return EXYNOS_DP_SUCCESS; +} + +unsigned int exynos_dp_start_aux_transaction(void) +{ + u32 reg; + unsigned int ret = 0; + unsigned int retry_cnt; + + /* Enable AUX CH operation */ + reg = lread32(&dp_regs->aux_ch_ctl2); + reg |= AUX_EN; + lwrite32(reg, &dp_regs->aux_ch_ctl2); + + retry_cnt = 10; + while (retry_cnt) { + reg = lread32(&dp_regs->int_sta); + if (!(reg & RPLY_RECEIV)) { + if (retry_cnt == 0) { + printk(BIOS_ERR, "DP Reply Timeout!!\n"); + ret = -1; + return ret; + } + mdelay(1); + retry_cnt--; + } else + break; + } + + /* Clear interrupt source for AUX CH command reply */ + lwrite32(reg, &dp_regs->int_sta); + + /* Clear interrupt source for AUX CH access error */ + reg = lread32(&dp_regs->int_sta); + if (reg & AUX_ERR) { + printk(BIOS_ERR, "DP Aux Access Error\n"); + lwrite32(AUX_ERR, &dp_regs->int_sta); + ret = -1; + return ret; + } + + /* Check AUX CH error access status */ + reg = lread32(&dp_regs->aux_ch_sta); + if ((reg & AUX_STATUS_MASK) != 0) { + printk(BIOS_DEBUG, "DP AUX CH error happens: %x\n", reg & AUX_STATUS_MASK); + ret = -1; + return ret; + } + return EXYNOS_DP_SUCCESS; +} + +unsigned int exynos_dp_write_byte_to_dpcd(u32 reg_addr, u8 data) +{ + u32 reg; + unsigned int ret; + + /* Clear AUX CH data buffer */ + reg = BUF_CLR; + lwrite32(reg, &dp_regs->buffer_data_ctl); + + /* Select DPCD device address */ + reg = AUX_ADDR_7_0(reg_addr); + lwrite32(reg, &dp_regs->aux_addr_7_0); + reg = AUX_ADDR_15_8(reg_addr); + lwrite32(reg, &dp_regs->aux_addr_15_8); + reg = AUX_ADDR_19_16(reg_addr); + lwrite32(reg, &dp_regs->aux_addr_19_16); + + /* Write data buffer */ + reg = data; + lwrite32(reg, &dp_regs->buf_data0); + + /* + * Set DisplayPort transaction and write 1 byte + * If bit 3 is 1, DisplayPort transaction. + * If Bit 3 is 0, I2C transaction. + */ + reg = AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_WRITE; + lwrite32(reg, &dp_regs->aux_ch_ctl1); + + /* Start AUX transaction */ + ret = exynos_dp_start_aux_transaction(); + if (ret != EXYNOS_DP_SUCCESS) { + printk(BIOS_ERR, "DP Aux transaction failed\n"); + } + + return ret; +} + +unsigned int exynos_dp_read_byte_from_dpcd(u32 reg_addr, + unsigned char *data) +{ + u32 reg; + int retval; + + /* Clear AUX CH data buffer */ + reg = BUF_CLR; + lwrite32(reg, &dp_regs->buffer_data_ctl); + + /* Select DPCD device address */ + reg = AUX_ADDR_7_0(reg_addr); + lwrite32(reg, &dp_regs->aux_addr_7_0); + reg = AUX_ADDR_15_8(reg_addr); + lwrite32(reg, &dp_regs->aux_addr_15_8); + reg = AUX_ADDR_19_16(reg_addr); + lwrite32(reg, &dp_regs->aux_addr_19_16); + + /* + * Set DisplayPort transaction and read 1 byte + * If bit 3 is 1, DisplayPort transaction. + * If Bit 3 is 0, I2C transaction. + */ + reg = AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_READ; + lwrite32(reg, &dp_regs->aux_ch_ctl1); + + /* Start AUX transaction */ + retval = exynos_dp_start_aux_transaction(); + if (retval != EXYNOS_DP_SUCCESS) + printk(BIOS_DEBUG, "DP Aux Transaction fail!\n"); + + /* Read data buffer */ + reg = lread32(&dp_regs->buf_data0); + *data = (unsigned char)(reg & 0xff); + + return retval; +} + +unsigned int exynos_dp_write_bytes_to_dpcd(u32 reg_addr, + unsigned int count, + unsigned char data[]) +{ + u32 reg; + unsigned int start_offset; + unsigned int cur_data_count; + unsigned int cur_data_idx; + unsigned int retry_cnt; + unsigned int ret = 0; + + /* Clear AUX CH data buffer */ + reg = BUF_CLR; + lwrite32(reg, &dp_regs->buffer_data_ctl); + + start_offset = 0; + while (start_offset < count) { + /* Buffer size of AUX CH is 16 * 4bytes */ + if ((count - start_offset) > 16) + cur_data_count = 16; + else + cur_data_count = count - start_offset; + + retry_cnt = 5; + while (retry_cnt) { + /* Select DPCD device address */ + reg = AUX_ADDR_7_0(reg_addr + start_offset); + lwrite32(reg, &dp_regs->aux_addr_7_0); + reg = AUX_ADDR_15_8(reg_addr + start_offset); + lwrite32(reg, &dp_regs->aux_addr_15_8); + reg = AUX_ADDR_19_16(reg_addr + start_offset); + lwrite32(reg, &dp_regs->aux_addr_19_16); + + for (cur_data_idx = 0; cur_data_idx < cur_data_count; + cur_data_idx++) { + reg = data[start_offset + cur_data_idx]; + lwrite32(reg, (void *)((unsigned int)&dp_regs->buf_data0 + + (4 * cur_data_idx))); + } + /* + * Set DisplayPort transaction and write + * If bit 3 is 1, DisplayPort transaction. + * If Bit 3 is 0, I2C transaction. + */ + reg = AUX_LENGTH(cur_data_count) | + AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_WRITE; + lwrite32(reg, &dp_regs->aux_ch_ctl1); + + /* Start AUX transaction */ + ret = exynos_dp_start_aux_transaction(); + if (ret != EXYNOS_DP_SUCCESS) { + if (retry_cnt == 0) { + printk(BIOS_ERR, "DP Aux Transaction failed\n"); + return ret; + } + retry_cnt--; + } else + break; + } + start_offset += cur_data_count; + } + + return ret; +} + +unsigned int exynos_dp_read_bytes_from_dpcd(u32 reg_addr, + unsigned int count, + unsigned char data[]) +{ + u32 reg; + unsigned int start_offset; + unsigned int cur_data_count; + unsigned int cur_data_idx; + unsigned int retry_cnt; + unsigned int ret = 0; + + /* Clear AUX CH data buffer */ + reg = BUF_CLR; + lwrite32(reg, &dp_regs->buffer_data_ctl); + + start_offset = 0; + while (start_offset < count) { + /* Buffer size of AUX CH is 16 * 4bytes */ + if ((count - start_offset) > 16) + cur_data_count = 16; + else + cur_data_count = count - start_offset; + + retry_cnt = 5; + while (retry_cnt) { + /* Select DPCD device address */ + reg = AUX_ADDR_7_0(reg_addr + start_offset); + lwrite32(reg, &dp_regs->aux_addr_7_0); + reg = AUX_ADDR_15_8(reg_addr + start_offset); + lwrite32(reg, &dp_regs->aux_addr_15_8); + reg = AUX_ADDR_19_16(reg_addr + start_offset); + lwrite32(reg, &dp_regs->aux_addr_19_16); + /* + * Set DisplayPort transaction and read + * If bit 3 is 1, DisplayPort transaction. + * If Bit 3 is 0, I2C transaction. + */ + reg = AUX_LENGTH(cur_data_count) | + AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_READ; + lwrite32(reg, &dp_regs->aux_ch_ctl1); + + /* Start AUX transaction */ + ret = exynos_dp_start_aux_transaction(); + if (ret != EXYNOS_DP_SUCCESS) { + if (retry_cnt == 0) { + printk(BIOS_ERR, "DP Aux Transaction failed\n"); + return ret; + } + retry_cnt--; + } else + break; + } + + for (cur_data_idx = 0; cur_data_idx < cur_data_count; + cur_data_idx++) { + reg = lread32((void *)((u32)&dp_regs->buf_data0 + + 4 * cur_data_idx)); + data[start_offset + cur_data_idx] = (unsigned char)reg; + } + + start_offset += cur_data_count; + } + + return ret; +} + +int exynos_dp_select_i2c_device(u32 device_addr, + u32 reg_addr) +{ + u32 reg; + int retval; + + /* Set EDID device address */ + reg = device_addr; + lwrite32(reg, &dp_regs->aux_addr_7_0); + lwrite32(0x0, &dp_regs->aux_addr_15_8); + lwrite32(0x0, &dp_regs->aux_addr_19_16); + + /* Set offset from base address of EDID device */ + lwrite32(reg_addr, &dp_regs->buf_data0); + + /* + * Set I2C transaction and write address + * If bit 3 is 1, DisplayPort transaction. + * If Bit 3 is 0, I2C transaction. + */ + reg = AUX_TX_COMM_I2C_TRANSACTION | AUX_TX_COMM_MOT | + AUX_TX_COMM_WRITE; + lwrite32(reg, &dp_regs->aux_ch_ctl1); + + /* Start AUX transaction */ + retval = exynos_dp_start_aux_transaction(); + if (retval != 0) + printk(BIOS_DEBUG, "%s: DP Aux Transaction fail!\n", __func__); + + return retval; +} + +int exynos_dp_read_byte_from_i2c(u32 device_addr, + u32 reg_addr, + unsigned int *data) +{ + u32 reg; + int i; + int retval; + + for (i = 0; i < 10; i++) { + /* Clear AUX CH data buffer */ + reg = BUF_CLR; + lwrite32(reg, &dp_regs->buffer_data_ctl); + + /* Select EDID device */ + retval = exynos_dp_select_i2c_device(device_addr, reg_addr); + if (retval != 0) { + printk(BIOS_DEBUG, "DP Select EDID device fail. retry !\n"); + continue; + } + + /* + * Set I2C transaction and read data + * If bit 3 is 1, DisplayPort transaction. + * If Bit 3 is 0, I2C transaction. + */ + reg = AUX_TX_COMM_I2C_TRANSACTION | + AUX_TX_COMM_READ; + lwrite32(reg, &dp_regs->aux_ch_ctl1); + + /* Start AUX transaction */ + retval = exynos_dp_start_aux_transaction(); + if (retval != EXYNOS_DP_SUCCESS) + printk(BIOS_DEBUG, "%s: DP Aux Transaction fail!\n", __func__); + } + + /* Read data */ + if (retval == 0) + *data = lread32(&dp_regs->buf_data0); + + return retval; +} + +int exynos_dp_read_bytes_from_i2c(u32 device_addr, + u32 reg_addr, unsigned int count, unsigned char edid[]) +{ + u32 reg; + unsigned int i, j; + unsigned int cur_data_idx; + unsigned int defer = 0; + int retval = 0; + + for (i = 0; i < count; i += 16) { /* use 16 burst */ + for (j = 0; j < 100; j++) { + /* Clear AUX CH data buffer */ + reg = BUF_CLR; + lwrite32(reg, &dp_regs->buffer_data_ctl); + + /* Set normal AUX CH command */ + reg = lread32(&dp_regs->aux_ch_ctl2); + reg &= ~ADDR_ONLY; + lwrite32(reg, &dp_regs->aux_ch_ctl2); + + /* + * If Rx sends defer, Tx sends only reads + * request without sending addres + */ + if (!defer) + retval = + exynos_dp_select_i2c_device(device_addr, + reg_addr + i); + else + defer = 0; + + if (retval == EXYNOS_DP_SUCCESS) { + /* + * Set I2C transaction and write data + * If bit 3 is 1, DisplayPort transaction. + * If Bit 3 is 0, I2C transaction. + */ + reg = AUX_LENGTH(16) | + AUX_TX_COMM_I2C_TRANSACTION | + AUX_TX_COMM_READ; + lwrite32(reg, &dp_regs->aux_ch_ctl1); + + /* Start AUX transaction */ + retval = exynos_dp_start_aux_transaction(); + if (retval == 0) + break; + else + printk(BIOS_ERR, "DP Aux Transaction fail!\n"); + } + /* Check if Rx sends defer */ + reg = lread32(&dp_regs->aux_rx_comm); + if (reg == AUX_RX_COMM_AUX_DEFER || + reg == AUX_RX_COMM_I2C_DEFER) { + printk(BIOS_ERR, "DP Defer: %d\n\n", reg); + defer = 1; + } + } + + for (cur_data_idx = 0; cur_data_idx < 16; cur_data_idx++) { + reg = lread32((void *)((u32)&dp_regs->buf_data0 + + 4 * cur_data_idx)); + edid[i + cur_data_idx] = (unsigned char)reg; + } + } + + return retval; +} + +void exynos_dp_reset_macro(void) +{ + u32 reg; + + reg = lread32(&dp_regs->phy_test); + reg |= MACRO_RST; + lwrite32(reg, &dp_regs->phy_test); + + /* 10 us is the minimum Macro reset time. */ + udelay(50); + + reg &= ~MACRO_RST; + lwrite32(reg, &dp_regs->phy_test); +} + +void exynos_dp_set_link_bandwidth(unsigned char bwtype) +{ + u32 reg; + + reg = (u32)bwtype; + + /* Set bandwidth to 2.7G or 1.62G */ + if ((bwtype == DP_LANE_BW_1_62) || (bwtype == DP_LANE_BW_2_70)) + lwrite32(reg, &dp_regs->link_bw_set); +} + +unsigned char exynos_dp_get_link_bandwidth(void) +{ + unsigned char ret; + u32 reg; + + reg = lread32(&dp_regs->link_bw_set); + ret = (unsigned char)reg; + + return ret; +} + +void exynos_dp_set_lane_count(unsigned char count) +{ + u32 reg; + + reg = (u32)count; + + if ((count == DP_LANE_CNT_1) || (count == DP_LANE_CNT_2) || + (count == DP_LANE_CNT_4)) + lwrite32(reg, &dp_regs->lane_count_set); +} + +unsigned int exynos_dp_get_lane_count(void) +{ + u32 reg; + + reg = lread32(&dp_regs->lane_count_set); + + return reg; +} + +unsigned char exynos_dp_get_lanex_pre_emphasis(unsigned char lanecnt) +{ + void *reg_list[DP_LANE_CNT_4] = { + &dp_regs->ln0_link_training_ctl, + &dp_regs->ln1_link_training_ctl, + &dp_regs->ln2_link_training_ctl, + &dp_regs->ln3_link_training_ctl, + }; + + return lread32(reg_list[lanecnt]); +} + +void exynos_dp_set_lanex_pre_emphasis(unsigned char request_val, + unsigned char lanecnt) +{ + void * reg_list[DP_LANE_CNT_4] = { + &dp_regs->ln0_link_training_ctl, + &dp_regs->ln1_link_training_ctl, + &dp_regs->ln2_link_training_ctl, + &dp_regs->ln3_link_training_ctl, + }; + + lwrite32(request_val, reg_list[lanecnt]); +} + +void exynos_dp_set_lane_pre_emphasis(unsigned int level, unsigned char lanecnt) +{ + unsigned char i; + u32 reg; + void *reg_list[DP_LANE_CNT_4] = { + &dp_regs->ln0_link_training_ctl, + &dp_regs->ln1_link_training_ctl, + &dp_regs->ln2_link_training_ctl, + &dp_regs->ln3_link_training_ctl, + }; + u32 reg_shift[DP_LANE_CNT_4] = { + PRE_EMPHASIS_SET_0_SHIFT, + PRE_EMPHASIS_SET_1_SHIFT, + PRE_EMPHASIS_SET_2_SHIFT, + PRE_EMPHASIS_SET_3_SHIFT + }; + + for (i = 0; i < lanecnt; i++) { + reg = level << reg_shift[i]; + lwrite32(reg, reg_list[i]); + } +} + +void exynos_dp_set_training_pattern(unsigned int pattern) +{ + u32 reg = 0; + + switch (pattern) { + case PRBS7: + reg = SCRAMBLING_ENABLE | LINK_QUAL_PATTERN_SET_PRBS7; + break; + case D10_2: + reg = SCRAMBLING_ENABLE | LINK_QUAL_PATTERN_SET_D10_2; + break; + case TRAINING_PTN1: + reg = SCRAMBLING_DISABLE | SW_TRAINING_PATTERN_SET_PTN1; + break; + case TRAINING_PTN2: + reg = SCRAMBLING_DISABLE | SW_TRAINING_PATTERN_SET_PTN2; + break; + case DP_NONE: + reg = SCRAMBLING_ENABLE | LINK_QUAL_PATTERN_SET_DISABLE | + SW_TRAINING_PATTERN_SET_NORMAL; + break; + default: + break; + } + + lwrite32(reg, &dp_regs->training_ptn_set); +} + +void exynos_dp_enable_enhanced_mode(unsigned char enable) +{ + u32 reg; + + reg = lread32(&dp_regs->sys_ctl4); + reg &= ~ENHANCED; + + if (enable) + reg |= ENHANCED; + + lwrite32(reg, &dp_regs->sys_ctl4); +} + +void exynos_dp_enable_scrambling(unsigned int enable) +{ + u32 reg; + + reg = lread32(&dp_regs->training_ptn_set); + reg &= ~(SCRAMBLING_DISABLE); + + if (!enable) + reg |= SCRAMBLING_DISABLE; + + lwrite32(reg, &dp_regs->training_ptn_set); +} +int exynos_dp_init_video(void) +{ + unsigned int reg; + + /* Clear VID_CLK_CHG[1] and VID_FORMAT_CHG[3] and VSYNC_DET[7] */ + reg = VSYNC_DET | VID_FORMAT_CHG | VID_CLK_CHG; + lwrite32(reg, &dp_regs->common_int_sta1); + + /* I_STRM__CLK detect : DE_CTL : Auto detect */ + reg &= ~DET_CTRL; + lwrite32(reg, &dp_regs->sys_ctl1); + return 0; +} + + +void exynos_dp_config_video_slave_mode(struct edp_video_info *video_info) +{ + u32 reg; + + /* Video Slave mode setting */ + reg = lread32(&dp_regs->func_en1); + reg &= ~(MASTER_VID_FUNC_EN_N|SLAVE_VID_FUNC_EN_N); + reg |= MASTER_VID_FUNC_EN_N; + lwrite32(reg, &dp_regs->func_en1); + + /* Configure Interlaced for slave mode video */ + reg = lread32(&dp_regs->video_ctl10); + reg &= ~INTERACE_SCAN_CFG; + reg |= (video_info->interlaced << INTERACE_SCAN_CFG_SHIFT); + printk(BIOS_SPEW, "interlaced %d\n", video_info->interlaced); + lwrite32(reg, &dp_regs->video_ctl10); + + /* Configure V sync polarity for slave mode video */ + reg = lread32(&dp_regs->video_ctl10); + reg &= ~VSYNC_POLARITY_CFG; + reg |= (video_info->v_sync_polarity << V_S_POLARITY_CFG_SHIFT); + lwrite32(reg, &dp_regs->video_ctl10); + + /* Configure H sync polarity for slave mode video */ + reg = lread32(&dp_regs->video_ctl10); + reg &= ~HSYNC_POLARITY_CFG; + reg |= (video_info->h_sync_polarity << H_S_POLARITY_CFG_SHIFT); + lwrite32(reg, &dp_regs->video_ctl10); + + /*Set video mode to slave mode */ + reg = AUDIO_MODE_SPDIF_MODE | VIDEO_MODE_SLAVE_MODE; + lwrite32(reg, &dp_regs->soc_general_ctl); +} + +void exynos_dp_set_video_color_format(struct edp_video_info *video_info) +{ + u32 reg; + + /* Configure the input color depth, color space, dynamic range */ + reg = (video_info->dynamic_range << IN_D_RANGE_SHIFT) | + (video_info->color_depth << IN_BPC_SHIFT) | + (video_info->color_space << IN_COLOR_F_SHIFT); + lwrite32(reg, &dp_regs->video_ctl2); + + /* Set Input Color YCbCr Coefficients to ITU601 or ITU709 */ + reg = lread32(&dp_regs->video_ctl3); + reg &= ~IN_YC_COEFFI_MASK; + if (video_info->ycbcr_coeff) + reg |= IN_YC_COEFFI_ITU709; + else + reg |= IN_YC_COEFFI_ITU601; + lwrite32(reg, &dp_regs->video_ctl3); +} + +unsigned int exynos_dp_is_slave_video_stream_clock_on(void) +{ + u32 reg; + + /* Update Video stream clk detect status */ + reg = lread32(&dp_regs->sys_ctl1); + lwrite32(reg, &dp_regs->sys_ctl1); + + reg = lread32(&dp_regs->sys_ctl1); + + if (!(reg & DET_STA)) { + printk(BIOS_DEBUG, "DP Input stream clock not detected.\n"); + return -1; + } + + return EXYNOS_DP_SUCCESS; +} + +void exynos_dp_set_video_cr_mn(unsigned int type, unsigned int m_value, + unsigned int n_value) +{ + u32 reg; + + if (type == REGISTER_M) { + reg = lread32(&dp_regs->sys_ctl4); + reg |= FIX_M_VID; + lwrite32(reg, &dp_regs->sys_ctl4); + reg = M_VID0_CFG(m_value); + lwrite32(reg, &dp_regs->m_vid0); + reg = M_VID1_CFG(m_value); + lwrite32(reg, &dp_regs->m_vid1); + reg = M_VID2_CFG(m_value); + lwrite32(reg, &dp_regs->m_vid2); + + reg = N_VID0_CFG(n_value); + lwrite32(reg, &dp_regs->n_vid0); + reg = N_VID1_CFG(n_value); + lwrite32(reg, &dp_regs->n_vid1); + reg = N_VID2_CFG(n_value); + lwrite32(reg, &dp_regs->n_vid2); + } else { + reg = lread32(&dp_regs->sys_ctl4); + reg &= ~FIX_M_VID; + lwrite32(reg, &dp_regs->sys_ctl4); + } +} + +void exynos_dp_set_video_timing_mode(unsigned int type) +{ + u32 reg; + + reg = lread32(&dp_regs->video_ctl10); + reg &= ~FORMAT_SEL; + + if (type != VIDEO_TIMING_FROM_CAPTURE) + reg |= FORMAT_SEL; + + lwrite32(reg, &dp_regs->video_ctl10); +} + +void exynos_dp_enable_video_master(unsigned int enable) +{ + u32 reg; + + reg = lread32(&dp_regs->soc_general_ctl); + if (enable) { + reg &= ~VIDEO_MODE_MASK; + reg |= VIDEO_MASTER_MODE_EN | VIDEO_MODE_MASTER_MODE; + } else { + reg &= ~VIDEO_MODE_MASK; + reg |= VIDEO_MODE_SLAVE_MODE; + } + + lwrite32(reg, &dp_regs->soc_general_ctl); +} + +void exynos_dp_start_video(void) +{ + u32 reg; + + /* Enable Video input and disable Mute */ + reg = lread32(&dp_regs->video_ctl1); + reg |= VIDEO_EN; + lwrite32(reg, &dp_regs->video_ctl1); +} + +unsigned int exynos_dp_is_video_stream_on(void) +{ + u32 reg; + + /* Update STRM_VALID */ + reg = lread32(&dp_regs->sys_ctl3); + lwrite32(reg, &dp_regs->sys_ctl3); + + reg = lread32(&dp_regs->sys_ctl3); + + if (!(reg & STRM_VALID)) + return -1; + + return EXYNOS_DP_SUCCESS; +} + +void dp_phy_control(unsigned int enable) +{ + u32 cfg; + + cfg = lread32(&exynos_power->dptx_phy_control); + if (enable) + cfg |= EXYNOS_DP_PHY_ENABLE; + else + cfg &= ~EXYNOS_DP_PHY_ENABLE; + lwrite32(cfg, &exynos_power->dptx_phy_control); +} diff --git a/src/soc/samsung/exynos5420/dsim.h b/src/soc/samsung/exynos5420/dsim.h new file mode 100644 index 0000000000..25015a26ad --- /dev/null +++ b/src/soc/samsung/exynos5420/dsim.h @@ -0,0 +1,109 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2012 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* Register map for Exynos5 MIPI-DSIM */ + +#ifndef CPU_SAMSUNG_EXYNOS5420_DSIM_H +#define CPU_SAMSUNG_EXYNOS5420_DSIM_H + +/* DSIM register map */ +struct exynos5_dsim { + unsigned int status; + unsigned int swrst; + unsigned int clkctrl; + unsigned int timeout; + unsigned int config; + unsigned int escmode; + unsigned int mdresol; + unsigned int mvporch; + unsigned int mhporch; + unsigned int msync; + unsigned int sdresol; + unsigned int intsrc; + unsigned int intmsk; + unsigned int pkthdr; + unsigned int payload; + unsigned int rxfifo; + unsigned int res1; + unsigned int fifoctrl; + unsigned int res2; + unsigned int pllctrl; + unsigned int plltmr; + unsigned int phyacchr; + unsigned int phyacchr1; +}; + +#define ENABLE 1 +#define DISABLE 0 + +#define DSIM_SWRST (1 << 0) +#define NUM_OF_DAT_LANE_IS_FOUR (3 << 5) +#define DATA_LANE_0_EN (1 << 0) +#define DATA_LANE_1_EN (1 << 1) +#define DATA_LANE_2_EN (1 << 2) +#define DATA_LANE_3_EN (1 << 3) +#define CLK_LANE_EN (1 << 4) +#define ENABLE_ALL_DATA_LANE DATA_LANE_0_EN | \ + DATA_LANE_1_EN | \ + DATA_LANE_2_EN | \ + DATA_LANE_3_EN +#define MAIN_PIX_FORMAT_OFFSET 12 +#define RGB_565_16_BIT 0x4 +#define VIDEO_MODE (1 << 25) +#define BURST_MODE (1 << 26) + + +#define DSIM_PHYACCHR_AFC_EN (1 << 14) +#define DSIM_PHYACCHR_AFC_CTL_OFFSET 5 + +#define DSIM_PLLCTRL_PMS_OFFSET 1 +#define DSIM_FREQ_BAND_OFFSET 24 + +#define LANE_ESC_CLK_EN_ALL (0x1f << 19) +#define BYTE_CLK_EN (1 << 24) +#define DSIM_ESC_CLK_EN (1 << 28) +#define TXREQUEST_HS_CLK_ON (1 << 31) + +#define LP_MODE_ENABLE (1 << 7) +#define STOP_STATE_CNT_OFFSET 21 + +#define MAIN_VBP_OFFSET 0 +#define STABLE_VFP_OFFSET 16 +#define CMD_ALLOW_OFFSET 28 + +#define MAIN_HBP_OFFSET 0 +#define MAIN_HFP_OFFSET 16 + +#define MAIN_HSA_OFFSET 0 +#define MAIN_VSA_OFFSET 22 + +#define MAIN_STANDBY (1 << 31) +#define MAIN_VRESOL_OFFSET 16 +#define MAIN_HRESOL_OFFSET 0 + +#define SFR_FIFO_EMPTY (1 << 29) + +#define DSIM_PLL_EN_SHIFT (1 << 23) +#define PLL_STABLE (1 << 31) + +#define DSIM_STOP_STATE_DAT(x) (((x) & 0xf) << 0) +#define DSIM_STOP_STATE_CLK (1 << 8) +#define DSIM_TX_READY_HS_CLK (1 << 10) + +#endif diff --git a/src/soc/samsung/exynos5420/fimd.c b/src/soc/samsung/exynos5420/fimd.c new file mode 100644 index 0000000000..1e51712e53 --- /dev/null +++ b/src/soc/samsung/exynos5420/fimd.c @@ -0,0 +1,432 @@ +/* + * Copyright 2013 Google Inc. + * Copyright (C) 2012 Samsung Electronics + * + * Author: InKi Dae <inki.dae@samsung.com> + * Author: Donghwa Lee <dh09.lee@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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <delay.h> +#include <arch/io.h> +#include <console/console.h> +#include "clk.h" +#include "dp.h" +#include "fimd.h" +#include "periph.h" +#include "sysreg.h" +#include "timer.h" + +/* fairly useful debugging stuff. */ +#if 0 +static inline void fwadl(unsigned long l,void *v) { + writel(l, v); + printk(BIOS_SPEW, "W %p %p\n", v, (void *)l); +} +#define lwritel(a,b) fwadl((unsigned long)(a), (void *)(b)) + +static inline unsigned long fradl(void *v) { + unsigned long l = readl(v); + printk(BIOS_SPEW, "R %p %p\n", v, (void *)l); + return l; +} + +#define lreadl(a) fradl((void *)(a)) + +#else +#define lwritel(a,b) writel((unsigned long)(a), (void *)(b)) +#define lreadl(a) readl((void *)(a)) +#endif + +/* not sure where we want this so ... */ +static unsigned long get_lcd_clk(void) +{ + u32 pclk, sclk; + unsigned int sel; + unsigned int ratio; + + /* + * CLK_SRC_DISP10 + * CLKMUX_FIMD1 [4] + * 0: SCLK_RPLL + * 1: SCLK_SPLL + */ + sel = lreadl(&exynos_clock->clk_src_disp10); + sel &= (1 << 4); + + if (sel){ + sclk = get_pll_clk(SPLL); + } else { + sclk = get_pll_clk(RPLL); + } + + /* + * CLK_DIV_DISP10 + * FIMD1_RATIO [3:0] + */ + ratio = lreadl(&exynos_clock->clk_div_disp10); + ratio = ratio & 0xf; + + pclk = sclk / (ratio + 1); + + return pclk; +} + +static void exynos_fimd_set_dualrgb(vidinfo_t *vid, unsigned int enabled) +{ + unsigned int cfg = 0; + printk(BIOS_SPEW, "%s %s\n", __func__, enabled ? "enabled" : "not enabled"); + if (enabled) { + cfg = EXYNOS_DUALRGB_BYPASS_DUAL | EXYNOS_DUALRGB_LINESPLIT | + EXYNOS_DUALRGB_VDEN_EN_ENABLE; + + /* in case of Line Split mode, MAIN_CNT doesn't neet to set. */ + cfg |= EXYNOS_DUALRGB_SUB_CNT(vid->vl_col / 2) | + EXYNOS_DUALRGB_MAIN_CNT(0); + } + + lwritel(cfg, &FIMD_CTRL->dualrgb); +} + +static void exynos_fimd_set_dp_clkcon(unsigned int enabled) +{ + unsigned int cfg = 0; + + if (enabled){ + cfg = EXYNOS_DP_CLK_ENABLE; + } + + lwritel(cfg, &FIMD_CTRL->dp_mie_clkcon); +} + +static void exynos_fimd_set_par(vidinfo_t *vid, unsigned int win_id) +{ + unsigned int cfg = 0; + printk(BIOS_SPEW, "%s %d\n", __func__, win_id); + /* set window control */ + cfg = lreadl(&FIMD_CTRL->wincon0 + + EXYNOS_WINCON(win_id)); + + cfg &= ~(EXYNOS_WINCON_BITSWP_ENABLE | EXYNOS_WINCON_BYTESWP_ENABLE | + EXYNOS_WINCON_HAWSWP_ENABLE | EXYNOS_WINCON_WSWP_ENABLE | + EXYNOS_WINCON_BURSTLEN_MASK | EXYNOS_WINCON_BPPMODE_MASK | + EXYNOS_WINCON_INRGB_MASK | EXYNOS_WINCON_DATAPATH_MASK); + + /* DATAPATH is DMA */ + cfg |= EXYNOS_WINCON_DATAPATH_DMA; + + cfg |= EXYNOS_WINCON_HAWSWP_ENABLE; + + /* dma burst is 16 */ + cfg |= EXYNOS_WINCON_BURSTLEN_16WORD; + + cfg |= EXYNOS_WINCON_BPPMODE_16BPP_565; + + lwritel(cfg, &FIMD_CTRL->wincon0 + + EXYNOS_WINCON(win_id)); + + /* set window position to x=0, y=0*/ + cfg = EXYNOS_VIDOSD_LEFT_X(0) | EXYNOS_VIDOSD_TOP_Y(0); + lwritel(cfg, &FIMD_CTRL->vidosd0a + + EXYNOS_VIDOSD(win_id)); + + cfg = EXYNOS_VIDOSD_RIGHT_X(vid->vl_col - 1) | + EXYNOS_VIDOSD_BOTTOM_Y(vid->vl_row - 1) | + EXYNOS_VIDOSD_RIGHT_X_E(1) | + EXYNOS_VIDOSD_BOTTOM_Y_E(0); + + lwritel(cfg, &FIMD_CTRL->vidosd0b + + EXYNOS_VIDOSD(win_id)); + /* set window size for window0*/ + cfg = EXYNOS_VIDOSD_SIZE(vid->vl_col * vid->vl_row); + lwritel(cfg, &FIMD_CTRL->vidosd0c + + EXYNOS_VIDOSD(win_id)); +} + +static void exynos_fimd_set_buffer_address(vidinfo_t *vid, + void *screen_base, int win_id) +{ + u32 start_addr, end_addr; + printk(BIOS_SPEW, "%s %d\n", __func__, win_id); + start_addr = (u32)screen_base; + end_addr = start_addr + ((vid->vl_col * ((1<<vid->vl_bpix) / 8)) * + vid->vl_row); + + lwritel(start_addr, &FIMD_CTRL->vidw00add0b0 + + EXYNOS_BUFFER_OFFSET(win_id)); + lwritel(end_addr, &FIMD_CTRL->vidw00add1b0 + + EXYNOS_BUFFER_OFFSET(win_id)); +} + +static void exynos_fimd_set_clock(vidinfo_t *vid) +{ + unsigned int cfg = 0, div = 0, remainder = 0, remainder_div; + unsigned long pixel_clock; + unsigned long long src_clock; + printk(BIOS_SPEW, "%s\n", __func__); + if (vid->dual_lcd_enabled) { + pixel_clock = vid->vl_freq * + (vid->vl_hspw + vid->vl_hfpd + + vid->vl_hbpd + vid->vl_col / 2) * + (vid->vl_vspw + vid->vl_vfpd + + vid->vl_vbpd + vid->vl_row); + } else if (vid->interface_mode == FIMD_CPU_INTERFACE) { + pixel_clock = vid->vl_freq * + vid->vl_width * vid->vl_height * + (vid->cs_setup + vid->wr_setup + + vid->wr_act + vid->wr_hold + 1); + } else { + pixel_clock = vid->vl_freq * + (vid->vl_hspw + vid->vl_hfpd + + vid->vl_hbpd + vid->vl_col) * + (vid->vl_vspw + vid->vl_vfpd + + vid->vl_vbpd + vid->vl_row); + } + printk(BIOS_SPEW, "Pixel clock is %lx\n", pixel_clock); + + cfg = lreadl(&FIMD_CTRL->vidcon0); + cfg &= ~(EXYNOS_VIDCON0_CLKSEL_MASK | EXYNOS_VIDCON0_CLKVALUP_MASK | + EXYNOS_VIDCON0_CLKVAL_F(0xFF) | EXYNOS_VIDCON0_VCLKEN_MASK | + EXYNOS_VIDCON0_CLKDIR_MASK); + cfg |= (EXYNOS_VIDCON0_CLKSEL_SCLK | EXYNOS_VIDCON0_CLKVALUP_ALWAYS | + EXYNOS_VIDCON0_VCLKEN_NORMAL | EXYNOS_VIDCON0_CLKDIR_DIVIDED); + + src_clock = (unsigned long long) get_lcd_clk(); + + /* get quotient and remainder. */ + remainder = src_clock % pixel_clock; + src_clock /= pixel_clock; + + div = src_clock; + + remainder *= 10; + remainder_div = remainder / pixel_clock; + + /* round about one places of decimals. */ + if (remainder_div >= 5) + div++; + + /* in case of dual lcd mode. */ + if (vid->dual_lcd_enabled) + div--; + + cfg |= EXYNOS_VIDCON0_CLKVAL_F(div - 1); + lwritel(cfg, &FIMD_CTRL->vidcon0); +} + +void exynos_set_trigger(void) +{ + unsigned int cfg = 0; + printk(BIOS_SPEW, "%s\n", __func__); + cfg = lreadl(&FIMD_CTRL->trigcon); + + cfg |= (EXYNOS_I80SOFT_TRIG_EN | EXYNOS_I80START_TRIG); + + lwritel(cfg, &FIMD_CTRL->trigcon); +} + +int exynos_is_i80_frame_done(void) +{ + unsigned int cfg = 0; + int status; + printk(BIOS_SPEW, "%s\n", __func__); + cfg = lreadl(&FIMD_CTRL->trigcon); + + /* frame done func is valid only when TRIMODE[0] is set to 1. */ + status = (cfg & EXYNOS_I80STATUS_TRIG_DONE) == + EXYNOS_I80STATUS_TRIG_DONE; + + return status; +} + +static void exynos_fimd_lcd_on(void) +{ + unsigned int cfg = 0; + + printk(BIOS_SPEW, "%s\n", __func__); + /* display on */ + cfg = lreadl(&FIMD_CTRL->vidcon0); + cfg |= (EXYNOS_VIDCON0_ENVID_ENABLE | EXYNOS_VIDCON0_ENVID_F_ENABLE); + lwritel(cfg, &FIMD_CTRL->vidcon0); +} + +static void exynos_fimd_window_on(unsigned int win_id) +{ + unsigned int cfg = 0; + printk(BIOS_SPEW, "%s %d\n", __func__, win_id); + /* enable window */ + cfg = lreadl(&FIMD_CTRL->wincon0 + + EXYNOS_WINCON(win_id)); + cfg |= EXYNOS_WINCON_ENWIN_ENABLE; + lwritel(cfg, &FIMD_CTRL->wincon0 + + EXYNOS_WINCON(win_id)); + + cfg = lreadl(&FIMD_CTRL->winshmap); + cfg |= EXYNOS_WINSHMAP_CH_ENABLE(win_id); + lwritel(cfg, &FIMD_CTRL->winshmap); + cfg = lreadl(&FIMD_CTRL->winshmap); +} + +void exynos_fimd_lcd_off(void) +{ + unsigned int cfg = 0; + printk(BIOS_SPEW, "%s\n", __func__); + + cfg = lreadl(&FIMD_CTRL->vidcon0); + cfg &= (EXYNOS_VIDCON0_ENVID_DISABLE | EXYNOS_VIDCON0_ENVID_F_DISABLE); + lwritel(cfg, &FIMD_CTRL->vidcon0); +} + +void exynos_fimd_window_off(unsigned int win_id) +{ + unsigned int cfg = 0; + printk(BIOS_SPEW, "%s %d\n", __func__, win_id); + + cfg = lreadl(&FIMD_CTRL->wincon0 + + EXYNOS_WINCON(win_id)); + cfg &= EXYNOS_WINCON_ENWIN_DISABLE; + lwritel(cfg, &FIMD_CTRL->wincon0 + + EXYNOS_WINCON(win_id)); + + cfg = lreadl(&FIMD_CTRL->winshmap); + cfg &= ~EXYNOS_WINSHMAP_CH_DISABLE(win_id); + lwritel(cfg, &FIMD_CTRL->winshmap); +} + +static void exynos5_set_system_display(void) +{ + unsigned int cfg = 0; + + /* + * system register path set + * 0: MIE/MDNIE + * 1: FIMD Bypass + */ + cfg = lreadl(&exynos_sysreg->disp1blk_cfg); + cfg |= (1 << 15); + lwritel(cfg, &exynos_sysreg->disp1blk_cfg); +} + +void exynos_fimd_lcd_init(vidinfo_t *vid) +{ + unsigned int cfg = 0, rgb_mode; + unsigned int offset; + + offset = exynos_fimd_get_base_offset(); + printk(BIOS_SPEW, "%s\n", __func__); + exynos5_set_system_display(); + + rgb_mode = vid->rgb_mode; + + if (vid->interface_mode == FIMD_RGB_INTERFACE) { + printk(BIOS_SPEW, "%s FIMD_RGB_INTERFACE\n", __func__); + + cfg |= EXYNOS_VIDCON0_VIDOUT_RGB; + lwritel(cfg, &FIMD_CTRL->vidcon0); + + cfg = lreadl(&FIMD_CTRL->vidcon2); + cfg &= ~(EXYNOS_VIDCON2_WB_MASK | + EXYNOS_VIDCON2_TVFORMATSEL_MASK | + EXYNOS_VIDCON2_TVFORMATSEL_YUV_MASK); + cfg |= EXYNOS_VIDCON2_WB_DISABLE; + lwritel(cfg, &FIMD_CTRL->vidcon2); + + /* set polarity */ + cfg = 0; + if (!vid->vl_clkp) + cfg |= EXYNOS_VIDCON1_IVCLK_RISING_EDGE; + if (!vid->vl_hsp) + cfg |= EXYNOS_VIDCON1_IHSYNC_INVERT; + if (!vid->vl_vsp) + cfg |= EXYNOS_VIDCON1_IVSYNC_INVERT; + if (!vid->vl_dp) + cfg |= EXYNOS_VIDCON1_IVDEN_INVERT; + + lwritel(cfg, &FIMD_CTRL->vidcon1 + offset); + + /* set timing */ + cfg = EXYNOS_VIDTCON0_VFPD(vid->vl_vfpd - 1); + cfg |= EXYNOS_VIDTCON0_VBPD(vid->vl_vbpd - 1); + cfg |= EXYNOS_VIDTCON0_VSPW(vid->vl_vspw - 1); + lwritel(cfg, &FIMD_CTRL->vidtcon0 + offset); + + cfg = EXYNOS_VIDTCON1_HFPD(vid->vl_hfpd - 1); + cfg |= EXYNOS_VIDTCON1_HBPD(vid->vl_hbpd - 1); + cfg |= EXYNOS_VIDTCON1_HSPW(vid->vl_hspw - 1); + + lwritel(cfg, &FIMD_CTRL->vidtcon1 + offset); + + /* set lcd size */ + cfg = EXYNOS_VIDTCON2_HOZVAL(vid->vl_col - 1) | + EXYNOS_VIDTCON2_LINEVAL(vid->vl_row - 1) | + EXYNOS_VIDTCON2_HOZVAL_E(vid->vl_col - 1) | + EXYNOS_VIDTCON2_LINEVAL_E(vid->vl_row - 1); + + lwritel(cfg, &FIMD_CTRL->vidtcon2 + offset); + } + + /* set display mode */ + cfg = lreadl(&FIMD_CTRL->vidcon0); + cfg &= ~EXYNOS_VIDCON0_PNRMODE_MASK; + cfg |= (rgb_mode << EXYNOS_VIDCON0_PNRMODE_SHIFT); + lwritel(cfg, &FIMD_CTRL->vidcon0); + + /* set par */ + exynos_fimd_set_par(vid, vid->win_id); + + /* set memory address */ + exynos_fimd_set_buffer_address(vid, vid->screen_base, vid->win_id); + + /* set buffer size */ + cfg = EXYNOS_VIDADDR_PAGEWIDTH(vid->vl_col * (1<<vid->vl_bpix) / 8) | + EXYNOS_VIDADDR_PAGEWIDTH_E(vid->vl_col * (1<<vid->vl_bpix) / 8) | + EXYNOS_VIDADDR_OFFSIZE(0) | + EXYNOS_VIDADDR_OFFSIZE_E(0); + + lwritel(cfg, &FIMD_CTRL->vidw00add2 + + EXYNOS_BUFFER_SIZE(vid->win_id)); + + /* set clock */ + exynos_fimd_set_clock(vid); + + /* set rgb mode to dual lcd. */ + exynos_fimd_set_dualrgb(vid, vid->dual_lcd_enabled); + + /* display on */ + exynos_fimd_lcd_on(); + + /* window on */ + exynos_fimd_window_on(vid->win_id); + + exynos_fimd_set_dp_clkcon(vid->dp_enabled); + exynos5_set_system_display(); + printk(BIOS_SPEW, "%s: done\n", __func__); +} + +unsigned long exynos_fimd_calc_fbsize(vidinfo_t *vid) +{ + printk(BIOS_SPEW, "%s\n", __func__); + return vid->vl_col * vid->vl_row * ((1<<vid->vl_bpix) / 8); +} + +void exynos_fimd_lcd_disable(void) +{ + int i; + printk(BIOS_SPEW, "%s\n", __func__); + + for (i = 0; i < 4; i++) + exynos_fimd_window_off(i); +} diff --git a/src/soc/samsung/exynos5420/fimd.h b/src/soc/samsung/exynos5420/fimd.h new file mode 100644 index 0000000000..ce8773fd17 --- /dev/null +++ b/src/soc/samsung/exynos5420/fimd.h @@ -0,0 +1,215 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2012 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* Register map for Exynos5 FIMD */ + +#ifndef CPU_SAMSUNG_EXYNOS5420_FIMD_H +#define CPU_SAMSUNG_EXYNOS5420_FIMD_H + +#include "cpu.h" + +/* FIMD register map */ +struct exynos5_fimd { + /* This is an incomplete list. Add registers as and when required */ + u32 vidcon0; + u8 res1[0x1c]; + u32 wincon0; + u32 wincon1; + u32 wincon2; + u32 wincon3; + u32 wincon4; + u32 shadowcon; + u8 res2[0x8]; + u32 vidosd0a; + u32 vidosd0b; + u32 vidosd0c; + u8 res3[0x54]; + u32 vidw00add0b0; + u8 res4[0x2c]; + u32 vidw00add1b0; + u8 res5[0x2c]; + u32 vidw00add2; + u8 res6[0x3c]; + u32 w1keycon0; + u32 w1keycon1; + u32 w2keycon0; + u32 w2keycon1; + u32 w3keycon0; + u32 w3keycon1; + u32 w4keycon0; + u32 w4keycon1; + u8 res7[0x20]; + u32 win0map; + u8 res8[0xdc]; + u32 blendcon; + u8 res9[0x18]; + u32 dpclkcon; +}; + +#define W0_SHADOW_PROTECT (0x1 << 10) +#define COMPKEY_F 0xffffff +#define ENVID_F_ON (0x1 << 0) +#define ENVID_ON (0x1 << 1) +#define CLKVAL_F 0xb +#define CLKVAL_F_OFFSET 6 + +/* + * Structure containing display panel specific data for FIMD + */ +struct exynos5_fimd_panel { + unsigned int is_dp:1; /* Display Panel interface is eDP */ + unsigned int is_mipi:1; /* Display Panel interface is MIPI */ + unsigned int fixvclk:2; /* VCLK hold scheme at data underflow */ + + /* + * Polarity of the VCLK active edge + * 0-falling + * 1-rising + */ + unsigned int ivclk:1; + unsigned int clkval_f; /* Divider to create pixel clock */ + + unsigned int upper_margin; /* Vertical Backporch */ + unsigned int lower_margin; /* Vertical frontporch */ + unsigned int vsync; /* Vertical Sync Pulse Width */ + unsigned int left_margin; /* Horizontal Backporch */ + unsigned int right_margin; /* Horizontal Frontporch */ + unsigned int hsync; /* Horizontal Sync Pulse Width */ + unsigned int xres; /* X Resolution */ + unsigned int yres; /* Y Resopultion */ +}; + +/* LCDIF Register Map */ +struct exynos5_disp_ctrl { + u32 vidout_con; + u32 vidcon1; + u8 res1[0x8]; + u32 vidtcon0; + u32 vidtcon1; + u32 vidtcon2; + u32 vidtcon3; + u8 res2[0x184]; + u32 trigcon; +}; + +#define VCLK_RISING_EDGE (1 << 7) +#define VCLK_RUNNING (1 << 9) + +#define CHANNEL0_EN (1 << 0) + +#define VSYNC_PULSE_WIDTH_VAL 0x3 +#define VSYNC_PULSE_WIDTH_OFFSET 0 +#define V_FRONT_PORCH_VAL 0x3 +#define V_FRONT_PORCH_OFFSET 8 +#define V_BACK_PORCH_VAL 0x3 +#define V_BACK_PORCH_OFFSET 16 + +#define HSYNC_PULSE_WIDTH_VAL 0x3 +#define HSYNC_PULSE_WIDTH_OFFSET 0 +#define H_FRONT_PORCH_VAL 0x3 +#define H_FRONT_PORCH_OFFSET 8 +#define H_BACK_PORCH_VAL 0x3 +#define H_BACK_PORCH_OFFSET 16 + +#define HOZVAL_OFFSET 0 +#define LINEVAL_OFFSET 11 + +#define BPPMODE_F_RGB_16BIT_565 0x5 +#define BPPMODE_F_OFFSET 2 +#define ENWIN_F_ENABLE (1 << 0) +#define HALF_WORD_SWAP_EN (1 << 16) + +#define OSD_RIGHTBOTX_F_OFFSET 11 +#define OSD_RIGHTBOTY_F_OFFSET 0 + +#define FIMD_CTRL ((struct exynos_fb *)0x14400000) + +/* from u-boot fb.h. It needs to be merged with these dp structs maybe. */ +enum { + FIMD_RGB_INTERFACE = 1, + FIMD_CPU_INTERFACE = 2, +}; + +enum exynos_fb_rgb_mode_t { + MODE_RGB_P = 0, + MODE_BGR_P = 1, + MODE_RGB_S = 2, + MODE_BGR_S = 3, +}; + +typedef struct vidinfo { + u16 vl_col; /* Number of columns (i.e. 640) */ + u16 vl_row; /* Number of rows (i.e. 480) */ + u16 vl_width; /* Width of display area in millimeters */ + u16 vl_height; /* Height of display area in millimeters */ + + /* LCD configuration register */ + u8 vl_freq; /* Frequency */ + u8 vl_clkp; /* Clock polarity */ + u8 vl_oep; /* Output Enable polarity */ + u8 vl_hsp; /* Horizontal Sync polarity */ + u8 vl_vsp; /* Vertical Sync polarity */ + u8 vl_dp; /* Data polarity */ + u8 vl_bpix; /* Bits per pixel */ + + /* Horizontal control register. Timing from data sheet */ + u8 vl_hspw; /* Horz sync pulse width */ + u8 vl_hfpd; /* Wait before of line */ + u8 vl_hbpd; /* Wait end of line */ + + /* Vertical control register. */ + u8 vl_vspw; /* Vertical sync pulse width */ + u8 vl_vfpd; /* Wait before of frame */ + u8 vl_vbpd; /* Wait end of frame */ + u8 vl_cmd_allow_len; /* Wait end of frame */ + + unsigned int win_id; + unsigned int init_delay; + unsigned int power_on_delay; + unsigned int reset_delay; + unsigned int interface_mode; + unsigned int mipi_enabled; + unsigned int dp_enabled; + unsigned int cs_setup; + unsigned int wr_setup; + unsigned int wr_act; + unsigned int wr_hold; + unsigned int rgb_mode; + unsigned int resolution; + + /* parent clock name(MPLL, EPLL or VPLL) */ + unsigned int pclk_name; + /* ratio value for source clock from parent clock. */ + unsigned int sclk_div; + + unsigned int dual_lcd_enabled; + void *screen_base; + void *cmap; /* Points at 8 to 16 bit conversion map. */ +} vidinfo_t; + +/* fimd.c */ +void exynos_set_trigger(void); +int exynos_is_i80_frame_done(void); +void exynos_fimd_lcd_off(void); +void exynos_fimd_window_off(unsigned int win_id); +unsigned long exynos_fimd_calc_fbsize(vidinfo_t *vid); +void exynos_fimd_lcd_disable(void); +void exynos_fimd_lcd_init(vidinfo_t *vid); + +#endif diff --git a/src/soc/samsung/exynos5420/gpio.c b/src/soc/samsung/exynos5420/gpio.c new file mode 100644 index 0000000000..2b65eda04e --- /dev/null +++ b/src/soc/samsung/exynos5420/gpio.c @@ -0,0 +1,279 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2009 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <console/console.h> +#include <string.h> +#include <delay.h> +#include <assert.h> +#include "gpio.h" +#include "cpu.h" + +#define CON_MASK(x) (0xf << ((x) << 2)) +#define CON_SFR(x, v) ((v) << ((x) << 2)) + +#define DAT_MASK(x) (0x1 << (x)) +#define DAT_SET(x) (0x1 << (x)) + +#define PULL_MASK(x) (0x3 << ((x) << 1)) +#define PULL_MODE(x, v) ((v) << ((x) << 1)) + +#define DRV_MASK(x) (0x3 << ((x) << 1)) +#define DRV_SET(x, m) ((m) << ((x) << 1)) +#define RATE_MASK(x) (0x1 << (x + 16)) +#define RATE_SET(x) (0x1 << (x + 16)) + +struct gpio_info { + unsigned int reg_addr; /* Address of register for this part */ + unsigned int max_gpio; /* Maximum GPIO in this part */ +}; + +static const struct gpio_info gpio_data[EXYNOS_GPIO_NUM_PARTS] = { + { EXYNOS5420_GPIO_PART1_BASE, GPIO_MAX_PORT_PART_1 }, + { EXYNOS5420_GPIO_PART2_BASE, GPIO_MAX_PORT_PART_2 }, + { EXYNOS5420_GPIO_PART3_BASE, GPIO_MAX_PORT_PART_3 }, + { EXYNOS5420_GPIO_PART4_BASE, GPIO_MAX_PORT_PART_4 }, + { EXYNOS5420_GPIO_PART5_BASE, GPIO_MAX_PORT_PART_5 }, + { EXYNOS5420_GPIO_PART6_BASE, GPIO_MAX_PORT }, +}; + +/* This macro gets gpio pin offset from 0..7 */ +#define GPIO_BIT(x) ((x) & 0x7) + +static struct gpio_bank *gpio_get_bank(unsigned int gpio) +{ + const struct gpio_info *data; + unsigned int upto; + int i; + + for (i = upto = 0, data = gpio_data; i < EXYNOS_GPIO_NUM_PARTS; + i++, upto = data->max_gpio, data++) { + if (gpio < data->max_gpio) { + struct gpio_bank *bank; + + bank = (struct gpio_bank *)data->reg_addr; + bank += (gpio - upto) / GPIO_PER_BANK; + return bank; + } + } + + ASSERT(gpio < GPIO_MAX_PORT); /* ...which it will not be */ + return NULL; +} + +/* Common GPIO API - only available on Exynos5 */ +void gpio_cfg_pin(int gpio, int cfg) +{ + unsigned int value; + struct gpio_bank *bank = gpio_get_bank(gpio); + + value = readl(&bank->con); + value &= ~CON_MASK(GPIO_BIT(gpio)); + value |= CON_SFR(GPIO_BIT(gpio), cfg); + writel(value, &bank->con); +} + +static int gpio_get_cfg(int gpio) +{ + struct gpio_bank *bank = gpio_get_bank(gpio); + int shift = GPIO_BIT(gpio) << 2; + + return (readl(&bank->con) & CON_MASK(GPIO_BIT(gpio))) >> shift; +} + +void gpio_set_pull(int gpio, int mode) +{ + unsigned int value; + struct gpio_bank *bank = gpio_get_bank(gpio); + + value = readl(&bank->pull); + value &= ~PULL_MASK(GPIO_BIT(gpio)); + + switch (mode) { + case GPIO_PULL_DOWN: + case GPIO_PULL_UP: + value |= PULL_MODE(GPIO_BIT(gpio), mode); + break; + default: + break; + } + + writel(value, &bank->pull); +} + +void gpio_set_drv(int gpio, int mode) +{ + unsigned int value; + struct gpio_bank *bank = gpio_get_bank(gpio); + + value = readl(&bank->drv); + value &= ~DRV_MASK(GPIO_BIT(gpio)); + + switch (mode) { + case GPIO_DRV_1X: + case GPIO_DRV_2X: + case GPIO_DRV_3X: + case GPIO_DRV_4X: + value |= DRV_SET(GPIO_BIT(gpio), mode); + break; + default: + return; + } + + writel(value, &bank->drv); +} + +void gpio_set_rate(int gpio, int mode) +{ + unsigned int value; + struct gpio_bank *bank = gpio_get_bank(gpio); + + value = readl(&bank->drv); + value &= ~RATE_MASK(GPIO_BIT(gpio)); + + switch (mode) { + case GPIO_DRV_FAST: + case GPIO_DRV_SLOW: + value |= RATE_SET(GPIO_BIT(gpio)); + break; + default: + return; + } + + writel(value, &bank->drv); +} + +int gpio_direction_input(unsigned gpio) +{ + gpio_cfg_pin(gpio, GPIO_INPUT); + + return 0; +} + +int gpio_direction_output(unsigned gpio, int value) +{ + unsigned int val; + struct gpio_bank *bank = gpio_get_bank(gpio); + + val = readl(&bank->dat); + val &= ~DAT_MASK(GPIO_BIT(gpio)); + if (value) + val |= DAT_SET(GPIO_BIT(gpio)); + writel(val, &bank->dat); + + gpio_cfg_pin(gpio, GPIO_OUTPUT); + + return 0; +} + +int gpio_get_value(unsigned gpio) +{ + unsigned int value; + struct gpio_bank *bank = gpio_get_bank(gpio); + + value = readl(&bank->dat); + return !!(value & DAT_MASK(GPIO_BIT(gpio))); +} + +int gpio_set_value(unsigned gpio, int value) +{ + unsigned int val; + struct gpio_bank *bank = gpio_get_bank(gpio); + + val = readl(&bank->dat); + val &= ~DAT_MASK(GPIO_BIT(gpio)); + if (value) + val |= DAT_SET(GPIO_BIT(gpio)); + writel(val, &bank->dat); + + return 0; +} + +/* + * Add a delay here to give the lines time to settle + * TODO(dianders): 5us does not always work, 10 is stable, so use 15 to be safe + * Come back to this and sort out what the datasheet says + */ +#define GPIO_DELAY_US 15 + +#ifndef __BOOT_BLOCK__ +/* + * FIXME(dhendrix): These functions use udelay, which has dependencies on + * pwm code and timer code. These aren't necessary for the bootblock and + * bloat the image significantly. + */ +int gpio_read_mvl3(unsigned gpio) +{ + int high, low; + enum mvl3 value; + + if (gpio >= GPIO_MAX_PORT) + return -1; + + gpio_direction_input(gpio); + gpio_set_pull(gpio, GPIO_PULL_UP); + udelay(GPIO_DELAY_US); + high = gpio_get_value(gpio); + gpio_set_pull(gpio, GPIO_PULL_DOWN); + udelay(GPIO_DELAY_US); + low = gpio_get_value(gpio); + + if (high && low) /* external pullup */ + value = LOGIC_1; + else if (!high && !low) /* external pulldown */ + value = LOGIC_0; + else /* floating */ + value = LOGIC_Z; + + /* + * Check if line is externally pulled high and + * configure the internal pullup to match. For + * floating and pulldowns, the GPIO is already + * configured with an internal pulldown from the + * above test. + */ + if (value == LOGIC_1) + gpio_set_pull(gpio, GPIO_PULL_UP); + + return value; +} +#endif /* __BOOT_BLOCK__ */ + +/* + * Display Exynos GPIO information + */ +void gpio_info(void) +{ + unsigned gpio; + + for (gpio = 0; gpio < GPIO_MAX_PORT; gpio++) { + int cfg = gpio_get_cfg(gpio); + + printk(BIOS_INFO, "GPIO_%-3d: ", gpio); + if (cfg == GPIO_INPUT) + printk(BIOS_INFO, "input"); + else if (cfg == GPIO_OUTPUT) + printk(BIOS_INFO, "output"); + else + printk(BIOS_INFO, "func %d", cfg); + + if (cfg == GPIO_INPUT || cfg == GPIO_OUTPUT) + printk(BIOS_INFO, ", value = %d", gpio_get_value(gpio)); + printk(BIOS_INFO, "\n"); + } +} diff --git a/src/soc/samsung/exynos5420/gpio.h b/src/soc/samsung/exynos5420/gpio.h new file mode 100644 index 0000000000..4cf8e57249 --- /dev/null +++ b/src/soc/samsung/exynos5420/gpio.h @@ -0,0 +1,550 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2010 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef CPU_SAMSUNG_EXYNOS5420_GPIO_H +#define CPU_SAMSUNG_EXYNOS5420_GPIO_H + +#include "cpu.h" + +struct gpio_bank { + unsigned int con; + unsigned int dat; + unsigned int pull; + unsigned int drv; + unsigned int pdn_con; + unsigned int pdn_pull; + unsigned char res1[8]; +}; + +/* GPIO pins per bank */ +#define GPIO_PER_BANK 8 + +/* Pin configurations */ +#define GPIO_INPUT 0x0 +#define GPIO_OUTPUT 0x1 +#define GPIO_IRQ 0xf +#define GPIO_FUNC(x) (x) + +/* Pull mode */ +#define GPIO_PULL_NONE 0x0 +#define GPIO_PULL_DOWN 0x1 +#define GPIO_PULL_UP 0x3 + +/* Drive Strength level */ +#define GPIO_DRV_1X 0x0 +#define GPIO_DRV_3X 0x1 +#define GPIO_DRV_2X 0x2 +#define GPIO_DRV_4X 0x3 +#define GPIO_DRV_FAST 0x0 +#define GPIO_DRV_SLOW 0x1 + +enum exynos5_gpio_port { + EXYNOS5_GPY7 = EXYNOS5420_GPIO_PART1_BASE + 0x0000, + + EXYNOS5_GPX0 = EXYNOS5420_GPIO_PART2_BASE + 0x0000, + EXYNOS5_GPX1 = EXYNOS5420_GPIO_PART2_BASE + 0x0020, + EXYNOS5_GPX2 = EXYNOS5420_GPIO_PART2_BASE + 0x0040, + EXYNOS5_GPX3 = EXYNOS5420_GPIO_PART2_BASE + 0x0060, + + EXYNOS5_GPC0 = EXYNOS5420_GPIO_PART3_BASE + 0x0000, + EXYNOS5_GPC1 = EXYNOS5420_GPIO_PART3_BASE + 0x0020, + EXYNOS5_GPC2 = EXYNOS5420_GPIO_PART3_BASE + 0x0040, + EXYNOS5_GPC3 = EXYNOS5420_GPIO_PART3_BASE + 0x0060, + EXYNOS5_GPC4 = EXYNOS5420_GPIO_PART3_BASE + 0x0080, + + EXYNOS5_GPD1 = EXYNOS5420_GPIO_PART3_BASE + 0x00a0, + + EXYNOS5_GPY0 = EXYNOS5420_GPIO_PART3_BASE + 0x00c0, + EXYNOS5_GPY1 = EXYNOS5420_GPIO_PART3_BASE + 0x00e0, + EXYNOS5_GPY2 = EXYNOS5420_GPIO_PART3_BASE + 0x0100, + EXYNOS5_GPY3 = EXYNOS5420_GPIO_PART3_BASE + 0x0120, + EXYNOS5_GPY4 = EXYNOS5420_GPIO_PART3_BASE + 0x0140, + EXYNOS5_GPY5 = EXYNOS5420_GPIO_PART3_BASE + 0x0160, + EXYNOS5_GPY6 = EXYNOS5420_GPIO_PART3_BASE + 0x0180, + + EXYNOS5_GPE0 = EXYNOS5420_GPIO_PART4_BASE + 0x0000, + EXYNOS5_GPE1 = EXYNOS5420_GPIO_PART4_BASE + 0x0020, + + EXYNOS5_GPF0 = EXYNOS5420_GPIO_PART4_BASE + 0x0040, + EXYNOS5_GPF1 = EXYNOS5420_GPIO_PART4_BASE + 0x0060, + + EXYNOS5_GPG0 = EXYNOS5420_GPIO_PART4_BASE + 0x0080, + EXYNOS5_GPG1 = EXYNOS5420_GPIO_PART4_BASE + 0x00a0, + EXYNOS5_GPG2 = EXYNOS5420_GPIO_PART4_BASE + 0x00c0, + + EXYNOS5_GPJ4 = EXYNOS5420_GPIO_PART4_BASE + 0x00e0, + + /* base == EXYNOS5420_GPIO_PART5_BASE */ + EXYNOS5_GPA0 = EXYNOS5420_GPIO_PART5_BASE + 0x0000, + EXYNOS5_GPA1 = EXYNOS5420_GPIO_PART5_BASE + 0x0020, + EXYNOS5_GPA2 = EXYNOS5420_GPIO_PART5_BASE + 0x0040, + + EXYNOS5_GPB0 = EXYNOS5420_GPIO_PART5_BASE + 0x0060, + EXYNOS5_GPB1 = EXYNOS5420_GPIO_PART5_BASE + 0x0080, + EXYNOS5_GPB2 = EXYNOS5420_GPIO_PART5_BASE + 0x00a0, + EXYNOS5_GPB3 = EXYNOS5420_GPIO_PART5_BASE + 0x00c0, + EXYNOS5_GPB4 = EXYNOS5420_GPIO_PART5_BASE + 0x00e0, + + EXYNOS5_GPH0 = EXYNOS5420_GPIO_PART5_BASE + 0x0100, + + /* base == EXYNOS5420_GPIO_PART6_BASE */ + EXYNOS5_GPZ0 = EXYNOS5420_GPIO_PART6_BASE + 0x0000, +}; + +enum { + /* GPIO banks are split into this many parts */ + EXYNOS_GPIO_NUM_PARTS = 6 +}; + +/* A list of valid GPIO numbers for the asm-generic/gpio.h interface */ +enum exynos5_gpio_pin { + /* GPIO_PART1_STARTS */ + GPIO_Y70, + GPIO_Y71, + GPIO_Y72, + GPIO_Y73, + GPIO_Y74, + GPIO_Y75, + GPIO_Y76, + GPIO_Y77, + + /* GPIO_PART2_STARTS */ + GPIO_MAX_PORT_PART_1, + GPIO_X00 = GPIO_MAX_PORT_PART_1, /* 0x08 */ + GPIO_X01, + GPIO_X02, + GPIO_X03, + GPIO_X04, + GPIO_X05, + GPIO_X06, + GPIO_X07, + GPIO_X10, + GPIO_X11, + GPIO_X12, + GPIO_X13, + GPIO_X14, + GPIO_X15, + GPIO_X16, + GPIO_X17, + GPIO_X20, + GPIO_X21, + GPIO_X22, + GPIO_X23, + GPIO_X24, + GPIO_X25, + GPIO_X26, + GPIO_X27, + GPIO_X30, + GPIO_X31, + GPIO_X32, + GPIO_X33, + GPIO_X34, + GPIO_X35, + GPIO_X36, + GPIO_X37, + + /* GPIO_PART3_STARTS */ + GPIO_MAX_PORT_PART_2, + GPIO_C00 = GPIO_MAX_PORT_PART_2, /* 0x28 */ + GPIO_C01, + GPIO_C02, + GPIO_C03, + GPIO_C04, + GPIO_C05, + GPIO_C06, + GPIO_C07, + GPIO_C10, + GPIO_C11, + GPIO_C12, + GPIO_C13, + GPIO_C14, + GPIO_C15, + GPIO_C16, + GPIO_C17, + GPIO_C20, + GPIO_C21, + GPIO_C22, + GPIO_C23, + GPIO_C24, + GPIO_C25, + GPIO_C26, + GPIO_C27, + GPIO_C30, + GPIO_C31, + GPIO_C32, + GPIO_C33, + GPIO_C34, + GPIO_C35, + GPIO_C36, + GPIO_C37, + GPIO_C40, + GPIO_C41, + GPIO_C42, + GPIO_C43, + GPIO_C44, + GPIO_C45, + GPIO_C46, + GPIO_C47, + + GPIO_D10, /* 0x50 */ + GPIO_D11, + GPIO_D12, + GPIO_D13, + GPIO_D14, + GPIO_D15, + GPIO_D16, + GPIO_D17, + + GPIO_Y00, /* 0x58 */ + GPIO_Y01, + GPIO_Y02, + GPIO_Y03, + GPIO_Y04, + GPIO_Y05, + GPIO_Y06, + GPIO_Y07, + GPIO_Y10, + GPIO_Y11, + GPIO_Y12, + GPIO_Y13, + GPIO_Y14, + GPIO_Y15, + GPIO_Y16, + GPIO_Y17, + GPIO_Y20, + GPIO_Y21, + GPIO_Y22, + GPIO_Y23, + GPIO_Y24, + GPIO_Y25, + GPIO_Y26, + GPIO_Y27, + GPIO_Y30, + GPIO_Y31, + GPIO_Y32, + GPIO_Y33, + GPIO_Y34, + GPIO_Y35, + GPIO_Y36, + GPIO_Y37, + GPIO_Y40, + GPIO_Y41, + GPIO_Y42, + GPIO_Y43, + GPIO_Y44, + GPIO_Y45, + GPIO_Y46, + GPIO_Y47, + GPIO_Y50, + GPIO_Y51, + GPIO_Y52, + GPIO_Y53, + GPIO_Y54, + GPIO_Y55, + GPIO_Y56, + GPIO_Y57, + GPIO_Y60, + GPIO_Y61, + GPIO_Y62, + GPIO_Y63, + GPIO_Y64, + GPIO_Y65, + GPIO_Y66, + GPIO_Y67, + + /* GPIO_PART4_STARTS */ + GPIO_MAX_PORT_PART_3, + GPIO_E00 = GPIO_MAX_PORT_PART_3, /* 0x90 */ + GPIO_E01, + GPIO_E02, + GPIO_E03, + GPIO_E04, + GPIO_E05, + GPIO_E06, + GPIO_E07, + GPIO_E10, + GPIO_E11, + GPIO_E12, + GPIO_E13, + GPIO_E14, + GPIO_E15, + GPIO_E16, + GPIO_E17, + + GPIO_F00, /* 0xa0 */ + GPIO_F01, + GPIO_F02, + GPIO_F03, + GPIO_F04, + GPIO_F05, + GPIO_F06, + GPIO_F07, + GPIO_F10, + GPIO_F11, + GPIO_F12, + GPIO_F13, + GPIO_F14, + GPIO_F15, + GPIO_F16, + GPIO_F17, + + GPIO_G00, /* 0xb0 */ + GPIO_G01, + GPIO_G02, + GPIO_G03, + GPIO_G04, + GPIO_G05, + GPIO_G06, + GPIO_G07, + GPIO_G10, + GPIO_G11, + GPIO_G12, + GPIO_G13, + GPIO_G14, + GPIO_G15, + GPIO_G16, + GPIO_G17, + GPIO_G20, + GPIO_G21, + GPIO_G22, + GPIO_G23, + GPIO_G24, + GPIO_G25, + GPIO_G26, + GPIO_G27, + + GPIO_J40, /* 0xc8 */ + GPIO_J41, + GPIO_J42, + GPIO_J43, + GPIO_J44, + GPIO_J45, + GPIO_J46, + GPIO_J47, + + /* GPIO_PART5_STARTS */ + GPIO_MAX_PORT_PART_4, + GPIO_A00 = GPIO_MAX_PORT_PART_4, /* 0xd0 */ + GPIO_A01, + GPIO_A02, + GPIO_A03, + GPIO_A04, + GPIO_A05, + GPIO_A06, + GPIO_A07, + GPIO_A10, + GPIO_A11, + GPIO_A12, + GPIO_A13, + GPIO_A14, + GPIO_A15, + GPIO_A16, + GPIO_A17, + GPIO_A20, + GPIO_A21, + GPIO_A22, + GPIO_A23, + GPIO_A24, + GPIO_A25, + GPIO_A26, + GPIO_A27, + + GPIO_B00, /* 0xe8 */ + GPIO_B01, + GPIO_B02, + GPIO_B03, + GPIO_B04, + GPIO_B05, + GPIO_B06, + GPIO_B07, + GPIO_B10, + GPIO_B11, + GPIO_B12, + GPIO_B13, + GPIO_B14, + GPIO_B15, + GPIO_B16, + GPIO_B17, + GPIO_B20, + GPIO_B21, + GPIO_B22, + GPIO_B23, + GPIO_B24, + GPIO_B25, + GPIO_B26, + GPIO_B27, + GPIO_B30, + GPIO_B31, + GPIO_B32, + GPIO_B33, + GPIO_B34, + GPIO_B35, + GPIO_B36, + GPIO_B37, + GPIO_B40, + GPIO_B41, + GPIO_B42, + GPIO_B43, + GPIO_B44, + GPIO_B45, + GPIO_B46, + GPIO_B47, + + GPIO_H00, /* 0x110 */ + GPIO_H01, + GPIO_H02, + GPIO_H03, + GPIO_H04, + GPIO_H05, + GPIO_H06, + GPIO_H07, + + /* GPIO_PART6_STARTS */ + GPIO_MAX_PORT_PART_5, + GPIO_Z00 = GPIO_MAX_PORT_PART_5, /* 0x118 */ + GPIO_Z01, + GPIO_Z02, + GPIO_Z03, + GPIO_Z04, + GPIO_Z05, + GPIO_Z06, + GPIO_Z07, + GPIO_MAX_PORT +}; + +/** + * Set GPIO pin configuration. + * + * @param gpio GPIO pin + * @param cfg Either GPIO_INPUT, GPIO_OUTPUT, or GPIO_IRQ + */ +void gpio_cfg_pin(int gpio, int cfg); + +/** + * Set GPIO pull mode. + * + * @param gpio GPIO pin + * @param mode Either GPIO_PULL_DOWN or GPIO_PULL_UP + */ +void gpio_set_pull(int gpio, int mode); + +/** + * Set GPIO drive strength level. + * + * @param gpio GPIO pin + * @param mode Either GPIO_DRV_1X, GPIO_DRV_2X, GPIO_DRV_3X, or GPIO_DRV_4X + */ +void gpio_set_drv(int gpio, int mode); + +/** + * Set GPIO drive rate. + * + * @param gpio GPIO pin + * @param mode Either GPIO_DRV_FAST or GPIO_DRV_SLOW + */ +void gpio_set_rate(int gpio, int mode); + +/* + * reads only a single GPIO + * + * @param gpio GPIO to read + * @return -1 if the value cannot be determined. Otherwise returns + * the corresponding MVL3 enum value. + */ +int gpio_read_mvl3(unsigned gpio); + +void gpio_info(void); + +/* + * Generic GPIO API for U-Boot + * + * GPIOs are numbered from 0 to GPIO_COUNT-1 which value is defined + * by the SOC/architecture. + * + * Each GPIO can be an input or output. If an input then its value can + * be read as 0 or 1. If an output then its value can be set to 0 or 1. + * If you try to write an input then the value is undefined. If you try + * to read an output, barring something very unusual, you will get + * back the value of the output that you previously set. + * + * In some cases the operation may fail, for example if the GPIO number + * is out of range, or the GPIO is not available because its pin is + * being used by another function. In that case, functions may return + * an error value of -1. + */ + +/** + * Make a GPIO an input. + * + * @param gpio GPIO number + * @return 0 if ok, -1 on error + */ +int gpio_direction_input(unsigned gpio); + +/** + * Make a GPIO an output, and set its value. + * + * @param gpio GPIO number + * @param value GPIO value (0 for low or 1 for high) + * @return 0 if ok, -1 on error + */ +int gpio_direction_output(unsigned gpio, int value); + +/** + * Get a GPIO's value. This will work whether the GPIO is an input + * or an output. + * + * @param gpio GPIO number + * @return 0 if low, 1 if high, -1 on error + */ +int gpio_get_value(unsigned gpio); + +/** + * Set an output GPIO's value. The GPIO must already be an output or + * this function may have no effect. + * + * @param gpio GPIO number + * @param value GPIO value (0 for low or 1 for high) + * @return 0 if ok, -1 on error + */ +int gpio_set_value(unsigned gpio, int value); + +/* + * Many-value logic (3 states). This can be used for inputs whereby presence + * of external pull-up or pull-down resistors can be added to overcome internal + * pull-ups/pull-downs and force a single value. + * + * Thus, external pull resistors can force a 0 or 1 and if the value changes + * along with internal pull-up/down enable then the input is floating. + * + * Vpd | Vpu | MVL + * ----------------- + * 0 | 0 | 0 + * ----------------- + * 0 | 1 | Z <-- floating input will follow internal pull up/down + * ----------------- + * 1 | 1 | 1 + */ +enum mvl3 { + LOGIC_0, + LOGIC_1, + LOGIC_Z, /* high impedence / tri-stated / floating */ +}; + +#endif /* CPU_SAMSUNG_EXYNOS5420_GPIO_H */ diff --git a/src/soc/samsung/exynos5420/i2c.c b/src/soc/samsung/exynos5420/i2c.c new file mode 100644 index 0000000000..226862802a --- /dev/null +++ b/src/soc/samsung/exynos5420/i2c.c @@ -0,0 +1,706 @@ +/* + * This file is part of the coreboot project. + * + * (C) Copyright 2002 + * David Mueller, ELSOFT AG, d.mueller@elsoft.ch + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <console/console.h> +#include <delay.h> +#include <timer.h> +#include <arch/io.h> +#include <device/i2c.h> +#include "clk.h" +#include "i2c.h" +#include "pinmux.h" + +#define I2C_WRITE 0 +#define I2C_READ 1 + +#define I2C_OK 0 +#define I2C_NOK 1 +#define I2C_NACK 2 +#define I2C_NOK_LA 3 /* Lost arbitration */ +#define I2C_NOK_TOUT 4 /* time out */ + +/* HSI2C specific register description */ + +/* I2C_CTL Register bits */ +/* FIXME(dhendrix): do we really need to cast these as unsigned? */ +#define HSI2C_FUNC_MODE_I2C (1u << 0) +#define HSI2C_MASTER (1u << 3) +#define HSI2C_RXCHON (1u << 6) /* Write/Send */ +#define HSI2C_TXCHON (1u << 7) /* Read/Receive */ +#define HSI2C_SW_RST (1u << 31) + +/* I2C_FIFO_STAT Register bits */ +#define HSI2C_TX_FIFO_LEVEL (0x7f << 0) +#define HSI2C_TX_FIFO_FULL (1u << 7) +#define HSI2C_TX_FIFO_EMPTY (1u << 8) +#define HSI2C_RX_FIFO_LEVEL (0x7f << 16) +#define HSI2C_RX_FIFO_FULL (1u << 23) +#define HSI2C_RX_FIFO_EMPTY (1u << 24) + +/* I2C_FIFO_CTL Register bits */ +#define HSI2C_RXFIFO_EN (1u << 0) +#define HSI2C_TXFIFO_EN (1u << 1) +#define HSI2C_TXFIFO_TRIGGER_LEVEL (0x20 << 16) +#define HSI2C_RXFIFO_TRIGGER_LEVEL (0x20 << 4) + +/* I2C_TRAILING_CTL Register bits */ +#define HSI2C_TRAILING_COUNT (0xff) + +/* I2C_INT_EN Register bits */ +#define HSI2C_INT_TX_ALMOSTEMPTY_EN (1u << 0) +#define HSI2C_INT_RX_ALMOSTFULL_EN (1u << 1) +#define HSI2C_INT_TRAILING_EN (1u << 6) +#define HSI2C_INT_I2C_EN (1u << 9) + +/* I2C_CONF Register bits */ +#define HSI2C_AUTO_MODE (1u << 31) +#define HSI2C_10BIT_ADDR_MODE (1u << 30) +#define HSI2C_HS_MODE (1u << 29) + +/* I2C_AUTO_CONF Register bits */ +#define HSI2C_READ_WRITE (1u << 16) +#define HSI2C_STOP_AFTER_TRANS (1u << 17) +#define HSI2C_MASTER_RUN (1u << 31) + +/* I2C_TIMEOUT Register bits */ +#define HSI2C_TIMEOUT_EN (1u << 31) + +/* I2C_TRANS_STATUS register bits */ +#define HSI2C_MASTER_BUSY (1u << 17) +#define HSI2C_SLAVE_BUSY (1u << 16) +#define HSI2C_TIMEOUT_AUTO (1u << 4) +#define HSI2C_NO_DEV (1u << 3) +#define HSI2C_NO_DEV_ACK (1u << 2) +#define HSI2C_TRANS_ABORT (1u << 1) +#define HSI2C_TRANS_DONE (1u << 0) + +#define HSI2C_SLV_ADDR_MAS(x) ((x & 0x3ff) << 10) + +/* S3C I2C Controller bits */ +#define I2CSTAT_BSY 0x20 /* Busy bit */ +#define I2CSTAT_NACK 0x01 /* Nack bit */ +#define I2CCON_ACKGEN 0x80 /* Acknowledge generation */ +#define I2CCON_IRPND 0x10 /* Interrupt pending bit */ +#define I2C_MODE_MT 0xC0 /* Master Transmit Mode */ +#define I2C_MODE_MR 0x80 /* Master Receive Mode */ +#define I2C_START_STOP 0x20 /* START / STOP */ +#define I2C_TXRX_ENA 0x10 /* I2C Tx/Rx enable */ + +#define I2C_TIMEOUT_MS 1000 /* 1 second */ + +#define HSI2C_TIMEOUT 100 + +/* The timeouts we live by */ +enum { + I2C_XFER_TIMEOUT_MS = 35, /* xfer to complete */ + I2C_INIT_TIMEOUT_MS = 1000, /* bus free on init */ + I2C_IDLE_TIMEOUT_MS = 100, /* waiting for bus idle */ + I2C_STOP_TIMEOUT_US = 200, /* waiting for stop events */ +}; + +static struct s3c24x0_i2c_bus i2c_buses[] = { + { + .bus_num = 0, + .regs = (struct s3c24x0_i2c *)0x12c60000, + .periph_id = PERIPH_ID_I2C0, + }, + { + .bus_num = 1, + .regs = (struct s3c24x0_i2c *)0x12c70000, + .periph_id = PERIPH_ID_I2C1, + }, + { + .bus_num = 2, + .regs = (struct s3c24x0_i2c *)0x12c80000, + .periph_id = PERIPH_ID_I2C2, + }, + { + .bus_num = 3, + .regs = (struct s3c24x0_i2c *)0x12c90000, + .periph_id = PERIPH_ID_I2C3, + }, + /* I2C4-I2C10 are part of the USI block */ + { + .bus_num = 4, + .hsregs = (struct exynos5_hsi2c *)0x12ca0000, + .periph_id = PERIPH_ID_I2C4, + .is_highspeed = 1, + }, + { + .bus_num = 5, + .hsregs = (struct exynos5_hsi2c *)0x12cb0000, + .periph_id = PERIPH_ID_I2C5, + .is_highspeed = 1, + }, + { + .bus_num = 6, + .hsregs = (struct exynos5_hsi2c *)0x12cc0000, + .periph_id = PERIPH_ID_I2C6, + .is_highspeed = 1, + }, + { + .bus_num = 7, + .hsregs = (struct exynos5_hsi2c *)0x12cd0000, + .periph_id = PERIPH_ID_I2C7, + .is_highspeed = 1, + }, + { + .bus_num = 8, + .hsregs = (struct exynos5_hsi2c *)0x12e00000, + .periph_id = PERIPH_ID_I2C8, + .is_highspeed = 1, + }, + { + .bus_num = 9, + .hsregs = (struct exynos5_hsi2c *)0x12e10000, + .periph_id = PERIPH_ID_I2C9, + .is_highspeed = 1, + }, + { + .bus_num = 10, + .hsregs = (struct exynos5_hsi2c *)0x12e20000, + .periph_id = PERIPH_ID_I2C10, + .is_highspeed = 1, + }, +}; + +/* + * Wait til the byte transfer is completed. + * + * @param i2c- pointer to the appropriate i2c register bank. + * @return I2C_OK, if transmission was ACKED + * I2C_NACK, if transmission was NACKED + * I2C_NOK_TIMEOUT, if transaction did not complete in I2C_TIMEOUT_MS + */ + +static int WaitForXfer(struct s3c24x0_i2c *i2c) +{ + struct mono_time current, end; + + timer_monotonic_get(¤t); + end = current; + mono_time_add_usecs(&end, I2C_TIMEOUT_MS * 1000); + do { + if (read32(&i2c->iiccon) & I2CCON_IRPND) + return (read32(&i2c->iicstat) & I2CSTAT_NACK) ? + I2C_NACK : I2C_OK; + timer_monotonic_get(¤t); + } while (mono_time_before(¤t, &end)); + + printk(BIOS_ERR, "%s timed out\n", __func__); + return I2C_NOK_TOUT; +} + +static void ReadWriteByte(struct s3c24x0_i2c *i2c) +{ + writel(read32(&i2c->iiccon) & ~I2CCON_IRPND, &i2c->iiccon); +} + +static void i2c_ch_init(struct s3c24x0_i2c_bus *bus, int speed, int slaveadd) +{ + unsigned long freq, pres = 16, div; + unsigned long val; + + freq = clock_get_periph_rate(bus->periph_id); + /* calculate prescaler and divisor values */ + if ((freq / pres / (16 + 1)) > speed) + /* set prescaler to 512 */ + pres = 512; + + div = 0; + while ((freq / pres / (div + 1)) > speed) + div++; + + /* set prescaler, divisor according to freq, also set ACKGEN, IRQ */ + val = (div & 0x0F) | 0xA0 | ((pres == 512) ? 0x40 : 0); + write32(val, &bus->regs->iiccon); + + /* init to SLAVE RECEIVE mode and clear I2CADDn */ + write32(0, &bus->regs->iicstat); + write32(slaveadd, &bus->regs->iicadd); + /* program Master Transmit (and implicit STOP) */ + write32(I2C_MODE_MT | I2C_TXRX_ENA, &bus->regs->iicstat); +} + +static int hsi2c_get_clk_details(struct s3c24x0_i2c_bus *i2c_bus, + unsigned int bus_freq_hz) +{ + struct exynos5_hsi2c *hsregs = i2c_bus->hsregs; + unsigned long clkin = clock_get_periph_rate(i2c_bus->periph_id); + unsigned int i = 0, utemp0 = 0, utemp1 = 0; + unsigned int t_ftl_cycle; + + /* FPCLK / FI2C = + * (CLK_DIV + 1) * (TSCLK_L + TSCLK_H + 2) + 8 + 2 * FLT_CYCLE + * uTemp0 = (CLK_DIV + 1) * (TSCLK_L + TSCLK_H + 2) + * uTemp1 = (TSCLK_L + TSCLK_H + 2) + * uTemp2 = TSCLK_L + TSCLK_H + */ + t_ftl_cycle = (read32(&hsregs->usi_conf) >> 16) & 0x7; + utemp0 = (clkin / bus_freq_hz) - 8 - 2 * t_ftl_cycle; + + /* CLK_DIV max is 256 */ + for (i = 0; i < 256; i++) { + utemp1 = utemp0 / (i + 1); + if ((utemp1 < 512) && (utemp1 > 4)) { + i2c_bus->clk_cycle = utemp1 - 2; + i2c_bus->clk_div = i; + return 0; + } + } + printk(BIOS_ERR, "%s: failed?\n", __func__); + return -1; +} + +static void hsi2c_ch_init(struct s3c24x0_i2c_bus *i2c_bus, + unsigned int bus_freq_hz) +{ + struct exynos5_hsi2c *hsregs = i2c_bus->hsregs; + unsigned int t_sr_release; + unsigned int n_clkdiv; + unsigned int t_start_su, t_start_hd; + unsigned int t_stop_su; + unsigned int t_data_su, t_data_hd; + unsigned int t_scl_l, t_scl_h; + u32 i2c_timing_s1; + u32 i2c_timing_s2; + u32 i2c_timing_s3; + u32 i2c_timing_sla; + + hsi2c_get_clk_details(i2c_bus, bus_freq_hz); + + n_clkdiv = i2c_bus->clk_div; + t_scl_l = i2c_bus->clk_cycle / 2; + t_scl_h = i2c_bus->clk_cycle / 2; + t_start_su = t_scl_l; + t_start_hd = t_scl_l; + t_stop_su = t_scl_l; + t_data_su = t_scl_l / 2; + t_data_hd = t_scl_l / 2; + t_sr_release = i2c_bus->clk_cycle; + + i2c_timing_s1 = t_start_su << 24 | t_start_hd << 16 | t_stop_su << 8; + i2c_timing_s2 = t_data_su << 24 | t_scl_l << 8 | t_scl_h << 0; + i2c_timing_s3 = n_clkdiv << 16 | t_sr_release << 0; + i2c_timing_sla = t_data_hd << 0; + + write32(HSI2C_TRAILING_COUNT, &hsregs->usi_trailing_ctl); + + /* Clear to enable Timeout */ + clrsetbits_le32(&hsregs->usi_timeout, HSI2C_TIMEOUT_EN, 0); + + write32(read32(&hsregs->usi_conf) | HSI2C_AUTO_MODE, &hsregs->usi_conf); + + /* Currently operating in Fast speed mode. */ + write32(i2c_timing_s1, &hsregs->usi_timing_fs1); + write32(i2c_timing_s2, &hsregs->usi_timing_fs2); + write32(i2c_timing_s3, &hsregs->usi_timing_fs3); + write32(i2c_timing_sla, &hsregs->usi_timing_sla); + + /* Enable TXFIFO and RXFIFO */ + write32(HSI2C_RXFIFO_EN | HSI2C_TXFIFO_EN, &hsregs->usi_fifo_ctl); + + /* i2c_conf configure */ + write32(readl(&hsregs->usi_conf) | HSI2C_AUTO_MODE, &hsregs->usi_conf); +} + +/* SW reset for the high speed bus */ +static void i2c_reset(struct s3c24x0_i2c_bus *i2c_bus) +{ + struct exynos5_hsi2c *i2c = i2c_bus->hsregs; + u32 i2c_ctl; + + /* Set and clear the bit for reset */ + i2c_ctl = read32(&i2c->usi_ctl); + i2c_ctl |= HSI2C_SW_RST; + write32(i2c_ctl, &i2c->usi_ctl); + + i2c_ctl = read32(&i2c->usi_ctl); + i2c_ctl &= ~HSI2C_SW_RST; + write32(i2c_ctl, &i2c->usi_ctl); + + /* Initialize the configure registers */ + /* FIXME: This just assumes 100KHz as a default bus freq */ + hsi2c_ch_init(i2c_bus, 100000); +} + +void i2c_init(unsigned bus_num, int speed, int slaveadd) +{ + struct s3c24x0_i2c_bus *i2c; + + i2c = &i2c_buses[bus_num]; + + i2c_reset(i2c); + + if (i2c->is_highspeed) + hsi2c_ch_init(i2c, speed); + else + i2c_ch_init(i2c, speed, slaveadd); +} + +/* + * Check whether the transfer is complete. + * Return values: + * 0 - transfer not done + * 1 - transfer finished successfully + * -1 - transfer failed + */ +static int hsi2c_check_transfer(struct exynos5_hsi2c *i2c) +{ + uint32_t status = read32(&i2c->usi_trans_status); + if (status & (HSI2C_TRANS_ABORT | HSI2C_NO_DEV_ACK | + HSI2C_NO_DEV | HSI2C_TIMEOUT_AUTO)) { + if (status & HSI2C_TRANS_ABORT) + printk(BIOS_ERR, + "%s: Transaction aborted.\n", __func__); + if (status & HSI2C_NO_DEV_ACK) + printk(BIOS_ERR, + "%s: No ack from device.\n", __func__); + if (status & HSI2C_NO_DEV) + printk(BIOS_ERR, + "%s: No response from device.\n", __func__); + if (status & HSI2C_TIMEOUT_AUTO) + printk(BIOS_ERR, + "%s: Transaction time out.\n", __func__); + return -1; + } + return !(status & HSI2C_MASTER_BUSY); +} + +/* + * Wait for the transfer to finish. + * Return values: + * 0 - transfer not done + * 1 - transfer finished successfully + * -1 - transfer failed + */ +static int hsi2c_wait_for_transfer(struct exynos5_hsi2c *i2c) +{ + struct mono_time current, end; + + timer_monotonic_get(¤t); + end = current; + mono_time_add_usecs(&end, HSI2C_TIMEOUT * 1000); + while (mono_time_before(¤t, &end)) { + int ret = hsi2c_check_transfer(i2c); + if (ret) + return ret; + udelay(5); + timer_monotonic_get(¤t); + } + return 0; +} + +static int hsi2c_senddata(struct exynos5_hsi2c *i2c, const uint8_t *data, + int len) +{ + while (!hsi2c_check_transfer(i2c) && len) { + if (!(read32(&i2c->usi_fifo_stat) & HSI2C_TX_FIFO_FULL)) { + write32(*data++, &i2c->usi_txdata); + len--; + } + } + return len ? -1 : 0; +} + +static int hsi2c_recvdata(struct exynos5_hsi2c *i2c, uint8_t *data, int len) +{ + while (!hsi2c_check_transfer(i2c) && len) { + if (!(read32(&i2c->usi_fifo_stat) & HSI2C_RX_FIFO_EMPTY)) { + *data++ = read32(&i2c->usi_rxdata); + len--; + } + } + return len ? -1 : 0; +} + +static int hsi2c_write(struct exynos5_hsi2c *i2c, + unsigned char chip, + unsigned char addr[], + unsigned char alen, + const uint8_t data[], + unsigned short len) +{ + uint32_t i2c_auto_conf; + + if (hsi2c_wait_for_transfer(i2c) != 1) + return -1; + + /* chip address */ + write32(HSI2C_SLV_ADDR_MAS(chip), &i2c->i2c_addr); + + /* usi_ctl enable i2c func, master write configure */ + write32((HSI2C_TXCHON | HSI2C_FUNC_MODE_I2C | HSI2C_MASTER), + &i2c->usi_ctl); + + /* auto_conf for write length and stop configure */ + i2c_auto_conf = ((len + alen) | HSI2C_STOP_AFTER_TRANS); + i2c_auto_conf &= ~HSI2C_READ_WRITE; + /* Master run, start xfer */ + i2c_auto_conf |= HSI2C_MASTER_RUN; + write32(i2c_auto_conf, &i2c->usi_auto_conf); + + if (hsi2c_senddata(i2c, addr, alen) || + hsi2c_senddata(i2c, data, len) || + hsi2c_wait_for_transfer(i2c) != 1) { + return -1; + } + + write32(HSI2C_FUNC_MODE_I2C, &i2c->usi_ctl); + return 0; +} + +static int hsi2c_read(struct exynos5_hsi2c *i2c, + unsigned char chip, + unsigned char addr[], + unsigned char alen, + uint8_t data[], + unsigned short len, + int check) +{ + uint32_t i2c_auto_conf; + + /* start read */ + if (hsi2c_wait_for_transfer(i2c) != 1) + return -1; + + /* chip address */ + write32(HSI2C_SLV_ADDR_MAS(chip), &i2c->i2c_addr); + + /* usi_ctl enable i2c func, master write configure */ + write32((HSI2C_TXCHON | HSI2C_FUNC_MODE_I2C | HSI2C_MASTER), + &i2c->usi_ctl); + + /* auto_conf */ + write32(alen | HSI2C_MASTER_RUN | HSI2C_STOP_AFTER_TRANS, + &i2c->usi_auto_conf); + + if (hsi2c_senddata(i2c, addr, alen) || + hsi2c_wait_for_transfer(i2c) != 1) { + return -1; + } + + /* usi_ctl enable i2c func, master WRITE configure */ + write32((HSI2C_RXCHON | HSI2C_FUNC_MODE_I2C | HSI2C_MASTER), + &i2c->usi_ctl); + + /* auto_conf, length and stop configure */ + i2c_auto_conf = (len | HSI2C_STOP_AFTER_TRANS | HSI2C_READ_WRITE); + i2c_auto_conf |= HSI2C_MASTER_RUN; + /* Master run, start xfer */ + write32(i2c_auto_conf, &i2c->usi_auto_conf); + + if (hsi2c_recvdata(i2c, data, len) || + hsi2c_wait_for_transfer(i2c) != 1) { + return -1; + } + + write32(HSI2C_FUNC_MODE_I2C, &i2c->usi_ctl); + return 0; +} + +/* + * cmd_type is 0 for write, 1 for read. + * + * addr_len can take any value from 0-255, it is only limited + * by the char, we could make it larger if needed. If it is + * 0 we skip the address write cycle. + */ +static int i2c_transfer(struct s3c24x0_i2c *i2c, + unsigned char cmd_type, + unsigned char chip, + unsigned char addr[], + unsigned char addr_len, + unsigned char data[], + unsigned short data_len) +{ + int i = 0, result; + struct mono_time current, end; + + if (data == 0 || data_len == 0) { + printk(BIOS_ERR, "i2c_transfer: bad call\n"); + return I2C_NOK; + } + + timer_monotonic_get(¤t); + end = current; + mono_time_add_usecs(&end, I2C_TIMEOUT_MS * 1000); + while (readl(&i2c->iicstat) & I2CSTAT_BSY) { + if (!mono_time_before(¤t, &end)){ + printk(BIOS_ERR, "%s timed out\n", __func__); + return I2C_NOK_TOUT; + } + timer_monotonic_get(¤t); + } + + write32(read32(&i2c->iiccon) | I2CCON_ACKGEN, &i2c->iiccon); + + /* Get the slave chip address going */ + write32(chip, &i2c->iicds); + if ((cmd_type == I2C_WRITE) || (addr && addr_len)) + write32(I2C_MODE_MT | I2C_TXRX_ENA | I2C_START_STOP, + &i2c->iicstat); + else + write32(I2C_MODE_MR | I2C_TXRX_ENA | I2C_START_STOP, + &i2c->iicstat); + + /* Wait for chip address to transmit. */ + result = WaitForXfer(i2c); + if (result != I2C_OK) + goto bailout; + + /* If register address needs to be transmitted - do it now. */ + if (addr && addr_len) { + while ((i < addr_len) && (result == I2C_OK)) { + write32(addr[i++], &i2c->iicds); + ReadWriteByte(i2c); + result = WaitForXfer(i2c); + } + i = 0; + if (result != I2C_OK) + goto bailout; + } + + switch (cmd_type) { + case I2C_WRITE: + while ((i < data_len) && (result == I2C_OK)) { + write32(data[i++], &i2c->iicds); + ReadWriteByte(i2c); + result = WaitForXfer(i2c); + } + break; + + case I2C_READ: + if (addr && addr_len) { + /* + * Register address has been sent, now send slave chip + * address again to start the actual read transaction. + */ + write32(chip, &i2c->iicds); + + /* Generate a re-START. */ + write32(I2C_MODE_MR | I2C_TXRX_ENA | I2C_START_STOP, + &i2c->iicstat); + ReadWriteByte(i2c); + result = WaitForXfer(i2c); + if (result != I2C_OK) + goto bailout; + } + + while ((i < data_len) && (result == I2C_OK)) { + /* disable ACK for final READ */ + if (i == data_len - 1) + write32(readl(&i2c->iiccon) + & ~I2CCON_ACKGEN, + &i2c->iiccon); + ReadWriteByte(i2c); + result = WaitForXfer(i2c); + data[i++] = read32(&i2c->iicds); + } + if (result == I2C_NACK) + result = I2C_OK; /* Normal terminated read. */ + break; + + default: + printk(BIOS_ERR, "i2c_transfer: bad call\n"); + result = I2C_NOK; + break; + } + +bailout: + /* Send STOP. */ + write32(I2C_MODE_MR | I2C_TXRX_ENA, &i2c->iicstat); + ReadWriteByte(i2c); + + return result; +} + +int i2c_read(unsigned bus, unsigned chip, unsigned addr, + unsigned alen, uint8_t *buf, unsigned len) +{ + struct s3c24x0_i2c_bus *i2c; + unsigned char xaddr[4]; + int ret; + + if (alen > 4) { + printk(BIOS_ERR, "I2C read: addr len %d not supported\n", alen); + return 1; + } + + if (alen > 0) { + xaddr[0] = (addr >> 24) & 0xFF; + xaddr[1] = (addr >> 16) & 0xFF; + xaddr[2] = (addr >> 8) & 0xFF; + xaddr[3] = addr & 0xFF; + } + + i2c = &i2c_buses[bus]; + if (i2c->is_highspeed) + ret = hsi2c_read(i2c->hsregs, chip, &xaddr[4 - alen], + alen, buf, len, 0); + else + ret = i2c_transfer(i2c->regs, I2C_READ, chip << 1, + &xaddr[4 - alen], alen, buf, len); + if (ret) { + i2c_reset(i2c); + printk(BIOS_ERR, "I2C read (bus %02x, chip addr %02x) failed: " + "%d\n", bus, chip, ret); + return 1; + } + return 0; +} + +int i2c_write(unsigned bus, unsigned chip, unsigned addr, + unsigned alen, const uint8_t *buf, unsigned len) +{ + struct s3c24x0_i2c_bus *i2c; + unsigned char xaddr[4]; + int ret; + + if (alen > 4) { + printk(BIOS_ERR, "I2C write: addr len %d not supported\n", + alen); + return 1; + } + + if (alen > 0) { + xaddr[0] = (addr >> 24) & 0xFF; + xaddr[1] = (addr >> 16) & 0xFF; + xaddr[2] = (addr >> 8) & 0xFF; + xaddr[3] = addr & 0xFF; + } + + i2c = &i2c_buses[bus]; + if (i2c->is_highspeed) + ret = hsi2c_write(i2c->hsregs, chip, &xaddr[4 - alen], + alen, buf, len); + else + ret = i2c_transfer(i2c->regs, I2C_WRITE, chip << 1, + &xaddr[4 - alen], alen, (void *)buf, len); + + + if (ret != 0) { + i2c_reset(i2c); + printk(BIOS_ERR, "I2C write (bus %02x, chip addr %02x) failed: " + "%d\n", bus, chip, ret); + return 1; + } + return 0; +} diff --git a/src/soc/samsung/exynos5420/i2c.h b/src/soc/samsung/exynos5420/i2c.h new file mode 100644 index 0000000000..e8fe8201db --- /dev/null +++ b/src/soc/samsung/exynos5420/i2c.h @@ -0,0 +1,78 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2012 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef CPU_SAMSUNG_EXYNOS5420_I2C_H +#define CPU_SAMSUNG_EXYNOS5420_I2C_H + +#include "periph.h" + +struct s3c24x0_i2c { + u32 iiccon; + u32 iicstat; + u32 iicadd; + u32 iicds; + u32 iiclc; +} __attribute__ ((packed)); + +struct exynos5_hsi2c { + u32 usi_ctl; + u32 usi_fifo_ctl; + u32 usi_trailing_ctl; + u32 usi_clk_ctl; + u32 usi_clk_slot; + u32 spi_ctl; + u32 uart_ctl; + u32 res1; + u32 usi_int_en; + u32 usi_int_stat; + u32 usi_modem_stat; + u32 usi_error_stat; + u32 usi_fifo_stat; + u32 usi_txdata; + u32 usi_rxdata; + u32 res2; + u32 usi_conf; + u32 usi_auto_conf; + u32 usi_timeout; + u32 usi_manual_cmd; + u32 usi_trans_status; + u32 usi_timing_hs1; + u32 usi_timing_hs2; + u32 usi_timing_hs3; + u32 usi_timing_fs1; + u32 usi_timing_fs2; + u32 usi_timing_fs3; + u32 usi_timing_sla; + u32 i2c_addr; +} __attribute__ ((packed)); + +struct s3c24x0_i2c_bus { + int bus_num; + struct s3c24x0_i2c *regs; + enum periph_id periph_id; + struct exynos5_hsi2c *hsregs; + int is_highspeed; /* High speed type, rather than I2C */ + int id; + unsigned clk_cycle; + unsigned clk_div; +}; + +void i2c_init(unsigned bus, int speed, int slaveadd); + +#endif /* CPU_SAMSUNG_EXYNOS5420_I2C_H */ diff --git a/src/soc/samsung/exynos5420/i2s-regs.h b/src/soc/samsung/exynos5420/i2s-regs.h new file mode 100644 index 0000000000..28d2685c63 --- /dev/null +++ b/src/soc/samsung/exynos5420/i2s-regs.h @@ -0,0 +1,142 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2012 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* Taken from the kernel code */ + +#ifndef CPU_SAMSUNG_EXYNOS5420_I2S_REGS_H +#define CPU_SAMSUNG_EXYNOS5420_I2S_REGS_H + +#define I2SCON 0x0 +#define I2SMOD 0x4 +#define I2SFIC 0x8 +#define I2SPSR 0xc +#define I2STXD 0x10 +#define I2SRXD 0x14 +#define I2SFICS 0x18 +#define I2STXDS 0x1c +#define I2SAHB 0x20 +#define I2SSTR0 0x24 +#define I2SSIZE 0x28 +#define I2STRNCNT 0x2c +#define I2SLVL0ADDR 0x30 +#define I2SLVL1ADDR 0x34 +#define I2SLVL2ADDR 0x38 +#define I2SLVL3ADDR 0x3c + +#define CON_RSTCLR (1 << 31) +#define CON_FRXOFSTATUS (1 << 26) +#define CON_FRXORINTEN (1 << 25) +#define CON_FTXSURSTAT (1 << 24) +#define CON_FTXSURINTEN (1 << 23) +#define CON_TXSDMA_PAUSE (1 << 20) +#define CON_TXSDMA_ACTIVE (1 << 18) + +#define CON_FTXURSTATUS (1 << 17) +#define CON_FTXURINTEN (1 << 16) +#define CON_TXFIFO2_EMPTY (1 << 15) +#define CON_TXFIFO1_EMPTY (1 << 14) +#define CON_TXFIFO2_FULL (1 << 13) +#define CON_TXFIFO1_FULL (1 << 12) + +#define CON_LRINDEX (1 << 11) +#define CON_TXFIFO_EMPTY (1 << 10) +#define CON_RXFIFO_EMPTY (1 << 9) +#define CON_TXFIFO_FULL (1 << 8) +#define CON_RXFIFO_FULL (1 << 7) +#define CON_TXDMA_PAUSE (1 << 6) +#define CON_RXDMA_PAUSE (1 << 5) +#define CON_TXCH_PAUSE (1 << 4) +#define CON_RXCH_PAUSE (1 << 3) +#define CON_TXDMA_ACTIVE (1 << 2) +#define CON_RXDMA_ACTIVE (1 << 1) +#define CON_ACTIVE (1 << 0) + +#define MOD_OPCLK_CDCLK_OUT (0 << 30) +#define MOD_OPCLK_CDCLK_IN (1 << 30) +#define MOD_OPCLK_BCLK_OUT (2 << 30) +#define MOD_OPCLK_PCLK (3 << 30) +#define MOD_OPCLK_MASK (3 << 30) +#define MOD_TXS_IDMA (1 << 28) /* Sec_TXFIFO use I-DMA */ + +#define MOD_BLCS_SHIFT 26 +#define MOD_BLCS_16BIT (0 << MOD_BLCS_SHIFT) +#define MOD_BLCS_8BIT (1 << MOD_BLCS_SHIFT) +#define MOD_BLCS_24BIT (2 << MOD_BLCS_SHIFT) +#define MOD_BLCS_MASK (3 << MOD_BLCS_SHIFT) + +#define MOD_BLCP_SHIFT 24 +#define MOD_BLCP_16BIT (0 << MOD_BLCP_SHIFT) +#define MOD_BLCP_8BIT (1 << MOD_BLCP_SHIFT) +#define MOD_BLCP_24BIT (2 << MOD_BLCP_SHIFT) +#define MOD_BLCP_MASK (3 << MOD_BLCP_SHIFT) + +#define MOD_C2DD_HHALF (1 << 21) /* Discard Higher-half */ +#define MOD_C2DD_LHALF (1 << 20) /* Discard Lower-half */ +#define MOD_C1DD_HHALF (1 << 19) +#define MOD_C1DD_LHALF (1 << 18) +#define MOD_DC2_EN (1 << 17) +#define MOD_DC1_EN (1 << 16) +#define MOD_BLC_16BIT (0 << 13) +#define MOD_BLC_8BIT (1 << 13) +#define MOD_BLC_24BIT (2 << 13) +#define MOD_BLC_MASK (3 << 13) + +#define MOD_IMS_SYSMUX (1 << 10) +#define MOD_SLAVE (1 << 11) +#define MOD_TXONLY (0 << 8) +#define MOD_RXONLY (1 << 8) +#define MOD_TXRX (2 << 8) +#define MOD_MASK (3 << 8) +#define MOD_LR_LLOW (0 << 7) +#define MOD_LR_RLOW (1 << 7) +#define MOD_SDF_IIS (0 << 5) +#define MOD_SDF_MSB (1 << 5) +#define MOD_SDF_LSB (2 << 5) +#define MOD_SDF_MASK (3 << 5) +#define MOD_RCLK_256FS (0 << 3) +#define MOD_RCLK_512FS (1 << 3) +#define MOD_RCLK_384FS (2 << 3) +#define MOD_RCLK_768FS (3 << 3) +#define MOD_RCLK_MASK (3 << 3) +#define MOD_BCLK_32FS (0 << 1) +#define MOD_BCLK_48FS (1 << 1) +#define MOD_BCLK_16FS (2 << 1) +#define MOD_BCLK_24FS (3 << 1) +#define MOD_BCLK_MASK (3 << 1) +#define MOD_8BIT (1 << 0) + +#define MOD_CDCLKCON (1 << 12) + +#define PSR_PSREN (1 << 15) + +#define FIC_TXFLUSH (1 << 15) +#define FIC_RXFLUSH (1 << 7) + +#define AHB_INTENLVL0 (1 << 24) +#define AHB_LVL0INT (1 << 20) +#define AHB_CLRLVL0INT (1 << 16) +#define AHB_DMARLD (1 << 5) +#define AHB_INTMASK (1 << 3) +#define AHB_DMAEN (1 << 0) +#define AHB_LVLINTMASK (0xf << 20) + +#define I2SSIZE_TRNMSK (0xffff) +#define I2SSIZE_SHIFT (16) + +#endif /* CPU_SAMSUNG_EXYNOS5420_I2S_REGS_H */ diff --git a/src/soc/samsung/exynos5420/mct.c b/src/soc/samsung/exynos5420/mct.c new file mode 100644 index 0000000000..bbb90e49bb --- /dev/null +++ b/src/soc/samsung/exynos5420/mct.c @@ -0,0 +1,36 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2012 Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <stdint.h> +#include <arch/io.h> +#include "clk.h" + +uint64_t mct_raw_value(void) +{ + uint64_t upper = readl(&exynos_mct->g_cnt_u); + uint64_t lower = readl(&exynos_mct->g_cnt_l); + + return (upper << 32) | lower; +} + +void mct_start(void) +{ + writel(readl(&exynos_mct->g_tcon) | (0x1 << 8), + &exynos_mct->g_tcon); +} diff --git a/src/soc/samsung/exynos5420/monotonic_timer.c b/src/soc/samsung/exynos5420/monotonic_timer.c new file mode 100644 index 0000000000..89ac416eb1 --- /dev/null +++ b/src/soc/samsung/exynos5420/monotonic_timer.c @@ -0,0 +1,34 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2013 Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <stdint.h> +#include <timer.h> + +#include "clk.h" + +static const uint32_t clocks_per_usec = MCT_HZ/1000000; + +void timer_monotonic_get(struct mono_time *mt) +{ + /* We don't have to call mct_start() here + * because it was already called in the bootblock + */ + + mono_time_set_usecs(mt, mct_raw_value() / clocks_per_usec); +} diff --git a/src/soc/samsung/exynos5420/periph.h b/src/soc/samsung/exynos5420/periph.h new file mode 100644 index 0000000000..94f150e6dd --- /dev/null +++ b/src/soc/samsung/exynos5420/periph.h @@ -0,0 +1,72 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2012 Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef CPU_SAMSUNG_EXYNOS5420_PERIPH_H +#define CPU_SAMSUNG_EXYNOS5420_PERIPH_H + +/* + * Peripherals requiring clock/pinmux configuration. List will + * grow with support for more devices getting added. + * + * At present the order is arbitrary - we may be able to take advantage + * of some orthogonality later. + */ +enum periph_id { + PERIPH_ID_UART0, + PERIPH_ID_UART1, + PERIPH_ID_UART2, + PERIPH_ID_UART3, + PERIPH_ID_SDMMC0, + PERIPH_ID_SDMMC1, + PERIPH_ID_SDMMC2, + PERIPH_ID_SDMMC3, + + PERIPH_ID_SROMC = 9, + PERIPH_ID_SPI0, + PERIPH_ID_SPI1, + PERIPH_ID_SPI2, + PERIPH_ID_SPI3, + PERIPH_ID_SPI4, + PERIPH_ID_LCD, + PERIPH_ID_BACKLIGHT, + PERIPH_ID_I2C0, + PERIPH_ID_I2C1, + PERIPH_ID_I2C2, + PERIPH_ID_I2C3, + PERIPH_ID_I2C4, + PERIPH_ID_I2C5, + PERIPH_ID_I2C6, + PERIPH_ID_I2C7, + PERIPH_ID_I2C8, + PERIPH_ID_I2C9, + PERIPH_ID_I2C10, + PERIPH_ID_DPHPD, /* eDP hot plug detect */ + PERIPH_ID_PWM0, + PERIPH_ID_PWM1, + PERIPH_ID_PWM2, + PERIPH_ID_PWM3, + PERIPH_ID_PWM4, + PERIPH_ID_I2S1, + PERIPH_ID_SATA, + + PERIPH_ID_COUNT, + PERIPH_ID_NONE = -1, +}; + +#endif diff --git a/src/soc/samsung/exynos5420/pinmux.c b/src/soc/samsung/exynos5420/pinmux.c new file mode 100644 index 0000000000..43a4332503 --- /dev/null +++ b/src/soc/samsung/exynos5420/pinmux.c @@ -0,0 +1,245 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2012 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <console/console.h> +#include <assert.h> +#include <stdlib.h> +#include "gpio.h" +#include "pinmux.h" + +static void exynos_pinmux_uart(int start, int count) +{ + int i; + + for (i = start; i < start + count; i++) { + gpio_set_pull(i, GPIO_PULL_NONE); + gpio_cfg_pin(i, GPIO_FUNC(0x2)); + } +} + +void exynos_pinmux_uart0(void) +{ + exynos_pinmux_uart(GPIO_A00, 4); +} + +void exynos_pinmux_uart1(void) +{ + exynos_pinmux_uart(GPIO_A04, 4); +} + +void exynos_pinmux_uart2(void) +{ + exynos_pinmux_uart(GPIO_A10, 4); +} + +void exynos_pinmux_uart3(void) +{ + exynos_pinmux_uart(GPIO_A14, 2); +} + +struct gpio { + enum exynos5_gpio_pin pin; + unsigned int func; + unsigned int pull; + unsigned int drv; +}; + +static void exynos_pinmux_sdmmc(struct gpio *gpios, int num_gpios) +{ + int i; + + for (i = 0; i < num_gpios; i++) { + gpio_set_drv(gpios[i].pin, gpios[i].drv); + gpio_set_pull(gpios[i].pin, gpios[i].pull); + gpio_cfg_pin(gpios[i].pin, GPIO_FUNC(gpios[i].func)); + } +} + +void exynos_pinmux_sdmmc0(void) +{ + struct gpio gpios[] = { + { GPIO_C00, 0x2, GPIO_PULL_NONE, GPIO_DRV_4X }, /* CLK */ + { GPIO_C01, 0x2, GPIO_PULL_NONE, GPIO_DRV_4X }, /* CMD */ + /* + * MMC0 is intended to be used for eMMC. The card detect + * pin is used as a VDDEN signal to power on the eMMC. The + * 5420 iROM makes this same assumption. + */ + { GPIO_C02, GPIO_OUTPUT, GPIO_PULL_NONE, GPIO_DRV_4X }, + { GPIO_C03, 0x2, GPIO_PULL_UP, GPIO_DRV_4X }, /* DATA[0] */ + { GPIO_C04, 0x2, GPIO_PULL_UP, GPIO_DRV_4X }, /* DATA[1] */ + { GPIO_C05, 0x2, GPIO_PULL_UP, GPIO_DRV_4X }, /* DATA[2] */ + { GPIO_C06, 0x2, GPIO_PULL_UP, GPIO_DRV_4X }, /* DATA[3] */ + + { GPIO_C30, 0x2, GPIO_PULL_UP, GPIO_DRV_4X }, /* DATA[4] */ + { GPIO_C31, 0x2, GPIO_PULL_UP, GPIO_DRV_4X }, /* DATA[5] */ + { GPIO_C32, 0x2, GPIO_PULL_UP, GPIO_DRV_4X }, /* DATA[6] */ + { GPIO_C33, 0x2, GPIO_PULL_UP, GPIO_DRV_4X }, /* DATA[7] */ + }; + + exynos_pinmux_sdmmc(&gpios[0], ARRAY_SIZE(gpios)); + + /* set VDDEN */ + gpio_set_value(GPIO_C02, 1); +} + +void exynos_pinmux_sdmmc1(void) +{ + struct gpio gpios[] = { + { GPIO_C10, 0x2, GPIO_PULL_NONE, GPIO_DRV_4X }, /* CLK */ + { GPIO_C11, 0x2, GPIO_PULL_NONE, GPIO_DRV_4X }, /* CMD */ + { GPIO_C12, 0x2, GPIO_PULL_NONE, GPIO_DRV_4X }, /* CDn */ + { GPIO_C13, 0x2, GPIO_PULL_NONE, GPIO_DRV_4X }, /* DATA[0] */ + { GPIO_C14, 0x2, GPIO_PULL_NONE, GPIO_DRV_4X }, /* DATA[1] */ + { GPIO_C15, 0x2, GPIO_PULL_NONE, GPIO_DRV_4X }, /* DATA[2] */ + { GPIO_C16, 0x2, GPIO_PULL_NONE, GPIO_DRV_4X }, /* DATA[3] */ + + { GPIO_D14, 0x2, GPIO_PULL_UP, GPIO_DRV_4X }, /* DATA[4] */ + { GPIO_D15, 0x2, GPIO_PULL_UP, GPIO_DRV_4X }, /* DATA[5] */ + { GPIO_D16, 0x2, GPIO_PULL_UP, GPIO_DRV_4X }, /* DATA[6] */ + { GPIO_D17, 0x2, GPIO_PULL_UP, GPIO_DRV_4X }, /* DATA[7] */ + }; + + exynos_pinmux_sdmmc(&gpios[0], ARRAY_SIZE(gpios)); +} + +void exynos_pinmux_sdmmc2(void) +{ + struct gpio gpios[] = { + { GPIO_C20, 0x2, GPIO_PULL_NONE, GPIO_DRV_4X }, /* CLK */ + { GPIO_C21, 0x2, GPIO_PULL_NONE, GPIO_DRV_4X }, /* CMD */ + { GPIO_C22, 0x2, GPIO_PULL_NONE, GPIO_DRV_4X }, /* CDn */ + { GPIO_C23, 0x2, GPIO_PULL_NONE, GPIO_DRV_4X }, /* DATA[0] */ + { GPIO_C24, 0x2, GPIO_PULL_NONE, GPIO_DRV_4X }, /* DATA[1] */ + { GPIO_C25, 0x2, GPIO_PULL_NONE, GPIO_DRV_4X }, /* DATA[2] */ + { GPIO_C26, 0x2, GPIO_PULL_NONE, GPIO_DRV_4X }, /* DATA[3] */ + }; + + exynos_pinmux_sdmmc(&gpios[0], ARRAY_SIZE(gpios)); +} + +static void exynos_pinmux_spi(int start, int cfg) +{ + int i; + + for (i = start; i < start + 4; i++) { + gpio_cfg_pin(i, cfg); + gpio_set_pull(i, GPIO_PULL_NONE); + gpio_set_drv(i, GPIO_DRV_3X); + } +} + +void exynos_pinmux_spi0(void) +{ + exynos_pinmux_spi(GPIO_A20, 0x2); +} + +void exynos_pinmux_spi1(void) +{ + exynos_pinmux_spi(GPIO_A24, 0x2); +} + +void exynos_pinmux_spi2(void) +{ + exynos_pinmux_spi(GPIO_B11, 0x5); +} + +void exynos_pinmux_spi3(void) +{ + exynos_pinmux_spi(GPIO_F10, 0x2); +} + +void exynos_pinmux_spi4(void) +{ + int i; + + for (i = 0; i < 2; i++) { + gpio_cfg_pin(GPIO_F02 + i, GPIO_FUNC(0x4)); + gpio_cfg_pin(GPIO_E04 + i, GPIO_FUNC(0x4)); + } +} + +static void exynos_pinmux_i2c(int start, int func) +{ + gpio_cfg_pin(start, GPIO_FUNC(func)); + gpio_cfg_pin(start + 1, GPIO_FUNC(func)); + gpio_set_pull(start, GPIO_PULL_NONE); + gpio_set_pull(start + 1, GPIO_PULL_NONE); +} + +void exynos_pinmux_i2c0(void) +{ + exynos_pinmux_i2c(GPIO_B30, 0x2); +} + +void exynos_pinmux_i2c1(void) +{ + exynos_pinmux_i2c(GPIO_B32, 0x2); +} + +void exynos_pinmux_i2c2(void) +{ + exynos_pinmux_i2c(GPIO_A06, 0x3); +} + +void exynos_pinmux_i2c3(void) +{ + exynos_pinmux_i2c(GPIO_A12, 0x3); +} + +void exynos_pinmux_i2c4(void) +{ + exynos_pinmux_i2c(GPIO_A20, 0x3); +} + +void exynos_pinmux_i2c5(void) +{ + exynos_pinmux_i2c(GPIO_A22, 0x3); +} + +void exynos_pinmux_i2c6(void) +{ + exynos_pinmux_i2c(GPIO_B13, 0x4); +} + +void exynos_pinmux_i2c7(void) +{ + exynos_pinmux_i2c(GPIO_B22, 0x3); +} + +void exynos_pinmux_i2c8(void) +{ + exynos_pinmux_i2c(GPIO_B34, 0x2); +} + +void exynos_pinmux_i2c9(void) +{ + exynos_pinmux_i2c(GPIO_B36, 0x2); +} + +void exynos_pinmux_i2c10(void) +{ + exynos_pinmux_i2c(GPIO_B40, 0x2); +} + +void exynos_pinmux_dphpd(void) +{ + gpio_cfg_pin(GPIO_X07, GPIO_FUNC(0x3)); + gpio_set_pull(GPIO_X07, GPIO_PULL_NONE); +} diff --git a/src/soc/samsung/exynos5420/pinmux.h b/src/soc/samsung/exynos5420/pinmux.h new file mode 100644 index 0000000000..fc09fc95fb --- /dev/null +++ b/src/soc/samsung/exynos5420/pinmux.h @@ -0,0 +1,53 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2012 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef CPU_SAMSUNG_EXYNOS5420_PINMUX_H +#define CPU_SAMSUNG_EXYNOS5420_PINMUX_H + +void exynos_pinmux_uart0(void); +void exynos_pinmux_uart1(void); +void exynos_pinmux_uart2(void); +void exynos_pinmux_uart3(void); + +void exynos_pinmux_sdmmc0(void); +void exynos_pinmux_sdmmc1(void); +void exynos_pinmux_sdmmc2(void); +void exynos_pinmux_sdmmc3(void); + +void exynos_pinmux_spi0(void); +void exynos_pinmux_spi1(void); +void exynos_pinmux_spi2(void); +void exynos_pinmux_spi3(void); +void exynos_pinmux_spi4(void); + +void exynos_pinmux_i2c0(void); +void exynos_pinmux_i2c1(void); +void exynos_pinmux_i2c2(void); +void exynos_pinmux_i2c3(void); +void exynos_pinmux_i2c4(void); +void exynos_pinmux_i2c5(void); +void exynos_pinmux_i2c6(void); +void exynos_pinmux_i2c7(void); +void exynos_pinmux_i2c8(void); +void exynos_pinmux_i2c9(void); +void exynos_pinmux_i2c10(void); + +void exynos_pinmux_dphpd(void); + +#endif diff --git a/src/soc/samsung/exynos5420/power.c b/src/soc/samsung/exynos5420/power.c new file mode 100644 index 0000000000..ecaf208c9c --- /dev/null +++ b/src/soc/samsung/exynos5420/power.c @@ -0,0 +1,92 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2012 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* Power setup code for EXYNOS5 */ + +#include <arch/io.h> +#include <arch/hlt.h> +#include <console/console.h> +#include "dmc.h" +#include "power.h" +#include "setup.h" + +void ps_hold_setup(void) +{ + /* Set PS-Hold high */ + setbits_le32(&exynos_power->ps_hold_ctrl, + POWER_PS_HOLD_CONTROL_DATA_HIGH); +} + +void power_reset(void) +{ + /* Clear inform1 so there's no change we think we've got a wake reset */ + exynos_power->inform1 = 0; + + setbits_le32(&exynos_power->sw_reset, 1); +} + +/* This function never returns */ +void power_shutdown(void) +{ + clrbits_le32(&exynos_power->ps_hold_ctrl, + POWER_PS_HOLD_CONTROL_DATA_HIGH); + + hlt(); +} + +void power_enable_dp_phy(void) +{ + setbits_le32(&exynos_power->dptx_phy_control, EXYNOS_DP_PHY_ENABLE); +} + +void power_enable_hw_thermal_trip(void) +{ + /* Enable HW thermal trip */ + setbits_le32(&exynos_power->ps_hold_ctrl, POWER_ENABLE_HW_TRIP); +} + +uint32_t power_read_reset_status(void) +{ + return exynos_power->inform1; +} + +void power_exit_wakeup(void) +{ + typedef void (*resume_func)(void); + + ((resume_func)exynos_power->inform0)(); +} + +int power_init(void) +{ + ps_hold_setup(); + return 0; +} + +void power_enable_xclkout(void) +{ + /* use xxti for xclk out */ + clrsetbits_le32(&exynos_power->pmu_debug, PMU_DEBUG_CLKOUT_SEL_MASK, + PMU_DEBUG_XXTI); +} + +void power_release_uart_retention(void) +{ + writel(1 << 28, &exynos_power->padret_uart_opt); +} diff --git a/src/soc/samsung/exynos5420/power.h b/src/soc/samsung/exynos5420/power.h new file mode 100644 index 0000000000..fd1eac5768 --- /dev/null +++ b/src/soc/samsung/exynos5420/power.h @@ -0,0 +1,112 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2012 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* Register map for Exynos5 PMU */ + +#ifndef CPU_SAMSUNG_EXYNOS5420_POWER_H +#define CPU_SAMSUNG_EXYNOS5420_POWER_H + +#include "cpu.h" + +/* Enable HW thermal trip with PS_HOLD_CONTROL register ENABLE_HW_TRIP bit */ +void power_enable_hw_thermal_trip(void); + +#define MIPI_PHY1_CONTROL_ENABLE (1 << 0) +#define MIPI_PHY1_CONTROL_M_RESETN (1 << 2) + +#define POWER_USB_PHY_CTRL_EN (1 << 0) +#define POWER_PS_HOLD_CONTROL_DATA_HIGH (1 << 8) +#define POWER_ENABLE_HW_TRIP (1UL << 31) + +#define EXYNOS_DP_PHY_ENABLE (1 << 0) + +/* PMU_DEBUG bits [12:8] = 0x1000 selects XXTI clock source */ +#define PMU_DEBUG_XXTI 0x1000 +/* Mask bit[12:8] for xxti clock selection */ +#define PMU_DEBUG_CLKOUT_SEL_MASK 0x1f00 + +/* Power Management Unit register map */ +struct exynos5_power { + /* Add registers as and when required */ + uint32_t om_stat; /* 0x0000 */ + uint8_t reserved1[0x03fc]; + uint32_t sw_reset; /* 0x0400 */ + uint8_t reserved2[0x0300]; + uint32_t usb_drd0_phy_ctrl; /* 0x0704 */ + uint32_t usb_drd1_phy_ctrl; /* 0x0708 */ + uint32_t usb_host_phy_ctrl; /* 0x070c */ + uint8_t reserved3[0x4]; + uint32_t mipi_phy1_control; /* 0x0714 */ + uint8_t reserved4[0x8]; + uint32_t dptx_phy_control; /* 0x0720 */ + uint8_t reserved5[0xdc]; + uint32_t inform0; /* 0x0800 */ + uint32_t inform1; /* 0x0804 */ + uint8_t reserved6[0x0f8]; + uint32_t spare0; /* 0x0900 */ + uint8_t reserved7[0x0fc]; + uint32_t pmu_debug; /* 0x0a00 */ + uint8_t reserved8[0x15fc]; + struct { /* 0x2000 */ + uint32_t config; /* 0x00 */ + uint32_t status; /* 0x04 */ + uint8_t reserved[0x78]; + } arm_core[4]; + uint8_t reserved9[0xe04]; + uint32_t padret_dram_status; /* 0x3004 */ + uint8_t reservedA[0xe0]; + uint32_t padret_uart_opt; /* 0x30e8 */ + uint8_t reservedB[0xfc]; + uint32_t padret_dram_cblk_opt; /* 0x31e8 */ + uint8_t reservedC[0x120]; + uint32_t ps_hold_ctrl; /* 0x330c */ +} __attribute__ ((__packed__)); + +static struct exynos5_power * const exynos_power = (void*)EXYNOS5_POWER_BASE; + +/** + * Perform a software reset. + */ +void power_reset(void); + +/** + * Power off the system; it should never return. + */ +void power_shutdown(void); + +/* Enable DPTX PHY */ +void power_enable_dp_phy(void); + +/* Initialize the pmic voltages to power up the system */ +int power_init(void); + +/* Read the reset status. */ +uint32_t power_read_reset_status(void); + +/* Read the resume function and call it. */ +void power_exit_wakeup(void); + +/* pmu debug is used for xclkout, enable xclkout with source as XXTI */ +void power_enable_xclkout(void); + +/* Release UART retention on resume (only for debugging, may conflict with + * kernel). */ +void power_release_uart_retention(void); + +#endif diff --git a/src/soc/samsung/exynos5420/setup.h b/src/soc/samsung/exynos5420/setup.h new file mode 100644 index 0000000000..63e40a8f0f --- /dev/null +++ b/src/soc/samsung/exynos5420/setup.h @@ -0,0 +1,888 @@ + /* + * This file is part of the coreboot project. + * + * Copyright (C) 2012 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* Machine Specific Values for SMDK5420 board based on Exynos5 */ + +#ifndef CPU_SAMSUNG_EXYNOS5420_SETUP_H +#define CPU_SAMSUNG_EXYNOS5420_SETUP_H + +struct exynos5_dmc; +enum ddr_mode; +struct exynos5_phy_control; + +#define NOT_AVAILABLE 0 +#define DATA_MASK 0xFFFFF + +#define ENABLE_BIT 0x1 +#define DISABLE_BIT 0x0 +#define CA_SWAP_EN (1 << 0) + +/* TZPC : Register Offsets */ +#define TZPC0_BASE 0x10100000 +#define TZPC1_BASE 0x10110000 +#define TZPC2_BASE 0x10120000 +#define TZPC3_BASE 0x10130000 +#define TZPC4_BASE 0x10140000 +#define TZPC5_BASE 0x10150000 +#define TZPC6_BASE 0x10160000 +#define TZPC7_BASE 0x10170000 +#define TZPC8_BASE 0x10180000 +#define TZPC9_BASE 0x10190000 + +#define APLL_FOUT (1 << 0) +#define KPLL_FOUT (1 << 0) + +#define CLK_DIV_CPERI1_VAL 0x3f3f0000 + +/* APLL_CON1 */ +#define APLL_CON1_VAL (0x0020f300) + +/* MPLL_CON1 */ +#define MPLL_CON1_VAL (0x0020f300) + +/* CPLL_CON1 */ +#define CPLL_CON1_VAL (0x0020f300) + +/* DPLL_CON1 */ +#define DPLL_CON1_VAL (0x0020f300) + +/* GPLL_CON1 */ +#define GPLL_CON1_VAL (NOT_AVAILABLE) + +/* EPLL_CON1, CON2 */ +#define EPLL_CON1_VAL 0x00000000 +#define EPLL_CON2_VAL 0x00000080 + +/* VPLL_CON1, CON2 */ +#define VPLL_CON1_VAL 0x0020f300 +#define VPLL_CON2_VAL NOT_AVAILABLE + +/* RPLL_CON1, CON2 */ +#define RPLL_CON1_VAL 0x00000000 +#define RPLL_CON2_VAL 0x00000080 + +/* BPLL_CON1 */ +#define BPLL_CON1_VAL 0x0020f300 + +/* SPLL_CON1 */ +#define SPLL_CON1_VAL 0x0020f300 + +/* IPLL_CON1 */ +#define IPLL_CON1_VAL 0x00000080 + +/* KPLL_CON1 */ +#define KPLL_CON1_VAL 0x200000 + +/* Set PLL */ +#define set_pll(mdiv, pdiv, sdiv) (1<<31 | mdiv<<16 | pdiv<<8 | sdiv) + +/* CLK_SRC_CPU */ +/* 0 = MOUTAPLL, 1 = SCLKMPLL */ +#define MUX_HPM_SEL 1 +#define MUX_CPU_SEL 0 +#define MUX_APLL_SEL 1 + +#define CLK_SRC_CPU_VAL ((MUX_HPM_SEL << 20) \ + | (MUX_CPU_SEL << 16) \ + | (MUX_APLL_SEL)) + +/* MEMCONTROL register bit fields */ +#define DMC_MEMCONTROL_CLK_STOP_DISABLE (0 << 0) +#define DMC_MEMCONTROL_DPWRDN_DISABLE (0 << 1) +#define DMC_MEMCONTROL_DPWRDN_ACTIVE_PRECHARGE (0 << 2) +#define DMC_MEMCONTROL_DSREF_DISABLE (0 << 5) +#define DMC_MEMCONTROL_DSREF_ENABLE (1 << 5) +#define DMC_MEMCONTROL_ADD_LAT_PALL_CYCLE(x) (x << 6) + +#define DMC_MEMCONTROL_MEM_TYPE_LPDDR3 (7 << 8) +#define DMC_MEMCONTROL_MEM_TYPE_DDR3 (6 << 8) +#define DMC_MEMCONTROL_MEM_TYPE_LPDDR2 (5 << 8) + +#define DMC_MEMCONTROL_MEM_WIDTH_32BIT (2 << 12) + +#define DMC_MEMCONTROL_NUM_CHIP_1 (0 << 16) +#define DMC_MEMCONTROL_NUM_CHIP_2 (1 << 16) + +#define DMC_MEMCONTROL_BL_8 (3 << 20) +#define DMC_MEMCONTROL_BL_4 (2 << 20) + +#define DMC_MEMCONTROL_PZQ_DISABLE (0 << 24) + +#define DMC_MEMCONTROL_MRR_BYTE_7_0 (0 << 25) +#define DMC_MEMCONTROL_MRR_BYTE_15_8 (1 << 25) +#define DMC_MEMCONTROL_MRR_BYTE_23_16 (2 << 25) +#define DMC_MEMCONTROL_MRR_BYTE_31_24 (3 << 25) + +/* MEMCONFIG0 register bit fields */ +#define DMC_MEMCONFIGx_CHIP_MAP_INTERLEAVED (1 << 12) +#define DMC_MEMCONFIG_CHIP_MAP_SPLIT (2 << 12) +#define DMC_MEMCONFIGx_CHIP_COL_10 (3 << 8) +#define DMC_MEMCONFIGx_CHIP_ROW_14 (2 << 4) +#define DMC_MEMCONFIGx_CHIP_ROW_15 (3 << 4) +#define DMC_MEMCONFIGx_CHIP_ROW_16 (4 << 4) +#define DMC_MEMCONFIGx_CHIP_BANK_8 (3 << 0) + +#define DMC_MEMBASECONFIG0_VAL DMC_MEMBASECONFIG_VAL(0x40) +#define DMC_MEMBASECONFIG1_VAL DMC_MEMBASECONFIG_VAL(0x80) + +#define DMC_PRECHCONFIG_VAL 0xFF000000 +#define DMC_PWRDNCONFIG_VAL 0xFFFF00FF + +#define DMC_CONCONTROL_RESET_VAL 0x0FFF0000 +#define DFI_INIT_START (1 << 28) +#define EMPTY (1 << 8) +#define AREF_EN (1 << 5) + +#define DFI_INIT_COMPLETE_CHO (1 << 2) +#define DFI_INIT_COMPLETE_CH1 (1 << 3) + +#define RDLVL_COMPLETE_CHO (1 << 14) +#define RDLVL_COMPLETE_CH1 (1 << 15) + +#define CLK_STOP_EN (1 << 0) +#define DPWRDN_EN (1 << 1) +#define DSREF_EN (1 << 5) + +/* COJCONTROL register bit fields */ +#define DMC_CONCONTROL_IO_PD_CON_DISABLE (0 << 3) +#define DMC_CONCONTROL_AREF_EN_DISABLE (0 << 5) +#define DMC_CONCONTROL_RD_FETCH_DISABLE (0x0 << 12) +#define DMC_CONCONTROL_TIMEOUT_LEVEL0 (0xFFF << 16) +#define DMC_CONCONTROL_DFI_INIT_START_DISABLE (0 << 28) + +/* CLK_FSYS */ +#define CLK_SRC_FSYS0_VAL 0x33033300 +#define CLK_DIV_FSYS0_VAL 0x0 +#define CLK_DIV_FSYS1_VAL 0x04f13c4f +#define CLK_DIV_FSYS2_VAL 0x041d0000 + +#define DMC_CONCONTROL_IO_PD_CON(x) (x << 6) + +/* CLK_DIV_CPU1 */ +#define HPM_RATIO 0x2 +#define COPY_RATIO 0x0 + +/* CLK_DIV_CPU1 = 0x00000003 */ +#define CLK_DIV_CPU1_VAL ((HPM_RATIO << 4) \ + | (COPY_RATIO)) + +/* CLK_SRC_CORE0 */ +#define CLK_SRC_CORE0_VAL 0x00000000 + +/* CLK_SRC_CORE1 */ +#define CLK_SRC_CORE1_VAL 0x100 + +/* CLK_DIV_CORE0 */ +#define CLK_DIV_CORE0_VAL 0x00120000 + +/* CLK_DIV_CORE1 */ +#define CLK_DIV_CORE1_VAL 0x07070700 + +/* CLK_DIV_SYSRGT */ +#define CLK_DIV_SYSRGT_VAL 0x00000111 + +/* CLK_DIV_ACP */ +#define CLK_DIV_ACP_VAL 0x12 + +/* CLK_DIV_SYSLFT */ +#define CLK_DIV_SYSLFT_VAL 0x00000311 + +/* CLK_SRC_CDREX */ +#define CLK_SRC_CDREX_VAL 0x00000001 +#define MUX_MCLK_CDR_MSPLL (1 << 4) +#define MUX_BPLL_SEL_FOUTBPLL (1 << 0) +#define BPLL_SEL_MASK 0x7 +#define FOUTBPLL 2 + +/* CLK_DIV_CDREX */ +#define CLK_DIV_CDREX0_VAL 0x30010100 +#define CLK_DIV_CDREX1_VAL 0x300 + +#define CLK_DIV_CDREX_VAL 0x17010100 + +/* CLK_DIV_CPU0_VAL */ +#define CLK_DIV_CPU0_VAL 0x01440020 + +/* CLK_SRC_TOP */ +#define CLK_SRC_TOP0_VAL 0x11101102 +#define CLK_SRC_TOP1_VAL 0x00200000 +#define CLK_SRC_TOP2_VAL 0x11101010 +#define CLK_SRC_TOP3_VAL 0x11111111 +#define CLK_SRC_TOP4_VAL 0x11110111 +#define CLK_SRC_TOP5_VAL 0x11111111 +#define CLK_SRC_TOP6_VAL 0x11110111 +#define CLK_SRC_TOP7_VAL 0x00022200 + +/* CLK_DIV_TOP */ +#define CLK_DIV_TOP0_VAL 0x22512211 +#define CLK_DIV_TOP1_VAL 0x13200900 +#define CLK_DIV_TOP2_VAL 0x11101110 + +/* APLL_LOCK */ +#define APLL_LOCK_VAL (0x320) +/* MPLL_LOCK */ +#define MPLL_LOCK_VAL (0x258) +/* BPLL_LOCK */ +#define BPLL_LOCK_VAL (0x258) +/* CPLL_LOCK */ +#define CPLL_LOCK_VAL (0x190) +/* DPLL_LOCK */ +#define DPLL_LOCK_VAL (0x190) +/* GPLL_LOCK */ +#define GPLL_LOCK_VAL NOT_AVAILABLE +/* IPLL_LOCK */ +#define IPLL_LOCK_VAL (0x320) +/* KPLL_LOCK */ +#define KPLL_LOCK_VAL (0x258) +/* SPLL_LOCK */ +#define SPLL_LOCK_VAL (0x320) +/* RPLL_LOCK */ +#define RPLL_LOCK_VAL (0x2328) +/* EPLL_LOCK */ +#define EPLL_LOCK_VAL (0x2328) +/* VPLL_LOCK */ +#define VPLL_LOCK_VAL (0x258) + +#define MUX_APLL_SEL_MASK (1 << 0) +#define MUX_MPLL_SEL_MASK (1 << 8) +#define MPLL_SEL_MOUT_MPLLFOUT (2 << 8) +#define MUX_CPLL_SEL_MASK (1 << 8) +#define MUX_EPLL_SEL_MASK (1 << 12) +#define MUX_VPLL_SEL_MASK (1 << 16) +#define MUX_GPLL_SEL_MASK (1 << 28) +#define MUX_BPLL_SEL_MASK (1 << 0) +#define MUX_HPM_SEL_MASK (1 << 20) +#define HPM_SEL_SCLK_MPLL (1 << 21) +#define PLL_LOCKED (1 << 29) +#define APLL_CON0_LOCKED (1 << 29) +#define MPLL_CON0_LOCKED (1 << 29) +#define BPLL_CON0_LOCKED (1 << 29) +#define CPLL_CON0_LOCKED (1 << 29) +#define EPLL_CON0_LOCKED (1 << 29) +#define GPLL_CON0_LOCKED (1 << 29) +#define VPLL_CON0_LOCKED (1 << 29) +#define CLK_REG_DISABLE 0x0 +#define TOP2_VAL 0x0110000 + +/* CLK_SRC_LEX */ +#define CLK_SRC_LEX_VAL 0x0 + +/* CLK_DIV_LEX */ +#define CLK_DIV_LEX_VAL 0x10 + +/* CLK_DIV_R0X */ +#define CLK_DIV_R0X_VAL 0x10 + +/* CLK_DIV_L0X */ +#define CLK_DIV_R1X_VAL 0x10 + +/* CLK_DIV_ISP2 */ +#define CLK_DIV_ISP2_VAL 0x1 + +/* CLK_SRC_KFC */ +#define SRC_KFC_HPM_SEL (1 << 15) + +/* CLK_SRC_KFC */ +#define CLK_SRC_KFC_VAL 0x00008001 + +/* CLK_DIV_KFC */ +#define CLK_DIV_KFC_VAL 0x03300110 + +/* CLK_DIV2_RATIO */ +#define CLK_DIV2_RATIO 0x10111150 + +/* CLK_DIV4_RATIO */ +#define CLK_DIV4_RATIO 0x00000003 + +/* CLK_DIV_G2D */ +#define CLK_DIV_G2D 0x00000010 + +/* CLK_SRC_PERIC0 */ +#define SPDIF_SEL 1 +#define PWM_SEL 3 +#define UART4_SEL 3 +#define UART3_SEL 3 +#define UART2_SEL 3 +#define UART1_SEL 3 +#define UART0_SEL 3 +/* SRC_CLOCK = SCLK_RPLL */ +#define CLK_SRC_PERIC0_VAL ((SPDIF_SEL << 28) \ + | (PWM_SEL << 24) \ + | (UART4_SEL << 20) \ + | (UART3_SEL << 16) \ + | (UART2_SEL << 12) \ + | (UART1_SEL << 8) \ + | (UART0_SEL << 4)) + +/* CLK_SRC_PERIC1 */ +/* SRC_CLOCK = SCLK_MPLL */ +#define SPI0_SEL 3 +#define SPI1_SEL 3 +#define SPI2_SEL 3 +/* SRC_CLOCK = SCLK_EPLL */ +#define AUDIO0_SEL 6 +#define AUDIO1_SEL 6 +#define AUDIO2_SEL 6 +#define CLK_SRC_PERIC1_VAL ((SPI2_SEL << 28) \ + | (SPI1_SEL << 24) \ + | (SPI0_SEL << 20) \ + | (AUDIO2_SEL << 16) \ + | (AUDIO2_SEL << 12) \ + | (AUDIO2_SEL << 8)) + +/* CLK_SRC_ISP */ +#define CLK_SRC_ISP_VAL 0x33366000 +#define CLK_DIV_ISP0_VAL 0x13131300 +#define CLK_DIV_ISP1_VAL 0xbb110202 + +/* SCLK_DIV_ISP - set SPI0/1 to 0xf = divide by 16 */ +#define SPI0_ISP_RATIO 0xf +#define SPI1_ISP_RATIO 0xf +#define SCLK_DIV_ISP_VAL (SPI1_ISP_RATIO << 12) \ + | (SPI0_ISP_RATIO << 0) + +/* CLK_DIV_PERIL0 */ +#define PWM_RATIO 8 +#define UART4_RATIO 9 +#define UART3_RATIO 9 +#define UART2_RATIO 9 +#define UART1_RATIO 9 +#define UART0_RATIO 9 + +#define CLK_DIV_PERIC0_VAL ((PWM_RATIO << 28) \ + | (UART4_RATIO << 24) \ + | (UART3_RATIO << 20) \ + | (UART2_RATIO << 16) \ + | (UART1_RATIO << 12) \ + | (UART0_RATIO << 8)) + +/* CLK_DIV_PERIC1 */ +#define SPI2_RATIO 0x1 +#define SPI1_RATIO 0x1 +#define SPI0_RATIO 0x1 +#define CLK_DIV_PERIC1_VAL ((SPI2_RATIO << 28) \ + | (SPI1_RATIO << 24) \ + | (SPI0_RATIO << 20)) + +/* CLK_DIV_PERIC2 */ +#define PCM2_RATIO 0x3 +#define PCM1_RATIO 0x3 +#define CLK_DIV_PERIC2_VAL ((PCM2_RATIO << 24) \ + | (PCM1_RATIO << 16)) + +/* CLK_DIV_PERIC3 */ +#define AUDIO2_RATIO 0x5 +#define AUDIO1_RATIO 0x5 +#define AUDIO0_RATIO 0x5 +#define CLK_DIV_PERIC3_VAL ((AUDIO2_RATIO << 28) \ + | (AUDIO1_RATIO << 24) \ + | (AUDIO0_RATIO << 20)) + +/* CLK_DIV_PERIC4 */ +#define SPI2_PRE_RATIO 0x3 +#define SPI1_PRE_RATIO 0x3 +#define SPI0_PRE_RATIO 0x3 +#define CLK_DIV_PERIC4_VAL ((SPI2_PRE_RATIO << 24) \ + | (SPI1_PRE_RATIO << 16) \ + | (SPI0_PRE_RATIO << 8)) + +/* CLK_DIV_FSYS2 */ +#define MMC2_RATIO_MASK 0xf +#define MMC2_RATIO_VAL 0x3 +#define MMC2_RATIO_OFFSET 0 + +#define MMC2_PRE_RATIO_MASK 0xff +#define MMC2_PRE_RATIO_VAL 0x9 +#define MMC2_PRE_RATIO_OFFSET 8 + +#define MMC3_RATIO_MASK 0xf +#define MMC3_RATIO_VAL 0x1 +#define MMC3_RATIO_OFFSET 16 + +#define MMC3_PRE_RATIO_MASK 0xff +#define MMC3_PRE_RATIO_VAL 0x0 +#define MMC3_PRE_RATIO_OFFSET 24 + +/* CLK_SRC_LEX */ +#define CLK_SRC_LEX_VAL 0x0 + +/* CLK_DIV_LEX */ +#define CLK_DIV_LEX_VAL 0x10 + +/* CLK_DIV_R0X */ +#define CLK_DIV_R0X_VAL 0x10 + +/* CLK_DIV_L0X */ +#define CLK_DIV_R1X_VAL 0x10 + +/* CLK_DIV_ISP2 */ +#define CLK_DIV_ISP2_VAL 0x1 + +/* CLK_SRC_DISP1_0 */ +#define CLK_SRC_DISP1_0_VAL 0x10006000 +#define CLK_DIV_DISP1_0_VAL 0x01050210 + +/* + * DIV_DISP1_0 + * For DP, divisor should be 2 + */ +#define CLK_DIV_DISP1_0_FIMD1 (2 << 0) + +/* CLK_GATE_IP_DISP1 */ +#define CLK_GATE_DP1_ALLOW (1 << 4) + +/* CLK_GATE_IP_SYSRGT */ +#define CLK_C2C_MASK (1 << 1) + +/* CLK_GATE_IP_ACP */ +#define CLK_SMMUG2D_MASK (1 << 7) +#define CLK_SMMUSSS_MASK (1 << 6) +#define CLK_SMMUMDMA_MASK (1 << 5) +#define CLK_ID_REMAPPER_MASK (1 << 4) +#define CLK_G2D_MASK (1 << 3) +#define CLK_SSS_MASK (1 << 2) +#define CLK_MDMA_MASK (1 << 1) +#define CLK_SECJTAG_MASK (1 << 0) + +/* CLK_GATE_BUS_SYSLFT */ +#define CLK_EFCLK_MASK (1 << 16) + +/* CLK_GATE_IP_ISP0 */ +#define CLK_UART_ISP_MASK (1 << 31) +#define CLK_WDT_ISP_MASK (1 << 30) +#define CLK_PWM_ISP_MASK (1 << 28) +#define CLK_MTCADC_ISP_MASK (1 << 27) +#define CLK_I2C1_ISP_MASK (1 << 26) +#define CLK_I2C0_ISP_MASK (1 << 25) +#define CLK_MPWM_ISP_MASK (1 << 24) +#define CLK_MCUCTL_ISP_MASK (1 << 23) +#define CLK_INT_COMB_ISP_MASK (1 << 22) +#define CLK_SMMU_MCUISP_MASK (1 << 13) +#define CLK_SMMU_SCALERP_MASK (1 << 12) +#define CLK_SMMU_SCALERC_MASK (1 << 11) +#define CLK_SMMU_FD_MASK (1 << 10) +#define CLK_SMMU_DRC_MASK (1 << 9) +#define CLK_SMMU_ISP_MASK (1 << 8) +#define CLK_GICISP_MASK (1 << 7) +#define CLK_ARM9S_MASK (1 << 6) +#define CLK_MCUISP_MASK (1 << 5) +#define CLK_SCALERP_MASK (1 << 4) +#define CLK_SCALERC_MASK (1 << 3) +#define CLK_FD_MASK (1 << 2) +#define CLK_DRC_MASK (1 << 1) +#define CLK_ISP_MASK (1 << 0) + +/* CLK_GATE_IP_ISP1 */ +#define CLK_SPI1_ISP_MASK (1 << 13) +#define CLK_SPI0_ISP_MASK (1 << 12) +#define CLK_SMMU3DNR_MASK (1 << 7) +#define CLK_SMMUDIS1_MASK (1 << 6) +#define CLK_SMMUDIS0_MASK (1 << 5) +#define CLK_SMMUODC_MASK (1 << 4) +#define CLK_3DNR_MASK (1 << 2) +#define CLK_DIS_MASK (1 << 1) +#define CLK_ODC_MASK (1 << 0) + +/* CLK_GATE_IP_GSCL */ +#define CLK_SMMUFIMC_LITE2_MASK (1 << 20) +#define CLK_SMMUFIMC_LITE1_MASK (1 << 12) +#define CLK_SMMUFIMC_LITE0_MASK (1 << 11) +#define CLK_SMMUGSCL3_MASK (1 << 10) +#define CLK_SMMUGSCL2_MASK (1 << 9) +#define CLK_SMMUGSCL1_MASK (1 << 8) +#define CLK_SMMUGSCL0_MASK (1 << 7) +#define CLK_GSCL_WRAP_B_MASK (1 << 6) +#define CLK_GSCL_WRAP_A_MASK (1 << 5) +#define CLK_CAMIF_TOP_MASK (1 << 4) +#define CLK_GSCL3_MASK (1 << 3) +#define CLK_GSCL2_MASK (1 << 2) +#define CLK_GSCL1_MASK (1 << 1) +#define CLK_GSCL0_MASK (1 << 0) + +/* CLK_GATE_IP_MFC */ +#define CLK_SMMUMFCR_MASK (1 << 2) +#define CLK_SMMUMFCL_MASK (1 << 1) +#define CLK_MFC_MASK (1 << 0) + +#define SCLK_MPWM_ISP_MASK (1 << 0) + +/* CLK_GATE_IP_DISP1 */ +#define CLK_SMMUTVX_MASK (1 << 9) +#define CLK_ASYNCTVX_MASK (1 << 7) +#define CLK_HDMI_MASK (1 << 6) +#define CLK_MIXER_MASK (1 << 5) +#define CLK_DSIM1_MASK (1 << 3) + +/* AUDIO CLK SEL */ +#define AUDIO0_SEL_EPLL (0x6 << 28) +#define AUDIO0_RATIO 0x5 +#define PCM0_RATIO 0x3 +#define DIV_MAU_VAL (PCM0_RATIO << 24 | AUDIO0_RATIO << 20) + +/* CLK_GATE_IP_GEN */ +#define CLK_SMMUMDMA1_MASK (1 << 9) +#define CLK_SMMUJPEG_MASK (1 << 7) +#define CLK_SMMUROTATOR_MASK (1 << 6) +#define CLK_MDMA1_MASK (1 << 4) +#define CLK_JPEG_MASK (1 << 2) +#define CLK_ROTATOR_MASK (1 << 1) + +/* CLK_GATE_IP_FSYS */ +#define CLK_WDT_IOP_MASK (1 << 30) +#define CLK_SMMUMCU_IOP_MASK (1 << 26) +#define CLK_SATA_PHY_I2C_MASK (1 << 25) +#define CLK_SATA_PHY_CTRL_MASK (1 << 24) +#define CLK_MCUCTL_MASK (1 << 23) +#define CLK_NFCON_MASK (1 << 22) +#define CLK_SMMURTIC_MASK (1 << 11) +#define CLK_RTIC_MASK (1 << 9) +#define CLK_MIPI_HSI_MASK (1 << 8) +#define CLK_USBOTG_MASK (1 << 7) +#define CLK_SATA_MASK (1 << 6) +#define CLK_PDMA1_MASK (1 << 2) +#define CLK_PDMA0_MASK (1 << 1) +#define CLK_MCU_IOP_MASK (1 << 0) + +/* CLK_GATE_IP_PERIC */ +#define CLK_HS_I2C3_MASK (1 << 31) +#define CLK_HS_I2C2_MASK (1 << 30) +#define CLK_HS_I2C1_MASK (1 << 29) +#define CLK_HS_I2C0_MASK (1 << 28) +#define CLK_AC97_MASK (1 << 27) +#define CLK_SPDIF_MASK (1 << 26) +#define CLK_PCM2_MASK (1 << 23) +#define CLK_PCM1_MASK (1 << 22) +#define CLK_I2S2_MASK (1 << 21) +#define CLK_I2S1_MASK (1 << 20) +#define CLK_SPI2_MASK (1 << 18) +#define CLK_SPI0_MASK (1 << 16) +#define CLK_I2CHDMI_MASK (1 << 14) +#define CLK_I2C7_MASK (1 << 13) +#define CLK_I2C6_MASK (1 << 12) +#define CLK_I2C5_MASK (1 << 11) +#define CLK_I2C4_MASK (1 << 10) +#define CLK_I2C3_MASK (1 << 9) +#define CLK_I2C2_MASK (1 << 8) +#define CLK_I2C1_MASK (1 << 7) +#define CLK_I2C0_MASK (1 << 6) + +/* CLK_GATE_IP_PERIS */ +#define CLK_RTC_MASK (1 << 20) +#define CLK_TZPC9_MASK (1 << 15) +#define CLK_TZPC8_MASK (1 << 14) +#define CLK_TZPC7_MASK (1 << 13) +#define CLK_TZPC6_MASK (1 << 12) +#define CLK_TZPC5_MASK (1 << 11) +#define CLK_TZPC4_MASK (1 << 10) +#define CLK_TZPC3_MASK (1 << 9) +#define CLK_TZPC2_MASK (1 << 8) +#define CLK_TZPC1_MASK (1 << 7) +#define CLK_TZPC0_MASK (1 << 6) +#define CLK_CHIPID_MASK (1 << 0) + +/* CLK_GATE_BLOCK */ +#define CLK_ACP_MASK (1 << 7) + +/* CLK_GATE_IP_CDREX */ +#define CLK_TZASC_DRBXW_MASK (1 << 23) +#define CLK_TZASC_DRBXR_MASK (1 << 22) +#define CLK_TZASC_XLBXW_MASK (1 << 21) +#define CLK_TZASC_XLBXR_MASK (1 << 20) +#define CLK_TZASC_XR1BXW_MASK (1 << 19) +#define CLK_TZASC_XR1BXR_MASK (1 << 18) +#define CLK_DPHY1_MASK (1 << 5) +#define CLK_DPHY0_MASK (1 << 4) + +/* + * TZPC Register Value : + * R0SIZE: 0x0 : Size of secured ram + */ +#define R0SIZE 0x0 + +/* + * TZPC Decode Protection Register Value : + * DECPROTXSET: 0xFF : Set Decode region to non-secure + */ +#define DECPROTXSET 0xFF + +#define LPDDR3PHY_CTRL_PHY_RESET (1 << 0) +#define LPDDR3PHY_CTRL_PHY_RESET_OFF (0 << 0) + +/* FIXME(dhendrix): misleading name. The reset value is 0x17021a40, bits 12:11 ++ default to 0x3 which indicates LPDDR3. We want DDR3, so we use 0x1. */ +#define PHY_CON0_RESET_VAL 0x17020a40 +#define P0_CMD_EN (1 << 14) +#define BYTE_RDLVL_EN (1 << 13) +#define CTRL_SHGATE (1 << 8) + +#define PHY_CON1_RESET_VAL 0x09210100 +#define RDLVL_PASS_ADJ_VAL 0x6 +#define RDLVL_PASS_ADJ_OFFSET 16 +#define CTRL_GATEDURADJ_MASK (0xf << 20) +#define READ_LEVELLING_DDR3 0x0100 + +#define PHY_CON2_RESET_VAL 0x00010004 +#define INIT_DESKEW_EN (1 << 6) +#define DLL_DESKEW_EN (1 << 12) +#define RDLVL_GATE_EN (1 << 24) +#define RDLVL_EN (1 << 25) +#define RDLVL_INCR_ADJ (0x1 << 16) + +/* DREX_PAUSE */ +#define DREX_PAUSE_EN (1 << 0) + +#define BYPASS_EN (1 << 22) + +/********-----MEMMORY VAL----------***/ +#define PHY_CON0_VAL 0x17021A00 + +#define PHY_CON12_RESET_VAL 0x10100070 +#define PHY_CON12_VAL 0x10107F50 +#define CTRL_START (1 << 6) +#define CTRL_DLL_ON (1 << 5) +#define CTRL_FORCE_MASK (0x7F << 8) +#define CTRL_LOCK_COARSE_MASK (0x7F << 10) + + +#define CTRL_OFFSETD_RESET_VAL 0x8 +#define CTRL_OFFSETD_VAL 0x7F + +#define CTRL_OFFSETR0 0x7F +#define CTRL_OFFSETR1 0x7F +#define CTRL_OFFSETR2 0x7F +#define CTRL_OFFSETR3 0x7F +#define PHY_CON4_VAL (CTRL_OFFSETR0 << 0 | \ + CTRL_OFFSETR1 << 8 | \ + CTRL_OFFSETR2 << 16 | \ + CTRL_OFFSETR3 << 24) +#define PHY_CON4_RESET_VAL 0x08080808 + +#define CTRL_OFFSETW0 0x7F +#define CTRL_OFFSETW1 0x7F +#define CTRL_OFFSETW2 0x7F +#define CTRL_OFFSETW3 0x7F +#define PHY_CON6_VAL (CTRL_OFFSETW0 << 0 | \ + CTRL_OFFSETW1 << 8 | \ + CTRL_OFFSETW2 << 16 | \ + CTRL_OFFSETW3 << 24) +#define PHY_CON6_RESET_VAL 0x08080808 + +#define PHY_CON14_RESET_VAL 0x001F0000 +#define CTRL_PULLD_DQS 0xF +#define CTRL_PULLD_DQS_OFFSET 0 + +/*ZQ Configurations */ +#define PHY_CON16_RESET_VAL 0x08000304 + +#define ZQ_CLK_EN (1 << 27) +#define ZQ_CLK_DIV_EN (1 << 18) +#define ZQ_MANUAL_MODE_OFFSET 2 +#define ZQ_LONG_CALIBRATION 0x1 +#define ZQ_MANUAL_STR (1 << 1) +#define ZQ_DONE (1 << 0) +#define ZQ_MODE_DDS_OFFSET 24 + +#define LONG_CALIBRATION (ZQ_LONG_CALIBRATION << ZQ_MANUAL_MODE_OFFSET) + +#define CTRL_RDLVL_GATE_ENABLE 1 +#define CTRL_RDLVL_GATE_DISABLE 0 + +#define CTRL_RDLVL_DATA_ENABLE (1 << 1) +/* Direct Command */ +#define DIRECT_CMD_NOP 0x07000000 +#define DIRECT_CMD_PALL 0x01000000 +#define DIRECT_CMD_ZQINIT 0x0a000000 +#define DIRECT_CMD_CHANNEL_SHIFT 28 +#define DIRECT_CMD_CHIP_SHIFT 20 +#define DIRECT_CMD_BANK_SHIFT 16 +#define DIRECT_CMD_REFA (5 << 24) +#define DIRECT_CMD_MRS1 0x71C00 +#define DIRECT_CMD_MRS2 0x10BFC +#define DIRECT_CMD_MRS3 0x0050C +#define DIRECT_CMD_MRS4 0x00868 +#define DIRECT_CMD_MRS5 0x00C04 + +/* Drive Strength */ +#define IMPEDANCE_48_OHM 4 +#define IMPEDANCE_40_OHM 5 +#define IMPEDANCE_34_OHM 6 +#define IMPEDANCE_30_OHM 7 +#define PHY_CON39_VAL_48_OHM 0x09240924 +#define PHY_CON39_VAL_40_OHM 0x0B6D0B6D +#define PHY_CON39_VAL_34_OHM 0x0DB60DB6 +#define PHY_CON39_VAL_30_OHM 0x0FFF0FFF + + +#define CTRL_BSTLEN_OFFSET 8 +#define CTRL_RDLAT_OFFSET 0 + +#define CMD_DEFAULT_LPDDR3 0xF +#define CMD_DEFUALT_OFFSET 0 +#define T_WRDATA_EN 0x7 +#define T_WRDATA_EN_DDR3 0x8 /* FIXME(dhendrix): 6 for DDR3? see T_wrdata_en */ +#define T_WRDATA_EN_OFFSET 16 +#define T_WRDATA_EN_MASK 0x1f + +#define PHY_CON31_VAL 0x0C183060 +#define PHY_CON32_VAL 0x60C18306 +#define PHY_CON33_VAL 0x00000030 + +#define PHY_CON31_RESET_VAL 0x0 +#define PHY_CON32_RESET_VAL 0x0 +#define PHY_CON33_RESET_VAL 0x0 + +#define SL_DLL_DYN_CON_EN (1 << 1) +#define FP_RESYNC (1 << 3) +#define CTRL_START (1 << 6) + +#define DMC_AREF_EN (1 << 5) +#define DMC_CONCONTROL_EMPTY (1 << 8) +#define DFI_INIT_START (1 << 28) + +#define DMC_MEMCONTROL_VAL 0x00312700 +#define CLK_STOP_EN (1 << 0) +#define DPWRDN_EN (1 << 1) +#define DSREF_EN (1 << 5) + +/* AXI base address mask */ +#define DMC_CHIP_MASK_256MB 0x7f0 +#define DMC_CHIP_MASK_512MB 0x7e0 +#define DMC_CHIP_MASK_1GB 0x7c0 +#define DMC_CHIP_MASK_2GB 0x780 +#define DMC_CHIP_MASK_4GB 0x700 + +#define MEMCONFIG_VAL 0x1323 +#define PRECHCONFIG_DEFAULT_VAL 0xFF000000 +#define PWRDNCONFIG_DEFAULT_VAL 0xFFFF00FF + +#define DFI_INIT_COMPLETE (1 << 3) + +#define BRBRSVCONTROL_VAL 0x00000033 +#define BRBRSVCONFIG_VAL 0x88778877 + +/* Clock Gating Control (CGCONTROL) register */ +#define MEMIF_CG_EN (1 << 3) /* Memory interface clock gating */ +#define SCG_CG_EN (1 << 2) /* Scheduler clock gating */ +#define BUSIF_WR_CG_EN (1 << 1) /* Bus interface write channel clock gating */ +#define BUSIF_RD_CG_EN (1 << 0) /* Bus interface read channel clock gating */ +#define DMC_INTERNAL_CG (MEMIF_CG_EN | SCG_CG_EN | \ + BUSIF_WR_CG_EN | BUSIF_RD_CG_EN) + +/* DMC PHY Control0 register */ +#define PHY_CONTROL0_RESET_VAL 0x0 +#define MEM_TERM_EN (1 << 31) /* Termination enable for memory */ +#define PHY_TERM_EN (1 << 30) /* Termination enable for PHY */ +#define DMC_CTRL_SHGATE (1 << 29) /* Duration of DQS gating signal */ +#define CTRL_ATGATE (1 << 6) +#define FP_RSYNC (1 << 3) /* Force DLL resyncronization */ + +/* Driver strength for CK, CKE, CS & CA */ +#define IMP_OUTPUT_DRV_40_OHM 0x5 +#define IMP_OUTPUT_DRV_30_OHM 0x7 +#define DA_3_DS_OFFSET 25 +#define DA_2_DS_OFFSET 22 +#define DA_1_DS_OFFSET 19 +#define DA_0_DS_OFFSET 16 +#define CA_CK_DRVR_DS_OFFSET 9 +#define CA_CKE_DRVR_DS_OFFSET 6 +#define CA_CS_DRVR_DS_OFFSET 3 +#define CA_ADR_DRVR_DS_OFFSET 0 + +#define PHY_CON42_CTRL_BSTLEN_SHIFT 8 +#define PHY_CON42_CTRL_RDLAT_SHIFT 0 + +struct mem_timings; + +/* Errors that we can encourter in low-level setup */ +enum { + SETUP_ERR_OK, + SETUP_ERR_RDLV_COMPLETE_TIMEOUT = -1, + SETUP_ERR_ZQ_CALIBRATION_FAILURE = -2, +}; + +/* Functions common between LPDDR2 and DDR3 */ + +/* CPU info initialization code */ +void cpu_info_init(void); + +void mem_ctrl_init(void); +/* + * Memory variant specific initialization code + * + * @param mem Memory timings for this memory type. + * @param mem_iv_size Memory interleaving size is a configurable parameter + * which the DMC uses to decide how to split a memory + * chunk into smaller chunks to support concurrent + * accesses; may vary across boards. + * @param mem_reset Reset memory during initialization. + * @return 0 if ok, SETUP_ERR_... if there is a problem + */ +int ddr3_mem_ctrl_init(struct mem_timings *mem, int interleave_size, int reset); + +/* Memory variant specific initialization code for LPDDR3 */ +int lpddr3_mem_ctrl_init(int reset); + +/* + * Configure ZQ I/O interface + * + * @param mem Memory timings for this memory type. + * @param phy0_ctrl Pointer to struct containing PHY0 control reg + * @param phy1_ctrl Pointer to struct containing PHY1 control reg + * @return 0 if ok, -1 on error + */ +int dmc_config_zq(struct mem_timings *mem, + struct exynos5_phy_control *phy0_ctrl, + struct exynos5_phy_control *phy1_ctrl); + +/* + * Send NOP and MRS/EMRS Direct commands + * + * @param mem Memory timings for this memory type. + * @param dmc Pointer to struct of DMC registers + */ +void dmc_config_mrs(struct mem_timings *mem, struct exynos5_dmc *dmc); + +/* + * Send PALL Direct commands + * + * @param mem Memory timings for this memory type. + * @param dmc Pointer to struct of DMC registers + */ +void dmc_config_prech(struct mem_timings *mem, struct exynos5_dmc *dmc); + +/* + * Configure the memconfig and membaseconfig registers + * + * @param mem Memory timings for this memory type. + * @param exynos5_dmc Pointer to struct of DMC registers + */ +void dmc_config_memory(struct mem_timings *mem, struct exynos5_dmc *dmc); + +/* Set the PS-Hold drive value */ +void ps_hold_setup(void); +/* + * Reset the DLL. This function is common between DDR3 and LPDDR2. + * However, the reset value is different. So we are passing a flag + * ddr_mode to distinguish between LPDDR2 and DDR3. + * + * @param exynos5_dmc Pointer to struct of DMC registers + * @param ddr_mode Type of DDR memory + */ +void update_reset_dll(struct exynos5_dmc *, enum ddr_mode); +#endif diff --git a/src/soc/samsung/exynos5420/smp.c b/src/soc/samsung/exynos5420/smp.c new file mode 100644 index 0000000000..6fc2fb01ea --- /dev/null +++ b/src/soc/samsung/exynos5420/smp.c @@ -0,0 +1,307 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2013 Google Inc. + * Copyright (C) 2012 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <stdlib.h> +#include <string.h> +#include <types.h> +#include <arch/cpu.h> +#include <arch/io.h> + +#include "cpu.h" +#include "power.h" + +/* ACTLR, L2CTLR L2ACTLR constants used in SMP core power up. */ + +#define ACTLR_SMP (1 << 6) + +#define L2CTLR_ECC_PARITY (1 << 21) +#define L2CTLR_DATA_RAM_LATENCY_MASK (7 << 0) +#define L2CTLR_TAG_RAM_LATENCY_MASK (7 << 6) +#define L2CTLR_DATA_RAM_LATENCY_CYCLES_3 (2 << 0) +#define L2CTLR_TAG_RAM_LATENCY_CYCLES_3 (2 << 6) + +#define L2ACTLR_DISABLE_CLEAN_EVICT_PUSH_EXTERNAL (1 << 3) +#define L2ACTLR_ENABLE_HAZARD_DETECT_TIMEOUT (1 << 7) +#define L2ACTLR_FORCE_L2_LOGIC_CLOCK_ENABLE_ACTIVE (1 << 27) + +/* Part number in CPU ID (MPIDR). */ +#define PART_NUMBER_CORTEX_A15 (0xc0f) + +/* State of CPU cores in Exynos 5420. */ +#define CORE_STATE_RESET (1 << 0) +#define CORE_STATE_SECONDARY_RESET (1 << 1) +#define CORE_STATE_SWITCH_CLUSTER (1 << 4) + +/* The default address to re-power on a code. */ +#define CORE_RESET_INIT_ADDRESS ((void*)0x00000000) + +/* Vectors in BL1 (0x02020000 = base of iRAM). */ +#define VECTOR_CORE_SEV_HANDLER ((void*)(intptr_t)0x02020004) +#define VECTOR_LOW_POWER_FLAG ((void*)(intptr_t)0x02020028) +#define VECTOR_LOW_POWER_ADDRESS ((void*)(intptr_t)0x0202002C) + +/* The data structure for the "CPU state" memory page (shared with kernel) + * controlling cores in active cluster. Kernel will put starting address for one + * core in "hotplug_address" before power on. Note the address is hard-coded in + * kernel (EXYNOS5420_PA_SYSRAM_NS = 0x02073000). */ +volatile struct exynos5420_cpu_states +{ + uint32_t _reserved[2]; /* RESV, +0x00 */ + uint32_t resume_address; /* REG0, +0x08 */ + uint32_t resume_flag; /* REG1, +0x0C */ + uint32_t _reg2; /* REG2, +0x10 */ + uint32_t _reg3; /* REG3, +0x14 */ + uint32_t switch_address; /* REG4, +0x18, cluster switching */ + uint32_t hotplug_address; /* REG5, +0x1C, core hotplug */ + uint32_t _reg6; /* REG6, +0x20 */ + uint32_t c2_address; /* REG7, +0x24, C2 state change */ + + /* Managed per core status for active cluster, offset: +0x28~0x38 */ + uint32_t cpu_states[4]; + + /* Managed per core GIC status for active cluster, offset: 0x38~0x48 */ + uint32_t cpu_gic_states[4]; +} *exynos_cpu_states = (volatile struct exynos5420_cpu_states*)0x02073000; + +/* When leaving core handlers and jump to hot-plug address (or cluster + * switching), we are not sure if the destination is Thumb or ARM mode. + * So a BX command is required. + */ +inline static void jump_bx(void *address) +{ + asm volatile ("bx %0" : : "r"(address)); + /* never returns. */ +} + +/* Extracts arbitrary bits from a 32-bit unsigned int. */ +inline static uint32_t get_bits(uint32_t value, uint32_t start, uint32_t len) +{ + return ((value << (sizeof(value) * 8 - len - start)) >> + (sizeof(value) * 8 - len)); +} + +/* Waits the referenced address to be ready (non-zero) and then jump into it. */ +static void wait_and_jump(volatile uint32_t* reference) +{ + while (!*reference) { + wfe(); + } + jump_bx((void*)*reference); +} + +/* Configures L2 Control Register to use 3 cycles for DATA/TAG RAM latency. */ +static void configure_l2ctlr(void) +{ + uint32_t val; + + val = read_l2ctlr(); + val &= ~(L2CTLR_DATA_RAM_LATENCY_MASK | L2CTLR_TAG_RAM_LATENCY_MASK); + val |= (L2CTLR_DATA_RAM_LATENCY_CYCLES_3 | L2CTLR_TAG_RAM_LATENCY_CYCLES_3 | + L2CTLR_ECC_PARITY); + write_l2ctlr(val); +} + +/* Configures L2 Auxiliary Control Register for Cortex A15. */ +static void configure_l2actlr(void) +{ + uint32_t val; + + val = read_l2actlr(); + val |= (L2ACTLR_DISABLE_CLEAN_EVICT_PUSH_EXTERNAL | + L2ACTLR_ENABLE_HAZARD_DETECT_TIMEOUT | + L2ACTLR_FORCE_L2_LOGIC_CLOCK_ENABLE_ACTIVE); + write_l2actlr(val); +} + +/* Initializes the CPU states to reset state. */ +static void init_exynos_cpu_states(void) { + memset((void*)exynos_cpu_states, 0, sizeof(*exynos_cpu_states)); + exynos_cpu_states->cpu_states[0] = CORE_STATE_RESET; + exynos_cpu_states->cpu_states[1] = CORE_STATE_SECONDARY_RESET; + exynos_cpu_states->cpu_states[2] = CORE_STATE_SECONDARY_RESET; + exynos_cpu_states->cpu_states[3] = CORE_STATE_SECONDARY_RESET; +} + +/* + * Ensures that the L2 logic has been used within the previous 256 cycles + * before modifying the ACTLR.SMP bit. This is required during boot before + * MMU has been enabled, or during a specified reset or power down sequence. + */ +static void enable_smp(void) +{ + uint32_t actlr, val; + + /* Enable SMP mode */ + actlr = read_actlr(); + actlr |= ACTLR_SMP; + + /* Dummy read to assure L2 access */ + val = readl(&exynos_power->inform0); + val &= 0; + actlr |= val; + + write_actlr(actlr); + dsb(); + isb(); +} + +/* Starts the core and jumps to correct location by its state. */ +static void core_start_execution(void) +{ + u32 cpu_id, cpu_state; + + enable_smp(); + set_system_mode(); + + cpu_id = read_mpidr() & 0x3; /* up to 4 processors for one cluster. */ + cpu_state = exynos_cpu_states->cpu_states[cpu_id]; + + if (cpu_state & CORE_STATE_SWITCH_CLUSTER) { + wait_and_jump(&exynos_cpu_states->switch_address); + /* never returns. */ + } + + /* Standard Exynos suspend/resume. */ + if (exynos_power->inform1) { + exynos_power->inform1 = 0; + jump_bx((void*)exynos_power->inform0); + /* never returns. */ + } + + if (cpu_state & CORE_STATE_RESET) { + /* For Reset, U-Boot jumps to its starting address; + * on Coreboot, seems ok to ignore for now. */ + } + wait_and_jump(&exynos_cpu_states->hotplug_address); + /* never returns. */ +} + +/* The entry point for hotplug-in and cluster switching. */ +static void low_power_start(void) +{ + uint32_t sctlr, reg_val; + + /* On warm reset, because iRAM is not cleared, all cores will enter + * low_power_start, not the initial address. So we need to check reset + * status again, and jump to 0x0 in that case. */ + reg_val = readl(&exynos_power->spare0); + if (reg_val != RST_FLAG_VAL) { + writel(0x0, VECTOR_LOW_POWER_FLAG); + jump_bx(CORE_RESET_INIT_ADDRESS); + /* restart cpu execution and never returns. */ + } + + /* Workaround for iROM EVT1. A7 core execution may flow into incorrect + * path, bypassing first jump address and makes final jump address 0x0, + * so we try to make any core set again low_power_start address, if that + * becomes zero. */ + reg_val = readl(VECTOR_CORE_SEV_HANDLER); + if (reg_val != (intptr_t)low_power_start) { + writel((intptr_t)low_power_start, VECTOR_CORE_SEV_HANDLER); + dsb(); + /* ask all cores to power on again. */ + sev(); + } + + set_system_mode(); + + /* Whenever a Cortex A-15 core powers on, iROM resets its L2 cache + * so we need to configure again. */ + if (get_bits(read_midr(), 4, 12) == PART_NUMBER_CORTEX_A15) { + configure_l2ctlr(); + configure_l2actlr(); + } + + /* Invalidate L1 & TLB */ + tlbiall(); + iciallu(); + + /* Disable MMU stuff and caches */ + sctlr = read_sctlr(); + sctlr &= ~(SCTLR_V | SCTLR_M | SCTLR_C); + sctlr |= (SCTLR_I | SCTLR_Z | SCTLR_A); + write_sctlr(sctlr); + + core_start_execution(); + /* The core should not return. But in order to prevent unexpected + * errors, a WFI command will help to put CPU back to idle state. */ + wfi(); +} + +/* Callback to shutdown a core, safe to be set as hot-plug address. */ +static void power_down_core(void) +{ + uint32_t mpidr, core_id; + + /* MPIDR: 0~2=ID, 8~11=cluster. On Exynos 5420, cluster will be only 0 + * or 1. */ + mpidr = read_mpidr(); + core_id = get_bits(mpidr, 0, 2) | (get_bits(mpidr, 8, 4) << 2); + + /* Set the status of the core to low. + * S5E5420A User Manual, 8.8.1.202, ARM_CORE0_CONFIGURATION, two bits to + * control power state in each power down level. + */ + writel(0x0, &exynos_power->arm_core[core_id].config); + + /* S5E5420A User Manual, 8.4.2.5, after ARM_CORE*_CONFIGURATION has been + * set to zero, PMU will detect and wait for WFI then run power-down + * sequence. */ + wfi(); +} + +/* Configures the CPU states shard memory page and then shutdown all cores. */ +static void configure_secondary_cores(void) +{ + if (get_bits(read_midr(), 4, 12) == PART_NUMBER_CORTEX_A15) { + configure_l2ctlr(); + configure_l2actlr(); + } + + /* Currently we use power_down_core as callback for each core to + * shutdown itself, but it is also ok to directly set ARM_CORE*_CONFIG + * to zero by CPU0 because every secondary cores should be already in + * WFI state (in bootblock). The power_down_core will be more helpful + * when we want to use SMP inside firmware. */ + + /* Clear boot reg (hotplug address) in cpu states */ + writel(0, (void*)&exynos_cpu_states->hotplug_address); + + /* set low_power flag and address */ + writel((intptr_t)low_power_start, VECTOR_LOW_POWER_ADDRESS); + writel(RST_FLAG_VAL, VECTOR_LOW_POWER_FLAG); + writel(RST_FLAG_VAL, &exynos_power->spare0); + + /* On next SEV, shutdown all cores. */ + writel((intptr_t)power_down_core, VECTOR_CORE_SEV_HANDLER); + + /* Ask all cores in WFE mode to shutdown. */ + dsb(); + sev(); +} + +/* Configures the SMP cores on Exynos 5420 SOC (and shutdown all secondary + * cores) */ +void exynos5420_config_smp(void) +{ + init_exynos_cpu_states(); + configure_secondary_cores(); +} + diff --git a/src/soc/samsung/exynos5420/spi.c b/src/soc/samsung/exynos5420/spi.c new file mode 100644 index 0000000000..c6c08e925c --- /dev/null +++ b/src/soc/samsung/exynos5420/spi.c @@ -0,0 +1,411 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2011 Samsung Electronics + * Copyright 2013 Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <console/console.h> +#include <arch/io.h> +#include <stdlib.h> +#include <assert.h> +#include <spi_flash.h> + +#include "cpu.h" +#include "spi.h" + +#define EXYNOS_SPI_MAX_TRANSFER_BYTES (65535) + +#if defined(CONFIG_DEBUG_SPI) && CONFIG_DEBUG_SPI +# define DEBUG_SPI(x,...) printk(BIOS_DEBUG, "EXYNOS_SPI: " x) +#else +# define DEBUG_SPI(x,...) +#endif + +struct exynos_spi_slave { + struct spi_slave slave; + struct exynos_spi *regs; + unsigned int fifo_size; + uint8_t half_duplex; + uint8_t frame_header; /* header byte to detect in half-duplex mode. */ +}; + +/* TODO(hungte) Move the SPI param list to per-board configuration, probably + * Kconfig or mainboard.c */ +static struct exynos_spi_slave exynos_spi_slaves[3] = { + // SPI 0 + { + .slave = { .bus = 0, }, + .regs = (void *)EXYNOS5_SPI0_BASE, + }, + // SPI 1 + { + .slave = { .bus = 1, .rw = SPI_READ_FLAG, }, + .regs = (void *)EXYNOS5_SPI1_BASE, + .fifo_size = 64, + .half_duplex = 0, + }, + // SPI 2 + { + .slave = { .bus = 2, + .rw = SPI_READ_FLAG | SPI_WRITE_FLAG, }, + .regs = (void *)EXYNOS5_SPI2_BASE, + .fifo_size = 64, + .half_duplex = 1, + .frame_header = 0xec, + }, +}; + +static inline struct exynos_spi_slave *to_exynos_spi(struct spi_slave *slave) +{ + return container_of(slave, struct exynos_spi_slave, slave); +} + +void spi_init(void) +{ + printk(BIOS_INFO, "Exynos SPI driver initiated.\n"); +} + +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs) +{ + ASSERT(bus >= 0 && bus < 3); + return &(exynos_spi_slaves[bus].slave); +} + +int spi_cs_is_valid(unsigned int bus, unsigned int cs) +{ + return bus > 0 && bus < 3; +} + +void spi_cs_activate(struct spi_slave *slave) +{ + struct exynos_spi *regs = to_exynos_spi(slave)->regs; + // TODO(hungte) Add some delay if too many transactions happen at once. + clrbits_le32(®s->cs_reg, SPI_SLAVE_SIG_INACT); +} + +void spi_cs_deactivate(struct spi_slave *slave) +{ + struct exynos_spi *regs = to_exynos_spi(slave)->regs; + setbits_le32(®s->cs_reg, SPI_SLAVE_SIG_INACT); +} + +static inline void exynos_spi_soft_reset(struct exynos_spi *regs) +{ + /* The soft reset clears only FIFO and status register. + * All special function registers are not changed. */ + setbits_le32(®s->ch_cfg, SPI_CH_RST); + clrbits_le32(®s->ch_cfg, SPI_CH_RST); +} + +static inline void exynos_spi_flush_fifo(struct exynos_spi *regs) +{ + /* + * Flush spi tx, rx fifos and reset the SPI controller + * and clear rx/tx channel + */ + clrbits_le32(®s->ch_cfg, SPI_RX_CH_ON | SPI_TX_CH_ON); + clrbits_le32(®s->ch_cfg, SPI_CH_HS_EN); + exynos_spi_soft_reset(regs); + setbits_le32(®s->ch_cfg, SPI_RX_CH_ON | SPI_TX_CH_ON); +} + +static void exynos_spi_request_bytes(struct exynos_spi *regs, int count, + int width) +{ + uint32_t mode_word = SPI_MODE_CH_WIDTH_WORD | SPI_MODE_BUS_WIDTH_WORD, + swap_word = (SPI_TX_SWAP_EN | SPI_RX_SWAP_EN | + SPI_TX_BYTE_SWAP | SPI_RX_BYTE_SWAP | + SPI_TX_HWORD_SWAP | SPI_RX_HWORD_SWAP); + + /* For word address we need to swap bytes */ + if (width == sizeof(uint32_t)) { + setbits_le32(®s->mode_cfg, mode_word); + setbits_le32(®s->swap_cfg, swap_word); + count /= width; + } else { + /* Select byte access and clear the swap configuration */ + clrbits_le32(®s->mode_cfg, mode_word); + writel(0, ®s->swap_cfg); + } + + exynos_spi_soft_reset(regs); + + if (count) { + ASSERT(count < (1 << 16)); + writel(count | SPI_PACKET_CNT_EN, ®s->pkt_cnt); + } else { + writel(0, ®s->pkt_cnt); + } +} + +static int spi_rx_tx(struct spi_slave *slave, uint8_t *rxp, int rx_bytes, + const uint8_t *txp, int tx_bytes) +{ + struct exynos_spi_slave *espi = to_exynos_spi(slave); + struct exynos_spi *regs = espi->regs; + + int step; + int todo = MAX(rx_bytes, tx_bytes); + int wait_for_frame_header = espi->half_duplex; + + ASSERT(todo < EXYNOS_SPI_MAX_TRANSFER_BYTES); + + /* Select transfer mode. */ + if (espi->half_duplex) { + step = 1; + } else if ((rx_bytes | tx_bytes | (uintptr_t)rxp |(uintptr_t)txp) & 3) { + printk(BIOS_CRIT, "%s: WARNING: tranfer mode decreased to 1B\n", + __func__); + step = 1; + } else { + step = sizeof(uint32_t); + } + + exynos_spi_request_bytes(regs, espi->half_duplex ? 0 : todo, step); + + /* Note: Some device, like ChromeOS EC, tries to work in half-duplex + * mode and sends a large amount of data (larger than FIFO size). + * Printing lots of debug messages or doing extra delay in the loop + * below may cause rx buffer to overflow and getting unexpected data + * error. + */ + while (rx_bytes || tx_bytes) { + int temp; + uint32_t spi_sts = readl(®s->spi_sts); + int rx_lvl = (spi_sts >> SPI_RX_LVL_OFFSET) & SPI_FIFO_LVL_MASK, + tx_lvl = (spi_sts >> SPI_TX_LVL_OFFSET) & SPI_FIFO_LVL_MASK; + int min_tx = ((tx_bytes || !espi->half_duplex) ? + (espi->fifo_size / 2) : 1); + + // TODO(hungte) Abort if timeout happens in half-duplex mode. + + /* + * Don't completely fill the txfifo, since we don't want our + * rxfifo to overflow, and it may already contain data. + */ + while (tx_lvl < min_tx) { + if (tx_bytes) { + if (step == sizeof(uint32_t)) { + temp = *((uint32_t *)txp); + txp += sizeof(uint32_t); + } else { + temp = *txp++; + } + tx_bytes -= step; + } else { + temp = -1; + } + writel(temp, ®s->tx_data); + tx_lvl += step; + } + + while ((rx_lvl >= step) && rx_bytes) { + temp = readl(®s->rx_data); + rx_lvl -= step; + if (wait_for_frame_header) { + if ((temp & 0xff) == espi->frame_header) { + wait_for_frame_header = 0; + } + break; /* Restart the outer loop. */ + } + if (step == sizeof(uint32_t)) { + *((uint32_t *)rxp) = temp; + rxp += sizeof(uint32_t); + } else { + *rxp++ = temp; + } + rx_bytes -= step; + } + } + return 0; +} + +int spi_claim_bus(struct spi_slave *slave) +{ + struct exynos_spi_slave *espi = to_exynos_spi(slave); + struct exynos_spi *regs = espi->regs; + + exynos_spi_flush_fifo(regs); + + // Select Active High Clock, Format A (SCP 30.2.1.8). + clrbits_le32(®s->ch_cfg, SPI_CH_CPOL_L | SPI_CH_CPHA_B); + + // Set FeedBack Clock Selection. + writel(SPI_FB_DELAY_180, ®s->fb_clk); + + // HIGH speed is required for Tx/Rx to work in 50MHz (SCP 30.2.1.6). + if (espi->half_duplex) { + clrbits_le32(®s->ch_cfg, SPI_CH_HS_EN); + printk(BIOS_DEBUG, "%s: LOW speed.\n", __func__); + } else { + setbits_le32(®s->ch_cfg, SPI_CH_HS_EN); + printk(BIOS_DEBUG, "%s: HIGH speed.\n", __func__); + } + return 0; +} + +int spi_xfer(struct spi_slave *slave, const void *dout, unsigned int out_bytes, + void *din, unsigned int in_bytes) +{ + uint8_t *out_ptr = (uint8_t *)dout, *in_ptr = (uint8_t *)din; + int offset, todo, len; + int ret = 0; + + len = MAX(out_bytes, in_bytes); + + /* + * Exynos SPI limits each transfer to (2^16-1=65535) bytes. To keep + * things simple (especially for word-width transfer mode), allow a + * maximum of (2^16-4=65532) bytes. We could allow more in word mode, + * but the performance difference is small. + */ + spi_cs_activate(slave); + for (offset = 0; !ret && (offset < len); offset += todo) { + todo = min(len - offset, (1 << 16) - 4); + ret = spi_rx_tx(slave, in_ptr, MIN(in_bytes, todo), out_ptr, + MIN(out_bytes, todo)); + // Adjust remaining bytes and pointers. + if (in_bytes >= todo) { + in_bytes -= todo; + in_ptr += todo; + } else { + in_bytes = 0; + in_ptr = NULL; + } + if (out_bytes >= todo) { + out_bytes -= todo; + out_ptr += todo; + } else { + out_bytes = 0; + out_ptr = NULL; + } + } + spi_cs_deactivate(slave); + + return ret; +} + +static int exynos_spi_read(struct spi_slave *slave, void *dest, uint32_t len, + uint32_t off) +{ + struct exynos_spi *regs = to_exynos_spi(slave)->regs; + int rv; + + // TODO(hungte) Merge the "read address" command into spi_xfer calls + // (full-duplex mode). + + spi_cs_activate(slave); + + // Specify read address (in word-width mode). + ASSERT(off < (1 << 24)); + exynos_spi_request_bytes(regs, sizeof(off), sizeof(off)); + writel(htonl((SF_READ_DATA_CMD << 24) | off), ®s->tx_data); + while (!(readl(®s->spi_sts) & SPI_ST_TX_DONE)) { + /* Wait for TX done */ + } + + // Now, safe to transfer. + rv = spi_xfer(slave, NULL, 0, dest, len * 8); + spi_cs_deactivate(slave); + + return (rv == 0) ? len : -1; +} + +void spi_release_bus(struct spi_slave *slave) +{ + struct exynos_spi *regs = to_exynos_spi(slave)->regs; + /* Reset swap mode to make sure no one relying on default values (Ex, + * payload or kernel) will go wrong. */ + clrbits_le32(®s->mode_cfg, (SPI_MODE_CH_WIDTH_WORD | + SPI_MODE_BUS_WIDTH_WORD)); + writel(0, ®s->swap_cfg); + exynos_spi_flush_fifo(regs); +} + +// SPI as CBFS media. +struct exynos_spi_media { + struct spi_slave *slave; + struct cbfs_simple_buffer buffer; +}; + +static int exynos_spi_cbfs_open(struct cbfs_media *media) +{ + struct exynos_spi_media *spi = (struct exynos_spi_media*)media->context; + DEBUG_SPI("exynos_spi_cbfs_open\n"); + return spi_claim_bus(spi->slave); +} + +static int exynos_spi_cbfs_close(struct cbfs_media *media) +{ + struct exynos_spi_media *spi = (struct exynos_spi_media*)media->context; + DEBUG_SPI("exynos_spi_cbfs_close\n"); + spi_release_bus(spi->slave); + return 0; +} + +static size_t exynos_spi_cbfs_read(struct cbfs_media *media, void *dest, + size_t offset, size_t count) +{ + struct exynos_spi_media *spi = (struct exynos_spi_media*)media->context; + int bytes; + DEBUG_SPI("exynos_spi_cbfs_read(%u)\n", count); + bytes = exynos_spi_read(spi->slave, dest, count, offset); + return bytes; +} + +static void *exynos_spi_cbfs_map(struct cbfs_media *media, size_t offset, + size_t count) +{ + struct exynos_spi_media *spi = (struct exynos_spi_media*)media->context; + DEBUG_SPI("exynos_spi_cbfs_map\n"); + // exynos: spi_rx_tx may work in 4 byte-width-transmission mode and + // requires buffer memory address to be aligned. + if (count % 4) + count += 4 - (count % 4); + return cbfs_simple_buffer_map(&spi->buffer, media, offset, count); +} + +static void *exynos_spi_cbfs_unmap(struct cbfs_media *media, + const void *address) +{ + struct exynos_spi_media *spi = (struct exynos_spi_media*)media->context; + DEBUG_SPI("exynos_spi_cbfs_unmap\n"); + return cbfs_simple_buffer_unmap(&spi->buffer, address); +} + +int initialize_exynos_spi_cbfs_media(struct cbfs_media *media, + void *buffer_address, + size_t buffer_size) +{ + // TODO Replace static variable to support multiple streams. + static struct exynos_spi_media context; + static struct exynos_spi_slave *eslave = &exynos_spi_slaves[1]; + DEBUG_SPI("initialize_exynos_spi_cbfs_media\n"); + + context.slave = &eslave->slave; + context.buffer.allocated = context.buffer.last_allocate = 0; + context.buffer.buffer = buffer_address; + context.buffer.size = buffer_size; + media->context = (void*)&context; + media->open = exynos_spi_cbfs_open; + media->close = exynos_spi_cbfs_close; + media->read = exynos_spi_cbfs_read; + media->map = exynos_spi_cbfs_map; + media->unmap = exynos_spi_cbfs_unmap; + + return 0; +} diff --git a/src/soc/samsung/exynos5420/spi.h b/src/soc/samsung/exynos5420/spi.h new file mode 100644 index 0000000000..94b4fdaace --- /dev/null +++ b/src/soc/samsung/exynos5420/spi.h @@ -0,0 +1,93 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2012 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef CPU_SAMSUNG_EXYNOS5420_SPI_H +#define CPU_SAMSUNG_EXYNOS5420_SPI_H + +/* This driver serves as a CBFS media source. */ +#include <cbfs.h> + +/* SPI peripheral register map; padded to 64KB */ +struct exynos_spi { + unsigned int ch_cfg; /* 0x00 */ + unsigned char reserved0[4]; + unsigned int mode_cfg; /* 0x08 */ + unsigned int cs_reg; /* 0x0c */ + unsigned char reserved1[4]; + unsigned int spi_sts; /* 0x14 */ + unsigned int tx_data; /* 0x18 */ + unsigned int rx_data; /* 0x1c */ + unsigned int pkt_cnt; /* 0x20 */ + unsigned char reserved2[4]; + unsigned int swap_cfg; /* 0x28 */ + unsigned int fb_clk; /* 0x2c */ + unsigned char padding[0xffd0]; +}; + +#define EXYNOS_SPI_MAX_FREQ 50000000 + +#define SPI_TIMEOUT_MS 10 + +#define SF_READ_DATA_CMD 0x3 + +/* SPI_CHCFG */ +#define SPI_CH_HS_EN (1 << 6) +#define SPI_CH_RST (1 << 5) +#define SPI_SLAVE_MODE (1 << 4) +#define SPI_CH_CPOL_L (1 << 3) +#define SPI_CH_CPHA_B (1 << 2) +#define SPI_RX_CH_ON (1 << 1) +#define SPI_TX_CH_ON (1 << 0) + +/* SPI_MODECFG */ +#define SPI_MODE_CH_WIDTH_WORD (0x2 << 29) +#define SPI_MODE_BUS_WIDTH_WORD (0x2 << 17) + +/* SPI_CSREG */ +#define SPI_SLAVE_SIG_INACT (1 << 0) + +/* SPI_STS */ +#define SPI_ST_TX_DONE (1 << 25) +#define SPI_FIFO_LVL_MASK 0x1ff +#define SPI_TX_LVL_OFFSET 6 +#define SPI_RX_LVL_OFFSET 15 + +/* Feedback Delay */ +#define SPI_CLK_BYPASS (0 << 0) +#define SPI_FB_DELAY_90 (1 << 0) +#define SPI_FB_DELAY_180 (2 << 0) +#define SPI_FB_DELAY_270 (3 << 0) + +/* Packet Count */ +#define SPI_PACKET_CNT_EN (1 << 16) + +/* Swap config */ +#define SPI_TX_SWAP_EN (1 << 0) +#define SPI_TX_BYTE_SWAP (1 << 2) +#define SPI_TX_HWORD_SWAP (1 << 3) +#define SPI_TX_BYTE_SWAP (1 << 2) +#define SPI_RX_SWAP_EN (1 << 4) +#define SPI_RX_BYTE_SWAP (1 << 6) +#define SPI_RX_HWORD_SWAP (1 << 7) + +/* Serve as CBFS media source */ +int initialize_exynos_spi_cbfs_media(struct cbfs_media *media, + void *buffer_address, + size_t buffer_size); +#endif diff --git a/src/soc/samsung/exynos5420/sysreg.h b/src/soc/samsung/exynos5420/sysreg.h new file mode 100644 index 0000000000..fa25def318 --- /dev/null +++ b/src/soc/samsung/exynos5420/sysreg.h @@ -0,0 +1,42 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* Register map for Exynos5 sysreg */ + +#ifndef CPU_SAMSUNG_EXYNOS5420_SYSREG_H +#define CPU_SAMSUNG_EXYNOS5420_SYSREG_H + +#include "cpu.h" + +/* sysreg map */ +struct exynos5_sysreg { + /* Add registers as and when required */ + unsigned char res1[0x214]; + unsigned int disp1blk_cfg; + unsigned char res2[0x18]; + unsigned int usb20_phy_cfg; +}; + +static struct exynos5_sysreg * const exynos_sysreg = + (void *)EXYNOS5_SYSREG_BASE; + +#define FIMDBYPASS_DISP1 (1 << 15) +#define USB20_PHY_CFG_EN (1 << 0) + +#endif diff --git a/src/soc/samsung/exynos5420/timer.c b/src/soc/samsung/exynos5420/timer.c new file mode 100644 index 0000000000..ae13342c92 --- /dev/null +++ b/src/soc/samsung/exynos5420/timer.c @@ -0,0 +1,56 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2009 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <console/console.h> +#include <timer.h> +#include <delay.h> +#include <thread.h> +#include "clk.h" + +void init_timer(void) +{ + /* Nothing to do because we manually + * call mct_start() in the bootblock + */ +} + +/* delay x useconds */ +void udelay(unsigned usec) +{ + struct mono_time current, end; + + if (!thread_yield_microseconds(usec)) + return; + + timer_monotonic_get(¤t); + end = current; + mono_time_add_usecs(&end, usec); + + if (mono_time_after(¤t, &end)) { + printk(BIOS_EMERG, "udelay: 0x%08x is impossibly large\n", + usec); + /* There's not much we can do if usec is too big. Use a long, + * paranoid delay value and hope for the best... */ + end = current; + mono_time_add_usecs(&end, USECS_PER_SEC); + } + + while (mono_time_before(¤t, &end)) + timer_monotonic_get(¤t); +} diff --git a/src/soc/samsung/exynos5420/tmu.c b/src/soc/samsung/exynos5420/tmu.c new file mode 100644 index 0000000000..1b04b6cb42 --- /dev/null +++ b/src/soc/samsung/exynos5420/tmu.c @@ -0,0 +1,215 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2012 Samsung Electronics + * Copyright 2013 Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* EXYNOS - Thermal Management Unit */ + +#include <console/console.h> +#include <arch/io.h> +#include "power.h" +#include "tmu.h" + +#define TRIMINFO_RELOAD 1 +#define CORE_EN 1 +#define THERM_TRIP_EN (1 << 12) + +#define INTEN_RISE0 1 +#define INTEN_RISE1 (1 << 4) +#define INTEN_RISE2 (1 << 8) +#define INTEN_FALL0 (1 << 16) +#define INTEN_FALL1 (1 << 20) +#define INTEN_FALL2 (1 << 24) + +#define TRIM_INFO_MASK 0xff + +#define INTCLEAR_RISE0 1 +#define INTCLEAR_RISE1 (1 << 4) +#define INTCLEAR_RISE2 (1 << 8) +#define INTCLEAR_FALL0 (1 << 16) +#define INTCLEAR_FALL1 (1 << 20) +#define INTCLEAR_FALL2 (1 << 24) +#define INTCLEARALL (INTCLEAR_RISE0 | INTCLEAR_RISE1 | \ + INTCLEAR_RISE2 | INTCLEAR_FALL0 | \ + INTCLEAR_FALL1 | INTCLEAR_FALL2) + +struct tmu_info exynos5420_tmu_info = { + .tmu_base = 0x10060000, + .tmu_mux = 6, + .data = { + .ts = { + .min_val = 25, + .max_val = 125, + .start_warning = 95, + .start_tripping = 105, + .hardware_tripping = 110, + }, + .efuse_min_value = 40, + .efuse_value = 55, + .efuse_max_value = 100, + .slope = 0x10008802, + }, + .dc_value = 25, +}; + +/* + * After reading temperature code from register, compensating + * its value and calculating celsius temperatue, + * get current temperatue. + * + * @return current temperature of the chip as sensed by TMU + */ +static int get_cur_temp(struct tmu_info *info) +{ + int cur_temp; + struct tmu_reg *reg = (struct tmu_reg *)info->tmu_base; + + /* Temperature code range between min 25 and max 125 */ + cur_temp = readl(®->current_temp) & 0xff; + + /* Calibrate current temperature */ + if (cur_temp) + cur_temp = cur_temp - info->te1 + info->dc_value; + + return cur_temp; +} + +/* + * Monitors status of the TMU device and exynos temperature + * + * @info TMU info + * @temp pointer to the current temperature value + * @return enum tmu_status_t value, code indicating event to execute + */ +enum tmu_status_t tmu_monitor(struct tmu_info *info, int *temp) +{ + if (info->tmu_state == TMU_STATUS_INIT) + return -1; + + int cur_temp; + struct tmu_data *data = &info->data; + + /* Read current temperature of the SOC */ + cur_temp = get_cur_temp(info); + *temp = cur_temp; + + /* Temperature code lies between min 25 and max 125 */ + if (cur_temp >= data->ts.start_tripping && + cur_temp <= data->ts.max_val) + return TMU_STATUS_TRIPPED; + else if (cur_temp >= data->ts.start_warning) + return TMU_STATUS_WARNING; + else if (cur_temp < data->ts.start_warning && + cur_temp >= data->ts.min_val) + return TMU_STATUS_NORMAL; + /* Temperature code does not lie between min 25 and max 125 */ + else { + info->tmu_state = TMU_STATUS_INIT; + printk(BIOS_DEBUG, "EXYNOS_TMU: Thermal reading failed\n"); + return -1; + } + return 0; +} + +/* + * Calibrate and calculate threshold values and + * enable interrupt levels + * + * @param info pointer to the tmu_info struct + */ +static void tmu_setup_parameters(struct tmu_info *info) +{ + unsigned int te_temp, con; + unsigned int warning_code, trip_code, hwtrip_code; + unsigned int cooling_temp; + unsigned int rising_value; + struct tmu_data *data = &info->data; + struct tmu_reg *reg = (struct tmu_reg *)info->tmu_base; + + /* Must reload for using efuse value at EXYNOS */ + writel(TRIMINFO_RELOAD, ®->triminfo_control); + + /* Get the compensation parameter */ + te_temp = readl(®->triminfo); + info->te1 = te_temp & TRIM_INFO_MASK; + info->te2 = ((te_temp >> 8) & TRIM_INFO_MASK); + + if ((data->efuse_min_value > info->te1) || + (info->te1 > data->efuse_max_value) + || (info->te2 != 0)) + info->te1 = data->efuse_value; + + /* Get RISING & FALLING Threshold value */ + warning_code = data->ts.start_warning + + info->te1 - info->dc_value; + trip_code = data->ts.start_tripping + + info->te1 - info->dc_value; + hwtrip_code = data->ts.hardware_tripping + + info->te1 - info->dc_value; + + cooling_temp = 0; + + rising_value = ((warning_code << 8) | + (trip_code << 16) | + (hwtrip_code << 24)); + + /* Set interrupt level */ + writel(rising_value, ®->threshold_temp_rise); + writel(cooling_temp, ®->threshold_temp_fall); + + /* + * Need to init all register settings after getting parameter info + * [28:23] vref [11:8] slope - Tuning parameter + * + * WARNING: this slope value writes into many bits in the tmu_control + * register, with the default FDT value of 268470274 (0x10008802) + * we are using this essentially sets the default register setting + * from the TRM for tmu_control. + * TODO(bhthompson): rewrite this code such that we are not performing + * a hard wipe of tmu_control and re verify functionality. + */ + writel(data->slope, ®->tmu_control); + + writel(INTCLEARALL, ®->intclear); + /* TMU core enable */ + con = readl(®->tmu_control); + con |= (info->tmu_mux << 20) | THERM_TRIP_EN | CORE_EN; + + writel(con, ®->tmu_control); + + /* Enable HW thermal trip */ + power_enable_hw_thermal_trip(); + + /* LEV1 LEV2 interrupt enable */ + writel(INTEN_RISE1 | INTEN_RISE2, ®->inten); +} + +/* + * Initialize TMU device + * + * @return int value, 0 for success + */ +int tmu_init(struct tmu_info *info) +{ + info->tmu_state = TMU_STATUS_INIT; + + tmu_setup_parameters(info); + info->tmu_state = TMU_STATUS_NORMAL; + + return 0; +} diff --git a/src/soc/samsung/exynos5420/tmu.h b/src/soc/samsung/exynos5420/tmu.h new file mode 100644 index 0000000000..cf81b9ad33 --- /dev/null +++ b/src/soc/samsung/exynos5420/tmu.h @@ -0,0 +1,134 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* EXYNOS - Thermal Management Unit */ + +#ifndef CPU_SAMSUNG_EXYNOS5420_TMU_H +#define CPU_SAMSUNG_EXYNOS5420_TMU_H + +struct tmu_reg { + unsigned triminfo; + unsigned rsvd1; + unsigned rsvd2; + unsigned rsvd3; + unsigned rsvd4; + unsigned triminfo_control; + unsigned rsvd5; + unsigned rsvd6; + unsigned tmu_control; + unsigned rsvd7; + unsigned tmu_status; + unsigned sampling_internal; + unsigned counter_value0; + unsigned counter_value1; + unsigned rsvd8; + unsigned rsvd9; + unsigned current_temp; + unsigned rsvd10; + unsigned rsvd11; + unsigned rsvd12; + unsigned threshold_temp_rise; + unsigned threshold_temp_fall; + unsigned rsvd13; + unsigned rsvd14; + unsigned past_temp3_0; + unsigned past_temp7_4; + unsigned past_temp11_8; + unsigned past_temp15_12; + unsigned inten; + unsigned intstat; + unsigned intclear; + unsigned rsvd15; + unsigned emul_con; +}; + +enum tmu_status_t { + TMU_STATUS_INIT = 0, + TMU_STATUS_NORMAL, + TMU_STATUS_WARNING, + TMU_STATUS_TRIPPED, +}; + +/* Tmeperature threshold values for various thermal events */ +struct temperature_params { + /* minimum value in temperature code range */ + unsigned int min_val; + /* maximum value in temperature code range */ + unsigned int max_val; + /* temperature threshold to start warning */ + unsigned int start_warning; + /* temperature threshold CPU tripping */ + unsigned int start_tripping; + /* temperature threshold for HW tripping */ + unsigned int hardware_tripping; +}; + +/* Pre-defined values and thresholds for calibration of current temperature */ +struct tmu_data { + /* pre-defined temperature thresholds */ + struct temperature_params ts; + /* pre-defined efuse range minimum value */ + unsigned int efuse_min_value; + /* pre-defined efuse value for temperature calibration */ + unsigned int efuse_value; + /* pre-defined efuse range maximum value */ + unsigned int efuse_max_value; + /* current temperature sensing slope */ + unsigned int slope; +}; + +/* TMU device specific details and status */ +struct tmu_info { + /* base Address for the TMU */ + unsigned tmu_base; + /* mux Address for the TMU */ + int tmu_mux; + /* pre-defined values for calibration and thresholds */ + struct tmu_data data; + /* value required for triminfo_25 calibration */ + unsigned int te1; + /* value required for triminfo_85 calibration */ + unsigned int te2; + /* TMU DC value for threshold calculation */ + int dc_value; + /* enum value indicating status of the TMU */ + int tmu_state; +}; + +extern struct tmu_info *tmu_info; + +/* + * Monitors status of the TMU device and exynos temperature + * + * @info pointer to TMU info struct + * @temp pointer to the current temperature value + * @return enum tmu_status_t value, code indicating event to execute + * and -1 on error + */ +enum tmu_status_t tmu_monitor(struct tmu_info *info, int *temp); + +/* + * Initialize TMU device + * + * @info pointer to TMU info struct + * @return int value, 0 for success + */ +int tmu_init(struct tmu_info *info); + +#endif /* CPU_SAMSUNG_EXYNOS5420_TMU_H */ diff --git a/src/soc/samsung/exynos5420/trustzone.c b/src/soc/samsung/exynos5420/trustzone.c new file mode 100644 index 0000000000..7b1489a1a6 --- /dev/null +++ b/src/soc/samsung/exynos5420/trustzone.c @@ -0,0 +1,42 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2013 Google Inc. + * Copyright (C) 2012 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <arch/io.h> +#include "trustzone.h" + +/* Setting TZPC[TrustZone Protection Controller] + * We pretty much disable it all, as the kernel + * expects it that way -- and that's not the default. + */ +void trustzone_init(void) +{ + struct exynos_tzpc *tzpc; + unsigned int addr; + + for (addr = TZPC10_BASE; addr <= TZPC9_BASE; addr += TZPC_BASE_OFFSET) { + tzpc = (struct exynos_tzpc *)addr; + if (addr == TZPC0_BASE) + writel(R0SIZE, &tzpc->r0size); + writel(DECPROTXSET, &tzpc->decprot0set); + writel(DECPROTXSET, &tzpc->decprot1set); + writel(DECPROTXSET, &tzpc->decprot2set); + writel(DECPROTXSET, &tzpc->decprot3set); + } +} diff --git a/src/soc/samsung/exynos5420/trustzone.h b/src/soc/samsung/exynos5420/trustzone.h new file mode 100644 index 0000000000..69e683aeba --- /dev/null +++ b/src/soc/samsung/exynos5420/trustzone.h @@ -0,0 +1,81 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2013 Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef CPU_SAMSUNG_EXYNOS5420_TRUSTZONE_H +#define CPU_SAMSUNG_EXYNOS5420_TRUSTZONE_H + +#include <stdint.h> + +/* Distance between each Trust Zone PC register set */ +#define TZPC_BASE_OFFSET 0x10000 +/* TZPC : Register Offsets */ +#define TZPC0_BASE 0x10100000 +#define TZPC1_BASE 0x10110000 +#define TZPC2_BASE 0x10120000 +#define TZPC3_BASE 0x10130000 +#define TZPC4_BASE 0x10140000 +#define TZPC5_BASE 0x10150000 +#define TZPC6_BASE 0x10160000 +#define TZPC7_BASE 0x10170000 +#define TZPC8_BASE 0x10180000 +#define TZPC9_BASE 0x10190000 +#define TZPC10_BASE 0x100E0000 +#define TZPC11_BASE 0x100F0000 + +/* + * TZPC Register Value : + * R0SIZE: 0x0 : Size of secured ram + */ +#define R0SIZE 0x0 + +/* + * TZPC Decode Protection Register Value : + * DECPROTXSET: 0xFF : Set Decode region to non-secure + */ +#define DECPROTXSET 0xFF + +struct exynos_tzpc { + u32 r0size; + u8 res1[0x7FC]; + u32 decprot0stat; + u32 decprot0set; + u32 decprot0clr; + u32 decprot1stat; + u32 decprot1set; + u32 decprot1clr; + u32 decprot2stat; + u32 decprot2set; + u32 decprot2clr; + u32 decprot3stat; + u32 decprot3set; + u32 decprot3clr; + u8 res2[0x7B0]; + u32 periphid0; + u32 periphid1; + u32 periphid2; + u32 periphid3; + u32 pcellid0; + u32 pcellid1; + u32 pcellid2; + u32 pcellid3; +}; + +void trustzone_init(void); + +#endif /* CPU_SAMSUNG_EXYNOS5420_TRUSTZONE_H */ diff --git a/src/soc/samsung/exynos5420/uart.c b/src/soc/samsung/exynos5420/uart.c new file mode 100644 index 0000000000..be96d5b86c --- /dev/null +++ b/src/soc/samsung/exynos5420/uart.c @@ -0,0 +1,194 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2009 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <types.h> +#include <console/uart.h> +#include <arch/io.h> +#include <boot/coreboot_tables.h> +#include "uart.h" +#include "clk.h" +#include "cpu.h" +#include "periph.h" +#include "uart.h" + +#define RX_FIFO_COUNT_MASK 0xff +#define RX_FIFO_FULL_MASK (1 << 8) +#define TX_FIFO_FULL_MASK (1 << 24) + + +/* + * 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(struct s5p_uart *uart) +{ + u32 uclk; + u32 val; + + // All UARTs share the same clock. + uclk = clock_get_periph_rate(PERIPH_ID_UART3); + val = uclk / default_baudrate(); + + writel(val / 16 - 1, &uart->ubrdiv); + + /* + * FIXME(dhendrix): the original uart.h had a "br_rest" value which + * does not seem relevant to the exynos5420... not entirely sure + * where/if we need to worry about it here + */ +#if 0 + if (s5p_uart_divslot()) + writel(udivslot[val % 16], &uart->rest.slot); + else + writeb(val % 16, &uart->rest.value); +#endif +} + +/* + * Initialise the serial port with the given baudrate. The settings + * are always 8 data bits, no parity, 1 stop bit, no start bits. + */ +static void exynos5_init_dev(struct s5p_uart *uart) +{ + /* 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(uart); +} + +static int exynos5_uart_err_check(struct s5p_uart *uart, int op) +{ + 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(struct s5p_uart *uart) +{ + /* wait for character to arrive */ + while (!(readl(&uart->ufstat) & (RX_FIFO_COUNT_MASK | + RX_FIFO_FULL_MASK))) { + if (exynos5_uart_err_check(uart, 0)) + return 0; + } + + return readb(&uart->urxh) & 0xff; +} + +/* + * Output a single byte to the serial port. + */ +static void exynos5_uart_tx_byte(struct s5p_uart *uart, unsigned char data) +{ + /* wait for room in the tx FIFO */ + while ((readl(&uart->ufstat) & TX_FIFO_FULL_MASK)) { + if (exynos5_uart_err_check(uart, 1)) + return; + } + + writeb(data, &uart->utxh); +} + +unsigned int uart_platform_base(int idx) +{ + if (idx < 4) + return 0x12c00000 + idx * 0x10000; + else + return 0; +} + +void uart_init(int idx) +{ + struct s5p_uart *uart = uart_platform_baseptr(idx); + exynos5_init_dev(uart); +} + +unsigned char uart_rx_byte(int idx) +{ + struct s5p_uart *uart = uart_platform_baseptr(idx); + return exynos5_uart_rx_byte(uart); +} + +void uart_tx_byte(int idx, unsigned char data) +{ + struct s5p_uart *uart = uart_platform_baseptr(idx); + exynos5_uart_tx_byte(uart, data); +} + +void uart_tx_flush(int idx) +{ + /* Exynos5250 implements this too. */ +} + +#ifndef __PRE_RAM__ +void uart_fill_lb(void *data) +{ + struct lb_serial serial; + serial.type = LB_SERIAL_TYPE_MEMORY_MAPPED; + serial.baseaddr = uart_platform_base(CONFIG_UART_FOR_CONSOLE); + serial.baud = default_baudrate(); + lb_add_serial(&serial, data); + + lb_add_console(LB_TAG_CONSOLE_SERIAL8250MEM, data); +} +#endif diff --git a/src/soc/samsung/exynos5420/uart.h b/src/soc/samsung/exynos5420/uart.h new file mode 100644 index 0000000000..72a5789b6d --- /dev/null +++ b/src/soc/samsung/exynos5420/uart.h @@ -0,0 +1,48 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2012 Google Inc. + * Copyright (C) 2009 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef CPU_SAMSUNG_EXYNOS5420_UART_H +#define CPU_SAMSUNG_EXYNOS5420_UART_H + +/* baudrate rest value */ +union br_rest { + unsigned short slot; /* udivslot */ + unsigned char value; /* ufracval */ +}; + +struct s5p_uart { + unsigned int ulcon; + unsigned int ucon; + unsigned int ufcon; + unsigned int umcon; + unsigned int utrstat; + unsigned int uerstat; + unsigned int ufstat; + unsigned int umstat; + unsigned char utxh; + unsigned char res1[3]; + unsigned char urxh; + unsigned char res2[3]; + unsigned int ubrdiv; + union br_rest rest; + unsigned char res3[0xffd0]; +}; + +#endif diff --git a/src/soc/samsung/exynos5420/usb.c b/src/soc/samsung/exynos5420/usb.c new file mode 100644 index 0000000000..84a6f4ca4c --- /dev/null +++ b/src/soc/samsung/exynos5420/usb.c @@ -0,0 +1,217 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2012 Samsung Electronics + * Copyright 2013 Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <delay.h> +#include <arch/io.h> +#include <console/console.h> +#include <device/device.h> +#include "gpio.h" +#include "power.h" +#include "sysreg.h" +#include "usb.h" + +static void reset_dwc3(struct exynos5_usb_drd_dwc3 *dwc3) +{ + setbits_le32(&dwc3->ctl, 0x1 << 11); /* core soft reset */ + setbits_le32(&dwc3->usb3pipectl, 0x1 << 31); /* PHY soft reset */ + setbits_le32(&dwc3->usb2phycfg, 0x1 << 31); /* PHY soft reset */ +} + +void reset_usb_drd0_dwc3() +{ + printk(BIOS_DEBUG, "Starting DWC3 reset for USB DRD0\n"); + reset_dwc3(exynos_usb_drd0_dwc3); +} + +void reset_usb_drd1_dwc3() +{ + printk(BIOS_DEBUG, "Starting DWC3 reset for USB DRD1\n"); + reset_dwc3(exynos_usb_drd1_dwc3); +} + +static void setup_dwc3(struct exynos5_usb_drd_dwc3 *dwc3) +{ + if (!(dwc3->ctl & 0x1 << 11) || + !(dwc3->usb3pipectl & 0x1 << 31) || + !(dwc3->usb2phycfg & 0x1 << 31)) { + printk(BIOS_ERR, "DWC3 at %p not in reset (you need to call " + "reset_usb_drdX_dwc3() first)!\n", dwc3); + } + + /* Set relevant registers to default values (clearing all reset bits) */ + + writel(0x1 << 24 | /* activate PHY low power states */ + 0x4 << 19 | /* low power delay value */ + 0x1 << 18 | /* activate PHY low power delay */ + 0x1 << 17 | /* enable SuperSpeed PHY suspend */ + 0x1 << 1 | /* default Tx deemphasis value */ + 0, &dwc3->usb3pipectl); + + /* Configure PHY clock turnaround for 8-bit UTMI+, disable suspend */ + writel(0x9 << 10 | /* PHY clock turnaround for 8-bit UTMI+ */ + 0x1 << 8 | /* enable PHY sleep in L1 */ + 0x1 << 6 | /* enable PHY suspend */ + 0, &dwc3->usb2phycfg); + + writel(0x5dc << 19 | /* suspend clock scale for 24MHz */ + 0x1 << 16 | /* retry SS three times (bugfix from U-Boot) */ + 0x1 << 12 | /* port capability HOST */ + 0, &dwc3->ctl); +} + +void setup_usb_drd0_dwc3() +{ + setup_dwc3(exynos_usb_drd0_dwc3); + printk(BIOS_DEBUG, "DWC3 setup for USB DRD0 finished\n"); +} + +void setup_usb_drd1_dwc3() +{ + setup_dwc3(exynos_usb_drd1_dwc3); + printk(BIOS_DEBUG, "DWC3 setup for USB DRD1 finished\n"); +} + +static void setup_drd_phy(struct exynos5_usb_drd_phy *phy) +{ + /* Set all PHY registers to default values */ + + /* XHCI Version 1.0, Frame Length adjustment 30 MHz */ + setbits_le32(&phy->linksystem, 0x1 << 27 | 0x20 << 1); + + /* Disable OTG, ID0 and DRVVBUS, do not force sleep/suspend */ + writel(1 << 6, &phy->utmi); + + writel(0x88 << 23 | /* spread spectrum refclk selector */ + 0x1 << 20 | /* enable spread spectrum */ + 0x1 << 19 | /* enable prescaler refclk */ + 0x68 << 11 | /* multiplier for 24MHz refclk */ + 0x5 << 5 | /* select 24MHz refclk (weird, from U-Boot) */ + 0x1 << 4 | /* power supply in normal operating mode */ + 0x3 << 2 | /* use external refclk (undocumented on 5420?)*/ + 0x1 << 1 | /* force port reset */ + 0x1 << 0 | /* normal operating mode */ + 0, &phy->clkrst); + + writel(0x9 << 26 | /* LOS level */ + 0x3 << 22 | /* TX VREF tune */ + 0x1 << 20 | /* TX rise tune */ + 0x1 << 18 | /* TX res tune */ + 0x3 << 13 | /* TX HS X Vtune */ + 0x3 << 9 | /* TX FS/LS tune */ + 0x3 << 6 | /* SQRX tune */ + 0x4 << 3 | /* OTG tune */ + 0x4 << 0 | /* comp disc tune */ + 0, &phy->param0); + + writel(0x7f << 19 | /* reserved */ + 0x7f << 12 | /* Tx launch amplitude */ + 0x20 << 6 | /* Tx deemphasis 6dB */ + 0x1c << 0 | /* Tx deemphasis 3.5dB (value from U-Boot) */ + 0, &phy->param1); + + /* disable all test features */ + writel(0, &phy->test); + + /* UTMI clock select? ("must be 0x1") */ + writel(0x1 << 2, &phy->utmiclksel); + + /* Samsung magic, undocumented (from U-Boot) */ + writel(0x0, &phy->resume); + + udelay(10); + clrbits_le32(&phy->clkrst, 0x1 << 1); /* deassert port reset */ +} + +void setup_usb_drd0_phy() +{ + printk(BIOS_DEBUG, "Powering up USB DRD0 PHY\n"); + setbits_le32(&exynos_power->usb_drd0_phy_ctrl, POWER_USB_PHY_CTRL_EN); + setup_drd_phy(exynos_usb_drd0_phy); +} + +void setup_usb_drd1_phy() +{ + printk(BIOS_DEBUG, "Powering up USB DRD1 PHY\n"); + setbits_le32(&exynos_power->usb_drd1_phy_ctrl, POWER_USB_PHY_CTRL_EN); + setup_drd_phy(exynos_usb_drd1_phy); +} + +void setup_usb_host_phy(int hsic_gpio) +{ + unsigned int hostphy_ctrl0; + + setbits_le32(&exynos_sysreg->usb20_phy_cfg, USB20_PHY_CFG_EN); + setbits_le32(&exynos_power->usb_host_phy_ctrl, POWER_USB_PHY_CTRL_EN); + + printk(BIOS_DEBUG, "Powering up USB HOST PHY (%s HSIC)\n", + hsic_gpio ? "with" : "without"); + + hostphy_ctrl0 = readl(&exynos_usb_host_phy->usbphyctrl0); + hostphy_ctrl0 &= ~(HOST_CTRL0_FSEL_MASK | + HOST_CTRL0_COMMONON_N | + /* HOST Phy setting */ + HOST_CTRL0_PHYSWRST | + HOST_CTRL0_PHYSWRSTALL | + HOST_CTRL0_SIDDQ | + HOST_CTRL0_FORCESUSPEND | + HOST_CTRL0_FORCESLEEP); + hostphy_ctrl0 |= (/* Setting up the ref freq */ + CLK_24MHZ << 16 | + /* HOST Phy setting */ + HOST_CTRL0_LINKSWRST | + HOST_CTRL0_UTMISWRST); + writel(hostphy_ctrl0, &exynos_usb_host_phy->usbphyctrl0); + udelay(10); + clrbits_le32(&exynos_usb_host_phy->usbphyctrl0, + HOST_CTRL0_LINKSWRST | + HOST_CTRL0_UTMISWRST); + udelay(20); + + /* EHCI Ctrl setting */ + setbits_le32(&exynos_usb_host_phy->ehcictrl, + EHCICTRL_ENAINCRXALIGN | + EHCICTRL_ENAINCR4 | + EHCICTRL_ENAINCR8 | + EHCICTRL_ENAINCR16); + + /* HSIC USB Hub initialization. */ + if (hsic_gpio) { + gpio_direction_output(hsic_gpio, 0); + udelay(100); + gpio_direction_output(hsic_gpio, 1); + udelay(5000); + + clrbits_le32(&exynos_usb_host_phy->hsicphyctrl1, + HOST_CTRL0_SIDDQ | + HOST_CTRL0_FORCESLEEP | + HOST_CTRL0_FORCESUSPEND); + setbits_le32(&exynos_usb_host_phy->hsicphyctrl1, + HOST_CTRL0_PHYSWRST); + udelay(10); + clrbits_le32(&exynos_usb_host_phy->hsicphyctrl1, + HOST_CTRL0_PHYSWRST); + } + + /* At this point we need to wait for 50ms before talking to + * the USB controller (PHY clock and power setup time) + * By the time we are actually in the payload, these 50ms + * will have passed. + */ +} diff --git a/src/soc/samsung/exynos5420/usb.h b/src/soc/samsung/exynos5420/usb.h new file mode 100644 index 0000000000..b3c2a33fd6 --- /dev/null +++ b/src/soc/samsung/exynos5420/usb.h @@ -0,0 +1,144 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2012 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef CPU_SAMSUNG_EXYNOS5420_USB_H +#define CPU_SAMSUNG_EXYNOS5420_USB_H + +#include "cpu.h" + +#define CLK_24MHZ 5 + +#define HOST_CTRL0_PHYSWRSTALL (1 << 31) +#define HOST_CTRL0_COMMONON_N (1 << 9) +#define HOST_CTRL0_SIDDQ (1 << 6) +#define HOST_CTRL0_FORCESLEEP (1 << 5) +#define HOST_CTRL0_FORCESUSPEND (1 << 4) +#define HOST_CTRL0_WORDINTERFACE (1 << 3) +#define HOST_CTRL0_UTMISWRST (1 << 2) +#define HOST_CTRL0_LINKSWRST (1 << 1) +#define HOST_CTRL0_PHYSWRST (1 << 0) + +#define HOST_CTRL0_FSEL_MASK (7 << 16) + +#define EHCICTRL_ENAINCRXALIGN (1 << 29) +#define EHCICTRL_ENAINCR4 (1 << 28) +#define EHCICTRL_ENAINCR8 (1 << 27) +#define EHCICTRL_ENAINCR16 (1 << 26) + +/* Register map for PHY control */ +struct exynos5_usb_host_phy { + uint32_t usbphyctrl0; + uint32_t usbphytune0; + uint8_t reserved1[8]; + uint32_t hsicphyctrl1; + uint32_t hsicphytune1; + uint8_t reserved2[8]; + uint32_t hsicphyctrl2; + uint32_t hsicphytune2; + uint8_t reserved3[8]; + uint32_t ehcictrl; + uint32_t ohcictrl; + uint32_t usbotgsys; + uint8_t reserved4[4]; + uint32_t usbotgtune; +}; + +static struct exynos5_usb_host_phy * const exynos_usb_host_phy = + (void *)EXYNOS5_USB_HOST_PHY_BASE; + +struct exynos5_usb_drd_phy { + uint8_t reserved1[4]; + uint32_t linksystem; + uint32_t utmi; + uint32_t pipe; + uint32_t clkrst; + uint32_t reg0; + uint32_t reg1; + uint32_t param0; + uint32_t param1; + uint32_t term; + uint32_t test; + uint32_t adp; + uint32_t utmiclksel; + uint32_t resume; + uint8_t reserved2[8]; + uint32_t linkhcbelt; + uint32_t linkport; +}; + +static struct exynos5_usb_drd_phy * const exynos_usb_drd0_phy = + (void *)EXYNOS5420_USB_DRD0_PHY_BASE; +static struct exynos5_usb_drd_phy * const exynos_usb_drd1_phy = + (void *)EXYNOS5420_USB_DRD1_PHY_BASE; + +struct exynos5_usb_drd_dwc3 { + uint32_t sbuscfg0; + uint32_t sbuscfg1; + uint32_t txthrcfg; + uint32_t rxthrcfg; + uint32_t ctl; + uint32_t evten; + uint32_t sts; + uint8_t reserved0[4]; + uint32_t snpsid; + uint32_t gpio; + uint32_t uid; + uint32_t uctl; + uint64_t buserraddr; + uint64_t prtbimap; + uint8_t reserved1[32]; + uint32_t dbgfifospace; + uint32_t dbgltssm; + uint32_t dbglnmcc; + uint32_t dbgbmu; + uint32_t dbglspmux; + uint32_t dbglsp; + uint32_t dbgepinfo0; + uint32_t dbgepinfo1; + uint64_t prtbimap_hs; + uint64_t prtbimap_fs; + uint8_t reserved2[112]; + uint32_t usb2phycfg; + uint8_t reserved3[60]; + uint32_t usb2i2cctl; + uint8_t reserved4[60]; + uint32_t usb2phyacc; + uint8_t reserved5[60]; + uint32_t usb3pipectl; + uint8_t reserved6[60]; +}; + +static struct exynos5_usb_drd_dwc3 * const exynos_usb_drd0_dwc3 = + (void *)EXYNOS5420_USB_DRD0_DWC3_BASE; +static struct exynos5_usb_drd_dwc3 * const exynos_usb_drd1_dwc3 = + (void *)EXYNOS5420_USB_DRD1_DWC3_BASE; + +/* Leave hsic_gpio at 0 to not enable HSIC. */ +void setup_usb_host_phy(int hsic_gpio); + +void setup_usb_drd0_phy(void); +void setup_usb_drd1_phy(void); + +/* Call reset_ before setup_, ensure at least 100ms pass in between. */ +void reset_usb_drd0_dwc3(void); +void reset_usb_drd1_dwc3(void); +void setup_usb_drd0_dwc3(void); +void setup_usb_drd1_dwc3(void); + +#endif diff --git a/src/soc/samsung/exynos5420/wakeup.c b/src/soc/samsung/exynos5420/wakeup.c new file mode 100644 index 0000000000..753afd9591 --- /dev/null +++ b/src/soc/samsung/exynos5420/wakeup.c @@ -0,0 +1,58 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2013 Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <arch/cache.h> +#include <console/console.h> +#include "power.h" +#include "wakeup.h" + +void wakeup(void) +{ + if (wakeup_need_reset()) + power_reset(); + + power_init(); /* Ensure ps_hold_setup() for early wakeup. */ + dcache_mmu_disable(); + icache_invalidate_all(); + power_exit_wakeup(); + /* Should never return. If we do, reset. */ + power_reset(); +} + +int get_wakeup_state(void) +{ + uint32_t status = power_read_reset_status(); + + /* DIDLE/LPA can be resumed without clock reset (ex, bootblock), + * and SLEEP requires resetting clock (should be done in ROM stage). + */ + + if (status == S5P_CHECK_DIDLE || status == S5P_CHECK_LPA) + return WAKEUP_DIRECT; + + if (status == S5P_CHECK_SLEEP) + return WAKEUP_NEED_CLOCK_RESET; + + return IS_NOT_WAKEUP; +} + +void wakeup_enable_uart(void) +{ + power_release_uart_retention(); +} diff --git a/src/soc/samsung/exynos5420/wakeup.h b/src/soc/samsung/exynos5420/wakeup.h new file mode 100644 index 0000000000..27ce8e2f2d --- /dev/null +++ b/src/soc/samsung/exynos5420/wakeup.h @@ -0,0 +1,43 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2013 Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef CPU_SAMSUNG_EXYNOS5420_WAKEUP_H +#define CPU_SAMSUNG_EXYNOS5420_WAKEUP_H + +/* Power Down Modes */ +#define S5P_CHECK_SLEEP 0x00000BAD +#define S5P_CHECK_DIDLE 0xBAD00000 +#define S5P_CHECK_LPA 0xABAD0000 + +enum { + // A normal boot (not suspend/resume) + IS_NOT_WAKEUP, + // A wake up event that can be resumed any time + WAKEUP_DIRECT, + // A wake up event that must be resumed only after + // clock and memory controllers are re-initialized + WAKEUP_NEED_CLOCK_RESET, +}; + +int wakeup_need_reset(void); +int get_wakeup_state(void); +void wakeup(void); +void wakeup_enable_uart(void); + +#endif /* CPU_SAMSUNG_EXYNOS5420_WAKEUP_H */ |