Merge Linux 4.1.37 to newkernel-4.1

Change-Id: I9c2feb2ac204703880761fd83ab23b3f90996ec8
diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt
index 07fad3d..4e2f2f9 100644
--- a/Documentation/networking/ip-sysctl.txt
+++ b/Documentation/networking/ip-sysctl.txt
@@ -375,6 +375,14 @@
 	Defaults are calculated at boot time from amount of available
 	memory.
 
+tcp_min_rtt_wlen - INTEGER
+	The window length of the windowed min filter to track the minimum RTT.
+	A shorter window lets a flow more quickly pick up new (higher)
+	minimum RTT when it is moved to a longer path (e.g., due to traffic
+	engineering). A longer window makes the filter more resistant to RTT
+	inflations such as transient congestion. The unit is seconds.
+	Default: 300
+
 tcp_moderate_rcvbuf - BOOLEAN
 	If set, TCP performs receive buffer auto-tuning, attempting to
 	automatically size the buffer (no greater than tcp_rmem[2]) to
diff --git a/Documentation/networking/ls1024a-fastforwarding.txt b/Documentation/networking/ls1024a-fastforwarding.txt
new file mode 100644
index 0000000..72edad1
--- /dev/null
+++ b/Documentation/networking/ls1024a-fastforwarding.txt
@@ -0,0 +1,100 @@
+NXP's (formerly Freescale) QorIQ LS1024A SoC (formerly known as Mindspeed
+Comcerto 2000) provides fast hardware forwarding between its three Ethernet
+MACs. For forwarding between an Ethernet MAC and WLAN, it depends on the ARM
+CPU to pass frames between the forwarding engine called PPFE (programmable
+packet forwarding engine) and the WLAN hardware connected via PCIe. Most
+forwarding decisions as well as NAT are handled inside the PPFE hardware. The
+role of Linux is reduced to forwarding descriptors between PPFE and the WLAN
+hardware.  Linux rarely accesses the data inside the Ethernet frames.
+
+Performance can be improved by using DMA coherent memory for data buffers
+because they do not require L1/L2 cache flushing, invalidation, etc. which
+turned out to be expensive on the LS1024A.  The PPFE driver allocates buffers
+using dma_alloc_coherent() and builds an skb around it. A few changes to the
+kernel were necessary to support DMA coherent skbs. We added the field
+dma_coherent to struct sk_buff to indicate that the skb head is in DMA coherent
+memory. skb_free_head() in skbuff.c checks for this flag and calls
+dma_free_coherent() instead of put_page() or kfree() on skb->head.
+
+dma_alloc_coherent() returns two values: (1) the CPU address which is a regular
+pointer i.e. a virtual address and (2) a dma handle which is basically a
+physical address that can be communicated to the hardware. For our fast
+forwarding approach to work, the virtual address must be in lowmem which means
+that there is a simple mapping from virtual address space to physical address
+space. In many situation, dma_alloc_coherent() returns an address from the
+vmalloc space which does not work for our purposes because dma_map_single()
+uses virt_to_page() to map a pointer (virtual address) to a page, and
+virt_to_page() does not work with addresses from the vmalloc space. For
+dma_alloc_coherent() to return an address in lowmem, a few conditions need to
+be met: (1) The caller needs to pass in GFP_ATOMIC for arm_dma_alloc() to
+allocate memory from its atomic pool. (2) The atomic pool needs to be allocated
+from CMA (CONFIG_CMA and CONFIG_DMA_CMA need to be set)
+
+dma_free_coherent() requires the cpu address (virtual address) and the dma
+handle (physical address) to be passed in. However, in our approach, the dma
+handle gets lost, because we do not store it in the skb. This turns out not to
+be a problem because the dma handle is not required if the memory came from the
+atomic pool. Although it does work in practice, the DMA-API debug feature
+complains about the incorrect dma handle passed into dma_free_coherent().
+Examples of these error messages can be found below. They can be ignored.
+
+December 9, 2015
+Daniel Mentz <danielmentz@google.com>
+
+
+[   10.468709] ------------[ cut here ]------------
+[   10.473383] WARNING: CPU: 0 PID: 0 at lib/dma-debug.c:1093 check_unmap+0x695/0x84c()
+[   10.481163] NULL NULL: DMA-API: device driver tries to free DMA memory it has not allocated [device address=0x0000000000000000] [size=2048 bytes]
+[   10.494246] Modules linked in: pfe(O)
+[   10.497952] CPU: 0 PID: 0 Comm: swapper/0 Tainted: G           O    4.1.0-gfiber0+ #298
+[   10.505988] Hardware name: Generic Freescale LS1024A (Flattened Device Tree)
+[   10.513095] [<800142a5>] (unwind_backtrace) from [<80011441>] (show_stack+0x11/0x14)
+[   10.520885] [<80011441>] (show_stack) from [<803dd59b>] (dump_stack+0x6f/0x98)
+[   10.528139] [<803dd59b>] (dump_stack) from [<80020f87>] (warn_slowpath_common+0x6b/0x94)
+[   10.536270] [<80020f87>] (warn_slowpath_common) from [<80020fd3>] (warn_slowpath_fmt+0x23/0x2c)
+[   10.545009] [<80020fd3>] (warn_slowpath_fmt) from [<802097d5>] (check_unmap+0x695/0x84c)
+[   10.553139] [<802097d5>] (check_unmap) from [<80209a41>] (debug_dma_free_coherent+0x61/0x68)
+[   10.561692] [<80209a41>] (debug_dma_free_coherent) from [<7f8042eb>] (pfe_eth_poll+0x3f6/0x644 [pfe])
+[   10.571001] [<7f8042eb>] (pfe_eth_poll [pfe]) from [<7f8045e7>] (pfe_eth_low_poll+0x36/0x3c [pfe])
+[   10.580027] [<7f8045e7>] (pfe_eth_low_poll [pfe]) from [<8031be17>] (net_rx_action+0x153/0x240)
+[   10.588759] [<8031be17>] (net_rx_action) from [<800233d1>] (__do_softirq+0xd5/0x294)
+[   10.596541] [<800233d1>] (__do_softirq) from [<80023813>] (irq_exit+0x8f/0xd8)
+[   10.603803] [<80023813>] (irq_exit) from [<8004b4a1>] (__handle_domain_irq+0x49/0x84)
+[   10.611670] [<8004b4a1>] (__handle_domain_irq) from [<800092ef>] (gic_handle_irq+0x27/0x50)
+[   10.620069] [<800092ef>] (gic_handle_irq) from [<803e1b3f>] (__irq_svc+0x3f/0x88)
+[   10.627573] Exception stack(0x80631f58 to 0x80631fa0)
+[   10.632649] 1f40:                                                       00000000 80633350
+[   10.640861] 1f60: 00000000 00000000 80630000 806324e8 80632400 8069b6b4 806324a0 8062a304
+[   10.649064] 1f80: 803e6a00 8069b6b4 8063003c 80631fa0 8000f14b 8000f14c 40070033 ffffffff
+[   10.657285] [<803e1b3f>] (__irq_svc) from [<8000f14c>] (arch_cpu_idle+0x30/0x34)
+[   10.664728] [<8000f14c>] (arch_cpu_idle) from [<80045fdb>] (cpu_startup_entry+0x11f/0x204)
+[   10.673035] [<80045fdb>] (cpu_startup_entry) from [<805dba31>] (start_kernel+0x309/0x314)
+[   10.681244] ---[ end trace 62a3b00cc739c0de ]---
+
+[   11.212979] ------------[ cut here ]------------
+[   11.217643] WARNING: CPU: 0 PID: 0 at lib/dma-debug.c:509 add_dma_entry+0xb1/0xd4()
+[   11.225323] DMA-API: exceeded 7 overlapping mappings of cacheline 0x03e00000
+[   11.232390] Modules linked in: pfe(O)
+[   11.236094] CPU: 0 PID: 0 Comm: swapper/0 Tainted: G        W  O    4.1.0-gfiber0+ #298
+[   11.244121] Hardware name: Generic Freescale LS1024A (Flattened Device Tree)
+[   11.251216] [<800142a5>] (unwind_backtrace) from [<80011441>] (show_stack+0x11/0x14)
+[   11.258994] [<80011441>] (show_stack) from [<803dd59b>] (dump_stack+0x6f/0x98)
+[   11.266247] [<803dd59b>] (dump_stack) from [<80020f87>] (warn_slowpath_common+0x6b/0x94)
+[   11.274366] [<80020f87>] (warn_slowpath_common) from [<80020fd3>] (warn_slowpath_fmt+0x23/0x2c)
+[   11.283094] [<80020fd3>] (warn_slowpath_fmt) from [<80208efd>] (add_dma_entry+0xb1/0xd4)
+[   11.291283] [<80208efd>] (add_dma_entry) from [<7f801861>] (pfe_hif_rx_poll+0x1c8/0x594 [pfe])
+[   11.299949] [<7f801861>] (pfe_hif_rx_poll [pfe]) from [<8031be17>] (net_rx_action+0x153/0x240)
+[   11.308593] [<8031be17>] (net_rx_action) from [<800233d1>] (__do_softirq+0xd5/0x294)
+[   11.316363] [<800233d1>] (__do_softirq) from [<80023813>] (irq_exit+0x8f/0xd8)
+[   11.323616] [<80023813>] (irq_exit) from [<8004b4a1>] (__handle_domain_irq+0x49/0x84)
+[   11.331474] [<8004b4a1>] (__handle_domain_irq) from [<800092ef>] (gic_handle_irq+0x27/0x50)
+[   11.339863] [<800092ef>] (gic_handle_irq) from [<803e1b3f>] (__irq_svc+0x3f/0x88)
+[   11.347366] Exception stack(0x80631f58 to 0x80631fa0)
+[   11.352433] 1f40:                                                       00000000 80633350
+[   11.360636] 1f60: 00000000 00000000 80630000 806324e8 80632400 8069b6b4 806324a0 8062a304
+[   11.368838] 1f80: 803e6a00 8069b6b4 8063003c 80631fa0 8000f14b 8000f14c 400f0033 ffffffff
+[   11.377049] [<803e1b3f>] (__irq_svc) from [<8000f14c>] (arch_cpu_idle+0x30/0x34)
+[   11.384480] [<8000f14c>] (arch_cpu_idle) from [<80045fdb>] (cpu_startup_entry+0x11f/0x204)
+[   11.392776] [<80045fdb>] (cpu_startup_entry) from [<805dba31>] (start_kernel+0x309/0x314)
+[   11.400978] ---[ end trace 62a3b00cc739c0df ]---
+
diff --git a/Makefile b/Makefile
index df72b64..5529123 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
 VERSION = 4
 PATCHLEVEL = 1
 SUBLEVEL = 37
-EXTRAVERSION =
+EXTRAVERSION = -gfiber0
 NAME = Series 4800
 
 # *DOCUMENTATION*
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 19f4cc6..6a83e0e 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -842,6 +842,8 @@
 
 source "arch/arm/mach-cns3xxx/Kconfig"
 
+source "arch/arm/mach-comcerto/Kconfig"
+
 source "arch/arm/mach-davinci/Kconfig"
 
 source "arch/arm/mach-digicolor/Kconfig"
diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug
index f775d71..bbfdfec 100644
--- a/arch/arm/Kconfig.debug
+++ b/arch/arm/Kconfig.debug
@@ -184,6 +184,14 @@
 		  If you have a Broadcom STB chip and would like early print
 		  messages to appear over the UART, select this option.
 
+	config DEBUG_COMCERTO2000_UART
+		bool "Freescale Comcerto 2000 SoC Debug UART"
+		depends on ARCH_COMCERTO
+		select DEBUG_UART_8250
+		help
+		  Say Y here if you want kernel low-level debugging support
+		  on Freescale Comcerto 2000 SoC based platforms.
+
 	config DEBUG_CLPS711X_UART1
 		bool "Kernel low-level debugging messages via UART1"
 		depends on ARCH_CLPS711X
@@ -1371,6 +1379,7 @@
 	default 0x80230000 if DEBUG_PICOXCELL_UART
 	default 0x808c0000 if ARCH_EP93XX
 	default 0x90020000 if DEBUG_NSPIRE_CLASSIC_UART || DEBUG_NSPIRE_CX_UART
+	default 0x96400000 if DEBUG_COMCERTO2000_UART
 	default 0xb0060000 if DEBUG_SIRFPRIMA2_UART1
 	default 0xb0090000 if DEBUG_VEXPRESS_UART0_CRX
 	default 0xc0013000 if DEBUG_U300_UART
@@ -1439,6 +1448,7 @@
 	default 0xf1009000 if DEBUG_MT8135_UART3
 	default 0xf11f1000 if ARCH_VERSATILE
 	default 0xf1600000 if ARCH_INTEGRATOR
+	default 0xf1a00000 if DEBUG_COMCERTO2000_UART
 	default 0xf1c28000 if DEBUG_SUNXI_UART0
 	default 0xf1c28400 if DEBUG_SUNXI_UART1
 	default 0xf1f02800 if DEBUG_SUNXI_R_UART
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index 47f10e7..6ac8554 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -157,6 +157,7 @@
 machine-$(CONFIG_ARCH_BERLIN)		+= berlin
 machine-$(CONFIG_ARCH_CLPS711X)		+= clps711x
 machine-$(CONFIG_ARCH_CNS3XXX)		+= cns3xxx
+machine-$(CONFIG_ARCH_COMCERTO)		+= comcerto
 machine-$(CONFIG_ARCH_DAVINCI)		+= davinci
 machine-$(CONFIG_ARCH_DIGICOLOR)	+= digicolor
 machine-$(CONFIG_ARCH_DOVE)		+= dove
diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
index 992736b..82f0059 100644
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -80,6 +80,9 @@
 	berlin2q-marvell-dmp.dtb
 dtb-$(CONFIG_ARCH_BRCMSTB) += \
 	bcm7445-bcm97445svmb.dtb
+dtb-$(CONFIG_ARCH_COMCERTO) += \
+	optimus.dtb \
+	gfsc100.dtb
 dtb-$(CONFIG_ARCH_DAVINCI) += \
 	da850-enbw-cmc.dtb \
 	da850-evm.dtb
diff --git a/arch/arm/boot/dts/gfsc100.dts b/arch/arm/boot/dts/gfsc100.dts
new file mode 100644
index 0000000..5a96f39
--- /dev/null
+++ b/arch/arm/boot/dts/gfsc100.dts
@@ -0,0 +1,200 @@
+/dts-v1/;
+
+#include "ls1024a.dtsi"
+/ {
+	model = "Google SpaceCast (GFSC100)";
+	compatible = "google,gfsc100", "fsl,ls1024a";
+
+	chosen {
+		bootargs = "log_buf_len=8M";
+	};
+
+	memory {
+		device_type = "memory";
+		reg = <0x80000000 0x40000000>; /* 1 GB */
+	};
+
+	pwmleds {
+		compatible = "pwm-leds";
+		pinctrl-names = "default";
+		pinctrl-0 = <&pinctrl_gfsc100_leds>;
+
+		pwmledblue: pwmledblue {
+			label = "blue";
+			pwms = <&pwm 4 1000000>;
+			max-brightness = <200>;
+		};
+		pwmledred: pwmledred {
+			label = "red";
+			pwms = <&pwm 5 1000000>;
+			max-brightness = <200>;
+			linux,default-trigger = "default-on";
+		};
+	};
+};
+
+&pinctrl0 {
+	tpm_int {
+		gpio-hog;
+		gpios = <0 GPIO_ACTIVE_HIGH>;
+		input;
+		line-name = "tpm_int";
+	};
+	fan_control_alert {
+		gpio-hog;
+		gpios = <1 GPIO_ACTIVE_HIGH>;
+		input;
+		line-name = "fan_control_alert";
+	};
+	usb_brg_rst_n {
+		gpio-hog;
+		gpios = <9 GPIO_ACTIVE_LOW>;
+		output-low;
+		line-name = "usb_brg_rst_n";
+	};
+	m88rs6k_rst_n {
+		gpio-hog;
+		gpios = <11 GPIO_ACTIVE_LOW>;
+		output-low;
+		line-name = "m88rs6k_rst_n";
+	};
+	mv88E1512_rst_n {
+		gpio-hog;
+		gpios = <14 GPIO_ACTIVE_LOW>;
+		output-low;
+		line-name = "mv88e1512_rst_n";
+	};
+	usb_power {
+		gpio-hog;
+		gpios = <15 GPIO_ACTIVE_HIGH>;
+		output-high;
+		line-name = "usb_power";
+	};
+	lnb_fault_n {
+		gpio-hog;
+		gpios = <47 GPIO_ACTIVE_HIGH>;
+		input;
+		line-name = "lnb_fault_n";
+	};
+	lnb_pwr_sel {
+		gpio-hog;
+		gpios = <51 GPIO_ACTIVE_HIGH>;
+		output-low;
+		line-name = "lnb_pwr_sel";
+	};
+	hw_rev_0 {
+		gpio-hog;
+		gpios = <52 GPIO_ACTIVE_HIGH>;
+		input;
+		line-name = "hw_rev_0";
+	};
+	hw_rev_1 {
+		gpio-hog;
+		gpios = <53 GPIO_ACTIVE_HIGH>;
+		input;
+		line-name = "hw_rev_1";
+	};
+	hw_rev_2 {
+		gpio-hog;
+		gpios = <54 GPIO_ACTIVE_HIGH>;
+		input;
+		line-name = "hw_rev_2";
+	};
+	board_id_0 {
+		gpio-hog;
+		gpios = <55 GPIO_ACTIVE_HIGH>;
+		input;
+		line-name = "board_id_0";
+	};
+	board_id_1 {
+		gpio-hog;
+		gpios = <56 GPIO_ACTIVE_HIGH>;
+		input;
+		line-name = "board_id_1";
+	};
+	board_id_2 {
+		gpio-hog;
+		gpios = <57 GPIO_ACTIVE_HIGH>;
+		input;
+		line-name = "board_id_2";
+	};
+	pinctrl_gfsc100_leds: gfsc100-leds-grp {
+		leds {
+			groups = "pwm4", "pwm5";
+			function = "pwm";
+		};
+	};
+	pinctrl_i2c: i2cgrp {
+		i2c {
+			groups = "i2c";
+			function = "i2c";
+		};
+	};
+};
+
+/* TODO(mka@): specify the partition layout only in the bootloader */
+&nor0 {
+	uloader@0 {
+		label = "uloader";
+		reg = <0x0 0x20000>;
+	};
+	loader0@20000 {
+		label = "loader0";
+		reg = <0x20000 0x80000>;
+	};
+	loader1@a0000 {
+		label = "loader1";
+		reg = <0xa0000 0x80000>;
+	};
+	env@120000 {
+		label = "env";
+		reg = <0x120000 0x20000>;
+	};
+	hnvram@140000 {
+		label = "hnvram";
+		reg = <0x140000 0x200000>;
+	};
+	kernel0@340000 {
+		label = "kernel0";
+		reg = <0x340000 0x600000>;
+	};
+	kernel1@940000 {
+		label = "kernel1";
+		reg = <0x940000 0x600000>;
+	};
+	norreserved0@F40000 {
+		label = "norreserved0";
+		reg = <0xF40000 0x00c0000>;
+	};
+};
+
+&i2c0 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_i2c>;
+	tpm_i2c_infineon: tpm_i2c_infineon@20 {
+		compatible = "infineon,tpm_i2c_infineon";
+		reg = <0x20>;
+	};
+	lm63: lm63@4c {
+		compatible = "national,lm63";
+		reg = <0x4c>;
+	};
+};
+
+&usb2_phy {
+	status = "okay";
+};
+
+&usb2 {
+	status = "okay";
+	dr_mode = "host";
+};
+
+&usb3_phy {
+	status = "okay";
+};
+
+&usb3 {
+	status = "okay";
+	dr_mode = "host";
+};
diff --git a/arch/arm/boot/dts/ls1024a.dtsi b/arch/arm/boot/dts/ls1024a.dtsi
new file mode 100644
index 0000000..f07c80d
--- /dev/null
+++ b/arch/arm/boot/dts/ls1024a.dtsi
@@ -0,0 +1,507 @@
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+#include <dt-bindings/pinctrl/omap.h>
+#include <dt-bindings/phy/phy.h>
+#include <dt-bindings/reset-controller/ls1024a-resets.h>
+
+#include "skeleton.dtsi"
+
+/ {
+	compatible = "fsl,ls1024a";
+	interrupt-parent = <&gic>;
+
+	cpus {
+		#address-cells = <1>;
+		#size-cells = <0>;
+		enable-method = "fsl,ls1024a-smp";
+
+		cpu@900 {
+			compatible = "arm,cortex-a9";
+			device_type = "cpu";
+			next-level-cache = <&L2>;
+			reg = <0x900>;
+
+			clocks = <&arm_clk>;
+			clock-names = "cpu";
+
+			// clock-latency = <300000>; /* From omap-cpufreq driver */
+		};
+		cpu@901 {
+			compatible = "arm,cortex-a9";
+			device_type = "cpu";
+			next-level-cache = <&L2>;
+			reg = <0x901>;
+		};
+	};
+
+	pmu {
+		compatible = "arm,cortex-a9-pmu";
+		interrupts = <GIC_SPI 9 IRQ_TYPE_LEVEL_HIGH>,
+			   <GIC_SPI 10 IRQ_TYPE_LEVEL_HIGH>;
+	};
+
+	clk_rst_syscon: clk_rst_syscon@904B0000 {
+		compatible = "syscon";
+		reg = <0x904B0000 0x1AC>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+	};
+
+	reboot: syscon-reboot {
+		compatible = "syscon-reboot";
+		regmap = <&clk_rst_syscon>;
+		offset = <0x0>;
+		mask = <0x1>;
+	};
+
+	gic: interrupt-controller@fff01000 {
+		compatible = "arm,cortex-a9-gic";
+		interrupt-controller;
+		#interrupt-cells = <3>;
+		reg = <0xFFF01000 0x1000>,
+		      <0xFFF00100 0x0100>;
+	};
+
+	L2: l2-cache-controller@fff10000 {
+		compatible = "arm,pl310-cache";
+		reg = <0xFFF10000 0x1000>;
+		arm,data-latency = <1 1 1>;
+		arm,tag-latency = <1 1 1>;
+		arm,filter-ranges = <0x0 0x80000000>;
+		cache-unified;
+		cache-level = <2>;
+	};
+
+	reset: reset-controller {
+		compatible = "fsl,ls1024a-reset";
+		#reset-cells = <1>;
+		syscon = <&clk_rst_syscon>;
+	};
+
+	ref_clk: ref_clk {
+		#clock-cells = <0>;
+		compatible = "fixed-clock";
+		clock-frequency = <24000000>;
+	};
+
+	pll0: pll0 {
+		#clock-cells = <0>;
+		compatible = "fixed-factor-clock";
+		clock-div = <6>;
+		clock-mult = <450>;
+		clocks = <&ref_clk>;
+	};
+
+	pll1: pll1 {
+		#clock-cells = <0>;
+		compatible = "fixed-factor-clock";
+		clock-div = <12>;
+		clock-mult = <500>;
+		clocks = <&ref_clk>;
+	};
+
+	pll2: pll2 {
+		#clock-cells = <0>;
+		compatible = "fixed-factor-clock";
+		clock-div = <6>;
+		clock-mult = <375>;
+		clocks = <&ref_clk>;
+	};
+
+	pll3: pll3 {
+		#clock-cells = <0>;
+		compatible = "fixed-clock";
+		clock-frequency = <1066000000>;
+	};
+
+	gemtx_clk: gemtx_clk {
+		#clock-cells = <0>;
+		reg = <0x904B0130 0x8>;
+		compatible = "fsl,ls1024a-clock";
+		clocks = <&pll0 &pll1 &pll2 &pll3 &ref_clk>;
+	};
+
+	ddr_clk: ddr_clk {
+		#clock-cells = <0>;
+		reg = <0x904B00F0 0x8>;
+		compatible = "fsl,ls1024a-clock";
+		clocks = <&pll0 &pll1 &pll2 &pll3 &ref_clk>;
+	};
+
+	arm_clk: arm_clk {
+		#clock-cells = <0>;
+		reg = <0x904B0080 0x8>;
+		compatible = "fsl,ls1024a-clock";
+		clocks = <&pll0 &pll1 &pll2 &pll3 &ref_clk>;
+	};
+
+	l2cc_clk: l2cc_clk {
+		#clock-cells = <0>;
+		reg = <0x904B0090 0x8>;
+		compatible = "fsl,ls1024a-clock";
+		clocks = <&pll0 &pll1 &pll2 &pll3 &ref_clk>;
+	};
+
+	axi_clk: axi_clk {
+		#clock-cells = <0>;
+		reg = <0x904B0040 0x10>;
+		fsl,divreg-offset = <0xC>;
+		compatible = "fsl,ls1024a-clock";
+		clocks = <&pll0 &pll1 &pll2 &pll3 &ref_clk>;
+	};
+
+	ipsec_eape_clk: ipsec_eape_clk {
+		#clock-cells = <0>;
+		reg = <0x904B0110 0x8>;
+		compatible = "fsl,ls1024a-clock";
+		clocks = <&pll0 &pll1 &pll2 &pll3 &ref_clk>;
+	};
+
+	hfe_core_clk: hfe_core_clk {
+		#clock-cells = <0>;
+		reg = <0x904B0100 0x8>;
+		compatible = "fsl,ls1024a-clock";
+		clocks = <&pll0 &pll1 &pll2 &pll3 &ref_clk>;
+	};
+
+	ntgref_clk: ntgref_clk {
+		#clock-cells = <0>;
+		reg = <0x904B0140 0x8>;
+		compatible = "fsl,ls1024a-clock";
+		clocks = <&pll0 &pll1 &pll2 &pll3 &ref_clk>;
+	};
+
+	sata_oob_clk: sata_oob_clk {
+		#clock-cells = <0>;
+		reg = <0x904B0170 0x8>;
+		compatible = "fsl,ls1024a-clock";
+		clocks = <&pll0 &pll1 &pll2 &pll3 &ref_clk>;
+	};
+
+	sata_pmu_clk: sata_pmu_clk {
+		#clock-cells = <0>;
+		reg = <0x904B0160 0x8>;
+		compatible = "fsl,ls1024a-clock";
+		clocks = <&pll0 &pll1 &pll2 &pll3 &ref_clk>;
+	};
+
+	ext_phy0_clk: ext_phy0_clk {
+		#clock-cells = <0>;
+		reg = <0x904B00C0 0x8>;
+		compatible = "fsl,ls1024a-clock";
+		clocks = <&pll0 &pll1 &pll2 &pll3 &ref_clk>;
+	};
+
+	ext_phy1_clk: ext_phy1_clk {
+		#clock-cells = <0>;
+		reg = <0x904B00D0 0x8>;
+		compatible = "fsl,ls1024a-clock";
+		clocks = <&pll0 &pll1 &pll2 &pll3 &ref_clk>;
+	};
+
+	ext_phy2_clk: ext_phy2_clk {
+		#clock-cells = <0>;
+		reg = <0x904B00E0 0x8>;
+		compatible = "fsl,ls1024a-clock";
+		clocks = <&pll0 &pll1 &pll2 &pll3 &ref_clk>;
+	};
+
+	tpi_clk: tpi_clk {
+		#clock-cells = <0>;
+		reg = <0x904B00A0 0x8>;
+		compatible = "fsl,ls1024a-clock";
+		clocks = <&pll0 &pll1 &pll2 &pll3 &ref_clk>;
+	};
+
+	csys_clk: csys_clk {
+		#clock-cells = <0>;
+		reg = <0x904B00B0 0x8>;
+		compatible = "fsl,ls1024a-clock";
+		clocks = <&pll0 &pll1 &pll2 &pll3 &ref_clk>;
+	};
+
+	tsuntg_clk: tsuntg_clk {
+		#clock-cells = <0>;
+		reg = <0x904B0150 0x8>;
+		compatible = "fsl,ls1024a-clock";
+		clocks = <&pll0 &pll1 &pll2 &pll3 &ref_clk>;
+	};
+
+	sata_occ_clk: sata_occ_clk {
+		#clock-cells = <0>;
+		reg = <0x904B0180 0x8>;
+		compatible = "fsl,ls1024a-clock";
+		clocks = <&pll0 &pll1 &pll2 &pll3 &ref_clk>;
+	};
+
+	pcie_occ_clk: pcie_occ_clk {
+		#clock-cells = <0>;
+		reg = <0x904B0190 0x8>;
+		compatible = "fsl,ls1024a-clock";
+		clocks = <&pll0 &pll1 &pll2 &pll3 &ref_clk>;
+	};
+
+	sgmii_occ_clk: sgmii_occ_clk {
+		#clock-cells = <0>;
+		reg = <0x904B01A0 0x8>;
+		compatible = "fsl,ls1024a-clock";
+		clocks = <&pll0 &pll1 &pll2 &pll3 &ref_clk>;
+	};
+	local-timer@fff00600 {
+		compatible = "arm,cortex-a9-twd-timer";
+		clocks = <&axi_clk>;
+		reg = <0xFFF00600 0x20>;
+		interrupts = <GIC_PPI 13 (GIC_CPU_MASK_RAW(3) | IRQ_TYPE_LEVEL_HIGH)>;
+	};
+	gp-timer@90450000 {
+		compatible = "fsl,ls1024a-gpt";
+		clocks = <&axi_clk>;
+		reg = <0x90450000 0xD0>;
+		interrupts = <GIC_SPI 55 IRQ_TYPE_EDGE_RISING
+			      GIC_SPI 56 IRQ_TYPE_EDGE_RISING
+			      GIC_SPI 57 IRQ_TYPE_EDGE_RISING
+			      GIC_SPI 58 IRQ_TYPE_EDGE_RISING
+			      GIC_SPI 59 IRQ_TYPE_EDGE_RISING
+			      GIC_SPI 60 IRQ_TYPE_EDGE_RISING>;
+	};
+
+	watchdog@904500D0 {
+		compatible = "fsl,ls1024a-wdt";
+		reg = <0x904500D0 0xC>;
+		regmap = <&clk_rst_syscon>;
+		clocks = <&axi_clk>;
+	};
+
+	nor0: flash@c0000000 {
+		compatible = "cfi-flash";
+		reg = <0xc0000000 0x04000000>;
+		bank-width = <2>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+	};
+
+	nand0: nand@c8300000 {
+		compatible = "fsl,ls1024a-nand";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		reg = <0xc8300000 0x2000
+		       0xCFFF0000 0x2c>;
+		ce-gpios = <&pinctrl0 28 GPIO_ACTIVE_HIGH>;
+		br-gpios = <&pinctrl0 29 GPIO_ACTIVE_HIGH>;
+	};
+
+	otp0: otp@904F0000 {
+		compatible = "fsl,ls1024a-otp";
+		reg = <0x904F0000 0x58>;
+		clocks = <&axi_clk>;
+	};
+
+	pwm: pwm@12dd0000 {
+		compatible = "fsl,ls1024a-pwm";
+		reg = <0x90458000 0x38>;
+		#pwm-cells = <2>;
+		clocks = <&axi_clk>;
+	};
+
+	pinctrl0: pinctrl@90470000 {
+		compatible = "fsl,ls1024a-pinctrl";
+		reg = <0x90470000 0x100>;
+		gpio-controller;
+		#gpio-cells = <2>;
+		gpio-ranges = <&pinctrl0 0 0 0>;
+		gpio-ranges-group-names = "gpio";
+		interrupt-controller;
+		#interrupt-cells = <2>;
+		interrupt-parent = <&gic>;
+		interrupts = <GIC_SPI 45 IRQ_TYPE_NONE>;
+	};
+
+	/* We are not using uart0, only uart1 */
+	uart1: serial@96400000 {
+		compatible = "snps,dw-apb-uart";
+		reg = <0x96400000 0x100>;
+		interrupts = <GIC_SPI 27 IRQ_TYPE_LEVEL_HIGH>;
+		reg-shift = <2>;
+		reg-io-width = <4>;
+		clocks = <&axi_clk>;
+		clock-names = "baudclk";
+	};
+
+	spi0: spi@fff00000 {
+		compatible = "snps,dw-apb-ssi";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		reg = <0x96500000 0x1000>;
+		interrupts = <GIC_SPI 28 IRQ_TYPE_LEVEL_HIGH>;
+		num-cs = <1>;
+		clocks = <&axi_clk>; /* TODO: Should be DUS clock */
+	};
+
+	i2c0: i2c@9049C000 {
+		compatible = "fsl,ls1024a-i2c";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		reg = <0x9049C000 0x1000>;
+		interrupts = <GIC_SPI 30 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&axi_clk>; /* TODO: Should be spi_i2c */
+	};
+
+	snowbush_phy: phy@90590000 {
+		compatible      = "fsl,ls1024a-snowbush-phy";
+		#address-cells	= <1>;
+		#size-cells	= <1>;
+		ranges;
+
+		phy_port0: port@90590000 {
+			#phy-cells = <1>;
+			reg = <0x90590000 0x4000>, <0x9041002C 0x10>;
+			reg-names = "serdes", "dwc1_cfg";
+			resets = <&reset LS1024A_SERDES0_RESET>,
+				 <&reset LS1024A_SERDES_PCIE0_RESET>;
+			reset-names = "serdes", "serdes_pcie";
+		};
+
+		phy_port1: port@90594000 {
+			#phy-cells = <1>;
+			reg = <0x90594000 0x4000>, <0x9041003C 0x10>;
+			reg-names = "serdes", "dwc1_cfg";
+			resets = <&reset LS1024A_SERDES1_RESET>,
+				 <&reset LS1024A_SERDES_PCIE1_RESET>,
+				 <&reset LS1024A_SERDES_SATA0_RESET>;
+			reset-names = "serdes", "serdes_pcie", "serdes_sata";
+		};
+		phy_port2: port@90598000 {
+			#phy-cells = <1>;
+			reg = <0x90598000 0x4000>, <0x9041004C 0x10>;
+			reg-names = "serdes", "dwc1_cfg";
+			resets = <&reset LS1024A_SERDES2_RESET>,
+				 <&reset LS1024A_SERDES_SATA1_RESET>;
+			reset-names = "serdes", "serdes_sata";
+		};
+	};
+
+	pci_sata_usb_ctrl: pci_sata_usb_ctrl@90460000 {
+		compatible = "syscon";
+		reg = <0x90460000 0x120>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+	};
+	ahci0: ahci@9d000000 {
+		compatible = "generic-ahci";
+		reg = <0x9d000000 0x10000>;
+		interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>;
+		ports-implemented = <0x3>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		sata0: sata-port@0 {
+			reg = <0>;
+		};
+		sata1: sata-port@1 {
+			reg = <1>;
+			phys = <&phy_port2 PHY_TYPE_SATA>;
+		};
+	};
+	pcie0: pcie@98000000 {
+		compatible = "fsl,ls1024a-pcie", "snps,dw-pcie";
+		reg = <0x98000000 0x04000>, /* TODO: verify size */
+		      <0xaff10000 0x20000>;
+		reg-names = "dbi", "config";
+		app-syscon = <&pci_sata_usb_ctrl>;
+		app-profile = <0>;
+		#address-cells = <3>;
+		#size-cells = <2>;
+		device_type = "pci";
+		ranges = <0x81000000 0 0          0xaff00000 0 0x00010000 /* downstream I/O */
+			  0x82000000 0 0xa0000000 0xa0000000 0 0x0fe00000>; /* non-prefetchable memory */
+		num-lanes = <1>;
+		phys	        = <&phy_port0 PHY_TYPE_PCIE>;
+		phy-names       = "pcie-phy";
+		interrupts = <GIC_SPI 43 IRQ_TYPE_LEVEL_HIGH>;
+		interrupt-names = "msi";
+		#interrupt-cells = <1>;
+		interrupt-map-mask = <0 0 0 0>;
+		interrupt-map = <0 0 0 0 &gic GIC_SPI 43 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&axi_clk>; /* TODO: Should be pcie0 */
+		clock-names = "pcie";
+		resets = <&reset LS1024A_AXI_PCIE0_RESET>;
+		reset-names = "axi";
+	};
+
+	pcie1: pcie@99000000 {
+		compatible = "fsl,ls1024a-pcie", "snps,dw-pcie";
+		reg = <0x99000000 0x04000>, /* TODO: verify size */
+		      <0xbff10000 0x20000>;
+		reg-names = "dbi", "config";
+		app-syscon = <&pci_sata_usb_ctrl>;
+		app-profile = <1>;
+		#address-cells = <3>;
+		#size-cells = <2>;
+		device_type = "pci";
+		ranges = <0x81000000 0 0          0xbff00000 0 0x00010000 /* downstream I/O */
+			  0x82000000 0 0xb0000000 0xb0000000 0 0x0fe00000>; /* non-prefetchable memory */
+		num-lanes = <1>;
+		phys	        = <&phy_port1 PHY_TYPE_PCIE>;
+		phy-names       = "pcie-phy";
+		interrupts = <GIC_SPI 44 IRQ_TYPE_LEVEL_HIGH>;
+		interrupt-names = "msi";
+		#interrupt-cells = <1>;
+		interrupt-map-mask = <0 0 0 0>;
+		interrupt-map = <0 0 0 0 &gic GIC_SPI 44 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&axi_clk>; /* TODO: Should be pcie1 */
+		clock-names = "pcie";
+		resets = <&reset LS1024A_AXI_PCIE1_RESET>;
+		reset-names = "axi";
+	};
+
+	usb2_phy: usb2_phy@90410000 {
+		compatible	= "fsl,ls1024a-usb2-phy";
+		reg		= <0x90410000 0x4>;
+		syscon		= <&pci_sata_usb_ctrl>;
+		#phy-cells	= <0>;
+		resets		= <&reset LS1024A_USB0_PHY_RESET &reset LS1024A_UTMI_USB0_RESET &reset LS1024A_AXI_USB0_RESET>;
+		reset-names	= "phy", "utmi", "axi";
+		clocks = <&axi_clk>; /* TODO: Should be usb0 */
+		clock-names = "usb";
+		status = "disabled";
+	};
+
+	/* USB 2.0 Synopsys DesignWare controller */
+	usb2: usb2@92000000 {
+		compatible = "snps,dwc2";
+		reg = <0x92000000 0x1000000>;
+		interrupts = <GIC_SPI 21 IRQ_TYPE_LEVEL_HIGH>;
+		phys = <&usb2_phy>;
+		phy-names = "usb2-phy";
+		status = "disabled";
+	};
+
+	usb3_phy: usb3_phy@904a0000 {
+		compatible	= "fsl,ls1024a-usb3-phy";
+		reg		= <0x904A0000 0x100>;
+		#phy-cells	= <0>;
+		resets		= <&reset LS1024A_USB1_PHY_RESET>;
+		reset-names	= "phy";
+		status = "disabled";
+	};
+
+	usb3: usb3 {
+		compatible = "fsl,ls1024a-dwc3";
+		clocks = <&axi_clk>; /* TODO: Should be usb1 */
+		clock-names = "usb";
+		resets = <&reset LS1024A_UTMI_USB1_RESET &reset LS1024A_AXI_USB1_RESET>;
+		reset-names = "utmi", "axi";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges;
+		status = "disabled";
+
+		dwc3: dwc3@9f000000  {
+			compatible = "snps,dwc3";
+			reg = <0x9F000000 0x00800000>;
+			interrupts = <GIC_SPI 22 IRQ_TYPE_LEVEL_HIGH>;
+			phys = <&usb3_phy>;
+			phy-names = "usb3-phy";
+		};
+	};
+
+};
diff --git a/arch/arm/boot/dts/optimus.dts b/arch/arm/boot/dts/optimus.dts
new file mode 100644
index 0000000..668ac74
--- /dev/null
+++ b/arch/arm/boot/dts/optimus.dts
@@ -0,0 +1,242 @@
+/dts-v1/;
+
+#include "ls1024a.dtsi"
+/ {
+	model = "Google Fiber Optimus";
+	compatible = "google,optimus", "fsl,ls1024a";
+
+	chosen {
+		bootargs = "log_buf_len=8M";
+	};
+
+	memory {
+		device_type = "memory";
+		reg = <0x80000000 0x40000000>; /* 1 GB */
+	};
+
+	pwmleds {
+		compatible = "pwm-leds";
+		pinctrl-names = "default";
+		pinctrl-0 = <&pinctrl_optimus_leds>;
+
+		pwmledblue: pwmledblue {
+			label = "blue";
+			pwms = <&pwm 4 1000000>;
+			max-brightness = <200>;
+		};
+		pwmledred: pwmledred {
+			label = "red";
+			pwms = <&pwm 5 1000000>;
+			max-brightness = <200>;
+			linux,default-trigger = "default-on";
+		};
+	};
+};
+
+&pinctrl0 {
+	fan_control_alert {
+		gpio-hog;
+		gpios = <1 GPIO_ACTIVE_HIGH>;
+		input;
+		line-name = "fan_control_alert";
+	};
+	moca_sideband_0 {
+		gpio-hog;
+		gpios = <2 GPIO_ACTIVE_HIGH>;
+		input;
+		line-name = "moca_sideband_0";
+	};
+	moca_sideband_1 {
+		gpio-hog;
+		gpios = <3 GPIO_ACTIVE_HIGH>;
+		input;
+		line-name = "moca_sideband_1";
+	};
+	mini_wake0n {
+		gpio-hog;
+		gpios = <4 GPIO_ACTIVE_HIGH>;
+		input;
+		line-name = "mini_wake0n";
+	};
+	mini_wake1n {
+		gpio-hog;
+		gpios = <5 GPIO_ACTIVE_HIGH>;
+		input;
+		line-name = "mini_wake1n";
+	};
+	qca8337n_reset {
+		gpio-hog;
+		gpios = <14 GPIO_ACTIVE_LOW>;
+		output-low;
+		line-name = "qca8337n_reset";
+	};
+	usb_power {
+		gpio-hog;
+		gpios = <15 GPIO_ACTIVE_HIGH>;
+		output-high;
+		line-name = "usb_power";
+	};
+	11ac_power {
+		gpio-hog;
+		gpios = <48 GPIO_ACTIVE_HIGH>;
+		output-high;
+		line-name = "wifi_11ac_power";
+	};
+	hw_rev_0 {
+		gpio-hog;
+		gpios = <52 GPIO_ACTIVE_HIGH>;
+		input;
+		line-name = "hw_rev_0";
+	};
+	hw_rev_1 {
+		gpio-hog;
+		gpios = <53 GPIO_ACTIVE_HIGH>;
+		input;
+		line-name = "hw_rev_1";
+	};
+	hw_rev_2 {
+		gpio-hog;
+		gpios = <54 GPIO_ACTIVE_HIGH>;
+		input;
+		line-name = "hw_rev_2";
+	};
+	board_id_0 {
+		gpio-hog;
+		gpios = <55 GPIO_ACTIVE_HIGH>;
+		input;
+		line-name = "board_id_0";
+	};
+	board_id_1 {
+		gpio-hog;
+		gpios = <56 GPIO_ACTIVE_HIGH>;
+		input;
+		line-name = "board_id_1";
+	};
+	board_id_2 {
+		gpio-hog;
+		gpios = <57 GPIO_ACTIVE_HIGH>;
+		input;
+		line-name = "board_id_2";
+	};
+	pinctrl_optimus_leds: optimus-leds-grp {
+		leds {
+			groups = "pwm4", "pwm5";
+			function = "pwm";
+		};
+	};
+	pinctrl_i2c: i2cgrp {
+		i2c {
+			groups = "i2c";
+			function = "i2c";
+		};
+	};
+	pinctrl_spi: spigrp {
+		spi {
+			groups = "spi_2", "spi_2_ss0";
+			function = "spi_2";
+		};
+	};
+};
+
+&nor0 {
+		uloader@0 {
+			label = "uloader";
+			reg = <0x0 0x20000>;
+		};
+		loader0@20000 {
+			label = "loader0";
+			reg = <0x20000 0x80000>;
+		};
+		loader1@a0000 {
+			label = "loader1";
+			reg = <0xa0000 0x80000>;
+		};
+		env@120000 {
+			label = "env";
+			reg = <0x120000 0x20000>;
+		};
+		hnvram@140000 {
+			label = "hnvram";
+			reg = <0x140000 0x200000>;
+		};
+		norreserved0@340000 {
+			label = "norreserved0";
+			reg = <0x340000 0x3cc0000>;
+		};
+};
+
+&nand0 {
+		reserved@0 {
+			label = "reserved";
+			reg = <0x0 0x800000>;
+		};
+		kernel0@800000 {
+			label = "kernel0";
+			reg = <0x800000 0x2000000>;
+		};
+		kernel1@2800000 {
+			label = "kernel1";
+			reg = <0x2800000 0x2000000>;
+		};
+		rootfs0@4800000 {
+			label = "rootfs0";
+			reg = <0x4800000 0x12000000>;
+		};
+		rootfs1@16800000 {
+			label = "rootfs1";
+			reg = <0x16800000 0x12000000>;
+		};
+		emergency@28800000 {
+			label = "emergency";
+			reg = <0x28800000 0x2000000>;
+		};
+		data+ubi@2a800000 {
+			label = "data+ubi";
+			reg = <0x2a800000 0x55800000>;
+		};
+};
+
+&pcie0 {
+		reset-gpios = <&pinctrl0 62 GPIO_ACTIVE_LOW>;
+};
+
+&pcie1 {
+		reset-gpios = <&pinctrl0 27 GPIO_ACTIVE_LOW>;
+};
+
+&spi0 {
+		pinctrl-names = "default";
+		pinctrl-0 = <&pinctrl_spi>;
+		BCM6803@0 {
+			compatible = "brcm,bcm6803-spi";
+			spi-max-frequency = <20000000>;
+			interrupt-parent = <&pinctrl0>;
+			interrupts = <0 IRQ_TYPE_LEVEL_HIGH>;
+			reg = <0>;
+			spi-cpol;
+			spi-cpha;
+			rf-band = "ext_d";
+			i2c-base = <0x10406200>;
+			i2c-addr = <0x70>;
+			reset-gpios = <&pinctrl0 11 GPIO_ACTIVE_LOW>;
+			if-name = "moca0";
+		};
+};
+
+&i2c0 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_i2c>;
+	lm63: lm63@4c {
+		compatible = "national,lm63";
+		reg = <0x4c>;
+	};
+};
+
+&usb3_phy {
+	status = "okay";
+};
+
+&usb3 {
+	status = "okay";
+	dr_mode = "host";
+};
diff --git a/arch/arm/configs/gfrg200_defconfig b/arch/arm/configs/gfrg200_defconfig
new file mode 100644
index 0000000..81ba863
--- /dev/null
+++ b/arch/arm/configs/gfrg200_defconfig
@@ -0,0 +1,346 @@
+# CONFIG_LOCALVERSION_AUTO is not set
+# CONFIG_SWAP is not set
+CONFIG_SYSVIPC=y
+CONFIG_IRQ_DOMAIN_DEBUG=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_LOG_BUF_SHIFT=14
+CONFIG_NAMESPACES=y
+CONFIG_RELAY=y
+CONFIG_ANTIREBOOTLOOP=y
+CONFIG_BLK_DEV_INITRD=y
+# CONFIG_RD_BZIP2 is not set
+# CONFIG_RD_LZMA is not set
+# CONFIG_RD_XZ is not set
+# CONFIG_RD_LZO is not set
+# CONFIG_RD_LZ4 is not set
+CONFIG_EXPERT=y
+CONFIG_PRINTK_PERSIST=y
+CONFIG_PROFILING=y
+CONFIG_KPROBES=y
+CONFIG_MODULES=y
+CONFIG_MODULE_FORCE_LOAD=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+CONFIG_MODVERSIONS=y
+CONFIG_MODULE_SRCVERSION_ALL=y
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_ARCH_COMCERTO=y
+CONFIG_COMCERTO_DMA_COHERENT_SKB=y
+# CONFIG_ARM_ERRATA_643719 is not set
+CONFIG_ARM_ERRATA_720789=y
+CONFIG_ARM_ERRATA_754322=y
+CONFIG_ARM_ERRATA_764369=y
+CONFIG_PCI=y
+CONFIG_PCI_MSI=y
+CONFIG_PCI_DEBUG=y
+CONFIG_PCI_LS1024A=y
+CONFIG_SMP=y
+CONFIG_HAVE_ARM_ARCH_TIMER=y
+CONFIG_VMSPLIT_2G=y
+CONFIG_NR_CPUS=2
+CONFIG_PREEMPT=y
+CONFIG_THUMB2_KERNEL=y
+CONFIG_CMA=y
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_ARM_APPENDED_DTB=y
+CONFIG_ARM_ATAG_DTB_COMPAT=y
+CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND=y
+CONFIG_VFP=y
+CONFIG_NEON=y
+CONFIG_BINFMT_MISC=y
+# CONFIG_SUSPEND 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_TCP_CONG_ADVANCED=y
+CONFIG_TCP_CONG_BIC=y
+CONFIG_TCP_CONG_WESTWOOD=y
+CONFIG_TCP_CONG_HTCP=y
+CONFIG_TCP_CONG_HSTCP=y
+CONFIG_TCP_CONG_HYBLA=y
+CONFIG_TCP_CONG_SCALABLE=y
+CONFIG_TCP_CONG_LP=y
+CONFIG_TCP_CONG_VENO=y
+CONFIG_TCP_CONG_YEAH=y
+CONFIG_TCP_CONG_ILLINOIS=y
+CONFIG_IPV6=y
+CONFIG_NETFILTER=y
+# CONFIG_BRIDGE_NETFILTER is not set
+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_RTSP=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_LOG=m
+CONFIG_NETFILTER_XT_TARGET_NETMAP=m
+CONFIG_NETFILTER_XT_TARGET_REDIRECT=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_CONNTRACK=m
+CONFIG_NETFILTER_XT_MATCH_DSCP=m
+CONFIG_NETFILTER_XT_MATCH_ECN=m
+CONFIG_NETFILTER_XT_MATCH_HELPER=m
+CONFIG_NETFILTER_XT_MATCH_HL=m
+CONFIG_NETFILTER_XT_MATCH_IPRANGE=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_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_IP_NF_IPTABLES=m
+CONFIG_IP_NF_FILTER=m
+CONFIG_IP_NF_TARGET_REJECT=m
+CONFIG_IP_NF_NAT=m
+CONFIG_IP_NF_TARGET_MASQUERADE=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_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_FILTER=m
+CONFIG_IP6_NF_TARGET_REJECT=m
+CONFIG_IP6_NF_MANGLE=m
+CONFIG_IP6_NF_RAW=m
+CONFIG_IP6_NF_NAT=m
+CONFIG_BRIDGE=y
+CONFIG_VLAN_8021Q=y
+CONFIG_NET_SCHED=y
+CONFIG_NET_SCH_CBQ=m
+CONFIG_NET_SCH_HTB=m
+CONFIG_NET_SCH_CSZ=m
+CONFIG_NET_SCH_HFSC=m
+CONFIG_NET_SCH_PRIO=m
+CONFIG_NET_SCH_MULTIQ=m
+CONFIG_NET_SCH_RED=m
+CONFIG_NET_SCH_SFB=m
+CONFIG_NET_SCH_SFQ=m
+CONFIG_NET_SCH_TEQL=m
+CONFIG_NET_SCH_TBF=m
+CONFIG_NET_SCH_GRED=m
+CONFIG_NET_SCH_DSMARK=m
+CONFIG_NET_SCH_NETEM=m
+CONFIG_NET_SCH_DRR=m
+CONFIG_NET_SCH_MQPRIO=m
+CONFIG_NET_SCH_CHOKE=m
+CONFIG_NET_SCH_QFQ=m
+CONFIG_NET_SCH_CODEL=m
+CONFIG_NET_SCH_FQ_CODEL=m
+CONFIG_NET_SCH_FQ=m
+CONFIG_NET_SCH_HHF=m
+CONFIG_NET_SCH_PIE=m
+CONFIG_NET_SCH_INGRESS=m
+CONFIG_NET_CLS=y
+CONFIG_NET_CLS_ACT=y
+CONFIG_NET_CLS_TCINDEX=m
+CONFIG_NET_CLS_ROUTE4=m
+CONFIG_NET_CLS_ROUTE=y
+CONFIG_NET_CLS_FW=m
+CONFIG_NET_CLS_U32=m
+CONFIG_NET_CLS_RSVP=m
+CONFIG_NET_CLS_RSVP6=m
+CONFIG_NET_ACT_SKBEDIT=m
+CONFIG_NET_CLS_POLICE=y
+CONFIG_NET_ESTIMATOR=y
+CONFIG_NET_PKTGEN=m
+CONFIG_NET_QOS=y
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
+CONFIG_DMA_CMA=y
+CONFIG_CMA_SIZE_MBYTES=32
+CONFIG_MTD=y
+CONFIG_MTD_BLOCK=y
+CONFIG_MTD_CFI=y
+CONFIG_MTD_CFI_INTELEXT=y
+CONFIG_MTD_CFI_AMDSTD=y
+CONFIG_MTD_CFI_STAA=y
+CONFIG_MTD_PHYSMAP_OF=y
+CONFIG_MTD_NAND=y
+CONFIG_MTD_NAND_ECC_BCH=y
+CONFIG_MTD_NAND_LS1024A=y
+CONFIG_MTD_UBI=y
+CONFIG_MTD_UBI_WL_THRESHOLD=128
+CONFIG_MTD_UBI_BEB_LIMIT=25
+CONFIG_MTD_UBI_GLUEBI=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_SIZE=16384
+CONFIG_EEPROM_93CX6=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_SCSI_CONSTANTS=y
+# CONFIG_SCSI_LOWLEVEL is not set
+CONFIG_ATA=y
+# CONFIG_SATA_PMP is not set
+CONFIG_SATA_AHCI_PLATFORM=y
+# CONFIG_ATA_SFF is not set
+CONFIG_MD=y
+CONFIG_BLK_DEV_DM=y
+CONFIG_DM_VERITY=y
+CONFIG_NETDEVICES=y
+CONFIG_TUN=y
+# CONFIG_NET_VENDOR_ARC is not set
+# CONFIG_NET_CADENCE is not set
+# CONFIG_NET_VENDOR_BROADCOM is not set
+# CONFIG_NET_VENDOR_CIRRUS is not set
+# CONFIG_NET_VENDOR_FARADAY is not set
+# CONFIG_NET_VENDOR_HISILICON is not set
+# CONFIG_NET_VENDOR_INTEL is not set
+# CONFIG_NET_VENDOR_MARVELL is not set
+# CONFIG_NET_VENDOR_MICREL is not set
+# CONFIG_NET_VENDOR_NATSEMI is not set
+# CONFIG_NET_VENDOR_QUALCOMM is not set
+# CONFIG_NET_VENDOR_ROCKER is not set
+# CONFIG_NET_VENDOR_SAMSUNG is not set
+# CONFIG_NET_VENDOR_SEEQ is not set
+# CONFIG_NET_VENDOR_SMSC is not set
+# CONFIG_NET_VENDOR_STMICRO is not set
+# CONFIG_NET_VENDOR_VIA is not set
+# CONFIG_NET_VENDOR_WIZNET is not set
+CONFIG_PHYLIB=y
+CONFIG_AT803X_PHY=y
+CONFIG_FIXED_PHY=y
+# CONFIG_WLAN is not set
+# CONFIG_INPUT_MOUSEDEV 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_DEPRECATED_OPTIONS is not set
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_8250_DW=y
+# CONFIG_HW_RANDOM is not set
+CONFIG_I2C=y
+# CONFIG_I2C_COMPAT is not set
+CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_LS1024A=y
+CONFIG_SPI=y
+CONFIG_SPI_DESIGNWARE=y
+CONFIG_SPI_DW_MMIO=y
+CONFIG_GPIOLIB=y
+CONFIG_GPIO_SYSFS=y
+CONFIG_POWER_SUPPLY=y
+CONFIG_POWER_RESET=y
+CONFIG_POWER_RESET_SYSCON=y
+CONFIG_SENSORS_LM63=y
+CONFIG_WATCHDOG=y
+CONFIG_LS1024A_WATCHDOG=y
+CONFIG_USB=y
+CONFIG_USB_XHCI_HCD=y
+CONFIG_USB_STORAGE=y
+CONFIG_USB_DWC3=y
+# CONFIG_USB_DWC3_PCI is not set
+CONFIG_USB_SERIAL=y
+CONFIG_USB_SERIAL_FTDI_SIO=y
+CONFIG_USB_SERIAL_KEYSPAN=y
+CONFIG_USB_SERIAL_PL2303=y
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_CLASS_FLASH=y
+CONFIG_LEDS_GPIO=y
+CONFIG_LEDS_PWM=y
+CONFIG_LEDS_TRIGGERS=y
+CONFIG_LEDS_TRIGGER_TIMER=y
+CONFIG_LEDS_TRIGGER_ONESHOT=y
+CONFIG_LEDS_TRIGGER_HEARTBEAT=y
+CONFIG_LEDS_TRIGGER_BACKLIGHT=y
+CONFIG_LEDS_TRIGGER_CPU=y
+CONFIG_LEDS_TRIGGER_GPIO=y
+CONFIG_LEDS_TRIGGER_DEFAULT_ON=y
+# CONFIG_IOMMU_SUPPORT is not set
+CONFIG_PWM=y
+CONFIG_PWM_LS1024A=y
+CONFIG_PHY_LS1024A_SNOWBUSH=y
+CONFIG_PHY_LS1024A_USB3=y
+CONFIG_FIRMWARE_MEMMAP=y
+CONFIG_EXT4_FS=y
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_TMPFS=y
+CONFIG_UBIFS_FS=y
+CONFIG_SQUASHFS=y
+CONFIG_SQUASHFS_DECOMP_MULTI=y
+CONFIG_SQUASHFS_XZ=y
+CONFIG_SQUASHFS_EMBEDDED=y
+CONFIG_NFS_FS=y
+CONFIG_NFS_V4=y
+CONFIG_NFS_V4_1=y
+CONFIG_NFS_V4_2=y
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_NLS_UTF8=y
+CONFIG_PRINTK_TIME=y
+CONFIG_DYNAMIC_DEBUG=y
+CONFIG_DEBUG_SECTION_MISMATCH=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE=0x0
+CONFIG_DEBUG_STACK_USAGE=y
+CONFIG_DEBUG_MEMORY_INIT=y
+CONFIG_LOCKUP_DETECTOR=y
+CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC=y
+CONFIG_DEFAULT_HUNG_TASK_TIMEOUT=30
+CONFIG_BOOTPARAM_HUNG_TASK_PANIC=y
+CONFIG_PANIC_ON_OOPS=y
+CONFIG_PANIC_TIMEOUT=3
+CONFIG_SCHEDSTATS=y
+CONFIG_SCHED_STACK_END_CHECK=y
+CONFIG_DEBUG_TIMEKEEPING=y
+CONFIG_TIMER_STATS=y
+CONFIG_DEBUG_SPINLOCK=y
+CONFIG_DEBUG_MUTEXES=y
+CONFIG_FUNCTION_TRACER=y
+CONFIG_IRQSOFF_TRACER=y
+CONFIG_FUNCTION_PROFILER=y
+CONFIG_DEBUG_LL=y
+CONFIG_EARLY_PRINTK=y
+CONFIG_CRYPTO_CCM=y
+CONFIG_CRYPTO_GCM=y
+CONFIG_CRYPTO_ARC4=y
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
+# CONFIG_CRYPTO_HW is not set
+CONFIG_ARM_CRYPTO=y
+CONFIG_CRYPTO_SHA1_ARM=y
+CONFIG_CRYPTO_SHA256_ARM=y
+CONFIG_CRC_CCITT=y
+CONFIG_CRC_T10DIF=y
+CONFIG_CRC_ITU_T=y
+CONFIG_CRC7=y
+CONFIG_LIBCRC32C=y
diff --git a/arch/arm/configs/gfsc100_defconfig b/arch/arm/configs/gfsc100_defconfig
new file mode 100644
index 0000000..03f2522
--- /dev/null
+++ b/arch/arm/configs/gfsc100_defconfig
@@ -0,0 +1,374 @@
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_KERNEL_XZ=y
+# CONFIG_SWAP is not set
+CONFIG_SYSVIPC=y
+CONFIG_IRQ_DOMAIN_DEBUG=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_LOG_BUF_SHIFT=14
+CONFIG_RELAY=y
+CONFIG_ANTIREBOOTLOOP=y
+CONFIG_BLK_DEV_INITRD=y
+# CONFIG_RD_BZIP2 is not set
+# CONFIG_RD_LZMA is not set
+# CONFIG_RD_LZO is not set
+# CONFIG_RD_LZ4 is not set
+CONFIG_EXPERT=y
+CONFIG_PRINTK_PERSIST=y
+CONFIG_PERF_EVENTS=y
+CONFIG_KPROBES=y
+CONFIG_MODULES=y
+CONFIG_MODULE_FORCE_LOAD=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+CONFIG_MODVERSIONS=y
+CONFIG_MODULE_SRCVERSION_ALL=y
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_ARCH_COMCERTO=y
+# CONFIG_COMCERTO_FP is not set
+# CONFIG_ARM_ERRATA_643719 is not set
+CONFIG_ARM_ERRATA_720789=y
+CONFIG_ARM_ERRATA_754322=y
+CONFIG_ARM_ERRATA_764369=y
+CONFIG_SMP=y
+CONFIG_HAVE_ARM_ARCH_TIMER=y
+CONFIG_VMSPLIT_2G=y
+CONFIG_NR_CPUS=2
+CONFIG_PREEMPT=y
+CONFIG_THUMB2_KERNEL=y
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_ARM_APPENDED_DTB=y
+CONFIG_ARM_ATAG_DTB_COMPAT=y
+CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND=y
+CONFIG_VFP=y
+CONFIG_NEON=y
+CONFIG_BINFMT_MISC=y
+# CONFIG_SUSPEND is not set
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=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_IPV6=y
+CONFIG_NETFILTER=y
+# CONFIG_NETFILTER_ADVANCED is not set
+# CONFIG_NETFILTER_NETLINK_LOG is not set
+# CONFIG_NF_CONNTRACK is not set
+# CONFIG_NETFILTER_XT_MARK is not set
+# CONFIG_NETFILTER_XT_TARGET_NFLOG is not set
+# CONFIG_NETFILTER_XT_TARGET_TCPMSS is not set
+# CONFIG_NETFILTER_XT_MATCH_ADDRTYPE is not set
+# CONFIG_NETFILTER_XT_MATCH_POLICY is not set
+# CONFIG_NF_LOG_ARP is not set
+# CONFIG_IP6_NF_MATCH_IPV6HEADER is not set
+CONFIG_BRIDGE=y
+CONFIG_NET_PKTGEN=m
+# CONFIG_WIRELESS is not set
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
+CONFIG_MTD=y
+CONFIG_MTD_BLOCK=y
+CONFIG_MTD_CFI=y
+CONFIG_MTD_CFI_INTELEXT=y
+CONFIG_MTD_CFI_AMDSTD=y
+CONFIG_MTD_CFI_STAA=y
+CONFIG_MTD_PHYSMAP_OF=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_SIZE=16384
+CONFIG_EEPROM_93CX6=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_SCSI_CONSTANTS=y
+# CONFIG_SCSI_LOWLEVEL is not set
+CONFIG_ATA=y
+# CONFIG_SATA_PMP is not set
+CONFIG_SATA_AHCI_PLATFORM=y
+# CONFIG_ATA_SFF is not set
+CONFIG_MD=y
+CONFIG_BLK_DEV_DM=y
+CONFIG_DM_CRYPT=m
+CONFIG_DM_VERITY=y
+CONFIG_NETDEVICES=y
+CONFIG_TUN=y
+# CONFIG_NET_VENDOR_ARC is not set
+# CONFIG_NET_CADENCE is not set
+# CONFIG_NET_VENDOR_BROADCOM is not set
+# CONFIG_NET_VENDOR_CIRRUS is not set
+# CONFIG_NET_VENDOR_FARADAY is not set
+# CONFIG_NET_VENDOR_HISILICON is not set
+# CONFIG_NET_VENDOR_INTEL is not set
+# CONFIG_NET_VENDOR_MARVELL is not set
+# CONFIG_NET_VENDOR_MICREL is not set
+# CONFIG_NET_VENDOR_NATSEMI is not set
+# CONFIG_NET_VENDOR_QUALCOMM is not set
+# CONFIG_NET_VENDOR_ROCKER is not set
+# CONFIG_NET_VENDOR_SAMSUNG is not set
+# CONFIG_NET_VENDOR_SEEQ is not set
+# CONFIG_NET_VENDOR_SMSC is not set
+# CONFIG_NET_VENDOR_STMICRO is not set
+# CONFIG_NET_VENDOR_VIA is not set
+# CONFIG_NET_VENDOR_WIZNET is not set
+CONFIG_PHYLIB=y
+CONFIG_MARVELL_PHY=y
+CONFIG_FIXED_PHY=y
+# CONFIG_WLAN is not set
+# CONFIG_INPUT_MOUSEDEV 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_DEPRECATED_OPTIONS is not set
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_8250_DW=y
+# CONFIG_HW_RANDOM is not set
+CONFIG_TCG_TPM=y
+CONFIG_TCG_TIS_I2C_INFINEON=y
+CONFIG_I2C=y
+# CONFIG_I2C_COMPAT is not set
+CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_MUX=y
+CONFIG_I2C_LS1024A=y
+CONFIG_SPI=y
+CONFIG_SPI_DESIGNWARE=y
+CONFIG_SPI_DW_MMIO=y
+CONFIG_GPIOLIB=y
+CONFIG_GPIO_SYSFS=y
+CONFIG_POWER_SUPPLY=y
+CONFIG_POWER_RESET=y
+CONFIG_POWER_RESET_SYSCON=y
+CONFIG_SENSORS_LM63=y
+CONFIG_WATCHDOG=y
+CONFIG_LS1024A_WATCHDOG=y
+CONFIG_MEDIA_SUPPORT=y
+CONFIG_MEDIA_DIGITAL_TV_SUPPORT=y
+CONFIG_MEDIA_USB_SUPPORT=y
+CONFIG_DVB_USB_V2=y
+CONFIG_DVB_USB_SC100=m
+CONFIG_CYPRESS_FIRMWARE=y
+# CONFIG_MEDIA_SUBDRV_AUTOSELECT is not set
+# CONFIG_MEDIA_TUNER_SIMPLE is not set
+# CONFIG_MEDIA_TUNER_TDA8290 is not set
+# CONFIG_MEDIA_TUNER_TDA827X is not set
+# CONFIG_MEDIA_TUNER_TDA18271 is not set
+# CONFIG_MEDIA_TUNER_TDA9887 is not set
+# CONFIG_MEDIA_TUNER_TEA5761 is not set
+# CONFIG_MEDIA_TUNER_TEA5767 is not set
+# CONFIG_MEDIA_TUNER_MT20XX is not set
+# CONFIG_MEDIA_TUNER_MT2060 is not set
+# CONFIG_MEDIA_TUNER_MT2063 is not set
+# CONFIG_MEDIA_TUNER_MT2266 is not set
+# CONFIG_MEDIA_TUNER_MT2131 is not set
+# CONFIG_MEDIA_TUNER_QT1010 is not set
+# CONFIG_MEDIA_TUNER_XC2028 is not set
+# CONFIG_MEDIA_TUNER_XC5000 is not set
+# CONFIG_MEDIA_TUNER_XC4000 is not set
+# CONFIG_MEDIA_TUNER_MXL5005S is not set
+# CONFIG_MEDIA_TUNER_MXL5007T is not set
+# CONFIG_MEDIA_TUNER_MC44S803 is not set
+# CONFIG_MEDIA_TUNER_MAX2165 is not set
+# CONFIG_MEDIA_TUNER_TDA18218 is not set
+# CONFIG_MEDIA_TUNER_FC0011 is not set
+# CONFIG_MEDIA_TUNER_FC0012 is not set
+# CONFIG_MEDIA_TUNER_FC0013 is not set
+# CONFIG_MEDIA_TUNER_TDA18212 is not set
+# CONFIG_MEDIA_TUNER_E4000 is not set
+# CONFIG_MEDIA_TUNER_FC2580 is not set
+CONFIG_MEDIA_TUNER_M88RS6000T=y
+# CONFIG_MEDIA_TUNER_TUA9001 is not set
+# CONFIG_MEDIA_TUNER_SI2157 is not set
+# CONFIG_MEDIA_TUNER_IT913X is not set
+# CONFIG_MEDIA_TUNER_R820T is not set
+# CONFIG_MEDIA_TUNER_MXL301RF is not set
+# CONFIG_MEDIA_TUNER_QM1D1C0042 is not set
+# CONFIG_DVB_STB0899 is not set
+# CONFIG_DVB_STB6100 is not set
+# CONFIG_DVB_STV090x is not set
+# CONFIG_DVB_STV6110x is not set
+CONFIG_DVB_M88DS3103=y
+CONFIG_DVB_M88DS3103_M88RS6000_FIRMWARE="dvbsky-firmware/dvb-demod-m88rs6000.fw"
+# CONFIG_DVB_DRXK is not set
+# CONFIG_DVB_TDA18271C2DD is not set
+# CONFIG_DVB_SI2165 is not set
+# CONFIG_DVB_CX24110 is not set
+# CONFIG_DVB_CX24123 is not set
+# CONFIG_DVB_MT312 is not set
+# CONFIG_DVB_ZL10036 is not set
+# CONFIG_DVB_ZL10039 is not set
+# CONFIG_DVB_S5H1420 is not set
+# CONFIG_DVB_STV0288 is not set
+# CONFIG_DVB_STB6000 is not set
+# CONFIG_DVB_STV0299 is not set
+# CONFIG_DVB_STV6110 is not set
+# CONFIG_DVB_STV0900 is not set
+# CONFIG_DVB_TDA8083 is not set
+# CONFIG_DVB_TDA10086 is not set
+# CONFIG_DVB_TDA8261 is not set
+# CONFIG_DVB_VES1X93 is not set
+# CONFIG_DVB_TUNER_ITD1000 is not set
+# CONFIG_DVB_TUNER_CX24113 is not set
+# CONFIG_DVB_TDA826X is not set
+# CONFIG_DVB_TUA6100 is not set
+# CONFIG_DVB_CX24116 is not set
+# CONFIG_DVB_CX24117 is not set
+# CONFIG_DVB_SI21XX is not set
+# CONFIG_DVB_TS2020 is not set
+# CONFIG_DVB_DS3000 is not set
+# CONFIG_DVB_MB86A16 is not set
+# CONFIG_DVB_TDA10071 is not set
+# CONFIG_DVB_SP8870 is not set
+# CONFIG_DVB_SP887X is not set
+# CONFIG_DVB_CX22700 is not set
+# CONFIG_DVB_CX22702 is not set
+# CONFIG_DVB_S5H1432 is not set
+# CONFIG_DVB_DRXD is not set
+# CONFIG_DVB_L64781 is not set
+# CONFIG_DVB_TDA1004X is not set
+# CONFIG_DVB_NXT6000 is not set
+# CONFIG_DVB_MT352 is not set
+# CONFIG_DVB_ZL10353 is not set
+# CONFIG_DVB_DIB3000MB is not set
+# CONFIG_DVB_DIB3000MC is not set
+# CONFIG_DVB_DIB7000M is not set
+# CONFIG_DVB_DIB7000P is not set
+# CONFIG_DVB_DIB9000 is not set
+# CONFIG_DVB_TDA10048 is not set
+# CONFIG_DVB_AF9013 is not set
+# CONFIG_DVB_EC100 is not set
+# CONFIG_DVB_HD29L2 is not set
+# CONFIG_DVB_STV0367 is not set
+# CONFIG_DVB_CXD2820R is not set
+# CONFIG_DVB_RTL2830 is not set
+# CONFIG_DVB_RTL2832 is not set
+# CONFIG_DVB_SI2168 is not set
+# CONFIG_DVB_VES1820 is not set
+# CONFIG_DVB_TDA10021 is not set
+# CONFIG_DVB_TDA10023 is not set
+# CONFIG_DVB_STV0297 is not set
+# CONFIG_DVB_NXT200X is not set
+# CONFIG_DVB_OR51211 is not set
+# CONFIG_DVB_OR51132 is not set
+# CONFIG_DVB_BCM3510 is not set
+# CONFIG_DVB_LGDT330X is not set
+# CONFIG_DVB_LGDT3305 is not set
+# CONFIG_DVB_LGDT3306A is not set
+# CONFIG_DVB_LG2160 is not set
+# CONFIG_DVB_S5H1409 is not set
+# CONFIG_DVB_AU8522_DTV is not set
+# CONFIG_DVB_S5H1411 is not set
+# CONFIG_DVB_S921 is not set
+# CONFIG_DVB_DIB8000 is not set
+# CONFIG_DVB_MB86A20S is not set
+# CONFIG_DVB_TC90522 is not set
+# CONFIG_DVB_PLL is not set
+# CONFIG_DVB_TUNER_DIB0070 is not set
+# CONFIG_DVB_TUNER_DIB0090 is not set
+# CONFIG_DVB_DRX39XYJ is not set
+# CONFIG_DVB_LNBP21 is not set
+# CONFIG_DVB_LNBP22 is not set
+# CONFIG_DVB_ISL6405 is not set
+# CONFIG_DVB_ISL6421 is not set
+# CONFIG_DVB_ISL6423 is not set
+# CONFIG_DVB_A8293 is not set
+# CONFIG_DVB_SP2 is not set
+# CONFIG_DVB_LGS8GL5 is not set
+# CONFIG_DVB_LGS8GXX is not set
+# CONFIG_DVB_ATBM8830 is not set
+# CONFIG_DVB_TDA665x is not set
+# CONFIG_DVB_IX2505V is not set
+# CONFIG_DVB_M88RS2000 is not set
+# CONFIG_DVB_AF9033 is not set
+CONFIG_USB=y
+CONFIG_USB_XHCI_HCD=y
+CONFIG_USB_STORAGE=y
+CONFIG_USB_DWC3=y
+CONFIG_USB_DWC2=y
+CONFIG_USB_SERIAL=y
+CONFIG_USB_SERIAL_FTDI_SIO=y
+CONFIG_USB_SERIAL_KEYSPAN=y
+CONFIG_USB_SERIAL_PL2303=y
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_CLASS_FLASH=y
+CONFIG_LEDS_GPIO=y
+CONFIG_LEDS_PWM=y
+CONFIG_LEDS_TRIGGERS=y
+CONFIG_LEDS_TRIGGER_TIMER=y
+CONFIG_LEDS_TRIGGER_ONESHOT=y
+CONFIG_LEDS_TRIGGER_HEARTBEAT=y
+CONFIG_LEDS_TRIGGER_BACKLIGHT=y
+CONFIG_LEDS_TRIGGER_CPU=y
+CONFIG_LEDS_TRIGGER_GPIO=y
+CONFIG_LEDS_TRIGGER_DEFAULT_ON=y
+# CONFIG_IOMMU_SUPPORT is not set
+CONFIG_PWM=y
+CONFIG_PWM_LS1024A=y
+CONFIG_PHY_LS1024A_SNOWBUSH=y
+CONFIG_PHY_LS1024A_USB2=y
+CONFIG_PHY_LS1024A_USB3=y
+CONFIG_FIRMWARE_MEMMAP=y
+CONFIG_EXT4_FS=m
+CONFIG_MSDOS_FS=m
+CONFIG_VFAT_FS=m
+CONFIG_TMPFS=y
+CONFIG_CONFIGFS_FS=m
+CONFIG_SQUASHFS=y
+CONFIG_SQUASHFS_DECOMP_MULTI=y
+CONFIG_SQUASHFS_XZ=y
+CONFIG_SQUASHFS_EMBEDDED=y
+CONFIG_NFS_FS=y
+CONFIG_NFS_V4=y
+CONFIG_NFS_V4_1=y
+CONFIG_NFS_V4_2=y
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_NLS_UTF8=y
+CONFIG_PRINTK_TIME=y
+CONFIG_DYNAMIC_DEBUG=y
+CONFIG_DEBUG_SECTION_MISMATCH=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE=0x0
+CONFIG_DEBUG_STACK_USAGE=y
+CONFIG_DEBUG_MEMORY_INIT=y
+CONFIG_LOCKUP_DETECTOR=y
+CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC=y
+CONFIG_DEFAULT_HUNG_TASK_TIMEOUT=120
+CONFIG_BOOTPARAM_HUNG_TASK_PANIC=y
+CONFIG_PANIC_ON_OOPS=y
+CONFIG_PANIC_TIMEOUT=3
+CONFIG_SCHEDSTATS=y
+CONFIG_SCHED_STACK_END_CHECK=y
+CONFIG_DEBUG_TIMEKEEPING=y
+CONFIG_TIMER_STATS=y
+CONFIG_DEBUG_SPINLOCK=y
+CONFIG_DEBUG_MUTEXES=y
+CONFIG_FUNCTION_TRACER=y
+CONFIG_IRQSOFF_TRACER=y
+CONFIG_FUNCTION_PROFILER=y
+CONFIG_DEBUG_LL=y
+CONFIG_EARLY_PRINTK=y
+CONFIG_CRYPTO_CCM=y
+CONFIG_CRYPTO_GCM=y
+CONFIG_CRYPTO_ARC4=y
+CONFIG_CRYPTO_DEFLATE=y
+CONFIG_CRYPTO_LZO=y
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
+# CONFIG_CRYPTO_HW is not set
+CONFIG_ARM_CRYPTO=y
+CONFIG_CRYPTO_SHA1_ARM=y
+CONFIG_CRYPTO_SHA256_ARM=y
+CONFIG_CRC_CCITT=y
+CONFIG_CRC16=y
+CONFIG_CRC_T10DIF=y
+CONFIG_CRC_ITU_T=y
+CONFIG_CRC7=y
+CONFIG_LIBCRC32C=y
diff --git a/arch/arm/kernel/devtree.c b/arch/arm/kernel/devtree.c
index 11c54de..cf03bb4 100644
--- a/arch/arm/kernel/devtree.c
+++ b/arch/arm/kernel/devtree.c
@@ -8,6 +8,8 @@
  * published by the Free Software Foundation.
  */
 
+#define DEBUG
+
 #include <linux/init.h>
 #include <linux/export.h>
 #include <linux/errno.h>
@@ -86,6 +88,7 @@
 	if (!cpus)
 		return;
 
+	pr_debug("is_smp %d MPIDR %08x\n", is_smp(), read_cpuid_mpidr());
 	for_each_child_of_node(cpus, cpu) {
 		u32 hwid;
 
diff --git a/arch/arm/mach-comcerto/Kconfig b/arch/arm/mach-comcerto/Kconfig
new file mode 100644
index 0000000..d2b2f13
--- /dev/null
+++ b/arch/arm/mach-comcerto/Kconfig
@@ -0,0 +1,29 @@
+menu "Freescale Comerto 2000"
+	depends on ARCH_MULTI_V7
+
+config ARCH_COMCERTO
+	bool "Freescale Comerto 2000"
+	depends on ARCH_MULTI_V7
+	select ARM_GIC
+	select PCI_DOMAINS if PCI
+	select CLKSRC_MMIO
+	select MFD_SYSCON
+	select HAVE_ARM_SCU if SMP
+	select HAVE_ARM_TWD if SMP
+	select ARCH_HAS_RESET_CONTROLLER
+	select PINCTRL
+	select PINCTRL_LS1024A
+	select MII
+
+endmenu
+
+config COMCERTO_FP
+	bool "Comcerto Fast Path Support"
+	depends on ARCH_COMCERTO
+	default y
+
+config COMCERTO_DMA_COHERENT_SKB
+	bool "Support for DMA coherent SKB heads"
+	depends on ARCH_COMCERTO
+	depends on DMA_CMA
+	default n
diff --git a/arch/arm/mach-comcerto/Makefile b/arch/arm/mach-comcerto/Makefile
new file mode 100644
index 0000000..87dd2c7
--- /dev/null
+++ b/arch/arm/mach-comcerto/Makefile
@@ -0,0 +1,7 @@
+ccflags-$(CONFIG_ARCH_MULTIPLATFORM) := -I$(srctree)/$(src)/include
+
+obj-y := board-generic.o comcerto-restart.o common.o io.o clk-comcerto2000.o \
+         platsmp.o comcerto-vwd.o
+
+obj-$(CONFIG_SMP)			+= headsmp.o
+AFLAGS_headsmp.o			:=-Wa,-march=armv7-a
diff --git a/arch/arm/mach-comcerto/board-generic.c b/arch/arm/mach-comcerto/board-generic.c
new file mode 100644
index 0000000..4421f0c
--- /dev/null
+++ b/arch/arm/mach-comcerto/board-generic.c
@@ -0,0 +1,529 @@
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clocksource.h>
+#include <linux/clockchips.h>
+#include <linux/sched_clock.h>
+#include <linux/io.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/printk.h>
+#include <linux/irqdomain.h>
+#include <linux/interrupt.h>
+
+#include <asm/hardware/cache-l2x0.h>
+
+/* PFE */
+#include <linux/phy.h>
+#include <linux/clkdev.h>
+
+#include <asm/mach/arch.h>
+#include <asm/irq.h>
+#include <mach/hardware.h>
+#include "common.h"
+
+static unsigned int persistent_mult, persistent_shift;
+unsigned long axi_clk_rate;
+static void __iomem *timer_base;
+
+
+/* We need to explicitly enable certain clocks. Otherwise, they are considered
+ * unused and clk_disable_unused() will disable them and freeze our system. */
+static const char *clks_to_enable[] = {
+	"arm_clk",
+	"axi_clk",
+	"ddr_clk",
+	"l2cc_clk",
+
+	"hfe_core_clk", /* PFE */
+
+	"sata_oob_clk", /* SATA */
+	"sata_pmu_clk", /* SATA */
+	NULL
+};
+static int __init c2k_enable_clks(void)
+{
+	const char **c;
+	struct device_node *node;
+	struct clk *clk;
+	struct of_phandle_args clkspec;
+	for (c = clks_to_enable; *c != NULL; c++) {
+		node = of_find_node_by_name(NULL, *c);
+		clkspec.np = node;
+		clkspec.args_count = 0;
+		clk = of_clk_get_from_provider(&clkspec);
+
+		if (!IS_ERR(clk)) {
+			clk_prepare(clk);
+			clk_enable(clk);
+		} else {
+			pr_warn("failed to lookup clock node %s\n",
+				*c);
+		}
+	}
+	return 0;
+
+
+	/* Set the SATA PMU clock to 30 MHZ and OOB clock to 125MHZ */
+	node = of_find_node_by_name(NULL, "sata_oob_clk");
+	clkspec.np = node;
+	clkspec.args_count = 0;
+	clk = of_clk_get_from_provider(&clkspec);
+
+	if (!IS_ERR(clk)) {
+		clk_set_rate(clk,125000000);
+	} else {
+		BUG();
+	}
+	node = of_find_node_by_name(NULL, "sata_pmu_clk");
+	clkspec.np = node;
+	clkspec.args_count = 0;
+	clk = of_clk_get_from_provider(&clkspec);
+
+	if (!IS_ERR(clk)) {
+		clk_set_rate(clk,30000000);
+	} else {
+		BUG();
+	}
+
+}
+
+late_initcall(c2k_enable_clks);
+
+struct clk_lookup axi_lk = {
+	.dev_id = NULL,
+	.con_id = "axi",
+};
+
+struct clk_lookup gemtx_lk = {
+	.dev_id = NULL,
+	.con_id = "gemtx",
+};
+
+static int __init c2k_register_clks(void)
+{
+	struct device_node *node;
+	struct clk *clk;
+	struct of_phandle_args clkspec;
+	node = of_find_node_by_name(NULL, "axi_clk");
+	clkspec.np = node;
+	clkspec.args_count = 0;
+	clk = of_clk_get_from_provider(&clkspec);
+	if (!IS_ERR(clk)) {
+		axi_lk.clk = clk;
+		clkdev_add(&axi_lk);
+	} else {
+		pr_warn("failed to lookup clock node axi_clk\n");
+	}
+
+
+	node = of_find_node_by_name(NULL, "gemtx_clk");
+	clkspec.np = node;
+	clkspec.args_count = 0;
+	clk = of_clk_get_from_provider(&clkspec);
+	if (!IS_ERR(clk)) {
+		gemtx_lk.clk = clk;
+		clkdev_add(&gemtx_lk);
+	} else {
+		pr_warn("failed to lookup clock node gemtx_clk\n");
+	}
+	return 0;
+}
+
+late_initcall(c2k_register_clks);
+
+static int c2k_timer_set_next_event(unsigned long cycles,
+					 struct clock_event_device *evt);
+static void c2k_timer_set_mode(enum clock_event_mode mode,
+				    struct clock_event_device *evt);
+
+static struct clock_event_device clockevent_c2k = {
+	.features       = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, // TODO
+	.rating		= 300,
+	.set_next_event	= c2k_timer_set_next_event,
+	.set_mode	= c2k_timer_set_mode,
+	.name		= "timer1",
+};
+
+#define timer_mask(timer)	(1 << timer)
+
+/*
+ * Routine to catch timer interrupts
+ */
+static irqreturn_t comcerto_timer1_interrupt(int irq, void *dev_id)
+{
+	u32 status;
+	struct clock_event_device *dev = &clockevent_c2k;
+
+	status = __raw_readl(timer_base + COMCERTO_TIMER_STATUS) & __raw_readl(timer_base + COMCERTO_TIMER_IRQ_MASK);
+
+	/* timer1 expired */
+	if (status & timer_mask(1)) {
+		/* we need to disable interrupt to simulate ONESHOT mode,
+		   do it before clearing the interrupt to avoid race */
+		if (dev->mode != CLOCK_EVT_MODE_PERIODIC)
+			__raw_writel(__raw_readl(timer_base + COMCERTO_TIMER_IRQ_MASK) & ~(1 << (1)), timer_base + COMCERTO_TIMER_IRQ_MASK);
+
+		__raw_writel(1 << (1), timer_base + COMCERTO_TIMER_STATUS_CLR);
+		dev->event_handler(dev);
+		return IRQ_HANDLED;
+	}
+
+	return IRQ_NONE;
+}
+
+static struct irqaction comcerto_timer1_irq = {
+	.name		= "timer1",
+	.flags		= IRQF_TIMER,
+	.handler	= comcerto_timer1_interrupt,
+};
+
+
+static int c2k_timer_set_next_event(unsigned long cycles,
+					 struct clock_event_device *evt)
+{
+	pr_err("%s cycles %lu\n", __func__, cycles);
+	/* now write correct bound and clear interrupt status.
+	   Writing high bound register automatically resets count to low bound value.
+	   For very small bound values it's possible that we ack the interrupt _after_ the timer has already expired,
+	   this is not very serious because the interrupt will be asserted again in a very short time */
+	__raw_writel(cycles & 0x3FFFFFFF, timer_base + COMCERTO_TIMER1_HIGH_BOUND);
+	__raw_writel(1 << 1, timer_base + COMCERTO_TIMER_STATUS_CLR);
+
+	/* enable interrupt for ONESHOT mode */
+	if (evt->mode == CLOCK_EVT_MODE_ONESHOT)
+		__raw_writel(__raw_readl(timer_base + COMCERTO_TIMER_IRQ_MASK) | (1 << 1), timer_base + COMCERTO_TIMER_IRQ_MASK);
+
+	return 0;
+}
+
+static void c2k_timer_set_mode(enum clock_event_mode mode,
+				    struct clock_event_device *evt)
+{
+	u32 period;
+	pr_err("%s mode %d\n", __func__, mode);
+
+	__raw_writel(__raw_readl(timer_base + COMCERTO_TIMER_IRQ_MASK) & ~(1 << (1)), timer_base + COMCERTO_TIMER_IRQ_MASK);
+
+	switch (mode) {
+	case CLOCK_EVT_MODE_PERIODIC:
+		period = axi_clk_rate / HZ;
+		period -= 1;
+		__raw_writel(period & 0x3FFFFFFF, timer_base + COMCERTO_TIMER1_HIGH_BOUND);
+		__raw_writel(__raw_readl(timer_base + COMCERTO_TIMER_IRQ_MASK) | (1 << (1)), timer_base + COMCERTO_TIMER_IRQ_MASK);
+		break;
+	case CLOCK_EVT_MODE_ONESHOT:
+		break;
+	case CLOCK_EVT_MODE_UNUSED:
+	case CLOCK_EVT_MODE_SHUTDOWN:
+	case CLOCK_EVT_MODE_RESUME:
+		break;
+	}
+}
+
+static void __init c2k_clockevent_init(int timer1_irq) {
+	/* Clear all the timers except timer0  */
+	__raw_writel(COMCERTO_TIMER_CSP, timer_base + COMCERTO_TIMER_STATUS);
+
+	/* Register interrupt handler for interrupt on IRQ_TIMERB*/
+	irq_set_irq_type(timer1_irq, IRQ_TYPE_EDGE_RISING);
+	setup_irq(timer1_irq, &comcerto_timer1_irq);
+	clockevent_c2k.cpumask = cpu_possible_mask;
+	clockevent_c2k.irq = timer1_irq;
+	clockevents_config_and_register(&clockevent_c2k, axi_clk_rate,
+					1,
+					0x3fffffff);
+}
+
+
+static u64 notrace c2k_read_sched_clock(void)
+{
+	return readl_relaxed(timer_base + COMCERTO_TIMER2_CURRENT_COUNT);
+}
+
+static void __init _ls1024a_timer_init(int timer1_irq, struct clk *clk_axi)
+{
+	int ret;
+
+	axi_clk_rate = clk_get_rate(clk_axi);
+	/*
+	err = twd_local_timer_register(&twd_local_timer);
+	if (err)
+		pr_err("twd_local_timer_register failed %d\n", err);
+		*/
+	__raw_writel(0, timer_base + COMCERTO_TIMER2_CTRL);
+	__raw_writel(0, timer_base + COMCERTO_TIMER2_LOW_BOUND);
+	__raw_writel(0xffffffff, timer_base + COMCERTO_TIMER2_HIGH_BOUND);
+	ret = clocksource_mmio_init(timer_base + COMCERTO_TIMER2_CURRENT_COUNT, "timer2", axi_clk_rate,
+			250, 32, clocksource_mmio_readl_up);
+	clocks_calc_mult_shift(&persistent_mult, &persistent_shift,
+			axi_clk_rate, NSEC_PER_SEC, 120000);
+	sched_clock_register(c2k_read_sched_clock, 32, axi_clk_rate);
+	c2k_clockevent_init(timer1_irq);
+}
+
+static void __init ls1024a_timer_init_dt(struct device_node *np)
+{
+	struct clk *clk_axi;
+	int irq;
+
+	timer_base = of_iomap(np, 0);
+	WARN_ON(!timer_base);
+	irq = irq_of_parse_and_map(np, 1);
+
+	clk_axi = of_clk_get(np, 0);
+	if (IS_ERR(clk_axi)) {
+		pr_err("LS1024A timer: unable to get AXI clk\n");
+		return;
+	}
+
+	_ls1024a_timer_init(irq, clk_axi);
+}
+
+CLOCKSOURCE_OF_DECLARE(ls1024a_timer, "fsl,ls1024a-gpt", ls1024a_timer_init_dt);
+
+
+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,
+        },
+
+};
+
+static struct comcerto_pfe_platform_data optimus_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 struct comcerto_pfe_platform_data gfsc100_pfe_pdata = {
+	.comcerto_eth_pdata[0] = {
+		.name = "unused",
+		.phy_flags = GEMAC_NO_PHY,
+		.gem_id = 0,
+	},
+
+	.comcerto_eth_pdata[1] = {
+		.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_PHY_RGMII_ADD_DELAY,
+		.bus_id = 0,
+		.phy_id = 1,
+		.gem_id = 1,
+		.mac_addr = (u8[])GEM1_MAC,
+	},
+
+	.comcerto_eth_pdata[2] = {
+		.name = "unused",
+		.phy_flags = GEMAC_NO_PHY,
+		.gem_id = 2,
+	},
+
+	/**
+	 * 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 = 0xFFFFFFFD,
+		.mdc_div = 96,
+		.irq = {
+			[1] = PHY_POLL,
+		},
+	},
+};
+
+static u64 comcerto_pfe_dma_mask = DMA_BIT_MASK(32);
+
+static struct platform_device comcerto_pfe_device = {
+	.name		= "pfe",
+	.id		= 0,
+	.dev		= {
+		.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 int is_mac_zero(u8 *buf)
+{
+        unsigned long dm;
+        for (dm = 0; dm < 6; dm++){
+		if ((*(buf+dm)) != 0)
+			return 1;
+	}
+	return 0;
+}
+
+static int __init mac_addr_atoi(u8 mac_addr[], char *mac_addr_str)
+{
+	int i, j, k;
+	int str_incr_cnt = 0;
+
+	if (*mac_addr_str == ',') {
+		mac_addr_str++;
+		str_incr_cnt++;
+		return str_incr_cnt;
+	}
+
+	for (i = 0; i < 6; i++) {
+
+		j = hex_to_bin(*mac_addr_str++);
+		str_incr_cnt++;
+		if (j == -1)
+			return str_incr_cnt;
+
+		k = hex_to_bin(*mac_addr_str++);
+		str_incr_cnt++;
+		if (k == -1)
+			return str_incr_cnt;
+
+		mac_addr_str++;
+		str_incr_cnt++;
+		mac_addr[i] = (j << 4) + k;
+	}
+
+	return str_incr_cnt;
+}
+
+static u8 c2k_mac_addr[3][14];
+
+static int __init mac_addr_setup(char *str)
+{
+	int str_incr_cnt = 0;
+
+	if (*str++ != '=' || !*str)  /* No mac addr specified */
+		return -1;
+
+	str_incr_cnt = mac_addr_atoi(c2k_mac_addr[0], str);
+
+	str += str_incr_cnt;
+
+	str_incr_cnt = mac_addr_atoi(c2k_mac_addr[1], str);
+
+	str += str_incr_cnt;
+
+	mac_addr_atoi(c2k_mac_addr[2], str);
+
+	return 0;
+}
+__setup("mac_addr", mac_addr_setup);
+
+void __init mac_addr_init(struct comcerto_pfe_platform_data * comcerto_pfe_data_ptr)
+{
+	u8 gem_port_id;
+
+	for (gem_port_id = 0; gem_port_id < NUM_GEMAC_SUPPORT; gem_port_id++) {
+		if (is_mac_zero(c2k_mac_addr[gem_port_id]))  /* If mac is non-zero */
+			comcerto_pfe_data_ptr->comcerto_eth_pdata[gem_port_id].mac_addr = c2k_mac_addr[gem_port_id];
+	}
+
+}
+
+static void __init pfe_init_pdata(void) {
+	if (of_machine_is_compatible("google,gfsc100")) {
+		comcerto_pfe_device.dev.platform_data = &gfsc100_pfe_pdata;
+	} else {
+		comcerto_pfe_device.dev.platform_data = &optimus_pfe_pdata;
+	}
+}
+
+static int __init register_pfe_device(void) {
+	int rc;
+	pfe_init_pdata();
+	mac_addr_init(comcerto_pfe_device.dev.platform_data);
+	rc = platform_device_register(&comcerto_pfe_device);
+	return 0;
+}
+late_initcall(register_pfe_device);
+
+
+static const char * const c2k_boards_compat[] __initconst = {
+	"fsl,ls1024a",
+	NULL,
+};
+
+DT_MACHINE_START(C2K_DT, "Generic Freescale LS1024A (Flattened Device Tree)")
+	.l2c_aux_val	= L2C_AUX_CTRL_SHARED_OVERRIDE | (1<<L220_AUX_CTRL_FWA_SHIFT), // 0,
+	.l2c_aux_mask	= ~( L2C_AUX_CTRL_SHARED_OVERRIDE | L220_AUX_CTRL_FWA_MASK ), //~0,
+	.reserve	= c2k_reserve,
+	.map_io		= c2k_map_io,
+	.init_early	= c2k_init_early,
+	.init_late	= c2k_init_late,
+	.dt_compat	= c2k_boards_compat,
+MACHINE_END
diff --git a/arch/arm/mach-comcerto/clk-comcerto2000.c b/arch/arm/mach-comcerto/clk-comcerto2000.c
new file mode 100644
index 0000000..99dc9d8
--- /dev/null
+++ b/arch/arm/mach-comcerto/clk-comcerto2000.c
@@ -0,0 +1,192 @@
+#include <linux/clk-provider.h>
+#include <linux/slab.h>
+#include <linux/of_address.h>
+#include <mach/hardware.h>
+#include "clock.h"
+
+struct clk_hw_comcerto2000 {
+	struct clk_hw hw;
+	void __iomem *reg;
+	u32 divreg_offset;
+	int div_bypass;
+};
+
+/*
+ * CLKGEN Divider registers
+ * The divider bypass bit in several configuration registers
+ * can only be written (if you read back you get zero).
+ *
+ * The Bug is in registers: 0x84 (A9DP_CLKDIV_CNTRL), 0x84 + 16 (L2CC_CLKDIV_CNTRL), 0x84 + 32
+ *	(TPI_CLKDIV_CNTRL), etc... until PLL_ADDR_SPACE at 0x1C0.
+ */
+
+/*
+ * Barebox uses IRAM to mirror the clock divider registers
+ * Linux will relocate this mirror from IRAM to DDR to free up IRAM.
+ */
+#define IRAM_CLK_REG_MIRROR		(0x8300FC00 - COMCERTO_AXI_IRAM_BASE + IRAM_MEMORY_VADDR)
+#define CLK_REG_DIV_BUG_BASE		AXI_CLK_DIV_CNTRL
+#define CLK_REG_DIV_BUG_SIZE		(PLL0_M_LSB - AXI_CLK_DIV_CNTRL)
+
+static u8 clk_div_backup_table [CLK_REG_DIV_BUG_SIZE];
+
+void c2k_clk_div_backup_relocate_table (void)
+{
+	memcpy (clk_div_backup_table, (void*) IRAM_CLK_REG_MIRROR, CLK_REG_DIV_BUG_SIZE);
+}
+EXPORT_SYMBOL(c2k_clk_div_backup_relocate_table);
+
+#define to_clk_hw_comcerto2000(_hw) container_of(_hw, struct clk_hw_comcerto2000, hw)
+
+unsigned long comcerto2000_recalc(struct clk_hw *hw,
+		unsigned long parent_rate) {
+	struct clk_hw_comcerto2000 *clk_hw = to_clk_hw_comcerto2000(hw);
+	unsigned long rate;
+	u32 div;
+
+	if (!parent_rate)
+		return 0;
+
+	if (clk_hw->div_bypass)
+		div = 1;
+	else {
+		/* Get clock divider bypass value from IRAM Clock Divider registers mirror location */
+		div = readl(clk_hw->reg + clk_hw->divreg_offset); //TODO(danielmentz): #define for div offset
+		div &= 0x1f;
+	}
+
+	rate = parent_rate / div;
+
+	// pr_err("clock %s running at %lu div %u parent rate %lu\n", __clk_get_name(hw->clk), rate, div, parent_rate);
+
+	return rate;
+}
+
+long comcerto2000_round_rate(struct clk_hw *hw, unsigned long target_rate,
+			unsigned long *parent_rate) {
+	u32 div;
+
+	/* Get the divider value for clock */
+	div = (*parent_rate + target_rate - 1) / target_rate;
+	if (div == 0 || div > 0x1f)
+		return -EINVAL;
+	return *parent_rate / div;
+}
+
+int comcerto2000_set_rate(struct clk_hw *hw, unsigned long rate,
+				unsigned long parent_rate) {
+	struct clk_hw_comcerto2000 *clk_hw = to_clk_hw_comcerto2000(hw);
+	u32 div, val;
+
+	/* Get the divider value for clock */
+	div = (parent_rate + rate - 1) / rate;
+	if (div == 0 || div > 0x1f)
+		return -EINVAL;
+
+	val = readl(clk_hw->reg + clk_hw->divreg_offset);
+	if (div == 1) {
+		//TODO(danielmentz): bypass bug
+		/* Enable the Bypass bit in hw reg (clk_div_bypass in div_reg) */
+		val |= CLK_DIV_BYPASS;
+		clk_hw->div_bypass = 1;
+	} else {
+		//TODO(danielmentz): bypass bug
+		/* Clear the Bypass bit in hw reg (clk_div_bypass in div_reg) */
+		clk_hw->div_bypass = 0;
+		val &= ~CLK_DIV_BYPASS;
+		val &= ~0x1f;
+		val |= div;
+	}
+	writel(val, clk_hw->reg + clk_hw->divreg_offset);
+
+	return 0;
+}
+
+
+const struct clk_ops comcerto2000_clk_ops = {
+	.set_rate	= &comcerto2000_set_rate,
+	.recalc_rate	= &comcerto2000_recalc,
+	.round_rate	= &comcerto2000_round_rate,
+};
+
+static void __init of_c2k_clk_setup(struct device_node *node) {
+	const char *name;
+	int num_parents;
+	const char **parent_names = NULL;
+	struct clk *clk;
+	struct clk_mux *mux = NULL;
+	struct clk_gate *gate = NULL;
+	struct clk_hw_comcerto2000 *rate = NULL;
+	void __iomem *regbase;
+	resource_size_t regbase_phys;
+	struct resource res;
+	u32 div_reg;
+	int i;
+
+	name = node->name;
+	of_property_read_string(node, "clock-output-names", &name);
+	num_parents = of_clk_get_parent_count(node);
+	if (num_parents != 5) {
+		pr_err("%s must have five parents\n", node->name);
+		goto cleanup;
+	}
+	parent_names = kzalloc(sizeof(char *) * num_parents, GFP_KERNEL);
+	if (!parent_names)
+		goto cleanup;
+	for (i = 0; i < num_parents; i++)
+		parent_names[i] = of_clk_get_parent_name(node, i);
+	regbase = of_iomap(node, 0);
+	WARN_ON(!regbase);
+	if (of_address_to_resource(node, 0, &res))
+		goto cleanup;
+	regbase_phys = res.start;
+	mux = kzalloc(sizeof(*mux), GFP_KERNEL);
+	if (!mux)
+		goto cleanup;
+	mux->reg = regbase + 0; // TODO(danielmentz): #define for mux offset
+	mux->shift = CLK_PLL_SRC_SHIFT;
+	mux->mask = CLK_PLL_SRC_MASK;
+	mux->flags = CLK_MUX_READ_ONLY;
+	mux->lock = 0; //TODO(danielmentz)
+
+	gate = kzalloc(sizeof(*gate), GFP_KERNEL);
+	if (!gate)
+		goto cleanup;
+	gate->flags = 0;
+	gate->reg = regbase + 0; // TODO(danielmentz): #define for gate offset
+	gate->bit_idx = 0; //TODO(danielmentz): #define for bit_idx
+	gate->lock = 0; //TODO(danielmentz)
+
+	rate = kzalloc(sizeof(*rate), GFP_KERNEL);
+	if (!rate)
+		goto cleanup;
+	rate->reg = regbase;
+	rate->divreg_offset = 4;// TODO(danielmentz): #define for div offset == 4
+	of_property_read_u32(node, "fsl,divreg-offset", &rate->divreg_offset);
+	div_reg = *((u32 *) (regbase_phys + rate->divreg_offset -
+			CLK_REG_DIV_BUG_BASE + clk_div_backup_table));
+	rate->div_bypass = (div_reg & CLK_DIV_BYPASS) ? 1 : 0;
+	clk = clk_register_composite(NULL, name,
+			parent_names, num_parents,
+			&mux->hw, &clk_mux_ro_ops,
+			&rate->hw, &comcerto2000_clk_ops,
+			&gate->hw, &clk_gate_ops,
+			0); // TODO: flags)
+	if (IS_ERR(clk))
+		goto cleanup;
+	of_clk_add_provider(node, of_clk_src_simple_get, clk);
+
+	goto out;
+
+cleanup:
+	kfree(mux);
+	kfree(gate);
+	kfree(rate);
+out:
+	/*
+	 * We don't need to retain parent_names. This array is only used for
+	 * initialization.
+	 * */
+	kfree(parent_names);
+}
+CLK_OF_DECLARE(c2k_pll_clock, "fsl,ls1024a-clock", of_c2k_clk_setup);
diff --git a/arch/arm/mach-comcerto/clock.h b/arch/arm/mach-comcerto/clock.h
new file mode 100644
index 0000000..27dc628
--- /dev/null
+++ b/arch/arm/mach-comcerto/clock.h
@@ -0,0 +1,28 @@
+/*
+ * arch/arm/mach-comcerto/clock.h
+ *
+ * Clock control driver for Comcerto C2K device - internal header file
+ *
+ * This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+#ifndef __ARCH_ARM_C2K_CLOCK_H__
+#define __ARCH_ARM_C2K_CLOCK_H__
+
+#define CLK_DOMAIN_MASK (1<<0)
+#define CLK_DOMAIN_SPI_I2C_MASK (1<<5)
+#define CLK_DOMAIN_TDMNTG_MASK (1<<4)
+#define CLK_DOMAIN_UART_MASK (1<<6)
+#define CLK_DOMAIN_PCIE0_MASK (1<<0)
+#define CLK_DOMAIN_PCIE1_MASK (1<<1)
+#define CLK_DOMAIN_IPSEC_SPACC_MASK (1<<2)
+#define CLK_DOMAIN_DPI_CIE_MASK (1<<5)
+#define CLK_DOMAIN_DPI_DECOMP_MASK (1<<6)
+#define CLK_DOMAIN_USB0_MASK (1<<3)
+#define CLK_DOMAIN_USB1_MASK (1<<4)
+#define CLK_DOMAIN_DUS_MASK  (1<<0)
+#define CLK_DOMAIN_SATA_MASK (1<<2)
+
+#endif
diff --git a/arch/arm/mach-comcerto/comcerto-restart.c b/arch/arm/mach-comcerto/comcerto-restart.c
new file mode 100644
index 0000000..a7ff5e2
--- /dev/null
+++ b/arch/arm/mach-comcerto/comcerto-restart.c
@@ -0,0 +1,5 @@
+#include "common.h"
+
+void c2k_restart(enum reboot_mode mode, const char *cmd)
+{
+}
diff --git a/arch/arm/mach-comcerto/comcerto-vwd.c b/arch/arm/mach-comcerto/comcerto-vwd.c
new file mode 100644
index 0000000..d6d9933
--- /dev/null
+++ b/arch/arm/mach-comcerto/comcerto-vwd.c
@@ -0,0 +1,53 @@
+/*
+ *  linux/arch/arm/mach-comcerto/comcerto-vwd.c
+ *
+ *  Copyright (C) 2011 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/module.h>
+#include <linux/skbuff.h>
+
+static int comcerto_wifi_rx_dummy_hdlr(struct sk_buff *skb)
+{
+	return -1;
+}
+
+static int (*vwd_rx_hdlr)(struct sk_buff *) = comcerto_wifi_rx_dummy_hdlr;
+
+int comcerto_wifi_rx_fastpath_register(int (*hdlr)(struct sk_buff *skb))
+{
+	printk(KERN_INFO "%s:%d VWD Tx function registered\n", __func__, __LINE__);
+	vwd_rx_hdlr = hdlr;
+
+	return 0;
+}
+
+void comcerto_wifi_rx_fastpath_unregister(void)
+{
+	printk(KERN_INFO "%s:%d VWD Tx function unregistered\n", __func__, __LINE__);
+	vwd_rx_hdlr = comcerto_wifi_rx_dummy_hdlr;
+
+	return;
+}
+
+int comcerto_wifi_rx_fastpath(struct sk_buff *skb)
+{
+	return (*vwd_rx_hdlr)(skb);
+}
+
+EXPORT_SYMBOL(comcerto_wifi_rx_fastpath_register);
+EXPORT_SYMBOL(comcerto_wifi_rx_fastpath_unregister);
+EXPORT_SYMBOL(comcerto_wifi_rx_fastpath);
diff --git a/arch/arm/mach-comcerto/common.c b/arch/arm/mach-comcerto/common.c
new file mode 100644
index 0000000..d7d4d56
--- /dev/null
+++ b/arch/arm/mach-comcerto/common.c
@@ -0,0 +1,38 @@
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/irqchip.h>
+
+#include <linux/memblock.h>
+#include <linux/antirebootloop.h>
+
+#include <mach/hardware.h>
+
+#include "common.h"
+
+void __init c2k_reserve(void)
+{
+	/* boot_secondary() in arch/arm/mach-comcerto/platsmp.c uses this range
+	 * to store a jump instruction. The second CPU executes this
+	 * instruction when it comes out of reset.*/
+	if (memblock_reserve(0, 0x24) < 0)
+		BUG();
+
+	if (memblock_reserve((phys_addr_t) get_antirebootloop_ptr(),
+				PAGE_SIZE) < 0)
+		BUG();
+
+	/* Allocate DDR block used by PFE/MSP, the base address is fixed so that util-pe code can
+	be linked at a fixed address */
+	if (memblock_reserve(COMCERTO_DDR_SHARED_BASE, COMCERTO_DDR_SHARED_SIZE) < 0)
+		BUG();
+
+	if (memblock_free(COMCERTO_DDR_SHARED_BASE, COMCERTO_DDR_SHARED_SIZE) < 0)
+		BUG();
+
+	if (memblock_remove(COMCERTO_DDR_SHARED_BASE, COMCERTO_DDR_SHARED_SIZE) < 0)
+		BUG();
+}
+
+phys_addr_t get_antirebootloop_ptr(void) {
+	return COMCERTO_AXI_DDR_BASE + (1 * PAGE_SIZE);
+}
diff --git a/arch/arm/mach-comcerto/common.h b/arch/arm/mach-comcerto/common.h
new file mode 100644
index 0000000..db13544
--- /dev/null
+++ b/arch/arm/mach-comcerto/common.h
@@ -0,0 +1,10 @@
+#include <linux/reboot.h>
+
+extern void ls1024a_secondary_startup(void);
+extern void c2k_local_timer_init(void);
+void c2k_init_early(void);
+void c2k_init_late(void);
+void c2k_restart(enum reboot_mode mode, const char *cmd);
+void __init c2k_map_io(void);
+void c2k_gic_of_init(void);
+extern void c2k_reserve(void);
diff --git a/arch/arm/mach-comcerto/headsmp.S b/arch/arm/mach-comcerto/headsmp.S
new file mode 100644
index 0000000..57202ef
--- /dev/null
+++ b/arch/arm/mach-comcerto/headsmp.S
@@ -0,0 +1,6 @@
+#include <linux/linkage.h>
+
+ENTRY(ls1024a_secondary_startup)
+	bl	v7_invalidate_l1
+	b	secondary_startup
+ENDPROC(ls1024a_secondary_startup)
diff --git a/arch/arm/mach-comcerto/include/mach/comcerto-2000.h b/arch/arm/mach-comcerto/include/mach/comcerto-2000.h
new file mode 100644
index 0000000..1e7c4b9
--- /dev/null
+++ b/arch/arm/mach-comcerto/include/mach/comcerto-2000.h
@@ -0,0 +1,244 @@
+/*
+ *  arch/arm/arch-comcerto/include/mach/comcerto-2000.h
+ *
+ *  Copyright (C) 2011 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
+ */
+
+#ifndef __ASM_ARCH_HARDWARE_H
+#error "Do not include this directly, instead #include <asm/hardware.h>"
+#endif
+
+#ifndef __ASM_ARCH_COMCERTO2000_H__
+#define __ASM_ARCH_COMCERTO2000_H__
+
+
+/*
+  * System Clock Frequencies
+  */
+#ifdef CONFIG_RTSM_C2K
+#define COMCERTO_DEFAULTAXICLK          (600000000/3) /* Hz */
+#else
+#define COMCERTO_DEFAULTAXICLK          200000000 /* Hz */
+#endif
+
+
+/*
+ * SCU registers
+ */
+#define COMCERTO_SCU_BASE					0xFFF00000
+#define COMCERTO_GIC_CPU_BASE				(COMCERTO_SCU_BASE + 0x100)
+#define COMCERTO_TWD_BASE					(COMCERTO_SCU_BASE + 0x600)
+#define COMCERTO_TWD_PERCPU_BASE			(COMCERTO_SCU_BASE + 0x700)
+#define COMCERTO_GIC_DIST_BASE				(COMCERTO_SCU_BASE + 0x1000)
+#define COMCERTO_L310_BASE					(COMCERTO_SCU_BASE + 0x10000)
+
+#define COMCERTO_L2CC_ASSOCIATIVITY_SHIFT	16
+#define COMCERTO_L2CC_ASSOCIATIVITY_MASK	0x00010000
+#define COMCERTO_L2CC_ASSOCIATIVITY_8WAY	0x0
+#define COMCERTO_L2CC_ASSOCIATIVITY_16WAY	0x1
+
+#define COMCERTO_L2CC_WAYSIZE_SHIFT			17
+#define COMCERTO_L2CC_WAYSIZE_MASK			0x000E0000
+#define COMCERTO_L2CC_ASSOCIATIVITY_32KB	0x2
+
+#define COMCERTO_L2CC_WR_LAT_SHIFT			8
+#define COMCERTO_L2CC_RD_LAT_SHIFT			4
+
+
+/*
+  * Physical address of IO on APB Bus
+  */
+
+#define COMCERTO_APB_TDM_BASE			0x90400000
+#define COMCERTO_APB_USBPHY_SERDES_STAT_BASE	0x90410000
+#define COMCERTO_APB_TDMA_BASE			0x90420000
+#define COMCERTO_APB_TIMER_BASE			0x90450000
+#define COMCERTO_APB_PCI_SATA_USB_CTRL_BASE	0x90460000
+#define COMCERTO_APB_GPIO_BASE			0x90470000
+#define COMCERTO_APB_UART0_BASE			0x90490000
+#define COMCERTO_APB_SPI_BASE			0x90498000
+#define COMCERTO_APB_I2C_BASE			0x9049C000
+#define COMCERTO_APB_USB3_BASE			0x904A0000
+#define COMCERTO_APB_CLK_BASE			0x904B0000
+#define COMCERTO_APB_RTC_BASE			0x904E0000
+#define COMCERTO_APB_OTP_BASE			0x904F0000
+#define COMCERTO_APB_PFE_BASE			0x90500000
+#define COMCERTO_APB_SERDES_CONF_BASE		0x90590000
+#define COMCERTO_APB_EXPBUS_BASE		0x905A0000
+#define COMCERTO_APB_DDRPHY_BASE		0x905B0000
+#define COMCERTO_APB_TDMA2_BASE			0x905D0000
+#define COMCERTO_APB_MDMA_BASE			0x905E0000
+#define COMCERTO_APB_A9CS_BASE			0x90600000
+
+
+/*
+  * Physical address on AXI Bus
+  */
+#define COMCERTO_AXI_HIGHMEMDDR_BASE		0xFFFF0000
+#define COMCERTO_AXI_DDR_BASE			0x00000000
+#define COMCERTO_AXI_ACP_BASE			0x80000000 /* 48MB */
+#define COMCERTO_AXI_IRAM_BASE			0x83000000
+#define COMCERTO_AXI_IBR_BASE			0x90000000
+#define COMCERTO_AXI_APB_BASE			0x90400000 /* 12MB */
+#define COMCERTO_AXI_SEMA_BASE			0x91000000 /* 16MB */
+#define COMCERTO_AXI_USB2P0_BASE		0x92000000
+#define COMCERTO_AXI_TRUSTZONE_BASE		0x93000000
+#define COMCERTO_AXI_DPI0_BASE			0x94000000 /* 16MB */
+#define COMCERTO_AXI_DPI1_BASE			0x95000000 /* 16MB */
+#define COMCERTO_AXI_UART_SPI_BASE		0x96000000 /* 16MB */
+#define COMCERTO_AXI_UART0_BASE			(COMCERTO_AXI_UART_SPI_BASE + 0x00300000)
+#define COMCERTO_AXI_UART1_BASE			(COMCERTO_AXI_UART_SPI_BASE + 0x00400000)
+#define COMCERTO_AXI_SPI_BASE			(COMCERTO_AXI_UART_SPI_BASE + 0x00500000)
+#define COMCERTO_AXI_DDRCONFIG_BASE		0x97000000
+#define COMCERTO_AXI_PCIe1_BASE			0x99000000
+#define COMCERTO_AXI_PCIe0_BASE			0x98000000
+#define COMCERTO_AXI_IPSEC_BASE			0x9A000000
+#define COMCERTO_AXI_SPACC_PDU_BASE		0x9B000000 /* 16MB */
+#define COMCERTO_AXI_PFE_BASE			0x9C000000 /* 16MB */
+#define COMCERTO_AXI_SATA_BASE			0x9D000000 /* 16MB */
+#define COMCERTO_AXI_DECT_BASE			0x9E000000 /* 16MB */
+#define COMCERTO_AXI_USB3P0_BASE                0x9F000000 /* 16MB */
+#define COMCERTO_AXI_PCIe0_SLAVE_BASE	0xA0000000
+#define COMCERTO_AXI_PCIe1_SLAVE_BASE	0xB0000000
+#define COMCERTO_AXI_EXP_BASE			0xC0000000
+#define COMCERTO_AXI_EXP_ECC_BASE		0xCFFF0000 /* 64KB */
+
+#define COMCERTO_AXI_ACP_SIZE			(1 << 24)
+
+#define COMCERTO_DDR_SHARED_BASE		(COMCERTO_AXI_DDR_BASE + 0x2C00000)
+#define COMCERTO_DDR_SHARED_SIZE		(SZ_16M + SZ_4M)
+
+/* MSP memory map */
+#define COMCERTO_MSP_DDR_BASE			COMCERTO_DDR_SHARED_BASE
+#define COMCERTO_MSP_DDR_SIZE_CB		(SZ_1M * 5)
+#define COMCERTO_MSP_DDR_SIZE_NCNB		(SZ_1M * 3)
+#define COMCERTO_MSP_DDR_SIZE			(COMCERTO_MSP_DDR_SIZE_CB + COMCERTO_MSP_DDR_SIZE_NCNB)
+
+/* PFE memory map */
+#define COMCERTO_APB_PFE_SIZE			SZ_64K
+#define COMCERTO_AXI_PFE_SIZE			SZ_16M
+#define COMCERTO_AXI_IPSEC_SIZE			SZ_16M
+#define COMCERTO_PFE_DDR_BASE			(COMCERTO_DDR_SHARED_BASE + SZ_8M)
+#define COMCERTO_PFE_DDR_SIZE			(SZ_8M + SZ_4M)
+#define COMCERTO_PFE_IRAM_BASE			(COMCERTO_AXI_IRAM_BASE + 0x0000)
+#define COMCERTO_PFE_IRAM_SIZE			SZ_8K
+
+/* MDMA memory map */
+#define COMCERTO_APB_MDMA_SIZE          SZ_1K+SZ_4
+
+/*
+ * Virtual address mapping
+ */
+#define COMCERTO_MSP_VADDR			0xf0000000
+#define COMCERTO_PFE_VADDR                     0xfa000000
+#define COMCERTO_PFE_AXI_VADDR         0xfc000000
+// TODO: #define COMCERTO_SCU_VADDR				COMCERTO_SCU_BASE
+#define COMCERTO_SCU_VADDR		0xf0000000
+#define COMCERTO_GIC_CPU_VADDR			(COMCERTO_SCU_VADDR + 0x100)
+#define COMCERTO_GIC_GLOBAL_TIMER_VADDR	(COMCERTO_SCU_VADDR + 0x200)
+#define COMCERTO_TWD_VADDR				(COMCERTO_SCU_VADDR + 0x600)
+#define COMCERTO_GIC_DIST_VADDR			(COMCERTO_SCU_VADDR + 0x1000)
+#define COMCERTO_L310_VADDR				(COMCERTO_SCU_VADDR + 0x10000)
+
+#define COMCERTO_DISTR_INT_SET_PENDING                   (COMCERTO_GIC_DIST_VADDR + 0x200)
+#define COMCERTO_DISTR_INT_SET_PENDING_OFFSET_4          (COMCERTO_DISTR_INT_SET_PENDING + 0x4)
+#define COMCERTO_DISTR_INT_SET_PENDING_OFFSET_8          (COMCERTO_DISTR_INT_SET_PENDING + 0x8)
+
+#define IRAM_MEMORY_VADDR			0xf0800000
+#define COMCERTO_APB_VADDR			0xf0900000	/* VA of IO on APB bus */
+#define COMCERTO_APB_SIZE			0x00C00000
+#define COMCERTO_AXI_UART_SPI_VADDR		0xF1600000
+#define COMCERTO_AXI_UART_SPI_SIZE		0x01000000
+#define COMCERTO_AXI_DMA_VADDR			(COMCERTO_AXI_UART_SPI_VADDR+0x00000000)
+#define COMCERTO_AXI_UART0_VADDR		(COMCERTO_AXI_UART_SPI_VADDR+0x00300000)
+#define COMCERTO_AXI_UART1_VADDR		(COMCERTO_AXI_UART_SPI_VADDR+0x00400000)
+#define COMCERTO_AXI_SSI_VADDR			(COMCERTO_AXI_UART_SPI_VADDR+0x00500000)
+
+#define COMCERTO_SEMA_VADDR			0xf4000000
+#define COMCERTO_AXI_PCIe0_VADDR_BASE		0xf5000000
+#define COMCERTO_AXI_PCIe1_VADDR_BASE		0xf6000000
+#define COMCERTO_DECT_VADDR			0xf7000000
+
+/*
+  * Reference Clock Option in Boot Strap Register
+  *
+  * BIT [9:8] System PLL Refclk Select
+  * '00' - USB XTAL
+  * '01' - Serdes #0 Refclk
+  * '10' - Serdes #1 Refclk
+  * '11' - Serdes XTAL
+  *
+  * BIT[7]   Serdes OSC PAD - Reference clock frequency selection bootstrap (inverted value of SF1)
+  * '0' - 30MHz ~ 50MHz
+  * '1' - 15MHz ~ 30MHz
+  * Note: SF0 is tied to 1 in GPIO block and might later on be controlled during DFT
+  *
+  * BIT[5]   USB/Sys PLL OSC PAD - Reference clock frequency selection bootstrap (inverted value of SF1)
+  *'0' - 30MHz ~ 50MHz
+  * '1' - 15MHz ~ 30MHz
+  * Note: SF0 is tied to 1 in GPIO block and might later on be controlled during DFT
+  *
+  */
+
+#define GPIO_SYS_PLL_REF_CLK_MASK			(0x00000100 | 0x00000200)
+#define GPIO_SYS_PLL_REF_CLK_SHIFT			8
+
+#define USB_XTAL_REF_CLK					0
+#define SERDES_0_REF_CLK					1
+#define SERDES_2_REF_CLK					2
+#define SERDES_XTAL_REF_CLK					3
+
+#define GPIO_SERDES_OSC_PAD_MASK			(0x00000080)
+#define GPIO_SERDES_OSC_PAD_SHIFT			7
+
+#define GPIO_USB_OSC_PAD_MASK				(0x00000020)
+#define GPIO_USB_OSC_PAD_SHIFT				5
+
+#define REF_CLK_24MHZ						24000000 /* 24 MHz */
+#define REF_CLK_48MHZ						48000000 /* 48 MHz */
+
+
+/* USB 2.0 */
+#define USB2_PHY_BASE						APB_VADDR(COMCERTO_APB_PCI_SATA_USB_CTRL_BASE)
+
+/* USB 3.0 */
+#define USB3_PHY_BASE           APB_VADDR(COMCERTO_APB_USB3_BASE)
+
+#define USBPHY_SERDES_STAT_BASE APB_VADDR(COMCERTO_APB_USBPHY_SERDES_STAT_BASE)
+
+/* Includes */
+#include <mach/comcerto-2000/clk-rst.h>
+#include <mach/comcerto-2000/timer.h>
+#include <mach/comcerto-2000/memory.h>
+
+#define comcerto_timer4_set(hbound)	__raw_writel((hbound), COMCERTO_TIMER4_HIGH_BOUND)
+
+#define comcerto_timer4_get()		__raw_readl(COMCERTO_TIMER4_CURRENT_COUNT)
+
+
+#define comcerto_timer5_set(lbound, hbound, ctrl)  do {								\
+						      __raw_writel((ctrl) & 0x1, COMCERTO_TIMER5_CTRL);	\
+						      __raw_writel((lbound), COMCERTO_TIMER5_LOW_BOUND);	\
+						      __raw_writel((hbound), COMCERTO_TIMER5_HIGH_BOUND);	\
+						   } while(0)
+
+#define comcerto_timer5_get()		__raw_readl(COMCERTO_TIMER5_CURRENT_COUNT)
+
+/* Number of gemacs supported in comcerto 2000 */
+#define NUM_GEMAC_SUPPORT	3
+
+#endif
diff --git a/arch/arm/mach-comcerto/include/mach/comcerto-2000/clk-rst.h b/arch/arm/mach-comcerto/include/mach/comcerto-2000/clk-rst.h
new file mode 100644
index 0000000..8cdb717
--- /dev/null
+++ b/arch/arm/mach-comcerto/include/mach/comcerto-2000/clk-rst.h
@@ -0,0 +1,252 @@
+/*
+ *  linux/arch/arm/mach-comcerto/include/mach/comcerto-2000/clk-rst.h
+ *
+ *  Copyright (C) 2008 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
+ */
+
+#ifndef __CLK_H__
+#define __CLK_H__
+
+/*
+ * Clock Reset Registers
+ */
+#define DEVICE_RST_CNTRL		(COMCERTO_APB_CLK_BASE + 0x000)
+#define SERDES_RST_CNTRL		(COMCERTO_APB_CLK_BASE + 0x004)
+#define PCIe_SATA_RST_CNTRL		(COMCERTO_APB_CLK_BASE + 0x008)
+#define USB_RST_CNTRL			(COMCERTO_APB_CLK_BASE + 0x00C)
+#define GNRL_DEVICE_CNFG_0		(COMCERTO_APB_CLK_BASE + 0x010)
+#define GNRL_DEVICE_CNFG_1 		(COMCERTO_APB_CLK_BASE + 0x014)
+#define GNRL_DEVICE_STATUS		(COMCERTO_APB_CLK_BASE + 0x018)
+#define A9DP_PWR_STAT			(COMCERTO_APB_CLK_BASE + 0x028)
+#define A9DP_PWR_CNTRL			(COMCERTO_APB_CLK_BASE + 0x02C)
+#define GNRL_CLK_CNTRL_0 		(COMCERTO_APB_CLK_BASE + 0x030)
+#define GNRL_CLK_CNTRL_1		(COMCERTO_APB_CLK_BASE + 0x034)
+#define PLLS_GLOBAL_CNTRL		(COMCERTO_APB_CLK_BASE + 0x038)
+#define AXI_CLK_CNTRL_0			(COMCERTO_APB_CLK_BASE + 0x040)
+#define AXI_CLK_CNTRL_1			(COMCERTO_APB_CLK_BASE + 0x044)
+#define AXI_CLK_CNTRL_2			(COMCERTO_APB_CLK_BASE + 0x048)
+#define AXI_CLK_DIV_CNTRL		(COMCERTO_APB_CLK_BASE + 0x04C)
+#define AXI_RESET_0			(COMCERTO_APB_CLK_BASE + 0x050)
+#define AXI_RESET_1			(COMCERTO_APB_CLK_BASE + 0x054)
+#define AXI_RESET_2			(COMCERTO_APB_CLK_BASE + 0x058)
+#define A9DP_MPU_CLK_CNTRL		(COMCERTO_APB_CLK_BASE + 0x68)
+#define A9DP_MPU_CLK_DIV_CNTRL       	(COMCERTO_APB_CLK_BASE + 0x6C)
+#define A9DP_MPU_RESET               	(COMCERTO_APB_CLK_BASE + 0x70)
+#define A9DP_CPU_CLK_CNTRL           	(COMCERTO_APB_CLK_BASE + 0x74)
+#define A9DP_CPU_RESET               	(COMCERTO_APB_CLK_BASE + 0x78)
+#define A9DP_CLK_CNTRL               	(COMCERTO_APB_CLK_BASE + 0x80)
+#define A9DP_CLK_DIV_CNTRL           	(COMCERTO_APB_CLK_BASE + 0x84)
+#define A9DP_RESET                   	(COMCERTO_APB_CLK_BASE + 0x88)
+#define L2CC_CLK_CNTRL			(COMCERTO_APB_CLK_BASE + 0x090)
+#define L2CC_CLK_DIV_CNTRL		(COMCERTO_APB_CLK_BASE + 0x094)
+#define L2CC_RESET			(COMCERTO_APB_CLK_BASE + 0x098)
+#define TPI_CLK_CNTRL			(COMCERTO_APB_CLK_BASE + 0x0A0)
+#define TPI_CLK_DIV_CNTRL		(COMCERTO_APB_CLK_BASE + 0x0A4)
+#define TPI_RESET			(COMCERTO_APB_CLK_BASE + 0x0A8)
+#define CSYS_CLK_CNTRL			(COMCERTO_APB_CLK_BASE + 0x0B0)
+#define CSYS_CLK_DIV_CNTRL		(COMCERTO_APB_CLK_BASE + 0x0B4)
+#define CSYS_RESET			(COMCERTO_APB_CLK_BASE + 0x0B8)
+#define EXTPHY0_CLK_CNTRL		(COMCERTO_APB_CLK_BASE + 0x0C0)
+#define EXTPHY0_CLK_DIV_CNTRL		(COMCERTO_APB_CLK_BASE + 0x0C4)
+#define EXTPHY0_RESET			(COMCERTO_APB_CLK_BASE + 0x0C8)
+#define EXTPHY1_CLK_CNTRL		(COMCERTO_APB_CLK_BASE + 0x0D0)
+#define EXTPHY1_CLK_DIV_CNTRL		(COMCERTO_APB_CLK_BASE + 0x0D4)
+#define EXTPHY1_RESET			(COMCERTO_APB_CLK_BASE + 0x0D8)
+#define EXTPHY2_CLK_CNTRL 		(COMCERTO_APB_CLK_BASE + 0x0E0)
+#define EXTPHY2_CLK_DIV_CNTRL 		(COMCERTO_APB_CLK_BASE + 0x0E4)
+#define EXTPHY2_RESET			(COMCERTO_APB_CLK_BASE + 0x0E8)
+#define DDR_CLK_CNTRL 			(COMCERTO_APB_CLK_BASE + 0x0F0)
+#define DDR_CLK_DIV_CNTRL 		(COMCERTO_APB_CLK_BASE + 0x0F4)
+#define DDR_RESET			(COMCERTO_APB_CLK_BASE + 0x0F8)
+#define PFE_CLK_CNTRL 			(COMCERTO_APB_CLK_BASE + 0x100)
+#define PFE_CLK_DIV_CNTRL 		(COMCERTO_APB_CLK_BASE + 0x104)
+#define PFE_RESET			(COMCERTO_APB_CLK_BASE + 0x108)
+#define IPSEC_CLK_CNTRL			(COMCERTO_APB_CLK_BASE + 0x110)
+#define IPSEC_CLK_DIV_CNTRL		(COMCERTO_APB_CLK_BASE + 0x114)
+#define IPSEC_RESET			(COMCERTO_APB_CLK_BASE + 0x118)
+#define DECT_CLK_CNTRL 			(COMCERTO_APB_CLK_BASE + 0x120)
+#define DECT_CLK_DIV_CNTRL 		(COMCERTO_APB_CLK_BASE + 0x124)
+#define DECT_RESET 			(COMCERTO_APB_CLK_BASE + 0x128)
+#define GEMTX_CLK_CNTRL			(COMCERTO_APB_CLK_BASE + 0x130)
+#define GEMTX_CLK_DIV_CNTRL		(COMCERTO_APB_CLK_BASE + 0x134)
+#define GEMTX_RESET			(COMCERTO_APB_CLK_BASE + 0x138)
+#define TDMNTG_REF_CLK_CNTRL 		(COMCERTO_APB_CLK_BASE + 0x140)
+#define TDMNTG_REF_CLK_DIV_CNTRL 	(COMCERTO_APB_CLK_BASE + 0x144)
+#define TDMNTG_RESET 			(COMCERTO_APB_CLK_BASE + 0x148)
+#define TDM_CLK_CNTRL			(COMCERTO_APB_CLK_BASE + 0x14C)
+#define TSUNTG_REF_CLK_CNTRL 		(COMCERTO_APB_CLK_BASE + 0x150)
+#define TSUNTG_REF_CLK_DIV_CNTRL 	(COMCERTO_APB_CLK_BASE + 0x154)
+#define TSUNTG_RESET 			(COMCERTO_APB_CLK_BASE + 0x158)
+#define SATA_PMU_CLK_CNTRL 		(COMCERTO_APB_CLK_BASE + 0x160)
+#define SATA_PMU_CLK_DIV_CNTRL 		(COMCERTO_APB_CLK_BASE + 0x164)
+#define SATA_PMU_RESET 			(COMCERTO_APB_CLK_BASE + 0x168)
+#define SATA_OOB_CLK_CNTRL 		(COMCERTO_APB_CLK_BASE + 0x170)
+#define SATA_OOB_CLK_DIV_CNTRL 		(COMCERTO_APB_CLK_BASE + 0x174)
+#define SATA_OOB_RESET 			(COMCERTO_APB_CLK_BASE + 0x178)
+#define SATA_OCC_CLK_CNTRL		(COMCERTO_APB_CLK_BASE + 0x180)
+#define SATA_OCC_CLK_DIV_CNTRL		(COMCERTO_APB_CLK_BASE + 0x184)
+#define SATA_OCC_RESET			(COMCERTO_APB_CLK_BASE + 0x188)
+#define PCIE_OCC_CLK_CNTRL		(COMCERTO_APB_CLK_BASE + 0x190)
+#define PCIE_OCC_CLK_DIV_CNTRL		(COMCERTO_APB_CLK_BASE + 0x194)
+#define PCIE_OCC_RESET			(COMCERTO_APB_CLK_BASE + 0x198)
+#define SGMII_OCC_CLK_CNTRL 		(COMCERTO_APB_CLK_BASE + 0x1A0)
+#define SGMII_OCC_CLK_DIV_CNTRL 	(COMCERTO_APB_CLK_BASE + 0x1A4)
+#define SGMII_OCC_RESET 		(COMCERTO_APB_CLK_BASE + 0x1A8)
+#define PLL0_M_LSB			(COMCERTO_APB_CLK_BASE + 0x1C0)
+#define PLL0_M_MSB			(COMCERTO_APB_CLK_BASE + 0x1C4)
+#define PLL0_P				(COMCERTO_APB_CLK_BASE + 0x1C8)
+#define PLL0_S				(COMCERTO_APB_CLK_BASE + 0x1CC)
+#define PLL0_CNTRL			(COMCERTO_APB_CLK_BASE + 0x1D0)
+#define PLL0_TEST			(COMCERTO_APB_CLK_BASE + 0x1D4)
+#define PLL0_STATUS			(COMCERTO_APB_CLK_BASE + 0x1D8)
+#define PLL0_DIV_CNTRL			(COMCERTO_APB_CLK_BASE + 0x1DC)
+#define PLL1_M_LSB			(COMCERTO_APB_CLK_BASE + 0x1E0)
+#define PLL1_M_MSB			(COMCERTO_APB_CLK_BASE + 0x1E4)
+#define PLL1_P				(COMCERTO_APB_CLK_BASE + 0x1E8)
+#define PLL1_S				(COMCERTO_APB_CLK_BASE + 0x1EC)
+#define PLL1_CNTRL			(COMCERTO_APB_CLK_BASE + 0x1F0)
+#define PLL1_TEST			(COMCERTO_APB_CLK_BASE + 0x1F4)
+#define PLL1_STATUS			(COMCERTO_APB_CLK_BASE + 0x1F8)
+#define PLL1_DIV_CNTRL			(COMCERTO_APB_CLK_BASE + 0x1FC)
+#define PLL2_M_LSB			(COMCERTO_APB_CLK_BASE + 0x200)
+#define PLL2_M_MSB			(COMCERTO_APB_CLK_BASE + 0x204)
+#define PLL2_P				(COMCERTO_APB_CLK_BASE + 0x208)
+#define PLL2_S				(COMCERTO_APB_CLK_BASE + 0x20C)
+#define PLL2_CNTRL			(COMCERTO_APB_CLK_BASE + 0x210)
+#define PLL2_TEST			(COMCERTO_APB_CLK_BASE + 0x214)
+#define PLL2_STATUS			(COMCERTO_APB_CLK_BASE + 0x218)
+#define PLL2_DIV_CNTRL			(COMCERTO_APB_CLK_BASE + 0x21C)
+#define PLL3_M_LSB			(COMCERTO_APB_CLK_BASE + 0x220)
+#define PLL3_M_MSB			(COMCERTO_APB_CLK_BASE + 0x224)
+#define PLL3_P				(COMCERTO_APB_CLK_BASE + 0x228)
+#define PLL3_S				(COMCERTO_APB_CLK_BASE + 0x22c)
+#define PLL3_CNTRL			(COMCERTO_APB_CLK_BASE + 0x230)
+#define PLL3_TEST			(COMCERTO_APB_CLK_BASE + 0x234)
+#define PLL3_STATUS			(COMCERTO_APB_CLK_BASE + 0x238)
+#define PLL3_DITHER_CNTRL		(COMCERTO_APB_CLK_BASE + 0x23C)
+#define PLL3_K_LSB 			(COMCERTO_APB_CLK_BASE + 0x240)
+#define PLL3_K_MSB 			(COMCERTO_APB_CLK_BASE + 0x244)
+#define PLL3_MFR 			(COMCERTO_APB_CLK_BASE + 0x248)
+#define PLL3_MRR 			(COMCERTO_APB_CLK_BASE + 0x24C)
+
+#define PLL0				0
+#define PLL1				1
+#define PLL2				2
+#define PLL3				3
+
+/* Device Reset Control Register (DEVICE_RST_CNTRL)*/
+
+#define CLK_DIV_RESTART         (1 << 7)
+#define WD_STATUS_CLR           (1 << 6)
+#define AXI_WD_RST_EN           (1 << 5)
+#define DEBUG_RST               (1 << 4)
+#define CLKRST_SCLR_RST         (1 << 3)
+#define FUNC_SCLR_RST           (1 << 2)
+#define GLB_SCLR_RST            (1 << 1)
+#define PWR_ON_SOFT_RST         (1 << 0)
+
+/* General Device Configuration Status Register (GNRL_DEVICE_STATUS) */
+#define CPU1_WD_RST_ACTIVATED   (1 << 2)
+#define CPU0_WD_RST_ACTIVATED   (1 << 1)
+#define AXI_WD_RST_ACTIVATED    (1 << 0)
+
+/* Clock Control Register */
+#define CLOCK_DOMAIN_ENABLE		1
+#define CLOCK_DOMAIN_ENABLE_SHIFT	0
+
+#define CLOCK_SOURCE_PLL0		0
+#define CLOCK_SOURCE_PLL1		1
+#define CLOCK_SOURCE_PLL2		2
+#define CLOCK_SOURCE_PLL3		3
+#define CLOCK_SOURCE_PLL4		4
+
+#define CLOCK_SOURCE_SHIFT		1
+
+#define CLK_DIV_BYPASS			(1 << 7)
+#define CLK_DIV_RATIO_MASK		0x1f
+
+#define PLL_VSEL			(1 << 6)
+#define PLL_LOCK_EN			(1 << 5)
+#define PLL_BYPASS			(1 << 4)
+#define PLL_RESET			(1 << 0)
+
+#define PLL_LOCK			(1 << 0)
+
+#define CLK_PLL_SRC_MASK		0x7
+#define CLK_PLL_SRC_SHIFT		1
+#define CLK_A9DP_PERI_DIV_BYPASS	(1 << 3)
+
+
+/* TDMNTG */
+#define TDMNTG_ADDR_SPACE_BASEADDR 		(COMCERTO_APB_CLK_BASE + 0x280)
+
+#define TDM_NTG_CLK_CTRL	(TDMNTG_ADDR_SPACE_BASEADDR + 0x00)
+#define TDM_NTG_INCR		(TDMNTG_ADDR_SPACE_BASEADDR + 0x04)
+#define TDM_FSYNC_GEN_CTRL	(TDMNTG_ADDR_SPACE_BASEADDR + 0x08)
+#define	TDM_FSYNC_LOW		(TDMNTG_ADDR_SPACE_BASEADDR + 0x0C)
+#define TDM_FSYNC_HIGH		(TDMNTG_ADDR_SPACE_BASEADDR + 0x10)
+
+#define FSYNC_FALL_EDGE		(1 << 1)
+#define	NTG_DIV_RST_N		(1 << 5)
+#define NTG_EN			(1 << 0)
+
+
+/* TSUNTG */
+#define TSUNTG_ADDR_SPACE_BASEADDR 		(COMCERTO_APB_CLK_BASE + 0x2C0)
+
+/* USB 3.0 */
+#define USB1_UTMI_RST           (1 << 5)
+#define USB1_PHY_RST            (1 << 4)
+#define USB1_AXI_RST            (1 << 4)
+
+/* USB 2.0 */
+#define USB0_PHY_RST		(1 << 0)
+#define USB0_UTMI_RST		(1 << 1)
+#define USB0_AXI_RST		(1 << 3)
+
+/* CPU */
+#define CPU0_RST			(1 << 0)
+#define NEON0_RST			(1 << 1)
+#define CPU1_RST			(1 << 2)
+#define NEON1_RST			(1 << 3)
+
+#define CPU0_CLK_ENABLE		(1 << 0)
+#define NEON0_CLK_ENABLE	(1 << 1)
+#define CPU1_CLK_ENABLE		(1 << 2)
+#define NEON1_CLK_ENABLE	(1 << 3)
+
+#define GLOBAL_CLK_ENABLE	(1 << 0)
+
+#define CLAMP_CORE0     	(1 << 4)
+#define CLAMP_CORE1     	(1 << 6)
+#define CORE_PWRDWN0        (1 << 5)
+#define CORE_PWRDWN1        (1 << 7)
+#define MP_PWRDWN       	(1 << 0)
+
+#define SCPRE   			(1 << 4)
+#define SCALL   			(1 << 5)
+#define ISO_EN  			(1 << 6)
+
+#ifndef __ASSEMBLY__
+
+/* Function Declaration */
+extern void HAL_clk_div_backup_relocate_table (void);
+extern unsigned long HAL_get_ref_clk (void);
+#endif
+
+
+#endif
diff --git a/arch/arm/mach-comcerto/include/mach/comcerto-2000/memory.h b/arch/arm/mach-comcerto/include/mach/comcerto-2000/memory.h
new file mode 100644
index 0000000..8253848
--- /dev/null
+++ b/arch/arm/mach-comcerto/include/mach/comcerto-2000/memory.h
@@ -0,0 +1,38 @@
+/*
+ *  arch/arm/arch-comcerto/include/mach/comcerto-2000/memory.h
+ *
+ *  Copyright (C) 2011 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
+ */
+
+
+#ifndef __MEMORY_H__
+#define __MEMORY_H__
+
+	/* Physical addresses of memories */
+	#define IRAM_MEMORY_SIZE			SZ_64K
+
+/*
+ * Virtual view <-> DMA view memory address translations
+ * virt_to_bus: Used to translate the virtual address to an
+ *              address suitable to be passed to set_dma_addr
+ * bus_to_virt: Used to convert an address for DMA operations
+ *              to an address that the kernel can use.
+ */
+
+#define aram_to_virt(p)		(void*)(((unsigned long)p - COMCERTO_AXI_IRAM_BASE) + IRAM_MEMORY_VADDR)
+#define virt_to_aram(v)		(((unsigned long)v - IRAM_MEMORY_VADDR) + COMCERTO_AXI_IRAM_BASE)
+#endif
diff --git a/arch/arm/mach-comcerto/include/mach/comcerto-2000/timer.h b/arch/arm/mach-comcerto/include/mach/comcerto-2000/timer.h
new file mode 100644
index 0000000..2320313
--- /dev/null
+++ b/arch/arm/mach-comcerto/include/mach/comcerto-2000/timer.h
@@ -0,0 +1,101 @@
+/*
+ *  linux/include/asm-arm/arch-comcerto/comcerto-1000/timer.h
+ *
+ *  Copyright (C) 2004-2008 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
+ */
+
+#ifndef __TIMER_H__
+#define __TIMER_H__
+
+
+/* Comcerto Timers  */
+#define COMCERTO_TIMER0_HIGH_BOUND          0x00
+#define COMCERTO_TIMER0_CURRENT_COUNT       0x04
+#define COMCERTO_TIMER1_HIGH_BOUND          0x08
+#define COMCERTO_TIMER1_CURRENT_COUNT       0x0C
+#define COMCERTO_TIMER2_LOW_BOUND           0x10
+#define COMCERTO_TIMER2_HIGH_BOUND          0x14
+#define COMCERTO_TIMER2_CTRL                0x18
+#define COMCERTO_TIMER2_CURRENT_COUNT       0x1C
+#define COMCERTO_TIMER3_LOW_BOUND           0x20
+#define COMCERTO_TIMER3_HIGH_BOUND          0x24
+#define COMCERTO_TIMER3_CTRL                0x28
+#define COMCERTO_TIMER3_CURRENT_COUNT       0x2C
+#define COMCERTO_TIMER4_HIGH_BOUND          0x30
+#define COMCERTO_TIMER4_CURRENT_COUNT       0x34
+#define COMCERTO_TIMER5_LOW_BOUND           0x38
+#define COMCERTO_TIMER5_HIGH_BOUND          0x3C
+#define COMCERTO_TIMER5_CURRENT_COUNT       0x40
+#define COMCERTO_TIMER5_CTRL                0x44
+#define COMCERTO_TIMER_IRQ_MASK             0x48
+#define COMCERTO_TIMER_STATUS               0x50
+#define COMCERTO_TIMER_STATUS_CLR           0x50
+
+/*COMCERTO_TIMER_IRQ_MASK*/
+#define COMCERTO_TIMER0     0x01
+#define COMCERTO_TIMER1     0x02
+#define COMCERTO_TIMER2     0x04
+#define COMCERTO_TIMER3     0x08
+#define COMCERTO_TIMER4     0x10
+#define COMCERTO_TIMER5     0x20
+#define COMCERTO_ALL        0xFF
+#define COMCERTO_TIMER_CSP  (COMCERTO_TIMER1 | COMCERTO_TIMER2 | COMCERTO_TIMER3 | COMCERTO_TIMER4 | COMCERTO_TIMER5)
+
+/*
+ * TIMERS
+ */
+
+
+/*Hardware Timer API*/
+#define COMCERTO_TIMER_RUN_ONCE		(1 << 0)
+#define __comcerto_timer_enable(t)	__raw_writel(__raw_readl(COMCERTO_TIMER_IRQ_MASK) | (1 << (t)), COMCERTO_TIMER_IRQ_MASK)
+#define __comcerto_timer_disable(t)	__raw_writel(__raw_readl(COMCERTO_TIMER_IRQ_MASK) & ~(1 << (t)), COMCERTO_TIMER_IRQ_MASK)
+#define comcerto_timer_ack(t)		__raw_writel(1 << (t), COMCERTO_TIMER_STATUS_CLR)
+
+#define comcerto_timer0_set(hbound)	__raw_writel((hbound), COMCERTO_TIMER0_HIGH_BOUND)
+#define comcerto_timer0_get()		__raw_readl(COMCERTO_TIMER0_CURRENT_COUNT)
+
+#define comcerto_timer1_set(hbound)	__raw_writel((hbound) & 0x3FFFFFFF, COMCERTO_TIMER1_HIGH_BOUND)
+#define comcerto_timer1_get()		__raw_readl(COMCERTO_TIMER1_CURRENT_COUNT)
+
+#define comcerto_timer2_set(lbound, hbound, ctrl)  do {\
+						      __raw_writel((ctrl) & 0x1, COMCERTO_TIMER2_CTRL);	\
+						      __raw_writel((lbound), COMCERTO_TIMER2_LOW_BOUND);	\
+						      __raw_writel((hbound), COMCERTO_TIMER2_HIGH_BOUND);	\
+						   } while (0)
+
+#define comcerto_timer2_get()		__raw_readl(COMCERTO_TIMER2_CURRENT_COUNT)
+
+
+#define comcerto_timer3_set(lbound, hbound, ctrl)  do {								\
+						      __raw_writel((ctrl) & 0x1, COMCERTO_TIMER3_CTRL);	\
+						      __raw_writel((lbound), COMCERTO_TIMER3_LOW_BOUND);	\
+						      __raw_writel((hbound), COMCERTO_TIMER3_HIGH_BOUND);	\
+						   } while(0)
+
+#define comcerto_timer3_get()		__raw_readl(COMCERTO_TIMER3_CURRENT_COUNT)
+
+#ifndef __ASSEMBLY__
+struct comcerto_timer {
+	unsigned long timeout;
+	void (*func) (unsigned long data);
+	unsigned long data;
+	unsigned char flags;
+	unsigned long thw;
+};
+#endif
+#endif
diff --git a/arch/arm/mach-comcerto/include/mach/comcerto-common.h b/arch/arm/mach-comcerto/include/mach/comcerto-common.h
new file mode 100644
index 0000000..1403f44
--- /dev/null
+++ b/arch/arm/mach-comcerto/include/mach/comcerto-common.h
@@ -0,0 +1,166 @@
+/*
+ *  arch/arm/mach-comcerto/include/mach/comcerto-common.h
+ *
+ *  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
+ */
+
+#ifndef __ASM_ARCH_HARDWARE_H
+#error "Do not include this directly, instead #include <asm/arch/hardware.h>"
+#endif
+
+#ifndef __ASM_COMCERTO_COMMON_H__
+#define __ASM_COMCERTO_COMMON_H__
+
+#include <asm/types.h>
+
+#define APB_VADDR(x)	((void __iomem *)((x) - COMCERTO_AXI_APB_BASE + COMCERTO_APB_VADDR))		/* macro to get virtual address of IO on APB Bus from Physical address*/
+#define AXI_VADDR(x)	((x) - COMCERTO_AXI_SPI_BASE + COMCERTO_AXI_SSI_VADDR)	/* macro to get virtual address of IO on AXI Bus from Physical address*/
+
+#define  BIT_0_MSK   0x00000001
+#define  BIT_1_MSK   0x00000002
+#define  BIT_2_MSK   0x00000004
+#define  BIT_3_MSK   0x00000008
+#define  BIT_4_MSK   0x00000010
+#define  BIT_5_MSK   0x00000020
+#define  BIT_6_MSK   0x00000040
+#define  BIT_7_MSK   0x00000080
+#define  BIT_8_MSK   0x00000100
+#define  BIT_9_MSK   0x00000200
+#define  BIT_10_MSK  0x00000400
+#define  BIT_11_MSK  0x00000800
+#define  BIT_12_MSK  0x00001000
+#define  BIT_13_MSK  0x00002000
+#define  BIT_14_MSK  0x00004000
+#define  BIT_15_MSK  0x00008000
+#define  BIT_16_MSK  0x00010000
+#define  BIT_17_MSK  0x00020000
+#define  BIT_18_MSK  0x00040000
+#define  BIT_19_MSK  0x00080000
+#define  BIT_20_MSK  0x00100000
+#define  BIT_21_MSK  0x00200000
+#define  BIT_22_MSK  0x00400000
+#define  BIT_23_MSK  0x00800000
+#define  BIT_24_MSK  0x01000000
+#define  BIT_25_MSK  0x02000000
+#define  BIT_26_MSK  0x04000000
+#define  BIT_27_MSK  0x08000000
+#define  BIT_28_MSK  0x10000000
+#define  BIT_29_MSK  0x20000000
+#define  BIT_30_MSK  0x40000000
+#define  BIT_31_MSK  0x80000000
+
+/*
+ * GPIO
+ */
+/* Set gpio pins specified by gpiomask to be outputs */
+#define comcerto_gpio_enable_output(gpiomask)	__raw_writel(__raw_readl(COMCERTO_GPIO_OE_REG) | (gpiomask), COMCERTO_GPIO_OE_REG)
+
+/* Set output pins specified by gpiomask to low */
+#define comcerto_gpio_set_0(gpiomask)	__raw_writel(__raw_readl(COMCERTO_GPIO_OUTPUT_REG) & ~(gpiomask), COMCERTO_GPIO_OUTPUT_REG)
+
+/* Set output pins specified by gpiomask to high */
+#define comcerto_gpio_set_1(gpiomask)	__raw_writel(__raw_readl(COMCERTO_GPIO_OUTPUT_REG) | (gpiomask), COMCERTO_GPIO_OUTPUT_REG)
+
+/* Read status of input pins specified by gpiomask */
+#define comcerto_gpio_read(gpiomask)	(__raw_readl(COMCERTO_GPIO_INPUT_REG) & (gpiomask))
+
+
+#ifndef __ASSEMBLY__
+#define CONFIG_COMCERTO_GEMAC		1
+
+#define CONFIG_COMCERTO_USE_MII		1
+#define CONFIG_COMCERTO_USE_RMII		2
+#define CONFIG_COMCERTO_USE_GMII		4
+#define CONFIG_COMCERTO_USE_RGMII	8
+#define CONFIG_COMCERTO_USE_SGMII	0x10
+
+#define GEMAC_SW_CONF			(1 << 8) | (1 << 11)	// GEMAC configured by SW
+#define GEMAC_PHY_CONF		0			// GEMAC configured by phy lines (not for MII/GMII)
+#define GEMAC_SW_FULL_DUPLEX	(1 << 9)
+#define GEMAC_SW_SPEED_10M	(0 << 12)
+#define GEMAC_SW_SPEED_100M	(1 << 12)
+#define GEMAC_SW_SPEED_1G		(2 << 12)
+
+#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)
+
+#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 }
+
+struct comcerto_eth_platform_data {
+	/* device specific information */
+	u32 device_flags;
+	char name[16];
+
+
+	/* board specific information */
+	u32 mii_config;
+	u32 gemac_mode;
+	u32 phy_flags;
+	u32 gem_id;
+	u32 bus_id;
+	u32 phy_id;
+	u8 *mac_addr;
+};
+
+struct comcerto_mdio_platform_data {
+	int enabled;
+	int irq[32];
+	u32 phy_mask;
+	int mdc_div;
+};
+
+struct comcerto_pfe_platform_data
+{
+	struct comcerto_eth_platform_data comcerto_eth_pdata[3];
+	struct comcerto_mdio_platform_data comcerto_mdio_pdata[3];
+};
+
+struct comcerto_tdm_data {
+	u8 fsoutput; /* Generic Pad Control and Version ID Register[2] */
+	u8 fspolarity; /*  28 FSYNC_FALL(RISE)_EDGE */
+	u16 fshwidth; /* High_Phase_Width[10:0] */
+	u16 fslwidth; /* Low_Phase_Width[26:16]] */
+	u32 clockhz; /* INC_VALUE[29:0] According to the desired TDM clock output frequency, this field should be configured */
+	u8 clockout; /* IO Control Register[21]  hardware or software control selection  IO Control Register[20] pads are input (output) */
+	u8 tdmmux;
+#if 0
+	u32 tdmck;
+	u32 tdmfs;
+	u32 tdmdx;
+	u32 tdmdr;
+#endif
+};
+
+/* L210 cache controller value
+    // Configure Aux:
+    //   [11:9]=[8:6]=[2:0]=001 RAM LAT = 2 cycles
+    //   [5:3]=000    Data write latency is 1
+    //   [12]=0       WRAP access is enabled
+    //   [16:13]=1000 8-way cache
+    //   [19:17]=001  16KB way
+    //   [20]=1       Event bus is enabled
+    //   [21]=1       Parity is enabled
+    //   [22]=0       Shared accesses treated as noncacheable
+    //   [23]=0       HPROT is used
+    //   [24]=1       Abort generation of exclusive access disabled
+*/
+#define L210_AUX_CTRL_REG	0x01330241
+
+#endif
+#endif
diff --git a/arch/arm/mach-comcerto/include/mach/hardware.h b/arch/arm/mach-comcerto/include/mach/hardware.h
new file mode 100644
index 0000000..c7dc18e
--- /dev/null
+++ b/arch/arm/mach-comcerto/include/mach/hardware.h
@@ -0,0 +1,49 @@
+/*
+ *  arch/arm/mach-comcerto/include/mach/hardware.h
+ *
+ *  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
+ */
+
+#ifndef __ASM_ARCH_HARDWARE_H
+#define __ASM_ARCH_HARDWARE_H
+
+#include <linux/types.h>
+
+#include <mach/comcerto-2000.h>
+
+
+#if 0
+	/***** 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)
+		#include <mach/board-c2kmfcnevm.h>
+	#elif defined(CONFIG_RTSM_C2K)
+		#include <mach/board-c2krtsm.h>
+
+	#else
+		#error "mach/board_XXX.h :  Unknown board"
+	#endif
+#endif
+
+#include <mach/comcerto-common.h>
+
+#endif
diff --git a/arch/arm/mach-comcerto/io.c b/arch/arm/mach-comcerto/io.c
new file mode 100644
index 0000000..0080cca
--- /dev/null
+++ b/arch/arm/mach-comcerto/io.c
@@ -0,0 +1,68 @@
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+
+#include <asm/tlb.h>
+#include <asm/mach/map.h>
+
+#include <mach/hardware.h>
+
+
+static struct map_desc c2k_io_desc[] __initdata = {
+#if defined(CONFIG_PCI)
+        {
+                .virtual    = COMCERTO_AXI_PCIe0_VADDR_BASE,
+                .pfn        = __phys_to_pfn(COMCERTO_AXI_PCIe0_BASE),
+                .length     = SZ_16M,
+                .type       = MT_DEVICE
+        },
+        {
+                .virtual    = COMCERTO_AXI_PCIe1_VADDR_BASE,
+                .pfn        = __phys_to_pfn(COMCERTO_AXI_PCIe1_BASE),
+                .length     = SZ_16M,
+                .type       = MT_DEVICE
+        },
+#endif
+	{
+		.virtual    = COMCERTO_SCU_VADDR,
+		.pfn        = __phys_to_pfn(COMCERTO_SCU_BASE),
+		.length     = SZ_128K,
+		.type       = MT_DEVICE
+	},
+	{
+		.virtual    = IRAM_MEMORY_VADDR,
+		.pfn        = __phys_to_pfn(COMCERTO_AXI_IRAM_BASE),
+		.length     = IRAM_MEMORY_SIZE,
+		.type       = MT_DEVICE
+	},
+	{
+		.virtual    = COMCERTO_APB_VADDR,
+		.pfn        = __phys_to_pfn(COMCERTO_AXI_APB_BASE),
+		.length     = COMCERTO_APB_SIZE,
+		.type       = MT_DEVICE
+	},
+	{
+		.virtual	= COMCERTO_AXI_UART_SPI_VADDR,
+		.pfn		= __phys_to_pfn(COMCERTO_AXI_UART_SPI_BASE),
+		.length 	= COMCERTO_AXI_UART_SPI_SIZE,
+		.type		= MT_DEVICE
+	},
+};
+
+void __init c2k_map_io(void)
+{
+	iotable_init(c2k_io_desc, ARRAY_SIZE(c2k_io_desc));
+}
+
+extern void c2k_clk_div_backup_relocate_table (void);
+
+void __init c2k_init_early(void)
+{
+	c2k_clk_div_backup_relocate_table();
+}
+void __init c2k_init_late(void)
+{
+}
diff --git a/arch/arm/mach-comcerto/platsmp.c b/arch/arm/mach-comcerto/platsmp.c
new file mode 100644
index 0000000..f31e578
--- /dev/null
+++ b/arch/arm/mach-comcerto/platsmp.c
@@ -0,0 +1,103 @@
+#include <linux/smp.h>
+#include <linux/io.h>
+#include <mach/hardware.h>
+#include <asm/cacheflush.h>
+#include <asm/smp_scu.h>
+#include <asm/unified.h>
+
+#include <linux/delay.h>
+
+#include "common.h"
+
+#define JUMP_TO_KERNEL_START_1		0xe3a00020 	/* mov	r0, #32 */
+#define JUMP_TO_KERNEL_START_2		0xe590f000 	/* ldr	pc, [r0] */
+
+/* SCU base address */
+static void __iomem *scu_base;
+
+static DEFINE_SPINLOCK(boot_lock);
+
+void __iomem *ls1024a_get_scu_base(void)
+{
+	return scu_base;
+}
+
+/*
+ * Initialise the CPU possible map early - this describes the CPUs
+ * which may be present or become present in the system.
+ */
+static void __init ls1024a_smp_init_cpus(void)
+{
+	int i, ncores;
+
+	scu_base = (void *) COMCERTO_SCU_VADDR;
+	ncores = scu_get_core_count(scu_base);
+
+	/* sanity check */
+	if (ncores > nr_cpu_ids) {
+		pr_warn("SMP: %u cores greater than maximum (%u), clipping\n",
+			ncores, nr_cpu_ids);
+		ncores = nr_cpu_ids;
+	}
+
+	for (i = 0; i < ncores; i++)
+		set_cpu_possible(i, true);
+}
+
+static void __init ls1024a_smp_prepare_cpus(unsigned int max_cpus)
+{
+	scu_enable(scu_base);
+}
+
+static int ls1024a_boot_secondary(unsigned int cpu, struct task_struct *idle)
+{
+	/*
+	 * Set synchronisation state between this boot processor
+	 * and the secondary one
+	 */
+	spin_lock(&boot_lock);
+
+	/*
+	 * Install the comcerto_secondary_startup pointer at 0x20
+	 * Physical Address
+	 */
+	__raw_writel(virt_to_phys(ls1024a_secondary_startup), phys_to_virt(0x20));
+	__raw_writel((unsigned int)JUMP_TO_KERNEL_START_1 , phys_to_virt(0x00));
+	__raw_writel((unsigned int)JUMP_TO_KERNEL_START_2 , phys_to_virt(0x04));
+	smp_wmb();
+	__cpuc_flush_dcache_area((void *)phys_to_virt(0x00), 0x24);
+	outer_clean_range(__pa(phys_to_virt(0x00)), __pa(phys_to_virt(0x24)));
+
+	/* Get CPU 1 out of reset */
+	__raw_writel((__raw_readl(APB_VADDR(A9DP_CPU_RESET)) & ~CPU1_RST), APB_VADDR(A9DP_CPU_RESET));
+	__raw_writel((__raw_readl(APB_VADDR(A9DP_PWR_CNTRL)) & ~CLAMP_CORE1), APB_VADDR(A9DP_PWR_CNTRL));
+	__raw_writel((__raw_readl(APB_VADDR(A9DP_CPU_CLK_CNTRL)) | CPU1_CLK_ENABLE), APB_VADDR(A9DP_CPU_CLK_CNTRL));
+
+#ifdef CONFIG_NEON
+	/* Get NEON 1 out of reset */
+	__raw_writel((__raw_readl(APB_VADDR(A9DP_CPU_RESET)) & ~NEON1_RST), APB_VADDR(A9DP_CPU_RESET));
+	__raw_writel((__raw_readl(APB_VADDR(A9DP_CPU_CLK_CNTRL)) | NEON1_CLK_ENABLE), APB_VADDR(A9DP_CPU_CLK_CNTRL));
+#endif
+
+	/*
+	 * Now the secondary core is starting up let it run its
+	 * calibrations, then wait for it to finish
+	 */
+	spin_unlock(&boot_lock);
+
+	return 0;
+}
+
+struct smp_operations ls1024a_smp_ops __initdata = {
+	.smp_init_cpus		= ls1024a_smp_init_cpus,
+	.smp_prepare_cpus	= ls1024a_smp_prepare_cpus,
+	//.smp_secondary_init	= ls1024a_secondary_init,
+	.smp_boot_secondary	= ls1024a_boot_secondary,
+#if 0
+#ifdef CONFIG_HOTPLUG_CPU
+	.cpu_die		= ls1024a_cpu_die,
+#endif
+#endif
+};
+
+CPU_METHOD_OF_DECLARE(fsl_ls1024a_smp, "fsl,ls1024a-smp", &ls1024a_smp_ops);
diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c
index 64d7486..75f1003 100644
--- a/arch/arm/mm/dma-mapping.c
+++ b/arch/arm/mm/dma-mapping.c
@@ -41,6 +41,26 @@
 
 #include "mm.h"
 
+#ifndef CONFIG_COMCERTO_DMA_COHERENT_SKB
+#define DEFAULT_DMA_COHERENT_POOL_SIZE	SZ_256K
+#else
+/*
+ * Experiments showed that, on the NXP (formerly Freescale) QorIQ LS1024A (fka
+ * Mindspeed Comcerto 2000), we need approx. 10 MB of DMA coherent memory for
+ * fast forwarding between wired Ethernet ports and ath10k.
+ */
+#define DEFAULT_DMA_COHERENT_POOL_SIZE	SZ_16M
+#endif
+
+static struct gen_pool *atomic_pool;
+static size_t atomic_pool_size = DEFAULT_DMA_COHERENT_POOL_SIZE;
+
+static inline bool __in_atomic_pool(void *start, size_t size)
+{
+	return atomic_pool && addr_in_gen_pool(atomic_pool,
+			(unsigned long)start, size);
+}
+
 /*
  * The DMA API is built upon the notion of "buffer ownership".  A buffer
  * is either exclusively owned by the CPU (and therefore may be accessed
@@ -76,8 +96,13 @@
 	     unsigned long offset, size_t size, enum dma_data_direction dir,
 	     struct dma_attrs *attrs)
 {
-	if (!dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
-		__dma_page_cpu_to_dev(page, offset, size, dir);
+	if (unlikely(!__in_atomic_pool(phys_to_virt(page_to_phys(page)),
+					size))) {
+		if (!dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
+			__dma_page_cpu_to_dev(page, offset, size, dir);
+	} else {
+		wmb();
+	}
 	return pfn_to_dma(dev, page_to_pfn(page)) + offset;
 }
 
@@ -106,9 +131,11 @@
 		size_t size, enum dma_data_direction dir,
 		struct dma_attrs *attrs)
 {
-	if (!dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
-		__dma_page_dev_to_cpu(pfn_to_page(dma_to_pfn(dev, handle)),
-				      handle & ~PAGE_MASK, size, dir);
+	if (unlikely(!__in_atomic_pool(phys_to_virt(handle), size))) {
+		if (!dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
+			__dma_page_dev_to_cpu(pfn_to_page(dma_to_pfn(dev, handle)),
+					      handle & ~PAGE_MASK, size, dir);
+	}
 }
 
 static void arm_dma_sync_single_for_cpu(struct device *dev,
@@ -314,10 +341,6 @@
 			VM_ARM_DMA_CONSISTENT | VM_USERMAP);
 }
 
-#define DEFAULT_DMA_COHERENT_POOL_SIZE	SZ_256K
-static struct gen_pool *atomic_pool;
-
-static size_t atomic_pool_size = DEFAULT_DMA_COHERENT_POOL_SIZE;
 
 static int __init early_coherent_pool(char *p)
 {
@@ -347,7 +370,9 @@
 static int __init atomic_pool_init(void)
 {
 	pgprot_t prot = pgprot_dmacoherent(PAGE_KERNEL);
+#ifndef CONFIG_COMCERTO_DMA_COHERENT_SKB
 	gfp_t gfp = GFP_KERNEL | GFP_DMA;
+#endif
 	struct page *page;
 	void *ptr;
 
@@ -358,9 +383,24 @@
 	if (dev_get_cma_area(NULL))
 		ptr = __alloc_from_contiguous(NULL, atomic_pool_size, prot,
 					      &page, atomic_pool_init, true);
-	else
+	else {
+#ifdef CONFIG_COMCERTO_DMA_COHERENT_SKB
+		/*
+		 * For CONFIG_COMCERTO_DMA_COHERENT_SKB, we must not use
+		 * __alloc_remap_buffer(), because it maps the DMA coherent
+		 * pages into vmalloc space which is incompatible with
+		 * dma_map_single(). You can't pass a pointer from vmalloc
+		 * space into dma_map_single().
+		 */
+		WARN(1, "If CONFIG_COMCERTO_DMA_COHERENT_SKB is enabled, the "
+				"DMA coherent pool must come from CMA. "
+				"Consider enabling CMA.");
+		ptr = 0;
+#else
 		ptr = __alloc_remap_buffer(NULL, atomic_pool_size, gfp, prot,
 					   &page, atomic_pool_init, true);
+#endif
+	}
 	if (ptr) {
 		int ret;
 
@@ -509,11 +549,6 @@
 	return ptr;
 }
 
-static bool __in_atomic_pool(void *start, size_t size)
-{
-	return addr_in_gen_pool(atomic_pool, (unsigned long)start, size);
-}
-
 static int __free_from_pool(void *start, size_t size)
 {
 	if (!__in_atomic_pool(start, size))
diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c
index 7186382..6fe3de9 100644
--- a/arch/arm/mm/mmu.c
+++ b/arch/arm/mm/mmu.c
@@ -854,6 +854,7 @@
 		return;
 	}
 
+	printk("Virtual 0x%08lx phys 0x%08llx length 0x%08lx\n", md->virtual, (long long)__pfn_to_phys((u64)md->pfn), md->length);
 	if ((md->type == MT_DEVICE || md->type == MT_ROM) &&
 	    md->virtual >= PAGE_OFFSET &&
 	    (md->virtual < VMALLOC_START || md->virtual >= VMALLOC_END)) {
diff --git a/arch/arm/mm/proc-v7.S b/arch/arm/mm/proc-v7.S
index 7911f14..9887a8d 100644
--- a/arch/arm/mm/proc-v7.S
+++ b/arch/arm/mm/proc-v7.S
@@ -261,6 +261,7 @@
 __v7_ca9mp_setup:
 __v7_cr7mp_setup:
 	mov	r10, #(1 << 0)			@ Cache/TLB ops broadcasting
+	orr     r10, #(3 << 1)                  @ L1 data prefetch, L2 prefetch hints enable
 	b	1f
 __v7_ca7mp_setup:
 __v7_ca12mp_setup:
diff --git a/drivers/char/tpm/tpm_i2c_infineon.c b/drivers/char/tpm/tpm_i2c_infineon.c
index 33c5f36..e7d97fb 100644
--- a/drivers/char/tpm/tpm_i2c_infineon.c
+++ b/drivers/char/tpm/tpm_i2c_infineon.c
@@ -23,6 +23,7 @@
  */
 #include <linux/i2c.h>
 #include <linux/module.h>
+#include <linux/reboot.h>
 #include <linux/wait.h>
 #include "tpm.h"
 
@@ -69,6 +70,8 @@
 	u8 buf[TPM_BUFSIZE + sizeof(u8)]; /* max. buffer size + addr */
 	struct tpm_chip *chip;
 	enum i2c_chip_type chip_type;
+	bool reboot_in_progress;
+	struct mutex reboot_lock;
 };
 
 static struct tpm_inf_dev tpm_dev;
@@ -114,6 +117,16 @@
 	/* Lock the adapter for the duration of the whole sequence. */
 	if (!tpm_dev.client->adapter->algo->master_xfer)
 		return -EOPNOTSUPP;
+
+	mutex_lock(&tpm_dev.reboot_lock);
+
+	if (tpm_dev.reboot_in_progress) {
+		dev_warn(tpm_dev.chip->pdev, "reboot in progress, aborting %s\n",
+			__func__);
+		mutex_unlock(&tpm_dev.reboot_lock);
+		return -EIO;
+	}
+
 	i2c_lock_adapter(tpm_dev.client->adapter);
 
 	if (tpm_dev.chip_type == SLB9645) {
@@ -156,6 +169,9 @@
 
 out:
 	i2c_unlock_adapter(tpm_dev.client->adapter);
+
+	mutex_unlock(&tpm_dev.reboot_lock);
+
 	/* take care of 'guard time' */
 	usleep_range(SLEEP_DURATION_LOW, SLEEP_DURATION_HI);
 
@@ -187,6 +203,13 @@
 
 	if (!tpm_dev.client->adapter->algo->master_xfer)
 		return -EOPNOTSUPP;
+
+	if (tpm_dev.reboot_in_progress) {
+                dev_warn(tpm_dev.chip->pdev, "reboot in progress, aborting %s\n",
+                        __func__);
+                return -EIO;
+        }
+
 	i2c_lock_adapter(tpm_dev.client->adapter);
 
 	/* prepend the 'register address' to the buffer */
@@ -642,6 +665,22 @@
 
 MODULE_DEVICE_TABLE(i2c, tpm_tis_i2c_table);
 
+static int tpm_reboot_handler(struct notifier_block *nb,unsigned long action, void *data)
+{
+	/* The assignment of the variable is atomic, the main purpose of the
+	   lock is to avoid returning from the handler while communicating with
+	   the TPM in iic_tpm_read() */
+	mutex_lock(&tpm_dev.reboot_lock);
+	tpm_dev.reboot_in_progress = true;
+	mutex_unlock(&tpm_dev.reboot_lock);
+
+	return 0;
+}
+
+static struct notifier_block tpm_reboot_notifier = {
+	.notifier_call  = tpm_reboot_handler
+};
+
 #ifdef CONFIG_OF
 static const struct of_device_id tpm_tis_i2c_of_match[] = {
 	{
@@ -685,12 +724,18 @@
 		return -ENODEV;
 	}
 
+	mutex_init(&tpm_dev.reboot_lock);
+
 	tpm_dev.client = client;
 	rc = tpm_tis_i2c_init(&client->dev);
 	if (rc != 0) {
 		tpm_dev.client = NULL;
 		rc = -ENODEV;
 	}
+
+	tpm_dev.reboot_in_progress = false;
+	register_reboot_notifier(&tpm_reboot_notifier);
+
 	return rc;
 }
 
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 9f9cadd..c09767b 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -524,6 +524,7 @@
 	if (clk->flags & CLK_IGNORE_UNUSED)
 		goto unlock_out;
 
+	pr_err("Disabling unused clock %s\n", clk->name);
 	/*
 	 * some gate clocks have special needs during the disable-unused
 	 * sequence.  call .disable_unused if available, otherwise fall
diff --git a/drivers/hwmon/lm63.c b/drivers/hwmon/lm63.c
index 33bfdb4..d4b8c27 100644
--- a/drivers/hwmon/lm63.c
+++ b/drivers/hwmon/lm63.c
@@ -44,10 +44,12 @@
 #include <linux/i2c.h>
 #include <linux/hwmon-sysfs.h>
 #include <linux/hwmon.h>
+#include <linux/ktime.h>
 #include <linux/err.h>
 #include <linux/mutex.h>
 #include <linux/sysfs.h>
 #include <linux/types.h>
+#include <linux/delay.h>
 
 /*
  * Addresses to scan
@@ -73,6 +75,7 @@
 #define LM63_REG_TACH_LIMIT_MSB		0x49
 #define LM63_REG_TACH_LIMIT_LSB		0x48
 
+#define LM63_REG_SPINUP			0x4B
 #define LM63_REG_PWM_VALUE		0x4C
 #define LM63_REG_PWM_FREQ		0x4D
 #define LM63_REG_LUT_TEMP_HYST		0x4F
@@ -187,6 +190,64 @@
 	bool trutherm;
 };
 
+#define MAX_SMBUS_RETRIES	10
+
+static s32 lm63_i2c_smbus_write_byte_data(const struct i2c_client *client,
+		u8 command, u8 value) {
+	s32 ret;
+	int retries = MAX_SMBUS_RETRIES;
+	ktime_t start_time;
+	s64 d;
+	while (retries-- > 0) {
+		start_time = ktime_get();
+		ret = i2c_smbus_write_byte_data(client, command, value);
+		if (!ret) {
+			d = ktime_us_delta(ktime_get(), start_time);
+			if (d <= 22000) return 0;
+			dev_printk(KERN_DEBUG, &client->dev,
+				"I2C transaction took too long: %lld us.\n",
+				(long long) d);
+			ret = -ETIME;
+		}
+		dev_printk(KERN_DEBUG, &client->dev,
+			"Failed to write value 0x%02x to register 0x%02x: rc %d. Retries left: %d\n",
+			value, command, ret, retries);
+		msleep(100);
+	}
+	dev_warn(&client->dev,
+		"Failed %d times to write value 0x%02x to register 0x%02x: rc %d\n",
+		MAX_SMBUS_RETRIES, value, command, ret);
+	return ret;
+}
+
+static s32 lm63_i2c_smbus_read_byte_data(const struct i2c_client *client,
+		u8 command) {
+	s32 ret;
+	int retries = MAX_SMBUS_RETRIES;
+	ktime_t start_time;
+	s64 d;
+	while (retries-- > 0) {
+		start_time = ktime_get();
+		ret = i2c_smbus_read_byte_data(client, command);
+		if (ret >= 0) {
+			d = ktime_us_delta(ktime_get(), start_time);
+			if (d <= 22000) return ret;
+			dev_printk(KERN_DEBUG, &client->dev,
+				"I2C transaction took too long: %lld us.\n",
+				(long long) d);
+			ret = -ETIME;
+		}
+		dev_printk(KERN_DEBUG, &client->dev,
+			"Failed to read from register 0x%02x: rc %d. Retries left: %d\n",
+			command, ret, retries);
+		msleep(100);
+	}
+	dev_warn(&client->dev,
+		"Failed %d times to read from register 0x%02x: rc %d\n",
+		MAX_SMBUS_RETRIES, command, ret);
+	return ret;
+}
+
 static inline int temp8_from_reg(struct lm63_data *data, int nr)
 {
 	if (data->remote_unsigned)
@@ -220,12 +281,12 @@
 	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,
+			data->pwm1[1 + i] = lm63_i2c_smbus_read_byte_data(client,
 					    LM63_REG_LUT_PWM(i));
-			data->temp8[3 + i] = i2c_smbus_read_byte_data(client,
+			data->temp8[3 + i] = lm63_i2c_smbus_read_byte_data(client,
 					     LM63_REG_LUT_TEMP(i));
 		}
-		data->lut_temp_hyst = i2c_smbus_read_byte_data(client,
+		data->lut_temp_hyst = lm63_i2c_smbus_read_byte_data(client,
 				      LM63_REG_LUT_TEMP_HYST);
 
 		data->lut_last_updated = jiffies;
@@ -246,58 +307,58 @@
 	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,
+			data->fan[0] = lm63_i2c_smbus_read_byte_data(client,
 				       LM63_REG_TACH_COUNT_LSB) & 0xFC;
-			data->fan[0] |= i2c_smbus_read_byte_data(client,
+			data->fan[0] |= lm63_i2c_smbus_read_byte_data(client,
 					LM63_REG_TACH_COUNT_MSB) << 8;
-			data->fan[1] = (i2c_smbus_read_byte_data(client,
+			data->fan[1] = (lm63_i2c_smbus_read_byte_data(client,
 					LM63_REG_TACH_LIMIT_LSB) & 0xFC)
-				     | (i2c_smbus_read_byte_data(client,
+				     | (lm63_i2c_smbus_read_byte_data(client,
 					LM63_REG_TACH_LIMIT_MSB) << 8);
 		}
 
-		data->pwm1_freq = i2c_smbus_read_byte_data(client,
+		data->pwm1_freq = lm63_i2c_smbus_read_byte_data(client,
 				  LM63_REG_PWM_FREQ);
 		if (data->pwm1_freq == 0)
 			data->pwm1_freq = 1;
-		data->pwm1[0] = i2c_smbus_read_byte_data(client,
+		data->pwm1[0] = lm63_i2c_smbus_read_byte_data(client,
 				LM63_REG_PWM_VALUE);
 
-		data->temp8[0] = i2c_smbus_read_byte_data(client,
+		data->temp8[0] = lm63_i2c_smbus_read_byte_data(client,
 				 LM63_REG_LOCAL_TEMP);
-		data->temp8[1] = i2c_smbus_read_byte_data(client,
+		data->temp8[1] = lm63_i2c_smbus_read_byte_data(client,
 				 LM63_REG_LOCAL_HIGH);
 
 		/* order matters for temp2_input */
-		data->temp11[0] = i2c_smbus_read_byte_data(client,
+		data->temp11[0] = lm63_i2c_smbus_read_byte_data(client,
 				  LM63_REG_REMOTE_TEMP_MSB) << 8;
-		data->temp11[0] |= i2c_smbus_read_byte_data(client,
+		data->temp11[0] |= lm63_i2c_smbus_read_byte_data(client,
 				   LM63_REG_REMOTE_TEMP_LSB);
-		data->temp11[1] = (i2c_smbus_read_byte_data(client,
+		data->temp11[1] = (lm63_i2c_smbus_read_byte_data(client,
 				  LM63_REG_REMOTE_LOW_MSB) << 8)
-				| i2c_smbus_read_byte_data(client,
+				| lm63_i2c_smbus_read_byte_data(client,
 				  LM63_REG_REMOTE_LOW_LSB);
-		data->temp11[2] = (i2c_smbus_read_byte_data(client,
+		data->temp11[2] = (lm63_i2c_smbus_read_byte_data(client,
 				  LM63_REG_REMOTE_HIGH_MSB) << 8)
-				| i2c_smbus_read_byte_data(client,
+				| lm63_i2c_smbus_read_byte_data(client,
 				  LM63_REG_REMOTE_HIGH_LSB);
-		data->temp11[3] = (i2c_smbus_read_byte_data(client,
+		data->temp11[3] = (lm63_i2c_smbus_read_byte_data(client,
 				  LM63_REG_REMOTE_OFFSET_MSB) << 8)
-				| i2c_smbus_read_byte_data(client,
+				| lm63_i2c_smbus_read_byte_data(client,
 				  LM63_REG_REMOTE_OFFSET_LSB);
 
 		if (data->kind == lm96163)
-			data->temp11u = (i2c_smbus_read_byte_data(client,
+			data->temp11u = (lm63_i2c_smbus_read_byte_data(client,
 					LM96163_REG_REMOTE_TEMP_U_MSB) << 8)
-				      | i2c_smbus_read_byte_data(client,
+				      | lm63_i2c_smbus_read_byte_data(client,
 					LM96163_REG_REMOTE_TEMP_U_LSB);
 
-		data->temp8[2] = i2c_smbus_read_byte_data(client,
+		data->temp8[2] = lm63_i2c_smbus_read_byte_data(client,
 				 LM63_REG_REMOTE_TCRIT);
-		data->temp2_crit_hyst = i2c_smbus_read_byte_data(client,
+		data->temp2_crit_hyst = lm63_i2c_smbus_read_byte_data(client,
 					LM63_REG_REMOTE_TCRIT_HYST);
 
-		data->alarms = i2c_smbus_read_byte_data(client,
+		data->alarms = lm63_i2c_smbus_read_byte_data(client,
 			       LM63_REG_ALERT_STATUS) & 0x7F;
 
 		data->last_updated = jiffies;
@@ -362,9 +423,9 @@
 
 	mutex_lock(&data->update_lock);
 	data->fan[1] = FAN_TO_REG(val);
-	i2c_smbus_write_byte_data(client, LM63_REG_TACH_LIMIT_LSB,
+	lm63_i2c_smbus_write_byte_data(client, LM63_REG_TACH_LIMIT_LSB,
 				  data->fan[1] & 0xFF);
-	i2c_smbus_write_byte_data(client, LM63_REG_TACH_LIMIT_MSB,
+	lm63_i2c_smbus_write_byte_data(client, LM63_REG_TACH_LIMIT_MSB,
 				  data->fan[1] >> 8);
 	mutex_unlock(&data->update_lock);
 	return count;
@@ -412,7 +473,7 @@
 	mutex_lock(&data->update_lock);
 	data->pwm1[nr] = data->pwm_highres ? val :
 			(val * data->pwm1_freq * 2 + 127) / 255;
-	i2c_smbus_write_byte_data(client, reg, data->pwm1[nr]);
+	lm63_i2c_smbus_write_byte_data(client, reg, data->pwm1[nr]);
 	mutex_unlock(&data->update_lock);
 	return count;
 }
@@ -447,13 +508,13 @@
 		return -EPERM;
 
 	mutex_lock(&data->update_lock);
-	data->config_fan = i2c_smbus_read_byte_data(client,
+	data->config_fan = lm63_i2c_smbus_read_byte_data(client,
 						    LM63_REG_CONFIG_FAN);
 	if (val == 1)
 		data->config_fan |= 0x20;
 	else
 		data->config_fan &= ~0x20;
-	i2c_smbus_write_byte_data(client, LM63_REG_CONFIG_FAN,
+	lm63_i2c_smbus_write_byte_data(client, LM63_REG_CONFIG_FAN,
 				  data->config_fan);
 	mutex_unlock(&data->update_lock);
 	return count;
@@ -528,7 +589,7 @@
 		temp = lut_temp_to_reg(data, val);
 	}
 	data->temp8[nr] = temp;
-	i2c_smbus_write_byte_data(client, reg, temp);
+	lm63_i2c_smbus_write_byte_data(client, reg, temp);
 	mutex_unlock(&data->update_lock);
 	return count;
 }
@@ -588,9 +649,9 @@
 	else
 		data->temp11[nr] = TEMP11_TO_REG(val - data->temp2_offset);
 
-	i2c_smbus_write_byte_data(client, reg[(nr - 1) * 2],
+	lm63_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],
+	lm63_i2c_smbus_write_byte_data(client, reg[(nr - 1) * 2 + 1],
 				  data->temp11[nr] & 0xff);
 	mutex_unlock(&data->update_lock);
 	return count;
@@ -640,7 +701,7 @@
 
 	mutex_lock(&data->update_lock);
 	hyst = temp8_from_reg(data, 2) + data->temp2_offset - val;
-	i2c_smbus_write_byte_data(client, LM63_REG_REMOTE_TCRIT_HYST,
+	lm63_i2c_smbus_write_byte_data(client, LM63_REG_REMOTE_TCRIT_HYST,
 				  HYST_TO_REG(hyst));
 	mutex_unlock(&data->update_lock);
 	return count;
@@ -666,7 +727,7 @@
 		if (interval >= update_interval * 3 / 4)
 			break;
 
-	i2c_smbus_write_byte_data(client, LM63_REG_CONVRATE, i);
+	lm63_i2c_smbus_write_byte_data(client, LM63_REG_CONVRATE, i);
 	data->update_interval = UPDATE_INTERVAL(data->max_convrate_hz, i);
 }
 
@@ -722,8 +783,8 @@
 
 	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 = lm63_i2c_smbus_read_byte_data(client, LM96163_REG_TRUTHERM) & ~0x02;
+	lm63_i2c_smbus_write_byte_data(client, LM96163_REG_TRUTHERM,
 				  reg | (data->trutherm ? 0x02 : 0x00));
 	data->valid = 0;
 	mutex_unlock(&data->update_lock);
@@ -989,14 +1050,14 @@
 	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
 		return -ENODEV;
 
-	man_id = i2c_smbus_read_byte_data(client, LM63_REG_MAN_ID);
-	chip_id = i2c_smbus_read_byte_data(client, LM63_REG_CHIP_ID);
+	man_id = lm63_i2c_smbus_read_byte_data(client, LM63_REG_MAN_ID);
+	chip_id = lm63_i2c_smbus_read_byte_data(client, LM63_REG_CHIP_ID);
 
-	reg_config1 = i2c_smbus_read_byte_data(client, LM63_REG_CONFIG1);
-	reg_config2 = i2c_smbus_read_byte_data(client, LM63_REG_CONFIG2);
-	reg_alert_status = i2c_smbus_read_byte_data(client,
+	reg_config1 = lm63_i2c_smbus_read_byte_data(client, LM63_REG_CONFIG1);
+	reg_config2 = lm63_i2c_smbus_read_byte_data(client, LM63_REG_CONFIG2);
+	reg_alert_status = lm63_i2c_smbus_read_byte_data(client,
 			   LM63_REG_ALERT_STATUS);
-	reg_alert_mask = i2c_smbus_read_byte_data(client, LM63_REG_ALERT_MASK);
+	reg_alert_mask = lm63_i2c_smbus_read_byte_data(client, LM63_REG_ALERT_MASK);
 
 	if (man_id != 0x01 /* National Semiconductor */
 	 || (reg_config1 & 0x18) != 0x00
@@ -1030,24 +1091,38 @@
 	struct i2c_client *client = data->client;
 	struct device *dev = &client->dev;
 	u8 convrate;
+	u8 config2;
 
-	data->config = i2c_smbus_read_byte_data(client, LM63_REG_CONFIG1);
-	data->config_fan = i2c_smbus_read_byte_data(client,
+	data->config = lm63_i2c_smbus_read_byte_data(client, LM63_REG_CONFIG1);
+	data->config_fan = lm63_i2c_smbus_read_byte_data(client,
 						    LM63_REG_CONFIG_FAN);
 
 	/* Start converting if needed */
 	if (data->config & 0x40) { /* standby */
 		dev_dbg(dev, "Switching to operational mode\n");
 		data->config &= 0xA7;
-		i2c_smbus_write_byte_data(client, LM63_REG_CONFIG1,
+		lm63_i2c_smbus_write_byte_data(client, LM63_REG_CONFIG1,
 					  data->config);
 	}
+
+	/* Enable digital filter */
+	/* From the LM96063 datasheet:
+	 * "In order to suppress erroneous remote temperature readings due to
+	 * noise as well as increase the resolution of the temperature, the
+	 * LM96063 incorporates a digital filter for remote temperature
+	 * readings." */
+	config2 = lm63_i2c_smbus_read_byte_data(client, LM63_REG_CONFIG2);
+	config2 |= 0x6; /* Enhanced Filter (Filter with transient noise
+			   clipping) */
+	lm63_i2c_smbus_write_byte_data(client, LM63_REG_CONFIG2,
+			config2);
+
 	/* 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);
+	data->pwm1_freq = lm63_i2c_smbus_read_byte_data(client, LM63_REG_PWM_FREQ);
 	if (data->pwm1_freq == 0)
 		data->pwm1_freq = 1;
 
@@ -1061,11 +1136,11 @@
 		data->max_convrate_hz = LM96163_MAX_CONVRATE_HZ;
 		data->lut_size = 12;
 		data->trutherm
-		  = i2c_smbus_read_byte_data(client,
+		  = lm63_i2c_smbus_read_byte_data(client,
 					     LM96163_REG_TRUTHERM) & 0x02;
 		break;
 	}
-	convrate = i2c_smbus_read_byte_data(client, LM63_REG_CONVRATE);
+	convrate = lm63_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,
@@ -1077,7 +1152,7 @@
 	 */
 	if (data->kind == lm96163) {
 		u8 config_enhanced
-		  = i2c_smbus_read_byte_data(client,
+		  = lm63_i2c_smbus_read_byte_data(client,
 					     LM96163_REG_CONFIG_ENHANCED);
 		if (config_enhanced & 0x20)
 			data->lut_temp_highres = true;
@@ -1088,6 +1163,9 @@
 			data->remote_unsigned = true;
 	}
 
+	/* 50% spinup for 3.2 seconds */
+	lm63_i2c_smbus_write_byte_data(client, LM63_REG_SPINUP, 0x0f);
+
 	/* Show some debug info about the LM63 configuration */
 	if (data->kind == lm63)
 		dev_dbg(dev, "Alert/tach pin configured for %s\n",
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 2255af2..4509845 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -602,6 +602,16 @@
 	  This driver can also be built as a module. If so, the module
 	  will be called i2c-kempld.
 
+config I2C_LS1024A
+	tristate "Freescale LS1024A I2C interface"
+	depends on ARCH_COMCERTO
+	help
+	  Say Y here if you want to use the I2C bus controller on
+	  the Freescale LS1024A SoC.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called i2c-ls1024a.
+
 config I2C_MESON
 	tristate "Amlogic Meson I2C controller"
 	depends on ARCH_MESON
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index cdf941d..ca8e1ec 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -58,6 +58,7 @@
 obj-$(CONFIG_I2C_IOP3XX)	+= i2c-iop3xx.o
 obj-$(CONFIG_I2C_JZ4780)	+= i2c-jz4780.o
 obj-$(CONFIG_I2C_KEMPLD)	+= i2c-kempld.o
+obj-$(CONFIG_I2C_LS1024A)	+= i2c-ls1024a.o
 obj-$(CONFIG_I2C_MESON)		+= i2c-meson.o
 obj-$(CONFIG_I2C_MPC)		+= i2c-mpc.o
 obj-$(CONFIG_I2C_MV64XXX)	+= i2c-mv64xxx.o
diff --git a/drivers/i2c/busses/i2c-ls1024a.c b/drivers/i2c/busses/i2c-ls1024a.c
new file mode 100644
index 0000000..5a5bb01
--- /dev/null
+++ b/drivers/i2c/busses/i2c-ls1024a.c
@@ -0,0 +1,626 @@
+/*
+ *  Copyright (C) 2008 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/clk.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+struct ls1024a_i2c_dev {
+	struct device		*dev;
+	struct i2c_adapter	adapter;
+	void __iomem		*membase;
+	int			irq;
+	struct clk		*clk;
+	wait_queue_head_t	wait;
+	struct i2c_msg		*msg;
+	int			msg_state;
+	int			msg_status;	/* < 0: error, == 0: success, > 0: message in progress */
+	int			msg_len;
+	int			msg_retries;
+};
+
+#define SPEED_NORMAL_KHZ	100
+
+#define COMCERTO_I2C_ADDR		(0x00*4)
+#define COMCERTO_I2C_DATA		(0x01*4)
+#define COMCERTO_I2C_CNTR		(0x02*4)
+#define COMCERTO_I2C_STAT		(0x03*4)
+#define COMCERTO_I2C_CCRFS		(0x03*4)
+#define COMCERTO_I2C_XADDR		(0x04*4)
+#define COMCERTO_I2C_CCRH		(0x05*4)
+#define COMCERTO_I2C_RESET		(0x07*4)
+
+/* CNTR - Control register bits */
+#define CNTR_IEN			(1<<7)
+#define CNTR_ENAB			(1<<6)
+#define CNTR_STA			(1<<5)
+#define CNTR_STP			(1<<4)
+#define CNTR_IFLG			(1<<3)
+#define CNTR_AAK			(1<<2)
+
+/* STAT - Status codes */
+#define STAT_BUS_ERROR			0x00	/* Bus error in master mode only */
+#define STAT_START			0x08	/* Start condition transmitted */
+#define STAT_START_REPEATED		0x10	/* Repeated Start condition transmited */
+#define STAT_ADDR_WR_ACK		0x18	/* Address + Write bit transmitted, ACK received */
+#define STAT_ADDR_WR_NACK		0x20	/* Address + Write bit transmitted, NACK received */
+#define STAT_DATA_WR_ACK		0x28	/* Data byte transmitted in master mode , ACK received */
+#define STAT_DATA_WR_NACK		0x30	/* Data byte transmitted in master mode , NACK received */
+#define STAT_ARBIT_LOST			0x38	/* Arbitration lost in address or data byte */
+#define STAT_ADDR_RD_ACK		0x40	/* Address + Read bit transmitted, ACK received  */
+#define STAT_ADDR_RD_NACK		0x48	/* Address + Read bit transmitted, NACK received  */
+#define STAT_DATA_RD_ACK		0x50	/* Data byte received in master mode, ACK transmitted  */
+#define STAT_DATA_RD_NACK		0x58	/* Data byte received in master mode, NACK transmitted*/
+#define STAT_ARBIT_LOST_ADDR		0x68	/* Arbitration lost in address  */
+#define STAT_GENERAL_CALL		0x70	/* General Call, ACK transmitted */
+#define STAT_NO_RELEVANT_INFO		0xF8	/* No relevant status information, IFLF=0 */
+
+#define REG_ADDR(i2c, offset)		((i2c)->membase + (offset))
+#define RD_REG(i2c, offset)		readb(REG_ADDR(i2c, offset))
+#define WR_REG(i2c, offset, byte)	writeb(byte, REG_ADDR(i2c, offset))
+#define RD_DATA(i2c)			RD_REG(i2c, COMCERTO_I2C_DATA)
+#define WR_DATA(i2c, byte)		WR_REG(i2c, COMCERTO_I2C_DATA, byte)
+#define RD_CNTR(i2c)			RD_REG(i2c, COMCERTO_I2C_CNTR)
+#define WR_CNTR(i2c, byte)		WR_REG(i2c, COMCERTO_I2C_CNTR, byte)
+#define RD_STAT(i2c)			RD_REG(i2c, COMCERTO_I2C_STAT)
+#define WR_CCRFS(i2c, byte)		WR_REG(i2c, COMCERTO_I2C_CCRFS, byte)
+#define WR_CCRH(i2c, byte)		WR_REG(i2c, COMCERTO_I2C_CCRH, byte)
+#define WR_RESET(i2c, byte)		WR_REG(i2c, COMCERTO_I2C_RESET, byte)
+
+enum
+{
+	TR_IDLE = 0,
+	TR_START_ACK,
+	TR_ADDR_ACK,
+	TR_DATA_ACK,
+	RX_DATA_NACK,
+};
+
+static u8 comcerto_i2c_calculate_dividers(struct ls1024a_i2c_dev *i2c)
+{
+	int m, n, hz, speed_hz;
+	int saved_n, saved_m, saved_hz;
+	u8 dividers;
+	unsigned int i2c_clk;
+
+	/* Get the i2c clock rate */
+	i2c_clk = clk_get_rate(i2c->clk);
+
+	speed_hz = SPEED_NORMAL_KHZ * 1000;
+	saved_hz = saved_n = saved_m = 0;
+
+	for (m = 0; m < 16; m++) {
+		for (n = 0; n < 8; n++) {
+			hz = i2c_clk / ((1 << n) * (m + 1) * 10);
+			if (!saved_hz || abs(speed_hz - hz) < abs(speed_hz - saved_hz)) {
+				saved_n = n;
+				saved_m = m;
+				saved_hz = hz;
+			}
+		}
+	}
+
+	dividers = (saved_m << 3) | saved_n;
+	dev_dbg(i2c->dev, "%s: speed=%dkHz, M=%d, N=%d, dividers=0x%02x\n", __FUNCTION__,
+		saved_hz/1000, saved_m, saved_n, dividers);
+	printk("%s: speed=%dkHz, M=%d, N=%d, dividers=0x%02x\n", __FUNCTION__, saved_hz/1000, saved_m, saved_n, dividers);
+
+	return dividers;
+}
+
+/*
+ * Returns the timeout (in jiffies) for the given message.
+ */
+static int comcerto_i2c_calculate_timeout(struct ls1024a_i2c_dev *i2c, struct i2c_msg *msg)
+{
+	int timeout;
+
+	/* if no timeout was specified, calculate it */
+	if (i2c->adapter.timeout <= 0) {
+		if (i2c->irq >= 0) {
+			/* for the interrupt mode calculate timeout for 'full' message */
+			timeout = ((int)msg->len) * 10;	/* convert approx. to bits */
+			timeout /= SPEED_NORMAL_KHZ;	/* convert to bits per ms (note of kHz scale) */
+			timeout += timeout >> 1;	/* add 50% */
+			timeout = timeout*HZ / 1000;	/* convert to jiffies */
+			if (timeout < HZ / 5)		/* at least 200ms */
+				timeout = HZ / 5;
+		}
+		else
+			timeout = HZ;			/* 1 second for the polling mode */
+	}
+	else
+		timeout = i2c->adapter.timeout;
+
+	return timeout;
+}
+
+/*
+ * Initialize I2C core. Zero CNTR and DATA, try RESET. Short busy wait and check core status.
+ * After that set dividers for choosen speed.
+ */
+static void comcerto_i2c_reset(struct ls1024a_i2c_dev *i2c)
+{
+	u8 status, dividers;
+
+	dev_dbg(i2c->dev, "%s\n", __FUNCTION__);
+
+	WR_CNTR(i2c, 0);
+	WR_DATA(i2c, 0);
+	WR_RESET(i2c, 1);
+
+	udelay(10);
+
+	status = RD_STAT(i2c);
+	if (status != STAT_NO_RELEVANT_INFO)
+		dev_printk(KERN_DEBUG, i2c->dev, "%s: unexpected status after reset: 0x%02x\n", __FUNCTION__, status);
+
+	/* dividers should be placed in CCRH for high-sped mode and in CCRFS for standard/full modes */
+	dividers = comcerto_i2c_calculate_dividers(i2c);
+	WR_CCRFS(i2c, dividers);
+}
+
+static inline void comcerto_i2c_message_complete(struct ls1024a_i2c_dev *i2c, int status)
+{
+	WR_CNTR(i2c, CNTR_STP);
+
+	i2c->msg_status = status;
+}
+
+static inline int comcerto_i2c_message_in_progress(struct ls1024a_i2c_dev *i2c)
+{
+	return i2c->msg_status > 0;
+}
+
+/*
+ * Wait event. This function sleeps in polling mode, in interrupt
+ * mode it enables IRQ from I2C core and exits immediately.
+ */
+static int comcerto_i2c_wait(struct ls1024a_i2c_dev *i2c, u8 cntr)
+{
+	cntr &= ~(CNTR_IFLG | CNTR_IEN);	/* clear both IFLG and IEN */
+
+	if (i2c->irq < 0) {
+		ulong jiffies_mark = jiffies + comcerto_i2c_calculate_timeout(i2c, i2c->msg);
+
+		WR_CNTR(i2c, cntr);
+		while ((RD_CNTR(i2c) & CNTR_IFLG) == 0) {
+			if (need_resched())
+				schedule();
+
+			if (time_after(jiffies, jiffies_mark)) {
+				dev_printk(KERN_DEBUG, i2c->dev, "%s: polling transfer timeout\n", __FUNCTION__);
+				comcerto_i2c_message_complete(i2c, -ETIME);
+				comcerto_i2c_reset(i2c);
+				break;
+			}
+		}
+	}
+	else {
+		/* enable interrupt */
+		WR_CNTR(i2c, cntr | CNTR_IEN);
+	}
+
+	return 0;
+}
+
+static void comcerto_i2c_state_idle(struct ls1024a_i2c_dev *i2c, u8 *cntr)
+{
+	if (unlikely(i2c->msg->flags & I2C_M_NOSTART)) {
+		i2c->msg_state = TR_ADDR_ACK;
+	}
+	else {
+		*cntr = CNTR_STP|CNTR_STA;	/* SPT|STA to auto recover from bus error state transparently at the start of the transfer */
+		i2c->msg_state = TR_START_ACK;
+	}
+}
+
+static void comcerto_i2c_state_start_ack(struct ls1024a_i2c_dev *i2c, u8 *cntr)
+{
+	u8 status, addr;
+
+	*cntr = 0;	/* zero IFLG, IEN (for the interrupt mode it will be enabled in wait function) */
+
+	status = RD_STAT(i2c);
+
+	if (status == STAT_START || status == STAT_START_REPEATED) {
+		i2c->msg_state = TR_ADDR_ACK;
+
+		addr = i2c->msg->addr << 1;
+		if (i2c->msg->flags & I2C_M_RD)
+			addr |= 1;
+		if (i2c->msg->flags & I2C_M_REV_DIR_ADDR)
+			addr ^= 1;		/* invert RW bit if it's requested */
+
+		WR_DATA(i2c, addr);		/* write address and read/write bit */
+	} else {
+		dev_printk(KERN_DEBUG, i2c->dev, "%s: unexpected state (%#x) on start phase, %s\n",
+			__FUNCTION__, status, i2c->msg_retries > 1 ? "retrying":"aborting");
+
+		if (--i2c->msg_retries < 0)
+			comcerto_i2c_message_complete(i2c, -1);
+		else
+			comcerto_i2c_state_idle(i2c, cntr);
+	}
+}
+
+static void comcerto_i2c_rx(struct ls1024a_i2c_dev *i2c)
+{
+	u8 status, cntr = 0;
+
+restart:
+	switch (i2c->msg_state) {
+	case TR_IDLE:
+		comcerto_i2c_state_idle(i2c, &cntr);
+		if (unlikely(i2c->msg->flags & I2C_M_NOSTART))
+			goto restart;	/* needed to avoid event loss in interrupt mode */
+		break;
+
+	case TR_START_ACK:
+		comcerto_i2c_state_start_ack(i2c, &cntr);
+		break;
+
+	case TR_ADDR_ACK:
+		if (unlikely(i2c->msg->flags & I2C_M_NOSTART)) {
+			/* we can enter this state if skip start/addr flag is set, so fake good ack */
+			status = STAT_ADDR_RD_ACK;
+		}
+		else {
+			status = RD_STAT(i2c);
+			/* check whether we should ignore NACK */
+			if (status == STAT_DATA_RD_NACK && (i2c->msg->flags & I2C_M_IGNORE_NAK))
+				status = STAT_DATA_RD_ACK;
+		}
+
+		if (likely(status == STAT_ADDR_RD_ACK)) {
+			/* start reception phase - wait until data is ready and loop in RX_DATA_ACK state
+			 * until we read all the data, sending ACK after each byte (but the last)
+			 */
+			i2c->msg_len = 0;
+			if (i2c->msg->len > 1) {
+				i2c->msg_state = TR_DATA_ACK;
+				cntr = CNTR_AAK;
+			}
+			else if (i2c->msg->len == 1) {
+				i2c->msg_state = RX_DATA_NACK;
+			}
+			else {	/* nothing to receive, send STOP and signal success */
+				comcerto_i2c_message_complete(i2c, 0);
+			}
+		}
+		else {
+			dev_printk(KERN_DEBUG, i2c->dev, "%s: unexpected state (%#x) on address phase, %s\n",
+				__FUNCTION__, status, i2c->msg_retries > 1 ? "retrying":"aborting");
+
+			if (--i2c->msg_retries < 0)
+				comcerto_i2c_message_complete(i2c, -1);
+			else
+				comcerto_i2c_state_idle(i2c, &cntr);
+		}
+		break;
+
+	case TR_DATA_ACK:
+		status = RD_STAT(i2c);
+
+		if (likely(status == STAT_DATA_RD_ACK)) {
+			i2c->msg->buf[i2c->msg_len++] = RD_DATA(i2c);
+			if (likely(i2c->msg->len - i2c->msg_len > 1)) {
+				cntr = CNTR_AAK;
+			}
+			else {
+				i2c->msg_state = RX_DATA_NACK;
+				/* NACK should be transmitted on the last byte */
+			}
+		}
+		else {
+			dev_printk(KERN_DEBUG, i2c->dev, "%s: unexpected state (%#x) on read phase\n", __FUNCTION__, status);
+			comcerto_i2c_message_complete(i2c, -1);
+		}
+		break;
+
+	case RX_DATA_NACK:
+		status = RD_STAT(i2c);
+		if (likely(status == STAT_DATA_RD_NACK)) {
+			i2c->msg->buf[i2c->msg_len++] = RD_DATA(i2c);
+			comcerto_i2c_message_complete(i2c, 0);
+		}
+		else {
+			dev_printk(KERN_DEBUG, i2c->dev, "%s: unexpected state (%#x) on finishing read phase\n", __FUNCTION__, status);
+			comcerto_i2c_message_complete(i2c, -1);
+		}
+	}
+
+	/* no wait if we completed message */
+	if (comcerto_i2c_message_in_progress(i2c))
+		comcerto_i2c_wait(i2c, cntr);
+}
+
+static void comcerto_i2c_tx(struct ls1024a_i2c_dev *i2c)
+{
+	u8 status, cntr = 0;
+
+restart:
+	switch (i2c->msg_state) {
+	case TR_IDLE:
+		comcerto_i2c_state_idle(i2c, &cntr);
+		if (unlikely(i2c->msg->flags & I2C_M_NOSTART))
+			goto restart;	/* needed to avoid event loss in interrupt mode */
+		break;
+
+	case TR_START_ACK:
+		comcerto_i2c_state_start_ack(i2c, &cntr);
+		break;
+
+	case TR_ADDR_ACK:
+		if (unlikely(i2c->msg->flags & I2C_M_NOSTART)) {
+			/* we can enter this state if skip start/addr flag is set, so fake good ack */
+			status = STAT_ADDR_WR_ACK;
+		}
+		else {
+			status = RD_STAT(i2c);
+			if (status == STAT_DATA_WR_NACK && (i2c->msg->flags & I2C_M_IGNORE_NAK))
+				status = STAT_DATA_WR_ACK;
+		}
+
+		if (likely(status == STAT_ADDR_WR_ACK)) {
+			/* start reception phase - wait until data is ready and loop in TX_DATA_ACK state
+			 * until we read all the data, sending ACK after each byte (but the last)
+			 */
+			i2c->msg_state = TR_DATA_ACK;
+			i2c->msg_len = 0;
+			if (likely(i2c->msg->len != 0)) {
+				WR_DATA(i2c, i2c->msg->buf[i2c->msg_len++]);
+				//printk("comcerto_i2c_tx: i2c->msg->buf[i2c->msg_len - 1]=%d\n", i2c->msg->buf[i2c->msg_len - 1]);
+			}
+			else {
+				/* nothing to transmit, send STOP and signal success */
+				comcerto_i2c_message_complete(i2c, 0);
+			}
+		}
+		else {
+			dev_printk(KERN_DEBUG, i2c->dev, "%s: unexpected state (%#x) on address phase, %s\n",
+				__FUNCTION__, status, i2c->msg_retries > 1 ? "retrying":"aborting");
+
+			if (--i2c->msg_retries < 0)
+				comcerto_i2c_message_complete(i2c, -1);
+			else
+				comcerto_i2c_state_idle(i2c, &cntr);
+		}
+		break;
+
+	case TR_DATA_ACK:
+		status = RD_STAT(i2c);
+		if (status == STAT_DATA_WR_NACK && (i2c->msg->flags & I2C_M_IGNORE_NAK))
+			status = STAT_DATA_WR_ACK;
+
+		if (likely(status == STAT_DATA_WR_ACK)) {
+			if (i2c->msg->len > i2c->msg_len)
+				WR_DATA(i2c, i2c->msg->buf[i2c->msg_len++]);
+			else
+				comcerto_i2c_message_complete(i2c, 0);
+		}
+		else {
+			dev_printk(KERN_DEBUG, i2c->dev, "%s: unexpected state (%#x) on read data phase\n", __FUNCTION__, status);
+			comcerto_i2c_message_complete(i2c, -1);
+		}
+		break;
+	}
+
+	if (comcerto_i2c_message_in_progress(i2c))
+		comcerto_i2c_wait(i2c, cntr);
+}
+
+static irqreturn_t comcerto_i2c_interrupt(int irq, void *dev_id)
+{
+	struct ls1024a_i2c_dev *i2c = dev_id;
+
+	if (!(RD_CNTR(i2c) & CNTR_IFLG))
+		goto none;
+
+	/* IRQ enable/disable logic is hidden in state handlers, all we need is to wake
+	 * process when message completed.
+	 */
+	if (i2c->msg->flags & I2C_M_RD)
+		comcerto_i2c_rx(i2c);
+	else
+		comcerto_i2c_tx(i2c);
+
+	if (!comcerto_i2c_message_in_progress(i2c)) {
+		WR_CNTR(i2c, RD_CNTR(i2c) & ~CNTR_IEN);	/* disable interrupt unconditionally */
+		wake_up(&i2c->wait);
+	}
+
+	return IRQ_HANDLED;
+none:
+	return IRQ_NONE;
+}
+
+static void comcerto_i2c_message_process(struct ls1024a_i2c_dev *i2c, struct i2c_msg *msg)
+{
+	i2c->msg = msg;
+	i2c->msg_state = TR_IDLE;
+	i2c->msg_status = 1;
+	i2c->msg_retries = i2c->adapter.retries;
+
+polling_mode:
+	if (msg->flags & I2C_M_RD)
+		comcerto_i2c_rx(i2c);
+	else
+		comcerto_i2c_tx(i2c);
+
+	if (i2c->irq < 0) {
+		/*if (i2c->msg != NULL)*/
+			goto polling_mode;
+	}
+	else {
+		int timeout, res;
+		ulong flags;
+
+		timeout = comcerto_i2c_calculate_timeout(i2c, msg);
+
+		res = wait_event_timeout(i2c->wait, i2c->msg_status <= 0, timeout);
+
+		local_irq_save(flags);
+
+		/* check if we timed out and set respective error codes */
+		if (res == 0) {
+			if (comcerto_i2c_message_in_progress(i2c)) {
+				dev_printk(KERN_DEBUG, i2c->dev, "%s: interrupt transfer timeout\n", __FUNCTION__);
+				comcerto_i2c_message_complete(i2c, -ETIME);
+				comcerto_i2c_reset(i2c);
+			}
+		}
+
+		local_irq_restore(flags);
+	}
+}
+
+/*
+ * Generic master transfer entrypoint.
+ * Returns the number of processed messages or error value
+ */
+static int comcerto_i2c_master_xfer(struct i2c_adapter *adapter, struct i2c_msg msgs[], int num)
+{
+	struct ls1024a_i2c_dev *i2c = i2c_get_adapdata(adapter);
+	int i;
+
+	dev_dbg(i2c->dev, "%s: %d messages to process\n", __FUNCTION__, num);
+
+	for (i = 0; i < num; i++) {
+		dev_dbg(i2c->dev, "%s: message #%d: addr=%#x, flags=%#x, len=%u\n", __FUNCTION__,
+			i, msgs[i].addr, msgs[i].flags, msgs[i].len);
+
+		comcerto_i2c_message_process(i2c, &msgs[i]);
+
+		if (i2c->msg_status < 0) {
+			dev_printk(KERN_DEBUG, i2c->dev, "%s: transfer failed on message #%d (addr=%#x, flags=%#x, len=%u)\n",
+				__FUNCTION__, i, msgs[i].addr, msgs[i].flags, msgs[i].len);
+			break;
+		}
+	}
+
+	if (i2c->msg_status == -1)
+		i2c->msg_status = -EIO;
+
+	if (i2c->msg_status == 0)
+		i2c->msg_status = num;
+
+	return i2c->msg_status;
+}
+
+
+static u32 comcerto_i2c_functionality(struct i2c_adapter *adap)
+{
+	return (I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL);
+}
+
+static struct i2c_algorithm ls1024a_i2c_algo = {
+	.master_xfer	= comcerto_i2c_master_xfer,
+	.functionality	= comcerto_i2c_functionality,
+};
+
+static int ls1024a_i2c_probe(struct platform_device *pdev)
+{
+	struct ls1024a_i2c_dev *priv;
+	struct resource *res;
+	int err = 0;
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	priv->membase = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(priv->membase))
+		return PTR_ERR(priv->membase);
+
+	priv->clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(priv->clk))
+		return PTR_ERR(priv->clk);
+
+	clk_prepare_enable(priv->clk);
+
+	priv->irq = platform_get_irq(pdev, 0);
+	if (priv->irq <= 0) {
+		dev_err(&pdev->dev, "invalid irq\n");
+		return priv->irq;
+	}
+
+	err = devm_request_irq(&pdev->dev, priv->irq, comcerto_i2c_interrupt, 0,
+			       pdev->name, priv);
+	if (err) {
+		dev_err(&pdev->dev, "IRQ request failed\n");
+		return err;
+	}
+
+	init_waitqueue_head(&priv->wait);
+	priv->adapter.dev.parent = &pdev->dev;
+	priv->adapter.algo = &ls1024a_i2c_algo;
+	priv->adapter.dev.of_node = pdev->dev.of_node;
+	priv->dev = &pdev->dev;
+
+	snprintf(priv->adapter.name, sizeof(priv->adapter.name), "ls1024a-i2c");
+	i2c_set_adapdata(&priv->adapter, priv);
+
+	comcerto_i2c_reset(priv);
+
+	err = i2c_add_adapter(&priv->adapter);
+	if (err) {
+		dev_err(&pdev->dev, "failed to add I2C adapter\n");
+		return err;
+	}
+
+	platform_set_drvdata(pdev, priv);
+	dev_dbg(&pdev->dev, "I2C bus:%d added\n", priv->adapter.nr);
+
+	return 0;
+}
+
+static int ls1024a_i2c_remove(struct platform_device *pdev)
+{
+	struct ls1024a_i2c_dev *priv;
+
+	priv = platform_get_drvdata(pdev);
+	synchronize_irq(priv->irq);
+	i2c_del_adapter(&priv->adapter);
+
+	return 0;
+}
+
+static const struct of_device_id ls1024a_i2c_of_match[] = {
+	{ .compatible = "fsl,ls1024a-i2c", },
+	{ /* sentinel */ },
+};
+
+static struct platform_driver ls1024a_i2c_driver = {
+	.probe = ls1024a_i2c_probe,
+	.remove = ls1024a_i2c_remove,
+	.driver = {
+		.name = "ls1024a-i2c",
+		.of_match_table = ls1024a_i2c_of_match,
+	},
+};
+
+module_platform_driver(ls1024a_i2c_driver);
+
+MODULE_DESCRIPTION("Freescale LS1024A I2C Bus Controller Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c
index 2ba7c0f..8fdef6a 100644
--- a/drivers/i2c/i2c-mux.c
+++ b/drivers/i2c/i2c-mux.c
@@ -37,6 +37,7 @@
 	u32 chan_id;
 
 	int (*select)(struct i2c_adapter *, void *mux_priv, u32 chan_id);
+	int (*select_num)(struct i2c_adapter *, void *mux_priv, u32 chan_id, int num);
 	int (*deselect)(struct i2c_adapter *, void *mux_priv, u32 chan_id);
 };
 
@@ -49,7 +50,12 @@
 
 	/* Switch to the right mux port and perform the transfer. */
 
-	ret = priv->select(parent, priv->mux_priv, priv->chan_id);
+	if (priv->select_num)
+		ret = priv->select_num(parent, priv->mux_priv, priv->chan_id,
+				num);
+	else
+		ret = priv->select(parent, priv->mux_priv, priv->chan_id);
+
 	if (ret >= 0)
 		ret = __i2c_transfer(parent, msgs, num);
 	if (priv->deselect)
@@ -69,7 +75,12 @@
 
 	/* Select the right mux port and perform the transfer. */
 
-	ret = priv->select(parent, priv->mux_priv, priv->chan_id);
+	if (priv->select_num)
+		ret = priv->select_num(parent, priv->mux_priv,
+				priv->chan_id, read_write? 2 : 1);
+	else
+		ret = priv->select(parent, priv->mux_priv, priv->chan_id);
+
 	if (ret >= 0)
 		ret = parent->algo->smbus_xfer(parent, addr, flags,
 					read_write, command, size, data);
@@ -101,12 +112,14 @@
 	return class;
 }
 
-struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent,
+static struct i2c_adapter *__i2c_add_mux_adapter(struct i2c_adapter *parent,
 				struct device *mux_dev,
 				void *mux_priv, u32 force_nr, u32 chan_id,
 				unsigned int class,
 				int (*select) (struct i2c_adapter *,
 					       void *, u32),
+				int (*select_num) (struct i2c_adapter *,
+					       void *, u32, int),
 				int (*deselect) (struct i2c_adapter *,
 						 void *, u32))
 {
@@ -124,6 +137,7 @@
 	priv->mux_priv = mux_priv;
 	priv->chan_id = chan_id;
 	priv->select = select;
+	priv->select_num = select_num;
 	priv->deselect = deselect;
 
 	/* Need to do algo dynamically because we don't know ahead
@@ -198,8 +212,35 @@
 
 	return &priv->adap;
 }
+
+struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent,
+				struct device *mux_dev,
+				void *mux_priv, u32 force_nr, u32 chan_id,
+				unsigned int class,
+				int (*select) (struct i2c_adapter *,
+					       void *, u32),
+				int (*deselect) (struct i2c_adapter *,
+						 void *, u32))
+{
+	return __i2c_add_mux_adapter(parent, mux_dev, mux_priv, force_nr,
+				chan_id, class, select, NULL, deselect);
+}
 EXPORT_SYMBOL_GPL(i2c_add_mux_adapter);
 
+struct i2c_adapter *i2c_add_mux_adapter_num(struct i2c_adapter *parent,
+				struct device *mux_dev,
+				void *mux_priv, u32 force_nr, u32 chan_id,
+				unsigned int class,
+				int (*select_num) (struct i2c_adapter *,
+						void *, u32, int),
+				int (*deselect) (struct i2c_adapter *,
+						 void *, u32))
+{
+	return __i2c_add_mux_adapter(parent, mux_dev, mux_priv, force_nr,
+				chan_id, class, NULL, select_num, deselect);
+}
+EXPORT_SYMBOL_GPL(i2c_add_mux_adapter_num);
+
 void i2c_del_mux_adapter(struct i2c_adapter *adap)
 {
 	struct i2c_mux_priv *priv = adap->algo_data;
diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c
index e8b1120..9e49f2e 100644
--- a/drivers/leds/led-triggers.c
+++ b/drivers/leds/led-triggers.c
@@ -115,8 +115,10 @@
 	char *envp[2];
 	const char *name;
 
-	name = trig ? trig->name : "none";
-	event = kasprintf(GFP_KERNEL, "TRIGGER=%s", name);
+	if (trig != led_cdev->trigger) {
+		name = trig ? trig->name : "none";
+		event = kasprintf(GFP_KERNEL, "TRIGGER=%s", name);
+	}
 
 	/* Remove any existing trigger */
 	if (led_cdev->trigger) {
diff --git a/drivers/media/dvb-core/dvb-usb-ids.h b/drivers/media/dvb-core/dvb-usb-ids.h
index c117fb3..ab57020 100644
--- a/drivers/media/dvb-core/dvb-usb-ids.h
+++ b/drivers/media/dvb-core/dvb-usb-ids.h
@@ -388,4 +388,6 @@
 #define USB_PID_PCTV_2002E_SE                           0x025d
 #define USB_PID_SVEON_STV27                             0xd3af
 #define USB_PID_TURBOX_DTT_2000                         0xd3a4
+#define USB_PID_SC100_COLD                              0x8613
+#define USB_PID_SC100_WARM                              0x1003
 #endif
diff --git a/drivers/media/dvb-core/dvb_net.c b/drivers/media/dvb-core/dvb_net.c
index a694fb1..69f7796 100644
--- a/drivers/media/dvb-core/dvb_net.c
+++ b/drivers/media/dvb-core/dvb_net.c
@@ -344,7 +344,7 @@
 
 			/* Check TS error conditions: sync_byte, transport_error_indicator, scrambling_control . */
 			if ((ts[0] != TS_SYNC) || (ts[1] & TS_TEI) || ((ts[3] & TS_SC) != 0)) {
-				printk(KERN_WARNING "%lu: Invalid TS cell: SYNC %#x, TEI %u, SC %#x.\n",
+				printk_ratelimited(KERN_WARNING "%lu: Invalid TS cell: SYNC %#x, TEI %u, SC %#x.\n",
 				       priv->ts_count, ts[0],
 				       (ts[1] & TS_TEI) >> 7,
 				       (ts[3] & TS_SC) >> 6);
@@ -376,7 +376,7 @@
 				priv->tscc = ts[3] & 0x0F;
 				/* There is a pointer field here. */
 				if (ts[4] > ts_remain) {
-					printk(KERN_ERR "%lu: Invalid ULE packet "
+					printk_ratelimited(KERN_ERR "%lu: Invalid ULE packet "
 					       "(pointer field %d)\n", priv->ts_count, ts[4]);
 					ts += TS_SZ;
 					priv->ts_count++;
@@ -400,7 +400,7 @@
 				priv->tscc = (priv->tscc + 1) & 0x0F;
 			else {
 				/* TS discontinuity handling: */
-				printk(KERN_WARNING "%lu: TS discontinuity: got %#x, "
+				printk_ratelimited(KERN_WARNING "%lu: TS discontinuity: got %#x, "
 				       "expected %#x.\n", priv->ts_count, ts[3] & 0x0F, priv->tscc);
 				/* Drop partly decoded SNDU, reset state, resync on PUSI. */
 				if (priv->ule_skb) {
@@ -423,7 +423,7 @@
 				if (! priv->need_pusi) {
 					if (!(*from_where < (ts_remain-1)) || *from_where != priv->ule_sndu_remain) {
 						/* Pointer field is invalid.  Drop this TS cell and any started ULE SNDU. */
-						printk(KERN_WARNING "%lu: Invalid pointer "
+						printk_ratelimited(KERN_WARNING "%lu: Invalid pointer "
 						       "field: %u.\n", priv->ts_count, *from_where);
 
 						/* Drop partly decoded SNDU, reset state, resync on PUSI. */
@@ -454,7 +454,7 @@
 					 * current TS cell. */
 					dev->stats.rx_errors++;
 					dev->stats.rx_length_errors++;
-					printk(KERN_WARNING "%lu: Expected %d more SNDU bytes, but "
+					printk_ratelimited(KERN_WARNING "%lu: Expected %d more SNDU bytes, but "
 					       "got PUSI (pf %d, ts_remain %d).  Flushing incomplete payload.\n",
 					       priv->ts_count, priv->ule_sndu_remain, ts[4], ts_remain);
 					dev_kfree_skb(priv->ule_skb);
@@ -475,7 +475,7 @@
 			 * TS.
 			 * Check ts_remain has to be >= 2 here. */
 			if (ts_remain < 2) {
-				printk(KERN_WARNING "Invalid payload packing: only %d "
+				printk_ratelimited(KERN_WARNING "Invalid payload packing: only %d "
 				       "bytes left in TS.  Resyncing.\n", ts_remain);
 				priv->ule_sndu_len = 0;
 				priv->need_pusi = 1;
@@ -494,7 +494,7 @@
 					priv->ule_dbit = 0;
 
 				if (priv->ule_sndu_len < 5) {
-					printk(KERN_WARNING "%lu: Invalid ULE SNDU length %u. "
+					printk_ratelimited(KERN_WARNING "%lu: Invalid ULE SNDU length %u. "
 					       "Resyncing.\n", priv->ts_count, priv->ule_sndu_len);
 					dev->stats.rx_errors++;
 					dev->stats.rx_length_errors++;
@@ -550,7 +550,7 @@
 			 * prepare for the largest case: bridged SNDU with MAC address (dbit = 0). */
 			priv->ule_skb = dev_alloc_skb( priv->ule_sndu_len + ETH_HLEN + ETH_ALEN );
 			if (priv->ule_skb == NULL) {
-				printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n",
+				printk_ratelimited(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n",
 				       dev->name);
 				dev->stats.rx_dropped++;
 				return;
@@ -595,7 +595,7 @@
 				       *(tail - 2) << 8 |
 				       *(tail - 1);
 			if (ule_crc != expected_crc) {
-				printk(KERN_WARNING "%lu: CRC32 check FAILED: %08x / %08x, SNDU len %d type %#x, ts_remain %d, next 2: %x.\n",
+				printk_ratelimited(KERN_WARNING "%lu: CRC32 check FAILED: %08x / %08x, SNDU len %d type %#x, ts_remain %d, next 2: %x.\n",
 				       priv->ts_count, ule_crc, expected_crc, priv->ule_sndu_len, priv->ule_sndu_type, ts_remain, ts_remain > 2 ? *(unsigned short *)from_where : 0);
 
 #ifdef ULE_DEBUG
diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig
index 97c151d..4b15378 100644
--- a/drivers/media/dvb-frontends/Kconfig
+++ b/drivers/media/dvb-frontends/Kconfig
@@ -42,6 +42,13 @@
 	help
 	  Say Y when you want to support this frontend.
 
+config DVB_M88DS3103_M88RS6000_FIRMWARE
+	string "M88RS6000 firmware"
+	depends on DVB_M88DS3103
+	default "dvb-demod-m88rs6000.fw"
+	help
+	  Name of the M88RS6000 firmware file.
+
 comment "Multistandard (cable + terrestrial) frontends"
 	depends on DVB_CORE
 
diff --git a/drivers/media/dvb-frontends/m88ds3103.c b/drivers/media/dvb-frontends/m88ds3103.c
index d3d928e..070c941 100644
--- a/drivers/media/dvb-frontends/m88ds3103.c
+++ b/drivers/media/dvb-frontends/m88ds3103.c
@@ -16,6 +16,8 @@
 
 #include "m88ds3103_priv.h"
 
+#define TS_PACKET_SIZE		188
+
 static struct dvb_frontend_ops m88ds3103_ops;
 
 /* write multiple registers */
@@ -991,6 +993,9 @@
 	int ret;
 	unsigned int utmp;
 	u8 buf[3], u8tmp;
+	u32 err_count;
+	u64 u64tmp;
+	uint32_t bch_payload, ts_packets;
 
 	dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
 
@@ -1011,27 +1016,74 @@
 			if (ret)
 				goto err;
 
-			priv->ber = (buf[1] << 8) | (buf[0] << 0);
+			err_count = (buf[1] << 8) | (buf[0] << 0);
+			if (u8tmp & 0x08) {
+				/* counter represents byte errors */
+				err_count *= 8;
+			}
 
 			/* restart counters */
 			ret = m88ds3103_wr_reg(priv, 0xf8, u8tmp);
 			if (ret)
 				goto err;
+
+			u64tmp = 1000000000ULL * err_count;
+			/* counter / total number of bits */
+			do_div(u64tmp, 0x800000);
+
+			priv->ber = (u32)u64tmp;
 		}
 		break;
 	case SYS_DVBS2:
+		/* DVB-S2 uses the FER (Frame Error Rate) as approximate BER */
+
+		switch (c->fec_inner) {
+			/* THe FEC coding equation:
+			 * FEC length (64800b) * (LDPC ratio)
+			 * = K(LDPC)
+			 * = K(BCH) + T(BCH) * 16
+			 * = BB Header (80b) + data + padding + T(BCH) * 16
+			 * FEC Frame - LDPC - K(BCH) - K(LDPC) - T(BCH)
+			 * 64800     - 1/4  - 16008  - 16200   - 12
+			 * 64800     - 1/3  - 21408  - 21600   - 12
+			 * 64800     - 2/5  - 25728  - 25920   - 12
+			 * 64800     - 1/2  - 32208  - 32400   - 12
+			 * 64800     - 3/5  - 38688  - 38880   - 12
+			 * 64800     - 2/3  - 43040  - 43200   - 10
+			 * 64800     - 3/4  - 48408  - 48600   - 12
+			 * 64800     - 4/5  - 51648  - 51840   - 12
+			 * 64800     - 5/6  - 53840  - 54000   - 10
+			 * 64800     - 8/9  - 57472  - 57600   - 8
+			 * 64800     - 9/10 - 58192  - 58320   - 8
+			 */
+			case FEC_1_2:	bch_payload = 32128;	break;
+			case FEC_3_5:	bch_payload = 38608;	break;
+			case FEC_2_3:	bch_payload = 42960;	break;
+			case FEC_3_4:	bch_payload = 48328;	break;
+			case FEC_4_5:	bch_payload = 51568;	break;
+			case FEC_5_6:	bch_payload = 53760;	break;
+			case FEC_8_9:	bch_payload = 57392;	break;
+			case FEC_9_10:	bch_payload = 58112;	break;
+			/* FEC_1_4 is not supported in this kernel */
+			/* FEC_1_3 is not supported in this kernel */
+			/* FEC_2_5 is not supported in this kernel */
+			default:	return -EINVAL;
+		}
+
+		/* determine number of LDPC frames */
 		ret = m88ds3103_rd_regs(priv, 0xd5, buf, 3);
 		if (ret)
 			goto err;
-
 		utmp = (buf[2] << 16) | (buf[1] << 8) | (buf[0] << 0);
 
 		if (utmp > 3000) {
+			ts_packets = bch_payload * utmp / (TS_PACKET_SIZE * 8);
+
+			/* get UPL (User Package Length) CRC error counter */
 			ret = m88ds3103_rd_regs(priv, 0xf7, buf, 2);
 			if (ret)
 				goto err;
-
-			priv->ber = (buf[1] << 8) | (buf[0] << 0);
+			err_count = (buf[1] << 8) | (buf[0] << 0);
 
 			/* restart counters */
 			ret = m88ds3103_wr_reg(priv, 0xd1, 0x01);
@@ -1049,6 +1101,10 @@
 			ret = m88ds3103_wr_reg(priv, 0xd1, 0x00);
 			if (ret)
 				goto err;
+
+			u64tmp = 1000000000ULL * err_count;
+			do_div(u64tmp, ts_packets);
+			priv->ber = (u32)u64tmp;
 		}
 		break;
 	default:
@@ -1066,6 +1122,76 @@
 	return ret;
 }
 
+static int m88ds3103_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+	struct m88ds3103_priv *priv = fe->demodulator_priv;
+	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+	int ret;
+	u8 buf[3], u8tmp;
+
+	switch (c->delivery_system) {
+	case SYS_DVBS:
+		/* hold packet counter */
+		ret = m88ds3103_rd_reg(priv, 0xf8, &u8tmp);
+		if (ret)
+			goto err;
+		u8tmp |= 0x40;
+		ret = m88ds3103_wr_reg(priv, 0xf8, u8tmp);
+		if (ret)
+			goto err;
+
+		ret = m88ds3103_rd_regs(priv, 0xf4, buf, 2);
+		if (ret)
+			goto err;
+		*ucblocks = (buf[1] << 8) | (buf[0] << 0);
+
+		/* clear packet counter */
+		u8tmp &= ~0x20;
+		ret = m88ds3103_wr_reg(priv, 0xf8, u8tmp);
+		if (ret)
+			goto err;
+		u8tmp |= 0x20;
+
+		/* re-enable packet counter update */
+		u8tmp &= ~0x40;
+		ret = m88ds3103_wr_reg(priv, 0xf8, u8tmp);
+		if (ret)
+			goto err;
+
+		break;
+	case SYS_DVBS2:
+		ret = m88ds3103_rd_regs(priv, 0xd8, buf, 3);
+		if (ret)
+			goto err;
+		*ucblocks = (buf[2] << 16) | (buf[1] << 8) | (buf[0] << 0);
+
+		/* clear LDPC counter */
+		ret = m88ds3103_rd_reg(priv, 0xd1, &u8tmp);
+		if (ret)
+			goto err;
+
+		u8tmp |= 0x01;
+		ret = m88ds3103_wr_reg(priv, 0xd1, u8tmp);
+		if (ret)
+			goto err;
+
+		u8tmp &= ~0x01;
+		ret = m88ds3103_wr_reg(priv, 0xd1, u8tmp);
+		if (ret)
+			goto err;
+
+		break;
+	default:
+		break;
+	}
+
+	return 0;
+
+err:
+	dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+	return ret;
+}
+
 static int m88ds3103_set_tone(struct dvb_frontend *fe,
 	fe_sec_tone_mode_t fe_sec_tone_mode)
 {
@@ -1320,19 +1446,24 @@
 	kfree(priv);
 }
 
-static int m88ds3103_select(struct i2c_adapter *adap, void *mux_priv, u32 chan)
+static int m88ds3103_select(struct i2c_adapter *adap, void *mux_priv, u32 chan,
+			    int num)
 {
 	struct m88ds3103_priv *priv = mux_priv;
 	int ret;
+	u8 buf[2] = {0x03, 0x10};
 	struct i2c_msg gate_open_msg[1] = {
 		{
 			.addr = priv->cfg->i2c_addr,
 			.flags = 0,
 			.len = 2,
-			.buf = "\x03\x11",
+			.buf = buf,
 		}
 	};
 
+	BUG_ON(num+0x10 > U8_MAX);
+	buf[1] += (u8)num;
+
 	mutex_lock(&priv->i2c_mutex);
 
 	/* open tuner I2C repeater for 1 xfer, closes automatically */
@@ -1432,7 +1563,7 @@
 		goto err;
 
 	/* create mux i2c adapter for tuner */
-	priv->i2c_adapter = i2c_add_mux_adapter(i2c, &i2c->dev, priv, 0, 0, 0,
+	priv->i2c_adapter = i2c_add_mux_adapter_num(i2c, &i2c->dev, priv, 0, 0, 0,
 			m88ds3103_select, m88ds3103_deselect);
 	if (priv->i2c_adapter == NULL)
 		goto err;
@@ -1491,6 +1622,7 @@
 	.read_status = m88ds3103_read_status,
 	.read_snr = m88ds3103_read_snr,
 	.read_ber = m88ds3103_read_ber,
+	.read_ucblocks = m88ds3103_read_ucblocks,
 
 	.diseqc_send_master_cmd = m88ds3103_diseqc_send_master_cmd,
 	.diseqc_send_burst = m88ds3103_diseqc_send_burst,
diff --git a/drivers/media/dvb-frontends/m88ds3103_priv.h b/drivers/media/dvb-frontends/m88ds3103_priv.h
index a2c0958..c9a8f56 100644
--- a/drivers/media/dvb-frontends/m88ds3103_priv.h
+++ b/drivers/media/dvb-frontends/m88ds3103_priv.h
@@ -25,7 +25,7 @@
 #include <linux/math64.h>
 
 #define M88DS3103_FIRMWARE "dvb-demod-m88ds3103.fw"
-#define M88RS6000_FIRMWARE "dvb-demod-m88rs6000.fw"
+#define M88RS6000_FIRMWARE CONFIG_DVB_M88DS3103_M88RS6000_FIRMWARE
 #define M88DS3103_MCLK_KHZ 96000
 #define M88RS6000_CHIP_ID 0x74
 #define M88DS3103_CHIP_ID 0x70
diff --git a/drivers/media/tuners/m88rs6000t.c b/drivers/media/tuners/m88rs6000t.c
index d4c13fe..5009324 100644
--- a/drivers/media/tuners/m88rs6000t.c
+++ b/drivers/media/tuners/m88rs6000t.c
@@ -494,9 +494,29 @@
 static int m88rs6000t_get_rf_strength(struct dvb_frontend *fe, u16 *strength)
 {
 	struct m88rs6000t_dev *dev = fe->tuner_priv;
+	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+	static u32 bb_list_dBm_negated[16][16] =
+		{
+			{5000, 4999, 4397, 4044, 3795, 3601, 3442, 3309, 3193, 3090, 2999, 2916, 2840, 2771, 2706, 2647},
+			{2590, 2538, 2488, 2441, 2397, 2354, 2314, 2275, 2238, 2203, 2169, 2136, 2104, 2074, 2044, 2016},
+			{1988, 1962, 1936, 1911, 1886, 1862, 1839, 1817, 1795, 1773, 1752, 1732, 1712, 1692, 1673, 1655},
+			{1636, 1618, 1601, 1584, 1567, 1550, 1534, 1518, 1502, 1487, 1472, 1457, 1442, 1428, 1414, 1400},
+			{1386, 1373, 1360, 1347, 1334, 1321, 1309, 1296, 1284, 1272, 1260, 1249, 1237, 1226, 1215, 1203},
+			{1193, 1182, 1171, 1161, 1150, 1140, 1130, 1120, 1110, 1100, 1090, 1081, 1071, 1062, 1052, 1043},
+			{1034, 1025, 1016, 1007,  999,  990,  982,  973,  965,  956,  948,  940,  932,  924,  916,  908},
+			{ 900,  893,  885,  877,  870,  862,  855,  848,  840,  833,  826,  819,  812,  805,  798,  791},
+			{ 784,  778,  771,  764,  758,  751,  745,  738,  732,  725,  719,  713,  706,  700,  694,  688},
+			{ 682,  676,  670,  664,  658,  652,  647,  641,  635,  629,  624,  618,  612,  607,  601,  596},
+			{ 590,  585,  580,  574,  569,  564,  558,  553,  548,  543,  538,  533,  528,  523,  518,  513},
+			{ 508,  503,  498,  493,  488,  483,  479,  474,  469,  464,  460,  455,  450,  446,  441,  437},
+			{ 432,  428,  423,  419,  414,  410,  405,  401,  397,  392,  388,  384,  379,  375,  371,  367},
+			{ 363,  358,  354,  350,  346,  342,  338,  334,  330,  326,  322,  318,  314,  310,  306,  302},
+			{ 298,  294,  290,  287,  283,  279,  275,  271,  268,  264,  260,  257,  253,  249,  246,  242},
+			{ 238,  235,  231,  227,  224,  220,  217,  213,  210,  206,  203,  199,  196,  192,  189,  186}
+		};
+
 	unsigned int val, i;
 	int ret;
-	u16 gain;
 	u32 PGA2_cri_GS = 46, PGA2_crf_GS = 290, TIA_GS = 290;
 	u32 RF_GC = 1200, IF_GC = 1100, BB_GC = 300;
 	u32 PGA2_GC = 300, TIA_GC = 300, PGA2_cri = 0, PGA2_crf = 0;
@@ -507,16 +527,25 @@
 			295, 285, 290, 295, 295, 310};
 	u32 BBGS[14] = {0, 286, 275, 290, 294, 300, 290,
 			290, 285, 283, 260, 295, 290, 260};
+	u32 bb_power, total_gain, delta, freq_mhz;
 
 	ret = regmap_read(dev->regmap, 0x5A, &val);
 	if (ret)
 		goto err;
 	RF_GC = val & 0x0f;
+	if (RF_GC >= ARRAY_SIZE(RFGS)) {
+		dev_err(&dev->client->dev, "Invalid, RFGC=%d\n", RF_GC);
+		return -EINVAL;
+	}
 
 	ret = regmap_read(dev->regmap, 0x5F, &val);
 	if (ret)
 		goto err;
 	IF_GC = val & 0x0f;
+	if (IF_GC >= ARRAY_SIZE(IFGS)) {
+		dev_err(&dev->client->dev, "Invalid, IFGC=%d\n", IF_GC);
+		return -EINVAL;
+	}
 
 	ret = regmap_read(dev->regmap, 0x3F, &val);
 	if (ret)
@@ -527,6 +556,10 @@
 	if (ret)
 		goto err;
 	BB_GC = (val >> 4) & 0x0f;
+	if (BB_GC >= ARRAY_SIZE(BBGS)) {
+		dev_err(&dev->client->dev, "Invalid, BBGC=%d\n", __func__, BB_GC);
+		return -EINVAL;
+	}
 
 	ret = regmap_read(dev->regmap, 0x76, &val);
 	if (ret)
@@ -557,11 +590,25 @@
 
 	PGA2G = PGA2_cri * PGA2_cri_GS + PGA2_crf * PGA2_crf_GS;
 
-	gain = RFG + IFG - TIAG + BBG + PGA2G;
+	total_gain = RFG + IFG - TIAG + BBG + PGA2G;
 
-	/* scale value to 0x0000-0xffff */
-	gain = clamp_val(gain, 1000U, 10500U);
-	*strength = (10500 - gain) * 0xffff / (10500 - 1000);
+	ret = regmap_read(dev->regmap, 0x96, &val);
+	if (ret)
+		goto err;
+	bb_power = bb_list_dBm_negated[(val >> 4) & 0x0f][val & 0x0f];
+
+	freq_mhz = (c->frequency + 500) / 1000;
+	if (freq_mhz > 1750) {
+		delta = 14;
+	} else if (freq_mhz > 1350) {
+		delta = 12;
+	} else {
+		delta = 13;
+	}
+
+	val = (total_gain + bb_power) / 100;
+	*strength = val < delta ? 0 : val - delta;
+
 err:
 	if (ret)
 		dev_dbg(&dev->client->dev, "failed=%d\n", ret);
diff --git a/drivers/media/usb/dvb-usb-v2/Kconfig b/drivers/media/usb/dvb-usb-v2/Kconfig
index 9facc92..ed37337 100644
--- a/drivers/media/usb/dvb-usb-v2/Kconfig
+++ b/drivers/media/usb/dvb-usb-v2/Kconfig
@@ -151,3 +151,12 @@
 	select DVB_SP2 if MEDIA_SUBDRV_AUTOSELECT
 	help
 	  Say Y here to support the USB receivers from DVBSky.
+
+config DVB_USB_SC100
+	tristate "Google SC100 DVB-S USB2.0 support"
+	depends on DVB_USB_V2
+	select DVB_M88DS3103
+	select MEDIA_TUNER_M88RS6000T
+	select CYPRESS_FIRMWARE
+	help
+	  Say Y here to support the Google SC-100 DVB-S USB2.0 receiver.
diff --git a/drivers/media/usb/dvb-usb-v2/Makefile b/drivers/media/usb/dvb-usb-v2/Makefile
index f10d4df..98a8fa3 100644
--- a/drivers/media/usb/dvb-usb-v2/Makefile
+++ b/drivers/media/usb/dvb-usb-v2/Makefile
@@ -40,6 +40,9 @@
 dvb-usb-dvbsky-objs := dvbsky.o
 obj-$(CONFIG_DVB_USB_DVBSKY) += dvb-usb-dvbsky.o
 
+dvb-usb-sc100-objs := sc100.o
+obj-$(CONFIG_DVB_USB_SC100) += dvb-usb-sc100.o
+
 ccflags-y += -I$(srctree)/drivers/media/dvb-core
 ccflags-y += -I$(srctree)/drivers/media/dvb-frontends
 ccflags-y += -I$(srctree)/drivers/media/tuners
diff --git a/drivers/media/usb/dvb-usb-v2/sc100.c b/drivers/media/usb/dvb-usb-v2/sc100.c
new file mode 100644
index 0000000..38884e1
--- /dev/null
+++ b/drivers/media/usb/dvb-usb-v2/sc100.c
@@ -0,0 +1,240 @@
+/*
+ * Driver for SC100 DVB-S USB2.0 receiver
+ *
+ * The hardware design is based on a Montage M88RS6000 tuner/demodulator,
+ * a Cypress CY7C68013A USB bridge and a TI TPS65233 voltage regulator.
+ * The M88RS6000 chip integrates a M88RS6000 tuner and a M88DS3103
+ * demodulator. The demodulator is directly connected to the I2C bus.
+ * The tuner is connected internally to the M88DS3103 and can be
+ * accessed through an I2C mux. The CY7C68013A USB bridge is connected
+ * to the MPEG-TS interface of the M88RS6000 and makes the data on the
+ * MPEG-TS available to the host through an USB bulk endpoint. The
+ * TPS65233 has an I2C interface, however in the sc100 design it is
+ * controlled through pins driven by the M88RS6000 chip.
+ *
+ * Copyright (C) 2015 Google 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "dvb_usb.h"
+#include "m88ds3103.h"
+#include "m88rs6000t.h"
+#include "cypress_firmware.h"
+
+static char *mac_addr_str = "90:09:1e:f1:be:33";
+module_param_named(mac_addr, mac_addr_str, charp, 0000);
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+struct sc100_state {
+	struct mutex stream_mutex;
+	struct i2c_client *i2c_client_tuner;
+};
+
+static int sc100_read_mac_addr(struct dvb_usb_adapter *adap, u8 mac[6])
+{
+	if (!mac_pton(mac_addr_str, mac)) {
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int sc100_download_firmware(struct dvb_usb_device *d,
+	const struct firmware *fw)
+{
+	pr_debug("Loading dvb-usb-sc100 firmware\n");
+
+	return cypress_load_firmware(d->udev, fw, CYPRESS_FX2);
+}
+
+static int sc100_identify_state(struct dvb_usb_device *d, const char **name)
+{
+	if (d->udev->descriptor.idProduct == USB_PID_SC100_WARM)
+		return WARM;
+	else
+		return COLD;
+}
+
+static int sc100_stream_ctrl(struct dvb_usb_device *d, u8 onoff)
+{
+	struct sc100_state *state = d_to_priv(d);
+	int ret = 0;
+
+	mutex_lock(&state->stream_mutex);
+
+	if (onoff) {
+		/* Configure the TS parameters */
+		/* TODO: details? */
+		ret = usb_control_msg(d->udev, usb_sndctrlpipe(d->udev, 0),
+				0xa4, USB_TYPE_VENDOR, 0, 0, 0, 0, 5000);
+	}
+
+	mutex_unlock(&state->stream_mutex);
+
+	return ret;
+}
+
+static int sc100_streaming_ctrl(struct dvb_frontend *fe, int onoff)
+{
+	struct dvb_usb_device *d = fe_to_d(fe);
+
+	return sc100_stream_ctrl(d, (onoff == 0) ? 0 : 1);
+}
+
+static const struct m88ds3103_config sc100_m88rs6000_cfg = {
+	.i2c_addr = 0x69,
+	.clock = 27000000,
+	.i2c_wr_max = 33,
+	.ts_mode = M88DS3103_TS_CI,
+	.ts_clk = 16000,
+	.ts_clk_pol = 1,
+	.agc = 0x99,
+	.lnb_hv_pol = 0,
+	.lnb_en_pol = 1,
+};
+
+static int sc100_attach(struct dvb_usb_adapter *adap)
+{
+	struct sc100_state *state = adap_to_priv(adap);
+	struct dvb_usb_device *d = adap_to_d(adap);
+	int ret = 0;
+	struct i2c_adapter *sc100_i2c_adap = i2c_get_adapter(0);
+	/* demod I2C adapter */
+	struct i2c_adapter *i2c_adapter;
+	struct i2c_client *client_tuner;
+	struct i2c_board_info info;
+	struct m88rs6000t_config m88rs6000t_config;
+	memset(&info, 0, sizeof(struct i2c_board_info));
+
+	/* attach demod */
+	adap->fe[0] = dvb_attach(m88ds3103_attach,
+				&sc100_m88rs6000_cfg,
+				sc100_i2c_adap,
+				&i2c_adapter);
+	if (!adap->fe[0]) {
+		dev_err(&d->udev->dev, "sc100_attach fail.\n");
+		ret = -ENODEV;
+		goto fail_attach;
+	}
+
+	/* attach tuner */
+	m88rs6000t_config.fe = adap->fe[0];
+	strlcpy(info.type, "m88rs6000t", I2C_NAME_SIZE);
+	info.addr = 0x21;
+	info.platform_data = &m88rs6000t_config;
+	request_module(info.type);
+	client_tuner = i2c_new_device(i2c_adapter, &info);
+	if (client_tuner == NULL || client_tuner->dev.driver == NULL) {
+		dvb_frontend_detach(adap->fe[0]);
+		ret = -ENODEV;
+		goto fail_attach;
+	}
+	state->i2c_client_tuner = client_tuner;
+
+	if (!try_module_get(client_tuner->dev.driver->owner)) {
+		i2c_unregister_device(client_tuner);
+		dvb_frontend_detach(adap->fe[0]);
+		ret = -ENODEV;
+		goto fail_attach;
+	}
+
+	/* delegate signal strength measurement to tuner */
+	adap->fe[0]->ops.read_signal_strength =
+		adap->fe[0]->ops.tuner_ops.get_rf_strength;
+
+	return ret;
+
+fail_attach:
+	return ret;
+}
+
+static int sc100_init(struct dvb_usb_device *d)
+{
+	struct sc100_state *state = d_to_priv(d);
+
+	mutex_init(&state->stream_mutex);
+
+	return 0;
+}
+
+static void sc100_exit(struct dvb_usb_device *d)
+{
+	struct sc100_state *state = d_to_priv(d);
+	struct i2c_client *client;
+
+	client = state->i2c_client_tuner;
+	/* remove I2C tuner */
+	if (client) {
+		module_put(client->dev.driver->owner);
+		i2c_unregister_device(client);
+	}
+}
+
+static struct dvb_usb_device_properties sc100_props = {
+	.firmware = "dvb-usb-sc100.fw",
+
+	.driver_name = KBUILD_MODNAME,
+	.owner = THIS_MODULE,
+	.adapter_nr = adapter_nr,
+	.size_of_priv = sizeof(struct sc100_state),
+
+	.frontend_attach	= sc100_attach,
+	.init			= sc100_init,
+	.streaming_ctrl		= sc100_streaming_ctrl,
+	.identify_state		= sc100_identify_state,
+	.exit			= sc100_exit,
+	.read_mac_address	= sc100_read_mac_addr,
+	.download_firmware	= sc100_download_firmware,
+
+	.num_adapters = 1,
+	.adapter = {
+		{
+			.stream = DVB_USB_STREAM_BULK(
+				0x82, MAX_NO_URBS_FOR_DATA_STREAM, 16384),
+		}
+	}
+};
+
+static const struct usb_device_id sc100_id_table[] = {
+	{ DVB_USB_DEVICE(USB_VID_CYPRESS,
+		USB_PID_SC100_COLD,
+		&sc100_props, "Google DVB-S SC100 USB2.0",
+		NULL) },
+	{ DVB_USB_DEVICE(USB_VID_CYPRESS,
+		USB_PID_SC100_WARM,
+		&sc100_props, "Google DVB-S SC100 USB2.0",
+		NULL) },
+	{ }
+};
+MODULE_DEVICE_TABLE(usb, sc100_id_table);
+
+static struct usb_driver sc100_usb_driver = {
+	.name = KBUILD_MODNAME,
+	.id_table = sc100_id_table,
+	.probe = dvb_usbv2_probe,
+	.disconnect = dvb_usbv2_disconnect,
+	.suspend = dvb_usbv2_suspend,
+	.resume = dvb_usbv2_resume,
+	.reset_resume = dvb_usbv2_reset_resume,
+	.no_dynamic_id = 1,
+	.soft_unbind = 1,
+};
+
+module_usb_driver(sc100_usb_driver);
+
+MODULE_AUTHOR("Matthias Kaehlcke <mka@google.com>");
+MODULE_DESCRIPTION("Driver for sc100 USB satellite receiver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index d5ad04d..8d67d7f 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -849,6 +849,16 @@
 	  programmable memory) support. This exposes a sysfs file to read
 	  out OTP values.
 
+config LS1024A_OTP
+	tristate "Freescale LS1024A On-Chip OTP Memory Support"
+	depends on ARCH_COMCERTO
+	default y
+	help
+		If you say Y here, you will get support for a proc file,
+		/proc/otp/islocked, that reports whether the LS1024A
+		One-Time Programmable (and thus the device) is locked or
+		not.
+
 config AB8500_CORE
 	bool "ST-Ericsson AB8500 Mixed Signal Power Management chip"
 	depends on ABX500_CORE && MFD_DB8500_PRCMU
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 0e5cfeb..a3abfe1 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -131,6 +131,7 @@
 obj-$(CONFIG_ABX500_CORE)	+= abx500-core.o
 obj-$(CONFIG_AB3100_CORE)	+= ab3100-core.o
 obj-$(CONFIG_AB3100_OTP)	+= ab3100-otp.o
+obj-$(CONFIG_LS1024A_OTP)	+= ls1024a-otp.o
 obj-$(CONFIG_AB8500_DEBUG)	+= ab8500-debugfs.o
 obj-$(CONFIG_AB8500_GPADC)	+= ab8500-gpadc.o
 obj-$(CONFIG_MFD_DB8500_PRCMU)	+= db8500-prcmu.o
diff --git a/drivers/mfd/ls1024a-otp.c b/drivers/mfd/ls1024a-otp.c
new file mode 100644
index 0000000..c39f4d0a
--- /dev/null
+++ b/drivers/mfd/ls1024a-otp.c
@@ -0,0 +1,229 @@
+/*
+ * Freescale LS1024A OTP access
+ *
+ * (C) Copyright 2014 Google 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/module.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+
+#define DRIVER_NAME "ls1024a-otp"
+
+#define OTP_CONFIG_LOCK_0	0x00
+#define OTP_CONFIG_LOCK_1	0x04
+#define OTP_CEB_INPUT		0x0C
+#define OTP_RSTB_INPUT		0x10
+#define OTP_ADDR_INPUT		0x14
+#define OTP_READEN_INPUT	0x18
+#define OTP_DATA_OUT_COUNTER	0x4C
+#define OTP_DATA_OUTPUT		0x50
+
+struct ls1024a_otp {
+	struct device *dev;
+	void __iomem *reg;
+	struct clk *clk;
+	spinlock_t lock;
+};
+
+/* Taken from repo://barebox/mindspeed/drivers/otp/c2k_otp.c */
+static void otp_read(struct ls1024a_otp *otp, u32 offset, u8 *read_data, int size)
+{
+	int i;
+	unsigned long flags;
+	u32 read_tmp = 0;
+	u32 dataout_counter;
+	u32 axi_clk;
+
+	if (NULL == read_data)
+		return;
+
+	if (size <= 0)
+		return;
+
+	axi_clk = clk_get_rate(otp->clk);
+	/* 70 nSec */
+	dataout_counter = ((axi_clk * 7 + 99999999) / 100000000) & 0x1FF;
+
+	spin_lock_irqsave(&otp->lock, flags);
+
+	/* configure the OTP_DATA_OUT_COUNTER for read operation.
+			70 nsec is needed except for blank check test, in which 1.5 usec is needed.*/
+	writel(dataout_counter, otp->reg + OTP_DATA_OUT_COUNTER);
+
+	/* Unlock the write protection. */
+	writel(0xEBCF0000, otp->reg + OTP_CONFIG_LOCK_0); /* config lock0 */
+	writel(0xEBCF1111, otp->reg + OTP_CONFIG_LOCK_1); /* config lock1 */
+	writel(0x0, otp->reg + OTP_CEB_INPUT);
+
+	udelay(1);
+
+	/* rstb drive 0 */
+	writel(0x0, otp->reg + OTP_RSTB_INPUT);
+	/* Wait for at least 20nsec */
+	ndelay(20);
+	/* rstb drive 1 to have pulse */
+	writel(0x1, otp->reg + OTP_RSTB_INPUT);
+	/* Wait for at least 1usec */
+	udelay(1);
+
+	/* Write the desired address to the ADDR register */
+	writel(offset, otp->reg + OTP_ADDR_INPUT);
+	/* read_enable drive */
+	writel(0x1, otp->reg + OTP_READEN_INPUT);
+	/* Wait for at least 70nsec/1.5usec depends on operation type */
+	ndelay(70);
+
+	/* Read First Byte */
+	read_tmp = readl(otp->reg + OTP_DATA_OUTPUT);
+	*read_data = read_tmp & 0xFF;
+
+	/* For consecutive read */
+	for(i = 1 ; i < size ; i++)
+	{
+		offset = offset + 8;
+
+		/* start reading from data out register */
+		writel(offset, otp->reg + OTP_ADDR_INPUT);
+		/* Wait for at least 70nsec/1.5usec depends on operation type */
+		ndelay(70);
+
+		read_tmp = readl(otp->reg + OTP_DATA_OUTPUT);
+		*(read_data + i) = read_tmp & 0xFF;
+	}
+
+	/* reading is done make the read_enable low */
+	writel(0x0, otp->reg + OTP_READEN_INPUT);
+
+	/* lock CEB register, return to standby mode */
+	writel(0x1, otp->reg + OTP_CEB_INPUT);
+
+	spin_unlock_irqrestore(&otp->lock, flags);
+}
+
+static int islocked_proc_show(struct seq_file *m, void *v)
+{
+	struct ls1024a_otp *otp = (struct ls1024a_otp *) m->private;
+	char status_buf[1];
+
+	otp_read(otp, 8, status_buf, 1);
+
+	seq_printf(m, "%d\n", (*status_buf & 0x2) == 0x2);
+	return 0;
+}
+
+static int islocked_proc_open(struct inode *inode, struct file *filp)
+{
+	struct ls1024a_otp *otp = PDE_DATA(inode);
+	return single_open(filp, islocked_proc_show, otp);
+}
+
+static struct file_operations islocked_proc_fops = {
+	.owner = THIS_MODULE,
+	.open = islocked_proc_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+};
+
+static int ls1024a_otp_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct resource *res;
+	struct ls1024a_otp *otp;
+	int rc;
+	struct proc_dir_entry *otp_dir;
+	struct proc_dir_entry *islocked_proc_file;
+
+	otp = devm_kzalloc(dev, sizeof(*otp), GFP_KERNEL);
+	if (!otp)
+		return -ENOMEM;
+
+	otp->dev = &pdev->dev;
+
+	platform_set_drvdata(pdev, otp);
+
+	spin_lock_init(&otp->lock);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	otp->reg = devm_ioremap_resource(dev, res);
+	if (IS_ERR(otp->reg)) {
+		rc = PTR_ERR(otp->reg);
+		goto err1;
+	}
+
+	otp->clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(otp->clk)) {
+		rc = PTR_ERR(otp->clk);
+		goto err2;
+	}
+	rc = clk_prepare_enable(otp->clk);
+	if (rc)
+		goto err2;
+
+	otp_dir = proc_mkdir("otp", NULL);
+	if (otp_dir == NULL) {
+		dev_err(otp->dev, "Unable to create /proc/otp directory\n");
+		rc = -ENOMEM;
+		goto err3;
+	}
+
+	islocked_proc_file = proc_create_data("islocked", 0, otp_dir, &islocked_proc_fops, otp);
+	if (islocked_proc_file == NULL) {
+		dev_err(otp->dev, "Unable to create /proc/otp/islocked\n");
+		rc = -ENOMEM;
+		goto err4;
+	}
+	return 0;
+
+err4:
+	remove_proc_entry("otp", NULL);
+err3:
+	clk_disable_unprepare(otp->clk);
+err2:
+	devm_iounmap(dev, otp->reg);
+err1:
+	platform_set_drvdata(pdev, NULL);
+	devm_kfree(dev, otp);
+	return rc;
+}
+
+static const struct of_device_id ls1024a_otp_of_match[] = {
+		{
+			.compatible	= "fsl,ls1024a-otp",
+		},
+		{},
+};
+MODULE_DEVICE_TABLE(of, ls1024a_otp_of_match);
+static struct platform_driver ls1024a_otp_driver = {
+	.probe		= ls1024a_otp_probe,
+	.driver		= {
+		.name	= DRIVER_NAME,
+		.of_match_table = ls1024a_otp_of_match,
+	},
+};
+module_platform_driver(ls1024a_otp_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Stephen McGruer <smcgruer@google.com>");
+MODULE_DESCRIPTION("LS1024A OTP driver");
diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c
index c50d8cf..7aa8442 100644
--- a/drivers/mtd/chips/cfi_cmdset_0002.c
+++ b/drivers/mtd/chips/cfi_cmdset_0002.c
@@ -809,7 +809,9 @@
 		return 0;
 
 	case FL_ERASING:
-		if (!cfip || !(cfip->EraseSuspend & (0x1|0x2)) ||
+		/* Apply OpenWRT patch from */
+		/* https://dev.openwrt.org/browser/trunk/target/linux/generic/patches-3.19/460-mtd-cfi_cmdset_0002-no-erase_suspend.patch */
+		if (1 /* no suspend */ || !cfip || !(cfip->EraseSuspend & (0x1|0x2)) ||
 		    !(mode == FL_READY || mode == FL_POINT ||
 		    (mode == FL_WRITING && (cfip->EraseSuspend & 0x2))))
 			goto sleep;
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 5897d8d..5b1c07e 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -448,6 +448,25 @@
 	  Enables support for NAND Flash chips wired onto Freescale PowerPC
 	  processor localbus with User-Programmable Machine support.
 
+config MTD_NAND_LS1024A
+	tristate "Support for NAND on Freescale LS1024A"
+	depends on ARCH_COMCERTO
+	help
+	  Enables support for NAND flash controller on the Freescale LS1024A
+          SoC.
+
+config NAND_LS1024A_ECC_8_HW_BCH
+	bool
+	depends on MTD_NAND_LS1024A
+	default n
+	prompt "LS1024A ECC-8 syndrome calculation using BCH"
+
+config NAND_LS1024A_ECC_24_HW_BCH
+	bool
+	depends on MTD_NAND_LS1024A
+	default y
+	prompt "LS1024A ECC-24 syndrome calculation using BCH"
+
 config MTD_NAND_MPC5121_NFC
 	tristate "MPC5121 built-in NAND Flash Controller support"
 	depends on PPC_MPC512x
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 582bbd05..dc5f41b 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -37,6 +37,7 @@
 obj-$(CONFIG_MTD_NAND_FSL_ELBC)		+= fsl_elbc_nand.o
 obj-$(CONFIG_MTD_NAND_FSL_IFC)		+= fsl_ifc_nand.o
 obj-$(CONFIG_MTD_NAND_FSL_UPM)		+= fsl_upm.o
+obj-$(CONFIG_MTD_NAND_LS1024A)		+= ls1024a_nand.o
 obj-$(CONFIG_MTD_NAND_SLC_LPC32XX)      += lpc32xx_slc.o
 obj-$(CONFIG_MTD_NAND_MLC_LPC32XX)      += lpc32xx_mlc.o
 obj-$(CONFIG_MTD_NAND_SH_FLCTL)		+= sh_flctl.o
diff --git a/drivers/mtd/nand/ls1024a_nand.c b/drivers/mtd/nand/ls1024a_nand.c
new file mode 100644
index 0000000..1b9e9a6
--- /dev/null
+++ b/drivers/mtd/nand/ls1024a_nand.c
@@ -0,0 +1,1113 @@
+/*
+ *  linux/drivers/mtd/nand/ls1024a_nand.c
+ *
+ *  Copyright (C) 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 version 2 as
+ * published by the Free Software Foundation.
+ *
+ *  Overview:
+ *   This is a device driver for the NAND flash device found on the
+ *   Comcerto board which utilizes the Toshiba TC58V64AFT part. This is
+ *   a 128Mibit (8MiB x 8 bits) NAND flash device.
+ */
+
+#include <linux/device.h>
+#include <linux/gpio.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/bch.h>
+#include <linux/bitrev.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/ratelimit.h>
+#include <linux/platform_device.h>
+#if 0
+#include <mach/ecc.h>
+#include <linux/mtd/exp_lock.h>
+#endif
+
+/* ECC Register Set */
+/********************/
+/* ECC Control Registers */
+#define ECC_SHIFT_EN_CFG	(0x0)
+#define ECC_GEN_CFG		(0x4)
+#define ECC_TAG_CFG		(0x8)
+#define ECC_INIT_CFG		(0xC)
+#define ECC_PRTY_OUT_SEL_CFG	(0x10)
+#define ECC_POLY_START_CFG	(0x14)
+#define ECC_CS_SEL_CFG		(0x18)
+/* ECC Status Registers */
+#define ECC_IDLE_STAT		(0x1C)
+#define ECC_POLY_STAT		(0x20)
+#define ECC_CORR_STAT		(0x24)
+#define ECC_CORR_DONE_STAT	(0x28)
+#define ECC_CORR_DATA_STAT	(0x2C)
+
+/* ECC general configuration register parameters */
+#define HAMM_MODE	BIT(28)
+#define BCH_MODE	(~ HAMM_MODE)
+
+#define PRTY_MODE_MASK	BIT_MASK(24)
+#define PRTY_CALC	PRTY_MODE_MASK
+#define SYNDROME_CALC	(~ PRTY_CALC)
+
+#define	ECC_LVL_MASK	0x3F0000
+#define ECC_LVL_SHIFT	16
+
+#define BLK_SIZE_MASK 0x7FF
+
+#define ECC_LVL_2 0x2
+#define ECC_LVL_4 0x4
+#define ECC_LVL_6 0x6
+#define ECC_LVL_8 0x8
+#define ECC_LVL_10 0xA
+#define ECC_LVL_12 0xC
+#define ECC_LVL_14 0xE
+#define ECC_LVL_16 0x10
+#define ECC_LVL_18 0x12
+#define ECC_LVL_20 0x14
+#define ECC_LVL_22 0x16
+#define ECC_LVL_24 0x18
+#define ECC_LVL_26 0x1A
+#define ECC_LVL_28 0x1C
+#define ECC_LVL_30 0x1E
+#define ECC_LVL_32 0x20
+
+#if defined (CONFIG_NAND_LS1024A_ECC_24_HW_BCH)
+	#define ECC_LVL_VAL ECC_LVL_24 /* ECC Level 24 is used */
+#elif defined (CONFIG_NAND_LS1024A_ECC_8_HW_BCH)
+	#define ECC_LVL_VAL ECC_LVL_8 /* ECC Level 8 is used */
+#endif
+
+/* Block size used in Bytes*/
+#define ECC_BLOCK_SIZE_512 512
+#define ECC_BLOCK_SIZE_1024 1024
+
+/* Maximum value of ECC Block size is 2k-(1+14*ECC_LVL/8) Bytes */
+#define ECC_BLOCK_SIZE		ECC_BLOCK_SIZE_1024
+#define ECC_BLOCK_SIZE_SHIFT	ECC_BLOCK_SIZE_1024_SHIFT
+
+#define ECC_CS4_SEL 0x10
+#define ECC_CS3_SEL 0x08
+#define ECC_CS2_SEL 0x04
+#define ECC_CS1_SEL 0x02
+#define ECC_CS0_SEL 0x01
+
+#define ECC_INIT		0x1
+#define ECC_SHIFT_ENABLE	0x1
+#define ECC_SHIFT_DISABLE	0x0
+#define ECC_PARITY_OUT_EN	0x1
+#define ECC_PARITY_OUT_DISABLE	0x0
+
+/* Polynomial Start Configuration (ECC_POLY_START_CFG) */
+#define ECC_POLY_START		(1 << 0)
+
+/* Idle Status (ECC_IDLE_STAT) */
+#define ECC_IDLE		(1 << 0)
+
+/* Polynomial Status (ECC_POLY_STAT) */
+#define ECC_CORR_REQ		(1 << 0)
+#define ECC_ERASED_PAGE		(1 << 1)
+#define ECC_UNCORR_ERR_HAMM	(1 << 2)
+
+/* Correction Status (ECC_CORR_STAT) */
+#define ECC_TAG_MASK		0xFFFF
+#define ECC_NUM_ERR_MASK	0x3F
+#define ECC_NUM_ERR_SHIFT	16
+#define ECC_UNCORR		(1 << 24)
+
+/* Correction Done Status (ECC_CORR_DONE_STAT) */
+#define ECC_DONE		(1 << 0)
+
+/* Correction Data Status (ECC_CORR_DATA_STAT), BCH Mode */
+#define ECC_BCH_MASK		0xFFFF
+#define ECC_BCH_INDEX_MASK	0x7FF
+#define ECC_BCH_INDEX_SHIFT	16
+#define ECC_BCH_VALID		(1 << 31)
+
+/* Correction Data Status (ECC_CORR_DATA_STAT), Hamming Mode */
+#define ECC_HAMM_MASK		0xF
+#define ECC_HAMM_INDEX_MASK	0x1FF
+#define ECC_HAMM_INDEX_SHIFT	16
+#define ECC_HAMM_VALID		(1 << 31)
+
+struct comcerto_bch_control {
+	struct bch_control   *bch;
+	unsigned int         *errloc;
+};
+
+/*
+ * MTD structure for Comcerto board
+ */
+struct comcerto_nand_info {
+	struct nand_chip	chip;
+	struct mtd_info		mtd;
+	struct gpio_desc	*ce_gpio;
+	struct gpio_desc	*br_gpio;
+	uint8_t			*bit_reversed;
+	struct comcerto_bch_control	cbc;
+};
+
+static inline
+struct comcerto_nand_info *to_comerto_nand_info(struct nand_chip *chip)
+{
+	return container_of(chip, struct comcerto_nand_info, chip);
+}
+
+static void __iomem *ecc_base_addr;
+
+/*
+ * Define partitions for flash device
+ */
+
+uint32_t COMCERTO_NAND_ALE = 0x00000200;
+uint32_t COMCERTO_NAND_CLE = 0x00000400;
+
+#ifdef CONFIG_NAND_LS1024A_ECC_24_HW_BCH
+/*
+ * spare area layout for BCH ECC bytes calculated over 512-Bytes ECC block size
+ */
+static struct nand_ecclayout comcerto_ecc_info_512_bch = {
+	.eccbytes = 42,
+	.eccpos = {0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10,
+		  11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
+		  21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
+		  31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41},
+	.oobfree = {
+		{.offset = 43, .length = 13}
+	}
+};
+
+/*
+ * spare area layout for BCH ECC bytes calculated over 1024-Bytes ECC block size
+ */
+static struct nand_ecclayout comcerto_ecc_info_1024_bch = {
+	.eccbytes = 42,
+	.eccpos = {0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10,
+		  11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
+		  21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
+		  31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41},
+	.oobfree = {
+		{.offset = 43, .length = 13}
+	}
+};
+
+#elif defined(CONFIG_NAND_LS1024A_ECC_8_HW_BCH)
+
+/*
+ * spare area layout for BCH ECC bytes calculated over 512-Bytes ECC block size
+ */
+static struct nand_ecclayout comcerto_ecc_info_512_bch = {
+	.eccbytes = 14,
+	.eccpos = {0, 1, 2, 3, 4, 5, 6,
+		   7, 8, 9, 10, 11, 12, 13},
+	.oobfree = {
+		{.offset = 15, .length = 1}
+	}
+};
+
+/*
+ * spare area layout for BCH ECC bytes calculated over 1024-Bytes ECC block size
+ */
+static struct nand_ecclayout comcerto_ecc_info_1024_bch = {
+	.eccbytes = 14,
+	.eccpos = {0, 1, 2, 3, 4, 5, 6,
+		   7, 8, 9, 10, 11, 12, 13},
+	.oobfree = {
+		{.offset = 15, .length = 17}
+	}
+};
+
+#else
+
+/*
+ * spare area layout for Hamming ECC bytes calculated over 512-Bytes ECC block
+ * size
+ */
+static struct nand_ecclayout comcerto_ecc_info_512_hamm = {
+	.eccbytes = 4,
+	.eccpos = {0, 1, 2, 3},
+	.oobfree = {
+		{.offset = 5, .length = 12}
+	}
+};
+
+/*
+ * spare area layout for Hamming ECC bytes calculated over 1024-Bytes ECC block
+ * size
+ */
+static struct nand_ecclayout comcerto_ecc_info_1024_hamm = {
+	.eccbytes = 4,
+	.eccpos = {0, 1, 2, 3},
+	.oobfree = {
+		{.offset = 5, .length = 28}
+	}
+};
+#endif
+
+static uint8_t bbt_pattern[] = { 'B', 'b', 't', '0' };
+static uint8_t mirror_pattern[] = { '1', 't', 'b', 'B' };
+
+static struct nand_bbt_descr bbt_main_descr = {
+	.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+		| NAND_BBT_8BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+	.offs = 44,
+	.len = 4,
+	.veroffs = 48,
+	.maxblocks = 8,
+	.pattern = bbt_pattern,
+};
+
+static struct nand_bbt_descr bbt_mirror_descr = {
+	.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+		| NAND_BBT_8BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+	.offs = 44,
+	.len = 4,
+	.veroffs = 48,
+	.maxblocks = 8,
+	.pattern = mirror_pattern,
+};
+
+static uint8_t scan_ff_pattern[] = { 0xff };
+
+#ifdef CONFIG_NAND_LS1024A_ECC_24_HW_BCH
+static struct nand_bbt_descr c2000_badblock_pattern = {
+	.offs = 42,
+	.len = 1,
+	.pattern = scan_ff_pattern
+};
+#elif defined(CONFIG_NAND_LS1024A_ECC_8_HW_BCH)
+static struct nand_bbt_descr c2000_badblock_pattern = {
+	.offs = 14,
+	.len = 1,
+	.pattern = scan_ff_pattern
+};
+#endif
+
+/** Disable/Enable shifting of data to parity module
+ *
+ * @param[in] en_dis_shift  Enable or disable shift to parity module.
+ *
+ */
+static void comcerto_ecc_shift(uint8_t en_dis_shift)
+{
+	writel_relaxed(en_dis_shift, ecc_base_addr + ECC_SHIFT_EN_CFG);
+}
+
+/** Initializes h/w ECC with proper configuration values.
+ *
+ * @param[in] mtd	MTD device structure
+ * @param[in] mode	Select between BCH and Hamming
+ *
+ */
+static void comcerto_enable_hw_ecc(struct mtd_info *mtd, int mode)
+{
+	struct nand_chip *nand_device = (struct nand_chip *)(mtd->priv);
+	uint32_t ecc_gen_cfg_val = 0;
+
+	if (mode == NAND_ECC_READSYN) return;
+	/* CS4 will have the option for ECC calculation */
+	writel_relaxed(ECC_CS4_SEL, ecc_base_addr + ECC_CS_SEL_CFG);
+
+	/* parity calculation for write, syndrome calculation for read.*/
+	(mode == NAND_ECC_WRITE) ? (ecc_gen_cfg_val |= PRTY_CALC) : (ecc_gen_cfg_val &= SYNDROME_CALC);
+
+#if defined (CONFIG_NAND_LS1024A_ECC_8_HW_BCH) || defined (CONFIG_NAND_LS1024A_ECC_24_HW_BCH)
+	ecc_gen_cfg_val &= BCH_MODE;
+	ecc_gen_cfg_val = (ecc_gen_cfg_val & ~(ECC_LVL_MASK)) | (ECC_LVL_VAL << ECC_LVL_SHIFT);
+#else
+	ecc_gen_cfg_val |= HAMM_MODE;
+#endif
+
+	ecc_gen_cfg_val = (ecc_gen_cfg_val & ~(BLK_SIZE_MASK)) | nand_device->ecc.size;
+
+	writel_relaxed(ecc_gen_cfg_val, ecc_base_addr + ECC_GEN_CFG);
+	/* Reset parity module and latch configured values */
+	writel_relaxed(ECC_INIT, ecc_base_addr + ECC_INIT_CFG);
+	comcerto_ecc_shift(ECC_SHIFT_ENABLE);
+	return;
+}
+
+/** writes ECC bytes generated by the parity module into the flash
+ *
+ * @param[in] mtd	MTD device structure
+ * @param[in] dat	raw data
+ * @param[in] ecc_code	buffer for ECC
+ *
+ */
+static int comcerto_calculate_ecc(struct mtd_info *mtd,
+				  const uint8_t *dat,
+				  uint8_t *ecc_code)
+{
+	struct nand_chip *nand_device = mtd->priv;
+	int ecc_bytes = nand_device->ecc.bytes;
+	uint8_t *ecc_calc = nand_device->buffers->ecccalc;
+	unsigned long timeo;
+
+	comcerto_ecc_shift(ECC_SHIFT_DISABLE);
+
+	/* Wait for syndrome calculation to complete */
+	timeo = jiffies + 4;
+	for (;;) {
+		int is_timeout = time_after_eq(jiffies, timeo);
+		int is_idle = readl_relaxed(ecc_base_addr + ECC_IDLE_STAT) & ECC_IDLE;
+		if (is_idle)
+			break;
+		if (is_timeout) {
+			pr_err("ECC Timeout waiting for parity module to become idle 1\n");
+			return -EIO;
+		}
+		touch_softlockup_watchdog();
+	}
+
+	comcerto_ecc_shift(ECC_SHIFT_ENABLE);
+
+	writel_relaxed(ECC_PARITY_OUT_EN, ecc_base_addr + ECC_PRTY_OUT_SEL_CFG);
+
+	/* Even though we do a dummy write to NAND flash, actual ECC bytes are
+	 * written to the ECC location in the flash. The contents of ecc_calc
+	 * are irrelevant. The ECC engine overwrites it with real ECC data. */
+	nand_device->write_buf(mtd, ecc_calc, ecc_bytes);
+
+	comcerto_ecc_shift(ECC_SHIFT_DISABLE);
+	writel_relaxed(ECC_PARITY_OUT_DISABLE, ecc_base_addr + ECC_PRTY_OUT_SEL_CFG);
+
+	return 0;
+}
+
+/** Checks ECC registers for errors and will correct them, if correctable
+ *
+ * @param[in] mtd	MTD device structure
+ * @param[in] dat	raw data
+ * @param[in] read_ecc  ECC read out from flash
+ * @param[in] calc_ecc	ECC calculated over the raw data
+ *
+ */
+static int comcerto_correct_ecc(struct mtd_info *mtd, uint8_t *dat,
+		uint8_t *read_ecc, uint8_t *calc_ecc)
+{
+	struct nand_chip *nand_device = mtd->priv;
+	int num_zero_bits = 0;
+	int empty = 0;
+#if defined (CONFIG_NAND_LS1024A_ECC_8_HW_BCH) || defined (CONFIG_NAND_LS1024A_ECC_24_HW_BCH)
+	uint8_t err_count = 0;
+	uint32_t err_corr_data_prev;
+#endif
+	uint32_t err_corr_data;
+	uint16_t mask, index;
+	unsigned long timeo;
+	int ret;
+
+	num_zero_bits = nand_check_erased_ecc_chunk(dat, nand_device->ecc.size,
+			read_ecc, nand_device->ecc.bytes,
+			NULL, 0,
+			nand_device->ecc.strength);
+	if (num_zero_bits >= 0) {
+		/* Consider ECC chunk empty */
+		empty = 1;
+		if (num_zero_bits > 2) {
+			pr_err_ratelimited("ECC: ECC chunk is mostly empty but has %d zero bits\n", num_zero_bits);
+		}
+		/*
+		 * Even though the ECC chunk is empty, we still continue, wait
+		 * for the syndrome calculation to complete and read out all
+		 * syndromes even though there should be none.
+		 * */
+	}
+
+	/* Wait for syndrome calculation to complete */
+	timeo = jiffies + 4;
+	for (;;) {
+		int is_timeout = time_after_eq(jiffies, timeo);
+		int is_idle = readl_relaxed(ecc_base_addr + ECC_IDLE_STAT) & ECC_IDLE;
+		if (is_idle)
+			break;
+		if (is_timeout) {
+			pr_err("ECC Timeout waiting for parity module to become idle 2\n");
+			ret = -EIO;
+			goto out;
+		}
+		touch_softlockup_watchdog();
+	}
+
+	 /* If no correction is required */
+	if (likely(!((readl_relaxed(ecc_base_addr + ECC_POLY_STAT)) & ECC_CORR_REQ))) {
+		ret = 0;
+		goto out;
+	}
+
+	/* Error found! Correction required */
+#if defined (CONFIG_NAND_LS1024A_ECC_8_HW_BCH) || defined (CONFIG_NAND_LS1024A_ECC_24_HW_BCH)
+	/* Initiate correction operation */
+	writel_relaxed(ECC_POLY_START, ecc_base_addr + ECC_POLY_START_CFG);
+
+	udelay(25);
+
+	timeo = jiffies + 4;
+	err_corr_data_prev = 0;
+	/* Read Correction data status register till header is 0x7FD */
+	for (;;) {
+		int is_startcode;
+		int is_timeout = time_after_eq(jiffies, timeo);
+		err_corr_data_prev = readl_relaxed(ecc_base_addr + ECC_CORR_DATA_STAT);
+		is_startcode = (err_corr_data_prev >> ECC_BCH_INDEX_SHIFT) == 0x87FD;
+		if (is_startcode)
+			break;
+		if (is_timeout) {
+			pr_err("Timeout waiting for ECC correction data, reg=%08x\n",
+				err_corr_data_prev);
+			ret = -EIO;
+			goto out;
+		}
+		touch_softlockup_watchdog();
+	}
+
+	udelay(25);
+	err_corr_data = 0x0;
+	/* start reading error locations */
+	while (((err_corr_data >> 16) !=  0x87FE)) {
+		err_corr_data = readl_relaxed(ecc_base_addr + ECC_CORR_DATA_STAT);
+		if ((err_corr_data >> 16) ==  0x87FE)
+			break;
+		if (err_corr_data == err_corr_data_prev)
+			continue;
+		err_corr_data_prev = err_corr_data;
+		/*
+		 * If we determined that the ECC chunk is empty, we ignore all
+		 * syndromes.
+		 * */
+		if (empty)
+			continue;
+		index = (err_corr_data >> 16) & 0x7FF;
+		mask = err_corr_data & 0xFFFF;
+		if (index * 2 >= nand_device->ecc.size) {
+			pr_err("ECC correction index out of "
+					"bounds. ECC_CORR_DATA_STAT %08x\n",
+					err_corr_data);
+			continue;
+		}
+		*((uint16_t *)(dat + (index * 2))) ^= mask;
+		while (mask) {
+			if (mask & 1)
+				err_count++;
+			mask >>= 1;
+		}
+	}
+
+	if (!((readl_relaxed(ecc_base_addr + ECC_CORR_DONE_STAT)) & ECC_DONE)) {
+		pr_err("ECC: uncorrectable error 1 !!!\n");
+		ret = -1;
+		goto out;
+	}
+
+	/* Check if the block has uncorrectable number of errors */
+	if ((readl_relaxed(ecc_base_addr + ECC_CORR_STAT)) & ECC_UNCORR) {
+		if (!empty)
+			pr_err("ECC: uncorrectable error 2 !!!\n");
+		ret = -EIO;
+		goto out;
+	}
+
+#else		/* Hamming Mode */
+		if (readl_relaxed(ecc_base_addr + ECC_POLY_STAT) == ECC_UNCORR_ERR_HAMM) {
+			/* 2 or more errors detected and hence cannot
+			be corrected */
+			ret = -1; /* uncorrectable */
+			goto out;
+		} else {  /* 1-bit correctable error */
+			err_corr_data = readl_relaxed(ecc_base_addr + ECC_CORR_DATA_STAT);
+			index = (err_corr_data >> 16) & 0x1FF;
+
+			if (nand_device->options & NAND_BUSWIDTH_16) {
+				mask = 1 << (err_corr_data & 0xF);
+				*((uint16_t *)(dat + index)) ^= mask;
+			} else {
+				mask = 1 << (err_corr_data & 0x7);
+				*(dat + index) ^= mask;
+			}
+			ret = 1;
+			goto out;
+
+		}
+#endif
+
+	ret = err_count;
+out:
+
+	if (empty) {
+		/*
+		 * If we previously determined that this ECC chunk is empty,
+		 * just ignore whatever errors were detected while reading
+		 * syndromes from the ECC engine.
+		 * */
+		ret = num_zero_bits;
+	}
+
+	comcerto_ecc_shift(ECC_SHIFT_DISABLE);
+
+	return ret;
+}
+
+/** writes single page to the NAND device along with the ECC bytes
+ *
+ * @param[in] mtd	MTD device structure
+ * @param[in] chip      nand chip info structure
+ * @param[in] buf	data buffer
+ *
+ */
+static int comcerto_nand_write_page_hwecc(struct mtd_info *mtd,
+					struct nand_chip *chip,
+					const uint8_t *buf, int oob_required)
+{
+	int i, eccsize = chip->ecc.size;
+	int eccbytes = chip->ecc.bytes;
+	int eccsteps = chip->ecc.steps;
+	const uint8_t *p = buf;
+	uint8_t *oob = chip->oob_poi;
+
+	for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+
+		chip->ecc.hwctl(mtd, NAND_ECC_WRITE);
+		chip->write_buf(mtd, p, eccsize);
+
+		chip->ecc.calculate(mtd, p, oob);
+		oob += eccbytes;
+
+		if (chip->ecc.postpad) {
+			chip->write_buf(mtd, oob, chip->ecc.postpad);
+			oob += chip->ecc.postpad;
+		}
+	}
+
+	/* Calculate remaining oob bytes */
+	i = mtd->oobsize - (oob - chip->oob_poi);
+	if (i)
+		chip->write_buf(mtd, oob, i);
+
+	return 0;
+}
+
+
+static void comcerto_hybrid_hwctl (struct mtd_info *mtd, int mode) {
+	if (mode == NAND_ECC_READ) comcerto_enable_hw_ecc(mtd, mode);
+}
+
+#if 0
+static void comcerto_fake_hwctl (struct mtd_info *mtd, int mode) {
+}
+#endif
+
+/* Replicate the LS1024A HW ECC engine in software. Compared to the BCH
+ * implemenation in Linux, the LS1024A HW ECC engine (seemingly licensed from
+ * Cyclic Design) does a few things differently:
+ *
+ * - They use 0x4443 as the primitive polynomial for BCH (Linux uses 0x402b by
+ *   default).
+ * - They reverse the 8 bits in every byte before they calculate the ECC code.
+ *   They then also reverse the bits in the ECC code before they write it to
+ *   flash.
+ * - They don't XOR the inverse of the ECC code of an empty page (all 0xFF's)
+ *   onto every calculated ECC code. (Linux does this to ensure that an empty
+ *   page has a valid ECC code).
+ *   */
+static int comcerto_bch_calculate_ecc (struct mtd_info *mtd, const uint8_t *dat,
+		uint8_t *ecc_code) {
+	struct nand_chip *chip = mtd->priv;
+	struct comcerto_nand_info *info = to_comerto_nand_info(chip);
+	int i;
+
+	for (i=0;i<chip->ecc.size;i++) {
+		info->bit_reversed[i] = bitrev8(dat[i]);
+	}
+	memset(ecc_code, 0, chip->ecc.bytes);
+	encode_bch(info->cbc.bch, info->bit_reversed, chip->ecc.size, ecc_code);
+	for (i=0;i<chip->ecc.bytes;i++) {
+		ecc_code[i] = bitrev8(ecc_code[i]);
+	}
+	return 0;
+}
+
+/* We currently don't need comcerto_bch_correct_ecc() because we are using a
+ * hybrid approach where we use the HW ECC engine when we read from NAND. We
+ * only calculate the ECC code in software when we write to the flash.  */
+#if 0
+/*
+ * This function replicates the Cyclic Design ECC engine that can
+ * be found in the LS1024A. It calculates the ECC code a little different from
+ * nand_bch_correct_data() (nand_bch.c)
+ * */
+static int comcerto_bch_correct_ecc (struct mtd_info *mtd, uint8_t *dat, uint8_t *read_ecc,
+		uint8_t *dummy) {
+	struct nand_chip *chip = mtd->priv;
+	struct comcerto_nand_info *info = to_comerto_nand_info(chip);
+	int num_zero_bits = 0;
+	int i, count;
+
+	num_zero_bits = nand_check_erased_ecc_chunk(dat, chip->ecc.size,
+			read_ecc, chip->ecc.bytes,
+			NULL, 0,
+			chip->ecc.strength);
+	if (num_zero_bits >= 0) {
+		if (num_zero_bits > 2) {
+			pr_err_ratelimited("ECC: ECC chunk is mostly empty but has %d zero bits\n", num_zero_bits);
+		}
+		return num_zero_bits;
+	}
+
+	for (i=0;i<chip->ecc.size;i++) {
+		info->bit_reversed[i] = bitrev8(dat[i]);
+	}
+	for (i=0;i<chip->ecc.bytes;i++) {
+		read_ecc[i] = bitrev8(read_ecc[i]);
+	}
+	count = decode_bch(info->cbc.bch, info->bit_reversed, chip->ecc.size, read_ecc, NULL,
+			NULL, info->cbc.errloc);
+	if (count > 0) {
+		for (i = 0; i < count; i++) {
+			if (info->cbc.errloc[i] < (chip->ecc.size*8))
+				/* error is located in data, correct it */
+				dat[info->cbc.errloc[i] >> 3] ^= (1 << (7 - (info->cbc.errloc[i] & 7)));
+			/* else error in ecc, no action needed */
+		}
+	} else if (count < 0) {
+		pr_err_ratelimited("ecc unrecoverable error\n");
+		count = -1;
+	}
+	return count;
+}
+#endif
+
+/** reads single page from the NAND device and will read ECC bytes from flash. A
+ * function call to comcerto_correct_ecc() will be used to validate the data.
+ *
+ * @param[in] mtd	MTD device structure
+ * @param[in] chip      nand chip info structure
+ * @param[in] buf	data buffer
+ *
+ */
+#if 0
+static int comcerto_nand_read_page_hwecc(struct mtd_info *mtd,
+		struct nand_chip *chip, uint8_t *buf, int page)
+{
+	struct nand_chip *nand_device = mtd->priv;
+	int i, eccsize = nand_device->ecc.size;
+	int eccbytes = nand_device->ecc.bytes;
+	int eccsteps = nand_device->ecc.steps;
+	uint8_t *p = buf;
+	uint8_t *ecc_code = nand_device->buffers->ecccode;
+	int ecc_bytes = nand_device->ecc.bytes;
+	int stat;
+	uint8_t *oob = nand_device->oob_poi;
+
+
+	for (; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+
+		chip->ecc.hwctl(mtd, NAND_ECC_READ);
+		chip->read_buf(mtd, p, eccsize);
+		chip->read_buf(mtd, ecc_code, ecc_bytes);
+
+		stat = chip->ecc.correct(mtd, p, oob, NULL);
+		if (stat < 0) {
+			mtd->ecc_stats.failed++;
+			pr_err("ECC correction failed for page 0x%08x\n", page);
+		} else {
+			int idx = eccsteps;
+			if (idx >= MTD_ECC_STAT_SUBPAGES) {
+				idx = MTD_ECC_STAT_SUBPAGES - 1;
+			}
+
+			mtd->ecc_stats.corrected += stat;
+			mtd->ecc_subpage_stats.subpage_corrected[idx] += stat;
+		}
+
+		if (chip->ecc.postpad) {
+			chip->read_buf(mtd, oob, chip->ecc.postpad);
+			oob += chip->ecc.postpad;
+		}
+	}
+	/* Calculate remaining oob bytes */
+	i = mtd->oobsize - (oob - chip->oob_poi);
+	if (i)
+		chip->read_buf(mtd, oob, i);
+
+	return 0;
+}
+#endif
+
+/*********************************************************************
+ * NAND Hardware functions
+ *
+ *********************************************************************/
+
+/*
+ *	hardware specific access to control-lines
+*/
+void comcerto_nand_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct comcerto_nand_info *info = to_comerto_nand_info(chip);
+
+	if (ctrl & NAND_CTRL_CHANGE) {
+		gpiod_set_value(info->ce_gpio, !(ctrl & NAND_NCE));
+	}
+
+	if (cmd == NAND_CMD_NONE)
+		return;
+
+	 if (ctrl & NAND_CLE)
+		 writeb(cmd, chip->IO_ADDR_W + COMCERTO_NAND_CLE);
+	 else if (ctrl & NAND_ALE)
+		writeb(cmd, chip->IO_ADDR_W + COMCERTO_NAND_ALE);
+	 else
+		return;
+
+}
+
+int comcerto_nand_ready(struct mtd_info *mtd)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct comcerto_nand_info *info = to_comerto_nand_info(chip);
+
+	return gpiod_get_value(info->br_gpio);
+}
+
+/*********************************************************************
+ * NAND Probe
+ *
+ *********************************************************************/
+static int comcerto_nand_probe(struct platform_device *pdev)
+{
+	struct comcerto_nand_info *info;
+	struct mtd_info *mtd;
+	struct nand_chip *chip;
+	struct resource *res;
+	int err = 0;
+	struct mtd_part_parser_data ppdata = {};
+
+	/* Allocate memory for info structure */
+	info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
+	if (!info)
+		return -ENOMEM;
+
+	mtd = &info->mtd;
+	chip = &info->chip;
+	mtd->owner = THIS_MODULE;
+
+	/* Link the private data with the MTD structure */
+	mtd->priv = &info->chip;
+
+	info->ce_gpio = devm_gpiod_get(&pdev->dev, "ce",
+			GPIOD_FLAGS_BIT_DIR_SET | GPIOD_FLAGS_BIT_DIR_OUT);
+	if (IS_ERR(info->ce_gpio)) {
+		dev_err(&pdev->dev, "Failed to get CE GPIO.\n");
+		goto out_info;
+	}
+	info->br_gpio = devm_gpiod_get(&pdev->dev, "br",
+			GPIOD_FLAGS_BIT_DIR_SET);
+	if (IS_ERR(info->br_gpio)) {
+		dev_err(&pdev->dev, "Failed to get BR GPIO.\n");
+		goto out_info;
+	}
+
+	/*Map physical address of nand into virtual space */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	chip->IO_ADDR_R = devm_ioremap_resource(&pdev->dev, res);
+
+	if (IS_ERR(chip->IO_ADDR_R)) {
+		pr_err("LS1024A NAND: cannot map nand memory\n");
+		err = PTR_ERR(chip->IO_ADDR_R);
+		goto out_info;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	ecc_base_addr = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(ecc_base_addr)) {
+		pr_err("LS1024A NAND: cannot map ecc config\n");
+		err = PTR_ERR(chip->IO_ADDR_R);
+		goto out_iorc;
+	}
+
+	/* This is the same address to read and write */
+	chip->IO_ADDR_W = chip->IO_ADDR_R;
+
+	/* Set address of hardware control function */
+	chip->cmd_ctrl = comcerto_nand_hwcontrol;
+	chip->dev_ready = comcerto_nand_ready;
+	chip->ecc.mode = NAND_ECC_HW_SYNDROME;
+//	chip->ecc.mode = NAND_ECC_SOFT_BCH;
+
+#if defined(CONFIG_C2K_ASIC) && defined(CONFIG_NAND_TYPE_SLC)
+	chip->options = NAND_BUSWIDTH_16;
+#else
+	chip->options = 0;
+#endif
+
+	/* Scan to find existence of the device */
+	if (nand_scan_ident(mtd, 1, NULL)) {
+		err = -ENXIO;
+		goto out_ior;
+	}
+
+	if (1 && chip->ecc.mode == NAND_ECC_HW_SYNDROME) {
+		/* We use a hybrid approach here where we use the ECC hardware
+		 * in the read path to correct ECC errors on read. For writing,
+		 * we calculate the ECC code in software. The reason for this
+		 * is to work around a silicon bug where the ECC hardware would
+		 * disturb and corrupt the NOR flash if the user tries to write
+		 * to NOR flash and NAND flash concurrently.
+		 */
+		unsigned int m, t;
+
+		chip->ecc.hwctl = comcerto_hybrid_hwctl;
+		chip->ecc.calculate = comcerto_bch_calculate_ecc;
+		chip->ecc.correct = comcerto_correct_ecc;
+		dev_info(&pdev->dev, "Using hybrid hw/sw ECC\n");
+		chip->ecc.size =  1024;
+		chip->ecc.layout = &comcerto_ecc_info_1024_bch;
+		chip->ecc.strength = 24;
+		chip->ecc.prepad = 0;
+		chip->ecc.postpad = 14;
+
+		chip->ecc.steps = mtd->writesize / chip->ecc.size;
+		if(chip->ecc.steps * chip->ecc.size != mtd->writesize) {
+			dev_err(&pdev->dev, "Invalid ecc parameters\n");
+			BUG();
+		}
+		chip->ecc.total = chip->ecc.steps * chip->ecc.bytes;
+		chip->ecc.bytes = DIV_ROUND_UP(
+				chip->ecc.strength * fls(8 * chip->ecc.size), 8);
+		if (chip->ecc.bytes != 42) {
+			dev_warn(&pdev->dev, "ecc->bytes != 42\n");
+		}
+
+		m = fls(1+8*chip->ecc.size);
+		t = (chip->ecc.bytes*8)/m;
+
+		/* The LS1024A HW ECC engine (seemingly licensed from Cyclic
+		 * Design) uses 0x4443 as the primitive polynomial for BCH
+		 * (Linux uses 0x402b by default). */
+		info->cbc.bch = init_bch(m, t, 0x4443);
+		if (!info->cbc.bch) {
+			err = -EINVAL;
+			goto out_ior;
+		}
+
+		/* verify that eccbytes has the expected value */
+		if (info->cbc.bch->ecc_bytes != chip->ecc.bytes) {
+			dev_warn(&pdev->dev, "invalid eccbytes %u, should be %u\n",
+					chip->ecc.bytes, info->cbc.bch->ecc_bytes);
+			free_bch(info->cbc.bch);
+			err = -EINVAL;
+			goto out_ior;
+		}
+
+		/* sanity checks */
+		if (8*(chip->ecc.size+chip->ecc.bytes) >= (1 << m)) {
+			dev_warn(&pdev->dev, "eccsize %u is too large\n", chip->ecc.size);
+			free_bch(info->cbc.bch);
+			err = -EINVAL;
+			goto out_ior;
+		}
+
+#if 0
+		info->cbc.errloc = devm_kmalloc(&pdev->dev, t*sizeof(*info->cbc.errloc), GFP_KERNEL);
+		if (!info->cbc.errloc) {
+			free_bch(info->cbc.bch);
+			err = -ENOMEM;
+			goto out_ior;
+		}
+#endif
+
+		info->bit_reversed = devm_kzalloc(&pdev->dev, chip->ecc.size, GFP_KERNEL);
+		if (!info->bit_reversed) {
+			free_bch(info->cbc.bch);
+			err = -ENOMEM;
+			goto out_ior;
+		}
+		chip->bbt_td = &bbt_main_descr;
+		chip->bbt_md = &bbt_mirror_descr;
+		chip->badblock_pattern = &c2000_badblock_pattern;
+		chip->bbt_options |= NAND_BBT_USE_FLASH;
+
+	} else if (chip->ecc.mode == NAND_ECC_HW_SYNDROME) {
+		chip->ecc.hwctl = comcerto_enable_hw_ecc;
+		chip->ecc.write_page = comcerto_nand_write_page_hwecc;
+		// chip->ecc.read_page = comcerto_nand_read_page_hwecc;
+		chip->ecc.calculate = comcerto_calculate_ecc;
+		chip->ecc.correct = comcerto_correct_ecc;
+		pr_info("hw_syndrome correction %d.\n", mtd->writesize);
+
+		switch (mtd->writesize) {
+		case 512:
+			chip->ecc.size = mtd->writesize;
+#if defined (CONFIG_NAND_LS1024A_ECC_24_HW_BCH)
+			chip->ecc.layout = &comcerto_ecc_info_512_bch;
+			chip->ecc.bytes = 42;
+			chip->ecc.strength = 24;
+			chip->ecc.prepad = 0;
+			chip->ecc.postpad = 14;
+#elif defined(CONFIG_NAND_LS1024A_ECC_8_HW_BCH)
+			chip->ecc.layout = &comcerto_ecc_info_512_bch;
+			chip->ecc.bytes = 14;
+			chip->ecc.strength = 8;
+			chip->ecc.prepad = 0;
+			chip->ecc.postpad = 2;
+#else
+			chip->ecc.layout = &comcerto_ecc_info_512_hamm;
+			chip->ecc.bytes = 4;
+			chip->ecc.prepad = 0;
+			chip->ecc.postpad = 2;
+#endif
+			break;
+		case 1024:
+			chip->ecc.size = mtd->writesize;
+#ifdef CONFIG_NAND_LS1024A_ECC_24_HW_BCH
+			chip->ecc.layout = &comcerto_ecc_info_1024_bch;
+			chip->ecc.bytes = 42;
+			chip->ecc.strength = 24;
+			chip->ecc.prepad = 0;
+			chip->ecc.postpad = 14;
+#elif defined(CONFIG_NAND_LS1024A_ECC_8_HW_BCH)
+			chip->ecc.layout = &comcerto_ecc_info_1024_bch;
+			chip->ecc.bytes = 14;
+			chip->ecc.strength = 8;
+			chip->ecc.prepad = 0;
+			chip->ecc.postpad = 18;
+#else
+			chip->ecc.layout = &comcerto_ecc_info_1024_hamm;
+			chip->ecc.bytes = 4;
+			chip->ecc.prepad = 0;
+			chip->ecc.postpad = 18;
+#endif
+			break;
+		default:
+			pr_err("Using default values for hw ecc\n");
+			chip->ecc.size =  1024;
+#ifdef CONFIG_NAND_LS1024A_ECC_24_HW_BCH
+			chip->ecc.layout = &comcerto_ecc_info_1024_bch;
+			chip->ecc.bytes = 42;
+			chip->ecc.strength = 24;
+			chip->ecc.prepad = 0;
+			chip->ecc.postpad = 14;
+#elif defined(CONFIG_NAND_LS1024A_ECC_8_HW_BCH)
+			chip->ecc.layout = &comcerto_ecc_info_1024_bch;
+			chip->ecc.bytes = 14;
+			chip->ecc.strength = 8;
+			chip->ecc.prepad = 0;
+			chip->ecc.postpad = 18;
+#else
+			chip->ecc.layout = &comcerto_ecc_info_1024_hamm;
+			chip->ecc.bytes = 4;
+			chip->ecc.prepad = 0;
+			chip->ecc.postpad = 18;
+#endif
+			break;
+		}
+		chip->ecc.steps = mtd->writesize / chip->ecc.size;
+		if(chip->ecc.steps * chip->ecc.size != mtd->writesize) {
+			pr_err("Invalid ecc parameters\n");
+			BUG();
+		}
+		chip->ecc.total = chip->ecc.steps * chip->ecc.bytes;
+
+
+		chip->bbt_td = &bbt_main_descr;
+		chip->bbt_md = &bbt_mirror_descr;
+		chip->badblock_pattern = &c2000_badblock_pattern;
+		chip->bbt_options |= NAND_BBT_USE_FLASH;
+
+	} else {
+		pr_info("using soft ecc.\n");
+		chip->ecc.size =  1024;
+		chip->ecc.strength = 24;
+		chip->ecc.bytes = 42;
+		chip->ecc.mode = NAND_ECC_SOFT_BCH;
+	}
+
+
+	chip->options |= NAND_NO_SUBPAGE_WRITE;
+
+	if(nand_scan_tail(mtd)) {
+		pr_err("nand_scan_tail returned error\n");
+		err = -ENXIO;
+		goto out_ior;
+	}
+
+	/* Name of the mtd device */
+	mtd->name = dev_name(&pdev->dev);
+
+	/* Link the info stucture with platform_device */
+	platform_set_drvdata(pdev, info);
+
+	ppdata.of_node = pdev->dev.of_node;
+	err = mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0);
+	if (err) {
+		nand_release(mtd);
+		goto out_ior;
+	}
+
+	goto out;
+
+      out_ior:
+	devm_iounmap(&pdev->dev, ecc_base_addr);
+      out_iorc:
+	devm_iounmap(&pdev->dev, chip->IO_ADDR_R);
+      out_info:
+	devm_kfree(&pdev->dev, info);
+      out:
+	return err;
+}
+
+/*********************************************************************
+ * NAND Remove
+ *
+ *********************************************************************/
+static int comcerto_nand_remove(struct platform_device *pdev)
+{
+	struct comcerto_nand_info *info =
+	    (struct comcerto_nand_info *)platform_get_drvdata(pdev);
+
+	platform_set_drvdata(pdev, NULL);
+
+	/* Release resources, unregister device */
+	nand_release(&info->mtd);
+
+	/*Deregister virtual address */
+	iounmap(info->chip.IO_ADDR_R);
+	iounmap(ecc_base_addr);
+	return 0;
+}
+
+/*********************************************************************
+ * Driver Registration
+ *
+ *********************************************************************/
+
+static const struct of_device_id fsl_ls1024a_nand_match[] = {
+	{
+		.compatible = "fsl,ls1024a-nand",
+	},
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, fsl_ls1024a_nand_match);
+
+static struct platform_driver ls1024a_nand_driver = {
+	.probe = comcerto_nand_probe,
+	.remove = comcerto_nand_remove,
+	.driver = {
+		.name = "ls1024a_nand",
+		.of_match_table = fsl_ls1024a_nand_match,
+	},
+};
+
+module_platform_driver(ls1024a_nand_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Board-specific glue layer for NAND flash on Comcerto board");
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index fa5cd51..ffdaddf 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -544,23 +544,32 @@
 	}
 }
 
-/* Wait for the ready pin, after a command. The timeout is caught later. */
+/**
+ * nand_wait_ready - [GENERIC] Wait for the ready pin after commands.
+ * @mtd: MTD device structure
+ *
+ * Wait for the ready pin after a command, and warn if a timeout occurs.
+ */
 void nand_wait_ready(struct mtd_info *mtd)
 {
 	struct nand_chip *chip = mtd->priv;
-	unsigned long timeo = jiffies + msecs_to_jiffies(20);
+	unsigned long timeo = 400;
 
-	/* 400ms timeout */
 	if (in_interrupt() || oops_in_progress)
-		return panic_nand_wait_ready(mtd, 400);
+		return panic_nand_wait_ready(mtd, timeo);
 
 	led_trigger_event(nand_led_trigger, LED_FULL);
 	/* Wait until command is processed or timeout occurs */
+	timeo = jiffies + msecs_to_jiffies(timeo);
 	do {
 		if (chip->dev_ready(mtd))
-			break;
-		touch_softlockup_watchdog();
+			goto out;
+		cond_resched();
 	} while (time_before(jiffies, timeo));
+
+	pr_warn_ratelimited(
+		"timeout while waiting for chip to become ready\n");
+out:
 	led_trigger_event(nand_led_trigger, LED_OFF);
 }
 EXPORT_SYMBOL_GPL(nand_wait_ready);
@@ -886,15 +895,13 @@
  * @mtd: MTD device structure
  * @chip: NAND chip structure
  *
- * Wait for command done. This applies to erase and program only. Erase can
- * take up to 400ms and program up to 20ms according to general NAND and
- * SmartMedia specs.
+ * Wait for command done. This applies to erase and program only.
  */
 static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
 {
 
-	int status, state = chip->state;
-	unsigned long timeo = (state == FL_ERASING ? 400 : 20);
+	int status;
+	unsigned long timeo = 400;
 
 	led_trigger_event(nand_led_trigger, LED_FULL);
 
@@ -910,7 +917,7 @@
 		panic_nand_wait(mtd, chip, timeo);
 	else {
 		timeo = jiffies + msecs_to_jiffies(timeo);
-		while (time_before(jiffies, timeo)) {
+		do {
 			if (chip->dev_ready) {
 				if (chip->dev_ready(mtd))
 					break;
@@ -919,7 +926,7 @@
 					break;
 			}
 			cond_resched();
-		}
+		} while (time_before(jiffies, timeo));
 	}
 	led_trigger_event(nand_led_trigger, LED_OFF);
 
@@ -1102,6 +1109,134 @@
 EXPORT_SYMBOL(nand_lock);
 
 /**
+ * nand_check_erased_buf - check if a buffer contains (almost) only 0xff data
+ * @buf: buffer to test
+ * @len: buffer length
+ * @bitflips_threshold: maximum number of bitflips
+ *
+ * Check if a buffer contains only 0xff, which means the underlying region
+ * has been erased and is ready to be programmed.
+ * The bitflips_threshold specify the maximum number of bitflips before
+ * considering the region is not erased.
+ * Note: The logic of this function has been extracted from the memweight
+ * implementation, except that nand_check_erased_buf function exit before
+ * testing the whole buffer if the number of bitflips exceed the
+ * bitflips_threshold value.
+ *
+ * Returns a positive number of bitflips less than or equal to
+ * bitflips_threshold, or -ERROR_CODE for bitflips in excess of the
+ * threshold.
+ */
+static int nand_check_erased_buf(void *buf, int len, int bitflips_threshold)
+{
+	const unsigned char *bitmap = buf;
+	int bitflips = 0;
+	int weight;
+
+	for (; len && ((uintptr_t)bitmap) % sizeof(long);
+	     len--, bitmap++) {
+		weight = hweight8(*bitmap);
+		bitflips += BITS_PER_BYTE - weight;
+		if (unlikely(bitflips > bitflips_threshold))
+			return -EBADMSG;
+	}
+
+	for (; len >= sizeof(long);
+	     len -= sizeof(long), bitmap += sizeof(long)) {
+		weight = hweight_long(*((unsigned long *)bitmap));
+		bitflips += BITS_PER_LONG - weight;
+		if (unlikely(bitflips > bitflips_threshold))
+			return -EBADMSG;
+	}
+
+	for (; len > 0; len--, bitmap++) {
+		weight = hweight8(*bitmap);
+		bitflips += BITS_PER_BYTE - weight;
+		if (unlikely(bitflips > bitflips_threshold))
+			return -EBADMSG;
+	}
+
+	return bitflips;
+}
+
+/**
+ * nand_check_erased_ecc_chunk - check if an ECC chunk contains (almost) only
+ *				 0xff data
+ * @data: data buffer to test
+ * @datalen: data length
+ * @ecc: ECC buffer
+ * @ecclen: ECC length
+ * @extraoob: extra OOB buffer
+ * @extraooblen: extra OOB length
+ * @bitflips_threshold: maximum number of bitflips
+ *
+ * Check if a data buffer and its associated ECC and OOB data contains only
+ * 0xff pattern, which means the underlying region has been erased and is
+ * ready to be programmed.
+ * The bitflips_threshold specify the maximum number of bitflips before
+ * considering the region as not erased.
+ *
+ * Note:
+ * 1/ ECC algorithms are working on pre-defined block sizes which are usually
+ *    different from the NAND page size. When fixing bitflips, ECC engines will
+ *    report the number of errors per chunk, and the NAND core infrastructure
+ *    expect you to return the maximum number of bitflips for the whole page.
+ *    This is why you should always use this function on a single chunk and
+ *    not on the whole page. After checking each chunk you should update your
+ *    max_bitflips value accordingly.
+ * 2/ When checking for bitflips in erased pages you should not only check
+ *    the payload data but also their associated ECC data, because a user might
+ *    have programmed almost all bits to 1 but a few. In this case, we
+ *    shouldn't consider the chunk as erased, and checking ECC bytes prevent
+ *    this case.
+ * 3/ The extraoob argument is optional, and should be used if some of your OOB
+ *    data are protected by the ECC engine.
+ *    It could also be used if you support subpages and want to attach some
+ *    extra OOB data to an ECC chunk.
+ *
+ * Returns a positive number of bitflips less than or equal to
+ * bitflips_threshold, or -ERROR_CODE for bitflips in excess of the
+ * threshold. In case of success, the passed buffers are filled with 0xff.
+ */
+int nand_check_erased_ecc_chunk(void *data, int datalen,
+				void *ecc, int ecclen,
+				void *extraoob, int extraooblen,
+				int bitflips_threshold)
+{
+	int data_bitflips = 0, ecc_bitflips = 0, extraoob_bitflips = 0;
+
+	data_bitflips = nand_check_erased_buf(data, datalen,
+					      bitflips_threshold);
+	if (data_bitflips < 0)
+		return data_bitflips;
+
+	bitflips_threshold -= data_bitflips;
+
+	ecc_bitflips = nand_check_erased_buf(ecc, ecclen, bitflips_threshold);
+	if (ecc_bitflips < 0)
+		return ecc_bitflips;
+
+	bitflips_threshold -= ecc_bitflips;
+
+	extraoob_bitflips = nand_check_erased_buf(extraoob, extraooblen,
+						  bitflips_threshold);
+	if (extraoob_bitflips < 0)
+		return extraoob_bitflips;
+
+	if (data_bitflips)
+		memset(data, 0xff, datalen);
+
+	if (ecc_bitflips)
+		memset(ecc, 0xff, ecclen);
+
+	if (extraoob_bitflips)
+		memset(extraoob, 0xff, extraooblen);
+
+	return data_bitflips + ecc_bitflips + extraoob_bitflips;
+}
+EXPORT_SYMBOL(nand_check_erased_ecc_chunk);
+
+/**
  * nand_read_page_raw - [INTERN] read raw page data without ecc
  * @mtd: mtd info structure
  * @chip: nand chip info structure
diff --git a/drivers/net/phy/at803x.c b/drivers/net/phy/at803x.c
index fabf11d..a285cd5 100644
--- a/drivers/net/phy/at803x.c
+++ b/drivers/net/phy/at803x.c
@@ -42,6 +42,7 @@
 #define ATH8030_PHY_ID 0x004dd076
 #define ATH8031_PHY_ID 0x004dd074
 #define ATH8035_PHY_ID 0x004dd072
+#define ATH8337_PHY_ID 0x004dd036
 
 MODULE_DESCRIPTION("Atheros 803x PHY driver");
 MODULE_AUTHOR("Matus Ujhelyi");
@@ -351,6 +352,25 @@
 	.driver			= {
 		.owner = THIS_MODULE,
 	},
+}, {
+	/* ATHEROS 8337 */
+	.phy_id			= ATH8337_PHY_ID,
+	.name			= "Atheros 8337 ethernet",
+	.phy_id_mask		= 0xffffffff,
+	.probe			= at803x_probe,
+	.config_init		= at803x_config_init,
+	.link_change_notify	= at803x_link_change_notify,
+	.set_wol		= at803x_set_wol,
+	.get_wol		= at803x_get_wol,
+	.suspend		= at803x_suspend,
+	.resume			= at803x_resume,
+	.features		= PHY_GBIT_FEATURES,
+	.flags			= PHY_HAS_INTERRUPT,
+	.config_aneg		= genphy_config_aneg,
+	.read_status		= genphy_read_status,
+	.driver			= {
+		.owner = THIS_MODULE,
+	},
 } };
 
 module_phy_driver(at803x_driver);
@@ -359,6 +379,7 @@
 	{ ATH8030_PHY_ID, 0xffffffef },
 	{ ATH8031_PHY_ID, 0xffffffef },
 	{ ATH8035_PHY_ID, 0xffffffef },
+	{ ATH8337_PHY_ID, 0xffffffff },
 	{ }
 };
 
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index 47cd578..7124895 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -421,8 +421,13 @@
 			      mii_data->reg_num, val);
 
 		if (mii_data->reg_num == MII_BMCR &&
-		    val & BMCR_RESET)
+		    val & BMCR_RESET) {
+#ifdef CONFIG_ARCH_COMCERTO
+			if (mii_data->phy_id >= 16)
+				return 0;
+#endif
 			return phy_init_hw(phydev);
+		}
 
 		if (change_autoneg)
 			return phy_start_aneg(phydev);
diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
index 1dfb567..e2ee75f 100644
--- a/drivers/pci/host/Kconfig
+++ b/drivers/pci/host/Kconfig
@@ -30,6 +30,12 @@
 	select PCIEPORTBUS
 	select PCIE_DW
 
+config PCI_LS1024A
+	bool "Freescale LS1024A PCIe controller"
+	depends on ARCH_COMCERTO
+	select PCIEPORTBUS
+	select PCIE_DW
+
 config PCI_TEGRA
 	bool "NVIDIA Tegra PCIe controller"
 	depends on ARCH_TEGRA && !ARM64
diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
index f733b4e..028de0d 100644
--- a/drivers/pci/host/Makefile
+++ b/drivers/pci/host/Makefile
@@ -2,6 +2,7 @@
 obj-$(CONFIG_PCI_DRA7XX) += pci-dra7xx.o
 obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o
 obj-$(CONFIG_PCI_IMX6) += pci-imx6.o
+obj-$(CONFIG_PCI_LS1024A) += pci-ls1024a.o
 obj-$(CONFIG_PCI_MVEBU) += pci-mvebu.o
 obj-$(CONFIG_PCI_TEGRA) += pci-tegra.o
 obj-$(CONFIG_PCI_RCAR_GEN2) += pci-rcar-gen2.o
diff --git a/drivers/pci/host/pci-ls1024a.c b/drivers/pci/host/pci-ls1024a.c
new file mode 100644
index 0000000..2afcb6b
--- /dev/null
+++ b/drivers/pci/host/pci-ls1024a.c
@@ -0,0 +1,382 @@
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+#include <linux/reset.h>
+
+#include "pcie-designware.h"
+
+#define to_ls1024a_pcie(x)	container_of(x, struct ls1024a_pcie, pp)
+
+struct ls1024a_pcie {
+	int			reset_gpio;
+	struct reset_control	*axi_rst;
+	struct clk		*pcie;
+	struct phy		*phy;
+	struct pcie_port	pp;
+	struct regmap		*app;
+	int			app_profile;
+	struct gpio_desc	*gpiod_reset;
+};
+
+
+/* The following register definitions are as per "DWC_regs_rev04.doc" document */
+
+struct pcie_app_reg {
+	u32	cfg0;
+	u32	cfg1;
+	u32	cfg2;
+	u32	cfg3;
+	u32	cfg4;
+	u32	cfg5;
+	u32	cfg6;
+	u32	sts0;
+	u32	sts1;
+	u32	sts2;
+	u32	sts3;
+	u32	pwr_cfg_bdgt_data;
+	u32	pwr_cfg_bdgt_fn;
+	u32	radm_sts;
+	u32	pwr_sts_bdgt;
+	u32	intr_sts;
+	u32	intr_en;
+	u32	intr_msi_sts;
+	u32	intr_msi_en;
+};
+
+#define MAX_PCIE_PORTS  2
+/* DWC PCIEe configuration register offsets on APB */
+struct pcie_app_reg app_regs[MAX_PCIE_PORTS] = {
+	/* PCIe0 */
+	{
+		0x00000000, /* cfg0 */
+		0x00000004,
+		0x00000008,
+		0x0000000C,
+		0x00000010,
+		0x00000014,
+		0x00000018, /* cfg6 */
+		0x00000040, /* sts0 */
+		0x00000044,
+		0x00000048,
+		0x00000058, /* sts3 */
+		0x00000080,
+		0x00000084,
+		0x000000C0, /* radm_sts */
+		0x000000C4,
+		0x00000100, /* intr_sts */
+		0x00000104,
+		0x00000108,
+		0x0000010C
+	},
+	/* PCIe1 */
+	{
+		0x00000020,
+		0x00000024,
+		0x00000028,
+		0x0000002C,
+		0x00000030,
+		0x00000034,
+		0x00000038,
+		0x0000004C,
+		0x00000050,
+		0x00000054,
+		0x0000005C,
+		0x00000088,
+		0x0000008C,
+		0x000000C8,
+		0x000000CC,
+		0x00000110,
+		0x00000114,
+		0x00000118,
+		0x0000011C
+	}
+};
+
+/* INTR_STS and INTR_EN Register definitions */
+#define 	INTR_CTRL_INTA_ASSERT	0x0001
+#define 	INTR_CTRL_INTA_DEASSERT	0x0002
+#define 	INTR_CTRL_INTB_ASSERT	0x0004
+#define 	INTR_CTRL_INTB_DEASSERT	0x0008
+#define 	INTR_CTRL_INTC_ASSERT	0x0010
+#define 	INTR_CTRL_INTC_DEASSERT	0x0020
+#define 	INTR_CTRL_INTD_ASSERT	0x0040
+#define 	INTR_CTRL_INTD_DEASSERT	0x0080
+#define 	INTR_CTRL_AER		0x0100
+#define 	INTR_CTRL_PME		0x0200
+#define 	INTR_CTRL_HP		0x0400
+#define 	INTR_CTRL_LINK_AUTO_BW	0x0800
+#define 	INTR_CTRL_MSI		0x1000
+static irqreturn_t ls1024a_pcie_msi_handler(int irq, void *arg)
+{
+	struct pcie_port *pp = arg;
+	struct ls1024a_pcie *ls1024a_pcie = to_ls1024a_pcie(pp);
+	irqreturn_t ret = IRQ_NONE;
+	u32 status = 0;
+
+	regmap_read(ls1024a_pcie->app, app_regs[ls1024a_pcie->app_profile].intr_sts, &status);
+	if (status & INTR_CTRL_MSI) {
+		regmap_write(ls1024a_pcie->app, app_regs[ls1024a_pcie->app_profile].intr_sts, INTR_CTRL_MSI);
+		status &= ~INTR_CTRL_MSI;
+		ret = dw_handle_msi_irq(pp);
+	}
+	regmap_write(ls1024a_pcie->app, app_regs[ls1024a_pcie->app_profile].intr_sts, status);
+	if (status)
+		return IRQ_NONE;
+	else
+		return ret;
+
+}
+
+static int ls1024a_pcie_wait_for_link(struct pcie_port *pp)
+{
+	int count = 200;
+
+	while (!dw_pcie_link_up(pp)) {
+		usleep_range(100, 1000);
+		if (--count)
+			continue;
+
+		dev_err(pp->dev, "phy link never came up\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/* Port Logic Registers */
+#define PCIE_ALRT_REG              0x700
+#define PCIE_AFL0L1_REG            0x70C
+#define PCIE_SYMNUM_REG            0x718
+#define PCIE_G2CTRL_REG            0x80C
+
+#define PCIE_CAP_BASE           0x70
+#define PCIE_LCAP_REG           (PCIE_CAP_BASE + 0x0C)
+#define PCIE_LCNT2_REG          (PCIE_CAP_BASE + 0x30)
+
+static void ls1024a_pcie_host_init(struct pcie_port *pp)
+{
+	struct ls1024a_pcie *ls1024a_pcie = to_ls1024a_pcie(pp);
+	int ret;
+	u32 val;
+
+	ret = reset_control_deassert(ls1024a_pcie->axi_rst);
+	ret = phy_init(ls1024a_pcie->phy);
+	/* TODO: Do we really need this delay */
+	mdelay(1); //After CMU locks wait for sometime
+
+	/* TODO: PCIe_SATA_RST_CNTRL */
+#define 	CFG5_APP_INIT_RST	0x01
+#define 	CFG5_LTSSM_ENABLE	0x02
+	//Hold the LTSSM in detect state
+	regmap_update_bits(ls1024a_pcie->app, app_regs[ls1024a_pcie->app_profile].cfg5, CFG5_LTSSM_ENABLE, 0);
+
+	//FIXME : Bit:27 definition is not clear from document
+	//	  This register setting is copied from simulation code.
+	regmap_update_bits(ls1024a_pcie->app, app_regs[ls1024a_pcie->app_profile].cfg0, 0x08007FF0, 0x08007FF0);
+
+	val = readl(pp->dbi_base + PCIE_AFL0L1_REG);
+	val &= ~(0x00FFFF00);
+	val |= 0x00F1F100;
+	writel(val, pp->dbi_base + PCIE_AFL0L1_REG);
+
+	if(0) // if (pcie_gen1_only)
+	{
+		writel(0x1, pp->dbi_base + PCIE_LCNT2_REG);
+		writel(0x1, pp->dbi_base + PCIE_LCAP_REG);
+	}
+	else
+	{
+		val = readl(pp->dbi_base + PCIE_G2CTRL_REG);
+		val &= ~(0xFF);
+		val |= 0xF1;
+		writel(val, pp->dbi_base + PCIE_G2CTRL_REG);
+
+		// instruct pcie to switch to gen2 after init
+		val = readl(pp->dbi_base + PCIE_G2CTRL_REG);
+		val |= (1 << 17);
+		writel(val, pp->dbi_base + PCIE_G2CTRL_REG);
+	}
+
+	dw_pcie_setup_rc(pp);
+
+	//Enable LTSSM to start link initialization
+	regmap_update_bits(ls1024a_pcie->app, app_regs[ls1024a_pcie->app_profile].cfg5,
+			(CFG5_APP_INIT_RST | CFG5_LTSSM_ENABLE),
+			(CFG5_APP_INIT_RST | CFG5_LTSSM_ENABLE));
+
+	ls1024a_pcie_wait_for_link(&ls1024a_pcie->pp);
+
+	if (IS_ENABLED(CONFIG_PCI_MSI))
+		dw_pcie_msi_init(pp);
+
+}
+
+#define 	STS0_RDLH_LINK_UP	0x10000
+static int ls1024a_pcie_link_up(struct pcie_port *pp)
+{
+	struct ls1024a_pcie *ls1024a_pcie = to_ls1024a_pcie(pp);
+	u32 rc = 0;
+
+	regmap_read(ls1024a_pcie->app, app_regs[ls1024a_pcie->app_profile].sts0, &rc);
+	return !!(rc & STS0_RDLH_LINK_UP);
+}
+
+static struct pcie_host_ops ls1024a_pcie_host_ops = {
+	.link_up = ls1024a_pcie_link_up,
+	.host_init = ls1024a_pcie_host_init,
+};
+
+static int __init ls1024a_add_pcie_port(struct pcie_port *pp,
+			struct platform_device *pdev)
+{
+	int ret;
+
+	if (IS_ENABLED(CONFIG_PCI_MSI)) {
+		pp->msi_irq = platform_get_irq_byname(pdev, "msi");
+		if (pp->msi_irq <= 0) {
+			dev_err(&pdev->dev, "failed to get MSI irq\n");
+			return -ENODEV;
+		}
+
+		ret = devm_request_irq(&pdev->dev, pp->msi_irq,
+				       ls1024a_pcie_msi_handler,
+				       IRQF_SHARED, "ls1024a-pcie-msi", pp);
+		if (ret) {
+			dev_err(&pdev->dev, "failed to request MSI irq\n");
+			return -ENODEV;
+		}
+	}
+
+	pp->root_bus_nr = -1;
+	pp->ops = &ls1024a_pcie_host_ops;
+
+	ret = dw_pcie_host_init(pp);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to initialize host\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int __init ls1024a_pcie_probe(struct platform_device *pdev)
+{
+	struct ls1024a_pcie *ls1024a_pcie;
+	struct pcie_port *pp;
+	struct device_node *np = pdev->dev.of_node;
+	struct resource *dbi_base;
+	int ret;
+
+	ls1024a_pcie = devm_kzalloc(&pdev->dev, sizeof(*ls1024a_pcie), GFP_KERNEL);
+	if (!ls1024a_pcie)
+		return -ENOMEM;
+
+	pp = &ls1024a_pcie->pp;
+	pp->dev = &pdev->dev;
+
+	/* Added for PCI abort handling */
+#if 0
+	hook_fault_code(16 + 6, ls1024aq_pcie_abort_handler, SIGBUS, 0,
+		"imprecise external abort");
+#endif
+
+	dbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi");
+	pp->dbi_base = devm_ioremap_resource(&pdev->dev, dbi_base);
+	if (IS_ERR(pp->dbi_base))
+		return PTR_ERR(pp->dbi_base);
+#if 0
+	app = platform_get_resource_byname(pdev, IORESOURCE_MEM, "app");
+	ls1024a_pcie->app = devm_ioremap_resource(&pdev->dev, app);
+	if (IS_ERR(ls1024a_pcie->app))
+		return PTR_ERR(ls1024a_pcie->app);
+#endif
+
+	ls1024a_pcie->app = syscon_regmap_lookup_by_phandle(np, "app-syscon");
+	if (IS_ERR(ls1024a_pcie->app)) {
+		dev_err(&pdev->dev,
+			"Error retrieving pci_sata_usb_ctrl syscon\n");
+		return PTR_ERR(ls1024a_pcie->app);
+	}
+
+	ls1024a_pcie->app_profile = 0;
+	of_property_read_u32(np, "app-profile", &ls1024a_pcie->app_profile);
+	/* Fetch clocks */
+	ls1024a_pcie->pcie = devm_clk_get(&pdev->dev, "pcie");
+	if (IS_ERR(ls1024a_pcie->pcie)) {
+		dev_err(&pdev->dev,
+			"pcie clock source missing or invalid\n");
+		return PTR_ERR(ls1024a_pcie->pcie);
+	}
+	ls1024a_pcie->phy = devm_of_phy_get(pp->dev, np, NULL);
+	if (IS_ERR(ls1024a_pcie->phy)) {
+		ret = PTR_ERR(ls1024a_pcie->phy);
+		if (ret == -EPROBE_DEFER) {
+			return ret;
+		} else if (ret != -ENOSYS && ret != -ENODEV) {
+			dev_err(pp->dev,
+				"Error retrieving PCIe phy: %d\n", ret);
+			return ret;
+		}
+	}
+
+	ls1024a_pcie->axi_rst = devm_reset_control_get(pp->dev, "axi");
+	if (IS_ERR(ls1024a_pcie->axi_rst))
+		return PTR_ERR(ls1024a_pcie->axi_rst);
+
+	ls1024a_pcie->gpiod_reset = devm_gpiod_get_optional(pp->dev, "reset", GPIOD_OUT_LOW);
+	if (IS_ERR(ls1024a_pcie->gpiod_reset)) {
+		ls1024a_pcie->gpiod_reset = 0;
+	}
+	if (ls1024a_pcie->gpiod_reset) {
+		pr_err("Pulsing GPIO reset line\n");
+		gpiod_set_value(ls1024a_pcie->gpiod_reset, 1);
+		msleep(1);
+		gpiod_set_value(ls1024a_pcie->gpiod_reset, 0);
+		msleep(1);
+	}
+	/* TODO */
+#define 	CFG0_DEV_TYPE_RC 	0x04
+	regmap_update_bits(ls1024a_pcie->app, app_regs[ls1024a_pcie->app_profile].cfg0, 0xf, CFG0_DEV_TYPE_RC);
+
+	ret = ls1024a_add_pcie_port(pp, pdev);
+	if (ret < 0)
+		return ret;
+	/* TODO */
+	regmap_update_bits(ls1024a_pcie->app, app_regs[ls1024a_pcie->app_profile].intr_en, INTR_CTRL_INTA_ASSERT, INTR_CTRL_INTA_ASSERT);
+	regmap_update_bits(ls1024a_pcie->app, app_regs[ls1024a_pcie->app_profile].intr_en, INTR_CTRL_MSI, INTR_CTRL_MSI);
+
+	platform_set_drvdata(pdev, ls1024a_pcie);
+	return 0;
+}
+
+static const struct of_device_id ls1024a_pcie_of_match[] = {
+	{ .compatible = "fsl,ls1024a-pcie", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, ls1024a_pcie_of_match);
+
+static struct platform_driver ls1024a_pcie_driver = {
+	.driver = {
+		.name	= "ls1024a-pcie",
+		.of_match_table = ls1024a_pcie_of_match,
+	},
+};
+
+static int __init ls1024a_pcie_init(void)
+{
+	return platform_driver_probe(&ls1024a_pcie_driver, ls1024a_pcie_probe);
+}
+module_init(ls1024a_pcie_init);
+
+MODULE_AUTHOR("Daniel Mentz <danielmentz@google.com>");
+MODULE_DESCRIPTION("Freescale LS1024A PCIe host controller driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c
index 2e9f84f..836351e 100644
--- a/drivers/pci/host/pcie-designware.c
+++ b/drivers/pci/host/pcie-designware.c
@@ -158,6 +158,9 @@
 	.irq_unmask = pci_msi_unmask_irq,
 };
 
+static void dw_pcie_msi_clear_irq(struct pcie_port *pp, int irq);
+static void dw_pcie_msi_set_irq(struct pcie_port *pp, int irq);
+
 /* MSI int handler */
 irqreturn_t dw_handle_msi_irq(struct pcie_port *pp)
 {
@@ -174,9 +177,22 @@
 			while ((pos = find_next_bit(&val, 32, pos)) != 32) {
 				irq = irq_find_mapping(pp->irq_domain,
 						i * 32 + pos);
+				/* On the Freescale LS1024A (formerly Mindspeed
+				 * Comcerto 2000) SoC, it appears to be
+				 * necessary for the interrupt to be masked
+				 * while we clear it. We found this workaround
+				 * in the source code provided by Mindspeed.
+				 * They added the following comment:
+				 *
+				 * FIXME : WA for bz69520
+				 * To avoid race condition during avk the interrupt disabling interrupt before
+				 * Ack and enabling after Ack.
+				 */
+				dw_pcie_msi_clear_irq(pp, i * 32 + pos);
 				dw_pcie_wr_own_conf(pp,
 						PCIE_MSI_INTR0_STATUS + i * 12,
 						4, 1 << pos);
+				dw_pcie_msi_set_irq(pp, i * 32 + pos);
 				generic_handle_irq(irq);
 				pos++;
 			}
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 993ff22..05195da 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -1945,6 +1945,7 @@
 	pci_bus_assign_domain_nr(b, parent);
 	b2 = pci_find_bus(pci_domain_nr(b), bus);
 	if (b2) {
+		dump_stack();
 		/* If we already got to this bus through a different bridge, ignore it */
 		dev_dbg(&b2->dev, "bus already known\n");
 		goto err_out;
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index fc9b9f0..0d67d05 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -313,4 +313,29 @@
 	help
 	  Support for UFS PHY on QCOM chipsets.
 
+config PHY_LS1024A_SNOWBUSH
+	tristate "LS1024A Snowbush PHY driver"
+	depends on OF && ARCH_COMCERTO
+	select GENERIC_PHY
+	help
+	  Support for Snowbush PHY in Freescale's LS1024A. This is required for
+	  PCIe and SATA support.
+
+config PHY_LS1024A_USB2
+	tristate "NXP QorIQ LS1024A USB2 PHY driver"
+	depends on OF && ARCH_COMCERTO
+	select GENERIC_PHY
+	help
+	  Support for USB2 PHY in NXP's QorIQ LS1024A. This is required
+	  for USB2 support.
+
+config PHY_LS1024A_USB3
+	tristate "Freescale QorIQ LS1024A USB3 PHY driver"
+	depends on OF && ARCH_COMCERTO
+	select GENERIC_PHY
+	help
+	  Support for USB3 PHY in Freescale's QorIQ LS1024A. This is required
+	  for USB3 support.
+
+
 endmenu
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index f126251..9e523ff 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -40,3 +40,6 @@
 obj-$(CONFIG_PHY_QCOM_UFS) 	+= phy-qcom-ufs.o
 obj-$(CONFIG_PHY_QCOM_UFS) 	+= phy-qcom-ufs-qmp-20nm.o
 obj-$(CONFIG_PHY_QCOM_UFS) 	+= phy-qcom-ufs-qmp-14nm.o
+obj-$(CONFIG_PHY_LS1024A_SNOWBUSH)	+= phy-snowbush.o
+obj-$(CONFIG_PHY_LS1024A_USB2)	+= phy-ls1024a-usb2.o
+obj-$(CONFIG_PHY_LS1024A_USB3)	+= phy-ls1024a-usb3.o
diff --git a/drivers/phy/phy-ls1024a-usb2.c b/drivers/phy/phy-ls1024a-usb2.c
new file mode 100644
index 0000000..d177239
--- /dev/null
+++ b/drivers/phy/phy-ls1024a-usb2.c
@@ -0,0 +1,173 @@
+/*
+ * PHY driver for NXP QorIQ LS1024A USB 2.0 OTG PHY
+ *
+ * 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.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/io.h>
+
+#define USB0_PHY_CTRL_REG0	0x000
+#define DWC_CFG_REGF		0x03C
+
+struct ls1024a_usb2_otg_phy {
+	struct phy *phy;
+	struct clk *clk;
+	void __iomem *reg;
+	struct regmap *pci_sata_usb_ctrl_reg;
+	struct reset_control *rstc_phy;
+	struct reset_control *rstc_utmi;
+	struct reset_control *rstc_axi;
+};
+
+static int ls1024a_usb2_otg_phy_init(struct phy *phy)
+{
+	struct ls1024a_usb2_otg_phy *lu2p = phy_get_drvdata(phy);
+	int ret;
+
+	ret = clk_prepare_enable(lu2p->clk);
+	if (ret)
+		return ret;
+
+	/*
+	 * DWC_CFG_REGF
+	 *
+	 * Bit[8]: USB0_iddig select line either from PHY utmiotg_iddig or from
+	 * the register bit:
+	 *   0: Selects iddig from PHY.
+	 *   1: Selects from the register bit (Bit[9])
+	 * Bit[9]:Sets whether the connected plug is a mini-A or mini-B plug RW
+	 * if usb0_id_sel register is programmed to be 1:
+	 *   0: sets Mini-A
+	 *   1: sets Mini-B
+	 *
+	 * Bit[12] and Bit[13] are the equivalents of Bit[8] and Bit[9] for the
+	 * second USB port. (The LS1024A has only one USB2.0 port anyway)
+	 *
+	 * We force both ports to Mini-A because we always operate in host
+	 * mode. Also, we are not using Mini-USB connectors in the first place.
+	 * All our boards have Type A receptacles.
+	 */
+	regmap_update_bits(lu2p->pci_sata_usb_ctrl_reg, DWC_CFG_REGF, 0x0000FF00, 0x00001100);
+
+	reset_control_assert(lu2p->rstc_utmi);
+	reset_control_assert(lu2p->rstc_phy);
+	reset_control_assert(lu2p->rstc_axi);
+
+	reset_control_deassert(lu2p->rstc_phy);
+	reset_control_deassert(lu2p->rstc_utmi);
+	reset_control_deassert(lu2p->rstc_axi);
+
+	return 0;
+}
+
+static int ls1024a_usb2_otg_phy_exit(struct phy *phy)
+{
+	struct ls1024a_usb2_otg_phy *lu2p = phy_get_drvdata(phy);
+
+	reset_control_assert(lu2p->rstc_utmi);
+	reset_control_assert(lu2p->rstc_phy);
+	reset_control_assert(lu2p->rstc_axi);
+
+	clk_disable_unprepare(lu2p->clk);
+
+	return 0;
+}
+
+static const struct phy_ops ls1024a_usb2_otg_phy_ops = {
+	.init		= ls1024a_usb2_otg_phy_init,
+	.exit		= ls1024a_usb2_otg_phy_exit,
+	.owner		= THIS_MODULE,
+};
+
+static int ls1024a_usb2_otg_phy_probe(struct platform_device *pdev)
+{
+	struct phy_provider *phy_provider;
+	struct ls1024a_usb2_otg_phy *lu2p;
+	struct resource *res;
+
+	lu2p = devm_kzalloc(&pdev->dev, sizeof(*lu2p), GFP_KERNEL);
+	if (!lu2p)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	lu2p->reg = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(lu2p->reg)) {
+		dev_err(&pdev->dev, "Failed to map register memory (phy)\n");
+		return PTR_ERR(lu2p->reg);
+	}
+
+	lu2p->pci_sata_usb_ctrl_reg =
+		syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "syscon");
+	if (IS_ERR(lu2p->pci_sata_usb_ctrl_reg)) {
+		dev_err(&pdev->dev, "Failed to get pci_sata_usb_ctrl syscon regmap\n");
+		return PTR_ERR(lu2p->pci_sata_usb_ctrl_reg);
+	}
+
+	lu2p->clk = devm_clk_get(&pdev->dev, "usb");
+	if (IS_ERR(lu2p->clk)) {
+		dev_err(&pdev->dev, "Failed to get clock of phy controller\n");
+		return PTR_ERR(lu2p->clk);
+	}
+
+	lu2p->rstc_phy = devm_reset_control_get(&pdev->dev, "phy");
+	if (IS_ERR(lu2p->rstc_phy)) {
+		dev_err(&pdev->dev, "Failed to get phy reset control\n");
+		return PTR_ERR(lu2p->rstc_phy);
+	}
+
+	lu2p->rstc_utmi = devm_reset_control_get(&pdev->dev, "utmi");
+	if (IS_ERR(lu2p->rstc_utmi)) {
+		dev_err(&pdev->dev, "Failed to get utmi reset control\n");
+		return PTR_ERR(lu2p->rstc_utmi);
+	}
+
+	lu2p->rstc_axi = devm_reset_control_get(&pdev->dev, "axi");
+	if (IS_ERR(lu2p->rstc_axi)) {
+		dev_err(&pdev->dev, "Failed to get axi reset control\n");
+		return PTR_ERR(lu2p->rstc_axi);
+	}
+
+	lu2p->phy = devm_phy_create(&pdev->dev, NULL, &ls1024a_usb2_otg_phy_ops);
+	if (IS_ERR(lu2p->phy)) {
+		dev_err(&pdev->dev, "Failed to create phy\n");
+		return PTR_ERR(lu2p->phy);
+	}
+
+	phy_set_drvdata(lu2p->phy, lu2p);
+
+	phy_provider = devm_of_phy_provider_register(&pdev->dev,
+						     of_phy_simple_xlate);
+
+	return PTR_ERR_OR_ZERO(phy_provider);
+}
+
+static const struct of_device_id ls1024a_usb2_otg_phy_match[] = {
+	{ .compatible = "fsl,ls1024a-usb2-phy", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, ls1024a_usb2_otg_phy_match);
+
+static struct platform_driver ls1024a_usb2_otg_phy_driver = {
+	.probe		= ls1024a_usb2_otg_phy_probe,
+	.driver		= {
+		.name 	= "ls1024a-usb2-phy",
+		.of_match_table = ls1024a_usb2_otg_phy_match,
+	},
+};
+module_platform_driver(ls1024a_usb2_otg_phy_driver);
+
+MODULE_DESCRIPTION("NXP QorIQ LS1024A USB2 PHY");
+MODULE_AUTHOR("Daniel Mentz <danielmentz@google.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/phy-ls1024a-usb3.c b/drivers/phy/phy-ls1024a-usb3.c
new file mode 100644
index 0000000..274ac0b
--- /dev/null
+++ b/drivers/phy/phy-ls1024a-usb3.c
@@ -0,0 +1,132 @@
+/*
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2 or later. This program is licensed "as is"
+ * without any warranty of any kind, whether express or implied.
+ */
+
+#include <dt-bindings/phy/phy.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+
+struct ls1024a_usb3_phy {
+	struct phy *phy;
+	void __iomem *reg;
+	struct reset_control *rstc;
+};
+
+static int ls1024a_usb_phy_init(struct phy *phy)
+{
+	struct ls1024a_usb3_phy *lu_phy;
+	u32 val;
+
+	lu_phy = phy_get_drvdata(phy);
+	if (!lu_phy)
+		return -ENODEV;
+
+	if (!reset_control_status(lu_phy->rstc)) {
+		reset_control_assert(lu_phy->rstc);
+		udelay(1000);
+	}
+
+        writel(0x00E00080, lu_phy->reg + 0x10);
+
+	/* TODO: Support 48 MHz refclock and external clock */
+#if 0
+	//Configuration for internal clock
+	if(usb3_clk_internal)
+	{
+		printk(KERN_INFO "USB3.0 clock selected: internal\n", __func__);
+
+		if(HAL_get_ref_clk() == REF_CLK_24MHZ)
+#endif
+			val = 0x420E82A8;
+#if 0
+		else
+			val = 0x420E82A9;
+	}
+	else
+	{
+		val = 0x4209927A;
+		printk(KERN_INFO "USB3.0 clock selected: external\n", __func__);
+	}
+#endif
+
+	writel(val, lu_phy->reg + 0x20);
+        writel(0x69C34F53, lu_phy->reg + 0x24);
+        writel(0x0005D815, lu_phy->reg + 0x28);
+        writel(0x00000801, lu_phy->reg + 0x2C);
+
+	reset_control_deassert(lu_phy->rstc);
+	udelay(1000);
+
+	return 0;
+}
+
+static struct phy_ops ls1024a_usb_phy_ops = {
+	.init = ls1024a_usb_phy_init,
+	.owner = THIS_MODULE,
+};
+
+static int ls1024a_usb_phy_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct phy *phy;
+	struct phy_provider *phy_provider;
+	void __iomem *reg;
+	struct reset_control *rstc;
+	struct resource *res;
+	struct ls1024a_usb3_phy *lu_phy;
+
+	lu_phy = devm_kzalloc(dev, sizeof(*lu_phy), GFP_KERNEL);
+	if (!lu_phy)
+		return  -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	reg = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(reg))
+		return PTR_ERR(reg);
+
+	rstc = devm_reset_control_get(&pdev->dev, "phy");
+	if (IS_ERR(rstc))
+		return PTR_ERR(rstc);
+
+	phy = devm_phy_create(dev, NULL, &ls1024a_usb_phy_ops);
+	if (IS_ERR(phy))
+		return PTR_ERR(phy);
+
+	lu_phy->phy = phy;
+	lu_phy->reg = reg;
+	lu_phy->rstc = rstc;
+
+	dev_set_drvdata(dev, lu_phy);
+	phy_set_drvdata(phy, lu_phy);
+
+	phy_provider = devm_of_phy_provider_register(&pdev->dev,
+						of_phy_simple_xlate);
+	return PTR_ERR_OR_ZERO(phy_provider);
+}
+
+static const struct of_device_id phy_of_match[] = {
+	{ .compatible = "fsl,ls1024a-usb3-phy", },
+	{ /* end of list */ },
+};
+MODULE_DEVICE_TABLE(of, phy_of_match);
+
+static struct platform_driver ls1024a_usb_phy_driver = {
+	.probe	= ls1024a_usb_phy_probe,
+	.driver = {
+		.of_match_table	= phy_of_match,
+		.name  = "ls1024a-usb3-phy",
+		.owner = THIS_MODULE,
+	}
+};
+module_platform_driver(ls1024a_usb_phy_driver);
+
+MODULE_DESCRIPTION("Freescale QorIQ LS1024A USB3 PHY");
+MODULE_AUTHOR("Daniel Mentz <danielmentz@google.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/phy-snowbush-regs.h b/drivers/phy/phy-snowbush-regs.h
new file mode 100644
index 0000000..7899897
--- /dev/null
+++ b/drivers/phy/phy-snowbush-regs.h
@@ -0,0 +1,1657 @@
+
+/*
+ * Copyright (C) Mindspeed Technologies, Inc. 2011. All rights reserved.
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ * @file serdes.h
+ * @brief this header file will contain all required data structure
+ *        and function definitions for Snowbush SerDes PHY interface.
+ * @date 10/02/2011
+ */
+/* SER-DES Address space */
+
+
+struct serdes_regs_s
+{
+	unsigned short ofst;
+	unsigned short val;
+};
+
+ /*  Initialize values of the Snowbush PHY (Serdes1) for SATA0 */
+
+/* 48MHz internal ref clock configuration */
+static struct serdes_regs_s sata_phy_reg_file_48[] __maybe_unused = {
+	{ 0x000 << 2, 0x06},
+	{ 0x001 << 2, 0x00},
+	{ 0x002 << 2, 0x88},
+	{ 0x003 << 2, 0x00},
+	{ 0x004 << 2, 0x84},
+	{ 0x005 << 2, 0x10},
+	{ 0x006 << 2, 0x20},
+	{ 0x007 << 2, 0x00},
+	{ 0x008 << 2, 0x00},
+	{ 0x009 << 2, 0x00},
+	{ 0x00A << 2, 0x00},
+	{ 0x00B << 2, 0x00},
+	{ 0x00C << 2, 0x00},
+	{ 0x00D << 2, 0x00},
+	{ 0x00E << 2, 0x00},
+	{ 0x00F << 2, 0x00},
+	{ 0x010 << 2, 0x00},
+	{ 0x011 << 2, 0x00},
+	{ 0x012 << 2, 0x00},
+	{ 0x013 << 2, 0x00},
+	{ 0x014 << 2, 0x00},
+	{ 0x015 << 2, 0x00},
+	{ 0x016 << 2, 0x00},
+	{ 0x017 << 2, 0x00},
+	{ 0x018 << 2, 0x00},
+	{ 0x019 << 2, 0x00},
+	{ 0x01A << 2, 0x00},
+	{ 0x01B << 2, 0x00},
+	{ 0x01C << 2, 0x00},
+	{ 0x01D << 2, 0x00},
+	{ 0x01E << 2, 0x00},
+	{ 0x01F << 2, 0x00},
+	{ 0x020 << 2, 0x00},
+	{ 0x021 << 2, 0x00},
+	{ 0x022 << 2, 0xa0},
+	{ 0x023 << 2, 0x62},
+	{ 0x024 << 2, 0x00},
+	{ 0x025 << 2, 0x00},
+	{ 0x026 << 2, 0x00},
+	{ 0x027 << 2, 0x00},
+	{ 0x028 << 2, 0x00},
+	{ 0x029 << 2, 0x00},
+	{ 0x02A << 2, 0x00},
+	{ 0x02B << 2, 0x00},
+	{ 0x02C << 2, 0x00},
+	{ 0x02D << 2, 0x00},
+	{ 0x02E << 2, 0x04},
+	{ 0x02F << 2, 0x50},
+	{ 0x030 << 2, 0x70},
+	{ 0x031 << 2, 0x02},
+	{ 0x032 << 2, 0x25},
+	{ 0x033 << 2, 0x40},
+	{ 0x034 << 2, 0x01},
+	{ 0x035 << 2, 0x40},
+	{ 0x036 << 2, 0x00},
+	{ 0x037 << 2, 0x00},
+	{ 0x038 << 2, 0x00},
+	{ 0x039 << 2, 0x00},
+	{ 0x03A << 2, 0x00},
+	{ 0x03B << 2, 0x00},
+	{ 0x03C << 2, 0x00},
+	{ 0x03D << 2, 0x00},
+	{ 0x03E << 2, 0x00},
+	{ 0x03F << 2, 0x00},
+	{ 0x040 << 2, 0x00},
+	{ 0x041 << 2, 0x00},
+	{ 0x042 << 2, 0x00},
+	{ 0x043 << 2, 0x00},
+	{ 0x044 << 2, 0x00},
+	{ 0x045 << 2, 0x00},
+	{ 0x046 << 2, 0x00},
+	{ 0x047 << 2, 0x00},
+	{ 0x048 << 2, 0x00},
+	{ 0x049 << 2, 0x00},
+	{ 0x04A << 2, 0x00},
+	{ 0x04B << 2, 0x00},
+	{ 0x04C << 2, 0x00},
+	{ 0x04D << 2, 0x00},
+	{ 0x04E << 2, 0x00},
+	{ 0x04F << 2, 0x00},
+	{ 0x050 << 2, 0x00},
+	{ 0x051 << 2, 0x00},
+	{ 0x052 << 2, 0x00},
+	{ 0x053 << 2, 0x00},
+	{ 0x054 << 2, 0x00},
+	{ 0x055 << 2, 0x00},
+	{ 0x056 << 2, 0x00},
+	{ 0x057 << 2, 0x00},
+	{ 0x058 << 2, 0x00},
+	{ 0x059 << 2, 0x00},
+	{ 0x05A << 2, 0x00},
+	{ 0x05B << 2, 0x00},
+	{ 0x05C << 2, 0x00},
+	{ 0x05D << 2, 0x00},
+	{ 0x05E << 2, 0x00},
+	{ 0x05F << 2, 0x00},
+	{ 0x060 << 2, 0x00},
+	{ 0x061 << 2, 0x2e},
+	{ 0x062 << 2, 0x08},
+	{ 0x063 << 2, 0x5e},
+	{ 0x064 << 2, 0x00},
+	{ 0x065 << 2, 0x42},
+	{ 0x066 << 2, 0xd1},
+	{ 0x067 << 2, 0xa0},
+	{ 0x068 << 2, 0x28},
+	{ 0x069 << 2, 0x78},
+	{ 0x06A << 2, 0xcc},
+	{ 0x06B << 2, 0xc1},
+	{ 0x06C << 2, 0x4e},
+	{ 0x06D << 2, 0x03},
+	{ 0x06E << 2, 0x00},
+	{ 0x06F << 2, 0x00},
+	{ 0x070 << 2, 0x00},
+	{ 0x071 << 2, 0x00},
+	{ 0x072 << 2, 0x00},
+	{ 0x200 << 2, 0x02},
+	{ 0x201 << 2, 0x00},
+	{ 0x202 << 2, 0x00},
+	{ 0x203 << 2, 0x00},
+	{ 0x204 << 2, 0x00},
+	{ 0x205 << 2, 0x10},
+	{ 0x206 << 2, 0x84},
+	{ 0x207 << 2, 0x3C},
+	{ 0x208 << 2, 0xe0},
+	{ 0x210 << 2, 0x23},
+	{ 0x211 << 2, 0x00},
+	{ 0x212 << 2, 0x40},
+	{ 0x213 << 2, 0x05},
+	{ 0x214 << 2, 0x20},/* */
+	{ 0x215 << 2, 0x17},
+	{ 0x216 << 2, 0x00},
+	{ 0x217 << 2, 0x68},
+	{ 0x218 << 2, 0xe2},
+	{ 0x219 << 2, 0x1e},
+	{ 0x21A << 2, 0x18},
+	{ 0x21B << 2, 0x0d},
+	{ 0x21C << 2, 0x0c},
+	{ 0x21D << 2, 0x00},
+	{ 0x21E << 2, 0x00},
+	{ 0x21F << 2, 0x00},
+	{ 0x220 << 2, 0x00},
+	{ 0x221 << 2, 0x00},
+	{ 0x222 << 2, 0x00},
+	{ 0x223 << 2, 0x00},
+	{ 0x224 << 2, 0x00},
+	{ 0x225 << 2, 0x00},
+	{ 0x226 << 2, 0x00},
+	{ 0x227 << 2, 0x00},
+	{ 0x228 << 2, 0x00},
+	{ 0x229 << 2, 0x00},
+	{ 0x22A << 2, 0x00},
+	{ 0x22B << 2, 0x00},
+	{ 0x22C << 2, 0x00},
+	{ 0x22D << 2, 0x00},
+	{ 0x22E << 2, 0x00},
+	{ 0x22F << 2, 0x00},
+	{ 0x230 << 2, 0x00},
+	{ 0x231 << 2, 0x00},
+	{ 0x232 << 2, 0x00},
+	{ 0x233 << 2, 0x00},
+	{ 0x234 << 2, 0x00},
+	{ 0x235 << 2, 0x00},
+	{ 0x236 << 2, 0x00},
+	{ 0x237 << 2, 0x00},
+	{ 0x238 << 2, 0x00},
+	{ 0x239 << 2, 0x00},
+	{ 0x23A << 2, 0x00},
+	{ 0x23B << 2, 0x00},
+	{ 0x23C << 2, 0x00},
+	{ 0x23D << 2, 0x00},
+	{ 0x23E << 2, 0x00},
+	{ 0x23F << 2, 0x00},
+	{ 0x240 << 2, 0x00},
+	{ 0x241 << 2, 0x00},
+	{ 0x242 << 2, 0x00},
+	{ 0x243 << 2, 0x00},
+	{ 0x244 << 2, 0x00},
+	{ 0x245 << 2, 0x00},
+	{ 0x246 << 2, 0x00},
+	{ 0x247 << 2, 0x00},
+	{ 0x248 << 2, 0x00},
+	{ 0x249 << 2, 0x00},
+	{ 0x24A << 2, 0x00},
+	{ 0x24B << 2, 0x00},
+	{ 0x24C << 2, 0x00},
+	{ 0x24D << 2, 0x00},
+	{ 0x24E << 2, 0x00},
+	{ 0x24F << 2, 0x00},
+	{ 0x250 << 2, 0x60},
+	{ 0x251 << 2, 0x0f},
+	{ 0xA00 << 2, 0x00},
+	{ 0xA01 << 2, 0x20},
+	{ 0xA02 << 2, 0x00},
+	{ 0xA03 << 2, 0x40},
+	{ 0xA04 << 2, 0x24},
+	{ 0xA05 << 2, 0xae},
+	{ 0xA06 << 2, 0x19},
+	{ 0xA07 << 2, 0x49},
+	{ 0xA08 << 2, 0x04},
+	{ 0xA09 << 2, 0xc3},
+	{ 0xA0A << 2, 0x2a},
+	{ 0xA0B << 2, 0xc6},
+	{ 0xA0C << 2, 0x01},
+	{ 0xA0D << 2, 0x03},
+	{ 0xA0E << 2, 0x28},
+	{ 0xA0F << 2, 0x98},
+	{ 0xA10 << 2, 0x19},
+	{ 0xA11 << 2, 0x00},
+	{ 0xA12 << 2, 0x00},
+	{ 0xA13 << 2, 0x80},
+	{ 0xA14 << 2, 0xf0},
+	{ 0xA15 << 2, 0xd0},
+	{ 0xA16 << 2, 0x00},
+	{ 0xA17 << 2, 0x00},
+	{ 0xA30 << 2, 0x00},
+	{ 0xA31 << 2, 0x00},
+	{ 0xA32 << 2, 0x00},
+	{ 0xA33 << 2, 0x00},
+	{ 0xA34 << 2, 0x00},
+	{ 0xA35 << 2, 0x00},
+	{ 0xA36 << 2, 0x00},
+	{ 0xA37 << 2, 0x00},
+	{ 0xA38 << 2, 0x00},
+	{ 0xA39 << 2, 0xa0},
+	{ 0xA3A << 2, 0xa0},
+	{ 0xA3B << 2, 0xa0},
+	{ 0xA3C << 2, 0xa0},
+	{ 0xA3D << 2, 0xa0},
+	{ 0xA3E << 2, 0xa0},
+	{ 0xA3F << 2, 0xa0},
+	{ 0xA40 << 2, 0x62},
+	{ 0xA41 << 2, 0x00},
+	{ 0xA42 << 2, 0x80},
+	{ 0xA43 << 2, 0x58},
+	{ 0xA44 << 2, 0x00},
+	{ 0xA45 << 2, 0x44},
+	{ 0xA46 << 2, 0x5c},
+	{ 0xA47 << 2, 0x86},
+	{ 0xA48 << 2, 0x8d},
+	{ 0xA49 << 2, 0xd0},
+	{ 0xA4A << 2, 0x09},
+	{ 0xA4B << 2, 0x90},
+	{ 0xA4C << 2, 0x07},
+	{ 0xA4D << 2, 0x40},
+	{ 0xA4E << 2, 0x00},
+	{ 0xA4F << 2, 0x00},
+	{ 0xA50 << 2, 0x00},
+	{ 0xA51 << 2, 0x20},
+	{ 0xA52 << 2, 0x32},
+	{ 0xA53 << 2, 0x00},
+	{ 0xA54 << 2, 0x00},
+	{ 0xA55 << 2, 0x00},
+	{ 0xA56 << 2, 0x00},
+	{ 0xA57 << 2, 0x00},
+	{ 0xA58 << 2, 0x00},
+	{ 0xA59 << 2, 0x00},
+	{ 0xA5A << 2, 0x00},
+	{ 0xA5B << 2, 0x00},
+	{ 0xA5C << 2, 0x00},
+	{ 0xA5D << 2, 0x00},
+	{ 0xA5E << 2, 0x00},
+	{ 0xA5F << 2, 0x00},
+	{ 0xA60 << 2, 0x00},
+	{ 0xA61 << 2, 0x00},
+	{ 0xA62 << 2, 0x00},
+	{ 0xA63 << 2, 0x00},
+	{ 0xA64 << 2, 0x00},
+	{ 0xA65 << 2, 0x00},
+	{ 0xA66 << 2, 0x00},
+	{ 0xA67 << 2, 0x00},
+	{ 0xA68 << 2, 0x00},
+	{ 0xA69 << 2, 0x00},
+	{ 0xA6A << 2, 0x00},
+	{ 0xA6B << 2, 0x00},
+	{ 0xA6C << 2, 0x00},
+	{ 0xA6D << 2, 0x00},
+	{ 0xA6E << 2, 0x00},
+	{ 0xA6F << 2, 0x00},
+	{ 0xA70 << 2, 0x00},
+	{ 0xA71 << 2, 0x00},
+	{ 0xA72 << 2, 0x00},
+	{ 0xA73 << 2, 0x00},
+	{ 0xA74 << 2, 0x00},
+	{ 0xA75 << 2, 0x00},
+	{ 0xA76 << 2, 0x00},
+	{ 0xA77 << 2, 0x00},
+	{ 0xA78 << 2, 0x00},
+	{ 0xA79 << 2, 0x00},
+	{ 0xA7A << 2, 0x00},
+	{ 0xA7B << 2, 0x00},
+	{ 0xA7C << 2, 0x00},
+	{ 0xA7D << 2, 0x00},
+	{ 0xA7E << 2, 0x00},
+	{ 0xA7F << 2, 0xd8},
+	{ 0xA80 << 2, 0x1a},
+	{ 0xA81 << 2, 0xff},
+	{ 0xA82 << 2, 0x11},
+	{ 0xA83 << 2, 0x00},
+	{ 0xA84 << 2, 0x00},
+	{ 0xA85 << 2, 0x00},
+	{ 0xA86 << 2, 0x00},
+	{ 0xA87 << 2, 0xf0},
+	{ 0xA88 << 2, 0xff},
+	{ 0xA89 << 2, 0xff},
+	{ 0xA8A << 2, 0xff},
+	{ 0xA8B << 2, 0xff},
+	{ 0xA8C << 2, 0x1c},
+	{ 0xA8D << 2, 0xc2},
+	{ 0xA8E << 2, 0xc3},
+	{ 0xA8F << 2, 0x3f},
+	{ 0xA90 << 2, 0x0a},
+	{ 0xA91 << 2, 0x00},
+	{ 0xA92 << 2, 0x00},
+	{ 0xA93 << 2, 0x00},
+	{ 0xA94 << 2, 0x00},
+	{ 0xA95 << 2, 0x00},
+	{ 0xA96 << 2, 0xf8},
+	{ 0x000 << 2, 0x07}
+};
+
+/* 24MHz internal ref clock configuration */
+static struct serdes_regs_s sata_phy_reg_file_24[] = {
+	{ 0x000 << 2, 0x06},
+	{ 0x001 << 2, 0x00},
+	{ 0x002 << 2, 0x88},
+	{ 0x003 << 2, 0x00},
+	{ 0x004 << 2, 0x7b},
+	{ 0x005 << 2, 0xc9},
+	{ 0x006 << 2, 0x03},
+	{ 0x007 << 2, 0x00},
+	{ 0x008 << 2, 0x00},
+	{ 0x009 << 2, 0x00},
+	{ 0x00A << 2, 0x00},
+	{ 0x00B << 2, 0x00},
+	{ 0x00C << 2, 0x00},
+	{ 0x00D << 2, 0x00},
+	{ 0x00E << 2, 0x00},
+	{ 0x00F << 2, 0x00},
+	{ 0x010 << 2, 0x00},
+	{ 0x011 << 2, 0x00},
+	{ 0x012 << 2, 0x00},
+	{ 0x013 << 2, 0x00},
+	{ 0x014 << 2, 0x00},
+	{ 0x015 << 2, 0x00},
+	{ 0x016 << 2, 0x00},
+	{ 0x017 << 2, 0x00},
+	{ 0x018 << 2, 0x00},
+	{ 0x019 << 2, 0x00},
+	{ 0x01A << 2, 0x00},
+	{ 0x01B << 2, 0x00},
+	{ 0x01C << 2, 0x00},
+	{ 0x01D << 2, 0x00},
+	{ 0x01E << 2, 0x00},
+	{ 0x01F << 2, 0x00},
+	{ 0x020 << 2, 0x00},
+	{ 0x021 << 2, 0x00},
+	{ 0x022 << 2, 0xa0},
+	{ 0x023 << 2, 0x54},
+	{ 0x024 << 2, 0x00},
+	{ 0x025 << 2, 0x00},
+	{ 0x026 << 2, 0x00},
+	{ 0x027 << 2, 0x00},
+	{ 0x028 << 2, 0x00},
+	{ 0x029 << 2, 0x00},
+	{ 0x02A << 2, 0x00},
+	{ 0x02B << 2, 0x00},
+	{ 0x02C << 2, 0x00},
+	{ 0x02D << 2, 0x00},
+	{ 0x02E << 2, 0x04},
+	{ 0x02F << 2, 0x50},
+	{ 0x030 << 2, 0x70},
+	{ 0x031 << 2, 0x02},
+	{ 0x032 << 2, 0x25},
+	{ 0x033 << 2, 0x40},
+	{ 0x034 << 2, 0x01},
+	{ 0x035 << 2, 0x40},
+	{ 0x036 << 2, 0x00},
+	{ 0x037 << 2, 0x00},
+	{ 0x038 << 2, 0x00},
+	{ 0x039 << 2, 0x00},
+	{ 0x03A << 2, 0x00},
+	{ 0x03B << 2, 0x00},
+	{ 0x03C << 2, 0x00},
+	{ 0x03D << 2, 0x00},
+	{ 0x03E << 2, 0x00},
+	{ 0x03F << 2, 0x00},
+	{ 0x040 << 2, 0x00},
+	{ 0x041 << 2, 0x00},
+	{ 0x042 << 2, 0x00},
+	{ 0x043 << 2, 0x00},
+	{ 0x044 << 2, 0x00},
+	{ 0x045 << 2, 0x00},
+	{ 0x046 << 2, 0x00},
+	{ 0x047 << 2, 0x00},
+	{ 0x048 << 2, 0x00},
+	{ 0x049 << 2, 0x00},
+	{ 0x04A << 2, 0x00},
+	{ 0x04B << 2, 0x00},
+	{ 0x04C << 2, 0x00},
+	{ 0x04D << 2, 0x00},
+	{ 0x04E << 2, 0x00},
+	{ 0x04F << 2, 0x00},
+	{ 0x050 << 2, 0x00},
+	{ 0x051 << 2, 0x00},
+	{ 0x052 << 2, 0x00},
+	{ 0x053 << 2, 0x00},
+	{ 0x054 << 2, 0x00},
+	{ 0x055 << 2, 0x00},
+	{ 0x056 << 2, 0x00},
+	{ 0x057 << 2, 0x00},
+	{ 0x058 << 2, 0x00},
+	{ 0x059 << 2, 0x00},
+	{ 0x05A << 2, 0x00},
+	{ 0x05B << 2, 0x00},
+	{ 0x05C << 2, 0x00},
+	{ 0x05D << 2, 0x00},
+	{ 0x05E << 2, 0x00},
+	{ 0x05F << 2, 0x00},
+	{ 0x060 << 2, 0x00},
+	//{ 0x061 << 2, 0x2e}, //for Rev-A0 device
+	{ 0x061 << 2, 0x2e}, //for Rev-A1 device
+	{ 0x062 << 2, 0x00},
+	{ 0x063 << 2, 0x5e},
+	{ 0x064 << 2, 0x00},
+	{ 0x065 << 2, 0x42},
+	{ 0x066 << 2, 0xd1},
+	{ 0x067 << 2, 0x20},
+	{ 0x068 << 2, 0x28},
+	{ 0x069 << 2, 0x78},
+	{ 0x06A << 2, 0x2c},
+	{ 0x06B << 2, 0xb9},
+	{ 0x06C << 2, 0x5e},
+	{ 0x06D << 2, 0x03},
+	{ 0x06E << 2, 0x00},
+	{ 0x06F << 2, 0x00},
+	{ 0x070 << 2, 0x00},
+	{ 0x071 << 2, 0x00},
+	{ 0x072 << 2, 0x00},
+	{ 0x200 << 2, 0x02},
+	{ 0x201 << 2, 0x00},
+	{ 0x202 << 2, 0x00},
+	{ 0x203 << 2, 0x00},
+	{ 0x204 << 2, 0x00},
+	{ 0x205 << 2, 0x10},
+	{ 0x206 << 2, 0x84},
+	{ 0x207 << 2, 0x3c},
+	{ 0x208 << 2, 0xe0},
+	{ 0x210 << 2, 0x23},
+	{ 0x211 << 2, 0x00},
+	{ 0x212 << 2, 0x40},
+	{ 0x213 << 2, 0x05},
+	{ 0x214 << 2, 0xd0},/* */
+	{ 0x215 << 2, 0x17},
+	{ 0x216 << 2, 0x00},
+	{ 0x217 << 2, 0x68},
+	{ 0x218 << 2, 0xf2},
+	{ 0x219 << 2, 0x1e},
+	{ 0x21A << 2, 0x18},
+	{ 0x21B << 2, 0x0d},
+	{ 0x21C << 2, 0x0c},
+	{ 0x21D << 2, 0x00},
+	{ 0x21E << 2, 0x00},
+	{ 0x21F << 2, 0x00},
+	{ 0x220 << 2, 0x00},
+	{ 0x221 << 2, 0x00},
+	{ 0x222 << 2, 0x00},
+	{ 0x223 << 2, 0x00},
+	{ 0x224 << 2, 0x00},
+	{ 0x225 << 2, 0x00},
+	{ 0x226 << 2, 0x00},
+	{ 0x227 << 2, 0x00},
+	{ 0x228 << 2, 0x00},
+	{ 0x229 << 2, 0x00},
+	{ 0x22A << 2, 0x00},
+	{ 0x22B << 2, 0x00},
+	{ 0x22C << 2, 0x00},
+	{ 0x22D << 2, 0x00},
+	{ 0x22E << 2, 0x00},
+	{ 0x22F << 2, 0x00},
+	{ 0x230 << 2, 0x00},
+	{ 0x231 << 2, 0x00},
+	{ 0x232 << 2, 0x00},
+	{ 0x233 << 2, 0x00},
+	{ 0x234 << 2, 0x00},
+	{ 0x235 << 2, 0x00},
+	{ 0x236 << 2, 0x00},
+	{ 0x237 << 2, 0x00},
+	{ 0x238 << 2, 0x00},
+	{ 0x239 << 2, 0x00},
+	{ 0x23A << 2, 0x00},
+	{ 0x23B << 2, 0x00},
+	{ 0x23C << 2, 0x00},
+	{ 0x23D << 2, 0x00},
+	{ 0x23E << 2, 0x00},
+	{ 0x23F << 2, 0x00},
+	{ 0x240 << 2, 0x00},
+	{ 0x241 << 2, 0x00},
+	{ 0x242 << 2, 0x00},
+	{ 0x243 << 2, 0x00},
+	{ 0x244 << 2, 0x00},
+	{ 0x245 << 2, 0x00},
+	{ 0x246 << 2, 0x00},
+	{ 0x247 << 2, 0x00},
+	{ 0x248 << 2, 0x00},
+	{ 0x249 << 2, 0x00},
+	{ 0x24A << 2, 0x00},
+	{ 0x24B << 2, 0x00},
+	{ 0x24C << 2, 0x00},
+	{ 0x24D << 2, 0x00},
+	{ 0x24E << 2, 0x00},
+	{ 0x24F << 2, 0x00},
+	{ 0x250 << 2, 0x60},
+	{ 0x251 << 2, 0x0f},
+	{ 0xA00 << 2, 0x00},
+	{ 0xA01 << 2, 0x20},
+	{ 0xA02 << 2, 0x00},
+	{ 0xA03 << 2, 0x40},
+	{ 0xA04 << 2, 0x24},
+	{ 0xA05 << 2, 0xae},
+	{ 0xA06 << 2, 0x19},
+	{ 0xA07 << 2, 0x49},
+	{ 0xA08 << 2, 0x04},
+	{ 0xA09 << 2, 0x83},
+	{ 0xA0A << 2, 0x4b},
+	{ 0xA0B << 2, 0xc5},
+	{ 0xA0C << 2, 0x01},
+	{ 0xA0D << 2, 0x03},
+	{ 0xA0E << 2, 0x28},
+	{ 0xA0F << 2, 0x98},
+	{ 0xA10 << 2, 0x19},
+	{ 0xA11 << 2, 0x00},
+	{ 0xA12 << 2, 0x00},
+	{ 0xA13 << 2, 0x80},
+	{ 0xA14 << 2, 0xf0},
+	{ 0xA15 << 2, 0xd0},
+	{ 0xA16 << 2, 0x00},
+	{ 0xA17 << 2, 0x00},
+	{ 0xA30 << 2, 0x00},
+	{ 0xA31 << 2, 0x00},
+	{ 0xA32 << 2, 0x00},
+	{ 0xA33 << 2, 0x00},
+	{ 0xA34 << 2, 0x00},
+	{ 0xA35 << 2, 0x00},
+	{ 0xA36 << 2, 0x00},
+	{ 0xA37 << 2, 0x00},
+	{ 0xA38 << 2, 0x00},
+	{ 0xA39 << 2, 0xa0},
+	{ 0xA3A << 2, 0xa0},
+	{ 0xA3B << 2, 0xa0},
+	{ 0xA3C << 2, 0xa0},
+	{ 0xA3D << 2, 0xa0},
+	{ 0xA3E << 2, 0xa0},
+	{ 0xA3F << 2, 0x54},
+	{ 0xA40 << 2, 0x62},
+	{ 0xA41 << 2, 0x00},
+	{ 0xA42 << 2, 0x80},
+	{ 0xA43 << 2, 0x58},
+	{ 0xA44 << 2, 0x00},
+	{ 0xA45 << 2, 0x44},
+	{ 0xA46 << 2, 0x5c},
+	{ 0xA47 << 2, 0x86},
+	{ 0xA48 << 2, 0x8d},
+	{ 0xA49 << 2, 0xd0},
+	{ 0xA4A << 2, 0x09},
+	{ 0xA4B << 2, 0x90},
+	{ 0xA4C << 2, 0x07},
+	{ 0xA4D << 2, 0x40},
+	{ 0xA4E << 2, 0x00},
+	{ 0xA4F << 2, 0x00},
+	{ 0xA50 << 2, 0x00},
+	{ 0xA51 << 2, 0x20},
+	{ 0xA52 << 2, 0x32},
+	{ 0xA53 << 2, 0x00},
+	{ 0xA54 << 2, 0x00},
+	{ 0xA55 << 2, 0x00},
+	{ 0xA56 << 2, 0x00},
+	{ 0xA57 << 2, 0x00},
+	{ 0xA58 << 2, 0x00},
+	{ 0xA59 << 2, 0x00},
+	{ 0xA5A << 2, 0x00},
+	{ 0xA5B << 2, 0x00},
+	{ 0xA5C << 2, 0x00},
+	{ 0xA5D << 2, 0x00},
+	{ 0xA5E << 2, 0x00},
+	{ 0xA5F << 2, 0x00},
+	{ 0xA60 << 2, 0x00},
+	{ 0xA61 << 2, 0x00},
+	{ 0xA62 << 2, 0x00},
+	{ 0xA63 << 2, 0x00},
+	{ 0xA64 << 2, 0x00},
+	{ 0xA65 << 2, 0x00},
+	{ 0xA66 << 2, 0x00},
+	{ 0xA67 << 2, 0x00},
+	{ 0xA68 << 2, 0x00},
+	{ 0xA69 << 2, 0x00},
+	{ 0xA6A << 2, 0x00},
+	{ 0xA6B << 2, 0x00},
+	{ 0xA6C << 2, 0x00},
+	{ 0xA6D << 2, 0x00},
+	{ 0xA6E << 2, 0x00},
+	{ 0xA6F << 2, 0x00},
+	{ 0xA70 << 2, 0x00},
+	{ 0xA71 << 2, 0x00},
+	{ 0xA72 << 2, 0x00},
+	{ 0xA73 << 2, 0x00},
+	{ 0xA74 << 2, 0x00},
+	{ 0xA75 << 2, 0x00},
+	{ 0xA76 << 2, 0x00},
+	{ 0xA77 << 2, 0x00},
+	{ 0xA78 << 2, 0x00},
+	{ 0xA79 << 2, 0x00},
+	{ 0xA7A << 2, 0x00},
+	{ 0xA7B << 2, 0x00},
+	{ 0xA7C << 2, 0x00},
+	{ 0xA7D << 2, 0x00},
+	{ 0xA7E << 2, 0x00},
+	{ 0xA7F << 2, 0xd8},
+	{ 0xA80 << 2, 0x1a},
+	{ 0xA81 << 2, 0xff},
+	{ 0xA82 << 2, 0x11},
+	{ 0xA83 << 2, 0x00},
+	{ 0xA84 << 2, 0x00},
+	{ 0xA85 << 2, 0x00},
+	{ 0xA86 << 2, 0x00},
+	{ 0xA87 << 2, 0xf0},
+	{ 0xA88 << 2, 0xff},
+	{ 0xA89 << 2, 0xff},
+	{ 0xA8A << 2, 0xff},
+	{ 0xA8B << 2, 0xff},
+	{ 0xA8C << 2, 0x1c},
+	{ 0xA8D << 2, 0xc2},
+	{ 0xA8E << 2, 0xc3},
+	{ 0xA8F << 2, 0x3f},
+	{ 0xA90 << 2, 0x0a},
+	{ 0xA91 << 2, 0x00},
+	{ 0xA92 << 2, 0x00},
+	{ 0xA93 << 2, 0x00},
+	{ 0xA94 << 2, 0x00},
+	{ 0xA95 << 2, 0x00},
+	{ 0xA96 << 2, 0xf8},
+	{ 0x000 << 2, 0x07}
+};
+
+/* PCIe SERDES is using internal clock */
+static struct serdes_regs_s pcie_phy_reg_file_48[] __maybe_unused = {
+	/* Common CMU block */
+	{ 0x000 << 2, 0x06},
+	{ 0x001 << 2, 0x00},
+	{ 0x002 << 2, 0x09},
+	{ 0x003 << 2, 0x00},
+	{ 0x004 << 2, 0x60},
+	{ 0x005 << 2, 0x09},
+	{ 0x006 << 2, 0x0e},
+	{ 0x007 << 2, 0x00},
+	{ 0x008 << 2, 0x00},
+	{ 0x009 << 2, 0x00},
+	{ 0x00A << 2, 0x00},
+	{ 0x00B << 2, 0x00},
+	{ 0x00C << 2, 0x00},
+	{ 0x00D << 2, 0x00},
+	{ 0x00E << 2, 0x00},
+	{ 0x00F << 2, 0x00},
+	{ 0x010 << 2, 0x00},
+	{ 0x011 << 2, 0x00},
+	{ 0x012 << 2, 0x00},
+	{ 0x013 << 2, 0x00},
+	{ 0x014 << 2, 0x00},
+	{ 0x015 << 2, 0x00},
+	{ 0x016 << 2, 0x00},
+	{ 0x017 << 2, 0x00},
+	{ 0x018 << 2, 0x00},
+	{ 0x019 << 2, 0x00},
+	{ 0x01A << 2, 0x00},
+	{ 0x01B << 2, 0x00},
+	{ 0x01C << 2, 0x00},
+	{ 0x01D << 2, 0x00},
+	{ 0x01E << 2, 0x00},
+	{ 0x01F << 2, 0x00},
+	{ 0x020 << 2, 0x00},
+	{ 0x021 << 2, 0x00},
+	{ 0x022 << 2, 0xa0},
+	{ 0x023 << 2, 0x6c},
+	{ 0x024 << 2, 0x00},
+	{ 0x025 << 2, 0x00},
+	{ 0x026 << 2, 0x00},
+	{ 0x027 << 2, 0x00},
+	{ 0x028 << 2, 0x00},
+	{ 0x029 << 2, 0x00},
+	{ 0x02A << 2, 0x00},
+	{ 0x02B << 2, 0x00},
+	{ 0x02C << 2, 0x00},
+	{ 0x02D << 2, 0x00},
+	{ 0x02E << 2, 0x04},
+	{ 0x02F << 2, 0x50},
+	{ 0x030 << 2, 0x70},
+	{ 0x031 << 2, 0x02},
+	{ 0x032 << 2, 0x25},
+	{ 0x033 << 2, 0x40},
+	{ 0x034 << 2, 0x01},
+	{ 0x035 << 2, 0x40},
+	{ 0x036 << 2, 0x00},
+	{ 0x037 << 2, 0x00},
+	{ 0x038 << 2, 0x00},
+	{ 0x039 << 2, 0x00},
+	{ 0x03A << 2, 0x00},
+	{ 0x03B << 2, 0x00},
+	{ 0x03C << 2, 0x00},
+	{ 0x03D << 2, 0x00},
+	{ 0x03E << 2, 0x00},
+	{ 0x03F << 2, 0x00},
+	{ 0x040 << 2, 0x00},
+	{ 0x041 << 2, 0x00},
+	{ 0x042 << 2, 0x00},
+	{ 0x043 << 2, 0x00},
+	{ 0x044 << 2, 0x00},
+	{ 0x045 << 2, 0x00},
+	{ 0x046 << 2, 0x00},
+	{ 0x047 << 2, 0x00},
+	{ 0x048 << 2, 0x00},
+	{ 0x049 << 2, 0x00},
+	{ 0x04A << 2, 0x00},
+	{ 0x04B << 2, 0x00},
+	{ 0x04C << 2, 0x00},
+	{ 0x04D << 2, 0x00},
+	{ 0x04E << 2, 0x00},
+	{ 0x04F << 2, 0x00},
+	{ 0x050 << 2, 0x00},
+	{ 0x051 << 2, 0x00},
+	{ 0x052 << 2, 0x00},
+	{ 0x053 << 2, 0x00},
+	{ 0x054 << 2, 0x00},
+	{ 0x055 << 2, 0x00},
+	{ 0x056 << 2, 0x00},
+	{ 0x057 << 2, 0x00},
+	{ 0x058 << 2, 0x00},
+	{ 0x059 << 2, 0x00},
+	{ 0x05A << 2, 0x00},
+	{ 0x05B << 2, 0x00},
+	{ 0x05C << 2, 0x00},
+	{ 0x05D << 2, 0x00},
+	{ 0x05E << 2, 0x00},
+	{ 0x05F << 2, 0x00},
+	{ 0x060 << 2, 0x00},
+	{ 0x061 << 2, 0x2e},
+	{ 0x062 << 2, 0x08},
+	{ 0x063 << 2, 0x5e},
+	{ 0x064 << 2, 0x00},
+	{ 0x065 << 2, 0x42},
+	{ 0x066 << 2, 0xd1},
+	{ 0x067 << 2, 0x90},
+	{ 0x068 << 2, 0x08},
+	{ 0x069 << 2, 0x90},
+	{ 0x06A << 2, 0x2c},
+	{ 0x06B << 2, 0x32},
+	{ 0x06C << 2, 0x59},
+	{ 0x06D << 2, 0x03},
+	{ 0x06E << 2, 0x00},
+	{ 0x06F << 2, 0x00},
+	{ 0x070 << 2, 0x00},
+	{ 0x071 << 2, 0x00},
+	{ 0x072 << 2, 0x00},
+	/* Lane0 Block */
+	{ 0x200 << 2, 0x00},
+	{ 0x201 << 2, 0x00},
+	{ 0x202 << 2, 0x00},
+	{ 0x203 << 2, 0x00},
+	{ 0x204 << 2, 0x00},
+	{ 0x205 << 2, 0x10},
+	{ 0x206 << 2, 0x84},
+	{ 0x207 << 2, 0x04},
+	{ 0x208 << 2, 0xe0},
+	{ 0x210 << 2, 0x23},
+	{ 0x211 << 2, 0x00},
+	{ 0x212 << 2, 0x00},
+	{ 0x213 << 2, 0x04},
+	{ 0x214 << 2, 0xc0},
+	{ 0x215 << 2, 0x18},
+	{ 0x216 << 2, 0x00},
+	{ 0x217 << 2, 0x68},
+	{ 0x218 << 2, 0xa2},
+	{ 0x219 << 2, 0x1e},
+	{ 0x21A << 2, 0x18},
+	{ 0x21B << 2, 0x0d},
+	{ 0x21C << 2, 0x0d},
+	{ 0x21D << 2, 0x00},
+	{ 0x21E << 2, 0x00},
+	{ 0x21F << 2, 0x00},
+	{ 0x220 << 2, 0x00},
+	{ 0x221 << 2, 0x00},
+	{ 0x222 << 2, 0x00},
+	{ 0x223 << 2, 0x00},
+	{ 0x224 << 2, 0x00},
+	{ 0x225 << 2, 0x00},
+	{ 0x226 << 2, 0x00},
+	{ 0x227 << 2, 0x00},
+	{ 0x228 << 2, 0x00},
+	{ 0x229 << 2, 0x00},
+	{ 0x22A << 2, 0x00},
+	{ 0x22B << 2, 0x00},
+	{ 0x22C << 2, 0x00},
+	{ 0x22D << 2, 0x00},
+	{ 0x22E << 2, 0x00},
+	{ 0x22F << 2, 0x00},
+	{ 0x230 << 2, 0x00},
+	{ 0x231 << 2, 0x00},
+	{ 0x232 << 2, 0x00},
+	{ 0x233 << 2, 0x00},
+	{ 0x234 << 2, 0x00},
+	{ 0x235 << 2, 0x00},
+	{ 0x236 << 2, 0x00},
+	{ 0x237 << 2, 0x00},
+	{ 0x238 << 2, 0x00},
+	{ 0x239 << 2, 0x00},
+	{ 0x23A << 2, 0x00},
+	{ 0x23B << 2, 0x00},
+	{ 0x23C << 2, 0x00},
+	{ 0x23D << 2, 0x00},
+	{ 0x23E << 2, 0x00},
+	{ 0x23F << 2, 0x00},
+	{ 0x240 << 2, 0x00},
+	{ 0x241 << 2, 0x00},
+	{ 0x242 << 2, 0x00},
+	{ 0x243 << 2, 0x00},
+	{ 0x244 << 2, 0x00},
+	{ 0x245 << 2, 0x00},
+	{ 0x246 << 2, 0x00},
+	{ 0x247 << 2, 0x00},
+	{ 0x248 << 2, 0x00},
+	{ 0x249 << 2, 0x00},
+	{ 0x24A << 2, 0x00},
+	{ 0x24B << 2, 0x00},
+	{ 0x24C << 2, 0x00},
+	{ 0x24D << 2, 0x00},
+	{ 0x24E << 2, 0x00},
+	{ 0x24F << 2, 0x00},
+	{ 0x250 << 2, 0x60},
+	{ 0x251 << 2, 0x0f},
+	/* Common Lane Block */
+	{ 0xA00 << 2, 0xc0},
+	{ 0xA01 << 2, 0x90},
+	{ 0xA02 << 2, 0x02},
+	{ 0xA03 << 2, 0x40},
+	{ 0xA04 << 2, 0x3c},
+	{ 0xA05 << 2, 0x00},
+	{ 0xA06 << 2, 0x00},
+	{ 0xA07 << 2, 0x00},
+	{ 0xA08 << 2, 0x00},
+	{ 0xA09 << 2, 0xc3},
+	{ 0xA0A << 2, 0xca},
+	{ 0xA0B << 2, 0xc6},
+	{ 0xA0C << 2, 0x01},
+	{ 0xA0D << 2, 0x03},
+	{ 0xA0E << 2, 0x28},
+	{ 0xA0F << 2, 0x98},
+	{ 0xA10 << 2, 0x19},
+	{ 0xA11 << 2, 0x28},
+	{ 0xA12 << 2, 0x78},
+	{ 0xA13 << 2, 0xe1},
+	{ 0xA14 << 2, 0xf0},
+	{ 0xA15 << 2, 0x10},
+	{ 0xA16 << 2, 0xf4},
+	{ 0xA17 << 2, 0x00},
+	{ 0xA30 << 2, 0x00},
+	{ 0xA31 << 2, 0x00},
+	{ 0xA32 << 2, 0x00},
+	{ 0xA33 << 2, 0x00},
+	{ 0xA34 << 2, 0x00},
+	{ 0xA35 << 2, 0x00},
+	{ 0xA36 << 2, 0x00},
+	{ 0xA37 << 2, 0x00},
+	{ 0xA38 << 2, 0x00},
+	{ 0xA39 << 2, 0xa0},
+	{ 0xA3A << 2, 0xa0},
+	{ 0xA3B << 2, 0xa0},
+	{ 0xA3C << 2, 0xa0},
+	{ 0xA3D << 2, 0xa0},
+	{ 0xA3E << 2, 0xa0},
+	{ 0xA3F << 2, 0xa0},
+	{ 0xA40 << 2, 0x6c},
+	{ 0xA41 << 2, 0x00},
+	{ 0xA42 << 2, 0xc0},
+	{ 0xA43 << 2, 0x9f},
+	{ 0xA44 << 2, 0x01},
+	{ 0xA45 << 2, 0x00},
+	{ 0xA46 << 2, 0x00},
+	{ 0xA47 << 2, 0x00},
+	{ 0xA48 << 2, 0x00},
+	{ 0xA49 << 2, 0x00},
+	{ 0xA4A << 2, 0x00},
+	{ 0xA4B << 2, 0x00},
+	{ 0xA4C << 2, 0x30},
+	{ 0xA4D << 2, 0x41},
+	{ 0xA4E << 2, 0x7e},
+	{ 0xA4F << 2, 0xd0},
+	{ 0xA50 << 2, 0xcc},
+	{ 0xA51 << 2, 0x85},
+	{ 0xA52 << 2, 0x52},
+	{ 0xA53 << 2, 0x93},
+	{ 0xA54 << 2, 0xe0},
+	{ 0xA55 << 2, 0x49},
+	{ 0xA56 << 2, 0xdd},
+	{ 0xA57 << 2, 0xb0},
+	{ 0xA58 << 2, 0x0b},
+	{ 0xA59 << 2, 0x02},
+	{ 0xA5A << 2, 0x00},
+	{ 0xA5B << 2, 0x00},
+	{ 0xA5C << 2, 0x00},
+	{ 0xA5D << 2, 0x00},
+	{ 0xA5E << 2, 0x00},
+	{ 0xA5F << 2, 0x00},
+	{ 0xA60 << 2, 0x00},
+	{ 0xA61 << 2, 0x00},
+	{ 0xA62 << 2, 0x00},
+	{ 0xA63 << 2, 0x00},
+	{ 0xA64 << 2, 0x00},
+	{ 0xA65 << 2, 0x00},
+	{ 0xA66 << 2, 0x00},
+	{ 0xA67 << 2, 0x00},
+	{ 0xA68 << 2, 0x00},
+	{ 0xA69 << 2, 0x00},
+	{ 0xA6A << 2, 0x00},
+	{ 0xA6B << 2, 0x00},
+	{ 0xA6C << 2, 0x00},
+	{ 0xA6D << 2, 0x00},
+	{ 0xA6E << 2, 0x00},
+	{ 0xA6F << 2, 0x00},
+	{ 0xA70 << 2, 0x00},
+	{ 0xA71 << 2, 0x00},
+	{ 0xA72 << 2, 0x00},
+	{ 0xA73 << 2, 0x00},
+	{ 0xA74 << 2, 0x00},
+	{ 0xA75 << 2, 0x00},
+	{ 0xA76 << 2, 0x00},
+	{ 0xA77 << 2, 0x00},
+	{ 0xA78 << 2, 0x00},
+	{ 0xA79 << 2, 0x00},
+	{ 0xA7A << 2, 0x00},
+	{ 0xA7B << 2, 0x00},
+	{ 0xA7C << 2, 0x00},
+	{ 0xA7D << 2, 0x00},
+	{ 0xA7E << 2, 0x00},
+	{ 0xA7F << 2, 0xd8},
+	{ 0xA80 << 2, 0x1a},
+	{ 0xA81 << 2, 0xff},
+	{ 0xA82 << 2, 0x01},
+	{ 0xA83 << 2, 0x00},
+	{ 0xA84 << 2, 0x00},
+	{ 0xA85 << 2, 0x00},
+	{ 0xA86 << 2, 0x00},
+	{ 0xA87 << 2, 0xf0},
+	{ 0xA88 << 2, 0xff},
+	{ 0xA89 << 2, 0xff},
+	{ 0xA8A << 2, 0xff},
+	{ 0xA8B << 2, 0xff},
+	{ 0xA8C << 2, 0x1c},
+	{ 0xA8D << 2, 0xc2},
+	{ 0xA8E << 2, 0xc3},
+	{ 0xA8F << 2, 0x3f},
+	{ 0xA90 << 2, 0x0a},
+	{ 0xA91 << 2, 0x00},
+	{ 0xA92 << 2, 0x00},
+	{ 0xA93 << 2, 0x00},
+	{ 0xA94 << 2, 0x00},
+	{ 0xA95 << 2, 0x00},
+	{ 0xA96 << 2, 0xf8},
+	{ 0x000 << 2, 0x07}
+};
+
+static struct serdes_regs_s pcie_phy_reg_file_100[] __maybe_unused = {
+	/* Common CMU block */
+	{ 0x000 << 2, 0x06},
+	{ 0x001 << 2, 0x00},
+	{ 0x002 << 2, 0x09},
+	{ 0x003 << 2, 0x00},
+	{ 0x004 << 2, 0x00},
+	{ 0x005 << 2, 0x00},
+	{ 0x006 << 2, 0x00},
+	{ 0x007 << 2, 0x00},
+	{ 0x008 << 2, 0x00},
+	{ 0x009 << 2, 0x00},
+	{ 0x00A << 2, 0x00},
+	{ 0x00B << 2, 0x00},
+	{ 0x00C << 2, 0x00},
+	{ 0x00D << 2, 0x00},
+	{ 0x00E << 2, 0x00},
+	{ 0x00F << 2, 0x00},
+	{ 0x010 << 2, 0x00},
+	{ 0x011 << 2, 0x00},
+	{ 0x012 << 2, 0x00},
+	{ 0x013 << 2, 0x00},
+	{ 0x014 << 2, 0x00},
+	{ 0x015 << 2, 0x00},
+	{ 0x016 << 2, 0x00},
+	{ 0x017 << 2, 0x00},
+	{ 0x018 << 2, 0x00},
+	{ 0x019 << 2, 0x00},
+	{ 0x01A << 2, 0x00},
+	{ 0x01B << 2, 0x00},
+	{ 0x01C << 2, 0x00},
+	{ 0x01D << 2, 0x00},
+	{ 0x01E << 2, 0x00},
+	{ 0x01F << 2, 0x00},
+	{ 0x020 << 2, 0x00},
+	{ 0x021 << 2, 0x00},
+	{ 0x022 << 2, 0xa0},
+	{ 0x023 << 2, 0x64},
+	{ 0x024 << 2, 0x00},
+	{ 0x025 << 2, 0x00},
+	{ 0x026 << 2, 0x00},
+	{ 0x027 << 2, 0x00},
+	{ 0x028 << 2, 0x00},
+	{ 0x029 << 2, 0x00},
+	{ 0x02A << 2, 0x00},
+	{ 0x02B << 2, 0x00},
+	{ 0x02C << 2, 0x00},
+	{ 0x02D << 2, 0x00},
+	{ 0x02E << 2, 0x04},
+	{ 0x02F << 2, 0x50},
+	{ 0x030 << 2, 0x70},
+	{ 0x031 << 2, 0x02},
+	{ 0x032 << 2, 0x25},
+	{ 0x033 << 2, 0x40},
+	{ 0x034 << 2, 0x01},
+	{ 0x035 << 2, 0x40},
+	{ 0x036 << 2, 0x00},
+	{ 0x037 << 2, 0x00},
+	{ 0x038 << 2, 0x00},
+	{ 0x039 << 2, 0x00},
+	{ 0x03A << 2, 0x00},
+	{ 0x03B << 2, 0x00},
+	{ 0x03C << 2, 0x00},
+	{ 0x03D << 2, 0x00},
+	{ 0x03E << 2, 0x00},
+	{ 0x03F << 2, 0x00},
+	{ 0x040 << 2, 0x00},
+	{ 0x041 << 2, 0x00},
+	{ 0x042 << 2, 0x00},
+	{ 0x043 << 2, 0x00},
+	{ 0x044 << 2, 0x00},
+	{ 0x045 << 2, 0x00},
+	{ 0x046 << 2, 0x00},
+	{ 0x047 << 2, 0x00},
+	{ 0x048 << 2, 0x00},
+	{ 0x049 << 2, 0x00},
+	{ 0x04A << 2, 0x00},
+	{ 0x04B << 2, 0x00},
+	{ 0x04C << 2, 0x00},
+	{ 0x04D << 2, 0x00},
+	{ 0x04E << 2, 0x00},
+	{ 0x04F << 2, 0x00},
+	{ 0x050 << 2, 0x00},
+	{ 0x051 << 2, 0x00},
+	{ 0x052 << 2, 0x00},
+	{ 0x053 << 2, 0x00},
+	{ 0x054 << 2, 0x00},
+	{ 0x055 << 2, 0x00},
+	{ 0x056 << 2, 0x00},
+	{ 0x057 << 2, 0x00},
+	{ 0x058 << 2, 0x00},
+	{ 0x059 << 2, 0x00},
+	{ 0x05A << 2, 0x00},
+	{ 0x05B << 2, 0x00},
+	{ 0x05C << 2, 0x00},
+	{ 0x05D << 2, 0x00},
+	{ 0x05E << 2, 0x00},
+	{ 0x05F << 2, 0x00},
+	{ 0x060 << 2, 0x00},
+	{ 0x061 << 2, 0x2e},//for Rev1 modified in code
+	{ 0x062 << 2, 0x00},
+	{ 0x063 << 2, 0x5e},
+	{ 0x064 << 2, 0x00},
+	{ 0x065 << 2, 0x42},
+	{ 0x066 << 2, 0xd1},
+	{ 0x067 << 2, 0x90},
+	{ 0x068 << 2, 0x08},
+	{ 0x069 << 2, 0x50},
+	{ 0x06A << 2, 0x44},
+	{ 0x06B << 2, 0xce},
+	{ 0x06C << 2, 0x0b},
+	{ 0x06D << 2, 0x00},
+	{ 0x06E << 2, 0x00},
+	{ 0x06F << 2, 0x00},
+	{ 0x070 << 2, 0x00},
+	{ 0x071 << 2, 0x00},
+	{ 0x072 << 2, 0x00},
+	/* Lane0 Block */
+	{ 0x200 << 2, 0x00},
+	{ 0x201 << 2, 0x00},
+	{ 0x202 << 2, 0x00},
+	{ 0x203 << 2, 0x00},
+	{ 0x204 << 2, 0x00},
+	{ 0x205 << 2, 0x10},
+	{ 0x206 << 2, 0x04},
+	{ 0x207 << 2, 0x18},
+	{ 0x208 << 2, 0xe0},
+	{ 0x210 << 2, 0x23},
+	{ 0x211 << 2, 0x00},
+	{ 0x212 << 2, 0x00},
+	{ 0x213 << 2, 0x03},
+	{ 0x214 << 2, 0x3C},
+	{ 0x215 << 2, 0x04},
+	{ 0x216 << 2, 0x00},
+	{ 0x217 << 2, 0x68},
+	{ 0x218 << 2, 0xa2},
+	{ 0x219 << 2, 0x1e},
+	{ 0x21A << 2, 0x18},
+	{ 0x21B << 2, 0x0d},
+	{ 0x21C << 2, 0x0d},
+	{ 0x21D << 2, 0x00},
+	{ 0x21E << 2, 0x00},
+	{ 0x21F << 2, 0x00},
+	{ 0x220 << 2, 0x00},
+	{ 0x221 << 2, 0x00},
+	{ 0x222 << 2, 0x00},
+	{ 0x223 << 2, 0x00},
+	{ 0x224 << 2, 0x00},
+	{ 0x225 << 2, 0x00},
+	{ 0x226 << 2, 0x00},
+	{ 0x227 << 2, 0x00},
+	{ 0x228 << 2, 0x00},
+	{ 0x229 << 2, 0x00},
+	{ 0x22A << 2, 0x00},
+	{ 0x22B << 2, 0x00},
+	{ 0x22C << 2, 0x00},
+	{ 0x22D << 2, 0x00},
+	{ 0x22E << 2, 0x00},
+	{ 0x22F << 2, 0x00},
+	{ 0x230 << 2, 0x00},
+	{ 0x231 << 2, 0x00},
+	{ 0x232 << 2, 0x00},
+	{ 0x233 << 2, 0x00},
+	{ 0x234 << 2, 0x00},
+	{ 0x235 << 2, 0x00},
+	{ 0x236 << 2, 0x00},
+	{ 0x237 << 2, 0x00},
+	{ 0x238 << 2, 0x00},
+	{ 0x239 << 2, 0x00},
+	{ 0x23A << 2, 0x00},
+	{ 0x23B << 2, 0x00},
+	{ 0x23C << 2, 0x00},
+	{ 0x23D << 2, 0x00},
+	{ 0x23E << 2, 0x00},
+	{ 0x23F << 2, 0x00},
+	{ 0x240 << 2, 0x00},
+	{ 0x241 << 2, 0x00},
+	{ 0x242 << 2, 0x00},
+	{ 0x243 << 2, 0x00},
+	{ 0x244 << 2, 0x00},
+	{ 0x245 << 2, 0x00},
+	{ 0x246 << 2, 0x00},
+	{ 0x247 << 2, 0x00},
+	{ 0x248 << 2, 0x00},
+	{ 0x249 << 2, 0x00},
+	{ 0x24A << 2, 0x00},
+	{ 0x24B << 2, 0x00},
+	{ 0x24C << 2, 0x00},
+	{ 0x24D << 2, 0x00},
+	{ 0x24E << 2, 0x00},
+	{ 0x24F << 2, 0x00},
+	{ 0x250 << 2, 0xf6},
+	{ 0x251 << 2, 0x03},
+	/* Common Lane Block */
+	{ 0xA00 << 2, 0xc0},
+	{ 0xA01 << 2, 0x90},
+	{ 0xA02 << 2, 0x02},
+	{ 0xA03 << 2, 0x40},
+	{ 0xA04 << 2, 0x3c},
+	{ 0xA05 << 2, 0x00},
+	{ 0xA06 << 2, 0x00},
+	{ 0xA07 << 2, 0x00},
+	{ 0xA08 << 2, 0x00},
+	{ 0xA09 << 2, 0x63},
+	{ 0xA0A << 2, 0x49},
+	{ 0xA0B << 2, 0xc6},
+	{ 0xA0C << 2, 0x01},
+	{ 0xA0D << 2, 0x03},
+	{ 0xA0E << 2, 0x28},
+	{ 0xA0F << 2, 0x98},
+	{ 0xA10 << 2, 0x19},
+	{ 0xA11 << 2, 0x28},
+	{ 0xA12 << 2, 0x78},
+	{ 0xA13 << 2, 0xe1},
+	{ 0xA14 << 2, 0xf0},
+	{ 0xA15 << 2, 0x10},
+	{ 0xA16 << 2, 0xf4},
+	{ 0xA17 << 2, 0x00},
+	{ 0xA30 << 2, 0x00},
+	{ 0xA31 << 2, 0x00},
+	{ 0xA32 << 2, 0x00},
+	{ 0xA33 << 2, 0x00},
+	{ 0xA34 << 2, 0x00},
+	{ 0xA35 << 2, 0x00},
+	{ 0xA36 << 2, 0x00},
+	{ 0xA37 << 2, 0x00},
+	{ 0xA38 << 2, 0x00},
+	{ 0xA39 << 2, 0xa0},
+	{ 0xA3A << 2, 0xa0},
+	{ 0xA3B << 2, 0xa0},
+	{ 0xA3C << 2, 0xa0},
+	{ 0xA3D << 2, 0xa0},
+	{ 0xA3E << 2, 0xa0},
+	{ 0xA3F << 2, 0xa0},
+	{ 0xA40 << 2, 0x64},
+	{ 0xA41 << 2, 0x00},
+	{ 0xA42 << 2, 0xc0},
+	{ 0xA43 << 2, 0x9f},
+	{ 0xA44 << 2, 0x01},
+	{ 0xA45 << 2, 0x00},
+	{ 0xA46 << 2, 0x00},
+	{ 0xA47 << 2, 0x00},
+	{ 0xA48 << 2, 0x00},
+	{ 0xA49 << 2, 0x00},
+	{ 0xA4A << 2, 0x00},
+	{ 0xA4B << 2, 0x00},
+	{ 0xA4C << 2, 0x30},
+	{ 0xA4D << 2, 0x41},
+	{ 0xA4E << 2, 0x7e},
+	{ 0xA4F << 2, 0xd0},
+	{ 0xA50 << 2, 0xcc},
+	{ 0xA51 << 2, 0x85},
+	{ 0xA52 << 2, 0x52},
+	{ 0xA53 << 2, 0x93},
+	{ 0xA54 << 2, 0xe0},
+	{ 0xA55 << 2, 0x49},
+	{ 0xA56 << 2, 0xdd},
+	{ 0xA57 << 2, 0xb0},
+	{ 0xA58 << 2, 0x0b},
+	{ 0xA59 << 2, 0x02},
+	{ 0xA5A << 2, 0x00},
+	{ 0xA5B << 2, 0x00},
+	{ 0xA5C << 2, 0x00},
+	{ 0xA5D << 2, 0x00},
+	{ 0xA5E << 2, 0x00},
+	{ 0xA5F << 2, 0x00},
+	{ 0xA60 << 2, 0x00},
+	{ 0xA61 << 2, 0x00},
+	{ 0xA62 << 2, 0x00},
+	{ 0xA63 << 2, 0x00},
+	{ 0xA64 << 2, 0x00},
+	{ 0xA65 << 2, 0x00},
+	{ 0xA66 << 2, 0x00},
+	{ 0xA67 << 2, 0x00},
+	{ 0xA68 << 2, 0x00},
+	{ 0xA69 << 2, 0x00},
+	{ 0xA6A << 2, 0x00},
+	{ 0xA6B << 2, 0x00},
+	{ 0xA6C << 2, 0x00},
+	{ 0xA6D << 2, 0x00},
+	{ 0xA6E << 2, 0x00},
+	{ 0xA6F << 2, 0x00},
+	{ 0xA70 << 2, 0x00},
+	{ 0xA71 << 2, 0x00},
+	{ 0xA72 << 2, 0x00},
+	{ 0xA73 << 2, 0x00},
+	{ 0xA74 << 2, 0x00},
+	{ 0xA75 << 2, 0x00},
+	{ 0xA76 << 2, 0x00},
+	{ 0xA77 << 2, 0x00},
+	{ 0xA78 << 2, 0x00},
+	{ 0xA79 << 2, 0x00},
+	{ 0xA7A << 2, 0x00},
+	{ 0xA7B << 2, 0x00},
+	{ 0xA7C << 2, 0x00},
+	{ 0xA7D << 2, 0x00},
+	{ 0xA7E << 2, 0x00},
+	{ 0xA7F << 2, 0xd8},
+	{ 0xA80 << 2, 0x1a},
+	{ 0xA81 << 2, 0xff},
+	{ 0xA82 << 2, 0x01},
+	{ 0xA83 << 2, 0x00},
+	{ 0xA84 << 2, 0x00},
+	{ 0xA85 << 2, 0x00},
+	{ 0xA86 << 2, 0x00},
+	{ 0xA87 << 2, 0xf0},
+	{ 0xA88 << 2, 0xff},
+	{ 0xA89 << 2, 0xff},
+	{ 0xA8A << 2, 0xff},
+	{ 0xA8B << 2, 0xff},
+	{ 0xA8C << 2, 0x1c},
+	{ 0xA8D << 2, 0xc2},
+	{ 0xA8E << 2, 0xc3},
+	{ 0xA8F << 2, 0x3f},
+	{ 0xA90 << 2, 0x0a},
+	{ 0xA91 << 2, 0x00},
+	{ 0xA92 << 2, 0x00},
+	{ 0xA93 << 2, 0x00},
+	{ 0xA94 << 2, 0x00},
+	{ 0xA95 << 2, 0x00},
+	{ 0xA96 << 2, 0xf8},
+	{ 0x000 << 2, 0x07}
+};
+
+
+/* PCIe SERDES is using internal clock */
+static struct serdes_regs_s pcie_phy_reg_file_24[] = {
+	/* Common CMU block */
+	{ 0x000 << 2, 0x06},
+	{ 0x001 << 2, 0x00},
+	{ 0x002 << 2, 0x09},
+	{ 0x003 << 2, 0x00},
+	{ 0x004 << 2, 0x60},
+	{ 0x005 << 2, 0x09},
+	{ 0x006 << 2, 0x0e},
+	{ 0x007 << 2, 0x00},
+	{ 0x008 << 2, 0x00},
+	{ 0x009 << 2, 0x00},
+	{ 0x00A << 2, 0x00},
+	{ 0x00B << 2, 0x00},
+	{ 0x00C << 2, 0x00},
+	{ 0x00D << 2, 0x00},
+	{ 0x00E << 2, 0x00},
+	{ 0x00F << 2, 0x00},
+	{ 0x010 << 2, 0x00},
+	{ 0x011 << 2, 0x00},
+	{ 0x012 << 2, 0x00},
+	{ 0x013 << 2, 0x00},
+	{ 0x014 << 2, 0x00},
+	{ 0x015 << 2, 0x00},
+	{ 0x016 << 2, 0x00},
+	{ 0x017 << 2, 0x00},
+	{ 0x018 << 2, 0x00},
+	{ 0x019 << 2, 0x00},
+	{ 0x01A << 2, 0x00},
+	{ 0x01B << 2, 0x00},
+	{ 0x01C << 2, 0x00},
+	{ 0x01D << 2, 0x00},
+	{ 0x01E << 2, 0x00},
+	{ 0x01F << 2, 0x00},
+	{ 0x020 << 2, 0x00},
+	{ 0x021 << 2, 0x00},
+	{ 0x022 << 2, 0xa0},
+	{ 0x023 << 2, 0x68},
+	{ 0x024 << 2, 0x00},
+	{ 0x025 << 2, 0x00},
+	{ 0x026 << 2, 0x00},
+	{ 0x027 << 2, 0x00},
+	{ 0x028 << 2, 0x00},
+	{ 0x029 << 2, 0x00},
+	{ 0x02A << 2, 0x00},
+	{ 0x02B << 2, 0x00},
+	{ 0x02C << 2, 0x00},
+	{ 0x02D << 2, 0x00},
+	{ 0x02E << 2, 0x04},
+	{ 0x02F << 2, 0x50},
+	{ 0x030 << 2, 0x70},
+	{ 0x031 << 2, 0x02},
+	{ 0x032 << 2, 0x25},
+	{ 0x033 << 2, 0x40},
+	{ 0x034 << 2, 0x01},
+	{ 0x035 << 2, 0x40},
+	{ 0x036 << 2, 0x00},
+	{ 0x037 << 2, 0x00},
+	{ 0x038 << 2, 0x00},
+	{ 0x039 << 2, 0x00},
+	{ 0x03A << 2, 0x00},
+	{ 0x03B << 2, 0x00},
+	{ 0x03C << 2, 0x00},
+	{ 0x03D << 2, 0x00},
+	{ 0x03E << 2, 0x00},
+	{ 0x03F << 2, 0x00},
+	{ 0x040 << 2, 0x00},
+	{ 0x041 << 2, 0x00},
+	{ 0x042 << 2, 0x00},
+	{ 0x043 << 2, 0x00},
+	{ 0x044 << 2, 0x00},
+	{ 0x045 << 2, 0x00},
+	{ 0x046 << 2, 0x00},
+	{ 0x047 << 2, 0x00},
+	{ 0x048 << 2, 0x00},
+	{ 0x049 << 2, 0x00},
+	{ 0x04A << 2, 0x00},
+	{ 0x04B << 2, 0x00},
+	{ 0x04C << 2, 0x00},
+	{ 0x04D << 2, 0x00},
+	{ 0x04E << 2, 0x00},
+	{ 0x04F << 2, 0x00},
+	{ 0x050 << 2, 0x00},
+	{ 0x051 << 2, 0x00},
+	{ 0x052 << 2, 0x00},
+	{ 0x053 << 2, 0x00},
+	{ 0x054 << 2, 0x00},
+	{ 0x055 << 2, 0x00},
+	{ 0x056 << 2, 0x00},
+	{ 0x057 << 2, 0x00},
+	{ 0x058 << 2, 0x00},
+	{ 0x059 << 2, 0x00},
+	{ 0x05A << 2, 0x00},
+	{ 0x05B << 2, 0x00},
+	{ 0x05C << 2, 0x00},
+	{ 0x05D << 2, 0x00},
+	{ 0x05E << 2, 0x00},
+	{ 0x05F << 2, 0x00},
+	{ 0x060 << 2, 0x00},
+	{ 0x061 << 2, 0x2e}, //for Rev-A0 device
+	{ 0x062 << 2, 0x00},
+	{ 0x063 << 2, 0x5e},
+	{ 0x064 << 2, 0x00},
+	{ 0x065 << 2, 0x42},
+	{ 0x066 << 2, 0x91},
+	{ 0x067 << 2, 0x10},
+	{ 0x068 << 2, 0x48},
+	{ 0x069 << 2, 0x90},
+	{ 0x06A << 2, 0x0c},
+	{ 0x06B << 2, 0x4c},
+	{ 0x06C << 2, 0x73},
+	{ 0x06D << 2, 0x03},
+	{ 0x06E << 2, 0x00},
+	{ 0x06F << 2, 0x00},
+	{ 0x070 << 2, 0x00},
+	{ 0x071 << 2, 0x00},
+	{ 0x072 << 2, 0x00},
+	/* Lane0 Block */
+	{ 0x200 << 2, 0x00},
+	{ 0x201 << 2, 0x00},
+	{ 0x202 << 2, 0x00},
+	{ 0x203 << 2, 0x00},
+	{ 0x204 << 2, 0x00},
+	{ 0x205 << 2, 0x10},
+	{ 0x206 << 2, 0x04},
+	{ 0x207 << 2, 0x18},
+	{ 0x208 << 2, 0xe0},
+	{ 0x210 << 2, 0x23},
+	{ 0x211 << 2, 0x00},
+	{ 0x212 << 2, 0x00},
+	{ 0x213 << 2, 0x04},
+	{ 0x214 << 2, 0x38},
+	{ 0x215 << 2, 0x10},
+	{ 0x216 << 2, 0x00},
+	{ 0x217 << 2, 0x68},
+	{ 0x218 << 2, 0xa2},
+	{ 0x219 << 2, 0x1e},
+	{ 0x21A << 2, 0x18},
+	{ 0x21B << 2, 0x0d},
+	{ 0x21C << 2, 0x0c},
+	{ 0x21D << 2, 0x00},
+	{ 0x21E << 2, 0x00},
+	{ 0x21F << 2, 0x00},
+	{ 0x220 << 2, 0x00},
+	{ 0x221 << 2, 0x00},
+	{ 0x222 << 2, 0x00},
+	{ 0x223 << 2, 0x00},
+	{ 0x224 << 2, 0x00},
+	{ 0x225 << 2, 0x00},
+	{ 0x226 << 2, 0x00},
+	{ 0x227 << 2, 0x00},
+	{ 0x228 << 2, 0x00},
+	{ 0x229 << 2, 0x00},
+	{ 0x22A << 2, 0x00},
+	{ 0x22B << 2, 0x00},
+	{ 0x22C << 2, 0x00},
+	{ 0x22D << 2, 0x00},
+	{ 0x22E << 2, 0x00},
+	{ 0x22F << 2, 0x00},
+	{ 0x230 << 2, 0x00},
+	{ 0x231 << 2, 0x00},
+	{ 0x232 << 2, 0x00},
+	{ 0x233 << 2, 0x00},
+	{ 0x234 << 2, 0x00},
+	{ 0x235 << 2, 0x00},
+	{ 0x236 << 2, 0x00},
+	{ 0x237 << 2, 0x00},
+	{ 0x238 << 2, 0x00},
+	{ 0x239 << 2, 0x00},
+	{ 0x23A << 2, 0x00},
+	{ 0x23B << 2, 0x00},
+	{ 0x23C << 2, 0x00},
+	{ 0x23D << 2, 0x00},
+	{ 0x23E << 2, 0x00},
+	{ 0x23F << 2, 0x00},
+	{ 0x240 << 2, 0x00},
+	{ 0x241 << 2, 0x00},
+	{ 0x242 << 2, 0x00},
+	{ 0x243 << 2, 0x00},
+	{ 0x244 << 2, 0x00},
+	{ 0x245 << 2, 0x00},
+	{ 0x246 << 2, 0x00},
+	{ 0x247 << 2, 0x00},
+	{ 0x248 << 2, 0x00},
+	{ 0x249 << 2, 0x00},
+	{ 0x24A << 2, 0x00},
+	{ 0x24B << 2, 0x00},
+	{ 0x24C << 2, 0x00},
+	{ 0x24D << 2, 0x00},
+	{ 0x24E << 2, 0x00},
+	{ 0x24F << 2, 0x00},
+	{ 0x250 << 2, 0xf6},
+	{ 0x251 << 2, 0x03},
+	/* Common Lane Block */
+	{ 0xA00 << 2, 0xc0},
+	{ 0xA01 << 2, 0x90},
+	{ 0xA02 << 2, 0x02},
+	{ 0xA03 << 2, 0x40},
+	{ 0xA04 << 2, 0x3c},
+	{ 0xA05 << 2, 0x00},
+	{ 0xA06 << 2, 0x00},
+	{ 0xA07 << 2, 0x00},
+	{ 0xA08 << 2, 0x00},
+	{ 0xA09 << 2, 0x83},
+	{ 0xA0A << 2, 0x8b},
+	{ 0xA0B << 2, 0xc6},
+	{ 0xA0C << 2, 0x01},
+	{ 0xA0D << 2, 0x03},
+	{ 0xA0E << 2, 0x28},
+	{ 0xA0F << 2, 0x98},
+	{ 0xA10 << 2, 0x19},
+	{ 0xA11 << 2, 0x28},
+	{ 0xA12 << 2, 0x78},
+	{ 0xA13 << 2, 0xe1},
+	{ 0xA14 << 2, 0xf0},
+	{ 0xA15 << 2, 0x10},
+	{ 0xA16 << 2, 0xf4},
+	{ 0xA17 << 2, 0x00},
+	{ 0xA30 << 2, 0x00},
+	{ 0xA31 << 2, 0x00},
+	{ 0xA32 << 2, 0x00},
+	{ 0xA33 << 2, 0x00},
+	{ 0xA34 << 2, 0x00},
+	{ 0xA35 << 2, 0x00},
+	{ 0xA36 << 2, 0x00},
+	{ 0xA37 << 2, 0x00},
+	{ 0xA38 << 2, 0x00},
+	{ 0xA39 << 2, 0xa0},
+	{ 0xA3A << 2, 0xa0},
+	{ 0xA3B << 2, 0xa0},
+	{ 0xA3C << 2, 0xa0},
+	{ 0xA3D << 2, 0xa0},
+	{ 0xA3E << 2, 0xa0},
+	{ 0xA3F << 2, 0xa0},
+	{ 0xA40 << 2, 0x68},
+	{ 0xA41 << 2, 0x00},
+	{ 0xA42 << 2, 0xc0},
+	{ 0xA43 << 2, 0x9f},
+	{ 0xA44 << 2, 0x01},
+	{ 0xA45 << 2, 0x00},
+	{ 0xA46 << 2, 0x00},
+	{ 0xA47 << 2, 0x00},
+	{ 0xA48 << 2, 0x00},
+	{ 0xA49 << 2, 0x00},
+	{ 0xA4A << 2, 0x00},
+	{ 0xA4B << 2, 0x00},
+	{ 0xA4C << 2, 0x30},
+	{ 0xA4D << 2, 0x41},
+	{ 0xA4E << 2, 0x7e},
+	{ 0xA4F << 2, 0xd0},
+	{ 0xA50 << 2, 0xcc},
+	{ 0xA51 << 2, 0x85},
+	{ 0xA52 << 2, 0x52},
+	{ 0xA53 << 2, 0x93},
+	{ 0xA54 << 2, 0xe0},
+	{ 0xA55 << 2, 0x49},
+	{ 0xA56 << 2, 0xdd},
+	{ 0xA57 << 2, 0xb0},
+	{ 0xA58 << 2, 0x0b},
+	{ 0xA59 << 2, 0x02},
+	{ 0xA5A << 2, 0x00},
+	{ 0xA5B << 2, 0x00},
+	{ 0xA5C << 2, 0x00},
+	{ 0xA5D << 2, 0x00},
+	{ 0xA5E << 2, 0x00},
+	{ 0xA5F << 2, 0x00},
+	{ 0xA60 << 2, 0x00},
+	{ 0xA61 << 2, 0x00},
+	{ 0xA62 << 2, 0x00},
+	{ 0xA63 << 2, 0x00},
+	{ 0xA64 << 2, 0x00},
+	{ 0xA65 << 2, 0x00},
+	{ 0xA66 << 2, 0x00},
+	{ 0xA67 << 2, 0x00},
+	{ 0xA68 << 2, 0x00},
+	{ 0xA69 << 2, 0x00},
+	{ 0xA6A << 2, 0x00},
+	{ 0xA6B << 2, 0x00},
+	{ 0xA6C << 2, 0x00},
+	{ 0xA6D << 2, 0x00},
+	{ 0xA6E << 2, 0x00},
+	{ 0xA6F << 2, 0x00},
+	{ 0xA70 << 2, 0x00},
+	{ 0xA71 << 2, 0x00},
+	{ 0xA72 << 2, 0x00},
+	{ 0xA73 << 2, 0x00},
+	{ 0xA74 << 2, 0x00},
+	{ 0xA75 << 2, 0x00},
+	{ 0xA76 << 2, 0x00},
+	{ 0xA77 << 2, 0x00},
+	{ 0xA78 << 2, 0x00},
+	{ 0xA79 << 2, 0x00},
+	{ 0xA7A << 2, 0x00},
+	{ 0xA7B << 2, 0x00},
+	{ 0xA7C << 2, 0x00},
+	{ 0xA7D << 2, 0x00},
+	{ 0xA7E << 2, 0x00},
+	{ 0xA7F << 2, 0xd8},
+	{ 0xA80 << 2, 0x1a},
+	{ 0xA81 << 2, 0xff},
+	{ 0xA82 << 2, 0x01},
+	{ 0xA83 << 2, 0x00},
+	{ 0xA84 << 2, 0x00},
+	{ 0xA85 << 2, 0x00},
+	{ 0xA86 << 2, 0x00},
+	{ 0xA87 << 2, 0xf0},
+	{ 0xA88 << 2, 0xff},
+	{ 0xA89 << 2, 0xff},
+	{ 0xA8A << 2, 0xff},
+	{ 0xA8B << 2, 0xff},
+	{ 0xA8C << 2, 0x1c},
+	{ 0xA8D << 2, 0xc2},
+	{ 0xA8E << 2, 0xc3},
+	{ 0xA8F << 2, 0x3f},
+	{ 0xA90 << 2, 0x0a},
+	{ 0xA91 << 2, 0x00},
+	{ 0xA92 << 2, 0x00},
+	{ 0xA93 << 2, 0x00},
+	{ 0xA94 << 2, 0x00},
+	{ 0xA95 << 2, 0x00},
+	{ 0xA96 << 2, 0xf8},
+	{ 0x000 << 2, 0x07}
+};
diff --git a/drivers/phy/phy-snowbush.c b/drivers/phy/phy-snowbush.c
new file mode 100644
index 0000000..fdc76a4
--- /dev/null
+++ b/drivers/phy/phy-snowbush.c
@@ -0,0 +1,409 @@
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/phy/phy.h>
+#include <linux/sched.h>
+#include <linux/reset.h>
+
+#include <dt-bindings/phy/phy.h>
+
+#include "phy-snowbush-regs.h"
+
+struct snowbush_phy {
+	struct phy *phy;
+	void __iomem *base;
+	void __iomem *dwc1_serdes_cfg_base;
+	struct reset_control *reset;
+	struct reset_control *pcie_rst;
+	struct reset_control *sata_rst;
+	bool pcie_tx_pol_inv;
+	bool sata_tx_pol_inv;
+	u32 sata_gen;
+	u32 ctrlreg;
+	u8 type;
+};
+
+struct snowbush_dev {
+	struct device *dev;
+	struct regmap *regmap;
+	struct mutex sbphy_mutex;
+	struct snowbush_phy **phys;
+	int nphys;
+};
+
+
+#define SD_COMMON_LANE    0xA00
+
+#define SD_PHY_STS_REG_OFST		0x00
+#define SD_PHY_CTRL1_REG_OFST		0x04
+#define SD_PHY_CTRL2_REG_OFST		0x08
+#define SD_PHY_CTRL3_REG_OFST		0x0C
+
+#define MAX_LANE_OK_WAIT_JIFFIES	(200 * HZ) / 1000    /* 200ms */
+#define MAX_CMU_OK_WAIT_JIFFIES		(2000 * HZ) / 1000   /* 2 Seconds */
+
+/**
+ * This function Wait for the 'Lane OK' to be signaled by the
+ * Snowbush Serdes PHY.
+ * @param sbphy_num	SerDes PHY intefrace number.
+ */
+static int wait_lane_ok(struct snowbush_phy *sbphy_phy,
+				    struct snowbush_dev *sbphy_dev)
+{
+	u32 rd_data = 0, masked_data = 0;
+	u32 lane_ok_dtctd_mask = 0x00001000;
+	unsigned long deadline = jiffies + MAX_LANE_OK_WAIT_JIFFIES;
+
+	/* Keep looping until you see the lane_ok_o of Serdes */
+	do
+	{
+		rd_data = readl(sbphy_phy->dwc1_serdes_cfg_base + SD_PHY_STS_REG_OFST);
+
+		/* Mask lane_ok Status */
+		masked_data = rd_data & lane_ok_dtctd_mask;
+
+		if(masked_data == lane_ok_dtctd_mask) {
+			/* Lane OK Detected on Serdes Port */
+			dev_info(&sbphy_phy->phy->dev, "Serdes: Lane OK passed\n");
+			return 1;
+		}
+
+		cond_resched();
+
+	} while (!time_after_eq(jiffies, deadline));
+
+	dev_warn(&sbphy_phy->phy->dev, "Serdes: Lane OK failed\n");
+	return 0;
+}
+
+
+/**
+ * This function wait for the 'CMU OK' to be signaled by the
+ * Snowbush Serdes PHY.
+ * @param sbphy_num	SerDes PHY intefrace number.
+ */
+static int wait_cmu_ok(struct snowbush_phy *sbphy_phy,
+				    struct snowbush_dev *sbphy_dev)
+{
+	u32 rd_data = 0, masked_data = 0;
+	u32 cmu_ok_dtctd_mask = 0x00004000;
+	volatile void __iomem *CMU_Offset;
+	unsigned long deadline = jiffies + MAX_CMU_OK_WAIT_JIFFIES;
+
+	CMU_Offset = sbphy_phy->dwc1_serdes_cfg_base + SD_PHY_STS_REG_OFST;
+
+	/* Keep looping until you see the cmu_ok_o of Serdes */
+	do
+	{
+		rd_data = readl(CMU_Offset);
+
+		/* Mask cmu_ok Status */
+		masked_data = rd_data & cmu_ok_dtctd_mask;
+
+		if(masked_data == cmu_ok_dtctd_mask) {
+			/* CMU OK Detected on Serdes Port */
+			dev_info(&sbphy_phy->phy->dev, "Serdes: CMU OK Passed\n");
+			return 1;
+		}
+
+		cond_resched();
+
+	} while (!time_after_eq(jiffies, deadline));
+
+	dev_warn(&sbphy_phy->phy->dev, "Serdes: CMU OK failed\n");
+
+	return 0;
+}
+
+
+/**
+ * This function wait for the specified configured Snowbush PHY
+ * (Serdes) to issue it's CMU-OK, and it's Lane to become Ready
+ * after releasing the CMU & Lane resets.
+ * @param sbphy_num	SerDes PHY intefrace number.
+ */
+static int wait_sb_cmu_lane_rdy(struct snowbush_phy *sbphy_phy,
+				    struct snowbush_dev *sbphy_dev)
+{
+	volatile void __iomem *sd_ctl2_reg_offset;
+	u32 cmu_rst_mask = 0x00010000;
+	u32 lane_rst_mask = 0x00000040;
+	u32 tmp = 0;
+
+	sd_ctl2_reg_offset =
+		sbphy_phy->dwc1_serdes_cfg_base + SD_PHY_CTRL2_REG_OFST;
+
+	/* Releasing the CMU Reset */
+	tmp = readl(sd_ctl2_reg_offset);
+	tmp = tmp & (~cmu_rst_mask);
+	tmp = tmp | cmu_rst_mask;
+
+	writel(tmp, sd_ctl2_reg_offset );
+
+	/* Waiting for CMU OK */
+	if( !wait_cmu_ok(sbphy_phy, sbphy_dev) )
+		return -1;
+
+	if (sbphy_phy->type == PHY_TYPE_PCIE)
+		writel(0xC3, sbphy_phy->base + (SD_COMMON_LANE << 2));
+	else
+		writel(0x03, sbphy_phy->base + (SD_COMMON_LANE << 2));
+
+	/* Releasing the Lane Reset */
+	tmp = readl(sd_ctl2_reg_offset);
+	tmp = tmp & (~lane_rst_mask);
+	tmp = tmp | lane_rst_mask;
+
+	writel(tmp, sd_ctl2_reg_offset);
+
+	/* Waiting for the Lane Ready */
+	if (sbphy_phy->type != PHY_TYPE_PCIE) {
+		if( !wait_lane_ok(sbphy_phy, sbphy_dev) )
+			return -1;
+	}
+
+	return 0;
+}
+
+
+/**
+ * This function initialize the Snowbush PHY (Serdes) for operation
+ * with the one of the PCIE,SATA or SGMII IP blocks, and then waiting
+ * until it issue it's CMU-OK, and it's  Lane to become Ready after
+ * releasing the CMU & Lane Resets.
+ * @param phy_num	SerDes PHY intefrace number.
+ * @param *regs		Register file (Array of registers and coresponding
+ *                      values to be programmed).
+ * @param size		Number of registers to be programmed.
+ */
+static int serdes_phy_init(struct snowbush_phy *sbphy_phy,
+			    struct snowbush_dev *sbphy_dev)
+{
+	int i, size, ret;
+	struct serdes_regs_s *regs;
+
+	static int misc = 0;
+	ret = reset_control_deassert(sbphy_phy->reset);
+	if (ret)
+		return ret;
+
+	if (sbphy_phy->type == PHY_TYPE_PCIE && sbphy_phy->pcie_rst) {
+		ret = reset_control_deassert(sbphy_phy->pcie_rst);
+		if (ret)
+			return ret;
+	}
+	if (sbphy_phy->type == PHY_TYPE_SATA && sbphy_phy->sata_rst) {
+		ret = reset_control_deassert(sbphy_phy->sata_rst);
+		if (ret)
+			return ret;
+	}
+	if (!misc) {
+		/* Move SATA controller to DDRC2 port */
+		writel(readl((void*) 0xF097006C) | 0x2, (void*) 0xF097006C); // TODO:COMCERTO_GPIO_FABRIC_CTRL_REG
+
+		writel(readl((void*) 0xF09B0058) & ~4, (void*) 0xF09B0058); // AXI_RESET_2 TODO: SATA_AXI
+
+		writel(readl((void*) 0xF09B0048) | 3, (void*) 0xF09B0048); // AXI_CLK_CNTRL_2
+		writel(readl((void*) 0xF09B0048) | 4, (void*) 0xF09B0048); // AXI_CLK_CNTRL_2: SATA
+
+		writel(readl((void*) 0xF09B0168) & ~1, (void*) 0xF09B0168); // TODO: SATA_PMU
+		writel(readl((void*) 0xF09B0178) & ~1, (void*) 0xF09B0178); // TODO: SATA_OOB
+
+		misc = 1;
+	}
+
+
+	if (sbphy_phy->type == PHY_TYPE_PCIE) {
+		/* SW select for ck_soc_div_i SOC clock */
+		writel(0xFF3C, sbphy_phy->dwc1_serdes_cfg_base + SD_PHY_CTRL3_REG_OFST);
+		writel(readl(sbphy_phy->dwc1_serdes_cfg_base + SD_PHY_CTRL2_REG_OFST) & ~0x3,
+				sbphy_phy->dwc1_serdes_cfg_base + SD_PHY_CTRL2_REG_OFST);
+	}
+
+	if (sbphy_phy->type == PHY_TYPE_SATA) {
+		regs = sata_phy_reg_file_24; /* TODO: Support for 48MHz crystal */
+		size = ARRAY_SIZE(sata_phy_reg_file_24);
+	} else if (sbphy_phy->type == PHY_TYPE_PCIE) {
+		regs = pcie_phy_reg_file_24;
+		size = ARRAY_SIZE(pcie_phy_reg_file_24);
+		if (1) { // (system_rev == 1) { /* TODO */
+			// C2K RevA1 devices use a different serdes clk divider
+			regs[0x61].val = 0x6;
+		}
+
+	} else {
+		BUG();
+	}
+
+	/* Initilize serdes phy registers */
+	for(i = 0; i < size; i++ )
+		writel(regs[i].val, sbphy_phy->base + regs[i].ofst);
+
+	/* Wait for the initialization of Serdes-1 Port/Lane to become Ready */
+	return wait_sb_cmu_lane_rdy(sbphy_phy, sbphy_dev);
+}
+
+static int snowbush_init(struct phy *phy)
+{
+	struct snowbush_phy *sbphy_phy = phy_get_drvdata(phy);
+	struct snowbush_dev *sbphy_dev = dev_get_drvdata(phy->dev.parent);
+	int ret = 0;
+
+	mutex_lock(&sbphy_dev->sbphy_mutex);
+	serdes_phy_init(sbphy_phy, sbphy_dev);
+	mutex_unlock(&sbphy_dev->sbphy_mutex);
+
+	return ret;
+}
+
+static struct phy *snowbush_xlate(struct device *dev,
+				   struct of_phandle_args *args)
+{
+	struct snowbush_dev *sbphy_dev = dev_get_drvdata(dev);
+	struct snowbush_phy *sbphy_phy = NULL;
+	struct device_node *phynode = args->np;
+	int index;
+
+	if (!of_device_is_available(phynode)) {
+		dev_warn(dev, "Requested PHY is disabled\n");
+		return ERR_PTR(-ENODEV);
+	}
+
+	if (args->args_count != 1) {
+		dev_err(dev, "Invalid number of cells in 'phy' property\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	for (index = 0; index < sbphy_dev->nphys; index++)
+		if (phynode == sbphy_dev->phys[index]->phy->dev.of_node) {
+			sbphy_phy = sbphy_dev->phys[index];
+			break;
+		}
+
+	if (!sbphy_phy) {
+		dev_err(dev, "Failed to find appropriate phy\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	sbphy_phy->type = args->args[0];
+
+	if (!(sbphy_phy->type == PHY_TYPE_SATA ||
+	      sbphy_phy->type == PHY_TYPE_PCIE)) {
+		dev_err(dev, "Unsupported device type: %d\n", sbphy_phy->type);
+		return ERR_PTR(-EINVAL);
+	}
+
+	return sbphy_phy->phy;
+}
+
+static struct phy_ops snowbush_ops = {
+	.init		= snowbush_init,
+	.owner		= THIS_MODULE,
+};
+
+static int snowbush_of_probe(struct device_node *phynode,
+			      struct snowbush_phy *sbphy_phy)
+{
+	sbphy_phy->base = of_iomap(phynode, 0);
+	if (!sbphy_phy->base) {
+		dev_err(&sbphy_phy->phy->dev, "Failed to map %s\n", phynode->full_name);
+		return -EINVAL;
+	}
+	sbphy_phy->dwc1_serdes_cfg_base = of_iomap(phynode, 1);
+	if (!sbphy_phy->dwc1_serdes_cfg_base) {
+		dev_err(&sbphy_phy->phy->dev, "Failed to map %s dwc1_serdes_cfg_base\n", phynode->full_name);
+		return -EINVAL;
+	}
+	sbphy_phy->reset = devm_reset_control_get(&sbphy_phy->phy->dev, "serdes");
+	if (IS_ERR(sbphy_phy->reset)) {
+		dev_err(&sbphy_phy->phy->dev, "failed to get reset control\n");
+		return PTR_ERR(sbphy_phy->reset);
+	}
+	sbphy_phy->pcie_rst = devm_reset_control_get(&sbphy_phy->phy->dev, "serdes_pcie");
+	if (IS_ERR(sbphy_phy->pcie_rst)) {
+		sbphy_phy->pcie_rst = 0;
+	}
+	sbphy_phy->sata_rst = devm_reset_control_get(&sbphy_phy->phy->dev, "serdes_sata");
+	if (IS_ERR(sbphy_phy->sata_rst)) {
+		sbphy_phy->sata_rst = 0;
+	}
+	/* TODO */
+	return 0;
+}
+
+static int snowbush_probe(struct platform_device *pdev)
+{
+	struct device_node *child, *np = pdev->dev.of_node;
+	struct snowbush_dev *sbphy_dev;
+	struct phy_provider *provider;
+	struct phy *phy;
+	int ret, port = 0;
+
+	sbphy_dev = devm_kzalloc(&pdev->dev, sizeof(*sbphy_dev), GFP_KERNEL);
+	if (!sbphy_dev)
+		return -ENOMEM;
+
+	sbphy_dev->nphys = of_get_child_count(np);
+	sbphy_dev->phys = devm_kcalloc(&pdev->dev, sbphy_dev->nphys,
+				       sizeof(*sbphy_dev->phys), GFP_KERNEL);
+	if (!sbphy_dev->phys)
+		return -ENOMEM;
+
+	sbphy_dev->dev = &pdev->dev;
+
+	dev_set_drvdata(&pdev->dev, sbphy_dev);
+
+	mutex_init(&sbphy_dev->sbphy_mutex);
+
+	for_each_child_of_node(np, child) {
+		struct snowbush_phy *sbphy_phy;
+
+		sbphy_phy = devm_kzalloc(&pdev->dev, sizeof(*sbphy_phy),
+					 GFP_KERNEL);
+		if (!sbphy_phy)
+			return -ENOMEM;
+
+		sbphy_dev->phys[port] = sbphy_phy;
+
+		phy = devm_phy_create(&pdev->dev, child, &snowbush_ops);
+		if (IS_ERR(phy)) {
+			dev_err(&pdev->dev, "failed to create PHY\n");
+			return PTR_ERR(phy);
+		}
+
+		sbphy_dev->phys[port]->phy = phy;
+
+		ret = snowbush_of_probe(child, sbphy_phy);
+		if (ret)
+			return ret;
+
+		phy_set_drvdata(phy, sbphy_dev->phys[port]);
+
+		port++;
+	}
+
+	provider = devm_of_phy_provider_register(&pdev->dev, snowbush_xlate);
+	return PTR_ERR_OR_ZERO(provider);
+}
+
+static const struct of_device_id snowbush_of_match[] = {
+	{ .compatible = "fsl,ls1024a-snowbush-phy", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, snowbush_of_match);
+
+static struct platform_driver snowbush_driver = {
+	.probe	= snowbush_probe,
+	.driver = {
+		.name	= "snowbush-phy",
+		.of_match_table	= snowbush_of_match,
+	}
+};
+module_platform_driver(snowbush_driver);
+
+MODULE_AUTHOR("Daniel Mentz <danielmentz@google.com>");
+MODULE_DESCRIPTION("Snowbush PHY driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index aeb5729..e9e0c21 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -93,6 +93,12 @@
 	depends on SOC_FALCON
 	depends on PINCTRL_LANTIQ
 
+config PINCTRL_LS1024A
+	bool
+	depends on OF
+	select PINMUX
+	select GENERIC_PINCONF
+
 config PINCTRL_MESON
 	bool
 	depends on OF
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index 6eadf04..816f4a4 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -16,6 +16,7 @@
 obj-$(CONFIG_PINCTRL_AT91)	+= pinctrl-at91.o
 obj-$(CONFIG_PINCTRL_AMD)	+= pinctrl-amd.o
 obj-$(CONFIG_PINCTRL_FALCON)	+= pinctrl-falcon.o
+obj-$(CONFIG_PINCTRL_LS1024A)	+= pinctrl-ls1024a.o
 obj-$(CONFIG_PINCTRL_MESON)	+= meson/
 obj-$(CONFIG_PINCTRL_PALMAS)	+= pinctrl-palmas.o
 obj-$(CONFIG_PINCTRL_ROCKCHIP)	+= pinctrl-rockchip.o
diff --git a/drivers/pinctrl/pinctrl-ls1024a.c b/drivers/pinctrl/pinctrl-ls1024a.c
new file mode 100644
index 0000000..25bd8bd
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-ls1024a.c
@@ -0,0 +1,927 @@
+#include <linux/gpio.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/printk.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/pinctrl/pinconf-generic.h>
+
+#include "pinctrl-utils.h"
+#include "core.h"
+
+#define LS1024A_GPIO_OUTPUT_REG			0x00
+#define LS1024A_GPIO_OE_REG			0x04
+#define LS1024A_GPIO_INT_CFG_REG		0x08
+#define LS1024A_GPIO_INPUT_REG			0x10
+#define LS1024A_PIN_SELECT_15_0_REG		0x58
+#define LS1024A_PIN_SELECT_31_16_REG		0x5C
+#define LS1024A_MISC_PIN_SELECT			0x60
+#define LS1024A_GPIO_63_32_PIN_OUTPUT		0xD0
+#define LS1024A_GPIO_63_32_PIN_OUTPUT_EN	0xD4
+#define LS1024A_GPIO_63_32_PIN_INPUT		0xD8
+#define LS1024A_GPIO_63_32_PIN_SELECT		0xDC
+
+#define LS1024A_GPIO_NUM_IRQS	8
+
+struct ls1024a_pinctrl {
+	struct device *dev;
+	struct pinctrl_dev *pctrl;
+	struct gpio_chip gc;
+	struct irq_chip	*irqchip;
+	struct irq_domain *irq_domain;
+
+	void __iomem *reg;
+
+	struct of_phandle_args parent_phandle_args;
+
+	/*
+	 * Used to lock ls1024a_pinctrl->data. Also, this is needed to keep
+	 * shadowed and real data registers writes together.
+	 */
+	spinlock_t lock;
+
+	/* Shadowed data register to clear/set bits safely. */
+	u32 data[2];
+
+	/* Shadowed direction registers to clear/set direction safely. */
+	u32 dir[2];
+};
+
+static inline struct ls1024a_pinctrl *gc_to_ls1024a_pinctrl(struct gpio_chip *gc)
+{
+	return container_of(gc, struct ls1024a_pinctrl, gc);
+}
+
+#define LS1024A_PIN_GEM0_RXD0		1
+#define LS1024A_PIN_GEM0_RXD1		2
+#define LS1024A_PIN_GEM0_RXD2		3
+#define LS1024A_PIN_GEM0_RXD3		4
+#define LS1024A_PIN_GEM0_RX_CTL		5
+#define LS1024A_PIN_GEM0_RXC		6
+#define LS1024A_PIN_GEM0_TXD0		7
+#define LS1024A_PIN_GEM0_TXD1		8
+#define LS1024A_PIN_GEM0_TXD2		9
+#define LS1024A_PIN_GEM0_TXD3		10
+#define LS1024A_PIN_GEM0_TX_CTL		11
+#define LS1024A_PIN_GEM0_TXC		12
+#define LS1024A_PIN_GEM1_RXD0		13
+#define LS1024A_PIN_GEM1_RXD1		14
+#define LS1024A_PIN_GEM1_RXD2		15
+#define LS1024A_PIN_GEM1_RXD3		16
+#define LS1024A_PIN_GEM1_RX_CTL		17
+#define LS1024A_PIN_GEM1_RXC		18
+#define LS1024A_PIN_GEM1_TXD0		19
+#define LS1024A_PIN_GEM1_TXD1		20
+#define LS1024A_PIN_GEM1_TXD2		21
+#define LS1024A_PIN_GEM1_TXD3		22
+#define LS1024A_PIN_GEM1_TX_CTL		23
+#define LS1024A_PIN_GEM1_TXC		24
+#define LS1024A_PIN_GEM2_RXD0		25
+#define LS1024A_PIN_GEM2_RXD1		26
+#define LS1024A_PIN_GEM2_RXD2		27
+#define LS1024A_PIN_GEM2_RXD3		28
+#define LS1024A_PIN_GEM2_RX_CTL		29
+#define LS1024A_PIN_GEM2_RXC		30
+#define LS1024A_PIN_GEM2_TXD0		31
+#define LS1024A_PIN_GEM2_TXD1		32
+#define LS1024A_PIN_GEM2_TXD2		33
+#define LS1024A_PIN_GEM2_TXD3		34
+#define LS1024A_PIN_GEM2_TX_CTL		35
+#define LS1024A_PIN_GEM2_TXC		36
+#define LS1024A_PIN_GEM2_REFCLK		37
+#define LS1024A_PIN_GPIO00		38
+#define LS1024A_PIN_GPIO01		39
+#define LS1024A_PIN_GPIO02		40
+#define LS1024A_PIN_GPIO03		41
+#define LS1024A_PIN_GPIO04		42
+#define LS1024A_PIN_GPIO05		43
+#define LS1024A_PIN_GPIO06		44
+#define LS1024A_PIN_GPIO07		45
+#define LS1024A_PIN_GPIO08		46
+#define LS1024A_PIN_GPIO09		47
+#define LS1024A_PIN_GPIO10		48
+#define LS1024A_PIN_GPIO11		49
+#define LS1024A_PIN_GPIO12		50
+#define LS1024A_PIN_GPIO13		51
+#define LS1024A_PIN_GPIO14		52
+#define LS1024A_PIN_GPIO15		53
+#define LS1024A_PIN_I2C_SCL		54
+#define LS1024A_PIN_I2C_SDA		55
+#define LS1024A_PIN_SPI_SS0_N		56
+#define LS1024A_PIN_SPI_SS1_N		57
+#define LS1024A_PIN_SPI_2_SS1_N		58
+#define LS1024A_PIN_SPI_SS2_N		59
+#define LS1024A_PIN_SPI_SS3_N		60
+#define LS1024A_PIN_EXP_CS2_N		61
+#define LS1024A_PIN_EXP_CS3_N		62
+#define LS1024A_PIN_EXP_ALE		63
+#define LS1024A_PIN_EXP_RDY		64
+#define LS1024A_PIN_TM_EXT_RESET	65
+#define LS1024A_PIN_EXP_NAND_CS		66
+#define LS1024A_PIN_EXP_NAND_RDY	67
+#define LS1024A_PIN_SPI_TXD		68
+#define LS1024A_PIN_SPI_SCLK		69
+#define LS1024A_PIN_SPI_RXD		70
+#define LS1024A_PIN_SPI_2_RXD		71
+#define LS1024A_PIN_SPI_2_SS0_N		72
+#define LS1024A_PIN_EXP_DQ08		73
+#define LS1024A_PIN_EXP_DQ09		74
+#define LS1024A_PIN_EXP_DQ10		75
+#define LS1024A_PIN_EXP_DQ11		76
+#define LS1024A_PIN_EXP_DQ12		77
+#define LS1024A_PIN_EXP_DQ13		78
+#define LS1024A_PIN_EXP_DQ14		79
+#define LS1024A_PIN_EXP_DQ15		80
+#define LS1024A_PIN_EXP_DM1		81
+#define LS1024A_PIN_CORESIGHT_D0	82
+#define LS1024A_PIN_CORESIGHT_D1	83
+#define LS1024A_PIN_CORESIGHT_D2	84
+#define LS1024A_PIN_CORESIGHT_D3	85
+#define LS1024A_PIN_CORESIGHT_D4	86
+#define LS1024A_PIN_CORESIGHT_D5	87
+#define LS1024A_PIN_CORESIGHT_D6	88
+#define LS1024A_PIN_CORESIGHT_D7	89
+#define LS1024A_PIN_CORESIGHT_D8	90
+#define LS1024A_PIN_CORESIGHT_D9	91
+#define LS1024A_PIN_CORESIGHT_D10	92
+#define LS1024A_PIN_CORESIGHT_D11	93
+#define LS1024A_PIN_CORESIGHT_D12	94
+#define LS1024A_PIN_CORESIGHT_D13	95
+#define LS1024A_PIN_CORESIGHT_D14	96
+#define LS1024A_PIN_CORESIGHT_D15	97
+#define LS1024A_PIN_UART1_RX		98
+#define LS1024A_PIN_UART1_TX		99
+#define LS1024A_PIN_TDM_CK		100
+#define LS1024A_PIN_TDM_FS		101
+#define LS1024A_PIN_TDM_DR		102
+#define LS1024A_PIN_TDM_DX		103
+
+
+
+static const struct pinctrl_pin_desc ls1024a_pins_desc[] = {
+	PINCTRL_PIN(LS1024A_PIN_GEM0_RXD0, "gem0_rxd0"),
+	PINCTRL_PIN(LS1024A_PIN_GEM0_RXD1, "gem0_rxd1"),
+	PINCTRL_PIN(LS1024A_PIN_GEM0_RXD2, "gem0_rxd2"),
+	PINCTRL_PIN(LS1024A_PIN_GEM0_RXD3, "gem0_rxd3"),
+	PINCTRL_PIN(LS1024A_PIN_GEM0_RX_CTL, "gem0_rx_ctl"),
+	PINCTRL_PIN(LS1024A_PIN_GEM0_RXC, "gem0_rxc"),
+	PINCTRL_PIN(LS1024A_PIN_GEM0_TXD0, "gem0_txd0"),
+	PINCTRL_PIN(LS1024A_PIN_GEM0_TXD1, "gem0_txd1"),
+	PINCTRL_PIN(LS1024A_PIN_GEM0_TXD2, "gem0_txd2"),
+	PINCTRL_PIN(LS1024A_PIN_GEM0_TXD3, "gem0_txd3"),
+	PINCTRL_PIN(LS1024A_PIN_GEM0_TX_CTL, "gem0_tx_ctl"),
+	PINCTRL_PIN(LS1024A_PIN_GEM0_TXC, "gem0_txc"),
+	PINCTRL_PIN(LS1024A_PIN_GEM1_RXD0, "gem1_rxd0"),
+	PINCTRL_PIN(LS1024A_PIN_GEM1_RXD1, "gem1_rxd1"),
+	PINCTRL_PIN(LS1024A_PIN_GEM1_RXD2, "gem1_rxd2"),
+	PINCTRL_PIN(LS1024A_PIN_GEM1_RXD3, "gem1_rxd3"),
+	PINCTRL_PIN(LS1024A_PIN_GEM1_RX_CTL, "gem1_rx_ctl"),
+	PINCTRL_PIN(LS1024A_PIN_GEM1_RXC, "gem1_rxc"),
+	PINCTRL_PIN(LS1024A_PIN_GEM1_TXD0, "gem1_txd0"),
+	PINCTRL_PIN(LS1024A_PIN_GEM1_TXD1, "gem1_txd1"),
+	PINCTRL_PIN(LS1024A_PIN_GEM1_TXD2, "gem1_txd2"),
+	PINCTRL_PIN(LS1024A_PIN_GEM1_TXD3, "gem1_txd3"),
+	PINCTRL_PIN(LS1024A_PIN_GEM1_TX_CTL, "gem1_tx_ctl"),
+	PINCTRL_PIN(LS1024A_PIN_GEM1_TXC, "gem1_txc"),
+	PINCTRL_PIN(LS1024A_PIN_GEM2_RXD0, "gem2_rxd0"),
+	PINCTRL_PIN(LS1024A_PIN_GEM2_RXD1, "gem2_rxd1"),
+	PINCTRL_PIN(LS1024A_PIN_GEM2_RXD2, "gem2_rxd2"),
+	PINCTRL_PIN(LS1024A_PIN_GEM2_RXD3, "gem2_rxd3"),
+	PINCTRL_PIN(LS1024A_PIN_GEM2_RX_CTL, "gem2_rx_ctl"),
+	PINCTRL_PIN(LS1024A_PIN_GEM2_RXC, "gem2_rxc"),
+	PINCTRL_PIN(LS1024A_PIN_GEM2_TXD0, "gem2_txd0"),
+	PINCTRL_PIN(LS1024A_PIN_GEM2_TXD1, "gem2_txd1"),
+	PINCTRL_PIN(LS1024A_PIN_GEM2_TXD2, "gem2_txd2"),
+	PINCTRL_PIN(LS1024A_PIN_GEM2_TXD3, "gem2_txd3"),
+	PINCTRL_PIN(LS1024A_PIN_GEM2_TX_CTL, "gem2_tx_ctl"),
+	PINCTRL_PIN(LS1024A_PIN_GEM2_TXC, "gem2_txc"),
+	PINCTRL_PIN(LS1024A_PIN_GEM2_REFCLK, "gem2_refclk"),
+	PINCTRL_PIN(LS1024A_PIN_GPIO00, "gpio00"),
+	PINCTRL_PIN(LS1024A_PIN_GPIO01, "gpio01"),
+	PINCTRL_PIN(LS1024A_PIN_GPIO02, "gpio02"),
+	PINCTRL_PIN(LS1024A_PIN_GPIO03, "gpio03"),
+	PINCTRL_PIN(LS1024A_PIN_GPIO04, "gpio04"),
+	PINCTRL_PIN(LS1024A_PIN_GPIO05, "gpio05"),
+	PINCTRL_PIN(LS1024A_PIN_GPIO06, "gpio06"),
+	PINCTRL_PIN(LS1024A_PIN_GPIO07, "gpio07"),
+	PINCTRL_PIN(LS1024A_PIN_GPIO08, "gpio08"),
+	PINCTRL_PIN(LS1024A_PIN_GPIO09, "gpio09"),
+	PINCTRL_PIN(LS1024A_PIN_GPIO10, "gpio10"),
+	PINCTRL_PIN(LS1024A_PIN_GPIO11, "gpio11"),
+	PINCTRL_PIN(LS1024A_PIN_GPIO12, "gpio12"),
+	PINCTRL_PIN(LS1024A_PIN_GPIO13, "gpio13"),
+	PINCTRL_PIN(LS1024A_PIN_GPIO14, "gpio14"),
+	PINCTRL_PIN(LS1024A_PIN_GPIO15, "gpio15"),
+	PINCTRL_PIN(LS1024A_PIN_I2C_SCL, "i2c_scl"), /* GPIO 16 */
+	PINCTRL_PIN(LS1024A_PIN_I2C_SDA, "i2c_sda"), /* GPIO 17 */
+	PINCTRL_PIN(LS1024A_PIN_SPI_SS0_N, "spi_ss0_n"), /* GPIO 18 */
+	PINCTRL_PIN(LS1024A_PIN_SPI_SS1_N, "spi_ss1_n"), /* GPIO 19 */
+	PINCTRL_PIN(LS1024A_PIN_SPI_2_SS1_N, "spi_2_ss1_n"), /* GPIO 20 */
+	PINCTRL_PIN(LS1024A_PIN_SPI_SS2_N, "spi_ss2_n"), /* GPIO 21 */
+	PINCTRL_PIN(LS1024A_PIN_SPI_SS3_N, "spi_ss3_n"), /* GPIO 22 */
+	PINCTRL_PIN(LS1024A_PIN_EXP_CS2_N, "exp_cs2_n"), /* GPIO 23 */
+	PINCTRL_PIN(LS1024A_PIN_EXP_CS3_N, "exp_cs3_n"), /* GPIO 24 */
+	PINCTRL_PIN(LS1024A_PIN_EXP_ALE, "exp_ale"), /* GPIO 25 */
+	PINCTRL_PIN(LS1024A_PIN_EXP_RDY, "exp_rdy"), /* GPIO 26 */
+	PINCTRL_PIN(LS1024A_PIN_TM_EXT_RESET, "tm_ext_reset"), /* GPIO 27 */
+	PINCTRL_PIN(LS1024A_PIN_EXP_NAND_CS, "exp_nand_cs"), /* GPIO 28 */
+	PINCTRL_PIN(LS1024A_PIN_EXP_NAND_RDY, "exp_nand_rdy"), /* GPIO 29 */
+	PINCTRL_PIN(LS1024A_PIN_SPI_TXD, "spi_txd"), /* GPIO 30 */
+	PINCTRL_PIN(LS1024A_PIN_SPI_SCLK, "spi_sclk"), /* GPIO 31 */
+	PINCTRL_PIN(LS1024A_PIN_SPI_RXD, "spi_rxd"), /* GPIO 32 */
+	PINCTRL_PIN(LS1024A_PIN_SPI_2_RXD, "spi_2_rxd"), /* GPIO 33 */
+	PINCTRL_PIN(LS1024A_PIN_SPI_2_SS0_N, "spi_2_ss0_n"), /* GPIO 34 */
+	PINCTRL_PIN(LS1024A_PIN_EXP_DQ08, "exp_dq08"), /* GPIO 35 */
+	PINCTRL_PIN(LS1024A_PIN_EXP_DQ09, "exp_dq09"), /* GPIO 36 */
+	PINCTRL_PIN(LS1024A_PIN_EXP_DQ10, "exp_dq10"), /* GPIO 37 */
+	PINCTRL_PIN(LS1024A_PIN_EXP_DQ11, "exp_dq11"), /* GPIO 38 */
+	PINCTRL_PIN(LS1024A_PIN_EXP_DQ12, "exp_dq12"), /* GPIO 39 */
+	PINCTRL_PIN(LS1024A_PIN_EXP_DQ13, "exp_dq13"), /* GPIO 40 */
+	PINCTRL_PIN(LS1024A_PIN_EXP_DQ14, "exp_dq14"), /* GPIO 41 */
+	PINCTRL_PIN(LS1024A_PIN_EXP_DQ15, "exp_dq15"), /* GPIO 42 */
+	PINCTRL_PIN(LS1024A_PIN_EXP_DM1, "exp_dm1"), /* GPIO 43 */
+	PINCTRL_PIN(LS1024A_PIN_CORESIGHT_D0, "coresight_d0"), /* GPIO 44 */
+	PINCTRL_PIN(LS1024A_PIN_CORESIGHT_D1, "coresight_d1"), /* GPIO 45 */
+	PINCTRL_PIN(LS1024A_PIN_CORESIGHT_D2, "coresight_d2"), /* GPIO 46 */
+	PINCTRL_PIN(LS1024A_PIN_CORESIGHT_D3, "coresight_d3"), /* GPIO 47 */
+	PINCTRL_PIN(LS1024A_PIN_CORESIGHT_D4, "coresight_d4"), /* GPIO 48 */
+	PINCTRL_PIN(LS1024A_PIN_CORESIGHT_D5, "coresight_d5"), /* GPIO 49 */
+	PINCTRL_PIN(LS1024A_PIN_CORESIGHT_D6, "coresight_d6"), /* GPIO 50 */
+	PINCTRL_PIN(LS1024A_PIN_CORESIGHT_D7, "coresight_d7"), /* GPIO 51 */
+	PINCTRL_PIN(LS1024A_PIN_CORESIGHT_D8, "coresight_d8"), /* GPIO 52 */
+	PINCTRL_PIN(LS1024A_PIN_CORESIGHT_D9, "coresight_d9"), /* GPIO 53 */
+	PINCTRL_PIN(LS1024A_PIN_CORESIGHT_D10, "coresight_d10"), /* GPIO 54 */
+	PINCTRL_PIN(LS1024A_PIN_CORESIGHT_D11, "coresight_d11"), /* GPIO 55 */
+	PINCTRL_PIN(LS1024A_PIN_CORESIGHT_D12, "coresight_d12"), /* GPIO 56 */
+	PINCTRL_PIN(LS1024A_PIN_CORESIGHT_D13, "coresight_d13"), /* GPIO 57 */
+	PINCTRL_PIN(LS1024A_PIN_CORESIGHT_D14, "coresight_d14"), /* GPIO 58 */
+	PINCTRL_PIN(LS1024A_PIN_CORESIGHT_D15, "coresight_d15"), /* GPIO 59 */
+	PINCTRL_PIN(LS1024A_PIN_UART1_RX, "uart1_rx"), /* not usable as GPIO */
+	PINCTRL_PIN(LS1024A_PIN_UART1_TX, "uart1_tx"), /* not usable as GPIO */
+	/* Warning: Following four GPIOs are in reverse order */
+	PINCTRL_PIN(LS1024A_PIN_TDM_CK, "tdm_ck"), /* GPIO 63 */
+	PINCTRL_PIN(LS1024A_PIN_TDM_FS, "tdm_fs"), /* GPIO 62 */
+	PINCTRL_PIN(LS1024A_PIN_TDM_DR, "tdm_dr"), /* GPIO 61 */
+	PINCTRL_PIN(LS1024A_PIN_TDM_DX, "tdm_dx"), /* GPIO 60 */
+};
+
+struct ls1024a_group {
+	const char *name;
+	const unsigned int *pins;
+	const unsigned num_pins;
+	const unsigned reg;
+	const unsigned shift;
+	const unsigned bits;
+};
+
+static const unsigned pwm0_pins[] = { LS1024A_PIN_GPIO04 };
+static const unsigned pwm1_pins[] = { LS1024A_PIN_GPIO05 };
+static const unsigned pwm2_pins[] = { LS1024A_PIN_GPIO06 };
+static const unsigned pwm3_pins[] = { LS1024A_PIN_GPIO07 };
+static const unsigned pwm4_pins[] = { LS1024A_PIN_GPIO12 };
+static const unsigned pwm5_pins[] = { LS1024A_PIN_GPIO13 };
+static const unsigned i2c_pins[] = { LS1024A_PIN_I2C_SCL, LS1024A_PIN_I2C_SDA };
+static const unsigned uart0_pins[] = { LS1024A_PIN_GPIO08, LS1024A_PIN_GPIO09, LS1024A_PIN_GPIO10, LS1024A_PIN_GPIO11 };
+static const unsigned uart1_pins[] = { LS1024A_PIN_UART1_RX, LS1024A_PIN_UART1_TX };
+static const unsigned spi_2_pins[] = { LS1024A_PIN_SPI_2_RXD }; /* SPI_2_TXD and SPI_2_SCLK are not muxed */
+static const unsigned spi_2_ss0_pins[] = { LS1024A_PIN_SPI_2_SS0_N };
+static const unsigned spi_2_ss1_pins[] = { LS1024A_PIN_SPI_2_SS1_N };
+static const unsigned gpio_pins[] = {
+	LS1024A_PIN_GPIO00, LS1024A_PIN_GPIO01, LS1024A_PIN_GPIO02,
+	LS1024A_PIN_GPIO03, LS1024A_PIN_GPIO04, LS1024A_PIN_GPIO05,
+	LS1024A_PIN_GPIO06, LS1024A_PIN_GPIO07, LS1024A_PIN_GPIO08,
+	LS1024A_PIN_GPIO09, LS1024A_PIN_GPIO10, LS1024A_PIN_GPIO11,
+	LS1024A_PIN_GPIO12, LS1024A_PIN_GPIO13, LS1024A_PIN_GPIO14,
+	LS1024A_PIN_GPIO15, LS1024A_PIN_I2C_SCL, LS1024A_PIN_I2C_SDA,
+	LS1024A_PIN_SPI_SS0_N, LS1024A_PIN_SPI_SS1_N, LS1024A_PIN_SPI_2_SS1_N,
+	LS1024A_PIN_SPI_SS2_N, LS1024A_PIN_SPI_SS3_N, LS1024A_PIN_EXP_CS2_N,
+	LS1024A_PIN_EXP_CS3_N, LS1024A_PIN_EXP_ALE, LS1024A_PIN_EXP_RDY,
+	LS1024A_PIN_TM_EXT_RESET, LS1024A_PIN_EXP_NAND_CS,
+	LS1024A_PIN_EXP_NAND_RDY, LS1024A_PIN_SPI_TXD, LS1024A_PIN_SPI_SCLK,
+	LS1024A_PIN_SPI_RXD, LS1024A_PIN_SPI_2_RXD, LS1024A_PIN_SPI_2_SS0_N,
+	LS1024A_PIN_EXP_DQ08, LS1024A_PIN_EXP_DQ09, LS1024A_PIN_EXP_DQ10,
+	LS1024A_PIN_EXP_DQ11, LS1024A_PIN_EXP_DQ12, LS1024A_PIN_EXP_DQ13,
+	LS1024A_PIN_EXP_DQ14, LS1024A_PIN_EXP_DQ15, LS1024A_PIN_EXP_DM1,
+	LS1024A_PIN_CORESIGHT_D0, LS1024A_PIN_CORESIGHT_D1,
+	LS1024A_PIN_CORESIGHT_D2, LS1024A_PIN_CORESIGHT_D3,
+	LS1024A_PIN_CORESIGHT_D4, LS1024A_PIN_CORESIGHT_D5,
+	LS1024A_PIN_CORESIGHT_D6, LS1024A_PIN_CORESIGHT_D7,
+	LS1024A_PIN_CORESIGHT_D8, LS1024A_PIN_CORESIGHT_D9,
+	LS1024A_PIN_CORESIGHT_D10, LS1024A_PIN_CORESIGHT_D11,
+	LS1024A_PIN_CORESIGHT_D12, LS1024A_PIN_CORESIGHT_D13,
+	LS1024A_PIN_CORESIGHT_D14, LS1024A_PIN_CORESIGHT_D15,
+	/* The last four GPIOs are in a non-intuitive order, i.e. they are in
+	 * reverse order compared to how the pins are defined. */
+	LS1024A_PIN_TDM_DX, LS1024A_PIN_TDM_DR, LS1024A_PIN_TDM_FS, LS1024A_PIN_TDM_CK,
+};
+
+static const struct ls1024a_group ls1024a_groups[] = {
+	{
+		.name = "gpio",
+		.pins = gpio_pins,
+		.num_pins = ARRAY_SIZE(gpio_pins),
+	},
+	{
+		.name = "pwm0",
+		.pins = pwm0_pins,
+		.num_pins = ARRAY_SIZE(pwm0_pins),
+		.reg = LS1024A_PIN_SELECT_15_0_REG,
+		.shift = 8,
+		.bits = 2,
+	},
+	{
+		.name = "pwm1",
+		.pins = pwm1_pins,
+		.num_pins = ARRAY_SIZE(pwm1_pins),
+		.reg = LS1024A_PIN_SELECT_15_0_REG,
+		.shift = 10,
+		.bits = 2,
+	},
+	{
+		.name = "pwm2",
+		.pins = pwm2_pins,
+		.num_pins = ARRAY_SIZE(pwm2_pins),
+		.reg = LS1024A_PIN_SELECT_15_0_REG,
+		.shift = 12,
+		.bits = 2,
+	},
+	{
+		.name = "pwm3",
+		.pins = pwm3_pins,
+		.num_pins = ARRAY_SIZE(pwm3_pins),
+		.reg = LS1024A_PIN_SELECT_15_0_REG,
+		.shift = 14,
+		.bits = 2,
+	},
+	{
+		.name = "pwm4",
+		.pins = pwm4_pins,
+		.num_pins = ARRAY_SIZE(pwm4_pins),
+		.reg = LS1024A_PIN_SELECT_15_0_REG,
+		.shift = 24,
+		.bits = 2,
+	},
+	{
+		.name = "pwm5",
+		.pins = pwm5_pins,
+		.num_pins = ARRAY_SIZE(pwm5_pins),
+		.reg = LS1024A_PIN_SELECT_15_0_REG,
+		.shift = 26,
+		.bits = 2,
+	},
+	{
+		.name = "i2c",
+		.pins = i2c_pins,
+		.num_pins = ARRAY_SIZE(i2c_pins),
+		.reg = LS1024A_PIN_SELECT_31_16_REG,
+		.shift = 0,
+		.bits = 4,
+	},
+	{
+		.name = "uart0",
+		.pins = uart0_pins,
+		.num_pins = ARRAY_SIZE(uart0_pins),
+	},
+	{
+		.name = "uart1",
+		.pins = uart1_pins,
+		.num_pins = ARRAY_SIZE(uart1_pins),
+	},
+	{
+		.name = "spi_2",
+		.pins = spi_2_pins,
+		.num_pins = ARRAY_SIZE(spi_2_pins),
+		.reg = LS1024A_GPIO_63_32_PIN_SELECT,
+		.shift = 1,
+		.bits = 1,
+	},
+	{
+		.name = "spi_2_ss0",
+		.pins = spi_2_ss0_pins,
+		.num_pins = ARRAY_SIZE(spi_2_ss0_pins),
+		.reg = LS1024A_GPIO_63_32_PIN_SELECT,
+		.shift = 2,
+		.bits = 1,
+	},
+	{
+		.name = "spi_2_ss1",
+		.pins = spi_2_ss1_pins,
+		.num_pins = ARRAY_SIZE(spi_2_ss1_pins),
+		.reg = LS1024A_PIN_SELECT_31_16_REG,
+		.shift = 8, /* TODO */
+		.bits = 2,
+	},
+};
+
+static const char * const pwm_groups[] = { "pwm0", "pwm1", "pwm2", "pwm3", "pwm4", "pwm5" };
+static const char * const i2c_groups[] = { "i2c" };
+static const char * const uart0_groups[] = { "uart0" };
+static const char * const uart1_groups[] = { "uart1" };
+static const char * const spi_2_groups[] = { "spi_2", "spi_2_ss0", "spi_2_ss1" };
+
+struct ls1024a_pmx_func {
+	const char *name;
+	const char * const *groups;
+	const unsigned num_groups;
+	const unsigned muxsetting;
+};
+
+static const struct ls1024a_pmx_func ls1024a_functions[] = {
+	{
+		.name = "pwm",
+		.groups = pwm_groups,
+		.num_groups = ARRAY_SIZE(pwm_groups),
+		.muxsetting = 1,
+	},
+	{
+		.name = "i2c",
+		.groups = i2c_groups,
+		.num_groups = ARRAY_SIZE(i2c_groups),
+		.muxsetting = 0,
+	},
+	{
+		.name = "uart0",
+		.groups = uart0_groups,
+		.num_groups = ARRAY_SIZE(uart0_groups),
+		.muxsetting = 2,
+	},
+	{
+		.name = "uart1",
+		.groups = uart1_groups,
+		.num_groups = ARRAY_SIZE(uart1_groups),
+		.muxsetting = 0,
+	},
+	{
+		.name = "spi_2",
+		.groups = spi_2_groups,
+		.num_groups = ARRAY_SIZE(spi_2_groups),
+		.muxsetting = 0,
+	},
+};
+
+static int ls1024a_get_groups_count(struct pinctrl_dev *pctldev)
+{
+	return ARRAY_SIZE(ls1024a_groups);
+}
+
+static const char *ls1024a_get_group_name(struct pinctrl_dev *pctldev,
+				      unsigned group)
+{
+	return ls1024a_groups[group].name;
+}
+
+static int ls1024a_get_group_pins(struct pinctrl_dev *pctldev,
+			      unsigned group,
+			      const unsigned **pins,
+			      unsigned *num_pins)
+{
+	*pins = ls1024a_groups[group].pins;
+	*num_pins = ls1024a_groups[group].num_pins;
+	return 0;
+}
+
+static int ls1024a_get_functions_count(struct pinctrl_dev *pctldev)
+{
+	return ARRAY_SIZE(ls1024a_functions);
+}
+
+static const char *ls1024a_get_function_name(struct pinctrl_dev *pctldev,
+					 unsigned function)
+{
+	return ls1024a_functions[function].name;
+}
+
+static int ls1024a_get_function_groups(struct pinctrl_dev *pctldev,
+				   unsigned function,
+				   const char * const **groups,
+				   unsigned * const num_groups)
+{
+	*groups = ls1024a_functions[function].groups;
+	*num_groups = ls1024a_functions[function].num_groups;
+	return 0;
+}
+
+static int ls1024a_pinmux_set_mux(struct pinctrl_dev *pctldev,
+			      unsigned function,
+			      unsigned group)
+{
+	struct ls1024a_pinctrl *lpc = pinctrl_dev_get_drvdata(pctldev);
+	const struct ls1024a_group *g;
+	const struct ls1024a_pmx_func *f;
+	unsigned long flags;
+	u32 val;
+	u32 mask;
+
+	g = &ls1024a_groups[group];
+	f = &ls1024a_functions[function];
+
+	spin_lock_irqsave(&lpc->lock, flags);
+
+	val = readl(lpc->reg + g->reg);
+	mask = GENMASK(g->shift + g->bits - 1, g->shift);
+	val &= ~mask;
+	val |= f->muxsetting << g->shift;
+	writel(val, lpc->reg + g->reg);
+
+	spin_unlock_irqrestore(&lpc->lock, flags);
+
+	return 0;
+}
+
+static void _ls1024a_gpio_set_direction(struct ls1024a_pinctrl *lpc, unsigned int gpio, int out);
+
+static inline unsigned int pin_to_gpio(struct pinctrl_gpio_range *range,
+				unsigned int pin)
+{
+	int idx;
+	BUG_ON(!range->pins);
+	for (idx = 0; idx < range->npins; idx++) {
+		if (pin == range->pins[idx])
+			break;
+	}
+	BUG_ON(idx >= range->npins);
+	return idx;
+}
+
+static int ls1024a_gpio_request_enable (struct pinctrl_dev *pctldev,
+				struct pinctrl_gpio_range *range,
+				unsigned offset)
+{
+	struct ls1024a_pinctrl *lpc = pinctrl_dev_get_drvdata(pctldev);
+	unsigned long flags;
+	unsigned int gpio;
+	u32 val;
+	u32 newval;
+	unsigned int i;
+
+	gpio = pin_to_gpio(range, offset);
+
+	spin_lock_irqsave(&lpc->lock, flags);
+	if (offset >= LS1024A_PIN_TDM_CK && offset <= LS1024A_PIN_TDM_DX) {
+		/* GPIO 60 to GPIO 63 */
+		val = readl(lpc->reg + LS1024A_MISC_PIN_SELECT);
+		newval = (val & ~GENMASK(5,4)) | 2<<4;
+		if (newval != val) {
+			BUILD_BUG_ON(!(LS1024A_PIN_TDM_CK < LS1024A_PIN_TDM_DX));
+			for (i = LS1024A_PIN_TDM_CK; i <= LS1024A_PIN_TDM_DX; i++) {
+				gpio = pin_to_gpio(range, i);
+				_ls1024a_gpio_set_direction(lpc, gpio, 0);
+			}
+			writel(newval, lpc->reg + LS1024A_MISC_PIN_SELECT);
+		}
+	} else if (offset >= LS1024A_PIN_SPI_RXD && offset <= LS1024A_PIN_CORESIGHT_D15) {
+		/* GPIO 32 to GPIO 59 */
+		i = offset - LS1024A_PIN_SPI_RXD;
+		val = readl(lpc->reg + LS1024A_GPIO_63_32_PIN_SELECT);
+		newval = val | BIT(i);
+		if (newval != val) {
+			_ls1024a_gpio_set_direction(lpc, gpio, 0);
+			writel(newval, lpc->reg + LS1024A_GPIO_63_32_PIN_SELECT);
+		}
+	} else if ( (offset >= LS1024A_PIN_I2C_SCL && offset <= LS1024A_PIN_EXP_RDY) ||
+			(offset >= LS1024A_PIN_SPI_TXD && offset <= LS1024A_PIN_SPI_SCLK) ) {
+		/* GPIO 16 to GPIO 26  and GPIO 30 to GPIO 31
+		 * From the datasheet:
+		 * "GPIO[27] is used as TM_EXT_RESET and is not muxed
+		 *  GPIO[28] is used as EXP_NAND_CS and is not muxed
+		 *  GPIO[29] is used as EXP_NAND_RDY and is not muxed"
+		 * */
+		i = offset - LS1024A_PIN_I2C_SCL;
+		val = readl(lpc->reg + LS1024A_PIN_SELECT_31_16_REG);
+		newval = (val & ~GENMASK(i*2 + 1, i*2)) | (1<<i*2);
+		if (newval != val) {
+			_ls1024a_gpio_set_direction(lpc, gpio, 0);
+			writel(newval, lpc->reg + LS1024A_PIN_SELECT_31_16_REG);
+		}
+	} else if (offset >= LS1024A_PIN_GPIO04 && offset <= LS1024A_PIN_GPIO15) {
+		/* GPIO 4 to GPIO 15 */
+		i = offset - LS1024A_PIN_GPIO00;
+		val = readl(lpc->reg + LS1024A_PIN_SELECT_15_0_REG);
+		newval = val & ~GENMASK(i*2 + 1, i*2);
+		if (newval != val) {
+			_ls1024a_gpio_set_direction(lpc, gpio, 0);
+			writel(newval, lpc->reg + LS1024A_PIN_SELECT_15_0_REG);
+		}
+	} else if (offset >= LS1024A_PIN_GPIO00 && offset <= LS1024A_PIN_GPIO03) {
+		/* No work change needed. GPIOs 0 through 3 are always selected. */
+	}
+
+	spin_unlock_irqrestore(&lpc->lock, flags);
+	return 0;
+}
+
+static const struct pinmux_ops ls1024a_pinmux_ops = {
+	.get_functions_count	= ls1024a_get_functions_count,
+	.get_function_name	= ls1024a_get_function_name,
+	.get_function_groups	= ls1024a_get_function_groups,
+	.set_mux		= ls1024a_pinmux_set_mux,
+	.gpio_request_enable	= ls1024a_gpio_request_enable,
+};
+
+static const struct pinctrl_ops ls1024a_pinctrl_ops = {
+	.get_groups_count	= ls1024a_get_groups_count,
+	.get_group_name		= ls1024a_get_group_name,
+	.get_group_pins		= ls1024a_get_group_pins,
+	.dt_node_to_map		= pinconf_generic_dt_node_to_map_group,
+	.dt_free_map		= pinctrl_utils_dt_free_map,
+};
+
+static struct pinctrl_desc ls1024a_pinctrl_desc = {
+	.pins = ls1024a_pins_desc,
+	.npins = ARRAY_SIZE(ls1024a_pins_desc),
+	.pctlops = &ls1024a_pinctrl_ops,
+	.pmxops = &ls1024a_pinmux_ops,
+	.owner = THIS_MODULE,
+};
+
+
+
+static int ls1024a_gpio_get(struct gpio_chip *gc, unsigned pin)
+{
+	u32 val;
+	struct ls1024a_pinctrl *lpc = gc_to_ls1024a_pinctrl(gc);
+
+	if (pin >= gc->ngpio)
+		return -EINVAL;
+
+	if (pin < 32) {
+		val = readl(lpc->reg + LS1024A_GPIO_INPUT_REG);
+	} else {
+		val = readl(lpc->reg + LS1024A_GPIO_63_32_PIN_INPUT);
+	}
+
+	if (val & BIT_MASK(pin))
+		return 1;
+
+	return 0;
+}
+
+static void ls1024a_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
+{
+	struct ls1024a_pinctrl *lpc = gc_to_ls1024a_pinctrl(gc);
+	unsigned long flags;
+
+	if (gpio >= gc->ngpio)
+		return;
+
+	spin_lock_irqsave(&lpc->lock, flags);
+
+	if (val)
+		lpc->data[BIT_WORD(gpio)] |= BIT_MASK(gpio);
+	else
+		lpc->data[BIT_WORD(gpio)] &= ~BIT_MASK(gpio);
+
+	writel(lpc->data[0], lpc->reg + LS1024A_GPIO_OUTPUT_REG);
+	writel(lpc->data[1], lpc->reg + LS1024A_GPIO_63_32_PIN_OUTPUT);
+
+	spin_unlock_irqrestore(&lpc->lock, flags);
+}
+
+static void _ls1024a_gpio_set_direction(struct ls1024a_pinctrl *lpc, unsigned int gpio, int out) {
+	if (out)
+		lpc->dir[BIT_WORD(gpio)] |= BIT_MASK(gpio);
+	else
+		lpc->dir[BIT_WORD(gpio)] &= ~BIT_MASK(gpio);
+
+	writel(lpc->dir[0], lpc->reg + LS1024A_GPIO_OE_REG);
+	/* LS1024A_GPIO_63_32_PIN_OUTPUT_EN uses inverse logic */
+	writel(~lpc->dir[1], lpc->reg + LS1024A_GPIO_63_32_PIN_OUTPUT_EN);
+}
+
+static int ls1024a_gpio_set_direction(struct gpio_chip *gc, unsigned int gpio, int out) {
+	struct ls1024a_pinctrl *lpc = gc_to_ls1024a_pinctrl(gc);
+	unsigned long flags;
+
+	if (gpio >= gc->ngpio)
+		return -EINVAL;
+
+	spin_lock_irqsave(&lpc->lock, flags);
+
+	_ls1024a_gpio_set_direction(lpc, gpio, out);
+
+	spin_unlock_irqrestore(&lpc->lock, flags);
+
+	return 0;
+}
+
+static int ls1024a_dir_in(struct gpio_chip *gc, unsigned int gpio)
+{
+	return ls1024a_gpio_set_direction(gc, gpio, 0);
+}
+
+static int ls1024a_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
+{
+	ls1024a_gpio_set(gc, gpio, val);
+	return ls1024a_gpio_set_direction(gc, gpio, 1);
+}
+
+static int ls1024a_gpio_request(struct gpio_chip *chip, unsigned gpio_pin)
+{
+	if (gpio_pin < chip->ngpio)
+		return pinctrl_request_gpio(chip->base + gpio_pin);
+
+	return -EINVAL;
+}
+
+static void ls1024a_gpio_free(struct gpio_chip *chip, unsigned gpio_pin)
+{
+	pinctrl_free_gpio(chip->base + gpio_pin);
+}
+
+static int ls1024a_set_type(struct irq_data *data, unsigned int type)
+{
+	struct ls1024a_pinctrl *lpc = irq_data_get_irq_chip_data(data);
+	int ls1024a_type;
+	int ret;
+	unsigned long flags;
+	u32 value;
+	switch(type) {
+		case IRQ_TYPE_EDGE_RISING:
+			ls1024a_type = 0x2;
+			break;
+		case IRQ_TYPE_EDGE_FALLING:
+			ls1024a_type = 0x1;
+			break;
+		case IRQ_TYPE_LEVEL_HIGH:
+			ls1024a_type = 0x3;
+			break;
+		default:
+			WARN_ON(1);
+			/* Fall through */
+		case IRQ_TYPE_NONE:
+			ls1024a_type = 0x0;
+			break;
+	}
+	spin_lock_irqsave(&lpc->lock, flags);
+	value = readl(lpc->reg + LS1024A_GPIO_INT_CFG_REG);
+	value &= ~(0x3 << data->hwirq*2);
+	value |= ls1024a_type << data->hwirq*2;
+	writel(value, lpc->reg + LS1024A_GPIO_INT_CFG_REG);
+	spin_unlock_irqrestore(&lpc->lock, flags);
+
+	data = data->parent_data;
+	ret = data->chip->irq_set_type(data, type);
+	return ret;
+}
+
+static struct irq_chip ls1024a_gpio_irq_chip = {
+	.name			= "GPIO",
+	.irq_mask		= irq_chip_mask_parent,
+	.irq_unmask		= irq_chip_unmask_parent,
+	.irq_eoi		= irq_chip_eoi_parent,
+	.irq_set_type		= ls1024a_set_type,
+	.irq_retrigger		= irq_chip_retrigger_hierarchy,
+	.irq_set_affinity	= irq_chip_set_affinity_parent,
+};
+
+static int ls1024a_domain_alloc(struct irq_domain *domain, unsigned int virq,
+				   unsigned int nr_irqs, void *arg)
+{
+	int i, ret;
+	irq_hw_number_t hwirq;
+	unsigned int type;
+	struct of_phandle_args *irq_data = arg;
+	struct of_phandle_args gic_data = *irq_data;
+	struct ls1024a_pinctrl *lpc = domain->host_data;
+
+	if (irq_data->args_count != 2)
+		return -EINVAL;
+
+	ret = domain->ops->xlate(domain, irq_data->np, irq_data->args,
+			irq_data->args_count, &hwirq, &type);
+	if (ret)
+		return ret;
+	for (i = 0; i < nr_irqs; i++)
+		irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
+					      &ls1024a_gpio_irq_chip,
+					      domain->host_data);
+	gic_data = lpc->parent_phandle_args;
+	gic_data.args[1] += hwirq;
+	gic_data.args[2] = type;
+	return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &gic_data);
+}
+
+static struct irq_domain_ops ls1024a_domain_ops = {
+	.xlate = irq_domain_xlate_twocell,
+	.alloc = ls1024a_domain_alloc,
+	.free = irq_domain_free_irqs_common,
+};
+
+
+static int ls1024a_pinctrl_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *node = dev->of_node;
+	struct resource *res;
+	struct ls1024a_pinctrl *lpc;
+	struct irq_domain *domain_parent;
+	unsigned long flags;
+	int rc;
+
+	lpc = devm_kzalloc(&pdev->dev, sizeof(*lpc), GFP_KERNEL);
+	if (!lpc)
+		return -ENOMEM;
+	lpc->dev = &pdev->dev;
+
+	lpc->gc.dev = dev;
+#ifdef CONFIG_OF_GPIO
+	lpc->gc.of_node = of_node_get(node);
+#endif
+	spin_lock_init(&lpc->lock);
+	lpc->gc.dev = dev;
+	lpc->gc.label = dev_name(dev);
+	lpc->gc.base = 0;
+	lpc->gc.ngpio = 64;
+	lpc->gc.request = ls1024a_gpio_request;
+	lpc->gc.free = ls1024a_gpio_free;
+
+	lpc->gc.direction_input = ls1024a_dir_in;
+	lpc->gc.direction_output = ls1024a_dir_out;
+	lpc->gc.set = ls1024a_gpio_set;
+	lpc->gc.get = ls1024a_gpio_get;
+	lpc->gc.can_sleep = false;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	lpc->reg = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(lpc->reg)) {
+		rc = PTR_ERR(lpc->reg);
+		goto err;
+	}
+
+	platform_set_drvdata(pdev, lpc);
+
+	ls1024a_pinctrl_desc.name = dev_name(&pdev->dev);
+	lpc->pctrl = pinctrl_register(&ls1024a_pinctrl_desc, &pdev->dev, lpc);
+	if (!lpc->pctrl) {
+		dev_err(&pdev->dev, "Couldn't register pinctrl driver\n");
+		rc = -ENODEV;
+		goto err_iounmap;
+	}
+
+	rc = of_irq_parse_one(node, 0, &lpc->parent_phandle_args);
+	if (rc) {
+		goto err_unregister_pinctrl;
+	}
+	domain_parent = irq_find_host(lpc->parent_phandle_args.np);
+	if (!domain_parent) {
+		dev_err(dev, "cannot find irq parent domain\n");
+		rc = -EPROBE_DEFER;
+		goto err_unregister_pinctrl;
+	}
+	lpc->irq_domain = irq_domain_add_hierarchy(domain_parent, 0,
+			LS1024A_GPIO_NUM_IRQS, node,
+			&ls1024a_domain_ops, lpc);
+	if (!lpc->irq_domain) {
+		rc = -ENOMEM;
+		goto err_unregister_pinctrl;
+	}
+
+	spin_lock_irqsave(&lpc->lock, flags);
+	lpc->dir[0] = readl(lpc->reg + LS1024A_GPIO_OE_REG);
+	/* LS1024A_GPIO_63_32_PIN_OUTPUT_EN uses inverse logic */
+	lpc->dir[1] = ~readl(lpc->reg + LS1024A_GPIO_63_32_PIN_OUTPUT_EN);
+	lpc->data[0] = readl(lpc->reg + LS1024A_GPIO_OUTPUT_REG);
+	lpc->data[1] = readl(lpc->reg + LS1024A_GPIO_63_32_PIN_OUTPUT);
+	spin_unlock_irqrestore(&lpc->lock, flags);
+
+	lpc->gc.owner = THIS_MODULE;
+
+	rc = gpiochip_add(&lpc->gc);
+	if (rc)
+		goto err_remove_domain;
+	return 0;
+
+err_remove_domain:
+	irq_domain_remove(lpc->irq_domain);
+err_unregister_pinctrl:
+	pinctrl_unregister(lpc->pctrl);
+err_iounmap:
+	devm_iounmap(dev, lpc->reg);
+err:
+	platform_set_drvdata(pdev, NULL);
+	devm_kfree(dev, lpc);
+	return rc;
+}
+
+static const struct of_device_id ls1024a_pinctrl_of_match[] = {
+	{ .compatible = "fsl,ls1024a-pinctrl", },
+	{ },
+};
+
+static struct platform_driver ls1024a_pinctrl_driver = {
+	.driver = {
+		.name = "ls1024a-pinctrl",
+		.of_match_table = ls1024a_pinctrl_of_match,
+	},
+	.probe = ls1024a_pinctrl_probe,
+};
+
+module_platform_driver(ls1024a_pinctrl_driver);
+
+MODULE_AUTHOR("Daniel Mentz <danielmentz@google.com>");
+MODULE_DESCRIPTION("Freescale QorIQ LS1024A pinctrl driver");
+MODULE_LICENSE("GPL v2");
+MODULE_DEVICE_TABLE(of, ls1024a_pinctrl_of_match);
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index b1541f4..5166de5 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -211,6 +211,15 @@
 	  To compile this driver as a module, choose M here: the module
 	  will be called pwm-lpss-platform.
 
+config PWM_LS1024A
+	tristate "Freescale QorIQ LS1024A PWM support"
+	depends on ARCH_COMCERTO && OF
+	help
+	  PWM framework driver for Freescale QorIQ LS1024A
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called pwm-ls1024a.
+
 config PWM_MXS
 	tristate "Freescale MXS PWM support"
 	depends on ARCH_MXS && OF
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index ec50eb5..2156996 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -18,6 +18,7 @@
 obj-$(CONFIG_PWM_LPSS)		+= pwm-lpss.o
 obj-$(CONFIG_PWM_LPSS_PCI)	+= pwm-lpss-pci.o
 obj-$(CONFIG_PWM_LPSS_PLATFORM)	+= pwm-lpss-platform.o
+obj-$(CONFIG_PWM_LS1024A)	+= pwm-ls1024a.o
 obj-$(CONFIG_PWM_MXS)		+= pwm-mxs.o
 obj-$(CONFIG_PWM_PCA9685)	+= pwm-pca9685.o
 obj-$(CONFIG_PWM_PUV3)		+= pwm-puv3.o
diff --git a/drivers/pwm/pwm-ls1024a.c b/drivers/pwm/pwm-ls1024a.c
new file mode 100644
index 0000000..ba0acbe
--- /dev/null
+++ b/drivers/pwm/pwm-ls1024a.c
@@ -0,0 +1,215 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2.
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/spinlock.h>
+#include <asm/div64.h>
+
+#define NUMBER_OF_PWMS		6
+
+#define CLK_DIV_VALUE		0xFF		/* Maximum value */
+
+#define PWM_VALUE_MASK		GENMASK(19,0)
+#define CLK_DIV_ENABLE		BIT(31)
+#define PWM_ENABLE		BIT(31)
+
+#define CLK_DIV_CTRL_REG	0
+#define ENABLE_MAX(x)		(((x) * 0x8) + 0x8)
+#define LOW_DUTY(x)		(((x) * 0x8) + 0xC)
+
+struct ls1024a_pwm {
+	struct pwm_chip chip;
+	struct device *dev;
+	unsigned long scaler;
+	unsigned long enabled_pwms;
+	void __iomem *base;
+	struct clk *clk;
+	spinlock_t lock;
+};
+
+static inline struct ls1024a_pwm *to_ls1024a_pwm(struct pwm_chip *chip)
+{
+	return container_of(chip, struct ls1024a_pwm, chip);
+}
+
+static int ls1024a_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+			      int duty_ns, int period_ns)
+{
+	struct ls1024a_pwm *pc = to_ls1024a_pwm(chip);
+	unsigned long flags;
+	u32 value;
+	unsigned long duty_ticks, period_ticks;
+
+	if (period_ns <= (int) pc->scaler) {
+		dev_err(pc->dev, "period %d not supported, minimum %lu\n",
+			period_ns, pc->scaler);
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&pc->lock, flags);
+
+	period_ticks = DIV_ROUND_CLOSEST(period_ns, pc->scaler);
+	period_ticks = max(period_ticks, (unsigned long) 1) - 1;
+	value = readl(pc->base + ENABLE_MAX(pwm->hwpwm));
+	value &= ~PWM_VALUE_MASK;
+	value |=  period_ticks & PWM_VALUE_MASK;
+	writel(value , pc->base + ENABLE_MAX(pwm->hwpwm));
+
+	duty_ticks = DIV_ROUND_CLOSEST(period_ns - duty_ns, pc->scaler);
+	duty_ticks = max(duty_ticks, (unsigned long) 1) - 1;
+	value = readl(pc->base + LOW_DUTY(pwm->hwpwm));
+	value &= ~PWM_VALUE_MASK;
+	value |=  duty_ticks & PWM_VALUE_MASK;
+	writel(value, pc->base + LOW_DUTY(pwm->hwpwm));
+
+	spin_unlock_irqrestore(&pc->lock, flags);
+
+	return 0;
+}
+
+static int ls1024a_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+	struct ls1024a_pwm *pc = to_ls1024a_pwm(chip);
+	unsigned long flags;
+	u32 value;
+
+	spin_lock_irqsave(&pc->lock, flags);
+
+	value = readl(pc->base + ENABLE_MAX(pwm->hwpwm));
+	value |= PWM_ENABLE;
+	writel(value, pc->base + ENABLE_MAX(pwm->hwpwm));
+
+	pc->enabled_pwms |= BIT(pwm->hwpwm);
+
+	/* Enable clock divider */
+	value = readl(pc->base + CLK_DIV_CTRL_REG);
+	if (!(value & CLK_DIV_ENABLE)) {
+		value = CLK_DIV_ENABLE | CLK_DIV_VALUE;
+		writel(value, pc->base + CLK_DIV_CTRL_REG);
+	}
+
+	spin_unlock_irqrestore(&pc->lock, flags);
+
+	return 0;
+}
+
+static void ls1024a_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+	struct ls1024a_pwm *pc = to_ls1024a_pwm(chip);
+	unsigned long flags;
+	u32 value;
+
+	spin_lock_irqsave(&pc->lock, flags);
+
+	value = readl(pc->base + ENABLE_MAX(pwm->hwpwm));
+	value &= ~PWM_ENABLE;
+	writel(value, pc->base + ENABLE_MAX(pwm->hwpwm));
+
+	pc->enabled_pwms &= ~BIT(pwm->hwpwm);
+
+	/* If we disabled the last PWM, shut down the clock divider, too. */
+	if (!pc->enabled_pwms) {
+		value = readl(pc->base + CLK_DIV_CTRL_REG);
+		if (value & CLK_DIV_ENABLE) {
+			value &= ~CLK_DIV_ENABLE;
+			writel(value, pc->base + CLK_DIV_CTRL_REG);
+		}
+	}
+
+	spin_unlock_irqrestore(&pc->lock, flags);
+}
+
+static const struct pwm_ops ls1024a_pwm_ops = {
+	.config = ls1024a_pwm_config,
+	.enable = ls1024a_pwm_enable,
+	.disable = ls1024a_pwm_disable,
+	.owner = THIS_MODULE,
+};
+
+static int ls1024a_pwm_probe(struct platform_device *pdev)
+{
+	struct ls1024a_pwm *pc;
+	struct resource *res;
+	int ret;
+
+	pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL);
+	if (!pc)
+		return -ENOMEM;
+
+	pc->dev = &pdev->dev;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	pc->base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(pc->base))
+		return PTR_ERR(pc->base);
+
+	pc->clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(pc->clk)) {
+		dev_err(&pdev->dev, "clock not found: %ld\n", PTR_ERR(pc->clk));
+		return PTR_ERR(pc->clk);
+	}
+
+	ret = clk_prepare_enable(pc->clk);
+	if (ret)
+		return ret;
+
+	pc->scaler = DIV_ROUND_CLOSEST_ULL((u64) NSEC_PER_SEC *
+			(CLK_DIV_VALUE + 1),
+			clk_get_rate(pc->clk));
+
+	pc->chip.dev = &pdev->dev;
+	pc->chip.ops = &ls1024a_pwm_ops;
+	pc->chip.npwm = NUMBER_OF_PWMS;
+
+	spin_lock_init(&pc->lock);
+
+	platform_set_drvdata(pdev, pc);
+
+	ret = pwmchip_add(&pc->chip);
+	if (ret < 0)
+		goto add_fail;
+
+	return 0;
+
+add_fail:
+	clk_disable_unprepare(pc->clk);
+	return ret;
+}
+
+static int ls1024a_pwm_remove(struct platform_device *pdev)
+{
+	struct ls1024a_pwm *pc = platform_get_drvdata(pdev);
+
+	clk_disable_unprepare(pc->clk);
+
+	return pwmchip_remove(&pc->chip);
+}
+
+static const struct of_device_id ls1024a_pwm_of_match[] = {
+	{ .compatible = "fsl,ls1024a-pwm", },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, ls1024a_pwm_of_match);
+
+static struct platform_driver ls1024a_pwm_driver = {
+	.driver = {
+		.name = "ls1024a-pwm",
+		.of_match_table = ls1024a_pwm_of_match,
+	},
+	.probe = ls1024a_pwm_probe,
+	.remove = ls1024a_pwm_remove,
+};
+module_platform_driver(ls1024a_pwm_driver);
+
+MODULE_AUTHOR("Daniel Mentz <danielmentz@google.com");
+MODULE_DESCRIPTION("Freescale LS1024A PWM driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
index 157d421..9d92fe2 100644
--- a/drivers/reset/Makefile
+++ b/drivers/reset/Makefile
@@ -2,4 +2,5 @@
 obj-$(CONFIG_ARCH_SOCFPGA) += reset-socfpga.o
 obj-$(CONFIG_ARCH_BERLIN) += reset-berlin.o
 obj-$(CONFIG_ARCH_SUNXI) += reset-sunxi.o
+obj-$(CONFIG_ARCH_COMCERTO) += reset-ls1024a.o
 obj-$(CONFIG_ARCH_STI) += sti/
diff --git a/drivers/reset/reset-ls1024a.c b/drivers/reset/reset-ls1024a.c
new file mode 100644
index 0000000..4cce5ef
--- /dev/null
+++ b/drivers/reset/reset-ls1024a.c
@@ -0,0 +1,228 @@
+/*
+ * Freescale LS1024A SoCs Reset Controller driver
+ *
+ * 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.
+ */
+
+#include <linux/reset-controller.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <dt-bindings/reset-controller/ls1024a-resets.h>
+
+/**
+ * Reset channel regmap configuration
+ *
+ * @reset: regmap field for the channel's reset bit.
+ */
+struct reset_channel {
+	struct regmap_field *reset;
+};
+
+struct ls1024a_reset_data {
+	spinlock_t			lock;
+	struct regmap			*regmap;
+	struct reset_channel		*channels;
+	struct reset_controller_dev	rcdev;
+};
+
+#define DEVICE_RST_CNTRL		0x000
+#define SERDES_RST_CNTRL		0x004
+#define PCIe_SATA_RST_CNTRL		0x008
+#define USB_RST_CNTRL			0x00C
+#define AXI_RESET_0			0x050
+#define AXI_RESET_1			0x054
+#define AXI_RESET_2			0x058
+#define A9DP_MPU_RESET			0x070
+#define A9DP_CPU_RESET			0x078
+#define A9DP_RESET			0x088
+#define L2CC_RESET_CNTRL		0x098
+#define TPI_RESET			0x0A8
+#define CSYS_RESET			0x0B8
+#define EXTPHY0_RESET			0x0C8
+#define EXTPHY1_RESET			0x0D8
+#define EXTPHY2_RESET			0x0E8
+#define DDR_RESET			0x0F8
+#define PFE_RESET			0x108
+#define IPSEC_RESET			0x118
+#define DECT_RESET_CNTRL		0x128
+#define GEMTX_RESET_CNTRL		0x138
+#define TDMNTG_RESET_CNTRL		0x148
+#define TSUNTG_RESET 			0x158
+#define SATA_PMU_RESET_CNTRL		0x168
+#define SATA_OOB_RESET_CNTRL		0x178
+#define SATA_OCC_RESET			0x188
+#define PCIE_OCC_RESET			0x198
+#define SGMII_OCC_RESET 		0x1A8
+
+#define RESET_BIT(_rr, _rb)	REG_FIELD((_rr), (_rb), (_rb))
+
+static const struct reg_field reset_bits[] = {
+	[LS1024A_AXI_DPI_CIE_RESET]	= RESET_BIT(AXI_RESET_0, 5),
+	[LS1024A_AXI_DPI_DECOMP_RESET]	= RESET_BIT(AXI_RESET_0, 6),
+	[LS1024A_AXI_IPSEC_EAPE_RESET]	= RESET_BIT(AXI_RESET_1, 1),
+	[LS1024A_AXI_IPSEC_SPACC_RESET]	= RESET_BIT(AXI_RESET_1, 2),
+	[LS1024A_PFE_SYS_RESET]		= RESET_BIT(AXI_RESET_1, 3),
+	[LS1024A_AXI_TDM_RESET]		= RESET_BIT(AXI_RESET_1, 4),
+	[LS1024A_AXI_RTC_RESET]		= RESET_BIT(AXI_RESET_1, 7),
+	[LS1024A_AXI_PCIE0_RESET]	= RESET_BIT(AXI_RESET_2, 0),
+	[LS1024A_AXI_PCIE1_RESET]	= RESET_BIT(AXI_RESET_2, 1),
+	[LS1024A_AXI_SATA_RESET]	= RESET_BIT(AXI_RESET_2, 2),
+	[LS1024A_AXI_USB0_RESET]	= RESET_BIT(AXI_RESET_2, 3),
+	[LS1024A_AXI_USB1_RESET]	= RESET_BIT(AXI_RESET_2, 4),
+	[LS1024A_USB0_PHY_RESET]	= RESET_BIT(USB_RST_CNTRL, 0),
+	[LS1024A_UTMI_USB0_RESET]	= RESET_BIT(USB_RST_CNTRL, 1),
+	[LS1024A_USB1_PHY_RESET]	= RESET_BIT(USB_RST_CNTRL, 4),
+	[LS1024A_UTMI_USB1_RESET]	= RESET_BIT(USB_RST_CNTRL, 5),
+	[LS1024A_SERDES_PCIE0_RESET]	= REG_FIELD(PCIe_SATA_RST_CNTRL, 0, 1),
+	[LS1024A_SERDES_PCIE1_RESET]	= REG_FIELD(PCIe_SATA_RST_CNTRL, 2, 3),
+	[LS1024A_SERDES_SATA0_RESET]	= REG_FIELD(PCIe_SATA_RST_CNTRL, 4, 5),
+	[LS1024A_SERDES_SATA1_RESET]	= REG_FIELD(PCIe_SATA_RST_CNTRL, 6, 7),
+	[LS1024A_PFE_CORE_RESET]	= RESET_BIT(PFE_RESET, 0),
+	[LS1024A_IPSEC_EAPE_CORE_RESET]	= RESET_BIT(IPSEC_RESET, 0),
+	[LS1024A_GEMTX_RESET]		= RESET_BIT(GEMTX_RESET_CNTRL, 0),
+	[LS1024A_DECT_RESET]		= RESET_BIT(DECT_RESET_CNTRL, 0),
+	[LS1024A_DDR_CNTLR_RESET]	= RESET_BIT(DDR_RESET, 1),
+	[LS1024A_DDR_PHY_RESET]		= RESET_BIT(DDR_RESET, 0),
+	[LS1024A_SERDES0_RESET]		= RESET_BIT(SERDES_RST_CNTRL, 0),
+	[LS1024A_SERDES1_RESET]		= RESET_BIT(SERDES_RST_CNTRL, 1),
+	[LS1024A_SERDES2_RESET]		= RESET_BIT(SERDES_RST_CNTRL, 2),
+	[LS1024A_SGMII_RESET]		= RESET_BIT(SGMII_OCC_RESET, 0),
+	[LS1024A_SATA_PMU_RESET]	= RESET_BIT(SATA_PMU_RESET_CNTRL, 0),
+	[LS1024A_SATA_OOB_RESET]	= RESET_BIT(SATA_OOB_RESET_CNTRL, 0),
+	[LS1024A_TDMNTG_RESET]		= RESET_BIT(TDMNTG_RESET_CNTRL, 0),
+};
+
+#define to_ls1024a_reset_data(_rst) \
+	container_of(_rst, struct ls1024a_reset_data, rcdev)
+
+static int ls1024a_reset_program_hw(struct reset_controller_dev *rcdev,
+				   unsigned long idx, int assert)
+{
+	struct ls1024a_reset_data *rst = to_ls1024a_reset_data(rcdev);
+	const struct reset_channel *ch;
+	u32 ctrl_val = assert ? 0xFFFFFFFF : 0;
+	int err;
+
+	if (idx >= rcdev->nr_resets)
+		return -EINVAL;
+
+	ch = &rst->channels[idx];
+
+	err = regmap_field_write(ch->reset, ctrl_val);
+	if (err)
+		return err;
+
+	return 0;
+}
+
+static int ls1024a_reset_assert(struct reset_controller_dev *rcdev,
+			       unsigned long idx)
+{
+	return ls1024a_reset_program_hw(rcdev, idx, true);
+}
+
+static int ls1024a_reset_deassert(struct reset_controller_dev *rcdev,
+				 unsigned long idx)
+{
+	return ls1024a_reset_program_hw(rcdev, idx, false);
+}
+
+static int ls1024a_reset_status(struct reset_controller_dev *rcdev,
+				   unsigned long idx)
+{
+	struct ls1024a_reset_data *rst = to_ls1024a_reset_data(rcdev);
+	const struct reset_channel *ch;
+	unsigned cur_val;
+	int err;
+
+	if (idx >= rcdev->nr_resets)
+		return -EINVAL;
+
+	ch = &rst->channels[idx];
+
+	err = regmap_field_read(ch->reset, &cur_val);
+	if (err)
+		return err;
+
+	return cur_val;
+}
+
+static struct reset_control_ops ls1024a_reset_ops = {
+	.assert		= ls1024a_reset_assert,
+	.deassert	= ls1024a_reset_deassert,
+	.status		= ls1024a_reset_status,
+};
+
+static int ls1024a_reset_probe(struct platform_device *pdev)
+{
+	struct ls1024a_reset_data *data;
+	size_t size;
+	int i;
+
+	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	size = sizeof(struct reset_channel) * ARRAY_SIZE(reset_bits);
+
+	data->channels = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
+	if (!data->channels)
+		return -ENOMEM;
+
+	data->regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "syscon");
+	if (IS_ERR(data->regmap)) {
+		dev_err(&pdev->dev,
+			"Error retrieving reset syscon\n");
+		return PTR_ERR(data->regmap);
+	}
+	spin_lock_init(&data->lock);
+
+	data->rcdev.owner = THIS_MODULE;
+	data->rcdev.nr_resets = ARRAY_SIZE(reset_bits);
+	data->rcdev.ops = &ls1024a_reset_ops;
+	data->rcdev.of_node = pdev->dev.of_node;
+
+	for (i = 0; i < ARRAY_SIZE(reset_bits); i++) {
+		struct regmap_field *f;
+		const struct reg_field zero = {0};
+
+		if (memcmp(&reset_bits[i], &zero, sizeof(zero))) {
+			f = devm_regmap_field_alloc(&pdev->dev, data->regmap, reset_bits[i]);
+			if (IS_ERR(f))
+				return PTR_ERR(f);
+			data->channels[i].reset = f;
+		}
+
+	}
+
+	return reset_controller_register(&data->rcdev);
+}
+
+static const struct of_device_id ls1024a_reset_dt_ids[] = {
+	 { .compatible = "fsl,ls1024a-reset", },
+	 { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, ls1024a_reset_dt_ids);
+
+static struct platform_driver ls1024a_reset_driver = {
+	.probe	= ls1024a_reset_probe,
+	.driver = {
+		.name		= "ls1024a-reset",
+		.of_match_table	= ls1024a_reset_dt_ids,
+	},
+};
+static int __init ls1024a_reset_init(void)
+{
+	return platform_driver_register(&ls1024a_reset_driver);
+}
+
+arch_initcall(ls1024a_reset_init);
+
+MODULE_DESCRIPTION("Freescale LS1024A SoCs Reset Controller Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c
index 4fbfcdc..851bb67 100644
--- a/drivers/spi/spi-dw.c
+++ b/drivers/spi/spi-dw.c
@@ -138,17 +138,27 @@
 }
 #endif /* CONFIG_DEBUG_FS */
 
-static void dw_spi_set_cs(struct spi_device *spi, bool enable)
+static void __dw_spi_set_cs(struct spi_device *spi, bool enable)
 {
 	struct dw_spi *dws = spi_master_get_devdata(spi->master);
+
+	if (!enable)
+		dw_writel(dws, DW_SPI_SER, BIT(spi->chip_select));
+	else
+		dw_writel(dws, DW_SPI_SER, 0);
+}
+
+static void dw_spi_set_cs(struct spi_device *spi, bool enable)
+{
+
 	struct chip_data *chip = spi_get_ctldata(spi);
 
 	/* Chip select logic is inverted from spi_set_cs() */
 	if (chip && chip->cs_control)
 		chip->cs_control(!enable);
 
-	if (!enable)
-		dw_writel(dws, DW_SPI_SER, BIT(spi->chip_select));
+	if (enable)
+		__dw_spi_set_cs(spi, enable);
 }
 
 /* Return the max entries we can fill into tx fifo */
@@ -197,6 +207,7 @@
 		dw_write_io_reg(dws, DW_SPI_DR, txw);
 		dws->tx += dws->n_bytes;
 	}
+	__dw_spi_set_cs(dws->master->cur_msg->spi, 0);
 }
 
 static void dw_reader(struct dw_spi *dws)
@@ -316,7 +327,8 @@
 			speed = transfer->speed_hz;
 
 			/* clk_div doesn't support odd number */
-			clk_div = (dws->max_freq / speed + 1) & 0xfffe;
+			clk_div = (DIV_ROUND_UP(dws->max_freq, speed) + 1) &
+				0xfffe;
 
 			chip->speed_hz = speed;
 			chip->clk_div = clk_div;
@@ -370,6 +382,7 @@
 	if (dws->dma_mapped) {
 		ret = dws->dma_ops->dma_setup(dws, transfer);
 		if (ret < 0) {
+			__dw_spi_set_cs(spi, 0);
 			spi_enable_chip(dws, 1);
 			return ret;
 		}
diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c
index a64d53f..7bc2148 100644
--- a/drivers/tty/serial/8250/8250_core.c
+++ b/drivers/tty/serial/8250/8250_core.c
@@ -1634,6 +1634,15 @@
 
 	DEBUG_INTR("status = %x...", status);
 
+	/* The Synopsys DesignWare UART (DW_apb_uart) sometimes gets into a
+	 * weird state where the IIR reads "Character timeout" but the Data
+	 * Ready bit in the LSR is not set. To avoid a stuck interrupt, we need
+	 * to clear it by reading the receive buffer register. We achieve this
+	 * by faking the Data Ready bit (DR) */
+	if ( (iir & UART_IIR_TOD) == UART_IIR_TOD && ! (status & UART_LSR_DR)) {
+		status |= UART_LSR_DR;
+	}
+
 	if (status & (UART_LSR_DR | UART_LSR_BI)) {
 		if (up->dma)
 			dma_err = up->dma->rx_dma(up, iir);
diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c
index 176f18f..d7352d9 100644
--- a/drivers/tty/serial/8250/8250_dw.c
+++ b/drivers/tty/serial/8250/8250_dw.c
@@ -181,7 +181,6 @@
 		d->last_mcr = value;
 
 	writel(value, p->membase + (offset << p->regshift));
-
 	/* Make sure LCR write wasn't ignored */
 	if (offset == UART_LCR) {
 		int tries = 1000;
diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c
index be96970..ee6d368 100644
--- a/drivers/tty/tty_io.c
+++ b/drivers/tty/tty_io.c
@@ -3216,10 +3216,18 @@
 static int tty_cdev_add(struct tty_driver *driver, dev_t dev,
 		unsigned int index, unsigned int count)
 {
+	int err;
+
 	/* init here, since reused cdevs cause crashes */
-	cdev_init(&driver->cdevs[index], &tty_fops);
-	driver->cdevs[index].owner = driver->owner;
-	return cdev_add(&driver->cdevs[index], dev, count);
+	driver->cdevs[index] = cdev_alloc();
+	if (!driver->cdevs[index])
+		return -ENOMEM;
+	driver->cdevs[index]->ops = &tty_fops;
+	driver->cdevs[index]->owner = driver->owner;
+	err = cdev_add(driver->cdevs[index], dev, count);
+	if (err)
+		kobject_put(&driver->cdevs[index]->kobj);
+	return err;
 }
 
 /**
@@ -3325,8 +3333,10 @@
 
 error:
 	put_device(dev);
-	if (cdev)
-		cdev_del(&driver->cdevs[index]);
+	if (cdev) {
+		cdev_del(driver->cdevs[index]);
+		driver->cdevs[index] = NULL;
+	}
 	return ERR_PTR(retval);
 }
 EXPORT_SYMBOL_GPL(tty_register_device_attr);
@@ -3346,8 +3356,10 @@
 {
 	device_destroy(tty_class,
 		MKDEV(driver->major, driver->minor_start) + index);
-	if (!(driver->flags & TTY_DRIVER_DYNAMIC_ALLOC))
-		cdev_del(&driver->cdevs[index]);
+	if (!(driver->flags & TTY_DRIVER_DYNAMIC_ALLOC)) {
+		cdev_del(driver->cdevs[index]);
+		driver->cdevs[index] = NULL;
+	}
 }
 EXPORT_SYMBOL(tty_unregister_device);
 
@@ -3412,6 +3424,7 @@
 	kfree(driver->ports);
 	kfree(driver->ttys);
 	kfree(driver->termios);
+	kfree(driver->cdevs);
 	kfree(driver);
 	return ERR_PTR(err);
 }
@@ -3440,7 +3453,7 @@
 		}
 		proc_tty_unregister_driver(driver);
 		if (driver->flags & TTY_DRIVER_DYNAMIC_ALLOC)
-			cdev_del(&driver->cdevs[0]);
+			cdev_del(driver->cdevs[0]);
 	}
 	kfree(driver->cdevs);
 	kfree(driver->ports);
diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig
index 827c4f8..b905889 100644
--- a/drivers/usb/dwc3/Kconfig
+++ b/drivers/usb/dwc3/Kconfig
@@ -97,6 +97,14 @@
 	  Recent Qualcomm SoCs ship with one DesignWare Core USB3 IP inside,
 	  say 'Y' or 'M' if you have one such device.
 
+config USB_DWC3_LS1024A
+	tristate "Freescale QorIQ LS1024A"
+	depends on ARCH_COMCERTO || COMPILE_TEST
+	default USB_DWC3
+	help
+	  Freescale QorIQ LS1024A with one DesignWare Core USB3 IP inside,
+	  say 'Y' or 'M' if you have one such device.
+
 comment "Debugging features"
 
 config USB_DWC3_DEBUG
diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile
index 46172f4..6f51230 100644
--- a/drivers/usb/dwc3/Makefile
+++ b/drivers/usb/dwc3/Makefile
@@ -35,5 +35,6 @@
 obj-$(CONFIG_USB_DWC3_EXYNOS)		+= dwc3-exynos.o
 obj-$(CONFIG_USB_DWC3_PCI)		+= dwc3-pci.o
 obj-$(CONFIG_USB_DWC3_KEYSTONE)		+= dwc3-keystone.o
+obj-$(CONFIG_USB_DWC3_LS1024A)		+= dwc3-ls1024a.o
 obj-$(CONFIG_USB_DWC3_QCOM)		+= dwc3-qcom.o
 obj-$(CONFIG_USB_DWC3_ST)		+= dwc3-st.o
diff --git a/drivers/usb/dwc3/dwc3-ls1024a.c b/drivers/usb/dwc3/dwc3-ls1024a.c
new file mode 100644
index 0000000..a344502
--- /dev/null
+++ b/drivers/usb/dwc3/dwc3-ls1024a.c
@@ -0,0 +1,112 @@
+/**
+ * dwc3-ls1024a.c Support for dwc3 platform devices on Freescale QorIQ LS1024A
+ *
+ * This is a small driver for the dwc3 to provide the glue logic
+ * to configure the controller.
+ *
+ * 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.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+
+struct ls1024a_dwc {
+	struct device *dev;
+	struct reset_control *rstc_utmi;
+	struct reset_control *rstc_axi;
+	struct clk *clk;
+};
+
+static int ls1024a_dwc_probe(struct platform_device *pdev)
+{
+	struct ls1024a_dwc *dwc3_data;
+	struct device *dev = &pdev->dev;
+	struct device_node *node = dev->of_node;
+	int ret;
+
+	dwc3_data = devm_kzalloc(dev, sizeof(*dwc3_data), GFP_KERNEL);
+	if (!dwc3_data)
+		return -ENOMEM;
+
+	dwc3_data->dev = dev;
+
+	dwc3_data->clk = devm_clk_get(dev, "usb");
+	if (IS_ERR(dwc3_data->clk)) {
+		ret = PTR_ERR(dwc3_data->clk);
+		goto undo_platform_dev_alloc;
+	}
+	clk_prepare_enable(dwc3_data->clk);
+
+	dwc3_data->rstc_utmi = devm_reset_control_get(dev, "utmi");
+	if (IS_ERR(dwc3_data->rstc_utmi)) {
+		ret = PTR_ERR(dwc3_data->rstc_utmi);
+		goto undo_clk;
+	}
+
+	if (reset_control_status(dwc3_data->rstc_utmi)) {
+		reset_control_deassert(dwc3_data->rstc_utmi);
+		udelay(1000);
+	}
+
+	dwc3_data->rstc_axi = devm_reset_control_get(dev, "axi");
+	if (IS_ERR(dwc3_data->rstc_axi)) {
+		ret = PTR_ERR(dwc3_data->rstc_axi);
+		goto undo_utmi;
+	}
+
+	if (reset_control_status(dwc3_data->rstc_axi)) {
+		reset_control_deassert(dwc3_data->rstc_axi);
+		udelay(1000);
+	}
+
+	/* Allocate and initialize the core */
+	ret = of_platform_populate(node, NULL, NULL, dev);
+	if (ret) {
+		dev_err(dev, "failed to add dwc3 core\n");
+		goto undo_axi;
+	}
+
+	platform_set_drvdata(pdev, dwc3_data);
+	return 0;
+
+undo_axi:
+	reset_control_assert(dwc3_data->rstc_axi);
+undo_utmi:
+	reset_control_assert(dwc3_data->rstc_utmi);
+undo_clk:
+	clk_disable_unprepare(dwc3_data->clk);
+undo_platform_dev_alloc:
+	platform_device_put(pdev);
+	return ret;
+}
+
+static const struct of_device_id ls1024a_dwc_match[] = {
+	{ .compatible = "fsl,ls1024a-dwc3" },
+	{ /* sentinel */ },
+};
+
+MODULE_DEVICE_TABLE(of, ls1024a_dwc_match);
+
+static struct platform_driver ls1024a_dwc_driver = {
+	.probe = ls1024a_dwc_probe,
+	.driver = {
+		.name = "usb-ls1024a-dwc3",
+		.of_match_table = ls1024a_dwc_match,
+	},
+};
+
+module_platform_driver(ls1024a_dwc_driver);
+
+MODULE_AUTHOR("Daniel Mentz <danielmentz@google.com>");
+MODULE_DESCRIPTION("DesignWare USB3 Freescale QorIQ LS1024a Glue Layer");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index e5e7c55..9d8f79a 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -514,6 +514,15 @@
 	  To compile this driver as a module, choose M here: the
 	  module will be called mtk_wdt.
 
+config LS1024A_WATCHDOG
+	tristate "LS1024A Watchdog"
+	depends on ARCH_COMCERTO
+	help
+	  Say Y here to include support for the watchdog timer
+	  in Freescale QorIQ LS1024A device.
+	  To compile this driver as a module, choose M here: the
+	  module will be called ls1024a_wdt.
+
 # AVR32 Architecture
 
 config AT32AP700X_WDT
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 5c19294..96a705a 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -64,6 +64,7 @@
 obj-$(CONFIG_TEGRA_WATCHDOG) += tegra_wdt.o
 obj-$(CONFIG_MESON_WATCHDOG) += meson_wdt.o
 obj-$(CONFIG_MEDIATEK_WATCHDOG) += mtk_wdt.o
+obj-$(CONFIG_LS1024A_WATCHDOG) += ls1024a_wdt.o
 
 # AVR32 Architecture
 obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o
diff --git a/drivers/watchdog/ls1024a_wdt.c b/drivers/watchdog/ls1024a_wdt.c
new file mode 100644
index 0000000..5d0ec0a
--- /dev/null
+++ b/drivers/watchdog/ls1024a_wdt.c
@@ -0,0 +1,501 @@
+/*
+ *  drivers/watchdog/ls1024a_wdt.c
+ *
+ *  Copyright (C) 2004,2005,2013 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/errno.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+#include <linux/watchdog.h>
+#include <linux/clk.h>
+#include <linux/timer.h>
+#include <linux/io.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+#include <asm/bitops.h>
+#include <asm/uaccess.h>
+
+#define LS1024A_TIMER_WDT_HIGH_BOUND			0x0
+#define LS1024A_TIMER_WDT_CONTROL			0x4
+#define  LS1024A_TIMER_WDT_CONTROL_TIMER_ENABLE		BIT(0)
+#define LS1024A_TIMER_WDT_CURRENT_COUNT			0x8
+
+#define LS1024A_DEVICE_RST_CNTRL			0x0
+#define  LS1024A_WD_STATUS_CLR				BIT(6)
+#define  LS1024A_AXI_WD_RST_EN				BIT(5)
+#define LS1024A_GNRL_DEVICE_STATUS			0x18
+#define  LS1024A_AXI_WD_RST_ACTIVATED			BIT(0)
+
+#define WDT_NAME					"comcerto_wdt"
+
+/* these are the actual wdt limits */
+#define WDT_DEFAULT_TIMEOUT				5
+#define WDT_MAX_TIMEOUT					(0xffffffff / LS1024A_AHBCLK)
+
+/* these are for the virtual wdt */
+#define WDT_DEFAULT_TIME				70	/* seconds */
+#define WDT_MAX_TIME					255	/* seconds */
+
+static unsigned long LS1024A_AHBCLK;
+static int wd_heartbeat = WDT_DEFAULT_TIMEOUT;
+static int wd_time = WDT_DEFAULT_TIME;
+static int nowayout = WATCHDOG_NOWAYOUT;
+static struct clk *axi_clk;
+static struct regmap *clk_rst_map;
+static void __iomem *ls1024a_wdt_base;
+
+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 ls1024a_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.
+ * If the boot was caused by AXI WDT, the WD status is cleared from
+ * reset control register.
+ */
+static int ls1024a_wdt_rst_status(void)
+{
+	unsigned long flags;
+	u32 val;
+	int ret = 0;
+
+	spin_lock_irqsave(&wdt_lock, flags);
+
+	regmap_read(clk_rst_map, LS1024A_GNRL_DEVICE_STATUS, &val);
+
+	if (val & LS1024A_AXI_WD_RST_ACTIVATED) {
+		regmap_update_bits(clk_rst_map, LS1024A_DEVICE_RST_CNTRL,
+				   LS1024A_WD_STATUS_CLR,
+				   LS1024A_WD_STATUS_CLR);
+		ret = 1;
+	}
+
+	spin_unlock_irqrestore(&wdt_lock, flags);
+
+	return ret;
+}
+
+/*
+ * Set a new heartbeat value for the watchdog device. If the heartbeat value is
+ * incorrect we keep the old value and return -EINVAL. If successfull we return 0.
+ */
+static int ls1024a_wdt_set_heartbeat(int t)
+{
+	if (t < 1 || t > WDT_MAX_TIMEOUT)
+		return -EINVAL;
+
+	wd_heartbeat = t;
+	return 0;
+}
+
+/*
+ * Write wd_heartbeat to high bound register.
+ */
+static void ls1024a_wdt_pet_watchdog_physical(void)
+{
+	__raw_writel(wd_heartbeat * LS1024A_AHBCLK,
+		     ls1024a_wdt_base + LS1024A_TIMER_WDT_HIGH_BOUND);
+}
+
+/*
+ * reset virtual wdt timer
+ */
+static void ls1024a_wdt_pet_watchdog_virtual(void)
+{
+	atomic_set(&ticks, wd_time);
+}
+
+/*
+ * set virtual wd timeout reset value
+ */
+static int ls1024a_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 ls1024a_timer_tick(unsigned long unused)
+{
+	if (!atomic_dec_and_test(&ticks)) {
+		ls1024a_wdt_pet_watchdog_physical();
+		mod_timer(&wdt_timer, jiffies + HZ);
+	} else {
+		printk(KERN_CRIT "%s: Watchdog will fire soon!!!\n", WDT_NAME);
+	}
+}
+
+/*
+ * Disable the watchdog.
+ */
+static void ls1024a_wdt_stop(void)
+{
+	unsigned long flags;
+	u32 wdt_control;
+
+	spin_lock_irqsave(&wdt_lock, flags);
+
+	del_timer(&wdt_timer);
+
+	wdt_control = __raw_readl(ls1024a_wdt_base + LS1024A_TIMER_WDT_CONTROL);
+	wdt_control &= ~LS1024A_TIMER_WDT_CONTROL_TIMER_ENABLE;
+	__raw_writel(wdt_control, ls1024a_wdt_base + LS1024A_TIMER_WDT_CONTROL);
+
+	spin_unlock_irqrestore(&wdt_lock, flags);
+
+	ls1024a_wdt_pet_watchdog_physical();
+}
+
+/*
+ * Enable the watchdog.
+ */
+static void ls1024a_wdt_start(void)
+{
+	unsigned long flags;
+	u32 wdt_control;
+
+	spin_lock_irqsave(&wdt_lock, flags);
+
+	wdt_control = __raw_readl(ls1024a_wdt_base + LS1024A_TIMER_WDT_CONTROL);
+	wdt_control |= LS1024A_TIMER_WDT_CONTROL_TIMER_ENABLE;
+	__raw_writel(wdt_control, ls1024a_wdt_base + LS1024A_TIMER_WDT_CONTROL);
+
+	regmap_update_bits(clk_rst_map, LS1024A_DEVICE_RST_CNTRL,
+			   LS1024A_AXI_WD_RST_EN, LS1024A_AXI_WD_RST_EN);
+
+	mod_timer(&wdt_timer, jiffies + HZ);
+
+	spin_unlock_irqrestore(&wdt_lock, flags);
+}
+
+/*
+ * Disable WDT and:
+ * - set max. possible timeout to avoid reset, it can occur
+ * since current counter value could be bigger then
+ * high bound one at the moment
+ * Function is called once at start (while configuration),
+ * and it's safe not to disable/enable IRQs.
+ */
+static void ls1024a_wdt_config(void)
+{
+	ls1024a_wdt_stop();
+
+	__raw_writel(~0, ls1024a_wdt_base + LS1024A_TIMER_WDT_HIGH_BOUND);	/* write max timeout */
+}
+
+/*
+ * Watchdog device is opened, and watchdog starts running.
+ */
+static int ls1024a_wdt_open(struct inode *inode, struct file *file)
+{
+	if (test_and_set_bit(0, &ls1024a_wdt_busy))
+		return -EBUSY;
+
+	ls1024a_wdt_pet_watchdog_virtual();
+	ls1024a_wdt_pet_watchdog_physical();
+	ls1024a_wdt_start();
+
+	return nonseekable_open(inode, file);
+}
+
+/*
+ * Release the watchdog device.
+ * If CONFIG_WATCHDOG_NOWAYOUT is NOT defined and expect_close == 42
+ * i.e. magic char 'V' has been passed while write() then the watchdog
+ * is also disabled.
+ */
+static int ls1024a_wdt_release(struct inode *inode, struct file *file)
+{
+	if (expect_close == 42) {
+		ls1024a_wdt_stop();	/* disable the watchdog when file is closed */
+		clear_bit(0, &ls1024a_wdt_busy);
+	} else {
+		printk(KERN_CRIT "%s: closed unexpectedly. WDT will not stop!\n", WDT_NAME);
+	}
+
+	expect_close = 0;
+	return 0;
+}
+
+/*
+ * Handle commands from user-space.
+ */
+static long ls1024a_wdt_ioctl(struct file *file, uint cmd, ulong arg)
+{
+	void __user *argp = (void __user *)arg;
+	int __user *p = argp;
+	int new_value;
+	int err;
+	static struct watchdog_info ls1024a_wdt_info = {
+		.options = 	WDIOF_SETTIMEOUT |
+				WDIOF_MAGICCLOSE |
+				WDIOF_KEEPALIVEPING,
+		.firmware_version = 1,
+	};
+
+	switch(cmd) {
+	case WDIOC_KEEPALIVE:
+		ls1024a_wdt_pet_watchdog_virtual();
+		break;
+
+	case WDIOC_GETSUPPORT:
+		strncpy(ls1024a_wdt_info.identity, WDT_NAME, sizeof(ls1024a_wdt_info.identity));
+		if (copy_to_user(argp, &ls1024a_wdt_info, sizeof(ls1024a_wdt_info)) != 0) {
+			err = -EFAULT;
+			goto err;
+		}
+		break;
+
+	case WDIOC_SETTIMEOUT:
+		if (get_user(new_value, p)) {
+			err = -EFAULT;
+			goto err;
+		}
+
+		if (ls1024a_wdt_settimeout(new_value)) {
+			err = -EINVAL;
+			goto err;
+		}
+
+		ls1024a_wdt_pet_watchdog_virtual();
+
+		return put_user(wd_time, p);
+		break;
+
+	case WDIOC_GETTIMEOUT:
+		return put_user(wd_time, p);
+		break;
+
+	case WDIOC_GETSTATUS:
+		return put_user(0, p);
+		break;
+
+	case WDIOC_GETBOOTSTATUS:
+		return put_user(ls1024a_wdt_rst_status(), p);
+		break;
+
+	case WDIOC_SETOPTIONS:
+		if (get_user(new_value, p)) {
+			err = -EFAULT;
+			goto err;
+		}
+
+		if (new_value & WDIOS_DISABLECARD)
+			ls1024a_wdt_stop();
+
+		if (new_value & WDIOS_ENABLECARD)
+			ls1024a_wdt_start();
+
+		break;
+
+	default:
+		err = -ENOIOCTLCMD;
+		goto err;
+		break;
+	}
+
+	return 0;
+
+err:
+	return err;
+}
+
+/*
+ * Pat the watchdog whenever device is written to.
+ */
+static ssize_t ls1024a_wdt_write(struct file *file, const char *buf, size_t len, loff_t *ppos)
+{
+	if (len) {
+		if (!nowayout) {
+			size_t i;
+			char c;
+
+			/* in case it was set long ago */
+			expect_close = 0;
+
+			for (i = 0; i != len; i++) {
+				if (get_user(c, buf + i))
+					return -EFAULT;
+
+				if (c == 'V')
+					expect_close = 42;
+			}
+		}
+
+		ls1024a_wdt_pet_watchdog_virtual();
+	}
+
+	return len;
+}
+
+static const struct file_operations ls1024a_wdt_fops = {
+	.owner		= THIS_MODULE,
+	.llseek		= no_llseek,
+	.unlocked_ioctl	= ls1024a_wdt_ioctl,
+	.open		= ls1024a_wdt_open,
+	.release	= ls1024a_wdt_release,
+	.write		= ls1024a_wdt_write,
+};
+
+static struct miscdevice ls1024a_wdt_miscdev = {
+	.minor		= WATCHDOG_MINOR,
+	.name		= WDT_NAME,
+	.fops		= &ls1024a_wdt_fops,
+};
+
+static int ls1024a_wdt_probe(struct platform_device *pdev)
+{
+	int res;
+	struct resource *iores_mem;
+	struct device *dev = &pdev->dev;
+
+	if (ls1024a_wdt_miscdev.parent)
+		return -EBUSY;
+
+	spin_lock_init(&wdt_lock);
+
+	axi_clk = devm_clk_get(dev, NULL);
+	if (IS_ERR(axi_clk)) {
+		dev_err(dev, "unable to obtain AXI clock: %ld\n", PTR_ERR(axi_clk));
+		return -ENODEV;
+	}
+
+	res = clk_prepare_enable(axi_clk);
+	if (res) {
+		dev_err(dev, "failed to enable AXI clock\n");
+		goto err_clk;
+	}
+
+	LS1024A_AHBCLK = clk_get_rate(axi_clk);
+
+	iores_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!iores_mem) {
+		dev_err(dev, "unable to obtain IORESOURCE_MEM\n");
+		goto err_mem;
+	}
+
+	ls1024a_wdt_base = devm_ioremap_resource(dev, iores_mem);
+	if (IS_ERR(ls1024a_wdt_base)) {
+		dev_err(dev, "devm_ioremap_resource failed: %ld\n", PTR_ERR(ls1024a_wdt_base));
+		goto err_mem;
+	}
+
+	clk_rst_map = syscon_regmap_lookup_by_phandle(dev->of_node, "regmap");
+	if (IS_ERR(clk_rst_map)) {
+		dev_err(dev, "unable to obtain regmap: %ld\n", PTR_ERR(clk_rst_map));
+		goto err_misc;
+	}
+
+	setup_timer(&wdt_timer, ls1024a_timer_tick, 0L);
+
+	ls1024a_wdt_miscdev.parent = dev;
+	ls1024a_wdt_config();
+
+	res = misc_register(&ls1024a_wdt_miscdev);
+	if (res)
+		goto err_misc;
+
+	/* check that the heartbeat value is within range; if not reset to the default */
+	if (ls1024a_wdt_set_heartbeat(wd_heartbeat)) {
+		ls1024a_wdt_set_heartbeat(WDT_DEFAULT_TIMEOUT);
+
+		dev_err(dev, "wd_heartbeat value is out of range: 1..%lu, using %d\n",
+			WDT_MAX_TIMEOUT, WDT_DEFAULT_TIMEOUT);
+	}
+
+	/* check that the time value is within range; if not reset to the default */
+	if (ls1024a_wdt_settimeout(wd_time)) {
+		ls1024a_wdt_settimeout(WDT_DEFAULT_TIMEOUT);
+
+		dev_err(dev, "wd_time value is out of range: 1..%d, using %d\n",
+			WDT_MAX_TIME, WDT_DEFAULT_TIME);
+	}
+
+	return 0;
+
+err_misc:
+	devm_iounmap(dev, ls1024a_wdt_base);
+err_mem:
+	clk_disable_unprepare(axi_clk);
+err_clk:
+	devm_clk_put(dev, axi_clk);
+
+	return res;
+}
+
+static int ls1024a_wdt_remove(struct platform_device *pdev)
+{
+	int res;
+
+	res = misc_deregister(&ls1024a_wdt_miscdev);
+	if (!res)
+		ls1024a_wdt_miscdev.parent = NULL;
+
+	devm_iounmap(&pdev->dev, ls1024a_wdt_base);
+	clk_disable_unprepare(axi_clk);
+	devm_clk_put(&pdev->dev, axi_clk);
+
+	return res;
+}
+
+static const struct of_device_id ls1024a_wdt_of_match[] = {
+	{ .compatible = "fsl,ls1024a-wdt", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, ls1024a_wdt_of_match);
+
+static struct platform_driver ls1024a_wdt_driver = {
+	.driver	= {
+		.name = WDT_NAME,
+		.of_match_table = ls1024a_wdt_of_match,
+	},
+	.probe = ls1024a_wdt_probe,
+	.remove = ls1024a_wdt_remove,
+};
+
+module_platform_driver(ls1024a_wdt_driver);
+
+MODULE_AUTHOR("Mindspeed Technologies, Inc.");
+MODULE_DESCRIPTION("Watchdog driver for Freescale QorIQ LS1024A devices");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 145d6ba..d2962a5 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -1032,6 +1032,7 @@
 #define EXT4_MOUNT_POSIX_ACL		0x08000	/* POSIX Access Control Lists */
 #define EXT4_MOUNT_NO_AUTO_DA_ALLOC	0x10000	/* No auto delalloc mapping */
 #define EXT4_MOUNT_BARRIER		0x20000 /* Use block barriers */
+#define EXT4_MOUNT_PIN_BLOCK_BITMAPS	0x40000 /* Pin block bitmaps */
 #define EXT4_MOUNT_QUOTA		0x80000 /* Some quota option set */
 #define EXT4_MOUNT_USRQUOTA		0x100000 /* "old" user quota */
 #define EXT4_MOUNT_GRPQUOTA		0x200000 /* "old" group quota */
@@ -1342,6 +1343,9 @@
 	unsigned long s_mb_last_group;
 	unsigned long s_mb_last_start;
 
+	/* gid that's allowed to see stale data via falloc flag. */
+	kgid_t nohide_stale_gid;
+
 	/* stats for buddy allocator */
 	atomic_t s_bal_reqs;	/* number of reqs with len > 1 */
 	atomic_t s_bal_success;	/* we found long enough chunks */
@@ -2680,6 +2684,7 @@
 #ifdef DOUBLE_CHECK
 	void            *bb_bitmap;
 #endif
+	struct buffer_head *bb_bh;
 	struct rw_semaphore alloc_sem;
 	ext4_grpblk_t	bb_counters[];	/* Nr of free power-of-two-block
 					 * regions, index is order.
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index a3276bf..e8e57ff 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -4685,6 +4685,9 @@
 	if (len <= EXT_UNWRITTEN_MAX_LEN)
 		flags |= EXT4_GET_BLOCKS_NO_NORMALIZE;
 
+	if (mode & FALLOC_FL_NO_HIDE_STALE)
+		flags &= ~EXT4_GET_BLOCKS_UNWRIT_EXT;
+
 	/*
 	 * credits to insert 1 extent into extent tree
 	 */
@@ -4907,6 +4910,7 @@
 	int flags;
 	ext4_lblk_t lblk;
 	unsigned int blkbits = inode->i_blkbits;
+	struct ext4_sb_info *sbi;
 
 	/*
 	 * Encrypted inodes can't handle collapse range or insert
@@ -4924,9 +4928,20 @@
 
 	/* Return error if mode is not supported */
 	if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE |
+		     FALLOC_FL_NO_HIDE_STALE |
 		     FALLOC_FL_COLLAPSE_RANGE | FALLOC_FL_ZERO_RANGE))
 		return -EOPNOTSUPP;
 
+	sbi = EXT4_SB(inode->i_sb);
+	/* Must have RAWIO to see stale data. */
+	if ((mode & FALLOC_FL_NO_HIDE_STALE) &&
+	    !in_egroup_p(sbi->nohide_stale_gid))
+		return -EACCES;
+
+	/* preallocation to directories is currently not supported */
+	if (S_ISDIR(inode->i_mode))
+		return -ENODEV;
+
 	if (mode & FALLOC_FL_PUNCH_HOLE)
 		return ext4_punch_hole(inode, offset, len);
 
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 4723d8b..cdabf73 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -730,6 +730,33 @@
 }
 
 /*
+ * Manage pinning (and unpinning) block bitmaps
+ */
+static void ext4_pin_block_bitmaps(struct super_block *sb)
+{
+	ext4_group_t i, ngroups = ext4_get_groups_count(sb);
+	struct ext4_group_info *grinfo;
+
+	for (i = 0; i < ngroups; i++) {
+		grinfo = ext4_get_group_info(sb, i);
+		if (!grinfo->bb_bh)
+			grinfo->bb_bh = ext4_read_block_bitmap(sb, i);
+	}
+}
+
+static void ext4_unpin_block_bitmaps(struct super_block *sb)
+{
+	ext4_group_t i, ngroups = ext4_get_groups_count(sb);
+	struct ext4_group_info *grinfo;
+
+	for (i = 0; i < ngroups; i++) {
+		grinfo = ext4_get_group_info(sb, i);
+		brelse(grinfo->bb_bh);
+		grinfo->bb_bh = NULL;
+	}
+}
+
+/*
  * Release the journal device
  */
 static void ext4_blkdev_put(struct block_device *bdev)
@@ -792,6 +819,7 @@
 	ext4_es_unregister_shrinker(sbi);
 	del_timer_sync(&sbi->s_err_report);
 	ext4_release_system_zone(sb);
+	ext4_unpin_block_bitmaps(sb);
 	ext4_mb_release(sb);
 	ext4_ext_release(sb);
 	ext4_xattr_put_super(sb);
@@ -1147,6 +1175,8 @@
 	Opt_no_mbcache,
 	Opt_discard, Opt_nodiscard, Opt_init_itable, Opt_noinit_itable,
 	Opt_max_dir_size_kb, Opt_nojournal_checksum,
+	Opt_nohide_stale_gid,
+	Opt_pin_block_bitmaps, Opt_nopin_block_bitmaps,
 };
 
 static const match_table_t tokens = {
@@ -1228,6 +1258,9 @@
 	{Opt_noinit_itable, "noinit_itable"},
 	{Opt_max_dir_size_kb, "max_dir_size_kb=%u"},
 	{Opt_test_dummy_encryption, "test_dummy_encryption"},
+	{Opt_nohide_stale_gid, "nohide_stale_gid=%u"},
+	{Opt_pin_block_bitmaps, "pin_block_bitmaps"},
+	{Opt_nopin_block_bitmaps, "nopin_block_bitmaps"},
 	{Opt_removed, "check=none"},	/* mount option from ext2/3 */
 	{Opt_removed, "nocheck"},	/* mount option from ext2/3 */
 	{Opt_removed, "reservation"},	/* mount option from ext2/3 */
@@ -1431,6 +1464,9 @@
 	{Opt_jqfmt_vfsv1, QFMT_VFS_V1, MOPT_QFMT},
 	{Opt_max_dir_size_kb, 0, MOPT_GTE0},
 	{Opt_test_dummy_encryption, 0, MOPT_GTE0},
+	{Opt_nohide_stale_gid, 0, MOPT_GTE0},
+	{Opt_pin_block_bitmaps, EXT4_MOUNT_PIN_BLOCK_BITMAPS, MOPT_SET},
+	{Opt_nopin_block_bitmaps, EXT4_MOUNT_PIN_BLOCK_BITMAPS, MOPT_CLEAR},
 	{Opt_err, 0, 0}
 };
 
@@ -1538,6 +1574,13 @@
 		sbi->s_li_wait_mult = arg;
 	} else if (token == Opt_max_dir_size_kb) {
 		sbi->s_max_dir_size_kb = arg;
+	} else if (token == Opt_nohide_stale_gid) {
+		gid = make_kgid(&init_user_ns, arg);
+		if (!gid_valid(gid)) {
+			ext4_msg(sb, KERN_ERR, "Invalid gid value %d", arg);
+			return -1;
+		}
+		sbi->nohide_stale_gid = gid;
 	} else if (token == Opt_stripe) {
 		sbi->s_stripe = arg;
 	} else if (token == Opt_resuid) {
@@ -1860,6 +1903,10 @@
 		SEQ_OPTS_PRINT("init_itable=%u", sbi->s_li_wait_mult);
 	if (nodefs || sbi->s_max_dir_size_kb)
 		SEQ_OPTS_PRINT("max_dir_size_kb=%u", sbi->s_max_dir_size_kb);
+	if (gid_valid(sbi->nohide_stale_gid))
+		SEQ_OPTS_PRINT("nohide_stale_gid=%u",
+				from_kgid_munged(&init_user_ns,
+						 sbi->nohide_stale_gid));
 
 	ext4_show_quota_options(seq, sb);
 	return 0;
@@ -3630,6 +3677,9 @@
 	sbi->s_min_batch_time = EXT4_DEF_MIN_BATCH_TIME;
 	sbi->s_max_batch_time = EXT4_DEF_MAX_BATCH_TIME;
 
+	/* Default to having no-hide-stale disabled. */
+	sbi->nohide_stale_gid = INVALID_GID;
+
 	if ((def_mount_opts & EXT4_DEFM_NOBARRIER) == 0)
 		set_opt(sb, BARRIER);
 
@@ -4310,6 +4360,8 @@
 				 "the device does not support discard");
 	}
 
+	if (test_opt(sb, PIN_BLOCK_BITMAPS))
+		ext4_pin_block_bitmaps(sb);
 	ext4_msg(sb, KERN_INFO, "mounted filesystem with%s. "
 		 "Opts: %s%s%s", descr, sbi->s_es->s_mount_opts,
 		 *sbi->s_es->s_mount_opts ? "; " : "", orig_data);
@@ -5137,6 +5189,11 @@
 	}
 #endif
 
+	if (test_opt(sb, PIN_BLOCK_BITMAPS))
+		ext4_pin_block_bitmaps(sb);
+	if (!test_opt(sb, PIN_BLOCK_BITMAPS))
+		ext4_unpin_block_bitmaps(sb);
+
 	*flags = (*flags & ~MS_LAZYTIME) | (sb->s_flags & MS_LAZYTIME);
 	ext4_msg(sb, KERN_INFO, "re-mounted. Opts: %s", orig_data);
 	kfree(orig_data);
diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c
index 2dc8ce4..b072f87 100644
--- a/fs/ubifs/file.c
+++ b/fs/ubifs/file.c
@@ -170,6 +170,7 @@
 		}
 		ubifs_err(c, "cannot read page %lu of inode %lu, error %d",
 			  page->index, inode->i_ino, err);
+		ubifs_ro_mode(c, err);
 		goto error;
 	}
 
diff --git a/include/dt-bindings/reset-controller/ls1024a-resets.h b/include/dt-bindings/reset-controller/ls1024a-resets.h
new file mode 100644
index 0000000..8af45e2
--- /dev/null
+++ b/include/dt-bindings/reset-controller/ls1024a-resets.h
@@ -0,0 +1,48 @@
+/*
+ * This header provides constants for the reset controller based peripheral
+ * powerdown requests on the Freescale LS1024A SoC.
+ */
+#ifndef _DT_BINDINGS_RESET_CONTROLLER_LS1024A
+#define _DT_BINDINGS_RESET_CONTROLLER_LS1024A
+
+#define LS1024A_AXI_RTC_RESET		1
+#define LS1024A_AXI_LEGACY_SPI_RESET	2
+#define LS1024A_AXI_I2C_RESET		3
+#define LS1024A_AXI_DMA_RESET		4
+#define LS1024A_AXI_FAST_UART_RESET	5
+#define LS1024A_AXI_FAST_SPI_RESET	6
+#define LS1024A_AXI_TDM_RESET		7
+#define LS1024A_PFE_SYS_RESET		8
+#define LS1024A_AXI_IPSEC_EAPE_RESET	9
+#define LS1024A_AXI_IPSEC_SPACC_RESET	10
+#define LS1024A_AXI_DPI_CIE_RESET	11
+#define LS1024A_AXI_DPI_DECOMP_RESET	12
+#define LS1024A_AXI_USB1_RESET		13 /* USB controller1, AXI Clock Domain reset control */
+#define LS1024A_UTMI_USB1_RESET  	14 /* USB controller1,UTMI Clock Domain reset control*/
+#define LS1024A_USB1_PHY_RESET		15 /* USB PHY1 Reset control */
+#define LS1024A_AXI_USB0_RESET		16 /* USB controller0, AXI Clock Domain reset control*/
+#define LS1024A_UTMI_USB0_RESET  	17 /* USB controller0. UTMI Clock Domain reset control */
+#define LS1024A_USB0_PHY_RESET		18 /* USB PHY0 Reset Control */
+#define LS1024A_AXI_SATA_RESET		19 /* SATA controller AXI Clock Domain Control Both for SATA 0/1*/
+#define LS1024A_SERDES_SATA0_RESET	20 /* SATA serdes Controller0  TX,Core Logic and RX clock domain control*/
+#define LS1024A_SERDES_SATA1_RESET	21 /* SATA serdes Controller1  TX,Core Logic and RX clock domain control*/
+#define LS1024A_AXI_PCIE1_RESET		22 /* PCIE Controller1,AXI Clock Domain reset control*/
+#define LS1024A_SERDES_PCIE1_RESET	23 /* PCIE serdes Controller1  Striky register and power register*/
+#define LS1024A_AXI_PCIE0_RESET		24 /* PCIE Controller0,AXI Clock Domain reset control*/
+#define LS1024A_SERDES_PCIE0_RESET	25 /* PCIE Controller1,TX,Core Logic and RX clock domain control*/
+#define LS1024A_PFE_CORE_RESET		26
+#define LS1024A_IPSEC_EAPE_CORE_RESET	27
+#define LS1024A_GEMTX_RESET		28
+#define LS1024A_L2CC_RESET		29
+#define LS1024A_DECT_RESET		30
+#define LS1024A_DDR_CNTLR_RESET		31
+#define LS1024A_DDR_PHY_RESET		32
+#define LS1024A_SERDES0_RESET		33
+#define LS1024A_SERDES1_RESET		34
+#define LS1024A_SERDES2_RESET		35
+#define LS1024A_SGMII_RESET		36
+#define LS1024A_SATA_PMU_RESET		37
+#define LS1024A_SATA_OOB_RESET		38
+#define LS1024A_TDMNTG_RESET		39
+
+#endif /* _DT_BINDINGS_RESET_CONTROLLER_LS1024A */
diff --git a/include/linux/antirebootloop.h b/include/linux/antirebootloop.h
new file mode 100644
index 0000000..78b48ff
--- /dev/null
+++ b/include/linux/antirebootloop.h
@@ -0,0 +1,28 @@
+#ifndef __ANTIREBOOTLOOP_H
+#define __ANTIREBOOTLOOP_H
+
+#ifdef CONFIG_ANTIREBOOTLOOP
+
+#include <linux/types.h>
+
+#define ARL_MAGIC 0x1c93f311
+#define ARL_KERNEL_VERSION 1
+
+struct arl_marker {
+	u32 magic[16]; /* Use a 64 byte magic value to increase the likelihood
+			  of detecting bit flips. magic[i] = ARL_MAGIC + i for
+			  0<i<15. */
+	u32 counter; /* bootloader increments this counter on every boot
+			attempt.  Kernel resets it to 0. */
+	/* The term version refers to this anti-reboot-loop mechanism not to
+	 * the barebox or kernel version number */
+	u32 bootloader_version; /* ARL version supported by bootloader */
+	u32 kernel_version; /* ARL version supported by bootloader. Filled in
+			       by kernel. */
+};
+
+phys_addr_t get_antirebootloop_ptr(void);
+
+#endif
+
+#endif
diff --git a/include/linux/falloc.h b/include/linux/falloc.h
index 9961110..aa57335 100644
--- a/include/linux/falloc.h
+++ b/include/linux/falloc.h
@@ -23,6 +23,7 @@
 
 #define	FALLOC_FL_SUPPORTED_MASK	(FALLOC_FL_KEEP_SIZE |		\
 					 FALLOC_FL_PUNCH_HOLE |		\
+					 FALLOC_FL_NO_HIDE_STALE |	\
 					 FALLOC_FL_COLLAPSE_RANGE |	\
 					 FALLOC_FL_ZERO_RANGE |		\
 					 FALLOC_FL_INSERT_RANGE)
diff --git a/include/linux/i2c-mux.h b/include/linux/i2c-mux.h
index b5f9a00..f6f8230 100644
--- a/include/linux/i2c-mux.h
+++ b/include/linux/i2c-mux.h
@@ -42,6 +42,23 @@
 				int (*deselect) (struct i2c_adapter *,
 						 void *mux_dev, u32 chan_id));
 
+/*
+ * Called to create a i2c bus on a multiplexed bus segment.
+ * The mux_dev and chan_id parameters are passed to the select
+ * and deselect callback functions to perform hardware-specific
+ * mux control. Additionaly the number of i2c transfers is passed
+ * to the select callback.
+ */
+struct i2c_adapter *i2c_add_mux_adapter_num(struct i2c_adapter *parent,
+				struct device *mux_dev,
+				void *mux_priv, u32 force_nr, u32 chan_id,
+				unsigned int class,
+				int (*select_num) (struct i2c_adapter *,
+						   void *mux_dev, u32 chan_id,
+					           int num),
+				int (*deselect) (struct i2c_adapter *,
+						 void *mux_dev, u32 chan_id));
+
 void i2c_del_mux_adapter(struct i2c_adapter *adap);
 
 #endif /* __KERNEL__ */
diff --git a/include/linux/if_bridge.h b/include/linux/if_bridge.h
index dad8b00..96d3c6f 100644
--- a/include/linux/if_bridge.h
+++ b/include/linux/if_bridge.h
@@ -56,6 +56,21 @@
 			       struct list_head *br_ip_list);
 bool br_multicast_has_querier_anywhere(struct net_device *dev, int proto);
 bool br_multicast_has_querier_adjacent(struct net_device *dev, int proto);
+#if defined(CONFIG_ARCH_COMCERTO)
+struct brevent_fdb_update{
+	char * mac_addr;
+	struct net_device * dev;
+};
+
+enum brevent_notif_type {
+	BREVENT_PORT_DOWN = 1,	/* arg is struct net_device ptr */
+	BREVENT_FDB_UPDATE	/* arg is struct brevent_fdb_update ptr */
+};
+
+int register_brevent_notifier(struct notifier_block *nb);
+int unregister_brevent_notifier(struct notifier_block *nb);
+int call_brevent_notifiers(unsigned long val, void *v);
+#endif
 #else
 static inline int br_multicast_list_adjacent(struct net_device *dev,
 					     struct list_head *br_ip_list)
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index 12b75f3..3b6564d 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -1026,4 +1026,9 @@
 
 /* get timing characteristics from ONFI timing mode. */
 const struct nand_sdr_timings *onfi_async_timing_mode_to_sdr_timings(int mode);
+
+int nand_check_erased_ecc_chunk(void *data, int datalen,
+				void *ecc, int ecclen,
+				void *extraoob, int extraooblen,
+				int threshold);
 #endif /* __LINUX_MTD_NAND_H */
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index ddd47c3..73a5445 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -1550,6 +1550,9 @@
 	netdev_features_t	hw_enc_features;
 	netdev_features_t	mpls_features;
 
+	/* This is pointing to network device that offload WiFi data to PFE */
+	struct net_device       *wifi_offload_dev;
+
 	int			ifindex;
 	int			group;
 
@@ -2201,6 +2204,7 @@
 	return dev_queue_xmit_sk(skb->sk, skb);
 }
 int dev_queue_xmit_accel(struct sk_buff *skb, void *accel_priv);
+int original_dev_queue_xmit(struct sk_buff *skb);
 int register_netdevice(struct net_device *dev);
 void unregister_netdevice_queue(struct net_device *dev, struct list_head *head);
 void unregister_netdevice_many(struct list_head *head);
@@ -2983,6 +2987,7 @@
 {
 	return netif_receive_skb_sk(skb->sk, skb);
 }
+int capture_receive_skb(struct sk_buff *skb);
 gro_result_t napi_gro_receive(struct napi_struct *napi, struct sk_buff *skb);
 void napi_gro_flush(struct napi_struct *napi, bool flush_old);
 struct sk_buff *napi_get_frags(struct napi_struct *napi);
diff --git a/include/linux/netfilter/netfilter_helpers.h b/include/linux/netfilter/netfilter_helpers.h
new file mode 100644
index 0000000..903f374
--- /dev/null
+++ b/include/linux/netfilter/netfilter_helpers.h
@@ -0,0 +1,133 @@
+/*
+ * Helpers for netfiler modules.  This file provides implementations for basic
+ * functions such as strncasecmp(), etc.
+ *
+ * gcc will warn for defined but unused functions, so we only include the
+ * functions requested.  The following macros are used:
+ *   NF_NEED_STRNCASECMP        nf_strncasecmp()
+ *   NF_NEED_STRTOU16           nf_strtou16()
+ *   NF_NEED_STRTOU32           nf_strtou32()
+ */
+#ifndef _NETFILTER_HELPERS_H
+#define _NETFILTER_HELPERS_H
+
+/* Only include these functions for kernel code. */
+#ifdef __KERNEL__
+
+#include <linux/ctype.h>
+#define iseol(c) ( (c) == '\r' || (c) == '\n' )
+
+/*
+ * The standard strncasecmp()
+ */
+#ifdef NF_NEED_STRNCASECMP
+static int
+nf_strncasecmp(const char* s1, const char* s2, u_int32_t len)
+{
+    if (s1 == NULL || s2 == NULL)
+    {
+        if (s1 == NULL && s2 == NULL)
+        {
+            return 0;
+        }
+        return (s1 == NULL) ? -1 : 1;
+    }
+    while (len > 0 && tolower(*s1) == tolower(*s2))
+    {
+        len--;
+        s1++;
+        s2++;
+    }
+    return ( (len == 0) ? 0 : (tolower(*s1) - tolower(*s2)) );
+}
+#endif /* NF_NEED_STRNCASECMP */
+
+/*
+ * Parse a string containing a 16-bit unsigned integer.
+ * Returns the number of chars used, or zero if no number is found.
+ */
+#ifdef NF_NEED_STRTOU16
+static int
+nf_strtou16(const char* pbuf, u_int16_t* pval)
+{
+    int n = 0;
+
+    *pval = 0;
+    while (isdigit(pbuf[n]))
+    {
+        *pval = (*pval * 10) + (pbuf[n] - '0');
+        n++;
+    }
+
+    return n;
+}
+#endif /* NF_NEED_STRTOU16 */
+
+/*
+ * Parse a string containing a 32-bit unsigned integer.
+ * Returns the number of chars used, or zero if no number is found.
+ */
+#ifdef NF_NEED_STRTOU32
+static int
+nf_strtou32(const char* pbuf, u_int32_t* pval)
+{
+    int n = 0;
+
+    *pval = 0;
+    while (pbuf[n] >= '0' && pbuf[n] <= '9')
+    {
+        *pval = (*pval * 10) + (pbuf[n] - '0');
+        n++;
+    }
+
+    return n;
+}
+#endif /* NF_NEED_STRTOU32 */
+
+/*
+ * Given a buffer and length, advance to the next line and mark the current
+ * line.
+ */
+#ifdef NF_NEED_NEXTLINE
+static int
+nf_nextline(char* p, uint len, uint* poff, uint* plineoff, uint* plinelen)
+{
+    uint    off = *poff;
+    uint    physlen = 0;
+
+    if (off >= len)
+    {
+        return 0;
+    }
+
+    while (p[off] != '\n')
+    {
+        if (len-off <= 1)
+        {
+            return 0;
+        }
+
+        physlen++;
+        off++;
+    }
+
+    /* if we saw a crlf, physlen needs adjusted */
+    if (physlen > 0 && p[off] == '\n' && p[off-1] == '\r')
+    {
+        physlen--;
+    }
+
+    /* advance past the newline */
+    off++;
+
+    *plineoff = *poff;
+    *plinelen = physlen;
+    *poff = off;
+
+    return 1;
+}
+#endif /* NF_NEED_NEXTLINE */
+
+#endif /* __KERNEL__ */
+
+#endif /* _NETFILTER_HELPERS_H */
diff --git a/include/linux/netfilter/netfilter_mime.h b/include/linux/netfilter/netfilter_mime.h
new file mode 100644
index 0000000..7eeb183
--- /dev/null
+++ b/include/linux/netfilter/netfilter_mime.h
@@ -0,0 +1,89 @@
+/*
+ * MIME functions for netfilter modules.  This file provides implementations
+ * for basic MIME parsing.  MIME headers are used in many protocols, such as
+ * HTTP, RTSP, SIP, etc.
+ *
+ * gcc will warn for defined but unused functions, so we only include the
+ * functions requested.  The following macros are used:
+ *   NF_NEED_MIME_NEXTLINE      nf_mime_nextline()
+ */
+#ifndef _NETFILTER_MIME_H
+#define _NETFILTER_MIME_H
+
+/* Only include these functions for kernel code. */
+#ifdef __KERNEL__
+
+#include <linux/ctype.h>
+
+/*
+ * Given a buffer and length, advance to the next line and mark the current
+ * line.  If the current line is empty, *plinelen will be set to zero.  If
+ * not, it will be set to the actual line length (including CRLF).
+ *
+ * 'line' in this context means logical line (includes LWS continuations).
+ * Returns 1 on success, 0 on failure.
+ */
+#ifdef NF_NEED_MIME_NEXTLINE
+static int
+nf_mime_nextline(char* p, uint len, uint* poff, uint* plineoff, uint* plinelen)
+{
+    uint    off = *poff;
+    uint    physlen = 0;
+    int     is_first_line = 1;
+
+    if (off >= len)
+    {
+        return 0;
+    }
+
+    do
+    {
+        while (p[off] != '\n')
+        {
+            if (len-off <= 1)
+            {
+                return 0;
+            }
+
+            physlen++;
+            off++;
+        }
+
+        /* if we saw a crlf, physlen needs adjusted */
+        if (physlen > 0 && p[off] == '\n' && p[off-1] == '\r')
+        {
+            physlen--;
+        }
+
+        /* advance past the newline */
+        off++;
+
+        /* check for an empty line */
+        if (physlen == 0)
+        {
+            break;
+        }
+
+        /* check for colon on the first physical line */
+        if (is_first_line)
+        {
+            is_first_line = 0;
+            if (memchr(p+(*poff), ':', physlen) == NULL)
+            {
+                return 0;
+            }
+        }
+    }
+    while (p[off] == ' ' || p[off] == '\t');
+
+    *plineoff = *poff;
+    *plinelen = (physlen == 0) ? 0 : (off - *poff);
+    *poff = off;
+
+    return 1;
+}
+#endif /* NF_NEED_MIME_NEXTLINE */
+
+#endif /* __KERNEL__ */
+
+#endif /* _NETFILTER_MIME_H */
diff --git a/include/linux/netfilter/nf_conntrack_rtsp.h b/include/linux/netfilter/nf_conntrack_rtsp.h
new file mode 100644
index 0000000..7965755
--- /dev/null
+++ b/include/linux/netfilter/nf_conntrack_rtsp.h
@@ -0,0 +1,72 @@
+/*
+ * RTSP extension for IP connection tracking.
+ * (C) 2003 by Tom Marshall <tmarshall at real.com>
+ * based on ip_conntrack_irc.h
+ *
+ *      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.
+ *
+ * 2013-03-04: Il'inykh Sergey <sergeyi at inango-sw.com>. Inango Systems Ltd
+ *	- conditional compilation for kernel 3.7
+ *	- port mapping improvements
+*/
+#ifndef _IP_CONNTRACK_RTSP_H
+#define _IP_CONNTRACK_RTSP_H
+
+#include <linux/version.h>
+
+//#define IP_NF_RTSP_DEBUG 1
+#define IP_NF_RTSP_VERSION "0.7"
+
+#ifdef __KERNEL__
+/* port block types */
+typedef enum {
+    pb_single,  /* client_port=x */
+    pb_range,   /* client_port=x-y */
+    pb_discon   /* client_port=x/y (rtspbis) */
+} portblock_t;
+
+/* We record seq number and length of rtsp headers here, all in host order. */
+
+/*
+ * This structure is per expected connection.  It is a member of struct
+ * ip_conntrack_expect.  The TCP SEQ for the conntrack expect is stored
+ * there and we are expected to only store the length of the data which
+ * needs replaced.  If a packet contains multiple RTSP messages, we create
+ * one expected connection per message.
+ *
+ * We use these variables to mark the entire header block.  This may seem
+ * like overkill, but the nature of RTSP requires it.  A header may appear
+ * multiple times in a message.  We must treat two Transport headers the
+ * same as one Transport header with two entries.
+ */
+struct ip_ct_rtsp_expect
+{
+    u_int32_t   len;        /* length of header block */
+    portblock_t pbtype;     /* Type of port block that was requested */
+    u_int16_t   loport;     /* Port that was requested, low or first */
+    u_int16_t   hiport;     /* Port that was requested, high or second */
+#if 0
+    uint        method;     /* RTSP method */
+    uint        cseq;       /* CSeq from request */
+#endif
+};
+
+extern unsigned int (*nf_nat_rtsp_hook)(struct sk_buff *skb,
+					enum ip_conntrack_info ctinfo,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
+					unsigned int protoff,
+#endif
+					unsigned int matchoff,
+					unsigned int matchlen,
+					struct ip_ct_rtsp_expect *prtspexp,
+					struct nf_conntrack_expect *rtp_exp,
+					struct nf_conntrack_expect *rtcp_exp);
+
+#define RTSP_PORT   554
+
+#endif /* __KERNEL__ */
+
+#endif /* _IP_CONNTRACK_RTSP_H */
diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h
index 7b8e260..3d3b09e 100644
--- a/include/linux/rtnetlink.h
+++ b/include/linux/rtnetlink.h
@@ -13,6 +13,9 @@
 			u32 group, struct nlmsghdr *nlh, gfp_t flags);
 extern void rtnl_set_sk_err(struct net *net, u32 group, int error);
 extern int rtnetlink_put_metrics(struct sk_buff *skb, u32 *metrics);
+#ifdef CONFIG_ARCH_COMCERTO
+extern int rtnetlink_put_metrics_2(struct sk_buff *skb, u32 *metrics, struct dst_entry *dst);
+#endif
 extern int rtnl_put_cacheinfo(struct sk_buff *skb, struct dst_entry *dst,
 			      u32 id, long expires, u32 error);
 
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index ca2e26a..ad687a4 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -568,8 +568,8 @@
 				fclone:2,
 				peeked:1,
 				head_frag:1,
-				xmit_more:1;
-	/* one bit hole */
+				xmit_more:1,
+				dma_coherent:1;
 	kmemcheck_bitfield_end(flags1);
 
 	/* fields enclosed in headers_start/headers_end are copied
@@ -634,6 +634,7 @@
 	};
 	__u32			priority;
 	int			skb_iif;
+	int			skb_orig_iif;
 	__u32			hash;
 	__be16			vlan_proto;
 	__u16			vlan_tci;
@@ -777,6 +778,7 @@
 			    int node);
 struct sk_buff *__build_skb(void *data, unsigned int frag_size);
 struct sk_buff *build_skb(void *data, unsigned int frag_size);
+struct sk_buff *alloc_dma_coherent_skb(unsigned int size);
 static inline struct sk_buff *alloc_skb(unsigned int size,
 					gfp_t priority)
 {
diff --git a/include/linux/tcp.h b/include/linux/tcp.h
index e8bbf40..ac9833f 100644
--- a/include/linux/tcp.h
+++ b/include/linux/tcp.h
@@ -210,6 +210,9 @@
 	u32	mdev_max_us;	/* maximal mdev for the last rtt period	*/
 	u32	rttvar_us;	/* smoothed mdev_max			*/
 	u32	rtt_seq;	/* sequence number to update rttvar	*/
+	struct rtt_meas {
+		u32 rtt, ts;	/* RTT in usec and sampling time in jiffies. */
+	} rtt_min[3];
 
 	u32	packets_out;	/* Packets which are "in flight"	*/
 	u32	retrans_out;	/* Retransmitted packets out		*/
@@ -328,6 +331,10 @@
 	 * socket. Used to retransmit SYNACKs etc.
 	 */
 	struct request_sock *fastopen_rsk;
+
+/* TCP congestion control overrides */
+	u8	is_rate_controlled:1;	/* Is flow using externally set rate? */
+	u8	is_aggressive_rto:1;    /* Using aggressive retry timeout? */
 };
 
 enum tsq_flags {
diff --git a/include/linux/tty_driver.h b/include/linux/tty_driver.h
index 92e337c..1610524 100644
--- a/include/linux/tty_driver.h
+++ b/include/linux/tty_driver.h
@@ -296,7 +296,7 @@
 struct tty_driver {
 	int	magic;		/* magic number for this structure */
 	struct kref kref;	/* Reference management */
-	struct cdev *cdevs;
+	struct cdev **cdevs;
 	struct module	*owner;
 	const char	*driver_name;
 	const char	*name;
diff --git a/include/net/netfilter/nf_conntrack.h b/include/net/netfilter/nf_conntrack.h
index 095433b..00ab649 100644
--- a/include/net/netfilter/nf_conntrack.h
+++ b/include/net/netfilter/nf_conntrack.h
@@ -71,6 +71,14 @@
 #include <net/netfilter/ipv4/nf_conntrack_ipv4.h>
 #include <net/netfilter/ipv6/nf_conntrack_ipv6.h>
 
+#if defined(CONFIG_COMCERTO_FP)
+struct comcerto_fp_info {
+	int ifindex;
+	int iif;
+	u32 mark;
+};
+#endif
+
 struct nf_conn {
 	/* Usage count in here is 1 for hash table/destruct timer, 1 per skb,
 	 * plus 1 for any connection(s) we are `master' for
@@ -111,6 +119,10 @@
 	u_int32_t secmark;
 #endif
 
+#if defined(CONFIG_COMCERTO_FP)
+	struct comcerto_fp_info fp_info[IP_CT_DIR_MAX];
+#endif
+
 	/* Extensions */
 	struct nf_ct_ext *ext;
 
diff --git a/include/net/netfilter/nf_conntrack_core.h b/include/net/netfilter/nf_conntrack_core.h
index f2f0fa3..c934b6d 100644
--- a/include/net/netfilter/nf_conntrack_core.h
+++ b/include/net/netfilter/nf_conntrack_core.h
@@ -86,4 +86,8 @@
 
 extern spinlock_t nf_conntrack_expect_lock;
 
+#ifdef CONFIG_COMCERTO_FP
+#define COMCERTO_PERMANENT_TIMEOUT 1000
+#endif
+
 #endif /* _NF_CONNTRACK_CORE_H */
diff --git a/include/net/sock.h b/include/net/sock.h
index 4c4b21c..667eb99 100644
--- a/include/net/sock.h
+++ b/include/net/sock.h
@@ -713,6 +713,7 @@
 	SOCK_TIMESTAMPING_RX_SOFTWARE,  /* %SOF_TIMESTAMPING_RX_SOFTWARE */
 	SOCK_FASYNC, /* fasync() active */
 	SOCK_RXQ_OVFL,
+	SOCK_RXQ_ALLOC,
 	SOCK_ZEROCOPY, /* buffers from userspace */
 	SOCK_WIFI_STATUS, /* push wifi status to userspace */
 	SOCK_NOFCS, /* Tell NIC not to do the Ethernet FCS.
@@ -2160,6 +2161,14 @@
 		sk->sk_stamp = skb->tstamp;
 }
 
+void __sock_recv_alloc(struct msghdr *msg, struct sock *sk);
+
+static inline void sock_recv_alloc(struct msghdr *msg, struct sock *sk)
+{
+	if (sock_flag(sk, SOCK_RXQ_ALLOC))
+		__sock_recv_alloc(msg, sk);
+}
+
 void __sock_tx_timestamp(const struct sock *sk, __u8 *tx_flags);
 
 /**
diff --git a/include/net/tcp.h b/include/net/tcp.h
index 3d3a365..ed82d4e 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -279,6 +279,7 @@
 extern int sysctl_tcp_challenge_ack_limit;
 extern unsigned int sysctl_tcp_notsent_lowat;
 extern int sysctl_tcp_min_tso_segs;
+extern int sysctl_tcp_min_rtt_wlen;
 extern int sysctl_tcp_autocorking;
 extern int sysctl_tcp_invalid_ratelimit;
 
@@ -442,6 +443,7 @@
 		       struct tcp_options_received *opt_rx,
 		       int estab, struct tcp_fastopen_cookie *foc);
 const u8 *tcp_parse_md5sig_option(const struct tcphdr *th);
+extern bool tcp_rate_control(struct sock *sk);
 
 /*
  *	TCP v4 functions exported for the inet6 API
@@ -591,6 +593,12 @@
 void tcp_mtup_init(struct sock *sk);
 void tcp_init_buffer_space(struct sock *sk);
 
+/* Minimum RTT in usec. ~0 means not available. */
+static inline u32 tcp_min_rtt(const struct tcp_sock *tp)
+{
+	return min(tp->rtt_min[0].rtt, tp->srtt_us>>3);
+}
+
 static inline void tcp_bound_rto(const struct sock *sk)
 {
 	if (inet_csk(sk)->icsk_rto > TCP_RTO_MAX)
@@ -599,7 +607,10 @@
 
 static inline u32 __tcp_set_rto(const struct tcp_sock *tp)
 {
-	return usecs_to_jiffies((tp->srtt_us >> 3) + tp->rttvar_us);
+	return usecs_to_jiffies(
+		tp->is_aggressive_rto
+		? ((tcp_min_rtt(tp) + (tp->srtt_us >> 3)) / 2)
+		: ((tp->srtt_us >> 3) + tp->rttvar_us));
 }
 
 static inline void __tcp_fast_path_on(struct tcp_sock *tp, u32 snd_wnd)
@@ -828,6 +839,8 @@
 	void (*in_ack_event)(struct sock *sk, u32 flags);
 	/* new value of cwnd after loss (optional) */
 	u32  (*undo_cwnd)(struct sock *sk);
+	/* adjust fixed-rate stream rate control (optional) */
+	bool (*rate_control)(struct sock *sk, unsigned int current_mss);
 	/* hook for packet ack accounting (optional) */
 	void (*pkts_acked)(struct sock *sk, u32 num_acked, s32 rtt_us);
 	/* get info for inet_diag (optional) */
@@ -1549,7 +1562,8 @@
  */
 static inline bool tcp_stream_is_thin(struct tcp_sock *tp)
 {
-	return tp->packets_out < 4 && !tcp_in_initial_slowstart(tp);
+	return (tp->packets_out < 4 && !tcp_in_initial_slowstart(tp))
+		|| tp->is_rate_controlled;
 }
 
 /* /proc */
diff --git a/include/uapi/asm-generic/socket.h b/include/uapi/asm-generic/socket.h
index 5c15c2a..c996bd4 100644
--- a/include/uapi/asm-generic/socket.h
+++ b/include/uapi/asm-generic/socket.h
@@ -87,4 +87,6 @@
 #define SO_ATTACH_BPF		50
 #define SO_DETACH_BPF		SO_DETACH_FILTER
 
+#define SO_RXQ_ALLOC		101 /* non-upstreamed sockopt */
+
 #endif /* __ASM_GENERIC_SOCKET_H */
diff --git a/include/uapi/linux/if.h b/include/uapi/linux/if.h
index 9cf2394..5e71f42 100644
--- a/include/uapi/linux/if.h
+++ b/include/uapi/linux/if.h
@@ -87,6 +87,7 @@
 	IFF_LOWER_UP			= 1<<16, /* volatile */
 	IFF_DORMANT			= 1<<17, /* volatile */
 	IFF_ECHO			= 1<<18, /* volatile */
+	IFF_WIFI_OFLD			= 1<<19,
 };
 
 #define IFF_UP				IFF_UP
@@ -108,6 +109,7 @@
 #define IFF_LOWER_UP			IFF_LOWER_UP
 #define IFF_DORMANT			IFF_DORMANT
 #define IFF_ECHO			IFF_ECHO
+#define IFF_WIFI_OFLD			IFF_WIFI_OFLD
 
 #define IFF_VOLATILE	(IFF_LOOPBACK|IFF_POINTOPOINT|IFF_BROADCAST|IFF_ECHO|\
 		IFF_MASTER|IFF_SLAVE|IFF_RUNNING|IFF_LOWER_UP|IFF_DORMANT)
diff --git a/include/uapi/linux/netfilter/nf_conntrack_common.h b/include/uapi/linux/netfilter/nf_conntrack_common.h
index 319f471..106d15b 100644
--- a/include/uapi/linux/netfilter/nf_conntrack_common.h
+++ b/include/uapi/linux/netfilter/nf_conntrack_common.h
@@ -91,6 +91,10 @@
 	/* Conntrack got a helper explicitly attached via CT target. */
 	IPS_HELPER_BIT = 13,
 	IPS_HELPER = (1 << IPS_HELPER_BIT),
+
+	/* Connection  cannot expire */
+	IPS_PERMANENT_BIT = 24,
+	IPS_PERMANENT = (1 << IPS_PERMANENT_BIT),
 };
 
 /* Connection tracking event types */
diff --git a/include/uapi/linux/netfilter/nfnetlink_conntrack.h b/include/uapi/linux/netfilter/nfnetlink_conntrack.h
index acad6c5..5d344fc 100644
--- a/include/uapi/linux/netfilter/nfnetlink_conntrack.h
+++ b/include/uapi/linux/netfilter/nfnetlink_conntrack.h
@@ -53,6 +53,8 @@
 	CTA_MARK_MASK,
 	CTA_LABELS,
 	CTA_LABELS_MASK,
+	CTA_COMCERTO_FP_ORIG = 32,
+	CTA_COMCERTO_FP_REPLY,
 	__CTA_MAX
 };
 #define CTA_MAX (__CTA_MAX - 1)
@@ -260,4 +262,13 @@
 };
 #define CTA_STATS_EXP_MAX (__CTA_STATS_EXP_MAX - 1)
 
+enum ctattr_comcerto_fp {
+	CTA_COMCERTO_FP_UNSPEC,
+	CTA_COMCERTO_FP_MARK,
+	CTA_COMCERTO_FP_IFINDEX,
+	CTA_COMCERTO_FP_IIF,
+	__CTA_COMCERTO_FP_MAX
+};
+#define CTA_COMCERTO_FP_MAX (__CTA_COMCERTO_FP_MAX - 1)
+
 #endif /* _IPCONNTRACK_NETLINK_H */
diff --git a/include/uapi/linux/netlink.h b/include/uapi/linux/netlink.h
index 1a85940..9466ef1 100644
--- a/include/uapi/linux/netlink.h
+++ b/include/uapi/linux/netlink.h
@@ -28,9 +28,13 @@
 #define NETLINK_RDMA		20
 #define NETLINK_CRYPTO		21	/* Crypto layer */
 
+/* Freescale LS1024A specific. */
+#define NETLINK_FF              30
+#define NETLINK_L2FLOW          33
+
 #define NETLINK_INET_DIAG	NETLINK_SOCK_DIAG
 
-#define MAX_LINKS 32		
+#define MAX_LINKS 34
 
 struct sockaddr_nl {
 	__kernel_sa_family_t	nl_family;	/* AF_NETLINK	*/
diff --git a/init/Kconfig b/init/Kconfig
index dc24dec..6af0411 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -1275,6 +1275,17 @@
 
 	  If unsure, say N.
 
+config ANTIREBOOTLOOP
+	bool "Anti reboot loop"
+	depends on HAVE_MEMBLOCK
+	help
+	  The anti reboot loop feature is a communication protocol between the
+	  kernel and the bootloader. It is basically a counter at a pre-defined
+	  location in DRAM that is incremented by the bootloader and reset to
+	  zero by the kernel when some user space process determined that the
+	  system came up ok. This mechanism allows the bootloader to switch to
+	  a different kernel/rootfs image if the system keeps on rebooting.
+
 config BLK_DEV_INITRD
 	bool "Initial RAM filesystem and RAM disk (initramfs/initrd) support"
 	depends on BROKEN || !FRV
@@ -1445,6 +1456,27 @@
 	  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.
+	  Note: you must supply the log_buf_len= kernel parameter to
+	  activate this feature.
+
+config BOOTLOG_COPY
+	default n
+	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/Makefile b/kernel/Makefile
index 60c302c..952af40 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -98,6 +98,7 @@
 obj-$(CONFIG_JUMP_LABEL) += jump_label.o
 obj-$(CONFIG_CONTEXT_TRACKING) += context_tracking.o
 obj-$(CONFIG_TORTURE_TEST) += torture.o
+obj-$(CONFIG_ANTIREBOOTLOOP) += antirebootloop.o
 
 $(obj)/configs.o: $(obj)/config_data.h
 
diff --git a/kernel/antirebootloop.c b/kernel/antirebootloop.c
new file mode 100644
index 0000000..ba82c8a
--- /dev/null
+++ b/kernel/antirebootloop.c
@@ -0,0 +1,96 @@
+#include <linux/antirebootloop.h>
+#include <asm/memory.h>
+#include <linux/uaccess.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+
+static struct arl_marker *antirebootloop_marker = 0;
+static u32 bootloader_version;
+
+static int verify_magic(struct arl_marker *m) {
+	int i;
+	for (i=0; i < ARRAY_SIZE(m->magic); i++) {
+		if (m->magic[i] != ARL_MAGIC + i) return -1;
+	}
+	return 0;
+}
+
+static int arl_proc_show(struct seq_file *m, void *v)
+{
+	seq_printf(m, "counter: %u\n", antirebootloop_marker->counter);
+	seq_printf(m, "bootloader_version: %u\n",
+			bootloader_version);
+	seq_printf(m, "kernel_version: %u\n",
+			antirebootloop_marker->kernel_version);
+	return 0;
+}
+
+#define MAX_ARL_WRITE 8
+static ssize_t arl_write(struct file *file, const char __user *buffer,
+		       size_t count, loff_t *ppos)
+{
+	char kbuf[MAX_ARL_WRITE + 1];
+	u32 counter;
+
+	if (count > MAX_ARL_WRITE)
+		return -EINVAL;
+	if (copy_from_user(&kbuf, buffer, count))
+		return -EFAULT;
+	kbuf[MAX_ARL_WRITE] = '\0';
+
+	if (sscanf(kbuf, "%d", &counter) != 1)
+		return -EINVAL;
+
+	antirebootloop_marker->counter = counter;
+	pr_info("Set antirebootloop counter to %u\n",
+			antirebootloop_marker->counter);
+	return count;
+}
+
+static int arl_proc_open(struct inode *inode, struct  file *file) {
+	if (!antirebootloop_marker)
+		return -ENOENT;
+	return single_open(file, arl_proc_show, NULL);
+}
+
+static const struct file_operations arl_proc_fops = {
+	.open		= arl_proc_open,
+	.read		= seq_read,
+	.write		= arl_write,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static int __init init_marker(void) {
+	phys_addr_t a;
+	static struct arl_marker *m;
+	a = get_antirebootloop_ptr();
+	if (!a)
+		return 0;
+	m = __va(a);
+	if (!verify_magic(m)) {
+		pr_info("Found antirebootloop marker. "
+			"counter %u bootloader_version %u "
+			"kernel_version %u\n",
+			m->counter, m->bootloader_version, m->kernel_version);
+		/* We need to make a copy of bootloader_version and then reset
+		 * it to 0. Otherwise, we might read a stale value if someone
+		 * downgrades from an ARL to an old, non-ARL bootloader.
+		 * */
+		bootloader_version = m->bootloader_version;
+		m->bootloader_version = 0;
+		m->kernel_version = ARL_KERNEL_VERSION;
+		antirebootloop_marker = m;
+	}
+	return 0;
+}
+
+static int __init create_procfs_entry(void) {
+	pr_info("Creating ARL proc fs entry\n");
+	if (antirebootloop_marker)
+		proc_create("antirebootloop", 0, NULL, &arl_proc_fops);
+	return 0;
+}
+
+early_initcall(init_marker);
+core_initcall(create_procfs_entry);
diff --git a/kernel/hung_task.c b/kernel/hung_task.c
index e0f90c2..fc04209 100644
--- a/kernel/hung_task.c
+++ b/kernel/hung_task.c
@@ -108,6 +108,14 @@
 	 * Ok, the task did not get scheduled for more than 2 minutes,
 	 * complain:
 	 */
+	if (sysctl_hung_task_panic) {
+		/* If we are going to panic later, then run console_verbose()
+		 * now to ensure important debug information is printed to the
+		 * console. The information printed by panic() alone is less
+		 * useful.
+		 * */
+		console_verbose();
+	}
 	pr_err("INFO: task %s:%d blocked for more than %ld seconds.\n",
 		t->comm, t->pid, timeout);
 	pr_err("      %s %s %.*s\n",
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index 3c1aca0..dc54a34 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -46,8 +46,10 @@
 #include <linux/utsname.h>
 #include <linux/ctype.h>
 #include <linux/uio.h>
+#include <linux/crc32.h>
 
 #include <asm/uaccess.h>
+#include <asm/cacheflush.h>
 
 #define CREATE_TRACE_POINTS
 #include <trace/events/printk.h>
@@ -218,6 +220,9 @@
 	u16 text_len;		/* length of text buffer */
 	u16 dict_len;		/* length of dictionary buffer */
 	u8 facility;		/* syslog facility */
+#ifdef CONFIG_PRINTK_PERSIST
+	u32 crc;		/* the flags may change, so don't include them */
+#endif
 	u8 flags:5;		/* internal record flags */
 	u8 level:3;		/* syslog level */
 };
@@ -231,6 +236,99 @@
 
 #ifdef CONFIG_PRINTK
 DECLARE_WAIT_QUEUE_HEAD(log_wait);
+
+#define PREFIX_MAX		32
+#define LOG_LINE_MAX		(1024 - PREFIX_MAX)
+
+/* record buffer */
+#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)
+#define LOG_ALIGN 4
+#else
+#define LOG_ALIGN __alignof__(struct printk_log)
+#endif
+#define __LOG_BUF_LEN (1 << CONFIG_LOG_BUF_SHIFT)
+static char __log_buf[__LOG_BUF_LEN] __aligned(LOG_ALIGN);
+static char *log_buf = __log_buf;
+
+static int log_make_free_space(u32 msg_size);
+static int log_store(int facility, int level,
+		     enum log_flags flags, u64 ts_nsec,
+		     const char *dict, u16 dict_len,
+		     const char *text, u16 text_len);
+
+#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);
+
+static __init 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;
+	}
+        printk(KERN_INFO "bootlog: bootlog_size was 0.\n");
+	return NULL;
+}
+
+#define printable(c) (((c)=='\t') || ((c)=='\n') || (0x20<=(c) && (c)<=0x7e))
+
+static inline void __init copy_bootlog(struct bloghdr *blog_hdr)
+{
+	if (blog_hdr) {
+		char *blog_buf = (char *)(blog_hdr + 1);
+		int i,j;
+		char *tmp;
+		/* Strip out nonprintable characters from the bootlog. */
+		for (i=0,j=0; i < blog_hdr->offset; i++) {
+			if (printable(blog_buf[i])) {
+				blog_buf[j++] = blog_buf[i];
+			}
+		}
+		/* Loop over each line in the boot loader log, and insert them into the
+		 * kernel log one at a time.  Attempting to insert the entire thing fails due
+		 * to some logic that truncates long messages.
+		 */
+		tmp = &blog_buf[0];
+		for (i=0; i < j; i++) {
+			if (blog_buf[i] == '\n') {
+				/* Insert with facility=0, level=INFO, time=0, No dict. */
+				log_store(0, 6, LOG_NEWLINE, 0,
+					NULL, 0, tmp, &blog_buf[i] - tmp);
+				tmp = &blog_buf[i+1];
+			}
+		}
+
+		/* Insert any trailing line into the kernel buffer. */
+		if (tmp != &blog_buf[i]) {
+			log_store(0, 6, 0, 0,
+				NULL, 0, tmp, &blog_buf[i] - tmp);
+		}
+	}
+}
+
+static inline void __init free_bootlog(void)
+{
+	free_bootmem(bootlog_get_addr(), bootlog_get_size());
+}
+#endif
+
+#ifndef CONFIG_PRINTK_PERSIST
 /* the next printk record to read by syslog(READ) or /proc/kmsg */
 static u64 syslog_seq;
 static u32 syslog_idx;
@@ -254,20 +352,210 @@
 static u64 clear_seq;
 static u32 clear_idx;
 
-#define PREFIX_MAX		32
-#define LOG_LINE_MAX		(1024 - PREFIX_MAX)
-
-/* record buffer */
-#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)
-#define LOG_ALIGN 4
-#else
-#define LOG_ALIGN __alignof__(struct printk_log)
-#endif
-#define __LOG_BUF_LEN (1 << CONFIG_LOG_BUF_SHIFT)
-static char __log_buf[__LOG_BUF_LEN] __aligned(LOG_ALIGN);
-static char *log_buf = __log_buf;
 static u32 log_buf_len = __LOG_BUF_LEN;
 
+#else  /*  CONFIG_PRINTK_PERSIST */
+
+struct logbits {
+	/* Cache line 1 (32 bytes) */
+	int magic; /* needed to verify the memory across reboots */
+	u32 _log_buf_len; /* leading _ so they aren't replaced by #define */
+	u64 _log_first_seq;
+	u64 _log_next_seq;
+	u32 _log_first_idx;
+	u32 _log_next_idx;
+
+	/*
+	 * All fields up to but not including _console_seq are flushed from the
+	 * L1 and L2 caches at certain times to ensure that most of the printk
+	 * buffer can be recovered after a watchdog reset. If you make changes
+	 * to this struct, make sure to take the cache flushing code into
+	 * account. Search for __sync_cache_range_w.
+	 */
+
+	/* Cache line 2 (32 bytes) */
+	u64 _console_seq;
+	u64 _syslog_seq;
+	u64 _clear_seq;
+	u32 _console_idx;
+	u32 _syslog_idx;
+
+	/* Cache line 3 (first 16 bytes only) */
+	u32 _clear_idx;
+	enum log_flags _syslog_prev;
+	size_t _syslog_partial;
+	enum log_flags _console_prev;
+};
+
+static struct logbits __logbits = {
+	._log_buf_len = __LOG_BUF_LEN,
+};
+static struct logbits *logbits = &__logbits;
+
+#define log_buf_len    logbits->_log_buf_len
+#define syslog_seq     logbits->_syslog_seq
+#define syslog_idx     logbits->_syslog_idx
+#define syslog_prev    logbits->_syslog_prev
+#define syslog_partial logbits->_syslog_partial
+#define log_first_seq  logbits->_log_first_seq
+#define log_first_idx  logbits->_log_first_idx
+#define log_next_seq   logbits->_log_next_seq
+#define log_next_idx   logbits->_log_next_idx
+#define console_seq    logbits->_console_seq
+#define console_idx    logbits->_console_idx
+#define console_prev   logbits->_console_prev
+#define clear_seq      logbits->_clear_seq
+#define clear_idx      logbits->_clear_idx
+
+#define PERSIST_SEARCH_START 0
+#define PERSIST_SEARCH_END 0xfe000000
+#define PERSIST_SEARCH_JUMP (16*1024*1024)
+#define PERSIST_MAGIC 0xba5eba11
+
+/*
+ * arm uses one memory model, mips uses another
+ */
+static __init phys_addr_t physmem_reserve(phys_addr_t size) {
+#ifdef CONFIG_NO_BOOTMEM
+	phys_addr_t alloc;
+	alloc = memblock_find_in_range_node(size, SMP_CACHE_BYTES,
+			PERSIST_SEARCH_START, PERSIST_SEARCH_END,
+			NUMA_NO_NODE);
+	if (!alloc) return alloc;
+	if (memblock_reserve(alloc, size)) {
+		pr_err("printk_persist: memblock_reserve failed\n");
+		return 0;
+	}
+	return alloc;
+#else
+	unsigned long where;
+	for (where = PERSIST_SEARCH_END - size;
+	     where >= PERSIST_SEARCH_START && where <= PERSIST_SEARCH_END - size;
+	     where -= PERSIST_SEARCH_JUMP) {
+		if (reserve_bootmem(where, size, BOOTMEM_EXCLUSIVE))
+			continue;
+		else
+			return where;
+	}
+	return 0;
+#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 struct logbits *log_buf_alloc(unsigned long size, char **new_logbuf)
+{
+	char *buf;
+	phys_addr_t alloc;
+	unsigned long full_size = size + sizeof(struct logbits);
+	struct logbits *new_logbits;
+	u64 seq;
+	int idx, lost_entries, recovered_entries;
+	struct printk_log *prlog;
+	u32 curr_crc;
+
+	alloc = physmem_reserve(full_size);
+	if (alloc) {
+		const char *bad_header;
+		buf = phys_to_virt(alloc);
+		*new_logbuf = buf;
+		new_logbits = (void*)buf + size;
+		printk(KERN_INFO "printk_persist: memory reserved @ 0x%p size %lu\n",
+				buf, size);
+		printk(KERN_INFO "printk_persist: header:\n"
+			"          magic=0x%08x\n"
+			"    log_buf_len=%u\n"
+			"  log_first_idx=%u\n"
+			"   log_next_idx=%u\n"
+			"  log_first_seq=%llu\n"
+			"   log_next_seq=%llu\n",
+			new_logbits->magic,
+			new_logbits->_log_buf_len,
+			new_logbits->_log_first_idx,
+			new_logbits->_log_next_idx,
+			new_logbits->_log_first_seq,
+			new_logbits->_log_next_seq);
+		bad_header = NULL;
+		if (new_logbits->magic != PERSIST_MAGIC) {
+			bad_header = "incorrect magic value";
+		} else if (new_logbits->_log_buf_len != size) {
+			bad_header = "log_buf_len does not match";
+		} else if (new_logbits->_log_first_seq > new_logbits->_log_next_seq) {
+			bad_header = "log_first_seq > log_next_seq";
+		} else if (new_logbits->_log_next_seq - new_logbits->_log_first_seq >
+				size / sizeof(struct printk_log)) {
+			bad_header = "too many entries";
+		} else if (new_logbits->_log_first_idx > size) {
+			bad_header = "log_first_idx > size";
+		} else if (new_logbits->_log_next_idx > size) {
+			bad_header = "log_next_idx > size";
+		}
+		if (bad_header) {
+			printk(KERN_INFO "printk_persist: header invalid (%s), cleared.\n", bad_header);
+			/*
+			 * full_size includes struct logbits at the end of the
+			 * message buffer
+			 */
+			memset(buf, 0, full_size);
+			new_logbits->magic = PERSIST_MAGIC;
+			new_logbits->_log_buf_len = size;
+		} else {
+			int corruption = 0;
+			for (seq = new_logbits->_log_first_seq, idx = new_logbits->_log_first_idx;
+				seq < new_logbits->_log_next_seq; ++seq) {
+				prlog = (struct printk_log *) &buf[idx];
+				// Verify validity of this record. If its bad, then move the next
+				// pointer to be where this record is at.
+				curr_crc = crc32(~0, prlog, offsetof(struct printk_log, crc));
+				if (prlog->crc != curr_crc) {
+					lost_entries = (int)(new_logbits->_log_next_seq - seq);
+					recovered_entries = (int)(seq - new_logbits->_log_first_seq);
+					printk(KERN_INFO "printk_persist: corruption found at %p, "
+							"recovered %d entries, lost %d entries\n",
+						prlog, recovered_entries, lost_entries);
+					printk(KERN_INFO "Expected CRC %08x, stored CRC %08x\n",
+							curr_crc, prlog->crc);
+					{
+						char *p = (char*) (((unsigned) prlog) & ~0x1f) - 256;
+						unsigned size = new_logbits->_log_next_idx - idx + ((char*)prlog - p);
+						if (size > 1024) size = 1024;
+						print_hex_dump(KERN_INFO, "corrupt: ", DUMP_PREFIX_ADDRESS, 16, 1, p, size, 1);
+					}
+					new_logbits->_log_next_seq = seq;
+					new_logbits->_log_next_idx = idx;
+					corruption = 1;
+					break;
+				}
+				if (prlog->len == 0) {
+					idx = 0;
+					// Do not increment the sequence counter for the
+					// record that's used to indicate wrapping. We
+					// do still want to verify its CRC above though.
+					--seq;
+				}
+				else
+					idx += prlog->len;
+			}
+			if (!corruption)
+				printk(KERN_INFO "printk_persist: successfully validated %llu entries\n",
+					(new_logbits->_log_next_seq - new_logbits->_log_first_seq));
+		}
+	} else {
+		/* replace the buffer, but don't bother to swap struct logbits */
+		printk(KERN_ERR "printk_persist: failed to reserve bootmem area. disabled.\n");
+		buf = alloc_bootmem(full_size);
+		*new_logbuf = buf;
+		new_logbits = (struct logbits*)(buf + size);
+		memset(buf, 0, full_size);
+	}
+
+	return new_logbits;
+}
+#endif
+
 /* Return log buffer address */
 char *log_buf_addr_get(void)
 {
@@ -433,11 +721,23 @@
 		 * to signify a wrap around.
 		 */
 		memset(log_buf + log_next_idx, 0, sizeof(struct printk_log));
+#ifdef CONFIG_PRINTK_PERSIST
+		((struct printk_log*) (log_buf + log_next_idx))->crc =
+			crc32(~0, log_buf + log_next_idx, offsetof(struct printk_log, crc));
+#endif
+		/* Flush L1 and L2 cache */
+		__sync_cache_range_w(log_buf + log_next_idx, sizeof(struct printk_log));
 		log_next_idx = 0;
 	}
 
+	/* insert message */
+	log_next_idx += size;
+	log_next_seq++;
+	/* Flush L1 and L2 cache */
+	__sync_cache_range_w(logbits, offsetof(struct logbits, _console_seq));
+
 	/* fill message */
-	msg = (struct printk_log *)(log_buf + log_next_idx);
+	msg = (struct printk_log *)(log_buf + log_next_idx - size);
 	memcpy(log_text(msg), text, text_len);
 	msg->text_len = text_len;
 	if (trunc_msg_len) {
@@ -455,10 +755,11 @@
 		msg->ts_nsec = local_clock();
 	memset(log_dict(msg) + dict_len, 0, pad_len);
 	msg->len = size;
-
-	/* insert message */
-	log_next_idx += msg->len;
-	log_next_seq++;
+#ifdef CONFIG_PRINTK_PERSIST
+	msg->crc = crc32(~0, msg, offsetof(struct printk_log, crc));
+#endif
+	/* Flush L1 and L2 cache */
+	__sync_cache_range_w(msg, size);
 
 	return msg->text_len;
 }
@@ -693,6 +994,10 @@
 	struct devkmsg_user *user = file->private_data;
 	loff_t ret = 0;
 
+	/* glibc's fdopen() calls _llseek(..., SEEK_CUR). Let's make it happy
+	 * by returning 0 instead of an error. */
+	if (offset == 0 && whence == SEEK_CUR)
+		return 0;
 	if (!user)
 		return -EBADF;
 	if (offset)
@@ -887,6 +1192,17 @@
 	unsigned long flags;
 	char *new_log_buf;
 	int free;
+#ifdef CONFIG_PRINTK_PERSIST
+	struct logbits *new_logbits;
+	struct logbits *old_logbits;
+	struct printk_log *prlog;
+	u64 seq;
+	int idx;
+	int console_found=0, syslog_found=0;
+#endif
+#ifdef CONFIG_BOOTLOG_COPY
+	struct bloghdr *blog_hdr = NULL;
+#endif
 
 	if (log_buf != __log_buf)
 		return;
@@ -897,6 +1213,7 @@
 	if (!new_log_buf_len)
 		return;
 
+#ifndef CONFIG_PRINTK_PERSIST
 	if (early) {
 		new_log_buf =
 			memblock_virt_alloc(new_log_buf_len, LOG_ALIGN);
@@ -904,6 +1221,9 @@
 		new_log_buf = memblock_virt_alloc_nopanic(new_log_buf_len,
 							  LOG_ALIGN);
 	}
+#else
+	new_logbits = log_buf_alloc(new_log_buf_len, &new_log_buf);
+#endif
 
 	if (unlikely(!new_log_buf)) {
 		pr_err("log_buf_len: %ld bytes not available\n",
@@ -911,17 +1231,100 @@
 		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_next_idx;
+
+#ifndef CONFIG_PRINTK_PERSIST
 	memcpy(log_buf, __log_buf, __LOG_BUF_LEN);
+#else
+	/* We have to copy over entries one at a time from the old
+	 * buffer to the new buffer.
+	 */
+	old_logbits = logbits;
+	logbits = new_logbits;
+
+#ifdef CONFIG_BOOTLOG_COPY
+	copy_bootlog(blog_hdr);
+#endif
+
+	for (seq = old_logbits->_log_first_seq, idx = old_logbits->_log_first_idx;
+		 seq < old_logbits->_log_next_seq; ++seq) {
+		prlog = (struct printk_log *)&__log_buf[idx];
+		if (prlog->len == 0) {
+			idx = 0;
+			prlog = (struct printk_log *)&__log_buf[0];
+		}
+
+		if (log_make_free_space(prlog->len)) {
+			pr_err("not copying entry due to it being too huge\n");
+			idx += prlog->len;
+			continue;
+		}
+
+		if (log_next_idx + prlog->len + sizeof(struct printk_log) > log_buf_len) {
+			/*
+			 * This message + an additional empty header does not fit
+			 * at the end of the buffer. Add an empty header with len == 0
+			 * to signify a wrap around.
+			 */
+			memset(log_buf + log_next_idx, 0, sizeof(struct printk_log));
+#ifdef CONFIG_PRINTK_PERSIST
+			((struct printk_log*) (log_buf + log_next_idx))->crc =
+				crc32(~0, log_buf + log_next_idx, offsetof(struct printk_log, crc));
+#endif
+			log_next_idx = 0;
+		}
+		memcpy(&log_buf[log_next_idx], &__log_buf[idx], prlog->len);
+		__sync_cache_range_w(&log_buf[log_next_idx], prlog->len);
+
+		if (old_logbits->_syslog_seq == seq) {
+			syslog_seq = log_next_seq;
+			syslog_idx = log_next_idx;
+			syslog_found = 1;
+		}
+
+		if (old_logbits->_console_seq == seq) {
+			console_seq = log_next_seq;
+			console_idx = log_next_idx;
+			console_found = 1;
+		}
+
+		idx += prlog->len;
+		log_next_seq++;
+		log_next_idx += prlog->len;
+	}
+
+	if (!syslog_found) {
+		syslog_seq = log_next_seq;
+		syslog_idx = log_next_idx;
+	}
+
+	if (!console_found) {
+		console_seq = log_next_seq;
+		console_idx = log_next_idx;
+	}
+	clear_seq = log_first_seq;
+	clear_idx = log_first_idx;
+	__sync_cache_range_w(logbits, offsetof(struct logbits, _console_seq));
+#endif
 	raw_spin_unlock_irqrestore(&logbuf_lock, flags);
 
-	pr_info("log_buf_len: %d bytes\n", log_buf_len);
-	pr_info("early log buf free: %d(%d%%)\n",
-		free, (free * 100) / __LOG_BUF_LEN);
+#ifdef CONFIG_BOOTLOG_COPY
+	free_bootlog();
+#endif
+
+	if (free < __LOG_BUF_LEN / 2)
+		pr_info("early log buf free: %d(%d%%)\n",
+			free, (free * 100) / __LOG_BUF_LEN);
 }
 
 static bool __read_mostly ignore_loglevel;
@@ -2792,6 +3195,8 @@
 	struct kmsg_dumper *dumper;
 	unsigned long flags;
 
+	cont_flush(LOG_NEWLINE);
+
 	if ((reason > KMSG_DUMP_OOPS) && !always_kmsg_dump)
 		return;
 
diff --git a/net/bridge/br.c b/net/bridge/br.c
index c72e01c..12d6db4 100644
--- a/net/bridge/br.c
+++ b/net/bridge/br.c
@@ -266,6 +266,60 @@
 	br_fdb_fini();
 }
 
+#if defined(CONFIG_ARCH_COMCERTO)
+static ATOMIC_NOTIFIER_HEAD(brevent_notif_chain);
+
+/**
+ *	register_brevent_notifier - register a netevent notifier block
+ *	@nb: notifier
+ *
+ *	Register a notifier to be called when a bridge event occurs.
+ *	The notifier passed is linked into the kernel structures and must
+ *	not be reused until it has been unregistered. A negative errno code
+ *	is returned on a failure.
+ */
+int register_brevent_notifier(struct notifier_block *nb)
+{
+	int err;
+
+	err = atomic_notifier_chain_register(&brevent_notif_chain, nb);
+	return err;
+}
+
+/**
+ *	unregister_brevent_notifier - unregister a netevent notifier block
+ *	@nb: notifier
+ *
+ *	Unregister a notifier previously registered by
+ *	register_neigh_notifier(). The notifier is unlinked into the
+ *	kernel structures and may then be reused. A negative errno code
+ *	is returned on a failure.
+ */
+
+int unregister_brevent_notifier(struct notifier_block *nb)
+{
+	return atomic_notifier_chain_unregister(&brevent_notif_chain, nb);
+}
+
+/**
+ *	call_brevent_notifiers - call all netevent notifier blocks
+ *      @val: value passed unmodified to notifier function
+ *      @v:   pointer passed unmodified to notifier function
+ *
+ *	Call all neighbour notifier blocks.  Parameters and return value
+ *	are as for notifier_call_chain().
+ */
+
+int call_brevent_notifiers(unsigned long val, void *v)
+{
+	return atomic_notifier_call_chain(&brevent_notif_chain, val, v);
+}
+
+EXPORT_SYMBOL_GPL(register_brevent_notifier);
+EXPORT_SYMBOL_GPL(unregister_brevent_notifier);
+EXPORT_SYMBOL_GPL(call_brevent_notifiers);
+#endif
+
 module_init(br_init)
 module_exit(br_deinit)
 MODULE_LICENSE("GPL");
diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
index 659fb96..f081204 100644
--- a/net/bridge/br_fdb.c
+++ b/net/bridge/br_fdb.c
@@ -37,6 +37,11 @@
 
 static u32 fdb_salt __read_mostly;
 
+#if defined(CONFIG_ARCH_COMCERTO)
+	int(*br_fdb_can_expire)(unsigned char *mac_addr, struct net_device *dev) = NULL;
+	DEFINE_SPINLOCK(br_fdb_cb_lock);
+#endif
+
 int __init br_fdb_init(void)
 {
 	br_fdb_cache = kmem_cache_create("bridge_fdb_cache",
@@ -47,6 +52,9 @@
 		return -ENOMEM;
 
 	get_random_bytes(&fdb_salt, sizeof(fdb_salt));
+#if defined(CONFIG_ARCH_COMCERTO)
+	spin_lock_init(&br_fdb_cb_lock);
+#endif
 	return 0;
 }
 
@@ -98,17 +106,23 @@
 	ASSERT_RTNL();
 
 	list_for_each_entry(p, &br->port_list, list) {
-		if (!br_promisc_port(p)) {
+		/* Program all local unicast addresses of the bridge into
+		 * Ethernet interface.  Required for NXP QorIQ LS1024A fast
+		 * forwarding feature. */
+		if (IS_ENABLED(CONFIG_ARCH_COMCERTO) || !br_promisc_port(p)) {
 			err = dev_uc_add(p->dev, addr);
-			if (err)
+			if (err) {
+				pr_err("bridge: dev_uc_add(%s, %pM) failed with %d\n",
+					netdev_name(p->dev), addr, err);
 				goto undo;
+			}
 		}
 	}
 
 	return;
 undo:
 	list_for_each_entry_continue_reverse(p, &br->port_list, list) {
-		if (!br_promisc_port(p))
+		if (IS_ENABLED(CONFIG_ARCH_COMCERTO) || !br_promisc_port(p))
 			dev_uc_del(p->dev, addr);
 	}
 }
@@ -125,7 +139,10 @@
 	ASSERT_RTNL();
 
 	list_for_each_entry(p, &br->port_list, list) {
-		if (!br_promisc_port(p))
+		/* Remove all previously added local unicast addresses of the
+		 * bridge from Ethernet interface.  Required for NXP QorIQ
+		 * LS1024A fast forwarding feature. */
+		if (IS_ENABLED(CONFIG_ARCH_COMCERTO) || !br_promisc_port(p))
 			dev_uc_del(p->dev, addr);
 	}
 }
@@ -282,6 +299,15 @@
 			unsigned long this_timer;
 			if (f->is_static)
 				continue;
+#if defined(CONFIG_ARCH_COMCERTO)
+			spin_lock(&br_fdb_cb_lock);
+			if(br_fdb_can_expire && !(*br_fdb_can_expire)(f->addr.addr, f->dst->dev)){
+				f->updated = jiffies;
+				spin_unlock(&br_fdb_cb_lock);
+				continue;
+			}
+			spin_unlock(&br_fdb_cb_lock);
+#endif
 			this_timer = f->updated + delay;
 			if (time_before_eq(this_timer, jiffies))
 				fdb_delete(br, f);
@@ -558,6 +584,18 @@
 					source->dev->name);
 		} else {
 			/* fastpath: update of existing entry */
+#if defined(CONFIG_ARCH_COMCERTO)
+			if (fdb->dst != source) {
+				struct brevent_fdb_update fdb_update;
+
+				fdb_update.dev = source->dev;
+				fdb_update.mac_addr = fdb->addr.addr;
+				//FIXME
+				//__rtmsg_ifinfo(RTM_NEWLINK, br->dev, 0, GFP_ATOMIC);
+				//FIXME
+				call_brevent_notifiers(BREVENT_FDB_UPDATE, &fdb_update);
+			}
+#endif
 			if (unlikely(source != fdb->dst)) {
 				fdb->dst = source;
 				fdb_modified = true;
@@ -585,6 +623,24 @@
 	}
 }
 
+#if defined(CONFIG_ARCH_COMCERTO)
+void br_fdb_register_can_expire_cb(int(*cb)(unsigned char *mac_addr, struct net_device *dev))
+{
+        spin_lock_bh(&br_fdb_cb_lock);
+        br_fdb_can_expire = cb;
+        spin_unlock_bh(&br_fdb_cb_lock);
+}
+EXPORT_SYMBOL(br_fdb_register_can_expire_cb);
+
+void br_fdb_deregister_can_expire_cb()
+{
+        spin_lock_bh(&br_fdb_cb_lock);
+        br_fdb_can_expire = NULL;
+        spin_unlock_bh(&br_fdb_cb_lock);
+}
+EXPORT_SYMBOL(br_fdb_deregister_can_expire_cb);
+#endif
+
 static int fdb_to_nud(const struct net_bridge_fdb_entry *fdb)
 {
 	if (fdb->is_local)
@@ -950,8 +1006,11 @@
 				continue;
 
 			err = dev_uc_add(p->dev, fdb->addr.addr);
-			if (err)
+			if (err) {
+				pr_err("bridge: dev_uc_add(%s, %pM) failed with %d\n",
+					netdev_name(p->dev), fdb->addr.addr, err);
 				goto rollback;
+			}
 		}
 	}
 	return 0;
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index 1849d96..baaee74 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -96,7 +96,12 @@
 	if (err)
 		return;
 
+#ifndef CONFIG_ARCH_COMCERTO
+	/* Even if we set the interface to promiscuous mode, we want all local
+	 * MAC addresses programmed into the interface.
+	 * Required for NXP QorIQ LS1024A fast forwarding feature. */
 	br_fdb_unsync_static(p->br, p);
+#endif
 	p->flags |= BR_PROMISC;
 }
 
@@ -133,6 +138,11 @@
 	struct net_bridge_port *p;
 	bool set_all = false;
 
+#ifdef CONFIG_ARCH_COMCERTO
+	/* Always use promiscuous mode */
+	set_all = true;
+#endif
+
 	/* If vlan filtering is disabled or bridge interface is placed
 	 * into promiscuous mode, place all ports in promiscuous mode.
 	 */
@@ -186,7 +196,12 @@
 	dev_set_allmulti(p->dev, -1);
 	if (br_promisc_port(p))
 		dev_set_promiscuity(p->dev, -1);
+#ifndef CONFIG_ARCH_COMCERTO
 	else
+	/* We always program the unicast MAC addresses even if the interface is
+	 * in promiscuous mode.
+	 * Required for NXP QorIQ LS1024A fast forwarding feature. */
+#endif
 		br_fdb_unsync_static(p->br, p);
 }
 
@@ -499,6 +514,13 @@
 
 	nbp_update_port_count(br);
 
+#ifdef CONFIG_ARCH_COMCERTO
+	/* Program all local unicast addresses of the bridge into Ethernet
+	 * interface.  Required for NXP QorIQ LS1024A fast forwarding feature.
+	 * */
+	br_fdb_sync_static(p->br, p);
+#endif
+
 	netdev_update_features(br->dev);
 
 	if (br->dev->needed_headroom < dev->needed_headroom)
diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c
index f921a5d..6c2301f 100644
--- a/net/bridge/br_input.c
+++ b/net/bridge/br_input.c
@@ -151,6 +151,10 @@
 
 	BR_INPUT_SKB_CB(skb)->brdev = br->dev;
 
+#if defined(CONFIG_ARCH_COMCERTO)
+	skb->cb[4] = 0;
+#endif
+
 	/* The packet skb2 goes to the local host (NULL to skip). */
 	skb2 = NULL;
 
@@ -191,6 +195,10 @@
 	if (skb) {
 		if (dst) {
 			dst->used = jiffies;
+#if defined(CONFIG_ARCH_COMCERTO)
+			/* Used by ABM module */
+			skb->cb[4] = 1;
+#endif
 			br_forward(dst->dst, skb, skb2);
 		} else
 			br_flood_forward(br, skb, skb2, unicast);
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 3362c29..4b3388a 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -395,6 +395,10 @@
 void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source,
 		   const unsigned char *addr, u16 vid, bool added_by_user);
 
+#if defined(CONFIG_ARCH_COMCERTO)
+	extern void br_fdb_register_can_expire_cb(int(*cb)(unsigned char *mac_addr, struct net_device *dev)); 
+	extern void br_fdb_deregister_can_expire_cb(void);
+#endif
 int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],
 		  struct net_device *dev, const unsigned char *addr, u16 vid);
 int br_fdb_add(struct ndmsg *nlh, struct nlattr *tb[], struct net_device *dev,
diff --git a/net/bridge/br_stp_if.c b/net/bridge/br_stp_if.c
index ce658ab..dcc99a9 100644
--- a/net/bridge/br_stp_if.c
+++ b/net/bridge/br_stp_if.c
@@ -120,6 +120,10 @@
 
 	if (br_is_root_bridge(br) && !wasroot)
 		br_become_root_bridge(br);
+
+#if defined(CONFIG_ARCH_COMCERTO)
+	call_brevent_notifiers(BREVENT_PORT_DOWN, p->dev);
+#endif
 }
 
 static void br_stp_start(struct net_bridge *br)
diff --git a/net/core/dev.c b/net/core/dev.c
index 56d820f..05c3937 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -3673,6 +3673,8 @@
 
 another_round:
 	skb->skb_iif = skb->dev->ifindex;
+	if (!skb->skb_orig_iif)
+		skb->skb_orig_iif = skb->dev->ifindex;
 
 	__this_cpu_inc(softnet_data.processed);
 
@@ -3724,9 +3726,12 @@
 			ret = deliver_skb(skb, pt_prev, orig_dev);
 			pt_prev = NULL;
 		}
-		if (vlan_do_receive(&skb))
+		if (vlan_do_receive(&skb)) {
+			/* For VLANs, we want skb_orig_iif to point to the
+			 * virtual VLAN device not the physical device. */
+			skb->skb_orig_iif = 0;
 			goto another_round;
-		else if (unlikely(!skb))
+		} else if (unlikely(!skb))
 			goto out;
 	}
 
@@ -4216,6 +4221,7 @@
 	skb->vlan_tci = 0;
 	skb->dev = napi->dev;
 	skb->skb_iif = 0;
+	skb->skb_orig_iif = 0;
 	skb->encapsulation = 0;
 	skb_shinfo(skb)->gso_type = 0;
 	skb->truesize = SKB_TRUESIZE(skb_end_offset(skb));
diff --git a/net/core/pktgen.c b/net/core/pktgen.c
index 043ea186..b28a732 100644
--- a/net/core/pktgen.c
+++ b/net/core/pktgen.c
@@ -262,6 +262,7 @@
 	u64 delay;		/* nano-seconds */
 
 	__u64 count;		/* Default No packets to send */
+	__u64 duration;		/* Duration to send in microseconds */
 	__u64 sofar;		/* How many pkts we've sent so far */
 	__u64 tx_bytes;		/* How many bytes we've transmitted */
 	__u64 errors;		/* Errors when trying to transmit, */
@@ -550,6 +551,9 @@
 		   pkt_dev->queue_map_min,
 		   pkt_dev->queue_map_max);
 
+	seq_printf(seq, "     duration: %llu\n",
+		   (unsigned long long)pkt_dev->duration);
+
 	if (pkt_dev->skb_priority)
 		seq_printf(seq, "     skb_priority: %u\n",
 			   pkt_dev->skb_priority);
@@ -1100,6 +1104,16 @@
 			(unsigned long long)pkt_dev->count);
 		return count;
 	}
+	if (!strcmp(name, "duration")) {
+		len = num_arg(&user_buffer[i], 10, &value);
+		if (len < 0)
+			return len;
+		i += len;
+		pkt_dev->duration = value;
+		sprintf(pg_result, "OK: duration=%llu",
+			(unsigned long long)pkt_dev->duration);
+		return count;
+	}
 	if (!strcmp(name, "src_mac_count")) {
 		len = num_arg(&user_buffer[i], 10, &value);
 		if (len < 0)
@@ -2181,6 +2195,7 @@
 	pkt_dev->cur_daddr = pkt_dev->daddr_min;
 	pkt_dev->cur_udp_dst = pkt_dev->udp_dst_min;
 	pkt_dev->cur_udp_src = pkt_dev->udp_src_min;
+	pkt_dev->cur_queue_map = pkt_dev->queue_map_min;
 	pkt_dev->nflows = 0;
 }
 
@@ -3318,6 +3333,7 @@
 	struct net_device *odev = pkt_dev->odev;
 	struct netdev_queue *txq;
 	int ret;
+	u64 elapsed;
 
 	/* If device is offline, then don't send */
 	if (unlikely(!netif_running(odev) || !netif_carrier_ok(odev))) {
@@ -3403,8 +3419,10 @@
 
 	local_bh_enable();
 
-	/* If pkt_dev->count is zero, then run forever */
-	if ((pkt_dev->count != 0) && (pkt_dev->sofar >= pkt_dev->count)) {
+	/* If pkt_dev->count and pkt_dev->duration are zero, then run forever */
+	elapsed = ktime_us_delta(ktime_get(), pkt_dev->started_at);
+	if (((pkt_dev->count != 0) && (pkt_dev->sofar >= pkt_dev->count)) ||
+	    ((pkt_dev->duration != 0) && (elapsed >= pkt_dev->duration))) {
 		pktgen_wait_for_skb(pkt_dev);
 
 		/* Done with this */
@@ -3587,6 +3605,7 @@
 	pkt_dev->nfrags = 0;
 	pkt_dev->delay = pg_delay_d;
 	pkt_dev->count = pg_count_d;
+	pkt_dev->duration = 0;
 	pkt_dev->sofar = 0;
 	pkt_dev->udp_src_min = 9;	/* sink port */
 	pkt_dev->udp_src_max = 9;
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index e9c4b51..f760a00 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -698,7 +698,51 @@
 	return -EMSGSIZE;
 }
 EXPORT_SYMBOL(rtnetlink_put_metrics);
+#ifdef CONFIG_ARCH_COMCERTO
+int rtnetlink_put_metrics_2(struct sk_buff *skb, u32 *metrics, struct dst_entry *dst)
+{
+	struct nlattr *mx;
+	int i, valid = 0;
 
+	mx = nla_nest_start(skb, RTA_METRICS);
+	if (mx == NULL)
+		return -ENOBUFS;
+
+	for (i = 0; i < RTAX_MAX; i++) {
+		if (metrics[i]) {
+			if (i == RTAX_CC_ALGO - 1) {
+				char tmp[TCP_CA_NAME_MAX], *name;
+
+				name = tcp_ca_get_name_by_key(metrics[i], tmp);
+				if (!name)
+					continue;
+				if (nla_put_string(skb, i + 1, name))
+					goto nla_put_failure;
+			} else {
+				if (nla_put_u32(skb, i + 1, metrics[i]))
+					goto nla_put_failure;
+			}
+			valid++;
+		}
+		else if ((i + 1) == RTAX_MTU){
+			valid++;
+			nla_put_u32(skb, i+1, dst_mtu(dst));
+		}
+	}
+
+	if (!valid) {
+		nla_nest_cancel(skb, mx);
+		return 0;
+	}
+
+	return nla_nest_end(skb, mx);
+
+nla_put_failure:
+	nla_nest_cancel(skb, mx);
+	return -EMSGSIZE;
+}
+EXPORT_SYMBOL(rtnetlink_put_metrics_2);
+#endif
 int rtnl_put_cacheinfo(struct sk_buff *skb, struct dst_entry *dst, u32 id,
 		       long expires, u32 error)
 {
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index c9793c6..c24904b 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -330,6 +330,7 @@
 
 	return skb;
 }
+EXPORT_SYMBOL(__build_skb);
 
 /* build_skb() is wrapper over __build_skb(), that specifically
  * takes care of skb->head and skb->pfmemalloc
@@ -349,6 +350,28 @@
 }
 EXPORT_SYMBOL(build_skb);
 
+struct sk_buff *alloc_dma_coherent_skb(unsigned int size)
+{
+	struct sk_buff *skb = NULL;
+	dma_addr_t dma_handle;
+	void *buf;
+
+	size = SKB_DATA_ALIGN(size);
+	size += SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
+	buf = dma_alloc_coherent(NULL, size, &dma_handle, GFP_ATOMIC);
+	if (buf) {
+		skb = __build_skb(buf, size);
+		if (skb) {
+			skb->dma_coherent = 1;
+		} else {
+			dma_free_coherent(NULL, size, buf, dma_handle);
+		}
+	}
+	return skb;
+	/* dma_handle is thrown away */
+}
+EXPORT_SYMBOL(alloc_dma_coherent_skb);
+
 struct netdev_alloc_cache {
 	struct page_frag	frag;
 	/* we maintain a pagecount bias, so that we dont dirty cache line
@@ -615,7 +638,12 @@
 {
 	if (skb->head_frag)
 		put_page(virt_to_head_page(skb->head));
-	else
+	else if (skb->dma_coherent) {
+		unsigned int size;
+		size = skb->end - skb->head;
+		size += SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
+		dma_free_coherent(NULL, size, skb->head, 0);
+	} else
 		kfree(skb->head);
 }
 
@@ -829,6 +857,7 @@
 	CHECK_SKB_FIELD(hash);
 	CHECK_SKB_FIELD(priority);
 	CHECK_SKB_FIELD(skb_iif);
+	CHECK_SKB_FIELD(skb_orig_iif);
 	CHECK_SKB_FIELD(vlan_proto);
 	CHECK_SKB_FIELD(vlan_tci);
 	CHECK_SKB_FIELD(transport_header);
@@ -863,6 +892,7 @@
  */
 static struct sk_buff *__skb_clone(struct sk_buff *n, struct sk_buff *skb)
 {
+	BUG_ON(skb->dma_coherent);
 #define C(x) n->x = skb->x
 
 	n->next = n->prev = NULL;
@@ -880,6 +910,7 @@
 	C(end);
 	C(head);
 	C(head_frag);
+	C(dma_coherent);
 	C(data);
 	C(truesize);
 	atomic_set(&n->users, 1);
@@ -993,6 +1024,9 @@
 	if (skb_orphan_frags(skb, gfp_mask))
 		return NULL;
 
+	if (skb->dma_coherent)
+		return skb_copy(skb, gfp_mask);
+
 	if (skb->fclone == SKB_FCLONE_ORIG &&
 	    atomic_read(&fclones->fclone_ref) == 1) {
 		n = &fclones->skb2;
@@ -1219,6 +1253,7 @@
 
 	skb->head     = data;
 	skb->head_frag = 0;
+	skb->dma_coherent = 0;
 	skb->data    += off;
 #ifdef NET_SKBUFF_DATA_USES_OFFSET
 	skb->end      = size;
@@ -4149,6 +4184,7 @@
 	skb->tstamp.tv64 = 0;
 	skb->pkt_type = PACKET_HOST;
 	skb->skb_iif = 0;
+	skb->skb_orig_iif = 0;
 	skb->ignore_df = 0;
 	skb_dst_drop(skb);
 	skb_sender_cpu_clear(skb);
diff --git a/net/core/sock.c b/net/core/sock.c
index 47fc8bb..df2d262 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -947,6 +947,10 @@
 		sock_valbool_flag(sk, SOCK_RXQ_OVFL, valbool);
 		break;
 
+	case SO_RXQ_ALLOC:
+		sock_valbool_flag(sk, SOCK_RXQ_ALLOC, valbool);
+		break;
+
 	case SO_WIFI_STATUS:
 		sock_valbool_flag(sk, SOCK_WIFI_STATUS, valbool);
 		break;
@@ -1196,6 +1200,10 @@
 		v.val = sock_flag(sk, SOCK_RXQ_OVFL);
 		break;
 
+	case SO_RXQ_ALLOC:
+		v.val = sock_flag(sk, SOCK_RXQ_ALLOC);
+		break;
+
 	case SO_WIFI_STATUS:
 		v.val = sock_flag(sk, SOCK_WIFI_STATUS);
 		break;
diff --git a/net/ipv4/Kconfig b/net/ipv4/Kconfig
index bd29016..173079d 100644
--- a/net/ipv4/Kconfig
+++ b/net/ipv4/Kconfig
@@ -485,6 +485,42 @@
 	among other techniques.
 	See http://www.csc.ncsu.edu/faculty/rhee/export/bitcp/cubic-paper.pdf
 
+config TCP_CONG_ACUBIC
+	tristate "ACUBIC TCP"
+	default m
+	---help---
+	ACUBIC is just CUBIC TCP, but with a much more agressive RTO
+	(retransmit timeout).  This prevents long dropouts when packets get
+	lost in a large burst on a low-quality link.  It is somewhat
+	dangerous to use on the open Internet, but may be useful in some
+	cases on LANs, where congestion collapse is very unlikely due to the
+	small number of participants, and "real" round trip times are small.
+	ACUBIC stands for "aggressive CUBIC."  It definitely does not stand
+	for "Avery's CUBIC."
+
+config TCP_CONG_TRANSONIC
+	tristate "Transonic TCP"
+	default m
+	---help---
+	Transonic switches to a hard coded fixed-rate congestion control
+	system.  It refuses to back off in the face of congestion, which is
+	a very bad idea on the open Internet, but which may be useful in
+	some cases on LANs.  The congestion window scales up and down
+	relative to the desired(!) throughput (rather than the observed
+	throughput) and the round trip time, which keeps latency reasonably
+	low.
+
+config TCP_CONG_TURBULENT
+	tristate "Turbulent TCP"
+	default m
+	---help---
+	Turbulent is similar to transonic, but always uses an oversized
+	congestion window.  This leads to large queues and a large number of
+	retries.  The advantage is that it fills the receiver window as fast
+	as it can, allowing for more lost data to be retransmitted per round
+	trip time when the link is extremely bad.  You should never use this
+	on the open Internet, because it actively disrupts other traffic.
+
 config TCP_CONG_WESTWOOD
 	tristate "TCP Westwood+"
 	default m
diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile
index 518c04e..64e3102 100644
--- a/net/ipv4/Makefile
+++ b/net/ipv4/Makefile
@@ -43,6 +43,9 @@
 obj-$(CONFIG_NET_TCPPROBE) += tcp_probe.o
 obj-$(CONFIG_TCP_CONG_BIC) += tcp_bic.o
 obj-$(CONFIG_TCP_CONG_CUBIC) += tcp_cubic.o
+obj-$(CONFIG_TCP_CONG_ACUBIC) += tcp_acubic.o
+obj-$(CONFIG_TCP_CONG_TRANSONIC) += tcp_transonic.o
+obj-$(CONFIG_TCP_CONG_TURBULENT) += tcp_turbulent.o
 obj-$(CONFIG_TCP_CONG_DCTCP) += tcp_dctcp.o
 obj-$(CONFIG_TCP_CONG_WESTWOOD) += tcp_westwood.o
 obj-$(CONFIG_TCP_CONG_HSTCP) += tcp_highspeed.o
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index eb1d983..e5ef8cd 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -2419,7 +2419,11 @@
 	memcpy(metrics, dst_metrics_ptr(&rt->dst), sizeof(metrics));
 	if (rt->rt_pmtu && expires)
 		metrics[RTAX_MTU - 1] = rt->rt_pmtu;
+#ifdef CONFIG_ARCH_COMCERTO
+	if (rtnetlink_put_metrics_2(skb, metrics, &rt->dst) < 0)
+#else
 	if (rtnetlink_put_metrics(skb, metrics) < 0)
+#endif
 		goto nla_put_failure;
 
 	if (fl4->flowi4_mark &&
diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c
index f0e8297..81ecc92 100644
--- a/net/ipv4/sysctl_net_ipv4.c
+++ b/net/ipv4/sysctl_net_ipv4.c
@@ -570,6 +570,13 @@
 		.proc_handler	= proc_dointvec
 	},
 	{
+		.procname	= "tcp_min_rtt_wlen",
+		.data		= &sysctl_tcp_min_rtt_wlen,
+		.maxlen		= sizeof(int),
+		.mode		= 0644,
+		.proc_handler	= proc_dointvec
+	},
+	{
 		.procname	= "tcp_low_latency",
 		.data		= &sysctl_tcp_low_latency,
 		.maxlen		= sizeof(int),
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index 19d385a..e5e6c1e 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -389,6 +389,7 @@
 
 	icsk->icsk_rto = TCP_TIMEOUT_INIT;
 	tp->mdev_us = jiffies_to_usecs(TCP_TIMEOUT_INIT);
+	tp->rtt_min[0].rtt = ~0U;
 
 	/* So many TCP implementations out there (incorrectly) count the
 	 * initial SYN frame in their delayed-ACK and congestion control
diff --git a/net/ipv4/tcp_acubic.c b/net/ipv4/tcp_acubic.c
new file mode 100644
index 0000000..637a91e
--- /dev/null
+++ b/net/ipv4/tcp_acubic.c
@@ -0,0 +1,55 @@
+/*
+ * TCP ACubic: a variation on CUBIC but with a more aggressive RTO.
+ * You shouldn't do this on the open Internet; it could cause congestion
+ * collapse problems.  It might be okay in a more controlled environment.
+ */
+
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/math64.h>
+#include <net/tcp.h>
+
+static void (*old_cubic_init)(struct sock *sk);
+
+static void acubic_init(struct sock *sk)
+{
+	struct tcp_sock *tp = tcp_sk(sk);
+	old_cubic_init(sk);
+	tp->is_aggressive_rto = 1;
+}
+
+static struct tcp_congestion_ops acubictcp __read_mostly;
+
+static int __init acubic_register(void)
+{
+	struct tcp_congestion_ops *ca;
+	u32 key = tcp_ca_get_key_by_name("cubic");
+	if (key == TCP_CA_UNSPEC)
+		return -EEXIST;
+	ca = tcp_ca_find_key(key);
+	if (!ca)
+		return -EINVAL;
+
+	// Wrap CUBIC's implementation, but replace the name and init
+	// function.  This way we can measure performance differences by
+	// tracking just the name of the congestion algorithm.
+	memcpy(&acubictcp, ca, sizeof(struct tcp_congestion_ops));
+	strncpy(acubictcp.name, "acubic", sizeof(acubictcp.name));
+	old_cubic_init = ca->init;
+	acubictcp.owner = THIS_MODULE;
+	acubictcp.init = acubic_init;
+	return tcp_register_congestion_control(&acubictcp);
+}
+
+static void __exit acubic_unregister(void)
+{
+	tcp_unregister_congestion_control(&acubictcp);
+}
+
+module_init(acubic_register);
+module_exit(acubic_unregister);
+
+MODULE_AUTHOR("Avery Pennarun");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("ACubic TCP");
+MODULE_VERSION("1.0");
diff --git a/net/ipv4/tcp_cong.c b/net/ipv4/tcp_cong.c
index 84be008..56de1dd 100644
--- a/net/ipv4/tcp_cong.c
+++ b/net/ipv4/tcp_cong.c
@@ -59,6 +59,7 @@
 
 	return NULL;
 }
+EXPORT_SYMBOL_GPL(tcp_ca_find_key);
 
 /*
  * Attach new congestion control algorithm to the list
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index 36b93ad..828cc4b 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -95,6 +95,7 @@
 int sysctl_tcp_rfc1337 __read_mostly;
 int sysctl_tcp_max_orphans __read_mostly = NR_FILE;
 int sysctl_tcp_frto __read_mostly = 2;
+int sysctl_tcp_min_rtt_wlen __read_mostly = 300;
 
 int sysctl_tcp_thin_dupack __read_mostly;
 
@@ -275,6 +276,11 @@
 	return false;
 }
 
+static inline bool tcp_is_rate_controlled(const struct sock *sk)
+{
+	return tcp_sk(sk)->is_rate_controlled;
+}
+
 /* Buffer size and advertised window tuning.
  *
  * 1. Tuning sk->sk_sndbuf, when connection enters established state.
@@ -758,6 +764,9 @@
 	/* set sk_pacing_rate to 200 % of current rate (mss * cwnd / srtt) */
 	rate = (u64)tp->mss_cache * 2 * (USEC_PER_SEC << 3);
 
+	if (tcp_is_rate_controlled(sk))
+		return;
+
 	rate *= max(tp->snd_cwnd, tp->packets_out);
 
 	if (likely(tp->srtt_us))
@@ -1915,6 +1924,18 @@
 	tp->undo_retrans = tp->retrans_out ? : -1;
 }
 
+/* Disable built-in CC by fixing cwnd based on qdisc or user specified rate,
+ * and min RTT. The minimum of qdisc and user rate is used to adjust cwnd.
+ * Returns true if built-in CC is overridden, and false otherwise.
+ */
+bool tcp_rate_control(struct sock *sk)
+{
+	const struct inet_connection_sock *icsk = inet_csk(sk);
+	if (!tcp_is_rate_controlled(sk) || !icsk->icsk_ca_ops->rate_control)
+		return false;
+	return icsk->icsk_ca_ops->rate_control(sk, tcp_current_mss(sk));
+}
+
 /* Enter Loss state. If we detect SACK reneging, forget all SACK information
  * and reset tags completely, otherwise preserve SACKs. If receiver
  * dropped its ofo queue, we will know this due to reneging detection.
@@ -1937,9 +1958,11 @@
 		tcp_ca_event(sk, CA_EVENT_LOSS);
 		tcp_init_undo(tp);
 	}
-	tp->snd_cwnd	   = 1;
-	tp->snd_cwnd_cnt   = 0;
-	tp->snd_cwnd_stamp = tcp_time_stamp;
+	if (!tcp_is_rate_controlled(sk)) {
+		tp->snd_cwnd       = 1;
+		tp->snd_cwnd_cnt   = 0;
+		tp->snd_cwnd_stamp = tcp_time_stamp;
+	}
 
 	tp->retrans_out = 0;
 	tp->lost_out = 0;
@@ -2390,6 +2413,10 @@
 		tcp_clear_all_retrans_hints(tp);
 	}
 
+	tp->undo_marker = 0;
+	if (tcp_is_rate_controlled(sk))
+		return;
+
 	if (tp->prior_ssthresh) {
 		const struct inet_connection_sock *icsk = inet_csk(sk);
 
@@ -2406,7 +2433,6 @@
 		tp->snd_cwnd = max(tp->snd_cwnd, tp->snd_ssthresh);
 	}
 	tp->snd_cwnd_stamp = tcp_time_stamp;
-	tp->undo_marker = 0;
 }
 
 static inline bool tcp_may_undo(const struct tcp_sock *tp)
@@ -2438,7 +2464,8 @@
 		/* Hold old state until something *above* high_seq
 		 * is ACKed. For Reno it is MUST to prevent false
 		 * fast retransmits (RFC2582). SACK TCP is safe. */
-		tcp_moderate_cwnd(tp);
+		if (!tcp_is_rate_controlled(sk))
+			tcp_moderate_cwnd(tp);
 		if (!tcp_any_retrans_done(sk))
 			tp->retrans_stamp = 0;
 		return true;
@@ -2515,6 +2542,9 @@
 	int newly_acked_sacked = prior_unsacked -
 				 (tp->packets_out - tp->sacked_out);
 
+	if (tcp_is_rate_controlled(sk))
+		return;
+
 	tp->prr_delivered += newly_acked_sacked;
 	if (tcp_packets_in_flight(tp) > tp->snd_ssthresh) {
 		u64 dividend = (u64)tp->snd_ssthresh * tp->prr_delivered +
@@ -2534,6 +2564,9 @@
 {
 	struct tcp_sock *tp = tcp_sk(sk);
 
+	if (tcp_is_rate_controlled(sk))
+		return;
+
 	/* Reset cwnd to ssthresh in CWR or Recovery (unless it's undone) */
 	if (inet_csk(sk)->icsk_ca_state == TCP_CA_CWR ||
 	    (tp->undo_marker && tp->snd_ssthresh < TCP_INFINITE_SSTHRESH)) {
@@ -2899,8 +2932,71 @@
 	tcp_xmit_retransmit_queue(sk);
 }
 
+/* Kathleen Nichols' algorithm for tracking the minimum value of
+ * a data stream over some fixed time interval. (E.g., the minimum
+ * RTT over the past five minutes.) It uses constant space and constant
+ * time per update yet almost always delivers the same minimum as an
+ * implementation that has to keep all the data in the window.
+ *
+ * The algorithm keeps track of the best, 2nd best & 3rd best min
+ * values, maintaining an invariant that the measurement time of the
+ * n'th best >= n-1'th best. It also makes sure that the three values
+ * are widely separated in the time window since that bounds the worse
+ * case error when that data is monotonically increasing over the window.
+ *
+ * Upon getting a new min, we can forget everything earlier because it
+ * has no value - the new min is <= everything else in the window by
+ * definition and it's the most recent. So we restart fresh on every new min
+ * and overwrites 2nd & 3rd choices. The same property holds for 2nd & 3rd
+ * best.
+ */
+static void tcp_update_rtt_min(struct sock *sk, u32 rtt_us)
+{
+	const u32 now = tcp_time_stamp, wlen = sysctl_tcp_min_rtt_wlen * HZ;
+	struct rtt_meas *m = tcp_sk(sk)->rtt_min;
+	struct rtt_meas rttm = { .rtt = (rtt_us ? : 1), .ts = now };
+	u32 elapsed;
+
+	if (!rtt_us) return;
+
+	/* Check if the new measurement updates the 1st, 2nd, or 3rd choices */
+	if (unlikely(rttm.rtt <= m[0].rtt))
+		m[0] = m[1] = m[2] = rttm;
+	else if (rttm.rtt <= m[1].rtt)
+		m[1] = m[2] = rttm;
+	else if (rttm.rtt <= m[2].rtt)
+		m[2] = rttm;
+
+	elapsed = now - m[0].ts;
+	if (unlikely(elapsed > wlen)) {
+		/* Passed entire window without a new min so make 2nd choice
+		 * the new min & 3rd choice the new 2nd. So forth and so on.
+		 */
+		m[0] = m[1];
+		m[1] = m[2];
+		m[2] = rttm;
+		if (now - m[0].ts > wlen) {
+			m[0] = m[1];
+			m[1] = rttm;
+			if (now - m[0].ts > wlen)
+				m[0] = rttm;
+		}
+	} else if (m[1].ts == m[0].ts && elapsed > wlen / 4) {
+		/* Passed a quarter of the window without a new min so
+		 * take 2nd choice from the 2nd quarter of the window.
+		 */
+		m[2] = m[1] = rttm;
+	} else if (m[2].ts == m[1].ts && elapsed > wlen / 2) {
+		/* Passed half the window without a new min so take the 3rd
+		 * choice from the last half of the window.
+		 */
+		m[2] = rttm;
+	}
+}
+
 static inline bool tcp_ack_update_rtt(struct sock *sk, const int flag,
-				      long seq_rtt_us, long sack_rtt_us)
+				      long seq_rtt_us, long sack_rtt_us,
+				      long ca_rtt_us)
 {
 	const struct tcp_sock *tp = tcp_sk(sk);
 
@@ -2923,11 +3019,16 @@
 	 */
 	if (seq_rtt_us < 0 && tp->rx_opt.saw_tstamp && tp->rx_opt.rcv_tsecr &&
 	    flag & FLAG_ACKED)
-		seq_rtt_us = jiffies_to_usecs(tcp_time_stamp - tp->rx_opt.rcv_tsecr);
-
+		seq_rtt_us = ca_rtt_us = jiffies_to_usecs(tcp_time_stamp -
+							  tp->rx_opt.rcv_tsecr);
 	if (seq_rtt_us < 0)
 		return false;
 
+	/* ca_rtt_us >= 0 is counting on the invariant that ca_rtt_us is
+	 * always taken together with ACK, SACK, or TS-opts. Any negative
+	 * values will be skipped with the seq_rtt_us < 0 check above.
+	 */
+	tcp_update_rtt_min(sk, ca_rtt_us);
 	tcp_rtt_estimator(sk, seq_rtt_us);
 	tcp_set_rto(sk);
 
@@ -2949,13 +3050,16 @@
 	 * sent in SYN_RECV, SYNACK RTT is the smooth RTT computed in tcp_ack()
 	 */
 	if (!tp->srtt_us)
-		tcp_ack_update_rtt(sk, FLAG_SYN_ACKED, seq_rtt_us, -1L);
+		tcp_ack_update_rtt(sk, FLAG_SYN_ACKED, seq_rtt_us, -1L, seq_rtt_us);
 }
 
 static void tcp_cong_avoid(struct sock *sk, u32 ack, u32 acked)
 {
 	const struct inet_connection_sock *icsk = inet_csk(sk);
 
+	if (tcp_is_rate_controlled(sk))
+		return;
+
 	icsk->icsk_ca_ops->cong_avoid(sk, ack, acked);
 	tcp_sk(sk)->snd_cwnd_stamp = tcp_time_stamp;
 }
@@ -3156,7 +3260,8 @@
 		ca_seq_rtt_us = skb_mstamp_us_delta(&now, &last_ackt);
 	}
 
-	rtt_update = tcp_ack_update_rtt(sk, flag, seq_rtt_us, sack_rtt_us);
+	rtt_update = tcp_ack_update_rtt(sk, flag, seq_rtt_us, sack_rtt_us,
+					ca_seq_rtt_us);
 
 	if (flag & FLAG_ACKED) {
 		const struct tcp_congestion_ops *ca_ops
@@ -3450,6 +3555,8 @@
 		/* ACK advances: there was a loss, so reduce cwnd. Reset
 		 * tlp_high_seq in tcp_init_cwnd_reduction()
 		 */
+		if (tcp_is_rate_controlled(sk))
+			return;
 		tcp_init_cwnd_reduction(sk);
 		tcp_set_ca_state(sk, TCP_CA_CWR);
 		tcp_end_cwnd_reduction(sk);
@@ -3516,6 +3623,8 @@
 		icsk->icsk_retransmits = 0;
 	}
 
+	tcp_rate_control(sk);
+
 	prior_fackets = tp->fackets_out;
 
 	/* ts_recent update must be made after we are sure that the packet
diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c
index fec2907..e84b057 100644
--- a/net/ipv4/tcp_minisocks.c
+++ b/net/ipv4/tcp_minisocks.c
@@ -461,6 +461,7 @@
 
 		newtp->srtt_us = 0;
 		newtp->mdev_us = jiffies_to_usecs(TCP_TIMEOUT_INIT);
+		newtp->rtt_min[0].rtt = ~0U;
 		newicsk->icsk_rto = TCP_TIMEOUT_INIT;
 
 		newtp->packets_out = 0;
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index ae66c84..176b621 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -2103,7 +2103,7 @@
 			break;
 
 		/* TCP Small Queues :
-		 * Control number of packets in qdisc/devices to two packets / or ~1 ms.
+		 * Control number of packets in qdisc/devices to two packets / or ~16 ms.
 		 * This allows for :
 		 *  - better RTT estimation and ACK scheduling
 		 *  - faster recovery
@@ -2112,7 +2112,7 @@
 		 * of queued bytes to ensure line rate.
 		 * One example is wifi aggregation (802.11 AMPDU)
 		 */
-		limit = max(2 * skb->truesize, sk->sk_pacing_rate >> 10);
+		limit = max(2 * skb->truesize, sk->sk_pacing_rate >> 6);
 		limit = min_t(u32, limit, sysctl_tcp_limit_output_bytes);
 
 		if (atomic_read(&sk->sk_wmem_alloc) > limit) {
diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c
index c8f9785..08d34d5 100644
--- a/net/ipv4/tcp_timer.c
+++ b/net/ipv4/tcp_timer.c
@@ -469,8 +469,10 @@
 	 * implemented ftp to mars will work nicely. We will have to fix
 	 * the 120 second clamps though!
 	 */
-	icsk->icsk_backoff++;
-	icsk->icsk_retransmits++;
+	if (!tcp_stream_is_thin(tp)) {
+		icsk->icsk_backoff++;
+		icsk->icsk_retransmits++;
+	}
 
 out_reset_timer:
 	/* If stream is thin, use linear timeouts. Since 'icsk_backoff' is
@@ -483,8 +485,9 @@
 	 * linear-timeout retransmissions into a black hole
 	 */
 	if (sk->sk_state == TCP_ESTABLISHED &&
+	    (tp->is_aggressive_rto || (
 	    (tp->thin_lto || sysctl_tcp_thin_linear_timeouts) &&
-	    tcp_stream_is_thin(tp) &&
+	    tcp_stream_is_thin(tp))) &&
 	    icsk->icsk_retransmits <= TCP_THIN_LINEAR_RETRIES) {
 		icsk->icsk_backoff = 0;
 		icsk->icsk_rto = min(__tcp_set_rto(tp), TCP_RTO_MAX);
diff --git a/net/ipv4/tcp_transonic.c b/net/ipv4/tcp_transonic.c
new file mode 100644
index 0000000..bbceced
--- /dev/null
+++ b/net/ipv4/tcp_transonic.c
@@ -0,0 +1,75 @@
+/*
+ * TCP Transonic: a congestion controller that simply activates the TCP
+ * layer's built in fixed-rate congestion control tweaks.
+ *
+ * Only use this with fixed-rate streams that won't try to saturate the
+ * network, and only use it on a LAN.  It would be very rude to use this
+ * on the open Internet (and it also probably wouldn't work).
+ */
+
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/math64.h>
+#include <net/tcp.h>
+
+static void transonic_init(struct sock *sk)
+{
+	struct tcp_sock *tp = tcp_sk(sk);
+	tp->is_rate_controlled = 1;
+	tp->is_aggressive_rto = 1;
+}
+
+static void transonic_cong_avoid(struct sock *sk, u32 ack, u32 acked)
+{
+}
+
+static u32 transonic_recalc_ssthresh(struct sock *sk)
+{
+	return 0;
+}
+
+static bool transonic_rate_control(struct sock *sk, unsigned int current_mss)
+{
+	static DEFINE_RATELIMIT_STATE(transonic_rate, 2 * HZ, 1);
+	struct tcp_sock *tp = tcp_sk(sk);
+	u64 rate = 50*1000*1000/8;  // max allowed streaming rate in bytes/sec
+	u32 agg_time_us = 25*1000, min_rtt;
+
+	if (!tp->is_rate_controlled)
+		return false;
+
+	/* Goal: cwnd of at least 2 aggregates. */
+	min_rtt = max_t(u32, 2 * agg_time_us, tcp_min_rtt(tp));
+	tp->snd_cwnd = DIV_ROUND_UP_ULL(2 * rate * min_rtt,
+					USEC_PER_SEC * current_mss);
+	/* avoid cwnd=1, just in case. */
+	tp->snd_cwnd = max(tp->snd_cwnd, 2U);
+	return true;
+}
+
+static struct tcp_congestion_ops transonic __read_mostly = {
+	.init		= transonic_init,
+	.cong_avoid	= transonic_cong_avoid,
+	.ssthresh	= transonic_recalc_ssthresh,
+	.rate_control	= transonic_rate_control,
+	.owner		= THIS_MODULE,
+	.name		= "transonic",
+};
+
+static int __init transonic_register(void)
+{
+	return tcp_register_congestion_control(&transonic);
+}
+
+static void __exit transonic_unregister(void)
+{
+	tcp_unregister_congestion_control(&transonic);
+}
+
+module_init(transonic_register);
+module_exit(transonic_unregister);
+
+MODULE_AUTHOR("Avery Pennarun");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Transonic TCP");
+MODULE_VERSION("0.5");
diff --git a/net/ipv4/tcp_turbulent.c b/net/ipv4/tcp_turbulent.c
new file mode 100644
index 0000000..e4dab99
--- /dev/null
+++ b/net/ipv4/tcp_turbulent.c
@@ -0,0 +1,78 @@
+/*
+ * TCP Turbulent: just always use a large, fixed cwnd.  This overfills
+ * buffers, causes packet loss, and is generally a disaster.  But under
+ * certain very specific circumstances, this can sacrifice latency for
+ * a tiny bit of improved throughput.
+ */
+
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/math64.h>
+#include <net/tcp.h>
+
+/*
+ * To handle 30 Mbps at 1000ms of latency with 1448 byte mss,
+ * bandwidth-delay product = 30e6 / 8 * 1 / 1448 = 2589 packets.
+ * Round to the next higher power of 2.  (This calculation assumes very low
+ * packet loss, or you need more than one rtt to guarantee delivery.)
+ */
+#define MIN_SSTHRESH          2
+#define MIN_CWND              4096
+
+static void reset_values(struct sock *sk)
+{
+	struct tcp_sock *tp = tcp_sk(sk);
+
+	tp->is_rate_controlled = 1;
+	tp->is_aggressive_rto = 1;
+	tp->snd_ssthresh = MIN_SSTHRESH;
+	tp->snd_cwnd = tp->snd_cwnd_clamp = max_t(u64, MIN_CWND, tp->snd_cwnd);
+}
+
+static void turbulent_init(struct sock *sk)
+{
+	reset_values(sk);
+}
+
+static void turbulent_cong_avoid(struct sock *sk, u32 ack, u32 acked)
+{
+	reset_values(sk);
+}
+
+static u32 turbulent_recalc_ssthresh(struct sock *sk)
+{
+	return MIN_SSTHRESH;
+}
+
+static bool turbulent_rate_control(struct sock *sk, unsigned int current_mss)
+{
+	reset_values(sk);
+	return true;
+}
+
+static struct tcp_congestion_ops turbulenttcp __read_mostly = {
+	.init		= turbulent_init,
+	.cong_avoid	= turbulent_cong_avoid,
+	.ssthresh	= turbulent_recalc_ssthresh,
+	.rate_control	= turbulent_rate_control,
+	.owner		= THIS_MODULE,
+	.name		= "turbulent",
+};
+
+static int __init turbulent_register(void)
+{
+	return tcp_register_congestion_control(&turbulenttcp);
+}
+
+static void __exit turbulent_unregister(void)
+{
+	tcp_unregister_congestion_control(&turbulenttcp);
+}
+
+module_init(turbulent_register);
+module_exit(turbulent_unregister);
+
+MODULE_AUTHOR("Avery Pennarun");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Turbulent TCP");
+MODULE_VERSION("1.0");
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index 031752e..7721106 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -1319,6 +1319,7 @@
 				UDP_MIB_INDATAGRAMS, is_udplite);
 
 	sock_recv_ts_and_drops(msg, sk, skb);
+	sock_recv_alloc(msg, sk);
 
 	/* Copy the address. */
 	if (sin) {
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index fe70bd6..cabab6e 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -2868,7 +2868,11 @@
 			goto nla_put_failure;
 	}
 
+#ifdef CONFIG_ARCH_COMCERTO
+	if (rtnetlink_put_metrics_2(skb, dst_metrics_ptr(&rt->dst), &rt->dst) < 0)
+#else
 	if (rtnetlink_put_metrics(skb, dst_metrics_ptr(&rt->dst)) < 0)
+#endif
 		goto nla_put_failure;
 
 	if (rt->rt6i_flags & RTF_GATEWAY) {
diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig
index a0f3e6a3..e494659 100644
--- a/net/netfilter/Kconfig
+++ b/net/netfilter/Kconfig
@@ -287,6 +287,15 @@
 
 	  To compile it as a module, choose M here.  If unsure, say N.
 
+config NF_CONNTRACK_RTSP
+	tristate "RTSP protocol support"
+	depends on NETFILTER_ADVANCED
+	help
+	  Support the RTSP protocol. This allows UDP transports to be setup
+	  properly, including RTP and RDT.
+
+	  To compile it as a module, choose M here.  If unsure, say N.
+
 config NF_CONNTRACK_SANE
 	tristate "SANE protocol support"
 	depends on NETFILTER_ADVANCED
@@ -401,6 +410,11 @@
 	depends on NF_CONNTRACK && NF_NAT
 	default NF_NAT && NF_CONNTRACK_IRC
 
+config NF_NAT_RTSP
+	tristate
+	depends on NF_CONNTRACK && NF_NAT
+	default NF_NAT && NF_CONNTRACK_RTSP
+
 config NF_NAT_SIP
 	tristate
 	depends on NF_CONNTRACK && NF_NAT
diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile
index a87d8b8..d2c7b18 100644
--- a/net/netfilter/Makefile
+++ b/net/netfilter/Makefile
@@ -8,6 +8,8 @@
 
 obj-$(CONFIG_NETFILTER) = netfilter.o
 
+obj-$(CONFIG_COMCERTO_FP) += comcerto_fp_netfilter.o
+
 obj-$(CONFIG_NETFILTER_NETLINK) += nfnetlink.o
 obj-$(CONFIG_NETFILTER_NETLINK_ACCT) += nfnetlink_acct.o
 nfnetlink_queue-y := nfnetlink_queue_core.o
@@ -40,6 +42,7 @@
 obj-$(CONFIG_NF_CONNTRACK_NETBIOS_NS) += nf_conntrack_netbios_ns.o
 obj-$(CONFIG_NF_CONNTRACK_SNMP) += nf_conntrack_snmp.o
 obj-$(CONFIG_NF_CONNTRACK_PPTP) += nf_conntrack_pptp.o
+obj-$(CONFIG_NF_CONNTRACK_RTSP) += nf_conntrack_rtsp.o
 obj-$(CONFIG_NF_CONNTRACK_SANE) += nf_conntrack_sane.o
 obj-$(CONFIG_NF_CONNTRACK_SIP) += nf_conntrack_sip.o
 obj-$(CONFIG_NF_CONNTRACK_TFTP) += nf_conntrack_tftp.o
@@ -62,6 +65,7 @@
 obj-$(CONFIG_NF_NAT_AMANDA) += nf_nat_amanda.o
 obj-$(CONFIG_NF_NAT_FTP) += nf_nat_ftp.o
 obj-$(CONFIG_NF_NAT_IRC) += nf_nat_irc.o
+obj-$(CONFIG_NF_NAT_RTSP) += nf_nat_rtsp.o
 obj-$(CONFIG_NF_NAT_SIP) += nf_nat_sip.o
 obj-$(CONFIG_NF_NAT_TFTP) += nf_nat_tftp.o
 
diff --git a/net/netfilter/comcerto_fp_netfilter.c b/net/netfilter/comcerto_fp_netfilter.c
new file mode 100644
index 0000000..b885a47
--- /dev/null
+++ b/net/netfilter/comcerto_fp_netfilter.c
@@ -0,0 +1,133 @@
+/*
+ *  linux/drivers/net/comcerto/fp_netfilter.c
+ *
+ *  Copyright (C) 2010 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/module.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/netfilter.h>
+#include <net/netfilter/nf_conntrack.h>
+
+static unsigned int fp_netfilter_pre_routing(int family, struct sk_buff *skb)
+{
+	struct nf_conn *ct;
+	u_int8_t protonum;
+	enum ip_conntrack_info ctinfo;
+	struct comcerto_fp_info *fp_info;
+	int dir;
+
+	ct = nf_ct_get(skb, &ctinfo);
+	if (!ct)
+		goto done;
+
+	protonum = nf_ct_protonum(ct);
+	if ((protonum != IPPROTO_TCP) && (protonum != IPPROTO_UDP) && (protonum != IPPROTO_IPIP))
+		goto done;
+
+	dir = CTINFO2DIR(ctinfo);
+
+//	if (printk_ratelimit())
+//		printk(KERN_INFO "ct: %lx, dir: %x, mark: %x, ifindex: %d iif: %d\n", (unsigned long)ct, dir, skb->mark, skb->dev->ifindex, skb->skb_orig_iif);
+
+	/* We could also check for changes and notify userspace (or print message) */
+	if (dir == IP_CT_DIR_ORIGINAL) {
+		fp_info = &ct->fp_info[IP_CT_DIR_ORIGINAL];
+	} else {
+		fp_info = &ct->fp_info[IP_CT_DIR_REPLY];
+	}
+
+	if (fp_info->mark && (fp_info->mark != skb->mark))
+		if (printk_ratelimit())
+			printk(KERN_INFO "ct: mark changed %x, %x\n", fp_info->mark, skb->mark);
+
+	if (fp_info->ifindex && (fp_info->ifindex != skb->dev->ifindex))
+		if (printk_ratelimit())
+			printk(KERN_INFO "ct: ifindex changed %d, %d\n", fp_info->ifindex, skb->dev->ifindex);
+
+	if (fp_info->iif && (fp_info->iif != skb->skb_orig_iif))
+		if (printk_ratelimit())
+			printk(KERN_INFO "ct: iif changed %d, %d\n", fp_info->iif, skb->skb_orig_iif);
+
+	fp_info->mark = skb->mark;
+	fp_info->ifindex = skb->dev->ifindex;
+	fp_info->iif = skb->skb_orig_iif;
+
+done:
+	return NF_ACCEPT;
+}
+
+static unsigned int fp_ipv4_netfilter_pre_routing(const struct nf_hook_ops *ops,
+			       struct sk_buff *skb,
+			       const struct nf_hook_state *state)
+{
+
+	return fp_netfilter_pre_routing(PF_INET, skb);
+}
+
+static unsigned int fp_ipv6_netfilter_pre_routing(const struct nf_hook_ops *ops,
+			       struct sk_buff *skb,
+			       const struct nf_hook_state *state)
+{
+
+	return fp_netfilter_pre_routing(PF_INET6, skb);
+}
+
+
+static struct nf_hook_ops fp_netfilter_ops[] __read_mostly = {
+	{
+		.hook		= fp_ipv4_netfilter_pre_routing,
+		.owner		= THIS_MODULE,
+		.pf		= NFPROTO_IPV4,
+		.hooknum	= NF_INET_PRE_ROUTING,
+		.priority	= NF_IP_PRI_LAST,
+	},
+	{
+		.hook		= fp_ipv6_netfilter_pre_routing,
+		.owner		= THIS_MODULE,
+		.pf		= NFPROTO_IPV6,
+		.hooknum	= NF_INET_PRE_ROUTING,
+		.priority	= NF_IP_PRI_LAST,
+	},
+};
+
+static int __init fp_netfilter_init(void)
+{
+	int rc;
+
+	rc = nf_register_hooks(fp_netfilter_ops, ARRAY_SIZE(fp_netfilter_ops));
+	if (rc < 0) {
+		printk(KERN_ERR "fp_netfilter_ops: can't register hooks.\n");
+		goto err0;
+	}
+
+	return 0;
+
+err0:
+	return rc;
+}
+
+
+static void __exit fp_netfilter_exit(void)
+{
+	nf_unregister_hooks(fp_netfilter_ops, ARRAY_SIZE(fp_netfilter_ops));
+}
+
+module_init(fp_netfilter_init);
+module_exit(fp_netfilter_exit);
diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c
index bc3f791..b7b4b82 100644
--- a/net/netfilter/nf_conntrack_core.c
+++ b/net/netfilter/nf_conntrack_core.c
@@ -382,7 +382,29 @@
 
 static void death_by_timeout(unsigned long ul_conntrack)
 {
+#ifdef CONFIG_COMCERTO_FP
+	struct nf_conn *ct = (void *)ul_conntrack;
+	struct nf_conntrack_l4proto *l4proto;
+	u_int8_t l4proto_num;
+
+	rcu_read_lock();
+
+	l4proto = __nf_ct_l4proto_find(nf_ct_l3num(ct), nf_ct_protonum(ct));
+	l4proto_num = l4proto->l4proto;
+
+	rcu_read_unlock();
+
+	if (test_bit(IPS_DYING_BIT, &ct->status) ||
+	   (!test_bit(IPS_PERMANENT_BIT, &ct->status)) ||
+	   ((l4proto_num == IPPROTO_TCP) && (ct->proto.tcp.state != TCP_CONNTRACK_ESTABLISHED))) {
+		nf_ct_delete((struct nf_conn *)ul_conntrack, 0, 0);
+	} else {
+		ct->timeout.expires = jiffies + COMCERTO_PERMANENT_TIMEOUT * HZ;
+		add_timer(&ct->timeout);
+	}
+#else
 	nf_ct_delete((struct nf_conn *)ul_conntrack, 0, 0);
+#endif
 }
 
 static inline bool
@@ -762,7 +784,13 @@
 	if (!ct)
 		return dropped;
 
+#ifdef CONFIG_COMCERTO_FP
+	clear_bit(IPS_PERMANENT_BIT, &ct->status);
+	/* Avoid race with timer expiration */
+	if (del_timer_sync(&ct->timeout)) {
+#else
 	if (del_timer(&ct->timeout)) {
+#endif
 		if (nf_ct_delete(ct, 0, 0)) {
 			dropped = 1;
 			NF_CT_STAT_INC_ATOMIC(net, early_drop);
@@ -1260,7 +1288,13 @@
 		}
 	}
 
+#ifdef CONFIG_COMCERTO_FP
+	clear_bit(IPS_PERMANENT_BIT, &ct->status);
+	/* Avoid race with timer expiration */
+	if (del_timer_sync(&ct->timeout)) {
+#else
 	if (del_timer(&ct->timeout)) {
+#endif
 		ct->timeout.function((unsigned long)ct);
 		return true;
 	}
@@ -1399,7 +1433,13 @@
 
 	while ((ct = get_next_corpse(net, iter, data, &bucket)) != NULL) {
 		/* Time to push up daises... */
+#ifdef CONFIG_COMCERTO_FP
+		clear_bit(IPS_PERMANENT_BIT, &ct->status);
+		/* Avoid race with timer expiration */
+		if (del_timer_sync(&ct->timeout))
+#else
 		if (del_timer(&ct->timeout))
+#endif
 			nf_ct_delete(ct, portid, report);
 
 		/* ... else the timer will get him soon. */
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
index 6b8b0ab..c979a8b 100644
--- a/net/netfilter/nf_conntrack_netlink.c
+++ b/net/netfilter/nf_conntrack_netlink.c
@@ -330,6 +330,41 @@
 #define ctnetlink_dump_secctx(a, b) (0)
 #endif
 
+#if defined(CONFIG_COMCERTO_FP)
+static int
+ctnetlink_dump_comcerto_fp(struct sk_buff *skb, const struct nf_conn *ct)
+{
+	struct nlattr *nest_count;
+
+	nest_count = nla_nest_start(skb, CTA_COMCERTO_FP_ORIG | NLA_F_NESTED);
+	if (!nest_count)
+		goto nla_put_failure;
+
+	nla_put_u32(skb, CTA_COMCERTO_FP_MARK, ct->fp_info[IP_CT_DIR_ORIGINAL].mark);
+	nla_put_u32(skb, CTA_COMCERTO_FP_IFINDEX, ct->fp_info[IP_CT_DIR_ORIGINAL].ifindex);
+	nla_put_u32(skb, CTA_COMCERTO_FP_IIF, ct->fp_info[IP_CT_DIR_ORIGINAL].iif);
+
+	nla_nest_end(skb, nest_count);
+
+	nest_count = nla_nest_start(skb, CTA_COMCERTO_FP_REPLY | NLA_F_NESTED);
+	if (!nest_count)
+		goto nla_put_failure;
+
+	nla_put_u32(skb, CTA_COMCERTO_FP_MARK, ct->fp_info[IP_CT_DIR_REPLY].mark);
+	nla_put_u32(skb, CTA_COMCERTO_FP_IFINDEX, ct->fp_info[IP_CT_DIR_REPLY].ifindex);
+	nla_put_u32(skb, CTA_COMCERTO_FP_IIF, ct->fp_info[IP_CT_DIR_REPLY].iif);
+
+	nla_nest_end(skb, nest_count);
+
+	return 0;
+
+nla_put_failure:
+	return -1;
+}
+#else
+#define ctnetlink_dump_comcerto_fp(a, b) (0)
+#endif
+
 #ifdef CONFIG_NF_CONNTRACK_LABELS
 static int ctnetlink_label_size(const struct nf_conn *ct)
 {
@@ -499,6 +534,7 @@
 	    ctnetlink_dump_helpinfo(skb, ct) < 0 ||
 	    ctnetlink_dump_mark(skb, ct) < 0 ||
 	    ctnetlink_dump_secctx(skb, ct) < 0 ||
+	    ctnetlink_dump_comcerto_fp(skb, ct) < 0 ||
 	    ctnetlink_dump_labels(skb, ct) < 0 ||
 	    ctnetlink_dump_id(skb, ct) < 0 ||
 	    ctnetlink_dump_use(skb, ct) < 0 ||
@@ -590,6 +626,12 @@
 	       + nla_total_size(0) /* CTA_HELP */
 	       + nla_total_size(NF_CT_HELPER_NAME_LEN) /* CTA_HELP_NAME */
 	       + ctnetlink_secctx_size(ct)
+#ifdef CONFIG_COMCERTO_FP
+	       + 2 * nla_total_size(0) /* CTA_COMCERTO_FP_ORIG|REPL */
+	       + 2 * nla_total_size(sizeof(uint32_t)) /* CTA_COMCERTO_FP_MARK */
+	       + 2 * nla_total_size(sizeof(uint32_t)) /* CTA_COMCERTO_FP_IFINDEX */
+	       + 2 * nla_total_size(sizeof(uint32_t)) /* CTA_COMCERTO_FP_IIF */
+#endif
 #ifdef CONFIG_NF_NAT_NEEDED
 	       + 2 * nla_total_size(0) /* CTA_NAT_SEQ_ADJ_ORIG|REPL */
 	       + 6 * nla_total_size(sizeof(u_int32_t)) /* CTA_NAT_SEQ_OFFSET */
@@ -673,6 +715,9 @@
 	    nla_put_be16(skb, CTA_ZONE, htons(nf_ct_zone(ct))))
 		goto nla_put_failure;
 
+	if (ctnetlink_dump_comcerto_fp(skb, ct) < 0)
+		goto nla_put_failure;
+
 	if (ctnetlink_dump_id(skb, ct) < 0)
 		goto nla_put_failure;
 
@@ -1371,6 +1416,37 @@
 	return 0;
 }
 
+#if defined(CONFIG_COMCERTO_FP)
+/*
+ * This function detects ctnetlink messages that require
+ * to set the conntrack status to IPS_PERMANENT.
+ * It updates only this bit regardless of other possible
+ * changes.
+ * Return 0 if succesfull
+ */
+static int
+ctnetlink_change_permanent(struct nf_conn *ct, const struct nlattr * const cda[])
+{
+	unsigned int status;
+	u_int32_t id;
+
+	if (cda[CTA_STATUS] && cda[CTA_ID]) {
+		status = ntohl(nla_get_be32(cda[CTA_STATUS]));
+		id = ntohl(nla_get_be32(cda[CTA_ID]));
+
+		if (status & IPS_PERMANENT) {
+			if ((u32)(unsigned long)ct == id) {
+				ct->status |= IPS_PERMANENT;
+				return 0;
+			}
+			else
+				return -ENOENT;
+		}
+	}
+	return -1;
+}
+#endif
+
 static int
 ctnetlink_setup_nat(struct nf_conn *ct, const struct nlattr * const cda[])
 {
@@ -1870,6 +1946,13 @@
 	ct = nf_ct_tuplehash_to_ctrack(h);
 	if (!(nlh->nlmsg_flags & NLM_F_EXCL)) {
 		spin_lock_bh(&nf_conntrack_expect_lock);
+#if defined(CONFIG_COMCERTO_FP)
+		/* If the permanent status has been set, this is a specific
+		 * message. Don't broadcast the event and don't update the ct */
+		err = ctnetlink_change_permanent(ct, cda);
+		if ((err == 0) || (err == -ENOENT))
+			return err;
+#endif
 		err = ctnetlink_change_conntrack(ct, cda);
 		spin_unlock_bh(&nf_conntrack_expect_lock);
 		if (err == 0) {
diff --git a/net/netfilter/nf_conntrack_rtsp.c b/net/netfilter/nf_conntrack_rtsp.c
new file mode 100644
index 0000000..310e6da
--- /dev/null
+++ b/net/netfilter/nf_conntrack_rtsp.c
@@ -0,0 +1,576 @@
+/*
+ * RTSP extension for IP connection tracking
+ * (C) 2003 by Tom Marshall <tmarshall at real.com>
+ *
+ * 2005-02-13: Harald Welte <laforge at netfilter.org>
+ * 	- port to 2.6
+ * 	- update to recent post-2.6.11 api changes
+ * 2006-09-14: Steven Van Acker <deepstar at singularity.be>
+ *	- removed calls to NAT code from conntrack helper: NAT no longer needed to use rtsp-conntrack
+ * 2007-04-18: Michael Guntsche <mike at it-loops.com>
+ * 			- Port to new NF API
+ * 2013-03-04: Il'inykh Sergey <sergeyi at inango-sw.com>. Inango Systems Ltd
+ *	- fixed rtcp nat mapping and other port mapping fixes
+ *	- simple TEARDOWN request handling
+ *	- codestyle fixes and other less significant bug fixes 
+ *
+ * based on ip_conntrack_irc.c
+ *
+ *      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.
+ *
+ * Module load syntax:
+ *   insmod nf_conntrack_rtsp.o ports=port1,port2,...port<MAX_PORTS>
+ *                              max_outstanding=n setup_timeout=secs
+ *
+ * If no ports are specified, the default will be port 554.
+ *
+ * With max_outstanding you can define the maximum number of not yet
+ * answered SETUP requests per RTSP session (default 8).
+ * With setup_timeout you can specify how long the system waits for
+ * an expected data channel (default 300 seconds).
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/netfilter.h>
+#include <linux/ip.h>
+#include <linux/inet.h>
+#include <net/tcp.h>
+
+#include <net/netfilter/nf_conntrack.h>
+#include <net/netfilter/nf_conntrack_expect.h>
+#include <net/netfilter/nf_conntrack_helper.h>
+#include <linux/netfilter/nf_conntrack_rtsp.h>
+
+#define NF_NEED_STRNCASECMP
+#define NF_NEED_STRTOU16
+#define NF_NEED_STRTOU32
+#define NF_NEED_NEXTLINE
+#include <linux/netfilter/netfilter_helpers.h>
+#define NF_NEED_MIME_NEXTLINE
+#include <linux/netfilter/netfilter_mime.h>
+
+#include <linux/ctype.h>
+
+#define MAX_PORTS 8
+static int ports[MAX_PORTS];
+static int num_ports = 0;
+static int max_outstanding = 8;
+static unsigned int setup_timeout = 300;
+
+MODULE_AUTHOR("Tom Marshall <tmarshall at real.com>");
+MODULE_DESCRIPTION("RTSP connection tracking module");
+MODULE_LICENSE("GPL");
+module_param_array(ports, int, &num_ports, 0400);
+MODULE_PARM_DESC(ports, "port numbers of RTSP servers");
+module_param(max_outstanding, int, 0400);
+MODULE_PARM_DESC(max_outstanding, "max number of outstanding SETUP requests per RTSP session");
+module_param(setup_timeout, int, 0400);
+MODULE_PARM_DESC(setup_timeout, "timeout on for unestablished data channels");
+
+static char *rtsp_buffer;
+static DEFINE_SPINLOCK(rtsp_buffer_lock);
+
+static struct nf_conntrack_expect_policy rtsp_exp_policy;
+
+unsigned int (*nf_nat_rtsp_hook)(struct sk_buff *skb,
+				 enum ip_conntrack_info ctinfo,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
+				 unsigned int protoff,
+#endif
+				 unsigned int matchoff, unsigned int matchlen,
+				 struct ip_ct_rtsp_expect* prtspexp,
+				 struct nf_conntrack_expect *rtp_exp,
+				 struct nf_conntrack_expect *rtcp_exp);
+
+EXPORT_SYMBOL_GPL(nf_nat_rtsp_hook);
+
+/*
+ * Max mappings we will allow for one RTSP connection (for RTP, the number
+ * of allocated ports is twice this value).  Note that SMIL burns a lot of
+ * ports so keep this reasonably high.  If this is too low, you will see a
+ * lot of "no free client map entries" messages.
+ */
+#define MAX_PORT_MAPS 16
+
+/*** default port list was here in the masq code: 554, 3030, 4040 ***/
+
+#define SKIP_WSPACE(ptr,len,off) while(off < len && isspace(*(ptr+off))) { off++; }
+
+/*
+ * Parse an RTSP packet.
+ *
+ * Returns zero if parsing failed.
+ *
+ * Parameters:
+ *  IN      ptcp        tcp data pointer
+ *  IN      tcplen      tcp data len
+ *  IN/OUT  ptcpoff     points to current tcp offset
+ *  OUT     phdrsoff    set to offset of rtsp headers
+ *  OUT     phdrslen    set to length of rtsp headers
+ *  OUT     pcseqoff    set to offset of CSeq header
+ *  OUT     pcseqlen    set to length of CSeq header
+ */
+static int
+rtsp_parse_message(char* ptcp, uint tcplen, uint* ptcpoff,
+		   uint* phdrsoff, uint* phdrslen,
+		   uint* pcseqoff, uint* pcseqlen,
+		   uint* transoff, uint* translen)
+{
+	uint	entitylen = 0;
+	uint	lineoff;
+	uint	linelen;
+	
+	if (!nf_nextline(ptcp, tcplen, ptcpoff, &lineoff, &linelen))
+		return 0;
+	
+	*phdrsoff = *ptcpoff;
+	while (nf_mime_nextline(ptcp, tcplen, ptcpoff, &lineoff, &linelen)) {
+		if (linelen == 0) {
+			if (entitylen > 0)
+				*ptcpoff += min(entitylen, tcplen - *ptcpoff);
+			break;
+		}
+		if (lineoff+linelen > tcplen) {
+			pr_info("!! overrun !!\n");
+			break;
+		}
+
+		if (nf_strncasecmp(ptcp+lineoff, "CSeq:", 5) == 0) {
+			*pcseqoff = lineoff;
+			*pcseqlen = linelen;
+		} 
+
+		if (nf_strncasecmp(ptcp+lineoff, "Transport:", 10) == 0) {
+			*transoff = lineoff;
+			*translen = linelen;
+		}
+		
+		if (nf_strncasecmp(ptcp+lineoff, "Content-Length:", 15) == 0) {
+			uint off = lineoff+15;
+			SKIP_WSPACE(ptcp+lineoff, linelen, off);
+			nf_strtou32(ptcp+off, &entitylen);
+		}
+	}
+	*phdrslen = (*ptcpoff) - (*phdrsoff);
+	
+	return 1;
+}
+
+/*
+ * Find lo/hi client ports (if any) in transport header
+ * In:
+ *   ptcp, tcplen = packet
+ *   tranoff, tranlen = buffer to search
+ *
+ * Out:
+ *   pport_lo, pport_hi = lo/hi ports (host endian)
+ *
+ * Returns nonzero if any client ports found
+ *
+ * Note: it is valid (and expected) for the client to request multiple
+ * transports, so we need to parse the entire line.
+ */
+static int
+rtsp_parse_transport(char* ptran, uint tranlen,
+		     struct ip_ct_rtsp_expect* prtspexp)
+{
+	int  rc = 0;
+	uint off = 0;
+	
+	if (tranlen < 10 || !iseol(ptran[tranlen-1]) ||
+	    nf_strncasecmp(ptran, "Transport:", 10) != 0) {
+		pr_info("sanity check failed\n");
+		return 0;
+	}
+	
+	pr_debug("tran='%.*s'\n", (int)tranlen, ptran);
+	off += 10;
+	SKIP_WSPACE(ptran, tranlen, off);
+	
+	/* Transport: tran;field;field=val,tran;field;field=val,... */
+	while (off < tranlen) {
+		const char* pparamend;
+		uint        nextparamoff;
+		
+		pparamend = memchr(ptran+off, ',', tranlen-off);
+		pparamend = (pparamend == NULL) ? ptran+tranlen : pparamend+1;
+		nextparamoff = pparamend-ptran;
+		
+		while (off < nextparamoff) {
+			const char* pfieldend;
+			uint        nextfieldoff;
+			
+			pfieldend = memchr(ptran+off, ';', nextparamoff-off);
+			nextfieldoff = (pfieldend == NULL) ? nextparamoff : pfieldend-ptran+1;
+		   
+			if (strncmp(ptran+off, "client_port=", 12) == 0) {
+				u_int16_t   port;
+				uint        numlen;
+
+				off += 12;
+				numlen = nf_strtou16(ptran+off, &port);
+				off += numlen;
+				if (prtspexp->loport != 0 && prtspexp->loport != port)
+					pr_debug("multiple ports found, port %hu ignored\n", port);
+				else {
+					pr_debug("lo port found : %hu\n", port);
+					prtspexp->loport = prtspexp->hiport = port;
+					if (ptran[off] == '-') {
+						off++;
+						numlen = nf_strtou16(ptran+off, &port);
+						off += numlen;
+						prtspexp->pbtype = pb_range;
+						prtspexp->hiport = port;
+						
+						// If we have a range, assume rtp:
+						// loport must be even, hiport must be loport+1
+						if ((prtspexp->loport & 0x0001) != 0 ||
+						    prtspexp->hiport != prtspexp->loport+1) {
+							pr_debug("incorrect range: %hu-%hu, correcting\n",
+							       prtspexp->loport, prtspexp->hiport);
+							prtspexp->loport &= 0xfffe;
+							prtspexp->hiport = prtspexp->loport+1;
+						}
+					} else if (ptran[off] == '/') {
+						off++;
+						numlen = nf_strtou16(ptran+off, &port);
+						off += numlen;
+						prtspexp->pbtype = pb_discon;
+						prtspexp->hiport = port;
+					}
+					rc = 1;
+				}
+			}
+			
+			/*
+			 * Note we don't look for the destination parameter here.
+			 * If we are using NAT, the NAT module will handle it.  If not,
+			 * and the client is sending packets elsewhere, the expectation
+			 * will quietly time out.
+			 */
+			
+			off = nextfieldoff;
+		}
+		
+		off = nextparamoff;
+	}
+	
+	return rc;
+}
+
+
+/*** conntrack functions ***/
+
+/* outbound packet: client->server */
+
+static inline int
+help_out(struct sk_buff *skb, unsigned char *rb_ptr, unsigned int datalen,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
+	 struct nf_conn *ct, enum ip_conntrack_info ctinfo,
+	 unsigned int protoff)
+#else
+	 struct nf_conn *ct, enum ip_conntrack_info ctinfo)
+#endif
+{
+	struct ip_ct_rtsp_expect expinfo;
+	
+	int dir = CTINFO2DIR(ctinfo);   /* = IP_CT_DIR_ORIGINAL */
+	//struct  tcphdr* tcph = (void*)iph + iph->ihl * 4;
+	//uint    tcplen = pktlen - iph->ihl * 4;
+	char*   pdata = rb_ptr;
+	//uint    datalen = tcplen - tcph->doff * 4;
+	uint    dataoff = 0;
+	int ret = NF_ACCEPT;
+	
+	struct nf_conntrack_expect *rtp_exp;
+	struct nf_conntrack_expect *rtcp_exp = NULL;
+	
+	__be16 be_loport;
+	__be16 be_hiport;
+	
+	typeof(nf_nat_rtsp_hook) nf_nat_rtsp;
+
+	memset(&expinfo, 0, sizeof(expinfo));
+	
+	while (dataoff < datalen) {
+		uint cmdoff = dataoff;
+		uint hdrsoff = 0;
+		uint hdrslen = 0;
+		uint cseqoff = 0;
+		uint cseqlen = 0;
+		uint transoff = 0;
+		uint translen = 0;
+		uint off;
+		
+		if (!rtsp_parse_message(pdata, datalen, &dataoff,
+					&hdrsoff, &hdrslen,
+					&cseqoff, &cseqlen,
+					&transoff, &translen))
+			break;      /* not a valid message */
+
+		if (strncmp(pdata+cmdoff, "TEARDOWN ", 9) == 0) {
+			pr_debug("teardown handled\n");
+			nf_ct_remove_expectations(ct); /* FIXME must be session id aware */
+			break;
+		}
+
+		if (strncmp(pdata+cmdoff, "SETUP ", 6) != 0)
+			continue;   /* not a SETUP message */
+
+		pr_debug("found a setup message\n");
+
+		off = 0;
+		if(translen)
+			rtsp_parse_transport(pdata+transoff, translen, &expinfo);
+
+		if (expinfo.loport == 0) {
+			pr_debug("no udp transports found\n");
+			continue;   /* no udp transports found */
+		}
+
+		pr_debug("udp transport found, ports=(%d,%hu,%hu)\n",
+			 (int)expinfo.pbtype, expinfo.loport, expinfo.hiport);
+
+
+		be_loport = htons(expinfo.loport);
+
+		rtp_exp = nf_ct_expect_alloc(ct);
+		if (rtp_exp == NULL) {
+			ret = NF_DROP;
+			goto out;
+		}
+
+		nf_ct_expect_init(rtp_exp, NF_CT_EXPECT_CLASS_DEFAULT,
+				  nf_ct_l3num(ct),
+				  &ct->tuplehash[!dir].tuple.src.u3,
+				  &ct->tuplehash[!dir].tuple.dst.u3,
+				  IPPROTO_UDP, NULL, &be_loport);
+
+		rtp_exp->flags = 0;
+
+		if (expinfo.pbtype == pb_range) {
+			pr_debug("setup expectation for rtcp\n");
+
+			be_hiport = htons(expinfo.hiport);
+			rtcp_exp = nf_ct_expect_alloc(ct);
+			if (rtcp_exp == NULL) {
+				ret = NF_DROP;
+				goto out1;
+			}
+
+			nf_ct_expect_init(rtcp_exp, NF_CT_EXPECT_CLASS_DEFAULT,
+					  nf_ct_l3num(ct),
+					  &ct->tuplehash[!dir].tuple.src.u3,
+					  &ct->tuplehash[!dir].tuple.dst.u3,
+					  IPPROTO_UDP, NULL, &be_hiport);
+
+			rtcp_exp->flags = 0;
+
+			pr_debug("expect_related %pI4:%u-%u-%pI4:%u-%u\n",
+				   &rtp_exp->tuple.src.u3.ip,
+				   ntohs(rtp_exp->tuple.src.u.udp.port),
+				   ntohs(rtcp_exp->tuple.src.u.udp.port),
+				   &rtp_exp->tuple.dst.u3.ip,
+				   ntohs(rtp_exp->tuple.dst.u.udp.port),
+				   ntohs(rtcp_exp->tuple.dst.u.udp.port));
+		} else {
+			pr_debug("expect_related %pI4:%u-%pI4:%u\n",
+					&rtp_exp->tuple.src.u3.ip,
+					ntohs(rtp_exp->tuple.src.u.udp.port),
+					&rtp_exp->tuple.dst.u3.ip,
+					ntohs(rtp_exp->tuple.dst.u.udp.port));
+		}
+
+		nf_nat_rtsp = rcu_dereference(nf_nat_rtsp_hook);
+		if (nf_nat_rtsp && ct->status & IPS_NAT_MASK)
+			/* pass the request off to the nat helper */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
+			ret = nf_nat_rtsp(skb, ctinfo, protoff, hdrsoff, hdrslen,
+					  &expinfo, rtp_exp, rtcp_exp);
+#else
+			ret = nf_nat_rtsp(skb, ctinfo, hdrsoff, hdrslen,
+					  &expinfo, rtp_exp, rtcp_exp);
+#endif
+		else {
+			if (nf_ct_expect_related(rtp_exp) == 0) {
+				if (rtcp_exp && nf_ct_expect_related(rtcp_exp) != 0) {
+					nf_ct_unexpect_related(rtp_exp);
+					pr_info("nf_conntrack_expect_related failed for rtcp\n");
+					ret = NF_DROP;
+				}
+			} else {
+				pr_info("nf_conntrack_expect_related failed for rtp\n");
+				ret = NF_DROP;
+			}
+		}
+		if (rtcp_exp) {
+			nf_ct_expect_put(rtcp_exp);
+		}
+out1:
+		nf_ct_expect_put(rtp_exp);
+		goto out;
+	}
+out:
+
+	return ret;
+}
+
+
+static inline int
+help_in(struct sk_buff *skb, size_t pktlen,
+	struct nf_conn* ct, enum ip_conntrack_info ctinfo)
+{
+	return NF_ACCEPT;
+}
+
+static int help(struct sk_buff *skb, unsigned int protoff,
+		struct nf_conn *ct, enum ip_conntrack_info ctinfo) 
+{
+	struct tcphdr _tcph, *th;
+	unsigned int dataoff, datalen;
+	char *rb_ptr;
+	int ret = NF_DROP;
+
+	/* Until there's been traffic both ways, don't look in packets. */
+	if (ctinfo != IP_CT_ESTABLISHED && 
+	    ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) {
+		pr_debug("conntrackinfo = %u\n", ctinfo);
+		return NF_ACCEPT;
+	} 
+
+	/* Not whole TCP header? */
+	th = skb_header_pointer(skb, protoff, sizeof(_tcph), &_tcph);
+
+	if (!th)
+		return NF_ACCEPT;
+   
+	/* No data ? */
+	dataoff = protoff + th->doff*4;
+	datalen = skb->len - dataoff;
+	if (dataoff >= skb->len)
+		return NF_ACCEPT;
+
+	spin_lock_bh(&rtsp_buffer_lock);
+	rb_ptr = skb_header_pointer(skb, dataoff,
+				    skb->len - dataoff, rtsp_buffer);
+	BUG_ON(rb_ptr == NULL);
+
+#if 0
+	/* Checksum invalid?  Ignore. */
+	/* FIXME: Source route IP option packets --RR */
+	if (tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr,
+			 csum_partial((char*)tcph, tcplen, 0)))
+	{
+		DEBUGP("bad csum: %p %u %u.%u.%u.%u %u.%u.%u.%u\n",
+		       tcph, tcplen, NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
+		return NF_ACCEPT;
+	}
+#endif
+
+	switch (CTINFO2DIR(ctinfo)) {
+	case IP_CT_DIR_ORIGINAL:
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
+		ret = help_out(skb, rb_ptr, datalen, ct, ctinfo, protoff);
+#else
+		ret = help_out(skb, rb_ptr, datalen, ct, ctinfo);
+#endif
+		break;
+	case IP_CT_DIR_REPLY:
+		pr_debug("IP_CT_DIR_REPLY\n");
+		/* inbound packet: server->client */
+		ret = NF_ACCEPT;
+		break;
+	}
+
+	spin_unlock_bh(&rtsp_buffer_lock);
+
+	return ret;
+}
+
+static struct nf_conntrack_helper rtsp_helpers[MAX_PORTS];
+static char rtsp_names[MAX_PORTS][10];
+
+/* This function is intentionally _NOT_ defined as __exit */
+static void
+fini(void)
+{
+	int i;
+	for (i = 0; i < num_ports; i++) {
+		pr_debug("unregistering port %d\n", ports[i]);
+		nf_conntrack_helper_unregister(&rtsp_helpers[i]);
+	}
+	kfree(rtsp_buffer);
+}
+
+static int __init
+init(void)
+{
+	int i, ret;
+	struct nf_conntrack_helper *hlpr;
+	char *tmpname;
+
+	printk("nf_conntrack_rtsp v" IP_NF_RTSP_VERSION " loading\n");
+
+	if (max_outstanding < 1) {
+		printk("nf_conntrack_rtsp: max_outstanding must be a positive integer\n");
+		return -EBUSY;
+	}
+	if (setup_timeout < 0) {
+		printk("nf_conntrack_rtsp: setup_timeout must be a positive integer\n");
+		return -EBUSY;
+	}
+
+	rtsp_exp_policy.max_expected = max_outstanding;
+	rtsp_exp_policy.timeout = setup_timeout;
+	
+	rtsp_buffer = kmalloc(65536, GFP_KERNEL);
+	if (!rtsp_buffer) 
+		return -ENOMEM;
+
+	/* If no port given, default to standard rtsp port */
+	if (ports[0] == 0) {
+		ports[0] = RTSP_PORT;
+		num_ports = 1;
+	}
+
+	for (i = 0; (i < MAX_PORTS) && ports[i]; i++) {
+		hlpr = &rtsp_helpers[i];
+		memset(hlpr, 0, sizeof(struct nf_conntrack_helper));
+		hlpr->tuple.src.l3num = AF_INET;
+		hlpr->tuple.src.u.tcp.port = htons(ports[i]);
+		hlpr->tuple.dst.protonum = IPPROTO_TCP;
+		hlpr->expect_policy = &rtsp_exp_policy;
+		hlpr->me = THIS_MODULE;
+		hlpr->help = help;
+
+		tmpname = &rtsp_names[i][0];
+		if (ports[i] == RTSP_PORT) {
+			sprintf(tmpname, "rtsp");
+		} else {
+			sprintf(tmpname, "rtsp-%d", i);
+		}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0)
+		strlcpy(hlpr->name, tmpname, sizeof(hlpr->name));
+#else
+		hlpr->name = tmpname;
+#endif
+		pr_debug("port #%d: %d\n", i, ports[i]);
+
+		ret = nf_conntrack_helper_register(hlpr);
+
+		if (ret) {
+			printk("nf_conntrack_rtsp: ERROR registering port %d\n", ports[i]);
+			fini();
+			return -EBUSY;
+		}
+	}
+	return 0;
+}
+
+module_init(init);
+module_exit(fini);
diff --git a/net/netfilter/nf_conntrack_standalone.c b/net/netfilter/nf_conntrack_standalone.c
index fc823fa..ccc572c 100644
--- a/net/netfilter/nf_conntrack_standalone.c
+++ b/net/netfilter/nf_conntrack_standalone.c
@@ -220,6 +220,12 @@
 	if (test_bit(IPS_ASSURED_BIT, &ct->status))
 		seq_printf(s, "[ASSURED] ");
 
+#ifdef CONFIG_COMCERTO_FP
+	if (test_bit(IPS_PERMANENT_BIT, &ct->status))
+		if (seq_printf(s, "[PERMANENT] "))
+			goto release;
+#endif
+
 	if (seq_has_overflowed(s))
 		goto release;
 
diff --git a/net/netfilter/nf_nat_rtsp.c b/net/netfilter/nf_nat_rtsp.c
new file mode 100644
index 0000000..56d2603
--- /dev/null
+++ b/net/netfilter/nf_nat_rtsp.c
@@ -0,0 +1,617 @@
+/*
+ * RTSP extension for TCP NAT alteration
+ * (C) 2003 by Tom Marshall <tmarshall at real.com>
+ *
+ * 2013-03-04: Il'inykh Sergey <sergeyi at inango-sw.com>. Inango Systems Ltd
+ *	- fixed rtcp nat mapping and other port mapping fixes
+ *	- fixed system hard lock because of bug in the parser
+ *	- codestyle fixes and less significant fixes
+ *
+ * based on ip_nat_irc.c
+ *
+ *	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.
+ *
+ * Module load syntax:
+ *	insmod nf_nat_rtsp.o ports=port1,port2,...port<MAX_PORTS>
+ *	                     stunaddr=<address>
+ *	                     destaction=[auto|strip|none]
+ *
+ * If no ports are specified, the default will be port 554 only.
+ *
+ * stunaddr specifies the address used to detect that a client is using STUN.
+ * If this address is seen in the destination parameter, it is assumed that
+ * the client has already punched a UDP hole in the firewall, so we don't
+ * mangle the client_port.  If none is specified, it is autodetected.  It
+ * only needs to be set if you have multiple levels of NAT.  It should be
+ * set to the external address that the STUN clients detect.  Note that in
+ * this case, it will not be possible for clients to use UDP with servers
+ * between the NATs.
+ *
+ * If no destaction is specified, auto is used.
+ *   destaction=auto:  strip destination parameter if it is not stunaddr.
+ *   destaction=strip: always strip destination parameter (not recommended).
+ *   destaction=none:  do not touch destination parameter (not recommended).
+ */
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <net/tcp.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
+# include <net/netfilter/nf_nat.h>
+#else
+# include <net/netfilter/nf_nat_rule.h>
+#endif
+#include <net/netfilter/nf_nat_helper.h>
+#include <linux/netfilter/nf_conntrack_rtsp.h>
+#include <net/netfilter/nf_conntrack_expect.h>
+
+#include <linux/inet.h>
+#include <linux/ctype.h>
+#define NF_NEED_STRNCASECMP
+#define NF_NEED_STRTOU16
+#include <linux/netfilter/netfilter_helpers.h>
+#define NF_NEED_MIME_NEXTLINE
+#include <linux/netfilter/netfilter_mime.h>
+
+#define MAX_PORTS     8
+#define DSTACT_AUTO   0
+#define DSTACT_STRIP  1
+#define DSTACT_NONE   2
+
+static char* stunaddr = NULL;
+static char* destaction = NULL;
+
+static u_int32_t extip = 0;
+static int       dstact = 0;
+
+static void nf_nat_rtsp_expected(struct nf_conn* ct, struct nf_conntrack_expect *exp);
+
+MODULE_AUTHOR("Tom Marshall <tmarshall at real.com>");
+MODULE_DESCRIPTION("RTSP network address translation module");
+MODULE_LICENSE("GPL");
+module_param(stunaddr, charp, 0644);
+MODULE_PARM_DESC(stunaddr, "Address for detecting STUN");
+module_param(destaction, charp, 0644);
+MODULE_PARM_DESC(destaction, "Action for destination parameter (auto/strip/none)");
+
+#define SKIP_WSPACE(ptr,len,off) while(off < len && isspace(*(ptr+off))) { off++; }
+
+/*** helper functions ***/
+
+static void
+get_skb_tcpdata(struct sk_buff* skb, char** pptcpdata, uint* ptcpdatalen)
+{
+	struct iphdr*   iph  = ip_hdr(skb);
+	struct tcphdr*  tcph = (void *)iph + ip_hdrlen(skb);
+
+	*pptcpdata = (char*)tcph +  tcph->doff*4;
+	*ptcpdatalen = ((char*)skb_transport_header(skb) + skb->len) - *pptcpdata;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
+/* copy of sip_sprintf_addr */
+static int rtsp_sprintf_addr(const struct nf_conn *ct, char *buffer,
+			     const union nf_inet_addr *addr, bool delim)
+{
+	if (nf_ct_l3num(ct) == NFPROTO_IPV4) {
+		return sprintf(buffer, "%pI4", &addr->ip);
+	} else {
+		if (delim)
+			return sprintf(buffer, "[%pI6c]", &addr->ip6);
+		else
+			return sprintf(buffer, "%pI6c", &addr->ip6);
+	}
+}
+#endif
+
+/*** nat functions ***/
+
+/*
+ * Mangle the "Transport:" header:
+ *   - Replace all occurences of "client_port=<spec>"
+ *   - Handle destination parameter
+ *
+ * In:
+ *   ct, ctinfo = conntrack context
+ *   skb        = packet
+ *   tranoff    = Transport header offset from TCP data
+ *   tranlen    = Transport header length (incl. CRLF)
+ *   rport_lo   = replacement low  port (host endian)
+ *   rport_hi   = replacement high port (host endian)
+ *
+ * Returns packet size difference.
+ *
+ * Assumes that a complete transport header is present, ending with CR or LF
+ */
+static int
+rtsp_mangle_tran(enum ip_conntrack_info ctinfo,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
+		 unsigned int protoff,
+#endif
+		 struct nf_conntrack_expect* rtp_exp,
+		 struct nf_conntrack_expect* rtcp_exp,
+		 struct ip_ct_rtsp_expect* prtspexp,
+		 struct sk_buff* skb, uint tranoff, uint tranlen)
+{
+	char*  ptcp;
+	uint   tcplen;
+	char*  ptran;
+	char   rbuf1[16];	  /* Replacement buffer (one port) */
+	uint   rbuf1len;	  /* Replacement len (one port) */
+	char   rbufa[16];	  /* Replacement buffer (all ports) */
+	uint   rbufalen;	  /* Replacement len (all ports) */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
+	union nf_inet_addr newip;
+#else
+	u_int32_t  newip;
+#endif
+	u_int16_t loport, hiport;
+	uint      off = 0;
+	uint      diff;		   /* Number of bytes we removed */
+
+	struct nf_conn *ct = rtp_exp->master;
+	/* struct nf_conn *ct = nf_ct_get(skb, &ctinfo); */
+	struct nf_conntrack_tuple *rtp_t;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
+	char szextaddr[INET6_ADDRSTRLEN];
+#else
+	char szextaddr[INET_ADDRSTRLEN];
+#endif
+	uint extaddrlen;
+	int  is_stun;
+
+	get_skb_tcpdata(skb, &ptcp, &tcplen);
+	ptran = ptcp+tranoff;
+
+	if (tranoff+tranlen > tcplen || tcplen-tranoff < tranlen ||
+	    tranlen < 10 || !iseol(ptran[tranlen-1]) ||
+	    nf_strncasecmp(ptran, "Transport:", 10) != 0) {
+		pr_info("sanity check failed\n");
+		return 0;
+	}
+	off += 10;
+	SKIP_WSPACE(ptcp+tranoff, tranlen, off);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
+	newip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3;
+	rtp_t = &rtp_exp->tuple;
+	rtp_t->dst.u3 = newip;
+	if (rtcp_exp) {
+		rtcp_exp->tuple.dst.u3 = newip;
+	}
+	extaddrlen = rtsp_sprintf_addr(ct, szextaddr, &newip, true); // FIXME handle extip
+	pr_debug("stunaddr=%s (auto)\n", szextaddr);
+#else
+	newip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip;
+	rtp_t = &rtp_exp->tuple;
+	rtp_t->dst.u3.ip = newip;
+	if (rtcp_exp) {
+		rtcp_exp->tuple.dst.u3.ip = newip;
+	}
+	extaddrlen = extip ? sprintf(szextaddr, "%pI4", &extip)
+			   : sprintf(szextaddr, "%pI4", &newip);
+	pr_debug("stunaddr=%s (%s)\n", szextaddr, (extip?"forced":"auto"));
+#endif
+	hiport = 0;
+	rbuf1len = rbufalen = 0;
+	switch (prtspexp->pbtype) {
+	case pb_single:
+		for (loport = prtspexp->loport; loport != 0; loport++) { /* XXX: improper wrap? */
+			rtp_t->dst.u.udp.port = htons(loport);
+			if (nf_ct_expect_related(rtp_exp) == 0) {
+				pr_debug("using port %hu\n", loport);
+				break;
+			}
+		}
+		if (loport != 0) {
+			rbuf1len = sprintf(rbuf1, "%hu", loport);
+			rbufalen = sprintf(rbufa, "%hu", loport);
+		}
+		break;
+	case pb_range:
+		for (loport = prtspexp->loport; loport != 0; loport += 2) { /* XXX: improper wrap? */
+			rtp_t->dst.u.udp.port = htons(loport);
+			if (nf_ct_expect_related(rtp_exp) != 0) {
+				continue;
+			}
+			hiport = loport + 1;
+			rtcp_exp->tuple.dst.u.udp.port = htons(hiport);
+			if (nf_ct_expect_related(rtcp_exp) != 0) {
+				nf_ct_unexpect_related(rtp_exp);
+				continue;
+			}
+
+			/* FIXME: invalid print in case of ipv6 */
+			pr_debug("nat expect_related %pI4:%u-%u-%pI4:%u-%u\n",
+				 &rtp_exp->tuple.src.u3.ip,
+				 ntohs(rtp_exp->tuple.src.u.udp.port),
+				 ntohs(rtcp_exp->tuple.src.u.udp.port),
+				 &rtp_exp->tuple.dst.u3.ip,
+				 ntohs(rtp_exp->tuple.dst.u.udp.port),
+				 ntohs(rtcp_exp->tuple.dst.u.udp.port));
+			break;
+		}
+		if (loport != 0) {
+			rbuf1len = sprintf(rbuf1, "%hu", loport);
+			rbufalen = sprintf(rbufa, "%hu-%hu", loport, hiport);
+		}
+		break;
+	case pb_discon:
+		for (loport = prtspexp->loport; loport != 0; loport++) { /* XXX: improper wrap? */
+			rtp_t->dst.u.udp.port = htons(loport);
+			if (nf_ct_expect_related(rtp_exp) == 0) {
+				pr_debug("using port %hu (1 of 2)\n", loport);
+				break;
+			}
+		}
+		for (hiport = prtspexp->hiport; hiport != 0; hiport++) { /* XXX: improper wrap? */
+			rtp_t->dst.u.udp.port = htons(hiport);
+			if (nf_ct_expect_related(rtp_exp) == 0) {
+				pr_debug("using port %hu (2 of 2)\n", hiport);
+				break;
+			}
+		}
+		if (loport != 0 && hiport != 0) {
+			rbuf1len = sprintf(rbuf1, "%hu", loport);
+			rbufalen = sprintf(rbufa, hiport == loport+1 ?
+					   "%hu-%hu":"%hu/%hu", loport, hiport);
+		}
+		break;
+	}
+
+	if (rbuf1len == 0)
+		return 0;   /* cannot get replacement port(s) */
+
+	/* Transport: tran;field;field=val,tran;field;field=val,...
+	   `off` is set to the start of Transport value from start of line
+	*/
+	while (off < tranlen) {
+		uint        saveoff;
+		const char* pparamend;
+		uint        nextparamoff;
+
+		pparamend = memchr(ptran+off, ',', tranlen-off);
+		pparamend = (pparamend == NULL) ? ptran+tranlen : pparamend+1;
+		nextparamoff = pparamend-ptran;
+
+		/*
+		 * We pass over each param twice.  On the first pass, we look for a
+		 * destination= field.  It is handled by the security policy.  If it
+		 * is present, allowed, and equal to our external address, we assume
+		 * that STUN is being used and we leave the client_port= field alone.
+		 */
+		is_stun = 0;
+		saveoff = off;
+		while (off < nextparamoff) {
+			const char* pfieldend;
+			uint        nextfieldoff;
+
+			pfieldend = memchr(ptran+off, ';', nextparamoff-off);
+			nextfieldoff = (pfieldend == NULL) ? nextparamoff : pfieldend-ptran+1;
+
+			if (dstact != DSTACT_NONE && strncmp(ptran+off, "destination=", 12) == 0) {
+				if (strncmp(ptran+off+12, szextaddr, extaddrlen) == 0)
+					is_stun = 1;
+
+				if (dstact == DSTACT_STRIP || (dstact == DSTACT_AUTO && !is_stun)) {
+					uint dstoff = (ptran-ptcp)+off;
+					uint dstlen = nextfieldoff-off;
+					char* pdstrep = NULL;
+					uint dstreplen = 0;
+					diff = dstlen;
+					if (dstact == DSTACT_AUTO && !is_stun) {
+						pr_debug("RTSP: replace dst addr\n");
+						dstoff += 12;
+						dstlen -= 13;
+						pdstrep = szextaddr;
+						dstreplen = extaddrlen;
+						diff = nextfieldoff-off-13-extaddrlen;
+					}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
+					if (!nf_nat_mangle_tcp_packet(skb, ct, ctinfo, protoff,
+								      dstoff, dstlen, pdstrep, dstreplen)) {
+#else
+					if (!nf_nat_mangle_tcp_packet(skb, ct, ctinfo,
+								      dstoff, dstlen, pdstrep, dstreplen)) {
+#endif
+						/* mangle failed, all we can do is bail */
+						nf_ct_unexpect_related(rtp_exp);
+						if (rtcp_exp)
+							nf_ct_unexpect_related(rtcp_exp);
+						return 0;
+					}
+					get_skb_tcpdata(skb, &ptcp, &tcplen);
+					ptran = ptcp+tranoff;
+					tranlen -= diff;
+					nextparamoff -= diff;
+					nextfieldoff -= diff;
+				}
+			}
+
+			off = nextfieldoff;
+		}
+
+		if (is_stun)
+			continue;
+
+		off = saveoff;
+		while (off < nextparamoff) {
+			const char* pfieldend;
+			uint        nextfieldoff;
+
+			pfieldend = memchr(ptran+off, ';', nextparamoff-off);
+			nextfieldoff = (pfieldend == NULL) ? nextparamoff : pfieldend-ptran+1;
+
+			if (strncmp(ptran+off, "client_port=", 12) == 0) {
+				u_int16_t port;
+				uint	  numlen;
+				uint      origoff;
+				uint      origlen;
+				char*     rbuf = rbuf1;
+				uint      rbuflen = rbuf1len;
+
+				off += 12;
+				origoff = (ptran-ptcp)+off;
+				origlen = 0;
+				numlen = nf_strtou16(ptran+off, &port);
+				off += numlen;
+				origlen += numlen;
+				if (port != prtspexp->loport) {
+					pr_debug("multiple ports found, port %hu ignored\n", port);
+				} else {
+					if (ptran[off] == '-' || ptran[off] == '/') {
+						off++;
+						origlen++;
+						numlen = nf_strtou16(ptran+off, &port);
+						off += numlen;
+						origlen += numlen;
+						rbuf = rbufa;
+						rbuflen = rbufalen;
+					}
+
+					/*
+					 * note we cannot just memcpy() if the sizes are the same.
+					 * the mangle function does skb resizing, checks for a
+					 * cloned skb, and updates the checksums.
+					 *
+					 * parameter 4 below is offset from start of tcp data.
+					 */
+					diff = origlen-rbuflen;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
+					if (!nf_nat_mangle_tcp_packet(skb, ct, ctinfo, protoff,
+								      origoff, origlen, rbuf, rbuflen)) {
+#else
+					if (!nf_nat_mangle_tcp_packet(skb, ct, ctinfo,
+								      origoff, origlen, rbuf, rbuflen)) {
+#endif
+						/* mangle failed, all we can do is bail */
+						nf_ct_unexpect_related(rtp_exp);
+						if (rtcp_exp)
+							nf_ct_unexpect_related(rtcp_exp);
+						return 0;
+					}
+					get_skb_tcpdata(skb, &ptcp, &tcplen);
+					ptran = ptcp+tranoff;
+					tranlen -= diff;
+					nextparamoff -= diff;
+					nextfieldoff -= diff;
+				}
+			}
+
+			off = nextfieldoff;
+		}
+
+		off = nextparamoff;
+	}
+
+	return 1;
+}
+
+static uint
+help_out(struct sk_buff *skb, enum ip_conntrack_info ctinfo,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
+	 unsigned int protoff,
+#endif
+	 unsigned int matchoff, unsigned int matchlen,
+	 struct ip_ct_rtsp_expect* prtspexp,
+	 struct nf_conntrack_expect* rtp_exp,
+	 struct nf_conntrack_expect* rtcp_exp)
+{
+	char* ptcp;
+	uint  tcplen;
+	uint  hdrsoff;
+	uint  hdrslen;
+	uint  lineoff;
+	uint  linelen;
+	uint  off;
+	int   dir = CTINFO2DIR(ctinfo);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
+	union nf_inet_addr saddr = rtp_exp->master->tuplehash[dir].tuple.src.u3;
+#else
+	__be32 saddr = rtp_exp->master->tuplehash[dir].tuple.src.u3.ip;
+#endif
+
+	//struct iphdr* iph = (struct iphdr*)(*pskb)->nh.iph;
+	//struct tcphdr* tcph = (struct tcphdr*)((void*)iph + iph->ihl*4);
+
+	get_skb_tcpdata(skb, &ptcp, &tcplen);
+	hdrsoff = matchoff;//exp->seq - ntohl(tcph->seq);
+	hdrslen = matchlen;
+	off = hdrsoff;
+	pr_debug("NAT rtsp help_out\n");
+
+	while (nf_mime_nextline(ptcp, hdrsoff+hdrslen, &off, &lineoff, &linelen)) {
+		if (linelen == 0)
+			break;
+
+		if (off > hdrsoff+hdrslen) {
+			pr_info("!! overrun !!");
+			break;
+		}
+		pr_debug("hdr: len=%u, %.*s", linelen, (int)linelen, ptcp+lineoff);
+
+		if (nf_strncasecmp(ptcp+lineoff, "Transport:", 10) == 0) {
+			uint oldtcplen = tcplen;
+			pr_debug("hdr: Transport\n");
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
+			if (!rtsp_mangle_tran(ctinfo, protoff, rtp_exp, rtcp_exp,
+					      prtspexp, skb, lineoff, linelen)) {
+#else
+			if (!rtsp_mangle_tran(ctinfo, rtp_exp, rtcp_exp, prtspexp,
+					      skb, lineoff, linelen)) {
+#endif
+				pr_debug("hdr: Transport mangle failed");
+				break;
+			}
+			rtp_exp->expectfn = nf_nat_rtsp_expected;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
+			rtp_exp->saved_addr = saddr;
+#else
+			rtp_exp->saved_ip = saddr;
+#endif
+			rtp_exp->saved_proto.udp.port = htons(prtspexp->loport);
+			rtp_exp->dir = !dir;
+			if (rtcp_exp) {
+				rtcp_exp->expectfn = nf_nat_rtsp_expected;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
+				rtcp_exp->saved_addr = saddr;
+#else
+				rtcp_exp->saved_ip = saddr;
+#endif
+				rtcp_exp->saved_proto.udp.port = htons(prtspexp->hiport);
+				rtcp_exp->dir = !dir;
+			}
+			get_skb_tcpdata(skb, &ptcp, &tcplen);
+			hdrslen -= (oldtcplen-tcplen);
+			off -= (oldtcplen-tcplen);
+			lineoff -= (oldtcplen-tcplen);
+			linelen -= (oldtcplen-tcplen);
+			pr_debug("rep: len=%u, %.*s", linelen, (int)linelen, ptcp+lineoff);
+		}
+	}
+
+	return NF_ACCEPT;
+}
+
+static unsigned int
+nf_nat_rtsp(struct sk_buff *skb, enum ip_conntrack_info ctinfo,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
+	    unsigned int protoff,
+#endif
+	    unsigned int matchoff, unsigned int matchlen,
+	    struct ip_ct_rtsp_expect* prtspexp,
+	    struct nf_conntrack_expect* rtp_exp,
+	    struct nf_conntrack_expect* rtcp_exp)
+{
+	int dir = CTINFO2DIR(ctinfo);
+	int rc = NF_ACCEPT;
+
+	switch (dir) {
+	case IP_CT_DIR_ORIGINAL:
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
+		rc = help_out(skb, ctinfo, protoff, matchoff, matchlen, prtspexp,
+			      rtp_exp, rtcp_exp);
+#else
+		rc = help_out(skb, ctinfo, matchoff, matchlen, prtspexp,
+			      rtp_exp, rtcp_exp);
+#endif
+		break;
+	case IP_CT_DIR_REPLY:
+		pr_debug("unmangle ! %u\n", ctinfo);
+		/* XXX: unmangle */
+		rc = NF_ACCEPT;
+		break;
+	}
+	//UNLOCK_BH(&ip_rtsp_lock);
+
+	return rc;
+}
+
+static void nf_nat_rtsp_expected(struct nf_conn* ct, struct nf_conntrack_expect *exp)
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,3,0) || LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
+	struct nf_nat_range range;
+#else
+	struct nf_nat_ipv4_range range;
+#endif
+
+	/* This must be a fresh one. */
+	BUG_ON(ct->status & IPS_NAT_DONE_MASK);
+
+	/* For DST manip, map port here to where it's expected. */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
+	range.min_proto = range.max_proto = exp->saved_proto;
+	range.min_addr = range.max_addr = exp->saved_addr;
+#else
+	range.min = range.max = exp->saved_proto;
+	range.min_ip = range.max_ip = exp->saved_ip;
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,3,0)
+	range.flags = (NF_NAT_RANGE_MAP_IPS | NF_NAT_RANGE_PROTO_SPECIFIED);
+	nf_nat_setup_info(ct, &range, NF_NAT_MANIP_DST);
+#else
+	range.flags = (IP_NAT_RANGE_MAP_IPS | IP_NAT_RANGE_PROTO_SPECIFIED);
+	nf_nat_setup_info(ct, &range, IP_NAT_MANIP_DST);
+#endif
+
+	/* Change src to where master sends to, but only if the connection
+	 * actually came from the same source. */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
+	if (nf_inet_addr_cmp(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3,
+			     &ct->master->tuplehash[exp->dir].tuple.src.u3)) {
+		range.min_addr = range.max_addr
+			= ct->master->tuplehash[!exp->dir].tuple.dst.u3;
+#else
+	if (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip ==
+	    ct->master->tuplehash[exp->dir].tuple.src.u3.ip) {
+		range.min_ip = range.max_ip
+			= ct->master->tuplehash[!exp->dir].tuple.dst.u3.ip;
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,3,0)
+		range.flags = NF_NAT_RANGE_MAP_IPS;
+		nf_nat_setup_info(ct, &range, NF_NAT_MANIP_SRC);
+#else
+		range.flags = IP_NAT_RANGE_MAP_IPS;
+		nf_nat_setup_info(ct, &range, IP_NAT_MANIP_SRC);
+#endif
+	}
+}
+
+
+static void __exit fini(void)
+{
+	rcu_assign_pointer(nf_nat_rtsp_hook, NULL);
+	synchronize_net();
+}
+
+static int __init init(void)
+{
+	printk("nf_nat_rtsp v" IP_NF_RTSP_VERSION " loading\n");
+
+	BUG_ON(nf_nat_rtsp_hook);
+	rcu_assign_pointer(nf_nat_rtsp_hook, nf_nat_rtsp);
+
+	if (stunaddr != NULL)
+		extip = in_aton(stunaddr);
+
+	if (destaction != NULL) {
+		if (strcmp(destaction, "auto") == 0)
+			dstact = DSTACT_AUTO;
+
+		if (strcmp(destaction, "strip") == 0)
+			dstact = DSTACT_STRIP;
+
+		if (strcmp(destaction, "none") == 0)
+			dstact = DSTACT_NONE;
+	}
+
+	return 0;
+}
+
+module_init(init);
+module_exit(fini);
diff --git a/net/socket.c b/net/socket.c
index e66e4f3..d28c1fe 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -709,6 +709,18 @@
 }
 EXPORT_SYMBOL_GPL(__sock_recv_ts_and_drops);
 
+void __sock_recv_alloc(struct msghdr *msg, struct sock *sk)
+{
+	__u32 rmem_alloc;
+
+	if (sock_flag(sk, SOCK_RXQ_ALLOC)) {
+		rmem_alloc = atomic_read(&sk->sk_rmem_alloc);
+		put_cmsg(msg, SOL_SOCKET, SO_RXQ_ALLOC,
+				sizeof(rmem_alloc), &rmem_alloc);
+	}
+}
+EXPORT_SYMBOL_GPL(__sock_recv_alloc);
+
 static inline int sock_recvmsg_nosec(struct socket *sock, struct msghdr *msg,
 				     size_t size, int flags)
 {