Browse Source

Merge branch 'connectivity-ti-linux-4.19.y' of git://git.ti.com/connectivity-integration-tree/connectivity-ti-linux-kernel into ti-linux-4.19.y

TI-Feature: connectivity
TI-Tree: git://git.ti.com/connectivity-integration-tree/connectivity-ti-linux-kernel.git
TI-Branch: connectivity-ti-linux-4.19.y

* 'connectivity-ti-linux-4.19.y' of git://git.ti.com/connectivity-integration-tree/connectivity-ti-linux-kernel:
  ARM: dts: nss 2u: update for using syscon node and phandles
  net: netcp: nss 2u: use syscon phandle to access subsystem
  dt-bindings: keystone-netcp: use syscon to access subsystem control for nss 2u switch
  ARM: dts: keystone: enable all dma channels for NetCP
  HACK: net: netcp: ale: add sysfs interface for control, ale table show
  HACK: net: netcp: add sysfs configuration/control for netcp ethss switch
  HACK: net: netcp: preparatory patch to introduce sysfs support
  ARM: dts: keystone: evm: enable gbe serdes support
  ARM: dts: add gbe serdes phy nodes and phandles for netcp dt nodes
  ti_config_fragments/connectivity.cfg: Enable Keystone SerDes driver
  net: keystone: add support of keystone common serdes phy driver for gbe
  dt-bindings: net: keystone-netcp: update to work with serdes phy
  phy: keystone: serdes driver for 1gbe, 10gbe and pcie
  dt-bindings: phy: add dt bindings for phy-keystone-serdes
  net: netcp: fix error in link status display
  net: netcp: ethss: add support of 10gbe pcsr link status
  dt-bindings: net: keystone-netcp: use syscon for pcs-r module region access
  ARM: dts: add gbe syscon subsys dt node for netcp
  net: netcp: ethss: add support of subsystem register region regmap
  dt-bindings: net: keystone-netcp: use syscon for netcp subsystem control access

Signed-off-by: LCPD Auto Merger <lcpd_integration@list.ti.com>
LCPD Auto Merger 6 years ago
parent
commit
1665c68bc6

+ 116 - 11
Documentation/devicetree/bindings/net/keystone-netcp.txt

@@ -62,32 +62,42 @@ Optional properties:
 		big endian mode with the DSP in little endian.
 		big endian mode with the DSP in little endian.
 
 
 NetCP device properties: Device specification for NetCP sub-modules.
 NetCP device properties: Device specification for NetCP sub-modules.
+
 1Gb/10Gb (gbe/xgbe) ethernet switch sub-module specifications.
 1Gb/10Gb (gbe/xgbe) ethernet switch sub-module specifications.
 Required properties:
 Required properties:
 - label:	Must be "netcp-gbe" for 1Gb & "netcp-xgbe" for 10Gb.
 - label:	Must be "netcp-gbe" for 1Gb & "netcp-xgbe" for 10Gb.
 - compatible:	Must be one of below:-
 - compatible:	Must be one of below:-
 		"ti,netcp-gbe" for 1GbE on NetCP 1.4
 		"ti,netcp-gbe" for 1GbE on NetCP 1.4
-		"ti,netcp-gbe-5" for 1GbE N NetCP 1.5 (N=5)
-		"ti,netcp-gbe-9" for 1GbE N NetCP 1.5 (N=9)
-		"ti,netcp-gbe-2" for 1GbE N NetCP 1.5 (N=2)
+		"ti,netcp-gbe-5" for 1GbE on NetCP 1.5 (N=5)
+		"ti,netcp-gbe-9" for 1GbE on NetCP 1.5 (N=9)
+		"ti,netcp-gbe-2" for 1GbE on NSS (Network Subsystem)
 		"ti,netcp-xgbe" for 10 GbE
 		"ti,netcp-xgbe" for 10 GbE
 
 
+- syscon-subsys:	phandle to syscon node of the switch
+			subsystem registers of NetCP 1.4/1.5
+			or EMAC registers of NSS
+
+- syscon-pcsr:		(10gbe only) phandle to syscon node of the
+			switch PCSR registers.
+
 - reg:		register location and the size for the following register
 - reg:		register location and the size for the following register
 		regions in the specified order.
 		regions in the specified order.
 		- switch subsystem registers
 		- switch subsystem registers
+		- sgmii module registers
 		- sgmii port3/4 module registers (only for NetCP 1.4)
 		- sgmii port3/4 module registers (only for NetCP 1.4)
 		- switch module registers
 		- switch module registers
-		- serdes registers (only for 10G)
 
 
 		NetCP 1.4 ethss, here is the order
 		NetCP 1.4 ethss, here is the order
-			index #0 - switch subsystem registers
+			index #0 - sgmii module registers
 			index #1 - sgmii port3/4 module registers
 			index #1 - sgmii port3/4 module registers
 			index #2 - switch module registers
 			index #2 - switch module registers
 
 
-		NetCP 1.5 ethss 9 port, 5 port and 2 port
-			index #0 - switch subsystem registers
+		NetCP 1.5 ethss 9 port, 5 port
+			index #0 - sgmii module registers
 			index #1 - switch module registers
 			index #1 - switch module registers
-			index #2 - serdes registers
+
+		NSS of K2G SoC
+			index #0 sgmii module registers
 
 
 - tx-channel:	the navigator packet dma channel name for tx.
 - tx-channel:	the navigator packet dma channel name for tx.
 - tx-queue:	the navigator queue number associated with the tx dma channel.
 - tx-queue:	the navigator queue number associated with the tx dma channel.
@@ -104,6 +114,10 @@ Required properties:
 			- 10Gb mac<->mac forced mode : 11
 			- 10Gb mac<->mac forced mode : 11
 ----phy-handle:	phandle to PHY device
 ----phy-handle:	phandle to PHY device
 
 
+----phys:	phandles to serdes PHY devices
+		see Documentation/devicetree/bindings/phy/ti-phy.txt
+		for Keystone SerDes device specificcations.
+
 Optional properties:
 Optional properties:
 - enable-ale:	NetCP driver keeps the address learning feature in the ethernet
 - enable-ale:	NetCP driver keeps the address learning feature in the ethernet
 		switch module disabled. This attribute is to enable the address
 		switch module disabled. This attribute is to enable the address
@@ -160,6 +174,16 @@ Optional properties:
 
 
 Example binding:
 Example binding:
 
 
+gbe_subsys: subsys@2090000 {
+	compatible = "syscon";
+	reg = <0x02090000 0x100>;
+};
+
+gbe_serdes: phy@232a000 {
+	compatible		= "ti,keystone-serdes-gbe";
+	...
+};
+
 netcp: netcp@2000000 {
 netcp: netcp@2000000 {
 	reg = <0x2620110 0x8>;
 	reg = <0x2620110 0x8>;
 	reg-names = "efuse";
 	reg-names = "efuse";
@@ -169,7 +193,6 @@ netcp: netcp@2000000 {
 	ranges  = <0 0x2000000 0xfffff>;
 	ranges  = <0 0x2000000 0xfffff>;
 	clocks = <&papllclk>, <&clkcpgmac>, <&chipclk12>;
 	clocks = <&papllclk>, <&clkcpgmac>, <&chipclk12>;
 	dma-coherent;
 	dma-coherent;
-	/* big-endian; */
 	dma-id = <0>;
 	dma-id = <0>;
 
 
 	netcp-devices {
 	netcp-devices {
@@ -178,17 +201,19 @@ netcp: netcp@2000000 {
 		ranges;
 		ranges;
 		gbe@90000 {
 		gbe@90000 {
 			label = "netcp-gbe";
 			label = "netcp-gbe";
-			reg = <0x90000 0x300>, <0x90400 0x400>, <0x90800 0x700>;
-			/* enable-ale; */
+			syscon-subsys = <&gbe_subsys>;
+			reg = <0x90100 0x200>, <0x90400 0x200>, <0x90800 0x700>;
 			tx-queue = <648>;
 			tx-queue = <648>;
 			tx-channel = <8>;
 			tx-channel = <8>;
 
 
 			interfaces {
 			interfaces {
 				gbe0: interface-0 {
 				gbe0: interface-0 {
+					phys = <&serdes_lane0>;
 					slave-port = <0>;
 					slave-port = <0>;
 					link-interface	= <4>;
 					link-interface	= <4>;
 				};
 				};
 				gbe1: interface-1 {
 				gbe1: interface-1 {
+					phys = <&serdes_lane1>;
 					slave-port = <1>;
 					slave-port = <1>;
 					link-interface	= <4>;
 					link-interface	= <4>;
 				};
 				};
@@ -196,10 +221,12 @@ netcp: netcp@2000000 {
 
 
 			secondary-slave-ports {
 			secondary-slave-ports {
 				port-2 {
 				port-2 {
+					phys = <&serdes_lane2>;
 					slave-port = <2>;
 					slave-port = <2>;
 					link-interface	= <2>;
 					link-interface	= <2>;
 				};
 				};
 				port-3 {
 				port-3 {
+					phys = <&serdes_lane3>;
 					slave-port = <3>;
 					slave-port = <3>;
 					link-interface	= <2>;
 					link-interface	= <2>;
 				};
 				};
@@ -234,3 +261,81 @@ netcp: netcp@2000000 {
 		};
 		};
 	};
 	};
 };
 };
+
+Example bindings (10gbe):
+
+xgbe_serdes: phy@231e000 {
+	compatible		= "ti,keystone-serdes-xgbe";
+	...
+};
+
+netcpx: netcpx@2f00000 {
+	status		= "disabled";
+	compatible	= "ti,netcp-1.0";
+	#address-cells	= <1>;
+	#size-cells	= <1>;
+	ranges;
+
+	clocks		= <&clkxge>;
+	clock-names	= "clk_xge";
+	dma-coherent;
+
+	ti,navigator-dmas = <&dma_xgbe 0>,
+			<&dma_xgbe 8>,
+			<&dma_xgbe 0>;
+	ti,navigator-dma-names = "xnetrx0", "xnetrx1", "xnettx";
+
+	netcp-devices {
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges;
+		xgbe@2f00000 {
+			label = "netcp-xgbe";
+			compatible = "ti,netcp-xgbe";
+			syscon-subsys = <&xgbe_subsys>;
+			syscon-pcsr = <&xgbe_pcsr>;
+			reg = <0x02f00100 0x200>, <0x02f01000 0xb00>;
+			tx-queue = <692>;
+			tx-channel = "xnettx";
+
+			interfaces {
+				xgbe0: interface-0 {
+					phys = <&xserdes_lane0>;
+					slave-port = <0>;
+					link-interface	= <11>;
+				};
+				xgbe1: interface-1 {
+					phys = <&xserdes_lane1>;
+					slave-port = <1>;
+					link-interface	= <11>;
+				};
+			};
+		};
+	};
+
+	netcp-interfaces {
+		interface-0 {
+			rx-channel = "xnetrx0";
+			rx-pool = <2048 12>;
+			tx-pool = <1024 12>;
+			rx-queue-depth = <1024 1024 0 0>;
+			rx-buffer-size = <1536 4096 0 0>;
+			rx-queue = <532>;
+			tx-completion-queue = <534>;
+			efuse-mac = <0>;
+			netcp-xgbe = <&xgbe0>;
+
+		};
+		interface-1 {
+			rx-channel = "xnetrx1";
+			rx-pool = <2048 12>;
+			tx-pool = <1024 12>;
+			rx-queue-depth = <1024 1024 0 0>;
+			rx-buffer-size = <1536 4096 0 0>;
+			rx-queue = <533>;
+			tx-completion-queue = <535>;
+			efuse-mac = <0>;
+			netcp-xgbe = <&xgbe1>;
+		};
+	};
+};

+ 273 - 0
Documentation/devicetree/bindings/phy/ti,k2-serdes-phy.txt

@@ -0,0 +1,273 @@
+TI Keystone SerDes PHY DT Documentation
+---------------------------------------
+Required properties:
+ - compatible: should be one of
+	* "ti,keystone-serdes-gbe"
+	* "ti,keystone-serdes-xgbe"
+	* "ti,keystone-serdes-pcie"
+ - reg:
+	* base address and length of the SerDes register set
+ - num-lanes:
+	* Number of lanes in SerDes.
+
+Optional properties:
+ - syscon-peripheral:
+	* Handle to the subsystem register region of the peripheral
+	  inside which the SerDes exists.  Required for 10gbe.
+ - syscon-link:
+	* Handle to the Link register region of the peripheral inside
+	  which the SerDes exists.  Example: it is the PCSR register
+	  region in the case of 10gbe.  Required for 10gbe.
+ - rx-force-enable:
+	* Include this property if receiver attenuation and boost are
+	  to be configured with specific values defined in rx-force.
+ - link-rate-kbps:
+	* SerDes link rate to be configured, in kbps.
+
+
+For gbe and 10gbe SerDes, it is optional to represent each lane as
+a sub-node, which can be enabled or disabled individually using
+the "status" property.  If a lane is not represented by a node, the
+lane is disabled.
+
+Required properties (lane sub-node):
+ - #phy-cells:
+	* From the generic phy bindings, must be 0;
+ - reg:
+	* lane number
+
+Optional properties (lane sub-node):
+ - control-rate:
+	* Lane control rate
+		0: full rate
+		1: half rate
+		2: quarter rate
+ - rx-start:
+	* Initial lane rx equalizer attenuation and boost configurations.
+	* Must be array of 2 integers.
+ - rx-force:
+	* Forced lane rx equalizer attenuation and boost configurations.
+	* Must be array of 2 integers.
+ - tx-coeff:
+	* Lane c1, c2, cm, attenuation and regulator output voltage
+	  configurations.
+	* Must be array of 5 integers.
+ - loopback:
+	* Include this property to enable loopback at the SerDes
+	  lane level.
+
+Example for Keystone K2E GBE:
+-----------------------------
+
+gbe_serdes0: phy@232a000 {
+	compatible		= "ti,keystone-serdes-gbe";
+	reg			= <0x0232a000 0x2000>;
+	link-rate-kbps		= <1250000>;
+	num-lanes		= <4>;
+	#address-cells = <1>;
+	#size-cells = <0>;
+
+	serdes0_lane0: lane@0 {
+		status		= "ok";
+		#phy-cells	= <0>;
+		reg		= <0>;
+		control-rate	= <2>;
+		rx-start	= <7 5>;
+		rx-force	= <1 1>;
+		tx-coeff	= <0 0 0 12 4>;
+	};
+	serdes0_lane1: lane@1 {
+		status		= "ok";
+		#phy-cells	= <0>;
+		reg		= <1>;
+		control-rate	= <2>;
+		rx-start	= <7 5>;
+		rx-force	= <1 1>;
+		tx-coeff	= <0 0 0 12 4>;
+	};
+	serdes0_lane2: lane@2 {
+		status		= "disabled";
+		#phy-cells	= <0>;
+		reg		= <2>;
+		control-rate	= <2>;
+		rx-start	= <7 5>;
+		rx-force	= <1 1>;
+		tx-coeff	= <0 0 0 12 4>;
+	};
+	serdes0_lane3: lane@3 {
+		status		= "disabled";
+		#phy-cells	= <0>;
+		reg		= <3>;
+		control-rate	= <2>;
+		rx-start	= <7 5>;
+		rx-force	= <1 1>;
+		tx-coeff	= <0 0 0 12 4>;
+	};
+};
+
+gbe_serdes1: phy@2324000 {
+	compatible		= "ti,keystone-serdes-gbe";
+	reg			= <0x02324000 0x2000>;
+	link-rate-kbps		= <1250000>;
+	num-lanes		= <4>;
+	#address-cells = <1>;
+	#size-cells = <0>;
+
+	serdes1_lane0: lane@0 {
+		status		= "disabled";
+		#phy-cells	= <0>;
+		reg		= <0>;
+		control-rate	= <2>;
+		rx-start	= <7 5>;
+		rx-force	= <1 1>;
+		tx-coeff	= <0 0 0 12 4>;
+	};
+	serdes1_lane1: lane@1 {
+		status		= "disabled";
+		#phy-cells	= <0>;
+		reg		= <1>;
+		control-rate	= <2>;
+		rx-start	= <7 5>;
+		rx-force	= <1 1>;
+		tx-coeff	= <0 0 0 12 4>;
+	};
+	serdes1_lane2: lane@2 {
+		status		= "disabled";
+		#phy-cells	= <0>;
+		reg		= <2>;
+		control-rate	= <2>;
+		rx-start	= <7 5>;
+		rx-force	= <1 1>;
+		tx-coeff	= <0 0 0 12 4>;
+	};
+	serdes1_lane3: lane@3 {
+		status		= "disabled";
+		#phy-cells	= <0>;
+		reg		= <3>;
+		control-rate	= <2>;
+		rx-start	= <7 5>;
+		rx-force	= <1 1>;
+		tx-coeff	= <0 0 0 12 4>;
+	};
+};
+
+netcp: netcp@24000000 {
+	...
+
+	netcp-devices {
+		...
+
+		gbe@200000 {
+			...
+
+			gbe0: interface-0 {
+				phys = <&serdes0_lane0>;
+				...
+			};
+			gbe1: interface-1 {
+				phys = <&serdes0_lane1>;
+				...
+			};
+
+			...
+		};
+
+		...
+	};
+};
+
+Example for Keystone PCIE:
+--------------------------
+
+	pcie0_phy: phy@2320000 {
+		compatible = "ti,keystone-serdes-pcie";
+		reg = <0x02320000 0x4000>;
+		link-rate-kbps = <5000000>;
+		num-lanes = <2>;
+	};
+
+No SerDes phy handle needed in PCIe controller node
+since the SerDes PHY provider driver performs all
+the configurations needed for PCIe.
+
+Example for K2E 10GBE:
+----------------------
+
+Define the syscon regmaps for 10gbe subsystem:
+
+xgbe_subsys: subsys@2f00000 {
+	status		= "ok";
+	compatible	= "syscon";
+	reg		= <0x02f00000 0x100>;
+};
+
+Define the syscon regmaps for 10gbe pcsr:
+
+xgbe_pcsr: pcsr@2f00000 {
+	status		= "ok";
+	compatible	= "syscon";
+	reg		= <0x02f00600 0x100>;
+};
+
+Define the 10gbe SerDes node:
+
+xgbe_serdes: phy@231e000 {
+	status			= "ok";
+	compatible		= "ti,keystone-serdes-xgbe";
+	reg			= <0x0231e000 0x2000>;
+	link-rate-kbps		= <10312500>;
+	num-lanes		= <2>;
+	syscon-peripheral	= <&xgbe_subsys>;
+	syscon-link		= <&xgbe_pcsr>;
+	#address-cells  = <1>;
+	#size-cells     = <0>;
+
+	xserdes_lane0: lane@0 {
+		status		= "ok";
+		#phy-cells	= <0>;
+		reg		= <0>;
+		control-rate	= <0>;
+		rx-start	= <7 5>;
+		rx-force	= <1 1>;
+		tx-coeff	= <2 0 0 12 4>;
+	};
+	xserdes_lane1: lane@1 {
+		status		= "ok";
+		#phy-cells	= <0>;
+		reg		= <1>;
+		control-rate	= <0>;
+		rx-start	= <7 5>;
+		rx-force	= <1 1>;
+		tx-coeff	= <2 0 0 12 4>;
+	};
+};
+
+Then the 10gbe SerDes PHY can be used in the 10gbe switch node:
+
+netcpx: netcpx@2f00000 {
+
+	...
+
+	netcp-devices {
+
+		...
+
+		xgbe@2f00000 {
+
+			...
+
+			xgbe0: interface-0 {
+				phys = <&xserdes_lane0>;
+				...
+			};
+			xgbe1: interface-1 {
+				phys = <&xserdes_lane1>;
+				...
+			};
+
+			...
+		};
+	};
+
+	...
+};

+ 4 - 0
arch/arm/boot/dts/keystone-k2e-evm.dts

@@ -182,3 +182,7 @@
 	memory-region = <&dsp_common_memory>;
 	memory-region = <&dsp_common_memory>;
 	status = "okay";
 	status = "okay";
 };
 };
+
+&gbe_serdes0 {
+	status = "okay";
+};

+ 112 - 4
arch/arm/boot/dts/keystone-k2e-netcp.dtsi

@@ -122,6 +122,106 @@ knav_dmas: knav_dmas@0 {
 			  <0x24189000 0x1000>;
 			  <0x24189000 0x1000>;
 		reg-names = "global", "txchan", "rxchan",
 		reg-names = "global", "txchan", "rxchan",
 				"txsched", "rxflow";
 				"txsched", "rxflow";
+		ti,enable-all;
+	};
+};
+
+gbe_subsys: subsys@24200000 {
+	compatible = "syscon";
+	reg = <0x24200000 0x100>;
+};
+
+gbe_serdes0: phy@232a000 {
+	compatible		= "ti,keystone-serdes-gbe";
+	reg			= <0x0232a000 0x2000>;
+	status			= "disabled";
+	link-rate-kbps		= <1250000>;
+	num-lanes		= <4>;
+	#address-cells	= <1>;
+	#size-cells	= <0>;
+
+	serdes0_lane0: lane@0 {
+		#phy-cells	= <0>;
+		reg		= <0>;
+		status		= "ok";
+		control-rate	= <2>;
+		rx-start	= <7 5>;
+		rx-force	= <1 1>;
+		tx-coeff	= <0 0 0 12 4>;
+	};
+	serdes0_lane1: lane@1 {
+		#phy-cells	= <0>;
+		reg		= <1>;
+		status		= "ok";
+		control-rate	= <2>;
+		rx-start	= <7 5>;
+		rx-force	= <1 1>;
+		tx-coeff	= <0 0 0 12 4>;
+	};
+	serdes0_lane2: lane@2 {
+		#phy-cells	= <0>;
+		reg		= <2>;
+		status		= "disabled";
+		control-rate	= <2>;
+		rx-start	= <7 5>;
+		rx-force	= <1 1>;
+		tx-coeff	= <0 0 0 12 4>;
+	};
+	serdes0_lane3: lane@3 {
+		#phy-cells	= <0>;
+		reg		= <3>;
+		status		= "disabled";
+		control-rate	= <2>;
+		rx-start	= <7 5>;
+		rx-force	= <1 1>;
+		tx-coeff	= <0 0 0 12 4>;
+	};
+};
+
+gbe_serdes1: phy@2324000 {
+	compatible		= "ti,keystone-serdes-gbe";
+	reg			= <0x02324000 0x2000>;
+	status			= "disabled";
+	link-rate-kbps		= <1250000>;
+	num-lanes		= <4>;
+	#address-cells	= <1>;
+	#size-cells	= <0>;
+
+	serdes1_lane0: lane@0 {
+		#phy-cells	= <0>;
+		reg		= <0>;
+		status		= "disabled";
+		control-rate	= <2>;
+		rx-start	= <7 5>;
+		rx-force	= <1 1>;
+		tx-coeff	= <0 0 0 12 4>;
+	};
+	serdes1_lane1: lane@1 {
+		#phy-cells	= <0>;
+		reg		= <1>;
+		status		= "disabled";
+		control-rate	= <2>;
+		rx-start	= <7 5>;
+		rx-force	= <1 1>;
+		tx-coeff	= <0 0 0 12 4>;
+	};
+	serdes1_lane2: lane@2 {
+		#phy-cells	= <0>;
+		reg		= <2>;
+		status		= "disabled";
+		control-rate	= <2>;
+		rx-start	= <7 5>;
+		rx-force	= <1 1>;
+		tx-coeff	= <0 0 0 12 4>;
+	};
+	serdes1_lane3: lane@3 {
+		#phy-cells	= <0>;
+		reg		= <3>;
+		status		= "disabled";
+		control-rate	= <2>;
+		rx-start	= <7 5>;
+		rx-force	= <1 1>;
+		tx-coeff	= <0 0 0 12 4>;
 	};
 	};
 };
 };
 
 
@@ -151,19 +251,21 @@ netcp: netcp@24000000 {
 		gbe@200000 { /* ETHSS */
 		gbe@200000 { /* ETHSS */
 			label = "netcp-gbe";
 			label = "netcp-gbe";
 			compatible = "ti,netcp-gbe-9";
 			compatible = "ti,netcp-gbe-9";
-			reg = <0x200000 0x900>, <0x220000 0x20000>;
-			/* enable-ale; */
+			syscon-subsys = <&gbe_subsys>;
+			reg = <0x200100 0x800>, <0x220000 0x20000>;
 			tx-queue = <896>;
 			tx-queue = <896>;
 			tx-channel = "nettx";
 			tx-channel = "nettx";
 
 
 			interfaces {
 			interfaces {
 				gbe0: interface-0 {
 				gbe0: interface-0 {
-					slave-port = <0>;
+					phys		= <&serdes0_lane0>;
+					slave-port	= <0>;
 					link-interface	= <1>;
 					link-interface	= <1>;
 					phy-handle	= <&ethphy0>;
 					phy-handle	= <&ethphy0>;
 				};
 				};
 				gbe1: interface-1 {
 				gbe1: interface-1 {
-					slave-port = <1>;
+					phys		= <&serdes0_lane1>;
+					slave-port	= <1>;
 					link-interface	= <1>;
 					link-interface	= <1>;
 					phy-handle	= <&ethphy1>;
 					phy-handle	= <&ethphy1>;
 				};
 				};
@@ -171,26 +273,32 @@ netcp: netcp@24000000 {
 
 
 			secondary-slave-ports {
 			secondary-slave-ports {
 				port-2 {
 				port-2 {
+					phys = <&serdes0_lane2>;
 					slave-port = <2>;
 					slave-port = <2>;
 					link-interface	= <2>;
 					link-interface	= <2>;
 				};
 				};
 				port-3 {
 				port-3 {
+					phys = <&serdes0_lane3>;
 					slave-port = <3>;
 					slave-port = <3>;
 					link-interface	= <2>;
 					link-interface	= <2>;
 				};
 				};
 				port-4 {
 				port-4 {
+					phys = <&serdes1_lane0>;
 					slave-port = <4>;
 					slave-port = <4>;
 					link-interface	= <2>;
 					link-interface	= <2>;
 				};
 				};
 				port-5 {
 				port-5 {
+					phys = <&serdes1_lane1>;
 					slave-port = <5>;
 					slave-port = <5>;
 					link-interface	= <2>;
 					link-interface	= <2>;
 				};
 				};
 				port-6 {
 				port-6 {
+					phys = <&serdes1_lane2>;
 					slave-port = <6>;
 					slave-port = <6>;
 					link-interface	= <2>;
 					link-interface	= <2>;
 				};
 				};
 				port-7 {
 				port-7 {
+					phys = <&serdes1_lane3>;
 					slave-port = <7>;
 					slave-port = <7>;
 					link-interface	= <2>;
 					link-interface	= <2>;
 				};
 				};

+ 7 - 1
arch/arm/boot/dts/keystone-k2g-netcp.dtsi

@@ -89,6 +89,11 @@ knav_dmas: knav_dmas@0 {
 
 
 };
 };
 
 
+gbe_subsys: subsys@4200000 {
+	compatible = "syscon";
+	reg = <0x4200000 0x20>;
+};
+
 netcp: netcp@4000000 {
 netcp: netcp@4000000 {
 	reg = <0x2620110 0x8>;
 	reg = <0x2620110 0x8>;
 	reg-names = "efuse";
 	reg-names = "efuse";
@@ -115,7 +120,8 @@ netcp: netcp@4000000 {
 		gbe: gbe@200000 {
 		gbe: gbe@200000 {
 			label = "netcp-gbe";
 			label = "netcp-gbe";
 			compatible = "ti,netcp-gbe-2";
 			compatible = "ti,netcp-gbe-2";
-			reg = <0x200000 0x20>, <0x220000 0x20000>;
+			syscon-subsys = <&gbe_subsys>;
+			reg = <0x220000 0x20000>;
 			enable-ale;
 			enable-ale;
 			tx-queue = <5>;
 			tx-queue = <5>;
 			tx-channel = "nettx";
 			tx-channel = "nettx";

+ 4 - 0
arch/arm/boot/dts/keystone-k2hk-evm.dts

@@ -241,3 +241,7 @@
 	memory-region = <&dsp_common_memory>;
 	memory-region = <&dsp_common_memory>;
 	status = "okay";
 	status = "okay";
 };
 };
+
+&gbe_serdes {
+	status = "okay";
+};

+ 59 - 2
arch/arm/boot/dts/keystone-k2hk-netcp.dtsi

@@ -139,6 +139,59 @@ knav_dmas: knav_dmas@0 {
 			  <0x2005000 0x400>;
 			  <0x2005000 0x400>;
 		reg-names = "global", "txchan", "rxchan",
 		reg-names = "global", "txchan", "rxchan",
 				"txsched", "rxflow";
 				"txsched", "rxflow";
+		ti,enable-all;
+	};
+};
+
+gbe_subsys: subsys@2090000 {
+	compatible = "syscon";
+	reg = <0x02090000 0x100>;
+};
+
+gbe_serdes: phy@232a000 {
+	compatible		= "ti,keystone-serdes-gbe";
+	reg			= <0x0232a000 0x2000>;
+	status			= "disabled";
+	link-rate-kbps		= <1250000>;
+	num-lanes		= <4>;
+	#address-cells	= <1>;
+	#size-cells	= <0>;
+
+	serdes_lane0: lane@0 {
+		#phy-cells	= <0>;
+		reg		= <0>;
+		status		= "ok";
+		control-rate	= <2>;
+		rx-start	= <7 5>;
+		rx-force	= <1 1>;
+		tx-coeff	= <0 0 0 12 4>;
+	};
+	serdes_lane1: lane@1 {
+		#phy-cells	= <0>;
+		reg		= <1>;
+		status		= "ok";
+		control-rate	= <2>;
+		rx-start	= <7 5>;
+		rx-force	= <1 1>;
+		tx-coeff	= <0 0 0 12 4>;
+	};
+	serdes_lane2: lane@2 {
+		#phy-cells	= <0>;
+		reg		= <2>;
+		status		= "disabled";
+		control-rate	= <2>;
+		rx-start	= <7 5>;
+		rx-force	= <1 1>;
+		tx-coeff	= <0 0 0 12 4>;
+	};
+	serdes_lane3: lane@3 {
+		#phy-cells	= <0>;
+		reg		= <3>;
+		status		= "disabled";
+		control-rate	= <2>;
+		rx-start	= <7 5>;
+		rx-force	= <1 1>;
+		tx-coeff	= <0 0 0 12 4>;
 	};
 	};
 };
 };
 
 
@@ -170,18 +223,20 @@ netcp: netcp@2000000 {
 			#size-cells = <1>;
 			#size-cells = <1>;
 			label = "netcp-gbe";
 			label = "netcp-gbe";
 			compatible = "ti,netcp-gbe";
 			compatible = "ti,netcp-gbe";
-			reg = <0x90000 0x300>, <0x90400 0x400>, <0x90800 0x700>;
-			/* enable-ale; */
+			syscon-subsys = <&gbe_subsys>;
+			reg = <0x90100 0x200>, <0x90400 0x200>, <0x90800 0x700>;
 			tx-queue = <648>;
 			tx-queue = <648>;
 			tx-channel = "nettx";
 			tx-channel = "nettx";
 
 
 			interfaces {
 			interfaces {
 				gbe0: interface-0 {
 				gbe0: interface-0 {
+					phys = <&serdes_lane0>;
 					slave-port = <0>;
 					slave-port = <0>;
 					link-interface = <1>;
 					link-interface = <1>;
 					phy-handle = <&ethphy0>;
 					phy-handle = <&ethphy0>;
 				};
 				};
 				gbe1: interface-1 {
 				gbe1: interface-1 {
+					phys = <&serdes_lane1>;
 					slave-port = <1>;
 					slave-port = <1>;
 					link-interface = <1>;
 					link-interface = <1>;
 					phy-handle = <&ethphy1>;
 					phy-handle = <&ethphy1>;
@@ -190,10 +245,12 @@ netcp: netcp@2000000 {
 
 
 			secondary-slave-ports {
 			secondary-slave-ports {
 				port-2 {
 				port-2 {
+					phys = <&serdes_lane2>;
 					slave-port = <2>;
 					slave-port = <2>;
 					link-interface	= <2>;
 					link-interface	= <2>;
 				};
 				};
 				port-3 {
 				port-3 {
+					phys = <&serdes_lane3>;
 					slave-port = <3>;
 					slave-port = <3>;
 					link-interface	= <2>;
 					link-interface	= <2>;
 				};
 				};

+ 4 - 0
arch/arm/boot/dts/keystone-k2l-evm.dts

@@ -170,3 +170,7 @@
 	memory-region = <&dsp_common_memory>;
 	memory-region = <&dsp_common_memory>;
 	status = "okay";
 	status = "okay";
 };
 };
+
+&gbe_serdes0 {
+	status = "okay";
+};

+ 72 - 4
arch/arm/boot/dts/keystone-k2l-netcp.dtsi

@@ -121,6 +121,70 @@ knav_dmas: knav_dmas@0 {
 			  <0x26189000 0x1000>;
 			  <0x26189000 0x1000>;
 		reg-names = "global", "txchan", "rxchan",
 		reg-names = "global", "txchan", "rxchan",
 				"txsched", "rxflow";
 				"txsched", "rxflow";
+		ti,enable-all;
+	};
+};
+
+gbe_subsys: subsys@26200000 {
+	compatible = "syscon";
+	reg = <0x26200000 0x100>;
+};
+
+gbe_serdes0: phy@232a000 {
+	compatible		= "ti,keystone-serdes-gbe";
+	reg			= <0x0232a000 0x2000>;
+	status			= "disabled";
+	link-rate-kbps		= <1250000>;
+	num-lanes		= <2>;
+	#address-cells	= <1>;
+	#size-cells	= <0>;
+
+	serdes0_lane0: lane@0 {
+		#phy-cells	= <0>;
+		reg		= <0>;
+		status		= "ok";
+		control-rate	= <2>;
+		rx-start	= <7 5>;
+		rx-force	= <1 1>;
+		tx-coeff	= <0 0 0 12 4>;
+	};
+	serdes0_lane1: lane@1 {
+		#phy-cells	= <0>;
+		reg		= <1>;
+		status		= "ok";
+		control-rate	= <2>;
+		rx-start	= <7 5>;
+		rx-force	= <1 1>;
+		tx-coeff	= <0 0 0 12 4>;
+	};
+};
+
+gbe_serdes1: phy@2320000 {
+	compatible		= "ti,keystone-serdes-gbe";
+	reg			= <0x02320000 0x2000>;
+	status			= "disabled";
+	link-rate-kbps		= <1250000>;
+	num-lanes		= <2>;
+	#address-cells	= <1>;
+	#size-cells	= <0>;
+
+	serdes1_lane0: lane@0 {
+		#phy-cells	= <0>;
+		reg		= <0>;
+		status		= "disabled";
+		control-rate	= <2>;
+		rx-start	= <7 5>;
+		rx-force	= <1 1>;
+		tx-coeff	= <0 0 0 12 4>;
+	};
+	serdes1_lane1: lane@1 {
+		#phy-cells	= <0>;
+		reg		= <1>;
+		status		= "disabled";
+		control-rate	= <2>;
+		rx-start	= <7 5>;
+		rx-force	= <1 1>;
+		tx-coeff	= <0 0 0 12 4>;
 	};
 	};
 };
 };
 
 
@@ -150,19 +214,21 @@ netcp: netcp@26000000 {
 		gbe@200000 { /* ETHSS */
 		gbe@200000 { /* ETHSS */
 			label = "netcp-gbe";
 			label = "netcp-gbe";
 			compatible = "ti,netcp-gbe-5";
 			compatible = "ti,netcp-gbe-5";
-			reg = <0x200000 0x900>, <0x220000 0x20000>;
-			/* enable-ale; */
+			syscon-subsys = <&gbe_subsys>;
+			reg = <0x200100 0x400>, <0x220000 0x20000>;
 			tx-queue = <896>;
 			tx-queue = <896>;
 			tx-channel = "nettx";
 			tx-channel = "nettx";
 
 
 			interfaces {
 			interfaces {
 				gbe0: interface-0 {
 				gbe0: interface-0 {
-					slave-port = <0>;
+					phys		= <&serdes0_lane0>;
+					slave-port	= <0>;
 					link-interface	= <1>;
 					link-interface	= <1>;
 					phy-handle	= <&ethphy0>;
 					phy-handle	= <&ethphy0>;
 				};
 				};
 				gbe1: interface-1 {
 				gbe1: interface-1 {
-					slave-port = <1>;
+					phys		= <&serdes0_lane1>;
+					slave-port	= <1>;
 					link-interface	= <1>;
 					link-interface	= <1>;
 					phy-handle	= <&ethphy1>;
 					phy-handle	= <&ethphy1>;
 				};
 				};
@@ -170,10 +236,12 @@ netcp: netcp@26000000 {
 
 
 			secondary-slave-ports {
 			secondary-slave-ports {
 				port-2 {
 				port-2 {
+					phys = <&serdes1_lane0>;
 					slave-port = <2>;
 					slave-port = <2>;
 					link-interface	= <2>;
 					link-interface	= <2>;
 				};
 				};
 				port-3 {
 				port-3 {
+					phys = <&serdes1_lane1>;
 					slave-port = <3>;
 					slave-port = <3>;
 					link-interface	= <2>;
 					link-interface	= <2>;
 				};
 				};

+ 1 - 1
drivers/net/ethernet/ti/Makefile

@@ -20,7 +20,7 @@ ti_cpsw-y := cpsw.o
 obj-$(CONFIG_TI_KEYSTONE_NETCP) += keystone_netcp.o
 obj-$(CONFIG_TI_KEYSTONE_NETCP) += keystone_netcp.o
 keystone_netcp-y := netcp_core.o
 keystone_netcp-y := netcp_core.o
 obj-$(CONFIG_TI_KEYSTONE_NETCP_ETHSS) += keystone_netcp_ethss.o
 obj-$(CONFIG_TI_KEYSTONE_NETCP_ETHSS) += keystone_netcp_ethss.o
-keystone_netcp_ethss-y := netcp_ethss.o netcp_sgmii.o netcp_xgbepcsr.o
+keystone_netcp_ethss-y := netcp_ethss.o netcp_ethss_sysfs.o netcp_sgmii.o
 
 
 obj-$(CONFIG_TI_AM65_CPSW_NUSS) += ti-am65-cpsw-nuss.o
 obj-$(CONFIG_TI_AM65_CPSW_NUSS) += ti-am65-cpsw-nuss.o
 ti-am65-cpsw-nuss-y := am65-cpsw-nuss.o cpsw_sl.o am65-cpsw-ethtool.o
 ti-am65-cpsw-nuss-y := am65-cpsw-nuss.o cpsw_sl.o am65-cpsw-ethtool.o

+ 847 - 0
drivers/net/ethernet/ti/cpsw_ale.c

@@ -26,11 +26,14 @@
 #include "cpsw_ale.h"
 #include "cpsw_ale.h"
 
 
 #define BITMASK(bits)		(BIT(bits) - 1)
 #define BITMASK(bits)		(BIT(bits) - 1)
+#define ALE_ENTRY_BITS		68
+#define ALE_ENTRY_WORDS	DIV_ROUND_UP(ALE_ENTRY_BITS, 32)
 
 
 #define ALE_VERSION_MAJOR(rev, mask) (((rev) >> 8) & (mask))
 #define ALE_VERSION_MAJOR(rev, mask) (((rev) >> 8) & (mask))
 #define ALE_VERSION_MINOR(rev)	(rev & 0xff)
 #define ALE_VERSION_MINOR(rev)	(rev & 0xff)
 #define ALE_VERSION_1R3		0x0103
 #define ALE_VERSION_1R3		0x0103
 #define ALE_VERSION_1R4		0x0104
 #define ALE_VERSION_1R4		0x0104
+#define ALE_VERSION_9R3		0x0903
 
 
 /* ALE Registers */
 /* ALE Registers */
 #define ALE_IDVER		0x00
 #define ALE_IDVER		0x00
@@ -64,6 +67,39 @@
 #define ALE_TABLE_SIZE_MULTIPLIER	1024
 #define ALE_TABLE_SIZE_MULTIPLIER	1024
 #define ALE_STATUS_SIZE_MASK		0x1f
 #define ALE_STATUS_SIZE_MASK		0x1f
 #define ALE_TABLE_SIZE_DEFAULT		64
 #define ALE_TABLE_SIZE_DEFAULT		64
+#define ALE_TBL_ENTRY_SHOW_LEN		160
+#define ALE_RAW_TBL_ENTRY_SHOW_LEN	32
+
+/* ALE Table store VLAN command param indices */
+enum {
+	ALE_VP_VID,
+	ALE_VP_FORCE_UT_EGR,
+	ALE_VP_REG_FLD,
+	ALE_VP_UNREG_FLD,
+	ALE_VP_M_LIST,
+	ALE_VP_NUM,
+};
+
+/* ALE Table store UCAST command param indices */
+enum {
+	ALE_UP_PORT,
+	ALE_UP_BLOCK,
+	ALE_UP_SECURE,
+	ALE_UP_AGEABLE,
+	ALE_UP_ADDR,
+	ALE_UP_VID,
+	ALE_UP_NUM,
+};
+
+/* ALE Table store MCAST command param indices */
+enum {
+	ALE_MP_PORT_MASK,
+	ALE_MP_SUPER,
+	ALE_MP_FW_ST,
+	ALE_MP_ADDR,
+	ALE_MP_VID,
+	ALE_MP_NUM
+};
 
 
 static inline int cpsw_ale_get_field(u32 *ale_entry, u32 start, u32 bits)
 static inline int cpsw_ale_get_field(u32 *ale_entry, u32 start, u32 bits)
 {
 {
@@ -424,6 +460,22 @@ static void cpsw_ale_set_vlan_mcast(struct cpsw_ale *ale, u32 *ale_entry,
 	writel(unreg_mcast, ale->params.ale_regs + ALE_VLAN_MASK_MUX(idx));
 	writel(unreg_mcast, ale->params.ale_regs + ALE_VLAN_MASK_MUX(idx));
 }
 }
 
 
+static void cpsw_ale_get_vlan_mcast(struct cpsw_ale *ale, u32 *ale_entry,
+				    int *reg_mcast, int *unreg_mcast)
+{
+	int idx;
+
+	/* Get VLAN registered multicast flood mask */
+	idx = cpsw_ale_get_vlan_reg_mcast_idx(ale_entry);
+	*reg_mcast = __raw_readl(ale->params.ale_regs +
+				 ALE_VLAN_MASK_MUX(idx));
+
+	/* Get VLAN unregistered multicast flood mask */
+	idx = cpsw_ale_get_vlan_unreg_mcast_idx(ale_entry);
+	*unreg_mcast = __raw_readl(ale->params.ale_regs +
+				   ALE_VLAN_MASK_MUX(idx));
+}
+
 int cpsw_ale_add_vlan(struct cpsw_ale *ale, u16 vid, int port, int untag,
 int cpsw_ale_add_vlan(struct cpsw_ale *ale, u16 vid, int port, int untag,
 		      int reg_mcast, int unreg_mcast)
 		      int reg_mcast, int unreg_mcast)
 {
 {
@@ -523,6 +575,14 @@ struct ale_control_info {
 };
 };
 
 
 static struct ale_control_info ale_controls[ALE_NUM_CONTROLS] = {
 static struct ale_control_info ale_controls[ALE_NUM_CONTROLS] = {
+	[ALE_VERSION]		= {
+		.name		= "version",
+		.offset		= ALE_IDVER,
+		.port_offset	= 0,
+		.shift		= 0,
+		.port_shift	= 0,
+		.bits		= 32,
+	},
 	[ALE_ENABLE]		= {
 	[ALE_ENABLE]		= {
 		.name		= "enable",
 		.name		= "enable",
 		.offset		= ALE_CONTROL,
 		.offset		= ALE_CONTROL,
@@ -691,6 +751,7 @@ static struct ale_control_info ale_controls[ALE_NUM_CONTROLS] = {
 		.port_shift	= 0,
 		.port_shift	= 0,
 		.bits		= 8,
 		.bits		= 8,
 	},
 	},
+	/* Fields below has individual registers on NetCP NU switch */
 	[ALE_PORT_UNKNOWN_VLAN_MEMBER] = {
 	[ALE_PORT_UNKNOWN_VLAN_MEMBER] = {
 		.name		= "unknown_vlan_member",
 		.name		= "unknown_vlan_member",
 		.offset		= ALE_UNKNOWNVLAN,
 		.offset		= ALE_UNKNOWNVLAN,
@@ -830,6 +891,757 @@ int cpsw_ale_set_ratelimit(struct cpsw_ale *ale, unsigned long freq, int port,
 }
 }
 EXPORT_SYMBOL_GPL(cpsw_ale_set_ratelimit);
 EXPORT_SYMBOL_GPL(cpsw_ale_set_ratelimit);
 
 
+static int cpsw_ale_dump_mcast(struct cpsw_ale *ale, u32 *ale_entry, char *buf,
+			       int len)
+{
+	static const char * const str_mcast_state[] = {"f", "blf", "lf", "f"};
+	int mcast_state = cpsw_ale_get_mcast_state(ale_entry);
+	int port_mask   = cpsw_ale_get_port_mask(ale_entry,
+						 ale->port_mask_bits);
+	int super       = cpsw_ale_get_super(ale_entry);
+	int outlen = 0;
+
+	outlen += snprintf(buf + outlen, len - outlen,
+			   "mcstate: %s(%d), ", str_mcast_state[mcast_state],
+			   mcast_state);
+	outlen += snprintf(buf + outlen, len - outlen,
+			   "port mask: %x, %ssuper\n", port_mask,
+			   super ? "" : "no ");
+	return outlen;
+}
+
+static int cpsw_ale_dump_ucast(struct cpsw_ale *ale,
+			       u32 *ale_entry, char *buf, int len)
+{
+	int outlen = 0;
+	static const char * const str_ucast_type[] = {"persistent", "untouched",
+							"oui", "touched"};
+	int ucast_type  = cpsw_ale_get_ucast_type(ale_entry);
+	int port_num    = cpsw_ale_get_port_num(ale_entry,
+						ale->port_num_bits);
+	int secure      = cpsw_ale_get_secure(ale_entry);
+	int blocked     = cpsw_ale_get_blocked(ale_entry);
+
+	outlen += snprintf(buf + outlen, len - outlen,
+			   "uctype: %s(%d)", str_ucast_type[ucast_type],
+			   ucast_type);
+	if (ucast_type == ALE_UCAST_OUI)
+		outlen += snprintf(buf + outlen, len - outlen, "\n");
+	else
+		outlen += snprintf(buf + outlen, len - outlen,
+				", port: %d%s%s\n", port_num,
+				secure ? ", Secure" : "",
+				blocked ? ", Blocked" : "");
+	return outlen;
+}
+
+static int cpsw_ale_dump_vlan(struct cpsw_ale *ale, u32 *ale_entry,
+			      char *buf, int len)
+{
+	int outlen = 0, reg_mc_fld, unreg_mc_fld;
+	int force_utag_egress	=
+		cpsw_ale_get_vlan_untag_force(ale_entry,
+					      ale->vlan_field_bits);
+	int mem_list	=
+		cpsw_ale_get_vlan_member_list(ale_entry, ale->vlan_field_bits);
+
+	if (!ale->params.nu_switch_ale) {
+		reg_mc_fld =
+			cpsw_ale_get_vlan_reg_mcast(ale_entry,
+						    ale->vlan_field_bits);
+		unreg_mc_fld =
+			cpsw_ale_get_vlan_unreg_mcast(ale_entry,
+						      ale->vlan_field_bits);
+	} else {
+		cpsw_ale_get_vlan_mcast(ale, ale_entry, &reg_mc_fld,
+					&unreg_mc_fld);
+	}
+
+	outlen += snprintf(buf + outlen, len - outlen,
+			   "force_untag_egress: %02x, ", force_utag_egress);
+	outlen += snprintf(buf + outlen, len - outlen,
+			   "reg_fld: %02x, ", reg_mc_fld);
+	outlen += snprintf(buf + outlen, len - outlen,
+			   "unreg_fld: %02x, ", unreg_mc_fld);
+	outlen += snprintf(buf + outlen, len - outlen,
+			   "mem_list: %02x\n", mem_list);
+	return outlen;
+}
+
+static int cpsw_ale_dump_entry(struct cpsw_ale *ale, int idx, u32 *ale_entry,
+			       char *buf, int len)
+{
+	static const char * const str_type[] = {"free", "addr",
+						"vlan", "vlan+addr"};
+	int type, outlen = 0;
+	u8 addr[6];
+
+	type = cpsw_ale_get_entry_type(ale_entry);
+	if (type == ALE_TYPE_FREE)
+		return outlen;
+
+	if (len < ALE_TBL_ENTRY_SHOW_LEN)
+		return outlen;
+
+	if (idx >= 0) {
+		outlen += snprintf(buf + outlen, len - outlen,
+				   "index %d, ", idx);
+	}
+
+	outlen += snprintf(buf + outlen, len - outlen, "raw: %08x %08x %08x, ",
+			   ale_entry[0], ale_entry[1], ale_entry[2]);
+
+	outlen += snprintf(buf + outlen, len - outlen,
+			   "type: %s(%d), ", str_type[type], type);
+
+	if (type != ALE_TYPE_VLAN) {
+		cpsw_ale_get_addr(ale_entry, addr);
+		outlen += snprintf(buf + outlen, len - outlen,
+			   "addr: %02x:%02x:%02x:%02x:%02x:%02x, ",
+			   (addr)[0], (addr)[1], (addr)[2],
+			   (addr)[3], (addr)[4], (addr)[5]);
+	}
+
+	if (type == ALE_TYPE_VLAN || type == ALE_TYPE_VLAN_ADDR) {
+		outlen += snprintf(buf + outlen, len - outlen, "vlan: %d, ",
+				   cpsw_ale_get_vlan_id(ale_entry));
+	}
+
+	if (type == ALE_TYPE_VLAN)
+		outlen += cpsw_ale_dump_vlan(ale, ale_entry,
+				buf + outlen, len - outlen);
+	else
+		outlen += cpsw_ale_get_mcast(ale_entry) ?
+		  cpsw_ale_dump_mcast(ale, ale_entry, buf + outlen,
+				      len - outlen) :
+		  cpsw_ale_dump_ucast(ale, ale_entry, buf + outlen,
+				      len - outlen);
+
+	return outlen;
+}
+
+static ssize_t ale_control_show(struct device *dev,
+				struct device_attribute *attr,
+				char *buf)
+{
+	int i, port, len = 0, max_control = ARRAY_SIZE(ale_controls);
+	struct cpsw_ale *ale = control_attr_to_ale(attr);
+	struct cpsw_ale_params *params = &ale->params;
+	const struct ale_control_info *info;
+	const char *fmt = "%s=%d\n";
+	u32 reg;
+
+	for (i = 0, info = ale_controls; i < max_control; i++, info++) {
+		if (i == ALE_VERSION) {
+			reg = cpsw_ale_control_get(ale, 0, i);
+			len +=
+			snprintf(buf + len, SZ_4K - len,
+				 "%s=(ALE_ID=0x%04x) Rev %d.%d\n",
+				 info->name,
+				 (reg & 0xffff0000) >> 16,
+				 ALE_VERSION_MAJOR(reg,
+						   params->major_ver_mask),
+						   ALE_VERSION_MINOR(reg));
+			continue;
+		}
+
+		/* global controls */
+		if (info->port_shift == 0 &&  info->port_offset == 0) {
+			if (i >= ALE_PORT_UNKNOWN_VLAN_MEMBER &&
+			    i <= ALE_PORT_UNTAGGED_EGRESS)
+				fmt = "%s=0x%x\n";
+
+			len += snprintf(buf + len, SZ_4K - len,
+					fmt, info->name,
+					cpsw_ale_control_get(ale, 0, i));
+			continue;
+		}
+
+		/* port specific controls */
+		for (port = 0; port < ale->params.ale_ports; port++) {
+			len += snprintf(buf + len, SZ_4K - len,
+					"%s.%d=%d\n", info->name, port,
+					cpsw_ale_control_get(ale, port, i));
+		}
+	}
+
+	return len;
+}
+
+static ssize_t ale_control_store(struct device *dev,
+				 struct device_attribute *attr,
+				 const char *buf, size_t count)
+{
+	int port = 0, value, len, ret, control, max_control = ALE_NUM_CONTROLS;
+	struct cpsw_ale *ale = control_attr_to_ale(attr);
+	char ctrl_str[33], tmp_str[9];
+	unsigned long end;
+
+	len = strcspn(buf, ".=");
+	if (len >= 32)
+		return -ENOMEM;
+
+	strncpy(ctrl_str, buf, len);
+	ctrl_str[len] = '\0';
+	buf += len;
+
+	if (*buf == '.') {
+		++buf;
+		len = strcspn(buf, "=");
+		if (len >= 8)
+			return -ENOMEM;
+		strncpy(tmp_str, buf, len);
+		tmp_str[len] = '\0';
+		if (kstrtoul(tmp_str, 0, &end))
+			return -EINVAL;
+		port = (int)end;
+		buf += len;
+	}
+
+	if (*buf != '=')
+		return -EINVAL;
+
+	if (kstrtoul(buf + 1, 0, &end))
+		return -EINVAL;
+
+	value = (int)end;
+
+	for (control = 0; control < max_control; control++)
+		if (strcmp(ctrl_str, ale_controls[control].name) == 0)
+			break;
+
+	if (control >= max_control)
+		return -ENOENT;
+
+	dev_dbg(ale->params.dev, "processing command %s.%d=%d\n",
+		ale_controls[control].name, port, value);
+
+	ret = cpsw_ale_control_set(ale, port, control, value);
+	if (ret < 0)
+		return ret;
+
+	return count;
+}
+DEVICE_ATTR_RW(ale_control);
+
+static ssize_t ale_table_show(struct device *dev,
+			      struct device_attribute *attr,
+			      char *buf)
+{
+	int not_shown = 0, total_outlen = 0, type, shown = 0;
+	struct cpsw_ale *ale = table_attr_to_ale(attr);
+	int len = SZ_4K, outlen = 0, idx, start;
+	u32 ale_entry[ALE_ENTRY_WORDS];
+
+	start = ale->show_next;
+
+	for (idx = start; (idx < ale->params.ale_entries) &&
+	     (len > total_outlen); idx++) {
+		cpsw_ale_read(ale, idx, ale_entry);
+		outlen = cpsw_ale_dump_entry(ale, idx, ale_entry,
+					     buf + total_outlen,
+					     len - total_outlen);
+		if (outlen == 0) {
+			type = cpsw_ale_get_entry_type(ale_entry);
+			if (type != ALE_TYPE_FREE) {
+				++not_shown;
+				break;
+			}
+		} else {
+			total_outlen += outlen;
+			++shown;
+		}
+	}
+
+	/* update next show index */
+	if (idx >= ale->params.ale_entries)
+		ale->show_next = 0;
+	else
+		ale->show_next = idx;
+
+	if (len > total_outlen + 32)
+		total_outlen += snprintf(buf + total_outlen, len - total_outlen,
+				"[%d..%d]: %d entries%s\n", start, idx - 1,
+				shown, not_shown ? ", +" : "");
+
+	return total_outlen;
+}
+
+struct ale_table_param {
+	const char *name;
+	union	{
+		int	val;
+		u8	addr[6];
+	};
+};
+
+struct ale_table_cmd {
+	const char *name;
+	int (*process)(struct cpsw_ale *ale,
+		       const u8 *params_str, size_t str_len);
+};
+
+static struct ale_table_param vlan_params[] = {
+	[ALE_VP_VID]		= { .name = "vid", },
+	[ALE_VP_FORCE_UT_EGR]	= { .name = "force_untag_egress", },
+	[ALE_VP_REG_FLD]	= { .name = "reg_fld_mask", },
+	[ALE_VP_UNREG_FLD]	= { .name = "unreg_fld_mask", },
+	[ALE_VP_M_LIST]		= { .name = "mem_list", },
+};
+
+static struct ale_table_param vlan_ucast_params[] = {
+	[ALE_UP_PORT]		= { .name = "port", },
+	[ALE_UP_BLOCK]		= { .name = "block", },
+	[ALE_UP_SECURE]		= { .name = "secure", },
+	[ALE_UP_AGEABLE]	= { .name = "ageable", },
+	[ALE_UP_ADDR]		= { .name = "addr", },
+	[ALE_UP_VID]		= { .name = "vid", },
+};
+
+static struct ale_table_param vlan_mcast_params[] = {
+	[ALE_MP_PORT_MASK]	= { .name = "port_mask", },
+	[ALE_MP_SUPER]		= { .name = "supervisory", },
+	[ALE_MP_FW_ST]		= { .name = "mc_fw_st", },
+	[ALE_MP_ADDR]		= { .name = "addr", },
+	[ALE_MP_VID]		= { .name = "vid", },
+};
+
+static struct ale_table_param oui_params[] = {
+	{ .name	= "addr", },
+};
+
+static void cpsw_ale_table_store_init_params(struct ale_table_param *params,
+					     int param_num)
+{
+	int i;
+
+	for (i = 0; i < param_num; i++)
+		memset(params[i].addr, 0, 6);
+}
+
+static int cpsw_ale_table_store_get_params(struct cpsw_ale *ale,
+					   struct ale_table_param *params,
+					   int param_num, const u8 *params_str,
+					   size_t str_len)
+{
+	char param_name[33], val_str[33];
+	size_t tmp_len = str_len;
+	int len, i, n, addr_len;
+	unsigned int iaddr[6];
+	unsigned long end;
+
+	while (tmp_len > 0) {
+		len = strcspn(params_str, "=");
+		if (len >= 32)
+			return -ENOMEM;
+
+		strncpy(param_name, params_str, len);
+		param_name[len] = '\0';
+		params_str += len;
+		tmp_len -= len;
+
+		if (*params_str != '=')
+			return -EINVAL;
+
+		++params_str;
+		--tmp_len;
+
+		len = strcspn(params_str, ".");
+		if (len >= 32)
+			return -ENOMEM;
+
+		strncpy(val_str, params_str, len);
+		val_str[len] = '\0';
+		params_str += len;
+		tmp_len -= len;
+
+		if (*params_str == '.') {
+			++params_str;
+			--tmp_len;
+		}
+
+		for (n = 0; n < param_num; n++) {
+			if (strcmp(param_name, params[n].name) != 0)
+				continue;
+
+			if (strcmp(param_name, "addr") == 0) {
+				addr_len =
+					sscanf(val_str,
+					       "%02x:%02x:%02x:%02x:%02x:%02x",
+					       &iaddr[0], &iaddr[1], &iaddr[2],
+					       &iaddr[3], &iaddr[4], &iaddr[5]);
+				if (addr_len != 6 && addr_len != 3)
+					return -EINVAL;
+
+				for (i = 0; i < addr_len; i++)
+					params[n].addr[i] = iaddr[i];
+
+				break;
+			}
+
+			if (kstrtoul(val_str, 0, &end))
+				return -EINVAL;
+
+			params[n].val = (int)end;
+			break;
+		}
+
+		if (n >= param_num)
+			return -EINVAL;
+	}
+
+	return str_len;
+}
+
+static int cpsw_ale_table_store_vlan(struct cpsw_ale *ale, const u8 *params_str,
+				     size_t str_len)
+{
+	int ret;
+
+	cpsw_ale_table_store_init_params(vlan_params, ALE_VP_NUM);
+	vlan_params[ALE_VP_VID].val = -1;
+
+	ret = cpsw_ale_table_store_get_params(ale, vlan_params, ALE_VP_NUM,
+					      params_str, str_len);
+	if (ret < 0)
+		return ret;
+
+	ret = cpsw_ale_add_vlan(ale, vlan_params[ALE_VP_VID].val,
+				vlan_params[ALE_VP_M_LIST].val,
+				vlan_params[ALE_VP_FORCE_UT_EGR].val,
+				vlan_params[ALE_VP_REG_FLD].val,
+				vlan_params[ALE_VP_UNREG_FLD].val);
+	if (ret < 0)
+		return ret;
+	return str_len;
+}
+
+static int
+cpsw_ale_table_store_vlan_ucast(struct cpsw_ale *ale, const u8 *params_str,
+				size_t str_len, int has_vid)
+{
+	int ret, flags = 0;
+
+	cpsw_ale_table_store_init_params(vlan_ucast_params, ALE_UP_NUM);
+	vlan_ucast_params[ALE_UP_VID].val = -1;
+
+	ret = cpsw_ale_table_store_get_params(ale, vlan_ucast_params,
+					      ALE_UP_NUM, params_str, str_len);
+
+	if (ret < 0)
+		return ret;
+
+	if (!has_vid && vlan_ucast_params[ALE_UP_VID].val >= 0)
+		return -EINVAL;
+
+	if (vlan_ucast_params[ALE_UP_BLOCK].val)
+		flags |= ALE_BLOCKED;
+
+	if (vlan_ucast_params[ALE_UP_SECURE].val)
+		flags |= ALE_SECURE;
+
+	ret = cpsw_ale_add_ucast(ale, vlan_ucast_params[ALE_UP_ADDR].addr,
+				 vlan_ucast_params[ALE_UP_PORT].val, flags,
+				 vlan_ucast_params[ALE_UP_VID].val);
+	if (ret < 0)
+		return ret;
+
+	return str_len;
+}
+
+static int cpsw_ale_table_store_u_proc(struct cpsw_ale *ale,
+				       const u8 *params_str, size_t str_len)
+{
+	return  cpsw_ale_table_store_vlan_ucast(ale, params_str, str_len, 0);
+}
+
+static int cpsw_ale_table_store_vu_proc(struct cpsw_ale *ale,
+					const u8 *params_str, size_t str_len)
+{
+	return  cpsw_ale_table_store_vlan_ucast(ale, params_str, str_len, 1);
+}
+
+static int cpsw_ale_table_store_vlan_mcast(struct cpsw_ale *ale,
+					   const u8 *params_str,
+					   size_t str_len, int has_vid)
+{
+	int ret;
+
+	cpsw_ale_table_store_init_params(vlan_mcast_params, ALE_MP_NUM);
+	vlan_mcast_params[ALE_MP_VID].val = -1;
+
+	ret = cpsw_ale_table_store_get_params(ale, vlan_mcast_params,
+					      ALE_MP_NUM, params_str, str_len);
+	if (ret < 0)
+		return ret;
+
+	if (!has_vid && vlan_mcast_params[ALE_MP_VID].val >= 0)
+		return -EINVAL;
+
+	ret = cpsw_ale_add_mcast(ale, vlan_mcast_params[ALE_MP_ADDR].addr,
+				 vlan_mcast_params[ALE_MP_PORT_MASK].val,
+				 vlan_mcast_params[ALE_MP_SUPER].val,
+				 vlan_mcast_params[ALE_MP_FW_ST].val,
+				 vlan_mcast_params[ALE_MP_VID].val);
+	if (ret < 0)
+		return ret;
+
+	return str_len;
+}
+
+static int cpsw_ale_table_store_m_proc(struct cpsw_ale *ale,
+				       const u8 *params_str, size_t str_len)
+{
+	return  cpsw_ale_table_store_vlan_mcast(ale, params_str, str_len, 0);
+}
+
+static int cpsw_ale_table_store_vm_proc(struct cpsw_ale *ale,
+					const u8 *params_str,
+					size_t str_len)
+{
+	return  cpsw_ale_table_store_vlan_mcast(ale, params_str, str_len, 1);
+}
+
+static int cpsw_ale_add_oui(struct cpsw_ale *ale, u8 *addr)
+{
+	u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0};
+	int idx;
+
+	cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_ADDR);
+
+	cpsw_ale_set_addr(ale_entry, addr);
+	cpsw_ale_set_ucast_type(ale_entry, ALE_UCAST_OUI);
+
+	idx = cpsw_ale_match_addr(ale, addr, -1);
+	if (idx < 0)
+		idx = cpsw_ale_match_free(ale);
+	if (idx < 0)
+		idx = cpsw_ale_find_ageable(ale);
+	if (idx < 0)
+		return -ENOMEM;
+
+	cpsw_ale_write(ale, idx, ale_entry);
+	return 0;
+}
+
+static int cpsw_ale_table_store_oui(struct cpsw_ale *ale, const u8 *params_str,
+				    size_t str_len)
+{
+	int ret;
+
+	cpsw_ale_table_store_init_params(oui_params, 1);
+
+	ret = cpsw_ale_table_store_get_params(ale, oui_params, 1, params_str,
+					      str_len);
+	if (ret < 0)
+		return ret;
+
+	/* Clear out the don't cares */
+	oui_params[0].addr[3] = 0;
+	oui_params[0].addr[4] = 0;
+	oui_params[0].addr[5] = 0;
+
+	ret = cpsw_ale_add_oui(ale, oui_params[0].addr);
+	if (ret < 0)
+		return ret;
+
+	return str_len;
+}
+
+static int cpsw_ale_table_store_del(struct cpsw_ale *ale, int idx)
+{
+	u32 ale_entry[ALE_ENTRY_WORDS];
+	int type;
+
+	dev_dbg(ale->params.dev, "deleting entry[%d] ...\n", idx);
+
+	if (idx >= ale->params.ale_entries)
+		return -EINVAL;
+
+	cpsw_ale_read(ale, idx, ale_entry);
+
+	type = cpsw_ale_get_entry_type(ale_entry);
+	if (type == ALE_TYPE_FREE)
+		return -EINVAL;
+
+	cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_FREE);
+	cpsw_ale_write(ale, idx, ale_entry);
+	return 0;
+}
+
+static struct ale_table_cmd ale_table_cmds[] = {
+	{
+		.name		= "v",
+		.process	= cpsw_ale_table_store_vlan,
+	},
+	{
+		.name		= "m",
+		.process	= cpsw_ale_table_store_m_proc,
+	},
+	{
+		.name		= "vm",
+		.process	= cpsw_ale_table_store_vm_proc,
+	},
+	{
+		.name		= "u",
+		.process	= cpsw_ale_table_store_u_proc,
+	},
+	{
+		.name		= "vu",
+		.process	= cpsw_ale_table_store_vu_proc,
+	},
+	{
+		.name		= "o",
+		.process	= cpsw_ale_table_store_oui,
+	},
+};
+
+static ssize_t cpsw_ale_table_store_proc(struct cpsw_ale *ale,
+					 const char *buf, size_t count)
+{
+	int len, i, tmp_count = count, ret = -EINVAL;
+	char ctrl_str[33];
+	unsigned long end;
+
+	len = strcspn(buf, ".:");
+	if (len >= 5)
+		return -ENOMEM;
+
+	strncpy(ctrl_str, buf, len);
+	ctrl_str[len] = '\0';
+
+	/* skip to param beginning */
+	buf += len;
+	tmp_count -= len;
+
+	if (*buf == ':') {
+		/* delete cmd */
+		if (kstrtoul(ctrl_str, 0, &end))
+			return -EINVAL;
+		ret = cpsw_ale_table_store_del(ale, end);
+		if (ret != 0)
+			return ret;
+		else
+			return count;
+	}
+
+	if (len >= 3)
+		return -ENOMEM;
+
+	if (*buf != '.')
+		return -EINVAL;
+
+	++buf;
+	--tmp_count;
+
+	for (i = 0; i < ARRAY_SIZE(ale_table_cmds); i++) {
+		if (strcmp(ale_table_cmds[i].name, ctrl_str) == 0) {
+			ret = ale_table_cmds[i].process(ale, buf, tmp_count);
+			break;
+		}
+	}
+
+	if (ret < 0)
+		return ret;
+	else
+		return count;
+}
+
+static ssize_t ale_table_store(struct device *dev,
+			       struct device_attribute *attr,
+			       const char *buf, size_t count)
+{
+	struct cpsw_ale *ale = table_attr_to_ale(attr);
+
+	return cpsw_ale_table_store_proc(ale, buf, count);
+}
+DEVICE_ATTR_RW(ale_table);
+
+static int cpsw_ale_dump_entry_raw(int idx, u32 *ale_entry, char *buf, int len)
+{
+	int type, outlen = 0;
+
+	type = cpsw_ale_get_entry_type(ale_entry);
+	if (type == ALE_TYPE_FREE)
+		return outlen;
+
+	if (len < ALE_RAW_TBL_ENTRY_SHOW_LEN)
+		return outlen;
+
+	if (idx >= 0)
+		outlen += snprintf(buf + outlen, len - outlen,
+				   "%d: ", idx);
+
+	outlen += snprintf(buf + outlen, len - outlen, "%02x %08x %08x\n",
+			   ale_entry[0], ale_entry[1], ale_entry[2]);
+
+	return outlen;
+}
+
+static ssize_t ale_table_raw_show(struct device *dev,
+				  struct device_attribute *attr, char *buf)
+{
+	struct cpsw_ale *ale = table_raw_attr_to_ale(attr);
+	int not_shown = 0, total_outlen = 0, shown = 0;
+	int outlen = 0, idx, start, type;
+	u32 ale_entry[ALE_ENTRY_WORDS];
+
+	start = ale->raw_show_next;
+
+	for (idx = start; (idx < ale->params.ale_entries) &&
+	     (total_outlen < PAGE_SIZE); idx++) {
+		cpsw_ale_read(ale, idx, ale_entry);
+		outlen = cpsw_ale_dump_entry_raw(idx, ale_entry,
+						 buf + total_outlen,
+						 PAGE_SIZE - total_outlen);
+		if (outlen == 0) {
+			type = cpsw_ale_get_entry_type(ale_entry);
+			if (type != ALE_TYPE_FREE) {
+				++not_shown;
+				break;
+			}
+		} else {
+			total_outlen += outlen;
+			++shown;
+		}
+	}
+
+	/* update next show index */
+	if (idx >= ale->params.ale_entries)
+		ale->raw_show_next = 0;
+	else
+		ale->raw_show_next = idx;
+
+	if ((total_outlen + 32) < PAGE_SIZE)
+		total_outlen += snprintf(buf + total_outlen,
+					 PAGE_SIZE - total_outlen,
+					 "[%d..%d]: %d entries%s\n",
+					 start, idx - 1, shown, not_shown ?
+					 ", +" : "");
+
+	return total_outlen;
+}
+
+static ssize_t ale_table_raw_store(struct device *dev,
+				   struct device_attribute *attr,
+				   const char *buf, size_t count)
+{
+	struct cpsw_ale *ale = table_raw_attr_to_ale(attr);
+	unsigned long end;
+
+	if (kstrtoul(buf, 0, &end) == 0) {
+		/* set start-show-index command */
+		ale->raw_show_next = (int)end;
+		if (ale->raw_show_next >= ale->params.ale_entries)
+			ale->raw_show_next = 0;
+		return count;
+	}
+
+	/* add or delete command */
+	return cpsw_ale_table_store_proc(ale, buf, count);
+}
+DEVICE_ATTR_RW(ale_table_raw);
+
 static void cpsw_ale_timer(struct timer_list *t)
 static void cpsw_ale_timer(struct timer_list *t)
 {
 {
 	struct cpsw_ale *ale = from_timer(ale, t, timer);
 	struct cpsw_ale *ale = from_timer(ale, t, timer);
@@ -844,6 +1656,33 @@ static void cpsw_ale_timer(struct timer_list *t)
 
 
 void cpsw_ale_start(struct cpsw_ale *ale)
 void cpsw_ale_start(struct cpsw_ale *ale)
 {
 {
+	int ret, i;
+
+	if (ale->version == ALE_VERSION_1R3 ||
+	    ale->params.nu_switch_ale ||
+	    ale->version == ALE_VERSION_9R3) {
+		/* disable forwarding on all ports */
+		for (i = 0; i < ale->params.ale_ports; ++i)
+			cpsw_ale_control_set(ale, i, ALE_PORT_STATE,
+					     ALE_PORT_STATE_DISABLE);
+
+		ale->ale_control_attr = dev_attr_ale_control;
+		sysfs_attr_init(&ale->ale_control_attr.attr);
+		ret = device_create_file(ale->params.dev,
+					 &ale->ale_control_attr);
+		WARN_ON(ret < 0);
+
+		ale->ale_table_attr = dev_attr_ale_table;
+		sysfs_attr_init(&ale->ale_table_attr.attr);
+		ret = device_create_file(ale->params.dev, &ale->ale_table_attr);
+		WARN_ON(ret < 0);
+
+		ale->ale_table_raw_attr = dev_attr_ale_table_raw;
+		sysfs_attr_init(&ale->ale_table_raw_attr.attr);
+		ret = device_create_file(ale->params.dev,
+					 &ale->ale_table_raw_attr);
+		WARN_ON(ret < 0);
+	}
 	cpsw_ale_control_set(ale, 0, ALE_ENABLE, 1);
 	cpsw_ale_control_set(ale, 0, ALE_ENABLE, 1);
 	cpsw_ale_control_set(ale, 0, ALE_CLEAR, 1);
 	cpsw_ale_control_set(ale, 0, ALE_CLEAR, 1);
 
 
@@ -852,6 +1691,7 @@ void cpsw_ale_start(struct cpsw_ale *ale)
 		ale->timer.expires = jiffies + ale->ageout;
 		ale->timer.expires = jiffies + ale->ageout;
 		add_timer(&ale->timer);
 		add_timer(&ale->timer);
 	}
 	}
+
 }
 }
 EXPORT_SYMBOL_GPL(cpsw_ale_start);
 EXPORT_SYMBOL_GPL(cpsw_ale_start);
 
 
@@ -859,6 +1699,13 @@ void cpsw_ale_stop(struct cpsw_ale *ale)
 {
 {
 	del_timer_sync(&ale->timer);
 	del_timer_sync(&ale->timer);
 	cpsw_ale_control_set(ale, 0, ALE_ENABLE, 0);
 	cpsw_ale_control_set(ale, 0, ALE_ENABLE, 0);
+	if (ale->version == ALE_VERSION_1R3 ||
+	    ale->params.nu_switch_ale ||
+	    ale->version == ALE_VERSION_9R3) {
+		device_remove_file(ale->params.dev, &ale->ale_table_attr);
+		device_remove_file(ale->params.dev, &ale->ale_control_attr);
+		device_remove_file(ale->params.dev, &ale->ale_table_raw_attr);
+	}
 }
 }
 EXPORT_SYMBOL_GPL(cpsw_ale_stop);
 EXPORT_SYMBOL_GPL(cpsw_ale_stop);
 
 

+ 12 - 0
drivers/net/ethernet/ti/cpsw_ale.h

@@ -39,6 +39,17 @@ struct cpsw_ale {
 	unsigned long		ageout;
 	unsigned long		ageout;
 	int			allmulti;
 	int			allmulti;
 	u32			version;
 	u32			version;
+	struct device_attribute ale_control_attr;
+#define control_attr_to_ale(attr)	\
+	container_of(attr, struct cpsw_ale, ale_control_attr)
+	struct device_attribute ale_table_attr;
+#define table_attr_to_ale(attr)		\
+	container_of(attr, struct cpsw_ale, ale_table_attr)
+	struct device_attribute ale_table_raw_attr;
+#define table_raw_attr_to_ale(attr)		\
+	container_of(attr, struct cpsw_ale, ale_table_raw_attr)
+	int			show_next;
+	int			raw_show_next;
 	/* These bits are different on NetCP NU Switch ALE */
 	/* These bits are different on NetCP NU Switch ALE */
 	u32			port_mask_bits;
 	u32			port_mask_bits;
 	u32			port_num_bits;
 	u32			port_num_bits;
@@ -47,6 +58,7 @@ struct cpsw_ale {
 
 
 enum cpsw_ale_control {
 enum cpsw_ale_control {
 	/* global */
 	/* global */
+	ALE_VERSION,
 	ALE_ENABLE,
 	ALE_ENABLE,
 	ALE_CLEAR,
 	ALE_CLEAR,
 	ALE_AGEOUT,
 	ALE_AGEOUT,

+ 1 - 1
drivers/net/ethernet/ti/netcp.h

@@ -247,7 +247,7 @@ void *netcp_device_find_module(struct netcp_device *netcp_device,
 /* SGMII functions */
 /* SGMII functions */
 int netcp_sgmii_reset(void __iomem *sgmii_ofs, int port);
 int netcp_sgmii_reset(void __iomem *sgmii_ofs, int port);
 bool netcp_sgmii_rtreset(void __iomem *sgmii_ofs, int port, bool set);
 bool netcp_sgmii_rtreset(void __iomem *sgmii_ofs, int port, bool set);
-int netcp_sgmii_get_port_link(void __iomem *sgmii_ofs, int port);
+bool netcp_sgmii_get_port_link(void __iomem *sgmii_ofs, int port);
 int netcp_sgmii_config(void __iomem *sgmii_ofs, int port, u32 interface);
 int netcp_sgmii_config(void __iomem *sgmii_ofs, int port, u32 interface);
 
 
 /* XGBE SERDES init functions */
 /* XGBE SERDES init functions */

+ 297 - 236
drivers/net/ethernet/ti/netcp_ethss.c

@@ -19,35 +19,38 @@
  */
  */
 
 
 #include <linux/io.h>
 #include <linux/io.h>
+#include <linux/mfd/syscon.h>
 #include <linux/module.h>
 #include <linux/module.h>
 #include <linux/of_mdio.h>
 #include <linux/of_mdio.h>
 #include <linux/of_net.h>
 #include <linux/of_net.h>
 #include <linux/of_address.h>
 #include <linux/of_address.h>
+#include <linux/regmap.h>
 #include <linux/if_vlan.h>
 #include <linux/if_vlan.h>
 #include <linux/ptp_classify.h>
 #include <linux/ptp_classify.h>
 #include <linux/net_tstamp.h>
 #include <linux/net_tstamp.h>
 #include <linux/ethtool.h>
 #include <linux/ethtool.h>
+#include <linux/phy/phy.h>
 
 
 #include "cpsw.h"
 #include "cpsw.h"
-#include "cpsw_ale.h"
-#include "netcp.h"
 #include "cpts.h"
 #include "cpts.h"
+#include "netcp_ethss.h"
 
 
 #define NETCP_DRIVER_NAME		"TI KeyStone Ethernet Driver"
 #define NETCP_DRIVER_NAME		"TI KeyStone Ethernet Driver"
 #define NETCP_DRIVER_VERSION		"v1.0"
 #define NETCP_DRIVER_VERSION		"v1.0"
 
 
-#define GBE_IDENT(reg)			((reg >> 16) & 0xffff)
-#define GBE_MAJOR_VERSION(reg)		(reg >> 8 & 0x7)
-#define GBE_MINOR_VERSION(reg)		(reg & 0xff)
-#define GBE_RTL_VERSION(reg)		((reg >> 11) & 0x1f)
-
 /* 1G Ethernet SS defines */
 /* 1G Ethernet SS defines */
 #define GBE_MODULE_NAME			"netcp-gbe"
 #define GBE_MODULE_NAME			"netcp-gbe"
-#define GBE_SS_VERSION_14		0x4ed2
 
 
+/* for devicetree backward compatible only */
 #define GBE_SS_REG_INDEX		0
 #define GBE_SS_REG_INDEX		0
+
+#define GBE_SGMII_REG_INDEX		0
 #define GBE_SGMII34_REG_INDEX		1
 #define GBE_SGMII34_REG_INDEX		1
 #define GBE_SM_REG_INDEX		2
 #define GBE_SM_REG_INDEX		2
+
+/* For 2U, index 0 points to Switch module register base */
+#define GBE_2U_SM_REG_INDEX		0
+
 /* offset relative to base of GBE_SS_REG_INDEX */
 /* offset relative to base of GBE_SS_REG_INDEX */
 #define GBE13_SGMII_MODULE_OFFSET	0x100
 #define GBE13_SGMII_MODULE_OFFSET	0x100
 /* offset relative to base of GBE_SM_REG_INDEX */
 /* offset relative to base of GBE_SM_REG_INDEX */
@@ -60,27 +63,21 @@
 #define GBE13_ALE_OFFSET		0x600
 #define GBE13_ALE_OFFSET		0x600
 #define GBE13_HOST_PORT_NUM		0
 #define GBE13_HOST_PORT_NUM		0
 #define GBE13_NUM_ALE_ENTRIES		1024
 #define GBE13_NUM_ALE_ENTRIES		1024
+/* offset relative to PCSR regmap */
+#define XGBE10_PCSR_OFFSET(x)		((x) * 0x80)
+#define XGBE10_PCSR_RX_STATUS(x)	(XGBE10_PCSR_OFFSET(x) + 0xc)
+
+#define XGBE10_PCSR_BLOCK_LOCK_MASK	BIT(30)
+#define XGBE10_PCSR_BLOCK_LOCK_SHIFT	30
 
 
 /* 1G Ethernet NU SS defines */
 /* 1G Ethernet NU SS defines */
 #define GBENU_MODULE_NAME		"netcp-gbenu"
 #define GBENU_MODULE_NAME		"netcp-gbenu"
-#define GBE_SS_ID_NU			0x4ee6
-#define GBE_SS_ID_2U			0x4ee8
 
 
-#define IS_SS_ID_MU(d) \
-	((GBE_IDENT((d)->ss_version) == GBE_SS_ID_NU) || \
-	 (GBE_IDENT((d)->ss_version) == GBE_SS_ID_2U))
-
-#define IS_SS_ID_NU(d) \
-	(GBE_IDENT((d)->ss_version) == GBE_SS_ID_NU)
-
-#define IS_SS_ID_VER_14(d) \
-	(GBE_IDENT((d)->ss_version) == GBE_SS_VERSION_14)
-#define IS_SS_ID_2U(d) \
-	(GBE_IDENT((d)->ss_version) == GBE_SS_ID_2U)
-
-#define GBENU_SS_REG_INDEX		0
+#define GBENU_SGMII_REG_INDEX		0
 #define GBENU_SM_REG_INDEX		1
 #define GBENU_SM_REG_INDEX		1
+/* offset relative to base of GBE_SS_REG_INDEX */
 #define GBENU_SGMII_MODULE_OFFSET	0x100
 #define GBENU_SGMII_MODULE_OFFSET	0x100
+/* offset relative to base of GBENU_SM_REG_INDEX */
 #define GBENU_HOST_PORT_OFFSET		0x1000
 #define GBENU_HOST_PORT_OFFSET		0x1000
 #define GBENU_SLAVE_PORT_OFFSET		0x2000
 #define GBENU_SLAVE_PORT_OFFSET		0x2000
 #define GBENU_EMAC_OFFSET		0x2330
 #define GBENU_EMAC_OFFSET		0x2330
@@ -88,13 +85,11 @@
 #define GBENU_CPTS_OFFSET		0x1d000
 #define GBENU_CPTS_OFFSET		0x1d000
 #define GBENU_ALE_OFFSET		0x1e000
 #define GBENU_ALE_OFFSET		0x1e000
 #define GBENU_HOST_PORT_NUM		0
 #define GBENU_HOST_PORT_NUM		0
-#define GBENU_SGMII_MODULE_SIZE		0x100
 
 
 /* 10G Ethernet SS defines */
 /* 10G Ethernet SS defines */
 #define XGBE_MODULE_NAME		"netcp-xgbe"
 #define XGBE_MODULE_NAME		"netcp-xgbe"
-#define XGBE_SS_VERSION_10		0x4ee4
 
 
-#define XGBE_SS_REG_INDEX		0
+#define XGBE_SGMII_REG_INDEX		0
 #define XGBE_SM_REG_INDEX		1
 #define XGBE_SM_REG_INDEX		1
 #define XGBE_SERDES_REG_INDEX		2
 #define XGBE_SERDES_REG_INDEX		2
 
 
@@ -144,25 +139,6 @@
 		(MACSL_XGIG_MODE | MACSL_XGMII_ENABLE |		\
 		(MACSL_XGIG_MODE | MACSL_XGMII_ENABLE |		\
 		 MACSL_ENABLE_EXT_CTL |	MACSL_RX_ENABLE_CSF)
 		 MACSL_ENABLE_EXT_CTL |	MACSL_RX_ENABLE_CSF)
 
 
-#define GBE_STATSA_MODULE			0
-#define GBE_STATSB_MODULE			1
-#define GBE_STATSC_MODULE			2
-#define GBE_STATSD_MODULE			3
-
-#define GBENU_STATS0_MODULE			0
-#define GBENU_STATS1_MODULE			1
-#define GBENU_STATS2_MODULE			2
-#define GBENU_STATS3_MODULE			3
-#define GBENU_STATS4_MODULE			4
-#define GBENU_STATS5_MODULE			5
-#define GBENU_STATS6_MODULE			6
-#define GBENU_STATS7_MODULE			7
-#define GBENU_STATS8_MODULE			8
-
-#define XGBE_STATS0_MODULE			0
-#define XGBE_STATS1_MODULE			1
-#define XGBE_STATS2_MODULE			2
-
 /* s: 0-based slave_port */
 /* s: 0-based slave_port */
 #define SGMII_BASE(d, s) \
 #define SGMII_BASE(d, s) \
 	(((s) < 2) ? (d)->sgmii_port_regs : (d)->sgmii_port34_regs)
 	(((s) < 2) ? (d)->sgmii_port_regs : (d)->sgmii_port34_regs)
@@ -185,9 +161,10 @@
 		offsetof(struct gbenu##_##rb, rn)
 		offsetof(struct gbenu##_##rb, rn)
 #define XGBE_SET_REG_OFS(p, rb, rn) p->rb##_ofs.rn = \
 #define XGBE_SET_REG_OFS(p, rb, rn) p->rb##_ofs.rn = \
 		offsetof(struct xgbe##_##rb, rn)
 		offsetof(struct xgbe##_##rb, rn)
-#define GBE_REG_ADDR(p, rb, rn) (p->rb + p->rb##_ofs.rn)
+#define GBE_REG_OFS(p, rb, rn) ((p)->rb##_ofs.rn)
 
 
 #define HOST_TX_PRI_MAP_DEFAULT			0x00000000
 #define HOST_TX_PRI_MAP_DEFAULT			0x00000000
+#define SGMII_MODULE_SIZE			0x100
 
 
 #if IS_ENABLED(CONFIG_TI_CPTS)
 #if IS_ENABLED(CONFIG_TI_CPTS)
 /* Px_TS_CTL register fields */
 /* Px_TS_CTL register fields */
@@ -239,6 +216,7 @@
 #define EVENT_MSG_BITS (BIT(0) | BIT(1) | BIT(2) | BIT(3))
 #define EVENT_MSG_BITS (BIT(0) | BIT(1) | BIT(2) | BIT(3))
 #endif /* CONFIG_TI_CPTS */
 #endif /* CONFIG_TI_CPTS */
 
 
+#define SGMII_MODULE_SIZE			0x100
 struct xgbe_ss_regs {
 struct xgbe_ss_regs {
 	u32	id_ver;
 	u32	id_ver;
 	u32	synce_count;
 	u32	synce_count;
@@ -557,12 +535,6 @@ struct gbe_ss_regs {
 	u32	synce_mux;
 	u32	synce_mux;
 };
 };
 
 
-struct gbe_ss_regs_ofs {
-	u16	id_ver;
-	u16	control;
-	u16	rgmii_status; /* 2U */
-};
-
 struct gbe_switch_regs {
 struct gbe_switch_regs {
 	u32	id_ver;
 	u32	id_ver;
 	u32	control;
 	u32	control;
@@ -576,16 +548,6 @@ struct gbe_switch_regs {
 	u32	flow_control;
 	u32	flow_control;
 };
 };
 
 
-struct gbe_switch_regs_ofs {
-	u16	id_ver;
-	u16	control;
-	u16	soft_reset;
-	u16	emcontrol;
-	u16	stat_port_en;
-	u16	ptype;
-	u16	flow_control;
-};
-
 struct gbe_port_regs {
 struct gbe_port_regs {
 	u32	max_blks;
 	u32	max_blks;
 	u32	blk_cnt;
 	u32	blk_cnt;
@@ -600,20 +562,6 @@ struct gbe_port_regs {
 	u32	ts_ctl2;
 	u32	ts_ctl2;
 };
 };
 
 
-struct gbe_port_regs_ofs {
-	u16	port_vlan;
-	u16	tx_pri_map;
-	u16     rx_pri_map;
-	u16	sa_lo;
-	u16	sa_hi;
-	u16	ts_ctl;
-	u16	ts_seq_ltype;
-	u16	ts_vlan;
-	u16	ts_ctl_ltype2;
-	u16	ts_ctl2;
-	u16	rx_maxlen;	/* 2U, NU */
-};
-
 struct gbe_host_port_regs {
 struct gbe_host_port_regs {
 	u32	src_id;
 	u32	src_id;
 	u32	port_vlan;
 	u32	port_vlan;
@@ -621,12 +569,6 @@ struct gbe_host_port_regs {
 	u32	rx_maxlen;
 	u32	rx_maxlen;
 };
 };
 
 
-struct gbe_host_port_regs_ofs {
-	u16	port_vlan;
-	u16	tx_pri_map;
-	u16	rx_maxlen;
-};
-
 struct gbe_emac_regs {
 struct gbe_emac_regs {
 	u32	id_ver;
 	u32	id_ver;
 	u32	mac_control;
 	u32	mac_control;
@@ -641,12 +583,6 @@ struct gbe_emac_regs {
 	u32	rsvd[6];
 	u32	rsvd[6];
 };
 };
 
 
-struct gbe_emac_regs_ofs {
-	u16	mac_control;
-	u16	soft_reset;
-	u16	rx_maxlen;
-};
-
 struct gbe_hw_stats {
 struct gbe_hw_stats {
 	u32	rx_good_frames;
 	u32	rx_good_frames;
 	u32	rx_broadcast_frames;
 	u32	rx_broadcast_frames;
@@ -685,98 +621,8 @@ struct gbe_hw_stats {
 	u32	rx_dma_overruns;
 	u32	rx_dma_overruns;
 };
 };
 
 
-#define GBE_MAX_HW_STAT_MODS			9
 #define GBE_HW_STATS_REG_MAP_SZ			0x100
 #define GBE_HW_STATS_REG_MAP_SZ			0x100
 
 
-struct ts_ctl {
-	int     uni;
-	u8      dst_port_map;
-	u8      maddr_map;
-	u8      ts_mcast_type;
-};
-
-struct gbe_slave {
-	void __iomem			*port_regs;
-	void __iomem			*emac_regs;
-	struct gbe_port_regs_ofs	port_regs_ofs;
-	struct gbe_emac_regs_ofs	emac_regs_ofs;
-	int				slave_num; /* 0 based logical number */
-	int				port_num;  /* actual port number */
-	atomic_t			link_state;
-	bool				open;
-	struct phy_device		*phy;
-	u32				link_interface;
-	u32				mac_control;
-	u8				phy_port_t;
-	struct device_node		*node;
-	struct device_node		*phy_node;
-	struct ts_ctl                   ts_ctl;
-	struct list_head		slave_list;
-};
-
-struct gbe_priv {
-	struct device			*dev;
-	struct netcp_device		*netcp_device;
-	struct timer_list		timer;
-	u32				num_slaves;
-	u32				ale_entries;
-	u32				ale_ports;
-	bool				enable_ale;
-	u8				max_num_slaves;
-	u8				max_num_ports; /* max_num_slaves + 1 */
-	u8				num_stats_mods;
-	struct netcp_tx_pipe		tx_pipe;
-
-	int				host_port;
-	u32				rx_packet_max;
-	u32				ss_version;
-	u32				stats_en_mask;
-
-	void __iomem			*ss_regs;
-	void __iomem			*switch_regs;
-	void __iomem			*host_port_regs;
-	void __iomem			*ale_reg;
-	void __iomem                    *cpts_reg;
-	void __iomem			*sgmii_port_regs;
-	void __iomem			*sgmii_port34_regs;
-	void __iomem			*xgbe_serdes_regs;
-	void __iomem			*hw_stats_regs[GBE_MAX_HW_STAT_MODS];
-
-	struct gbe_ss_regs_ofs		ss_regs_ofs;
-	struct gbe_switch_regs_ofs	switch_regs_ofs;
-	struct gbe_host_port_regs_ofs	host_port_regs_ofs;
-
-	struct cpsw_ale			*ale;
-	unsigned int			tx_queue_id;
-	const char			*dma_chan_name;
-
-	struct list_head		gbe_intf_head;
-	struct list_head		secondary_slaves;
-	struct net_device		*dummy_ndev;
-
-	u64				*hw_stats;
-	u32				*hw_stats_prev;
-	const struct netcp_ethtool_stat *et_stats;
-	int				num_et_stats;
-	/*  Lock for updating the hwstats */
-	spinlock_t			hw_stats_lock;
-
-	int                             cpts_registered;
-	struct cpts                     *cpts;
-	int				rx_ts_enabled;
-	int				tx_ts_enabled;
-};
-
-struct gbe_intf {
-	struct net_device	*ndev;
-	struct device		*dev;
-	struct gbe_priv		*gbe_dev;
-	struct netcp_tx_pipe	tx_pipe;
-	struct gbe_slave	*slave;
-	struct list_head	gbe_intf_list;
-	unsigned long		active_vlans[BITS_TO_LONGS(VLAN_N_VID)];
-};
-
 static struct netcp_module gbe_module;
 static struct netcp_module gbe_module;
 static struct netcp_module xgbe_module;
 static struct netcp_module xgbe_module;
 
 
@@ -1733,9 +1579,6 @@ static const struct netcp_ethtool_stat xgbe10_et_stats[] = {
 	XGBE_STATS2_INFO(rx_dma_overruns),
 	XGBE_STATS2_INFO(rx_dma_overruns),
 };
 };
 
 
-#define for_each_intf(i, priv) \
-	list_for_each_entry((i), &(priv)->gbe_intf_head, gbe_intf_list)
-
 #define for_each_sec_slave(slave, priv) \
 #define for_each_sec_slave(slave, priv) \
 	list_for_each_entry((slave), &(priv)->secondary_slaves, slave_list)
 	list_for_each_entry((slave), &(priv)->secondary_slaves, slave_list)
 
 
@@ -1822,7 +1665,7 @@ static int keystone_get_sset_count(struct net_device *ndev, int stringset)
 	}
 	}
 }
 }
 
 
-static void gbe_reset_mod_stats(struct gbe_priv *gbe_dev, int stats_mod)
+void gbe_reset_mod_stats(struct gbe_priv *gbe_dev, int stats_mod)
 {
 {
 	void __iomem *base = gbe_dev->hw_stats_regs[stats_mod];
 	void __iomem *base = gbe_dev->hw_stats_regs[stats_mod];
 	u32  __iomem *p_stats_entry;
 	u32  __iomem *p_stats_entry;
@@ -1891,7 +1734,7 @@ static inline void gbe_stats_mod_visible_ver14(struct gbe_priv *gbe_dev,
 	writel(val, GBE_REG_ADDR(gbe_dev, switch_regs, stat_port_en));
 	writel(val, GBE_REG_ADDR(gbe_dev, switch_regs, stat_port_en));
 }
 }
 
 
-static void gbe_reset_mod_stats_ver14(struct gbe_priv *gbe_dev, int stats_mod)
+void gbe_reset_mod_stats_ver14(struct gbe_priv *gbe_dev, int stats_mod)
 {
 {
 	gbe_stats_mod_visible_ver14(gbe_dev, stats_mod);
 	gbe_stats_mod_visible_ver14(gbe_dev, stats_mod);
 	gbe_reset_mod_stats(gbe_dev, stats_mod);
 	gbe_reset_mod_stats(gbe_dev, stats_mod);
@@ -2081,6 +1924,24 @@ static int gbe_get_slave_port(struct gbe_priv *priv, u32 slave_num)
 	return slave_num;
 	return slave_num;
 }
 }
 
 
+/* Number of GBE_TIMER_INTERVAL */
+#define LINK_RECOVER_THRESHOLD	6
+
+static void gbe_slave_link_recover(struct work_struct *work)
+{
+	struct gbe_slave *slave = container_of(work, struct gbe_slave,
+					       link_recover_work.work);
+	struct device *dev = slave->gbe_dev->dev;
+	int lane = slave->slave_num;
+	int ret;
+
+	dev_dbg(dev, "recovering serdes lane %d ...\n", lane);
+
+	ret = phy_reset(slave->serdes_phy);
+	if (!ret)
+		dev_dbg(dev, "Serdes Lane %u rx recovered\n", lane);
+}
+
 static void netcp_ethss_link_state_action(struct gbe_priv *gbe_dev,
 static void netcp_ethss_link_state_action(struct gbe_priv *gbe_dev,
 					  struct net_device *ndev,
 					  struct net_device *ndev,
 					  struct gbe_slave *slave,
 					  struct gbe_slave *slave,
@@ -2111,6 +1972,11 @@ static void netcp_ethss_link_state_action(struct gbe_priv *gbe_dev,
 		    (slave->link_interface != RGMII_LINK_MAC_PHY) &&
 		    (slave->link_interface != RGMII_LINK_MAC_PHY) &&
 		    (slave->link_interface != XGMII_LINK_MAC_PHY)))
 		    (slave->link_interface != XGMII_LINK_MAC_PHY)))
 			netif_carrier_on(ndev);
 			netif_carrier_on(ndev);
+
+		if (phy)
+			phy_print_status(phy);
+		else
+			netdev_printk(KERN_INFO, ndev, "Link is Up\n");
 	} else {
 	} else {
 		writel(mac_control, GBE_REG_ADDR(slave, emac_regs,
 		writel(mac_control, GBE_REG_ADDR(slave, emac_regs,
 						 mac_control));
 						 mac_control));
@@ -2122,10 +1988,30 @@ static void netcp_ethss_link_state_action(struct gbe_priv *gbe_dev,
 		    (slave->link_interface != RGMII_LINK_MAC_PHY) &&
 		    (slave->link_interface != RGMII_LINK_MAC_PHY) &&
 		    (slave->link_interface != XGMII_LINK_MAC_PHY)))
 		    (slave->link_interface != XGMII_LINK_MAC_PHY)))
 			netif_carrier_off(ndev);
 			netif_carrier_off(ndev);
+
+		netdev_printk(KERN_INFO, ndev, "Link is Down\n");
 	}
 	}
 
 
-	if (phy)
-		phy_print_status(phy);
+	if (slave->link_interface == XGMII_LINK_MAC_MAC_FORCED ||
+	    (slave->link_interface == SGMII_LINK_MAC_MAC_FORCED && ndev)) {
+		if (up) {
+			if (slave->link_recover_thresh ||
+			    slave->link_recovering) {
+				slave->link_recover_thresh = 0;
+				slave->link_recovering = 0;
+				dev_info(gbe_dev->dev,
+					 "link_recover process cancelled: %s slave %d\n",
+					 netdev_name(ndev), slave->slave_num);
+			}
+		} else {
+			/* from up to down */
+			slave->link_recover_thresh = LINK_RECOVER_THRESHOLD;
+			slave->link_recovering = 1;
+			dev_info(gbe_dev->dev,
+				 "link_recover process initiated: %s slave %d\n",
+				 netdev_name(ndev), slave->slave_num);
+		}
+	}
 }
 }
 
 
 static bool gbe_phy_link_status(struct gbe_slave *slave)
 static bool gbe_phy_link_status(struct gbe_slave *slave)
@@ -2135,12 +2021,21 @@ static bool gbe_phy_link_status(struct gbe_slave *slave)
 
 
 #define RGMII_REG_STATUS_LINK	BIT(0)
 #define RGMII_REG_STATUS_LINK	BIT(0)
 
 
-static void netcp_2u_rgmii_get_port_link(struct gbe_priv *gbe_dev, bool *status)
+static int netcp_2u_rgmii_get_port_link(struct gbe_priv *gbe_dev, bool *status)
 {
 {
 	u32 val = 0;
 	u32 val = 0;
+	int ret;
 
 
-	val = readl(GBE_REG_ADDR(gbe_dev, ss_regs, rgmii_status));
+	ret = regmap_read(gbe_dev->ss_regmap,
+			  GBE_REG_OFS(gbe_dev, ss_regs, rgmii_status), &val);
+	if (ret) {
+		dev_err(gbe_dev->dev,
+			"%s: error in reading rgmii status\n", __func__);
+		return ret;
+	}
 	*status = !!(val & RGMII_REG_STATUS_LINK);
 	*status = !!(val & RGMII_REG_STATUS_LINK);
+
+	return ret;
 }
 }
 
 
 static void netcp_ethss_update_link_state(struct gbe_priv *gbe_dev,
 static void netcp_ethss_update_link_state(struct gbe_priv *gbe_dev,
@@ -2148,24 +2043,50 @@ static void netcp_ethss_update_link_state(struct gbe_priv *gbe_dev,
 					  struct net_device *ndev)
 					  struct net_device *ndev)
 {
 {
 	bool sw_link_state = true, phy_link_state;
 	bool sw_link_state = true, phy_link_state;
-	int sp = slave->slave_num, link_state;
+	int sp = slave->slave_num, link_state, ret;
+	u32 pcsr_rx_stat;
 
 
 	if (!slave->open)
 	if (!slave->open)
 		return;
 		return;
 
 
-	if (SLAVE_LINK_IS_RGMII(slave))
-		netcp_2u_rgmii_get_port_link(gbe_dev,
-					     &sw_link_state);
+	if (SLAVE_LINK_IS_RGMII(slave)) {
+		ret = netcp_2u_rgmii_get_port_link(gbe_dev,
+						   &sw_link_state);
+		if (ret)
+			return;
+	}
 	if (SLAVE_LINK_IS_SGMII(slave))
 	if (SLAVE_LINK_IS_SGMII(slave))
 		sw_link_state =
 		sw_link_state =
 		netcp_sgmii_get_port_link(SGMII_BASE(gbe_dev, sp), sp);
 		netcp_sgmii_get_port_link(SGMII_BASE(gbe_dev, sp), sp);
 
 
+	if (SLAVE_LINK_IS_XGMII(slave) &&
+	    slave->link_interface == XGMII_LINK_MAC_MAC_FORCED) {
+		/* read status from pcsr status reg */
+		ret = regmap_read(gbe_dev->pcsr_regmap,
+				  XGBE10_PCSR_RX_STATUS(sp), &pcsr_rx_stat);
+		if (ret)
+			return;
+
+		sw_link_state = (pcsr_rx_stat & XGBE10_PCSR_BLOCK_LOCK_MASK) >>
+				 XGBE10_PCSR_BLOCK_LOCK_SHIFT;
+	}
+
 	phy_link_state = gbe_phy_link_status(slave);
 	phy_link_state = gbe_phy_link_status(slave);
 	link_state = phy_link_state & sw_link_state;
 	link_state = phy_link_state & sw_link_state;
 
 
-	if (atomic_xchg(&slave->link_state, link_state) != link_state)
+	if (atomic_xchg(&slave->link_state, link_state) != link_state) {
 		netcp_ethss_link_state_action(gbe_dev, ndev, slave,
 		netcp_ethss_link_state_action(gbe_dev, ndev, slave,
 					      link_state);
 					      link_state);
+	} else {
+		if (slave->link_recover_thresh) {
+			if (++slave->link_recovering >=
+					slave->link_recover_thresh) {
+				schedule_delayed_work(&slave->link_recover_work,
+						      0);
+				slave->link_recovering = 1;
+			}
+		}
+	}
 }
 }
 
 
 static void xgbe_adjust_link(struct net_device *ndev)
 static void xgbe_adjust_link(struct net_device *ndev)
@@ -2229,7 +2150,7 @@ static void gbe_port_config(struct gbe_priv *gbe_dev, struct gbe_slave *slave,
 			    int max_rx_len)
 			    int max_rx_len)
 {
 {
 	void __iomem *rx_maxlen_reg;
 	void __iomem *rx_maxlen_reg;
-	u32 xgmii_mode;
+	int ret;
 
 
 	if (max_rx_len > NETCP_MAX_FRAME_SIZE)
 	if (max_rx_len > NETCP_MAX_FRAME_SIZE)
 		max_rx_len = NETCP_MAX_FRAME_SIZE;
 		max_rx_len = NETCP_MAX_FRAME_SIZE;
@@ -2237,9 +2158,16 @@ static void gbe_port_config(struct gbe_priv *gbe_dev, struct gbe_slave *slave,
 	/* Enable correct MII mode at SS level */
 	/* Enable correct MII mode at SS level */
 	if (IS_SS_ID_XGBE(gbe_dev) &&
 	if (IS_SS_ID_XGBE(gbe_dev) &&
 	    (slave->link_interface >= XGMII_LINK_MAC_PHY)) {
 	    (slave->link_interface >= XGMII_LINK_MAC_PHY)) {
-		xgmii_mode = readl(GBE_REG_ADDR(gbe_dev, ss_regs, control));
-		xgmii_mode |= (1 << slave->slave_num);
-		writel(xgmii_mode, GBE_REG_ADDR(gbe_dev, ss_regs, control));
+		ret = regmap_update_bits(gbe_dev->ss_regmap,
+					 GBE_REG_OFS(gbe_dev, ss_regs, control),
+					 BIT(slave->slave_num),
+					 BIT(slave->slave_num));
+
+		if (ret) {
+			dev_err(gbe_dev->dev,
+				"regmap update xgmii mode bit Failed\n");
+			return;
+		}
 	}
 	}
 
 
 	if (IS_SS_ID_MU(gbe_dev))
 	if (IS_SS_ID_MU(gbe_dev))
@@ -2776,6 +2704,27 @@ static inline int gbe_hwtstamp_set(struct gbe_intf *gbe_intf, struct ifreq *req)
 }
 }
 #endif /* CONFIG_TI_CPTS */
 #endif /* CONFIG_TI_CPTS */
 
 
+static int init_serdes_phys(struct gbe_priv *gbe_dev, struct gbe_slave *slave,
+			    struct device_node *node, bool do_phy_init)
+{
+	struct device *dev = gbe_dev->dev;
+	struct phy *phy;
+
+	phy = devm_of_phy_get_by_index(dev, node, 0);
+	if (IS_ERR(phy)) {
+		/* this one may be disabled, quietly skip */
+		dev_dbg(dev, "%s sl-%d: No serdes phy found: %ld\n",
+			node->name, slave->slave_num, PTR_ERR(phy));
+		return 0;
+	}
+
+	slave->serdes_phy = phy;
+	if (!do_phy_init)
+		return 0;
+
+	return phy_init(phy);
+}
+
 static int gbe_set_rx_mode(void *intf_priv, bool promisc)
 static int gbe_set_rx_mode(void *intf_priv, bool promisc)
 {
 {
 	struct gbe_intf *gbe_intf = intf_priv;
 	struct gbe_intf *gbe_intf = intf_priv;
@@ -3021,6 +2970,7 @@ static int init_slave(struct gbe_priv *gbe_dev, struct gbe_slave *slave,
 	}
 	}
 
 
 	slave->node = node;
 	slave->node = node;
+	slave->gbe_dev = gbe_dev;
 	slave->open = false;
 	slave->open = false;
 	if ((slave->link_interface == SGMII_LINK_MAC_PHY) ||
 	if ((slave->link_interface == SGMII_LINK_MAC_PHY) ||
 	    (slave->link_interface == RGMII_LINK_MAC_PHY) ||
 	    (slave->link_interface == RGMII_LINK_MAC_PHY) ||
@@ -3083,6 +3033,9 @@ static int init_slave(struct gbe_priv *gbe_dev, struct gbe_slave *slave,
 		GBE_SET_REG_OFS(slave, emac_regs, soft_reset);
 		GBE_SET_REG_OFS(slave, emac_regs, soft_reset);
 		GBE_SET_REG_OFS(slave, emac_regs, rx_maxlen);
 		GBE_SET_REG_OFS(slave, emac_regs, rx_maxlen);
 
 
+		if (slave->link_interface == SGMII_LINK_MAC_MAC_FORCED)
+			INIT_DELAYED_WORK(&slave->link_recover_work,
+					  gbe_slave_link_recover);
 	} else if (IS_SS_ID_MU(gbe_dev)) {
 	} else if (IS_SS_ID_MU(gbe_dev)) {
 		/* Initialize  slave port register offsets */
 		/* Initialize  slave port register offsets */
 		GBENU_SET_REG_OFS(slave, port_regs, port_vlan);
 		GBENU_SET_REG_OFS(slave, port_regs, port_vlan);
@@ -3101,6 +3054,10 @@ static int init_slave(struct gbe_priv *gbe_dev, struct gbe_slave *slave,
 		GBENU_SET_REG_OFS(slave, emac_regs, mac_control);
 		GBENU_SET_REG_OFS(slave, emac_regs, mac_control);
 		GBENU_SET_REG_OFS(slave, emac_regs, soft_reset);
 		GBENU_SET_REG_OFS(slave, emac_regs, soft_reset);
 
 
+		if (slave->link_interface == SGMII_LINK_MAC_MAC_FORCED)
+			INIT_DELAYED_WORK(&slave->link_recover_work,
+					  gbe_slave_link_recover);
+
 	} else if (IS_SS_ID_XGBE(gbe_dev)) {
 	} else if (IS_SS_ID_XGBE(gbe_dev)) {
 		/* Initialize  slave port register offsets */
 		/* Initialize  slave port register offsets */
 		XGBE_SET_REG_OFS(slave, port_regs, port_vlan);
 		XGBE_SET_REG_OFS(slave, port_regs, port_vlan);
@@ -3117,6 +3074,8 @@ static int init_slave(struct gbe_priv *gbe_dev, struct gbe_slave *slave,
 		XGBE_SET_REG_OFS(slave, emac_regs, mac_control);
 		XGBE_SET_REG_OFS(slave, emac_regs, mac_control);
 		XGBE_SET_REG_OFS(slave, emac_regs, soft_reset);
 		XGBE_SET_REG_OFS(slave, emac_regs, soft_reset);
 		XGBE_SET_REG_OFS(slave, emac_regs, rx_maxlen);
 		XGBE_SET_REG_OFS(slave, emac_regs, rx_maxlen);
+		INIT_DELAYED_WORK(&slave->link_recover_work,
+				  gbe_slave_link_recover);
 	}
 	}
 
 
 	atomic_set(&slave->link_state, NETCP_LINK_STATE_INVALID);
 	atomic_set(&slave->link_state, NETCP_LINK_STATE_INVALID);
@@ -3134,6 +3093,7 @@ static void init_secondary_ports(struct gbe_priv *gbe_dev,
 	struct device_node *port;
 	struct device_node *port;
 	struct gbe_slave *slave;
 	struct gbe_slave *slave;
 	bool mac_phy_link = false;
 	bool mac_phy_link = false;
+	int ret;
 
 
 	for_each_child_of_node(node, port) {
 	for_each_child_of_node(node, port) {
 		slave = devm_kzalloc(dev, sizeof(*slave), GFP_KERNEL);
 		slave = devm_kzalloc(dev, sizeof(*slave), GFP_KERNEL);
@@ -3151,7 +3111,16 @@ static void init_secondary_ports(struct gbe_priv *gbe_dev,
 			continue;
 			continue;
 		}
 		}
 
 
-		if (!IS_SS_ID_2U(gbe_dev))
+		if (!IS_SS_ID_2U(gbe_dev)) {
+			ret = init_serdes_phys(gbe_dev, slave, port, true);
+			if (ret && (ret != -ENODEV)) {
+				dev_err(dev, "serdes phy init failed\n");
+				devm_kfree(dev, slave);
+				continue;
+			}
+		}
+
+		if (IS_SS_ID_VER_14(gbe_dev) || IS_SS_ID_NU(gbe_dev))
 			gbe_sgmii_config(gbe_dev, slave);
 			gbe_sgmii_config(gbe_dev, slave);
 		gbe_port_reset(slave);
 		gbe_port_reset(slave);
 		gbe_port_config(gbe_dev, slave, gbe_dev->rx_packet_max);
 		gbe_port_config(gbe_dev, slave, gbe_dev->rx_packet_max);
@@ -3222,6 +3191,8 @@ static void free_secondary_ports(struct gbe_priv *gbe_dev)
 	while (!list_empty(&gbe_dev->secondary_slaves)) {
 	while (!list_empty(&gbe_dev->secondary_slaves)) {
 		slave = first_sec_slave(gbe_dev);
 		slave = first_sec_slave(gbe_dev);
 
 
+		phy_exit(slave->serdes_phy);
+
 		if (slave->phy)
 		if (slave->phy)
 			phy_disconnect(slave->phy);
 			phy_disconnect(slave->phy);
 		list_del(&slave->slave_list);
 		list_del(&slave->slave_list);
@@ -3237,20 +3208,25 @@ static int set_xgbe_ethss10_priv(struct gbe_priv *gbe_dev,
 	void __iomem *regs;
 	void __iomem *regs;
 	int ret, i;
 	int ret, i;
 
 
-	ret = of_address_to_resource(node, XGBE_SS_REG_INDEX, &res);
-	if (ret) {
+	gbe_dev->ss_regmap = syscon_regmap_lookup_by_phandle(node,
+							     "syscon-subsys");
+
+	if (IS_ERR(gbe_dev->ss_regmap)) {
 		dev_err(gbe_dev->dev,
 		dev_err(gbe_dev->dev,
-			"Can't xlate xgbe of node(%s) ss address at %d\n",
-			node->name, XGBE_SS_REG_INDEX);
-		return ret;
+			"subsys regmap lookup failed: %ld\n",
+			PTR_ERR(gbe_dev->ss_regmap));
+		return PTR_ERR(gbe_dev->ss_regmap);
 	}
 	}
 
 
-	regs = devm_ioremap_resource(gbe_dev->dev, &res);
-	if (IS_ERR(regs)) {
-		dev_err(gbe_dev->dev, "Failed to map xgbe ss register base\n");
-		return PTR_ERR(regs);
+	gbe_dev->pcsr_regmap = syscon_regmap_lookup_by_phandle(node,
+							       "syscon-pcsr");
+
+	if (IS_ERR(gbe_dev->pcsr_regmap)) {
+		dev_err(gbe_dev->dev,
+			"pcsr regmap lookup failed: %ld\n",
+			PTR_ERR(gbe_dev->pcsr_regmap));
+		return PTR_ERR(gbe_dev->pcsr_regmap);
 	}
 	}
-	gbe_dev->ss_regs = regs;
 
 
 	ret = of_address_to_resource(node, XGBE_SM_REG_INDEX, &res);
 	ret = of_address_to_resource(node, XGBE_SM_REG_INDEX, &res);
 	if (ret) {
 	if (ret) {
@@ -3267,20 +3243,23 @@ static int set_xgbe_ethss10_priv(struct gbe_priv *gbe_dev,
 	}
 	}
 	gbe_dev->switch_regs = regs;
 	gbe_dev->switch_regs = regs;
 
 
-	ret = of_address_to_resource(node, XGBE_SERDES_REG_INDEX, &res);
+	ret = of_address_to_resource(node, XGBE_SGMII_REG_INDEX, &res);
 	if (ret) {
 	if (ret) {
 		dev_err(gbe_dev->dev,
 		dev_err(gbe_dev->dev,
-			"Can't xlate xgbe serdes of node(%s) address at %d\n",
-			node->name, XGBE_SERDES_REG_INDEX);
+			"Can't xlate xgbe of node(%s) sgmii address at %d\n",
+			node->name, XGBE_SGMII_REG_INDEX);
 		return ret;
 		return ret;
 	}
 	}
 
 
 	regs = devm_ioremap_resource(gbe_dev->dev, &res);
 	regs = devm_ioremap_resource(gbe_dev->dev, &res);
 	if (IS_ERR(regs)) {
 	if (IS_ERR(regs)) {
-		dev_err(gbe_dev->dev, "Failed to map xgbe serdes register base\n");
+		dev_err(gbe_dev->dev,
+			"Failed to map xgbe sgmii register base\n");
 		return PTR_ERR(regs);
 		return PTR_ERR(regs);
 	}
 	}
-	gbe_dev->xgbe_serdes_regs = regs;
+	gbe_dev->sgmii_port_regs = regs;
+	gbe_dev->sgmii_port34_regs = gbe_dev->sgmii_port_regs +
+				     (2 * SGMII_MODULE_SIZE);
 
 
 	gbe_dev->num_stats_mods = gbe_dev->max_num_ports;
 	gbe_dev->num_stats_mods = gbe_dev->max_num_ports;
 	gbe_dev->et_stats = xgbe10_et_stats;
 	gbe_dev->et_stats = xgbe10_et_stats;
@@ -3305,9 +3284,9 @@ static int set_xgbe_ethss10_priv(struct gbe_priv *gbe_dev,
 	}
 	}
 
 
 	gbe_dev->ss_version = XGBE_SS_VERSION_10;
 	gbe_dev->ss_version = XGBE_SS_VERSION_10;
-	gbe_dev->sgmii_port_regs = gbe_dev->ss_regs +
-					XGBE10_SGMII_MODULE_OFFSET;
-	gbe_dev->host_port_regs = gbe_dev->ss_regs + XGBE10_HOST_PORT_OFFSET;
+
+	gbe_dev->host_port_regs = gbe_dev->switch_regs +
+					XGBE10_HOST_PORT_OFFSET;
 
 
 	for (i = 0; i < gbe_dev->max_num_ports; i++)
 	for (i = 0; i < gbe_dev->max_num_ports; i++)
 		gbe_dev->hw_stats_regs[i] = gbe_dev->switch_regs +
 		gbe_dev->hw_stats_regs[i] = gbe_dev->switch_regs +
@@ -3338,8 +3317,8 @@ static int set_xgbe_ethss10_priv(struct gbe_priv *gbe_dev,
 	return 0;
 	return 0;
 }
 }
 
 
-static int get_gbe_resource_version(struct gbe_priv *gbe_dev,
-				    struct device_node *node)
+static int get_gbe_resource_version_ss_regs(struct gbe_priv *gbe_dev,
+					    struct device_node *node)
 {
 {
 	struct resource res;
 	struct resource res;
 	void __iomem *regs;
 	void __iomem *regs;
@@ -3358,8 +3337,27 @@ static int get_gbe_resource_version(struct gbe_priv *gbe_dev,
 		dev_err(gbe_dev->dev, "Failed to map gbe register base\n");
 		dev_err(gbe_dev->dev, "Failed to map gbe register base\n");
 		return PTR_ERR(regs);
 		return PTR_ERR(regs);
 	}
 	}
+
 	gbe_dev->ss_regs = regs;
 	gbe_dev->ss_regs = regs;
 	gbe_dev->ss_version = readl(gbe_dev->ss_regs);
 	gbe_dev->ss_version = readl(gbe_dev->ss_regs);
+	gbe_dev->ss_regmap = NULL;
+	return 0;
+}
+
+static int get_gbe_resource_version(struct gbe_priv *gbe_dev,
+				    struct device_node *node)
+{
+	gbe_dev->ss_regmap = syscon_regmap_lookup_by_phandle(node,
+							     "syscon-subsys");
+	if (IS_ERR(gbe_dev->ss_regmap)) {
+		dev_dbg(gbe_dev->dev,
+			"subsys regmap lookup failed: %ld. try reg property\n",
+			PTR_ERR(gbe_dev->ss_regmap));
+		return get_gbe_resource_version_ss_regs(gbe_dev, node);
+	}
+
+	regmap_read(gbe_dev->ss_regmap, 0, &gbe_dev->ss_version);
+	gbe_dev->ss_regs = NULL;
 	return 0;
 	return 0;
 }
 }
 
 
@@ -3370,6 +3368,27 @@ static int set_gbe_ethss14_priv(struct gbe_priv *gbe_dev,
 	void __iomem *regs;
 	void __iomem *regs;
 	int i, ret;
 	int i, ret;
 
 
+	if (gbe_dev->ss_regs) {
+		gbe_dev->sgmii_port_regs = gbe_dev->ss_regs +
+					   GBE13_SGMII_MODULE_OFFSET;
+	} else {
+		ret = of_address_to_resource(node, GBE_SGMII_REG_INDEX, &res);
+		if (ret) {
+			dev_err(gbe_dev->dev,
+				"Can't translate of gbe node(%s) address at index %d\n",
+				node->name, GBE_SGMII_REG_INDEX);
+			return ret;
+		}
+
+		regs = devm_ioremap_resource(gbe_dev->dev, &res);
+		if (IS_ERR(regs)) {
+			dev_err(gbe_dev->dev,
+				"Failed to map gbe sgmii port register base\n");
+			return PTR_ERR(regs);
+		}
+		gbe_dev->sgmii_port_regs = regs;
+	}
+
 	ret = of_address_to_resource(node, GBE_SGMII34_REG_INDEX, &res);
 	ret = of_address_to_resource(node, GBE_SGMII34_REG_INDEX, &res);
 	if (ret) {
 	if (ret) {
 		dev_err(gbe_dev->dev,
 		dev_err(gbe_dev->dev,
@@ -3424,7 +3443,6 @@ static int set_gbe_ethss14_priv(struct gbe_priv *gbe_dev,
 		return -ENOMEM;
 		return -ENOMEM;
 	}
 	}
 
 
-	gbe_dev->sgmii_port_regs = gbe_dev->ss_regs + GBE13_SGMII_MODULE_OFFSET;
 	gbe_dev->host_port_regs = gbe_dev->switch_regs + GBE13_HOST_PORT_OFFSET;
 	gbe_dev->host_port_regs = gbe_dev->switch_regs + GBE13_HOST_PORT_OFFSET;
 
 
 	/* K2HK has only 2 hw stats modules visible at a time, so
 	/* K2HK has only 2 hw stats modules visible at a time, so
@@ -3464,9 +3482,9 @@ static int set_gbe_ethss14_priv(struct gbe_priv *gbe_dev,
 static int set_gbenu_ethss_priv(struct gbe_priv *gbe_dev,
 static int set_gbenu_ethss_priv(struct gbe_priv *gbe_dev,
 				struct device_node *node)
 				struct device_node *node)
 {
 {
+	int i, ret, sm_index = GBENU_SM_REG_INDEX;
 	struct resource res;
 	struct resource res;
 	void __iomem *regs;
 	void __iomem *regs;
-	int i, ret;
 
 
 	gbe_dev->num_stats_mods = gbe_dev->max_num_ports;
 	gbe_dev->num_stats_mods = gbe_dev->max_num_ports;
 	gbe_dev->et_stats = gbenu_et_stats;
 	gbe_dev->et_stats = gbenu_et_stats;
@@ -3496,11 +3514,14 @@ static int set_gbenu_ethss_priv(struct gbe_priv *gbe_dev,
 		return -ENOMEM;
 		return -ENOMEM;
 	}
 	}
 
 
-	ret = of_address_to_resource(node, GBENU_SM_REG_INDEX, &res);
+	if (IS_SS_ID_2U(gbe_dev))
+		sm_index = GBE_2U_SM_REG_INDEX;
+
+	ret = of_address_to_resource(node, sm_index, &res);
 	if (ret) {
 	if (ret) {
 		dev_err(gbe_dev->dev,
 		dev_err(gbe_dev->dev,
 			"Can't translate of gbenu node(%s) addr at index %d\n",
 			"Can't translate of gbenu node(%s) addr at index %d\n",
-			node->name, GBENU_SM_REG_INDEX);
+			node->name, sm_index);
 		return ret;
 		return ret;
 	}
 	}
 
 
@@ -3512,16 +3533,37 @@ static int set_gbenu_ethss_priv(struct gbe_priv *gbe_dev,
 	}
 	}
 	gbe_dev->switch_regs = regs;
 	gbe_dev->switch_regs = regs;
 
 
-	if (!IS_SS_ID_2U(gbe_dev))
-		gbe_dev->sgmii_port_regs =
-		       gbe_dev->ss_regs + GBENU_SGMII_MODULE_OFFSET;
+	if (!IS_SS_ID_2U(gbe_dev)) {
+		if (gbe_dev->ss_regs) {
+			gbe_dev->sgmii_port_regs =
+				gbe_dev->ss_regs + GBENU_SGMII_MODULE_OFFSET;
+		} else {
+			ret = of_address_to_resource(node,
+						     GBENU_SGMII_REG_INDEX,
+						     &res);
+			if (ret) {
+				dev_err(gbe_dev->dev,
+					"Can't xslate of node(%s) addr at %d\n",
+					node->name, GBENU_SGMII_REG_INDEX);
+				return ret;
+			}
+
+			regs = devm_ioremap_resource(gbe_dev->dev, &res);
+			if (IS_ERR(regs)) {
+				dev_err(gbe_dev->dev,
+					"Failed to map gbenu sgmii reg base\n");
+				return PTR_ERR(regs);
+			}
+			gbe_dev->sgmii_port_regs = regs;
+		}
 
 
-	/* Although sgmii modules are mem mapped to one contiguous
-	 * region on GBENU devices, setting sgmii_port34_regs allows
-	 * consistent code when accessing sgmii api
-	 */
-	gbe_dev->sgmii_port34_regs = gbe_dev->sgmii_port_regs +
-				     (2 * GBENU_SGMII_MODULE_SIZE);
+		/* Although sgmii modules are mem mapped to one contiguous
+		 * region on GBENU devices, setting sgmii_port34_regs allows
+		 * consistent code when accessing sgmii api
+		 */
+		gbe_dev->sgmii_port34_regs = gbe_dev->sgmii_port_regs +
+				     (2 * SGMII_MODULE_SIZE);
+	}
 
 
 	gbe_dev->host_port_regs = gbe_dev->switch_regs + GBENU_HOST_PORT_OFFSET;
 	gbe_dev->host_port_regs = gbe_dev->switch_regs + GBENU_HOST_PORT_OFFSET;
 
 
@@ -3565,6 +3607,7 @@ static int gbe_probe(struct netcp_device *netcp_device, struct device *dev,
 	struct device_node *secondary_ports;
 	struct device_node *secondary_ports;
 	struct cpsw_ale_params ale_params;
 	struct cpsw_ale_params ale_params;
 	struct gbe_priv *gbe_dev;
 	struct gbe_priv *gbe_dev;
+	struct phy *phy;
 	u32 slave_num;
 	u32 slave_num;
 	int i, ret = 0;
 	int i, ret = 0;
 
 
@@ -3638,10 +3681,6 @@ static int gbe_probe(struct netcp_device *netcp_device, struct device *dev,
 
 
 	} else if (!strcmp(node->name, "xgbe")) {
 	} else if (!strcmp(node->name, "xgbe")) {
 		ret = set_xgbe_ethss10_priv(gbe_dev, node);
 		ret = set_xgbe_ethss10_priv(gbe_dev, node);
-		if (ret)
-			return ret;
-		ret = netcp_xgbe_serdes_init(gbe_dev->xgbe_serdes_regs,
-					     gbe_dev->ss_regs);
 	} else {
 	} else {
 		dev_err(dev, "unknown GBE node(%s)\n", node->name);
 		dev_err(dev, "unknown GBE node(%s)\n", node->name);
 		ret = -ENODEV;
 		ret = -ENODEV;
@@ -3657,21 +3696,28 @@ static int gbe_probe(struct netcp_device *netcp_device, struct device *dev,
 	ret = netcp_txpipe_init(&gbe_dev->tx_pipe, netcp_device,
 	ret = netcp_txpipe_init(&gbe_dev->tx_pipe, netcp_device,
 				gbe_dev->dma_chan_name, gbe_dev->tx_queue_id);
 				gbe_dev->dma_chan_name, gbe_dev->tx_queue_id);
 	if (ret)
 	if (ret)
-		return ret;
+		goto exit_err;
 
 
 	ret = netcp_txpipe_open(&gbe_dev->tx_pipe);
 	ret = netcp_txpipe_open(&gbe_dev->tx_pipe);
 	if (ret)
 	if (ret)
-		return ret;
+		goto exit_err;
 
 
 	/* Create network interfaces */
 	/* Create network interfaces */
 	INIT_LIST_HEAD(&gbe_dev->gbe_intf_head);
 	INIT_LIST_HEAD(&gbe_dev->gbe_intf_head);
 	for_each_child_of_node(interfaces, interface) {
 	for_each_child_of_node(interfaces, interface) {
+		if (!IS_SS_ID_2U(gbe_dev)) {
+			phy = devm_of_phy_get_by_index(dev, interface, 0);
+			if (!IS_ERR(phy))
+				phy_init(phy);
+		}
+
 		ret = of_property_read_u32(interface, "slave-port", &slave_num);
 		ret = of_property_read_u32(interface, "slave-port", &slave_num);
 		if (ret) {
 		if (ret) {
 			dev_err(dev, "missing slave-port parameter, skipping interface configuration for %s\n",
 			dev_err(dev, "missing slave-port parameter, skipping interface configuration for %s\n",
 				interface->name);
 				interface->name);
 			continue;
 			continue;
 		}
 		}
+
 		gbe_dev->num_slaves++;
 		gbe_dev->num_slaves++;
 		if (gbe_dev->num_slaves >= gbe_dev->max_num_slaves) {
 		if (gbe_dev->num_slaves >= gbe_dev->max_num_slaves) {
 			of_node_put(interface);
 			of_node_put(interface);
@@ -3734,14 +3780,19 @@ static int gbe_probe(struct netcp_device *netcp_device, struct device *dev,
 	}
 	}
 	spin_unlock_bh(&gbe_dev->hw_stats_lock);
 	spin_unlock_bh(&gbe_dev->hw_stats_lock);
 
 
+	ret = gbe_create_sysfs_entries(gbe_dev);
+	if (ret)
+		goto free_sec_ports;
 	timer_setup(&gbe_dev->timer, netcp_ethss_timer, 0);
 	timer_setup(&gbe_dev->timer, netcp_ethss_timer, 0);
 	gbe_dev->timer.expires	 = jiffies + GBE_TIMER_INTERVAL;
 	gbe_dev->timer.expires	 = jiffies + GBE_TIMER_INTERVAL;
 	add_timer(&gbe_dev->timer);
 	add_timer(&gbe_dev->timer);
 	*inst_priv = gbe_dev;
 	*inst_priv = gbe_dev;
+	dev_dbg(dev, "probed");
 	return 0;
 	return 0;
 
 
 free_sec_ports:
 free_sec_ports:
 	free_secondary_ports(gbe_dev);
 	free_secondary_ports(gbe_dev);
+exit_err:
 	return ret;
 	return ret;
 }
 }
 
 
@@ -3778,6 +3829,14 @@ static int gbe_attach(void *inst_priv, struct net_device *ndev,
 		goto fail;
 		goto fail;
 	}
 	}
 
 
+	if (!IS_SS_ID_2U(gbe_dev)) {
+		ret = init_serdes_phys(gbe_dev, gbe_intf->slave, node, false);
+		if (ret && (ret != -ENODEV)) {
+			dev_err(gbe_dev->dev, "serdes phy init failed\n");
+			goto fail;
+		}
+	}
+
 	gbe_intf->tx_pipe = gbe_dev->tx_pipe;
 	gbe_intf->tx_pipe = gbe_dev->tx_pipe;
 	ndev->ethtool_ops = &keystone_ethtool_ops;
 	ndev->ethtool_ops = &keystone_ethtool_ops;
 	list_add_tail(&gbe_intf->gbe_intf_list, &gbe_dev->gbe_intf_head);
 	list_add_tail(&gbe_intf->gbe_intf_list, &gbe_dev->gbe_intf_head);
@@ -3796,6 +3855,7 @@ static int gbe_release(void *intf_priv)
 {
 {
 	struct gbe_intf *gbe_intf = intf_priv;
 	struct gbe_intf *gbe_intf = intf_priv;
 
 
+	phy_exit(gbe_intf->slave->serdes_phy);
 	gbe_intf->ndev->ethtool_ops = NULL;
 	gbe_intf->ndev->ethtool_ops = NULL;
 	list_del(&gbe_intf->gbe_intf_list);
 	list_del(&gbe_intf->gbe_intf_list);
 	devm_kfree(gbe_intf->dev, gbe_intf->slave);
 	devm_kfree(gbe_intf->dev, gbe_intf->slave);
@@ -3811,6 +3871,7 @@ static int gbe_remove(struct netcp_device *netcp_device, void *inst_priv)
 	cpts_release(gbe_dev->cpts);
 	cpts_release(gbe_dev->cpts);
 	cpsw_ale_stop(gbe_dev->ale);
 	cpsw_ale_stop(gbe_dev->ale);
 	netcp_txpipe_close(&gbe_dev->tx_pipe);
 	netcp_txpipe_close(&gbe_dev->tx_pipe);
+	gbe_remove_sysfs_entries(gbe_dev);
 	free_secondary_ports(gbe_dev);
 	free_secondary_ports(gbe_dev);
 
 
 	if (!list_empty(&gbe_dev->gbe_intf_head))
 	if (!list_empty(&gbe_dev->gbe_intf_head))

+ 240 - 0
drivers/net/ethernet/ti/netcp_ethss.h

@@ -0,0 +1,240 @@
+/*
+ * NetCP ethss header file
+ *
+ * Copyright (C) 2014 - 2019 Texas Instruments Incorporated
+ * Authors:	Sandeep Nair <sandeep_n@ti.com>
+ *		Sandeep Paulraj <s-paulraj@ti.com>
+ *		Cyril Chemparathy <cyril@ti.com>
+ *		Santosh Shilimkar <santosh.shilimkar@ti.com>
+ *		Wingman Kwok <w-kwok2@ti.com>
+ *
+ * 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.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __NETCP_ETHSS_H__
+#define __NETCP_ETHSS_H__
+
+#include <linux/device.h>
+#include <linux/netdevice.h>
+#include <linux/if_vlan.h>
+#include <linux/io.h>
+#include <linux/kobject.h>
+#include <linux/list.h>
+#include <linux/phy/phy.h>
+#include <linux/spinlock.h>
+#include <linux/regmap.h>
+#include <linux/timer.h>
+#include <linux/ethtool.h>
+
+#include "cpsw_ale.h"
+#include "netcp.h"
+
+#define MAX_SLAVES				8
+
+struct gbe_ss_regs_ofs {
+	u16	id_ver;
+	u16	control;
+	u16	rgmii_status; /* 2U */
+};
+
+struct gbe_switch_regs_ofs {
+	u16	id_ver;
+	u16	control;
+	u16	soft_reset;
+	u16	emcontrol;
+	u16	stat_port_en;
+	u16	ptype;
+	u16	flow_control;
+};
+
+struct gbe_port_regs_ofs {
+	u16	port_vlan;
+	u16	tx_pri_map;
+	u16	rx_pri_map;
+	u16	sa_lo;
+	u16	sa_hi;
+	u16	ts_ctl;
+	u16	ts_seq_ltype;
+	u16	ts_vlan;
+	u16	ts_ctl_ltype2;
+	u16	ts_ctl2;
+	u16	rx_maxlen;	/* 2U, NU */
+};
+
+struct gbe_host_port_regs_ofs {
+	u16	port_vlan;
+	u16	tx_pri_map;
+	u16	rx_maxlen;
+};
+
+struct gbe_emac_regs_ofs {
+	u16	mac_control;
+	u16	mac_status;
+	u16	soft_reset;
+	u16	rx_maxlen;
+};
+
+#define GBE_MAX_HW_STAT_MODS			9
+
+struct cpts;
+
+struct gbe_priv {
+	struct device			*dev;
+	struct netcp_device		*netcp_device;
+	struct timer_list		timer;
+	u32				num_slaves;
+	u32				ale_entries;
+	u32				ale_ports;
+	int				enable_ale;
+	u8				max_num_slaves;
+	u8				max_num_ports; /* max_num_slaves + 1 */
+	u8				num_stats_mods;
+	struct netcp_tx_pipe		tx_pipe;
+
+	int				host_port;
+	u32				rx_packet_max;
+	u32				ss_version;
+	u32				stats_en_mask;
+
+	struct regmap			*ss_regmap;
+	struct regmap			*pcsr_regmap;
+	void __iomem                    *ss_regs;
+	void __iomem			*switch_regs;
+	void __iomem			*host_port_regs;
+	void __iomem			*ale_reg;
+	void __iomem                    *cpts_reg;
+	void __iomem			*sgmii_port_regs;
+	void __iomem			*sgmii_port34_regs;
+	void __iomem			*hw_stats_regs[GBE_MAX_HW_STAT_MODS];
+
+	struct gbe_ss_regs_ofs		ss_regs_ofs;
+	struct gbe_switch_regs_ofs	switch_regs_ofs;
+	struct gbe_host_port_regs_ofs	host_port_regs_ofs;
+
+	struct cpsw_ale			*ale;
+	unsigned int			tx_queue_id;
+	const char			*dma_chan_name;
+
+	struct list_head		gbe_intf_head;
+	struct list_head		secondary_slaves;
+	struct net_device		*dummy_ndev;
+
+	u64				*hw_stats;
+	u32				*hw_stats_prev;
+	const struct netcp_ethtool_stat *et_stats;
+	int				num_et_stats;
+	/*  Lock for updating the hwstats */
+	spinlock_t			hw_stats_lock;
+	int                             cpts_registered;
+	struct cpts                     *cpts;
+	int				rx_ts_enabled;
+	int				tx_ts_enabled;
+
+	struct kobject			kobj;
+	struct kobject			tx_pri_kobj;
+	struct kobject			pvlan_kobj;
+	struct kobject			port_ts_kobj[MAX_SLAVES];
+	struct kobject			stats_kobj;
+};
+
+struct ts_ctl {
+	int     uni;
+	u8      dst_port_map;
+	u8      maddr_map;
+	u8      ts_mcast_type;
+};
+
+struct gbe_slave {
+	struct gbe_priv			*gbe_dev;
+	void __iomem			*port_regs;
+	void __iomem			*emac_regs;
+	struct gbe_port_regs_ofs	port_regs_ofs;
+	struct gbe_emac_regs_ofs	emac_regs_ofs;
+	int				slave_num; /* 0 based logical number */
+	int				port_num;  /* actual port number */
+	atomic_t			link_state;
+	int				open;
+	struct phy_device		*phy;
+	u32				link_interface;
+	u32				mac_control;
+	u8				phy_port_t;
+					/* work trigger threshold
+					 *   0: triger disabled
+					 * > 1: trigger enabled
+					 */
+	u32				link_recover_thresh;
+					/* 0:NOT, > 0:recovering */
+	u32				link_recovering;
+	struct delayed_work		link_recover_work;
+	struct device_node		*node;
+	struct device_node		*phy_node;
+	struct ts_ctl                   ts_ctl;
+	struct list_head		slave_list;
+	struct phy			*serdes_phy;
+};
+
+struct gbe_intf {
+	struct net_device	*ndev;
+	struct device		*dev;
+	struct gbe_priv		*gbe_dev;
+	struct netcp_tx_pipe	tx_pipe;
+	struct gbe_slave	*slave;
+	struct list_head	gbe_intf_list;
+	unsigned long		active_vlans[BITS_TO_LONGS(VLAN_N_VID)];
+};
+
+int gbe_create_sysfs_entries(struct gbe_priv *gbe_dev);
+void gbe_remove_sysfs_entries(struct gbe_priv *gbe_dev);
+void gbe_reset_mod_stats(struct gbe_priv *gbe_dev, int stats_mod);
+void gbe_reset_mod_stats_ver14(struct gbe_priv *gbe_dev, int stats_mod);
+
+#define for_each_intf(i, priv) \
+	list_for_each_entry((i), &(priv)->gbe_intf_head, gbe_intf_list)
+
+#define GBE_REG_ADDR(p, rb, rn) ((p)->rb + (p)->rb##_ofs.rn)
+#define GBE_MAJOR_VERSION(reg)		((reg) >> 8 & 0x7)
+#define GBE_MINOR_VERSION(reg)		((reg) & 0xff)
+#define GBE_RTL_VERSION(reg)		(((reg) >> 11) & 0x1f)
+#define GBE_IDENT(reg)			(((reg) >> 16) & 0xffff)
+#define GBE_SS_ID_NU			0x4ee6
+#define GBE_SS_ID_2U			0x4ee8
+#define GBE_SS_VERSION_14		0x4ed2
+#define XGBE_SS_VERSION_10		0x4ee4
+
+#define IS_SS_ID_MU(d) \
+	((GBE_IDENT((d)->ss_version) == GBE_SS_ID_NU) || \
+	 (GBE_IDENT((d)->ss_version) == GBE_SS_ID_2U))
+#define IS_SS_ID_NU(d) \
+	(GBE_IDENT((d)->ss_version) == GBE_SS_ID_NU)
+#define IS_SS_ID_VER_14(d) \
+	(GBE_IDENT((d)->ss_version) == GBE_SS_VERSION_14)
+#define IS_SS_ID_2U(d) \
+	(GBE_IDENT((d)->ss_version) == GBE_SS_ID_2U)
+
+#define GBE_STATSA_MODULE			0
+#define GBE_STATSB_MODULE			1
+#define GBE_STATSC_MODULE			2
+#define GBE_STATSD_MODULE			3
+
+#define GBENU_STATS0_MODULE			0
+#define GBENU_STATS1_MODULE			1
+#define GBENU_STATS2_MODULE			2
+#define GBENU_STATS3_MODULE			3
+#define GBENU_STATS4_MODULE			4
+#define GBENU_STATS5_MODULE			5
+#define GBENU_STATS6_MODULE			6
+#define GBENU_STATS7_MODULE			7
+#define GBENU_STATS8_MODULE			8
+
+#define XGBE_STATS0_MODULE			0
+#define XGBE_STATS1_MODULE			1
+#define XGBE_STATS2_MODULE			2
+
+#endif /* __NETCP_ETHSS_H */

+ 1403 - 0
drivers/net/ethernet/ti/netcp_ethss_sysfs.c

@@ -0,0 +1,1403 @@
+/*
+ * Keystone GBE and XGBE sysfs driver code
+ *
+ * Copyright (C) 2016-2019 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * Authors:	Sandeep Nair <sandeep_n@ti.com>
+ *		Sandeep Paulraj <s-paulraj@ti.com>
+ *		Cyril Chemparathy <cyril@ti.com>
+ *		Santosh Shilimkar <santosh.shilimkar@ti.com>
+ *		Wingman Kwok <w-kwok2@ti.com>
+ *
+ * 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.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include "netcp_ethss.h"
+
+#define to_gbe_dev(obj) container_of(obj, struct gbe_priv, kobj)
+#define tx_pri_to_gbe_dev(obj) container_of(obj, struct gbe_priv, tx_pri_kobj)
+#define pvlan_to_gbe_dev(obj) container_of(obj, struct gbe_priv, pvlan_kobj)
+#define stats_to_gbe_dev(obj) container_of(obj, struct gbe_priv, stats_kobj)
+#define gbe_sw_mod_info_field_val(r, i) \
+	(((r) & BITMASK((i)->bits, (i)->shift)) >> (i)->shift)
+
+#define __GBE_SW_ATTR_FULL(_name, _mode, _show, _store, _info,	\
+				_info_size, _ctxt)		\
+	{ \
+		.attr = {.name = __stringify(_name), .mode = _mode },	\
+		.show	= _show,		\
+		.store	= _store,		\
+		.info	= _info,		\
+		.info_size = _info_size,	\
+		.context = (_ctxt),		\
+	}
+
+#define __GBE_SW_ATTR(_name, _mode, _show, _store, _info) \
+		__GBE_SW_ATTR_FULL(_name, _mode, _show, _store, _info, \
+					(ARRAY_SIZE(_info)), NULL)
+
+#define __GBE_SW_CTXT_ATTR(_name, _mode, _show, _store, _info, _ctxt) \
+		__GBE_SW_ATTR_FULL(_name, _mode, _show, _store, _info, \
+					(ARRAY_SIZE(_info)), _ctxt)
+
+#define BITS(x)			(BIT(x) - 1)
+#define BITMASK(n, s)		(BITS(n) << (s))
+
+enum gbe_sysfs_sw_entry {
+	GBE_SYSFS_SW_CONTROL,
+	GBE_SYSFS_SW_TX_PRIO,
+	GBE_SYSFS_SW_VLAN,
+	GBE_SYSFS_SW_STATS,
+	GBE_SYSFS_SW_MAX
+};
+
+struct gbe_sw_mod_info {
+	const char	*name;
+	int		shift;
+	int		bits;
+};
+
+struct gbe_sw_parse_result {
+	int control;
+	int port;
+	u32 value;
+};
+
+struct gbe_attribute {
+	struct attribute attr;
+	ssize_t (*show)(struct gbe_priv *gbe_dev,
+			struct gbe_attribute *attr, char *buf);
+	ssize_t	(*store)(struct gbe_priv *gbe_dev,
+			 struct gbe_attribute *attr,
+			 const char *buf, size_t size);
+	const struct gbe_sw_mod_info *info;
+	ssize_t info_size;
+	void *context;
+};
+
+#define to_gbe_attr(_attr) container_of(_attr, struct gbe_attribute, attr)
+
+static struct gbe_slave *gbe_port_num_get_slave(struct gbe_priv *gbe_dev,
+						int port)
+{
+	struct gbe_intf *gbe_intf;
+
+	for_each_intf(gbe_intf, gbe_dev) {
+		if (gbe_intf->slave->port_num == port)
+			return gbe_intf->slave;
+	}
+	return NULL;
+}
+
+static ssize_t gbe_sw_version_show(struct gbe_priv *gbe_dev,
+				   struct gbe_attribute *attr, char *buf)
+{
+	u32 reg;
+
+	reg = readl(GBE_REG_ADDR(gbe_dev, switch_regs, id_ver));
+
+	return snprintf(buf, PAGE_SIZE,
+		"\nGBE Switch Version %d.%d (%d) Identification value 0x%x\n",
+		 GBE_MAJOR_VERSION(reg), GBE_MINOR_VERSION(reg),
+		 GBE_RTL_VERSION(reg), GBE_IDENT(reg));
+}
+
+static struct gbe_attribute gbe_sw_version_attribute =
+	      __ATTR(version, 0444, gbe_sw_version_show, NULL);
+
+static const struct gbe_sw_mod_info gbe_sw_ver14_controls[] = {
+	{
+		.name		= "fifo_loopback",
+		.shift		= 0,
+		.bits		= 1,
+	},
+	{
+		.name		= "vlan_aware",
+		.shift		= 1,
+		.bits		= 1,
+	},
+	{
+		.name		= "p0_enable",
+		.shift		= 2,
+		.bits		= 1,
+	},
+	{
+		.name		= "p0_pass_pri_tagged",
+		.shift		= 3,
+		.bits		= 1,
+	},
+	{
+		.name		= "p1_pass_pri_tagged",
+		.shift		= 4,
+		.bits		= 1,
+	},
+	{
+		.name		= "p2_pass_pri_tagged",
+		.shift		= 5,
+		.bits		= 1,
+	},
+	{
+		.name		= "p3_pass_pri_tagged",
+		.shift		= 7,
+		.bits		= 1,
+	},
+	{
+		.name		= "p4_pass_pri_tagged",
+		.shift		= 8,
+		.bits		= 1,
+	},
+};
+
+static const struct gbe_sw_mod_info gbe_sw_xge_controls[] = {
+	{
+		.name		= "fifo_loopback",
+		.shift		= 0,
+		.bits		= 1,
+	},
+	{
+		.name		= "vlan_aware",
+		.shift		= 1,
+		.bits		= 1,
+	},
+	{
+		.name		= "p0_enable",
+		.shift		= 2,
+		.bits		= 1,
+	},
+	{
+		.name		= "p0_pass_pri_tagged",
+		.shift		= 3,
+		.bits		= 1,
+	},
+	{
+		.name		= "p1_pass_pri_tagged",
+		.shift		= 4,
+		.bits		= 1,
+	},
+	{
+		.name		= "p2_pass_pri_tagged",
+		.shift		= 5,
+		.bits		= 1,
+	},
+	{
+		.name		= "p0_tx_crc_type",
+		.shift		= 12,
+		.bits		= 1,
+	},
+};
+
+static const struct gbe_sw_mod_info gbe_sw_nu_controls[] = {
+	{
+		.name		= "vlan_aware",
+		.shift		= 1,
+		.bits		= 1,
+	},
+	{
+		.name		= "p0_enable",
+		.shift		= 2,
+		.bits		= 1,
+	},
+	{
+		.name		= "p0_pass_pri_tagged",
+		.shift		= 3,
+		.bits		= 1,
+	},
+	{
+		.name		= "p1_pass_pri_tagged",
+		.shift		= 4,
+		.bits		= 1,
+	},
+	{
+		.name		= "p2_pass_pri_tagged",
+		.shift		= 5,
+		.bits		= 1,
+	},
+	{
+		.name		= "p3_pass_pri_tagged",
+		.shift		= 6,
+		.bits		= 1,
+	},
+	{
+		.name		= "p4_pass_pri_tagged",
+		.shift		= 7,
+		.bits		= 1,
+	},
+	{
+		.name		= "p5_pass_pri_tagged",
+		.shift		= 8,
+		.bits		= 1,
+	},
+	{
+		.name		= "p6_pass_pri_tagged",
+		.shift		= 9,
+		.bits		= 1,
+	},
+	{
+		.name		= "p7_pass_pri_tagged",
+		.shift		= 10,
+		.bits		= 1,
+	},
+	{
+		.name		= "p8_pass_pri_tagged",
+		.shift		= 11,
+		.bits		= 1,
+	},
+	{
+		.name		= "p0_tx_crc_type",
+		.shift		= 12,
+		.bits		= 1,
+	},
+	{
+		.name		= "p0_rx_pad",
+		.shift		= 14,
+		.bits		= 1,
+	},
+	{
+		.name		= "p0_rx_pass_crc_err",
+		.shift		= 15,
+		.bits		= 1,
+	},
+};
+
+static inline void
+gbe_sw_info_set_reg_field(void __iomem *reg, const struct gbe_sw_mod_info *info,
+			  int val)
+{
+	u32 rv;
+
+	rv = readl(reg);
+	rv = ((rv & ~BITMASK(info->bits, info->shift)) | (val << info->shift));
+	writel(rv, reg);
+}
+
+static ssize_t
+gbe_sw_attr_parse_set_command(struct gbe_priv *gbe_dev,
+			      struct gbe_attribute *attr,
+			      const char *buf, size_t count,
+			      struct gbe_sw_parse_result *res)
+{
+	char ctrl_str[33], tmp_str[9];
+	int port = -1, value, len, control;
+	unsigned long end;
+	const struct gbe_sw_mod_info *info = attr->info;
+
+	len = strcspn(buf, ".=");
+	if (len >= 32)
+		return -ENOMEM;
+
+	strncpy(ctrl_str, buf, len);
+	ctrl_str[len] = '\0';
+	buf += len;
+
+	if (*buf == '.') {
+		++buf;
+		len = strcspn(buf, "=");
+		if (len >= 8)
+			return -ENOMEM;
+		strncpy(tmp_str, buf, len);
+		tmp_str[len] = '\0';
+		if (kstrtoul(tmp_str, 0, &end))
+			return -EINVAL;
+		port = (int)end;
+		buf += len;
+	}
+
+	if (*buf != '=')
+		return -EINVAL;
+
+	if (kstrtoul(buf + 1, 0, &end))
+		return -EINVAL;
+
+	value = (int)end;
+
+	for (control = 0; control < attr->info_size; control++)
+		if (strcmp(ctrl_str, info[control].name) == 0)
+			break;
+
+	if (control >= attr->info_size)
+		return -ENOENT;
+
+	res->control = control;
+	res->port = port;
+	res->value = value;
+
+	dev_info(gbe_dev->dev, "parsed command %s.%d=%d\n",
+		 attr->info[control].name, port, value);
+
+	return 0;
+}
+
+static ssize_t
+gbe_sw_attr_info_show(const struct gbe_sw_mod_info *info, int info_size,
+		      u32 reg_val, char *buf)
+{
+	int i, len = 0;
+
+	for (i = 0; i < info_size; i++, info++) {
+		len += snprintf(buf + len, PAGE_SIZE - len,
+			"%s=%d\n", info->name,
+			(int)gbe_sw_mod_info_field_val(reg_val, info));
+	}
+
+	return len;
+}
+
+static ssize_t gbe_sw_control_show(struct gbe_priv *gbe_dev,
+				   struct gbe_attribute *attr, char *buf)
+{
+	u32 reg_val = readl(GBE_REG_ADDR(gbe_dev, switch_regs, control));
+
+	return gbe_sw_attr_info_show(attr->info, attr->info_size, reg_val, buf);
+}
+
+static ssize_t
+gbe_sw_control_store(struct gbe_priv *gbe_dev, struct gbe_attribute *attr,
+		     const char *buf, size_t count)
+{
+	const struct gbe_sw_mod_info *info;
+	struct gbe_sw_parse_result res;
+	int ret;
+
+	ret = gbe_sw_attr_parse_set_command(gbe_dev, attr, buf, count, &res);
+	if (ret)
+		return ret;
+
+	info = &attr->info[res.control];
+
+	gbe_sw_info_set_reg_field(GBE_REG_ADDR(gbe_dev, switch_regs, control),
+				  info, res.value);
+	return count;
+}
+
+static struct gbe_attribute gbe_sw_ver14_control_attribute =
+	      __GBE_SW_ATTR(control, 0644, gbe_sw_control_show,
+			    gbe_sw_control_store, gbe_sw_ver14_controls);
+
+static struct gbe_attribute gbe_sw_xge_control_attribute =
+	      __GBE_SW_ATTR(control, 0644, gbe_sw_control_show,
+			    gbe_sw_control_store, gbe_sw_xge_controls);
+
+static struct gbe_attribute gbe_sw_nu_control_attribute =
+	      __GBE_SW_ATTR(control, 0644, gbe_sw_control_show,
+			    gbe_sw_control_store, gbe_sw_nu_controls);
+
+static const struct gbe_sw_mod_info gbe_sw_ver14_ptypes[] = {
+	{
+		.name		= "escalate_pri_load_val",
+		.shift		= 0,
+		.bits		= 5,
+	},
+	{
+		.name		= "port0_pri_type_escalate",
+		.shift		= 8,
+		.bits		= 1,
+	},
+	{
+		.name		= "port1_pri_type_escalate",
+		.shift		= 9,
+		.bits		= 1,
+	},
+	{
+		.name		= "port2_pri_type_escalate",
+		.shift		= 10,
+		.bits		= 1,
+	},
+	{
+		.name		= "port3_pri_type_escalate",
+		.shift		= 11,
+		.bits		= 1,
+	},
+	{
+		.name		= "port4_pri_type_escalate",
+		.shift		= 12,
+		.bits		= 1,
+	},
+};
+
+static const struct gbe_sw_mod_info gbe_sw_xge_ptypes[] = {
+	{
+		.name		= "escalate_pri_load_val",
+		.shift		= 0,
+		.bits		= 5,
+	},
+	{
+		.name		= "port0_pri_type_escalate",
+		.shift		= 8,
+		.bits		= 1,
+	},
+	{
+		.name		= "port1_pri_type_escalate",
+		.shift		= 9,
+		.bits		= 1,
+	},
+	{
+		.name		= "port2_pri_type_escalate",
+		.shift		= 10,
+		.bits		= 1,
+	},
+};
+
+static const struct gbe_sw_mod_info gbe_sw_nu_ptypes[] = {
+	{
+		.name		= "escalate_pri_load_val",
+		.shift		= 0,
+		.bits		= 5,
+	},
+	{
+		.name		= "port0_pri_type_escalate",
+		.shift		= 8,
+		.bits		= 1,
+	},
+	{
+		.name		= "port1_pri_type_escalate",
+		.shift		= 9,
+		.bits		= 1,
+	},
+	{
+		.name		= "port2_pri_type_escalate",
+		.shift		= 10,
+		.bits		= 1,
+	},
+	{
+		.name		= "port3_pri_type_escalate",
+		.shift		= 11,
+		.bits		= 1,
+	},
+	{
+		.name		= "port4_pri_type_escalate",
+		.shift		= 12,
+		.bits		= 1,
+	},
+	{
+		.name		= "port5_pri_type_escalate",
+		.shift		= 13,
+		.bits		= 1,
+	},
+	{
+		.name		= "port6_pri_type_escalate",
+		.shift		= 14,
+		.bits		= 1,
+	},
+	{
+		.name		= "port7_pri_type_escalate",
+		.shift		= 15,
+		.bits		= 1,
+	},
+	{
+		.name		= "port8_pri_type_escalate",
+		.shift		= 16,
+		.bits		= 1,
+	},
+};
+
+static ssize_t gbe_sw_pri_type_show(struct gbe_priv *gbe_dev,
+				    struct gbe_attribute *attr, char *buf)
+{
+	u32 reg_val = readl(GBE_REG_ADDR(gbe_dev, switch_regs, ptype));
+
+	return gbe_sw_attr_info_show(attr->info, attr->info_size, reg_val, buf);
+}
+
+static ssize_t gbe_sw_pri_type_store(struct gbe_priv *gbe_dev,
+				     struct gbe_attribute *attr,
+				     const char *buf, size_t count)
+{
+	const struct gbe_sw_mod_info *info;
+	struct gbe_sw_parse_result res;
+	int ret;
+
+	ret = gbe_sw_attr_parse_set_command(gbe_dev, attr, buf, count, &res);
+	if (ret)
+		return ret;
+
+	info = &attr->info[res.control];
+
+	gbe_sw_info_set_reg_field(GBE_REG_ADDR(gbe_dev, switch_regs, ptype),
+				  info, res.value);
+	return count;
+}
+
+static struct gbe_attribute gbe_sw_ver14_pri_type_attribute =
+			__GBE_SW_ATTR(priority_type, 0644,
+				      gbe_sw_pri_type_show,
+				      gbe_sw_pri_type_store,
+				      gbe_sw_ver14_ptypes);
+
+static struct gbe_attribute gbe_sw_xge_pri_type_attribute =
+			__GBE_SW_ATTR(priority_type, 0644,
+				      gbe_sw_pri_type_show,
+				      gbe_sw_pri_type_store,
+				      gbe_sw_xge_ptypes);
+
+static struct gbe_attribute gbe_sw_nu_pri_type_attribute =
+			__GBE_SW_ATTR(priority_type, 0644,
+				      gbe_sw_pri_type_show,
+				      gbe_sw_pri_type_store,
+				      gbe_sw_nu_ptypes);
+
+static const struct gbe_sw_mod_info gbe_sw_ver14_flow_controls[] = {
+	{
+		.name		= "port0_flow_control_en",
+		.shift		= 0,
+		.bits		= 1,
+	},
+	{
+		.name		= "port1_flow_control_en",
+		.shift		= 1,
+		.bits		= 1,
+	},
+	{
+		.name		= "port2_flow_control_en",
+		.shift		= 2,
+		.bits		= 1,
+	},
+	{
+		.name		= "port3_flow_control_en",
+		.shift		= 3,
+		.bits		= 1,
+	},
+	{
+		.name		= "port4_flow_control_en",
+		.shift		= 4,
+		.bits		= 1,
+	},
+};
+
+static const struct gbe_sw_mod_info gbe_sw_xge_flow_controls[] = {
+	{
+		.name		= "port0_flow_control_en",
+		.shift		= 0,
+		.bits		= 1,
+	},
+	{
+		.name		= "port1_flow_control_en",
+		.shift		= 1,
+		.bits		= 1,
+	},
+	{
+		.name		= "port2_flow_control_en",
+		.shift		= 2,
+		.bits		= 1,
+	},
+};
+
+static ssize_t gbe_sw_flow_control_show(struct gbe_priv *gbe_dev,
+					struct gbe_attribute *attr, char *buf)
+{
+	u32 reg_val = readl(GBE_REG_ADDR(gbe_dev, switch_regs, flow_control));
+
+	return gbe_sw_attr_info_show(attr->info, attr->info_size, reg_val, buf);
+}
+
+static ssize_t gbe_sw_flow_control_store(struct gbe_priv *gbe_dev,
+					 struct gbe_attribute *attr,
+					 const char *buf, size_t count)
+{
+	const struct gbe_sw_mod_info *info;
+	struct gbe_sw_parse_result res;
+	int ret;
+
+	ret = gbe_sw_attr_parse_set_command(gbe_dev, attr, buf, count, &res);
+	if (ret)
+		return ret;
+
+	info = &attr->info[res.control];
+
+	gbe_sw_info_set_reg_field(GBE_REG_ADDR(gbe_dev, switch_regs,
+					       flow_control),
+					       info, res.value);
+	return count;
+}
+
+static struct gbe_attribute gbe_sw_ver14_flow_control_attribute =
+			__GBE_SW_ATTR(flow_control, 0644,
+				      gbe_sw_flow_control_show,
+				      gbe_sw_flow_control_store,
+				      gbe_sw_ver14_flow_controls);
+
+static struct gbe_attribute gbe_sw_xge_flow_control_attribute =
+			__GBE_SW_ATTR(flow_control, 0644,
+				      gbe_sw_flow_control_show,
+				      gbe_sw_flow_control_store,
+				      gbe_sw_xge_flow_controls);
+
+static struct attribute *gbe_sw_ver14_default_attrs[] = {
+	&gbe_sw_version_attribute.attr,
+	&gbe_sw_ver14_control_attribute.attr,
+	&gbe_sw_ver14_pri_type_attribute.attr,
+	&gbe_sw_ver14_flow_control_attribute.attr,
+	NULL
+};
+
+static struct attribute *gbe_sw_xge_default_attrs[] = {
+	&gbe_sw_version_attribute.attr,
+	&gbe_sw_xge_control_attribute.attr,
+	&gbe_sw_xge_pri_type_attribute.attr,
+	&gbe_sw_xge_flow_control_attribute.attr,
+	NULL
+};
+
+static struct attribute *gbe_sw_nu_default_attrs[] = {
+	&gbe_sw_version_attribute.attr,
+	&gbe_sw_nu_control_attribute.attr,
+	&gbe_sw_nu_pri_type_attribute.attr,
+	NULL
+};
+
+static const struct gbe_sw_mod_info gbe_sw_port_tx_pri_maps[] = {
+	{
+		.name		= "port_tx_pri_0",
+		.shift		= 0,
+		.bits		= 3,
+	},
+	{
+		.name		= "port_tx_pri_1",
+		.shift		= 4,
+		.bits		= 3,
+	},
+	{
+		.name		= "port_tx_pri_2",
+		.shift		= 8,
+		.bits		= 3,
+	},
+	{
+		.name		= "port_tx_pri_3",
+		.shift		= 12,
+		.bits		= 3,
+	},
+	{
+		.name		= "port_tx_pri_4",
+		.shift		= 16,
+		.bits		= 3,
+	},
+	{
+		.name		= "port_tx_pri_5",
+		.shift		= 20,
+		.bits		= 3,
+	},
+	{
+		.name		= "port_tx_pri_6",
+		.shift		= 24,
+		.bits		= 3,
+	},
+	{
+		.name		= "port_tx_pri_7",
+		.shift		= 28,
+		.bits		= 3,
+	},
+};
+
+static ssize_t gbe_sw_port_tx_pri_map_show(struct gbe_priv *gbe_dev,
+					   struct gbe_attribute *attr,
+					   char *buf)
+{
+	int len = 0, total_len = 0, port;
+	struct gbe_slave *slave;
+	u32 reg_val;
+
+	port = (int)(attr->context);
+
+	slave = gbe_port_num_get_slave(gbe_dev, port);
+	if (!slave)
+		return 0;
+
+	reg_val = readl(GBE_REG_ADDR(slave, port_regs, tx_pri_map));
+	len = gbe_sw_attr_info_show(attr->info, attr->info_size, reg_val, buf);
+
+	return (total_len += len);
+}
+
+static ssize_t gbe_sw_port_tx_pri_map_store(struct gbe_priv *gbe_dev,
+					    struct gbe_attribute *attr,
+					    const char *buf, size_t count)
+{
+	const struct gbe_sw_mod_info *info;
+	struct gbe_sw_parse_result res;
+	void __iomem *reg = NULL;
+	struct gbe_slave *slave;
+	int ret, port;
+
+	port = (int)(attr->context);
+
+	slave = gbe_port_num_get_slave(gbe_dev, port);
+	if (!slave)
+		return -EINVAL;
+
+	ret = gbe_sw_attr_parse_set_command(gbe_dev, attr, buf, count, &res);
+	if (ret)
+		return ret;
+
+	info = &attr->info[res.control];
+
+	reg = GBE_REG_ADDR(slave, port_regs, tx_pri_map);
+	if (!reg)
+		return  -ENOENT;
+
+	gbe_sw_info_set_reg_field(reg, info, res.value);
+
+	return count;
+}
+
+static struct gbe_attribute gbe_sw_tx_pri_0_attribute =
+			__GBE_SW_CTXT_ATTR(0, 0644,
+					   gbe_sw_port_tx_pri_map_show,
+					   gbe_sw_port_tx_pri_map_store,
+					   gbe_sw_port_tx_pri_maps, (void *)0);
+
+static struct gbe_attribute gbe_sw_tx_pri_1_attribute =
+			__GBE_SW_CTXT_ATTR(1, 0644,
+					   gbe_sw_port_tx_pri_map_show,
+					   gbe_sw_port_tx_pri_map_store,
+					   gbe_sw_port_tx_pri_maps, (void *)1);
+
+static struct gbe_attribute gbe_sw_tx_pri_2_attribute =
+			__GBE_SW_CTXT_ATTR(2, 0644,
+					   gbe_sw_port_tx_pri_map_show,
+					   gbe_sw_port_tx_pri_map_store,
+					   gbe_sw_port_tx_pri_maps, (void *)2);
+
+static struct gbe_attribute gbe_sw_tx_pri_3_attribute =
+			__GBE_SW_CTXT_ATTR(3, 0644,
+					   gbe_sw_port_tx_pri_map_show,
+					   gbe_sw_port_tx_pri_map_store,
+					   gbe_sw_port_tx_pri_maps, (void *)3);
+
+static struct gbe_attribute gbe_sw_tx_pri_4_attribute =
+			__GBE_SW_CTXT_ATTR(4, 0644,
+					   gbe_sw_port_tx_pri_map_show,
+					   gbe_sw_port_tx_pri_map_store,
+					   gbe_sw_port_tx_pri_maps, (void *)4);
+
+static struct gbe_attribute gbe_sw_tx_pri_5_attribute =
+			__GBE_SW_CTXT_ATTR(5, 0644,
+					   gbe_sw_port_tx_pri_map_show,
+					   gbe_sw_port_tx_pri_map_store,
+					   gbe_sw_port_tx_pri_maps, (void *)5);
+
+static struct gbe_attribute gbe_sw_tx_pri_6_attribute =
+			__GBE_SW_CTXT_ATTR(6, 0644,
+					   gbe_sw_port_tx_pri_map_show,
+					   gbe_sw_port_tx_pri_map_store,
+					   gbe_sw_port_tx_pri_maps, (void *)6);
+
+static struct gbe_attribute gbe_sw_tx_pri_7_attribute =
+			__GBE_SW_CTXT_ATTR(7, 0644,
+					   gbe_sw_port_tx_pri_map_show,
+					   gbe_sw_port_tx_pri_map_store,
+					   gbe_sw_port_tx_pri_maps, (void *)7);
+
+static struct gbe_attribute gbe_sw_tx_pri_8_attribute =
+			__GBE_SW_CTXT_ATTR(8, 0644,
+					   gbe_sw_port_tx_pri_map_show,
+					   gbe_sw_port_tx_pri_map_store,
+					   gbe_sw_port_tx_pri_maps, (void *)8);
+
+static struct attribute *gbe_sw_ver14_tx_pri_default_attrs[] = {
+	&gbe_sw_tx_pri_1_attribute.attr,
+	&gbe_sw_tx_pri_2_attribute.attr,
+	&gbe_sw_tx_pri_3_attribute.attr,
+	&gbe_sw_tx_pri_4_attribute.attr,
+	NULL
+};
+
+static struct attribute *gbe_sw_xge_tx_pri_default_attrs[] = {
+	&gbe_sw_tx_pri_0_attribute.attr,
+	&gbe_sw_tx_pri_1_attribute.attr,
+	&gbe_sw_tx_pri_2_attribute.attr,
+	NULL
+};
+
+static struct attribute *gbe_sw_nu_tx_pri_default_attrs[] = {
+	&gbe_sw_tx_pri_1_attribute.attr,
+	&gbe_sw_tx_pri_2_attribute.attr,
+	&gbe_sw_tx_pri_3_attribute.attr,
+	&gbe_sw_tx_pri_4_attribute.attr,
+	&gbe_sw_tx_pri_5_attribute.attr,
+	&gbe_sw_tx_pri_6_attribute.attr,
+	&gbe_sw_tx_pri_7_attribute.attr,
+	&gbe_sw_tx_pri_8_attribute.attr,
+	NULL
+};
+
+static ssize_t gbe_sw_tx_pri_attr_show(struct kobject *kobj,
+				       struct attribute *attr, char *buf)
+{
+	struct gbe_attribute *attribute = to_gbe_attr(attr);
+	struct gbe_priv *gbe_dev = tx_pri_to_gbe_dev(kobj);
+
+	if (!attribute->show)
+		return -EIO;
+
+	return attribute->show(gbe_dev, attribute, buf);
+}
+
+static ssize_t gbe_sw_tx_pri_attr_store(struct kobject *kobj,
+					struct attribute *attr,
+					const char *buf, size_t count)
+{
+	struct gbe_attribute *attribute = to_gbe_attr(attr);
+	struct gbe_priv *gbe_dev = tx_pri_to_gbe_dev(kobj);
+
+	if (!attribute->store)
+		return -EIO;
+
+	return attribute->store(gbe_dev, attribute, buf, count);
+}
+
+static const struct sysfs_ops gbe_sw_tx_pri_sysfs_ops = {
+	.show = gbe_sw_tx_pri_attr_show,
+	.store = gbe_sw_tx_pri_attr_store,
+};
+
+static struct kobj_type gbe_sw_ver14_tx_pri_ktype = {
+	.sysfs_ops = &gbe_sw_tx_pri_sysfs_ops,
+	.default_attrs = gbe_sw_ver14_tx_pri_default_attrs,
+};
+
+static struct kobj_type gbe_sw_xge_tx_pri_ktype = {
+	.sysfs_ops = &gbe_sw_tx_pri_sysfs_ops,
+	.default_attrs = gbe_sw_xge_tx_pri_default_attrs,
+};
+
+static struct kobj_type gbe_sw_nu_tx_pri_ktype = {
+	.sysfs_ops = &gbe_sw_tx_pri_sysfs_ops,
+	.default_attrs = gbe_sw_nu_tx_pri_default_attrs,
+};
+
+static const struct gbe_sw_mod_info gbe_sw_port_vlans[] = {
+	{
+		.name		= "port_vlan_id",
+		.shift		= 0,
+		.bits		= 12,
+	},
+	{
+		.name		= "port_cfi",
+		.shift		= 12,
+		.bits		= 1,
+	},
+	{
+		.name		= "port_vlan_pri",
+		.shift		= 13,
+		.bits		= 3,
+	},
+};
+
+static ssize_t gbe_sw_port_vlan_show(struct gbe_priv *gbe_dev,
+				     struct gbe_attribute *attr,
+				     char *buf)
+{
+	int len = 0, total_len = 0, port;
+	struct gbe_slave *slave;
+	u32 reg_val;
+
+	port = (int)(attr->context);
+
+	if (port == gbe_dev->host_port) {
+		/* Host port */
+		reg_val = readl(GBE_REG_ADDR(gbe_dev, host_port_regs,
+					     port_vlan));
+		len = gbe_sw_attr_info_show(attr->info, attr->info_size,
+					    reg_val, buf);
+		return len;
+	}
+
+	slave = gbe_port_num_get_slave(gbe_dev, port);
+	if (!slave)
+		return 0;
+
+	reg_val = readl(GBE_REG_ADDR(slave, port_regs, port_vlan));
+	len = gbe_sw_attr_info_show(attr->info, attr->info_size, reg_val, buf);
+
+	return (total_len += len);
+}
+
+static ssize_t gbe_sw_port_vlan_store(struct gbe_priv *gbe_dev,
+				      struct gbe_attribute *attr,
+				      const char *buf, size_t count)
+{
+	const struct gbe_sw_mod_info *info;
+	struct gbe_sw_parse_result res;
+	struct gbe_slave *slave;
+	void __iomem *reg = NULL;
+	int ret, port;
+
+	port = (int)(attr->context);
+
+	ret = gbe_sw_attr_parse_set_command(gbe_dev, attr, buf, count, &res);
+	if (ret)
+		return ret;
+
+	info = &attr->info[res.control];
+
+	/* Host port */
+	if (port == gbe_dev->host_port) {
+		reg = GBE_REG_ADDR(gbe_dev, host_port_regs, port_vlan);
+		goto set;
+	}
+
+	slave = gbe_port_num_get_slave(gbe_dev, port);
+	if (!slave)
+		return -EINVAL;
+
+	/* Slave port */
+	reg = GBE_REG_ADDR(slave, port_regs, port_vlan);
+	if (!reg)
+		return  -ENOENT;
+
+set:
+	gbe_sw_info_set_reg_field(reg, info, res.value);
+
+	return count;
+}
+
+static struct gbe_attribute gbe_sw_pvlan_0_attribute =
+	__GBE_SW_CTXT_ATTR(0, 0644,
+			   gbe_sw_port_vlan_show,
+			   gbe_sw_port_vlan_store,
+			   gbe_sw_port_vlans, (void *)0);
+
+static struct gbe_attribute gbe_sw_pvlan_1_attribute =
+	__GBE_SW_CTXT_ATTR(1, 0644,
+			   gbe_sw_port_vlan_show,
+			   gbe_sw_port_vlan_store,
+			   gbe_sw_port_vlans, (void *)1);
+
+static struct gbe_attribute gbe_sw_pvlan_2_attribute =
+	__GBE_SW_CTXT_ATTR(2, 0644,
+			   gbe_sw_port_vlan_show,
+			   gbe_sw_port_vlan_store,
+			   gbe_sw_port_vlans, (void *)2);
+
+static struct gbe_attribute gbe_sw_pvlan_3_attribute =
+	__GBE_SW_CTXT_ATTR(3, 0644,
+			   gbe_sw_port_vlan_show,
+			   gbe_sw_port_vlan_store,
+			   gbe_sw_port_vlans, (void *)3);
+
+static struct gbe_attribute gbe_sw_pvlan_4_attribute =
+	__GBE_SW_CTXT_ATTR(4, 0644,
+			   gbe_sw_port_vlan_show,
+			   gbe_sw_port_vlan_store,
+			   gbe_sw_port_vlans, (void *)4);
+
+static struct gbe_attribute gbe_sw_pvlan_5_attribute =
+	__GBE_SW_CTXT_ATTR(5, 0644,
+			   gbe_sw_port_vlan_show,
+			   gbe_sw_port_vlan_store,
+			   gbe_sw_port_vlans, (void *)5);
+
+static struct gbe_attribute gbe_sw_pvlan_6_attribute =
+	__GBE_SW_CTXT_ATTR(6, 0644,
+			   gbe_sw_port_vlan_show,
+			   gbe_sw_port_vlan_store,
+			   gbe_sw_port_vlans, (void *)6);
+
+static struct gbe_attribute gbe_sw_pvlan_7_attribute =
+	__GBE_SW_CTXT_ATTR(7, 0644,
+			   gbe_sw_port_vlan_show,
+			   gbe_sw_port_vlan_store,
+			   gbe_sw_port_vlans, (void *)7);
+
+static struct gbe_attribute gbe_sw_pvlan_8_attribute =
+	__GBE_SW_CTXT_ATTR(8, 0644,
+			   gbe_sw_port_vlan_show,
+			   gbe_sw_port_vlan_store,
+			   gbe_sw_port_vlans, (void *)8);
+
+static struct attribute *gbe_sw_ver14_pvlan_default_attrs[] = {
+	&gbe_sw_pvlan_0_attribute.attr,
+	&gbe_sw_pvlan_1_attribute.attr,
+	&gbe_sw_pvlan_2_attribute.attr,
+	&gbe_sw_pvlan_3_attribute.attr,
+	&gbe_sw_pvlan_4_attribute.attr,
+	NULL
+};
+
+static struct attribute *gbe_sw_xge_pvlan_default_attrs[] = {
+	&gbe_sw_pvlan_0_attribute.attr,
+	&gbe_sw_pvlan_1_attribute.attr,
+	&gbe_sw_pvlan_2_attribute.attr,
+	NULL
+};
+
+static struct attribute *gbe_sw_nu_pvlan_default_attrs[] = {
+	&gbe_sw_pvlan_0_attribute.attr,
+	&gbe_sw_pvlan_1_attribute.attr,
+	&gbe_sw_pvlan_2_attribute.attr,
+	&gbe_sw_pvlan_3_attribute.attr,
+	&gbe_sw_pvlan_4_attribute.attr,
+	&gbe_sw_pvlan_5_attribute.attr,
+	&gbe_sw_pvlan_6_attribute.attr,
+	&gbe_sw_pvlan_7_attribute.attr,
+	&gbe_sw_pvlan_8_attribute.attr,
+	NULL
+};
+
+static ssize_t gbe_sw_pvlan_attr_show(struct kobject *kobj,
+				      struct attribute *attr, char *buf)
+{
+	struct gbe_attribute *attribute = to_gbe_attr(attr);
+	struct gbe_priv *gbe_dev = pvlan_to_gbe_dev(kobj);
+
+	if (!attribute->show)
+		return -EIO;
+
+	return attribute->show(gbe_dev, attribute, buf);
+}
+
+static ssize_t gbe_sw_pvlan_attr_store(struct kobject *kobj,
+				       struct attribute *attr,
+				       const char *buf, size_t count)
+{
+	struct gbe_attribute *attribute = to_gbe_attr(attr);
+	struct gbe_priv *gbe_dev = pvlan_to_gbe_dev(kobj);
+
+	if (!attribute->store)
+		return -EIO;
+
+	return attribute->store(gbe_dev, attribute, buf, count);
+}
+
+static const struct sysfs_ops gbe_sw_pvlan_sysfs_ops = {
+	.show = gbe_sw_pvlan_attr_show,
+	.store = gbe_sw_pvlan_attr_store,
+};
+
+static struct kobj_type gbe_sw_ver14_pvlan_ktype = {
+	.sysfs_ops = &gbe_sw_pvlan_sysfs_ops,
+	.default_attrs = gbe_sw_ver14_pvlan_default_attrs,
+};
+
+static struct kobj_type gbe_sw_xge_pvlan_ktype = {
+	.sysfs_ops = &gbe_sw_pvlan_sysfs_ops,
+	.default_attrs = gbe_sw_xge_pvlan_default_attrs,
+};
+
+static struct kobj_type gbe_sw_nu_pvlan_ktype = {
+	.sysfs_ops = &gbe_sw_pvlan_sysfs_ops,
+	.default_attrs = gbe_sw_nu_pvlan_default_attrs,
+};
+
+static ssize_t gbe_sw_stats_attr_store(struct kobject *kobj,
+				       struct attribute *attr,
+				       const char *buf, size_t count)
+{
+	struct gbe_attribute *attribute = to_gbe_attr(attr);
+	struct gbe_priv *gbe_dev = stats_to_gbe_dev(kobj);
+
+	if (!attribute->store)
+		return -EIO;
+
+	return attribute->store(gbe_dev, attribute, buf, count);
+}
+
+static const struct sysfs_ops gbe_sw_stats_sysfs_ops = {
+	.store = gbe_sw_stats_attr_store,
+};
+
+static ssize_t gbe_sw_stats_mod_store(struct gbe_priv *gbe_dev,
+				      struct gbe_attribute *attr,
+				      const char *buf, size_t count)
+{
+	int stat_mod, max_ports;
+	unsigned long end;
+
+	if (kstrtoul(buf, 0, &end) != 0 || end != 0)
+		return -EINVAL;
+
+	stat_mod = (int)(attr->context);
+
+	/* We have stats blocks for only slave ports on GBE_SS_VERSION_14
+	 * but also for host port for other variations. So check this
+	 * value accordingly
+	 */
+	max_ports = (gbe_dev->ss_version == GBE_SS_VERSION_14) ?
+		     gbe_dev->max_num_slaves : gbe_dev->max_num_ports;
+
+	if (stat_mod >= max_ports)
+		return -EINVAL;
+
+	spin_lock_bh(&gbe_dev->hw_stats_lock);
+	if (gbe_dev->ss_version == GBE_SS_VERSION_14)
+		gbe_reset_mod_stats_ver14(gbe_dev, stat_mod);
+	else
+		gbe_reset_mod_stats(gbe_dev, stat_mod);
+	spin_unlock_bh(&gbe_dev->hw_stats_lock);
+	return count;
+}
+
+static struct gbe_attribute gbe_sw_stats_a_attribute =
+			__GBE_SW_ATTR_FULL(A, 0200, NULL,
+					   gbe_sw_stats_mod_store,
+					   NULL, 0, (void *)GBE_STATSA_MODULE);
+
+static struct gbe_attribute gbe_sw_stats_b_attribute =
+			__GBE_SW_ATTR_FULL(B, 0200, NULL,
+					   gbe_sw_stats_mod_store,
+					   NULL, 0, (void *)GBE_STATSB_MODULE);
+
+static struct gbe_attribute gbe_sw_stats_c_attribute =
+			__GBE_SW_ATTR_FULL(C, 0200, NULL,
+					   gbe_sw_stats_mod_store,
+					   NULL, 0, (void *)GBE_STATSC_MODULE);
+
+static struct gbe_attribute gbe_sw_stats_d_attribute =
+			__GBE_SW_ATTR_FULL(D, 0200, NULL,
+					   gbe_sw_stats_mod_store,
+					   NULL, 0, (void *)GBE_STATSD_MODULE);
+
+static struct attribute *gbe_sw_ver14_stats_default_attrs[] = {
+	&gbe_sw_stats_a_attribute.attr,
+	&gbe_sw_stats_b_attribute.attr,
+	&gbe_sw_stats_c_attribute.attr,
+	&gbe_sw_stats_d_attribute.attr,
+	NULL
+};
+
+static struct kobj_type gbe_sw_ver14_stats_ktype = {
+	.sysfs_ops = &gbe_sw_stats_sysfs_ops,
+	.default_attrs = gbe_sw_ver14_stats_default_attrs,
+};
+
+static struct gbe_attribute gbe_sw_xge_stats_0_attribute =
+			__GBE_SW_ATTR_FULL(0, 0200, NULL,
+					   gbe_sw_stats_mod_store,
+					   NULL, 0, (void *)XGBE_STATS0_MODULE);
+
+static struct gbe_attribute gbe_sw_xge_stats_1_attribute =
+			__GBE_SW_ATTR_FULL(1, 0200, NULL,
+					   gbe_sw_stats_mod_store,
+					   NULL, 0, (void *)XGBE_STATS1_MODULE);
+
+static struct gbe_attribute gbe_sw_xge_stats_2_attribute =
+			__GBE_SW_ATTR_FULL(2, 0200, NULL,
+					   gbe_sw_stats_mod_store,
+					   NULL, 0, (void *)XGBE_STATS2_MODULE);
+
+static struct attribute *gbe_sw_xge_stats_default_attrs[] = {
+	&gbe_sw_xge_stats_0_attribute.attr,
+	&gbe_sw_xge_stats_1_attribute.attr,
+	&gbe_sw_xge_stats_2_attribute.attr,
+	NULL
+};
+
+static struct kobj_type gbe_sw_xge_stats_ktype = {
+	.sysfs_ops = &gbe_sw_stats_sysfs_ops,
+	.default_attrs = gbe_sw_xge_stats_default_attrs,
+};
+
+static struct gbe_attribute gbe_sw_stats_0_attribute =
+			__GBE_SW_ATTR_FULL(0, 0200, NULL,
+					   gbe_sw_stats_mod_store,
+					   NULL, 0,
+					   (void *)GBENU_STATS0_MODULE);
+
+static struct gbe_attribute gbe_sw_stats_1_attribute =
+			__GBE_SW_ATTR_FULL(1, 0200, NULL,
+					   gbe_sw_stats_mod_store,
+					   NULL, 0,
+					   (void *)GBENU_STATS1_MODULE);
+
+static struct gbe_attribute gbe_sw_stats_2_attribute =
+			__GBE_SW_ATTR_FULL(2, 0200, NULL,
+					   gbe_sw_stats_mod_store,
+					   NULL, 0,
+					   (void *)GBENU_STATS2_MODULE);
+
+static struct gbe_attribute gbe_sw_stats_3_attribute =
+			__GBE_SW_ATTR_FULL(3, 0200, NULL,
+					   gbe_sw_stats_mod_store,
+					   NULL, 0,
+					   (void *)GBENU_STATS3_MODULE);
+
+static struct gbe_attribute gbe_sw_stats_4_attribute =
+			__GBE_SW_ATTR_FULL(4, 0200, NULL,
+					   gbe_sw_stats_mod_store,
+					   NULL, 0,
+					   (void *)GBENU_STATS4_MODULE);
+
+static struct gbe_attribute gbe_sw_stats_5_attribute =
+			__GBE_SW_ATTR_FULL(5, 0200, NULL,
+					   gbe_sw_stats_mod_store,
+					   NULL, 0,
+					   (void *)GBENU_STATS5_MODULE);
+
+static struct gbe_attribute gbe_sw_stats_6_attribute =
+			__GBE_SW_ATTR_FULL(6, 0200, NULL,
+					   gbe_sw_stats_mod_store,
+					   NULL, 0,
+					   (void *)GBENU_STATS6_MODULE);
+
+static struct gbe_attribute gbe_sw_stats_7_attribute =
+			__GBE_SW_ATTR_FULL(7, 0200, NULL,
+					   gbe_sw_stats_mod_store,
+					   NULL, 0,
+					   (void *)GBENU_STATS7_MODULE);
+
+static struct gbe_attribute gbe_sw_stats_8_attribute =
+			__GBE_SW_ATTR_FULL(8, 0200, NULL,
+					   gbe_sw_stats_mod_store,
+					   NULL, 0,
+					   (void *)GBENU_STATS8_MODULE);
+
+static struct attribute *gbe_sw_nu_stats_default_attrs[] = {
+	&gbe_sw_stats_0_attribute.attr,
+	&gbe_sw_stats_1_attribute.attr,
+	&gbe_sw_stats_2_attribute.attr,
+	&gbe_sw_stats_3_attribute.attr,
+	&gbe_sw_stats_4_attribute.attr,
+	&gbe_sw_stats_5_attribute.attr,
+	&gbe_sw_stats_6_attribute.attr,
+	&gbe_sw_stats_7_attribute.attr,
+	&gbe_sw_stats_8_attribute.attr,
+	NULL
+};
+
+static struct kobj_type gbe_sw_nu_stats_ktype = {
+	.sysfs_ops = &gbe_sw_stats_sysfs_ops,
+	.default_attrs = gbe_sw_nu_stats_default_attrs,
+};
+
+static ssize_t gbe_sw_attr_show(struct kobject *kobj,
+				struct attribute *attr, char *buf)
+{
+	struct gbe_attribute *attribute = to_gbe_attr(attr);
+	struct gbe_priv *gbe_dev = to_gbe_dev(kobj);
+
+	if (!attribute->show)
+		return -EIO;
+
+	return attribute->show(gbe_dev, attribute, buf);
+}
+
+static ssize_t gbe_sw_attr_store(struct kobject *kobj,
+				 struct attribute *attr, const char *buf,
+				 size_t count)
+{
+	struct gbe_attribute *attribute = to_gbe_attr(attr);
+	struct gbe_priv *gbe_dev = to_gbe_dev(kobj);
+
+	if (!attribute->store)
+		return -EIO;
+
+	return attribute->store(gbe_dev, attribute, buf, count);
+}
+
+static const struct sysfs_ops gbe_sw_sysfs_ops = {
+	.show = gbe_sw_attr_show,
+	.store = gbe_sw_attr_store,
+};
+
+static struct kobj_type gbe_sw_ver14_ktype = {
+	.sysfs_ops = &gbe_sw_sysfs_ops,
+	.default_attrs = gbe_sw_ver14_default_attrs,
+};
+
+static struct kobj_type gbe_sw_xge_ktype = {
+	.sysfs_ops = &gbe_sw_sysfs_ops,
+	.default_attrs = gbe_sw_xge_default_attrs,
+};
+
+static struct kobj_type gbe_sw_nu_ktype = {
+	.sysfs_ops = &gbe_sw_sysfs_ops,
+	.default_attrs = gbe_sw_nu_default_attrs,
+};
+
+/* for ver14 switch */
+static struct kobj_type *gbe_sw_ver14_kobjs[GBE_SYSFS_SW_MAX] = {
+	&gbe_sw_ver14_ktype,
+	&gbe_sw_ver14_tx_pri_ktype,
+	&gbe_sw_ver14_pvlan_ktype,
+	&gbe_sw_ver14_stats_ktype,
+};
+
+/* for xge switch */
+static struct kobj_type *gbe_sw_xge_kobjs[GBE_SYSFS_SW_MAX] = {
+	&gbe_sw_xge_ktype,
+	&gbe_sw_xge_tx_pri_ktype,
+	&gbe_sw_xge_pvlan_ktype,
+	&gbe_sw_xge_stats_ktype,
+};
+
+/* for NU switch */
+static struct kobj_type *gbe_sw_nu_kobjs[GBE_SYSFS_SW_MAX] = {
+	&gbe_sw_nu_ktype,
+	&gbe_sw_nu_tx_pri_ktype,
+	&gbe_sw_nu_pvlan_ktype,
+	&gbe_sw_nu_stats_ktype,
+};
+
+int gbe_create_sysfs_entries(struct gbe_priv *gbe_dev)
+{
+	struct device *dev = gbe_dev->dev;
+	static struct kobj_type **kobjs;
+	int ret;
+
+	switch (gbe_dev->ss_version) {
+	case XGBE_SS_VERSION_10:
+		kobjs = &gbe_sw_xge_kobjs[0];
+		break;
+	case GBE_SS_VERSION_14:
+		kobjs = &gbe_sw_ver14_kobjs[0];
+		break;
+	default:
+		kobjs = &gbe_sw_nu_kobjs[0];
+	}
+
+	ret = kobject_init_and_add(&gbe_dev->kobj, kobjs[GBE_SYSFS_SW_CONTROL],
+				   &dev->kobj, "gbe_sw");
+	if (ret) {
+		dev_err(dev, "failed to create gbe sw sysfs entry\n");
+		return ret;
+	}
+
+	ret = kobject_init_and_add(&gbe_dev->tx_pri_kobj,
+				   kobjs[GBE_SYSFS_SW_TX_PRIO],
+				   &gbe_dev->kobj,
+				   "port_tx_pri_map");
+	if (ret) {
+		dev_err(dev, "failed to create sysfs port_tx_pri_map entry\n");
+		goto clean_tx_pri_kobj;
+	}
+
+	ret = kobject_init_and_add(&gbe_dev->pvlan_kobj,
+				   kobjs[GBE_SYSFS_SW_VLAN],
+				   &gbe_dev->kobj, "port_vlan");
+	if (ret) {
+		dev_err(dev, "failed to create sysfs port_vlan entry\n");
+		goto clean_pvlan_kobj;
+	}
+
+	ret = kobject_init_and_add(&gbe_dev->stats_kobj,
+				   kobjs[GBE_SYSFS_SW_STATS],
+				   &gbe_dev->kobj, "stats");
+	if (ret) {
+		dev_err(dev, "failed to create sysfs stats entry\n");
+		goto clean_stats_kobj;
+	}
+
+	return ret;
+
+clean_stats_kobj:
+	kobject_put(&gbe_dev->pvlan_kobj);
+clean_pvlan_kobj:
+	kobject_put(&gbe_dev->tx_pri_kobj);
+clean_tx_pri_kobj:
+	kobject_put(&gbe_dev->kobj);
+	return ret;
+}
+
+void gbe_remove_sysfs_entries(struct gbe_priv *gbe_dev)
+{
+	kobject_put(&gbe_dev->stats_kobj);
+	kobject_put(&gbe_dev->pvlan_kobj);
+	kobject_put(&gbe_dev->tx_pri_kobj);
+	kobject_put(&gbe_dev->kobj);
+}

+ 4 - 3
drivers/net/ethernet/ti/netcp_sgmii.c

@@ -83,13 +83,14 @@ bool netcp_sgmii_rtreset(void __iomem *sgmii_ofs, int port, bool set)
 	return oldval;
 	return oldval;
 }
 }
 
 
-int netcp_sgmii_get_port_link(void __iomem *sgmii_ofs, int port)
+bool netcp_sgmii_get_port_link(void __iomem *sgmii_ofs, int port)
 {
 {
-	u32 status = 0, link = 0;
+	u32 status = 0;
+	bool link = false;
 
 
 	status = sgmii_read_reg(sgmii_ofs, SGMII_STATUS_REG(port));
 	status = sgmii_read_reg(sgmii_ofs, SGMII_STATUS_REG(port));
 	if ((status & SGMII_REG_STATUS_LINK) != 0)
 	if ((status & SGMII_REG_STATUS_LINK) != 0)
-		link = 1;
+		link = true;
 	return link;
 	return link;
 }
 }
 
 

+ 0 - 501
drivers/net/ethernet/ti/netcp_xgbepcsr.c

@@ -1,501 +0,0 @@
-/*
- * XGE PCSR module initialisation
- *
- * Copyright (C) 2014 Texas Instruments Incorporated
- * Authors:	Sandeep Nair <sandeep_n@ti.com>
- *		WingMan Kwok <w-kwok2@ti.com>
- *
- * 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.
- *
- * This program is distributed "as is" WITHOUT ANY WARRANTY of any
- * kind, whether express or implied; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-#include "netcp.h"
-
-/* XGBE registers */
-#define XGBE_CTRL_OFFSET		0x0c
-#define XGBE_SGMII_1_OFFSET		0x0114
-#define XGBE_SGMII_2_OFFSET		0x0214
-
-/* PCS-R registers */
-#define PCSR_CPU_CTRL_OFFSET		0x1fd0
-#define POR_EN				BIT(29)
-
-#define reg_rmw(addr, value, mask) \
-	writel(((readl(addr) & (~(mask))) | \
-			(value & (mask))), (addr))
-
-/* bit mask of width w at offset s */
-#define MASK_WID_SH(w, s)		(((1 << w) - 1) << s)
-
-/* shift value v to offset s */
-#define VAL_SH(v, s)			(v << s)
-
-#define PHY_A(serdes)			0
-
-struct serdes_cfg {
-	u32 ofs;
-	u32 val;
-	u32 mask;
-};
-
-static struct serdes_cfg cfg_phyb_1p25g_156p25mhz_cmu0[] = {
-	{0x0000, 0x00800002, 0x00ff00ff},
-	{0x0014, 0x00003838, 0x0000ffff},
-	{0x0060, 0x1c44e438, 0xffffffff},
-	{0x0064, 0x00c18400, 0x00ffffff},
-	{0x0068, 0x17078200, 0xffffff00},
-	{0x006c, 0x00000014, 0x000000ff},
-	{0x0078, 0x0000c000, 0x0000ff00},
-	{0x0000, 0x00000003, 0x000000ff},
-};
-
-static struct serdes_cfg cfg_phyb_10p3125g_156p25mhz_cmu1[] = {
-	{0x0c00, 0x00030002, 0x00ff00ff},
-	{0x0c14, 0x00005252, 0x0000ffff},
-	{0x0c28, 0x80000000, 0xff000000},
-	{0x0c2c, 0x000000f6, 0x000000ff},
-	{0x0c3c, 0x04000405, 0xff00ffff},
-	{0x0c40, 0xc0800000, 0xffff0000},
-	{0x0c44, 0x5a202062, 0xffffffff},
-	{0x0c48, 0x40040424, 0xffffffff},
-	{0x0c4c, 0x00004002, 0x0000ffff},
-	{0x0c50, 0x19001c00, 0xff00ff00},
-	{0x0c54, 0x00002100, 0x0000ff00},
-	{0x0c58, 0x00000060, 0x000000ff},
-	{0x0c60, 0x80131e7c, 0xffffffff},
-	{0x0c64, 0x8400cb02, 0xff00ffff},
-	{0x0c68, 0x17078200, 0xffffff00},
-	{0x0c6c, 0x00000016, 0x000000ff},
-	{0x0c74, 0x00000400, 0x0000ff00},
-	{0x0c78, 0x0000c000, 0x0000ff00},
-	{0x0c00, 0x00000003, 0x000000ff},
-};
-
-static struct serdes_cfg cfg_phyb_10p3125g_16bit_lane[] = {
-	{0x0204, 0x00000080, 0x000000ff},
-	{0x0208, 0x0000920d, 0x0000ffff},
-	{0x0204, 0xfc000000, 0xff000000},
-	{0x0208, 0x00009104, 0x0000ffff},
-	{0x0210, 0x1a000000, 0xff000000},
-	{0x0214, 0x00006b58, 0x00ffffff},
-	{0x0218, 0x75800084, 0xffff00ff},
-	{0x022c, 0x00300000, 0x00ff0000},
-	{0x0230, 0x00003800, 0x0000ff00},
-	{0x024c, 0x008f0000, 0x00ff0000},
-	{0x0250, 0x30000000, 0xff000000},
-	{0x0260, 0x00000002, 0x000000ff},
-	{0x0264, 0x00000057, 0x000000ff},
-	{0x0268, 0x00575700, 0x00ffff00},
-	{0x0278, 0xff000000, 0xff000000},
-	{0x0280, 0x00500050, 0x00ff00ff},
-	{0x0284, 0x00001f15, 0x0000ffff},
-	{0x028c, 0x00006f00, 0x0000ff00},
-	{0x0294, 0x00000000, 0xffffff00},
-	{0x0298, 0x00002640, 0xff00ffff},
-	{0x029c, 0x00000003, 0x000000ff},
-	{0x02a4, 0x00000f13, 0x0000ffff},
-	{0x02a8, 0x0001b600, 0x00ffff00},
-	{0x0380, 0x00000030, 0x000000ff},
-	{0x03c0, 0x00000200, 0x0000ff00},
-	{0x03cc, 0x00000018, 0x000000ff},
-	{0x03cc, 0x00000000, 0x000000ff},
-};
-
-static struct serdes_cfg cfg_phyb_10p3125g_comlane[] = {
-	{0x0a00, 0x00000800, 0x0000ff00},
-	{0x0a84, 0x00000000, 0x000000ff},
-	{0x0a8c, 0x00130000, 0x00ff0000},
-	{0x0a90, 0x77a00000, 0xffff0000},
-	{0x0a94, 0x00007777, 0x0000ffff},
-	{0x0b08, 0x000f0000, 0xffff0000},
-	{0x0b0c, 0x000f0000, 0x00ffffff},
-	{0x0b10, 0xbe000000, 0xff000000},
-	{0x0b14, 0x000000ff, 0x000000ff},
-	{0x0b18, 0x00000014, 0x000000ff},
-	{0x0b5c, 0x981b0000, 0xffff0000},
-	{0x0b64, 0x00001100, 0x0000ff00},
-	{0x0b78, 0x00000c00, 0x0000ff00},
-	{0x0abc, 0xff000000, 0xff000000},
-	{0x0ac0, 0x0000008b, 0x000000ff},
-};
-
-static struct serdes_cfg cfg_cm_c1_c2[] = {
-	{0x0208, 0x00000000, 0x00000f00},
-	{0x0208, 0x00000000, 0x0000001f},
-	{0x0204, 0x00000000, 0x00040000},
-	{0x0208, 0x000000a0, 0x000000e0},
-};
-
-static void netcp_xgbe_serdes_cmu_init(void __iomem *serdes_regs)
-{
-	int i;
-
-	/* cmu0 setup */
-	for (i = 0; i < ARRAY_SIZE(cfg_phyb_1p25g_156p25mhz_cmu0); i++) {
-		reg_rmw(serdes_regs + cfg_phyb_1p25g_156p25mhz_cmu0[i].ofs,
-			cfg_phyb_1p25g_156p25mhz_cmu0[i].val,
-			cfg_phyb_1p25g_156p25mhz_cmu0[i].mask);
-	}
-
-	/* cmu1 setup */
-	for (i = 0; i < ARRAY_SIZE(cfg_phyb_10p3125g_156p25mhz_cmu1); i++) {
-		reg_rmw(serdes_regs + cfg_phyb_10p3125g_156p25mhz_cmu1[i].ofs,
-			cfg_phyb_10p3125g_156p25mhz_cmu1[i].val,
-			cfg_phyb_10p3125g_156p25mhz_cmu1[i].mask);
-	}
-}
-
-/* lane is 0 based */
-static void netcp_xgbe_serdes_lane_config(
-			void __iomem *serdes_regs, int lane)
-{
-	int i;
-
-	/* lane setup */
-	for (i = 0; i < ARRAY_SIZE(cfg_phyb_10p3125g_16bit_lane); i++) {
-		reg_rmw(serdes_regs +
-				cfg_phyb_10p3125g_16bit_lane[i].ofs +
-				(0x200 * lane),
-			cfg_phyb_10p3125g_16bit_lane[i].val,
-			cfg_phyb_10p3125g_16bit_lane[i].mask);
-	}
-
-	/* disable auto negotiation*/
-	reg_rmw(serdes_regs + (0x200 * lane) + 0x0380,
-		0x00000000, 0x00000010);
-
-	/* disable link training */
-	reg_rmw(serdes_regs + (0x200 * lane) + 0x03c0,
-		0x00000000, 0x00000200);
-}
-
-static void netcp_xgbe_serdes_com_enable(void __iomem *serdes_regs)
-{
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(cfg_phyb_10p3125g_comlane); i++) {
-		reg_rmw(serdes_regs + cfg_phyb_10p3125g_comlane[i].ofs,
-			cfg_phyb_10p3125g_comlane[i].val,
-			cfg_phyb_10p3125g_comlane[i].mask);
-	}
-}
-
-static void netcp_xgbe_serdes_lane_enable(
-			void __iomem *serdes_regs, int lane)
-{
-	/* Set Lane Control Rate */
-	writel(0xe0e9e038, serdes_regs + 0x1fe0 + (4 * lane));
-}
-
-static void netcp_xgbe_serdes_phyb_rst_clr(void __iomem *serdes_regs)
-{
-	reg_rmw(serdes_regs + 0x0a00, 0x0000001f, 0x000000ff);
-}
-
-static void netcp_xgbe_serdes_pll_disable(void __iomem *serdes_regs)
-{
-	writel(0x88000000, serdes_regs + 0x1ff4);
-}
-
-static void netcp_xgbe_serdes_pll_enable(void __iomem *serdes_regs)
-{
-	netcp_xgbe_serdes_phyb_rst_clr(serdes_regs);
-	writel(0xee000000, serdes_regs + 0x1ff4);
-}
-
-static int netcp_xgbe_wait_pll_locked(void __iomem *sw_regs)
-{
-	unsigned long timeout;
-	int ret = 0;
-	u32 val_1, val_0;
-
-	timeout = jiffies + msecs_to_jiffies(500);
-	do {
-		val_0 = (readl(sw_regs + XGBE_SGMII_1_OFFSET) & BIT(4));
-		val_1 = (readl(sw_regs + XGBE_SGMII_2_OFFSET) & BIT(4));
-
-		if (val_1 && val_0)
-			return 0;
-
-		if (time_after(jiffies, timeout)) {
-			ret = -ETIMEDOUT;
-			break;
-		}
-
-		cpu_relax();
-	} while (true);
-
-	pr_err("XGBE serdes not locked: time out.\n");
-	return ret;
-}
-
-static void netcp_xgbe_serdes_enable_xgmii_port(void __iomem *sw_regs)
-{
-	writel(0x03, sw_regs + XGBE_CTRL_OFFSET);
-}
-
-static u32 netcp_xgbe_serdes_read_tbus_val(void __iomem *serdes_regs)
-{
-	u32 tmp;
-
-	if (PHY_A(serdes_regs)) {
-		tmp  = (readl(serdes_regs + 0x0ec) >> 24) & 0x0ff;
-		tmp |= ((readl(serdes_regs + 0x0fc) >> 16) & 0x00f00);
-	} else {
-		tmp  = (readl(serdes_regs + 0x0f8) >> 16) & 0x0fff;
-	}
-
-	return tmp;
-}
-
-static void netcp_xgbe_serdes_write_tbus_addr(void __iomem *serdes_regs,
-					      int select, int ofs)
-{
-	if (PHY_A(serdes_regs)) {
-		reg_rmw(serdes_regs + 0x0008, ((select << 5) + ofs) << 24,
-			~0x00ffffff);
-		return;
-	}
-
-	/* For 2 lane Phy-B, lane0 is actually lane1 */
-	switch (select) {
-	case 1:
-		select = 2;
-		break;
-	case 2:
-		select = 3;
-		break;
-	default:
-		return;
-	}
-
-	reg_rmw(serdes_regs + 0x00fc, ((select << 8) + ofs) << 16, ~0xf800ffff);
-}
-
-static u32 netcp_xgbe_serdes_read_select_tbus(void __iomem *serdes_regs,
-					      int select, int ofs)
-{
-	/* Set tbus address */
-	netcp_xgbe_serdes_write_tbus_addr(serdes_regs, select, ofs);
-	/* Get TBUS Value */
-	return netcp_xgbe_serdes_read_tbus_val(serdes_regs);
-}
-
-static void netcp_xgbe_serdes_reset_cdr(void __iomem *serdes_regs,
-					void __iomem *sig_detect_reg, int lane)
-{
-	u32 tmp, dlpf, tbus;
-
-	/*Get the DLPF values */
-	tmp = netcp_xgbe_serdes_read_select_tbus(
-			serdes_regs, lane + 1, 5);
-
-	dlpf = tmp >> 2;
-
-	if (dlpf < 400 || dlpf > 700) {
-		reg_rmw(sig_detect_reg, VAL_SH(2, 1), MASK_WID_SH(2, 1));
-		mdelay(1);
-		reg_rmw(sig_detect_reg, VAL_SH(0, 1), MASK_WID_SH(2, 1));
-	} else {
-		tbus = netcp_xgbe_serdes_read_select_tbus(serdes_regs, lane +
-							  1, 0xe);
-
-		pr_debug("XGBE: CDR centered, DLPF: %4d,%d,%d.\n",
-			 tmp >> 2, tmp & 3, (tbus >> 2) & 3);
-	}
-}
-
-/* Call every 100 ms */
-static int netcp_xgbe_check_link_status(void __iomem *serdes_regs,
-					void __iomem *sw_regs, u32 lanes,
-					u32 *current_state, u32 *lane_down)
-{
-	void __iomem *pcsr_base = sw_regs + 0x0600;
-	void __iomem *sig_detect_reg;
-	u32 pcsr_rx_stat, blk_lock, blk_errs;
-	int loss, i, status = 1;
-
-	for (i = 0; i < lanes; i++) {
-		/* Get the Loss bit */
-		loss = readl(serdes_regs + 0x1fc0 + 0x20 + (i * 0x04)) & 0x1;
-
-		/* Get Block Errors and Block Lock bits */
-		pcsr_rx_stat = readl(pcsr_base + 0x0c + (i * 0x80));
-		blk_lock = (pcsr_rx_stat >> 30) & 0x1;
-		blk_errs = (pcsr_rx_stat >> 16) & 0x0ff;
-
-		/* Get Signal Detect Overlay Address */
-		sig_detect_reg = serdes_regs + (i * 0x200) + 0x200 + 0x04;
-
-		/* If Block errors maxed out, attempt recovery! */
-		if (blk_errs == 0x0ff)
-			blk_lock = 0;
-
-		switch (current_state[i]) {
-		case 0:
-			/* if good link lock the signal detect ON! */
-			if (!loss && blk_lock) {
-				pr_debug("XGBE PCSR Linked Lane: %d\n", i);
-				reg_rmw(sig_detect_reg, VAL_SH(3, 1),
-					MASK_WID_SH(2, 1));
-				current_state[i] = 1;
-			} else if (!blk_lock) {
-				/* if no lock, then reset CDR */
-				pr_debug("XGBE PCSR Recover Lane: %d\n", i);
-				netcp_xgbe_serdes_reset_cdr(serdes_regs,
-							    sig_detect_reg, i);
-			}
-			break;
-
-		case 1:
-			if (!blk_lock) {
-				/* Link Lost? */
-				lane_down[i] = 1;
-				current_state[i] = 2;
-			}
-			break;
-
-		case 2:
-			if (blk_lock)
-				/* Nope just noise */
-				current_state[i] = 1;
-			else {
-				/* Lost the block lock, reset CDR if it is
-				 * not centered and go back to sync state
-				 */
-				netcp_xgbe_serdes_reset_cdr(serdes_regs,
-							    sig_detect_reg, i);
-				current_state[i] = 0;
-			}
-			break;
-
-		default:
-			pr_err("XGBE: unknown current_state[%d] %d\n",
-			       i, current_state[i]);
-			break;
-		}
-
-		if (blk_errs > 0) {
-			/* Reset the Error counts! */
-			reg_rmw(pcsr_base + 0x08 + (i * 0x80), VAL_SH(0x19, 0),
-				MASK_WID_SH(8, 0));
-
-			reg_rmw(pcsr_base + 0x08 + (i * 0x80), VAL_SH(0x00, 0),
-				MASK_WID_SH(8, 0));
-		}
-
-		status &= (current_state[i] == 1);
-	}
-
-	return status;
-}
-
-static int netcp_xgbe_serdes_check_lane(void __iomem *serdes_regs,
-					void __iomem *sw_regs)
-{
-	u32 current_state[2] = {0, 0};
-	int retries = 0, link_up;
-	u32 lane_down[2];
-
-	do {
-		lane_down[0] = 0;
-		lane_down[1] = 0;
-
-		link_up = netcp_xgbe_check_link_status(serdes_regs, sw_regs, 2,
-						       current_state,
-						       lane_down);
-
-		/* if we did not get link up then wait 100ms before calling
-		 * it again
-		 */
-		if (link_up)
-			break;
-
-		if (lane_down[0])
-			pr_debug("XGBE: detected link down on lane 0\n");
-
-		if (lane_down[1])
-			pr_debug("XGBE: detected link down on lane 1\n");
-
-		if (++retries > 1) {
-			pr_debug("XGBE: timeout waiting for serdes link up\n");
-			return -ETIMEDOUT;
-		}
-		mdelay(100);
-	} while (!link_up);
-
-	pr_debug("XGBE: PCSR link is up\n");
-	return 0;
-}
-
-static void netcp_xgbe_serdes_setup_cm_c1_c2(void __iomem *serdes_regs,
-					     int lane, int cm, int c1, int c2)
-{
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(cfg_cm_c1_c2); i++) {
-		reg_rmw(serdes_regs + cfg_cm_c1_c2[i].ofs + (0x200 * lane),
-			cfg_cm_c1_c2[i].val,
-			cfg_cm_c1_c2[i].mask);
-	}
-}
-
-static void netcp_xgbe_reset_serdes(void __iomem *serdes_regs)
-{
-	/* Toggle the POR_EN bit in CONFIG.CPU_CTRL */
-	/* enable POR_EN bit */
-	reg_rmw(serdes_regs + PCSR_CPU_CTRL_OFFSET, POR_EN, POR_EN);
-	usleep_range(10, 100);
-
-	/* disable POR_EN bit */
-	reg_rmw(serdes_regs + PCSR_CPU_CTRL_OFFSET, 0, POR_EN);
-	usleep_range(10, 100);
-}
-
-static int netcp_xgbe_serdes_config(void __iomem *serdes_regs,
-				    void __iomem *sw_regs)
-{
-	u32 ret, i;
-
-	netcp_xgbe_serdes_pll_disable(serdes_regs);
-	netcp_xgbe_serdes_cmu_init(serdes_regs);
-
-	for (i = 0; i < 2; i++)
-		netcp_xgbe_serdes_lane_config(serdes_regs, i);
-
-	netcp_xgbe_serdes_com_enable(serdes_regs);
-	/* This is EVM + RTM-BOC specific */
-	for (i = 0; i < 2; i++)
-		netcp_xgbe_serdes_setup_cm_c1_c2(serdes_regs, i, 0, 0, 5);
-
-	netcp_xgbe_serdes_pll_enable(serdes_regs);
-	for (i = 0; i < 2; i++)
-		netcp_xgbe_serdes_lane_enable(serdes_regs, i);
-
-	/* SB PLL Status Poll */
-	ret = netcp_xgbe_wait_pll_locked(sw_regs);
-	if (ret)
-		return ret;
-
-	netcp_xgbe_serdes_enable_xgmii_port(sw_regs);
-	netcp_xgbe_serdes_check_lane(serdes_regs, sw_regs);
-	return ret;
-}
-
-int netcp_xgbe_serdes_init(void __iomem *serdes_regs, void __iomem *xgbe_regs)
-{
-	u32 val;
-
-	/* read COMLANE bits 4:0 */
-	val = readl(serdes_regs + 0xa00);
-	if (val & 0x1f) {
-		pr_debug("XGBE: serdes already in operation - reset\n");
-		netcp_xgbe_reset_serdes(serdes_regs);
-	}
-	return netcp_xgbe_serdes_config(serdes_regs, xgbe_regs);
-}

+ 8 - 0
drivers/phy/ti/Kconfig

@@ -76,3 +76,11 @@ config TWL4030_USB
 	  family chips (including the TWL5030 and TPS659x0 devices).
 	  family chips (including the TWL5030 and TPS659x0 devices).
 	  This transceiver supports high and full speed devices plus,
 	  This transceiver supports high and full speed devices plus,
 	  in host mode, low speed.
 	  in host mode, low speed.
+
+config PHY_TI_KEYSTONE_SERDES
+	tristate "TI Keystone SerDes PHY support"
+	depends on OF && ARCH_KEYSTONE || COMPILE_TEST
+	select GENERIC_PHY
+	help
+	  This option enables support for TI Keystone SerDes PHY found
+	  in peripherals GBE, 10GBE and PCIe.

+ 1 - 0
drivers/phy/ti/Makefile

@@ -6,3 +6,4 @@ obj-$(CONFIG_OMAP_USB2)			+= phy-omap-usb2.o
 obj-$(CONFIG_TI_PIPE3)			+= phy-ti-pipe3.o
 obj-$(CONFIG_TI_PIPE3)			+= phy-ti-pipe3.o
 obj-$(CONFIG_PHY_TUSB1210)		+= phy-tusb1210.o
 obj-$(CONFIG_PHY_TUSB1210)		+= phy-tusb1210.o
 obj-$(CONFIG_TWL4030_USB)		+= phy-twl4030-usb.o
 obj-$(CONFIG_TWL4030_USB)		+= phy-twl4030-usb.o
+obj-$(CONFIG_PHY_TI_KEYSTONE_SERDES)	+= phy-keystone-serdes.o

+ 2569 - 0
drivers/phy/ti/phy-keystone-serdes.c

@@ -0,0 +1,2569 @@
+/*
+ * Texas Instruments Keystone SerDes driver
+ * Authors: WingMan Kwok <w-kwok2@ti.com>
+ *
+ * This is the SerDes Phy driver for Keystone devices. This is
+ * required to support PCIe RC functionality based on designware
+ * PCIe hardware, gbe and 10gbe found on these devices.
+ *
+ * Revision History:
+ *    3.3.0.2c
+ *	- Full update based on CSL version 3.3.0.2c
+ *	- This update requires the remodelling of each SerDes lane
+ *	  as a separate PHY device, as opposed to each SerDes as a
+ *	  separate PHY device prior to this patch.
+ *
+ *    1.0.0
+ *	- Initial revision.
+ *
+ * Copyright (C) 2015-18 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Texas Instruments Incorporated nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/mfd/syscon.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/regmap.h>
+#include <linux/sizes.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+
+#define KSERDES_SS_OFFSET	0x1fc0
+#define MOD_VER_REG		(KSERDES_SS_OFFSET + 0x00)
+#define MEM_ADR_REG		(KSERDES_SS_OFFSET + 0x04)
+#define MEM_DAT_REG		(KSERDES_SS_OFFSET + 0x08)
+#define MEM_DATINC_REG		(KSERDES_SS_OFFSET + 0x0c)
+#define CPU_CTRL_REG		(KSERDES_SS_OFFSET + 0x10)
+#define LANE_CTRL_STS_REG(x)	(KSERDES_SS_OFFSET + 0x20 + (x * 0x04))
+#define LINK_LOSS_WAIT_REG	(KSERDES_SS_OFFSET + 0x30)
+#define PLL_CTRL_REG		(KSERDES_SS_OFFSET + 0x34)
+
+#define CMU0_SS_OFFSET		0x0000
+#define CMU0_REG(x)		(CMU0_SS_OFFSET + x)
+
+#define LANE0_SS_OFFSET		0x0200
+#define LANEX_SS_OFFSET(x)	(LANE0_SS_OFFSET * (x + 1))
+#define LANEX_REG(x, y)		(LANEX_SS_OFFSET(x) + y)
+
+#define CML_SS_OFFSET		0x0a00
+#define CML_REG(x)		(CML_SS_OFFSET + x)
+
+#define CMU1_SS_OFFSET		0x0c00
+#define CMU1_REG(x)		(CMU1_SS_OFFSET + x)
+
+#define PCSR_OFFSET(x)		(x * 0x80)
+
+#define PCSR_TX_CTL(x)		(PCSR_OFFSET(x) + 0x00)
+#define PCSR_TX_STATUS(x)	(PCSR_OFFSET(x) + 0x04)
+#define PCSR_RX_CTL(x)		(PCSR_OFFSET(x) + 0x08)
+#define PCSR_RX_STATUS(x)	(PCSR_OFFSET(x) + 0x0C)
+
+#define XGE_CTRL_OFFSET		0x0c
+#define PCIE_PL_GEN2_OFFSET	0x180c
+
+#define reg_rmw(addr, value, mask) \
+	writel(((readl(addr) & (~(mask))) | (value & (mask))), (addr))
+
+#define FINSR(base, offset, msb, lsb, val) \
+	reg_rmw((base) + (offset), ((val) << (lsb)), GENMASK((msb), (lsb)))
+
+#define FEXTR(val, msb, lsb) \
+	(((val) >> (lsb)) & ((1 << ((msb) - (lsb) + 1)) - 1))
+
+#define MOD_VER(serdes) \
+	((kserdes_readl(serdes, MOD_VER_REG) >> 16) & 0xffff)
+
+#define PHY_A(serdes) (MOD_VER(serdes) != 0x4eba)
+
+#define FOUR_LANE(serdes) \
+	((MOD_VER(serdes) == 0x4eb9) || (MOD_VER(serdes) == 0x4ebd))
+
+#define KSERDES_MAX_LANES	4
+#define MAX_COEFS		5
+#define MAX_CMP			5
+#define OFFSET_SAMPLES		100
+
+#define for_each_cmp(i)	\
+	for (i = 1; i < MAX_CMP; i++)
+
+#define CPU_EN			BIT(31)
+#define CPU_GO			BIT(30)
+#define POR_EN			BIT(29)
+#define CPUREG_EN		BIT(28)
+#define AUTONEG_CTL		BIT(27)
+#define DATASPLIT		BIT(26)
+#define LNKTRN_SIG_DET		BIT(8)
+
+#define PLL_ENABLE_1P25G	0xe0000000
+#define LANE_CTRL_1P25G		0xf800f8c0
+#define XFM_FLUSH_CMD		0x00009c9c
+
+#define ANEG_LINK_CTL_10GKR_MASK	GENMASK(21, 20)
+#define ANEG_LINK_CTL_1GKX_MASK		GENMASK(17, 16)
+#define ANEG_LINK_CTL_1G10G_MASK \
+	(ANEG_LINK_CTL_10GKR_MASK | ANEG_LINK_CTL_1GKX_MASK)
+
+#define ANEG_1G_10G_OPT_MASK		GENMASK(7, 5)
+
+#define SERDES_REG_INDEX		0
+
+#define KSERDES_XFW_MEM_SIZE		SZ_64K
+#define KSERDES_XFW_CONFIG_MEM_SIZE	SZ_64
+#define KSERDES_XFW_NUM_PARAMS		5
+
+#define KSERDES_XFW_CONFIG_START_ADDR \
+	(KSERDES_XFW_MEM_SIZE - KSERDES_XFW_CONFIG_MEM_SIZE)
+
+#define KSERDES_XFW_PARAM_START_ADDR \
+	(KSERDES_XFW_MEM_SIZE - (KSERDES_XFW_NUM_PARAMS * 4))
+
+#define LANE_ENABLE(sc, n) ((sc)->lane[n].enable)
+
+enum kserdes_link_rate {
+	KSERDES_LINK_RATE_1P25G		=  1250000,
+	KSERDES_LINK_RATE_3P125G	=  3125000,
+	KSERDES_LINK_RATE_4P9152G	=  4915200,
+	KSERDES_LINK_RATE_5G		=  5000000,
+	KSERDES_LINK_RATE_6P144G	=  6144000,
+	KSERDES_LINK_RATE_6P25G		=  6250000,
+	KSERDES_LINK_RATE_7P3728G	=  7372800,
+	KSERDES_LINK_RATE_9P8304G	=  9830400,
+	KSERDES_LINK_RATE_10G		= 10000000,
+	KSERDES_LINK_RATE_10P3125G	= 10312500,
+	KSERDES_LINK_RATE_12P5G		= 12500000,
+};
+
+enum kserdes_lane_ctrl_rate {
+	KSERDES_FULL_RATE,
+	KSERDES_HALF_RATE,
+	KSERDES_QUARTER_RATE,
+};
+
+enum kserdes_phy_type {
+	KSERDES_PHY_SGMII,
+	KSERDES_PHY_XGE,
+	KSERDES_PHY_PCIE,
+	KSERDES_PHY_HYPERLINK,
+};
+
+struct kserdes_tx_coeff {
+	u32	c1;
+	u32	c2;
+	u32	cm;
+	u32	att;
+	u32	vreg;
+};
+
+struct kserdes_equalizer {
+	u32	att;
+	u32	boost;
+};
+
+struct kserdes_lane_config {
+	bool				enable;
+	u32				ctrl_rate;
+	struct kserdes_tx_coeff		tx_coeff;
+	struct kserdes_equalizer	rx_start;
+	struct kserdes_equalizer	rx_force;
+	bool				loopback;
+};
+
+struct kserdes_fw_config {
+	bool				on;
+	u32				rate;
+	u32				link_loss_wait;
+	u32				lane_seeds;
+	u32				fast_train;
+	u32				active_lane;
+	u32				c1, c2, cm, attn, boost, dlpf, rxcal;
+	u32				lane_config[KSERDES_MAX_LANES];
+};
+
+struct kserdes_lane_dlev_out {
+	u32 delay;
+	int coef_vals[MAX_COEFS];
+};
+
+struct kserdes_dlev_out {
+	struct kserdes_lane_dlev_out lane_dlev_out[KSERDES_MAX_LANES];
+};
+
+struct kserdes_cmp_coef_ofs {
+	u32 cmp;
+	u32 coef1;
+	u32 coef2;
+	u32 coef3;
+	u32 coef4;
+	u32 coef5;
+};
+
+struct kserdes_lane_ofs {
+	struct kserdes_cmp_coef_ofs ct_ofs[MAX_CMP];
+};
+
+struct kserdes_ofs {
+	struct kserdes_lane_ofs lane_ofs[KSERDES_MAX_LANES];
+};
+
+/*
+ * All firmware file names end up here. List the firmware file names below.
+ * Newest first. Search starts from the 0-th array entry until a firmware
+ * file is found.
+ */
+static const char * const ks2_gbe_serdes_firmwares[] = {"ks2_gbe_serdes.bin"};
+static const char * const ks2_xgbe_serdes_firmwares[] = {"ks2_xgbe_serdes.bin"};
+static const char * const ks2_pcie_serdes_firmwares[] = {"ks2_pcie_serdes.bin"};
+
+#define MAX_VERSION		64
+#define INIT_FW_MAGIC_1		0xfaceface
+#define INIT_FW_MAGIC_2		0xcafecafe
+
+static char *compatible_init_fw_version[] = {
+	"3.3.0.2c",
+	NULL,
+};
+
+struct serdes_cfg_header {
+	u32 magic_1;
+	char version[MAX_VERSION];
+	u32 magic_2;
+};
+
+struct serdes_cfg {
+	u32 ofs;
+	u32 msb;
+	u32 lsb;
+	u32 val;
+};
+
+struct kserdes_config {
+	struct device			*dev;
+	enum kserdes_phy_type		phy_type;
+	u32				lanes;
+	void __iomem			*regs;
+	struct regmap			*peripheral_regmap;
+	struct regmap			*pcsr_regmap;
+	const char			*init_fw;
+	struct serdes_cfg		*init_cfg;
+	int				init_cfg_len;
+	enum kserdes_link_rate		link_rate;
+	bool				rx_force_enable;
+	struct kserdes_lane_config	lane[KSERDES_MAX_LANES];
+	struct kserdes_ofs		sofs;
+	bool				firmware;
+	struct kserdes_fw_config	fw;
+};
+
+struct kserdes_phy {
+	u32 lane;
+	struct phy *phy;
+};
+
+struct kserdes_dev {
+	struct device *dev;
+	u32 nphys;
+	struct kserdes_phy *phys[KSERDES_MAX_LANES];
+	struct kserdes_config sc;
+};
+
+static struct platform_device_id kserdes_devtype[] = {
+	{
+		.name = "kserdes-gbe",
+		.driver_data = KSERDES_PHY_SGMII,
+	}, {
+		.name = "kserdes-xgbe",
+		.driver_data = KSERDES_PHY_XGE,
+	}, {
+		.name = "kserdes-pcie",
+		.driver_data = KSERDES_PHY_PCIE,
+	}
+};
+
+static inline int next_enable_lane(struct kserdes_config *sc, int i)
+{
+	int j = i;
+
+	while (++j < sc->lanes) {
+		if (sc->lane[j].enable)
+			return j;
+	}
+	return j;
+}
+
+#define for_each_lane(sc, i) \
+	for (i = 0; i < (sc)->lanes; i++)
+
+#define for_each_enable_lane(sc, i) \
+	for (i = -1; i = next_enable_lane(sc, i), i < sc->lanes; )
+
+static inline u32 kserdes_readl(void __iomem *base, u32 offset)
+{
+	return readl(base + offset);
+}
+
+static inline void kserdes_writel(void __iomem *base, u32 offset, u32 value)
+{
+	writel(value, base + offset);
+}
+
+static void kserdes_do_config(void __iomem *base,
+			      struct serdes_cfg *cfg, u32 size)
+{
+	u32 i;
+
+	for (i = 0; i < size; i++)
+		FINSR(base, cfg[i].ofs, cfg[i].msb, cfg[i].lsb, cfg[i].val);
+}
+
+static bool is_init_fw_compatible(struct kserdes_config *sc,
+				  struct serdes_cfg_header *hdr)
+{
+	int i = 0;
+
+	if ((hdr->magic_1 != INIT_FW_MAGIC_1) ||
+	    (hdr->magic_2 != INIT_FW_MAGIC_2)) {
+		dev_err(sc->dev, "incompatible fw %s\n", sc->init_fw);
+		return false;
+	}
+
+	while (compatible_init_fw_version[i]) {
+		if (!strcmp(compatible_init_fw_version[i], hdr->version)) {
+			dev_info(sc->dev, "init fw %s: version %s\n",
+				 sc->init_fw, hdr->version);
+			return true;
+		}
+
+		++i;
+	}
+
+	dev_err(sc->dev, "incompatible fw %s: version %s\n",
+		sc->init_fw, hdr->version);
+	return false;
+}
+
+static int kserdes_load_init_fw(struct kserdes_config *sc,
+				const char * const *a_firmwares,
+				int n_firmwares)
+{
+	const struct firmware *fw;
+	bool found = false;
+	int ret, i;
+	struct serdes_cfg_header hdr;
+	int hdr_sz;
+
+	for (i = 0; i < n_firmwares; i++) {
+		if (a_firmwares[i]) {
+			ret = request_firmware(&fw, a_firmwares[i], sc->dev);
+			if (!ret) {
+				found = true;
+				break;
+			}
+		}
+	}
+
+	if (!found) {
+		dev_err(sc->dev, "can't get any serdes init fw");
+		return -ENODEV;
+	}
+
+	sc->init_fw = a_firmwares[i];
+
+	memcpy((void *)&hdr, fw->data, sizeof(hdr));
+	hdr_sz = sizeof(hdr);
+	hdr.version[MAX_VERSION - 1] = 0;
+	if (!is_init_fw_compatible(sc, &hdr))
+		return -EINVAL;
+
+	sc->init_cfg = devm_kzalloc(sc->dev, fw->size - hdr_sz, GFP_KERNEL);
+	memcpy((void *)sc->init_cfg, fw->data + hdr_sz, fw->size - hdr_sz);
+	sc->init_cfg_len = fw->size - hdr_sz;
+	release_firmware(fw);
+
+	kserdes_do_config(sc->regs, sc->init_cfg,
+			  sc->init_cfg_len / sizeof(struct serdes_cfg));
+
+	return 0;
+}
+
+static inline u32 _kserdes_read_tbus_val(void __iomem *sregs)
+{
+	u32 tmp;
+
+	if (PHY_A(sregs)) {
+		tmp  = ((kserdes_readl(sregs, CMU0_REG(0xec))) >> 24) & 0x0ff;
+		tmp |= ((kserdes_readl(sregs, CMU0_REG(0xfc))) >> 16) & 0xf00;
+	} else {
+		tmp  = ((kserdes_readl(sregs, CMU0_REG(0xf8))) >> 16) & 0xfff;
+	}
+
+	return tmp;
+}
+
+static void _kserdes_write_tbus_addr(void __iomem *sregs, int select, int ofs)
+{
+	if (select && !FOUR_LANE(sregs))
+		++select;
+
+	if (PHY_A(sregs))
+		FINSR(sregs, CMU0_REG(0x8), 31, 24, ((select << 5) + ofs));
+	else
+		FINSR(sregs, CMU0_REG(0xfc), 26, 16, ((select << 8) + ofs));
+}
+
+static u32 _kserdes_read_select_tbus(void __iomem *sregs, int select, int ofs)
+{
+	_kserdes_write_tbus_addr(sregs, select, ofs);
+	return _kserdes_read_tbus_val(sregs);
+}
+
+static inline void kserdes_set_tx_idle(struct kserdes_config *sc, u32 lane)
+{
+	if (sc->phy_type != KSERDES_PHY_XGE)
+		FINSR(sc->regs, LANEX_REG(lane, 0xb8), 17, 16, 3);
+
+	FINSR(sc->regs, LANE_CTRL_STS_REG(lane), 25, 24, 3);
+	FINSR(sc->regs, LANEX_REG(lane, 0x28), 21, 20, 0);
+}
+
+static inline void kserdes_clr_tx_idle(struct kserdes_config *sc, u32 lane)
+{
+	if (sc->phy_type != KSERDES_PHY_XGE)
+		FINSR(sc->regs, LANEX_REG(lane, 0xb8), 17, 16, 0);
+
+	FINSR(sc->regs, LANE_CTRL_STS_REG(lane), 25, 24, 0);
+	FINSR(sc->regs, LANEX_REG(lane, 0x28), 21, 20, 0);
+}
+
+static void kserdes_set_lane_ov(struct kserdes_config *sc, u32 lane)
+{
+	u32 val_0, val_1, val;
+
+	val_0 = _kserdes_read_select_tbus(sc->regs, lane + 1, 0);
+	val_1 = _kserdes_read_select_tbus(sc->regs, lane + 1, 1);
+
+	val = 0;
+	val |= ((val_1 >> 9) & 0x3) << 1;
+	val |= (val_0 & 0x3) << 3;
+	val |= ((val_0 >> 2) & 0x1ff) << 5;
+	val |= (1 << 14);
+	val &= ~0x60;
+
+	FINSR(sc->regs, LANEX_REG(lane, 0x028), 29, 15, val);
+}
+
+static inline void kserdes_assert_reset(struct kserdes_config *sc)
+{
+	int lane;
+
+	for_each_enable_lane(sc, lane)
+		kserdes_set_lane_ov(sc, lane);
+}
+
+static inline void kserdes_config_c1_c2_cm(struct kserdes_config *sc, u32 lane)
+{
+	u32 c1, c2, cm;
+
+	c1 = sc->lane[lane].tx_coeff.c1;
+	c2 = sc->lane[lane].tx_coeff.c2;
+	cm = sc->lane[lane].tx_coeff.cm;
+
+	if (sc->phy_type == KSERDES_PHY_XGE) {
+		FINSR(sc->regs, LANEX_REG(lane, 0x8), 11,  8, (cm & 0xf));
+		FINSR(sc->regs, LANEX_REG(lane, 0x8),  4,  0, (c1 & 0x1f));
+		FINSR(sc->regs, LANEX_REG(lane, 0x8),  7,  5, (c2 & 0x7));
+		FINSR(sc->regs, LANEX_REG(lane, 0x4),
+		      18, 18, ((c2 >> 3) & 0x1));
+	} else {
+		FINSR(sc->regs, LANEX_REG(lane, 0x8), 15, 12, (cm & 0xf));
+		FINSR(sc->regs, LANEX_REG(lane, 0x8),  4,  0, (c1 & 0x1f));
+		FINSR(sc->regs, LANEX_REG(lane, 0x8), 11,  8, (c2 & 0xf));
+	}
+}
+
+static inline void kserdes_config_att_boost(struct kserdes_config *sc, u32 lane)
+{
+	u32 att, boost;
+
+	att = sc->lane[lane].rx_force.att;
+	boost = sc->lane[lane].rx_force.boost;
+
+	if (sc->phy_type == KSERDES_PHY_XGE) {
+		FINSR(sc->regs, LANEX_REG(lane, 0x98), 13, 13, 0);
+		FINSR(sc->regs, LANEX_REG(lane, 0x8c), 15, 12, boost);
+		FINSR(sc->regs, LANEX_REG(lane, 0x8c), 11, 8, att);
+	} else {
+		if (att != -1) {
+			FINSR(sc->regs, CML_REG(0x84), 0, 0, 0);
+			FINSR(sc->regs, CML_REG(0x8c), 24, 24, 0);
+			FINSR(sc->regs, LANEX_REG(lane, 0x8c), 11, 8, att);
+		}
+		if (boost != -1) {
+			FINSR(sc->regs, CML_REG(0x84), 1, 1, 0);
+			FINSR(sc->regs, CML_REG(0x8c), 25, 25, 0);
+			FINSR(sc->regs, LANEX_REG(lane, 0x8c), 15, 12, boost);
+		}
+	}
+}
+
+static void kserdes_set_tx_rx_fir_coeff(struct kserdes_config *sc, u32 lane)
+{
+	struct kserdes_tx_coeff *tc = &sc->lane[lane].tx_coeff;
+
+	if (sc->phy_type == KSERDES_PHY_XGE) {
+		FINSR(sc->regs, LANEX_REG(lane, 0x004), 29, 26, tc->att);
+		FINSR(sc->regs, LANEX_REG(lane, 0x0a4), 2, 0, tc->vreg);
+	} else {
+		FINSR(sc->regs, LANEX_REG(lane, 0x004), 28, 25, tc->att);
+		FINSR(sc->regs, LANEX_REG(lane, 0x084), 7, 5, tc->vreg);
+	}
+
+	kserdes_config_c1_c2_cm(sc, lane);
+
+	if (sc->rx_force_enable)
+		kserdes_config_att_boost(sc, lane);
+}
+
+static inline void
+_kserdes_force_signal_detect_low(void __iomem *sregs, u32 lane)
+{
+	FINSR(sregs, LANEX_REG(lane, 0x004), 2, 1, 0x2);
+}
+
+static inline void
+kserdes_force_signal_detect_low(struct kserdes_config *sc, u32 lane)
+{
+	_kserdes_force_signal_detect_low(sc->regs, lane);
+}
+
+static inline void
+_kserdes_force_signal_detect_high(void __iomem *sregs, u32 lane)
+{
+	FINSR(sregs, LANEX_REG(lane, 0x004), 2, 1, 0x0);
+}
+
+static inline void
+kserdes_force_signal_detect_high(struct kserdes_config *sc, u32 lane)
+{
+	_kserdes_force_signal_detect_high(sc->regs, lane);
+}
+
+static int kserdes_deassert_reset_poll_others(struct kserdes_config *sc)
+{
+	unsigned long timeout = jiffies + msecs_to_jiffies(500);
+	unsigned long time_check;
+	u32 lanes_not_ok = 0;
+	u32 ofs = 28;
+	u32 ret, i;
+
+	for_each_enable_lane(sc, i)
+		lanes_not_ok |= BIT(i);
+
+	if (!FOUR_LANE(sc->regs))
+		ofs = 29;
+
+	do {
+		time_check = jiffies;
+		for_each_enable_lane(sc, i) {
+			if (!(lanes_not_ok & (1 << i)))
+				continue;
+
+			ret = kserdes_readl(sc->regs, CML_REG(0x1f8));
+
+			if (ret & BIT(ofs + i))
+				lanes_not_ok &= ~BIT(i);
+		}
+
+		if (!lanes_not_ok)
+			return 0;
+
+		if (time_after(time_check, timeout))
+			return -ETIMEDOUT;
+
+		cpu_relax();
+	} while (true);
+}
+
+static int kserdes_deassert_reset_poll_pcie(struct kserdes_config *sc)
+{
+	unsigned long timeout = jiffies + msecs_to_jiffies(500);
+	unsigned long time_check;
+	u32 lanes_not_ok = 0;
+	u32 ret, i;
+
+	for_each_enable_lane(sc, i)
+		lanes_not_ok |= (1 << i);
+
+	do {
+		time_check = jiffies;
+		for_each_enable_lane(sc, i) {
+			if (!(lanes_not_ok & BIT(i)))
+				continue;
+
+			ret = _kserdes_read_select_tbus(sc->regs, i + 1, 0x02);
+
+			if (!(ret & BIT(4)))
+				lanes_not_ok &= ~BIT(i);
+		}
+
+		if (!lanes_not_ok)
+			return 0;
+
+		if (time_after(time_check, timeout))
+			return -ETIMEDOUT;
+
+		cpu_relax();
+	} while (true);
+}
+
+static inline void _kserdes_lane_reset(void __iomem *serdes,
+				       u32 lane, u32 reset)
+{
+	FINSR(serdes, LANEX_REG(lane, 0x28), 29, 29, !!reset);
+}
+
+static inline void kserdes_release_reset(struct kserdes_config *sc, u32 lane)
+{
+	if (sc->phy_type == KSERDES_PHY_XGE)
+		FINSR(sc->regs, LANEX_REG(lane, 0x60), 0, 0, 0x1);
+	_kserdes_lane_reset(sc->regs, lane, 0);
+}
+
+static int kserdes_deassert_reset(struct kserdes_config *sc, u32 poll)
+{
+	int ret = 0, lane;
+
+	for_each_enable_lane(sc, lane)
+		kserdes_release_reset(sc, lane);
+
+	if (!poll)
+		goto done;
+
+	if (sc->phy_type == KSERDES_PHY_PCIE)
+		ret = kserdes_deassert_reset_poll_pcie(sc);
+	else
+		ret = kserdes_deassert_reset_poll_others(sc);
+
+done:
+	return ret;
+}
+
+static inline void _kserdes_lane_enable(void __iomem *sregs, u32 lane)
+{
+	FINSR(sregs, LANE_CTRL_STS_REG(lane), 31, 29, 0x7);
+	FINSR(sregs, LANE_CTRL_STS_REG(lane), 15, 13, 0x7);
+}
+
+static inline int _kserdes_set_lane_ctrl_rate(void __iomem *sregs, u32 lane,
+					      enum kserdes_lane_ctrl_rate rate)
+{
+	u32 rate_mode;
+
+	switch (rate) {
+	case KSERDES_FULL_RATE:
+		rate_mode = 0x4;
+		break;
+	case KSERDES_QUARTER_RATE:
+		rate_mode = 0x6;
+		break;
+	case KSERDES_HALF_RATE:
+		rate_mode = 0x5;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	FINSR(sregs, LANE_CTRL_STS_REG(lane), 28, 26, rate_mode);
+	FINSR(sregs, LANE_CTRL_STS_REG(lane), 12, 10, rate_mode);
+	return 0;
+}
+
+static inline void _kserdes_set_lane_loopback(void __iomem *sregs, u32 lane,
+					      enum kserdes_phy_type phy_type)
+{
+	if (phy_type == KSERDES_PHY_XGE) {
+		FINSR(sregs, LANEX_REG(lane, 0x0), 7, 0, 0x4);
+		FINSR(sregs, LANEX_REG(lane, 0x4), 2, 1, 0x3);
+	} else {
+		FINSR(sregs, LANEX_REG(lane, 0x0), 31, 24, 0x40);
+	}
+}
+
+static inline void _kserdes_clear_wait_after(void __iomem *sregs)
+{
+	FINSR(sregs, PLL_CTRL_REG, 17, 16, 0);
+}
+
+static inline void _kserdes_clear_lane_wait_after(void __iomem *sregs, u32 lane)
+{
+	FINSR(sregs, PLL_CTRL_REG, lane + 12, lane + 12, 1);
+	FINSR(sregs, PLL_CTRL_REG, lane + 4, lane + 4, 1);
+}
+
+static inline void _kserdes_pll_enable(void __iomem *sregs)
+{
+	FINSR(sregs, PLL_CTRL_REG, 31, 29, 0x7);
+}
+
+static inline void _kserdes_pll2_enable(void __iomem *sregs)
+{
+	FINSR(sregs, PLL_CTRL_REG, 27, 25, 0x7);
+}
+
+static inline void _kserdes_pll_disable(void __iomem *sregs)
+{
+	FINSR(sregs, PLL_CTRL_REG, 31, 29, 0x4);
+}
+
+static inline void _kserdes_pll2_disable(void __iomem *sregs)
+{
+	FINSR(sregs, PLL_CTRL_REG, 27, 25, 0x4);
+}
+
+static inline u32 _kserdes_get_pll_status(void __iomem *sregs)
+{
+	return FEXTR(kserdes_readl(sregs, PLL_CTRL_REG), 28, 28);
+}
+
+static inline u32 _kserdes_get_pll2_status(void __iomem *sregs)
+{
+	return FEXTR(kserdes_readl(sregs, PLL_CTRL_REG), 24, 24);
+}
+
+static inline void kserdes_lane_enable_loopback(void __iomem *serdes, u32 lane)
+{
+	FINSR(serdes, LANEX_REG(lane, 0), 31, 24, 0x40);
+}
+
+static inline u32 _kserdes_get_lane_status(void __iomem *sregs, u32 lane,
+					   enum kserdes_phy_type phy_type)
+{
+	int d = ((phy_type == KSERDES_PHY_PCIE) ? 0 : 8);
+
+	return FEXTR(kserdes_readl(sregs, PLL_CTRL_REG), lane + d, lane + d);
+}
+
+static u32 kserdes_get_pll_lanes_status(struct kserdes_config *sc)
+{
+	u32 val, i;
+
+	val = _kserdes_get_pll_status(sc->regs);
+	if (!val)
+		goto done;
+
+	if (sc->phy_type == KSERDES_PHY_XGE) {
+		val = _kserdes_get_pll2_status(sc->regs);
+		if (!val)
+			goto done;
+	}
+
+	for_each_enable_lane(sc, i)
+		val &= _kserdes_get_lane_status(sc->regs, i, sc->phy_type);
+
+done:
+	return val;
+}
+
+static int kserdes_get_status(struct kserdes_config *sc)
+{
+	unsigned long timeout = jiffies + msecs_to_jiffies(500);
+	unsigned long time_check;
+
+	do {
+		time_check = jiffies;
+		if (kserdes_get_pll_lanes_status(sc))
+			break;
+
+		if (time_after(time_check, timeout))
+			return -ETIMEDOUT;
+
+		cpu_relax();
+	} while (true);
+
+	return 0;
+}
+
+static inline u32 _kserdes_get_tx_termination(void __iomem *sregs,
+					      enum kserdes_phy_type phy_type)
+{
+	return (_kserdes_read_select_tbus(sregs, 1,
+					  ((phy_type == KSERDES_PHY_XGE) ?
+					  0x1a : 0x1b)) & 0xff);
+}
+
+static void kserdes_set_tx_terminations(struct kserdes_config *sc, u32 term)
+{
+	int i;
+
+	for_each_lane(sc, i) {
+		FINSR(sc->regs, LANEX_REG(i, 0x7c), 31, 24, term);
+		FINSR(sc->regs, LANEX_REG(i, 0x7c), 20, 20, 0x1);
+	}
+}
+
+static void
+_kserdes_write_ofs_xge(void __iomem *sregs, u32 lane, u32 cmp,
+		       struct kserdes_cmp_coef_ofs *ofs)
+{
+	FINSR(sregs, CML_REG(0x8c), 23, 21, cmp);
+
+	FINSR(sregs, CMU0_REG(0xfc), 26, 16, ((lane + 2) << 8) + 0x11);
+	ofs->cmp = (_kserdes_read_tbus_val(sregs) & 0x0ff0) >> 4;
+
+	FINSR(sregs, CMU0_REG(0xfc), 26, 16, ((lane + 2) << 8) + 0x11);
+	ofs->coef1 = (_kserdes_read_tbus_val(sregs) & 0x000f) << 3;
+
+	FINSR(sregs, CMU0_REG(0xfc), 26, 16, ((lane + 2) << 8) + 0x12);
+	ofs->coef1 |= (_kserdes_read_tbus_val(sregs) & 0x0e00) >> 9;
+	ofs->coef2  = (_kserdes_read_tbus_val(sregs) & 0x01f8) >> 3;
+	ofs->coef3  = (_kserdes_read_tbus_val(sregs) & 0x0007) << 3;
+
+	FINSR(sregs, CMU0_REG(0xfc), 26, 16, ((lane + 2) << 8) + 0x13);
+	ofs->coef3 |= (_kserdes_read_tbus_val(sregs) & 0x0e00) >> 9;
+	ofs->coef4  = (_kserdes_read_tbus_val(sregs) & 0x01f8) >> 3;
+	ofs->coef5  = (_kserdes_read_tbus_val(sregs) & 0x0007) << 3;
+
+	FINSR(sregs, CMU0_REG(0xfc), 26, 16, ((lane + 2) << 8) + 0x14);
+	ofs->coef5 |= (_kserdes_read_tbus_val(sregs) & 0x0e00) >> 9;
+}
+
+static void kserdes_add_ofs_xge(struct kserdes_config *sc,
+				struct kserdes_ofs *sofs)
+{
+	struct kserdes_cmp_coef_ofs *ctofs;
+	struct kserdes_cmp_coef_ofs sample;
+	struct kserdes_lane_ofs *lofs;
+	u32 lane, cmp;
+
+	for_each_enable_lane(sc, lane) {
+		lofs = &sofs->lane_ofs[lane];
+		for_each_cmp(cmp) {
+			ctofs = &lofs->ct_ofs[cmp];
+
+			_kserdes_write_ofs_xge(sc->regs, lane, cmp, &sample);
+
+			ctofs->cmp  += sample.cmp;
+			ctofs->coef1 += sample.coef1;
+			ctofs->coef2 += sample.coef2;
+			ctofs->coef3 += sample.coef3;
+			ctofs->coef4 += sample.coef4;
+			ctofs->coef5 += sample.coef5;
+		}
+	}
+}
+
+static void
+kserdes_get_cmp_coef_ofs_non_xge(void __iomem *sregs, u32 lane, u32 cmp,
+				 struct kserdes_cmp_coef_ofs *ofs)
+{
+	FINSR(sregs, CML_REG(0x8c), 23, 21, cmp);
+	FINSR(sregs, CMU0_REG(0x8), 31, 24, ((lane + 1) << 5) + 0x12);
+	ofs->cmp = (_kserdes_read_tbus_val(sregs) & 0x0ff0) >> 4;
+}
+
+static void kserdes_add_ofs_non_xge(struct kserdes_config *sc,
+				    struct kserdes_ofs *sofs)
+{
+	struct kserdes_cmp_coef_ofs *ctofs;
+	struct kserdes_cmp_coef_ofs sample;
+	struct kserdes_lane_ofs *lofs;
+	u32 lane, cmp;
+
+	for_each_enable_lane(sc, lane) {
+		lofs = &sofs->lane_ofs[lane];
+		for_each_cmp(cmp) {
+			ctofs = &lofs->ct_ofs[cmp];
+
+			kserdes_get_cmp_coef_ofs_non_xge(sc->regs, lane,
+							 cmp, &sample);
+
+			ctofs->cmp  += sample.cmp;
+		}
+	}
+}
+
+static void kserdes_get_average_ofs(struct kserdes_config *sc, u32 samples,
+				    struct kserdes_ofs *sofs)
+{
+	struct kserdes_cmp_coef_ofs *ctofs;
+	struct kserdes_lane_ofs *lofs;
+	u32 i, lane, cmp;
+	int ret;
+
+	memset(sofs, 0, sizeof(*sofs));
+
+	for (i = 0; i < samples; i++) {
+		kserdes_assert_reset(sc);
+		ret = kserdes_deassert_reset(sc, 1);
+		if (ret) {
+			dev_err(sc->dev, "%s: reset failed %d\n",
+				__func__, ret);
+			return;
+		}
+
+		if (sc->phy_type == KSERDES_PHY_XGE)
+			kserdes_add_ofs_xge(sc, sofs);
+		else
+			kserdes_add_ofs_non_xge(sc, sofs);
+	}
+
+	for_each_enable_lane(sc, lane) {
+		lofs = &sofs->lane_ofs[lane];
+		for_each_cmp(cmp) {
+			ctofs = &lofs->ct_ofs[cmp];
+			if (sc->phy_type == KSERDES_PHY_XGE) {
+				ctofs->cmp  /= samples;
+				ctofs->coef1 /= samples;
+				ctofs->coef2 /= samples;
+				ctofs->coef3 /= samples;
+				ctofs->coef4 /= samples;
+				ctofs->coef5 /= samples;
+			} else {
+				ctofs->cmp  /= samples;
+			}
+		}
+	}
+}
+
+static void _kserdes_set_ofs(void __iomem *sregs, u32 lane, u32 cmp,
+			     struct kserdes_cmp_coef_ofs *ofs)
+{
+	FINSR(sregs, CML_REG(0xf0), 27, 26, (lane + 1));
+	FINSR(sregs, CML_REG(0x98), 24, 24, 0x1);
+	FINSR(sregs, LANEX_REG(lane, 0x2c), 2, 2, 0x1);
+	FINSR(sregs, LANEX_REG(lane, 0x30), 7, 5, cmp);
+	FINSR(sregs, LANEX_REG(lane, 0x5c), 31, 31, 0x1);
+	FINSR(sregs, CML_REG(0x9c), 7, 0, ofs->cmp);
+	FINSR(sregs, LANEX_REG(lane, 0x58), 30, 24, ofs->coef1);
+	FINSR(sregs, LANEX_REG(lane, 0x5c),  5,  0, ofs->coef2);
+	FINSR(sregs, LANEX_REG(lane, 0x5c), 13,  8, ofs->coef3);
+	FINSR(sregs, LANEX_REG(lane, 0x5c), 21, 16, ofs->coef4);
+	FINSR(sregs, LANEX_REG(lane, 0x5c), 29, 24, ofs->coef5);
+
+	FINSR(sregs, LANEX_REG(lane, 0x2c), 10, 10, 0x1);
+	FINSR(sregs, LANEX_REG(lane, 0x2c), 10, 10, 0x0);
+
+	FINSR(sregs, CML_REG(0x98), 24, 24, 0x0);
+	FINSR(sregs, LANEX_REG(lane, 0x2c), 2, 2, 0x0);
+	FINSR(sregs, LANEX_REG(lane, 0x5c), 31, 31, 0x0);
+}
+
+static inline void _kserdes_set_cmp_ofs_phyb(void __iomem *sregs, u32 lane,
+					     u32 cmp, u32 cmp_ofs)
+{
+	FINSR(sregs, LANEX_REG(lane, 0x58), 18, 18, 0x1);
+	FINSR(sregs, LANEX_REG(lane, 0x4c), 5, 2, (0x1 << (cmp - 1)));
+	FINSR(sregs, LANEX_REG(lane, 0x48), 24, 17, cmp_ofs);
+	FINSR(sregs, LANEX_REG(lane, 0x48), 29, 29, 0x1);
+	FINSR(sregs, LANEX_REG(lane, 0x48), 29, 29, 0x0);
+	FINSR(sregs, LANEX_REG(lane, 0x58), 18, 18, 0x0);
+}
+
+static inline void _kserdes_set_coef_ofs(void __iomem *sregs, u32 lane,
+					 u32 coef, u32 width, u32 coef_ofs)
+{
+	FINSR(sregs, LANEX_REG(lane, 0x58), 23, 19, (0x1 << (coef - 1)));
+	FINSR(sregs, LANEX_REG(lane, 0x48), 17 + (width - 1), 17, coef_ofs);
+	FINSR(sregs, LANEX_REG(lane, 0x48), 29, 29, 0x1);
+	FINSR(sregs, LANEX_REG(lane, 0x48), 29, 29, 0x0);
+}
+
+static void _kserdes_set_ofs_phyb(void __iomem *sregs, u32 lane, u32 cmp,
+				  struct kserdes_cmp_coef_ofs *ofs)
+{
+	FINSR(sregs, LANEX_REG(lane, 0x58), 16, 16, 0x1);
+	FINSR(sregs, LANEX_REG(lane, 0x48), 16, 16, 0x1);
+
+	_kserdes_set_cmp_ofs_phyb(sregs, lane, cmp, ofs->cmp);
+
+	FINSR(sregs, LANEX_REG(lane, 0x58), 17, 17, 0x1);
+
+	_kserdes_set_coef_ofs(sregs, lane, 1, 7, ofs->coef1);
+	_kserdes_set_coef_ofs(sregs, lane, 2, 6, ofs->coef2);
+	_kserdes_set_coef_ofs(sregs, lane, 3, 6, ofs->coef3);
+	_kserdes_set_coef_ofs(sregs, lane, 4, 6, ofs->coef4);
+	_kserdes_set_coef_ofs(sregs, lane, 5, 6, ofs->coef5);
+
+	FINSR(sregs, LANEX_REG(lane, 0x58), 16, 16, 0x0);
+	FINSR(sregs, LANEX_REG(lane, 0x48), 16, 16, 0x0);
+	FINSR(sregs, LANEX_REG(lane, 0x58), 18, 18, 0x0);
+	FINSR(sregs, LANEX_REG(lane, 0x58), 17, 17, 0x0);
+}
+
+static void kserdes_set_ofs_xge(struct kserdes_config *sc,
+				struct kserdes_ofs *sofs)
+{
+	struct kserdes_cmp_coef_ofs *ctofs;
+	struct kserdes_lane_ofs *lofs;
+	int lane, cmp;
+
+	for_each_enable_lane(sc, lane) {
+		lofs = &sofs->lane_ofs[lane];
+		for_each_cmp(cmp) {
+			ctofs = &lofs->ct_ofs[cmp];
+			_kserdes_set_ofs(sc->regs, lane, cmp, ctofs);
+			_kserdes_set_ofs_phyb(sc->regs, lane, cmp, ctofs);
+		}
+	}
+}
+
+static void kserdes_set_ofs_non_xge(struct kserdes_config *sc,
+				    struct kserdes_ofs *sofs)
+{
+	struct kserdes_cmp_coef_ofs *ctofs;
+	struct kserdes_lane_ofs *lofs;
+	u32 lane, cmp;
+
+	for_each_enable_lane(sc, lane) {
+		lofs = &sofs->lane_ofs[lane];
+		for_each_cmp(cmp) {
+			ctofs = &lofs->ct_ofs[cmp];
+			_kserdes_set_ofs(sc->regs, lane, cmp, ctofs);
+		}
+	}
+}
+
+static void kserdes_set_average_ofs(struct kserdes_config *sc,
+				    struct kserdes_ofs *sofs)
+{
+	if (sc->phy_type == KSERDES_PHY_XGE)
+		kserdes_set_ofs_xge(sc, sofs);
+	else
+		kserdes_set_ofs_non_xge(sc, sofs);
+}
+
+static void kserdes_phyb_init_config(struct kserdes_config *sc,
+				     struct kserdes_ofs *sofs)
+{
+	int lane;
+
+	for_each_enable_lane(sc, lane)
+		kserdes_force_signal_detect_low(sc, lane);
+
+	usleep_range(10, 20);
+
+	kserdes_get_average_ofs(sc, OFFSET_SAMPLES, sofs);
+	kserdes_set_average_ofs(sc, sofs);
+	usleep_range(10, 20);
+
+	for_each_enable_lane(sc, lane)
+		kserdes_force_signal_detect_high(sc, lane);
+
+	usleep_range(10, 20);
+}
+
+static int kserdes_wait_lane_rx_valid(struct kserdes_config *sc, u32 lane)
+{
+	unsigned long timeout = jiffies + msecs_to_jiffies(500);
+	unsigned long time_check;
+	u32 status;
+
+	do {
+		time_check = jiffies;
+		status = _kserdes_read_select_tbus(sc->regs, lane + 1, 0x02);
+
+		if (status & 0x20)
+			return 0;
+
+		if (time_after(time_check, timeout))
+			return -ETIMEDOUT;
+
+		cpu_relax();
+	} while (true);
+}
+
+static inline void _kserdes_reset(void __iomem *sregs)
+{
+	FINSR(sregs, CPU_CTRL_REG, 29, 29, 0x1);
+	usleep_range(10, 20);
+	FINSR(sregs, CPU_CTRL_REG, 29, 29, 0x0);
+	usleep_range(10, 20);
+}
+
+static inline void kserdes_xge_pll_enable(struct kserdes_config *sc)
+{
+	if (!sc->firmware)
+		FINSR(sc->regs, CML_REG(0), 7, 0, 0x1f);
+
+	if (sc->link_rate == KSERDES_LINK_RATE_10P3125G) {
+		_kserdes_pll_enable(sc->regs);
+		_kserdes_pll2_enable(sc->regs);
+	} else if (sc->link_rate == KSERDES_LINK_RATE_1P25G) {
+		kserdes_writel(sc->regs, PLL_CTRL_REG, PLL_ENABLE_1P25G);
+	}
+}
+
+static inline void _kserdes_enable_xgmii_port(struct regmap *peripheral_regmap,
+					      u32 port)
+{
+	regmap_update_bits(peripheral_regmap, XGE_CTRL_OFFSET,
+			   GENMASK(port, port), BIT(port));
+}
+
+static inline void _kserdes_reset_rx(void __iomem *sregs, int lane)
+{
+	_kserdes_force_signal_detect_low(sregs, lane);
+	usleep_range(1000, 2000);
+	_kserdes_force_signal_detect_high(sregs, lane);
+}
+
+static int kserdes_check_link_status(struct kserdes_config *sc,
+				     u32 *current_lane_state,
+				     u32 lanes_chk_mask,
+				     u32 *lanes_up_mask)
+{
+	struct device *dev = sc->dev;
+	u32 pcsr_rx_stat, blk_lock, blk_errs;
+	int loss, i, link_up = 1;
+	int ret;
+	unsigned long lmask = (unsigned long)lanes_chk_mask;
+
+	for_each_set_bit(i, &lmask, 8) {
+		loss = (kserdes_readl(sc->regs, LANE_CTRL_STS_REG(i))) & 0x01;
+
+		ret = regmap_read(sc->pcsr_regmap, PCSR_RX_STATUS(i),
+				  &pcsr_rx_stat);
+
+		if (ret)
+			return ret;
+
+		blk_lock = (pcsr_rx_stat >> 30) & 0x1;
+		blk_errs = (pcsr_rx_stat >> 16) & 0x0ff;
+
+		if (blk_errs)
+			blk_lock = 0;
+
+		switch (current_lane_state[i]) {
+		case 0:
+			if (!loss && blk_lock) {
+				dev_dbg(dev, "XGE PCSR Linked Lane: %d\n", i);
+				FINSR(sc->regs, LANEX_REG(i, 0x04), 2, 1, 0);
+				current_lane_state[i] = 1;
+			} else if (!blk_lock) {
+				dev_dbg(dev,
+					"XGE PCSR Recover Lane: %d\n", i);
+				_kserdes_reset_rx(sc->regs, i);
+			}
+			break;
+		case 1:
+			if (!blk_lock)
+				current_lane_state[i] = 2;
+
+			break;
+		case 2:
+			if (blk_lock) {
+				current_lane_state[i] = 1;
+			} else {
+				_kserdes_reset_rx(sc->regs, i);
+				current_lane_state[i] = 0;
+			}
+			break;
+		default:
+			dev_info(dev,
+				 "XGE: unknown current_lane_state[%d] %d\n",
+				 i, current_lane_state[i]);
+			break;
+		}
+
+		if (blk_errs) {
+			regmap_update_bits(sc->pcsr_regmap, PCSR_RX_CTL(i),
+					   GENMASK(7, 0), 0x19);
+			regmap_update_bits(sc->pcsr_regmap, PCSR_RX_CTL(i),
+					   GENMASK(7, 0), 0x00);
+		}
+
+		if (current_lane_state[i] == 1) {
+			*lanes_up_mask |= BIT(i);
+		} else {
+			*lanes_up_mask &= ~BIT(i);
+			link_up = 0;
+		}
+	}
+
+	return link_up;
+}
+
+static int kserdes_check_lanes_status(struct kserdes_config *sc);
+
+static int kserdes_wait_link_up(struct kserdes_config *sc,
+				u32 lanes_chk_mask,
+				u32 *lanes_up_mask)
+{
+	u32 current_state[KSERDES_MAX_LANES];
+	unsigned long time_check = 0;
+	int i, link_up, lane_status, ret = 0;
+
+	memset(current_state, 0, sizeof(current_state));
+
+	do {
+		usleep_range(10000, 20000);
+		if (sc->phy_type == KSERDES_PHY_XGE)
+			link_up = kserdes_check_link_status(sc, current_state,
+							    lanes_chk_mask,
+							    lanes_up_mask);
+		else {
+			lane_status = kserdes_check_lanes_status(sc);
+			if (lane_status)
+				link_up = 0;
+			else
+				link_up = 1;
+		}
+
+		if (link_up)
+			break;
+
+		for_each_enable_lane(sc, i) {
+			if (!(*lanes_up_mask & BIT(i))) {
+				dev_dbg(sc->dev,
+					"Lane %d down when waiting link up\n",
+					 i);
+			}
+		}
+
+		if (++time_check >= 200) {
+			ret = -ETIMEDOUT;
+			break;
+		}
+
+	} while (1);
+
+	return ret;
+}
+
+static inline void kserdes_xfw_get_lane_params(struct kserdes_config *sc,
+					       int lane)
+{
+	struct kserdes_fw_config *fw = &sc->fw;
+	u32 tx_ctrl, val_0, val_1;
+	u32 phy_a = PHY_A(sc->regs);
+
+	val_0 = kserdes_readl(sc->regs, LANEX_REG(lane, 0x04));
+	val_1 = kserdes_readl(sc->regs, LANEX_REG(lane, 0x08));
+
+	tx_ctrl = ((((val_0 >> 18) & 0x1)    << 24) |
+		   (((val_1 >> 0)  & 0xffff) <<  8) |
+		   (((val_0 >> 24) & 0xff)   <<  0));
+
+	if (phy_a) {
+		fw->cm = (val_1 >> 12) & 0xf;
+		fw->c1 = (val_1 >> 0) & 0x1f;
+		fw->c2 = (val_1 >> 8) & 0xf;
+	} else {
+		fw->cm = (tx_ctrl >> 16) & 0xf;
+		fw->c1 = (tx_ctrl >> 8) & 0x1f;
+		fw->c2 = (tx_ctrl >> 13) & 0x7;
+		fw->c2 = fw->c2 | (((tx_ctrl >> 24) & 0x1) << 3);
+	}
+
+	val_0 = _kserdes_read_select_tbus(sc->regs, lane + 1,
+					  (phy_a ? 0x11 : 0x10));
+	fw->attn = (val_0 >> 4) & 0xf;
+	fw->boost = (val_0 >> 8) & 0xf;
+
+	val_0 = _kserdes_read_select_tbus(sc->regs, lane + 1, 0x5);
+	fw->dlpf = (val_0 >> 2) & 0x3ff;
+
+	val_0 = _kserdes_read_select_tbus(sc->regs, lane + 1, 0x6);
+	fw->rxcal = (val_0 >> 3) & 0xff;
+}
+
+static inline void kserdes_xfw_mem_init(struct kserdes_config *sc)
+{
+	struct kserdes_fw_config *fw = &sc->fw;
+	u32 i, lane_config = 0;
+
+	for_each_lane(sc, i)
+		lane_config = (lane_config << 8) | (fw->lane_config[i] & 0xff);
+
+	lane_config <<= 8;
+
+	kserdes_writel(sc->regs, MEM_ADR_REG, KSERDES_XFW_CONFIG_START_ADDR);
+
+	for (i = KSERDES_XFW_CONFIG_START_ADDR;
+	     i < KSERDES_XFW_PARAM_START_ADDR; i += 4)
+		kserdes_writel(sc->regs, MEM_DATINC_REG, 0x00000000);
+
+	kserdes_writel(sc->regs, MEM_DATINC_REG, XFM_FLUSH_CMD);
+	kserdes_writel(sc->regs, MEM_DATINC_REG, fw->fast_train);
+	kserdes_writel(sc->regs, MEM_DATINC_REG, 0x00000000);
+	kserdes_writel(sc->regs, MEM_DATINC_REG, fw->lane_seeds);
+	kserdes_writel(sc->regs, MEM_DATINC_REG, lane_config);
+}
+
+static int kserdes_pcie_lanes_enable(struct kserdes_config *sc)
+{
+	int ret, i;
+	u32 lanes_enable = 0;
+
+	for_each_enable_lane(sc, i)
+		lanes_enable |= BIT(i);
+
+	for_each_lane(sc, i) {
+		kserdes_release_reset(sc, i);
+
+		if (sc->lane[i].loopback)
+			_kserdes_set_lane_loopback(sc->regs, i, sc->phy_type);
+	}
+
+	ret = kserdes_get_status(sc);
+	if (ret)
+		return ret;
+
+	return lanes_enable;
+}
+
+static void kserdes_clear_wait_after(struct kserdes_config *sc,
+				     unsigned long lanes_mask)
+{
+	u32 lane;
+
+	if (!sc->rx_force_enable) {
+		for_each_set_bit(lane, &lanes_mask, 8) {
+			if (!LANE_ENABLE(sc, lane))
+				continue;
+
+			_kserdes_clear_lane_wait_after(sc->regs, lane);
+		}
+	} else {
+		_kserdes_clear_wait_after(sc->regs);
+	}
+}
+
+static int kserdes_check_lanes_status(struct kserdes_config *sc)
+{
+	unsigned long timeout = jiffies + msecs_to_jiffies(500);
+	unsigned long time_check;
+	u32 val, i;
+
+	do {
+		time_check = jiffies;
+		val = 1;
+
+		for_each_enable_lane(sc, i)
+			val &= _kserdes_get_lane_status(sc->regs, i,
+							sc->phy_type);
+
+		if (val)
+			break;
+
+		if (time_after(time_check, timeout))
+			return -ETIMEDOUT;
+
+		cpu_relax();
+	} while (true);
+
+	return 0;
+}
+
+static void kserdes_phya_init_config(struct kserdes_config *sc, u32 lane)
+{
+	u32 coef1val, coef2val, coef3val, coef4val, coef5val;
+	void __iomem *sregs = sc->regs;
+	u32 cmp, coef1_ofs;
+
+	for_each_cmp(cmp) {
+		if (!(cmp & 0x1))
+			continue;
+
+		FINSR(sregs, CML_REG(0x8c), 23, 21, cmp);
+
+		FINSR(sregs, CMU0_REG(0x8), 31, 24, ((lane + 1) << 5) + 0x12);
+		coef1_ofs = (_kserdes_read_tbus_val(sregs) & 0x000f) << 3;
+
+		FINSR(sregs, CMU0_REG(0x8), 31, 24, ((lane + 1) << 5) + 0x13);
+		coef1_ofs |= (_kserdes_read_tbus_val(sregs) & 0x0e00) >> 9;
+
+		coef1val = coef1_ofs - 14;
+		coef2val = 31;
+		coef3val = 31;
+		coef4val = 31;
+		coef5val = 31;
+
+		FINSR(sregs, CML_REG(0xf0), 27, 26, lane + 1);
+		FINSR(sregs, LANEX_REG(lane, 0x2c), 2, 2, 0x1);
+		FINSR(sregs, LANEX_REG(lane, 0x30), 7, 5, cmp);
+		FINSR(sregs, LANEX_REG(lane, 0x5c), 31, 31, 0x1);
+
+		FINSR(sregs, LANEX_REG(lane, 0x58), 30, 24, coef1val);
+		FINSR(sregs, LANEX_REG(lane, 0x5c),  6,  0, coef2val);
+		FINSR(sregs, LANEX_REG(lane, 0x5c), 13,  8, coef3val);
+		FINSR(sregs, LANEX_REG(lane, 0x5c), 21, 16, coef4val);
+		FINSR(sregs, LANEX_REG(lane, 0x5c), 29, 24, coef5val);
+
+		FINSR(sregs, LANEX_REG(lane, 0x2c), 10, 10, 0x1);
+		FINSR(sregs, LANEX_REG(lane, 0x2c), 10, 10, 0x0);
+
+		FINSR(sregs, LANEX_REG(lane, 0x2c), 2, 2, 0x0);
+		FINSR(sregs, LANEX_REG(lane, 0x5c), 31, 31, 0x0);
+
+		FINSR(sregs, LANEX_REG(lane, 0x58), 16, 16, 0x1);
+		FINSR(sregs, LANEX_REG(lane, 0x48), 16, 16, 0x1);
+
+		FINSR(sregs, LANEX_REG(lane, 0x4c), 5, 2, (0x1 << (cmp - 1)));
+		FINSR(sregs, LANEX_REG(lane, 0x58), 17, 17, 0x1);
+
+		_kserdes_set_coef_ofs(sregs, lane, 1, 7, coef1val);
+		_kserdes_set_coef_ofs(sregs, lane, 2, 6, coef2val);
+		_kserdes_set_coef_ofs(sregs, lane, 3, 6, coef3val);
+		_kserdes_set_coef_ofs(sregs, lane, 4, 6, coef4val);
+		_kserdes_set_coef_ofs(sregs, lane, 5, 6, coef5val);
+
+		FINSR(sregs, LANEX_REG(lane, 0x58), 16, 16, 0x0);
+		FINSR(sregs, LANEX_REG(lane, 0x48), 16, 16, 0x0);
+		FINSR(sregs, LANEX_REG(lane, 0x58), 17, 17, 0x0);
+	}
+}
+
+static int kserdes_check_pll_status(struct kserdes_config *sc)
+{
+	unsigned long timeout = jiffies + msecs_to_jiffies(500);
+	unsigned long time_check;
+	u32 val;
+
+	do {
+		time_check = jiffies;
+		val = _kserdes_get_pll_status(sc->regs);
+
+		if (sc->phy_type == KSERDES_PHY_XGE)
+			val &= _kserdes_get_pll2_status(sc->regs);
+
+		if (val)
+			break;
+
+		if (time_after(time_check, timeout))
+			return -ETIMEDOUT;
+
+		cpu_relax();
+	} while (true);
+
+	return 0;
+}
+
+static void kserdes_enable_common_set_lane_rate(struct kserdes_config *sc,
+						u32 lane)
+{
+	int ret;
+
+	ret = _kserdes_set_lane_ctrl_rate(sc->regs, lane,
+					  sc->lane[lane].ctrl_rate);
+	if (ret) {
+		dev_err(sc->dev, "set_lane_rate FAILED: lane = %d err = %d\n",
+			lane, ret);
+		return;
+	}
+
+	switch (sc->phy_type) {
+	case KSERDES_PHY_SGMII:
+		FINSR(sc->regs, LANE_CTRL_STS_REG(lane),  7,  6, 0x3);
+		FINSR(sc->regs, LANE_CTRL_STS_REG(lane), 23, 21, 0x4);
+		FINSR(sc->regs, LANE_CTRL_STS_REG(lane),  5,  3, 0x4);
+		break;
+
+	case KSERDES_PHY_XGE:
+		FINSR(sc->regs, LANE_CTRL_STS_REG(lane), 23, 21, 0x7);
+		FINSR(sc->regs, LANE_CTRL_STS_REG(lane),  5,  3, 0x7);
+		if (sc->link_rate == KSERDES_LINK_RATE_10P3125G) {
+			FINSR(sc->regs, LANE_CTRL_STS_REG(lane), 16, 16, 0x1);
+			FINSR(sc->regs, LANE_CTRL_STS_REG(lane), 19, 19, 0x1);
+		}
+		break;
+
+	case KSERDES_PHY_PCIE:
+		break;
+
+	default:
+		FINSR(sc->regs, LANE_CTRL_STS_REG(lane), 23, 21, 0x6);
+		FINSR(sc->regs, LANE_CTRL_STS_REG(lane),  5,  3, 0x6);
+		break;
+	}
+
+	if (sc->lane[lane].loopback)
+		_kserdes_set_lane_loopback(sc->regs, lane, sc->phy_type);
+
+	if (sc->phy_type != KSERDES_PHY_XGE) {
+		FINSR(sc->regs, LANEX_REG(lane, 0x30), 11, 11, 0x1);
+		FINSR(sc->regs, LANEX_REG(lane, 0x30), 13, 12, 0x0);
+	}
+}
+
+static inline void kserdes_set_lane_rx_starts(struct kserdes_config *sc,
+					      u32 lane)
+{
+	FINSR(sc->regs, LANEX_REG(lane, 0x8c), 11, 8,
+	      sc->lane[lane].rx_start.att);
+	FINSR(sc->regs, LANEX_REG(lane, 0x8c), 15, 12,
+	      sc->lane[lane].rx_start.boost);
+
+	FINSR(sc->regs, LANEX_REG(lane, 0x84), 27, 24,
+	      sc->lane[lane].rx_start.att);
+	FINSR(sc->regs, LANEX_REG(lane, 0x84), 31, 28,
+	      sc->lane[lane].rx_start.boost);
+
+	FINSR(sc->regs, LANEX_REG(lane, 0x84), 19, 16,
+	      sc->lane[lane].rx_start.att);
+	FINSR(sc->regs, LANEX_REG(lane, 0x84), 23, 20,
+	      sc->lane[lane].rx_start.boost);
+}
+
+static void kserdes_hs_init_config(struct kserdes_config *sc)
+{
+	int i;
+
+	if (sc->phy_type != KSERDES_PHY_XGE) {
+		if (sc->link_rate >= KSERDES_LINK_RATE_9P8304G)
+			FINSR(sc->regs, CML_REG(0xbc), 28, 24, 0x1e);
+	}
+
+	for_each_enable_lane(sc, i)
+		kserdes_set_tx_idle(sc, i);
+
+	if (sc->link_rate >= KSERDES_LINK_RATE_9P8304G) {
+		if (sc->phy_type != KSERDES_PHY_XGE) {
+			for_each_enable_lane(sc, i)
+				kserdes_force_signal_detect_low(sc, i);
+
+			for_each_enable_lane(sc, i)
+				FINSR(sc->regs, LANEX_REG(i, 0x78),
+				      30, 24, 0x7f);
+		} else {
+			FINSR(sc->regs, CML_REG(0x10c), 7, 0, 0xff);
+		}
+	}
+}
+
+static int kserdes_lanes_enable_common(struct kserdes_config *sc,
+				       struct kserdes_ofs *sofs)
+{
+	u32 val, lane_mask = 0;
+	int i, ret;
+
+	for_each_lane(sc, i) {
+		if (sc->lane[i].enable)
+			lane_mask |= BIT(i);
+		else
+			sc->lane[i].enable = 1;
+	}
+
+	if (sc->phy_type == KSERDES_PHY_PCIE) {
+		dev_err(sc->dev, "%s: pcie TBD.\n", __func__);
+		return -EINVAL;
+	}
+
+	kserdes_hs_init_config(sc);
+
+	for_each_enable_lane(sc, i)
+		kserdes_set_lane_rx_starts(sc, i);
+
+	kserdes_assert_reset(sc);
+
+	for_each_enable_lane(sc, i)
+		kserdes_set_tx_rx_fir_coeff(sc, i);
+
+	for_each_enable_lane(sc, i)
+		kserdes_force_signal_detect_low(sc, i);
+
+	ret = kserdes_deassert_reset(sc, 0);
+	if (ret) {
+		dev_err(sc->dev, "kserdes_deassert_reset FAILED %d\n", ret);
+		return ret;
+	}
+
+	for_each_enable_lane(sc, i)
+		kserdes_enable_common_set_lane_rate(sc, i);
+
+	if (sc->phy_type == KSERDES_PHY_XGE)
+		kserdes_xge_pll_enable(sc);
+	else
+		_kserdes_pll_enable(sc->regs);
+
+	ret = kserdes_check_pll_status(sc);
+	if (ret) {
+		dev_err(sc->dev,
+			"common init: check pll status FAILED %d\n", ret);
+		return ret;
+	}
+
+	for_each_enable_lane(sc, i)
+		_kserdes_lane_enable(sc->regs, i);
+
+	ret = kserdes_check_lanes_status(sc);
+	if (ret) {
+		dev_err(sc->dev,
+			"common init: check lanes status FAILED %d\n", ret);
+		return ret;
+	}
+
+	usleep_range(5, 10);
+
+	val = _kserdes_get_tx_termination(sc->regs, sc->phy_type);
+
+	kserdes_set_tx_terminations(sc, val);
+
+	if (sc->phy_type == KSERDES_PHY_XGE)
+		kserdes_phyb_init_config(sc, sofs);
+	else if (sc->link_rate >= KSERDES_LINK_RATE_9P8304G)
+		for_each_enable_lane(sc, i)
+			kserdes_phya_init_config(sc, i);
+
+	for_each_enable_lane(sc, i)
+		kserdes_clr_tx_idle(sc, i);
+
+	for_each_lane(sc, i)
+		sc->lane[i].enable = (lane_mask & BIT(i)) >> i;
+
+	return 0;
+}
+
+static inline u32 _kserdes_get_lane_sd(void __iomem *sregs, u32 lane)
+{
+	return FEXTR(kserdes_readl(sregs, PLL_CTRL_REG), lane, lane);
+}
+
+static int _kserdes_wait_lane_sd(void __iomem *sregs, u32 lane)
+{
+	unsigned long timeout = jiffies + msecs_to_jiffies(500);
+	unsigned long time_check;
+
+	do {
+		time_check = jiffies;
+
+		if (_kserdes_get_lane_sd(sregs, lane))
+			break;
+
+		if (time_after(time_check, timeout))
+			return -ETIMEDOUT;
+
+		cpu_relax();
+	} while (true);
+
+	return 0;
+}
+
+static void kserdes_rx_att_boost_config_phyb(struct kserdes_config *sc,
+					     u32 lane)
+{
+	u32 tbus_ofs, rxeq_init_reg_ofs, rxeq_ln_reg_ofs, rxeq_ln_force_bit;
+	void __iomem *sregs = sc->regs;
+	u32 att_start, att_read, boost_read;
+	int ret;
+
+	if (sc->phy_type == KSERDES_PHY_XGE) {
+		tbus_ofs = 0x10;
+		rxeq_init_reg_ofs = 0x9c;
+		rxeq_ln_reg_ofs = 0x98;
+		rxeq_ln_force_bit = 14;
+	} else {
+		tbus_ofs = 0x11;
+		rxeq_init_reg_ofs = 0x84;
+		rxeq_ln_reg_ofs = 0xac;
+		rxeq_ln_force_bit = 11;
+	}
+
+	att_start = kserdes_readl(sregs, LANEX_REG(lane, 0x8c));
+	att_start = (att_start >> 8) & 0xf;
+
+	att_read = _kserdes_read_select_tbus(sregs, lane + 1, tbus_ofs);
+	att_read = (att_read >> 4) & 0xf;
+
+	FINSR(sregs, LANEX_REG(lane, 0x8c), 11, 8, att_read);
+	FINSR(sregs, LANEX_REG(lane, rxeq_init_reg_ofs), 0, 0, 0x0);
+	FINSR(sregs, CML_REG(0x8c), 24, 24, 0x0);
+
+	FINSR(sregs, LANEX_REG(lane, rxeq_ln_reg_ofs),
+	      rxeq_ln_force_bit, rxeq_ln_force_bit, 0x1);
+	FINSR(sregs, LANEX_REG(lane, rxeq_ln_reg_ofs),
+	      rxeq_ln_force_bit, rxeq_ln_force_bit, 0x0);
+
+	ret = kserdes_wait_lane_rx_valid(sc, lane);
+	if (ret) {
+		dev_dbg(sc->dev, "kserdes_wait_lane_rx_valid %d FAILED: %d\n",
+			lane, ret);
+	}
+	usleep_range(300, 600);
+
+	boost_read = _kserdes_read_select_tbus(sregs, lane + 1, tbus_ofs);
+	boost_read = (boost_read >> 8) & 0xf;
+
+	if (!boost_read) {
+		FINSR(sregs, LANEX_REG(lane, 0x2c),  2,  2, 0x1);
+		FINSR(sregs, LANEX_REG(lane, 0x2c), 18, 12, 0x2);
+		FINSR(sregs, LANEX_REG(lane, 0x2c),  9,  3, 0x1);
+		FINSR(sregs, LANEX_REG(lane, 0x2c), 10, 10, 0x1);
+		FINSR(sregs, LANEX_REG(lane, 0x2c), 10, 10, 0x0);
+		FINSR(sregs, LANEX_REG(lane, 0x2c),  2,  2, 0x0);
+		FINSR(sregs, LANEX_REG(lane, 0x2c), 18, 12, 0x0);
+		FINSR(sregs, LANEX_REG(lane, 0x2c),  9,  3, 0x0);
+	}
+
+	FINSR(sregs, LANEX_REG(lane, 0x8c), 11, 8, att_start);
+	FINSR(sregs, LANEX_REG(lane, rxeq_init_reg_ofs), 0, 0, 0x1);
+	FINSR(sregs, CML_REG(0x8c), 24, 24, 0x1);
+}
+
+static int kserdes_set_dlev_patt_adapt(struct kserdes_config *sc,
+				       u32 lane, u32 pattern,
+				       struct kserdes_lane_ofs *lofs)
+{
+	struct kserdes_cmp_coef_ofs *ctofs = &lofs->ct_ofs[4];
+	void __iomem *sregs = sc->regs;
+	u32 dlevp, dlevn, dlevavg;
+	int ret;
+
+	FINSR(sregs, CML_REG(0x158), 14, 8, pattern);
+	FINSR(sregs, LANEX_REG(lane, 0x98), 14, 14, 1);
+	FINSR(sregs, LANEX_REG(lane, 0x98), 14, 14, 0);
+
+	ret = kserdes_wait_lane_rx_valid(sc, lane);
+	if (ret) {
+		dev_dbg(sc->dev,
+			"set dlev patt: wait_lane_rx_valid FAILED %d\n", ret);
+		return ret;
+	}
+
+	dlevp = _kserdes_read_select_tbus(sregs, lane + 1, 0x44);
+	dlevp = (dlevp >> 4) & 0xff;
+
+	dlevn = _kserdes_read_select_tbus(sregs, lane + 1, 0x45);
+	dlevn &= 0xff;
+
+	if (ctofs->cmp <= 120)
+		dlevavg = ctofs->cmp - dlevn;
+	else if (ctofs->cmp >= 134)
+		dlevavg = dlevp - ctofs->cmp;
+	else
+		dlevavg = (dlevp - dlevn) / 2;
+
+	return dlevavg;
+}
+
+static u32 kserdes_eye_monitor_dll_ovr(struct kserdes_config *sc,
+				       u32 lane, u32 phase_num, u32 t_offset,
+				       u32 phase_shift)
+{
+	void __iomem *sregs = sc->regs;
+	u32 tbus_data, delay, partial_eye, i;
+	u32 start_bin = 0, end_bin = 0;
+	u32 eye_scan_errors_array[128];
+	u32 error_free = 0, val_0, val_1;
+	u32 max_dly = 128;
+	bool not_phy_xge = (sc->phy_type != KSERDES_PHY_XGE);
+
+	if (t_offset == 0)
+		t_offset++;
+
+	if (phase_num == 1) {
+		val_0 = 0x00400000;
+		val_1 = 0x00000011;
+	} else {
+		val_0 = 0x00800000;
+		val_1 = 0x00000009;
+	}
+	reg_rmw(sregs + LANEX_REG(lane, 0x2c), val_0, GENMASK(23, 22));
+	reg_rmw(sregs + LANEX_REG(lane, 0x30), val_1, GENMASK(4, 0));
+
+	reg_rmw(sregs + CML_REG(0xb8), 0x00004000, GENMASK(15, 8));
+
+	if (phase_num == 1)
+		val_0 = 0xffef0000;
+	else
+		val_0 = 0xfff60000;
+
+	reg_rmw(sregs + CML_REG(0xb8), val_0, GENMASK(31, 16));
+
+	reg_rmw(sregs + CML_REG(0xbc), 0x000fffff, GENMASK(19, 0));
+	tbus_data = _kserdes_read_select_tbus(sregs, lane + 1, 0x02);
+	tbus_data = tbus_data;
+	usleep_range(250, 500);
+
+	for (i = 0; i < max_dly; i = i + t_offset) {
+		reg_rmw(sregs + LANEX_REG(lane, 0x2c),
+			(i & 0xff) << 24, GENMASK(31, 24));
+
+		reg_rmw(sregs + LANEX_REG(lane, 0x30),
+			phase_shift, GENMASK(1, 0));
+		usleep_range(5, 10);
+		reg_rmw(sregs + CML_REG(0xb8), 0x0000c000, GENMASK(15, 8));
+		usleep_range(500, 1000);
+
+		val_0 = _kserdes_read_select_tbus(sregs, lane + 1,
+						  not_phy_xge ?  0x1a : 0x19);
+		val_0 <<= 4;
+
+		val_1 = _kserdes_read_select_tbus(sregs, lane + 1,
+						  not_phy_xge ?  0x1b : 0x1a);
+		val_1 = (val_1 >> 8) & 0xf;
+
+		eye_scan_errors_array[i] = (val_0 | val_1);
+
+		reg_rmw(sregs + CML_REG(0xb8), 0x00004000, GENMASK(15, 8));
+	}
+
+	partial_eye = 0;
+	error_free = 0;
+
+	for (i = 0; i < max_dly; i = i + t_offset) {
+		if (i == 0) {
+			if (eye_scan_errors_array[i] < 16384)
+				partial_eye = 1;
+		} else {
+			if (eye_scan_errors_array[i] > 16384 + 3000)
+				partial_eye = 0;
+		}
+
+		if ((eye_scan_errors_array[i] < 16384) &&
+		    (partial_eye == 0) &&
+		    (error_free  == 0)) {
+			if (!((eye_scan_errors_array[i - 1] > 16384) &&
+			      (eye_scan_errors_array[i + 1] > 16384))) {
+				error_free = 1;
+				start_bin = i;
+			}
+		} else if ((eye_scan_errors_array[i] > 16384) &&
+			   (partial_eye == 0) &&
+			   (error_free  == 1)) {
+			if (!((eye_scan_errors_array[i - 1] < 16384) &&
+			      (eye_scan_errors_array[i + 1] < 16384))) {
+				end_bin = i;
+				break;
+			}
+		}
+	}
+
+	delay = (end_bin - start_bin) / 4 + start_bin;
+	reg_rmw(sregs + LANEX_REG(lane, 0x30), 0x00000000, GENMASK(7, 0));
+	reg_rmw(sregs + LANEX_REG(lane, 0x2c), 0x00000003, GENMASK(7, 0));
+	reg_rmw(sregs + CML_REG(0x98), 0x00000000, GENMASK(31, 0));
+	reg_rmw(sregs + CML_REG(0xb8), 0x00000000, GENMASK(15, 14));
+	reg_rmw(sregs + LANEX_REG(lane, 0x2c), 0x00000000, GENMASK(23, 22));
+	return delay;
+}
+
+static void kserdes_rx_calibration_phyb(struct kserdes_config *sc, u32 lane,
+					struct kserdes_lane_ofs *lofs,
+					struct kserdes_lane_dlev_out *ldlevo)
+{
+	struct kserdes_cmp_coef_ofs *ctofs, *ctofs_temp;
+	struct kserdes_lane_ofs lofs_temp;
+	void __iomem *sregs = sc->regs;
+	u32 att, boost, comp_no, att_start, boost_start;
+	u32 delay_ovr = 0;
+	int dlevavg_temp[6];
+
+	delay_ovr = kserdes_eye_monitor_dll_ovr(sc, lane, 0, 1, 0);
+	ldlevo->delay = delay_ovr;
+
+	FINSR(sregs, CML_REG(0x164), 15, 15, 1);
+
+	FINSR(sregs, CML_REG(0x164), 16, 16, 1);
+	FINSR(sregs, CML_REG(0x164), 31, 26, (128 + delay_ovr) & 0x3f);
+	FINSR(sregs, CML_REG(0x168),  2,  0, (128 + delay_ovr) >> 6);
+	FINSR(sregs, LANEX_REG(lane, 0x9c), 1, 1, 0);
+	FINSR(sregs, LANEX_REG(lane, 0x9c), 0, 0, 0);
+
+	att_start = (kserdes_readl(sregs, LANEX_REG(lane, 0x8c)) >> 8) & 0xf;
+	boost_start = (kserdes_readl(sregs, LANEX_REG(lane, 0x8c)) >> 12) & 0xf;
+
+	att = _kserdes_read_select_tbus(sregs, lane + 1, 0x10);
+	boost = (att >> 8) & 0xf;
+	att = (att >> 4) & 0xf;
+
+	FINSR(sregs, LANEX_REG(lane, 0x8c), 11, 8, att);
+	FINSR(sregs, LANEX_REG(lane, 0x8c), 15, 12, boost);
+
+	dlevavg_temp[0] = kserdes_set_dlev_patt_adapt(sc, lane, 0x71, lofs);
+	dlevavg_temp[1] = kserdes_set_dlev_patt_adapt(sc, lane, 0x61, lofs);
+	dlevavg_temp[2] = kserdes_set_dlev_patt_adapt(sc, lane, 0x79, lofs);
+	dlevavg_temp[3] = kserdes_set_dlev_patt_adapt(sc, lane, 0x75, lofs);
+	dlevavg_temp[4] = kserdes_set_dlev_patt_adapt(sc, lane, 0x73, lofs);
+	dlevavg_temp[5] = kserdes_set_dlev_patt_adapt(sc, lane, 0x70, lofs);
+
+	ldlevo->coef_vals[0] = (dlevavg_temp[0] - dlevavg_temp[1]) /  2;
+	ldlevo->coef_vals[1] = (dlevavg_temp[0] - dlevavg_temp[2]) / -2;
+	ldlevo->coef_vals[2] = (dlevavg_temp[0] - dlevavg_temp[3]) / -2;
+	ldlevo->coef_vals[3] = (dlevavg_temp[0] - dlevavg_temp[4]) / -2;
+	ldlevo->coef_vals[4] = (dlevavg_temp[0] - dlevavg_temp[5]) /  2;
+
+	ldlevo->coef_vals[0] = ldlevo->coef_vals[0] -
+					ldlevo->coef_vals[0] / 3;
+
+	for (comp_no = 1; comp_no < 5; comp_no++) {
+		ctofs = &lofs->ct_ofs[comp_no];
+		ctofs_temp = &lofs_temp.ct_ofs[comp_no];
+
+		ctofs_temp->cmp = ctofs->cmp;
+
+		if ((comp_no == 1) || (comp_no == 3)) {
+			ctofs_temp->coef1 = ldlevo->coef_vals[0] + ctofs->coef1;
+			ctofs_temp->coef2 = ldlevo->coef_vals[1] + ctofs->coef2;
+			ctofs_temp->coef3 = ldlevo->coef_vals[2] + ctofs->coef3;
+			ctofs_temp->coef4 = ldlevo->coef_vals[3] + ctofs->coef4;
+			ctofs_temp->coef5 = ldlevo->coef_vals[4] + ctofs->coef5;
+		} else {
+			ctofs_temp->coef1 = ctofs->coef1;
+			ctofs_temp->coef2 = ctofs->coef2;
+			ctofs_temp->coef3 = ctofs->coef3;
+			ctofs_temp->coef4 = ctofs->coef4;
+			ctofs_temp->coef5 = ctofs->coef5;
+		}
+
+		_kserdes_set_ofs(sregs, lane, comp_no, ctofs_temp);
+	}
+
+	FINSR(sregs, LANEX_REG(lane, 0x8c), 11, 8, att_start);
+	FINSR(sregs, LANEX_REG(lane, 0x8c), 15, 12, boost_start);
+	FINSR(sregs, LANEX_REG(lane, 0x9c), 1, 1, 1);
+	FINSR(sregs, LANEX_REG(lane, 0x9c), 0, 0, 1);
+}
+
+static int kserdes_rx_boost_config_phya(struct kserdes_config *sc, u32 lane)
+{
+	u32 boost_read;
+	int ret;
+	bool phy_xge = (sc->phy_type == KSERDES_PHY_XGE);
+
+	ret = kserdes_wait_lane_rx_valid(sc, lane);
+	if (ret) {
+		dev_err(sc->dev,
+			"config_phya: wait_lane_rx_valid FAILED %d\n", ret);
+		return ret;
+	}
+
+	boost_read = _kserdes_read_select_tbus(sc->regs, lane + 1,
+					       phy_xge ?  0x10 : 0x11);
+
+	boost_read = (boost_read >> 8) & 0xf;
+
+	if (!boost_read) {
+		FINSR(sc->regs, LANEX_REG(lane, 0x2c),  2,  2, 0x1);
+		FINSR(sc->regs, LANEX_REG(lane, 0x2c), 18, 12, 0x2);
+		FINSR(sc->regs, LANEX_REG(lane, 0x2c),  9,  3, 0x1);
+		FINSR(sc->regs, LANEX_REG(lane, 0x2c), 10, 10, 0x1);
+		FINSR(sc->regs, LANEX_REG(lane, 0x2c), 10, 10, 0x0);
+		FINSR(sc->regs, LANEX_REG(lane, 0x2c),  2,  2, 0x0);
+		FINSR(sc->regs, LANEX_REG(lane, 0x2c), 18, 12, 0x0);
+		FINSR(sc->regs, LANEX_REG(lane, 0x2c),  9,  3, 0x0);
+	}
+	return 0;
+}
+
+static int kserdes_enable_lane_rx(struct kserdes_config *sc, u32 lane,
+				  struct kserdes_lane_ofs *lofs,
+				  struct kserdes_lane_dlev_out *ldlevo)
+{
+	int ret = 0;
+
+	_kserdes_force_signal_detect_high(sc->regs, lane);
+
+	if (!sc->rx_force_enable) {
+		ret = _kserdes_wait_lane_sd(sc->regs, lane);
+		if (ret) {
+			dev_err(sc->dev,
+				"init_lane_rx wait sd valid FAILED %d\n", ret);
+			return ret;
+		}
+
+		if ((sc->phy_type == KSERDES_PHY_XGE) ||
+		    (sc->link_rate >= KSERDES_LINK_RATE_5G)) {
+			if (sc->lane[lane].ctrl_rate == KSERDES_FULL_RATE) {
+				ret = kserdes_wait_lane_rx_valid(sc, lane);
+				if (ret) {
+					dev_err(sc->dev,
+						"init_lane_rx wait rx valid FAILED %d\n",
+					       ret);
+					return ret;
+				}
+			}
+		}
+
+		if (sc->phy_type == KSERDES_PHY_XGE) {
+			kserdes_rx_att_boost_config_phyb(sc, lane);
+		} else if ((sc->link_rate >= KSERDES_LINK_RATE_5G) &&
+			   (sc->lane[lane].ctrl_rate == KSERDES_FULL_RATE)) {
+			kserdes_rx_boost_config_phya(sc, lane);
+		}
+	}
+
+	if (sc->phy_type == KSERDES_PHY_XGE)
+		kserdes_rx_calibration_phyb(sc, lane, lofs, ldlevo);
+
+	return ret;
+}
+
+static int xge_kserdes_recover_lane_rx(struct kserdes_config *sc, u32 lane,
+				   struct kserdes_lane_ofs *lofs,
+				   struct kserdes_lane_dlev_out *ldlevo)
+{
+	int ret;
+
+	_kserdes_force_signal_detect_high(sc->regs, lane);
+
+	if (!sc->rx_force_enable) {
+		ret = _kserdes_wait_lane_sd(sc->regs, lane);
+		if (ret) {
+			dev_dbg(sc->dev,
+				"init_lane_rx wait sd valid FAILED %d\n", ret);
+			return ret;
+		}
+		dev_dbg(sc->dev, "recover_lane_rx sig detcected\n");
+
+		if ((sc->phy_type == KSERDES_PHY_XGE) ||
+		    (sc->link_rate >= KSERDES_LINK_RATE_5G)) {
+			if (sc->lane[lane].ctrl_rate == KSERDES_FULL_RATE) {
+				ret = kserdes_wait_lane_rx_valid(sc, lane);
+				if (ret) {
+					dev_err(sc->dev,
+						"init_lane_rx wait rx valid FAILED %d\n",
+					       ret);
+					return ret;
+				}
+				dev_dbg(sc->dev, "recover_lane_rx rx valid\n");
+			}
+		}
+
+		if (sc->phy_type == KSERDES_PHY_XGE) {
+			kserdes_rx_att_boost_config_phyb(sc, lane);
+		} else if ((sc->link_rate >= KSERDES_LINK_RATE_5G) &&
+			   (sc->lane[lane].ctrl_rate == KSERDES_FULL_RATE)) {
+			kserdes_rx_boost_config_phya(sc, lane);
+		}
+	}
+
+	if (sc->phy_type == KSERDES_PHY_XGE)
+		kserdes_rx_calibration_phyb(sc, lane, lofs, ldlevo);
+
+	return 0;
+}
+
+static int gbe_kserdes_recover_lane_rx(struct kserdes_config *sc, u32 lane,
+				       struct kserdes_lane_ofs *lofs,
+				       struct kserdes_lane_dlev_out *ldlevo)
+{
+	int ret;
+
+	_kserdes_force_signal_detect_high(sc->regs, lane);
+
+	if (!sc->rx_force_enable) {
+		ret = _kserdes_wait_lane_sd(sc->regs, lane);
+		if (ret) {
+			dev_dbg(sc->dev,
+				"init_lane_rx wait sd valid FAILED %d\n", ret);
+			return ret;
+		}
+		dev_dbg(sc->dev, "recover_lane_rx sig detcected\n");
+
+		if (sc->lane[lane].ctrl_rate == KSERDES_QUARTER_RATE) {
+			ret = kserdes_wait_lane_rx_valid(sc, lane);
+			if (ret) {
+				dev_err(sc->dev,
+					"init_lane_rx wait rx valid FAILED %d\n",
+					ret);
+				return ret;
+			}
+			dev_dbg(sc->dev, "recover_lane_rx rx valid\n");
+		}
+	}
+
+	return 0;
+}
+
+static int kserdes_sgmii_init(struct kserdes_config *sc)
+{
+	return kserdes_load_init_fw(sc, ks2_gbe_serdes_firmwares,
+				    ARRAY_SIZE(ks2_gbe_serdes_firmwares));
+}
+
+static int kserdes_xge_init(struct kserdes_config *sc)
+{
+	_kserdes_reset(sc->regs);
+	return kserdes_load_init_fw(sc, ks2_xgbe_serdes_firmwares,
+				    ARRAY_SIZE(ks2_xgbe_serdes_firmwares));
+}
+
+static int kserdes_pcie_init(struct kserdes_config *sc)
+{
+	return kserdes_load_init_fw(sc, ks2_pcie_serdes_firmwares,
+				    ARRAY_SIZE(ks2_pcie_serdes_firmwares));
+}
+
+static int kserdes_of_parse_lane(struct device *dev,
+				 struct device_node *np,
+				 struct kserdes_config *sc)
+{
+	struct kserdes_lane_config *lc;
+	struct kserdes_equalizer *eq;
+	struct kserdes_tx_coeff *tc;
+	int lane_num, ret;
+
+	ret = of_property_read_u32(np, "reg", &lane_num);
+	if (ret) {
+		dev_err(dev, "Failed to parse reg\n");
+		return -EINVAL;
+	}
+
+	if (lane_num >= sc->lanes) {
+		dev_err(dev, "Invalid lane number %u\n", lane_num);
+		return -EINVAL;
+	}
+
+	lc = &sc->lane[lane_num];
+
+	lc->enable = of_device_is_available(np);
+	dev_dbg(dev, "lane %d enabled\n", lane_num);
+
+	if (of_property_read_u32(np, "control-rate", &lc->ctrl_rate)) {
+		dev_info(dev, "use default lane control-rate: %u\n",
+			 lc->ctrl_rate);
+	}
+	dev_dbg(dev, "lane control-rate: %d\n", lc->ctrl_rate);
+
+	if (of_find_property(np, "loopback", NULL))
+		lc->loopback = true;
+	else
+		lc->loopback = false;
+
+	dev_dbg(dev, "lane loopback: %d\n", lc->loopback);
+
+	eq = &lc->rx_start;
+	if (of_property_read_u32_array(np, "rx-start", &eq->att, 2)) {
+		dev_info(dev, "use default lane rx-start 0 0\n");
+		eq->att = 0;
+		eq->boost = 0;
+	}
+	dev_dbg(dev, "lane rx-start: %d %d\n", eq->att, eq->boost);
+
+	eq = &lc->rx_force;
+	if (of_property_read_u32_array(np, "rx-force", &eq->att, 2)) {
+		dev_info(dev, "use default lane rx-force 0 0\n");
+		eq->att = 0;
+		eq->boost = 0;
+	}
+	dev_dbg(dev, "lane rx-force: %d %d\n", eq->att, eq->boost);
+
+	tc = &lc->tx_coeff;
+	if (of_property_read_u32_array(np, "tx-coeff", &tc->c1, 5)) {
+		dev_info(dev, "use default tx-coeff 0\n");
+		tc->c1 = 0;
+	}
+	dev_dbg(dev, "tx-coeff: %d %d %d %d %d\n",
+		tc->c1, tc->c2, tc->cm, tc->att, tc->vreg);
+
+	return lane_num;
+}
+
+static void kserdes_set_sgmii_defaults(struct kserdes_config *sc)
+{
+	int i;
+
+	sc->link_rate		= KSERDES_LINK_RATE_1P25G;
+	sc->lanes		= 4;
+	sc->rx_force_enable	= false;
+
+	for_each_lane(sc, i) {
+		memset(&sc->lane[i], 0, sizeof(sc->lane[i]));
+		sc->lane[i].ctrl_rate = KSERDES_QUARTER_RATE;
+	}
+}
+
+static void kserdes_set_xge_defaults(struct kserdes_config *sc)
+{
+	int i;
+
+	sc->link_rate		= KSERDES_LINK_RATE_10P3125G;
+	sc->lanes		= 2;
+	sc->rx_force_enable	= false;
+
+	for_each_lane(sc, i) {
+		memset(&sc->lane[i], 0, sizeof(sc->lane[i]));
+		sc->lane[i].ctrl_rate = KSERDES_FULL_RATE;
+	}
+}
+
+static void kserdes_set_pcie_defaults(struct kserdes_config *sc)
+{
+	int i;
+
+	sc->link_rate		= KSERDES_LINK_RATE_5G;
+	sc->lanes		= 2;
+	sc->rx_force_enable	= false;
+
+	for_each_lane(sc, i)
+		memset(&sc->lane[i], 0, sizeof(sc->lane[i]));
+}
+
+static void kserdes_set_defaults(struct kserdes_config *sc,
+				 enum kserdes_phy_type phy_type)
+{
+	switch (phy_type) {
+	case KSERDES_PHY_SGMII:
+		kserdes_set_sgmii_defaults(sc);
+		break;
+	case KSERDES_PHY_XGE:
+		kserdes_set_xge_defaults(sc);
+		break;
+	case KSERDES_PHY_PCIE:
+		kserdes_set_pcie_defaults(sc);
+		break;
+	default:
+		break;
+	}
+}
+
+static const struct of_device_id kserdes_of_match[] = {
+	{ .compatible	= "ti,keystone-serdes-gbe",
+	  .data		= &kserdes_devtype[KSERDES_PHY_SGMII], },
+
+	{ .compatible	= "ti,keystone-serdes-xgbe",
+	  .data		= &kserdes_devtype[KSERDES_PHY_XGE], },
+
+	{ .compatible	= "ti,keystone-serdes-pcie",
+	  .data		= &kserdes_devtype[KSERDES_PHY_PCIE], },
+
+	{ },
+};
+MODULE_DEVICE_TABLE(of, kserdes_of_match);
+
+static int kserdes_phy_enable_rx(struct phy *phy)
+{
+	struct kserdes_phy *ks_phy = phy_get_drvdata(phy);
+	struct kserdes_dev *sd = dev_get_drvdata(phy->dev.parent);
+	struct kserdes_config *sc = &sd->sc;
+	struct kserdes_ofs *sofs = &sc->sofs;
+	struct kserdes_dlev_out dlevo;
+	u32 lanes_up_map = 0;
+	u32 i = ks_phy->lane;
+	int ret;
+
+	ret = kserdes_enable_lane_rx(sc, i, &sofs->lane_ofs[i],
+				     &dlevo.lane_dlev_out[i]);
+
+	kserdes_clear_wait_after(sc, BIT(i));
+
+	if (sc->phy_type == KSERDES_PHY_XGE) {
+		_kserdes_enable_xgmii_port(sc->peripheral_regmap, i);
+		kserdes_wait_link_up(sc, BIT(i), &lanes_up_map);
+	}
+
+	return 0;
+}
+
+static int kserdes_phy_reset(struct phy *phy)
+{
+	struct kserdes_phy *ks_phy = phy_get_drvdata(phy);
+	struct kserdes_dev *sd = dev_get_drvdata(phy->dev.parent);
+	struct kserdes_config *sc = &sd->sc;
+	struct kserdes_ofs *sofs = &sc->sofs;
+	struct kserdes_dlev_out dlevo;
+	u32 i = ks_phy->lane;
+	u32 lanes_up_map = 0;
+	int ret = 0;
+
+	if (sc->phy_type == KSERDES_PHY_PCIE)
+		return ret;
+
+	if (sc->phy_type == KSERDES_PHY_XGE)
+		ret = xge_kserdes_recover_lane_rx(sc, i, &sofs->lane_ofs[i],
+			&dlevo.lane_dlev_out[i]);
+	else
+		ret = gbe_kserdes_recover_lane_rx(sc, i, &sofs->lane_ofs[i],
+			&dlevo.lane_dlev_out[i]);
+
+	kserdes_clear_wait_after(sc, BIT(i));
+
+	if (sc->phy_type == KSERDES_PHY_XGE)
+		_kserdes_enable_xgmii_port(sc->peripheral_regmap, i);
+
+	kserdes_wait_link_up(sc, BIT(i), &lanes_up_map);
+
+	dev_dbg(sd->dev, "phy reset: recover lane %u rx\n", i);
+
+	return ret;
+}
+
+static struct phy_ops kserdes_phy_ops = {
+	.init		= kserdes_phy_enable_rx,
+	.reset		= kserdes_phy_reset,
+	.owner		= THIS_MODULE,
+};
+
+static int kserdes_of_parse(struct platform_device *pdev,
+			    struct kserdes_dev *sd,
+			    struct device_node *np)
+{
+	const struct of_device_id *of_id;
+	struct kserdes_config *sc = &sd->sc;
+	struct device_node *child;
+	struct device *dev = sd->dev;
+	struct resource res;
+	void __iomem *regs;
+	int ret, lane = 0;
+
+	ret = of_address_to_resource(np, SERDES_REG_INDEX, &res);
+	if (ret) {
+		dev_err(dev, "Can't xlate serdes reg addr of node(%s)\n",
+			np->name);
+		return ret;
+	}
+
+	regs = devm_ioremap_resource(dev, &res);
+	if (IS_ERR(regs)) {
+		dev_err(dev, "Failed to map serdes register base\n");
+		return PTR_ERR(regs);
+	}
+	sc->regs = regs;
+
+	of_id = of_match_device(kserdes_of_match, dev);
+	if (!of_id) {
+		dev_err(dev, "unknown phy type\n");
+		return -EINVAL;
+	}
+	pdev->id_entry = of_id->data;
+	sc->phy_type = pdev->id_entry->driver_data;
+
+	sc->dev = dev;
+
+	kserdes_set_defaults(sc, sc->phy_type);
+
+	if (sc->phy_type == KSERDES_PHY_XGE) {
+		sc->peripheral_regmap =
+			syscon_regmap_lookup_by_phandle(np,
+							"syscon-peripheral");
+
+		if (IS_ERR(sc->peripheral_regmap)) {
+			dev_err(sc->dev,
+				"peripheral regmap lookup failed: %ld\n",
+				PTR_ERR(sc->peripheral_regmap));
+			return PTR_ERR(sc->peripheral_regmap);
+		}
+
+		sc->pcsr_regmap =
+			syscon_regmap_lookup_by_phandle(np, "syscon-link");
+
+		if (IS_ERR(sc->pcsr_regmap)) {
+			dev_err(sc->dev, "link regmap lookup failed: %ld\n",
+				PTR_ERR(sc->pcsr_regmap));
+			return PTR_ERR(sc->pcsr_regmap);
+		}
+	}
+
+	if (of_property_read_u32(np, "link-rate-kbps", &sc->link_rate)) {
+		dev_info(dev, "use default link-rate-kbps: %u\n",
+			 sc->link_rate);
+	}
+
+	if (of_property_read_u32(np, "num-lanes", &sc->lanes))
+		dev_info(dev, "use default num-lanes %d\n", sc->lanes);
+
+	if (sc->lanes > KSERDES_MAX_LANES) {
+		sc->lanes = KSERDES_MAX_LANES;
+		dev_info(dev, "use max allowed lanes %d\n", sc->lanes);
+	}
+
+	if (of_property_read_bool(np, "rx-force-enable"))
+		sc->rx_force_enable = true;
+	else
+		sc->rx_force_enable = false;
+
+	sd->nphys = sc->lanes;
+
+	for_each_child_of_node(np, child) {
+		struct kserdes_phy *ks_phy;
+		struct phy *phy;
+
+		lane = kserdes_of_parse_lane(dev, child, sc);
+		if (lane < 0) {
+			ret = lane;
+			goto err_child;
+		}
+
+		if (!sc->lane[lane].enable)
+			continue;
+
+		ks_phy = devm_kzalloc(dev, sizeof(*ks_phy), GFP_KERNEL);
+		if (!ks_phy) {
+			ret = -ENOMEM;
+			goto err_child;
+		}
+
+		sd->phys[lane] = ks_phy;
+
+		phy = devm_phy_create(dev, child, &kserdes_phy_ops);
+		if (IS_ERR(phy)) {
+			dev_err(dev, "failed to create PHY\n");
+			ret = PTR_ERR(phy);
+			goto err_child;
+		}
+
+		sd->phys[lane]->phy = phy;
+		sd->phys[lane]->lane = lane;
+		phy_set_drvdata(phy, sd->phys[lane]);
+	}
+
+	return 0;
+err_child:
+	of_node_put(child);
+	return ret;
+}
+
+static int kserdes_provider_lanes_enable_common(struct kserdes_config *sc)
+{
+	struct kserdes_ofs *sofs = &sc->sofs;
+	unsigned long lanes_needed = 0;
+	int ret, i;
+
+	if (sc->firmware)
+		return 0;
+
+	for_each_enable_lane(sc, i)
+		lanes_needed |= BIT(i);
+
+	if (sc->phy_type == KSERDES_PHY_PCIE) {
+		kserdes_pcie_lanes_enable(sc);
+		return lanes_needed;
+	}
+
+	ret = kserdes_lanes_enable_common(sc, sofs);
+	if (ret)
+		dev_err(sc->dev, "provider lanes enable: FAILED %d\n", ret);
+
+	return ret;
+}
+
+static int kserdes_provider_init(struct kserdes_dev *sd)
+{
+	struct kserdes_config *sc = &sd->sc;
+	struct device *dev = sd->dev;
+	int ret;
+
+	switch (sc->phy_type) {
+	case KSERDES_PHY_SGMII:
+		ret = kserdes_sgmii_init(sc);
+		break;
+	case KSERDES_PHY_XGE:
+		ret = kserdes_xge_init(sc);
+		break;
+	case KSERDES_PHY_PCIE:
+		ret = kserdes_pcie_init(sc);
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	if (ret < 0) {
+		dev_err(dev, "serdes procider init failed %d\n", ret);
+		return ret;
+	}
+
+	return kserdes_provider_lanes_enable_common(sc);
+}
+
+static struct phy *kserdes_xlate(struct device *dev,
+				 struct of_phandle_args *args)
+{
+	struct kserdes_dev *sd = dev_get_drvdata(dev);
+	struct phy *phy = NULL;
+	struct device_node *phynode = args->np;
+	int i;
+
+	if (args->args_count) {
+		dev_err(dev, "invalid #cell in PHY property\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	for (i = 0; i < sd->nphys; i++) {
+		if (sd->phys[i] &&
+		    (phynode == sd->phys[i]->phy->dev.of_node)) {
+			phy = sd->phys[i]->phy;
+			break;
+		}
+	}
+
+	return phy;
+}
+
+static int kserdes_probe(struct platform_device *pdev)
+{
+	struct phy_provider *phy_provider;
+	struct kserdes_dev *sd;
+	struct device_node *np = pdev->dev.of_node;
+	struct device *dev = &pdev->dev;
+	int ret;
+
+	sd = devm_kzalloc(dev, sizeof(*sd), GFP_KERNEL);
+	if (!sd)
+		return -ENOMEM;
+
+	pm_runtime_enable(&pdev->dev);
+	ret = pm_runtime_get_sync(&pdev->dev);
+	if (ret < 0) {
+		dev_err(dev, "Failed to enable SerDes power-domain\n");
+		pm_runtime_set_suspended(dev);
+		pm_runtime_put_noidle(dev);
+		return ret;
+	}
+
+	sd->dev = dev;
+	dev_set_drvdata(dev, sd);
+
+	ret = kserdes_of_parse(pdev, sd, np);
+	if (ret)
+		goto error;
+
+	phy_provider = devm_of_phy_provider_register(sd->dev, kserdes_xlate);
+	if (IS_ERR(phy_provider)) {
+		ret = PTR_ERR_OR_ZERO(phy_provider);
+		goto error;
+	}
+
+	kserdes_provider_init(sd);
+
+	dev_vdbg(&pdev->dev, "probed");
+	return 0;
+
+error:
+	pm_runtime_put_sync(dev);
+	pm_runtime_disable(dev);
+	return ret;
+}
+
+static struct platform_driver kserdes_driver = {
+	.probe	= kserdes_probe,
+	.driver = {
+		.of_match_table	= kserdes_of_match,
+		.name  = "ti,keystone-serdes",
+	}
+};
+module_platform_driver(kserdes_driver);
+
+MODULE_AUTHOR("WingMan Kwok <w-kwok2@ti.com>");
+MODULE_DESCRIPTION("TI Keystone SerDes driver");
+MODULE_LICENSE("GPL v2");

+ 1 - 0
ti_config_fragments/connectivity.cfg

@@ -135,6 +135,7 @@ CONFIG_IWLDVM=m
 CONFIG_IWLMVM=m
 CONFIG_IWLMVM=m
 CONFIG_B43=m
 CONFIG_B43=m
 #Generic Phys
 #Generic Phys
+CONFIG_PHY_TI_KEYSTONE_SERDES=y
 
 
 # Networking
 # Networking
 CONFIG_NF_CONNTRACK=m
 CONFIG_NF_CONNTRACK=m