David Hendricks (dhendrix@chromium.org) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/2142
-gerrit
commit 2c20eab5083695f4bf5c0332c107ca80a0d79f00 Author: David Hendricks dhendrix@chromium.org Date: Fri Jan 11 15:31:56 2013 -0800
WIP: first attempt at porting SPI driver into bootblock
** do not submit **
This is just a lame attempt at copying the necessary code into snow's bootblock so that we can copy SPI content into SRAM.
Change-Id: I2a33605504abecee5ed2689400cf6c555da3f2c3 Signed-off-by: David Hendricks dhendrix@chromium.org --- src/arch/armv7/Makefile.inc | 3 + src/arch/armv7/bootblock_simple.c | 33 ++- src/arch/armv7/lib/_divsi3.S | 145 +++++++++++ src/arch/armv7/lib/_udivsi3.S | 96 ++++++++ src/cpu/samsung/exynos5250/bootblock.c | 48 ++++ src/cpu/samsung/exynos5250/clk.h | 2 + src/mainboard/google/snow/Kconfig | 4 + src/mainboard/google/snow/bootblock.c | 424 +++++++++++++++++++++++++++++++++ 8 files changed, 738 insertions(+), 17 deletions(-)
diff --git a/src/arch/armv7/Makefile.inc b/src/arch/armv7/Makefile.inc index d7fbbdf..ae66670 100644 --- a/src/arch/armv7/Makefile.inc +++ b/src/arch/armv7/Makefile.inc @@ -228,6 +228,9 @@ bootblock_lds = $(src)/arch/armv7/lib/id.lds #bootblock_lds = $(src)/arch/armv7/romstage.ld bootblock_lds += $(chipset_bootblock_lds)
+bootblock_inc += $(src)/arch/armv7/bootblock.inc +bootblock_inc += $(src)/arch/armv7/lib/_udivsi3.S +bootblock_inc += $(src)/arch/armv7/lib/_divsi3.S bootblock_inc += $(src)/arch/armv7/lib/id.inc bootblock_inc += $(chipset_bootblock_inc)
diff --git a/src/arch/armv7/bootblock_simple.c b/src/arch/armv7/bootblock_simple.c index f447a29..6897e15 100644 --- a/src/arch/armv7/bootblock_simple.c +++ b/src/arch/armv7/bootblock_simple.c @@ -19,33 +19,32 @@ * MA 02110-1301 USA */
- - #include <bootblock_common.h> +#include <arch/cbfs.h> +#include <arch/hlt.h>
- -#include "../../lib/uart8250.c" -#include "lib/div.c" - -struct uart8250 uart = { - 115200 -}; +static int boot_cpu(void) +{ + /* + * FIXME: This is a stub for now. All non-boot CPUs should be + * waiting for an interrupt. We could move the chunk of assembly + * which puts them to sleep in here... + */ + return 1; +}
void main(unsigned long bist) { - init_uart8250(CONFIG_TTYS0_BASE, &uart); - uart8250_tx_byte(CONFIG_TTYS0_BASE, '@'); + const char *target1 = "fallback/romstage"; + unsigned long entry;
if (boot_cpu()) { + bootblock_mainboard_init(); bootblock_cpu_init(); - bootblock_northbridge_init(); - bootblock_southbridge_init(); } - const char* target1 = "fallback/romstage"; - unsigned long entry; + entry = findstage(target1); - if (entry) call(entry, bist); + if (entry) call(entry);
hlt(); } - diff --git a/src/arch/armv7/lib/_divsi3.S b/src/arch/armv7/lib/_divsi3.S new file mode 100644 index 0000000..fd0aa03 --- /dev/null +++ b/src/arch/armv7/lib/_divsi3.S @@ -0,0 +1,145 @@ + +.macro ARM_DIV_BODY dividend, divisor, result, curbit + +#if __LINUX_ARM_ARCH__ >= 5 + + clz \curbit, \divisor + clz \result, \dividend + sub \result, \curbit, \result + mov \curbit, #1 + mov \divisor, \divisor, lsl \result + mov \curbit, \curbit, lsl \result + mov \result, #0 + +#else + + @ Initially shift the divisor left 3 bits if possible, + @ set curbit accordingly. This allows for curbit to be located + @ at the left end of each 4 bit nibbles in the division loop + @ to save one loop in most cases. + tst \divisor, #0xe0000000 + moveq \divisor, \divisor, lsl #3 + moveq \curbit, #8 + movne \curbit, #1 + + @ Unless the divisor is very big, shift it up in multiples of + @ four bits, since this is the amount of unwinding in the main + @ division loop. Continue shifting until the divisor is + @ larger than the dividend. +1: cmp \divisor, #0x10000000 + cmplo \divisor, \dividend + movlo \divisor, \divisor, lsl #4 + movlo \curbit, \curbit, lsl #4 + blo 1b + + @ For very big divisors, we must shift it a bit at a time, or + @ we will be in danger of overflowing. +1: cmp \divisor, #0x80000000 + cmplo \divisor, \dividend + movlo \divisor, \divisor, lsl #1 + movlo \curbit, \curbit, lsl #1 + blo 1b + + mov \result, #0 + +#endif + + @ Division loop +1: cmp \dividend, \divisor + subhs \dividend, \dividend, \divisor + orrhs \result, \result, \curbit + cmp \dividend, \divisor, lsr #1 + subhs \dividend, \dividend, \divisor, lsr #1 + orrhs \result, \result, \curbit, lsr #1 + cmp \dividend, \divisor, lsr #2 + subhs \dividend, \dividend, \divisor, lsr #2 + orrhs \result, \result, \curbit, lsr #2 + cmp \dividend, \divisor, lsr #3 + subhs \dividend, \dividend, \divisor, lsr #3 + orrhs \result, \result, \curbit, lsr #3 + cmp \dividend, #0 @ Early termination? + movnes \curbit, \curbit, lsr #4 @ No, any more bits to do? + movne \divisor, \divisor, lsr #4 + bne 1b + +.endm + +.macro ARM_DIV2_ORDER divisor, order + +#if __LINUX_ARM_ARCH__ >= 5 + + clz \order, \divisor + rsb \order, \order, #31 + +#else + + cmp \divisor, #(1 << 16) + movhs \divisor, \divisor, lsr #16 + movhs \order, #16 + movlo \order, #0 + + cmp \divisor, #(1 << 8) + movhs \divisor, \divisor, lsr #8 + addhs \order, \order, #8 + + cmp \divisor, #(1 << 4) + movhs \divisor, \divisor, lsr #4 + addhs \order, \order, #4 + + cmp \divisor, #(1 << 2) + addhi \order, \order, #3 + addls \order, \order, \divisor, lsr #1 + +#endif + +.endm + + .align 5 +.globl __divsi3 +.globl __aeabi_idiv +__divsi3: +__aeabi_idiv: + cmp r1, #0 + eor ip, r0, r1 @ save the sign of the result. + beq Ldiv0 + rsbmi r1, r1, #0 @ loops below use unsigned. + subs r2, r1, #1 @ division by 1 or -1 ? + beq 10f + movs r3, r0 + rsbmi r3, r0, #0 @ positive dividend value + cmp r3, r1 + bls 11f + tst r1, r2 @ divisor is power of 2 ? + beq 12f + + ARM_DIV_BODY r3, r1, r0, r2 + + cmp ip, #0 + rsbmi r0, r0, #0 + mov pc, lr + +10: teq ip, r0 @ same sign ? + rsbmi r0, r0, #0 + mov pc, lr + +11: movlo r0, #0 + moveq r0, ip, asr #31 + orreq r0, r0, #1 + mov pc, lr + +12: ARM_DIV2_ORDER r1, r2 + + cmp ip, #0 + mov r0, r3, lsr r2 + rsbmi r0, r0, #0 + mov pc, lr + +/* FIXME(dhendrix): Ldiv0 already defined in _udivsi3.S */ +/* +Ldiv0: + + str lr, [sp, #-4]! + bl __div0 + mov r0, #0 @ About as wrong as it could be. + ldr pc, [sp], #4 +*/ diff --git a/src/arch/armv7/lib/_udivsi3.S b/src/arch/armv7/lib/_udivsi3.S new file mode 100644 index 0000000..13b41ad --- /dev/null +++ b/src/arch/armv7/lib/_udivsi3.S @@ -0,0 +1,96 @@ +/* # 1 "libgcc1.S" */ +@ libgcc1 routines for ARM cpu. +@ Division routines, written by Richard Earnshaw, (rearnsha@armltd.co.uk) +dividend .req r0 +divisor .req r1 +result .req r2 +curbit .req r3 +/* ip .req r12 */ +/* sp .req r13 */ +/* lr .req r14 */ +/* pc .req r15 */ + .text + .globl __udivsi3 + .type __udivsi3 ,function + .globl __aeabi_uidiv + .type __aeabi_uidiv ,function + .align 0 + __udivsi3: + __aeabi_uidiv: + cmp divisor, #0 + beq Ldiv0 + mov curbit, #1 + mov result, #0 + cmp dividend, divisor + bcc Lgot_result +Loop1: + @ Unless the divisor is very big, shift it up in multiples of + @ four bits, since this is the amount of unwinding in the main + @ division loop. Continue shifting until the divisor is + @ larger than the dividend. + cmp divisor, #0x10000000 + cmpcc divisor, dividend + movcc divisor, divisor, lsl #4 + movcc curbit, curbit, lsl #4 + bcc Loop1 +Lbignum: + @ For very big divisors, we must shift it a bit at a time, or + @ we will be in danger of overflowing. + cmp divisor, #0x80000000 + cmpcc divisor, dividend + movcc divisor, divisor, lsl #1 + movcc curbit, curbit, lsl #1 + bcc Lbignum +Loop3: + @ Test for possible subtractions, and note which bits + @ are done in the result. On the final pass, this may subtract + @ too much from the dividend, but the result will be ok, since the + @ "bit" will have been shifted out at the bottom. + cmp dividend, divisor + subcs dividend, dividend, divisor + orrcs result, result, curbit + cmp dividend, divisor, lsr #1 + subcs dividend, dividend, divisor, lsr #1 + orrcs result, result, curbit, lsr #1 + cmp dividend, divisor, lsr #2 + subcs dividend, dividend, divisor, lsr #2 + orrcs result, result, curbit, lsr #2 + cmp dividend, divisor, lsr #3 + subcs dividend, dividend, divisor, lsr #3 + orrcs result, result, curbit, lsr #3 + cmp dividend, #0 @ Early termination? + movnes curbit, curbit, lsr #4 @ No, any more bits to do? + movne divisor, divisor, lsr #4 + bne Loop3 +Lgot_result: + mov r0, result + mov pc, lr +Ldiv0: + str lr, [sp, #-4]! + bl hang + mov r0, #0 @ about as wrong as it could be + ldmia sp!, {pc} + .size __udivsi3 , . - __udivsi3 + +.globl __aeabi_uidivmod +__aeabi_uidivmod: + + stmfd sp!, {r0, r1, ip, lr} + bl __aeabi_uidiv + ldmfd sp!, {r1, r2, ip, lr} + mul r3, r0, r2 + sub r1, r1, r3 + mov pc, lr + +.globl __aeabi_idivmod +__aeabi_idivmod: + + stmfd sp!, {r0, r1, ip, lr} + bl __aeabi_idiv + ldmfd sp!, {r1, r2, ip, lr} + mul r3, r0, r2 + sub r1, r1, r3 + mov pc, lr + +hang: + bl hang diff --git a/src/cpu/samsung/exynos5250/bootblock.c b/src/cpu/samsung/exynos5250/bootblock.c new file mode 100644 index 0000000..46b52b9 --- /dev/null +++ b/src/cpu/samsung/exynos5250/bootblock.c @@ -0,0 +1,48 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2013 The Chromium OS Authors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#if 0 +/* + * Set/clear program flow prediction and return the previous state. + */ +static int config_branch_prediction(int set_cr_z) +{ + unsigned int cr; + + /* System Control Register: 11th bit Z Branch prediction enable */ + cr = get_cr(); + set_cr(set_cr_z ? cr | CR_Z : cr & ~CR_Z); + + return cr & CR_Z; +} +#endif + +void bootblock_cpu_init(void); +void bootblock_cpu_init(void) +{ + /* + * FIXME: this is a stub for now. It should eventually copy + * romstage data (and maybe more) from SPI to SRAM. + */ +#if 0 + volatile unsigned long *addr = (unsigned long *)0x1004330c; + *addr |= 0x100; + while (1) ; +#endif +} diff --git a/src/cpu/samsung/exynos5250/clk.h b/src/cpu/samsung/exynos5250/clk.h index 2de949a..762dbf3 100644 --- a/src/cpu/samsung/exynos5250/clk.h +++ b/src/cpu/samsung/exynos5250/clk.h @@ -25,6 +25,8 @@ #include <cpu/samsung/exynos5-common/clk.h> #include <cpu/samsung/exynos5250/pinmux.h>
+enum periph_id; + /* * Set mshci controller instances clock drivder * diff --git a/src/mainboard/google/snow/Kconfig b/src/mainboard/google/snow/Kconfig index 21cdfa5..8f41c64 100644 --- a/src/mainboard/google/snow/Kconfig +++ b/src/mainboard/google/snow/Kconfig @@ -56,6 +56,10 @@ config MAINBOARD_VENDOR string default "Samsung"
+config BOOTBLOCK_MAINBOARD_INIT + string + default "mainboard/google/snow/bootblock.c" + # SPL (second-phase loader) stuff config SPL_TEXT_BASE hex diff --git a/src/mainboard/google/snow/bootblock.c b/src/mainboard/google/snow/bootblock.c new file mode 100644 index 0000000..59de9d2 --- /dev/null +++ b/src/mainboard/google/snow/bootblock.c @@ -0,0 +1,424 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2013 The ChromiumOS Authors. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <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/gpio.h" +#include "cpu/samsung/s5p-common/gpio.h" +#include "cpu/samsung/exynos5-common/spi.h" + +#define EXYNOS5_CLOCK_BASE 0x10010000 + +/* 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) +{ + unsigned int *rxp = (unsigned int *)(dinp + (i * (32 * 1024))); + int rx_lvl, tx_lvl; + unsigned int out_bytes, in_bytes; + + out_bytes = in_bytes = todo; + setbits_le32(®s->ch_cfg, SPI_CH_RST); + clrbits_le32(®s->ch_cfg, SPI_CH_RST); + writel(((todo * 8) / 32) | SPI_PACKET_CNT_EN, ®s->pkt_cnt); + + while (in_bytes) { + uint32_t spi_sts; + int temp; + + spi_sts = readl(®s->spi_sts); + rx_lvl = ((spi_sts >> 15) & 0x7f); + tx_lvl = ((spi_sts >> 6) & 0x7f); + while (tx_lvl < 32 && out_bytes) { + temp = 0xffffffff; + writel(temp, ®s->tx_data); + out_bytes -= 4; + tx_lvl += 4; + } + while (rx_lvl >= 4 && in_bytes) { + temp = readl(®s->rx_data); + if (rxp) + *rxp++ = temp; + in_bytes -= 4; + rx_lvl -= 4; + } + } +} + +#if 0 +void clock_ll_set_pre_ratio(enum periph_id periph_id, unsigned divisor) +{ + struct exynos5_clock *clk = + (struct exynos5_clock *)samsung_get_base_clock(); + unsigned shift; + unsigned mask = 0xff; + u32 *reg; + + /* + * For now we only handle a very small subset of peipherals here. + * Others will need to (and do) mangle the clock registers + * themselves, At some point it is hoped that this function can work + * from a table or calculated register offset / mask. For now this + * is at least better than spreading clock control code around + * U-Boot. + */ + switch (periph_id) { + case PERIPH_ID_SPI0: + reg = &clk->div_peric1; + shift = 8; + break; + case PERIPH_ID_SPI1: + reg = &clk->div_peric1; + shift = 24; + break; + case PERIPH_ID_SPI2: + reg = &clk->div_peric2; + shift = 8; + break; + case PERIPH_ID_SPI3: + reg = &clk->sclk_div_isp; + shift = 4; + break; + case PERIPH_ID_SPI4: + reg = &clk->sclk_div_isp; + shift = 16; + break; + default: + debug("%s: Unsupported peripheral ID %d\n", __func__, + periph_id); + return; + } +} +#endif +void clock_ll_set_pre_ratio(enum periph_id periph_id, unsigned divisor) +{ + struct exynos5_clock *clk = + (struct exynos5_clock *)EXYNOS5_CLOCK_BASE; + unsigned shift; + unsigned mask = 0xff; + u32 *reg; + + reg = &clk->div_peric1; + shift = 24; + clrsetbits_le32(reg, mask << shift, (divisor & mask) << shift); +} + +#if 0 +void clock_ll_set_ratio(enum periph_id periph_id, unsigned divisor) +{ + struct exynos5_clock *clk = + (struct exynos5_clock *)samsung_get_base_clock(); + unsigned shift; + unsigned mask = 0xff; + u32 *reg; + + switch (periph_id) { + case PERIPH_ID_SPI0: + reg = &clk->div_peric1; + shift = 0; + break; + case PERIPH_ID_SPI1: + reg = &clk->div_peric1; + shift = 16; + break; + case PERIPH_ID_SPI2: + reg = &clk->div_peric2; + shift = 0; + break; + case PERIPH_ID_SPI3: + reg = &clk->sclk_div_isp; + shift = 0; + break; + case PERIPH_ID_SPI4: + reg = &clk->sclk_div_isp; + shift = 12; + break; + default: + debug("%s: Unsupported peripheral ID %d\n", __func__, + periph_id); + return; + } +} +#endif +void clock_ll_set_ratio(enum periph_id periph_id, unsigned divisor) +{ + struct exynos5_clock *clk = + (struct exynos5_clock *)EXYNOS5_CLOCK_BASE; + unsigned shift; + unsigned mask = 0xff; + u32 *reg; + + reg = &clk->div_peric1; + shift = 16; + clrsetbits_le32(reg, mask << shift, (divisor & mask) << shift); +} + +/** + * Linearly searches for the most accurate main and fine stage clock scalars + * (divisors) for a specified target frequency and scalar bit sizes by checking + * all multiples of main_scalar_bits values. Will always return scalars up to or + * slower than target. + * + * @param main_scalar_bits Number of main scalar bits, must be > 0 and < 32 + * @param fine_scalar_bits Number of fine scalar bits, must be > 0 and < 32 + * @param input_freq Clock frequency to be scaled in Hz + * @param target_freq Desired clock frequency in Hz + * @param best_fine_scalar Pointer to store the fine stage divisor + * + * @return best_main_scalar Main scalar for desired frequency or -1 if none + * found + */ +static int clock_calc_best_scalar(unsigned int main_scaler_bits, + unsigned int fine_scalar_bits, unsigned int input_rate, + unsigned int target_rate, unsigned int *best_fine_scalar) +{ + int i; + int best_main_scalar = -1; + unsigned int best_error = target_rate; + const unsigned int cap = (1 << fine_scalar_bits) - 1; + const unsigned int loops = 1 << main_scaler_bits; + +#if 0 + debug("Input Rate is %u, Target is %u, Cap is %u\n", input_rate, + target_rate, cap); + + assert(best_fine_scalar != NULL); + assert(main_scaler_bits <= fine_scalar_bits); +#endif + + *best_fine_scalar = 1; + + if (input_rate == 0 || target_rate == 0) + return -1; + + if (target_rate >= input_rate) + return 1; + + for (i = 1; i <= loops; i++) { + const unsigned int effective_div = MAX(MIN(input_rate / i / + target_rate, cap), 1); + const unsigned int effective_rate = input_rate / i / + effective_div; + const int error = target_rate - effective_rate; + +#if 0 + debug("%d|effdiv:%u, effrate:%u, error:%d\n", i, effective_div, + effective_rate, error); +#endif + + if (error >= 0 && error <= best_error) { + best_error = error; + best_main_scalar = i; + *best_fine_scalar = effective_div; + } + } + + return best_main_scalar; +} + +#if 0 +int clock_set_rate(enum periph_id periph_id, unsigned int rate) +{ + int main; + unsigned int fine; + + switch (periph_id) { + case PERIPH_ID_SPI0: + case PERIPH_ID_SPI1: + case PERIPH_ID_SPI2: + case PERIPH_ID_SPI3: + case PERIPH_ID_SPI4: + main = clock_calc_best_scalar(4, 8, 400000000, rate, &fine); + if (main < 0) { + debug("%s: Cannot set clock rate for periph %d", + __func__, periph_id); + return -1; + } + clock_ll_set_ratio(periph_id, main - 1); + clock_ll_set_pre_ratio(periph_id, fine - 1); + break; + default: + debug("%s: Unsupported peripheral ID %d\n", __func__, + periph_id); + return -1; + } + main = clock_calc_best_scalar(4, 8, 400000000, rate, &fine); + if (main < 0) { + debug("%s: Cannot set clock rate for periph %d", + __func__, periph_id); + return -1; + } + clock_ll_set_ratio(PERIPH_ID_SPI1, main - 1); + clock_ll_set_pre_ratio(PERIPH_ID_SPI1, fine - 1); + + return 0; +} +#endif +int clock_set_rate(enum periph_id periph_id, unsigned int rate) +{ + int main; + unsigned int fine; + + main = clock_calc_best_scalar(4, 8, 400000000, rate, &fine); + if (main < 0) { +// debug("%s: Cannot set clock rate for periph %d", +// __func__, periph_id); + return -1; + } + clock_ll_set_ratio(-1, main - 1); + clock_ll_set_pre_ratio(-1, fine - 1); + + return 0; +} + +struct gpio_info { + unsigned int reg_addr; /* Address of register for this part */ + unsigned int max_gpio; /* Maximum GPIO in this part */ +}; + +static const struct gpio_info gpio_data[EXYNOS_GPIO_NUM_PARTS] = { + { EXYNOS5_GPIO_PART1_BASE, GPIO_MAX_PORT_PART_1 }, + { EXYNOS5_GPIO_PART2_BASE, GPIO_MAX_PORT_PART_2 }, + { EXYNOS5_GPIO_PART3_BASE, GPIO_MAX_PORT_PART_3 }, + { EXYNOS5_GPIO_PART4_BASE, GPIO_MAX_PORT_PART_4 }, + { EXYNOS5_GPIO_PART5_BASE, GPIO_MAX_PORT_PART_5 }, + { EXYNOS5_GPIO_PART6_BASE, GPIO_MAX_PORT }, +}; + +static struct s5p_gpio_bank *gpio_get_bank(unsigned int gpio) +{ + const struct gpio_info *data; + unsigned int upto; + int i; + + for (i = upto = 0, data = gpio_data; i < EXYNOS_GPIO_NUM_PARTS; + i++, upto = data->max_gpio, data++) { + if (gpio < data->max_gpio) { + struct s5p_gpio_bank *bank; + + bank = (struct s5p_gpio_bank *)data->reg_addr; + bank += (gpio - upto) / GPIO_PER_BANK; + return bank; + } + } + +#ifndef CONFIG_SPL_BUILD + assert(gpio < GPIO_MAX_PORT); /* ...which it will not be */ +#endif + return NULL; +} + +#define CON_MASK(x) (0xf << ((x) << 2)) +#define CON_SFR(x, v) ((v) << ((x) << 2)) + +/* This macro gets gpio pin offset from 0..7 */ +#define GPIO_BIT(x) ((x) & 0x7) + +void gpio_cfg_pin(int gpio, int cfg) +{ + unsigned int value; + struct s5p_gpio_bank *bank = gpio_get_bank(gpio); + + value = readl(&bank->con); + value &= ~CON_MASK(GPIO_BIT(gpio)); + value |= CON_SFR(GPIO_BIT(gpio), cfg); + writel(value, &bank->con); +} + +//static void exynos_spi_copy(unsigned int uboot_size) +static void copy_romstage(uint32_t spi_addr, uint32_t sram_addr, unsigned int len) +{ + int upto, todo; + int i; +// struct exynos_spi *regs = (struct exynos_spi *)samsung_get_base_spi1(); + struct exynos_spi *regs = (struct exynos_spi *)0x12d30000; + + clock_set_rate(PERIPH_ID_SPI1, 50000000); /* set spi clock to 50Mhz */ + /* set the spi1 GPIO */ +// exynos_pinmux_config(PERIPH_ID_SPI1, PINMUX_FLAG_NONE); + gpio_cfg_pin(GPIO_A24, 0x2); + gpio_cfg_pin(GPIO_A25, 0x2); + gpio_cfg_pin(GPIO_A26, 0x2); + gpio_cfg_pin(GPIO_A27, 0x2); + + /* set pktcnt and enable it */ + writel(4 | SPI_PACKET_CNT_EN, ®s->pkt_cnt); + /* set FB_CLK_SEL */ + writel(SPI_FB_DELAY_180, ®s->fb_clk); + /* set CH_WIDTH and BUS_WIDTH as word */ + setbits_le32(®s->mode_cfg, SPI_MODE_CH_WIDTH_WORD | + SPI_MODE_BUS_WIDTH_WORD); + clrbits_le32(®s->ch_cfg, SPI_CH_CPOL_L); /* CPOL: active high */ + + /* clear rx and tx channel if set priveously */ + clrbits_le32(®s->ch_cfg, SPI_RX_CH_ON | SPI_TX_CH_ON); + + setbits_le32(®s->swap_cfg, SPI_RX_SWAP_EN | + SPI_RX_BYTE_SWAP | + SPI_RX_HWORD_SWAP); + + /* do a soft reset */ + setbits_le32(®s->ch_cfg, SPI_CH_RST); + clrbits_le32(®s->ch_cfg, SPI_CH_RST); + + /* now set rx and tx channel ON */ + setbits_le32(®s->ch_cfg, SPI_RX_CH_ON | SPI_TX_CH_ON | SPI_CH_HS_EN); + clrbits_le32(®s->cs_reg, SPI_SLAVE_SIG_INACT); /* CS low */ + + /* Send read instruction (0x3h) followed by a 24 bit addr */ + writel((SF_READ_DATA_CMD << 24) | spi_addr, ®s->tx_data); + + /* waiting for TX done */ + while (!(readl(®s->spi_sts) & SPI_ST_TX_DONE)); + + for (upto = 0, i = 0; upto < len; upto += todo, i++) { + todo = MIN(len - upto, (1 << 15)); + spi_rx_tx(regs, todo, (void *)(sram_addr), + (void *)(spi_addr), i); + } + + setbits_le32(®s->cs_reg, SPI_SLAVE_SIG_INACT);/* make the CS high */ + + /* + * Let put controller mode to BYTE as + * SPI driver does not support WORD mode yet + */ + clrbits_le32(®s->mode_cfg, SPI_MODE_CH_WIDTH_WORD | + SPI_MODE_BUS_WIDTH_WORD); + writel(0, ®s->swap_cfg); + + /* + * Flush spi tx, rx fifos and reset the SPI controller + * and clear rx/tx channel + */ + clrsetbits_le32(®s->ch_cfg, SPI_CH_HS_EN, SPI_CH_RST); + clrbits_le32(®s->ch_cfg, SPI_CH_RST); + clrbits_le32(®s->ch_cfg, SPI_TX_CH_ON | SPI_RX_CH_ON); +} + +void bootblock_mainboard_init(void); +void bootblock_mainboard_init(void) +{ + /* Copy romstage data from SPI ROM to SRAM */ + /* FIXME: magic constant... */ + copy_romstage(0x2000, CONFIG_ROMSTAGE_BASE, CONFIG_XIP_ROM_SIZE); +}