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)
{