Ronald G. Minnich ([email protected]) just uploaded a new patch set to gerrit, 
which you can find at http://review.coreboot.org/2156

-gerrit

commit 2517610ec856f1e0dfb7ad51b4b534a58c88f47c
Author: David Hendricks <[email protected]>
Date:   Tue Jan 15 21:37:52 2013 -0800

    WIP: add UART support in bootblock for exynos5250
    
    ** do not submit **
    
    This is a very rough hack to get basic UART support in the bootblock
    for exynos5250. It is basically just a bunch of copy+pasted portions
    from other portions of the code. A few magic offsets had to be
    adjusted too, or so I thought...
    
    Change-Id: I4aae023ebf9335fac88bcb5ae49cb99c15f6c29e
    Signed-off-by: David Hendricks <[email protected]>
---
 src/arch/armv7/lib/id.lds             |    3 +-
 src/arch/armv7/romstage.ld            |    5 +-
 src/cpu/samsung/exynos5250/Kconfig    |    2 +-
 src/mainboard/google/snow/bootblock.c | 1718 ++++++++++++++++++++++++++++++++-
 4 files changed, 1718 insertions(+), 10 deletions(-)

diff --git a/src/arch/armv7/lib/id.lds b/src/arch/armv7/lib/id.lds
index 22b4a02..7cf803c 100644
--- a/src/arch/armv7/lib/id.lds
+++ b/src/arch/armv7/lib/id.lds
@@ -1,5 +1,6 @@
 SECTIONS {
-       . = (0x2024000) - (__id_end - __id_start);
+/*     . = (0x2024000) - (__id_end - __id_start); */
+       . = (0x2026400);
        .id (.): {
                *(.id)
        }
diff --git a/src/arch/armv7/romstage.ld b/src/arch/armv7/romstage.ld
index b63a78e..56c7f2b 100644
--- a/src/arch/armv7/romstage.ld
+++ b/src/arch/armv7/romstage.ld
@@ -35,14 +35,15 @@
 OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
 OUTPUT_ARCH(arm)
 
-ENTRY(_start)
+/* ENTRY(_start) */
 
 SECTIONS
 {
-       . = CONFIG_ROMSTAGE_BASE;
+       . = 0x02023400 + 0x4000;
 
        .romtext . : {
                _rom = .;
+               *(.text.startup);
                *(.text);
        }
 
diff --git a/src/cpu/samsung/exynos5250/Kconfig 
b/src/cpu/samsung/exynos5250/Kconfig
index 3d66c77..206df92 100644
--- a/src/cpu/samsung/exynos5250/Kconfig
+++ b/src/cpu/samsung/exynos5250/Kconfig
@@ -45,7 +45,7 @@ config IRAM_TOP
 
 config SYS_INIT_SP_ADDR
        hex
-       default 0x0204F800
+       default 0x02058000
 
 config IRAM_STACK
        hex
diff --git a/src/mainboard/google/snow/bootblock.c 
b/src/mainboard/google/snow/bootblock.c
index 09f6f74..a5af872 100644
--- a/src/mainboard/google/snow/bootblock.c
+++ b/src/mainboard/google/snow/bootblock.c
@@ -17,17 +17,33 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
+#define uchar unsigned char
+#define uint  unsigned int
+
 #include <stdlib.h>
 #include <types.h>
 #include <arch/io.h>
 #include "cpu/samsung/exynos5250/clk.h"
 #include "cpu/samsung/exynos5250/cpu.h"
+#include "cpu/samsung/exynos5250/dmc.h"
 #include "cpu/samsung/exynos5250/gpio.h"
+#include "cpu/samsung/exynos5250/periph.h"
+#include "cpu/samsung/exynos5250/setup.h"
+#include "cpu/samsung/exynos5250/clock_init.h"
 #include "cpu/samsung/s5p-common/gpio.h"
+#include "cpu/samsung/s5p-common/s3c24x0_i2c.h"
 #include "cpu/samsung/exynos5-common/spi.h"
+#include "cpu/samsung/exynos5-common/uart.h"
+
+#include <device/i2c.h>
+#include <drivers/maxim/max77686/max77686.h>
+
+#include <console/console.h>
 
 #define EXYNOS5_CLOCK_BASE             0x10010000
 
+volatile unsigned long *pshold = (unsigned long *)0x1004330c;
+
 /* FIXME(dhendrix): Can we move this SPI stuff elsewhere? */
 static void spi_rx_tx(struct exynos_spi *regs, int todo,
                        void *dinp, void const *doutp, int i)
@@ -415,17 +431,1707 @@ static void copy_romstage(uint32_t spi_addr, uint32_t 
sram_addr, unsigned int le
        clrbits_le32(&regs->ch_cfg, SPI_TX_CH_ON | SPI_RX_CH_ON);
 }
 
-void bootblock_mainboard_init(void);
-void bootblock_mainboard_init(void)
+/* Pull mode */
+#define EXYNOS_GPIO_PULL_NONE  0x0
+#define EXYNOS_GPIO_PULL_DOWN  0x1
+#define EXYNOS_GPIO_PULL_UP    0x3
+
+#define PULL_MASK(x)           (0x3 << ((x) << 1))
+#define PULL_MODE(x, v)                ((v) << ((x) << 1))
+
+void gpio_set_pull(int gpio, int mode)
 {
-       /* Copy romstage data from SPI ROM to SRAM */
-       /* FIXME: magic constant... */
-       copy_romstage(0x2000, CONFIG_ROMSTAGE_BASE, CONFIG_XIP_ROM_SIZE);
-       
+       unsigned int value;
+       struct s5p_gpio_bank *bank = gpio_get_bank(gpio);
+
+       value = readl(&bank->pull);
+       value &= ~PULL_MASK(GPIO_BIT(gpio));
+
+       switch (mode) {
+       case EXYNOS_GPIO_PULL_DOWN:
+       case EXYNOS_GPIO_PULL_UP:
+               value |= PULL_MODE(GPIO_BIT(gpio), mode);
+               break;
+       default:
+               break;
+       }
+
+       writel(value, &bank->pull);
+}
+
+static uint32_t uart3_base = CONFIG_CONSOLE_SERIAL_UART_ADDRESS;
+
+#define CONFIG_SYS_CLK_FREQ            24000000
+
+/* exynos5: return pll clock frequency */
+unsigned long get_pll_clk(int pllreg);
+unsigned long get_pll_clk(int pllreg)
+{
+       struct exynos5_clock *clk =
+               (struct exynos5_clock *)EXYNOS5_CLOCK_BASE;
+       unsigned long r, m, p, s, k = 0, mask, fout;
+       unsigned int freq;
+
+       switch (pllreg) {
+       case APLL:
+               r = readl(&clk->apll_con0);
+               break;
+       case BPLL:
+               r = readl(&clk->bpll_con0);
+               break;
+       case MPLL:
+               r = readl(&clk->mpll_con0);
+               break;
+       case EPLL:
+               r = readl(&clk->epll_con0);
+               k = readl(&clk->epll_con1);
+               break;
+       case VPLL:
+               r = readl(&clk->vpll_con0);
+               k = readl(&clk->vpll_con1);
+               break;
+       default:
+//             printk(BIOS_DEBUG, "Unsupported PLL (%d)\n", pllreg);
+               return 0;
+       }
+
+       /*
+        * APLL_CON: MIDV [25:16]
+        * MPLL_CON: MIDV [25:16]
+        * EPLL_CON: MIDV [24:16]
+        * VPLL_CON: MIDV [24:16]
+        */
+       if (pllreg == APLL || pllreg == BPLL || pllreg == MPLL)
+               mask = 0x3ff;
+       else
+               mask = 0x1ff;
+
+       m = (r >> 16) & mask;
+
+       /* PDIV [13:8] */
+       p = (r >> 8) & 0x3f;
+       /* SDIV [2:0] */
+       s = r & 0x7;
+
+       freq = CONFIG_SYS_CLK_FREQ;
+
+       if (pllreg == EPLL) {
+               k = k & 0xffff;
+               /* FOUT = (MDIV + K / 65536) * FIN / (PDIV * 2^SDIV) */
+               fout = (m + k / 65536) * (freq / (p * (1 << s)));
+       } else if (pllreg == VPLL) {
+               k = k & 0xfff;
+               /* FOUT = (MDIV + K / 1024) * FIN / (PDIV * 2^SDIV) */
+               fout = (m + k / 1024) * (freq / (p * (1 << s)));
+       } else {
+               /* FOUT = MDIV * FIN / (PDIV * 2^SDIV) */
+               fout = m * (freq / (p * (1 << s)));
+       }
+
+       return fout;
+}
+
+/* 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 */
+};
+
+static unsigned long my_clock_get_periph_rate(enum periph_id peripheral)
+{
+//     struct exynos5_clock *clk =
+//             (struct exynos5_clock *)samsung_get_base_clock();
+       struct exynos5_clock *clk =
+               (struct exynos5_clock *)EXYNOS5_CLOCK_BASE;
+       struct clk_bit_info *bit_info = &clk_bit_info[peripheral];
+//     struct clk_bit_info bit_info = { 12, 4, 12, -1 };
+       unsigned long sclk, sub_clk;
+       unsigned int src, div, sub_div;
+
+       switch (peripheral) {
+       case PERIPH_ID_UART0:
+       case PERIPH_ID_UART1:
+       case PERIPH_ID_UART2:
+       case PERIPH_ID_UART3:
+               src = readl(&clk->src_peric0);
+               div = readl(&clk->div_peric0);
+               break;
+       case PERIPH_ID_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:
+               src = 0;
+               sclk = get_pll_clk(MPLL);
+               sub_div = ((readl(&clk->div_top1) >> bit_info->div_bit) & 0x7) 
+ 1;
+               div = ((readl(&clk->div_top0) >> bit_info->prediv_bit) & 0x7) + 
1;
+               return (sclk / sub_div) / div;
+       default:
+               return -1;
+       };
+
+       src = (src >> bit_info->src_bit) & ((1 << bit_info->n_src_bits) - 1);
+       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);
+
+       return sub_clk;
+}
+
+static void serial_setbrg_dev(void)
+{
+//     struct s5p_uart *const uart = s5p_get_base_uart(dev_index);
+       struct s5p_uart *uart = (struct s5p_uart *)uart3_base;
+       u32 uclk;
+       u32 baudrate = CONFIG_TTYS0_BAUD;
+       u32 val;
+//     enum periph_id periph;
+
+//     periph = exynos5_get_periph_id(base_port);
+       uclk = my_clock_get_periph_rate(PERIPH_ID_UART3);
+       val = uclk / baudrate;
+
+       writel(val / 16 - 1, &uart->ubrdiv);
+
+       /*
+        * FIXME(dhendrix): the original uart.h had a "br_rest" value which
+        * does not seem relevant to the exynos5250... not entirely sure
+        * where/if we need to worry about it here
+        */
+#if 0
+       if (s5p_uart_divslot())
+               writew(udivslot[val % 16], &uart->rest.slot);
+       else
+               writeb(val % 16, &uart->rest.value);
+#endif
+}
+
+/*
+ * Initialise the serial port with the given baudrate. The settings
+ * are always 8 data bits, no parity, 1 stop bit, no start bits.
+ */
+static void exynos5_init_dev(void)
+{
+//     struct s5p_uart *const uart = s5p_get_base_uart(dev_index);
+       struct s5p_uart *uart = (struct s5p_uart *)uart3_base;
+
+       /* enable FIFOs */
+       writel(0x1, &uart->ufcon);
+       writel(0, &uart->umcon);
+       /* 8N1 */
+       writel(0x3, &uart->ulcon);
+       /* No interrupts, no DMA, pure polling */
+       writel(0x245, &uart->ucon);
+
+       serial_setbrg_dev();
+}
+
+static int exynos5_uart_err_check(int op)
+{
+       //struct s5p_uart *const uart = s5p_get_base_uart(dev_index);
+       struct s5p_uart *uart = (struct s5p_uart *)uart3_base;
+       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;
+}
+
+#define RX_FIFO_COUNT_MASK     0xff
+#define RX_FIFO_FULL_MASK      (1 << 8)
+#define TX_FIFO_FULL_MASK      (1 << 24)
+
+#if 0
+/*
+ * Read a single byte from the serial port. Returns 1 on success, 0
+ * otherwise. When the function is succesfull, the character read is
+ * written into its argument c.
+ */
+static unsigned char exynos5_uart_rx_byte(void)
+{
+//     struct s5p_uart *const uart = s5p_get_base_uart(dev_index);
+       struct s5p_uart *uart = (struct s5p_uart *)uart3_base;
+
+       /* wait for character to arrive */
+       while (!(readl(&uart->ufstat) & (RX_FIFO_COUNT_MASK |
+                                        RX_FIFO_FULL_MASK))) {
+               if (exynos5_uart_err_check(0))
+                       return 0;
+       }
+
+       return readb(&uart->urxh) & 0xff;
+}
+#endif
+
+/*
+ * Output a single byte to the serial port.
+ */
+static void exynos5_uart_tx_byte(unsigned char data)
+{
+//     struct s5p_uart *const uart = s5p_get_base_uart(dev_index);
+       struct s5p_uart *uart = (struct s5p_uart *)uart3_base;
+
+       /* wait for room in the tx FIFO */
+       while ((readl(uart->ufstat) & TX_FIFO_FULL_MASK)) {
+               if (exynos5_uart_err_check(1))
+                       return;
+       }
+
+       writeb(data, &uart->utxh);
+}
+
+void puts(const char *s);
+void puts(const char *s)
+{
+       int n = 0;
+
+       while (*s) {
+               exynos5_uart_tx_byte(*s++);
+               n++;
+       }
+
+       exynos5_uart_tx_byte(0xd);      /* CR */
+       exynos5_uart_tx_byte(0xa);      /* LF */
+}
+
+static void do_serial(void)
+{
+       //exynos_pinmux_config(PERIPH_ID_UART3, PINMUX_FLAG_NONE);
+       gpio_set_pull(GPIO_A14, EXYNOS_GPIO_PULL_NONE);
+       gpio_cfg_pin(GPIO_A15, EXYNOS_GPIO_FUNC(0x2));
+
+       exynos5_init_dev();
+}
+
+#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 */
+};
+
+#define I2C0_BASE      0x12c60000
+struct s3c24x0_i2c_bus i2c0 = {
+       .node = 0,
+       .bus_num = 0,
+       .regs = (struct s3c24x0_i2c *)I2C0_BASE,
+       .id = PERIPH_ID_I2C0,
+};
+
+static void i2c_ch_init(struct s3c24x0_i2c *i2c, int speed, int slaveadd)
+{
+       unsigned long freq, pres = 16, div;
+
+       freq = my_clock_get_periph_rate(PERIPH_ID_I2C0);
+       /* 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 */
+       writel((div & 0x0F) | 0xA0 | ((pres == 512) ? 0x40 : 0), &i2c->iiccon);
+
+       /* init to SLAVE REVEIVE and set slaveaddr */
+       writel(0, &i2c->iicstat);
+       writel(slaveadd, &i2c->iicadd);
+       /* program Master Transmit (and implicit STOP) */
+       writel(I2C_MODE_MT | I2C_TXRX_ENA, &i2c->iicstat);
+}
+
+static void i2c_bus_init(struct s3c24x0_i2c_bus *i2c, unsigned int bus)
+{
+//     exynos_pinmux_config(i2c->id, 0);
+       gpio_cfg_pin(GPIO_B30, EXYNOS_GPIO_FUNC(0x2));
+       gpio_cfg_pin(GPIO_B31, EXYNOS_GPIO_FUNC(0x2));
+       gpio_set_pull(GPIO_B30, EXYNOS_GPIO_PULL_NONE);
+       gpio_set_pull(GPIO_B31, EXYNOS_GPIO_PULL_NONE);
+
+       i2c_ch_init(i2c->regs, CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);
+}
+
+void do_barriers(void);
+void do_barriers(void)
+{
+       /*
+        * The reason we don't write out the instructions dsb/isb/sev:
+        * While ARM Cortex-A8 supports ARM v7 instruction set (-march=armv7a),
+        * we compile with -march=armv5 to allow more compilers to work.
+        * For U-Boot code this has no performance impact.
+        */
+       __asm__ __volatile__(
+#if defined(__thumb__)
+       ".hword 0xF3BF, 0x8F4F\n"  /* dsb; darn -march=armv5 */
+       ".hword 0xF3BF, 0x8F6F\n"  /* isb; darn -march=armv5 */
+       ".hword 0xBF40\n"          /* sev; darn -march=armv5 */
+#else
+       ".word  0xF57FF04F\n"      /* dsb; darn -march=armv5 */
+       ".word  0xF57FF06F\n"      /* isb; darn -march=armv5 */
+       ".word  0xE320F004\n"      /* sev; darn -march=armv5 */
+#endif
+       );
+}
+
+void sdelay(unsigned long loops);
+void sdelay(unsigned long loops)
+{
+       __asm__ volatile ("1:\n" "subs %0, %1, #1\n"
+                         "bne 1b":"=r" (loops):"0"(loops));
+}
+
+/* is this right? meh, it seems to work well enough... */
+void my_udelay(unsigned int n);
+void my_udelay(unsigned int n)
+{
+       sdelay(n * 1000);
+}
+
+
+void i2c_init(int speed, int slaveadd)
+{
+       struct s3c24x0_i2c_bus *i2c = &i2c0;
+       struct exynos5_gpio_part1 *gpio;
+       int i;
+       uint32_t x;
+
+#if 0
+       /* By default i2c channel 0 is the current bus */
+       g_current_bus = 0;
+
+       i2c = get_bus(g_current_bus);
+       if (!i2c)
+               return;
+#endif
+
+       i2c_bus_init(i2c, 0);
+
+       /* 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)) {
+               my_udelay(50);
+               i--;
+       }
+
+       gpio = (struct exynos5_gpio_part1 *)(EXYNOS5_GPIO_PART1_BASE);
+       /* FIXME(dhendrix): cannot use nested macro (compilation failure) */
+//     writel((readl(&gpio->b3.con) & ~0x00FF) | 0x0022, &gpio->b3.con);
+       x = readl(&gpio->b3.con);
+       writel((x & ~0x00FF) | 0x0022, &gpio->b3.con);
+
+       i2c_ch_init(i2c->regs, speed, slaveadd);
+}
+
+static int WaitForXfer(struct s3c24x0_i2c *i2c)
+{
+       int i;
+
+       i = I2C_XFER_TIMEOUT_MS * 20;
+       while (!(readl(&i2c->iiccon) & I2CCON_IRPND)) {
+               if (i == 0) {
+                       //debug("%s: i2c xfer timeout\n", __func__);
+                       return I2C_NOK_TOUT;
+               }
+               my_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);
+       /* FIXME(dhendrix): cannot use nested macro (compilation failure) */
+//     writel(readl(&i2c->iiccon) & ~I2CCON_IRPND, &i2c->iiccon);
+}
+
+/*
+ * 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;
+}
+
+/*
+ * 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;
+               my_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 */
+               //debug("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)) {
+               my_udelay(50);
+               i--;
+       }
+
+       if (readl(&i2c->iicstat) & I2CSTAT_BSY) {
+               //debug("%s: bus busy\n", __func__);
+               return I2C_NOK_TOUT;
+       }
+
+       /* FIXME(dhendrix): cannot use nested macro (compilation failure) */
+       //writel(readl(&i2c->iiccon) | I2CCON_ACKGEN, &i2c->iiccon);
+       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) {
+                                       /* FIXME(dhendrix): nested macro */
+#if 0
+                                       writel(readl(&i2c->iiccon) &
+                                              ~I2CCON_ACKGEN,
+                                              &i2c->iiccon);
+#endif
+                                       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:
+               //debug("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(uchar chip, uint addr, int alen, uchar *buffer, int len)
+{
+       struct s3c24x0_i2c_bus *i2c = &i2c0;
+       uchar xaddr[4];
+       int ret;
+
+       if (alen > 4) {
+               //debug("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;
+       }
+
+#ifdef CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW
+       /*
+        * EEPROM chips that implement "address overflow" are ones
+        * like Catalyst 24WC04/08/16 which has 9/10/11 bits of
+        * address and the extra bits end up in the "chip address"
+        * bit slots. This makes a 24WC08 (1Kbyte) chip look like
+        * four 256 byte chips.
+        *
+        * Note that we consider the length of the address field to
+        * still be one byte because the extra address bits are
+        * hidden in the chip address.
+        */
+       if (alen > 0)
+               chip |= ((addr >> (alen * 8)) &
+                        CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW);
+#endif
+       if (!i2c)
+               return -1;
+#if 0
+       if (board_i2c_claim_bus(i2c->node)) {
+               debug("I2C cannot claim bus %d\n", i2c->bus_num);
+               return -1;
+       }
+#endif
+       ret = i2c_transfer(i2c->regs, I2C_READ, chip << 1, &xaddr[4 - alen],
+                          alen, buffer, len);
+       //board_i2c_release_bus(i2c->node);
+       if (ret) {
+               //debug("I2c read: failed %d\n", ret);
+               return 1;
+       }
+       return 0;
+}
+
+int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len)
+{
+       struct s3c24x0_i2c_bus *i2c;
+       uchar xaddr[4];
+       int ret;
+
+       if (alen > 4) {
+               //debug("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;
+       }
+#ifdef CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW
+       /*
+        * EEPROM chips that implement "address overflow" are ones
+        * like Catalyst 24WC04/08/16 which has 9/10/11 bits of
+        * address and the extra bits end up in the "chip address"
+        * bit slots. This makes a 24WC08 (1Kbyte) chip look like
+        * four 256 byte chips.
+        *
+        * Note that we consider the length of the address field to
+        * still be one byte because the extra address bits are
+        * hidden in the chip address.
+        */
+       if (alen > 0)
+               chip |= ((addr >> (alen * 8)) &
+                        CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW);
+#endif
+       //i2c = get_bus(g_current_bus);
+       i2c = &i2c0;
+       if (!i2c)
+               return -1;
+#if 0
+       if (board_i2c_claim_bus(i2c->node)) {
+               //debug("I2C cannot claim bus %d\n", i2c->bus_num);
+               return -1;
+       }
+#endif
+       ret = i2c_transfer(i2c->regs, I2C_WRITE, chip << 1, &xaddr[4 - alen],
+                          alen, buffer, len);
+       //board_i2c_release_bus(i2c->node);
+
+       return ret != 0;
+}
+
+/*
+ * Max77686 parameters values
+ * see max77686.h for parameters details
+ */
+struct max77686_para max77686_param[] = {/*{vol_addr, vol_bitpos,
+       vol_bitmask, reg_enaddr, reg_enbitpos, reg_enbitmask, reg_enbiton,
+       reg_enbitoff, vol_min, vol_div}*/
+       {/* PMIC_BUCK1 */ 0x11, 0x0, 0x3F, 0x10, 0x0, 0x3, 0x3, 0x0, 750, 
50000},
+       {/* PMIC_BUCK2 */ 0x14, 0x0, 0xFF, 0x12, 0x4, 0x3, 0x1, 0x0, 600, 
12500},
+       {/* PMIC_BUCK3 */ 0x1E, 0x0, 0xFF, 0x1C, 0x4, 0x3, 0x1, 0x0, 600, 
12500},
+       {/* PMIC_BUCK4 */ 0x28, 0x0, 0xFF, 0x26, 0x4, 0x3, 0x1, 0x0, 600, 
12500},
+       {/* PMIC_BUCK5 */ 0x31, 0x0, 0x3F, 0x30, 0x0, 0x3, 0x3, 0x0, 750, 
50000},
+       {/* PMIC_BUCK6 */ 0x33, 0x0, 0x3F, 0x32, 0x0, 0x3, 0x3, 0x0, 750, 
50000},
+       {/* PMIC_BUCK7 */ 0x35, 0x0, 0x3F, 0x34, 0x0, 0x3, 0x3, 0x0, 750, 
50000},
+       {/* PMIC_BUCK8 */ 0x37, 0x0, 0x3F, 0x36, 0x0, 0x3, 0x3, 0x0, 750, 
50000},
+       {/* PMIC_BUCK9 */ 0x39, 0x0, 0x3F, 0x38, 0x0, 0x3, 0x3, 0x0, 750, 
50000},
+       {/* PMIC_LDO1 */  0x40, 0x0, 0x3F, 0x40, 0x6, 0x3, 0x3, 0x0, 800, 
25000},
+       {/* PMIC_LDO2 */  0x41, 0x0, 0x3F, 0x41, 0x6, 0x3, 0x1, 0x0, 800, 
25000},
+       {/* PMIC_LDO3 */  0x42, 0x0, 0x3F, 0x42, 0x6, 0x3, 0x3, 0x0, 800, 
50000},
+       {/* PMIC_LDO4 */  0x43, 0x0, 0x3F, 0x43, 0x6, 0x3, 0x3, 0x0, 800, 
50000},
+       {/* PMIC_LDO5 */  0x44, 0x0, 0x3F, 0x44, 0x6, 0x3, 0x3, 0x0, 800, 
50000},
+       {/* PMIC_LDO6 */  0x45, 0x0, 0x3F, 0x45, 0x6, 0x3, 0x1, 0x0, 800, 
25000},
+       {/* PMIC_LDO7 */  0x46, 0x0, 0x3F, 0x46, 0x6, 0x3, 0x1, 0x0, 800, 
25000},
+       {/* PMIC_LDO8 */  0x47, 0x0, 0x3F, 0x47, 0x6, 0x3, 0x1, 0x0, 800, 
25000},
+       {/* PMIC_LDO9 */  0x48, 0x0, 0x3F, 0x48, 0x6, 0x3, 0x3, 0x0, 800, 
50000},
+       {/* PMIC_LDO10 */ 0x49, 0x0, 0x3F, 0x49, 0x6, 0x3, 0x1, 0x0, 800, 
50000},
+       {/* PMIC_LDO11 */ 0x4A, 0x0, 0x3F, 0x4A, 0x6, 0x3, 0x1, 0x0, 800, 
50000},
+       {/* PMIC_LDO12 */ 0x4B, 0x0, 0x3F, 0x4B, 0x6, 0x3, 0x1, 0x0, 800, 
50000},
+       {/* PMIC_LDO13 */ 0x4C, 0x0, 0x3F, 0x4C, 0x6, 0x3, 0x3, 0x0, 800, 
50000},
+       {/* PMIC_LDO14 */ 0x4D, 0x0, 0x3F, 0x4D, 0x6, 0x3, 0x1, 0x0, 800, 
50000},
+       {/* PMIC_LDO15 */ 0x4E, 0x0, 0x3F, 0x4E, 0x6, 0x3, 0x1, 0x0, 800, 
25000},
+       {/* PMIC_LDO16 */ 0x4F, 0x0, 0x3F, 0x4F, 0x6, 0x3, 0x1, 0x0, 800, 
50000},
+       {/* PMIC_LDO17 */ 0x50, 0x0, 0x3F, 0x50, 0x6, 0x3, 0x3, 0x0, 800, 
50000},
+       {/* PMIC_LDO18 */ 0x51, 0x0, 0x3F, 0x51, 0x6, 0x3, 0x3, 0x0, 800, 
50000},
+       {/* PMIC_LDO19 */ 0x52, 0x0, 0x3F, 0x52, 0x6, 0x3, 0x3, 0x0, 800, 
50000},
+       {/* PMIC_LDO20 */ 0x53, 0x0, 0x3F, 0x53, 0x6, 0x3, 0x3, 0x0, 800, 
50000},
+       {/* PMIC_LDO21 */ 0x54, 0x0, 0x3F, 0x54, 0x6, 0x3, 0x3, 0x0, 800, 
50000},
+       {/* PMIC_LDO22 */ 0x55, 0x0, 0x3F, 0x55, 0x6, 0x3, 0x3, 0x0, 800, 
50000},
+       {/* PMIC_LDO23 */ 0x56, 0x0, 0x3F, 0x56, 0x6, 0x3, 0x3, 0x0, 800, 
50000},
+       {/* PMIC_LDO24 */ 0x57, 0x0, 0x3F, 0x57, 0x6, 0x3, 0x3, 0x0, 800, 
50000},
+       {/* PMIC_LDO25 */ 0x58, 0x0, 0x3F, 0x58, 0x6, 0x3, 0x3, 0x0, 800, 
50000},
+       {/* PMIC_LDO26 */ 0x59, 0x0, 0x3F, 0x59, 0x6, 0x3, 0x3, 0x0, 800, 
50000},
+       {/* PMIC_EN32KHZ_CP */ 0x0, 0x0, 0x0, 0x7F, 0x1, 0x1, 0x1, 0x0, 0x0, 
0x0},
+};
+
+/*
+ * Write a value to a register
+ *
+ * @param chip_addr    i2c addr for max77686
+ * @param reg          reg number to write
+ * @param val          value to be written
+ *
+ */
+static inline int max77686_i2c_write(unsigned char chip_addr,
+                                       unsigned int reg, unsigned char val)
+{
+       return i2c_write(chip_addr, reg, 1, &val, 1);
+}
+
+/*
+ * Read a value from a register
+ *
+ * @param chip_addr    i2c addr for max77686
+ * @param reg          reg number to write
+ * @param val          value to be written
+ *
+ */
+static inline int max77686_i2c_read(unsigned char chip_addr,
+                                       unsigned int reg, unsigned char *val)
+{
+       return i2c_read(chip_addr, reg, 1, val, 1);
+}
+
+/* Chip register numbers (not exported from this module) */
+enum {
+       REG_BBAT                = 0x7e,
+
+       /* Bits for BBAT */
+       BBAT_BBCHOSTEN_MASK     = 1 << 0,
+       BBAT_BBCVS_SHIFT        = 3,
+       BBAT_BBCVS_MASK         = 3 << BBAT_BBCVS_SHIFT,
+};
+
+int max77686_disable_backup_batt(void)
+{
+       unsigned char val;
+       int ret;
+
+       //i2c_set_bus_num(0);
+       ret = max77686_i2c_read(MAX77686_I2C_ADDR, REG_BBAT, &val);
+       if (ret) {
+//             debug("max77686 i2c read failed\n");
+               return ret;
+       }
+
+       /* If we already have the correct values, exit */
+       if ((val & (BBAT_BBCVS_MASK | BBAT_BBCHOSTEN_MASK)) ==
+                       BBAT_BBCVS_MASK)
+               return 0;
+
+       /* First disable charging */
+       val &= ~BBAT_BBCHOSTEN_MASK;
+       ret = max77686_i2c_write(MAX77686_I2C_ADDR, REG_BBAT, val);
+       if (ret) {
+               //debug("max77686 i2c write failed\n");
+               return -1;
+       }
+
+       /* Finally select 3.5V to minimize power consumption */
+       val |= BBAT_BBCVS_MASK;
+       ret = max77686_i2c_write(MAX77686_I2C_ADDR, REG_BBAT, val);
+       if (ret) {
+               //debug("max77686 i2c write failed\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+static int max77686_enablereg(enum max77686_regnum reg, int enable)
+{
+       struct max77686_para *pmic;
+       unsigned char read_data;
+       int ret;
+
+       pmic = &max77686_param[reg];
+
+       ret = max77686_i2c_read(MAX77686_I2C_ADDR, pmic->reg_enaddr,
+                               &read_data);
+       if (ret != 0) {
+               //debug("max77686 i2c read failed.\n");
+               return -1;
+       }
+
+       if (enable == REG_DISABLE) {
+               clrbits_8(&read_data,
+                               pmic->reg_enbitmask << pmic->reg_enbitpos);
+       } else {
+               clrsetbits_8(&read_data,
+                               pmic->reg_enbitmask << pmic->reg_enbitpos,
+                               pmic->reg_enbiton << pmic->reg_enbitpos);
+       }
+
+       ret = max77686_i2c_write(MAX77686_I2C_ADDR,
+                                pmic->reg_enaddr, read_data);
+       if (ret != 0) {
+               //debug("max77686 i2c write failed.\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+static int max77686_do_volsetting(enum max77686_regnum reg, unsigned int volt,
+                                 int enable, int volt_units)
+{
+       struct max77686_para *pmic;
+       unsigned char read_data;
+       int vol_level = 0;
+       int ret;
+
+       pmic = &max77686_param[reg];
+
+       if (pmic->vol_addr == 0) {
+               //debug("not a voltage register.\n");
+               return -1;
+       }
+
+       ret = max77686_i2c_read(MAX77686_I2C_ADDR, pmic->vol_addr, &read_data);
+       if (ret != 0) {
+               //debug("max77686 i2c read failed.\n");
+               return -1;
+       }
+
+       if (volt_units == MAX77686_UV)
+               vol_level = volt - (u32)pmic->vol_min * 1000;
+       else
+               vol_level = (volt - (u32)pmic->vol_min) * 1000;
+
+       if (vol_level < 0) {
+               //debug("Not a valid voltage level to set\n");
+               return -1;
+       }
+       vol_level /= (u32)pmic->vol_div;
+
+       clrsetbits_8(&read_data, pmic->vol_bitmask << pmic->vol_bitpos,
+                       vol_level << pmic->vol_bitpos);
+
+       ret = max77686_i2c_write(MAX77686_I2C_ADDR, pmic->vol_addr, read_data);
+       if (ret != 0) {
+               //debug("max77686 i2c write failed.\n");
+               return -1;
+       }
+
+       ret = max77686_enablereg(reg, enable);
+       if (ret != 0) {
+               //debug("Failed to enable buck/ldo.\n");
+               return -1;
+       }
+       return 0;
+}
+
+int max77686_volsetting(enum max77686_regnum reg, unsigned int volt,
+                       int enable, int volt_units)
+{
+//     int old_bus = i2c_get_bus_num();
+       int ret;
+
+//     i2c_set_bus_num(0);
+       ret = max77686_do_volsetting(reg, volt, enable, volt_units);
+//     i2c_set_bus_num(old_bus);
+       return ret;
+}
+
+/* power_init() stuff */
+static void power_init(void)
+{
+       max77686_disable_backup_batt();
+
 #if 0
        /* FIXME: for debugging... */
        volatile unsigned long *addr = (unsigned long *)0x1004330c;
        *addr |= 0x100;
        while (1);
 #endif
+
+       max77686_volsetting(PMIC_BUCK2, CONFIG_VDD_ARM_MV,
+                                               REG_ENABLE, MAX77686_MV);
+       max77686_volsetting(PMIC_BUCK3, CONFIG_VDD_INT_UV,
+                                               REG_ENABLE, MAX77686_UV);
+       max77686_volsetting(PMIC_BUCK1, CONFIG_VDD_MIF_MV,
+                                               REG_ENABLE, MAX77686_MV);
+       max77686_volsetting(PMIC_BUCK4, CONFIG_VDD_G3D_MV,
+                                               REG_ENABLE, MAX77686_MV);
+       max77686_volsetting(PMIC_LDO2, CONFIG_VDD_LDO2_MV,
+                                               REG_ENABLE, MAX77686_MV);
+       max77686_volsetting(PMIC_LDO3, CONFIG_VDD_LDO3_MV,
+                                               REG_ENABLE, MAX77686_MV);
+       max77686_volsetting(PMIC_LDO5, CONFIG_VDD_LDO5_MV,
+                                               REG_ENABLE, MAX77686_MV);
+       max77686_volsetting(PMIC_LDO10, CONFIG_VDD_LDO10_MV,
+                                               REG_ENABLE, MAX77686_MV);
+}
+
+struct mem_timings mem_timings[] = {
+       {
+               .mem_manuf = MEM_MANUF_ELPIDA,
+               .mem_type = DDR_MODE_DDR3,
+               .frequency_mhz = 800,
+               .mpll_mdiv = 0x64,
+               .mpll_pdiv = 0x3,
+               .mpll_sdiv = 0x0,
+               .cpll_mdiv = 0xde,
+               .cpll_pdiv = 0x4,
+               .cpll_sdiv = 0x2,
+               .gpll_mdiv = 0x215,
+               .gpll_pdiv = 0xc,
+               .gpll_sdiv = 0x1,
+               .epll_mdiv = 0x60,
+               .epll_pdiv = 0x3,
+               .epll_sdiv = 0x3,
+               .vpll_mdiv = 0x96,
+               .vpll_pdiv = 0x3,
+               .vpll_sdiv = 0x2,
+
+               .bpll_mdiv = 0x64,
+               .bpll_pdiv = 0x3,
+               .bpll_sdiv = 0x0,
+               .use_bpll = 0,
+               .pclk_cdrex_ratio = 0x5,
+               .direct_cmd_msr = {
+                       0x00020018, 0x00030000, 0x00010042, 0x00000d70
+               },
+               .timing_ref = 0x000000bb,
+               .timing_row = 0x8c36660f,
+               .timing_data = 0x3630580b,
+               .timing_power = 0x41000a44,
+               .phy0_dqs = 0x08080808,
+               .phy1_dqs = 0x08080808,
+               .phy0_dq = 0x08080808,
+               .phy1_dq = 0x08080808,
+               .phy0_tFS = 0x4,
+               .phy1_tFS = 0x4,
+               .phy0_pulld_dqs = 0xf,
+               .phy1_pulld_dqs = 0xf,
+
+               .lpddr3_ctrl_phy_reset = 0x1,
+               .ctrl_start_point = 0x10,
+               .ctrl_inc = 0x10,
+               .ctrl_start = 0x1,
+               .ctrl_dll_on = 0x1,
+               .ctrl_ref = 0x8,
+
+               .ctrl_force = 0x1a,
+               .ctrl_rdlat = 0x0b,
+               .ctrl_bstlen = 0x08,
+
+               .fp_resync = 0x8,
+               .iv_size = 0x7,
+               .dfi_init_start = 1,
+               .aref_en = 1,
+
+               .rd_fetch = 0x3,
+
+               .zq_mode_dds = 0x7,
+               .zq_mode_term = 0x1,
+               .zq_mode_noterm = 0,
+
+               /*
+               * Dynamic Clock: Always Running
+               * Memory Burst length: 8
+               * Number of chips: 1
+               * Memory Bus width: 32 bit
+               * Memory Type: DDR3
+               * Additional Latancy for PLL: 0 Cycle
+               */
+               .memcontrol = DMC_MEMCONTROL_CLK_STOP_DISABLE |
+                       DMC_MEMCONTROL_DPWRDN_DISABLE |
+                       DMC_MEMCONTROL_DPWRDN_ACTIVE_PRECHARGE |
+                       DMC_MEMCONTROL_TP_DISABLE |
+                       DMC_MEMCONTROL_DSREF_ENABLE |
+                       DMC_MEMCONTROL_ADD_LAT_PALL_CYCLE(0) |
+                       DMC_MEMCONTROL_MEM_TYPE_DDR3 |
+                       DMC_MEMCONTROL_MEM_WIDTH_32BIT |
+                       DMC_MEMCONTROL_NUM_CHIP_1 |
+                       DMC_MEMCONTROL_BL_8 |
+                       DMC_MEMCONTROL_PZQ_DISABLE |
+                       DMC_MEMCONTROL_MRR_BYTE_7_0,
+               .memconfig = DMC_MEMCONFIGx_CHIP_MAP_INTERLEAVED |
+                       DMC_MEMCONFIGx_CHIP_COL_10 |
+                       DMC_MEMCONFIGx_CHIP_ROW_15 |
+                       DMC_MEMCONFIGx_CHIP_BANK_8,
+               .membaseconfig0 = DMC_MEMBASECONFIG_VAL(0x40),
+               .membaseconfig1 = DMC_MEMBASECONFIG_VAL(0x80),
+               .prechconfig_tp_cnt = 0xff,
+               .dpwrdn_cyc = 0xff,
+               .dsref_cyc = 0xffff,
+               .concontrol = DMC_CONCONTROL_DFI_INIT_START_DISABLE |
+                       DMC_CONCONTROL_TIMEOUT_LEVEL0 |
+                       DMC_CONCONTROL_RD_FETCH_DISABLE |
+                       DMC_CONCONTROL_EMPTY_DISABLE |
+                       DMC_CONCONTROL_AREF_EN_DISABLE |
+                       DMC_CONCONTROL_IO_PD_CON_DISABLE,
+               .dmc_channels = 2,
+               .chips_per_channel = 2,
+               .chips_to_configure = 1,
+               .send_zq_init = 1,
+               .impedance = IMP_OUTPUT_DRV_30_OHM,
+               .gate_leveling_enable = 0,
+       },
+};
+
+struct arm_clk_ratios arm_clk_ratios[] = {
+       {
+               .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,
+       }
+};
+
+static void clock_init(void)
+{
+       struct exynos5_clock *clk = (struct exynos5_clock *)EXYNOS5_CLOCK_BASE;
+       struct exynos5_mct_regs *mct_regs =
+               (struct exynos5_mct_regs *)EXYNOS5_MULTI_CORE_TIMER_BASE;
+       struct mem_timings *mem = &mem_timings[0];
+       struct arm_clk_ratios *arm_clk_ratio = &arm_clk_ratios[0];
+       u32 val, tmp;
+
+       /* Turn on the MCT as early as possible. */
+       mct_regs->g_tcon |= (1 << 8);
+
+//     mem = clock_get_mem_timings();
+//     arm_clk_ratio = get_arm_ratios();
+
+       clrbits_le32(&clk->src_cpu, MUX_APLL_SEL_MASK);
+       do {
+               val = readl(&clk->mux_stat_cpu);
+       } while ((val | MUX_APLL_SEL_MASK) != val);
+
+       clrbits_le32(&clk->src_core1, MUX_MPLL_SEL_MASK);
+       do {
+               val = readl(&clk->mux_stat_core1);
+       } while ((val | MUX_MPLL_SEL_MASK) != val);
+
+       clrbits_le32(&clk->src_top2, MUX_CPLL_SEL_MASK);
+       clrbits_le32(&clk->src_top2, MUX_EPLL_SEL_MASK);
+       clrbits_le32(&clk->src_top2, MUX_VPLL_SEL_MASK);
+       clrbits_le32(&clk->src_top2, MUX_GPLL_SEL_MASK);
+       tmp = MUX_CPLL_SEL_MASK | MUX_EPLL_SEL_MASK | MUX_VPLL_SEL_MASK
+               | MUX_GPLL_SEL_MASK;
+       do {
+               val = readl(&clk->mux_stat_top2);
+       } while ((val | tmp) != val);
+
+       clrbits_le32(&clk->src_cdrex, MUX_BPLL_SEL_MASK);
+       do {
+               val = readl(&clk->mux_stat_cdrex);
+       } while ((val | MUX_BPLL_SEL_MASK) != val);
+
+       /* PLL locktime */
+       writel(APLL_LOCK_VAL, &clk->apll_lock);
+
+       writel(MPLL_LOCK_VAL, &clk->mpll_lock);
+
+       writel(BPLL_LOCK_VAL, &clk->bpll_lock);
+
+       writel(CPLL_LOCK_VAL, &clk->cpll_lock);
+
+       writel(GPLL_LOCK_VAL, &clk->gpll_lock);
+
+       writel(EPLL_LOCK_VAL, &clk->epll_lock);
+
+       writel(VPLL_LOCK_VAL, &clk->vpll_lock);
+
+       writel(CLK_REG_DISABLE, &clk->pll_div2_sel);
+
+       writel(MUX_HPM_SEL_MASK, &clk->src_cpu);
+       do {
+               val = readl(&clk->mux_stat_cpu);
+       } while ((val | HPM_SEL_SCLK_MPLL) != val);
+
+       val = arm_clk_ratio->arm2_ratio << 28
+               | arm_clk_ratio->apll_ratio << 24
+               | arm_clk_ratio->pclk_dbg_ratio << 20
+               | arm_clk_ratio->atb_ratio << 16
+               | arm_clk_ratio->periph_ratio << 12
+               | arm_clk_ratio->acp_ratio << 8
+               | arm_clk_ratio->cpud_ratio << 4
+               | arm_clk_ratio->arm_ratio;
+       writel(val, &clk->div_cpu0);
+       do {
+               val = readl(&clk->div_stat_cpu0);
+       } while (0 != val);
+
+       writel(CLK_DIV_CPU1_VAL, &clk->div_cpu1);
+       do {
+               val = readl(&clk->div_stat_cpu1);
+       } while (0 != val);
+
+       /* Set APLL */
+       writel(APLL_CON1_VAL, &clk->apll_con1);
+       val = set_pll(arm_clk_ratio->apll_mdiv, arm_clk_ratio->apll_pdiv,
+                       arm_clk_ratio->apll_sdiv);
+       writel(val, &clk->apll_con0);
+       while ((readl(&clk->apll_con0) & APLL_CON0_LOCKED) == 0)
+               ;
+
+       /* Set MPLL */
+       writel(MPLL_CON1_VAL, &clk->mpll_con1);
+       val = set_pll(mem->mpll_mdiv, mem->mpll_pdiv, mem->mpll_sdiv);
+       writel(val, &clk->mpll_con0);
+       while ((readl(&clk->mpll_con0) & MPLL_CON0_LOCKED) == 0)
+               ;
+
+       /*
+        * Configure MUX_MPLL_FOUT to choose the direct clock source
+        * path and avoid the fixed DIV/2 block to save power
+        */
+       setbits_le32(&clk->pll_div2_sel, MUX_MPLL_FOUT_SEL);
+
+       /* Set BPLL */
+       if (mem->use_bpll) {
+               writel(BPLL_CON1_VAL, &clk->bpll_con1);
+               val = set_pll(mem->bpll_mdiv, mem->bpll_pdiv, mem->bpll_sdiv);
+               writel(val, &clk->bpll_con0);
+               while ((readl(&clk->bpll_con0) & BPLL_CON0_LOCKED) == 0)
+                       ;
+
+               setbits_le32(&clk->pll_div2_sel, MUX_BPLL_FOUT_SEL);
+       }
+
+       /* Set CPLL */
+       writel(CPLL_CON1_VAL, &clk->cpll_con1);
+       val = set_pll(mem->cpll_mdiv, mem->cpll_pdiv, mem->cpll_sdiv);
+       writel(val, &clk->cpll_con0);
+       while ((readl(&clk->cpll_con0) & CPLL_CON0_LOCKED) == 0)
+               ;
+
+       /* Set GPLL */
+       writel(GPLL_CON1_VAL, &clk->gpll_con1);
+       val = set_pll(mem->gpll_mdiv, mem->gpll_pdiv, mem->gpll_sdiv);
+       writel(val, &clk->gpll_con0);
+       while ((readl(&clk->gpll_con0) & GPLL_CON0_LOCKED) == 0)
+               ;
+
+       /* Set EPLL */
+       writel(EPLL_CON2_VAL, &clk->epll_con2);
+       writel(EPLL_CON1_VAL, &clk->epll_con1);
+       val = set_pll(mem->epll_mdiv, mem->epll_pdiv, mem->epll_sdiv);
+       writel(val, &clk->epll_con0);
+       while ((readl(&clk->epll_con0) & EPLL_CON0_LOCKED) == 0)
+               ;
+
+       /* Set VPLL */
+       writel(VPLL_CON2_VAL, &clk->vpll_con2);
+       writel(VPLL_CON1_VAL, &clk->vpll_con1);
+       val = set_pll(mem->vpll_mdiv, mem->vpll_pdiv, mem->vpll_sdiv);
+       writel(val, &clk->vpll_con0);
+       while ((readl(&clk->vpll_con0) & VPLL_CON0_LOCKED) == 0)
+               ;
+
+       writel(CLK_SRC_CORE0_VAL, &clk->src_core0);
+       writel(CLK_DIV_CORE0_VAL, &clk->div_core0);
+       while (readl(&clk->div_stat_core0) != 0)
+               ;
+
+       writel(CLK_DIV_CORE1_VAL, &clk->div_core1);
+       while (readl(&clk->div_stat_core1) != 0)
+               ;
+
+       writel(CLK_DIV_SYSRGT_VAL, &clk->div_sysrgt);
+       while (readl(&clk->div_stat_sysrgt) != 0)
+               ;
+
+       writel(CLK_DIV_ACP_VAL, &clk->div_acp);
+       while (readl(&clk->div_stat_acp) != 0)
+               ;
+
+       writel(CLK_DIV_SYSLFT_VAL, &clk->div_syslft);
+       while (readl(&clk->div_stat_syslft) != 0)
+               ;
+
+       writel(CLK_SRC_TOP0_VAL, &clk->src_top0);
+       writel(CLK_SRC_TOP1_VAL, &clk->src_top1);
+       writel(TOP2_VAL, &clk->src_top2);
+       writel(CLK_SRC_TOP3_VAL, &clk->src_top3);
+
+       writel(CLK_DIV_TOP0_VAL, &clk->div_top0);
+       while (readl(&clk->div_stat_top0))
+               ;
+
+       writel(CLK_DIV_TOP1_VAL, &clk->div_top1);
+       while (readl(&clk->div_stat_top1))
+               ;
+
+       writel(CLK_SRC_LEX_VAL, &clk->src_lex);
+       while (1) {
+               val = readl(&clk->mux_stat_lex);
+               if (val == (val | 1))
+                       break;
+       }
+
+       writel(CLK_DIV_LEX_VAL, &clk->div_lex);
+       while (readl(&clk->div_stat_lex))
+               ;
+
+       writel(CLK_DIV_R0X_VAL, &clk->div_r0x);
+       while (readl(&clk->div_stat_r0x))
+               ;
+
+       writel(CLK_DIV_R0X_VAL, &clk->div_r0x);
+       while (readl(&clk->div_stat_r0x))
+               ;
+
+       writel(CLK_DIV_R1X_VAL, &clk->div_r1x);
+       while (readl(&clk->div_stat_r1x))
+               ;
+
+       if (mem->use_bpll) {
+               writel(MUX_BPLL_SEL_MASK | MUX_MCLK_CDREX_SEL |
+                       MUX_MCLK_DPHY_SEL, &clk->src_cdrex);
+       } else {
+               writel(CLK_REG_DISABLE, &clk->src_cdrex);
+       }
+
+       writel(CLK_DIV_CDREX_VAL, &clk->div_cdrex);
+       while (readl(&clk->div_stat_cdrex))
+               ;
+
+       val = readl(&clk->src_cpu);
+       val |= CLK_SRC_CPU_VAL;
+       writel(val, &clk->src_cpu);
+
+       val = readl(&clk->src_top2);
+       val |= CLK_SRC_TOP2_VAL;
+       writel(val, &clk->src_top2);
+
+       val = readl(&clk->src_core1);
+       val |= CLK_SRC_CORE1_VAL;
+       writel(val, &clk->src_core1);
+
+       writel(CLK_SRC_FSYS0_VAL, &clk->src_fsys);
+       writel(CLK_DIV_FSYS0_VAL, &clk->div_fsys0);
+       while (readl(&clk->div_stat_fsys0))
+               ;
+
+       writel(CLK_REG_DISABLE, &clk->clkout_cmu_cpu);
+       writel(CLK_REG_DISABLE, &clk->clkout_cmu_core);
+       writel(CLK_REG_DISABLE, &clk->clkout_cmu_acp);
+       writel(CLK_REG_DISABLE, &clk->clkout_cmu_top);
+       writel(CLK_REG_DISABLE, &clk->clkout_cmu_lex);
+       writel(CLK_REG_DISABLE, &clk->clkout_cmu_r0x);
+       writel(CLK_REG_DISABLE, &clk->clkout_cmu_r1x);
+       writel(CLK_REG_DISABLE, &clk->clkout_cmu_cdrex);
+
+       writel(CLK_SRC_PERIC0_VAL, &clk->src_peric0);
+       writel(CLK_DIV_PERIC0_VAL, &clk->div_peric0);
+
+       writel(CLK_SRC_PERIC1_VAL, &clk->src_peric1);
+       writel(CLK_DIV_PERIC1_VAL, &clk->div_peric1);
+       writel(CLK_DIV_PERIC2_VAL, &clk->div_peric2);
+       writel(SCLK_SRC_ISP_VAL, &clk->sclk_src_isp);
+       writel(SCLK_DIV_ISP_VAL, &clk->sclk_div_isp);
+       writel(CLK_DIV_ISP0_VAL, &clk->div_isp0);
+       writel(CLK_DIV_ISP1_VAL, &clk->div_isp1);
+       writel(CLK_DIV_ISP2_VAL, &clk->div_isp2);
+
+       /* FIMD1 SRC CLK SELECTION */
+       writel(CLK_SRC_DISP1_0_VAL, &clk->src_disp1_0);
+
+       val = MMC2_PRE_RATIO_VAL << MMC2_PRE_RATIO_OFFSET
+               | MMC2_RATIO_VAL << MMC2_RATIO_OFFSET
+               | MMC3_PRE_RATIO_VAL << MMC3_PRE_RATIO_OFFSET
+               | MMC3_RATIO_VAL << MMC3_RATIO_OFFSET;
+       writel(val, &clk->div_fsys2);
+}
+
+#if 0
+#include <console/vtxprintf.h>
+#include <string.h>
+#define ZEROPAD        1               /* pad with zero */
+#define SIGN   2               /* unsigned/signed long */
+#define PLUS   4               /* show plus */
+#define SPACE  8               /* space if plus */
+#define LEFT   16              /* left justified */
+#define SPECIAL        32              /* 0x */
+#define LARGE  64              /* use 'ABCDEF' instead of 'abcdef' */
+/* haha, don't need ctype.c */
+#define isdigit(c)     ((c) >= '0' && (c) <= '9')
+#define is_digit isdigit
+#define isxdigit(c)    (((c) >= '0' && (c) <= '9') || ((c) >= 'a' && (c) <= 
'f') || ((c) >= 'A' && (c) <= 'F'))
+
+static int skip_atoi(const char **s)
+{
+       int i=0;
+
+       while (is_digit(**s))
+               i = i*10 + *((*s)++) - '0';
+       return i;
+}
+
+#include <div64.h>
+static int number(void (*tx_byte)(unsigned char byte),
+       unsigned long long num, int base, int size, int precision, int type)
+{
+       char c,sign,tmp[66];
+       const char *digits="0123456789abcdefghijklmnopqrstuvwxyz";
+       int i;
+       int count = 0;
+
+       if (type & LARGE)
+               digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+       if (type & LEFT)
+               type &= ~ZEROPAD;
+       if (base < 2 || base > 36)
+               return 0;
+       c = (type & ZEROPAD) ? '0' : ' ';
+       sign = 0;
+       if (type & SIGN) {
+               if ((signed long long)num < 0) {
+                       sign = '-';
+                       num = -num;
+                       size--;
+               } else if (type & PLUS) {
+                       sign = '+';
+                       size--;
+               } else if (type & SPACE) {
+                       sign = ' ';
+                       size--;
+               }
+       }
+       if (type & SPECIAL) {
+               if (base == 16)
+                       size -= 2;
+               else if (base == 8)
+                       size--;
+       }
+       i = 0;
+       if (num == 0)
+               tmp[i++]='0';
+       else while (num != 0)
+               tmp[i++] = digits[do_div(num,base)];
+       if (i > precision)
+               precision = i;
+       size -= precision;
+       if (!(type&(ZEROPAD+LEFT)))
+               while(size-->0)
+                       tx_byte(' '), count++;
+       if (sign)
+               tx_byte(sign), count++;
+       if (type & SPECIAL) {
+               if (base==8)
+                       tx_byte('0'), count++;
+               else if (base==16) {
+                       tx_byte('0'), count++;
+                       tx_byte(digits[33]), count++;
+               }
+       }
+       if (!(type & LEFT))
+               while (size-- > 0)
+                       tx_byte(c), count++;
+       while (i < precision--)
+               tx_byte('0'), count++;
+       while (i-- > 0)
+               tx_byte(tmp[i]), count++;
+       while (size-- > 0)
+               tx_byte(' '), count++;
+       return count;
+}
+
+int vtxprintf(void (*tx_byte)(unsigned char byte), const char *fmt, va_list 
args)
+{
+       int len;
+       unsigned long long num;
+       int i, base;
+       const char *s;
+
+       int flags;              /* flags to number() */
+
+       int field_width;        /* width of output field */
+       int precision;          /* min. # of digits for integers; max
+                                  number of chars for from string */
+       int qualifier;          /* 'h', 'l', or 'L' for integer fields */
+
+       int count;
+
+       for (count=0; *fmt ; ++fmt) {
+               if (*fmt != '%') {
+                       tx_byte(*fmt), count++;
+                       continue;
+               }
+
+               /* process flags */
+               flags = 0;
+repeat:
+                       ++fmt;          /* this also skips first '%' */
+                       switch (*fmt) {
+                               case '-': flags |= LEFT; goto repeat;
+                               case '+': flags |= PLUS; goto repeat;
+                               case ' ': flags |= SPACE; goto repeat;
+                               case '#': flags |= SPECIAL; goto repeat;
+                               case '0': flags |= ZEROPAD; goto repeat;
+                               }
+
+               /* get field width */
+               field_width = -1;
+               if (is_digit(*fmt))
+                       field_width = skip_atoi(&fmt);
+               else if (*fmt == '*') {
+                       ++fmt;
+                       /* it's the next argument */
+                       field_width = va_arg(args, int);
+                       if (field_width < 0) {
+                               field_width = -field_width;
+                               flags |= LEFT;
+                       }
+               }
+
+               /* get the precision */
+               precision = -1;
+               if (*fmt == '.') {
+                       ++fmt;
+                       if (is_digit(*fmt))
+                               precision = skip_atoi(&fmt);
+                       else if (*fmt == '*') {
+                               ++fmt;
+                               /* it's the next argument */
+                               precision = va_arg(args, int);
+                       }
+                       if (precision < 0)
+                               precision = 0;
+               }
+
+               /* get the conversion qualifier */
+               qualifier = -1;
+               if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' || *fmt == 'z') {
+                       qualifier = *fmt;
+                       ++fmt;
+                       if (*fmt == 'l') {
+                               qualifier = 'L';
+                               ++fmt;
+                       }
+               }
+
+               /* default base */
+               base = 10;
+
+               switch (*fmt) {
+               case 'c':
+                       if (!(flags & LEFT))
+                               while (--field_width > 0)
+                                       tx_byte(' '), count++;
+                       tx_byte((unsigned char) va_arg(args, int)), count++;
+                       while (--field_width > 0)
+                               tx_byte(' '), count++;
+                       continue;
+
+               case 's':
+                       s = va_arg(args, char *);
+                       if (!s)
+                               s = "<NULL>";
+
+                       len = strnlen(s, precision);
+
+                       if (!(flags & LEFT))
+                               while (len < field_width--)
+                                       tx_byte(' '), count++;
+                       for (i = 0; i < len; ++i)
+                               tx_byte(*s++), count++;
+                       while (len < field_width--)
+                               tx_byte(' '), count++;
+                       continue;
+
+               case 'p':
+                       if (field_width == -1) {
+                               field_width = 2*sizeof(void *);
+                               flags |= ZEROPAD;
+                       }
+                       count += number(tx_byte,
+                               (unsigned long) va_arg(args, void *), 16,
+                               field_width, precision, flags);
+                       continue;
+
+               case 'n':
+                       if (qualifier == 'L') {
+                               long long *ip = va_arg(args, long long *);
+                               *ip = count;
+                       } else if (qualifier == 'l') {
+                               long * ip = va_arg(args, long *);
+                               *ip = count;
+                       } else {
+                               int * ip = va_arg(args, int *);
+                               *ip = count;
+                       }
+                       continue;
+
+               case '%':
+                       tx_byte('%'), count++;
+                       continue;
+
+               /* integer number formats - set up the flags and "break" */
+               case 'o':
+                       base = 8;
+                       break;
+
+               case 'X':
+                       flags |= LARGE;
+               case 'x':
+                       base = 16;
+                       break;
+
+               case 'd':
+               case 'i':
+                       flags |= SIGN;
+               case 'u':
+                       break;
+
+               default:
+                       tx_byte('%'), count++;
+                       if (*fmt)
+                               tx_byte(*fmt), count++;
+                       else
+                               --fmt;
+                       continue;
+               }
+               if (qualifier == 'L') {
+                       num = va_arg(args, unsigned long long);
+               } else if (qualifier == 'l') {
+                       num = va_arg(args, unsigned long);
+               } else if (qualifier == 'z') {
+                       num = va_arg(args, size_t);
+               } else if (qualifier == 'h') {
+                       num = (unsigned short) va_arg(args, int);
+                       if (flags & SIGN)
+                               num = (short) num;
+               } else if (flags & SIGN) {
+                       num = va_arg(args, int);
+               } else {
+                       num = va_arg(args, unsigned int);
+               }
+               count += number(tx_byte, num, base, field_width, precision, 
flags);
+       }
+       return count;
+}
+
+int do_printk(int msg_level, const char *fmt, ...)
+{
+       va_list args;
+       int i;
+
+       va_start(args, fmt);
+       i = vtxprintf(console_tx_byte, fmt, args);
+       va_end(args);
+
+       return i;
+}
+#endif
+
+void bootblock_mainboard_init(void);
+void bootblock_mainboard_init(void)
+{
+       /* FIXME: we should not need UART in bootblock, this is only
+          done for testing purposes */
+       i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);
+       power_init();
+       clock_init();
+       do_serial();
+       puts("hello world");
+
+       /* Copy romstage data from SPI ROM to SRAM */
+       /* FIXME: test with something benign, then fix the offsets once
+          we're more confident in this */
+       copy_romstage(0x2000, 0x2060000, 0x800);
 }

-- 
coreboot mailing list: [email protected]
http://www.coreboot.org/mailman/listinfo/coreboot

Reply via email to