aboutsummaryrefslogtreecommitdiffstats
path: root/roms/u-boot/drivers/net/phy
diff options
context:
space:
mode:
Diffstat (limited to 'roms/u-boot/drivers/net/phy')
-rw-r--r--roms/u-boot/drivers/net/phy/Kconfig315
-rw-r--r--roms/u-boot/drivers/net/phy/Makefile36
-rw-r--r--roms/u-boot/drivers/net/phy/aquantia.c738
-rw-r--r--roms/u-boot/drivers/net/phy/atheros.c375
-rw-r--r--roms/u-boot/drivers/net/phy/b53.c770
-rw-r--r--roms/u-boot/drivers/net/phy/broadcom.c374
-rw-r--r--roms/u-boot/drivers/net/phy/ca_phy.c133
-rw-r--r--roms/u-boot/drivers/net/phy/cortina.c393
-rw-r--r--roms/u-boot/drivers/net/phy/davicom.c87
-rw-r--r--roms/u-boot/drivers/net/phy/dp83867.c438
-rw-r--r--roms/u-boot/drivers/net/phy/et1011c.c104
-rw-r--r--roms/u-boot/drivers/net/phy/fixed.c111
-rw-r--r--roms/u-boot/drivers/net/phy/generic_10g.c91
-rw-r--r--roms/u-boot/drivers/net/phy/lxt.c76
-rw-r--r--roms/u-boot/drivers/net/phy/marvell.c709
-rw-r--r--roms/u-boot/drivers/net/phy/meson-gxl.c142
-rw-r--r--roms/u-boot/drivers/net/phy/micrel_ksz8xxx.c234
-rw-r--r--roms/u-boot/drivers/net/phy/micrel_ksz90x1.c525
-rw-r--r--roms/u-boot/drivers/net/phy/miiphybb.c357
-rw-r--r--roms/u-boot/drivers/net/phy/mscc.c1586
-rw-r--r--roms/u-boot/drivers/net/phy/mv88e61xx.c1223
-rw-r--r--roms/u-boot/drivers/net/phy/mv88e6352.c304
-rw-r--r--roms/u-boot/drivers/net/phy/natsemi.c166
-rw-r--r--roms/u-boot/drivers/net/phy/ncsi.c898
-rw-r--r--roms/u-boot/drivers/net/phy/phy.c1104
-rw-r--r--roms/u-boot/drivers/net/phy/realtek.c464
-rw-r--r--roms/u-boot/drivers/net/phy/smsc.c116
-rw-r--r--roms/u-boot/drivers/net/phy/teranetics.c111
-rw-r--r--roms/u-boot/drivers/net/phy/ti_phy_init.c101
-rw-r--r--roms/u-boot/drivers/net/phy/ti_phy_init.h15
-rw-r--r--roms/u-boot/drivers/net/phy/vitesse.c444
-rw-r--r--roms/u-boot/drivers/net/phy/xilinx_gmii2rgmii.c144
-rw-r--r--roms/u-boot/drivers/net/phy/xilinx_phy.c146
33 files changed, 12830 insertions, 0 deletions
diff --git a/roms/u-boot/drivers/net/phy/Kconfig b/roms/u-boot/drivers/net/phy/Kconfig
new file mode 100644
index 000000000..070ffa82c
--- /dev/null
+++ b/roms/u-boot/drivers/net/phy/Kconfig
@@ -0,0 +1,315 @@
+
+config BITBANGMII
+ bool "Bit-banged ethernet MII management channel support"
+
+config MV88E6352_SWITCH
+ bool "Marvell 88E6352 switch support"
+
+menuconfig PHYLIB
+ bool "Ethernet PHY (physical media interface) support"
+ depends on NET
+ help
+ Enable Ethernet PHY (physical media interface) support.
+
+if PHYLIB
+
+config PHY_ADDR_ENABLE
+ bool "Limit phy address"
+ default y if ARCH_SUNXI
+ help
+ Select this if you want to control which phy address is used
+
+if PHY_ADDR_ENABLE
+config PHY_ADDR
+ int "PHY address"
+ default 1 if ARCH_SUNXI
+ default 0
+ help
+ The address of PHY on MII bus. Usually in range of 0 to 31.
+endif
+
+config B53_SWITCH
+ bool "Broadcom BCM53xx (RoboSwitch) Ethernet switch PHY support."
+ help
+ Enable support for Broadcom BCM53xx (RoboSwitch) Ethernet switches.
+ This currently supports BCM53125 and similar models.
+
+if B53_SWITCH
+
+config B53_CPU_PORT
+ int "CPU port"
+ default 8
+
+config B53_PHY_PORTS
+ hex "Bitmask of PHY ports"
+
+endif # B53_SWITCH
+
+config MV88E61XX_SWITCH
+ bool "Marvell MV88E61xx Ethernet switch PHY support."
+
+if MV88E61XX_SWITCH
+
+config MV88E61XX_CPU_PORT
+ int "CPU Port"
+
+config MV88E61XX_PHY_PORTS
+ hex "Bitmask of PHY Ports"
+
+config MV88E61XX_FIXED_PORTS
+ hex "Bitmask of PHYless serdes Ports"
+
+endif # MV88E61XX_SWITCH
+
+config PHYLIB_10G
+ bool "Generic 10G PHY support"
+
+menuconfig PHY_AQUANTIA
+ bool "Aquantia Ethernet PHYs support"
+ select PHY_GIGE
+ select PHYLIB_10G
+
+config PHY_AQUANTIA_UPLOAD_FW
+ bool "Aquantia firmware loading support"
+ default n
+ depends on PHY_AQUANTIA
+ help
+ Aquantia PHYs use firmware which can be either loaded automatically
+ from storage directly attached to the phy or loaded by the boot loader
+ via MDIO commands. The firmware is loaded from a file, specified by
+ the PHY_AQUANTIA_FW_PART and PHY_AQUANTIA_FW_NAME options.
+
+config PHY_AQUANTIA_FW_PART
+ string "Aquantia firmware partition"
+ depends on PHY_AQUANTIA_UPLOAD_FW
+ help
+ Partition containing the firmware file.
+
+config PHY_AQUANTIA_FW_NAME
+ string "Aquantia firmware filename"
+ depends on PHY_AQUANTIA_UPLOAD_FW
+ help
+ Firmware filename.
+
+config PHY_ATHEROS
+ bool "Atheros Ethernet PHYs support"
+
+config PHY_BROADCOM
+ bool "Broadcom Ethernet PHYs support"
+
+config PHY_CORTINA
+ bool "Cortina Ethernet PHYs support"
+
+config SYS_CORTINA_NO_FW_UPLOAD
+ bool "Cortina firmware loading support"
+ default n
+ depends on PHY_CORTINA
+ help
+ Cortina phy has provision to store phy firmware in attached dedicated
+ EEPROM. And boards designed with such EEPROM does not require firmware
+ upload.
+
+choice
+ prompt "Location of the Cortina firmware"
+ default SYS_CORTINA_FW_IN_NOR
+ depends on PHY_CORTINA
+
+config SYS_CORTINA_FW_IN_MMC
+ bool "Cortina firmware in MMC"
+
+config SYS_CORTINA_FW_IN_NAND
+ bool "Cortina firmware in NAND flash"
+
+config SYS_CORTINA_FW_IN_NOR
+ bool "Cortina firmware in NOR flash"
+
+config SYS_CORTINA_FW_IN_REMOTE
+ bool "Cortina firmware in remote device"
+
+config SYS_CORTINA_FW_IN_SPIFLASH
+ bool "Cortina firmware in SPI flash"
+
+endchoice
+
+config PHY_CORTINA_ACCESS
+ bool "Cortina Access Ethernet PHYs support"
+ default y
+ depends on CORTINA_NI_ENET
+ help
+ Cortina Access Ethernet PHYs init process
+
+config PHY_DAVICOM
+ bool "Davicom Ethernet PHYs support"
+
+config PHY_ET1011C
+ bool "LSI TruePHY ET1011C support"
+
+config PHY_LXT
+ bool "LXT971 Ethernet PHY support"
+
+config PHY_MARVELL
+ bool "Marvell Ethernet PHYs support"
+
+config PHY_MESON_GXL
+ bool "Amlogic Meson GXL Internal PHY support"
+
+config PHY_MICREL
+ bool "Micrel Ethernet PHYs support"
+ help
+ Enable support for the GbE PHYs manufactured by Micrel (now
+ a part of Microchip). This includes drivers for the KSZ804, KSZ8031,
+ KSZ8051, KSZ8081, KSZ8895, KSZ886x and KSZ8721 (if "Micrel KSZ8xxx
+ family support" is selected) and the KSZ9021 and KSZ9031 (if "Micrel
+ KSZ90x1 family support" is selected).
+
+if PHY_MICREL
+
+config PHY_MICREL_KSZ9021
+ bool
+ select PHY_MICREL_KSZ90X1
+
+config PHY_MICREL_KSZ9031
+ bool
+ select PHY_MICREL_KSZ90X1
+
+config PHY_MICREL_KSZ90X1
+ bool "Micrel KSZ90x1 family support"
+ select PHY_GIGE
+ help
+ Enable support for the Micrel KSZ9021 and KSZ9031 GbE PHYs. If
+ enabled, the extended register read/write for KSZ90x1 PHYs
+ is supported through the 'mdio' command and any RGMII signal
+ delays configured in the device tree will be applied to the
+ PHY during initialization.
+
+config PHY_MICREL_KSZ8XXX
+ bool "Micrel KSZ8xxx family support"
+ help
+ Enable support for the 8000 series 10/100 PHYs manufactured by Micrel
+ (now a part of Microchip). This includes drivers for the KSZ804,
+ KSZ8031, KSZ8051, KSZ8081, KSZ8895, KSZ886x, and KSZ8721.
+
+endif # PHY_MICREL
+
+config PHY_MSCC
+ bool "Microsemi Corp Ethernet PHYs support"
+
+config PHY_NATSEMI
+ bool "National Semiconductor Ethernet PHYs support"
+
+config PHY_REALTEK
+ bool "Realtek Ethernet PHYs support"
+
+config RTL8211E_PINE64_GIGABIT_FIX
+ bool "Fix gigabit throughput on some Pine64+ models"
+ depends on PHY_REALTEK
+ help
+ Configure the Realtek RTL8211E found on some Pine64+ models differently to
+ fix throughput on Gigabit links, turning off all internal delays in the
+ process. The settings that this touches are not documented in the CONFREG
+ section of the RTL8211E datasheet, but come from Realtek by way of the
+ Pine64 engineering team.
+
+config RTL8211X_PHY_FORCE_MASTER
+ bool "Ethernet PHY RTL8211x: force 1000BASE-T master mode"
+ depends on PHY_REALTEK
+ help
+ Force master mode for 1000BASE-T on RTl8211x PHYs (except for RTL8211F).
+ This can work around link stability and data corruption issues on gigabit
+ links which can occur in slave mode on certain PHYs, e.g. on the
+ RTL8211C(L).
+
+ Please note that two directly connected devices (i.e. via crossover cable)
+ will not be able to establish a link between each other if they both force
+ master mode. Multiple devices forcing master mode when connected by a
+ network switch do not pose a problem as the switch configures its affected
+ ports into slave mode.
+
+ This option only affects gigabit links. If you must establish a direct
+ connection between two devices which both force master mode, try forcing
+ the link speed to 100MBit/s.
+
+ If unsure, say N.
+
+config RTL8211F_PHY_FORCE_EEE_RXC_ON
+ bool "Ethernet PHY RTL8211F: do not stop receiving the xMII clock during LPI"
+ depends on PHY_REALTEK
+ default n
+ help
+ The IEEE 802.3az-2010 (EEE) standard provides a protocol to coordinate
+ transitions to/from a lower power consumption level (Low Power Idle
+ mode) based on link utilization. When no packets are being
+ transmitted, the system goes to Low Power Idle mode to save power.
+
+ Under particular circumstances this setting can cause issues where
+ the PHY is unable to transmit or receive any packet when in LPI mode.
+ The problem is caused when the PHY is configured to stop receiving
+ the xMII clock while it is signaling LPI. For some PHYs the bit
+ configuring this behavior is set by the Linux kernel, causing the
+ issue in U-Boot on reboot if the PHY retains the register value.
+
+ Default n, which means that the PHY state is not changed. To work
+ around the issues, change this setting to y.
+
+config RTL8201F_PHY_S700_RMII_TIMINGS
+ bool "Ethernet PHY RTL8201F: adjust RMII Tx Interface timings"
+ depends on PHY_REALTEK
+ help
+ This provides an option to configure specific timing requirements (needed
+ for proper PHY operations) for the PHY module present on ACTION SEMI S700
+ based cubieboard7. Exact timing requiremnets seems to be SoC specific
+ (and it's undocumented) that comes from vendor code itself.
+
+config PHY_SMSC
+ bool "Microchip(SMSC) Ethernet PHYs support"
+
+config PHY_TERANETICS
+ bool "Teranetics Ethernet PHYs support"
+
+config PHY_TI
+ bool "Texas Instruments Ethernet PHYs support"
+ ---help---
+ Adds PHY registration support for TI PHYs.
+
+config PHY_TI_DP83867
+ select PHY_TI
+ bool "Texas Instruments Ethernet DP83867 PHY support"
+ ---help---
+ Adds support for the TI DP83867 1Gbit PHY.
+
+config PHY_TI_GENERIC
+ select PHY_TI
+ bool "Texas Instruments Generic Ethernet PHYs support"
+ ---help---
+ Adds support for Generic TI PHYs that don't need special handling but
+ the PHY name is associated with a PHY ID.
+
+config PHY_VITESSE
+ bool "Vitesse Ethernet PHYs support"
+
+config PHY_XILINX
+ bool "Xilinx Ethernet PHYs support"
+
+config PHY_XILINX_GMII2RGMII
+ bool "Xilinx GMII to RGMII Ethernet PHYs support"
+ depends on DM_ETH
+ help
+ This adds support for Xilinx GMII to RGMII IP core. This IP acts
+ as bridge between MAC connected over GMII and external phy that
+ is connected over RGMII interface.
+
+config PHY_FIXED
+ bool "Fixed-Link PHY"
+ depends on DM_ETH
+ help
+ Fixed PHY is used for having a 'fixed-link' to another MAC with a direct
+ connection (MII, RGMII, ...).
+ There is nothing like autoneogation and so
+ on, the link is always up with fixed speed and fixed duplex-setting.
+ More information: doc/device-tree-bindings/net/fixed-link.txt
+
+config PHY_NCSI
+ bool "NC-SI based PHY"
+ depends on DM_ETH
+
+endif #PHYLIB
diff --git a/roms/u-boot/drivers/net/phy/Makefile b/roms/u-boot/drivers/net/phy/Makefile
new file mode 100644
index 000000000..e967f8220
--- /dev/null
+++ b/roms/u-boot/drivers/net/phy/Makefile
@@ -0,0 +1,36 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# (C) Copyright 2008
+# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+
+obj-$(CONFIG_BITBANGMII) += miiphybb.o
+obj-$(CONFIG_B53_SWITCH) += b53.o
+obj-$(CONFIG_MV88E61XX_SWITCH) += mv88e61xx.o
+obj-$(CONFIG_MV88E6352_SWITCH) += mv88e6352.o
+
+obj-$(CONFIG_PHYLIB) += phy.o
+obj-$(CONFIG_PHYLIB_10G) += generic_10g.o
+obj-$(CONFIG_PHY_AQUANTIA) += aquantia.o
+obj-$(CONFIG_PHY_ATHEROS) += atheros.o
+obj-$(CONFIG_PHY_BROADCOM) += broadcom.o
+obj-$(CONFIG_PHY_CORTINA) += cortina.o
+obj-$(CONFIG_PHY_CORTINA_ACCESS) += ca_phy.o
+obj-$(CONFIG_PHY_DAVICOM) += davicom.o
+obj-$(CONFIG_PHY_ET1011C) += et1011c.o
+obj-$(CONFIG_PHY_LXT) += lxt.o
+obj-$(CONFIG_PHY_MARVELL) += marvell.o
+obj-$(CONFIG_PHY_MICREL_KSZ8XXX) += micrel_ksz8xxx.o
+obj-$(CONFIG_PHY_MICREL_KSZ90X1) += micrel_ksz90x1.o
+obj-$(CONFIG_PHY_MESON_GXL) += meson-gxl.o
+obj-$(CONFIG_PHY_NATSEMI) += natsemi.o
+obj-$(CONFIG_PHY_REALTEK) += realtek.o
+obj-$(CONFIG_PHY_SMSC) += smsc.o
+obj-$(CONFIG_PHY_TERANETICS) += teranetics.o
+obj-$(CONFIG_PHY_TI) += ti_phy_init.o
+obj-$(CONFIG_PHY_TI_DP83867) += dp83867.o
+obj-$(CONFIG_PHY_XILINX) += xilinx_phy.o
+obj-$(CONFIG_PHY_XILINX_GMII2RGMII) += xilinx_gmii2rgmii.o
+obj-$(CONFIG_PHY_VITESSE) += vitesse.o
+obj-$(CONFIG_PHY_MSCC) += mscc.o
+obj-$(CONFIG_PHY_FIXED) += fixed.o
+obj-$(CONFIG_PHY_NCSI) += ncsi.o
diff --git a/roms/u-boot/drivers/net/phy/aquantia.c b/roms/u-boot/drivers/net/phy/aquantia.c
new file mode 100644
index 000000000..9061afa62
--- /dev/null
+++ b/roms/u-boot/drivers/net/phy/aquantia.c
@@ -0,0 +1,738 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Aquantia PHY drivers
+ *
+ * Copyright 2014 Freescale Semiconductor, Inc.
+ * Copyright 2018 NXP
+ */
+#include <config.h>
+#include <common.h>
+#include <dm.h>
+#include <log.h>
+#include <net.h>
+#include <phy.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <u-boot/crc.h>
+#include <malloc.h>
+#include <asm/byteorder.h>
+#include <fs.h>
+
+#define AQUNTIA_10G_CTL 0x20
+#define AQUNTIA_VENDOR_P1 0xc400
+
+#define AQUNTIA_SPEED_LSB_MASK 0x2000
+#define AQUNTIA_SPEED_MSB_MASK 0x40
+
+#define AQUANTIA_SYSTEM_INTERFACE_SR 0xe812
+#define AQUANTIA_SYSTEM_INTERFACE_SR_READY BIT(0)
+#define AQUANTIA_VENDOR_PROVISIONING_REG 0xC441
+#define AQUANTIA_FIRMWARE_ID 0x20
+#define AQUANTIA_RESERVED_STATUS 0xc885
+#define AQUANTIA_FIRMWARE_MAJOR_MASK 0xff00
+#define AQUANTIA_FIRMWARE_MINOR_MASK 0xff
+#define AQUANTIA_FIRMWARE_BUILD_MASK 0xf0
+
+#define AQUANTIA_USX_AUTONEG_CONTROL_ENA 0x0008
+#define AQUANTIA_SI_IN_USE_MASK 0x0078
+#define AQUANTIA_SI_USXGMII 0x0018
+
+/* registers in MDIO_MMD_VEND1 region */
+#define AQUANTIA_VND1_GLOBAL_SC 0x000
+#define AQUANTIA_VND1_GLOBAL_SC_LP BIT(0xb)
+
+#define GLOBAL_FIRMWARE_ID 0x20
+#define GLOBAL_FAULT 0xc850
+#define GLOBAL_RSTATUS_1 0xc885
+
+#define GLOBAL_ALARM_1 0xcc00
+#define SYSTEM_READY_BIT 0x40
+
+#define GLOBAL_STANDARD_CONTROL 0x0
+#define SOFT_RESET BIT(15)
+#define LOW_POWER BIT(11)
+
+#define MAILBOX_CONTROL 0x0200
+#define MAILBOX_EXECUTE BIT(15)
+#define MAILBOX_WRITE BIT(14)
+#define MAILBOX_RESET_CRC BIT(12)
+#define MAILBOX_BUSY BIT(8)
+
+#define MAILBOX_CRC 0x0201
+
+#define MAILBOX_ADDR_MSW 0x0202
+#define MAILBOX_ADDR_LSW 0x0203
+
+#define MAILBOX_DATA_MSW 0x0204
+#define MAILBOX_DATA_LSW 0x0205
+
+#define UP_CONTROL 0xc001
+#define UP_RESET BIT(15)
+#define UP_RUN_STALL_OVERRIDE BIT(6)
+#define UP_RUN_STALL BIT(0)
+
+#define AQUANTIA_PMA_RX_VENDOR_P1 0xe400
+#define AQUANTIA_PMA_RX_VENDOR_P1_MDI_MSK GENMASK(1, 0)
+/* MDI reversal configured through registers */
+#define AQUANTIA_PMA_RX_VENDOR_P1_MDI_CFG BIT(1)
+/* MDI reversal enabled */
+#define AQUANTIA_PMA_RX_VENDOR_P1_MDI_REV BIT(0)
+
+/*
+ * global start rate, the protocol associated with this speed is used by default
+ * on SI.
+ */
+#define AQUANTIA_VND1_GSTART_RATE 0x31a
+#define AQUANTIA_VND1_GSTART_RATE_OFF 0
+#define AQUANTIA_VND1_GSTART_RATE_100M 1
+#define AQUANTIA_VND1_GSTART_RATE_1G 2
+#define AQUANTIA_VND1_GSTART_RATE_10G 3
+#define AQUANTIA_VND1_GSTART_RATE_2_5G 4
+#define AQUANTIA_VND1_GSTART_RATE_5G 5
+
+/* SYSCFG registers for 100M, 1G, 2.5G, 5G, 10G */
+#define AQUANTIA_VND1_GSYSCFG_BASE 0x31b
+#define AQUANTIA_VND1_GSYSCFG_100M 0
+#define AQUANTIA_VND1_GSYSCFG_1G 1
+#define AQUANTIA_VND1_GSYSCFG_2_5G 2
+#define AQUANTIA_VND1_GSYSCFG_5G 3
+#define AQUANTIA_VND1_GSYSCFG_10G 4
+
+#define AQUANTIA_VND1_SMBUS0 0xc485
+#define AQUANTIA_VND1_SMBUS1 0xc495
+
+/* addresses of memory segments in the phy */
+#define DRAM_BASE_ADDR 0x3FFE0000
+#define IRAM_BASE_ADDR 0x40000000
+
+/* firmware image format constants */
+#define VERSION_STRING_SIZE 0x40
+#define VERSION_STRING_OFFSET 0x0200
+#define HEADER_OFFSET 0x300
+
+/* driver private data */
+#define AQUANTIA_NA 0
+#define AQUANTIA_GEN1 1
+#define AQUANTIA_GEN2 2
+#define AQUANTIA_GEN3 3
+
+#pragma pack(1)
+struct fw_header {
+ u8 padding[4];
+ u8 iram_offset[3];
+ u8 iram_size[3];
+ u8 dram_offset[3];
+ u8 dram_size[3];
+};
+
+#pragma pack()
+
+#if defined(CONFIG_PHY_AQUANTIA_UPLOAD_FW)
+static int aquantia_read_fw(u8 **fw_addr, size_t *fw_length)
+{
+ loff_t length, read;
+ int ret;
+ void *addr = NULL;
+
+ *fw_addr = NULL;
+ *fw_length = 0;
+ debug("Loading Acquantia microcode from %s %s\n",
+ CONFIG_PHY_AQUANTIA_FW_PART, CONFIG_PHY_AQUANTIA_FW_NAME);
+ ret = fs_set_blk_dev("mmc", CONFIG_PHY_AQUANTIA_FW_PART, FS_TYPE_ANY);
+ if (ret < 0)
+ goto cleanup;
+
+ ret = fs_size(CONFIG_PHY_AQUANTIA_FW_NAME, &length);
+ if (ret < 0)
+ goto cleanup;
+
+ addr = malloc(length);
+ if (!addr) {
+ ret = -ENOMEM;
+ goto cleanup;
+ }
+
+ ret = fs_set_blk_dev("mmc", CONFIG_PHY_AQUANTIA_FW_PART, FS_TYPE_ANY);
+ if (ret < 0)
+ goto cleanup;
+
+ ret = fs_read(CONFIG_PHY_AQUANTIA_FW_NAME, (ulong)addr, 0, length,
+ &read);
+ if (ret < 0)
+ goto cleanup;
+
+ *fw_addr = addr;
+ *fw_length = length;
+ debug("Found Acquantia microcode.\n");
+
+cleanup:
+ if (ret < 0) {
+ printf("loading firmware file %s %s failed with error %d\n",
+ CONFIG_PHY_AQUANTIA_FW_PART,
+ CONFIG_PHY_AQUANTIA_FW_NAME, ret);
+ free(addr);
+ }
+ return ret;
+}
+
+/* load data into the phy's memory */
+static int aquantia_load_memory(struct phy_device *phydev, u32 addr,
+ const u8 *data, size_t len)
+{
+ size_t pos;
+ u16 crc = 0, up_crc;
+
+ phy_write(phydev, MDIO_MMD_VEND1, MAILBOX_CONTROL, MAILBOX_RESET_CRC);
+ phy_write(phydev, MDIO_MMD_VEND1, MAILBOX_ADDR_MSW, addr >> 16);
+ phy_write(phydev, MDIO_MMD_VEND1, MAILBOX_ADDR_LSW, addr & 0xfffc);
+
+ for (pos = 0; pos < len; pos += min(sizeof(u32), len - pos)) {
+ u32 word = 0;
+
+ memcpy(&word, &data[pos], min(sizeof(u32), len - pos));
+
+ phy_write(phydev, MDIO_MMD_VEND1, MAILBOX_DATA_MSW,
+ (word >> 16));
+ phy_write(phydev, MDIO_MMD_VEND1, MAILBOX_DATA_LSW,
+ word & 0xffff);
+
+ phy_write(phydev, MDIO_MMD_VEND1, MAILBOX_CONTROL,
+ MAILBOX_EXECUTE | MAILBOX_WRITE);
+
+ /* keep a big endian CRC to match the phy processor */
+ word = cpu_to_be32(word);
+ crc = crc16_ccitt(crc, (u8 *)&word, sizeof(word));
+ }
+
+ up_crc = phy_read(phydev, MDIO_MMD_VEND1, MAILBOX_CRC);
+ if (crc != up_crc) {
+ printf("%s crc mismatch: calculated 0x%04hx phy 0x%04hx\n",
+ phydev->dev->name, crc, up_crc);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static u32 unpack_u24(const u8 *data)
+{
+ return (data[2] << 16) + (data[1] << 8) + data[0];
+}
+
+static int aquantia_upload_firmware(struct phy_device *phydev)
+{
+ int ret;
+ u8 *addr = NULL;
+ size_t fw_length = 0;
+ u16 calculated_crc, read_crc;
+ char version[VERSION_STRING_SIZE];
+ u32 primary_offset, iram_offset, iram_size, dram_offset, dram_size;
+ const struct fw_header *header;
+
+ ret = aquantia_read_fw(&addr, &fw_length);
+ if (ret != 0)
+ return ret;
+
+ read_crc = (addr[fw_length - 2] << 8) | addr[fw_length - 1];
+ calculated_crc = crc16_ccitt(0, addr, fw_length - 2);
+ if (read_crc != calculated_crc) {
+ printf("%s bad firmware crc: file 0x%04x calculated 0x%04x\n",
+ phydev->dev->name, read_crc, calculated_crc);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ /* Find the DRAM and IRAM sections within the firmware file. */
+ primary_offset = ((addr[9] & 0xf) << 8 | addr[8]) << 12;
+
+ header = (struct fw_header *)&addr[primary_offset + HEADER_OFFSET];
+
+ iram_offset = primary_offset + unpack_u24(header->iram_offset);
+ iram_size = unpack_u24(header->iram_size);
+
+ dram_offset = primary_offset + unpack_u24(header->dram_offset);
+ dram_size = unpack_u24(header->dram_size);
+
+ debug("primary %d iram offset=%d size=%d dram offset=%d size=%d\n",
+ primary_offset, iram_offset, iram_size, dram_offset, dram_size);
+
+ strlcpy(version, (char *)&addr[dram_offset + VERSION_STRING_OFFSET],
+ VERSION_STRING_SIZE);
+ printf("%s loading firmare version '%s'\n", phydev->dev->name, version);
+
+ /* stall the microcprocessor */
+ phy_write(phydev, MDIO_MMD_VEND1, UP_CONTROL,
+ UP_RUN_STALL | UP_RUN_STALL_OVERRIDE);
+
+ debug("loading dram 0x%08x from offset=%d size=%d\n",
+ DRAM_BASE_ADDR, dram_offset, dram_size);
+ ret = aquantia_load_memory(phydev, DRAM_BASE_ADDR, &addr[dram_offset],
+ dram_size);
+ if (ret != 0)
+ goto done;
+
+ debug("loading iram 0x%08x from offset=%d size=%d\n",
+ IRAM_BASE_ADDR, iram_offset, iram_size);
+ ret = aquantia_load_memory(phydev, IRAM_BASE_ADDR, &addr[iram_offset],
+ iram_size);
+ if (ret != 0)
+ goto done;
+
+ /* make sure soft reset and low power mode are clear */
+ phy_write(phydev, MDIO_MMD_VEND1, GLOBAL_STANDARD_CONTROL, 0);
+
+ /* Release the microprocessor. UP_RESET must be held for 100 usec. */
+ phy_write(phydev, MDIO_MMD_VEND1, UP_CONTROL,
+ UP_RUN_STALL | UP_RUN_STALL_OVERRIDE | UP_RESET);
+
+ udelay(100);
+
+ phy_write(phydev, MDIO_MMD_VEND1, UP_CONTROL, UP_RUN_STALL_OVERRIDE);
+
+ printf("%s firmare loading done.\n", phydev->dev->name);
+done:
+ free(addr);
+ return ret;
+}
+#else
+static int aquantia_upload_firmware(struct phy_device *phydev)
+{
+ printf("ERROR %s firmware loading disabled.\n", phydev->dev->name);
+ return -1;
+}
+#endif
+
+struct {
+ u16 syscfg;
+ int cnt;
+ u16 start_rate;
+} aquantia_syscfg[PHY_INTERFACE_MODE_COUNT] = {
+ [PHY_INTERFACE_MODE_SGMII] = {0x04b, AQUANTIA_VND1_GSYSCFG_1G,
+ AQUANTIA_VND1_GSTART_RATE_1G},
+ [PHY_INTERFACE_MODE_SGMII_2500] = {0x144, AQUANTIA_VND1_GSYSCFG_2_5G,
+ AQUANTIA_VND1_GSTART_RATE_2_5G},
+ [PHY_INTERFACE_MODE_XFI] = {0x100, AQUANTIA_VND1_GSYSCFG_10G,
+ AQUANTIA_VND1_GSTART_RATE_10G},
+ [PHY_INTERFACE_MODE_USXGMII] = {0x080, AQUANTIA_VND1_GSYSCFG_10G,
+ AQUANTIA_VND1_GSTART_RATE_10G},
+};
+
+static int aquantia_set_proto(struct phy_device *phydev,
+ phy_interface_t interface)
+{
+ int i;
+
+ if (!aquantia_syscfg[interface].cnt)
+ return 0;
+
+ /* set the default rate to enable the SI link */
+ phy_write(phydev, MDIO_MMD_VEND1, AQUANTIA_VND1_GSTART_RATE,
+ aquantia_syscfg[interface].start_rate);
+
+ /* set selected protocol for all relevant line side link speeds */
+ for (i = 0; i <= aquantia_syscfg[interface].cnt; i++)
+ phy_write(phydev, MDIO_MMD_VEND1,
+ AQUANTIA_VND1_GSYSCFG_BASE + i,
+ aquantia_syscfg[interface].syscfg);
+ return 0;
+}
+
+static int aquantia_dts_config(struct phy_device *phydev)
+{
+#ifdef CONFIG_DM_ETH
+ ofnode node = phydev->node;
+ u32 prop;
+ u16 reg;
+
+ /* this code only works on gen2 and gen3 PHYs */
+ if (phydev->drv->data != AQUANTIA_GEN2 &&
+ phydev->drv->data != AQUANTIA_GEN3)
+ return -ENOTSUPP;
+
+ if (!ofnode_valid(node))
+ return 0;
+
+ if (!ofnode_read_u32(node, "mdi-reversal", &prop)) {
+ debug("mdi-reversal = %d\n", (int)prop);
+ reg = phy_read(phydev, MDIO_MMD_PMAPMD,
+ AQUANTIA_PMA_RX_VENDOR_P1);
+ reg &= ~AQUANTIA_PMA_RX_VENDOR_P1_MDI_MSK;
+ reg |= AQUANTIA_PMA_RX_VENDOR_P1_MDI_CFG;
+ reg |= prop ? AQUANTIA_PMA_RX_VENDOR_P1_MDI_REV : 0;
+ phy_write(phydev, MDIO_MMD_PMAPMD, AQUANTIA_PMA_RX_VENDOR_P1,
+ reg);
+ }
+ if (!ofnode_read_u32(node, "smb-addr", &prop)) {
+ debug("smb-addr = %x\n", (int)prop);
+ /*
+ * there are two addresses here, normally just one bus would
+ * be in use so we're setting both regs using the same DT
+ * property.
+ */
+ phy_write(phydev, MDIO_MMD_VEND1, AQUANTIA_VND1_SMBUS0,
+ (u16)(prop << 1));
+ phy_write(phydev, MDIO_MMD_VEND1, AQUANTIA_VND1_SMBUS1,
+ (u16)(prop << 1));
+ }
+
+#endif
+ return 0;
+}
+
+static bool aquantia_link_is_up(struct phy_device *phydev)
+{
+ u16 reg, regmask;
+ int devad, regnum;
+
+ /*
+ * On Gen 2 and 3 we have a bit that indicates that both system and
+ * line side are ready for data, use that if possible.
+ */
+ if (phydev->drv->data == AQUANTIA_GEN2 ||
+ phydev->drv->data == AQUANTIA_GEN3) {
+ devad = MDIO_MMD_PHYXS;
+ regnum = AQUANTIA_SYSTEM_INTERFACE_SR;
+ regmask = AQUANTIA_SYSTEM_INTERFACE_SR_READY;
+ } else {
+ devad = MDIO_MMD_AN;
+ regnum = MDIO_STAT1;
+ regmask = MDIO_AN_STAT1_COMPLETE;
+ }
+ /* the register should be latched, do a double read */
+ phy_read(phydev, devad, regnum);
+ reg = phy_read(phydev, devad, regnum);
+
+ return !!(reg & regmask);
+}
+
+int aquantia_config(struct phy_device *phydev)
+{
+ int interface = phydev->interface;
+ u32 val, id, rstatus, fault;
+ u32 reg_val1 = 0;
+ int num_retries = 5;
+ int usx_an = 0;
+
+ /*
+ * check if the system is out of reset and init sequence completed.
+ * chip-wide reset for gen1 quad phys takes longer
+ */
+ while (--num_retries) {
+ rstatus = phy_read(phydev, MDIO_MMD_VEND1, GLOBAL_ALARM_1);
+ if (rstatus & SYSTEM_READY_BIT)
+ break;
+ mdelay(10);
+ }
+
+ id = phy_read(phydev, MDIO_MMD_VEND1, GLOBAL_FIRMWARE_ID);
+ rstatus = phy_read(phydev, MDIO_MMD_VEND1, GLOBAL_RSTATUS_1);
+ fault = phy_read(phydev, MDIO_MMD_VEND1, GLOBAL_FAULT);
+
+ if (id != 0)
+ debug("%s running firmware version %X.%X.%X\n",
+ phydev->dev->name, (id >> 8), id & 0xff,
+ (rstatus >> 4) & 0xf);
+
+ if (fault != 0)
+ printf("%s fault 0x%04x detected\n", phydev->dev->name, fault);
+
+ if (id == 0 || fault != 0) {
+ int ret;
+
+ ret = aquantia_upload_firmware(phydev);
+ if (ret != 0)
+ return ret;
+ }
+ /*
+ * for backward compatibility convert XGMII into either XFI or USX based
+ * on FW config
+ */
+ if (interface == PHY_INTERFACE_MODE_XGMII) {
+ debug("use XFI or USXGMII SI protos, XGMII is not valid\n");
+
+ reg_val1 = phy_read(phydev, MDIO_MMD_PHYXS,
+ AQUANTIA_SYSTEM_INTERFACE_SR);
+ if ((reg_val1 & AQUANTIA_SI_IN_USE_MASK) == AQUANTIA_SI_USXGMII)
+ interface = PHY_INTERFACE_MODE_USXGMII;
+ else
+ interface = PHY_INTERFACE_MODE_XFI;
+ }
+
+ /*
+ * if link is up already we can just use it, otherwise configure
+ * the protocols in the PHY. If link is down set the system
+ * interface protocol to use based on phydev->interface
+ */
+ if (!aquantia_link_is_up(phydev) &&
+ (phydev->drv->data == AQUANTIA_GEN2 ||
+ phydev->drv->data == AQUANTIA_GEN3)) {
+ /* set PHY in low power mode so we can configure protocols */
+ phy_write(phydev, MDIO_MMD_VEND1, AQUANTIA_VND1_GLOBAL_SC,
+ AQUANTIA_VND1_GLOBAL_SC_LP);
+ mdelay(10);
+
+ /* configure protocol based on phydev->interface */
+ aquantia_set_proto(phydev, interface);
+ /* apply custom configuration based on DT */
+ aquantia_dts_config(phydev);
+
+ /* wake PHY back up */
+ phy_write(phydev, MDIO_MMD_VEND1, AQUANTIA_VND1_GLOBAL_SC, 0);
+ mdelay(10);
+ }
+
+ val = phy_read(phydev, MDIO_MMD_PMAPMD, MII_BMCR);
+
+ switch (interface) {
+ case PHY_INTERFACE_MODE_SGMII:
+ /* 1000BASE-T mode */
+ phydev->advertising = SUPPORTED_1000baseT_Full;
+ phydev->supported = phydev->advertising;
+
+ val = (val & ~AQUNTIA_SPEED_LSB_MASK) | AQUNTIA_SPEED_MSB_MASK;
+ phy_write(phydev, MDIO_MMD_PMAPMD, MII_BMCR, val);
+ break;
+ case PHY_INTERFACE_MODE_USXGMII:
+ usx_an = 1;
+ /* FALLTHROUGH */
+ case PHY_INTERFACE_MODE_XFI:
+ /* 10GBASE-T mode */
+ phydev->advertising = SUPPORTED_10000baseT_Full;
+ phydev->supported = phydev->advertising;
+
+ if (!(val & AQUNTIA_SPEED_LSB_MASK) ||
+ !(val & AQUNTIA_SPEED_MSB_MASK))
+ phy_write(phydev, MDIO_MMD_PMAPMD, MII_BMCR,
+ AQUNTIA_SPEED_LSB_MASK |
+ AQUNTIA_SPEED_MSB_MASK);
+
+ /* If SI is USXGMII then start USXGMII autoneg */
+ reg_val1 = phy_read(phydev, MDIO_MMD_PHYXS,
+ AQUANTIA_VENDOR_PROVISIONING_REG);
+
+ if (usx_an) {
+ reg_val1 |= AQUANTIA_USX_AUTONEG_CONTROL_ENA;
+ debug("%s: system interface USXGMII\n",
+ phydev->dev->name);
+ } else {
+ reg_val1 &= ~AQUANTIA_USX_AUTONEG_CONTROL_ENA;
+ debug("%s: system interface XFI\n",
+ phydev->dev->name);
+ }
+
+ phy_write(phydev, MDIO_MMD_PHYXS,
+ AQUANTIA_VENDOR_PROVISIONING_REG, reg_val1);
+ break;
+ case PHY_INTERFACE_MODE_SGMII_2500:
+ /* 2.5GBASE-T mode */
+ phydev->advertising = SUPPORTED_1000baseT_Full;
+ phydev->supported = phydev->advertising;
+
+ phy_write(phydev, MDIO_MMD_AN, AQUNTIA_10G_CTL, 1);
+ phy_write(phydev, MDIO_MMD_AN, AQUNTIA_VENDOR_P1, 0x9440);
+ break;
+ case PHY_INTERFACE_MODE_MII:
+ /* 100BASE-TX mode */
+ phydev->advertising = SUPPORTED_100baseT_Full;
+ phydev->supported = phydev->advertising;
+
+ val = (val & ~AQUNTIA_SPEED_MSB_MASK) | AQUNTIA_SPEED_LSB_MASK;
+ phy_write(phydev, MDIO_MMD_PMAPMD, MII_BMCR, val);
+ break;
+ };
+
+ val = phy_read(phydev, MDIO_MMD_VEND1, AQUANTIA_RESERVED_STATUS);
+ reg_val1 = phy_read(phydev, MDIO_MMD_VEND1, AQUANTIA_FIRMWARE_ID);
+
+ debug("%s: %s Firmware Version %x.%x.%x\n", phydev->dev->name,
+ phydev->drv->name,
+ (reg_val1 & AQUANTIA_FIRMWARE_MAJOR_MASK) >> 8,
+ reg_val1 & AQUANTIA_FIRMWARE_MINOR_MASK,
+ (val & AQUANTIA_FIRMWARE_BUILD_MASK) >> 4);
+
+ return 0;
+}
+
+int aquantia_startup(struct phy_device *phydev)
+{
+ u32 reg, speed;
+ int i = 0;
+
+ phydev->duplex = DUPLEX_FULL;
+
+ /* if the AN is still in progress, wait till timeout. */
+ if (!aquantia_link_is_up(phydev)) {
+ printf("%s Waiting for PHY auto negotiation to complete",
+ phydev->dev->name);
+ do {
+ udelay(1000);
+ if ((i++ % 500) == 0)
+ printf(".");
+ } while (!aquantia_link_is_up(phydev) &&
+ i < (4 * PHY_ANEG_TIMEOUT));
+
+ if (i > PHY_ANEG_TIMEOUT)
+ printf(" TIMEOUT !\n");
+ }
+
+ /* Read twice because link state is latched and a
+ * read moves the current state into the register */
+ phy_read(phydev, MDIO_MMD_AN, MDIO_STAT1);
+ reg = phy_read(phydev, MDIO_MMD_AN, MDIO_STAT1);
+ if (reg < 0 || !(reg & MDIO_STAT1_LSTATUS))
+ phydev->link = 0;
+ else
+ phydev->link = 1;
+
+ speed = phy_read(phydev, MDIO_MMD_PMAPMD, MII_BMCR);
+ if (speed & AQUNTIA_SPEED_MSB_MASK) {
+ if (speed & AQUNTIA_SPEED_LSB_MASK)
+ phydev->speed = SPEED_10000;
+ else
+ phydev->speed = SPEED_1000;
+ } else {
+ if (speed & AQUNTIA_SPEED_LSB_MASK)
+ phydev->speed = SPEED_100;
+ else
+ phydev->speed = SPEED_10;
+ }
+
+ return 0;
+}
+
+struct phy_driver aq1202_driver = {
+ .name = "Aquantia AQ1202",
+ .uid = 0x3a1b445,
+ .mask = 0xfffffff0,
+ .features = PHY_10G_FEATURES,
+ .mmds = (MDIO_MMD_PMAPMD | MDIO_MMD_PCS|
+ MDIO_MMD_PHYXS | MDIO_MMD_AN |
+ MDIO_MMD_VEND1),
+ .config = &aquantia_config,
+ .startup = &aquantia_startup,
+ .shutdown = &gen10g_shutdown,
+};
+
+struct phy_driver aq2104_driver = {
+ .name = "Aquantia AQ2104",
+ .uid = 0x3a1b460,
+ .mask = 0xfffffff0,
+ .features = PHY_10G_FEATURES,
+ .mmds = (MDIO_MMD_PMAPMD | MDIO_MMD_PCS|
+ MDIO_MMD_PHYXS | MDIO_MMD_AN |
+ MDIO_MMD_VEND1),
+ .config = &aquantia_config,
+ .startup = &aquantia_startup,
+ .shutdown = &gen10g_shutdown,
+};
+
+struct phy_driver aqr105_driver = {
+ .name = "Aquantia AQR105",
+ .uid = 0x3a1b4a2,
+ .mask = 0xfffffff0,
+ .features = PHY_10G_FEATURES,
+ .mmds = (MDIO_MMD_PMAPMD | MDIO_MMD_PCS|
+ MDIO_MMD_PHYXS | MDIO_MMD_AN |
+ MDIO_MMD_VEND1),
+ .config = &aquantia_config,
+ .startup = &aquantia_startup,
+ .shutdown = &gen10g_shutdown,
+ .data = AQUANTIA_GEN1,
+};
+
+struct phy_driver aqr106_driver = {
+ .name = "Aquantia AQR106",
+ .uid = 0x3a1b4d0,
+ .mask = 0xfffffff0,
+ .features = PHY_10G_FEATURES,
+ .mmds = (MDIO_MMD_PMAPMD | MDIO_MMD_PCS|
+ MDIO_MMD_PHYXS | MDIO_MMD_AN |
+ MDIO_MMD_VEND1),
+ .config = &aquantia_config,
+ .startup = &aquantia_startup,
+ .shutdown = &gen10g_shutdown,
+};
+
+struct phy_driver aqr107_driver = {
+ .name = "Aquantia AQR107",
+ .uid = 0x3a1b4e0,
+ .mask = 0xfffffff0,
+ .features = PHY_10G_FEATURES,
+ .mmds = (MDIO_MMD_PMAPMD | MDIO_MMD_PCS|
+ MDIO_MMD_PHYXS | MDIO_MMD_AN |
+ MDIO_MMD_VEND1),
+ .config = &aquantia_config,
+ .startup = &aquantia_startup,
+ .shutdown = &gen10g_shutdown,
+ .data = AQUANTIA_GEN2,
+};
+
+struct phy_driver aqr112_driver = {
+ .name = "Aquantia AQR112",
+ .uid = 0x3a1b660,
+ .mask = 0xfffffff0,
+ .features = PHY_10G_FEATURES,
+ .mmds = (MDIO_MMD_PMAPMD | MDIO_MMD_PCS |
+ MDIO_MMD_PHYXS | MDIO_MMD_AN |
+ MDIO_MMD_VEND1),
+ .config = &aquantia_config,
+ .startup = &aquantia_startup,
+ .shutdown = &gen10g_shutdown,
+ .data = AQUANTIA_GEN3,
+};
+
+struct phy_driver aqr113c_driver = {
+ .name = "Aquantia AQR113C",
+ .uid = 0x31c31c12,
+ .mask = 0xfffffff0,
+ .features = PHY_10G_FEATURES,
+ .mmds = (MDIO_MMD_PMAPMD | MDIO_MMD_PCS |
+ MDIO_MMD_PHYXS | MDIO_MMD_AN |
+ MDIO_MMD_VEND1),
+ .config = &aquantia_config,
+ .startup = &aquantia_startup,
+ .shutdown = &gen10g_shutdown,
+ .data = AQUANTIA_GEN3,
+};
+
+struct phy_driver aqr405_driver = {
+ .name = "Aquantia AQR405",
+ .uid = 0x3a1b4b2,
+ .mask = 0xfffffff0,
+ .features = PHY_10G_FEATURES,
+ .mmds = (MDIO_MMD_PMAPMD | MDIO_MMD_PCS|
+ MDIO_MMD_PHYXS | MDIO_MMD_AN |
+ MDIO_MMD_VEND1),
+ .config = &aquantia_config,
+ .startup = &aquantia_startup,
+ .shutdown = &gen10g_shutdown,
+ .data = AQUANTIA_GEN1,
+};
+
+struct phy_driver aqr412_driver = {
+ .name = "Aquantia AQR412",
+ .uid = 0x3a1b710,
+ .mask = 0xfffffff0,
+ .features = PHY_10G_FEATURES,
+ .mmds = (MDIO_MMD_PMAPMD | MDIO_MMD_PCS |
+ MDIO_MMD_PHYXS | MDIO_MMD_AN |
+ MDIO_MMD_VEND1),
+ .config = &aquantia_config,
+ .startup = &aquantia_startup,
+ .shutdown = &gen10g_shutdown,
+ .data = AQUANTIA_GEN3,
+};
+
+int phy_aquantia_init(void)
+{
+ phy_register(&aq1202_driver);
+ phy_register(&aq2104_driver);
+ phy_register(&aqr105_driver);
+ phy_register(&aqr106_driver);
+ phy_register(&aqr107_driver);
+ phy_register(&aqr112_driver);
+ phy_register(&aqr113c_driver);
+ phy_register(&aqr405_driver);
+ phy_register(&aqr412_driver);
+
+ return 0;
+}
diff --git a/roms/u-boot/drivers/net/phy/atheros.c b/roms/u-boot/drivers/net/phy/atheros.c
new file mode 100644
index 000000000..f922fecd6
--- /dev/null
+++ b/roms/u-boot/drivers/net/phy/atheros.c
@@ -0,0 +1,375 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Atheros PHY drivers
+ *
+ * Copyright 2011, 2013 Freescale Semiconductor, Inc.
+ * author Andy Fleming
+ * Copyright (c) 2019 Michael Walle <michael@walle.cc>
+ */
+#include <common.h>
+#include <phy.h>
+#include <dm/device_compat.h>
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <dt-bindings/net/qca-ar803x.h>
+
+#define AR803x_PHY_DEBUG_ADDR_REG 0x1d
+#define AR803x_PHY_DEBUG_DATA_REG 0x1e
+
+/* Debug registers */
+#define AR803x_DEBUG_REG_0 0x0
+#define AR803x_RGMII_RX_CLK_DLY BIT(15)
+
+#define AR803x_DEBUG_REG_5 0x5
+#define AR803x_RGMII_TX_CLK_DLY BIT(8)
+
+#define AR803x_DEBUG_REG_1F 0x1f
+#define AR803x_PLL_ON BIT(2)
+#define AR803x_RGMII_1V8 BIT(3)
+
+/* CLK_25M register is at MMD 7, address 0x8016 */
+#define AR803x_CLK_25M_SEL_REG 0x8016
+
+#define AR803x_CLK_25M_MASK GENMASK(4, 2)
+#define AR803x_CLK_25M_25MHZ_XTAL 0
+#define AR803x_CLK_25M_25MHZ_DSP 1
+#define AR803x_CLK_25M_50MHZ_PLL 2
+#define AR803x_CLK_25M_50MHZ_DSP 3
+#define AR803x_CLK_25M_62_5MHZ_PLL 4
+#define AR803x_CLK_25M_62_5MHZ_DSP 5
+#define AR803x_CLK_25M_125MHZ_PLL 6
+#define AR803x_CLK_25M_125MHZ_DSP 7
+#define AR8035_CLK_25M_MASK GENMASK(4, 3)
+
+#define AR803x_CLK_25M_DR_MASK GENMASK(8, 7)
+#define AR803x_CLK_25M_DR_FULL 0
+#define AR803x_CLK_25M_DR_HALF 1
+#define AR803x_CLK_25M_DR_QUARTER 2
+
+#define AR8021_PHY_ID 0x004dd040
+#define AR8031_PHY_ID 0x004dd074
+#define AR8035_PHY_ID 0x004dd072
+
+struct ar803x_priv {
+ int flags;
+#define AR803x_FLAG_KEEP_PLL_ENABLED BIT(0) /* don't turn off internal PLL */
+#define AR803x_FLAG_RGMII_1V8 BIT(1) /* use 1.8V RGMII I/O voltage */
+ u16 clk_25m_reg;
+ u16 clk_25m_mask;
+};
+
+static int ar803x_debug_reg_read(struct phy_device *phydev, u16 reg)
+{
+ int ret;
+
+ ret = phy_write(phydev, MDIO_DEVAD_NONE, AR803x_PHY_DEBUG_ADDR_REG,
+ reg);
+ if (ret < 0)
+ return ret;
+
+ return phy_read(phydev, MDIO_DEVAD_NONE, AR803x_PHY_DEBUG_DATA_REG);
+}
+
+static int ar803x_debug_reg_mask(struct phy_device *phydev, u16 reg,
+ u16 clear, u16 set)
+{
+ int val;
+
+ val = ar803x_debug_reg_read(phydev, reg);
+ if (val < 0)
+ return val;
+
+ val &= 0xffff;
+ val &= ~clear;
+ val |= set;
+
+ return phy_write(phydev, MDIO_DEVAD_NONE, AR803x_PHY_DEBUG_DATA_REG,
+ val);
+}
+
+static int ar803x_enable_rx_delay(struct phy_device *phydev, bool on)
+{
+ u16 clear = 0, set = 0;
+
+ if (on)
+ set = AR803x_RGMII_RX_CLK_DLY;
+ else
+ clear = AR803x_RGMII_RX_CLK_DLY;
+
+ return ar803x_debug_reg_mask(phydev, AR803x_DEBUG_REG_0, clear, set);
+}
+
+static int ar803x_enable_tx_delay(struct phy_device *phydev, bool on)
+{
+ u16 clear = 0, set = 0;
+
+ if (on)
+ set = AR803x_RGMII_TX_CLK_DLY;
+ else
+ clear = AR803x_RGMII_TX_CLK_DLY;
+
+ return ar803x_debug_reg_mask(phydev, AR803x_DEBUG_REG_5, clear, set);
+}
+
+static int ar8021_config(struct phy_device *phydev)
+{
+ phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR,
+ BMCR_ANENABLE | BMCR_ANRESTART);
+
+ ar803x_enable_tx_delay(phydev, true);
+
+ phydev->supported = phydev->drv->features;
+ return 0;
+}
+
+static int ar803x_delay_config(struct phy_device *phydev)
+{
+ int ret;
+
+ if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID ||
+ phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
+ ret = ar803x_enable_tx_delay(phydev, true);
+ else
+ ret = ar803x_enable_tx_delay(phydev, false);
+
+ if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID ||
+ phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
+ ret = ar803x_enable_rx_delay(phydev, true);
+ else
+ ret = ar803x_enable_rx_delay(phydev, false);
+
+ return ret;
+}
+
+static int ar803x_regs_config(struct phy_device *phydev)
+{
+ struct ar803x_priv *priv = phydev->priv;
+ u16 set = 0, clear = 0;
+ int val;
+ int ret;
+
+ /* no configuration available */
+ if (!priv)
+ return 0;
+
+ /*
+ * Only supported on the AR8031, AR8035 has strappings for the PLL mode
+ * as well as the RGMII voltage.
+ */
+ if (phydev->drv->uid == AR8031_PHY_ID) {
+ if (priv->flags & AR803x_FLAG_KEEP_PLL_ENABLED)
+ set |= AR803x_PLL_ON;
+ else
+ clear |= AR803x_PLL_ON;
+
+ if (priv->flags & AR803x_FLAG_RGMII_1V8)
+ set |= AR803x_RGMII_1V8;
+ else
+ clear |= AR803x_RGMII_1V8;
+
+ ret = ar803x_debug_reg_mask(phydev, AR803x_DEBUG_REG_1F, clear,
+ set);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* save the write access if the mask is empty */
+ if (priv->clk_25m_mask) {
+ val = phy_read_mmd(phydev, MDIO_MMD_AN, AR803x_CLK_25M_SEL_REG);
+ if (val < 0)
+ return val;
+ val &= ~priv->clk_25m_mask;
+ val |= priv->clk_25m_reg;
+ ret = phy_write_mmd(phydev, MDIO_MMD_AN,
+ AR803x_CLK_25M_SEL_REG, val);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ar803x_of_init(struct phy_device *phydev)
+{
+#if defined(CONFIG_DM_ETH)
+ struct ar803x_priv *priv;
+ ofnode node, vddio_reg_node;
+ u32 strength, freq, min_uV, max_uV;
+ int sel;
+
+ node = phy_get_ofnode(phydev);
+ if (!ofnode_valid(node))
+ return -EINVAL;
+
+ priv = malloc(sizeof(*priv));
+ if (!priv)
+ return -ENOMEM;
+ memset(priv, 0, sizeof(*priv));
+
+ phydev->priv = priv;
+
+ debug("%s: found PHY node: %s\n", __func__, ofnode_get_name(node));
+
+ if (ofnode_read_bool(node, "qca,keep-pll-enabled"))
+ priv->flags |= AR803x_FLAG_KEEP_PLL_ENABLED;
+
+ /*
+ * We can't use the regulator framework because the regulator is
+ * a subnode of the PHY. So just read the two properties we are
+ * interested in.
+ */
+ vddio_reg_node = ofnode_find_subnode(node, "vddio-regulator");
+ if (ofnode_valid(vddio_reg_node)) {
+ min_uV = ofnode_read_u32_default(vddio_reg_node,
+ "regulator-min-microvolt", 0);
+ max_uV = ofnode_read_u32_default(vddio_reg_node,
+ "regulator-max-microvolt", 0);
+
+ if (min_uV != max_uV) {
+ free(priv);
+ return -EINVAL;
+ }
+
+ switch (min_uV) {
+ case 1500000:
+ break;
+ case 1800000:
+ priv->flags |= AR803x_FLAG_RGMII_1V8;
+ break;
+ default:
+ free(priv);
+ return -EINVAL;
+ }
+ }
+
+ /*
+ * Get the CLK_25M frequency from the device tree. Only XTAL and PLL
+ * sources are supported right now. There is also the possibilty to use
+ * the DSP as frequency reference, this is used for synchronous
+ * ethernet.
+ */
+ if (!ofnode_read_u32(node, "qca,clk-out-frequency", &freq)) {
+ switch (freq) {
+ case 25000000:
+ sel = AR803x_CLK_25M_25MHZ_XTAL;
+ break;
+ case 50000000:
+ sel = AR803x_CLK_25M_50MHZ_PLL;
+ break;
+ case 62500000:
+ sel = AR803x_CLK_25M_62_5MHZ_PLL;
+ break;
+ case 125000000:
+ sel = AR803x_CLK_25M_125MHZ_PLL;
+ break;
+ default:
+ dev_err(phydev->dev,
+ "invalid qca,clk-out-frequency\n");
+ free(priv);
+ return -EINVAL;
+ }
+
+ priv->clk_25m_mask |= AR803x_CLK_25M_MASK;
+ priv->clk_25m_reg |= FIELD_PREP(AR803x_CLK_25M_MASK, sel);
+ /*
+ * Fixup for the AR8035 which only has two bits. The two
+ * remaining bits map to the same frequencies.
+ */
+
+ if (phydev->drv->uid == AR8035_PHY_ID) {
+ priv->clk_25m_reg &= AR8035_CLK_25M_MASK;
+ priv->clk_25m_mask &= AR8035_CLK_25M_MASK;
+ }
+ }
+
+ if (phydev->drv->uid == AR8031_PHY_ID &&
+ !ofnode_read_u32(node, "qca,clk-out-strength", &strength)) {
+ switch (strength) {
+ case AR803X_STRENGTH_FULL:
+ sel = AR803x_CLK_25M_DR_FULL;
+ break;
+ case AR803X_STRENGTH_HALF:
+ sel = AR803x_CLK_25M_DR_HALF;
+ break;
+ case AR803X_STRENGTH_QUARTER:
+ sel = AR803x_CLK_25M_DR_QUARTER;
+ break;
+ default:
+ dev_err(phydev->dev,
+ "invalid qca,clk-out-strength\n");
+ free(priv);
+ return -EINVAL;
+ }
+ priv->clk_25m_mask |= AR803x_CLK_25M_DR_MASK;
+ priv->clk_25m_reg |= FIELD_PREP(AR803x_CLK_25M_DR_MASK, sel);
+ }
+
+ debug("%s: flags=%x clk_25m_reg=%04x clk_25m_mask=%04x\n", __func__,
+ priv->flags, priv->clk_25m_reg, priv->clk_25m_mask);
+#endif
+
+ return 0;
+}
+
+static int ar803x_config(struct phy_device *phydev)
+{
+ int ret;
+
+ ret = ar803x_of_init(phydev);
+ if (ret < 0)
+ return ret;
+
+ ret = ar803x_delay_config(phydev);
+ if (ret < 0)
+ return ret;
+
+ ret = ar803x_regs_config(phydev);
+ if (ret < 0)
+ return ret;
+
+ phydev->supported = phydev->drv->features;
+
+ genphy_config_aneg(phydev);
+ genphy_restart_aneg(phydev);
+
+ return 0;
+}
+
+static struct phy_driver AR8021_driver = {
+ .name = "AR8021",
+ .uid = AR8021_PHY_ID,
+ .mask = 0xfffffff0,
+ .features = PHY_GBIT_FEATURES,
+ .config = ar8021_config,
+ .startup = genphy_startup,
+ .shutdown = genphy_shutdown,
+};
+
+static struct phy_driver AR8031_driver = {
+ .name = "AR8031/AR8033",
+ .uid = AR8031_PHY_ID,
+ .mask = 0xffffffef,
+ .features = PHY_GBIT_FEATURES,
+ .config = ar803x_config,
+ .startup = genphy_startup,
+ .shutdown = genphy_shutdown,
+};
+
+static struct phy_driver AR8035_driver = {
+ .name = "AR8035",
+ .uid = AR8035_PHY_ID,
+ .mask = 0xffffffef,
+ .features = PHY_GBIT_FEATURES,
+ .config = ar803x_config,
+ .startup = genphy_startup,
+ .shutdown = genphy_shutdown,
+};
+
+int phy_atheros_init(void)
+{
+ phy_register(&AR8021_driver);
+ phy_register(&AR8031_driver);
+ phy_register(&AR8035_driver);
+
+ return 0;
+}
diff --git a/roms/u-boot/drivers/net/phy/b53.c b/roms/u-boot/drivers/net/phy/b53.c
new file mode 100644
index 000000000..21da53c7e
--- /dev/null
+++ b/roms/u-boot/drivers/net/phy/b53.c
@@ -0,0 +1,770 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2017
+ * Broadcom
+ * Florian Fainelli <f.fainelli@gmail.com>
+ */
+
+/*
+ * PHY driver for Broadcom BCM53xx (roboswitch) Ethernet switches.
+ *
+ * This driver configures the b53 for basic use as a PHY. The switch supports
+ * vendor tags and VLAN configuration that can affect the switching decisions.
+ * This driver uses a simple configuration in which all ports are only allowed
+ * to send frames to the CPU port and receive frames from the CPU port this
+ * providing port isolation (no cross talk).
+ *
+ * The configuration determines which PHY ports to activate using the
+ * CONFIG_B53_PHY_PORTS bitmask. Set bit N will active port N and so on.
+ *
+ * This driver was written primarily for the Lamobo R1 platform using a BCM53152
+ * switch but the BCM53xx being largely register compatible, extending it to
+ * cover other switches would be trivial.
+ */
+
+#include <common.h>
+#include <command.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+
+#include <errno.h>
+#include <malloc.h>
+#include <miiphy.h>
+#include <netdev.h>
+
+/* Pseudo-PHY address (non configurable) to access internal registers */
+#define BRCM_PSEUDO_PHY_ADDR 30
+
+/* Maximum number of ports possible */
+#define B53_N_PORTS 9
+
+#define B53_CTRL_PAGE 0x00 /* Control */
+#define B53_MGMT_PAGE 0x02 /* Management Mode */
+/* Port VLAN Page */
+#define B53_PVLAN_PAGE 0x31
+
+/* Control Page registers */
+#define B53_PORT_CTRL(i) (0x00 + (i))
+#define PORT_CTRL_RX_DISABLE BIT(0)
+#define PORT_CTRL_TX_DISABLE BIT(1)
+#define PORT_CTRL_RX_BCST_EN BIT(2) /* Broadcast RX (P8 only) */
+#define PORT_CTRL_RX_MCST_EN BIT(3) /* Multicast RX (P8 only) */
+#define PORT_CTRL_RX_UCST_EN BIT(4) /* Unicast RX (P8 only) */
+
+/* Switch Mode Control Register (8 bit) */
+#define B53_SWITCH_MODE 0x0b
+#define SM_SW_FWD_MODE BIT(0) /* 1 = Managed Mode */
+#define SM_SW_FWD_EN BIT(1) /* Forwarding Enable */
+
+/* IMP Port state override register (8 bit) */
+#define B53_PORT_OVERRIDE_CTRL 0x0e
+#define PORT_OVERRIDE_LINK BIT(0)
+#define PORT_OVERRIDE_FULL_DUPLEX BIT(1) /* 0 = Half Duplex */
+#define PORT_OVERRIDE_SPEED_S 2
+#define PORT_OVERRIDE_SPEED_10M (0 << PORT_OVERRIDE_SPEED_S)
+#define PORT_OVERRIDE_SPEED_100M (1 << PORT_OVERRIDE_SPEED_S)
+#define PORT_OVERRIDE_SPEED_1000M (2 << PORT_OVERRIDE_SPEED_S)
+/* BCM5325 only */
+#define PORT_OVERRIDE_RV_MII_25 BIT(4)
+#define PORT_OVERRIDE_RX_FLOW BIT(4)
+#define PORT_OVERRIDE_TX_FLOW BIT(5)
+/* BCM5301X only, requires setting 1000M */
+#define PORT_OVERRIDE_SPEED_2000M BIT(6)
+#define PORT_OVERRIDE_EN BIT(7) /* Use the register contents */
+
+#define B53_RGMII_CTRL_IMP 0x60
+#define RGMII_CTRL_ENABLE_GMII BIT(7)
+#define RGMII_CTRL_TIMING_SEL BIT(2)
+#define RGMII_CTRL_DLL_RXC BIT(1)
+#define RGMII_CTRL_DLL_TXC BIT(0)
+
+/* Switch control (8 bit) */
+#define B53_SWITCH_CTRL 0x22
+#define B53_MII_DUMB_FWDG_EN BIT(6)
+
+/* Software reset register (8 bit) */
+#define B53_SOFTRESET 0x79
+#define SW_RST BIT(7)
+#define EN_CH_RST BIT(6)
+#define EN_SW_RST BIT(4)
+
+/* Fast Aging Control register (8 bit) */
+#define B53_FAST_AGE_CTRL 0x88
+#define FAST_AGE_STATIC BIT(0)
+#define FAST_AGE_DYNAMIC BIT(1)
+#define FAST_AGE_PORT BIT(2)
+#define FAST_AGE_VLAN BIT(3)
+#define FAST_AGE_STP BIT(4)
+#define FAST_AGE_MC BIT(5)
+#define FAST_AGE_DONE BIT(7)
+
+/* Port VLAN mask (16 bit) IMP port is always 8, also on 5325 & co */
+#define B53_PVLAN_PORT_MASK(i) ((i) * 2)
+
+/* MII registers */
+#define REG_MII_PAGE 0x10 /* MII Page register */
+#define REG_MII_ADDR 0x11 /* MII Address register */
+#define REG_MII_DATA0 0x18 /* MII Data register 0 */
+#define REG_MII_DATA1 0x19 /* MII Data register 1 */
+#define REG_MII_DATA2 0x1a /* MII Data register 2 */
+#define REG_MII_DATA3 0x1b /* MII Data register 3 */
+
+#define REG_MII_PAGE_ENABLE BIT(0)
+#define REG_MII_ADDR_WRITE BIT(0)
+#define REG_MII_ADDR_READ BIT(1)
+
+struct b53_device {
+ struct mii_dev *bus;
+ unsigned int cpu_port;
+};
+
+static int b53_mdio_op(struct mii_dev *bus, u8 page, u8 reg, u16 op)
+{
+ int ret;
+ int i;
+ u16 v;
+
+ /* set page number */
+ v = (page << 8) | REG_MII_PAGE_ENABLE;
+ ret = bus->write(bus, BRCM_PSEUDO_PHY_ADDR, MDIO_DEVAD_NONE,
+ REG_MII_PAGE, v);
+ if (ret)
+ return ret;
+
+ /* set register address */
+ v = (reg << 8) | op;
+ ret = bus->write(bus, BRCM_PSEUDO_PHY_ADDR, MDIO_DEVAD_NONE,
+ REG_MII_ADDR, v);
+ if (ret)
+ return ret;
+
+ /* check if operation completed */
+ for (i = 0; i < 5; ++i) {
+ v = bus->read(bus, BRCM_PSEUDO_PHY_ADDR, MDIO_DEVAD_NONE,
+ REG_MII_ADDR);
+ if (!(v & (REG_MII_ADDR_WRITE | REG_MII_ADDR_READ)))
+ break;
+
+ udelay(100);
+ }
+
+ if (i == 5)
+ return -EIO;
+
+ return 0;
+}
+
+static int b53_mdio_read8(struct mii_dev *bus, u8 page, u8 reg, u8 *val)
+{
+ int ret;
+
+ ret = b53_mdio_op(bus, page, reg, REG_MII_ADDR_READ);
+ if (ret)
+ return ret;
+
+ *val = bus->read(bus, BRCM_PSEUDO_PHY_ADDR, MDIO_DEVAD_NONE,
+ REG_MII_DATA0) & 0xff;
+
+ return 0;
+}
+
+static int b53_mdio_read16(struct mii_dev *bus, u8 page, u8 reg, u16 *val)
+{
+ int ret;
+
+ ret = b53_mdio_op(bus, page, reg, REG_MII_ADDR_READ);
+ if (ret)
+ return ret;
+
+ *val = bus->read(bus, BRCM_PSEUDO_PHY_ADDR, MDIO_DEVAD_NONE,
+ REG_MII_DATA0);
+
+ return 0;
+}
+
+static int b53_mdio_read32(struct mii_dev *bus, u8 page, u8 reg, u32 *val)
+{
+ int ret;
+
+ ret = b53_mdio_op(bus, page, reg, REG_MII_ADDR_READ);
+ if (ret)
+ return ret;
+
+ *val = bus->read(bus, BRCM_PSEUDO_PHY_ADDR, MDIO_DEVAD_NONE,
+ REG_MII_DATA0);
+ *val |= bus->read(bus, BRCM_PSEUDO_PHY_ADDR, MDIO_DEVAD_NONE,
+ REG_MII_DATA1) << 16;
+
+ return 0;
+}
+
+static int b53_mdio_read48(struct mii_dev *bus, u8 page, u8 reg, u64 *val)
+{
+ u64 temp = 0;
+ int i;
+ int ret;
+
+ ret = b53_mdio_op(bus, page, reg, REG_MII_ADDR_READ);
+ if (ret)
+ return ret;
+
+ for (i = 2; i >= 0; i--) {
+ temp <<= 16;
+ temp |= bus->read(bus, BRCM_PSEUDO_PHY_ADDR, MDIO_DEVAD_NONE,
+ REG_MII_DATA0 + i);
+ }
+
+ *val = temp;
+
+ return 0;
+}
+
+static int b53_mdio_read64(struct mii_dev *bus, u8 page, u8 reg, u64 *val)
+{
+ u64 temp = 0;
+ int i;
+ int ret;
+
+ ret = b53_mdio_op(bus, page, reg, REG_MII_ADDR_READ);
+ if (ret)
+ return ret;
+
+ for (i = 3; i >= 0; i--) {
+ temp <<= 16;
+ temp |= bus->read(bus, BRCM_PSEUDO_PHY_ADDR, MDIO_DEVAD_NONE,
+ REG_MII_DATA0 + i);
+ }
+
+ *val = temp;
+
+ return 0;
+}
+
+static int b53_mdio_write8(struct mii_dev *bus, u8 page, u8 reg, u8 value)
+{
+ int ret;
+
+ ret = bus->write(bus, BRCM_PSEUDO_PHY_ADDR, MDIO_DEVAD_NONE,
+ REG_MII_DATA0, value);
+ if (ret)
+ return ret;
+
+ return b53_mdio_op(bus, page, reg, REG_MII_ADDR_WRITE);
+}
+
+static int b53_mdio_write16(struct mii_dev *bus, u8 page, u8 reg,
+ u16 value)
+{
+ int ret;
+
+ ret = bus->write(bus, BRCM_PSEUDO_PHY_ADDR, MDIO_DEVAD_NONE,
+ REG_MII_DATA0, value);
+ if (ret)
+ return ret;
+
+ return b53_mdio_op(bus, page, reg, REG_MII_ADDR_WRITE);
+}
+
+static int b53_mdio_write32(struct mii_dev *bus, u8 page, u8 reg,
+ u32 value)
+{
+ unsigned int i;
+ u32 temp = value;
+
+ for (i = 0; i < 2; i++) {
+ int ret = bus->write(bus, BRCM_PSEUDO_PHY_ADDR,
+ MDIO_DEVAD_NONE,
+ REG_MII_DATA0 + i, temp & 0xffff);
+ if (ret)
+ return ret;
+ temp >>= 16;
+ }
+
+ return b53_mdio_op(bus, page, reg, REG_MII_ADDR_WRITE);
+}
+
+static int b53_mdio_write48(struct mii_dev *bus, u8 page, u8 reg,
+ u64 value)
+{
+ unsigned int i;
+ u64 temp = value;
+
+ for (i = 0; i < 3; i++) {
+ int ret = bus->write(bus, BRCM_PSEUDO_PHY_ADDR,
+ MDIO_DEVAD_NONE,
+ REG_MII_DATA0 + i, temp & 0xffff);
+ if (ret)
+ return ret;
+ temp >>= 16;
+ }
+
+ return b53_mdio_op(bus, page, reg, REG_MII_ADDR_WRITE);
+}
+
+static int b53_mdio_write64(struct mii_dev *bus, u8 page, u8 reg,
+ u64 value)
+{
+ unsigned int i;
+ u64 temp = value;
+
+ for (i = 0; i < 4; i++) {
+ int ret = bus->write(bus, BRCM_PSEUDO_PHY_ADDR,
+ MDIO_DEVAD_NONE,
+ REG_MII_DATA0 + i, temp & 0xffff);
+ if (ret)
+ return ret;
+ temp >>= 16;
+ }
+
+ return b53_mdio_op(bus, page, reg, REG_MII_ADDR_WRITE);
+}
+
+static inline int b53_read8(struct b53_device *dev, u8 page,
+ u8 reg, u8 *value)
+{
+ return b53_mdio_read8(dev->bus, page, reg, value);
+}
+
+static inline int b53_read16(struct b53_device *dev, u8 page,
+ u8 reg, u16 *value)
+{
+ return b53_mdio_read16(dev->bus, page, reg, value);
+}
+
+static inline int b53_read32(struct b53_device *dev, u8 page,
+ u8 reg, u32 *value)
+{
+ return b53_mdio_read32(dev->bus, page, reg, value);
+}
+
+static inline int b53_read48(struct b53_device *dev, u8 page,
+ u8 reg, u64 *value)
+{
+ return b53_mdio_read48(dev->bus, page, reg, value);
+}
+
+static inline int b53_read64(struct b53_device *dev, u8 page,
+ u8 reg, u64 *value)
+{
+ return b53_mdio_read64(dev->bus, page, reg, value);
+}
+
+static inline int b53_write8(struct b53_device *dev, u8 page,
+ u8 reg, u8 value)
+{
+ return b53_mdio_write8(dev->bus, page, reg, value);
+}
+
+static inline int b53_write16(struct b53_device *dev, u8 page,
+ u8 reg, u16 value)
+{
+ return b53_mdio_write16(dev->bus, page, reg, value);
+}
+
+static inline int b53_write32(struct b53_device *dev, u8 page,
+ u8 reg, u32 value)
+{
+ return b53_mdio_write32(dev->bus, page, reg, value);
+}
+
+static inline int b53_write48(struct b53_device *dev, u8 page,
+ u8 reg, u64 value)
+{
+ return b53_mdio_write48(dev->bus, page, reg, value);
+}
+
+static inline int b53_write64(struct b53_device *dev, u8 page,
+ u8 reg, u64 value)
+{
+ return b53_mdio_write64(dev->bus, page, reg, value);
+}
+
+static int b53_flush_arl(struct b53_device *dev, u8 mask)
+{
+ unsigned int i;
+
+ b53_write8(dev, B53_CTRL_PAGE, B53_FAST_AGE_CTRL,
+ FAST_AGE_DONE | FAST_AGE_DYNAMIC | mask);
+
+ for (i = 0; i < 10; i++) {
+ u8 fast_age_ctrl;
+
+ b53_read8(dev, B53_CTRL_PAGE, B53_FAST_AGE_CTRL,
+ &fast_age_ctrl);
+
+ if (!(fast_age_ctrl & FAST_AGE_DONE))
+ goto out;
+
+ mdelay(1);
+ }
+
+ return -ETIMEDOUT;
+out:
+ /* Only age dynamic entries (default behavior) */
+ b53_write8(dev, B53_CTRL_PAGE, B53_FAST_AGE_CTRL, FAST_AGE_DYNAMIC);
+ return 0;
+}
+
+static int b53_switch_reset(struct phy_device *phydev)
+{
+ struct b53_device *dev = phydev->priv;
+ unsigned int timeout = 1000;
+ u8 mgmt;
+ u8 reg;
+
+ b53_read8(dev, B53_CTRL_PAGE, B53_SOFTRESET, &reg);
+ reg |= SW_RST | EN_SW_RST | EN_CH_RST;
+ b53_write8(dev, B53_CTRL_PAGE, B53_SOFTRESET, reg);
+
+ do {
+ b53_read8(dev, B53_CTRL_PAGE, B53_SOFTRESET, &reg);
+ if (!(reg & SW_RST))
+ break;
+
+ mdelay(1);
+ } while (timeout-- > 0);
+
+ if (timeout == 0)
+ return -ETIMEDOUT;
+
+ b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt);
+
+ if (!(mgmt & SM_SW_FWD_EN)) {
+ mgmt &= ~SM_SW_FWD_MODE;
+ mgmt |= SM_SW_FWD_EN;
+
+ b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, mgmt);
+ b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt);
+
+ if (!(mgmt & SM_SW_FWD_EN)) {
+ printf("Failed to enable switch!\n");
+ return -EINVAL;
+ }
+ }
+
+ /* Include IMP port in dumb forwarding mode when no tagging protocol
+ * is configured
+ */
+ b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_CTRL, &mgmt);
+ mgmt |= B53_MII_DUMB_FWDG_EN;
+ b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_CTRL, mgmt);
+
+ return b53_flush_arl(dev, FAST_AGE_STATIC);
+}
+
+static void b53_enable_cpu_port(struct phy_device *phydev)
+{
+ struct b53_device *dev = phydev->priv;
+ u8 port_ctrl;
+
+ port_ctrl = PORT_CTRL_RX_BCST_EN |
+ PORT_CTRL_RX_MCST_EN |
+ PORT_CTRL_RX_UCST_EN;
+ b53_write8(dev, B53_CTRL_PAGE, B53_PORT_CTRL(dev->cpu_port), port_ctrl);
+
+ port_ctrl = PORT_OVERRIDE_EN | PORT_OVERRIDE_LINK |
+ PORT_OVERRIDE_FULL_DUPLEX | PORT_OVERRIDE_SPEED_1000M;
+ b53_write8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL, port_ctrl);
+
+ b53_read8(dev, B53_CTRL_PAGE, B53_RGMII_CTRL_IMP, &port_ctrl);
+}
+
+static void b53_imp_vlan_setup(struct b53_device *dev, int cpu_port)
+{
+ unsigned int port;
+ u16 pvlan;
+
+ /* Enable the IMP port to be in the same VLAN as the other ports
+ * on a per-port basis such that we only have Port i and IMP in
+ * the same VLAN.
+ */
+ for (port = 0; port < B53_N_PORTS; port++) {
+ if (!((1 << port) & CONFIG_B53_PHY_PORTS))
+ continue;
+
+ b53_read16(dev, B53_PVLAN_PAGE, B53_PVLAN_PORT_MASK(port),
+ &pvlan);
+ pvlan |= BIT(cpu_port);
+ b53_write16(dev, B53_PVLAN_PAGE, B53_PVLAN_PORT_MASK(port),
+ pvlan);
+ }
+}
+
+static int b53_port_enable(struct phy_device *phydev, unsigned int port)
+{
+ struct b53_device *dev = phydev->priv;
+ unsigned int cpu_port = dev->cpu_port;
+ u16 pvlan;
+
+ /* Clear the Rx and Tx disable bits and set to no spanning tree */
+ b53_write8(dev, B53_CTRL_PAGE, B53_PORT_CTRL(port), 0);
+
+ /* Set this port, and only this one to be in the default VLAN */
+ b53_read16(dev, B53_PVLAN_PAGE, B53_PVLAN_PORT_MASK(port), &pvlan);
+ pvlan &= ~0x1ff;
+ pvlan |= BIT(port);
+ b53_write16(dev, B53_PVLAN_PAGE, B53_PVLAN_PORT_MASK(port), pvlan);
+
+ b53_imp_vlan_setup(dev, cpu_port);
+
+ return 0;
+}
+
+static int b53_switch_init(struct phy_device *phydev)
+{
+ static int init;
+ int ret;
+
+ if (init)
+ return 0;
+
+ ret = b53_switch_reset(phydev);
+ if (ret < 0)
+ return ret;
+
+ b53_enable_cpu_port(phydev);
+
+ init = 1;
+
+ return 0;
+}
+
+static int b53_probe(struct phy_device *phydev)
+{
+ struct b53_device *dev;
+ int ret;
+
+ dev = malloc(sizeof(*dev));
+ if (!dev)
+ return -ENOMEM;
+
+ memset(dev, 0, sizeof(*dev));
+
+ phydev->priv = dev;
+ dev->bus = phydev->bus;
+ dev->cpu_port = CONFIG_B53_CPU_PORT;
+
+ ret = b53_switch_reset(phydev);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int b53_phy_config(struct phy_device *phydev)
+{
+ unsigned int port;
+ int res;
+
+ res = b53_switch_init(phydev);
+ if (res < 0)
+ return res;
+
+ for (port = 0; port < B53_N_PORTS; port++) {
+ if (!((1 << port) & CONFIG_B53_PHY_PORTS))
+ continue;
+
+ res = b53_port_enable(phydev, port);
+ if (res < 0) {
+ printf("Error enabling port %i\n", port);
+ continue;
+ }
+
+ res = genphy_config_aneg(phydev);
+ if (res < 0) {
+ printf("Error setting PHY %i autoneg\n", port);
+ continue;
+ }
+
+ res = 0;
+ }
+
+ return res;
+}
+
+static int b53_phy_startup(struct phy_device *phydev)
+{
+ unsigned int port;
+ int res;
+
+ for (port = 0; port < B53_N_PORTS; port++) {
+ if (!((1 << port) & CONFIG_B53_PHY_PORTS))
+ continue;
+
+ phydev->addr = port;
+
+ res = genphy_startup(phydev);
+ if (res < 0)
+ continue;
+ else
+ break;
+ }
+
+ /* Since we are connected directly to the switch, hardcode the link
+ * parameters to match those of the CPU port configured in
+ * b53_enable_cpu_port, we cannot be dependent on the user-facing port
+ * settings (e.g: 100Mbits/sec would not work here)
+ */
+ phydev->speed = 1000;
+ phydev->duplex = 1;
+ phydev->link = 1;
+
+ return 0;
+}
+
+static struct phy_driver b53_driver = {
+ .name = "Broadcom BCM53125",
+ .uid = 0x03625c00,
+ .mask = 0xfffffc00,
+ .features = PHY_GBIT_FEATURES,
+ .probe = b53_probe,
+ .config = b53_phy_config,
+ .startup = b53_phy_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+int phy_b53_init(void)
+{
+ phy_register(&b53_driver);
+
+ return 0;
+}
+
+int do_b53_reg_read(const char *name, int argc, char *const argv[])
+{
+ u8 page, offset, width;
+ struct mii_dev *bus;
+ int ret = -EINVAL;
+ u64 value64 = 0;
+ u32 value32 = 0;
+ u16 value16 = 0;
+ u8 value8 = 0;
+
+ bus = miiphy_get_dev_by_name(name);
+ if (!bus) {
+ printf("unable to find MDIO bus: %s\n", name);
+ return ret;
+ }
+
+ page = simple_strtoul(argv[1], NULL, 16);
+ offset = simple_strtoul(argv[2], NULL, 16);
+ width = simple_strtoul(argv[3], NULL, 10);
+
+ switch (width) {
+ case 8:
+ ret = b53_mdio_read8(bus, page, offset, &value8);
+ printf("page=0x%02x, offset=0x%02x, value=0x%02x\n",
+ page, offset, value8);
+ break;
+ case 16:
+ ret = b53_mdio_read16(bus, page, offset, &value16);
+ printf("page=0x%02x, offset=0x%02x, value=0x%04x\n",
+ page, offset, value16);
+ break;
+ case 32:
+ ret = b53_mdio_read32(bus, page, offset, &value32);
+ printf("page=0x%02x, offset=0x%02x, value=0x%08x\n",
+ page, offset, value32);
+ break;
+ case 48:
+ ret = b53_mdio_read48(bus, page, offset, &value64);
+ printf("page=0x%02x, offset=0x%02x, value=0x%012llx\n",
+ page, offset, value64);
+ break;
+ case 64:
+ ret = b53_mdio_read48(bus, page, offset, &value64);
+ printf("page=0x%02x, offset=0x%02x, value=0x%016llx\n",
+ page, offset, value64);
+ break;
+ default:
+ printf("Unsupported width: %d\n", width);
+ break;
+ }
+
+ return ret;
+}
+
+int do_b53_reg_write(const char *name, int argc, char *const argv[])
+{
+ u8 page, offset, width;
+ struct mii_dev *bus;
+ int ret = -EINVAL;
+ u64 value64 = 0;
+ u32 value = 0;
+
+ bus = miiphy_get_dev_by_name(name);
+ if (!bus) {
+ printf("unable to find MDIO bus: %s\n", name);
+ return ret;
+ }
+
+ page = simple_strtoul(argv[1], NULL, 16);
+ offset = simple_strtoul(argv[2], NULL, 16);
+ width = simple_strtoul(argv[3], NULL, 10);
+ if (width == 48 || width == 64)
+ value64 = simple_strtoull(argv[4], NULL, 16);
+ else
+ value = simple_strtoul(argv[4], NULL, 16);
+
+ switch (width) {
+ case 8:
+ ret = b53_mdio_write8(bus, page, offset, value & 0xff);
+ break;
+ case 16:
+ ret = b53_mdio_write16(bus, page, offset, value);
+ break;
+ case 32:
+ ret = b53_mdio_write32(bus, page, offset, value);
+ break;
+ case 48:
+ ret = b53_mdio_write48(bus, page, offset, value64);
+ break;
+ case 64:
+ ret = b53_mdio_write64(bus, page, offset, value64);
+ break;
+ default:
+ printf("Unsupported width: %d\n", width);
+ break;
+ }
+
+ return ret;
+}
+
+int do_b53_reg(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ const char *cmd, *mdioname;
+ int ret = 0;
+
+ if (argc < 2)
+ return cmd_usage(cmdtp);
+
+ cmd = argv[1];
+ --argc;
+ ++argv;
+
+ if (!strcmp(cmd, "write")) {
+ if (argc < 4)
+ return cmd_usage(cmdtp);
+ mdioname = argv[1];
+ --argc;
+ ++argv;
+ ret = do_b53_reg_write(mdioname, argc, argv);
+ } else if (!strcmp(cmd, "read")) {
+ if (argc < 5)
+ return cmd_usage(cmdtp);
+ mdioname = argv[1];
+ --argc;
+ ++argv;
+ ret = do_b53_reg_read(mdioname, argc, argv);
+ } else {
+ return cmd_usage(cmdtp);
+ }
+
+ return ret;
+}
+
+U_BOOT_CMD(b53_reg, 7, 1, do_b53_reg,
+ "Broadcom B53 switch register access",
+ "write mdioname page (hex) offset (hex) width (dec) value (hex)\n"
+ "read mdioname page (hex) offset (hex) width (dec)\n"
+ );
diff --git a/roms/u-boot/drivers/net/phy/broadcom.c b/roms/u-boot/drivers/net/phy/broadcom.c
new file mode 100644
index 000000000..566fcb8de
--- /dev/null
+++ b/roms/u-boot/drivers/net/phy/broadcom.c
@@ -0,0 +1,374 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Broadcom PHY drivers
+ *
+ * Copyright 2010-2011 Freescale Semiconductor, Inc.
+ * author Andy Fleming
+ */
+#include <common.h>
+#include <phy.h>
+#include <linux/delay.h>
+
+/* Broadcom BCM54xx -- taken from linux sungem_phy */
+#define MIIM_BCM54xx_AUXCNTL 0x18
+#define MIIM_BCM54xx_AUXCNTL_ENCODE(val) (((val & 0x7) << 12)|(val & 0x7))
+#define MIIM_BCM54xx_AUXSTATUS 0x19
+#define MIIM_BCM54xx_AUXSTATUS_LINKMODE_MASK 0x0700
+#define MIIM_BCM54xx_AUXSTATUS_LINKMODE_SHIFT 8
+
+#define MIIM_BCM54XX_SHD 0x1c
+#define MIIM_BCM54XX_SHD_WRITE 0x8000
+#define MIIM_BCM54XX_SHD_VAL(x) ((x & 0x1f) << 10)
+#define MIIM_BCM54XX_SHD_DATA(x) ((x & 0x3ff) << 0)
+#define MIIM_BCM54XX_SHD_WR_ENCODE(val, data) \
+ (MIIM_BCM54XX_SHD_WRITE | MIIM_BCM54XX_SHD_VAL(val) | \
+ MIIM_BCM54XX_SHD_DATA(data))
+
+#define MIIM_BCM54XX_EXP_DATA 0x15 /* Expansion register data */
+#define MIIM_BCM54XX_EXP_SEL 0x17 /* Expansion register select */
+#define MIIM_BCM54XX_EXP_SEL_SSD 0x0e00 /* Secondary SerDes select */
+#define MIIM_BCM54XX_EXP_SEL_ER 0x0f00 /* Expansion register select */
+
+#define MIIM_BCM_AUXCNTL_SHDWSEL_MISC 0x0007
+#define MIIM_BCM_AUXCNTL_ACTL_SMDSP_EN 0x0800
+
+#define MIIM_BCM_CHANNEL_WIDTH 0x2000
+
+static void bcm_phy_write_misc(struct phy_device *phydev,
+ u16 reg, u16 chl, u16 value)
+{
+ int reg_val;
+
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54xx_AUXCNTL,
+ MIIM_BCM_AUXCNTL_SHDWSEL_MISC);
+
+ reg_val = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_BCM54xx_AUXCNTL);
+ reg_val |= MIIM_BCM_AUXCNTL_ACTL_SMDSP_EN;
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54xx_AUXCNTL, reg_val);
+
+ reg_val = (chl * MIIM_BCM_CHANNEL_WIDTH) | reg;
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_EXP_SEL, reg_val);
+
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_EXP_DATA, value);
+}
+
+/* Broadcom BCM5461S */
+static int bcm5461_config(struct phy_device *phydev)
+{
+ genphy_config_aneg(phydev);
+
+ phy_reset(phydev);
+
+ return 0;
+}
+
+static int bcm54xx_parse_status(struct phy_device *phydev)
+{
+ unsigned int mii_reg;
+
+ mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_BCM54xx_AUXSTATUS);
+
+ switch ((mii_reg & MIIM_BCM54xx_AUXSTATUS_LINKMODE_MASK) >>
+ MIIM_BCM54xx_AUXSTATUS_LINKMODE_SHIFT) {
+ case 1:
+ phydev->duplex = DUPLEX_HALF;
+ phydev->speed = SPEED_10;
+ break;
+ case 2:
+ phydev->duplex = DUPLEX_FULL;
+ phydev->speed = SPEED_10;
+ break;
+ case 3:
+ phydev->duplex = DUPLEX_HALF;
+ phydev->speed = SPEED_100;
+ break;
+ case 5:
+ phydev->duplex = DUPLEX_FULL;
+ phydev->speed = SPEED_100;
+ break;
+ case 6:
+ phydev->duplex = DUPLEX_HALF;
+ phydev->speed = SPEED_1000;
+ break;
+ case 7:
+ phydev->duplex = DUPLEX_FULL;
+ phydev->speed = SPEED_1000;
+ break;
+ default:
+ printf("Auto-neg error, defaulting to 10BT/HD\n");
+ phydev->duplex = DUPLEX_HALF;
+ phydev->speed = SPEED_10;
+ break;
+ }
+
+ return 0;
+}
+
+static int bcm54xx_startup(struct phy_device *phydev)
+{
+ int ret;
+
+ /* Read the Status (2x to make sure link is right) */
+ ret = genphy_update_link(phydev);
+ if (ret)
+ return ret;
+
+ return bcm54xx_parse_status(phydev);
+}
+
+/* Broadcom BCM5482S */
+/*
+ * "Ethernet@Wirespeed" needs to be enabled to achieve link in certain
+ * circumstances. eg a gigabit TSEC connected to a gigabit switch with
+ * a 4-wire ethernet cable. Both ends advertise gigabit, but can't
+ * link. "Ethernet@Wirespeed" reduces advertised speed until link
+ * can be achieved.
+ */
+static u32 bcm5482_read_wirespeed(struct phy_device *phydev, u32 reg)
+{
+ return (phy_read(phydev, MDIO_DEVAD_NONE, reg) & 0x8FFF) | 0x8010;
+}
+
+static int bcm5482_config(struct phy_device *phydev)
+{
+ unsigned int reg;
+
+ /* reset the PHY */
+ reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR);
+ reg |= BMCR_RESET;
+ phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, reg);
+
+ /* Setup read from auxilary control shadow register 7 */
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54xx_AUXCNTL,
+ MIIM_BCM54xx_AUXCNTL_ENCODE(7));
+ /* Read Misc Control register and or in Ethernet@Wirespeed */
+ reg = bcm5482_read_wirespeed(phydev, MIIM_BCM54xx_AUXCNTL);
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54xx_AUXCNTL, reg);
+
+ /* Initial config/enable of secondary SerDes interface */
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_SHD,
+ MIIM_BCM54XX_SHD_WR_ENCODE(0x14, 0xf));
+ /* Write intial value to secondary SerDes Contol */
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_EXP_SEL,
+ MIIM_BCM54XX_EXP_SEL_SSD | 0);
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_EXP_DATA,
+ BMCR_ANRESTART);
+ /* Enable copper/fiber auto-detect */
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_SHD,
+ MIIM_BCM54XX_SHD_WR_ENCODE(0x1e, 0x201));
+
+ genphy_config_aneg(phydev);
+
+ return 0;
+}
+
+static int bcm_cygnus_startup(struct phy_device *phydev)
+{
+ int ret;
+
+ /* Read the Status (2x to make sure link is right) */
+ ret = genphy_update_link(phydev);
+ if (ret)
+ return ret;
+
+ return genphy_parse_link(phydev);
+}
+
+static void bcm_cygnus_afe(struct phy_device *phydev)
+{
+ /* ensures smdspclk is enabled */
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54xx_AUXCNTL, 0x0c30);
+
+ /* AFE_VDAC_ICTRL_0 bit 7:4 Iq=1100 for 1g 10bt, normal modes */
+ bcm_phy_write_misc(phydev, 0x39, 0x01, 0xA7C8);
+
+ /* AFE_HPF_TRIM_OTHERS bit11=1, short cascode for all modes*/
+ bcm_phy_write_misc(phydev, 0x3A, 0x00, 0x0803);
+
+ /* AFE_TX_CONFIG_1 bit 7:4 Iq=1100 for test modes */
+ bcm_phy_write_misc(phydev, 0x3A, 0x01, 0xA740);
+
+ /* AFE TEMPSEN_OTHERS rcal_HT, rcal_LT 10000 */
+ bcm_phy_write_misc(phydev, 0x3A, 0x03, 0x8400);
+
+ /* AFE_FUTURE_RSV bit 2:0 rccal <2:0>=100 */
+ bcm_phy_write_misc(phydev, 0x3B, 0x00, 0x0004);
+
+ /* Adjust bias current trim to overcome digital offSet */
+ phy_write(phydev, MDIO_DEVAD_NONE, 0x1E, 0x02);
+
+ /* make rcal=100, since rdb default is 000 */
+ phy_write(phydev, MDIO_DEVAD_NONE, 0x17, 0x00B1);
+ phy_write(phydev, MDIO_DEVAD_NONE, 0x15, 0x0010);
+
+ /* CORE_EXPB0, Reset R_CAL/RC_CAL Engine */
+ phy_write(phydev, MDIO_DEVAD_NONE, 0x17, 0x00B0);
+ phy_write(phydev, MDIO_DEVAD_NONE, 0x15, 0x0010);
+
+ /* CORE_EXPB0, Disable Reset R_CAL/RC_CAL Engine */
+ phy_write(phydev, MDIO_DEVAD_NONE, 0x17, 0x00B0);
+ phy_write(phydev, MDIO_DEVAD_NONE, 0x15, 0x0000);
+}
+
+static int bcm_cygnus_config(struct phy_device *phydev)
+{
+ genphy_config_aneg(phydev);
+ phy_reset(phydev);
+ /* AFE settings for PHY stability */
+ bcm_cygnus_afe(phydev);
+ /* Forcing aneg after applying the AFE settings */
+ genphy_restart_aneg(phydev);
+
+ return 0;
+}
+
+/*
+ * Find out if PHY is in copper or serdes mode by looking at Expansion Reg
+ * 0x42 - "Operating Mode Status Register"
+ */
+static int bcm5482_is_serdes(struct phy_device *phydev)
+{
+ u16 val;
+ int serdes = 0;
+
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_EXP_SEL,
+ MIIM_BCM54XX_EXP_SEL_ER | 0x42);
+ val = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_EXP_DATA);
+
+ switch (val & 0x1f) {
+ case 0x0d: /* RGMII-to-100Base-FX */
+ case 0x0e: /* RGMII-to-SGMII */
+ case 0x0f: /* RGMII-to-SerDes */
+ case 0x12: /* SGMII-to-SerDes */
+ case 0x13: /* SGMII-to-100Base-FX */
+ case 0x16: /* SerDes-to-Serdes */
+ serdes = 1;
+ break;
+ case 0x6: /* RGMII-to-Copper */
+ case 0x14: /* SGMII-to-Copper */
+ case 0x17: /* SerDes-to-Copper */
+ break;
+ default:
+ printf("ERROR, invalid PHY mode (0x%x\n)", val);
+ break;
+ }
+
+ return serdes;
+}
+
+/*
+ * Determine SerDes link speed and duplex from Expansion reg 0x42 "Operating
+ * Mode Status Register"
+ */
+static u32 bcm5482_parse_serdes_sr(struct phy_device *phydev)
+{
+ u16 val;
+ int i = 0;
+
+ /* Wait 1s for link - Clause 37 autonegotiation happens very fast */
+ while (1) {
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_EXP_SEL,
+ MIIM_BCM54XX_EXP_SEL_ER | 0x42);
+ val = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_EXP_DATA);
+
+ if (val & 0x8000)
+ break;
+
+ if (i++ > 1000) {
+ phydev->link = 0;
+ return 1;
+ }
+
+ udelay(1000); /* 1 ms */
+ }
+
+ phydev->link = 1;
+ switch ((val >> 13) & 0x3) {
+ case (0x00):
+ phydev->speed = 10;
+ break;
+ case (0x01):
+ phydev->speed = 100;
+ break;
+ case (0x02):
+ phydev->speed = 1000;
+ break;
+ }
+
+ phydev->duplex = (val & 0x1000) == 0x1000;
+
+ return 0;
+}
+
+/*
+ * Figure out if BCM5482 is in serdes or copper mode and determine link
+ * configuration accordingly
+ */
+static int bcm5482_startup(struct phy_device *phydev)
+{
+ int ret;
+
+ if (bcm5482_is_serdes(phydev)) {
+ bcm5482_parse_serdes_sr(phydev);
+ phydev->port = PORT_FIBRE;
+ return 0;
+ }
+
+ /* Wait for auto-negotiation to complete or fail */
+ ret = genphy_update_link(phydev);
+ if (ret)
+ return ret;
+
+ /* Parse BCM54xx copper aux status register */
+ return bcm54xx_parse_status(phydev);
+}
+
+static struct phy_driver BCM5461S_driver = {
+ .name = "Broadcom BCM5461S",
+ .uid = 0x2060c0,
+ .mask = 0xfffff0,
+ .features = PHY_GBIT_FEATURES,
+ .config = &bcm5461_config,
+ .startup = &bcm54xx_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+static struct phy_driver BCM5464S_driver = {
+ .name = "Broadcom BCM5464S",
+ .uid = 0x2060b0,
+ .mask = 0xfffff0,
+ .features = PHY_GBIT_FEATURES,
+ .config = &bcm5461_config,
+ .startup = &bcm54xx_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+static struct phy_driver BCM5482S_driver = {
+ .name = "Broadcom BCM5482S",
+ .uid = 0x143bcb0,
+ .mask = 0xffffff0,
+ .features = PHY_GBIT_FEATURES,
+ .config = &bcm5482_config,
+ .startup = &bcm5482_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+static struct phy_driver BCM_CYGNUS_driver = {
+ .name = "Broadcom CYGNUS GPHY",
+ .uid = 0xae025200,
+ .mask = 0xfffff0,
+ .features = PHY_GBIT_FEATURES,
+ .config = &bcm_cygnus_config,
+ .startup = &bcm_cygnus_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+int phy_broadcom_init(void)
+{
+ phy_register(&BCM5482S_driver);
+ phy_register(&BCM5464S_driver);
+ phy_register(&BCM5461S_driver);
+ phy_register(&BCM_CYGNUS_driver);
+
+ return 0;
+}
diff --git a/roms/u-boot/drivers/net/phy/ca_phy.c b/roms/u-boot/drivers/net/phy/ca_phy.c
new file mode 100644
index 000000000..16851a682
--- /dev/null
+++ b/roms/u-boot/drivers/net/phy/ca_phy.c
@@ -0,0 +1,133 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Cortina CS4315/CS4340 10G PHY drivers
+ *
+ * Copyright 2014 Freescale Semiconductor, Inc.
+ * Copyright 2018 NXP
+ *
+ */
+
+#include <config.h>
+#include <common.h>
+#include <log.h>
+#include <malloc.h>
+#include <linux/ctype.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/err.h>
+#include <phy.h>
+
+#define PHY_ID_RTL8211_EXT 0x001cc910
+#define PHY_ID_RTL8211_INT 0x001cc980
+#define PHY_ID_MASK 0xFFFFFFF0
+
+static void __internal_phy_init(struct phy_device *phydev, int reset_phy)
+{
+ u8 phy_addr;
+ u16 data;
+
+ /* should initialize 4 GPHYs at once */
+ for (phy_addr = 4; phy_addr > 0; phy_addr--) {
+ phydev->addr = phy_addr;
+ phy_write(phydev, MDIO_DEVAD_NONE, 31, 0x0BC6);
+ phy_write(phydev, MDIO_DEVAD_NONE, 16, 0x0053);
+ phy_write(phydev, MDIO_DEVAD_NONE, 18, 0x4003);
+ phy_write(phydev, MDIO_DEVAD_NONE, 22, 0x7e01);
+ phy_write(phydev, MDIO_DEVAD_NONE, 31, 0x0A42);
+ phy_write(phydev, MDIO_DEVAD_NONE, 31, 0x0A40);
+ phy_write(phydev, MDIO_DEVAD_NONE, 0, 0x1140);
+ }
+
+ /* workaround to fix GPHY fail */
+ for (phy_addr = 1; phy_addr < 5; phy_addr++) {
+ /* Clear clock fail interrupt */
+ phydev->addr = phy_addr;
+ phy_write(phydev, MDIO_DEVAD_NONE, 31, 0xB90);
+ data = phy_read(phydev, MDIO_DEVAD_NONE, 19);
+ if (data == 0x10) {
+ phy_write(phydev, MDIO_DEVAD_NONE, 31, 0xB90);
+ data = phy_read(phydev, MDIO_DEVAD_NONE, 19);
+ printf("%s: read again.\n", __func__);
+ }
+
+ printf("%s: phy_addr=%d, read register 19, value=0x%x\n",
+ __func__, phy_addr, data);
+ }
+}
+
+static void __external_phy_init(struct phy_device *phydev, int reset_phy)
+{
+ u16 val;
+
+ /* Disable response PHYAD=0 function of RTL8211 series PHY */
+ /* REG31 write 0x0007, set to extension page */
+ phy_write(phydev, MDIO_DEVAD_NONE, 31, 0x0007);
+
+ /* REG30 write 0x002C, set to extension page 44 */
+ phy_write(phydev, MDIO_DEVAD_NONE, 30, 0x002C);
+
+ /*
+ * REG27 write bit[2] = 0 disable response PHYAD = 0 function.
+ * we should read REG27 and clear bit[2], and write back
+ */
+ val = phy_read(phydev, MDIO_DEVAD_NONE, 27);
+ val &= ~(1 << 2);
+ phy_write(phydev, MDIO_DEVAD_NONE, 27, val);
+
+ /* REG31 write 0X0000, back to page0 */
+ phy_write(phydev, MDIO_DEVAD_NONE, 31, 0x0000);
+}
+
+static int rtl8211_external_config(struct phy_device *phydev)
+{
+ __external_phy_init(phydev, 0);
+ printf("%s: initialize RTL8211 external done.\n", __func__);
+ return 0;
+}
+
+static int rtl8211_internal_config(struct phy_device *phydev)
+{
+ struct phy_device phydev_init;
+
+ memcpy(&phydev_init, phydev, sizeof(struct phy_device));
+ /* should initialize 4 GPHYs at once */
+ __internal_phy_init(&phydev_init, 0);
+ printf("%s: initialize RTL8211 internal done.\n", __func__);
+ return 0;
+}
+
+static int rtl8211_probe(struct phy_device *phydev)
+{
+ /* disable reset behavior */
+ phydev->flags = PHY_FLAG_BROKEN_RESET;
+ return 0;
+}
+
+/* Support for RTL8211 External PHY */
+struct phy_driver rtl8211_external_driver = {
+ .name = "Cortina RTL8211 External",
+ .uid = PHY_ID_RTL8211_EXT,
+ .mask = PHY_ID_MASK,
+ .features = PHY_GBIT_FEATURES,
+ .config = &rtl8211_external_config,
+ .probe = &rtl8211_probe,
+ .startup = &genphy_startup,
+};
+
+/* Support for RTL8211 Internal PHY */
+struct phy_driver rtl8211_internal_driver = {
+ .name = "Cortina RTL8211 Inrernal",
+ .uid = PHY_ID_RTL8211_INT,
+ .mask = PHY_ID_MASK,
+ .features = PHY_GBIT_FEATURES,
+ .config = &rtl8211_internal_config,
+ .probe = &rtl8211_probe,
+ .startup = &genphy_startup,
+};
+
+int phy_cortina_access_init(void)
+{
+ phy_register(&rtl8211_external_driver);
+ phy_register(&rtl8211_internal_driver);
+ return 0;
+}
diff --git a/roms/u-boot/drivers/net/phy/cortina.c b/roms/u-boot/drivers/net/phy/cortina.c
new file mode 100644
index 000000000..b381a431f
--- /dev/null
+++ b/roms/u-boot/drivers/net/phy/cortina.c
@@ -0,0 +1,393 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Cortina CS4315/CS4340 10G PHY drivers
+ *
+ * Copyright 2014 Freescale Semiconductor, Inc.
+ * Copyright 2018, 2020 NXP
+ *
+ */
+
+#include <config.h>
+#include <common.h>
+#include <log.h>
+#include <malloc.h>
+#include <linux/ctype.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/err.h>
+#include <phy.h>
+#include <cortina.h>
+#ifdef CONFIG_SYS_CORTINA_FW_IN_NAND
+#include <nand.h>
+#elif defined(CONFIG_SYS_CORTINA_FW_IN_SPIFLASH)
+#include <spi_flash.h>
+#elif defined(CONFIG_SYS_CORTINA_FW_IN_MMC)
+#include <mmc.h>
+#endif
+
+#ifndef CONFIG_PHYLIB_10G
+#error The Cortina PHY needs 10G support
+#endif
+
+#ifndef CONFIG_SYS_CORTINA_NO_FW_UPLOAD
+struct cortina_reg_config cortina_reg_cfg[] = {
+ /* CS4315_enable_sr_mode */
+ {VILLA_GLOBAL_MSEQCLKCTRL, 0x8004},
+ {VILLA_MSEQ_OPTIONS, 0xf},
+ {VILLA_MSEQ_PC, 0x0},
+ {VILLA_MSEQ_BANKSELECT, 0x4},
+ {VILLA_LINE_SDS_COMMON_SRX0_RX_CPA, 0x55},
+ {VILLA_LINE_SDS_COMMON_SRX0_RX_LOOP_FILTER, 0x30},
+ {VILLA_DSP_SDS_SERDES_SRX_DFE0_SELECT, 0x1},
+ {VILLA_DSP_SDS_DSP_COEF_DFE0_SELECT, 0x2},
+ {VILLA_LINE_SDS_COMMON_SRX0_RX_CPB, 0x2003},
+ {VILLA_DSP_SDS_SERDES_SRX_FFE_DELAY_CTRL, 0xF047},
+ {VILLA_MSEQ_ENABLE_MSB, 0x0000},
+ {VILLA_MSEQ_SPARE21_LSB, 0x6},
+ {VILLA_MSEQ_RESET_COUNT_LSB, 0x0},
+ {VILLA_MSEQ_SPARE12_MSB, 0x0000},
+ /*
+ * to invert the receiver path, uncomment the next line
+ * write (VILLA_MSEQ_SPARE12_MSB, 0x4000)
+ *
+ * SPARE2_LSB is used to configure the device while in sr mode to
+ * enable power savings and to use the optical module LOS signal.
+ * in power savings mode, the internal prbs checker can not be used.
+ * if the optical module LOS signal is used as an input to the micro
+ * code, then the micro code will wait until the optical module
+ * LOS = 0 before turning on the adaptive equalizer.
+ * Setting SPARE2_LSB bit 0 to 1 places the devie in power savings mode
+ * while setting bit 0 to 0 disables power savings mode.
+ * Setting SPARE2_LSB bit 2 to 0 configures the device to use the
+ * optical module LOS signal while setting bit 2 to 1 configures the
+ * device so that it will ignore the optical module LOS SPARE2_LSB = 0
+ */
+
+ /* enable power savings, ignore optical module LOS */
+ {VILLA_MSEQ_SPARE2_LSB, 0x5},
+
+ {VILLA_MSEQ_SPARE7_LSB, 0x1e},
+ {VILLA_MSEQ_BANKSELECT, 0x4},
+ {VILLA_MSEQ_SPARE9_LSB, 0x2},
+ {VILLA_MSEQ_SPARE3_LSB, 0x0F53},
+ {VILLA_MSEQ_SPARE3_MSB, 0x2006},
+ {VILLA_MSEQ_SPARE8_LSB, 0x3FF7},
+ {VILLA_MSEQ_SPARE8_MSB, 0x0A46},
+ {VILLA_MSEQ_COEF8_FFE0_LSB, 0xD500},
+ {VILLA_MSEQ_COEF8_FFE1_LSB, 0x0200},
+ {VILLA_MSEQ_COEF8_FFE2_LSB, 0xBA00},
+ {VILLA_MSEQ_COEF8_FFE3_LSB, 0x0100},
+ {VILLA_MSEQ_COEF8_FFE4_LSB, 0x0300},
+ {VILLA_MSEQ_COEF8_FFE5_LSB, 0x0300},
+ {VILLA_MSEQ_COEF8_DFE0_LSB, 0x0700},
+ {VILLA_MSEQ_COEF8_DFE0N_LSB, 0x0E00},
+ {VILLA_MSEQ_COEF8_DFE1_LSB, 0x0B00},
+ {VILLA_DSP_SDS_DSP_COEF_LARGE_LEAK, 0x2},
+ {VILLA_DSP_SDS_SERDES_SRX_DAC_ENABLEB_LSB, 0xD000},
+ {VILLA_MSEQ_POWER_DOWN_LSB, 0xFFFF},
+ {VILLA_MSEQ_POWER_DOWN_MSB, 0x0},
+ {VILLA_MSEQ_CAL_RX_SLICER, 0x80},
+ {VILLA_DSP_SDS_SERDES_SRX_DAC_BIAS_SELECT1_MSB, 0x3f},
+ {VILLA_GLOBAL_MSEQCLKCTRL, 0x4},
+ {VILLA_MSEQ_OPTIONS, 0x7},
+
+ /* set up min value for ffe1 */
+ {VILLA_MSEQ_COEF_INIT_SEL, 0x2},
+ {VILLA_DSP_SDS_DSP_PRECODEDINITFFE21, 0x41},
+
+ /* CS4315_sr_rx_pre_eq_set_4in */
+ {VILLA_GLOBAL_MSEQCLKCTRL, 0x8004},
+ {VILLA_MSEQ_OPTIONS, 0xf},
+ {VILLA_MSEQ_BANKSELECT, 0x4},
+ {VILLA_MSEQ_PC, 0x0},
+
+ /* for lengths from 3.5 to 4.5inches */
+ {VILLA_MSEQ_SERDES_PARAM_LSB, 0x0306},
+ {VILLA_MSEQ_SPARE25_LSB, 0x0306},
+ {VILLA_MSEQ_SPARE21_LSB, 0x2},
+ {VILLA_MSEQ_SPARE23_LSB, 0x2},
+ {VILLA_MSEQ_CAL_RX_DFE_EQ, 0x0},
+
+ {VILLA_GLOBAL_MSEQCLKCTRL, 0x4},
+ {VILLA_MSEQ_OPTIONS, 0x7},
+
+ /* CS4315_rx_drive_4inch */
+ /* for length 4inches */
+ {VILLA_GLOBAL_VILLA2_COMPATIBLE, 0x0000},
+ {VILLA_HOST_SDS_COMMON_STX0_TX_OUTPUT_CTRLA, 0x3023},
+ {VILLA_LINE_SDS_COMMON_STX0_TX_OUTPUT_CTRLB, 0xc01E},
+
+ /* CS4315_tx_drive_4inch */
+ /* for length 4inches */
+ {VILLA_GLOBAL_VILLA2_COMPATIBLE, 0x0000},
+ {VILLA_LINE_SDS_COMMON_STX0_TX_OUTPUT_CTRLA, 0x3023},
+ {VILLA_LINE_SDS_COMMON_STX0_TX_OUTPUT_CTRLB, 0xc01E},
+};
+
+void cs4340_upload_firmware(struct phy_device *phydev)
+{
+ char line_temp[0x50] = {0};
+ char reg_addr[0x50] = {0};
+ char reg_data[0x50] = {0};
+ int i, line_cnt = 0, column_cnt = 0;
+ struct cortina_reg_config fw_temp;
+ char *addr = NULL;
+
+#if defined(CONFIG_SYS_CORTINA_FW_IN_NOR) || \
+ defined(CONFIG_SYS_CORTINA_FW_IN_REMOTE)
+
+ addr = (char *)CONFIG_CORTINA_FW_ADDR;
+#elif defined(CONFIG_SYS_CORTINA_FW_IN_NAND)
+ int ret;
+ size_t fw_length = CONFIG_CORTINA_FW_LENGTH;
+
+ addr = malloc(CONFIG_CORTINA_FW_LENGTH);
+ ret = nand_read(get_nand_dev_by_index(0),
+ (loff_t)CONFIG_CORTINA_FW_ADDR,
+ &fw_length, (u_char *)addr);
+ if (ret == -EUCLEAN) {
+ printf("NAND read of Cortina firmware at 0x%x failed %d\n",
+ CONFIG_CORTINA_FW_ADDR, ret);
+ }
+#elif defined(CONFIG_SYS_CORTINA_FW_IN_SPIFLASH)
+ int ret;
+ struct spi_flash *ucode_flash;
+
+ addr = malloc(CONFIG_CORTINA_FW_LENGTH);
+ ucode_flash = spi_flash_probe(CONFIG_ENV_SPI_BUS, CONFIG_ENV_SPI_CS,
+ CONFIG_ENV_SPI_MAX_HZ, CONFIG_ENV_SPI_MODE);
+ if (!ucode_flash) {
+ puts("SF: probe for Cortina ucode failed\n");
+ } else {
+ ret = spi_flash_read(ucode_flash, CONFIG_CORTINA_FW_ADDR,
+ CONFIG_CORTINA_FW_LENGTH, addr);
+ if (ret)
+ puts("SF: read for Cortina ucode failed\n");
+ spi_flash_free(ucode_flash);
+ }
+#elif defined(CONFIG_SYS_CORTINA_FW_IN_MMC)
+ int dev = CONFIG_SYS_MMC_ENV_DEV;
+ u32 cnt = CONFIG_CORTINA_FW_LENGTH / 512;
+ u32 blk = CONFIG_CORTINA_FW_ADDR / 512;
+ struct mmc *mmc = find_mmc_device(CONFIG_SYS_MMC_ENV_DEV);
+
+ if (!mmc) {
+ puts("Failed to find MMC device for Cortina ucode\n");
+ } else {
+ addr = malloc(CONFIG_CORTINA_FW_LENGTH);
+ printf("MMC read: dev # %u, block # %u, count %u ...\n",
+ dev, blk, cnt);
+ mmc_init(mmc);
+#ifdef CONFIG_BLK
+ (void)blk_dread(mmc_get_blk_desc(mmc), blk, cnt,
+ addr);
+#else
+ (void)mmc->block_dev.block_read(&mmc->block_dev, blk, cnt,
+ addr);
+#endif
+ }
+#endif
+
+ while (*addr != 'Q') {
+ i = 0;
+
+ while (*addr != 0x0a) {
+ line_temp[i++] = *addr++;
+ if (0x50 < i) {
+ printf("Not found Cortina PHY ucode at 0x%p\n",
+ (char *)CONFIG_CORTINA_FW_ADDR);
+ return;
+ }
+ }
+
+ addr++; /* skip '\n' */
+ line_cnt++;
+ column_cnt = i;
+ line_temp[column_cnt] = '\0';
+
+ if (CONFIG_CORTINA_FW_LENGTH < line_cnt)
+ return;
+
+ for (i = 0; i < column_cnt; i++) {
+ if (isspace(line_temp[i++]))
+ break;
+ }
+
+ memcpy(reg_addr, line_temp, i);
+ memcpy(reg_data, &line_temp[i], column_cnt - i);
+ strim(reg_addr);
+ strim(reg_data);
+ fw_temp.reg_addr = (simple_strtoul(reg_addr, NULL, 0)) & 0xffff;
+ fw_temp.reg_value = (simple_strtoul(reg_data, NULL, 0)) &
+ 0xffff;
+ phy_write(phydev, 0x00, fw_temp.reg_addr, fw_temp.reg_value);
+ }
+}
+#endif
+
+int cs4340_phy_init(struct phy_device *phydev)
+{
+#ifndef CONFIG_SYS_CORTINA_NO_FW_UPLOAD
+ int timeout = 100; /* 100ms */
+#endif
+ int reg_value;
+
+ /*
+ * Cortina phy has provision to store
+ * phy firmware in attached dedicated EEPROM.
+ * Boards designed with EEPROM attached to Cortina
+ * does not require FW upload.
+ */
+#ifndef CONFIG_SYS_CORTINA_NO_FW_UPLOAD
+ /* step1: BIST test */
+ phy_write(phydev, 0x00, VILLA_GLOBAL_MSEQCLKCTRL, 0x0004);
+ phy_write(phydev, 0x00, VILLA_GLOBAL_LINE_SOFT_RESET, 0x0000);
+ phy_write(phydev, 0x00, VILLA_GLOBAL_BIST_CONTROL, 0x0001);
+ while (--timeout) {
+ reg_value = phy_read(phydev, 0x00, VILLA_GLOBAL_BIST_STATUS);
+ if (reg_value & mseq_edc_bist_done) {
+ if (0 == (reg_value & mseq_edc_bist_fail))
+ break;
+ }
+ udelay(1000);
+ }
+
+ if (!timeout) {
+ printf("%s BIST mseq_edc_bist_done timeout!\n", __func__);
+ return -1;
+ }
+
+ /* setp2: upload ucode */
+ cs4340_upload_firmware(phydev);
+#endif
+ reg_value = phy_read(phydev, 0x00, VILLA_GLOBAL_DWNLD_CHECKSUM_STATUS);
+ if (reg_value) {
+ debug("%s checksum status failed.\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int cs4340_config(struct phy_device *phydev)
+{
+ cs4340_phy_init(phydev);
+ return 0;
+}
+
+int cs4340_probe(struct phy_device *phydev)
+{
+ phydev->flags = PHY_FLAG_BROKEN_RESET;
+ return 0;
+}
+
+int cs4340_startup(struct phy_device *phydev)
+{
+ phydev->link = 1;
+
+ /* For now just lie and say it's 10G all the time */
+ phydev->speed = SPEED_10000;
+ phydev->duplex = DUPLEX_FULL;
+ return 0;
+}
+
+int cs4223_phy_init(struct phy_device *phydev)
+{
+ int reg_value;
+
+ reg_value = phy_read(phydev, 0x00, CS4223_EEPROM_STATUS);
+ if (!(reg_value & CS4223_EEPROM_FIRMWARE_LOADDONE)) {
+ printf("%s CS4223 Firmware not present in EERPOM\n", __func__);
+ return -ENOSYS;
+ }
+
+ return 0;
+}
+
+int cs4223_config(struct phy_device *phydev)
+{
+ return cs4223_phy_init(phydev);
+}
+
+int cs4223_probe(struct phy_device *phydev)
+{
+ phydev->flags = PHY_FLAG_BROKEN_RESET;
+ return 0;
+}
+
+int cs4223_startup(struct phy_device *phydev)
+{
+ phydev->link = 1;
+ phydev->speed = SPEED_10000;
+ phydev->duplex = DUPLEX_FULL;
+ return 0;
+}
+
+struct phy_driver cs4340_driver = {
+ .name = "Cortina CS4315/CS4340",
+ .uid = PHY_UID_CS4340,
+ .mask = 0xfffffff0,
+ .features = PHY_10G_FEATURES,
+ .mmds = (MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS |
+ MDIO_DEVS_PHYXS | MDIO_DEVS_AN |
+ MDIO_DEVS_VEND1 | MDIO_DEVS_VEND2),
+ .config = &cs4340_config,
+ .probe = &cs4340_probe,
+ .startup = &cs4340_startup,
+ .shutdown = &gen10g_shutdown,
+};
+
+struct phy_driver cs4223_driver = {
+ .name = "Cortina CS4223",
+ .uid = PHY_UID_CS4223,
+ .mask = 0x0ffff00f,
+ .features = PHY_10G_FEATURES,
+ .mmds = (MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS |
+ MDIO_DEVS_AN),
+ .config = &cs4223_config,
+ .probe = &cs4223_probe,
+ .startup = &cs4223_startup,
+ .shutdown = &gen10g_shutdown,
+};
+
+int phy_cortina_init(void)
+{
+ phy_register(&cs4340_driver);
+ phy_register(&cs4223_driver);
+ return 0;
+}
+
+int get_phy_id(struct mii_dev *bus, int addr, int devad, u32 *phy_id)
+{
+ int phy_reg;
+
+ /* Cortina PHY has non-standard offset of PHY ID registers */
+ phy_reg = bus->read(bus, addr, 0, VILLA_GLOBAL_CHIP_ID_LSB);
+ if (phy_reg < 0)
+ return -EIO;
+ *phy_id = (phy_reg & 0xffff) << 16;
+
+ phy_reg = bus->read(bus, addr, 0, VILLA_GLOBAL_CHIP_ID_MSB);
+ if (phy_reg < 0)
+ return -EIO;
+ *phy_id |= (phy_reg & 0xffff);
+
+ if ((*phy_id == PHY_UID_CS4340) || (*phy_id == PHY_UID_CS4223))
+ return 0;
+
+ /*
+ * If Cortina PHY not detected,
+ * try generic way to find PHY ID registers
+ */
+ phy_reg = bus->read(bus, addr, devad, MII_PHYSID1);
+ if (phy_reg < 0)
+ return -EIO;
+ *phy_id = (phy_reg & 0xffff) << 16;
+
+ phy_reg = bus->read(bus, addr, devad, MII_PHYSID2);
+ if (phy_reg < 0)
+ return -EIO;
+ *phy_id |= (phy_reg & 0xffff);
+
+ return 0;
+}
diff --git a/roms/u-boot/drivers/net/phy/davicom.c b/roms/u-boot/drivers/net/phy/davicom.c
new file mode 100644
index 000000000..4666497d4
--- /dev/null
+++ b/roms/u-boot/drivers/net/phy/davicom.c
@@ -0,0 +1,87 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Davicom PHY drivers
+ *
+ * Copyright 2010-2011 Freescale Semiconductor, Inc.
+ * author Andy Fleming
+ */
+#include <common.h>
+#include <phy.h>
+
+#define MIIM_DM9161_SCR 0x10
+#define MIIM_DM9161_SCR_INIT 0x0610
+
+/* DM9161 Specified Configuration and Status Register */
+#define MIIM_DM9161_SCSR 0x11
+#define MIIM_DM9161_SCSR_100F 0x8000
+#define MIIM_DM9161_SCSR_100H 0x4000
+#define MIIM_DM9161_SCSR_10F 0x2000
+#define MIIM_DM9161_SCSR_10H 0x1000
+
+/* DM9161 10BT Configuration/Status */
+#define MIIM_DM9161_10BTCSR 0x12
+#define MIIM_DM9161_10BTCSR_INIT 0x7800
+
+
+/* Davicom DM9161E */
+static int dm9161_config(struct phy_device *phydev)
+{
+ phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_ISOLATE);
+ /* Do not bypass the scrambler/descrambler */
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_DM9161_SCR,
+ MIIM_DM9161_SCR_INIT);
+ /* Clear 10BTCSR to default */
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_DM9161_10BTCSR,
+ MIIM_DM9161_10BTCSR_INIT);
+
+ genphy_config_aneg(phydev);
+
+ return 0;
+}
+
+static int dm9161_parse_status(struct phy_device *phydev)
+{
+ int mii_reg;
+
+ mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_DM9161_SCSR);
+
+ if (mii_reg & (MIIM_DM9161_SCSR_100F | MIIM_DM9161_SCSR_100H))
+ phydev->speed = SPEED_100;
+ else
+ phydev->speed = SPEED_10;
+
+ if (mii_reg & (MIIM_DM9161_SCSR_100F | MIIM_DM9161_SCSR_10F))
+ phydev->duplex = DUPLEX_FULL;
+ else
+ phydev->duplex = DUPLEX_HALF;
+
+ return 0;
+}
+
+static int dm9161_startup(struct phy_device *phydev)
+{
+ int ret;
+
+ ret = genphy_update_link(phydev);
+ if (ret)
+ return ret;
+
+ return dm9161_parse_status(phydev);
+}
+
+static struct phy_driver DM9161_driver = {
+ .name = "Davicom DM9161E",
+ .uid = 0x181b880,
+ .mask = 0xffffff0,
+ .features = PHY_BASIC_FEATURES,
+ .config = &dm9161_config,
+ .startup = &dm9161_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+int phy_davicom_init(void)
+{
+ phy_register(&DM9161_driver);
+
+ return 0;
+}
diff --git a/roms/u-boot/drivers/net/phy/dp83867.c b/roms/u-boot/drivers/net/phy/dp83867.c
new file mode 100644
index 000000000..eada4541c
--- /dev/null
+++ b/roms/u-boot/drivers/net/phy/dp83867.c
@@ -0,0 +1,438 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * TI PHY drivers
+ *
+ */
+#include <common.h>
+#include <log.h>
+#include <phy.h>
+#include <dm/devres.h>
+#include <linux/bitops.h>
+#include <linux/compat.h>
+#include <malloc.h>
+
+#include <dm.h>
+#include <dt-bindings/net/ti-dp83867.h>
+
+#include "ti_phy_init.h"
+
+/* TI DP83867 */
+#define DP83867_DEVADDR 0x1f
+
+#define MII_DP83867_PHYCTRL 0x10
+#define MII_DP83867_MICR 0x12
+#define MII_DP83867_CFG2 0x14
+#define MII_DP83867_BISCR 0x16
+#define DP83867_CTRL 0x1f
+
+/* Extended Registers */
+#define DP83867_CFG4 0x0031
+#define DP83867_RGMIICTL 0x0032
+#define DP83867_STRAP_STS1 0x006E
+#define DP83867_STRAP_STS2 0x006f
+#define DP83867_RGMIIDCTL 0x0086
+#define DP83867_IO_MUX_CFG 0x0170
+#define DP83867_SGMIICTL 0x00D3
+
+#define DP83867_SW_RESET BIT(15)
+#define DP83867_SW_RESTART BIT(14)
+
+/* MICR Interrupt bits */
+#define MII_DP83867_MICR_AN_ERR_INT_EN BIT(15)
+#define MII_DP83867_MICR_SPEED_CHNG_INT_EN BIT(14)
+#define MII_DP83867_MICR_DUP_MODE_CHNG_INT_EN BIT(13)
+#define MII_DP83867_MICR_PAGE_RXD_INT_EN BIT(12)
+#define MII_DP83867_MICR_AUTONEG_COMP_INT_EN BIT(11)
+#define MII_DP83867_MICR_LINK_STS_CHNG_INT_EN BIT(10)
+#define MII_DP83867_MICR_FALSE_CARRIER_INT_EN BIT(8)
+#define MII_DP83867_MICR_SLEEP_MODE_CHNG_INT_EN BIT(4)
+#define MII_DP83867_MICR_WOL_INT_EN BIT(3)
+#define MII_DP83867_MICR_XGMII_ERR_INT_EN BIT(2)
+#define MII_DP83867_MICR_POL_CHNG_INT_EN BIT(1)
+#define MII_DP83867_MICR_JABBER_INT_EN BIT(0)
+
+/* RGMIICTL bits */
+#define DP83867_RGMII_TX_CLK_DELAY_EN BIT(1)
+#define DP83867_RGMII_RX_CLK_DELAY_EN BIT(0)
+
+/* STRAP_STS1 bits */
+#define DP83867_STRAP_STS1_RESERVED BIT(11)
+
+/* STRAP_STS2 bits */
+#define DP83867_STRAP_STS2_CLK_SKEW_TX_MASK GENMASK(6, 4)
+#define DP83867_STRAP_STS2_CLK_SKEW_TX_SHIFT 4
+#define DP83867_STRAP_STS2_CLK_SKEW_RX_MASK GENMASK(2, 0)
+#define DP83867_STRAP_STS2_CLK_SKEW_RX_SHIFT 0
+#define DP83867_STRAP_STS2_CLK_SKEW_NONE BIT(2)
+
+/* PHY CTRL bits */
+#define DP83867_PHYCR_FIFO_DEPTH_SHIFT 14
+#define DP83867_PHYCR_FIFO_DEPTH_MASK GENMASK(15, 14)
+#define DP83867_PHYCR_RESERVED_MASK BIT(11)
+#define DP83867_PHYCR_FORCE_LINK_GOOD BIT(10)
+#define DP83867_MDI_CROSSOVER 5
+#define DP83867_MDI_CROSSOVER_MDIX 2
+#define DP83867_PHYCTRL_SGMIIEN 0x0800
+#define DP83867_PHYCTRL_RXFIFO_SHIFT 12
+#define DP83867_PHYCTRL_TXFIFO_SHIFT 14
+
+/* RGMIIDCTL bits */
+#define DP83867_RGMII_TX_CLK_DELAY_MAX 0xf
+#define DP83867_RGMII_TX_CLK_DELAY_SHIFT 4
+#define DP83867_RGMII_RX_CLK_DELAY_MAX 0xf
+
+/* CFG2 bits */
+#define MII_DP83867_CFG2_SPEEDOPT_10EN 0x0040
+#define MII_DP83867_CFG2_SGMII_AUTONEGEN 0x0080
+#define MII_DP83867_CFG2_SPEEDOPT_ENH 0x0100
+#define MII_DP83867_CFG2_SPEEDOPT_CNT 0x0800
+#define MII_DP83867_CFG2_SPEEDOPT_INTLOW 0x2000
+#define MII_DP83867_CFG2_MASK 0x003F
+
+/* User setting - can be taken from DTS */
+#define DEFAULT_FIFO_DEPTH DP83867_PHYCR_FIFO_DEPTH_4_B_NIB
+
+/* IO_MUX_CFG bits */
+#define DP83867_IO_MUX_CFG_IO_IMPEDANCE_CTRL 0x1f
+
+#define DP83867_IO_MUX_CFG_IO_IMPEDANCE_MAX 0x0
+#define DP83867_IO_MUX_CFG_IO_IMPEDANCE_MIN 0x1f
+#define DP83867_IO_MUX_CFG_CLK_O_DISABLE BIT(6)
+#define DP83867_IO_MUX_CFG_CLK_O_SEL_SHIFT 8
+#define DP83867_IO_MUX_CFG_CLK_O_SEL_MASK \
+ GENMASK(0x1f, DP83867_IO_MUX_CFG_CLK_O_SEL_SHIFT)
+
+/* CFG4 bits */
+#define DP83867_CFG4_PORT_MIRROR_EN BIT(0)
+
+/* SGMIICTL bits */
+#define DP83867_SGMII_TYPE BIT(14)
+
+enum {
+ DP83867_PORT_MIRRORING_KEEP,
+ DP83867_PORT_MIRRORING_EN,
+ DP83867_PORT_MIRRORING_DIS,
+};
+
+struct dp83867_private {
+ u32 rx_id_delay;
+ u32 tx_id_delay;
+ int fifo_depth;
+ int io_impedance;
+ bool rxctrl_strap_quirk;
+ int port_mirroring;
+ bool set_clk_output;
+ unsigned int clk_output_sel;
+ bool sgmii_ref_clk_en;
+};
+
+static int dp83867_config_port_mirroring(struct phy_device *phydev)
+{
+ struct dp83867_private *dp83867 =
+ (struct dp83867_private *)phydev->priv;
+ u16 val;
+
+ val = phy_read_mmd(phydev, DP83867_DEVADDR, DP83867_CFG4);
+
+ if (dp83867->port_mirroring == DP83867_PORT_MIRRORING_EN)
+ val |= DP83867_CFG4_PORT_MIRROR_EN;
+ else
+ val &= ~DP83867_CFG4_PORT_MIRROR_EN;
+
+ phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_CFG4, val);
+
+ return 0;
+}
+
+#if defined(CONFIG_DM_ETH)
+/**
+ * dp83867_data_init - Convenience function for setting PHY specific data
+ *
+ * @phydev: the phy_device struct
+ */
+static int dp83867_of_init(struct phy_device *phydev)
+{
+ struct dp83867_private *dp83867 = phydev->priv;
+ ofnode node;
+ int ret;
+
+ node = phy_get_ofnode(phydev);
+ if (!ofnode_valid(node))
+ return -EINVAL;
+
+ /* Optional configuration */
+ ret = ofnode_read_u32(node, "ti,clk-output-sel",
+ &dp83867->clk_output_sel);
+ /* If not set, keep default */
+ if (!ret) {
+ dp83867->set_clk_output = true;
+ /* Valid values are 0 to DP83867_CLK_O_SEL_REF_CLK or
+ * DP83867_CLK_O_SEL_OFF.
+ */
+ if (dp83867->clk_output_sel > DP83867_CLK_O_SEL_REF_CLK &&
+ dp83867->clk_output_sel != DP83867_CLK_O_SEL_OFF) {
+ pr_debug("ti,clk-output-sel value %u out of range\n",
+ dp83867->clk_output_sel);
+ return -EINVAL;
+ }
+ }
+
+ if (ofnode_read_bool(node, "ti,max-output-impedance"))
+ dp83867->io_impedance = DP83867_IO_MUX_CFG_IO_IMPEDANCE_MAX;
+ else if (ofnode_read_bool(node, "ti,min-output-impedance"))
+ dp83867->io_impedance = DP83867_IO_MUX_CFG_IO_IMPEDANCE_MIN;
+ else
+ dp83867->io_impedance = -EINVAL;
+
+ if (ofnode_read_bool(node, "ti,dp83867-rxctrl-strap-quirk"))
+ dp83867->rxctrl_strap_quirk = true;
+
+ /* Existing behavior was to use default pin strapping delay in rgmii
+ * mode, but rgmii should have meant no delay. Warn existing users.
+ */
+ if (phydev->interface == PHY_INTERFACE_MODE_RGMII) {
+ u16 val = phy_read_mmd(phydev, DP83867_DEVADDR,
+ DP83867_STRAP_STS2);
+ u16 txskew = (val & DP83867_STRAP_STS2_CLK_SKEW_TX_MASK) >>
+ DP83867_STRAP_STS2_CLK_SKEW_TX_SHIFT;
+ u16 rxskew = (val & DP83867_STRAP_STS2_CLK_SKEW_RX_MASK) >>
+ DP83867_STRAP_STS2_CLK_SKEW_RX_SHIFT;
+
+ if (txskew != DP83867_STRAP_STS2_CLK_SKEW_NONE ||
+ rxskew != DP83867_STRAP_STS2_CLK_SKEW_NONE)
+ pr_warn("PHY has delays via pin strapping, but phy-mode = 'rgmii'\n"
+ "Should be 'rgmii-id' to use internal delays\n");
+ }
+
+ /* RX delay *must* be specified if internal delay of RX is used. */
+ if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
+ phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) {
+ ret = ofnode_read_u32(node, "ti,rx-internal-delay",
+ &dp83867->rx_id_delay);
+ if (ret) {
+ pr_debug("ti,rx-internal-delay must be specified\n");
+ return ret;
+ }
+ if (dp83867->rx_id_delay > DP83867_RGMII_RX_CLK_DELAY_MAX) {
+ pr_debug("ti,rx-internal-delay value of %u out of range\n",
+ dp83867->rx_id_delay);
+ return -EINVAL;
+ }
+ }
+
+ /* TX delay *must* be specified if internal delay of RX is used. */
+ if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
+ phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
+ ret = ofnode_read_u32(node, "ti,tx-internal-delay",
+ &dp83867->tx_id_delay);
+ if (ret) {
+ debug("ti,tx-internal-delay must be specified\n");
+ return ret;
+ }
+ if (dp83867->tx_id_delay > DP83867_RGMII_TX_CLK_DELAY_MAX) {
+ pr_debug("ti,tx-internal-delay value of %u out of range\n",
+ dp83867->tx_id_delay);
+ return -EINVAL;
+ }
+ }
+
+ dp83867->fifo_depth = ofnode_read_u32_default(node, "ti,fifo-depth",
+ DEFAULT_FIFO_DEPTH);
+ if (ofnode_read_bool(node, "enet-phy-lane-swap"))
+ dp83867->port_mirroring = DP83867_PORT_MIRRORING_EN;
+
+ if (ofnode_read_bool(node, "enet-phy-lane-no-swap"))
+ dp83867->port_mirroring = DP83867_PORT_MIRRORING_DIS;
+
+ if (ofnode_read_bool(node, "ti,sgmii-ref-clock-output-enable"))
+ dp83867->sgmii_ref_clk_en = true;
+
+ return 0;
+}
+#else
+static int dp83867_of_init(struct phy_device *phydev)
+{
+ struct dp83867_private *dp83867 = phydev->priv;
+
+ dp83867->rx_id_delay = DP83867_RGMIIDCTL_2_25_NS;
+ dp83867->tx_id_delay = DP83867_RGMIIDCTL_2_75_NS;
+ dp83867->fifo_depth = DEFAULT_FIFO_DEPTH;
+ dp83867->io_impedance = -EINVAL;
+
+ return 0;
+}
+#endif
+
+static int dp83867_config(struct phy_device *phydev)
+{
+ struct dp83867_private *dp83867;
+ unsigned int val, delay, cfg2;
+ int ret, bs;
+
+ dp83867 = (struct dp83867_private *)phydev->priv;
+
+ ret = dp83867_of_init(phydev);
+ if (ret)
+ return ret;
+
+ /* Restart the PHY. */
+ val = phy_read(phydev, MDIO_DEVAD_NONE, DP83867_CTRL);
+ phy_write(phydev, MDIO_DEVAD_NONE, DP83867_CTRL,
+ val | DP83867_SW_RESTART);
+
+ /* Mode 1 or 2 workaround */
+ if (dp83867->rxctrl_strap_quirk) {
+ val = phy_read_mmd(phydev, DP83867_DEVADDR,
+ DP83867_CFG4);
+ val &= ~BIT(7);
+ phy_write_mmd(phydev, DP83867_DEVADDR,
+ DP83867_CFG4, val);
+ }
+
+ if (phy_interface_is_rgmii(phydev)) {
+ val = phy_read(phydev, MDIO_DEVAD_NONE, MII_DP83867_PHYCTRL);
+ if (val < 0)
+ goto err_out;
+ val &= ~DP83867_PHYCR_FIFO_DEPTH_MASK;
+ val |= (dp83867->fifo_depth << DP83867_PHYCR_FIFO_DEPTH_SHIFT);
+
+ /* Do not force link good */
+ val &= ~DP83867_PHYCR_FORCE_LINK_GOOD;
+
+ /* The code below checks if "port mirroring" N/A MODE4 has been
+ * enabled during power on bootstrap.
+ *
+ * Such N/A mode enabled by mistake can put PHY IC in some
+ * internal testing mode and disable RGMII transmission.
+ *
+ * In this particular case one needs to check STRAP_STS1
+ * register's bit 11 (marked as RESERVED).
+ */
+
+ bs = phy_read_mmd(phydev, DP83867_DEVADDR, DP83867_STRAP_STS1);
+ if (bs & DP83867_STRAP_STS1_RESERVED)
+ val &= ~DP83867_PHYCR_RESERVED_MASK;
+
+ ret = phy_write(phydev, MDIO_DEVAD_NONE,
+ MII_DP83867_PHYCTRL, val);
+
+ val = phy_read_mmd(phydev, DP83867_DEVADDR,
+ DP83867_RGMIICTL);
+
+ val &= ~(DP83867_RGMII_TX_CLK_DELAY_EN |
+ DP83867_RGMII_RX_CLK_DELAY_EN);
+ if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
+ val |= (DP83867_RGMII_TX_CLK_DELAY_EN |
+ DP83867_RGMII_RX_CLK_DELAY_EN);
+
+ if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
+ val |= DP83867_RGMII_TX_CLK_DELAY_EN;
+
+ if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
+ val |= DP83867_RGMII_RX_CLK_DELAY_EN;
+
+ phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_RGMIICTL, val);
+
+ delay = (dp83867->rx_id_delay |
+ (dp83867->tx_id_delay <<
+ DP83867_RGMII_TX_CLK_DELAY_SHIFT));
+
+ phy_write_mmd(phydev, DP83867_DEVADDR,
+ DP83867_RGMIIDCTL, delay);
+ }
+
+ if (phy_interface_is_sgmii(phydev)) {
+ if (dp83867->sgmii_ref_clk_en)
+ phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_SGMIICTL,
+ DP83867_SGMII_TYPE);
+
+ phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR,
+ (BMCR_ANENABLE | BMCR_FULLDPLX | BMCR_SPEED1000));
+
+ cfg2 = phy_read(phydev, phydev->addr, MII_DP83867_CFG2);
+ cfg2 &= MII_DP83867_CFG2_MASK;
+ cfg2 |= (MII_DP83867_CFG2_SPEEDOPT_10EN |
+ MII_DP83867_CFG2_SGMII_AUTONEGEN |
+ MII_DP83867_CFG2_SPEEDOPT_ENH |
+ MII_DP83867_CFG2_SPEEDOPT_CNT |
+ MII_DP83867_CFG2_SPEEDOPT_INTLOW);
+ phy_write(phydev, MDIO_DEVAD_NONE, MII_DP83867_CFG2, cfg2);
+
+ phy_write_mmd(phydev, DP83867_DEVADDR,
+ DP83867_RGMIICTL, 0x0);
+
+ phy_write(phydev, MDIO_DEVAD_NONE, MII_DP83867_PHYCTRL,
+ DP83867_PHYCTRL_SGMIIEN |
+ (DP83867_MDI_CROSSOVER_MDIX <<
+ DP83867_MDI_CROSSOVER) |
+ (dp83867->fifo_depth << DP83867_PHYCTRL_RXFIFO_SHIFT) |
+ (dp83867->fifo_depth << DP83867_PHYCTRL_TXFIFO_SHIFT));
+ phy_write(phydev, MDIO_DEVAD_NONE, MII_DP83867_BISCR, 0x0);
+ }
+
+ if (dp83867->io_impedance >= 0) {
+ val = phy_read_mmd(phydev,
+ DP83867_DEVADDR,
+ DP83867_IO_MUX_CFG);
+ val &= ~DP83867_IO_MUX_CFG_IO_IMPEDANCE_CTRL;
+ val |= dp83867->io_impedance &
+ DP83867_IO_MUX_CFG_IO_IMPEDANCE_CTRL;
+ phy_write_mmd(phydev, DP83867_DEVADDR,
+ DP83867_IO_MUX_CFG, val);
+ }
+
+ if (dp83867->port_mirroring != DP83867_PORT_MIRRORING_KEEP)
+ dp83867_config_port_mirroring(phydev);
+
+ /* Clock output selection if muxing property is set */
+ if (dp83867->set_clk_output) {
+ val = phy_read_mmd(phydev, DP83867_DEVADDR,
+ DP83867_IO_MUX_CFG);
+
+ if (dp83867->clk_output_sel == DP83867_CLK_O_SEL_OFF) {
+ val |= DP83867_IO_MUX_CFG_CLK_O_DISABLE;
+ } else {
+ val &= ~(DP83867_IO_MUX_CFG_CLK_O_SEL_MASK |
+ DP83867_IO_MUX_CFG_CLK_O_DISABLE);
+ val |= dp83867->clk_output_sel <<
+ DP83867_IO_MUX_CFG_CLK_O_SEL_SHIFT;
+ }
+ phy_write_mmd(phydev, DP83867_DEVADDR,
+ DP83867_IO_MUX_CFG, val);
+ }
+
+ genphy_config_aneg(phydev);
+ return 0;
+
+err_out:
+ return ret;
+}
+
+static int dp83867_probe(struct phy_device *phydev)
+{
+ struct dp83867_private *dp83867;
+
+ dp83867 = kzalloc(sizeof(*dp83867), GFP_KERNEL);
+ if (!dp83867)
+ return -ENOMEM;
+
+ phydev->priv = dp83867;
+ return 0;
+}
+
+static struct phy_driver DP83867_driver = {
+ .name = "TI DP83867",
+ .uid = 0x2000a231,
+ .mask = 0xfffffff0,
+ .features = PHY_GBIT_FEATURES,
+ .probe = dp83867_probe,
+ .config = &dp83867_config,
+ .startup = &genphy_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+int phy_dp83867_init(void)
+{
+ phy_register(&DP83867_driver);
+ return 0;
+}
diff --git a/roms/u-boot/drivers/net/phy/et1011c.c b/roms/u-boot/drivers/net/phy/et1011c.c
new file mode 100644
index 000000000..c243c5b72
--- /dev/null
+++ b/roms/u-boot/drivers/net/phy/et1011c.c
@@ -0,0 +1,104 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * ET1011C PHY driver
+ *
+ * Derived from Linux kernel driver by Chaithrika U S
+ * Copyright (C) 2013, Texas Instruments, Incorporated - http://www.ti.com/
+ */
+#include <config.h>
+#include <phy.h>
+
+#define ET1011C_CONFIG_REG (0x16)
+#define ET1011C_TX_FIFO_MASK (0x3 << 12)
+#define ET1011C_TX_FIFO_DEPTH_8 (0x0 << 12)
+#define ET1011C_TX_FIFO_DEPTH_16 (0x1 << 12)
+#define ET1011C_INTERFACE_MASK (0x7 << 0)
+#define ET1011C_GMII_INTERFACE (0x2 << 0)
+#define ET1011C_SYS_CLK_EN (0x1 << 4)
+#define ET1011C_TX_CLK_EN (0x1 << 5)
+
+#define ET1011C_STATUS_REG (0x1A)
+#define ET1011C_DUPLEX_STATUS (0x1 << 7)
+#define ET1011C_SPEED_MASK (0x3 << 8)
+#define ET1011C_SPEED_1000 (0x2 << 8)
+#define ET1011C_SPEED_100 (0x1 << 8)
+#define ET1011C_SPEED_10 (0x0 << 8)
+
+static int et1011c_config(struct phy_device *phydev)
+{
+ int ctl = 0;
+ ctl = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR);
+ if (ctl < 0)
+ return ctl;
+ ctl &= ~(BMCR_FULLDPLX | BMCR_SPEED100 | BMCR_SPEED1000 |
+ BMCR_ANENABLE);
+ /* First clear the PHY */
+ phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, ctl | BMCR_RESET);
+
+ return genphy_config_aneg(phydev);
+}
+
+static int et1011c_parse_status(struct phy_device *phydev)
+{
+ int mii_reg;
+ int speed;
+
+ mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, ET1011C_STATUS_REG);
+
+ if (mii_reg & ET1011C_DUPLEX_STATUS)
+ phydev->duplex = DUPLEX_FULL;
+ else
+ phydev->duplex = DUPLEX_HALF;
+
+ speed = mii_reg & ET1011C_SPEED_MASK;
+ switch (speed) {
+ case ET1011C_SPEED_1000:
+ phydev->speed = SPEED_1000;
+ mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, ET1011C_CONFIG_REG);
+ mii_reg &= ~ET1011C_TX_FIFO_MASK;
+ phy_write(phydev, MDIO_DEVAD_NONE, ET1011C_CONFIG_REG,
+ mii_reg |
+ ET1011C_GMII_INTERFACE |
+ ET1011C_SYS_CLK_EN |
+#ifdef CONFIG_PHY_ET1011C_TX_CLK_FIX
+ ET1011C_TX_CLK_EN |
+#endif
+ ET1011C_TX_FIFO_DEPTH_16);
+ break;
+ case ET1011C_SPEED_100:
+ phydev->speed = SPEED_100;
+ break;
+ case ET1011C_SPEED_10:
+ phydev->speed = SPEED_10;
+ break;
+ }
+
+ return 0;
+}
+
+static int et1011c_startup(struct phy_device *phydev)
+{
+ int ret;
+
+ ret = genphy_update_link(phydev);
+ if (ret)
+ return ret;
+
+ return et1011c_parse_status(phydev);
+}
+
+static struct phy_driver et1011c_driver = {
+ .name = "ET1011C",
+ .uid = 0x0282f014,
+ .mask = 0xfffffff0,
+ .features = PHY_GBIT_FEATURES,
+ .config = &et1011c_config,
+ .startup = &et1011c_startup,
+};
+
+int phy_et1011c_init(void)
+{
+ phy_register(&et1011c_driver);
+
+ return 0;
+}
diff --git a/roms/u-boot/drivers/net/phy/fixed.c b/roms/u-boot/drivers/net/phy/fixed.c
new file mode 100644
index 000000000..1192915ee
--- /dev/null
+++ b/roms/u-boot/drivers/net/phy/fixed.c
@@ -0,0 +1,111 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Fixed-Link phy
+ *
+ * Copyright 2017 Bernecker & Rainer Industrieelektronik GmbH
+ */
+
+#include <config.h>
+#include <common.h>
+#include <malloc.h>
+#include <phy.h>
+#include <dm.h>
+#include <fdt_support.h>
+#include <asm/global_data.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static int fixedphy_probe(struct phy_device *phydev)
+{
+ /* fixed-link phy must not be reset by core phy code */
+ phydev->flags |= PHY_FLAG_BROKEN_RESET;
+
+ return 0;
+}
+
+static int fixedphy_config(struct phy_device *phydev)
+{
+ ofnode node = phy_get_ofnode(phydev);
+ struct fixed_link *priv;
+ bool old_binding = false;
+ u32 old_val[5];
+ u32 val;
+
+ if (!ofnode_valid(node))
+ return -EINVAL;
+
+ /* check for mandatory properties within fixed-link node */
+ val = ofnode_read_u32_default(node, "speed", 0);
+
+ if (!val) {
+ /* try old binding */
+ old_binding = true;
+ if (ofnode_read_u32_array(node, "fixed-link", old_val,
+ ARRAY_SIZE(old_val))) {
+ printf("ERROR: no/invalid <fixed-link> property!\n");
+ return -ENOENT;
+ }
+ val = old_val[2];
+ }
+
+ if (val != SPEED_10 && val != SPEED_100 && val != SPEED_1000 &&
+ val != SPEED_2500 && val != SPEED_10000) {
+ printf("ERROR: no/invalid speed given in fixed-link node!\n");
+ return -EINVAL;
+ }
+
+ priv = malloc(sizeof(*priv));
+ if (!priv)
+ return -ENOMEM;
+ memset(priv, 0, sizeof(*priv));
+
+ phydev->priv = priv;
+
+ priv->link_speed = val;
+ if (!old_binding) {
+ priv->duplex = ofnode_read_bool(node, "full-duplex");
+ priv->pause = ofnode_read_bool(node, "pause");
+ priv->asym_pause = ofnode_read_bool(node, "asym-pause");
+ } else {
+ priv->duplex = old_val[1];
+ priv->pause = old_val[3];
+ priv->asym_pause = old_val[4];
+ }
+
+ return 0;
+}
+
+static int fixedphy_startup(struct phy_device *phydev)
+{
+ struct fixed_link *priv = phydev->priv;
+
+ phydev->asym_pause = priv->asym_pause;
+ phydev->pause = priv->pause;
+ phydev->duplex = priv->duplex;
+ phydev->speed = priv->link_speed;
+ phydev->link = 1;
+
+ return 0;
+}
+
+static int fixedphy_shutdown(struct phy_device *phydev)
+{
+ return 0;
+}
+
+static struct phy_driver fixedphy_driver = {
+ .uid = PHY_FIXED_ID,
+ .mask = 0xffffffff,
+ .name = "Fixed PHY",
+ .features = PHY_GBIT_FEATURES | SUPPORTED_MII,
+ .probe = fixedphy_probe,
+ .config = fixedphy_config,
+ .startup = fixedphy_startup,
+ .shutdown = fixedphy_shutdown,
+};
+
+int phy_fixed_init(void)
+{
+ phy_register(&fixedphy_driver);
+ return 0;
+}
diff --git a/roms/u-boot/drivers/net/phy/generic_10g.c b/roms/u-boot/drivers/net/phy/generic_10g.c
new file mode 100644
index 000000000..b4384e1f7
--- /dev/null
+++ b/roms/u-boot/drivers/net/phy/generic_10g.c
@@ -0,0 +1,91 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Generic PHY Management code
+ *
+ * Copyright 2011 Freescale Semiconductor, Inc.
+ * author Andy Fleming
+ *
+ * Based loosely off of Linux's PHY Lib
+ */
+#include <common.h>
+#include <miiphy.h>
+#include <phy.h>
+
+int gen10g_shutdown(struct phy_device *phydev)
+{
+ return 0;
+}
+
+int gen10g_startup(struct phy_device *phydev)
+{
+ int devad, reg;
+ u32 mmd_mask = phydev->mmds & MDIO_DEVS_LINK;
+
+ phydev->link = 1;
+
+ /* For now just lie and say it's 10G all the time */
+ phydev->speed = SPEED_10000;
+ phydev->duplex = DUPLEX_FULL;
+
+ /*
+ * Go through all the link-reporting devices, and make sure
+ * they're all up and happy
+ */
+ for (devad = 0; mmd_mask; devad++, mmd_mask = mmd_mask >> 1) {
+ if (!(mmd_mask & 1))
+ continue;
+
+ /* Read twice because link state is latched and a
+ * read moves the current state into the register */
+ phy_read(phydev, devad, MDIO_STAT1);
+ reg = phy_read(phydev, devad, MDIO_STAT1);
+ if (reg < 0 || !(reg & MDIO_STAT1_LSTATUS))
+ phydev->link = 0;
+ }
+
+ return 0;
+}
+
+int gen10g_discover_mmds(struct phy_device *phydev)
+{
+ int mmd, stat2, devs1, devs2;
+
+ /* Assume PHY must have at least one of PMA/PMD, WIS, PCS, PHY
+ * XS or DTE XS; give up if none is present. */
+ for (mmd = 1; mmd <= 5; mmd++) {
+ /* Is this MMD present? */
+ stat2 = phy_read(phydev, mmd, MDIO_STAT2);
+ if (stat2 < 0 ||
+ (stat2 & MDIO_STAT2_DEVPRST) != MDIO_STAT2_DEVPRST_VAL)
+ continue;
+
+ /* It should tell us about all the other MMDs */
+ devs1 = phy_read(phydev, mmd, MDIO_DEVS1);
+ devs2 = phy_read(phydev, mmd, MDIO_DEVS2);
+ if (devs1 < 0 || devs2 < 0)
+ continue;
+
+ phydev->mmds = devs1 | (devs2 << 16);
+ return 0;
+ }
+
+ return 0;
+}
+
+int gen10g_config(struct phy_device *phydev)
+{
+ /* For now, assume 10000baseT. Fill in later */
+ phydev->supported = phydev->advertising = SUPPORTED_10000baseT_Full;
+
+ return gen10g_discover_mmds(phydev);
+}
+
+struct phy_driver gen10g_driver = {
+ .uid = 0xffffffff,
+ .mask = 0xffffffff,
+ .name = "Generic 10G PHY",
+ .features = 0,
+ .config = gen10g_config,
+ .startup = gen10g_startup,
+ .shutdown = gen10g_shutdown,
+};
diff --git a/roms/u-boot/drivers/net/phy/lxt.c b/roms/u-boot/drivers/net/phy/lxt.c
new file mode 100644
index 000000000..2618deb00
--- /dev/null
+++ b/roms/u-boot/drivers/net/phy/lxt.c
@@ -0,0 +1,76 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * LXT PHY drivers
+ *
+ * Copyright 2010-2011 Freescale Semiconductor, Inc.
+ * author Andy Fleming
+ */
+#include <common.h>
+#include <phy.h>
+
+/* LXT971 Status 2 registers */
+#define MIIM_LXT971_SR2 0x11 /* Status Register 2 */
+#define MIIM_LXT971_SR2_SPEED_MASK 0x4200
+#define MIIM_LXT971_SR2_10HDX 0x0000 /* 10 Mbit half duplex selected */
+#define MIIM_LXT971_SR2_10FDX 0x0200 /* 10 Mbit full duplex selected */
+#define MIIM_LXT971_SR2_100HDX 0x4000 /* 100 Mbit half duplex selected */
+#define MIIM_LXT971_SR2_100FDX 0x4200 /* 100 Mbit full duplex selected */
+
+
+/* LXT971 */
+static int lxt971_parse_status(struct phy_device *phydev)
+{
+ int mii_reg;
+ int speed;
+
+ mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_LXT971_SR2);
+ speed = mii_reg & MIIM_LXT971_SR2_SPEED_MASK;
+
+ switch (speed) {
+ case MIIM_LXT971_SR2_10HDX:
+ phydev->speed = SPEED_10;
+ phydev->duplex = DUPLEX_HALF;
+ break;
+ case MIIM_LXT971_SR2_10FDX:
+ phydev->speed = SPEED_10;
+ phydev->duplex = DUPLEX_FULL;
+ break;
+ case MIIM_LXT971_SR2_100HDX:
+ phydev->speed = SPEED_100;
+ phydev->duplex = DUPLEX_HALF;
+ break;
+ default:
+ phydev->speed = SPEED_100;
+ phydev->duplex = DUPLEX_FULL;
+ }
+
+ return 0;
+}
+
+static int lxt971_startup(struct phy_device *phydev)
+{
+ int ret;
+
+ ret = genphy_update_link(phydev);
+ if (ret)
+ return ret;
+
+ return lxt971_parse_status(phydev);
+}
+
+static struct phy_driver LXT971_driver = {
+ .name = "LXT971",
+ .uid = 0x1378e0,
+ .mask = 0xfffff0,
+ .features = PHY_BASIC_FEATURES,
+ .config = &genphy_config_aneg,
+ .startup = &lxt971_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+int phy_lxt_init(void)
+{
+ phy_register(&LXT971_driver);
+
+ return 0;
+}
diff --git a/roms/u-boot/drivers/net/phy/marvell.c b/roms/u-boot/drivers/net/phy/marvell.c
new file mode 100644
index 000000000..a62c695c5
--- /dev/null
+++ b/roms/u-boot/drivers/net/phy/marvell.c
@@ -0,0 +1,709 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Marvell PHY drivers
+ *
+ * Copyright 2010-2011 Freescale Semiconductor, Inc.
+ * author Andy Fleming
+ */
+#include <common.h>
+#include <errno.h>
+#include <phy.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+
+#define PHY_AUTONEGOTIATE_TIMEOUT 5000
+
+#define MII_MARVELL_PHY_PAGE 22
+
+/* 88E1011 PHY Status Register */
+#define MIIM_88E1xxx_PHY_STATUS 0x11
+#define MIIM_88E1xxx_PHYSTAT_SPEED 0xc000
+#define MIIM_88E1xxx_PHYSTAT_GBIT 0x8000
+#define MIIM_88E1xxx_PHYSTAT_100 0x4000
+#define MIIM_88E1xxx_PHYSTAT_DUPLEX 0x2000
+#define MIIM_88E1xxx_PHYSTAT_SPDDONE 0x0800
+#define MIIM_88E1xxx_PHYSTAT_LINK 0x0400
+
+#define MIIM_88E1xxx_PHY_SCR 0x10
+#define MIIM_88E1xxx_PHY_MDI_X_AUTO 0x0060
+
+/* 88E1111 PHY LED Control Register */
+#define MIIM_88E1111_PHY_LED_CONTROL 24
+#define MIIM_88E1111_PHY_LED_DIRECT 0x4100
+#define MIIM_88E1111_PHY_LED_COMBINE 0x411C
+
+/* 88E1111 Extended PHY Specific Control Register */
+#define MIIM_88E1111_PHY_EXT_CR 0x14
+#define MIIM_88E1111_RX_DELAY 0x80
+#define MIIM_88E1111_TX_DELAY 0x2
+
+/* 88E1111 Extended PHY Specific Status Register */
+#define MIIM_88E1111_PHY_EXT_SR 0x1b
+#define MIIM_88E1111_HWCFG_MODE_MASK 0xf
+#define MIIM_88E1111_HWCFG_MODE_COPPER_RGMII 0xb
+#define MIIM_88E1111_HWCFG_MODE_FIBER_RGMII 0x3
+#define MIIM_88E1111_HWCFG_MODE_SGMII_NO_CLK 0x4
+#define MIIM_88E1111_HWCFG_MODE_COPPER_RTBI 0x9
+#define MIIM_88E1111_HWCFG_FIBER_COPPER_AUTO 0x8000
+#define MIIM_88E1111_HWCFG_FIBER_COPPER_RES 0x2000
+
+#define MIIM_88E1111_COPPER 0
+#define MIIM_88E1111_FIBER 1
+
+/* 88E1118 PHY defines */
+#define MIIM_88E1118_PHY_PAGE 22
+#define MIIM_88E1118_PHY_LED_PAGE 3
+
+/* 88E1121 PHY LED Control Register */
+#define MIIM_88E1121_PHY_LED_CTRL 16
+#define MIIM_88E1121_PHY_LED_PAGE 3
+#define MIIM_88E1121_PHY_LED_DEF 0x0030
+
+/* 88E1121 PHY IRQ Enable/Status Register */
+#define MIIM_88E1121_PHY_IRQ_EN 18
+#define MIIM_88E1121_PHY_IRQ_STATUS 19
+
+#define MIIM_88E1121_PHY_PAGE 22
+
+/* 88E1145 Extended PHY Specific Control Register */
+#define MIIM_88E1145_PHY_EXT_CR 20
+#define MIIM_M88E1145_RGMII_RX_DELAY 0x0080
+#define MIIM_M88E1145_RGMII_TX_DELAY 0x0002
+
+#define MIIM_88E1145_PHY_LED_CONTROL 24
+#define MIIM_88E1145_PHY_LED_DIRECT 0x4100
+
+#define MIIM_88E1145_PHY_PAGE 29
+#define MIIM_88E1145_PHY_CAL_OV 30
+
+#define MIIM_88E1149_PHY_PAGE 29
+
+/* 88E1310 PHY defines */
+#define MIIM_88E1310_PHY_LED_CTRL 16
+#define MIIM_88E1310_PHY_IRQ_EN 18
+#define MIIM_88E1310_PHY_RGMII_CTRL 21
+#define MIIM_88E1310_PHY_PAGE 22
+
+/* 88E151x PHY defines */
+/* Page 2 registers */
+#define MIIM_88E151x_PHY_MSCR 21
+#define MIIM_88E151x_RGMII_RX_DELAY BIT(5)
+#define MIIM_88E151x_RGMII_TX_DELAY BIT(4)
+#define MIIM_88E151x_RGMII_RXTX_DELAY (BIT(5) | BIT(4))
+/* Page 3 registers */
+#define MIIM_88E151x_LED_FUNC_CTRL 16
+#define MIIM_88E151x_LED_FLD_SZ 4
+#define MIIM_88E151x_LED0_OFFS (0 * MIIM_88E151x_LED_FLD_SZ)
+#define MIIM_88E151x_LED1_OFFS (1 * MIIM_88E151x_LED_FLD_SZ)
+#define MIIM_88E151x_LED0_ACT 3
+#define MIIM_88E151x_LED1_100_1000_LINK 6
+#define MIIM_88E151x_LED_TIMER_CTRL 18
+#define MIIM_88E151x_INT_EN_OFFS 7
+/* Page 18 registers */
+#define MIIM_88E151x_GENERAL_CTRL 20
+#define MIIM_88E151x_MODE_SGMII 1
+#define MIIM_88E151x_RESET_OFFS 15
+
+static int m88e1xxx_phy_extread(struct phy_device *phydev, int addr,
+ int devaddr, int regnum)
+{
+ int oldpage = phy_read(phydev, MDIO_DEVAD_NONE, MII_MARVELL_PHY_PAGE);
+ int val;
+
+ phy_write(phydev, MDIO_DEVAD_NONE, MII_MARVELL_PHY_PAGE, devaddr);
+ val = phy_read(phydev, MDIO_DEVAD_NONE, regnum);
+ phy_write(phydev, MDIO_DEVAD_NONE, MII_MARVELL_PHY_PAGE, oldpage);
+
+ return val;
+}
+
+static int m88e1xxx_phy_extwrite(struct phy_device *phydev, int addr,
+ int devaddr, int regnum, u16 val)
+{
+ int oldpage = phy_read(phydev, MDIO_DEVAD_NONE, MII_MARVELL_PHY_PAGE);
+
+ phy_write(phydev, MDIO_DEVAD_NONE, MII_MARVELL_PHY_PAGE, devaddr);
+ phy_write(phydev, MDIO_DEVAD_NONE, regnum, val);
+ phy_write(phydev, MDIO_DEVAD_NONE, MII_MARVELL_PHY_PAGE, oldpage);
+
+ return 0;
+}
+
+/* Marvell 88E1011S */
+static int m88e1011s_config(struct phy_device *phydev)
+{
+ /* Reset and configure the PHY */
+ phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_RESET);
+
+ phy_write(phydev, MDIO_DEVAD_NONE, 0x1d, 0x1f);
+ phy_write(phydev, MDIO_DEVAD_NONE, 0x1e, 0x200c);
+ phy_write(phydev, MDIO_DEVAD_NONE, 0x1d, 0x5);
+ phy_write(phydev, MDIO_DEVAD_NONE, 0x1e, 0);
+ phy_write(phydev, MDIO_DEVAD_NONE, 0x1e, 0x100);
+
+ phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_RESET);
+
+ genphy_config_aneg(phydev);
+
+ return 0;
+}
+
+/* Parse the 88E1011's status register for speed and duplex
+ * information
+ */
+static int m88e1xxx_parse_status(struct phy_device *phydev)
+{
+ unsigned int speed;
+ unsigned int mii_reg;
+
+ mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_88E1xxx_PHY_STATUS);
+
+ if ((mii_reg & MIIM_88E1xxx_PHYSTAT_LINK) &&
+ !(mii_reg & MIIM_88E1xxx_PHYSTAT_SPDDONE)) {
+ int i = 0;
+
+ puts("Waiting for PHY realtime link");
+ while (!(mii_reg & MIIM_88E1xxx_PHYSTAT_SPDDONE)) {
+ /* Timeout reached ? */
+ if (i > PHY_AUTONEGOTIATE_TIMEOUT) {
+ puts(" TIMEOUT !\n");
+ phydev->link = 0;
+ return -ETIMEDOUT;
+ }
+
+ if ((i++ % 1000) == 0)
+ putc('.');
+ udelay(1000);
+ mii_reg = phy_read(phydev, MDIO_DEVAD_NONE,
+ MIIM_88E1xxx_PHY_STATUS);
+ }
+ puts(" done\n");
+ mdelay(500); /* another 500 ms (results in faster booting) */
+ } else {
+ if (mii_reg & MIIM_88E1xxx_PHYSTAT_LINK)
+ phydev->link = 1;
+ else
+ phydev->link = 0;
+ }
+
+ if (mii_reg & MIIM_88E1xxx_PHYSTAT_DUPLEX)
+ phydev->duplex = DUPLEX_FULL;
+ else
+ phydev->duplex = DUPLEX_HALF;
+
+ speed = mii_reg & MIIM_88E1xxx_PHYSTAT_SPEED;
+
+ switch (speed) {
+ case MIIM_88E1xxx_PHYSTAT_GBIT:
+ phydev->speed = SPEED_1000;
+ break;
+ case MIIM_88E1xxx_PHYSTAT_100:
+ phydev->speed = SPEED_100;
+ break;
+ default:
+ phydev->speed = SPEED_10;
+ break;
+ }
+
+ return 0;
+}
+
+static int m88e1011s_startup(struct phy_device *phydev)
+{
+ int ret;
+
+ ret = genphy_update_link(phydev);
+ if (ret)
+ return ret;
+
+ return m88e1xxx_parse_status(phydev);
+}
+
+/* Marvell 88E1111S */
+static int m88e1111s_config(struct phy_device *phydev)
+{
+ int reg;
+
+ if (phy_interface_is_rgmii(phydev)) {
+ reg = phy_read(phydev,
+ MDIO_DEVAD_NONE, MIIM_88E1111_PHY_EXT_CR);
+ if ((phydev->interface == PHY_INTERFACE_MODE_RGMII) ||
+ (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)) {
+ reg |= (MIIM_88E1111_RX_DELAY | MIIM_88E1111_TX_DELAY);
+ } else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) {
+ reg &= ~MIIM_88E1111_TX_DELAY;
+ reg |= MIIM_88E1111_RX_DELAY;
+ } else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
+ reg &= ~MIIM_88E1111_RX_DELAY;
+ reg |= MIIM_88E1111_TX_DELAY;
+ }
+
+ phy_write(phydev,
+ MDIO_DEVAD_NONE, MIIM_88E1111_PHY_EXT_CR, reg);
+
+ reg = phy_read(phydev,
+ MDIO_DEVAD_NONE, MIIM_88E1111_PHY_EXT_SR);
+
+ reg &= ~(MIIM_88E1111_HWCFG_MODE_MASK);
+
+ if (reg & MIIM_88E1111_HWCFG_FIBER_COPPER_RES)
+ reg |= MIIM_88E1111_HWCFG_MODE_FIBER_RGMII;
+ else
+ reg |= MIIM_88E1111_HWCFG_MODE_COPPER_RGMII;
+
+ phy_write(phydev,
+ MDIO_DEVAD_NONE, MIIM_88E1111_PHY_EXT_SR, reg);
+ }
+
+ if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
+ reg = phy_read(phydev,
+ MDIO_DEVAD_NONE, MIIM_88E1111_PHY_EXT_SR);
+
+ reg &= ~(MIIM_88E1111_HWCFG_MODE_MASK);
+ reg |= MIIM_88E1111_HWCFG_MODE_SGMII_NO_CLK;
+ reg |= MIIM_88E1111_HWCFG_FIBER_COPPER_AUTO;
+
+ phy_write(phydev, MDIO_DEVAD_NONE,
+ MIIM_88E1111_PHY_EXT_SR, reg);
+ }
+
+ if (phydev->interface == PHY_INTERFACE_MODE_RTBI) {
+ reg = phy_read(phydev,
+ MDIO_DEVAD_NONE, MIIM_88E1111_PHY_EXT_CR);
+ reg |= (MIIM_88E1111_RX_DELAY | MIIM_88E1111_TX_DELAY);
+ phy_write(phydev,
+ MDIO_DEVAD_NONE, MIIM_88E1111_PHY_EXT_CR, reg);
+
+ reg = phy_read(phydev, MDIO_DEVAD_NONE,
+ MIIM_88E1111_PHY_EXT_SR);
+ reg &= ~(MIIM_88E1111_HWCFG_MODE_MASK |
+ MIIM_88E1111_HWCFG_FIBER_COPPER_RES);
+ reg |= 0x7 | MIIM_88E1111_HWCFG_FIBER_COPPER_AUTO;
+ phy_write(phydev, MDIO_DEVAD_NONE,
+ MIIM_88E1111_PHY_EXT_SR, reg);
+
+ /* soft reset */
+ phy_reset(phydev);
+
+ reg = phy_read(phydev, MDIO_DEVAD_NONE,
+ MIIM_88E1111_PHY_EXT_SR);
+ reg &= ~(MIIM_88E1111_HWCFG_MODE_MASK |
+ MIIM_88E1111_HWCFG_FIBER_COPPER_RES);
+ reg |= MIIM_88E1111_HWCFG_MODE_COPPER_RTBI |
+ MIIM_88E1111_HWCFG_FIBER_COPPER_AUTO;
+ phy_write(phydev, MDIO_DEVAD_NONE,
+ MIIM_88E1111_PHY_EXT_SR, reg);
+ }
+
+ /* soft reset */
+ phy_reset(phydev);
+
+ genphy_config_aneg(phydev);
+ genphy_restart_aneg(phydev);
+
+ return 0;
+}
+
+/**
+ * m88e151x_phy_writebits - write bits to a register
+ */
+void m88e151x_phy_writebits(struct phy_device *phydev,
+ u8 reg_num, u16 offset, u16 len, u16 data)
+{
+ u16 reg, mask;
+
+ if ((len + offset) >= 16)
+ mask = 0 - (1 << offset);
+ else
+ mask = (1 << (len + offset)) - (1 << offset);
+
+ reg = phy_read(phydev, MDIO_DEVAD_NONE, reg_num);
+
+ reg &= ~mask;
+ reg |= data << offset;
+
+ phy_write(phydev, MDIO_DEVAD_NONE, reg_num, reg);
+}
+
+static int m88e151x_config(struct phy_device *phydev)
+{
+ u16 reg;
+
+ /*
+ * As per Marvell Release Notes - Alaska 88E1510/88E1518/88E1512
+ * /88E1514 Rev A0, Errata Section 3.1
+ */
+
+ /* EEE initialization */
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1118_PHY_PAGE, 0x00ff);
+ phy_write(phydev, MDIO_DEVAD_NONE, 17, 0x214B);
+ phy_write(phydev, MDIO_DEVAD_NONE, 16, 0x2144);
+ phy_write(phydev, MDIO_DEVAD_NONE, 17, 0x0C28);
+ phy_write(phydev, MDIO_DEVAD_NONE, 16, 0x2146);
+ phy_write(phydev, MDIO_DEVAD_NONE, 17, 0xB233);
+ phy_write(phydev, MDIO_DEVAD_NONE, 16, 0x214D);
+ phy_write(phydev, MDIO_DEVAD_NONE, 17, 0xCC0C);
+ phy_write(phydev, MDIO_DEVAD_NONE, 16, 0x2159);
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1118_PHY_PAGE, 0x0000);
+
+ /* SGMII-to-Copper mode initialization */
+ if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
+ /* Select page 18 */
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1118_PHY_PAGE, 18);
+
+ /* In reg 20, write MODE[2:0] = 0x1 (SGMII to Copper) */
+ m88e151x_phy_writebits(phydev, MIIM_88E151x_GENERAL_CTRL,
+ 0, 3, MIIM_88E151x_MODE_SGMII);
+
+ /* PHY reset is necessary after changing MODE[2:0] */
+ m88e151x_phy_writebits(phydev, MIIM_88E151x_GENERAL_CTRL,
+ MIIM_88E151x_RESET_OFFS, 1, 1);
+
+ /* Reset page selection */
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1118_PHY_PAGE, 0);
+
+ udelay(100);
+ }
+
+ if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
+ reg = phy_read(phydev, MDIO_DEVAD_NONE,
+ MIIM_88E1111_PHY_EXT_SR);
+
+ reg &= ~(MIIM_88E1111_HWCFG_MODE_MASK);
+ reg |= MIIM_88E1111_HWCFG_MODE_SGMII_NO_CLK;
+ reg |= MIIM_88E1111_HWCFG_FIBER_COPPER_AUTO;
+
+ phy_write(phydev, MDIO_DEVAD_NONE,
+ MIIM_88E1111_PHY_EXT_SR, reg);
+ }
+
+ if (phy_interface_is_rgmii(phydev)) {
+ phy_write(phydev, MDIO_DEVAD_NONE, MII_MARVELL_PHY_PAGE, 2);
+
+ reg = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_88E151x_PHY_MSCR);
+ reg &= ~MIIM_88E151x_RGMII_RXTX_DELAY;
+ if (phydev->interface == PHY_INTERFACE_MODE_RGMII ||
+ phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
+ reg |= MIIM_88E151x_RGMII_RXTX_DELAY;
+ else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
+ reg |= MIIM_88E151x_RGMII_RX_DELAY;
+ else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
+ reg |= MIIM_88E151x_RGMII_TX_DELAY;
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E151x_PHY_MSCR, reg);
+
+ phy_write(phydev, MDIO_DEVAD_NONE, MII_MARVELL_PHY_PAGE, 0);
+ }
+
+ /* soft reset */
+ phy_reset(phydev);
+
+ genphy_config_aneg(phydev);
+ genphy_restart_aneg(phydev);
+
+ return 0;
+}
+
+/* Marvell 88E1118 */
+static int m88e1118_config(struct phy_device *phydev)
+{
+ /* Change Page Number */
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1118_PHY_PAGE, 0x0002);
+ /* Delay RGMII TX and RX */
+ phy_write(phydev, MDIO_DEVAD_NONE, 0x15, 0x1070);
+ /* Change Page Number */
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1118_PHY_PAGE, 0x0003);
+ /* Adjust LED control */
+ phy_write(phydev, MDIO_DEVAD_NONE, 0x10, 0x021e);
+ /* Change Page Number */
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1118_PHY_PAGE, 0x0000);
+
+ return genphy_config_aneg(phydev);
+}
+
+static int m88e1118_startup(struct phy_device *phydev)
+{
+ int ret;
+
+ /* Change Page Number */
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1118_PHY_PAGE, 0x0000);
+
+ ret = genphy_update_link(phydev);
+ if (ret)
+ return ret;
+
+ return m88e1xxx_parse_status(phydev);
+}
+
+/* Marvell 88E1121R */
+static int m88e1121_config(struct phy_device *phydev)
+{
+ int pg;
+
+ /* Configure the PHY */
+ genphy_config_aneg(phydev);
+
+ /* Switch the page to access the led register */
+ pg = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_88E1121_PHY_PAGE);
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1121_PHY_PAGE,
+ MIIM_88E1121_PHY_LED_PAGE);
+ /* Configure leds */
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1121_PHY_LED_CTRL,
+ MIIM_88E1121_PHY_LED_DEF);
+ /* Restore the page pointer */
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1121_PHY_PAGE, pg);
+
+ /* Disable IRQs and de-assert interrupt */
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1121_PHY_IRQ_EN, 0);
+ phy_read(phydev, MDIO_DEVAD_NONE, MIIM_88E1121_PHY_IRQ_STATUS);
+
+ return 0;
+}
+
+/* Marvell 88E1145 */
+static int m88e1145_config(struct phy_device *phydev)
+{
+ int reg;
+
+ /* Errata E0, E1 */
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1145_PHY_PAGE, 0x001b);
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1145_PHY_CAL_OV, 0x418f);
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1145_PHY_PAGE, 0x0016);
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1145_PHY_CAL_OV, 0xa2da);
+
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1xxx_PHY_SCR,
+ MIIM_88E1xxx_PHY_MDI_X_AUTO);
+
+ reg = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_88E1145_PHY_EXT_CR);
+ if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
+ reg |= MIIM_M88E1145_RGMII_RX_DELAY |
+ MIIM_M88E1145_RGMII_TX_DELAY;
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1145_PHY_EXT_CR, reg);
+
+ genphy_config_aneg(phydev);
+
+ /* soft reset */
+ reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR);
+ reg |= BMCR_RESET;
+ phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, reg);
+
+ return 0;
+}
+
+static int m88e1145_startup(struct phy_device *phydev)
+{
+ int ret;
+
+ ret = genphy_update_link(phydev);
+ if (ret)
+ return ret;
+
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1145_PHY_LED_CONTROL,
+ MIIM_88E1145_PHY_LED_DIRECT);
+ return m88e1xxx_parse_status(phydev);
+}
+
+/* Marvell 88E1149S */
+static int m88e1149_config(struct phy_device *phydev)
+{
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1149_PHY_PAGE, 0x1f);
+ phy_write(phydev, MDIO_DEVAD_NONE, 0x1e, 0x200c);
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1149_PHY_PAGE, 0x5);
+ phy_write(phydev, MDIO_DEVAD_NONE, 0x1e, 0x0);
+ phy_write(phydev, MDIO_DEVAD_NONE, 0x1e, 0x100);
+
+ genphy_config_aneg(phydev);
+
+ phy_reset(phydev);
+
+ return 0;
+}
+
+/* Marvell 88E1310 */
+static int m88e1310_config(struct phy_device *phydev)
+{
+ u16 reg;
+
+ /* LED link and activity */
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1310_PHY_PAGE, 0x0003);
+ reg = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_88E1310_PHY_LED_CTRL);
+ reg = (reg & ~0xf) | 0x1;
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1310_PHY_LED_CTRL, reg);
+
+ /* Set LED2/INT to INT mode, low active */
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1310_PHY_PAGE, 0x0003);
+ reg = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_88E1310_PHY_IRQ_EN);
+ reg = (reg & 0x77ff) | 0x0880;
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1310_PHY_IRQ_EN, reg);
+
+ /* Set RGMII delay */
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1310_PHY_PAGE, 0x0002);
+ reg = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_88E1310_PHY_RGMII_CTRL);
+ reg |= 0x0030;
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1310_PHY_RGMII_CTRL, reg);
+
+ /* Ensure to return to page 0 */
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1310_PHY_PAGE, 0x0000);
+
+ return genphy_config_aneg(phydev);
+}
+
+static int m88e1680_config(struct phy_device *phydev)
+{
+ /*
+ * As per Marvell Release Notes - Alaska V 88E1680 Rev A2
+ * Errata Section 4.1
+ */
+ u16 reg;
+ int res;
+
+ /* Matrix LED mode (not neede if single LED mode is used */
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1118_PHY_PAGE, 0x0004);
+ reg = phy_read(phydev, MDIO_DEVAD_NONE, 27);
+ reg |= (1 << 5);
+ phy_write(phydev, MDIO_DEVAD_NONE, 27, reg);
+
+ /* QSGMII TX amplitude change */
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1118_PHY_PAGE, 0x00fd);
+ phy_write(phydev, MDIO_DEVAD_NONE, 8, 0x0b53);
+ phy_write(phydev, MDIO_DEVAD_NONE, 7, 0x200d);
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1118_PHY_PAGE, 0x0000);
+
+ /* EEE initialization */
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1118_PHY_PAGE, 0x00ff);
+ phy_write(phydev, MDIO_DEVAD_NONE, 17, 0xb030);
+ phy_write(phydev, MDIO_DEVAD_NONE, 16, 0x215c);
+ phy_write(phydev, MDIO_DEVAD_NONE, 22, 0x00fc);
+ phy_write(phydev, MDIO_DEVAD_NONE, 24, 0x888c);
+ phy_write(phydev, MDIO_DEVAD_NONE, 25, 0x888c);
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1118_PHY_PAGE, 0x0000);
+ phy_write(phydev, MDIO_DEVAD_NONE, 0, 0x9140);
+
+ res = genphy_config_aneg(phydev);
+ if (res < 0)
+ return res;
+
+ /* soft reset */
+ reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR);
+ reg |= BMCR_RESET;
+ phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, reg);
+
+ return 0;
+}
+
+static struct phy_driver M88E1011S_driver = {
+ .name = "Marvell 88E1011S",
+ .uid = 0x1410c60,
+ .mask = 0xffffff0,
+ .features = PHY_GBIT_FEATURES,
+ .config = &m88e1011s_config,
+ .startup = &m88e1011s_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+static struct phy_driver M88E1111S_driver = {
+ .name = "Marvell 88E1111S",
+ .uid = 0x1410cc0,
+ .mask = 0xffffff0,
+ .features = PHY_GBIT_FEATURES,
+ .config = &m88e1111s_config,
+ .startup = &m88e1011s_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+static struct phy_driver M88E1118_driver = {
+ .name = "Marvell 88E1118",
+ .uid = 0x1410e10,
+ .mask = 0xffffff0,
+ .features = PHY_GBIT_FEATURES,
+ .config = &m88e1118_config,
+ .startup = &m88e1118_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+static struct phy_driver M88E1118R_driver = {
+ .name = "Marvell 88E1118R",
+ .uid = 0x1410e40,
+ .mask = 0xffffff0,
+ .features = PHY_GBIT_FEATURES,
+ .config = &m88e1118_config,
+ .startup = &m88e1118_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+static struct phy_driver M88E1121R_driver = {
+ .name = "Marvell 88E1121R",
+ .uid = 0x1410cb0,
+ .mask = 0xffffff0,
+ .features = PHY_GBIT_FEATURES,
+ .config = &m88e1121_config,
+ .startup = &genphy_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+static struct phy_driver M88E1145_driver = {
+ .name = "Marvell 88E1145",
+ .uid = 0x1410cd0,
+ .mask = 0xffffff0,
+ .features = PHY_GBIT_FEATURES,
+ .config = &m88e1145_config,
+ .startup = &m88e1145_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+static struct phy_driver M88E1149S_driver = {
+ .name = "Marvell 88E1149S",
+ .uid = 0x1410ca0,
+ .mask = 0xffffff0,
+ .features = PHY_GBIT_FEATURES,
+ .config = &m88e1149_config,
+ .startup = &m88e1011s_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+static struct phy_driver M88E151x_driver = {
+ .name = "Marvell 88E151x",
+ .uid = 0x1410dd0,
+ .mask = 0xffffff0,
+ .features = PHY_GBIT_FEATURES,
+ .config = &m88e151x_config,
+ .startup = &m88e1011s_startup,
+ .shutdown = &genphy_shutdown,
+ .readext = &m88e1xxx_phy_extread,
+ .writeext = &m88e1xxx_phy_extwrite,
+};
+
+static struct phy_driver M88E1310_driver = {
+ .name = "Marvell 88E1310",
+ .uid = 0x01410e90,
+ .mask = 0xffffff0,
+ .features = PHY_GBIT_FEATURES,
+ .config = &m88e1310_config,
+ .startup = &m88e1011s_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+static struct phy_driver M88E1680_driver = {
+ .name = "Marvell 88E1680",
+ .uid = 0x1410ed0,
+ .mask = 0xffffff0,
+ .features = PHY_GBIT_FEATURES,
+ .config = &m88e1680_config,
+ .startup = &genphy_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+int phy_marvell_init(void)
+{
+ phy_register(&M88E1310_driver);
+ phy_register(&M88E1149S_driver);
+ phy_register(&M88E1145_driver);
+ phy_register(&M88E1121R_driver);
+ phy_register(&M88E1118_driver);
+ phy_register(&M88E1118R_driver);
+ phy_register(&M88E1111S_driver);
+ phy_register(&M88E1011S_driver);
+ phy_register(&M88E151x_driver);
+ phy_register(&M88E1680_driver);
+
+ return 0;
+}
diff --git a/roms/u-boot/drivers/net/phy/meson-gxl.c b/roms/u-boot/drivers/net/phy/meson-gxl.c
new file mode 100644
index 000000000..ffb1a6140
--- /dev/null
+++ b/roms/u-boot/drivers/net/phy/meson-gxl.c
@@ -0,0 +1,142 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Meson GXL Internal PHY Driver
+ *
+ * Copyright (C) 2015 Amlogic, Inc. All rights reserved.
+ * Copyright (C) 2016 BayLibre, SAS. All rights reserved.
+ * Author: Neil Armstrong <narmstrong@baylibre.com>
+ */
+#include <config.h>
+#include <common.h>
+#include <linux/bitops.h>
+#include <dm.h>
+#include <phy.h>
+
+/* This function is provided to cope with the possible failures of this phy
+ * during aneg process. When aneg fails, the PHY reports that aneg is done
+ * but the value found in MII_LPA is wrong:
+ * - Early failures: MII_LPA is just 0x0001. if MII_EXPANSION reports that
+ * the link partner (LP) supports aneg but the LP never acked our base
+ * code word, it is likely that we never sent it to begin with.
+ * - Late failures: MII_LPA is filled with a value which seems to make sense
+ * but it actually is not what the LP is advertising. It seems that we
+ * can detect this using a magic bit in the WOL bank (reg 12 - bit 12).
+ * If this particular bit is not set when aneg is reported being done,
+ * it means MII_LPA is likely to be wrong.
+ *
+ * In both case, forcing a restart of the aneg process solve the problem.
+ * When this failure happens, the first retry is usually successful but,
+ * in some cases, it may take up to 6 retries to get a decent result
+ */
+int meson_gxl_startup(struct phy_device *phydev)
+{
+ unsigned int retries = 10;
+ int ret, wol, lpa, exp;
+
+restart_aneg:
+ ret = genphy_update_link(phydev);
+ if (ret)
+ return ret;
+
+ if (phydev->autoneg == AUTONEG_ENABLE) {
+ /* Need to access WOL bank, make sure the access is open */
+ ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x14, 0x0000);
+ if (ret)
+ return ret;
+ ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x14, 0x0400);
+ if (ret)
+ return ret;
+ ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x14, 0x0000);
+ if (ret)
+ return ret;
+ ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x14, 0x0400);
+ if (ret)
+ return ret;
+
+ /* Request LPI_STATUS WOL register */
+ ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x14, 0x8D80);
+ if (ret)
+ return ret;
+
+ /* Read LPI_STATUS value */
+ wol = phy_read(phydev, MDIO_DEVAD_NONE, 0x15);
+ if (wol < 0)
+ return wol;
+
+ lpa = phy_read(phydev, MDIO_DEVAD_NONE, MII_LPA);
+ if (lpa < 0)
+ return lpa;
+
+ exp = phy_read(phydev, MDIO_DEVAD_NONE, MII_EXPANSION);
+ if (exp < 0)
+ return exp;
+
+ if (!(wol & BIT(12)) ||
+ ((exp & EXPANSION_NWAY) && !(lpa & LPA_LPACK))) {
+
+ /* Looks like aneg failed after all */
+ if (!retries) {
+ printf("%s LPA corruption max attempts\n",
+ phydev->dev->name);
+ return -ETIMEDOUT;
+ }
+
+ printf("%s LPA corruption - aneg restart\n",
+ phydev->dev->name);
+
+ ret = genphy_restart_aneg(phydev);
+ if (ret)
+ return ret;
+
+ --retries;
+
+ goto restart_aneg;
+ }
+ }
+
+ return genphy_parse_link(phydev);
+}
+
+static int meson_gxl_phy_config(struct phy_device *phydev)
+{
+ /* Enable Analog and DSP register Bank access by */
+ phy_write(phydev, MDIO_DEVAD_NONE, 0x14, 0x0000);
+ phy_write(phydev, MDIO_DEVAD_NONE, 0x14, 0x0400);
+ phy_write(phydev, MDIO_DEVAD_NONE, 0x14, 0x0000);
+ phy_write(phydev, MDIO_DEVAD_NONE, 0x14, 0x0400);
+
+ /* Write Analog register 23 */
+ phy_write(phydev, MDIO_DEVAD_NONE, 0x17, 0x8E0D);
+ phy_write(phydev, MDIO_DEVAD_NONE, 0x14, 0x4417);
+
+ /* Enable fractional PLL */
+ phy_write(phydev, MDIO_DEVAD_NONE, 0x17, 0x0005);
+ phy_write(phydev, MDIO_DEVAD_NONE, 0x14, 0x5C1B);
+
+ /* Program fraction FR_PLL_DIV1 */
+ phy_write(phydev, MDIO_DEVAD_NONE, 0x17, 0x029A);
+ phy_write(phydev, MDIO_DEVAD_NONE, 0x14, 0x5C1D);
+
+ /* Program fraction FR_PLL_DIV1 */
+ phy_write(phydev, MDIO_DEVAD_NONE, 0x17, 0xAAAA);
+ phy_write(phydev, MDIO_DEVAD_NONE, 0x14, 0x5C1C);
+
+ return genphy_config(phydev);
+}
+
+static struct phy_driver meson_gxl_phy_driver = {
+ .name = "Meson GXL Internal PHY",
+ .uid = 0x01814400,
+ .mask = 0xfffffff0,
+ .features = PHY_BASIC_FEATURES,
+ .config = &meson_gxl_phy_config,
+ .startup = &meson_gxl_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+int phy_meson_gxl_init(void)
+{
+ phy_register(&meson_gxl_phy_driver);
+
+ return 0;
+}
diff --git a/roms/u-boot/drivers/net/phy/micrel_ksz8xxx.c b/roms/u-boot/drivers/net/phy/micrel_ksz8xxx.c
new file mode 100644
index 000000000..60d42fe98
--- /dev/null
+++ b/roms/u-boot/drivers/net/phy/micrel_ksz8xxx.c
@@ -0,0 +1,234 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Micrel PHY drivers
+ *
+ * Copyright 2010-2011 Freescale Semiconductor, Inc.
+ * author Andy Fleming
+ * (C) 2012 NetModule AG, David Andrey, added KSZ9031
+ */
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <fdtdec.h>
+#include <micrel.h>
+#include <phy.h>
+#include <linux/bitops.h>
+
+static struct phy_driver KSZ804_driver = {
+ .name = "Micrel KSZ804",
+ .uid = 0x221510,
+ .mask = 0xfffff0,
+ .features = PHY_BASIC_FEATURES,
+ .config = &genphy_config,
+ .startup = &genphy_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+#define MII_KSZPHY_OMSO 0x16
+#define KSZPHY_OMSO_FACTORY_TEST BIT(15)
+#define KSZPHY_OMSO_B_CAST_OFF (1 << 9)
+
+static int ksz_genconfig_bcastoff(struct phy_device *phydev)
+{
+ int ret;
+
+ ret = phy_read(phydev, MDIO_DEVAD_NONE, MII_KSZPHY_OMSO);
+ if (ret < 0)
+ return ret;
+
+ ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_KSZPHY_OMSO,
+ ret | KSZPHY_OMSO_B_CAST_OFF);
+ if (ret < 0)
+ return ret;
+
+ return genphy_config(phydev);
+}
+
+static struct phy_driver KSZ8031_driver = {
+ .name = "Micrel KSZ8021/KSZ8031",
+ .uid = 0x221550,
+ .mask = 0xfffff0,
+ .features = PHY_BASIC_FEATURES,
+ .config = &ksz_genconfig_bcastoff,
+ .startup = &genphy_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+/**
+ * KSZ8051
+ */
+#define MII_KSZ8051_PHY_OMSO 0x16
+#define MII_KSZ8051_PHY_OMSO_NAND_TREE_ON (1 << 5)
+
+static int ksz8051_config(struct phy_device *phydev)
+{
+ unsigned val;
+
+ /* Disable NAND-tree */
+ val = phy_read(phydev, MDIO_DEVAD_NONE, MII_KSZ8051_PHY_OMSO);
+ val &= ~MII_KSZ8051_PHY_OMSO_NAND_TREE_ON;
+ phy_write(phydev, MDIO_DEVAD_NONE, MII_KSZ8051_PHY_OMSO, val);
+
+ return genphy_config(phydev);
+}
+
+static struct phy_driver KSZ8051_driver = {
+ .name = "Micrel KSZ8051",
+ .uid = 0x221550,
+ .mask = 0xfffff0,
+ .features = PHY_BASIC_FEATURES,
+ .config = &ksz8051_config,
+ .startup = &genphy_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+static int ksz8061_config(struct phy_device *phydev)
+{
+ return phy_write(phydev, MDIO_MMD_PMAPMD, MDIO_DEVID1, 0xB61A);
+}
+
+static struct phy_driver KSZ8061_driver = {
+ .name = "Micrel KSZ8061",
+ .uid = 0x00221570,
+ .mask = 0xfffff0,
+ .features = PHY_BASIC_FEATURES,
+ .config = &ksz8061_config,
+ .startup = &genphy_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+static int ksz8081_config(struct phy_device *phydev)
+{
+ int ret;
+
+ ret = phy_read(phydev, MDIO_DEVAD_NONE, MII_KSZPHY_OMSO);
+ if (ret < 0)
+ return ret;
+
+ ret &= ~KSZPHY_OMSO_FACTORY_TEST;
+
+ ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_KSZPHY_OMSO,
+ ret | KSZPHY_OMSO_B_CAST_OFF);
+ if (ret < 0)
+ return ret;
+
+ return genphy_config(phydev);
+}
+
+static struct phy_driver KSZ8081_driver = {
+ .name = "Micrel KSZ8081",
+ .uid = 0x221560,
+ .mask = 0xfffff0,
+ .features = PHY_BASIC_FEATURES,
+ .config = &ksz8081_config,
+ .startup = &genphy_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+/**
+ * KSZ8895
+ */
+
+static unsigned short smireg_to_phy(unsigned short reg)
+{
+ return ((reg & 0xc0) >> 3) + 0x06 + ((reg & 0x20) >> 5);
+}
+
+static unsigned short smireg_to_reg(unsigned short reg)
+{
+ return reg & 0x1F;
+}
+
+static void ksz8895_write_smireg(struct phy_device *phydev, int smireg, int val)
+{
+ phydev->bus->write(phydev->bus, smireg_to_phy(smireg), MDIO_DEVAD_NONE,
+ smireg_to_reg(smireg), val);
+}
+
+#if 0
+static int ksz8895_read_smireg(struct phy_device *phydev, int smireg)
+{
+ return phydev->bus->read(phydev->bus, smireg_to_phy(smireg),
+ MDIO_DEVAD_NONE, smireg_to_reg(smireg));
+}
+#endif
+
+int ksz8895_config(struct phy_device *phydev)
+{
+ /* we are connected directly to the switch without
+ * dedicated PHY. SCONF1 == 001 */
+ phydev->link = 1;
+ phydev->duplex = DUPLEX_FULL;
+ phydev->speed = SPEED_100;
+
+ /* Force the switch to start */
+ ksz8895_write_smireg(phydev, 1, 1);
+
+ return 0;
+}
+
+static int ksz8895_startup(struct phy_device *phydev)
+{
+ return 0;
+}
+
+static struct phy_driver ksz8895_driver = {
+ .name = "Micrel KSZ8895/KSZ8864",
+ .uid = 0x221450,
+ .mask = 0xffffe1,
+ .features = PHY_BASIC_FEATURES,
+ .config = &ksz8895_config,
+ .startup = &ksz8895_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+/* Micrel used the exact same model number for the KSZ9021,
+ * so the revision number is used to distinguish them.
+ */
+static struct phy_driver KS8721_driver = {
+ .name = "Micrel KS8721BL",
+ .uid = 0x221618,
+ .mask = 0xfffffc,
+ .features = PHY_BASIC_FEATURES,
+ .config = &genphy_config,
+ .startup = &genphy_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+int ksz886x_config(struct phy_device *phydev)
+{
+ /* we are connected directly to the switch without
+ * dedicated PHY. */
+ phydev->link = 1;
+ phydev->duplex = DUPLEX_FULL;
+ phydev->speed = SPEED_100;
+ return 0;
+}
+
+static int ksz886x_startup(struct phy_device *phydev)
+{
+ return 0;
+}
+
+static struct phy_driver ksz886x_driver = {
+ .name = "Micrel KSZ886x Switch",
+ .uid = 0x00221430,
+ .mask = 0xfffff0,
+ .features = PHY_BASIC_FEATURES,
+ .config = &ksz886x_config,
+ .startup = &ksz886x_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+int phy_micrel_ksz8xxx_init(void)
+{
+ phy_register(&KSZ804_driver);
+ phy_register(&KSZ8031_driver);
+ phy_register(&KSZ8051_driver);
+ phy_register(&KSZ8061_driver);
+ phy_register(&KSZ8081_driver);
+ phy_register(&KS8721_driver);
+ phy_register(&ksz8895_driver);
+ phy_register(&ksz886x_driver);
+ return 0;
+}
diff --git a/roms/u-boot/drivers/net/phy/micrel_ksz90x1.c b/roms/u-boot/drivers/net/phy/micrel_ksz90x1.c
new file mode 100644
index 000000000..e5f578201
--- /dev/null
+++ b/roms/u-boot/drivers/net/phy/micrel_ksz90x1.c
@@ -0,0 +1,525 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Micrel PHY drivers
+ *
+ * Copyright 2010-2011 Freescale Semiconductor, Inc.
+ * author Andy Fleming
+ * (C) 2012 NetModule AG, David Andrey, added KSZ9031
+ * (C) Copyright 2017 Adaptrum, Inc.
+ * Written by Alexandru Gagniuc <alex.g@adaptrum.com> for Adaptrum, Inc.
+ */
+#include <common.h>
+#include <dm.h>
+#include <env.h>
+#include <errno.h>
+#include <micrel.h>
+#include <phy.h>
+
+/*
+ * KSZ9021 - KSZ9031 common
+ */
+
+#define MII_KSZ90xx_PHY_CTL 0x1f
+#define MIIM_KSZ90xx_PHYCTL_1000 (1 << 6)
+#define MIIM_KSZ90xx_PHYCTL_100 (1 << 5)
+#define MIIM_KSZ90xx_PHYCTL_10 (1 << 4)
+#define MIIM_KSZ90xx_PHYCTL_DUPLEX (1 << 3)
+
+/* KSZ9021 PHY Registers */
+#define MII_KSZ9021_EXTENDED_CTRL 0x0b
+#define MII_KSZ9021_EXTENDED_DATAW 0x0c
+#define MII_KSZ9021_EXTENDED_DATAR 0x0d
+
+#define CTRL1000_PREFER_MASTER (1 << 10)
+#define CTRL1000_CONFIG_MASTER (1 << 11)
+#define CTRL1000_MANUAL_CONFIG (1 << 12)
+
+#define KSZ9021_PS_TO_REG 120
+
+/* KSZ9031 PHY Registers */
+#define MII_KSZ9031_MMD_ACCES_CTRL 0x0d
+#define MII_KSZ9031_MMD_REG_DATA 0x0e
+
+#define KSZ9031_PS_TO_REG 60
+
+static int ksz90xx_startup(struct phy_device *phydev)
+{
+ unsigned phy_ctl;
+ int ret;
+
+ ret = genphy_update_link(phydev);
+ if (ret)
+ return ret;
+
+ phy_ctl = phy_read(phydev, MDIO_DEVAD_NONE, MII_KSZ90xx_PHY_CTL);
+
+ if (phy_ctl & MIIM_KSZ90xx_PHYCTL_DUPLEX)
+ phydev->duplex = DUPLEX_FULL;
+ else
+ phydev->duplex = DUPLEX_HALF;
+
+ if (phy_ctl & MIIM_KSZ90xx_PHYCTL_1000)
+ phydev->speed = SPEED_1000;
+ else if (phy_ctl & MIIM_KSZ90xx_PHYCTL_100)
+ phydev->speed = SPEED_100;
+ else if (phy_ctl & MIIM_KSZ90xx_PHYCTL_10)
+ phydev->speed = SPEED_10;
+ return 0;
+}
+
+/* Common OF config bits for KSZ9021 and KSZ9031 */
+#ifdef CONFIG_DM_ETH
+struct ksz90x1_reg_field {
+ const char *name;
+ const u8 size; /* Size of the bitfield, in bits */
+ const u8 off; /* Offset from bit 0 */
+ const u8 dflt; /* Default value */
+};
+
+struct ksz90x1_ofcfg {
+ const u16 reg;
+ const u16 devad;
+ const struct ksz90x1_reg_field *grp;
+ const u16 grpsz;
+};
+
+static const struct ksz90x1_reg_field ksz90x1_rxd_grp[] = {
+ { "rxd0-skew-ps", 4, 0, 0x7 }, { "rxd1-skew-ps", 4, 4, 0x7 },
+ { "rxd2-skew-ps", 4, 8, 0x7 }, { "rxd3-skew-ps", 4, 12, 0x7 }
+};
+
+static const struct ksz90x1_reg_field ksz90x1_txd_grp[] = {
+ { "txd0-skew-ps", 4, 0, 0x7 }, { "txd1-skew-ps", 4, 4, 0x7 },
+ { "txd2-skew-ps", 4, 8, 0x7 }, { "txd3-skew-ps", 4, 12, 0x7 },
+};
+
+static const struct ksz90x1_reg_field ksz9021_clk_grp[] = {
+ { "txen-skew-ps", 4, 0, 0x7 }, { "txc-skew-ps", 4, 4, 0x7 },
+ { "rxdv-skew-ps", 4, 8, 0x7 }, { "rxc-skew-ps", 4, 12, 0x7 },
+};
+
+static const struct ksz90x1_reg_field ksz9031_ctl_grp[] = {
+ { "txen-skew-ps", 4, 0, 0x7 }, { "rxdv-skew-ps", 4, 4, 0x7 }
+};
+
+static const struct ksz90x1_reg_field ksz9031_clk_grp[] = {
+ { "rxc-skew-ps", 5, 0, 0xf }, { "txc-skew-ps", 5, 5, 0xf }
+};
+
+static int ksz90x1_of_config_group(struct phy_device *phydev,
+ struct ksz90x1_ofcfg *ofcfg,
+ int ps_to_regval)
+{
+ struct udevice *dev = phydev->dev;
+ struct phy_driver *drv = phydev->drv;
+ int val[4];
+ int i, changed = 0, offset, max;
+ u16 regval = 0;
+ ofnode node;
+
+ if (!drv || !drv->writeext)
+ return -EOPNOTSUPP;
+
+ node = phydev->node;
+
+ if (!ofnode_valid(node)) {
+ /* Look for a PHY node under the Ethernet node */
+ node = dev_read_subnode(dev, "ethernet-phy");
+ }
+
+ if (!ofnode_valid(node)) {
+ /* No node found, look in the Ethernet node */
+ node = dev_ofnode(dev);
+ }
+
+ for (i = 0; i < ofcfg->grpsz; i++) {
+ val[i] = ofnode_read_u32_default(node, ofcfg->grp[i].name, ~0);
+ offset = ofcfg->grp[i].off;
+ if (val[i] == -1) {
+ /* Default register value for KSZ9021 */
+ regval |= ofcfg->grp[i].dflt << offset;
+ } else {
+ changed = 1; /* Value was changed in OF */
+ /* Calculate the register value and fix corner cases */
+ max = (1 << ofcfg->grp[i].size) - 1;
+ if (val[i] > ps_to_regval * max) {
+ regval |= max << offset;
+ } else {
+ regval |= (val[i] / ps_to_regval) << offset;
+ }
+ }
+ }
+
+ if (!changed)
+ return 0;
+
+ return drv->writeext(phydev, 0, ofcfg->devad, ofcfg->reg, regval);
+}
+
+static int ksz9021_of_config(struct phy_device *phydev)
+{
+ struct ksz90x1_ofcfg ofcfg[] = {
+ { MII_KSZ9021_EXT_RGMII_RX_DATA_SKEW, 0, ksz90x1_rxd_grp, 4 },
+ { MII_KSZ9021_EXT_RGMII_TX_DATA_SKEW, 0, ksz90x1_txd_grp, 4 },
+ { MII_KSZ9021_EXT_RGMII_CLOCK_SKEW, 0, ksz9021_clk_grp, 4 },
+ };
+ int i, ret = 0;
+
+ for (i = 0; i < ARRAY_SIZE(ofcfg); i++) {
+ ret = ksz90x1_of_config_group(phydev, &ofcfg[i],
+ KSZ9021_PS_TO_REG);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ksz9031_of_config(struct phy_device *phydev)
+{
+ struct ksz90x1_ofcfg ofcfg[] = {
+ { MII_KSZ9031_EXT_RGMII_CTRL_SIG_SKEW, 2, ksz9031_ctl_grp, 2 },
+ { MII_KSZ9031_EXT_RGMII_RX_DATA_SKEW, 2, ksz90x1_rxd_grp, 4 },
+ { MII_KSZ9031_EXT_RGMII_TX_DATA_SKEW, 2, ksz90x1_txd_grp, 4 },
+ { MII_KSZ9031_EXT_RGMII_CLOCK_SKEW, 2, ksz9031_clk_grp, 2 },
+ };
+ int i, ret = 0;
+
+ for (i = 0; i < ARRAY_SIZE(ofcfg); i++) {
+ ret = ksz90x1_of_config_group(phydev, &ofcfg[i],
+ KSZ9031_PS_TO_REG);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ksz9031_center_flp_timing(struct phy_device *phydev)
+{
+ struct phy_driver *drv = phydev->drv;
+ int ret = 0;
+
+ if (!drv || !drv->writeext)
+ return -EOPNOTSUPP;
+
+ ret = drv->writeext(phydev, 0, 0, MII_KSZ9031_FLP_BURST_TX_LO, 0x1A80);
+ if (ret)
+ return ret;
+
+ ret = drv->writeext(phydev, 0, 0, MII_KSZ9031_FLP_BURST_TX_HI, 0x6);
+ return ret;
+}
+
+#else /* !CONFIG_DM_ETH */
+static int ksz9021_of_config(struct phy_device *phydev)
+{
+ return 0;
+}
+
+static int ksz9031_of_config(struct phy_device *phydev)
+{
+ return 0;
+}
+
+static int ksz9031_center_flp_timing(struct phy_device *phydev)
+{
+ return 0;
+}
+#endif
+
+/*
+ * KSZ9021
+ */
+int ksz9021_phy_extended_write(struct phy_device *phydev, int regnum, u16 val)
+{
+ /* extended registers */
+ phy_write(phydev, MDIO_DEVAD_NONE,
+ MII_KSZ9021_EXTENDED_CTRL, regnum | 0x8000);
+ return phy_write(phydev, MDIO_DEVAD_NONE,
+ MII_KSZ9021_EXTENDED_DATAW, val);
+}
+
+int ksz9021_phy_extended_read(struct phy_device *phydev, int regnum)
+{
+ /* extended registers */
+ phy_write(phydev, MDIO_DEVAD_NONE, MII_KSZ9021_EXTENDED_CTRL, regnum);
+ return phy_read(phydev, MDIO_DEVAD_NONE, MII_KSZ9021_EXTENDED_DATAR);
+}
+
+
+static int ksz9021_phy_extread(struct phy_device *phydev, int addr, int devaddr,
+ int regnum)
+{
+ return ksz9021_phy_extended_read(phydev, regnum);
+}
+
+static int ksz9021_phy_extwrite(struct phy_device *phydev, int addr,
+ int devaddr, int regnum, u16 val)
+{
+ return ksz9021_phy_extended_write(phydev, regnum, val);
+}
+
+static int ksz9021_config(struct phy_device *phydev)
+{
+ unsigned ctrl1000 = 0;
+ const unsigned master = CTRL1000_PREFER_MASTER |
+ CTRL1000_CONFIG_MASTER | CTRL1000_MANUAL_CONFIG;
+ unsigned features = phydev->drv->features;
+ int ret;
+
+ ret = ksz9021_of_config(phydev);
+ if (ret)
+ return ret;
+
+ if (env_get("disable_giga"))
+ features &= ~(SUPPORTED_1000baseT_Half |
+ SUPPORTED_1000baseT_Full);
+ /* force master mode for 1000BaseT due to chip errata */
+ if (features & SUPPORTED_1000baseT_Half)
+ ctrl1000 |= ADVERTISE_1000HALF | master;
+ if (features & SUPPORTED_1000baseT_Full)
+ ctrl1000 |= ADVERTISE_1000FULL | master;
+ phydev->advertising = features;
+ phydev->supported = features;
+ phy_write(phydev, MDIO_DEVAD_NONE, MII_CTRL1000, ctrl1000);
+ genphy_config_aneg(phydev);
+ genphy_restart_aneg(phydev);
+ return 0;
+}
+
+static struct phy_driver ksz9021_driver = {
+ .name = "Micrel ksz9021",
+ .uid = 0x221610,
+ .mask = 0xfffffe,
+ .features = PHY_GBIT_FEATURES,
+ .config = &ksz9021_config,
+ .startup = &ksz90xx_startup,
+ .shutdown = &genphy_shutdown,
+ .writeext = &ksz9021_phy_extwrite,
+ .readext = &ksz9021_phy_extread,
+};
+
+/*
+ * KSZ9031
+ */
+int ksz9031_phy_extended_write(struct phy_device *phydev,
+ int devaddr, int regnum, u16 mode, u16 val)
+{
+ /*select register addr for mmd*/
+ phy_write(phydev, MDIO_DEVAD_NONE,
+ MII_KSZ9031_MMD_ACCES_CTRL, devaddr);
+ /*select register for mmd*/
+ phy_write(phydev, MDIO_DEVAD_NONE,
+ MII_KSZ9031_MMD_REG_DATA, regnum);
+ /*setup mode*/
+ phy_write(phydev, MDIO_DEVAD_NONE,
+ MII_KSZ9031_MMD_ACCES_CTRL, (mode | devaddr));
+ /*write the value*/
+ return phy_write(phydev, MDIO_DEVAD_NONE,
+ MII_KSZ9031_MMD_REG_DATA, val);
+}
+
+int ksz9031_phy_extended_read(struct phy_device *phydev, int devaddr,
+ int regnum, u16 mode)
+{
+ phy_write(phydev, MDIO_DEVAD_NONE,
+ MII_KSZ9031_MMD_ACCES_CTRL, devaddr);
+ phy_write(phydev, MDIO_DEVAD_NONE,
+ MII_KSZ9031_MMD_REG_DATA, regnum);
+ phy_write(phydev, MDIO_DEVAD_NONE,
+ MII_KSZ9031_MMD_ACCES_CTRL, (devaddr | mode));
+ return phy_read(phydev, MDIO_DEVAD_NONE, MII_KSZ9031_MMD_REG_DATA);
+}
+
+static int ksz9031_phy_extread(struct phy_device *phydev, int addr, int devaddr,
+ int regnum)
+{
+ return ksz9031_phy_extended_read(phydev, devaddr, regnum,
+ MII_KSZ9031_MOD_DATA_NO_POST_INC);
+}
+
+static int ksz9031_phy_extwrite(struct phy_device *phydev, int addr,
+ int devaddr, int regnum, u16 val)
+{
+ return ksz9031_phy_extended_write(phydev, devaddr, regnum,
+ MII_KSZ9031_MOD_DATA_POST_INC_RW, val);
+}
+
+static int ksz9031_config(struct phy_device *phydev)
+{
+ int ret;
+
+ ret = ksz9031_of_config(phydev);
+ if (ret)
+ return ret;
+ ret = ksz9031_center_flp_timing(phydev);
+ if (ret)
+ return ret;
+
+ /* add an option to disable the gigabit feature of this PHY */
+ if (env_get("disable_giga")) {
+ unsigned features;
+ unsigned bmcr;
+
+ /* disable speed 1000 in features supported by the PHY */
+ features = phydev->drv->features;
+ features &= ~(SUPPORTED_1000baseT_Half |
+ SUPPORTED_1000baseT_Full);
+ phydev->advertising = phydev->supported = features;
+
+ /* disable speed 1000 in Basic Control Register */
+ bmcr = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR);
+ bmcr &= ~(1 << 6);
+ phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, bmcr);
+
+ /* disable speed 1000 in 1000Base-T Control Register */
+ phy_write(phydev, MDIO_DEVAD_NONE, MII_CTRL1000, 0);
+
+ /* start autoneg */
+ genphy_config_aneg(phydev);
+ genphy_restart_aneg(phydev);
+
+ return 0;
+ }
+
+ return genphy_config(phydev);
+}
+
+static struct phy_driver ksz9031_driver = {
+ .name = "Micrel ksz9031",
+ .uid = PHY_ID_KSZ9031,
+ .mask = MII_KSZ9x31_SILICON_REV_MASK,
+ .features = PHY_GBIT_FEATURES,
+ .config = &ksz9031_config,
+ .startup = &ksz90xx_startup,
+ .shutdown = &genphy_shutdown,
+ .writeext = &ksz9031_phy_extwrite,
+ .readext = &ksz9031_phy_extread,
+};
+
+/*
+ * KSZ9131
+ */
+
+#define KSZ9131RN_MMD_COMMON_CTRL_REG 2
+#define KSZ9131RN_RXC_DLL_CTRL 76
+#define KSZ9131RN_TXC_DLL_CTRL 77
+#define KSZ9131RN_DLL_CTRL_BYPASS BIT_MASK(12)
+#define KSZ9131RN_DLL_ENABLE_DELAY 0
+#define KSZ9131RN_DLL_DISABLE_DELAY BIT(12)
+
+static int ksz9131_config_rgmii_delay(struct phy_device *phydev)
+{
+ struct phy_driver *drv = phydev->drv;
+ u16 rxcdll_val, txcdll_val, val;
+ int ret;
+
+ switch (phydev->interface) {
+ case PHY_INTERFACE_MODE_RGMII:
+ rxcdll_val = KSZ9131RN_DLL_DISABLE_DELAY;
+ txcdll_val = KSZ9131RN_DLL_DISABLE_DELAY;
+ break;
+ case PHY_INTERFACE_MODE_RGMII_ID:
+ rxcdll_val = KSZ9131RN_DLL_ENABLE_DELAY;
+ txcdll_val = KSZ9131RN_DLL_ENABLE_DELAY;
+ break;
+ case PHY_INTERFACE_MODE_RGMII_RXID:
+ rxcdll_val = KSZ9131RN_DLL_ENABLE_DELAY;
+ txcdll_val = KSZ9131RN_DLL_DISABLE_DELAY;
+ break;
+ case PHY_INTERFACE_MODE_RGMII_TXID:
+ rxcdll_val = KSZ9131RN_DLL_DISABLE_DELAY;
+ txcdll_val = KSZ9131RN_DLL_ENABLE_DELAY;
+ break;
+ default:
+ return 0;
+ }
+
+ val = drv->readext(phydev, 0, KSZ9131RN_MMD_COMMON_CTRL_REG,
+ KSZ9131RN_RXC_DLL_CTRL);
+ val &= ~KSZ9131RN_DLL_CTRL_BYPASS;
+ val |= rxcdll_val;
+ ret = drv->writeext(phydev, 0, KSZ9131RN_MMD_COMMON_CTRL_REG,
+ KSZ9131RN_RXC_DLL_CTRL, val);
+ if (ret)
+ return ret;
+
+ val = drv->readext(phydev, 0, KSZ9131RN_MMD_COMMON_CTRL_REG,
+ KSZ9131RN_TXC_DLL_CTRL);
+
+ val &= ~KSZ9131RN_DLL_CTRL_BYPASS;
+ val |= txcdll_val;
+ ret = drv->writeext(phydev, 0, KSZ9131RN_MMD_COMMON_CTRL_REG,
+ KSZ9131RN_TXC_DLL_CTRL, val);
+
+ return ret;
+}
+
+static int ksz9131_config(struct phy_device *phydev)
+{
+ int ret;
+
+ if (phy_interface_is_rgmii(phydev)) {
+ ret = ksz9131_config_rgmii_delay(phydev);
+ if (ret)
+ return ret;
+ }
+
+ /* add an option to disable the gigabit feature of this PHY */
+ if (env_get("disable_giga")) {
+ unsigned features;
+ unsigned bmcr;
+
+ /* disable speed 1000 in features supported by the PHY */
+ features = phydev->drv->features;
+ features &= ~(SUPPORTED_1000baseT_Half |
+ SUPPORTED_1000baseT_Full);
+ phydev->advertising = phydev->supported = features;
+
+ /* disable speed 1000 in Basic Control Register */
+ bmcr = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR);
+ bmcr &= ~(1 << 6);
+ phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, bmcr);
+
+ /* disable speed 1000 in 1000Base-T Control Register */
+ phy_write(phydev, MDIO_DEVAD_NONE, MII_CTRL1000, 0);
+
+ /* start autoneg */
+ genphy_config_aneg(phydev);
+ genphy_restart_aneg(phydev);
+
+ return 0;
+ }
+
+ return genphy_config(phydev);
+}
+
+static struct phy_driver ksz9131_driver = {
+ .name = "Micrel ksz9131",
+ .uid = PHY_ID_KSZ9131,
+ .mask = MII_KSZ9x31_SILICON_REV_MASK,
+ .features = PHY_GBIT_FEATURES,
+ .config = &ksz9131_config,
+ .startup = &ksz90xx_startup,
+ .shutdown = &genphy_shutdown,
+ .writeext = &ksz9031_phy_extwrite,
+ .readext = &ksz9031_phy_extread,
+};
+
+int ksz9xx1_phy_get_id(struct phy_device *phydev)
+{
+ unsigned int phyid;
+
+ get_phy_id(phydev->bus, phydev->addr, MDIO_DEVAD_NONE, &phyid);
+
+ return phyid;
+}
+
+int phy_micrel_ksz90x1_init(void)
+{
+ phy_register(&ksz9021_driver);
+ phy_register(&ksz9031_driver);
+ phy_register(&ksz9131_driver);
+ return 0;
+}
diff --git a/roms/u-boot/drivers/net/phy/miiphybb.c b/roms/u-boot/drivers/net/phy/miiphybb.c
new file mode 100644
index 000000000..24d617553
--- /dev/null
+++ b/roms/u-boot/drivers/net/phy/miiphybb.c
@@ -0,0 +1,357 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2009 Industrie Dial Face S.p.A.
+ * Luigi 'Comio' Mantellini <luigi.mantellini@idf-hit.com>
+ *
+ * (C) Copyright 2001
+ * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com.
+ */
+
+/*
+ * This provides a bit-banged interface to the ethernet MII management
+ * channel.
+ */
+
+#include <common.h>
+#include <ioports.h>
+#include <ppc_asm.tmpl>
+#include <miiphy.h>
+#include <asm/global_data.h>
+
+#define BB_MII_RELOCATE(v,off) (v += (v?off:0))
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#ifndef CONFIG_BITBANGMII_MULTI
+
+/*
+ * If CONFIG_BITBANGMII_MULTI is not defined we use a
+ * compatibility layer with the previous miiphybb implementation
+ * based on macros usage.
+ *
+ */
+static int bb_mii_init_wrap(struct bb_miiphy_bus *bus)
+{
+#ifdef MII_INIT
+ MII_INIT;
+#endif
+ return 0;
+}
+
+static int bb_mdio_active_wrap(struct bb_miiphy_bus *bus)
+{
+#ifdef MDIO_DECLARE
+ MDIO_DECLARE;
+#endif
+ MDIO_ACTIVE;
+ return 0;
+}
+
+static int bb_mdio_tristate_wrap(struct bb_miiphy_bus *bus)
+{
+#ifdef MDIO_DECLARE
+ MDIO_DECLARE;
+#endif
+ MDIO_TRISTATE;
+ return 0;
+}
+
+static int bb_set_mdio_wrap(struct bb_miiphy_bus *bus, int v)
+{
+#ifdef MDIO_DECLARE
+ MDIO_DECLARE;
+#endif
+ MDIO(v);
+ return 0;
+}
+
+static int bb_get_mdio_wrap(struct bb_miiphy_bus *bus, int *v)
+{
+#ifdef MDIO_DECLARE
+ MDIO_DECLARE;
+#endif
+ *v = MDIO_READ;
+ return 0;
+}
+
+static int bb_set_mdc_wrap(struct bb_miiphy_bus *bus, int v)
+{
+#ifdef MDC_DECLARE
+ MDC_DECLARE;
+#endif
+ MDC(v);
+ return 0;
+}
+
+static int bb_delay_wrap(struct bb_miiphy_bus *bus)
+{
+ MIIDELAY;
+ return 0;
+}
+
+struct bb_miiphy_bus bb_miiphy_buses[] = {
+ {
+ .name = BB_MII_DEVNAME,
+ .init = bb_mii_init_wrap,
+ .mdio_active = bb_mdio_active_wrap,
+ .mdio_tristate = bb_mdio_tristate_wrap,
+ .set_mdio = bb_set_mdio_wrap,
+ .get_mdio = bb_get_mdio_wrap,
+ .set_mdc = bb_set_mdc_wrap,
+ .delay = bb_delay_wrap,
+ }
+};
+
+int bb_miiphy_buses_num = sizeof(bb_miiphy_buses) /
+ sizeof(bb_miiphy_buses[0]);
+#endif
+
+int bb_miiphy_init(void)
+{
+ int i;
+
+ for (i = 0; i < bb_miiphy_buses_num; i++) {
+#if defined(CONFIG_NEEDS_MANUAL_RELOC)
+ /* Relocate the hook pointers*/
+ BB_MII_RELOCATE(bb_miiphy_buses[i].init, gd->reloc_off);
+ BB_MII_RELOCATE(bb_miiphy_buses[i].mdio_active, gd->reloc_off);
+ BB_MII_RELOCATE(bb_miiphy_buses[i].mdio_tristate, gd->reloc_off);
+ BB_MII_RELOCATE(bb_miiphy_buses[i].set_mdio, gd->reloc_off);
+ BB_MII_RELOCATE(bb_miiphy_buses[i].get_mdio, gd->reloc_off);
+ BB_MII_RELOCATE(bb_miiphy_buses[i].set_mdc, gd->reloc_off);
+ BB_MII_RELOCATE(bb_miiphy_buses[i].delay, gd->reloc_off);
+#endif
+ if (bb_miiphy_buses[i].init != NULL) {
+ bb_miiphy_buses[i].init(&bb_miiphy_buses[i]);
+ }
+ }
+
+ return 0;
+}
+
+static inline struct bb_miiphy_bus *bb_miiphy_getbus(const char *devname)
+{
+#ifdef CONFIG_BITBANGMII_MULTI
+ int i;
+
+ /* Search the correct bus */
+ for (i = 0; i < bb_miiphy_buses_num; i++) {
+ if (!strcmp(bb_miiphy_buses[i].name, devname)) {
+ return &bb_miiphy_buses[i];
+ }
+ }
+ return NULL;
+#else
+ /* We have just one bitbanging bus */
+ return &bb_miiphy_buses[0];
+#endif
+}
+
+/*****************************************************************************
+ *
+ * Utility to send the preamble, address, and register (common to read
+ * and write).
+ */
+static void miiphy_pre(struct bb_miiphy_bus *bus, char read,
+ unsigned char addr, unsigned char reg)
+{
+ int j;
+
+ /*
+ * Send a 32 bit preamble ('1's) with an extra '1' bit for good measure.
+ * The IEEE spec says this is a PHY optional requirement. The AMD
+ * 79C874 requires one after power up and one after a MII communications
+ * error. This means that we are doing more preambles than we need,
+ * but it is safer and will be much more robust.
+ */
+
+ bus->mdio_active(bus);
+ bus->set_mdio(bus, 1);
+ for (j = 0; j < 32; j++) {
+ bus->set_mdc(bus, 0);
+ bus->delay(bus);
+ bus->set_mdc(bus, 1);
+ bus->delay(bus);
+ }
+
+ /* send the start bit (01) and the read opcode (10) or write (10) */
+ bus->set_mdc(bus, 0);
+ bus->set_mdio(bus, 0);
+ bus->delay(bus);
+ bus->set_mdc(bus, 1);
+ bus->delay(bus);
+ bus->set_mdc(bus, 0);
+ bus->set_mdio(bus, 1);
+ bus->delay(bus);
+ bus->set_mdc(bus, 1);
+ bus->delay(bus);
+ bus->set_mdc(bus, 0);
+ bus->set_mdio(bus, read);
+ bus->delay(bus);
+ bus->set_mdc(bus, 1);
+ bus->delay(bus);
+ bus->set_mdc(bus, 0);
+ bus->set_mdio(bus, !read);
+ bus->delay(bus);
+ bus->set_mdc(bus, 1);
+ bus->delay(bus);
+
+ /* send the PHY address */
+ for (j = 0; j < 5; j++) {
+ bus->set_mdc(bus, 0);
+ if ((addr & 0x10) == 0) {
+ bus->set_mdio(bus, 0);
+ } else {
+ bus->set_mdio(bus, 1);
+ }
+ bus->delay(bus);
+ bus->set_mdc(bus, 1);
+ bus->delay(bus);
+ addr <<= 1;
+ }
+
+ /* send the register address */
+ for (j = 0; j < 5; j++) {
+ bus->set_mdc(bus, 0);
+ if ((reg & 0x10) == 0) {
+ bus->set_mdio(bus, 0);
+ } else {
+ bus->set_mdio(bus, 1);
+ }
+ bus->delay(bus);
+ bus->set_mdc(bus, 1);
+ bus->delay(bus);
+ reg <<= 1;
+ }
+}
+
+/*****************************************************************************
+ *
+ * Read a MII PHY register.
+ *
+ * Returns:
+ * 0 on success
+ */
+int bb_miiphy_read(struct mii_dev *miidev, int addr, int devad, int reg)
+{
+ unsigned short rdreg; /* register working value */
+ int v;
+ int j; /* counter */
+ struct bb_miiphy_bus *bus;
+
+ bus = bb_miiphy_getbus(miidev->name);
+ if (bus == NULL) {
+ return -1;
+ }
+
+ miiphy_pre (bus, 1, addr, reg);
+
+ /* tri-state our MDIO I/O pin so we can read */
+ bus->set_mdc(bus, 0);
+ bus->mdio_tristate(bus);
+ bus->delay(bus);
+ bus->set_mdc(bus, 1);
+ bus->delay(bus);
+
+ /* check the turnaround bit: the PHY should be driving it to zero */
+ bus->get_mdio(bus, &v);
+ if (v != 0) {
+ /* puts ("PHY didn't drive TA low\n"); */
+ for (j = 0; j < 32; j++) {
+ bus->set_mdc(bus, 0);
+ bus->delay(bus);
+ bus->set_mdc(bus, 1);
+ bus->delay(bus);
+ }
+ /* There is no PHY, return */
+ return -1;
+ }
+
+ bus->set_mdc(bus, 0);
+ bus->delay(bus);
+
+ /* read 16 bits of register data, MSB first */
+ rdreg = 0;
+ for (j = 0; j < 16; j++) {
+ bus->set_mdc(bus, 1);
+ bus->delay(bus);
+ rdreg <<= 1;
+ bus->get_mdio(bus, &v);
+ rdreg |= (v & 0x1);
+ bus->set_mdc(bus, 0);
+ bus->delay(bus);
+ }
+
+ bus->set_mdc(bus, 1);
+ bus->delay(bus);
+ bus->set_mdc(bus, 0);
+ bus->delay(bus);
+ bus->set_mdc(bus, 1);
+ bus->delay(bus);
+
+#ifdef DEBUG
+ printf("miiphy_read(0x%x) @ 0x%x = 0x%04x\n", reg, addr, rdreg);
+#endif
+
+ return rdreg;
+}
+
+
+/*****************************************************************************
+ *
+ * Write a MII PHY register.
+ *
+ * Returns:
+ * 0 on success
+ */
+int bb_miiphy_write(struct mii_dev *miidev, int addr, int devad, int reg,
+ u16 value)
+{
+ struct bb_miiphy_bus *bus;
+ int j; /* counter */
+
+ bus = bb_miiphy_getbus(miidev->name);
+ if (bus == NULL) {
+ /* Bus not found! */
+ return -1;
+ }
+
+ miiphy_pre (bus, 0, addr, reg);
+
+ /* send the turnaround (10) */
+ bus->set_mdc(bus, 0);
+ bus->set_mdio(bus, 1);
+ bus->delay(bus);
+ bus->set_mdc(bus, 1);
+ bus->delay(bus);
+ bus->set_mdc(bus, 0);
+ bus->set_mdio(bus, 0);
+ bus->delay(bus);
+ bus->set_mdc(bus, 1);
+ bus->delay(bus);
+
+ /* write 16 bits of register data, MSB first */
+ for (j = 0; j < 16; j++) {
+ bus->set_mdc(bus, 0);
+ if ((value & 0x00008000) == 0) {
+ bus->set_mdio(bus, 0);
+ } else {
+ bus->set_mdio(bus, 1);
+ }
+ bus->delay(bus);
+ bus->set_mdc(bus, 1);
+ bus->delay(bus);
+ value <<= 1;
+ }
+
+ /*
+ * Tri-state the MDIO line.
+ */
+ bus->mdio_tristate(bus);
+ bus->set_mdc(bus, 0);
+ bus->delay(bus);
+ bus->set_mdc(bus, 1);
+ bus->delay(bus);
+
+ return 0;
+}
diff --git a/roms/u-boot/drivers/net/phy/mscc.c b/roms/u-boot/drivers/net/phy/mscc.c
new file mode 100644
index 000000000..d1a643cf5
--- /dev/null
+++ b/roms/u-boot/drivers/net/phy/mscc.c
@@ -0,0 +1,1586 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Microsemi PHY drivers
+ *
+ *
+ * Copyright (c) 2016 Microsemi Corporation
+ *
+ * Author: John Haechten
+ *
+ */
+
+#include <log.h>
+#include <miiphy.h>
+#include <bitfield.h>
+#include <time.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+
+/* Microsemi PHY ID's */
+#define PHY_ID_VSC8530 0x00070560
+#define PHY_ID_VSC8531 0x00070570
+#define PHY_ID_VSC8540 0x00070760
+#define PHY_ID_VSC8541 0x00070770
+#define PHY_ID_VSC8574 0x000704a0
+#define PHY_ID_VSC8584 0x000707c0
+
+/* Microsemi VSC85xx PHY Register Pages */
+#define MSCC_EXT_PAGE_ACCESS 31 /* Page Access Register */
+#define MSCC_PHY_PAGE_STD 0x0000 /* Standard registers */
+#define MSCC_PHY_PAGE_EXT1 0x0001 /* Extended registers - page 1 */
+#define MSCC_PHY_PAGE_EXT2 0x0002 /* Extended registers - page 2 */
+#define MSCC_PHY_PAGE_EXT3 0x0003 /* Extended registers - page 3 */
+#define MSCC_PHY_PAGE_EXT4 0x0004 /* Extended registers - page 4 */
+#define MSCC_PHY_PAGE_GPIO 0x0010 /* GPIO registers */
+#define MSCC_PHY_PAGE_TEST 0x2A30 /* TEST Page registers */
+#define MSCC_PHY_PAGE_TR 0x52B5 /* Token Ring Page registers */
+
+/* Std Page Register 18 */
+#define MSCC_PHY_BYPASS_CONTROL 18
+#define PARALLEL_DET_IGNORE_ADVERTISED BIT(3)
+
+/* Std Page Register 22 */
+#define MSCC_PHY_EXT_CNTL_STATUS 22
+#define SMI_BROADCAST_WR_EN BIT(0)
+
+/* Std Page Register 24 */
+#define MSCC_PHY_EXT_PHY_CNTL_2 24
+
+/* Std Page Register 28 - PHY AUX Control/Status */
+#define MIIM_AUX_CNTRL_STAT_REG 28
+#define MIIM_AUX_CNTRL_STAT_ACTIPHY_TO (0x0004)
+#define MIIM_AUX_CNTRL_STAT_F_DUPLEX (0x0020)
+#define MIIM_AUX_CNTRL_STAT_SPEED_MASK (0x0018)
+#define MIIM_AUX_CNTRL_STAT_SPEED_POS (3)
+#define MIIM_AUX_CNTRL_STAT_SPEED_10M (0x0)
+#define MIIM_AUX_CNTRL_STAT_SPEED_100M (0x1)
+#define MIIM_AUX_CNTRL_STAT_SPEED_1000M (0x2)
+
+/* Std Page Register 23 - Extended PHY CTRL_1 */
+#define MSCC_PHY_EXT_PHY_CNTL_1_REG 23
+#define MAC_IF_SELECTION_MASK (0x1800)
+#define MAC_IF_SELECTION_GMII (0)
+#define MAC_IF_SELECTION_RMII (1)
+#define MAC_IF_SELECTION_RGMII (2)
+#define MAC_IF_SELECTION_POS (11)
+#define MAC_IF_SELECTION_WIDTH (2)
+#define VSC8584_MAC_IF_SELECTION_MASK BIT(12)
+#define VSC8584_MAC_IF_SELECTION_SGMII 0
+#define VSC8584_MAC_IF_SELECTION_1000BASEX 1
+#define VSC8584_MAC_IF_SELECTION_POS 12
+#define MEDIA_OP_MODE_MASK GENMASK(10, 8)
+#define MEDIA_OP_MODE_COPPER 0
+#define MEDIA_OP_MODE_SERDES 1
+#define MEDIA_OP_MODE_1000BASEX 2
+#define MEDIA_OP_MODE_100BASEFX 3
+#define MEDIA_OP_MODE_AMS_COPPER_SERDES 5
+#define MEDIA_OP_MODE_AMS_COPPER_1000BASEX 6
+#define MEDIA_OP_MODE_AMS_COPPER_100BASEFX 7
+#define MEDIA_OP_MODE_POS 8
+
+/* Extended Page 1 Register 20E1 */
+#define MSCC_PHY_ACTIPHY_CNTL 20
+#define PHY_ADDR_REVERSED BIT(9)
+
+/* Extended Page 1 Register 23E1 */
+
+#define MSCC_PHY_EXT_PHY_CNTL_4 23
+#define PHY_CNTL_4_ADDR_POS 11
+
+/* Extended Page 1 Register 25E1 */
+#define MSCC_PHY_VERIPHY_CNTL_2 25
+
+/* Extended Page 1 Register 26E1 */
+#define MSCC_PHY_VERIPHY_CNTL_3 26
+
+/* Extended Page 2 Register 16E2 */
+#define MSCC_PHY_CU_PMD_TX_CNTL 16
+
+/* Extended Page 2 Register 20E2 */
+#define MSCC_PHY_RGMII_CNTL_REG 20
+#define VSC_FAST_LINK_FAIL2_ENA_MASK (0x8000)
+#define RX_CLK_OUT_MASK (0x0800)
+#define RX_CLK_OUT_POS (11)
+#define RX_CLK_OUT_WIDTH (1)
+#define RX_CLK_OUT_NORMAL (0)
+#define RX_CLK_OUT_DISABLE (1)
+#define RGMII_RX_CLK_DELAY_POS (4)
+#define RGMII_RX_CLK_DELAY_WIDTH (3)
+#define RGMII_RX_CLK_DELAY_MASK (0x0070)
+#define RGMII_TX_CLK_DELAY_POS (0)
+#define RGMII_TX_CLK_DELAY_WIDTH (3)
+#define RGMII_TX_CLK_DELAY_MASK (0x0007)
+
+/* Extended Page 2 Register 27E2 */
+#define MSCC_PHY_WOL_MAC_CONTROL 27
+#define EDGE_RATE_CNTL_POS (5)
+#define EDGE_RATE_CNTL_WIDTH (3)
+#define EDGE_RATE_CNTL_MASK (0x00E0)
+#define RMII_CLK_OUT_ENABLE_POS (4)
+#define RMII_CLK_OUT_ENABLE_WIDTH (1)
+#define RMII_CLK_OUT_ENABLE_MASK (0x10)
+
+/* Extended Page 3 Register 22E3 */
+#define MSCC_PHY_SERDES_TX_CRC_ERR_CNT 22
+
+/* Extended page GPIO register 00G */
+#define MSCC_DW8051_CNTL_STATUS 0
+#define MICRO_NSOFT_RESET BIT(15)
+#define RUN_FROM_INT_ROM BIT(14)
+#define AUTOINC_ADDR BIT(13)
+#define PATCH_RAM_CLK BIT(12)
+#define MICRO_PATCH_EN BIT(7)
+#define DW8051_CLK_EN BIT(4)
+#define MICRO_CLK_EN BIT(3)
+#define MICRO_CLK_DIVIDE(x) ((x) >> 1)
+#define MSCC_DW8051_VLD_MASK 0xf1ff
+
+/* Extended page GPIO register 09G */
+#define MSCC_TRAP_ROM_ADDR(x) ((x) * 2 + 1)
+#define MSCC_TRAP_ROM_ADDR_SERDES_INIT 0x3eb7
+
+/* Extended page GPIO register 10G */
+#define MSCC_PATCH_RAM_ADDR(x) (((x) + 1) * 2)
+#define MSCC_PATCH_RAM_ADDR_SERDES_INIT 0x4012
+
+/* Extended page GPIO register 11G */
+#define MSCC_INT_MEM_ADDR 11
+
+/* Extended page GPIO register 12G */
+#define MSCC_INT_MEM_CNTL 12
+#define READ_SFR (BIT(14) | BIT(13))
+#define READ_PRAM BIT(14)
+#define READ_ROM BIT(13)
+#define READ_RAM (0x00 << 13)
+#define INT_MEM_WRITE_EN BIT(12)
+#define EN_PATCH_RAM_TRAP_ADDR(x) BIT((x) + 7)
+#define INT_MEM_DATA_M GENMASK(7, 0)
+#define INT_MEM_DATA(x) (INT_MEM_DATA_M & (x))
+
+/* Extended page GPIO register 13G */
+#define MSCC_CLKOUT_CNTL 13
+#define CLKOUT_ENABLE BIT(15)
+#define CLKOUT_FREQ_MASK GENMASK(14, 13)
+#define CLKOUT_FREQ_25M (0x0 << 13)
+#define CLKOUT_FREQ_50M (0x1 << 13)
+#define CLKOUT_FREQ_125M (0x2 << 13)
+
+/* Extended page GPIO register 18G */
+#define MSCC_PHY_PROC_CMD 18
+#define PROC_CMD_NCOMPLETED BIT(15)
+#define PROC_CMD_FAILED BIT(14)
+#define PROC_CMD_SGMII_PORT(x) ((x) << 8)
+#define PROC_CMD_FIBER_PORT(x) BIT(8 + (x) % 4)
+#define PROC_CMD_QSGMII_PORT (BIT(11) | BIT(10))
+#define PROC_CMD_RST_CONF_PORT BIT(7)
+#define PROC_CMD_RECONF_PORT (0 << 7)
+#define PROC_CMD_READ_MOD_WRITE_PORT BIT(6)
+#define PROC_CMD_WRITE BIT(6)
+#define PROC_CMD_READ (0 << 6)
+#define PROC_CMD_FIBER_DISABLE BIT(5)
+#define PROC_CMD_FIBER_100BASE_FX BIT(4)
+#define PROC_CMD_FIBER_1000BASE_X (0 << 4)
+#define PROC_CMD_SGMII_MAC (BIT(5) | BIT(4))
+#define PROC_CMD_QSGMII_MAC BIT(5)
+#define PROC_CMD_NO_MAC_CONF (0x00 << 4)
+#define PROC_CMD_1588_DEFAULT_INIT BIT(4)
+#define PROC_CMD_NOP GENMASK(3, 0)
+#define PROC_CMD_PHY_INIT (BIT(3) | BIT(1))
+#define PROC_CMD_CRC16 BIT(3)
+#define PROC_CMD_FIBER_MEDIA_CONF BIT(0)
+#define PROC_CMD_MCB_ACCESS_MAC_CONF (0x0000 << 0)
+#define PROC_CMD_NCOMPLETED_TIMEOUT_MS 500
+
+/* Extended page GPIO register 19G */
+#define MSCC_PHY_MAC_CFG_FASTLINK 19
+#define MAC_CFG_MASK GENMASK(15, 14)
+#define MAC_CFG_SGMII (0x00 << 14)
+#define MAC_CFG_QSGMII BIT(14)
+
+/* Test Registers */
+#define MSCC_PHY_TEST_PAGE_5 5
+
+#define MSCC_PHY_TEST_PAGE_8 8
+#define TR_CLK_DISABLE BIT(15)
+
+#define MSCC_PHY_TEST_PAGE_9 9
+#define MSCC_PHY_TEST_PAGE_20 20
+#define MSCC_PHY_TEST_PAGE_24 24
+
+/* Token Ring Page 0x52B5 Registers */
+#define MSCC_PHY_REG_TR_ADDR_16 16
+#define MSCC_PHY_REG_TR_DATA_17 17
+#define MSCC_PHY_REG_TR_DATA_18 18
+
+/* Token Ring - Read Value in */
+#define MSCC_PHY_TR_16_READ (0xA000)
+/* Token Ring - Write Value out */
+#define MSCC_PHY_TR_16_WRITE (0x8000)
+
+/* Token Ring Registers */
+#define MSCC_PHY_TR_LINKDETCTRL_POS (3)
+#define MSCC_PHY_TR_LINKDETCTRL_WIDTH (2)
+#define MSCC_PHY_TR_LINKDETCTRL_VAL (3)
+#define MSCC_PHY_TR_LINKDETCTRL_MASK (0x0018)
+#define MSCC_PHY_TR_LINKDETCTRL_ADDR (0x07F8)
+
+#define MSCC_PHY_TR_VGATHRESH100_POS (0)
+#define MSCC_PHY_TR_VGATHRESH100_WIDTH (7)
+#define MSCC_PHY_TR_VGATHRESH100_VAL (0x0018)
+#define MSCC_PHY_TR_VGATHRESH100_MASK (0x007f)
+#define MSCC_PHY_TR_VGATHRESH100_ADDR (0x0FA4)
+
+#define MSCC_PHY_TR_VGAGAIN10_U_POS (0)
+#define MSCC_PHY_TR_VGAGAIN10_U_WIDTH (1)
+#define MSCC_PHY_TR_VGAGAIN10_U_MASK (0x0001)
+#define MSCC_PHY_TR_VGAGAIN10_U_VAL (0)
+
+#define MSCC_PHY_TR_VGAGAIN10_L_POS (12)
+#define MSCC_PHY_TR_VGAGAIN10_L_WIDTH (4)
+#define MSCC_PHY_TR_VGAGAIN10_L_MASK (0xf000)
+#define MSCC_PHY_TR_VGAGAIN10_L_VAL (0x0001)
+#define MSCC_PHY_TR_VGAGAIN10_ADDR (0x0F92)
+
+/* General Timeout Values */
+#define MSCC_PHY_RESET_TIMEOUT (100)
+#define MSCC_PHY_MICRO_TIMEOUT (500)
+
+#define VSC8584_REVB 0x0001
+#define MSCC_DEV_REV_MASK GENMASK(3, 0)
+
+#define MSCC_VSC8574_REVB_INT8051_FW_START_ADDR 0x4000
+#define MSCC_VSC8574_REVB_INT8051_FW_CRC 0x29e8
+
+#define MSCC_VSC8584_REVB_INT8051_FW_START_ADDR 0xe800
+#define MSCC_VSC8584_REVB_INT8051_FW_CRC 0xfb48
+
+/* RGMII/GMII Clock Delay (Skew) Options */ enum vsc_phy_rgmii_skew {
+ VSC_PHY_RGMII_DELAY_200_PS,
+ VSC_PHY_RGMII_DELAY_800_PS,
+ VSC_PHY_RGMII_DELAY_1100_PS,
+ VSC_PHY_RGMII_DELAY_1700_PS,
+ VSC_PHY_RGMII_DELAY_2000_PS,
+ VSC_PHY_RGMII_DELAY_2300_PS,
+ VSC_PHY_RGMII_DELAY_2600_PS,
+ VSC_PHY_RGMII_DELAY_3400_PS,
+};
+
+/* MAC i/f Clock Edge Rage Control (Slew), See Reg27E2 */ enum
+vsc_phy_clk_slew {
+ VSC_PHY_CLK_SLEW_RATE_0,
+ VSC_PHY_CLK_SLEW_RATE_1,
+ VSC_PHY_CLK_SLEW_RATE_2,
+ VSC_PHY_CLK_SLEW_RATE_3,
+ VSC_PHY_CLK_SLEW_RATE_4,
+ VSC_PHY_CLK_SLEW_RATE_5,
+ VSC_PHY_CLK_SLEW_RATE_6,
+ VSC_PHY_CLK_SLEW_RATE_7,
+};
+
+struct vsc85xx_priv {
+ int (*config_pre)(struct phy_device *phydev);
+};
+
+static void vsc8584_csr_write(struct mii_dev *bus, int phy0, u16 addr, u32 val)
+{
+ bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_REG_TR_DATA_18,
+ val >> 16);
+ bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_REG_TR_DATA_17,
+ val & GENMASK(15, 0));
+ bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_REG_TR_ADDR_16,
+ MSCC_PHY_TR_16_WRITE | addr);
+}
+
+static int vsc8584_cmd(struct mii_dev *bus, int phy, u16 val)
+{
+ unsigned long deadline;
+ u16 reg_val;
+
+ bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+ MSCC_PHY_PAGE_GPIO);
+
+ bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_PHY_PROC_CMD,
+ PROC_CMD_NCOMPLETED | val);
+
+ deadline = timer_get_us() + PROC_CMD_NCOMPLETED_TIMEOUT_MS * 1000;
+ do {
+ reg_val = bus->read(bus, phy, MDIO_DEVAD_NONE,
+ MSCC_PHY_PROC_CMD);
+ } while (timer_get_us() <= deadline &&
+ (reg_val & PROC_CMD_NCOMPLETED) &&
+ !(reg_val & PROC_CMD_FAILED));
+
+ bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+ MSCC_PHY_PAGE_STD);
+
+ if (reg_val & PROC_CMD_FAILED)
+ return -EIO;
+ if (reg_val & PROC_CMD_NCOMPLETED)
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+static int vsc8584_micro_deassert_reset(struct mii_dev *bus, int phy,
+ bool patch_en)
+{
+ u32 enable, release;
+
+ bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+ MSCC_PHY_PAGE_GPIO);
+
+ enable = RUN_FROM_INT_ROM | MICRO_CLK_EN | DW8051_CLK_EN;
+ release = MICRO_NSOFT_RESET | RUN_FROM_INT_ROM | DW8051_CLK_EN |
+ MICRO_CLK_EN;
+
+ if (patch_en) {
+ enable |= MICRO_PATCH_EN;
+ release |= MICRO_PATCH_EN;
+
+ /* Clear all patches */
+ bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_INT_MEM_CNTL,
+ READ_RAM);
+ }
+
+ /*
+ * Enable 8051 Micro clock; CLEAR/SET patch present; disable PRAM clock
+ * override and addr. auto-incr; operate at 125 MHz
+ */
+ bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_DW8051_CNTL_STATUS, enable);
+ /* Release 8051 Micro SW reset */
+ bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_DW8051_CNTL_STATUS, release);
+
+ bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+ MSCC_PHY_PAGE_STD);
+
+ return 0;
+}
+
+static int vsc8584_micro_assert_reset(struct mii_dev *bus, int phy)
+{
+ int ret;
+ u16 reg;
+
+ ret = vsc8584_cmd(bus, phy, PROC_CMD_NOP);
+ if (ret)
+ return ret;
+
+ bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+ MSCC_PHY_PAGE_GPIO);
+
+ reg = bus->read(bus, phy, MDIO_DEVAD_NONE, MSCC_INT_MEM_CNTL);
+ reg &= ~EN_PATCH_RAM_TRAP_ADDR(4);
+ bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_INT_MEM_CNTL, reg);
+
+ bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_TRAP_ROM_ADDR(4), 0x005b);
+ bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_PATCH_RAM_ADDR(4), 0x005b);
+
+ reg = bus->read(bus, phy, MDIO_DEVAD_NONE, MSCC_INT_MEM_CNTL);
+ reg |= EN_PATCH_RAM_TRAP_ADDR(4);
+ bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_INT_MEM_CNTL, reg);
+
+ bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_PHY_PROC_CMD, PROC_CMD_NOP);
+
+ reg = bus->read(bus, phy, MDIO_DEVAD_NONE, MSCC_DW8051_CNTL_STATUS);
+ reg &= ~MICRO_NSOFT_RESET;
+ bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_DW8051_CNTL_STATUS, reg);
+
+ bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_PHY_PROC_CMD,
+ PROC_CMD_MCB_ACCESS_MAC_CONF | PROC_CMD_SGMII_PORT(0) |
+ PROC_CMD_NO_MAC_CONF | PROC_CMD_READ);
+
+ reg = bus->read(bus, phy, MDIO_DEVAD_NONE, MSCC_INT_MEM_CNTL);
+ reg &= ~EN_PATCH_RAM_TRAP_ADDR(4);
+ bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_INT_MEM_CNTL, reg);
+
+ bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+ MSCC_PHY_PAGE_STD);
+
+ return 0;
+}
+
+static const u8 fw_patch_vsc8574[] = {
+ 0x46, 0x4a, 0x02, 0x43, 0x37, 0x02, 0x46, 0x26, 0x02, 0x46, 0x77, 0x02,
+ 0x45, 0x60, 0x02, 0x45, 0xaf, 0xed, 0xff, 0xe5, 0xfc, 0x54, 0x38, 0x64,
+ 0x20, 0x70, 0x08, 0x65, 0xff, 0x70, 0x04, 0xed, 0x44, 0x80, 0xff, 0x22,
+ 0x8f, 0x19, 0x7b, 0xbb, 0x7d, 0x0e, 0x7f, 0x04, 0x12, 0x3d, 0xd7, 0xef,
+ 0x4e, 0x60, 0x03, 0x02, 0x41, 0xf9, 0xe4, 0xf5, 0x1a, 0x74, 0x01, 0x7e,
+ 0x00, 0xa8, 0x1a, 0x08, 0x80, 0x05, 0xc3, 0x33, 0xce, 0x33, 0xce, 0xd8,
+ 0xf9, 0xff, 0xef, 0x55, 0x19, 0x70, 0x03, 0x02, 0x41, 0xed, 0x85, 0x1a,
+ 0xfb, 0x7b, 0xbb, 0xe4, 0xfd, 0xff, 0x12, 0x3d, 0xd7, 0xef, 0x4e, 0x60,
+ 0x03, 0x02, 0x41, 0xed, 0xe5, 0x1a, 0x54, 0x02, 0x75, 0x1d, 0x00, 0x25,
+ 0xe0, 0x25, 0xe0, 0xf5, 0x1c, 0xe4, 0x78, 0xc5, 0xf6, 0xd2, 0x0a, 0x12,
+ 0x41, 0xfa, 0x7b, 0xff, 0x7d, 0x12, 0x7f, 0x07, 0x12, 0x3d, 0xd7, 0xef,
+ 0x4e, 0x60, 0x03, 0x02, 0x41, 0xe7, 0xc2, 0x0a, 0x74, 0xc7, 0x25, 0x1a,
+ 0xf9, 0x74, 0xe7, 0x25, 0x1a, 0xf8, 0xe6, 0x27, 0xf5, 0x1b, 0xe5, 0x1d,
+ 0x24, 0x5b, 0x12, 0x45, 0xea, 0x12, 0x3e, 0xda, 0x7b, 0xfc, 0x7d, 0x11,
+ 0x7f, 0x07, 0x12, 0x3d, 0xd7, 0x78, 0xcc, 0xef, 0xf6, 0x78, 0xc1, 0xe6,
+ 0xfe, 0xef, 0xd3, 0x9e, 0x40, 0x06, 0x78, 0xcc, 0xe6, 0x78, 0xc1, 0xf6,
+ 0x12, 0x41, 0xfa, 0x7b, 0xec, 0x7d, 0x12, 0x7f, 0x07, 0x12, 0x3d, 0xd7,
+ 0x78, 0xcb, 0xef, 0xf6, 0xbf, 0x07, 0x06, 0x78, 0xc3, 0x76, 0x1a, 0x80,
+ 0x1f, 0x78, 0xc5, 0xe6, 0xff, 0x60, 0x0f, 0xc3, 0xe5, 0x1b, 0x9f, 0xff,
+ 0x78, 0xcb, 0xe6, 0x85, 0x1b, 0xf0, 0xa4, 0x2f, 0x80, 0x07, 0x78, 0xcb,
+ 0xe6, 0x85, 0x1b, 0xf0, 0xa4, 0x78, 0xc3, 0xf6, 0xe4, 0x78, 0xc2, 0xf6,
+ 0x78, 0xc2, 0xe6, 0xff, 0xc3, 0x08, 0x96, 0x40, 0x03, 0x02, 0x41, 0xd1,
+ 0xef, 0x54, 0x03, 0x60, 0x33, 0x14, 0x60, 0x46, 0x24, 0xfe, 0x60, 0x42,
+ 0x04, 0x70, 0x4b, 0xef, 0x24, 0x02, 0xff, 0xe4, 0x33, 0xfe, 0xef, 0x78,
+ 0x02, 0xce, 0xa2, 0xe7, 0x13, 0xce, 0x13, 0xd8, 0xf8, 0xff, 0xe5, 0x1d,
+ 0x24, 0x5c, 0xcd, 0xe5, 0x1c, 0x34, 0xf0, 0xcd, 0x2f, 0xff, 0xed, 0x3e,
+ 0xfe, 0x12, 0x46, 0x0d, 0x7d, 0x11, 0x80, 0x0b, 0x78, 0xc2, 0xe6, 0x70,
+ 0x04, 0x7d, 0x11, 0x80, 0x02, 0x7d, 0x12, 0x7f, 0x07, 0x12, 0x3e, 0x9a,
+ 0x8e, 0x1e, 0x8f, 0x1f, 0x80, 0x03, 0xe5, 0x1e, 0xff, 0x78, 0xc5, 0xe6,
+ 0x06, 0x24, 0xcd, 0xf8, 0xa6, 0x07, 0x78, 0xc2, 0x06, 0xe6, 0xb4, 0x1a,
+ 0x0a, 0xe5, 0x1d, 0x24, 0x5c, 0x12, 0x45, 0xea, 0x12, 0x3e, 0xda, 0x78,
+ 0xc5, 0xe6, 0x65, 0x1b, 0x70, 0x82, 0x75, 0xdb, 0x20, 0x75, 0xdb, 0x28,
+ 0x12, 0x46, 0x02, 0x12, 0x46, 0x02, 0xe5, 0x1a, 0x12, 0x45, 0xf5, 0xe5,
+ 0x1a, 0xc3, 0x13, 0x12, 0x45, 0xf5, 0x78, 0xc5, 0x16, 0xe6, 0x24, 0xcd,
+ 0xf8, 0xe6, 0xff, 0x7e, 0x08, 0x1e, 0xef, 0xa8, 0x06, 0x08, 0x80, 0x02,
+ 0xc3, 0x13, 0xd8, 0xfc, 0xfd, 0xc4, 0x33, 0x54, 0xe0, 0xf5, 0xdb, 0xef,
+ 0xa8, 0x06, 0x08, 0x80, 0x02, 0xc3, 0x13, 0xd8, 0xfc, 0xfd, 0xc4, 0x33,
+ 0x54, 0xe0, 0x44, 0x08, 0xf5, 0xdb, 0xee, 0x70, 0xd8, 0x78, 0xc5, 0xe6,
+ 0x70, 0xc8, 0x75, 0xdb, 0x10, 0x02, 0x40, 0xfd, 0x78, 0xc2, 0xe6, 0xc3,
+ 0x94, 0x17, 0x50, 0x0e, 0xe5, 0x1d, 0x24, 0x62, 0x12, 0x42, 0x08, 0xe5,
+ 0x1d, 0x24, 0x5c, 0x12, 0x42, 0x08, 0x20, 0x0a, 0x03, 0x02, 0x40, 0x76,
+ 0x05, 0x1a, 0xe5, 0x1a, 0xc3, 0x94, 0x04, 0x50, 0x03, 0x02, 0x40, 0x3a,
+ 0x22, 0xe5, 0x1d, 0x24, 0x5c, 0xff, 0xe5, 0x1c, 0x34, 0xf0, 0xfe, 0x12,
+ 0x46, 0x0d, 0x22, 0xff, 0xe5, 0x1c, 0x34, 0xf0, 0xfe, 0x12, 0x46, 0x0d,
+ 0x22, 0xe4, 0xf5, 0x19, 0x12, 0x46, 0x43, 0x20, 0xe7, 0x1e, 0x7b, 0xfe,
+ 0x12, 0x42, 0xf9, 0xef, 0xc4, 0x33, 0x33, 0x54, 0xc0, 0xff, 0xc0, 0x07,
+ 0x7b, 0x54, 0x12, 0x42, 0xf9, 0xd0, 0xe0, 0x4f, 0xff, 0x74, 0x2a, 0x25,
+ 0x19, 0xf8, 0xa6, 0x07, 0x12, 0x46, 0x43, 0x20, 0xe7, 0x03, 0x02, 0x42,
+ 0xdf, 0x54, 0x03, 0x64, 0x03, 0x70, 0x03, 0x02, 0x42, 0xcf, 0x7b, 0xcb,
+ 0x12, 0x43, 0x2c, 0x8f, 0xfb, 0x7b, 0x30, 0x7d, 0x03, 0xe4, 0xff, 0x12,
+ 0x3d, 0xd7, 0xc3, 0xef, 0x94, 0x02, 0xee, 0x94, 0x00, 0x50, 0x2a, 0x12,
+ 0x42, 0xec, 0xef, 0x4e, 0x70, 0x23, 0x12, 0x43, 0x04, 0x60, 0x0a, 0x12,
+ 0x43, 0x12, 0x70, 0x0c, 0x12, 0x43, 0x1f, 0x70, 0x07, 0x12, 0x46, 0x39,
+ 0x7b, 0x03, 0x80, 0x07, 0x12, 0x46, 0x39, 0x12, 0x46, 0x43, 0xfb, 0x7a,
+ 0x00, 0x7d, 0x54, 0x80, 0x3e, 0x12, 0x42, 0xec, 0xef, 0x4e, 0x70, 0x24,
+ 0x12, 0x43, 0x04, 0x60, 0x0a, 0x12, 0x43, 0x12, 0x70, 0x0f, 0x12, 0x43,
+ 0x1f, 0x70, 0x0a, 0x12, 0x46, 0x39, 0xe4, 0xfb, 0xfa, 0x7d, 0xee, 0x80,
+ 0x1e, 0x12, 0x46, 0x39, 0x7b, 0x01, 0x7a, 0x00, 0x7d, 0xee, 0x80, 0x13,
+ 0x12, 0x46, 0x39, 0x12, 0x46, 0x43, 0x54, 0x40, 0xfe, 0xc4, 0x13, 0x13,
+ 0x54, 0x03, 0xfb, 0x7a, 0x00, 0x7d, 0xee, 0x12, 0x38, 0xbd, 0x7b, 0xff,
+ 0x12, 0x43, 0x2c, 0xef, 0x4e, 0x70, 0x07, 0x74, 0x2a, 0x25, 0x19, 0xf8,
+ 0xe4, 0xf6, 0x05, 0x19, 0xe5, 0x19, 0xc3, 0x94, 0x02, 0x50, 0x03, 0x02,
+ 0x42, 0x15, 0x22, 0xe5, 0x19, 0x24, 0x17, 0xfd, 0x7b, 0x20, 0x7f, 0x04,
+ 0x12, 0x3d, 0xd7, 0x22, 0xe5, 0x19, 0x24, 0x17, 0xfd, 0x7f, 0x04, 0x12,
+ 0x3d, 0xd7, 0x22, 0x7b, 0x22, 0x7d, 0x18, 0x7f, 0x06, 0x12, 0x3d, 0xd7,
+ 0xef, 0x64, 0x01, 0x4e, 0x22, 0x7d, 0x1c, 0xe4, 0xff, 0x12, 0x3e, 0x9a,
+ 0xef, 0x54, 0x1b, 0x64, 0x0a, 0x22, 0x7b, 0xcc, 0x7d, 0x10, 0xff, 0x12,
+ 0x3d, 0xd7, 0xef, 0x64, 0x01, 0x4e, 0x22, 0xe5, 0x19, 0x24, 0x17, 0xfd,
+ 0x7f, 0x04, 0x12, 0x3d, 0xd7, 0x22, 0xd2, 0x08, 0x75, 0xfb, 0x03, 0xab,
+ 0x7e, 0xaa, 0x7d, 0x7d, 0x19, 0x7f, 0x03, 0x12, 0x3e, 0xda, 0xe5, 0x7e,
+ 0x54, 0x0f, 0x24, 0xf3, 0x60, 0x03, 0x02, 0x43, 0xe9, 0x12, 0x46, 0x5a,
+ 0x12, 0x46, 0x61, 0xd8, 0xfb, 0xff, 0x20, 0xe2, 0x35, 0x13, 0x92, 0x0c,
+ 0xef, 0xa2, 0xe1, 0x92, 0x0b, 0x30, 0x0c, 0x2a, 0xe4, 0xf5, 0x10, 0x7b,
+ 0xfe, 0x12, 0x43, 0xff, 0xef, 0xc4, 0x33, 0x33, 0x54, 0xc0, 0xff, 0xc0,
+ 0x07, 0x7b, 0x54, 0x12, 0x43, 0xff, 0xd0, 0xe0, 0x4f, 0xff, 0x74, 0x2a,
+ 0x25, 0x10, 0xf8, 0xa6, 0x07, 0x05, 0x10, 0xe5, 0x10, 0xc3, 0x94, 0x02,
+ 0x40, 0xd9, 0x12, 0x46, 0x5a, 0x12, 0x46, 0x61, 0xd8, 0xfb, 0x54, 0x05,
+ 0x64, 0x04, 0x70, 0x27, 0x78, 0xc4, 0xe6, 0x78, 0xc6, 0xf6, 0xe5, 0x7d,
+ 0xff, 0x33, 0x95, 0xe0, 0xef, 0x54, 0x0f, 0x78, 0xc4, 0xf6, 0x12, 0x44,
+ 0x0a, 0x20, 0x0c, 0x0c, 0x12, 0x46, 0x5a, 0x12, 0x46, 0x61, 0xd8, 0xfb,
+ 0x13, 0x92, 0x0d, 0x22, 0xc2, 0x0d, 0x22, 0x12, 0x46, 0x5a, 0x12, 0x46,
+ 0x61, 0xd8, 0xfb, 0x54, 0x05, 0x64, 0x05, 0x70, 0x1e, 0x78, 0xc4, 0x7d,
+ 0xb8, 0x12, 0x43, 0xf5, 0x78, 0xc1, 0x7d, 0x74, 0x12, 0x43, 0xf5, 0xe4,
+ 0x78, 0xc1, 0xf6, 0x22, 0x7b, 0x01, 0x7a, 0x00, 0x7d, 0xee, 0x7f, 0x92,
+ 0x12, 0x38, 0xbd, 0x22, 0xe6, 0xfb, 0x7a, 0x00, 0x7f, 0x92, 0x12, 0x38,
+ 0xbd, 0x22, 0xe5, 0x10, 0x24, 0x17, 0xfd, 0x7f, 0x04, 0x12, 0x3d, 0xd7,
+ 0x22, 0x78, 0xc1, 0xe6, 0xfb, 0x7a, 0x00, 0x7d, 0x74, 0x7f, 0x92, 0x12,
+ 0x38, 0xbd, 0xe4, 0x78, 0xc1, 0xf6, 0xf5, 0x11, 0x74, 0x01, 0x7e, 0x00,
+ 0xa8, 0x11, 0x08, 0x80, 0x05, 0xc3, 0x33, 0xce, 0x33, 0xce, 0xd8, 0xf9,
+ 0xff, 0x78, 0xc4, 0xe6, 0xfd, 0xef, 0x5d, 0x60, 0x44, 0x85, 0x11, 0xfb,
+ 0xe5, 0x11, 0x54, 0x02, 0x25, 0xe0, 0x25, 0xe0, 0xfe, 0xe4, 0x24, 0x5b,
+ 0xfb, 0xee, 0x12, 0x45, 0xed, 0x12, 0x3e, 0xda, 0x7b, 0x40, 0x7d, 0x11,
+ 0x7f, 0x07, 0x12, 0x3d, 0xd7, 0x74, 0xc7, 0x25, 0x11, 0xf8, 0xa6, 0x07,
+ 0x7b, 0x11, 0x7d, 0x12, 0x7f, 0x07, 0x12, 0x3d, 0xd7, 0xef, 0x4e, 0x60,
+ 0x09, 0x74, 0xe7, 0x25, 0x11, 0xf8, 0x76, 0x04, 0x80, 0x07, 0x74, 0xe7,
+ 0x25, 0x11, 0xf8, 0x76, 0x0a, 0x05, 0x11, 0xe5, 0x11, 0xc3, 0x94, 0x04,
+ 0x40, 0x9a, 0x78, 0xc6, 0xe6, 0x70, 0x15, 0x78, 0xc4, 0xe6, 0x60, 0x10,
+ 0x75, 0xd9, 0x38, 0x75, 0xdb, 0x10, 0x7d, 0xfe, 0x12, 0x44, 0xb8, 0x7d,
+ 0x76, 0x12, 0x44, 0xb8, 0x79, 0xc6, 0xe7, 0x78, 0xc4, 0x66, 0xff, 0x60,
+ 0x03, 0x12, 0x40, 0x25, 0x78, 0xc4, 0xe6, 0x70, 0x09, 0xfb, 0xfa, 0x7d,
+ 0xfe, 0x7f, 0x8e, 0x12, 0x38, 0xbd, 0x22, 0x7b, 0x01, 0x7a, 0x00, 0x7f,
+ 0x8e, 0x12, 0x38, 0xbd, 0x22, 0xe4, 0xf5, 0xfb, 0x7d, 0x1c, 0xe4, 0xff,
+ 0x12, 0x3e, 0x9a, 0xad, 0x07, 0xac, 0x06, 0xec, 0x54, 0xc0, 0xff, 0xed,
+ 0x54, 0x3f, 0x4f, 0xf5, 0x20, 0x30, 0x06, 0x2c, 0x30, 0x01, 0x08, 0xa2,
+ 0x04, 0x72, 0x03, 0x92, 0x07, 0x80, 0x21, 0x30, 0x04, 0x06, 0x7b, 0xcc,
+ 0x7d, 0x11, 0x80, 0x0d, 0x30, 0x03, 0x06, 0x7b, 0xcc, 0x7d, 0x10, 0x80,
+ 0x04, 0x7b, 0x66, 0x7d, 0x16, 0xe4, 0xff, 0x12, 0x3d, 0xd7, 0xee, 0x4f,
+ 0x24, 0xff, 0x92, 0x07, 0xaf, 0xfb, 0x74, 0x26, 0x2f, 0xf8, 0xe6, 0xff,
+ 0xa6, 0x20, 0x20, 0x07, 0x39, 0x8f, 0x20, 0x30, 0x07, 0x34, 0x30, 0x00,
+ 0x31, 0x20, 0x04, 0x2e, 0x20, 0x03, 0x2b, 0xe4, 0xf5, 0xff, 0x75, 0xfc,
+ 0xc2, 0xe5, 0xfc, 0x30, 0xe0, 0xfb, 0xaf, 0xfe, 0xef, 0x20, 0xe3, 0x1a,
+ 0xae, 0xfd, 0x44, 0x08, 0xf5, 0xfe, 0x75, 0xfc, 0x80, 0xe5, 0xfc, 0x30,
+ 0xe0, 0xfb, 0x8f, 0xfe, 0x8e, 0xfd, 0x75, 0xfc, 0x80, 0xe5, 0xfc, 0x30,
+ 0xe0, 0xfb, 0x05, 0xfb, 0xaf, 0xfb, 0xef, 0xc3, 0x94, 0x04, 0x50, 0x03,
+ 0x02, 0x44, 0xc5, 0xe4, 0xf5, 0xfb, 0x22, 0xe5, 0x7e, 0x54, 0x0f, 0x64,
+ 0x01, 0x70, 0x23, 0xe5, 0x7e, 0x30, 0xe4, 0x1e, 0x90, 0x47, 0xd0, 0xe0,
+ 0x44, 0x02, 0xf0, 0x54, 0xfb, 0xf0, 0x90, 0x47, 0xd4, 0xe0, 0x44, 0x04,
+ 0xf0, 0x7b, 0x03, 0x7d, 0x5b, 0x7f, 0x5d, 0x12, 0x36, 0x29, 0x7b, 0x0e,
+ 0x80, 0x1c, 0x90, 0x47, 0xd0, 0xe0, 0x54, 0xfd, 0xf0, 0x44, 0x04, 0xf0,
+ 0x90, 0x47, 0xd4, 0xe0, 0x54, 0xfb, 0xf0, 0x7b, 0x02, 0x7d, 0x5b, 0x7f,
+ 0x5d, 0x12, 0x36, 0x29, 0x7b, 0x06, 0x7d, 0x60, 0x7f, 0x63, 0x12, 0x36,
+ 0x29, 0x22, 0xe5, 0x7e, 0x30, 0xe5, 0x35, 0x30, 0xe4, 0x0b, 0x7b, 0x02,
+ 0x7d, 0x33, 0x7f, 0x35, 0x12, 0x36, 0x29, 0x80, 0x10, 0x7b, 0x01, 0x7d,
+ 0x33, 0x7f, 0x35, 0x12, 0x36, 0x29, 0x90, 0x47, 0xd2, 0xe0, 0x44, 0x04,
+ 0xf0, 0x90, 0x47, 0xd2, 0xe0, 0x54, 0xf7, 0xf0, 0x90, 0x47, 0xd1, 0xe0,
+ 0x44, 0x10, 0xf0, 0x7b, 0x05, 0x7d, 0x84, 0x7f, 0x86, 0x12, 0x36, 0x29,
+ 0x22, 0xfb, 0xe5, 0x1c, 0x34, 0xf0, 0xfa, 0x7d, 0x10, 0x7f, 0x07, 0x22,
+ 0x54, 0x01, 0xc4, 0x33, 0x54, 0xe0, 0xf5, 0xdb, 0x44, 0x08, 0xf5, 0xdb,
+ 0x22, 0xf5, 0xdb, 0x75, 0xdb, 0x08, 0xf5, 0xdb, 0x75, 0xdb, 0x08, 0x22,
+ 0xab, 0x07, 0xaa, 0x06, 0x7d, 0x10, 0x7f, 0x07, 0x12, 0x3e, 0xda, 0x7b,
+ 0xff, 0x7d, 0x10, 0x7f, 0x07, 0x12, 0x3d, 0xd7, 0xef, 0x4e, 0x60, 0xf3,
+ 0x22, 0x12, 0x44, 0xc2, 0x30, 0x0c, 0x03, 0x12, 0x42, 0x12, 0x78, 0xc4,
+ 0xe6, 0xff, 0x60, 0x03, 0x12, 0x40, 0x25, 0x22, 0xe5, 0x19, 0x24, 0x17,
+ 0x54, 0x1f, 0x44, 0x80, 0xff, 0x22, 0x74, 0x2a, 0x25, 0x19, 0xf8, 0xe6,
+ 0x22, 0x12, 0x46, 0x72, 0x12, 0x46, 0x68, 0x90, 0x47, 0xfa, 0xe0, 0x54,
+ 0xf8, 0x44, 0x02, 0xf0, 0x22, 0xe5, 0x7e, 0xae, 0x7d, 0x78, 0x04, 0x22,
+ 0xce, 0xa2, 0xe7, 0x13, 0xce, 0x13, 0x22, 0xe4, 0x78, 0xc4, 0xf6, 0xc2,
+ 0x0d, 0x78, 0xc1, 0xf6, 0x22, 0xc2, 0x0c, 0xc2, 0x0b, 0x22, 0x22,
+};
+
+static const u8 fw_patch_vsc8584[] = {
+ 0xe8, 0x59, 0x02, 0xe8, 0x12, 0x02, 0xe8, 0x42, 0x02, 0xe8, 0x5a, 0x02,
+ 0xe8, 0x5b, 0x02, 0xe8, 0x5c, 0xe5, 0x69, 0x54, 0x0f, 0x24, 0xf7, 0x60,
+ 0x27, 0x24, 0xfc, 0x60, 0x23, 0x24, 0x08, 0x70, 0x14, 0xe5, 0x69, 0xae,
+ 0x68, 0x78, 0x04, 0xce, 0xa2, 0xe7, 0x13, 0xce, 0x13, 0xd8, 0xf8, 0x7e,
+ 0x00, 0x54, 0x0f, 0x80, 0x00, 0x7b, 0x01, 0x7a, 0x00, 0x7d, 0xee, 0x7f,
+ 0x92, 0x12, 0x50, 0xee, 0x22, 0xe4, 0xf5, 0x10, 0x85, 0x10, 0xfb, 0x7d,
+ 0x1c, 0xe4, 0xff, 0x12, 0x59, 0xea, 0x05, 0x10, 0xe5, 0x10, 0xc3, 0x94,
+ 0x04, 0x40, 0xed, 0x22, 0x22, 0x22, 0x22, 0x22,
+};
+
+static int vsc8584_get_fw_crc(struct mii_dev *bus, int phy, u16 start,
+ u16 *crc, const u8 *fw_patch, int fw_size)
+{
+ int ret;
+
+ bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+ MSCC_PHY_PAGE_EXT1);
+
+ bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_PHY_VERIPHY_CNTL_2, start);
+ /* Add one byte to size for the one added by the patch_fw function */
+ bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_PHY_VERIPHY_CNTL_3,
+ fw_size + 1);
+
+ ret = vsc8584_cmd(bus, phy, PROC_CMD_CRC16);
+ if (ret)
+ goto out;
+
+ bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+ MSCC_PHY_PAGE_EXT1);
+
+ *crc = bus->read(bus, phy, MDIO_DEVAD_NONE, MSCC_PHY_VERIPHY_CNTL_2);
+
+out:
+ bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+ MSCC_PHY_PAGE_STD);
+
+ return ret;
+}
+
+static int vsc8584_patch_fw(struct mii_dev *bus, int phy, const u8 *fw_patch,
+ int fw_size)
+{
+ int i, ret;
+
+ ret = vsc8584_micro_assert_reset(bus, phy);
+ if (ret) {
+ pr_err("%s: failed to assert reset of micro\n", __func__);
+ return ret;
+ }
+
+ bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+ MSCC_PHY_PAGE_GPIO);
+
+ /*
+ * Hold 8051 Micro in SW Reset, Enable auto incr address and patch clock
+ * Disable the 8051 Micro clock
+ */
+ bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_DW8051_CNTL_STATUS,
+ RUN_FROM_INT_ROM | AUTOINC_ADDR | PATCH_RAM_CLK |
+ MICRO_CLK_EN | MICRO_CLK_DIVIDE(2));
+ bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_INT_MEM_CNTL, READ_PRAM |
+ INT_MEM_WRITE_EN | INT_MEM_DATA(2));
+ bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_INT_MEM_ADDR, 0x0000);
+
+ for (i = 0; i < fw_size; i++)
+ bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_INT_MEM_CNTL,
+ READ_PRAM | INT_MEM_WRITE_EN | fw_patch[i]);
+
+ /* Clear internal memory access */
+ bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_INT_MEM_CNTL, READ_RAM);
+
+ bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+ MSCC_PHY_PAGE_STD);
+
+ return 0;
+}
+
+static bool vsc8574_is_serdes_init(struct mii_dev *bus, int phy)
+{
+ u16 reg;
+ bool ret;
+
+ bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+ MSCC_PHY_PAGE_GPIO);
+
+ reg = bus->read(bus, phy, MDIO_DEVAD_NONE, MSCC_TRAP_ROM_ADDR(1));
+ if (reg != MSCC_TRAP_ROM_ADDR_SERDES_INIT) {
+ ret = false;
+ goto out;
+ }
+
+ reg = bus->read(bus, phy, MDIO_DEVAD_NONE, MSCC_PATCH_RAM_ADDR(1));
+ if (reg != MSCC_PATCH_RAM_ADDR_SERDES_INIT) {
+ ret = false;
+ goto out;
+ }
+
+ reg = bus->read(bus, phy, MDIO_DEVAD_NONE, MSCC_INT_MEM_CNTL);
+ if (reg != EN_PATCH_RAM_TRAP_ADDR(1)) {
+ ret = false;
+ goto out;
+ }
+
+ reg = bus->read(bus, phy, MDIO_DEVAD_NONE, MSCC_DW8051_CNTL_STATUS);
+ if ((MICRO_NSOFT_RESET | RUN_FROM_INT_ROM | DW8051_CLK_EN |
+ MICRO_CLK_EN) != (reg & MSCC_DW8051_VLD_MASK)) {
+ ret = false;
+ goto out;
+ }
+
+ ret = true;
+
+out:
+ bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+ MSCC_PHY_PAGE_GPIO);
+
+ return ret;
+}
+
+static int vsc8574_config_pre_init(struct phy_device *phydev)
+{
+ struct mii_dev *bus = phydev->bus;
+ u16 crc, reg, phy0, addr;
+ bool serdes_init;
+ int ret;
+
+ phy_write(phydev, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+ MSCC_PHY_PAGE_EXT1);
+ addr = phy_read(phydev, MDIO_DEVAD_NONE, MSCC_PHY_EXT_PHY_CNTL_4);
+ addr >>= PHY_CNTL_4_ADDR_POS;
+
+ reg = phy_read(phydev, MDIO_DEVAD_NONE, MSCC_PHY_ACTIPHY_CNTL);
+ if (reg & PHY_ADDR_REVERSED)
+ phy0 = phydev->addr + addr;
+ else
+ phy0 = phydev->addr - addr;
+
+ bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+ MSCC_PHY_PAGE_STD);
+
+ /* all writes below are broadcasted to all PHYs in the same package */
+ reg = bus->read(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_EXT_CNTL_STATUS);
+ reg |= SMI_BROADCAST_WR_EN;
+ bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_EXT_CNTL_STATUS, reg);
+
+ /*
+ * The below register writes are tweaking analog and electrical
+ * configuration that were determined through characterization by PHY
+ * engineers. These don't mean anything more than "these are the best
+ * values".
+ */
+ bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_EXT_PHY_CNTL_2, 0x0040);
+
+ bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+ MSCC_PHY_PAGE_TEST);
+
+ bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_TEST_PAGE_20, 0x4320);
+ bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_TEST_PAGE_24, 0x0c00);
+ bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_TEST_PAGE_9, 0x18ca);
+ bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_TEST_PAGE_5, 0x1b20);
+
+ reg = bus->read(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_TEST_PAGE_8);
+ reg |= TR_CLK_DISABLE;
+ bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_TEST_PAGE_8, reg);
+
+ bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+ MSCC_PHY_PAGE_TR);
+
+ vsc8584_csr_write(bus, phy0, 0x0fae, 0x000401bd);
+ vsc8584_csr_write(bus, phy0, 0x0fac, 0x000f000f);
+ vsc8584_csr_write(bus, phy0, 0x17a0, 0x00a0f147);
+ vsc8584_csr_write(bus, phy0, 0x0fe4, 0x00052f54);
+ vsc8584_csr_write(bus, phy0, 0x1792, 0x0027303d);
+ vsc8584_csr_write(bus, phy0, 0x07fe, 0x00000704);
+ vsc8584_csr_write(bus, phy0, 0x0fe0, 0x00060150);
+ vsc8584_csr_write(bus, phy0, 0x0f82, 0x0012b00a);
+ vsc8584_csr_write(bus, phy0, 0x0f80, 0x00000d74);
+ vsc8584_csr_write(bus, phy0, 0x02e0, 0x00000012);
+ vsc8584_csr_write(bus, phy0, 0x03a2, 0x00050208);
+ vsc8584_csr_write(bus, phy0, 0x03b2, 0x00009186);
+ vsc8584_csr_write(bus, phy0, 0x0fb0, 0x000e3700);
+ vsc8584_csr_write(bus, phy0, 0x1688, 0x00049f81);
+ vsc8584_csr_write(bus, phy0, 0x0fd2, 0x0000ffff);
+ vsc8584_csr_write(bus, phy0, 0x168a, 0x00039fa2);
+ vsc8584_csr_write(bus, phy0, 0x1690, 0x0020640b);
+ vsc8584_csr_write(bus, phy0, 0x0258, 0x00002220);
+ vsc8584_csr_write(bus, phy0, 0x025a, 0x00002a20);
+ vsc8584_csr_write(bus, phy0, 0x025c, 0x00003060);
+ vsc8584_csr_write(bus, phy0, 0x025e, 0x00003fa0);
+ vsc8584_csr_write(bus, phy0, 0x03a6, 0x0000e0f0);
+ vsc8584_csr_write(bus, phy0, 0x0f92, 0x00001489);
+ vsc8584_csr_write(bus, phy0, 0x16a2, 0x00007000);
+ vsc8584_csr_write(bus, phy0, 0x16a6, 0x00071448);
+ vsc8584_csr_write(bus, phy0, 0x16a0, 0x00eeffdd);
+ vsc8584_csr_write(bus, phy0, 0x0fe8, 0x0091b06c);
+ vsc8584_csr_write(bus, phy0, 0x0fea, 0x00041600);
+ vsc8584_csr_write(bus, phy0, 0x16b0, 0x00eeff00);
+ vsc8584_csr_write(bus, phy0, 0x16b2, 0x00007000);
+ vsc8584_csr_write(bus, phy0, 0x16b4, 0x00000814);
+ vsc8584_csr_write(bus, phy0, 0x0f90, 0x00688980);
+ vsc8584_csr_write(bus, phy0, 0x03a4, 0x0000d8f0);
+ vsc8584_csr_write(bus, phy0, 0x0fc0, 0x00000400);
+ vsc8584_csr_write(bus, phy0, 0x07fa, 0x0050100f);
+ vsc8584_csr_write(bus, phy0, 0x0796, 0x00000003);
+ vsc8584_csr_write(bus, phy0, 0x07f8, 0x00c3ff98);
+ vsc8584_csr_write(bus, phy0, 0x0fa4, 0x0018292a);
+ vsc8584_csr_write(bus, phy0, 0x168c, 0x00d2c46f);
+ vsc8584_csr_write(bus, phy0, 0x17a2, 0x00000620);
+ vsc8584_csr_write(bus, phy0, 0x16a4, 0x0013132f);
+ vsc8584_csr_write(bus, phy0, 0x16a8, 0x00000000);
+ vsc8584_csr_write(bus, phy0, 0x0ffc, 0x00c0a028);
+ vsc8584_csr_write(bus, phy0, 0x0fec, 0x00901c09);
+ vsc8584_csr_write(bus, phy0, 0x0fee, 0x0004a6a1);
+ vsc8584_csr_write(bus, phy0, 0x0ffe, 0x00b01807);
+
+ bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+ MSCC_PHY_PAGE_EXT2);
+
+ bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_CU_PMD_TX_CNTL, 0x028e);
+
+ bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+ MSCC_PHY_PAGE_TR);
+
+ vsc8584_csr_write(bus, phy0, 0x0486, 0x0008a518);
+ vsc8584_csr_write(bus, phy0, 0x0488, 0x006dc696);
+ vsc8584_csr_write(bus, phy0, 0x048a, 0x00000912);
+ vsc8584_csr_write(bus, phy0, 0x048e, 0x00000db6);
+ vsc8584_csr_write(bus, phy0, 0x049c, 0x00596596);
+ vsc8584_csr_write(bus, phy0, 0x049e, 0x00000514);
+ vsc8584_csr_write(bus, phy0, 0x04a2, 0x00410280);
+ vsc8584_csr_write(bus, phy0, 0x04a4, 0x00000000);
+ vsc8584_csr_write(bus, phy0, 0x04a6, 0x00000000);
+ vsc8584_csr_write(bus, phy0, 0x04a8, 0x00000000);
+ vsc8584_csr_write(bus, phy0, 0x04aa, 0x00000000);
+ vsc8584_csr_write(bus, phy0, 0x04ae, 0x007df7dd);
+ vsc8584_csr_write(bus, phy0, 0x04b0, 0x006d95d4);
+ vsc8584_csr_write(bus, phy0, 0x04b2, 0x00492410);
+
+ bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+ MSCC_PHY_PAGE_TEST);
+
+ reg = bus->read(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_TEST_PAGE_8);
+ reg &= ~TR_CLK_DISABLE;
+ bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_TEST_PAGE_8, reg);
+
+ bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+ MSCC_PHY_PAGE_STD);
+
+ /* end of write broadcasting */
+ reg = bus->read(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_EXT_CNTL_STATUS);
+ reg &= ~SMI_BROADCAST_WR_EN;
+ bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_EXT_CNTL_STATUS, reg);
+
+ ret = vsc8584_get_fw_crc(bus, phy0,
+ MSCC_VSC8574_REVB_INT8051_FW_START_ADDR, &crc,
+ fw_patch_vsc8574,
+ ARRAY_SIZE(fw_patch_vsc8574));
+ if (ret)
+ goto out;
+
+ if (crc == MSCC_VSC8574_REVB_INT8051_FW_CRC) {
+ serdes_init = vsc8574_is_serdes_init(bus, phy0);
+
+ if (!serdes_init) {
+ ret = vsc8584_micro_assert_reset(bus, phy0);
+ if (ret) {
+ pr_err("failed to assert reset of micro\n");
+ return ret;
+ }
+ }
+ } else {
+ pr_debug("FW CRC is not the expected one, patching FW\n");
+
+ serdes_init = false;
+
+ if (vsc8584_patch_fw(bus, phy0, fw_patch_vsc8574,
+ ARRAY_SIZE(fw_patch_vsc8574)))
+ pr_warn("failed to patch FW, expect non-optimal device\n");
+ }
+
+ if (!serdes_init) {
+ bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+ MSCC_PHY_PAGE_GPIO);
+
+ bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_TRAP_ROM_ADDR(1),
+ MSCC_TRAP_ROM_ADDR_SERDES_INIT);
+ bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_PATCH_RAM_ADDR(1),
+ MSCC_PATCH_RAM_ADDR_SERDES_INIT);
+
+ bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_INT_MEM_CNTL,
+ EN_PATCH_RAM_TRAP_ADDR(1));
+
+ vsc8584_micro_deassert_reset(bus, phy0, false);
+
+ ret = vsc8584_get_fw_crc(bus, phy0,
+ MSCC_VSC8574_REVB_INT8051_FW_START_ADDR,
+ &crc, fw_patch_vsc8574,
+ ARRAY_SIZE(fw_patch_vsc8574));
+ if (ret)
+ goto out;
+
+ if (crc != MSCC_VSC8574_REVB_INT8051_FW_CRC)
+ pr_warn("FW CRC after patching is not the expected one, expect non-optimal device\n");
+ }
+
+ bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+ MSCC_PHY_PAGE_GPIO);
+
+ ret = vsc8584_cmd(bus, phy0, PROC_CMD_1588_DEFAULT_INIT |
+ PROC_CMD_PHY_INIT);
+
+out:
+ bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+ MSCC_PHY_PAGE_STD);
+
+ return ret;
+}
+
+static int vsc8584_config_pre_init(struct phy_device *phydev)
+{
+ struct mii_dev *bus = phydev->bus;
+ u16 reg, crc, phy0, addr;
+ int ret;
+
+ if ((phydev->phy_id & MSCC_DEV_REV_MASK) != VSC8584_REVB) {
+ pr_warn("VSC8584 revA not officially supported, skipping firmware patching. Use at your own risk.\n");
+ return 0;
+ }
+
+ phy_write(phydev, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+ MSCC_PHY_PAGE_EXT1);
+ addr = phy_read(phydev, MDIO_DEVAD_NONE, MSCC_PHY_EXT_PHY_CNTL_4);
+ addr >>= PHY_CNTL_4_ADDR_POS;
+
+ reg = phy_read(phydev, MDIO_DEVAD_NONE, MSCC_PHY_ACTIPHY_CNTL);
+ if (reg & PHY_ADDR_REVERSED)
+ phy0 = phydev->addr + addr;
+ else
+ phy0 = phydev->addr - addr;
+
+ bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+ MSCC_PHY_PAGE_STD);
+
+ /* all writes below are broadcasted to all PHYs in the same package */
+ reg = bus->read(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_EXT_CNTL_STATUS);
+ reg |= SMI_BROADCAST_WR_EN;
+ bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_EXT_CNTL_STATUS, reg);
+
+ /*
+ * The below register writes are tweaking analog and electrical
+ * configuration that were determined through characterization by PHY
+ * engineers. These don't mean anything more than "these are the best
+ * values".
+ */
+ reg = bus->read(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_BYPASS_CONTROL);
+ reg |= PARALLEL_DET_IGNORE_ADVERTISED;
+ bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_BYPASS_CONTROL, reg);
+
+ bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+ MSCC_PHY_PAGE_EXT3);
+
+ bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_SERDES_TX_CRC_ERR_CNT,
+ 0x2000);
+
+ bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+ MSCC_PHY_PAGE_TEST);
+
+ bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_TEST_PAGE_5, 0x1f20);
+
+ reg = bus->read(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_TEST_PAGE_8);
+ reg |= TR_CLK_DISABLE;
+ bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_TEST_PAGE_8, reg);
+
+ bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+ MSCC_PHY_PAGE_TR);
+
+ bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_REG_TR_ADDR_16, 0xafa4);
+
+ reg = bus->read(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_REG_TR_DATA_18);
+ reg &= ~0x007f;
+ reg |= 0x0019;
+ bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_REG_TR_DATA_18, reg);
+
+ bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_REG_TR_ADDR_16, 0x8fa4);
+
+ vsc8584_csr_write(bus, phy0, 0x07fa, 0x0050100f);
+ vsc8584_csr_write(bus, phy0, 0x1688, 0x00049f81);
+ vsc8584_csr_write(bus, phy0, 0x0f90, 0x00688980);
+ vsc8584_csr_write(bus, phy0, 0x03a4, 0x0000d8f0);
+ vsc8584_csr_write(bus, phy0, 0x0fc0, 0x00000400);
+ vsc8584_csr_write(bus, phy0, 0x0f82, 0x0012b002);
+ vsc8584_csr_write(bus, phy0, 0x1686, 0x00000004);
+ vsc8584_csr_write(bus, phy0, 0x168c, 0x00d2c46f);
+ vsc8584_csr_write(bus, phy0, 0x17a2, 0x00000620);
+ vsc8584_csr_write(bus, phy0, 0x16a0, 0x00eeffdd);
+ vsc8584_csr_write(bus, phy0, 0x16a6, 0x00071448);
+ vsc8584_csr_write(bus, phy0, 0x16a4, 0x0013132f);
+ vsc8584_csr_write(bus, phy0, 0x16a8, 0x00000000);
+ vsc8584_csr_write(bus, phy0, 0x0ffc, 0x00c0a028);
+ vsc8584_csr_write(bus, phy0, 0x0fe8, 0x0091b06c);
+ vsc8584_csr_write(bus, phy0, 0x0fea, 0x00041600);
+ vsc8584_csr_write(bus, phy0, 0x0f80, 0x00fffaff);
+ vsc8584_csr_write(bus, phy0, 0x0fec, 0x00901809);
+ vsc8584_csr_write(bus, phy0, 0x0ffe, 0x00b01007);
+ vsc8584_csr_write(bus, phy0, 0x16b0, 0x00eeff00);
+ vsc8584_csr_write(bus, phy0, 0x16b2, 0x00007000);
+ vsc8584_csr_write(bus, phy0, 0x16b4, 0x00000814);
+
+ bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+ MSCC_PHY_PAGE_EXT2);
+
+ bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_CU_PMD_TX_CNTL, 0x028e);
+
+ bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+ MSCC_PHY_PAGE_TR);
+
+ vsc8584_csr_write(bus, phy0, 0x0486, 0x0008a518);
+ vsc8584_csr_write(bus, phy0, 0x0488, 0x006dc696);
+ vsc8584_csr_write(bus, phy0, 0x048a, 0x00000912);
+
+ bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+ MSCC_PHY_PAGE_TEST);
+
+ reg = bus->read(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_TEST_PAGE_8);
+ reg &= ~TR_CLK_DISABLE;
+ bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_TEST_PAGE_8, reg);
+
+ bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+ MSCC_PHY_PAGE_STD);
+
+ /* end of write broadcasting */
+ reg = bus->read(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_EXT_CNTL_STATUS);
+ reg &= ~SMI_BROADCAST_WR_EN;
+ bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_EXT_CNTL_STATUS, reg);
+
+ ret = vsc8584_get_fw_crc(bus, phy0,
+ MSCC_VSC8584_REVB_INT8051_FW_START_ADDR, &crc,
+ fw_patch_vsc8584,
+ ARRAY_SIZE(fw_patch_vsc8584));
+ if (ret)
+ goto out;
+
+ if (crc != MSCC_VSC8584_REVB_INT8051_FW_CRC) {
+ debug("FW CRC is not the expected one, patching FW...\n");
+ if (vsc8584_patch_fw(bus, phy0, fw_patch_vsc8584,
+ ARRAY_SIZE(fw_patch_vsc8584)))
+ pr_warn("failed to patch FW, expect non-optimal device\n");
+ }
+
+ vsc8584_micro_deassert_reset(bus, phy0, false);
+
+ ret = vsc8584_get_fw_crc(bus, phy0,
+ MSCC_VSC8584_REVB_INT8051_FW_START_ADDR, &crc,
+ fw_patch_vsc8584,
+ ARRAY_SIZE(fw_patch_vsc8584));
+ if (ret)
+ goto out;
+
+ if (crc != MSCC_VSC8584_REVB_INT8051_FW_CRC)
+ pr_warn("FW CRC after patching is not the expected one, expect non-optimal device\n");
+
+ ret = vsc8584_micro_assert_reset(bus, phy0);
+ if (ret)
+ goto out;
+
+ vsc8584_micro_deassert_reset(bus, phy0, true);
+
+out:
+ bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+ MSCC_PHY_PAGE_STD);
+
+ return ret;
+}
+
+static int mscc_vsc8531_vsc8541_init_scripts(struct phy_device *phydev)
+{
+ u16 reg_val;
+
+ /* Set to Access Token Ring Registers */
+ phy_write(phydev, MDIO_DEVAD_NONE,
+ MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_TR);
+
+ /* Update LinkDetectCtrl default to optimized values */
+ /* Determined during Silicon Validation Testing */
+ phy_write(phydev, MDIO_DEVAD_NONE, MSCC_PHY_REG_TR_ADDR_16,
+ (MSCC_PHY_TR_LINKDETCTRL_ADDR | MSCC_PHY_TR_16_READ));
+ reg_val = phy_read(phydev, MDIO_DEVAD_NONE, MSCC_PHY_REG_TR_DATA_17);
+ reg_val = bitfield_replace(reg_val, MSCC_PHY_TR_LINKDETCTRL_POS,
+ MSCC_PHY_TR_LINKDETCTRL_WIDTH,
+ MSCC_PHY_TR_LINKDETCTRL_VAL);
+
+ phy_write(phydev, MDIO_DEVAD_NONE, MSCC_PHY_REG_TR_DATA_17, reg_val);
+ phy_write(phydev, MDIO_DEVAD_NONE, MSCC_PHY_REG_TR_ADDR_16,
+ (MSCC_PHY_TR_LINKDETCTRL_ADDR | MSCC_PHY_TR_16_WRITE));
+
+ /* Update VgaThresh100 defaults to optimized values */
+ /* Determined during Silicon Validation Testing */
+ phy_write(phydev, MDIO_DEVAD_NONE, MSCC_PHY_REG_TR_ADDR_16,
+ (MSCC_PHY_TR_VGATHRESH100_ADDR | MSCC_PHY_TR_16_READ));
+
+ reg_val = phy_read(phydev, MDIO_DEVAD_NONE, MSCC_PHY_REG_TR_DATA_18);
+ reg_val = bitfield_replace(reg_val, MSCC_PHY_TR_VGATHRESH100_POS,
+ MSCC_PHY_TR_VGATHRESH100_WIDTH,
+ MSCC_PHY_TR_VGATHRESH100_VAL);
+
+ phy_write(phydev, MDIO_DEVAD_NONE, MSCC_PHY_REG_TR_DATA_18, reg_val);
+ phy_write(phydev, MDIO_DEVAD_NONE, MSCC_PHY_REG_TR_ADDR_16,
+ (MSCC_PHY_TR_VGATHRESH100_ADDR | MSCC_PHY_TR_16_WRITE));
+
+ /* Update VgaGain10 defaults to optimized values */
+ /* Determined during Silicon Validation Testing */
+ phy_write(phydev, MDIO_DEVAD_NONE, MSCC_PHY_REG_TR_ADDR_16,
+ (MSCC_PHY_TR_VGAGAIN10_ADDR | MSCC_PHY_TR_16_READ));
+
+ reg_val = phy_read(phydev, MDIO_DEVAD_NONE, MSCC_PHY_REG_TR_DATA_18);
+ reg_val = bitfield_replace(reg_val, MSCC_PHY_TR_VGAGAIN10_U_POS,
+ MSCC_PHY_TR_VGAGAIN10_U_WIDTH,
+ MSCC_PHY_TR_VGAGAIN10_U_VAL);
+
+ phy_write(phydev, MDIO_DEVAD_NONE, MSCC_PHY_REG_TR_DATA_18, reg_val);
+ reg_val = phy_read(phydev, MDIO_DEVAD_NONE, MSCC_PHY_REG_TR_DATA_17);
+ reg_val = bitfield_replace(reg_val, MSCC_PHY_TR_VGAGAIN10_L_POS,
+ MSCC_PHY_TR_VGAGAIN10_L_WIDTH,
+ MSCC_PHY_TR_VGAGAIN10_L_VAL);
+
+ phy_write(phydev, MDIO_DEVAD_NONE, MSCC_PHY_REG_TR_DATA_17, reg_val);
+ phy_write(phydev, MDIO_DEVAD_NONE, MSCC_PHY_REG_TR_ADDR_16,
+ (MSCC_PHY_TR_VGAGAIN10_ADDR | MSCC_PHY_TR_16_WRITE));
+
+ /* Set back to Access Standard Page Registers */
+ phy_write(phydev, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+ MSCC_PHY_PAGE_STD);
+
+ return 0;
+}
+
+static int mscc_parse_status(struct phy_device *phydev)
+{
+ u16 speed;
+ u16 mii_reg;
+
+ mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_AUX_CNTRL_STAT_REG);
+
+ if (mii_reg & MIIM_AUX_CNTRL_STAT_F_DUPLEX)
+ phydev->duplex = DUPLEX_FULL;
+ else
+ phydev->duplex = DUPLEX_HALF;
+
+ speed = mii_reg & MIIM_AUX_CNTRL_STAT_SPEED_MASK;
+ speed = speed >> MIIM_AUX_CNTRL_STAT_SPEED_POS;
+
+ switch (speed) {
+ case MIIM_AUX_CNTRL_STAT_SPEED_1000M:
+ phydev->speed = SPEED_1000;
+ break;
+ case MIIM_AUX_CNTRL_STAT_SPEED_100M:
+ phydev->speed = SPEED_100;
+ break;
+ case MIIM_AUX_CNTRL_STAT_SPEED_10M:
+ phydev->speed = SPEED_10;
+ break;
+ default:
+ phydev->speed = SPEED_10;
+ break;
+ }
+
+ return 0;
+}
+
+static int mscc_startup(struct phy_device *phydev)
+{
+ int retval;
+
+ retval = genphy_update_link(phydev);
+
+ if (retval)
+ return retval;
+
+ return mscc_parse_status(phydev);
+}
+
+static int mscc_phy_soft_reset(struct phy_device *phydev)
+{
+ int retval = 0;
+ u16 timeout = MSCC_PHY_RESET_TIMEOUT;
+ u16 reg_val = 0;
+
+ phy_write(phydev, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+ MSCC_PHY_PAGE_STD);
+
+ reg_val = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR);
+ phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, (reg_val | BMCR_RESET));
+
+ reg_val = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR);
+
+ while ((reg_val & BMCR_RESET) && (timeout > 0)) {
+ reg_val = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR);
+ timeout--;
+ udelay(1000); /* 1 ms */
+ }
+
+ if (timeout == 0) {
+ printf("MSCC PHY Soft_Reset Error: mac i/f = 0x%x\n",
+ phydev->interface);
+ retval = -ETIME;
+ }
+
+ return retval;
+}
+
+static int vsc8531_vsc8541_mac_config(struct phy_device *phydev)
+{
+ u16 reg_val = 0;
+ u16 mac_if = 0;
+ u16 rx_clk_out = 0;
+
+ /* For VSC8530/31 the only MAC modes are RMII/RGMII. */
+ /* For VSC8540/41 the only MAC modes are (G)MII and RMII/RGMII. */
+ /* Setup MAC Configuration */
+ switch (phydev->interface) {
+ case PHY_INTERFACE_MODE_MII:
+ case PHY_INTERFACE_MODE_GMII:
+ /* Set Reg23.12:11=0 */
+ mac_if = MAC_IF_SELECTION_GMII;
+ /* Set Reg20E2.11=1 */
+ rx_clk_out = RX_CLK_OUT_DISABLE;
+ break;
+
+ case PHY_INTERFACE_MODE_RMII:
+ /* Set Reg23.12:11=1 */
+ mac_if = MAC_IF_SELECTION_RMII;
+ /* Set Reg20E2.11=0 */
+ rx_clk_out = RX_CLK_OUT_NORMAL;
+ break;
+
+ case PHY_INTERFACE_MODE_RGMII_TXID:
+ case PHY_INTERFACE_MODE_RGMII_RXID:
+ case PHY_INTERFACE_MODE_RGMII_ID:
+ case PHY_INTERFACE_MODE_RGMII:
+ /* Set Reg23.12:11=2 */
+ mac_if = MAC_IF_SELECTION_RGMII;
+ /* Set Reg20E2.11=0 */
+ rx_clk_out = RX_CLK_OUT_NORMAL;
+ break;
+
+ default:
+ printf("MSCC PHY - INVALID MAC i/f Config: mac i/f = 0x%x\n",
+ phydev->interface);
+ return -EINVAL;
+ }
+
+ phy_write(phydev, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+ MSCC_PHY_PAGE_STD);
+
+ reg_val = phy_read(phydev, MDIO_DEVAD_NONE,
+ MSCC_PHY_EXT_PHY_CNTL_1_REG);
+ /* Set MAC i/f bits Reg23.12:11 */
+ reg_val = bitfield_replace(reg_val, MAC_IF_SELECTION_POS,
+ MAC_IF_SELECTION_WIDTH, mac_if);
+ /* Update Reg23.12:11 */
+ phy_write(phydev, MDIO_DEVAD_NONE,
+ MSCC_PHY_EXT_PHY_CNTL_1_REG, reg_val);
+ /* Setup ExtPg_2 Register Access */
+ phy_write(phydev, MDIO_DEVAD_NONE,
+ MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_EXT2);
+ /* Read Reg20E2 */
+ reg_val = phy_read(phydev, MDIO_DEVAD_NONE,
+ MSCC_PHY_RGMII_CNTL_REG);
+ reg_val = bitfield_replace(reg_val, RX_CLK_OUT_POS,
+ RX_CLK_OUT_WIDTH, rx_clk_out);
+ /* Update Reg20E2.11 */
+ phy_write(phydev, MDIO_DEVAD_NONE,
+ MSCC_PHY_RGMII_CNTL_REG, reg_val);
+ /* Before leaving - Change back to Std Page Register Access */
+ phy_write(phydev, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+ MSCC_PHY_PAGE_STD);
+
+ return 0;
+}
+
+static int vsc8531_vsc8541_clkout_config(struct phy_device *phydev)
+{
+ struct ofnode_phandle_args phandle_args;
+ u32 clkout_rate = 0;
+ u16 reg_val;
+ int retval;
+
+ retval = dev_read_phandle_with_args(phydev->dev, "phy-handle", NULL,
+ 0, 0, &phandle_args);
+ if (!retval)
+ clkout_rate = ofnode_read_u32_default(phandle_args.node,
+ "vsc8531,clk-out-frequency", 0);
+
+ switch (clkout_rate) {
+ case 0:
+ reg_val = 0;
+ break;
+ case 25000000:
+ reg_val = CLKOUT_FREQ_25M | CLKOUT_ENABLE;
+ break;
+ case 50000000:
+ reg_val = CLKOUT_FREQ_50M | CLKOUT_ENABLE;
+ break;
+ case 125000000:
+ reg_val = CLKOUT_FREQ_125M | CLKOUT_ENABLE;
+ break;
+ default:
+ printf("PHY 8530/31 invalid clkout rate %u\n",
+ clkout_rate);
+ return -EINVAL;
+ }
+
+ phy_write(phydev, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+ MSCC_PHY_PAGE_GPIO);
+ phy_write(phydev, MDIO_DEVAD_NONE, MSCC_CLKOUT_CNTL, reg_val);
+ phy_write(phydev, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+ MSCC_PHY_PAGE_STD);
+
+ return 0;
+}
+
+static int vsc8531_vsc8541_clk_skew_config(struct phy_device *phydev)
+{
+ enum vsc_phy_rgmii_skew rx_clk_skew = VSC_PHY_RGMII_DELAY_200_PS;
+ enum vsc_phy_rgmii_skew tx_clk_skew = VSC_PHY_RGMII_DELAY_200_PS;
+ u16 reg_val;
+
+ if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID ||
+ phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
+ rx_clk_skew = VSC_PHY_RGMII_DELAY_2000_PS;
+
+ if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID ||
+ phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
+ tx_clk_skew = VSC_PHY_RGMII_DELAY_2000_PS;
+
+ phy_write(phydev, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+ MSCC_PHY_PAGE_EXT2);
+ reg_val = phy_read(phydev, MDIO_DEVAD_NONE, MSCC_PHY_RGMII_CNTL_REG);
+
+ /* Reg20E2 - Update RGMII RX_Clk Skews. */
+ reg_val = bitfield_replace(reg_val, RGMII_RX_CLK_DELAY_POS,
+ RGMII_RX_CLK_DELAY_WIDTH, rx_clk_skew);
+ /* Reg20E2 - Update RGMII TX_Clk Skews. */
+ reg_val = bitfield_replace(reg_val, RGMII_TX_CLK_DELAY_POS,
+ RGMII_TX_CLK_DELAY_WIDTH, tx_clk_skew);
+
+ phy_write(phydev, MDIO_DEVAD_NONE, MSCC_PHY_RGMII_CNTL_REG, reg_val);
+ phy_write(phydev, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+ MSCC_PHY_PAGE_STD);
+
+ return 0;
+}
+
+static int vsc8531_config(struct phy_device *phydev)
+{
+ int retval = -EINVAL;
+ u16 reg_val;
+ u16 rmii_clk_out;
+ enum vsc_phy_clk_slew edge_rate = VSC_PHY_CLK_SLEW_RATE_4;
+
+ /* For VSC8530/31 and VSC8540/41 the init scripts are the same */
+ mscc_vsc8531_vsc8541_init_scripts(phydev);
+
+ /* For VSC8530/31 the only MAC modes are RMII/RGMII. */
+ switch (phydev->interface) {
+ case PHY_INTERFACE_MODE_RMII:
+ case PHY_INTERFACE_MODE_RGMII:
+ case PHY_INTERFACE_MODE_RGMII_TXID:
+ case PHY_INTERFACE_MODE_RGMII_RXID:
+ case PHY_INTERFACE_MODE_RGMII_ID:
+ retval = vsc8531_vsc8541_mac_config(phydev);
+ if (retval != 0)
+ return retval;
+
+ retval = mscc_phy_soft_reset(phydev);
+ if (retval != 0)
+ return retval;
+ break;
+ default:
+ printf("PHY 8530/31 MAC i/f Config Error: mac i/f = 0x%x\n",
+ phydev->interface);
+ return -EINVAL;
+ }
+ /* Default RMII Clk Output to 0=OFF/1=ON */
+ rmii_clk_out = 0;
+
+ retval = vsc8531_vsc8541_clk_skew_config(phydev);
+ if (retval != 0)
+ return retval;
+
+ phy_write(phydev, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+ MSCC_PHY_PAGE_EXT2);
+ reg_val = phy_read(phydev, MDIO_DEVAD_NONE, MSCC_PHY_WOL_MAC_CONTROL);
+ /* Reg27E2 - Update Clk Slew Rate. */
+ reg_val = bitfield_replace(reg_val, EDGE_RATE_CNTL_POS,
+ EDGE_RATE_CNTL_WIDTH, edge_rate);
+ /* Reg27E2 - Update RMII Clk Out. */
+ reg_val = bitfield_replace(reg_val, RMII_CLK_OUT_ENABLE_POS,
+ RMII_CLK_OUT_ENABLE_WIDTH, rmii_clk_out);
+ /* Update Reg27E2 */
+ phy_write(phydev, MDIO_DEVAD_NONE, MSCC_PHY_WOL_MAC_CONTROL, reg_val);
+ phy_write(phydev, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+ MSCC_PHY_PAGE_STD);
+
+ /* Configure the clk output */
+ retval = vsc8531_vsc8541_clkout_config(phydev);
+ if (retval != 0)
+ return retval;
+
+ return genphy_config_aneg(phydev);
+}
+
+static int vsc8541_config(struct phy_device *phydev)
+{
+ int retval = -EINVAL;
+ u16 reg_val;
+ u16 rmii_clk_out;
+ enum vsc_phy_clk_slew edge_rate = VSC_PHY_CLK_SLEW_RATE_4;
+
+ /* For VSC8530/31 and VSC8540/41 the init scripts are the same */
+ mscc_vsc8531_vsc8541_init_scripts(phydev);
+
+ /* For VSC8540/41 the only MAC modes are (G)MII and RMII/RGMII. */
+ switch (phydev->interface) {
+ case PHY_INTERFACE_MODE_MII:
+ case PHY_INTERFACE_MODE_GMII:
+ case PHY_INTERFACE_MODE_RMII:
+ case PHY_INTERFACE_MODE_RGMII:
+ retval = vsc8531_vsc8541_mac_config(phydev);
+ if (retval != 0)
+ return retval;
+
+ retval = mscc_phy_soft_reset(phydev);
+ if (retval != 0)
+ return retval;
+ break;
+ default:
+ printf("PHY 8541 MAC i/f config Error: mac i/f = 0x%x\n",
+ phydev->interface);
+ return -EINVAL;
+ }
+ /* Default RMII Clk Output to 0=OFF/1=ON */
+ rmii_clk_out = 0;
+
+ retval = vsc8531_vsc8541_clk_skew_config(phydev);
+ if (retval != 0)
+ return retval;
+
+ phy_write(phydev, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+ MSCC_PHY_PAGE_EXT2);
+ reg_val = phy_read(phydev, MDIO_DEVAD_NONE, MSCC_PHY_WOL_MAC_CONTROL);
+ /* Reg27E2 - Update Clk Slew Rate. */
+ reg_val = bitfield_replace(reg_val, EDGE_RATE_CNTL_POS,
+ EDGE_RATE_CNTL_WIDTH, edge_rate);
+ /* Reg27E2 - Update RMII Clk Out. */
+ reg_val = bitfield_replace(reg_val, RMII_CLK_OUT_ENABLE_POS,
+ RMII_CLK_OUT_ENABLE_WIDTH, rmii_clk_out);
+ /* Update Reg27E2 */
+ phy_write(phydev, MDIO_DEVAD_NONE, MSCC_PHY_WOL_MAC_CONTROL, reg_val);
+ phy_write(phydev, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+ MSCC_PHY_PAGE_STD);
+
+ /* Configure the clk output */
+ retval = vsc8531_vsc8541_clkout_config(phydev);
+ if (retval != 0)
+ return retval;
+
+ return genphy_config_aneg(phydev);
+}
+
+static int vsc8584_config_init(struct phy_device *phydev)
+{
+ struct vsc85xx_priv *priv = phydev->priv;
+ int ret;
+ u16 addr;
+ u16 reg_val;
+ u16 val;
+
+ phy_write(phydev, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+ MSCC_PHY_PAGE_EXT1);
+ addr = phy_read(phydev, MDIO_DEVAD_NONE, MSCC_PHY_EXT_PHY_CNTL_4);
+ addr >>= PHY_CNTL_4_ADDR_POS;
+
+ ret = priv->config_pre(phydev);
+ if (ret)
+ return ret;
+
+ phy_write(phydev, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+ MSCC_PHY_PAGE_GPIO);
+
+ if (phydev->interface == PHY_INTERFACE_MODE_QSGMII)
+ val = MAC_CFG_QSGMII;
+ else
+ val = MAC_CFG_SGMII;
+
+ reg_val = phy_read(phydev, MDIO_DEVAD_NONE, MSCC_PHY_MAC_CFG_FASTLINK);
+ reg_val &= ~MAC_CFG_MASK;
+ reg_val |= val;
+ ret = phy_write(phydev, MDIO_DEVAD_NONE, MSCC_PHY_MAC_CFG_FASTLINK,
+ reg_val);
+ if (ret)
+ return ret;
+
+ reg_val = PROC_CMD_MCB_ACCESS_MAC_CONF | PROC_CMD_RST_CONF_PORT |
+ PROC_CMD_READ_MOD_WRITE_PORT;
+ if (phydev->interface == PHY_INTERFACE_MODE_QSGMII)
+ reg_val |= PROC_CMD_QSGMII_MAC;
+ else
+ reg_val |= PROC_CMD_SGMII_MAC;
+
+ ret = vsc8584_cmd(phydev->bus, phydev->addr, reg_val);
+ if (ret)
+ return ret;
+
+ mdelay(10);
+
+ /* Disable SerDes for 100Base-FX */
+ ret = vsc8584_cmd(phydev->bus, phydev->addr, PROC_CMD_FIBER_MEDIA_CONF |
+ PROC_CMD_FIBER_PORT(addr) | PROC_CMD_FIBER_DISABLE |
+ PROC_CMD_READ_MOD_WRITE_PORT |
+ PROC_CMD_RST_CONF_PORT | PROC_CMD_FIBER_100BASE_FX);
+ if (ret)
+ return ret;
+
+ /* Disable SerDes for 1000Base-X */
+ ret = vsc8584_cmd(phydev->bus, phydev->addr, PROC_CMD_FIBER_MEDIA_CONF |
+ PROC_CMD_FIBER_PORT(addr) | PROC_CMD_FIBER_DISABLE |
+ PROC_CMD_READ_MOD_WRITE_PORT |
+ PROC_CMD_RST_CONF_PORT | PROC_CMD_FIBER_1000BASE_X);
+ if (ret)
+ return ret;
+
+ phy_write(phydev, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+ MSCC_PHY_PAGE_STD);
+ reg_val = phy_read(phydev, MDIO_DEVAD_NONE,
+ MSCC_PHY_EXT_PHY_CNTL_1_REG);
+ reg_val &= ~(MEDIA_OP_MODE_MASK | VSC8584_MAC_IF_SELECTION_MASK);
+ reg_val |= MEDIA_OP_MODE_COPPER |
+ (VSC8584_MAC_IF_SELECTION_SGMII <<
+ VSC8584_MAC_IF_SELECTION_POS);
+ ret = phy_write(phydev, MDIO_DEVAD_NONE, MSCC_PHY_EXT_PHY_CNTL_1_REG,
+ reg_val);
+
+ ret = mscc_phy_soft_reset(phydev);
+ if (ret != 0)
+ return ret;
+
+ return genphy_config(phydev);
+}
+
+static struct vsc85xx_priv vsc8574_priv = {
+ .config_pre = vsc8574_config_pre_init,
+};
+
+static int vsc8574_config(struct phy_device *phydev)
+{
+ phydev->priv = &vsc8574_priv;
+
+ return vsc8584_config_init(phydev);
+}
+
+static struct vsc85xx_priv vsc8584_priv = {
+ .config_pre = vsc8584_config_pre_init,
+};
+
+static int vsc8584_config(struct phy_device *phydev)
+{
+ phydev->priv = &vsc8584_priv;
+
+ return vsc8584_config_init(phydev);
+}
+
+static struct phy_driver VSC8530_driver = {
+ .name = "Microsemi VSC8530",
+ .uid = PHY_ID_VSC8530,
+ .mask = 0x000ffff0,
+ .features = PHY_BASIC_FEATURES,
+ .config = &vsc8531_config,
+ .startup = &mscc_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+static struct phy_driver VSC8531_driver = {
+ .name = "Microsemi VSC8531",
+ .uid = PHY_ID_VSC8531,
+ .mask = 0x000ffff0,
+ .features = PHY_GBIT_FEATURES,
+ .config = &vsc8531_config,
+ .startup = &mscc_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+static struct phy_driver VSC8540_driver = {
+ .name = "Microsemi VSC8540",
+ .uid = PHY_ID_VSC8540,
+ .mask = 0x000ffff0,
+ .features = PHY_BASIC_FEATURES,
+ .config = &vsc8541_config,
+ .startup = &mscc_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+static struct phy_driver VSC8541_driver = {
+ .name = "Microsemi VSC8541",
+ .uid = PHY_ID_VSC8541,
+ .mask = 0x000ffff0,
+ .features = PHY_GBIT_FEATURES,
+ .config = &vsc8541_config,
+ .startup = &mscc_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+static struct phy_driver VSC8574_driver = {
+ .name = "Microsemi VSC8574",
+ .uid = PHY_ID_VSC8574,
+ .mask = 0x000ffff0,
+ .features = PHY_GBIT_FEATURES,
+ .config = &vsc8574_config,
+ .startup = &mscc_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+static struct phy_driver VSC8584_driver = {
+ .name = "Microsemi VSC8584",
+ .uid = PHY_ID_VSC8584,
+ .mask = 0x000ffff0,
+ .features = PHY_GBIT_FEATURES,
+ .config = &vsc8584_config,
+ .startup = &mscc_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+int phy_mscc_init(void)
+{
+ phy_register(&VSC8530_driver);
+ phy_register(&VSC8531_driver);
+ phy_register(&VSC8540_driver);
+ phy_register(&VSC8541_driver);
+ phy_register(&VSC8574_driver);
+ phy_register(&VSC8584_driver);
+
+ return 0;
+}
diff --git a/roms/u-boot/drivers/net/phy/mv88e61xx.c b/roms/u-boot/drivers/net/phy/mv88e61xx.c
new file mode 100644
index 000000000..7eff37b24
--- /dev/null
+++ b/roms/u-boot/drivers/net/phy/mv88e61xx.c
@@ -0,0 +1,1223 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2015
+ * Elecsys Corporation <www.elecsyscorp.com>
+ * Kevin Smith <kevin.smith@elecsyscorp.com>
+ *
+ * Original driver:
+ * (C) Copyright 2009
+ * Marvell Semiconductor <www.marvell.com>
+ * Prafulla Wadaskar <prafulla@marvell.com>
+ */
+
+/*
+ * PHY driver for mv88e61xx ethernet switches.
+ *
+ * This driver configures the mv88e61xx for basic use as a PHY. The switch
+ * supports a VLAN configuration that determines how traffic will be routed
+ * between the ports. This driver uses a simple configuration that routes
+ * traffic from each PHY port only to the CPU port, and from the CPU port to
+ * any PHY port.
+ *
+ * The configuration determines which PHY ports to activate using the
+ * CONFIG_MV88E61XX_PHY_PORTS bitmask. Setting bit 0 will activate port 0, bit
+ * 1 activates port 1, etc. Do not set the bit for the port the CPU is
+ * connected to unless it is connected over a PHY interface (not MII).
+ *
+ * This driver was written for and tested on the mv88e6176 with an SGMII
+ * connection. Other configurations should be supported, but some additions or
+ * changes may be required.
+ */
+
+#include <common.h>
+#include <log.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+
+#include <bitfield.h>
+#include <errno.h>
+#include <malloc.h>
+#include <miiphy.h>
+#include <netdev.h>
+
+#define PHY_AUTONEGOTIATE_TIMEOUT 5000
+
+#define PORT_MASK(port_count) ((1 << (port_count)) - 1)
+
+/* Device addresses */
+#define DEVADDR_PHY(p) (p)
+#define DEVADDR_SERDES 0x0F
+
+/* SMI indirection registers for multichip addressing mode */
+#define SMI_CMD_REG 0x00
+#define SMI_DATA_REG 0x01
+
+/* Global registers */
+#define GLOBAL1_STATUS 0x00
+#define GLOBAL1_CTRL 0x04
+#define GLOBAL1_MON_CTRL 0x1A
+
+/* Global 2 registers */
+#define GLOBAL2_REG_PHY_CMD 0x18
+#define GLOBAL2_REG_PHY_DATA 0x19
+
+/* Port registers */
+#define PORT_REG_STATUS 0x00
+#define PORT_REG_PHYS_CTRL 0x01
+#define PORT_REG_SWITCH_ID 0x03
+#define PORT_REG_CTRL 0x04
+#define PORT_REG_VLAN_MAP 0x06
+#define PORT_REG_VLAN_ID 0x07
+
+/* Phy registers */
+#define PHY_REG_CTRL1 0x10
+#define PHY_REG_STATUS1 0x11
+#define PHY_REG_PAGE 0x16
+
+/* Serdes registers */
+#define SERDES_REG_CTRL_1 0x10
+
+/* Phy page numbers */
+#define PHY_PAGE_COPPER 0
+#define PHY_PAGE_SERDES 1
+
+/* Register fields */
+#define GLOBAL1_CTRL_SWRESET BIT(15)
+
+#define GLOBAL1_MON_CTRL_CPUDEST_SHIFT 4
+#define GLOBAL1_MON_CTRL_CPUDEST_WIDTH 4
+
+#define PORT_REG_STATUS_SPEED_SHIFT 8
+#define PORT_REG_STATUS_SPEED_10 0
+#define PORT_REG_STATUS_SPEED_100 1
+#define PORT_REG_STATUS_SPEED_1000 2
+
+#define PORT_REG_STATUS_CMODE_MASK 0xF
+#define PORT_REG_STATUS_CMODE_100BASE_X 0x8
+#define PORT_REG_STATUS_CMODE_1000BASE_X 0x9
+#define PORT_REG_STATUS_CMODE_SGMII 0xa
+
+#define PORT_REG_PHYS_CTRL_PCS_AN_EN BIT(10)
+#define PORT_REG_PHYS_CTRL_PCS_AN_RST BIT(9)
+#define PORT_REG_PHYS_CTRL_FC_VALUE BIT(7)
+#define PORT_REG_PHYS_CTRL_FC_FORCE BIT(6)
+#define PORT_REG_PHYS_CTRL_LINK_VALUE BIT(5)
+#define PORT_REG_PHYS_CTRL_LINK_FORCE BIT(4)
+#define PORT_REG_PHYS_CTRL_DUPLEX_VALUE BIT(3)
+#define PORT_REG_PHYS_CTRL_DUPLEX_FORCE BIT(2)
+#define PORT_REG_PHYS_CTRL_SPD1000 BIT(1)
+#define PORT_REG_PHYS_CTRL_SPD100 BIT(0)
+#define PORT_REG_PHYS_CTRL_SPD_MASK (BIT(1) | BIT(0))
+
+#define PORT_REG_CTRL_PSTATE_SHIFT 0
+#define PORT_REG_CTRL_PSTATE_WIDTH 2
+
+#define PORT_REG_VLAN_ID_DEF_VID_SHIFT 0
+#define PORT_REG_VLAN_ID_DEF_VID_WIDTH 12
+
+#define PORT_REG_VLAN_MAP_TABLE_SHIFT 0
+#define PORT_REG_VLAN_MAP_TABLE_WIDTH 11
+
+#define SERDES_REG_CTRL_1_FORCE_LINK BIT(10)
+
+/* Field values */
+#define PORT_REG_CTRL_PSTATE_DISABLED 0
+#define PORT_REG_CTRL_PSTATE_FORWARD 3
+
+#define PHY_REG_CTRL1_ENERGY_DET_OFF 0
+#define PHY_REG_CTRL1_ENERGY_DET_SENSE_PULSE 1
+#define PHY_REG_CTRL1_ENERGY_DET_SENSE_ONLY 2
+#define PHY_REG_CTRL1_ENERGY_DET_SENSE_XMIT 3
+
+/* PHY Status Register */
+#define PHY_REG_STATUS1_SPEED 0xc000
+#define PHY_REG_STATUS1_GBIT 0x8000
+#define PHY_REG_STATUS1_100 0x4000
+#define PHY_REG_STATUS1_DUPLEX 0x2000
+#define PHY_REG_STATUS1_SPDDONE 0x0800
+#define PHY_REG_STATUS1_LINK 0x0400
+#define PHY_REG_STATUS1_ENERGY 0x0010
+
+/*
+ * Macros for building commands for indirect addressing modes. These are valid
+ * for both the indirect multichip addressing mode and the PHY indirection
+ * required for the writes to any PHY register.
+ */
+#define SMI_BUSY BIT(15)
+#define SMI_CMD_CLAUSE_22 BIT(12)
+#define SMI_CMD_CLAUSE_22_OP_READ (2 << 10)
+#define SMI_CMD_CLAUSE_22_OP_WRITE (1 << 10)
+
+#define SMI_CMD_READ (SMI_BUSY | SMI_CMD_CLAUSE_22 | \
+ SMI_CMD_CLAUSE_22_OP_READ)
+#define SMI_CMD_WRITE (SMI_BUSY | SMI_CMD_CLAUSE_22 | \
+ SMI_CMD_CLAUSE_22_OP_WRITE)
+
+#define SMI_CMD_ADDR_SHIFT 5
+#define SMI_CMD_ADDR_WIDTH 5
+#define SMI_CMD_REG_SHIFT 0
+#define SMI_CMD_REG_WIDTH 5
+
+/* Check for required macros */
+#ifndef CONFIG_MV88E61XX_PHY_PORTS
+#error Define CONFIG_MV88E61XX_PHY_PORTS to indicate which physical ports \
+ to activate
+#endif
+#ifndef CONFIG_MV88E61XX_CPU_PORT
+#error Define CONFIG_MV88E61XX_CPU_PORT to the port the CPU is attached to
+#endif
+
+/*
+ * These are ports without PHYs that may be wired directly
+ * to other serdes interfaces
+ */
+#ifndef CONFIG_MV88E61XX_FIXED_PORTS
+#define CONFIG_MV88E61XX_FIXED_PORTS 0
+#endif
+
+/* ID register values for different switch models */
+#define PORT_SWITCH_ID_6020 0x0200
+#define PORT_SWITCH_ID_6070 0x0700
+#define PORT_SWITCH_ID_6071 0x0710
+#define PORT_SWITCH_ID_6096 0x0980
+#define PORT_SWITCH_ID_6097 0x0990
+#define PORT_SWITCH_ID_6172 0x1720
+#define PORT_SWITCH_ID_6176 0x1760
+#define PORT_SWITCH_ID_6220 0x2200
+#define PORT_SWITCH_ID_6240 0x2400
+#define PORT_SWITCH_ID_6250 0x2500
+#define PORT_SWITCH_ID_6352 0x3520
+
+struct mv88e61xx_phy_priv {
+ struct mii_dev *mdio_bus;
+ int smi_addr;
+ int id;
+ int port_count; /* Number of switch ports */
+ int port_reg_base; /* Base of the switch port registers */
+ u16 port_stat_link_mask;/* Bitmask for port link status bits */
+ u16 port_stat_dup_mask; /* Bitmask for port duplex status bits */
+ u8 port_stat_speed_width;/* Width of speed status bitfield */
+ u8 global1; /* Offset of Switch Global 1 registers */
+ u8 global2; /* Offset of Switch Global 2 registers */
+ u8 phy_ctrl1_en_det_shift; /* 'EDet' bit field offset */
+ u8 phy_ctrl1_en_det_width; /* Width of 'EDet' bit field */
+ u8 phy_ctrl1_en_det_ctrl; /* 'EDet' control value */
+};
+
+static inline int smi_cmd(int cmd, int addr, int reg)
+{
+ cmd = bitfield_replace(cmd, SMI_CMD_ADDR_SHIFT, SMI_CMD_ADDR_WIDTH,
+ addr);
+ cmd = bitfield_replace(cmd, SMI_CMD_REG_SHIFT, SMI_CMD_REG_WIDTH, reg);
+ return cmd;
+}
+
+static inline int smi_cmd_read(int addr, int reg)
+{
+ return smi_cmd(SMI_CMD_READ, addr, reg);
+}
+
+static inline int smi_cmd_write(int addr, int reg)
+{
+ return smi_cmd(SMI_CMD_WRITE, addr, reg);
+}
+
+__weak int mv88e61xx_hw_reset(struct phy_device *phydev)
+{
+ return 0;
+}
+
+/* Wait for the current SMI indirect command to complete */
+static int mv88e61xx_smi_wait(struct mii_dev *bus, int smi_addr)
+{
+ int val;
+ u32 timeout = 100;
+
+ do {
+ val = bus->read(bus, smi_addr, MDIO_DEVAD_NONE, SMI_CMD_REG);
+ if (val >= 0 && (val & SMI_BUSY) == 0)
+ return 0;
+
+ mdelay(1);
+ } while (--timeout);
+
+ puts("SMI busy timeout\n");
+ return -ETIMEDOUT;
+}
+
+/*
+ * The mv88e61xx has three types of addresses: the smi bus address, the device
+ * address, and the register address. The smi bus address distinguishes it on
+ * the smi bus from other PHYs or switches. The device address determines
+ * which on-chip register set you are reading/writing (the various PHYs, their
+ * associated ports, or global configuration registers). The register address
+ * is the offset of the register you are reading/writing.
+ *
+ * When the mv88e61xx is hardware configured to have address zero, it behaves in
+ * single-chip addressing mode, where it responds to all SMI addresses, using
+ * the smi address as its device address. This obviously only works when this
+ * is the only chip on the SMI bus. This allows the driver to access device
+ * registers without using indirection. When the chip is configured to a
+ * non-zero address, it only responds to that SMI address and requires indirect
+ * writes to access the different device addresses.
+ */
+static int mv88e61xx_reg_read(struct phy_device *phydev, int dev, int reg)
+{
+ struct mv88e61xx_phy_priv *priv = phydev->priv;
+ struct mii_dev *mdio_bus = priv->mdio_bus;
+ int smi_addr = priv->smi_addr;
+ int res;
+
+ /* In single-chip mode, the device can be addressed directly */
+ if (smi_addr == 0)
+ return mdio_bus->read(mdio_bus, dev, MDIO_DEVAD_NONE, reg);
+
+ /* Wait for the bus to become free */
+ res = mv88e61xx_smi_wait(mdio_bus, smi_addr);
+ if (res < 0)
+ return res;
+
+ /* Issue the read command */
+ res = mdio_bus->write(mdio_bus, smi_addr, MDIO_DEVAD_NONE, SMI_CMD_REG,
+ smi_cmd_read(dev, reg));
+ if (res < 0)
+ return res;
+
+ /* Wait for the read command to complete */
+ res = mv88e61xx_smi_wait(mdio_bus, smi_addr);
+ if (res < 0)
+ return res;
+
+ /* Read the data */
+ res = mdio_bus->read(mdio_bus, smi_addr, MDIO_DEVAD_NONE, SMI_DATA_REG);
+ if (res < 0)
+ return res;
+
+ return bitfield_extract(res, 0, 16);
+}
+
+/* See the comment above mv88e61xx_reg_read */
+static int mv88e61xx_reg_write(struct phy_device *phydev, int dev, int reg,
+ u16 val)
+{
+ struct mv88e61xx_phy_priv *priv = phydev->priv;
+ struct mii_dev *mdio_bus = priv->mdio_bus;
+ int smi_addr = priv->smi_addr;
+ int res;
+
+ /* In single-chip mode, the device can be addressed directly */
+ if (smi_addr == 0) {
+ return mdio_bus->write(mdio_bus, dev, MDIO_DEVAD_NONE, reg,
+ val);
+ }
+
+ /* Wait for the bus to become free */
+ res = mv88e61xx_smi_wait(mdio_bus, smi_addr);
+ if (res < 0)
+ return res;
+
+ /* Set the data to write */
+ res = mdio_bus->write(mdio_bus, smi_addr, MDIO_DEVAD_NONE,
+ SMI_DATA_REG, val);
+ if (res < 0)
+ return res;
+
+ /* Issue the write command */
+ res = mdio_bus->write(mdio_bus, smi_addr, MDIO_DEVAD_NONE, SMI_CMD_REG,
+ smi_cmd_write(dev, reg));
+ if (res < 0)
+ return res;
+
+ /* Wait for the write command to complete */
+ res = mv88e61xx_smi_wait(mdio_bus, smi_addr);
+ if (res < 0)
+ return res;
+
+ return 0;
+}
+
+static int mv88e61xx_phy_wait(struct phy_device *phydev)
+{
+ struct mv88e61xx_phy_priv *priv = phydev->priv;
+ int val;
+ u32 timeout = 100;
+
+ do {
+ val = mv88e61xx_reg_read(phydev, priv->global2,
+ GLOBAL2_REG_PHY_CMD);
+ if (val >= 0 && (val & SMI_BUSY) == 0)
+ return 0;
+
+ mdelay(1);
+ } while (--timeout);
+
+ return -ETIMEDOUT;
+}
+
+static int mv88e61xx_phy_read_indirect(struct mii_dev *smi_wrapper, int dev,
+ int devad, int reg)
+{
+ struct mv88e61xx_phy_priv *priv;
+ struct phy_device *phydev;
+ int res;
+
+ phydev = (struct phy_device *)smi_wrapper->priv;
+ priv = phydev->priv;
+
+ /* Issue command to read */
+ res = mv88e61xx_reg_write(phydev, priv->global2,
+ GLOBAL2_REG_PHY_CMD,
+ smi_cmd_read(dev, reg));
+
+ /* Wait for data to be read */
+ res = mv88e61xx_phy_wait(phydev);
+ if (res < 0)
+ return res;
+
+ /* Read retrieved data */
+ return mv88e61xx_reg_read(phydev, priv->global2,
+ GLOBAL2_REG_PHY_DATA);
+}
+
+static int mv88e61xx_phy_write_indirect(struct mii_dev *smi_wrapper, int dev,
+ int devad, int reg, u16 data)
+{
+ struct mv88e61xx_phy_priv *priv;
+ struct phy_device *phydev;
+ int res;
+
+ phydev = (struct phy_device *)smi_wrapper->priv;
+ priv = phydev->priv;
+
+ /* Set the data to write */
+ res = mv88e61xx_reg_write(phydev, priv->global2,
+ GLOBAL2_REG_PHY_DATA, data);
+ if (res < 0)
+ return res;
+ /* Issue the write command */
+ res = mv88e61xx_reg_write(phydev, priv->global2,
+ GLOBAL2_REG_PHY_CMD,
+ smi_cmd_write(dev, reg));
+ if (res < 0)
+ return res;
+
+ /* Wait for command to complete */
+ return mv88e61xx_phy_wait(phydev);
+}
+
+/* Wrapper function to make calls to phy_read_indirect simpler */
+static int mv88e61xx_phy_read(struct phy_device *phydev, int phy, int reg)
+{
+ return mv88e61xx_phy_read_indirect(phydev->bus, DEVADDR_PHY(phy),
+ MDIO_DEVAD_NONE, reg);
+}
+
+/* Wrapper function to make calls to phy_read_indirect simpler */
+static int mv88e61xx_phy_write(struct phy_device *phydev, int phy,
+ int reg, u16 val)
+{
+ return mv88e61xx_phy_write_indirect(phydev->bus, DEVADDR_PHY(phy),
+ MDIO_DEVAD_NONE, reg, val);
+}
+
+static int mv88e61xx_port_read(struct phy_device *phydev, u8 port, u8 reg)
+{
+ struct mv88e61xx_phy_priv *priv = phydev->priv;
+
+ return mv88e61xx_reg_read(phydev, priv->port_reg_base + port, reg);
+}
+
+static int mv88e61xx_port_write(struct phy_device *phydev, u8 port, u8 reg,
+ u16 val)
+{
+ struct mv88e61xx_phy_priv *priv = phydev->priv;
+
+ return mv88e61xx_reg_write(phydev, priv->port_reg_base + port,
+ reg, val);
+}
+
+static int mv88e61xx_set_page(struct phy_device *phydev, u8 phy, u8 page)
+{
+ return mv88e61xx_phy_write(phydev, phy, PHY_REG_PAGE, page);
+}
+
+static int mv88e61xx_get_switch_id(struct phy_device *phydev)
+{
+ int res;
+
+ res = mv88e61xx_port_read(phydev, 0, PORT_REG_SWITCH_ID);
+ if (res < 0)
+ return res;
+ return res & 0xfff0;
+}
+
+static bool mv88e61xx_6352_family(struct phy_device *phydev)
+{
+ struct mv88e61xx_phy_priv *priv = phydev->priv;
+
+ switch (priv->id) {
+ case PORT_SWITCH_ID_6172:
+ case PORT_SWITCH_ID_6176:
+ case PORT_SWITCH_ID_6240:
+ case PORT_SWITCH_ID_6352:
+ return true;
+ }
+ return false;
+}
+
+static int mv88e61xx_get_cmode(struct phy_device *phydev, u8 port)
+{
+ int res;
+
+ res = mv88e61xx_port_read(phydev, port, PORT_REG_STATUS);
+ if (res < 0)
+ return res;
+ return res & PORT_REG_STATUS_CMODE_MASK;
+}
+
+static int mv88e61xx_parse_status(struct phy_device *phydev)
+{
+ unsigned int speed;
+ unsigned int mii_reg;
+
+ mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, PHY_REG_STATUS1);
+
+ if ((mii_reg & PHY_REG_STATUS1_LINK) &&
+ !(mii_reg & PHY_REG_STATUS1_SPDDONE)) {
+ int i = 0;
+
+ puts("Waiting for PHY realtime link");
+ while (!(mii_reg & PHY_REG_STATUS1_SPDDONE)) {
+ /* Timeout reached ? */
+ if (i > PHY_AUTONEGOTIATE_TIMEOUT) {
+ puts(" TIMEOUT !\n");
+ phydev->link = 0;
+ break;
+ }
+
+ if ((i++ % 1000) == 0)
+ putc('.');
+ udelay(1000);
+ mii_reg = phy_read(phydev, MDIO_DEVAD_NONE,
+ PHY_REG_STATUS1);
+ }
+ puts(" done\n");
+ udelay(500000); /* another 500 ms (results in faster booting) */
+ } else {
+ if (mii_reg & PHY_REG_STATUS1_LINK)
+ phydev->link = 1;
+ else
+ phydev->link = 0;
+ }
+
+ if (mii_reg & PHY_REG_STATUS1_DUPLEX)
+ phydev->duplex = DUPLEX_FULL;
+ else
+ phydev->duplex = DUPLEX_HALF;
+
+ speed = mii_reg & PHY_REG_STATUS1_SPEED;
+
+ switch (speed) {
+ case PHY_REG_STATUS1_GBIT:
+ phydev->speed = SPEED_1000;
+ break;
+ case PHY_REG_STATUS1_100:
+ phydev->speed = SPEED_100;
+ break;
+ default:
+ phydev->speed = SPEED_10;
+ break;
+ }
+
+ return 0;
+}
+
+static int mv88e61xx_switch_reset(struct phy_device *phydev)
+{
+ struct mv88e61xx_phy_priv *priv = phydev->priv;
+ int time;
+ int val;
+ u8 port;
+
+ /* Disable all ports */
+ for (port = 0; port < priv->port_count; port++) {
+ val = mv88e61xx_port_read(phydev, port, PORT_REG_CTRL);
+ if (val < 0)
+ return val;
+ val = bitfield_replace(val, PORT_REG_CTRL_PSTATE_SHIFT,
+ PORT_REG_CTRL_PSTATE_WIDTH,
+ PORT_REG_CTRL_PSTATE_DISABLED);
+ val = mv88e61xx_port_write(phydev, port, PORT_REG_CTRL, val);
+ if (val < 0)
+ return val;
+ }
+
+ /* Wait 2 ms for queues to drain */
+ udelay(2000);
+
+ /* Reset switch */
+ val = mv88e61xx_reg_read(phydev, priv->global1, GLOBAL1_CTRL);
+ if (val < 0)
+ return val;
+ val |= GLOBAL1_CTRL_SWRESET;
+ val = mv88e61xx_reg_write(phydev, priv->global1,
+ GLOBAL1_CTRL, val);
+ if (val < 0)
+ return val;
+
+ /* Wait up to 1 second for switch reset complete */
+ for (time = 1000; time; time--) {
+ val = mv88e61xx_reg_read(phydev, priv->global1,
+ GLOBAL1_CTRL);
+ if (val >= 0 && ((val & GLOBAL1_CTRL_SWRESET) == 0))
+ break;
+ udelay(1000);
+ }
+ if (!time)
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+static int mv88e61xx_serdes_init(struct phy_device *phydev)
+{
+ int val;
+
+ val = mv88e61xx_set_page(phydev, DEVADDR_SERDES, PHY_PAGE_SERDES);
+ if (val < 0)
+ return val;
+
+ /* Power up serdes module */
+ val = mv88e61xx_phy_read(phydev, DEVADDR_SERDES, MII_BMCR);
+ if (val < 0)
+ return val;
+ val &= ~(BMCR_PDOWN);
+ val = mv88e61xx_phy_write(phydev, DEVADDR_SERDES, MII_BMCR, val);
+ if (val < 0)
+ return val;
+
+ return 0;
+}
+
+static int mv88e61xx_port_enable(struct phy_device *phydev, u8 port)
+{
+ int val;
+
+ val = mv88e61xx_port_read(phydev, port, PORT_REG_CTRL);
+ if (val < 0)
+ return val;
+ val = bitfield_replace(val, PORT_REG_CTRL_PSTATE_SHIFT,
+ PORT_REG_CTRL_PSTATE_WIDTH,
+ PORT_REG_CTRL_PSTATE_FORWARD);
+ val = mv88e61xx_port_write(phydev, port, PORT_REG_CTRL, val);
+ if (val < 0)
+ return val;
+
+ return 0;
+}
+
+static int mv88e61xx_port_set_vlan(struct phy_device *phydev, u8 port,
+ u16 mask)
+{
+ int val;
+
+ /* Set VID to port number plus one */
+ val = mv88e61xx_port_read(phydev, port, PORT_REG_VLAN_ID);
+ if (val < 0)
+ return val;
+ val = bitfield_replace(val, PORT_REG_VLAN_ID_DEF_VID_SHIFT,
+ PORT_REG_VLAN_ID_DEF_VID_WIDTH,
+ port + 1);
+ val = mv88e61xx_port_write(phydev, port, PORT_REG_VLAN_ID, val);
+ if (val < 0)
+ return val;
+
+ /* Set VID mask */
+ val = mv88e61xx_port_read(phydev, port, PORT_REG_VLAN_MAP);
+ if (val < 0)
+ return val;
+ val = bitfield_replace(val, PORT_REG_VLAN_MAP_TABLE_SHIFT,
+ PORT_REG_VLAN_MAP_TABLE_WIDTH,
+ mask);
+ val = mv88e61xx_port_write(phydev, port, PORT_REG_VLAN_MAP, val);
+ if (val < 0)
+ return val;
+
+ return 0;
+}
+
+static int mv88e61xx_read_port_config(struct phy_device *phydev, u8 port)
+{
+ struct mv88e61xx_phy_priv *priv = phydev->priv;
+ int res;
+ int val;
+ bool forced = false;
+
+ val = mv88e61xx_port_read(phydev, port, PORT_REG_STATUS);
+ if (val < 0)
+ return val;
+ if (!(val & priv->port_stat_link_mask)) {
+ /* Temporarily force link to read port configuration */
+ u32 timeout = 100;
+ forced = true;
+
+ val = mv88e61xx_port_read(phydev, port, PORT_REG_PHYS_CTRL);
+ if (val < 0)
+ return val;
+ val |= (PORT_REG_PHYS_CTRL_LINK_FORCE |
+ PORT_REG_PHYS_CTRL_LINK_VALUE);
+ val = mv88e61xx_port_write(phydev, port, PORT_REG_PHYS_CTRL,
+ val);
+ if (val < 0)
+ return val;
+
+ /* Wait for status register to reflect forced link */
+ do {
+ val = mv88e61xx_port_read(phydev, port,
+ PORT_REG_STATUS);
+ if (val < 0) {
+ res = -EIO;
+ goto unforce;
+ }
+ if (val & priv->port_stat_link_mask)
+ break;
+ } while (--timeout);
+
+ if (timeout == 0) {
+ res = -ETIMEDOUT;
+ goto unforce;
+ }
+ }
+
+ if (val & priv->port_stat_dup_mask)
+ phydev->duplex = DUPLEX_FULL;
+ else
+ phydev->duplex = DUPLEX_HALF;
+
+ val = bitfield_extract(val, PORT_REG_STATUS_SPEED_SHIFT,
+ priv->port_stat_speed_width);
+ switch (val) {
+ case PORT_REG_STATUS_SPEED_1000:
+ phydev->speed = SPEED_1000;
+ break;
+ case PORT_REG_STATUS_SPEED_100:
+ phydev->speed = SPEED_100;
+ break;
+ default:
+ phydev->speed = SPEED_10;
+ break;
+ }
+
+ res = 0;
+
+unforce:
+ if (forced) {
+ val = mv88e61xx_port_read(phydev, port, PORT_REG_PHYS_CTRL);
+ if (val < 0)
+ return val;
+ val &= ~(PORT_REG_PHYS_CTRL_LINK_FORCE |
+ PORT_REG_PHYS_CTRL_LINK_VALUE);
+ val = mv88e61xx_port_write(phydev, port, PORT_REG_PHYS_CTRL,
+ val);
+ if (val < 0)
+ return val;
+ }
+
+ return res;
+}
+
+static int mv88e61xx_fixed_port_setup(struct phy_device *phydev, u8 port)
+{
+ struct mv88e61xx_phy_priv *priv = phydev->priv;
+ int val;
+
+ val = mv88e61xx_port_read(phydev, port, PORT_REG_PHYS_CTRL);
+ if (val < 0)
+ return val;
+
+ val &= ~(PORT_REG_PHYS_CTRL_SPD_MASK |
+ PORT_REG_PHYS_CTRL_FC_VALUE |
+ PORT_REG_PHYS_CTRL_FC_FORCE);
+ val |= PORT_REG_PHYS_CTRL_FC_FORCE |
+ PORT_REG_PHYS_CTRL_DUPLEX_VALUE |
+ PORT_REG_PHYS_CTRL_DUPLEX_FORCE;
+
+ if (priv->id == PORT_SWITCH_ID_6071) {
+ val |= PORT_REG_PHYS_CTRL_SPD100;
+ } else {
+ val |= PORT_REG_PHYS_CTRL_PCS_AN_EN |
+ PORT_REG_PHYS_CTRL_PCS_AN_RST |
+ PORT_REG_PHYS_CTRL_SPD1000;
+ }
+
+ if (port == CONFIG_MV88E61XX_CPU_PORT)
+ val |= PORT_REG_PHYS_CTRL_LINK_VALUE |
+ PORT_REG_PHYS_CTRL_LINK_FORCE;
+
+ return mv88e61xx_port_write(phydev, port, PORT_REG_PHYS_CTRL,
+ val);
+}
+
+static int mv88e61xx_set_cpu_port(struct phy_device *phydev)
+{
+ struct mv88e61xx_phy_priv *priv = phydev->priv;
+ int val;
+
+ /* Set CPUDest */
+ val = mv88e61xx_reg_read(phydev, priv->global1, GLOBAL1_MON_CTRL);
+ if (val < 0)
+ return val;
+ val = bitfield_replace(val, GLOBAL1_MON_CTRL_CPUDEST_SHIFT,
+ GLOBAL1_MON_CTRL_CPUDEST_WIDTH,
+ CONFIG_MV88E61XX_CPU_PORT);
+ val = mv88e61xx_reg_write(phydev, priv->global1,
+ GLOBAL1_MON_CTRL, val);
+ if (val < 0)
+ return val;
+
+ /* Allow CPU to route to any port */
+ val = PORT_MASK(priv->port_count) & ~(1 << CONFIG_MV88E61XX_CPU_PORT);
+ val = mv88e61xx_port_set_vlan(phydev, CONFIG_MV88E61XX_CPU_PORT, val);
+ if (val < 0)
+ return val;
+
+ /* Enable CPU port */
+ val = mv88e61xx_port_enable(phydev, CONFIG_MV88E61XX_CPU_PORT);
+ if (val < 0)
+ return val;
+
+ val = mv88e61xx_read_port_config(phydev, CONFIG_MV88E61XX_CPU_PORT);
+ if (val < 0)
+ return val;
+
+ /* If CPU is connected to serdes, initialize serdes */
+ if (mv88e61xx_6352_family(phydev)) {
+ val = mv88e61xx_get_cmode(phydev, CONFIG_MV88E61XX_CPU_PORT);
+ if (val < 0)
+ return val;
+ if (val == PORT_REG_STATUS_CMODE_100BASE_X ||
+ val == PORT_REG_STATUS_CMODE_1000BASE_X ||
+ val == PORT_REG_STATUS_CMODE_SGMII) {
+ val = mv88e61xx_serdes_init(phydev);
+ if (val < 0)
+ return val;
+ }
+ } else {
+ val = mv88e61xx_fixed_port_setup(phydev,
+ CONFIG_MV88E61XX_CPU_PORT);
+ if (val < 0)
+ return val;
+ }
+
+ return 0;
+}
+
+static int mv88e61xx_switch_init(struct phy_device *phydev)
+{
+ static int init;
+ int res;
+
+ if (init)
+ return 0;
+
+ res = mv88e61xx_switch_reset(phydev);
+ if (res < 0)
+ return res;
+
+ res = mv88e61xx_set_cpu_port(phydev);
+ if (res < 0)
+ return res;
+
+ init = 1;
+
+ return 0;
+}
+
+static int mv88e61xx_phy_enable(struct phy_device *phydev, u8 phy)
+{
+ int val;
+
+ val = mv88e61xx_phy_read(phydev, phy, MII_BMCR);
+ if (val < 0)
+ return val;
+ val &= ~(BMCR_PDOWN);
+ val = mv88e61xx_phy_write(phydev, phy, MII_BMCR, val);
+ if (val < 0)
+ return val;
+
+ return 0;
+}
+
+static int mv88e61xx_phy_setup(struct phy_device *phydev, u8 phy)
+{
+ struct mv88e61xx_phy_priv *priv = phydev->priv;
+ int val;
+
+ /*
+ * Enable energy-detect sensing on PHY, used to determine when a PHY
+ * port is physically connected
+ */
+ val = mv88e61xx_phy_read(phydev, phy, PHY_REG_CTRL1);
+ if (val < 0)
+ return val;
+ val = bitfield_replace(val, priv->phy_ctrl1_en_det_shift,
+ priv->phy_ctrl1_en_det_width,
+ priv->phy_ctrl1_en_det_ctrl);
+ val = mv88e61xx_phy_write(phydev, phy, PHY_REG_CTRL1, val);
+ if (val < 0)
+ return val;
+
+ return 0;
+}
+
+static int mv88e61xx_phy_config_port(struct phy_device *phydev, u8 phy)
+{
+ int val;
+
+ val = mv88e61xx_port_enable(phydev, phy);
+ if (val < 0)
+ return val;
+
+ val = mv88e61xx_port_set_vlan(phydev, phy,
+ 1 << CONFIG_MV88E61XX_CPU_PORT);
+ if (val < 0)
+ return val;
+
+ return 0;
+}
+
+/*
+ * This function is used to pre-configure the required register
+ * offsets, so that the indirect register access to the PHY registers
+ * is possible. This is necessary to be able to read the PHY ID
+ * while driver probing or in get_phy_id(). The globalN register
+ * offsets must be initialized correctly for a detected switch,
+ * otherwise detection of the PHY ID won't work!
+ */
+static int mv88e61xx_priv_reg_offs_pre_init(struct phy_device *phydev)
+{
+ struct mv88e61xx_phy_priv *priv = phydev->priv;
+
+ /*
+ * Initial 'port_reg_base' value must be an offset of existing
+ * port register, then reading the ID should succeed. First, try
+ * to read via port registers with device address 0x10 (88E6096
+ * and compatible switches).
+ */
+ priv->port_reg_base = 0x10;
+ priv->id = mv88e61xx_get_switch_id(phydev);
+ if (priv->id != 0xfff0) {
+ priv->global1 = 0x1B;
+ priv->global2 = 0x1C;
+ return 0;
+ }
+
+ /*
+ * Now try via port registers with device address 0x08
+ * (88E6020 and compatible switches).
+ */
+ priv->port_reg_base = 0x08;
+ priv->id = mv88e61xx_get_switch_id(phydev);
+ if (priv->id != 0xfff0) {
+ priv->global1 = 0x0F;
+ priv->global2 = 0x07;
+ return 0;
+ }
+
+ debug("%s Unknown ID 0x%x\n", __func__, priv->id);
+ return -ENODEV;
+}
+
+static int mv88e61xx_probe(struct phy_device *phydev)
+{
+ struct mii_dev *smi_wrapper;
+ struct mv88e61xx_phy_priv *priv;
+ int res;
+
+ res = mv88e61xx_hw_reset(phydev);
+ if (res < 0)
+ return res;
+
+ priv = malloc(sizeof(*priv));
+ if (!priv)
+ return -ENOMEM;
+
+ memset(priv, 0, sizeof(*priv));
+
+ /*
+ * This device requires indirect reads/writes to the PHY registers
+ * which the generic PHY code can't handle. Make a wrapper MII device
+ * to handle reads/writes
+ */
+ smi_wrapper = mdio_alloc();
+ if (!smi_wrapper) {
+ free(priv);
+ return -ENOMEM;
+ }
+
+ /*
+ * Store the mdio bus in the private data, as we are going to replace
+ * the bus with the wrapper bus
+ */
+ priv->mdio_bus = phydev->bus;
+
+ /*
+ * Store the smi bus address in private data. This lets us use the
+ * phydev addr field for device address instead, as the genphy code
+ * expects.
+ */
+ priv->smi_addr = phydev->addr;
+
+ /*
+ * Store the phy_device in the wrapper mii device. This lets us get it
+ * back when genphy functions call phy_read/phy_write.
+ */
+ smi_wrapper->priv = phydev;
+ strncpy(smi_wrapper->name, "indirect mii", sizeof(smi_wrapper->name));
+ smi_wrapper->read = mv88e61xx_phy_read_indirect;
+ smi_wrapper->write = mv88e61xx_phy_write_indirect;
+
+ /* Replace the bus with the wrapper device */
+ phydev->bus = smi_wrapper;
+
+ phydev->priv = priv;
+
+ res = mv88e61xx_priv_reg_offs_pre_init(phydev);
+ if (res < 0)
+ return res;
+
+ debug("%s ID 0x%x\n", __func__, priv->id);
+
+ switch (priv->id) {
+ case PORT_SWITCH_ID_6096:
+ case PORT_SWITCH_ID_6097:
+ case PORT_SWITCH_ID_6172:
+ case PORT_SWITCH_ID_6176:
+ case PORT_SWITCH_ID_6240:
+ case PORT_SWITCH_ID_6352:
+ priv->port_count = 11;
+ priv->port_stat_link_mask = BIT(11);
+ priv->port_stat_dup_mask = BIT(10);
+ priv->port_stat_speed_width = 2;
+ priv->phy_ctrl1_en_det_shift = 8;
+ priv->phy_ctrl1_en_det_width = 2;
+ priv->phy_ctrl1_en_det_ctrl =
+ PHY_REG_CTRL1_ENERGY_DET_SENSE_XMIT;
+ break;
+ case PORT_SWITCH_ID_6020:
+ case PORT_SWITCH_ID_6070:
+ case PORT_SWITCH_ID_6071:
+ case PORT_SWITCH_ID_6220:
+ case PORT_SWITCH_ID_6250:
+ priv->port_count = 7;
+ priv->port_stat_link_mask = BIT(12);
+ priv->port_stat_dup_mask = BIT(9);
+ priv->port_stat_speed_width = 1;
+ priv->phy_ctrl1_en_det_shift = 14;
+ priv->phy_ctrl1_en_det_width = 1;
+ priv->phy_ctrl1_en_det_ctrl =
+ PHY_REG_CTRL1_ENERGY_DET_SENSE_PULSE;
+ break;
+ default:
+ free(priv);
+ return -ENODEV;
+ }
+
+ res = mdio_register(smi_wrapper);
+ if (res)
+ printf("Failed to register SMI bus\n");
+
+ return 0;
+}
+
+static int mv88e61xx_phy_config(struct phy_device *phydev)
+{
+ struct mv88e61xx_phy_priv *priv = phydev->priv;
+ int res;
+ int i;
+ int ret = -1;
+
+ res = mv88e61xx_switch_init(phydev);
+ if (res < 0)
+ return res;
+
+ for (i = 0; i < priv->port_count; i++) {
+ if ((1 << i) & CONFIG_MV88E61XX_PHY_PORTS) {
+ phydev->addr = i;
+
+ res = mv88e61xx_phy_enable(phydev, i);
+ if (res < 0) {
+ printf("Error enabling PHY %i\n", i);
+ continue;
+ }
+ res = mv88e61xx_phy_setup(phydev, i);
+ if (res < 0) {
+ printf("Error setting up PHY %i\n", i);
+ continue;
+ }
+ res = mv88e61xx_phy_config_port(phydev, i);
+ if (res < 0) {
+ printf("Error configuring PHY %i\n", i);
+ continue;
+ }
+
+ res = phy_reset(phydev);
+ if (res < 0) {
+ printf("Error resetting PHY %i\n", i);
+ continue;
+ }
+ res = genphy_config_aneg(phydev);
+ if (res < 0) {
+ printf("Error setting PHY %i autoneg\n", i);
+ continue;
+ }
+
+ /* Return success if any PHY succeeds */
+ ret = 0;
+ } else if ((1 << i) & CONFIG_MV88E61XX_FIXED_PORTS) {
+ res = mv88e61xx_fixed_port_setup(phydev, i);
+ if (res < 0) {
+ printf("Error configuring port %i\n", i);
+ continue;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static int mv88e61xx_phy_is_connected(struct phy_device *phydev)
+{
+ int val;
+
+ val = mv88e61xx_phy_read(phydev, phydev->addr, PHY_REG_STATUS1);
+ if (val < 0)
+ return 0;
+
+ /*
+ * After reset, the energy detect signal remains high for a few seconds
+ * regardless of whether a cable is connected. This function will
+ * return false positives during this time.
+ */
+ return (val & PHY_REG_STATUS1_ENERGY) == 0;
+}
+
+static int mv88e61xx_phy_startup(struct phy_device *phydev)
+{
+ struct mv88e61xx_phy_priv *priv = phydev->priv;
+ int i;
+ int link = 0;
+ int res;
+ int speed = phydev->speed;
+ int duplex = phydev->duplex;
+
+ for (i = 0; i < priv->port_count; i++) {
+ if ((1 << i) & CONFIG_MV88E61XX_PHY_PORTS) {
+ phydev->addr = i;
+ if (!mv88e61xx_phy_is_connected(phydev))
+ continue;
+ res = genphy_update_link(phydev);
+ if (res < 0)
+ continue;
+ res = mv88e61xx_parse_status(phydev);
+ if (res < 0)
+ continue;
+ link = (link || phydev->link);
+ }
+ }
+ phydev->link = link;
+
+ /* Restore CPU interface speed and duplex after it was changed for
+ * other ports */
+ phydev->speed = speed;
+ phydev->duplex = duplex;
+
+ return 0;
+}
+
+static struct phy_driver mv88e61xx_driver = {
+ .name = "Marvell MV88E61xx",
+ .uid = 0x01410eb1,
+ .mask = 0xfffffff0,
+ .features = PHY_GBIT_FEATURES,
+ .probe = mv88e61xx_probe,
+ .config = mv88e61xx_phy_config,
+ .startup = mv88e61xx_phy_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+static struct phy_driver mv88e609x_driver = {
+ .name = "Marvell MV88E609x",
+ .uid = 0x1410c89,
+ .mask = 0xfffffff0,
+ .features = PHY_GBIT_FEATURES,
+ .probe = mv88e61xx_probe,
+ .config = mv88e61xx_phy_config,
+ .startup = mv88e61xx_phy_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+static struct phy_driver mv88e6071_driver = {
+ .name = "Marvell MV88E6071",
+ .uid = 0x1410db0,
+ .mask = 0xfffffff0,
+ .features = PHY_BASIC_FEATURES | SUPPORTED_MII,
+ .probe = mv88e61xx_probe,
+ .config = mv88e61xx_phy_config,
+ .startup = mv88e61xx_phy_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+int phy_mv88e61xx_init(void)
+{
+ phy_register(&mv88e61xx_driver);
+ phy_register(&mv88e609x_driver);
+ phy_register(&mv88e6071_driver);
+
+ return 0;
+}
+
+/*
+ * Overload weak get_phy_id definition since we need non-standard functions
+ * to read PHY registers
+ */
+int get_phy_id(struct mii_dev *bus, int smi_addr, int devad, u32 *phy_id)
+{
+ struct phy_device temp_phy;
+ struct mv88e61xx_phy_priv temp_priv;
+ struct mii_dev temp_mii;
+ int val;
+
+ /*
+ * Buid temporary data structures that the chip reading code needs to
+ * read the ID
+ */
+ temp_priv.mdio_bus = bus;
+ temp_priv.smi_addr = smi_addr;
+ temp_phy.priv = &temp_priv;
+ temp_mii.priv = &temp_phy;
+
+ /*
+ * get_phy_id() can be called by framework before mv88e61xx driver
+ * probing, in this case the global register offsets are not
+ * initialized yet. Do this initialization here before indirect
+ * PHY register access.
+ */
+ val = mv88e61xx_priv_reg_offs_pre_init(&temp_phy);
+ if (val < 0)
+ return val;
+
+ val = mv88e61xx_phy_read_indirect(&temp_mii, 0, devad, MII_PHYSID1);
+ if (val < 0)
+ return -EIO;
+
+ *phy_id = val << 16;
+
+ val = mv88e61xx_phy_read_indirect(&temp_mii, 0, devad, MII_PHYSID2);
+ if (val < 0)
+ return -EIO;
+
+ *phy_id |= (val & 0xffff);
+
+ return 0;
+}
diff --git a/roms/u-boot/drivers/net/phy/mv88e6352.c b/roms/u-boot/drivers/net/phy/mv88e6352.c
new file mode 100644
index 000000000..62a7f1921
--- /dev/null
+++ b/roms/u-boot/drivers/net/phy/mv88e6352.c
@@ -0,0 +1,304 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2012
+ * Valentin Lontgchamp, Keymile AG, valentin.longchamp@keymile.com
+ */
+
+#include <common.h>
+#include <command.h>
+#include <log.h>
+#include <miiphy.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <mv88e6352.h>
+
+#define SMI_HDR ((0x8 | 0x1) << 12)
+#define SMI_BUSY_MASK (0x8000)
+#define SMIRD_OP (0x2 << 10)
+#define SMIWR_OP (0x1 << 10)
+#define SMI_MASK 0x1f
+#define PORT_SHIFT 5
+
+#define COMMAND_REG 0
+#define DATA_REG 1
+
+/* global registers */
+#define GLOBAL 0x1b
+
+#define GLOBAL_STATUS 0x00
+#define PPU_STATE 0x8000
+
+#define GLOBAL_CTRL 0x04
+#define SW_RESET 0x8000
+#define PPU_ENABLE 0x4000
+
+static int sw_wait_rdy(const char *devname, u8 phy_addr)
+{
+ u16 command;
+ u32 timeout = 100;
+ int ret;
+
+ /* wait till the SMI is not busy */
+ do {
+ /* read command register */
+ ret = miiphy_read(devname, phy_addr, COMMAND_REG, &command);
+ if (ret < 0) {
+ printf("%s: Error reading command register\n",
+ __func__);
+ return ret;
+ }
+ if (timeout-- == 0) {
+ printf("Err..(%s) SMI busy timeout\n", __func__);
+ return -EFAULT;
+ }
+ } while (command & SMI_BUSY_MASK);
+
+ return 0;
+}
+
+static int sw_reg_read(const char *devname, u8 phy_addr, u8 port,
+ u8 reg, u16 *data)
+{
+ int ret;
+ u16 command;
+
+ ret = sw_wait_rdy(devname, phy_addr);
+ if (ret)
+ return ret;
+
+ command = SMI_HDR | SMIRD_OP | ((port&SMI_MASK) << PORT_SHIFT) |
+ (reg & SMI_MASK);
+ debug("%s: write to command: %#x\n", __func__, command);
+ ret = miiphy_write(devname, phy_addr, COMMAND_REG, command);
+ if (ret)
+ return ret;
+
+ ret = sw_wait_rdy(devname, phy_addr);
+ if (ret)
+ return ret;
+
+ ret = miiphy_read(devname, phy_addr, DATA_REG, data);
+
+ return ret;
+}
+
+static int sw_reg_write(const char *devname, u8 phy_addr, u8 port,
+ u8 reg, u16 data)
+{
+ int ret;
+ u16 value;
+
+ ret = sw_wait_rdy(devname, phy_addr);
+ if (ret)
+ return ret;
+
+ debug("%s: write to data: %#x\n", __func__, data);
+ ret = miiphy_write(devname, phy_addr, DATA_REG, data);
+ if (ret)
+ return ret;
+
+ value = SMI_HDR | SMIWR_OP | ((port & SMI_MASK) << PORT_SHIFT) |
+ (reg & SMI_MASK);
+ debug("%s: write to command: %#x\n", __func__, value);
+ ret = miiphy_write(devname, phy_addr, COMMAND_REG, value);
+ if (ret)
+ return ret;
+
+ ret = sw_wait_rdy(devname, phy_addr);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int ppu_enable(const char *devname, u8 phy_addr)
+{
+ int i, ret = 0;
+ u16 reg;
+
+ ret = sw_reg_read(devname, phy_addr, GLOBAL, GLOBAL_CTRL, &reg);
+ if (ret) {
+ printf("%s: Error reading global ctrl reg\n", __func__);
+ return ret;
+ }
+
+ reg |= PPU_ENABLE;
+
+ ret = sw_reg_write(devname, phy_addr, GLOBAL, GLOBAL_CTRL, reg);
+ if (ret) {
+ printf("%s: Error writing global ctrl reg\n", __func__);
+ return ret;
+ }
+
+ for (i = 0; i < 1000; i++) {
+ sw_reg_read(devname, phy_addr, GLOBAL, GLOBAL_STATUS,
+ &reg);
+ if ((reg & 0xc000) == 0xc000)
+ return 0;
+ udelay(1000);
+ }
+
+ return -ETIMEDOUT;
+}
+
+static int ppu_disable(const char *devname, u8 phy_addr)
+{
+ int i, ret = 0;
+ u16 reg;
+
+ ret = sw_reg_read(devname, phy_addr, GLOBAL, GLOBAL_CTRL, &reg);
+ if (ret) {
+ printf("%s: Error reading global ctrl reg\n", __func__);
+ return ret;
+ }
+
+ reg &= ~PPU_ENABLE;
+
+ ret = sw_reg_write(devname, phy_addr, GLOBAL, GLOBAL_CTRL, reg);
+ if (ret) {
+ printf("%s: Error writing global ctrl reg\n", __func__);
+ return ret;
+ }
+
+ for (i = 0; i < 1000; i++) {
+ sw_reg_read(devname, phy_addr, GLOBAL, GLOBAL_STATUS,
+ &reg);
+ if ((reg & 0xc000) != 0xc000)
+ return 0;
+ udelay(1000);
+ }
+
+ return -ETIMEDOUT;
+}
+
+int mv88e_sw_program(const char *devname, u8 phy_addr,
+ struct mv88e_sw_reg *regs, int regs_nb)
+{
+ int i, ret = 0;
+
+ /* first we need to disable the PPU */
+ ret = ppu_disable(devname, phy_addr);
+ if (ret) {
+ printf("%s: Error disabling PPU\n", __func__);
+ return ret;
+ }
+
+ for (i = 0; i < regs_nb; i++) {
+ ret = sw_reg_write(devname, phy_addr, regs[i].port,
+ regs[i].reg, regs[i].value);
+ if (ret) {
+ printf("%s: Error configuring switch\n", __func__);
+ ppu_enable(devname, phy_addr);
+ return ret;
+ }
+ }
+
+ /* re-enable the PPU */
+ ret = ppu_enable(devname, phy_addr);
+ if (ret) {
+ printf("%s: Error enabling PPU\n", __func__);
+ return ret;
+ }
+
+ return 0;
+}
+
+int mv88e_sw_reset(const char *devname, u8 phy_addr)
+{
+ int i, ret = 0;
+ u16 reg;
+
+ ret = sw_reg_read(devname, phy_addr, GLOBAL, GLOBAL_CTRL, &reg);
+ if (ret) {
+ printf("%s: Error reading global ctrl reg\n", __func__);
+ return ret;
+ }
+
+ reg = SW_RESET | PPU_ENABLE | 0x0400;
+
+ ret = sw_reg_write(devname, phy_addr, GLOBAL, GLOBAL_CTRL, reg);
+ if (ret) {
+ printf("%s: Error writing global ctrl reg\n", __func__);
+ return ret;
+ }
+
+ for (i = 0; i < 1000; i++) {
+ sw_reg_read(devname, phy_addr, GLOBAL, GLOBAL_STATUS,
+ &reg);
+ if ((reg & 0xc800) != 0xc800)
+ return 0;
+ udelay(1000);
+ }
+
+ return -ETIMEDOUT;
+}
+
+int do_mvsw_reg_read(const char *name, int argc, char *const argv[])
+{
+ u16 value = 0, phyaddr, reg, port;
+ int ret;
+
+ phyaddr = simple_strtoul(argv[1], NULL, 10);
+ port = simple_strtoul(argv[2], NULL, 10);
+ reg = simple_strtoul(argv[3], NULL, 10);
+
+ ret = sw_reg_read(name, phyaddr, port, reg, &value);
+ printf("%#x\n", value);
+
+ return ret;
+}
+
+int do_mvsw_reg_write(const char *name, int argc, char *const argv[])
+{
+ u16 value = 0, phyaddr, reg, port;
+ int ret;
+
+ phyaddr = simple_strtoul(argv[1], NULL, 10);
+ port = simple_strtoul(argv[2], NULL, 10);
+ reg = simple_strtoul(argv[3], NULL, 10);
+ value = simple_strtoul(argv[4], NULL, 16);
+
+ ret = sw_reg_write(name, phyaddr, port, reg, value);
+
+ return ret;
+}
+
+
+int do_mvsw_reg(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ int ret;
+ const char *cmd, *ethname;
+
+ if (argc < 2)
+ return cmd_usage(cmdtp);
+
+ cmd = argv[1];
+ --argc;
+ ++argv;
+
+ if (strcmp(cmd, "read") == 0) {
+ if (argc < 5)
+ return cmd_usage(cmdtp);
+ ethname = argv[1];
+ --argc;
+ ++argv;
+ ret = do_mvsw_reg_read(ethname, argc, argv);
+ } else if (strcmp(cmd, "write") == 0) {
+ if (argc < 6)
+ return cmd_usage(cmdtp);
+ ethname = argv[1];
+ --argc;
+ ++argv;
+ ret = do_mvsw_reg_write(ethname, argc, argv);
+ } else
+ return cmd_usage(cmdtp);
+
+ return ret;
+}
+
+U_BOOT_CMD(
+ mvsw_reg, 7, 1, do_mvsw_reg,
+ "marvell 88e6352 switch register access",
+ "write ethname phyaddr port reg value\n"
+ "mvsw_reg read ethname phyaddr port reg\n"
+ );
diff --git a/roms/u-boot/drivers/net/phy/natsemi.c b/roms/u-boot/drivers/net/phy/natsemi.c
new file mode 100644
index 000000000..efde4574d
--- /dev/null
+++ b/roms/u-boot/drivers/net/phy/natsemi.c
@@ -0,0 +1,166 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * National Semiconductor PHY drivers
+ *
+ * Copyright 2010-2011 Freescale Semiconductor, Inc.
+ * author Andy Fleming
+ */
+#include <common.h>
+#include <phy.h>
+
+/* NatSemi DP83630 */
+
+#define DP83630_PHY_PAGESEL_REG 0x13
+#define DP83630_PHY_PTP_COC_REG 0x14
+#define DP83630_PHY_PTP_CLKOUT_EN (1<<15)
+#define DP83630_PHY_RBR_REG 0x17
+
+static int dp83630_config(struct phy_device *phydev)
+{
+ int ptp_coc_reg;
+
+ phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_RESET);
+ phy_write(phydev, MDIO_DEVAD_NONE, DP83630_PHY_PAGESEL_REG, 0x6);
+ ptp_coc_reg = phy_read(phydev, MDIO_DEVAD_NONE,
+ DP83630_PHY_PTP_COC_REG);
+ ptp_coc_reg &= ~DP83630_PHY_PTP_CLKOUT_EN;
+ phy_write(phydev, MDIO_DEVAD_NONE, DP83630_PHY_PTP_COC_REG,
+ ptp_coc_reg);
+ phy_write(phydev, MDIO_DEVAD_NONE, DP83630_PHY_PAGESEL_REG, 0);
+
+ genphy_config_aneg(phydev);
+
+ return 0;
+}
+
+static struct phy_driver DP83630_driver = {
+ .name = "NatSemi DP83630",
+ .uid = 0x20005ce1,
+ .mask = 0xfffffff0,
+ .features = PHY_BASIC_FEATURES,
+ .config = &dp83630_config,
+ .startup = &genphy_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+
+/* DP83865 Link and Auto-Neg Status Register */
+#define MIIM_DP83865_LANR 0x11
+#define MIIM_DP83865_SPD_MASK 0x0018
+#define MIIM_DP83865_SPD_1000 0x0010
+#define MIIM_DP83865_SPD_100 0x0008
+#define MIIM_DP83865_DPX_FULL 0x0002
+
+
+/* NatSemi DP83865 */
+static int dp838xx_config(struct phy_device *phydev)
+{
+ phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_RESET);
+ genphy_config_aneg(phydev);
+
+ return 0;
+}
+
+static int dp83865_parse_status(struct phy_device *phydev)
+{
+ int mii_reg;
+
+ mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_DP83865_LANR);
+
+ switch (mii_reg & MIIM_DP83865_SPD_MASK) {
+
+ case MIIM_DP83865_SPD_1000:
+ phydev->speed = SPEED_1000;
+ break;
+
+ case MIIM_DP83865_SPD_100:
+ phydev->speed = SPEED_100;
+ break;
+
+ default:
+ phydev->speed = SPEED_10;
+ break;
+
+ }
+
+ if (mii_reg & MIIM_DP83865_DPX_FULL)
+ phydev->duplex = DUPLEX_FULL;
+ else
+ phydev->duplex = DUPLEX_HALF;
+
+ return 0;
+}
+
+static int dp83865_startup(struct phy_device *phydev)
+{
+ int ret;
+
+ ret = genphy_update_link(phydev);
+ if (ret)
+ return ret;
+
+ return dp83865_parse_status(phydev);
+}
+
+
+static struct phy_driver DP83865_driver = {
+ .name = "NatSemi DP83865",
+ .uid = 0x20005c70,
+ .mask = 0xfffffff0,
+ .features = PHY_GBIT_FEATURES,
+ .config = &dp838xx_config,
+ .startup = &dp83865_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+/* NatSemi DP83848 */
+static int dp83848_parse_status(struct phy_device *phydev)
+{
+ int mii_reg;
+
+ mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR);
+
+ if(mii_reg & (BMSR_100FULL | BMSR_100HALF)) {
+ phydev->speed = SPEED_100;
+ } else {
+ phydev->speed = SPEED_10;
+ }
+
+ if (mii_reg & (BMSR_10FULL | BMSR_100FULL)) {
+ phydev->duplex = DUPLEX_FULL;
+ } else {
+ phydev->duplex = DUPLEX_HALF;
+ }
+
+ return 0;
+}
+
+static int dp83848_startup(struct phy_device *phydev)
+{
+ int ret;
+
+ ret = genphy_update_link(phydev);
+ if (ret)
+ return ret;
+
+ return dp83848_parse_status(phydev);
+}
+
+static struct phy_driver DP83848_driver = {
+ .name = "NatSemi DP83848",
+ .uid = 0x20005c90,
+ .mask = 0x2000ff90,
+ .features = PHY_BASIC_FEATURES,
+ .config = &dp838xx_config,
+ .startup = &dp83848_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+int phy_natsemi_init(void)
+{
+ phy_register(&DP83630_driver);
+ phy_register(&DP83865_driver);
+ phy_register(&DP83848_driver);
+
+ return 0;
+}
diff --git a/roms/u-boot/drivers/net/phy/ncsi.c b/roms/u-boot/drivers/net/phy/ncsi.c
new file mode 100644
index 000000000..bf1e832be
--- /dev/null
+++ b/roms/u-boot/drivers/net/phy/ncsi.c
@@ -0,0 +1,898 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * NC-SI protocol configuration
+ *
+ * Copyright (C) 2019, IBM Corporation.
+ */
+
+#include <common.h>
+#include <log.h>
+#include <malloc.h>
+#include <phy.h>
+#include <net/ncsi.h>
+#include <net/ncsi-pkt.h>
+#include <asm/unaligned.h>
+
+#define NCSI_PACKAGE_MAX 8
+#define NCSI_CHANNEL_MAX 31
+
+#define NCSI_PACKAGE_SHIFT 5
+#define NCSI_PACKAGE_INDEX(c) (((c) >> NCSI_PACKAGE_SHIFT) & 0x7)
+#define NCSI_RESERVED_CHANNEL 0x1f
+#define NCSI_CHANNEL_INDEX(c) ((c) & ((1 << NCSI_PACKAGE_SHIFT) - 1))
+#define NCSI_TO_CHANNEL(p, c) (((p) << NCSI_PACKAGE_SHIFT) | (c))
+
+#define NCSI_PKT_REVISION 0x01
+
+#define NCSI_CAP_GENERIC_MASK 0x7f
+#define NCSI_CAP_BC_MASK 0x0f
+#define NCSI_CAP_MC_MASK 0x3f
+#define NCSI_CAP_AEN_MASK 0x07
+#define NCSI_CAP_VLAN_MASK 0x07
+
+static void ncsi_send_ebf(unsigned int np, unsigned int nc);
+static void ncsi_send_ae(unsigned int np, unsigned int nc);
+static void ncsi_send_gls(unsigned int np, unsigned int nc);
+static int ncsi_send_command(unsigned int np, unsigned int nc, unsigned int cmd,
+ uchar *payload, int len, bool wait);
+
+struct ncsi_channel {
+ unsigned int id;
+ bool has_link;
+
+ /* capabilities */
+ u32 cap_generic;
+ u32 cap_bc;
+ u32 cap_mc;
+ u32 cap_buffer;
+ u32 cap_aen;
+ u32 cap_vlan;
+
+ /* version information */
+ struct {
+ u32 version; /* Supported BCD encoded NCSI version */
+ u32 alpha2; /* Supported BCD encoded NCSI version */
+ u8 fw_name[12]; /* Firmware name string */
+ u32 fw_version; /* Firmware version */
+ u16 pci_ids[4]; /* PCI identification */
+ u32 mf_id; /* Manufacture ID */
+ } version;
+
+};
+
+struct ncsi_package {
+ unsigned int id;
+ unsigned int n_channels;
+ struct ncsi_channel *channels;
+};
+
+struct ncsi {
+ enum {
+ NCSI_PROBE_PACKAGE_SP,
+ NCSI_PROBE_PACKAGE_DP,
+ NCSI_PROBE_CHANNEL_SP,
+ NCSI_PROBE_CHANNEL,
+ NCSI_CONFIG,
+ } state;
+
+ unsigned int pending_requests;
+ unsigned int requests[256];
+ unsigned int last_request;
+
+ unsigned int current_package;
+ unsigned int current_channel;
+
+ unsigned int n_packages;
+ struct ncsi_package *packages;
+};
+
+struct ncsi *ncsi_priv;
+
+bool ncsi_active(void)
+{
+ unsigned int np, nc;
+
+ if (!ncsi_priv)
+ return false;
+
+ np = ncsi_priv->current_package;
+ nc = ncsi_priv->current_channel;
+
+ if (ncsi_priv->state != NCSI_CONFIG)
+ return false;
+
+ return np < NCSI_PACKAGE_MAX && nc < NCSI_CHANNEL_MAX &&
+ ncsi_priv->packages[np].channels[nc].has_link;
+}
+
+static unsigned int cmd_payload(int cmd)
+{
+ switch (cmd) {
+ case NCSI_PKT_CMD_CIS:
+ return 0;
+ case NCSI_PKT_CMD_SP:
+ return 4;
+ case NCSI_PKT_CMD_DP:
+ return 0;
+ case NCSI_PKT_CMD_EC:
+ return 0;
+ case NCSI_PKT_CMD_DC:
+ return 4;
+ case NCSI_PKT_CMD_RC:
+ return 4;
+ case NCSI_PKT_CMD_ECNT:
+ return 0;
+ case NCSI_PKT_CMD_DCNT:
+ return 0;
+ case NCSI_PKT_CMD_AE:
+ return 8;
+ case NCSI_PKT_CMD_SL:
+ return 8;
+ case NCSI_PKT_CMD_GLS:
+ return 0;
+ case NCSI_PKT_CMD_SVF:
+ return 8;
+ case NCSI_PKT_CMD_EV:
+ return 4;
+ case NCSI_PKT_CMD_DV:
+ return 0;
+ case NCSI_PKT_CMD_SMA:
+ return 8;
+ case NCSI_PKT_CMD_EBF:
+ return 4;
+ case NCSI_PKT_CMD_DBF:
+ return 0;
+ case NCSI_PKT_CMD_EGMF:
+ return 4;
+ case NCSI_PKT_CMD_DGMF:
+ return 0;
+ case NCSI_PKT_CMD_SNFC:
+ return 4;
+ case NCSI_PKT_CMD_GVI:
+ return 0;
+ case NCSI_PKT_CMD_GC:
+ return 0;
+ case NCSI_PKT_CMD_GP:
+ return 0;
+ case NCSI_PKT_CMD_GCPS:
+ return 0;
+ case NCSI_PKT_CMD_GNS:
+ return 0;
+ case NCSI_PKT_CMD_GNPTS:
+ return 0;
+ case NCSI_PKT_CMD_GPS:
+ return 0;
+ default:
+ printf("NCSI: Unknown command 0x%02x\n", cmd);
+ return 0;
+ }
+}
+
+static u32 ncsi_calculate_checksum(unsigned char *data, int len)
+{
+ u32 checksum = 0;
+ int i;
+
+ for (i = 0; i < len; i += 2)
+ checksum += (((u32)data[i] << 8) | data[i + 1]);
+
+ checksum = (~checksum + 1);
+ return checksum;
+}
+
+static int ncsi_validate_rsp(struct ncsi_rsp_pkt *pkt, int payload)
+{
+ struct ncsi_rsp_pkt_hdr *hdr = &pkt->rsp;
+ u32 checksum, c_offset;
+ __be32 pchecksum;
+
+ if (hdr->common.revision != 1) {
+ printf("NCSI: 0x%02x response has unsupported revision 0x%x\n",
+ hdr->common.type, hdr->common.revision);
+ return -1;
+ }
+
+ if (hdr->code != 0) {
+ printf("NCSI: 0x%02x response returns error %d\n",
+ hdr->common.type, __be16_to_cpu(hdr->code));
+ if (ntohs(hdr->reason) == 0x05)
+ printf("(Invalid command length)\n");
+ return -1;
+ }
+
+ if (ntohs(hdr->common.length) != payload) {
+ printf("NCSI: 0x%02x response has incorrect length %d\n",
+ hdr->common.type, hdr->common.length);
+ return -1;
+ }
+
+ c_offset = sizeof(struct ncsi_rsp_pkt_hdr) + payload - sizeof(checksum);
+ pchecksum = get_unaligned_be32((void *)hdr + c_offset);
+ if (pchecksum != 0) {
+ checksum = ncsi_calculate_checksum((unsigned char *)hdr,
+ c_offset);
+ if (pchecksum != checksum) {
+ printf("NCSI: 0x%02x response has invalid checksum\n",
+ hdr->common.type);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static void ncsi_rsp_ec(struct ncsi_rsp_pkt *pkt)
+{
+ struct ncsi_rsp_pkt_hdr *rsp = (struct ncsi_rsp_pkt_hdr *)&pkt->rsp;
+ unsigned int np, nc;
+
+ np = NCSI_PACKAGE_INDEX(rsp->common.channel);
+ nc = NCSI_CHANNEL_INDEX(rsp->common.channel);
+
+ if (ncsi_priv->packages[np].channels[nc].cap_aen != 0)
+ ncsi_send_ae(np, nc);
+ /* else, done */
+}
+
+static void ncsi_rsp_ecnt(struct ncsi_rsp_pkt *pkt)
+{
+ struct ncsi_rsp_pkt_hdr *rsp = (struct ncsi_rsp_pkt_hdr *)&pkt->rsp;
+ unsigned int np, nc;
+
+ np = NCSI_PACKAGE_INDEX(rsp->common.channel);
+ nc = NCSI_CHANNEL_INDEX(rsp->common.channel);
+
+ ncsi_send_command(np, nc, NCSI_PKT_CMD_EC, NULL, 0, true);
+}
+
+static void ncsi_rsp_ebf(struct ncsi_rsp_pkt *pkt)
+{
+ struct ncsi_rsp_pkt_hdr *rsp = (struct ncsi_rsp_pkt_hdr *)&pkt->rsp;
+ unsigned int np, nc;
+
+ np = NCSI_PACKAGE_INDEX(rsp->common.channel);
+ nc = NCSI_CHANNEL_INDEX(rsp->common.channel);
+
+ ncsi_send_command(np, nc, NCSI_PKT_CMD_ECNT, NULL, 0, true);
+}
+
+static void ncsi_rsp_sma(struct ncsi_rsp_pkt *pkt)
+{
+ struct ncsi_rsp_pkt_hdr *rsp = (struct ncsi_rsp_pkt_hdr *)&pkt->rsp;
+ unsigned int np, nc;
+
+ np = NCSI_PACKAGE_INDEX(rsp->common.channel);
+ nc = NCSI_CHANNEL_INDEX(rsp->common.channel);
+
+ ncsi_send_ebf(np, nc);
+}
+
+static void ncsi_rsp_gc(struct ncsi_rsp_pkt *pkt)
+{
+ struct ncsi_rsp_gc_pkt *gc = (struct ncsi_rsp_gc_pkt *)pkt;
+ struct ncsi_rsp_pkt_hdr *rsp = (struct ncsi_rsp_pkt_hdr *)&gc->rsp;
+ struct ncsi_channel *c;
+ unsigned int np, nc;
+
+ np = NCSI_PACKAGE_INDEX(rsp->common.channel);
+ nc = NCSI_CHANNEL_INDEX(rsp->common.channel);
+
+ if (np >= ncsi_priv->n_packages ||
+ nc >= ncsi_priv->packages[np].n_channels) {
+ printf("NCSI: Invalid package / channel (0x%02x, 0x%02x)\n",
+ np, nc);
+ return;
+ }
+
+ c = &ncsi_priv->packages[np].channels[nc];
+ c->cap_generic = ntohl(gc->cap) & NCSI_CAP_GENERIC_MASK;
+ c->cap_bc = ntohl(gc->bc_cap) & NCSI_CAP_BC_MASK;
+ c->cap_mc = ntohl(gc->mc_cap) & NCSI_CAP_MC_MASK;
+ c->cap_aen = ntohl(gc->aen_cap) & NCSI_CAP_AEN_MASK;
+ c->cap_vlan = ntohl(gc->vlan_mode) & NCSI_CAP_VLAN_MASK;
+
+ /* End of probe for this channel */
+}
+
+static void ncsi_rsp_gvi(struct ncsi_rsp_pkt *pkt)
+{
+ struct ncsi_rsp_gvi_pkt *gvi = (struct ncsi_rsp_gvi_pkt *)pkt;
+ struct ncsi_rsp_pkt_hdr *rsp = (struct ncsi_rsp_pkt_hdr *)&gvi->rsp;
+ struct ncsi_channel *c;
+ unsigned int np, nc, i;
+
+ np = NCSI_PACKAGE_INDEX(rsp->common.channel);
+ nc = NCSI_CHANNEL_INDEX(rsp->common.channel);
+
+ if (np >= ncsi_priv->n_packages ||
+ nc >= ncsi_priv->packages[np].n_channels) {
+ printf("NCSI: Invalid package / channel (0x%02x, 0x%02x)\n",
+ np, nc);
+ return;
+ }
+
+ c = &ncsi_priv->packages[np].channels[nc];
+ c->version.version = get_unaligned_be32(&gvi->ncsi_version);
+ c->version.alpha2 = gvi->alpha2;
+ memcpy(c->version.fw_name, gvi->fw_name, sizeof(c->version.fw_name));
+ c->version.fw_version = get_unaligned_be32(&gvi->fw_version);
+ for (i = 0; i < ARRAY_SIZE(c->version.pci_ids); i++)
+ c->version.pci_ids[i] = get_unaligned_be16(gvi->pci_ids + i);
+ c->version.mf_id = get_unaligned_be32(&gvi->mf_id);
+
+ if (ncsi_priv->state == NCSI_PROBE_CHANNEL)
+ ncsi_send_command(np, nc, NCSI_PKT_CMD_GC, NULL, 0, true);
+}
+
+static void ncsi_rsp_gls(struct ncsi_rsp_pkt *pkt)
+{
+ struct ncsi_rsp_gls_pkt *gls = (struct ncsi_rsp_gls_pkt *)pkt;
+ struct ncsi_rsp_pkt_hdr *rsp = (struct ncsi_rsp_pkt_hdr *)&gls->rsp;
+ unsigned int np, nc;
+
+ np = NCSI_PACKAGE_INDEX(rsp->common.channel);
+ nc = NCSI_CHANNEL_INDEX(rsp->common.channel);
+
+ if (np >= ncsi_priv->n_packages ||
+ nc >= ncsi_priv->packages[np].n_channels) {
+ printf("NCSI: Invalid package / channel (0x%02x, 0x%02x)\n",
+ np, nc);
+ return;
+ }
+
+ ncsi_priv->packages[np].channels[nc].has_link =
+ !!(get_unaligned_be32(&gls->status));
+
+ if (ncsi_priv->state == NCSI_PROBE_CHANNEL)
+ ncsi_send_command(np, nc, NCSI_PKT_CMD_GVI, NULL, 0, true);
+}
+
+static void ncsi_rsp_cis(struct ncsi_rsp_pkt *pkt)
+{
+ struct ncsi_rsp_pkt_hdr *rsp = (struct ncsi_rsp_pkt_hdr *)pkt;
+ struct ncsi_package *package;
+ unsigned int np, nc;
+
+ np = NCSI_PACKAGE_INDEX(rsp->common.channel);
+ nc = NCSI_CHANNEL_INDEX(rsp->common.channel);
+
+ if (np >= ncsi_priv->n_packages) {
+ printf("NCSI: Mystery package 0x%02x from CIS\n", np);
+ return;
+ }
+
+ package = &ncsi_priv->packages[np];
+
+ if (nc < package->n_channels) {
+ /*
+ * This is fine in general but in the current design we
+ * don't send CIS commands to known channels.
+ */
+ debug("NCSI: Duplicate channel 0x%02x\n", nc);
+ return;
+ }
+
+ package->channels = realloc(package->channels,
+ sizeof(struct ncsi_channel) *
+ (package->n_channels + 1));
+ if (!package->channels) {
+ printf("NCSI: Could not allocate memory for new channel\n");
+ return;
+ }
+
+ debug("NCSI: New channel 0x%02x\n", nc);
+
+ package->channels[nc].id = nc;
+ package->channels[nc].has_link = false;
+ package->n_channels++;
+
+ ncsi_send_gls(np, nc);
+}
+
+static void ncsi_rsp_dp(struct ncsi_rsp_pkt *pkt)
+{
+ struct ncsi_rsp_pkt_hdr *rsp = (struct ncsi_rsp_pkt_hdr *)pkt;
+ unsigned int np;
+
+ /* No action needed */
+
+ np = NCSI_PACKAGE_INDEX(rsp->common.channel);
+ if (np >= ncsi_priv->n_packages)
+ debug("NCSI: DP response from unknown package %d\n", np);
+}
+
+static void ncsi_rsp_sp(struct ncsi_rsp_pkt *pkt)
+{
+ struct ncsi_rsp_pkt_hdr *rsp = (struct ncsi_rsp_pkt_hdr *)pkt;
+ unsigned int np;
+
+ np = NCSI_PACKAGE_INDEX(rsp->common.channel);
+
+ if (np < ncsi_priv->n_packages) {
+ /* Already know about this package */
+ debug("NCSI: package 0x%02x selected\n", np);
+ return;
+ }
+
+ debug("NCSI: adding new package %d\n", np);
+
+ ncsi_priv->packages = realloc(ncsi_priv->packages,
+ sizeof(struct ncsi_package) *
+ (ncsi_priv->n_packages + 1));
+ if (!ncsi_priv->packages) {
+ printf("NCSI: could not allocate memory for new package\n");
+ return;
+ }
+
+ ncsi_priv->packages[np].id = np;
+ ncsi_priv->packages[np].n_channels = 0;
+ ncsi_priv->packages[np].channels = NULL;
+ ncsi_priv->n_packages++;
+}
+
+static void ncsi_update_state(struct ncsi_rsp_pkt_hdr *nh)
+{
+ bool timeout = !nh;
+ int np, nc;
+
+ switch (ncsi_priv->state) {
+ case NCSI_PROBE_PACKAGE_SP:
+ if (!timeout &&
+ ncsi_priv->current_package + 1 < NCSI_PACKAGE_MAX) {
+ ncsi_priv->current_package++;
+ } else {
+ ncsi_priv->state = NCSI_PROBE_PACKAGE_DP;
+ ncsi_priv->current_package = 0;
+ }
+ return ncsi_probe_packages();
+ case NCSI_PROBE_PACKAGE_DP:
+ if (ncsi_priv->current_package + 1 < ncsi_priv->n_packages &&
+ !timeout) {
+ ncsi_priv->current_package++;
+ } else {
+ if (!ncsi_priv->n_packages) {
+ printf("NCSI: no packages found\n");
+ net_set_state(NETLOOP_FAIL);
+ return;
+ }
+ printf("NCSI: probing channels\n");
+ ncsi_priv->state = NCSI_PROBE_CHANNEL_SP;
+ ncsi_priv->current_package = 0;
+ ncsi_priv->current_channel = 0;
+ }
+ return ncsi_probe_packages();
+ case NCSI_PROBE_CHANNEL_SP:
+ if (!timeout && nh->common.type == NCSI_PKT_RSP_SP) {
+ ncsi_priv->state = NCSI_PROBE_CHANNEL;
+ return ncsi_probe_packages();
+ }
+ printf("NCSI: failed to select package 0x%0x2 or timeout\n",
+ ncsi_priv->current_package);
+ net_set_state(NETLOOP_FAIL);
+ break;
+ case NCSI_PROBE_CHANNEL:
+ // TODO only does package 0 for now
+ if (ncsi_priv->pending_requests == 0) {
+ np = ncsi_priv->current_package;
+ nc = ncsi_priv->current_channel;
+
+ /* Configure first channel that has link */
+ if (ncsi_priv->packages[np].channels[nc].has_link) {
+ ncsi_priv->state = NCSI_CONFIG;
+ } else if (ncsi_priv->current_channel + 1 <
+ NCSI_CHANNEL_MAX) {
+ ncsi_priv->current_channel++;
+ } else {
+ // XXX As above only package 0
+ printf("NCSI: no channel found with link\n");
+ net_set_state(NETLOOP_FAIL);
+ return;
+ }
+ return ncsi_probe_packages();
+ }
+ break;
+ case NCSI_CONFIG:
+ if (ncsi_priv->pending_requests == 0) {
+ printf("NCSI: configuration done!\n");
+ net_set_state(NETLOOP_SUCCESS);
+ } else if (timeout) {
+ printf("NCSI: timeout during configure\n");
+ net_set_state(NETLOOP_FAIL);
+ }
+ break;
+ default:
+ printf("NCSI: something went very wrong, nevermind\n");
+ net_set_state(NETLOOP_FAIL);
+ break;
+ }
+}
+
+static void ncsi_timeout_handler(void)
+{
+ if (ncsi_priv->pending_requests)
+ ncsi_priv->pending_requests--;
+
+ ncsi_update_state(NULL);
+}
+
+static int ncsi_send_command(unsigned int np, unsigned int nc, unsigned int cmd,
+ uchar *payload, int len, bool wait)
+{
+ struct ncsi_pkt_hdr *hdr;
+ __be32 *pchecksum;
+ int eth_hdr_size;
+ u32 checksum;
+ uchar *pkt, *start;
+ int final_len;
+
+ pkt = calloc(1, PKTSIZE_ALIGN + PKTALIGN);
+ if (!pkt)
+ return -ENOMEM;
+ start = pkt;
+
+ eth_hdr_size = net_set_ether(pkt, net_bcast_ethaddr, PROT_NCSI);
+ pkt += eth_hdr_size;
+
+ /* Set NCSI command header fields */
+ hdr = (struct ncsi_pkt_hdr *)pkt;
+ hdr->mc_id = 0;
+ hdr->revision = NCSI_PKT_REVISION;
+ hdr->id = ++ncsi_priv->last_request;
+ ncsi_priv->requests[ncsi_priv->last_request] = 1;
+ hdr->type = cmd;
+ hdr->channel = NCSI_TO_CHANNEL(np, nc);
+ hdr->length = htons(len);
+
+ if (payload && len)
+ memcpy(pkt + sizeof(struct ncsi_pkt_hdr), payload, len);
+
+ /* Calculate checksum */
+ checksum = ncsi_calculate_checksum((unsigned char *)hdr,
+ sizeof(*hdr) + len);
+ pchecksum = (__be32 *)((void *)(hdr + 1) + len);
+ put_unaligned_be32(htonl(checksum), pchecksum);
+
+ if (wait) {
+ net_set_timeout_handler(1000UL, ncsi_timeout_handler);
+ ncsi_priv->pending_requests++;
+ }
+
+ if (len < 26)
+ len = 26;
+ /* frame header, packet header, payload, checksum */
+ final_len = eth_hdr_size + sizeof(struct ncsi_cmd_pkt_hdr) + len + 4;
+
+ net_send_packet(start, final_len);
+ free(start);
+ return 0;
+}
+
+static void ncsi_handle_aen(struct ip_udp_hdr *ip, unsigned int len)
+{
+ struct ncsi_aen_pkt_hdr *hdr = (struct ncsi_aen_pkt_hdr *)ip;
+ int payload, i;
+ __be32 pchecksum;
+ u32 checksum;
+
+ switch (hdr->type) {
+ case NCSI_PKT_AEN_LSC:
+ printf("NCSI: link state changed\n");
+ payload = 12;
+ break;
+ case NCSI_PKT_AEN_CR:
+ printf("NCSI: re-configuration required\n");
+ payload = 4;
+ break;
+ case NCSI_PKT_AEN_HNCDSC:
+ /* Host notifcation - N/A but weird */
+ debug("NCSI: HNCDSC AEN received\n");
+ return;
+ default:
+ printf("%s: Invalid type 0x%02x\n", __func__, hdr->type);
+ return;
+ }
+
+ /* Validate packet */
+ if (hdr->common.revision != 1) {
+ printf("NCSI: 0x%02x response has unsupported revision 0x%x\n",
+ hdr->common.type, hdr->common.revision);
+ return;
+ }
+
+ if (ntohs(hdr->common.length) != payload) {
+ printf("NCSI: 0x%02x response has incorrect length %d\n",
+ hdr->common.type, hdr->common.length);
+ return;
+ }
+
+ pchecksum = get_unaligned_be32((void *)(hdr + 1) + payload - 4);
+ if (pchecksum != 0) {
+ checksum = ncsi_calculate_checksum((unsigned char *)hdr,
+ sizeof(*hdr) + payload - 4);
+ if (pchecksum != checksum) {
+ printf("NCSI: 0x%02x response has invalid checksum\n",
+ hdr->common.type);
+ return;
+ }
+ }
+
+ /* Link or configuration lost - just redo the discovery process */
+ ncsi_priv->state = NCSI_PROBE_PACKAGE_SP;
+ for (i = 0; i < ncsi_priv->n_packages; i++)
+ free(ncsi_priv->packages[i].channels);
+ free(ncsi_priv->packages);
+ ncsi_priv->n_packages = 0;
+
+ ncsi_priv->current_package = NCSI_PACKAGE_MAX;
+ ncsi_priv->current_channel = NCSI_CHANNEL_MAX;
+
+ ncsi_probe_packages();
+}
+
+void ncsi_receive(struct ethernet_hdr *et, struct ip_udp_hdr *ip,
+ unsigned int len)
+{
+ struct ncsi_rsp_pkt *pkt = (struct ncsi_rsp_pkt *)ip;
+ struct ncsi_rsp_pkt_hdr *nh = (struct ncsi_rsp_pkt_hdr *)&pkt->rsp;
+ void (*handler)(struct ncsi_rsp_pkt *pkt) = NULL;
+ unsigned short payload;
+
+ if (ncsi_priv->pending_requests)
+ ncsi_priv->pending_requests--;
+
+ if (len < sizeof(struct ncsi_rsp_pkt_hdr)) {
+ printf("NCSI: undersized packet: %u bytes\n", len);
+ goto out;
+ }
+
+ if (nh->common.type == NCSI_PKT_AEN)
+ return ncsi_handle_aen(ip, len);
+
+ switch (nh->common.type) {
+ case NCSI_PKT_RSP_SP:
+ payload = 4;
+ handler = ncsi_rsp_sp;
+ break;
+ case NCSI_PKT_RSP_DP:
+ payload = 4;
+ handler = ncsi_rsp_dp;
+ break;
+ case NCSI_PKT_RSP_CIS:
+ payload = 4;
+ handler = ncsi_rsp_cis;
+ break;
+ case NCSI_PKT_RSP_GLS:
+ payload = 16;
+ handler = ncsi_rsp_gls;
+ break;
+ case NCSI_PKT_RSP_GVI:
+ payload = 40;
+ handler = ncsi_rsp_gvi;
+ break;
+ case NCSI_PKT_RSP_GC:
+ payload = 32;
+ handler = ncsi_rsp_gc;
+ break;
+ case NCSI_PKT_RSP_SMA:
+ payload = 4;
+ handler = ncsi_rsp_sma;
+ break;
+ case NCSI_PKT_RSP_EBF:
+ payload = 4;
+ handler = ncsi_rsp_ebf;
+ break;
+ case NCSI_PKT_RSP_ECNT:
+ payload = 4;
+ handler = ncsi_rsp_ecnt;
+ break;
+ case NCSI_PKT_RSP_EC:
+ payload = 4;
+ handler = ncsi_rsp_ec;
+ break;
+ case NCSI_PKT_RSP_AE:
+ payload = 4;
+ handler = NULL;
+ break;
+ default:
+ printf("NCSI: unsupported packet type 0x%02x\n",
+ nh->common.type);
+ goto out;
+ }
+
+ if (ncsi_validate_rsp(pkt, payload) != 0) {
+ printf("NCSI: discarding invalid packet of type 0x%02x\n",
+ nh->common.type);
+ goto out;
+ }
+
+ if (handler)
+ handler(pkt);
+out:
+ ncsi_update_state(nh);
+}
+
+static void ncsi_send_sp(unsigned int np)
+{
+ uchar payload[4] = {0};
+
+ ncsi_send_command(np, NCSI_RESERVED_CHANNEL, NCSI_PKT_CMD_SP,
+ (unsigned char *)&payload,
+ cmd_payload(NCSI_PKT_CMD_SP), true);
+}
+
+static void ncsi_send_dp(unsigned int np)
+{
+ ncsi_send_command(np, NCSI_RESERVED_CHANNEL, NCSI_PKT_CMD_DP, NULL, 0,
+ true);
+}
+
+static void ncsi_send_gls(unsigned int np, unsigned int nc)
+{
+ ncsi_send_command(np, nc, NCSI_PKT_CMD_GLS, NULL, 0, true);
+}
+
+static void ncsi_send_cis(unsigned int np, unsigned int nc)
+{
+ ncsi_send_command(np, nc, NCSI_PKT_CMD_CIS, NULL, 0, true);
+}
+
+static void ncsi_send_ae(unsigned int np, unsigned int nc)
+{
+ struct ncsi_cmd_ae_pkt cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.mode = htonl(ncsi_priv->packages[np].channels[nc].cap_aen);
+
+ ncsi_send_command(np, nc, NCSI_PKT_CMD_AE,
+ ((unsigned char *)&cmd)
+ + sizeof(struct ncsi_cmd_pkt_hdr),
+ cmd_payload(NCSI_PKT_CMD_AE), true);
+}
+
+static void ncsi_send_ebf(unsigned int np, unsigned int nc)
+{
+ struct ncsi_cmd_ebf_pkt cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.mode = htonl(ncsi_priv->packages[np].channels[nc].cap_bc);
+
+ ncsi_send_command(np, nc, NCSI_PKT_CMD_EBF,
+ ((unsigned char *)&cmd)
+ + sizeof(struct ncsi_cmd_pkt_hdr),
+ cmd_payload(NCSI_PKT_CMD_EBF), true);
+}
+
+static void ncsi_send_sma(unsigned int np, unsigned int nc)
+{
+ struct ncsi_cmd_sma_pkt cmd;
+ unsigned char *addr, i;
+
+ addr = eth_get_ethaddr();
+ if (!addr) {
+ printf("NCSI: no MAC address configured\n");
+ return;
+ }
+
+ memset(&cmd, 0, sizeof(cmd));
+ for (i = 0; i < ARP_HLEN; i++)
+ cmd.mac[i] = addr[i];
+ cmd.index = 1;
+ cmd.at_e = 1;
+
+ ncsi_send_command(np, nc, NCSI_PKT_CMD_SMA,
+ ((unsigned char *)&cmd)
+ + sizeof(struct ncsi_cmd_pkt_hdr),
+ cmd_payload(NCSI_PKT_CMD_SMA), true);
+}
+
+void ncsi_probe_packages(void)
+{
+ struct ncsi_package *package;
+ unsigned int np, nc;
+
+ switch (ncsi_priv->state) {
+ case NCSI_PROBE_PACKAGE_SP:
+ if (ncsi_priv->current_package == NCSI_PACKAGE_MAX)
+ ncsi_priv->current_package = 0;
+ ncsi_send_sp(ncsi_priv->current_package);
+ break;
+ case NCSI_PROBE_PACKAGE_DP:
+ ncsi_send_dp(ncsi_priv->current_package);
+ break;
+ case NCSI_PROBE_CHANNEL_SP:
+ if (ncsi_priv->n_packages > 0)
+ ncsi_send_sp(ncsi_priv->current_package);
+ else
+ printf("NCSI: no packages discovered, configuration not possible\n");
+ break;
+ case NCSI_PROBE_CHANNEL:
+ /* Kicks off chain of channel discovery */
+ ncsi_send_cis(ncsi_priv->current_package,
+ ncsi_priv->current_channel);
+ break;
+ case NCSI_CONFIG:
+ for (np = 0; np < ncsi_priv->n_packages; np++) {
+ package = &ncsi_priv->packages[np];
+ for (nc = 0; nc < package->n_channels; nc++)
+ if (package->channels[nc].has_link)
+ break;
+ if (nc < package->n_channels)
+ break;
+ }
+ if (np == ncsi_priv->n_packages) {
+ printf("NCSI: no link available\n");
+ return;
+ }
+
+ printf("NCSI: configuring channel %d\n", nc);
+ ncsi_priv->current_package = np;
+ ncsi_priv->current_channel = nc;
+ /* Kicks off rest of configure chain */
+ ncsi_send_sma(np, nc);
+ break;
+ default:
+ printf("NCSI: unknown state 0x%x\n", ncsi_priv->state);
+ }
+}
+
+int ncsi_probe(struct phy_device *phydev)
+{
+ if (!phydev->priv) {
+ phydev->priv = malloc(sizeof(struct ncsi));
+ if (!phydev->priv)
+ return -ENOMEM;
+ memset(phydev->priv, 0, sizeof(struct ncsi));
+ }
+
+ ncsi_priv = phydev->priv;
+
+ return 0;
+}
+
+int ncsi_startup(struct phy_device *phydev)
+{
+ /* Set phydev parameters */
+ phydev->speed = SPEED_100;
+ phydev->duplex = DUPLEX_FULL;
+ /* Normal phy reset is N/A */
+ phydev->flags |= PHY_FLAG_BROKEN_RESET;
+
+ /* Set initial probe state */
+ ncsi_priv->state = NCSI_PROBE_PACKAGE_SP;
+
+ /* No active package/channel yet */
+ ncsi_priv->current_package = NCSI_PACKAGE_MAX;
+ ncsi_priv->current_channel = NCSI_CHANNEL_MAX;
+
+ /* Pretend link works so the MAC driver sets final bits up */
+ phydev->link = true;
+
+ /* Set ncsi_priv so we can use it when called from net_loop() */
+ ncsi_priv = phydev->priv;
+
+ return 0;
+}
+
+int ncsi_shutdown(struct phy_device *phydev)
+{
+ printf("NCSI: Disabling package %d\n", ncsi_priv->current_package);
+ ncsi_send_dp(ncsi_priv->current_package);
+ return 0;
+}
+
+static struct phy_driver ncsi_driver = {
+ .uid = PHY_NCSI_ID,
+ .mask = 0xffffffff,
+ .name = "NC-SI",
+ .features = PHY_100BT_FEATURES | PHY_DEFAULT_FEATURES |
+ SUPPORTED_100baseT_Full | SUPPORTED_MII,
+ .probe = ncsi_probe,
+ .startup = ncsi_startup,
+ .shutdown = ncsi_shutdown,
+};
+
+int phy_ncsi_init(void)
+{
+ phy_register(&ncsi_driver);
+ return 0;
+}
diff --git a/roms/u-boot/drivers/net/phy/phy.c b/roms/u-boot/drivers/net/phy/phy.c
new file mode 100644
index 000000000..ed197fa46
--- /dev/null
+++ b/roms/u-boot/drivers/net/phy/phy.c
@@ -0,0 +1,1104 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Generic PHY Management code
+ *
+ * Copyright 2011 Freescale Semiconductor, Inc.
+ * author Andy Fleming
+ *
+ * Based loosely off of Linux's PHY Lib
+ */
+#include <common.h>
+#include <console.h>
+#include <dm.h>
+#include <log.h>
+#include <malloc.h>
+#include <net.h>
+#include <command.h>
+#include <miiphy.h>
+#include <phy.h>
+#include <errno.h>
+#include <asm/global_data.h>
+#include <dm/of_extra.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/compiler.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/* Generic PHY support and helper functions */
+
+/**
+ * genphy_config_advert - sanitize and advertise auto-negotiation parameters
+ * @phydev: target phy_device struct
+ *
+ * Description: Writes MII_ADVERTISE with the appropriate values,
+ * after sanitizing the values to make sure we only advertise
+ * what is supported. Returns < 0 on error, 0 if the PHY's advertisement
+ * hasn't changed, and > 0 if it has changed.
+ */
+static int genphy_config_advert(struct phy_device *phydev)
+{
+ u32 advertise;
+ int oldadv, adv, bmsr;
+ int err, changed = 0;
+
+ /* Only allow advertising what this PHY supports */
+ phydev->advertising &= phydev->supported;
+ advertise = phydev->advertising;
+
+ /* Setup standard advertisement */
+ adv = phy_read(phydev, MDIO_DEVAD_NONE, MII_ADVERTISE);
+ oldadv = adv;
+
+ if (adv < 0)
+ return adv;
+
+ adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4 | ADVERTISE_PAUSE_CAP |
+ ADVERTISE_PAUSE_ASYM);
+ if (advertise & ADVERTISED_10baseT_Half)
+ adv |= ADVERTISE_10HALF;
+ if (advertise & ADVERTISED_10baseT_Full)
+ adv |= ADVERTISE_10FULL;
+ if (advertise & ADVERTISED_100baseT_Half)
+ adv |= ADVERTISE_100HALF;
+ if (advertise & ADVERTISED_100baseT_Full)
+ adv |= ADVERTISE_100FULL;
+ if (advertise & ADVERTISED_Pause)
+ adv |= ADVERTISE_PAUSE_CAP;
+ if (advertise & ADVERTISED_Asym_Pause)
+ adv |= ADVERTISE_PAUSE_ASYM;
+ if (advertise & ADVERTISED_1000baseX_Half)
+ adv |= ADVERTISE_1000XHALF;
+ if (advertise & ADVERTISED_1000baseX_Full)
+ adv |= ADVERTISE_1000XFULL;
+
+ if (adv != oldadv) {
+ err = phy_write(phydev, MDIO_DEVAD_NONE, MII_ADVERTISE, adv);
+
+ if (err < 0)
+ return err;
+ changed = 1;
+ }
+
+ bmsr = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR);
+ if (bmsr < 0)
+ return bmsr;
+
+ /* Per 802.3-2008, Section 22.2.4.2.16 Extended status all
+ * 1000Mbits/sec capable PHYs shall have the BMSR_ESTATEN bit set to a
+ * logical 1.
+ */
+ if (!(bmsr & BMSR_ESTATEN))
+ return changed;
+
+ /* Configure gigabit if it's supported */
+ adv = phy_read(phydev, MDIO_DEVAD_NONE, MII_CTRL1000);
+ oldadv = adv;
+
+ if (adv < 0)
+ return adv;
+
+ adv &= ~(ADVERTISE_1000FULL | ADVERTISE_1000HALF);
+
+ if (phydev->supported & (SUPPORTED_1000baseT_Half |
+ SUPPORTED_1000baseT_Full)) {
+ if (advertise & SUPPORTED_1000baseT_Half)
+ adv |= ADVERTISE_1000HALF;
+ if (advertise & SUPPORTED_1000baseT_Full)
+ adv |= ADVERTISE_1000FULL;
+ }
+
+ if (adv != oldadv)
+ changed = 1;
+
+ err = phy_write(phydev, MDIO_DEVAD_NONE, MII_CTRL1000, adv);
+ if (err < 0)
+ return err;
+
+ return changed;
+}
+
+/**
+ * genphy_setup_forced - configures/forces speed/duplex from @phydev
+ * @phydev: target phy_device struct
+ *
+ * Description: Configures MII_BMCR to force speed/duplex
+ * to the values in phydev. Assumes that the values are valid.
+ */
+static int genphy_setup_forced(struct phy_device *phydev)
+{
+ int err;
+ int ctl = BMCR_ANRESTART;
+
+ phydev->pause = 0;
+ phydev->asym_pause = 0;
+
+ if (phydev->speed == SPEED_1000)
+ ctl |= BMCR_SPEED1000;
+ else if (phydev->speed == SPEED_100)
+ ctl |= BMCR_SPEED100;
+
+ if (phydev->duplex == DUPLEX_FULL)
+ ctl |= BMCR_FULLDPLX;
+
+ err = phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, ctl);
+
+ return err;
+}
+
+/**
+ * genphy_restart_aneg - Enable and Restart Autonegotiation
+ * @phydev: target phy_device struct
+ */
+int genphy_restart_aneg(struct phy_device *phydev)
+{
+ int ctl;
+
+ ctl = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR);
+
+ if (ctl < 0)
+ return ctl;
+
+ ctl |= (BMCR_ANENABLE | BMCR_ANRESTART);
+
+ /* Don't isolate the PHY if we're negotiating */
+ ctl &= ~(BMCR_ISOLATE);
+
+ ctl = phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, ctl);
+
+ return ctl;
+}
+
+/**
+ * genphy_config_aneg - restart auto-negotiation or write BMCR
+ * @phydev: target phy_device struct
+ *
+ * Description: If auto-negotiation is enabled, we configure the
+ * advertising, and then restart auto-negotiation. If it is not
+ * enabled, then we write the BMCR.
+ */
+int genphy_config_aneg(struct phy_device *phydev)
+{
+ int result;
+
+ if (phydev->autoneg != AUTONEG_ENABLE)
+ return genphy_setup_forced(phydev);
+
+ result = genphy_config_advert(phydev);
+
+ if (result < 0) /* error */
+ return result;
+
+ if (result == 0) {
+ /*
+ * Advertisment hasn't changed, but maybe aneg was never on to
+ * begin with? Or maybe phy was isolated?
+ */
+ int ctl = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR);
+
+ if (ctl < 0)
+ return ctl;
+
+ if (!(ctl & BMCR_ANENABLE) || (ctl & BMCR_ISOLATE))
+ result = 1; /* do restart aneg */
+ }
+
+ /*
+ * Only restart aneg if we are advertising something different
+ * than we were before.
+ */
+ if (result > 0)
+ result = genphy_restart_aneg(phydev);
+
+ return result;
+}
+
+/**
+ * genphy_update_link - update link status in @phydev
+ * @phydev: target phy_device struct
+ *
+ * Description: Update the value in phydev->link to reflect the
+ * current link value. In order to do this, we need to read
+ * the status register twice, keeping the second value.
+ */
+int genphy_update_link(struct phy_device *phydev)
+{
+ unsigned int mii_reg;
+
+ /*
+ * Wait if the link is up, and autonegotiation is in progress
+ * (ie - we're capable and it's not done)
+ */
+ mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR);
+
+ /*
+ * If we already saw the link up, and it hasn't gone down, then
+ * we don't need to wait for autoneg again
+ */
+ if (phydev->link && mii_reg & BMSR_LSTATUS)
+ return 0;
+
+ if ((phydev->autoneg == AUTONEG_ENABLE) &&
+ !(mii_reg & BMSR_ANEGCOMPLETE)) {
+ int i = 0;
+
+ printf("%s Waiting for PHY auto negotiation to complete",
+ phydev->dev->name);
+ while (!(mii_reg & BMSR_ANEGCOMPLETE)) {
+ /*
+ * Timeout reached ?
+ */
+ if (i > (PHY_ANEG_TIMEOUT / 50)) {
+ printf(" TIMEOUT !\n");
+ phydev->link = 0;
+ return -ETIMEDOUT;
+ }
+
+ if (ctrlc()) {
+ puts("user interrupt!\n");
+ phydev->link = 0;
+ return -EINTR;
+ }
+
+ if ((i++ % 10) == 0)
+ printf(".");
+
+ mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR);
+ mdelay(50); /* 50 ms */
+ }
+ printf(" done\n");
+ phydev->link = 1;
+ } else {
+ /* Read the link a second time to clear the latched state */
+ mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR);
+
+ if (mii_reg & BMSR_LSTATUS)
+ phydev->link = 1;
+ else
+ phydev->link = 0;
+ }
+
+ return 0;
+}
+
+/*
+ * Generic function which updates the speed and duplex. If
+ * autonegotiation is enabled, it uses the AND of the link
+ * partner's advertised capabilities and our advertised
+ * capabilities. If autonegotiation is disabled, we use the
+ * appropriate bits in the control register.
+ *
+ * Stolen from Linux's mii.c and phy_device.c
+ */
+int genphy_parse_link(struct phy_device *phydev)
+{
+ int mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR);
+
+ /* We're using autonegotiation */
+ if (phydev->autoneg == AUTONEG_ENABLE) {
+ u32 lpa = 0;
+ int gblpa = 0;
+ u32 estatus = 0;
+
+ /* Check for gigabit capability */
+ if (phydev->supported & (SUPPORTED_1000baseT_Full |
+ SUPPORTED_1000baseT_Half)) {
+ /* We want a list of states supported by
+ * both PHYs in the link
+ */
+ gblpa = phy_read(phydev, MDIO_DEVAD_NONE, MII_STAT1000);
+ if (gblpa < 0) {
+ debug("Could not read MII_STAT1000. ");
+ debug("Ignoring gigabit capability\n");
+ gblpa = 0;
+ }
+ gblpa &= phy_read(phydev,
+ MDIO_DEVAD_NONE, MII_CTRL1000) << 2;
+ }
+
+ /* Set the baseline so we only have to set them
+ * if they're different
+ */
+ phydev->speed = SPEED_10;
+ phydev->duplex = DUPLEX_HALF;
+
+ /* Check the gigabit fields */
+ if (gblpa & (PHY_1000BTSR_1000FD | PHY_1000BTSR_1000HD)) {
+ phydev->speed = SPEED_1000;
+
+ if (gblpa & PHY_1000BTSR_1000FD)
+ phydev->duplex = DUPLEX_FULL;
+
+ /* We're done! */
+ return 0;
+ }
+
+ lpa = phy_read(phydev, MDIO_DEVAD_NONE, MII_ADVERTISE);
+ lpa &= phy_read(phydev, MDIO_DEVAD_NONE, MII_LPA);
+
+ if (lpa & (LPA_100FULL | LPA_100HALF)) {
+ phydev->speed = SPEED_100;
+
+ if (lpa & LPA_100FULL)
+ phydev->duplex = DUPLEX_FULL;
+
+ } else if (lpa & LPA_10FULL) {
+ phydev->duplex = DUPLEX_FULL;
+ }
+
+ /*
+ * Extended status may indicate that the PHY supports
+ * 1000BASE-T/X even though the 1000BASE-T registers
+ * are missing. In this case we can't tell whether the
+ * peer also supports it, so we only check extended
+ * status if the 1000BASE-T registers are actually
+ * missing.
+ */
+ if ((mii_reg & BMSR_ESTATEN) && !(mii_reg & BMSR_ERCAP))
+ estatus = phy_read(phydev, MDIO_DEVAD_NONE,
+ MII_ESTATUS);
+
+ if (estatus & (ESTATUS_1000_XFULL | ESTATUS_1000_XHALF |
+ ESTATUS_1000_TFULL | ESTATUS_1000_THALF)) {
+ phydev->speed = SPEED_1000;
+ if (estatus & (ESTATUS_1000_XFULL | ESTATUS_1000_TFULL))
+ phydev->duplex = DUPLEX_FULL;
+ }
+
+ } else {
+ u32 bmcr = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR);
+
+ phydev->speed = SPEED_10;
+ phydev->duplex = DUPLEX_HALF;
+
+ if (bmcr & BMCR_FULLDPLX)
+ phydev->duplex = DUPLEX_FULL;
+
+ if (bmcr & BMCR_SPEED1000)
+ phydev->speed = SPEED_1000;
+ else if (bmcr & BMCR_SPEED100)
+ phydev->speed = SPEED_100;
+ }
+
+ return 0;
+}
+
+int genphy_config(struct phy_device *phydev)
+{
+ int val;
+ u32 features;
+
+ features = (SUPPORTED_TP | SUPPORTED_MII
+ | SUPPORTED_AUI | SUPPORTED_FIBRE |
+ SUPPORTED_BNC);
+
+ /* Do we support autonegotiation? */
+ val = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR);
+
+ if (val < 0)
+ return val;
+
+ if (val & BMSR_ANEGCAPABLE)
+ features |= SUPPORTED_Autoneg;
+
+ if (val & BMSR_100FULL)
+ features |= SUPPORTED_100baseT_Full;
+ if (val & BMSR_100HALF)
+ features |= SUPPORTED_100baseT_Half;
+ if (val & BMSR_10FULL)
+ features |= SUPPORTED_10baseT_Full;
+ if (val & BMSR_10HALF)
+ features |= SUPPORTED_10baseT_Half;
+
+ if (val & BMSR_ESTATEN) {
+ val = phy_read(phydev, MDIO_DEVAD_NONE, MII_ESTATUS);
+
+ if (val < 0)
+ return val;
+
+ if (val & ESTATUS_1000_TFULL)
+ features |= SUPPORTED_1000baseT_Full;
+ if (val & ESTATUS_1000_THALF)
+ features |= SUPPORTED_1000baseT_Half;
+ if (val & ESTATUS_1000_XFULL)
+ features |= SUPPORTED_1000baseX_Full;
+ if (val & ESTATUS_1000_XHALF)
+ features |= SUPPORTED_1000baseX_Half;
+ }
+
+ phydev->supported &= features;
+ phydev->advertising &= features;
+
+ genphy_config_aneg(phydev);
+
+ return 0;
+}
+
+int genphy_startup(struct phy_device *phydev)
+{
+ int ret;
+
+ ret = genphy_update_link(phydev);
+ if (ret)
+ return ret;
+
+ return genphy_parse_link(phydev);
+}
+
+int genphy_shutdown(struct phy_device *phydev)
+{
+ return 0;
+}
+
+static struct phy_driver genphy_driver = {
+ .uid = 0xffffffff,
+ .mask = 0xffffffff,
+ .name = "Generic PHY",
+ .features = PHY_GBIT_FEATURES | SUPPORTED_MII |
+ SUPPORTED_AUI | SUPPORTED_FIBRE |
+ SUPPORTED_BNC,
+ .config = genphy_config,
+ .startup = genphy_startup,
+ .shutdown = genphy_shutdown,
+};
+
+int genphy_init(void)
+{
+ return phy_register(&genphy_driver);
+}
+
+static LIST_HEAD(phy_drivers);
+
+int phy_init(void)
+{
+#ifdef CONFIG_NEEDS_MANUAL_RELOC
+ /*
+ * The pointers inside phy_drivers also needs to be updated incase of
+ * manual reloc, without which these points to some invalid
+ * pre reloc address and leads to invalid accesses, hangs.
+ */
+ struct list_head *head = &phy_drivers;
+
+ head->next = (void *)head->next + gd->reloc_off;
+ head->prev = (void *)head->prev + gd->reloc_off;
+#endif
+
+#ifdef CONFIG_B53_SWITCH
+ phy_b53_init();
+#endif
+#ifdef CONFIG_MV88E61XX_SWITCH
+ phy_mv88e61xx_init();
+#endif
+#ifdef CONFIG_PHY_AQUANTIA
+ phy_aquantia_init();
+#endif
+#ifdef CONFIG_PHY_ATHEROS
+ phy_atheros_init();
+#endif
+#ifdef CONFIG_PHY_BROADCOM
+ phy_broadcom_init();
+#endif
+#ifdef CONFIG_PHY_CORTINA
+ phy_cortina_init();
+#endif
+#ifdef CONFIG_PHY_CORTINA_ACCESS
+ phy_cortina_access_init();
+#endif
+#ifdef CONFIG_PHY_DAVICOM
+ phy_davicom_init();
+#endif
+#ifdef CONFIG_PHY_ET1011C
+ phy_et1011c_init();
+#endif
+#ifdef CONFIG_PHY_LXT
+ phy_lxt_init();
+#endif
+#ifdef CONFIG_PHY_MARVELL
+ phy_marvell_init();
+#endif
+#ifdef CONFIG_PHY_MICREL_KSZ8XXX
+ phy_micrel_ksz8xxx_init();
+#endif
+#ifdef CONFIG_PHY_MICREL_KSZ90X1
+ phy_micrel_ksz90x1_init();
+#endif
+#ifdef CONFIG_PHY_MESON_GXL
+ phy_meson_gxl_init();
+#endif
+#ifdef CONFIG_PHY_NATSEMI
+ phy_natsemi_init();
+#endif
+#ifdef CONFIG_PHY_REALTEK
+ phy_realtek_init();
+#endif
+#ifdef CONFIG_PHY_SMSC
+ phy_smsc_init();
+#endif
+#ifdef CONFIG_PHY_TERANETICS
+ phy_teranetics_init();
+#endif
+#ifdef CONFIG_PHY_TI
+ phy_ti_init();
+#endif
+#ifdef CONFIG_PHY_VITESSE
+ phy_vitesse_init();
+#endif
+#ifdef CONFIG_PHY_XILINX
+ phy_xilinx_init();
+#endif
+#ifdef CONFIG_PHY_MSCC
+ phy_mscc_init();
+#endif
+#ifdef CONFIG_PHY_FIXED
+ phy_fixed_init();
+#endif
+#ifdef CONFIG_PHY_NCSI
+ phy_ncsi_init();
+#endif
+#ifdef CONFIG_PHY_XILINX_GMII2RGMII
+ phy_xilinx_gmii2rgmii_init();
+#endif
+ genphy_init();
+
+ return 0;
+}
+
+int phy_register(struct phy_driver *drv)
+{
+ INIT_LIST_HEAD(&drv->list);
+ list_add_tail(&drv->list, &phy_drivers);
+
+#ifdef CONFIG_NEEDS_MANUAL_RELOC
+ if (drv->probe)
+ drv->probe += gd->reloc_off;
+ if (drv->config)
+ drv->config += gd->reloc_off;
+ if (drv->startup)
+ drv->startup += gd->reloc_off;
+ if (drv->shutdown)
+ drv->shutdown += gd->reloc_off;
+ if (drv->readext)
+ drv->readext += gd->reloc_off;
+ if (drv->writeext)
+ drv->writeext += gd->reloc_off;
+ if (drv->read_mmd)
+ drv->read_mmd += gd->reloc_off;
+ if (drv->write_mmd)
+ drv->write_mmd += gd->reloc_off;
+#endif
+ return 0;
+}
+
+int phy_set_supported(struct phy_device *phydev, u32 max_speed)
+{
+ /* The default values for phydev->supported are provided by the PHY
+ * driver "features" member, we want to reset to sane defaults first
+ * before supporting higher speeds.
+ */
+ phydev->supported &= PHY_DEFAULT_FEATURES;
+
+ switch (max_speed) {
+ default:
+ return -ENOTSUPP;
+ case SPEED_1000:
+ phydev->supported |= PHY_1000BT_FEATURES;
+ /* fall through */
+ case SPEED_100:
+ phydev->supported |= PHY_100BT_FEATURES;
+ /* fall through */
+ case SPEED_10:
+ phydev->supported |= PHY_10BT_FEATURES;
+ }
+
+ return 0;
+}
+
+static int phy_probe(struct phy_device *phydev)
+{
+ int err = 0;
+
+ phydev->advertising = phydev->drv->features;
+ phydev->supported = phydev->drv->features;
+
+ phydev->mmds = phydev->drv->mmds;
+
+ if (phydev->drv->probe)
+ err = phydev->drv->probe(phydev);
+
+ return err;
+}
+
+static struct phy_driver *generic_for_interface(phy_interface_t interface)
+{
+#ifdef CONFIG_PHYLIB_10G
+ if (is_10g_interface(interface))
+ return &gen10g_driver;
+#endif
+
+ return &genphy_driver;
+}
+
+static struct phy_driver *get_phy_driver(struct phy_device *phydev,
+ phy_interface_t interface)
+{
+ struct list_head *entry;
+ int phy_id = phydev->phy_id;
+ struct phy_driver *drv = NULL;
+
+ list_for_each(entry, &phy_drivers) {
+ drv = list_entry(entry, struct phy_driver, list);
+ if ((drv->uid & drv->mask) == (phy_id & drv->mask))
+ return drv;
+ }
+
+ /* If we made it here, there's no driver for this PHY */
+ return generic_for_interface(interface);
+}
+
+static struct phy_device *phy_device_create(struct mii_dev *bus, int addr,
+ u32 phy_id, bool is_c45,
+ phy_interface_t interface)
+{
+ struct phy_device *dev;
+
+ /*
+ * We allocate the device, and initialize the
+ * default values
+ */
+ dev = malloc(sizeof(*dev));
+ if (!dev) {
+ printf("Failed to allocate PHY device for %s:%d\n",
+ bus ? bus->name : "(null bus)", addr);
+ return NULL;
+ }
+
+ memset(dev, 0, sizeof(*dev));
+
+ dev->duplex = -1;
+ dev->link = 0;
+ dev->interface = interface;
+
+#ifdef CONFIG_DM_ETH
+ dev->node = ofnode_null();
+#endif
+
+ dev->autoneg = AUTONEG_ENABLE;
+
+ dev->addr = addr;
+ dev->phy_id = phy_id;
+ dev->is_c45 = is_c45;
+ dev->bus = bus;
+
+ dev->drv = get_phy_driver(dev, interface);
+
+ if (phy_probe(dev)) {
+ printf("%s, PHY probe failed\n", __func__);
+ return NULL;
+ }
+
+ if (addr >= 0 && addr < PHY_MAX_ADDR && phy_id != PHY_FIXED_ID)
+ bus->phymap[addr] = dev;
+
+ return dev;
+}
+
+/**
+ * get_phy_id - reads the specified addr for its ID.
+ * @bus: the target MII bus
+ * @addr: PHY address on the MII bus
+ * @phy_id: where to store the ID retrieved.
+ *
+ * Description: Reads the ID registers of the PHY at @addr on the
+ * @bus, stores it in @phy_id and returns zero on success.
+ */
+int __weak get_phy_id(struct mii_dev *bus, int addr, int devad, u32 *phy_id)
+{
+ int phy_reg;
+
+ /*
+ * Grab the bits from PHYIR1, and put them
+ * in the upper half
+ */
+ phy_reg = bus->read(bus, addr, devad, MII_PHYSID1);
+
+ if (phy_reg < 0)
+ return -EIO;
+
+ *phy_id = (phy_reg & 0xffff) << 16;
+
+ /* Grab the bits from PHYIR2, and put them in the lower half */
+ phy_reg = bus->read(bus, addr, devad, MII_PHYSID2);
+
+ if (phy_reg < 0)
+ return -EIO;
+
+ *phy_id |= (phy_reg & 0xffff);
+
+ return 0;
+}
+
+static struct phy_device *create_phy_by_mask(struct mii_dev *bus,
+ uint phy_mask, int devad,
+ phy_interface_t interface)
+{
+ u32 phy_id = 0xffffffff;
+ bool is_c45;
+
+ while (phy_mask) {
+ int addr = ffs(phy_mask) - 1;
+ int r = get_phy_id(bus, addr, devad, &phy_id);
+
+ /*
+ * If the PHY ID is flat 0 we ignore it. There are C45 PHYs
+ * that return all 0s for C22 reads (like Aquantia AQR112) and
+ * there are C22 PHYs that return all 0s for C45 reads (like
+ * Atheros AR8035).
+ */
+ if (r == 0 && phy_id == 0)
+ goto next;
+
+ /* If the PHY ID is mostly f's, we didn't find anything */
+ if (r == 0 && (phy_id & 0x1fffffff) != 0x1fffffff) {
+ is_c45 = (devad == MDIO_DEVAD_NONE) ? false : true;
+ return phy_device_create(bus, addr, phy_id, is_c45,
+ interface);
+ }
+next:
+ phy_mask &= ~(1 << addr);
+ }
+ return NULL;
+}
+
+static struct phy_device *search_for_existing_phy(struct mii_dev *bus,
+ uint phy_mask,
+ phy_interface_t interface)
+{
+ /* If we have one, return the existing device, with new interface */
+ while (phy_mask) {
+ int addr = ffs(phy_mask) - 1;
+
+ if (bus->phymap[addr]) {
+ bus->phymap[addr]->interface = interface;
+ return bus->phymap[addr];
+ }
+ phy_mask &= ~(1 << addr);
+ }
+ return NULL;
+}
+
+static struct phy_device *get_phy_device_by_mask(struct mii_dev *bus,
+ uint phy_mask,
+ phy_interface_t interface)
+{
+ struct phy_device *phydev;
+ int devad[] = {
+ /* Clause-22 */
+ MDIO_DEVAD_NONE,
+ /* Clause-45 */
+ MDIO_MMD_PMAPMD,
+ MDIO_MMD_WIS,
+ MDIO_MMD_PCS,
+ MDIO_MMD_PHYXS,
+ MDIO_MMD_VEND1,
+ };
+ int i, devad_cnt;
+
+ devad_cnt = sizeof(devad)/sizeof(int);
+ phydev = search_for_existing_phy(bus, phy_mask, interface);
+ if (phydev)
+ return phydev;
+ /* try different access clauses */
+ for (i = 0; i < devad_cnt; i++) {
+ phydev = create_phy_by_mask(bus, phy_mask,
+ devad[i], interface);
+ if (IS_ERR(phydev))
+ return NULL;
+ if (phydev)
+ return phydev;
+ }
+
+ debug("\n%s PHY: ", bus->name);
+ while (phy_mask) {
+ int addr = ffs(phy_mask) - 1;
+
+ debug("%d ", addr);
+ phy_mask &= ~(1 << addr);
+ }
+ debug("not found\n");
+
+ return NULL;
+}
+
+/**
+ * get_phy_device - reads the specified PHY device and returns its
+ * @phy_device struct
+ * @bus: the target MII bus
+ * @addr: PHY address on the MII bus
+ *
+ * Description: Reads the ID registers of the PHY at @addr on the
+ * @bus, then allocates and returns the phy_device to represent it.
+ */
+static struct phy_device *get_phy_device(struct mii_dev *bus, int addr,
+ phy_interface_t interface)
+{
+ return get_phy_device_by_mask(bus, 1 << addr, interface);
+}
+
+int phy_reset(struct phy_device *phydev)
+{
+ int reg;
+ int timeout = 500;
+ int devad = MDIO_DEVAD_NONE;
+
+ if (phydev->flags & PHY_FLAG_BROKEN_RESET)
+ return 0;
+
+#ifdef CONFIG_PHYLIB_10G
+ /* If it's 10G, we need to issue reset through one of the MMDs */
+ if (is_10g_interface(phydev->interface)) {
+ if (!phydev->mmds)
+ gen10g_discover_mmds(phydev);
+
+ devad = ffs(phydev->mmds) - 1;
+ }
+#endif
+
+ if (phy_write(phydev, devad, MII_BMCR, BMCR_RESET) < 0) {
+ debug("PHY reset failed\n");
+ return -1;
+ }
+
+#ifdef CONFIG_PHY_RESET_DELAY
+ udelay(CONFIG_PHY_RESET_DELAY); /* Intel LXT971A needs this */
+#endif
+ /*
+ * Poll the control register for the reset bit to go to 0 (it is
+ * auto-clearing). This should happen within 0.5 seconds per the
+ * IEEE spec.
+ */
+ reg = phy_read(phydev, devad, MII_BMCR);
+ while ((reg & BMCR_RESET) && timeout--) {
+ reg = phy_read(phydev, devad, MII_BMCR);
+
+ if (reg < 0) {
+ debug("PHY status read failed\n");
+ return -1;
+ }
+ udelay(1000);
+ }
+
+ if (reg & BMCR_RESET) {
+ puts("PHY reset timed out\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+int miiphy_reset(const char *devname, unsigned char addr)
+{
+ struct mii_dev *bus = miiphy_get_dev_by_name(devname);
+ struct phy_device *phydev;
+
+ /*
+ * miiphy_reset was only used on standard PHYs, so we'll fake it here.
+ * If later code tries to connect with the right interface, this will
+ * be corrected by get_phy_device in phy_connect()
+ */
+ phydev = get_phy_device(bus, addr, PHY_INTERFACE_MODE_MII);
+
+ return phy_reset(phydev);
+}
+
+struct phy_device *phy_find_by_mask(struct mii_dev *bus, uint phy_mask,
+ phy_interface_t interface)
+{
+ /* Reset the bus */
+ if (bus->reset) {
+ bus->reset(bus);
+
+ /* Wait 15ms to make sure the PHY has come out of hard reset */
+ mdelay(15);
+ }
+
+ return get_phy_device_by_mask(bus, phy_mask, interface);
+}
+
+#ifdef CONFIG_DM_ETH
+void phy_connect_dev(struct phy_device *phydev, struct udevice *dev)
+#else
+void phy_connect_dev(struct phy_device *phydev, struct eth_device *dev)
+#endif
+{
+ /* Soft Reset the PHY */
+ phy_reset(phydev);
+ if (phydev->dev && phydev->dev != dev) {
+ printf("%s:%d is connected to %s. Reconnecting to %s\n",
+ phydev->bus->name, phydev->addr,
+ phydev->dev->name, dev->name);
+ }
+ phydev->dev = dev;
+ debug("%s connected to %s\n", dev->name, phydev->drv->name);
+}
+
+#ifdef CONFIG_PHY_XILINX_GMII2RGMII
+static struct phy_device *phy_connect_gmii2rgmii(struct mii_dev *bus,
+ struct udevice *dev,
+ phy_interface_t interface)
+{
+ struct phy_device *phydev = NULL;
+ ofnode node;
+
+ ofnode_for_each_subnode(node, dev_ofnode(dev)) {
+ node = ofnode_by_compatible(node, "xlnx,gmii-to-rgmii-1.0");
+ if (ofnode_valid(node)) {
+ phydev = phy_device_create(bus, 0,
+ PHY_GMII2RGMII_ID, false,
+ interface);
+ if (phydev)
+ phydev->node = node;
+ break;
+ }
+
+ node = ofnode_first_subnode(node);
+ }
+
+ return phydev;
+}
+#endif
+
+#ifdef CONFIG_PHY_FIXED
+/**
+ * fixed_phy_create() - create an unconnected fixed-link pseudo-PHY device
+ * @node: OF node for the container of the fixed-link node
+ *
+ * Description: Creates a struct phy_device based on a fixed-link of_node
+ * description. Can be used without phy_connect by drivers which do not expose
+ * a UCLASS_ETH udevice.
+ */
+struct phy_device *fixed_phy_create(ofnode node)
+{
+ phy_interface_t interface = PHY_INTERFACE_MODE_NONE;
+ struct phy_device *phydev;
+ const char *if_str;
+ ofnode subnode;
+
+ if_str = ofnode_read_string(node, "phy-mode");
+ if (!if_str) {
+ if_str = ofnode_read_string(node, "phy-interface-type");
+ }
+ if (if_str) {
+ interface = phy_get_interface_by_name(if_str);
+ }
+
+ subnode = ofnode_find_subnode(node, "fixed-link");
+ if (!ofnode_valid(subnode)) {
+ return NULL;
+ }
+
+ phydev = phy_device_create(NULL, 0, PHY_FIXED_ID, false, interface);
+ if (phydev)
+ phydev->node = subnode;
+
+ return phydev;
+}
+
+static struct phy_device *phy_connect_fixed(struct mii_dev *bus,
+ struct udevice *dev,
+ phy_interface_t interface)
+{
+ ofnode node = dev_ofnode(dev), subnode;
+ struct phy_device *phydev = NULL;
+
+ if (ofnode_phy_is_fixed_link(node, &subnode)) {
+ phydev = phy_device_create(bus, 0, PHY_FIXED_ID,
+ false, interface);
+ if (phydev)
+ phydev->node = subnode;
+ }
+
+ return phydev;
+}
+#endif
+
+#ifdef CONFIG_DM_ETH
+struct phy_device *phy_connect(struct mii_dev *bus, int addr,
+ struct udevice *dev,
+ phy_interface_t interface)
+#else
+struct phy_device *phy_connect(struct mii_dev *bus, int addr,
+ struct eth_device *dev,
+ phy_interface_t interface)
+#endif
+{
+ struct phy_device *phydev = NULL;
+ uint mask = (addr >= 0) ? (1 << addr) : 0xffffffff;
+
+#ifdef CONFIG_PHY_FIXED
+ phydev = phy_connect_fixed(bus, dev, interface);
+#endif
+
+#ifdef CONFIG_PHY_NCSI
+ if (!phydev)
+ phydev = phy_device_create(bus, 0, PHY_NCSI_ID, false, interface);
+#endif
+
+#ifdef CONFIG_PHY_XILINX_GMII2RGMII
+ if (!phydev)
+ phydev = phy_connect_gmii2rgmii(bus, dev, interface);
+#endif
+
+ if (!phydev)
+ phydev = phy_find_by_mask(bus, mask, interface);
+
+ if (phydev)
+ phy_connect_dev(phydev, dev);
+ else
+ printf("Could not get PHY for %s: addr %d\n", bus->name, addr);
+ return phydev;
+}
+
+/*
+ * Start the PHY. Returns 0 on success, or a negative error code.
+ */
+int phy_startup(struct phy_device *phydev)
+{
+ if (phydev->drv->startup)
+ return phydev->drv->startup(phydev);
+
+ return 0;
+}
+
+__weak int board_phy_config(struct phy_device *phydev)
+{
+ if (phydev->drv->config)
+ return phydev->drv->config(phydev);
+ return 0;
+}
+
+int phy_config(struct phy_device *phydev)
+{
+ /* Invoke an optional board-specific helper */
+ return board_phy_config(phydev);
+}
+
+int phy_shutdown(struct phy_device *phydev)
+{
+ if (phydev->drv->shutdown)
+ phydev->drv->shutdown(phydev);
+
+ return 0;
+}
+
+int phy_get_interface_by_name(const char *str)
+{
+ int i;
+
+ for (i = 0; i < PHY_INTERFACE_MODE_COUNT; i++) {
+ if (!strcmp(str, phy_interface_strings[i]))
+ return i;
+ }
+
+ return -1;
+}
diff --git a/roms/u-boot/drivers/net/phy/realtek.c b/roms/u-boot/drivers/net/phy/realtek.c
new file mode 100644
index 000000000..b1b1fa508
--- /dev/null
+++ b/roms/u-boot/drivers/net/phy/realtek.c
@@ -0,0 +1,464 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * RealTek PHY drivers
+ *
+ * Copyright 2010-2011, 2015 Freescale Semiconductor, Inc.
+ * author Andy Fleming
+ * Copyright 2016 Karsten Merker <merker@debian.org>
+ */
+#include <common.h>
+#include <linux/bitops.h>
+#include <phy.h>
+#include <linux/delay.h>
+
+#define PHY_RTL8211x_FORCE_MASTER BIT(1)
+#define PHY_RTL8211E_PINE64_GIGABIT_FIX BIT(2)
+#define PHY_RTL8211F_FORCE_EEE_RXC_ON BIT(3)
+#define PHY_RTL8201F_S700_RMII_TIMINGS BIT(4)
+
+#define PHY_AUTONEGOTIATE_TIMEOUT 5000
+
+/* RTL8211x 1000BASE-T Control Register */
+#define MIIM_RTL8211x_CTRL1000T_MSCE BIT(12);
+#define MIIM_RTL8211x_CTRL1000T_MASTER BIT(11);
+
+/* RTL8211x PHY Status Register */
+#define MIIM_RTL8211x_PHY_STATUS 0x11
+#define MIIM_RTL8211x_PHYSTAT_SPEED 0xc000
+#define MIIM_RTL8211x_PHYSTAT_GBIT 0x8000
+#define MIIM_RTL8211x_PHYSTAT_100 0x4000
+#define MIIM_RTL8211x_PHYSTAT_DUPLEX 0x2000
+#define MIIM_RTL8211x_PHYSTAT_SPDDONE 0x0800
+#define MIIM_RTL8211x_PHYSTAT_LINK 0x0400
+
+/* RTL8211x PHY Interrupt Enable Register */
+#define MIIM_RTL8211x_PHY_INER 0x12
+#define MIIM_RTL8211x_PHY_INTR_ENA 0x9f01
+#define MIIM_RTL8211x_PHY_INTR_DIS 0x0000
+
+/* RTL8211x PHY Interrupt Status Register */
+#define MIIM_RTL8211x_PHY_INSR 0x13
+
+/* RTL8211F PHY Status Register */
+#define MIIM_RTL8211F_PHY_STATUS 0x1a
+#define MIIM_RTL8211F_AUTONEG_ENABLE 0x1000
+#define MIIM_RTL8211F_PHYSTAT_SPEED 0x0030
+#define MIIM_RTL8211F_PHYSTAT_GBIT 0x0020
+#define MIIM_RTL8211F_PHYSTAT_100 0x0010
+#define MIIM_RTL8211F_PHYSTAT_DUPLEX 0x0008
+#define MIIM_RTL8211F_PHYSTAT_SPDDONE 0x0800
+#define MIIM_RTL8211F_PHYSTAT_LINK 0x0004
+
+#define MIIM_RTL8211E_CONFREG 0x1c
+#define MIIM_RTL8211E_CONFREG_TXD 0x0002
+#define MIIM_RTL8211E_CONFREG_RXD 0x0004
+#define MIIM_RTL8211E_CONFREG_MAGIC 0xb400 /* Undocumented */
+
+#define MIIM_RTL8211E_EXT_PAGE_SELECT 0x1e
+
+#define MIIM_RTL8211F_PAGE_SELECT 0x1f
+#define MIIM_RTL8211F_TX_DELAY 0x100
+#define MIIM_RTL8211F_RX_DELAY 0x8
+#define MIIM_RTL8211F_LCR 0x10
+
+#define RTL8201F_RMSR 0x10
+
+#define RMSR_RX_TIMING_SHIFT BIT(2)
+#define RMSR_RX_TIMING_MASK GENMASK(7, 4)
+#define RMSR_RX_TIMING_VAL 0x4
+#define RMSR_TX_TIMING_SHIFT BIT(3)
+#define RMSR_TX_TIMING_MASK GENMASK(11, 8)
+#define RMSR_TX_TIMING_VAL 0x5
+
+static int rtl8211f_phy_extread(struct phy_device *phydev, int addr,
+ int devaddr, int regnum)
+{
+ int oldpage = phy_read(phydev, MDIO_DEVAD_NONE,
+ MIIM_RTL8211F_PAGE_SELECT);
+ int val;
+
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211F_PAGE_SELECT, devaddr);
+ val = phy_read(phydev, MDIO_DEVAD_NONE, regnum);
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211F_PAGE_SELECT, oldpage);
+
+ return val;
+}
+
+static int rtl8211f_phy_extwrite(struct phy_device *phydev, int addr,
+ int devaddr, int regnum, u16 val)
+{
+ int oldpage = phy_read(phydev, MDIO_DEVAD_NONE,
+ MIIM_RTL8211F_PAGE_SELECT);
+
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211F_PAGE_SELECT, devaddr);
+ phy_write(phydev, MDIO_DEVAD_NONE, regnum, val);
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211F_PAGE_SELECT, oldpage);
+
+ return 0;
+}
+
+static int rtl8211b_probe(struct phy_device *phydev)
+{
+#ifdef CONFIG_RTL8211X_PHY_FORCE_MASTER
+ phydev->flags |= PHY_RTL8211x_FORCE_MASTER;
+#endif
+
+ return 0;
+}
+
+static int rtl8211e_probe(struct phy_device *phydev)
+{
+#ifdef CONFIG_RTL8211E_PINE64_GIGABIT_FIX
+ phydev->flags |= PHY_RTL8211E_PINE64_GIGABIT_FIX;
+#endif
+
+ return 0;
+}
+
+static int rtl8211f_probe(struct phy_device *phydev)
+{
+#ifdef CONFIG_RTL8211F_PHY_FORCE_EEE_RXC_ON
+ phydev->flags |= PHY_RTL8211F_FORCE_EEE_RXC_ON;
+#endif
+
+ return 0;
+}
+
+static int rtl8210f_probe(struct phy_device *phydev)
+{
+#ifdef CONFIG_RTL8201F_PHY_S700_RMII_TIMINGS
+ phydev->flags |= PHY_RTL8201F_S700_RMII_TIMINGS;
+#endif
+
+ return 0;
+}
+
+/* RealTek RTL8211x */
+static int rtl8211x_config(struct phy_device *phydev)
+{
+ phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_RESET);
+
+ /* mask interrupt at init; if the interrupt is
+ * needed indeed, it should be explicitly enabled
+ */
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211x_PHY_INER,
+ MIIM_RTL8211x_PHY_INTR_DIS);
+
+ if (phydev->flags & PHY_RTL8211x_FORCE_MASTER) {
+ unsigned int reg;
+
+ reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_CTRL1000);
+ /* force manual master/slave configuration */
+ reg |= MIIM_RTL8211x_CTRL1000T_MSCE;
+ /* force master mode */
+ reg |= MIIM_RTL8211x_CTRL1000T_MASTER;
+ phy_write(phydev, MDIO_DEVAD_NONE, MII_CTRL1000, reg);
+ }
+ if (phydev->flags & PHY_RTL8211E_PINE64_GIGABIT_FIX) {
+ unsigned int reg;
+
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211F_PAGE_SELECT,
+ 7);
+ phy_write(phydev, MDIO_DEVAD_NONE,
+ MIIM_RTL8211E_EXT_PAGE_SELECT, 0xa4);
+ reg = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211E_CONFREG);
+ /* Ensure both internal delays are turned off */
+ reg &= ~(MIIM_RTL8211E_CONFREG_TXD | MIIM_RTL8211E_CONFREG_RXD);
+ /* Flip the magic undocumented bits */
+ reg |= MIIM_RTL8211E_CONFREG_MAGIC;
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211E_CONFREG, reg);
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211F_PAGE_SELECT,
+ 0);
+ }
+ /* read interrupt status just to clear it */
+ phy_read(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211x_PHY_INER);
+
+ genphy_config_aneg(phydev);
+
+ return 0;
+}
+
+/* RealTek RTL8201F */
+static int rtl8201f_config(struct phy_device *phydev)
+{
+ unsigned int reg;
+
+ if (phydev->flags & PHY_RTL8201F_S700_RMII_TIMINGS) {
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211F_PAGE_SELECT,
+ 7);
+ reg = phy_read(phydev, MDIO_DEVAD_NONE, RTL8201F_RMSR);
+ reg &= ~(RMSR_RX_TIMING_MASK | RMSR_TX_TIMING_MASK);
+ /* Set the needed Rx/Tx Timings for proper PHY operation */
+ reg |= (RMSR_RX_TIMING_VAL << RMSR_RX_TIMING_SHIFT)
+ | (RMSR_TX_TIMING_VAL << RMSR_TX_TIMING_SHIFT);
+ phy_write(phydev, MDIO_DEVAD_NONE, RTL8201F_RMSR, reg);
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211F_PAGE_SELECT,
+ 0);
+ }
+
+ genphy_config_aneg(phydev);
+
+ return 0;
+}
+
+static int rtl8211f_config(struct phy_device *phydev)
+{
+ u16 reg;
+
+ if (phydev->flags & PHY_RTL8211F_FORCE_EEE_RXC_ON) {
+ unsigned int reg;
+
+ reg = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1);
+ reg &= ~MDIO_PCS_CTRL1_CLKSTOP_EN;
+ phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1, reg);
+ }
+
+ phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_RESET);
+
+ phy_write(phydev, MDIO_DEVAD_NONE,
+ MIIM_RTL8211F_PAGE_SELECT, 0xd08);
+ reg = phy_read(phydev, MDIO_DEVAD_NONE, 0x11);
+
+ /* enable TX-delay for rgmii-id and rgmii-txid, otherwise disable it */
+ if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
+ phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
+ reg |= MIIM_RTL8211F_TX_DELAY;
+ else
+ reg &= ~MIIM_RTL8211F_TX_DELAY;
+
+ phy_write(phydev, MDIO_DEVAD_NONE, 0x11, reg);
+
+ /* enable RX-delay for rgmii-id and rgmii-rxid, otherwise disable it */
+ reg = phy_read(phydev, MDIO_DEVAD_NONE, 0x15);
+ if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
+ phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
+ reg |= MIIM_RTL8211F_RX_DELAY;
+ else
+ reg &= ~MIIM_RTL8211F_RX_DELAY;
+ phy_write(phydev, MDIO_DEVAD_NONE, 0x15, reg);
+
+ /* restore to default page 0 */
+ phy_write(phydev, MDIO_DEVAD_NONE,
+ MIIM_RTL8211F_PAGE_SELECT, 0x0);
+
+ /* Set green LED for Link, yellow LED for Active */
+ phy_write(phydev, MDIO_DEVAD_NONE,
+ MIIM_RTL8211F_PAGE_SELECT, 0xd04);
+ phy_write(phydev, MDIO_DEVAD_NONE, 0x10, 0x617f);
+ phy_write(phydev, MDIO_DEVAD_NONE,
+ MIIM_RTL8211F_PAGE_SELECT, 0x0);
+
+ genphy_config_aneg(phydev);
+
+ return 0;
+}
+
+static int rtl8211x_parse_status(struct phy_device *phydev)
+{
+ unsigned int speed;
+ unsigned int mii_reg;
+
+ mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211x_PHY_STATUS);
+
+ if (!(mii_reg & MIIM_RTL8211x_PHYSTAT_SPDDONE)) {
+ int i = 0;
+
+ /* in case of timeout ->link is cleared */
+ phydev->link = 1;
+ puts("Waiting for PHY realtime link");
+ while (!(mii_reg & MIIM_RTL8211x_PHYSTAT_SPDDONE)) {
+ /* Timeout reached ? */
+ if (i > PHY_AUTONEGOTIATE_TIMEOUT) {
+ puts(" TIMEOUT !\n");
+ phydev->link = 0;
+ break;
+ }
+
+ if ((i++ % 1000) == 0)
+ putc('.');
+ udelay(1000); /* 1 ms */
+ mii_reg = phy_read(phydev, MDIO_DEVAD_NONE,
+ MIIM_RTL8211x_PHY_STATUS);
+ }
+ puts(" done\n");
+ udelay(500000); /* another 500 ms (results in faster booting) */
+ } else {
+ if (mii_reg & MIIM_RTL8211x_PHYSTAT_LINK)
+ phydev->link = 1;
+ else
+ phydev->link = 0;
+ }
+
+ if (mii_reg & MIIM_RTL8211x_PHYSTAT_DUPLEX)
+ phydev->duplex = DUPLEX_FULL;
+ else
+ phydev->duplex = DUPLEX_HALF;
+
+ speed = (mii_reg & MIIM_RTL8211x_PHYSTAT_SPEED);
+
+ switch (speed) {
+ case MIIM_RTL8211x_PHYSTAT_GBIT:
+ phydev->speed = SPEED_1000;
+ break;
+ case MIIM_RTL8211x_PHYSTAT_100:
+ phydev->speed = SPEED_100;
+ break;
+ default:
+ phydev->speed = SPEED_10;
+ }
+
+ return 0;
+}
+
+static int rtl8211f_parse_status(struct phy_device *phydev)
+{
+ unsigned int speed;
+ unsigned int mii_reg;
+ int i = 0;
+
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211F_PAGE_SELECT, 0xa43);
+ mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211F_PHY_STATUS);
+
+ phydev->link = 1;
+ while (!(mii_reg & MIIM_RTL8211F_PHYSTAT_LINK)) {
+ if (i > PHY_AUTONEGOTIATE_TIMEOUT) {
+ puts(" TIMEOUT !\n");
+ phydev->link = 0;
+ break;
+ }
+
+ if ((i++ % 1000) == 0)
+ putc('.');
+ udelay(1000);
+ mii_reg = phy_read(phydev, MDIO_DEVAD_NONE,
+ MIIM_RTL8211F_PHY_STATUS);
+ }
+
+ if (mii_reg & MIIM_RTL8211F_PHYSTAT_DUPLEX)
+ phydev->duplex = DUPLEX_FULL;
+ else
+ phydev->duplex = DUPLEX_HALF;
+
+ speed = (mii_reg & MIIM_RTL8211F_PHYSTAT_SPEED);
+
+ switch (speed) {
+ case MIIM_RTL8211F_PHYSTAT_GBIT:
+ phydev->speed = SPEED_1000;
+ break;
+ case MIIM_RTL8211F_PHYSTAT_100:
+ phydev->speed = SPEED_100;
+ break;
+ default:
+ phydev->speed = SPEED_10;
+ }
+
+ return 0;
+}
+
+static int rtl8211x_startup(struct phy_device *phydev)
+{
+ int ret;
+
+ /* Read the Status (2x to make sure link is right) */
+ ret = genphy_update_link(phydev);
+ if (ret)
+ return ret;
+
+ return rtl8211x_parse_status(phydev);
+}
+
+static int rtl8211e_startup(struct phy_device *phydev)
+{
+ int ret;
+
+ ret = genphy_update_link(phydev);
+ if (ret)
+ return ret;
+
+ return genphy_parse_link(phydev);
+}
+
+static int rtl8211f_startup(struct phy_device *phydev)
+{
+ int ret;
+
+ /* Read the Status (2x to make sure link is right) */
+ ret = genphy_update_link(phydev);
+ if (ret)
+ return ret;
+ /* Read the Status (2x to make sure link is right) */
+
+ return rtl8211f_parse_status(phydev);
+}
+
+/* Support for RTL8211B PHY */
+static struct phy_driver RTL8211B_driver = {
+ .name = "RealTek RTL8211B",
+ .uid = 0x1cc912,
+ .mask = 0xffffff,
+ .features = PHY_GBIT_FEATURES,
+ .probe = &rtl8211b_probe,
+ .config = &rtl8211x_config,
+ .startup = &rtl8211x_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+/* Support for RTL8211E-VB-CG, RTL8211E-VL-CG and RTL8211EG-VB-CG PHYs */
+static struct phy_driver RTL8211E_driver = {
+ .name = "RealTek RTL8211E",
+ .uid = 0x1cc915,
+ .mask = 0xffffff,
+ .features = PHY_GBIT_FEATURES,
+ .probe = &rtl8211e_probe,
+ .config = &rtl8211x_config,
+ .startup = &rtl8211e_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+/* Support for RTL8211DN PHY */
+static struct phy_driver RTL8211DN_driver = {
+ .name = "RealTek RTL8211DN",
+ .uid = 0x1cc914,
+ .mask = 0xffffff,
+ .features = PHY_GBIT_FEATURES,
+ .config = &rtl8211x_config,
+ .startup = &rtl8211x_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+/* Support for RTL8211F PHY */
+static struct phy_driver RTL8211F_driver = {
+ .name = "RealTek RTL8211F",
+ .uid = 0x1cc916,
+ .mask = 0xffffff,
+ .features = PHY_GBIT_FEATURES,
+ .probe = &rtl8211f_probe,
+ .config = &rtl8211f_config,
+ .startup = &rtl8211f_startup,
+ .shutdown = &genphy_shutdown,
+ .readext = &rtl8211f_phy_extread,
+ .writeext = &rtl8211f_phy_extwrite,
+};
+
+/* Support for RTL8201F PHY */
+static struct phy_driver RTL8201F_driver = {
+ .name = "RealTek RTL8201F 10/100Mbps Ethernet",
+ .uid = 0x1cc816,
+ .mask = 0xffffff,
+ .features = PHY_BASIC_FEATURES,
+ .probe = &rtl8210f_probe,
+ .config = &rtl8201f_config,
+ .startup = &rtl8211e_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+int phy_realtek_init(void)
+{
+ phy_register(&RTL8211B_driver);
+ phy_register(&RTL8211E_driver);
+ phy_register(&RTL8211F_driver);
+ phy_register(&RTL8211DN_driver);
+ phy_register(&RTL8201F_driver);
+
+ return 0;
+}
diff --git a/roms/u-boot/drivers/net/phy/smsc.c b/roms/u-boot/drivers/net/phy/smsc.c
new file mode 100644
index 000000000..7740a2510
--- /dev/null
+++ b/roms/u-boot/drivers/net/phy/smsc.c
@@ -0,0 +1,116 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * SMSC PHY drivers
+ *
+ * Base code from drivers/net/phy/davicom.c
+ * Copyright 2010-2011 Freescale Semiconductor, Inc.
+ * author Andy Fleming
+ *
+ * Some code copied from linux kernel
+ * Copyright (c) 2006 Herbert Valerio Riedel <hvr@gnu.org>
+ */
+#include <common.h>
+#include <miiphy.h>
+
+/* This code does not check the partner abilities. */
+static int smsc_parse_status(struct phy_device *phydev)
+{
+ int mii_reg;
+
+ mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR);
+
+ if (mii_reg & (BMSR_100FULL | BMSR_100HALF))
+ phydev->speed = SPEED_100;
+ else
+ phydev->speed = SPEED_10;
+
+ if (mii_reg & (BMSR_10FULL | BMSR_100FULL))
+ phydev->duplex = DUPLEX_FULL;
+ else
+ phydev->duplex = DUPLEX_HALF;
+
+ return 0;
+}
+
+static int smsc_startup(struct phy_device *phydev)
+{
+ int ret;
+
+ ret = genphy_update_link(phydev);
+ if (ret)
+ return ret;
+
+ return smsc_parse_status(phydev);
+}
+
+static struct phy_driver lan8700_driver = {
+ .name = "SMSC LAN8700",
+ .uid = 0x0007c0c0,
+ .mask = 0xffff0,
+ .features = PHY_BASIC_FEATURES,
+ .config = &genphy_config_aneg,
+ .startup = &smsc_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+static struct phy_driver lan911x_driver = {
+ .name = "SMSC LAN911x Internal PHY",
+ .uid = 0x0007c0d0,
+ .mask = 0xffff0,
+ .features = PHY_BASIC_FEATURES,
+ .config = &genphy_config_aneg,
+ .startup = &smsc_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+static struct phy_driver lan8710_driver = {
+ .name = "SMSC LAN8710/LAN8720",
+ .uid = 0x0007c0f0,
+ .mask = 0xffff0,
+ .features = PHY_BASIC_FEATURES,
+ .config = &genphy_config_aneg,
+ .startup = &genphy_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+static struct phy_driver lan8740_driver = {
+ .name = "SMSC LAN8740",
+ .uid = 0x0007c110,
+ .mask = 0xffff0,
+ .features = PHY_BASIC_FEATURES,
+ .config = &genphy_config_aneg,
+ .startup = &genphy_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+static struct phy_driver lan8741_driver = {
+ .name = "SMSC LAN8741",
+ .uid = 0x0007c120,
+ .mask = 0xffff0,
+ .features = PHY_BASIC_FEATURES,
+ .config = &genphy_config_aneg,
+ .startup = &genphy_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+static struct phy_driver lan8742_driver = {
+ .name = "SMSC LAN8742",
+ .uid = 0x0007c130,
+ .mask = 0xffff0,
+ .features = PHY_BASIC_FEATURES,
+ .config = &genphy_config_aneg,
+ .startup = &genphy_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+int phy_smsc_init(void)
+{
+ phy_register(&lan8710_driver);
+ phy_register(&lan911x_driver);
+ phy_register(&lan8700_driver);
+ phy_register(&lan8740_driver);
+ phy_register(&lan8741_driver);
+ phy_register(&lan8742_driver);
+
+ return 0;
+}
diff --git a/roms/u-boot/drivers/net/phy/teranetics.c b/roms/u-boot/drivers/net/phy/teranetics.c
new file mode 100644
index 000000000..60049c207
--- /dev/null
+++ b/roms/u-boot/drivers/net/phy/teranetics.c
@@ -0,0 +1,111 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Teranetics PHY drivers
+ *
+ * Copyright 2010-2011 Freescale Semiconductor, Inc.
+ * author Andy Fleming
+ */
+#include <common.h>
+#include <phy.h>
+#include <linux/delay.h>
+
+#ifndef CONFIG_PHYLIB_10G
+#error The Teranetics PHY needs 10G support
+#endif
+
+int tn2020_config(struct phy_device *phydev)
+{
+ if (phydev->port == PORT_FIBRE) {
+ unsigned short restart_an = (MDIO_AN_CTRL1_RESTART |
+ MDIO_AN_CTRL1_ENABLE |
+ MDIO_AN_CTRL1_XNP);
+ u8 phy_hwversion;
+
+ /*
+ * bit 15:12 of register 30.32 indicates PHY hardware
+ * version. It can be used to distinguish TN80xx from
+ * TN2020. TN2020 needs write 0x2 to 30.93, but TN80xx
+ * needs 0x1.
+ */
+ phy_hwversion = (phy_read(phydev, 30, 32) >> 12) & 0xf;
+ if (phy_hwversion <= 3) {
+ phy_write(phydev, 30, 93, 2);
+ phy_write(phydev, MDIO_MMD_AN, MDIO_CTRL1, restart_an);
+ } else {
+ phy_write(phydev, 30, 93, 1);
+ }
+ }
+
+ return 0;
+}
+
+int tn2020_startup(struct phy_device *phydev)
+{
+ unsigned int timeout = 5 * 1000; /* 5 second timeout */
+
+#define MDIO_PHYXS_LANE_READY (MDIO_PHYXS_LNSTAT_SYNC0 | \
+ MDIO_PHYXS_LNSTAT_SYNC1 | \
+ MDIO_PHYXS_LNSTAT_SYNC2 | \
+ MDIO_PHYXS_LNSTAT_SYNC3 | \
+ MDIO_PHYXS_LNSTAT_ALIGN)
+
+ /*
+ * Wait for the XAUI-SERDES lanes to align first. Under normal
+ * circumstances, this can take up to three seconds.
+ */
+ while (--timeout) {
+ int reg = phy_read(phydev, MDIO_MMD_PHYXS, MDIO_PHYXS_LNSTAT);
+ if (reg < 0) {
+ printf("TN2020: Error reading from PHY at "
+ "address %u\n", phydev->addr);
+ break;
+ }
+ if ((reg & MDIO_PHYXS_LANE_READY) == MDIO_PHYXS_LANE_READY)
+ break;
+ udelay(1000);
+ }
+ if (!timeout) {
+ /*
+ * A timeout is bad, but it may not be fatal, so don't
+ * return an error. Display a warning instead.
+ */
+ printf("TN2020: Timeout waiting for PHY at address %u to "
+ "align.\n", phydev->addr);
+ }
+
+ if (phydev->port != PORT_FIBRE)
+ return gen10g_startup(phydev);
+
+ /*
+ * The TN2020 only pretends to support fiber.
+ * It works, but it doesn't look like it works,
+ * so the link status reports no link.
+ */
+ phydev->link = 1;
+
+ /* For now just lie and say it's 10G all the time */
+ phydev->speed = SPEED_10000;
+ phydev->duplex = DUPLEX_FULL;
+
+ return 0;
+}
+
+struct phy_driver tn2020_driver = {
+ .name = "Teranetics TN2020",
+ .uid = PHY_UID_TN2020,
+ .mask = 0xfffffff0,
+ .features = PHY_10G_FEATURES,
+ .mmds = (MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS |
+ MDIO_DEVS_PHYXS | MDIO_DEVS_AN |
+ MDIO_DEVS_VEND1 | MDIO_DEVS_VEND2),
+ .config = &tn2020_config,
+ .startup = &tn2020_startup,
+ .shutdown = &gen10g_shutdown,
+};
+
+int phy_teranetics_init(void)
+{
+ phy_register(&tn2020_driver);
+
+ return 0;
+}
diff --git a/roms/u-boot/drivers/net/phy/ti_phy_init.c b/roms/u-boot/drivers/net/phy/ti_phy_init.c
new file mode 100644
index 000000000..50eff7769
--- /dev/null
+++ b/roms/u-boot/drivers/net/phy/ti_phy_init.c
@@ -0,0 +1,101 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * TI Generic PHY Init to register any TI Ethernet PHYs
+ *
+ * Author: Dan Murphy <dmurphy@ti.com>
+ *
+ * Copyright (C) 2019-20 Texas Instruments Inc.
+ */
+
+#include <phy.h>
+#include "ti_phy_init.h"
+
+#ifdef CONFIG_PHY_TI_GENERIC
+static struct phy_driver dp83822_driver = {
+ .name = "TI DP83822",
+ .uid = 0x2000a240,
+ .mask = 0xfffffff0,
+ .features = PHY_BASIC_FEATURES,
+ .config = &genphy_config_aneg,
+ .startup = &genphy_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+static struct phy_driver dp83826nc_driver = {
+ .name = "TI DP83826NC",
+ .uid = 0x2000a110,
+ .mask = 0xfffffff0,
+ .features = PHY_BASIC_FEATURES,
+ .config = &genphy_config_aneg,
+ .startup = &genphy_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+static struct phy_driver dp83826c_driver = {
+ .name = "TI DP83826C",
+ .uid = 0x2000a130,
+ .mask = 0xfffffff0,
+ .features = PHY_BASIC_FEATURES,
+ .config = &genphy_config_aneg,
+ .startup = &genphy_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+static struct phy_driver dp83825s_driver = {
+ .name = "TI DP83825S",
+ .uid = 0x2000a140,
+ .mask = 0xfffffff0,
+ .features = PHY_BASIC_FEATURES,
+ .config = &genphy_config_aneg,
+ .startup = &genphy_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+static struct phy_driver dp83825i_driver = {
+ .name = "TI DP83825I",
+ .uid = 0x2000a150,
+ .mask = 0xfffffff0,
+ .features = PHY_BASIC_FEATURES,
+ .config = &genphy_config_aneg,
+ .startup = &genphy_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+static struct phy_driver dp83825m_driver = {
+ .name = "TI DP83825M",
+ .uid = 0x2000a160,
+ .mask = 0xfffffff0,
+ .features = PHY_BASIC_FEATURES,
+ .config = &genphy_config_aneg,
+ .startup = &genphy_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+static struct phy_driver dp83825cs_driver = {
+ .name = "TI DP83825CS",
+ .uid = 0x2000a170,
+ .mask = 0xfffffff0,
+ .features = PHY_BASIC_FEATURES,
+ .config = &genphy_config_aneg,
+ .startup = &genphy_startup,
+ .shutdown = &genphy_shutdown,
+};
+#endif /* CONFIG_PHY_TI_GENERIC */
+
+int phy_ti_init(void)
+{
+#ifdef CONFIG_PHY_TI_DP83867
+ phy_dp83867_init();
+#endif
+
+#ifdef CONFIG_PHY_TI_GENERIC
+ phy_register(&dp83822_driver);
+ phy_register(&dp83825s_driver);
+ phy_register(&dp83825i_driver);
+ phy_register(&dp83825m_driver);
+ phy_register(&dp83825cs_driver);
+ phy_register(&dp83826c_driver);
+ phy_register(&dp83826nc_driver);
+#endif
+ return 0;
+}
diff --git a/roms/u-boot/drivers/net/phy/ti_phy_init.h b/roms/u-boot/drivers/net/phy/ti_phy_init.h
new file mode 100644
index 000000000..6c7f6c640
--- /dev/null
+++ b/roms/u-boot/drivers/net/phy/ti_phy_init.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * TI Generic Ethernet PHY
+ *
+ * Author: Dan Murphy <dmurphy@ti.com>
+ *
+ * Copyright (C) 2019-20 Texas Instruments Inc.
+ */
+
+#ifndef _TI_GEN_PHY_H
+#define _TI_GEN_PHY_H
+
+int phy_dp83867_init(void);
+
+#endif /* _TI_GEN_PHY_H */
diff --git a/roms/u-boot/drivers/net/phy/vitesse.c b/roms/u-boot/drivers/net/phy/vitesse.c
new file mode 100644
index 000000000..eca26c989
--- /dev/null
+++ b/roms/u-boot/drivers/net/phy/vitesse.c
@@ -0,0 +1,444 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Vitesse PHY drivers
+ *
+ * Copyright 2010-2014 Freescale Semiconductor, Inc.
+ * Original Author: Andy Fleming
+ * Add vsc8662 phy support - Priyanka Jain
+ */
+#include <common.h>
+#include <miiphy.h>
+
+/* Cicada Auxiliary Control/Status Register */
+#define MIIM_CIS82xx_AUX_CONSTAT 0x1c
+#define MIIM_CIS82xx_AUXCONSTAT_INIT 0x0004
+#define MIIM_CIS82xx_AUXCONSTAT_DUPLEX 0x0020
+#define MIIM_CIS82xx_AUXCONSTAT_SPEED 0x0018
+#define MIIM_CIS82xx_AUXCONSTAT_GBIT 0x0010
+#define MIIM_CIS82xx_AUXCONSTAT_100 0x0008
+
+/* Cicada Extended Control Register 1 */
+#define MIIM_CIS82xx_EXT_CON1 0x17
+#define MIIM_CIS8201_EXTCON1_INIT 0x0000
+
+/* Cicada 8204 Extended PHY Control Register 1 */
+#define MIIM_CIS8204_EPHY_CON 0x17
+#define MIIM_CIS8204_EPHYCON_INIT 0x0006
+#define MIIM_CIS8204_EPHYCON_RGMII 0x1100
+
+/* Cicada 8204 Serial LED Control Register */
+#define MIIM_CIS8204_SLED_CON 0x1b
+#define MIIM_CIS8204_SLEDCON_INIT 0x1115
+
+/* Vitesse VSC8601 Extended PHY Control Register 1 */
+#define MII_VSC8601_EPHY_CTL 0x17
+#define MII_VSC8601_EPHY_CTL_RGMII_SKEW (1 << 8)
+
+#define PHY_EXT_PAGE_ACCESS 0x1f
+#define PHY_EXT_PAGE_ACCESS_GENERAL 0x10
+#define PHY_EXT_PAGE_ACCESS_EXTENDED3 0x3
+
+/* Vitesse VSC8574 control register */
+#define MIIM_VSC8574_MAC_SERDES_CON 0x10
+#define MIIM_VSC8574_MAC_SERDES_ANEG 0x80
+#define MIIM_VSC8574_GENERAL18 0x12
+#define MIIM_VSC8574_GENERAL19 0x13
+
+/* Vitesse VSC8574 gerenal purpose register 18 */
+#define MIIM_VSC8574_18G_SGMII 0x80f0
+#define MIIM_VSC8574_18G_QSGMII 0x80e0
+#define MIIM_VSC8574_18G_CMDSTAT 0x8000
+
+/* Vitesse VSC8514 control register */
+#define MIIM_VSC8514_MAC_SERDES_CON 0x10
+#define MIIM_VSC8514_GENERAL18 0x12
+#define MIIM_VSC8514_GENERAL19 0x13
+#define MIIM_VSC8514_GENERAL23 0x17
+
+/* Vitesse VSC8514 gerenal purpose register 18 */
+#define MIIM_VSC8514_18G_QSGMII 0x80e0
+#define MIIM_VSC8514_18G_CMDSTAT 0x8000
+
+/* Vitesse VSC8664 Control/Status Register */
+#define MIIM_VSC8664_SERDES_AND_SIGDET 0x13
+#define MIIM_VSC8664_ADDITIONAL_DEV 0x16
+#define MIIM_VSC8664_EPHY_CON 0x17
+#define MIIM_VSC8664_LED_CON 0x1E
+
+#define PHY_EXT_PAGE_ACCESS_EXTENDED 0x0001
+
+/* CIS8201 */
+static int vitesse_config(struct phy_device *phydev)
+{
+ /* Override PHY config settings */
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_CIS82xx_AUX_CONSTAT,
+ MIIM_CIS82xx_AUXCONSTAT_INIT);
+ /* Set up the interface mode */
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_CIS82xx_EXT_CON1,
+ MIIM_CIS8201_EXTCON1_INIT);
+
+ genphy_config_aneg(phydev);
+
+ return 0;
+}
+
+static int vitesse_parse_status(struct phy_device *phydev)
+{
+ int speed;
+ int mii_reg;
+
+ mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_CIS82xx_AUX_CONSTAT);
+
+ if (mii_reg & MIIM_CIS82xx_AUXCONSTAT_DUPLEX)
+ phydev->duplex = DUPLEX_FULL;
+ else
+ phydev->duplex = DUPLEX_HALF;
+
+ speed = mii_reg & MIIM_CIS82xx_AUXCONSTAT_SPEED;
+ switch (speed) {
+ case MIIM_CIS82xx_AUXCONSTAT_GBIT:
+ phydev->speed = SPEED_1000;
+ break;
+ case MIIM_CIS82xx_AUXCONSTAT_100:
+ phydev->speed = SPEED_100;
+ break;
+ default:
+ phydev->speed = SPEED_10;
+ break;
+ }
+
+ return 0;
+}
+
+static int vitesse_startup(struct phy_device *phydev)
+{
+ int ret;
+
+ ret = genphy_update_link(phydev);
+ if (ret)
+ return ret;
+ return vitesse_parse_status(phydev);
+}
+
+static int cis8204_config(struct phy_device *phydev)
+{
+ /* Override PHY config settings */
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_CIS82xx_AUX_CONSTAT,
+ MIIM_CIS82xx_AUXCONSTAT_INIT);
+
+ genphy_config_aneg(phydev);
+
+ if (phy_interface_is_rgmii(phydev))
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_CIS8204_EPHY_CON,
+ MIIM_CIS8204_EPHYCON_INIT |
+ MIIM_CIS8204_EPHYCON_RGMII);
+ else
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_CIS8204_EPHY_CON,
+ MIIM_CIS8204_EPHYCON_INIT);
+
+ return 0;
+}
+
+/* Vitesse VSC8601 */
+/* This adds a skew for both TX and RX clocks, so the skew should only be
+ * applied to "rgmii-id" interfaces. It may not work as expected
+ * on "rgmii-txid", "rgmii-rxid" or "rgmii" interfaces. */
+static int vsc8601_add_skew(struct phy_device *phydev)
+{
+ int ret;
+
+ ret = phy_read(phydev, MDIO_DEVAD_NONE, MII_VSC8601_EPHY_CTL);
+ if (ret < 0)
+ return ret;
+
+ ret |= MII_VSC8601_EPHY_CTL_RGMII_SKEW;
+ return phy_write(phydev, MDIO_DEVAD_NONE, MII_VSC8601_EPHY_CTL, ret);
+}
+
+static int vsc8601_config(struct phy_device *phydev)
+{
+ int ret = 0;
+
+ if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
+ ret = vsc8601_add_skew(phydev);
+
+ if (ret < 0)
+ return ret;
+
+ return genphy_config_aneg(phydev);
+}
+
+static int vsc8574_config(struct phy_device *phydev)
+{
+ u32 val;
+ /* configure register 19G for MAC */
+ phy_write(phydev, MDIO_DEVAD_NONE, PHY_EXT_PAGE_ACCESS,
+ PHY_EXT_PAGE_ACCESS_GENERAL);
+
+ val = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_VSC8574_GENERAL19);
+ if (phydev->interface == PHY_INTERFACE_MODE_QSGMII) {
+ /* set bit 15:14 to '01' for QSGMII mode */
+ val = (val & 0x3fff) | (1 << 14);
+ phy_write(phydev, MDIO_DEVAD_NONE,
+ MIIM_VSC8574_GENERAL19, val);
+ /* Enable 4 ports MAC QSGMII */
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_VSC8574_GENERAL18,
+ MIIM_VSC8574_18G_QSGMII);
+ } else {
+ /* set bit 15:14 to '00' for SGMII mode */
+ val = val & 0x3fff;
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_VSC8574_GENERAL19, val);
+ /* Enable 4 ports MAC SGMII */
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_VSC8574_GENERAL18,
+ MIIM_VSC8574_18G_SGMII);
+ }
+ val = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_VSC8574_GENERAL18);
+ /* When bit 15 is cleared the command has completed */
+ while (val & MIIM_VSC8574_18G_CMDSTAT)
+ val = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_VSC8574_GENERAL18);
+
+ /* Enable Serdes Auto-negotiation */
+ phy_write(phydev, MDIO_DEVAD_NONE, PHY_EXT_PAGE_ACCESS,
+ PHY_EXT_PAGE_ACCESS_EXTENDED3);
+ val = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_VSC8574_MAC_SERDES_CON);
+ val = val | MIIM_VSC8574_MAC_SERDES_ANEG;
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_VSC8574_MAC_SERDES_CON, val);
+
+ phy_write(phydev, MDIO_DEVAD_NONE, PHY_EXT_PAGE_ACCESS, 0);
+
+ genphy_config_aneg(phydev);
+
+ return 0;
+}
+
+static int vsc8514_config(struct phy_device *phydev)
+{
+ u32 val;
+ int timeout = 1000000;
+
+ /* configure register to access 19G */
+ phy_write(phydev, MDIO_DEVAD_NONE, PHY_EXT_PAGE_ACCESS,
+ PHY_EXT_PAGE_ACCESS_GENERAL);
+
+ val = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_VSC8514_GENERAL19);
+ if (phydev->interface == PHY_INTERFACE_MODE_QSGMII) {
+ /* set bit 15:14 to '01' for QSGMII mode */
+ val = (val & 0x3fff) | (1 << 14);
+ phy_write(phydev, MDIO_DEVAD_NONE,
+ MIIM_VSC8514_GENERAL19, val);
+ /* Enable 4 ports MAC QSGMII */
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_VSC8514_GENERAL18,
+ MIIM_VSC8514_18G_QSGMII);
+ } else {
+ /*TODO Add SGMII functionality once spec sheet
+ * for VSC8514 defines complete functionality
+ */
+ }
+
+ val = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_VSC8514_GENERAL18);
+ /* When bit 15 is cleared the command has completed */
+ while ((val & MIIM_VSC8514_18G_CMDSTAT) && timeout--)
+ val = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_VSC8514_GENERAL18);
+
+ if (0 == timeout) {
+ printf("PHY 8514 config failed\n");
+ return -1;
+ }
+
+ phy_write(phydev, MDIO_DEVAD_NONE, PHY_EXT_PAGE_ACCESS, 0);
+
+ /* configure register to access 23 */
+ val = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_VSC8514_GENERAL23);
+ /* set bits 10:8 to '000' */
+ val = (val & 0xf8ff);
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_VSC8514_GENERAL23, val);
+
+ /* Enable Serdes Auto-negotiation */
+ phy_write(phydev, MDIO_DEVAD_NONE, PHY_EXT_PAGE_ACCESS,
+ PHY_EXT_PAGE_ACCESS_EXTENDED3);
+ val = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_VSC8514_MAC_SERDES_CON);
+ val = val | MIIM_VSC8574_MAC_SERDES_ANEG;
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_VSC8514_MAC_SERDES_CON, val);
+ phy_write(phydev, MDIO_DEVAD_NONE, PHY_EXT_PAGE_ACCESS, 0);
+
+ genphy_config_aneg(phydev);
+
+ return 0;
+}
+
+static int vsc8664_config(struct phy_device *phydev)
+{
+ u32 val;
+
+ /* Enable MAC interface auto-negotiation */
+ phy_write(phydev, MDIO_DEVAD_NONE, PHY_EXT_PAGE_ACCESS, 0);
+ val = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_VSC8664_EPHY_CON);
+ val |= (1 << 13);
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_VSC8664_EPHY_CON, val);
+
+ phy_write(phydev, MDIO_DEVAD_NONE, PHY_EXT_PAGE_ACCESS,
+ PHY_EXT_PAGE_ACCESS_EXTENDED);
+ val = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_VSC8664_SERDES_AND_SIGDET);
+ val |= (1 << 11);
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_VSC8664_SERDES_AND_SIGDET, val);
+ phy_write(phydev, MDIO_DEVAD_NONE, PHY_EXT_PAGE_ACCESS, 0);
+
+ /* Enable LED blink */
+ val = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_VSC8664_LED_CON);
+ val &= ~(1 << 2);
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_VSC8664_LED_CON, val);
+
+ genphy_config_aneg(phydev);
+
+ return 0;
+}
+
+static struct phy_driver VSC8211_driver = {
+ .name = "Vitesse VSC8211",
+ .uid = 0xfc4b0,
+ .mask = 0xffff0,
+ .features = PHY_GBIT_FEATURES,
+ .config = &vitesse_config,
+ .startup = &vitesse_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+static struct phy_driver VSC8221_driver = {
+ .name = "Vitesse VSC8221",
+ .uid = 0xfc550,
+ .mask = 0xffff0,
+ .features = PHY_GBIT_FEATURES,
+ .config = &genphy_config_aneg,
+ .startup = &vitesse_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+static struct phy_driver VSC8244_driver = {
+ .name = "Vitesse VSC8244",
+ .uid = 0xfc6c0,
+ .mask = 0xffff0,
+ .features = PHY_GBIT_FEATURES,
+ .config = &genphy_config_aneg,
+ .startup = &vitesse_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+static struct phy_driver VSC8234_driver = {
+ .name = "Vitesse VSC8234",
+ .uid = 0xfc620,
+ .mask = 0xffff0,
+ .features = PHY_GBIT_FEATURES,
+ .config = &genphy_config_aneg,
+ .startup = &vitesse_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+static struct phy_driver VSC8574_driver = {
+ .name = "Vitesse VSC8574",
+ .uid = 0x704a0,
+ .mask = 0xffff0,
+ .features = PHY_GBIT_FEATURES,
+ .config = &vsc8574_config,
+ .startup = &vitesse_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+static struct phy_driver VSC8514_driver = {
+ .name = "Vitesse VSC8514",
+ .uid = 0x70670,
+ .mask = 0xffff0,
+ .features = PHY_GBIT_FEATURES,
+ .config = &vsc8514_config,
+ .startup = &vitesse_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+static struct phy_driver VSC8584_driver = {
+ .name = "Vitesse VSC8584",
+ .uid = 0x707c0,
+ .mask = 0xffff0,
+ .features = PHY_GBIT_FEATURES,
+ .config = &vsc8574_config,
+ .startup = &vitesse_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+static struct phy_driver VSC8601_driver = {
+ .name = "Vitesse VSC8601",
+ .uid = 0x70420,
+ .mask = 0xffff0,
+ .features = PHY_GBIT_FEATURES,
+ .config = &vsc8601_config,
+ .startup = &vitesse_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+static struct phy_driver VSC8641_driver = {
+ .name = "Vitesse VSC8641",
+ .uid = 0x70430,
+ .mask = 0xffff0,
+ .features = PHY_GBIT_FEATURES,
+ .config = &genphy_config_aneg,
+ .startup = &vitesse_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+static struct phy_driver VSC8662_driver = {
+ .name = "Vitesse VSC8662",
+ .uid = 0x70660,
+ .mask = 0xffff0,
+ .features = PHY_GBIT_FEATURES,
+ .config = &genphy_config_aneg,
+ .startup = &vitesse_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+static struct phy_driver VSC8664_driver = {
+ .name = "Vitesse VSC8664",
+ .uid = 0x70660,
+ .mask = 0xffff0,
+ .features = PHY_GBIT_FEATURES,
+ .config = &vsc8664_config,
+ .startup = &vitesse_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+/* Vitesse bought Cicada, so we'll put these here */
+static struct phy_driver cis8201_driver = {
+ .name = "CIS8201",
+ .uid = 0xfc410,
+ .mask = 0xffff0,
+ .features = PHY_GBIT_FEATURES,
+ .config = &vitesse_config,
+ .startup = &vitesse_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+static struct phy_driver cis8204_driver = {
+ .name = "Cicada Cis8204",
+ .uid = 0xfc440,
+ .mask = 0xffff0,
+ .features = PHY_GBIT_FEATURES,
+ .config = &cis8204_config,
+ .startup = &vitesse_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+int phy_vitesse_init(void)
+{
+ phy_register(&VSC8641_driver);
+ phy_register(&VSC8601_driver);
+ phy_register(&VSC8234_driver);
+ phy_register(&VSC8244_driver);
+ phy_register(&VSC8211_driver);
+ phy_register(&VSC8221_driver);
+ phy_register(&VSC8574_driver);
+ phy_register(&VSC8584_driver);
+ phy_register(&VSC8514_driver);
+ phy_register(&VSC8662_driver);
+ phy_register(&VSC8664_driver);
+ phy_register(&cis8201_driver);
+ phy_register(&cis8204_driver);
+
+ return 0;
+}
diff --git a/roms/u-boot/drivers/net/phy/xilinx_gmii2rgmii.c b/roms/u-boot/drivers/net/phy/xilinx_gmii2rgmii.c
new file mode 100644
index 000000000..635c0570e
--- /dev/null
+++ b/roms/u-boot/drivers/net/phy/xilinx_gmii2rgmii.c
@@ -0,0 +1,144 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Xilinx GMII2RGMII phy driver
+ *
+ * Copyright (C) 2018 Xilinx, Inc.
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <log.h>
+#include <phy.h>
+#include <asm/global_data.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define ZYNQ_GMII2RGMII_REG 0x10
+#define ZYNQ_GMII2RGMII_SPEED_MASK (BMCR_SPEED1000 | BMCR_SPEED100)
+
+static int xilinxgmiitorgmii_config(struct phy_device *phydev)
+{
+ ofnode node = phy_get_ofnode(phydev);
+ struct phy_device *ext_phydev;
+ struct ofnode_phandle_args phandle;
+ int ext_phyaddr = -1;
+ int ret;
+
+ debug("%s\n", __func__);
+
+ if (!ofnode_valid(node))
+ return -EINVAL;
+
+ phydev->addr = ofnode_read_u32_default(node, "reg", -1);
+ ret = ofnode_parse_phandle_with_args(node, "phy-handle",
+ NULL, 0, 0, &phandle);
+ if (ret)
+ return ret;
+
+ ext_phyaddr = ofnode_read_u32_default(phandle.node, "reg", -1);
+ ext_phydev = phy_find_by_mask(phydev->bus,
+ 1 << ext_phyaddr,
+ PHY_INTERFACE_MODE_RGMII);
+ if (!ext_phydev) {
+ printf("%s, No external phy device found\n", __func__);
+ return -EINVAL;
+ }
+
+ ext_phydev->node = phandle.node;
+ phydev->priv = ext_phydev;
+
+ debug("%s, gmii2rgmmi:0x%x, extphy:0x%x\n", __func__, phydev->addr,
+ ext_phyaddr);
+
+ if (ext_phydev->drv->config)
+ ext_phydev->drv->config(ext_phydev);
+
+ return 0;
+}
+
+static int xilinxgmiitorgmii_extread(struct phy_device *phydev, int addr,
+ int devaddr, int regnum)
+{
+ struct phy_device *ext_phydev = phydev->priv;
+
+ debug("%s\n", __func__);
+ if (ext_phydev->drv->readext)
+ ext_phydev->drv->readext(ext_phydev, addr, devaddr, regnum);
+
+ return 0;
+}
+
+static int xilinxgmiitorgmii_extwrite(struct phy_device *phydev, int addr,
+ int devaddr, int regnum, u16 val)
+
+{
+ struct phy_device *ext_phydev = phydev->priv;
+
+ debug("%s\n", __func__);
+ if (ext_phydev->drv->writeext)
+ ext_phydev->drv->writeext(ext_phydev, addr, devaddr, regnum,
+ val);
+
+ return 0;
+}
+
+static int xilinxgmiitorgmii_startup(struct phy_device *phydev)
+{
+ u16 val = 0;
+ struct phy_device *ext_phydev = phydev->priv;
+
+ debug("%s\n", __func__);
+ ext_phydev->dev = phydev->dev;
+ if (ext_phydev->drv->startup)
+ ext_phydev->drv->startup(ext_phydev);
+
+ val = phy_read(phydev, phydev->addr, ZYNQ_GMII2RGMII_REG);
+ val &= ~ZYNQ_GMII2RGMII_SPEED_MASK;
+
+ if (ext_phydev->speed == SPEED_1000)
+ val |= BMCR_SPEED1000;
+ else if (ext_phydev->speed == SPEED_100)
+ val |= BMCR_SPEED100;
+
+ phy_write(phydev, phydev->addr, ZYNQ_GMII2RGMII_REG, val |
+ BMCR_FULLDPLX);
+
+ phydev->duplex = ext_phydev->duplex;
+ phydev->speed = ext_phydev->speed;
+ phydev->link = ext_phydev->link;
+
+ return 0;
+}
+
+static int xilinxgmiitorgmii_probe(struct phy_device *phydev)
+{
+ debug("%s\n", __func__);
+
+ if (phydev->interface != PHY_INTERFACE_MODE_GMII) {
+ printf("Incorrect interface type\n");
+ return -EINVAL;
+ }
+
+ phydev->flags |= PHY_FLAG_BROKEN_RESET;
+
+ return 0;
+}
+
+static struct phy_driver gmii2rgmii_driver = {
+ .name = "XILINX GMII2RGMII",
+ .uid = PHY_GMII2RGMII_ID,
+ .mask = 0xffffffff,
+ .features = PHY_GBIT_FEATURES,
+ .probe = xilinxgmiitorgmii_probe,
+ .config = xilinxgmiitorgmii_config,
+ .startup = xilinxgmiitorgmii_startup,
+ .writeext = xilinxgmiitorgmii_extwrite,
+ .readext = xilinxgmiitorgmii_extread,
+};
+
+int phy_xilinx_gmii2rgmii_init(void)
+{
+ phy_register(&gmii2rgmii_driver);
+
+ return 0;
+}
diff --git a/roms/u-boot/drivers/net/phy/xilinx_phy.c b/roms/u-boot/drivers/net/phy/xilinx_phy.c
new file mode 100644
index 000000000..39dbfdb7d
--- /dev/null
+++ b/roms/u-boot/drivers/net/phy/xilinx_phy.c
@@ -0,0 +1,146 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Xilinx PCS/PMA Core phy driver
+ *
+ * Copyright (C) 2015 - 2016 Xilinx, Inc.
+ */
+
+#include <config.h>
+#include <common.h>
+#include <log.h>
+#include <phy.h>
+#include <dm.h>
+
+#define MII_PHY_STATUS_SPD_MASK 0x0C00
+#define MII_PHY_STATUS_FULLDUPLEX 0x1000
+#define MII_PHY_STATUS_1000 0x0800
+#define MII_PHY_STATUS_100 0x0400
+#define XPCSPMA_PHY_CTRL_ISOLATE_DISABLE 0xFBFF
+
+/* Mask used for ID comparisons */
+#define XILINX_PHY_ID_MASK 0xfffffff0
+
+/* Known PHY IDs */
+#define XILINX_PHY_ID 0x01740c00
+
+/* struct phy_device dev_flags definitions */
+#define XAE_PHY_TYPE_MII 0
+#define XAE_PHY_TYPE_GMII 1
+#define XAE_PHY_TYPE_RGMII_1_3 2
+#define XAE_PHY_TYPE_RGMII_2_0 3
+#define XAE_PHY_TYPE_SGMII 4
+#define XAE_PHY_TYPE_1000BASE_X 5
+
+static int xilinxphy_startup(struct phy_device *phydev)
+{
+ int err;
+ int status = 0;
+
+ debug("%s\n", __func__);
+ /* Update the link, but return if there
+ * was an error
+ */
+ err = genphy_update_link(phydev);
+ if (err)
+ return err;
+
+ if (AUTONEG_ENABLE == phydev->autoneg) {
+ status = phy_read(phydev, MDIO_DEVAD_NONE, MII_LPA);
+ status = status & MII_PHY_STATUS_SPD_MASK;
+
+ if (status & MII_PHY_STATUS_FULLDUPLEX)
+ phydev->duplex = DUPLEX_FULL;
+ else
+ phydev->duplex = DUPLEX_HALF;
+
+ switch (status) {
+ case MII_PHY_STATUS_1000:
+ phydev->speed = SPEED_1000;
+ break;
+
+ case MII_PHY_STATUS_100:
+ phydev->speed = SPEED_100;
+ break;
+
+ default:
+ phydev->speed = SPEED_10;
+ break;
+ }
+ } else {
+ int bmcr = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR);
+
+ if (bmcr < 0)
+ return bmcr;
+
+ if (bmcr & BMCR_FULLDPLX)
+ phydev->duplex = DUPLEX_FULL;
+ else
+ phydev->duplex = DUPLEX_HALF;
+
+ if (bmcr & BMCR_SPEED1000)
+ phydev->speed = SPEED_1000;
+ else if (bmcr & BMCR_SPEED100)
+ phydev->speed = SPEED_100;
+ else
+ phydev->speed = SPEED_10;
+ }
+
+ /*
+ * For 1000BASE-X Phy Mode the speed/duplex will always be
+ * 1000Mbps/fullduplex
+ */
+ if (phydev->flags == XAE_PHY_TYPE_1000BASE_X) {
+ phydev->duplex = DUPLEX_FULL;
+ phydev->speed = SPEED_1000;
+ }
+
+ return 0;
+}
+
+static int xilinxphy_of_init(struct phy_device *phydev)
+{
+ u32 phytype;
+ ofnode node;
+
+ debug("%s\n", __func__);
+ node = phy_get_ofnode(phydev);
+ if (!ofnode_valid(node))
+ return -EINVAL;
+
+ phytype = ofnode_read_u32_default(node, "xlnx,phy-type", -1);
+ if (phytype == XAE_PHY_TYPE_1000BASE_X)
+ phydev->flags |= XAE_PHY_TYPE_1000BASE_X;
+
+ return 0;
+}
+
+static int xilinxphy_config(struct phy_device *phydev)
+{
+ int temp;
+
+ debug("%s\n", __func__);
+ xilinxphy_of_init(phydev);
+ temp = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR);
+ temp &= XPCSPMA_PHY_CTRL_ISOLATE_DISABLE;
+ phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, temp);
+
+ return 0;
+}
+
+static struct phy_driver xilinxphy_driver = {
+ .uid = XILINX_PHY_ID,
+ .mask = XILINX_PHY_ID_MASK,
+ .name = "Xilinx PCS/PMA PHY",
+ .features = PHY_GBIT_FEATURES,
+ .config = &xilinxphy_config,
+ .startup = &xilinxphy_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+int phy_xilinx_init(void)
+{
+ debug("%s\n", __func__);
+ phy_register(&xilinxphy_driver);
+
+ return 0;
+}