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.
 
 NetCP device properties: Device specification for NetCP sub-modules.
+
 1Gb/10Gb (gbe/xgbe) ethernet switch sub-module specifications.
 Required properties:
 - label:	Must be "netcp-gbe" for 1Gb & "netcp-xgbe" for 10Gb.
 - compatible:	Must be one of below:-
 		"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
 
+- 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
 		regions in the specified order.
 		- switch subsystem registers
+		- sgmii module registers
 		- sgmii port3/4 module registers (only for NetCP 1.4)
 		- switch module registers
-		- serdes registers (only for 10G)
 
 		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 #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 #2 - serdes registers
+
+		NSS of K2G SoC
+			index #0 sgmii module registers
 
 - tx-channel:	the navigator packet dma channel name for tx.
 - tx-queue:	the navigator queue number associated with the tx dma channel.
@@ -104,6 +114,10 @@ Required properties:
 			- 10Gb mac<->mac forced mode : 11
 ----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:
 - enable-ale:	NetCP driver keeps the address learning feature in the ethernet
 		switch module disabled. This attribute is to enable the address
@@ -160,6 +174,16 @@ Optional properties:
 
 Example binding:
 
+gbe_subsys: subsys@2090000 {
+	compatible = "syscon";
+	reg = <0x02090000 0x100>;
+};
+
+gbe_serdes: phy@232a000 {
+	compatible		= "ti,keystone-serdes-gbe";
+	...
+};
+
 netcp: netcp@2000000 {
 	reg = <0x2620110 0x8>;
 	reg-names = "efuse";
@@ -169,7 +193,6 @@ netcp: netcp@2000000 {
 	ranges  = <0 0x2000000 0xfffff>;
 	clocks = <&papllclk>, <&clkcpgmac>, <&chipclk12>;
 	dma-coherent;
-	/* big-endian; */
 	dma-id = <0>;
 
 	netcp-devices {
@@ -178,17 +201,19 @@ netcp: netcp@2000000 {
 		ranges;
 		gbe@90000 {
 			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-channel = <8>;
 
 			interfaces {
 				gbe0: interface-0 {
+					phys = <&serdes_lane0>;
 					slave-port = <0>;
 					link-interface	= <4>;
 				};
 				gbe1: interface-1 {
+					phys = <&serdes_lane1>;
 					slave-port = <1>;
 					link-interface	= <4>;
 				};
@@ -196,10 +221,12 @@ netcp: netcp@2000000 {
 
 			secondary-slave-ports {
 				port-2 {
+					phys = <&serdes_lane2>;
 					slave-port = <2>;
 					link-interface	= <2>;
 				};
 				port-3 {
+					phys = <&serdes_lane3>;
 					slave-port = <3>;
 					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>;
 	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>;
 		reg-names = "global", "txchan", "rxchan",
 				"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 */
 			label = "netcp-gbe";
 			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-channel = "nettx";
 
 			interfaces {
 				gbe0: interface-0 {
-					slave-port = <0>;
+					phys		= <&serdes0_lane0>;
+					slave-port	= <0>;
 					link-interface	= <1>;
 					phy-handle	= <&ethphy0>;
 				};
 				gbe1: interface-1 {
-					slave-port = <1>;
+					phys		= <&serdes0_lane1>;
+					slave-port	= <1>;
 					link-interface	= <1>;
 					phy-handle	= <&ethphy1>;
 				};
@@ -171,26 +273,32 @@ netcp: netcp@24000000 {
 
 			secondary-slave-ports {
 				port-2 {
+					phys = <&serdes0_lane2>;
 					slave-port = <2>;
 					link-interface	= <2>;
 				};
 				port-3 {
+					phys = <&serdes0_lane3>;
 					slave-port = <3>;
 					link-interface	= <2>;
 				};
 				port-4 {
+					phys = <&serdes1_lane0>;
 					slave-port = <4>;
 					link-interface	= <2>;
 				};
 				port-5 {
+					phys = <&serdes1_lane1>;
 					slave-port = <5>;
 					link-interface	= <2>;
 				};
 				port-6 {
+					phys = <&serdes1_lane2>;
 					slave-port = <6>;
 					link-interface	= <2>;
 				};
 				port-7 {
+					phys = <&serdes1_lane3>;
 					slave-port = <7>;
 					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 {
 	reg = <0x2620110 0x8>;
 	reg-names = "efuse";
@@ -115,7 +120,8 @@ netcp: netcp@4000000 {
 		gbe: gbe@200000 {
 			label = "netcp-gbe";
 			compatible = "ti,netcp-gbe-2";
-			reg = <0x200000 0x20>, <0x220000 0x20000>;
+			syscon-subsys = <&gbe_subsys>;
+			reg = <0x220000 0x20000>;
 			enable-ale;
 			tx-queue = <5>;
 			tx-channel = "nettx";

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

@@ -241,3 +241,7 @@
 	memory-region = <&dsp_common_memory>;
 	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>;
 		reg-names = "global", "txchan", "rxchan",
 				"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>;
 			label = "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-channel = "nettx";
 
 			interfaces {
 				gbe0: interface-0 {
+					phys = <&serdes_lane0>;
 					slave-port = <0>;
 					link-interface = <1>;
 					phy-handle = <&ethphy0>;
 				};
 				gbe1: interface-1 {
+					phys = <&serdes_lane1>;
 					slave-port = <1>;
 					link-interface = <1>;
 					phy-handle = <&ethphy1>;
@@ -190,10 +245,12 @@ netcp: netcp@2000000 {
 
 			secondary-slave-ports {
 				port-2 {
+					phys = <&serdes_lane2>;
 					slave-port = <2>;
 					link-interface	= <2>;
 				};
 				port-3 {
+					phys = <&serdes_lane3>;
 					slave-port = <3>;
 					link-interface	= <2>;
 				};

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

@@ -170,3 +170,7 @@
 	memory-region = <&dsp_common_memory>;
 	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>;
 		reg-names = "global", "txchan", "rxchan",
 				"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 */
 			label = "netcp-gbe";
 			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-channel = "nettx";
 
 			interfaces {
 				gbe0: interface-0 {
-					slave-port = <0>;
+					phys		= <&serdes0_lane0>;
+					slave-port	= <0>;
 					link-interface	= <1>;
 					phy-handle	= <&ethphy0>;
 				};
 				gbe1: interface-1 {
-					slave-port = <1>;
+					phys		= <&serdes0_lane1>;
+					slave-port	= <1>;
 					link-interface	= <1>;
 					phy-handle	= <&ethphy1>;
 				};
@@ -170,10 +236,12 @@ netcp: netcp@26000000 {
 
 			secondary-slave-ports {
 				port-2 {
+					phys = <&serdes1_lane0>;
 					slave-port = <2>;
 					link-interface	= <2>;
 				};
 				port-3 {
+					phys = <&serdes1_lane1>;
 					slave-port = <3>;
 					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
 keystone_netcp-y := netcp_core.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
 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"
 
 #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_MINOR(rev)	(rev & 0xff)
 #define ALE_VERSION_1R3		0x0103
 #define ALE_VERSION_1R4		0x0104
+#define ALE_VERSION_9R3		0x0903
 
 /* ALE Registers */
 #define ALE_IDVER		0x00
@@ -64,6 +67,39 @@
 #define ALE_TABLE_SIZE_MULTIPLIER	1024
 #define ALE_STATUS_SIZE_MASK		0x1f
 #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)
 {
@@ -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));
 }
 
+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 reg_mcast, int unreg_mcast)
 {
@@ -523,6 +575,14 @@ struct ale_control_info {
 };
 
 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]		= {
 		.name		= "enable",
 		.offset		= ALE_CONTROL,
@@ -691,6 +751,7 @@ static struct ale_control_info ale_controls[ALE_NUM_CONTROLS] = {
 		.port_shift	= 0,
 		.bits		= 8,
 	},
+	/* Fields below has individual registers on NetCP NU switch */
 	[ALE_PORT_UNKNOWN_VLAN_MEMBER] = {
 		.name		= "unknown_vlan_member",
 		.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);
 
+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)
 {
 	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)
 {
+	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_CLEAR, 1);
 
@@ -852,6 +1691,7 @@ void cpsw_ale_start(struct cpsw_ale *ale)
 		ale->timer.expires = jiffies + ale->ageout;
 		add_timer(&ale->timer);
 	}
+
 }
 EXPORT_SYMBOL_GPL(cpsw_ale_start);
 
@@ -859,6 +1699,13 @@ void cpsw_ale_stop(struct cpsw_ale *ale)
 {
 	del_timer_sync(&ale->timer);
 	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);
 

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

@@ -39,6 +39,17 @@ struct cpsw_ale {
 	unsigned long		ageout;
 	int			allmulti;
 	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 */
 	u32			port_mask_bits;
 	u32			port_num_bits;
@@ -47,6 +58,7 @@ struct cpsw_ale {
 
 enum cpsw_ale_control {
 	/* global */
+	ALE_VERSION,
 	ALE_ENABLE,
 	ALE_CLEAR,
 	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 */
 int netcp_sgmii_reset(void __iomem *sgmii_ofs, int port);
 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);
 
 /* XGBE SERDES init functions */

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

@@ -19,35 +19,38 @@
  */
 
 #include <linux/io.h>
+#include <linux/mfd/syscon.h>
 #include <linux/module.h>
 #include <linux/of_mdio.h>
 #include <linux/of_net.h>
 #include <linux/of_address.h>
+#include <linux/regmap.h>
 #include <linux/if_vlan.h>
 #include <linux/ptp_classify.h>
 #include <linux/net_tstamp.h>
 #include <linux/ethtool.h>
+#include <linux/phy/phy.h>
 
 #include "cpsw.h"
-#include "cpsw_ale.h"
-#include "netcp.h"
 #include "cpts.h"
+#include "netcp_ethss.h"
 
 #define NETCP_DRIVER_NAME		"TI KeyStone Ethernet Driver"
 #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 */
 #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_SGMII_REG_INDEX		0
 #define GBE_SGMII34_REG_INDEX		1
 #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 */
 #define GBE13_SGMII_MODULE_OFFSET	0x100
 /* offset relative to base of GBE_SM_REG_INDEX */
@@ -60,27 +63,21 @@
 #define GBE13_ALE_OFFSET		0x600
 #define GBE13_HOST_PORT_NUM		0
 #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 */
 #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
+/* offset relative to base of GBE_SS_REG_INDEX */
 #define GBENU_SGMII_MODULE_OFFSET	0x100
+/* offset relative to base of GBENU_SM_REG_INDEX */
 #define GBENU_HOST_PORT_OFFSET		0x1000
 #define GBENU_SLAVE_PORT_OFFSET		0x2000
 #define GBENU_EMAC_OFFSET		0x2330
@@ -88,13 +85,11 @@
 #define GBENU_CPTS_OFFSET		0x1d000
 #define GBENU_ALE_OFFSET		0x1e000
 #define GBENU_HOST_PORT_NUM		0
-#define GBENU_SGMII_MODULE_SIZE		0x100
 
 /* 10G Ethernet SS defines */
 #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_SERDES_REG_INDEX		2
 
@@ -144,25 +139,6 @@
 		(MACSL_XGIG_MODE | MACSL_XGMII_ENABLE |		\
 		 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 */
 #define SGMII_BASE(d, s) \
 	(((s) < 2) ? (d)->sgmii_port_regs : (d)->sgmii_port34_regs)
@@ -185,9 +161,10 @@
 		offsetof(struct gbenu##_##rb, rn)
 #define XGBE_SET_REG_OFS(p, rb, rn) p->rb##_ofs.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 SGMII_MODULE_SIZE			0x100
 
 #if IS_ENABLED(CONFIG_TI_CPTS)
 /* Px_TS_CTL register fields */
@@ -239,6 +216,7 @@
 #define EVENT_MSG_BITS (BIT(0) | BIT(1) | BIT(2) | BIT(3))
 #endif /* CONFIG_TI_CPTS */
 
+#define SGMII_MODULE_SIZE			0x100
 struct xgbe_ss_regs {
 	u32	id_ver;
 	u32	synce_count;
@@ -557,12 +535,6 @@ struct gbe_ss_regs {
 	u32	synce_mux;
 };
 
-struct gbe_ss_regs_ofs {
-	u16	id_ver;
-	u16	control;
-	u16	rgmii_status; /* 2U */
-};
-
 struct gbe_switch_regs {
 	u32	id_ver;
 	u32	control;
@@ -576,16 +548,6 @@ struct gbe_switch_regs {
 	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 {
 	u32	max_blks;
 	u32	blk_cnt;
@@ -600,20 +562,6 @@ struct gbe_port_regs {
 	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 {
 	u32	src_id;
 	u32	port_vlan;
@@ -621,12 +569,6 @@ struct gbe_host_port_regs {
 	u32	rx_maxlen;
 };
 
-struct gbe_host_port_regs_ofs {
-	u16	port_vlan;
-	u16	tx_pri_map;
-	u16	rx_maxlen;
-};
-
 struct gbe_emac_regs {
 	u32	id_ver;
 	u32	mac_control;
@@ -641,12 +583,6 @@ struct gbe_emac_regs {
 	u32	rsvd[6];
 };
 
-struct gbe_emac_regs_ofs {
-	u16	mac_control;
-	u16	soft_reset;
-	u16	rx_maxlen;
-};
-
 struct gbe_hw_stats {
 	u32	rx_good_frames;
 	u32	rx_broadcast_frames;
@@ -685,98 +621,8 @@ struct gbe_hw_stats {
 	u32	rx_dma_overruns;
 };
 
-#define GBE_MAX_HW_STAT_MODS			9
 #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 xgbe_module;
 
@@ -1733,9 +1579,6 @@ static const struct netcp_ethtool_stat xgbe10_et_stats[] = {
 	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) \
 	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];
 	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));
 }
 
-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_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;
 }
 
+/* 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,
 					  struct net_device *ndev,
 					  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 != XGMII_LINK_MAC_PHY)))
 			netif_carrier_on(ndev);
+
+		if (phy)
+			phy_print_status(phy);
+		else
+			netdev_printk(KERN_INFO, ndev, "Link is Up\n");
 	} else {
 		writel(mac_control, GBE_REG_ADDR(slave, emac_regs,
 						 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 != XGMII_LINK_MAC_PHY)))
 			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)
@@ -2135,12 +2021,21 @@ static bool gbe_phy_link_status(struct gbe_slave *slave)
 
 #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;
+	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);
+
+	return ret;
 }
 
 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)
 {
 	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)
 		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))
 		sw_link_state =
 		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);
 	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,
 					      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)
@@ -2229,7 +2150,7 @@ static void gbe_port_config(struct gbe_priv *gbe_dev, struct gbe_slave *slave,
 			    int max_rx_len)
 {
 	void __iomem *rx_maxlen_reg;
-	u32 xgmii_mode;
+	int ret;
 
 	if (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 */
 	if (IS_SS_ID_XGBE(gbe_dev) &&
 	    (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))
@@ -2776,6 +2704,27 @@ static inline int gbe_hwtstamp_set(struct gbe_intf *gbe_intf, struct ifreq *req)
 }
 #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)
 {
 	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->gbe_dev = gbe_dev;
 	slave->open = false;
 	if ((slave->link_interface == SGMII_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, 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)) {
 		/* Initialize  slave port register offsets */
 		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, 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)) {
 		/* Initialize  slave port register offsets */
 		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, soft_reset);
 		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);
@@ -3134,6 +3093,7 @@ static void init_secondary_ports(struct gbe_priv *gbe_dev,
 	struct device_node *port;
 	struct gbe_slave *slave;
 	bool mac_phy_link = false;
+	int ret;
 
 	for_each_child_of_node(node, port) {
 		slave = devm_kzalloc(dev, sizeof(*slave), GFP_KERNEL);
@@ -3151,7 +3111,16 @@ static void init_secondary_ports(struct gbe_priv *gbe_dev,
 			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_port_reset(slave);
 		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)) {
 		slave = first_sec_slave(gbe_dev);
 
+		phy_exit(slave->serdes_phy);
+
 		if (slave->phy)
 			phy_disconnect(slave->phy);
 		list_del(&slave->slave_list);
@@ -3237,20 +3208,25 @@ static int set_xgbe_ethss10_priv(struct gbe_priv *gbe_dev,
 	void __iomem *regs;
 	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,
-			"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);
 	if (ret) {
@@ -3267,20 +3243,23 @@ static int set_xgbe_ethss10_priv(struct gbe_priv *gbe_dev,
 	}
 	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) {
 		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;
 	}
 
 	regs = devm_ioremap_resource(gbe_dev->dev, &res);
 	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);
 	}
-	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->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->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++)
 		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;
 }
 
-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;
 	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");
 		return PTR_ERR(regs);
 	}
+
 	gbe_dev->ss_regs = 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;
 }
 
@@ -3370,6 +3368,27 @@ static int set_gbe_ethss14_priv(struct gbe_priv *gbe_dev,
 	void __iomem *regs;
 	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);
 	if (ret) {
 		dev_err(gbe_dev->dev,
@@ -3424,7 +3443,6 @@ static int set_gbe_ethss14_priv(struct gbe_priv *gbe_dev,
 		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;
 
 	/* 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,
 				struct device_node *node)
 {
+	int i, ret, sm_index = GBENU_SM_REG_INDEX;
 	struct resource res;
 	void __iomem *regs;
-	int i, ret;
 
 	gbe_dev->num_stats_mods = gbe_dev->max_num_ports;
 	gbe_dev->et_stats = gbenu_et_stats;
@@ -3496,11 +3514,14 @@ static int set_gbenu_ethss_priv(struct gbe_priv *gbe_dev,
 		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) {
 		dev_err(gbe_dev->dev,
 			"Can't translate of gbenu node(%s) addr at index %d\n",
-			node->name, GBENU_SM_REG_INDEX);
+			node->name, sm_index);
 		return ret;
 	}
 
@@ -3512,16 +3533,37 @@ static int set_gbenu_ethss_priv(struct gbe_priv *gbe_dev,
 	}
 	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;
 
@@ -3565,6 +3607,7 @@ static int gbe_probe(struct netcp_device *netcp_device, struct device *dev,
 	struct device_node *secondary_ports;
 	struct cpsw_ale_params ale_params;
 	struct gbe_priv *gbe_dev;
+	struct phy *phy;
 	u32 slave_num;
 	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")) {
 		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 {
 		dev_err(dev, "unknown GBE node(%s)\n", node->name);
 		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,
 				gbe_dev->dma_chan_name, gbe_dev->tx_queue_id);
 	if (ret)
-		return ret;
+		goto exit_err;
 
 	ret = netcp_txpipe_open(&gbe_dev->tx_pipe);
 	if (ret)
-		return ret;
+		goto exit_err;
 
 	/* Create network interfaces */
 	INIT_LIST_HEAD(&gbe_dev->gbe_intf_head);
 	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);
 		if (ret) {
 			dev_err(dev, "missing slave-port parameter, skipping interface configuration for %s\n",
 				interface->name);
 			continue;
 		}
+
 		gbe_dev->num_slaves++;
 		if (gbe_dev->num_slaves >= gbe_dev->max_num_slaves) {
 			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);
 
+	ret = gbe_create_sysfs_entries(gbe_dev);
+	if (ret)
+		goto free_sec_ports;
 	timer_setup(&gbe_dev->timer, netcp_ethss_timer, 0);
 	gbe_dev->timer.expires	 = jiffies + GBE_TIMER_INTERVAL;
 	add_timer(&gbe_dev->timer);
 	*inst_priv = gbe_dev;
+	dev_dbg(dev, "probed");
 	return 0;
 
 free_sec_ports:
 	free_secondary_ports(gbe_dev);
+exit_err:
 	return ret;
 }
 
@@ -3778,6 +3829,14 @@ static int gbe_attach(void *inst_priv, struct net_device *ndev,
 		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;
 	ndev->ethtool_ops = &keystone_ethtool_ops;
 	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;
 
+	phy_exit(gbe_intf->slave->serdes_phy);
 	gbe_intf->ndev->ethtool_ops = NULL;
 	list_del(&gbe_intf->gbe_intf_list);
 	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);
 	cpsw_ale_stop(gbe_dev->ale);
 	netcp_txpipe_close(&gbe_dev->tx_pipe);
+	gbe_remove_sysfs_entries(gbe_dev);
 	free_secondary_ports(gbe_dev);
 
 	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;
 }
 
-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));
 	if ((status & SGMII_REG_STATUS_LINK) != 0)
-		link = 1;
+		link = true;
 	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).
 	  This transceiver supports high and full speed devices plus,
 	  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_PHY_TUSB1210)		+= phy-tusb1210.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_B43=m
 #Generic Phys
+CONFIG_PHY_TI_KEYSTONE_SERDES=y
 
 # Networking
 CONFIG_NF_CONNTRACK=m