[coreboot] New patch to review for coreboot: 2c20eab WIP: first attempt at porting SPI driver into bootblock

David Hendricks (dhendrix@chromium.org) gerrit at coreboot.org
Sat Jan 12 00:39:08 CET 2013


David Hendricks (dhendrix at 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 at 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 at 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 at 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(&regs->ch_cfg, SPI_CH_RST);
+	clrbits_le32(&regs->ch_cfg, SPI_CH_RST);
+	writel(((todo * 8) / 32) | SPI_PACKET_CNT_EN, &regs->pkt_cnt);
+
+	while (in_bytes) {
+		uint32_t spi_sts;
+		int temp;
+
+		spi_sts = readl(&regs->spi_sts);
+		rx_lvl = ((spi_sts >> 15) & 0x7f);
+		tx_lvl = ((spi_sts >> 6) & 0x7f);
+		while (tx_lvl < 32 && out_bytes) {
+			temp = 0xffffffff;
+			writel(temp, &regs->tx_data);
+			out_bytes -= 4;
+			tx_lvl += 4;
+		}
+		while (rx_lvl >= 4 && in_bytes) {
+			temp = readl(&regs->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, &regs->pkt_cnt);
+	/* set FB_CLK_SEL */
+	writel(SPI_FB_DELAY_180, &regs->fb_clk);
+	/* set CH_WIDTH and BUS_WIDTH as word */
+	setbits_le32(&regs->mode_cfg, SPI_MODE_CH_WIDTH_WORD |
+					SPI_MODE_BUS_WIDTH_WORD);
+	clrbits_le32(&regs->ch_cfg, SPI_CH_CPOL_L); /* CPOL: active high */
+
+	/* clear rx and tx channel if set priveously */
+	clrbits_le32(&regs->ch_cfg, SPI_RX_CH_ON | SPI_TX_CH_ON);
+
+	setbits_le32(&regs->swap_cfg, SPI_RX_SWAP_EN |
+		SPI_RX_BYTE_SWAP |
+		SPI_RX_HWORD_SWAP);
+
+	/* do a soft reset */
+	setbits_le32(&regs->ch_cfg, SPI_CH_RST);
+	clrbits_le32(&regs->ch_cfg, SPI_CH_RST);
+
+	/* now set rx and tx channel ON */
+	setbits_le32(&regs->ch_cfg, SPI_RX_CH_ON | SPI_TX_CH_ON | SPI_CH_HS_EN);
+	clrbits_le32(&regs->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, &regs->tx_data);
+
+	/* waiting for TX done */
+	while (!(readl(&regs->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(&regs->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(&regs->mode_cfg, SPI_MODE_CH_WIDTH_WORD |
+					SPI_MODE_BUS_WIDTH_WORD);
+	writel(0, &regs->swap_cfg);
+
+	/*
+	 * Flush spi tx, rx fifos and reset the SPI controller
+	 * and clear rx/tx channel
+	 */
+	clrsetbits_le32(&regs->ch_cfg, SPI_CH_HS_EN, SPI_CH_RST);
+	clrbits_le32(&regs->ch_cfg, SPI_CH_RST);
+	clrbits_le32(&regs->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);
+}



More information about the coreboot mailing list