diff --git a/README.md b/README.md
index 5c67a7c40596bbce20cc789915889a4265c60089..7f651ed3e295998cd7227943bea42def58ca9396 100644
--- a/README.md
+++ b/README.md
@@ -23,7 +23,7 @@ Run the following command from a terminal.
 $ source /<XILINX_INSTALL_FOLDER>/PetaLinux/2020.2/bin/settings.sh
 ```
 
-In my case /<XILINX\_INSTALL\_FOLDER>/ corresponds to /tools/Xilinx/.
+In my case **\<XILINX\_INSTALL\_FOLDER\>** corresponds to /tools/Xilinx/.
 
 **Check that the "rsync" tool is installed.**
 
@@ -53,6 +53,16 @@ $ git checkout SCALP-SAFE-v0.01
 
 ### FPGA scalp_safe_firmware creation
 
+First of all... Go to the **board_files** folder of the GIT **scalp_firmware** deposit. [Scalp Firmware - Board files](https://gitedu.hesge.ch/soma/scalp_firmware/-/tree/master/board_files)
+
+And follow these instructions...
+
+The **scalp_node** folder can be copied into **\<Vivado directory\>/\<version\>/data/boards/board_files** to add **SCALP node** support in Vivado. The definition "hepia-cores.ch:scalp_node:part0:0.1" can then be used as a proper board definition for project creation. This board definition is currently blank and does not provide full board definition but it will evolve in the future and should be used for new project creation.
+
+**In your case, the definition is already present in the scalp_safe_firmware project. However, if the scalp_node folder is not copied, the creation of the project will fail.**
+
+Next...
+
 ```
 $ cd <SCALP_PROJECT_FOLDER>/scalp_firmware/designs/vivado/scalp_safe_firmware/2020.2/lin64/
 $ source setup.sh
@@ -166,46 +176,125 @@ In my case **\<path-to-hw\>** corresponds to /home/jo/Documents/Projets/Hepia/sc
 
 ```
 
-### Build the PetaLinux project
+### Bug in the U-Boot config with the selection of the ZYNQ_SPI driver
+
+Despite the fact that the ZYNQ_SPI driver is preselected in the U-Boot configuration specific to the SCALP board, this configuration is not automatically taken into account. Therefore, it is necessary to select the ZYNQ_SPI driver manually.
+
+First, you need to open the U-Boot configuration menu, after cleaning up the project.
 
 ```
 $ petalinux-build -x mrproper -f
+$ petalinux-config -c u-boot
+```
+
+Please select...
+
+```
+U-Boot 2020.01 Configuration
+    Device Drivers  --->
+    -*- SPI Support  --->
+        [*]   Zynq SPI driver (select with spacebar)
+```
+
+Then select 3 times EXIT...
+
+And save the configuration.
+
+**You can eventually go back to the U-Boot configuration menu to check that the ZYNQ_SPI driver has been taken into account.**
+
+### Build the PetaLinux project
+
+```
 $ petalinux-build
 ```
 
 The produced binary files can be found in the **./images/linux** folder.
 
+**You can possibly check that the ZYNQ_SPI driver has been compiled.**
+
+```
+
+```
+
+### Creation of the basic image of the U-Boot environment
+
+The image content can be produced by using the **mkenvimage** command.
+
+```
+$ echo -e "custom_board_name=\"Zynq Scalp Board\"\ncustom_board_version=\"RevB 2008\"\ncustom_board_vendor=\"Hepia CoRES - LSN\"" > images/linux/scalp-u-boot.env
+$ /tools/u-boot-tools/mkenvimage -s 0x20000 -o images/linux/scalp-u-boot.env.bin images/linux/scalp-u-boot.env
+```
+
+In my case, I copied the tools produced by U-Boot into the **/tools/u-boot-tools/** folder.
+
 ### QSPI firmware packaging
 
 ```
-$ petalinux-package --boot --fsbl images/linux/zynq_fsbl.elf --fpga images/linux/system.bit --u-boot --kernel --offset 0x520000
+$ petalinux-package --boot --fsbl images/linux/zynq_fsbl.elf --fpga images/linux/system.bit --u-boot --kernel --offset 0x540000 --dtb images/linux/system.dtb --offset 0x520000 --force
+
+$ petalinux-package --boot --fsbl images/linux/zynq_fsbl.elf --offset 0x0 --fpga images/linux/system.bit --u-boot --add images/linux/scalp-u-boot.env.bin --offset 0x500000 --kernel --offset 0x540000 --dtb images/linux/system.dtb --offset 0x520000 --force
+```
+
+The produced binary file can be found in the **./images/linux** folder. The **FIT image** of the Linux kernel is located at the address **0x540000**.
+
+Below is the partitioning of the flash memory.
+
 ```
+/* FSBL + safe bitstream (design with only Zynq) + U-Boot */
+/* Size : 5242880 Bytes */
+partition@boot {
+    label = "boot";
+    reg = <0x0 0x500000>;
+};
+        
+/* Safe U-Boot Env. */
+/* Size : 131072 Bytes */
+partition@bootenv {
+    label = "bootenv";
+    reg = <0x500000 0x20000>;
+};
 
-The produced binary file can be found in the **./images/linux** folder. This is copied in **0x520000** offset in the final image.
+/* Safe DTB */
+/* Size : 131072 Bytes */
+partition@dtb {
+    label = "dtb";
+    reg = <0x520000 0x20000>;
+};
+
+/* Safe Fit Image */
+/* Size : 28049408 Bytes */
+partition@fitimage {
+    label = "fitimage";
+    reg = <0x540000 0x1ac0000>;
+};
+```
 
 ### Flash firmware on QSPI memory
 
+**First of all, to flash the board, it is necessary to set switch SW1 (BOOT MODE) in JTAG mode. Then, once the card is flashed, it is necessary to set switch SW1 back in QSPI mode and reconnect the board.**
+
 If you are using a single JTAG probe.
 
 ```
 $ program_flash -f images/linux/BOOT.BIN -offset 0 -flash_type qspi-x4-single -fsbl images/linux/zynq_fsbl.elf -cable type xilinx_tcf url TCP:127.0.0.1:3121
 ```
 
-The **program_flash** command is located in the Vivado installation folder.
+The **program_flash** command is located in the Vitis installation folder **/tools/Xilinx/Vitis/2020.2/bin/program_flash**.
 
 If you use several JTAG probes.
 
 ```
 $ program_flash -jtagtarget
 
+> Available targets and devices:
 > JTAG chain configuration
 > --------------------------------------------------
 > 1 Digilent 210299AFC175 
-> 	2    jsn-JTAG-HS3-210299AFC175-4ba00477-0  (name arm_dap  idcode 4ba00477)
+> 	2    jsn-JTAG-HS3-210299AFC175-4ba00477-0  (name arm_dap  idcode 4ba00477) <-- target id 2
 > 	3    jsn-JTAG-HS3-210299AFC175-0373b093-0  (name xc7z015  idcode 0373b093)
-> 4 Xilinx 00001a3719fe01 
-> 	5    jsn-DLC10-00001a3719fe01-4ba00477-0  (name arm_dap  idcode 4ba00477)
->	6    jsn-DLC10-00001a3719fe01-0373b093-0  (name xc7z015  idcode 0373b093)
+> 4 Digilent 210299AFB167 
+> 	5    jsn-JTAG-HS3-210299AFB167-4ba00477-0  (name arm_dap  idcode 4ba00477) <-- target id 5
+> 	6    jsn-JTAG-HS3-210299AFB167-0373b093-0  (name xc7z015  idcode 0373b093)
 
 $ program_flash -f images/linux/BOOT.BIN -offset 0 -flash_type qspi-x4-single -fsbl images/linux/zynq_fsbl.elf -target_id 2 -url TCP:127.0.0.1:3121
 
diff --git a/scalp_safe_petalinux/.petalinux/metadata b/scalp_safe_petalinux/.petalinux/metadata
index ce403eb1cf4eca2bc1359bb3a2a8f7d45cd4f126..90e319015a4d1abd9fb570ad2d4534f1262d5980 100644
--- a/scalp_safe_petalinux/.petalinux/metadata
+++ b/scalp_safe_petalinux/.petalinux/metadata
@@ -1,6 +1,6 @@
 PETALINUX_VER=2020.2
 VALIDATE_HW_CHKSUM=1
 HARDWARE_PATH=/home/jo/Documents/Projets/Hepia/scalp_project/scalp_firmware/designs/vivado/scalp_safe_firmware/2020.2/lin64/scalp_safe_firmware/scalp_safe_firmware.xsa
-HARDWARE_CHECKSUM=86e69a2505aca58b710730ddd68dd848
+HARDWARE_CHECKSUM=c0e54849f94ffc5aaa53e1cad4e1235f
 YOCTO_SDK=5ff8fc5f85d1566b314bb73eaa378212
 RFSCONFIG_CHKSUM=7fb2a289957dc67ab720c7cb67e09ee0
diff --git a/scalp_safe_petalinux/project-spec/configs/config b/scalp_safe_petalinux/project-spec/configs/config
index eca355025f00b043c62267f072d2da69def3cc4d..dd4576075b2b365fb95d88df2b6e9c9b4163ec5d 100644
--- a/scalp_safe_petalinux/project-spec/configs/config
+++ b/scalp_safe_petalinux/project-spec/configs/config
@@ -105,16 +105,16 @@ CONFIG_SUBSYSTEM_FLASH_PS7_QSPI_0_BANKLESS_PART1_FLAGS="bootenv"
 #
 # partition 2
 #
-CONFIG_SUBSYSTEM_FLASH_PS7_QSPI_0_BANKLESS_PART2_NAME="fitimage"
-CONFIG_SUBSYSTEM_FLASH_PS7_QSPI_0_BANKLESS_PART2_SIZE=0x1AC0000
-CONFIG_SUBSYSTEM_FLASH_PS7_QSPI_0_BANKLESS_PART2_FLAGS="fitimage"
+CONFIG_SUBSYSTEM_FLASH_PS7_QSPI_0_BANKLESS_PART2_NAME="dtb"
+CONFIG_SUBSYSTEM_FLASH_PS7_QSPI_0_BANKLESS_PART2_SIZE=0x20000
+CONFIG_SUBSYSTEM_FLASH_PS7_QSPI_0_BANKLESS_PART2_FLAGS="dtb"
 
 #
 # partition 3
 #
-CONFIG_SUBSYSTEM_FLASH_PS7_QSPI_0_BANKLESS_PART3_NAME="dtb"
-CONFIG_SUBSYSTEM_FLASH_PS7_QSPI_0_BANKLESS_PART3_SIZE=0x20000
-CONFIG_SUBSYSTEM_FLASH_PS7_QSPI_0_BANKLESS_PART3_FLAGS="dtb"
+CONFIG_SUBSYSTEM_FLASH_PS7_QSPI_0_BANKLESS_PART3_NAME="fitimage"
+CONFIG_SUBSYSTEM_FLASH_PS7_QSPI_0_BANKLESS_PART3_SIZE=0x1AC0000
+CONFIG_SUBSYSTEM_FLASH_PS7_QSPI_0_BANKLESS_PART3_FLAGS="fitimage"
 
 #
 # partition 4
@@ -195,7 +195,7 @@ CONFIG_SUBSYSTEM_EXTRA_DT_FILES=""
 #
 CONFIG_SUBSYSTEM_BOOTARGS_AUTO=y
 CONFIG_SUBSYSTEM_BOOTARGS_EARLYPRINTK=y
-
+CONFIG_SUBSYSTEM_BOOTARGS_GENERATED="console=ttyPS0,115200 earlycon"
 CONFIG_SUBSYSTEM_DEVICETREE_COMPILER_FLAGS="-@"
 # CONFIG_SUBSYSTEM_DTB_OVERLAY is not set
 # CONFIG_SUBSYSTEM_REMOVE_PL_DTB is not set
@@ -290,4 +290,3 @@ CONFIG_YOCTO_NETWORK_SSTATE_FEEDS_URL="http://petalinux.xilinx.com/sswreleases/r
 # User Layers
 #
 CONFIG_USER_LAYER_0=""
-CONFIG_SUBSYSTEM_BOOTARGS_GENERATED="console=ttyPS0,115200 earlycon"
diff --git a/scalp_safe_petalinux/project-spec/hw-description/scalp_safe_firmware.bit b/scalp_safe_petalinux/project-spec/hw-description/scalp_safe_firmware.bit
index 8c4c4527bb931dfe476b87c6fe53e0ef84073712..23eb74d76dece145f45538952c942a7f8de9eec0 100644
Binary files a/scalp_safe_petalinux/project-spec/hw-description/scalp_safe_firmware.bit and b/scalp_safe_petalinux/project-spec/hw-description/scalp_safe_firmware.bit differ
diff --git a/scalp_safe_petalinux/project-spec/hw-description/system.xsa b/scalp_safe_petalinux/project-spec/hw-description/system.xsa
index b89135d74407fa63c8abfe03e63b60c1b0bc7465..5d18795d6fe6b33532d6e4287580d2f70772f40f 100644
Binary files a/scalp_safe_petalinux/project-spec/hw-description/system.xsa and b/scalp_safe_petalinux/project-spec/hw-description/system.xsa differ
diff --git a/scalp_safe_petalinux/project-spec/meta-user/recipes-bsp/device-tree/files/system-user.dtsi b/scalp_safe_petalinux/project-spec/meta-user/recipes-bsp/device-tree/files/system-user.dtsi
index 337d87f84fd0d076d61ce1fc692145b717c0d890..b48d920ba9ce388314b55752df3e0192097b5b8e 100644
--- a/scalp_safe_petalinux/project-spec/meta-user/recipes-bsp/device-tree/files/system-user.dtsi
+++ b/scalp_safe_petalinux/project-spec/meta-user/recipes-bsp/device-tree/files/system-user.dtsi
@@ -89,35 +89,36 @@
         /* m25p,fast-read; */
         #address-cells = <1>;
         #size-cells = <1>;
-        
-        /* FSBL + safe bitstream (design with only Zynq) + U-Boot + (devicetree) => BOOT.BIN */
-        /* Size : 5242880 Bytes */
-        partition@boot {       
-            label = "boot";
-            reg = <0x0 0x500000>;
-        };
-        
-        /* Safe U-Boot Env. */
-        /* Size : 131072 Bytes */
-        partition@bootenv {       
-            label = "bootenv";
-            reg = <0x500000 0x20000>;
-        };
-        
-        /* Safe Fit Image */
-        /* Size : 28049408 Bytes */
-        partition@fitimage {            
-            label = "fitimage";
-            reg = <0x520000 0x1ac0000>;
-        };
-
-        /* Safe DTB */
-        /* Size : 131072 Bytes */
-        partition@dtb {            
-            label = "dtb";
-            reg = <0x1ac0000 0x20000>;
-        };
     };
+    
+    //     /* FSBL + safe bitstream (design with only Zynq) + U-Boot */
+    //     /* Size : 5242880 Bytes */
+    //     partition@boot {       
+    //         label = "boot";
+    //         reg = <0x0 0x500000>;
+    //     };
+        
+    //     /* Safe U-Boot Env. */
+    //     /* Size : 131072 Bytes */
+    //     partition@bootenv {       
+    //         label = "bootenv";
+    //         reg = <0x500000 0x20000>;
+    //     };
+
+    //     /* Safe DTB */
+    //     /* Size : 131072 Bytes */
+    //     partition@dtb {            
+    //         label = "dtb";
+    //         reg = <0x520000 0x20000>;
+    //     };
+
+    //     /* Safe Fit Image */
+    //     /* Size : 28049408 Bytes */
+    //     partition@fitimage {            
+    //         label = "fitimage";
+    //         reg = <0x540000 0x1ac0000>;
+    //     };
+    // };
 };
 
 /* Switch Ethernet 5 ports - SPI0 - MIO 40, 41, 42, 45 */
@@ -157,14 +158,14 @@
 			#address-cells = <1>;
 			#size-cells = <0>;
             
-			port@0 {
+			swp0_east: port@0 {
 				label = "swp0_east";
 				phy-mode = "rgmii";
 				reg = <0>;
-                sja1105,role-phy;
+                //sja1105,role-phy;
 
                 fixed-link {
-					speed = <1000>;
+					speed = <10>;
 					full-duplex;
 				};
 			};
@@ -173,46 +174,46 @@
 				ethernet = <&gem0>;
 				phy-mode = "rgmii";
 				reg = <1>;
-                sja1105,role-phy;
+                //sja1105,role-phy;
 				
 				fixed-link {
-					speed = <1000>;
+					speed = <10>;
 					full-duplex;
 				};
 			};
             
-			port@2 {
+			swp2_bottom: port@2 {
 				label = "swp2_bottom";
 				phy-mode = "rgmii";
 				reg = <2>;
-                sja1105,role-mac;
+                //sja1105,role-mac;
 
                 fixed-link {
-					speed = <1000>;
+					speed = <10>;
 					full-duplex;
 				};
 			};
             
-			port@3 {
+			swp3_top: port@3 {
 				label = "swp3_top";
 				phy-mode = "rgmii";
 				reg = <3>;
-                sja1105,role-phy;
+                //sja1105,role-phy;
 
                 fixed-link {
-					speed = <1000>;
+					speed = <10>;
 					full-duplex;
 				};
 			};
             
-            port@4 {
+            swp4_west: port@4 {
 				label = "swp4_west";
 				phy-mode = "rgmii";
 				reg = <4>;
-                sja1105,role-mac;
+                //sja1105,role-mac;
 
                 fixed-link {
-					speed = <1000>;
+					speed = <10>;
 					full-duplex;
 				};
 			};			
@@ -220,13 +221,21 @@
 	};
 };
 
-/* spi2 */
-&spi1 {
-    u-boot,dm-pre-reloc;
-    is-decoded-cs = <0>;
-	num-cs = <3>;
-	status = "okay";
-};
+// &spi0 {
+//       bus-num = <0>;
+//       status = "okay";
+
+//       /* SCALP specific SJA1105T driver */
+//       sja1105@0 {
+//                 reg = <0>;
+//                 #address-cells = <1>;
+//                 #size-cells = <0>;
+//                 compatible = "scalp,sja1105t";
+//                 reset-gpios = <&gpio0 44 GPIO_ACTIVE_LOW>;
+//                 spi-max-frequency = <12000000>;
+//                 spi-cpha;
+//       };
+// };
 
 /* ethernet0 */
 &gem0 {
@@ -237,7 +246,7 @@
       //local-mac-address = [00 0a 35 00 00 00];
 
       fixed-link {
-		  speed = <1000>;
+		  speed = <10>;
 	      full-duplex;
 	  };
 
@@ -246,6 +255,16 @@
 	  //};
 };
 
+/* spi2 */
+&spi1 {
+    u-boot,dm-pre-reloc;
+    is-decoded-cs = <0>;
+	num-cs = <3>;
+	status = "okay";
+};
+
+
+
 &gpio0 {
 	emio-gpio-width = <64>;
 	gpio-mask-high = <0x0>;
diff --git a/scalp_safe_petalinux/project-spec/meta-user/recipes-bsp/u-boot/files/platform-top.h b/scalp_safe_petalinux/project-spec/meta-user/recipes-bsp/u-boot/files/platform-top.h
index 2c626cc7787f7076ee6ad6d3de874d9a05aad875..a372e17eecb164c7dbce4ec25990434277658800 100644
--- a/scalp_safe_petalinux/project-spec/meta-user/recipes-bsp/u-boot/files/platform-top.h
+++ b/scalp_safe_petalinux/project-spec/meta-user/recipes-bsp/u-boot/files/platform-top.h
@@ -10,10 +10,10 @@
     "SCALP_BOOT_PARTITION_SIZE=0x500000\0"                              \
     "SCALP_BOOTENV_PARTITION_OFFSET=0x500000\0"                         \
     "SCALP_BOOTENV_PARTITION_SIZE=0x20000\0"                            \
-    "SCALP_FITIMAGE_PARTITION_OFFSET=0x520000\0"                        \
+    "SCALP_FITIMAGE_PARTITION_OFFSET=0x540000\0"                        \
     "SCALP_FITIMAGE_PARTITION_SIZE=0x1AC0000\0"                         \
     "SCALP_FITIMAGE_LOAD_OFFSET=0x2000000\0"                            \
-    "SCALP_DTB_PARTITION_OFFSET=0x1AC0000\0"                            \
+    "SCALP_DTB_PARTITION_OFFSET=0x520000\0"                            \
     "SCALP_DTB_PARTITION_SIZE=0x20000\0"                                \
     "load_scalp_fitimage=sf probe 0 0 0 && sf read ${SCALP_FITIMAGE_LOAD_OFFSET} ${SCALP_FITIMAGE_PARTITION_OFFSET} ${SCALP_FITIMAGE_PARTITION_SIZE}\0" \
     "macmemaddr=0x1000000\0"                                            \
@@ -39,7 +39,7 @@
     "setenv bootargs \"ip=${ipaddr}:::::eth0\"\0"                       \
     "setup_eth=run readmac buildmac buildip\0"                          \
     "boot_from_qspi=run setup_eth && run load_scalp_fitimage && iminfo ${SCALP_FITIMAGE_LOAD_OFFSET} && bootm ${SCALP_FITIMAGE_LOAD_OFFSET}\0" \
-    "custom_autoboot=echo Scalp board autoboot && sja1105 init && run boot_from_qspi\0" \
+    "custom_autoboot=echo Scalp board autoboot && run boot_from_qspi\0" \
     "baudrate=115200\0"                                                 \
     "bootargs=ip=${ipaddr}:::::eth0\0"                                  \
     "bootdelay=2\0"                                                     \
diff --git a/scalp_safe_petalinux/project-spec/meta-user/recipes-bsp/u-boot/files/scalp_uboot.cfg b/scalp_safe_petalinux/project-spec/meta-user/recipes-bsp/u-boot/files/scalp_uboot.cfg
index 1a2104cf411b066cd6afa7565cc22c76de3f11ff..e4573d7894cb1b07634b3582dc7447bf0c4ea7ab 100644
--- a/scalp_safe_petalinux/project-spec/meta-user/recipes-bsp/u-boot/files/scalp_uboot.cfg
+++ b/scalp_safe_petalinux/project-spec/meta-user/recipes-bsp/u-boot/files/scalp_uboot.cfg
@@ -38,17 +38,36 @@ CONFIG_MTD_UBI=y
 CONFIG_MTD_UBI_WL_THRESHOLD=4096
 CONFIG_MTD_UBI_BEB_LIMIT=20
 # CONFIG_MTD_UBI_FASTMAP is not set
+CONFIG_SPI=y
+CONFIG_DM_SPI=y
+CONFIG_ZYNQ_SPI=y
+CONFIG_ZYNQ_QSPI=y
 CONFIG_RGMII=y
-CONFIG_XILINX_SPI=y
 CONFIG_FS_JFFS2=y
 CONFIG_RBTREE=y
 CONFIG_LZO=y
 CONFIG_ENV_OFFSET=0x0
 CONFIG_ENV_ADDR=0x500000
-CONFIG_XILINX_OF_BOARD_DTB_ADDR=0x1AC0000
+CONFIG_XILINX_OF_BOARD_DTB_ADDR=0x520000
 CONFIG_PACKING=y
 CONFIG_CMD_SJA1105=y
 CONFIG_SCALP_SJA1105=y
+CONFIG_PINCTRL=y
+CONFIG_PINCTRL_FULL=y
+CONFIG_PINCTRL_GENERIC=y
+CONFIG_PINMUX=y
+# CONFIG_PINCONF is not set
+CONFIG_PINCONF_RECURSIVE=y
+# CONFIG_PINCTRL_AT91 is not set
+# CONFIG_PINCTRL_AT91PIO4 is not set
+# CONFIG_PINCTRL_ROCKCHIP_RV1108 is not set
+# CONFIG_PINCTRL_SINGLE is not set
+# CONFIG_PINCTRL_STM32 is not set
+# CONFIG_PINCTRL_STMFX is not set
+CONFIG_DEFAULT_DEVICE_TREE="zynq-scalp"
 # CONFIG_OF_SEPARATE is not set
 CONFIG_OF_EMBED=y
-CONFIG_DEFAULT_DEVICE_TREE="zynq-scalp"
+CONFIG_DM_MDIO=y
+CONFIG_DM_MDIO_MUX=y
+# CONFIG_MDIO_MUX_I2CREG is not set
+# CONFIG_MVMDIO is not set
\ No newline at end of file
diff --git a/scalp_safe_petalinux/project-spec/meta-user/recipes-bsp/u-boot/files/u-boot-scalp-sja1105-dbg.patch b/scalp_safe_petalinux/project-spec/meta-user/recipes-bsp/u-boot/files/u-boot-scalp-sja1105-dbg.patch
new file mode 100644
index 0000000000000000000000000000000000000000..919bddd13057009d0a65a0d6b0c8f15a2c526d34
--- /dev/null
+++ b/scalp_safe_petalinux/project-spec/meta-user/recipes-bsp/u-boot/files/u-boot-scalp-sja1105-dbg.patch
@@ -0,0 +1,1164 @@
+diff --git a/cmd/Kconfig b/cmd/Kconfig
+index 1ae0f4055a..06d2c55e4e 100644
+--- a/cmd/Kconfig
++++ b/cmd/Kconfig
+@@ -935,6 +935,12 @@ config RANDOM_UUID
+ 	  Enable the generation of partitions with random UUIDs if none
+ 	  are provided.
+
++config CMD_SJA1105
++	bool "sja1105 - Commands for interacting with scalp-sja1105 simple driver"
++	help
++	  Provides a 'sja1105' command which can be used to probe and
++	  initialize the scalp-sja1105 driver.
++
+ config CMD_GPT_RENAME
+ 	bool "GPT partition renaming commands"
+ 	depends on CMD_GPT
+diff --git a/cmd/Makefile b/cmd/Makefile
+index 3feb7741c8..c4321ff92b 100644
+--- a/cmd/Makefile
++++ b/cmd/Makefile
+@@ -42,6 +42,7 @@ obj-$(CONFIG_CMD_CPU) += cpu.o
+ obj-$(CONFIG_DATAFLASH_MMC_SELECT) += dataflash_mmc_mux.o
+ obj-$(CONFIG_CMD_DATE) += date.o
+ obj-$(CONFIG_CMD_DEMO) += demo.o
++obj-$(CONFIG_CMD_SJA1105) += sja1105.o
+ obj-$(CONFIG_CMD_DM) += dm.o
+ obj-$(CONFIG_CMD_FRU) += fru.o
+ obj-$(CONFIG_CMD_SOUND) += sound.o
+diff --git a/cmd/sja1105.c b/cmd/sja1105.c
+new file mode 100644
+index 0000000000..7fbcac9e83
+--- /dev/null
++++ b/cmd/sja1105.c
+@@ -0,0 +1,100 @@
++// SPDX-License-Identifier: GPL-2.0+
++/*
++ * Copyright (c) 2013 Google, Inc
++ *
++ * (C) Copyright 2012
++ * Pavel Herrmann <morpheus.ibis@gmail.com>
++ */
++
++#include <common.h>
++//#include <dm-demo.h>
++#include <dm.h>
++#include <mapmem.h>
++#include <asm/io.h>
++
++struct udevice *sja1105_dev;
++
++static int do_sja1105_status(cmd_tbl_t *cmdtp, int flag, int argc,
++			  char * const argv[])
++{
++	printf("Not implemented\n");
++	return 0;
++}
++
++int do_sja1105_init(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
++{
++	struct udevice *dev;
++	int i, ret;
++    printf("DEBUG : do_sja1105_init START\n");
++
++	for (i = 0, ret = uclass_first_device(UCLASS_SPI, &dev);
++	     dev;
++	     ret = uclass_next_device(&dev)) {
++		printf("entry %d - instance %08x, ops %08x, platdata %08x\n",
++		       i++, (uint)map_to_sysmem(dev),
++		       (uint)map_to_sysmem(dev->driver->ops),
++		       (uint)map_to_sysmem(dev_get_platdata(dev)));
++	}
++
++    printf("DEBUG : do_sja1105_init END\n");
++
++	return cmd_process_error(cmdtp, ret);
++}
++
++static cmd_tbl_t sja1105_commands[] = {
++	U_BOOT_CMD_MKENT(init, 0, 1, do_sja1105_init, "", ""),
++	U_BOOT_CMD_MKENT(status, 0, 1, do_sja1105_status, "", ""),
++};
++
++static int do_sja1105(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
++{
++	cmd_tbl_t *sja1105_cmd;
++	int devnum = 0;
++	int ret;
++
++    printf("DEBUG : do_sja1105 START\n");
++
++	if (argc < 2)
++    {
++        printf("DEBUG : do_sja1105 END 0\n");
++		return CMD_RET_USAGE;
++    }
++
++	sja1105_cmd = find_cmd_tbl(argv[1], sja1105_commands,
++				ARRAY_SIZE(sja1105_commands));
++	argc -= 2;
++	argv += 2;
++
++	if ((!sja1105_cmd || argc > sja1105_cmd->maxargs))
++    {
++        printf("DEBUG : do_sja1105 END 1\n");
++		return CMD_RET_USAGE;
++    }
++
++	/*if (argc) {
++		devnum = simple_strtoul(argv[0], NULL, 10);
++		ret = uclass_get_device(UCLASS_DEMO, devnum, &sja1105_dev);
++		if (ret)
++			return cmd_process_error(cmdtp, ret);
++		argc--;
++		argv++;
++	} else {
++		sja1105_dev = NULL;
++		if (sja1105_cmd->cmd != do_sja1105_list)
++			return CMD_RET_USAGE;
++	}*/
++
++	ret = sja1105_cmd->cmd(sja1105_cmd, flag, argc, argv);
++
++    printf("DEBUG : do_sja1105 END 2\n");
++
++	return cmd_process_error(sja1105_cmd, ret);
++}
++
++U_BOOT_CMD(
++	sja1105,   2,      1,      do_sja1105,
++	"sja1105 operations",
++	"init                     Initialize sja1105 devices\n"
++	"sja1105 status                   Get sja1105 device status (not implemented)\n"
++	"sja1105 init                     Initialize sja1105 devices"
++);
+diff --git a/drivers/Kconfig b/drivers/Kconfig
+index 9d99ce0226..13e4ccaaec 100644
+--- a/drivers/Kconfig
++++ b/drivers/Kconfig
+@@ -26,6 +26,8 @@ source "drivers/ddr/Kconfig"
+
+ source "drivers/demo/Kconfig"
+
++source "drivers/scalp_sja1105/Kconfig"
++
+ source "drivers/board/Kconfig"
+
+ source "drivers/ddr/fsl/Kconfig"
+diff --git a/drivers/Makefile b/drivers/Makefile
+index e977f19af6..3a107c85a4 100644
+--- a/drivers/Makefile
++++ b/drivers/Makefile
+@@ -72,6 +72,7 @@ ifeq ($(CONFIG_SPL_BUILD)$(CONFIG_TPL_BUILD),)
+ obj-y += adc/
+ obj-y += ata/
+ obj-$(CONFIG_DM_DEMO) += demo/
++obj-$(CONFIG_SCALP_SJA1105) += scalp_sja1105/
+ obj-$(CONFIG_BIOSEMU) += bios_emulator/
+ obj-y += block/
+ obj-y += board/
+diff --git a/drivers/scalp_sja1105/Kconfig b/drivers/scalp_sja1105/Kconfig
+new file mode 100644
+index 0000000000..f6960bf4d0
+--- /dev/null
++++ b/drivers/scalp_sja1105/Kconfig
+@@ -0,0 +1,15 @@
++menu "Scalp specific"
++
++config SCALP_SJA1105
++	bool "NXP SJA1105 Ethernet switch driver for SCALP board"
++	depends on DM_SPI
++	select PACKING
++	select BITREVERSE
++	help
++	  This a basic driver for the NXP SJA1105 automotive Ethernet switch
++	  specifically created for it's use on the SCALP board. A fixed
++	  configuration is sent to the SJA1105 thru SPI to configure it
++	  as a standard switch.
++
++
++endmenu
+diff --git a/drivers/scalp_sja1105/Makefile b/drivers/scalp_sja1105/Makefile
+new file mode 100644
+index 0000000000..b89b1bd62a
+--- /dev/null
++++ b/drivers/scalp_sja1105/Makefile
+@@ -0,0 +1,5 @@
++# SPDX-License-Identifier: GPL-2.0+
++#
++# Copyright (c) 2013 Google, Inc
++
++obj-y += scalp_sja1105.o
+diff --git a/drivers/scalp_sja1105/scalp_sja1105.c b/drivers/scalp_sja1105/scalp_sja1105.c
+new file mode 100644
+index 0000000000..c0f0b67998
+--- /dev/null
++++ b/drivers/scalp_sja1105/scalp_sja1105.c
+@@ -0,0 +1,639 @@
++/*
++
++ */
++
++#define DEBUG
++#undef CONFIG_LOGLEVEL
++#define CONFIG_LOGLEVEL 8
++
++#include <common.h>
++#include <linux/packing.h>
++#include <spi.h>
++#include <dm.h>
++#include <dm/uclass-internal.h>
++
++// Static configuration generated by pyhton scripts
++#include "scalp_sja1105_static_config.h"
++
++#define SJA1105_NUM_PORTS					5
++#define SJA1105_SIZE_CGU_CMD				4
++#define SJA1105_SIZE_SPI_MSG_HEADER			4
++#define SJA1105_SIZE_RESET_CMD				4
++#define SJA1105_SIZE_SPI_MSG_MAXLEN			(64 * 4)
++#define SJA1105_ADDR_DEVICE_ID				0
++#define SJA1105_SIZE_DEVICE_ID				4
++#define SJA1105_ADDR_PROD_ID				0x100BC3
++
++#define SJA1105_ADDR_CONFIG					0x020000
++#define SJA1105_ADDR_RGU					0x100440
++#define SJA1105_ADDR_STATUS					0x1
++
++#define SJA1105T_DEVICE_ID					0x9E00030Eull
++#define SJA1105ET_PART_NO					0x9A83
++
++u64 reg_addr_rgmii_tx_clk[] = {0x100016, 0x10001D, 0x100024, 0x10002B, 0x100032};
++u64 reg_addr_pad_mii_tx[] = {0x100800, 0x100802, 0x100804, 0x100806, 0x100808};
++u64 reg_addr_cgu_idiv[] = {0x10000B, 0x10000C, 0x10000D, 0x10000E, 0x10000F};
++
++enum {
++	CLKSRC_MII0_TX_CLK	= 0x00,
++	CLKSRC_MII0_RX_CLK	= 0x01,
++	CLKSRC_MII1_TX_CLK	= 0x02,
++	CLKSRC_MII1_RX_CLK	= 0x03,
++	CLKSRC_MII2_TX_CLK	= 0x04,
++	CLKSRC_MII2_RX_CLK	= 0x05,
++	CLKSRC_MII3_TX_CLK	= 0x06,
++	CLKSRC_MII3_RX_CLK	= 0x07,
++	CLKSRC_MII4_TX_CLK	= 0x08,
++	CLKSRC_MII4_RX_CLK	= 0x09,
++	CLKSRC_PLL0		= 0x0B,
++	CLKSRC_PLL1		= 0x0E,
++	CLKSRC_IDIV0		= 0x11,
++	CLKSRC_IDIV1		= 0x12,
++	CLKSRC_IDIV2		= 0x13,
++	CLKSRC_IDIV3		= 0x14,
++	CLKSRC_IDIV4		= 0x15,
++};
++
++struct scalp_sja1105_private {
++	u64 phy_mac[5];
++	const struct scalp_sja1105_info *info;
++	struct udevice *dev;
++};
++
++typedef enum {
++	XMII_MAC = 0,
++	XMII_PHY = 1,
++} sja1105_mii_role_t;
++
++struct sja1105_cgu_mii_ctrl {
++	u64 clksrc;
++	u64 autoblock;
++	u64 pd;
++};
++
++struct sja1105_cfg_pad_mii_tx {
++	u64 d32_os;
++	u64 d32_ipud;
++	u64 d10_os;
++	u64 d10_ipud;
++	u64 ctrl_os;
++	u64 ctrl_ipud;
++	u64 clk_os;
++	u64 clk_ih;
++	u64 clk_ipud;
++};
++
++struct scalp_sja1105_info {
++	u64 device_id;
++	u64 part_no;
++	const struct sja1105_table_ops *static_ops;
++	const struct sja1105_regs *regs;
++	int (*reset_cmd)(struct scalp_sja1105_private *priv);
++	//int (*setup_rgmii_delay)(struct scalp_sja1105_private *priv, int port);
++	const char *name;
++};
++
++typedef enum {
++	SPI_READ = 0,
++	SPI_WRITE = 1,
++} sja1105_spi_rw_mode_t;
++
++struct sja1105_chunk {
++	u8	*buf;
++	size_t	len;
++	u64	reg_addr;
++};
++
++struct sja1105_spi_message {
++	u64 access;
++	u64 read_count;
++	u64 address;
++};
++
++struct sja1105_cgu_idiv {
++	u64 clksrc;
++	u64 autoblock;
++	u64 idiv;
++	u64 pd;
++};
++
++typedef enum {
++	SJA1105_SPEED_10MBPS	= 3,
++	SJA1105_SPEED_100MBPS	= 2,
++	SJA1105_SPEED_1000MBPS	= 1,
++} sja1105_speed_t;
++
++static void scalp_sja1105_packing(void *buf, u64 *val, int start, int end, size_t len, enum packing_op op)
++{
++	int rc;
++
++	rc = packing(buf, val, start, end, len, op, QUIRK_LSW32_IS_FIRST);
++	if (likely(!rc))
++		return;
++
++	printf("Invalid use of packing API: start %d end %d returned %d\n",
++	       start, end, rc);
++}
++
++static void scalp_sja1105_spi_message_pack(void *buf, struct sja1105_spi_message *msg)
++{
++	const int size = SJA1105_SIZE_SPI_MSG_HEADER;
++
++	memset(buf, 0, size);
++
++	scalp_sja1105_packing(buf, &msg->access,     31, 31, size, PACK);
++	scalp_sja1105_packing(buf, &msg->read_count, 30, 25, size, PACK);
++	scalp_sja1105_packing(buf, &msg->address,    24,  4, size, PACK);
++}
++
++static int scalp_sja1105_xfer_buf(const struct scalp_sja1105_private *priv,
++			    sja1105_spi_rw_mode_t rw, u64 reg_addr,
++			    u8 *buf, size_t len)
++{
++	struct udevice *dev = priv->dev;
++	struct sja1105_chunk chunk = {
++		.len = min_t(size_t, len, SJA1105_SIZE_SPI_MSG_MAXLEN),
++		.reg_addr = reg_addr,
++		.buf = buf,
++	};
++	int num_chunks;
++	int rc, i;
++
++	rc = dm_spi_claim_bus(dev);
++	if (rc)
++		return rc;
++
++	num_chunks = DIV_ROUND_UP(len, SJA1105_SIZE_SPI_MSG_MAXLEN);
++
++	for (i = 0; i < num_chunks; i++) {
++		u8 hdr_buf[SJA1105_SIZE_SPI_MSG_HEADER];
++		struct sja1105_spi_message msg;
++		u8 *rx_buf = NULL;
++		u8 *tx_buf = NULL;
++
++		/* Populate the transfer's header buffer */
++		msg.address = chunk.reg_addr;
++		msg.access = rw;
++		if (rw == SPI_READ)
++			msg.read_count = chunk.len / 4;
++		else
++			/* Ignored */
++			msg.read_count = 0;
++		scalp_sja1105_spi_message_pack(hdr_buf, &msg);
++		rc = dm_spi_xfer(dev, SJA1105_SIZE_SPI_MSG_HEADER * 8, hdr_buf,
++				 NULL, SPI_XFER_BEGIN);
++		if (rc)
++			goto out;
++
++		/* Populate the transfer's data buffer */
++		if (rw == SPI_READ)
++			rx_buf = chunk.buf;
++		else
++			tx_buf = chunk.buf;
++		rc = dm_spi_xfer(dev, chunk.len * 8, tx_buf, rx_buf,
++				 SPI_XFER_END);
++		if (rc)
++			goto out;
++
++		/* Calculate next chunk */
++		chunk.buf += chunk.len;
++		chunk.reg_addr += chunk.len / 4;
++		chunk.len = min_t(size_t, (ptrdiff_t)(buf + len - chunk.buf),
++				  SJA1105_SIZE_SPI_MSG_MAXLEN);
++	}
++
++out:
++	dm_spi_release_bus(dev);
++
++	return rc;
++}
++
++static int scalp_sja1105_transmit_raw(const struct scalp_sja1105_private *priv,
++									  const u8 *buf, size_t len)
++{
++	struct udevice *dev = priv->dev;
++	int rc;
++
++	if (len > 260)
++		return -EFBIG;
++
++	rc = dm_spi_claim_bus(dev);
++	if (rc)
++		return rc;
++
++	rc = dm_spi_xfer(dev, len * 8, buf, NULL, SPI_XFER_ONCE);
++	if (rc)
++		goto out;
++
++out:
++	dm_spi_release_bus(dev);
++
++	return rc;
++}
++
++/* AGU */
++static void
++sja1105_cfg_pad_mii_tx_packing(void *buf, struct sja1105_cfg_pad_mii_tx *cmd,
++			       enum packing_op op)
++{
++	const int size = 4;
++
++	scalp_sja1105_packing(buf, &cmd->d32_os,   28, 27, size, op);
++	scalp_sja1105_packing(buf, &cmd->d32_ipud, 25, 24, size, op);
++	scalp_sja1105_packing(buf, &cmd->d10_os,   20, 19, size, op);
++	scalp_sja1105_packing(buf, &cmd->d10_ipud, 17, 16, size, op);
++	scalp_sja1105_packing(buf, &cmd->ctrl_os,  12, 11, size, op);
++	scalp_sja1105_packing(buf, &cmd->ctrl_ipud, 9,  8, size, op);
++	scalp_sja1105_packing(buf, &cmd->clk_os,    4,  3, size, op);
++	scalp_sja1105_packing(buf, &cmd->clk_ih,    2,  2, size, op);
++	scalp_sja1105_packing(buf, &cmd->clk_ipud,  1,  0, size, op);
++}
++
++static void
++sja1105_cgu_mii_control_packing(void *buf, struct sja1105_cgu_mii_ctrl *cmd,
++				enum packing_op op)
++{
++	const int size = 4;
++
++	scalp_sja1105_packing(buf, &cmd->clksrc,    28, 24, size, op);
++	scalp_sja1105_packing(buf, &cmd->autoblock, 11, 11, size, op);
++	scalp_sja1105_packing(buf, &cmd->pd,         0,  0, size, op);
++}
++
++static int scalp_sja1105_check_device_id(struct scalp_sja1105_private *priv)
++{
++    u8 packed_buf[SJA1105_SIZE_DEVICE_ID] = {0};
++	u64 device_id;
++	u64 part_no;
++	int rc;
++
++	rc = scalp_sja1105_xfer_buf(priv, SPI_READ, SJA1105_ADDR_DEVICE_ID, packed_buf,
++			      SJA1105_SIZE_DEVICE_ID);
++	if (rc < 0)
++		return rc;
++
++	scalp_sja1105_packing(packed_buf, &device_id, 31, 0, SJA1105_SIZE_DEVICE_ID,
++			UNPACK);
++
++	if (device_id != SJA1105T_DEVICE_ID) {
++		printf("Expected device ID 0x%llx but read 0x%llx\n",
++		       SJA1105T_DEVICE_ID, device_id);
++		return -ENODEV;
++	}
++
++	rc = scalp_sja1105_xfer_buf(priv, SPI_READ, SJA1105_ADDR_PROD_ID, packed_buf,
++			      SJA1105_SIZE_DEVICE_ID);
++	if (rc < 0)
++		return rc;
++
++	scalp_sja1105_packing(packed_buf, &part_no, 19, 4, SJA1105_SIZE_DEVICE_ID,
++			UNPACK);
++
++	if (part_no != SJA1105ET_PART_NO) {
++		printf("Expected part number 0x%llx but read 0x%llx\n",
++		       (long long unsigned int)SJA1105ET_PART_NO, part_no);
++		return -ENODEV;
++	}
++
++	return 0;
++}
++
++struct scalp_sja1105_status {
++	u64 configs;
++	u64 crcchkl;
++	u64 ids;
++	u64 crcchkg;
++};
++
++static void scalp_sja1105_status_unpack(void *buf, struct scalp_sja1105_status *status)
++{
++	scalp_sja1105_packing(buf, &status->configs,   31, 31, 4, UNPACK);
++	scalp_sja1105_packing(buf, &status->crcchkl,   30, 30, 4, UNPACK);
++	scalp_sja1105_packing(buf, &status->ids,       29, 29, 4, UNPACK);
++	scalp_sja1105_packing(buf, &status->crcchkg,   28, 28, 4, UNPACK);
++}
++
++static int scalp_sja1105_status_get(struct scalp_sja1105_private *priv,
++			      struct scalp_sja1105_status *status)
++{
++	u8 packed_buf[4];
++	int rc;
++
++	rc = scalp_sja1105_xfer_buf(priv, SPI_READ, SJA1105_ADDR_STATUS, packed_buf, 4);
++	if (rc < 0)
++		return rc;
++
++	scalp_sja1105_status_unpack(packed_buf, status);
++
++	return 0;
++}
++
++static int scalp_sja1105et_reset_cmd(struct scalp_sja1105_private *priv)
++{
++	u8 packed_buf[SJA1105_SIZE_RESET_CMD] = {0};
++	const int size = SJA1105_SIZE_RESET_CMD;
++	u64 cold_rst = 1;
++
++	scalp_sja1105_packing(packed_buf, &cold_rst, 3, 3, size, PACK);
++
++	return scalp_sja1105_xfer_buf(priv, SPI_WRITE, SJA1105_ADDR_RGU, packed_buf,
++				SJA1105_SIZE_RESET_CMD);
++}
++
++static void sja1105_cgu_idiv_packing(void *buf, struct sja1105_cgu_idiv *idiv,
++				     enum packing_op op)
++{
++	const int size = 4;
++
++	scalp_sja1105_packing(buf, &idiv->clksrc,    28, 24, size, op);
++	scalp_sja1105_packing(buf, &idiv->autoblock, 11, 11, size, op);
++	scalp_sja1105_packing(buf, &idiv->idiv,       5,  2, size, op);
++	scalp_sja1105_packing(buf, &idiv->pd,         0,  0, size, op);
++}
++
++static int sja1105_cgu_idiv_config(struct scalp_sja1105_private *priv, int port,
++				   bool enabled, int factor)
++{
++	//const struct sja1105_regs *regs = priv->info->regs;
++	struct sja1105_cgu_idiv idiv;
++	u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0};
++
++	if (enabled && factor != 1 && factor != 10)
++		return -ERANGE;
++
++	/* Payload for packed_buf */
++	idiv.clksrc    = 0x0A;            /* 25MHz */
++	idiv.autoblock = 1;               /* Block clk automatically */
++	idiv.idiv      = factor - 1;      /* Divide by 1 or 10 */
++	idiv.pd        = enabled ? 0 : 1; /* Power down? */
++	sja1105_cgu_idiv_packing(packed_buf, &idiv, PACK);
++
++	return scalp_sja1105_xfer_buf(priv, SPI_WRITE, reg_addr_cgu_idiv[port],
++				packed_buf, SJA1105_SIZE_CGU_CMD);
++}
++
++static int scalp_sja1105_static_config_upload(struct scalp_sja1105_private *priv)
++{
++	struct scalp_sja1105_status status;
++	int rc;
++
++	/* Put the SJA1105 in programming mode */
++	rc = scalp_sja1105et_reset_cmd(priv);
++	if (rc < 0) {
++		printf("Failed to reset switch\n");
++		goto out;
++	}
++
++	/* Wait for the switch to come out of reset */
++	udelay(1000);
++
++	/* Upload the static config to the device */
++    rc  = scalp_sja1105_transmit_raw(priv, config_data_0, sizeof(config_data_0));
++    rc |= scalp_sja1105_transmit_raw(priv, config_data_1, sizeof(config_data_1));
++    rc |= scalp_sja1105_transmit_raw(priv, config_data_2, sizeof(config_data_2));
++    rc |= scalp_sja1105_transmit_raw(priv, config_data_3, sizeof(config_data_3));
++	if (rc < 0) {
++		printf("Failed to upload config\n");
++		goto out;
++	}
++
++	/* Check that SJA1105 responded well to the config upload */
++	rc = scalp_sja1105_status_get(priv, &status);
++	if (rc < 0)
++		goto out;
++
++	if (status.ids == 1) {
++		printf("Mismatch between hardware and static config device id.");
++		rc = -EIO;
++		goto out;
++	}
++	if (status.crcchkl == 1 || status.crcchkg == 1) {
++		printf("Switch reported invalid CRC on static config\n");
++		rc = -EIO;
++		goto out;
++	}
++	if (status.configs == 0) {
++		printf("Switch reported that config is invalid\n");
++		rc = -EIO;
++		goto out;
++	}
++	printf("Config uploaded\n");
++out:
++	return rc;
++}
++
++static int scalp_sja1105_static_config_load(struct scalp_sja1105_private *priv)
++{
++	return scalp_sja1105_static_config_upload(priv);
++}
++
++
++
++static int sja1105_cgu_rgmii_tx_clk_config(struct scalp_sja1105_private *priv,
++					   int port, sja1105_speed_t speed)
++{
++	struct sja1105_cgu_mii_ctrl txc;
++	u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0};
++	int clksrc;
++
++	if (speed == SJA1105_SPEED_1000MBPS) {
++		clksrc = CLKSRC_PLL0;
++	} else {
++		int clk_sources[] = {CLKSRC_IDIV0, CLKSRC_IDIV1, CLKSRC_IDIV2,
++				     CLKSRC_IDIV3, CLKSRC_IDIV4};
++		clksrc = clk_sources[port];
++	}
++
++	/* RGMII: 125MHz for 1000, 25MHz for 100, 2.5MHz for 10 */
++	txc.clksrc = clksrc;
++	/* Autoblock clk while changing clksrc */
++	txc.autoblock = 1;
++	/* Power Down off => enabled */
++	txc.pd = 0;
++	sja1105_cgu_mii_control_packing(packed_buf, &txc, PACK);
++
++	return scalp_sja1105_xfer_buf(priv, SPI_WRITE, reg_addr_rgmii_tx_clk[port],
++				packed_buf, SJA1105_SIZE_CGU_CMD);
++}
++
++static int sja1105_rgmii_cfg_pad_tx_config(struct scalp_sja1105_private *priv,
++					   int port)
++{
++	struct sja1105_cfg_pad_mii_tx pad_mii_tx;
++	u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0};
++
++	/* Payload */
++	pad_mii_tx.d32_os    = 3; /* TXD[3:2] output stage: */
++				  /*          high noise/high speed */
++	pad_mii_tx.d10_os    = 3; /* TXD[1:0] output stage: */
++				  /*          high noise/high speed */
++	pad_mii_tx.d32_ipud  = 2; /* TXD[3:2] input stage: */
++				  /*          plain input (default) */
++	pad_mii_tx.d10_ipud  = 2; /* TXD[1:0] input stage: */
++				  /*          plain input (default) */
++	pad_mii_tx.ctrl_os   = 3; /* TX_CTL / TX_ER output stage */
++	pad_mii_tx.ctrl_ipud = 2; /* TX_CTL / TX_ER input stage (default) */
++	pad_mii_tx.clk_os    = 3; /* TX_CLK output stage */
++	pad_mii_tx.clk_ih    = 0; /* TX_CLK input hysteresis (default) */
++	pad_mii_tx.clk_ipud  = 2; /* TX_CLK input stage (default) */
++	sja1105_cfg_pad_mii_tx_packing(packed_buf, &pad_mii_tx, PACK);
++
++	return scalp_sja1105_xfer_buf(priv, SPI_WRITE, reg_addr_pad_mii_tx[port],
++				packed_buf, SJA1105_SIZE_CGU_CMD);
++}
++
++static int scalp_sja1105_rgmii_clocking_setup(struct scalp_sja1105_private *priv, int port,
++					sja1105_mii_role_t role)
++{
++	//struct sja1105_mac_config_entry *mac;
++	sja1105_speed_t speed;
++	int rc;
++
++	//mac = priv->static_config.tables[BLK_IDX_MAC_CONFIG].entries;
++	speed = SJA1105_SPEED_10MBPS;
++
++	switch (speed) {
++	case SJA1105_SPEED_1000MBPS:
++		/* 1000Mbps, IDIV disabled (125 MHz) */
++		rc = sja1105_cgu_idiv_config(priv, port, false, 1);
++		break;
++	case SJA1105_SPEED_100MBPS:
++		/* 100Mbps, IDIV enabled, divide by 1 (25 MHz) */
++		rc = sja1105_cgu_idiv_config(priv, port, true, 1);
++		break;
++	case SJA1105_SPEED_10MBPS:
++		/* 10Mbps, IDIV enabled, divide by 10 (2.5 MHz) */
++		rc = sja1105_cgu_idiv_config(priv, port, true, 10);
++		break;
++	default:
++		rc = -EINVAL;
++	}
++
++	if (rc < 0) {
++		dev_err(dev, "Failed to configure idiv\n");
++		return rc;
++	}
++
++	rc = sja1105_cgu_rgmii_tx_clk_config(priv, port, speed);
++	if (rc < 0) {
++		dev_err(dev, "Failed to configure RGMII Tx clock\n");
++		return rc;
++	}
++
++	rc = sja1105_rgmii_cfg_pad_tx_config(priv, port);
++	if (rc < 0) {
++		dev_err(dev, "Failed to configure Tx pad registers\n");
++		return rc;
++	}
++
++	return 0;
++
++	// the following case is only relevant for non-T version
++	// of the chip, which have internal delay capability:
++	/*if (!priv->info->setup_rgmii_delay)
++		return 0;*/
++	/* The role has no hardware effect for RGMII. However we use it as
++	 * a proxy for this interface being a MAC-to-MAC connection, with
++	 * the RGMII internal delays needing to be applied by us.
++	 */
++	/*if (role == XMII_MAC)
++		return 0;
++
++	return priv->info->setup_rgmii_delay(priv, port);*/
++}
++
++
++static int scalp_sja1105_clocking_setup_port(struct scalp_sja1105_private *priv, int port)
++{
++	sja1105_mii_role_t role;
++	int rc;
++
++	/* MAC or PHY, for applicable types (not RGMII) */
++	role = priv->phy_mac[port];
++
++	rc = scalp_sja1105_rgmii_clocking_setup(priv, port, role);
++
++	return rc;
++}
++
++static int scalp_sja1105_clocking_setup(struct scalp_sja1105_private *priv)
++{
++	int port, rc;
++
++	for (port = 0; port < SJA1105_NUM_PORTS; port++) {
++		rc = scalp_sja1105_clocking_setup_port(priv, port);
++		if (rc < 0)
++			return rc;
++	}
++	return 0;
++}
++
++static int scalp_sja1105_init(struct scalp_sja1105_private *priv)
++{
++	int rc;
++
++    printf("DEBUG : scalp_sja1105_init START\n");
++
++	rc = scalp_sja1105_static_config_load(priv);
++	if (rc < 0) {
++		printf("Failed to load static config: %d\n", rc);
++		return rc;
++	}
++
++	priv->phy_mac[0] = XMII_PHY; /* East       , external delay = yes */
++	priv->phy_mac[1] = XMII_PHY; /* Local host , external delay = yes */
++	priv->phy_mac[2] = XMII_MAC; /* Bottom     , external delay = no  */
++	priv->phy_mac[3] = XMII_PHY; /* Top        , external delay = yes */
++	priv->phy_mac[4] = XMII_MAC; /* West       , external delay = no  */
++
++	// Configure the CGU (PHY link modes and speeds)
++	rc = scalp_sja1105_clocking_setup(priv);
++	if (rc < 0) {
++		printf("Failed to configure MII clocking: %d\n", rc);
++		return rc;
++	}
++
++	return 0;
++}
++
++static int scalp_sja1105_probe(struct udevice *dev)
++{
++    printf("DEBUG : scalp_sja1105_probe START\n");
++
++	pr_warn("scalp_sja1105_probe()\n");
++
++	struct scalp_sja1105_private *priv = dev_get_priv(dev);
++
++	int rc;
++
++	// FIXME: usefull ?
++	if (ofnode_valid(dev->node) && !ofnode_is_available(dev->node)) {
++		dev_dbg(dev, "switch disabled\n");
++		return -ENODEV;
++	}
++
++	priv->dev = dev;
++
++	rc = scalp_sja1105_check_device_id(priv);
++	if (rc < 0) {
++		dev_err(dev, "Device ID check failed: %d\n", rc);
++		return rc;
++	}
++	dev_dbg(dev, "Device ID check succeeded\n");
++
++	return scalp_sja1105_init(priv);
++}
++
++static const struct udevice_id scalp_sja1105_of_match[] = {
++	{ .compatible = "scalp,sja1105t"},
++	{},
++};
++
++U_BOOT_DRIVER(scalp_sja1105) = {
++	.name = "scalp_sja1105",
++	.id = UCLASS_SPI,
++	.of_match = scalp_sja1105_of_match,
++	.priv_auto_alloc_size		= sizeof(struct scalp_sja1105_private),
++	.probe = scalp_sja1105_probe,
++};
+diff --git a/drivers/scalp_sja1105/scalp_sja1105_static_config.h b/drivers/scalp_sja1105/scalp_sja1105_static_config.h
+new file mode 100644
+index 0000000000..3a736522b1
+--- /dev/null
++++ b/drivers/scalp_sja1105/scalp_sja1105_static_config.h
+@@ -0,0 +1,9 @@
++#ifndef _SCALP_SJA1105_STATIC_CONFIG_H
++#define _SCALP_SJA1105_STATIC_CONFIG_H
++
++static const uint8_t config_data_0[] = { 0x80, 0x20, 0x00, 0x00, 0x9E, 0x00, 0x03, 0x0E, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x21, 0x6F, 0x25, 0x6B, 0xFE, 0xF7, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFE, 0xF7, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFE, 0xF7, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFE, 0xF7, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFE, 0xF7, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFE, 0xF7, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFE, 0xF7, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFE, 0xF7, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFE, 0xF7, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFE, 0xF7, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFE, 0xF7, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFE, 0xF7, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFE, 0xF7, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFE, 0xF7, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFE, 0xF7, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFE, 0xF7, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFE, 0xF7, 0x00, 0x00, 0x0B, 0xFF, 0xFF, 0xFF, 0xFE, 0xF7, 0x00, 0x00, 0x0B, 0xFF, 0xFF, 0xFF, 0xFE, 0xF7, 0x00, 0x00, 0x0B, 0xFF, 0xFF, 0xFF, 0xFE, 0xF7, 0x00, 0x00, 0x0B, 0xFF, 0xFF, 0xFF, 0xFE, 0xF7, 0x00, 0x00, 0x0B, 0xFF, 0xFF, 0xFF, 0xFE, 0xF7, 0x00, 0x00, 0x0B, 0xFF, 0xFF, 0xFF, 0xFE, 0xF7, 0x00, 0x00, 0x0B, 0xFF, 0xFF, 0xFF, 0xFE, 0xF7, 0x00, 0x00, 0x0B, 0xFF, 0xFF, 0xFF, 0xFE, 0xF7, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFE, 0xF7, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFE, 0xF7, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFE, 0xF7, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFE, 0xF7, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFE, 0xF7, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF };
++static const uint8_t config_data_1[] = { 0x80, 0x20, 0x04, 0x00, 0xFE, 0xF7, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFE, 0xF7, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFE, 0xF7, 0x00, 0x00, 0x13, 0xFF, 0xFF, 0xFF, 0xFE, 0xF7, 0x00, 0x00, 0x13, 0xFF, 0xFF, 0xFF, 0xFE, 0xF7, 0x00, 0x00, 0x13, 0xFF, 0xFF, 0xFF, 0xFE, 0xF7, 0x00, 0x00, 0x13, 0xFF, 0xFF, 0xFF, 0xFE, 0xF7, 0x00, 0x00, 0x13, 0xFF, 0xFF, 0xFF, 0xFE, 0xF7, 0x00, 0x00, 0x13, 0xFF, 0xFF, 0xFF, 0xFE, 0xF7, 0x00, 0x00, 0x13, 0xFF, 0xFF, 0xFF, 0xFE, 0xF7, 0x00, 0x00, 0x13, 0xFF, 0xFF, 0xFF, 0xFA, 0x2E, 0x19, 0xF8, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x58, 0x60, 0x94, 0x2E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xF0, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0x80, 0xC7, 0xEF, 0x16, 0xA7, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A, 0x6A, 0xF6, 0x23, 0x53, 0x10, 0x00, 0x00, 0x00, 0xF7, 0xBD, 0xF5, 0x8D, 0x10, 0x00, 0x00, 0x00, 0xEF, 0x7B, 0xF5, 0x8D, 0x10, 0x00, 0x00, 0x00, 0xDE, 0xF7, 0xF5, 0x8D, 0x10, 0x00, 0x00, 0x00, 0xBD, 0xEF, 0xF5, 0x8D, 0x10, 0x00, 0x00, 0x00, 0x7B, 0xDF, 0xF5, 0x8D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x04, 0xA6, 0x06, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0xDA, 0xB5, 0xBD, 0xC8, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00 };
++static const uint8_t config_data_2[] = { 0x80, 0x20, 0x08, 0x00, 0x07, 0xFC, 0x01, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFC, 0x01, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFC, 0x01, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFC, 0x01, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFC, 0x01, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA3, 0x8C, 0xFC, 0x39, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x25, 0x0E, 0x7C, 0xBD, 0x00, 0x01, 0x25, 0xC0, 0x70, 0x94, 0x84, 0x50, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xC8, 0xA7, 0xCE, 0xE6, 0x00, 0x71, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC3, 0xF7, 0x04, 0xB9, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x57, 0x1F, 0x81, 0x3F, 0x06, 0x44, 0x00, 0x00, 0x00, 0x00, 0x04, 0x08, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x0D, 0x80, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0xCE, 0xDD, 0x8B, 0x4F, 0x4E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x3A, 0x5D, 0x5E, 0x24, 0x59, 0x6C, 0x00, 0x00 };
++static const uint8_t config_data_3[] = { 0x80, 0x20, 0x0C, 0x00, 0xFA, 0xBB, 0x12, 0xE7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDF, 0x65, 0xAA, 0x65 };
++
++#endif
+\ No newline at end of file
+diff --git a/include/linux/packing.h b/include/linux/packing.h
+new file mode 100644
+index 0000000000..c2741b8d2e
+--- /dev/null
++++ b/include/linux/packing.h
+@@ -0,0 +1,49 @@
++/* SPDX-License-Identifier: BSD-3-Clause
++ * Copyright (c) 2016-2018, NXP Semiconductors
++ * Copyright (c) 2018-2019, Vladimir Oltean <olteanv@gmail.com>
++ */
++#ifndef _LINUX_PACKING_H
++#define _LINUX_PACKING_H
++
++#include <linux/types.h>
++#include <linux/bitops.h>
++
++#define QUIRK_MSB_ON_THE_RIGHT	BIT(0)
++#define QUIRK_LITTLE_ENDIAN	BIT(1)
++#define QUIRK_LSW32_IS_FIRST	BIT(2)
++
++enum packing_op {
++	PACK,
++	UNPACK,
++};
++
++/**
++ * packing - Convert numbers (currently u64) between a packed and an unpacked
++ *	     format. Unpacked means laid out in memory in the CPU's native
++ *	     understanding of integers, while packed means anything else that
++ *	     requires translation.
++ *
++ * @pbuf: Pointer to a buffer holding the packed value.
++ * @uval: Pointer to an u64 holding the unpacked value.
++ * @startbit: The index (in logical notation, compensated for quirks) where
++ *	      the packed value starts within pbuf. Must be larger than, or
++ *	      equal to, endbit.
++ * @endbit: The index (in logical notation, compensated for quirks) where
++ *	    the packed value ends within pbuf. Must be smaller than, or equal
++ *	    to, startbit.
++ * @op: If PACK, then uval will be treated as const pointer and copied (packed)
++ *	into pbuf, between startbit and endbit.
++ *	If UNPACK, then pbuf will be treated as const pointer and the logical
++ *	value between startbit and endbit will be copied (unpacked) to uval.
++ * @quirks: A bit mask of QUIRK_LITTLE_ENDIAN, QUIRK_LSW32_IS_FIRST and
++ *	    QUIRK_MSB_ON_THE_RIGHT.
++ *
++ * Return: 0 on success, EINVAL or ERANGE if called incorrectly. Assuming
++ *	   correct usage, return code may be discarded.
++ *	   If op is PACK, pbuf is modified.
++ *	   If op is UNPACK, uval is modified.
++ */
++int packing(void *pbuf, u64 *uval, int startbit, int endbit, size_t pbuflen,
++	    enum packing_op op, u8 quirks);
++
++#endif
+\ No newline at end of file
+diff --git a/lib/Kconfig b/lib/Kconfig
+index 965cf7bc03..a828b80a40 100644
+--- a/lib/Kconfig
++++ b/lib/Kconfig
+@@ -33,6 +33,23 @@ config HAVE_PRIVATE_LIBGCC
+ config LIB_UUID
+ 	bool
+
++config PACKING
++	bool "Generic bitfield packing and unpacking"
++	default n
++	help
++	  This option provides the packing() helper function, which permits
++	  converting bitfields between a CPU-usable representation and a
++	  memory representation that can have any combination of these quirks:
++	    - Is little endian (bytes are reversed within a 32-bit group)
++	    - The least-significant 32-bit word comes first (within a 64-bit
++	      group)
++	    - The most significant bit of a byte is at its right (bit 0 of a
++	      register description is numerically 2^7).
++	  Drivers may use these helpers to match the bit indices as described
++	  in the data sheets of the peripherals they are in control of.
++
++	  When in doubt, say N.
++
+ config PRINTF
+ 	bool
+ 	default y
+diff --git a/lib/Makefile b/lib/Makefile
+index 1fb650cd90..4f99bd47c2 100644
+--- a/lib/Makefile
++++ b/lib/Makefile
+@@ -104,6 +104,7 @@ obj-y += hexdump.o
+ obj-$(CONFIG_TRACE) += trace.o
+ obj-$(CONFIG_LIB_UUID) += uuid.o
+ obj-$(CONFIG_LIB_RAND) += rand.o
++obj-$(CONFIG_PACKING) += packing.o
+ obj-y += panic.o
+
+ ifeq ($(CONFIG_$(SPL_TPL_)BUILD),y)
+diff --git a/lib/packing.c b/lib/packing.c
+new file mode 100644
+index 0000000000..ec576ea6c0
+--- /dev/null
++++ b/lib/packing.c
+@@ -0,0 +1,208 @@
++// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
++/* Copyright (c) 2016-2018, NXP Semiconductors
++ * Copyright (c) 2018-2019, Vladimir Oltean <olteanv@gmail.com>
++ */
++#include <linux/packing.h>
++#include <linux/bitops.h>
++#include <linux/errno.h>
++#include <linux/types.h>
++
++static int get_le_offset(int offset)
++{
++	int closest_multiple_of_4;
++
++	closest_multiple_of_4 = (offset / 4) * 4;
++	offset -= closest_multiple_of_4;
++	return closest_multiple_of_4 + (3 - offset);
++}
++
++static int get_reverse_lsw32_offset(int offset, size_t len)
++{
++	int closest_multiple_of_4;
++	int word_index;
++
++	word_index = offset / 4;
++	closest_multiple_of_4 = word_index * 4;
++	offset -= closest_multiple_of_4;
++	word_index = (len / 4) - word_index - 1;
++	return word_index * 4 + offset;
++}
++
++static u64 bit_reverse(u64 val, unsigned int width)
++{
++	u64 new_val = 0;
++	unsigned int bit;
++	unsigned int i;
++
++	for (i = 0; i < width; i++) {
++		bit = (val & (1 << i)) != 0;
++		new_val |= (bit << (width - i - 1));
++	}
++	return new_val;
++}
++
++static void adjust_for_msb_right_quirk(u64 *to_write, int *box_start_bit,
++				       int *box_end_bit, u8 *box_mask)
++{
++	int box_bit_width = *box_start_bit - *box_end_bit + 1;
++	int new_box_start_bit, new_box_end_bit;
++
++	*to_write >>= *box_end_bit;
++	*to_write = bit_reverse(*to_write, box_bit_width);
++	*to_write <<= *box_end_bit;
++
++	new_box_end_bit   = box_bit_width - *box_start_bit - 1;
++	new_box_start_bit = box_bit_width - *box_end_bit - 1;
++	*box_mask = GENMASK_ULL(new_box_start_bit, new_box_end_bit);
++	*box_start_bit = new_box_start_bit;
++	*box_end_bit   = new_box_end_bit;
++}
++
++/**
++ * packing - Convert numbers (currently u64) between a packed and an unpacked
++ *	     format. Unpacked means laid out in memory in the CPU's native
++ *	     understanding of integers, while packed means anything else that
++ *	     requires translation.
++ *
++ * @pbuf: Pointer to a buffer holding the packed value.
++ * @uval: Pointer to an u64 holding the unpacked value.
++ * @startbit: The index (in logical notation, compensated for quirks) where
++ *	      the packed value starts within pbuf. Must be larger than, or
++ *	      equal to, endbit.
++ * @endbit: The index (in logical notation, compensated for quirks) where
++ *	    the packed value ends within pbuf. Must be smaller than, or equal
++ *	    to, startbit.
++ * @op: If PACK, then uval will be treated as const pointer and copied (packed)
++ *	into pbuf, between startbit and endbit.
++ *	If UNPACK, then pbuf will be treated as const pointer and the logical
++ *	value between startbit and endbit will be copied (unpacked) to uval.
++ * @quirks: A bit mask of QUIRK_LITTLE_ENDIAN, QUIRK_LSW32_IS_FIRST and
++ *	    QUIRK_MSB_ON_THE_RIGHT.
++ *
++ * Return: 0 on success, EINVAL or ERANGE if called incorrectly. Assuming
++ *	   correct usage, return code may be discarded.
++ *	   If op is PACK, pbuf is modified.
++ *	   If op is UNPACK, uval is modified.
++ */
++int packing(void *pbuf, u64 *uval, int startbit, int endbit, size_t pbuflen,
++	    enum packing_op op, u8 quirks)
++{
++	/* Number of bits for storing "uval"
++	 * also width of the field to access in the pbuf
++	 */
++	u64 value_width;
++	/* Logical byte indices corresponding to the
++	 * start and end of the field.
++	 */
++	int plogical_first_u8, plogical_last_u8, box;
++
++	/* startbit is expected to be larger than endbit */
++	if (startbit < endbit)
++		/* Invalid function call */
++		return -EINVAL;
++
++	value_width = startbit - endbit + 1;
++	if (value_width > 64)
++		return -ERANGE;
++
++	/* Check if "uval" fits in "value_width" bits.
++	 * If value_width is 64, the check will fail, but any
++	 * 64-bit uval will surely fit.
++	 */
++	if (op == PACK && value_width < 64 && (*uval >= (1ull << value_width)))
++		/* Cannot store "uval" inside "value_width" bits.
++		 * Truncating "uval" is most certainly not desirable,
++		 * so simply erroring out is appropriate.
++		 */
++		return -ERANGE;
++
++	/* Initialize parameter */
++	if (op == UNPACK)
++		*uval = 0;
++
++	/* Iterate through an idealistic view of the pbuf as an u64 with
++	 * no quirks, u8 by u8 (aligned at u8 boundaries), from high to low
++	 * logical bit significance. "box" denotes the current logical u8.
++	 */
++	plogical_first_u8 = startbit / 8;
++	plogical_last_u8  = endbit / 8;
++
++	for (box = plogical_first_u8; box >= plogical_last_u8; box--) {
++		/* Bit indices into the currently accessed 8-bit box */
++		int box_start_bit, box_end_bit, box_addr;
++		u8  box_mask;
++		/* Corresponding bits from the unpacked u64 parameter */
++		int proj_start_bit, proj_end_bit;
++		u64 proj_mask;
++
++		/* This u8 may need to be accessed in its entirety
++		 * (from bit 7 to bit 0), or not, depending on the
++		 * input arguments startbit and endbit.
++		 */
++		if (box == plogical_first_u8)
++			box_start_bit = startbit % 8;
++		else
++			box_start_bit = 7;
++		if (box == plogical_last_u8)
++			box_end_bit = endbit % 8;
++		else
++			box_end_bit = 0;
++
++		/* We have determined the box bit start and end.
++		 * Now we calculate where this (masked) u8 box would fit
++		 * in the unpacked (CPU-readable) u64 - the u8 box's
++		 * projection onto the unpacked u64. Though the
++		 * box is u8, the projection is u64 because it may fall
++		 * anywhere within the unpacked u64.
++		 */
++		proj_start_bit = ((box * 8) + box_start_bit) - endbit;
++		proj_end_bit   = ((box * 8) + box_end_bit) - endbit;
++		proj_mask = GENMASK_ULL(proj_start_bit, proj_end_bit);
++		box_mask  = GENMASK_ULL(box_start_bit, box_end_bit);
++
++		/* Determine the offset of the u8 box inside the pbuf,
++		 * adjusted for quirks. The adjusted box_addr will be used for
++		 * effective addressing inside the pbuf (so it's not
++		 * logical any longer).
++		 */
++		box_addr = pbuflen - box - 1;
++		if (quirks & QUIRK_LITTLE_ENDIAN)
++			box_addr = get_le_offset(box_addr);
++		if (quirks & QUIRK_LSW32_IS_FIRST)
++			box_addr = get_reverse_lsw32_offset(box_addr,
++							    pbuflen);
++
++		if (op == UNPACK) {
++			u64 pval;
++
++			/* Read from pbuf, write to uval */
++			pval = ((u8 *)pbuf)[box_addr] & box_mask;
++			if (quirks & QUIRK_MSB_ON_THE_RIGHT)
++				adjust_for_msb_right_quirk(&pval,
++							   &box_start_bit,
++							   &box_end_bit,
++							   &box_mask);
++
++			pval >>= box_end_bit;
++			pval <<= proj_end_bit;
++			*uval &= ~proj_mask;
++			*uval |= pval;
++		} else {
++			u64 pval;
++
++			/* Write to pbuf, read from uval */
++			pval = (*uval) & proj_mask;
++			pval >>= proj_end_bit;
++			if (quirks & QUIRK_MSB_ON_THE_RIGHT)
++				adjust_for_msb_right_quirk(&pval,
++							   &box_start_bit,
++							   &box_end_bit,
++							   &box_mask);
++
++			pval <<= box_end_bit;
++			((u8 *)pbuf)[box_addr] &= ~box_mask;
++			((u8 *)pbuf)[box_addr] |= pval;
++		}
++	}
++	return 0;
++}
+\ No newline at end of file
diff --git a/scalp_safe_petalinux/project-spec/meta-user/recipes-bsp/u-boot/files/u-boot-scalp-sja1105-official.patch b/scalp_safe_petalinux/project-spec/meta-user/recipes-bsp/u-boot/files/u-boot-scalp-sja1105-official.patch
new file mode 100644
index 0000000000000000000000000000000000000000..c23166ecbddcc54b477f803939f085a02eb79236
--- /dev/null
+++ b/scalp_safe_petalinux/project-spec/meta-user/recipes-bsp/u-boot/files/u-boot-scalp-sja1105-official.patch
@@ -0,0 +1,3280 @@
+diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
+index 2d606b0dbb..b6f74f7320 100644
+--- a/drivers/net/Kconfig
++++ b/drivers/net/Kconfig
+@@ -37,6 +37,19 @@ config DM_MDIO_MUX
+ 	  This is currently implemented in net/mdio-mux-uclass.c
+ 	  Look in include/miiphy.h for details.
+ 
++config DM_DSA
++	bool "Enable Driver Model for DSA switches"
++	depends on DM_ETH && DM_MDIO
++	help
++	  Enable Driver Model for DSA switches
++
++	  Adds UCLASS_DSA class supporting switches that follow the Distributed
++	  Switch Architecture (DSA).  These switches rely on the presence of a
++	  management switch port connected to an Ethernet controller capable of
++	  receiving frames from the switch.  This host Ethernet controller is
++	  called "master" and "cpu" in DSA terminology.
++	  This is currently implemented in net/dsa-uclass.c
++
+ config MDIO_SANDBOX
+ 	depends on DM_MDIO && SANDBOX
+ 	default y
+@@ -57,6 +70,14 @@ config MDIO_MUX_SANDBOX
+ 
+ 	  This driver is used for testing in test/dm/mdio.c
+ 
++config DSA_SANDBOX
++	depends on DM_DSA && SANDBOX
++	default y
++	bool "Sandbox: Mocked DSA driver"
++	help
++	  This driver implements a dummy switch and a dummy Ethernet device used
++	  to test DSA class code.
++
+ menuconfig NETDEVICES
+ 	bool "Network device support"
+ 	depends on NET
+@@ -360,6 +381,23 @@ config RTL8169
+ 	  This driver supports Realtek 8169 series gigabit ethernet family of
+ 	  PCI/PCIe chipsets/adapters.
+ 
++config SJA1105
++	bool "NXP SJA1105 Ethernet switch family driver"
++	depends on DM_DSA && DM_SPI
++	select BITREVERSE
++	select PACKING
++	help
++	  This is the driver for the NXP SJA1105 automotive Ethernet switch
++	  family. These are 5-port devices and are managed over an SPI
++	  interface. Probing is handled based on OF bindings. The driver
++	  supports the following revisions:
++	    - SJA1105E (Gen. 1, No TT-Ethernet)
++	    - SJA1105T (Gen. 1, TT-Ethernet)
++	    - SJA1105P (Gen. 2, No SGMII, No TT-Ethernet)
++	    - SJA1105Q (Gen. 2, No SGMII, TT-Ethernet)
++	    - SJA1105R (Gen. 2, SGMII, No TT-Ethernet)
++	    - SJA1105S (Gen. 2, SGMII, TT-Ethernet)
++
+ config SMC911X
+ 	bool "SMSC LAN911x and LAN921x controller driver"
+ 
+diff --git a/drivers/net/Makefile b/drivers/net/Makefile
+index 30991834ec..67204ef3f4 100644
+--- a/drivers/net/Makefile
++++ b/drivers/net/Makefile
+@@ -22,6 +22,7 @@ obj-$(CONFIG_DNET) += dnet.o
+ obj-$(CONFIG_E1000) += e1000.o
+ obj-$(CONFIG_E1000_SPI) += e1000_spi.o
+ obj-$(CONFIG_EEPRO100) += eepro100.o
++obj-$(CONFIG_SJA1105) += sja1105.o
+ obj-$(CONFIG_SUN4I_EMAC) += sunxi_emac.o
+ obj-$(CONFIG_SUN8I_EMAC) += sun8i_emac.o
+ obj-$(CONFIG_EP93XX) += ep93xx_eth.o
+@@ -82,3 +83,4 @@ obj-y += mscc_eswitch/
+ obj-$(CONFIG_HIGMACV300_ETH) += higmacv300.o
+ obj-$(CONFIG_MDIO_SANDBOX) += mdio_sandbox.o
+ obj-$(CONFIG_FSL_ENETC) += fsl_enetc.o fsl_enetc_mdio.o
++obj-$(CONFIG_DSA_SANDBOX) += dsa_sandbox.o
+diff --git a/drivers/net/dsa_sandbox.c b/drivers/net/dsa_sandbox.c
+new file mode 100644
+index 0000000000..eb98274430
+--- /dev/null
++++ b/drivers/net/dsa_sandbox.c
+@@ -0,0 +1,272 @@
++// SPDX-License-Identifier: GPL-2.0+
++/*
++ * Copyright 2019 NXP
++ */
++
++#include <net/dsa.h>
++
++#define DSA_SANDBOX_MAGIC	0x00415344
++#define DSA_SANDBOX_TAG_LEN	sizeof(struct dsa_sandbox_tag)
++/*
++ * This global flag is used to enable DSA just for DSA test so it doesn't affect
++ * the existing eth unit test.
++ */
++int dsa_sandbox_port_mask;
++
++struct dsa_sandbox_priv {
++	int enabled;
++	int port_enabled;
++};
++
++struct dsa_sandbox_tag {
++	u32 magic;
++	u32 port;
++};
++
++static int dsa_sandbox_port_enable(struct udevice *dev, int port,
++				   struct phy_device *phy)
++{
++	struct dsa_sandbox_priv *priv = dev->priv;
++
++	if (!priv->enabled)
++		return -EFAULT;
++
++	priv->port_enabled |= BIT(port);
++
++	return 0;
++}
++
++static void dsa_sandbox_port_disable(struct udevice *dev, int port,
++				     struct phy_device *phy)
++{
++	struct dsa_sandbox_priv *priv = dev->priv;
++
++	if (!priv->enabled)
++		return;
++
++	priv->port_enabled &= ~BIT(port);
++}
++
++static int dsa_sandbox_xmit(struct udevice *dev, int port, void *packet,
++			    int length)
++{
++	struct dsa_sandbox_priv *priv = dev->priv;
++	struct dsa_sandbox_tag *tag = packet;
++
++	if (!priv->enabled)
++		return -EFAULT;
++
++	if (!(priv->port_enabled & BIT(port)))
++		return -EFAULT;
++
++	tag->magic = DSA_SANDBOX_MAGIC;
++	tag->port = port;
++
++	return 0;
++}
++
++static int dsa_sandbox_rcv(struct udevice *dev, int *port, void *packet,
++			   int length)
++{
++	struct dsa_sandbox_priv *priv = dev->priv;
++	struct dsa_sandbox_tag *tag = packet;
++
++	if (!priv->enabled)
++		return -EFAULT;
++
++	if (tag->magic != DSA_SANDBOX_MAGIC)
++		return -EFAULT;
++
++	*port = tag->port;
++	if (!(priv->port_enabled & BIT(*port)))
++		return -EFAULT;
++
++	return 0;
++}
++
++static const struct dsa_ops dsa_sandbox_ops = {
++	.port_enable = dsa_sandbox_port_enable,
++	.port_disable = dsa_sandbox_port_disable,
++	.xmit = dsa_sandbox_xmit,
++	.rcv = dsa_sandbox_rcv,
++};
++
++static int dsa_sandbox_bind(struct udevice *dev)
++{
++	struct dsa_perdev_platdata *pdata = dev->platdata;
++
++	/* must be at least 4 to match sandbox test DT */
++	pdata->num_ports = 4;
++	pdata->headroom = DSA_SANDBOX_TAG_LEN;
++
++	return 0;
++}
++
++static int dsa_sandbox_probe(struct udevice *dev)
++{
++	struct dsa_sandbox_priv *priv = dev_get_priv(dev);
++
++	/*
++	 * return error if DSA is not being tested so we don't break existing
++	 * eth test.
++	 */
++	if (!dsa_sandbox_port_mask)
++		return -EINVAL;
++
++	priv->enabled = 1;
++
++	return 0;
++}
++
++static int dsa_sandbox_remove(struct udevice *dev)
++{
++	struct dsa_sandbox_priv *priv = dev_get_priv(dev);
++
++	priv->enabled = 0;
++
++	return 0;
++}
++
++static const struct udevice_id dsa_sandbox_ids[] = {
++	{ .compatible = "sandbox,dsa" },
++	{ }
++};
++
++U_BOOT_DRIVER(dsa_sandbox) = {
++	.name		= "dsa_sandbox",
++	.id		= UCLASS_DSA,
++	.of_match	= dsa_sandbox_ids,
++	.bind		= dsa_sandbox_bind,
++	.probe		= dsa_sandbox_probe,
++	.remove		= dsa_sandbox_remove,
++	.ops		= &dsa_sandbox_ops,
++	.priv_auto_alloc_size = sizeof(struct dsa_sandbox_priv),
++	.platdata_auto_alloc_size = sizeof(struct dsa_perdev_platdata),
++};
++
++struct dsa_sandbox_eth_priv {
++	int enabled;
++	int started;
++	int packet_length;
++	uchar packet[DSA_MAX_FRAME_SIZE];
++};
++
++static int dsa_eth_sandbox_start(struct udevice *dev)
++{
++	struct dsa_sandbox_eth_priv *priv = dev->priv;
++
++	if (!priv->enabled)
++		return -EFAULT;
++
++	priv->started = 1;
++
++	return 0;
++}
++
++static void dsa_eth_sandbox_stop(struct udevice *dev)
++{
++	struct dsa_sandbox_eth_priv *priv = dev->priv;
++
++	if (!priv->enabled)
++		return;
++
++	priv->started = 0;
++}
++
++static int dsa_eth_sandbox_send(struct udevice *dev, void *packet, int length)
++{
++	struct dsa_sandbox_eth_priv *priv = dev->priv;
++	struct dsa_sandbox_tag *tag = packet;
++
++	if (!priv->enabled || !priv->started)
++		return -EFAULT;
++
++	memcpy(priv->packet, packet, length);
++	priv->packet_length = length;
++
++	/*
++	 * for DSA test frames we only respond if the associated port is enabled
++	 * in the dsa test port mask
++	 */
++
++	if (tag->magic == DSA_SANDBOX_MAGIC) {
++		int port = tag->port;
++
++		if (!(dsa_sandbox_port_mask & BIT(port)))
++			/* drop the frame, port is not enabled */
++			priv->packet_length = 0;
++	}
++
++	return 0;
++}
++
++static int dsa_eth_sandbox_recv(struct udevice *dev, int flags, uchar **packetp)
++{
++	struct dsa_sandbox_eth_priv *priv = dev->priv;
++	int length = priv->packet_length;
++
++	if (!priv->enabled || !priv->started)
++		return -EFAULT;
++
++	if (!length) {
++		/* no frames pending, force a time-out */
++		timer_test_add_offset(100);
++		return -EAGAIN;
++	}
++
++	*packetp = priv->packet;
++	priv->packet_length = 0;
++
++	return length;
++}
++
++static const struct eth_ops dsa_eth_sandbox_ops = {
++	.start	= dsa_eth_sandbox_start,
++	.send	= dsa_eth_sandbox_send,
++	.recv	= dsa_eth_sandbox_recv,
++	.stop	= dsa_eth_sandbox_stop,
++};
++
++static int dsa_eth_sandbox_bind(struct udevice *dev)
++{
++	return 0;
++}
++
++static int dsa_eth_sandbox_probe(struct udevice *dev)
++{
++	struct dsa_sandbox_eth_priv *priv = dev->priv;
++
++	priv->enabled = 1;
++
++	/*
++	 * return error if DSA is not being tested do we don't break existing
++	 * eth test.
++	 */
++	return dsa_sandbox_port_mask ? 0 : -EINVAL;
++}
++
++static int dsa_eth_sandbox_remove(struct udevice *dev)
++{
++	struct dsa_sandbox_eth_priv *priv = dev->priv;
++
++	priv->enabled = 0;
++
++	return 0;
++}
++
++static const struct udevice_id dsa_eth_sandbox_ids[] = {
++	{ .compatible = "sandbox,dsa-eth" },
++	{ }
++};
++
++U_BOOT_DRIVER(dsa_eth_sandbox) = {
++	.name		= "dsa_eth_sandbox",
++	.id		= UCLASS_ETH,
++	.of_match	= dsa_eth_sandbox_ids,
++	.bind		= dsa_eth_sandbox_bind,
++	.probe		= dsa_eth_sandbox_probe,
++	.remove		= dsa_eth_sandbox_remove,
++	.ops		= &dsa_eth_sandbox_ops,
++	.platdata_auto_alloc_size = sizeof(struct eth_pdata),
++	.priv_auto_alloc_size = sizeof(struct dsa_sandbox_eth_priv),
++};
+diff --git a/drivers/net/sja1105.c b/drivers/net/sja1105.c
+new file mode 100644
+index 0000000000..775ec7774e
+--- /dev/null
++++ b/drivers/net/sja1105.c
+@@ -0,0 +1,2416 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright 2016-2018,2020 NXP
++ * Copyright 2018, Sensor-Technik Wiedemann GmbH
++ *
++ * Ported from Linux (drivers/net/dsa/sja1105/).
++ */
++
++#include <common.h>
++#include <linux/if_vlan.h>
++#include <linux/packing.h>
++#include <linux/bitrev.h>
++#include <net/dsa.h>
++#include <stdlib.h>
++#include <spi.h>
++
++#define ETHER_CRC32_POLY				0x04C11DB7
++#define ETH_P_SJA1105					0xdadb
++#define SJA1105_NUM_PORTS				5
++#define SJA1105_NUM_TC					8
++#define SJA1105ET_FDB_BIN_SIZE				4
++#define SJA1105_SIZE_CGU_CMD				4
++#define SJA1105_SIZE_RESET_CMD				4
++#define SJA1105_SIZE_SPI_MSG_HEADER			4
++#define SJA1105_SIZE_SPI_MSG_MAXLEN			(64 * 4)
++#define SJA1105_SIZE_DEVICE_ID				4
++#define SJA1105_SIZE_TABLE_HEADER			12
++#define SJA1105_SIZE_L2_POLICING_ENTRY			8
++#define SJA1105_SIZE_VLAN_LOOKUP_ENTRY			8
++#define SJA1105_SIZE_L2_FORWARDING_ENTRY		8
++#define SJA1105_SIZE_L2_FORWARDING_PARAMS_ENTRY		12
++#define SJA1105_SIZE_XMII_PARAMS_ENTRY			4
++#define SJA1105ET_SIZE_MAC_CONFIG_ENTRY			28
++#define SJA1105ET_SIZE_L2_LOOKUP_PARAMS_ENTRY		4
++#define SJA1105ET_SIZE_GENERAL_PARAMS_ENTRY		40
++#define SJA1105PQRS_SIZE_MAC_CONFIG_ENTRY		32
++#define SJA1105PQRS_SIZE_L2_LOOKUP_PARAMS_ENTRY		16
++#define SJA1105PQRS_SIZE_GENERAL_PARAMS_ENTRY		44
++
++#define SJA1105_MAX_L2_LOOKUP_COUNT			1024
++#define SJA1105_MAX_L2_POLICING_COUNT			45
++#define SJA1105_MAX_VLAN_LOOKUP_COUNT			4096
++#define SJA1105_MAX_L2_FORWARDING_COUNT			13
++#define SJA1105_MAX_MAC_CONFIG_COUNT			5
++#define SJA1105_MAX_L2_LOOKUP_PARAMS_COUNT		1
++#define SJA1105_MAX_L2_FORWARDING_PARAMS_COUNT		1
++#define SJA1105_MAX_GENERAL_PARAMS_COUNT		1
++#define SJA1105_MAX_XMII_PARAMS_COUNT			1
++
++#define SJA1105_SGMII_PORT				4
++
++/* DIGITAL_CONTROL_1 (address 1f8000h) */
++#define SJA1105_DC1_EN_VSMMD1				BIT(13)
++#define SJA1105_DC1_CLOCK_STOP_EN			BIT(10)
++#define SJA1105_DC1_MAC_AUTO_SW				BIT(9)
++#define SJA1105_DC1_INIT				BIT(8)
++
++/* DIGITAL_CONTROL_2 register (address 1f80e1h) */
++#define SJA1105_DC2_TX_POL_INV_DISABLE			BIT(4)
++#define SJA1105_DC2_RX_POL_INV				BIT(0)
++
++/* AUTONEG_CONTROL register (address 1f8001h) */
++#define SJA1105_AC_SGMII_LINK				BIT(4)
++#define SJA1105_AC_PHY_MODE				BIT(3)
++#define SJA1105_AC_AUTONEG_MODE_SGMII			(2 << 1)
++
++#define SJA1105_MAX_FRAME_MEMORY			929
++
++#define SJA1105E_DEVICE_ID				0x9C00000Cull
++#define SJA1105T_DEVICE_ID				0x9E00030Eull
++#define SJA1105PR_DEVICE_ID				0xAF00030Eull
++#define SJA1105QS_DEVICE_ID				0xAE00030Eull
++
++#define SJA1105ET_PART_NO				0x9A83
++#define SJA1105P_PART_NO				0x9A84
++#define SJA1105Q_PART_NO				0x9A85
++#define SJA1105R_PART_NO				0x9A86
++#define SJA1105S_PART_NO				0x9A87
++
++#define DSA_8021Q_DIR_RX		BIT(10)
++#define DSA_8021Q_PORT_SHIFT		0
++#define DSA_8021Q_PORT_MASK		GENMASK(3, 0)
++#define DSA_8021Q_PORT(x)		(((x) << DSA_8021Q_PORT_SHIFT) & \
++						 DSA_8021Q_PORT_MASK)
++
++#define SJA1105_RATE_MBPS(speed) (((speed) * 64000) / 1000)
++
++/* UM10944.pdf Page 11, Table 2. Configuration Blocks */
++enum {
++	BLKID_L2_POLICING				= 0x06,
++	BLKID_VLAN_LOOKUP				= 0x07,
++	BLKID_L2_FORWARDING				= 0x08,
++	BLKID_MAC_CONFIG				= 0x09,
++	BLKID_L2_LOOKUP_PARAMS				= 0x0D,
++	BLKID_L2_FORWARDING_PARAMS			= 0x0E,
++	BLKID_GENERAL_PARAMS				= 0x11,
++	BLKID_XMII_PARAMS				= 0x4E,
++};
++
++enum sja1105_blk_idx {
++	BLK_IDX_L2_POLICING = 0,
++	BLK_IDX_VLAN_LOOKUP,
++	BLK_IDX_L2_FORWARDING,
++	BLK_IDX_MAC_CONFIG,
++	BLK_IDX_L2_LOOKUP_PARAMS,
++	BLK_IDX_L2_FORWARDING_PARAMS,
++	BLK_IDX_GENERAL_PARAMS,
++	BLK_IDX_XMII_PARAMS,
++	BLK_IDX_MAX,
++};
++
++struct sja1105_general_params_entry {
++	u64 mac_fltres1;
++	u64 mac_fltres0;
++	u64 mac_flt1;
++	u64 mac_flt0;
++	u64 casc_port;
++	u64 host_port;
++	u64 mirr_port;
++	u64 tpid;
++	u64 tpid2;
++};
++
++struct sja1105_vlan_lookup_entry {
++	u64 vmemb_port;
++	u64 vlan_bc;
++	u64 tag_port;
++	u64 vlanid;
++};
++
++struct sja1105_l2_lookup_params_entry {
++	u64 maxaddrp[5];
++	u64 start_dynspc;
++	u64 use_static;
++	u64 owr_dyn;
++	u64 dyn_tbsz;
++	u64 poly;
++};
++
++struct sja1105_l2_forwarding_entry {
++	u64 bc_domain;
++	u64 reach_port;
++	u64 fl_domain;
++};
++
++struct sja1105_l2_forwarding_params_entry {
++	u64 part_spc[8];
++};
++
++struct sja1105_l2_policing_entry {
++	u64 sharindx;
++	u64 smax;
++	u64 rate;
++	u64 maxlen;
++	u64 partition;
++};
++
++struct sja1105_mac_config_entry {
++	u64 top[8];
++	u64 base[8];
++	u64 enabled[8];
++	u64 speed;
++	u64 maxage;
++	u64 vlanprio;
++	u64 vlanid;
++	u64 dyn_learn;
++	u64 egress;
++	u64 ingress;
++};
++
++struct sja1105_xmii_params_entry {
++	u64 phy_mac[5];
++	u64 xmii_mode[5];
++};
++
++struct sja1105_table_header {
++	u64 block_id;
++	u64 len;
++	u64 crc;
++};
++
++struct sja1105_table_ops {
++	size_t (*packing)(void *buf, void *entry_ptr, enum packing_op op);
++	size_t unpacked_entry_size;
++	size_t packed_entry_size;
++	size_t max_entry_count;
++};
++
++struct sja1105_table {
++	const struct sja1105_table_ops *ops;
++	size_t entry_count;
++	void *entries;
++};
++
++struct sja1105_static_config {
++	u64 device_id;
++	struct sja1105_table tables[BLK_IDX_MAX];
++};
++
++struct sja1105_sgmii_cfg {
++	bool inband_an;
++	u16 pcs_speed;
++};
++
++struct sja1105_private {
++	struct sja1105_static_config static_config;
++	bool rgmii_rx_delay[SJA1105_NUM_PORTS];
++	bool rgmii_tx_delay[SJA1105_NUM_PORTS];
++	int phy_modes[SJA1105_NUM_PORTS];
++	u16 pvid[SJA1105_NUM_PORTS];
++	struct sja1105_sgmii_cfg sgmii_cfg;
++	const struct sja1105_info *info;
++	struct udevice *dev;
++};
++
++typedef enum {
++	SPI_READ = 0,
++	SPI_WRITE = 1,
++} sja1105_spi_rw_mode_t;
++
++typedef enum {
++	XMII_MAC = 0,
++	XMII_PHY = 1,
++} sja1105_mii_role_t;
++
++typedef enum {
++	XMII_MODE_MII		= 0,
++	XMII_MODE_RMII		= 1,
++	XMII_MODE_RGMII		= 2,
++	XMII_MODE_SGMII		= 3,
++} sja1105_phy_interface_t;
++
++typedef enum {
++	SJA1105_SPEED_10MBPS	= 3,
++	SJA1105_SPEED_100MBPS	= 2,
++	SJA1105_SPEED_1000MBPS	= 1,
++} sja1105_speed_t;
++
++/* Keeps the different addresses between E/T and P/Q/R/S */
++struct sja1105_regs {
++	u64 device_id;
++	u64 prod_id;
++	u64 status;
++	u64 port_control;
++	u64 rgu;
++	u64 config;
++	u64 sgmii;
++	u64 rmii_pll1;
++	u64 pad_mii_tx[SJA1105_NUM_PORTS];
++	u64 pad_mii_id[SJA1105_NUM_PORTS];
++	u64 cgu_idiv[SJA1105_NUM_PORTS];
++	u64 mii_tx_clk[SJA1105_NUM_PORTS];
++	u64 mii_rx_clk[SJA1105_NUM_PORTS];
++	u64 mii_ext_tx_clk[SJA1105_NUM_PORTS];
++	u64 mii_ext_rx_clk[SJA1105_NUM_PORTS];
++	u64 rgmii_tx_clk[SJA1105_NUM_PORTS];
++	u64 rmii_ref_clk[SJA1105_NUM_PORTS];
++	u64 rmii_ext_tx_clk[SJA1105_NUM_PORTS];
++};
++
++struct sja1105_info {
++	u64 device_id;
++	u64 part_no;
++	const struct sja1105_table_ops *static_ops;
++	const struct sja1105_regs *regs;
++	int (*reset_cmd)(struct sja1105_private *priv);
++	int (*setup_rgmii_delay)(struct sja1105_private *priv, int port);
++	const char *name;
++};
++
++struct sja1105_chunk {
++	u8	*buf;
++	size_t	len;
++	u64	reg_addr;
++};
++
++struct sja1105_spi_message {
++	u64 access;
++	u64 read_count;
++	u64 address;
++};
++
++struct sja1105_cfg_pad_mii_tx {
++	u64 d32_os;
++	u64 d32_ipud;
++	u64 d10_os;
++	u64 d10_ipud;
++	u64 ctrl_os;
++	u64 ctrl_ipud;
++	u64 clk_os;
++	u64 clk_ih;
++	u64 clk_ipud;
++};
++
++struct sja1105_cfg_pad_mii_id {
++	u64 rxc_stable_ovr;
++	u64 rxc_delay;
++	u64 rxc_bypass;
++	u64 rxc_pd;
++	u64 txc_stable_ovr;
++	u64 txc_delay;
++	u64 txc_bypass;
++	u64 txc_pd;
++};
++
++struct sja1105_cgu_idiv {
++	u64 clksrc;
++	u64 autoblock;
++	u64 idiv;
++	u64 pd;
++};
++
++struct sja1105_cgu_pll_ctrl {
++	u64 pllclksrc;
++	u64 msel;
++	u64 autoblock;
++	u64 psel;
++	u64 direct;
++	u64 fbsel;
++	u64 bypass;
++	u64 pd;
++};
++
++enum {
++	CLKSRC_MII0_TX_CLK	= 0x00,
++	CLKSRC_MII0_RX_CLK	= 0x01,
++	CLKSRC_MII1_TX_CLK	= 0x02,
++	CLKSRC_MII1_RX_CLK	= 0x03,
++	CLKSRC_MII2_TX_CLK	= 0x04,
++	CLKSRC_MII2_RX_CLK	= 0x05,
++	CLKSRC_MII3_TX_CLK	= 0x06,
++	CLKSRC_MII3_RX_CLK	= 0x07,
++	CLKSRC_MII4_TX_CLK	= 0x08,
++	CLKSRC_MII4_RX_CLK	= 0x09,
++	CLKSRC_PLL0		= 0x0B,
++	CLKSRC_PLL1		= 0x0E,
++	CLKSRC_IDIV0		= 0x11,
++	CLKSRC_IDIV1		= 0x12,
++	CLKSRC_IDIV2		= 0x13,
++	CLKSRC_IDIV3		= 0x14,
++	CLKSRC_IDIV4		= 0x15,
++};
++
++struct sja1105_cgu_mii_ctrl {
++	u64 clksrc;
++	u64 autoblock;
++	u64 pd;
++};
++
++static void sja1105_packing(void *buf, u64 *val, int start, int end,
++			    size_t len, enum packing_op op)
++{
++	int rc;
++
++	rc = packing(buf, val, start, end, len, op, QUIRK_LSW32_IS_FIRST);
++	if (likely(!rc))
++		return;
++
++	printf("Invalid use of packing API: start %d end %d returned %d\n",
++	       start, end, rc);
++}
++
++static u32 crc32_add(u32 crc, u8 byte)
++{
++	u32 byte32 = bitrev32(byte);
++	int i;
++
++	for (i = 0; i < 8; i++) {
++		if ((crc ^ byte32) & BIT(31)) {
++			crc <<= 1;
++			crc ^= ETHER_CRC32_POLY;
++		} else {
++			crc <<= 1;
++		}
++		byte32 <<= 1;
++	}
++	return crc;
++}
++
++/* Little-endian Ethernet CRC32 of data packed as big-endian u32 words */
++static uint32_t sja1105_crc32(void *buf, size_t len)
++{
++	unsigned int i;
++	u64 chunk;
++	u32 crc;
++
++	/* seed */
++	crc = 0xFFFFFFFF;
++	for (i = 0; i < len; i += 4) {
++		sja1105_packing(buf + i, &chunk, 31, 0, 4, UNPACK);
++		crc = crc32_add(crc, chunk & 0xFF);
++		crc = crc32_add(crc, (chunk >> 8) & 0xFF);
++		crc = crc32_add(crc, (chunk >> 16) & 0xFF);
++		crc = crc32_add(crc, (chunk >> 24) & 0xFF);
++	}
++	return bitrev32(~crc);
++}
++
++static void sja1105_spi_message_pack(void *buf, struct sja1105_spi_message *msg)
++{
++	const int size = SJA1105_SIZE_SPI_MSG_HEADER;
++
++	memset(buf, 0, size);
++
++	sja1105_packing(buf, &msg->access,     31, 31, size, PACK);
++	sja1105_packing(buf, &msg->read_count, 30, 25, size, PACK);
++	sja1105_packing(buf, &msg->address,    24,  4, size, PACK);
++}
++
++static int sja1105_xfer_buf(const struct sja1105_private *priv,
++			    sja1105_spi_rw_mode_t rw, u64 reg_addr,
++			    u8 *buf, size_t len)
++{
++	struct udevice *dev = priv->dev;
++	struct sja1105_chunk chunk = {
++		.len = min_t(size_t, len, SJA1105_SIZE_SPI_MSG_MAXLEN),
++		.reg_addr = reg_addr,
++		.buf = buf,
++	};
++	int num_chunks;
++	int rc, i;
++
++	rc = dm_spi_claim_bus(dev);
++	if (rc)
++		return rc;
++
++	num_chunks = DIV_ROUND_UP(len, SJA1105_SIZE_SPI_MSG_MAXLEN);
++
++	for (i = 0; i < num_chunks; i++) {
++		u8 hdr_buf[SJA1105_SIZE_SPI_MSG_HEADER];
++		struct sja1105_spi_message msg;
++		u8 *rx_buf = NULL;
++		u8 *tx_buf = NULL;
++
++		/* Populate the transfer's header buffer */
++		msg.address = chunk.reg_addr;
++		msg.access = rw;
++		if (rw == SPI_READ)
++			msg.read_count = chunk.len / 4;
++		else
++			/* Ignored */
++			msg.read_count = 0;
++		sja1105_spi_message_pack(hdr_buf, &msg);
++		rc = dm_spi_xfer(dev, SJA1105_SIZE_SPI_MSG_HEADER * 8, hdr_buf,
++				 NULL, SPI_XFER_BEGIN);
++		if (rc)
++			goto out;
++
++		/* Populate the transfer's data buffer */
++		if (rw == SPI_READ)
++			rx_buf = chunk.buf;
++		else
++			tx_buf = chunk.buf;
++		rc = dm_spi_xfer(dev, chunk.len * 8, tx_buf, rx_buf,
++				 SPI_XFER_END);
++		if (rc)
++			goto out;
++
++		/* Calculate next chunk */
++		chunk.buf += chunk.len;
++		chunk.reg_addr += chunk.len / 4;
++		chunk.len = min_t(size_t, (ptrdiff_t)(buf + len - chunk.buf),
++				  SJA1105_SIZE_SPI_MSG_MAXLEN);
++	}
++
++out:
++	dm_spi_release_bus(dev);
++
++	return rc;
++}
++
++static int sja1105et_reset_cmd(struct sja1105_private *priv)
++{
++	const struct sja1105_regs *regs = priv->info->regs;
++	u8 packed_buf[SJA1105_SIZE_RESET_CMD] = {0};
++	const int size = SJA1105_SIZE_RESET_CMD;
++	u64 cold_rst = 1;
++
++	sja1105_packing(packed_buf, &cold_rst, 3, 3, size, PACK);
++
++	return sja1105_xfer_buf(priv, SPI_WRITE, regs->rgu, packed_buf,
++				SJA1105_SIZE_RESET_CMD);
++}
++
++static int sja1105pqrs_reset_cmd(struct sja1105_private *priv)
++{
++	const struct sja1105_regs *regs = priv->info->regs;
++	u8 packed_buf[SJA1105_SIZE_RESET_CMD] = {0};
++	const int size = SJA1105_SIZE_RESET_CMD;
++	u64 cold_rst = 1;
++
++	sja1105_packing(packed_buf, &cold_rst, 2, 2, size, PACK);
++
++	return sja1105_xfer_buf(priv, SPI_WRITE, regs->rgu, packed_buf,
++				SJA1105_SIZE_RESET_CMD);
++}
++
++static size_t sja1105et_general_params_entry_packing(void *buf, void *entry_ptr,
++						     enum packing_op op)
++{
++	const size_t size = SJA1105ET_SIZE_GENERAL_PARAMS_ENTRY;
++	struct sja1105_general_params_entry *entry = entry_ptr;
++
++	sja1105_packing(buf, &entry->mac_fltres1, 311, 264, size, op);
++	sja1105_packing(buf, &entry->mac_fltres0, 263, 216, size, op);
++	sja1105_packing(buf, &entry->mac_flt1,    215, 168, size, op);
++	sja1105_packing(buf, &entry->mac_flt0,    167, 120, size, op);
++	sja1105_packing(buf, &entry->casc_port,   115, 113, size, op);
++	sja1105_packing(buf, &entry->host_port,   112, 110, size, op);
++	sja1105_packing(buf, &entry->mirr_port,   109, 107, size, op);
++	sja1105_packing(buf, &entry->tpid,         42,  27, size, op);
++	sja1105_packing(buf, &entry->tpid2,        25,  10, size, op);
++	return size;
++}
++
++static size_t
++sja1105pqrs_general_params_entry_packing(void *buf, void *entry_ptr,
++					 enum packing_op op)
++{
++	const size_t size = SJA1105PQRS_SIZE_GENERAL_PARAMS_ENTRY;
++	struct sja1105_general_params_entry *entry = entry_ptr;
++
++	sja1105_packing(buf, &entry->mac_fltres1, 343, 296, size, op);
++	sja1105_packing(buf, &entry->mac_fltres0, 295, 248, size, op);
++	sja1105_packing(buf, &entry->mac_flt1,    247, 200, size, op);
++	sja1105_packing(buf, &entry->mac_flt0,    199, 152, size, op);
++	sja1105_packing(buf, &entry->casc_port,   147, 145, size, op);
++	sja1105_packing(buf, &entry->host_port,   144, 142, size, op);
++	sja1105_packing(buf, &entry->mirr_port,   141, 139, size, op);
++	sja1105_packing(buf, &entry->tpid,         74,  59, size, op);
++	sja1105_packing(buf, &entry->tpid2,        57,  42, size, op);
++	return size;
++}
++
++static size_t
++sja1105_l2_forwarding_params_entry_packing(void *buf, void *entry_ptr,
++					   enum packing_op op)
++{
++	const size_t size = SJA1105_SIZE_L2_FORWARDING_PARAMS_ENTRY;
++	struct sja1105_l2_forwarding_params_entry *entry = entry_ptr;
++	int offset, i;
++
++	for (i = 0, offset = 13; i < 8; i++, offset += 10)
++		sja1105_packing(buf, &entry->part_spc[i],
++				offset + 9, offset + 0, size, op);
++	return size;
++}
++
++static size_t sja1105_l2_forwarding_entry_packing(void *buf, void *entry_ptr,
++						  enum packing_op op)
++{
++	const size_t size = SJA1105_SIZE_L2_FORWARDING_ENTRY;
++	struct sja1105_l2_forwarding_entry *entry = entry_ptr;
++
++	sja1105_packing(buf, &entry->bc_domain,  63, 59, size, op);
++	sja1105_packing(buf, &entry->reach_port, 58, 54, size, op);
++	sja1105_packing(buf, &entry->fl_domain,  53, 49, size, op);
++	return size;
++}
++
++static size_t
++sja1105et_l2_lookup_params_entry_packing(void *buf, void *entry_ptr,
++					 enum packing_op op)
++{
++	const size_t size = SJA1105ET_SIZE_L2_LOOKUP_PARAMS_ENTRY;
++	struct sja1105_l2_lookup_params_entry *entry = entry_ptr;
++
++	sja1105_packing(buf, &entry->dyn_tbsz,       16, 14, size, op);
++	sja1105_packing(buf, &entry->poly,           13,  6, size, op);
++	return size;
++}
++
++static size_t
++sja1105pqrs_l2_lookup_params_entry_packing(void *buf, void *entry_ptr,
++					   enum packing_op op)
++{
++	const size_t size = SJA1105PQRS_SIZE_L2_LOOKUP_PARAMS_ENTRY;
++	struct sja1105_l2_lookup_params_entry *entry = entry_ptr;
++	int offset, i;
++
++	for (i = 0, offset = 58; i < 5; i++, offset += 11)
++		sja1105_packing(buf, &entry->maxaddrp[i],
++				offset + 10, offset + 0, size, op);
++	sja1105_packing(buf, &entry->start_dynspc,   42,  33, size, op);
++	sja1105_packing(buf, &entry->use_static,     24,  24, size, op);
++	sja1105_packing(buf, &entry->owr_dyn,        23,  23, size, op);
++	return size;
++}
++
++static size_t sja1105_l2_policing_entry_packing(void *buf, void *entry_ptr,
++						enum packing_op op)
++{
++	const size_t size = SJA1105_SIZE_L2_POLICING_ENTRY;
++	struct sja1105_l2_policing_entry *entry = entry_ptr;
++
++	sja1105_packing(buf, &entry->sharindx,  63, 58, size, op);
++	sja1105_packing(buf, &entry->smax,      57, 42, size, op);
++	sja1105_packing(buf, &entry->rate,      41, 26, size, op);
++	sja1105_packing(buf, &entry->maxlen,    25, 15, size, op);
++	sja1105_packing(buf, &entry->partition, 14, 12, size, op);
++	return size;
++}
++
++static size_t sja1105et_mac_config_entry_packing(void *buf, void *entry_ptr,
++						 enum packing_op op)
++{
++	const size_t size = SJA1105ET_SIZE_MAC_CONFIG_ENTRY;
++	struct sja1105_mac_config_entry *entry = entry_ptr;
++	int offset, i;
++
++	for (i = 0, offset = 72; i < 8; i++, offset += 19) {
++		sja1105_packing(buf, &entry->enabled[i],
++				offset +  0, offset +  0, size, op);
++		sja1105_packing(buf, &entry->base[i],
++				offset +  9, offset +  1, size, op);
++		sja1105_packing(buf, &entry->top[i],
++				offset + 18, offset + 10, size, op);
++	}
++	sja1105_packing(buf, &entry->speed,     66, 65, size, op);
++	sja1105_packing(buf, &entry->maxage,    32, 25, size, op);
++	sja1105_packing(buf, &entry->vlanprio,  24, 22, size, op);
++	sja1105_packing(buf, &entry->vlanid,    21, 10, size, op);
++	sja1105_packing(buf, &entry->dyn_learn,  3,  3, size, op);
++	sja1105_packing(buf, &entry->egress,     2,  2, size, op);
++	sja1105_packing(buf, &entry->ingress,    1,  1, size, op);
++	return size;
++}
++
++static size_t sja1105pqrs_mac_config_entry_packing(void *buf, void *entry_ptr,
++						   enum packing_op op)
++{
++	const size_t size = SJA1105PQRS_SIZE_MAC_CONFIG_ENTRY;
++	struct sja1105_mac_config_entry *entry = entry_ptr;
++	int offset, i;
++
++	for (i = 0, offset = 104; i < 8; i++, offset += 19) {
++		sja1105_packing(buf, &entry->enabled[i],
++				offset +  0, offset +  0, size, op);
++		sja1105_packing(buf, &entry->base[i],
++				offset +  9, offset +  1, size, op);
++		sja1105_packing(buf, &entry->top[i],
++				offset + 18, offset + 10, size, op);
++	}
++	sja1105_packing(buf, &entry->speed,      98, 97, size, op);
++	sja1105_packing(buf, &entry->maxage,     64, 57, size, op);
++	sja1105_packing(buf, &entry->vlanprio,   56, 54, size, op);
++	sja1105_packing(buf, &entry->vlanid,     53, 42, size, op);
++	sja1105_packing(buf, &entry->dyn_learn,  33, 33, size, op);
++	sja1105_packing(buf, &entry->egress,     32, 32, size, op);
++	sja1105_packing(buf, &entry->ingress,    31, 31, size, op);
++	return size;
++}
++
++static size_t sja1105_vlan_lookup_entry_packing(void *buf, void *entry_ptr,
++						enum packing_op op)
++{
++	const size_t size = SJA1105_SIZE_VLAN_LOOKUP_ENTRY;
++	struct sja1105_vlan_lookup_entry *entry = entry_ptr;
++
++	sja1105_packing(buf, &entry->vmemb_port, 53, 49, size, op);
++	sja1105_packing(buf, &entry->vlan_bc,    48, 44, size, op);
++	sja1105_packing(buf, &entry->tag_port,   43, 39, size, op);
++	sja1105_packing(buf, &entry->vlanid,     38, 27, size, op);
++	return size;
++}
++
++static size_t sja1105_xmii_params_entry_packing(void *buf, void *entry_ptr,
++						enum packing_op op)
++{
++	const size_t size = SJA1105_SIZE_XMII_PARAMS_ENTRY;
++	struct sja1105_xmii_params_entry *entry = entry_ptr;
++	int offset, i;
++
++	for (i = 0, offset = 17; i < 5; i++, offset += 3) {
++		sja1105_packing(buf, &entry->xmii_mode[i],
++				offset + 1, offset + 0, size, op);
++		sja1105_packing(buf, &entry->phy_mac[i],
++				offset + 2, offset + 2, size, op);
++	}
++	return size;
++}
++
++static size_t sja1105_table_header_packing(void *buf, void *entry_ptr,
++					   enum packing_op op)
++{
++	const size_t size = SJA1105_SIZE_TABLE_HEADER;
++	struct sja1105_table_header *entry = entry_ptr;
++
++	sja1105_packing(buf, &entry->block_id, 31, 24, size, op);
++	sja1105_packing(buf, &entry->len,      55, 32, size, op);
++	sja1105_packing(buf, &entry->crc,      95, 64, size, op);
++	return size;
++}
++
++static void
++sja1105_table_header_pack_with_crc(void *buf, struct sja1105_table_header *hdr)
++{
++	/* First pack the table as-is, then calculate the CRC, and
++	 * finally put the proper CRC into the packed buffer
++	 */
++	memset(buf, 0, SJA1105_SIZE_TABLE_HEADER);
++	sja1105_table_header_packing(buf, hdr, PACK);
++	hdr->crc = sja1105_crc32(buf, SJA1105_SIZE_TABLE_HEADER - 4);
++	sja1105_packing(buf + SJA1105_SIZE_TABLE_HEADER - 4, &hdr->crc,
++			31, 0, 4, PACK);
++}
++
++static void sja1105_table_write_crc(u8 *table_start, u8 *crc_ptr)
++{
++	u64 computed_crc;
++	int len_bytes;
++
++	len_bytes = (uintptr_t)(crc_ptr - table_start);
++	computed_crc = sja1105_crc32(table_start, len_bytes);
++	sja1105_packing(crc_ptr, &computed_crc, 31, 0, 4, PACK);
++}
++
++/* The block IDs that the switches support are unfortunately sparse, so keep a
++ * mapping table to "block indices" and translate back and forth.
++ */
++static u64 blk_id_map[BLK_IDX_MAX] = {
++	[BLK_IDX_L2_POLICING] = BLKID_L2_POLICING,
++	[BLK_IDX_VLAN_LOOKUP] = BLKID_VLAN_LOOKUP,
++	[BLK_IDX_L2_FORWARDING] = BLKID_L2_FORWARDING,
++	[BLK_IDX_MAC_CONFIG] = BLKID_MAC_CONFIG,
++	[BLK_IDX_L2_LOOKUP_PARAMS] = BLKID_L2_LOOKUP_PARAMS,
++	[BLK_IDX_L2_FORWARDING_PARAMS] = BLKID_L2_FORWARDING_PARAMS,
++	[BLK_IDX_GENERAL_PARAMS] = BLKID_GENERAL_PARAMS,
++	[BLK_IDX_XMII_PARAMS] = BLKID_XMII_PARAMS,
++};
++
++static void
++sja1105_static_config_pack(void *buf, struct sja1105_static_config *config)
++{
++	struct sja1105_table_header header = {0};
++	enum sja1105_blk_idx i;
++	u8 *p = buf;
++	int j;
++
++	sja1105_packing(p, &config->device_id, 31, 0, 4, PACK);
++	p += SJA1105_SIZE_DEVICE_ID;
++
++	for (i = 0; i < BLK_IDX_MAX; i++) {
++		const struct sja1105_table *table;
++		u8 *table_start;
++
++		table = &config->tables[i];
++		if (!table->entry_count)
++			continue;
++
++		header.block_id = blk_id_map[i];
++		header.len = table->entry_count *
++			     table->ops->packed_entry_size / 4;
++		sja1105_table_header_pack_with_crc(p, &header);
++		p += SJA1105_SIZE_TABLE_HEADER;
++		table_start = p;
++		for (j = 0; j < table->entry_count; j++) {
++			u8 *entry_ptr = table->entries;
++
++			entry_ptr += j * table->ops->unpacked_entry_size;
++			memset(p, 0, table->ops->packed_entry_size);
++			table->ops->packing(p, entry_ptr, PACK);
++			p += table->ops->packed_entry_size;
++		}
++		sja1105_table_write_crc(table_start, p);
++		p += 4;
++	}
++	/* Final header:
++	 * Block ID does not matter
++	 * Length of 0 marks that header is final
++	 * CRC will be replaced on-the-fly
++	 */
++	header.block_id = 0;
++	header.len = 0;
++	header.crc = 0xDEADBEEF;
++	memset(p, 0, SJA1105_SIZE_TABLE_HEADER);
++	sja1105_table_header_packing(p, &header, PACK);
++}
++
++static size_t
++sja1105_static_config_get_length(const struct sja1105_static_config *config)
++{
++	unsigned int header_count;
++	enum sja1105_blk_idx i;
++	unsigned int sum;
++
++	/* Ending header */
++	header_count = 1;
++	sum = SJA1105_SIZE_DEVICE_ID;
++
++	/* Tables (headers and entries) */
++	for (i = 0; i < BLK_IDX_MAX; i++) {
++		const struct sja1105_table *table;
++
++		table = &config->tables[i];
++		if (table->entry_count)
++			header_count++;
++
++		sum += table->ops->packed_entry_size * table->entry_count;
++	}
++	/* Headers have an additional CRC at the end */
++	sum += header_count * (SJA1105_SIZE_TABLE_HEADER + 4);
++	/* Last header does not have an extra CRC because there is no data */
++	sum -= 4;
++
++	return sum;
++}
++
++/* Compatibility matrices */
++static struct sja1105_table_ops sja1105et_table_ops[BLK_IDX_MAX] = {
++	[BLK_IDX_L2_POLICING] = {
++		.packing = sja1105_l2_policing_entry_packing,
++		.unpacked_entry_size = sizeof(struct sja1105_l2_policing_entry),
++		.packed_entry_size = SJA1105_SIZE_L2_POLICING_ENTRY,
++		.max_entry_count = SJA1105_MAX_L2_POLICING_COUNT,
++	},
++	[BLK_IDX_VLAN_LOOKUP] = {
++		.packing = sja1105_vlan_lookup_entry_packing,
++		.unpacked_entry_size = sizeof(struct sja1105_vlan_lookup_entry),
++		.packed_entry_size = SJA1105_SIZE_VLAN_LOOKUP_ENTRY,
++		.max_entry_count = SJA1105_MAX_VLAN_LOOKUP_COUNT,
++	},
++	[BLK_IDX_L2_FORWARDING] = {
++		.packing = sja1105_l2_forwarding_entry_packing,
++		.unpacked_entry_size = sizeof(struct sja1105_l2_forwarding_entry),
++		.packed_entry_size = SJA1105_SIZE_L2_FORWARDING_ENTRY,
++		.max_entry_count = SJA1105_MAX_L2_FORWARDING_COUNT,
++	},
++	[BLK_IDX_MAC_CONFIG] = {
++		.packing = sja1105et_mac_config_entry_packing,
++		.unpacked_entry_size = sizeof(struct sja1105_mac_config_entry),
++		.packed_entry_size = SJA1105ET_SIZE_MAC_CONFIG_ENTRY,
++		.max_entry_count = SJA1105_MAX_MAC_CONFIG_COUNT,
++	},
++	[BLK_IDX_L2_LOOKUP_PARAMS] = {
++		.packing = sja1105et_l2_lookup_params_entry_packing,
++		.unpacked_entry_size = sizeof(struct sja1105_l2_lookup_params_entry),
++		.packed_entry_size = SJA1105ET_SIZE_L2_LOOKUP_PARAMS_ENTRY,
++		.max_entry_count = SJA1105_MAX_L2_LOOKUP_PARAMS_COUNT,
++	},
++	[BLK_IDX_L2_FORWARDING_PARAMS] = {
++		.packing = sja1105_l2_forwarding_params_entry_packing,
++		.unpacked_entry_size = sizeof(struct sja1105_l2_forwarding_params_entry),
++		.packed_entry_size = SJA1105_SIZE_L2_FORWARDING_PARAMS_ENTRY,
++		.max_entry_count = SJA1105_MAX_L2_FORWARDING_PARAMS_COUNT,
++	},
++	[BLK_IDX_GENERAL_PARAMS] = {
++		.packing = sja1105et_general_params_entry_packing,
++		.unpacked_entry_size = sizeof(struct sja1105_general_params_entry),
++		.packed_entry_size = SJA1105ET_SIZE_GENERAL_PARAMS_ENTRY,
++		.max_entry_count = SJA1105_MAX_GENERAL_PARAMS_COUNT,
++	},
++	[BLK_IDX_XMII_PARAMS] = {
++		.packing = sja1105_xmii_params_entry_packing,
++		.unpacked_entry_size = sizeof(struct sja1105_xmii_params_entry),
++		.packed_entry_size = SJA1105_SIZE_XMII_PARAMS_ENTRY,
++		.max_entry_count = SJA1105_MAX_XMII_PARAMS_COUNT,
++	},
++};
++
++static struct sja1105_table_ops sja1105pqrs_table_ops[BLK_IDX_MAX] = {
++	[BLK_IDX_L2_POLICING] = {
++		.packing = sja1105_l2_policing_entry_packing,
++		.unpacked_entry_size = sizeof(struct sja1105_l2_policing_entry),
++		.packed_entry_size = SJA1105_SIZE_L2_POLICING_ENTRY,
++		.max_entry_count = SJA1105_MAX_L2_POLICING_COUNT,
++	},
++	[BLK_IDX_VLAN_LOOKUP] = {
++		.packing = sja1105_vlan_lookup_entry_packing,
++		.unpacked_entry_size = sizeof(struct sja1105_vlan_lookup_entry),
++		.packed_entry_size = SJA1105_SIZE_VLAN_LOOKUP_ENTRY,
++		.max_entry_count = SJA1105_MAX_VLAN_LOOKUP_COUNT,
++	},
++	[BLK_IDX_L2_FORWARDING] = {
++		.packing = sja1105_l2_forwarding_entry_packing,
++		.unpacked_entry_size = sizeof(struct sja1105_l2_forwarding_entry),
++		.packed_entry_size = SJA1105_SIZE_L2_FORWARDING_ENTRY,
++		.max_entry_count = SJA1105_MAX_L2_FORWARDING_COUNT,
++	},
++	[BLK_IDX_MAC_CONFIG] = {
++		.packing = sja1105pqrs_mac_config_entry_packing,
++		.unpacked_entry_size = sizeof(struct sja1105_mac_config_entry),
++		.packed_entry_size = SJA1105PQRS_SIZE_MAC_CONFIG_ENTRY,
++		.max_entry_count = SJA1105_MAX_MAC_CONFIG_COUNT,
++	},
++	[BLK_IDX_L2_LOOKUP_PARAMS] = {
++		.packing = sja1105pqrs_l2_lookup_params_entry_packing,
++		.unpacked_entry_size = sizeof(struct sja1105_l2_lookup_params_entry),
++		.packed_entry_size = SJA1105PQRS_SIZE_L2_LOOKUP_PARAMS_ENTRY,
++		.max_entry_count = SJA1105_MAX_L2_LOOKUP_PARAMS_COUNT,
++	},
++	[BLK_IDX_L2_FORWARDING_PARAMS] = {
++		.packing = sja1105_l2_forwarding_params_entry_packing,
++		.unpacked_entry_size = sizeof(struct sja1105_l2_forwarding_params_entry),
++		.packed_entry_size = SJA1105_SIZE_L2_FORWARDING_PARAMS_ENTRY,
++		.max_entry_count = SJA1105_MAX_L2_FORWARDING_PARAMS_COUNT,
++	},
++	[BLK_IDX_GENERAL_PARAMS] = {
++		.packing = sja1105pqrs_general_params_entry_packing,
++		.unpacked_entry_size = sizeof(struct sja1105_general_params_entry),
++		.packed_entry_size = SJA1105PQRS_SIZE_GENERAL_PARAMS_ENTRY,
++		.max_entry_count = SJA1105_MAX_GENERAL_PARAMS_COUNT,
++	},
++	[BLK_IDX_XMII_PARAMS] = {
++		.packing = sja1105_xmii_params_entry_packing,
++		.unpacked_entry_size = sizeof(struct sja1105_xmii_params_entry),
++		.packed_entry_size = SJA1105_SIZE_XMII_PARAMS_ENTRY,
++		.max_entry_count = SJA1105_MAX_XMII_PARAMS_COUNT,
++	},
++};
++
++static int
++sja1105_static_config_init(struct sja1105_static_config *config,
++			   const struct sja1105_table_ops *static_ops,
++			   u64 device_id)
++{
++	enum sja1105_blk_idx i;
++
++	*config = (struct sja1105_static_config) {0};
++
++	/* Transfer static_ops array from priv into per-table ops
++	 * for handier access
++	 */
++	for (i = 0; i < BLK_IDX_MAX; i++)
++		config->tables[i].ops = &static_ops[i];
++
++	config->device_id = device_id;
++	return 0;
++}
++
++static void sja1105_static_config_free(struct sja1105_static_config *config)
++{
++	enum sja1105_blk_idx i;
++
++	for (i = 0; i < BLK_IDX_MAX; i++) {
++		if (config->tables[i].entry_count) {
++			free(config->tables[i].entries);
++			config->tables[i].entry_count = 0;
++		}
++	}
++}
++
++static void sja1105_cgu_idiv_packing(void *buf, struct sja1105_cgu_idiv *idiv,
++				     enum packing_op op)
++{
++	const int size = 4;
++
++	sja1105_packing(buf, &idiv->clksrc,    28, 24, size, op);
++	sja1105_packing(buf, &idiv->autoblock, 11, 11, size, op);
++	sja1105_packing(buf, &idiv->idiv,       5,  2, size, op);
++	sja1105_packing(buf, &idiv->pd,         0,  0, size, op);
++}
++
++static int sja1105_cgu_idiv_config(struct sja1105_private *priv, int port,
++				   bool enabled, int factor)
++{
++	const struct sja1105_regs *regs = priv->info->regs;
++	struct sja1105_cgu_idiv idiv;
++	u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0};
++
++	if (enabled && factor != 1 && factor != 10)
++		return -ERANGE;
++
++	/* Payload for packed_buf */
++	idiv.clksrc    = 0x0A;            /* 25MHz */
++	idiv.autoblock = 1;               /* Block clk automatically */
++	idiv.idiv      = factor - 1;      /* Divide by 1 or 10 */
++	idiv.pd        = enabled ? 0 : 1; /* Power down? */
++	sja1105_cgu_idiv_packing(packed_buf, &idiv, PACK);
++
++	return sja1105_xfer_buf(priv, SPI_WRITE, regs->cgu_idiv[port],
++				packed_buf, SJA1105_SIZE_CGU_CMD);
++}
++
++static void
++sja1105_cgu_mii_control_packing(void *buf, struct sja1105_cgu_mii_ctrl *cmd,
++				enum packing_op op)
++{
++	const int size = 4;
++
++	sja1105_packing(buf, &cmd->clksrc,    28, 24, size, op);
++	sja1105_packing(buf, &cmd->autoblock, 11, 11, size, op);
++	sja1105_packing(buf, &cmd->pd,         0,  0, size, op);
++}
++
++static int sja1105_cgu_mii_tx_clk_config(struct sja1105_private *priv,
++					 int port, sja1105_mii_role_t role)
++{
++	const struct sja1105_regs *regs = priv->info->regs;
++	struct sja1105_cgu_mii_ctrl mii_tx_clk;
++	const int mac_clk_sources[] = {
++		CLKSRC_MII0_TX_CLK,
++		CLKSRC_MII1_TX_CLK,
++		CLKSRC_MII2_TX_CLK,
++		CLKSRC_MII3_TX_CLK,
++		CLKSRC_MII4_TX_CLK,
++	};
++	const int phy_clk_sources[] = {
++		CLKSRC_IDIV0,
++		CLKSRC_IDIV1,
++		CLKSRC_IDIV2,
++		CLKSRC_IDIV3,
++		CLKSRC_IDIV4,
++	};
++	u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0};
++	int clksrc;
++
++	if (role == XMII_MAC)
++		clksrc = mac_clk_sources[port];
++	else
++		clksrc = phy_clk_sources[port];
++
++	/* Payload for packed_buf */
++	mii_tx_clk.clksrc    = clksrc;
++	mii_tx_clk.autoblock = 1;  /* Autoblock clk while changing clksrc */
++	mii_tx_clk.pd        = 0;  /* Power Down off => enabled */
++	sja1105_cgu_mii_control_packing(packed_buf, &mii_tx_clk, PACK);
++
++	return sja1105_xfer_buf(priv, SPI_WRITE, regs->mii_tx_clk[port],
++				packed_buf, SJA1105_SIZE_CGU_CMD);
++}
++
++static int
++sja1105_cgu_mii_rx_clk_config(struct sja1105_private *priv, int port)
++{
++	const struct sja1105_regs *regs = priv->info->regs;
++	struct sja1105_cgu_mii_ctrl mii_rx_clk;
++	u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0};
++	const int clk_sources[] = {
++		CLKSRC_MII0_RX_CLK,
++		CLKSRC_MII1_RX_CLK,
++		CLKSRC_MII2_RX_CLK,
++		CLKSRC_MII3_RX_CLK,
++		CLKSRC_MII4_RX_CLK,
++	};
++
++	/* Payload for packed_buf */
++	mii_rx_clk.clksrc    = clk_sources[port];
++	mii_rx_clk.autoblock = 1;  /* Autoblock clk while changing clksrc */
++	mii_rx_clk.pd        = 0;  /* Power Down off => enabled */
++	sja1105_cgu_mii_control_packing(packed_buf, &mii_rx_clk, PACK);
++
++	return sja1105_xfer_buf(priv, SPI_WRITE, regs->mii_rx_clk[port],
++				packed_buf, SJA1105_SIZE_CGU_CMD);
++}
++
++static int
++sja1105_cgu_mii_ext_tx_clk_config(struct sja1105_private *priv, int port)
++{
++	const struct sja1105_regs *regs = priv->info->regs;
++	struct sja1105_cgu_mii_ctrl mii_ext_tx_clk;
++	u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0};
++	const int clk_sources[] = {
++		CLKSRC_IDIV0,
++		CLKSRC_IDIV1,
++		CLKSRC_IDIV2,
++		CLKSRC_IDIV3,
++		CLKSRC_IDIV4,
++	};
++
++	/* Payload for packed_buf */
++	mii_ext_tx_clk.clksrc    = clk_sources[port];
++	mii_ext_tx_clk.autoblock = 1; /* Autoblock clk while changing clksrc */
++	mii_ext_tx_clk.pd        = 0; /* Power Down off => enabled */
++	sja1105_cgu_mii_control_packing(packed_buf, &mii_ext_tx_clk, PACK);
++
++	return sja1105_xfer_buf(priv, SPI_WRITE, regs->mii_ext_tx_clk[port],
++				packed_buf, SJA1105_SIZE_CGU_CMD);
++}
++
++static int
++sja1105_cgu_mii_ext_rx_clk_config(struct sja1105_private *priv, int port)
++{
++	const struct sja1105_regs *regs = priv->info->regs;
++	struct sja1105_cgu_mii_ctrl mii_ext_rx_clk;
++	u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0};
++	const int clk_sources[] = {
++		CLKSRC_IDIV0,
++		CLKSRC_IDIV1,
++		CLKSRC_IDIV2,
++		CLKSRC_IDIV3,
++		CLKSRC_IDIV4,
++	};
++
++	/* Payload for packed_buf */
++	mii_ext_rx_clk.clksrc    = clk_sources[port];
++	mii_ext_rx_clk.autoblock = 1; /* Autoblock clk while changing clksrc */
++	mii_ext_rx_clk.pd        = 0; /* Power Down off => enabled */
++	sja1105_cgu_mii_control_packing(packed_buf, &mii_ext_rx_clk, PACK);
++
++	return sja1105_xfer_buf(priv, SPI_WRITE, regs->mii_ext_rx_clk[port],
++				packed_buf, SJA1105_SIZE_CGU_CMD);
++}
++
++static int sja1105_mii_clocking_setup(struct sja1105_private *priv, int port,
++				      sja1105_mii_role_t role)
++{
++	int rc;
++
++	rc = sja1105_cgu_idiv_config(priv, port, (role == XMII_PHY), 1);
++	if (rc < 0)
++		return rc;
++
++	rc = sja1105_cgu_mii_tx_clk_config(priv, port, role);
++	if (rc < 0)
++		return rc;
++
++	rc = sja1105_cgu_mii_rx_clk_config(priv, port);
++	if (rc < 0)
++		return rc;
++
++	if (role == XMII_PHY) {
++		rc = sja1105_cgu_mii_ext_tx_clk_config(priv, port);
++		if (rc < 0)
++			return rc;
++
++		rc = sja1105_cgu_mii_ext_rx_clk_config(priv, port);
++		if (rc < 0)
++			return rc;
++	}
++	return 0;
++}
++
++static void
++sja1105_cgu_pll_control_packing(void *buf, struct sja1105_cgu_pll_ctrl *cmd,
++				enum packing_op op)
++{
++	const int size = 4;
++
++	sja1105_packing(buf, &cmd->pllclksrc, 28, 24, size, op);
++	sja1105_packing(buf, &cmd->msel,      23, 16, size, op);
++	sja1105_packing(buf, &cmd->autoblock, 11, 11, size, op);
++	sja1105_packing(buf, &cmd->psel,       9,  8, size, op);
++	sja1105_packing(buf, &cmd->direct,     7,  7, size, op);
++	sja1105_packing(buf, &cmd->fbsel,      6,  6, size, op);
++	sja1105_packing(buf, &cmd->bypass,     1,  1, size, op);
++	sja1105_packing(buf, &cmd->pd,         0,  0, size, op);
++}
++
++static int sja1105_cgu_rgmii_tx_clk_config(struct sja1105_private *priv,
++					   int port, sja1105_speed_t speed)
++{
++	const struct sja1105_regs *regs = priv->info->regs;
++	struct sja1105_cgu_mii_ctrl txc;
++	u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0};
++	int clksrc;
++
++	if (speed == SJA1105_SPEED_1000MBPS) {
++		clksrc = CLKSRC_PLL0;
++	} else {
++		int clk_sources[] = {CLKSRC_IDIV0, CLKSRC_IDIV1, CLKSRC_IDIV2,
++				     CLKSRC_IDIV3, CLKSRC_IDIV4};
++		clksrc = clk_sources[port];
++	}
++
++	/* RGMII: 125MHz for 1000, 25MHz for 100, 2.5MHz for 10 */
++	txc.clksrc = clksrc;
++	/* Autoblock clk while changing clksrc */
++	txc.autoblock = 1;
++	/* Power Down off => enabled */
++	txc.pd = 0;
++	sja1105_cgu_mii_control_packing(packed_buf, &txc, PACK);
++
++	return sja1105_xfer_buf(priv, SPI_WRITE, regs->rgmii_tx_clk[port],
++				packed_buf, SJA1105_SIZE_CGU_CMD);
++}
++
++/* AGU */
++static void
++sja1105_cfg_pad_mii_tx_packing(void *buf, struct sja1105_cfg_pad_mii_tx *cmd,
++			       enum packing_op op)
++{
++	const int size = 4;
++
++	sja1105_packing(buf, &cmd->d32_os,   28, 27, size, op);
++	sja1105_packing(buf, &cmd->d32_ipud, 25, 24, size, op);
++	sja1105_packing(buf, &cmd->d10_os,   20, 19, size, op);
++	sja1105_packing(buf, &cmd->d10_ipud, 17, 16, size, op);
++	sja1105_packing(buf, &cmd->ctrl_os,  12, 11, size, op);
++	sja1105_packing(buf, &cmd->ctrl_ipud, 9,  8, size, op);
++	sja1105_packing(buf, &cmd->clk_os,    4,  3, size, op);
++	sja1105_packing(buf, &cmd->clk_ih,    2,  2, size, op);
++	sja1105_packing(buf, &cmd->clk_ipud,  1,  0, size, op);
++}
++
++static int sja1105_rgmii_cfg_pad_tx_config(struct sja1105_private *priv,
++					   int port)
++{
++	const struct sja1105_regs *regs = priv->info->regs;
++	struct sja1105_cfg_pad_mii_tx pad_mii_tx;
++	u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0};
++
++	/* Payload */
++	pad_mii_tx.d32_os    = 3; /* TXD[3:2] output stage: */
++				  /*          high noise/high speed */
++	pad_mii_tx.d10_os    = 3; /* TXD[1:0] output stage: */
++				  /*          high noise/high speed */
++	pad_mii_tx.d32_ipud  = 2; /* TXD[3:2] input stage: */
++				  /*          plain input (default) */
++	pad_mii_tx.d10_ipud  = 2; /* TXD[1:0] input stage: */
++				  /*          plain input (default) */
++	pad_mii_tx.ctrl_os   = 3; /* TX_CTL / TX_ER output stage */
++	pad_mii_tx.ctrl_ipud = 2; /* TX_CTL / TX_ER input stage (default) */
++	pad_mii_tx.clk_os    = 3; /* TX_CLK output stage */
++	pad_mii_tx.clk_ih    = 0; /* TX_CLK input hysteresis (default) */
++	pad_mii_tx.clk_ipud  = 2; /* TX_CLK input stage (default) */
++	sja1105_cfg_pad_mii_tx_packing(packed_buf, &pad_mii_tx, PACK);
++
++	return sja1105_xfer_buf(priv, SPI_WRITE, regs->pad_mii_tx[port],
++				packed_buf, SJA1105_SIZE_CGU_CMD);
++}
++
++static void
++sja1105_cfg_pad_mii_id_packing(void *buf, struct sja1105_cfg_pad_mii_id *cmd,
++			       enum packing_op op)
++{
++	const int size = SJA1105_SIZE_CGU_CMD;
++
++	sja1105_packing(buf, &cmd->rxc_stable_ovr, 15, 15, size, op);
++	sja1105_packing(buf, &cmd->rxc_delay,      14, 10, size, op);
++	sja1105_packing(buf, &cmd->rxc_bypass,      9,  9, size, op);
++	sja1105_packing(buf, &cmd->rxc_pd,          8,  8, size, op);
++	sja1105_packing(buf, &cmd->txc_stable_ovr,  7,  7, size, op);
++	sja1105_packing(buf, &cmd->txc_delay,       6,  2, size, op);
++	sja1105_packing(buf, &cmd->txc_bypass,      1,  1, size, op);
++	sja1105_packing(buf, &cmd->txc_pd,          0,  0, size, op);
++}
++
++/* Valid range in degrees is an integer between 73.8 and 101.7 */
++static u64 sja1105_rgmii_delay(u64 phase)
++{
++	/* UM11040.pdf: The delay in degree phase is 73.8 + delay_tune * 0.9.
++	 * To avoid floating point operations we'll multiply by 10
++	 * and get 1 decimal point precision.
++	 */
++	phase *= 10;
++	return (phase - 738) / 9;
++}
++
++static int sja1105pqrs_setup_rgmii_delay(struct sja1105_private *priv, int port)
++{
++	const struct sja1105_regs *regs = priv->info->regs;
++	struct sja1105_cfg_pad_mii_id pad_mii_id = {0};
++	u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0};
++	int rc;
++
++	if (priv->rgmii_rx_delay[port])
++		pad_mii_id.rxc_delay = sja1105_rgmii_delay(90);
++	if (priv->rgmii_tx_delay[port])
++		pad_mii_id.txc_delay = sja1105_rgmii_delay(90);
++
++	/* Stage 1: Turn the RGMII delay lines off. */
++	pad_mii_id.rxc_bypass = 1;
++	pad_mii_id.rxc_pd = 1;
++	pad_mii_id.txc_bypass = 1;
++	pad_mii_id.txc_pd = 1;
++	sja1105_cfg_pad_mii_id_packing(packed_buf, &pad_mii_id, PACK);
++
++	rc = sja1105_xfer_buf(priv, SPI_WRITE, regs->pad_mii_id[port],
++			      packed_buf, SJA1105_SIZE_CGU_CMD);
++	if (rc < 0)
++		return rc;
++
++	/* Stage 2: Turn the RGMII delay lines on. */
++	if (priv->rgmii_rx_delay[port]) {
++		pad_mii_id.rxc_bypass = 0;
++		pad_mii_id.rxc_pd = 0;
++	}
++	if (priv->rgmii_tx_delay[port]) {
++		pad_mii_id.txc_bypass = 0;
++		pad_mii_id.txc_pd = 0;
++	}
++	sja1105_cfg_pad_mii_id_packing(packed_buf, &pad_mii_id, PACK);
++
++	return sja1105_xfer_buf(priv, SPI_WRITE, regs->pad_mii_id[port],
++				packed_buf, SJA1105_SIZE_CGU_CMD);
++}
++
++static int sja1105_rgmii_clocking_setup(struct sja1105_private *priv, int port,
++					sja1105_mii_role_t role)
++{
++	struct sja1105_mac_config_entry *mac;
++	sja1105_speed_t speed;
++	int rc;
++
++	mac = priv->static_config.tables[BLK_IDX_MAC_CONFIG].entries;
++	speed = mac[port].speed;
++
++	switch (speed) {
++	case SJA1105_SPEED_1000MBPS:
++		/* 1000Mbps, IDIV disabled (125 MHz) */
++		rc = sja1105_cgu_idiv_config(priv, port, false, 1);
++		break;
++	case SJA1105_SPEED_100MBPS:
++		/* 100Mbps, IDIV enabled, divide by 1 (25 MHz) */
++		rc = sja1105_cgu_idiv_config(priv, port, true, 1);
++		break;
++	case SJA1105_SPEED_10MBPS:
++		/* 10Mbps, IDIV enabled, divide by 10 (2.5 MHz) */
++		rc = sja1105_cgu_idiv_config(priv, port, true, 10);
++		break;
++	default:
++		rc = -EINVAL;
++	}
++
++	if (rc < 0) {
++		dev_err(dev, "Failed to configure idiv\n");
++		return rc;
++	}
++	rc = sja1105_cgu_rgmii_tx_clk_config(priv, port, speed);
++	if (rc < 0) {
++		dev_err(dev, "Failed to configure RGMII Tx clock\n");
++		return rc;
++	}
++	rc = sja1105_rgmii_cfg_pad_tx_config(priv, port);
++	if (rc < 0) {
++		dev_err(dev, "Failed to configure Tx pad registers\n");
++		return rc;
++	}
++	if (!priv->info->setup_rgmii_delay)
++		return 0;
++	/* The role has no hardware effect for RGMII. However we use it as
++	 * a proxy for this interface being a MAC-to-MAC connection, with
++	 * the RGMII internal delays needing to be applied by us.
++	 */
++	if (role == XMII_MAC)
++		return 0;
++
++	return priv->info->setup_rgmii_delay(priv, port);
++}
++
++static int sja1105_cgu_rmii_ref_clk_config(struct sja1105_private *priv,
++					   int port)
++{
++	const struct sja1105_regs *regs = priv->info->regs;
++	struct sja1105_cgu_mii_ctrl ref_clk;
++	u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0};
++	const int clk_sources[] = {
++		CLKSRC_MII0_TX_CLK,
++		CLKSRC_MII1_TX_CLK,
++		CLKSRC_MII2_TX_CLK,
++		CLKSRC_MII3_TX_CLK,
++		CLKSRC_MII4_TX_CLK,
++	};
++
++	/* Payload for packed_buf */
++	ref_clk.clksrc    = clk_sources[port];
++	ref_clk.autoblock = 1;      /* Autoblock clk while changing clksrc */
++	ref_clk.pd        = 0;      /* Power Down off => enabled */
++	sja1105_cgu_mii_control_packing(packed_buf, &ref_clk, PACK);
++
++	return sja1105_xfer_buf(priv, SPI_WRITE, regs->rmii_ref_clk[port],
++				packed_buf, SJA1105_SIZE_CGU_CMD);
++}
++
++static int
++sja1105_cgu_rmii_ext_tx_clk_config(struct sja1105_private *priv, int port)
++{
++	const struct sja1105_regs *regs = priv->info->regs;
++	struct sja1105_cgu_mii_ctrl ext_tx_clk;
++	u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0};
++
++	/* Payload for packed_buf */
++	ext_tx_clk.clksrc    = CLKSRC_PLL1;
++	ext_tx_clk.autoblock = 1;   /* Autoblock clk while changing clksrc */
++	ext_tx_clk.pd        = 0;   /* Power Down off => enabled */
++	sja1105_cgu_mii_control_packing(packed_buf, &ext_tx_clk, PACK);
++
++	return sja1105_xfer_buf(priv, SPI_WRITE, regs->rmii_ext_tx_clk[port],
++				packed_buf, SJA1105_SIZE_CGU_CMD);
++}
++
++static int sja1105_cgu_rmii_pll_config(struct sja1105_private *priv)
++{
++	const struct sja1105_regs *regs = priv->info->regs;
++	u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0};
++	struct sja1105_cgu_pll_ctrl pll = {0};
++	int rc;
++
++	/* Step 1: PLL1 setup for 50Mhz */
++	pll.pllclksrc = 0xA;
++	pll.msel      = 0x1;
++	pll.autoblock = 0x1;
++	pll.psel      = 0x1;
++	pll.direct    = 0x0;
++	pll.fbsel     = 0x1;
++	pll.bypass    = 0x0;
++	pll.pd        = 0x1;
++
++	sja1105_cgu_pll_control_packing(packed_buf, &pll, PACK);
++	rc = sja1105_xfer_buf(priv, SPI_WRITE, regs->rmii_pll1, packed_buf,
++			      SJA1105_SIZE_CGU_CMD);
++	if (rc < 0)
++		return rc;
++
++	/* Step 2: Enable PLL1 */
++	pll.pd = 0x0;
++
++	sja1105_cgu_pll_control_packing(packed_buf, &pll, PACK);
++	rc = sja1105_xfer_buf(priv, SPI_WRITE, regs->rmii_pll1, packed_buf,
++			      SJA1105_SIZE_CGU_CMD);
++	return rc;
++}
++
++static int sja1105_rmii_clocking_setup(struct sja1105_private *priv, int port,
++				       sja1105_mii_role_t role)
++{
++	int rc;
++
++	/* AH1601.pdf chapter 2.5.1. Sources */
++	if (role == XMII_MAC) {
++		/* Configure and enable PLL1 for 50Mhz output */
++		rc = sja1105_cgu_rmii_pll_config(priv);
++		if (rc < 0)
++			return rc;
++	}
++	/* Disable IDIV for this port */
++	rc = sja1105_cgu_idiv_config(priv, port, false, 1);
++	if (rc < 0)
++		return rc;
++	/* Source to sink mappings */
++	rc = sja1105_cgu_rmii_ref_clk_config(priv, port);
++	if (rc < 0)
++		return rc;
++	if (role == XMII_MAC) {
++		rc = sja1105_cgu_rmii_ext_tx_clk_config(priv, port);
++		if (rc < 0)
++			return rc;
++	}
++	return 0;
++}
++
++static int sja1105_sgmii_write(struct sja1105_private *priv, int pcs_reg,
++			       u16 pcs_val)
++{
++	const struct sja1105_regs *regs = priv->info->regs;
++	u8 packed_buf[4] = {0};
++	u64 val = pcs_val;
++
++	sja1105_packing(packed_buf, &val, 31, 0, 4, PACK);
++
++	return sja1105_xfer_buf(priv, SPI_WRITE, regs->sgmii + pcs_reg,
++				packed_buf, 4);
++}
++
++static int sja1105_sgmii_setup(struct sja1105_private *priv, int port)
++{
++	int bmcr = BMCR_FULLDPLX | priv->sgmii_cfg.pcs_speed;
++
++	if (priv->sgmii_cfg.inband_an)
++		bmcr |= BMCR_ANENABLE | BMCR_ANRESTART;
++
++	sja1105_sgmii_write(priv, MII_BMCR, bmcr);
++	/* DIGITAL_CONTROL_1: Enable vendor-specific MMD1, allow the PHY to
++	 * stop the clock during LPI mode, make the MAC reconfigure
++	 * autonomously after PCS autoneg is done, flush the internal FIFOs.
++	 */
++	sja1105_sgmii_write(priv, 0x8000, SJA1105_DC1_EN_VSMMD1 |
++					  SJA1105_DC1_CLOCK_STOP_EN |
++					  SJA1105_DC1_MAC_AUTO_SW |
++					  SJA1105_DC1_INIT);
++	/* DIGITAL_CONTROL_2: No polarity inversion for TX and RX lanes */
++	sja1105_sgmii_write(priv, 0x80e1, SJA1105_DC2_TX_POL_INV_DISABLE);
++	/* AUTONEG_CONTROL: Use SGMII autoneg */
++	sja1105_sgmii_write(priv, 0x8001, SJA1105_AC_AUTONEG_MODE_SGMII);
++
++	return 0;
++}
++
++static int sja1105_clocking_setup_port(struct sja1105_private *priv, int port)
++{
++	struct sja1105_xmii_params_entry *mii;
++	sja1105_phy_interface_t phy_mode;
++	sja1105_mii_role_t role;
++	int rc;
++
++	mii = priv->static_config.tables[BLK_IDX_XMII_PARAMS].entries;
++
++	/* RGMII etc */
++	phy_mode = mii->xmii_mode[port];
++	/* MAC or PHY, for applicable types (not RGMII) */
++	role = mii->phy_mac[port];
++
++	switch (phy_mode) {
++	case XMII_MODE_MII:
++		rc = sja1105_mii_clocking_setup(priv, port, role);
++		break;
++	case XMII_MODE_RMII:
++		rc = sja1105_rmii_clocking_setup(priv, port, role);
++		break;
++	case XMII_MODE_RGMII:
++		rc = sja1105_rgmii_clocking_setup(priv, port, role);
++		break;
++	case XMII_MODE_SGMII:
++		rc = sja1105_sgmii_setup(priv, port);
++		break;
++	default:
++		return -EINVAL;
++	}
++	return rc;
++}
++
++static int sja1105_clocking_setup(struct sja1105_private *priv)
++{
++	int port, rc;
++
++	for (port = 0; port < SJA1105_NUM_PORTS; port++) {
++		rc = sja1105_clocking_setup_port(priv, port);
++		if (rc < 0)
++			return rc;
++	}
++	return 0;
++}
++
++static struct sja1105_regs sja1105et_regs = {
++	.device_id = 0x0,
++	.prod_id = 0x100BC3,
++	.status = 0x1,
++	.port_control = 0x11,
++	.config = 0x020000,
++	.rgu = 0x100440,
++	/* UM10944.pdf, Table 86, ACU Register overview */
++	.pad_mii_tx = {0x100800, 0x100802, 0x100804, 0x100806, 0x100808},
++	.rmii_pll1 = 0x10000A,
++	.cgu_idiv = {0x10000B, 0x10000C, 0x10000D, 0x10000E, 0x10000F},
++	/* UM10944.pdf, Table 78, CGU Register overview */
++	.mii_tx_clk = {0x100013, 0x10001A, 0x100021, 0x100028, 0x10002F},
++	.mii_rx_clk = {0x100014, 0x10001B, 0x100022, 0x100029, 0x100030},
++	.mii_ext_tx_clk = {0x100018, 0x10001F, 0x100026, 0x10002D, 0x100034},
++	.mii_ext_rx_clk = {0x100019, 0x100020, 0x100027, 0x10002E, 0x100035},
++	.rgmii_tx_clk = {0x100016, 0x10001D, 0x100024, 0x10002B, 0x100032},
++	.rmii_ref_clk = {0x100015, 0x10001C, 0x100023, 0x10002A, 0x100031},
++	.rmii_ext_tx_clk = {0x100018, 0x10001F, 0x100026, 0x10002D, 0x100034},
++};
++
++static struct sja1105_regs sja1105pqrs_regs = {
++	.device_id = 0x0,
++	.prod_id = 0x100BC3,
++	.status = 0x1,
++	.port_control = 0x12,
++	.config = 0x020000,
++	.rgu = 0x100440,
++	/* UM10944.pdf, Table 86, ACU Register overview */
++	.pad_mii_tx = {0x100800, 0x100802, 0x100804, 0x100806, 0x100808},
++	.pad_mii_id = {0x100810, 0x100811, 0x100812, 0x100813, 0x100814},
++	.rmii_pll1 = 0x10000A,
++	.cgu_idiv = {0x10000B, 0x10000C, 0x10000D, 0x10000E, 0x10000F},
++	/* UM11040.pdf, Table 114 */
++	.mii_tx_clk = {0x100013, 0x100019, 0x10001F, 0x100025, 0x10002B},
++	.mii_rx_clk = {0x100014, 0x10001A, 0x100020, 0x100026, 0x10002C},
++	.mii_ext_tx_clk = {0x100017, 0x10001D, 0x100023, 0x100029, 0x10002F},
++	.mii_ext_rx_clk = {0x100018, 0x10001E, 0x100024, 0x10002A, 0x100030},
++	.rgmii_tx_clk = {0x100016, 0x10001C, 0x100022, 0x100028, 0x10002E},
++	.rmii_ref_clk = {0x100015, 0x10001B, 0x100021, 0x100027, 0x10002D},
++	.rmii_ext_tx_clk = {0x100017, 0x10001D, 0x100023, 0x100029, 0x10002F},
++	.sgmii = 0x1F0000,
++};
++
++enum sja1105_switch_id {
++	SJA1105E = 0,
++	SJA1105T,
++	SJA1105P,
++	SJA1105Q,
++	SJA1105R,
++	SJA1105S,
++};
++
++struct sja1105_info sja1105_info[] = {
++	[SJA1105E] = {
++		.device_id		= SJA1105E_DEVICE_ID,
++		.part_no		= SJA1105ET_PART_NO,
++		.static_ops		= sja1105et_table_ops,
++		.reset_cmd		= sja1105et_reset_cmd,
++		.regs			= &sja1105et_regs,
++		.name			= "SJA1105E",
++	},
++	[SJA1105T] = {
++		.device_id		= SJA1105T_DEVICE_ID,
++		.part_no		= SJA1105ET_PART_NO,
++		.static_ops		= sja1105et_table_ops,
++		.reset_cmd		= sja1105et_reset_cmd,
++		.regs			= &sja1105et_regs,
++		.name			= "SJA1105T",
++	},
++	[SJA1105P] = {
++		.device_id		= SJA1105PR_DEVICE_ID,
++		.part_no		= SJA1105P_PART_NO,
++		.static_ops		= sja1105pqrs_table_ops,
++		.setup_rgmii_delay	= sja1105pqrs_setup_rgmii_delay,
++		.reset_cmd		= sja1105pqrs_reset_cmd,
++		.regs			= &sja1105pqrs_regs,
++		.name			= "SJA1105P",
++	},
++	[SJA1105Q] = {
++		.device_id		= SJA1105QS_DEVICE_ID,
++		.part_no		= SJA1105Q_PART_NO,
++		.static_ops		= sja1105pqrs_table_ops,
++		.setup_rgmii_delay	= sja1105pqrs_setup_rgmii_delay,
++		.reset_cmd		= sja1105pqrs_reset_cmd,
++		.regs			= &sja1105pqrs_regs,
++		.name			= "SJA1105Q",
++	},
++	[SJA1105R] = {
++		.device_id		= SJA1105PR_DEVICE_ID,
++		.part_no		= SJA1105R_PART_NO,
++		.static_ops		= sja1105pqrs_table_ops,
++		.setup_rgmii_delay	= sja1105pqrs_setup_rgmii_delay,
++		.reset_cmd		= sja1105pqrs_reset_cmd,
++		.regs			= &sja1105pqrs_regs,
++		.name			= "SJA1105R",
++	},
++	[SJA1105S] = {
++		.device_id		= SJA1105QS_DEVICE_ID,
++		.part_no		= SJA1105S_PART_NO,
++		.static_ops		= sja1105pqrs_table_ops,
++		.setup_rgmii_delay	= sja1105pqrs_setup_rgmii_delay,
++		.reset_cmd		= sja1105pqrs_reset_cmd,
++		.regs			= &sja1105pqrs_regs,
++		.name			= "SJA1105S",
++	},
++};
++
++static void
++sja1105_port_allow_traffic(struct sja1105_l2_forwarding_entry *l2_fwd,
++			   int from, int to)
++{
++	l2_fwd[from].bc_domain  |= BIT(to);
++	l2_fwd[from].reach_port |= BIT(to);
++	l2_fwd[from].fl_domain  |= BIT(to);
++}
++
++static int sja1105_init_mac_settings(struct sja1105_private *priv)
++{
++	struct sja1105_mac_config_entry default_mac = {
++		/* Enable 1 priority queue on egress. */
++		.top  = {0x1FF, 0, 0, 0, 0, 0, 0},
++		.base = {0x0, 0, 0, 0, 0, 0, 0, 0},
++		.enabled = {1, 0, 0, 0, 0, 0, 0, 0},
++		/* Default for all ports including the CPU. FIXME: may not work
++		 * for all ports.
++		 */
++		.speed = SJA1105_SPEED_1000MBPS,
++		/* Disable aging for critical TTEthernet traffic */
++		.maxage = 0xFF,
++		.dyn_learn = true,
++		.egress = true,
++		.ingress = true,
++	};
++	struct sja1105_mac_config_entry *mac;
++	struct sja1105_table *table;
++	int port;
++
++	table = &priv->static_config.tables[BLK_IDX_MAC_CONFIG];
++
++	table->entries = calloc(SJA1105_NUM_PORTS,
++				table->ops->unpacked_entry_size);
++	if (!table->entries)
++		return -ENOMEM;
++
++	table->entry_count = SJA1105_NUM_PORTS;
++
++	mac = table->entries;
++
++	for (port = 0; port < SJA1105_NUM_PORTS; port++) {
++		mac[port] = default_mac;
++		/* Internal VLAN (pvid) to apply to untagged ingress */
++		mac[port].vlanid = priv->pvid[port];
++	}
++
++	return 0;
++}
++
++static bool sja1105_supports_sgmii(struct sja1105_private *priv, int port)
++{
++	if (priv->info->part_no != SJA1105R_PART_NO &&
++	    priv->info->part_no != SJA1105S_PART_NO)
++		return false;
++
++	if (port != SJA1105_SGMII_PORT)
++		return false;
++
++	return true;
++}
++
++static int sja1105_init_mii_settings(struct sja1105_private *priv)
++{
++	struct dsa_perdev_platdata *platdata = priv->dev->platdata;
++	struct sja1105_xmii_params_entry *mii;
++	struct sja1105_table *table;
++	int port;
++
++	table = &priv->static_config.tables[BLK_IDX_XMII_PARAMS];
++
++	table->entries = calloc(SJA1105_MAX_XMII_PARAMS_COUNT,
++				table->ops->unpacked_entry_size);
++	if (!table->entries)
++		return -ENOMEM;
++
++	/* Override table based on DT bindings */
++	table->entry_count = SJA1105_MAX_XMII_PARAMS_COUNT;
++
++	mii = table->entries;
++
++	for (port = 0; port < SJA1105_NUM_PORTS; port++) {
++		struct phy_device *phy = platdata->port[port].phy;
++
++		switch (priv->phy_modes[port]) {
++		case PHY_INTERFACE_MODE_MII:
++			mii->xmii_mode[port] = XMII_MODE_MII;
++			break;
++		case PHY_INTERFACE_MODE_RMII:
++			mii->xmii_mode[port] = XMII_MODE_RMII;
++			break;
++		case PHY_INTERFACE_MODE_RGMII:
++		case PHY_INTERFACE_MODE_RGMII_ID:
++		case PHY_INTERFACE_MODE_RGMII_RXID:
++		case PHY_INTERFACE_MODE_RGMII_TXID:
++			mii->xmii_mode[port] = XMII_MODE_RGMII;
++			break;
++		case PHY_INTERFACE_MODE_SGMII:
++			if (!sja1105_supports_sgmii(priv, port))
++				return -EINVAL;
++			mii->xmii_mode[port] = XMII_MODE_SGMII;
++			break;
++		default:
++			return -EINVAL;
++		}
++
++		if (priv->phy_modes[port] != PHY_INTERFACE_MODE_SGMII &&
++		    (!phy || phy->phy_id == PHY_FIXED_ID))
++			mii->phy_mac[port] = XMII_PHY;
++		else
++			mii->phy_mac[port] = XMII_MAC;
++	}
++	return 0;
++}
++
++static int sja1105_init_l2_lookup_params(struct sja1105_private *priv)
++{
++	u64 max_fdb_entries = SJA1105_MAX_L2_LOOKUP_COUNT / SJA1105_NUM_PORTS;
++	struct sja1105_l2_lookup_params_entry default_l2_lookup_params = {
++		/* All entries within a FDB bin are available for learning */
++		.dyn_tbsz = SJA1105ET_FDB_BIN_SIZE,
++		/* And the P/Q/R/S equivalent setting: */
++		.start_dynspc = 0,
++		.maxaddrp = {max_fdb_entries, max_fdb_entries, max_fdb_entries,
++			     max_fdb_entries, max_fdb_entries, },
++		/* 2^8 + 2^5 + 2^3 + 2^2 + 2^1 + 1 in Koopman notation */
++		.poly = 0x97,
++		/* P/Q/R/S only */
++		.use_static = true,
++		/* Dynamically learned FDB entries can overwrite other (older)
++		 * dynamic FDB entries
++		 */
++		.owr_dyn = true,
++	};
++	struct sja1105_table *table;
++
++	table = &priv->static_config.tables[BLK_IDX_L2_LOOKUP_PARAMS];
++
++	table->entries = calloc(SJA1105_MAX_L2_LOOKUP_PARAMS_COUNT,
++				table->ops->unpacked_entry_size);
++	if (!table->entries)
++		return -ENOMEM;
++
++	table->entry_count = SJA1105_MAX_L2_LOOKUP_PARAMS_COUNT;
++
++	/* This table only has a single entry */
++	((struct sja1105_l2_lookup_params_entry *)table->entries)[0] =
++				default_l2_lookup_params;
++
++	return 0;
++}
++
++static void sja1105_setup_tagging(struct sja1105_private *priv, int port)
++{
++	struct dsa_perdev_platdata *platdata = priv->dev->platdata;
++	struct sja1105_vlan_lookup_entry *vlan;
++	int cpu = platdata->cpu_port;
++
++	/* The CPU port is implicitly configured by
++	 * configuring the front-panel ports
++	 */
++	if (port == cpu)
++		return;
++
++	vlan = priv->static_config.tables[BLK_IDX_VLAN_LOOKUP].entries;
++
++	priv->pvid[port] = DSA_8021Q_DIR_RX | DSA_8021Q_PORT(port);
++
++	vlan[port].vmemb_port	= BIT(port) | BIT(cpu);
++	vlan[port].vlan_bc	= BIT(port) | BIT(cpu);
++	vlan[port].tag_port	= BIT(cpu);
++	vlan[port].vlanid	= priv->pvid[port];
++}
++
++static int sja1105_init_vlan(struct sja1105_private *priv)
++{
++	struct sja1105_table *table;
++	int port;
++
++	table = &priv->static_config.tables[BLK_IDX_VLAN_LOOKUP];
++
++	table->entries = calloc(SJA1105_NUM_PORTS,
++				table->ops->unpacked_entry_size);
++	if (!table->entries)
++		return -ENOMEM;
++
++	table->entry_count = SJA1105_NUM_PORTS;
++
++	for (port = 0; port < SJA1105_NUM_PORTS; port++)
++		sja1105_setup_tagging(priv, port);
++
++	return 0;
++}
++
++static int sja1105_init_l2_forwarding(struct sja1105_private *priv)
++{
++	struct dsa_perdev_platdata *platdata = priv->dev->platdata;
++	struct sja1105_l2_forwarding_entry *l2fwd;
++	int cpu = platdata->cpu_port;
++	struct sja1105_table *table;
++	int i;
++
++	table = &priv->static_config.tables[BLK_IDX_L2_FORWARDING];
++
++	table->entries = calloc(SJA1105_MAX_L2_FORWARDING_COUNT,
++				table->ops->unpacked_entry_size);
++	if (!table->entries)
++		return -ENOMEM;
++
++	table->entry_count = SJA1105_MAX_L2_FORWARDING_COUNT;
++
++	l2fwd = table->entries;
++
++	/* First 5 entries define the forwarding rules */
++	for (i = 0; i < SJA1105_NUM_PORTS; i++) {
++		if (i == cpu)
++			continue;
++
++		sja1105_port_allow_traffic(l2fwd, i, cpu);
++		sja1105_port_allow_traffic(l2fwd, cpu, i);
++	}
++	/* Next 8 entries define VLAN PCP mapping from ingress to egress.
++	 * Leave them unpopulated (implicitly 0) but present.
++	 */
++	return 0;
++}
++
++static int sja1105_init_l2_forwarding_params(struct sja1105_private *priv)
++{
++	struct sja1105_l2_forwarding_params_entry default_l2fwd_params = {
++		/* Use a single memory partition for all ingress queues */
++		.part_spc = { SJA1105_MAX_FRAME_MEMORY, 0, 0, 0, 0, 0, 0, 0 },
++	};
++	struct sja1105_table *table;
++
++	table = &priv->static_config.tables[BLK_IDX_L2_FORWARDING_PARAMS];
++
++	table->entries = calloc(SJA1105_MAX_L2_FORWARDING_PARAMS_COUNT,
++				table->ops->unpacked_entry_size);
++	if (!table->entries)
++		return -ENOMEM;
++
++	table->entry_count = SJA1105_MAX_L2_FORWARDING_PARAMS_COUNT;
++
++	/* This table only has a single entry */
++	((struct sja1105_l2_forwarding_params_entry *)table->entries)[0] =
++				default_l2fwd_params;
++
++	return 0;
++}
++
++static int sja1105_init_general_params(struct sja1105_private *priv)
++{
++	struct sja1105_general_params_entry default_general_params = {
++		/* No frame trapping */
++		.mac_fltres1 = 0x0,
++		.mac_flt1    = 0xffffffffffff,
++		.mac_fltres0 = 0x0,
++		.mac_flt0    = 0xffffffffffff,
++		.host_port = SJA1105_NUM_PORTS,
++		/* No mirroring => specify an out-of-range port value */
++		.mirr_port = SJA1105_NUM_PORTS,
++		/* No link-local trapping => specify an out-of-range port value
++		 */
++		.casc_port = SJA1105_NUM_PORTS,
++		/* Force the switch to see all traffic as untagged. */
++		.tpid = ETH_P_SJA1105,
++		.tpid2 = ETH_P_SJA1105,
++	};
++	struct sja1105_table *table;
++
++	table = &priv->static_config.tables[BLK_IDX_GENERAL_PARAMS];
++
++	table->entries = calloc(SJA1105_MAX_GENERAL_PARAMS_COUNT,
++				table->ops->unpacked_entry_size);
++	if (!table->entries)
++		return -ENOMEM;
++
++	table->entry_count = SJA1105_MAX_GENERAL_PARAMS_COUNT;
++
++	/* This table only has a single entry */
++	((struct sja1105_general_params_entry *)table->entries)[0] =
++				default_general_params;
++
++	return 0;
++}
++
++static void sja1105_setup_policer(struct sja1105_l2_policing_entry *policing,
++				  int index, int mtu)
++{
++	policing[index].sharindx = index;
++	policing[index].smax = 65535; /* Burst size in bytes */
++	policing[index].rate = SJA1105_RATE_MBPS(1000);
++	policing[index].maxlen = mtu;
++	policing[index].partition = 0;
++}
++
++static int sja1105_init_l2_policing(struct sja1105_private *priv)
++{
++	struct dsa_perdev_platdata *platdata = priv->dev->platdata;
++	struct sja1105_l2_policing_entry *policing;
++	int cpu = platdata->cpu_port;
++	struct sja1105_table *table;
++	int i, j, k;
++
++	table = &priv->static_config.tables[BLK_IDX_L2_POLICING];
++
++	table->entries = calloc(SJA1105_MAX_L2_POLICING_COUNT,
++				table->ops->unpacked_entry_size);
++	if (!table->entries)
++		return -ENOMEM;
++
++	table->entry_count = SJA1105_MAX_L2_POLICING_COUNT;
++
++	policing = table->entries;
++
++	/* k sweeps through all unicast policers (0-39).
++	 * bcast sweeps through policers 40-44.
++	 */
++	for (i = 0, k = 0; i < SJA1105_NUM_PORTS; i++) {
++		int bcast = (SJA1105_NUM_PORTS * SJA1105_NUM_TC) + i;
++		int mtu = VLAN_ETH_FRAME_LEN + ETH_FCS_LEN;
++
++		if (i == cpu)
++			mtu += VLAN_HLEN;
++
++		for (j = 0; j < SJA1105_NUM_TC; j++, k++)
++			sja1105_setup_policer(policing, k, mtu);
++
++		/* Set up this port's policer for broadcast traffic */
++		sja1105_setup_policer(policing, bcast, mtu);
++	}
++	return 0;
++}
++
++struct sja1105_status {
++	u64 configs;
++	u64 crcchkl;
++	u64 ids;
++	u64 crcchkg;
++};
++
++static void sja1105_status_unpack(void *buf, struct sja1105_status *status)
++{
++	sja1105_packing(buf, &status->configs,   31, 31, 4, UNPACK);
++	sja1105_packing(buf, &status->crcchkl,   30, 30, 4, UNPACK);
++	sja1105_packing(buf, &status->ids,       29, 29, 4, UNPACK);
++	sja1105_packing(buf, &status->crcchkg,   28, 28, 4, UNPACK);
++}
++
++static int sja1105_status_get(struct sja1105_private *priv,
++			      struct sja1105_status *status)
++{
++	const struct sja1105_regs *regs = priv->info->regs;
++	u8 packed_buf[4];
++	int rc;
++
++	rc = sja1105_xfer_buf(priv, SPI_READ, regs->status, packed_buf, 4);
++	if (rc < 0)
++		return rc;
++
++	sja1105_status_unpack(packed_buf, status);
++
++	return 0;
++}
++
++/* Not const because unpacking priv->static_config into buffers and preparing
++ * for upload requires the recalculation of table CRCs and updating the
++ * structures with these.
++ */
++static int
++static_config_buf_prepare_for_upload(struct sja1105_private *priv,
++				     void *config_buf, int buf_len)
++{
++	struct sja1105_static_config *config = &priv->static_config;
++	struct sja1105_table_header final_header;
++	char *final_header_ptr;
++	int crc_len;
++
++	/* Write Device ID and config tables to config_buf */
++	sja1105_static_config_pack(config_buf, config);
++	/* Recalculate CRC of the last header (right now 0xDEADBEEF).
++	 * Don't include the CRC field itself.
++	 */
++	crc_len = buf_len - 4;
++	/* Read the whole table header */
++	final_header_ptr = config_buf + buf_len - SJA1105_SIZE_TABLE_HEADER;
++	sja1105_table_header_packing(final_header_ptr, &final_header, UNPACK);
++	/* Modify */
++	final_header.crc = sja1105_crc32(config_buf, crc_len);
++	/* Rewrite */
++	sja1105_table_header_packing(final_header_ptr, &final_header, PACK);
++
++	return 0;
++}
++
++static int sja1105_static_config_upload(struct sja1105_private *priv)
++{
++	struct sja1105_static_config *config = &priv->static_config;
++	const struct sja1105_regs *regs = priv->info->regs;
++	struct sja1105_status status;
++	u8 *config_buf;
++	int buf_len;
++	int rc;
++
++	buf_len = sja1105_static_config_get_length(config);
++	config_buf = calloc(buf_len, sizeof(char));
++	if (!config_buf)
++		return -ENOMEM;
++
++	rc = static_config_buf_prepare_for_upload(priv, config_buf, buf_len);
++	if (rc < 0) {
++		printf("Invalid config, cannot upload\n");
++		rc = -EINVAL;
++		goto out;
++	}
++	/* Put the SJA1105 in programming mode */
++	rc = priv->info->reset_cmd(priv);
++	if (rc < 0) {
++		printf("Failed to reset switch\n");
++		goto out;
++	}
++	/* Wait for the switch to come out of reset */
++	udelay(1000);
++	/* Upload the static config to the device */
++	rc = sja1105_xfer_buf(priv, SPI_WRITE, regs->config,
++			      config_buf, buf_len);
++	if (rc < 0) {
++		printf("Failed to upload config\n");
++		goto out;
++	}
++	/* Check that SJA1105 responded well to the config upload */
++	rc = sja1105_status_get(priv, &status);
++	if (rc < 0)
++		goto out;
++
++	if (status.ids == 1) {
++		printf("Mismatch between hardware and static config device id. "
++		       "Wrote 0x%llx, wants 0x%llx\n",
++		       config->device_id, priv->info->device_id);
++		rc = -EIO;
++		goto out;
++	}
++	if (status.crcchkl == 1 || status.crcchkg == 1) {
++		printf("Switch reported invalid CRC on static config\n");
++		rc = -EIO;
++		goto out;
++	}
++	if (status.configs == 0) {
++		printf("Switch reported that config is invalid\n");
++		rc = -EIO;
++		goto out;
++	}
++
++out:
++	free(config_buf);
++	return rc;
++}
++
++static int sja1105_static_config_load(struct sja1105_private *priv)
++{
++	int rc;
++
++	rc = sja1105_static_config_init(&priv->static_config,
++					priv->info->static_ops,
++					priv->info->device_id);
++	if (rc)
++		return rc;
++
++	/* Build static configuration */
++	rc = sja1105_init_vlan(priv);
++	if (rc < 0)
++		return rc;
++	rc = sja1105_init_mac_settings(priv);
++	if (rc < 0)
++		return rc;
++	rc = sja1105_init_mii_settings(priv);
++	if (rc < 0)
++		return rc;
++	rc = sja1105_init_l2_lookup_params(priv);
++	if (rc < 0)
++		return rc;
++	rc = sja1105_init_l2_forwarding(priv);
++	if (rc < 0)
++		return rc;
++	rc = sja1105_init_l2_forwarding_params(priv);
++	if (rc < 0)
++		return rc;
++	rc = sja1105_init_l2_policing(priv);
++	if (rc < 0)
++		return rc;
++	rc = sja1105_init_general_params(priv);
++	if (rc < 0)
++		return rc;
++
++	/* Send initial configuration to hardware via SPI */
++	return sja1105_static_config_upload(priv);
++}
++
++static int sja1105_static_config_reload(struct sja1105_private *priv)
++{
++	int rc;
++
++	rc = sja1105_static_config_upload(priv);
++	if (rc < 0) {
++		printf("Failed to load static config: %d\n", rc);
++		return rc;
++	}
++
++	/* Configure the CGU (PHY link modes and speeds) */
++	rc = sja1105_clocking_setup(priv);
++	if (rc < 0) {
++		printf("Failed to configure MII clocking: %d\n", rc);
++		return rc;
++	}
++
++	return 0;
++}
++
++static int sja1105_port_enable(struct udevice *dev, int port,
++			       struct phy_device *phy)
++{
++	struct sja1105_private *priv = dev_get_priv(dev);
++	struct sja1105_xmii_params_entry *mii;
++	struct sja1105_mac_config_entry *mac;
++
++	if (phy)
++		phy_startup(phy);
++
++	mii = priv->static_config.tables[BLK_IDX_XMII_PARAMS].entries;
++	mac = priv->static_config.tables[BLK_IDX_MAC_CONFIG].entries;
++
++	if (mii->xmii_mode[port] == XMII_MODE_SGMII) {
++		priv->sgmii_cfg.inband_an = (phy && phy->phy_id != PHY_FIXED_ID);
++		priv->sgmii_cfg.pcs_speed = BMCR_SPEED1000;
++		mac[port].speed = SJA1105_SPEED_1000MBPS;
++	} else {
++		int speed = phy ? phy->speed : SPEED_1000;
++
++		switch (speed) {
++		case SPEED_1000:
++			mac[port].speed = SJA1105_SPEED_1000MBPS;
++			break;
++		case SPEED_100:
++			mac[port].speed = SJA1105_SPEED_100MBPS;
++			break;
++		case SPEED_10:
++			mac[port].speed = SJA1105_SPEED_10MBPS;
++			break;
++		default:
++			printf("Invalid speed %d\n", speed);
++			return -EINVAL;
++		}
++	}
++
++	return sja1105_static_config_reload(priv);
++}
++
++static void sja1105_port_disable(struct udevice *dev, int port,
++				 struct phy_device *phy)
++{
++	if (phy)
++		phy_shutdown(phy);
++}
++
++static int sja1105_xmit(struct udevice *dev, int port, void *packet, int length)
++{
++	struct sja1105_private *priv = dev_get_priv(dev);
++	u8 *from = (u8 *)packet + VLAN_HLEN;
++	struct vlan_ethhdr *hdr = packet;
++	u8 *dest = (u8 *)packet;
++
++	memmove(dest, from, 2 * ETH_ALEN);
++	hdr->h_vlan_proto = htons(ETH_P_SJA1105);
++	hdr->h_vlan_TCI = htons(priv->pvid[port]);
++
++	return 0;
++}
++
++static int sja1105_rcv(struct udevice *dev, int *port, void *packet, int length)
++{
++	struct vlan_ethhdr *hdr = packet;
++	u8 *dest = packet + VLAN_HLEN;
++	u8 *from = packet;
++
++	if (ntohs(hdr->h_vlan_proto) != ETH_P_SJA1105)
++		return -EINVAL;
++
++	*port = ntohs(hdr->h_vlan_TCI) & DSA_8021Q_PORT_MASK;
++	memmove(dest, from, 2 * ETH_ALEN);
++
++	return 0;
++}
++
++static const struct dsa_ops sja1105_dsa_ops = {
++	.port_enable	= sja1105_port_enable,
++	.port_disable	= sja1105_port_disable,
++	.xmit		= sja1105_xmit,
++	.rcv		= sja1105_rcv,
++};
++
++static int sja1105_init_port(struct sja1105_private *priv, int port)
++{
++	struct dsa_perdev_platdata *platdata = priv->dev->platdata;
++	int phy_mode = PHY_INTERFACE_MODE_NONE;
++	struct phy_device *phy;
++	const char *if_str;
++
++	if (!ofnode_is_available(platdata->port[port].node)) {
++		dev_dbg(priv->dev, "port %d is not available, skipping\n",
++			port);
++		return 0;
++	}
++
++	if_str = ofnode_read_string(platdata->port[port].node, "phy-mode");
++	if (if_str)
++		phy_mode = phy_get_interface_by_name(if_str);
++
++	phy = platdata->port[port].phy;
++	if (phy) {
++		if (phy_mode == PHY_INTERFACE_MODE_MII ||
++		    phy_mode == PHY_INTERFACE_MODE_RMII) {
++			phy->supported &= PHY_BASIC_FEATURES;
++			phy->advertising &= PHY_BASIC_FEATURES;
++		} else {
++			phy->supported &= PHY_GBIT_FEATURES;
++			phy->advertising &= PHY_GBIT_FEATURES;
++		}
++		phy_config(phy);
++	}
++
++	if (phy_mode != PHY_INTERFACE_MODE_RGMII_TXID &&
++	    phy_mode != PHY_INTERFACE_MODE_RGMII_RXID &&
++	    phy_mode != PHY_INTERFACE_MODE_RGMII_ID &&
++	    phy_mode != PHY_INTERFACE_MODE_RGMII &&
++	    phy_mode != PHY_INTERFACE_MODE_SGMII &&
++	    phy_mode != PHY_INTERFACE_MODE_RMII &&
++	    phy_mode != PHY_INTERFACE_MODE_MII) {
++		printf("Unsupported PHY mode %s!\n", if_str);
++		return -EINVAL;
++	}
++
++	/* Let the PHY handle the RGMII delays, if present. */
++	if (!phy || phy->phy_id == PHY_FIXED_ID) {
++		if (phy_mode == PHY_INTERFACE_MODE_RGMII_RXID ||
++		    phy_mode == PHY_INTERFACE_MODE_RGMII_ID)
++			priv->rgmii_rx_delay[port] = true;
++
++		if (phy_mode == PHY_INTERFACE_MODE_RGMII_TXID ||
++		    phy_mode == PHY_INTERFACE_MODE_RGMII_ID)
++			priv->rgmii_tx_delay[port] = true;
++
++		if ((priv->rgmii_rx_delay[port] ||
++		     priv->rgmii_tx_delay[port]) &&
++		     !priv->info->setup_rgmii_delay)
++			return -EINVAL;
++	}
++
++	priv->phy_modes[port] = phy_mode;
++
++	return 0;
++}
++
++static int sja1105_init(struct sja1105_private *priv)
++{
++	int port;
++	int rc;
++
++	for (port = 0; port < SJA1105_NUM_PORTS; port++) {
++		rc = sja1105_init_port(priv, port);
++		if (rc < 0) {
++			printf("Failed to initialize port %d\n", port);
++			return rc;
++		}
++	}
++
++	rc = sja1105_static_config_load(priv);
++	if (rc < 0) {
++		printf("Failed to load static config: %d\n", rc);
++		return rc;
++	}
++
++	/* Configure the CGU (PHY link modes and speeds) */
++	rc = sja1105_clocking_setup(priv);
++	if (rc < 0) {
++		printf("Failed to configure MII clocking: %d\n", rc);
++		return rc;
++	}
++
++	return 0;
++}
++
++static int sja1105_check_device_id(struct sja1105_private *priv)
++{
++	const struct sja1105_regs *regs = priv->info->regs;
++	u8 packed_buf[SJA1105_SIZE_DEVICE_ID] = {0};
++	u64 device_id;
++	u64 part_no;
++	int rc;
++
++	rc = sja1105_xfer_buf(priv, SPI_READ, regs->device_id, packed_buf,
++			      SJA1105_SIZE_DEVICE_ID);
++	if (rc < 0)
++		return rc;
++
++	sja1105_packing(packed_buf, &device_id, 31, 0, SJA1105_SIZE_DEVICE_ID,
++			UNPACK);
++
++	if (device_id != priv->info->device_id) {
++		printf("Expected device ID 0x%llx but read 0x%llx\n",
++		       priv->info->device_id, device_id);
++		return -ENODEV;
++	}
++
++	rc = sja1105_xfer_buf(priv, SPI_READ, regs->prod_id, packed_buf,
++			      SJA1105_SIZE_DEVICE_ID);
++	if (rc < 0)
++		return rc;
++
++	sja1105_packing(packed_buf, &part_no, 19, 4, SJA1105_SIZE_DEVICE_ID,
++			UNPACK);
++
++	if (part_no != priv->info->part_no) {
++		printf("Expected part number 0x%llx but read 0x%llx\n",
++		       priv->info->part_no, part_no);
++		return -ENODEV;
++	}
++
++	return 0;
++}
++
++static int sja1105_probe(struct udevice *dev)
++{
++	enum sja1105_switch_id id = dev_get_driver_data(dev);
++	struct sja1105_private *priv = dev_get_priv(dev);
++	int rc;
++
++	if (ofnode_valid(dev->node) && !ofnode_is_available(dev->node)) {
++		dev_dbg(dev, "switch disabled\n");
++		return -ENODEV;
++	}
++
++	priv->info = &sja1105_info[id];
++	priv->dev = dev;
++
++	rc = sja1105_check_device_id(priv);
++	if (rc < 0) {
++		dev_err(dev, "Device ID check failed: %d\n", rc);
++		return rc;
++	}
++
++	return sja1105_init(priv);
++}
++
++static int sja1105_remove(struct udevice *dev)
++{
++	struct sja1105_private *priv = dev->priv;
++
++	sja1105_static_config_free(&priv->static_config);
++
++	return 0;
++}
++
++static int sja1105_bind(struct udevice *dev)
++{
++	struct dsa_perdev_platdata *pdata = dev->platdata;
++
++	pdata->num_ports = SJA1105_NUM_PORTS;
++	pdata->headroom = VLAN_HLEN;
++
++	return 0;
++}
++
++static const struct udevice_id sja1105_ids[] = {
++	{ .compatible = "nxp,sja1105e", .data = SJA1105E },
++	{ .compatible = "nxp,sja1105t", .data = SJA1105T },
++	{ .compatible = "nxp,sja1105p", .data = SJA1105P },
++	{ .compatible = "nxp,sja1105q", .data = SJA1105Q },
++	{ .compatible = "nxp,sja1105r", .data = SJA1105R },
++	{ .compatible = "nxp,sja1105s", .data = SJA1105S },
++	{ }
++};
++
++U_BOOT_DRIVER(sja1105) = {
++	.name				= "sja1105",
++	.id				= UCLASS_DSA,
++	.of_match			= sja1105_ids,
++	.bind				= sja1105_bind,
++	.probe				= sja1105_probe,
++	.remove				= sja1105_remove,
++	.ops				= &sja1105_dsa_ops,
++	.priv_auto_alloc_size		= sizeof(struct sja1105_private),
++	.platdata_auto_alloc_size	= sizeof(struct dsa_perdev_platdata),
++};
+diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
+index 0c563d898b..f21ee4071a 100644
+--- a/include/dm/uclass-id.h
++++ b/include/dm/uclass-id.h
+@@ -42,6 +42,7 @@ enum uclass_id {
+ 	UCLASS_DISPLAY,		/* Display (e.g. DisplayPort, HDMI) */
+ 	UCLASS_DSI_HOST,	/* Display Serial Interface host */
+ 	UCLASS_DMA,		/* Direct Memory Access */
++    UCLASS_DSA,		/* Distributed (Ethernet) Switch Architecture */
+ 	UCLASS_EFI,		/* EFI managed devices */
+ 	UCLASS_ETH,		/* Ethernet device */
+ 	UCLASS_FIRMWARE,	/* Firmware */
+diff --git a/include/linux/if_vlan.h b/include/linux/if_vlan.h
+new file mode 100644
+index 0000000000..cbc82f4cc2
+--- /dev/null
++++ b/include/linux/if_vlan.h
+@@ -0,0 +1,26 @@
++/* SPDX-License-Identifier: GPL-2.0-or-later */
++/*
++ * VLAN		An implementation of 802.1Q VLAN tagging.
++ *
++ * Authors:	Ben Greear <greearb@candelatech.com>
++ */
++#ifndef _LINUX_IF_VLAN_H_
++#define _LINUX_IF_VLAN_H_
++
++/**
++ *	struct vlan_ethhdr - vlan ethernet header (ethhdr + vlan_hdr)
++ *	@h_dest: destination ethernet address
++ *	@h_source: source ethernet address
++ *	@h_vlan_proto: ethernet protocol
++ *	@h_vlan_TCI: priority and VLAN ID
++ *	@h_vlan_encapsulated_proto: packet type ID or len
++ */
++struct vlan_ethhdr {
++	unsigned char	h_dest[ETH_ALEN];
++	unsigned char	h_source[ETH_ALEN];
++	__be16		h_vlan_proto;
++	__be16		h_vlan_TCI;
++	__be16		h_vlan_encapsulated_proto;
++};
++
++#endif /* !(_LINUX_IF_VLAN_H_) */
+diff --git a/include/linux/packing.h b/include/linux/packing.h
+new file mode 100644
+index 0000000000..194e29b8c9
+--- /dev/null
++++ b/include/linux/packing.h
+@@ -0,0 +1,49 @@
++// SPDX-License-Identifier: BSD-3-Clause
++/*
++ * Copyright 2016-2018,2020 NXP
++ */
++#ifndef _LINUX_PACKING_H
++#define _LINUX_PACKING_H
++
++#include <linux/types.h>
++#include <linux/bitops.h>
++
++#define QUIRK_MSB_ON_THE_RIGHT	BIT(0)
++#define QUIRK_LITTLE_ENDIAN	BIT(1)
++#define QUIRK_LSW32_IS_FIRST	BIT(2)
++
++enum packing_op {
++	PACK,
++	UNPACK,
++};
++
++/**
++ * packing - Convert numbers (currently u64) between a packed and an unpacked
++ *	     format. Unpacked means laid out in memory in the CPU's native
++ *	     understanding of integers, while packed means anything else that
++ *	     requires translation.
++ *
++ * @pbuf: Pointer to a buffer holding the packed value.
++ * @uval: Pointer to an u64 holding the unpacked value.
++ * @startbit: The index (in logical notation, compensated for quirks) where
++ *	      the packed value starts within pbuf. Must be larger than, or
++ *	      equal to, endbit.
++ * @endbit: The index (in logical notation, compensated for quirks) where
++ *	    the packed value ends within pbuf. Must be smaller than, or equal
++ *	    to, startbit.
++ * @op: If PACK, then uval will be treated as const pointer and copied (packed)
++ *	into pbuf, between startbit and endbit.
++ *	If UNPACK, then pbuf will be treated as const pointer and the logical
++ *	value between startbit and endbit will be copied (unpacked) to uval.
++ * @quirks: A bit mask of QUIRK_LITTLE_ENDIAN, QUIRK_LSW32_IS_FIRST and
++ *	    QUIRK_MSB_ON_THE_RIGHT.
++ *
++ * Return: 0 on success, EINVAL or ERANGE if called incorrectly. Assuming
++ *	   correct usage, return code may be discarded.
++ *	   If op is PACK, pbuf is modified.
++ *	   If op is UNPACK, uval is modified.
++ */
++int packing(void *pbuf, u64 *uval, int startbit, int endbit, size_t pbuflen,
++	    enum packing_op op, u8 quirks);
++
++#endif
+diff --git a/include/net/dsa.h b/include/net/dsa.h
+new file mode 100644
+index 0000000000..be95cc68ea
+--- /dev/null
++++ b/include/net/dsa.h
+@@ -0,0 +1,141 @@
++/* SPDX-License-Identifier: GPL-2.0+ */
++/*
++ * Copyright 2019 NXP
++ */
++
++#ifndef __DSA_H__
++#define __DSA_H__
++
++#include <common.h>
++#include <dm.h>
++#include <phy.h>
++
++/**
++ * DSA stands for Distributed Switch Architecture and it is infrastructure
++ * intended to support drivers for Switches that rely on an intermediary
++ * Ethernet device for I/O.  These switches may support cascading allowing
++ * them to be arranged as a tree.
++ * DSA is documented in detail in the Linux kernel documentation under
++ * Documentation/networking/dsa/dsa.txt
++ * The network layout of such a switch is shown below:
++ *
++ *	    |---------------------------
++ *	    | CPU network device (eth0)|
++ *	    ----------------------------
++ *	    | <tag added by switch     |
++ *	    |                          |
++ *	    |                          |
++ *	    |        tag added by CPU> |
++ *	|--------------------------------------------|
++ *	| Switch driver				     |
++ *	|--------------------------------------------|
++ *	    ||        ||         ||
++ *	|-------|  |-------|  |-------|
++ *	| sw0p0 |  | sw0p1 |  | sw0p2 |
++ *	|-------|  |-------|  |-------|
++ *
++ * In U-Boot the intent is to allow access to front panel ports (shown at the
++ * bottom of the picture) though the master Ethernet port (eth0 in the picture).
++ * Front panel ports are presented as regular Ethernet devices in U-Boot and
++ * they are expected to support the typical networking commands.
++ * In general DSA switches require the use of tags, extra headers added both by
++ * software on Tx and by the switch on Rx.  These tags carry at a minimum port
++ * information and switch information for cascaded set-ups.
++ * In U-Boot these tags are inserted and parsed by the DSA switch driver, the
++ * class code helps with headroom/tailroom for the extra headers.
++ *
++ * TODO:
++ * - handle switch cascading, for now U-Boot only supports stand-alone switches.
++ * - propagate the master Eth MAC address to switch ports, this is used in
++ * Linux to avoid using additional MAC addresses on master Eth.
++ * - Add support to probe DSA switches connected to a MDIO bus, this is needed
++ * to convert switch drivers that are now under drivers/net/phy.
++ */
++
++#define DSA_PORT_NAME_LENGTH	16
++
++/* Maximum number of ports each DSA device can have */
++#define DSA_MAX_PORTS		12
++/* Used to size internal buffers, no support for jumbo yet */
++#define DSA_MAX_FRAME_SIZE	2048
++
++/**
++ * struct dsa_ops - DSA operations
++ *
++ * @port_enable:  Initialize a switch port for I/O
++ * @port_disable: Disable a port
++ * @xmit:         Insert the DSA tag for transmission
++ *                DSA drivers receive a copy of the packet with headroom and
++ *                tailroom reserved and set to 0.
++ *                Packet points to headroom and length is updated to include
++ *                both headroom and tailroom
++ * @rcv:          Process the DSA tag on reception
++ *                Packet and length describe the frame as received from master
++ *                including any additional headers
++ */
++struct dsa_ops {
++	int (*port_enable)(struct udevice *dev, int port,
++			   struct phy_device *phy);
++	void (*port_disable)(struct udevice *dev, int port,
++			     struct phy_device *phy);
++	int (*xmit)(struct udevice *dev, int port, void *packet, int length);
++	int (*rcv)(struct udevice *dev, int *port, void *packet, int length);
++};
++
++#define dsa_get_ops(dev) ((struct dsa_ops *)(dev)->driver->ops)
++
++/**
++ * struct dsa_port_platdata - DSA port platform data
++ *
++ * @dev :  Port u-device
++ *         Uclass code sets this field for all ports
++ * @phy:   PHY device associated with this port
++ *         Uclass code sets this field for all ports except CPU port, based on
++ *         DT information.  It may be NULL.
++ * @node:  Port DT node, if any.  Uclass code sets this field.
++ * @index: Port index in the DSA switch, set by class code.
++ * @name:  Name of the port Eth device.  If a label property is present in the
++ *         port DT node, it is used as name.  Drivers can use custom names by
++ *         populating this field, otherwise class code generates a default.
++ */
++struct dsa_port_platdata {
++	struct udevice *dev;
++	struct phy_device *phy;
++	ofnode node;
++	int index;
++	char name[DSA_PORT_NAME_LENGTH];
++};
++
++/**
++ * struct dsa_perdev_platdata - Per-device platform data for DSA DM
++ *
++ * @num_ports:   Number of ports the device has, must be <= DSA_MAX_PORTS
++ *               All DSA drivers must set this at _bind
++ * @headroom:    Size, in bytes, of headroom needed for the DSA tag
++ *               All DSA drivers must set this at _bind or _probe
++ * @tailroom:    Size, in bytes, of tailroom needed for the DSA tag
++ *               DSA class code allocates headroom and tailroom on Tx before
++ *               calling DSA driver xmit function
++ *               All DSA drivers must set this at _bind or _probe
++ * @master_node: DT node of the master Ethernet.  DT is optional so this may be
++ *               null.
++ * @master_dev:  Ethernet device to be used as master.  Uclass code sets this
++ *               based on DT information if present, otherwise drivers must set
++ *               this field in _probe.
++ * @cpu_port:    Index of switch port linked to master Ethernet.
++ *               Uclass code sets this based on DT information if present,
++ *               otherwise drivers must set this field in _bind.
++ * @port:        per-port data
++ */
++struct dsa_perdev_platdata {
++	int num_ports;
++	int headroom;
++	int tailroom;
++
++	ofnode master_node;
++	struct udevice *master_dev;
++	int cpu_port;
++	struct dsa_port_platdata port[DSA_MAX_PORTS];
++};
++
++#endif /* __DSA_H__ */
+diff --git a/lib/Kconfig b/lib/Kconfig
+index 965cf7bc03..a828b80a40 100644
+--- a/lib/Kconfig
++++ b/lib/Kconfig
+@@ -33,6 +33,23 @@ config HAVE_PRIVATE_LIBGCC
+ config LIB_UUID
+ 	bool
+ 
++config PACKING
++	bool "Generic bitfield packing and unpacking"
++	default n
++	help
++	  This option provides the packing() helper function, which permits
++	  converting bitfields between a CPU-usable representation and a
++	  memory representation that can have any combination of these quirks:
++	    - Is little endian (bytes are reversed within a 32-bit group)
++	    - The least-significant 32-bit word comes first (within a 64-bit
++	      group)
++	    - The most significant bit of a byte is at its right (bit 0 of a
++	      register description is numerically 2^7).
++	  Drivers may use these helpers to match the bit indices as described
++	  in the data sheets of the peripherals they are in control of.
++
++	  When in doubt, say N.
++
+ config PRINTF
+ 	bool
+ 	default y
+diff --git a/lib/Makefile b/lib/Makefile
+index 1fb650cd90..4f99bd47c2 100644
+--- a/lib/Makefile
++++ b/lib/Makefile
+@@ -104,6 +104,7 @@ obj-y += hexdump.o
+ obj-$(CONFIG_TRACE) += trace.o
+ obj-$(CONFIG_LIB_UUID) += uuid.o
+ obj-$(CONFIG_LIB_RAND) += rand.o
++obj-$(CONFIG_PACKING) += packing.o
+ obj-y += panic.o
+ 
+ ifeq ($(CONFIG_$(SPL_TPL_)BUILD),y)
+diff --git a/lib/packing.c b/lib/packing.c
+new file mode 100644
+index 0000000000..21e0fab20d
+--- /dev/null
++++ b/lib/packing.c
+@@ -0,0 +1,208 @@
++// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
++/*
++ * Copyright 2016-2018,2020 NXP
++ */
++#include <linux/packing.h>
++#include <linux/bitops.h>
++#include <linux/errno.h>
++#include <linux/types.h>
++
++static int get_le_offset(int offset)
++{
++	int closest_multiple_of_4;
++
++	closest_multiple_of_4 = (offset / 4) * 4;
++	offset -= closest_multiple_of_4;
++	return closest_multiple_of_4 + (3 - offset);
++}
++
++static int get_reverse_lsw32_offset(int offset, size_t len)
++{
++	int closest_multiple_of_4;
++	int word_index;
++
++	word_index = offset / 4;
++	closest_multiple_of_4 = word_index * 4;
++	offset -= closest_multiple_of_4;
++	word_index = (len / 4) - word_index - 1;
++	return word_index * 4 + offset;
++}
++
++static u64 bit_reverse(u64 val, unsigned int width)
++{
++	u64 new_val = 0;
++	unsigned int bit;
++	unsigned int i;
++
++	for (i = 0; i < width; i++) {
++		bit = (val & (1 << i)) != 0;
++		new_val |= (bit << (width - i - 1));
++	}
++	return new_val;
++}
++
++static void adjust_for_msb_right_quirk(u64 *to_write, int *box_start_bit,
++				       int *box_end_bit, u8 *box_mask)
++{
++	int box_bit_width = *box_start_bit - *box_end_bit + 1;
++	int new_box_start_bit, new_box_end_bit;
++
++	*to_write >>= *box_end_bit;
++	*to_write = bit_reverse(*to_write, box_bit_width);
++	*to_write <<= *box_end_bit;
++
++	new_box_end_bit   = box_bit_width - *box_start_bit - 1;
++	new_box_start_bit = box_bit_width - *box_end_bit - 1;
++	*box_mask = GENMASK_ULL(new_box_start_bit, new_box_end_bit);
++	*box_start_bit = new_box_start_bit;
++	*box_end_bit   = new_box_end_bit;
++}
++
++/**
++ * packing - Convert numbers (currently u64) between a packed and an unpacked
++ *	     format. Unpacked means laid out in memory in the CPU's native
++ *	     understanding of integers, while packed means anything else that
++ *	     requires translation.
++ *
++ * @pbuf: Pointer to a buffer holding the packed value.
++ * @uval: Pointer to an u64 holding the unpacked value.
++ * @startbit: The index (in logical notation, compensated for quirks) where
++ *	      the packed value starts within pbuf. Must be larger than, or
++ *	      equal to, endbit.
++ * @endbit: The index (in logical notation, compensated for quirks) where
++ *	    the packed value ends within pbuf. Must be smaller than, or equal
++ *	    to, startbit.
++ * @op: If PACK, then uval will be treated as const pointer and copied (packed)
++ *	into pbuf, between startbit and endbit.
++ *	If UNPACK, then pbuf will be treated as const pointer and the logical
++ *	value between startbit and endbit will be copied (unpacked) to uval.
++ * @quirks: A bit mask of QUIRK_LITTLE_ENDIAN, QUIRK_LSW32_IS_FIRST and
++ *	    QUIRK_MSB_ON_THE_RIGHT.
++ *
++ * Return: 0 on success, EINVAL or ERANGE if called incorrectly. Assuming
++ *	   correct usage, return code may be discarded.
++ *	   If op is PACK, pbuf is modified.
++ *	   If op is UNPACK, uval is modified.
++ */
++int packing(void *pbuf, u64 *uval, int startbit, int endbit, size_t pbuflen,
++	    enum packing_op op, u8 quirks)
++{
++	/* Number of bits for storing "uval"
++	 * also width of the field to access in the pbuf
++	 */
++	u64 value_width;
++	/* Logical byte indices corresponding to the
++	 * start and end of the field.
++	 */
++	int plogical_first_u8, plogical_last_u8, box;
++
++	/* startbit is expected to be larger than endbit */
++	if (startbit < endbit)
++		/* Invalid function call */
++		return -EINVAL;
++
++	value_width = startbit - endbit + 1;
++	if (value_width > 64)
++		return -ERANGE;
++
++	/* Check if "uval" fits in "value_width" bits.
++	 * If value_width is 64, the check will fail, but any
++	 * 64-bit uval will surely fit.
++	 */
++	if (op == PACK && value_width < 64 && (*uval >= (1ull << value_width)))
++		/* Cannot store "uval" inside "value_width" bits.
++		 * Truncating "uval" is most certainly not desirable,
++		 * so simply erroring out is appropriate.
++		 */
++		return -ERANGE;
++
++	/* Initialize parameter */
++	if (op == UNPACK)
++		*uval = 0;
++
++	/* Iterate through an idealistic view of the pbuf as an u64 with
++	 * no quirks, u8 by u8 (aligned at u8 boundaries), from high to low
++	 * logical bit significance. "box" denotes the current logical u8.
++	 */
++	plogical_first_u8 = startbit / 8;
++	plogical_last_u8  = endbit / 8;
++
++	for (box = plogical_first_u8; box >= plogical_last_u8; box--) {
++		/* Bit indices into the currently accessed 8-bit box */
++		int box_start_bit, box_end_bit, box_addr;
++		u8  box_mask;
++		/* Corresponding bits from the unpacked u64 parameter */
++		int proj_start_bit, proj_end_bit;
++		u64 proj_mask;
++
++		/* This u8 may need to be accessed in its entirety
++		 * (from bit 7 to bit 0), or not, depending on the
++		 * input arguments startbit and endbit.
++		 */
++		if (box == plogical_first_u8)
++			box_start_bit = startbit % 8;
++		else
++			box_start_bit = 7;
++		if (box == plogical_last_u8)
++			box_end_bit = endbit % 8;
++		else
++			box_end_bit = 0;
++
++		/* We have determined the box bit start and end.
++		 * Now we calculate where this (masked) u8 box would fit
++		 * in the unpacked (CPU-readable) u64 - the u8 box's
++		 * projection onto the unpacked u64. Though the
++		 * box is u8, the projection is u64 because it may fall
++		 * anywhere within the unpacked u64.
++		 */
++		proj_start_bit = ((box * 8) + box_start_bit) - endbit;
++		proj_end_bit   = ((box * 8) + box_end_bit) - endbit;
++		proj_mask = GENMASK_ULL(proj_start_bit, proj_end_bit);
++		box_mask  = GENMASK_ULL(box_start_bit, box_end_bit);
++
++		/* Determine the offset of the u8 box inside the pbuf,
++		 * adjusted for quirks. The adjusted box_addr will be used for
++		 * effective addressing inside the pbuf (so it's not
++		 * logical any longer).
++		 */
++		box_addr = pbuflen - box - 1;
++		if (quirks & QUIRK_LITTLE_ENDIAN)
++			box_addr = get_le_offset(box_addr);
++		if (quirks & QUIRK_LSW32_IS_FIRST)
++			box_addr = get_reverse_lsw32_offset(box_addr,
++							    pbuflen);
++
++		if (op == UNPACK) {
++			u64 pval;
++
++			/* Read from pbuf, write to uval */
++			pval = ((u8 *)pbuf)[box_addr] & box_mask;
++			if (quirks & QUIRK_MSB_ON_THE_RIGHT)
++				adjust_for_msb_right_quirk(&pval,
++							   &box_start_bit,
++							   &box_end_bit,
++							   &box_mask);
++
++			pval >>= box_end_bit;
++			pval <<= proj_end_bit;
++			*uval &= ~proj_mask;
++			*uval |= pval;
++		} else {
++			u64 pval;
++
++			/* Write to pbuf, read from uval */
++			pval = (*uval) & proj_mask;
++			pval >>= proj_end_bit;
++			if (quirks & QUIRK_MSB_ON_THE_RIGHT)
++				adjust_for_msb_right_quirk(&pval,
++							   &box_start_bit,
++							   &box_end_bit,
++							   &box_mask);
++
++			pval <<= box_end_bit;
++			((u8 *)pbuf)[box_addr] &= ~box_mask;
++			((u8 *)pbuf)[box_addr] |= pval;
++		}
++	}
++	return 0;
++}
diff --git a/scalp_safe_petalinux/project-spec/meta-user/recipes-bsp/u-boot/files/u-boot-zynq-scalp-dts-sja1105-official.patch b/scalp_safe_petalinux/project-spec/meta-user/recipes-bsp/u-boot/files/u-boot-zynq-scalp-dts-sja1105-official.patch
new file mode 100644
index 0000000000000000000000000000000000000000..55fa9c3565640f1c170f0a7e15122e8e88e8a326
--- /dev/null
+++ b/scalp_safe_petalinux/project-spec/meta-user/recipes-bsp/u-boot/files/u-boot-zynq-scalp-dts-sja1105-official.patch
@@ -0,0 +1,293 @@
+diff --git a/arch/arm/dts/zynq-scalp.dts b/arch/arm/dts/zynq-scalp.dts
+new file mode 100644
+index 0000000000..fb7bebd40d
+--- /dev/null
++++ b/arch/arm/dts/zynq-scalp.dts
+@@ -0,0 +1,287 @@
++/*
++ * Scalp Board DTS
++ *
++ * Copyright (c) 2018-2021 Hepia
++ * Joachim Schmidt <joachim.schmidt@hesge.ch>
++ *
++ */
++
++/dts-v1/;
++#include "zynq-7000.dtsi"
++#include <dt-bindings/gpio/gpio.h>
++
++/ {
++    model = "Zynq Scalp Board";
++    compatible = "xlnx,zynq-scalp", "xlnx,zynq-7000";
++
++	aliases {
++        serial0   = &uart0;                      /* Bottom : MIO  46 - 47 (Switch on schematic) */
++		serial1   = &uart1;                      /* Top    : MIO  48 - 49                       */
++        ethernet0 = &gem0;                       /*          MIO  16 - 27                       */
++        /* Switch the name. QSPI first */
++		spi0      = &qspi;                       /*          MIO  1 - 6                        */
++        spi1      = &spi0;                       /*          MIO  40 - 41, 45                  */
++        spi2      = &spi1;                       /*          EMIO                              */
++		mmc0      = &sdhci1;                     /*          MIO  10 - 15, 9 (card detect)     */
++        usb0      = &usb0;                       /*          MIO  28 - 39                      */
++        i2c0      = &i2c0;                       /*          MIO  50 - 51                      */
++        can0      = &can1;                       /*          MIO  52 - 53                      */
++        //gpio0     = &gpio0;                    /*          MIO  0, 7, 8, 43, 44              */
++	};
++
++    /* 256 MB DDR3 */
++	memory@0 {
++		device_type = "memory";
++		reg = <0 0x10000000>;
++	};
++
++    chosen {
++           bootargs = "earlyprintk cpuidle.off=1 crashkernel=256M rw";
++           stdout-path = "serial0:115200n8";
++           xlnx,eeprom = &eeprom;
++    };
++};
++
++/* PS_Clk50M_i */
++&clkc {
++      fclk-enable = <0x1>;
++      ps-clk-frequency = <50000000>;
++};
++
++&intc {
++	num_cpus = <2>;
++	num_interrupts = <96>;
++};
++
++/* serial0 */
++&uart0 {
++	u-boot,dm-pre-reloc;
++    cts-override ;
++	device_type = "serial";
++	port-number = <0>;
++	status = "okay";
++};
++
++/* serial1 */
++&uart1 {
++	u-boot,dm-pre-reloc;
++    cts-override ;
++	device_type = "serial";
++	port-number = <1>;
++	status = "okay";
++};
++
++&qspi {
++    u-boot,dm-pre-reloc;
++    status = "okay";
++    is-dual = <0>;
++    num-cs = <1>;
++
++    /* Erase sector size of 64KB */
++    flash@0 {
++        /* compatible = "s25fl256s_64k"; drivers/mtd/spi/spi_flash_ids.c */
++        /* compatible = "spansion,s25fl256s1", "jedec,spi-nor"; */
++        /* compatible = "spansion,S25FL256S_64K", "spi-flash"; */
++        compatible = "spansion,s25fl256s", "jedec,spi-nor";
++        reg = <0x0>;
++        spi-tx-bus-width = <1>;
++        spi-rx-bus-width = <4>;
++        spi-max-frequency = <50000000>;
++        /* m25p,fast-read; */
++        #address-cells = <1>;
++        #size-cells = <1>;
++    };
++
++    //     /* FSBL + safe bitstream (design with only Zynq) + U-Boot + (devicetree) => BOOT.BIN */
++    //     /* Size : 5242880 Bytes */
++    //     partition@boot {
++    //         label = "boot";
++    //         reg = <0x0 0x500000>;
++    //     };
++
++    //     /* Safe U-Boot Env. */
++    //     /* Size : 131072 Bytes */
++    //     partition@bootenv {
++    //         label = "bootenv";
++    //         reg = <0x500000 0x20000>;
++    //     };
++
++    //     /* Safe Fit Image */
++    //     /* Size : 28049408 Bytes */
++    //     partition@fitimage {
++    //         label = "fitimage";
++    //         reg = <0x520000 0x1ac0000>;
++    //     };
++
++    //     /* Safe DTB */
++    //     /* Size : 131072 Bytes */
++    //     partition@dtb {
++    //         label = "dtb";
++    //         reg = <0x1ac0000 0x20000>;
++    //     };
++    // };
++};
++
++/* Switch Ethernet 5 ports - SPI0 - MIO 40, 41, 42, 45 */
++
++/*
++ * Port 0 => East
++ * Port 1 => Local
++ * Port 2 => Bottom
++ * Port 3 => Top
++ * Port 4 => West
++ */
++
++/*            (PYH + Delay)
++ *                Top 3
++ *
++ * 4 (MAC) West          East (PHY + Delay) 0
++ *
++ *               Bottom 2
++ *               (MAC)
++ *
++ */
++
++&spi0 {
++    bus-num = <0>;
++	status = "okay";
++
++    sja1105@0 {
++        reg = <0>;
++		#address-cells = <1>;
++		#size-cells = <0>;
++		compatible = "nxp,sja1105t";
++        reset-gpios = <&gpio0 44 GPIO_ACTIVE_LOW>;
++		spi-max-frequency = <12000000>;
++		spi-cpha;
++
++		ports {
++			#address-cells = <1>;
++			#size-cells = <0>;
++
++			swp0_east: port@0 {
++				label = "swp0_east";
++				phy-mode = "rgmii";
++				reg = <0>;
++                //sja1105,role-phy;
++
++                // fixed-link {
++				// 	speed = <10>;
++				// 	full-duplex;
++				// };
++			};
++
++			port@1 {
++				ethernet = <&gem0>;
++				phy-mode = "rgmii";
++				reg = <1>;
++                //sja1105,role-phy;
++
++				fixed-link {
++					speed = <10>;
++					full-duplex;
++				};
++			};
++
++			swp2_bottom: port@2 {
++				label = "swp2_bottom";
++				phy-mode = "rgmii";
++				reg = <2>;
++                //sja1105,role-mac;
++
++                // fixed-link {
++				// 	speed = <10>;
++				// 	full-duplex;
++				// };
++			};
++
++			swp3_top: port@3 {
++				label = "swp3_top";
++				phy-mode = "rgmii";
++				reg = <3>;
++                //sja1105,role-phy;
++
++                // fixed-link {
++				// 	speed = <10>;
++				// 	full-duplex;
++				// };
++			};
++
++            swp4_west: port@4 {
++				label = "swp4_west";
++				phy-mode = "rgmii";
++				reg = <4>;
++                //sja1105,role-mac;
++
++                // fixed-link {
++				// 	speed = <10>;
++				// 	full-duplex;
++				// };
++			};
++		};
++	};
++};
++
++/* ethernet0 */
++&gem0 {
++      status = "okay";
++      phy-mode = "rgmii"; /* "rgmii_id" */
++	  //phy-handle = <&ethernet_phy>;
++      xlnx,ptp-enet-clock = <0x7735940>;
++      //local-mac-address = [00 0a 35 00 00 00];
++
++      fixed-link {
++		  speed = <1000>;
++	      full-duplex;
++	  };
++
++	  //ethernet_phy: ethernet-phy@0 {
++      //    reg = <0>;
++	  //};
++};
++
++/* spi2 */
++&spi1 {
++    u-boot,dm-pre-reloc;
++    is-decoded-cs = <0>;
++	num-cs = <3>;
++	status = "okay";
++};
++
++&gpio0 {
++	emio-gpio-width = <64>;
++	gpio-mask-high = <0x0>;
++	gpio-mask-low = <0x5600>;
++};
++
++/* mmc0 */
++&sdhci1 {
++	u-boot,dm-pre-reloc;
++    xlnx,has-cd = <0x1>;
++	xlnx,has-power = <0x0>;
++	xlnx,has-wp = <0x0>;
++	status = "okay";
++};
++
++/* usb0 */
++/*&usb0 {
++    u-boot,dm-pre-reloc;
++    phy_type = "ulpi";
++	status = "okay";
++};*/
++
++/* i2c0 */
++&i2c0 {
++	status = "okay";
++
++    eeprom: eeprom@53 {
++        compatible = "microchip,24aa02e48";
++		reg = <0x53>;
++    };
++};
++
++/* can0 */
++&can1 {
++    u-boot,dm-pre-reloc;
++	status = "okay";
++};
diff --git a/scalp_safe_petalinux/project-spec/meta-user/recipes-bsp/u-boot/files/u-boot-zynq-scalp-dts-sja1105-static-config.patch b/scalp_safe_petalinux/project-spec/meta-user/recipes-bsp/u-boot/files/u-boot-zynq-scalp-dts-sja1105-static-config.patch
new file mode 100644
index 0000000000000000000000000000000000000000..06005896f81653d24ce5988153c813edec353e50
--- /dev/null
+++ b/scalp_safe_petalinux/project-spec/meta-user/recipes-bsp/u-boot/files/u-boot-zynq-scalp-dts-sja1105-static-config.patch
@@ -0,0 +1,209 @@
+diff --git a/arch/arm/dts/zynq-scalp.dts b/arch/arm/dts/zynq-scalp.dts
+new file mode 100644
+index 0000000000..cfe81d0a0c
+--- /dev/null
++++ b/arch/arm/dts/zynq-scalp.dts
+@@ -0,0 +1,203 @@
++/*
++ * Scalp Board DTS
++ *
++ * Copyright (c) 2018-2021 Hepia
++ * Joachim Schmidt <joachim.schmidt@hesge.ch>
++ *
++ */
++
++/dts-v1/;
++#include "zynq-7000.dtsi"
++#include <dt-bindings/gpio/gpio.h>
++
++/ {
++    model = "Zynq Scalp Board";
++    compatible = "xlnx,zynq-scalp", "xlnx,zynq-7000";
++
++	aliases {
++        serial0   = &uart0;                      /* Bottom : MIO  46 - 47 (Switch on schematic) */
++		serial1   = &uart1;                      /* Top    : MIO  48 - 49                       */
++        ethernet0 = &gem0;                       /*          MIO  16 - 27                       */
++        /* Switch the name. QSPI first */
++		spi0      = &qspi;                       /*          MIO  1 - 6                        */
++        spi1      = &spi0;                       /*          MIO  40 - 41, 45                  */
++        spi2      = &spi1;                       /*          EMIO                              */
++		mmc0      = &sdhci1;                     /*          MIO  10 - 15, 9 (card detect)     */
++        usb0      = &usb0;                       /*          MIO  28 - 39                      */
++        i2c0      = &i2c0;                       /*          MIO  50 - 51                      */
++        can0      = &can1;                       /*          MIO  52 - 53                      */
++        //gpio0     = &gpio0;                    /*          MIO  0, 7, 8, 43, 44              */
++	};
++
++    /* 256 MB DDR3 */
++	memory@0 {
++		device_type = "memory";
++		reg = <0 0x10000000>;
++	};
++
++    chosen {
++           bootargs = "earlyprintk cpuidle.off=1 crashkernel=256M rw";
++           stdout-path = "serial0:115200n8";
++           xlnx,eeprom = &eeprom;
++    };
++};
++
++/* PS_Clk50M_i */
++&clkc {
++      fclk-enable = <0x1>;
++      ps-clk-frequency = <50000000>;
++};
++
++&intc {
++	num_cpus = <2>;
++	num_interrupts = <96>;
++};
++
++/* serial0 */
++&uart0 {
++	u-boot,dm-pre-reloc;
++    cts-override ;
++	device_type = "serial";
++	port-number = <0>;
++	status = "okay";
++};
++
++/* serial1 */
++&uart1 {
++	u-boot,dm-pre-reloc;
++    cts-override ;
++	device_type = "serial";
++	port-number = <1>;
++	status = "okay";
++};
++
++&qspi {
++    u-boot,dm-pre-reloc;
++    status = "okay";
++    is-dual = <0>;
++    num-cs = <1>;
++
++    /* Erase sector size of 64KB */
++    flash@0 {
++        /* compatible = "s25fl256s_64k"; drivers/mtd/spi/spi_flash_ids.c */
++        /* compatible = "spansion,s25fl256s1", "jedec,spi-nor"; */
++        /* compatible = "spansion,S25FL256S_64K", "spi-flash"; */
++        compatible = "spansion,s25fl256s", "jedec,spi-nor";
++        reg = <0x0>;
++        spi-tx-bus-width = <1>;
++        spi-rx-bus-width = <4>;
++        spi-max-frequency = <50000000>;
++        /* m25p,fast-read; */
++        #address-cells = <1>;
++        #size-cells = <1>;
++    };
++
++    //     /* FSBL + safe bitstream (design with only Zynq) + U-Boot + (devicetree) => BOOT.BIN */
++    //     /* Size : 5242880 Bytes */
++    //     partition@boot {
++    //         label = "boot";
++    //         reg = <0x0 0x500000>;
++    //     };
++
++    //     /* Safe U-Boot Env. */
++    //     /* Size : 131072 Bytes */
++    //     partition@bootenv {
++    //         label = "bootenv";
++    //         reg = <0x500000 0x20000>;
++    //     };
++
++    //     /* Safe Fit Image */
++    //     /* Size : 28049408 Bytes */
++    //     partition@fitimage {
++    //         label = "fitimage";
++    //         reg = <0x520000 0x1ac0000>;
++    //     };
++
++    //     /* Safe DTB */
++    //     /* Size : 131072 Bytes */
++    //     partition@dtb {
++    //         label = "dtb";
++    //         reg = <0x1ac0000 0x20000>;
++    //     };
++    // };
++};
++
++&spi0 {
++    bus-num = <0>;
++    status = "okay";
++
++    /* SCALP specific SJA1105T driver */
++    sja1105@0 {
++        reg = <0>;
++        #address-cells = <1>;
++        #size-cells = <0>;
++        compatible = "scalp,sja1105t";
++        reset-gpios = <&gpio0 44 GPIO_ACTIVE_LOW>;
++        spi-max-frequency = <12000000>;
++        spi-cpha;
++    };
++};
++
++/* ethernet0 */
++&gem0 {
++      status = "okay";
++      phy-mode = "rgmii"; /* "rgmii_id" */
++	  //phy-handle = <&ethernet_phy>;
++      xlnx,ptp-enet-clock = <0x7735940>;
++      //local-mac-address = [00 0a 35 00 00 00];
++
++      fixed-link {
++		  speed = <1000>;
++	      full-duplex;
++	  };
++
++	  //ethernet_phy: ethernet-phy@0 {
++      //    reg = <0>;
++	  //};
++};
++
++/* spi2 */
++&spi1 {
++    u-boot,dm-pre-reloc;
++    is-decoded-cs = <0>;
++	num-cs = <3>;
++	status = "okay";
++};
++
++&gpio0 {
++	emio-gpio-width = <64>;
++	gpio-mask-high = <0x0>;
++	gpio-mask-low = <0x5600>;
++};
++
++/* mmc0 */
++&sdhci1 {
++	u-boot,dm-pre-reloc;
++    xlnx,has-cd = <0x1>;
++	xlnx,has-power = <0x0>;
++	xlnx,has-wp = <0x0>;
++	status = "okay";
++};
++
++/* usb0 */
++/*&usb0 {
++    u-boot,dm-pre-reloc;
++    phy_type = "ulpi";
++	status = "okay";
++};*/
++
++/* i2c0 */
++&i2c0 {
++	status = "okay";
++
++    eeprom: eeprom@53 {
++        compatible = "microchip,24aa02e48";
++		reg = <0x53>;
++    };
++};
++
++/* can0 */
++&can1 {
++    u-boot,dm-pre-reloc;
++	status = "okay";
++};
diff --git a/scalp_safe_petalinux/project-spec/meta-user/recipes-bsp/u-boot/files/u-boot-zynq-scalp-dts.patch b/scalp_safe_petalinux/project-spec/meta-user/recipes-bsp/u-boot/files/u-boot-zynq-scalp-dts.patch
index 9b0b03593d6411db7ff686d4c419b23aa9eed76a..c3116348e5eadf0c267177afb1731f01d6f5ae9a 100644
--- a/scalp_safe_petalinux/project-spec/meta-user/recipes-bsp/u-boot/files/u-boot-zynq-scalp-dts.patch
+++ b/scalp_safe_petalinux/project-spec/meta-user/recipes-bsp/u-boot/files/u-boot-zynq-scalp-dts.patch
@@ -11,7 +11,7 @@ index 0000000000..201cd16922
 + * Joachim Schmidt <joachim.schmidt@hesge.ch>
 + *
 + */
-+ 
++
 +/dts-v1/;
 +#include "zynq-7000.dtsi"
 +#include <dt-bindings/gpio/gpio.h>
@@ -20,7 +20,7 @@ index 0000000000..201cd16922
 +    model = "Zynq Scalp Board";
 +    compatible = "xlnx,zynq-scalp", "xlnx,zynq-7000";
 +
-+	aliases {        
++	aliases {
 +        serial0   = &uart0;                      /* Bottom : MIO  46 - 47 (Switch on schematic) */
 +		serial1   = &uart1;                      /* Top    : MIO  48 - 49                       */
 +        ethernet0 = &gem0;                       /*          MIO  16 - 27                       */
@@ -96,31 +96,31 @@ index 0000000000..201cd16922
 +        /* m25p,fast-read; */
 +        #address-cells = <1>;
 +        #size-cells = <1>;
-+        
++
 +        /* FSBL + safe bitstream (design with only Zynq) + U-Boot + (devicetree) => BOOT.BIN */
 +        /* Size : 5242880 Bytes */
-+        partition@boot {       
++        partition@boot {
 +            label = "boot";
 +            reg = <0x0 0x500000>;
 +        };
-+        
++
 +        /* Safe U-Boot Env. */
 +        /* Size : 131072 Bytes */
-+        partition@bootenv {       
++        partition@bootenv {
 +            label = "bootenv";
 +            reg = <0x500000 0x20000>;
 +        };
-+        
++
 +        /* Safe Fit Image */
 +        /* Size : 28049408 Bytes */
-+        partition@fitimage {            
++        partition@fitimage {
 +            label = "fitimage";
 +            reg = <0x520000 0x1ac0000>;
 +        };
 +
 +        /* Safe DTB */
 +        /* Size : 131072 Bytes */
-+        partition@dtb {            
++        partition@dtb {
 +            label = "dtb";
 +            reg = <0x1ac0000 0x20000>;
 +        };
@@ -141,7 +141,7 @@ index 0000000000..201cd16922
 +        spi-max-frequency = <12000000>;
 +        spi-cpha;
 +    };
-+}; 
++};
 +
 +/* spi2 */
 +&spi1 {
diff --git a/scalp_safe_petalinux/project-spec/meta-user/recipes-kernel/linux/linux-xlnx/scalp-linux-mtdcore-config-name.patch b/scalp_safe_petalinux/project-spec/meta-user/recipes-kernel/linux/linux-xlnx/scalp-linux-mtdcore-config-name.patch
new file mode 100644
index 0000000000000000000000000000000000000000..3d7ed62405d647f26d327f0d5b8709cecf0487c1
--- /dev/null
+++ b/scalp_safe_petalinux/project-spec/meta-user/recipes-kernel/linux/linux-xlnx/scalp-linux-mtdcore-config-name.patch
@@ -0,0 +1,15 @@
+diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c
+index 6cc7ecb0c788..1867f1a41d82 100644
+--- a/drivers/mtd/mtdcore.c
++++ b/drivers/mtd/mtdcore.c
+@@ -563,7 +563,9 @@ static int mtd_nvmem_add(struct mtd_info *mtd)
+ 
+ 	config.id = -1;
+ 	config.dev = &mtd->dev;
+-	config.name = mtd->name;
++    /* MODIF JOACHIM */
++	config.name = dev_name(&mtd->dev);
++    /* FIN MODIF JOACHIM */
+ 	config.owner = THIS_MODULE;
+ 	config.reg_read = mtd_nvmem_reg_read;
+ 	config.size = mtd->size;