Merge remote-tracking branch 'goog/android-master' into HEAD
diff --git a/MAINTAINERS b/MAINTAINERS
index 2f60a60..cba893b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -888,6 +888,10 @@
 
 	hawkboard	ARM926EJS (OMAP-L138)
 
+Mike J. Chen <mjchen@google.com>
+
+	omap4_tungsten	ARM ARMV7 (OMAP4xx SoC)
+
 -------------------------------------------------------------------------
 
 Unknown / orphaned boards:
diff --git a/arch/arm/cpu/armv7/omap-common/gpio.c b/arch/arm/cpu/armv7/omap-common/gpio.c
index e62c6f4..d16ebd7 100644
--- a/arch/arm/cpu/armv7/omap-common/gpio.c
+++ b/arch/arm/cpu/armv7/omap-common/gpio.c
@@ -234,9 +234,25 @@
 
 /**
  * Reset and free the gpio after using it.
+ *
+ * Note: do not actually do this in manufacturing builds.  The Tungsten 
+ * project uses the uboot gpio console during factory line diagnostics to do
+ * basic pin IO tests.  The consoles code follows the pattern of
+ *
+ * 1) Request target gpio
+ * 2) Get/Set/Toggle target gpio
+ * 3) Free target gpio
+ *
+ * Allowing the implementation of free to reset a given GPIO back to being an
+ * input causes the console's pattern to break the diags which need the pin to
+ * hold its state after the command has been issued until the external test rig
+ * can verify the pin state.  For now, we just #if this out; but there probably
+ * should be a more general fix applied to the console commands at some future
+ * point in time.
  */
 void gpio_free(unsigned gpio)
 {
+#ifndef CONFIG_MFG
 	const struct gpio_bank *bank;
 
 	if (check_gpio(gpio) < 0)
@@ -244,4 +260,5 @@
 	bank = get_gpio_bank(gpio);
 
 	_set_gpio_direction(bank, get_gpio_index(gpio), 1);
+#endif
 }
diff --git a/arch/arm/cpu/armv7/omap4/omap4_mux_data.h b/arch/arm/cpu/armv7/omap4/omap4_mux_data.h
index 00c52f8..bf69700 100644
--- a/arch/arm/cpu/armv7/omap4/omap4_mux_data.h
+++ b/arch/arm/cpu/armv7/omap4/omap4_mux_data.h
@@ -30,6 +30,7 @@
 
 const struct pad_conf_entry core_padconf_array_essential[] = {
 
+#ifndef CONFIG_TUNGSTEN
 {GPMC_AD0, (PTU | IEN | OFF_EN | OFF_PD | OFF_IN | M1)}, /* sdmmc2_dat0 */
 {GPMC_AD1, (PTU | IEN | OFF_EN | OFF_PD | OFF_IN | M1)}, /* sdmmc2_dat1 */
 {GPMC_AD2, (PTU | IEN | OFF_EN | OFF_PD | OFF_IN | M1)}, /* sdmmc2_dat2 */
@@ -40,6 +41,7 @@
 {GPMC_AD7, (PTU | IEN | OFF_EN | OFF_PD | OFF_IN | M1)}, /* sdmmc2_dat7 */
 {GPMC_NOE, (PTU | IEN | OFF_EN | OFF_OUT_PTD | M1)},	 /* sdmmc2_clk */
 {GPMC_NWE, (PTU | IEN | OFF_EN | OFF_PD | OFF_IN | M1)}, /* sdmmc2_cmd */
+#endif
 {SDMMC1_CLK, (PTU | OFF_EN | OFF_OUT_PTD | M0)},	 /* sdmmc1_clk */
 {SDMMC1_CMD, (PTU | IEN | OFF_EN | OFF_PD | OFF_IN | M0)}, /* sdmmc1_cmd */
 {SDMMC1_DAT0, (PTU | IEN | OFF_EN | OFF_PD | OFF_IN | M0)}, /* sdmmc1_dat0 */
@@ -50,6 +52,7 @@
 {SDMMC1_DAT5, (PTU | IEN | OFF_EN | OFF_PD | OFF_IN | M0)}, /* sdmmc1_dat5 */
 {SDMMC1_DAT6, (PTU | IEN | OFF_EN | OFF_PD | OFF_IN | M0)}, /* sdmmc1_dat6 */
 {SDMMC1_DAT7, (PTU | IEN | OFF_EN | OFF_PD | OFF_IN | M0)}, /* sdmmc1_dat7 */
+#ifndef CONFIG_TUNGSTEN
 {I2C1_SCL, (PTU | IEN | M0)},				/* i2c1_scl */
 {I2C1_SDA, (PTU | IEN | M0)},				/* i2c1_sda */
 {I2C2_SCL, (PTU | IEN | M0)},				/* i2c2_scl */
@@ -60,6 +63,7 @@
 {I2C4_SDA, (PTU | IEN | M0)},				/* i2c4_sda */
 {UART3_CTS_RCTX, (PTU | IEN | M0)},			/* uart3_tx */
 {UART3_RTS_SD, (M0)},					/* uart3_rts_sd */
+#endif
 {UART3_RX_IRRX, (IEN | M0)},				/* uart3_rx */
 {UART3_TX_IRTX, (M0)}					/* uart3_tx */
 
diff --git a/arch/arm/include/asm/mach-types.h b/arch/arm/include/asm/mach-types.h
index a1fd03a..d732450 100644
--- a/arch/arm/include/asm/mach-types.h
+++ b/arch/arm/include/asm/mach-types.h
@@ -3312,6 +3312,7 @@
 #define MACH_TYPE_T5388P               3336
 #define MACH_TYPE_DINGO                3337
 #define MACH_TYPE_GOFLEXHOME           3338
+#define MACH_TYPE_STEELHEAD            3533
 
 #ifdef CONFIG_ARCH_EBSA110
 # ifdef machine_arch_type
@@ -36433,6 +36434,18 @@
 # define machine_is_omap4_panda()	(0)
 #endif
 
+#ifdef CONFIG_MACH_STEELHEAD
+# ifdef machine_arch_type
+#  undef machine_arch_type
+#  define machine_arch_type	__machine_arch_type
+# else
+#  define machine_arch_type	MACH_TYPE_STEELHEAD
+# endif
+# define machine_is_steelhead()	(machine_arch_type == MACH_TYPE_STEELHEAD)
+#else
+# define machine_is_steelhead()	(0)
+#endif
+
 #ifdef CONFIG_MACH_DF7220
 # ifdef machine_arch_type
 #  undef machine_arch_type
diff --git a/board/google/tungsten/Makefile b/board/google/tungsten/Makefile
new file mode 100644
index 0000000..25b3dc0
--- /dev/null
+++ b/board/google/tungsten/Makefile
@@ -0,0 +1,51 @@
+#
+# (C) Copyright 2000, 2001, 2002
+# Wolfgang Denk, DENX Software Engineering, wd@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
+#
+
+include $(TOPDIR)/config.mk
+
+LIB	= $(obj)lib$(BOARD).o
+
+ifndef CONFIG_SPL_BUILD
+COBJS	:= $(BOARD).o steelhead_avr.o pseudorandom_ids.o
+endif
+
+SRCS	:= $(COBJS:.o=.c)
+OBJS	:= $(addprefix $(obj),$(COBJS))
+
+$(LIB):	$(obj).depend $(OBJS)
+	$(call cmd_link_o_target, $(OBJS))
+
+clean:
+	rm -f $(OBJS)
+
+distclean:	clean
+	rm -f $(LIB) core *.bak $(obj).depend
+
+#########################################################################
+
+# defines $(obj).depend target
+include $(SRCTREE)/rules.mk
+
+sinclude $(obj).depend
+
+#########################################################################
diff --git a/board/google/tungsten/config.mk b/board/google/tungsten/config.mk
new file mode 100644
index 0000000..3bc935a
--- /dev/null
+++ b/board/google/tungsten/config.mk
@@ -0,0 +1 @@
+PLATFORM_CPPFLAGS += -Werror
diff --git a/board/google/tungsten/pseudorandom_ids.c b/board/google/tungsten/pseudorandom_ids.c
new file mode 100644
index 0000000..81a8305
--- /dev/null
+++ b/board/google/tungsten/pseudorandom_ids.c
@@ -0,0 +1,106 @@
+/*
+ * (C) Copyright 2011
+ * Google, Inc.
+ *
+ * Author :
+ *	John Grossman <johngro@google.com>
+ *
+ * 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
+ */
+
+#include <common.h>
+#include <asm/arch/mem.h>
+#include <asm/arch/sys_proto.h>
+
+#include "pseudorandom_ids.h"
+
+#define DIE_ID_REG_BASE		(OMAP44XX_L4_CORE_BASE + 0x2000)
+#define DIE_ID_REG_OFFSET	0x200
+
+/* Implements a variation of a 64 bit Galois LFSR with the generator
+ *
+ * gp(x) = x^64 + x^63 + x^61 + x^60 + 1
+ *
+ * This variant will actually blend in the bits of data by xor'ing with the
+ * output of the register before feeding back.  Returned data is taken before
+ * this XOR operation.  Passing data = 0 will result in the traditional Galois
+ * LFSR behavior, suitable for a PRNG.
+ *
+ */
+static u32 hash_lfsr_data(u64* state, u32 data) {
+	static const u64 taps = 0xD800000000000000ull;
+	u32 i, ret = 0;
+
+	for (i=0; i < (sizeof(ret) << 3); ++i) {
+		ret <<= 1;
+		ret |= (((u32)(*state)) & 0x1);
+
+		*state >>= 1;
+		if ((ret ^ data) & 0x1)
+			*state ^= taps;
+		data >>= 1;
+	}
+
+	return ret;
+}
+
+static void init_dieid_lfsr(u64* state, u32 salt) {
+	unsigned int reg = DIE_ID_REG_BASE + DIE_ID_REG_OFFSET;
+	int i;
+
+	/* prime the core of the LFSR with the lower 64 bits of the die ID */
+	*state = ((u64)readl(reg + 0x8) << 32) | /* dieid bits [63:32] */
+		  (u64)readl(reg); 		 /* dieid bits [31:0] */
+
+	/* hash in the upper 64 bits of the die ID to finish initializing the
+	 * state
+	 */
+	hash_lfsr_data(state, readl(reg + 0xC));
+	hash_lfsr_data(state, readl(reg + 0x10));
+
+	/* Add the salt */
+	hash_lfsr_data(state, salt);
+
+	/* Blend up the core state a bunch and we are ready to go */
+	for (i = 0; i < 32; ++i)
+		hash_lfsr_data(state, 0);
+}
+
+void generate_default_mac_addr(u32 salt, u8* mac_out) {
+	u64 lfsr_state;
+	u32 rand;
+
+	init_dieid_lfsr(&lfsr_state, salt);
+
+	mac_out[5] = 0x00;
+	mac_out[4] = 0x1A;
+	mac_out[3] = 0x11;
+
+	rand = hash_lfsr_data(&lfsr_state, 0);
+	mac_out[2] = (u8)(rand & 0xFF);
+	mac_out[1] = (u8)((rand >> 8)  & 0xFF);
+	mac_out[0] = (u8)((rand >> 16) & 0xFF);
+}
+
+void generate_default_64bit_id(u32 salt, u64* id_64) {
+	u64 lfsr_state;
+	init_dieid_lfsr(&lfsr_state, salt);
+	*id_64 = ((u64)hash_lfsr_data(&lfsr_state, 0) << 32) |
+		  (u64)hash_lfsr_data(&lfsr_state, 0);
+}
diff --git a/board/google/tungsten/pseudorandom_ids.h b/board/google/tungsten/pseudorandom_ids.h
new file mode 100644
index 0000000..06258a8
--- /dev/null
+++ b/board/google/tungsten/pseudorandom_ids.h
@@ -0,0 +1,28 @@
+/*
+ * (C) Copyright 2011
+ * Google, Inc.
+ *
+ * Author :
+ *	John Grossman <johngro@google.com>
+ *
+ * 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
+ */
+
+extern void generate_default_mac_addr(u32 salt, u8* mac_out);
+extern void generate_default_64bit_id(u32 salt, u64* id_64);
diff --git a/board/google/tungsten/steelhead_avr.c b/board/google/tungsten/steelhead_avr.c
new file mode 100644
index 0000000..4c19fb3
--- /dev/null
+++ b/board/google/tungsten/steelhead_avr.c
@@ -0,0 +1,664 @@
+/* board/google/tungsten/steelhead_avr.c
+ *
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ * This is a driver that communicates with an Atmel AVR ATmega328P
+ * subboard in the Android@Home device via gpios and i2c.  This subboard
+ * is Arduino-Compatible and the firmware in it is developed using the
+ * Arduino SDK.
+ *
+ * The functionality implemented by the subboard is a set of capacitive touch
+ * keys and many leds.  To keep things simple for now, we have just
+ * one driver that implements two input_device exposing the keys and
+ * a misc_device exposing custom ioctls for controlling the leds.  We don't
+ * use the Linux led driver API because we have too many leds and want
+ * a more custom API to be more efficient.  Also, the subboard firmware
+ * implements some macro led modes (like volume mode) which doesn't make
+ * sense in the led API.
+ */
+
+#include <common.h>
+#include <config.h>
+#include <command.h>
+#include <i2c.h>
+#include <malloc.h>
+#include <errno.h>
+#include <fastboot.h>
+#include <linux/ctype.h>
+
+#include "steelhead_avr.h"
+#include "steelhead_avr_regs.h"
+
+#define LED_BYTE_SZ 3
+
+#define AVR_I2C_CLIENT_ID (0x20) /* 7 bit i2c id */
+#define AVR_I2C_BUS_ID    (0x1)
+
+static struct avr_driver_state {
+	int avr_detected;
+
+	/* device info */
+	u16 firmware_rev;
+	u8  hardware_type;
+	u8  hardware_rev;
+	u16 led_count;
+
+	/* Current LED state. */
+	u8 led_mode;
+	u8 mute_threshold;
+} state;
+
+/* the AVR can be temporarily busy doing other things and be unable
+ * to respond to a i2c request (it's not multi-threaded) so we need
+ * to retry on failure a few times.
+ */
+#define MAX_I2C_ATTEMPTS 5
+static int avr_i2c_write(u8 cmd, u8* buf, u16 len)
+{
+	int rc, try;
+
+	rc = i2c_set_bus_num(AVR_I2C_BUS_ID);
+	if (rc) {
+		printf("Failed in i2c_set_bus_num(%d), error %d\n",
+		       AVR_I2C_BUS_ID, rc);
+		return rc;
+	}
+
+	for (try = 0; try < MAX_I2C_ATTEMPTS; try++) {
+		rc = i2c_write(AVR_I2C_CLIENT_ID, cmd, 1, buf, len);
+		if (rc == 0)
+			break;
+	}
+	if (rc) {
+		printf("%s: write to reg %d failed after %d attempts\n",
+		       __func__, cmd, MAX_I2C_ATTEMPTS);
+	}
+	return rc;
+}
+
+/* the AVR can't do a normal i2c_read() with the
+ * register address passed in one transfer.
+ * it needs a delay between sending the register
+ * address and the read transfer.
+ */
+static int avr_i2c_read(u8 cmd, u16 len, u8 *buf)
+{
+	int rc, try;
+
+	rc = i2c_set_bus_num(AVR_I2C_BUS_ID);
+	if (rc) {
+		printf("Failed in i2c_set_bus_num(%d), error %d\n",
+		       AVR_I2C_BUS_ID, rc);
+		return rc;
+	}
+
+	for (try = 0; try < MAX_I2C_ATTEMPTS; try++) {
+		rc = i2c_write(AVR_I2C_CLIENT_ID, cmd, 1, NULL, 0);
+		if (rc == 0)
+			break;
+	}
+	if (rc) {
+		printf("%s: error writing reg addr %u\n", __func__, cmd);
+		return rc;
+	}
+
+	/* Need to wait a little bit between the write of the register ID
+	 * and the read of the actual data.  Failure to do so will not
+	 * result in a NAK, only corrupt data.
+	 */
+	udelay(50);
+
+	for (try = 0; try < MAX_I2C_ATTEMPTS; try++) {
+		rc = i2c_read(AVR_I2C_CLIENT_ID, 0, 0, buf, len);
+		if (rc == 0)
+			return 0;
+	}
+	printf("%s: error reading reg %u, failed after %d attempts\n",
+	       __func__, cmd, MAX_I2C_ATTEMPTS);
+	return -1;
+}
+
+static int avr_get_firmware_rev(void)
+{
+	u8 buf[2];
+	int rc = avr_i2c_read(AVR_FW_VERSION_REG_ADDR, sizeof(buf), buf);
+
+	state.firmware_rev = ((u16)buf[0] << 8) | (u16)buf[1];
+	return rc;
+}
+
+static int avr_get_hardware_type(void)
+{
+	return avr_i2c_read(AVR_HW_TYPE_REG_ADDR, 1, &state.hardware_type);
+}
+
+static int avr_get_hardware_rev(void)
+{
+	return avr_i2c_read(AVR_HW_REVISION_REG_ADDR, 1, &state.hardware_rev);
+}
+
+static int avr_get_led_count(void)
+{
+	u8 buf[2];
+	int rc = avr_i2c_read(AVR_LED_GET_COUNT_ADDR, sizeof(buf), buf);
+
+	state.led_count = ((u16)buf[0] << 8) | (u16)buf[1];
+	return rc;
+}
+
+int avr_led_set_all(const struct avr_led_rgb_vals *req)
+{
+	if (!req)
+		return -EFAULT;
+
+	return avr_i2c_write(AVR_LED_SET_ALL_REG_ADDR,
+			     (uchar *)req->rgb, sizeof(req->rgb));
+}
+
+int avr_led_set_mute(const struct avr_led_rgb_vals *req)
+{
+	if (!req)
+		return -EFAULT;
+
+	return avr_i2c_write(AVR_LED_SET_MUTE_ADDR,
+			     (uchar *)req->rgb, sizeof(req->rgb));
+}
+
+int avr_set_mute_threshold(u8 mute_threshold)
+{
+	return avr_i2c_write(AVR_KEY_MUTE_THRESHOLD_REG_ADDR,
+			     &mute_threshold, sizeof(mute_threshold));
+
+}
+
+static int avr_get_mute_threshold(void)
+{
+	return avr_i2c_read(AVR_KEY_MUTE_THRESHOLD_REG_ADDR,
+			    1, &state.mute_threshold);
+}
+
+int avr_led_set_range(struct avr_led_set_range_vals *req)
+{
+	if (!req)
+		return -EFAULT;
+	printf("Sending i2c set range packet, %d bytes\n", 3 + (req->rgb_triples * 3));
+	{
+		int i;
+		for (i = 0; i < (3 + (req->rgb_triples * 3)); i++) {
+			printf("0x%x\n", ((uint8_t*)req)[i]);
+		}
+	}
+	return avr_i2c_write(AVR_LED_SET_RANGE_REG_ADDR,
+			     (uint8_t*)req, 3 + (req->rgb_triples * 3));
+}
+
+int avr_led_set_mode(u8 mode)
+{
+	int rc = avr_i2c_write(AVR_LED_MODE_REG_ADDR, &mode, 1);
+	/* If the command failed, then skip the update of our internal
+	 * bookkeeping and just get out.
+	 */
+	if (rc)
+		return rc;
+
+	state.led_mode = mode;
+	return rc;
+}
+
+int avr_led_commit_led_state(u8 val)
+{
+	return avr_i2c_write(AVR_LED_COMMIT_REG_ADDR, &val, 1);
+}
+
+static int avr_read_event_fifo(u8 *next_event)
+{
+	if (!next_event)
+		return -EFAULT;
+
+	return avr_i2c_read(AVR_KEY_EVENT_FIFO_REG_ADDR, 1, next_event);
+}
+
+int detect_avr(void)
+{
+	int rc;
+	struct avr_led_rgb_vals clear_led_req;
+
+	printf("%s\n", __func__);
+
+	/* Cache the firmware revision (also checks to be sure the AVR is
+	 * actually there and talking to us).
+	 */
+	rc = avr_get_firmware_rev();
+	if (rc) {
+		printf("Failed to fetch AVR firmware revision (rc = %d)\n", rc);
+		goto error;
+	}
+
+	/* Cache the hardware type and revision.
+	 */
+	rc = avr_get_hardware_type();
+	if (rc) {
+		printf("Failed to fetch AVR hardware type (rc = %d)\n", rc);
+		goto error;
+	}
+
+	rc = avr_get_hardware_rev();
+	if (rc) {
+		printf("Failed to fetch AVR hardware revision (rc = %d)\n", rc);
+		goto error;
+	}
+
+	rc = avr_get_led_count();
+	if (rc) {
+		printf("Failed to fetch AVR led count (rc = %d)\n", rc);
+		goto error;
+	}
+
+	/* Set the LED state to all off in order to match the internal state we
+	 * just established.
+	 */
+	clear_led_req.rgb[0] = 0x00;
+	clear_led_req.rgb[1] = 0x00;
+	clear_led_req.rgb[2] = 0x00;
+	rc = avr_led_set_all(&clear_led_req);
+	if (rc) {
+		printf("Failed to clear LEDs on AVR (rc = %d)\n", rc);
+		goto error;
+	}
+
+	printf("Steelhead AVR detected and initialized\n");
+
+	state.avr_detected = 1;
+	return 0;
+ error:
+	return rc;
+}
+
+
+static int do_avr_reinit(cmd_tbl_t *cmdtp, int flag,
+			 int argc, char * const argv[])
+{
+	/* force redetect */
+	if (detect_avr()) {
+		puts("Could not detect avr\n");
+		return 1;
+	}
+	return 0;
+}
+
+static int do_avr_commit_state(cmd_tbl_t *cmdtp, int flag,
+			 int argc, char * const argv[])
+{
+	if (avr_led_commit_led_state(1)) {
+		printf("Error committing led state\n");
+		return 1;
+	}
+	printf("Succeeded committing led state\n");
+	return 0;
+}
+
+int avr_get_key(u8 *key_code)
+{
+	u8 next_event;
+	int rc = i2c_set_bus_num(AVR_I2C_BUS_ID);
+	if (rc) {
+		printf("Failed in i2c_set_bus_num(%d), error %d\n",
+		       AVR_I2C_BUS_ID, rc);
+		return rc;
+	}
+	rc = avr_read_event_fifo(&next_event);
+	if (rc) {
+		printf("Failed to read event fifo, err = %d\n", rc);
+		return 1;
+	}
+	*key_code = next_event;
+	if (next_event != AVR_KEY_EVENT_EMPTY) {
+		printf("%s returning 0x%x\n", __func__, next_event);
+	}
+	return 0;
+}
+
+static int do_avr_get_key(cmd_tbl_t *cmdtp, int flag,
+			 int argc, char * const argv[])
+{
+	int saw_key = 0;
+	int rc;
+	u8 next_event;
+
+	while (1) {
+		rc = avr_get_key(&next_event);
+		if (rc)
+			return 1;
+		if (next_event == AVR_KEY_EVENT_EMPTY) {
+			if (saw_key)
+				printf("done\n");
+			else
+				printf("avr returned no key events\n");
+			break;
+		} else {
+			int was_down = next_event & AVR_KEY_EVENT_DOWN;
+			u8 key_code = next_event & AVR_KEY_EVENT_CODE_MASK;
+			char *key_string[3] = {"UNKNOWN", "VOLUME_UP",
+					       "VOLUME_DOWN"};
+			saw_key = 1;
+			printf("avr returned key raw = %d, '%s' %s\n",
+			       next_event,
+			       key_code < ARRAY_SIZE(key_string) ?
+			       key_string[key_code] : "BAD CODE",
+			       was_down ? "down" : "up");
+		}
+	}
+	return 0;
+}
+
+static int do_avr_get_info(cmd_tbl_t *cmdtp, int flag,
+			   int argc, char * const argv[])
+{
+	printf("avr info:\n");
+	printf("  firmware_rev = %d.%d\n",
+	       state.firmware_rev >> 8,
+	       state.firmware_rev & 0xFF);
+	printf("  hardware_type = %d\n", state.hardware_type);
+	printf("  hardware_rev = %d\n", state.hardware_rev);
+	printf("  led_mode = %d\n", state.led_mode);
+	printf("  led_count = %d\n", state.led_count);
+	avr_get_mute_threshold();
+	printf("  mute_threshold = %d\n", state.mute_threshold);
+	return 0;
+}
+
+static int do_avr_set_mode(cmd_tbl_t *cmdtp, int flag,
+			   int argc, char * const argv[])
+{
+	ulong raw;
+	const char *mode_names[] = {"BOOT_ANIMATION", "HOST_AUTO_COMMIT", "HOST"};
+	if (argc != 2) {
+		printf("usage: avr set mode "
+		       "[0=boot_animation,1=host_auto_commit,2=host]\n");
+		return 1;
+	}
+	raw = simple_strtoul(argv[1], NULL, 10);
+	if (raw > AVR_LED_MODE_HOST) {
+		printf("invalid mode %lu, must be 0 to %d\n",
+		       raw, AVR_LED_MODE_HOST);
+		return 1;
+	}
+	if (avr_led_set_mode(raw)) {
+		printf("Error setting mode [%lu:%s]\n",
+		       raw, mode_names[raw]);
+	} else {
+		printf("Succeeded setting mode [%lu:%s]\n",
+		       raw, mode_names[raw]);
+	}
+	return 0;
+}
+
+static int do_avr_set_all(cmd_tbl_t *cmdtp, int flag,
+			  int argc, char * const argv[])
+{
+	struct avr_led_rgb_vals req;
+	ulong raw;
+	if (argc != 2) {
+		printf("usage: avr set all rgb888_value\n");
+		return 1;
+	}
+	raw = simple_strtoul(argv[1], NULL, 16);
+	if (raw > 0x00ffffff) {
+		printf("rgb888_value 0x%lx too large\n", raw);
+		return 1;
+	}
+	req.rgb[0] = (raw >> 16) & 0xff;
+	req.rgb[1] = (raw >> 8) & 0xff;
+	req.rgb[2] = (raw >> 0) & 0xff;
+	if (avr_led_set_all(&req)) {
+		printf("Error setting all led values\n");
+		return 1;
+	}
+	printf("Succeeded setting all led values\n");
+	return 0;
+}
+
+static int do_avr_set_mute(cmd_tbl_t *cmdtp, int flag,
+			  int argc, char * const argv[])
+{
+	struct avr_led_rgb_vals req;
+	ulong raw;
+	if (argc != 2) {
+		printf("usage: avr set mute rgb888_value\n");
+		return 1;
+	}
+	raw = simple_strtoul(argv[1], NULL, 16);
+	if (raw > 0x00ffffff) {
+		printf("rgb888_value 0x%lx too large\n", raw);
+		return 1;
+	}
+	req.rgb[0] = (raw >> 16) & 0xff;
+	req.rgb[1] = (raw >> 8) & 0xff;
+	req.rgb[2] = (raw >> 0) & 0xff;
+	if (avr_led_set_mute(&req)) {
+		printf("Error setting mute led values\n");
+		return 1;
+	}
+	printf("Succeeded setting mute led values\n");
+	return 0;
+}
+
+static int do_avr_set_mute_threshold(cmd_tbl_t *cmdtp, int flag,
+				     int argc, char * const argv[])
+{
+	ulong raw;
+	if (argc != 2) {
+		printf("usage: avr set mute_threshold value\n");
+		return 1;
+	}
+	raw = simple_strtoul(argv[1], NULL, 10);
+	if (raw > 0xff) {
+		printf("mute_threshold_value 0x%lx too large\n", raw);
+		return 1;
+	}
+	if (avr_set_mute_threshold((u8)raw)) {
+		printf("Error setting mute_threshold\n");
+		return 1;
+	}
+	printf("Succeeded setting mute_threshold\n");
+	return 0;
+}
+
+/* examples:
+  1) to set 4 leds starting at 0 to white, red, green, blue:
+    avr set range 0 4 ffffffff000000ff000000ff
+  2) to set all first led to green and rest to white:
+    avr set range 0 32 00ff00ffffff
+*/
+static int do_avr_set_range(cmd_tbl_t *cmdtp, int flag,
+			  int argc, char * const argv[])
+{
+	struct avr_led_set_range_vals req;
+	ulong start, count, rgb_triples, end;
+	ulong value, i;
+	char *cp;
+	if (argc != 4) {
+		printf("usage: avr set range start count rgb888_hex_string\n");
+		return 1;
+	}
+	start = simple_strtoul(argv[1], NULL, 10);
+	if (start > state.led_count) {
+		printf("start %lu too large, must be between 0 and %u\n",
+		       start, state.led_count);
+		return 1;
+	}
+	count = simple_strtoul(argv[2], NULL, 10);
+	if (count == 0) {
+		printf("0 count invalid\n");
+		return 1;
+	}
+	end = start + count - 1;
+	if (end >= state.led_count) {
+		printf("count %lu too large, must be between 1 and %lu\n",
+		       count, state.led_count - start);
+		return 1;
+	}
+	rgb_triples = strlen(argv[3]);
+	/* the length of the string must be a multiple of 6 bytes
+	   (two bytes each for red, green, and blue */
+	if (rgb_triples % 6) {
+		printf("rgb888_hex_string is invalid, must have a multiple of 6 characters,"
+		       "two each for red, green, and blue\n");
+		return 1;
+	}
+	rgb_triples /= 6;
+
+	req.start = start;
+	req.count = count;
+	req.rgb_triples = rgb_triples;
+	cp = argv[3];
+	for (i = 0; i < rgb_triples; i++) {
+		int j;
+		for (j = 0; j < 3; j++) {
+			value = (isdigit(*cp) ? *cp - '0' : (islower(*cp) ? toupper(*cp) : *cp) - 'A' + 10) * 16;
+			cp++;
+			value += (isdigit(*cp) ? *cp - '0' : (islower(*cp) ? toupper(*cp) : *cp) - 'A' + 10);
+			cp++;
+
+			req.rgb[i][j] = value;
+		}
+	}
+	if (avr_led_set_range(&req)) {
+		printf("Error setting range led values\n");
+		return 1;
+	}
+	printf("Succeeded setting range led values\n");
+	return 0;
+}
+
+static cmd_tbl_t cmd_avr_get_sub[] = {
+	U_BOOT_CMD_MKENT(info, 1, 1, do_avr_get_info, "", ""),
+	U_BOOT_CMD_MKENT(key, 1, 1, do_avr_get_key, "", ""),
+};
+
+static int do_avr_get(cmd_tbl_t *cmdtp, int flag,
+			 int argc, char * const argv[])
+{
+	cmd_tbl_t *c;
+
+	if (argc < 2) {
+		printf("Too few arguments\n");
+		return cmd_usage(cmdtp);
+	}
+
+	/* Strip off leading 'get' command argument */
+	argc--;
+	argv++;
+
+	c = find_cmd_tbl(argv[0], &cmd_avr_get_sub[0],
+			 ARRAY_SIZE(cmd_avr_get_sub));
+
+	if (c)
+		return c->cmd(cmdtp, flag, argc, argv);
+	else {
+		printf("unknown command %s\n", argv[2]);
+		return cmd_usage(cmdtp);
+	}
+}
+
+static cmd_tbl_t cmd_avr_set_sub[] = {
+	U_BOOT_CMD_MKENT(mode, 2, 1, do_avr_set_mode, "", ""),
+	U_BOOT_CMD_MKENT(all, 2, 1, do_avr_set_all, "", ""),
+	U_BOOT_CMD_MKENT(mute, 2, 1, do_avr_set_mute, "", ""),
+	U_BOOT_CMD_MKENT(mute_threshold, 2, 1, do_avr_set_mute_threshold, "", ""),
+	U_BOOT_CMD_MKENT(range, 4, 1, do_avr_set_range, "", ""),
+};
+
+static int do_avr_set(cmd_tbl_t *cmdtp, int flag,
+			 int argc, char * const argv[])
+{
+	cmd_tbl_t *c;
+
+	if (argc < 2) {
+		printf("Too few arguments\n");
+		return cmd_usage(cmdtp);
+	}
+
+	/* Strip off leading 'set' command argument */
+	argc--;
+	argv++;
+
+	c = find_cmd_tbl(argv[0], &cmd_avr_set_sub[0],
+			 ARRAY_SIZE(cmd_avr_set_sub));
+
+	if (c)
+		return c->cmd(cmdtp, flag, argc, argv);
+	else {
+		printf("unknown command %s\n", argv[2]);
+		return cmd_usage(cmdtp);
+	}
+}
+
+static cmd_tbl_t cmd_avr_sub[] = {
+	U_BOOT_CMD_MKENT(reinit, 1, 1, do_avr_reinit, "", ""),
+	U_BOOT_CMD_MKENT(commit_state, 1, 1, do_avr_commit_state, "", ""),
+	U_BOOT_CMD_MKENT(get, 5, 1, do_avr_get, "", ""),
+	U_BOOT_CMD_MKENT(set, 6, 1, do_avr_set, "", ""),
+};
+
+#ifdef CONFIG_NEEDS_MANUAL_RELOC
+void avr_reloc(void)
+{
+	fixup_cmdtable(cmd_avr_sub, ARRAY_SIZE(cmd_avr_sub));
+	fixup_cmdtable(cmd_avr_get_sub, ARRAY_SIZE(cmd_avr_get_sub));
+	fixup_cmdtable(cmd_avr_set_sub, ARRAY_SIZE(cmd_avr_set_sub));
+}
+#endif
+
+static int do_avr(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+	cmd_tbl_t *c;
+
+	if (argc < 2) {
+		printf("Too few arguments\n");
+		return cmd_usage(cmdtp);
+	}
+
+	if (!state.avr_detected) {
+		if (detect_avr()) {
+			puts("Could not detect avr\n");
+			return 1;
+		}
+	}
+
+	/* Strip off leading 'avr' command argument */
+	argc--;
+	argv++;
+
+	c = find_cmd_tbl(argv[0], &cmd_avr_sub[0], ARRAY_SIZE(cmd_avr_sub));
+
+	if (c)
+		return c->cmd(cmdtp, flag, argc, argv);
+	else {
+		printf("unknown command %s\n", argv[2]);
+		return cmd_usage(cmdtp);
+	}
+}
+
+U_BOOT_CMD(
+	avr, 6, 1, do_avr,
+	"steelhead avr sub system",
+	"avr reinit\n"
+	"avr commit_state\n"
+	"avr get key\n"
+	"avr get info\n"
+	"avr set mode [0=boot_animation,1=host_auto_commit,2=host]\n"
+	"avr set all rgb888_hex\n"
+	"avr set mute rgb888_hex\n"
+	"avr set mute_threshold value\n"
+	"avr set range start count rgb888_hex_string\n");
+
diff --git a/board/google/tungsten/steelhead_avr.h b/board/google/tungsten/steelhead_avr.h
new file mode 100644
index 0000000..4d369b3
--- /dev/null
+++ b/board/google/tungsten/steelhead_avr.h
@@ -0,0 +1,50 @@
+/* board/google/tungsten/steelhead_avr.h
+ *
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ * This is a driver that communicates with an Atmel AVR ATmega328P
+ * subboard in the Android@Home device via gpios and i2c.  This subboard
+ * is Arduino-Compatible and the firmware in it is developed using the
+ * Arduino SDK.
+ *
+ * The functionality implemented by the subboard is a set of capacitive touch
+ * keys and many leds.  To keep things simple for now, we have just
+ * one driver that implements two input_device exposing the keys and
+ * a misc_device exposing custom ioctls for controlling the leds.  We don't
+ * use the Linux led driver API because we have too many leds and want
+ * a more custom API to be more efficient.  Also, the subboard firmware
+ * implements some macro led modes (like volume mode) which doesn't make
+ * sense in the led API.
+ */
+#ifndef _STEELHEAD_AVR_H_
+#define _STEELHEAD_AVR_H_
+
+struct avr_led_rgb_vals {
+	u8 rgb[3];
+};
+
+struct avr_led_set_range_vals {
+	u8 start;
+	u8 count;
+	u8 rgb_triples;
+	u8 rgb[32][3]; /* maximum number for now */
+};
+
+extern int detect_avr(void);
+extern int avr_led_set_all(const struct avr_led_rgb_vals *req);
+extern int avr_led_set_mute(const struct avr_led_rgb_vals *req);
+extern int avr_led_commit_led_state(u8 val);
+extern int avr_led_set_mode(u8 mode);
+extern int avr_get_key(u8 *key_code);
+extern int avr_set_mute_threshold(u8 mute_threshold);
+
+#endif /* _STEELHEAD_AVR_H_ */
diff --git a/board/google/tungsten/steelhead_avr_regs.h b/board/google/tungsten/steelhead_avr_regs.h
new file mode 100644
index 0000000..56da1d2
--- /dev/null
+++ b/board/google/tungsten/steelhead_avr_regs.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+
+#ifndef __STEELHEAD_AVR_REGS_H
+#define __STEELHEAD_AVR_REGS_H
+
+/* key event registers */
+#define AVR_KEY_EVENT_FIFO_REG_ADDR	0x00
+#define AVR_KEY_EVENT_DOWN		0x80	/* set when key down */
+#define AVR_KEY_EVENT_CODE_MASK		0x3F	/* mask for key code
+						 * (keeping bit unused)
+						 */
+#define AVR_KEY_EVENT_EMPTY		0xFF	/* key event value returned
+						 * when fifo empty
+						 */
+
+#define AVR_KEY_MUTE_THRESHOLD_REG_ADDR 0x01
+
+#define AVR_KEY_MUTE			0x00
+#define AVR_KEY_VOLUME_UP		(AVR_KEY_MUTE + 1)
+#define AVR_KEY_VOLUME_DOWN		(AVR_KEY_VOLUME_UP + 1)
+#define AVR_KEYCODE_COUNT		(AVR_KEY_VOLUME_DOWN + 1)
+
+/* led registers */
+#define AVR_LED_MODE_REG_ADDR		0x02
+#define AVR_LED_MODE_BOOT_ANIMATION	0x00
+#define AVR_LED_MODE_HOST_AUTO_COMMIT	0x01
+#define AVR_LED_MODE_HOST		0x02
+
+#define AVR_LED_SET_ALL_REG_ADDR	0x03
+
+#define AVR_LED_SET_RANGE_REG_ADDR	0x04
+
+#define AVR_LED_COMMIT_REG_ADDR		0x05	/* push the buffered host led
+						 * values to active display
+						 */
+#define AVR_LED_COMMMIT_IMMEDIATELY	0x00	/* update the active led's
+						 * immediately
+						 */
+#define AVR_LED_COMMMIT_INTERPOLATE	0x01	/* animate from the current
+						 * led state to the new one?
+						 * Later!
+						 */
+
+#define AVR_LED_SET_MUTE_ADDR		0x06	/* set mute led color */
+#define AVR_LED_GET_COUNT_ADDR		0x07	/* get # of leds */
+
+
+/* fw registers */
+#define AVR_HW_TYPE_REG_ADDR		0x08
+#define AVR_HE_TYPE_UNKNOWN		0x00
+#define AVR_HE_TYPE_SPHERE		0x01
+#define AVR_HE_TYPE_RHOMBUS		0x02
+
+#define AVR_HW_REVISION_REG_ADDR	0x09
+
+#define AVR_FW_VERSION_REG_ADDR		0x0a	/* 16 bit register 8.8 */
+
+#endif  /* __STEELHEAD_AVR_REGS_H */
diff --git a/board/google/tungsten/tungsten.c b/board/google/tungsten/tungsten.c
new file mode 100644
index 0000000..6a790b7
--- /dev/null
+++ b/board/google/tungsten/tungsten.c
@@ -0,0 +1,895 @@
+/*
+ * (C) Copyright 2011
+ * Google, Inc.
+ * (C) Copyright 2010
+ * Texas Instruments Incorporated, <www.ti.com>
+ *
+ * Author :
+ *	Mike J Chen <mjchen@google.com>
+ *
+ * Derived from Panda Board code by
+ *	Steve Sakoman  <steve@sakoman.com>
+ *
+ * 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
+ */
+#include <common.h>
+#include <asm/gpio.h>
+#include <asm/arch/sys_proto.h>
+#include <asm/arch/mmc_host_def.h>
+#include <fastboot.h>
+#include <mmc.h>
+#include <malloc.h>
+#include <linux/string.h>
+
+#include "steelhead_avr.h"
+#include "steelhead_avr_regs.h"
+#include "tungsten_mux_data.h"
+#include "pseudorandom_ids.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+enum steelhead_rev {
+	STEELHEAD_REV_ALPHA  = 0x0,
+	STEELHEAD_REV_EVT    = 0x1,
+	STEELHEAD_REV_EVT2   = 0x2,
+	STEELHEAD_REV_DVT    = 0x3,
+	STEELHEAD_REV_DVT1_5 = 0x4,
+	STEELHEAD_REV_DVT2   = 0x5,
+	STEELHEAD_REV_DVT3   = 0x6,
+	STEELHEAD_REV_DVT4   = 0x7,
+	STEELHEAD_REV_DVT5   = 0x8,
+	STEELHEAD_REV_PVT    = 0x9,
+	STEELHEAD_REV_PROD   = 0xA,
+};
+
+static const char const *steelhead_hw_name[] = {
+	[STEELHEAD_REV_ALPHA]  = "Steelhead ALPHA",
+	[STEELHEAD_REV_EVT]    = "Steelhead EVT",
+	[STEELHEAD_REV_EVT2]   = "Steelhead EVT2",
+	[STEELHEAD_REV_DVT]    = "Steelhead DVT",
+	[STEELHEAD_REV_DVT1_5] = "Steelhead DVT1.5",
+	[STEELHEAD_REV_DVT2]   = "Steelhead DVT2",
+	[STEELHEAD_REV_DVT3]   = "Steelhead DVT3",
+	[STEELHEAD_REV_DVT4]   = "Steelhead DVT4",
+	[STEELHEAD_REV_DVT5]   = "Steelhead DVT5",
+	[STEELHEAD_REV_PVT]    = "Steelhead PVT",
+	[STEELHEAD_REV_PROD]   = "Steelhead PROD",
+};
+int hwrev_gpios[] = {
+	182, /* board_id_0 */
+	101, /* board_id_1 */
+	171, /* board_id_2 */
+};
+/* We have 3 bits of board-id to track revision.  Older
+ * revisions started to get deprecated and their board-id
+ * values reused, so we use a mapping table to converet
+ * the raw board-id values to the enum values.
+ */
+static const enum steelhead_rev board_id_to_steelhead_rev[8] = {
+	STEELHEAD_REV_DVT5,   /* board_id: 0x0 */
+	STEELHEAD_REV_PVT,    /* board_id: 0x1 */
+	STEELHEAD_REV_PROD,   /* board_id: 0x2 */
+	STEELHEAD_REV_DVT,    /* board_id: 0x3 */
+	STEELHEAD_REV_DVT1_5, /* board_id: 0x4 */
+	STEELHEAD_REV_DVT2,   /* board_id: 0x5 */
+	STEELHEAD_REV_DVT3,   /* board_id: 0x6 */
+	STEELHEAD_REV_DVT4    /* board_id: 0x7 */
+};
+
+enum steelhead_rev steelhead_hw_rev;
+int avr_detected;
+
+static unsigned long key_pressed_start_time;
+static unsigned long last_time;
+#define KEY_CHECK_POLLING_INTERVAL_MS 100
+#define RECOVERY_KEY_HOLD_TIME_SECS 10
+
+static int force_fastboot = 0;
+
+const struct omap_sysinfo sysinfo = {
+	"Board: OMAP4 Tungsten\n"
+};
+
+struct mac_generator {
+	const u32 salt;
+	const char* name;
+};
+
+#define MAKE_SALT(a, b, c, d) (((u32)a << 24) | ((u32)b << 16) | \
+			      ((u32)c <<  8) | ((u32)d))
+static const struct mac_generator mac_defaults[] = {
+	{ MAKE_SALT('W','i','F','i'), "androidboot.wifi_macaddr" },
+	{ MAKE_SALT('W','i','r','e'), "smsc95xx.mac_addr" },
+	{ MAKE_SALT('B','l','u','T'), "board_steelhead_bluetooth.btaddr" },
+};
+static const u32 serial_no_salt = MAKE_SALT('S','e','r','#');
+#undef MAKE_SALT
+
+static const char *steelhead_hw_rev_name(void)
+{
+	int num = ARRAY_SIZE(steelhead_hw_name);
+
+	if (steelhead_hw_rev >= num ||
+	    !steelhead_hw_name[steelhead_hw_rev])
+		return "Steelhead unknown version";
+
+	return steelhead_hw_name[steelhead_hw_rev];
+}
+
+static void init_hw_rev(void)
+{
+	int i;
+	int board_id;
+
+	do_set_mux(CONTROL_PADCONF_CORE, core_padconf_array_non_essential,
+		   sizeof(core_padconf_array_non_essential) /
+		   sizeof(struct pad_conf_entry));
+
+	board_id = 0;
+
+	for (i = 0; i < ARRAY_SIZE(hwrev_gpios); i++)
+		board_id |= gpio_get_value(hwrev_gpios[i]) << i;
+
+	/* put board_id pins into safe mode to save power */
+	do_set_mux(CONTROL_PADCONF_CORE, core_padconf_array_disable_board_id,
+		   sizeof(core_padconf_array_disable_board_id) /
+		   sizeof(struct pad_conf_entry));
+
+	/* not absolutely necessary but good in case the size of the
+	   array ever changes */
+	if (board_id < ARRAY_SIZE(board_id_to_steelhead_rev)) {
+		steelhead_hw_rev = board_id_to_steelhead_rev[board_id];
+		printf("HW revision: 0x%x = \"%s\" (board_id 0x%x)\n",
+		       steelhead_hw_rev, steelhead_hw_rev_name(), board_id);
+	} else {
+		/* default to the highest rev we know of */
+		steelhead_hw_rev = STEELHEAD_REV_PROD;
+		printf("board_id 0x%x invalid, setting steelhead_hw_rev to "
+		       "0x%x = \"%s\"", board_id,
+		       steelhead_hw_rev, steelhead_hw_rev_name());
+	}
+}
+
+/**
+ * @brief board_init
+ *
+ * @return 0
+ */
+int board_init(void)
+{
+	/* gpmc_init() touches bss, which cannot be used until
+	 * after relocation.
+	 */
+	gpmc_init();
+	gd->bd->bi_arch_number = MACH_TYPE_STEELHEAD;
+	gd->bd->bi_boot_params = (0x80000000 + 0x100); /* boot param addr */
+	init_hw_rev();
+	return 0;
+}
+
+int board_eth_init(bd_t *bis)
+{
+	return 0;
+}
+
+/**
+ * @brief set_muxconf_regs Setting up the configuration Mux registers
+ * specific to the board.
+ */
+void set_muxconf_regs_non_essential(void)
+{
+	do_set_mux(CONTROL_PADCONF_CORE, core_padconf_array_non_essential,
+		   sizeof(core_padconf_array_non_essential) /
+		   sizeof(struct pad_conf_entry));
+	do_set_mux(CONTROL_PADCONF_WKUP, wkup_padconf_array_non_essential,
+		   sizeof(wkup_padconf_array_non_essential) /
+		   sizeof(struct pad_conf_entry));
+}
+
+#ifdef CONFIG_GENERIC_MMC
+static int samsung_mmc_cmd(struct mmc *mmc, uint mode)
+{
+	struct mmc_cmd cmd;
+	int ret;
+
+	cmd.cmdidx = 62;		/* Samsung OEM command */
+	cmd.cmdarg = 0xEFAC62EC;	/* Magic value */
+	cmd.resp_type = MMC_RSP_R1b;
+	cmd.flags = 0;
+	ret = mmc->send_cmd(mmc, &cmd, NULL);
+	if (ret) {
+		printf("%s: Error %d sending magic.\n", __func__, ret);
+		return 1;
+	}
+	cmd.cmdarg = mode;
+	ret = mmc->send_cmd(mmc, &cmd, NULL);
+	if (ret) {
+		printf("%s: Error %d sending mode 0x%08X.\n",
+						__func__, ret, mode);
+		return 1;
+	}
+	return 0;
+}
+static int samsung_mmc_mem(struct mmc *mmc, uint addr, uint val)
+{
+	struct mmc_cmd cmd;
+	int ret;
+
+	cmd.cmdidx = MMC_CMD_ERASE_GROUP_START;
+	cmd.cmdarg = addr;
+	cmd.resp_type = MMC_RSP_R1;
+	cmd.flags = 0;
+	ret = mmc->send_cmd(mmc, &cmd, NULL);
+	if (ret) {
+		printf("%s: Error %d sending addr 0x%08X.\n",
+						__func__, ret, addr);
+		return 1;
+	}
+	cmd.cmdidx = MMC_CMD_ERASE_GROUP_END;
+	cmd.cmdarg = val;
+	ret = mmc->send_cmd(mmc, &cmd, NULL);
+	if (ret) {
+		printf("%s: Error %d sending val 0x%08X.\n",
+						__func__, ret, val);
+		return 1;
+	}
+	cmd.cmdidx = MMC_CMD_ERASE;
+	cmd.cmdarg = 0;
+	ret = mmc->send_cmd(mmc, &cmd, NULL);
+	if (ret) {
+		printf("%s: Error %d latching 0x%08X:0x%08X.\n",
+						__func__, ret, addr, val);
+		return 1;
+	}
+	return 0;
+}
+static int samsung_mmc_read_word(struct mmc *mmc, uint *val)
+{
+	struct mmc_cmd cmd;
+	struct mmc_data data;
+	uint *dst;
+	int ret;
+
+	cmd.cmdidx = MMC_CMD_READ_SINGLE_BLOCK;
+	cmd.cmdarg = 0;
+	cmd.resp_type = MMC_RSP_R1;
+	cmd.flags = 0;
+
+	dst = malloc(mmc->read_bl_len);
+	if (!dst) {
+		printf("%s: Error allocating read buffer.\n", __func__);
+		return 1;
+	}
+	data.dest = (char *)dst;
+	data.blocks = 1;
+	data.blocksize = mmc->read_bl_len;
+	data.flags = MMC_DATA_READ;
+
+	ret = mmc->send_cmd(mmc, &cmd, &data);
+	if (ret)
+		printf("%s: Error %d reading.\n", __func__, ret);
+	else
+		*val = dst[0];
+
+	free(dst);
+	return ret;
+}
+
+int board_mmc_init(bd_t *bis)
+{
+	struct mmc *mmc;
+	uint8_t mmc_manufacturer;
+	uint8_t mmc_firmware_version;
+	int err;
+
+	omap_mmc_init(CONFIG_MMC_DEV);
+
+	mmc = find_mmc_device(CONFIG_MMC_DEV);
+	if (!mmc) {
+		printf("mmc device not found!!\n");
+		/* Having mmc_initialize() invoke cpu_mmc_init() won't help. */
+		return 0;
+	}
+
+	err = mmc_init(mmc);
+	if (err)
+		printf("mmc init failed: err - %d\n", err);
+
+	printf("cid = %x%08x%08x%08x\n", mmc->cid[0], mmc->cid[1],
+	       mmc->cid[2], mmc->cid[3]);
+	mmc_manufacturer = (mmc->cid[0] >> 24) & 0xff;
+	mmc_firmware_version = (mmc->cid[2] >> 16) & 0xff;
+	if ((mmc_manufacturer == 0x15) && (mmc_firmware_version == 0x12)) {
+		/* Unfortunately, we have units deployed with old
+		 * eMMC firmware (Samsung version 1.2) though there
+		 * may be one or more good versions.  Units with 
+		 * this eMMC firmware eventually stop erasing.  Instead 
+		 * of letting users keep using them until they fail and
+		 * then reporting an issue, force a stop if we detect 
+		 * this old firmware and force them to update right away
+		 * instead of getting a constant trickle of these failed
+		 * units coming in one at a time.
+		 */
+		printf("\tSamsung eMMC firmware %x.%x is bad, must update\n",
+		       mmc_firmware_version >> 4,
+		       mmc_firmware_version & 0xf);
+		force_fastboot = 1;
+	} else if ((mmc_manufacturer == 0x15)
+					&& (mmc_firmware_version == 0x25)) {
+		uint val1 = 0, val2 = 0;
+		/* Samsung e-MMC firmware has a bug that can be worked around
+		 * by changing the Write Buffer Block (LDB) Wear Level Policy
+		 * from page to block unit.  Do that with a Vendor Command
+		 * sequence.
+		 */
+		printf("\tWorkaround check on Samsung eMMC firmware %x.%x\n",
+		       mmc_firmware_version >> 4,
+		       mmc_firmware_version & 0xf);
+
+		/* Fetch and verify the prior values. */
+		err = samsung_mmc_cmd(mmc, 0x10210002) ||
+				samsung_mmc_mem(mmc, 0x04DD9C, 4) ||
+				samsung_mmc_read_word(mmc, &val1) ||
+				samsung_mmc_mem(mmc, 0x0379A4, 4) ||
+				samsung_mmc_read_word(mmc, &val2);
+		err = samsung_mmc_cmd(mmc, 0xDECCEE) || err;
+		if (!err && val1 == 0x000000FF && val2 == 0xD20228FF) {
+			printf("\tWorkaround already applied\n");
+			return 0;
+		}
+
+		if (err) {
+			printf("Reading eMMC RAM failed (%d)\n", err);
+			force_fastboot = 1; /* Fatal */
+			return 0;
+		}
+
+		if (val1 != 0x00000000 || val2 != 0xD2022820) {
+			printf("Assuming workaround not needed for "
+				"values (0x%08X 0x%08X)\n", val1, val2);
+			/* We didn't see what we expected.  Don't write to
+			 * the RAM, and consider it success assuming that
+			 * the version number got reused but the firmware
+			 * does not need the workaround.
+			 */
+			return 0;
+		}
+
+		printf("\tApplying block LDB Wear Level Policy workaround\n");
+		/* Write the new values. */
+		err = samsung_mmc_cmd(mmc, 0x10210000) ||
+				samsung_mmc_mem(mmc, 0x04DD9C, 0x000000FF) ||
+				samsung_mmc_mem(mmc, 0x0379A4, 0xD20228FF);
+		err = samsung_mmc_cmd(mmc, 0xDECCEE) || err;
+		if (err) {
+			printf("Writing workaround failed (%d)\n", err);
+			force_fastboot = 1; /* Fatal */
+			return 0;
+		}
+
+		/* Verify the new values. */
+		err = samsung_mmc_cmd(mmc, 0x10210002) ||
+				samsung_mmc_mem(mmc, 0x04DD9C, 4) ||
+				samsung_mmc_read_word(mmc, &val1) ||
+				samsung_mmc_mem(mmc, 0x0379A4, 4) ||
+				samsung_mmc_read_word(mmc, &val2);
+		err = samsung_mmc_cmd(mmc, 0xDECCEE) || err;
+		if (err || val1 != 0x000000FF || val2 != 0xD20228FF) {
+			printf("Verifying workaround failed "
+					"(%d 0x%08X 0x%08X)\n",
+					err, val1, val2);
+			force_fastboot = 1; /* Fatal */
+			return 0;
+		}
+	} else {
+		printf("\teMMC manufacturer 0x%02X version %x.%x\n",
+				mmc_manufacturer,
+				mmc_firmware_version >> 4,
+				mmc_firmware_version & 0xf);
+	}
+
+	return 0;
+}
+#endif
+
+static const struct avr_led_rgb_vals red = {
+	.rgb[0] = 128, .rgb[1] = 0, .rgb[2] = 0
+};
+static const struct avr_led_rgb_vals black = {
+	.rgb[0] = 0, .rgb[1] = 0, .rgb[2] = 0
+};
+
+int board_fbt_key_pressed(void)
+{
+	int is_pressed = 0;
+	u8 key_code;
+	unsigned long start_time = get_timer(0);
+
+#define DETECT_AVR_DELAY_MSEC 2000 /* 2 seconds */
+
+	/* If we power up with USB cable connected, the ROM bootloader
+	 * delays the OMAP boot long enough (as it checks for peripheral
+	 * boot) that the AVR will be ready at this point for us to
+	 * query.  If we power up with no USB cable, we will most likely
+	 * be here before the AVR is ready.  If we don't detect
+	 * the AVR right away, sleep a few seconds and try again.
+	 * We don't just poll until the AVR can respond because even
+	 * after we detect the AVR, it might not quite be ready to
+	 * do key detection so need to wait a bit more.
+	 */
+	avr_detected = !detect_avr();
+	if (!avr_detected) {
+		printf("\tavr not detected\n");
+		printf("\tdelaying %d milliseconds until we try again\n",
+			DETECT_AVR_DELAY_MSEC);
+		while((get_timer(0) - start_time) < DETECT_AVR_DELAY_MSEC)
+			; /* spin on purpose */
+		avr_detected = !detect_avr();
+	}
+	if (!avr_detected) {
+		/* always start fastboot if we're forcing it, even
+		 * if we can't show we're in fastboot mode with the LEDs
+		 */
+		if (force_fastboot) {
+			printf("Forcing fastboot even with no avr\n");
+			return 1;
+		}
+
+		/* This might happen if avr_updater got interrupted
+		 * while an avr firmware update was in progress.
+		 * It's better to allow regular booting instead of
+		 * stopping in fastboot mode because the OS might
+		 * be able to recovery it by doing the update again.
+		 * It's also not good to stop in fastboot because we
+		 * can't use the LEDs to indicate to the user we're
+		 * in this state.
+		 */
+		printf("%s: avr not detected, returning false\n", __func__);
+		return 0;
+	}
+
+	/* If we got here from a warm reset, AVR could be in some
+	 * other state than host mode so just make sure it is
+	 * in host mode.
+	 */
+	avr_led_set_mode(AVR_LED_MODE_HOST_AUTO_COMMIT);
+
+	if (force_fastboot) {
+		printf("Forcing fastboot\n");
+		return 1;
+	}
+
+	/* check for the mute key to be pressed as an indicator
+	 * to enter fastboot mode in preboot mode.  since the
+	 * AVR sends an initial boot indication key, we have to
+	 * filter that out first.  we also filter out and volume
+	 * up/down keys and don't care if we spin until those
+	 * stop (it's almost impossible to make the volume
+	 * up/down key events repeat indefinitely since they
+	 * involve actually rotating the top of the sphere
+	 * without pause).
+	 */
+	while (1) {
+		if (avr_get_key(&key_code))
+			break;
+		if (key_code == AVR_KEY_EVENT_EMPTY)
+			break;
+		if (key_code == (AVR_KEY_MUTE | AVR_KEY_EVENT_DOWN)) {
+			avr_led_set_all(&red);
+			avr_led_set_mute(&red);
+			is_pressed = 1;
+			key_pressed_start_time = get_timer(0);
+			/* don't wait for key release */
+			break;
+		}
+	}
+
+	/* All black to indicate we've made our decision to boot. */
+	if (!is_pressed) {
+		serial_printf("\tsetting to black\n");
+		avr_led_set_all(&black);
+		avr_led_set_mute(&black);
+	}
+
+	printf("Returning key pressed %s\n", is_pressed ? "true" : "false");
+	return is_pressed;
+}
+
+/* we only check for a long press of mute as an indicator to
+ * go into recovery.  due to i2c errors if we poll too fast,
+ * we only poll every 100ms right now.
+ */
+enum fbt_reboot_type board_fbt_key_command(void)
+{
+	unsigned long time_elapsed;
+
+	if (!avr_detected)
+		return FASTBOOT_REBOOT_NONE;
+
+	time_elapsed = get_timer(last_time);
+	if (time_elapsed > KEY_CHECK_POLLING_INTERVAL_MS) {
+		u8 key_code;
+
+		last_time = get_timer(0);
+
+		if (avr_get_key(&key_code))
+			return FASTBOOT_REBOOT_NONE;
+
+		if (key_code == (AVR_KEY_MUTE | AVR_KEY_EVENT_DOWN)) {
+			/* key down start */
+			key_pressed_start_time = last_time;
+			printf("%s: mute key down starting at time %lu\n",
+			       __func__, key_pressed_start_time);
+		} else if (key_code == AVR_KEY_MUTE) {
+			/* key down end, before hold time satisfied */
+			printf("%s: mute key released within %lu ms\n",
+			       __func__, last_time - key_pressed_start_time);
+			key_pressed_start_time = 0;
+			avr_led_set_all(&red);
+		} else if (key_pressed_start_time) {
+			unsigned long time_down;
+			time_down = last_time - key_pressed_start_time;
+
+			if (time_down > (RECOVERY_KEY_HOLD_TIME_SECS * 1000)) {
+				printf("%s: mute key down more than %u seconds,"
+				       " starting recovery\n",
+				       __func__, RECOVERY_KEY_HOLD_TIME_SECS);
+				return FASTBOOT_REBOOT_RECOVERY_WIPE_DATA;
+			}
+			printf("%s: mute key still down after %lu ms\n",
+			       __func__, time_down);
+			/* toggle led ring red and black while down
+			   to give user some feedback */
+			if ((time_down / KEY_CHECK_POLLING_INTERVAL_MS) & 1)
+				avr_led_set_all(&red);
+			else
+				avr_led_set_all(&black);
+		}
+	}
+	return FASTBOOT_REBOOT_NONE;
+}
+
+void board_fbt_start(void)
+{
+	/* in case we entered fastboot by request from ADB or other
+	 * means that we couldn't detect in board_fbt_key_command(),
+	 * make sure the LEDs are set to red to indicate fastboot mode
+	 */
+	if (avr_detected) {
+		avr_led_set_mute(&red);
+		avr_led_set_all(&red);
+	}
+}
+
+void board_fbt_end(void)
+{
+	if (avr_detected) {
+		/* to match spec, put avr into boot animation mode. */
+		avr_led_set_mode(AVR_LED_MODE_BOOT_ANIMATION);
+	}
+}
+
+struct fbt_partition {
+	const char *name;
+	const char *type;
+	unsigned size_kb;
+};
+
+/* For the 16GB eMMC part used in Tungsten, the erase group size is 512KB.
+ * So every partition should be at least 512KB to make it possible to use
+ * the mmc erase operation when doing 'fastboot erase'.
+ * However, the xloader is an exception because in order for the OMAP4 ROM
+ * bootloader to find it, it must be at offset 0KB, 128KB, 256KB, or 384KB.
+ * Since the partition table is at 0KB, we choose 128KB.  Special care
+ * must be kept to prevent erase the partition table when/if the xloader
+ * partition is being erased.
+ */
+struct fbt_partition fbt_partitions[] = {
+	{ "--ptable", NULL,  17},  /* partition table in
+					* first 34 sectors */
+	{ "environment", "raw", 95 },  /* partition used to u-boot environment,
+					* which is also where we store
+					* oem lock/unlock state.  size
+					* must match CONFIG_ENV_SIZE.
+					*/
+	{ "crypto", "raw", 16},        /* 16KB partition for crypto keys.
+					* used when userdata is encrypted.
+					*/
+	{ "xloader", "raw", 384 },	/* must start at 128KB offset into eMMC
+					 * for ROM bootloader to find it.
+					 * pad out to fill whole erase group */
+	{ "bootloader", "raw", 512 },  /* u-boot, one erase group in size */
+	{ "device_info", "raw", 512 }, /* device specific info like MAC
+					* addresses.  read-only once it has
+					* been written to.  bootloader parses
+					* this at boot and sends the contents
+					* to the kernel via cmdline args.
+					*/
+	{ "bootloader2", "raw", 512 }, /* u-boot, alternate copy */
+	{ "misc", "raw", 512 }, 	/* misc partition used by recovery for
+					 * storing parameters in the case of a
+					 * power failure during recovery
+					 * operation.
+					 */
+	{ "recovery", "boot", 8*1024 },
+	{ "boot", "boot", 8*1024 },
+	{ "efs", "ext4", 8*1024 },      /* for factory programmed keys,
+					 * minimum size for a ext4 fs is
+					 * about 8MB
+					 */
+	{ "system", "ext4", 1024*1024 },
+	{ "cache", "ext4", 512*1024 },
+	{ "userdata", "ext4", 0},
+	{ 0, 0, 0 },
+};
+
+void board_fbt_finalize_bootargs(char* args, size_t buf_sz) {
+	int used = strlen(args);
+	int i;
+	int bgap_threshold_t_hot  = 83000; /* 83 deg C */
+	int bgap_threshold_t_cold = 76000; /* 76 deg C */
+
+	for (i = 0; i < ARRAY_SIZE(mac_defaults); ++i) {
+		u8 m[6];
+		char mac[18];
+
+		if (strstr(args, mac_defaults[i].name))
+			continue;
+
+		generate_default_mac_addr(mac_defaults[i].salt, m);
+		snprintf(mac, sizeof(mac), "%02x:%02x:%02x:%02x:%02x:%02x",
+				m[5], m[4], m[3], m[2], m[1], m[0]);
+		mac[sizeof(mac) - 1] = 0;
+		used += snprintf(args + used,
+				buf_sz - used,
+				" %s=%s",
+				mac_defaults[i].name,
+				mac);
+	}
+
+	/* Add board_id */
+	used += snprintf(args + used,
+			 buf_sz - used,
+			 " board_steelhead.steelhead_hw_rev=%d",
+			 steelhead_hw_rev);
+
+	/* Add bandgap threshold temperature based on board_id.
+	 * Units before DVT2 didn't have a thermal rework so
+	 * we'll throttle at a lower temperature to try to
+	 * prevent damage to the OMAP.
+	 */
+	if (steelhead_hw_rev < STEELHEAD_REV_DVT2) {
+		bgap_threshold_t_hot  = 64000; /* 64 deg C */
+		bgap_threshold_t_cold = 61000; /* 61 deg C */
+	}
+	snprintf(args + used,
+		 buf_sz - used,
+		 " omap_temp_sensor.bgap_threshold_t_hot=%d"
+		 " omap_temp_sensor.bgap_threshold_t_cold=%d",
+		 bgap_threshold_t_hot, bgap_threshold_t_cold);
+
+	args[buf_sz-1] = 0;
+
+	/* this is called just before booting normal image.  we
+	 * use opportunity to start boot animation.
+	 */
+	board_fbt_end();
+}
+
+struct TOC_entry {
+	uint32_t start_offset;
+	uint32_t size;
+	uint8_t reserved[8];
+	uint32_t load_address;
+	char filename[12];
+};
+
+#define NUM_MPKH_REGISTERS 8
+struct MPKH_info {
+	char tag[4];
+	uint32_t mpkh[NUM_MPKH_REGISTERS];
+};
+
+int board_fbt_handle_flash(disk_partition_t *ptn,
+			   struct cmd_fastboot_interface *priv)
+{
+	struct TOC_entry *toc_p;
+	void *end_ptr;
+	void *image_ptr;
+	struct MPKH_info *mpkh_ptr;
+	struct MPKH_info my_mpkh;
+	struct ctrl_id *ctrl;
+	int i;
+
+	/* We support flashing a MLO "package" that has more than one
+	 * MLO image, each signed with a different MPK for a different
+	 * HS part.  We check if the MLO image has a MPKH_info structure
+	 * appended to it that has the tag, and the 8 MPKH values for
+	 * us to compare against our own MPKH register.  If no MPKH_info,
+	 * we'll just go ahead and accept it presuming the user knows
+	 * what they're doing or it's an old MLO file.  If the MPKH_info
+	 * structure exists, we'll compare the MPK info and if no
+	 * match, we'll try the next image in the downloaded file if
+	 * there is one.
+	 */
+	if ((get_device_type() != HS_DEVICE) ||
+	    (strcmp((char *)ptn->name, "xloader"))) {
+		/* not an HS part, or not flashing the xloader, just
+		 * go ahead and flash it.
+		 */
+		return 0;
+	}
+
+
+	/* read the MPKH registers into a local struct for easy comparison
+	 * with the MKPH_info we expect to find appended to the end
+	 * of the MLO image, if any.
+	 */
+	ctrl = (struct ctrl_id *)CTRL_BASE;
+	my_mpkh.tag[0] = 'M';
+	my_mpkh.tag[1] = 'P';
+	my_mpkh.tag[2] = 'K';
+	my_mpkh.tag[3] = 'H';
+	for (i = 0; i < NUM_MPKH_REGISTERS; i++) {
+		my_mpkh.mpkh[i] = ((uint32_t*)&ctrl->core_std_fuse_mpk_0)[i];
+	}
+
+	end_ptr = priv->transfer_buffer + priv->d_bytes;
+	image_ptr = priv->transfer_buffer;
+
+	printf("Verifying xloader image before flashing\n");
+	do {
+		printf("Checking image at offset 0x%x... ",
+		       image_ptr - (void*)priv->transfer_buffer);
+		toc_p = (struct TOC_entry *)image_ptr;
+		if ((void*)(toc_p + 1) >= end_ptr) {
+			printf("Image too small, not flashing\n");
+			snprintf(priv->response, sizeof(priv->response),
+				 "FAILImage too small\n");
+			return -1;
+		}
+		if (strcmp("MLO", toc_p->filename)) {
+			/* downloaded image does not have the MLO as the first
+			 * TOC entry like we expect, or is too small,
+			 * return error
+			 */
+			printf("Not an MLO image, not flashing\n");
+			snprintf(priv->response, sizeof(priv->response),
+				"FAILxloader requires MLO image");
+			return -1;
+		}
+		/* check for special case of a simple MLO image with
+		 * no MPKH_info appended to it.
+		 */
+		if (toc_p->start_offset + toc_p->size == priv->d_bytes) {
+			printf("    No MPKH_info in image, "
+			       "accepting unconditionally\n");
+			return 0;
+		}
+		mpkh_ptr = (struct MPKH_info *)(image_ptr +
+						toc_p->start_offset +
+						toc_p->size);
+		if ((void*)(mpkh_ptr + 1) <= end_ptr) {
+			if (memcmp(&my_mpkh, mpkh_ptr, sizeof(my_mpkh)) == 0) {
+				printf("    MPKH match, using this image\n");
+				priv->image_start_ptr = image_ptr;
+				priv->d_bytes = (toc_p->start_offset +
+						 toc_p->size);
+				return 0;
+			}
+			printf("    MPKH mismatch, not using this image\n");
+#ifdef DEBUG
+			for (i = 0; i < NUM_MPKH_REGISTERS; i++) {
+				printf("MPKH[%d]: 0x%08x %s 0x%08x\n",
+				       i, my_mpkh.mpkh[i],
+				       (my_mpkh.mpkh[i] == mpkh_ptr->mpkh[i]) ?
+				       "==" : "!=", mpkh_ptr->mpkh[i]);
+			}
+#endif
+		}
+		/* go to the next image, if there is one */
+		image_ptr += (toc_p->start_offset + toc_p->size +
+			      sizeof(*mpkh_ptr));
+	} while (image_ptr < end_ptr);
+	snprintf(priv->response, sizeof(priv->response),
+		 "FAILMLO verification failed");
+	return -1;
+}
+
+
+#ifdef CONFIG_MFG
+static void set_default_mac_env_vars(void)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(mac_defaults); ++i) {
+		u8 m[6];
+		char tmp_buf[18];
+		generate_default_mac_addr(mac_defaults[i].salt, m);
+		snprintf(tmp_buf, sizeof(tmp_buf),
+			"%02x:%02x:%02x:%02x:%02x:%02x",
+			m[5], m[4], m[3], m[2], m[1], m[0]);
+		tmp_buf[sizeof(tmp_buf) - 1] = 0;
+		setenv(mac_defaults[i].name, tmp_buf);
+	}
+}
+#endif
+
+int board_late_init(void)
+{
+	char tmp_buf[17];
+	u64 id_64;
+
+	dieid_num_r();
+
+	generate_default_64bit_id(serial_no_salt, &id_64);
+	snprintf(tmp_buf, sizeof(tmp_buf), "%016llx", id_64);
+	tmp_buf[sizeof(tmp_buf)-1] = 0;
+	setenv("fbt_id#", tmp_buf);
+
+#ifdef CONFIG_MFG
+	set_default_mac_env_vars();
+#endif
+
+	fbt_preboot();
+
+	return 0;
+}
+
+struct gpio_name_mapping {
+	const char* name;
+	int num;
+};
+
+int name_to_gpio(const char* name) {
+	static const struct gpio_name_mapping map[] = {
+		{ "aud_intfc_en",	40 },
+		{ "hdmi_ls_oe",		41 },
+		{ "aud_rstn",		42 },
+		{ "wlan_en",		43 },
+		{ "aud_pdn",		44 },
+		{ "bt_host_wake_bt",	45 },
+		{ "bt_en",		46 },
+		{ "bt_wakeup_host",	47 },
+		{ "ui_avr_rst_n_a",	48 },
+		{ "ui_avr_int_n",	49 },
+		{ "bt_rst_n",		52 },
+		{ "wlan_irq_n",		53 },
+		{ "hdmi_ct_cp_hpd",	60 },
+		{ "hdmi_hpd",		63 },
+		{ "hdmi_cec",		64 },
+		{ "spdif",		111 },
+		{ "nfc_dl_mode",	162 },
+		{ "nfc_en",		163 },
+		{ "nfc_irq",		164 },
+	};
+	int i;
+	const char* tmp;
+
+	for (i = 0; i < ARRAY_SIZE(map); ++i) {
+		if (!strcmp(name, map[i].name))
+			return map[i].num;
+	}
+
+	for (tmp = name; *tmp; ++tmp)
+		if ((*tmp < '0') || (*tmp > '9'))
+			return -1;
+
+	return simple_strtoul(name, NULL, 10);
+}
diff --git a/board/google/tungsten/tungsten_mux_data.h b/board/google/tungsten/tungsten_mux_data.h
new file mode 100644
index 0000000..7e80ae3
--- /dev/null
+++ b/board/google/tungsten/tungsten_mux_data.h
@@ -0,0 +1,95 @@
+/*
+ * (C) Copyright 2011
+ * Google, Inc.
+ * (C) Copyright 2010
+ * Texas Instruments Incorporated, <www.ti.com>
+ *
+ * Author :
+ *	Mike J Chen <mjchen@google.com>
+ *
+ *	Balaji Krishnamoorthy	<balajitk@ti.com>
+ *	Aneesh V		<aneesh@ti.com>
+ *
+ * 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
+ */
+
+#ifndef _TUNGSTEN_MUX_DATA_H_
+#define _TUNGSTEN_MUX_DATA_H_
+
+#include <asm/arch/mux_omap4.h>
+
+static const struct pad_conf_entry core_padconf_array_non_essential[] = {
+	/* Needed in both regular and MFG build, to communicate
+	 * with the AVR and proximity sensor.  For now, we're
+	 * going to let the kernel setup the mux conf for anything
+	 * it uses and do the minimum in the bootloader.
+	 */
+	{I2C2_SCL, (IEN | M0)},
+	{I2C2_SDA, (IEN | M0)},
+	{GPMC_A24, (M3)},			/* gpio_48  - UI_AVR_RST_N_A */
+	{FREF_CLK2_OUT, (IEN | PTD | M3)},	/* gpio_182 - board_id_0 */
+	{GPMC_NCS4,	(IEN | PTD | M3)},	/* gpio_101 - board_id_1 */
+	{KPD_COL3,	(IEN | PTD | M3)},	/* gpio_171 - board_id_2 */
+
+#ifdef CONFIG_MFG
+	/* during the first stage of manufacturing diagnostics, many pins are
+	 * configured as GPIOs for basic connectivity testing.  This section
+	 * makes sure pins are properly configured as either inputs or outputs
+	 * for diagnostics. */
+	{GPMC_A16,		(PTD | M3)},		/* gpio_40  - aud_intfc_en    */
+	{GPMC_A17,		(PTD | M3)},		/* gpio_41  - hdmi_ls_oe      */
+	{GPMC_A18,		(PTD | M3)},		/* gpio_42  - aud_rstn        */
+	{GPMC_A19,		(PTD | M3)},		/* gpio_43  - wlan_en         */
+	{GPMC_A20,		(PTD | M3)},		/* gpio_44  - aud_pdn         */
+	{GPMC_A21,		(PTD | M3)},		/* gpio_45  - bt_host_wake_bt */
+	{GPMC_A22,		(PTD | M3)},		/* gpio_46  - bt_en           */
+	{GPMC_A23,		(IEN | PTD | M3)},	/* gpio_47  - bt_wakeup_host  */
+	{GPMC_A24,		(M3)},			/* gpio_48  - ui_avr_rst_n_a  */
+	{GPMC_A25,		(IEN | PTU | M3)},	/* gpio_49  - ui_avr_int_n    */
+	{GPMC_NCS2,		(PTD | M3)},		/* gpio_52  - bt_rst_n        */
+	{GPMC_NCS3,		(IEN | PTU | M3)},	/* gpio_53  - wlan_irq_n      */
+	{GPMC_NBE1,		(PTD | M3)},		/* gpio_60  - hdmi_ct_cp_hpd  */
+	{HDMI_HPD,		(IEN | PTD | M3)},	/* gpio_63  - hdmi_hpd        */
+	{HDMI_CEC,		(IEN | PTU | M3)},	/* gpio_64  - hdmi_cec        */
+	{ABE_MCBSP2_DR,		(PTD | M3)},		/* gpio_111 - spdif           */
+	{USBB2_ULPITLL_DAT1,	(PTD | M3)},		/* gpio_162 - nfc_dl_mode     */
+	{USBB2_ULPITLL_DAT2,	(PTD | M3)},		/* gpio_163 - nfc_en          */
+	{USBB2_ULPITLL_DAT3,	(PTD | M3)},		/* gpio_164 - nfc_irq         */
+
+	/* during mfg diags, all I2C pins should be set up for I2C. No need to
+	 * set up I2C1 pins.  It's power on reset state is to be I2C and they
+	 * can have no other mode.
+	 */
+	{I2C3_SCL, (IEN | M0)},
+	{I2C3_SDA, (IEN | M0)},
+	{I2C4_SCL, (IEN | M0)},
+	{I2C4_SDA, (IEN | M0)},
+#endif
+};
+
+static const struct pad_conf_entry core_padconf_array_disable_board_id[] = {
+	{FREF_CLK2_OUT, (SAFE_MODE)},	/* gpio_182 - board_id_0 */
+	{GPMC_NCS4,	(SAFE_MODE)},	/* gpio_101 - board_id_1 */
+	{KPD_COL3,	(SAFE_MODE)},	/* gpio_171 - board_id_2 */
+};
+
+static const struct pad_conf_entry wkup_padconf_array_non_essential[] = {
+};
+
+#endif /* _TUNGSTEN_MUX_DATA_H_ */
diff --git a/boards.cfg b/boards.cfg
index 8a5bfc1..3d47843 100644
--- a/boards.cfg
+++ b/boards.cfg
@@ -178,6 +178,7 @@
 devkit8000                   arm         armv7       devkit8000          timll          omap3
 omap4_panda                  arm         armv7       panda               ti             omap4
 omap4_sdp4430                arm         armv7       sdp4430             ti             omap4
+omap4_tungsten               arm         armv7       tungsten            google         omap4
 s5p_goni                     arm         armv7       goni                samsung        s5pc1xx
 smdkc100                     arm         armv7       smdkc100            samsung        s5pc1xx
 origen			     arm	 armv7	     origen		 samsung	s5pc2xx
diff --git a/build_server.sh b/build_server.sh
new file mode 100755
index 0000000..9363826
--- /dev/null
+++ b/build_server.sh
@@ -0,0 +1,42 @@
+#!/bin/bash
+# This build script is used by the build server.
+# It's supposed to be executed in the directory where it lives.
+
+set -x -e
+
+if [ -z $BUILD_CONFIG ]; then
+  echo "Please set BUILD_CONFIG!"
+  exit 2
+fi
+
+if [ -z $BUILD_OUTPUT ]; then
+  echo "Please set output dir BUILD_OUTPUT!"
+  exit 2
+fi
+
+if [ -z $BUILD_JOBS ]; then
+  BUILD_JOBS=10
+fi
+
+FILES_TO_ARCHIVE="u-boot.bin"
+if [ "$BUILD_CONFIG" = "omap4_tungsten_config" ]; then
+FILES_TO_ARCHIVE="u-boot.img MLO spl/u-boot-spl.bin"
+fi
+
+export ARCH=arm
+export CROSS_COMPILE=arm-eabi-
+
+make clean
+make $BUILD_CONFIG
+make -j $BUILD_JOBS
+mkdir -p $BUILD_OUTPUT
+cp -f $FILES_TO_ARCHIVE $BUILD_OUTPUT
+
+if [ "$BUILD_CONFIG" = "omap4_tungsten_config" ]; then
+make clean
+make MFG=1 $BUILD_CONFIG
+make MFG=1 -j $BUILD_JOBS
+cp -f u-boot.img $BUILD_OUTPUT/factory_u-boot.img
+fi
+
+
diff --git a/drivers/i2c/omap24xx_i2c.c b/drivers/i2c/omap24xx_i2c.c
index f96563b..3469a63 100644
--- a/drivers/i2c/omap24xx_i2c.c
+++ b/drivers/i2c/omap24xx_i2c.c
@@ -443,7 +443,7 @@
 	return 0;
 }
 
-static void wait_for_bb(void)
+static void wait_for_bb (void)
 {
 	int timeout = I2C_TIMEOUT;
 	u16 stat;
diff --git a/include/configs/omap4_tungsten.h b/include/configs/omap4_tungsten.h
new file mode 100644
index 0000000..2c72139
--- /dev/null
+++ b/include/configs/omap4_tungsten.h
@@ -0,0 +1,358 @@
+/*
+ * (C) Copyright 2011
+ * Google, Inc.
+ *
+ * Based on omap4_panda.h:
+ * (C) Copyright 2010
+ * Texas Instruments Incorporated.
+ * Steve Sakoman  <steve@sakoman.com>
+ *
+ * Configuration settings for the Google OMAP4 Tungsten board.
+ * Based on Panda board settings.
+ *
+ * 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
+ */
+
+#ifndef __CONFIG_H
+#define __CONFIG_H
+
+/*
+ * High Level Configuration Options
+ */
+#define CONFIG_ARMV7		1	/* This is an ARM V7 CPU core */
+#define CONFIG_OMAP		1	/* in a TI OMAP core */
+#define CONFIG_OMAP44XX		1	/* which is a 44XX */
+#define CONFIG_OMAP4430		1	/* which is in a 4430 */
+#define CONFIG_ARCH_CPU_INIT
+
+#define CONFIG_TUNGSTEN		1	/* working with Tungsten */
+#define	CONFIG_FASTBOOT /* Android fast boot */
+
+/* Get CPU defs */
+#include <asm/arch/cpu.h>
+#include <asm/arch/omap4.h>
+#include <asm/sizes.h>
+
+/* Display CPU and Board Info */
+#define CONFIG_DISPLAY_CPUINFO		1
+#define CONFIG_DISPLAY_BOARDINFO	1
+
+/* Clock Defines */
+#define V_OSCK			38400000	/* Clock output from T2 */
+#define V_SCLK                   V_OSCK
+
+#undef CONFIG_USE_IRQ				/* no support for IRQs */
+
+#define CONFIG_OF_LIBFDT		1
+
+#define CONFIG_CMDLINE_TAG		1	/* enable passing of ATAGs */
+#define CONFIG_SETUP_MEMORY_TAGS	1
+#define CONFIG_INITRD_TAG		1
+
+/*
+ * Size of malloc() pool
+ * Total Size Environment - 95KB - must match partition table
+ * Malloc - add 128KB
+ */
+#define CONFIG_ENV_IS_IN_BLKDEV
+#define CONFIG_SYS_ENV_BLKDEV		"mmc0"
+#define CONFIG_ENV_SIZE_KB		(95)
+#define CONFIG_ENV_SIZE			((CONFIG_ENV_SIZE_KB) << 10)
+#define CONFIG_MALLOC_SIZE_KB		(128)
+#define CONFIG_SYS_MALLOC_LEN		(CONFIG_ENV_SIZE + ((CONFIG_MALLOC_SIZE_KB) << 10))
+/* Vector Base */
+#define CONFIG_SYS_CA9_VECTOR_BASE	SRAM_ROM_VECT_BASE
+
+/*
+ * Hardware drivers
+ */
+
+/*
+ * serial port - NS16550 compatible
+ */
+#define V_NS16550_CLK			48000000
+
+#define CONFIG_SYS_NS16550
+#define CONFIG_SYS_NS16550_SERIAL
+#define CONFIG_SYS_NS16550_REG_SIZE	(-4)
+#define CONFIG_SYS_NS16550_CLK		V_NS16550_CLK
+#define CONFIG_CONS_INDEX		3
+#define CONFIG_SYS_NS16550_COM3		UART3_BASE
+
+/*
+ * select serial console configuration
+ */
+#define CONFIG_SERIAL3                  3 /* UART3 on Tungsten */
+#define CONFIG_BAUDRATE			115200
+#define CONFIG_SYS_BAUDRATE_TABLE	{4800, 9600, 19200, 38400, 57600,\
+					115200}
+
+/* I2C  */
+#define CONFIG_HARD_I2C			1
+#define CONFIG_SYS_I2C_SPEED		400000
+#define CONFIG_SYS_I2C_SLAVE		1
+#define CONFIG_SYS_I2C_BUS		0
+#define CONFIG_SYS_I2C_BUS_SELECT	1
+#define CONFIG_DRIVER_OMAP34XX_I2C	1
+#define CONFIG_I2C_MULTI_BUS		1
+
+/* TWL6030 - initialize musb detection */
+#define CONFIG_TWL6030_POWER
+
+/* MMC */
+#define CONFIG_GENERIC_MMC		1
+#define CONFIG_MMC			1
+#define CONFIG_OMAP_HSMMC		1
+#define CONFIG_EFI_PARTITION		1
+#define CONFIG_MIN_PARTITION_NUM	1
+#define CONFIG_MMC_DEV			0
+#define CONFIG_OMAP_MMC_USE_DMA_WRITES
+
+/* USB */
+#define CONFIG_MUSB_UDC			1
+#define CONFIG_USB_OMAP3		1
+#define CONFIG_MUSB_RXFIFO_DOUBLE	1
+#define CONFIG_MUSB_DMA_MODE1		1
+
+/* Disable some non-essential dpll and clock setup because it
+ * increases power consumption and heat significantly.  We'll let
+ * the kernel initialize these.
+ */
+#define CONFIG_SKIP_NON_ESSENTIAL_CLOCKS 1
+
+/* Specify lower MPU DPLL freq to reduce power consumption and heat
+ * in the bootloader or else it's way unstable with TI's default settings.
+ */
+#define CONFIG_FORCE_TPS62361 /* always use TPS62361, don't do runtime
+				 check of omap4430 vs omap4460 */
+#define CONFIG_OMAP4460_MPU_DPLL mpu_dpll_params_700mhz
+#define CONFIG_OMAP4430_ES1_0_MPU_DPLL mpu_dpll_params_700mhz
+#define CONFIG_OMAP4430_non_ES1_0_MPU_DPLL mpu_dpll_params_700mhz
+
+/* Make sure that the ABE is clocked off the sysclk and not the 32KHz clock.
+ * Timers used for remote synchronization as well as the external fref fed to
+ * the TAS5713 can only source from sysclk, and must be phase locked with the
+ * McBSP output (which is fed from the ABE)
+ */
+#define CONFIG_SYS_OMAP4_ABE_SYSCK 1
+
+/* Fastboot settings
+ */
+/* Another macro may also be used or instead used to take care of the case
+ * where fastboot is started at boot (to be incorporated) based on key press
+ */
+#define	CONFIG_CMD_FASTBOOT
+#define	CONFIG_FASTBOOT_TRANSFER_BUFFER		(OMAP44XX_DRAM_ADDR_SPACE_START + SZ_16M)
+#define	CONFIG_FASTBOOT_TRANSFER_BUFFER_SIZE	(SZ_512M - SZ_16M)
+/* Fastboot product name */
+#define	FASTBOOT_PRODUCT_NAME	"steelhead"
+/* Fastboot reboot paramenter address, it's currently put at
+ * PUBLIC_SAR_RAM1_FREE
+ */
+#define FASTBOOT_REBOOT_PARAMETER_ADDR (0x4a326000 + 0xA0C)
+/* Address of the kernel's ramconsole so we can dump it.  This is
+ * used by the 'fastboot oem kmsg' command.  It needs to be done
+ * early in fastboot (before large amount of transfer buffer is used,
+ * since they overlap).
+ */
+#define CONFIG_FASTBOOT_RAMCONSOLE_START (OMAP44XX_DRAM_ADDR_SPACE_START + SZ_512M)
+
+/* device to use */
+#define FASTBOOT_BLKDEV                 "mmc0"
+/* Use HS */
+#define	USB_BCD_VERSION			0x0200
+
+#define CONFIG_USB_DEVICE		1
+/* Change these to suit your needs */
+#define CONFIG_USBD_VENDORID		0x18d1
+#define CONFIG_USBD_PRODUCTID		0x2c10
+#define CONFIG_USBD_MANUFACTURER	"Google"
+#define CONFIG_USBD_PRODUCT_NAME	"Tungsten"
+
+/* Flash */
+#define CONFIG_SYS_NO_FLASH	1
+
+/* commands to include */
+#include <config_cmd_default.h>
+
+/* Enabled commands */
+#define CONFIG_CMD_I2C		/* I2C serial bus support	*/
+#define CONFIG_CMD_MMC		/* MMC support                  */
+#define CONFIG_CMD_ENV		/* Environment support          */
+
+/* Disabled commands */
+#undef CONFIG_CMD_EXT2		/* EXT2 Support                 */
+#undef CONFIG_CMD_FAT		/* FAT support                  */
+#undef CONFIG_CMD_NET
+#undef CONFIG_CMD_NFS
+#undef CONFIG_CMD_FPGA		/* FPGA configuration Support   */
+#undef CONFIG_CMD_IMLS		/* List all found images        */
+#undef CONFIG_CMD_LOADB         /* don't need loading binary over serial */
+#undef CONFIG_CMD_LOADS         /* don't need loading s-record over serial */
+
+/*
+ * Environment setup
+ */
+#define CONFIG_BOOTDELAY	0
+/* use preboot to detect key press for fastboot */
+#define CONFIG_PREBOOT
+#define CONFIG_BOOTCOMMAND "booti"
+#ifdef CONFIG_MFG
+/*
+ * Manufacturing build:
+ * + Force the system into fastboot mode initially, and make sure that fastboot
+ *   will never load a kernel from MMC.  The test harness can then force the
+ *   system into the diagnostic console by either executing "fastboot continue"
+ *   from the host, or by sending a CTRL-C via the serial console.
+ *
+ *   Note: this was originally being done by manipulating CONFIG_BOOTCOMMAND and
+ *   CONFIG_PREBOOT, but that led to unforseen issues with the MFG bootcmd and
+ *   preboot settings getting saved in the environment.  Rather than attempting
+ *   to layer hack after hack on top of that approach trying to get things to
+ *   behave, we just introduce a new #def which gets obeyed by main.c
+ *
+ * + Turn on the gpio console commands.
+ */
+#define CONFIG_BOOTCOMMAND_FORCE_OVERRIDE "fastboot"
+#define CONFIG_CMD_GPIO
+#define CONFIG_MMC_SAMSUNG_SMART
+#define CONFIG_CMD_BLK
+#define CONFIG_SYS_LONGHELP 1
+#else
+#undef CONFIG_SYS_LONGHELP	/* undef to save memory */
+#endif
+
+/* overloaded in board/google/tungsten/tungsten.c */
+#ifndef __ASSEMBLY__
+int name_to_gpio(const char *name);
+#endif
+#define name_to_gpio(name) name_to_gpio(name)
+
+#define CONFIG_ENV_OVERWRITE
+
+#define CONFIG_EXTRA_ENV_SETTINGS \
+	"console=ttyS2,115200n8\0" \
+	"usbtty=cdc_acm\0" \
+	"fastboot_unlocked=1\0"
+
+#define CONFIG_AUTO_COMPLETE		1
+
+/*
+ * Miscellaneous configurable options
+ */
+
+#undef CONFIG_SYS_HUSH_PARSER	/* use "hush" command parser */
+#undef CONFIG_SYS_PROMPT_HUSH_PS2
+#define CONFIG_SYS_PROMPT		"Tungsten # "
+#define CONFIG_SYS_CBSIZE		256
+/* Print Buffer Size */
+#define CONFIG_SYS_PBSIZE		(CONFIG_SYS_CBSIZE + \
+					sizeof(CONFIG_SYS_PROMPT) + 16)
+#define CONFIG_SYS_MAXARGS		16
+/* Boot Argument Buffer Size */
+#define CONFIG_SYS_BARGSIZE		(CONFIG_SYS_CBSIZE)
+
+/*
+ * memtest setup
+ */
+#define CONFIG_SYS_MEMTEST_START	0x80000000
+#define CONFIG_SYS_MEMTEST_END		(CONFIG_SYS_MEMTEST_START + (32 << 20))
+
+/* Default load address */
+#define CONFIG_SYS_LOAD_ADDR		0x80000000
+
+/* Use General purpose timer 1 */
+#define CONFIG_SYS_TIMERBASE		GPT2_BASE
+#define CONFIG_SYS_PTV			2	/* Divisor: 2^(PTV+1) => 8 */
+#define CONFIG_SYS_HZ			1000
+
+/*
+ * Stack sizes
+ *
+ * The stack sizes are set up in start.S using the settings below
+ */
+#define CONFIG_STACKSIZE	(128 << 10)	/* Regular stack */
+#ifdef CONFIG_USE_IRQ
+#define CONFIG_STACKSIZE_IRQ	(4 << 10)	/* IRQ stack */
+#define CONFIG_STACKSIZE_FIQ	(4 << 10)	/* FIQ stack */
+#endif
+
+/* which initialization functions to call for this board */
+#define BOARD_LATE_INIT			1
+
+/*
+ * SDRAM Memory Map
+ * Even though we use two CS all the memory
+ * is mapped to one contiguous block
+ */
+#define CONFIG_NR_DRAM_BANKS	1
+
+#define CONFIG_SYS_SDRAM_BASE		0x80000000
+#define CONFIG_SYS_INIT_RAM_ADDR	0x4030D800
+#define CONFIG_SYS_INIT_RAM_SIZE	0x800
+#define CONFIG_SYS_INIT_SP_ADDR		(CONFIG_SYS_INIT_RAM_ADDR + \
+					 CONFIG_SYS_INIT_RAM_SIZE - \
+					 GENERATED_GBL_DATA_SIZE)
+
+#ifndef CONFIG_SYS_L2CACHE_OFF
+#define CONFIG_SYS_L2_PL310		1
+#define CONFIG_SYS_PL310_BASE	0x48242000
+#endif
+
+/* Defines for SDRAM init */
+#ifndef CONFIG_SYS_EMIF_PRECALCULATED_TIMING_REGS
+#define CONFIG_SYS_AUTOMATIC_SDRAM_DETECTION
+#define CONFIG_SYS_DEFAULT_LPDDR2_TIMINGS
+#endif
+
+/* Defines for SPL */
+#define CONFIG_SPL
+#define CONFIG_SPL_TEXT_BASE		0x40304350
+#define CONFIG_SPL_MAX_SIZE		(38 * 1024)
+#define CONFIG_SPL_STACK		LOW_LEVEL_SRAM_STACK
+
+#define CONFIG_SPL_BSS_START_ADDR	0x80000000
+#define CONFIG_SPL_BSS_MAX_SIZE		0x80000		/* 512 KB */
+
+#define CONFIG_SPL_VERIFY_IMAGE
+
+/* These values need to match the partition table in tungsten.c */
+/* emmc offset: 0x80000 */
+#define CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR	                0x400
+/* emmc offset: 0x180000 */
+#define CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_ALTERNATE_SECTOR	0xc00
+#define CONFIG_SYS_U_BOOT_MAX_SIZE_SECTORS	0x400 /* 512 KB */
+
+#define CONFIG_SPL_LIBCOMMON_SUPPORT
+#define CONFIG_SPL_LIBDISK_SUPPORT
+#define CONFIG_SPL_I2C_SUPPORT
+#define CONFIG_SPL_MMC_SUPPORT
+#define CONFIG_SPL_LIBGENERIC_SUPPORT
+#define CONFIG_SPL_SERIAL_SUPPORT
+#define CONFIG_SPL_LDSCRIPT "arch/arm/cpu/armv7/omap-common/u-boot-spl.lds"
+
+/*
+ * 1MB into the SDRAM to allow for SPL's bss at the beginning of SDRAM
+ * 64 bytes before this address should be set aside for u-boot.img's
+ * header. That is 0x800FFFC0--0x80100000 should not be used for any
+ * other needs.
+ */
+#define CONFIG_SYS_TEXT_BASE		0x80100000
+
+#endif /* __CONFIG_H */