[coreboot] Patch set updated for coreboot: 2517610 WIP: add UART support in bootblock for exynos5250
Ronald G. Minnich (rminnich@gmail.com)
gerrit at coreboot.org
Wed Jan 16 19:01:06 CET 2013
Ronald G. Minnich (rminnich at gmail.com) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/2156
-gerrit
commit 2517610ec856f1e0dfb7ad51b4b534a58c88f47c
Author: David Hendricks <dhendrix at chromium.org>
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 <dhendrix at chromium.org>
---
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(®s->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);
}
More information about the coreboot
mailing list