From 189867ccec5eee1ef5521ed4a86eb04e743f4ca3 Mon Sep 17 00:00:00 2001 From: hchyhchyxh <641953443@qq.com> Date: Tue, 6 Aug 2024 15:09:04 +0800 Subject: [PATCH 1/7] mediatek:add ccc,ddd support --- .../boot/dts/mediatek/mt7986a-ccc-ddd-gsw.dts | 284 ++++++++++++++++ .../boot/dts/mediatek/mt7986a-ccc-ddd.dts | 306 ++++++++++++++++++ .../net/ethernet/mediatek/mtk_eth_soc.c | 2 +- target/linux/mediatek/image/mt7986.mk | 12 + .../mt7986/base-files/etc/board.d/02_network | 6 + .../base-files/lib/preinit/90_extract_caldata | 5 + .../mt7986/base-files/lib/upgrade/platform.sh | 2 + 7 files changed, 616 insertions(+), 1 deletion(-) create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-ccc-ddd-gsw.dts create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-ccc-ddd.dts diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-ccc-ddd-gsw.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-ccc-ddd-gsw.dts new file mode 100644 index 00000000000..3b674c7978b --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-ccc-ddd-gsw.dts @@ -0,0 +1,284 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT + +/dts-v1/; +#include "mt7986a.dtsi" +#include "mt7986a-pinctrl.dtsi" +#include +#include +#include + +/ { + model = "ABABAABB"; + compatible = "ccc,ddd", "mediatek,mt7986a-emmc-rfb"; + + aliases { + led-boot = &green_led; + led-failsafe = &green_led; + led-running = &green_led; + led-upgrade = &green_led; + }; + + chosen { + bootargs = "console=ttyS0,115200n1 loglevel=8 \ + earlycon=uart8250,mmio32,0x11002000 \ + root=PARTLABEL=rootfs rootwait rootfstype=squashfs,f2fs"; + }; + + memory { + reg = <0 0x40000000 0 0x40000000>; + }; + + gpio-keys { + compatible = "gpio-keys"; + + button-reset { + label = "reset"; + linux,code = ; + gpios = <&pio 16 GPIO_ACTIVE_LOW>; + }; + }; + + gsw: gsw@0 { + compatible = "mediatek,mt753x"; + mediatek,ethsys = <ðsys>; + #address-cells = <1>; + #size-cells = <0>; + }; + + gpio-leds { + compatible = "gpio-leds"; + green_led: green_led { + label = "green"; + gpios = <&pio 22 GPIO_ACTIVE_LOW>; + }; + }; + + reg_1p8v: regulator-1p8v { + compatible = "regulator-fixed"; + regulator-name = "fixed-1.8V"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-boot-on; + regulator-always-on; + }; + + reg_3p3v: regulator-3p3v { + compatible = "regulator-fixed"; + regulator-name = "fixed-3.3V"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + }; + + usb_vbus: regulator-usb-vbus { + compatible = "regulator-fixed"; + regulator-name = "usb_vbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + gpios = <&pio 17 GPIO_ACTIVE_HIGH>; + enable-active-high; + regulator-boot-on; + }; + +}; + +&mmc0 { + bus-width = <8>; + cap-mmc-highspeed; + hs400-ds-delay = <0x14014>; + max-frequency = <200000000>; + mmc-hs200-1_8v; + mmc-hs400-1_8v; + no-sd; + no-sdio; + non-removable; + pinctrl-names = "default", "state_uhs"; + pinctrl-0 = <&mmc0_pins_default>; + pinctrl-1 = <&mmc0_pins_uhs>; + vmmc-supply = <®_3p3v>; + vqmmc-supply = <®_1p8v>; + status = "okay"; +}; + +&pio { + mmc0_pins_default: mmc0-pins-default { + mux { + function = "flash"; + groups = "emmc_51"; + }; + conf-cmd-dat { + pins = "EMMC_DATA_0", "EMMC_DATA_1", "EMMC_DATA_2", + "EMMC_DATA_3", "EMMC_DATA_4", "EMMC_DATA_5", + "EMMC_DATA_6", "EMMC_DATA_7", "EMMC_CMD"; + input-enable; + drive-strength = ; + mediatek,pull-up-adv = <1>; + }; + conf-clk { + pins = "EMMC_CK"; + drive-strength = ; + mediatek,pull-down-adv = <2>; + }; + conf-ds { + pins = "EMMC_DSL"; + mediatek,pull-down-adv = <2>; + }; + conf-rst { + pins = "EMMC_RSTB"; + drive-strength = ; + mediatek,pull-up-adv = <1>; + }; + }; + + mmc0_pins_uhs: mmc0-uhs-pins { + mux { + function = "flash"; + groups = "emmc_51"; + }; + conf-cmd-dat { + pins = "EMMC_DATA_0", "EMMC_DATA_1", "EMMC_DATA_2", + "EMMC_DATA_3", "EMMC_DATA_4", "EMMC_DATA_5", + "EMMC_DATA_6", "EMMC_DATA_7", "EMMC_CMD"; + input-enable; + drive-strength = ; + mediatek,pull-up-adv = <1>; + }; + conf-clk { + pins = "EMMC_CK"; + drive-strength = ; + mediatek,pull-down-adv = <2>; + }; + conf-ds { + pins = "EMMC_DSL"; + mediatek,pull-down-adv = <2>; + }; + conf-rst { + pins = "EMMC_RSTB"; + drive-strength = ; + mediatek,pull-up-adv = <1>; + }; + }; + + wf_2g_5g_pins: wf-2g-5g-pins { + mux { + function = "wifi"; + groups = "wf_2g", "wf_5g"; + }; + conf { + pins = "WF0_HB1", "WF0_HB2", "WF0_HB3", "WF0_HB4", + "WF0_HB0", "WF0_HB0_B", "WF0_HB5", "WF0_HB6", + "WF0_HB7", "WF0_HB8", "WF0_HB9", "WF0_HB10", + "WF0_TOP_CLK", "WF0_TOP_DATA", "WF1_HB1", + "WF1_HB2", "WF1_HB3", "WF1_HB4", "WF1_HB0", + "WF1_HB5", "WF1_HB6", "WF1_HB7", "WF1_HB8", + "WF1_TOP_CLK", "WF1_TOP_DATA"; + drive-strength = ; + }; + }; +}; +ð { + status = "okay"; + + gmac0: mac@0 { + compatible = "mediatek,eth-mac"; + reg = <0>; + phy-mode = "2500base-x"; + ext-phy-reg = <5>; + ext-phy-reset-gpios = <&pio 48 0>; + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + + gmac1: mac@1 { + compatible = "mediatek,eth-mac"; + reg = <1>; + phy-mode = "2500base-x"; + ext-phy-reg = <7>; + ext-phy-reset-gpios = <&pio 6 0>; + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + + mdio: mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + + phy5: phy@5 { + compatible = "ethernet-phy-ieee802.3-c45"; + reg = <5>; + }; + + phy7: phy@7 { + compatible = "ethernet-phy-ieee802.3-c45"; + reg = <7>; + }; + + }; + +}; + +&gsw { + mediatek,mdio = <&mdio>; + mediatek,mdio_master_pinmux = <1>; + reset-gpios = <&pio 5 0>; + interrupt-parent = <&pio>; + interrupts = <66 IRQ_TYPE_LEVEL_HIGH>; + status = "okay"; + + port5: port@5 { + compatible = "mediatek,mt753x-port"; + reg = <5>; + phy-mode = "sgmii"; + + fixed-link { + speed = <2500>; + full-duplex; + }; + }; + + port6: port@6 { + compatible = "mediatek,mt753x-port"; + /* mediatek,ssc-on; */ + reg = <6>; + phy-mode = "sgmii"; + fixed-link { + speed = <2500>; + full-duplex; + }; + }; +}; + +&hnat { + mtketh-wan = "eth1"; + mtketh-lan = "eth0"; + mtketh-max-gmac = <2>; + status = "okay"; +}; + +&uart0 { + status = "okay"; +}; + +&watchdog { + status = "okay"; +}; + +&wbsys { + pinctrl-names = "default"; + pinctrl-0 = <&wf_2g_5g_pins>; + status = "okay"; +}; + +&xhci { + vusb33-supply = <®_3p3v>; + vbus-supply = <&usb_vbus>; + status = "okay"; +}; + diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-ccc-ddd.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-ccc-ddd.dts new file mode 100644 index 00000000000..f233cee8efe --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-ccc-ddd.dts @@ -0,0 +1,306 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT + +/dts-v1/; +#include "mt7986a.dtsi" +#include "mt7986a-pinctrl.dtsi" +#include +#include +#include + +/ { + model = "ABABAABB"; + compatible = "ccc,ddd", "mediatek,mt7986a-emmc-rfb"; + + aliases { + led-boot = &green_led; + led-failsafe = &green_led; + led-running = &green_led; + led-upgrade = &green_led; + }; + + chosen { + bootargs = "console=ttyS0,115200n1 loglevel=8 \ + earlycon=uart8250,mmio32,0x11002000 \ + root=PARTLABEL=rootfs rootwait rootfstype=squashfs,f2fs"; + }; + + memory { + reg = <0 0x40000000 0 0x40000000>; + }; + + gpio-keys { + compatible = "gpio-keys"; + + button-reset { + label = "reset"; + linux,code = ; + gpios = <&pio 16 GPIO_ACTIVE_LOW>; + }; + }; + + gpio-leds { + compatible = "gpio-leds"; + green_led: green_led { + label = "green"; + gpios = <&pio 22 GPIO_ACTIVE_LOW>; + }; + }; + + reg_1p8v: regulator-1p8v { + compatible = "regulator-fixed"; + regulator-name = "fixed-1.8V"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-boot-on; + regulator-always-on; + }; + + reg_3p3v: regulator-3p3v { + compatible = "regulator-fixed"; + regulator-name = "fixed-3.3V"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + }; + + usb_vbus: regulator-usb-vbus { + compatible = "regulator-fixed"; + regulator-name = "usb_vbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + gpios = <&pio 17 GPIO_ACTIVE_HIGH>; + enable-active-high; + regulator-boot-on; + }; + +}; + +&mmc0 { + bus-width = <8>; + cap-mmc-highspeed; + hs400-ds-delay = <0x14014>; + max-frequency = <200000000>; + mmc-hs200-1_8v; + mmc-hs400-1_8v; + no-sd; + no-sdio; + non-removable; + pinctrl-names = "default", "state_uhs"; + pinctrl-0 = <&mmc0_pins_default>; + pinctrl-1 = <&mmc0_pins_uhs>; + vmmc-supply = <®_3p3v>; + vqmmc-supply = <®_1p8v>; + status = "okay"; +}; + +&pio { + mmc0_pins_default: mmc0-pins-default { + mux { + function = "flash"; + groups = "emmc_51"; + }; + conf-cmd-dat { + pins = "EMMC_DATA_0", "EMMC_DATA_1", "EMMC_DATA_2", + "EMMC_DATA_3", "EMMC_DATA_4", "EMMC_DATA_5", + "EMMC_DATA_6", "EMMC_DATA_7", "EMMC_CMD"; + input-enable; + drive-strength = ; + mediatek,pull-up-adv = <1>; + }; + conf-clk { + pins = "EMMC_CK"; + drive-strength = ; + mediatek,pull-down-adv = <2>; + }; + conf-ds { + pins = "EMMC_DSL"; + mediatek,pull-down-adv = <2>; + }; + conf-rst { + pins = "EMMC_RSTB"; + drive-strength = ; + mediatek,pull-up-adv = <1>; + }; + }; + + mmc0_pins_uhs: mmc0-uhs-pins { + mux { + function = "flash"; + groups = "emmc_51"; + }; + conf-cmd-dat { + pins = "EMMC_DATA_0", "EMMC_DATA_1", "EMMC_DATA_2", + "EMMC_DATA_3", "EMMC_DATA_4", "EMMC_DATA_5", + "EMMC_DATA_6", "EMMC_DATA_7", "EMMC_CMD"; + input-enable; + drive-strength = ; + mediatek,pull-up-adv = <1>; + }; + conf-clk { + pins = "EMMC_CK"; + drive-strength = ; + mediatek,pull-down-adv = <2>; + }; + conf-ds { + pins = "EMMC_DSL"; + mediatek,pull-down-adv = <2>; + }; + conf-rst { + pins = "EMMC_RSTB"; + drive-strength = ; + mediatek,pull-up-adv = <1>; + }; + }; + + wf_2g_5g_pins: wf-2g-5g-pins { + mux { + function = "wifi"; + groups = "wf_2g", "wf_5g"; + }; + conf { + pins = "WF0_HB1", "WF0_HB2", "WF0_HB3", "WF0_HB4", + "WF0_HB0", "WF0_HB0_B", "WF0_HB5", "WF0_HB6", + "WF0_HB7", "WF0_HB8", "WF0_HB9", "WF0_HB10", + "WF0_TOP_CLK", "WF0_TOP_DATA", "WF1_HB1", + "WF1_HB2", "WF1_HB3", "WF1_HB4", "WF1_HB0", + "WF1_HB5", "WF1_HB6", "WF1_HB7", "WF1_HB8", + "WF1_TOP_CLK", "WF1_TOP_DATA"; + drive-strength = ; + }; + }; +}; +ð { + status = "okay"; + + gmac0: mac@0 { + compatible = "mediatek,eth-mac"; + reg = <0>; + phy-mode = "2500base-x"; + ext-phy-reg = <5>; + ext-phy-reset-gpios = <&pio 48 0>; + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + + gmac1: mac@1 { + compatible = "mediatek,eth-mac"; + reg = <1>; + phy-mode = "2500base-x"; + ext-phy-reg = <7>; + ext-phy-reset-gpios = <&pio 6 0>; + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + + mdio: mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + + phy5: phy@5 { + compatible = "ethernet-phy-ieee802.3-c45"; + reg = <5>; + }; + + phy7: phy@7 { + compatible = "ethernet-phy-ieee802.3-c45"; + reg = <7>; + }; + + + switch@0 { + compatible = "mediatek,mt7531"; + reg = <31>; + reset-gpios = <&pio 5 0>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + label = "lan0"; + }; + + port@1 { + reg = <1>; + label = "lan1"; + }; + + port@2 { + reg = <2>; + label = "lan2"; + }; + + port@3 { + reg = <3>; + label = "lan3"; + }; + + port@4 { + reg = <4>; + label = "lan4"; + }; + + port@5 { + reg = <5>; + label = "lan5"; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + + port@6 { + reg = <6>; + label = "cpu"; + ethernet = <&gmac0>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + }; + }; + }; +}; + +&hnat { + mtketh-wan = "eth1"; + mtketh-lan = "lan"; + mtketh-max-gmac = <2>; + status = "okay"; +}; + +&uart0 { + status = "okay"; +}; + +&watchdog { + status = "okay"; +}; + +&wbsys { + pinctrl-names = "default"; + pinctrl-0 = <&wf_2g_5g_pins>; + status = "okay"; +}; + +&xhci { + vusb33-supply = <®_3p3v>; + vbus-supply = <&usb_vbus>; + status = "okay"; +}; + diff --git a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.c index 717e8202518..8920abe3dcd 100644 --- a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.c @@ -3859,7 +3859,7 @@ static const struct mtk_soc_data mt7986_data = { .hw_features = MTK_HW_FEATURES, .required_clks = MT7986_CLKS_BITMAP, .required_pctl = false, - .has_sram = false, + .has_sram = true, }; static const struct mtk_soc_data mt7981_data = { diff --git a/target/linux/mediatek/image/mt7986.mk b/target/linux/mediatek/image/mt7986.mk index 1b0540ded36..1f4faf0236f 100644 --- a/target/linux/mediatek/image/mt7986.mk +++ b/target/linux/mediatek/image/mt7986.mk @@ -534,3 +534,15 @@ define Device/tplink_tl-xdr6088 $(call Device/tplink_tl-common) endef TARGET_DEVICES += tplink_tl-xdr6088 + + +define Device/pm_ccc-ddd + DEVICE_VENDOR := CCC + DEVICE_MODEL := DDD + DEVICE_DTS := mt7986a-ccc-ddd + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + DEVICE_PACKAGES := $(MT7986_USB_PKGS) f2fsck losetup mkf2fs kmod-fs-f2fs kmod-mmc + IMAGE/factory.bin := append-kernel | pad-to 32M | append-rootfs + IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata +endef +TARGET_DEVICES += pm_ccc-ddd diff --git a/target/linux/mediatek/mt7986/base-files/etc/board.d/02_network b/target/linux/mediatek/mt7986/base-files/etc/board.d/02_network index 52058e32432..7e83f322492 100755 --- a/target/linux/mediatek/mt7986/base-files/etc/board.d/02_network +++ b/target/linux/mediatek/mt7986/base-files/etc/board.d/02_network @@ -87,6 +87,12 @@ mediatek_setup_macs() lan_mac=$(mmc_get_mac_binary factory 0x24) label_mac=$lan_mac ;; + ccc,ddd) + wan_mac=$(mmc_get_mac_binary factory 0x24) + lan_mac=$(macaddr_add "$wan_mac" 2) + wlan5g_mac=$(macaddr_add $(mmc_get_mac_binary factory 0x4) 1) + label_mac=$wan_mac + ;; tplink,tl-xdr6086|\ tplink,tl-xdr6088) lan_mac=$(mtd_get_mac_binary config 0x1c) diff --git a/target/linux/mediatek/mt7986/base-files/lib/preinit/90_extract_caldata b/target/linux/mediatek/mt7986/base-files/lib/preinit/90_extract_caldata index 77d78694c67..fa9b62804ad 100644 --- a/target/linux/mediatek/mt7986/base-files/lib/preinit/90_extract_caldata +++ b/target/linux/mediatek/mt7986/base-files/lib/preinit/90_extract_caldata @@ -23,6 +23,11 @@ do_extract_caldata() { caldata_validate && exit 0 caldata_extract_mmc "factory" 0x0 0x1000 ;; + ccc,ddd) + FIRMWARE=MT7986_iPAiLNA_EEPROM_AX6000.bin + caldata_validate && exit 0 + caldata_extract_mmc "factory" 0x0 0x1000 + ;; esac } diff --git a/target/linux/mediatek/mt7986/base-files/lib/upgrade/platform.sh b/target/linux/mediatek/mt7986/base-files/lib/upgrade/platform.sh index 8cee36893d5..ce903d8e876 100755 --- a/target/linux/mediatek/mt7986/base-files/lib/upgrade/platform.sh +++ b/target/linux/mediatek/mt7986/base-files/lib/upgrade/platform.sh @@ -155,6 +155,7 @@ platform_do_upgrade() { ;; bananapi,bpi-r3mini-emmc |\ glinet,gl-mt6000 |\ + ccc,ddd |\ jdcloud,re-cp-03 |\ *emmc*) CI_KERNPART="kernel" @@ -184,6 +185,7 @@ platform_check_image() { glinet,gl-mt6000 |\ jdcloud,re-cp-03 |\ tplink,tl-xdr608* |\ + ccc,ddd |\ *emmc*) # tar magic `ustar` magic="$(dd if="$1" bs=1 skip=257 count=5 2>/dev/null)" From 2a6262a143526458c5e9953b1621595da453cd5b Mon Sep 17 00:00:00 2001 From: hchyhchyxh <641953443@qq.com> Date: Mon, 9 Sep 2024 17:20:43 +0800 Subject: [PATCH 2/7] mediatek:add zhao-7981r128 support --- package/boot/uboot-envtools/files/mediatek | 2 + .../mediatek/mt7981-spim-snand-7981r128.dts | 337 ++++++++++++++++++ target/linux/mediatek/image/mt7981.mk | 18 + .../mt7981/base-files/etc/board.d/01_leds | 6 + .../mt7981/base-files/etc/board.d/02_network | 5 + 5 files changed, 368 insertions(+) create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-snand-7981r128.dts diff --git a/package/boot/uboot-envtools/files/mediatek b/package/boot/uboot-envtools/files/mediatek index 926f36c3dda..b3db73fc107 100644 --- a/package/boot/uboot-envtools/files/mediatek +++ b/package/boot/uboot-envtools/files/mediatek @@ -26,6 +26,7 @@ livinet,zr-3020*) h3c,nx30pro |\ *clt,r30b1* |\ ruijie,rg-x60-pro* |\ +*snand-7981r128* |\ cmcc,rax3000m) ubootenv_add_uci_config "/dev/mtd2" "0x0" "0x80000" "0x20000" "4" ;; @@ -41,6 +42,7 @@ nradio,wt9103 |\ *konka,komi-a31*) ubootenv_add_uci_config "/dev/mtd2" "0x0" "0x20000" "0x80000" "1" ;; + esac config_load ubootenv diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-snand-7981r128.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-snand-7981r128.dts new file mode 100644 index 00000000000..523baeb4caa --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-snand-7981r128.dts @@ -0,0 +1,337 @@ +/dts-v1/; +#include "mt7981.dtsi" +/ { + model = "7981R128"; + compatible = "mediatek,mt7981-spim-snand-7981r128"; + + aliases { + led-boot = &POWER; + led-failsafe = &POWER; + led-running = &POWER; + led-upgrade = &POWER; + }; + + + + chosen { + bootargs = "console=ttyS0,115200n1 loglevel=8 \ + earlycon=uart8250,mmio32,0x11002000"; + }; + + + gpio-keys { + compatible = "gpio-keys"; + reset { + label = "reset"; + linux,code = ; + gpios = <&pio 1 GPIO_ACTIVE_LOW>; + }; + }; + + leds { + compatible = "gpio-leds"; + + SFP: sfp { + label = "SFP"; + gpios = <&pio 4 GPIO_ACTIVE_LOW>; + default-state = "off"; + }; + + WIFI5G: wifi5g { + label = "WIFI5G"; + gpios = <&pio 5 GPIO_ACTIVE_LOW>; + default-state = "off"; + }; + + WIFI2G: wifi2g { + label = "WIFI2G"; + gpios = <&pio 6 GPIO_ACTIVE_LOW>; + default-state = "off"; + }; + + LAN: lan { + label = "LAN"; + gpios = <&pio 8 GPIO_ACTIVE_LOW>; + default-state = "off"; + }; + + POWER: power { + label = "POWER"; + gpios = <&pio 13 GPIO_ACTIVE_LOW>; + default-state = "on"; + }; + + }; + + i2c_sfp:i2c-gpio-0 { + compatible = "i2c-gpio"; + sda-gpios = <&pio 9 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + scl-gpios = <&pio 10 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + i2c-gpio,delay-us = <2>; + #address-cells = <1>; + #size-cells = <0>; + }; + + sfp1:sfp-wan { + compatible = "sff,sfp"; + i2c-bus = <&i2c_sfp>; + los-gpios = <&pio 3 GPIO_ACTIVE_HIGH>; + mod-def0-gpios = <&pio 35 GPIO_ACTIVE_HIGH>; + tx-disable-gpios = <&pio 12 GPIO_ACTIVE_HIGH>; + tx-fault-gpios = <&pio 34 GPIO_ACTIVE_HIGH>; + maximum-power-milliwatt = <3000>; + }; + + nmbm_spim_nand { + compatible = "generic,nmbm"; + + #address-cells = <1>; + #size-cells = <1>; + + lower-mtd-device = <&spi_nand>; + forced-create; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "BL2"; + reg = <0x00000 0x0100000>; + read-only; + }; + + partition@100000 { + label = "u-boot-env"; + reg = <0x0100000 0x0080000>; + }; + + partition@180000 { + label = "Factory"; + reg = <0x180000 0x0200000>; + }; + + partition@380000 { + label = "FIP"; + reg = <0x380000 0x0200000>; + }; + + partition@580000 { + label = "ubi"; + reg = <0x580000 0x7200000>; + }; + }; + }; +}; + +&uart0 { + status = "okay"; +}; + +&watchdog { + status = "okay"; +}; + +ð { + status = "okay"; + + gmac0: mac@0 { + compatible = "mediatek,eth-mac"; + reg = <0>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + + gmac1: mac@1 { + compatible = "mediatek,eth-mac"; + reg = <1>; + phy-mode = "2500base-x"; + managed = "in-band-status"; + sfp = <&sfp1>; + }; + + + mdio: mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + + phy5: phy@5 { + compatible = "ethernet-phy-id67c9.de0a"; + reg = <5>; + reset-gpios = <&pio 7 1>; + reset-assert-us = <600>; + reset-deassert-us = <20000>; + phy-mode = "2500base-x"; + }; + + switch@0 { + compatible = "mediatek,mt7531"; + reg = <31>; + reset-gpios = <&pio 39 0>; + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@1 { + reg = <1>; + label = "lan1"; + }; + + + port@5 { + reg = <5>; + label = "lan2"; + phy-mode = "2500base-x"; + phy-handle = <&phy5>; + }; + + port@6 { + reg = <6>; + label = "cpu"; + ethernet = <&gmac0>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + }; + }; + }; +}; + +&hnat { + mtketh-wan = "eth1"; + mtketh-lan = "lan"; + mtketh-max-gmac = <2>; + status = "okay"; +}; + +&spi0 { + pinctrl-names = "default"; + pinctrl-0 = <&spi0_flash_pins>; + status = "okay"; + spi_nand: spi_nand@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "spi-nand"; + spi-cal-enable; + spi-cal-mode = "read-data"; + spi-cal-datalen = <7>; + spi-cal-data = /bits/ 8 <0x53 0x50 0x49 0x4E 0x41 0x4E 0x44>; + spi-cal-addrlen = <5>; + spi-cal-addr = /bits/ 32 <0x0 0x0 0x0 0x0 0x0>; + reg = <0>; + spi-max-frequency = <52000000>; + spi-tx-bus-width = <4>; + spi-rx-bus-width = <4>; + }; +}; + +&spi1 { + pinctrl-names = "default"; + pinctrl-0 = <&spic_pins>; + status = "disabled"; + + slb9670: slb9670@0 { + compatible = "infineon,slb9670"; + reg = <0>; /* CE0 */ + #address-cells = <1>; + #size-cells = <0>; + spi-cal-enable; + spi-cal-mode = "read-data"; + spi-cal-datalen = <2>; + spi-cal-data = /bits/ 8 <0x00 0x1b>; + spi-max-frequency = <20000000>; + }; +}; + +&pio { + + i2c_pins: i2c-pins-g0 { + mux { + function = "i2c"; + groups = "i2c0_0"; + }; + }; + + pcm_pins: pcm-pins-g0 { + mux { + function = "pcm"; + groups = "pcm"; + }; + }; + + pwm0_pin: pwm0-pin-g0 { + mux { + function = "pwm"; + groups = "pwm0_0"; + }; + }; + + pwm1_pin: pwm1-pin-g0 { + mux { + function = "pwm"; + groups = "pwm1_0"; + }; + }; + + pwm2_pin: pwm2-pin { + mux { + function = "pwm"; + groups = "pwm2"; + }; + }; + + spi0_flash_pins: spi0-pins { + mux { + function = "spi"; + groups = "spi0", "spi0_wp_hold"; + }; + + conf-pu { + pins = "SPI0_CS", "SPI0_HOLD", "SPI0_WP"; + drive-strength = ; + bias-pull-up = ; + }; + + conf-pd { + pins = "SPI0_CLK", "SPI0_MOSI", "SPI0_MISO"; + drive-strength = ; + bias-pull-down = ; + }; + }; + + spic_pins: spi1-pins { + mux { + function = "spi"; + groups = "spi1_1"; + }; + }; + + uart1_pins: uart1-pins-g1 { + mux { + function = "uart"; + groups = "uart1_1"; + }; + }; + + uart2_pins: uart2-pins-g1 { + mux { + function = "uart"; + groups = "uart2_1"; + }; + }; +}; + +&xhci { + status = "okay"; +}; diff --git a/target/linux/mediatek/image/mt7981.mk b/target/linux/mediatek/image/mt7981.mk index a44c43f759c..77304b7b45c 100644 --- a/target/linux/mediatek/image/mt7981.mk +++ b/target/linux/mediatek/image/mt7981.mk @@ -553,3 +553,21 @@ define Device/nradio_wt9103_512m IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata endef TARGET_DEVICES += nradio_wt9103_512m + +define Device/zhao_7981-r128 + DEVICE_VENDOR := ZHAO + DEVICE_MODEL := 7981 R128 + DEVICE_DTS := mt7981-spim-snand-7981r128 + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + DEVICE_PACKAGES := $(MT7981_USB_PKGS) + SUPPORTED_DEVICES := zhao,7981-r128 + UBINIZE_OPTS := -E 5 + BLOCKSIZE := 128k + PAGESIZE := 2048 + IMAGE_SIZE := 114688k + KERNEL_IN_UBI := 1 + IMAGES += factory.bin + IMAGE/factory.bin := append-ubi | check-size $$$$(IMAGE_SIZE) + IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata +endef +TARGET_DEVICES += zhao_7981-r128 diff --git a/target/linux/mediatek/mt7981/base-files/etc/board.d/01_leds b/target/linux/mediatek/mt7981/base-files/etc/board.d/01_leds index c535653d4e5..837c6a6ab5e 100755 --- a/target/linux/mediatek/mt7981/base-files/etc/board.d/01_leds +++ b/target/linux/mediatek/mt7981/base-files/etc/board.d/01_leds @@ -51,6 +51,12 @@ nradio,wt9103) ucidef_set_led_netdev "5g" "5G" "hc:blue:cmode5" "eth1" "tx rx" ucidef_set_led_netdev "lan" "LAN" "hc:blue:cmode4" "br-lan" "tx rx" ;; +mediatek,mt7981-spim-snand-7981r128) + ucidef_set_led_netdev "lan" "LAN" "LAN" "lan2" + ucidef_set_led_netdev "sfp" "SFP" "SFP" "eth1" + ucidef_set_led_netdev "wifi5g" "WIFI5G" "WIFI5G" "rax0" + ucidef_set_led_netdev "wifi2g" "WIFI2G" "WIFI2G" "ra0" + ;; esac board_config_flush diff --git a/target/linux/mediatek/mt7981/base-files/etc/board.d/02_network b/target/linux/mediatek/mt7981/base-files/etc/board.d/02_network index 49638ecbb62..f44a7747087 100755 --- a/target/linux/mediatek/mt7981/base-files/etc/board.d/02_network +++ b/target/linux/mediatek/mt7981/base-files/etc/board.d/02_network @@ -228,6 +228,11 @@ mediatek_setup_macs() label_mac=$wan_mac local wifi_mac="$(mtd_get_mac_binary $part_name 0x04)" ;; + mediatek,mt7981-spim-snand-7981r128) + lan_mac=$(mtd_get_mac_binary $part_name 0x04) + wan_mac=$(macaddr_add $lan_mac 1) + label_mac=$lan_mac + ;; *) wan_mac=$(mtd_get_mac_binary $part_name $wan_mac_offset) lan_mac=$(mtd_get_mac_binary $part_name $lan_mac_offset) From 4452eb5a52481a8d9294ec10c0b294646f1444ac Mon Sep 17 00:00:00 2001 From: hchyhchyxh <641953443@qq.com> Date: Tue, 10 Sep 2024 11:16:24 +0800 Subject: [PATCH 3/7] update 7981r128 --- .../mediatek/mt7981-spim-snand-7981r128.dts | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-snand-7981r128.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-snand-7981r128.dts index 523baeb4caa..b96920f6500 100644 --- a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-snand-7981r128.dts +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-snand-7981r128.dts @@ -82,6 +82,24 @@ maximum-power-milliwatt = <3000>; }; + reg_3p3v: regulator-3p3v { + compatible = "regulator-fixed"; + regulator-name = "fixed-3.3V"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + }; + + usb_vbus: regulator-usb-vbus { + compatible = "regulator-fixed"; + regulator-name = "usb_vbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + enable-active-high; + regulator-boot-on; + }; + nmbm_spim_nand { compatible = "generic,nmbm"; @@ -333,5 +351,7 @@ }; &xhci { - status = "okay"; + vusb33-supply = <®_3p3v>; + vbus-supply = <&usb_vbus>; + status = "okay"; }; From 9ac6a82d0701a15773731c6076331bc8d5e4e96a Mon Sep 17 00:00:00 2001 From: hchyhchyxh <641953443@qq.com> Date: Tue, 10 Sep 2024 23:22:33 +0800 Subject: [PATCH 4/7] update --- package/boot/uboot-envtools/files/mediatek | 2 +- ...dts => mt7981-spim-snand-7981r128-dsa.dts} | 2 +- .../mt7981-spim-snand-7981r128-gsw.dts | 362 ++++++++++++++++++ .../files-5.4/drivers/net/phy/gpy211.c | 327 +++++++++++++++- target/linux/mediatek/image/mt7981.mk | 28 +- .../mt7981/base-files/etc/board.d/01_leds | 2 +- .../mt7981/base-files/etc/board.d/02_network | 10 +- .../mt7981/base-files/lib/upgrade/platform.sh | 2 + 8 files changed, 721 insertions(+), 14 deletions(-) rename target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/{mt7981-spim-snand-7981r128.dts => mt7981-spim-snand-7981r128-dsa.dts} (99%) create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-snand-7981r128-gsw.dts diff --git a/package/boot/uboot-envtools/files/mediatek b/package/boot/uboot-envtools/files/mediatek index 81f9ee9e9cd..650106df8cc 100644 --- a/package/boot/uboot-envtools/files/mediatek +++ b/package/boot/uboot-envtools/files/mediatek @@ -31,7 +31,7 @@ h3c,nx30pro |\ *clt,r30b1* |\ ruijie,rg-x60-pro* |\ cmcc,xr30 |\ -*snand-7981r128* |\ +mediatek,zhao-7981r128* |\ cmcc,rax3000m) ubootenv_add_uci_config "/dev/mtd2" "0x0" "0x80000" "0x20000" "4" ;; diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-snand-7981r128.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-snand-7981r128-dsa.dts similarity index 99% rename from target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-snand-7981r128.dts rename to target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-snand-7981r128-dsa.dts index b96920f6500..20dc97b856e 100644 --- a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-snand-7981r128.dts +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-snand-7981r128-dsa.dts @@ -2,7 +2,7 @@ #include "mt7981.dtsi" / { model = "7981R128"; - compatible = "mediatek,mt7981-spim-snand-7981r128"; + compatible = "mediatek,zhao-7981r128-d"; aliases { led-boot = &POWER; diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-snand-7981r128-gsw.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-snand-7981r128-gsw.dts new file mode 100644 index 00000000000..d630703b087 --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-snand-7981r128-gsw.dts @@ -0,0 +1,362 @@ +/dts-v1/; +#include "mt7981.dtsi" +/ { + model = "7981R128"; + compatible = "mediatek,zhao-7981r128-g"; + + aliases { + led-boot = &POWER; + led-failsafe = &POWER; + led-running = &POWER; + led-upgrade = &POWER; + }; + + + + chosen { + bootargs = "console=ttyS0,115200n1 loglevel=8 \ + earlycon=uart8250,mmio32,0x11002000"; + }; + + + gpio-keys { + compatible = "gpio-keys"; + reset { + label = "reset"; + linux,code = ; + gpios = <&pio 1 GPIO_ACTIVE_LOW>; + }; + }; + + leds { + compatible = "gpio-leds"; + + SFP: sfp { + label = "SFP"; + gpios = <&pio 4 GPIO_ACTIVE_LOW>; + default-state = "off"; + }; + + WIFI5G: wifi5g { + label = "WIFI5G"; + gpios = <&pio 5 GPIO_ACTIVE_LOW>; + default-state = "off"; + }; + + WIFI2G: wifi2g { + label = "WIFI2G"; + gpios = <&pio 6 GPIO_ACTIVE_LOW>; + default-state = "off"; + }; + + LAN: lan { + label = "LAN"; + gpios = <&pio 8 GPIO_ACTIVE_LOW>; + default-state = "off"; + }; + + POWER: power { + label = "POWER"; + gpios = <&pio 13 GPIO_ACTIVE_LOW>; + default-state = "on"; + }; + + }; + + gsw: gsw@0 { + compatible = "mediatek,mt753x"; + mediatek,ethsys = <ðsys>; + #address-cells = <1>; + #size-cells = <0>; + }; + + i2c_sfp:i2c-gpio-0 { + compatible = "i2c-gpio"; + sda-gpios = <&pio 9 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + scl-gpios = <&pio 10 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + i2c-gpio,delay-us = <2>; + #address-cells = <1>; + #size-cells = <0>; + }; + + sfp1:sfp-wan { + compatible = "sff,sfp"; + i2c-bus = <&i2c_sfp>; + los-gpios = <&pio 3 GPIO_ACTIVE_HIGH>; + mod-def0-gpios = <&pio 35 GPIO_ACTIVE_HIGH>; + tx-disable-gpios = <&pio 12 GPIO_ACTIVE_HIGH>; + tx-fault-gpios = <&pio 34 GPIO_ACTIVE_HIGH>; + maximum-power-milliwatt = <3000>; + }; + + reg_3p3v: regulator-3p3v { + compatible = "regulator-fixed"; + regulator-name = "fixed-3.3V"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + }; + + usb_vbus: regulator-usb-vbus { + compatible = "regulator-fixed"; + regulator-name = "usb_vbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + enable-active-high; + regulator-boot-on; + }; + + nmbm_spim_nand { + compatible = "generic,nmbm"; + + #address-cells = <1>; + #size-cells = <1>; + + lower-mtd-device = <&spi_nand>; + forced-create; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "BL2"; + reg = <0x00000 0x0100000>; + read-only; + }; + + partition@100000 { + label = "u-boot-env"; + reg = <0x0100000 0x0080000>; + }; + + partition@180000 { + label = "Factory"; + reg = <0x180000 0x0200000>; + }; + + partition@380000 { + label = "FIP"; + reg = <0x380000 0x0200000>; + }; + + partition@580000 { + label = "ubi"; + reg = <0x580000 0x7200000>; + }; + }; + }; +}; + +&uart0 { + status = "okay"; +}; + +&watchdog { + status = "okay"; +}; + +ð { + status = "okay"; + + gmac0: mac@0 { + compatible = "mediatek,eth-mac"; + reg = <0>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + link-gpio = <&pio 7 0>; + phy-handle = <&phy5>; + label = "lan2"; + }; + }; + + gmac1: mac@1 { + compatible = "mediatek,eth-mac"; + reg = <1>; + phy-mode = "2500base-x"; + phy-handle = <&phy6>; + }; + + mdio: mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + + phy5: phy@5 { + compatible = "ethernet-phy-id67c9.de0a"; + reg = <5>; + }; + + phy6: phy@6 { + compatible = "ethernet-phy-id67c9.de0a"; + reg = <6>; + }; + }; +}; + +&gsw { + mediatek,mdio = <&mdio>; + mediatek,portmap = "lllll"; + mediatek,mdio_master_pinmux = <1>; + reset-gpios = <&pio 39 0>; + interrupt-parent = <&pio>; + interrupts = <38 IRQ_TYPE_LEVEL_HIGH>; + status = "okay"; + + port5: port@5 { + compatible = "mediatek,mt753x-port"; + reg = <5>; + phy-mode = "sgmii"; + + fixed-link { + speed = <2500>; + full-duplex; + }; + }; + + port6: port@6 { + compatible = "mediatek,mt753x-port"; + /* mediatek,ssc-on; */ + reg = <6>; + phy-mode = "sgmii"; + fixed-link { + speed = <2500>; + full-duplex; + }; + }; +}; + +&hnat { + mtketh-wan = "eth1"; + mtketh-lan = "eth0"; + mtketh-max-gmac = <2>; + status = "okay"; +}; + +&spi0 { + pinctrl-names = "default"; + pinctrl-0 = <&spi0_flash_pins>; + status = "okay"; + spi_nand: spi_nand@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "spi-nand"; + spi-cal-enable; + spi-cal-mode = "read-data"; + spi-cal-datalen = <7>; + spi-cal-data = /bits/ 8 <0x53 0x50 0x49 0x4E 0x41 0x4E 0x44>; + spi-cal-addrlen = <5>; + spi-cal-addr = /bits/ 32 <0x0 0x0 0x0 0x0 0x0>; + reg = <0>; + spi-max-frequency = <52000000>; + spi-tx-bus-width = <4>; + spi-rx-bus-width = <4>; + }; +}; + +&spi1 { + pinctrl-names = "default"; + pinctrl-0 = <&spic_pins>; + status = "disabled"; + + slb9670: slb9670@0 { + compatible = "infineon,slb9670"; + reg = <0>; /* CE0 */ + #address-cells = <1>; + #size-cells = <0>; + spi-cal-enable; + spi-cal-mode = "read-data"; + spi-cal-datalen = <2>; + spi-cal-data = /bits/ 8 <0x00 0x1b>; + spi-max-frequency = <20000000>; + }; +}; + +&pio { + + i2c_pins: i2c-pins-g0 { + mux { + function = "i2c"; + groups = "i2c0_0"; + }; + }; + + pcm_pins: pcm-pins-g0 { + mux { + function = "pcm"; + groups = "pcm"; + }; + }; + + pwm0_pin: pwm0-pin-g0 { + mux { + function = "pwm"; + groups = "pwm0_0"; + }; + }; + + pwm1_pin: pwm1-pin-g0 { + mux { + function = "pwm"; + groups = "pwm1_0"; + }; + }; + + pwm2_pin: pwm2-pin { + mux { + function = "pwm"; + groups = "pwm2"; + }; + }; + + spi0_flash_pins: spi0-pins { + mux { + function = "spi"; + groups = "spi0", "spi0_wp_hold"; + }; + + conf-pu { + pins = "SPI0_CS", "SPI0_HOLD", "SPI0_WP"; + drive-strength = ; + bias-pull-up = ; + }; + + conf-pd { + pins = "SPI0_CLK", "SPI0_MOSI", "SPI0_MISO"; + drive-strength = ; + bias-pull-down = ; + }; + }; + + spic_pins: spi1-pins { + mux { + function = "spi"; + groups = "spi1_1"; + }; + }; + + uart1_pins: uart1-pins-g1 { + mux { + function = "uart"; + groups = "uart1_1"; + }; + }; + + uart2_pins: uart2-pins-g1 { + mux { + function = "uart"; + groups = "uart2_1"; + }; + }; +}; + +&xhci { + vusb33-supply = <®_3p3v>; + vbus-supply = <&usb_vbus>; + status = "okay"; +}; diff --git a/target/linux/mediatek/files-5.4/drivers/net/phy/gpy211.c b/target/linux/mediatek/files-5.4/drivers/net/phy/gpy211.c index 1f8394db21f..be14f2aca99 100644 --- a/target/linux/mediatek/files-5.4/drivers/net/phy/gpy211.c +++ b/target/linux/mediatek/files-5.4/drivers/net/phy/gpy211.c @@ -1,31 +1,261 @@ // SPDX-License-Identifier: GPL-2.0+ #include #include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include -static int gpy211_phy_config_init(struct phy_device *phydev) +struct t_phydev { + // struct timer_list timer; + struct phy_device *phydev; + struct delayed_work dw; +}; + +#define SUPPORT_ESMD_EVENT 1 + +#if defined(SUPPORT_ESMD_EVENT) +#define MAX_PAYLOAD_LEN 64 +#define NETLINK_MONITOR 30 +static ssize_t user_pid_write_proc(struct file *file, const char *buffer, size_t count, loff_t *data); +void kerSysRecvFrmMonitorTask(struct sk_buff *skb); +static void kerSysInitMonitorSocket(void); +static void kerSysCleanupMonitorSocket(void); +void kerSysSendtoMonitorTask(int msgType, char *msgData, int msgDataLen); +static struct sock *g_monitor_nl_sk; +static int g_monitor_nl_pid = 0; +static int kmonitor_proc_is_init = 0; +struct proc_dir_entry *kmonitor_proc_dir = NULL; +struct proc_dir_entry *kmonitor_proc_user_pid = NULL; +#endif + +#define PHY_MIISTAT 0x18 + +#define PHY_MIISTAT_SPD_MASK GENMASK(2, 0) +#define PHY_MIISTAT_DPX BIT(3) +#define PHY_MIISTAT_LS BIT(10) + +#define PHY_MIISTAT_SPD_10 0 +#define PHY_MIISTAT_SPD_100 1 +#define PHY_MIISTAT_SPD_1000 2 +#define PHY_MIISTAT_SPD_2500 4 + +void gpy211_status_timer(struct work_struct *t); + +// Starder Magament Registers +#define MDIO_MMD_STD 0x0 +#define VSPEC1_NBT_DS_CTRL 0xA + #define DOWNSHIFT_THR_MASK GENMASK(6, 2) + #define DOWNSHIFT_EN BIT(1) + +#define DEFAULT_INTEL_GPY211_PHYID1_VALUE 0x67c9 + +#define MAXLINEAR_MAX_LED_INDEX 4 + +#if defined(SUPPORT_ESMD_EVENT) +static ssize_t user_pid_write_proc(struct file *file, const char *buffer, size_t count, loff_t *data) { - int sgmii_reg = phy_read_mmd(phydev, MDIO_MMD_VEND1, 8); + char val_string[5]; + uint user_pid; - if (sgmii_reg != 0x24e2){ - phy_write_mmd(phydev, MDIO_MMD_VEND1, 8, 0x24e2); + if (count > sizeof(val_string)) + { + return -EINVAL; + } + + if (copy_from_user(val_string, buffer, count)) + { + return -EFAULT; } - phy_write_mmd(phydev, MDIO_MMD_VEND1, 1, 0x03f0); + val_string[count] = '\0'; + + sscanf(val_string, "%d", &user_pid) ; + + g_monitor_nl_pid = user_pid; + printk(KERN_INFO "set user_pid flag to %d\n", g_monitor_nl_pid); + + return count; +} + +static const struct file_operations kmonitor_proc_fops = +{ + .read = NULL, + .write = user_pid_write_proc, +}; + +void kerSysRecvFrmMonitorTask(struct sk_buff *skb) +{ + /*process the message here*/ + printk(KERN_WARNING "unexpected skb received at %s \n",__FUNCTION__); + kfree_skb(skb); + return; +} + +void kerSysInitMonitorSocket(void) +{ + struct netlink_kernel_cfg cfg = {0}; + + cfg.input = kerSysRecvFrmMonitorTask; + cfg.groups = 0, + cfg.cb_mutex = NULL; + + g_monitor_nl_sk = netlink_kernel_create(&init_net, NETLINK_MONITOR, &cfg); + + if(!g_monitor_nl_sk) + { + printk(KERN_ERR "Failed to create a netlink socket for monitor\n"); + return; + } + +} + +void kerSysCleanupMonitorSocket(void) +{ + g_monitor_nl_pid = 0 ; + sock_release(g_monitor_nl_sk->sk_socket); +} + +void kerSysSendtoMonitorTask(int msgType, char *msgData, int msgDataLen) +{ + struct sk_buff *skb = NULL; + struct nlmsghdr *nl_msgHdr = NULL; + unsigned int nl_msgLen; + + if(!g_monitor_nl_pid) + { + printk(KERN_INFO "message received before monitor task is initialized %s \n",__FUNCTION__); + return; + } + + if(msgData && (msgDataLen > MAX_PAYLOAD_LEN)) + { + printk(KERN_ERR "invalid message len in %s",__FUNCTION__); + return; + } + + nl_msgLen = NLMSG_SPACE(msgDataLen); + + /*Alloc skb ,this check helps to call the fucntion from interrupt context */ + + if(in_atomic()) + { + skb = alloc_skb(nl_msgLen, GFP_ATOMIC); + } + else + { + skb = alloc_skb(nl_msgLen, GFP_KERNEL); + } + + if(!skb) + { + printk(KERN_ERR "failed to alloc skb in %s",__FUNCTION__); + return; + } + + nl_msgHdr = (struct nlmsghdr *)skb->data; + nl_msgHdr->nlmsg_type = msgType; + nl_msgHdr->nlmsg_pid = 0;/*from kernel */ + nl_msgHdr->nlmsg_len = nl_msgLen; + nl_msgHdr->nlmsg_flags = 0; + + if(msgData) + { + memcpy(NLMSG_DATA(nl_msgHdr), msgData, msgDataLen); + } + + // NETLINK_CB(skb).pid = 0; /*from kernel */ + + skb->len = nl_msgLen; + + netlink_unicast(g_monitor_nl_sk, skb, g_monitor_nl_pid, MSG_DONTWAIT); + + return; +} +#endif + +static int gpy211_phy_config_init(struct phy_device *phydev) +{ return 0; } +#define MAX_RETRY_TIMES 80 +#define RETRY_INTERVAL 10 // unit is ms int gpy211_phy_probe(struct phy_device *phydev) { int sgmii_reg = phy_read_mmd(phydev, MDIO_MMD_VEND1, 8); + struct device_node *of_node = phydev->mdio.dev.of_node; + u32 reg_value[MAXLINEAR_MAX_LED_INDEX] = {0}; + int ret; + int i=0; + u32 phyid1; + int buf = 0; + + /* + * After reset signal to GPY211B1VC(SSTEP SLN8A), the chip may take 600 ms to bootup complete. + * driver can successfully read/write the register after bootup complete. + * If phy is ready, the STD_PHYID1(Register 0.2) should be 0x67c9. + */ + i = MAX_RETRY_TIMES; + while (i) { + phyid1 = phy_read_mmd(phydev, MDIO_MMD_STD, MDIO_DEVID1); + if ( phyid1 == DEFAULT_INTEL_GPY211_PHYID1_VALUE ) + break; + + msleep(RETRY_INTERVAL); + i--; + } + if (!i) { + phydev_err(phydev, "phy is not ready over %d ms!\n", (MAX_RETRY_TIMES-i)*10); + }else { + phydev_info(phydev, "driver wait %d ms for phy ready!\n", (MAX_RETRY_TIMES-i)*10); + } + + ret = of_property_read_u32_array(of_node, "maxlinear,led-reg", reg_value, MAXLINEAR_MAX_LED_INDEX); + + if (ret < 0) { + phydev_info(phydev, "not config \"maxlinear,led-reg\" parameter\n"); + } else { + for(i=0;iphydev = phydev; + int ret; ret = genphy_read_abilities(phydev); @@ -33,6 +263,7 @@ static int gpy211_get_features(struct phy_device *phydev) return ret; /* GPY211 with rate adaption supports 100M/1G/2.5G speed. */ + linkmode_clear_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT, phydev->supported); linkmode_clear_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, @@ -40,9 +271,94 @@ static int gpy211_get_features(struct phy_device *phydev) linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseX_Full_BIT, phydev->supported); + //set timer to query status + // timer_setup(&t_phy->timer, gpy211_status_timer, 0); + // mod_timer(&t_phy->timer, jiffies + 20*HZ); + INIT_DELAYED_WORK(&t_phy->dw, gpy211_status_timer); + schedule_delayed_work(&t_phy->dw, msecs_to_jiffies(2000)); + return 0; } +static int gpy_read_status(struct phy_device *phydev) +{ + int old_link = phydev->link; + const char *speed; + const char *duplex; + const char *interface; + phydev_dbg(phydev, "### line[%d] addr[%d] link[%d] phyid[0x%x]\n", __LINE__, phydev->mdio.addr, phydev->link, phydev->phy_id); + struct device_node *of_node = phydev->mdio.dev.of_node; +#if defined(SUPPORT_ESMD_EVENT) + char temp[64] = "\0"; +#endif + + // int ret = phydev->mdio.bus->read(phydev->mdio.bus, phydev->mdio.addr, PHY_MIISTAT); + int ret = phy_read_mmd(phydev, MDIO_MMD_STD, PHY_MIISTAT); + + if(ret) + { + phydev_dbg(phydev, "### line[%d] addr[%d] val=[0x%x] link[%d] phyid[0x%x]\n", __LINE__, phydev->mdio.addr, ret, phydev->link, phydev->phy_id); + phydev->link = (ret & PHY_MIISTAT_LS) ? 1 : 0; + phydev->duplex = (ret & PHY_MIISTAT_DPX) ? DUPLEX_FULL : DUPLEX_HALF; + duplex = (phydev->duplex == DUPLEX_FULL) ? "F" : "H"; + switch (FIELD_GET(PHY_MIISTAT_SPD_MASK, ret)) { + case PHY_MIISTAT_SPD_10: + phydev->speed = SPEED_10; + speed = "10"; + break; + case PHY_MIISTAT_SPD_100: + phydev->speed = SPEED_100; + speed = "100"; + break; + case PHY_MIISTAT_SPD_1000: + phydev->speed = SPEED_1000; + speed = "1000"; + break; + case PHY_MIISTAT_SPD_2500: + phydev->speed = SPEED_2500; + speed = "2500"; + break; + } + + of_property_read_string(of_node, "ifname", &interface); + + phydev_dbg(phydev, "### line[%d] addr[%d] old[%d] newlink[%d] \n", __LINE__, phydev->mdio.addr, old_link, phydev->link); + + if(old_link != phydev->link) + { + if(phydev->link) + { + phydev_info(phydev, "###phy_addr[%d] [%s] link up speed[%s]\n", phydev->mdio.addr, interface, speed); +#if defined(SUPPORT_ESMD_EVENT) + sprintf(temp, "%s:up:%s:%s", interface, speed, duplex); +#endif + } + else + { + phydev_info(phydev, "###phy_addr[%d] [%s] link down \n", phydev->mdio.addr, interface); +#if defined(SUPPORT_ESMD_EVENT) + sprintf(temp, "%s:down", interface); +#endif + } +#if defined(SUPPORT_ESMD_EVENT) + kerSysSendtoMonitorTask(0, temp, sizeof(temp)); +#endif + } + } + + return 0; +} + +void gpy211_status_timer(struct work_struct *t) +{ + struct t_phydev *t_phy = container_of(t, struct t_phydev, dw.work); + gpy_read_status(t_phy->phydev); + + //trigger timer again + // mod_timer(&t_phy->timer, jiffies + HZ); + schedule_delayed_work(&t_phy->dw, msecs_to_jiffies(2000)); +} + static struct phy_driver gpy211_phy_driver[] = { { PHY_ID_MATCH_MODEL(0x67c9de0a), @@ -50,6 +366,7 @@ static struct phy_driver gpy211_phy_driver[] = { .config_init = gpy211_phy_config_init, .probe = gpy211_phy_probe, .get_features = gpy211_get_features, + .read_status = gpy_read_status, } }; diff --git a/target/linux/mediatek/image/mt7981.mk b/target/linux/mediatek/image/mt7981.mk index 66cebfdf6ab..3b755ecbf58 100644 --- a/target/linux/mediatek/image/mt7981.mk +++ b/target/linux/mediatek/image/mt7981.mk @@ -781,13 +781,13 @@ define Device/routerich_ax3000 endef TARGET_DEVICES += routerich_ax3000 -define Device/zhao_7981-r128 +define Device/zhao_7981-r128-dsa DEVICE_VENDOR := ZHAO - DEVICE_MODEL := 7981 R128 - DEVICE_DTS := mt7981-spim-snand-7981r128 + DEVICE_MODEL := 7981 R128-DSA + DEVICE_DTS := mt7981-spim-snand-7981r128-dsa DEVICE_DTS_DIR := $(DTS_DIR)/mediatek DEVICE_PACKAGES := $(MT7981_USB_PKGS) - SUPPORTED_DEVICES := zhao,7981-r128 + SUPPORTED_DEVICES := zhao,7981-r128-dsa UBINIZE_OPTS := -E 5 BLOCKSIZE := 128k PAGESIZE := 2048 @@ -797,4 +797,22 @@ define Device/zhao_7981-r128 IMAGE/factory.bin := append-ubi | check-size $$$$(IMAGE_SIZE) IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata endef -TARGET_DEVICES += zhao_7981-r128 +TARGET_DEVICES += zhao_7981-r128-dsa + +define Device/zhao_7981-r128-gsw + DEVICE_VENDOR := ZHAO + DEVICE_MODEL := 7981 R128-GSW + DEVICE_DTS := mt7981-spim-snand-7981r128-gsw + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + DEVICE_PACKAGES := $(MT7981_USB_PKGS) + SUPPORTED_DEVICES := zhao,7981-r128-gsw + UBINIZE_OPTS := -E 5 + BLOCKSIZE := 128k + PAGESIZE := 2048 + IMAGE_SIZE := 114688k + KERNEL_IN_UBI := 1 + IMAGES += factory.bin + IMAGE/factory.bin := append-ubi | check-size $$$$(IMAGE_SIZE) + IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata +endef +TARGET_DEVICES += zhao_7981-r128-gsw diff --git a/target/linux/mediatek/mt7981/base-files/etc/board.d/01_leds b/target/linux/mediatek/mt7981/base-files/etc/board.d/01_leds index 31cd7eaf600..bb1c3608258 100755 --- a/target/linux/mediatek/mt7981/base-files/etc/board.d/01_leds +++ b/target/linux/mediatek/mt7981/base-files/etc/board.d/01_leds @@ -71,7 +71,7 @@ routerich,ax3000) ucidef_set_led_default "green" "GREEN" "green:status" "0" ucidef_set_led_default "blue" "BLUE" "blue:status" "1" ;; -mediatek,mt7981-spim-snand-7981r128) +mediatek,zhao-7981r128*) ucidef_set_led_netdev "lan" "LAN" "LAN" "lan2" ucidef_set_led_netdev "sfp" "SFP" "SFP" "eth1" ucidef_set_led_netdev "wifi5g" "WIFI5G" "WIFI5G" "rax0" diff --git a/target/linux/mediatek/mt7981/base-files/etc/board.d/02_network b/target/linux/mediatek/mt7981/base-files/etc/board.d/02_network index f04031c5250..24b7ef2a743 100755 --- a/target/linux/mediatek/mt7981/base-files/etc/board.d/02_network +++ b/target/linux/mediatek/mt7981/base-files/etc/board.d/02_network @@ -99,6 +99,14 @@ mediatek_setup_interfaces() ruijie,rg-x30e*) ucidef_set_interfaces_lan_wan "lan1 lan2 lan3" wan ;; + mediatek,zhao-7981r128-d) + ucidef_set_interfaces_lan_wan "lan1 lan2" eth1 + ;; + mediatek,zhao-7981r128-g) + ucidef_set_interfaces_lan_wan "eth0" "eth1" + ucidef_add_switch "switch0" \ + "0:lan" "1:lan" "2:lan" "3:lan" "4:lan" "5:lan" "6u@eth0" + ;; *) ucidef_set_interfaces_lan_wan "lan1 lan2 lan3 lan4" eth1 ;; @@ -291,7 +299,7 @@ mediatek_setup_macs() lan_mac=$(macaddr_add $wifi_mac -1) wan_mac=$(macaddr_add $wifi_mac -2) ;; - mediatek,mt7981-spim-snand-7981r128) + mediatek,zhao-7981r128*) lan_mac=$(mtd_get_mac_binary $part_name 0x04) wan_mac=$(macaddr_add $lan_mac 1) label_mac=$lan_mac diff --git a/target/linux/mediatek/mt7981/base-files/lib/upgrade/platform.sh b/target/linux/mediatek/mt7981/base-files/lib/upgrade/platform.sh index 363d495c286..1b8ec7a67d1 100644 --- a/target/linux/mediatek/mt7981/base-files/lib/upgrade/platform.sh +++ b/target/linux/mediatek/mt7981/base-files/lib/upgrade/platform.sh @@ -212,6 +212,7 @@ platform_do_upgrade() { *nokia,ea0326gmp* |\ *newland,nl-wr8103* |\ newland,nl-wr9103 |\ + mediatek,zhao-7981r128* |\ *snand*) nand_do_upgrade "$1" ;; @@ -266,6 +267,7 @@ platform_check_image() { *newland,nl-wr8103* |\ newland,nl-wr9103 |\ nradio,wt9103 |\ + mediatek,zhao-7981r128* |\ *snand* |\ *emmc* |\ routerich,ax3000) From d5cd7c31822980d2d35de018c96fc99f64197da6 Mon Sep 17 00:00:00 2001 From: hchyhchyxh <641953443@qq.com> Date: Wed, 11 Sep 2024 12:41:16 +0800 Subject: [PATCH 5/7] update --- .../mt7981-spim-snand-7981r128-gsw.dts | 22 +++++++------------ .../mt7981/base-files/etc/board.d/02_network | 2 +- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-snand-7981r128-gsw.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-snand-7981r128-gsw.dts index d630703b087..20f7760883b 100644 --- a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-snand-7981r128-gsw.dts +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-snand-7981r128-gsw.dts @@ -165,43 +165,38 @@ compatible = "mediatek,eth-mac"; reg = <0>; phy-mode = "2500base-x"; - + ext-phy-reg = <5>; + ext-phy-reset-gpios = <&pio 7 0>; fixed-link { speed = <2500>; full-duplex; pause; - link-gpio = <&pio 7 0>; - phy-handle = <&phy5>; - label = "lan2"; }; }; - gmac1: mac@1 { + gmac1: mac@1 { compatible = "mediatek,eth-mac"; reg = <1>; phy-mode = "2500base-x"; - phy-handle = <&phy6>; + managed = "in-band-status"; + sfp = <&sfp1>; }; mdio: mdio-bus { #address-cells = <1>; #size-cells = <0>; - + phy5: phy@5 { compatible = "ethernet-phy-id67c9.de0a"; reg = <5>; }; - phy6: phy@6 { - compatible = "ethernet-phy-id67c9.de0a"; - reg = <6>; - }; }; }; &gsw { mediatek,mdio = <&mdio>; - mediatek,portmap = "lllll"; + mediatek,portmap = "ll"; mediatek,mdio_master_pinmux = <1>; reset-gpios = <&pio 39 0>; interrupt-parent = <&pio>; @@ -212,7 +207,6 @@ compatible = "mediatek,mt753x-port"; reg = <5>; phy-mode = "sgmii"; - fixed-link { speed = <2500>; full-duplex; @@ -221,7 +215,7 @@ port6: port@6 { compatible = "mediatek,mt753x-port"; - /* mediatek,ssc-on; */ + mediatek,ssc-on; reg = <6>; phy-mode = "sgmii"; fixed-link { diff --git a/target/linux/mediatek/mt7981/base-files/etc/board.d/02_network b/target/linux/mediatek/mt7981/base-files/etc/board.d/02_network index 24b7ef2a743..9177f703682 100755 --- a/target/linux/mediatek/mt7981/base-files/etc/board.d/02_network +++ b/target/linux/mediatek/mt7981/base-files/etc/board.d/02_network @@ -105,7 +105,7 @@ mediatek_setup_interfaces() mediatek,zhao-7981r128-g) ucidef_set_interfaces_lan_wan "eth0" "eth1" ucidef_add_switch "switch0" \ - "0:lan" "1:lan" "2:lan" "3:lan" "4:lan" "5:lan" "6u@eth0" + "1:lan:1" "5:lan:2" "6u@eth0" ;; *) ucidef_set_interfaces_lan_wan "lan1 lan2 lan3 lan4" eth1 From 3052dc6be88ddef3104e6c2f751d3dea3e7cc118 Mon Sep 17 00:00:00 2001 From: hchyhchyxh <641953443@qq.com> Date: Fri, 20 Sep 2024 19:17:56 +0800 Subject: [PATCH 6/7] update 7981r128 --- .../mt7981-spim-nand-7981r128-sdk.dts | 316 + .../mt7981-spim-snand-7981r128-dsa.dts | 38 +- .../mt7981-spim-snand-7981r128-gsw.dts | 38 +- target/linux/mediatek/image/mt7981.mk | 6 +- ...ort-SX-7981R128-for-mtk-sdk-v7.6.6.1.patch | 7591 +++++++++++++++++ 5 files changed, 7958 insertions(+), 31 deletions(-) create mode 100755 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-nand-7981r128-sdk.dts create mode 100644 target/linux/mediatek/patches-5.4/9921-support-SX-7981R128-for-mtk-sdk-v7.6.6.1.patch diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-nand-7981r128-sdk.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-nand-7981r128-sdk.dts new file mode 100755 index 00000000000..080f5c1909f --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-nand-7981r128-sdk.dts @@ -0,0 +1,316 @@ +/dts-v1/; +#include "mt7981.dtsi" +/ { + model = "7981R128"; + compatible = "mediatek,zhao-7981r128-d"; + chosen { + bootargs = "console=ttyS0,115200n1 loglevel=8 \ + earlycon=uart8250,mmio32,0x11002000"; + }; + + memory { + // fpga ddr2: 128MB*2 + reg = <0 0x40000000 0 0x10000000>; + }; + + + leds { + compatible = "gpio-leds"; + + SFP{ + label = "SFP"; + gpios = <&pio 4 1>; + default-state = "off"; + }; + + WIFI5G{ + label = "WIFI5G"; + gpios = <&pio 5 1>; + default-state = "off"; + }; + + WIFI2G{ + label = "WIFI2G"; + gpios = <&pio 6 1>; + default-state = "off"; + }; + + LAN{ + label = "LAN"; + gpios = <&pio 8 1>; + default-state = "off"; + }; + + POWER{ + label = "POWER"; + gpios = <&pio 13 1>; + default-state = "on"; + }; + + }; + gpio-keys { + compatible = "gpio-keys"; + + reset { + label = "reset"; + linux,code = ; + gpios = <&pio 1 GPIO_ACTIVE_LOW>; + }; + }; + + i2c_sfp1: i2c-gpio-0 { + compatible = "i2c-gpio"; + sda-gpios = <&pio 9 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + scl-gpios = <&pio 10 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + i2c-gpio,delay-us = <2>; + #address-cells = <1>; + #size-cells = <0>; + }; + + sfp1: sfp-wan { + compatible = "sff,sfp"; + i2c-bus = <&i2c_sfp1>; + los-gpios = <&pio 3 GPIO_ACTIVE_HIGH>; + mod-def0-gpios = <&pio 35 GPIO_ACTIVE_LOW>; + tx-disable-gpios = <&pio 12 GPIO_ACTIVE_HIGH>; + tx-fault-gpios = <&pio 34 GPIO_ACTIVE_HIGH>; + maximum-power-milliwatt = <3000>; + }; + + nmbm_spim_nand { + compatible = "generic,nmbm"; + + #address-cells = <1>; + #size-cells = <1>; + + lower-mtd-device = <&spi_nand>; + forced-create; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "BL2"; + reg = <0x00000 0x0100000>; + read-only; + }; + + partition@100000 { + label = "u-boot-env"; + reg = <0x0100000 0x0080000>; + }; + + factory:partition@180000 { + label = "Factory"; + reg = <0x180000 0x0200000>; + }; + + partition@380000 { + label = "FIP"; + reg = <0x380000 0x0200000>; + }; + + partition@580000 { + label = "ubi"; + reg = <0x580000 0x7200000>; + }; + }; + }; +}; + +&afe { + pinctrl-names = "default"; + pinctrl-0 = <&pcm_pins>; + status = "okay"; +}; + +&uart0 { + status = "okay"; +}; + +&watchdog { + status = "okay"; +}; + +ð { + status = "okay"; + + gmac0: mac@0 { + compatible = "mediatek,eth-mac"; + reg = <0>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + + gmac1: mac@1 { + compatible = "mediatek,eth-mac"; + reg = <1>; + phy-mode = "2500base-x"; + managed = "in-band-status"; + sfp = <&sfp1>; + }; + + mdio: mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + + phy5: phy@5 { + compatible = "ethernet-phy-id67c9.de0a"; + reg = <5>; + reset-gpios = <&pio 7 1>; + reset-assert-us = <600>; + reset-deassert-us = <20000>; + phy-mode = "2500base-x"; + }; + + switch@0 { + compatible = "mediatek,mt7531"; + reg = <31>; + reset-gpios = <&pio 39 0>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@1 { + reg = <1>; + label = "lan1"; + }; + + port@5 { + reg = <5>; + label = "lan2"; + phy-mode = "2500base-x"; + phy-handle = <&phy5>; + }; + + port@6 { + reg = <6>; + label = "cpu"; + ethernet = <&gmac0>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + }; + }; + }; +}; + +&hnat { + mtketh-wan = "eth1"; + mtketh-lan = "lan"; + mtketh-max-gmac = <2>; + status = "okay"; +}; + +&spi0 { + pinctrl-names = "default"; + pinctrl-0 = <&spi0_flash_pins>; + status = "okay"; + spi_nand: spi_nand@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "spi-nand"; + reg = <0>; + spi-max-frequency = <52000000>; + spi-tx-buswidth = <4>; + spi-rx-buswidth = <4>; + }; +}; + +&pio { + + i2c_pins: i2c-pins-g0 { + mux { + function = "i2c"; + groups = "i2c0_0"; + }; + }; + + pcm_pins: pcm-pins-g0 { + mux { + function = "pcm"; + groups = "pcm"; + }; + }; + + pwm0_pin: pwm0-pin-g0 { + mux { + function = "pwm"; + groups = "pwm0_0"; + }; + }; + + pwm1_pin: pwm1-pin-g0 { + mux { + function = "pwm"; + groups = "pwm1_0"; + }; + }; + + pwm2_pin: pwm2-pin { + mux { + function = "pwm"; + groups = "pwm2"; + }; + }; + + spi0_flash_pins: spi0-pins { + mux { + function = "spi"; + groups = "spi0", "spi0_wp_hold"; + }; + + conf-pu { + pins = "SPI0_CS", "SPI0_HOLD", "SPI0_WP"; + drive-strength = ; + bias-pull-up = ; + }; + + conf-pd { + pins = "SPI0_CLK", "SPI0_MOSI", "SPI0_MISO"; + drive-strength = ; + bias-pull-down = ; + }; + }; + + spic_pins: spi1-pins { + mux { + function = "spi"; + groups = "spi1_1"; + }; + }; + + uart1_pins: uart1-pins-g1 { + mux { + function = "uart"; + groups = "uart1_1"; + }; + }; + + uart2_pins: uart2-pins-g1 { + mux { + function = "uart"; + groups = "uart2_1"; + }; + }; +}; + +&xhci { + mediatek,u3p-dis-msk = <0x0>; + phys = <&u2port0 PHY_TYPE_USB2>, + <&u3port0 PHY_TYPE_USB3>; + status = "okay"; +}; diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-snand-7981r128-dsa.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-snand-7981r128-dsa.dts index 20dc97b856e..3941148140f 100644 --- a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-snand-7981r128-dsa.dts +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-snand-7981r128-dsa.dts @@ -18,14 +18,17 @@ earlycon=uart8250,mmio32,0x11002000"; }; + memory { + reg = <0 0x40000000 0 0x20000000>; + }; gpio-keys { - compatible = "gpio-keys"; - reset { - label = "reset"; - linux,code = ; - gpios = <&pio 1 GPIO_ACTIVE_LOW>; - }; + compatible = "gpio-keys"; + reset { + label = "reset"; + linux,code = ; + gpios = <&pio 1 GPIO_ACTIVE_LOW>; + }; }; leds { @@ -63,7 +66,7 @@ }; - i2c_sfp:i2c-gpio-0 { + i2c_sfp1: i2c-gpio-0 { compatible = "i2c-gpio"; sda-gpios = <&pio 9 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; scl-gpios = <&pio 10 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; @@ -72,11 +75,11 @@ #size-cells = <0>; }; - sfp1:sfp-wan { + sfp1: sfp-wan { compatible = "sff,sfp"; - i2c-bus = <&i2c_sfp>; + i2c-bus = <&i2c_sfp1>; los-gpios = <&pio 3 GPIO_ACTIVE_HIGH>; - mod-def0-gpios = <&pio 35 GPIO_ACTIVE_HIGH>; + mod-def0-gpios = <&pio 35 GPIO_ACTIVE_LOW>; tx-disable-gpios = <&pio 12 GPIO_ACTIVE_HIGH>; tx-fault-gpios = <&pio 34 GPIO_ACTIVE_HIGH>; maximum-power-milliwatt = <3000>; @@ -125,7 +128,7 @@ reg = <0x0100000 0x0080000>; }; - partition@180000 { + factory:partition@180000 { label = "Factory"; reg = <0x180000 0x0200000>; }; @@ -143,6 +146,12 @@ }; }; +&afe { + pinctrl-names = "default"; + pinctrl-0 = <&pcm_pins>; + status = "okay"; +}; + &uart0 { status = "okay"; }; @@ -351,7 +360,8 @@ }; &xhci { - vusb33-supply = <®_3p3v>; - vbus-supply = <&usb_vbus>; - status = "okay"; + mediatek,u3p-dis-msk = <0x0>; + phys = <&u2port0 PHY_TYPE_USB2>, + <&u3port0 PHY_TYPE_USB3>; + status = "okay"; }; diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-snand-7981r128-gsw.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-snand-7981r128-gsw.dts index 20f7760883b..e1abe36769f 100644 --- a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-snand-7981r128-gsw.dts +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-snand-7981r128-gsw.dts @@ -18,14 +18,17 @@ earlycon=uart8250,mmio32,0x11002000"; }; + memory { + reg = <0 0x40000000 0 0x20000000>; + }; gpio-keys { - compatible = "gpio-keys"; - reset { - label = "reset"; - linux,code = ; - gpios = <&pio 1 GPIO_ACTIVE_LOW>; - }; + compatible = "gpio-keys"; + reset { + label = "reset"; + linux,code = ; + gpios = <&pio 1 GPIO_ACTIVE_LOW>; + }; }; leds { @@ -70,7 +73,7 @@ #size-cells = <0>; }; - i2c_sfp:i2c-gpio-0 { + i2c_sfp1: i2c-gpio-0 { compatible = "i2c-gpio"; sda-gpios = <&pio 9 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; scl-gpios = <&pio 10 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; @@ -79,11 +82,11 @@ #size-cells = <0>; }; - sfp1:sfp-wan { + sfp1: sfp-wan { compatible = "sff,sfp"; - i2c-bus = <&i2c_sfp>; + i2c-bus = <&i2c_sfp1>; los-gpios = <&pio 3 GPIO_ACTIVE_HIGH>; - mod-def0-gpios = <&pio 35 GPIO_ACTIVE_HIGH>; + mod-def0-gpios = <&pio 35 GPIO_ACTIVE_LOW>; tx-disable-gpios = <&pio 12 GPIO_ACTIVE_HIGH>; tx-fault-gpios = <&pio 34 GPIO_ACTIVE_HIGH>; maximum-power-milliwatt = <3000>; @@ -132,7 +135,7 @@ reg = <0x0100000 0x0080000>; }; - partition@180000 { + factory:partition@180000 { label = "Factory"; reg = <0x180000 0x0200000>; }; @@ -150,6 +153,12 @@ }; }; +&afe { + pinctrl-names = "default"; + pinctrl-0 = <&pcm_pins>; + status = "okay"; +}; + &uart0 { status = "okay"; }; @@ -350,7 +359,8 @@ }; &xhci { - vusb33-supply = <®_3p3v>; - vbus-supply = <&usb_vbus>; - status = "okay"; + mediatek,u3p-dis-msk = <0x0>; + phys = <&u2port0 PHY_TYPE_USB2>, + <&u3port0 PHY_TYPE_USB3>; + status = "okay"; }; diff --git a/target/linux/mediatek/image/mt7981.mk b/target/linux/mediatek/image/mt7981.mk index 3b755ecbf58..9a8088fec7b 100644 --- a/target/linux/mediatek/image/mt7981.mk +++ b/target/linux/mediatek/image/mt7981.mk @@ -784,10 +784,10 @@ TARGET_DEVICES += routerich_ax3000 define Device/zhao_7981-r128-dsa DEVICE_VENDOR := ZHAO DEVICE_MODEL := 7981 R128-DSA - DEVICE_DTS := mt7981-spim-snand-7981r128-dsa + DEVICE_DTS := mt7981-spim-nand-7981r128 DEVICE_DTS_DIR := $(DTS_DIR)/mediatek DEVICE_PACKAGES := $(MT7981_USB_PKGS) - SUPPORTED_DEVICES := zhao,7981-r128-dsa + SUPPORTED_DEVICES := mediatek,zhao-7981r128-d UBINIZE_OPTS := -E 5 BLOCKSIZE := 128k PAGESIZE := 2048 @@ -805,7 +805,7 @@ define Device/zhao_7981-r128-gsw DEVICE_DTS := mt7981-spim-snand-7981r128-gsw DEVICE_DTS_DIR := $(DTS_DIR)/mediatek DEVICE_PACKAGES := $(MT7981_USB_PKGS) - SUPPORTED_DEVICES := zhao,7981-r128-gsw + SUPPORTED_DEVICES := mediatek,zhao-7981r128-g UBINIZE_OPTS := -E 5 BLOCKSIZE := 128k PAGESIZE := 2048 diff --git a/target/linux/mediatek/patches-5.4/9921-support-SX-7981R128-for-mtk-sdk-v7.6.6.1.patch b/target/linux/mediatek/patches-5.4/9921-support-SX-7981R128-for-mtk-sdk-v7.6.6.1.patch new file mode 100644 index 00000000000..dc13df11568 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/9921-support-SX-7981R128-for-mtk-sdk-v7.6.6.1.patch @@ -0,0 +1,7591 @@ +Index: linux-5.4.284/drivers/gpio/gpiolib.c +=================================================================== +--- linux-5.4.284.orig/drivers/gpio/gpiolib.c ++++ linux-5.4.284/drivers/gpio/gpiolib.c +@@ -4391,6 +4391,55 @@ static int platform_gpio_count(struct de + return count; + } + ++ ++/** ++ * fwnode_gpiod_get_index - obtain a GPIO from firmware node ++ * @fwnode: handle of the firmware node ++ * @con_id: function within the GPIO consumer ++ * @index: index of the GPIO to obtain for the consumer ++ * @flags: GPIO initialization flags ++ * @label: label to attach to the requested GPIO ++ * ++ * This function can be used for drivers that get their configuration ++ * from opaque firmware. ++ * ++ * The function properly finds the corresponding GPIO using whatever is the ++ * underlying firmware interface and then makes sure that the GPIO ++ * descriptor is requested before it is returned to the caller. ++ * ++ * Returns: ++ * On successful request the GPIO pin is configured in accordance with ++ * provided @flags. ++ * ++ * In case of error an ERR_PTR() is returned. ++ */ ++struct gpio_desc *fwnode_gpiod_get_index(struct fwnode_handle *fwnode, ++ const char *con_id, int index, ++ enum gpiod_flags flags, ++ const char *label) ++{ ++ struct gpio_desc *desc; ++ char prop_name[32]; /* 32 is max size of property name */ ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) { ++ if (con_id) ++ snprintf(prop_name, sizeof(prop_name), "%s-%s", ++ con_id, gpio_suffixes[i]); ++ else ++ snprintf(prop_name, sizeof(prop_name), "%s", ++ gpio_suffixes[i]); ++ ++ desc = fwnode_get_named_gpiod(fwnode, prop_name, index, flags, ++ label); ++ if (!gpiod_not_found(desc)) ++ break; ++ } ++ ++ return desc; ++} ++EXPORT_SYMBOL_GPL(fwnode_gpiod_get_index); ++ + /** + * gpiod_count - return the number of GPIOs associated with a device / function + * or -ENOENT if no GPIO has been assigned to the requested function +Index: linux-5.4.284/drivers/gpio/gpiolib.h +=================================================================== +--- linux-5.4.284.orig/drivers/gpio/gpiolib.h ++++ linux-5.4.284/drivers/gpio/gpiolib.h +@@ -117,6 +117,8 @@ struct gpio_desc { + const char *name; + }; + ++#define gpiod_not_found(desc) (IS_ERR(desc) && PTR_ERR(desc) == -ENOENT) ++ + int gpiod_request(struct gpio_desc *desc, const char *label); + void gpiod_free(struct gpio_desc *desc); + int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id, +Index: linux-5.4.284/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c +=================================================================== +--- linux-5.4.284.orig/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c ++++ linux-5.4.284/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c +@@ -11,6 +11,7 @@ + #include + #include + #include ++#include + #include + #include + #include +Index: linux-5.4.284/drivers/net/ethernet/intel/igb/igb_main.c +=================================================================== +--- linux-5.4.284.orig/drivers/net/ethernet/intel/igb/igb_main.c ++++ linux-5.4.284/drivers/net/ethernet/intel/igb/igb_main.c +@@ -2294,6 +2294,7 @@ void igb_reset(struct igb_adapter *adapt + case e1000_82575: + case e1000_i210: + case e1000_i211: ++ fallthrough; + default: + pba = E1000_PBA_34K; + break; +Index: linux-5.4.284/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +=================================================================== +--- linux-5.4.284.orig/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c ++++ linux-5.4.284/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +@@ -58,11 +58,8 @@ static struct { + */ + static void mvpp2_mac_config(struct phylink_config *config, unsigned int mode, + const struct phylink_link_state *state); +-static void mvpp2_mac_link_up(struct phylink_config *config, +- struct phy_device *phy, +- unsigned int mode, phy_interface_t interface, +- int speed, int duplex, +- bool tx_pause, bool rx_pause); ++static void mvpp2_mac_link_up(struct phylink_config *config, unsigned int mode, ++ phy_interface_t interface, struct phy_device *phy); + + /* Queue modes */ + #define MVPP2_QDIST_SINGLE_MODE 0 +@@ -3471,9 +3468,8 @@ static void mvpp2_start_dev(struct mvpp2 + .interface = port->phy_interface, + }; + mvpp2_mac_config(&port->phylink_config, MLO_AN_INBAND, &state); +- mvpp2_mac_link_up(&port->phylink_config, NULL, +- MLO_AN_INBAND, port->phy_interface, +- SPEED_UNKNOWN, DUPLEX_UNKNOWN, false, false); ++ mvpp2_mac_link_up(&port->phylink_config, MLO_AN_INBAND, ++ port->phy_interface, NULL); + } + + netif_tx_start_all_queues(port->dev); +@@ -4832,8 +4828,8 @@ empty_set: + bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS); + } + +-static void mvpp22_xlg_link_state(struct mvpp2_port *port, +- struct phylink_link_state *state) ++static void mvpp22_xlg_pcs_get_state(struct mvpp2_port *port, ++ struct phylink_link_state *state) + { + u32 val; + +@@ -4852,8 +4848,8 @@ static void mvpp22_xlg_link_state(struct + state->pause |= MLO_PAUSE_RX; + } + +-static void mvpp2_gmac_link_state(struct mvpp2_port *port, +- struct phylink_link_state *state) ++static void mvpp2_gmac_pcs_get_state(struct mvpp2_port *port, ++ struct phylink_link_state *state) + { + u32 val; + +@@ -4886,8 +4882,8 @@ static void mvpp2_gmac_link_state(struct + state->pause |= MLO_PAUSE_TX; + } + +-static int mvpp2_phylink_mac_link_state(struct phylink_config *config, +- struct phylink_link_state *state) ++static void mvpp2_phylink_mac_pcs_get_state(struct phylink_config *config, ++ struct phylink_link_state *state) + { + struct mvpp2_port *port = mvpp2_phylink_to_port(config); + +@@ -4896,13 +4892,12 @@ static int mvpp2_phylink_mac_link_state( + mode &= MVPP22_XLG_CTRL3_MACMODESELECT_MASK; + + if (mode == MVPP22_XLG_CTRL3_MACMODESELECT_10G) { +- mvpp22_xlg_link_state(port, state); +- return 1; ++ mvpp22_xlg_pcs_get_state(port, state); ++ return; + } + } + +- mvpp2_gmac_link_state(port, state); +- return 1; ++ mvpp2_gmac_pcs_get_state(port, state); + } + + static void mvpp2_mac_an_restart(struct phylink_config *config) +@@ -5134,11 +5129,8 @@ static void mvpp2_mac_config(struct phyl + mvpp2_port_enable(port); + } + +-static void mvpp2_mac_link_up(struct phylink_config *config, +- struct phy_device *phy, +- unsigned int mode, phy_interface_t interface, +- int speed, int duplex, +- bool tx_pause, bool rx_pause) ++static void mvpp2_mac_link_up(struct phylink_config *config, unsigned int mode, ++ phy_interface_t interface, struct phy_device *phy) + { + struct mvpp2_port *port = mvpp2_phylink_to_port(config); + u32 val; +@@ -5193,7 +5185,7 @@ static void mvpp2_mac_link_down(struct p + + static const struct phylink_mac_ops mvpp2_phylink_ops = { + .validate = mvpp2_phylink_validate, +- .mac_link_state = mvpp2_phylink_mac_link_state, ++ .mac_pcs_get_state = mvpp2_phylink_mac_pcs_get_state, + .mac_an_restart = mvpp2_mac_an_restart, + .mac_config = mvpp2_mac_config, + .mac_link_up = mvpp2_mac_link_up, +Index: linux-5.4.284/drivers/net/ethernet/mediatek/mtk_eth_soc.c +=================================================================== +--- linux-5.4.284.orig/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ linux-5.4.284/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -441,8 +441,8 @@ init_err: + mac->id, phy_modes(state->interface), err); + } + +-static int mtk_mac_link_state(struct phylink_config *config, +- struct phylink_link_state *state) ++static void mtk_mac_pcs_get_state(struct phylink_config *config, ++ struct phylink_link_state *state) + { + struct mtk_mac *mac = container_of(config, struct mtk_mac, + phylink_config); +@@ -471,8 +471,6 @@ static int mtk_mac_link_state(struct phy + state->pause |= MLO_PAUSE_RX; + if (pmsr & MAC_MSR_TX_FC) + state->pause |= MLO_PAUSE_TX; +- +- return 1; + } + + static void mtk_mac_an_restart(struct phylink_config *config) +@@ -595,7 +593,7 @@ static void mtk_validate(struct phylink_ + + static const struct phylink_mac_ops mtk_phylink_ops = { + .validate = mtk_validate, +- .mac_link_state = mtk_mac_link_state, ++ .mac_pcs_get_state = mtk_mac_pcs_get_state, + .mac_an_restart = mtk_mac_an_restart, + .mac_config = mtk_mac_config, + .mac_link_down = mtk_mac_link_down, +Index: linux-5.4.284/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +=================================================================== +--- linux-5.4.284.orig/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c ++++ linux-5.4.284/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +@@ -843,10 +843,10 @@ static void stmmac_validate(struct phyli + __ETHTOOL_LINK_MODE_MASK_NBITS); + } + +-static int stmmac_mac_link_state(struct phylink_config *config, +- struct phylink_link_state *state) ++static void stmmac_mac_pcs_get_state(struct phylink_config *config, ++ struct phylink_link_state *state) + { +- return -EOPNOTSUPP; ++ state->link = 0; + } + + static void stmmac_mac_config(struct phylink_config *config, unsigned int mode, +@@ -925,10 +925,8 @@ static void stmmac_mac_link_down(struct + } + + static void stmmac_mac_link_up(struct phylink_config *config, +- struct phy_device *phy, + unsigned int mode, phy_interface_t interface, +- int speed, int duplex, +- bool tx_pause, bool rx_pause) ++ struct phy_device *phy) + { + struct stmmac_priv *priv = netdev_priv(to_net_dev(config->dev)); + +@@ -943,7 +941,7 @@ static void stmmac_mac_link_up(struct ph + + static const struct phylink_mac_ops stmmac_phylink_mac_ops = { + .validate = stmmac_validate, +- .mac_link_state = stmmac_mac_link_state, ++ .mac_pcs_get_state = stmmac_mac_pcs_get_state, + .mac_config = stmmac_mac_config, + .mac_an_restart = stmmac_mac_an_restart, + .mac_link_down = stmmac_mac_link_down, +Index: linux-5.4.284/drivers/net/ethernet/xilinx/xilinx_axienet_main.c +=================================================================== +--- linux-5.4.284.orig/drivers/net/ethernet/xilinx/xilinx_axienet_main.c ++++ linux-5.4.284/drivers/net/ethernet/xilinx/xilinx_axienet_main.c +@@ -1540,8 +1540,8 @@ static void axienet_validate(struct phyl + __ETHTOOL_LINK_MODE_MASK_NBITS); + } + +-static int axienet_mac_link_state(struct phylink_config *config, +- struct phylink_link_state *state) ++static void axienet_mac_pcs_get_state(struct phylink_config *config, ++ struct phylink_link_state *state) + { + struct net_device *ndev = to_net_dev(config->dev); + struct axienet_local *lp = netdev_priv(ndev); +@@ -1566,8 +1566,6 @@ static int axienet_mac_link_state(struct + + state->an_complete = 0; + state->duplex = 1; +- +- return 1; + } + + static void axienet_mac_an_restart(struct phylink_config *config) +@@ -1623,17 +1621,16 @@ static void axienet_mac_link_down(struct + } + + static void axienet_mac_link_up(struct phylink_config *config, +- struct phy_device *phy, +- unsigned int mode, phy_interface_t interface, +- int speed, int duplex, +- bool tx_pause, bool rx_pause) ++ unsigned int mode, ++ phy_interface_t interface, ++ struct phy_device *phy) + { + /* nothing meaningful to do */ + } + + static const struct phylink_mac_ops axienet_phylink_ops = { + .validate = axienet_validate, +- .mac_link_state = axienet_mac_link_state, ++ .mac_pcs_get_state = axienet_mac_pcs_get_state, + .mac_an_restart = axienet_mac_an_restart, + .mac_config = axienet_mac_config, + .mac_link_down = axienet_mac_link_down, +Index: linux-5.4.284/drivers/net/phy/Makefile +=================================================================== +--- linux-5.4.284.orig/drivers/net/phy/Makefile ++++ linux-5.4.284/drivers/net/phy/Makefile +@@ -1,7 +1,8 @@ + # SPDX-License-Identifier: GPL-2.0 + # Makefile for Linux PHY drivers and MDIO bus drivers + +-libphy-y := phy.o phy-c45.o phy-core.o phy_device.o ++libphy-y := phy.o phy-c45.o phy-core.o phy_device.o \ ++ linkmode.o + mdio-bus-y += mdio_bus.o mdio_device.o + + ifdef CONFIG_MDIO_DEVICE +Index: linux-5.4.284/drivers/net/phy/bcm87xx.c +=================================================================== +--- linux-5.4.284.orig/drivers/net/phy/bcm87xx.c ++++ linux-5.4.284/drivers/net/phy/bcm87xx.c +@@ -55,7 +55,7 @@ static int bcm87xx_of_reg_init(struct ph + u16 mask = be32_to_cpup(paddr++); + u16 val_bits = be32_to_cpup(paddr++); + int val; +- u32 regnum = MII_ADDR_C45 | (devid << 16) | reg; ++ u32 regnum = mdiobus_c45_addr(devid, reg); + val = 0; + if (mask) { + val = phy_read(phydev, regnum); +Index: linux-5.4.284/drivers/net/phy/cortina.c +=================================================================== +--- linux-5.4.284.orig/drivers/net/phy/cortina.c ++++ linux-5.4.284/drivers/net/phy/cortina.c +@@ -17,8 +17,7 @@ + + static int cortina_read_reg(struct phy_device *phydev, u16 regnum) + { +- return mdiobus_read(phydev->mdio.bus, phydev->mdio.addr, +- MII_ADDR_C45 | regnum); ++ return mdiobus_c45_read(phydev->mdio.bus, phydev->mdio.addr, 0, regnum); + } + + static int cortina_read_status(struct phy_device *phydev) +Index: linux-5.4.284/drivers/net/phy/fixed_phy.c +=================================================================== +--- linux-5.4.284.orig/drivers/net/phy/fixed_phy.c ++++ linux-5.4.284/drivers/net/phy/fixed_phy.c +@@ -167,8 +167,8 @@ static int fixed_phy_add_gpiod(unsigned + } + + int fixed_phy_add(unsigned int irq, int phy_addr, +- struct fixed_phy_status *status) { +- ++ struct fixed_phy_status *status) ++{ + return fixed_phy_add_gpiod(irq, phy_addr, status, NULL); + } + EXPORT_SYMBOL_GPL(fixed_phy_add); +Index: linux-5.4.284/drivers/net/phy/linkmode.c +=================================================================== +--- /dev/null ++++ linux-5.4.284/drivers/net/phy/linkmode.c +@@ -0,0 +1,95 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++#include ++ ++/** ++ * linkmode_resolve_pause - resolve the allowable pause modes ++ * @local_adv: local advertisement in ethtool format ++ * @partner_adv: partner advertisement in ethtool format ++ * @tx_pause: pointer to bool to indicate whether transmit pause should be ++ * enabled. ++ * @rx_pause: pointer to bool to indicate whether receive pause should be ++ * enabled. ++ * ++ * Flow control is resolved according to our and the link partners ++ * advertisements using the following drawn from the 802.3 specs: ++ * Local device Link partner ++ * Pause AsymDir Pause AsymDir Result ++ * 0 X 0 X Disabled ++ * 0 1 1 0 Disabled ++ * 0 1 1 1 TX ++ * 1 0 0 X Disabled ++ * 1 X 1 X TX+RX ++ * 1 1 0 1 RX ++ */ ++void linkmode_resolve_pause(const unsigned long *local_adv, ++ const unsigned long *partner_adv, ++ bool *tx_pause, bool *rx_pause) ++{ ++ __ETHTOOL_DECLARE_LINK_MODE_MASK(m); ++ ++ linkmode_and(m, local_adv, partner_adv); ++ if (linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, m)) { ++ *tx_pause = true; ++ *rx_pause = true; ++ } else if (linkmode_test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, m)) { ++ *tx_pause = linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, ++ partner_adv); ++ *rx_pause = linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, ++ local_adv); ++ } else { ++ *tx_pause = false; ++ *rx_pause = false; ++ } ++} ++EXPORT_SYMBOL_GPL(linkmode_resolve_pause); ++ ++/** ++ * linkmode_set_pause - set the pause mode advertisement ++ * @advertisement: advertisement in ethtool format ++ * @tx: boolean from ethtool struct ethtool_pauseparam tx_pause member ++ * @rx: boolean from ethtool struct ethtool_pauseparam rx_pause member ++ * ++ * Configure the advertised Pause and Asym_Pause bits according to the ++ * capabilities of provided in @tx and @rx. ++ * ++ * We convert as follows: ++ * tx rx Pause AsymDir ++ * 0 0 0 0 ++ * 0 1 1 1 ++ * 1 0 0 1 ++ * 1 1 1 0 ++ * ++ * Note: this translation from ethtool tx/rx notation to the advertisement ++ * is actually very problematical. Here are some examples: ++ * ++ * For tx=0 rx=1, meaning transmit is unsupported, receive is supported: ++ * ++ * Local device Link partner ++ * Pause AsymDir Pause AsymDir Result ++ * 1 1 1 0 TX + RX - but we have no TX support. ++ * 1 1 0 1 Only this gives RX only ++ * ++ * For tx=1 rx=1, meaning we have the capability to transmit and receive ++ * pause frames: ++ * ++ * Local device Link partner ++ * Pause AsymDir Pause AsymDir Result ++ * 1 0 0 1 Disabled - but since we do support tx and rx, ++ * this should resolve to RX only. ++ * ++ * Hence, asking for: ++ * rx=1 tx=0 gives Pause+AsymDir advertisement, but we may end up ++ * resolving to tx+rx pause or only rx pause depending on ++ * the partners advertisement. ++ * rx=0 tx=1 gives AsymDir only, which will only give tx pause if ++ * the partners advertisement allows it. ++ * rx=1 tx=1 gives Pause only, which will only allow tx+rx pause ++ * if the other end also advertises Pause. ++ */ ++void linkmode_set_pause(unsigned long *advertisement, bool tx, bool rx) ++{ ++ linkmode_mod_bit(ETHTOOL_LINK_MODE_Pause_BIT, advertisement, rx); ++ linkmode_mod_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, advertisement, ++ rx ^ tx); ++} ++EXPORT_SYMBOL_GPL(linkmode_set_pause); +Index: linux-5.4.284/drivers/net/phy/lxt.c +=================================================================== +--- linux-5.4.284.orig/drivers/net/phy/lxt.c ++++ linux-5.4.284/drivers/net/phy/lxt.c +@@ -168,8 +168,8 @@ static int lxt973a2_read_status(struct p + return lpa; + + /* If both registers are equal, it is suspect but not +- * impossible, hence a new try +- */ ++ * impossible, hence a new try ++ */ + } while (lpa == adv && retry--); + + mii_lpa_to_linkmode_lpa_t(phydev->lp_advertising, lpa); +Index: linux-5.4.284/drivers/net/phy/mdio_bus.c +=================================================================== +--- linux-5.4.284.orig/drivers/net/phy/mdio_bus.c ++++ linux-5.4.284/drivers/net/phy/mdio_bus.c +@@ -328,7 +328,7 @@ static inline void of_mdiobus_link_mdiod + #endif + + /** +- * mdiobus_create_device_from_board_info - create a full MDIO device given ++ * mdiobus_create_device - create a full MDIO device given + * a mdio_board_info structure + * @bus: MDIO bus to create the devices on + * @bi: mdio_board_info structure describing the devices +@@ -599,6 +599,38 @@ int __mdiobus_write(struct mii_bus *bus, + EXPORT_SYMBOL(__mdiobus_write); + + /** ++ * __mdiobus_modify_changed - Unlocked version of the mdiobus_modify function ++ * @bus: the mii_bus struct ++ * @addr: the phy address ++ * @regnum: register number to modify ++ * @mask: bit mask of bits to clear ++ * @set: bit mask of bits to set ++ * ++ * Read, modify, and if any change, write the register value back to the ++ * device. Any error returns a negative number. ++ * ++ * NOTE: MUST NOT be called from interrupt context. ++ */ ++int __mdiobus_modify_changed(struct mii_bus *bus, int addr, u32 regnum, ++ u16 mask, u16 set) ++{ ++ int new, ret; ++ ++ ret = __mdiobus_read(bus, addr, regnum); ++ if (ret < 0) ++ return ret; ++ ++ new = (ret & ~mask) | set; ++ if (new == ret) ++ return 0; ++ ++ ret = __mdiobus_write(bus, addr, regnum, new); ++ ++ return ret < 0 ? ret : 1; ++} ++EXPORT_SYMBOL_GPL(__mdiobus_modify_changed); ++ ++/** + * mdiobus_read_nested - Nested version of the mdiobus_read function + * @bus: the mii_bus struct + * @addr: the phy address +@@ -703,6 +735,27 @@ int mdiobus_write(struct mii_bus *bus, i + EXPORT_SYMBOL(mdiobus_write); + + /** ++ * mdiobus_modify - Convenience function for modifying a given mdio device ++ * register ++ * @bus: the mii_bus struct ++ * @addr: the phy address ++ * @regnum: register number to write ++ * @mask: bit mask of bits to clear ++ * @set: bit mask of bits to set ++ */ ++int mdiobus_modify(struct mii_bus *bus, int addr, u32 regnum, u16 mask, u16 set) ++{ ++ int err; ++ ++ mutex_lock(&bus->mdio_lock); ++ err = __mdiobus_modify_changed(bus, addr, regnum, mask, set); ++ mutex_unlock(&bus->mdio_lock); ++ ++ return err < 0 ? err : 0; ++} ++EXPORT_SYMBOL_GPL(mdiobus_modify); ++ ++/** + * mdio_bus_match - determine if given MDIO driver supports the given + * MDIO device + * @dev: target MDIO device +Index: linux-5.4.284/drivers/net/phy/national.c +=================================================================== +--- linux-5.4.284.orig/drivers/net/phy/national.c ++++ linux-5.4.284/drivers/net/phy/national.c +@@ -83,7 +83,8 @@ static int ns_ack_interrupt(struct phy_d + return ret; + + /* Clear the interrupt status bit by writing a “1” +- * to the corresponding bit in INT_CLEAR (2:0 are reserved) */ ++ * to the corresponding bit in INT_CLEAR (2:0 are reserved) ++ */ + ret = phy_write(phydev, DP83865_INT_CLEAR, ret & ~0x7); + + return ret; +@@ -122,7 +123,8 @@ static int ns_config_init(struct phy_dev + { + ns_giga_speed_fallback(phydev, ALL_FALLBACK_ON); + /* In the latest MAC or switches design, the 10 Mbps loopback +- is desired to be turned off. */ ++ * is desired to be turned off. ++ */ + ns_10_base_t_hdx_loopack(phydev, hdx_loopback_off); + return ns_ack_interrupt(phydev); + } +Index: linux-5.4.284/drivers/net/phy/phy-c45.c +=================================================================== +--- linux-5.4.284.orig/drivers/net/phy/phy-c45.c ++++ linux-5.4.284/drivers/net/phy/phy-c45.c +@@ -9,7 +9,7 @@ + #include + + /** +- * genphy_c45_setup_forced - configures a forced speed ++ * genphy_c45_pma_setup_forced - configures a forced speed + * @phydev: target phy_device struct + */ + int genphy_c45_pma_setup_forced(struct phy_device *phydev) +@@ -219,7 +219,7 @@ int genphy_c45_read_link(struct phy_devi + int val, devad; + bool link = true; + +- if (phydev->c45_ids.devices_in_package & MDIO_DEVS_AN) { ++ if (phydev->c45_ids.mmds_present & MDIO_DEVS_AN) { + val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1); + if (val < 0) + return val; +@@ -409,7 +409,7 @@ int genphy_c45_pma_read_abilities(struct + int val; + + linkmode_clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported); +- if (phydev->c45_ids.devices_in_package & MDIO_DEVS_AN) { ++ if (phydev->c45_ids.mmds_present & MDIO_DEVS_AN) { + val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1); + if (val < 0) + return val; +Index: linux-5.4.284/drivers/net/phy/phy-core.c +=================================================================== +--- linux-5.4.284.orig/drivers/net/phy/phy-core.c ++++ linux-5.4.284/drivers/net/phy/phy-core.c +@@ -8,7 +8,7 @@ + + const char *phy_speed_to_str(int speed) + { +- BUILD_BUG_ON_MSG(__ETHTOOL_LINK_MODE_MASK_NBITS != 69, ++ BUILD_BUG_ON_MSG(__ETHTOOL_LINK_MODE_MASK_NBITS != 92, + "Enum ethtool_link_mode_bit_indices and phylib are out of sync. " + "If a speed or mode has been added please update phy_speed_to_str " + "and the PHY settings array.\n"); +@@ -42,6 +42,8 @@ const char *phy_speed_to_str(int speed) + return "100Gbps"; + case SPEED_200000: + return "200Gbps"; ++ case SPEED_400000: ++ return "400Gbps"; + case SPEED_UNKNOWN: + return "Unknown"; + default: +@@ -64,7 +66,8 @@ EXPORT_SYMBOL_GPL(phy_duplex_to_str); + + /* A mapping of all SUPPORTED settings to speed/duplex. This table + * must be grouped by speed and sorted in descending match priority +- * - iow, descending speed. */ ++ * - iow, descending speed. ++ */ + + #define PHY_SETTING(s, d, b) { .speed = SPEED_ ## s, .duplex = DUPLEX_ ## d, \ + .bit = ETHTOOL_LINK_MODE_ ## b ## _BIT} +@@ -382,9 +385,8 @@ int __phy_read_mmd(struct phy_device *ph + if (phydev->drv->read_mmd) { + val = phydev->drv->read_mmd(phydev, devad, regnum); + } else if (phydev->is_c45) { +- u32 addr = MII_ADDR_C45 | (devad << 16) | (regnum & 0xffff); +- +- val = __mdiobus_read(phydev->mdio.bus, phydev->mdio.addr, addr); ++ val = __mdiobus_c45_read(phydev->mdio.bus, phydev->mdio.addr, ++ devad, regnum); + } else { + struct mii_bus *bus = phydev->mdio.bus; + int phy_addr = phydev->mdio.addr; +@@ -439,10 +441,8 @@ int __phy_write_mmd(struct phy_device *p + if (phydev->drv->write_mmd) { + ret = phydev->drv->write_mmd(phydev, devad, regnum, val); + } else if (phydev->is_c45) { +- u32 addr = MII_ADDR_C45 | (devad << 16) | (regnum & 0xffff); +- +- ret = __mdiobus_write(phydev->mdio.bus, phydev->mdio.addr, +- addr, val); ++ ret = __mdiobus_c45_write(phydev->mdio.bus, phydev->mdio.addr, ++ devad, regnum, val); + } else { + struct mii_bus *bus = phydev->mdio.bus; + int phy_addr = phydev->mdio.addr; +Index: linux-5.4.284/drivers/net/phy/phy.c +=================================================================== +--- linux-5.4.284.orig/drivers/net/phy/phy.c ++++ linux-5.4.284/drivers/net/phy/phy.c +@@ -52,13 +52,13 @@ static const char *phy_state_to_str(enum + + static void phy_link_up(struct phy_device *phydev) + { +- phydev->phy_link_change(phydev, true, true); ++ phydev->phy_link_change(phydev, true); + phy_led_trigger_change_speed(phydev); + } + +-static void phy_link_down(struct phy_device *phydev, bool do_carrier) ++static void phy_link_down(struct phy_device *phydev) + { +- phydev->phy_link_change(phydev, false, do_carrier); ++ phydev->phy_link_change(phydev, false); + phy_led_trigger_change_speed(phydev); + } + +@@ -420,7 +420,7 @@ int phy_mii_ioctl(struct phy_device *phy + if (mdio_phy_id_is_c45(mii_data->phy_id)) { + prtad = mdio_phy_id_prtad(mii_data->phy_id); + devad = mdio_phy_id_devad(mii_data->phy_id); +- devad = MII_ADDR_C45 | devad << 16 | mii_data->reg_num; ++ devad = mdiobus_c45_addr(devad, mii_data->reg_num); + } else { + prtad = mii_data->phy_id; + devad = mii_data->reg_num; +@@ -433,7 +433,7 @@ int phy_mii_ioctl(struct phy_device *phy + if (mdio_phy_id_is_c45(mii_data->phy_id)) { + prtad = mdio_phy_id_prtad(mii_data->phy_id); + devad = mdio_phy_id_devad(mii_data->phy_id); +- devad = MII_ADDR_C45 | devad << 16 | mii_data->reg_num; ++ devad = mdiobus_c45_addr(devad, mii_data->reg_num); + } else { + prtad = mii_data->phy_id; + devad = mii_data->reg_num; +@@ -454,8 +454,7 @@ int phy_mii_ioctl(struct phy_device *phy + else if (val & BMCR_SPEED100) + phydev->speed = SPEED_100; + else phydev->speed = SPEED_10; +- } +- else { ++ } else { + if (phydev->autoneg == AUTONEG_DISABLE) + change_autoneg = true; + phydev->autoneg = AUTONEG_ENABLE; +@@ -554,7 +553,7 @@ static int phy_check_link_status(struct + phy_link_up(phydev); + } else if (!phydev->link && phydev->state != PHY_NOLINK) { + phydev->state = PHY_NOLINK; +- phy_link_down(phydev, true); ++ phy_link_down(phydev); + } + + return 0; +@@ -982,7 +981,7 @@ void phy_state_machine(struct work_struc + case PHY_HALTED: + if (phydev->link) { + phydev->link = 0; +- phy_link_down(phydev, true); ++ phy_link_down(phydev); + } + do_suspend = true; + break; +Index: linux-5.4.284/drivers/net/phy/phy_device.c +=================================================================== +--- linux-5.4.284.orig/drivers/net/phy/phy_device.c ++++ linux-5.4.284/drivers/net/phy/phy_device.c +@@ -31,6 +31,8 @@ + #include + #include + #include ++#include ++#include + + MODULE_DESCRIPTION("PHY library"); + MODULE_AUTHOR("Andy Fleming"); +@@ -675,23 +677,18 @@ EXPORT_SYMBOL(phy_device_create); + static int get_phy_c45_devs_in_pkg(struct mii_bus *bus, int addr, int dev_addr, + u32 *devices_in_package) + { +- int phy_reg, reg_addr; ++ int phy_reg; + +- reg_addr = MII_ADDR_C45 | dev_addr << 16 | MDIO_DEVS2; +- phy_reg = mdiobus_read(bus, addr, reg_addr); ++ phy_reg = mdiobus_c45_read(bus, addr, dev_addr, MDIO_DEVS2); + if (phy_reg < 0) + return -EIO; + *devices_in_package = phy_reg << 16; + +- reg_addr = MII_ADDR_C45 | dev_addr << 16 | MDIO_DEVS1; +- phy_reg = mdiobus_read(bus, addr, reg_addr); ++ phy_reg = mdiobus_c45_read(bus, addr, dev_addr, MDIO_DEVS1); + if (phy_reg < 0) + return -EIO; + *devices_in_package |= phy_reg; + +- /* Bit 0 doesn't represent a device, it indicates c22 regs presence */ +- *devices_in_package &= ~BIT(0); +- + return 0; + } + +@@ -709,11 +706,11 @@ static int get_phy_c45_devs_in_pkg(struc + * + */ + static int get_phy_c45_ids(struct mii_bus *bus, int addr, u32 *phy_id, +- struct phy_c45_device_ids *c45_ids) { +- int phy_reg; +- int i, reg_addr; ++ struct phy_c45_device_ids *c45_ids) ++{ + const int num_ids = ARRAY_SIZE(c45_ids->device_ids); + u32 *devs = &c45_ids->devices_in_package; ++ int i, phy_reg; + + /* Find first non-zero Devices In package. Device zero is reserved + * for 802.3 c45 complied PHYs, so don't probe it at first. +@@ -747,14 +744,12 @@ static int get_phy_c45_ids(struct mii_bu + if (!(c45_ids->devices_in_package & (1 << i))) + continue; + +- reg_addr = MII_ADDR_C45 | i << 16 | MII_PHYSID1; +- phy_reg = mdiobus_read(bus, addr, reg_addr); ++ phy_reg = mdiobus_c45_read(bus, addr, i, MII_PHYSID1); + if (phy_reg < 0) + return -EIO; + c45_ids->device_ids[i] = phy_reg << 16; + +- reg_addr = MII_ADDR_C45 | i << 16 | MII_PHYSID2; +- phy_reg = mdiobus_read(bus, addr, reg_addr); ++ phy_reg = mdiobus_c45_read(bus, addr, i, MII_PHYSID2); + if (phy_reg < 0) + return -EIO; + c45_ids->device_ids[i] |= phy_reg; +@@ -825,6 +820,7 @@ struct phy_device *get_phy_device(struct + int r; + + c45_ids.devices_in_package = 0; ++ c45_ids.mmds_present = 0; + memset(c45_ids.device_ids, 0xff, sizeof(c45_ids.device_ids)); + + r = get_phy_id(bus, addr, &phy_id, is_c45, &c45_ids); +@@ -915,16 +911,14 @@ struct phy_device *phy_find_first(struct + } + EXPORT_SYMBOL(phy_find_first); + +-static void phy_link_change(struct phy_device *phydev, bool up, bool do_carrier) ++static void phy_link_change(struct phy_device *phydev, bool up) + { + struct net_device *netdev = phydev->attached_dev; + +- if (do_carrier) { +- if (up) +- netif_carrier_on(netdev); +- else +- netif_carrier_off(netdev); +- } ++ if (up) ++ netif_carrier_on(netdev); ++ else ++ netif_carrier_off(netdev); + phydev->adjust_link(netdev); + } + +@@ -1109,9 +1103,8 @@ void phy_attached_info(struct phy_device + EXPORT_SYMBOL(phy_attached_info); + + #define ATTACHED_FMT "attached PHY driver [%s] (mii_bus:phy_addr=%s, irq=%s)" +-void phy_attached_print(struct phy_device *phydev, const char *fmt, ...) ++char *phy_attached_info_irq(struct phy_device *phydev) + { +- const char *drv_name = phydev->drv ? phydev->drv->name : "unbound"; + char *irq_str; + char irq_num[8]; + +@@ -1128,6 +1121,14 @@ void phy_attached_print(struct phy_devic + break; + } + ++ return kasprintf(GFP_KERNEL, "%s", irq_str); ++} ++EXPORT_SYMBOL(phy_attached_info_irq); ++ ++void phy_attached_print(struct phy_device *phydev, const char *fmt, ...) ++{ ++ const char *drv_name = phydev->drv ? phydev->drv->name : "unbound"; ++ char *irq_str = phy_attached_info_irq(phydev); + + if (!fmt) { + phydev_info(phydev, ATTACHED_FMT "\n", +@@ -1144,6 +1145,7 @@ void phy_attached_print(struct phy_devic + vprintk(fmt, ap); + va_end(ap); + } ++ kfree(irq_str); + } + EXPORT_SYMBOL(phy_attached_print); + +@@ -1439,6 +1441,78 @@ static bool phy_driver_is_genphy_kind(st + return ret; + } + ++/** ++ * fwnode_mdio_find_device - Given a fwnode, find the mdio_device ++ * @fwnode: pointer to the mdio_device's fwnode ++ * ++ * If successful, returns a pointer to the mdio_device with the embedded ++ * struct device refcount incremented by one, or NULL on failure. ++ * The caller should call put_device() on the mdio_device after its use. ++ */ ++struct mdio_device *fwnode_mdio_find_device(struct fwnode_handle *fwnode) ++{ ++ struct device *d; ++ ++ if (!fwnode) ++ return NULL; ++ ++ d = bus_find_device_by_fwnode(&mdio_bus_type, fwnode); ++ if (!d) ++ return NULL; ++ ++ return to_mdio_device(d); ++} ++EXPORT_SYMBOL(fwnode_mdio_find_device); ++ ++/** ++ * fwnode_phy_find_device - For provided phy_fwnode, find phy_device. ++ * ++ * @phy_fwnode: Pointer to the phy's fwnode. ++ * ++ * If successful, returns a pointer to the phy_device with the embedded ++ * struct device refcount incremented by one, or NULL on failure. ++ */ ++struct phy_device *fwnode_phy_find_device(struct fwnode_handle *phy_fwnode) ++{ ++ struct mdio_device *mdiodev; ++ ++ mdiodev = fwnode_mdio_find_device(phy_fwnode); ++ if (!mdiodev) ++ return NULL; ++ ++ if (mdiodev->flags & MDIO_DEVICE_FLAG_PHY) ++ return to_phy_device(&mdiodev->dev); ++ ++ put_device(&mdiodev->dev); ++ ++ return NULL; ++} ++EXPORT_SYMBOL(fwnode_phy_find_device); ++ ++/** ++ * fwnode_get_phy_node - Get the phy_node using the named reference. ++ * @fwnode: Pointer to fwnode from which phy_node has to be obtained. ++ * ++ * Refer return conditions of fwnode_find_reference(). ++ * For ACPI, only "phy-handle" is supported. Legacy DT properties "phy" ++ * and "phy-device" are not supported in ACPI. DT supports all the three ++ * named references to the phy node. ++ */ ++struct fwnode_handle *fwnode_get_phy_node(struct fwnode_handle *fwnode) ++{ ++ struct fwnode_handle *phy_node; ++ ++ /* Only phy-handle is used for ACPI */ ++ phy_node = fwnode_find_reference(fwnode, "phy-handle", 0); ++ if (is_acpi_node(fwnode) || !IS_ERR(phy_node)) ++ return phy_node; ++ phy_node = fwnode_find_reference(fwnode, "phy", 0); ++ if (IS_ERR(phy_node)) ++ phy_node = fwnode_find_reference(fwnode, "phy-device", 0); ++ return phy_node; ++} ++EXPORT_SYMBOL_GPL(fwnode_get_phy_node); ++ + bool phy_driver_is_genphy(struct phy_device *phydev) + { + return phy_driver_is_genphy_kind(phydev, +@@ -2378,6 +2452,32 @@ bool phy_validate_pause(struct phy_devic + } + EXPORT_SYMBOL(phy_validate_pause); + ++/** ++ * phy_get_pause - resolve negotiated pause modes ++ * @phydev: phy_device struct ++ * @tx_pause: pointer to bool to indicate whether transmit pause should be ++ * enabled. ++ * @rx_pause: pointer to bool to indicate whether receive pause should be ++ * enabled. ++ * ++ * Resolve and return the flow control modes according to the negotiation ++ * result. This includes checking that we are operating in full duplex mode. ++ * See linkmode_resolve_pause() for further details. ++ */ ++void phy_get_pause(struct phy_device *phydev, bool *tx_pause, bool *rx_pause) ++{ ++ if (phydev->duplex != DUPLEX_FULL) { ++ *tx_pause = false; ++ *rx_pause = false; ++ return; ++ } ++ ++ return linkmode_resolve_pause(phydev->advertising, ++ phydev->lp_advertising, ++ tx_pause, rx_pause); ++} ++EXPORT_SYMBOL(phy_get_pause); ++ + static bool phy_drv_supports_irq(struct phy_driver *phydrv) + { + return phydrv->config_intr && phydrv->ack_interrupt; +@@ -2427,15 +2527,14 @@ static int phy_probe(struct device *dev) + * a controller will attach, and may modify one + * or both of these values + */ +- if (phydrv->features) { ++ if (phydrv->features) + linkmode_copy(phydev->supported, phydrv->features); +- } else if (phydrv->get_features) { ++ else if (phydrv->get_features) + err = phydrv->get_features(phydev); +- } else if (phydev->is_c45) { ++ else if (phydev->is_c45) + err = genphy_c45_pma_read_abilities(phydev); +- } else { ++ else + err = genphy_read_abilities(phydev); +- } + + if (err) + goto out; +Index: linux-5.4.284/drivers/net/phy/phylink.c +=================================================================== +--- linux-5.4.284.orig/drivers/net/phy/phylink.c ++++ linux-5.4.284/drivers/net/phy/phylink.c +@@ -5,6 +5,7 @@ + * + * Copyright (C) 2015 Russell King + */ ++#include + #include + #include + #include +@@ -32,6 +33,11 @@ + enum { + PHYLINK_DISABLE_STOPPED, + PHYLINK_DISABLE_LINK, ++ PHYLINK_DISABLE_MAC_WOL, ++ ++ PCS_STATE_DOWN = 0, ++ PCS_STATE_STARTING, ++ PCS_STATE_STARTED, + }; + + /** +@@ -40,8 +46,10 @@ enum { + struct phylink { + /* private: */ + struct net_device *netdev; +- const struct phylink_mac_ops *ops; ++ const struct phylink_mac_ops *mac_ops; ++ const struct phylink_pcs_ops *pcs_ops; + struct phylink_config *config; ++ struct phylink_pcs *pcs; + struct device *dev; + unsigned int old_link_state:1; + +@@ -68,8 +76,10 @@ struct phylink { + struct mutex state_mutex; + struct phylink_link_state phy_state; + struct work_struct resolve; ++ unsigned int pcs_state; + + bool mac_link_dropped; ++ bool using_mac_select_pcs; + + struct sfp_bus *sfp_bus; + bool sfp_may_have_phy; +@@ -137,9 +147,7 @@ static int phylink_is_empty_linkmode(con + phylink_set(tmp, Pause); + phylink_set(tmp, Asym_Pause); + +- bitmap_andnot(tmp, linkmode, tmp, __ETHTOOL_LINK_MODE_MASK_NBITS); +- +- return linkmode_empty(tmp); ++ return linkmode_subset(linkmode, tmp); + } + + static const char *phylink_an_mode_str(unsigned int mode) +@@ -153,12 +161,347 @@ static const char *phylink_an_mode_str(u + return mode < ARRAY_SIZE(modestr) ? modestr[mode] : "unknown"; + } + ++static int phylink_validate_mac_and_pcs(struct phylink *pl, ++ unsigned long *supported, ++ struct phylink_link_state *state) ++{ ++ struct phylink_pcs *pcs; ++ int ret; ++ ++ /* Get the PCS for this interface mode */ ++ if (pl->using_mac_select_pcs) { ++ pcs = pl->mac_ops->mac_select_pcs(pl->config, state->interface); ++ if (IS_ERR(pcs)) ++ return PTR_ERR(pcs); ++ } else { ++ pcs = pl->pcs; ++ } ++ ++ if (pcs) { ++ /* The PCS, if present, must be setup before phylink_create() ++ * has been called. If the ops is not initialised, print an ++ * error and backtrace rather than oopsing the kernel. ++ */ ++ if (!pcs->ops) { ++ phylink_err(pl, "interface %s: uninitialised PCS\n", ++ phy_modes(state->interface)); ++ dump_stack(); ++ return -EINVAL; ++ } ++ ++ /* Validate the link parameters with the PCS */ ++ if (pcs->ops->pcs_validate) { ++ ret = pcs->ops->pcs_validate(pcs, supported, state); ++ if (ret < 0 || phylink_is_empty_linkmode(supported)) ++ return -EINVAL; ++ ++ /* Ensure the advertising mask is a subset of the ++ * supported mask. ++ */ ++ linkmode_and(state->advertising, state->advertising, ++ supported); ++ } ++ } ++ ++ /* Then validate the link parameters with the MAC */ ++ pl->mac_ops->validate(pl->config, supported, state); ++ ++ return phylink_is_empty_linkmode(supported) ? -EINVAL : 0; ++} ++ ++static void phylink_caps_to_linkmodes(unsigned long *linkmodes, ++ unsigned long caps) ++{ ++ if (caps & MAC_SYM_PAUSE) ++ __set_bit(ETHTOOL_LINK_MODE_Pause_BIT, linkmodes); ++ ++ if (caps & MAC_ASYM_PAUSE) ++ __set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, linkmodes); ++ ++ if (caps & MAC_10HD) ++ __set_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT, linkmodes); ++ ++ if (caps & MAC_10FD) ++ __set_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, linkmodes); ++ ++ if (caps & MAC_100HD) { ++ __set_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_100baseFX_Half_BIT, linkmodes); ++ } ++ ++ if (caps & MAC_100FD) { ++ __set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_100baseT1_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_100baseFX_Full_BIT, linkmodes); ++ } ++ ++ if (caps & MAC_1000HD) ++ __set_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, linkmodes); ++ ++ if (caps & MAC_1000FD) { ++ __set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_1000baseT1_Full_BIT, linkmodes); ++ } ++ ++ if (caps & MAC_2500FD) { ++ __set_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_2500baseX_Full_BIT, linkmodes); ++ } ++ ++ if (caps & MAC_5000FD) ++ __set_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT, linkmodes); ++ ++ if (caps & MAC_10000FD) { ++ __set_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_10000baseR_FEC_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_10000baseCR_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_10000baseSR_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_10000baseLR_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_10000baseER_Full_BIT, linkmodes); ++ } ++ ++ if (caps & MAC_25000FD) { ++ __set_bit(ETHTOOL_LINK_MODE_25000baseCR_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_25000baseKR_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_25000baseSR_Full_BIT, linkmodes); ++ } ++ ++ if (caps & MAC_40000FD) { ++ __set_bit(ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT, linkmodes); ++ } ++ ++ if (caps & MAC_50000FD) { ++ __set_bit(ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_50000baseKR_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_50000baseSR_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_50000baseCR_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT, ++ linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_50000baseDR_Full_BIT, linkmodes); ++ } ++ ++ if (caps & MAC_56000FD) { ++ __set_bit(ETHTOOL_LINK_MODE_56000baseKR4_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_56000baseCR4_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_56000baseSR4_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_56000baseLR4_Full_BIT, linkmodes); ++ } ++ ++ if (caps & MAC_100000FD) { ++ __set_bit(ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT, ++ linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_100000baseKR2_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_100000baseSR2_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_100000baseCR2_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_100000baseLR2_ER2_FR2_Full_BIT, ++ linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_100000baseDR2_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_100000baseKR_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_100000baseSR_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_100000baseLR_ER_FR_Full_BIT, ++ linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_100000baseCR_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_100000baseDR_Full_BIT, linkmodes); ++ } ++ ++ if (caps & MAC_200000FD) { ++ __set_bit(ETHTOOL_LINK_MODE_200000baseKR4_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_200000baseSR4_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_200000baseLR4_ER4_FR4_Full_BIT, ++ linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_200000baseDR4_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_200000baseCR4_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_200000baseKR2_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_200000baseSR2_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_200000baseLR2_ER2_FR2_Full_BIT, ++ linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_200000baseDR2_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_200000baseCR2_Full_BIT, linkmodes); ++ } ++ ++ if (caps & MAC_400000FD) { ++ __set_bit(ETHTOOL_LINK_MODE_400000baseKR8_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_400000baseSR8_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_400000baseLR8_ER8_FR8_Full_BIT, ++ linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_400000baseDR8_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_400000baseCR8_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_400000baseKR4_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_400000baseSR4_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_400000baseLR4_ER4_FR4_Full_BIT, ++ linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_400000baseDR4_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_400000baseCR4_Full_BIT, linkmodes); ++ } ++} ++ ++/** ++ * phylink_get_linkmodes() - get acceptable link modes ++ * @linkmodes: ethtool linkmode mask (must be already initialised) ++ * @interface: phy interface mode defined by &typedef phy_interface_t ++ * @mac_capabilities: bitmask of MAC capabilities ++ * ++ * Set all possible pause, speed and duplex linkmodes in @linkmodes that ++ * are supported by the @interface mode and @mac_capabilities. @linkmodes ++ * must have been initialised previously. ++ */ ++void phylink_get_linkmodes(unsigned long *linkmodes, phy_interface_t interface, ++ unsigned long mac_capabilities) ++{ ++ unsigned long caps = MAC_SYM_PAUSE | MAC_ASYM_PAUSE; ++ ++ switch (interface) { ++ case PHY_INTERFACE_MODE_USXGMII: ++ caps |= MAC_10000FD | MAC_5000FD | MAC_2500FD; ++ fallthrough; ++ ++ case PHY_INTERFACE_MODE_RGMII_TXID: ++ case PHY_INTERFACE_MODE_RGMII_RXID: ++ case PHY_INTERFACE_MODE_RGMII_ID: ++ case PHY_INTERFACE_MODE_RGMII: ++ case PHY_INTERFACE_MODE_QSGMII: ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_GMII: ++ caps |= MAC_1000HD | MAC_1000FD; ++ fallthrough; ++ ++ case PHY_INTERFACE_MODE_REVRMII: ++ case PHY_INTERFACE_MODE_RMII: ++ case PHY_INTERFACE_MODE_REVMII: ++ case PHY_INTERFACE_MODE_MII: ++ caps |= MAC_10HD | MAC_10FD; ++ fallthrough; ++ ++ case PHY_INTERFACE_MODE_100BASEX: ++ caps |= MAC_100HD | MAC_100FD; ++ break; ++ ++ case PHY_INTERFACE_MODE_TBI: ++ case PHY_INTERFACE_MODE_MOCA: ++ case PHY_INTERFACE_MODE_RTBI: ++ case PHY_INTERFACE_MODE_1000BASEX: ++ caps |= MAC_1000HD; ++ fallthrough; ++ case PHY_INTERFACE_MODE_TRGMII: ++ caps |= MAC_1000FD; ++ break; ++ ++ case PHY_INTERFACE_MODE_2500BASEX: ++ caps |= MAC_2500FD; ++ break; ++ ++ case PHY_INTERFACE_MODE_5GBASER: ++ caps |= MAC_5000FD; ++ break; ++ ++ case PHY_INTERFACE_MODE_XGMII: ++ case PHY_INTERFACE_MODE_RXAUI: ++ case PHY_INTERFACE_MODE_XAUI: ++ case PHY_INTERFACE_MODE_10GBASER: ++ case PHY_INTERFACE_MODE_10GKR: ++ caps |= MAC_10000FD; ++ break; ++ ++ case PHY_INTERFACE_MODE_25GBASER: ++ caps |= MAC_25000FD; ++ break; ++ ++ case PHY_INTERFACE_MODE_XLGMII: ++ caps |= MAC_40000FD; ++ break; ++ ++ case PHY_INTERFACE_MODE_INTERNAL: ++ caps |= ~0; ++ break; ++ ++ case PHY_INTERFACE_MODE_NA: ++ case PHY_INTERFACE_MODE_MAX: ++ case PHY_INTERFACE_MODE_SMII: ++ break; ++ } ++ ++ phylink_caps_to_linkmodes(linkmodes, caps & mac_capabilities); ++} ++EXPORT_SYMBOL_GPL(phylink_get_linkmodes); ++ ++/** ++ * phylink_generic_validate() - generic validate() callback implementation ++ * @config: a pointer to a &struct phylink_config. ++ * @supported: ethtool bitmask for supported link modes. ++ * @state: a pointer to a &struct phylink_link_state. ++ * ++ * Generic implementation of the validate() callback that MAC drivers can ++ * use when they pass the range of supported interfaces and MAC capabilities. ++ * This makes use of phylink_get_linkmodes(). ++ */ ++void phylink_generic_validate(struct phylink_config *config, ++ unsigned long *supported, ++ struct phylink_link_state *state) ++{ ++ __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; ++ ++ phylink_set_port_modes(mask); ++ phylink_set(mask, Autoneg); ++ phylink_get_linkmodes(mask, state->interface, config->mac_capabilities); ++ ++ linkmode_and(supported, supported, mask); ++ linkmode_and(state->advertising, state->advertising, mask); ++} ++EXPORT_SYMBOL_GPL(phylink_generic_validate); ++ ++static int phylink_validate_any(struct phylink *pl, unsigned long *supported, ++ struct phylink_link_state *state) ++{ ++ __ETHTOOL_DECLARE_LINK_MODE_MASK(all_adv) = { 0, }; ++ __ETHTOOL_DECLARE_LINK_MODE_MASK(all_s) = { 0, }; ++ __ETHTOOL_DECLARE_LINK_MODE_MASK(s); ++ struct phylink_link_state t; ++ int intf; ++ ++ for (intf = 0; intf < PHY_INTERFACE_MODE_MAX; intf++) { ++ if (test_bit(intf, pl->config->supported_interfaces)) { ++ linkmode_copy(s, supported); ++ ++ t = *state; ++ t.interface = intf; ++ if (!phylink_validate_mac_and_pcs(pl, s, &t)) { ++ linkmode_or(all_s, all_s, s); ++ linkmode_or(all_adv, all_adv, t.advertising); ++ } ++ } ++ } ++ ++ linkmode_copy(supported, all_s); ++ linkmode_copy(state->advertising, all_adv); ++ ++ return phylink_is_empty_linkmode(supported) ? -EINVAL : 0; ++} ++ + static int phylink_validate(struct phylink *pl, unsigned long *supported, + struct phylink_link_state *state) + { +- pl->ops->validate(pl->config, supported, state); ++ if (!phy_interface_empty(pl->config->supported_interfaces)) { ++ if (state->interface == PHY_INTERFACE_MODE_NA) ++ return phylink_validate_any(pl, supported, state); + +- return phylink_is_empty_linkmode(supported) ? -EINVAL : 0; ++ if (!test_bit(state->interface, ++ pl->config->supported_interfaces)) ++ return -EINVAL; ++ } ++ ++ return phylink_validate_mac_and_pcs(pl, supported, state); + } + + static int phylink_parse_fixedlink(struct phylink *pl, +@@ -181,15 +524,18 @@ static int phylink_parse_fixedlink(struc + pl->link_config.duplex = DUPLEX_FULL; + + /* We treat the "pause" and "asym-pause" terminology as +- * defining the link partner's ability. */ ++ * defining the link partner's ability. ++ */ + if (fwnode_property_read_bool(fixed_node, "pause")) +- pl->link_config.pause |= MLO_PAUSE_SYM; ++ __set_bit(ETHTOOL_LINK_MODE_Pause_BIT, ++ pl->link_config.lp_advertising); + if (fwnode_property_read_bool(fixed_node, "asym-pause")) +- pl->link_config.pause |= MLO_PAUSE_ASYM; ++ __set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, ++ pl->link_config.lp_advertising); + + if (ret == 0) { +- desc = fwnode_get_named_gpiod(fixed_node, "link-gpios", +- 0, GPIOD_IN, "?"); ++ desc = fwnode_gpiod_get_index(fixed_node, "link", 0, ++ GPIOD_IN, "?"); + + if (!IS_ERR(desc)) + pl->link_gpio = desc; +@@ -217,9 +563,11 @@ static int phylink_parse_fixedlink(struc + DUPLEX_FULL : DUPLEX_HALF; + pl->link_config.speed = prop[2]; + if (prop[3]) +- pl->link_config.pause |= MLO_PAUSE_SYM; ++ __set_bit(ETHTOOL_LINK_MODE_Pause_BIT, ++ pl->link_config.lp_advertising); + if (prop[4]) +- pl->link_config.pause |= MLO_PAUSE_ASYM; ++ __set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, ++ pl->link_config.lp_advertising); + } + } + +@@ -238,8 +586,10 @@ static int phylink_parse_fixedlink(struc + phylink_set(pl->supported, MII); + phylink_set(pl->supported, Pause); + phylink_set(pl->supported, Asym_Pause); ++ phylink_set(pl->supported, Autoneg); + if (s) { + __set_bit(s->bit, pl->supported); ++ __set_bit(s->bit, pl->link_config.lp_advertising); + } else { + phylink_warn(pl, "fixed link %s duplex %dMbps not recognised\n", + pl->link_config.duplex == DUPLEX_FULL ? "full" : "half", +@@ -265,8 +615,9 @@ static int phylink_parse_mode(struct phy + pl->cfg_link_an_mode = MLO_AN_FIXED; + fwnode_handle_put(dn); + +- if (fwnode_property_read_string(fwnode, "managed", &managed) == 0 && +- strcmp(managed, "in-band-status") == 0) { ++ if ((fwnode_property_read_string(fwnode, "managed", &managed) == 0 && ++ strcmp(managed, "in-band-status") == 0) || ++ pl->config->ovr_an_inband) { + if (pl->cfg_link_an_mode == MLO_AN_FIXED) { + phylink_err(pl, + "can't use both fixed-link and in-band-status\n"); +@@ -283,6 +634,7 @@ static int phylink_parse_mode(struct phy + + switch (pl->link_config.interface) { + case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_QSGMII: + phylink_set(pl->supported, 10baseT_Half); + phylink_set(pl->supported, 10baseT_Full); + phylink_set(pl->supported, 100baseT_Half); +@@ -299,7 +651,18 @@ static int phylink_parse_mode(struct phy + phylink_set(pl->supported, 2500baseX_Full); + break; + ++ case PHY_INTERFACE_MODE_5GBASER: ++ phylink_set(pl->supported, 5000baseT_Full); ++ break; ++ ++ case PHY_INTERFACE_MODE_25GBASER: ++ phylink_set(pl->supported, 25000baseCR_Full); ++ phylink_set(pl->supported, 25000baseKR_Full); ++ phylink_set(pl->supported, 25000baseSR_Full); ++ fallthrough; ++ case PHY_INTERFACE_MODE_USXGMII: + case PHY_INTERFACE_MODE_10GKR: ++ case PHY_INTERFACE_MODE_10GBASER: + phylink_set(pl->supported, 10baseT_Half); + phylink_set(pl->supported, 10baseT_Full); + phylink_set(pl->supported, 100baseT_Half); +@@ -307,7 +670,13 @@ static int phylink_parse_mode(struct phy + phylink_set(pl->supported, 1000baseT_Half); + phylink_set(pl->supported, 1000baseT_Full); + phylink_set(pl->supported, 1000baseX_Full); ++ phylink_set(pl->supported, 1000baseKX_Full); ++ phylink_set(pl->supported, 2500baseT_Full); ++ phylink_set(pl->supported, 2500baseX_Full); ++ phylink_set(pl->supported, 5000baseT_Full); ++ phylink_set(pl->supported, 10000baseT_Full); + phylink_set(pl->supported, 10000baseKR_Full); ++ phylink_set(pl->supported, 10000baseKX4_Full); + phylink_set(pl->supported, 10000baseCR_Full); + phylink_set(pl->supported, 10000baseSR_Full); + phylink_set(pl->supported, 10000baseLR_Full); +@@ -315,6 +684,33 @@ static int phylink_parse_mode(struct phy + phylink_set(pl->supported, 10000baseER_Full); + break; + ++ case PHY_INTERFACE_MODE_XLGMII: ++ phylink_set(pl->supported, 25000baseCR_Full); ++ phylink_set(pl->supported, 25000baseKR_Full); ++ phylink_set(pl->supported, 25000baseSR_Full); ++ phylink_set(pl->supported, 40000baseKR4_Full); ++ phylink_set(pl->supported, 40000baseCR4_Full); ++ phylink_set(pl->supported, 40000baseSR4_Full); ++ phylink_set(pl->supported, 40000baseLR4_Full); ++ phylink_set(pl->supported, 50000baseCR2_Full); ++ phylink_set(pl->supported, 50000baseKR2_Full); ++ phylink_set(pl->supported, 50000baseSR2_Full); ++ phylink_set(pl->supported, 50000baseKR_Full); ++ phylink_set(pl->supported, 50000baseSR_Full); ++ phylink_set(pl->supported, 50000baseCR_Full); ++ phylink_set(pl->supported, 50000baseLR_ER_FR_Full); ++ phylink_set(pl->supported, 50000baseDR_Full); ++ phylink_set(pl->supported, 100000baseKR4_Full); ++ phylink_set(pl->supported, 100000baseSR4_Full); ++ phylink_set(pl->supported, 100000baseCR4_Full); ++ phylink_set(pl->supported, 100000baseLR4_ER4_Full); ++ phylink_set(pl->supported, 100000baseKR2_Full); ++ phylink_set(pl->supported, 100000baseSR2_Full); ++ phylink_set(pl->supported, 100000baseCR2_Full); ++ phylink_set(pl->supported, 100000baseLR2_ER2_FR2_Full); ++ phylink_set(pl->supported, 100000baseDR2_Full); ++ break; ++ + default: + phylink_err(pl, + "incorrect link mode %s for in-band status\n", +@@ -329,11 +725,54 @@ static int phylink_parse_mode(struct phy + "failed to validate link configuration for in-band status\n"); + return -EINVAL; + } ++ ++ /* Check if MAC/PCS also supports Autoneg. */ ++ pl->link_config.an_enabled = phylink_test(pl->supported, Autoneg); + } + + return 0; + } + ++static void phylink_apply_manual_flow(struct phylink *pl, ++ struct phylink_link_state *state) ++{ ++ /* If autoneg is disabled, pause AN is also disabled */ ++ if (!state->an_enabled) ++ state->pause &= ~MLO_PAUSE_AN; ++ ++ /* Manual configuration of pause modes */ ++ if (!(pl->link_config.pause & MLO_PAUSE_AN)) ++ state->pause = pl->link_config.pause; ++} ++ ++static void phylink_resolve_flow(struct phylink_link_state *state) ++{ ++ bool tx_pause, rx_pause; ++ ++ state->pause = MLO_PAUSE_NONE; ++ if (state->duplex == DUPLEX_FULL) { ++ linkmode_resolve_pause(state->advertising, ++ state->lp_advertising, ++ &tx_pause, &rx_pause); ++ if (tx_pause) ++ state->pause |= MLO_PAUSE_TX; ++ if (rx_pause) ++ state->pause |= MLO_PAUSE_RX; ++ } ++} ++ ++static void phylink_pcs_poll_stop(struct phylink *pl) ++{ ++ if (pl->cfg_link_an_mode == MLO_AN_INBAND) ++ del_timer(&pl->link_poll); ++} ++ ++static void phylink_pcs_poll_start(struct phylink *pl) ++{ ++ if (pl->pcs && pl->pcs->poll && pl->cfg_link_an_mode == MLO_AN_INBAND) ++ mod_timer(&pl->link_poll, jiffies + HZ); ++} ++ + static void phylink_mac_config(struct phylink *pl, + const struct phylink_link_state *state) + { +@@ -346,26 +785,156 @@ static void phylink_mac_config(struct ph + __ETHTOOL_LINK_MODE_MASK_NBITS, state->advertising, + state->pause, state->link, state->an_enabled); + +- pl->ops->mac_config(pl->config, pl->cur_link_an_mode, state); ++ pl->mac_ops->mac_config(pl->config, pl->cur_link_an_mode, state); + } + +-static void phylink_mac_config_up(struct phylink *pl, +- const struct phylink_link_state *state) ++static void phylink_mac_pcs_an_restart(struct phylink *pl) + { +- if (state->link) +- phylink_mac_config(pl, state); ++ if (pl->link_config.an_enabled && ++ phy_interface_mode_is_8023z(pl->link_config.interface) && ++ phylink_autoneg_inband(pl->cur_link_an_mode)) { ++ if (pl->pcs_ops) ++ pl->pcs_ops->pcs_an_restart(pl->pcs); ++ else if (pl->config->legacy_pre_march2020) ++ pl->mac_ops->mac_an_restart(pl->config); ++ } + } + +-static void phylink_mac_an_restart(struct phylink *pl) ++static void phylink_pcs_disable(struct phylink_pcs *pcs) + { +- if (pl->link_config.an_enabled && +- phy_interface_mode_is_8023z(pl->link_config.interface)) +- pl->ops->mac_an_restart(pl->config); ++ if (pcs && pcs->ops->pcs_disable) ++ pcs->ops->pcs_disable(pcs); ++} ++ ++static int phylink_pcs_enable(struct phylink_pcs *pcs) ++{ ++ int err = 0; ++ ++ if (pcs && pcs->ops->pcs_enable) ++ err = pcs->ops->pcs_enable(pcs); ++ ++ return err; + } + +-static int phylink_get_mac_state(struct phylink *pl, struct phylink_link_state *state) ++static void phylink_major_config(struct phylink *pl, bool restart, ++ const struct phylink_link_state *state) + { ++ struct phylink_pcs *pcs = NULL; ++ bool pcs_changed = false; ++ int err; ++ ++ phylink_dbg(pl, "major config %s\n", phy_modes(state->interface)); ++ ++ if (pl->using_mac_select_pcs) { ++ pcs = pl->mac_ops->mac_select_pcs(pl->config, state->interface); ++ if (IS_ERR(pcs)) { ++ phylink_err(pl, ++ "mac_select_pcs unexpectedly failed: %pe\n", ++ pcs); ++ return; ++ } ++ ++ pcs_changed = pcs && pl->pcs != pcs; ++ } ++ ++ phylink_pcs_poll_stop(pl); ++ ++ if (pl->mac_ops->mac_prepare) { ++ err = pl->mac_ops->mac_prepare(pl->config, pl->cur_link_an_mode, ++ state->interface); ++ if (err < 0) { ++ phylink_err(pl, "mac_prepare failed: %pe\n", ++ ERR_PTR(err)); ++ return; ++ } ++ } + ++ /* If we have a new PCS, switch to the new PCS after preparing the MAC ++ * for the change. ++ */ ++ if (pcs_changed) { ++ phylink_pcs_disable(pl->pcs); ++ pl->pcs = pcs; ++ pl->pcs_ops = pcs->ops; ++ } ++ ++ phylink_mac_config(pl, state); ++ ++ if (pl->pcs_state == PCS_STATE_STARTING || pcs_changed) ++ phylink_pcs_enable(pl->pcs); ++ ++ if (pl->pcs_ops) { ++ err = pl->pcs_ops->pcs_config(pl->pcs, pl->cur_link_an_mode, ++ state->interface, ++ state->advertising, ++ !!(pl->link_config.pause & ++ MLO_PAUSE_AN)); ++ if (err < 0) ++ phylink_err(pl, "pcs_config failed: %pe\n", ++ ERR_PTR(err)); ++ if (err > 0) ++ restart = true; ++ } ++ if (restart) ++ phylink_mac_pcs_an_restart(pl); ++ ++ if (pl->mac_ops->mac_finish) { ++ err = pl->mac_ops->mac_finish(pl->config, pl->cur_link_an_mode, ++ state->interface); ++ if (err < 0) ++ phylink_err(pl, "mac_finish failed: %pe\n", ++ ERR_PTR(err)); ++ } ++ ++ phylink_pcs_poll_start(pl); ++} ++ ++/* ++ * Reconfigure for a change of inband advertisement. ++ * If we have a separate PCS, we only need to call its pcs_config() method, ++ * and then restart AN if it indicates something changed. Otherwise, we do ++ * the full MAC reconfiguration. ++ */ ++static int phylink_change_inband_advert(struct phylink *pl) ++{ ++ int ret; ++ ++ if (test_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state)) ++ return 0; ++ ++ if (!pl->pcs_ops && pl->config->legacy_pre_march2020) { ++ /* Legacy method */ ++ phylink_mac_config(pl, &pl->link_config); ++ phylink_mac_pcs_an_restart(pl); ++ return 0; ++ } ++ ++ phylink_dbg(pl, "%s: mode=%s/%s adv=%*pb pause=%02x\n", __func__, ++ phylink_an_mode_str(pl->cur_link_an_mode), ++ phy_modes(pl->link_config.interface), ++ __ETHTOOL_LINK_MODE_MASK_NBITS, pl->link_config.advertising, ++ pl->link_config.pause); ++ ++ /* Modern PCS-based method; update the advert at the PCS, and ++ * restart negotiation if the pcs_config() helper indicates that ++ * the programmed advertisement has changed. ++ */ ++ ret = pl->pcs_ops->pcs_config(pl->pcs, pl->cur_link_an_mode, ++ pl->link_config.interface, ++ pl->link_config.advertising, ++ !!(pl->link_config.pause & MLO_PAUSE_AN)); ++ if (ret < 0) ++ return ret; ++ ++ if (ret > 0) ++ phylink_mac_pcs_an_restart(pl); ++ ++ return 0; ++} ++ ++static void phylink_mac_pcs_get_state(struct phylink *pl, ++ struct phylink_link_state *state) ++{ + linkmode_copy(state->advertising, pl->link_config.advertising); + linkmode_zero(state->lp_advertising); + state->interface = pl->link_config.interface; +@@ -376,55 +945,57 @@ static int phylink_get_mac_state(struct + state->an_complete = 0; + state->link = 1; + +- return pl->ops->mac_link_state(pl->config, state); ++ if (pl->pcs_ops) ++ pl->pcs_ops->pcs_get_state(pl->pcs, state); ++ else if (pl->mac_ops->mac_pcs_get_state && ++ pl->config->legacy_pre_march2020) ++ pl->mac_ops->mac_pcs_get_state(pl->config, state); ++ else ++ state->link = 0; + } + + /* The fixed state is... fixed except for the link state, + * which may be determined by a GPIO or a callback. + */ +-static void phylink_get_fixed_state(struct phylink *pl, struct phylink_link_state *state) ++static void phylink_get_fixed_state(struct phylink *pl, ++ struct phylink_link_state *state) + { + *state = pl->link_config; +- if (pl->get_fixed_state) +- pl->get_fixed_state(pl->netdev, state); ++ if (pl->config->get_fixed_state) ++ pl->config->get_fixed_state(pl->config, state); + else if (pl->link_gpio) + state->link = !!gpiod_get_value_cansleep(pl->link_gpio); ++ ++ phylink_resolve_flow(state); + } + +-/* Flow control is resolved according to our and the link partners +- * advertisements using the following drawn from the 802.3 specs: +- * Local device Link partner +- * Pause AsymDir Pause AsymDir Result +- * 1 X 1 X TX+RX +- * 0 1 1 1 TX +- * 1 1 0 1 RX +- */ +-static void phylink_resolve_flow(struct phylink *pl, +- struct phylink_link_state *state) ++static void phylink_mac_initial_config(struct phylink *pl, bool force_restart) + { +- int new_pause = 0; ++ struct phylink_link_state link_state; ++ ++ switch (pl->cur_link_an_mode) { ++ case MLO_AN_PHY: ++ link_state = pl->phy_state; ++ break; + +- if (pl->link_config.pause & MLO_PAUSE_AN) { +- int pause = 0; ++ case MLO_AN_FIXED: ++ phylink_get_fixed_state(pl, &link_state); ++ break; + +- if (phylink_test(pl->link_config.advertising, Pause)) +- pause |= MLO_PAUSE_SYM; +- if (phylink_test(pl->link_config.advertising, Asym_Pause)) +- pause |= MLO_PAUSE_ASYM; +- +- pause &= state->pause; +- +- if (pause & MLO_PAUSE_SYM) +- new_pause = MLO_PAUSE_TX | MLO_PAUSE_RX; +- else if (pause & MLO_PAUSE_ASYM) +- new_pause = state->pause & MLO_PAUSE_SYM ? +- MLO_PAUSE_TX : MLO_PAUSE_RX; +- } else { +- new_pause = pl->link_config.pause & MLO_PAUSE_TXRX_MASK; ++ case MLO_AN_INBAND: ++ link_state = pl->link_config; ++ if (link_state.interface == PHY_INTERFACE_MODE_SGMII) ++ link_state.pause = MLO_PAUSE_NONE; ++ break; ++ ++ default: /* can't happen */ ++ return; + } + +- state->pause &= ~MLO_PAUSE_TXRX_MASK; +- state->pause |= new_pause; ++ link_state.link = false; ++ ++ phylink_apply_manual_flow(pl, &link_state); ++ phylink_major_config(pl, force_restart, &link_state); + } + + static const char *phylink_pause_to_str(int pause) +@@ -441,17 +1012,23 @@ static const char *phylink_pause_to_str( + } + } + +-static void phylink_mac_link_up(struct phylink *pl, ++static void phylink_link_up(struct phylink *pl, + struct phylink_link_state link_state) + { + struct net_device *ndev = pl->netdev; + + pl->cur_interface = link_state.interface; +- pl->ops->mac_link_up(pl->config, pl->phydev, +- pl->cur_link_an_mode, pl->cur_interface, +- link_state.speed, link_state.duplex, +- !!(link_state.pause & MLO_PAUSE_TX), +- !!(link_state.pause & MLO_PAUSE_RX)); ++ ++ if (pl->pcs_ops && pl->pcs_ops->pcs_link_up) ++ pl->pcs_ops->pcs_link_up(pl->pcs, pl->cur_link_an_mode, ++ pl->cur_interface, ++ link_state.speed, link_state.duplex); ++ ++ pl->mac_ops->mac_link_up(pl->config, pl->phydev, ++ pl->cur_link_an_mode, pl->cur_interface, ++ link_state.speed, link_state.duplex, ++ !!(link_state.pause & MLO_PAUSE_TX), ++ !!(link_state.pause & MLO_PAUSE_RX)); + + if (ndev) + netif_carrier_on(ndev); +@@ -463,14 +1040,14 @@ static void phylink_mac_link_up(struct p + phylink_pause_to_str(link_state.pause)); + } + +-static void phylink_mac_link_down(struct phylink *pl) ++static void phylink_link_down(struct phylink *pl) + { + struct net_device *ndev = pl->netdev; + + if (ndev) + netif_carrier_off(ndev); +- pl->ops->mac_link_down(pl->config, pl->cur_link_an_mode, +- pl->cur_interface); ++ pl->mac_ops->mac_link_down(pl->config, pl->cur_link_an_mode, ++ pl->cur_interface); + phylink_info(pl, "Link is Down\n"); + } + +@@ -479,62 +1056,109 @@ static void phylink_resolve(struct work_ + struct phylink *pl = container_of(w, struct phylink, resolve); + struct phylink_link_state link_state; + struct net_device *ndev = pl->netdev; +- int link_changed; ++ bool mac_config = false; ++ bool retrigger = false; ++ bool cur_link_state; + + mutex_lock(&pl->state_mutex); ++ if (pl->netdev) ++ cur_link_state = netif_carrier_ok(ndev); ++ else ++ cur_link_state = pl->old_link_state; ++ + if (pl->phylink_disable_state) { + pl->mac_link_dropped = false; + link_state.link = false; + } else if (pl->mac_link_dropped) { + link_state.link = false; ++ retrigger = true; + } else { + switch (pl->cur_link_an_mode) { + case MLO_AN_PHY: + link_state = pl->phy_state; +- phylink_resolve_flow(pl, &link_state); +- phylink_mac_config_up(pl, &link_state); ++ phylink_apply_manual_flow(pl, &link_state); ++ mac_config = link_state.link; + break; + + case MLO_AN_FIXED: + phylink_get_fixed_state(pl, &link_state); +- phylink_mac_config_up(pl, &link_state); ++ mac_config = link_state.link; + break; + + case MLO_AN_INBAND: +- phylink_get_mac_state(pl, &link_state); ++ phylink_mac_pcs_get_state(pl, &link_state); ++ /* The PCS may have a latching link-fail indicator. ++ * If the link was up, bring the link down and ++ * re-trigger the resolve. Otherwise, re-read the ++ * PCS state to get the current status of the link. ++ */ ++ if (!link_state.link) { ++ if (cur_link_state) ++ retrigger = true; ++ else ++ phylink_mac_pcs_get_state(pl, ++ &link_state); ++ } + + /* If we have a phy, the "up" state is the union of +- * both the PHY and the MAC */ ++ * both the PHY and the MAC ++ */ + if (pl->phydev) + link_state.link &= pl->phy_state.link; + + /* Only update if the PHY link is up */ + if (pl->phydev && pl->phy_state.link) { ++ /* If the interface has changed, force a ++ * link down event if the link isn't already ++ * down, and re-resolve. ++ */ ++ if (link_state.interface != ++ pl->phy_state.interface) { ++ retrigger = true; ++ link_state.link = false; ++ } + link_state.interface = pl->phy_state.interface; + + /* If we have a PHY, we need to update with +- * the pause mode bits. */ +- link_state.pause |= pl->phy_state.pause; +- phylink_resolve_flow(pl, &link_state); +- phylink_mac_config(pl, &link_state); ++ * the PHY flow control bits. ++ */ ++ link_state.pause = pl->phy_state.pause; ++ mac_config = true; + } ++ phylink_apply_manual_flow(pl, &link_state); + break; + } + } + +- if (pl->netdev) +- link_changed = (link_state.link != netif_carrier_ok(ndev)); +- else +- link_changed = (link_state.link != pl->old_link_state); ++ if (mac_config) { ++ if (link_state.interface != pl->link_config.interface) { ++ /* The interface has changed, force the link down and ++ * then reconfigure. ++ */ ++ if (cur_link_state) { ++ phylink_link_down(pl); ++ cur_link_state = false; ++ } ++ phylink_major_config(pl, false, &link_state); ++ pl->link_config.interface = link_state.interface; ++ } else if (!pl->pcs_ops && pl->config->legacy_pre_march2020) { ++ /* The interface remains unchanged, only the speed, ++ * duplex or pause settings have changed. Call the ++ * old mac_config() method to configure the MAC/PCS ++ * only if we do not have a legacy MAC driver. ++ */ ++ phylink_mac_config(pl, &link_state); ++ } ++ } + +- if (link_changed) { ++ if (link_state.link != cur_link_state) { + pl->old_link_state = link_state.link; + if (!link_state.link) +- phylink_mac_link_down(pl); ++ phylink_link_down(pl); + else +- phylink_mac_link_up(pl, link_state); ++ phylink_link_up(pl, link_state); + } +- if (!link_state.link && pl->mac_link_dropped) { ++ if (!link_state.link && retrigger) { + pl->mac_link_dropped = false; + queue_work(system_power_efficient_wq, &pl->resolve); + } +@@ -575,6 +1199,9 @@ static int phylink_register_sfp(struct p + struct sfp_bus *bus; + int ret; + ++ if (!fwnode) ++ return 0; ++ + bus = sfp_bus_find_fwnode(fwnode); + if (IS_ERR(bus)) { + ret = PTR_ERR(bus); +@@ -596,7 +1223,7 @@ static int phylink_register_sfp(struct p + * @fwnode: a pointer to a &struct fwnode_handle describing the network + * interface + * @iface: the desired link mode defined by &typedef phy_interface_t +- * @ops: a pointer to a &struct phylink_mac_ops for the MAC. ++ * @mac_ops: a pointer to a &struct phylink_mac_ops for the MAC. + * + * Create a new phylink instance, and parse the link parameters found in @np. + * This will parse in-band modes, fixed-link or SFP configuration. +@@ -609,11 +1236,25 @@ static int phylink_register_sfp(struct p + struct phylink *phylink_create(struct phylink_config *config, + struct fwnode_handle *fwnode, + phy_interface_t iface, +- const struct phylink_mac_ops *ops) ++ const struct phylink_mac_ops *mac_ops) + { ++ bool using_mac_select_pcs = false; + struct phylink *pl; + int ret; + ++ if (mac_ops->mac_select_pcs && ++ mac_ops->mac_select_pcs(config, PHY_INTERFACE_MODE_NA) != ++ ERR_PTR(-EOPNOTSUPP)) ++ using_mac_select_pcs = true; ++ ++ /* Validate the supplied configuration */ ++ if (using_mac_select_pcs && ++ phy_interface_empty(config->supported_interfaces)) { ++ dev_err(config->dev, ++ "phylink: error: empty supported_interfaces but mac_select_pcs() method present\n"); ++ return ERR_PTR(-EINVAL); ++ } ++ + pl = kzalloc(sizeof(*pl), GFP_KERNEL); + if (!pl) + return ERR_PTR(-ENOMEM); +@@ -631,6 +1272,7 @@ struct phylink *phylink_create(struct ph + return ERR_PTR(-EINVAL); + } + ++ pl->using_mac_select_pcs = using_mac_select_pcs; + pl->phy_state.interface = iface; + pl->link_interface = iface; + if (iface == PHY_INTERFACE_MODE_MOCA) +@@ -642,7 +1284,8 @@ struct phylink *phylink_create(struct ph + pl->link_config.speed = SPEED_UNKNOWN; + pl->link_config.duplex = DUPLEX_UNKNOWN; + pl->link_config.an_enabled = true; +- pl->ops = ops; ++ pl->pcs_state = PCS_STATE_DOWN; ++ pl->mac_ops = mac_ops; + __set_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state); + timer_setup(&pl->link_poll, phylink_fixed_poll, 0); + +@@ -677,6 +1320,36 @@ struct phylink *phylink_create(struct ph + EXPORT_SYMBOL_GPL(phylink_create); + + /** ++ * phylink_set_pcs() - set the current PCS for phylink to use ++ * @pl: a pointer to a &struct phylink returned from phylink_create() ++ * @pcs: a pointer to the &struct phylink_pcs ++ * ++ * Bind the MAC PCS to phylink. This may be called after phylink_create(). ++ * If it is desired to dynamically change the PCS, then the preferred method ++ * is to use mac_select_pcs(), but it may also be called in mac_prepare() ++ * or mac_config(). ++ * ++ * Please note that there are behavioural changes with the mac_config() ++ * callback if a PCS is present (denoting a newer setup) so removing a PCS ++ * is not supported, and if a PCS is going to be used, it must be registered ++ * by calling phylink_set_pcs() at the latest in the first mac_config() call. ++ */ ++void phylink_set_pcs(struct phylink *pl, struct phylink_pcs *pcs) ++{ ++ pl->pcs = pcs; ++ pl->pcs_ops = pcs->ops; ++ ++ if (!pl->phylink_disable_state && ++ pl->cfg_link_an_mode == MLO_AN_INBAND) { ++ if (pl->config->pcs_poll || pcs->poll) ++ mod_timer(&pl->link_poll, jiffies + HZ); ++ else ++ del_timer(&pl->link_poll); ++ } ++} ++EXPORT_SYMBOL_GPL(phylink_set_pcs); ++ ++/** + * phylink_destroy() - cleanup and destroy the phylink instance + * @pl: a pointer to a &struct phylink returned from phylink_create() + * +@@ -696,29 +1369,32 @@ void phylink_destroy(struct phylink *pl) + } + EXPORT_SYMBOL_GPL(phylink_destroy); + +-static void phylink_phy_change(struct phy_device *phydev, bool up, +- bool do_carrier) ++static void phylink_phy_change(struct phy_device *phydev, bool up) + { + struct phylink *pl = phydev->phylink; ++ bool tx_pause, rx_pause; ++ ++ phy_get_pause(phydev, &tx_pause, &rx_pause); + + mutex_lock(&pl->state_mutex); + pl->phy_state.speed = phydev->speed; + pl->phy_state.duplex = phydev->duplex; + pl->phy_state.pause = MLO_PAUSE_NONE; +- if (phydev->pause) +- pl->phy_state.pause |= MLO_PAUSE_SYM; +- if (phydev->asym_pause) +- pl->phy_state.pause |= MLO_PAUSE_ASYM; ++ if (tx_pause) ++ pl->phy_state.pause |= MLO_PAUSE_TX; ++ if (rx_pause) ++ pl->phy_state.pause |= MLO_PAUSE_RX; + pl->phy_state.interface = phydev->interface; + pl->phy_state.link = up; + mutex_unlock(&pl->state_mutex); + + phylink_run_resolve(pl); + +- phylink_dbg(pl, "phy link %s %s/%s/%s\n", up ? "up" : "down", ++ phylink_dbg(pl, "phy link %s %s/%s/%s/%s\n", up ? "up" : "down", + phy_modes(phydev->interface), + phy_speed_to_str(phydev->speed), +- phy_duplex_to_str(phydev->duplex)); ++ phy_duplex_to_str(phydev->duplex), ++ phylink_pause_to_str(pl->phy_state.pause)); + } + + static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy, +@@ -726,6 +1402,7 @@ static int phylink_bringup_phy(struct ph + { + struct phylink_link_state config; + __ETHTOOL_DECLARE_LINK_MODE_MASK(supported); ++ char *irq_str; + int ret; + + /* +@@ -740,23 +1417,46 @@ static int phylink_bringup_phy(struct ph + memset(&config, 0, sizeof(config)); + linkmode_copy(supported, phy->supported); + linkmode_copy(config.advertising, phy->advertising); +- config.interface = interface; ++ ++ /* Clause 45 PHYs switch their Serdes lane between several different ++ * modes, normally 10GBASE-R, SGMII. Some use 2500BASE-X for 2.5G ++ * speeds. We really need to know which interface modes the PHY and ++ * MAC supports to properly work out which linkmodes can be supported. ++ */ ++ if (phy->is_c45 && ++ interface != PHY_INTERFACE_MODE_RXAUI && ++ interface != PHY_INTERFACE_MODE_XAUI && ++ interface != PHY_INTERFACE_MODE_USXGMII) ++ config.interface = PHY_INTERFACE_MODE_NA; ++ else ++ config.interface = interface; + + ret = phylink_validate(pl, supported, &config); +- if (ret) ++ if (ret) { ++ phylink_warn(pl, "validation of %s with support %*pb and advertisement %*pb failed: %d\n", ++ phy_modes(config.interface), ++ __ETHTOOL_LINK_MODE_MASK_NBITS, phy->supported, ++ __ETHTOOL_LINK_MODE_MASK_NBITS, config.advertising, ++ ret); + return ret; ++ } + + phy->phylink = pl; + phy->phy_link_change = phylink_phy_change; + ++ irq_str = phy_attached_info_irq(phy); + phylink_info(pl, +- "PHY [%s] driver [%s]\n", dev_name(&phy->mdio.dev), +- phy->drv->name); ++ "PHY [%s] driver [%s] (irq=%s)\n", ++ dev_name(&phy->mdio.dev), phy->drv->name, irq_str); ++ kfree(irq_str); + + mutex_lock(&phy->lock); + mutex_lock(&pl->state_mutex); + pl->phydev = phy; + pl->phy_state.interface = interface; ++ pl->phy_state.pause = MLO_PAUSE_NONE; ++ pl->phy_state.speed = SPEED_UNKNOWN; ++ pl->phy_state.duplex = DUPLEX_UNKNOWN; + linkmode_copy(pl->supported, supported); + linkmode_copy(pl->link_config.advertising, config.advertising); + +@@ -773,6 +1473,9 @@ static int phylink_bringup_phy(struct ph + if (phy_interrupt_is_valid(phy)) + phy_request_interrupt(phy); + ++ if (pl->config->mac_managed_pm) ++ phy->mac_managed_pm = true; ++ + return 0; + } + +@@ -842,7 +1545,26 @@ EXPORT_SYMBOL_GPL(phylink_connect_phy); + int phylink_of_phy_connect(struct phylink *pl, struct device_node *dn, + u32 flags) + { +- struct device_node *phy_node; ++ return phylink_fwnode_phy_connect(pl, of_fwnode_handle(dn), flags); ++} ++EXPORT_SYMBOL_GPL(phylink_of_phy_connect); ++ ++/** ++ * phylink_fwnode_phy_connect() - connect the PHY specified in the fwnode. ++ * @pl: a pointer to a &struct phylink returned from phylink_create() ++ * @fwnode: a pointer to a &struct fwnode_handle. ++ * @flags: PHY-specific flags to communicate to the PHY device driver ++ * ++ * Connect the phy specified @fwnode to the phylink instance specified ++ * by @pl. ++ * ++ * Returns 0 on success or a negative errno. ++ */ ++int phylink_fwnode_phy_connect(struct phylink *pl, ++ struct fwnode_handle *fwnode, ++ u32 flags) ++{ ++ struct fwnode_handle *phy_fwnode; + struct phy_device *phy_dev; + int ret; + +@@ -852,33 +1574,32 @@ int phylink_of_phy_connect(struct phylin + phy_interface_mode_is_8023z(pl->link_interface))) + return 0; + +- phy_node = of_parse_phandle(dn, "phy-handle", 0); +- if (!phy_node) +- phy_node = of_parse_phandle(dn, "phy", 0); +- if (!phy_node) +- phy_node = of_parse_phandle(dn, "phy-device", 0); +- +- if (!phy_node) { ++ phy_fwnode = fwnode_get_phy_node(fwnode); ++ if (IS_ERR(phy_fwnode)) { + if (pl->cfg_link_an_mode == MLO_AN_PHY) + return -ENODEV; + return 0; + } + +- phy_dev = of_phy_attach(pl->netdev, phy_node, flags, +- pl->link_interface); ++ phy_dev = fwnode_phy_find_device(phy_fwnode); + /* We're done with the phy_node handle */ +- of_node_put(phy_node); +- ++ fwnode_handle_put(phy_fwnode); + if (!phy_dev) + return -ENODEV; + ++ ret = phy_attach_direct(pl->netdev, phy_dev, flags, ++ pl->link_interface); ++ phy_device_free(phy_dev); ++ if (ret) ++ return ret; ++ + ret = phylink_bringup_phy(pl, phy_dev, pl->link_config.interface); + if (ret) + phy_detach(phy_dev); + + return ret; + } +-EXPORT_SYMBOL_GPL(phylink_of_phy_connect); ++EXPORT_SYMBOL_GPL(phylink_fwnode_phy_connect); + + /** + * phylink_disconnect_phy() - disconnect any PHY attached to the phylink +@@ -908,32 +1629,6 @@ void phylink_disconnect_phy(struct phyli + EXPORT_SYMBOL_GPL(phylink_disconnect_phy); + + /** +- * phylink_fixed_state_cb() - allow setting a fixed link callback +- * @pl: a pointer to a &struct phylink returned from phylink_create() +- * @cb: callback to execute to determine the fixed link state. +- * +- * The MAC driver should call this driver when the state of its link +- * can be determined through e.g: an out of band MMIO register. +- */ +-int phylink_fixed_state_cb(struct phylink *pl, +- void (*cb)(struct net_device *dev, +- struct phylink_link_state *state)) +-{ +- /* It does not make sense to let the link be overriden unless we use +- * MLO_AN_FIXED +- */ +- if (pl->cfg_link_an_mode != MLO_AN_FIXED) +- return -EINVAL; +- +- mutex_lock(&pl->state_mutex); +- pl->get_fixed_state = cb; +- mutex_unlock(&pl->state_mutex); +- +- return 0; +-} +-EXPORT_SYMBOL_GPL(phylink_fixed_state_cb); +- +-/** + * phylink_mac_change() - notify phylink of a change in MAC state + * @pl: a pointer to a &struct phylink returned from phylink_create() + * @up: indicates whether the link is currently up. +@@ -969,6 +1664,8 @@ static irqreturn_t phylink_link_handler( + */ + void phylink_start(struct phylink *pl) + { ++ bool poll = false; ++ + ASSERT_RTNL(); + + phylink_info(pl, "configuring for %s/%s link mode\n", +@@ -979,18 +1676,19 @@ void phylink_start(struct phylink *pl) + if (pl->netdev) + netif_carrier_off(pl->netdev); + ++ pl->pcs_state = PCS_STATE_STARTING; ++ + /* Apply the link configuration to the MAC when starting. This allows + * a fixed-link to start with the correct parameters, and also + * ensures that we set the appropriate advertisement for Serdes links. +- */ +- phylink_resolve_flow(pl, &pl->link_config); +- phylink_mac_config(pl, &pl->link_config); +- +- /* Restart autonegotiation if using 802.3z to ensure that the link ++ * ++ * Restart autonegotiation if using 802.3z to ensure that the link + * parameters are properly negotiated. This is necessary for DSA + * switches using 802.3z negotiation to ensure they see our modes. + */ +- phylink_mac_an_restart(pl); ++ phylink_mac_initial_config(pl, true); ++ ++ pl->pcs_state = PCS_STATE_STARTED; + + clear_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state); + phylink_run_resolve(pl); +@@ -1008,9 +1706,13 @@ void phylink_start(struct phylink *pl) + irq = 0; + } + if (irq <= 0) +- mod_timer(&pl->link_poll, jiffies + HZ); ++ poll = true; + } +- if (pl->cfg_link_an_mode == MLO_AN_FIXED && pl->get_fixed_state) ++ ++ if (pl->cfg_link_an_mode == MLO_AN_FIXED) ++ poll |= pl->config->poll_fixed_state; ++ ++ if (poll) + mod_timer(&pl->link_poll, jiffies + HZ); + if (pl->phydev) + phy_start(pl->phydev); +@@ -1027,6 +1729,9 @@ EXPORT_SYMBOL_GPL(phylink_start); + * network device driver's &struct net_device_ops ndo_stop() method. The + * network device's carrier state should not be changed prior to calling this + * function. ++ * ++ * This will synchronously bring down the link if the link is not already ++ * down (in other words, it will trigger a mac_link_down() method call.) + */ + void phylink_stop(struct phylink *pl) + { +@@ -1043,10 +1748,95 @@ void phylink_stop(struct phylink *pl) + } + + phylink_run_resolve_and_disable(pl, PHYLINK_DISABLE_STOPPED); ++ ++ pl->pcs_state = PCS_STATE_DOWN; ++ ++ phylink_pcs_disable(pl->pcs); + } + EXPORT_SYMBOL_GPL(phylink_stop); + + /** ++ * phylink_suspend() - handle a network device suspend event ++ * @pl: a pointer to a &struct phylink returned from phylink_create() ++ * @mac_wol: true if the MAC needs to receive packets for Wake-on-Lan ++ * ++ * Handle a network device suspend event. There are several cases: ++ * - If Wake-on-Lan is not active, we can bring down the link between ++ * the MAC and PHY by calling phylink_stop(). ++ * - If Wake-on-Lan is active, and being handled only by the PHY, we ++ * can also bring down the link between the MAC and PHY. ++ * - If Wake-on-Lan is active, but being handled by the MAC, the MAC ++ * still needs to receive packets, so we can not bring the link down. ++ */ ++void phylink_suspend(struct phylink *pl, bool mac_wol) ++{ ++ ASSERT_RTNL(); ++ ++ if (mac_wol && (!pl->netdev || pl->netdev->wol_enabled)) { ++ /* Wake-on-Lan enabled, MAC handling */ ++ mutex_lock(&pl->state_mutex); ++ ++ /* Stop the resolver bringing the link up */ ++ __set_bit(PHYLINK_DISABLE_MAC_WOL, &pl->phylink_disable_state); ++ ++ /* Disable the carrier, to prevent transmit timeouts, ++ * but one would hope all packets have been sent. This ++ * also means phylink_resolve() will do nothing. ++ */ ++ if (pl->netdev) ++ netif_carrier_off(pl->netdev); ++ else ++ pl->old_link_state = false; ++ ++ /* We do not call mac_link_down() here as we want the ++ * link to remain up to receive the WoL packets. ++ */ ++ mutex_unlock(&pl->state_mutex); ++ } else { ++ phylink_stop(pl); ++ } ++} ++EXPORT_SYMBOL_GPL(phylink_suspend); ++ ++/** ++ * phylink_resume() - handle a network device resume event ++ * @pl: a pointer to a &struct phylink returned from phylink_create() ++ * ++ * Undo the effects of phylink_suspend(), returning the link to an ++ * operational state. ++ */ ++void phylink_resume(struct phylink *pl) ++{ ++ ASSERT_RTNL(); ++ ++ if (test_bit(PHYLINK_DISABLE_MAC_WOL, &pl->phylink_disable_state)) { ++ /* Wake-on-Lan enabled, MAC handling */ ++ ++ /* Call mac_link_down() so we keep the overall state balanced. ++ * Do this under the state_mutex lock for consistency. This ++ * will cause a "Link Down" message to be printed during ++ * resume, which is harmless - the true link state will be ++ * printed when we run a resolve. ++ */ ++ mutex_lock(&pl->state_mutex); ++ phylink_link_down(pl); ++ mutex_unlock(&pl->state_mutex); ++ ++ /* Re-apply the link parameters so that all the settings get ++ * restored to the MAC. ++ */ ++ phylink_mac_initial_config(pl, true); ++ ++ /* Re-enable and re-resolve the link parameters */ ++ clear_bit(PHYLINK_DISABLE_MAC_WOL, &pl->phylink_disable_state); ++ phylink_run_resolve(pl); ++ } else { ++ phylink_start(pl); ++ } ++} ++EXPORT_SYMBOL_GPL(phylink_resume); ++ ++/** + * phylink_ethtool_get_wol() - get the wake on lan parameters for the PHY + * @pl: a pointer to a &struct phylink returned from phylink_create() + * @wol: a pointer to &struct ethtool_wolinfo to hold the read parameters +@@ -1129,11 +1919,10 @@ int phylink_ethtool_ksettings_get(struct + + ASSERT_RTNL(); + +- if (pl->phydev) { ++ if (pl->phydev) + phy_ethtool_ksettings_get(pl->phydev, kset); +- } else { ++ else + kset->base.port = pl->link_port; +- } + + linkmode_copy(kset->link_modes.supported, pl->supported); + +@@ -1154,7 +1943,7 @@ int phylink_ethtool_ksettings_get(struct + if (pl->phydev) + break; + +- phylink_get_mac_state(pl, &link_state); ++ phylink_mac_pcs_get_state(pl, &link_state); + + /* The MAC is reporting the link results from its own PCS + * layer via in-band status. Report these as the current +@@ -1177,60 +1966,93 @@ int phylink_ethtool_ksettings_set(struct + const struct ethtool_link_ksettings *kset) + { + __ETHTOOL_DECLARE_LINK_MODE_MASK(support); +- struct ethtool_link_ksettings our_kset; + struct phylink_link_state config; +- int ret; ++ const struct phy_setting *s; + + ASSERT_RTNL(); + +- if (kset->base.autoneg != AUTONEG_DISABLE && +- kset->base.autoneg != AUTONEG_ENABLE) +- return -EINVAL; ++ if (pl->phydev) { ++ /* We can rely on phylib for this update; we also do not need ++ * to update the pl->link_config settings: ++ * - the configuration returned via ksettings_get() will come ++ * from phylib whenever a PHY is present. ++ * - link_config.interface will be updated by the PHY calling ++ * back via phylink_phy_change() and a subsequent resolve. ++ * - initial link configuration for PHY mode comes from the ++ * last phy state updated via phylink_phy_change(). ++ * - other configuration changes (e.g. pause modes) are ++ * performed directly via phylib. ++ * - if in in-band mode with a PHY, the link configuration ++ * is passed on the link from the PHY, and all of ++ * link_config.{speed,duplex,an_enabled,pause} are not used. ++ * - the only possible use would be link_config.advertising ++ * pause modes when in 1000base-X mode with a PHY, but in ++ * the presence of a PHY, this should not be changed as that ++ * should be determined from the media side advertisement. ++ */ ++ return phy_ethtool_ksettings_set(pl->phydev, kset); ++ } + +- linkmode_copy(support, pl->supported); + config = pl->link_config; + + /* Mask out unsupported advertisements */ + linkmode_and(config.advertising, kset->link_modes.advertising, +- support); ++ pl->supported); + + /* FIXME: should we reject autoneg if phy/mac does not support it? */ +- if (kset->base.autoneg == AUTONEG_DISABLE) { +- const struct phy_setting *s; +- ++ switch (kset->base.autoneg) { ++ case AUTONEG_DISABLE: + /* Autonegotiation disabled, select a suitable speed and + * duplex. + */ + s = phy_lookup_setting(kset->base.speed, kset->base.duplex, +- support, false); ++ pl->supported, false); + if (!s) + return -EINVAL; + +- /* If we have a fixed link (as specified by firmware), refuse +- * to change link parameters. ++ /* If we have a fixed link, refuse to change link parameters. ++ * If the link parameters match, accept them but do nothing. + */ +- if (pl->cur_link_an_mode == MLO_AN_FIXED && +- (s->speed != pl->link_config.speed || +- s->duplex != pl->link_config.duplex)) +- return -EINVAL; ++ if (pl->cur_link_an_mode == MLO_AN_FIXED) { ++ if (s->speed != pl->link_config.speed || ++ s->duplex != pl->link_config.duplex) ++ return -EINVAL; ++ return 0; ++ } + + config.speed = s->speed; + config.duplex = s->duplex; +- config.an_enabled = false; ++ break; + +- __clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, config.advertising); +- } else { +- /* If we have a fixed link, refuse to enable autonegotiation */ +- if (pl->cur_link_an_mode == MLO_AN_FIXED) +- return -EINVAL; ++ case AUTONEG_ENABLE: ++ /* If we have a fixed link, allow autonegotiation (since that ++ * is our default case) but do not allow the advertisement to ++ * be changed. If the advertisement matches, simply return. ++ */ ++ if (pl->cur_link_an_mode == MLO_AN_FIXED) { ++ if (!linkmode_equal(config.advertising, ++ pl->link_config.advertising)) ++ return -EINVAL; ++ return 0; ++ } + + config.speed = SPEED_UNKNOWN; + config.duplex = DUPLEX_UNKNOWN; +- config.an_enabled = true; ++ break; + +- __set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, config.advertising); ++ default: ++ return -EINVAL; + } + ++ /* We have ruled out the case with a PHY attached, and the ++ * fixed-link cases. All that is left are in-band links. ++ */ ++ config.an_enabled = kset->base.autoneg == AUTONEG_ENABLE; ++ linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, config.advertising, ++ config.an_enabled); ++ ++ /* Validate without changing the current supported mask. */ ++ linkmode_copy(support, pl->supported); + if (phylink_validate(pl, support, &config)) + return -EINVAL; + +@@ -1238,35 +2060,53 @@ int phylink_ethtool_ksettings_set(struct + if (config.an_enabled && phylink_is_empty_linkmode(config.advertising)) + return -EINVAL; + +- our_kset = *kset; +- linkmode_copy(our_kset.link_modes.advertising, config.advertising); +- our_kset.base.speed = config.speed; +- our_kset.base.duplex = config.duplex; ++ /* If this link is with an SFP, ensure that changes to advertised modes ++ * also cause the associated interface to be selected such that the ++ * link can be configured correctly. ++ */ ++ if (pl->sfp_port && pl->sfp_bus) { ++ config.interface = sfp_select_interface(pl->sfp_bus, ++ config.advertising); ++ if (config.interface == PHY_INTERFACE_MODE_NA) { ++ phylink_err(pl, ++ "selection of interface failed, advertisement %*pb\n", ++ __ETHTOOL_LINK_MODE_MASK_NBITS, ++ config.advertising); ++ return -EINVAL; ++ } + +- /* If we have a PHY, configure the phy */ +- if (pl->phydev) { +- ret = phy_ethtool_ksettings_set(pl->phydev, &our_kset); +- if (ret) +- return ret; ++ /* Revalidate with the selected interface */ ++ linkmode_copy(support, pl->supported); ++ if (phylink_validate(pl, support, &config)) { ++ phylink_err(pl, "validation of %s/%s with support %*pb failed\n", ++ phylink_an_mode_str(pl->cur_link_an_mode), ++ phy_modes(config.interface), ++ __ETHTOOL_LINK_MODE_MASK_NBITS, support); ++ return -EINVAL; ++ } + } + + mutex_lock(&pl->state_mutex); +- /* Configure the MAC to match the new settings */ +- linkmode_copy(pl->link_config.advertising, our_kset.link_modes.advertising); +- pl->link_config.interface = config.interface; +- pl->link_config.speed = our_kset.base.speed; +- pl->link_config.duplex = our_kset.base.duplex; +- pl->link_config.an_enabled = our_kset.base.autoneg != AUTONEG_DISABLE; +- +- /* If we have a PHY, phylib will call our link state function if the +- * mode has changed, which will trigger a resolve and update the MAC +- * configuration. For a fixed link, this isn't able to change any +- * parameters, which just leaves inband mode. +- */ +- if (pl->cur_link_an_mode == MLO_AN_INBAND && +- !test_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state)) { +- phylink_mac_config(pl, &pl->link_config); +- phylink_mac_an_restart(pl); ++ pl->link_config.speed = config.speed; ++ pl->link_config.duplex = config.duplex; ++ pl->link_config.an_enabled = config.an_enabled; ++ ++ if (pl->link_config.interface != config.interface) { ++ /* The interface changed, e.g. 1000base-X <-> 2500base-X */ ++ /* We need to force the link down, then change the interface */ ++ if (pl->old_link_state) { ++ phylink_link_down(pl); ++ pl->old_link_state = false; ++ } ++ if (!test_bit(PHYLINK_DISABLE_STOPPED, ++ &pl->phylink_disable_state)) ++ phylink_major_config(pl, false, &config); ++ pl->link_config.interface = config.interface; ++ linkmode_copy(pl->link_config.advertising, config.advertising); ++ } else if (!linkmode_equal(pl->link_config.advertising, ++ config.advertising)) { ++ linkmode_copy(pl->link_config.advertising, config.advertising); ++ phylink_change_inband_advert(pl); + } + mutex_unlock(&pl->state_mutex); + +@@ -1293,7 +2133,7 @@ int phylink_ethtool_nway_reset(struct ph + + if (pl->phydev) + ret = phy_restart_aneg(pl->phydev); +- phylink_mac_an_restart(pl); ++ phylink_mac_pcs_an_restart(pl); + + return ret; + } +@@ -1324,9 +2164,14 @@ int phylink_ethtool_set_pauseparam(struc + struct ethtool_pauseparam *pause) + { + struct phylink_link_state *config = &pl->link_config; ++ bool manual_changed; ++ int pause_state; + + ASSERT_RTNL(); + ++ if (pl->cur_link_an_mode == MLO_AN_FIXED) ++ return -EOPNOTSUPP; ++ + if (!phylink_test(pl->supported, Pause) && + !phylink_test(pl->supported, Asym_Pause)) + return -EOPNOTSUPP; +@@ -1335,36 +2180,61 @@ int phylink_ethtool_set_pauseparam(struc + pause->rx_pause != pause->tx_pause) + return -EINVAL; + +- config->pause &= ~(MLO_PAUSE_AN | MLO_PAUSE_TXRX_MASK); +- ++ pause_state = 0; + if (pause->autoneg) +- config->pause |= MLO_PAUSE_AN; ++ pause_state |= MLO_PAUSE_AN; + if (pause->rx_pause) +- config->pause |= MLO_PAUSE_RX; ++ pause_state |= MLO_PAUSE_RX; + if (pause->tx_pause) +- config->pause |= MLO_PAUSE_TX; ++ pause_state |= MLO_PAUSE_TX; + +- /* If we have a PHY, phylib will call our link state function if the +- * mode has changed, which will trigger a resolve and update the MAC +- * configuration. ++ mutex_lock(&pl->state_mutex); ++ /* ++ * See the comments for linkmode_set_pause(), wrt the deficiencies ++ * with the current implementation. A solution to this issue would ++ * be: ++ * ethtool Local device ++ * rx tx Pause AsymDir ++ * 0 0 0 0 ++ * 1 0 1 1 ++ * 0 1 0 1 ++ * 1 1 1 1 ++ * and then use the ethtool rx/tx enablement status to mask the ++ * rx/tx pause resolution. + */ +- if (pl->phydev) { ++ linkmode_set_pause(config->advertising, pause->tx_pause, ++ pause->rx_pause); ++ ++ manual_changed = (config->pause ^ pause_state) & MLO_PAUSE_AN || ++ (!(pause_state & MLO_PAUSE_AN) && ++ (config->pause ^ pause_state) & MLO_PAUSE_TXRX_MASK); ++ ++ config->pause = pause_state; ++ ++ /* Update our in-band advertisement, triggering a renegotiation if ++ * the advertisement changed. ++ */ ++ if (!pl->phydev) ++ phylink_change_inband_advert(pl); ++ ++ mutex_unlock(&pl->state_mutex); ++ ++ /* If we have a PHY, a change of the pause frame advertisement will ++ * cause phylib to renegotiate (if AN is enabled) which will in turn ++ * call our phylink_phy_change() and trigger a resolve. Note that ++ * we can't hold our state mutex while calling phy_set_asym_pause(). ++ */ ++ if (pl->phydev) + phy_set_asym_pause(pl->phydev, pause->rx_pause, + pause->tx_pause); +- } else if (!test_bit(PHYLINK_DISABLE_STOPPED, +- &pl->phylink_disable_state)) { +- switch (pl->cur_link_an_mode) { +- case MLO_AN_FIXED: +- /* Should we allow fixed links to change against the config? */ +- phylink_resolve_flow(pl, config); +- phylink_mac_config(pl, config); +- break; + +- case MLO_AN_INBAND: +- phylink_mac_config(pl, config); +- phylink_mac_an_restart(pl); +- break; +- } ++ /* If the manual pause settings changed, make sure we trigger a ++ * resolve to update their state; we can not guarantee that the ++ * link will cycle. ++ */ ++ if (manual_changed) { ++ pl->mac_link_dropped = true; ++ phylink_run_resolve(pl); + } + + return 0; +@@ -1372,7 +2242,7 @@ int phylink_ethtool_set_pauseparam(struc + EXPORT_SYMBOL_GPL(phylink_ethtool_set_pauseparam); + + /** +- * phylink_ethtool_get_eee_err() - read the energy efficient ethernet error ++ * phylink_get_eee_err() - read the energy efficient ethernet error + * counter + * @pl: a pointer to a &struct phylink returned from phylink_create(). + * +@@ -1457,13 +2327,14 @@ static int phylink_mii_emul_read(unsigne + struct phylink_link_state *state) + { + struct fixed_phy_status fs; ++ unsigned long *lpa = state->lp_advertising; + int val; + + fs.link = state->link; + fs.speed = state->speed; + fs.duplex = state->duplex; +- fs.pause = state->pause & MLO_PAUSE_SYM; +- fs.asym_pause = state->pause & MLO_PAUSE_ASYM; ++ fs.pause = test_bit(ETHTOOL_LINK_MODE_Pause_BIT, lpa); ++ fs.asym_pause = test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, lpa); + + val = swphy_read_reg(reg, &fs); + if (reg == MII_BMSR) { +@@ -1482,18 +2353,18 @@ static int phylink_phy_read(struct phyli + if (mdio_phy_id_is_c45(phy_id)) { + prtad = mdio_phy_id_prtad(phy_id); + devad = mdio_phy_id_devad(phy_id); +- devad = MII_ADDR_C45 | devad << 16 | reg; ++ devad = mdiobus_c45_addr(devad, reg); + } else if (phydev->is_c45) { + switch (reg) { + case MII_BMCR: + case MII_BMSR: + case MII_PHYSID1: + case MII_PHYSID2: +- devad = __ffs(phydev->c45_ids.devices_in_package); ++ devad = __ffs(phydev->c45_ids.mmds_present); + break; + case MII_ADVERTISE: + case MII_LPA: +- if (!(phydev->c45_ids.devices_in_package & MDIO_DEVS_AN)) ++ if (!(phydev->c45_ids.mmds_present & MDIO_DEVS_AN)) + return -EINVAL; + devad = MDIO_MMD_AN; + if (reg == MII_ADVERTISE) +@@ -1505,7 +2376,7 @@ static int phylink_phy_read(struct phyli + return -EINVAL; + } + prtad = phy_id; +- devad = MII_ADDR_C45 | devad << 16 | reg; ++ devad = mdiobus_c45_addr(devad, reg); + } else { + prtad = phy_id; + devad = reg; +@@ -1522,18 +2393,18 @@ static int phylink_phy_write(struct phyl + if (mdio_phy_id_is_c45(phy_id)) { + prtad = mdio_phy_id_prtad(phy_id); + devad = mdio_phy_id_devad(phy_id); +- devad = MII_ADDR_C45 | devad << 16 | reg; ++ devad = mdiobus_c45_addr(devad, reg); + } else if (phydev->is_c45) { + switch (reg) { + case MII_BMCR: + case MII_BMSR: + case MII_PHYSID1: + case MII_PHYSID2: +- devad = __ffs(phydev->c45_ids.devices_in_package); ++ devad = __ffs(phydev->c45_ids.mmds_present); + break; + case MII_ADVERTISE: + case MII_LPA: +- if (!(phydev->c45_ids.devices_in_package & MDIO_DEVS_AN)) ++ if (!(phydev->c45_ids.mmds_present & MDIO_DEVS_AN)) + return -EINVAL; + devad = MDIO_MMD_AN; + if (reg == MII_ADVERTISE) +@@ -1545,7 +2416,7 @@ static int phylink_phy_write(struct phyl + return -EINVAL; + } + prtad = phy_id; +- devad = MII_ADDR_C45 | devad << 16 | reg; ++ devad = mdiobus_c45_addr(devad, reg); + } else { + prtad = phy_id; + devad = reg; +@@ -1573,10 +2444,7 @@ static int phylink_mii_read(struct phyli + + case MLO_AN_INBAND: + if (phy_id == 0) { +- val = phylink_get_mac_state(pl, &state); +- if (val < 0) +- return val; +- ++ phylink_mac_pcs_get_state(pl, &state); + val = phylink_mii_emul_read(reg, &state); + } + break; +@@ -1632,7 +2500,7 @@ int phylink_mii_ioctl(struct phylink *pl + switch (cmd) { + case SIOCGMIIPHY: + mii->phy_id = pl->phydev->mdio.addr; +- /* fall through */ ++ fallthrough; + + case SIOCGMIIREG: + ret = phylink_phy_read(pl, mii->phy_id, mii->reg_num); +@@ -1655,7 +2523,7 @@ int phylink_mii_ioctl(struct phylink *pl + switch (cmd) { + case SIOCGMIIPHY: + mii->phy_id = 0; +- /* fall through */ ++ fallthrough; + + case SIOCGMIIREG: + ret = phylink_mii_read(pl, mii->phy_id, mii->reg_num); +@@ -1680,6 +2548,54 @@ int phylink_mii_ioctl(struct phylink *pl + } + EXPORT_SYMBOL_GPL(phylink_mii_ioctl); + ++/** ++ * phylink_speed_down() - set the non-SFP PHY to lowest speed supported by both ++ * link partners ++ * @pl: a pointer to a &struct phylink returned from phylink_create() ++ * @sync: perform action synchronously ++ * ++ * If we have a PHY that is not part of a SFP module, then set the speed ++ * as described in the phy_speed_down() function. Please see this function ++ * for a description of the @sync parameter. ++ * ++ * Returns zero if there is no PHY, otherwise as per phy_speed_down(). ++ */ ++int phylink_speed_down(struct phylink *pl, bool sync) ++{ ++ int ret = 0; ++ ++ ASSERT_RTNL(); ++ ++ if (!pl->sfp_bus && pl->phydev) ++ ret = phy_speed_down(pl->phydev, sync); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(phylink_speed_down); ++ ++/** ++ * phylink_speed_up() - restore the advertised speeds prior to the call to ++ * phylink_speed_down() ++ * @pl: a pointer to a &struct phylink returned from phylink_create() ++ * ++ * If we have a PHY that is not part of a SFP module, then restore the ++ * PHY speeds as per phy_speed_up(). ++ * ++ * Returns zero if there is no PHY, otherwise as per phy_speed_up(). ++ */ ++int phylink_speed_up(struct phylink *pl) ++{ ++ int ret = 0; ++ ++ ASSERT_RTNL(); ++ ++ if (!pl->sfp_bus && pl->phydev) ++ ret = phy_speed_up(pl->phydev); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(phylink_speed_up); ++ + static void phylink_sfp_attach(void *upstream, struct sfp_bus *bus) + { + struct phylink *pl = upstream; +@@ -1749,8 +2665,9 @@ static int phylink_sfp_config(struct phy + if (phy_interface_mode_is_8023z(iface) && pl->phydev) + return -EINVAL; + +- changed = !bitmap_equal(pl->supported, support, +- __ETHTOOL_LINK_MODE_MASK_NBITS); ++ changed = !linkmode_equal(pl->supported, support) || ++ !linkmode_equal(pl->link_config.advertising, ++ config.advertising); + if (changed) { + linkmode_copy(pl->supported, support); + linkmode_copy(pl->link_config.advertising, config.advertising); +@@ -1772,7 +2689,7 @@ static int phylink_sfp_config(struct phy + + if (changed && !test_bit(PHYLINK_DISABLE_STOPPED, + &pl->phylink_disable_state)) +- phylink_mac_config(pl, &pl->link_config); ++ phylink_mac_initial_config(pl, false); + + return ret; + } +@@ -1885,14 +2802,6 @@ static int phylink_sfp_connect_phy(void + if (ret < 0) + return ret; + +- /* Clause 45 PHYs switch their Serdes lane between several different +- * modes, normally 10GBASE-R, SGMII. Some use 2500BASE-X for 2.5G +- * speeds. We really need to know which interface modes the PHY and +- * MAC supports to properly work out which linkmodes can be supported. +- */ +- if (phy->is_c45) +- interface = PHY_INTERFACE_MODE_NA; +- + ret = phylink_bringup_phy(pl, phy, interface); + if (ret) + phy_detach(phy); +@@ -1947,4 +2856,409 @@ void phylink_helper_basex_speed(struct p + } + EXPORT_SYMBOL_GPL(phylink_helper_basex_speed); + ++static void phylink_decode_c37_word(struct phylink_link_state *state, ++ uint16_t config_reg, int speed) ++{ ++ bool tx_pause, rx_pause; ++ int fd_bit; ++ ++ if (speed == SPEED_2500) ++ fd_bit = ETHTOOL_LINK_MODE_2500baseX_Full_BIT; ++ else ++ fd_bit = ETHTOOL_LINK_MODE_1000baseX_Full_BIT; ++ ++ mii_lpa_mod_linkmode_x(state->lp_advertising, config_reg, fd_bit); ++ ++ if (linkmode_test_bit(fd_bit, state->advertising) && ++ linkmode_test_bit(fd_bit, state->lp_advertising)) { ++ state->speed = speed; ++ state->duplex = DUPLEX_FULL; ++ } else { ++ /* negotiation failure */ ++ state->link = false; ++ } ++ ++ linkmode_resolve_pause(state->advertising, state->lp_advertising, ++ &tx_pause, &rx_pause); ++ ++ if (tx_pause) ++ state->pause |= MLO_PAUSE_TX; ++ if (rx_pause) ++ state->pause |= MLO_PAUSE_RX; ++} ++ ++static void phylink_decode_sgmii_word(struct phylink_link_state *state, ++ uint16_t config_reg) ++{ ++ if (!(config_reg & LPA_SGMII_LINK)) { ++ state->link = false; ++ return; ++ } ++ ++ switch (config_reg & LPA_SGMII_SPD_MASK) { ++ case LPA_SGMII_10: ++ state->speed = SPEED_10; ++ break; ++ case LPA_SGMII_100: ++ state->speed = SPEED_100; ++ break; ++ case LPA_SGMII_1000: ++ state->speed = SPEED_1000; ++ break; ++ default: ++ state->link = false; ++ return; ++ } ++ if (config_reg & LPA_SGMII_FULL_DUPLEX) ++ state->duplex = DUPLEX_FULL; ++ else ++ state->duplex = DUPLEX_HALF; ++} ++ ++/** ++ * phylink_decode_usxgmii_word() - decode the USXGMII word from a MAC PCS ++ * @state: a pointer to a struct phylink_link_state. ++ * @lpa: a 16 bit value which stores the USXGMII auto-negotiation word ++ * ++ * Helper for MAC PCS supporting the USXGMII protocol and the auto-negotiation ++ * code word. Decode the USXGMII code word and populate the corresponding fields ++ * (speed, duplex) into the phylink_link_state structure. ++ */ ++void phylink_decode_usxgmii_word(struct phylink_link_state *state, ++ uint16_t lpa) ++{ ++ switch (lpa & MDIO_USXGMII_SPD_MASK) { ++ case MDIO_USXGMII_10: ++ state->speed = SPEED_10; ++ break; ++ case MDIO_USXGMII_100: ++ state->speed = SPEED_100; ++ break; ++ case MDIO_USXGMII_1000: ++ state->speed = SPEED_1000; ++ break; ++ case MDIO_USXGMII_2500: ++ state->speed = SPEED_2500; ++ break; ++ case MDIO_USXGMII_5000: ++ state->speed = SPEED_5000; ++ break; ++ case MDIO_USXGMII_10G: ++ state->speed = SPEED_10000; ++ break; ++ default: ++ state->link = false; ++ return; ++ } ++ ++ if (lpa & MDIO_USXGMII_FULL_DUPLEX) ++ state->duplex = DUPLEX_FULL; ++ else ++ state->duplex = DUPLEX_HALF; ++} ++EXPORT_SYMBOL_GPL(phylink_decode_usxgmii_word); ++ ++/** ++ * phylink_mii_c22_pcs_get_state() - read the MAC PCS state ++ * @pcs: a pointer to a &struct mdio_device. ++ * @state: a pointer to a &struct phylink_link_state. ++ * ++ * Helper for MAC PCS supporting the 802.3 clause 22 register set for ++ * clause 37 negotiation and/or SGMII control. ++ * ++ * Read the MAC PCS state from the MII device configured in @config and ++ * parse the Clause 37 or Cisco SGMII link partner negotiation word into ++ * the phylink @state structure. This is suitable to be directly plugged ++ * into the mac_pcs_get_state() member of the struct phylink_mac_ops ++ * structure. ++ */ ++void phylink_mii_c22_pcs_get_state(struct mdio_device *pcs, ++ struct phylink_link_state *state) ++{ ++ struct mii_bus *bus = pcs->bus; ++ int addr = pcs->addr; ++ int bmsr, lpa; ++ ++ bmsr = mdiobus_read(bus, addr, MII_BMSR); ++ lpa = mdiobus_read(bus, addr, MII_LPA); ++ if (bmsr < 0 || lpa < 0) { ++ state->link = false; ++ return; ++ } ++ ++ state->link = !!(bmsr & BMSR_LSTATUS); ++ state->an_complete = !!(bmsr & BMSR_ANEGCOMPLETE); ++ if (!state->link) ++ return; ++ ++ switch (state->interface) { ++ case PHY_INTERFACE_MODE_1000BASEX: ++ phylink_decode_c37_word(state, lpa, SPEED_1000); ++ break; ++ ++ case PHY_INTERFACE_MODE_2500BASEX: ++ phylink_decode_c37_word(state, lpa, SPEED_2500); ++ break; ++ ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_QSGMII: ++ phylink_decode_sgmii_word(state, lpa); ++ break; ++ ++ default: ++ state->link = false; ++ break; ++ } ++} ++EXPORT_SYMBOL_GPL(phylink_mii_c22_pcs_get_state); ++ ++/** ++ * phylink_mii_c22_pcs_decode_state() - Decode MAC PCS state from MII registers ++ * @state: a pointer to a &struct phylink_link_state. ++ * @bmsr: The value of the %MII_BMSR register ++ * @lpa: The value of the %MII_LPA register ++ * ++ * Helper for MAC PCS supporting the 802.3 clause 22 register set for ++ * clause 37 negotiation and/or SGMII control. ++ * ++ * Parse the Clause 37 or Cisco SGMII link partner negotiation word into ++ * the phylink @state structure. This is suitable to be used for implementing ++ * the mac_pcs_get_state() member of the struct phylink_mac_ops structure if ++ * accessing @bmsr and @lpa cannot be done with MDIO directly. ++ */ ++void phylink_mii_c22_pcs_decode_state(struct phylink_link_state *state, ++ u16 bmsr, u16 lpa) ++{ ++ state->link = !!(bmsr & BMSR_LSTATUS); ++ state->an_complete = !!(bmsr & BMSR_ANEGCOMPLETE); ++ /* If there is no link or autonegotiation is disabled, the LP advertisement ++ * data is not meaningful, so don't go any further. ++ */ ++ if (!state->link || !state->an_enabled) ++ return; ++ ++ switch (state->interface) { ++ case PHY_INTERFACE_MODE_1000BASEX: ++ phylink_decode_c37_word(state, lpa, SPEED_1000); ++ break; ++ ++ case PHY_INTERFACE_MODE_2500BASEX: ++ phylink_decode_c37_word(state, lpa, SPEED_2500); ++ break; ++ ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_QSGMII: ++ phylink_decode_sgmii_word(state, lpa); ++ break; ++ ++ default: ++ state->link = false; ++ break; ++ } ++} ++EXPORT_SYMBOL_GPL(phylink_mii_c22_pcs_decode_state); ++ ++/** ++ * phylink_mii_c22_pcs_set_advertisement() - configure the clause 37 PCS ++ * advertisement ++ * @pcs: a pointer to a &struct mdio_device. ++ * @interface: the PHY interface mode being configured ++ * @advertising: the ethtool advertisement mask ++ * ++ * Helper for MAC PCS supporting the 802.3 clause 22 register set for ++ * clause 37 negotiation and/or SGMII control. ++ * ++ * Configure the clause 37 PCS advertisement as specified by @state. This ++ * does not trigger a renegotiation; phylink will do that via the ++ * mac_an_restart() method of the struct phylink_mac_ops structure. ++ * ++ * Returns negative error code on failure to configure the advertisement, ++ * zero if no change has been made, or one if the advertisement has changed. ++ */ ++int phylink_mii_c22_pcs_set_advertisement(struct mdio_device *pcs, ++ phy_interface_t interface, ++ const unsigned long *advertising) ++{ ++ struct mii_bus *bus = pcs->bus; ++ int addr = pcs->addr; ++ int val, ret; ++ u16 adv; ++ ++ switch (interface) { ++ case PHY_INTERFACE_MODE_1000BASEX: ++ case PHY_INTERFACE_MODE_2500BASEX: ++ adv = ADVERTISE_1000XFULL; ++ if (linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, ++ advertising)) ++ adv |= ADVERTISE_1000XPAUSE; ++ if (linkmode_test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, ++ advertising)) ++ adv |= ADVERTISE_1000XPSE_ASYM; ++ ++ val = mdiobus_read(bus, addr, MII_ADVERTISE); ++ if (val < 0) ++ return val; ++ ++ if (val == adv) ++ return 0; ++ ++ ret = mdiobus_write(bus, addr, MII_ADVERTISE, adv); ++ if (ret < 0) ++ return ret; ++ ++ return 1; ++ ++ case PHY_INTERFACE_MODE_SGMII: ++ val = mdiobus_read(bus, addr, MII_ADVERTISE); ++ if (val < 0) ++ return val; ++ ++ if (val == 0x0001) ++ return 0; ++ ++ ret = mdiobus_write(bus, addr, MII_ADVERTISE, 0x0001); ++ if (ret < 0) ++ return ret; ++ ++ return 1; ++ ++ default: ++ /* Nothing to do for other modes */ ++ return 0; ++ } ++} ++EXPORT_SYMBOL_GPL(phylink_mii_c22_pcs_set_advertisement); ++ ++/** ++ * phylink_mii_c22_pcs_encode_advertisement() - configure the clause 37 PCS ++ * advertisement ++ * @interface: the PHY interface mode being configured ++ * @advertising: the ethtool advertisement mask ++ * ++ * Helper for MAC PCS supporting the 802.3 clause 22 register set for ++ * clause 37 negotiation and/or SGMII control. ++ * ++ * Encode the clause 37 PCS advertisement as specified by @interface and ++ * @advertising. ++ * ++ * Return: The new value for @adv, or ``-EINVAL`` if it should not be changed. ++ */ ++int phylink_mii_c22_pcs_encode_advertisement(phy_interface_t interface, ++ const unsigned long *advertising) ++{ ++ u16 adv; ++ ++ switch (interface) { ++ case PHY_INTERFACE_MODE_1000BASEX: ++ case PHY_INTERFACE_MODE_2500BASEX: ++ adv = ADVERTISE_1000XFULL; ++ if (linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, ++ advertising)) ++ adv |= ADVERTISE_1000XPAUSE; ++ if (linkmode_test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, ++ advertising)) ++ adv |= ADVERTISE_1000XPSE_ASYM; ++ return adv; ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_QSGMII: ++ return 0x0001; ++ default: ++ /* Nothing to do for other modes */ ++ return -EINVAL; ++ } ++} ++EXPORT_SYMBOL_GPL(phylink_mii_c22_pcs_encode_advertisement); ++ ++/** ++ * phylink_mii_c22_pcs_config() - configure clause 22 PCS ++ * @pcs: a pointer to a &struct mdio_device. ++ * @mode: link autonegotiation mode ++ * @interface: the PHY interface mode being configured ++ * @advertising: the ethtool advertisement mask ++ * ++ * Configure a Clause 22 PCS PHY with the appropriate negotiation ++ * parameters for the @mode, @interface and @advertising parameters. ++ * Returns negative error number on failure, zero if the advertisement ++ * has not changed, or positive if there is a change. ++ */ ++int phylink_mii_c22_pcs_config(struct mdio_device *pcs, unsigned int mode, ++ phy_interface_t interface, ++ const unsigned long *advertising) ++{ ++ bool changed; ++ u16 bmcr; ++ int ret; ++ ++ ret = phylink_mii_c22_pcs_set_advertisement(pcs, interface, ++ advertising); ++ if (ret < 0) ++ return ret; ++ ++ changed = ret > 0; ++ ++ /* Ensure ISOLATE bit is disabled */ ++ bmcr = mode == MLO_AN_INBAND ? BMCR_ANENABLE : 0; ++ ret = mdiobus_modify(pcs->bus, pcs->addr, MII_BMCR, ++ BMCR_ANENABLE | BMCR_ISOLATE, bmcr); ++ if (ret < 0) ++ return ret; ++ ++ return changed ? 1 : 0; ++} ++EXPORT_SYMBOL_GPL(phylink_mii_c22_pcs_config); ++ ++/** ++ * phylink_mii_c22_pcs_an_restart() - restart 802.3z autonegotiation ++ * @pcs: a pointer to a &struct mdio_device. ++ * ++ * Helper for MAC PCS supporting the 802.3 clause 22 register set for ++ * clause 37 negotiation. ++ * ++ * Restart the clause 37 negotiation with the link partner. This is ++ * suitable to be directly plugged into the mac_pcs_get_state() member ++ * of the struct phylink_mac_ops structure. ++ */ ++void phylink_mii_c22_pcs_an_restart(struct mdio_device *pcs) ++{ ++ struct mii_bus *bus = pcs->bus; ++ int val, addr = pcs->addr; ++ ++ val = mdiobus_read(bus, addr, MII_BMCR); ++ if (val >= 0) { ++ val |= BMCR_ANRESTART; ++ ++ mdiobus_write(bus, addr, MII_BMCR, val); ++ } ++} ++EXPORT_SYMBOL_GPL(phylink_mii_c22_pcs_an_restart); ++ ++void phylink_mii_c45_pcs_get_state(struct mdio_device *pcs, ++ struct phylink_link_state *state) ++{ ++ struct mii_bus *bus = pcs->bus; ++ int addr = pcs->addr; ++ int stat; ++ ++ stat = mdiobus_c45_read(bus, addr, MDIO_MMD_PCS, MDIO_STAT1); ++ if (stat < 0) { ++ state->link = false; ++ return; ++ } ++ ++ state->link = !!(stat & MDIO_STAT1_LSTATUS); ++ if (!state->link) ++ return; ++ ++ switch (state->interface) { ++ case PHY_INTERFACE_MODE_10GBASER: ++ state->speed = SPEED_10000; ++ state->duplex = DUPLEX_FULL; ++ break; ++ ++ default: ++ break; ++ } ++} ++EXPORT_SYMBOL_GPL(phylink_mii_c45_pcs_get_state); ++ + MODULE_LICENSE("GPL v2"); +Index: linux-5.4.284/drivers/net/phy/vitesse.c +=================================================================== +--- linux-5.4.284.orig/drivers/net/phy/vitesse.c ++++ linux-5.4.284/drivers/net/phy/vitesse.c +@@ -245,7 +245,8 @@ static int vsc73xx_config_aneg(struct ph + + /* 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. */ ++ * on "rgmii-txid", "rgmii-rxid" or "rgmii" interfaces. ++ */ + static int vsc8601_add_skew(struct phy_device *phydev) + { + int ret; +Index: linux-5.4.284/include/linux/gpio/consumer.h +=================================================================== +--- linux-5.4.284.orig/include/linux/gpio/consumer.h ++++ linux-5.4.284/include/linux/gpio/consumer.h +@@ -177,6 +177,15 @@ struct gpio_desc *fwnode_get_named_gpiod + const char *propname, int index, + enum gpiod_flags dflags, + const char *label); ++struct gpio_desc *fwnode_gpiod_get_index(struct fwnode_handle *fwnode, ++ const char *con_id, int index, ++ enum gpiod_flags flags, ++ const char *label); ++struct gpio_desc *devm_fwnode_gpiod_get_index(struct device *dev, ++ struct fwnode_handle *child, ++ const char *con_id, int index, ++ enum gpiod_flags flags, ++ const char *label); + struct gpio_desc *devm_fwnode_get_index_gpiod_from_child(struct device *dev, + const char *con_id, int index, + struct fwnode_handle *child, +Index: linux-5.4.284/include/linux/linkmode.h +=================================================================== +--- linux-5.4.284.orig/include/linux/linkmode.h ++++ linux-5.4.284/include/linux/linkmode.h +@@ -82,4 +82,16 @@ static inline int linkmode_equal(const u + return bitmap_equal(src1, src2, __ETHTOOL_LINK_MODE_MASK_NBITS); + } + ++static inline int linkmode_subset(const unsigned long *src1, ++ const unsigned long *src2) ++{ ++ return bitmap_subset(src1, src2, __ETHTOOL_LINK_MODE_MASK_NBITS); ++} ++ ++void linkmode_resolve_pause(const unsigned long *local_adv, ++ const unsigned long *partner_adv, ++ bool *tx_pause, bool *rx_pause); ++ ++void linkmode_set_pause(unsigned long *advertisement, bool tx, bool rx); ++ + #endif /* __LINKMODE_H */ +Index: linux-5.4.284/include/linux/mdio.h +=================================================================== +--- linux-5.4.284.orig/include/linux/mdio.h ++++ linux-5.4.284/include/linux/mdio.h +@@ -18,6 +18,13 @@ + #define MII_DEVADDR_C45_MASK GENMASK(20, 16) + #define MII_REGADDR_C45_MASK GENMASK(15, 0) + ++/* Or MII_ADDR_C45 into regnum for read/write on mii_bus to enable the 21 bit ++ * IEEE 802.3ae clause 45 addressing mode used by 10GIGE phy chips. ++ */ ++#define MII_ADDR_C45 (1<<30) ++#define MII_DEVADDR_C45_SHIFT 16 ++#define MII_REGADDR_C45_MASK GENMASK(15, 0) ++ + struct gpio_desc; + struct mii_bus; + +@@ -328,11 +335,15 @@ static inline void mii_10gbt_stat_mod_li + + int __mdiobus_read(struct mii_bus *bus, int addr, u32 regnum); + int __mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val); ++int __mdiobus_modify_changed(struct mii_bus *bus, int addr, u32 regnum, ++ u16 mask, u16 set); + + int mdiobus_read(struct mii_bus *bus, int addr, u32 regnum); + int mdiobus_read_nested(struct mii_bus *bus, int addr, u32 regnum); + int mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val); + int mdiobus_write_nested(struct mii_bus *bus, int addr, u32 regnum, u16 val); ++int mdiobus_modify(struct mii_bus *bus, int addr, u32 regnum, u16 mask, ++ u16 set); + + static inline u32 mdiobus_c45_addr(int devad, u16 regnum) + { +@@ -398,4 +409,30 @@ static void __exit mdio_module_exit(void + } \ + module_exit(mdio_module_exit) + ++/* UsxgmiiChannelInfo[15:0] for USXGMII in-band auto-negotiation.*/ ++#define MDIO_USXGMII_EEE_CLK_STP 0x0080 /* EEE clock stop supported */ ++#define MDIO_USXGMII_EEE 0x0100 /* EEE supported */ ++#define MDIO_USXGMII_SPD_MASK 0x0e00 /* USXGMII speed mask */ ++#define MDIO_USXGMII_FULL_DUPLEX 0x1000 /* USXGMII full duplex */ ++#define MDIO_USXGMII_DPX_SPD_MASK 0x1e00 /* USXGMII duplex and speed bits */ ++#define MDIO_USXGMII_10 0x0000 /* 10Mbps */ ++#define MDIO_USXGMII_10HALF 0x0000 /* 10Mbps half-duplex */ ++#define MDIO_USXGMII_10FULL 0x1000 /* 10Mbps full-duplex */ ++#define MDIO_USXGMII_100 0x0200 /* 100Mbps */ ++#define MDIO_USXGMII_100HALF 0x0200 /* 100Mbps half-duplex */ ++#define MDIO_USXGMII_100FULL 0x1200 /* 100Mbps full-duplex */ ++#define MDIO_USXGMII_1000 0x0400 /* 1000Mbps */ ++#define MDIO_USXGMII_1000HALF 0x0400 /* 1000Mbps half-duplex */ ++#define MDIO_USXGMII_1000FULL 0x1400 /* 1000Mbps full-duplex */ ++#define MDIO_USXGMII_10G 0x0600 /* 10Gbps */ ++#define MDIO_USXGMII_10GHALF 0x0600 /* 10Gbps half-duplex */ ++#define MDIO_USXGMII_10GFULL 0x1600 /* 10Gbps full-duplex */ ++#define MDIO_USXGMII_2500 0x0800 /* 2500Mbps */ ++#define MDIO_USXGMII_2500HALF 0x0800 /* 2500Mbps half-duplex */ ++#define MDIO_USXGMII_2500FULL 0x1800 /* 2500Mbps full-duplex */ ++#define MDIO_USXGMII_5000 0x0a00 /* 5000Mbps */ ++#define MDIO_USXGMII_5000HALF 0x0a00 /* 5000Mbps half-duplex */ ++#define MDIO_USXGMII_5000FULL 0x1a00 /* 5000Mbps full-duplex */ ++#define MDIO_USXGMII_LINK 0x8000 /* PHY link with copper-side partner */ ++ + #endif /* __LINUX_MDIO_H__ */ +Index: linux-5.4.284/include/linux/mii.h +=================================================================== +--- linux-5.4.284.orig/include/linux/mii.h ++++ linux-5.4.284/include/linux/mii.h +@@ -486,6 +486,25 @@ static inline u32 linkmode_adv_to_lcl_ad + } + + /** ++ * mii_lpa_mod_linkmode_x - decode the link partner's config_reg to linkmodes ++ * @linkmodes: link modes array ++ * @lpa: config_reg word from link partner ++ * @fd_bit: link mode for 1000XFULL bit ++ */ ++static inline void mii_lpa_mod_linkmode_x(unsigned long *linkmodes, u16 lpa, ++ int fd_bit) ++{ ++ linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, linkmodes, ++ lpa & LPA_LPACK); ++ linkmode_mod_bit(ETHTOOL_LINK_MODE_Pause_BIT, linkmodes, ++ lpa & LPA_1000XPAUSE); ++ linkmode_mod_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, linkmodes, ++ lpa & LPA_1000XPAUSE_ASYM); ++ linkmode_mod_bit(fd_bit, linkmodes, ++ lpa & LPA_1000XFULL); ++} ++ ++/** + * mii_advertise_flowctrl - get flow control advertisement flags + * @cap: Flow control capabilities (FLOW_CTRL_RX, FLOW_CTRL_TX or both) + */ +Index: linux-5.4.284/include/linux/phy.h +=================================================================== +--- linux-5.4.284.orig/include/linux/phy.h ++++ linux-5.4.284/include/linux/phy.h +@@ -86,6 +86,7 @@ typedef enum { + PHY_INTERFACE_MODE_TBI, + PHY_INTERFACE_MODE_REVMII, + PHY_INTERFACE_MODE_RMII, ++ PHY_INTERFACE_MODE_REVRMII, + PHY_INTERFACE_MODE_RGMII, + PHY_INTERFACE_MODE_RGMII_ID, + PHY_INTERFACE_MODE_RGMII_RXID, +@@ -93,19 +94,59 @@ typedef enum { + PHY_INTERFACE_MODE_RTBI, + PHY_INTERFACE_MODE_SMII, + PHY_INTERFACE_MODE_XGMII, ++ PHY_INTERFACE_MODE_XLGMII, + PHY_INTERFACE_MODE_MOCA, + PHY_INTERFACE_MODE_QSGMII, + PHY_INTERFACE_MODE_TRGMII, ++ PHY_INTERFACE_MODE_100BASEX, + PHY_INTERFACE_MODE_1000BASEX, + PHY_INTERFACE_MODE_2500BASEX, ++ PHY_INTERFACE_MODE_5GBASER, + PHY_INTERFACE_MODE_RXAUI, + PHY_INTERFACE_MODE_XAUI, +- /* 10GBASE-KR, XFI, SFI - single lane 10G Serdes */ +- PHY_INTERFACE_MODE_10GKR, ++ /* 10GBASE-R, XFI, SFI - single lane 10G Serdes */ ++ PHY_INTERFACE_MODE_10GBASER, ++ PHY_INTERFACE_MODE_25GBASER, + PHY_INTERFACE_MODE_USXGMII, ++ /* 10GBASE-KR - with Clause 73 AN */ ++ PHY_INTERFACE_MODE_10GKR, + PHY_INTERFACE_MODE_MAX, + } phy_interface_t; + ++/* PHY interface mode bitmap handling */ ++#define DECLARE_PHY_INTERFACE_MASK(name) \ ++ DECLARE_BITMAP(name, PHY_INTERFACE_MODE_MAX) ++ ++static inline void phy_interface_zero(unsigned long *intf) ++{ ++ bitmap_zero(intf, PHY_INTERFACE_MODE_MAX); ++} ++ ++static inline bool phy_interface_empty(const unsigned long *intf) ++{ ++ return bitmap_empty(intf, PHY_INTERFACE_MODE_MAX); ++} ++ ++static inline void phy_interface_and(unsigned long *dst, const unsigned long *a, ++ const unsigned long *b) ++{ ++ bitmap_and(dst, a, b, PHY_INTERFACE_MODE_MAX); ++} ++ ++static inline void phy_interface_or(unsigned long *dst, const unsigned long *a, ++ const unsigned long *b) ++{ ++ bitmap_or(dst, a, b, PHY_INTERFACE_MODE_MAX); ++} ++ ++static inline void phy_interface_set_rgmii(unsigned long *intf) ++{ ++ __set_bit(PHY_INTERFACE_MODE_RGMII, intf); ++ __set_bit(PHY_INTERFACE_MODE_RGMII_ID, intf); ++ __set_bit(PHY_INTERFACE_MODE_RGMII_RXID, intf); ++ __set_bit(PHY_INTERFACE_MODE_RGMII_TXID, intf); ++} ++ + /** + * phy_supported_speeds - return all speeds currently supported by a phy device + * @phy: The phy device to return supported speeds of. +@@ -196,12 +237,6 @@ static inline const char *phy_modes(phy_ + + #define MII_BUS_ID_SIZE 61 + +-/* Or MII_ADDR_C45 into regnum for read/write on mii_bus to enable the 21 bit +- IEEE 802.3ae clause 45 addressing mode used by 10GIGE phy chips. */ +-#define MII_ADDR_C45 (1<<30) +-#define MII_DEVADDR_C45_SHIFT 16 +-#define MII_REGADDR_C45_MASK GENMASK(15, 0) +- + struct device; + struct phylink; + struct sfp_bus; +@@ -320,11 +355,13 @@ enum phy_state { + + /** + * struct phy_c45_device_ids - 802.3-c45 Device Identifiers +- * @devices_in_package: Bit vector of devices present. ++ * @devices_in_package: IEEE 802.3 devices in package register value. ++ * @mmds_present: bit vector of MMDs present. + * @device_ids: The device identifer for each present device. + */ + struct phy_c45_device_ids { + u32 devices_in_package; ++ u32 mmds_present; + u32 device_ids[8]; + }; + +@@ -382,6 +419,8 @@ struct phy_device { + unsigned sysfs_links:1; + unsigned loopback_enabled:1; + ++ unsigned mac_managed_pm:1; ++ + unsigned autoneg:1; + /* The most recently read link state */ + unsigned link:1; +@@ -448,7 +487,7 @@ struct phy_device { + u8 mdix; + u8 mdix_ctrl; + +- void (*phy_link_change)(struct phy_device *, bool up, bool do_carrier); ++ void (*phy_link_change)(struct phy_device *phydev, bool up); + void (*adjust_link)(struct net_device *dev); + }; + #define to_phy_device(d) container_of(to_mdio_device(d), \ +@@ -1029,6 +1068,10 @@ struct phy_device *phy_device_create(str + struct phy_device *get_phy_device(struct mii_bus *bus, int addr, bool is_c45); + int phy_device_register(struct phy_device *phy); + void phy_device_free(struct phy_device *phydev); ++struct fwnode_handle *fwnode_get_phy_node(struct fwnode_handle *fwnode); ++struct mdio_device *fwnode_mdio_find_device(struct fwnode_handle *fwnode); ++struct phy_device *fwnode_phy_find_device(struct fwnode_handle *phy_fwnode); ++ + #else + static inline + struct phy_device *get_phy_device(struct mii_bus *bus, int addr, bool is_c45) +@@ -1041,6 +1084,24 @@ static inline int phy_device_register(st + return 0; + } + ++static inline ++struct fwnode_handle *fwnode_get_phy_node(struct fwnode_handle *fwnode) ++{ ++ return NULL; ++} ++ ++static inline ++struct mdio_device *fwnode_mdio_find_device(struct fwnode_handle *fwnode) ++{ ++ return 0; ++} ++ ++static inline ++struct phy_device *fwnode_phy_find_device(struct fwnode_handle *phy_fwnode) ++{ ++ return NULL; ++} ++ + static inline void phy_device_free(struct phy_device *phydev) { } + #endif /* CONFIG_PHYLIB */ + void phy_device_remove(struct phy_device *phydev); +@@ -1100,6 +1161,8 @@ static inline const char *phydev_name(co + + void phy_attached_print(struct phy_device *phydev, const char *fmt, ...) + __printf(2, 3); ++char *phy_attached_info_irq(struct phy_device *phydev) ++ __malloc; + void phy_attached_info(struct phy_device *phydev); + + /* Clause 22 PHY */ +@@ -1201,6 +1264,7 @@ void phy_set_sym_pause(struct phy_device + void phy_set_asym_pause(struct phy_device *phydev, bool rx, bool tx); + bool phy_validate_pause(struct phy_device *phydev, + struct ethtool_pauseparam *pp); ++void phy_get_pause(struct phy_device *phydev, bool *tx_pause, bool *rx_pause); + + int phy_register_fixup(const char *bus_id, u32 phy_uid, u32 phy_uid_mask, + int (*run)(struct phy_device *)); +Index: linux-5.4.284/include/linux/phylink.h +=================================================================== +--- linux-5.4.284.orig/include/linux/phylink.h ++++ linux-5.4.284/include/linux/phylink.h +@@ -12,16 +12,37 @@ struct net_device; + + enum { + MLO_PAUSE_NONE, +- MLO_PAUSE_ASYM = BIT(0), +- MLO_PAUSE_SYM = BIT(1), +- MLO_PAUSE_RX = BIT(2), +- MLO_PAUSE_TX = BIT(3), ++ MLO_PAUSE_RX = BIT(0), ++ MLO_PAUSE_TX = BIT(1), + MLO_PAUSE_TXRX_MASK = MLO_PAUSE_TX | MLO_PAUSE_RX, +- MLO_PAUSE_AN = BIT(4), ++ MLO_PAUSE_AN = BIT(2), + + MLO_AN_PHY = 0, /* Conventional PHY */ + MLO_AN_FIXED, /* Fixed-link mode */ + MLO_AN_INBAND, /* In-band protocol */ ++ ++ MAC_SYM_PAUSE = BIT(0), ++ MAC_ASYM_PAUSE = BIT(1), ++ MAC_10HD = BIT(2), ++ MAC_10FD = BIT(3), ++ MAC_10 = MAC_10HD | MAC_10FD, ++ MAC_100HD = BIT(4), ++ MAC_100FD = BIT(5), ++ MAC_100 = MAC_100HD | MAC_100FD, ++ MAC_1000HD = BIT(6), ++ MAC_1000FD = BIT(7), ++ MAC_1000 = MAC_1000HD | MAC_1000FD, ++ MAC_2500FD = BIT(8), ++ MAC_5000FD = BIT(9), ++ MAC_10000FD = BIT(10), ++ MAC_20000FD = BIT(11), ++ MAC_25000FD = BIT(12), ++ MAC_40000FD = BIT(13), ++ MAC_50000FD = BIT(14), ++ MAC_56000FD = BIT(15), ++ MAC_100000FD = BIT(16), ++ MAC_200000FD = BIT(17), ++ MAC_400000FD = BIT(18), + }; + + static inline bool phylink_autoneg_inband(unsigned int mode) +@@ -63,17 +84,32 @@ enum phylink_op_type { + * struct phylink_config - PHYLINK configuration structure + * @dev: a pointer to a struct device associated with the MAC + * @type: operation type of PHYLINK instance ++* @legacy_pre_march2020: driver has not been updated for March 2020 updates ++ * (See commit 7cceb599d15d ("net: phylink: avoid mac_config calls") ++ * @pcs_poll: MAC PCS cannot provide link change interrupt + */ + struct phylink_config { + struct device *dev; + enum phylink_op_type type; ++ bool legacy_pre_march2020; ++ bool pcs_poll; ++ bool poll_fixed_state; ++ bool mac_managed_pm; ++ bool ovr_an_inband; ++ void (*get_fixed_state)(struct phylink_config *config, ++ struct phylink_link_state *state); ++ DECLARE_PHY_INTERFACE_MASK(supported_interfaces); ++ unsigned long mac_capabilities; + }; + + /** + * struct phylink_mac_ops - MAC operations structure. + * @validate: Validate and update the link configuration. +- * @mac_link_state: Read the current link state from the hardware. ++ * @mac_select_pcs: Select a PCS for the interface mode. ++ * @mac_pcs_get_state: Read the current link state from the hardware. ++ * @mac_prepare: prepare for a major reconfiguration of the interface. + * @mac_config: configure the MAC for the selected mode and state. ++ * @mac_finish: finish a major reconfiguration of the interface. + * @mac_an_restart: restart 802.3z BaseX autonegotiation. + * @mac_link_down: take the link down. + * @mac_link_up: allow the link to come up. +@@ -84,17 +120,23 @@ struct phylink_mac_ops { + void (*validate)(struct phylink_config *config, + unsigned long *supported, + struct phylink_link_state *state); +- int (*mac_link_state)(struct phylink_config *config, +- struct phylink_link_state *state); ++ struct phylink_pcs *(*mac_select_pcs)(struct phylink_config *config, ++ phy_interface_t interface); ++ void (*mac_pcs_get_state)(struct phylink_config *config, ++ struct phylink_link_state *state); ++ int (*mac_prepare)(struct phylink_config *config, unsigned int mode, ++ phy_interface_t iface); + void (*mac_config)(struct phylink_config *config, unsigned int mode, + const struct phylink_link_state *state); ++ int (*mac_finish)(struct phylink_config *config, unsigned int mode, ++ phy_interface_t iface); + void (*mac_an_restart)(struct phylink_config *config); + void (*mac_link_down)(struct phylink_config *config, unsigned int mode, + phy_interface_t interface); + void (*mac_link_up)(struct phylink_config *config, +- struct phy_device *phy, unsigned int mode, +- phy_interface_t interface, int speed, int duplex, +- bool tx_pause, bool rx_pause); ++ struct phy_device *phy, unsigned int mode, ++ phy_interface_t interface, int speed, int duplex, ++ bool tx_pause, bool rx_pause); + }; + + #if 0 /* For kernel-doc purposes only. */ +@@ -126,20 +168,61 @@ struct phylink_mac_ops { + */ + void validate(struct phylink_config *config, unsigned long *supported, + struct phylink_link_state *state); +- + /** +- * mac_link_state() - Read the current link state from the hardware ++ * mac_select_pcs: Select a PCS for the interface mode. ++ * @config: a pointer to a &struct phylink_config. ++ * @interface: PHY interface mode for PCS ++ * ++ * Return the &struct phylink_pcs for the specified interface mode, or ++ * NULL if none is required, or an error pointer on error. ++ * ++ * This must not modify any state. It is used to query which PCS should ++ * be used. Phylink will use this during validation to ensure that the ++ * configuration is valid, and when setting a configuration to internally ++ * set the PCS that will be used. ++ */ ++struct phylink_pcs *mac_select_pcs(struct phylink_config *config, ++ phy_interface_t interface); ++ ++/** ++ * mac_pcs_get_state() - Read the current inband link state from the hardware + * @config: a pointer to a &struct phylink_config. + * @state: a pointer to a &struct phylink_link_state. + * +- * Read the current link state from the MAC, reporting the current +- * speed in @state->speed, duplex mode in @state->duplex, pause mode +- * in @state->pause using the %MLO_PAUSE_RX and %MLO_PAUSE_TX bits, +- * negotiation completion state in @state->an_complete, and link +- * up state in @state->link. ++ * Read the current inband link state from the MAC PCS, reporting the ++ * current speed in @state->speed, duplex mode in @state->duplex, pause ++ * mode in @state->pause using the %MLO_PAUSE_RX and %MLO_PAUSE_TX bits, ++ * negotiation completion state in @state->an_complete, and link up state ++ * in @state->link. If possible, @state->lp_advertising should also be ++ * populated. + */ +-int mac_link_state(struct phylink_config *config, +- struct phylink_link_state *state); ++void mac_pcs_get_state(struct phylink_config *config, ++ struct phylink_link_state *state); ++ ++/** ++ * mac_prepare() - prepare to change the PHY interface mode ++ * @config: a pointer to a &struct phylink_config. ++ * @mode: one of %MLO_AN_FIXED, %MLO_AN_PHY, %MLO_AN_INBAND. ++ * @iface: interface mode to switch to ++ * ++ * phylink will call this method at the beginning of a full initialisation ++ * of the link, which includes changing the interface mode or at initial ++ * startup time. It may be called for the current mode. The MAC driver ++ * should perform whatever actions are required, e.g. disabling the ++ * Serdes PHY. ++ * ++ * This will be the first call in the sequence: ++ * - mac_prepare() ++ * - mac_config() ++ * - pcs_config() ++ * - possible pcs_an_restart() ++ * - mac_finish() ++ * ++ * Returns zero on success, or negative errno on failure which will be ++ * reported to the kernel log. ++ */ ++int mac_prepare(struct phylink_config *config, unsigned int mode, ++ phy_interface_t iface); + + /** + * mac_config() - configure the MAC for the selected mode and state +@@ -167,7 +250,7 @@ int mac_link_state(struct phylink_config + * 1000base-X or Cisco SGMII mode depending on the @state->interface + * mode). In both cases, link state management (whether the link + * is up or not) is performed by the MAC, and reported via the +- * mac_link_state() callback. Changes in link state must be made ++ * mac_pcs_get_state() callback. Changes in link state must be made + * by calling phylink_mac_change(). + * + * If in 802.3z mode, the link speed is fixed, dependent on the +@@ -196,6 +279,23 @@ void mac_config(struct phylink_config *c + const struct phylink_link_state *state); + + /** ++ * mac_finish() - finish a to change the PHY interface mode ++ * @config: a pointer to a &struct phylink_config. ++ * @mode: one of %MLO_AN_FIXED, %MLO_AN_PHY, %MLO_AN_INBAND. ++ * @iface: interface mode to switch to ++ * ++ * phylink will call this if it called mac_prepare() to allow the MAC to ++ * complete any necessary steps after the MAC and PCS have been configured ++ * for the @mode and @iface. E.g. a MAC driver may wish to re-enable the ++ * Serdes PHY here if it was previously disabled by mac_prepare(). ++ * ++ * Returns zero on success, or negative errno on failure which will be ++ * reported to the kernel log. ++ */ ++int mac_finish(struct phylink_config *config, unsigned int mode, ++ phy_interface_t iface); ++ ++/** + * mac_an_restart() - restart 802.3z BaseX autonegotiation + * @config: a pointer to a &struct phylink_config. + */ +@@ -218,53 +318,179 @@ void mac_link_down(struct phylink_config + /** + * mac_link_up() - allow the link to come up + * @config: a pointer to a &struct phylink_config. +- * @phy: any attached phy + * @mode: link autonegotiation mode + * @interface: link &typedef phy_interface_t mode +- * @speed: link speed +- * @duplex: link duplex +- * @tx_pause: link transmit pause enablement status +- * @rx_pause: link receive pause enablement status +- * +- * Configure the MAC for an established link. +- * +- * @speed, @duplex, @tx_pause and @rx_pause indicate the finalised link +- * settings, and should be used to configure the MAC block appropriately +- * where these settings are not automatically conveyed from the PCS block, +- * or if in-band negotiation (as defined by phylink_autoneg_inband(@mode)) +- * is disabled. +- * +- * Note that when 802.3z in-band negotiation is in use, it is possible +- * that the user wishes to override the pause settings, and this should +- * be allowed when considering the implementation of this method. ++ * @phy: any attached phy + * +- * If in-band negotiation mode is disabled, allow the link to come up. If +- * @phy is non-%NULL, configure Energy Efficient Ethernet by calling ++ * If @mode is not an in-band negotiation mode (as defined by ++ * phylink_autoneg_inband()), allow the link to come up. If @phy ++ * is non-%NULL, configure Energy Efficient Ethernet by calling + * phy_init_eee() and perform appropriate MAC configuration for EEE. + * Interface type selection must be done in mac_config(). + */ + void mac_link_up(struct phylink_config *config, struct phy_device *phy, +- unsigned int mode, phy_interface_t interface, +- int speed, int duplex, bool tx_pause, bool rx_pause); ++ unsigned int mode, phy_interface_t interface, ++ int speed, int duplex, bool tx_pause, bool rx_pause); + #endif + ++struct phylink_pcs_ops; ++ ++/** ++ * struct phylink_pcs - PHYLINK PCS instance ++ * @ops: a pointer to the &struct phylink_pcs_ops structure ++ * @poll: poll the PCS for link changes ++ * ++ * This structure is designed to be embedded within the PCS private data, ++ * and will be passed between phylink and the PCS. ++ */ ++struct phylink_pcs { ++ const struct phylink_pcs_ops *ops; ++ bool poll; ++}; ++ ++/** ++ * struct phylink_pcs_ops - MAC PCS operations structure. ++ * @pcs_validate: validate the link configuration. ++ * @pcs_get_state: read the current MAC PCS link state from the hardware. ++ * @pcs_config: configure the MAC PCS for the selected mode and state. ++ * @pcs_an_restart: restart 802.3z BaseX autonegotiation. ++ * @pcs_link_up: program the PCS for the resolved link configuration ++ * (where necessary). ++ */ ++struct phylink_pcs_ops { ++ int (*pcs_validate)(struct phylink_pcs *pcs, unsigned long *supported, ++ const struct phylink_link_state *state); ++ void (*pcs_get_state)(struct phylink_pcs *pcs, ++ struct phylink_link_state *state); ++ int (*pcs_config)(struct phylink_pcs *pcs, unsigned int mode, ++ phy_interface_t interface, ++ const unsigned long *advertising, ++ bool permit_pause_to_mac); ++ void (*pcs_an_restart)(struct phylink_pcs *pcs); ++ void (*pcs_link_up)(struct phylink_pcs *pcs, unsigned int mode, ++ phy_interface_t interface, int speed, int duplex); ++ int (*pcs_enable)(struct phylink_pcs *pcs); ++ void (*pcs_disable)(struct phylink_pcs *pcs); ++}; ++ ++int pcs_enable(struct phylink_pcs *pcs); ++ ++void pcs_disable(struct phylink_pcs *pcs); ++ ++#if 0 /* For kernel-doc purposes only. */ ++/** ++ * pcs_validate() - validate the link configuration. ++ * @pcs: a pointer to a &struct phylink_pcs. ++ * @supported: ethtool bitmask for supported link modes. ++ * @state: a const pointer to a &struct phylink_link_state. ++ * ++ * Validate the interface mode, and advertising's autoneg bit, removing any ++ * media ethtool link modes that would not be supportable from the supported ++ * mask. Phylink will propagate the changes to the advertising mask. See the ++ * &struct phylink_mac_ops validate() method. ++ * ++ * Returns -EINVAL if the interface mode/autoneg mode is not supported. ++ * Returns non-zero positive if the link state can be supported. ++ */ ++int pcs_validate(struct phylink_pcs *pcs, unsigned long *supported, ++ const struct phylink_link_state *state); ++ ++/** ++ * pcs_get_state() - Read the current inband link state from the hardware ++ * @pcs: a pointer to a &struct phylink_pcs. ++ * @state: a pointer to a &struct phylink_link_state. ++ * ++ * Read the current inband link state from the MAC PCS, reporting the ++ * current speed in @state->speed, duplex mode in @state->duplex, pause ++ * mode in @state->pause using the %MLO_PAUSE_RX and %MLO_PAUSE_TX bits, ++ * negotiation completion state in @state->an_complete, and link up state ++ * in @state->link. If possible, @state->lp_advertising should also be ++ * populated. ++ * ++ * When present, this overrides mac_pcs_get_state() in &struct ++ * phylink_mac_ops. ++ */ ++void pcs_get_state(struct phylink_pcs *pcs, ++ struct phylink_link_state *state); ++ ++/** ++ * pcs_config() - Configure the PCS mode and advertisement ++ * @pcs: a pointer to a &struct phylink_pcs. ++ * @mode: one of %MLO_AN_FIXED, %MLO_AN_PHY, %MLO_AN_INBAND. ++ * @interface: interface mode to be used ++ * @advertising: adertisement ethtool link mode mask ++ * @permit_pause_to_mac: permit forwarding pause resolution to MAC ++ * ++ * Configure the PCS for the operating mode, the interface mode, and set ++ * the advertisement mask. @permit_pause_to_mac indicates whether the ++ * hardware may forward the pause mode resolution to the MAC. ++ * ++ * When operating in %MLO_AN_INBAND, inband should always be enabled, ++ * otherwise inband should be disabled. ++ * ++ * For SGMII, there is no advertisement from the MAC side, the PCS should ++ * be programmed to acknowledge the inband word from the PHY. ++ * ++ * For 1000BASE-X, the advertisement should be programmed into the PCS. ++ * ++ * For most 10GBASE-R, there is no advertisement. ++ */ ++int pcs_config(struct phylink_pcs *pcs, unsigned int mode, ++ phy_interface_t interface, const unsigned long *advertising); ++ ++/** ++ * pcs_an_restart() - restart 802.3z BaseX autonegotiation ++ * @pcs: a pointer to a &struct phylink_pcs. ++ * ++ * When PCS ops are present, this overrides mac_an_restart() in &struct ++ * phylink_mac_ops. ++ */ ++void pcs_an_restart(struct phylink_pcs *pcs); ++ ++/** ++ * pcs_link_up() - program the PCS for the resolved link configuration ++ * @pcs: a pointer to a &struct phylink_pcs. ++ * @mode: link autonegotiation mode ++ * @interface: link &typedef phy_interface_t mode ++ * @speed: link speed ++ * @duplex: link duplex ++ * ++ * This call will be made just before mac_link_up() to inform the PCS of ++ * the resolved link parameters. For example, a PCS operating in SGMII ++ * mode without in-band AN needs to be manually configured for the link ++ * and duplex setting. Otherwise, this should be a no-op. ++ */ ++void pcs_link_up(struct phylink_pcs *pcs, unsigned int mode, ++ phy_interface_t interface, int speed, int duplex); ++#endif ++ ++void phylink_get_linkmodes(unsigned long *linkmodes, phy_interface_t interface, ++ unsigned long mac_capabilities); ++void phylink_generic_validate(struct phylink_config *config, ++ unsigned long *supported, ++ struct phylink_link_state *state); ++ + struct phylink *phylink_create(struct phylink_config *, struct fwnode_handle *, + phy_interface_t iface, +- const struct phylink_mac_ops *ops); ++ const struct phylink_mac_ops *mac_ops); ++void phylink_set_pcs(struct phylink *, struct phylink_pcs *pcs); + void phylink_destroy(struct phylink *); + + int phylink_connect_phy(struct phylink *, struct phy_device *); + int phylink_of_phy_connect(struct phylink *, struct device_node *, u32 flags); ++int phylink_fwnode_phy_connect(struct phylink *pl, ++ struct fwnode_handle *fwnode, ++ u32 flags); + void phylink_disconnect_phy(struct phylink *); +-int phylink_fixed_state_cb(struct phylink *, +- void (*cb)(struct net_device *dev, +- struct phylink_link_state *)); + + void phylink_mac_change(struct phylink *, bool up); + + void phylink_start(struct phylink *); + void phylink_stop(struct phylink *); + ++void phylink_suspend(struct phylink *pl, bool mac_wol); ++void phylink_resume(struct phylink *pl); ++ + void phylink_ethtool_get_wol(struct phylink *, struct ethtool_wolinfo *); + int phylink_ethtool_set_wol(struct phylink *, struct ethtool_wolinfo *); + +@@ -282,6 +508,8 @@ int phylink_init_eee(struct phylink *, b + int phylink_ethtool_get_eee(struct phylink *, struct ethtool_eee *); + int phylink_ethtool_set_eee(struct phylink *, struct ethtool_eee *); + int phylink_mii_ioctl(struct phylink *, struct ifreq *, int); ++int phylink_speed_down(struct phylink *pl, bool sync); ++int phylink_speed_up(struct phylink *pl); + + #define phylink_zero(bm) \ + bitmap_zero(bm, __ETHTOOL_LINK_MODE_MASK_NBITS) +@@ -293,6 +521,47 @@ int phylink_mii_ioctl(struct phylink *, + #define phylink_test(bm, mode) __phylink_do_bit(test_bit, bm, mode) + + void phylink_set_port_modes(unsigned long *bits); ++ ++/** ++ * phylink_get_link_timer_ns - return the PCS link timer value ++ * @interface: link &typedef phy_interface_t mode ++ * ++ * Return the PCS link timer setting in nanoseconds for the PHY @interface ++ * mode, or -EINVAL if not appropriate. ++ */ ++static inline int phylink_get_link_timer_ns(phy_interface_t interface) ++{ ++ switch (interface) { ++ case PHY_INTERFACE_MODE_SGMII: ++ return 1600000; ++ ++ case PHY_INTERFACE_MODE_1000BASEX: ++ case PHY_INTERFACE_MODE_2500BASEX: ++ return 10000000; ++ ++ default: ++ return -EINVAL; ++ } ++} ++ + void phylink_helper_basex_speed(struct phylink_link_state *state); ++void phylink_mii_c22_pcs_decode_state(struct phylink_link_state *state, ++ u16 bmsr, u16 lpa); ++void phylink_mii_c22_pcs_get_state(struct mdio_device *pcs, ++ struct phylink_link_state *state); ++int phylink_mii_c22_pcs_encode_advertisement(phy_interface_t interface, ++ const unsigned long *advertising); ++int phylink_mii_c22_pcs_set_advertisement(struct mdio_device *pcs, ++ phy_interface_t interface, ++ const unsigned long *advertising); ++int phylink_mii_c22_pcs_config(struct mdio_device *pcs, unsigned int mode, ++ phy_interface_t interface, ++ const unsigned long *advertising); ++void phylink_mii_c22_pcs_an_restart(struct mdio_device *pcs); ++ ++void phylink_mii_c45_pcs_get_state(struct mdio_device *pcs, ++ struct phylink_link_state *state); + ++void phylink_decode_usxgmii_word(struct phylink_link_state *state, ++ uint16_t lpa); + #endif +Index: linux-5.4.284/include/uapi/linux/ethtool.h +=================================================================== +--- linux-5.4.284.orig/include/uapi/linux/ethtool.h ++++ linux-5.4.284/include/uapi/linux/ethtool.h +@@ -1507,6 +1507,29 @@ enum ethtool_link_mode_bit_indices { + ETHTOOL_LINK_MODE_200000baseCR4_Full_BIT = 66, + ETHTOOL_LINK_MODE_100baseT1_Full_BIT = 67, + ETHTOOL_LINK_MODE_1000baseT1_Full_BIT = 68, ++ ETHTOOL_LINK_MODE_400000baseKR8_Full_BIT = 69, ++ ETHTOOL_LINK_MODE_400000baseSR8_Full_BIT = 70, ++ ETHTOOL_LINK_MODE_400000baseLR8_ER8_FR8_Full_BIT = 71, ++ ETHTOOL_LINK_MODE_400000baseDR8_Full_BIT = 72, ++ ETHTOOL_LINK_MODE_400000baseCR8_Full_BIT = 73, ++ ETHTOOL_LINK_MODE_FEC_LLRS_BIT = 74, ++ ETHTOOL_LINK_MODE_100000baseKR_Full_BIT = 75, ++ ETHTOOL_LINK_MODE_100000baseSR_Full_BIT = 76, ++ ETHTOOL_LINK_MODE_100000baseLR_ER_FR_Full_BIT = 77, ++ ETHTOOL_LINK_MODE_100000baseCR_Full_BIT = 78, ++ ETHTOOL_LINK_MODE_100000baseDR_Full_BIT = 79, ++ ETHTOOL_LINK_MODE_200000baseKR2_Full_BIT = 80, ++ ETHTOOL_LINK_MODE_200000baseSR2_Full_BIT = 81, ++ ETHTOOL_LINK_MODE_200000baseLR2_ER2_FR2_Full_BIT = 82, ++ ETHTOOL_LINK_MODE_200000baseDR2_Full_BIT = 83, ++ ETHTOOL_LINK_MODE_200000baseCR2_Full_BIT = 84, ++ ETHTOOL_LINK_MODE_400000baseKR4_Full_BIT = 85, ++ ETHTOOL_LINK_MODE_400000baseSR4_Full_BIT = 86, ++ ETHTOOL_LINK_MODE_400000baseLR4_ER4_FR4_Full_BIT = 87, ++ ETHTOOL_LINK_MODE_400000baseDR4_Full_BIT = 88, ++ ETHTOOL_LINK_MODE_400000baseCR4_Full_BIT = 89, ++ ETHTOOL_LINK_MODE_100baseFX_Half_BIT = 90, ++ ETHTOOL_LINK_MODE_100baseFX_Full_BIT = 91, + + /* must be last entry */ + __ETHTOOL_LINK_MODE_MASK_NBITS +@@ -1618,6 +1641,7 @@ enum ethtool_link_mode_bit_indices { + #define SPEED_56000 56000 + #define SPEED_100000 100000 + #define SPEED_200000 200000 ++#define SPEED_400000 400000 + + #define SPEED_UNKNOWN -1 + +Index: linux-5.4.284/include/uapi/linux/mii.h +=================================================================== +--- linux-5.4.284.orig/include/uapi/linux/mii.h ++++ linux-5.4.284/include/uapi/linux/mii.h +@@ -131,14 +131,33 @@ + #define NWAYTEST_LOOPBACK 0x0100 /* Enable loopback for N-way */ + #define NWAYTEST_RESV2 0xfe00 /* Unused... */ + ++/* MAC and PHY tx_config_Reg[15:0] for SGMII in-band auto-negotiation.*/ ++#define ADVERTISE_SGMII 0x0001 /* MAC can do SGMII */ ++#define LPA_SGMII 0x0001 /* PHY can do SGMII */ ++#define LPA_SGMII_SPD_MASK 0x0c00 /* SGMII speed mask */ ++#define LPA_SGMII_FULL_DUPLEX 0x1000 /* SGMII full duplex */ ++#define LPA_SGMII_DPX_SPD_MASK 0x1C00 /* SGMII duplex and speed bits */ ++#define LPA_SGMII_10 0x0000 /* 10Mbps */ ++#define LPA_SGMII_10HALF 0x0000 /* Can do 10mbps half-duplex */ ++#define LPA_SGMII_10FULL 0x1000 /* Can do 10mbps full-duplex */ ++#define LPA_SGMII_100 0x0400 /* 100Mbps */ ++#define LPA_SGMII_100HALF 0x0400 /* Can do 100mbps half-duplex */ ++#define LPA_SGMII_100FULL 0x1400 /* Can do 100mbps full-duplex */ ++#define LPA_SGMII_1000 0x0800 /* 1000Mbps */ ++#define LPA_SGMII_1000HALF 0x0800 /* Can do 1000mbps half-duplex */ ++#define LPA_SGMII_1000FULL 0x1800 /* Can do 1000mbps full-duplex */ ++#define LPA_SGMII_LINK 0x8000 /* PHY link with copper-side partner */ ++ + /* 1000BASE-T Control register */ + #define ADVERTISE_1000FULL 0x0200 /* Advertise 1000BASE-T full duplex */ + #define ADVERTISE_1000HALF 0x0100 /* Advertise 1000BASE-T half duplex */ ++#define CTL1000_PREFER_MASTER 0x0400 /* prefer to operate as master */ + #define CTL1000_AS_MASTER 0x0800 + #define CTL1000_ENABLE_MASTER 0x1000 + + /* 1000BASE-T Status register */ + #define LPA_1000MSFAIL 0x8000 /* Master/Slave resolution failure */ ++#define LPA_1000MSRES 0x4000 /* Master/Slave resolution status */ + #define LPA_1000LOCALRXOK 0x2000 /* Link partner local receiver status */ + #define LPA_1000REMRXOK 0x1000 /* Link partner remote receiver status */ + #define LPA_1000FULL 0x0800 /* Link partner 1000BASE-T full duplex */ +Index: linux-5.4.284/net/dsa/dsa_priv.h +=================================================================== +--- linux-5.4.284.orig/net/dsa/dsa_priv.h ++++ linux-5.4.284/net/dsa/dsa_priv.h +@@ -182,8 +182,8 @@ void dsa_port_link_unregister_of(struct + void dsa_port_phylink_validate(struct phylink_config *config, + unsigned long *supported, + struct phylink_link_state *state); +-int dsa_port_phylink_mac_link_state(struct phylink_config *config, +- struct phylink_link_state *state); ++void dsa_port_phylink_mac_pcs_get_state(struct phylink_config *config, ++ struct phylink_link_state *state); + void dsa_port_phylink_mac_config(struct phylink_config *config, + unsigned int mode, + const struct phylink_link_state *state); +@@ -192,11 +192,11 @@ void dsa_port_phylink_mac_link_down(stru + unsigned int mode, + phy_interface_t interface); + void dsa_port_phylink_mac_link_up(struct phylink_config *config, +- struct phy_device *phydev, +- unsigned int mode, +- phy_interface_t interface, +- int speed, int duplex, +- bool tx_pause, bool rx_pause); ++ struct phy_device *phydev, ++ unsigned int mode, ++ phy_interface_t interface, ++ int speed, int duplex, ++ bool tx_pause, bool rx_pause); + extern const struct phylink_mac_ops dsa_port_phylink_mac_ops; + + /* slave.c */ +Index: linux-5.4.284/net/dsa/port.c +=================================================================== +--- linux-5.4.284.orig/net/dsa/port.c ++++ linux-5.4.284/net/dsa/port.c +@@ -467,19 +467,22 @@ void dsa_port_phylink_validate(struct ph + } + EXPORT_SYMBOL_GPL(dsa_port_phylink_validate); + +-int dsa_port_phylink_mac_link_state(struct phylink_config *config, +- struct phylink_link_state *state) ++void dsa_port_phylink_mac_pcs_get_state(struct phylink_config *config, ++ struct phylink_link_state *state) + { + struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); + struct dsa_switch *ds = dp->ds; + +- /* Only called for SGMII and 802.3z */ +- if (!ds->ops->phylink_mac_link_state) +- return -EOPNOTSUPP; ++ /* Only called for inband modes */ ++ if (!ds->ops->phylink_mac_link_state) { ++ state->link = 0; ++ return; ++ } + +- return ds->ops->phylink_mac_link_state(ds, dp->index, state); ++ if (ds->ops->phylink_mac_link_state(ds, dp->index, state) < 0) ++ state->link = 0; + } +-EXPORT_SYMBOL_GPL(dsa_port_phylink_mac_link_state); ++EXPORT_SYMBOL_GPL(dsa_port_phylink_mac_pcs_get_state); + + void dsa_port_phylink_mac_config(struct phylink_config *config, + unsigned int mode, +@@ -529,11 +532,11 @@ void dsa_port_phylink_mac_link_down(stru + EXPORT_SYMBOL_GPL(dsa_port_phylink_mac_link_down); + + void dsa_port_phylink_mac_link_up(struct phylink_config *config, +- struct phy_device *phydev, +- unsigned int mode, +- phy_interface_t interface, +- int speed, int duplex, +- bool tx_pause, bool rx_pause) ++ struct phy_device *phydev, ++ unsigned int mode, ++ phy_interface_t interface, ++ int speed, int duplex, ++ bool tx_pause, bool rx_pause) + { + struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); + struct dsa_switch *ds = dp->ds; +@@ -551,7 +554,7 @@ EXPORT_SYMBOL_GPL(dsa_port_phylink_mac_l + + const struct phylink_mac_ops dsa_port_phylink_mac_ops = { + .validate = dsa_port_phylink_validate, +- .mac_link_state = dsa_port_phylink_mac_link_state, ++ .mac_pcs_get_state = dsa_port_phylink_mac_pcs_get_state, + .mac_config = dsa_port_phylink_mac_config, + .mac_an_restart = dsa_port_phylink_mac_an_restart, + .mac_link_down = dsa_port_phylink_mac_link_down, +Index: linux-5.4.284/net/dsa/slave.c +=================================================================== +--- linux-5.4.284.orig/net/dsa/slave.c ++++ linux-5.4.284/net/dsa/slave.c +@@ -1290,10 +1290,10 @@ void dsa_port_phylink_mac_change(struct + } + EXPORT_SYMBOL_GPL(dsa_port_phylink_mac_change); + +-static void dsa_slave_phylink_fixed_state(struct net_device *dev, ++static void dsa_slave_phylink_fixed_state(struct phylink_config *config, + struct phylink_link_state *state) + { +- struct dsa_port *dp = dsa_slave_to_port(dev); ++ struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); + struct dsa_switch *ds = dp->ds; + + /* No need to check that this operation is valid, the callback would +@@ -1332,6 +1332,15 @@ static int dsa_slave_phy_setup(struct ne + dp->pl_config.dev = &slave_dev->dev; + dp->pl_config.type = PHYLINK_NETDEV; + ++ /* The get_fixed_state callback takes precedence over polling the ++ * link GPIO in PHYLINK (see phylink_get_fixed_state). Only set ++ * this if the switch provides such a callback. ++ */ ++ if (ds->ops->phylink_fixed_state) { ++ dp->pl_config.get_fixed_state = dsa_slave_phylink_fixed_state; ++ dp->pl_config.poll_fixed_state = true; ++ } ++ + dp->pl = phylink_create(&dp->pl_config, of_fwnode_handle(port_dn), mode, + &dsa_port_phylink_mac_ops); + if (IS_ERR(dp->pl)) { +@@ -1340,13 +1349,6 @@ static int dsa_slave_phy_setup(struct ne + return PTR_ERR(dp->pl); + } + +- /* Register only if the switch provides such a callback, since this +- * callback takes precedence over polling the link GPIO in PHYLINK +- * (see phylink_get_fixed_state). +- */ +- if (ds->ops->phylink_fixed_state) +- phylink_fixed_state_cb(dp->pl, dsa_slave_phylink_fixed_state); +- + if (ds->ops->get_phy_flags) + phy_flags = ds->ops->get_phy_flags(ds, dp->index); + +Index: linux-5.4.284/drivers/net/phy/sfp-bus.c +=================================================================== +--- linux-5.4.284.orig/drivers/net/phy/sfp-bus.c ++++ linux-5.4.284/drivers/net/phy/sfp-bus.c +@@ -10,12 +10,6 @@ + + #include "sfp.h" + +-struct sfp_quirk { +- const char *vendor; +- const char *part; +- void (*modes)(const struct sfp_eeprom_id *id, unsigned long *modes); +-}; +- + /** + * struct sfp_bus - internal representation of a sfp bus + */ +@@ -38,87 +32,6 @@ struct sfp_bus { + bool started; + }; + +-static void sfp_quirk_2500basex(const struct sfp_eeprom_id *id, +- unsigned long *modes) +-{ +- phylink_set(modes, 2500baseX_Full); +-} +- +-static void sfp_quirk_ubnt_uf_instant(const struct sfp_eeprom_id *id, +- unsigned long *modes) +-{ +- /* Ubiquiti U-Fiber Instant module claims that support all transceiver +- * types including 10G Ethernet which is not truth. So clear all claimed +- * modes and set only one mode which module supports: 1000baseX_Full. +- */ +- phylink_zero(modes); +- phylink_set(modes, 1000baseX_Full); +-} +- +-static const struct sfp_quirk sfp_quirks[] = { +- { +- // Alcatel Lucent G-010S-P can operate at 2500base-X, but +- // incorrectly report 2500MBd NRZ in their EEPROM +- .vendor = "ALCATELLUCENT", +- .part = "G010SP", +- .modes = sfp_quirk_2500basex, +- }, { +- // Alcatel Lucent G-010S-A can operate at 2500base-X, but +- // report 3.2GBd NRZ in their EEPROM +- .vendor = "ALCATELLUCENT", +- .part = "3FE46541AA", +- .modes = sfp_quirk_2500basex, +- }, { +- // Huawei MA5671A can operate at 2500base-X, but report 1.2GBd +- // NRZ in their EEPROM +- .vendor = "HUAWEI", +- .part = "MA5671A", +- .modes = sfp_quirk_2500basex, +- }, { +- .vendor = "UBNT", +- .part = "UF-INSTANT", +- .modes = sfp_quirk_ubnt_uf_instant, +- }, +-}; +- +-static size_t sfp_strlen(const char *str, size_t maxlen) +-{ +- size_t size, i; +- +- /* Trailing characters should be filled with space chars */ +- for (i = 0, size = 0; i < maxlen; i++) +- if (str[i] != ' ') +- size = i + 1; +- +- return size; +-} +- +-static bool sfp_match(const char *qs, const char *str, size_t len) +-{ +- if (!qs) +- return true; +- if (strlen(qs) != len) +- return false; +- return !strncmp(qs, str, len); +-} +- +-static const struct sfp_quirk *sfp_lookup_quirk(const struct sfp_eeprom_id *id) +-{ +- const struct sfp_quirk *q; +- unsigned int i; +- size_t vs, ps; +- +- vs = sfp_strlen(id->base.vendor_name, ARRAY_SIZE(id->base.vendor_name)); +- ps = sfp_strlen(id->base.vendor_pn, ARRAY_SIZE(id->base.vendor_pn)); +- +- for (i = 0, q = sfp_quirks; i < ARRAY_SIZE(sfp_quirks); i++, q++) +- if (sfp_match(q->vendor, id->base.vendor_name, vs) && +- sfp_match(q->part, id->base.vendor_pn, ps)) +- return q; +- +- return NULL; +-} +- + /** + * sfp_parse_port() - Parse the EEPROM base ID, setting the port type + * @bus: a pointer to the &struct sfp_bus structure for the sfp module +@@ -164,7 +77,7 @@ int sfp_parse_port(struct sfp_bus *bus, + port = PORT_TP; + break; + } +- /* fallthrough */ ++ fallthrough; + case SFF8024_CONNECTOR_SG: /* guess */ + case SFF8024_CONNECTOR_HSSDC_II: + case SFF8024_CONNECTOR_NOSEPARATE: +@@ -235,7 +148,6 @@ void sfp_parse_support(struct sfp_bus *b + { + unsigned int br_min, br_nom, br_max; + __ETHTOOL_DECLARE_LINK_MODE_MASK(modes) = { 0, }; +- + /* Decode the bitrate information to MBd */ + br_min = br_nom = br_max = 0; + if (id->base.br_nominal) { +@@ -280,6 +192,12 @@ void sfp_parse_support(struct sfp_bus *b + br_min <= 1300 && br_max >= 1200) + phylink_set(modes, 1000baseX_Full); + ++ /* 100Base-FX, 100Base-LX, 100Base-PX, 100Base-BX10 */ ++ if (id->base.e100_base_fx || id->base.e100_base_lx) ++ phylink_set(modes, 100baseFX_Full); ++ if ((id->base.e_base_px || id->base.e_base_bx10) && br_nom == 100) ++ phylink_set(modes, 100baseFX_Full); ++ + /* For active or passive cables, select the link modes + * based on the bit rates and the cable compliance bytes. + */ +@@ -292,6 +210,7 @@ void sfp_parse_support(struct sfp_bus *b + if (br_min <= 1300 && br_max >= 1200) + phylink_set(modes, 1000baseX_Full); + } ++ + if (id->base.sfp_ct_passive) { + if (id->base.passive.sff8431_app_e) + phylink_set(modes, 10000baseCR_Full); +@@ -316,7 +235,7 @@ void sfp_parse_support(struct sfp_bus *b + break; + case SFF8024_ECC_100GBASE_CR4: + phylink_set(modes, 100000baseCR4_Full); +- /* fallthrough */ ++ fallthrough; + case SFF8024_ECC_25GBASE_CR_S: + case SFF8024_ECC_25GBASE_CR_N: + phylink_set(modes, 25000baseCR_Full); +@@ -352,21 +271,26 @@ void sfp_parse_support(struct sfp_bus *b + * the bitrate to determine supported modes. Some BiDi modules (eg, + * 1310nm/1550nm) are not 1000BASE-BX compliant due to the differing + * wavelengths, so do not set any transceiver bits. ++ * ++ * Do the same for modules supporting 2500BASE-X. Note that some ++ * modules use 2500Mbaud rather than 3100 or 3200Mbaud for ++ * 2500BASE-X, so we allow some slack here. + */ +- if (bitmap_empty(modes, __ETHTOOL_LINK_MODE_MASK_NBITS)) { +- /* If the bit rate allows 1000baseX */ +- if (br_nom && br_min <= 1300 && br_max >= 1200) ++ if (bitmap_empty(modes, __ETHTOOL_LINK_MODE_MASK_NBITS) && br_nom) { ++ if (br_min <= 1300 && br_max >= 1200) + phylink_set(modes, 1000baseX_Full); ++ if (br_min <= 3200 && br_max >= 2500) ++ phylink_set(modes, 2500baseX_Full); + } + +- if (bus->sfp_quirk) ++ if (bus->sfp_quirk && bus->sfp_quirk->modes) + bus->sfp_quirk->modes(id, modes); +- +- bitmap_or(support, support, modes, __ETHTOOL_LINK_MODE_MASK_NBITS); +- ++ ++ linkmode_or(support, support, modes); + phylink_set(support, Autoneg); + phylink_set(support, Pause); + phylink_set(support, Asym_Pause); ++ + } + EXPORT_SYMBOL_GPL(sfp_parse_support); + +@@ -381,13 +305,21 @@ EXPORT_SYMBOL_GPL(sfp_parse_support); + phy_interface_t sfp_select_interface(struct sfp_bus *bus, + unsigned long *link_modes) + { ++ if (phylink_test(link_modes, 25000baseCR_Full) || ++ phylink_test(link_modes, 25000baseKR_Full) || ++ phylink_test(link_modes, 25000baseSR_Full)) ++ return PHY_INTERFACE_MODE_25GBASER; ++ + if (phylink_test(link_modes, 10000baseCR_Full) || + phylink_test(link_modes, 10000baseSR_Full) || + phylink_test(link_modes, 10000baseLR_Full) || + phylink_test(link_modes, 10000baseLRM_Full) || + phylink_test(link_modes, 10000baseER_Full) || + phylink_test(link_modes, 10000baseT_Full)) +- return PHY_INTERFACE_MODE_10GKR; ++ return PHY_INTERFACE_MODE_10GBASER; ++ ++ if (phylink_test(link_modes, 5000baseT_Full)) ++ return PHY_INTERFACE_MODE_5GBASER; + + if (phylink_test(link_modes, 2500baseX_Full)) + return PHY_INTERFACE_MODE_2500BASEX; +@@ -399,6 +331,9 @@ phy_interface_t sfp_select_interface(str + if (phylink_test(link_modes, 1000baseX_Full)) + return PHY_INTERFACE_MODE_1000BASEX; + ++ if (phylink_test(link_modes, 100baseFX_Full)) ++ return PHY_INTERFACE_MODE_100BASEX; ++ + dev_warn(bus->sfp_dev, "Unable to ascertain link mode\n"); + + return PHY_INTERFACE_MODE_NA; +@@ -539,6 +474,26 @@ int sfp_get_module_eeprom(struct sfp_bus + EXPORT_SYMBOL_GPL(sfp_get_module_eeprom); + + /** ++ * sfp_get_module_eeprom_by_page() - Read a page from the SFP module EEPROM ++ * @bus: a pointer to the &struct sfp_bus structure for the sfp module ++ * @page: a &struct ethtool_module_eeprom ++ * @extack: extack for reporting problems ++ * ++ * Read an EEPROM page as specified by the supplied @page. See the ++ * documentation for &struct ethtool_module_eeprom for the page to be read. ++ * ++ * Returns 0 on success or a negative errno number. More error ++ * information might be provided via extack ++ */ ++int sfp_get_module_eeprom_by_page(struct sfp_bus *bus, ++ const struct ethtool_module_eeprom *page, ++ struct netlink_ext_ack *extack) ++{ ++ return bus->socket_ops->module_eeprom_by_page(bus->sfp, page, extack); ++} ++EXPORT_SYMBOL_GPL(sfp_get_module_eeprom_by_page); ++ ++/** + * sfp_upstream_start() - Inform the SFP that the network device is up + * @bus: a pointer to the &struct sfp_bus structure for the sfp module + * +@@ -586,13 +541,15 @@ static void sfp_upstream_clear(struct sf + * the sfp_bus structure, incrementing its reference count. This must + * be put via sfp_bus_put() when done. + * +- * Returns: on success, a pointer to the sfp_bus structure, +- * %NULL if no SFP is specified, +- * on failure, an error pointer value: +- * corresponding to the errors detailed for +- * fwnode_property_get_reference_args(). +- * %-ENOMEM if we failed to allocate the bus. +- * an error from the upstream's connect_phy() method. ++ * Returns: ++ * - on success, a pointer to the sfp_bus structure, ++ * - %NULL if no SFP is specified, ++ * - on failure, an error pointer value: ++ * ++ * - corresponding to the errors detailed for ++ * fwnode_property_get_reference_args(). ++ * - %-ENOMEM if we failed to allocate the bus. ++ * - an error from the upstream's connect_phy() method. + */ + struct sfp_bus *sfp_bus_find_fwnode(struct fwnode_handle *fwnode) + { +@@ -631,13 +588,15 @@ EXPORT_SYMBOL_GPL(sfp_bus_find_fwnode); + * the SFP bus using sfp_register_upstream(). This takes a reference on the + * bus, so it is safe to put the bus after this call. + * +- * Returns: on success, a pointer to the sfp_bus structure, +- * %NULL if no SFP is specified, +- * on failure, an error pointer value: +- * corresponding to the errors detailed for +- * fwnode_property_get_reference_args(). +- * %-ENOMEM if we failed to allocate the bus. +- * an error from the upstream's connect_phy() method. ++ * Returns: ++ * - on success, a pointer to the sfp_bus structure, ++ * - %NULL if no SFP is specified, ++ * - on failure, an error pointer value: ++ * ++ * - corresponding to the errors detailed for ++ * fwnode_property_get_reference_args(). ++ * - %-ENOMEM if we failed to allocate the bus. ++ * - an error from the upstream's connect_phy() method. + */ + int sfp_bus_add_upstream(struct sfp_bus *bus, void *upstream, + const struct sfp_upstream_ops *ops) +@@ -734,12 +693,13 @@ void sfp_link_down(struct sfp_bus *bus) + } + EXPORT_SYMBOL_GPL(sfp_link_down); + +-int sfp_module_insert(struct sfp_bus *bus, const struct sfp_eeprom_id *id) ++int sfp_module_insert(struct sfp_bus *bus, const struct sfp_eeprom_id *id, ++ const struct sfp_quirk *quirk) + { + const struct sfp_upstream_ops *ops = sfp_get_upstream_ops(bus); + int ret = 0; + +- bus->sfp_quirk = sfp_lookup_quirk(id); ++ bus->sfp_quirk = quirk; + + if (ops && ops->module_insert) + ret = ops->module_insert(bus->upstream, id); +Index: linux-5.4.284/drivers/net/phy/sfp.c +=================================================================== +--- linux-5.4.284.orig/drivers/net/phy/sfp.c ++++ linux-5.4.284/drivers/net/phy/sfp.c +@@ -1,6 +1,7 @@ + // SPDX-License-Identifier: GPL-2.0 + #include + #include ++#include + #include + #include + #include +@@ -207,6 +208,12 @@ static const enum gpiod_flags gpio_flags + */ + #define SFP_PHY_ADDR 22 + ++/* SFP_EEPROM_BLOCK_SIZE is the size of data chunk to read the EEPROM ++ * at a time. Some SFP modules and also some Linux I2C drivers do not like ++ * reads longer than 16 bytes. ++ */ ++#define SFP_EEPROM_BLOCK_SIZE 16 ++ + struct sff_data { + unsigned int gpios; + bool (*module_supported)(const struct sfp_eeprom_id *id); +@@ -233,6 +240,7 @@ struct sfp { + bool need_poll; + + struct mutex st_mutex; /* Protects state */ ++ unsigned int state_hw_mask; + unsigned int state_soft_mask; + unsigned int state; + struct delayed_work poll; +@@ -249,6 +257,9 @@ struct sfp { + struct sfp_eeprom_id id; + unsigned int module_power_mW; + unsigned int module_t_start_up; ++ bool tx_fault_ignore; ++ ++ const struct sfp_quirk *quirk; + + #if IS_ENABLED(CONFIG_HWMON) + struct sfp_diag diag; +@@ -258,6 +269,9 @@ struct sfp { + char *hwmon_name; + #endif + ++#if IS_ENABLED(CONFIG_DEBUG_FS) ++ struct dentry *debugfs_dir; ++#endif + }; + + static bool sff_module_supported(const struct sfp_eeprom_id *id) +@@ -303,6 +317,135 @@ static const struct of_device_id sfp_of_ + }; + MODULE_DEVICE_TABLE(of, sfp_of_match); + ++static void sfp_fixup_long_startup(struct sfp *sfp) ++{ ++ sfp->module_t_start_up = T_START_UP_BAD_GPON; ++} ++ ++static void sfp_fixup_ignore_tx_fault(struct sfp *sfp) ++{ ++ sfp->tx_fault_ignore = true; ++} ++ ++static void sfp_fixup_halny_gsfp(struct sfp *sfp) ++{ ++ /* Ignore the TX_FAULT and LOS signals on this module. ++ * these are possibly used for other purposes on this ++ * module, e.g. a serial port. ++ */ ++ sfp->state_hw_mask &= ~(SFP_F_TX_FAULT | SFP_F_LOS); ++} ++ ++static void sfp_quirk_2500basex(const struct sfp_eeprom_id *id, ++ unsigned long *modes) ++{ ++ linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseX_Full_BIT, modes); ++} ++ ++static void sfp_quirk_ubnt_uf_instant(const struct sfp_eeprom_id *id, ++ unsigned long *modes) ++{ ++ /* Ubiquiti U-Fiber Instant module claims that support all transceiver ++ * types including 10G Ethernet which is not truth. So clear all claimed ++ * modes and set only one mode which module supports: 1000baseX_Full. ++ */ ++ linkmode_zero(modes); ++ linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT, modes); ++} ++ ++static const struct sfp_quirk sfp_quirks[] = { ++ { ++ // Alcatel Lucent G-010S-P can operate at 2500base-X, but ++ // incorrectly report 2500MBd NRZ in their EEPROM ++ .vendor = "ALCATELLUCENT", ++ .part = "G010SP", ++ .modes = sfp_quirk_2500basex, ++ }, { ++ // Alcatel Lucent G-010S-A can operate at 2500base-X, but ++ // report 3.2GBd NRZ in their EEPROM ++ .vendor = "ALCATELLUCENT", ++ .part = "3FE46541AA", ++ .modes = sfp_quirk_2500basex, ++ .fixup = sfp_fixup_long_startup, ++ }, { ++ // Fiberstore GPON-ONU-34-20BI can operate at 2500base-X, but report 1.2GBd ++ // NRZ in their EEPROM ++ .vendor = "FS", ++ .part = "GPON-ONU-34-20BI", ++ .modes = sfp_quirk_2500basex, ++ .fixup = sfp_fixup_ignore_tx_fault, ++ }, { ++ .vendor = "HALNy", ++ .part = "HL-GSFP", ++ .fixup = sfp_fixup_halny_gsfp, ++ }, { ++ .vendor = "HG GENUINE", ++ .part = "MXPD-483II", ++ .modes = sfp_quirk_2500basex, ++ }, { ++ // Huawei MA5671A can operate at 2500base-X, but report 1.2GBd ++ // NRZ in their EEPROM ++ .vendor = "HUAWEI", ++ .part = "MA5671A", ++ .modes = sfp_quirk_2500basex, ++ .fixup = sfp_fixup_ignore_tx_fault, ++ }, { ++ // OEM SFP-GE-T is 1000Base-T module ++ .vendor = "OEM", ++ .part = "SFP-GE-T", ++ .fixup = sfp_fixup_ignore_tx_fault, ++ }, { ++ // Lantech 8330-262D-E can operate at 2500base-X, but ++ // incorrectly report 2500MBd NRZ in their EEPROM ++ .vendor = "Lantech", ++ .part = "8330-262D-E", ++ .modes = sfp_quirk_2500basex, ++ }, { ++ .vendor = "UBNT", ++ .part = "UF-INSTANT", ++ .modes = sfp_quirk_ubnt_uf_instant, ++ } ++}; ++ ++static size_t sfp_strlen(const char *str, size_t maxlen) ++{ ++ size_t size, i; ++ ++ /* Trailing characters should be filled with space chars, but ++ * some manufacturers can't read SFF-8472 and use NUL. ++ */ ++ for (i = 0, size = 0; i < maxlen; i++) ++ if (str[i] != ' ' && str[i] != '\0') ++ size = i + 1; ++ ++ return size; ++} ++ ++static bool sfp_match(const char *qs, const char *str, size_t len) ++{ ++ if (!qs) ++ return true; ++ if (strlen(qs) != len) ++ return false; ++ return !strncmp(qs, str, len); ++} ++ ++static const struct sfp_quirk *sfp_lookup_quirk(const struct sfp_eeprom_id *id) ++{ ++ const struct sfp_quirk *q; ++ unsigned int i; ++ size_t vs, ps; ++ vs = sfp_strlen(id->base.vendor_name, ARRAY_SIZE(id->base.vendor_name)); ++ ps = sfp_strlen(id->base.vendor_pn, ARRAY_SIZE(id->base.vendor_pn)); ++ ++ for (i = 0, q = sfp_quirks; i < ARRAY_SIZE(sfp_quirks); i++, q++) ++ if (sfp_match(q->vendor, id->base.vendor_name, vs) && ++ sfp_match(q->part, id->base.vendor_pn, ps)) ++ return q; ++ ++ return NULL; ++} ++ + static unsigned long poll_jiffies; + + static unsigned int sfp_gpio_get_state(struct sfp *sfp) +@@ -457,13 +600,20 @@ static unsigned int sfp_soft_get_state(s + { + unsigned int state = 0; + u8 status; ++ int ret; + +- if (sfp_read(sfp, true, SFP_STATUS, &status, sizeof(status)) == +- sizeof(status)) { ++ ret = sfp_read(sfp, true, SFP_STATUS, &status, sizeof(status)); ++ if (ret == sizeof(status)) { + if (status & SFP_STATUS_RX_LOS) + state |= SFP_F_LOS; + if (status & SFP_STATUS_TX_FAULT) + state |= SFP_F_TX_FAULT; ++ } else { ++ dev_err_ratelimited(sfp->dev, ++ "failed to read SFP soft status: %d\n", ++ ret); ++ /* Preserve the current state */ ++ state = sfp->state; + } + + return state & sfp->state_soft_mask; +@@ -487,17 +637,18 @@ static void sfp_soft_set_state(struct sf + static void sfp_soft_start_poll(struct sfp *sfp) + { + const struct sfp_eeprom_id *id = &sfp->id; ++ unsigned int mask = 0; + + sfp->state_soft_mask = 0; +- if (id->ext.enhopts & SFP_ENHOPTS_SOFT_TX_DISABLE && +- !sfp->gpio[GPIO_TX_DISABLE]) +- sfp->state_soft_mask |= SFP_F_TX_DISABLE; +- if (id->ext.enhopts & SFP_ENHOPTS_SOFT_TX_FAULT && +- !sfp->gpio[GPIO_TX_FAULT]) +- sfp->state_soft_mask |= SFP_F_TX_FAULT; +- if (id->ext.enhopts & SFP_ENHOPTS_SOFT_RX_LOS && +- !sfp->gpio[GPIO_LOS]) +- sfp->state_soft_mask |= SFP_F_LOS; ++ if (id->ext.enhopts & SFP_ENHOPTS_SOFT_TX_DISABLE) ++ mask |= SFP_F_TX_DISABLE; ++ if (id->ext.enhopts & SFP_ENHOPTS_SOFT_TX_FAULT) ++ mask |= SFP_F_TX_FAULT; ++ if (id->ext.enhopts & SFP_ENHOPTS_SOFT_RX_LOS) ++ mask |= SFP_F_LOS; ++ ++ // Poll the soft state for hardware pins we want to ignore ++ sfp->state_soft_mask = ~sfp->state_hw_mask & mask; + + if (sfp->state_soft_mask & (SFP_F_LOS | SFP_F_TX_FAULT) && + !sfp->need_poll) +@@ -511,10 +662,11 @@ static void sfp_soft_stop_poll(struct sf + + static unsigned int sfp_get_state(struct sfp *sfp) + { +- unsigned int state = sfp->get_state(sfp); ++ unsigned int soft = sfp->state_soft_mask & (SFP_F_LOS | SFP_F_TX_FAULT); ++ unsigned int state; + +- if (state & SFP_F_PRESENT && +- sfp->state_soft_mask & (SFP_F_LOS | SFP_F_TX_FAULT)) ++ state = sfp->get_state(sfp) & sfp->state_hw_mask; ++ if (state & SFP_F_PRESENT && soft) + state |= sfp_soft_get_state(sfp); + + return state; +@@ -560,7 +712,7 @@ static umode_t sfp_hwmon_is_visible(cons + case hwmon_temp_crit: + if (!(sfp->id.ext.enhopts & SFP_ENHOPTS_ALARMWARN)) + return 0; +- /* fall through */ ++ fallthrough; + case hwmon_temp_input: + case hwmon_temp_label: + return 0444; +@@ -579,7 +731,7 @@ static umode_t sfp_hwmon_is_visible(cons + case hwmon_in_crit: + if (!(sfp->id.ext.enhopts & SFP_ENHOPTS_ALARMWARN)) + return 0; +- /* fall through */ ++ fallthrough; + case hwmon_in_input: + case hwmon_in_label: + return 0444; +@@ -598,7 +750,7 @@ static umode_t sfp_hwmon_is_visible(cons + case hwmon_curr_crit: + if (!(sfp->id.ext.enhopts & SFP_ENHOPTS_ALARMWARN)) + return 0; +- /* fall through */ ++ fallthrough; + case hwmon_curr_input: + case hwmon_curr_label: + return 0444; +@@ -626,7 +778,7 @@ static umode_t sfp_hwmon_is_visible(cons + case hwmon_power_crit: + if (!(sfp->id.ext.enhopts & SFP_ENHOPTS_ALARMWARN)) + return 0; +- /* fall through */ ++ fallthrough; + case hwmon_power_input: + case hwmon_power_label: + return 0444; +@@ -1402,6 +1554,54 @@ static void sfp_module_tx_enable(struct + sfp_set_state(sfp, sfp->state); + } + ++#if IS_ENABLED(CONFIG_DEBUG_FS) ++static int sfp_debug_state_show(struct seq_file *s, void *data) ++{ ++ struct sfp *sfp = s->private; ++ ++ seq_printf(s, "Module state: %s\n", ++ mod_state_to_str(sfp->sm_mod_state)); ++ seq_printf(s, "Module probe attempts: %d %d\n", ++ R_PROBE_RETRY_INIT - sfp->sm_mod_tries_init, ++ R_PROBE_RETRY_SLOW - sfp->sm_mod_tries); ++ seq_printf(s, "Device state: %s\n", ++ dev_state_to_str(sfp->sm_dev_state)); ++ seq_printf(s, "Main state: %s\n", ++ sm_state_to_str(sfp->sm_state)); ++ seq_printf(s, "Fault recovery remaining retries: %d\n", ++ sfp->sm_fault_retries); ++ seq_printf(s, "PHY probe remaining retries: %d\n", ++ sfp->sm_phy_retries); ++ seq_printf(s, "moddef0: %d\n", !!(sfp->state & SFP_F_PRESENT)); ++ seq_printf(s, "rx_los: %d\n", !!(sfp->state & SFP_F_LOS)); ++ seq_printf(s, "tx_fault: %d\n", !!(sfp->state & SFP_F_TX_FAULT)); ++ seq_printf(s, "tx_disable: %d\n", !!(sfp->state & SFP_F_TX_DISABLE)); ++ return 0; ++} ++DEFINE_SHOW_ATTRIBUTE(sfp_debug_state); ++ ++static void sfp_debugfs_init(struct sfp *sfp) ++{ ++ sfp->debugfs_dir = debugfs_create_dir(dev_name(sfp->dev), NULL); ++ ++ debugfs_create_file("state", 0600, sfp->debugfs_dir, sfp, ++ &sfp_debug_state_fops); ++} ++ ++static void sfp_debugfs_exit(struct sfp *sfp) ++{ ++ debugfs_remove_recursive(sfp->debugfs_dir); ++} ++#else ++static void sfp_debugfs_init(struct sfp *sfp) ++{ ++} ++ ++static void sfp_debugfs_exit(struct sfp *sfp) ++{ ++} ++#endif ++ + static void sfp_module_tx_fault_reset(struct sfp *sfp) + { + unsigned int state = sfp->state; +@@ -1582,17 +1782,20 @@ static int sfp_sm_probe_for_phy(struct s + static int sfp_module_parse_power(struct sfp *sfp) + { + u32 power_mW = 1000; ++ bool supports_a2; + + if (sfp->id.ext.options & cpu_to_be16(SFP_OPTIONS_POWER_DECL)) + power_mW = 1500; + if (sfp->id.ext.options & cpu_to_be16(SFP_OPTIONS_HIGH_POWER_LEVEL)) + power_mW = 2000; + ++ supports_a2 = sfp->id.ext.sff8472_compliance != ++ SFP_SFF8472_COMPLIANCE_NONE || ++ sfp->id.ext.diagmon & SFP_DIAGMON_DDM; ++ + if (power_mW > sfp->max_power_mW) { + /* Module power specification exceeds the allowed maximum. */ +- if (sfp->id.ext.sff8472_compliance == +- SFP_SFF8472_COMPLIANCE_NONE && +- !(sfp->id.ext.diagmon & SFP_DIAGMON_DDM)) { ++ if (!supports_a2) { + /* The module appears not to implement bus address + * 0xa2, so assume that the module powers up in the + * indicated mode. +@@ -1609,13 +1812,27 @@ static int sfp_module_parse_power(struct + } + } + ++ if (power_mW <= 1000) { ++ /* Modules below 1W do not require a power change sequence */ ++ sfp->module_power_mW = power_mW; ++ return 0; ++ } ++ ++ if (!supports_a2) { ++ /* The module power level is below the host maximum and the ++ * module appears not to implement bus address 0xa2, so assume ++ * that the module powers up in the indicated mode. ++ */ ++ return 0; ++ } ++ + /* If the module requires a higher power mode, but also requires + * an address change sequence, warn the user that the module may + * not be functional. + */ +- if (sfp->id.ext.diagmon & SFP_DIAGMON_ADDRMODE && power_mW > 1000) { ++ if (sfp->id.ext.diagmon & SFP_DIAGMON_ADDRMODE) { + dev_warn(sfp->dev, +- "Address Change Sequence not supported but module requies %u.%uW, module may not be functional\n", ++ "Address Change Sequence not supported but module requires %u.%uW, module may not be functional\n", + power_mW / 1000, (power_mW / 100) % 10); + return 0; + } +@@ -1688,19 +1905,48 @@ static bool sfp_id_needs_byte_io(struct + return true; + } + ++static int sfp_cotsworks_fixup_check(struct sfp *sfp, struct sfp_eeprom_id *id) ++{ ++ u8 check; ++ int err; ++ ++ if (id->base.phys_id != SFF8024_ID_SFF_8472 || ++ id->base.phys_ext_id != SFP_PHYS_EXT_ID_SFP || ++ id->base.connector != SFF8024_CONNECTOR_LC) { ++ dev_warn(sfp->dev, "Rewriting fiber module EEPROM with corrected values\n"); ++ id->base.phys_id = SFF8024_ID_SFF_8472; ++ id->base.phys_ext_id = SFP_PHYS_EXT_ID_SFP; ++ id->base.connector = SFF8024_CONNECTOR_LC; ++ err = sfp_write(sfp, false, SFP_PHYS_ID, &id->base, 3); ++ if (err != 3) { ++ dev_err(sfp->dev, "Failed to rewrite module EEPROM: %d\n", err); ++ return err; ++ } ++ ++ /* Cotsworks modules have been found to require a delay between write operations. */ ++ mdelay(50); ++ ++ /* Update base structure checksum */ ++ check = sfp_check(&id->base, sizeof(id->base) - 1); ++ err = sfp_write(sfp, false, SFP_CC_BASE, &check, 1); ++ if (err != 1) { ++ dev_err(sfp->dev, "Failed to update base structure checksum in fiber module EEPROM: %d\n", err); ++ return err; ++ } ++ } ++ return 0; ++} ++ + static int sfp_sm_mod_probe(struct sfp *sfp, bool report) + { + /* SFP module inserted - read I2C data */ + struct sfp_eeprom_id id; ++ bool cotsworks_sfbg; + bool cotsworks; + u8 check; + int ret; + +- /* Some SFP modules and also some Linux I2C drivers do not like reads +- * longer than 16 bytes, so read the EEPROM in chunks of 16 bytes at +- * a time. +- */ +- sfp->i2c_block_size = 16; ++ sfp->i2c_block_size = SFP_EEPROM_BLOCK_SIZE; + + ret = sfp_read(sfp, false, 0, &id.base, sizeof(id.base)); + if (ret < 0) { +@@ -1746,6 +1992,17 @@ static int sfp_sm_mod_probe(struct sfp * + * serial number and date code. + */ + cotsworks = !memcmp(id.base.vendor_name, "COTSWORKS ", 16); ++ cotsworks_sfbg = !memcmp(id.base.vendor_pn, "SFBG", 4); ++ ++ /* Cotsworks SFF module EEPROM do not always have valid phys_id, ++ * phys_ext_id, and connector bytes. Rewrite SFF EEPROM bytes if ++ * Cotsworks PN matches and bytes are not correct. ++ */ ++ if (cotsworks && cotsworks_sfbg) { ++ ret = sfp_cotsworks_fixup_check(sfp, &id); ++ if (ret < 0) ++ return ret; ++ } + + /* Validate the checksum over the base structure */ + check = sfp_check(&id.base, sizeof(id.base) - 1); +@@ -1819,12 +2076,24 @@ static int sfp_sm_mod_probe(struct sfp * + if (ret < 0) + return ret; + +- if (!memcmp(id.base.vendor_name, "ALCATELLUCENT ", 16) && +- !memcmp(id.base.vendor_pn, "3FE46541AA ", 16)) +- sfp->module_t_start_up = T_START_UP_BAD_GPON; +- else +- sfp->module_t_start_up = T_START_UP; ++ /* Initialise state bits to use from hardware */ ++ sfp->state_hw_mask = SFP_F_PRESENT; ++ if (sfp->gpio[GPIO_TX_DISABLE]) ++ sfp->state_hw_mask |= SFP_F_TX_DISABLE; ++ if (sfp->gpio[GPIO_TX_FAULT]) ++ sfp->state_hw_mask |= SFP_F_TX_FAULT; ++ if (sfp->gpio[GPIO_LOS]) ++ sfp->state_hw_mask |= SFP_F_LOS; ++ ++ sfp->module_t_start_up = T_START_UP; ++ ++ sfp->tx_fault_ignore = false; + ++ sfp->quirk = sfp_lookup_quirk(&id); ++ ++ if (sfp->quirk && sfp->quirk->fixup){ ++ sfp->quirk->fixup(sfp); ++ } + return 0; + } + +@@ -1929,14 +2198,15 @@ static void sfp_sm_module(struct sfp *sf + dev_warn(sfp->dev, "hwmon probe failed: %d\n", err); + + sfp_sm_mod_next(sfp, SFP_MOD_WAITDEV, 0); +- /* fall through */ ++ fallthrough; + case SFP_MOD_WAITDEV: + /* Ensure that the device is attached before proceeding */ + if (sfp->sm_dev_state < SFP_DEV_DOWN) + break; +- ++ + /* Report the module insertion to the upstream device */ +- err = sfp_module_insert(sfp->sfp_bus, &sfp->id); ++ err = sfp_module_insert(sfp->sfp_bus, &sfp->id, ++ sfp->quirk); + if (err < 0) { + sfp_sm_mod_next(sfp, SFP_MOD_ERROR, 0); + break; +@@ -1947,7 +2217,7 @@ static void sfp_sm_module(struct sfp *sf + goto insert; + + sfp_sm_mod_next(sfp, SFP_MOD_HPOWER, 0); +- /* fall through */ ++ fallthrough; + case SFP_MOD_HPOWER: + /* Enable high power mode */ + err = sfp_sm_mod_hpower(sfp, true); +@@ -2050,11 +2320,12 @@ static void sfp_sm_main(struct sfp *sfp, + + case SFP_S_INIT: + if (event == SFP_E_TIMEOUT && sfp->state & SFP_F_TX_FAULT) { +- /* TX_FAULT is still asserted after t_init or ++ /* TX_FAULT is still asserted after t_init + * or t_start_up, so assume there is a fault. + */ + sfp_sm_fault(sfp, SFP_S_INIT_TX_FAULT, +- sfp->sm_fault_retries == N_FAULT_INIT); ++ !sfp->tx_fault_ignore && ++ (sfp->sm_fault_retries == N_FAULT_INIT)); + } else if (event == SFP_E_TIMEOUT || event == SFP_E_TX_CLEAR) { + init_done: + sfp->sm_phy_retries = R_PHY_RETRY; +@@ -2227,6 +2498,30 @@ static int sfp_module_eeprom(struct sfp + return 0; + } + ++static int sfp_module_eeprom_by_page(struct sfp *sfp, ++ const struct ethtool_module_eeprom *page, ++ struct netlink_ext_ack *extack) ++{ ++ if (page->bank) { ++ NL_SET_ERR_MSG(extack, "Banks not supported"); ++ return -EOPNOTSUPP; ++ } ++ ++ if (page->page) { ++ NL_SET_ERR_MSG(extack, "Only page 0 supported"); ++ return -EOPNOTSUPP; ++ } ++ ++ if (page->i2c_address != 0x50 && ++ page->i2c_address != 0x51) { ++ NL_SET_ERR_MSG(extack, "Only address 0x50 and 0x51 supported"); ++ return -EOPNOTSUPP; ++ } ++ ++ return sfp_read(sfp, page->i2c_address == 0x51, page->offset, ++ page->data, page->length); ++}; ++ + static const struct sfp_socket_ops sfp_module_ops = { + .attach = sfp_attach, + .detach = sfp_detach, +@@ -2234,6 +2529,7 @@ static const struct sfp_socket_ops sfp_m + .stop = sfp_stop, + .module_info = sfp_module_info, + .module_eeprom = sfp_module_eeprom, ++ .module_eeprom_by_page = sfp_module_eeprom_by_page, + }; + + static void sfp_timeout(struct work_struct *work) +@@ -2252,7 +2548,12 @@ static void sfp_check_state(struct sfp * + mutex_lock(&sfp->st_mutex); + state = sfp_get_state(sfp); + changed = state ^ sfp->state; +- changed &= SFP_F_PRESENT | SFP_F_LOS | SFP_F_TX_FAULT; ++ if (sfp->tx_fault_ignore) { ++ changed &= SFP_F_PRESENT | SFP_F_LOS; ++ state &= ~SFP_F_TX_FAULT; ++ } else { ++ changed &= SFP_F_PRESENT | SFP_F_LOS | SFP_F_TX_FAULT; ++ } + + for (i = 0; i < GPIO_MAX; i++) + if (changed & BIT(i)) +@@ -2307,6 +2608,7 @@ static struct sfp *sfp_alloc(struct devi + return ERR_PTR(-ENOMEM); + + sfp->dev = dev; ++ sfp->i2c_block_size = SFP_EEPROM_BLOCK_SIZE; + + mutex_init(&sfp->sm_mutex); + mutex_init(&sfp->st_mutex); +@@ -2339,6 +2641,7 @@ static int sfp_probe(struct platform_dev + { + const struct sff_data *sff; + struct i2c_adapter *i2c; ++ char *sfp_irq_name; + struct sfp *sfp; + int err, i; + +@@ -2409,6 +2712,8 @@ static int sfp_probe(struct platform_dev + return PTR_ERR(sfp->gpio[i]); + } + ++ sfp->state_hw_mask = SFP_F_PRESENT; ++ + sfp->get_state = sfp_gpio_get_state; + sfp->set_state = sfp_gpio_set_state; + +@@ -2451,12 +2756,19 @@ static int sfp_probe(struct platform_dev + continue; + } + ++ sfp_irq_name = devm_kasprintf(sfp->dev, GFP_KERNEL, ++ "%s-%s", dev_name(sfp->dev), ++ gpio_of_names[i]); ++ ++ if (!sfp_irq_name) ++ return -ENOMEM; ++ + err = devm_request_threaded_irq(sfp->dev, sfp->gpio_irq[i], + NULL, sfp_irq, + IRQF_ONESHOT | + IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING, +- dev_name(sfp->dev), sfp); ++ sfp_irq_name, sfp); + if (err) { + sfp->gpio_irq[i] = 0; + sfp->need_poll = true; +@@ -2479,6 +2791,8 @@ static int sfp_probe(struct platform_dev + if (!sfp->sfp_bus) + return -ENOMEM; + ++ sfp_debugfs_init(sfp); ++ + return 0; + } + +@@ -2486,6 +2800,7 @@ static int sfp_remove(struct platform_de + { + struct sfp *sfp = platform_get_drvdata(pdev); + ++ sfp_debugfs_exit(sfp); + sfp_unregister_socket(sfp->sfp_bus); + + rtnl_lock(); +Index: linux-5.4.284/drivers/net/phy/sfp.h +=================================================================== +--- linux-5.4.284.orig/drivers/net/phy/sfp.h ++++ linux-5.4.284/drivers/net/phy/sfp.h +@@ -6,6 +6,13 @@ + + struct sfp; + ++struct sfp_quirk { ++ const char *vendor; ++ const char *part; ++ void (*modes)(const struct sfp_eeprom_id *id, unsigned long *modes); ++ void (*fixup)(struct sfp *sfp); ++}; ++ + struct sfp_socket_ops { + void (*attach)(struct sfp *sfp); + void (*detach)(struct sfp *sfp); +@@ -14,13 +21,17 @@ struct sfp_socket_ops { + int (*module_info)(struct sfp *sfp, struct ethtool_modinfo *modinfo); + int (*module_eeprom)(struct sfp *sfp, struct ethtool_eeprom *ee, + u8 *data); ++ int (*module_eeprom_by_page)(struct sfp *sfp, ++ const struct ethtool_module_eeprom *page, ++ struct netlink_ext_ack *extack); + }; + + int sfp_add_phy(struct sfp_bus *bus, struct phy_device *phydev); + void sfp_remove_phy(struct sfp_bus *bus); + void sfp_link_up(struct sfp_bus *bus); + void sfp_link_down(struct sfp_bus *bus); +-int sfp_module_insert(struct sfp_bus *bus, const struct sfp_eeprom_id *id); ++int sfp_module_insert(struct sfp_bus *bus, const struct sfp_eeprom_id *id, ++ const struct sfp_quirk *quirk); + void sfp_module_remove(struct sfp_bus *bus); + int sfp_module_start(struct sfp_bus *bus); + void sfp_module_stop(struct sfp_bus *bus); +Index: linux-5.4.284/include/linux/ethtool.h +=================================================================== +--- linux-5.4.284.orig/include/linux/ethtool.h ++++ linux-5.4.284/include/linux/ethtool.h +@@ -416,6 +416,15 @@ struct ethtool_rx_flow_spec_input { + u32 rss_ctx; + }; + ++struct ethtool_module_eeprom { ++ u32 offset; ++ u32 length; ++ u8 page; ++ u8 bank; ++ u8 i2c_address; ++ u8 *data; ++}; ++ + struct ethtool_rx_flow_rule * + ethtool_rx_flow_rule_create(const struct ethtool_rx_flow_spec_input *input); + void ethtool_rx_flow_rule_destroy(struct ethtool_rx_flow_rule *rule); +Index: linux-5.4.284/include/uapi/linux/ethtool.h +=================================================================== +--- linux-5.4.284.orig/include/uapi/linux/ethtool.h ++++ linux-5.4.284/include/uapi/linux/ethtool.h +@@ -26,6 +26,14 @@ + * have the same layout for 32-bit and 64-bit userland. + */ + ++/* Note on reserved space. ++ * Reserved fields must not be accessed directly by user space because ++ * they may be replaced by a different field in the future. They must ++ * be initialized to zero before making the request, e.g. via memset ++ * of the entire structure or implicitly by not being set in a structure ++ * initializer. ++ */ ++ + /** + * struct ethtool_cmd - DEPRECATED, link control and status + * This structure is DEPRECATED, please use struct ethtool_link_settings. +@@ -67,6 +75,7 @@ + * and other link features that the link partner advertised + * through autonegotiation; 0 if unknown or not applicable. + * Read-only. ++ * @reserved: Reserved for future use; see the note on reserved space. + * + * The link speed in Mbps is split between @speed and @speed_hi. Use + * the ethtool_cmd_speed() and ethtool_cmd_speed_set() functions to +@@ -155,6 +164,7 @@ static inline __u32 ethtool_cmd_speed(co + * @bus_info: Device bus address. This should match the dev_name() + * string for the underlying bus device, if there is one. May be + * an empty string. ++ * @reserved2: Reserved for future use; see the note on reserved space. + * @n_priv_flags: Number of flags valid for %ETHTOOL_GPFLAGS and + * %ETHTOOL_SPFLAGS commands; also the number of strings in the + * %ETH_SS_PRIV_FLAGS set +@@ -356,6 +366,7 @@ struct ethtool_eeprom { + * @tx_lpi_timer: Time in microseconds the interface delays prior to asserting + * its tx lpi (after reaching 'idle' state). Effective only when eee + * was negotiated and tx_lpi_enabled was set. ++ * @reserved: Reserved for future use; see the note on reserved space. + */ + struct ethtool_eee { + __u32 cmd; +@@ -374,6 +385,7 @@ struct ethtool_eee { + * @cmd: %ETHTOOL_GMODULEINFO + * @type: Standard the module information conforms to %ETH_MODULE_SFF_xxxx + * @eeprom_len: Length of the eeprom ++ * @reserved: Reserved for future use; see the note on reserved space. + * + * This structure is used to return the information to + * properly size memory for a subsequent call to %ETHTOOL_GMODULEEEPROM. +@@ -579,6 +591,64 @@ struct ethtool_pauseparam { + __u32 tx_pause; + }; + ++/* Link extended state */ ++enum ethtool_link_ext_state { ++ ETHTOOL_LINK_EXT_STATE_AUTONEG, ++ ETHTOOL_LINK_EXT_STATE_LINK_TRAINING_FAILURE, ++ ETHTOOL_LINK_EXT_STATE_LINK_LOGICAL_MISMATCH, ++ ETHTOOL_LINK_EXT_STATE_BAD_SIGNAL_INTEGRITY, ++ ETHTOOL_LINK_EXT_STATE_NO_CABLE, ++ ETHTOOL_LINK_EXT_STATE_CABLE_ISSUE, ++ ETHTOOL_LINK_EXT_STATE_EEPROM_ISSUE, ++ ETHTOOL_LINK_EXT_STATE_CALIBRATION_FAILURE, ++ ETHTOOL_LINK_EXT_STATE_POWER_BUDGET_EXCEEDED, ++ ETHTOOL_LINK_EXT_STATE_OVERHEAT, ++}; ++ ++/* More information in addition to ETHTOOL_LINK_EXT_STATE_AUTONEG. */ ++enum ethtool_link_ext_substate_autoneg { ++ ETHTOOL_LINK_EXT_SUBSTATE_AN_NO_PARTNER_DETECTED = 1, ++ ETHTOOL_LINK_EXT_SUBSTATE_AN_ACK_NOT_RECEIVED, ++ ETHTOOL_LINK_EXT_SUBSTATE_AN_NEXT_PAGE_EXCHANGE_FAILED, ++ ETHTOOL_LINK_EXT_SUBSTATE_AN_NO_PARTNER_DETECTED_FORCE_MODE, ++ ETHTOOL_LINK_EXT_SUBSTATE_AN_FEC_MISMATCH_DURING_OVERRIDE, ++ ETHTOOL_LINK_EXT_SUBSTATE_AN_NO_HCD, ++}; ++ ++/* More information in addition to ETHTOOL_LINK_EXT_STATE_LINK_TRAINING_FAILURE. ++ */ ++enum ethtool_link_ext_substate_link_training { ++ ETHTOOL_LINK_EXT_SUBSTATE_LT_KR_FRAME_LOCK_NOT_ACQUIRED = 1, ++ ETHTOOL_LINK_EXT_SUBSTATE_LT_KR_LINK_INHIBIT_TIMEOUT, ++ ETHTOOL_LINK_EXT_SUBSTATE_LT_KR_LINK_PARTNER_DID_NOT_SET_RECEIVER_READY, ++ ETHTOOL_LINK_EXT_SUBSTATE_LT_REMOTE_FAULT, ++}; ++ ++/* More information in addition to ETHTOOL_LINK_EXT_STATE_LINK_LOGICAL_MISMATCH. ++ */ ++enum ethtool_link_ext_substate_link_logical_mismatch { ++ ETHTOOL_LINK_EXT_SUBSTATE_LLM_PCS_DID_NOT_ACQUIRE_BLOCK_LOCK = 1, ++ ETHTOOL_LINK_EXT_SUBSTATE_LLM_PCS_DID_NOT_ACQUIRE_AM_LOCK, ++ ETHTOOL_LINK_EXT_SUBSTATE_LLM_PCS_DID_NOT_GET_ALIGN_STATUS, ++ ETHTOOL_LINK_EXT_SUBSTATE_LLM_FC_FEC_IS_NOT_LOCKED, ++ ETHTOOL_LINK_EXT_SUBSTATE_LLM_RS_FEC_IS_NOT_LOCKED, ++}; ++ ++/* More information in addition to ETHTOOL_LINK_EXT_STATE_BAD_SIGNAL_INTEGRITY. ++ */ ++enum ethtool_link_ext_substate_bad_signal_integrity { ++ ETHTOOL_LINK_EXT_SUBSTATE_BSI_LARGE_NUMBER_OF_PHYSICAL_ERRORS = 1, ++ ETHTOOL_LINK_EXT_SUBSTATE_BSI_UNSUPPORTED_RATE, ++ ETHTOOL_LINK_EXT_SUBSTATE_BSI_SERDES_REFERENCE_CLOCK_LOST, ++ ETHTOOL_LINK_EXT_SUBSTATE_BSI_SERDES_ALOS, ++}; ++ ++/* More information in addition to ETHTOOL_LINK_EXT_STATE_CABLE_ISSUE. */ ++enum ethtool_link_ext_substate_cable_issue { ++ ETHTOOL_LINK_EXT_SUBSTATE_CI_UNSUPPORTED_CABLE = 1, ++ ETHTOOL_LINK_EXT_SUBSTATE_CI_CABLE_TEST_FAILURE, ++}; ++ + #define ETH_GSTRING_LEN 32 + + /** +@@ -591,8 +661,23 @@ struct ethtool_pauseparam { + * now deprecated + * @ETH_SS_FEATURES: Device feature names + * @ETH_SS_RSS_HASH_FUNCS: RSS hush function names ++ * @ETH_SS_TUNABLES: tunable names + * @ETH_SS_PHY_STATS: Statistic names, for use with %ETHTOOL_GPHYSTATS + * @ETH_SS_PHY_TUNABLES: PHY tunable names ++ * @ETH_SS_LINK_MODES: link mode names ++ * @ETH_SS_MSG_CLASSES: debug message class names ++ * @ETH_SS_WOL_MODES: wake-on-lan modes ++ * @ETH_SS_SOF_TIMESTAMPING: SOF_TIMESTAMPING_* flags ++ * @ETH_SS_TS_TX_TYPES: timestamping Tx types ++ * @ETH_SS_TS_RX_FILTERS: timestamping Rx filters ++ * @ETH_SS_UDP_TUNNEL_TYPES: UDP tunnel types ++ * @ETH_SS_STATS_STD: standardized stats ++ * @ETH_SS_STATS_ETH_PHY: names of IEEE 802.3 PHY statistics ++ * @ETH_SS_STATS_ETH_MAC: names of IEEE 802.3 MAC statistics ++ * @ETH_SS_STATS_ETH_CTRL: names of IEEE 802.3 MAC Control statistics ++ * @ETH_SS_STATS_RMON: names of RMON statistics ++ * ++ * @ETH_SS_COUNT: number of defined string sets + */ + enum ethtool_stringset { + ETH_SS_TEST = 0, +@@ -604,6 +689,21 @@ enum ethtool_stringset { + ETH_SS_TUNABLES, + ETH_SS_PHY_STATS, + ETH_SS_PHY_TUNABLES, ++ ETH_SS_LINK_MODES, ++ ETH_SS_MSG_CLASSES, ++ ETH_SS_WOL_MODES, ++ ETH_SS_SOF_TIMESTAMPING, ++ ETH_SS_TS_TX_TYPES, ++ ETH_SS_TS_RX_FILTERS, ++ ETH_SS_UDP_TUNNEL_TYPES, ++ ETH_SS_STATS_STD, ++ ETH_SS_STATS_ETH_PHY, ++ ETH_SS_STATS_ETH_MAC, ++ ETH_SS_STATS_ETH_CTRL, ++ ETH_SS_STATS_RMON, ++ ++ /* add new constants above here */ ++ ETH_SS_COUNT + }; + + /** +@@ -628,6 +728,7 @@ struct ethtool_gstrings { + /** + * struct ethtool_sset_info - string set information + * @cmd: Command number = %ETHTOOL_GSSET_INFO ++ * @reserved: Reserved for future use; see the note on reserved space. + * @sset_mask: On entry, a bitmask of string sets to query, with bits + * numbered according to &enum ethtool_stringset. On return, a + * bitmask of those string sets queried that are supported. +@@ -672,6 +773,7 @@ enum ethtool_test_flags { + * @flags: A bitmask of flags from &enum ethtool_test_flags. Some + * flags may be set by the user on entry; others may be set by + * the driver on return. ++ * @reserved: Reserved for future use; see the note on reserved space. + * @len: On return, the number of test results + * @data: Array of test results + * +@@ -872,6 +974,7 @@ union ethtool_flow_union { + * @vlan_etype: VLAN EtherType + * @vlan_tci: VLAN tag control information + * @data: user defined data ++ * @padding: Reserved for future use; see the note on reserved space. + * + * Note, @vlan_etype, @vlan_tci, and @data are only valid if %FLOW_EXT + * is set in &struct ethtool_rx_flow_spec @flow_type. +@@ -1047,7 +1150,8 @@ struct ethtool_rxfh_indir { + * hardware hash key. + * @hfunc: Defines the current RSS hash function used by HW (or to be set to). + * Valid values are one of the %ETH_RSS_HASH_*. +- * @rsvd: Reserved for future extensions. ++ * @rsvd8: Reserved for future use; see the note on reserved space. ++ * @rsvd32: Reserved for future use; see the note on reserved space. + * @rss_config: RX ring/queue index for each hash value i.e., indirection table + * of @indir_size __u32 elements, followed by hash key of @key_size + * bytes. +@@ -1215,7 +1319,9 @@ struct ethtool_sfeatures { + * @so_timestamping: bit mask of the sum of the supported SO_TIMESTAMPING flags + * @phc_index: device index of the associated PHC, or -1 if there is none + * @tx_types: bit mask of the supported hwtstamp_tx_types enumeration values ++ * @tx_reserved: Reserved for future use; see the note on reserved space. + * @rx_filters: bit mask of the supported hwtstamp_rx_filters enumeration values ++ * @rx_reserved: Reserved for future use; see the note on reserved space. + * + * The bits in the 'tx_types' and 'rx_filters' fields correspond to + * the 'hwtstamp_tx_types' and 'hwtstamp_rx_filters' enumeration values, +@@ -1289,15 +1395,33 @@ struct ethtool_per_queue_op { + }; + + /** +- * struct ethtool_fecparam - Ethernet forward error correction(fec) parameters ++ * struct ethtool_fecparam - Ethernet Forward Error Correction parameters + * @cmd: Command number = %ETHTOOL_GFECPARAM or %ETHTOOL_SFECPARAM +- * @active_fec: FEC mode which is active on porte +- * @fec: Bitmask of supported/configured FEC modes +- * @rsvd: Reserved for future extensions. i.e FEC bypass feature. ++ * @active_fec: FEC mode which is active on the port, single bit set, GET only. ++ * @fec: Bitmask of configured FEC modes. ++ * @reserved: Reserved for future extensions, ignore on GET, write 0 for SET. ++ * ++ * Note that @reserved was never validated on input and ethtool user space ++ * left it uninitialized when calling SET. Hence going forward it can only be ++ * used to return a value to userspace with GET. ++ * ++ * FEC modes supported by the device can be read via %ETHTOOL_GLINKSETTINGS. ++ * FEC settings are configured by link autonegotiation whenever it's enabled. ++ * With autoneg on %ETHTOOL_GFECPARAM can be used to read the current mode. ++ * ++ * When autoneg is disabled %ETHTOOL_SFECPARAM controls the FEC settings. ++ * It is recommended that drivers only accept a single bit set in @fec. ++ * When multiple bits are set in @fec drivers may pick mode in an implementation ++ * dependent way. Drivers should reject mixing %ETHTOOL_FEC_AUTO_BIT with other ++ * FEC modes, because it's unclear whether in this case other modes constrain ++ * AUTO or are independent choices. ++ * Drivers must reject SET requests if they support none of the requested modes. + * +- * Drivers should reject a non-zero setting of @autoneg when +- * autoneogotiation is disabled (or not supported) for the link. ++ * If device does not support FEC drivers may use %ETHTOOL_FEC_NONE instead ++ * of returning %EOPNOTSUPP from %ETHTOOL_GFECPARAM. + * ++ * See enum ethtool_fec_config_bits for definition of valid bits for both ++ * @fec and @active_fec. + */ + struct ethtool_fecparam { + __u32 cmd; +@@ -1309,11 +1433,16 @@ struct ethtool_fecparam { + + /** + * enum ethtool_fec_config_bits - flags definition of ethtool_fec_configuration +- * @ETHTOOL_FEC_NONE: FEC mode configuration is not supported +- * @ETHTOOL_FEC_AUTO: Default/Best FEC mode provided by driver +- * @ETHTOOL_FEC_OFF: No FEC Mode +- * @ETHTOOL_FEC_RS: Reed-Solomon Forward Error Detection mode +- * @ETHTOOL_FEC_BASER: Base-R/Reed-Solomon Forward Error Detection mode ++ * @ETHTOOL_FEC_NONE_BIT: FEC mode configuration is not supported. Should not ++ * be used together with other bits. GET only. ++ * @ETHTOOL_FEC_AUTO_BIT: Select default/best FEC mode automatically, usually ++ * based link mode and SFP parameters read from module's ++ * EEPROM. This bit does _not_ mean autonegotiation. ++ * @ETHTOOL_FEC_OFF_BIT: No FEC Mode ++ * @ETHTOOL_FEC_RS_BIT: Reed-Solomon FEC Mode ++ * @ETHTOOL_FEC_BASER_BIT: Base-R/Reed-Solomon FEC Mode ++ * @ETHTOOL_FEC_LLRS_BIT: Low Latency Reed Solomon FEC Mode (25G/50G Ethernet ++ * Consortium) + */ + enum ethtool_fec_config_bits { + ETHTOOL_FEC_NONE_BIT, +@@ -1321,6 +1450,7 @@ enum ethtool_fec_config_bits { + ETHTOOL_FEC_OFF_BIT, + ETHTOOL_FEC_RS_BIT, + ETHTOOL_FEC_BASER_BIT, ++ ETHTOOL_FEC_LLRS_BIT, + }; + + #define ETHTOOL_FEC_NONE (1 << ETHTOOL_FEC_NONE_BIT) +@@ -1328,6 +1458,7 @@ enum ethtool_fec_config_bits { + #define ETHTOOL_FEC_OFF (1 << ETHTOOL_FEC_OFF_BIT) + #define ETHTOOL_FEC_RS (1 << ETHTOOL_FEC_RS_BIT) + #define ETHTOOL_FEC_BASER (1 << ETHTOOL_FEC_BASER_BIT) ++#define ETHTOOL_FEC_LLRS (1 << ETHTOOL_FEC_LLRS_BIT) + + /* CMDs currently supported */ + #define ETHTOOL_GSET 0x00000001 /* DEPRECATED, Get settings. +@@ -1530,7 +1661,6 @@ enum ethtool_link_mode_bit_indices { + ETHTOOL_LINK_MODE_400000baseCR4_Full_BIT = 89, + ETHTOOL_LINK_MODE_100baseFX_Half_BIT = 90, + ETHTOOL_LINK_MODE_100baseFX_Full_BIT = 91, +- + /* must be last entry */ + __ETHTOOL_LINK_MODE_MASK_NBITS + }; +@@ -1667,6 +1797,18 @@ static inline int ethtool_validate_duple + return 0; + } + ++#define MASTER_SLAVE_CFG_UNSUPPORTED 0 ++#define MASTER_SLAVE_CFG_UNKNOWN 1 ++#define MASTER_SLAVE_CFG_MASTER_PREFERRED 2 ++#define MASTER_SLAVE_CFG_SLAVE_PREFERRED 3 ++#define MASTER_SLAVE_CFG_MASTER_FORCE 4 ++#define MASTER_SLAVE_CFG_SLAVE_FORCE 5 ++#define MASTER_SLAVE_STATE_UNSUPPORTED 0 ++#define MASTER_SLAVE_STATE_UNKNOWN 1 ++#define MASTER_SLAVE_STATE_MASTER 2 ++#define MASTER_SLAVE_STATE_SLAVE 3 ++#define MASTER_SLAVE_STATE_ERR 4 ++ + /* Which connector port. */ + #define PORT_TP 0x00 + #define PORT_AUI 0x01 +@@ -1706,6 +1848,8 @@ static inline int ethtool_validate_duple + #define WAKE_MAGICSECURE (1 << 6) /* only meaningful if WAKE_MAGIC */ + #define WAKE_FILTER (1 << 7) + ++#define WOL_MODE_COUNT 8 ++ + /* L2-L4 network traffic flow types */ + #define TCP_V4_FLOW 0x01 /* hash or spec (tcp_ip4_spec) */ + #define UDP_V4_FLOW 0x02 /* hash or spec (udp_ip4_spec) */ +@@ -1856,6 +2000,11 @@ enum ethtool_reset_flags { + * autonegotiation; 0 if unknown or not applicable. Read-only. + * @transceiver: Used to distinguish different possible PHY types, + * reported consistently by PHYLIB. Read-only. ++ * @master_slave_cfg: Master/slave port mode. ++ * @master_slave_state: Master/slave port state. ++ * @reserved: Reserved for future use; see the note on reserved space. ++ * @reserved1: Reserved for future use; see the note on reserved space. ++ * @link_mode_masks: Variable length bitmaps. + * + * If autonegotiation is disabled, the speed and @duplex represent the + * fixed link mode and are writable if the driver supports multiple +@@ -1903,7 +2052,9 @@ struct ethtool_link_settings { + __u8 eth_tp_mdix_ctrl; + __s8 link_mode_masks_nwords; + __u8 transceiver; +- __u8 reserved1[3]; ++ __u8 master_slave_cfg; ++ __u8 master_slave_state; ++ __u8 reserved1[1]; + __u32 reserved[7]; + __u32 link_mode_masks[0]; + /* layout of link_mode_masks fields: +Index: linux-5.4.284/drivers/net/ethernet/mediatek/mtk_eth_path.c +=================================================================== +--- linux-5.4.284.orig/drivers/net/ethernet/mediatek/mtk_eth_path.c ++++ linux-5.4.284/drivers/net/ethernet/mediatek/mtk_eth_path.c +@@ -15,10 +15,10 @@ + struct mtk_eth_muxc { + const char *name; + int cap_bit; +- int (*set_path)(struct mtk_eth *eth, int path); ++ int (*set_path)(struct mtk_eth *eth, u64 path); + }; + +-static const char *mtk_eth_path_name(int path) ++static const char *mtk_eth_path_name(u64 path) + { + switch (path) { + case MTK_ETH_PATH_GMAC1_RGMII: +@@ -31,19 +31,29 @@ static const char *mtk_eth_path_name(int + return "gmac2_rgmii"; + case MTK_ETH_PATH_GMAC2_SGMII: + return "gmac2_sgmii"; ++ case MTK_ETH_PATH_GMAC2_2P5GPHY: ++ return "gmac2_2p5gphy"; + case MTK_ETH_PATH_GMAC2_GEPHY: + return "gmac2_gephy"; ++ case MTK_ETH_PATH_GMAC3_SGMII: ++ return "gmac3_sgmii"; + case MTK_ETH_PATH_GDM1_ESW: + return "gdm1_esw"; ++ case MTK_ETH_PATH_GMAC1_USXGMII: ++ return "gmac1_usxgmii"; ++ case MTK_ETH_PATH_GMAC2_USXGMII: ++ return "gmac2_usxgmii"; ++ case MTK_ETH_PATH_GMAC3_USXGMII: ++ return "gmac3_usxgmii"; + default: + return "unknown path"; + } + } + +-static int set_mux_gdm1_to_gmac1_esw(struct mtk_eth *eth, int path) ++static int set_mux_gdm1_to_gmac1_esw(struct mtk_eth *eth, u64 path) + { + bool updated = true; +- u32 val, mask, set; ++ u32 mask, set, reg; + + switch (path) { + case MTK_ETH_PATH_GMAC1_SGMII: +@@ -57,21 +67,23 @@ static int set_mux_gdm1_to_gmac1_esw(str + default: + updated = false; + break; +- }; +- +- if (updated) { +- val = mtk_r32(eth, MTK_MAC_MISC); +- val = (val & mask) | set; +- mtk_w32(eth, val, MTK_MAC_MISC); + } + ++ if (mtk_is_netsys_v3_or_greater(eth)) ++ reg = MTK_MAC_MISC_V3; ++ else ++ reg = MTK_MAC_MISC; ++ ++ if (updated) ++ mtk_m32(eth, mask, set, reg); ++ + dev_dbg(eth->dev, "path %s in %s updated = %d\n", + mtk_eth_path_name(path), __func__, updated); + + return 0; + } + +-static int set_mux_gmac2_gmac0_to_gephy(struct mtk_eth *eth, int path) ++static int set_mux_gmac2_gmac0_to_gephy(struct mtk_eth *eth, u64 path) + { + unsigned int val = 0; + bool updated = true; +@@ -94,9 +106,9 @@ static int set_mux_gmac2_gmac0_to_gephy( + return 0; + } + +-static int set_mux_u3_gmac2_to_qphy(struct mtk_eth *eth, int path) ++static int set_mux_u3_gmac2_to_qphy(struct mtk_eth *eth, u64 path) + { +- unsigned int val = 0,mask=0,reg=0; ++ unsigned int val = 0, mask = 0, reg = 0; + bool updated = true; + + switch (path) { +@@ -125,13 +137,32 @@ static int set_mux_u3_gmac2_to_qphy(stru + return 0; + } + +-static int set_mux_gmac1_gmac2_to_sgmii_rgmii(struct mtk_eth *eth, int path) ++static int set_mux_gmac2_to_2p5gphy(struct mtk_eth *eth, u64 path) ++{ ++ int ret; ++ ++ if (path == MTK_ETH_PATH_GMAC2_2P5GPHY) { ++ ret = regmap_clear_bits(eth->ethsys, ETHSYS_SYSCFG0, SYSCFG0_SGMII_GMAC2_V2); ++ if (ret) ++ return ret; ++ ++ /* Setup mux to 2p5g PHY */ ++ ret = regmap_clear_bits(eth->infra, TOP_MISC_NETSYS_PCS_MUX, MUX_G2_USXGMII_SEL); ++ if (ret) ++ return ret; ++ ++ dev_dbg(eth->dev, "path %s in %s updated\n", ++ mtk_eth_path_name(path), __func__); ++ } ++ ++ return 0; ++} ++ ++static int set_mux_gmac1_gmac2_to_sgmii_rgmii(struct mtk_eth *eth, u64 path) + { + unsigned int val = 0; + bool updated = true; + +- spin_lock(ð->syscfg0_lock); +- + switch (path) { + case MTK_ETH_PATH_GMAC1_SGMII: + val = SYSCFG0_SGMII_GMAC1; +@@ -153,26 +184,63 @@ static int set_mux_gmac1_gmac2_to_sgmii_ + default: + updated = false; + break; +- }; ++ } + + if (updated) + regmap_update_bits(eth->ethsys, ETHSYS_SYSCFG0, + SYSCFG0_SGMII_MASK, val); + +- spin_unlock(ð->syscfg0_lock); +- + dev_dbg(eth->dev, "path %s in %s updated = %d\n", + mtk_eth_path_name(path), __func__, updated); + + return 0; + } + +-static int set_mux_gmac12_to_gephy_sgmii(struct mtk_eth *eth, int path) ++static int set_mux_gmac123_to_usxgmii(struct mtk_eth *eth, u64 path) + { + unsigned int val = 0; + bool updated = true; ++ int mac_id = 0; + +- spin_lock(ð->syscfg0_lock); ++ /* Disable SYSCFG1 SGMII */ ++ regmap_read(eth->ethsys, ETHSYS_SYSCFG0, &val); ++ ++ switch (path) { ++ case MTK_ETH_PATH_GMAC1_USXGMII: ++ val &= ~(u32)SYSCFG0_SGMII_GMAC1_V2; ++ mac_id = MTK_GMAC1_ID; ++ break; ++ case MTK_ETH_PATH_GMAC2_USXGMII: ++ val &= ~(u32)SYSCFG0_SGMII_GMAC2_V2; ++ mac_id = MTK_GMAC2_ID; ++ break; ++ case MTK_ETH_PATH_GMAC3_USXGMII: ++ val &= ~(u32)SYSCFG0_SGMII_GMAC3_V2; ++ mac_id = MTK_GMAC3_ID; ++ break; ++ default: ++ updated = false; ++ }; ++ ++ if (updated) { ++ regmap_update_bits(eth->ethsys, ETHSYS_SYSCFG0, ++ SYSCFG0_SGMII_MASK, val); ++ ++ if (mac_id == MTK_GMAC2_ID) ++ regmap_set_bits(eth->infra, TOP_MISC_NETSYS_PCS_MUX, ++ MUX_G2_USXGMII_SEL); ++ } ++ ++ dev_dbg(eth->dev, "path %s in %s updated = %d\n", ++ mtk_eth_path_name(path), __func__, updated); ++ ++ return 0; ++} ++ ++static int set_mux_gmac123_to_gephy_sgmii(struct mtk_eth *eth, u64 path) ++{ ++ unsigned int val = 0; ++ bool updated = true; + + regmap_read(eth->ethsys, ETHSYS_SYSCFG0, &val); + +@@ -186,16 +254,17 @@ static int set_mux_gmac12_to_gephy_sgmii + case MTK_ETH_PATH_GMAC2_SGMII: + val |= SYSCFG0_SGMII_GMAC2_V2; + break; ++ case MTK_ETH_PATH_GMAC3_SGMII: ++ val |= SYSCFG0_SGMII_GMAC3_V2; ++ break; + default: + updated = false; +- }; ++ } + + if (updated) + regmap_update_bits(eth->ethsys, ETHSYS_SYSCFG0, + SYSCFG0_SGMII_MASK, val); + +- spin_unlock(ð->syscfg0_lock); +- + dev_dbg(eth->dev, "path %s in %s updated = %d\n", + mtk_eth_path_name(path), __func__, updated); + +@@ -216,17 +285,29 @@ static const struct mtk_eth_muxc mtk_eth + .cap_bit = MTK_ETH_MUX_U3_GMAC2_TO_QPHY, + .set_path = set_mux_u3_gmac2_to_qphy, + }, { ++ .name = "mux_gmac2_to_2p5gphy", ++ .cap_bit = MTK_ETH_MUX_GMAC2_TO_2P5GPHY, ++ .set_path = set_mux_gmac2_to_2p5gphy, ++ }, { + .name = "mux_gmac1_gmac2_to_sgmii_rgmii", + .cap_bit = MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII, + .set_path = set_mux_gmac1_gmac2_to_sgmii_rgmii, + }, { + .name = "mux_gmac12_to_gephy_sgmii", + .cap_bit = MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII, +- .set_path = set_mux_gmac12_to_gephy_sgmii, ++ .set_path = set_mux_gmac123_to_gephy_sgmii, ++ }, { ++ .name = "mux_gmac123_to_gephy_sgmii", ++ .cap_bit = MTK_ETH_MUX_GMAC123_TO_GEPHY_SGMII, ++ .set_path = set_mux_gmac123_to_gephy_sgmii, ++ }, { ++ .name = "mux_gmac123_to_usxgmii", ++ .cap_bit = MTK_ETH_MUX_GMAC123_TO_USXGMII, ++ .set_path = set_mux_gmac123_to_usxgmii, + }, + }; + +-static int mtk_eth_mux_setup(struct mtk_eth *eth, int path) ++static int mtk_eth_mux_setup(struct mtk_eth *eth, u64 path) + { + int i, err = 0; + +@@ -255,24 +336,47 @@ out: + return err; + } + ++int mtk_gmac_usxgmii_path_setup(struct mtk_eth *eth, int mac_id) ++{ ++ u64 path; ++ ++ path = (mac_id == MTK_GMAC1_ID) ? MTK_ETH_PATH_GMAC1_USXGMII : ++ (mac_id == MTK_GMAC2_ID) ? MTK_ETH_PATH_GMAC2_USXGMII : ++ MTK_ETH_PATH_GMAC3_USXGMII; ++ ++ /* Setup proper MUXes along the path */ ++ return mtk_eth_mux_setup(eth, path); ++} ++ + int mtk_gmac_sgmii_path_setup(struct mtk_eth *eth, int mac_id) + { +- int err, path; ++ u64 path; + +- path = (mac_id == 0) ? MTK_ETH_PATH_GMAC1_SGMII : +- MTK_ETH_PATH_GMAC2_SGMII; ++ path = (mac_id == MTK_GMAC1_ID) ? MTK_ETH_PATH_GMAC1_SGMII : ++ (mac_id == MTK_GMAC2_ID) ? MTK_ETH_PATH_GMAC2_SGMII : ++ MTK_ETH_PATH_GMAC3_SGMII; + + /* Setup proper MUXes along the path */ +- err = mtk_eth_mux_setup(eth, path); +- if (err) +- return err; ++ return mtk_eth_mux_setup(eth, path); ++} + +- return 0; ++int mtk_gmac_2p5gphy_path_setup(struct mtk_eth *eth, int mac_id) ++{ ++ u64 path = 0; ++ ++ if (mac_id == MTK_GMAC2_ID) ++ path = MTK_ETH_PATH_GMAC2_2P5GPHY; ++ ++ if (!path) ++ return -EINVAL; ++ ++ /* Setup proper MUXes along the path */ ++ return mtk_eth_mux_setup(eth, path); + } + + int mtk_gmac_gephy_path_setup(struct mtk_eth *eth, int mac_id) + { +- int err, path = 0; ++ u64 path = 0; + + if (mac_id == 1) + path = MTK_ETH_PATH_GMAC2_GEPHY; +@@ -281,25 +385,16 @@ int mtk_gmac_gephy_path_setup(struct mtk + return -EINVAL; + + /* Setup proper MUXes along the path */ +- err = mtk_eth_mux_setup(eth, path); +- if (err) +- return err; +- +- return 0; ++ return mtk_eth_mux_setup(eth, path); + } + + int mtk_gmac_rgmii_path_setup(struct mtk_eth *eth, int mac_id) + { +- int err, path; ++ u64 path; + + path = (mac_id == 0) ? MTK_ETH_PATH_GMAC1_RGMII : + MTK_ETH_PATH_GMAC2_RGMII; + + /* Setup proper MUXes along the path */ +- err = mtk_eth_mux_setup(eth, path); +- if (err) +- return err; +- +- return 0; ++ return mtk_eth_mux_setup(eth, path); + } +- +Index: linux-5.4.284/drivers/net/ethernet/mediatek/mtk_eth_soc.c +=================================================================== +--- linux-5.4.284.orig/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ linux-5.4.284/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -215,7 +215,7 @@ static int mt7621_gmac0_rgmii_adjust(str + } + + static void mtk_gmac0_rgmii_adjust(struct mtk_eth *eth, +- phy_interface_t interface, int speed) ++ phy_interface_t interface) + { + u32 val; + int ret; +@@ -229,26 +229,7 @@ static void mtk_gmac0_rgmii_adjust(struc + return; + } + +- val = (speed == SPEED_1000) ? +- INTF_MODE_RGMII_1000 : INTF_MODE_RGMII_10_100; +- mtk_w32(eth, val, INTF_MODE); +- +- regmap_update_bits(eth->ethsys, ETHSYS_CLKCFG0, +- ETHSYS_TRGMII_CLK_SEL362_5, +- ETHSYS_TRGMII_CLK_SEL362_5); +- +- val = (speed == SPEED_1000) ? 250000000 : 500000000; +- ret = clk_set_rate(eth->clks[MTK_CLK_TRGPLL], val); +- if (ret) +- dev_err(eth->dev, "Failed to set trgmii pll: %d\n", ret); +- +- val = (speed == SPEED_1000) ? +- RCK_CTRL_RGMII_1000 : RCK_CTRL_RGMII_10_100; +- mtk_w32(eth, val, TRGMII_RCK_CTRL); +- +- val = (speed == SPEED_1000) ? +- TCK_CTRL_RGMII_1000 : TCK_CTRL_RGMII_10_100; +- mtk_w32(eth, val, TRGMII_TCK_CTRL); ++ dev_err(eth->dev, "Missing PLL configuration, ethernet may not work\n"); + } + + static void mtk_mac_config(struct phylink_config *config, unsigned int mode, +@@ -257,8 +238,8 @@ static void mtk_mac_config(struct phylin + struct mtk_mac *mac = container_of(config, struct mtk_mac, + phylink_config); + struct mtk_eth *eth = mac->hw; +- u32 mcr_cur, mcr_new, sid, i; +- int val, ge_mode, err=0; ++ int val, ge_mode, err = 0; ++ u32 i; + + /* MT76x8 has no hardware settings between for the MAC */ + if (!MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628) && +@@ -271,7 +252,7 @@ static void mtk_mac_config(struct phylin + if (!MTK_HAS_CAPS(mac->hw->soc->caps, + MTK_GMAC1_TRGMII)) + goto err_phy; +- /* fall through */ ++ fallthrough; + case PHY_INTERFACE_MODE_RGMII_TXID: + case PHY_INTERFACE_MODE_RGMII_RXID: + case PHY_INTERFACE_MODE_RGMII_ID: +@@ -316,8 +297,7 @@ static void mtk_mac_config(struct phylin + goto err_phy; + } else { + mtk_gmac0_rgmii_adjust(mac->hw, +- state->interface, +- state->speed); ++ state->interface); + + /* mt7623_pad_clk_setup */ + for (i = 0 ; i < NUM_TRGMII_CTRL; i++) +@@ -351,14 +331,10 @@ static void mtk_mac_config(struct phylin + } + + /* put the gmac into the right mode */ +- spin_lock(ð->syscfg0_lock); + regmap_read(eth->ethsys, ETHSYS_SYSCFG0, &val); + val &= ~SYSCFG0_GE_MODE(SYSCFG0_GE_MASK, mac->id); + val |= SYSCFG0_GE_MODE(ge_mode, mac->id); + regmap_write(eth->ethsys, ETHSYS_SYSCFG0, val); +- spin_unlock(ð->syscfg0_lock); +- +- mac->interface = state->interface; + } + + /* SGMII */ +@@ -367,67 +343,24 @@ static void mtk_mac_config(struct phylin + /* The path GMAC to SGMII will be enabled once the SGMIISYS is + * being setup done. + */ +- spin_lock(ð->syscfg0_lock); + regmap_read(eth->ethsys, ETHSYS_SYSCFG0, &val); + + regmap_update_bits(eth->ethsys, ETHSYS_SYSCFG0, + SYSCFG0_SGMII_MASK, + ~(u32)SYSCFG0_SGMII_MASK); + +- /* Decide how GMAC and SGMIISYS be mapped */ +- sid = (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_SGMII)) ? +- 0 : mac->id; +- +- /* Setup SGMIISYS with the determined property */ +- if (state->interface != PHY_INTERFACE_MODE_SGMII) +- err = mtk_sgmii_setup_mode_force(eth->sgmii, sid, +- state); +- else if (phylink_autoneg_inband(mode)) +- err = mtk_sgmii_setup_mode_an(eth->sgmii, sid); +- +- if (err) { +- spin_unlock(ð->syscfg0_lock); +- goto init_err; +- } +- +- regmap_update_bits(eth->ethsys, ETHSYS_SYSCFG0, +- SYSCFG0_SGMII_MASK, val); +- spin_unlock(ð->syscfg0_lock); +- } else if (phylink_autoneg_inband(mode)) { ++ /* Save the syscfg0 value for mac_finish */ ++ mac->syscfg0 = val; ++ } else if (state->interface != PHY_INTERFACE_MODE_USXGMII && ++ state->interface != PHY_INTERFACE_MODE_10GBASER && ++ state->interface != PHY_INTERFACE_MODE_5GBASER && ++ phylink_autoneg_inband(mode)) { + dev_err(eth->dev, +- "In-band mode not supported in non SGMII mode!\n"); ++ "In-band mode not supported in non-SerDes modes!\n"); + return; + } + +- /* Setup gmac */ +- mcr_cur = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id)); +- mcr_new = mcr_cur; +- mcr_new &= ~(MAC_MCR_SPEED_100 | MAC_MCR_SPEED_1000 | +- MAC_MCR_FORCE_DPX | MAC_MCR_FORCE_TX_FC | +- MAC_MCR_FORCE_RX_FC); +- mcr_new |= MAC_MCR_MAX_RX_1536 | MAC_MCR_IPG_CFG | MAC_MCR_FORCE_MODE | +- MAC_MCR_BACKOFF_EN | MAC_MCR_BACKPR_EN | MAC_MCR_FORCE_LINK; +- +- switch (state->speed) { +- case SPEED_2500: +- case SPEED_1000: +- mcr_new |= MAC_MCR_SPEED_1000; +- break; +- case SPEED_100: +- mcr_new |= MAC_MCR_SPEED_100; +- break; +- } +- if (state->duplex == DUPLEX_FULL) { +- mcr_new |= MAC_MCR_FORCE_DPX; +- if (state->pause & MLO_PAUSE_TX) +- mcr_new |= MAC_MCR_FORCE_TX_FC; +- if (state->pause & MLO_PAUSE_RX) +- mcr_new |= MAC_MCR_FORCE_RX_FC; +- } +- +- /* Only update control register when needed! */ +- if (mcr_new != mcr_cur) +- mtk_w32(mac->hw, mcr_new, MTK_MAC_MCR(mac->id)); ++ mac->interface = state->interface; + + return; + +@@ -441,6 +374,35 @@ init_err: + mac->id, phy_modes(state->interface), err); + } + ++ ++static int mtk_mac_finish(struct phylink_config *config, unsigned int mode, ++ phy_interface_t interface) ++{ ++ struct mtk_mac *mac = container_of(config, struct mtk_mac, ++ phylink_config); ++ struct mtk_eth *eth = mac->hw; ++ u32 mcr_cur, mcr_new; ++ ++ /* Enable SGMII */ ++ if (interface == PHY_INTERFACE_MODE_SGMII || ++ phy_interface_mode_is_8023z(interface)) ++ regmap_update_bits(eth->ethsys, ETHSYS_SYSCFG0, ++ SYSCFG0_SGMII_MASK, mac->syscfg0); ++ ++ /* Setup gmac */ ++ mcr_cur = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id)); ++ mcr_new = mcr_cur; ++ mcr_new |= MAC_MCR_IPG_CFG | MAC_MCR_FORCE_MODE | ++ MAC_MCR_BACKOFF_EN | MAC_MCR_BACKPR_EN | MAC_MCR_FORCE_LINK | ++ MAC_MCR_RX_FIFO_CLR_DIS; ++ ++ /* Only update control register when needed! */ ++ if (mcr_new != mcr_cur) ++ mtk_w32(mac->hw, mcr_new, MTK_MAC_MCR(mac->id)); ++ ++ return 0; ++} ++ + static void mtk_mac_pcs_get_state(struct phylink_config *config, + struct phylink_link_state *state) + { +@@ -484,14 +446,91 @@ static void mtk_mac_an_restart(struct ph + static void mtk_mac_link_down(struct phylink_config *config, unsigned int mode, + phy_interface_t interface) + { ++ // struct mtk_mac *mac = container_of(config, struct mtk_mac, ++ // phylink_config); ++ // u32 mcr = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id)); ++ ++ // mcr &= ~(MAC_MCR_TX_EN | MAC_MCR_RX_EN); ++ // mtk_w32(mac->hw, mcr, MTK_MAC_MCR(mac->id)); ++ struct mtk_mac *mac = container_of(config, struct mtk_mac, ++ phylink_config); ++ ++ if (mac->id != MTK_GMAC1_ID) { ++ mtk_m32(mac->hw, XMAC_MCR_TRX_DISABLE, XMAC_MCR_TRX_DISABLE, MTK_XMAC_MCR(mac->id)); ++ } ++} ++ ++ ++static struct phylink_pcs *mtk_mac_select_pcs(struct phylink_config *config, ++ phy_interface_t interface) ++{ + struct mtk_mac *mac = container_of(config, struct mtk_mac, + phylink_config); +- u32 mcr = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id)); ++ struct mtk_eth *eth = mac->hw; ++ unsigned int sid; ++ ++ if ((interface == PHY_INTERFACE_MODE_SGMII || ++ phy_interface_mode_is_8023z(interface)) && ++ MTK_HAS_CAPS(eth->soc->caps, MTK_SGMII)) { ++ sid = mtk_mac2xgmii_id(eth, mac->id); ++ return eth->sgmii_pcs[sid]; ++ } + +- mcr &= ~(MAC_MCR_TX_EN | MAC_MCR_RX_EN); ++ return NULL; ++} ++ ++ ++static void mtk_gdm_mac_link_up(struct mtk_mac *mac, ++ struct phy_device *phy, ++ unsigned int mode, phy_interface_t interface, ++ int speed, int duplex, bool tx_pause, bool rx_pause) ++{ ++ u32 mcr; ++ ++ mcr = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id)); ++ mcr &= ~(MAC_MCR_SPEED_100 | MAC_MCR_SPEED_1000 | ++ MAC_MCR_FORCE_DPX | MAC_MCR_FORCE_TX_FC | ++ MAC_MCR_FORCE_RX_FC); ++ ++ /* Configure speed */ ++ mac->speed = speed; ++ switch (speed) { ++ case SPEED_2500: ++ case SPEED_1000: ++ mcr |= MAC_MCR_SPEED_1000; ++ break; ++ case SPEED_100: ++ mcr |= MAC_MCR_SPEED_100; ++ break; ++ } ++ ++ /* Configure duplex */ ++ if (duplex == DUPLEX_FULL) ++ mcr |= MAC_MCR_FORCE_DPX; ++ ++ /* Configure pause modes - phylink will avoid these for half duplex */ ++ if (tx_pause) ++ mcr |= MAC_MCR_FORCE_TX_FC; ++ if (rx_pause) ++ mcr |= MAC_MCR_FORCE_RX_FC; ++ ++ mcr |= MAC_MCR_TX_EN | MAC_MCR_RX_EN; + mtk_w32(mac->hw, mcr, MTK_MAC_MCR(mac->id)); + } + ++// static void mtk_mac_link_up(struct phylink_config *config, ++// struct phy_device *phy, ++// unsigned int mode, phy_interface_t interface, ++// int speed, int duplex, bool tx_pause, bool rx_pause) ++// { ++// struct mtk_mac *mac = container_of(config, struct mtk_mac, ++// phylink_config); ++// u32 mcr = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id)); ++ ++// mcr |= MAC_MCR_TX_EN | MAC_MCR_RX_EN; ++// mtk_w32(mac->hw, mcr, MTK_MAC_MCR(mac->id)); ++// } ++ + static void mtk_mac_link_up(struct phylink_config *config, + struct phy_device *phy, + unsigned int mode, phy_interface_t interface, +@@ -499,10 +538,10 @@ static void mtk_mac_link_up(struct phyli + { + struct mtk_mac *mac = container_of(config, struct mtk_mac, + phylink_config); +- u32 mcr = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id)); + +- mcr |= MAC_MCR_TX_EN | MAC_MCR_RX_EN; +- mtk_w32(mac->hw, mcr, MTK_MAC_MCR(mac->id)); ++ ++ mtk_gdm_mac_link_up(mac, phy, mode, interface, speed, duplex, ++ tx_pause, rx_pause); + } + + static void mtk_validate(struct phylink_config *config, +@@ -592,6 +631,10 @@ static void mtk_validate(struct phylink_ + } + + static const struct phylink_mac_ops mtk_phylink_ops = { ++ // 5.15 ++ .mac_select_pcs = mtk_mac_select_pcs, ++ .mac_finish = mtk_mac_finish, ++ // 5.4 + .validate = mtk_validate, + .mac_pcs_get_state = mtk_mac_pcs_get_state, + .mac_an_restart = mtk_mac_an_restart, +@@ -3471,6 +3514,51 @@ static int mtk_add_mac(struct mtk_eth *e + + mac->phylink_config.dev = ð->netdev[id]->dev; + mac->phylink_config.type = PHYLINK_NETDEV; ++ mac->phylink_config.mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | ++ MAC_10 | MAC_100 | MAC_1000 | MAC_2500FD; ++ ++ /* MT7623 gmac0 is now missing its speed-specific PLL configuration ++ * in its .mac_config method (since state->speed is not valid there. ++ * Disable support for MII, GMII and RGMII. ++ */ ++ if (!mac->hw->soc->disable_pll_modes || mac->id != 0) { ++ __set_bit(PHY_INTERFACE_MODE_MII, ++ mac->phylink_config.supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_GMII, ++ mac->phylink_config.supported_interfaces); ++ ++ if (MTK_HAS_CAPS(mac->hw->soc->caps, MTK_RGMII)) ++ phy_interface_set_rgmii(mac->phylink_config.supported_interfaces); ++ } ++ ++ if (MTK_HAS_CAPS(mac->hw->soc->caps, MTK_TRGMII) && !mac->id) ++ __set_bit(PHY_INTERFACE_MODE_TRGMII, ++ mac->phylink_config.supported_interfaces); ++ ++ if (MTK_HAS_CAPS(mac->hw->soc->caps, MTK_SGMII)) { ++ __set_bit(PHY_INTERFACE_MODE_SGMII, ++ mac->phylink_config.supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_1000BASEX, ++ mac->phylink_config.supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_2500BASEX, ++ mac->phylink_config.supported_interfaces); ++ } ++ ++ if (mac->hw->soc->version > 2 && ++ MTK_HAS_CAPS(mac->hw->soc->caps, MTK_ESW_BIT) && ++ id == MTK_GMAC1_ID) { ++ mac->phylink_config.mac_capabilities = MAC_ASYM_PAUSE | ++ MAC_SYM_PAUSE | ++ MAC_10000FD; ++ phy_interface_zero(mac->phylink_config.supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_INTERNAL, ++ mac->phylink_config.supported_interfaces); ++ } ++ ++ if (MTK_HAS_CAPS(mac->hw->soc->caps, MTK_2P5GPHY) && ++ id == MTK_GMAC2_ID) ++ __set_bit(PHY_INTERFACE_MODE_INTERNAL, ++ mac->phylink_config.supported_interfaces); + + phylink = phylink_create(&mac->phylink_config, + of_fwnode_handle(mac->of_node), +@@ -3506,6 +3594,7 @@ free_netdev: + return err; + } + ++ + static int mtk_probe(struct platform_device *pdev) + { + struct device_node *mac_np; +@@ -3571,7 +3660,6 @@ static int mtk_probe(struct platform_dev + return PTR_ERR(eth->infra); + } + } +- + if (MTK_HAS_CAPS(eth->soc->caps, MTK_SGMII)) { + eth->sgmii = devm_kzalloc(eth->dev, sizeof(*eth->sgmii), + GFP_KERNEL); +@@ -3579,7 +3667,7 @@ static int mtk_probe(struct platform_dev + return -ENOMEM; + + err = mtk_sgmii_init(eth->sgmii, pdev->dev.of_node, +- eth->soc->ana_rgc3); ++ eth->soc->ana_rgc3,eth); + + if (err) + return err; +Index: linux-5.4.284/drivers/net/ethernet/mediatek/mtk_eth_soc.h +=================================================================== +--- linux-5.4.284.orig/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ linux-5.4.284/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -109,6 +109,11 @@ + #define MTK_GDMA_TO_PDMA 0x0 + #define MTK_GDMA_DROP_ALL 0x7777 + ++/* GDM Egress Control Register */ ++#define MTK_GDMA_EG_CTRL(x) ({ typeof(x) _x = (x); (_x == MTK_GMAC3_ID) ? \ ++ 0x544 : 0x504 + (_x * 0x1000); }) ++#define MTK_GDMA_XGDM_SEL BIT(31) ++ + /* Unicast Filter MAC Address Register - Low */ + #define MTK_GDMA_MAC_ADRL(x) (0x508 + (x * 0x1000)) + +@@ -586,6 +591,14 @@ + #define PHY_IAC_TIMEOUT HZ + + #define MTK_MAC_MISC 0x1000c ++#define MTK_MAC_MISC_V3 0x10010 ++#define MTK_MUX_TO_ESW BIT(0) ++#define MISC_MDC_TURBO BIT(4) ++#define MUX_G2_USXGMII_SEL BIT(1) ++#define SYSCFG0_SGMII_GMAC3_V2 BIT(7) ++#define TOP_MISC_NETSYS_PCS_MUX 0x84 ++ ++#define MTK_MAC_MISC 0x1000c + #define MTK_MUX_TO_ESW BIT(0) + + /* Mac control registers */ +@@ -595,6 +608,7 @@ + #define MAC_MCR_FORCE_MODE BIT(15) + #define MAC_MCR_TX_EN BIT(14) + #define MAC_MCR_RX_EN BIT(13) ++#define MAC_MCR_RX_FIFO_CLR_DIS BIT(12) + #define MAC_MCR_BACKOFF_EN BIT(9) + #define MAC_MCR_BACKPR_EN BIT(8) + #define MAC_MCR_FORCE_RX_FC BIT(5) +@@ -649,6 +663,21 @@ + #define INTF_MODE_RGMII_1000 (TRGMII_MODE | TRGMII_CENTRAL_ALIGNED) + #define INTF_MODE_RGMII_10_100 0 + ++/* XFI Mac control registers */ ++#define MTK_XMAC_BASE(x) (0x12000 + (((x) - 1) * 0x1000)) ++#define MTK_XMAC_MCR(x) (MTK_XMAC_BASE(x)) ++#define XMAC_MCR_TRX_DISABLE 0xf ++#define XMAC_MCR_FORCE_TX_FC BIT(5) ++#define XMAC_MCR_FORCE_RX_FC BIT(4) ++ ++/* XFI Mac logic reset registers */ ++#define MTK_XMAC_LOGIC_RST(x) (MTK_XMAC_BASE(x) + 0x10) ++#define XMAC_LOGIC_RST BIT(0) ++ ++/* XFI Mac count global control */ ++#define MTK_XMAC_CNT_CTRL(x) (MTK_XMAC_BASE(x) + 0x100) ++#define XMAC_GLB_CNTCLR BIT(0) ++ + /* GPIO port control registers for GMAC 2*/ + #define GPIO_OD33_CTRL8 0x4c0 + #define GPIO_BIAS_CTRL 0xed0 +@@ -1032,81 +1061,161 @@ struct mtk_napi { + }; + + enum mkt_eth_capabilities { ++#if 0 ++// 5.4 ++ MTK_RGMII_BIT = 0, ++ MTK_TRGMII_BIT = 1, ++ MTK_SGMII_BIT = 2, ++ MTK_USXGMII_BIT = 3, ++ MTK_2P5GPHY_BIT = 4, ++ MTK_ESW_BIT = 5, ++ MTK_GEPHY_BIT = 6, ++ MTK_MUX_BIT = 7, ++ MTK_INFRA_BIT = 8, ++ MTK_SHARED_SGMII_BIT = 9, ++ MTK_HWLRO_BIT = 10, ++ MTK_RSS_BIT = 11, ++ MTK_SHARED_INT_BIT = 12, ++ MTK_TRGMII_MT7621_CLK_BIT = 13, ++ MTK_QDMA_BIT = 14, ++ MTK_NETSYS_V2_BIT = 15, ++ MTK_SOC_MT7628_BIT = 16, ++ MTK_RSTCTRL_PPE1_BIT = 17, ++ MTK_RSTCTRL_PPE2_BIT = 18, ++ MTK_U3_COPHY_V2_BIT = 19, ++ MTK_SRAM_BIT = 20, ++ MTK_36BIT_DMA_BIT = 21, ++ ++ /* MUX BITS*/ ++ MTK_ETH_MUX_GDM1_TO_GMAC1_ESW_BIT = 22, ++ MTK_ETH_MUX_GMAC2_GMAC0_TO_GEPHY_BIT = 23, ++ MTK_ETH_MUX_U3_GMAC2_TO_QPHY_BIT = 24, ++ MTK_ETH_MUX_GMAC2_TO_2P5GPHY_BIT = 25, ++ MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII_BIT = 26, ++ MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII_BIT = 27, ++ MTK_ETH_MUX_GMAC123_TO_GEPHY_SGMII_BIT = 28, ++ MTK_ETH_MUX_GMAC123_TO_USXGMII_BIT = 29, ++ ++ /* PATH BITS */ ++ MTK_ETH_PATH_GMAC1_RGMII_BIT = 30, ++ MTK_ETH_PATH_GMAC1_TRGMII_BIT = 31, ++ MTK_ETH_PATH_GMAC1_SGMII_BIT = 32, ++ MTK_ETH_PATH_GMAC2_RGMII_BIT = 33, ++ MTK_ETH_PATH_GMAC2_SGMII_BIT = 34, ++ MTK_ETH_PATH_GMAC2_2P5GPHY_BIT = 35, ++ MTK_ETH_PATH_GMAC2_GEPHY_BIT = 36, ++ MTK_ETH_PATH_GMAC3_SGMII_BIT = 37, ++ MTK_ETH_PATH_GDM1_ESW_BIT = 38, ++ MTK_ETH_PATH_GMAC1_USXGMII_BIT = 39, ++ MTK_ETH_PATH_GMAC2_USXGMII_BIT = 40, ++ MTK_ETH_PATH_GMAC3_USXGMII_BIT = 41, ++#else ++// 5.15 + MTK_RGMII_BIT = 0, +- MTK_TRGMII_BIT, +- MTK_SGMII_BIT, +- MTK_ESW_BIT, +- MTK_GEPHY_BIT, +- MTK_MUX_BIT, +- MTK_INFRA_BIT, +- MTK_SHARED_SGMII_BIT, +- MTK_HWLRO_BIT, +- MTK_RSS_BIT, +- MTK_SHARED_INT_BIT, +- MTK_TRGMII_MT7621_CLK_BIT, +- MTK_QDMA_BIT, +- MTK_NETSYS_V2_BIT, + MTK_NETSYS_RX_V2_BIT, +- MTK_SOC_MT7628_BIT, +- MTK_RSTCTRL_PPE1_BIT, +- MTK_U3_COPHY_V2_BIT, ++ MTK_TRGMII_BIT = 1, ++ MTK_SGMII_BIT = 2, ++ MTK_USXGMII_BIT = 3, ++ MTK_2P5GPHY_BIT = 4, ++ MTK_ESW_BIT = 5, ++ MTK_GEPHY_BIT = 6, ++ MTK_MUX_BIT = 7, ++ MTK_INFRA_BIT = 8, ++ MTK_SHARED_SGMII_BIT = 9, ++ MTK_HWLRO_BIT = 10, ++ MTK_SHARED_INT_BIT = 11, ++ MTK_TRGMII_MT7621_CLK_BIT = 12, ++ MTK_QDMA_BIT = 13, ++ MTK_SOC_MT7628_BIT = 14, ++ MTK_RSTCTRL_PPE1_BIT = 15, ++ MTK_RSTCTRL_PPE2_BIT = 16, ++ MTK_U3_COPHY_V2_BIT = 17, ++ MTK_SRAM_BIT = 18, ++ MTK_36BIT_DMA_BIT = 19, + + /* MUX BITS*/ +- MTK_ETH_MUX_GDM1_TO_GMAC1_ESW_BIT, +- MTK_ETH_MUX_GMAC2_GMAC0_TO_GEPHY_BIT, +- MTK_ETH_MUX_U3_GMAC2_TO_QPHY_BIT, +- MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII_BIT, +- MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII_BIT, ++ MTK_ETH_MUX_GDM1_TO_GMAC1_ESW_BIT = 20, ++ MTK_ETH_MUX_GMAC2_GMAC0_TO_GEPHY_BIT = 21, ++ MTK_ETH_MUX_U3_GMAC2_TO_QPHY_BIT = 22, ++ MTK_ETH_MUX_GMAC2_TO_2P5GPHY_BIT = 23, ++ MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII_BIT = 24, ++ MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII_BIT = 25, ++ MTK_ETH_MUX_GMAC123_TO_GEPHY_SGMII_BIT = 26, ++ MTK_ETH_MUX_GMAC123_TO_USXGMII_BIT = 27, + + /* PATH BITS */ +- MTK_ETH_PATH_GMAC1_RGMII_BIT, +- MTK_ETH_PATH_GMAC1_TRGMII_BIT, +- MTK_ETH_PATH_GMAC1_SGMII_BIT, +- MTK_ETH_PATH_GMAC2_RGMII_BIT, +- MTK_ETH_PATH_GMAC2_SGMII_BIT, +- MTK_ETH_PATH_GMAC2_GEPHY_BIT, +- MTK_ETH_PATH_GDM1_ESW_BIT, ++ MTK_ETH_PATH_GMAC1_RGMII_BIT = 28, ++ MTK_ETH_PATH_GMAC1_TRGMII_BIT = 29, ++ MTK_ETH_PATH_GMAC1_SGMII_BIT = 30, ++ MTK_ETH_PATH_GMAC2_RGMII_BIT = 31, ++ MTK_ETH_PATH_GMAC2_SGMII_BIT = 32, ++ MTK_ETH_PATH_GMAC2_2P5GPHY_BIT = 33, ++ MTK_ETH_PATH_GMAC2_GEPHY_BIT = 34, ++ MTK_ETH_PATH_GMAC3_SGMII_BIT = 35, ++ MTK_ETH_PATH_GDM1_ESW_BIT = 36, ++ MTK_ETH_PATH_GMAC1_USXGMII_BIT = 37, ++ MTK_ETH_PATH_GMAC2_USXGMII_BIT = 38, ++ MTK_ETH_PATH_GMAC3_USXGMII_BIT = 39, ++#endif + }; + + /* Supported hardware group on SoCs */ +-#define MTK_RGMII BIT(MTK_RGMII_BIT) +-#define MTK_TRGMII BIT(MTK_TRGMII_BIT) +-#define MTK_SGMII BIT(MTK_SGMII_BIT) +-#define MTK_ESW BIT(MTK_ESW_BIT) +-#define MTK_GEPHY BIT(MTK_GEPHY_BIT) +-#define MTK_MUX BIT(MTK_MUX_BIT) +-#define MTK_INFRA BIT(MTK_INFRA_BIT) +-#define MTK_SHARED_SGMII BIT(MTK_SHARED_SGMII_BIT) +-#define MTK_HWLRO BIT(MTK_HWLRO_BIT) +-#define MTK_RSS BIT(MTK_RSS_BIT) +-#define MTK_SHARED_INT BIT(MTK_SHARED_INT_BIT) +-#define MTK_TRGMII_MT7621_CLK BIT(MTK_TRGMII_MT7621_CLK_BIT) +-#define MTK_QDMA BIT(MTK_QDMA_BIT) +-#define MTK_NETSYS_V2 BIT(MTK_NETSYS_V2_BIT) + #define MTK_NETSYS_RX_V2 BIT(MTK_NETSYS_RX_V2_BIT) +-#define MTK_SOC_MT7628 BIT(MTK_SOC_MT7628_BIT) +-#define MTK_RSTCTRL_PPE1 BIT(MTK_RSTCTRL_PPE1_BIT) +-#define MTK_U3_COPHY_V2 BIT(MTK_U3_COPHY_V2_BIT) ++#define MTK_RGMII BIT_ULL(MTK_RGMII_BIT) ++#define MTK_TRGMII BIT_ULL(MTK_TRGMII_BIT) ++#define MTK_SGMII BIT_ULL(MTK_SGMII_BIT) ++ ++#define MTK_2P5GPHY BIT_ULL(MTK_2P5GPHY_BIT) ++#define MTK_ESW BIT_ULL(MTK_ESW_BIT) ++#define MTK_GEPHY BIT_ULL(MTK_GEPHY_BIT) ++#define MTK_MUX BIT_ULL(MTK_MUX_BIT) ++#define MTK_INFRA BIT_ULL(MTK_INFRA_BIT) ++#define MTK_SHARED_SGMII BIT_ULL(MTK_SHARED_SGMII_BIT) ++#define MTK_HWLRO BIT_ULL(MTK_HWLRO_BIT) ++#define MTK_SHARED_INT BIT_ULL(MTK_SHARED_INT_BIT) ++#define MTK_TRGMII_MT7621_CLK BIT_ULL(MTK_TRGMII_MT7621_CLK_BIT) ++#define MTK_QDMA BIT_ULL(MTK_QDMA_BIT) ++#define MTK_SOC_MT7628 BIT_ULL(MTK_SOC_MT7628_BIT) ++#define MTK_RSTCTRL_PPE1 BIT_ULL(MTK_RSTCTRL_PPE1_BIT) ++#define MTK_RSTCTRL_PPE2 BIT_ULL(MTK_RSTCTRL_PPE2_BIT) ++#define MTK_U3_COPHY_V2 BIT_ULL(MTK_U3_COPHY_V2_BIT) ++#define MTK_SRAM BIT_ULL(MTK_SRAM_BIT) ++#define MTK_36BIT_DMA BIT_ULL(MTK_36BIT_DMA_BIT) ++ ++// fix me ++#define MTK_RSS BIT(MTK_SHARED_INT_BIT) ++#define MTK_NETSYS_V2 BIT(MTK_RSTCTRL_PPE1_BIT) + + #define MTK_ETH_MUX_GDM1_TO_GMAC1_ESW \ +- BIT(MTK_ETH_MUX_GDM1_TO_GMAC1_ESW_BIT) ++ BIT_ULL(MTK_ETH_MUX_GDM1_TO_GMAC1_ESW_BIT) + #define MTK_ETH_MUX_GMAC2_GMAC0_TO_GEPHY \ +- BIT(MTK_ETH_MUX_GMAC2_GMAC0_TO_GEPHY_BIT) ++ BIT_ULL(MTK_ETH_MUX_GMAC2_GMAC0_TO_GEPHY_BIT) + #define MTK_ETH_MUX_U3_GMAC2_TO_QPHY \ +- BIT(MTK_ETH_MUX_U3_GMAC2_TO_QPHY_BIT) ++ BIT_ULL(MTK_ETH_MUX_U3_GMAC2_TO_QPHY_BIT) ++#define MTK_ETH_MUX_GMAC2_TO_2P5GPHY \ ++ BIT_ULL(MTK_ETH_MUX_GMAC2_TO_2P5GPHY_BIT) + #define MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII \ +- BIT(MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII_BIT) ++ BIT_ULL(MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII_BIT) + #define MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII \ +- BIT(MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII_BIT) ++ BIT_ULL(MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII_BIT) ++#define MTK_ETH_MUX_GMAC123_TO_GEPHY_SGMII \ ++ BIT_ULL(MTK_ETH_MUX_GMAC123_TO_GEPHY_SGMII_BIT) ++#define MTK_ETH_MUX_GMAC123_TO_USXGMII \ ++ BIT_ULL(MTK_ETH_MUX_GMAC123_TO_USXGMII_BIT) + + /* Supported path present on SoCs */ +-#define MTK_ETH_PATH_GMAC1_RGMII BIT(MTK_ETH_PATH_GMAC1_RGMII_BIT) +-#define MTK_ETH_PATH_GMAC1_TRGMII BIT(MTK_ETH_PATH_GMAC1_TRGMII_BIT) +-#define MTK_ETH_PATH_GMAC1_SGMII BIT(MTK_ETH_PATH_GMAC1_SGMII_BIT) +-#define MTK_ETH_PATH_GMAC2_RGMII BIT(MTK_ETH_PATH_GMAC2_RGMII_BIT) +-#define MTK_ETH_PATH_GMAC2_SGMII BIT(MTK_ETH_PATH_GMAC2_SGMII_BIT) +-#define MTK_ETH_PATH_GMAC2_GEPHY BIT(MTK_ETH_PATH_GMAC2_GEPHY_BIT) +-#define MTK_ETH_PATH_GDM1_ESW BIT(MTK_ETH_PATH_GDM1_ESW_BIT) ++#define MTK_ETH_PATH_GMAC1_RGMII BIT_ULL(MTK_ETH_PATH_GMAC1_RGMII_BIT) ++#define MTK_ETH_PATH_GMAC1_TRGMII BIT_ULL(MTK_ETH_PATH_GMAC1_TRGMII_BIT) ++#define MTK_ETH_PATH_GMAC1_SGMII BIT_ULL(MTK_ETH_PATH_GMAC1_SGMII_BIT) ++#define MTK_ETH_PATH_GMAC2_RGMII BIT_ULL(MTK_ETH_PATH_GMAC2_RGMII_BIT) ++#define MTK_ETH_PATH_GMAC2_SGMII BIT_ULL(MTK_ETH_PATH_GMAC2_SGMII_BIT) ++#define MTK_ETH_PATH_GMAC2_2P5GPHY BIT_ULL(MTK_ETH_PATH_GMAC2_2P5GPHY_BIT) ++#define MTK_ETH_PATH_GMAC2_GEPHY BIT_ULL(MTK_ETH_PATH_GMAC2_GEPHY_BIT) ++#define MTK_ETH_PATH_GMAC3_SGMII BIT_ULL(MTK_ETH_PATH_GMAC3_SGMII_BIT) ++#define MTK_ETH_PATH_GDM1_ESW BIT_ULL(MTK_ETH_PATH_GDM1_ESW_BIT) ++#define MTK_ETH_PATH_GMAC1_USXGMII BIT_ULL(MTK_ETH_PATH_GMAC1_USXGMII_BIT) ++#define MTK_ETH_PATH_GMAC2_USXGMII BIT_ULL(MTK_ETH_PATH_GMAC2_USXGMII_BIT) ++#define MTK_ETH_PATH_GMAC3_USXGMII BIT_ULL(MTK_ETH_PATH_GMAC3_USXGMII_BIT) + + #define MTK_GMAC1_RGMII (MTK_ETH_PATH_GMAC1_RGMII | MTK_RGMII) + #define MTK_GMAC1_TRGMII (MTK_ETH_PATH_GMAC1_TRGMII | MTK_TRGMII) +@@ -1114,7 +1221,12 @@ enum mkt_eth_capabilities { + #define MTK_GMAC2_RGMII (MTK_ETH_PATH_GMAC2_RGMII | MTK_RGMII) + #define MTK_GMAC2_SGMII (MTK_ETH_PATH_GMAC2_SGMII | MTK_SGMII) + #define MTK_GMAC2_GEPHY (MTK_ETH_PATH_GMAC2_GEPHY | MTK_GEPHY) ++#define MTK_GMAC2_2P5GPHY (MTK_ETH_PATH_GMAC2_2P5GPHY | MTK_2P5GPHY) ++#define MTK_GMAC3_SGMII (MTK_ETH_PATH_GMAC3_SGMII | MTK_SGMII) + #define MTK_GDM1_ESW (MTK_ETH_PATH_GDM1_ESW | MTK_ESW) ++#define MTK_GMAC1_USXGMII (MTK_ETH_PATH_GMAC1_USXGMII | MTK_USXGMII) ++#define MTK_GMAC2_USXGMII (MTK_ETH_PATH_GMAC2_USXGMII | MTK_USXGMII) ++#define MTK_GMAC3_USXGMII (MTK_ETH_PATH_GMAC3_USXGMII | MTK_USXGMII) + + /* MUXes present on SoCs */ + /* 0: GDM1 -> GMAC1, 1: GDM1 -> ESW */ +@@ -1161,12 +1273,12 @@ enum mkt_eth_capabilities { + + #define MT7986_CAPS (MTK_GMAC1_SGMII | MTK_GMAC2_SGMII | \ + MTK_MUX_GMAC12_TO_GEPHY_SGMII | MTK_QDMA | \ +- MTK_NETSYS_V2 | MTK_NETSYS_RX_V2) ++ MTK_RSTCTRL_PPE1 | MTK_NETSYS_RX_V2) + + #define MT7981_CAPS (MTK_GMAC1_SGMII | MTK_GMAC2_SGMII | MTK_GMAC2_GEPHY | \ + MTK_MUX_GMAC12_TO_GEPHY_SGMII | MTK_QDMA | \ + MTK_MUX_U3_GMAC2_TO_QPHY | MTK_U3_COPHY_V2 | \ +- MTK_NETSYS_V2) ++ MTK_RSTCTRL_PPE1 | MTK_SRAM) + + /* struct mtk_eth_data - This is the structure holding all differences + * among various plaforms +@@ -1181,11 +1298,21 @@ struct mtk_tx_dma_desc_info { + */ + struct mtk_soc_data { + u32 ana_rgc3; +- u32 caps; +- u32 required_clks; ++ u64 caps; ++ u64 required_clks; + bool required_pctl; + netdev_features_t hw_features; + bool has_sram; ++ u8 version; ++ bool disable_pll_modes; ++ struct { ++ u32 txd_size; ++ u32 rxd_size; ++ u32 rx_irq_done_mask; ++ u32 rx_dma_l4_valid; ++ u32 dma_max_len; ++ u32 dma_len_offset; ++ } txrx; + }; + + /* currently no SoC has more than 2 macs */ +@@ -1270,6 +1397,7 @@ struct mtk_eth { + unsigned long sysclk; + struct regmap *ethsys; + struct regmap *infra; ++ struct phylink_pcs *sgmii_pcs[MTK_MAX_DEVS]; + struct mtk_sgmii *sgmii; + struct regmap *pctl; + bool hwlro; +@@ -1319,6 +1447,8 @@ struct mtk_mac { + struct mtk_hw_stats *hw_stats; + __be32 hwlro_ip[MTK_MAX_LRO_IP_CNT]; + int hwlro_ip_cnt; ++ unsigned int syscfg0; ++ + }; + + /* the struct describing the SoC. these are declared in the soc_xyz.c files */ +@@ -1333,7 +1463,7 @@ u32 mtk_r32(struct mtk_eth *eth, unsigne + u32 mtk_m32(struct mtk_eth *eth, u32 mask, u32 set, unsigned reg); + + int mtk_sgmii_init(struct mtk_sgmii *ss, struct device_node *np, +- u32 ana_rgc3); ++ u32 ana_rgc3,struct mtk_eth *eth); + int mtk_sgmii_setup_mode_an(struct mtk_sgmii *ss, unsigned int id); + int mtk_sgmii_setup_mode_force(struct mtk_sgmii *ss, unsigned int id, + const struct phylink_link_state *state); +@@ -1346,4 +1476,37 @@ void mtk_gdm_config(struct mtk + void ethsys_reset(struct mtk_eth *eth, u32 reset_bits); + int mtk_soc_extphy_init(struct mtk_eth *eth, int addr); + int mtk_mdio_busy_wait(struct mtk_eth *eth); ++/* GMAC Identifier */ ++enum mtk_gmac_id { ++ MTK_GMAC1_ID = 0, ++ MTK_GMAC2_ID, ++ MTK_GMAC3_ID, ++ MTK_GMAC_ID_MAX ++}; ++ ++static inline int mtk_mac2xgmii_id(struct mtk_eth *eth, int mac_id) ++{ ++ int xgmii_id = mac_id; ++ if (eth->soc->version > 2) { ++ switch (mac_id) { ++ case MTK_GMAC1_ID: ++ case MTK_GMAC2_ID: ++ xgmii_id = 1; ++ break; ++ case MTK_GMAC3_ID: ++ xgmii_id = 0; ++ break; ++ default: ++ xgmii_id = -1; ++ } ++ } ++ ++ return MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_SGMII) ? 0 : xgmii_id; ++} ++ ++static inline bool mtk_is_netsys_v3_or_greater(struct mtk_eth *eth) ++{ ++ return eth->soc->version > 2; ++} ++ + #endif /* MTK_ETH_H */ +Index: linux-5.4.284/drivers/net/ethernet/mediatek/mtk_sgmii.c +=================================================================== +--- linux-5.4.284.orig/drivers/net/ethernet/mediatek/mtk_sgmii.c ++++ linux-5.4.284/drivers/net/ethernet/mediatek/mtk_sgmii.c +@@ -10,12 +10,305 @@ + #include + #include + #include ++#include ++#include + + #include "mtk_eth_soc.h" + +-int mtk_sgmii_init(struct mtk_sgmii *ss, struct device_node *r, u32 ana_rgc3) ++#define MTK_SGMII_FLAG_PN_SWAP BIT(0) ++ ++ ++/* SGMII subsystem config registers */ ++/* BMCR (low 16) BMSR (high 16) */ ++#define SGMSYS_PCS_CONTROL_1 0x0 ++#define SGMII_BMCR GENMASK(15, 0) ++#define SGMII_BMSR GENMASK(31, 16) ++ ++#define SGMSYS_PCS_DEVICE_ID 0x4 ++#define SGMII_LYNXI_DEV_ID 0x4d544950 ++ ++#define SGMSYS_PCS_ADVERTISE 0x8 ++#define SGMII_ADVERTISE GENMASK(15, 0) ++#define SGMII_LPA GENMASK(31, 16) ++ ++#define SGMSYS_PCS_SCRATCH 0x14 ++#define SGMII_DEV_VERSION GENMASK(31, 16) ++ ++/* Register to programmable link timer, the unit in 2 * 8ns */ ++#define SGMSYS_PCS_LINK_TIMER 0x18 ++#define SGMII_LINK_TIMER_MASK GENMASK(19, 0) ++#define SGMII_LINK_TIMER_VAL(ns) FIELD_PREP(SGMII_LINK_TIMER_MASK, \ ++ ((ns) / 2 / 8)) ++ ++/* Register to control remote fault */ ++#define SGMSYS_SGMII_MODE 0x20 ++#define SGMII_IF_MODE_SGMII BIT(0) ++#define SGMII_SPEED_DUPLEX_AN BIT(1) ++#define SGMII_SPEED_MASK GENMASK(3, 2) ++#define SGMII_SPEED_10 FIELD_PREP(SGMII_SPEED_MASK, 0) ++#define SGMII_SPEED_100 FIELD_PREP(SGMII_SPEED_MASK, 1) ++#define SGMII_SPEED_1000 FIELD_PREP(SGMII_SPEED_MASK, 2) ++#define SGMII_DUPLEX_HALF BIT(4) ++#define SGMII_REMOTE_FAULT_DIS BIT(8) ++ ++/* Register to reset SGMII design */ ++#define SGMSYS_RESERVED_0 0x34 ++#define SGMII_SW_RESET BIT(0) ++ ++/* Register to set SGMII speed, ANA RG_ Control Signals III */ ++#define SGMII_PHY_SPEED_MASK GENMASK(3, 2) ++#define SGMII_PHY_SPEED_1_25G FIELD_PREP(SGMII_PHY_SPEED_MASK, 0) ++#define SGMII_PHY_SPEED_3_125G FIELD_PREP(SGMII_PHY_SPEED_MASK, 1) ++ ++/* Register to power up QPHY */ ++#define SGMSYS_QPHY_PWR_STATE_CTRL 0xe8 ++#define SGMII_PHYA_PWD BIT(4) ++ ++/* Register to QPHY wrapper control */ ++#define SGMSYS_QPHY_WRAP_CTRL 0xec ++#define SGMII_PN_SWAP_MASK GENMASK(1, 0) ++#define SGMII_PN_SWAP_TX_RX (BIT(0) | BIT(1)) ++ ++struct mtk_pcs_lynxi { ++ struct regmap *regmap; ++ u32 ana_rgc3; ++ phy_interface_t interface; ++ struct phylink_pcs pcs; ++ u32 flags; ++}; ++ ++static struct mtk_pcs_lynxi *pcs_to_mtk_pcs_lynxi(struct phylink_pcs *pcs) ++{ ++ return container_of(pcs, struct mtk_pcs_lynxi, pcs); ++} ++ ++static void mtk_pcs_lynxi_get_state(struct phylink_pcs *pcs, ++ struct phylink_link_state *state) ++{ ++ struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs); ++ unsigned int bm, bmsr, adv; ++ ++ /* Read the BMSR and LPA */ ++ regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &bm); ++ bmsr = FIELD_GET(SGMII_BMSR, bm); ++ ++ if (state->interface == PHY_INTERFACE_MODE_2500BASEX) { ++ state->link = !!(bmsr & BMSR_LSTATUS); ++ state->an_complete = !!(bmsr & BMSR_ANEGCOMPLETE); ++ state->speed = SPEED_2500; ++ state->duplex = DUPLEX_FULL; ++ ++ return; ++ } ++ ++ regmap_read(mpcs->regmap, SGMSYS_PCS_ADVERTISE, &adv); ++ phylink_mii_c22_pcs_decode_state(state, bmsr, FIELD_GET(SGMII_LPA, adv)); ++} ++ ++static int mtk_pcs_lynxi_config(struct phylink_pcs *pcs, unsigned int mode, ++ phy_interface_t interface, ++ const unsigned long *advertising, ++ bool permit_pause_to_mac) ++{ ++ struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs); ++ bool mode_changed = false, changed, use_an; ++ unsigned int rgc3, sgm_mode, bmcr; ++ int advertise, link_timer; ++ ++ advertise = phylink_mii_c22_pcs_encode_advertisement(interface, ++ advertising); ++ if (advertise < 0) ++ return advertise; ++ ++ /* Clearing IF_MODE_BIT0 switches the PCS to BASE-X mode, and ++ * we assume that fixes it's speed at bitrate = line rate (in ++ * other words, 1000Mbps or 2500Mbps). ++ */ ++ if (interface == PHY_INTERFACE_MODE_SGMII) { ++ sgm_mode = SGMII_IF_MODE_SGMII; ++ if (phylink_autoneg_inband(mode)) { ++ sgm_mode |= SGMII_REMOTE_FAULT_DIS | ++ SGMII_SPEED_DUPLEX_AN; ++ use_an = true; ++ } else { ++ use_an = false; ++ } ++ } else if (phylink_autoneg_inband(mode)) { ++ /* 1000base-X or 2500base-X autoneg */ ++ sgm_mode = SGMII_REMOTE_FAULT_DIS; ++ use_an = linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, ++ advertising) && ++ !(interface == PHY_INTERFACE_MODE_2500BASEX); ++ } else { ++ /* 1000base-X or 2500base-X without autoneg */ ++ sgm_mode = 0; ++ use_an = false; ++ } ++ ++ if (use_an) ++ bmcr = BMCR_ANENABLE; ++ else ++ bmcr = 0; ++ ++ if (mpcs->interface != interface) { ++ link_timer = phylink_get_link_timer_ns(interface); ++ if (link_timer < 0) ++ return link_timer; ++ ++ /* PHYA power down */ ++ regmap_set_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, ++ SGMII_PHYA_PWD); ++ ++ /* Reset SGMII PCS state */ ++ regmap_set_bits(mpcs->regmap, SGMSYS_RESERVED_0, ++ SGMII_SW_RESET); ++ ++ if (mpcs->flags & MTK_SGMII_FLAG_PN_SWAP) ++ regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_WRAP_CTRL, ++ SGMII_PN_SWAP_MASK, ++ SGMII_PN_SWAP_TX_RX); ++ ++ if (interface == PHY_INTERFACE_MODE_2500BASEX) ++ rgc3 = SGMII_PHY_SPEED_3_125G; ++ else ++ rgc3 = SGMII_PHY_SPEED_1_25G; ++ ++ /* Configure the underlying interface speed */ ++ regmap_update_bits(mpcs->regmap, mpcs->ana_rgc3, ++ SGMII_PHY_SPEED_MASK, rgc3); ++ ++ /* Setup the link timer */ ++ regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER, ++ SGMII_LINK_TIMER_VAL(link_timer)); ++ ++ mpcs->interface = interface; ++ mode_changed = true; ++ } ++ ++ /* Update the advertisement, noting whether it has changed */ ++ regmap_update_bits_check(mpcs->regmap, SGMSYS_PCS_ADVERTISE, ++ SGMII_ADVERTISE, advertise, &changed); ++ ++ /* Update the sgmsys mode register */ ++ regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE, ++ SGMII_REMOTE_FAULT_DIS | SGMII_SPEED_DUPLEX_AN | ++ SGMII_IF_MODE_SGMII, sgm_mode); ++ ++ /* Update the BMCR */ ++ regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1, ++ BMCR_ANENABLE, bmcr); ++ ++ /* Release PHYA power down state ++ * Only removing bit SGMII_PHYA_PWD isn't enough. ++ * There are cases when the SGMII_PHYA_PWD register contains 0x9 which ++ * prevents SGMII from working. The SGMII still shows link but no traffic ++ * can flow. Writing 0x0 to the PHYA_PWD register fix the issue. 0x0 was ++ * taken from a good working state of the SGMII interface. ++ * Unknown how much the QPHY needs but it is racy without a sleep. ++ * Tested on mt7622 & mt7986. ++ */ ++ usleep_range(50, 100); ++ regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, 0); ++ ++ return changed || mode_changed; ++} ++ ++static void mtk_pcs_lynxi_restart_an(struct phylink_pcs *pcs) ++{ ++ struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs); ++ ++ regmap_set_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1, BMCR_ANRESTART); ++} ++ ++static void mtk_pcs_lynxi_link_up(struct phylink_pcs *pcs, unsigned int mode, ++ phy_interface_t interface, int speed, ++ int duplex) ++{ ++ struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs); ++ unsigned int sgm_mode; ++ ++ if (!phylink_autoneg_inband(mode)) { ++ /* Force the speed and duplex setting */ ++ if (speed == SPEED_10) ++ sgm_mode = SGMII_SPEED_10; ++ else if (speed == SPEED_100) ++ sgm_mode = SGMII_SPEED_100; ++ else ++ sgm_mode = SGMII_SPEED_1000; ++ ++ if (duplex != DUPLEX_FULL) ++ sgm_mode |= SGMII_DUPLEX_HALF; ++ ++ regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE, ++ SGMII_DUPLEX_HALF | SGMII_SPEED_MASK, ++ sgm_mode); ++ } ++} ++ ++static void mtk_pcs_lynxi_disable(struct phylink_pcs *pcs) ++{ ++ struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs); ++ ++ mpcs->interface = PHY_INTERFACE_MODE_NA; ++} ++ ++static const struct phylink_pcs_ops mtk_pcs_lynxi_ops = { ++ .pcs_get_state = mtk_pcs_lynxi_get_state, ++ .pcs_config = mtk_pcs_lynxi_config, ++ .pcs_an_restart = mtk_pcs_lynxi_restart_an, ++ .pcs_link_up = mtk_pcs_lynxi_link_up, ++ .pcs_disable = mtk_pcs_lynxi_disable, ++}; ++ ++struct phylink_pcs *mtk_pcs_lynxi_create(struct device *dev, ++ struct regmap *regmap, u32 ana_rgc3, ++ u32 flags) ++{ ++ struct mtk_pcs_lynxi *mpcs; ++ u32 id, ver; ++ int ret; ++ ++ ret = regmap_read(regmap, SGMSYS_PCS_DEVICE_ID, &id); ++ if (ret < 0) ++ return NULL; ++ ++ if (id != SGMII_LYNXI_DEV_ID) { ++ dev_err(dev, "unknown PCS device id %08x\n", id); ++ return NULL; ++ } ++ ++ ret = regmap_read(regmap, SGMSYS_PCS_SCRATCH, &ver); ++ if (ret < 0) ++ return NULL; ++ ++ ver = FIELD_GET(SGMII_DEV_VERSION, ver); ++ if (ver != 0x1) { ++ dev_err(dev, "unknown PCS device version %04x\n", ver); ++ return NULL; ++ } ++ ++ dev_dbg(dev, "MediaTek LynxI SGMII PCS (id 0x%08x, ver 0x%04x)\n", id, ++ ver); ++ ++ mpcs = kzalloc(sizeof(*mpcs), GFP_KERNEL); ++ if (!mpcs) ++ return NULL; ++ ++ mpcs->ana_rgc3 = ana_rgc3; ++ mpcs->regmap = regmap; ++ mpcs->flags = flags; ++ mpcs->pcs.ops = &mtk_pcs_lynxi_ops; ++ mpcs->pcs.poll = true; ++ mpcs->interface = PHY_INTERFACE_MODE_NA; ++ ++ return &mpcs->pcs; ++} ++ ++int mtk_sgmii_init(struct mtk_sgmii *ss, struct device_node *r, u32 ana_rgc3,struct mtk_eth *eth) + { + struct device_node *np; ++ struct regmap *regmap; ++ u32 flags; + int i; + + ss->ana_rgc3 = ana_rgc3; +@@ -25,6 +318,14 @@ int mtk_sgmii_init(struct mtk_sgmii *ss, + if (!np) + break; + ++ regmap = syscon_node_to_regmap(np); ++ flags = 0; ++ if (of_property_read_bool(np, "mediatek,pnswap")) ++ flags |= MTK_SGMII_FLAG_PN_SWAP; ++ ++ if (IS_ERR(regmap)) ++ return PTR_ERR(regmap); ++ + ss->regmap[i] = syscon_node_to_regmap(np); + if (IS_ERR(ss->regmap[i])) + return PTR_ERR(ss->regmap[i]); +@@ -32,6 +333,12 @@ int mtk_sgmii_init(struct mtk_sgmii *ss, + ss->flags[i] &= ~(MTK_SGMII_PN_SWAP); + if (of_property_read_bool(np, "pn_swap")) + ss->flags[i] |= MTK_SGMII_PN_SWAP; ++ ++ ++ ++ eth->sgmii_pcs[i] = mtk_pcs_lynxi_create(eth->dev, regmap, ++ ana_rgc3, ++ flags); + } + + return 0; +Index: linux-5.4.284/drivers/net/phy/phylink.c +=================================================================== +--- linux-5.4.284.orig/drivers/net/phy/phylink.c ++++ linux-5.4.284/drivers/net/phy/phylink.c +@@ -19,10 +19,19 @@ + #include + #include + #include ++#include + + #include "sfp.h" + #include "swphy.h" + ++#define SGMSYS_PCS_CONTROL_1 0x0 ++#define SGMII_BMCR GENMASK(15, 0) ++#define SGMII_BMSR GENMASK(31, 16) ++ ++#define SGMSYS_PCS_ADVERTISE 0x8 ++#define SGMII_ADVERTISE GENMASK(15, 0) ++#define SGMII_LPA GENMASK(31, 16) ++ + #define SUPPORTED_INTERFACES \ + (SUPPORTED_TP | SUPPORTED_MII | SUPPORTED_FIBRE | \ + SUPPORTED_BNC | SUPPORTED_AUI | SUPPORTED_Backplane) +@@ -776,8 +785,7 @@ static void phylink_pcs_poll_start(struc + static void phylink_mac_config(struct phylink *pl, + const struct phylink_link_state *state) + { +- phylink_dbg(pl, +- "%s: mode=%s/%s/%s/%s adv=%*pb pause=%02x link=%u an=%u\n", ++ phylink_dbg(pl, "%s: mode=%s/%s/%s/%s adv=%*pb pause=%02x link=%u an=%u\n", + __func__, phylink_an_mode_str(pl->cur_link_an_mode), + phy_modes(state->interface), + phy_speed_to_str(state->speed), +@@ -822,7 +830,6 @@ static void phylink_major_config(struct + struct phylink_pcs *pcs = NULL; + bool pcs_changed = false; + int err; +- + phylink_dbg(pl, "major config %s\n", phy_modes(state->interface)); + + if (pl->using_mac_select_pcs) { +@@ -833,10 +840,8 @@ static void phylink_major_config(struct + pcs); + return; + } +- + pcs_changed = pcs && pl->pcs != pcs; + } +- + phylink_pcs_poll_stop(pl); + + if (pl->mac_ops->mac_prepare) { +@@ -848,7 +853,6 @@ static void phylink_major_config(struct + return; + } + } +- + /* If we have a new PCS, switch to the new PCS after preparing the MAC + * for the change. + */ +@@ -932,6 +936,40 @@ static int phylink_change_inband_advert( + return 0; + } + ++struct mtk_pcs_lynxi { ++ struct regmap *regmap; ++ u32 ana_rgc3; ++ phy_interface_t interface; ++ struct phylink_pcs pcs; ++ u32 flags; ++}; ++ ++static struct mtk_pcs_lynxi *pcs_to_mtk_pcs_lynxi(struct phylink_pcs *pcs) ++{ ++ return container_of(pcs, struct mtk_pcs_lynxi, pcs); ++} ++ ++static void mtk_pcs_lynxi_get_state(struct phylink_pcs *pcs, ++ struct phylink_link_state *state) ++{ ++ struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs); ++ unsigned int bm, bmsr, adv; ++ ++ /* Read the BMSR and LPA */ ++ regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &bm); ++ bmsr = FIELD_GET(SGMII_BMSR, bm); ++ if (state->interface == PHY_INTERFACE_MODE_2500BASEX) { ++ state->link = !!(bmsr & BMSR_LSTATUS); ++ state->an_complete = !!(bmsr & BMSR_ANEGCOMPLETE); ++ state->speed = SPEED_2500; ++ state->duplex = DUPLEX_FULL; ++ ++ return; ++ } ++ regmap_read(mpcs->regmap, SGMSYS_PCS_ADVERTISE, &adv); ++ phylink_mii_c22_pcs_decode_state(state, bmsr, FIELD_GET(SGMII_LPA, adv)); ++} ++ + static void phylink_mac_pcs_get_state(struct phylink *pl, + struct phylink_link_state *state) + { +@@ -945,13 +983,18 @@ static void phylink_mac_pcs_get_state(st + state->an_complete = 0; + state->link = 1; + +- if (pl->pcs_ops) +- pl->pcs_ops->pcs_get_state(pl->pcs, state); +- else if (pl->mac_ops->mac_pcs_get_state && +- pl->config->legacy_pre_march2020) +- pl->mac_ops->mac_pcs_get_state(pl->config, state); ++ if(pl->pcs_ops) ++ mtk_pcs_lynxi_get_state(pl->pcs, state); + else + state->link = 0; ++ ++ // if (pl->pcs_ops) ++ // pl->pcs_ops->pcs_get_state(pl->pcs, state); ++ // else if (pl->mac_ops->mac_pcs_get_state && ++ // pl->config->legacy_pre_march2020) ++ // pl->mac_ops->mac_pcs_get_state(pl->config, state); ++ // else ++ // state->link = 0; + } + + /* The fixed state is... fixed except for the link state, +@@ -1241,7 +1284,6 @@ struct phylink *phylink_create(struct ph + bool using_mac_select_pcs = false; + struct phylink *pl; + int ret; +- + if (mac_ops->mac_select_pcs && + mac_ops->mac_select_pcs(config, PHY_INTERFACE_MODE_NA) != + ERR_PTR(-EOPNOTSUPP)) +Index: linux-5.4.284/drivers/of/address.c +=================================================================== +--- linux-5.4.284.orig/drivers/of/address.c ++++ linux-5.4.284/drivers/of/address.c +@@ -993,14 +993,16 @@ out: + } + EXPORT_SYMBOL_GPL(of_dma_get_range); + +-/** +- * of_dma_is_coherent - Check if device is coherent +- * @np: device node +- * +- * It returns true if "dma-coherent" property was found +- * for this device in the DT, or if DMA is coherent by +- * default for OF devices on the current platform. +- */ ++static struct device_node *of_get_next_dma_parent(struct device_node *np) ++{ ++ struct device_node *parent; ++ ++ parent = __of_get_dma_parent(np); ++ of_node_put(np); ++ ++ return parent; ++} ++ + bool of_dma_is_coherent(struct device_node *np) + { + struct device_node *node; +@@ -1015,9 +1017,9 @@ bool of_dma_is_coherent(struct device_no + of_node_put(node); + return true; + } +- node = of_get_next_parent(node); ++ node = of_get_next_dma_parent(node); + } + of_node_put(node); + return false; + } +-EXPORT_SYMBOL_GPL(of_dma_is_coherent); ++EXPORT_SYMBOL_GPL(of_dma_is_coherent); +\ No newline at end of file +Index: linux-5.4.284/include/linux/phylink.h +=================================================================== +--- linux-5.4.284.orig/include/linux/phylink.h ++++ linux-5.4.284/include/linux/phylink.h +@@ -360,6 +360,8 @@ struct phylink_pcs { + struct phylink_pcs_ops { + int (*pcs_validate)(struct phylink_pcs *pcs, unsigned long *supported, + const struct phylink_link_state *state); ++ int (*pcs_enable)(struct phylink_pcs *pcs); ++ void (*pcs_disable)(struct phylink_pcs *pcs); + void (*pcs_get_state)(struct phylink_pcs *pcs, + struct phylink_link_state *state); + int (*pcs_config)(struct phylink_pcs *pcs, unsigned int mode, +@@ -369,8 +371,6 @@ struct phylink_pcs_ops { + void (*pcs_an_restart)(struct phylink_pcs *pcs); + void (*pcs_link_up)(struct phylink_pcs *pcs, unsigned int mode, + phy_interface_t interface, int speed, int duplex); +- int (*pcs_enable)(struct phylink_pcs *pcs); +- void (*pcs_disable)(struct phylink_pcs *pcs); + }; + + int pcs_enable(struct phylink_pcs *pcs); +Index: linux-5.4.284/include/linux/regmap.h +=================================================================== +--- linux-5.4.284.orig/include/linux/regmap.h ++++ linux-5.4.284/include/linux/regmap.h +@@ -1081,6 +1081,19 @@ static inline bool regmap_reg_in_range(u + return reg >= range->range_min && reg <= range->range_max; + } + ++static inline int regmap_set_bits(struct regmap *map, ++ unsigned int reg, unsigned int bits) ++{ ++ return regmap_update_bits_base(map, reg, bits, bits, ++ NULL, false, false); ++} ++ ++static inline int regmap_clear_bits(struct regmap *map, ++ unsigned int reg, unsigned int bits) ++{ ++ return regmap_update_bits_base(map, reg, bits, 0, NULL, false, false); ++} ++ + bool regmap_reg_in_ranges(unsigned int reg, + const struct regmap_range *ranges, + unsigned int nranges); +Index: linux-5.4.284/include/net/dsa.h +=================================================================== +--- linux-5.4.284.orig/include/net/dsa.h ++++ linux-5.4.284/include/net/dsa.h +@@ -369,6 +369,9 @@ typedef int dsa_fdb_dump_cb_t(const unsi + enum dsa_tag_protocol (*get_tag_protocol)(struct dsa_switch *ds, + int port, + enum dsa_tag_protocol mprot); ++ struct phylink_pcs *(*phylink_mac_select_pcs)(struct dsa_switch *ds, ++ int port, ++ phy_interface_t iface); + + int (*setup)(struct dsa_switch *ds); + void (*teardown)(struct dsa_switch *ds); +Index: linux-5.4.284/net/dsa/port.c +=================================================================== +--- linux-5.4.284.orig/net/dsa/port.c ++++ linux-5.4.284/net/dsa/port.c +@@ -484,6 +484,20 @@ void dsa_port_phylink_mac_pcs_get_state( + } + EXPORT_SYMBOL_GPL(dsa_port_phylink_mac_pcs_get_state); + ++static struct phylink_pcs * ++dsa_port_phylink_mac_select_pcs(struct phylink_config *config, ++ phy_interface_t interface) ++{ ++ struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); ++ struct phylink_pcs *pcs = ERR_PTR(-EOPNOTSUPP); ++ struct dsa_switch *ds = dp->ds; ++ ++ if (ds->ops->phylink_mac_select_pcs) ++ pcs = ds->ops->phylink_mac_select_pcs(ds, dp->index, interface); ++ ++ return pcs; ++} ++ + void dsa_port_phylink_mac_config(struct phylink_config *config, + unsigned int mode, + const struct phylink_link_state *state) +@@ -555,6 +569,7 @@ EXPORT_SYMBOL_GPL(dsa_port_phylink_mac_l + const struct phylink_mac_ops dsa_port_phylink_mac_ops = { + .validate = dsa_port_phylink_validate, + .mac_pcs_get_state = dsa_port_phylink_mac_pcs_get_state, ++ .mac_select_pcs = dsa_port_phylink_mac_select_pcs, + .mac_config = dsa_port_phylink_mac_config, + .mac_an_restart = dsa_port_phylink_mac_an_restart, + .mac_link_down = dsa_port_phylink_mac_link_down, From 323f9351ac522b6254d26c228efd3d6ecf2ba97c Mon Sep 17 00:00:00 2001 From: hchyhchyxh <641953443@qq.com> Date: Fri, 20 Sep 2024 22:55:53 +0800 Subject: [PATCH 7/7] update --- ...and-7981r128-dsa.dts => mt7981-spim-nand-7981r128-dsa.dts} | 0 ...and-7981r128-gsw.dts => mt7981-spim-nand-7981r128-gsw.dts} | 0 target/linux/mediatek/image/mt7981.mk | 4 ++-- 3 files changed, 2 insertions(+), 2 deletions(-) rename target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/{mt7981-spim-snand-7981r128-dsa.dts => mt7981-spim-nand-7981r128-dsa.dts} (100%) rename target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/{mt7981-spim-snand-7981r128-gsw.dts => mt7981-spim-nand-7981r128-gsw.dts} (100%) diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-snand-7981r128-dsa.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-nand-7981r128-dsa.dts similarity index 100% rename from target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-snand-7981r128-dsa.dts rename to target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-nand-7981r128-dsa.dts diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-snand-7981r128-gsw.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-nand-7981r128-gsw.dts similarity index 100% rename from target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-snand-7981r128-gsw.dts rename to target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-nand-7981r128-gsw.dts diff --git a/target/linux/mediatek/image/mt7981.mk b/target/linux/mediatek/image/mt7981.mk index 9a8088fec7b..75471788776 100644 --- a/target/linux/mediatek/image/mt7981.mk +++ b/target/linux/mediatek/image/mt7981.mk @@ -784,7 +784,7 @@ TARGET_DEVICES += routerich_ax3000 define Device/zhao_7981-r128-dsa DEVICE_VENDOR := ZHAO DEVICE_MODEL := 7981 R128-DSA - DEVICE_DTS := mt7981-spim-nand-7981r128 + DEVICE_DTS := mt7981-spim-nand-7981r128-dsa DEVICE_DTS_DIR := $(DTS_DIR)/mediatek DEVICE_PACKAGES := $(MT7981_USB_PKGS) SUPPORTED_DEVICES := mediatek,zhao-7981r128-d @@ -802,7 +802,7 @@ TARGET_DEVICES += zhao_7981-r128-dsa define Device/zhao_7981-r128-gsw DEVICE_VENDOR := ZHAO DEVICE_MODEL := 7981 R128-GSW - DEVICE_DTS := mt7981-spim-snand-7981r128-gsw + DEVICE_DTS := mt7981-spim-nand-7981r128-gsw DEVICE_DTS_DIR := $(DTS_DIR)/mediatek DEVICE_PACKAGES := $(MT7981_USB_PKGS) SUPPORTED_DEVICES := mediatek,zhao-7981r128-g