blob: d5ee0c03641a3d2e859b0e8781f37378188f7df0 [file] [log] [blame]
/*
* Copyright (C) 2012 Samsung Electronics
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <common.h>
#include <fdtdec.h>
#include <i2c.h>
#include <max77686.h>
#include <mkbp.h>
#include <mmc.h>
#include <netdev.h>
#include <tps65090.h>
#include <errno.h>
#include <asm/gpio.h>
#include <asm/arch/cpu.h>
#include <asm/arch/ehci-s5p.h>
#include <asm/arch/board.h>
#include <asm/arch/gpio.h>
#include <asm/arch/mmc.h>
#include <asm/arch/mshc.h>
#include <asm/arch/pinmux.h>
#include <asm/arch/sromc.h>
#include <asm/arch-exynos5/power.h>
#include <asm/arch/sata.h>
#include <asm/arch/exynos-tmu.h>
#include <asm/arch/exynos-cpufreq.h>
#include <asm/arch/s5p-dp.h>
#include "board.h"
#ifndef CONFIG_EXYNOS_DISPLAYPORT
#error "CONFIG_EXYNOS_DISPLAYPORT must be defined for smdk5250"
#endif
DECLARE_GLOBAL_DATA_PTR;
struct local_info {
struct mkbp_dev *mkbp_dev; /* Pointer to mkbp device */
int arbitrate_node;
struct fdt_gpio_state ap_claim;
struct fdt_gpio_state ec_claim;
/* DisplayPort gpios */
struct fdt_gpio_state dp_pd;
struct fdt_gpio_state dp_rst;
struct fdt_gpio_state dp_hpd;
/* Time between requesting bus and deciding that we have it */
unsigned slew_delay_us;
/* Time between retrying to see if the AP has released the bus */
unsigned wait_retry_us;
/* Time to wait until the bus becomes free */
unsigned wait_free_ms;
};
static struct local_info local;
static uint32_t cpufreq_loop_count;
/*
* Called to do the needful when tstc has a character ready
* Meant to work in contrast to board_poll_devices
*/
void board_tstc_ready(void)
{
#ifdef CONFIG_EXYNOS_CPUFREQ
if (cpufreq_loop_count >= 10000000) {
/* Character received, increase ARM frequency */
exynos5250_set_frequency(CPU_FREQ_L1700);
}
cpufreq_loop_count = 0;
#endif /* CONFIG_EXYNOS_CPUFREQ */
}
/*
* Polling various devices on board for details and status monitoring purposes
*/
void board_poll_devices(void)
{
#if defined CONFIG_EXYNOS_TMU
int temp;
switch (tmu_monitor(&temp)) {
case TMU_STATUS_TRIPPED:
puts("EXYNOS_TMU: TRIPPING! Device power going down ...\n");
power_shutdown();
break;
case TMU_STATUS_WARNING:
puts("EXYNOS_TMU: WARNING! Temperature very high\n");
break;
case TMU_STATUS_INIT:
case TMU_STATUS_NORMAL:
break;
default:
debug("Unknown TMU state\n");
}
#endif /* CONFIG_EXYNOS_TMU */
#ifdef CONFIG_EXYNOS_CPUFREQ
cpufreq_loop_count++;
if (cpufreq_loop_count == 10000000) {
/* User is idle, decrease ARM frequency*/
exynos5250_set_frequency(CPU_FREQ_L200);
}
#endif /* CONFIG_EXYNOS_CPUFREQ */
}
#ifdef CONFIG_OF_CONTROL
static int decode_sromc(const void *blob, struct fdt_sromc *config)
{
int err;
int node;
node = fdtdec_next_compatible(blob, 0, COMPAT_SAMSUNG_EXYNOS5_SROMC);
if (node < 0) {
debug("Could not find SROMC node\n");
return node;
}
config->bank = fdtdec_get_int(blob, node, "bank", 0);
config->width = fdtdec_get_int(blob, node, "width", 2);
err = fdtdec_get_int_array(blob, node, "srom-timing", config->timing,
FDT_SROM_TIMING_COUNT);
if (err < 0) {
debug("Could not decode SROMC configuration\n");
return -FDT_ERR_NOTFOUND;
}
return 0;
}
#endif
uint32_t exynos5_read_and_clear_spl_marker(void)
{
uint32_t value, *marker = (uint32_t *)CONFIG_IRAM_STACK;
/* Stack grows toward lower address of memory */
value = marker[-1];
marker[-1] = 0;
return value;
}
int board_eth_init(bd_t *bis)
{
#ifdef CONFIG_SMC911X
u32 smc_bw_conf, smc_bc_conf;
struct fdt_sromc config;
fdt_addr_t base_addr;
int node;
#ifdef CONFIG_OF_CONTROL
node = decode_sromc(gd->fdt_blob, &config);
if (node < 0)
return -1;
node = fdtdec_next_compatible(gd->fdt_blob, node, COMPAT_SMSC_LAN9215);
if (node < 0) {
debug("%s: Could not find lan9215 configuration\n", __func__);
return -1;
}
base_addr = fdtdec_get_addr(gd->fdt_blob, node, "reg");
if (base_addr == FDT_ADDR_T_NONE) {
debug("%s: Could not find lan9215 address\n", __func__);
return -1;
}
#else
/* Non-FDT configuration - bank number and timing parameters*/
config.bank = CONFIG_ENV_SROM_BANK;
config.width = 2;
config.timing[FDT_SROM_TACS] = 0x01;
config.timing[FDT_SROM_TCOS] = 0x01;
config.timing[FDT_SROM_TACC] = 0x06;
config.timing[FDT_SROM_TCOH] = 0x01;
config.timing[FDT_SROM_TAH] = 0x0C;
config.timing[FDT_SROM_TACP] = 0x09;
config.timing[FDT_SROM_PMC] = 0x01;
base_addr = CONFIG_SMC911X_BASE;
#endif
/* Ethernet needs data bus width of 16 bits */
if (config.width != 2) {
debug("%s: Unsupported bus width %d\n", __func__,
config.width);
return -1;
}
smc_bw_conf = SROMC_DATA16_WIDTH(config.bank)
| SROMC_BYTE_ENABLE(config.bank);
smc_bc_conf = SROMC_BC_TACS(config.timing[FDT_SROM_TACS]) |\
SROMC_BC_TCOS(config.timing[FDT_SROM_TCOS]) |\
SROMC_BC_TACC(config.timing[FDT_SROM_TACC]) |\
SROMC_BC_TCOH(config.timing[FDT_SROM_TCOH]) |\
SROMC_BC_TAH(config.timing[FDT_SROM_TAH]) |\
SROMC_BC_TACP(config.timing[FDT_SROM_TACP]) |\
SROMC_BC_PMC(config.timing[FDT_SROM_PMC]);
/* Select and configure the SROMC bank */
exynos_pinmux_config(PERIPH_ID_SROMC, config.bank | PINMUX_FLAG_16BIT);
s5p_config_sromc(config.bank, smc_bw_conf, smc_bc_conf);
return smc911x_initialize(0, base_addr);
#endif
return 0;
}
int fdtdec_decode_memory(const void *blob, struct fdt_memory *config)
{
int node, len;
const fdt_addr_t *cell;
node = fdt_path_offset(blob, "/memory");
if (node < 0) {
debug("Could not find the path /memory\n");
return node;
}
cell = fdt_getprop(blob, node, "reg", &len);
if (cell && len == sizeof(fdt_addr_t) * 2) {
config->start = fdt_addr_to_cpu(cell[0]);
config->end = fdt_addr_to_cpu(cell[1]);
} else {
return -FDT_ERR_BADLAYOUT;
}
return 0;
}
int board_usb_vbus_init(void)
{
/* Enable VBUS power switch */
gpio_direction_output(GPIO_X11, 1);
/* VBUS turn ON time */
mdelay(3);
return 0;
}
struct mkbp_dev *board_get_mkbp_dev(void)
{
return local.mkbp_dev;
}
static int board_init_mkbp_devices(const void *blob)
{
local.mkbp_dev = mkbp_init(blob);
if (!local.mkbp_dev) {
debug("%s: cannot init mkbp device\n", __func__);
return -1;
}
return 0;
}
static int board_i2c_arb_init(const void *blob)
{
int node;
local.arbitrate_node = -1;
node = fdtdec_next_compatible(blob, 0, COMPAT_GOOGLE_ARBITRATOR);
if (node < 0) {
debug("Cannot find bus arbitrator node\n");
return 0;
}
if (fdtdec_decode_gpio(blob, node, "google,ap-claim-gpios",
&local.ap_claim) ||
fdtdec_decode_gpio(blob, node, "google,ec-claim-gpios",
&local.ec_claim)) {
debug("Cannot find bus arbitrator GPIOs\n");
return 0;
}
if (fdtdec_setup_gpio(&local.ap_claim) ||
fdtdec_setup_gpio(&local.ec_claim)) {
debug("Cannot claim arbitration GPIOs\n");
return -1;
}
/* We are currently not claiming the bus */
gpio_direction_output(local.ap_claim.gpio, 1);
gpio_direction_input(local.ec_claim.gpio);
gpio_set_pull(local.ec_claim.gpio, EXYNOS_GPIO_PULL_UP);
local.arbitrate_node = fdtdec_lookup_phandle(blob, node,
"google,arbitrate-bus");
if (local.arbitrate_node < 0) {
debug("Cannot find bus to arbitrate\n");
return -1;
}
local.slew_delay_us = fdtdec_get_int(blob, node,
"google,slew-delay-us", 10);
local.wait_retry_us = fdtdec_get_int(blob, node,
"google,wait-retry-us", 2000);
local.wait_free_ms = fdtdec_get_int(blob, node,
"google,wait-free-us", 50000);
local.wait_free_ms = DIV_ROUND_UP(local.wait_free_ms, 1000);
debug("Bus arbitration ready on fdt node %d\n", local.arbitrate_node);
return 0;
}
/**
* Fix-up the kernel device tree so the bridge pd_n and rst_n gpios accurately
* reflect the current board rev.
*/
void ft_board_setup(void *blob, bd_t *bd)
{
int ret, rev, np, len;
const struct fdt_property *prop;
/* Do nothing for newer boards */
rev = board_get_revision();
if (rev < 4 || rev == 6)
return;
/*
* If this is an older board, replace powerdown-gpio contents with that
* of reset-gpio and delete reset-gpio from the dt.
*/
np = fdtdec_next_compatible(blob, 0, COMPAT_NXP_PTN3460);
if (np < 0) {
debug("%s: Could not find COMPAT_NXP_PTN3460\n", __func__);
return;
}
prop = fdt_get_property(blob, np, "reset-gpio", &len);
if (!prop) {
debug("%s: Could not get property err=%d\n", __func__, len);
return;
}
ret = fdt_setprop_inplace(blob, np, "powerdown-gpio", prop->data,
len);
if (ret) {
debug("%s: Could not setprop inplace err=%d\n", __func__, ret);
return;
}
ret = fdt_delprop(blob, np, "reset-gpio");
if (ret) {
debug("%s: Could not delprop err=%d\n", __func__, ret);
return;
}
}
#ifdef CONFIG_TPS65090_POWER
int board_dp_lcd_vdd(const void *blob, unsigned *wait_ms)
{
*wait_ms = 0;
return tps65090_fet_enable(6); /* Enable FET6, lcd panel */
}
#endif
static int board_dp_fill_gpios(const void *blob)
{
int np, ret, rev;
np = fdtdec_next_compatible(blob, 0, COMPAT_NXP_PTN3460);
if (np < 0) {
debug("%s: Could not find COMPAT_NXP_PTN3460 (%d)\n", __func__,
ret);
return np;
}
ret = fdtdec_decode_gpio(blob, np, "powerdown-gpio", &local.dp_pd);
if (ret) {
debug("%s: Could not decode powerdown-gpio (%d)\n", __func__,
ret);
return ret;
}
ret = fdtdec_decode_gpio(blob, np, "reset-gpio", &local.dp_rst);
if (ret) {
debug("%s: Could not decode reset-gpio (%d)\n", __func__, ret);
return ret;
}
ret = fdtdec_decode_gpio(blob, np, "hotplug-gpio", &local.dp_hpd);
if (ret) {
debug("%s: Could not decode hotplug (%d)\n", __func__, ret);
return ret;
}
/* If board is older, replace pd gpio with rst gpio */
rev = board_get_revision();
if (rev >= 4 && rev != 6) {
local.dp_pd = local.dp_rst;
local.dp_rst.gpio = FDT_GPIO_NONE;
}
return 0;
}
int board_dp_bridge_setup(const void *blob, unsigned *wait_ms)
{
int ret;
ret = board_dp_fill_gpios(blob);
if (ret)
return ret;
/* Mux HPHPD to the special hotplug detect mode */
exynos_pinmux_config(PERIPH_ID_DPHPD, 0);
/* Setup the GPIOs */
ret = fdtdec_setup_gpio(&local.dp_pd);
if (ret) {
debug("%s: Could not setup pd gpio (%d)\n", __func__, ret);
return ret;
}
ret = fdtdec_setup_gpio(&local.dp_rst);
if (ret) {
debug("%s: Could not setup rst gpio (%d)\n", __func__, ret);
return ret;
}
ret = fdtdec_setup_gpio(&local.dp_hpd);
if (ret) {
debug("%s: Could not setup hpd gpio (%d)\n", __func__, ret);
return ret;
}
fdtdec_set_gpio(&local.dp_pd, 0);
gpio_cfg_pin(local.dp_pd.gpio, EXYNOS_GPIO_OUTPUT);
gpio_set_pull(local.dp_pd.gpio, EXYNOS_GPIO_PULL_NONE);
if (fdt_gpio_isvalid(&local.dp_rst)) {
fdtdec_set_gpio(&local.dp_rst, 1);
gpio_cfg_pin(local.dp_rst.gpio, EXYNOS_GPIO_OUTPUT);
gpio_set_pull(local.dp_rst.gpio, EXYNOS_GPIO_PULL_NONE);
udelay(10);
fdtdec_set_gpio(&local.dp_rst, 0);
}
*wait_ms = 0;
return 0;
}
int board_dp_bridge_init(const void *blob, unsigned *wait_ms)
{
/* De-assert PD (and possibly RST) to power up the bridge */
fdtdec_set_gpio(&local.dp_pd, 0);
/* Ignore the return value here, on some boards this is NC */
fdtdec_set_gpio(&local.dp_rst, 0);
/*
* We need to wait for 90ms after bringing up the bridge since there
* is a phantom "high" on the HPD chip during its bootup. The phantom
* high comes within 7ms of de-asserting PD and persists for at least
* 15ms. The real high comes roughly 50ms after PD is de-asserted. The
* phantom high makes it hard for us to know when the NXP chip is up.
*/
*wait_ms = 90;
return 0;
}
int board_dp_bridge_reset(const void *blob, unsigned *wait_ms)
{
debug("%s: eDP bridge failed to come up\n", __func__);
/*
* If we're here, the bridge chip failed to initialize.
* Drive DP_N low in an attempt to reset.
*/
fdtdec_set_gpio(&local.dp_pd, 1);
/* Ignore the return value here, on some boards this is NC */
fdtdec_set_gpio(&local.dp_rst, 1);
/*
* Arbitrarily wait 300ms here with DP_N low. Don't know for
* sure how long we should wait, but we're being paranoid.
*/
*wait_ms = 300;
return 0;
}
int board_dp_hotplug(const void *blob, unsigned *wait_ms)
{
const int MAX_TRIES = 10;
static int num_tries;
/* Check HPD. If it's high, we're all good. */
if (fdtdec_get_gpio(&local.dp_hpd)) {
*wait_ms = 0;
return 0;
}
debug("%s: eDP bridge failed to come up; try %d of %d\n", __func__,
num_tries, MAX_TRIES);
/* Immediately go into bridge reset if the hp line is not high */
*wait_ms = 0;
++num_tries;
return num_tries <= MAX_TRIES ? -EAGAIN : -ENODEV;
}
#ifdef CONFIG_TPS65090_POWER
int board_dp_backlight_vdd(const void *blob, unsigned *wait_ms)
{
/* This delay is T5 in the LCD timing spec (defined as > 10ms) */
*wait_ms = 10;
return tps65090_fet_enable(1); /* Enable FET1, backlight */
}
#endif
int board_dp_backlight_pwm(const void *blob, unsigned *wait_ms)
{
/*
* Configure backlight PWM as a simple output high (100% brightness)
* TODO(hatim.rv@samsung.com): Move to FDT
*/
gpio_direction_output(GPIO_B20, 1);
/* This delay is T6 in the LCD timing spec (defined as > 10ms) */
*wait_ms = 10;
return 0;
}
int board_dp_backlight_en(const void *blob, unsigned *wait_ms)
{
/*
* Configure GPIO for LCD_BL_EN
* TODO(hatim.rv@samsung.com): Move to FDT
*/
gpio_direction_output(GPIO_X30, 1);
/* We're done, no more delays! */
*wait_ms = 0;
return 0;
}
int board_init(void)
{
struct fdt_memory mem_config;
if (fdtdec_decode_memory(gd->fdt_blob, &mem_config)) {
debug("%s: Failed to decode memory\n", __func__);
return -1;
}
gd->bd->bi_boot_params = mem_config.start + 0x100UL;
#ifdef CONFIG_OF_CONTROL
gd->bd->bi_arch_number = fdtdec_get_config_int(gd->fdt_blob,
"machine-arch-id", -1);
if (gd->bd->bi_arch_number == -1U)
debug("Warning: No /config/machine-arch-id defined in fdt\n");
#endif
#ifdef CONFIG_EXYNOS_SPI
spi_init();
#endif
if (board_i2c_arb_init(gd->fdt_blob))
return -1;
board_i2c_init(gd->fdt_blob);
#ifdef CONFIG_TPS65090_POWER
tps65090_init();
#endif
exynos_lcd_check_next_stage(gd->fdt_blob, 0);
if (max77686_enable_32khz_cp()) {
debug("%s: Failed to enable max77686 32khz coprocessor clock\n",
__func__);
return -1;
}
#if defined CONFIG_EXYNOS_CPUFREQ
if (exynos5250_cpufreq_init(gd->fdt_blob)) {
debug("%s: Failed to init CPU frequency scaling\n", __func__);
return -1;
}
#endif
#if defined CONFIG_EXYNOS_TMU
if (tmu_init(gd->fdt_blob)) {
debug("%s: Failed to init TMU\n", __func__);
return -1;
}
#endif
if (board_init_mkbp_devices(gd->fdt_blob))
return -1;
exynos_lcd_check_next_stage(gd->fdt_blob, 0);
return 0;
}
int dram_init(void)
{
struct fdt_memory mem_config;
if (fdtdec_decode_memory(gd->fdt_blob, &mem_config)) {
debug("%s: Failed to decode memory\n", __func__);
return -1;
}
gd->ram_size = get_ram_size((long *)mem_config.start,
mem_config.end);
return 0;
}
void dram_init_banksize(void)
{
struct fdt_memory mem_config;
if (fdtdec_decode_memory(gd->fdt_blob, &mem_config)) {
debug("%s: Failed to decode memory\n", __func__);
return;
}
gd->bd->bi_dram[0].start = mem_config.start;
gd->bd->bi_dram[0].size = get_ram_size((long *)mem_config.start,
mem_config.end);
}
int board_get_revision(void)
{
struct fdt_gpio_state gpios[CONFIG_BOARD_REV_GPIO_COUNT];
unsigned gpio_list[CONFIG_BOARD_REV_GPIO_COUNT];
int board_rev = -1;
int count = 0;
int node;
node = fdtdec_next_compatible(gd->fdt_blob, 0,
COMPAT_GOOGLE_BOARD_REV);
if (node >= 0) {
count = fdtdec_decode_gpios(gd->fdt_blob, node,
"google,board-rev-gpios", gpios,
CONFIG_BOARD_REV_GPIO_COUNT);
}
if (count > 0) {
int i;
for (i = 0; i < count; i++)
gpio_list[i] = gpios[i].gpio;
board_rev = gpio_decode_number(gpio_list, count);
} else {
debug("%s: No board revision information in fdt\n", __func__);
}
return board_rev;
}
#ifdef CONFIG_DISPLAY_BOARDINFO
int checkboard(void)
{
#ifdef CONFIG_OF_CONTROL
const char *board_name;
board_name = fdt_getprop(gd->fdt_blob, 0, "model", NULL);
printf("\nBoard: %s, rev %d\n", board_name ? board_name : "<unknown>",
board_get_revision());
#else
printf("\nBoard: SMDK5250\n");
#endif
return 0;
}
#endif
#ifdef CONFIG_GENERIC_MMC
int board_mmc_getcd(struct mmc *mmc)
{
struct mshci_host *host = mmc->priv;
int present = 1; /* for ch0 (eMMC) card is always present */
if (host->peripheral == PERIPH_ID_SDMMC2)
present = !readl(&host->reg->cdetect);
return present;
}
int board_mmc_init(bd_t *bis)
{
#ifdef CONFIG_S5P_MSHCI
s5p_mshci_init(gd->fdt_blob);
#endif
return 0;
}
#endif
#ifdef CONFIG_BOARD_EARLY_INIT_F
int board_early_init_f(void)
{
exynos_pinmux_config(EXYNOS_UART, PINMUX_FLAG_NONE);
return 0;
}
#endif
void board_i2c_release_bus(int node)
{
/* If this is us, release the bus */
if (node == local.arbitrate_node) {
gpio_set_value(local.ap_claim.gpio, 1);
udelay(local.slew_delay_us);
}
}
int board_i2c_claim_bus(int node)
{
unsigned start;
if (node != local.arbitrate_node)
return 0;
// putc('c');
/* Start a round of trying to claim the bus */
start = get_timer(0);
do {
unsigned start_retry;
int waiting = 0;
/* Indicate that we want to claim the bus */
gpio_set_value(local.ap_claim.gpio, 0);
udelay(local.slew_delay_us);
/* Wait for the EC to release it */
start_retry = get_timer(0);
while (get_timer(start_retry) < local.wait_retry_us) {
if (gpio_get_value(local.ec_claim.gpio)) {
/* We got it, so return */
return 0;
}
if (!waiting) {
waiting = 1;
}
}
/* It didn't release, so give up, wait, and try again */
gpio_set_value(local.ap_claim.gpio, 1);
udelay(local.wait_retry_us);
} while (get_timer(start) < local.wait_free_ms);
/* Give up, release our claim */
printf("I2C: Could not claim bus, timeout %lu\n", get_timer(start));
return -1;
}
#ifdef CONFIG_SATA_AHCI
int sata_initialize(void)
{
return exynos5_sata_init(gd->fdt_blob);
}
#endif