[PATCH 1/2] qi-add-s3c2410-cpu.patch

Micael Henriksson micael.h at gmail.com
Wed Nov 12 00:29:10 CET 2008


This patch add support for s3c2410 in Qi.

Signed-off-by: Micael Henriksson <micael.h at gmail.com>
---

 include/neo_gta01.h                   |   30 ++
 include/pcf50606.h                    |  260 +++++++++++++++
 src/cpu/s3c2410/gta01.c               |  291 +++++++++++++++++
 src/cpu/s3c2410/i2c-bitbang-s3c24xx.c |   68 ++++
 src/cpu/s3c2410/lowlevel_init.S       |  162 +++++++++
 src/cpu/s3c2410/nand_read.c           |  136 ++++++++
 src/cpu/s3c2410/nand_read.h           |   22 +
 src/cpu/s3c2410/qi.lds                |   63 ++++
 src/cpu/s3c2410/s3c24xx-mci.c         |  565 +++++++++++++++++++++++++++++++++
 src/cpu/s3c2410/serial-s3c24xx.c      |   77 ++++
 src/cpu/s3c2410/start.S               |  304 ++++++++++++++++++
 src/cpu/s3c2410/start_qi.c            |  131 ++++++++
 12 files changed, 2109 insertions(+), 0 deletions(-)
 create mode 100644 include/neo_gta01.h
 create mode 100644 include/pcf50606.h
 create mode 100644 src/cpu/s3c2410/gta01.c
 create mode 100644 src/cpu/s3c2410/i2c-bitbang-s3c24xx.c
 create mode 100644 src/cpu/s3c2410/lowlevel_init.S
 create mode 100644 src/cpu/s3c2410/nand_read.c
 create mode 100644 src/cpu/s3c2410/nand_read.h
 create mode 100644 src/cpu/s3c2410/qi.lds
 create mode 100644 src/cpu/s3c2410/s3c24xx-mci.c
 create mode 100644 src/cpu/s3c2410/serial-s3c24xx.c
 create mode 100644 src/cpu/s3c2410/start.S
 create mode 100644 src/cpu/s3c2410/start_qi.c

diff --git a/include/neo_gta01.h b/include/neo_gta01.h
new file mode 100644
index 0000000..6b4b174
--- /dev/null
+++ b/include/neo_gta01.h
@@ -0,0 +1,30 @@
+/*
+ * (C) Copyright 2007 OpenMoko, Inc.
+ * Author: xiangfu liu <xiangfu at openmoko.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#ifndef __CONFIG_H
+#define __CONFIG_H
+#ifndef __ASM_MODE__
+#include <qi.h>
+extern const struct board_api board_api_gta01;
+#endif
+
+#define TEXT_BASE 0x33000000
+
+#endif	/* __CONFIG_H */
diff --git a/include/pcf50606.h b/include/pcf50606.h
new file mode 100644
index 0000000..b0a1807
--- /dev/null
+++ b/include/pcf50606.h
@@ -0,0 +1,260 @@
+#ifndef _PCF50606_H
+#define _PCF50606_H
+
+/* Philips PCF50606 Power Managemnt Unit (PMU) driver
+ * (C) 2006-2007 by OpenMoko, Inc.
+ * Author: Harald Welte <laforge at openmoko.org>
+ *
+ */
+
+enum pfc50606_regs {
+	PCF50606_REG_ID		= 0x00,
+	PCF50606_REG_OOCS	= 0x01,
+	PCF50606_REG_INT1	= 0x02,	/* Interrupt Status */
+	PCF50606_REG_INT2	= 0x03,	/* Interrupt Status */
+	PCF50606_REG_INT3	= 0x04,	/* Interrupt Status */
+	PCF50606_REG_INT1M	= 0x05,	/* Interrupt Mask */
+	PCF50606_REG_INT2M	= 0x06,	/* Interrupt Mask */
+	PCF50606_REG_INT3M	= 0x07,	/* Interrupt Mask */
+	PCF50606_REG_OOCC1	= 0x08,
+	PCF50606_REG_OOCC2	= 0x09,
+	PCF50606_REG_RTCSC	= 0x0a,	/* Second */
+	PCF50606_REG_RTCMN	= 0x0b,	/* Minute */
+	PCF50606_REG_RTCHR	= 0x0c,	/* Hour */
+	PCF50606_REG_RTCWD	= 0x0d,	/* Weekday */
+	PCF50606_REG_RTCDT	= 0x0e,	/* Day */
+	PCF50606_REG_RTCMT	= 0x0f,	/* Month */
+	PCF50606_REG_RTCYR	= 0x10,	/* Year */
+	PCF50606_REG_RTCSCA	= 0x11, /* Alarm Second */
+	PCF50606_REG_RTCMNA	= 0x12, /* Alarm Minute */
+	PCF50606_REG_RTCHRA	= 0x13, /* Alarm Hour */
+	PCF50606_REG_RTCWDA	= 0x14, /* Alarm Weekday */
+	PCF50606_REG_RTCDTA	= 0x15, /* Alarm Day */
+	PCF50606_REG_RTCMTA	= 0x16, /* Alarm Month */
+	PCF50606_REG_RTCYRA	= 0x17, /* Alarm Year */
+	PCF50606_REG_PSSC	= 0x18,	/* Power sequencing */
+	PCF50606_REG_PWROKM	= 0x19,	/* PWROK mask */
+	PCF50606_REG_PWROKS	= 0x1a,	/* PWROK status */
+	PCF50606_REG_DCDC1	= 0x1b,
+	PCF50606_REG_DCDC2	= 0x1c,
+	PCF50606_REG_DCDC3	= 0x1d,
+	PCF50606_REG_DCDC4	= 0x1e,
+	PCF50606_REG_DCDEC1	= 0x1f,
+	PCF50606_REG_DCDEC2	= 0x20,
+	PCF50606_REG_DCUDC1	= 0x21,
+	PCF50606_REG_DCUDC2	= 0x22,
+	PCF50606_REG_IOREGC	= 0x23,
+	PCF50606_REG_D1REGC1	= 0x24,
+	PCF50606_REG_D2REGC1	= 0x25,
+	PCF50606_REG_D3REGC1	= 0x26,
+	PCF50606_REG_LPREGC1	= 0x27,
+	PCF50606_REG_LPREGC2	= 0x28,
+	PCF50606_REG_MBCC1	= 0x29,
+	PCF50606_REG_MBCC2	= 0x2a,
+	PCF50606_REG_MBCC3	= 0x2b,
+	PCF50606_REG_MBCS1	= 0x2c,
+	PCF50606_REG_BBCC	= 0x2d,
+	PCF50606_REG_ADCC1	= 0x2e,
+	PCF50606_REG_ADCC2	= 0x2f,
+	PCF50606_REG_ADCS1	= 0x30,
+	PCF50606_REG_ADCS2	= 0x31,
+	PCF50606_REG_ADCS3	= 0x32,
+	PCF50606_REG_ACDC1	= 0x33,
+	PCF50606_REG_BVMC	= 0x34,
+	PCF50606_REG_PWMC1	= 0x35,
+	PCF50606_REG_LEDC1	= 0x36,
+	PCF50606_REG_LEDC2	= 0x37,
+	PCF50606_REG_GPOC1	= 0x38,
+	PCF50606_REG_GPOC2	= 0x39,
+	PCF50606_REG_GPOC3	= 0x3a,
+	PCF50606_REG_GPOC4	= 0x3b,
+	PCF50606_REG_GPOC5	= 0x3c,
+	__NUM_PCF50606_REGS
+};
+
+enum pcf50606_reg_oocs {
+	PFC50606_OOCS_ONKEY	= 0x01,
+	PCF50606_OOCS_EXTON	= 0x02,
+	PCF50606_OOCS_PWROKRST	= 0x04,
+	PCF50606_OOCS_BATOK	= 0x08,
+	PCF50606_OOCS_BACKOK	= 0x10,
+	PCF50606_OOCS_CHGOK	= 0x20,
+	PCF50606_OOCS_TEMPOK	= 0x40,
+	PCF50606_OOCS_WDTEXP	= 0x80,
+};
+
+enum pcf50606_reg_oocc1 {
+	PCF50606_OOCC1_GOSTDBY	= 0x01,
+	PCF50606_OOCC1_TOTRST	= 0x02,
+	PCF50606_OOCC1_CLK32ON	= 0x04,
+	PCF50606_OOCC1_WDTRST	= 0x08,
+	PCF50606_OOCC1_RTCWAK	= 0x10,
+	PCF50606_OOCC1_CHGWAK	= 0x20,
+	PCF50606_OOCC1_EXTONWAK_HIGH	= 0x40,
+	PCF50606_OOCC1_EXTONWAK_LOW	= 0x80,
+	PCF50606_OOCC1_EXTONWAK_NO_WAKEUP = 0x3f,
+};
+
+enum pcf50606_reg_oocc2 {
+	PCF50606_OOCC2_ONKEYDB_NONE	= 0x00,
+	PCF50606_OOCC2_ONKEYDB_14ms	= 0x01,
+	PCF50606_OOCC2_ONKEYDB_62ms	= 0x02,
+	PCF50606_OOCC2_ONKEYDB_500ms	= 0x03,
+	PCF50606_OOCC2_EXTONDB_NONE	= 0x00,
+	PCF50606_OOCC2_EXTONDB_14ms	= 0x04,
+	PCF50606_OOCC2_EXTONDB_62ms	= 0x08,
+	PCF50606_OOCC2_EXTONDB_500ms	= 0x0c,
+};
+
+enum pcf50606_reg_int1 {
+	PCF50606_INT1_ONKEYR	= 0x01,	/* ONKEY rising edge */
+	PCF50606_INT1_ONKEYF	= 0x02,	/* ONKEY falling edge */
+	PCF50606_INT1_ONKEY1S	= 0x04,	/* OMKEY at least 1sec low */
+	PCF50606_INT1_EXTONR	= 0x08,	/* EXTON rising edge */
+	PCF50606_INT1_EXTONF	= 0x10,	/* EXTON falling edge */
+	PCF50606_INT1_SECOND	= 0x40,	/* RTC periodic second interrupt */
+	PCF50606_INT1_ALARM	= 0x80, /* RTC alarm time is reached */
+};
+
+enum pcf50606_reg_int2 {
+	PCF50606_INT2_CHGINS	= 0x01, /* Charger inserted */
+	PCF50606_INT2_CHGRM	= 0x02, /* Charger removed */
+	PCF50606_INT2_CHGFOK	= 0x04,	/* Fast charging OK */
+	PCF50606_INT2_CHGERR	= 0x08,	/* Error in charging mode */
+	PCF50606_INT2_CHGFRDY	= 0x10,	/* Fast charge completed */
+	PCF50606_INT2_CHGPROT	= 0x20,	/* Charging protection interrupt */
+	PCF50606_INT2_CHGWD10S	= 0x40,	/* Charger watchdig expires in 10s */
+	PCF50606_INT2_CHGWDEXP	= 0x80,	/* Charger watchdog expires */
+};
+
+enum pcf50606_reg_int3 {
+	PCF50606_INT3_ADCRDY	= 0x01,	/* ADC conversion finished */
+	PCF50606_INT3_ACDINS	= 0x02,	/* Accessory inserted */
+	PCF50606_INT3_ACDREM	= 0x04, /* Accessory removed */
+	PCF50606_INT3_TSCPRES	= 0x08,	/* Touch screen pressed */
+	PCF50606_INT3_LOWBAT	= 0x40,	/* Low battery voltage */
+	PCF50606_INT3_HIGHTMP	= 0x80, /* High temperature */
+};
+
+/* used by PSSC, PWROKM, PWROKS, */
+enum pcf50606_regu {
+	PCF50606_REGU_DCD	= 0x01,	/* DCD in phase 2 */
+	PCF50606_REGU_DCDE	= 0x02,	/* DCDE in phase 2 */
+	PCF50606_REGU_DCUD	= 0x04,	/* DCDU in phase 2 */
+	PCF50606_REGU_IO	= 0x08,	/* IO in phase 2 */
+	PCF50606_REGU_D1	= 0x10, /* D1 in phase 2 */
+	PCF50606_REGU_D2	= 0x20,	/* D2 in phase 2 */
+	PCF50606_REGU_D3	= 0x40,	/* D3 in phase 2 */
+	PCF50606_REGU_LP	= 0x80,	/* LP in phase 2 */
+};
+
+enum pcf50606_reg_dcdc4 {
+	PCF50606_DCDC4_MODE_AUTO	= 0x00,
+	PCF50606_DCDC4_MODE_PWM		= 0x01,
+	PCF50606_DCDC4_MODE_PCF		= 0x02,
+	PCF50606_DCDC4_OFF_FLOAT	= 0x00,
+	PCF50606_DCDC4_OFF_BYPASS	= 0x04,
+	PCF50606_DCDC4_OFF_PULLDOWN	= 0x08,
+	PCF50606_DCDC4_CURLIM_500mA	= 0x00,
+	PCF50606_DCDC4_CURLIM_750mA	= 0x10,
+	PCF50606_DCDC4_CURLIM_1000mA	= 0x20,
+	PCF50606_DCDC4_CURLIM_1250mA	= 0x30,
+	PCF50606_DCDC4_TOGGLE		= 0x40,
+	PCF50606_DCDC4_REGSEL_DCDC2	= 0x80,
+};
+
+enum pcf50606_reg_dcdec2 {
+	PCF50606_DCDEC2_MODE_AUTO	= 0x00,
+	PCF50606_DCDEC2_MODE_PWM	= 0x01,
+	PCF50606_DCDEC2_MODE_PCF	= 0x02,
+	PCF50606_DCDEC2_OFF_FLOAT	= 0x00,
+	PCF50606_DCDEC2_OFF_BYPASS	= 0x04,
+};
+
+enum pcf50606_reg_dcudc2 {
+	PCF50606_DCUDC2_MODE_AUTO	= 0x00,
+	PCF50606_DCUDC2_MODE_PWM	= 0x01,
+	PCF50606_DCUDC2_MODE_PCF	= 0x02,
+	PCF50606_DCUDC2_OFF_FLOAT	= 0x00,
+	PCF50606_DCUDC2_OFF_BYPASS	= 0x04,
+};
+
+enum pcf50606_reg_adcc1 {
+	PCF50606_ADCC1_TSCMODACT	= 0x01,
+	PCF50606_ADCC1_TSCMODSTB	= 0x02,
+	PCF50606_ADCC1_TRATSET		= 0x04,
+	PCF50606_ADCC1_NTCSWAPE		= 0x08,
+	PCF50606_ADCC1_NTCSWAOFF	= 0x10,
+	PCF50606_ADCC1_EXTSYNCBREAK	= 0x20,
+	/* reserved */
+	PCF50606_ADCC1_TSCINT		= 0x80,
+};
+
+enum pcf50606_reg_adcc2 {
+	PCF50606_ADCC2_ADCSTART		= 0x01,
+	/* see enum pcf50606_adcc2_adcmux */
+	PCF50606_ADCC2_SYNC_NONE	= 0x00,
+	PCF50606_ADCC2_SYNC_TXON	= 0x20,
+	PCF50606_ADCC2_SYNC_PWREN1	= 0x40,
+	PCF50606_ADCC2_SYNC_PWREN2	= 0x60,
+	PCF50606_ADCC2_RES_10BIT	= 0x00,
+	PCF50606_ADCC2_RES_8BIT		= 0x80,
+};
+
+#define PCF50606_ADCC2_ADCMUX_MASK	(0xf << 1)
+
+#define ADCMUX_SHIFT	1
+enum pcf50606_adcc2_adcmux {
+	PCF50606_ADCMUX_BATVOLT_RES	= 0x0 << ADCMUX_SHIFT,
+	PCF50606_ADCMUX_BATVOLT_SUBTR	= 0x1 << ADCMUX_SHIFT,
+	PCF50606_ADCMUX_ADCIN1_RES	= 0x2 << ADCMUX_SHIFT,
+	PCF50606_ADCMUX_ADCIN1_SUBTR	= 0x3 << ADCMUX_SHIFT,
+	PCF50606_ADCMUX_BATTEMP		= 0x4 << ADCMUX_SHIFT,
+	PCF50606_ADCMUX_ADCIN2		= 0x5 << ADCMUX_SHIFT,
+	PCF50606_ADCMUX_ADCIN3		= 0x6 << ADCMUX_SHIFT,
+	PCF50606_ADCMUX_ADCIN3_RATIO	= 0x7 << ADCMUX_SHIFT,
+	PCF50606_ADCMUX_XPOS		= 0x8 << ADCMUX_SHIFT,
+	PCF50606_ADCMUX_YPOS		= 0x9 << ADCMUX_SHIFT,
+	PCF50606_ADCMUX_P1		= 0xa << ADCMUX_SHIFT,
+	PCF50606_ADCMUX_P2		= 0xb << ADCMUX_SHIFT,
+	PCF50606_ADCMUX_BATVOLT_ADCIN1	= 0xc << ADCMUX_SHIFT,
+	PCF50606_ADCMUX_XY_SEQUENCE	= 0xe << ADCMUX_SHIFT,
+	PCF50606_P1_P2_RESISTANCE	= 0xf << ADCMUX_SHIFT,
+};
+
+enum pcf50606_adcs2 {
+	PCF50606_ADCS2_ADCRDY		= 0x80,
+};
+
+enum pcf50606_reg_mbcc1 {
+	PCF50606_MBCC1_CHGAPE		= 0x01,
+	PCF50606_MBCC1_AUTOFST		= 0x02,
+#define	PCF50606_MBCC1_CHGMOD_MASK	  0x1c
+#define	PCF50606_MBCC1_CHGMOD_SHIFT	  2
+	PCF50606_MBCC1_CHGMOD_QUAL	= 0x00,
+	PCF50606_MBCC1_CHGMOD_PRE	= 0x04,
+	PCF50606_MBCC1_CHGMOD_TRICKLE	= 0x08,
+	PCF50606_MBCC1_CHGMOD_FAST_CCCV	= 0x0c,
+	PCF50606_MBCC1_CHGMOD_FAST_NOCC	= 0x10,
+	PCF50606_MBCC1_CHGMOD_FAST_NOCV	= 0x14,
+	PCF50606_MBCC1_CHGMOD_FAST_SW	= 0x18,
+	PCF50606_MBCC1_CHGMOD_IDLE	= 0x1c,
+	PCF50606_MBCC1_DETMOD_LOWCHG	= 0x20,
+	PCF50606_MBCC1_DETMOD_WDRST	= 0x40,
+};
+
+enum pcf50606_reg_bvmc {
+	PCF50606_BVMC_LOWBAT		= 0x01,
+	PCF50606_BVMC_THRSHLD_NULL	= 0x00,
+	PCF50606_BVMC_THRSHLD_2V8	= 0x02,
+	PCF50606_BVMC_THRSHLD_2V9	= 0x04,
+	PCF50606_BVMC_THRSHLD_3V	= 0x08,
+	PCF50606_BVMC_THRSHLD_3V1	= 0x08,
+	PCF50606_BVMC_THRSHLD_3V2	= 0x0a,
+	PCF50606_BVMC_THRSHLD_3V3	= 0x0c,
+	PCF50606_BVMC_THRSHLD_3V4	= 0x0e,
+	PCF50606_BVMC_DISDB		= 0x10,
+};
+
+#endif /* _PCF50606_H */
+
diff --git a/src/cpu/s3c2410/gta01.c b/src/cpu/s3c2410/gta01.c
new file mode 100644
index 0000000..5d97daa
--- /dev/null
+++ b/src/cpu/s3c2410/gta01.c
@@ -0,0 +1,291 @@
+/*
+ * (C) Copyright 2007 OpenMoko, Inc.
+ * Author: Andy Green <andy at openmoko.com>
+ *
+ * Configuation settings for the OPENMOKO Neo GTA02 Linux GSM phone
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ */
+
+#include <qi.h>
+#include <neo_gta01.h>
+#include <serial-s3c24xx.h>
+#include <ports-s3c24xx.h>
+#include <s3c24xx-mci.h>
+#include <i2c-bitbang-s3c24xx.h>
+#include <pcf50606.h>
+
+#define GTA01_DEBUG_UART 0
+#define PCF50606_I2C_ADS 0x08
+
+
+struct pcf50606_init {
+        u8 index;
+        u8 value;
+};
+
+/* initial register set for PCF50606 in Neo1973 devices */
+const struct pcf50606_init pcf50606_initial_regs[] = {
+	{ PCF50606_REG_OOCS,	0x00 },
+	{ PCF50606_REG_INT1M,	0x00 },
+	{ PCF50606_REG_INT2M,	0x00 },
+	{ PCF50606_REG_INT3M,	PCF50606_INT3_TSCPRES },
+	{ PCF50606_REG_OOCC1, 	PCF50606_OOCC1_RTCWAK | PCF50606_OOCC1_CHGWAK |
+				  PCF50606_OOCC1_EXTONWAK_HIGH },
+	{ PCF50606_REG_OOCC2,	PCF50606_OOCC2_ONKEYDB_14ms | PCF50606_OOCC2_EXTONDB_14ms },
+	{ PCF50606_REG_PSSC,	0x00 },
+	{ PCF50606_REG_PWROKM,	0x00 },
+	{ PCF50606_REG_DCDC1,	0x18 },	/* GL_1V5: off */
+	{ PCF50606_REG_DCDC2,	0x00 },
+	{ PCF50606_REG_DCDC3,	0x00 },
+	{ PCF50606_REG_DCDC4,	0x30 }, /* 1.25A */
+
+	{ PCF50606_REG_DCDEC1,	0xe8 }, /* IO_3V3: on */
+	{ PCF50606_REG_DCDEC2,	0x00 },
+
+	{ PCF50606_REG_DCUDC1,	0xc4 }, /* CORE_1V8: 2.1V if PWREN2 = HIGH */
+	{ PCF50606_REG_DCUDC2,	0x30 }, /* 1.25A current limit */
+
+	{ PCF50606_REG_IOREGC,	0xf8 }, /* CODEC_3V3: on */
+	{ PCF50606_REG_D1REGC1,	0x16 }, /* BT_3V15: off */
+
+	{ PCF50606_REG_D2REGC1,	0x10 }, /* GL_2V5: off */
+
+	{ PCF50606_REG_D3REGC1,	0xec }, /* STBY_1V8: 2.1V */
+
+	{ PCF50606_REG_LPREGC1,	0xf8 }, /* LCM_3V3: on */
+	{ PCF50606_REG_LPREGC2,	0x00 },
+
+	{ PCF50606_REG_MBCC1,	0x01 }, /* CHGAPE */
+	{ PCF50606_REG_MBCC2,	0x00 },	/* unlimited charging */
+	{ PCF50606_REG_MBCC3,	0x1a }, /* 0.2*Ifast, 4.20V */
+	{ PCF50606_REG_BBCC,	0x1f }, /* 400uA */
+	{ PCF50606_REG_ADCC1,	0x00 },
+	{ PCF50606_REG_ADCC2,	0x00 },
+	{ PCF50606_REG_ACDC1,	0x86 },	/* ACD thresh 1.6V, enabled */
+	{ PCF50606_REG_BVMC,	PCF50606_BVMC_THRSHLD_3V3 },
+	{ PCF50606_REG_PWMC1,	0x00 },
+	{ PCF50606_REG_LEDC1,	0x00 },
+	{ PCF50606_REG_LEDC2,	0x00 },
+	{ PCF50606_REG_GPOC1,	0x00 },
+	{ PCF50606_REG_GPOC2,	0x00 },
+	{ PCF50606_REG_GPOC3,	0x00 },
+	{ PCF50606_REG_GPOC4,	0x00 },
+	{ PCF50606_REG_GPOC5,	0x00 },
+};
+
+
+static const struct board_variant board_variants[] = {
+	[0] = {
+		.name = "Bv4",
+		.machine_revision = 0x240,
+	}
+};
+
+
+void port_init_gta01(void)
+{
+	int n;
+	unsigned int * MPLLCON = (unsigned int *)0x4c000004;
+
+	rGPACON = 0x005E0FFF;
+	rGPADAT = 0x00010000; /* nNAND_WP set high */
+
+	rGPBCON = 0x00045455;
+	rGPBUP  = 0x000007FF; 
+	rGPBDAT = 0x00000004; /* SD-card pwr off */
+
+	rGPCCON = 0xAAAA12A9;
+	rGPCUP  = 0x0000FFFF;
+
+	rGPDCON = 0xAAAAAAAA;
+	rGPDUP  = 0x0000FFFF;
+
+	rGPECON = 0xAAAAAAAA;
+	rGPEUP  = 0x0000FFFF;
+
+	rGPFCON = 0x0000aa99;
+	rGPFUP  = 0x000000FF;
+	rGPFDAT = 0x00000004;
+
+	rGPGCON = 0xFF14F0F8;
+	rGPGUP  = 0x0000AFEF;
+
+	rGPHCON = 0x0000FAAA;
+	rGPHUP  = 0x000007FF;
+
+
+	/* Load PMU with safe values */
+
+	for (n = 0; n < ARRAY_SIZE(pcf50606_initial_regs); n++)
+		i2c_write_sync(&bb_s3c24xx, PCF50606_I2C_ADS,
+				pcf50606_initial_regs[n].index,
+				pcf50606_initial_regs[n].value);
+
+	/* Give a short vibrate notification */
+	rGPBDAT |= (1 << 3);
+	udelay(1000000);
+	rGPBDAT &= ~(1 << 3);
+
+
+	/* change CPU to 266MHz 1:2:4 */
+	*MPLLCON = ((0x7d << 12) + (0x1 << 4) + 0x1);
+	/* Delay after update of PLL: Page 7-19, seven nops */
+	asm __volatile__ (
+		"nop\n"\
+		"nop\n"\
+		"nop\n"\
+		"nop\n"\
+		"nop\n"\
+		"nop\n"\
+		"nop\n"\
+	);
+
+	
+	/* set debug UART working at 115kbps */
+	serial_init_115200_s3c24xx(GTA01_DEBUG_UART, 66 /* 66.5MHz PCLK */);
+}
+
+
+int sd_card_init_gta01(void)
+{
+	int retval = -1;
+
+	/* Check if AUX is held. Then skip SD-card kernels!
+	 * FIXME: This would be nicer to do with an API.
+	 */
+	if (!(rGPFDAT & (1 << 6))) {
+		return -1;
+	}
+
+	/* if SD card inserted, power it up and initialize*/
+	if (!(rGPFDAT & (1 << 5)))
+	{
+		rGPBDAT &= ~(1 << 2);
+		retval = s3c24xx_mmc_init(1);
+	}
+	return retval;
+}
+
+int sd_card_block_read_gta01(unsigned char * buf, unsigned long start512,
+				int blocks512)
+{
+	return s3c24xx_mmc_bread(0, start512, blocks512, buf);
+}
+
+int is_this_board_gta01(void)
+{
+	/* FIXME: How to check for GTA01 ? */
+        return 1;
+}
+
+const struct board_variant const * get_board_variant_gta01(void)
+{
+	return &board_variants[0];
+}
+
+static __attribute__ (( section (".steppingstone") )) void putc_gta01(char c)
+{
+	serial_putc_s3c24xx(GTA01_DEBUG_UART, c);
+}
+
+static void close_gta01(void)
+{
+	/* set I2C GPIO back to peripheral unit */
+	(bb_s3c24xx.close)();
+}
+
+/*
+ * API for bootloader on this machine
+ */
+
+const struct board_api board_api_gta01 = {
+	.name = "Neo1973 GTA01",
+	.linux_machine_id = 1182,
+	.linux_mem_start = 0x30000000,
+	.linux_mem_size = (128 * 1024 * 1024),
+	.linux_tag_placement = 0x30000000 + 0x100,
+	.get_board_variant = get_board_variant_gta01,
+	.is_this_board = is_this_board_gta01,
+	.port_init = port_init_gta01,
+	.putc = putc_gta01,
+	.close = close_gta01,
+	/* these are the ways we could boot GTA01 in order to try */
+	.kernel_source = {
+		[0] = {
+			.name = "SD Card EXT2 Kernel p1",
+			.block_init = sd_card_init_gta01,
+			.block_read = sd_card_block_read_gta01,
+			.partition_index = 1,
+			.filesystem = FS_EXT2,
+			.filepath = "boot/uImage",
+			.commandline = "mtdparts=" \
+					"neo1973-nand:" \
+					 "0x00040000(qi)," \
+					 "0x00004000(u-boot_env)," \
+					 "0x00200000(kernel)," \
+					 "0x000a0000(splash)," \
+					 "0x03d1c000(rootfs) " \
+				       "rootfstype=ext3 " \
+				       "root=/dev/mmcblk0p2 " \
+				       "console=ttySAC0,115200 " \
+				       "loglevel=4 " \
+				       "init=/sbin/init "\
+				       "ro"
+		},
+		[1] = {
+			.name = "SD Card EXT2 Kernel p2",
+			.block_init = sd_card_init_gta01,
+			.block_read = sd_card_block_read_gta01,
+			.partition_index = 2,
+			.filesystem = FS_EXT2,
+			.filepath = "boot/uImage",
+			.commandline = "mtdparts=" \
+					"neo1973-nand:" \
+					 "0x00040000(qi)," \
+					 "0x00004000(u-boot_env)," \
+					 "0x00200000(kernel)," \
+					 "0x000a0000(splash)," \
+					 "0x03d1c000(rootfs) " \
+				       "rootfstype=ext3 " \
+				       "root=/dev/mmcblk0p2 " \
+				       "console=ttySAC0,115200 " \
+				       "loglevel=4 " \
+				       "init=/sbin/init "\
+				       "ro"
+		},
+		[2] = {
+			.name = "NAND Kernel",
+			.block_read = nand_read_ll,
+			.offset_blocks512_if_no_partition = 0x44000 / 512,
+			.filesystem = FS_RAW,
+			.commandline = "mtdparts=" \
+					"neo1973-nand:" \
+					 "0x00040000(qi)," \
+					 "0x00004000(u-boot_env)," \
+					 "0x00200000(kernel)," \
+					 "0x000a0000(splash)," \
+					 "0x03d1c000(rootfs) " \
+				       "rootfstype=jffs2 " \
+				       "root=/dev/mtdblock4 " \
+				       "console=ttySAC0,115200 " \
+				       "loglevel=4 " \
+				       "init=/sbin/init "\
+				       "ro"
+		},
+	},
+};
diff --git a/src/cpu/s3c2410/i2c-bitbang-s3c24xx.c b/src/cpu/s3c2410/i2c-bitbang-s3c24xx.c
new file mode 100644
index 0000000..c2d46ed
--- /dev/null
+++ b/src/cpu/s3c2410/i2c-bitbang-s3c24xx.c
@@ -0,0 +1,68 @@
+/*
+ * (C) Copyright 2007 OpenMoko, Inc.
+ * Author: Andy Green <andy at openmoko.com>
+ *
+ * s3c24xx-specific i2c shared by, eg, GTA02 and GTA03
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ */
+
+#include <qi.h>
+#include <i2c-bitbang.h>
+#include <ports-s3c24xx.h>
+
+static char i2c_read_sda_s3c24xx(void)
+{
+	return (rGPEDAT & 0x8000) != 0;
+}
+
+static void i2c_set_s3c24xx(char clock, char data)
+{
+	if (clock) /* SCL <- input */
+		rGPECON = (rGPECON & ~0x30000000);
+	else { /* SCL <- output 0 */
+		rGPEDAT = (rGPEDAT & ~0x4000);
+		rGPECON = (rGPECON & ~0x30000000) | 0x10000000;
+	}
+	if (data) /* SDA <- input */
+		rGPECON = (rGPECON & ~0xc0000000);
+	else { /* SDA <- output 0 */
+		rGPEDAT = (rGPEDAT & ~0x8000);
+		rGPECON = (rGPECON & ~0xc0000000) | 0x40000000;
+	}
+}
+
+static void i2c_close_s3c24xx(void)
+{
+	/* set back to hardware I2C ready for Linux */
+	rGPECON = (rGPECON & ~0xf0000000) | 0xa0000000;
+}
+
+static void i2c_spin_s3c24xx(void)
+{
+	int n;
+
+	for (n = 0; n < 1000; n++)
+		rGPJDAT |= (1 << 5);
+}
+
+struct i2c_bitbang bb_s3c24xx = {
+	.read_sda = i2c_read_sda_s3c24xx,
+	.set = i2c_set_s3c24xx,
+	.spin = i2c_spin_s3c24xx,
+	.close = i2c_close_s3c24xx,
+};
diff --git a/src/cpu/s3c2410/lowlevel_init.S b/src/cpu/s3c2410/lowlevel_init.S
new file mode 100644
index 0000000..2b14373
--- /dev/null
+++ b/src/cpu/s3c2410/lowlevel_init.S
@@ -0,0 +1,162 @@
+/*
+ * Memory Setup stuff - taken from blob memsetup.S
+ *
+ * Modified for the FIC Neo1973 GTA01 by Harald Welte <laforge at openmoko.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+/* NOTE this stuff runs in steppingstone context! */
+
+
+/*
+ * #include <config.h>
+ * #include <version.h>
+ */
+#define __ASM_MODE__
+#include <neo_gta01.h>
+
+/*
+ *
+ * Taken from linux/arch/arm/boot/compressed/head-s3c2410.S
+ *
+ * Copyright (C) 2002 Samsung Electronics SW.LEE  <hitchcar at sec.samsung.com>
+ *
+ */
+
+#define BWSCON	0x48000000
+
+/* BWSCON */
+#define DW8		 	(0x0)
+#define DW16		 	(0x1)
+#define DW32		 	(0x2)
+#define WAIT		 	(0x1<<2)
+#define UBLB		 	(0x1<<3)
+
+#define B1_BWSCON	  	(DW16 + WAIT + UBLB)
+#define B2_BWSCON	  	(DW16)
+#define B3_BWSCON	  	(DW16 + WAIT + UBLB)
+#define B4_BWSCON	  	(DW16)
+#define B5_BWSCON	  	(DW16)
+#define B6_BWSCON	  	(DW32)
+#define B7_BWSCON	  	(DW32)
+
+/* BANK0CON */
+#define B0_Tacs		 	0x0	/*  0clk */
+#define B0_Tcos		 	0x0	/*  0clk */
+#define B0_Tacc		 	0x7	/* 14clk */
+#define B0_Tcoh		 	0x0	/*  0clk */
+#define B0_Tah		 	0x0	/*  0clk */
+#define B0_Tacp		 	0x0
+#define B0_PMC		 	0x0	/* normal */
+
+/* BANK1CON: Smedia Glamo 3362 (on GTA02) */
+#define B1_Tacs		 	0x0	/*  0clk */
+#define B1_Tcos		 	0x3	/*  4clk */
+#define B1_Tacc		 	0x3	/*  4clk */
+#define B1_Tcoh		 	0x3	/*  4clk */
+#define B1_Tah		 	0x0	/*  0clk */
+#define B1_Tacp		 	0x0
+#define B1_PMC		 	0x0
+
+#define B2_Tacs		 	0x0
+#define B2_Tcos		 	0x0
+#define B2_Tacc		 	0x7
+#define B2_Tcoh		 	0x0
+#define B2_Tah			0x0
+#define B2_Tacp		 	0x0
+#define B2_PMC		 	0x0
+
+#define B3_Tacs		 	0x0	/*  0clk */
+#define B3_Tcos		 	0x3	/*  4clk */
+#define B3_Tacc		 	0x7	/* 14clk */
+#define B3_Tcoh		 	0x1	/*  1clk */
+#define B3_Tah		 	0x0	/*  0clk */
+#define B3_Tacp		 	0x3	/*  6clk */
+#define B3_PMC		 	0x0	/* normal */
+
+#define B4_Tacs		 	0x0	/*  0clk */
+#define B4_Tcos		 	0x0	/*  0clk */
+#define B4_Tacc		 	0x7	/* 14clk */
+#define B4_Tcoh		 	0x0	/*  0clk */
+#define B4_Tah		 	0x0	/*  0clk */
+#define B4_Tacp		 	0x0
+#define B4_PMC		 	0x0	/* normal */
+
+#define B5_Tacs		 	0x0	/*  0clk */
+#define B5_Tcos		 	0x0	/*  0clk */
+#define B5_Tacc		 	0x7	/* 14clk */
+#define B5_Tcoh		 	0x0	/*  0clk */
+#define B5_Tah		 	0x0	/*  0clk */
+#define B5_Tacp		 	0x0
+#define B5_PMC		 	0x0	/* normal */
+
+#define B6_MT		 	0x3	/* SDRAM */
+#define B6_Trcd	 	 	0x1	/* 3clk */
+
+#define B6_SCAN		 	0x2	/* 10bit */
+#define B7_SCAN		 	0x2	/* 10bit */
+
+
+#define B7_MT		 	0x3	/* SDRAM */
+#define B7_Trcd		 	0x1	/* 3clk */
+
+/* REFRESH parameter */
+#define REFEN			0x1	/* Refresh enable */
+#define TREFMD		 	0x0	/* CBR(CAS before RAS)/Auto refresh */
+#define Trp	 		0x1	/* 3clk */
+#define Trc	 		0x3	/* 7clk */
+#define Tchr	 		0x2	/* 3clk */
+//#define REFCNT		 	1113	/* period=15.6us, HCLK=60Mhz, (2048+1-15.6*60) */
+#define REFCNT		 	997	/* period=17.5us, HCLK=60Mhz, (2048+1-15.6*60) */
+/**************************************/
+
+.globl lowlevel_init
+lowlevel_init:
+
+	ldr	r0, =SMRDATA
+	ldr	r1, =BWSCON	/* Bus Width Status Controller */
+	add     r2, r0, #13*4
+0:
+	ldr     r3, [r0], #4
+	str     r3, [r1], #4
+	cmp     r2, r0
+	bne     0b
+
+	/* setup asynchronous bus mode */
+	mrc	p15, 0, r1 ,c1 ,c0, 0
+	orr	r1, r1, #0xc0000000
+	mcr	p15, 0, r1, c1, c0, 0
+
+	/* everything is fine now */
+	mov	pc, lr
+
+	.ltorg
+/* the literal pools origin */
+SMRDATA:
+    .word (0+(B1_BWSCON<<4)+(B2_BWSCON<<8)+(B3_BWSCON<<12)+(B4_BWSCON<<16)+(B5_BWSCON<<20)+(B6_BWSCON<<24)+(B7_BWSCON<<28))
+    .word ((B0_Tacs<<13)+(B0_Tcos<<11)+(B0_Tacc<<8)+(B0_Tcoh<<6)+(B0_Tah<<4)+(B0_Tacp<<2)+(B0_PMC))
+    .word ((B1_Tacs<<13)+(B1_Tcos<<11)+(B1_Tacc<<8)+(B1_Tcoh<<6)+(B1_Tah<<4)+(B1_Tacp<<2)+(B1_PMC))
+    .word ((B2_Tacs<<13)+(B2_Tcos<<11)+(B2_Tacc<<8)+(B2_Tcoh<<6)+(B2_Tah<<4)+(B2_Tacp<<2)+(B2_PMC))
+    .word ((B3_Tacs<<13)+(B3_Tcos<<11)+(B3_Tacc<<8)+(B3_Tcoh<<6)+(B3_Tah<<4)+(B3_Tacp<<2)+(B3_PMC))
+    .word ((B4_Tacs<<13)+(B4_Tcos<<11)+(B4_Tacc<<8)+(B4_Tcoh<<6)+(B4_Tah<<4)+(B4_Tacp<<2)+(B4_PMC))
+    .word ((B5_Tacs<<13)+(B5_Tcos<<11)+(B5_Tacc<<8)+(B5_Tcoh<<6)+(B5_Tah<<4)+(B5_Tacp<<2)+(B5_PMC))
+    .word ((B6_MT<<15)+(B6_Trcd<<2)+(B6_SCAN))
+    .word ((B7_MT<<15)+(B7_Trcd<<2)+(B7_SCAN))
+    .word ((REFEN<<23)+(TREFMD<<22)+(Trp<<20)+(Trc<<18)+(Tchr<<16)+REFCNT)
+    .word 0xb2
+    .word 0x30
+    .word 0x30
diff --git a/src/cpu/s3c2410/nand_read.c b/src/cpu/s3c2410/nand_read.c
new file mode 100644
index 0000000..83b1651
--- /dev/null
+++ b/src/cpu/s3c2410/nand_read.c
@@ -0,0 +1,136 @@
+/*
+ * nand_read.c: Simple NAND read functions for booting from NAND
+ *
+ * This is used by cpu/arm920/start.S assembler code,
+ * and the board-specific linker script must make sure this
+ * file is linked within the first 4kB of NAND flash.
+ *
+ * Taken from GPLv2 licensed vivi bootloader,
+ * Copyright (C) 2002 MIZI Research, Inc.
+ *
+ * Author: Hwang, Chideok <hwang at mizi.com>
+ * Date  : $Date: 2004/02/04 10:37:37 $
+ *
+ * u-boot integration and bad-block skipping (C) 2006 by OpenMoko, Inc.
+ * Author: Harald Welte <laforge at openmoko.org>
+ */
+
+/* NOTE this stuff runs in steppingstone context! */
+
+/* the API refers to 512-byte blocks */
+
+#include <qi.h>
+#include "nand_read.h"
+
+#define NAND_CMD_READ0          0
+#define NAND_CMD_READOOB        0x50
+
+#define __REGb(x)	(*(volatile unsigned char *)(x))
+#define __REGw(x)	(*(volatile unsigned short *)(x))
+#define __REGi(x)	(*(volatile unsigned int *)(x))
+#define NF_BASE		0x4e000000
+#define NFCONF          __REGi(NF_BASE + 0x0)
+#define NFCMD           __REGb(NF_BASE + 0x4)
+#define NFADDR          __REGb(NF_BASE + 0x8)
+#define NFDATA          __REGb(NF_BASE + 0xc)
+#define NFSTAT          __REGb(NF_BASE + 0x10)
+#define NFSTAT_BUSY     1
+#define nand_select()   (NFCONF &= ~0x800)
+#define nand_deselect() (NFCONF |= 0x800)
+#define nand_clear_RnB()        do {} while (0)
+
+
+static inline void nand_wait(void)
+{
+	int i;
+
+	while (!(NFSTAT & NFSTAT_BUSY))
+		for (i=0; i<10; i++);
+}
+
+/* configuration for 2410 with 512byte sized flash */
+#define NAND_PAGE_SIZE		512
+#define BAD_BLOCK_OFFSET	5
+#define	NAND_BLOCK_MASK		(NAND_PAGE_SIZE - 1)
+#define NAND_BLOCK_SIZE		0x4000
+
+static int is_bad_block(unsigned long block_index)
+{
+	unsigned char data;
+
+	nand_clear_RnB();
+
+        NFCMD = NAND_CMD_READOOB; /* 0x50 */
+        NFADDR = BAD_BLOCK_OFFSET & 0xf;
+        NFADDR = (block_index      ) & 0xff;
+        NFADDR = (block_index >> 8 ) & 0xff;
+        NFADDR = (block_index >> 16) & 0xff;
+
+	nand_wait();
+	data = (NFDATA & 0xff);
+
+	if (data != 0xff)
+		return 1;
+
+	return 0;
+}
+
+static int nand_read_page_ll(unsigned char *buf, unsigned long block512)
+{
+	unsigned int i;
+
+	nand_clear_RnB();
+
+	NFCMD = NAND_CMD_READ0;
+
+        /* Write Address */
+        NFADDR = 0;
+        NFADDR = (block512      ) & 0xff;
+        NFADDR = (block512 >> 8 ) & 0xff;
+        NFADDR = (block512 >> 16) & 0xff;
+
+	nand_wait();
+
+        for (i = 0; i < NAND_PAGE_SIZE; i++) {
+                *buf = (NFDATA & 0xff);
+                buf++;
+        }
+
+	return 1;
+}
+
+/* low level nand read function */
+int nand_read_ll(unsigned char *buf, unsigned long start_block512, int blocks512)
+{
+	int i, j;
+	int bad_count = 0;
+
+	/* chip Enable */
+	nand_select();
+	nand_clear_RnB();
+
+	for (i = 0; i < 10; i++)
+		;
+
+	while (blocks512 > 0) {
+		if (is_bad_block(start_block512) ||
+				is_bad_block(start_block512 + 1)) {
+			start_block512 += 1;
+			blocks512 += 1;
+			if (bad_count++ == 4)
+				return -1;
+			continue;
+		}
+
+		j = nand_read_page_ll(buf, start_block512);
+		start_block512 += j;
+		buf += j << 9;
+		blocks512 -= j;
+	}
+
+	/* chip Disable */
+	nand_deselect();
+
+	return 0;
+}
+
diff --git a/src/cpu/s3c2410/nand_read.h b/src/cpu/s3c2410/nand_read.h
new file mode 100644
index 0000000..71aeda5
--- /dev/null
+++ b/src/cpu/s3c2410/nand_read.h
@@ -0,0 +1,22 @@
+/*
+ * nand_read.c: Simple NAND read functions for booting from NAND
+ *
+ * This is used by cpu/arm920/start.S assembler code,
+ * and the board-specific linker script must make sure this
+ * file is linked within the first 4kB of NAND flash.
+ *
+ * Taken from GPLv2 licensed vivi bootloader,
+ * Copyright (C) 2002 MIZI Research, Inc.
+ *
+ * Author: Hwang, Chideok <hwang at mizi.com>
+ * Date  : $Date: 2004/02/04 10:37:37 $
+ *
+ * u-boot integration and bad-block skipping (C) 2006 by OpenMoko, Inc.
+ * Author: Harald Welte <laforge at openmoko.org>
+ */
+#ifndef __NAND_READ_H
+#define __NAND_READ_H
+
+int nand_read_ll(unsigned char *buf, unsigned long start_addr, int size);
+
+#endif /* __NAND_READ_H */
diff --git a/src/cpu/s3c2410/qi.lds b/src/cpu/s3c2410/qi.lds
new file mode 100644
index 0000000..27ba2f0
--- /dev/null
+++ b/src/cpu/s3c2410/qi.lds
@@ -0,0 +1,63 @@
+/*
+ * (C) Copyright 2002
+ * Gary Jennejohn, DENX Software Engineering, <gj at denx.de>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
+/*OUTPUT_FORMAT("elf32-arm", "elf32-arm", "elf32-arm")*/
+OUTPUT_ARCH(arm)
+ENTRY(_start)
+SECTIONS
+{
+	. = 0x00000000;
+
+	/* this is intended to take the first 4KBytes of stuff initially.
+	 * We have to make sure we have .rodata* in there for everything
+	 * because we do not compile PIC.
+	 */
+
+	. = ALIGN(4);
+	.text      :
+	{
+	  src/cpu/s3c2410/start.o		(.text .rodata* .data .bss)
+	  src/cpu/s3c2410/lowlevel_init.o	(.text .rodata* .data .bss)
+	  src/cpu/s3c2410/start_qi.o		(.text .rodata* .data .bss)
+	  src/cpu/s3c2410/nand_read.o		(.text .rodata* .data .bss)
+	  src/cpu/s3c2410/serial-s3c24xx.o	(.text .rodata* .data .bss)
+	  src/memory-test.o			(.text .rodata* .data .bss)
+	  src/utils.o				(.text .rodata* .data .bss)
+	  src/ctype.o				(.text .rodata* .data .bss)
+	  *					(.steppingstone)
+	}
+
+	. = ALIGN(4);
+	.everything_else  ADDR (.text) + SIZEOF (.text) + 0x33000000 :
+		AT ( ADDR (.text) + SIZEOF (.text) ) { *(.text .rodata* .data) }
+
+	. = 0x33800000 ;
+	__bss_start = .;
+	.bss (NOLOAD) : 
+    { 
+        *(.bss) 
+    }
+
+	_end = .;
+}
diff --git a/src/cpu/s3c2410/s3c24xx-mci.c b/src/cpu/s3c2410/s3c24xx-mci.c
new file mode 100644
index 0000000..7d53b42
--- /dev/null
+++ b/src/cpu/s3c2410/s3c24xx-mci.c
@@ -0,0 +1,565 @@
+/*
+ * qi s3c24xx SD card driver
+ * Author: Andy Green <andy at openmoko.com>
+ * based on ---->
+ *
+ * u-boot S3C2410 MMC/SD card driver
+ * (C) Copyright 2006 by OpenMoko, Inc.
+ * Author: Harald Welte <laforge at openmoko.org>
+ *
+ * based on u-boot pxa MMC driver and linux/drivers/mmc/s3c2410mci.c
+ * (C) 2005-2005 Thomas Kleffel
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <qi.h>
+#include <mmc.h>
+#include <s3c24xx-regs-sdi.h>
+#include <string.h>
+
+#define SDICON (*(u32 *)0x5a000000)
+#define SDIPRE (*(u32 *)0x5a000004)
+#define SDICARG (*(u32 *)0x5a000008)
+#define SDICCON (*(u32 *)0x5a00000c)
+#define SDICSTA (*(u32 *)0x5a000010)
+#define SDIRSP0 (*(u32 *)0x5a000014)
+#define SDIRSP1 (*(u32 *)0x5a000018)
+#define SDIRSP2 (*(u32 *)0x5a00001c)
+#define SDIRSP3 (*(u32 *)0x5a000020)
+#define SDIDTIMER (*(u32 *)0x5a000024)
+#define SDIBSIZE (*(u32 *)0x5a000028)
+#define SDIDCON (*(u32 *)0x5a00002c)
+#define SDIDCNT (*(u32 *)0x5a000030)
+#define SDIDSTA (*(u32 *)0x5a000034)
+#define SDIFSTA (*(u32 *)0x5a000038)
+/* s3c2410 in GTA01 has these two last ones the other way around!!! */
+#define SDIIMSK (*(u32 *)0x5a00003c)
+#define SDIDAT (*(u32 *)0x5a000040)
+#define SDIDAT2410 (*(u32 *)0x5a00003c)
+#define SDIIMSK2410 (*(u32 *)0x5a000040)
+
+#define CFG_MMC_BASE		0xff000000
+
+int am_i_s3c2410(void)
+{
+	return 1;
+}
+
+#define CONFIG_MMC_WIDE
+#define MMC_BLOCK_SIZE 512
+
+/*
+ * FIXME needs to read cid and csd info to determine block size
+ * and other parameters
+ */
+static u8 mmc_buf[MMC_BLOCK_SIZE];
+static mmc_csd_t mmc_csd;
+static int mmc_ready = 0;
+static int wide = 0;
+
+
+#define CMD_F_RESP	0x01
+#define CMD_F_RESP_LONG	0x02
+
+static u32 *mmc_cmd(u16 cmd, u32 arg, u16 flags)
+{
+	static u32 resp[5];
+
+	u32 ccon, csta;
+	u32 csta_rdy_bit = S3C2410_SDICMDSTAT_CMDSENT;
+
+	memset(resp, 0, sizeof(resp));
+
+//	debug("mmc_cmd CMD%d arg=0x%08x flags=%x\n", cmd, arg, flags);
+
+	SDICSTA = 0xffffffff;
+	SDIDSTA = 0xffffffff;
+	SDIFSTA = 0xffffffff;
+
+	SDICARG = arg;
+
+	ccon = cmd & S3C2410_SDICMDCON_INDEX;
+	ccon |= S3C2410_SDICMDCON_SENDERHOST|S3C2410_SDICMDCON_CMDSTART;
+
+	if (flags & CMD_F_RESP) {
+		ccon |= S3C2410_SDICMDCON_WAITRSP;
+		csta_rdy_bit = S3C2410_SDICMDSTAT_RSPFIN; /* 1 << 9 */
+	}
+
+	if (flags & CMD_F_RESP_LONG)
+		ccon |= S3C2410_SDICMDCON_LONGRSP;
+
+	SDICCON = ccon;
+
+	while (1) {
+		csta = SDICSTA;
+		if (csta & csta_rdy_bit)
+			break;
+		if (csta & S3C2410_SDICMDSTAT_CMDTIMEOUT) {
+			puts("===============> MMC CMD Timeout\n");
+			SDICSTA |= S3C2410_SDICMDSTAT_CMDTIMEOUT;
+			break;
+		}
+	}
+
+//	debug("final MMC CMD status 0x%x\n", csta);
+
+	SDICSTA |= csta_rdy_bit;
+
+	if (flags & CMD_F_RESP) {
+		resp[0] = SDIRSP0;
+		resp[1] = SDIRSP1;
+		resp[2] = SDIRSP2;
+		resp[3] = SDIRSP3;
+	}
+
+	return resp;
+}
+
+#define FIFO_FILL() ((SDIFSTA & S3C2410_SDIFSTA_COUNTMASK) >> 2)
+
+static int mmc_block_read(u8 *dst, u32 src, u32 len)
+{
+	u32 dcon, fifo;
+	u32 *dst_u32 = (u32 *)dst;
+	u32 *resp;
+
+	if (len == 0)
+		return 0;
+
+//	debug("mmc_block_rd dst %lx src %lx len %d\n", (u32)dst, src, len);
+
+	/* set block len */
+	resp = mmc_cmd(MMC_CMD_SET_BLOCKLEN, len, CMD_F_RESP);
+	SDIBSIZE = len;
+
+	//SDIPRE = 0xff;
+
+	/* setup data */
+	dcon = (len >> 9) & S3C2410_SDIDCON_BLKNUM;
+	dcon |= S3C2410_SDIDCON_BLOCKMODE;
+	dcon |= S3C2410_SDIDCON_RXAFTERCMD|S3C2410_SDIDCON_XFER_RXSTART;
+	if (wide)
+		dcon |= S3C2410_SDIDCON_WIDEBUS;
+
+	if (!am_i_s3c2410())
+		dcon |= S3C2440_SDIDCON_DS_WORD | S3C2440_SDIDCON_DATSTART;
+
+	SDIDCON = dcon;
+
+	/* send read command */
+	resp = mmc_cmd(MMC_CMD_READ_BLOCK, src, CMD_F_RESP);
+
+	while (len > 0) {
+		u32 sdidsta = SDIDSTA;
+		fifo = FIFO_FILL();
+		if (sdidsta & (S3C2410_SDIDSTA_FIFOFAIL|
+				S3C2410_SDIDSTA_CRCFAIL|
+				S3C2410_SDIDSTA_RXCRCFAIL|
+				S3C2410_SDIDSTA_DATATIMEOUT)) {
+			puts("mmc_block_read: err SDIDSTA=0x");
+			print32(sdidsta);
+			puts("\n");
+			return -1;
+		}
+
+		if (am_i_s3c2410()) {
+			while (fifo--) {
+				//debug("dst_u32 = 0x%08x\n", dst_u32);
+				*(dst_u32++) = SDIDAT2410;
+				if (len >= 4)
+					len -= 4;
+				else {
+					len = 0;
+					break;
+				}
+			}
+		} else {
+			while (fifo--) {
+				//debug("dst_u32 = 0x%08x\n", dst_u32);
+				*(dst_u32++) = SDIDAT;
+				if (len >= 4)
+					len -= 4;
+				else {
+					len = 0;
+					break;
+				}
+			}
+		}
+	}
+
+//	debug("waiting for SDIDSTA  (currently 0x%08x\n", SDIDSTA);
+	while (!(SDIDSTA & (1 << 4))) {}
+//	debug("done waiting for SDIDSTA (currently 0x%08x\n", SDIDSTA);
+
+	SDIDCON = 0;
+
+	if (!(SDIDSTA & S3C2410_SDIDSTA_XFERFINISH))
+		puts("mmc_block_read; transfer not finished!\n");
+
+	return 0;
+}
+
+static int mmc_block_write(u32 dst, u8 *src, int len)
+{
+	puts("MMC block write not yet supported on S3C2410!\n");
+	return -1;
+}
+
+
+int  s3c24xx_mmc_read(u32 src, u8 *dst, int size)
+{
+	u32 end, part_start, part_end, part_len, aligned_start, aligned_end;
+	u32 mmc_block_size, mmc_block_address;
+
+	if (size == 0)
+		return 0;
+
+	if (!mmc_ready) {
+		puts("Please initialize the MMC first\n");
+		return -1;
+	}
+
+	mmc_block_size = MMC_BLOCK_SIZE;
+	mmc_block_address = ~(mmc_block_size - 1);
+
+	src -= CFG_MMC_BASE;
+	end = src + size;
+	part_start = ~mmc_block_address & src;
+	part_end = ~mmc_block_address & end;
+	aligned_start = mmc_block_address & src;
+	aligned_end = mmc_block_address & end;
+
+	/* all block aligned accesses */
+//	debug("src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
+//	src, (u32)dst, end, part_start, part_end, aligned_start, aligned_end);
+	if (part_start) {
+		part_len = mmc_block_size - part_start;
+//		debug("ps src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
+//		src, (u32)dst, end, part_start, part_end, aligned_start, aligned_end);
+		if ((mmc_block_read(mmc_buf, aligned_start, mmc_block_size)) < 0)
+			return -1;
+
+		memcpy(dst, mmc_buf+part_start, part_len);
+		dst += part_len;
+		src += part_len;
+	}
+//	debug("src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
+//	src, (u32)dst, end, part_start, part_end, aligned_start, aligned_end);
+	for (; src < aligned_end; src += mmc_block_size, dst += mmc_block_size) {
+//		debug("al src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
+//		src, (u32)dst, end, part_start, part_end, aligned_start, aligned_end);
+		if ((mmc_block_read((u8 *)(dst), src, mmc_block_size)) < 0)
+			return -1;
+	}
+//	debug("src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
+//	src, (u32)dst, end, part_start, part_end, aligned_start, aligned_end);
+	if (part_end && src < end) {
+//		debug("pe src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
+//		src, (u32)dst, end, part_start, part_end, aligned_start, aligned_end);
+		if ((mmc_block_read(mmc_buf, aligned_end, mmc_block_size)) < 0)
+			return -1;
+
+		memcpy(dst, mmc_buf, part_end);
+	}
+	return 0;
+}
+
+int s3c24xx_mmc_write(u8 *src, u32 dst, int size)
+{
+	u32 end, part_start, part_end, part_len, aligned_start, aligned_end;
+	u32 mmc_block_size, mmc_block_address;
+
+	if (size == 0)
+		return 0;
+
+	if (!mmc_ready) {
+		puts("Please initialize the MMC first\n");
+		return -1;
+	}
+
+	mmc_block_size = MMC_BLOCK_SIZE;
+	mmc_block_address = ~(mmc_block_size - 1);
+
+	dst -= CFG_MMC_BASE;
+	end = dst + size;
+	part_start = ~mmc_block_address & dst;
+	part_end = ~mmc_block_address & end;
+	aligned_start = mmc_block_address & dst;
+	aligned_end = mmc_block_address & end;
+
+	/* all block aligned accesses */
+//	debug("src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
+//	src, (u32)dst, end, part_start, part_end, aligned_start, aligned_end);
+	if (part_start) {
+		part_len = mmc_block_size - part_start;
+//		debug("ps src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
+//		(u32)src, dst, end, part_start, part_end, aligned_start, aligned_end);
+		if ((mmc_block_read(mmc_buf, aligned_start, mmc_block_size)) < 0)
+			return -1;
+
+		memcpy(mmc_buf+part_start, src, part_len);
+		if ((mmc_block_write(aligned_start, mmc_buf, mmc_block_size)) < 0)
+			return -1;
+
+		dst += part_len;
+		src += part_len;
+	}
+//	debug("src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
+//	src, (u32)dst, end, part_start, part_end, aligned_start, aligned_end);
+	for (; dst < aligned_end; src += mmc_block_size, dst += mmc_block_size) {
+//		debug("al src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
+//		src, (u32)dst, end, part_start, part_end, aligned_start, aligned_end);
+		if ((mmc_block_write(dst, (u8 *)src, mmc_block_size)) < 0)
+			return -1;
+
+	}
+//	debug("src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
+//	src, (u32)dst, end, part_start, part_end, aligned_start, aligned_end);
+	if (part_end && dst < end) {
+//		debug("pe src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
+//		src, (u32)dst, end, part_start, part_end, aligned_start, aligned_end);
+		if ((mmc_block_read(mmc_buf, aligned_end, mmc_block_size)) < 0)
+			return -1;
+
+		memcpy(mmc_buf, src, part_end);
+		if ((mmc_block_write(aligned_end, mmc_buf, mmc_block_size)) < 0)
+			return -1;
+
+	}
+	return 0;
+}
+
+u32 s3c24xx_mmc_bread(int dev_num, u32 blknr, u32 blkcnt, void *dst)
+{
+	int mmc_block_size = MMC_BLOCK_SIZE;
+	u32 src = blknr * mmc_block_size + CFG_MMC_BASE;
+
+	s3c24xx_mmc_read(src, dst, blkcnt*mmc_block_size);
+	return blkcnt;
+}
+
+/* MMC_DEFAULT_RCA should probably be just 1, but this may break other code
+   that expects it to be shifted. */
+static u_int16_t rca = MMC_DEFAULT_RCA >> 16;
+
+#if 0
+static u32 mmc_size(const struct mmc_csd *csd)
+{
+	u32 block_len, mult, blocknr;
+
+	block_len = csd->read_bl_len << 12;
+	mult = csd->c_size_mult1 << 8;
+	blocknr = (csd->c_size+1) * mult;
+
+	return blocknr * block_len;
+}
+#endif
+
+struct sd_cid {
+	char		pnm_0;	/* product name */
+	char		oid_1;	/* OEM/application ID */
+	char		oid_0;
+	uint8_t		mid;	/* manufacturer ID */
+	char		pnm_4;
+	char		pnm_3;
+	char		pnm_2;
+	char		pnm_1;
+	uint8_t		psn_2;	/* product serial number */
+	uint8_t		psn_1;
+	uint8_t		psn_0;	/* MSB */
+	uint8_t		prv;	/* product revision */
+	uint8_t		crc;	/* CRC7 checksum, b0 is unused and set to 1 */
+	uint8_t		mdt_1;	/* manufacturing date, LSB, RRRRyyyy yyyymmmm */
+	uint8_t		mdt_0;	/* MSB */
+	uint8_t		psn_3;	/* LSB */
+};
+
+static void print_mmc_cid(mmc_cid_t *cid)
+{
+	puts("MMC found. Card desciption is:\n");
+	puts("Manufacturer ID = ");
+	print8(cid->id[0]);
+	print8(cid->id[1]);
+	print8(cid->id[2]);
+	puts("\nHW/FW Revision = ");
+	print8(cid->hwrev);
+	print8(cid->fwrev);
+	cid->hwrev = cid->fwrev = 0;	/* null terminate string */
+	puts("Product Name = ");
+	puts((char *)cid->name);
+	puts("\nSerial Number = ");
+	print8(cid->sn[0]);
+	print8(cid->sn[1]);
+	print8(cid->sn[2]);
+	puts("\nMonth = ");
+	printdec(cid->month);
+	puts("\nYear = ");
+	printdec(1997 + cid->year);
+	puts("\n");
+}
+
+static void print_sd_cid(const struct sd_cid *cid)
+{
+	puts("Manufacturer:       0x");
+	print8(cid->mid);
+	puts("OEM \"");
+	this_board->putc(cid->oid_0);
+	this_board->putc(cid->oid_1);
+	puts("\"\nProduct name:       \"");
+	this_board->putc(cid->pnm_0);
+	this_board->putc(cid->pnm_1);
+	this_board->putc(cid->pnm_2);
+	this_board->putc(cid->pnm_3);
+	this_board->putc(cid->pnm_4);
+	puts("\", revision ");
+	printdec(cid->prv >> 4);
+	puts(".");
+	printdec(cid->prv & 15);
+	puts("\nSerial number:      ");
+	printdec(cid->psn_0 << 24 | cid->psn_1 << 16 | cid->psn_2 << 8 |
+	    cid->psn_3);
+	puts("\nManufacturing date: ");
+	printdec(cid->mdt_1 & 15);
+	puts("/");
+	printdec(2000+((cid->mdt_0 & 15) << 4)+((cid->mdt_1 & 0xf0) >> 4));
+	puts("\nCRC:                0x");
+	print8(cid->crc >> 1);
+	puts(" b0 = ");
+	print8(cid->crc & 1);
+	puts("\n");
+}
+
+int s3c24xx_mmc_init(int verbose)
+{
+ 	int retries, rc = -2;
+	int is_sd = 0;
+	u32 *resp;
+
+	SDICON = S3C2410_SDICON_FIFORESET | S3C2410_SDICON_CLOCKTYPE;
+	SDIBSIZE = 512;
+	if (am_i_s3c2410()) {
+		/* S3C2410 has some bug that prevents reliable operation at higher speed */
+		//SDIPRE = 0x3e;  /* SDCLK = PCLK/2 / (SDIPRE+1) = 396kHz */
+		SDIPRE = 0x02;  /* 2410: SDCLK = PCLK/2 / (SDIPRE+1) = 11MHz */
+		SDIDTIMER = 0xffff;
+		SDIIMSK2410 = 0x0;
+	} else {
+		SDIPRE = 0x05;  /* 2410: SDCLK = PCLK / (SDIPRE+1) = 11MHz */
+		SDIDTIMER = 0x7fffff;
+		SDIIMSK = 0x0;
+	}
+
+	udelay(1250000); /* FIXME: 74 SDCLK cycles */
+
+	mmc_csd.c_size = 0;
+
+	puts("Sending reset...\n");
+
+	/* reset */
+	retries = 10;
+	resp = mmc_cmd(MMC_CMD_RESET, 0, 0);
+
+	puts("trying to detect SD Card...\n");
+	while (retries--) {
+		udelay(1000000);
+		resp = mmc_cmd(55, 0x00000000, CMD_F_RESP);
+		resp = mmc_cmd(41, 0x00300000, CMD_F_RESP);
+
+		if (resp[0] & (1 << 31)) {
+			is_sd = 1;
+			break;
+		}
+	}
+
+	if (retries < 0 && !is_sd)
+		return -3;
+
+	/* try to get card id */
+	resp = mmc_cmd(MMC_CMD_ALL_SEND_CID, 0, CMD_F_RESP|CMD_F_RESP_LONG);
+	if (resp) {
+		if (!is_sd) {
+			/* TODO configure mmc driver depending on card
+			   attributes */
+			mmc_cid_t *cid = (mmc_cid_t *)resp;
+
+			if (verbose)
+				print_mmc_cid(cid);
+#if 0
+			sprintf((char *) mmc_dev.vendor,
+				"Man %02x%02x%02x Snr %02x%02x%02x",
+				cid->id[0], cid->id[1], cid->id[2],
+				cid->sn[0], cid->sn[1], cid->sn[2]);
+			sprintf((char *) mmc_dev.product,"%s",cid->name);
+			sprintf((char *) mmc_dev.revision,"%x %x",
+				cid->hwrev, cid->fwrev);
+#endif
+		}
+		else {
+			struct sd_cid *cid = (struct sd_cid *) resp;
+
+			if (verbose)
+				print_sd_cid(cid);
+#if 0
+			sprintf((char *) mmc_dev.vendor,
+			    "Man %02 OEM %c%c \"%c%c%c%c%c\"",
+			    cid->mid, cid->oid_0, cid->oid_1,
+			    cid->pnm_0, cid->pnm_1, cid->pnm_2, cid->pnm_3,
+			    cid->pnm_4);
+			sprintf((char *) mmc_dev.product, "%d",
+			    cid->psn_0 << 24 | cid->psn_1 << 16 |
+			    cid->psn_2 << 8 | cid->psn_3);
+			sprintf((char *) mmc_dev.revision, "%d.%d",
+			    cid->prv >> 4, cid->prv & 15);
+#endif
+		}
+
+
+		/* MMC exists, get CSD too */
+		resp = mmc_cmd(MMC_CMD_SET_RCA, MMC_DEFAULT_RCA, CMD_F_RESP);
+		if (is_sd)
+			rca = resp[0] >> 16;
+
+		resp = mmc_cmd(MMC_CMD_SEND_CSD, rca<<16, CMD_F_RESP|CMD_F_RESP_LONG);
+		if (resp) {
+			mmc_csd_t *csd = (mmc_csd_t *)resp;
+			memcpy(&mmc_csd, csd, sizeof(csd));
+			rc = 0;
+			mmc_ready = 1;
+#if 0
+			/* FIXME add verbose printout for csd */
+			printf("READ_BL_LEN=%u, C_SIZE_MULT=%u, C_SIZE=%u\n",
+				csd->read_bl_len, csd->c_size_mult1, csd->c_size);
+			printf("size = %u\n", mmc_size(csd));
+#endif
+		}
+	}
+
+	resp = mmc_cmd(MMC_CMD_SELECT_CARD, rca<<16, CMD_F_RESP);
+
+#ifdef CONFIG_MMC_WIDE
+	if (is_sd) {
+		resp = mmc_cmd(55, rca<<16, CMD_F_RESP);
+		resp = mmc_cmd(6, 0x02, CMD_F_RESP);
+		wide = 1;
+	}
+#endif
+
+	return rc;
+}
+
+
diff --git a/src/cpu/s3c2410/serial-s3c24xx.c b/src/cpu/s3c2410/serial-s3c24xx.c
new file mode 100644
index 0000000..0f4ba22
--- /dev/null
+++ b/src/cpu/s3c2410/serial-s3c24xx.c
@@ -0,0 +1,77 @@
+/*
+ * (C) Copyright 2007 OpenMoko, Inc.
+ * Author: xiangfu liu <xiangfu at openmoko.org>
+ *
+ * Configuation settings for the FIC Neo GTA02 Linux GSM phone
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <qi.h>
+#include <serial-s3c24xx.h>
+
+void serial_init_115200_s3c24xx(const int uart, const int pclk_MHz)
+{
+	int div = (((54 * pclk_MHz) + 26) / 100) -1;
+	switch(uart)
+	{
+	case UART0:
+		rULCON0 = 0x3;
+		rUCON0 = 0x245;
+		rUFCON0 = 0x0;
+		rUMCON0 = 0x0;
+		rUBRDIV0 = div;
+		break;
+	case UART1:
+		rULCON1 = 0x3;
+		rUCON1 = 0x245;
+		rUFCON1 = 0x0;
+		rUMCON1 = 0x0;
+		rUBRDIV1 = div;
+		break;
+	case UART2:
+		rULCON2 = 0x3;
+		rUCON2 = 0x245;
+		rUFCON2 = 0x1;
+		rUBRDIV2 = div;
+		break;
+	default:
+		break;
+	}
+}
+/*
+ * Output a single byte to the serial port.
+ */
+void serial_putc_s3c24xx(const int uart, const char c)
+{
+	switch(uart)
+	{
+	case UART0:
+		while ( !( rUTRSTAT0 & 0x2 ) );
+		WrUTXH0(c);
+		break;
+	case UART1:
+		while ( !( rUTRSTAT1 & 0x2 ) );
+		WrUTXH1(c);
+		break;
+	case UART2:
+		while ( !( rUTRSTAT2 & 0x2 ) );
+		WrUTXH2(c);
+		break;
+	default:
+		break;
+	}
+}
diff --git a/src/cpu/s3c2410/start.S b/src/cpu/s3c2410/start.S
new file mode 100644
index 0000000..5c9d3db
--- /dev/null
+++ b/src/cpu/s3c2410/start.S
@@ -0,0 +1,304 @@
+/*
+ * (C) Copyright 2007 OpenMoko, Inc.
+ * 
+ * Configuation settings for the OPENMOKO Neo GTA02 Linux GSM phone
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#define __ASM_MODE__
+#define __ASSEMBLY__
+
+#include <neo_gta01.h>
+
+#define S3C2410_MISCCR_nEN_SCLK0    (1 << 17)
+#define S3C2410_MISCCR_nEN_SCLK1    (1 << 18)
+#define S3C2410_MISCCR_nEN_SCLKE    (1 << 19)
+
+
+.globl _start, processor_id, is_jtag
+
+_start:	b       start_code
+/* if we are injected by JTAG, the script sets _istag content to nonzero */
+is_jtag:
+	.word	0
+
+/* it's at a fixed address (+0x8) so we can breakpoint it in the JTAG script
+ * we need to go through this hassle because before this moment, SDRAM is not
+ * working so we can't prep it from JTAG
+ */
+
+_steppingstone_done:
+	ldr	pc, _start_armboot
+
+_start_armboot:
+	.word 	start_qi
+
+_TEXT_BASE:
+	.word	TEXT_BASE
+
+/*
+ * These are defined in the board-specific linker script.
+ */
+.globl _bss_start
+_bss_start:
+	.word __bss_start
+
+.globl _bss_end
+_bss_end:
+	.word _end
+/*
+ * we have a stack in steppingstone because we can want to run full memory
+ * memory tests
+ */
+
+	.fill   128
+.globl _ss_stack
+_ss_stack:
+
+
+start_code:	
+	/*
+	 * set the cpu to SVC32 mode
+	 */
+	mrs	r0,cpsr
+	bic	r0,r0,#0x1f
+	orr	r0,r0,#0xd3
+	msr	cpsr,r0
+
+# define pWTCON		0x53000000
+
+	ldr     r0, =pWTCON
+	mov     r1, #0x0
+	str     r1, [r0]
+
+	/*
+	 * mask all IRQs by setting all bits in the INTMR - default
+	 */
+# define INTMSK		0x4A000008	/* Interupt-Controller base addresses */
+# define INTSUBMSK	0x4A00001C
+# define INTSUBMSK_val 	0x000007ff
+
+	mov	r1, #0xffffffff
+	ldr	r0, =INTMSK
+	str	r1, [r0]
+
+	ldr	r1, =INTSUBMSK_val
+	ldr	r0, =INTSUBMSK
+	str	r1, [r0]
+
+	/* Clock asynchronous mode */
+	mrc	p15, 0, r1, c1, c0, 0
+	orr	r1, r1, #0xc0000000
+	mcr	p15, 0, r1, c1, c0, 0
+
+#define LOCKTIME	0x4c000000
+
+	ldr	r0, =LOCKTIME
+	mov	r1, #0xffffff
+	str	r1, [r0]
+	
+# define UPLLCON	0x4c000008	
+# define MPLLCON_val    ((0x90 << 12) + (0x7 << 4) + 0x0)       /* 202 MHz */
+# define UPLLCON_val    ((0x78 << 12) + (0x2 << 4) + 0x3)
+
+	ldr	r0, =UPLLCON
+	ldr	r1, =UPLLCON_val
+	str	r1, [r0]
+
+	/* Page 7-19, seven nops between UPLL and MPLL */
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+
+	ldr	r1, =MPLLCON_val
+	str	r1, [r0, #-4]		/* MPLLCON */
+
+# define CLKDIVN	0x4C000014	/* clock divisor register */
+# define CLKDIVN_val    3 /* FCLK:HCLK:PCLK = 1:2:4 */
+
+	/* FCLK:HCLK:PCLK = 1:3:6 */
+	ldr	r0, =CLKDIVN
+	mov	r1, #CLKDIVN_val
+	str	r1, [r0]
+
+	/* enable only CPU peripheral block clocks we actually use */
+	ldr	r0, =0x4c00000c		/* clkcon */
+	ldr	r1, =0x3f10	/* uart, pwm, gpio, nand, sdi clocks on */
+	str	r1, [r0]
+
+	/* gpio UART0 init */
+	ldr	r0, =0x56000070
+	ldr	r1, =0x000000AA
+	str	r1, [r0]
+
+	/* init uart0 */
+	ldr	r0, =0x50000000
+	mov	r1, #0x03
+	str	r1, [r0]
+	ldr	r1, =0x245
+	str	r1, [r0, #0x04]
+	mov	r1, #0x01
+	str	r1, [r0, #0x08]
+	mov	r1, #0x00
+	str	r1, [r0, #0x0c]
+	mov	r1, #0x1a
+	str	r1, [r0, #0x28]
+
+/* reset nand controller, or it is dead to us */
+
+#define oNFCONF			0x00
+#define oNFCMD			0x04
+#define oNFSTAT			0x10
+
+	mov	r1, #0x4E000000
+	ldr	r2, =0xf842		@ initial value enable tacls=3,rph0=6,rph1=0
+	str	r2, [r1, #oNFCONF]
+	ldr	r2, [r1, #oNFCONF]
+	bic	r2, r2, #0x800		@ enable chip
+	str	r2, [r1, #oNFCONF]
+	mov	r2, #0xff		@ RESET command
+	strb	r2, [r1, #oNFCMD]
+	mov	r3, #0			@ wait
+1:	add	r3, r3, #0x1
+	cmp	r3, #0xa
+	blt	1b
+2:	ldr	r2, [r1, #oNFSTAT]	@ wait ready
+	tst	r2, #0x1
+	beq	2b
+	ldr	r2, [r1, #oNFCONF]
+	orr	r2, r2, #0x800		@ disable chip
+	str	r2, [r1, #oNFCONF]
+
+	/* take sdram out of power down */
+	ldr	r0, =0x56000080		/* misccr */
+	ldr	r1, [ r0 ]
+	bic	r1, r1, #(S3C2410_MISCCR_nEN_SCLK0 | S3C2410_MISCCR_nEN_SCLK1 | S3C2410_MISCCR_nEN_SCLKE)
+	str	r1, [ r0 ]
+
+	/* ensure signals stabalise */
+	mov	r1, #128
+3:	subs	r1, r1, #1
+	bpl	3b
+
+	bl	cpu_init_crit
+
+	/* ensure some refresh has happened */
+	ldr	r1, =0xfffff
+4:	subs	r1, r1, #1
+	bpl	4b
+
+	/* capture full EINT situation into gstatus 4 */
+
+	ldr	r0, =0x4A000000 /* SRCPND */
+	ldr	r1, [ r0 ]
+	and	r1, r1, #0xf
+
+	ldr	r0, =0x560000BC /* gstatus4 */
+	str	r1, [ r0 ]
+
+	ldr	r0, =0x560000A8 /* EINTPEND */
+	ldr	r1, [ r0 ]
+	ldr	r0, =0xfff0
+	and	r1, r1, r0
+	ldr	r0, =0x560000BC /* gstatus4 */
+	ldr     r0, [ r0 ]
+	orr	r1, r1, r0
+	ldr	r0, =0x560000BC /* gstatus4 */
+	str	r1, [ r0 ]
+
+	/* test for resume */
+
+	ldr	r1, =0x560000B4		/* gstatus2 */
+	ldr	r0, [ r1 ]
+	tst	r0, #0x02		/* is this resume from power down */
+					/* well, if it was, we are going to jump to
+					 * whatever address we stashed in gstatus3,
+					 * and gstatus4 will hold the wake interrupt
+					 * source for the OS to look at
+					 */
+	ldrne	pc, [r1, #4]
+
+
+								/* >> CFG_VIDEO_LOGO_MAX_SIZE */
+#define CFG_GBL_DATA_SIZE		128			/* size in bytes reserved for initial data */
+
+stack_setup:
+	ldr	r0, _TEXT_BASE		/* upper 128 KiB: relocated uboot   */
+	sub	r0, r0, #CFG_GBL_DATA_SIZE 	/* bdinfo                        */
+	sub	sp, r0, #12		/* leave 3 words for abort-stack    */
+
+clear_bss:
+	ldr	r0, _bss_start		/* find start of bss segment        */
+	ldr	r1, _bss_end		/* stop here                        */
+	mov 	r2, #0x00000000	/* clear                            */
+
+clbss_l:
+	str	r2, [r0]			/* clear loop...                    */
+	add	r0, r0, #4
+	cmp	r0, r1
+	ble	clbss_l
+
+/* we are going to jump into the C part of the init now */
+spin:
+	b	_steppingstone_done
+
+/*
+ *************************************************************************
+ *
+ * CPU_init_critical registers
+ *
+ * setup important registers
+ * setup memory timing
+ *
+ *************************************************************************
+ */
+
+cpu_init_crit:
+
+	/*
+	 * flush v4 I/D caches
+	 */
+	mov	r0, #0
+	mcr	p15, 0, r0, c7, c7, 0	/* flush v3/v4 cache */
+	mcr	p15, 0, r0, c8, c7, 0	/* flush v4 TLB */
+
+	/*
+	 * disable MMU stuff and caches
+	 */
+	mrc	p15, 0, r0, c1, c0, 0
+	bic	r0, r0, #0x00002300	@ clear bits 13, 9:8 (--V- --RS)
+	bic	r0, r0, #0x00000087	@ clear bits 7, 2:0 (B--- -CAM)
+	orr	r0, r0, #0x00000002	@ set bit 2 (A) Align
+	orr	r0, r0, #0x00001000	@ set bit 12 (I) I-Cache
+	mcr	p15, 0, r0, c1, c0, 0
+
+	/*
+	 * before relocating, we have to setup RAM timing
+	 * because memory timing is board-dependend, you will
+	 * find a lowlevel_init.S in your board directory.
+	 */
+	mov	ip, lr
+ 
+	bl	lowlevel_init
+
+	mov	lr, ip
+	mov	pc, lr
+
diff --git a/src/cpu/s3c2410/start_qi.c b/src/cpu/s3c2410/start_qi.c
new file mode 100644
index 0000000..4d9b7ae
--- /dev/null
+++ b/src/cpu/s3c2410/start_qi.c
@@ -0,0 +1,131 @@
+/*
+ * (C) Copyright 2007 OpenMoko, Inc.
+ * Author: xiangfu liu <xiangfu at openmoko.org>
+ *         Andy Green <andy at openmoko.com>
+ *
+ * Configuation settings for the OPENMOKO Neo GTA02 Linux GSM phone
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+/* NOTE this stuff runs in steppingstone context! */
+
+
+#include <qi.h>
+#include "nand_read.h"
+#include <neo_gta01.h>
+
+#define stringify2(s) stringify1(s)
+#define stringify1(s) #s
+
+
+extern void bootloader_second_phase(void);
+
+const struct board_api *boards[] = {
+			&board_api_gta01,
+			NULL /* always last */
+};
+
+
+struct board_api const * this_board;
+extern int is_jtag;
+
+void start_qi(void)
+{
+	int flag = 0;
+	int board = 0;
+
+	/*
+	 * well, we can be running on this CPU two different ways.
+	 *
+	 * 1) We were copied into steppingstone and TEXT_BASE already
+	 *    by JTAG.  We don't have to do anything else.  JTAG script
+	 *    then sets data at address 0x4 to 0xffffffff as a signal we
+	 *    are running by JTAG.
+	 *
+	 * 2) We only got our first 4K into steppingstone, we need to copy
+	 *    the rest of ourselves into TEXT_BASE.
+	 *
+	 * So we do the copy out of NAND only if we see we did not come up
+	 * under control of JTAG.
+	 */
+
+	if (!is_jtag)
+		/*
+		* We got the first 4KBytes of the bootloader pulled into the
+		* steppingstone SRAM for free.  Now we pull the whole bootloader
+		* image into SDRAM.
+		*
+		* This code and the .S files are arranged by the linker script
+		* to expect to run from 0x0.  But the linker script has told
+		* everything else to expect to run from 0x33000000+.  That's
+		* why we are going to be able to copy this code and not have it
+		* crash when we run it from there.
+		*/
+
+		/* We randomly pull 32KBytes of bootloader */
+		if (nand_read_ll((u8 *)TEXT_BASE, 0, 32 * 1024 / 512) < 0)
+			goto unhappy;
+
+	/* ask all the boards we support in turn if they recognize this
+	 * hardware we are running on, accept the first positive answer
+	 */
+
+	this_board = boards[board];
+	while (!flag && this_board) {
+
+		/* check if it is the right board... */
+		if (this_board->is_this_board()) {
+			flag = 1;
+			continue;
+		}
+
+		this_board = boards[board++];
+	}
+
+	/* No valid board found */
+	if (!this_board)
+		goto unhappy;
+
+	this_board->port_init();
+	set_putc_func(this_board->putc);
+
+	/* stick some hello messages on debug console */
+
+	puts("\n\n\nQi Bootloader "stringify2(QI_CPU)"  "
+				   stringify2(BUILD_HOST)" "
+				   stringify2(BUILD_VERSION)" "
+				   "\n");
+
+	puts(stringify2(BUILD_DATE) "  Copyright (C) 2008 Openmoko, Inc.\n");
+	puts("\n     Detected: ");
+
+	puts(this_board->name);
+	puts(", ");
+	puts((this_board->get_board_variant)()->name);
+	puts("\n");
+
+	
+	/*
+	 * jump to bootloader_second_phase() running from DRAM copy
+	 */
+	bootloader_second_phase();
+
+unhappy:
+	while(1)
+		;
+
+}




More information about the openmoko-kernel mailing list