Merge branch 'mindspeed_drops' into HEAD

Conflicts:
	arch/arm/mach-comcerto/comcerto-2000.c
	drivers/mtd/nand/comcerto_nand.c

Enable CONFIG_SUSPEND because the new Mindspeed kernel would not compile
without it.

Change-Id: I336163cf5b3de56e4d38a135dd05d0e75c9ad2b2
diff --git a/arch/arm/configs/gfrg200_defconfig b/arch/arm/configs/gfrg200_defconfig
new file mode 100644
index 0000000..3ac89f6
--- /dev/null
+++ b/arch/arm/configs/gfrg200_defconfig
@@ -0,0 +1,313 @@
+CONFIG_EXPERIMENTAL=y
+# CONFIG_LOCALVERSION_AUTO is not set
+# CONFIG_SWAP is not set
+CONFIG_SYSVIPC=y
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_BSD_PROCESS_ACCT_V3=y
+CONFIG_LOG_BUF_SHIFT=14
+CONFIG_CGROUPS=y
+CONFIG_CGROUP_DEVICE=y
+CONFIG_CPUSETS=y
+CONFIG_CGROUP_CPUACCT=y
+# CONFIG_NET_NS is not set
+CONFIG_SYSFS_DEPRECATED=y
+CONFIG_SYSFS_DEPRECATED_V2=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_PRINTK_PERSIST=y
+CONFIG_BOOTLOG_COPY=y
+CONFIG_PERF_EVENTS=y
+# CONFIG_COMPAT_BRK is not set
+CONFIG_SLAB=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_IOSCHED_DEADLINE is not set
+# CONFIG_IOSCHED_CFQ is not set
+CONFIG_ARCH_COMCERTO=y
+CONFIG_GOOGLE_FIBER_OPTIMUS=y
+CONFIG_COMCERTO_MEMBUF=y
+CONFIG_COMCERTO_EPAVIS=y
+# CONFIG_COMCERTO_MSP is not set
+CONFIG_COMCERTO_SATA=y
+CONFIG_COMCERTO_ZONE_DMA_NCNB=y
+CONFIG_COMCERTO_CUSTOM_SKB_LAYOUT=y
+CONFIG_DSPG_DECT_CSS=y
+# CONFIG_SWP_EMULATE is not set
+CONFIG_PCI=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_SMP=y
+# CONFIG_SMP_ON_UP is not set
+CONFIG_VMSPLIT_2G=y
+CONFIG_NR_CPUS=2
+CONFIG_AEABI=y
+# CONFIG_OABI_COMPAT is not set
+CONFIG_ZBOOT_ROM_TEXT=0x3008000
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
+CONFIG_CPU_FREQ_GOV_POWERSAVE=y
+CONFIG_CPU_FREQ_GOV_ONDEMAND=y
+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
+CONFIG_VFP=y
+CONFIG_NEON=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_IP_ROUTE_VERBOSE=y
+CONFIG_IP_MROUTE=y
+CONFIG_SYN_COOKIES=y
+# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
+# CONFIG_INET_XFRM_MODE_TUNNEL is not set
+# CONFIG_INET_XFRM_MODE_BEET is not set
+# CONFIG_INET_LRO is not set
+# CONFIG_INET_DIAG is not set
+CONFIG_IPV6=y
+CONFIG_IPV6_PRIVACY=y
+# CONFIG_INET6_XFRM_MODE_TRANSPORT is not set
+# CONFIG_INET6_XFRM_MODE_TUNNEL is not set
+# CONFIG_INET6_XFRM_MODE_BEET is not set
+# CONFIG_IPV6_SIT is not set
+CONFIG_NETFILTER=y
+CONFIG_NF_CONNTRACK=m
+CONFIG_NF_CONNTRACK_EVENTS=y
+CONFIG_NF_CONNTRACK_FTP=m
+CONFIG_NF_CONNTRACK_H323=m
+CONFIG_NF_CONNTRACK_IRC=m
+CONFIG_NF_CONNTRACK_PPTP=m
+CONFIG_NF_CONNTRACK_SIP=m
+CONFIG_NF_CONNTRACK_TFTP=m
+CONFIG_NF_CT_NETLINK=m
+CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m
+CONFIG_NETFILTER_XT_TARGET_CONNMARK=m
+CONFIG_NETFILTER_XT_TARGET_DSCP=m
+CONFIG_NETFILTER_XT_TARGET_HL=m
+CONFIG_NETFILTER_XT_TARGET_MARK=m
+CONFIG_NETFILTER_XT_TARGET_NOTRACK=m
+CONFIG_NETFILTER_XT_TARGET_TRACE=m
+CONFIG_NETFILTER_XT_TARGET_TCPMSS=m
+CONFIG_NETFILTER_XT_MATCH_COMMENT=m
+CONFIG_NETFILTER_XT_MATCH_CONNBYTES=m
+CONFIG_NETFILTER_XT_MATCH_CONNMARK=m
+CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m
+CONFIG_NETFILTER_XT_MATCH_DSCP=m
+CONFIG_NETFILTER_XT_MATCH_HELPER=m
+CONFIG_NETFILTER_XT_MATCH_HL=m
+CONFIG_NETFILTER_XT_MATCH_IPRANGE=m
+CONFIG_NETFILTER_XT_MATCH_LAYER7=m
+CONFIG_NETFILTER_XT_MATCH_LENGTH=m
+CONFIG_NETFILTER_XT_MATCH_LIMIT=m
+CONFIG_NETFILTER_XT_MATCH_MAC=m
+CONFIG_NETFILTER_XT_MATCH_MARK=m
+CONFIG_NETFILTER_XT_MATCH_MULTIPORT=m
+CONFIG_NETFILTER_XT_MATCH_OWNER=m
+CONFIG_NETFILTER_XT_MATCH_PHYSDEV=m
+CONFIG_NETFILTER_XT_MATCH_PKTTYPE=m
+CONFIG_NETFILTER_XT_MATCH_QUOTA=m
+CONFIG_NETFILTER_XT_MATCH_RECENT=m
+CONFIG_NETFILTER_XT_MATCH_STATE=m
+CONFIG_NETFILTER_XT_MATCH_STATISTIC=m
+CONFIG_NETFILTER_XT_MATCH_STRING=m
+CONFIG_NETFILTER_XT_MATCH_TCPMSS=m
+CONFIG_NETFILTER_XT_MATCH_TIME=m
+CONFIG_NF_CONNTRACK_IPV4=m
+# CONFIG_NF_CONNTRACK_PROC_COMPAT is not set
+CONFIG_IP_NF_IPTABLES=m
+CONFIG_IP_NF_MATCH_ECN=m
+CONFIG_IP_NF_FILTER=m
+CONFIG_IP_NF_TARGET_REJECT=m
+CONFIG_IP_NF_TARGET_LOG=m
+CONFIG_NF_NAT=m
+CONFIG_IP_NF_TARGET_MASQUERADE=m
+CONFIG_IP_NF_TARGET_NETMAP=m
+CONFIG_IP_NF_TARGET_REDIRECT=m
+CONFIG_IP_NF_MANGLE=m
+CONFIG_IP_NF_TARGET_ECN=m
+CONFIG_IP_NF_RAW=m
+CONFIG_NF_CONNTRACK_IPV6=m
+CONFIG_IP6_NF_QUEUE=m
+CONFIG_IP6_NF_IPTABLES=m
+CONFIG_IP6_NF_MATCH_AH=m
+CONFIG_IP6_NF_MATCH_EUI64=m
+CONFIG_IP6_NF_MATCH_FRAG=m
+CONFIG_IP6_NF_MATCH_OPTS=m
+CONFIG_IP6_NF_MATCH_IPV6HEADER=m
+CONFIG_IP6_NF_MATCH_MH=m
+CONFIG_IP6_NF_MATCH_RT=m
+CONFIG_IP6_NF_TARGET_LOG=m
+CONFIG_IP6_NF_FILTER=m
+CONFIG_IP6_NF_TARGET_REJECT=m
+CONFIG_IP6_NF_MANGLE=m
+CONFIG_IP6_NF_RAW=m
+CONFIG_BRIDGE_NF_EBTABLES=m
+CONFIG_BRIDGE_EBT_BROUTE=m
+CONFIG_BRIDGE_EBT_T_FILTER=m
+CONFIG_BRIDGE_EBT_T_NAT=m
+CONFIG_BRIDGE_EBT_802_3=m
+CONFIG_BRIDGE_EBT_AMONG=m
+CONFIG_BRIDGE_EBT_LIMIT=m
+CONFIG_BRIDGE_EBT_MARK=m
+CONFIG_BRIDGE_EBT_PKTTYPE=m
+CONFIG_BRIDGE_EBT_STP=m
+CONFIG_BRIDGE_EBT_VLAN=m
+CONFIG_BRIDGE_EBT_MARK_T=m
+CONFIG_BRIDGE_EBT_REDIRECT=m
+CONFIG_BRIDGE=y
+CONFIG_VLAN_8021Q=y
+CONFIG_NET_SCHED=y
+CONFIG_WIRELESS_EXT=y
+CONFIG_WEXT_SPY=y
+CONFIG_WEXT_PRIV=y
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
+CONFIG_MTD=y
+CONFIG_MTD_CMDLINE_PARTS=y
+CONFIG_MTD_CHAR=y
+CONFIG_MTD_BLOCK=y
+CONFIG_MTD_CFI=y
+CONFIG_MTD_CFI_INTELEXT=y
+CONFIG_MTD_CFI_AMDSTD=y
+CONFIG_MTD_RAM=y
+CONFIG_MTD_ROM=y
+CONFIG_MTD_PHYSMAP=y
+CONFIG_MTD_NAND=y
+CONFIG_MTD_NAND_ECC_BCH=y
+CONFIG_MTD_NAND_COMCERTO=y
+CONFIG_MTD_UBI=y
+CONFIG_MTD_UBI_WL_THRESHOLD=128
+CONFIG_MTD_UBI_BEB_RESERVE=25
+CONFIG_MTD_UBI_GLUEBI=y
+CONFIG_MISC_DEVICES=y
+CONFIG_EEPROM_AT24=y
+CONFIG_SCSI_MULTI_LUN=y
+# CONFIG_SATA_PMP is not set
+CONFIG_SATA_AHCI=y
+# CONFIG_ATA_SFF is not set
+CONFIG_NETDEVICES=y
+CONFIG_MII=y
+CONFIG_TUN=y
+# CONFIG_NET_VENDOR_3COM is not set
+# CONFIG_NET_VENDOR_ADAPTEC is not set
+# CONFIG_NET_VENDOR_ALTEON is not set
+# CONFIG_NET_VENDOR_AMD is not set
+# CONFIG_NET_VENDOR_ATHEROS is not set
+# CONFIG_NET_VENDOR_BROADCOM is not set
+# CONFIG_NET_VENDOR_BROCADE is not set
+# CONFIG_NET_VENDOR_CHELSIO is not set
+# CONFIG_NET_VENDOR_CISCO is not set
+# CONFIG_NET_VENDOR_DEC is not set
+# CONFIG_NET_VENDOR_DLINK is not set
+# CONFIG_NET_VENDOR_EMULEX is not set
+# CONFIG_NET_VENDOR_EXAR is not set
+# CONFIG_NET_VENDOR_FARADAY is not set
+# CONFIG_NET_VENDOR_HP is not set
+# CONFIG_NET_VENDOR_INTEL is not set
+# CONFIG_NET_VENDOR_MARVELL is not set
+# CONFIG_NET_VENDOR_MELLANOX is not set
+# CONFIG_NET_VENDOR_MICREL is not set
+# CONFIG_NET_VENDOR_MICROCHIP is not set
+# CONFIG_NET_VENDOR_MYRI is not set
+# CONFIG_NET_VENDOR_NATSEMI is not set
+# CONFIG_NET_VENDOR_NVIDIA is not set
+# CONFIG_NET_VENDOR_OKI is not set
+# CONFIG_NET_PACKET_ENGINE is not set
+# CONFIG_NET_VENDOR_QLOGIC is not set
+# CONFIG_NET_VENDOR_REALTEK is not set
+# CONFIG_NET_VENDOR_RDC is not set
+# CONFIG_NET_VENDOR_SEEQ is not set
+# CONFIG_NET_VENDOR_SILAN is not set
+# CONFIG_NET_VENDOR_SIS is not set
+# CONFIG_NET_VENDOR_SMSC is not set
+# CONFIG_NET_VENDOR_STMICRO is not set
+# CONFIG_NET_VENDOR_SUN is not set
+# CONFIG_NET_VENDOR_TEHUTI is not set
+# CONFIG_NET_VENDOR_TI is not set
+# CONFIG_NET_VENDOR_VIA is not set
+CONFIG_PHYLIB=y
+CONFIG_ATHEROS_PHY=y
+CONFIG_FIXED_PHY=y
+# CONFIG_WLAN is not set
+# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
+# CONFIG_INPUT_KEYBOARD is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_LEGACY_PTYS is not set
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_8250_NR_UARTS=32
+CONFIG_SERIAL_8250_EXTENDED=y
+CONFIG_SERIAL_8250_MANY_PORTS=y
+CONFIG_SERIAL_8250_SHARE_IRQ=y
+CONFIG_SERIAL_8250_RSA=y
+# CONFIG_HW_RANDOM is not set
+CONFIG_I2C=y
+# CONFIG_I2C_COMPAT is not set
+CONFIG_I2C_CHARDEV=y
+# CONFIG_I2C_HELPER_AUTO is not set
+CONFIG_I2C_SMBUS=y
+CONFIG_I2C_COMCERTO=y
+CONFIG_SPI=y
+CONFIG_SPI_DEBUG=y
+CONFIG_HWMON_DEBUG_CHIP=y
+CONFIG_SENSORS_LM63=y
+CONFIG_WATCHDOG=y
+CONFIG_WATCHDOG_CORE=y
+CONFIG_WATCHDOG_NOWAYOUT=y
+CONFIG_COMCERTO_WATCHDOG=y
+# CONFIG_HID_SUPPORT is not set
+CONFIG_USB=y
+CONFIG_USB_DEBUG=y
+CONFIG_USB_DEVICEFS=y
+CONFIG_USB_XHCI_HCD=y
+CONFIG_USB_STORAGE=y
+CONFIG_RTC_CLASS=y
+# CONFIG_RTC_HCTOSYS is not set
+# CONFIG_RTC_INTF_SYSFS is not set
+# CONFIG_RTC_INTF_PROC is not set
+# CONFIG_RTC_INTF_DEV is not set
+CONFIG_EXT4_FS=y
+# CONFIG_DNOTIFY is not set
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_TMPFS=y
+CONFIG_TMPFS_XATTR=y
+CONFIG_UBIFS_FS=y
+CONFIG_UBIFS_FS_DEBUG=y
+CONFIG_SQUASHFS=y
+CONFIG_SQUASHFS_EMBEDDED=y
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3=y
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_NLS_UTF8=y
+CONFIG_PRINTK_TIME=y
+CONFIG_FRAME_WARN=2048
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_UNUSED_SYMBOLS=y
+CONFIG_DEBUG_KERNEL=y
+CONFIG_LOCKUP_DETECTOR=y
+CONFIG_BOOTPARAM_HARDLOCKUP_PANIC=y
+CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC=y
+CONFIG_BOOTPARAM_HUNG_TASK_PANIC=y
+# CONFIG_SCHED_DEBUG is not set
+CONFIG_SYSCTL_SYSCALL_CHECK=y
+CONFIG_BLK_DEV_IO_TRACE=y
+# CONFIG_ARM_UNWIND is not set
+CONFIG_CRYPTO_NULL=y
+CONFIG_CRYPTO_AUTHENC=y
+CONFIG_CRYPTO_CCM=m
+CONFIG_CRYPTO_CBC=y
+CONFIG_CRYPTO_HMAC=y
+CONFIG_CRYPTO_MD5=y
+CONFIG_CRYPTO_SHA1=y
+CONFIG_CRYPTO_SHA256=y
+CONFIG_CRYPTO_AES=y
+CONFIG_CRYPTO_ARC4=m
+CONFIG_CRYPTO_DES=y
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
+CONFIG_CRC_CCITT=m
+CONFIG_AVERAGE=y
diff --git a/arch/arm/mach-comcerto/Kconfig b/arch/arm/mach-comcerto/Kconfig
index 3735479..f94f77c 100644
--- a/arch/arm/mach-comcerto/Kconfig
+++ b/arch/arm/mach-comcerto/Kconfig
@@ -46,6 +46,12 @@
 	prompt "Comcerto Board type"
 	default RTSM_C2K
 
+config GOOGLE_FIBER_OPTIMUS
+	bool "Google Fiber Optimus"
+	depends on ARCH_M86XXX
+	help
+	  Say Y here if you intend to run this kernel with a Google Fiber Optimus board.
+
 config C2K_EVM
 	bool "EVM"
 	depends on ARCH_M86XXX
diff --git a/arch/arm/mach-comcerto/Makefile b/arch/arm/mach-comcerto/Makefile
index 9f5cfa0..9cc213a 100644
--- a/arch/arm/mach-comcerto/Makefile
+++ b/arch/arm/mach-comcerto/Makefile
@@ -21,6 +21,7 @@
 obj-$(CONFIG_COMCERTO_MEMBUF)                   += membuf.o
 obj-$(CONFIG_COMCERTO_TDM_CLOCK)                += sysfstdm.o
 obj-$(CONFIG_RTSM_C2K) 				+= board-c2krtsm.o
+obj-$(CONFIG_GOOGLE_FIBER_OPTIMUS)		+= board-optimus.o
 obj-$(CONFIG_C2K_EVM) 				+= board-c2kevm.o
 obj-$(CONFIG_C2K_MFCN_EVM)			+= board-c2kmfcnevm.o
 obj-$(CONFIG_C2K_ASIC) 				+= board-c2kasic.o
@@ -28,3 +29,4 @@
 obj-$(CONFIG_COMCERTO_MSP)			+= msp/
 obj-$(CONFIG_HOTPLUG_CPU)			+= hotplug.o
 obj-$(CONFIG_COMCERTO_CSYS_TPI_CLOCK)		+= comcerto-tpicsys.o
+obj-$(CONFIG_BOOTLOG_COPY)			+= bootlog.o
diff --git a/arch/arm/mach-comcerto/board-c2kevm.c b/arch/arm/mach-comcerto/board-c2kevm.c
index d944636..a99dab7 100644
--- a/arch/arm/mach-comcerto/board-c2kevm.c
+++ b/arch/arm/mach-comcerto/board-c2kevm.c
@@ -587,7 +587,7 @@
 
 static struct comcerto_pfe_platform_data comcerto_pfe_pdata = {
 	.comcerto_eth_pdata[0] = {
-		.name = GEM0_ITF_NAME,
+		.name = "wan0",
 		.device_flags = CONFIG_COMCERTO_GEMAC,
 		.mii_config = CONFIG_COMCERTO_USE_RGMII,
 		.gemac_mode = GEMAC_SW_CONF | GEMAC_SW_FULL_DUPLEX | GEMAC_SW_SPEED_1G,
@@ -599,7 +599,7 @@
 	},
 
 	.comcerto_eth_pdata[1] = {
-		.name = GEM1_ITF_NAME,
+		.name = "lan0",
 		.device_flags = CONFIG_COMCERTO_GEMAC,
 		.mii_config = CONFIG_COMCERTO_USE_RGMII,
 		.gemac_mode = GEMAC_SW_CONF | GEMAC_SW_FULL_DUPLEX | GEMAC_SW_SPEED_1G,
@@ -609,7 +609,7 @@
 	},
 
 	.comcerto_eth_pdata[2] = {
-		.name = GEM2_ITF_NAME,
+		.name = "moca0",
 		.device_flags = CONFIG_COMCERTO_GEMAC,
 		.mii_config = CONFIG_COMCERTO_USE_RGMII,
 		.gemac_mode = GEMAC_SW_CONF | GEMAC_SW_FULL_DUPLEX | GEMAC_SW_SPEED_1G,
diff --git a/arch/arm/mach-comcerto/board-optimus.c b/arch/arm/mach-comcerto/board-optimus.c
new file mode 100644
index 0000000..682e9b0
--- /dev/null
+++ b/arch/arm/mach-comcerto/board-optimus.c
@@ -0,0 +1,693 @@
+/*
+ * arch/arm/mach-comcerto/board-optimus.c
+ *
+ *  Copyright (C) 2012 Mindspeed Technologies, Inc.
+ *
+ * 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 <linux/sched.h>
+#include <linux/device.h>
+#include <linux/serial_8250.h>
+#include <linux/memblock.h>
+#include <linux/phy.h>
+
+#include <linux/mtd/mtd.h>
+#if defined(CONFIG_MTD_NAND_COMCERTO) || defined(CONFIG_MTD_NAND_COMCERTO_MODULE)
+#include <linux/mtd/nand.h>
+#endif
+#include <linux/mtd/partitions.h>
+
+#if defined(CONFIG_SPI_MSPD_LOW_SPEED) || defined(CONFIG_SPI_MSPD_HIGH_SPEED)
+#include <linux/spi/spi.h>
+#endif
+
+#if defined(CONFIG_COMCERTO_I2C_SUPPORT)
+#include <linux/i2c.h>
+#endif
+
+#include <asm/sizes.h>
+#include <asm/setup.h>
+#include <asm/mach-types.h>
+#include <asm/io.h>
+
+#include <asm/mach/flash.h>
+#include <asm/mach/arch.h>
+
+#include <mach/hardware.h>
+#include <mach/irqs.h>
+#include <mach/dma.h>
+#include <linux/dw_dmac.h>
+
+#include <linux/clockchips.h>
+#include <linux/init.h>
+#include <linux/smp.h>
+#include <asm/smp_twd.h>
+#include <asm/localtimer.h>
+#include <asm/hardware/gic.h>
+#include <asm/mach/time.h>
+#include <mach/gpio.h>
+
+
+extern void platform_reserve(void);
+extern void device_map_io (void);
+extern void device_irq_init(void);
+extern void device_init(void);
+extern void mac_addr_init(struct comcerto_pfe_platform_data *);
+extern struct sys_timer comcerto_timer;
+
+static void __init board_gpio_init(void)
+{
+#ifdef CONFIG_COMCERTO_PFE_UART_SUPPORT
+	writel((readl(COMCERTO_GPIO_PIN_SELECT_REG) & ~PFE_UART_GPIO) | PFE_UART_BUS, COMCERTO_GPIO_PIN_SELECT_REG);
+	c2k_gpio_pin_stat.c2k_gpio_pins_0_31 |= PFE_UART_GPIO_PIN; /* GPIOs 12 & 13 are used for PFE_UART */
+#endif
+
+#if defined(CONFIG_SPI_MSPD_LOW_SPEED) || defined(CONFIG_SPI2_MSPD_LOW_SPEED)
+	/* enable SPI pins */
+	writel((readl(COMCERTO_GPIO_PIN_SELECT_REG1) & ~(SPI_MUX_GPIO_1)) | (SPI_MUX_BUS_1), COMCERTO_GPIO_PIN_SELECT_REG1);
+	writel((readl(COMCERTO_GPIO_63_32_PIN_SELECT) & ~(SPI_MUX_GPIO_2)) | (SPI_MUX_BUS_2), COMCERTO_GPIO_63_32_PIN_SELECT);
+	c2k_gpio_pin_stat.c2k_gpio_pins_0_31 |= SPI_MUX_GPIO_1_PIN; /* GPIOs 18,19, 21,22, 30,31 are used for SPI*/
+	c2k_gpio_pin_stat.c2k_gpio_pins_32_63 |= SPI_MUX_GPIO_2_PIN; /* GPIO 32 is used for SPI*/
+#endif
+
+#if defined(CONFIG_SPI_MSPD_HIGH_SPEED)
+	/* enable SPI pins */
+	writel((readl(COMCERTO_GPIO_PIN_SELECT_REG1) & ~(SPI_2_MUX_GPIO_1)) | (SPI_2_MUX_BUS_1), COMCERTO_GPIO_PIN_SELECT_REG1);
+	writel((readl(COMCERTO_GPIO_63_32_PIN_SELECT) & ~(SPI_2_MUX_GPIO_2)) | (SPI_2_MUX_BUS_2), COMCERTO_GPIO_63_32_PIN_SELECT);
+	c2k_gpio_pin_stat.c2k_gpio_pins_0_31 |= SPI_2_MUX_GPIO_1_PIN;
+	c2k_gpio_pin_stat.c2k_gpio_pins_32_63 |= SPI_2_MUX_GPIO_2_PIN;
+#endif
+
+#if defined(CONFIG_COMCERTO_I2C_SUPPORT)
+	writel((readl(COMCERTO_GPIO_PIN_SELECT_REG1) & ~I2C_GPIO) | I2C_BUS, COMCERTO_GPIO_PIN_SELECT_REG1);
+	c2k_gpio_pin_stat.c2k_gpio_pins_0_31 |= I2C_GPIO_PIN;
+#endif
+
+#if defined(CONFIG_MTD_NAND_COMCERTO) || defined(CONFIG_MTD_NAND_COMCERTO_MODULE)
+	writel((readl(COMCERTO_GPIO_PIN_SELECT_REG1) & ~NAND_GPIO) | NAND_BUS, COMCERTO_GPIO_PIN_SELECT_REG1);
+	c2k_gpio_pin_stat.c2k_gpio_pins_0_31 |= NAND_GPIO_PIN;
+#endif
+
+#if defined(CONFIG_MTD_COMCERTO_NOR)
+	writel((readl(COMCERTO_GPIO_PIN_SELECT_REG1) & ~NOR_GPIO) | NOR_BUS, COMCERTO_GPIO_PIN_SELECT_REG1);
+	c2k_gpio_pin_stat.c2k_gpio_pins_0_31 |= NOR_GPIO_PIN;
+#endif
+
+#ifdef MOCA_RESET_GPIO_PIN
+	__raw_writel(__raw_readl(COMCERTO_GPIO_OUTPUT_REG) | MOCA_RESET_GPIO_PIN, COMCERTO_GPIO_OUTPUT_REG);
+#endif
+
+	// enable GPIO0 interrupt (for MoCA) as level triggered, active high.
+	__raw_writel(__raw_readl(COMCERTO_GPIO_INT_CFG_REG) | (0x3),
+			COMCERTO_GPIO_INT_CFG_REG);
+}
+
+/* --------------------------------------------------------------------
+ *  NOR device
+ * -------------------------------------------------------------------- */
+#if defined(CONFIG_MTD_COMCERTO_NOR)
+
+static struct resource comcerto_nor_resources[] = {
+	{
+		.start	= NORFLASH_MEMORY_PHY1,
+		.end	= NORFLASH_MEMORY_PHY1 + SZ_64M - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+};
+
+static struct flash_platform_data comcerto_nor_data = {
+	.map_name	= "cfi_probe",
+	.width	= 2,
+};
+
+static struct platform_device comcerto_nor = {
+	.name           = "comcertoflash",
+	.id             = 0,
+	.num_resources  = ARRAY_SIZE(comcerto_nor_resources),
+	.resource       = comcerto_nor_resources,
+	.dev = {
+		.platform_data	= &comcerto_nor_data,
+	},
+};
+#endif
+
+static struct resource rtc_res[] = {
+       {
+               .start = COMCERTO_APB_RTC_BASE,
+               .end = COMCERTO_APB_RTC_BASE + SZ_32 - 1,
+               .flags = IORESOURCE_MEM,
+       },
+       {
+               .start = IRQ_RTC_ALM,
+               .flags = IORESOURCE_IRQ,
+       },
+       {
+               .start = IRQ_RTC_PRI,
+               .flags = IORESOURCE_IRQ,
+       },
+};
+static struct platform_device rtc_dev = {
+       .name = "c2k-rtc",
+       .id = -1,
+       .num_resources = ARRAY_SIZE(rtc_res),
+       .resource = rtc_res,
+};
+
+/* --------------------------------------------------------------------
+ *  DMAC controller
+ * -------------------------------------------------------------------- */
+#if defined(CONFIG_COMCERTO_DW_DMA_SUPPORT)
+static struct resource dw_dmac_resource[] = {
+	{
+		.start          = DW_DMA_DMAC_BASEADDR,
+		.end            = DW_DMA_DMAC_BASEADDR + 0x2C0,
+		.flags          = IORESOURCE_MEM,
+	},
+	{
+		.start          = IRQ_DMAC,
+		.flags          = IORESOURCE_IRQ,
+	}
+};
+
+static struct dw_dma_platform_data dw_dmac_data = {
+	.nr_channels    = 8,
+};
+
+static u64 dw_dmac_dma_mask = DMA_BIT_MASK(32);
+
+static struct platform_device dw_dmac_device = {
+	.name           = "dw_dmac",
+	.id             = 0,
+	.dev            = {
+		.dma_mask = &dw_dmac_dma_mask,
+		.platform_data  = &dw_dmac_data,
+		.coherent_dma_mask = DMA_BIT_MASK(32),
+	},
+	.resource       = dw_dmac_resource,
+	.num_resources  = ARRAY_SIZE(dw_dmac_resource),
+};
+#endif
+
+/* --------------------------------------------------------------------
+ *  NAND device
+ * -------------------------------------------------------------------- */
+#if defined(CONFIG_MTD_NAND_COMCERTO) || defined(CONFIG_MTD_NAND_COMCERTO_MODULE)
+static struct resource comcerto_nand_resources[] = {
+	{
+		.start	= COMCERTO_NAND_FIO_ADDR,
+		.end	= COMCERTO_NAND_FIO_ADDR + COMCERTO_NAND_IO_SZ - 1,
+		.flags	= IORESOURCE_MEM,
+	}
+};
+
+static struct platform_device comcerto_nand = {
+	.name		= "comcertonand",
+	.id		= -1,
+	.dev		= {
+				.platform_data	= NULL,
+	},
+	.resource	= comcerto_nand_resources,
+	.num_resources	= ARRAY_SIZE(comcerto_nand_resources),
+};
+#endif
+
+/* --------------------------------------------------------------------
+ *  SPI bus controller
+ * -------------------------------------------------------------------- */
+#if defined(CONFIG_SPI_MSPD_LOW_SPEED) || defined(CONFIG_SPI_MSPD_HIGH_SPEED)
+
+#define	CLK_NAME	10
+struct spi_controller_pdata {
+	int use_dma;
+	int num_chipselects;
+	int bus_num;
+	u32 max_freq;
+	char clk_name[CLK_NAME];
+};
+
+struct spi_platform_data {
+	int type;
+	int dummy;
+};
+
+struct spi_controller_data {
+        u8 poll_mode;   /* 0 for contoller polling mode */
+        u8 type;        /* SPI/SSP/Micrwire */
+        u8 enable_dma;
+        void (*cs_control)(u32 command);
+};
+
+struct spi_platform_data spi_pdata = {
+	.type = 0,
+	.dummy = 0,
+};
+
+struct spi_controller_data spi_ctrl_data =  {
+        .poll_mode = 1,
+};
+
+// We list more than one bmoca in here because it was attached to different
+// chip selects in different variations of the board. We have to trust the
+// driver to reject instances that aren't actually present.
+static struct spi_board_info comcerto_spi_board_info[] = {
+	{
+		.modalias = "bmoca",
+		.chip_select = 0,
+		.max_speed_hz = 20*1000*1000,
+		.bus_num = 0,
+		.irq = IRQ_G0,
+		.mode = SPI_MODE_3,
+		.platform_data = &spi_pdata,
+                .controller_data = &spi_ctrl_data,
+	},
+
+	{
+		.modalias = "bmoca",
+		.chip_select = 0,
+		.max_speed_hz = 20*1000*1000,
+		.bus_num = 1,
+		.irq = IRQ_G0,
+		.mode = SPI_MODE_3,
+		.platform_data = &spi_pdata,
+                .controller_data = &spi_ctrl_data,
+	},
+};
+#endif
+
+#if defined(CONFIG_SPI_MSPD_HIGH_SPEED)
+struct spi_controller_pdata fast_spi_pdata = {
+	.use_dma = 0,
+	.num_chipselects = 2,
+	.bus_num = 1,
+	.max_freq = 50 * 1000 * 1000,
+	.clk_name = "DUS",
+};
+#endif
+
+#if defined(CONFIG_SPI_MSPD_HIGH_SPEED) || defined(CONFIG_SPI2_MSPD_HIGH_SPEED)
+static struct resource comcerto_fast_spi_resource[] = {
+	{
+		.start  = COMCERTO_AXI_SPI_BASE,
+		.end    = COMCERTO_AXI_SPI_BASE + SZ_4K - 1,
+		.flags  = IORESOURCE_MEM,
+	},
+	{
+		.start  = IRQ_SPI,
+		.flags  = IORESOURCE_IRQ,
+	}
+};
+
+static struct platform_device comcerto_fast_spi = {
+	.name = "comcerto_spi",
+	.id = 1,
+	.num_resources = ARRAY_SIZE(comcerto_fast_spi_resource),
+	.resource = comcerto_fast_spi_resource,
+#if defined(CONFIG_SPI_MSPD_HIGH_SPEED)
+	.dev = {
+		.platform_data = &fast_spi_pdata,
+	},
+#endif
+};
+#endif
+
+#if defined(CONFIG_SPI_MSPD_LOW_SPEED)
+struct spi_controller_pdata ls_spi_pdata = {
+	.use_dma = 0,
+	.num_chipselects = 4,
+	.bus_num = 0,
+	.max_freq = 20 * 1000 * 1000,
+	.clk_name = "spi_i2c",
+};
+#endif
+
+#if defined(CONFIG_SPI_MSPD_LOW_SPEED) || defined(CONFIG_SPI2_MSPD_LOW_SPEED)
+static struct resource comcerto_spi_resource[] = {
+	{
+		.start  = COMCERTO_APB_SPI_BASE,
+		.end    = COMCERTO_APB_SPI_BASE + SZ_4K - 1,
+		.flags  = IORESOURCE_MEM,
+	},
+	{
+		.start  = IRQ_SPI_LS,
+		.flags  = IORESOURCE_IRQ,
+	}
+};
+
+static struct platform_device comcerto_spi = {
+	.name = "comcerto_spi",
+	.id = 0,
+	.num_resources = ARRAY_SIZE(comcerto_spi_resource),
+	.resource = comcerto_spi_resource,
+#if defined(CONFIG_SPI_MSPD_LOW_SPEED)
+	.dev = {
+		.platform_data = &ls_spi_pdata,
+	},
+#endif
+};
+#endif
+
+/* --------------------------------------------------------------------
+ *  I2C bus controller
+ * -------------------------------------------------------------------- */
+#if defined(CONFIG_COMCERTO_I2C_SUPPORT)
+static struct resource comcerto_i2c_resources[] = {
+	{
+		.start	= COMCERTO_APB_I2C_BASE,
+		.end	= COMCERTO_APB_I2C_BASE + SZ_4K - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+	{
+		.start	= IRQ_I2C,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct i2c_board_info comcerto_i2c_board_info[] __initdata = {
+	{
+		I2C_BOARD_INFO("lm96163", 0x4c),
+	},
+	{
+		I2C_BOARD_INFO("24c512", 0x50),
+	},
+};
+
+static struct platform_device comcerto_i2c = {
+	.name           = "comcerto_i2c",
+	.id             = 0,
+	.num_resources  = ARRAY_SIZE(comcerto_i2c_resources),
+	.resource       = comcerto_i2c_resources,
+};
+#endif
+
+/* --------------------------------------------------------------------
+*  Watchdog
+* -------------------------------------------------------------------- */
+#ifdef CONFIG_MPCORE_WATCHDOG
+static struct resource comcerto_a9wd_resources[] = {
+	{
+		.start	= COMCERTO_TWD_BASE,
+		.end	= COMCERTO_TWD_BASE + 0xFF,
+		.flags	= IORESOURCE_MEM,
+	},
+	{
+		.name	= "mpcore_wdt",
+		.start	= IRQ_LOCALWDOG,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device comcerto_a9wd = {
+	.name		= "mpcore_wdt",
+	.id             = -1,
+	.num_resources  = ARRAY_SIZE(comcerto_a9wd_resources),
+	.resource       = comcerto_a9wd_resources,
+};
+#endif
+
+#ifdef CONFIG_COMCERTO_WATCHDOG
+static struct resource comcerto_wdt_resources[] = {
+	{
+		.start	= COMCERTO_APB_TIMER_BASE + 0xD0,
+		.end	= COMCERTO_APB_TIMER_BASE + 0xD8,
+		.flags	= IORESOURCE_MEM,
+	},
+};
+
+static struct platform_device comcerto_wdt = {
+        .name   = "comcerto_wdt",
+        .id     = -1,
+	.num_resources  = ARRAY_SIZE(comcerto_wdt_resources),
+	.resource       = comcerto_wdt_resources,
+};
+#endif
+
+#if defined(CONFIG_COMCERTO_ELP_SUPPORT)
+/* --------------------------------------------------------------------
+ *  IPsec
+ * -------------------------------------------------------------------- */
+static struct resource comcerto_elp_resources[] = {
+	{
+		.name   = "elp",
+		.start  = COMCERTO_AXI_SPACC_PDU_BASE,
+		.end    = COMCERTO_AXI_SPACC_PDU_BASE + SZ_16M  - 1,
+		.flags  = IORESOURCE_MEM,
+	},
+	{
+		.name   = "irq_spacc",
+		.start  = IRQ_SPACC,
+		.end    = IRQ_SPACC,
+		.flags  = IORESOURCE_IRQ,
+	}
+};
+
+static u64 comcerto_elp_dma_mask = DMA_BIT_MASK(32);
+
+static struct platform_device  comcerto_elp_device = {
+	.name                   = "Elliptic-EPN1802",
+	.id                     = 0,
+	.num_resources          = 2,
+	.resource               = comcerto_elp_resources,
+	.dev = {
+		.dma_mask               = &comcerto_elp_dma_mask,
+		.coherent_dma_mask      = DMA_BIT_MASK(32),
+	},
+};
+#endif
+
+static struct comcerto_tdm_data comcerto_tdm_pdata = {
+	.fsoutput = 1, /* Generic Pad Control and Version ID Register[2] */
+	.fspolarity = 0, /* 28 FSYNC_FALL(RISE)_EDGE */
+	.fshwidth = 1, /* High_Phase_Width[10:0] */
+	.fslwidth = 0xFF, /* Low_Phase_Width[10:0] */
+	.clockhz = 2048000, /* INC_VALUE[29:0] According to the desired TDM clock output frequency, this field should be configured */
+	.clockout = 1, /* 0 -> set bit 21, clear bit 20 in COMCERTO_GPIO_IOCTRL_REG
+			  (software control, clock input)
+			  1 -> set bit 21 and 20 in COMCERTO_GPIO_IOCTRL_REG
+			  (software control, clock output)
+			  2 -> clear bit 21 in COMCERTO_GPIO_IOCTRL_REG (hardware control) */
+	.tdmmux = 0x1, /* TDM interface Muxing:0x0 - TDM block, 0x1 - ZDS block,
+		0x2 - GPIO[63:60] signals and 0x3 - MSIF block is selected */
+#if 0
+	/* FIX ME - Need correct values for TDM_DR, TDM_DX, TDM_FS and TDM_CK */
+	.tdmck = 0x3F,
+	.tdmfs = 0x3F,
+	.tdmdx = 0x3F,
+	.tdmdr = 0x3F,
+#endif
+};
+
+static struct platform_device comcerto_tdm_device = {
+	.name	= "comcerto-tdm",
+	.id		= 0,
+	.dev.platform_data = &comcerto_tdm_pdata,
+	.num_resources	= 0,
+	.resource = NULL,
+};
+
+static struct resource comcerto_pfe_resources[] = {
+	{
+		.name	= "apb",
+		.start  = COMCERTO_APB_PFE_BASE,
+		.end    = COMCERTO_APB_PFE_BASE + COMCERTO_APB_PFE_SIZE - 1,
+		.flags  = IORESOURCE_MEM,
+	},
+	{
+		.name	= "axi",
+		.start  = COMCERTO_AXI_PFE_BASE,
+		.end    = COMCERTO_AXI_PFE_BASE + COMCERTO_AXI_PFE_SIZE - 1,
+		.flags  = IORESOURCE_MEM,
+	},
+	{
+		.name	= "ddr",
+		.start  = COMCERTO_PFE_DDR_BASE,
+		.end	= COMCERTO_PFE_DDR_BASE + COMCERTO_PFE_DDR_SIZE - 1,
+		.flags  = IORESOURCE_MEM,
+	},
+	{
+		.name	= "iram",
+		.start  = COMCERTO_PFE_IRAM_BASE,
+		.end	= COMCERTO_PFE_IRAM_BASE + COMCERTO_PFE_IRAM_SIZE - 1,
+		.flags  = IORESOURCE_MEM,
+	},
+        {
+                .name   = "ipsec",
+                .start  = COMCERTO_AXI_IPSEC_BASE,
+                .end    = COMCERTO_AXI_IPSEC_BASE + COMCERTO_AXI_IPSEC_SIZE - 1,
+                .flags  = IORESOURCE_MEM,
+        },
+
+	{
+		.name	= "hif",
+		.start  = IRQ_PFE_HIF,
+		.flags  = IORESOURCE_IRQ,
+	},
+};
+
+static struct comcerto_pfe_platform_data comcerto_pfe_pdata = {
+	.comcerto_eth_pdata[0] = {
+		.name = "lan0",
+		.device_flags = CONFIG_COMCERTO_GEMAC,
+		.mii_config = CONFIG_COMCERTO_USE_RGMII,
+		.gemac_mode = GEMAC_SW_CONF | GEMAC_SW_FULL_DUPLEX | GEMAC_SW_SPEED_1G,
+		.phy_flags = GEMAC_NO_PHY,
+		.gem_id = 0,
+		.mac_addr = (u8[])GEM0_MAC,
+	},
+
+	.comcerto_eth_pdata[1] = {
+		.name = "wan0",
+		.device_flags = CONFIG_COMCERTO_GEMAC,
+		.mii_config = CONFIG_COMCERTO_USE_RGMII,
+		.gemac_mode = GEMAC_SW_CONF | GEMAC_SW_FULL_DUPLEX | GEMAC_SW_SPEED_1G,
+		.phy_flags = GEMAC_PHY_RGMII_ADD_DELAY,
+		.bus_id = 0,
+		.phy_id = 4,
+		.gem_id = 1,
+		.mac_addr = (u8[])GEM1_MAC,
+	},
+
+	.comcerto_eth_pdata[2] = {
+		.name = "moca0",
+		.device_flags = CONFIG_COMCERTO_GEMAC,
+		.mii_config = CONFIG_COMCERTO_USE_RGMII,
+		.gemac_mode = GEMAC_SW_CONF | GEMAC_SW_FULL_DUPLEX | GEMAC_SW_SPEED_1G,
+		.phy_flags = GEMAC_NO_PHY,
+		.gem_id = 2,
+		.mac_addr = (u8[])GEM2_MAC,
+	},
+
+	/**
+	 * There is a single mdio bus coming out of C2K.  And that's the one
+	 * connected to GEM0. All PHY's, switchs will be connected to the same
+	 * bus using different addresses. Typically .bus_id is always 0, only
+	 * .phy_id will change in the different comcerto_eth_pdata[] structures above.
+	 */
+	.comcerto_mdio_pdata[0] = {
+		.enabled = 1,
+		.phy_mask = 0xFFFFFFEF,
+		.mdc_div = 96,
+		.irq = {
+			[4] = PHY_POLL,
+		},
+	},
+};
+
+static u64 comcerto_pfe_dma_mask = DMA_BIT_MASK(32);
+
+static struct platform_device comcerto_pfe_device = {
+	.name		= "pfe",
+	.id		= 0,
+	.dev		= {
+		.platform_data		= &comcerto_pfe_pdata,
+		.dma_mask		= &comcerto_pfe_dma_mask,
+		.coherent_dma_mask	= DMA_BIT_MASK(32),
+	},
+	.num_resources	= ARRAY_SIZE(comcerto_pfe_resources),
+	.resource	= comcerto_pfe_resources,
+};
+
+static struct platform_device *comcerto_devices[] __initdata = {
+#if defined(CONFIG_MTD_NAND_COMCERTO) || defined(CONFIG_MTD_NAND_COMCERTO_MODULE)
+		&comcerto_nand,
+#endif
+#if defined(CONFIG_MTD_COMCERTO_NOR)
+		&comcerto_nor,
+#endif
+#if defined(CONFIG_COMCERTO_I2C_SUPPORT)
+		&comcerto_i2c,
+#endif
+
+#if defined (CONFIG_MPCORE_WATCHDOG)
+		&comcerto_a9wd,
+#endif
+
+#if defined(CONFIG_COMCERTO_WATCHDOG)
+		&comcerto_wdt,
+#endif
+
+#if defined(CONFIG_SPI_MSPD_HIGH_SPEED) || defined(CONFIG_SPI2_MSPD_HIGH_SPEED)
+		&comcerto_fast_spi,
+#endif
+#if defined(CONFIG_SPI_MSPD_LOW_SPEED) || defined(CONFIG_SPI2_MSPD_LOW_SPEED)
+		&comcerto_spi,
+#endif
+#if defined(CONFIG_COMCERTO_DW_DMA_SUPPORT)
+		&dw_dmac_device,
+#endif
+		&comcerto_tdm_device,
+		&comcerto_pfe_device,
+		&rtc_dev,
+#if defined(CONFIG_COMCERTO_ELP_SUPPORT)
+	&comcerto_elp_device,
+#endif
+};
+
+
+/************************************************************************
+ *  Expansion bus
+ *
+ ************************************************************************/
+/* This variable is used by comcerto-2000.c to initialize the expansion bus */
+int comcerto_exp_values[5][7]= {
+	/* ENABLE, BASE, SEG_SZ, CFG, TMG1, TMG2, TMG3 */
+	{1, (EXP_BUS_REG_BASE_CS0 >> 12), ((EXP_BUS_REG_BASE_CS0 + EXP_CS0_SEG_SIZE - 1) >> 12), EXP_MEM_BUS_SIZE_16, 0x1A1A401F, 0x06060A04, 0x00000002},		/*TODO Values to check*/
+	{0, (EXP_BUS_REG_BASE_CS1 >> 12), ((EXP_BUS_REG_BASE_CS1 + EXP_CS1_SEG_SIZE - 1) >> 12), EXP_RDY_EN|EXP_MEM_BUS_SIZE_32, 0x1A1A401F, 0x06060A04, 0x00000002},	/*TODO Values to check*/
+	{0, (EXP_BUS_REG_BASE_CS2 >> 12), ((EXP_BUS_REG_BASE_CS2 + EXP_CS2_SEG_SIZE - 1) >> 12), EXP_STRB_MODE|EXP_ALE_MODE|EXP_MEM_BUS_SIZE_8, 0x1A10201A, 0x03080403, 0x0000002},	/*TODO Values to check*/
+	{0, (EXP_BUS_REG_BASE_CS3 >> 12), ((EXP_BUS_REG_BASE_CS3 + EXP_CS3_SEG_SIZE - 1) >> 12), EXP_STRB_MODE|EXP_ALE_MODE|EXP_MEM_BUS_SIZE_8, 0x1A10201A, 0x03080403, 0x0000002},	/*BT8370*/
+	{0, (EXP_BUS_REG_BASE_CS4 >> 12), ((EXP_BUS_REG_BASE_CS4 + EXP_CS4_SEG_SIZE - 1) >> 12), EXP_NAND_MODE|EXP_MEM_BUS_SIZE_8, 0x01010001, 0x01010101, 0x00000001},	/* NAND: TODO Values to check */
+};
+
+/************************************************************************
+ *  Machine definition
+ *
+ ************************************************************************/
+static void __init platform_map_io(void)
+{
+	device_map_io();
+}
+
+static void __init platform_irq_init(void)
+{
+	device_irq_init();
+}
+
+static void __init platform_init(void)
+{
+	device_init();
+	board_gpio_init();
+
+#if defined(CONFIG_SPI_MSPD_LOW_SPEED) || defined(CONFIG_SPI_MSPD_HIGH_SPEED)
+	spi_register_board_info(comcerto_spi_board_info, ARRAY_SIZE(comcerto_spi_board_info));
+#endif
+#if defined(CONFIG_COMCERTO_I2C_SUPPORT)
+	i2c_register_board_info(0, comcerto_i2c_board_info, ARRAY_SIZE(comcerto_i2c_board_info));
+#endif
+	mac_addr_init(&comcerto_pfe_pdata);
+
+	platform_add_devices(comcerto_devices, ARRAY_SIZE(comcerto_devices));
+}
+
+MACHINE_START(COMCERTO, "Google Fiber Optimus")
+	.atag_offset    = COMCERTO_AXI_DDR_BASE + 0x100,
+	.reserve	= platform_reserve,
+	.map_io		= platform_map_io,
+	.init_irq	= platform_irq_init,
+	.init_machine	= platform_init,
+	.timer		= &comcerto_timer,
+#ifdef CONFIG_ZONE_DMA
+	.dma_zone_size	= SZ_32M + 3*SZ_4M,
+#endif
+MACHINE_END
diff --git a/arch/arm/mach-comcerto/bootlog.c b/arch/arm/mach-comcerto/bootlog.c
new file mode 100644
index 0000000..357f8ff
--- /dev/null
+++ b/arch/arm/mach-comcerto/bootlog.c
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+/* CONFIG_BOOTLOG_COPY */
+
+#include <linux/bootmem.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/ioport.h>
+#include <linux/list.h>
+#include <linux/vmalloc.h>
+#include <linux/compiler.h>
+#include <linux/module.h>
+
+#define BOOTLOG_MAXSIZE		(1024*1024)
+
+static unsigned long bootlog_addr = 0;
+static unsigned long bootlog_size = 0;
+static int bootlog_is_reserved = 0;
+
+unsigned long bootlog_get_addr(void)
+{
+
+	if (!bootlog_addr)
+		return 0;
+
+	if (!bootlog_is_reserved) {
+		if (reserve_bootmem(bootlog_addr, bootlog_size, BOOTMEM_EXCLUSIVE)) {
+			printk(KERN_WARNING "bootlog: reserve_bootmem(0x%lx, 0x%lx) failed\n", bootlog_addr, bootlog_size);
+			return 0;
+		}
+	}
+	return bootlog_addr;
+}
+
+unsigned long bootlog_get_size(void)
+{
+	if (!bootlog_addr)
+		return 0;
+
+	return bootlog_size;
+}
+
+void bootlog_free(void)
+{
+	if (bootlog_is_reserved) {
+	      free_bootmem(bootlog_addr, bootlog_size);
+	}
+	bootlog_addr = bootlog_size = 0;
+}
+
+static int __init bootlog_setup(char *str)
+{
+	unsigned long addr = 0;
+	unsigned long size;
+
+	size = (unsigned long) memparse(str, &str);
+	if (*str == '@')
+		addr = (unsigned long)memparse(str + 1, &str);
+
+	if (!addr) {
+		printk(KERN_WARNING "bootlog: address and size must not be 0,"
+		       " ignoring range '%s'\n", str);
+		return 0;
+	}
+
+	bootlog_addr = addr;
+	bootlog_size = size;
+
+	return 0;
+}
+
+early_param("bootlog", bootlog_setup);
diff --git a/arch/arm/mach-comcerto/comcerto-2000.c b/arch/arm/mach-comcerto/comcerto-2000.c
index b5b0cb7..9ce93ed 100644
--- a/arch/arm/mach-comcerto/comcerto-2000.c
+++ b/arch/arm/mach-comcerto/comcerto-2000.c
@@ -207,20 +207,11 @@
 	/*[FIXME]: GPIO Output, others are input*/
 	__raw_writel(__raw_readl(COMCERTO_GPIO_OE_REG) | COMCERTO_OUTPUT_GPIO, COMCERTO_GPIO_OE_REG);
 
-	/*[FIXME]: GPIO IRQ Configuration */
-	__raw_writel(COMCERTO_IRQ_RISING_EDGE_GPIO, COMCERTO_GPIO_INT_CFG_REG);
-
-#if !defined(CONFIG_C2K_MFCN_EVM)
-	/* [FIXME]: Need to have proper defines for enabling the GPIO irq */
-	__raw_writel(__raw_readl(COMCERTO_GPIO_OE_REG)     | (0x1 << 5), COMCERTO_GPIO_OE_REG);		// enable GPIO5 (SLIC_RESET_n) as output
-	__raw_writel(__raw_readl(COMCERTO_GPIO_OUTPUT_REG) | (0x1 << 5), COMCERTO_GPIO_OUTPUT_REG);     // clear reset
-	udelay(15);
-	__raw_writel(__raw_readl(COMCERTO_GPIO_OUTPUT_REG) & ~(0x1 << 5), COMCERTO_GPIO_OUTPUT_REG);	// put in reset
-	udelay(15);
-	__raw_writel(__raw_readl(COMCERTO_GPIO_OUTPUT_REG) | (0x1 << 5), COMCERTO_GPIO_OUTPUT_REG); 	// clear reset after some time
-#endif
-	__raw_writel(0x4, COMCERTO_GPIO_INT_CFG_REG); /* si3227 is falling edge interrupt(gpio1) */
-
+	/*
+	 * Default GPIO IRQ Configuration. Enable specific IRQs in the
+	 * board-*.c file
+	 */
+	__raw_writel(0, COMCERTO_GPIO_INT_CFG_REG);
 
 	/* [FIXME]: Are pins GPIO or pins used by another block*/
 	//__raw_writel(COMCERTO_GPIO_PIN_USAGE, COMCERTO_GPIO_IOCTRL_REG);
@@ -284,7 +275,6 @@
 			__raw_writel(comcerto_exp_values[cs][2], COMCERTO_EXP_CSx_SEG_R(cs));
 
 			/*Chip select timing configuration*/
-			/* [FIXME] : Using default timing values */
 			__raw_writel(comcerto_exp_values[cs][4], COMCERTO_EXP_CSx_TMG1_R(cs));
 			__raw_writel(comcerto_exp_values[cs][5], COMCERTO_EXP_CSx_TMG2_R(cs));
 			__raw_writel(comcerto_exp_values[cs][6], COMCERTO_EXP_CSx_TMG3_R(cs));
diff --git a/arch/arm/mach-comcerto/include/mach/board-optimus.h b/arch/arm/mach-comcerto/include/mach/board-optimus.h
new file mode 100644
index 0000000..581114c
--- /dev/null
+++ b/arch/arm/mach-comcerto/include/mach/board-optimus.h
@@ -0,0 +1,52 @@
+/*
+ * arch/arm/mach-comcerto/include/mach/board-optimus.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __BOARD_OPTIMUS_H__
+#define __BOARD_OPTIMUS_H__
+
+#include <mach/hardware.h>
+
+	/***********************************
+	 * Expansion bus configuration
+	 ***********************************/
+
+	#define COMCERTO_EXPCLK		50000000	/* 50MHz */
+	#define MOCA_RESET_GPIO_PIN	GPIO_PIN_11
+
+	/***********************************
+	 * GPIO
+	 ***********************************/
+	#define COMCERTO_OUTPUT_GPIO	(COMCERTO_NAND_CE|MOCA_RESET_GPIO_PIN)
+	/*Are pins used either as GPIO or as pins for others IP blocks*/
+	#define COMCERTO_GPIO_PIN_USAGE		(SPI_BUS) // [FIXME]
+
+	/***********************************
+	 * EEPROM
+	 ***********************************/
+
+	/***********************************
+	 * NOR
+	 ***********************************/
+	#define NORFLASH_MEMORY_PHY1		EXP_CS0_AXI_BASEADDR
+
+	/***********************************
+	 * NAND
+	 ***********************************/
+	#define COMCERTO_EXP_CS4_SEG_SZ		1
+
+	#define COMCERTO_NAND_FIO_ADDR		EXP_CS4_AXI_BASEADDR
+	#define COMCERTO_NAND_BR		0x20000000 /* BR is on GPIO_29 */
+	#define COMCERTO_NAND_CE		0x10000000 /* CE is on GPIO_28 */
+	#define COMCERTO_NAND_IO_SZ		((COMCERTO_EXP_CS4_SEG_SZ << 12) +0x1000)
+
+	/***********************************
+	 * SLIC
+	 ***********************************/
+	#define COMCERTO_SLIC_GPIO_IRQ		IRQ_G2
+
+#endif
diff --git a/arch/arm/mach-comcerto/include/mach/comcerto-common.h b/arch/arm/mach-comcerto/include/mach/comcerto-common.h
index 6a14993..f87b8a8 100644
--- a/arch/arm/mach-comcerto/include/mach/comcerto-common.h
+++ b/arch/arm/mach-comcerto/include/mach/comcerto-common.h
@@ -98,11 +98,6 @@
 #define GEMAC_NO_PHY			(1 << 0)		// set if no phy connected to MAC (ex ethernet switch). In this case use MAC fixed configuration
 #define GEMAC_PHY_RGMII_ADD_DELAY	(1 << 1)
 
-/* gemac to interface name assignment */
-#define GEM0_ITF_NAME "eth0"
-#define GEM1_ITF_NAME "eth2"
-#define GEM2_ITF_NAME "eth3"
-
 #define GEM0_MAC { 0x00, 0xED, 0xCD, 0xEF, 0xAA, 0xCC }
 #define GEM1_MAC { 0x00, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E }
 #define GEM2_MAC { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 }
diff --git a/arch/arm/mach-comcerto/include/mach/debug-macro.S b/arch/arm/mach-comcerto/include/mach/debug-macro.S
index c1efb9b..345ac4c 100644
--- a/arch/arm/mach-comcerto/include/mach/debug-macro.S
+++ b/arch/arm/mach-comcerto/include/mach/debug-macro.S
@@ -22,14 +22,14 @@
 
 
 	.macro	addruart, rx, rv, tmp
-	moveq	\rx, #0x96000000	@ physical base address
-	@movne	\rv, #0xf1600000	@ virtual base
+	ldr	\rx, =0x96000000	@ physical base address
+	ldr	\rv, =0xf1600000	@ virtual base
 	#ifdef CONFIG_COMCERTO_UART1_SUPPORT
-		orr	\rx, \rx, #0x00400000
-		orr	\rv, \rv, #0x00400000
+		add	\rx, \rx, #0x00400000
+		add	\rv, \rv, #0x00400000
 	#elif CONFIG_COMCERTO_UART0_SUPPORT
-		orr	\rx, \rx, #0x00300000
-		orr	\rv, \rv, #0x00300000
+		add	\rx, \rx, #0x00300000
+		add	\rv, \rv, #0x00300000
 	#else
 		#error no uart defined
 	#endif
diff --git a/arch/arm/mach-comcerto/include/mach/hardware.h b/arch/arm/mach-comcerto/include/mach/hardware.h
index 3099117..b86a3f1 100644
--- a/arch/arm/mach-comcerto/include/mach/hardware.h
+++ b/arch/arm/mach-comcerto/include/mach/hardware.h
@@ -34,6 +34,8 @@
 	/***** Board *****/
 	#if defined(CONFIG_C2K_ASIC)
 		#include <mach/board-c2kasic.h>
+	#elif defined(CONFIG_GOOGLE_FIBER_OPTIMUS)
+		#include <mach/board-optimus.h>
 	#elif defined(CONFIG_C2K_EVM)
 		#include <mach/board-c2kevm.h>
 	#elif defined(CONFIG_C2K_MFCN_EVM)
diff --git a/arch/arm/mach-comcerto/pcie-c2000.c b/arch/arm/mach-comcerto/pcie-c2000.c
index d4c01fb..e31d010 100644
--- a/arch/arm/mach-comcerto/pcie-c2000.c
+++ b/arch/arm/mach-comcerto/pcie-c2000.c
@@ -1842,8 +1842,7 @@
         }
 
 	if (system_rev == 1) {
-		printk(KERN_INFO "PCIe: Detected C2K RevA1 device serdes clk devider old:new=%x:%x\n",
-				p_pcie_phy_reg_file[0x61].val, 0x06);
+		// C2K RevA1 devices use a different serdes clk divider
 		p_pcie_phy_reg_file[0x61].val = 0x6;
 	}
 
diff --git a/arch/arm/mach-comcerto/platsmp.c b/arch/arm/mach-comcerto/platsmp.c
index 7c0c727..568917b 100644
--- a/arch/arm/mach-comcerto/platsmp.c
+++ b/arch/arm/mach-comcerto/platsmp.c
@@ -106,22 +106,24 @@
 wait_for_cpu1_hotplug_done:
 
 	/* Waiting for hotplug event invoked by CPU hotplug framework */
-	wait_event(cpu1_hotplug, cpu1_hotplug_done>0);
+	pr_info("cpu1 waiting for hotplug event.\n");
+	wait_event_interruptible(cpu1_hotplug, cpu1_hotplug_done>0);
+	pr_info("cpu1 hotplug done!\n");
 
+	if (cpu1_hotplug_done > 0) {
 #ifdef CONFIG_NEON
-	__raw_writel((__raw_readl(A9DP_CPU_CLK_CNTRL) & ~NEON1_CLK_ENABLE), A9DP_CPU_CLK_CNTRL);
-	__raw_writel((__raw_readl(A9DP_CPU_RESET) | NEON1_RST), A9DP_CPU_RESET);
+		__raw_writel((__raw_readl(A9DP_CPU_CLK_CNTRL) & ~NEON1_CLK_ENABLE), A9DP_CPU_CLK_CNTRL);
+		__raw_writel((__raw_readl(A9DP_CPU_RESET) | NEON1_RST), A9DP_CPU_RESET);
 #endif
-	__raw_writel((__raw_readl(A9DP_CPU_CLK_CNTRL) & ~CPU1_CLK_ENABLE), A9DP_CPU_CLK_CNTRL);
-	__raw_writel((__raw_readl(A9DP_PWR_CNTRL) | CLAMP_CORE1), A9DP_PWR_CNTRL);
-	__raw_writel((__raw_readl(A9DP_PWR_CNTRL) | CORE_PWRDWN1), A9DP_PWR_CNTRL);
-	__raw_writel((__raw_readl(A9DP_CPU_RESET) | CPU1_RST), A9DP_CPU_RESET);
-	__raw_writel((__raw_readl(A9DP_PWR_CNTRL) & ~CORE_PWRDWN1), A9DP_PWR_CNTRL);
+		__raw_writel((__raw_readl(A9DP_CPU_CLK_CNTRL) & ~CPU1_CLK_ENABLE), A9DP_CPU_CLK_CNTRL);
+		__raw_writel((__raw_readl(A9DP_PWR_CNTRL) | CLAMP_CORE1), A9DP_PWR_CNTRL);
+		__raw_writel((__raw_readl(A9DP_PWR_CNTRL) | CORE_PWRDWN1), A9DP_PWR_CNTRL);
+		__raw_writel((__raw_readl(A9DP_CPU_RESET) | CPU1_RST), A9DP_CPU_RESET);
+		__raw_writel((__raw_readl(A9DP_PWR_CNTRL) & ~CORE_PWRDWN1), A9DP_PWR_CNTRL);
 
-	cpu1_hotplug_done = 0;
+		cpu1_hotplug_done = 0;
+	}
 	goto wait_for_cpu1_hotplug_done;
-
-	return;
 }
 
 void __init platform_smp_prepare_cpus(unsigned int max_cpus)
diff --git a/drivers/hwmon/lm63.c b/drivers/hwmon/lm63.c
index 508cb29..5e6457a 100644
--- a/drivers/hwmon/lm63.c
+++ b/drivers/hwmon/lm63.c
@@ -47,10 +47,14 @@
 #include <linux/err.h>
 #include <linux/mutex.h>
 #include <linux/sysfs.h>
+#include <linux/types.h>
 
 /*
  * Addresses to scan
- * Address is fully defined internally and cannot be changed.
+ * Address is fully defined internally and cannot be changed except for
+ * LM64 which has one pin dedicated to address selection.
+ * LM63 and LM96163 have address 0x4c.
+ * LM64 can have address 0x18 or 0x4e.
  */
 
 static const unsigned short normal_i2c[] = { 0x18, 0x4c, 0x4e, I2C_CLIENT_END };
@@ -60,6 +64,7 @@
  */
 
 #define LM63_REG_CONFIG1		0x03
+#define LM63_REG_CONVRATE		0x04
 #define LM63_REG_CONFIG2		0xBF
 #define LM63_REG_CONFIG_FAN		0x4A
 
@@ -70,6 +75,9 @@
 
 #define LM63_REG_PWM_VALUE		0x4C
 #define LM63_REG_PWM_FREQ		0x4D
+#define LM63_REG_LUT_TEMP_HYST		0x4F
+#define LM63_REG_LUT_TEMP(nr)		(0x50 + 2 * (nr))
+#define LM63_REG_LUT_PWM(nr)		(0x51 + 2 * (nr))
 
 #define LM63_REG_LOCAL_TEMP		0x00
 #define LM63_REG_LOCAL_HIGH		0x05
@@ -91,6 +99,16 @@
 #define LM63_REG_MAN_ID			0xFE
 #define LM63_REG_CHIP_ID		0xFF
 
+#define LM96163_REG_TRUTHERM		0x30
+#define LM96163_REG_REMOTE_TEMP_U_MSB	0x31
+#define LM96163_REG_REMOTE_TEMP_U_LSB	0x32
+#define LM96163_REG_CONFIG_ENHANCED	0x45
+
+#define LM63_MAX_CONVRATE		9
+
+#define LM63_MAX_CONVRATE_HZ		32
+#define LM96163_MAX_CONVRATE_HZ		26
+
 /*
  * Conversions and various macros
  * For tachometer counts, the LM63 uses 16-bit values.
@@ -112,15 +130,24 @@
 				 (val) >= 127000 ? 127 : \
 				 (val) < 0 ? ((val) - 500) / 1000 : \
 				 ((val) + 500) / 1000)
+#define TEMP8U_TO_REG(val)	((val) <= 0 ? 0 : \
+				 (val) >= 255000 ? 255 : \
+				 ((val) + 500) / 1000)
 #define TEMP11_FROM_REG(reg)	((reg) / 32 * 125)
 #define TEMP11_TO_REG(val)	((val) <= -128000 ? 0x8000 : \
 				 (val) >= 127875 ? 0x7FE0 : \
 				 (val) < 0 ? ((val) - 62) / 125 * 32 : \
 				 ((val) + 62) / 125 * 32)
+#define TEMP11U_TO_REG(val)	((val) <= 0 ? 0 : \
+				 (val) >= 255875 ? 0xFFE0 : \
+				 ((val) + 62) / 125 * 32)
 #define HYST_TO_REG(val)	((val) <= 0 ? 0 : \
 				 (val) >= 127000 ? 127 : \
 				 ((val) + 500) / 1000)
 
+#define UPDATE_INTERVAL(max, rate) \
+			((1000 << (LM63_MAX_CONVRATE - (rate))) / (max))
+
 /*
  * Functions declaration
  */
@@ -134,7 +161,7 @@
 static int lm63_detect(struct i2c_client *client, struct i2c_board_info *info);
 static void lm63_init_client(struct i2c_client *client);
 
-enum chips { lm63, lm64 };
+enum chips { lm63, lm64, lm96163 };
 
 /*
  * Driver data (common to all clients)
@@ -143,6 +170,7 @@
 static const struct i2c_device_id lm63_id[] = {
 	{ "lm63", lm63 },
 	{ "lm64", lm64 },
+	{ "lm96163", lm96163 },
 	{ }
 };
 MODULE_DEVICE_TABLE(i2c, lm63_id);
@@ -167,26 +195,53 @@
 	struct device *hwmon_dev;
 	struct mutex update_lock;
 	char valid; /* zero until following fields are valid */
+	char lut_valid; /* zero until lut fields are valid */
 	unsigned long last_updated; /* in jiffies */
-	int kind;
+	unsigned long lut_last_updated; /* in jiffies */
+	enum chips kind;
 	int temp2_offset;
 
+	int update_interval;	/* in milliseconds */
+	int max_convrate_hz;
+	int lut_size;		/* 8 or 12 */
+
 	/* registers values */
 	u8 config, config_fan;
 	u16 fan[2];	/* 0: input
 			   1: low limit */
 	u8 pwm1_freq;
-	u8 pwm1_value;
-	s8 temp8[3];	/* 0: local input
+	u8 pwm1[13];	/* 0: current output
+			   1-12: lookup table */
+	s8 temp8[15];	/* 0: local input
 			   1: local high limit
-			   2: remote critical limit */
-	s16 temp11[3];	/* 0: remote input
+			   2: remote critical limit
+			   3-14: lookup table */
+	s16 temp11[4];	/* 0: remote input
 			   1: remote low limit
-			   2: remote high limit */
+			   2: remote high limit
+			   3: remote offset */
+	u16 temp11u;	/* remote input (unsigned) */
 	u8 temp2_crit_hyst;
+	u8 lut_temp_hyst;
 	u8 alarms;
+	bool pwm_highres;
+	bool lut_temp_highres;
+	bool remote_unsigned; /* true if unsigned remote upper limits */
+	bool trutherm;
 };
 
+static inline int temp8_from_reg(struct lm63_data *data, int nr)
+{
+	if (data->remote_unsigned)
+		return TEMP8_FROM_REG((u8)data->temp8[nr]);
+	return TEMP8_FROM_REG(data->temp8[nr]);
+}
+
+static inline int lut_temp_from_reg(struct lm63_data *data, int nr)
+{
+	return data->temp8[nr] * (data->lut_temp_highres ? 500 : 1000);
+}
+
 /*
  * Sysfs callback functions and files
  */
@@ -204,7 +259,12 @@
 {
 	struct i2c_client *client = to_i2c_client(dev);
 	struct lm63_data *data = i2c_get_clientdata(client);
-	unsigned long val = simple_strtoul(buf, NULL, 10);
+	unsigned long val;
+	int err;
+
+	err = kstrtoul(buf, 10, &val);
+	if (err)
+		return err;
 
 	mutex_lock(&data->update_lock);
 	data->fan[1] = FAN_TO_REG(val);
@@ -216,13 +276,22 @@
 	return count;
 }
 
-static ssize_t show_pwm1(struct device *dev, struct device_attribute *dummy,
+static ssize_t show_pwm1(struct device *dev, struct device_attribute *devattr,
 			 char *buf)
 {
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
 	struct lm63_data *data = lm63_update_device(dev);
-	return sprintf(buf, "%d\n", data->pwm1_value >= 2 * data->pwm1_freq ?
-		       255 : (data->pwm1_value * 255 + data->pwm1_freq) /
-		       (2 * data->pwm1_freq));
+	int nr = attr->index;
+	int pwm;
+
+	if (data->pwm_highres)
+		pwm = data->pwm1[nr];
+	else
+		pwm = data->pwm1[nr] >= 2 * data->pwm1_freq ?
+		       255 : (data->pwm1[nr] * 255 + data->pwm1_freq) /
+		       (2 * data->pwm1_freq);
+
+	return sprintf(buf, "%d\n", pwm);
 }
 
 static ssize_t set_pwm1(struct device *dev, struct device_attribute *dummy,
@@ -231,22 +300,26 @@
 	struct i2c_client *client = to_i2c_client(dev);
 	struct lm63_data *data = i2c_get_clientdata(client);
 	unsigned long val;
-	
+	int err;
+
 	if (!(data->config_fan & 0x20)) /* register is read-only */
 		return -EPERM;
 
-	val = simple_strtoul(buf, NULL, 10);
+	err = kstrtoul(buf, 10, &val);
+	if (err)
+		return err;
+
+	val = SENSORS_LIMIT(val, 0, 255);
 	mutex_lock(&data->update_lock);
-	data->pwm1_value = val <= 0 ? 0 :
-			   val >= 255 ? 2 * data->pwm1_freq :
-			   (val * data->pwm1_freq * 2 + 127) / 255;
-	i2c_smbus_write_byte_data(client, LM63_REG_PWM_VALUE, data->pwm1_value);
+	data->pwm1[0] = data->pwm_highres ? val :
+			(val * data->pwm1_freq * 2 + 127) / 255;
+	i2c_smbus_write_byte_data(client, LM63_REG_PWM_VALUE, data->pwm1[0]);
 	mutex_unlock(&data->update_lock);
 	return count;
 }
 
-static ssize_t show_pwm1_enable(struct device *dev, struct device_attribute *dummy,
-				char *buf)
+static ssize_t show_pwm1_enable(struct device *dev,
+				struct device_attribute *dummy, char *buf)
 {
 	struct lm63_data *data = lm63_update_device(dev);
 	return sprintf(buf, "%d\n", data->config_fan & 0x20 ? 1 : 2);
@@ -273,21 +346,47 @@
 {
 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
 	struct lm63_data *data = lm63_update_device(dev);
-	return sprintf(buf, "%d\n", TEMP8_FROM_REG(data->temp8[attr->index])
+	return sprintf(buf, "%d\n", temp8_from_reg(data, attr->index)
 		       + data->temp2_offset);
 }
 
-static ssize_t set_local_temp8(struct device *dev,
-			       struct device_attribute *dummy,
-			       const char *buf, size_t count)
+static ssize_t show_lut_temp(struct device *dev,
+			      struct device_attribute *devattr,
+			      char *buf)
 {
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+	struct lm63_data *data = lm63_update_device(dev);
+	return sprintf(buf, "%d\n", lut_temp_from_reg(data, attr->index)
+		       + data->temp2_offset);
+}
+
+static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr,
+			 const char *buf, size_t count)
+{
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
 	struct i2c_client *client = to_i2c_client(dev);
 	struct lm63_data *data = i2c_get_clientdata(client);
-	long val = simple_strtol(buf, NULL, 10);
+	int nr = attr->index;
+	int reg = nr == 2 ? LM63_REG_REMOTE_TCRIT : LM63_REG_LOCAL_HIGH;
+	long val;
+	int err;
+	int temp;
+
+	err = kstrtol(buf, 10, &val);
+	if (err)
+		return err;
 
 	mutex_lock(&data->update_lock);
-	data->temp8[1] = TEMP8_TO_REG(val);
-	i2c_smbus_write_byte_data(client, LM63_REG_LOCAL_HIGH, data->temp8[1]);
+	if (nr == 2) {
+		if (data->remote_unsigned)
+			temp = TEMP8U_TO_REG(val - data->temp2_offset);
+		else
+			temp = TEMP8_TO_REG(val - data->temp2_offset);
+	} else {
+		temp = TEMP8_TO_REG(val);
+	}
+	data->temp8[nr] = temp;
+	i2c_smbus_write_byte_data(client, reg, temp);
 	mutex_unlock(&data->update_lock);
 	return count;
 }
@@ -297,28 +396,56 @@
 {
 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
 	struct lm63_data *data = lm63_update_device(dev);
-	return sprintf(buf, "%d\n", TEMP11_FROM_REG(data->temp11[attr->index])
-		       + data->temp2_offset);
+	int nr = attr->index;
+	int temp;
+
+	if (!nr) {
+		/*
+		 * Use unsigned temperature unless its value is zero.
+		 * If it is zero, use signed temperature.
+		 */
+		if (data->temp11u)
+			temp = TEMP11_FROM_REG(data->temp11u);
+		else
+			temp = TEMP11_FROM_REG(data->temp11[nr]);
+	} else {
+		if (data->remote_unsigned && nr == 2)
+			temp = TEMP11_FROM_REG((u16)data->temp11[nr]);
+		else
+			temp = TEMP11_FROM_REG(data->temp11[nr]);
+	}
+	return sprintf(buf, "%d\n", temp + data->temp2_offset);
 }
 
 static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr,
 			  const char *buf, size_t count)
 {
-	static const u8 reg[4] = {
+	static const u8 reg[6] = {
 		LM63_REG_REMOTE_LOW_MSB,
 		LM63_REG_REMOTE_LOW_LSB,
 		LM63_REG_REMOTE_HIGH_MSB,
 		LM63_REG_REMOTE_HIGH_LSB,
+		LM63_REG_REMOTE_OFFSET_MSB,
+		LM63_REG_REMOTE_OFFSET_LSB,
 	};
 
 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
 	struct i2c_client *client = to_i2c_client(dev);
 	struct lm63_data *data = i2c_get_clientdata(client);
-	long val = simple_strtol(buf, NULL, 10);
+	long val;
+	int err;
 	int nr = attr->index;
 
+	err = kstrtol(buf, 10, &val);
+	if (err)
+		return err;
+
 	mutex_lock(&data->update_lock);
-	data->temp11[nr] = TEMP11_TO_REG(val - data->temp2_offset);
+	if (data->remote_unsigned && nr == 2)
+		data->temp11[nr] = TEMP11U_TO_REG(val - data->temp2_offset);
+	else
+		data->temp11[nr] = TEMP11_TO_REG(val - data->temp2_offset);
+
 	i2c_smbus_write_byte_data(client, reg[(nr - 1) * 2],
 				  data->temp11[nr] >> 8);
 	i2c_smbus_write_byte_data(client, reg[(nr - 1) * 2 + 1],
@@ -327,35 +454,143 @@
 	return count;
 }
 
-/* Hysteresis register holds a relative value, while we want to present
-   an absolute to user-space */
-static ssize_t show_temp2_crit_hyst(struct device *dev, struct device_attribute *dummy,
-				    char *buf)
+/*
+ * Hysteresis register holds a relative value, while we want to present
+ * an absolute to user-space
+ */
+static ssize_t show_temp2_crit_hyst(struct device *dev,
+				    struct device_attribute *dummy, char *buf)
 {
 	struct lm63_data *data = lm63_update_device(dev);
-	return sprintf(buf, "%d\n", TEMP8_FROM_REG(data->temp8[2])
+	return sprintf(buf, "%d\n", temp8_from_reg(data, 2)
 		       + data->temp2_offset
 		       - TEMP8_FROM_REG(data->temp2_crit_hyst));
 }
 
-/* And now the other way around, user-space provides an absolute
-   hysteresis value and we have to store a relative one */
-static ssize_t set_temp2_crit_hyst(struct device *dev, struct device_attribute *dummy,
+static ssize_t show_lut_temp_hyst(struct device *dev,
+				  struct device_attribute *devattr, char *buf)
+{
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+	struct lm63_data *data = lm63_update_device(dev);
+
+	return sprintf(buf, "%d\n", lut_temp_from_reg(data, attr->index)
+		       + data->temp2_offset
+		       - TEMP8_FROM_REG(data->lut_temp_hyst));
+}
+
+/*
+ * And now the other way around, user-space provides an absolute
+ * hysteresis value and we have to store a relative one
+ */
+static ssize_t set_temp2_crit_hyst(struct device *dev,
+				   struct device_attribute *dummy,
 				   const char *buf, size_t count)
 {
 	struct i2c_client *client = to_i2c_client(dev);
 	struct lm63_data *data = i2c_get_clientdata(client);
-	long val = simple_strtol(buf, NULL, 10);
+	long val;
+	int err;
 	long hyst;
 
+	err = kstrtol(buf, 10, &val);
+	if (err)
+		return err;
+
 	mutex_lock(&data->update_lock);
-	hyst = TEMP8_FROM_REG(data->temp8[2]) + data->temp2_offset - val;
+	hyst = temp8_from_reg(data, 2) + data->temp2_offset - val;
 	i2c_smbus_write_byte_data(client, LM63_REG_REMOTE_TCRIT_HYST,
 				  HYST_TO_REG(hyst));
 	mutex_unlock(&data->update_lock);
 	return count;
 }
 
+/*
+ * Set conversion rate.
+ * client->update_lock must be held when calling this function.
+ */
+static void lm63_set_convrate(struct i2c_client *client, struct lm63_data *data,
+			      unsigned int interval)
+{
+	int i;
+	unsigned int update_interval;
+
+	/* Shift calculations to avoid rounding errors */
+	interval <<= 6;
+
+	/* find the nearest update rate */
+	update_interval = (1 << (LM63_MAX_CONVRATE + 6)) * 1000
+	  / data->max_convrate_hz;
+	for (i = 0; i < LM63_MAX_CONVRATE; i++, update_interval >>= 1)
+		if (interval >= update_interval * 3 / 4)
+			break;
+
+	i2c_smbus_write_byte_data(client, LM63_REG_CONVRATE, i);
+	data->update_interval = UPDATE_INTERVAL(data->max_convrate_hz, i);
+}
+
+static ssize_t show_update_interval(struct device *dev,
+				    struct device_attribute *attr, char *buf)
+{
+	struct lm63_data *data = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%u\n", data->update_interval);
+}
+
+static ssize_t set_update_interval(struct device *dev,
+				   struct device_attribute *attr,
+				   const char *buf, size_t count)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct lm63_data *data = i2c_get_clientdata(client);
+	unsigned long val;
+	int err;
+
+	err = kstrtoul(buf, 10, &val);
+	if (err)
+		return err;
+
+	mutex_lock(&data->update_lock);
+	lm63_set_convrate(client, data, SENSORS_LIMIT(val, 0, 100000));
+	mutex_unlock(&data->update_lock);
+
+	return count;
+}
+
+static ssize_t show_type(struct device *dev, struct device_attribute *attr,
+			 char *buf)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct lm63_data *data = i2c_get_clientdata(client);
+
+	return sprintf(buf, data->trutherm ? "1\n" : "2\n");
+}
+
+static ssize_t set_type(struct device *dev, struct device_attribute *attr,
+			const char *buf, size_t count)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct lm63_data *data = i2c_get_clientdata(client);
+	unsigned long val;
+	int ret;
+	u8 reg;
+
+	ret = kstrtoul(buf, 10, &val);
+	if (ret < 0)
+		return ret;
+	if (val != 1 && val != 2)
+		return -EINVAL;
+
+	mutex_lock(&data->update_lock);
+	data->trutherm = val == 1;
+	reg = i2c_smbus_read_byte_data(client, LM96163_REG_TRUTHERM) & ~0x02;
+	i2c_smbus_write_byte_data(client, LM96163_REG_TRUTHERM,
+				  reg | (data->trutherm ? 0x02 : 0x00));
+	data->valid = 0;
+	mutex_unlock(&data->update_lock);
+
+	return count;
+}
+
 static ssize_t show_alarms(struct device *dev, struct device_attribute *dummy,
 			   char *buf)
 {
@@ -377,27 +612,87 @@
 static SENSOR_DEVICE_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan,
 	set_fan, 1);
 
-static DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm1, set_pwm1);
+static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm1, set_pwm1, 0);
 static DEVICE_ATTR(pwm1_enable, S_IRUGO, show_pwm1_enable, NULL);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point1_pwm, S_IRUGO, show_pwm1, NULL, 1);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point1_temp, S_IRUGO,
+	show_lut_temp, NULL, 3);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point1_temp_hyst, S_IRUGO,
+	show_lut_temp_hyst, NULL, 3);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point2_pwm, S_IRUGO, show_pwm1, NULL, 2);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point2_temp, S_IRUGO,
+	show_lut_temp, NULL, 4);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point2_temp_hyst, S_IRUGO,
+	show_lut_temp_hyst, NULL, 4);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point3_pwm, S_IRUGO, show_pwm1, NULL, 3);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point3_temp, S_IRUGO,
+	show_lut_temp, NULL, 5);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point3_temp_hyst, S_IRUGO,
+	show_lut_temp_hyst, NULL, 5);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point4_pwm, S_IRUGO, show_pwm1, NULL, 4);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point4_temp, S_IRUGO,
+	show_lut_temp, NULL, 6);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point4_temp_hyst, S_IRUGO,
+	show_lut_temp_hyst, NULL, 6);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point5_pwm, S_IRUGO, show_pwm1, NULL, 5);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point5_temp, S_IRUGO,
+	show_lut_temp, NULL, 7);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point5_temp_hyst, S_IRUGO,
+	show_lut_temp_hyst, NULL, 7);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point6_pwm, S_IRUGO, show_pwm1, NULL, 6);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point6_temp, S_IRUGO,
+	show_lut_temp, NULL, 8);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point6_temp_hyst, S_IRUGO,
+	show_lut_temp_hyst, NULL, 8);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point7_pwm, S_IRUGO, show_pwm1, NULL, 7);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point7_temp, S_IRUGO,
+	show_lut_temp, NULL, 9);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point7_temp_hyst, S_IRUGO,
+	show_lut_temp_hyst, NULL, 9);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point8_pwm, S_IRUGO, show_pwm1, NULL, 8);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point8_temp, S_IRUGO,
+	show_lut_temp, NULL, 10);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point8_temp_hyst, S_IRUGO,
+	show_lut_temp_hyst, NULL, 10);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point9_pwm, S_IRUGO, show_pwm1, NULL, 9);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point9_temp, S_IRUGO,
+	show_lut_temp, NULL, 11);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point9_temp_hyst, S_IRUGO,
+	show_lut_temp_hyst, NULL, 11);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point10_pwm, S_IRUGO, show_pwm1, NULL, 10);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point10_temp, S_IRUGO,
+	show_lut_temp, NULL, 12);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point10_temp_hyst, S_IRUGO,
+	show_lut_temp_hyst, NULL, 12);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point11_pwm, S_IRUGO, show_pwm1, NULL, 11);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point11_temp, S_IRUGO,
+	show_lut_temp, NULL, 13);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point11_temp_hyst, S_IRUGO,
+	show_lut_temp_hyst, NULL, 13);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point12_pwm, S_IRUGO, show_pwm1, NULL, 12);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point12_temp, S_IRUGO,
+	show_lut_temp, NULL, 14);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point12_temp_hyst, S_IRUGO,
+	show_lut_temp_hyst, NULL, 14);
 
 static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_local_temp8, NULL, 0);
 static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_local_temp8,
-	set_local_temp8, 1);
+	set_temp8, 1);
 
 static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp11, NULL, 0);
 static SENSOR_DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_temp11,
 	set_temp11, 1);
 static SENSOR_DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_temp11,
 	set_temp11, 2);
-/*
- * On LM63, temp2_crit can be set only once, which should be job
- * of the bootloader.
- */
+static SENSOR_DEVICE_ATTR(temp2_offset, S_IWUSR | S_IRUGO, show_temp11,
+	set_temp11, 3);
 static SENSOR_DEVICE_ATTR(temp2_crit, S_IRUGO, show_remote_temp8,
-	NULL, 2);
+	set_temp8, 2);
 static DEVICE_ATTR(temp2_crit_hyst, S_IWUSR | S_IRUGO, show_temp2_crit_hyst,
 	set_temp2_crit_hyst);
 
+static DEVICE_ATTR(temp2_type, S_IWUSR | S_IRUGO, show_type, set_type);
+
 /* Individual alarm files */
 static SENSOR_DEVICE_ATTR(fan1_min_alarm, S_IRUGO, show_alarm, NULL, 0);
 static SENSOR_DEVICE_ATTR(temp2_crit_alarm, S_IRUGO, show_alarm, NULL, 1);
@@ -408,14 +703,43 @@
 /* Raw alarm file for compatibility */
 static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
 
+static DEVICE_ATTR(update_interval, S_IRUGO | S_IWUSR, show_update_interval,
+		   set_update_interval);
+
 static struct attribute *lm63_attributes[] = {
-	&dev_attr_pwm1.attr,
+	&sensor_dev_attr_pwm1.dev_attr.attr,
 	&dev_attr_pwm1_enable.attr,
+	&sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr,
+	&sensor_dev_attr_pwm1_auto_point1_temp.dev_attr.attr,
+	&sensor_dev_attr_pwm1_auto_point1_temp_hyst.dev_attr.attr,
+	&sensor_dev_attr_pwm1_auto_point2_pwm.dev_attr.attr,
+	&sensor_dev_attr_pwm1_auto_point2_temp.dev_attr.attr,
+	&sensor_dev_attr_pwm1_auto_point2_temp_hyst.dev_attr.attr,
+	&sensor_dev_attr_pwm1_auto_point3_pwm.dev_attr.attr,
+	&sensor_dev_attr_pwm1_auto_point3_temp.dev_attr.attr,
+	&sensor_dev_attr_pwm1_auto_point3_temp_hyst.dev_attr.attr,
+	&sensor_dev_attr_pwm1_auto_point4_pwm.dev_attr.attr,
+	&sensor_dev_attr_pwm1_auto_point4_temp.dev_attr.attr,
+	&sensor_dev_attr_pwm1_auto_point4_temp_hyst.dev_attr.attr,
+	&sensor_dev_attr_pwm1_auto_point5_pwm.dev_attr.attr,
+	&sensor_dev_attr_pwm1_auto_point5_temp.dev_attr.attr,
+	&sensor_dev_attr_pwm1_auto_point5_temp_hyst.dev_attr.attr,
+	&sensor_dev_attr_pwm1_auto_point6_pwm.dev_attr.attr,
+	&sensor_dev_attr_pwm1_auto_point6_temp.dev_attr.attr,
+	&sensor_dev_attr_pwm1_auto_point6_temp_hyst.dev_attr.attr,
+	&sensor_dev_attr_pwm1_auto_point7_pwm.dev_attr.attr,
+	&sensor_dev_attr_pwm1_auto_point7_temp.dev_attr.attr,
+	&sensor_dev_attr_pwm1_auto_point7_temp_hyst.dev_attr.attr,
+	&sensor_dev_attr_pwm1_auto_point8_pwm.dev_attr.attr,
+	&sensor_dev_attr_pwm1_auto_point8_temp.dev_attr.attr,
+	&sensor_dev_attr_pwm1_auto_point8_temp_hyst.dev_attr.attr,
+
 	&sensor_dev_attr_temp1_input.dev_attr.attr,
 	&sensor_dev_attr_temp2_input.dev_attr.attr,
 	&sensor_dev_attr_temp2_min.dev_attr.attr,
 	&sensor_dev_attr_temp1_max.dev_attr.attr,
 	&sensor_dev_attr_temp2_max.dev_attr.attr,
+	&sensor_dev_attr_temp2_offset.dev_attr.attr,
 	&sensor_dev_attr_temp2_crit.dev_attr.attr,
 	&dev_attr_temp2_crit_hyst.attr,
 
@@ -425,10 +749,54 @@
 	&sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
 	&sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
 	&dev_attr_alarms.attr,
+	&dev_attr_update_interval.attr,
 	NULL
 };
 
+static struct attribute *lm63_attributes_extra_lut[] = {
+	&sensor_dev_attr_pwm1_auto_point9_pwm.dev_attr.attr,
+	&sensor_dev_attr_pwm1_auto_point9_temp.dev_attr.attr,
+	&sensor_dev_attr_pwm1_auto_point9_temp_hyst.dev_attr.attr,
+	&sensor_dev_attr_pwm1_auto_point10_pwm.dev_attr.attr,
+	&sensor_dev_attr_pwm1_auto_point10_temp.dev_attr.attr,
+	&sensor_dev_attr_pwm1_auto_point10_temp_hyst.dev_attr.attr,
+	&sensor_dev_attr_pwm1_auto_point11_pwm.dev_attr.attr,
+	&sensor_dev_attr_pwm1_auto_point11_temp.dev_attr.attr,
+	&sensor_dev_attr_pwm1_auto_point11_temp_hyst.dev_attr.attr,
+	&sensor_dev_attr_pwm1_auto_point12_pwm.dev_attr.attr,
+	&sensor_dev_attr_pwm1_auto_point12_temp.dev_attr.attr,
+	&sensor_dev_attr_pwm1_auto_point12_temp_hyst.dev_attr.attr,
+	NULL
+};
+
+static const struct attribute_group lm63_group_extra_lut = {
+	.attrs = lm63_attributes_extra_lut,
+};
+
+/*
+ * On LM63, temp2_crit can be set only once, which should be job
+ * of the bootloader.
+ * On LM64, temp2_crit can always be set.
+ * On LM96163, temp2_crit can be set if bit 1 of the configuration
+ * register is true.
+ */
+static umode_t lm63_attribute_mode(struct kobject *kobj,
+				   struct attribute *attr, int index)
+{
+	struct device *dev = container_of(kobj, struct device, kobj);
+	struct i2c_client *client = to_i2c_client(dev);
+	struct lm63_data *data = i2c_get_clientdata(client);
+
+	if (attr == &sensor_dev_attr_temp2_crit.dev_attr.attr
+	    && (data->kind == lm64 ||
+		(data->kind == lm96163 && (data->config & 0x02))))
+		return attr->mode | S_IWUSR;
+
+	return attr->mode;
+}
+
 static const struct attribute_group lm63_group = {
+	.is_visible = lm63_attribute_mode,
 	.attrs = lm63_attributes,
 };
 
@@ -487,6 +855,8 @@
 		strlcpy(info->type, "lm63", I2C_NAME_SIZE);
 	else if (chip_id == 0x51 && (address == 0x18 || address == 0x4e))
 		strlcpy(info->type, "lm64", I2C_NAME_SIZE);
+	else if (chip_id == 0x49 && address == 0x4c)
+		strlcpy(info->type, "lm96163", I2C_NAME_SIZE);
 	else
 		return -ENODEV;
 
@@ -518,12 +888,24 @@
 	lm63_init_client(new_client);
 
 	/* Register sysfs hooks */
-	if ((err = sysfs_create_group(&new_client->dev.kobj,
-				      &lm63_group)))
+	err = sysfs_create_group(&new_client->dev.kobj, &lm63_group);
+	if (err)
 		goto exit_free;
 	if (data->config & 0x04) { /* tachometer enabled */
-		if ((err = sysfs_create_group(&new_client->dev.kobj,
-					      &lm63_group_fan1)))
+		err = sysfs_create_group(&new_client->dev.kobj,
+					 &lm63_group_fan1);
+		if (err)
+			goto exit_remove_files;
+	}
+	if (data->kind == lm96163) {
+		err = device_create_file(&new_client->dev,
+					 &dev_attr_temp2_type);
+		if (err)
+			goto exit_remove_files;
+
+		err = sysfs_create_group(&new_client->dev.kobj,
+					 &lm63_group_extra_lut);
+		if (err)
 			goto exit_remove_files;
 	}
 
@@ -538,17 +920,25 @@
 exit_remove_files:
 	sysfs_remove_group(&new_client->dev.kobj, &lm63_group);
 	sysfs_remove_group(&new_client->dev.kobj, &lm63_group_fan1);
+	if (data->kind == lm96163) {
+		device_remove_file(&new_client->dev, &dev_attr_temp2_type);
+		sysfs_remove_group(&new_client->dev.kobj,
+				   &lm63_group_extra_lut);
+	}
 exit_free:
 	kfree(data);
 exit:
 	return err;
 }
 
-/* Idealy we shouldn't have to initialize anything, since the BIOS
-   should have taken care of everything */
+/*
+ * Ideally we shouldn't have to initialize anything, since the BIOS
+ * should have taken care of everything
+ */
 static void lm63_init_client(struct i2c_client *client)
 {
 	struct lm63_data *data = i2c_get_clientdata(client);
+	u8 convrate;
 
 	data->config = i2c_smbus_read_byte_data(client, LM63_REG_CONFIG1);
 	data->config_fan = i2c_smbus_read_byte_data(client,
@@ -561,16 +951,57 @@
 		i2c_smbus_write_byte_data(client, LM63_REG_CONFIG1,
 					  data->config);
 	}
+	/* Tachometer is always enabled on LM64 */
+	if (data->kind == lm64)
+		data->config |= 0x04;
 
 	/* We may need pwm1_freq before ever updating the client data */
 	data->pwm1_freq = i2c_smbus_read_byte_data(client, LM63_REG_PWM_FREQ);
 	if (data->pwm1_freq == 0)
 		data->pwm1_freq = 1;
 
+	switch (data->kind) {
+	case lm63:
+	case lm64:
+		data->max_convrate_hz = LM63_MAX_CONVRATE_HZ;
+		data->lut_size = 8;
+		break;
+	case lm96163:
+		data->max_convrate_hz = LM96163_MAX_CONVRATE_HZ;
+		data->lut_size = 12;
+		data->trutherm
+		  = i2c_smbus_read_byte_data(client,
+					     LM96163_REG_TRUTHERM) & 0x02;
+		break;
+	}
+	convrate = i2c_smbus_read_byte_data(client, LM63_REG_CONVRATE);
+	if (unlikely(convrate > LM63_MAX_CONVRATE))
+		convrate = LM63_MAX_CONVRATE;
+	data->update_interval = UPDATE_INTERVAL(data->max_convrate_hz,
+						convrate);
+
+	/*
+	 * For LM96163, check if high resolution PWM
+	 * and unsigned temperature format is enabled.
+	 */
+	if (data->kind == lm96163) {
+		u8 config_enhanced
+		  = i2c_smbus_read_byte_data(client,
+					     LM96163_REG_CONFIG_ENHANCED);
+		if (config_enhanced & 0x20)
+			data->lut_temp_highres = true;
+		if ((config_enhanced & 0x10)
+		    && !(data->config_fan & 0x08) && data->pwm1_freq == 8)
+			data->pwm_highres = true;
+		if (config_enhanced & 0x08)
+			data->remote_unsigned = true;
+	}
+
 	/* Show some debug info about the LM63 configuration */
-	dev_dbg(&client->dev, "Alert/tach pin configured for %s\n",
-		(data->config & 0x04) ? "tachometer input" :
-		"alert output");
+	if (data->kind == lm63)
+		dev_dbg(&client->dev, "Alert/tach pin configured for %s\n",
+			(data->config & 0x04) ? "tachometer input" :
+			"alert output");
 	dev_dbg(&client->dev, "PWM clock %s kHz, output frequency %u Hz\n",
 		(data->config_fan & 0x08) ? "1.4" : "360",
 		((data->config_fan & 0x08) ? 700 : 180000) / data->pwm1_freq);
@@ -586,6 +1017,10 @@
 	hwmon_device_unregister(data->hwmon_dev);
 	sysfs_remove_group(&client->dev.kobj, &lm63_group);
 	sysfs_remove_group(&client->dev.kobj, &lm63_group_fan1);
+	if (data->kind == lm96163) {
+		device_remove_file(&client->dev, &dev_attr_temp2_type);
+		sysfs_remove_group(&client->dev.kobj, &lm63_group_extra_lut);
+	}
 
 	kfree(data);
 	return 0;
@@ -595,10 +1030,15 @@
 {
 	struct i2c_client *client = to_i2c_client(dev);
 	struct lm63_data *data = i2c_get_clientdata(client);
+	unsigned long next_update;
+	int i;
 
 	mutex_lock(&data->update_lock);
 
-	if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
+	next_update = data->last_updated
+	  + msecs_to_jiffies(data->update_interval) + 1;
+
+	if (time_after(jiffies, next_update) || !data->valid) {
 		if (data->config & 0x04) { /* tachometer enabled  */
 			/* order matters for fan1_input */
 			data->fan[0] = i2c_smbus_read_byte_data(client,
@@ -615,8 +1055,8 @@
 				  LM63_REG_PWM_FREQ);
 		if (data->pwm1_freq == 0)
 			data->pwm1_freq = 1;
-		data->pwm1_value = i2c_smbus_read_byte_data(client,
-				   LM63_REG_PWM_VALUE);
+		data->pwm1[0] = i2c_smbus_read_byte_data(client,
+				LM63_REG_PWM_VALUE);
 
 		data->temp8[0] = i2c_smbus_read_byte_data(client,
 				 LM63_REG_LOCAL_TEMP);
@@ -636,6 +1076,17 @@
 				  LM63_REG_REMOTE_HIGH_MSB) << 8)
 				| i2c_smbus_read_byte_data(client,
 				  LM63_REG_REMOTE_HIGH_LSB);
+		data->temp11[3] = (i2c_smbus_read_byte_data(client,
+				  LM63_REG_REMOTE_OFFSET_MSB) << 8)
+				| i2c_smbus_read_byte_data(client,
+				  LM63_REG_REMOTE_OFFSET_LSB);
+
+		if (data->kind == lm96163)
+			data->temp11u = (i2c_smbus_read_byte_data(client,
+					LM96163_REG_REMOTE_TEMP_U_MSB) << 8)
+				      | i2c_smbus_read_byte_data(client,
+					LM96163_REG_REMOTE_TEMP_U_LSB);
+
 		data->temp8[2] = i2c_smbus_read_byte_data(client,
 				 LM63_REG_REMOTE_TCRIT);
 		data->temp2_crit_hyst = i2c_smbus_read_byte_data(client,
@@ -648,6 +1099,21 @@
 		data->valid = 1;
 	}
 
+	if (time_after(jiffies, data->lut_last_updated + 5 * HZ) ||
+	    !data->lut_valid) {
+		for (i = 0; i < data->lut_size; i++) {
+			data->pwm1[1 + i] = i2c_smbus_read_byte_data(client,
+					    LM63_REG_LUT_PWM(i));
+			data->temp8[3 + i] = i2c_smbus_read_byte_data(client,
+					     LM63_REG_LUT_TEMP(i));
+		}
+		data->lut_temp_hyst = i2c_smbus_read_byte_data(client,
+				      LM63_REG_LUT_TEMP_HYST);
+
+		data->lut_last_updated = jiffies;
+		data->lut_valid = 1;
+	}
+
 	mutex_unlock(&data->update_lock);
 
 	return data;
diff --git a/drivers/i2c/busses/i2c-comcerto.c b/drivers/i2c/busses/i2c-comcerto.c
index c2c8649..4611ba8 100644
--- a/drivers/i2c/busses/i2c-comcerto.c
+++ b/drivers/i2c/busses/i2c-comcerto.c
@@ -45,7 +45,7 @@
 module_param(force_poll, bool, S_IRUGO);
 MODULE_PARM_DESC(force_poll, "Force polling mode: 0=interrupt mode, polling mode otherwise");
 
-static int speed = 0;
+static int speed = 1;
 module_param(speed, int, S_IRUGO);
 MODULE_PARM_DESC(speed, "I2C speed: 0=standard, 1=fast, 2=high speed");
 
@@ -632,7 +632,7 @@
 	else
 		i2c->irq = -1;
 
-	if (i2c_add_adapter(&comcerto_i2c_adapter) != 0) {
+	if (i2c_add_numbered_adapter(&comcerto_i2c_adapter) != 0) {
 		dev_err(i2c->dev, "%s: failed to add I2C adapter\n", __FUNCTION__);
 		goto err3;
 	}
diff --git a/drivers/mtd/cmdlinepart.c b/drivers/mtd/cmdlinepart.c
index ddf9ec6..37c2449 100644
--- a/drivers/mtd/cmdlinepart.c
+++ b/drivers/mtd/cmdlinepart.c
@@ -155,7 +155,13 @@
         /* test for options */
         if (strncmp(s, "ro", 2) == 0)
 	{
+#if 0
+		/* It turns out that if you have a bootloader which marks
+		 * its partitions read-only, and you don't want it to do so,
+		 * you have trouble updating it BECAUSE IT IS READONLY.
+		 * <sob>. */
 		mask_flags |= MTD_WRITEABLE;
+#endif
 		s += 2;
         }
 
diff --git a/drivers/mtd/nand/comcerto_nand.c b/drivers/mtd/nand/comcerto_nand.c
index c23439a..3f19fa8 100644
--- a/drivers/mtd/nand/comcerto_nand.c
+++ b/drivers/mtd/nand/comcerto_nand.c
@@ -23,6 +23,7 @@
 #include <linux/mtd/partitions.h>
 #include <asm/io.h>
 #include <linux/delay.h>
+#include <linux/ratelimit.h>
 #include <linux/platform_device.h>
 #include <mach/ecc.h>
 
@@ -323,13 +324,13 @@
 
 	if (!((readl_relaxed(ecc_base_addr + ECC_CORR_DONE_STAT)) & ECC_DONE)) {
 		temp_nand_ecc_errors[0] += 1 ;
-		printk(KERN_WARNING "ECC: uncorrectable error 1 !!!\n");
+		printk_ratelimited(KERN_WARNING "ECC: uncorrectable error 1 !!!\n");
 		return -1;
 	}
 
 	/* Check if the block has uncorrectable number of errors */
 	if ((readl_relaxed(ecc_base_addr + ECC_CORR_STAT)) & ECC_UNCORR) {
-		printk(KERN_WARNING "ECC: uncorrectable error  2 !!!\n");
+		printk_ratelimited(KERN_WARNING "ECC: uncorrectable error  2 !!!\n");
 		temp_nand_ecc_errors[1] += 1 ;
 		return -EIO;
 	}
@@ -582,6 +583,7 @@
 		nand_device->ecc.read_page = comcerto_nand_read_page_hwecc;
 		nand_device->ecc.calculate = comcerto_calculate_ecc;
 		nand_device->ecc.correct = comcerto_correct_ecc;
+		printk("hw_syndrome correction %d.\n", mtd->writesize);
 
 		switch (mtd->writesize) {
 		case 512:
@@ -651,13 +653,16 @@
 	nand_device->ecc.total = nand_device->ecc.steps * nand_device->ecc.bytes;
 
 
+#if 0
 	nand_device->bbt_td = &bbt_main_descr;
 	nand_device->bbt_md = &bbt_mirror_descr;
+#endif
 	nand_device->badblock_pattern = &c2000_badblock_pattern;
 	nand_device->bbt_options |= NAND_BBT_USE_FLASH;
 
 	} else {
-		nand_device->ecc.mode = NAND_ECC_SOFT;
+		printk("using soft ecc.\n");
+		nand_device->ecc.mode = NAND_ECC_SOFT_BCH;
 	}
 
 #endif
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index ed4d76d..32de310 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -39,6 +39,7 @@
 #include <linux/sched.h>
 #include <linux/slab.h>
 #include <linux/types.h>
+#include <linux/ratelimit.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/nand.h>
 #include <linux/mtd/nand_ecc.h>
@@ -1420,6 +1421,49 @@
 	return NULL;
 }
 
+/*
+ * NOTE(apenwarr): Newer kernels do this much better.
+ *  Among other things, they report a max_flips value that's the largest
+ *  number of flips in any 1024-byte ECC calculation, as opposed to the total
+ *  flips in the whole 4096-byte page.  The latter is dangerous because
+ *  you could see 24 flips in a single 1024-byte region, which is the edge
+ *  of disaster, even though it's only 1/4 of the maximum 96 flips we could
+ *  handle if averaged across 4 pages.  So where we'd like to set a threshold
+ *  per 1024-byte region, we instead have to set a threshold per
+ *  4096-byte region that *still* must be well under 24.
+ *
+ *  Anyway, this code can go away someday when we use a newer kernel.
+ */
+static int unclean_if_too_many_flips(struct mtd_info *mtd,
+		struct mtd_ecc_stats *stats) {
+	uint32_t flips = mtd->ecc_stats.corrected - stats->corrected;
+	uint32_t threshold;
+	switch (mtd->oobsize) {
+	case 8:
+	case 16:
+	case 64:
+		threshold = 0;
+		break;
+	case 128:
+		threshold = 4;
+		break;
+	case 224:
+		threshold = 18;
+		break;
+	default:
+		threshold = 0;
+		break;
+	}
+	if (flips > threshold / 2) {
+		// This should be very rare, bu we want to know as we
+		// approach our threshold, which should be even more rare.
+		printk_ratelimited(KERN_WARNING
+			"ECC: corrected %d bits (threshold=%d)\n",
+			flips, threshold);
+	}
+	return  flips > threshold ? -EUCLEAN : 0;
+}
+
 /**
  * nand_do_read_ops - [INTERN] Read data with ECC
  * @mtd: MTD device structure
@@ -1566,7 +1610,7 @@
 	if (mtd->ecc_stats.failed - stats.failed)
 		return -EBADMSG;
 
-	return  mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0;
+	return unclean_if_too_many_flips(mtd, &stats);
 }
 
 /**
@@ -1848,7 +1892,7 @@
 	if (mtd->ecc_stats.failed - stats.failed)
 		return -EBADMSG;
 
-	return  mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0;
+	return unclean_if_too_many_flips(mtd, &stats);
 }
 
 /**
@@ -2593,8 +2637,13 @@
 					chip->page_shift, 0, allowbbt)) {
 			pr_warn("%s: attempt to erase a bad block at page 0x%08x\n",
 				    __func__, page);
+#if 0
+			// NOTE(apenwarr): for us it's ok to erase bad blocks.
+			//  UBI deals with bad blocks a different way.
+			//  And we don't trust our bad block detection logic.
 			instr->state = MTD_ERASE_FAILED;
 			goto erase_exit;
+#endif
 		}
 
 		/*
diff --git a/drivers/mtd/nand/nand_bch.c b/drivers/mtd/nand/nand_bch.c
index 3803e0b..8202379 100644
--- a/drivers/mtd/nand/nand_bch.c
+++ b/drivers/mtd/nand/nand_bch.c
@@ -28,6 +28,7 @@
 #include <linux/mtd/nand.h>
 #include <linux/mtd/nand_bch.h>
 #include <linux/bch.h>
+#include <linux/ratelimit.h>
 
 /**
  * struct nand_bch_control - private NAND BCH control structure
@@ -97,7 +98,7 @@
 					errloc[i]);
 		}
 	} else if (count < 0) {
-		printk(KERN_ERR "ecc unrecoverable error\n");
+		printk_ratelimited(KERN_ERR "ecc unrecoverable error\n");
 		count = -1;
 	}
 	return count;
diff --git a/drivers/net/phy/atheros.c b/drivers/net/phy/atheros.c
index ef238ca..d44ff7b 100644
--- a/drivers/net/phy/atheros.c
+++ b/drivers/net/phy/atheros.c
@@ -35,6 +35,7 @@
 #define PHY_ID_AR8035			0x004dd072
 #define PHY_ID_AR8033			0x004dd074
 #define PHY_ID_AR8327			0x004dd033
+#define PHY_ID_AR8337			0x004dd034
 
 MODULE_DESCRIPTION("Atheros PHY driver");
 MODULE_LICENSE("GPL");
@@ -152,6 +153,25 @@
 	.driver 	= { .owner = THIS_MODULE,},
 };
 
+/* Atheros Ar8337 */
+static struct phy_driver ar8337_driver = {
+	.phy_id		= PHY_ID_AR8337,
+	.name		= "Atheros AR8337",
+	.phy_id_mask	= 0xffffff00,
+	.features	= PHY_GBIT_FEATURES,
+	.flags		= PHY_HAS_INTERRUPT,
+	.config_init	= &ar8x_config_init,
+	.config_aneg	= &genphy_config_aneg,
+	.read_status	= &genphy_read_status,
+	.ack_interrupt	= &ar8x_ack_interrupt,
+	.config_intr	= &ar8x_config_intr,
+#ifdef CONFIG_PM
+	.suspend	= &genphy_suspend,
+	.resume		= &genphy_resume,
+#endif
+	.driver 	= { .owner = THIS_MODULE,},
+};
+
 static int __init ar8x_init(void)
 {
 	int err;
@@ -164,6 +184,12 @@
 	if (err < 0)
 		phy_driver_unregister(&ar8035_driver);
 
+	err = phy_driver_register(&ar8337_driver);
+	if (err < 0) {
+		phy_driver_unregister(&ar8035_driver);
+		phy_driver_unregister(&ar8327_driver);
+	}
+
 	return err;
 }
 
@@ -171,6 +197,7 @@
 {
 	phy_driver_unregister(&ar8035_driver);
 	phy_driver_unregister(&ar8327_driver);
+	phy_driver_unregister(&ar8337_driver);
 }
 
 module_init(ar8x_init);
@@ -179,6 +206,7 @@
 static struct mdio_device_id __maybe_unused atheros_tbl[] = {
 	{ PHY_ID_AR8035, 0xffffff00 },
 	{ PHY_ID_AR8327, 0xffffff00 },
+	{ PHY_ID_AR8337, 0xffffff00 },
 	{ }
 };
 
diff --git a/drivers/spi/designware_spi.c b/drivers/spi/designware_spi.c
index 5e2046b..4211ede 100644
--- a/drivers/spi/designware_spi.c
+++ b/drivers/spi/designware_spi.c
@@ -129,7 +129,9 @@
 
 static void dwspi_baudcfg(struct designware_spi *dwspi, u32 speed_hz)
 {
-	u16 div = (speed_hz) ? dwspi->ssi_clk/speed_hz : 0xffff;
+	/* Divisor must be an even number */
+	u16 div = (speed_hz) ? DIV_ROUND_UP(dwspi->ssi_clk, speed_hz) + 1 &
+		0xfffe : 0xffff;
 
 	writew(div, dwspi->regs + DWSPI_BAUDR);
 }
@@ -434,7 +436,7 @@
 	/* This delay should be good enough for 100KHz spi transfers. Slower
 	 * transfers may need a longer delay.
 	 */
-	udelay(10);
+	//udelay(10);
 
 	/* get remaining rx bytes */
     do {
diff --git a/drivers/watchdog/comcerto_wdt.c b/drivers/watchdog/comcerto_wdt.c
index d89aeb2..da497c0 100644
--- a/drivers/watchdog/comcerto_wdt.c
+++ b/drivers/watchdog/comcerto_wdt.c
@@ -29,6 +29,7 @@
 #include <linux/types.h>
 #include <linux/watchdog.h>
 #include <linux/clk.h>
+#include <linux/timer.h>
 #include <asm/bitops.h>
 #include <asm/uaccess.h>
 #include <asm/io.h>
@@ -37,25 +38,38 @@
 #include <mach/reset.h>
 
 #define WDT_NAME					"comcerto_wdt"
+
+/* these are the actual wdt limits */
 #define WDT_DEFAULT_TIMEOUT				5
 #define WDT_MAX_TIMEOUT					(0xffffffff / COMCERTO_AHBCLK)
 
+/* these are for the virtual wdt */
+#define WDT_DEFAULT_TIME				70	/* seconds */
+#define WDT_MAX_TIME					255	/* seconds */
+
 static unsigned long COMCERTO_AHBCLK;
 static int wd_heartbeat = WDT_DEFAULT_TIMEOUT;
+static int wd_time = WDT_DEFAULT_TIME;
 static int nowayout = WATCHDOG_NOWAYOUT;
 static struct clk *clk_axi;
 
 module_param(wd_heartbeat, int, 0);
 MODULE_PARM_DESC(wd_heartbeat, "Watchdog heartbeat in seconds. (default="__MODULE_STRING(WDT_DEFAULT_TIMEOUT) ")");
 
+module_param(wd_time, int, 0);
+MODULE_PARM_DESC(wd_time, "Watchdog time in seconds. (default="__MODULE_STRING(WDT_DEFAULT_TIME) ")");
+
 #ifdef CONFIG_WATCHDOG_NOWAYOUT
 module_param(nowayout, int, 0);
 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
 #endif
 
+static struct timer_list wdt_timer;
+
 static unsigned long comcerto_wdt_busy;
 static char expect_close;
 static spinlock_t wdt_lock;
+static atomic_t ticks;
 
 /*
  * Inform whether the boot was caused by AXI watchdog or not.
@@ -95,12 +109,46 @@
 /*
  * Write wd_heartbeat to high bound register.
  */
-static void comcerto_wdt_set_timeout(void)
+static void comcerto_wdt_pet_watchdog_physical(void)
 {
 	__raw_writel(wd_heartbeat * COMCERTO_AHBCLK, COMCERTO_TIMER_WDT_HIGH_BOUND);
 }
 
 /*
+ * reset virtual wdt timer
+ */
+static void comcerto_wdt_pet_watchdog_virtual(void)
+{
+	atomic_set(&ticks, wd_time);
+}
+
+/*
+ * set virtual wd timeout reset value
+ */
+static int comcerto_wdt_settimeout(int new_time)
+{
+	if ((new_time <= 0) || (new_time > WDT_MAX_TIME))
+		return -EINVAL;
+
+	wd_time = new_time;
+	return 0;
+}
+
+/*
+ * implement virtual wdt on physical wdt with timer
+ */
+static void comcerto_timer_tick(unsigned long unused)
+{
+	if (!atomic_dec_and_test(&ticks)) {
+		comcerto_wdt_pet_watchdog_physical();
+		mod_timer(&wdt_timer, jiffies + HZ);
+		//printk(KERN_CRIT WDT_NAME ": Watchdog will fire in %d secs\n", atomic_read(&ticks));
+	} else {
+		printk(KERN_CRIT WDT_NAME ": Watchdog will fire soon!!!\n");
+	}
+}
+
+/*
  * Disable the watchdog.
  */
 static void comcerto_wdt_stop(void)
@@ -110,13 +158,15 @@
 
 	spin_lock_irqsave(&wdt_lock, flags);
 
+	del_timer(&wdt_timer);
+
 	wdt_control = __raw_readl(COMCERTO_TIMER_WDT_CONTROL);
 
 	__raw_writel(wdt_control & ~COMCERTO_TIMER_WDT_CONTROL_TIMER_ENABLE, COMCERTO_TIMER_WDT_CONTROL);
 
 	spin_unlock_irqrestore(&wdt_lock, flags);
 
-	comcerto_wdt_set_timeout();
+	comcerto_wdt_pet_watchdog_physical();
 }
 
 /*
@@ -135,6 +185,8 @@
 
 	comcerto_rst_cntrl_set(AXI_WD_RST_EN);
 
+	mod_timer(&wdt_timer, jiffies + HZ);
+
 	spin_unlock_irqrestore(&wdt_lock, flags);
 }
 
@@ -150,7 +202,7 @@
 {
 	comcerto_wdt_stop();
 
-	__raw_writel(~0, COMCERTO_TIMER_WDT_HIGH_BOUND);			/* write max timout */
+	__raw_writel(~0, COMCERTO_TIMER_WDT_HIGH_BOUND);			/* write max timeout */
 }
 
 /*
@@ -161,7 +213,8 @@
 	if (test_and_set_bit(0, &comcerto_wdt_busy))
 		return -EBUSY;
 
-	comcerto_wdt_set_timeout();
+	comcerto_wdt_pet_watchdog_virtual();
+	comcerto_wdt_pet_watchdog_physical();
 	comcerto_wdt_start();
 
 	return nonseekable_open(inode, file);
@@ -204,7 +257,7 @@
 
 	switch(cmd) {
 	case WDIOC_KEEPALIVE:
-		comcerto_wdt_set_timeout();
+		comcerto_wdt_pet_watchdog_virtual();
 		break;
 
 	case WDIOC_GETSUPPORT:
@@ -221,18 +274,18 @@
 			goto err;
 		}
 
-		if (comcerto_wdt_set_heartbeat(new_value)) {
+		if (comcerto_wdt_settimeout(new_value)) {
 			err = -EINVAL;
 			goto err;
 		}
 
-		comcerto_wdt_set_timeout();
+		comcerto_wdt_pet_watchdog_virtual();
 
-		return put_user(wd_heartbeat, p);
+		return put_user(wd_time, p);
 		break;
 
 	case WDIOC_GETTIMEOUT:
-		return put_user(wd_heartbeat, p);
+		return put_user(wd_time, p);
 		break;
 
 	case WDIOC_GETSTATUS:
@@ -291,7 +344,7 @@
 			}
 		}
 
-		comcerto_wdt_set_timeout();
+		comcerto_wdt_pet_watchdog_virtual();
 	}
 
 	return len;
@@ -352,6 +405,16 @@
                         WDT_NAME, WDT_MAX_TIMEOUT, WDT_DEFAULT_TIMEOUT);
         }
 
+        /* check that the time value is within range; if not reset to the default */
+        if (comcerto_wdt_settimeout(wd_time)) {
+                comcerto_wdt_settimeout(WDT_DEFAULT_TIMEOUT);
+
+                printk(KERN_INFO "%s: wd_time value is out of range: 1..%lu, using %d\n",
+                        WDT_NAME, WDT_MAX_TIME, WDT_DEFAULT_TIME);
+        }
+
+	setup_timer(&wdt_timer, comcerto_timer_tick, 0L);
+
 	return 0;
 
 err_misc:
diff --git a/fs/ubifs/debug.c b/fs/ubifs/debug.c
index b09ba2d..5047b57 100644
--- a/fs/ubifs/debug.c
+++ b/fs/ubifs/debug.c
@@ -2802,6 +2802,8 @@
 		val = d->chk_fs;
 	else if (dent == d->dfs_tst_rcvry)
 		val = d->tst_rcvry;
+	else if (dent == d->dfs_readonly)
+		val = c->ro_error;
 	else
 		return -EINVAL;
 
@@ -2993,6 +2995,12 @@
 		goto out_remove;
 	d->dfs_tst_rcvry = dent;
 
+	fname = "readonly";
+	dent = debugfs_create_file(fname, S_IRUSR, d->dfs_dir, c, &dfs_fops);
+	if (IS_ERR_OR_NULL(dent))
+		goto out_remove;
+	d->dfs_readonly = dent;
+
 	return 0;
 
 out_remove:
diff --git a/fs/ubifs/debug.h b/fs/ubifs/debug.h
index c9d2941..1a244ac 100644
--- a/fs/ubifs/debug.h
+++ b/fs/ubifs/debug.h
@@ -81,6 +81,7 @@
  * @dfs_chk_lprops: debugfs knob to enable UBIFS LEP properties extra checks
  * @dfs_chk_fs: debugfs knob to enable UBIFS contents extra checks
  * @dfs_tst_rcvry: debugfs knob to enable UBIFS recovery testing
+ * @dfs_readonly: debugfs file to get ro_error state
  */
 struct ubifs_debug_info {
 	struct ubifs_zbranch old_zroot;
@@ -124,6 +125,7 @@
 	struct dentry *dfs_chk_lprops;
 	struct dentry *dfs_chk_fs;
 	struct dentry *dfs_tst_rcvry;
+	struct dentry *dfs_readonly;
 };
 
 /**
diff --git a/init/Kconfig b/init/Kconfig
index de390b3..a846f22 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -1009,6 +1009,44 @@
 	  very difficult to diagnose system problems, saying N here is
 	  strongly discouraged.
 
+config PRINTK_PERSIST
+	default n
+	bool "printk log persists across reboots" if PRINTK
+	help
+	  This option tries to keep the printk memory buffer in a well-known
+	  location in physical memory. It isn't cleared on reboot (unless RAM
+	  is wiped by your boot loader or BIOS) so if your system crashes
+	  or panics, you might get to examine all the log messages next time you
+	  boot. The persisted log messages show up in your 'dmesg' output.
+	  Use log_buf_len= to specify the size of the persistent memory buffer.
+	  A value of 0 turns off this feature.
+
+config PRINTK_PERSIST_BUF_SHIFT
+	int "Printk persist buffer size (16 => 64KB, 23 => 8MB)"
+	range 16 23
+	depends on PRINTK_PERSIST
+	default 23
+	help
+	  The printk persist buffer is a log buffer that persists across reboots.
+	  Select buffer size as a power of 2.
+	  Examples:
+		     17 => 128 KB
+		     16 => 64 KB
+		     15 => 32 KB
+		     14 => 16 KB
+		     13 =>  8 KB
+		     12 =>  4 KB
+
+config BOOTLOG_COPY
+	default n
+	depends on PRINTK_PERSIST
+	bool "copy boot log to printk log" if PRINTK
+	help
+	  This option copies the boot log stored in bootlog memory and save it
+	  in the printk log.
+	  Note: you must supply the bootlog= and log_buf_len= kernel parameters
+	  to activate this feature.
+
 config BUG
 	bool "BUG() support" if EXPERT
 	default y
diff --git a/kernel/panic.c b/kernel/panic.c
index 3458469..bd00edd 100644
--- a/kernel/panic.c
+++ b/kernel/panic.c
@@ -27,13 +27,13 @@
 #define PANIC_TIMER_STEP 100
 #define PANIC_BLINK_SPD 18
 
-int panic_on_oops;
+int panic_on_oops = 1;
 static unsigned long tainted_mask;
 static int pause_on_oops;
 static int pause_on_oops_flag;
 static DEFINE_SPINLOCK(pause_on_oops_lock);
 
-int panic_timeout;
+int panic_timeout = 3;
 EXPORT_SYMBOL_GPL(panic_timeout);
 
 ATOMIC_NOTIFIER_HEAD(panic_notifier_list);
diff --git a/kernel/printk.c b/kernel/printk.c
index 7982a0a..40a766c 100644
--- a/kernel/printk.c
+++ b/kernel/printk.c
@@ -111,7 +111,12 @@
  */
 static unsigned log_start;	/* Index into log_buf: next char to be read by syslog() */
 static unsigned con_start;	/* Index into log_buf: next char to be sent to consoles */
+
+#ifdef CONFIG_PRINTK_PERSIST
+#define log_end logbits->_log_end
+#else
 static unsigned log_end;	/* Index into log_buf: most-recently-written-char + 1 */
+#endif
 
 /*
  * If exclusive_console is non-NULL then only this console is to be printed to.
@@ -144,12 +149,6 @@
 
 #ifdef CONFIG_PRINTK
 
-static char __log_buf[__LOG_BUF_LEN];
-static char *log_buf = __log_buf;
-static int log_buf_len = __LOG_BUF_LEN;
-static unsigned logged_chars; /* Number of chars produced since last read+clear operation */
-static int saved_console_loglevel = -1;
-
 #ifdef CONFIG_KEXEC
 /*
  * This appends the listed symbols to /proc/vmcoreinfo
@@ -168,8 +167,163 @@
 }
 #endif
 
+static int saved_console_loglevel = -1;
+static char __log_buf[__LOG_BUF_LEN];
+static char *log_buf = __log_buf;
+
+#ifndef CONFIG_PRINTK_PERSIST
+
+static int log_buf_len = __LOG_BUF_LEN;
+static unsigned logged_chars; /* Number of chars produced since last read+clear operation */
+
+static __init char *log_buf_alloc(unsigned long size, unsigned *dest_offset)
+{
+	return alloc_bootmem(size);
+}
+
+#else	/* CONFIG_PRINTK_PERSIST */
+
+struct logbits {
+	int magic; /* needed to verify the memory across reboots */
+	int _log_buf_len; /* leading _ so they aren't replaced by #define */
+	unsigned _logged_chars;
+	unsigned _log_end;
+};
+static struct logbits __logbits = {
+	._log_buf_len = __LOG_BUF_LEN,
+};
+static struct logbits *logbits = &__logbits;
+#define log_buf_len logbits->_log_buf_len
+#define logged_chars logbits->_logged_chars
+
+#define PERSIST_SEARCH_START 0
+#define PERSIST_SEARCH_END 0xfe000000
+#define PERSIST_SEARCH_JUMP (16*1024*1024)
+#define PERSIST_MAGIC 0xbabb1e
+
+#ifdef CONFIG_BOOTLOG_COPY
+#define BOOTLOG_MAGIC (0x1090091e)
+struct bloghdr {
+	unsigned int magic; /* for kernel verification */
+	unsigned int offset; /* current log offset */
+};
+
+extern unsigned long bootlog_get_addr(void);
+extern unsigned long bootlog_get_size(void);
+extern void bootlog_free(void);
+
+static inline struct bloghdr *get_bootlog_hdr(void)
+{
+	unsigned long bootlog_size = bootlog_get_size();
+	if (bootlog_size) {
+		struct bloghdr *blog_hdr = (struct bloghdr *)
+				phys_to_virt(bootlog_get_addr());
+		if (BOOTLOG_MAGIC != blog_hdr->magic ||
+		    (blog_hdr->offset + sizeof(struct bloghdr) >
+                     bootlog_size)) {
+			printk(KERN_INFO "bootlog: header invalid m:0x%08x "
+			       "o:0x%08x s:0x%08lx\n", blog_hdr->magic,
+                               blog_hdr->offset, bootlog_size);
+			return NULL;
+		}
+		return blog_hdr;
+	}
+	return NULL;
+}
+
+static inline unsigned copy_bootlog(struct bloghdr *blog_hdr,
+				    unsigned dest_offset)
+{
+	if (blog_hdr) {
+		unsigned idx = dest_offset;
+		char *blog_buf = (char *)(blog_hdr + 1);
+		unsigned i;
+
+		for (i = 0; i < blog_hdr->offset; ++i) {
+			LOG_BUF(idx) = blog_buf[i];
+			++idx;
+		}
+		if (logged_chars + blog_hdr->offset <= log_buf_len)
+			logged_chars += blog_hdr->offset;
+		else
+			logged_chars = log_buf_len;
+		dest_offset = idx;
+	}
+        return dest_offset;
+}
+
+static inline void free_bootlog(void)
+{
+	bootlog_free();
+}
+
+#endif
+
+/*
+ * size is a power of 2 so that the printk offset mask will work.  We'll add
+ * a bit more space to the end of the buffer for our extra data, but that
+ * won't change the offset of the buffers.
+ */
+static __init char *log_buf_alloc(unsigned long size, unsigned *dest_offset)
+{
+	unsigned long where;
+	char *buf;
+	unsigned long full_size = size + sizeof(struct logbits);
+	struct logbits *new_logbits;
+
+	for (where = PERSIST_SEARCH_END - size;
+			where >= PERSIST_SEARCH_START;
+			where -= PERSIST_SEARCH_JUMP) {
+		if (reserve_bootmem(where, full_size, BOOTMEM_EXCLUSIVE))
+			continue;
+
+		buf = phys_to_virt(where);
+		new_logbits = phys_to_virt(where + size);
+		printk(KERN_INFO "printk_persist: memory reserved @ 0x%08lx\n",
+			where);
+		if (new_logbits->magic != PERSIST_MAGIC ||
+				new_logbits->_log_buf_len != size ||
+				new_logbits->_logged_chars > size) {
+			printk(KERN_INFO "printk_persist: header invalid, "
+				"cleared.\n");
+			memset(buf, 0, full_size);
+			new_logbits->magic = PERSIST_MAGIC;
+			new_logbits->_log_buf_len = size;
+			new_logbits->_logged_chars = 0;
+			new_logbits->_log_end = 0;
+		} else {
+			printk(KERN_INFO "printk_persist: header valid; "
+				"logged=%d next=%d\n",
+				new_logbits->_logged_chars,
+				new_logbits->_log_end);
+		}
+		*dest_offset = new_logbits->_log_end;
+		new_logbits->_log_end = log_end;
+		if (new_logbits->_logged_chars + logged_chars <= size)
+			new_logbits->_logged_chars += logged_chars;
+		else
+			new_logbits->_logged_chars = size;
+		logbits = new_logbits;
+		return buf;
+	}
+	goto error;
+
+error:
+	/* replace the buffer, but don't bother to swap struct logbits */
+	printk(KERN_ERR "printk_persist: failed to reserve bootmem "
+		"area. disabled.\n");
+	return alloc_bootmem(full_size);
+}
+#endif	 /* CONFIG_PRINTK_PERSIST */
+
 /* requested log_buf_len from kernel cmdline */
-static unsigned long __initdata new_log_buf_len;
+static unsigned long __initdata new_log_buf_len =
+#ifdef CONFIG_PRINTK_PERSIST_BUF_SHIFT
+	(1 << CONFIG_PRINTK_PERSIST_BUF_SHIFT)
+#else
+	0
+#endif
+;
 
 /* save requested log_buf_len since it's too early to process it */
 static int __init log_buf_len_setup(char *str)
@@ -178,7 +332,7 @@
 
 	if (size)
 		size = roundup_pow_of_two(size);
-	if (size > log_buf_len)
+	if (size > log_buf_len || size == 0)
 		new_log_buf_len = size;
 
 	return 0;
@@ -188,9 +342,12 @@
 void __init setup_log_buf(int early)
 {
 	unsigned long flags;
-	unsigned start, dest_idx, offset;
-	char *new_log_buf;
+	unsigned start, dest_offset = 0, dest_idx, offset;
+	char *new_log_buf = NULL;
 	int free;
+#ifdef CONFIG_BOOTLOG_COPY
+	struct bloghdr *blog_hdr = NULL;
+#endif
 
 	if (!new_log_buf_len)
 		return;
@@ -203,7 +360,7 @@
 			return;
 		new_log_buf = __va(mem);
 	} else {
-		new_log_buf = alloc_bootmem_nopanic(new_log_buf_len);
+		new_log_buf = log_buf_alloc(new_log_buf_len, &dest_offset);
 	}
 
 	if (unlikely(!new_log_buf)) {
@@ -212,26 +369,39 @@
 		return;
 	}
 
+#ifdef CONFIG_BOOTLOG_COPY
+	/* Read out the blog_hdr before logbuf is locked in case print
+	 * is needed. */
+	blog_hdr = get_bootlog_hdr();
+#endif
+
 	raw_spin_lock_irqsave(&logbuf_lock, flags);
 	log_buf_len = new_log_buf_len;
 	log_buf = new_log_buf;
-	new_log_buf_len = 0;
 	free = __LOG_BUF_LEN - log_end;
 
+#ifdef CONFIG_BOOTLOG_COPY
+	dest_offset = copy_bootlog(blog_hdr, dest_offset);
+#endif
 	offset = start = min(con_start, log_start);
-	dest_idx = 0;
+	dest_idx = dest_offset;
 	while (start != log_end) {
 		unsigned log_idx_mask = start & (__LOG_BUF_LEN - 1);
+		unsigned dest_idx_mask = dest_idx & (new_log_buf_len - 1);
 
-		log_buf[dest_idx] = __log_buf[log_idx_mask];
+		log_buf[dest_idx_mask] = __log_buf[log_idx_mask];
 		start++;
 		dest_idx++;
 	}
-	log_start -= offset;
-	con_start -= offset;
-	log_end -= offset;
+	log_start += dest_offset - offset;
+	con_start += dest_offset - offset;
+	log_end += dest_offset - offset;
 	raw_spin_unlock_irqrestore(&logbuf_lock, flags);
 
+#ifdef CONFIG_BOOTLOG_COPY
+	free_bootlog();
+#endif
+
 	pr_info("log_buf_len: %d\n", log_buf_len);
 	pr_info("early log buf free: %d(%d%%)\n",
 		free, (free * 100) / __LOG_BUF_LEN);
@@ -329,8 +499,11 @@
 	return 0;
 }
 
+#define COPY_SIZE 4096
+
 int do_syslog(int type, char __user *buf, int len, bool from_file)
 {
+	char *copybuf;
 	unsigned i, j, limit, count;
 	int do_clear = 0;
 	char c;
@@ -396,6 +569,11 @@
 			error = -EFAULT;
 			goto out;
 		}
+		copybuf = kmalloc(COPY_SIZE, GFP_KERNEL);
+		if (!copybuf) {
+			error = -ENOMEM;
+			goto out;
+		}
 		count = len;
 		if (count > log_buf_len)
 			count = log_buf_len;
@@ -406,7 +584,7 @@
 			logged_chars = 0;
 		limit = log_end;
 		/*
-		 * __put_user() could sleep, and while we sleep
+		 * copy_to_user() could sleep, and while we sleep
 		 * printk() could overwrite the messages
 		 * we try to copy to user space. Therefore
 		 * the messages are copied in reverse. <manfreds>
@@ -416,14 +594,26 @@
 			if (j + log_buf_len < log_end)
 				break;
 			c = LOG_BUF(j);
-			raw_spin_unlock_irq(&logbuf_lock);
-			error = __put_user(c,&buf[count-1-i]);
-			cond_resched();
-			raw_spin_lock_irq(&logbuf_lock);
+			copybuf[COPY_SIZE-1-(i % COPY_SIZE)] = c;
+			if ((i+1) % COPY_SIZE == 0) {
+				raw_spin_unlock_irq(&logbuf_lock);
+				error = copy_to_user(&buf[count-1-i],
+						copybuf, COPY_SIZE);
+				cond_resched();
+				raw_spin_lock_irq(&logbuf_lock);
+			}
 		}
 		raw_spin_unlock_irq(&logbuf_lock);
-		if (error)
-			break;
+		if (!error) {
+			/* in case copybuf was only partially filled */
+			error = copy_to_user(&buf[count-i],
+				copybuf + COPY_SIZE - (i % COPY_SIZE),
+				i % COPY_SIZE);
+		}
+		if (error) {
+			error = -EFAULT;
+			goto copy_done;
+		}
 		error = i;
 		if (i != count) {
 			int offset = count-error;
@@ -437,6 +627,8 @@
 				cond_resched();
 			}
 		}
+copy_done:
+		kfree(copybuf);
 		break;
 	/* Clear ring buffer */
 	case SYSLOG_ACTION_CLEAR:
@@ -911,13 +1103,11 @@
 
 				for (i = 0; i < plen; i++)
 					emit_log_char(printk_buf[i]);
-				printed_len += plen;
 			} else {
 				/* Add log prefix */
 				emit_log_char('<');
 				emit_log_char(current_log_level + '0');
 				emit_log_char('>');
-				printed_len += 3;
 			}
 
 			if (printk_time) {
@@ -935,7 +1125,6 @@
 
 				for (tp = tbuf; tp < tbuf + tlen; tp++)
 					emit_log_char(*tp);
-				printed_len += tlen;
 			}
 
 			if (!*p)
diff --git a/mm/bootmem.c b/mm/bootmem.c
index d514f27..a5cbc05 100644
--- a/mm/bootmem.c
+++ b/mm/bootmem.c
@@ -352,7 +352,8 @@
 			return 0;
 		pos = bdata->node_low_pfn;
 	}
-	BUG();
+	//BUG();
+	return -ENXIO;
 }
 
 /**
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index d325941..9b9be63 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -853,6 +853,24 @@
 		n->fclone = SKB_FCLONE_UNAVAILABLE;
 	}
 
+#if defined(CONFIG_COMCERTO_CUSTOM_SKB_LAYOUT)
+	if (skb->mspd_data) {
+		if (skb->mspd_len) {
+			int ofst = skb->len - skb->mspd_len;
+
+			memcpy(skb->data + ofst, skb->mspd_data + skb->mspd_ofst, skb->mspd_len);
+			skb->mspd_len = 0;
+		}
+
+		WARN_ON(skb_shared(skb));
+
+		if (!skb_shared(skb)) {
+			kfree(skb->mspd_data);
+			skb->mspd_data = NULL;
+		}
+	}
+#endif
+
 	return __skb_clone(n, skb);
 }
 EXPORT_SYMBOL(skb_clone);
diff --git a/net/netfilter/nf_conntrack_proto.c b/net/netfilter/nf_conntrack_proto.c
index 5701c8d..7b6f708 100644
--- a/net/netfilter/nf_conntrack_proto.c
+++ b/net/netfilter/nf_conntrack_proto.c
@@ -98,13 +98,18 @@
 nf_ct_l3proto_try_module_get(unsigned short l3proto)
 {
 	int ret;
+	int retried = 0;
 	struct nf_conntrack_l3proto *p;
 
 retry:	p = nf_ct_l3proto_find_get(l3proto);
 	if (p == &nf_conntrack_l3proto_generic) {
-		ret = request_module("nf_conntrack-%d", l3proto);
-		if (!ret)
-			goto retry;
+		if (!retried) {
+			ret = request_module("nf_conntrack-%d", l3proto);
+			if (!ret) {
+				retried = 1;
+				goto retry;
+			}
+		}
 
 		return -EPROTOTYPE;
 	}