diff options
author | Felix Fietkau <nbd@nbd.name> | 2017-02-06 11:46:48 +0100 |
---|---|---|
committer | Felix Fietkau <nbd@nbd.name> | 2017-02-16 17:17:15 +0100 |
commit | 2bf9ea6a31736db059aa97addce3a34b933dacc1 (patch) | |
tree | ecfe628aabaf38e6bd40ca841443d19079105a83 /target/linux/mvebu/patches-4.9 | |
parent | 237454991618e0e8b7ceb8a8a2a43fca12c1a454 (diff) | |
download | upstream-2bf9ea6a31736db059aa97addce3a34b933dacc1.tar.gz upstream-2bf9ea6a31736db059aa97addce3a34b933dacc1.tar.bz2 upstream-2bf9ea6a31736db059aa97addce3a34b933dacc1.zip |
mvebu: add linux 4.9 support
Signed-off-by: Felix Fietkau <nbd@nbd.name>
Signed-off-by: Jonas Gorski <jonas.gorski@gmail.com>
Diffstat (limited to 'target/linux/mvebu/patches-4.9')
59 files changed, 9324 insertions, 0 deletions
diff --git a/target/linux/mvebu/patches-4.9/002-add_powertables.patch b/target/linux/mvebu/patches-4.9/002-add_powertables.patch new file mode 100644 index 0000000000..a5a47e4ab2 --- /dev/null +++ b/target/linux/mvebu/patches-4.9/002-add_powertables.patch @@ -0,0 +1,748 @@ +--- a/arch/arm/boot/dts/armada-xp-linksys-mamba.dts ++++ b/arch/arm/boot/dts/armada-xp-linksys-mamba.dts +@@ -86,12 +86,100 @@ + pcie@2,0 { + /* Port 0, Lane 1 */ + status = "okay"; ++ ++ mwlwifi { ++ marvell,5ghz = <0>; ++ marvell,chainmask = <4 4>; ++ marvell,powertable { ++ FCC = ++ <1 0 0x17 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0xf 0xf 0xf 0xf 0x0 0x0 0x0 0x0 0 0xf>, ++ <2 0 0x17 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x10 0x10 0x10 0x10 0x0 0x0 0x0 0x0 0 0xf>, ++ <3 0 0x17 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x10 0x10 0x10 0x10 0x0 0x0 0x0 0x0 0 0xf>, ++ <4 0 0x17 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x10 0x10 0x10 0x10 0x0 0x0 0x0 0x0 0 0xf>, ++ <5 0 0x17 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x10 0x10 0x10 0x10 0x0 0x0 0x0 0x0 0 0xf>, ++ <6 0 0x17 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x10 0x10 0x10 0x10 0x0 0x0 0x0 0x0 0 0xf>, ++ <7 0 0x17 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x10 0x10 0x10 0x10 0x0 0x0 0x0 0x0 0 0xf>, ++ <8 0 0x17 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x10 0x10 0x10 0x10 0x0 0x0 0x0 0x0 0 0xf>, ++ <9 0 0x17 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x10 0x10 0x10 0x10 0x0 0x0 0x0 0x0 0 0xf>, ++ <10 0 0x17 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x10 0x10 0x10 0x10 0x0 0x0 0x0 0x0 0 0xf>, ++ <11 0 0x17 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x0 0x0 0x0 0x0 0 0xf>; ++ ++ ETSI = ++ <1 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>, ++ <2 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>, ++ <3 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>, ++ <4 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>, ++ <5 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>, ++ <6 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>, ++ <7 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>, ++ <8 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>, ++ <9 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>, ++ <10 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>, ++ <11 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>, ++ <12 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>, ++ <13 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>; ++ }; ++ }; + }; + + /* Second mini-PCIe port */ + pcie@3,0 { + /* Port 0, Lane 3 */ + status = "okay"; ++ ++ mwlwifi { ++ marvell,2ghz = <0>; ++ marvell,chainmask = <4 4>; ++ marvell,powertable { ++ FCC = ++ <36 0 0x8 0x8 0x8 0x8 0x8 0x8 0x8 0x8 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0 0xf>, ++ <40 0 0x8 0x8 0x8 0x8 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0 0xf>, ++ <44 0 0x8 0x8 0x8 0x8 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0 0xf>, ++ <48 0 0x8 0x8 0x8 0x8 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0 0xf>, ++ <52 0 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0 0xf>, ++ <56 0 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0 0xf>, ++ <60 0 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0 0xf>, ++ <64 0 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0 0xf>, ++ <100 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <104 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <108 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <112 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <116 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <120 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <124 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <128 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <132 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <136 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <140 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <149 0 0x16 0x16 0x16 0x16 0x14 0x14 0x14 0x14 0x15 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0 0xf>, ++ <153 0 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0 0xf>, ++ <157 0 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0 0xf>, ++ <161 0 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0 0xf>, ++ <165 0 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0 0xf>; ++ ++ ETSI = ++ <36 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>, ++ <40 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>, ++ <44 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>, ++ <48 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>, ++ <52 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>, ++ <56 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>, ++ <60 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>, ++ <64 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>, ++ <100 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>, ++ <104 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>, ++ <108 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>, ++ <112 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>, ++ <116 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>, ++ <120 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>, ++ <124 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>, ++ <128 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>, ++ <132 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>, ++ <136 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>, ++ <140 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>, ++ <149 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>; ++ }; ++ }; + }; + }; + +--- a/arch/arm/boot/dts/armada-385-linksys-cobra.dts ++++ b/arch/arm/boot/dts/armada-385-linksys-cobra.dts +@@ -100,6 +100,212 @@ + }; + }; + }; ++ ++ pcie-controller { ++ pcie@1,0 { ++ mwlwifi { ++ marvell,2ghz = <0>; ++ marvell,chainmask = <4 4>; ++ marvell,powertable { ++ AU = ++ <36 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <40 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <44 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <48 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <52 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <56 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <60 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <64 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <100 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <104 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <108 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <112 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <116 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <120 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <124 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <128 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <132 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <136 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <140 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <149 0 0x19 0x19 0x19 0x17 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0 0xf>, ++ <153 0 0x19 0x19 0x19 0x17 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0 0xf>, ++ <157 0 0x19 0x19 0x19 0x17 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0 0xf>, ++ <161 0 0x19 0x19 0x19 0x17 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0 0xf>, ++ <165 0 0x19 0x19 0x19 0x17 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0 0xf>; ++ CA = ++ <36 0 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0 0xf>, ++ <40 0 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0 0xf>, ++ <44 0 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0 0xf>, ++ <48 0 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0 0xf>, ++ <52 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <56 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <60 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <64 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <100 0 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <104 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <108 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <112 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <116 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <120 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <124 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <128 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <132 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <136 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <140 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <149 0 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>, ++ <153 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>, ++ <157 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>, ++ <161 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>, ++ <165 0 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>; ++ CN = ++ <36 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <40 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <44 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <48 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <52 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <56 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <60 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <64 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <100 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <104 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <108 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <112 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <116 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <120 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <124 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <128 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <132 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <136 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <140 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <149 0 0x14 0x14 0x14 0x14 0x13 0x13 0x13 0x13 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <153 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0x10 0x10 0x10 0x10 0 0xf>, ++ <157 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0x10 0x10 0x10 0x10 0 0xf>, ++ <161 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0x10 0x10 0x10 0x10 0 0xf>, ++ <165 0 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x14 0x14 0x14 0x14 0x10 0x10 0x10 0x10 0 0xf>; ++ ETSI = ++ <36 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <40 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <44 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <48 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <52 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <56 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <60 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <64 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <100 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <104 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <108 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <112 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <116 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <120 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <124 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <128 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <132 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <136 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <140 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <149 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>; ++ FCC = ++ <36 0 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0xf 0xf 0xf 0xf 0 0xf>, ++ <40 0 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0xf 0xf 0xf 0xf 0 0xf>, ++ <44 0 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0xf 0xf 0xf 0xf 0 0xf>, ++ <48 0 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0xf 0xf 0xf 0xf 0 0xf>, ++ <52 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <56 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <60 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <64 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <100 0 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <104 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <108 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <112 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <116 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <120 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <124 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <128 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <132 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <136 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <140 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <149 0 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>, ++ <153 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>, ++ <157 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>, ++ <161 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>, ++ <165 0 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>; ++ }; ++ }; ++ }; ++ ++ pcie@2,0 { ++ mwlwifi { ++ marvell,5ghz = <0>; ++ marvell,chainmask = <4 4>; ++ marvell,powertable { ++ AU = ++ <1 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <2 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <3 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <4 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <5 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <6 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <7 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <8 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <9 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <10 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <11 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>; ++ CA = ++ <1 0 0x17 0x10 0x10 0x10 0xf 0xf 0xf 0xf 0xe 0xe 0xe 0xe 0x0 0x0 0x0 0x0 0 0xf>, ++ <2 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <3 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <4 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <5 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <6 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <7 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <8 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <9 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <10 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <11 0 0x17 0x12 0x12 0x12 0x13 0x13 0x13 0x13 0xf 0xf 0xf 0xf 0x0 0x0 0x0 0x0 0 0xf>; ++ CN = ++ <1 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <2 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <3 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <4 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <5 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <6 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <7 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <8 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <9 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <10 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <11 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <12 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <13 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <14 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>; ++ ETSI = ++ <1 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <2 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <3 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <4 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <5 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <6 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <7 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <8 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <9 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <10 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <11 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <12 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <13 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>; ++ FCC = ++ <1 0 0x17 0x10 0x10 0x10 0xf 0xf 0xf 0xf 0xe 0xe 0xe 0xe 0x0 0x0 0x0 0x0 0 0xf>, ++ <2 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <3 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <4 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <5 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <6 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <7 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <8 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <9 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <10 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <11 0 0x17 0x12 0x12 0x12 0x13 0x13 0x13 0x13 0xf 0xf 0xf 0xf 0x0 0x0 0x0 0x0 0 0xf>; ++ }; ++ }; ++ }; ++ }; + }; + + gpio-leds { +--- a/arch/arm/boot/dts/armada-385-linksys-caiman.dts ++++ b/arch/arm/boot/dts/armada-385-linksys-caiman.dts +@@ -100,6 +100,212 @@ + }; + }; + }; ++ ++ pcie-controller { ++ pcie@1,0 { ++ mwlwifi { ++ marvell,2ghz = <0>; ++ marvell,chainmask = <2 2>; ++ marvell,powertable { ++ AU = ++ <36 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <40 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <44 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <48 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <52 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <56 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <60 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <64 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <100 0 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x15 0x17 0x17 0x17 0x14 0x17 0x17 0x17 0x14 0 0xf>, ++ <104 0 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x15 0x17 0x17 0x17 0x14 0x17 0x17 0x17 0x14 0 0xf>, ++ <108 0 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x15 0x17 0x17 0x17 0x14 0x17 0x17 0x17 0x14 0 0xf>, ++ <112 0 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x15 0x17 0x17 0x17 0x14 0x17 0x17 0x17 0x14 0 0xf>, ++ <116 0 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x15 0x17 0x17 0x17 0x14 0x17 0x17 0x17 0x14 0 0xf>, ++ <120 0 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x15 0x17 0x17 0x17 0x14 0x17 0x17 0x17 0x14 0 0xf>, ++ <124 0 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x15 0x17 0x17 0x17 0x14 0x17 0x17 0x17 0x14 0 0xf>, ++ <128 0 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x15 0x17 0x17 0x17 0x14 0x17 0x17 0x17 0x14 0 0xf>, ++ <132 0 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x15 0x17 0x17 0x17 0x14 0x17 0x17 0x17 0x14 0 0xf>, ++ <136 0 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x15 0x17 0x17 0x17 0x14 0x17 0x17 0x17 0x14 0 0xf>, ++ <140 0 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x15 0x17 0x17 0x17 0x14 0x17 0x17 0x17 0x14 0 0xf>, ++ <149 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x1a 0x1a 0x17 0x14 0 0xf>, ++ <153 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x1a 0x1a 0x17 0x14 0 0xf>, ++ <157 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x1a 0x1a 0x17 0x14 0 0xf>, ++ <161 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x1a 0x1a 0x17 0x14 0 0xf>, ++ <165 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x1a 0x1a 0x17 0x14 0 0xf>; ++ CA = ++ <36 0 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0 0xf>, ++ <40 0 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0 0xf>, ++ <44 0 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0 0xf>, ++ <48 0 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0 0xf>, ++ <52 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <56 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <60 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <64 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <100 0 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <104 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <108 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <112 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <116 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <120 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <124 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <128 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <132 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <136 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <140 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <149 0 0x1a 0x1a 0x18 0x17 0x19 0x19 0x17 0x15 0x18 0x18 0x17 0x14 0x15 0x15 0x15 0x14 0 0xf>, ++ <153 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x15 0x15 0x15 0x14 0 0xf>, ++ <157 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x15 0x15 0x15 0x14 0 0xf>, ++ <161 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x15 0x15 0x15 0x14 0 0xf>, ++ <165 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x15 0x15 0x15 0x14 0 0xf>; ++ CN = ++ <36 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <40 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <44 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <48 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <52 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <56 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <60 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <64 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <100 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <104 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <108 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <112 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <116 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <120 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <124 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <128 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <132 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <136 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <140 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <149 0 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0x11 0x11 0x11 0x11 0 0xf>, ++ <153 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0 0xf>, ++ <157 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0 0xf>, ++ <161 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0 0xf>, ++ <165 0 0x15 0x15 0x15 0x15 0x16 0x16 0x16 0x15 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0 0xf>; ++ ETSI = ++ <36 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <40 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <44 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <48 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <52 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <56 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <60 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <64 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <100 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <104 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <108 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <112 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <116 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <120 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <124 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <128 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <132 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <136 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <140 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <149 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>; ++ FCC = ++ <36 0 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <40 0 0x19 0x19 0x18 0x17 0x19 0x19 0x17 0x15 0x17 0x17 0x17 0x14 0x10 0x10 0x10 0x10 0 0xf>, ++ <44 0 0x19 0x19 0x18 0x17 0x19 0x19 0x17 0x15 0x17 0x17 0x17 0x14 0x10 0x10 0x10 0x10 0 0xf>, ++ <48 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x17 0x17 0x17 0x14 0x10 0x10 0x10 0x10 0 0xf>, ++ <52 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <56 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <60 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <64 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <100 0 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <104 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <108 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <112 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <116 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <120 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <124 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <128 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <132 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <136 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <140 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <149 0 0x1a 0x1a 0x18 0x17 0x19 0x19 0x17 0x15 0x18 0x18 0x17 0x14 0x15 0x15 0x15 0x14 0 0xf>, ++ <153 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x15 0x15 0x15 0x14 0 0xf>, ++ <157 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x15 0x15 0x15 0x14 0 0xf>, ++ <161 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x15 0x15 0x15 0x14 0 0xf>, ++ <165 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x15 0x15 0x15 0x14 0 0xf>; ++ }; ++ }; ++ }; ++ ++ pcie@2,0 { ++ mwlwifi { ++ marvell,5ghz = <0>; ++ marvell,chainmask = <2 2>; ++ marvell,powertable { ++ AU = ++ <1 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <2 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <3 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <4 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <5 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <6 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <7 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <8 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <9 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <10 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <11 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>; ++ CA = ++ <1 0 0x19 0x14 0x14 0x14 0x13 0x13 0x13 0x13 0x10 0x10 0x10 0x10 0x00 0x00 0x00 0x00 0 0xf>, ++ <2 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x00 0x00 0x00 0x00 0 0xf>, ++ <3 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x00 0x00 0x00 0x00 0 0xf>, ++ <4 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x00 0x00 0x00 0x00 0 0xf>, ++ <5 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x00 0x00 0x00 0x00 0 0xf>, ++ <6 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x00 0x00 0x00 0x00 0 0xf>, ++ <7 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x00 0x00 0x00 0x00 0 0xf>, ++ <8 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x00 0x00 0x00 0x00 0 0xf>, ++ <9 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x00 0x00 0x00 0x00 0 0xf>, ++ <10 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x00 0x00 0x00 0x00 0 0xf>, ++ <11 0 0x19 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0x13 0x13 0x13 0x13 0x00 0x00 0x00 0x00 0 0xf>; ++ CN = ++ <1 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <2 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <3 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <4 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <5 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <6 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <7 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <8 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <9 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <10 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <11 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <12 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <13 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <14 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>; ++ ETSI = ++ <1 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <2 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <3 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <4 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <5 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <6 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <7 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <8 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <9 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <10 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <11 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <12 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <13 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>; ++ FCC = ++ <1 0 0x19 0x14 0x14 0x14 0x13 0x13 0x13 0x13 0x10 0x10 0x10 0x10 0x0 0x0 0x0 0x0 0 0xf>, ++ <2 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x0 0x0 0x0 0x0 0 0xf>, ++ <3 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x0 0x0 0x0 0x0 0 0xf>, ++ <4 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x0 0x0 0x0 0x0 0 0xf>, ++ <5 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x0 0x0 0x0 0x0 0 0xf>, ++ <6 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x0 0x0 0x0 0x0 0 0xf>, ++ <7 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x0 0x0 0x0 0x0 0 0xf>, ++ <8 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x0 0x0 0x0 0x0 0 0xf>, ++ <9 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x0 0x0 0x0 0x0 0 0xf>, ++ <10 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x0 0x0 0x0 0x0 0 0xf>, ++ <11 0 0x19 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0x13 0x13 0x13 0x13 0x0 0x0 0x0 0x0 0 0xf>; ++ }; ++ }; ++ }; ++ }; + }; + + gpio-leds { +--- a/arch/arm/boot/dts/armada-385-linksys-shelby.dts ++++ b/arch/arm/boot/dts/armada-385-linksys-shelby.dts +@@ -100,6 +100,212 @@ + }; + }; + }; ++ ++ pcie-controller { ++ pcie@1,0 { ++ mwlwifi { ++ marvell,2ghz = <0>; ++ marvell,chainmask = <4 4>; ++ marvell,powertable { ++ AU = ++ <36 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <40 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <44 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <48 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <52 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <56 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <60 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <64 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <100 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <104 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <108 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <112 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <116 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <120 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <124 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <128 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <132 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <136 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <140 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <149 0 0x19 0x19 0x19 0x17 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0 0xf>, ++ <153 0 0x19 0x19 0x19 0x17 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0 0xf>, ++ <157 0 0x19 0x19 0x19 0x17 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0 0xf>, ++ <161 0 0x19 0x19 0x19 0x17 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0 0xf>, ++ <165 0 0x19 0x19 0x19 0x17 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0 0xf>; ++ CA = ++ <36 0 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0 0xf>, ++ <40 0 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0 0xf>, ++ <44 0 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0 0xf>, ++ <48 0 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0 0xf>, ++ <52 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <56 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <60 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <64 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <100 0 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <104 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <108 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <112 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <116 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <120 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <124 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <128 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <132 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <136 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <140 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <149 0 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>, ++ <153 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>, ++ <157 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>, ++ <161 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>, ++ <165 0 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>; ++ CN = ++ <36 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <40 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <44 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <48 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <52 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <56 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <60 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <64 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <100 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <104 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <108 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <112 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <116 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <120 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <124 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <128 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <132 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <136 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <140 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <149 0 0x14 0x14 0x14 0x14 0x13 0x13 0x13 0x13 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <153 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0x10 0x10 0x10 0x10 0 0xf>, ++ <157 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0x10 0x10 0x10 0x10 0 0xf>, ++ <161 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0x10 0x10 0x10 0x10 0 0xf>, ++ <165 0 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x14 0x14 0x14 0x14 0x10 0x10 0x10 0x10 0 0xf>; ++ ETSI = ++ <36 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <40 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <44 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <48 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <52 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <56 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <60 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <64 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <100 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <104 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <108 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <112 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <116 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <120 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <124 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <128 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <132 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <136 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <140 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <149 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>; ++ FCC = ++ <36 0 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0xf 0xf 0xf 0xf 0 0xf>, ++ <40 0 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0xf 0xf 0xf 0xf 0 0xf>, ++ <44 0 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0xf 0xf 0xf 0xf 0 0xf>, ++ <48 0 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0xf 0xf 0xf 0xf 0 0xf>, ++ <52 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <56 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <60 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <64 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <100 0 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <104 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <108 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <112 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <116 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <120 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <124 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <128 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <132 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <136 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <140 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <149 0 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>, ++ <153 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>, ++ <157 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>, ++ <161 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>, ++ <165 0 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>; ++ }; ++ }; ++ }; ++ ++ pcie@2,0 { ++ mwlwifi { ++ marvell,5ghz = <0>; ++ marvell,chainmask = <4 4>; ++ marvell,powertable { ++ AU = ++ <1 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <2 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <3 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <4 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <5 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <6 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <7 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <8 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <9 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <10 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <11 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>; ++ CA = ++ <1 0 0x17 0x10 0x10 0x10 0xf 0xf 0xf 0xf 0xe 0xe 0xe 0xe 0x0 0x0 0x0 0x0 0 0xf>, ++ <2 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <3 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <4 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <5 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <6 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <7 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <8 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <9 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <10 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <11 0 0x17 0x12 0x12 0x12 0x13 0x13 0x13 0x13 0xf 0xf 0xf 0xf 0x0 0x0 0x0 0x0 0 0xf>; ++ CN = ++ <1 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <2 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <3 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <4 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <5 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <6 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <7 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <8 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <9 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <10 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <11 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <12 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <13 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <14 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>; ++ ETSI = ++ <1 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <2 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <3 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <4 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <5 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <6 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <7 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <8 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <9 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <10 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <11 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <12 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <13 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>; ++ FCC = ++ <1 0 0x17 0x10 0x10 0x10 0xf 0xf 0xf 0xf 0xe 0xe 0xe 0xe 0x0 0x0 0x0 0x0 0 0xf>, ++ <2 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <3 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <4 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <5 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <6 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <7 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <8 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <9 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <10 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <11 0 0x17 0x12 0x12 0x12 0x13 0x13 0x13 0x13 0xf 0xf 0xf 0xf 0x0 0x0 0x0 0x0 0 0xf>; ++ }; ++ }; ++ }; ++ }; + }; + + gpio-leds { diff --git a/target/linux/mvebu/patches-4.9/003-add_switch_nodes.patch b/target/linux/mvebu/patches-4.9/003-add_switch_nodes.patch new file mode 100644 index 0000000000..38a48d308e --- /dev/null +++ b/target/linux/mvebu/patches-4.9/003-add_switch_nodes.patch @@ -0,0 +1,40 @@ +--- a/arch/arm/boot/dts/armada-xp-linksys-mamba.dts ++++ b/arch/arm/boot/dts/armada-xp-linksys-mamba.dts +@@ -455,6 +455,16 @@ + }; + }; + }; ++ ++ mvsw61xx { ++ compatible = "marvell,88e6172"; ++ status = "okay"; ++ reg = <0x10>; ++ ++ mii-bus = <&mdio>; ++ cpu-port-0 = <5>; ++ cpu-port-1 = <6>; ++ }; + }; + + &pinctrl { +--- a/arch/arm/boot/dts/armada-385-linksys.dtsi ++++ b/arch/arm/boot/dts/armada-385-linksys.dtsi +@@ -304,6 +304,18 @@ + }; + }; + }; ++ ++ mvsw61xx { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ compatible = "marvell,88e6176"; ++ status = "okay"; ++ reg = <0x10>; ++ ++ mii-bus = <&mdio>; ++ cpu-port-0 = <5>; ++ cpu-port-1 = <6>; ++ }; + }; + + &pinctrl { diff --git a/target/linux/mvebu/patches-4.9/010-build_new_dtbs.patch b/target/linux/mvebu/patches-4.9/010-build_new_dtbs.patch new file mode 100644 index 0000000000..1eb647df9c --- /dev/null +++ b/target/linux/mvebu/patches-4.9/010-build_new_dtbs.patch @@ -0,0 +1,11 @@ +--- a/arch/arm/boot/dts/Makefile ++++ b/arch/arm/boot/dts/Makefile +@@ -921,6 +921,8 @@ dtb-$(CONFIG_MACH_ARMADA_38X) += \ + armada-385-db-ap.dtb \ + armada-385-linksys-caiman.dtb \ + armada-385-linksys-cobra.dtb \ ++ armada-385-linksys-rango.dtb \ ++ armada-385-linksys-shelby.dtb \ + armada-388-clearfog.dtb \ + armada-388-db.dtb \ + armada-388-gp.dtb \ diff --git a/target/linux/mvebu/patches-4.9/100-find_active_root.patch b/target/linux/mvebu/patches-4.9/100-find_active_root.patch new file mode 100644 index 0000000000..b8e8c92aab --- /dev/null +++ b/target/linux/mvebu/patches-4.9/100-find_active_root.patch @@ -0,0 +1,60 @@ +The WRT1900AC among other Linksys routers uses a dual-firmware layout. +Dynamically rename the active partition to "ubi". + +Signed-off-by: Imre Kaloz <kaloz@openwrt.org> + +--- a/drivers/mtd/ofpart.c ++++ b/drivers/mtd/ofpart.c +@@ -25,6 +25,8 @@ static bool node_has_compatible(struct d + return of_get_property(pp, "compatible", NULL); + } + ++static int mangled_rootblock; ++ + static int parse_ofpart_partitions(struct mtd_info *master, + const struct mtd_partition **pparts, + struct mtd_part_parser_data *data) +@@ -33,6 +35,7 @@ static int parse_ofpart_partitions(struc + struct device_node *mtd_node; + struct device_node *ofpart_node; + const char *partname; ++ const char *owrtpart = "ubi"; + struct device_node *pp; + int nr_parts, i, ret = 0; + bool dedicated = true; +@@ -109,9 +112,13 @@ static int parse_ofpart_partitions(struc + parts[i].offset = of_read_number(reg, a_cells); + parts[i].size = of_read_number(reg + a_cells, s_cells); + +- partname = of_get_property(pp, "label", &len); +- if (!partname) +- partname = of_get_property(pp, "name", &len); ++ if (mangled_rootblock && (i == mangled_rootblock)) { ++ partname = owrtpart; ++ } else { ++ partname = of_get_property(pp, "label", &len); ++ if (!partname) ++ partname = of_get_property(pp, "name", &len); ++ } + parts[i].name = partname; + + if (of_get_property(pp, "read-only", &len)) +@@ -212,6 +219,18 @@ static int __init ofpart_parser_init(voi + return 0; + } + ++static int __init active_root(char *str) ++{ ++ get_option(&str, &mangled_rootblock); ++ ++ if (!mangled_rootblock) ++ return 1; ++ ++ return 1; ++} ++ ++__setup("mangled_rootblock=", active_root); ++ + static void __exit ofpart_parser_exit(void) + { + deregister_mtd_parser(&ofpart_parser); diff --git a/target/linux/mvebu/patches-4.9/102-revert_i2c_delay.patch b/target/linux/mvebu/patches-4.9/102-revert_i2c_delay.patch new file mode 100644 index 0000000000..473bdd9973 --- /dev/null +++ b/target/linux/mvebu/patches-4.9/102-revert_i2c_delay.patch @@ -0,0 +1,15 @@ +--- a/arch/arm/boot/dts/armada-xp.dtsi ++++ b/arch/arm/boot/dts/armada-xp.dtsi +@@ -85,12 +85,10 @@ + }; + + i2c0: i2c@11000 { +- compatible = "marvell,mv78230-i2c", "marvell,mv64xxx-i2c"; + reg = <0x11000 0x100>; + }; + + i2c1: i2c@11100 { +- compatible = "marvell,mv78230-i2c", "marvell,mv64xxx-i2c"; + reg = <0x11100 0x100>; + }; + diff --git a/target/linux/mvebu/patches-4.9/103-remove-nand-driver-bug.patch b/target/linux/mvebu/patches-4.9/103-remove-nand-driver-bug.patch new file mode 100644 index 0000000000..19a2a1e276 --- /dev/null +++ b/target/linux/mvebu/patches-4.9/103-remove-nand-driver-bug.patch @@ -0,0 +1,13 @@ +Remove a BUG() call that would crash on a race condition that should +otherwise be harmless. + +--- a/drivers/mtd/nand/pxa3xx_nand.c ++++ b/drivers/mtd/nand/pxa3xx_nand.c +@@ -727,7 +727,6 @@ static void handle_data_pio(struct pxa3x + default: + dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__, + info->state); +- BUG(); + } + + /* Update buffer pointers for multi-page read/write */ diff --git a/target/linux/mvebu/patches-4.9/104-linksys_mamba_disable_keep_config.patch b/target/linux/mvebu/patches-4.9/104-linksys_mamba_disable_keep_config.patch new file mode 100644 index 0000000000..4c6b3115ed --- /dev/null +++ b/target/linux/mvebu/patches-4.9/104-linksys_mamba_disable_keep_config.patch @@ -0,0 +1,10 @@ +--- a/arch/arm/boot/dts/armada-xp-linksys-mamba.dts ++++ b/arch/arm/boot/dts/armada-xp-linksys-mamba.dts +@@ -298,7 +298,6 @@ + nand@d0000 { + status = "okay"; + num-cs = <1>; +- marvell,nand-keep-config; + marvell,nand-enable-arbiter; + nand-on-flash-bbt; + nand-ecc-strength = <4>; diff --git a/target/linux/mvebu/patches-4.9/106-enable-bm-on-linksys-devices.patch b/target/linux/mvebu/patches-4.9/106-enable-bm-on-linksys-devices.patch new file mode 100644 index 0000000000..39e713898a --- /dev/null +++ b/target/linux/mvebu/patches-4.9/106-enable-bm-on-linksys-devices.patch @@ -0,0 +1,107 @@ +--- a/arch/arm/boot/dts/armada-385-linksys.dtsi ++++ b/arch/arm/boot/dts/armada-385-linksys.dtsi +@@ -59,7 +59,8 @@ + ranges = <MBUS_ID(0xf0, 0x01) 0 0xf1000000 0x100000 + MBUS_ID(0x01, 0x1d) 0 0xfff00000 0x100000 + MBUS_ID(0x09, 0x19) 0 0xf1100000 0x10000 +- MBUS_ID(0x09, 0x15) 0 0xf1110000 0x10000>; ++ MBUS_ID(0x09, 0x15) 0 0xf1110000 0x10000 ++ MBUS_ID(0x0c, 0x04) 0 0xf1200000 0x100000>; + + internal-regs { + i2c@11000 { +@@ -88,6 +89,9 @@ + ethernet@70000 { + status = "okay"; + phy-mode = "rgmii-id"; ++ buffer-manager = <&bm>; ++ bm,pool-long = <0>; ++ bm,pool-short = <3>; + fixed-link { + speed = <1000>; + full-duplex; +@@ -97,6 +101,9 @@ + ethernet@34000 { + status = "okay"; + phy-mode = "sgmii"; ++ buffer-manager = <&bm>; ++ bm,pool-long = <2>; ++ bm,pool-short = <3>; + fixed-link { + speed = <1000>; + full-duplex; +@@ -107,6 +114,10 @@ + status = "okay"; + }; + ++ bm@c8000 { ++ status = "okay"; ++ }; ++ + sata@a8000 { + status = "okay"; + }; +@@ -193,6 +204,10 @@ + }; + }; + ++ bm-bppi { ++ status = "okay"; ++ }; ++ + pcie-controller { + status = "okay"; + +--- a/arch/arm/boot/dts/armada-xp-linksys-mamba.dts ++++ b/arch/arm/boot/dts/armada-xp-linksys-mamba.dts +@@ -71,7 +71,8 @@ + ranges = <MBUS_ID(0xf0, 0x01) 0 0 0xf1000000 0x100000 + MBUS_ID(0x01, 0x1d) 0 0 0xfff00000 0x100000 + MBUS_ID(0x09, 0x09) 0 0 0xf1100000 0x10000 +- MBUS_ID(0x09, 0x05) 0 0 0xf1110000 0x10000>; ++ MBUS_ID(0x09, 0x05) 0 0 0xf1110000 0x10000 ++ MBUS_ID(0x0c, 0x04) 0 0 0xf1200000 0x100000>; + + pcie-controller { + status = "okay"; +@@ -205,6 +206,9 @@ + pinctrl-names = "default"; + status = "okay"; + phy-mode = "rgmii-id"; ++ buffer-manager = <&bm>; ++ bm,pool-long = <0>; ++ bm,pool-short = <3>; + fixed-link { + speed = <1000>; + full-duplex; +@@ -216,12 +220,19 @@ + pinctrl-names = "default"; + status = "okay"; + phy-mode = "rgmii-id"; ++ buffer-manager = <&bm>; ++ bm,pool-long = <1>; ++ bm,pool-short = <3>; + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + ++ bm@c0000 { ++ status = "okay"; ++ }; ++ + /* USB part of the eSATA/USB 2.0 port */ + usb@50000 { + status = "okay"; +@@ -367,6 +378,10 @@ + }; + }; + }; ++ ++ bm-bppi { ++ status = "okay"; ++ }; + }; + + gpio_keys { diff --git a/target/linux/mvebu/patches-4.9/110-pxa3xxx_revert_irq_thread.patch b/target/linux/mvebu/patches-4.9/110-pxa3xxx_revert_irq_thread.patch new file mode 100644 index 0000000000..419306ed90 --- /dev/null +++ b/target/linux/mvebu/patches-4.9/110-pxa3xxx_revert_irq_thread.patch @@ -0,0 +1,69 @@ +Revert "mtd: pxa3xx-nand: handle PIO in threaded interrupt" + +This reverts commit 24542257a3b987025d4b998ec2d15e556c98ad3f +This upstream change has been causing spurious timeouts on accesses +to the NAND flash if something else on the system is causing +significant latency. + +Nothing guarantees that the thread will run in time, so the +usual timeout is unreliable. + +Signed-off-by: Felix Fietkau <nbd@nbd.name> + +--- a/drivers/mtd/nand/pxa3xx_nand.c ++++ b/drivers/mtd/nand/pxa3xx_nand.c +@@ -791,24 +791,11 @@ static void start_data_dma(struct pxa3xx + __func__, direction, info->dma_cookie, info->sg.length); + } + +-static irqreturn_t pxa3xx_nand_irq_thread(int irq, void *data) +-{ +- struct pxa3xx_nand_info *info = data; +- +- handle_data_pio(info); +- +- info->state = STATE_CMD_DONE; +- nand_writel(info, NDSR, NDSR_WRDREQ | NDSR_RDDREQ); +- +- return IRQ_HANDLED; +-} +- + static irqreturn_t pxa3xx_nand_irq(int irq, void *devid) + { + struct pxa3xx_nand_info *info = devid; + unsigned int status, is_completed = 0, is_ready = 0; + unsigned int ready, cmd_done; +- irqreturn_t ret = IRQ_HANDLED; + + if (info->cs == 0) { + ready = NDSR_FLASH_RDY; +@@ -850,8 +837,7 @@ static irqreturn_t pxa3xx_nand_irq(int i + } else { + info->state = (status & NDSR_RDDREQ) ? + STATE_PIO_READING : STATE_PIO_WRITING; +- ret = IRQ_WAKE_THREAD; +- goto NORMAL_IRQ_EXIT; ++ handle_data_pio(info); + } + } + if (status & cmd_done) { +@@ -896,7 +882,7 @@ static irqreturn_t pxa3xx_nand_irq(int i + if (is_ready) + complete(&info->dev_ready); + NORMAL_IRQ_EXIT: +- return ret; ++ return IRQ_HANDLED; + } + + static inline int is_buf_blank(uint8_t *buf, size_t len) +@@ -1856,9 +1842,7 @@ static int alloc_nand_resource(struct pl + /* initialize all interrupts to be disabled */ + disable_int(info, NDSR_MASK); + +- ret = request_threaded_irq(irq, pxa3xx_nand_irq, +- pxa3xx_nand_irq_thread, IRQF_ONESHOT, +- pdev->name, info); ++ ret = request_irq(irq, pxa3xx_nand_irq, 0, pdev->name, info); + if (ret < 0) { + dev_err(&pdev->dev, "failed to request IRQ\n"); + goto fail_free_buf; diff --git a/target/linux/mvebu/patches-4.9/120-net-mvneta-add-BQL-support.patch b/target/linux/mvebu/patches-4.9/120-net-mvneta-add-BQL-support.patch new file mode 100644 index 0000000000..0b822f3863 --- /dev/null +++ b/target/linux/mvebu/patches-4.9/120-net-mvneta-add-BQL-support.patch @@ -0,0 +1,83 @@ +--- a/drivers/net/ethernet/marvell/mvneta.c ++++ b/drivers/net/ethernet/marvell/mvneta.c +@@ -1719,8 +1719,10 @@ static struct mvneta_tx_queue *mvneta_tx + + /* Free tx queue skbuffs */ + static void mvneta_txq_bufs_free(struct mvneta_port *pp, +- struct mvneta_tx_queue *txq, int num) ++ struct mvneta_tx_queue *txq, int num, ++ struct netdev_queue *nq) + { ++ unsigned int bytes_compl = 0, pkts_compl = 0; + int i; + + for (i = 0; i < num; i++) { +@@ -1728,6 +1730,11 @@ static void mvneta_txq_bufs_free(struct + txq->txq_get_index; + struct sk_buff *skb = txq->tx_skb[txq->txq_get_index]; + ++ if (skb) { ++ bytes_compl += skb->len; ++ pkts_compl++; ++ } ++ + mvneta_txq_inc_get(txq); + + if (!IS_TSO_HEADER(txq, tx_desc->buf_phys_addr)) +@@ -1738,6 +1745,8 @@ static void mvneta_txq_bufs_free(struct + continue; + dev_kfree_skb_any(skb); + } ++ ++ netdev_tx_completed_queue(nq, pkts_compl, bytes_compl); + } + + /* Handle end of transmission */ +@@ -1751,7 +1760,7 @@ static void mvneta_txq_done(struct mvnet + if (!tx_done) + return; + +- mvneta_txq_bufs_free(pp, txq, tx_done); ++ mvneta_txq_bufs_free(pp, txq, tx_done, nq); + + txq->count -= tx_done; + +@@ -2358,6 +2367,8 @@ out: + struct mvneta_pcpu_stats *stats = this_cpu_ptr(pp->stats); + struct netdev_queue *nq = netdev_get_tx_queue(dev, txq_id); + ++ netdev_tx_sent_queue(nq, len); ++ + txq->count += frags; + mvneta_txq_pend_desc_add(pp, txq, frags); + +@@ -2382,9 +2393,10 @@ static void mvneta_txq_done_force(struct + struct mvneta_tx_queue *txq) + + { ++ struct netdev_queue *nq = netdev_get_tx_queue(pp->dev, txq->id); + int tx_done = txq->count; + +- mvneta_txq_bufs_free(pp, txq, tx_done); ++ mvneta_txq_bufs_free(pp, txq, tx_done, nq); + + /* reset txq */ + txq->count = 0; +@@ -2882,6 +2894,8 @@ static int mvneta_txq_init(struct mvneta + static void mvneta_txq_deinit(struct mvneta_port *pp, + struct mvneta_tx_queue *txq) + { ++ struct netdev_queue *nq = netdev_get_tx_queue(pp->dev, txq->id); ++ + kfree(txq->tx_skb); + + if (txq->tso_hdrs) +@@ -2893,6 +2907,8 @@ static void mvneta_txq_deinit(struct mvn + txq->size * MVNETA_DESC_ALIGNED_SIZE, + txq->descs, txq->descs_phys); + ++ netdev_tx_reset_queue(nq); ++ + txq->descs = NULL; + txq->last_desc = 0; + txq->next_desc_to_proc = 0; diff --git a/target/linux/mvebu/patches-4.9/200-gpio_mvebu_add_limited_pwm_support.patch b/target/linux/mvebu/patches-4.9/200-gpio_mvebu_add_limited_pwm_support.patch new file mode 100644 index 0000000000..0261b1e538 --- /dev/null +++ b/target/linux/mvebu/patches-4.9/200-gpio_mvebu_add_limited_pwm_support.patch @@ -0,0 +1,433 @@ +Armada 370/XP devices can 'blink' gpio lines with a configurable on +and off period. This can be modelled as a PWM. + +However, there are only two sets of PWM configuration registers for +all the gpio lines. This driver simply allows a single gpio line per +gpio chip of 32 lines to be used as a PWM. Attempts to use more return +EBUSY. + +Due to the interleaving of registers it is not simple to separate the +PWM driver from the gpio driver. Thus the gpio driver has been +extended with a PWM driver. + +Signed-off-by: Andrew Lunn <andrew@lunn.ch> +--- + drivers/gpio/Kconfig | 5 ++ + drivers/gpio/Makefile | 1 + + drivers/gpio/gpio-mvebu-pwm.c | 202 ++++++++++++++++++++++++++++++++++++++++++ + drivers/gpio/gpio-mvebu.c | 37 +++----- + drivers/gpio/gpio-mvebu.h | 79 +++++++++++++++++ + 5 files changed, 299 insertions(+), 25 deletions(-) + create mode 100644 drivers/gpio/gpio-mvebu-pwm.c + create mode 100644 drivers/gpio/gpio-mvebu.h + +--- a/drivers/gpio/Kconfig ++++ b/drivers/gpio/Kconfig +@@ -319,6 +319,11 @@ config GPIO_MVEBU + depends on OF_GPIO + select GENERIC_IRQ_CHIP + ++config GPIO_MVEBU_PWM ++ def_bool y ++ depends on GPIO_MVEBU ++ depends on PWM ++ + config GPIO_MXC + def_bool y + depends on ARCH_MXC +--- a/drivers/gpio/Makefile ++++ b/drivers/gpio/Makefile +@@ -81,6 +81,7 @@ obj-$(CONFIG_GPIO_MPC5200) += gpio-mpc52 + obj-$(CONFIG_GPIO_MPC8XXX) += gpio-mpc8xxx.o + obj-$(CONFIG_GPIO_MSIC) += gpio-msic.o + obj-$(CONFIG_GPIO_MVEBU) += gpio-mvebu.o ++obj-$(CONFIG_GPIO_MVEBU_PWM) += gpio-mvebu-pwm.o + obj-$(CONFIG_GPIO_MXC) += gpio-mxc.o + obj-$(CONFIG_GPIO_MXS) += gpio-mxs.o + obj-$(CONFIG_GPIO_OCTEON) += gpio-octeon.o +--- /dev/null ++++ b/drivers/gpio/gpio-mvebu-pwm.c +@@ -0,0 +1,202 @@ ++#include <linux/err.h> ++#include <linux/module.h> ++#include <linux/gpio.h> ++#include <linux/pwm.h> ++#include <linux/clk.h> ++#include <linux/platform_device.h> ++#include "gpio-mvebu.h" ++#include "gpiolib.h" ++ ++static void __iomem *mvebu_gpioreg_blink_select(struct mvebu_gpio_chip *mvchip) ++{ ++ return mvchip->membase + GPIO_BLINK_CNT_SELECT; ++} ++ ++static inline struct mvebu_pwm *to_mvebu_pwm(struct pwm_chip *chip) ++{ ++ return container_of(chip, struct mvebu_pwm, chip); ++} ++ ++static inline struct mvebu_gpio_chip *to_mvchip(struct mvebu_pwm *pwm) ++{ ++ return container_of(pwm, struct mvebu_gpio_chip, pwm); ++} ++ ++static int mvebu_pwm_request(struct pwm_chip *chip, struct pwm_device *pwmd) ++{ ++ struct mvebu_pwm *pwm = to_mvebu_pwm(chip); ++ struct mvebu_gpio_chip *mvchip = to_mvchip(pwm); ++ struct gpio_desc *desc = gpio_to_desc(pwmd->pwm); ++ unsigned long flags; ++ int ret = 0; ++ ++ spin_lock_irqsave(&pwm->lock, flags); ++ if (pwm->used) { ++ ret = -EBUSY; ++ } else { ++ if (!desc) { ++ ret = -ENODEV; ++ goto out; ++ } ++ ret = gpiod_request(desc, "mvebu-pwm"); ++ if (ret) ++ goto out; ++ ++ ret = gpiod_direction_output(desc, 0); ++ if (ret) { ++ gpiod_free(desc); ++ goto out; ++ } ++ ++ pwm->pin = pwmd->pwm - mvchip->chip.base; ++ pwm->used = true; ++ } ++ ++out: ++ spin_unlock_irqrestore(&pwm->lock, flags); ++ return ret; ++} ++ ++static void mvebu_pwm_free(struct pwm_chip *chip, struct pwm_device *pwmd) ++{ ++ struct mvebu_pwm *pwm = to_mvebu_pwm(chip); ++ struct gpio_desc *desc = gpio_to_desc(pwmd->pwm); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&pwm->lock, flags); ++ gpiod_free(desc); ++ pwm->used = false; ++ spin_unlock_irqrestore(&pwm->lock, flags); ++} ++ ++static int mvebu_pwm_config(struct pwm_chip *chip, struct pwm_device *pwmd, ++ int duty_ns, int period_ns) ++{ ++ struct mvebu_pwm *pwm = to_mvebu_pwm(chip); ++ struct mvebu_gpio_chip *mvchip = to_mvchip(pwm); ++ unsigned int on, off; ++ unsigned long long val; ++ u32 u; ++ ++ val = (unsigned long long) pwm->clk_rate * duty_ns; ++ do_div(val, NSEC_PER_SEC); ++ if (val > UINT_MAX) ++ return -EINVAL; ++ if (val) ++ on = val; ++ else ++ on = 1; ++ ++ val = (unsigned long long) pwm->clk_rate * (period_ns - duty_ns); ++ do_div(val, NSEC_PER_SEC); ++ if (val > UINT_MAX) ++ return -EINVAL; ++ if (val) ++ off = val; ++ else ++ off = 1; ++ ++ u = readl_relaxed(mvebu_gpioreg_blink_select(mvchip)); ++ u &= ~(1 << pwm->pin); ++ u |= (pwm->id << pwm->pin); ++ writel_relaxed(u, mvebu_gpioreg_blink_select(mvchip)); ++ ++ writel_relaxed(on, pwm->membase + BLINK_ON_DURATION); ++ writel_relaxed(off, pwm->membase + BLINK_OFF_DURATION); ++ ++ return 0; ++} ++ ++static int mvebu_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwmd) ++{ ++ struct mvebu_pwm *pwm = to_mvebu_pwm(chip); ++ struct mvebu_gpio_chip *mvchip = to_mvchip(pwm); ++ ++ mvebu_gpio_blink(&mvchip->chip, pwm->pin, 1); ++ ++ return 0; ++} ++ ++static void mvebu_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwmd) ++{ ++ struct mvebu_pwm *pwm = to_mvebu_pwm(chip); ++ struct mvebu_gpio_chip *mvchip = to_mvchip(pwm); ++ ++ mvebu_gpio_blink(&mvchip->chip, pwm->pin, 0); ++} ++ ++static const struct pwm_ops mvebu_pwm_ops = { ++ .request = mvebu_pwm_request, ++ .free = mvebu_pwm_free, ++ .config = mvebu_pwm_config, ++ .enable = mvebu_pwm_enable, ++ .disable = mvebu_pwm_disable, ++ .owner = THIS_MODULE, ++}; ++ ++void mvebu_pwm_suspend(struct mvebu_gpio_chip *mvchip) ++{ ++ struct mvebu_pwm *pwm = &mvchip->pwm; ++ ++ pwm->blink_select = readl_relaxed(mvebu_gpioreg_blink_select(mvchip)); ++ pwm->blink_on_duration = ++ readl_relaxed(pwm->membase + BLINK_ON_DURATION); ++ pwm->blink_off_duration = ++ readl_relaxed(pwm->membase + BLINK_OFF_DURATION); ++} ++ ++void mvebu_pwm_resume(struct mvebu_gpio_chip *mvchip) ++{ ++ struct mvebu_pwm *pwm = &mvchip->pwm; ++ ++ writel_relaxed(pwm->blink_select, mvebu_gpioreg_blink_select(mvchip)); ++ writel_relaxed(pwm->blink_on_duration, ++ pwm->membase + BLINK_ON_DURATION); ++ writel_relaxed(pwm->blink_off_duration, ++ pwm->membase + BLINK_OFF_DURATION); ++} ++ ++/* ++ * Armada 370/XP has simple PWM support for gpio lines. Other SoCs ++ * don't have this hardware. So if we don't have the necessary ++ * resource, it is not an error. ++ */ ++int mvebu_pwm_probe(struct platform_device *pdev, ++ struct mvebu_gpio_chip *mvchip, ++ int id) ++{ ++ struct device *dev = &pdev->dev; ++ struct mvebu_pwm *pwm = &mvchip->pwm; ++ struct resource *res; ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pwm"); ++ if (!res) ++ return 0; ++ ++ mvchip->pwm.membase = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(mvchip->pwm.membase)) ++ return PTR_ERR(mvchip->percpu_membase); ++ ++ if (id < 0 || id > 1) ++ return -EINVAL; ++ pwm->id = id; ++ ++ if (IS_ERR(mvchip->clk)) ++ return PTR_ERR(mvchip->clk); ++ ++ pwm->clk_rate = clk_get_rate(mvchip->clk); ++ if (!pwm->clk_rate) { ++ dev_err(dev, "failed to get clock rate\n"); ++ return -EINVAL; ++ } ++ ++ pwm->chip.dev = dev; ++ pwm->chip.ops = &mvebu_pwm_ops; ++ pwm->chip.base = mvchip->chip.base; ++ pwm->chip.npwm = mvchip->chip.ngpio; ++ pwm->chip.can_sleep = false; ++ ++ spin_lock_init(&pwm->lock); ++ ++ return pwmchip_add(&pwm->chip); ++} +--- a/drivers/gpio/gpio-mvebu.c ++++ b/drivers/gpio/gpio-mvebu.c +@@ -42,10 +42,11 @@ + #include <linux/io.h> + #include <linux/of_irq.h> + #include <linux/of_device.h> ++#include <linux/pwm.h> + #include <linux/clk.h> + #include <linux/pinctrl/consumer.h> + #include <linux/irqchip/chained_irq.h> +- ++#include "gpio-mvebu.h" + /* + * GPIO unit register offsets. + */ +@@ -75,24 +76,6 @@ + + #define MVEBU_MAX_GPIO_PER_BANK 32 + +-struct mvebu_gpio_chip { +- struct gpio_chip chip; +- spinlock_t lock; +- void __iomem *membase; +- void __iomem *percpu_membase; +- int irqbase; +- struct irq_domain *domain; +- int soc_variant; +- +- /* Used to preserve GPIO registers across suspend/resume */ +- u32 out_reg; +- u32 io_conf_reg; +- u32 blink_en_reg; +- u32 in_pol_reg; +- u32 edge_mask_regs[4]; +- u32 level_mask_regs[4]; +-}; +- + /* + * Functions returning addresses of individual registers for a given + * GPIO controller. +@@ -216,7 +199,7 @@ static int mvebu_gpio_get(struct gpio_ch + return (u >> pin) & 1; + } + +-static void mvebu_gpio_blink(struct gpio_chip *chip, unsigned pin, int value) ++void mvebu_gpio_blink(struct gpio_chip *chip, unsigned pin, int value) + { + struct mvebu_gpio_chip *mvchip = gpiochip_get_data(chip); + unsigned long flags; +@@ -596,6 +579,8 @@ static int mvebu_gpio_suspend(struct pla + BUG(); + } + ++ mvebu_pwm_suspend(mvchip); ++ + return 0; + } + +@@ -639,6 +624,8 @@ static int mvebu_gpio_resume(struct plat + BUG(); + } + ++ mvebu_pwm_resume(mvchip); ++ + return 0; + } + +@@ -650,7 +637,6 @@ static int mvebu_gpio_probe(struct platf + struct resource *res; + struct irq_chip_generic *gc; + struct irq_chip_type *ct; +- struct clk *clk; + unsigned int ngpios; + bool have_irqs; + int soc_variant; +@@ -684,10 +670,10 @@ static int mvebu_gpio_probe(struct platf + return id; + } + +- clk = devm_clk_get(&pdev->dev, NULL); ++ mvchip->clk = devm_clk_get(&pdev->dev, NULL); + /* Not all SoCs require a clock.*/ +- if (!IS_ERR(clk)) +- clk_prepare_enable(clk); ++ if (!IS_ERR(mvchip->clk)) ++ clk_prepare_enable(mvchip->clk); + + mvchip->soc_variant = soc_variant; + mvchip->chip.label = dev_name(&pdev->dev); +@@ -814,7 +800,8 @@ static int mvebu_gpio_probe(struct platf + mvchip); + } + +- return 0; ++ /* Armada 370/XP has simple PWM support for gpio lines */ ++ return mvebu_pwm_probe(pdev, mvchip, id); + + err_domain: + irq_domain_remove(mvchip->domain); +--- /dev/null ++++ b/drivers/gpio/gpio-mvebu.h +@@ -0,0 +1,79 @@ ++/* ++ * Interface between MVEBU GPIO driver and PWM driver for GPIO pins ++ * ++ * Copyright (C) 2015, Andrew Lunn <andrew@lunn.ch> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#ifndef MVEBU_GPIO_PWM_H ++#define MVEBU_GPIO_PWM_H ++ ++#define BLINK_ON_DURATION 0x0 ++#define BLINK_OFF_DURATION 0x4 ++#define GPIO_BLINK_CNT_SELECT 0x0020 ++ ++struct mvebu_pwm { ++ void __iomem *membase; ++ unsigned long clk_rate; ++ bool used; ++ unsigned pin; ++ struct pwm_chip chip; ++ int id; ++ spinlock_t lock; ++ ++ /* Used to preserve GPIO/PWM registers across suspend / ++ * resume */ ++ u32 blink_select; ++ u32 blink_on_duration; ++ u32 blink_off_duration; ++}; ++ ++struct mvebu_gpio_chip { ++ struct gpio_chip chip; ++ spinlock_t lock; ++ void __iomem *membase; ++ void __iomem *percpu_membase; ++ int irqbase; ++ struct irq_domain *domain; ++ int soc_variant; ++ struct clk *clk; ++#ifdef CONFIG_PWM ++ struct mvebu_pwm pwm; ++#endif ++ /* Used to preserve GPIO registers across suspend/resume */ ++ u32 out_reg; ++ u32 io_conf_reg; ++ u32 blink_en_reg; ++ u32 in_pol_reg; ++ u32 edge_mask_regs[4]; ++ u32 level_mask_regs[4]; ++}; ++ ++void mvebu_gpio_blink(struct gpio_chip *chip, unsigned pin, int value); ++ ++#ifdef CONFIG_PWM ++int mvebu_pwm_probe(struct platform_device *pdev, ++ struct mvebu_gpio_chip *mvchip, ++ int id); ++void mvebu_pwm_suspend(struct mvebu_gpio_chip *mvchip); ++void mvebu_pwm_resume(struct mvebu_gpio_chip *mvchip); ++#else ++int mvebu_pwm_probe(struct platform_device *pdev, ++ struct mvebu_gpio_chip *mvchip, ++ int id) ++{ ++ return 0; ++} ++ ++void mvebu_pwm_suspend(struct mvebu_gpio_chip *mvchip) ++{ ++} ++ ++void mvebu_pwm_resume(struct mvebu_gpio_chip *mvchip) ++{ ++} ++#endif ++#endif diff --git a/target/linux/mvebu/patches-4.9/201-dt_bindings_extend_mvebu_gpio_documentation_with_pwm.patch b/target/linux/mvebu/patches-4.9/201-dt_bindings_extend_mvebu_gpio_documentation_with_pwm.patch new file mode 100644 index 0000000000..48f93944bf --- /dev/null +++ b/target/linux/mvebu/patches-4.9/201-dt_bindings_extend_mvebu_gpio_documentation_with_pwm.patch @@ -0,0 +1,52 @@ +Document the optional parameters needed for PWM operation of gpio +lines. + +Signed-off-by: Andrew Lunn <andrew@lunn.ch> +--- + .../devicetree/bindings/gpio/gpio-mvebu.txt | 31 ++++++++++++++++++++++ + 1 file changed, 31 insertions(+) + +--- a/Documentation/devicetree/bindings/gpio/gpio-mvebu.txt ++++ b/Documentation/devicetree/bindings/gpio/gpio-mvebu.txt +@@ -38,6 +38,23 @@ Required properties: + - #gpio-cells: Should be two. The first cell is the pin number. The + second cell is reserved for flags, unused at the moment. + ++Optional properties: ++ ++In order to use the gpio lines in PWM mode, some additional optional ++properties are required. Only Armada 370 and XP supports these ++properties. ++ ++- reg: an additional register set is needed, for the GPIO Blink ++ Counter on/off registers. ++ ++- reg-names: Must contain an entry "pwm" corresponding to the ++ additional register range needed for pwm operation. ++ ++- #pwm-cells: Should be two. The first cell is the pin number. The ++ second cell is reserved for flags, unused at the moment. ++ ++- clocks: Must be a phandle to the clock for the gpio controller. ++ + Example: + + gpio0: gpio@d0018100 { +@@ -51,3 +68,17 @@ Example: + #interrupt-cells = <2>; + interrupts = <16>, <17>, <18>, <19>; + }; ++ ++ gpio1: gpio@18140 { ++ compatible = "marvell,orion-gpio"; ++ reg = <0x18140 0x40>, <0x181c8 0x08>; ++ reg-names = "gpio", "pwm"; ++ ngpios = <17>; ++ gpio-controller; ++ #gpio-cells = <2>; ++ #pwm-cells = <2>; ++ interrupt-controller; ++ #interrupt-cells = <2>; ++ interrupts = <87>, <88>, <89>; ++ clocks = <&coreclk 0>; ++ }; diff --git a/target/linux/mvebu/patches-4.9/202-mvebu_xp_add_pwm_properties_to_dtsi_files.patch b/target/linux/mvebu/patches-4.9/202-mvebu_xp_add_pwm_properties_to_dtsi_files.patch new file mode 100644 index 0000000000..90e951e2d2 --- /dev/null +++ b/target/linux/mvebu/patches-4.9/202-mvebu_xp_add_pwm_properties_to_dtsi_files.patch @@ -0,0 +1,149 @@ +Add properties to the gpio nodes to allow them to be also used +as pwm lines. + +Signed-off-by: Andrew Lunn <andrew@lunn.ch> +--- + arch/arm/boot/dts/armada-370.dtsi | 10 ++++++++-- + arch/arm/boot/dts/armada-xp-mv78230.dtsi | 10 ++++++++-- + arch/arm/boot/dts/armada-xp-mv78260.dtsi | 8 ++++++-- + arch/arm/boot/dts/armada-xp-mv78460.dtsi | 10 ++++++++-- + 4 files changed, 30 insertions(+), 8 deletions(-) + +--- a/arch/arm/boot/dts/armada-370.dtsi ++++ b/arch/arm/boot/dts/armada-370.dtsi +@@ -144,24 +144,30 @@ + + gpio0: gpio@18100 { + compatible = "marvell,orion-gpio"; +- reg = <0x18100 0x40>; ++ reg = <0x18100 0x40>, <0x181c0 0x08>; ++ reg-names = "gpio", "pwm"; + ngpios = <32>; + gpio-controller; + #gpio-cells = <2>; ++ #pwm-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + interrupts = <82>, <83>, <84>, <85>; ++ clocks = <&coreclk 0>; + }; + + gpio1: gpio@18140 { + compatible = "marvell,orion-gpio"; +- reg = <0x18140 0x40>; ++ reg = <0x18140 0x40>, <0x181c8 0x08>; ++ reg-names = "gpio", "pwm"; + ngpios = <32>; + gpio-controller; + #gpio-cells = <2>; ++ #pwm-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + interrupts = <87>, <88>, <89>, <90>; ++ clocks = <&coreclk 0>; + }; + + gpio2: gpio@18180 { +--- a/arch/arm/boot/dts/armada-xp-mv78230.dtsi ++++ b/arch/arm/boot/dts/armada-xp-mv78230.dtsi +@@ -203,24 +203,30 @@ + internal-regs { + gpio0: gpio@18100 { + compatible = "marvell,orion-gpio"; +- reg = <0x18100 0x40>; ++ reg = <0x18100 0x40>, <0x181c0 0x08>; ++ reg-names = "gpio", "pwm"; + ngpios = <32>; + gpio-controller; + #gpio-cells = <2>; ++ #pwm-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + interrupts = <82>, <83>, <84>, <85>; ++ clocks = <&coreclk 0>; + }; + + gpio1: gpio@18140 { + compatible = "marvell,orion-gpio"; +- reg = <0x18140 0x40>; ++ reg = <0x18140 0x40>, <0x181c8 0x08>; ++ reg-names = "gpio", "pwm"; + ngpios = <17>; + gpio-controller; + #gpio-cells = <2>; ++ #pwm-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + interrupts = <87>, <88>, <89>; ++ clocks = <&coreclk 0>; + }; + }; + }; +--- a/arch/arm/boot/dts/armada-xp-mv78260.dtsi ++++ b/arch/arm/boot/dts/armada-xp-mv78260.dtsi +@@ -286,24 +286,28 @@ + internal-regs { + gpio0: gpio@18100 { + compatible = "marvell,orion-gpio"; +- reg = <0x18100 0x40>; ++ reg = <0x18100 0x40>, <0x181c0 0x08>; ++ reg-names = "gpio", "pwm"; + ngpios = <32>; + gpio-controller; + #gpio-cells = <2>; ++ #pwm-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + interrupts = <82>, <83>, <84>, <85>; ++ clocks = <&coreclk 0>; + }; + + gpio1: gpio@18140 { + compatible = "marvell,orion-gpio"; +- reg = <0x18140 0x40>; ++ reg = <0x18140 0x40>, <0x181c8 0x08>; + ngpios = <32>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + interrupts = <87>, <88>, <89>, <90>; ++ clocks = <&coreclk 0>; + }; + + gpio2: gpio@18180 { +--- a/arch/arm/boot/dts/armada-xp-mv78460.dtsi ++++ b/arch/arm/boot/dts/armada-xp-mv78460.dtsi +@@ -324,24 +324,30 @@ + internal-regs { + gpio0: gpio@18100 { + compatible = "marvell,orion-gpio"; +- reg = <0x18100 0x40>; ++ reg = <0x18100 0x40>, <0x181c0 0x08>; ++ reg-names = "gpio", "pwm"; + ngpios = <32>; + gpio-controller; + #gpio-cells = <2>; ++ #pwm-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + interrupts = <82>, <83>, <84>, <85>; ++ clocks = <&coreclk 0>; + }; + + gpio1: gpio@18140 { + compatible = "marvell,orion-gpio"; +- reg = <0x18140 0x40>; ++ reg = <0x18140 0x40>, <0x181c8 0x08>; ++ reg-names = "gpio", "pwm"; + ngpios = <32>; + gpio-controller; + #gpio-cells = <2>; ++ #pwm-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + interrupts = <87>, <88>, <89>, <90>; ++ clocks = <&coreclk 0>; + }; + + gpio2: gpio@18180 { diff --git a/target/linux/mvebu/patches-4.9/203-arm_mvebu_enable_pwm_in_defconfig.patch b/target/linux/mvebu/patches-4.9/203-arm_mvebu_enable_pwm_in_defconfig.patch new file mode 100644 index 0000000000..21eb24da22 --- /dev/null +++ b/target/linux/mvebu/patches-4.9/203-arm_mvebu_enable_pwm_in_defconfig.patch @@ -0,0 +1,18 @@ +Now that the gpio driver also supports PWM operation, enable +the PWM framework in mvebu_v7_defconfig. + +Signed-off-by: Andrew Lunn <andrew@lunn.ch> +--- + arch/arm/configs/mvebu_v7_defconfig | 1 + + 1 file changed, 1 insertion(+) + +--- a/arch/arm/configs/mvebu_v7_defconfig ++++ b/arch/arm/configs/mvebu_v7_defconfig +@@ -135,6 +135,7 @@ CONFIG_DMADEVICES=y + CONFIG_MV_XOR=y + # CONFIG_IOMMU_SUPPORT is not set + CONFIG_MEMORY=y ++CONFIG_PWM=y + CONFIG_EXT4_FS=y + CONFIG_ISO9660_FS=y + CONFIG_JOLIET=y diff --git a/target/linux/mvebu/patches-4.9/204-mvebu_wrt1900ac_use_pwm-fan_rather_than_gpio-fan.patch b/target/linux/mvebu/patches-4.9/204-mvebu_wrt1900ac_use_pwm-fan_rather_than_gpio-fan.patch new file mode 100644 index 0000000000..b46697b727 --- /dev/null +++ b/target/linux/mvebu/patches-4.9/204-mvebu_wrt1900ac_use_pwm-fan_rather_than_gpio-fan.patch @@ -0,0 +1,28 @@ +The mvebu gpio driver can also perform PWM on some pins. Us the +pwm-fan driver to control the fan of the WRT1900AC, giving us fine +grain control over its speed and hence noise. + +Signed-off-by: Andrew Lunn <andrew@lunn.ch> +--- + arch/arm/boot/dts/armada-xp-wrt1900ac.dts | 8 +++----- + 1 file changed, 3 insertions(+), 5 deletions(-) + +--- a/arch/arm/boot/dts/armada-xp-linksys-mamba.dts ++++ b/arch/arm/boot/dts/armada-xp-linksys-mamba.dts +@@ -416,13 +416,11 @@ + }; + }; + +- gpio_fan { ++ pwm_fan { + /* SUNON HA4010V4-0000-C99 */ +- compatible = "gpio-fan"; +- gpios = <&gpio0 24 0>; + +- gpio-fan,speed-map = <0 0 +- 4500 1>; ++ compatible = "pwm-fan"; ++ pwms = <&gpio0 24 4000 0>; + }; + + dsa@0 { diff --git a/target/linux/mvebu/patches-4.9/205-armada-385-rd-mtd-partitions.patch b/target/linux/mvebu/patches-4.9/205-armada-385-rd-mtd-partitions.patch new file mode 100644 index 0000000000..e75a5eebda --- /dev/null +++ b/target/linux/mvebu/patches-4.9/205-armada-385-rd-mtd-partitions.patch @@ -0,0 +1,19 @@ +--- a/arch/arm/boot/dts/armada-388-rd.dts ++++ b/arch/arm/boot/dts/armada-388-rd.dts +@@ -140,6 +140,16 @@ + compatible = "st,m25p128", "jedec,spi-nor"; + reg = <0>; /* Chip select 0 */ + spi-max-frequency = <108000000>; ++ ++ partition@0 { ++ label = "uboot"; ++ reg = <0 0x400000>; ++ }; ++ ++ partition@1 { ++ label = "firmware"; ++ reg = <0x400000 0xc00000>; ++ }; + }; + }; + diff --git a/target/linux/mvebu/patches-4.9/206-ARM-mvebu-385-ap-Add-partitions.patch b/target/linux/mvebu/patches-4.9/206-ARM-mvebu-385-ap-Add-partitions.patch new file mode 100644 index 0000000000..b7af272360 --- /dev/null +++ b/target/linux/mvebu/patches-4.9/206-ARM-mvebu-385-ap-Add-partitions.patch @@ -0,0 +1,35 @@ +From 9861f93a59142a3131870df2521eb2deb73026d7 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime.ripard@free-electrons.com> +Date: Tue, 13 Jan 2015 11:14:09 +0100 +Subject: [PATCH 2/2] ARM: mvebu: 385-ap: Add partitions + +Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> +--- + arch/arm/boot/dts/armada-385-db-ap.dts | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + +--- a/arch/arm/boot/dts/armada-385-db-ap.dts ++++ b/arch/arm/boot/dts/armada-385-db-ap.dts +@@ -181,19 +181,19 @@ + #size-cells = <1>; + + partition@0 { +- label = "U-Boot"; ++ label = "u-boot"; + reg = <0x00000000 0x00800000>; + read-only; + }; + + partition@800000 { +- label = "uImage"; ++ label = "kernel"; + reg = <0x00800000 0x00400000>; + read-only; + }; + + partition@c00000 { +- label = "Root"; ++ label = "ubi"; + reg = <0x00c00000 0x3f400000>; + }; + }; diff --git a/target/linux/mvebu/patches-4.9/210-clearfog_switch_node.patch b/target/linux/mvebu/patches-4.9/210-clearfog_switch_node.patch new file mode 100644 index 0000000000..022dc6f5de --- /dev/null +++ b/target/linux/mvebu/patches-4.9/210-clearfog_switch_node.patch @@ -0,0 +1,21 @@ +--- a/arch/arm/boot/dts/armada-388-clearfog.dts ++++ b/arch/arm/boot/dts/armada-388-clearfog.dts +@@ -406,6 +406,18 @@ + }; + }; + ++ mvsw61xx { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ compatible = "marvell,88e6176"; ++ status = "okay"; ++ reg = <0x4>; ++ is-indirect; ++ ++ mii-bus = <&mdio>; ++ cpu-port-0 = <5>; ++ }; ++ + gpio-keys { + compatible = "gpio-keys"; + pinctrl-0 = <&rear_button_pins>; diff --git a/target/linux/mvebu/patches-4.9/220-ARM-dts-armada388-clearfog-add-SFP-module-support.patch b/target/linux/mvebu/patches-4.9/220-ARM-dts-armada388-clearfog-add-SFP-module-support.patch new file mode 100644 index 0000000000..e7aa5c4cb7 --- /dev/null +++ b/target/linux/mvebu/patches-4.9/220-ARM-dts-armada388-clearfog-add-SFP-module-support.patch @@ -0,0 +1,84 @@ +From 63ff73593c2f5d3fc1cba479321d192caaca48aa Mon Sep 17 00:00:00 2001 +From: Russell King <rmk+kernel@arm.linux.org.uk> +Date: Sat, 12 Sep 2015 18:43:39 +0100 +Subject: [PATCH 738/744] ARM: dts: armada388-clearfog: add SFP module support + +Add SFP module support for Clearfog using the SFP phylink support. + +Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> +--- + arch/arm/boot/dts/armada-388-clearfog.dts | 44 ++++++++----------------------- + 1 file changed, 11 insertions(+), 33 deletions(-) + +--- a/arch/arm/boot/dts/armada-388-clearfog.dts ++++ b/arch/arm/boot/dts/armada-388-clearfog.dts +@@ -90,16 +90,12 @@ + }; + + ethernet@34000 { ++ managed = "in-band-status"; + phy-mode = "sgmii"; + buffer-manager = <&bm>; + bm,pool-long = <3>; + bm,pool-short = <1>; + status = "okay"; +- +- fixed-link { +- speed = <1000>; +- full-duplex; +- }; + }; + + i2c@11000 { +@@ -183,34 +179,6 @@ + output-low; + line-name = "m.2 devslp"; + }; +- sfp_los { +- /* SFP loss of signal */ +- gpio-hog; +- gpios = <12 GPIO_ACTIVE_HIGH>; +- input; +- line-name = "sfp-los"; +- }; +- sfp_tx_fault { +- /* SFP laser fault */ +- gpio-hog; +- gpios = <13 GPIO_ACTIVE_HIGH>; +- input; +- line-name = "sfp-tx-fault"; +- }; +- sfp_tx_disable { +- /* SFP transmit disable */ +- gpio-hog; +- gpios = <14 GPIO_ACTIVE_HIGH>; +- output-low; +- line-name = "sfp-tx-disable"; +- }; +- sfp_mod_def0 { +- /* SFP module present */ +- gpio-hog; +- gpios = <15 GPIO_ACTIVE_LOW>; +- input; +- line-name = "sfp-mod-def0"; +- }; + }; + + /* The MCP3021 is 100kHz clock only */ +@@ -350,6 +318,16 @@ + }; + }; + ++ sfp: sfp { ++ compatible = "sff,sfp"; ++ i2c-bus = <&i2c1>; ++ los-gpio = <&expander0 12 GPIO_ACTIVE_HIGH>; ++ moddef0-gpio = <&expander0 15 GPIO_ACTIVE_LOW>; ++ sfp,ethernet = <ð2>; ++ tx-disable-gpio = <&expander0 14 GPIO_ACTIVE_HIGH>; ++ tx-fault-gpio = <&expander0 13 GPIO_ACTIVE_HIGH>; ++ }; ++ + dsa@0 { + compatible = "marvell,dsa"; + dsa,ethernet = <ð1>; diff --git a/target/linux/mvebu/patches-4.9/300-mvneta-tx-queue-workaround.patch b/target/linux/mvebu/patches-4.9/300-mvneta-tx-queue-workaround.patch new file mode 100644 index 0000000000..4801c4a0de --- /dev/null +++ b/target/linux/mvebu/patches-4.9/300-mvneta-tx-queue-workaround.patch @@ -0,0 +1,36 @@ +The hardware queue scheduling is apparently configured with fixed +priorities, which creates a nasty fairness issue where traffic from one +CPU can starve traffic from all other CPUs. + +Work around this issue by forcing all tx packets to go through one CPU, +until this issue is fixed properly. + +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- +--- a/drivers/net/ethernet/marvell/mvneta.c ++++ b/drivers/net/ethernet/marvell/mvneta.c +@@ -3835,6 +3835,16 @@ static int mvneta_ethtool_get_rxfh(struc + return 0; + } + ++static u16 mvneta_select_queue(struct net_device *dev, struct sk_buff *skb, ++ void *accel_priv, ++ select_queue_fallback_t fallback) ++{ ++ /* XXX: hardware queue scheduling is broken, ++ * use only one queue until it is fixed */ ++ return 0; ++} ++ ++ + static const struct net_device_ops mvneta_netdev_ops = { + .ndo_open = mvneta_open, + .ndo_stop = mvneta_stop, +@@ -3845,6 +3855,7 @@ static const struct net_device_ops mvnet + .ndo_fix_features = mvneta_fix_features, + .ndo_get_stats64 = mvneta_get_stats64, + .ndo_do_ioctl = mvneta_ioctl, ++ .ndo_select_queue = mvneta_select_queue, + }; + + const struct ethtool_ops mvneta_eth_tool_ops = { diff --git a/target/linux/mvebu/patches-4.9/400-phy-provide-a-hook-for-link-up-link-down-events.patch b/target/linux/mvebu/patches-4.9/400-phy-provide-a-hook-for-link-up-link-down-events.patch new file mode 100644 index 0000000000..3e30d4957c --- /dev/null +++ b/target/linux/mvebu/patches-4.9/400-phy-provide-a-hook-for-link-up-link-down-events.patch @@ -0,0 +1,177 @@ +From: Russell King <rmk+kernel@arm.linux.org.uk> +Date: Sun, 12 Feb 2017 17:27:17 +0100 +Subject: [PATCH] phy: provide a hook for link up/link down events + +Sometimes, we need to do additional work between the PHY coming up and +marking the carrier present - for example, we may need to wait for the +PHY to MAC link to finish negotiation. This changes phylib to provide +a notification function pointer which avoids the built-in +netif_carrier_on() and netif_carrier_off() functions. + +Standard ->adjust_link functionality is provided by hooking a helper +into the new ->phy_link_change method. + +Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> +--- + +--- a/drivers/net/phy/phy.c ++++ b/drivers/net/phy/phy.c +@@ -955,6 +955,16 @@ void phy_start(struct phy_device *phydev + } + EXPORT_SYMBOL(phy_start); + ++static void phy_link_up(struct phy_device *phydev) ++{ ++ phydev->phy_link_change(phydev, true, true); ++} ++ ++static void phy_link_down(struct phy_device *phydev, bool do_carrier) ++{ ++ phydev->phy_link_change(phydev, false, do_carrier); ++} ++ + /** + * phy_state_machine - Handle the state machine + * @work: work_struct that describes the work to be done +@@ -996,8 +1006,7 @@ void phy_state_machine(struct work_struc + /* If the link is down, give up on negotiation for now */ + if (!phydev->link) { + phydev->state = PHY_NOLINK; +- netif_carrier_off(phydev->attached_dev); +- phydev->adjust_link(phydev->attached_dev); ++ phy_link_down(phydev, true); + break; + } + +@@ -1009,9 +1018,7 @@ void phy_state_machine(struct work_struc + /* If AN is done, we're running */ + if (err > 0) { + phydev->state = PHY_RUNNING; +- netif_carrier_on(phydev->attached_dev); +- phydev->adjust_link(phydev->attached_dev); +- ++ phy_link_up(phydev); + } else if (0 == phydev->link_timeout--) + needs_aneg = true; + break; +@@ -1036,8 +1043,7 @@ void phy_state_machine(struct work_struc + } + } + phydev->state = PHY_RUNNING; +- netif_carrier_on(phydev->attached_dev); +- phydev->adjust_link(phydev->attached_dev); ++ phy_link_up(phydev); + } + break; + case PHY_FORCING: +@@ -1047,13 +1053,12 @@ void phy_state_machine(struct work_struc + + if (phydev->link) { + phydev->state = PHY_RUNNING; +- netif_carrier_on(phydev->attached_dev); ++ phy_link_up(phydev); + } else { + if (0 == phydev->link_timeout--) + needs_aneg = true; ++ phy_link_down(phydev, false); + } +- +- phydev->adjust_link(phydev->attached_dev); + break; + case PHY_RUNNING: + /* Only register a CHANGE if we are polling and link changed +@@ -1076,14 +1081,12 @@ void phy_state_machine(struct work_struc + + if (phydev->link) { + phydev->state = PHY_RUNNING; +- netif_carrier_on(phydev->attached_dev); ++ phy_link_up(phydev); + } else { + phydev->state = PHY_NOLINK; +- netif_carrier_off(phydev->attached_dev); ++ phy_link_down(phydev, true); + } + +- phydev->adjust_link(phydev->attached_dev); +- + if (phy_interrupt_is_valid(phydev)) + err = phy_config_interrupt(phydev, + PHY_INTERRUPT_ENABLED); +@@ -1091,8 +1094,7 @@ void phy_state_machine(struct work_struc + case PHY_HALTED: + if (phydev->link) { + phydev->link = 0; +- netif_carrier_off(phydev->attached_dev); +- phydev->adjust_link(phydev->attached_dev); ++ phy_link_down(phydev, true); + do_suspend = true; + } + break; +@@ -1112,11 +1114,11 @@ void phy_state_machine(struct work_struc + + if (phydev->link) { + phydev->state = PHY_RUNNING; +- netif_carrier_on(phydev->attached_dev); ++ phy_link_up(phydev); + } else { + phydev->state = PHY_NOLINK; ++ phy_link_down(phydev, false); + } +- phydev->adjust_link(phydev->attached_dev); + } else { + phydev->state = PHY_AN; + phydev->link_timeout = PHY_AN_TIMEOUT; +@@ -1128,11 +1130,11 @@ void phy_state_machine(struct work_struc + + if (phydev->link) { + phydev->state = PHY_RUNNING; +- netif_carrier_on(phydev->attached_dev); ++ phy_link_up(phydev); + } else { + phydev->state = PHY_NOLINK; ++ phy_link_down(phydev, false); + } +- phydev->adjust_link(phydev->attached_dev); + } + break; + } +--- a/drivers/net/phy/phy_device.c ++++ b/drivers/net/phy/phy_device.c +@@ -645,6 +645,19 @@ 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) ++{ ++ struct net_device *netdev = phydev->attached_dev; ++ ++ if (do_carrier) { ++ if (up) ++ netif_carrier_on(netdev); ++ else ++ netif_carrier_off(netdev); ++ } ++ phydev->adjust_link(netdev); ++} ++ + /** + * phy_prepare_link - prepares the PHY layer to monitor link status + * @phydev: target phy_device struct +@@ -899,6 +912,7 @@ int phy_attach_direct(struct net_device + goto error; + } + ++ phydev->phy_link_change = phy_link_change; + phydev->attached_dev = dev; + dev->phydev = phydev; + +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -426,6 +426,7 @@ struct phy_device { + + u8 mdix; + ++ void (*phy_link_change)(struct phy_device *, bool up, bool do_carrier); + void (*adjust_link)(struct net_device *dev); + }; + #define to_phy_device(d) container_of(to_mdio_device(d), \ diff --git a/target/linux/mvebu/patches-4.9/401-net-phy-move-phy-MMD-accessors-to-phy-core.c.patch b/target/linux/mvebu/patches-4.9/401-net-phy-move-phy-MMD-accessors-to-phy-core.c.patch new file mode 100644 index 0000000000..970aec1d24 --- /dev/null +++ b/target/linux/mvebu/patches-4.9/401-net-phy-move-phy-MMD-accessors-to-phy-core.c.patch @@ -0,0 +1,292 @@ +From: Russell King <rmk+kernel@armlinux.org.uk> +Date: Wed, 4 Jan 2017 10:46:43 +0000 +Subject: [PATCH] net: phy: move phy MMD accessors to phy-core.c + +Move the phy_(read|write)__mmd() helpers out of line, they will become +our main MMD accessor functions, and so will be a little more complex. +This complexity doesn't belong in an inline function. Also move the +_indirect variants as well to keep like functionality together. + +Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> +--- + create mode 100644 drivers/net/phy/phy-core.c + +--- a/drivers/net/phy/Makefile ++++ b/drivers/net/phy/Makefile +@@ -1,6 +1,7 @@ + # Makefile for Linux PHY drivers and MDIO bus drivers + +-libphy-y := phy.o phy_device.o mdio_bus.o mdio_device.o ++libphy-y := phy.o phy_device.o mdio_bus.o mdio_device.o \ ++ phy-core.o + libphy-$(CONFIG_SWPHY) += swphy.o + + obj-$(CONFIG_MDIO_BOARDINFO) += mdio-boardinfo.o +--- a/drivers/net/phy/phy.c ++++ b/drivers/net/phy/phy.c +@@ -1171,91 +1171,6 @@ void phy_mac_interrupt(struct phy_device + } + EXPORT_SYMBOL(phy_mac_interrupt); + +-static inline void mmd_phy_indirect(struct mii_bus *bus, int prtad, int devad, +- int addr) +-{ +- /* Write the desired MMD Devad */ +- bus->write(bus, addr, MII_MMD_CTRL, devad); +- +- /* Write the desired MMD register address */ +- bus->write(bus, addr, MII_MMD_DATA, prtad); +- +- /* Select the Function : DATA with no post increment */ +- bus->write(bus, addr, MII_MMD_CTRL, (devad | MII_MMD_CTRL_NOINCR)); +-} +- +-/** +- * phy_read_mmd_indirect - reads data from the MMD registers +- * @phydev: The PHY device bus +- * @prtad: MMD Address +- * @devad: MMD DEVAD +- * +- * Description: it reads data from the MMD registers (clause 22 to access to +- * clause 45) of the specified phy address. +- * To read these register we have: +- * 1) Write reg 13 // DEVAD +- * 2) Write reg 14 // MMD Address +- * 3) Write reg 13 // MMD Data Command for MMD DEVAD +- * 3) Read reg 14 // Read MMD data +- */ +-int phy_read_mmd_indirect(struct phy_device *phydev, int prtad, int devad) +-{ +- struct phy_driver *phydrv = phydev->drv; +- int addr = phydev->mdio.addr; +- int value = -1; +- +- if (!phydrv->read_mmd_indirect) { +- struct mii_bus *bus = phydev->mdio.bus; +- +- mutex_lock(&bus->mdio_lock); +- mmd_phy_indirect(bus, prtad, devad, addr); +- +- /* Read the content of the MMD's selected register */ +- value = bus->read(bus, addr, MII_MMD_DATA); +- mutex_unlock(&bus->mdio_lock); +- } else { +- value = phydrv->read_mmd_indirect(phydev, prtad, devad, addr); +- } +- return value; +-} +-EXPORT_SYMBOL(phy_read_mmd_indirect); +- +-/** +- * phy_write_mmd_indirect - writes data to the MMD registers +- * @phydev: The PHY device +- * @prtad: MMD Address +- * @devad: MMD DEVAD +- * @data: data to write in the MMD register +- * +- * Description: Write data from the MMD registers of the specified +- * phy address. +- * To write these register we have: +- * 1) Write reg 13 // DEVAD +- * 2) Write reg 14 // MMD Address +- * 3) Write reg 13 // MMD Data Command for MMD DEVAD +- * 3) Write reg 14 // Write MMD data +- */ +-void phy_write_mmd_indirect(struct phy_device *phydev, int prtad, +- int devad, u32 data) +-{ +- struct phy_driver *phydrv = phydev->drv; +- int addr = phydev->mdio.addr; +- +- if (!phydrv->write_mmd_indirect) { +- struct mii_bus *bus = phydev->mdio.bus; +- +- mutex_lock(&bus->mdio_lock); +- mmd_phy_indirect(bus, prtad, devad, addr); +- +- /* Write the data into MMD's selected register */ +- bus->write(bus, addr, MII_MMD_DATA, data); +- mutex_unlock(&bus->mdio_lock); +- } else { +- phydrv->write_mmd_indirect(phydev, prtad, devad, addr, data); +- } +-} +-EXPORT_SYMBOL(phy_write_mmd_indirect); +- + /** + * phy_init_eee - init and check the EEE feature + * @phydev: target phy_device struct +--- /dev/null ++++ b/drivers/net/phy/phy-core.c +@@ -0,0 +1,135 @@ ++/* ++ * Core PHY library, taken from phy.c ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ */ ++#include <linux/export.h> ++#include <linux/phy.h> ++ ++static inline void mmd_phy_indirect(struct mii_bus *bus, int prtad, int devad, ++ int addr) ++{ ++ /* Write the desired MMD Devad */ ++ bus->write(bus, addr, MII_MMD_CTRL, devad); ++ ++ /* Write the desired MMD register address */ ++ bus->write(bus, addr, MII_MMD_DATA, prtad); ++ ++ /* Select the Function : DATA with no post increment */ ++ bus->write(bus, addr, MII_MMD_CTRL, (devad | MII_MMD_CTRL_NOINCR)); ++} ++ ++/** ++ * phy_read_mmd_indirect - reads data from the MMD registers ++ * @phydev: The PHY device bus ++ * @prtad: MMD Address ++ * @devad: MMD DEVAD ++ * ++ * Description: it reads data from the MMD registers (clause 22 to access to ++ * clause 45) of the specified phy address. ++ * To read these register we have: ++ * 1) Write reg 13 // DEVAD ++ * 2) Write reg 14 // MMD Address ++ * 3) Write reg 13 // MMD Data Command for MMD DEVAD ++ * 3) Read reg 14 // Read MMD data ++ */ ++int phy_read_mmd_indirect(struct phy_device *phydev, int prtad, int devad) ++{ ++ struct phy_driver *phydrv = phydev->drv; ++ int addr = phydev->mdio.addr; ++ int value = -1; ++ ++ if (!phydrv->read_mmd_indirect) { ++ struct mii_bus *bus = phydev->mdio.bus; ++ ++ mutex_lock(&bus->mdio_lock); ++ mmd_phy_indirect(bus, prtad, devad, addr); ++ ++ /* Read the content of the MMD's selected register */ ++ value = bus->read(bus, addr, MII_MMD_DATA); ++ mutex_unlock(&bus->mdio_lock); ++ } else { ++ value = phydrv->read_mmd_indirect(phydev, prtad, devad, addr); ++ } ++ return value; ++} ++EXPORT_SYMBOL(phy_read_mmd_indirect); ++ ++/** ++ * phy_read_mmd - Convenience function for reading a register ++ * from an MMD on a given PHY. ++ * @phydev: The phy_device struct ++ * @devad: The MMD to read from ++ * @regnum: The register on the MMD to read ++ * ++ * Same rules as for phy_read(); ++ */ ++int phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum) ++{ ++ if (!phydev->is_c45) ++ return -EOPNOTSUPP; ++ ++ return mdiobus_read(phydev->mdio.bus, phydev->mdio.addr, ++ MII_ADDR_C45 | (devad << 16) | (regnum & 0xffff)); ++} ++EXPORT_SYMBOL(phy_read_mmd); ++ ++/** ++ * phy_write_mmd_indirect - writes data to the MMD registers ++ * @phydev: The PHY device ++ * @prtad: MMD Address ++ * @devad: MMD DEVAD ++ * @data: data to write in the MMD register ++ * ++ * Description: Write data from the MMD registers of the specified ++ * phy address. ++ * To write these register we have: ++ * 1) Write reg 13 // DEVAD ++ * 2) Write reg 14 // MMD Address ++ * 3) Write reg 13 // MMD Data Command for MMD DEVAD ++ * 3) Write reg 14 // Write MMD data ++ */ ++void phy_write_mmd_indirect(struct phy_device *phydev, int prtad, ++ int devad, u32 data) ++{ ++ struct phy_driver *phydrv = phydev->drv; ++ int addr = phydev->mdio.addr; ++ ++ if (!phydrv->write_mmd_indirect) { ++ struct mii_bus *bus = phydev->mdio.bus; ++ ++ mutex_lock(&bus->mdio_lock); ++ mmd_phy_indirect(bus, prtad, devad, addr); ++ ++ /* Write the data into MMD's selected register */ ++ bus->write(bus, addr, MII_MMD_DATA, data); ++ mutex_unlock(&bus->mdio_lock); ++ } else { ++ phydrv->write_mmd_indirect(phydev, prtad, devad, addr, data); ++ } ++} ++EXPORT_SYMBOL(phy_write_mmd_indirect); ++ ++/** ++ * phy_write_mmd - Convenience function for writing a register ++ * on an MMD on a given PHY. ++ * @phydev: The phy_device struct ++ * @devad: The MMD to read from ++ * @regnum: The register on the MMD to read ++ * @val: value to write to @regnum ++ * ++ * Same rules as for phy_write(); ++ */ ++int phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val) ++{ ++ if (!phydev->is_c45) ++ return -EOPNOTSUPP; ++ ++ regnum = MII_ADDR_C45 | ((devad & 0x1f) << 16) | (regnum & 0xffff); ++ ++ return mdiobus_write(phydev->mdio.bus, phydev->mdio.addr, regnum, val); ++} ++EXPORT_SYMBOL(phy_write_mmd); +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -627,14 +627,7 @@ struct phy_fixup { + * + * Same rules as for phy_read(); + */ +-static inline int phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum) +-{ +- if (!phydev->is_c45) +- return -EOPNOTSUPP; +- +- return mdiobus_read(phydev->mdio.bus, phydev->mdio.addr, +- MII_ADDR_C45 | (devad << 16) | (regnum & 0xffff)); +-} ++int phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum); + + /** + * phy_read_mmd_indirect - reads data from the MMD registers +@@ -728,16 +721,7 @@ static inline bool phy_is_pseudo_fixed_l + * + * Same rules as for phy_write(); + */ +-static inline int phy_write_mmd(struct phy_device *phydev, int devad, +- u32 regnum, u16 val) +-{ +- if (!phydev->is_c45) +- return -EOPNOTSUPP; +- +- regnum = MII_ADDR_C45 | ((devad & 0x1f) << 16) | (regnum & 0xffff); +- +- return mdiobus_write(phydev->mdio.bus, phydev->mdio.addr, regnum, val); +-} ++int phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val); + + /** + * phy_write_mmd_indirect - writes data to the MMD registers diff --git a/target/linux/mvebu/patches-4.9/402-net-phy-make-phy_-read-write-_mmd-generic-MMD-access.patch b/target/linux/mvebu/patches-4.9/402-net-phy-make-phy_-read-write-_mmd-generic-MMD-access.patch new file mode 100644 index 0000000000..a37b0a7527 --- /dev/null +++ b/target/linux/mvebu/patches-4.9/402-net-phy-make-phy_-read-write-_mmd-generic-MMD-access.patch @@ -0,0 +1,97 @@ +From: Russell King <rmk+kernel@armlinux.org.uk> +Date: Wed, 4 Jan 2017 19:20:21 +0000 +Subject: [PATCH] net: phy: make phy_(read|write)_mmd() generic MMD + accessors + +Make phy_(read|write)_mmd() generic 802.3 clause 45 register accessors +for both Clause 22 and Clause 45 PHYs, using either the direct register +reading for Clause 45, or the indirect method for Clause 22 PHYs. +Allow this behaviour to be overriden by PHY drivers where necessary. + +Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> +--- + +--- a/drivers/net/phy/phy-core.c ++++ b/drivers/net/phy/phy-core.c +@@ -69,11 +69,18 @@ EXPORT_SYMBOL(phy_read_mmd_indirect); + */ + int phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum) + { +- if (!phydev->is_c45) +- return -EOPNOTSUPP; ++ if (regnum > (u16)~0 || devad > 32) ++ return -EINVAL; + +- return mdiobus_read(phydev->mdio.bus, phydev->mdio.addr, +- MII_ADDR_C45 | (devad << 16) | (regnum & 0xffff)); ++ if (phydev->drv->read_mmd) ++ return phydev->drv->read_mmd(phydev, devad, regnum); ++ ++ if (phydev->is_c45) { ++ u32 addr = MII_ADDR_C45 | (devad << 16) | (regnum & 0xffff); ++ return mdiobus_read(phydev->mdio.bus, phydev->mdio.addr, addr); ++ } ++ ++ return phy_read_mmd_indirect(phydev, regnum, devad); + } + EXPORT_SYMBOL(phy_read_mmd); + +@@ -125,11 +132,21 @@ EXPORT_SYMBOL(phy_write_mmd_indirect); + */ + int phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val) + { +- if (!phydev->is_c45) +- return -EOPNOTSUPP; ++ if (regnum > (u16)~0 || devad > 32) ++ return -EINVAL; ++ ++ if (phydev->drv->read_mmd) ++ return phydev->drv->write_mmd(phydev, devad, regnum, val); ++ ++ if (phydev->is_c45) { ++ u32 addr = MII_ADDR_C45 | (devad << 16) | (regnum & 0xffff); ++ ++ return mdiobus_write(phydev->mdio.bus, phydev->mdio.addr, ++ addr, val); ++ } + +- regnum = MII_ADDR_C45 | ((devad & 0x1f) << 16) | (regnum & 0xffff); ++ phy_write_mmd_indirect(phydev, regnum, devad, val); + +- return mdiobus_write(phydev->mdio.bus, phydev->mdio.addr, regnum, val); ++ return 0; + } + EXPORT_SYMBOL(phy_write_mmd); +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -570,6 +570,30 @@ struct phy_driver { + */ + void (*link_change_notify)(struct phy_device *dev); + ++ /* ++ * Phy specific driver override for reading a MMD register. ++ * This function is optional for PHY specific drivers. When ++ * not provided, the default MMD read function will be used ++ * by phy_read_mmd(), which will use either a direct read for ++ * Clause 45 PHYs or an indirect read for Clause 22 PHYs. ++ * devnum is the MMD device number within the PHY device, ++ * regnum is the register within the selected MMD device. ++ */ ++ int (*read_mmd)(struct phy_device *dev, int devnum, u16 regnum); ++ ++ /* ++ * Phy specific driver override for writing a MMD register. ++ * This function is optional for PHY specific drivers. When ++ * not provided, the default MMD write function will be used ++ * by phy_write_mmd(), which will use either a direct write for ++ * Clause 45 PHYs, or an indirect write for Clause 22 PHYs. ++ * devnum is the MMD device number within the PHY device, ++ * regnum is the register within the selected MMD device. ++ * val is the value to be written. ++ */ ++ int (*write_mmd)(struct phy_device *dev, int devnum, u16 regnum, ++ u16 val); ++ + /* A function provided by a phy specific driver to override the + * the PHY driver framework support for reading a MMD register + * from the PHY. If not supported, return -1. This function is diff --git a/target/linux/mvebu/patches-4.9/403-net-phy-avoid-setting-unsupported-EEE-advertisments.patch b/target/linux/mvebu/patches-4.9/403-net-phy-avoid-setting-unsupported-EEE-advertisments.patch new file mode 100644 index 0000000000..35479007d0 --- /dev/null +++ b/target/linux/mvebu/patches-4.9/403-net-phy-avoid-setting-unsupported-EEE-advertisments.patch @@ -0,0 +1,52 @@ +From: Russell King <rmk+kernel@armlinux.org.uk> +Date: Wed, 4 Jan 2017 21:00:51 +0000 +Subject: [PATCH] net: phy: avoid setting unsupported EEE advertisments + +We currently allow userspace to set any EEE advertisments it desires, +whether or not the PHY supports them. For example: + + # ethtool --set-eee eth1 advertise 0xffffffff + # ethtool --show-eee eth1 + EEE Settings for eth1: + EEE status: disabled + Tx LPI: disabled + Supported EEE link modes: 100baseT/Full + 1000baseT/Full + 10000baseT/Full + Advertised EEE link modes: 100baseT/Full + 1000baseT/Full + 1000baseKX/Full + 10000baseT/Full + 10000baseKX4/Full + 10000baseKR/Full + +Clearly, this is not sane, we should only allow link modes that are +supported to be advertised (as we do elsewhere.) Ensure that we mask +the MDIO_AN_EEE_ADV value with the capabilities retrieved from the +MDIO_PCS_EEE_ABLE register. + +Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> +--- + +--- a/drivers/net/phy/phy.c ++++ b/drivers/net/phy/phy.c +@@ -1307,11 +1307,16 @@ EXPORT_SYMBOL(phy_ethtool_get_eee); + */ + int phy_ethtool_set_eee(struct phy_device *phydev, struct ethtool_eee *data) + { +- int val = ethtool_adv_to_mmd_eee_adv_t(data->advertised); ++ int cap, adv; + +- phy_write_mmd_indirect(phydev, MDIO_AN_EEE_ADV, MDIO_MMD_AN, val); ++ /* Get Supported EEE */ ++ cap = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_PCS_EEE_ABLE); ++ if (cap < 0) ++ return cap; + +- return 0; ++ adv = ethtool_adv_to_mmd_eee_adv_t(data->advertised) & cap; ++ ++ return phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, adv); + } + EXPORT_SYMBOL(phy_ethtool_set_eee); + diff --git a/target/linux/mvebu/patches-4.9/404-net-phy-restart-phy-autonegotiation-after-EEE-advert.patch b/target/linux/mvebu/patches-4.9/404-net-phy-restart-phy-autonegotiation-after-EEE-advert.patch new file mode 100644 index 0000000000..5290e71408 --- /dev/null +++ b/target/linux/mvebu/patches-4.9/404-net-phy-restart-phy-autonegotiation-after-EEE-advert.patch @@ -0,0 +1,50 @@ +From: Russell King <rmk+kernel@armlinux.org.uk> +Date: Thu, 5 Jan 2017 12:21:09 +0000 +Subject: [PATCH] net: phy: restart phy autonegotiation after EEE + advertisment change + +When the EEE advertisment is changed, we should restart autonegotiation +to update the link partner with the new EEE settings. Add this trigger +but only if the advertisment has changed. + +Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> +--- + +--- a/drivers/net/phy/phy.c ++++ b/drivers/net/phy/phy.c +@@ -1307,16 +1307,33 @@ EXPORT_SYMBOL(phy_ethtool_get_eee); + */ + int phy_ethtool_set_eee(struct phy_device *phydev, struct ethtool_eee *data) + { +- int cap, adv; ++ int cap, old_adv, adv, ret; + + /* Get Supported EEE */ + cap = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_PCS_EEE_ABLE); + if (cap < 0) + return cap; + ++ old_adv = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV); ++ if (old_adv < 0) ++ return old_adv; ++ + adv = ethtool_adv_to_mmd_eee_adv_t(data->advertised) & cap; + +- return phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, adv); ++ if (old_adv != adv) { ++ ret = phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, adv); ++ if (ret < 0) ++ return ret; ++ ++ /* Restart autonegotiation so the new modes get sent to the ++ * link partner. ++ */ ++ ret = phy_restart_aneg(phydev); ++ if (ret < 0) ++ return ret; ++ } ++ ++ return 0; + } + EXPORT_SYMBOL(phy_ethtool_set_eee); + diff --git a/target/linux/mvebu/patches-4.9/405-net-phy-allow-EEE-with-SGMII-interface-modes.patch b/target/linux/mvebu/patches-4.9/405-net-phy-allow-EEE-with-SGMII-interface-modes.patch new file mode 100644 index 0000000000..db7b00404f --- /dev/null +++ b/target/linux/mvebu/patches-4.9/405-net-phy-allow-EEE-with-SGMII-interface-modes.patch @@ -0,0 +1,21 @@ +From: Russell King <rmk+kernel@armlinux.org.uk> +Date: Thu, 5 Jan 2017 09:34:42 +0000 +Subject: [PATCH] net: phy: allow EEE with SGMII interface modes + +As EEE is able to work in SGMII mode as well, add it to the list of +permissable EEE modes that phy_init_eee() will accept. This is +necessary so that EEE can work with an 88E1512 connected in SGMII mode. + +Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> +--- + +--- a/drivers/net/phy/phy.c ++++ b/drivers/net/phy/phy.c +@@ -1191,6 +1191,7 @@ int phy_init_eee(struct phy_device *phyd + if ((phydev->duplex == DUPLEX_FULL) && + ((phydev->interface == PHY_INTERFACE_MODE_MII) || + (phydev->interface == PHY_INTERFACE_MODE_GMII) || ++ phydev->interface == PHY_INTERFACE_MODE_SGMII || + phy_interface_is_rgmii(phydev) || + phy_is_internal(phydev))) { + int eee_lp, eee_cap, eee_adv; diff --git a/target/linux/mvebu/patches-4.9/406-net-phy-improve-phylib-correctness-for-non-autoneg-s.patch b/target/linux/mvebu/patches-4.9/406-net-phy-improve-phylib-correctness-for-non-autoneg-s.patch new file mode 100644 index 0000000000..16516c6846 --- /dev/null +++ b/target/linux/mvebu/patches-4.9/406-net-phy-improve-phylib-correctness-for-non-autoneg-s.patch @@ -0,0 +1,199 @@ +From: Russell King <rmk+kernel@armlinux.org.uk> +Date: Thu, 5 Jan 2017 16:32:14 +0000 +Subject: [PATCH] net: phy: improve phylib correctness for non-autoneg + settings + +phylib has some undesirable behaviour when forcing a link mode through +ethtool. phylib uses this code: + + idx = phy_find_valid(phy_find_setting(phydev->speed, phydev->duplex), + features); + +to find an index in the settings table. phy_find_setting() starts at +index 0, and scans upwards looking for an exact speed and duplex match. +When it doesn't find it, it returns MAX_NUM_SETTINGS - 1, which is +10baseT-Half duplex. + +phy_find_valid() then scans from the point (and effectively only checks +one entry) before bailing out, returning MAX_NUM_SETTINGS - 1. + +phy_sanitize_settings() then sets ->speed to SPEED_10 and ->duplex to +DUPLEX_HALF whether or not 10baseT-Half is supported or not. This goes +against all the comments against these functions, and 10baseT-Half may +not even be supported by the hardware. + +Rework these functions, introducing a new method of scanning the table. +There are two modes of lookup that phylib wants: exact, and inexact. + +- in exact mode, we return either an exact match or failure +- in inexact mode, we return an exact match if it exists, a match at + the highest speed that is not greater than the requested speed + (ignoring duplex), or failing that, the lowest supported speed, or + failure. + +The biggest difference is that we always check whether the entry is +supported before further consideration, so all unsupported entries are +not considered as candidates. + +This results in arguably saner behaviour, better matches the comments, +and is probably what users would expect. + +This becomes important as ethernet speeds increase, PHYs exist which do +not support the 10Mbit speeds, and half-duplex is likely to become +obsolete - it's already not even an option on 10Gbit and faster links. + +Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> +--- + +--- a/drivers/net/phy/phy.c ++++ b/drivers/net/phy/phy.c +@@ -160,7 +160,9 @@ struct phy_setting { + u32 setting; + }; + +-/* A mapping of all SUPPORTED settings to speed/duplex */ ++/* 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. */ + static const struct phy_setting settings[] = { + { + .speed = SPEED_10000, +@@ -219,45 +221,70 @@ static const struct phy_setting settings + }, + }; + +-#define MAX_NUM_SETTINGS ARRAY_SIZE(settings) +- + /** +- * phy_find_setting - find a PHY settings array entry that matches speed & duplex ++ * phy_lookup_setting - lookup a PHY setting + * @speed: speed to match + * @duplex: duplex to match ++ * @feature: allowed link modes ++ * @exact: an exact match is required ++ * ++ * Search the settings array for a setting that matches the speed and ++ * duplex, and which is supported. ++ * ++ * If @exact is unset, either an exact match or %NULL for no match will ++ * be returned. + * +- * Description: Searches the settings array for the setting which +- * matches the desired speed and duplex, and returns the index +- * of that setting. Returns the index of the last setting if +- * none of the others match. ++ * If @exact is set, an exact match, the fastest supported setting at ++ * or below the specified speed, the slowest supported setting, or if ++ * they all fail, %NULL will be returned. + */ +-static inline unsigned int phy_find_setting(int speed, int duplex) ++static const struct phy_setting * ++phy_lookup_setting(int speed, int duplex, u32 features, bool exact) + { +- unsigned int idx = 0; ++ const struct phy_setting *p, *match = NULL, *last = NULL; ++ int i; + +- while (idx < ARRAY_SIZE(settings) && +- (settings[idx].speed != speed || settings[idx].duplex != duplex)) +- idx++; ++ for (i = 0, p = settings; i < ARRAY_SIZE(settings); i++, p++) { ++ if (p->setting & features) { ++ last = p; ++ if (p->speed == speed && p->duplex == duplex) { ++ /* Exact match for speed and duplex */ ++ match = p; ++ break; ++ } else if (!exact) { ++ if (!match && p->speed <= speed) ++ /* Candidate */ ++ match = p; ++ ++ if (p->speed < speed) ++ break; ++ } ++ } ++ } + +- return idx < MAX_NUM_SETTINGS ? idx : MAX_NUM_SETTINGS - 1; ++ if (!match && !exact) ++ match = last; ++ ++ return match; + } + + /** +- * phy_find_valid - find a PHY setting that matches the requested features mask +- * @idx: The first index in settings[] to search +- * @features: A mask of the valid settings ++ * phy_find_valid - find a PHY setting that matches the requested parameters ++ * @speed: desired speed ++ * @duplex: desired duplex ++ * @supported: mask of supported link modes + * +- * Description: Returns the index of the first valid setting less +- * than or equal to the one pointed to by idx, as determined by +- * the mask in features. Returns the index of the last setting +- * if nothing else matches. ++ * Locate a supported phy setting that is, in priority order: ++ * - an exact match for the specified speed and duplex mode ++ * - a match for the specified speed, or slower speed ++ * - the slowest supported speed ++ * Returns the matched phy_setting entry, or %NULL if no supported phy ++ * settings were found. + */ +-static inline unsigned int phy_find_valid(unsigned int idx, u32 features) ++static const struct phy_setting * ++phy_find_valid(int speed, int duplex, u32 supported) + { +- while (idx < MAX_NUM_SETTINGS && !(settings[idx].setting & features)) +- idx++; +- +- return idx < MAX_NUM_SETTINGS ? idx : MAX_NUM_SETTINGS - 1; ++ return phy_lookup_setting(speed, duplex, supported, false); + } + + /** +@@ -271,12 +298,7 @@ static inline unsigned int phy_find_vali + */ + static inline bool phy_check_valid(int speed, int duplex, u32 features) + { +- unsigned int idx; +- +- idx = phy_find_valid(phy_find_setting(speed, duplex), features); +- +- return settings[idx].speed == speed && settings[idx].duplex == duplex && +- (settings[idx].setting & features); ++ return !!phy_lookup_setting(speed, duplex, features, true); + } + + /** +@@ -289,18 +311,22 @@ static inline bool phy_check_valid(int s + */ + static void phy_sanitize_settings(struct phy_device *phydev) + { ++ const struct phy_setting *setting; + u32 features = phydev->supported; +- unsigned int idx; + + /* Sanitize settings based on PHY capabilities */ + if ((features & SUPPORTED_Autoneg) == 0) + phydev->autoneg = AUTONEG_DISABLE; + +- idx = phy_find_valid(phy_find_setting(phydev->speed, phydev->duplex), +- features); +- +- phydev->speed = settings[idx].speed; +- phydev->duplex = settings[idx].duplex; ++ setting = phy_find_valid(phydev->speed, phydev->duplex, features); ++ if (setting) { ++ phydev->speed = setting->speed; ++ phydev->duplex = setting->duplex; ++ } else { ++ /* We failed to find anything (no supported speeds?) */ ++ phydev->speed = SPEED_UNKNOWN; ++ phydev->duplex = DUPLEX_UNKNOWN; ++ } + } + + /** diff --git a/target/linux/mvebu/patches-4.9/407-net-phy-add-802.3-clause-45-support-to-phylib.patch b/target/linux/mvebu/patches-4.9/407-net-phy-add-802.3-clause-45-support-to-phylib.patch new file mode 100644 index 0000000000..8e983cc7ff --- /dev/null +++ b/target/linux/mvebu/patches-4.9/407-net-phy-add-802.3-clause-45-support-to-phylib.patch @@ -0,0 +1,323 @@ +From: Russell King <rmk+kernel@armlinux.org.uk> +Date: Thu, 29 Dec 2016 11:03:09 +0000 +Subject: [PATCH] net: phy: add 802.3 clause 45 support to phylib + +Add generic helpers for 802.3 clause 45 PHYs for >= 10Gbps support. + +Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> +--- + create mode 100644 drivers/net/phy/phy-c45.c + +--- a/drivers/net/phy/Makefile ++++ b/drivers/net/phy/Makefile +@@ -1,7 +1,7 @@ + # Makefile for Linux PHY drivers and MDIO bus drivers + + libphy-y := phy.o phy_device.o mdio_bus.o mdio_device.o \ +- phy-core.o ++ phy-c45.o phy-core.o + libphy-$(CONFIG_SWPHY) += swphy.o + + obj-$(CONFIG_MDIO_BOARDINFO) += mdio-boardinfo.o +--- /dev/null ++++ b/drivers/net/phy/phy-c45.c +@@ -0,0 +1,234 @@ ++/* ++ * Clause 45 PHY support ++ */ ++#include <linux/ethtool.h> ++#include <linux/export.h> ++#include <linux/mdio.h> ++#include <linux/mii.h> ++#include <linux/phy.h> ++ ++/** ++ * genphy_c45_setup_forced - configures a forced speed ++ * @phydev: target phy_device struct ++ */ ++int genphy_c45_pma_setup_forced(struct phy_device *phydev) ++{ ++ int ctrl1, ctrl2, ret; ++ ++ /* Half duplex is not supported */ ++ if (phydev->duplex != DUPLEX_FULL) ++ return -EINVAL; ++ ++ ctrl1 = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_CTRL1); ++ if (ctrl1 < 0) ++ return ctrl1; ++ ++ ctrl2 = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_CTRL2); ++ if (ctrl2 < 0) ++ return ctrl2; ++ ++ ctrl1 &= ~MDIO_CTRL1_SPEEDSEL; ++ /* PMA/PMD type selection is 1.7.5:0 not 1.7.3:0. See 45.2.1.6.1. */ ++ ctrl2 &= ~(MDIO_PMA_CTRL2_TYPE | 0x30); ++ ++ switch (phydev->speed) { ++ case SPEED_10: ++ ctrl2 |= MDIO_PMA_CTRL2_10BT; ++ break; ++ case SPEED_100: ++ ctrl1 |= MDIO_PMA_CTRL1_SPEED100; ++ ctrl2 |= MDIO_PMA_CTRL2_100BTX; ++ break; ++ case SPEED_1000: ++ ctrl1 |= MDIO_PMA_CTRL1_SPEED1000; ++ /* Assume 1000base-T */ ++ ctrl2 |= MDIO_PMA_CTRL2_1000BT; ++ break; ++ case SPEED_10000: ++ ctrl1 |= MDIO_CTRL1_SPEED10G; ++ /* Assume 10Gbase-T */ ++ ctrl2 |= MDIO_PMA_CTRL2_10GBT; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ ret = phy_write_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_CTRL1, ctrl1); ++ if (ret < 0) ++ return ret; ++ ++ return phy_write_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_CTRL2, ctrl2); ++} ++EXPORT_SYMBOL_GPL(genphy_c45_pma_setup_forced); ++ ++/** ++ * genphy_c45_an_disable_aneg - disable auto-negotiation ++ * @phydev: target phy_device struct ++ * ++ * Disable auto-negotiation in the Clause 45 PHY. The link parameters ++ * parameters are controlled through the PMA/PMD MMD registers. ++ * ++ * Returns zero on success, negative errno code on failure. ++ */ ++int genphy_c45_an_disable_aneg(struct phy_device *phydev) ++{ ++ int val; ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1); ++ if (val < 0) ++ return val; ++ ++ val &= ~(MDIO_AN_CTRL1_ENABLE | MDIO_AN_CTRL1_RESTART); ++ ++ return phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1, val); ++} ++EXPORT_SYMBOL_GPL(genphy_c45_an_disable_aneg); ++ ++/** ++ * genphy_c45_restart_aneg - Enable and restart auto-negotiation ++ * @phydev: target phy_device struct ++ * ++ * This assumes that the auto-negotiation MMD is present. ++ * ++ * Enable and restart auto-negotiation. ++ */ ++int genphy_c45_restart_aneg(struct phy_device *phydev) ++{ ++ int val; ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1); ++ if (val < 0) ++ return val; ++ ++ val |= MDIO_AN_CTRL1_ENABLE | MDIO_AN_CTRL1_RESTART; ++ ++ return phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1, val); ++} ++EXPORT_SYMBOL_GPL(genphy_c45_restart_aneg); ++ ++/** ++ * genphy_c45_aneg_done - return auto-negotiation complete status ++ * @phydev: target phy_device struct ++ * ++ * This assumes that the auto-negotiation MMD is present. ++ * ++ * Reads the status register from the auto-negotiation MMD, returning: ++ * - positive if auto-negotiation is complete ++ * - negative errno code on error ++ * - zero otherwise ++ */ ++int genphy_c45_aneg_done(struct phy_device *phydev) ++{ ++ int val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1); ++ ++ return val < 0 ? val : val & MDIO_AN_STAT1_COMPLETE ? 1 : 0; ++} ++EXPORT_SYMBOL_GPL(genphy_c45_aneg_done); ++ ++/** ++ * genphy_c45_read_link - read the overall link status from the MMDs ++ * @phydev: target phy_device struct ++ * @mmd_mask: MMDs to read status from ++ * ++ * Read the link status from the specified MMDs, and if they all indicate ++ * that the link is up, return positive. If an error is encountered, ++ * a negative errno will be returned, otherwise zero. ++ */ ++int genphy_c45_read_link(struct phy_device *phydev, u32 mmd_mask) ++{ ++ int val, devad; ++ bool link = true; ++ ++ while (mmd_mask) { ++ devad = __ffs(mmd_mask); ++ mmd_mask &= ~BIT(devad); ++ ++ val = phy_read_mmd(phydev, devad, MDIO_STAT1); ++ if (val < 0) ++ return val; ++ ++ /* Read twice because link state is latched and a ++ * read moves the current state into the register ++ */ ++ val = phy_read_mmd(phydev, devad, MDIO_STAT1); ++ if (val < 0) ++ return val; ++ ++ if (!(val & MDIO_STAT1_LSTATUS)) ++ link = false; ++ } ++ ++ return link; ++} ++EXPORT_SYMBOL_GPL(genphy_c45_read_link); ++ ++/** ++ * genphy_c45_read_lpa - read the link partner advertisment and pause ++ * @phydev: target phy_device struct ++ * ++ * Read the Clause 45 defined base (7.19) and 10G (7.33) status registers, ++ * filling in the link partner advertisment, pause and asym_pause members ++ * in @phydev. This assumes that the auto-negotiation MMD is present, and ++ * the backplane bit (7.48.0) is clear. Clause 45 PHY drivers are expected ++ * to fill in the remainder of the link partner advert from vendor registers. ++ */ ++int genphy_c45_read_lpa(struct phy_device *phydev) ++{ ++ int val; ++ ++ /* Read the link partner's base page advertisment */ ++ val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_LPA); ++ if (val < 0) ++ return val; ++ ++ phydev->lp_advertising = mii_lpa_to_ethtool_lpa_t(val); ++ phydev->pause = val & LPA_PAUSE_CAP ? 1 : 0; ++ phydev->asym_pause = val & LPA_PAUSE_ASYM ? 1 : 0; ++ ++ /* Read the link partner's 10G advertisment */ ++ val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_STAT); ++ if (val < 0) ++ return val; ++ ++ if (val & MDIO_AN_10GBT_STAT_LP10G) ++ phydev->lp_advertising |= ADVERTISED_10000baseT_Full; ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(genphy_c45_read_lpa); ++ ++/** ++ * genphy_c45_read_pma - read link speed etc from PMA ++ * @phydev: target phy_device struct ++ */ ++int genphy_c45_read_pma(struct phy_device *phydev) ++{ ++ int val; ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_CTRL1); ++ if (val < 0) ++ return val; ++ ++ switch (val & MDIO_CTRL1_SPEEDSEL) { ++ case 0: ++ phydev->speed = SPEED_10; ++ break; ++ case MDIO_PMA_CTRL1_SPEED100: ++ phydev->speed = SPEED_100; ++ break; ++ case MDIO_PMA_CTRL1_SPEED1000: ++ phydev->speed = SPEED_1000; ++ break; ++ case MDIO_CTRL1_SPEED10G: ++ phydev->speed = SPEED_10000; ++ break; ++ default: ++ phydev->speed = SPEED_UNKNOWN; ++ break; ++ } ++ ++ phydev->duplex = DUPLEX_FULL; ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(genphy_c45_read_pma); +--- a/drivers/net/phy/phy_device.c ++++ b/drivers/net/phy/phy_device.c +@@ -1390,27 +1390,19 @@ EXPORT_SYMBOL(genphy_read_status); + + static int gen10g_read_status(struct phy_device *phydev) + { +- int devad, reg; + u32 mmd_mask = phydev->c45_ids.devices_in_package; +- +- phydev->link = 1; ++ int ret; + + /* For now just lie and say it's 10G all the time */ + phydev->speed = SPEED_10000; + phydev->duplex = DUPLEX_FULL; + +- for (devad = 0; mmd_mask; devad++, mmd_mask = mmd_mask >> 1) { +- if (!(mmd_mask & 1)) +- continue; +- +- /* Read twice because link state is latched and a +- * read moves the current state into the register +- */ +- phy_read_mmd(phydev, devad, MDIO_STAT1); +- reg = phy_read_mmd(phydev, devad, MDIO_STAT1); +- if (reg < 0 || !(reg & MDIO_STAT1_LSTATUS)) +- phydev->link = 0; +- } ++ /* Avoid reading the vendor MMDs */ ++ mmd_mask &= ~(BIT(MDIO_MMD_VEND1) | BIT(MDIO_MMD_VEND2)); ++ ++ ret = genphy_c45_read_link(phydev, mmd_mask); ++ ++ phydev->link = ret > 0 ? 1 : 0; + + return 0; + } +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -807,6 +807,8 @@ static inline const char *phydev_name(co + void phy_attached_print(struct phy_device *phydev, const char *fmt, ...) + __printf(2, 3); + void phy_attached_info(struct phy_device *phydev); ++ ++/* Clause 22 PHY */ + int genphy_config_init(struct phy_device *phydev); + int genphy_setup_forced(struct phy_device *phydev); + int genphy_restart_aneg(struct phy_device *phydev); +@@ -817,6 +819,16 @@ int genphy_read_status(struct phy_device + int genphy_suspend(struct phy_device *phydev); + int genphy_resume(struct phy_device *phydev); + int genphy_soft_reset(struct phy_device *phydev); ++ ++/* Clause 45 PHY */ ++int genphy_c45_restart_aneg(struct phy_device *phydev); ++int genphy_c45_aneg_done(struct phy_device *phydev); ++int genphy_c45_read_link(struct phy_device *phydev, u32 mmd_mask); ++int genphy_c45_read_lpa(struct phy_device *phydev); ++int genphy_c45_read_pma(struct phy_device *phydev); ++int genphy_c45_pma_setup_forced(struct phy_device *phydev); ++int genphy_c45_an_disable_aneg(struct phy_device *phydev); ++ + void phy_driver_unregister(struct phy_driver *drv); + void phy_drivers_unregister(struct phy_driver *drv, int n); + int phy_driver_register(struct phy_driver *new_driver, struct module *owner); diff --git a/target/linux/mvebu/patches-4.9/408-net-phy-hook-up-clause-45-autonegotiation-restart.patch b/target/linux/mvebu/patches-4.9/408-net-phy-hook-up-clause-45-autonegotiation-restart.patch new file mode 100644 index 0000000000..7a16bd5963 --- /dev/null +++ b/target/linux/mvebu/patches-4.9/408-net-phy-hook-up-clause-45-autonegotiation-restart.patch @@ -0,0 +1,54 @@ +From: Russell King <rmk+kernel@armlinux.org.uk> +Date: Fri, 13 Jan 2017 11:17:30 +0000 +Subject: [PATCH] net: phy: hook up clause 45 autonegotiation restart + +genphy_restart_aneg() can only restart autonegotiation on clause 22 +PHYs. Add a phy_restart_aneg() function which selects between the +clause 22 and clause 45 restart functionality depending on the PHY +type. + +Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> +--- + +--- a/drivers/net/phy/phy.c ++++ b/drivers/net/phy/phy.c +@@ -134,6 +134,24 @@ static int phy_config_interrupt(struct p + return 0; + } + ++/** ++ * phy_restart_aneg - restart auto-negotiation ++ * @phydev: target phy_device struct ++ * ++ * Restart the autonegotiation on @phydev. Returns >= 0 on success or ++ * negative errno on error. ++ */ ++static int phy_restart_aneg(struct phy_device *phydev) ++{ ++ int ret; ++ ++ if (phydev->is_c45) ++ ret = genphy_c45_restart_aneg(phydev); ++ else ++ ret = genphy_restart_aneg(phydev); ++ ++ return ret; ++} + + /** + * phy_aneg_done - return auto-negotiation status +@@ -1403,3 +1421,14 @@ int phy_ethtool_set_link_ksettings(struc + return phy_ethtool_ksettings_set(phydev, cmd); + } + EXPORT_SYMBOL(phy_ethtool_set_link_ksettings); ++ ++int phy_ethtool_nway_reset(struct net_device *ndev) ++{ ++ struct phy_device *phydev = ndev->phydev; ++ ++ if (!phydev) ++ return -ENODEV; ++ ++ return phy_restart_aneg(phydev); ++} ++EXPORT_SYMBOL(phy_ethtool_nway_reset); diff --git a/target/linux/mvebu/patches-4.9/409-net-phy-don-t-double-read-clause-45-status-register.patch b/target/linux/mvebu/patches-4.9/409-net-phy-don-t-double-read-clause-45-status-register.patch new file mode 100644 index 0000000000..ee53fe0380 --- /dev/null +++ b/target/linux/mvebu/patches-4.9/409-net-phy-don-t-double-read-clause-45-status-register.patch @@ -0,0 +1,35 @@ +From: Russell King <rmk+kernel@armlinux.org.uk> +Date: Thu, 5 Jan 2017 00:23:40 +0000 +Subject: [PATCH] net: phy: don't double-read clause 45 status register + +One of the design decisions behind the link status bit in the status +register is that it latches low on link loss. This is so that link loss +events are not missed. Double-reading the status register means that we +always read the current state of the link, clearing any link loss event. + +This can cause problems - for example, if the link has negotiated a +different set of operating parameters, these will not be communicated +to the MAC as the PHY state machine will still think that the link has +remained active. + +Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> +--- + +--- a/drivers/net/phy/phy-c45.c ++++ b/drivers/net/phy/phy-c45.c +@@ -143,12 +143,9 @@ int genphy_c45_read_link(struct phy_devi + devad = __ffs(mmd_mask); + mmd_mask &= ~BIT(devad); + +- val = phy_read_mmd(phydev, devad, MDIO_STAT1); +- if (val < 0) +- return val; +- +- /* Read twice because link state is latched and a +- * read moves the current state into the register ++ /* The link state is latched low so that momentary link ++ * drops can be detected. Do not double-read the status ++ * register if the link is down. + */ + val = phy_read_mmd(phydev, devad, MDIO_STAT1); + if (val < 0) diff --git a/target/linux/mvebu/patches-4.9/410-net-phy-allow-settings-table-to-support-more-than-32.patch b/target/linux/mvebu/patches-4.9/410-net-phy-allow-settings-table-to-support-more-than-32.patch new file mode 100644 index 0000000000..b6d850577f --- /dev/null +++ b/target/linux/mvebu/patches-4.9/410-net-phy-allow-settings-table-to-support-more-than-32.patch @@ -0,0 +1,142 @@ +From: Russell King <rmk+kernel@armlinux.org.uk> +Date: Thu, 5 Jan 2017 16:32:14 +0000 +Subject: [PATCH] net: phy: allow settings table to support more than 32 + link modes + +Allow the phy settings table to support more than 32 link modes by +switching to the ethtool link mode bit number representation, rather +than storing the mask. This will allow phylink and other ethtool +code to share the settings table to look up settings. + +Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> +--- + +--- a/drivers/net/phy/phy.c ++++ b/drivers/net/phy/phy.c +@@ -175,7 +175,7 @@ static inline int phy_aneg_done(struct p + struct phy_setting { + int speed; + int duplex; +- u32 setting; ++ int bit; + }; + + /* A mapping of all SUPPORTED settings to speed/duplex. This table +@@ -185,57 +185,57 @@ static const struct phy_setting settings + { + .speed = SPEED_10000, + .duplex = DUPLEX_FULL, +- .setting = SUPPORTED_10000baseKR_Full, ++ .bit = ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, + }, + { + .speed = SPEED_10000, + .duplex = DUPLEX_FULL, +- .setting = SUPPORTED_10000baseKX4_Full, ++ .bit = ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT, + }, + { + .speed = SPEED_10000, + .duplex = DUPLEX_FULL, +- .setting = SUPPORTED_10000baseT_Full, ++ .bit = ETHTOOL_LINK_MODE_10000baseT_Full_BIT, + }, + { + .speed = SPEED_2500, + .duplex = DUPLEX_FULL, +- .setting = SUPPORTED_2500baseX_Full, ++ .bit = ETHTOOL_LINK_MODE_2500baseX_Full_BIT, + }, + { + .speed = SPEED_1000, + .duplex = DUPLEX_FULL, +- .setting = SUPPORTED_1000baseKX_Full, ++ .bit = ETHTOOL_LINK_MODE_1000baseKX_Full_BIT, + }, + { + .speed = SPEED_1000, + .duplex = DUPLEX_FULL, +- .setting = SUPPORTED_1000baseT_Full, ++ .bit = ETHTOOL_LINK_MODE_1000baseT_Full_BIT, + }, + { + .speed = SPEED_1000, + .duplex = DUPLEX_HALF, +- .setting = SUPPORTED_1000baseT_Half, ++ .bit = ETHTOOL_LINK_MODE_1000baseT_Half_BIT, + }, + { + .speed = SPEED_100, + .duplex = DUPLEX_FULL, +- .setting = SUPPORTED_100baseT_Full, ++ .bit = ETHTOOL_LINK_MODE_100baseT_Full_BIT, + }, + { + .speed = SPEED_100, + .duplex = DUPLEX_HALF, +- .setting = SUPPORTED_100baseT_Half, ++ .bit = ETHTOOL_LINK_MODE_100baseT_Half_BIT, + }, + { + .speed = SPEED_10, + .duplex = DUPLEX_FULL, +- .setting = SUPPORTED_10baseT_Full, ++ .bit = ETHTOOL_LINK_MODE_10baseT_Full_BIT, + }, + { + .speed = SPEED_10, + .duplex = DUPLEX_HALF, +- .setting = SUPPORTED_10baseT_Half, ++ .bit = ETHTOOL_LINK_MODE_10baseT_Half_BIT, + }, + }; + +@@ -243,7 +243,8 @@ static const struct phy_setting settings + * phy_lookup_setting - lookup a PHY setting + * @speed: speed to match + * @duplex: duplex to match +- * @feature: allowed link modes ++ * @mask: allowed link modes ++ * @maxbit: bit size of link modes + * @exact: an exact match is required + * + * Search the settings array for a setting that matches the speed and +@@ -257,13 +258,14 @@ static const struct phy_setting settings + * they all fail, %NULL will be returned. + */ + static const struct phy_setting * +-phy_lookup_setting(int speed, int duplex, u32 features, bool exact) ++phy_lookup_setting(int speed, int duplex, const unsigned long *mask, ++ size_t maxbit, bool exact) + { + const struct phy_setting *p, *match = NULL, *last = NULL; + int i; + + for (i = 0, p = settings; i < ARRAY_SIZE(settings); i++, p++) { +- if (p->setting & features) { ++ if (p->bit < maxbit && test_bit(p->bit, mask)) { + last = p; + if (p->speed == speed && p->duplex == duplex) { + /* Exact match for speed and duplex */ +@@ -302,7 +304,9 @@ phy_lookup_setting(int speed, int duplex + static const struct phy_setting * + phy_find_valid(int speed, int duplex, u32 supported) + { +- return phy_lookup_setting(speed, duplex, supported, false); ++ unsigned long mask = supported; ++ ++ return phy_lookup_setting(speed, duplex, &mask, BITS_PER_LONG, false); + } + + /** +@@ -316,7 +320,9 @@ phy_find_valid(int speed, int duplex, u3 + */ + static inline bool phy_check_valid(int speed, int duplex, u32 features) + { +- return !!phy_lookup_setting(speed, duplex, features, true); ++ unsigned long mask = features; ++ ++ return !!phy_lookup_setting(speed, duplex, &mask, BITS_PER_LONG, true); + } + + /** diff --git a/target/linux/mvebu/patches-4.9/411-net-phy-split-out-PHY-speed-and-duplex-string-genera.patch b/target/linux/mvebu/patches-4.9/411-net-phy-split-out-PHY-speed-and-duplex-string-genera.patch new file mode 100644 index 0000000000..4111b004d2 --- /dev/null +++ b/target/linux/mvebu/patches-4.9/411-net-phy-split-out-PHY-speed-and-duplex-string-genera.patch @@ -0,0 +1,103 @@ +From: Russell King <rmk+kernel@armlinux.org.uk> +Date: Mon, 2 Jan 2017 17:52:18 +0000 +Subject: [PATCH] net: phy: split out PHY speed and duplex string + generation + +Other code would like to make use of this, so make the speed and duplex +string generation visible, and place it in a separate file. + +Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> +--- + +--- a/drivers/net/phy/phy.c ++++ b/drivers/net/phy/phy.c +@@ -38,26 +38,6 @@ + + #include <asm/irq.h> + +-static const char *phy_speed_to_str(int speed) +-{ +- switch (speed) { +- case SPEED_10: +- return "10Mbps"; +- case SPEED_100: +- return "100Mbps"; +- case SPEED_1000: +- return "1Gbps"; +- case SPEED_2500: +- return "2.5Gbps"; +- case SPEED_10000: +- return "10Gbps"; +- case SPEED_UNKNOWN: +- return "Unknown"; +- default: +- return "Unsupported (update phy.c)"; +- } +-} +- + #define PHY_STATE_STR(_state) \ + case PHY_##_state: \ + return __stringify(_state); \ +@@ -93,7 +73,7 @@ void phy_print_status(struct phy_device + netdev_info(phydev->attached_dev, + "Link is Up - %s/%s - flow control %s\n", + phy_speed_to_str(phydev->speed), +- DUPLEX_FULL == phydev->duplex ? "Full" : "Half", ++ phy_duplex_to_str(phydev->duplex), + phydev->pause ? "rx/tx" : "off"); + } else { + netdev_info(phydev->attached_dev, "Link is Down\n"); +--- a/drivers/net/phy/phy-core.c ++++ b/drivers/net/phy/phy-core.c +@@ -9,6 +9,39 @@ + #include <linux/export.h> + #include <linux/phy.h> + ++const char *phy_speed_to_str(int speed) ++{ ++ switch (speed) { ++ case SPEED_10: ++ return "10Mbps"; ++ case SPEED_100: ++ return "100Mbps"; ++ case SPEED_1000: ++ return "1Gbps"; ++ case SPEED_2500: ++ return "2.5Gbps"; ++ case SPEED_10000: ++ return "10Gbps"; ++ case SPEED_UNKNOWN: ++ return "Unknown"; ++ default: ++ return "Unsupported (update phy-core.c)"; ++ } ++} ++EXPORT_SYMBOL_GPL(phy_speed_to_str); ++ ++const char *phy_duplex_to_str(unsigned int duplex) ++{ ++ if (duplex == DUPLEX_HALF) ++ return "Half"; ++ if (duplex == DUPLEX_FULL) ++ return "Full"; ++ if (duplex == DUPLEX_UNKNOWN) ++ return "Unknown"; ++ return "Unsupported (update phy-core.c)"; ++} ++EXPORT_SYMBOL_GPL(phy_duplex_to_str); ++ + static inline void mmd_phy_indirect(struct mii_bus *bus, int prtad, int devad, + int addr) + { +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -642,6 +642,9 @@ struct phy_fixup { + int (*run)(struct phy_device *phydev); + }; + ++const char *phy_speed_to_str(int speed); ++const char *phy_duplex_to_str(unsigned int duplex); ++ + /** + * phy_read_mmd - Convenience function for reading a register + * from an MMD on a given PHY. diff --git a/target/linux/mvebu/patches-4.9/412-net-phy-move-phy_lookup_setting-and-guts-of-phy_supp.patch b/target/linux/mvebu/patches-4.9/412-net-phy-move-phy_lookup_setting-and-guts-of-phy_supp.patch new file mode 100644 index 0000000000..02a8fa5748 --- /dev/null +++ b/target/linux/mvebu/patches-4.9/412-net-phy-move-phy_lookup_setting-and-guts-of-phy_supp.patch @@ -0,0 +1,329 @@ +From: Russell King <rmk+kernel@armlinux.org.uk> +Date: Thu, 5 Jan 2017 16:47:39 +0000 +Subject: [PATCH] net: phy: move phy_lookup_setting() and guts of + phy_supported_speeds() to phy-core + +phy_lookup_setting() provides useful functionality in ethtool code +outside phylib. Move it to phy-core and allow it to be re-used (eg, +in phylink) rather than duplicated elsewhere. Note that this supports +the larger linkmode space. + +As we move the phy settings table, we also need to move the guts of +phy_supported_speeds() as well. + +Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> +--- + +--- a/drivers/net/phy/phy.c ++++ b/drivers/net/phy/phy.c +@@ -149,125 +149,6 @@ static inline int phy_aneg_done(struct p + return genphy_aneg_done(phydev); + } + +-/* A structure for mapping a particular speed and duplex +- * combination to a particular SUPPORTED and ADVERTISED value +- */ +-struct phy_setting { +- int speed; +- int duplex; +- int bit; +-}; +- +-/* 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. */ +-static const struct phy_setting settings[] = { +- { +- .speed = SPEED_10000, +- .duplex = DUPLEX_FULL, +- .bit = ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, +- }, +- { +- .speed = SPEED_10000, +- .duplex = DUPLEX_FULL, +- .bit = ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT, +- }, +- { +- .speed = SPEED_10000, +- .duplex = DUPLEX_FULL, +- .bit = ETHTOOL_LINK_MODE_10000baseT_Full_BIT, +- }, +- { +- .speed = SPEED_2500, +- .duplex = DUPLEX_FULL, +- .bit = ETHTOOL_LINK_MODE_2500baseX_Full_BIT, +- }, +- { +- .speed = SPEED_1000, +- .duplex = DUPLEX_FULL, +- .bit = ETHTOOL_LINK_MODE_1000baseKX_Full_BIT, +- }, +- { +- .speed = SPEED_1000, +- .duplex = DUPLEX_FULL, +- .bit = ETHTOOL_LINK_MODE_1000baseT_Full_BIT, +- }, +- { +- .speed = SPEED_1000, +- .duplex = DUPLEX_HALF, +- .bit = ETHTOOL_LINK_MODE_1000baseT_Half_BIT, +- }, +- { +- .speed = SPEED_100, +- .duplex = DUPLEX_FULL, +- .bit = ETHTOOL_LINK_MODE_100baseT_Full_BIT, +- }, +- { +- .speed = SPEED_100, +- .duplex = DUPLEX_HALF, +- .bit = ETHTOOL_LINK_MODE_100baseT_Half_BIT, +- }, +- { +- .speed = SPEED_10, +- .duplex = DUPLEX_FULL, +- .bit = ETHTOOL_LINK_MODE_10baseT_Full_BIT, +- }, +- { +- .speed = SPEED_10, +- .duplex = DUPLEX_HALF, +- .bit = ETHTOOL_LINK_MODE_10baseT_Half_BIT, +- }, +-}; +- +-/** +- * phy_lookup_setting - lookup a PHY setting +- * @speed: speed to match +- * @duplex: duplex to match +- * @mask: allowed link modes +- * @maxbit: bit size of link modes +- * @exact: an exact match is required +- * +- * Search the settings array for a setting that matches the speed and +- * duplex, and which is supported. +- * +- * If @exact is unset, either an exact match or %NULL for no match will +- * be returned. +- * +- * If @exact is set, an exact match, the fastest supported setting at +- * or below the specified speed, the slowest supported setting, or if +- * they all fail, %NULL will be returned. +- */ +-static const struct phy_setting * +-phy_lookup_setting(int speed, int duplex, const unsigned long *mask, +- size_t maxbit, bool exact) +-{ +- const struct phy_setting *p, *match = NULL, *last = NULL; +- int i; +- +- for (i = 0, p = settings; i < ARRAY_SIZE(settings); i++, p++) { +- if (p->bit < maxbit && test_bit(p->bit, mask)) { +- last = p; +- if (p->speed == speed && p->duplex == duplex) { +- /* Exact match for speed and duplex */ +- match = p; +- break; +- } else if (!exact) { +- if (!match && p->speed <= speed) +- /* Candidate */ +- match = p; +- +- if (p->speed < speed) +- break; +- } +- } +- } +- +- if (!match && !exact) +- match = last; +- +- return match; +-} +- + /** + * phy_find_valid - find a PHY setting that matches the requested parameters + * @speed: desired speed +@@ -290,6 +171,25 @@ phy_find_valid(int speed, int duplex, u3 + } + + /** ++ * phy_supported_speeds - return all speeds currently supported by a phy device ++ * @phy: The phy device to return supported speeds of. ++ * @speeds: buffer to store supported speeds in. ++ * @size: size of speeds buffer. ++ * ++ * Description: Returns the number of supported speeds, and fills the speeds ++ * buffer with the supported speeds. If speeds buffer is too small to contain ++ * all currently supported speeds, will return as many speeds as can fit. ++ */ ++unsigned int phy_supported_speeds(struct phy_device *phy, ++ unsigned int *speeds, ++ unsigned int size) ++{ ++ unsigned long supported = phy->supported; ++ ++ return phy_speeds(speeds, size, &supported, BITS_PER_LONG); ++} ++ ++/** + * phy_check_valid - check if there is a valid PHY setting which matches + * speed, duplex, and feature mask + * @speed: speed to match +--- a/drivers/net/phy/phy-core.c ++++ b/drivers/net/phy/phy-core.c +@@ -42,6 +42,132 @@ const char *phy_duplex_to_str(unsigned i + } + 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. */ ++static const struct phy_setting settings[] = { ++ { ++ .speed = SPEED_10000, ++ .duplex = DUPLEX_FULL, ++ .bit = ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, ++ }, ++ { ++ .speed = SPEED_10000, ++ .duplex = DUPLEX_FULL, ++ .bit = ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT, ++ }, ++ { ++ .speed = SPEED_10000, ++ .duplex = DUPLEX_FULL, ++ .bit = ETHTOOL_LINK_MODE_10000baseT_Full_BIT, ++ }, ++ { ++ .speed = SPEED_2500, ++ .duplex = DUPLEX_FULL, ++ .bit = ETHTOOL_LINK_MODE_2500baseX_Full_BIT, ++ }, ++ { ++ .speed = SPEED_1000, ++ .duplex = DUPLEX_FULL, ++ .bit = ETHTOOL_LINK_MODE_1000baseKX_Full_BIT, ++ }, ++ { ++ .speed = SPEED_1000, ++ .duplex = DUPLEX_FULL, ++ .bit = ETHTOOL_LINK_MODE_1000baseT_Full_BIT, ++ }, ++ { ++ .speed = SPEED_1000, ++ .duplex = DUPLEX_HALF, ++ .bit = ETHTOOL_LINK_MODE_1000baseT_Half_BIT, ++ }, ++ { ++ .speed = SPEED_100, ++ .duplex = DUPLEX_FULL, ++ .bit = ETHTOOL_LINK_MODE_100baseT_Full_BIT, ++ }, ++ { ++ .speed = SPEED_100, ++ .duplex = DUPLEX_HALF, ++ .bit = ETHTOOL_LINK_MODE_100baseT_Half_BIT, ++ }, ++ { ++ .speed = SPEED_10, ++ .duplex = DUPLEX_FULL, ++ .bit = ETHTOOL_LINK_MODE_10baseT_Full_BIT, ++ }, ++ { ++ .speed = SPEED_10, ++ .duplex = DUPLEX_HALF, ++ .bit = ETHTOOL_LINK_MODE_10baseT_Half_BIT, ++ }, ++}; ++ ++/** ++ * phy_lookup_setting - lookup a PHY setting ++ * @speed: speed to match ++ * @duplex: duplex to match ++ * @mask: allowed link modes ++ * @maxbit: bit size of link modes ++ * @exact: an exact match is required ++ * ++ * Search the settings array for a setting that matches the speed and ++ * duplex, and which is supported. ++ * ++ * If @exact is unset, either an exact match or %NULL for no match will ++ * be returned. ++ * ++ * If @exact is set, an exact match, the fastest supported setting at ++ * or below the specified speed, the slowest supported setting, or if ++ * they all fail, %NULL will be returned. ++ */ ++const struct phy_setting * ++phy_lookup_setting(int speed, int duplex, const unsigned long *mask, ++ size_t maxbit, bool exact) ++{ ++ const struct phy_setting *p, *match = NULL, *last = NULL; ++ int i; ++ ++ for (i = 0, p = settings; i < ARRAY_SIZE(settings); i++, p++) { ++ if (p->bit < maxbit && test_bit(p->bit, mask)) { ++ last = p; ++ if (p->speed == speed && p->duplex == duplex) { ++ /* Exact match for speed and duplex */ ++ match = p; ++ break; ++ } else if (!exact) { ++ if (!match && p->speed <= speed) ++ /* Candidate */ ++ match = p; ++ ++ if (p->speed < speed) ++ break; ++ } ++ } ++ } ++ ++ if (!match && !exact) ++ match = last; ++ ++ return match; ++} ++EXPORT_SYMBOL_GPL(phy_lookup_setting); ++ ++size_t phy_speeds(unsigned int *speeds, size_t size, ++ unsigned long *mask, size_t maxbit) ++{ ++ size_t count; ++ int i; ++ ++ for (i = 0, count = 0; i < ARRAY_SIZE(settings) && count < size; i++) ++ if (settings[i].bit < maxbit && ++ test_bit(settings[i].bit, mask) && ++ (count == 0 || speeds[count - 1] != settings[i].speed)) ++ speeds[count++] = settings[i].speed; ++ ++ return count; ++} ++ + static inline void mmd_phy_indirect(struct mii_bus *bus, int prtad, int devad, + int addr) + { +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -645,6 +645,21 @@ struct phy_fixup { + const char *phy_speed_to_str(int speed); + const char *phy_duplex_to_str(unsigned int duplex); + ++/* A structure for mapping a particular speed and duplex ++ * combination to a particular SUPPORTED and ADVERTISED value ++ */ ++struct phy_setting { ++ u32 speed; ++ u8 duplex; ++ u8 bit; ++}; ++ ++const struct phy_setting * ++phy_lookup_setting(int speed, int duplex, const unsigned long *mask, ++ size_t maxbit, bool exact); ++size_t phy_speeds(unsigned int *speeds, size_t size, ++ unsigned long *mask, size_t maxbit); ++ + /** + * phy_read_mmd - Convenience function for reading a register + * from an MMD on a given PHY. diff --git a/target/linux/mvebu/patches-4.9/413-phy-export-phy_start_machine-for-phylink.patch b/target/linux/mvebu/patches-4.9/413-phy-export-phy_start_machine-for-phylink.patch new file mode 100644 index 0000000000..93f979ab53 --- /dev/null +++ b/target/linux/mvebu/patches-4.9/413-phy-export-phy_start_machine-for-phylink.patch @@ -0,0 +1,22 @@ +From: Russell King <rmk+kernel@arm.linux.org.uk> +Date: Fri, 16 Oct 2015 12:18:41 +0100 +Subject: [PATCH] phy: export phy_start_machine() for phylink + +phylink will need phy_start_machine exported, so lets export it as a +GPL symbol. Documentation/networking/phy.txt indicates that this +should be a PHY API function. + +Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> +Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> +--- + +--- a/drivers/net/phy/phy.c ++++ b/drivers/net/phy/phy.c +@@ -580,6 +580,7 @@ void phy_start_machine(struct phy_device + { + queue_delayed_work(system_power_efficient_wq, &phydev->state_queue, HZ); + } ++EXPORT_SYMBOL_GPL(phy_start_machine); + + /** + * phy_trigger_machine - trigger the state machine to run diff --git a/target/linux/mvebu/patches-4.9/414-phy-add-I2C-mdio-bus.patch b/target/linux/mvebu/patches-4.9/414-phy-add-I2C-mdio-bus.patch new file mode 100644 index 0000000000..c389817c6e --- /dev/null +++ b/target/linux/mvebu/patches-4.9/414-phy-add-I2C-mdio-bus.patch @@ -0,0 +1,180 @@ +From: Russell King <rmk+kernel@arm.linux.org.uk> +Date: Fri, 25 Sep 2015 17:43:52 +0100 +Subject: [PATCH] phy: add I2C mdio bus + +Add an I2C MDIO bus bridge library, to allow phylib to access PHYs which +are connected to an I2C bus instead of the more conventional MDIO bus. +Such PHYs can be found in SFP adapters and SFF modules. + +Since PHYs appear at I2C bus address 0x40..0x5f, and 0x50/0x51 are +reserved for SFP EEPROMs/diagnostics, we must not allow the MDIO bus +to access these I2C addresses. + +Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> +--- + create mode 100644 drivers/net/phy/mdio-i2c.c + create mode 100644 drivers/net/phy/mdio-i2c.h + +--- a/drivers/net/phy/Kconfig ++++ b/drivers/net/phy/Kconfig +@@ -105,6 +105,16 @@ config MDIO_HISI_FEMAC + This module provides a driver for the MDIO busses found in the + Hisilicon SoC that have an Fast Ethernet MAC. + ++config MDIO_I2C ++ tristate ++ depends on I2C ++ help ++ Support I2C based PHYs. This provides a MDIO bus bridged ++ to I2C to allow PHYs connected in I2C mode to be accessed ++ using the existing infrastructure. ++ ++ This is library mode. ++ + config MDIO_MOXART + tristate "MOXA ART MDIO interface support" + depends on ARCH_MOXART +--- a/drivers/net/phy/Makefile ++++ b/drivers/net/phy/Makefile +@@ -33,6 +33,7 @@ obj-$(CONFIG_MDIO_BUS_MUX_MMIOREG) += md + obj-$(CONFIG_MDIO_CAVIUM) += mdio-cavium.o + obj-$(CONFIG_MDIO_GPIO) += mdio-gpio.o + obj-$(CONFIG_MDIO_HISI_FEMAC) += mdio-hisi-femac.o ++obj-$(CONFIG_MDIO_I2C) += mdio-i2c.o + obj-$(CONFIG_MDIO_MOXART) += mdio-moxart.o + obj-$(CONFIG_MDIO_OCTEON) += mdio-octeon.o + obj-$(CONFIG_MDIO_SUN4I) += mdio-sun4i.o +--- /dev/null ++++ b/drivers/net/phy/mdio-i2c.c +@@ -0,0 +1,109 @@ ++/* ++ * MDIO I2C bridge ++ * ++ * Copyright (C) 2015-2016 Russell King ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * Network PHYs can appear on I2C buses when they are part of SFP module. ++ * This driver exposes these PHYs to the networking PHY code, allowing ++ * our PHY drivers access to these PHYs, and so allowing configuration ++ * of their settings. ++ */ ++#include <linux/i2c.h> ++#include <linux/phy.h> ++ ++#include "mdio-i2c.h" ++ ++/* ++ * I2C bus addresses 0x50 and 0x51 are normally an EEPROM, which is ++ * specified to be present in SFP modules. These correspond with PHY ++ * addresses 16 and 17. Disallow access to these "phy" addresses. ++ */ ++static bool i2c_mii_valid_phy_id(int phy_id) ++{ ++ return phy_id != 0x10 && phy_id != 0x11; ++} ++ ++static unsigned int i2c_mii_phy_addr(int phy_id) ++{ ++ return phy_id + 0x40; ++} ++ ++static int i2c_mii_read(struct mii_bus *bus, int phy_id, int reg) ++{ ++ struct i2c_adapter *i2c = bus->priv; ++ struct i2c_msg msgs[2]; ++ u8 data[2], dev_addr = reg; ++ int bus_addr, ret; ++ ++ if (!i2c_mii_valid_phy_id(phy_id)) ++ return 0xffff; ++ ++ bus_addr = i2c_mii_phy_addr(phy_id); ++ msgs[0].addr = bus_addr; ++ msgs[0].flags = 0; ++ msgs[0].len = 1; ++ msgs[0].buf = &dev_addr; ++ msgs[1].addr = bus_addr; ++ msgs[1].flags = I2C_M_RD; ++ msgs[1].len = sizeof(data); ++ msgs[1].buf = data; ++ ++ ret = i2c_transfer(i2c, msgs, ARRAY_SIZE(msgs)); ++ if (ret != ARRAY_SIZE(msgs)) ++ return 0xffff; ++ ++ return data[0] << 8 | data[1]; ++} ++ ++static int i2c_mii_write(struct mii_bus *bus, int phy_id, int reg, u16 val) ++{ ++ struct i2c_adapter *i2c = bus->priv; ++ struct i2c_msg msg; ++ int ret; ++ u8 data[3]; ++ ++ if (!i2c_mii_valid_phy_id(phy_id)) ++ return 0; ++ ++ data[0] = reg; ++ data[1] = val >> 8; ++ data[2] = val; ++ ++ msg.addr = i2c_mii_phy_addr(phy_id); ++ msg.flags = 0; ++ msg.len = 3; ++ msg.buf = data; ++ ++ ret = i2c_transfer(i2c, &msg, 1); ++ ++ return ret < 0 ? ret : 0; ++} ++ ++struct mii_bus *mdio_i2c_alloc(struct device *parent, struct i2c_adapter *i2c) ++{ ++ struct mii_bus *mii; ++ ++ if (!i2c_check_functionality(i2c, I2C_FUNC_I2C)) ++ return ERR_PTR(-EINVAL); ++ ++ mii = mdiobus_alloc(); ++ if (!mii) ++ return ERR_PTR(-ENOMEM); ++ ++ snprintf(mii->id, MII_BUS_ID_SIZE, "i2c:%s", dev_name(parent)); ++ mii->parent = parent; ++ mii->read = i2c_mii_read; ++ mii->write = i2c_mii_write; ++ mii->priv = i2c; ++ ++ return mii; ++} ++EXPORT_SYMBOL_GPL(mdio_i2c_alloc); ++ ++MODULE_AUTHOR("Russell King"); ++MODULE_DESCRIPTION("MDIO I2C bridge library"); ++MODULE_LICENSE("GPL v2"); +--- /dev/null ++++ b/drivers/net/phy/mdio-i2c.h +@@ -0,0 +1,19 @@ ++/* ++ * MDIO I2C bridge ++ * ++ * Copyright (C) 2015 Russell King ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++#ifndef MDIO_I2C_H ++#define MDIO_I2C_H ++ ++struct device; ++struct i2c_adapter; ++struct mii_bus; ++ ++struct mii_bus *mdio_i2c_alloc(struct device *parent, struct i2c_adapter *i2c); ++ ++#endif diff --git a/target/linux/mvebu/patches-4.9/415-phylink-add-phylink-infrastructure.patch b/target/linux/mvebu/patches-4.9/415-phylink-add-phylink-infrastructure.patch new file mode 100644 index 0000000000..87c0daf178 --- /dev/null +++ b/target/linux/mvebu/patches-4.9/415-phylink-add-phylink-infrastructure.patch @@ -0,0 +1,1120 @@ +From: Russell King <rmk+kernel@arm.linux.org.uk> +Date: Tue, 22 Sep 2015 20:52:18 +0100 +Subject: [PATCH] phylink: add phylink infrastructure + +The link between the ethernet MAC and its PHY has become more complex +as the interface evolves. This is especially true with serdes links, +where the part of the PHY is effectively integrated into the MAC. + +Serdes links can be connected to a variety of devices, including SFF +modules soldered down onto the board with the MAC, a SFP cage with +a hotpluggable SFP module which may contain a PHY or directly modulate +the serdes signals onto optical media with or without a PHY, or even +a classical PHY connection. + +Moreover, the negotiation information on serdes links comes in two +varieties - SGMII mode, where the PHY provides its speed/duplex/flow +control information to the MAC, and 1000base-X mode where both ends +exchange their abilities and each resolve the link capabilities. + +This means we need a more flexible means to support these arrangements, +particularly with the hotpluggable nature of SFP, where the PHY can +be attached or detached after the network device has been brought up. + +Ethtool information can come from multiple sources: +- we may have a PHY operating in either SGMII or 1000base-X mode, in + which case we take ethtool/mii data directly from the PHY. +- we may have a optical SFP module without a PHY, with the MAC + operating in 1000base-X mode - the ethtool/mii data needs to come + from the MAC. +- we may have a copper SFP module with a PHY whic can't be accessed, + which means we need to take ethtool/mii data from the MAC. + +Phylink aims to solve this by providing an intermediary between the +MAC and PHY, providing a safe way for PHYs to be hotplugged, and +allowing a SFP driver to reconfigure the serdes connection. + +Phylink also takes over support of fixed link connections, where the +speed/duplex/flow control are fixed, but link status may be controlled +by a GPIO signal. By avoiding the fixed-phy implementation, phylink +can provide a faster response to link events: fixed-phy has to wait for +phylib to operate its state machine, which can take several seconds. +In comparison, phylink takes milliseconds. + +Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> + +- remove sync status +- rework supported and advertisment handling +- add 1000base-x speed for fixed links +- use functionality exported from phy-core, reworking + __phylink_ethtool_ksettings_set for it +--- + create mode 100644 drivers/net/phy/phylink.c + create mode 100644 include/linux/phylink.h + +--- a/drivers/net/phy/Kconfig ++++ b/drivers/net/phy/Kconfig +@@ -10,6 +10,16 @@ menuconfig PHYLIB + devices. This option provides infrastructure for + managing PHY devices. + ++config PHYLINK ++ tristate ++ depends on NETDEVICES ++ select PHYLIB ++ select SWPHY ++ help ++ PHYlink models the link between the PHY and MAC, allowing fixed ++ configuration links, PHYs, and Serdes links with MAC level ++ autonegotiation modes. ++ + if PHYLIB + + config SWPHY +--- a/drivers/net/phy/Makefile ++++ b/drivers/net/phy/Makefile +@@ -6,6 +6,7 @@ libphy-$(CONFIG_SWPHY) += swphy.o + + obj-$(CONFIG_MDIO_BOARDINFO) += mdio-boardinfo.o + ++obj-$(CONFIG_PHYLINK) += phylink.o + obj-$(CONFIG_PHYLIB) += libphy.o + + obj-$(CONFIG_SWCONFIG) += swconfig.o +--- a/drivers/net/phy/phy_device.c ++++ b/drivers/net/phy/phy_device.c +@@ -1003,6 +1003,7 @@ void phy_detach(struct phy_device *phyde + phydev->attached_dev->phydev = NULL; + phydev->attached_dev = NULL; + phy_suspend(phydev); ++ phydev->phylink = NULL; + + /* If the device had no specific driver before (i.e. - it + * was using the generic driver), we unbind the device +--- /dev/null ++++ b/drivers/net/phy/phylink.c +@@ -0,0 +1,903 @@ ++/* ++ * phylink models the MAC to optional PHY connection, supporting ++ * technologies such as SFP cages where the PHY is hot-pluggable. ++ * ++ * Copyright (C) 2015 Russell King ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++#include <linux/ethtool.h> ++#include <linux/export.h> ++#include <linux/gpio/consumer.h> ++#include <linux/netdevice.h> ++#include <linux/of.h> ++#include <linux/of_mdio.h> ++#include <linux/phy.h> ++#include <linux/phy_fixed.h> ++#include <linux/phylink.h> ++#include <linux/spinlock.h> ++#include <linux/workqueue.h> ++ ++#include "swphy.h" ++ ++#define SUPPORTED_INTERFACES \ ++ (SUPPORTED_TP | SUPPORTED_MII | SUPPORTED_FIBRE | \ ++ SUPPORTED_BNC | SUPPORTED_AUI | SUPPORTED_Backplane) ++#define ADVERTISED_INTERFACES \ ++ (ADVERTISED_TP | ADVERTISED_MII | ADVERTISED_FIBRE | \ ++ ADVERTISED_BNC | ADVERTISED_AUI | ADVERTISED_Backplane) ++ ++enum { ++ PHYLINK_DISABLE_STOPPED, ++}; ++ ++struct phylink { ++ struct net_device *netdev; ++ const struct phylink_mac_ops *ops; ++ struct mutex config_mutex; ++ ++ unsigned long phylink_disable_state; /* bitmask of disables */ ++ struct phy_device *phydev; ++ phy_interface_t link_interface; /* PHY_INTERFACE_xxx */ ++ u8 link_an_mode; /* MLO_AN_xxx */ ++ u8 link_port; /* The current non-phy ethtool port */ ++ /* ethtool supported mask for ports */ ++ __ETHTOOL_DECLARE_LINK_MODE_MASK(supported); ++ ++ /* The link configuration settings */ ++ struct phylink_link_state link_config; ++ struct gpio_desc *link_gpio; ++ ++ struct mutex state_mutex; /* may be taken within config_mutex */ ++ struct phylink_link_state phy_state; ++ struct work_struct resolve; ++ ++ const struct phylink_module_ops *module_ops; ++ void *module_data; ++}; ++ ++static inline void linkmode_zero(unsigned long *dst) ++{ ++ bitmap_zero(dst, __ETHTOOL_LINK_MODE_MASK_NBITS); ++} ++ ++static inline void linkmode_copy(unsigned long *dst, const unsigned long *src) ++{ ++ bitmap_copy(dst, src, __ETHTOOL_LINK_MODE_MASK_NBITS); ++} ++ ++static inline void linkmode_and(unsigned long *dst, const unsigned long *a, ++ const unsigned long *b) ++{ ++ bitmap_and(dst, a, b, __ETHTOOL_LINK_MODE_MASK_NBITS); ++} ++ ++static inline void linkmode_or(unsigned long *dst, const unsigned long *a, ++ const unsigned long *b) ++{ ++ bitmap_or(dst, a, b, __ETHTOOL_LINK_MODE_MASK_NBITS); ++} ++ ++static inline bool linkmode_empty(const unsigned long *src) ++{ ++ return bitmap_empty(src, __ETHTOOL_LINK_MODE_MASK_NBITS); ++} ++ ++static void phylink_set_port_bits(unsigned long *bits) ++{ ++ __set_bit(ETHTOOL_LINK_MODE_TP_BIT, bits); ++ __set_bit(ETHTOOL_LINK_MODE_AUI_BIT, bits); ++ __set_bit(ETHTOOL_LINK_MODE_MII_BIT, bits); ++ __set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, bits); ++ __set_bit(ETHTOOL_LINK_MODE_BNC_BIT, bits); ++ __set_bit(ETHTOOL_LINK_MODE_Backplane_BIT, bits); ++} ++ ++static const char *phylink_an_mode_str(unsigned int mode) ++{ ++ static const char *modestr[] = { ++ [MLO_AN_PHY] = "phy", ++ [MLO_AN_FIXED] = "fixed", ++ [MLO_AN_SGMII] = "SGMII", ++ [MLO_AN_8023Z] = "802.3z", ++ }; ++ ++ return mode < ARRAY_SIZE(modestr) ? modestr[mode] : "unknown"; ++} ++ ++static int phylink_parse_fixedlink(struct phylink *pl, struct device_node *np) ++{ ++ __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; ++ struct device_node *fixed_node; ++ const struct phy_setting *s; ++ struct gpio_desc *desc; ++ const __be32 *fixed_prop; ++ u32 speed; ++ int ret, len; ++ ++ fixed_node = of_get_child_by_name(np, "fixed-link"); ++ if (fixed_node) { ++ ret = of_property_read_u32(fixed_node, "speed", &speed); ++ ++ pl->link_config.speed = speed; ++ pl->link_config.duplex = DUPLEX_HALF; ++ ++ if (of_property_read_bool(fixed_node, "full-duplex")) ++ pl->link_config.duplex = DUPLEX_FULL; ++ if (of_property_read_bool(fixed_node, "pause")) ++ pl->link_config.pause |= MLO_PAUSE_SYM; ++ if (of_property_read_bool(fixed_node, "asym-pause")) ++ pl->link_config.pause |= MLO_PAUSE_ASYM; ++ ++ if (ret == 0) { ++ desc = fwnode_get_named_gpiod(&fixed_node->fwnode, ++ "link-gpios"); ++ ++ if (!IS_ERR(desc)) ++ pl->link_gpio = desc; ++ else if (desc == ERR_PTR(-EPROBE_DEFER)) ++ ret = -EPROBE_DEFER; ++ } ++ of_node_put(fixed_node); ++ ++ if (ret) ++ return ret; ++ } else { ++ fixed_prop = of_get_property(np, "fixed-link", &len); ++ if (!fixed_prop) { ++ netdev_err(pl->netdev, "broken fixed-link?\n"); ++ return -EINVAL; ++ } ++ if (len == 5 * sizeof(*fixed_prop)) { ++ pl->link_config.duplex = be32_to_cpu(fixed_prop[1]) ? ++ DUPLEX_FULL : DUPLEX_HALF; ++ pl->link_config.speed = be32_to_cpu(fixed_prop[2]); ++ if (be32_to_cpu(fixed_prop[3])) ++ pl->link_config.pause |= MLO_PAUSE_SYM; ++ if (be32_to_cpu(fixed_prop[4])) ++ pl->link_config.pause |= MLO_PAUSE_ASYM; ++ } ++ } ++ ++ bitmap_fill(mask, __ETHTOOL_LINK_MODE_MASK_NBITS); ++ pl->ops->validate_support(pl->netdev, MLO_AN_FIXED, mask); ++ ++ pl->link_config.link = 1; ++ pl->link_config.an_complete = 1; ++ ++ if (pl->link_config.speed > SPEED_1000 && ++ pl->link_config.duplex != DUPLEX_FULL) ++ netdev_warn(pl->netdev, "fixed link specifies half duplex for %dMbps link?\n", ++ pl->link_config.speed); ++ ++ s = phy_lookup_setting(pl->link_config.speed, pl->link_config.duplex, ++ mask, __ETHTOOL_LINK_MODE_MASK_NBITS, true); ++ if (s) { ++ __set_bit(s->bit, pl->supported); ++ } else { ++ netdev_warn(pl->netdev, "fixed link %s duplex %dMbps not recognised\n", ++ pl->link_config.duplex == DUPLEX_FULL ? "full" : "half", ++ pl->link_config.speed); ++ } ++ return 0; ++} ++ ++static int phylink_parse_mode(struct phylink *pl, struct device_node *np) ++{ ++ struct device_node *dn; ++ const char *managed; ++ ++ dn = of_get_child_by_name(np, "fixed-link"); ++ if (dn || of_find_property(np, "fixed-link", NULL)) ++ pl->link_an_mode = MLO_AN_FIXED; ++ of_node_put(dn); ++ ++ if (of_property_read_string(np, "managed", &managed) == 0 && ++ strcmp(managed, "in-band-status") == 0) { ++ if (pl->link_an_mode == MLO_AN_FIXED) { ++ netdev_err(pl->netdev, ++ "can't use both fixed-link and in-band-status\n"); ++ return -EINVAL; ++ } ++ phylink_set(pl->supported, 10baseT_Half); ++ phylink_set(pl->supported, 10baseT_Full); ++ phylink_set(pl->supported, 100baseT_Half); ++ phylink_set(pl->supported, 100baseT_Full); ++ phylink_set(pl->supported, 1000baseT_Half); ++ phylink_set(pl->supported, 1000baseT_Full); ++ phylink_set(pl->supported, Asym_Pause); ++ phylink_set(pl->supported, Pause); ++ pl->link_an_mode = MLO_AN_SGMII; ++ pl->link_config.an_enabled = true; ++ pl->ops->validate_support(pl->netdev, pl->link_an_mode, ++ pl->supported); ++ } ++ ++ return 0; ++} ++ ++ ++static void phylink_init_advert(struct phylink *pl, unsigned int mode, ++ const unsigned long *supported, ++ unsigned long *advertising) ++{ ++ linkmode_copy(advertising, supported); ++ if (pl->ops->validate_advert) ++ pl->ops->validate_advert(pl->netdev, mode, supported, ++ advertising); ++} ++ ++static void phylink_mac_config(struct phylink *pl, ++ const struct phylink_link_state *state) ++{ ++ netdev_dbg(pl->netdev, ++ "%s: mode=%s/%s/%s adv=%*pb pause=%02x link=%u an=%u\n", ++ __func__, phylink_an_mode_str(pl->link_an_mode), ++ phy_speed_to_str(state->speed), ++ phy_duplex_to_str(state->duplex), ++ __ETHTOOL_LINK_MODE_MASK_NBITS, state->advertising, ++ state->pause, state->link, state->an_enabled); ++ ++ pl->ops->mac_config(pl->netdev, pl->link_an_mode, state); ++} ++ ++static void phylink_mac_an_restart(struct phylink *pl) ++{ ++ if (pl->link_config.an_enabled) ++ pl->ops->mac_an_restart(pl->netdev, pl->link_an_mode); ++} ++ ++static int phylink_get_mac_state(struct phylink *pl, struct phylink_link_state *state) ++{ ++ struct net_device *ndev = pl->netdev; ++ ++ linkmode_copy(state->advertising, pl->link_config.advertising); ++ linkmode_zero(state->lp_advertising); ++ state->an_enabled = pl->link_config.an_enabled; ++ state->link = 1; ++ ++ return pl->ops->mac_link_state(ndev, state); ++} ++ ++/* The fixed state is... fixed except for the link state, ++ * which may be determined by a GPIO. ++ */ ++static void phylink_get_fixed_state(struct phylink *pl, struct phylink_link_state *state) ++{ ++ *state = pl->link_config; ++ if (pl->link_gpio) ++ state->link = !!gpiod_get_value(pl->link_gpio); ++} ++ ++static void phylink_resolve(struct work_struct *w) ++{ ++ struct phylink *pl = container_of(w, struct phylink, resolve); ++ struct phylink_link_state link_state; ++ struct net_device *ndev = pl->netdev; ++ ++ mutex_lock(&pl->state_mutex); ++ if (pl->phylink_disable_state) { ++ link_state.link = false; ++ } else { ++ switch (pl->link_an_mode) { ++ case MLO_AN_PHY: ++ link_state = pl->phy_state; ++ break; ++ ++ case MLO_AN_FIXED: ++ phylink_get_fixed_state(pl, &link_state); ++ break; ++ ++ case MLO_AN_SGMII: ++ phylink_get_mac_state(pl, &link_state); ++ if (pl->phydev) ++ link_state.link = link_state.link && ++ pl->phy_state.link; ++ break; ++ ++ case MLO_AN_8023Z: ++ phylink_get_mac_state(pl, &link_state); ++ break; ++ } ++ } ++ ++ if (link_state.link != netif_carrier_ok(ndev)) { ++ if (!link_state.link) { ++ netif_carrier_off(ndev); ++ pl->ops->mac_link_down(ndev, pl->link_an_mode); ++ netdev_info(ndev, "Link is Down\n"); ++ } else { ++ /* If we have a PHY, we need the MAC updated with ++ * the current link parameters (eg, in SGMII mode, ++ * with flow control status.) ++ */ ++ if (pl->phydev) ++ phylink_mac_config(pl, &link_state); ++ ++ pl->ops->mac_link_up(ndev, pl->link_an_mode); ++ ++ netif_carrier_on(ndev); ++ ++ netdev_info(ndev, ++ "Link is Up - %s/%s - flow control %s\n", ++ phy_speed_to_str(link_state.speed), ++ phy_duplex_to_str(link_state.duplex), ++ link_state.pause ? "rx/tx" : "off"); ++ } ++ } ++ mutex_unlock(&pl->state_mutex); ++} ++ ++static void phylink_run_resolve(struct phylink *pl) ++{ ++ if (!pl->phylink_disable_state) ++ queue_work(system_power_efficient_wq, &pl->resolve); ++} ++ ++struct phylink *phylink_create(struct net_device *ndev, struct device_node *np, ++ phy_interface_t iface, const struct phylink_mac_ops *ops) ++{ ++ struct phylink *pl; ++ int ret; ++ ++ pl = kzalloc(sizeof(*pl), GFP_KERNEL); ++ if (!pl) ++ return ERR_PTR(-ENOMEM); ++ ++ mutex_init(&pl->state_mutex); ++ mutex_init(&pl->config_mutex); ++ INIT_WORK(&pl->resolve, phylink_resolve); ++ pl->netdev = ndev; ++ pl->link_interface = iface; ++ pl->link_port = PORT_MII; ++ pl->link_config.speed = SPEED_UNKNOWN; ++ pl->link_config.duplex = DUPLEX_UNKNOWN; ++ pl->ops = ops; ++ __set_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state); ++ ++ ret = phylink_parse_mode(pl, np); ++ if (ret < 0) { ++ kfree(pl); ++ return ERR_PTR(ret); ++ } ++ ++ if (pl->link_an_mode == MLO_AN_FIXED) { ++ ret = phylink_parse_fixedlink(pl, np); ++ if (ret < 0) { ++ kfree(pl); ++ return ERR_PTR(ret); ++ } ++ } ++ ++ phylink_set(pl->supported, MII); ++ phylink_init_advert(pl, pl->link_an_mode, pl->supported, ++ pl->link_config.advertising); ++ ++ return pl; ++} ++EXPORT_SYMBOL_GPL(phylink_create); ++ ++void phylink_destroy(struct phylink *pl) ++{ ++ cancel_work_sync(&pl->resolve); ++ kfree(pl); ++} ++EXPORT_SYMBOL_GPL(phylink_destroy); ++ ++void phylink_phy_change(struct phy_device *phydev, bool up, bool do_carrier) ++{ ++ struct phylink *pl = phydev->phylink; ++ ++ 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; ++ pl->phy_state.link = up; ++ mutex_unlock(&pl->state_mutex); ++ ++ phylink_run_resolve(pl); ++ ++ netdev_dbg(pl->netdev, "phy link %s %s/%s\n", up ? "up" : "down", ++ phy_speed_to_str(phydev->speed), ++ phy_duplex_to_str(phydev->duplex)); ++} ++ ++static int phylink_empty_linkmode(const unsigned long *linkmode) ++{ ++ __ETHTOOL_DECLARE_LINK_MODE_MASK(tmp) = { 0, }; ++ ++ phylink_set_port_bits(tmp); ++ phylink_set(tmp, Autoneg); ++ phylink_set(tmp, Pause); ++ phylink_set(tmp, Asym_Pause); ++ ++ bitmap_andnot(tmp, linkmode, tmp, __ETHTOOL_LINK_MODE_MASK_NBITS); ++ ++ return linkmode_empty(tmp); ++} ++ ++static int phylink_validate_support(struct phylink *pl, int mode, ++ unsigned long *mask) ++{ ++ pl->ops->validate_support(pl->netdev, mode, mask); ++ ++ return phylink_empty_linkmode(mask) ? -EINVAL : 0; ++} ++ ++static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy) ++{ ++ __ETHTOOL_DECLARE_LINK_MODE_MASK(mask); ++ u32 advertising; ++ int ret; ++ ++ ethtool_convert_legacy_u32_to_link_mode(mask, phy->supported); ++ ret = phylink_validate_support(pl, pl->link_an_mode, mask); ++ if (ret) ++ return ret; ++ ++ mutex_lock(&pl->config_mutex); ++ phy->phylink = pl; ++ phy->phy_link_change = phylink_phy_change; ++ ++ netdev_info(pl->netdev, ++ "PHY [%s] driver [%s]\n", dev_name(&phy->mdio.dev), ++ phy->drv->name); ++ ++ mutex_lock(&pl->state_mutex); ++ pl->phydev = phy; ++ linkmode_copy(pl->supported, mask); ++ ++ /* Restrict the phy advertisment according to the MAC support. */ ++ ethtool_convert_link_mode_to_legacy_u32(&advertising, mask); ++ phy->advertising &= ADVERTISED_INTERFACES | advertising; ++ ethtool_convert_legacy_u32_to_link_mode(pl->link_config.advertising, ++ phy->advertising); ++ mutex_unlock(&pl->state_mutex); ++ ++ netdev_dbg(pl->netdev, ++ "phy: setting supported %*pb advertising 0x%08x\n", ++ __ETHTOOL_LINK_MODE_MASK_NBITS, pl->supported, ++ phy->advertising); ++ ++ phy_start_machine(phy); ++ if (phy->irq > 0) ++ phy_start_interrupts(phy); ++ ++ mutex_unlock(&pl->config_mutex); ++ ++ return 0; ++} ++ ++int phylink_connect_phy(struct phylink *pl, struct phy_device *phy) ++{ ++ int ret; ++ ++ ret = phy_attach_direct(pl->netdev, phy, 0, pl->link_interface); ++ if (ret) ++ return ret; ++ ++ ret = phylink_bringup_phy(pl, phy); ++ if (ret) ++ phy_detach(phy); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(phylink_connect_phy); ++ ++int phylink_of_phy_connect(struct phylink *pl, struct device_node *dn) ++{ ++ struct device_node *phy_node; ++ struct phy_device *phy_dev; ++ int ret; ++ ++ /* Fixed links are handled without needing a PHY */ ++ if (pl->link_an_mode == MLO_AN_FIXED) ++ 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) { ++ if (pl->link_an_mode == MLO_AN_PHY) { ++ netdev_err(pl->netdev, "unable to find PHY node\n"); ++ return -ENODEV; ++ } ++ return 0; ++ } ++ ++ phy_dev = of_phy_attach(pl->netdev, phy_node, 0, pl->link_interface); ++ /* We're done with the phy_node handle */ ++ of_node_put(phy_node); ++ ++ if (!phy_dev) ++ return -ENODEV; ++ ++ ret = phylink_bringup_phy(pl, phy_dev); ++ if (ret) ++ phy_detach(phy_dev); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(phylink_of_phy_connect); ++ ++void phylink_disconnect_phy(struct phylink *pl) ++{ ++ struct phy_device *phy; ++ ++ mutex_lock(&pl->config_mutex); ++ phy = pl->phydev; ++ ++ mutex_lock(&pl->state_mutex); ++ pl->phydev = NULL; ++ mutex_unlock(&pl->state_mutex); ++ flush_work(&pl->resolve); ++ ++ if (phy) ++ phy_disconnect(phy); ++ ++ mutex_unlock(&pl->config_mutex); ++} ++EXPORT_SYMBOL_GPL(phylink_disconnect_phy); ++ ++void phylink_mac_change(struct phylink *pl, bool up) ++{ ++ phylink_run_resolve(pl); ++ netdev_dbg(pl->netdev, "mac link %s\n", up ? "up" : "down"); ++} ++EXPORT_SYMBOL_GPL(phylink_mac_change); ++ ++void phylink_start(struct phylink *pl) ++{ ++ mutex_lock(&pl->config_mutex); ++ ++ netdev_info(pl->netdev, "configuring for %s link mode\n", ++ phylink_an_mode_str(pl->link_an_mode)); ++ ++ /* 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 advertisment for Serdes links. ++ */ ++ phylink_mac_config(pl, &pl->link_config); ++ ++ clear_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state); ++ phylink_run_resolve(pl); ++ ++ if (pl->phydev) ++ phy_start(pl->phydev); ++ ++ mutex_unlock(&pl->config_mutex); ++} ++EXPORT_SYMBOL_GPL(phylink_start); ++ ++void phylink_stop(struct phylink *pl) ++{ ++ mutex_lock(&pl->config_mutex); ++ ++ if (pl->phydev) ++ phy_stop(pl->phydev); ++ ++ set_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state); ++ flush_work(&pl->resolve); ++ ++ mutex_unlock(&pl->config_mutex); ++} ++EXPORT_SYMBOL_GPL(phylink_stop); ++ ++static void phylink_merge_link_mode(unsigned long *dst, const unsigned long *b) ++{ ++ __ETHTOOL_DECLARE_LINK_MODE_MASK(mask); ++ ++ linkmode_zero(mask); ++ phylink_set_port_bits(mask); ++ ++ linkmode_and(dst, dst, mask); ++ linkmode_or(dst, dst, b); ++} ++ ++static void phylink_get_ksettings(const struct phylink_link_state *state, ++ struct ethtool_link_ksettings *kset) ++{ ++ phylink_merge_link_mode(kset->link_modes.advertising, state->advertising); ++ linkmode_copy(kset->link_modes.lp_advertising, state->lp_advertising); ++ kset->base.speed = state->speed; ++ kset->base.duplex = state->duplex; ++ kset->base.autoneg = state->an_enabled ? AUTONEG_ENABLE : ++ AUTONEG_DISABLE; ++} ++ ++static int __phylink_ethtool_ksettings_get(struct phylink *pl, ++ struct ethtool_link_ksettings *kset) ++{ ++ struct phylink_link_state link_state; ++ int ret; ++ ++ if (pl->phydev) { ++ ret = phy_ethtool_ksettings_get(pl->phydev, kset); ++ if (ret) ++ return ret; ++ } else { ++ kset->base.port = pl->link_port; ++ } ++ ++ linkmode_copy(kset->link_modes.supported, pl->supported); ++ ++ switch (pl->link_an_mode) { ++ case MLO_AN_FIXED: ++ /* We are using fixed settings. Report these as the ++ * current link settings - and note that these also ++ * represent the supported speeds/duplex/pause modes. ++ */ ++ phylink_get_fixed_state(pl, &link_state); ++ phylink_get_ksettings(&link_state, kset); ++ break; ++ ++ case MLO_AN_SGMII: ++ /* If there is a phy attached, then use the reported ++ * settings from the phy with no modification. ++ */ ++ if (pl->phydev) ++ break; ++ ++ case MLO_AN_8023Z: ++ phylink_get_mac_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 ++ * link settings. ++ */ ++ phylink_get_ksettings(&link_state, kset); ++ break; ++ } ++ ++ return 0; ++} ++ ++int phylink_ethtool_ksettings_get(struct phylink *pl, ++ struct ethtool_link_ksettings *kset) ++{ ++ int ret; ++ ++ mutex_lock(&pl->config_mutex); ++ ret = __phylink_ethtool_ksettings_get(pl, kset); ++ mutex_unlock(&pl->config_mutex); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(phylink_ethtool_ksettings_get); ++ ++static int __phylink_ethtool_ksettings_set(struct phylink *pl, ++ const struct ethtool_link_ksettings *kset) ++{ ++ struct ethtool_link_ksettings our_kset = *kset; ++ int ret; ++ ++ /* Mask out unsupported advertisments */ ++ linkmode_and(our_kset.link_modes.advertising, ++ kset->link_modes.advertising, pl->supported); ++ ++ if (pl->ops->validate_advert) ++ pl->ops->validate_advert(pl->netdev, pl->link_an_mode, ++ pl->supported, ++ our_kset.link_modes.advertising); ++ ++ /* FIXME: should we reject autoneg if phy/mac does not support it? */ ++ ++ if (kset->base.autoneg == AUTONEG_DISABLE) { ++ const struct phy_setting *s; ++ ++ /* Autonegotiation disabled, select a suitable speed and ++ * duplex. ++ */ ++ s = phy_lookup_setting(kset->base.speed, kset->base.duplex, ++ pl->supported, ++ __ETHTOOL_LINK_MODE_MASK_NBITS, false); ++ if (!s) ++ return -EINVAL; ++ ++ /* If we have a fixed link (as specified by firmware), refuse ++ * to change link parameters. ++ */ ++ if (pl->link_an_mode == MLO_AN_FIXED && ++ (s->speed != pl->link_config.speed || ++ s->duplex != pl->link_config.duplex)) ++ return -EINVAL; ++ ++ our_kset.base.speed = s->speed; ++ our_kset.base.duplex = s->duplex; ++ ++ __clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, ++ our_kset.link_modes.advertising); ++ } else { ++ /* If we have a fixed link, refuse to enable autonegotiation */ ++ if (pl->link_an_mode == MLO_AN_FIXED) ++ return -EINVAL; ++ ++ /* Autonegotiation enabled, validate advertisment */ ++ if (phylink_empty_linkmode(our_kset.link_modes.advertising)) ++ return -EINVAL; ++ ++ __set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, ++ our_kset.link_modes.advertising); ++ } ++ ++ /* If we have a PHY, configure the phy */ ++ if (pl->phydev) { ++ ret = phy_ethtool_ksettings_set(pl->phydev, &our_kset); ++ if (ret) ++ return ret; ++ } ++ ++ 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.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 (!test_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state)) { ++ phylink_mac_config(pl, &pl->link_config); ++ phylink_mac_an_restart(pl); ++ } ++ mutex_unlock(&pl->state_mutex); ++ ++ return ret; ++} ++ ++int phylink_ethtool_ksettings_set(struct phylink *pl, ++ const struct ethtool_link_ksettings *kset) ++{ ++ int ret; ++ ++ if (kset->base.autoneg != AUTONEG_DISABLE && ++ kset->base.autoneg != AUTONEG_ENABLE) ++ return -EINVAL; ++ ++ mutex_lock(&pl->config_mutex); ++ ret = __phylink_ethtool_ksettings_set(pl, kset); ++ mutex_unlock(&pl->config_mutex); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(phylink_ethtool_ksettings_set); ++ ++/* This emulates MII registers for a fixed-mode phy operating as per the ++ * passed in state. "aneg" defines if we report negotiation is possible. ++ * ++ * FIXME: should deal with negotiation state too. ++ */ ++static int phylink_mii_emul_read(struct net_device *ndev, unsigned int reg, ++ struct phylink_link_state *state, bool aneg) ++{ ++ struct fixed_phy_status fs; ++ 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; ++ ++ val = swphy_read_reg(reg, &fs); ++ if (reg == MII_BMSR) { ++ if (!state->an_complete) ++ val &= ~BMSR_ANEGCOMPLETE; ++ if (!aneg) ++ val &= ~BMSR_ANEGCAPABLE; ++ } ++ return val; ++} ++ ++static int phylink_mii_read(struct phylink *pl, unsigned int phy_id, ++ unsigned int reg) ++{ ++ struct phylink_link_state state; ++ int val = 0xffff; ++ ++ /* PHYs only exist for MLO_AN_PHY and MLO_AN_SGMII */ ++ if (pl->phydev) ++ return mdiobus_read(pl->phydev->mdio.bus, phy_id, reg); ++ ++ switch (pl->link_an_mode) { ++ case MLO_AN_FIXED: ++ if (phy_id == 0) { ++ phylink_get_fixed_state(pl, &state); ++ val = phylink_mii_emul_read(pl->netdev, reg, &state, ++ true); ++ } ++ break; ++ ++ case MLO_AN_PHY: ++ return -EOPNOTSUPP; ++ ++ case MLO_AN_SGMII: ++ /* No phy, fall through to 8023z method */ ++ case MLO_AN_8023Z: ++ if (phy_id == 0) { ++ val = phylink_get_mac_state(pl, &state); ++ if (val < 0) ++ return val; ++ ++ val = phylink_mii_emul_read(pl->netdev, reg, &state, ++ true); ++ } ++ break; ++ } ++ ++ return val & 0xffff; ++} ++ ++static int phylink_mii_write(struct phylink *pl, unsigned int phy_id, ++ unsigned int reg, unsigned int val) ++{ ++ /* PHYs only exist for MLO_AN_PHY and MLO_AN_SGMII */ ++ if (pl->phydev) { ++ mdiobus_write(pl->phydev->mdio.bus, phy_id, reg, val); ++ return 0; ++ } ++ ++ switch (pl->link_an_mode) { ++ case MLO_AN_FIXED: ++ break; ++ ++ case MLO_AN_PHY: ++ return -EOPNOTSUPP; ++ ++ case MLO_AN_SGMII: ++ /* No phy, fall through to 8023z method */ ++ case MLO_AN_8023Z: ++ break; ++ } ++ ++ return 0; ++} ++ ++int phylink_mii_ioctl(struct phylink *pl, struct ifreq *ifr, int cmd) ++{ ++ struct mii_ioctl_data *mii_data = if_mii(ifr); ++ int val, ret; ++ ++ mutex_lock(&pl->config_mutex); ++ ++ switch (cmd) { ++ case SIOCGMIIPHY: ++ mii_data->phy_id = pl->phydev ? pl->phydev->mdio.addr : 0; ++ /* fallthrough */ ++ ++ case SIOCGMIIREG: ++ val = phylink_mii_read(pl, mii_data->phy_id, mii_data->reg_num); ++ if (val < 0) { ++ ret = val; ++ } else { ++ mii_data->val_out = val; ++ ret = 0; ++ } ++ break; ++ ++ case SIOCSMIIREG: ++ ret = phylink_mii_write(pl, mii_data->phy_id, mii_data->reg_num, ++ mii_data->val_in); ++ break; ++ ++ default: ++ ret = -EOPNOTSUPP; ++ if (pl->phydev) ++ ret = phy_mii_ioctl(pl->phydev, ifr, cmd); ++ break; ++ } ++ ++ mutex_unlock(&pl->config_mutex); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(phylink_mii_ioctl); ++ ++MODULE_LICENSE("GPL"); +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -153,6 +153,7 @@ static inline const char *phy_modes(phy_ + #define MII_ADDR_C45 (1<<30) + + struct device; ++struct phylink; + struct sk_buff; + + /* +@@ -422,6 +423,7 @@ struct phy_device { + + struct mutex lock; + ++ struct phylink *phylink; + struct net_device *attached_dev; + + u8 mdix; +--- /dev/null ++++ b/include/linux/phylink.h +@@ -0,0 +1,100 @@ ++#ifndef NETDEV_PCS_H ++#define NETDEV_PCS_H ++ ++#include <linux/phy.h> ++#include <linux/spinlock.h> ++#include <linux/workqueue.h> ++ ++struct device_node; ++struct ethtool_cmd; ++struct net_device; ++ ++enum { ++ MLO_PAUSE_NONE, ++ MLO_PAUSE_ASYM = BIT(0), ++ MLO_PAUSE_SYM = BIT(1), ++ ++ MLO_AN_PHY = 0, ++ MLO_AN_FIXED, ++ MLO_AN_SGMII, ++ MLO_AN_8023Z, ++}; ++ ++struct phylink_link_state { ++ __ETHTOOL_DECLARE_LINK_MODE_MASK(advertising); ++ __ETHTOOL_DECLARE_LINK_MODE_MASK(lp_advertising); ++ int speed; ++ int duplex; ++ int pause; ++ unsigned int link:1; ++ unsigned int an_enabled:1; ++ unsigned int an_complete:1; ++}; ++ ++struct phylink_mac_ops { ++ /** ++ * @validate_support: ++ * ++ * Validate and update the support mask provided by a PHY or ++ * module. Unsupported link modes should be cleared by the ++ * MAC. ++ * ++ * Note: the PHY may be able to transform from one connection ++ * technology to another, so, eg, don't clear 1000base-X just ++ * because the MAC is unable to support it. This is more about ++ * clearing unsupported speeds and duplex settings. ++ */ ++ void (*validate_support)(struct net_device *, unsigned int mode, ++ unsigned long *support); ++ ++ /** ++ * @validate_advert: ++ * ++ * Validate and update the advertisment mask, clearing bits that ++ * can not be advertised in the chosen mode or with each other. ++ */ ++ void (*validate_advert)(struct net_device *, unsigned int mode, ++ const unsigned long *support, ++ unsigned long *advert); ++ ++ /* Read the current link state from the hardware */ ++ int (*mac_link_state)(struct net_device *, struct phylink_link_state *); ++ ++ /* Configure the MAC */ ++ void (*mac_config)(struct net_device *, unsigned int mode, ++ const struct phylink_link_state *); ++ void (*mac_an_restart)(struct net_device *, unsigned int mode); ++ ++ void (*mac_link_down)(struct net_device *, unsigned int mode); ++ void (*mac_link_up)(struct net_device *, unsigned int mode); ++}; ++ ++struct phylink *phylink_create(struct net_device *, struct device_node *, ++ phy_interface_t iface, const struct phylink_mac_ops *ops); ++void phylink_destroy(struct phylink *); ++ ++int phylink_connect_phy(struct phylink *, struct phy_device *); ++int phylink_of_phy_connect(struct phylink *, struct device_node *); ++void phylink_disconnect_phy(struct phylink *); ++ ++void phylink_mac_change(struct phylink *, bool up); ++ ++void phylink_start(struct phylink *); ++void phylink_stop(struct phylink *); ++ ++int phylink_ethtool_ksettings_get(struct phylink *, ++ struct ethtool_link_ksettings *); ++int phylink_ethtool_ksettings_set(struct phylink *, ++ const struct ethtool_link_ksettings *); ++int phylink_mii_ioctl(struct phylink *, struct ifreq *, int); ++ ++#define phylink_zero(bm) \ ++ bitmap_zero(bm, __ETHTOOL_LINK_MODE_MASK_NBITS) ++#define __phylink_do_bit(op, bm, mode) \ ++ op(ETHTOOL_LINK_MODE_ ## mode ## _BIT, bm) ++ ++#define phylink_set(bm, mode) __phylink_do_bit(__set_bit, bm, mode) ++#define phylink_clear(bm, mode) __phylink_do_bit(__clear_bit, bm, mode) ++#define phylink_test(bm, mode) __phylink_do_bit(test_bit, bm, mode) ++ ++#endif diff --git a/target/linux/mvebu/patches-4.9/416-phylink-add-hooks-for-SFP-support.patch b/target/linux/mvebu/patches-4.9/416-phylink-add-hooks-for-SFP-support.patch new file mode 100644 index 0000000000..da44f75dbe --- /dev/null +++ b/target/linux/mvebu/patches-4.9/416-phylink-add-hooks-for-SFP-support.patch @@ -0,0 +1,177 @@ +From: Russell King <rmk+kernel@arm.linux.org.uk> +Date: Thu, 24 Sep 2015 11:01:13 +0100 +Subject: [PATCH] phylink: add hooks for SFP support + +Add support to phylink for SFP, which needs to control and configure +the ethernet MAC link state. Specifically, SFP needs to: + +1. set the negotiation mode between SGMII and 1000base-X +2. attach and detach the module PHY +3. prevent the link coming up when errors are reported + +In the absence of a PHY, we also need to set the ethtool port type +according to the module plugged in. + +Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> +Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> + +- rework phylink_set_link_*(), combining into a single function. +--- + +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -11,6 +11,7 @@ + #include <linux/ethtool.h> + #include <linux/export.h> + #include <linux/gpio/consumer.h> ++#include <linux/list.h> + #include <linux/netdevice.h> + #include <linux/of.h> + #include <linux/of_mdio.h> +@@ -29,11 +30,16 @@ + (ADVERTISED_TP | ADVERTISED_MII | ADVERTISED_FIBRE | \ + ADVERTISED_BNC | ADVERTISED_AUI | ADVERTISED_Backplane) + ++static LIST_HEAD(phylinks); ++static DEFINE_MUTEX(phylink_mutex); ++ + enum { + PHYLINK_DISABLE_STOPPED, ++ PHYLINK_DISABLE_LINK, + }; + + struct phylink { ++ struct list_head node; + struct net_device *netdev; + const struct phylink_mac_ops *ops; + struct mutex config_mutex; +@@ -375,12 +381,20 @@ struct phylink *phylink_create(struct ne + phylink_init_advert(pl, pl->link_an_mode, pl->supported, + pl->link_config.advertising); + ++ mutex_lock(&phylink_mutex); ++ list_add_tail(&pl->node, &phylinks); ++ mutex_unlock(&phylink_mutex); ++ + return pl; + } + EXPORT_SYMBOL_GPL(phylink_create); + + void phylink_destroy(struct phylink *pl) + { ++ mutex_lock(&phylink_mutex); ++ list_del(&pl->node); ++ mutex_unlock(&phylink_mutex); ++ + cancel_work_sync(&pl->resolve); + kfree(pl); + } +@@ -900,4 +914,93 @@ int phylink_mii_ioctl(struct phylink *pl + } + EXPORT_SYMBOL_GPL(phylink_mii_ioctl); + ++ ++ ++void phylink_disable(struct phylink *pl) ++{ ++ set_bit(PHYLINK_DISABLE_LINK, &pl->phylink_disable_state); ++ flush_work(&pl->resolve); ++ ++ netif_carrier_off(pl->netdev); ++} ++EXPORT_SYMBOL_GPL(phylink_disable); ++ ++void phylink_enable(struct phylink *pl) ++{ ++ clear_bit(PHYLINK_DISABLE_LINK, &pl->phylink_disable_state); ++ phylink_run_resolve(pl); ++} ++EXPORT_SYMBOL_GPL(phylink_enable); ++ ++int phylink_set_link(struct phylink *pl, unsigned int mode, u8 port, ++ const unsigned long *support) ++{ ++ __ETHTOOL_DECLARE_LINK_MODE_MASK(mask); ++ int ret = 0; ++ ++ netdev_dbg(pl->netdev, "requesting link mode %s with support %*pb\n", ++ phylink_an_mode_str(mode), ++ __ETHTOOL_LINK_MODE_MASK_NBITS, support); ++ ++ if (mode == MLO_AN_FIXED) ++ return -EINVAL; ++ ++ linkmode_copy(mask, support); ++ ++ /* Ignore errors if we're expecting a PHY to attach later */ ++ ret = phylink_validate_support(pl, mode, mask); ++ if (ret && mode != MLO_AN_PHY) ++ return ret; ++ ++ mutex_lock(&pl->config_mutex); ++ if (mode == MLO_AN_8023Z && pl->phydev) { ++ ret = -EINVAL; ++ } else { ++ bool changed = !bitmap_equal(pl->supported, mask, ++ __ETHTOOL_LINK_MODE_MASK_NBITS); ++ if (changed) { ++ linkmode_copy(pl->supported, mask); ++ ++ phylink_init_advert(pl, mode, mask, ++ pl->link_config.advertising); ++ } ++ ++ if (pl->link_an_mode != mode) { ++ pl->link_an_mode = mode; ++ ++ changed = true; ++ ++ netdev_info(pl->netdev, "switched to %s link mode\n", ++ phylink_an_mode_str(mode)); ++ } ++ ++ pl->link_port = port; ++ ++ if (changed && !test_bit(PHYLINK_DISABLE_STOPPED, ++ &pl->phylink_disable_state)) ++ phylink_mac_config(pl, &pl->link_config); ++ } ++ mutex_unlock(&pl->config_mutex); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(phylink_set_link); ++ ++struct phylink *phylink_lookup_by_netdev(struct net_device *ndev) ++{ ++ struct phylink *pl, *found = NULL; ++ ++ mutex_lock(&phylink_mutex); ++ list_for_each_entry(pl, &phylinks, node) ++ if (pl->netdev == ndev) { ++ found = pl; ++ break; ++ } ++ ++ mutex_unlock(&phylink_mutex); ++ ++ return found; ++} ++EXPORT_SYMBOL_GPL(phylink_lookup_by_netdev); ++ + MODULE_LICENSE("GPL"); +--- a/include/linux/phylink.h ++++ b/include/linux/phylink.h +@@ -88,6 +88,12 @@ int phylink_ethtool_ksettings_set(struct + const struct ethtool_link_ksettings *); + int phylink_mii_ioctl(struct phylink *, struct ifreq *, int); + ++int phylink_set_link(struct phylink *pl, unsigned int mode, u8 port, ++ const unsigned long *support); ++void phylink_disable(struct phylink *pl); ++void phylink_enable(struct phylink *pl); ++struct phylink *phylink_lookup_by_netdev(struct net_device *ndev); ++ + #define phylink_zero(bm) \ + bitmap_zero(bm, __ETHTOOL_LINK_MODE_MASK_NBITS) + #define __phylink_do_bit(op, bm, mode) \ diff --git a/target/linux/mvebu/patches-4.9/417-sfp-add-phylink-based-SFP-module-support.patch b/target/linux/mvebu/patches-4.9/417-sfp-add-phylink-based-SFP-module-support.patch new file mode 100644 index 0000000000..4abefe6236 --- /dev/null +++ b/target/linux/mvebu/patches-4.9/417-sfp-add-phylink-based-SFP-module-support.patch @@ -0,0 +1,1477 @@ +From: Russell King <rmk+kernel@arm.linux.org.uk> +Date: Sat, 12 Sep 2015 18:43:39 +0100 +Subject: [PATCH] sfp: add phylink based SFP module support + +Add support for SFP hotpluggable modules via phylink. This supports +both copper and optical SFP modules, which require different Serdes +modes in order to properly negotiate the link. + +Optical SFP modules typically require the Serdes link to be talking +1000base-X mode - this is the gigabit ethernet mode defined by the +802.3 standard. + +Copper SFP modules typically integrate a PHY in the module to convert +from Serdes to copper, and the PHY will be configured by the vendor +to either present a 1000base-X Serdes link (for fixed 1000base-T) or +a SGMII Serdes link. However, this is vendor defined, so we instead +detect the PHY, switch the link to SGMII mode, and use traditional +PHY based negotiation. + +Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> + +- set port and port capability depending on connector type +- move autoneg mode setting to probe function +- set "supported" speed capabilities depending on reported ethernet + capabilities +- checks for short read +- dump eeprom base ID when checksum fails +--- + create mode 100644 drivers/net/phy/sfp.c + create mode 100644 include/linux/sfp.h + +--- a/drivers/net/phy/Kconfig ++++ b/drivers/net/phy/Kconfig +@@ -256,6 +256,11 @@ endif # RTL8366_SMI + + comment "MII PHY device drivers" + ++config SFP ++ tristate "SFP cage support" ++ depends on I2C && PHYLINK ++ select MDIO_I2C ++ + config AMD_PHY + tristate "AMD PHYs" + ---help--- +--- a/drivers/net/phy/Makefile ++++ b/drivers/net/phy/Makefile +@@ -41,6 +41,8 @@ obj-$(CONFIG_MDIO_SUN4I) += mdio-sun4i.o + obj-$(CONFIG_MDIO_THUNDER) += mdio-thunder.o + obj-$(CONFIG_MDIO_XGENE) += mdio-xgene.o + ++obj-$(CONFIG_SFP) += sfp.o ++ + obj-$(CONFIG_AMD_PHY) += amd.o + obj-$(CONFIG_AQUANTIA_PHY) += aquantia.o + obj-$(CONFIG_AT803X_PHY) += at803x.o +--- /dev/null ++++ b/drivers/net/phy/sfp.c +@@ -0,0 +1,1071 @@ ++#include <linux/delay.h> ++#include <linux/gpio.h> ++#include <linux/i2c.h> ++#include <linux/interrupt.h> ++#include <linux/jiffies.h> ++#include <linux/module.h> ++#include <linux/mutex.h> ++#include <linux/netdevice.h> ++#include <linux/of.h> ++#include <linux/of_net.h> ++#include <linux/phylink.h> ++#include <linux/platform_device.h> ++#include <linux/sfp.h> ++#include <linux/slab.h> ++#include <linux/workqueue.h> ++ ++#include "mdio-i2c.h" ++#include "swphy.h" ++ ++enum { ++ GPIO_MODDEF0, ++ GPIO_LOS, ++ GPIO_TX_FAULT, ++ GPIO_TX_DISABLE, ++ GPIO_RATE_SELECT, ++ GPIO_MAX, ++ ++ SFP_F_PRESENT = BIT(GPIO_MODDEF0), ++ SFP_F_LOS = BIT(GPIO_LOS), ++ SFP_F_TX_FAULT = BIT(GPIO_TX_FAULT), ++ SFP_F_TX_DISABLE = BIT(GPIO_TX_DISABLE), ++ SFP_F_RATE_SELECT = BIT(GPIO_RATE_SELECT), ++ ++ SFP_E_INSERT = 0, ++ SFP_E_REMOVE, ++ SFP_E_DEV_DOWN, ++ SFP_E_DEV_UP, ++ SFP_E_TX_FAULT, ++ SFP_E_TX_CLEAR, ++ SFP_E_LOS_HIGH, ++ SFP_E_LOS_LOW, ++ SFP_E_TIMEOUT, ++ ++ SFP_MOD_EMPTY = 0, ++ SFP_MOD_PROBE, ++ SFP_MOD_PRESENT, ++ SFP_MOD_ERROR, ++ ++ SFP_DEV_DOWN = 0, ++ SFP_DEV_UP, ++ ++ SFP_S_DOWN = 0, ++ SFP_S_INIT, ++ SFP_S_WAIT_LOS, ++ SFP_S_LINK_UP, ++ SFP_S_TX_FAULT, ++ SFP_S_REINIT, ++ SFP_S_TX_DISABLE, ++}; ++ ++static const char *gpio_of_names[] = { ++ "moddef0", ++ "los", ++ "tx-fault", ++ "tx-disable", ++ "rate-select", ++}; ++ ++static const enum gpiod_flags gpio_flags[] = { ++ GPIOD_IN, ++ GPIOD_IN, ++ GPIOD_IN, ++ GPIOD_ASIS, ++ GPIOD_ASIS, ++}; ++ ++#define T_INIT_JIFFIES msecs_to_jiffies(300) ++#define T_RESET_US 10 ++#define T_FAULT_RECOVER msecs_to_jiffies(1000) ++ ++/* SFP module presence detection is poor: the three MOD DEF signals are ++ * the same length on the PCB, which means it's possible for MOD DEF 0 to ++ * connect before the I2C bus on MOD DEF 1/2. ++ * ++ * The SFP MSA specifies 300ms as t_init (the time taken for TX_FAULT to ++ * be deasserted) but makes no mention of the earliest time before we can ++ * access the I2C EEPROM. However, Avago modules require 300ms. ++ */ ++#define T_PROBE_INIT msecs_to_jiffies(300) ++#define T_PROBE_RETRY msecs_to_jiffies(100) ++ ++/* ++ * SFP modules appear to always have their PHY configured for bus address ++ * 0x56 (which with mdio-i2c, translates to a PHY address of 22). ++ */ ++#define SFP_PHY_ADDR 22 ++ ++/* ++ * Give this long for the PHY to reset. ++ */ ++#define T_PHY_RESET_MS 50 ++ ++static DEFINE_MUTEX(sfp_mutex); ++ ++struct sfp { ++ struct device *dev; ++ struct i2c_adapter *i2c; ++ struct mii_bus *i2c_mii; ++ struct net_device *ndev; ++ struct phylink *phylink; ++ struct phy_device *mod_phy; ++ ++ unsigned int (*get_state)(struct sfp *); ++ void (*set_state)(struct sfp *, unsigned int); ++ int (*read)(struct sfp *, bool, u8, void *, size_t); ++ ++ struct gpio_desc *gpio[GPIO_MAX]; ++ ++ unsigned int state; ++ struct delayed_work poll; ++ struct delayed_work timeout; ++ struct mutex sm_mutex; ++ unsigned char sm_mod_state; ++ unsigned char sm_dev_state; ++ unsigned short sm_state; ++ unsigned int sm_retries; ++ ++ struct sfp_eeprom_id id; ++ ++ struct notifier_block netdev_nb; ++}; ++ ++static unsigned long poll_jiffies; ++ ++static unsigned int sfp_gpio_get_state(struct sfp *sfp) ++{ ++ unsigned int i, state, v; ++ ++ for (i = state = 0; i < GPIO_MAX; i++) { ++ if (gpio_flags[i] != GPIOD_IN || !sfp->gpio[i]) ++ continue; ++ ++ v = gpiod_get_value_cansleep(sfp->gpio[i]); ++ if (v) ++ state |= BIT(i); ++ } ++ ++ return state; ++} ++ ++static void sfp_gpio_set_state(struct sfp *sfp, unsigned int state) ++{ ++ if (state & SFP_F_PRESENT) { ++ /* If the module is present, drive the signals */ ++ if (sfp->gpio[GPIO_TX_DISABLE]) ++ gpiod_direction_output(sfp->gpio[GPIO_TX_DISABLE], ++ state & SFP_F_TX_DISABLE); ++ if (state & SFP_F_RATE_SELECT) ++ gpiod_direction_output(sfp->gpio[GPIO_RATE_SELECT], ++ state & SFP_F_RATE_SELECT); ++ } else { ++ /* Otherwise, let them float to the pull-ups */ ++ if (sfp->gpio[GPIO_TX_DISABLE]) ++ gpiod_direction_input(sfp->gpio[GPIO_TX_DISABLE]); ++ if (state & SFP_F_RATE_SELECT) ++ gpiod_direction_input(sfp->gpio[GPIO_RATE_SELECT]); ++ } ++} ++ ++static int sfp__i2c_read(struct i2c_adapter *i2c, u8 bus_addr, u8 dev_addr, ++ void *buf, size_t len) ++{ ++ struct i2c_msg msgs[2]; ++ int ret; ++ ++ msgs[0].addr = bus_addr; ++ msgs[0].flags = 0; ++ msgs[0].len = 1; ++ msgs[0].buf = &dev_addr; ++ msgs[1].addr = bus_addr; ++ msgs[1].flags = I2C_M_RD; ++ msgs[1].len = len; ++ msgs[1].buf = buf; ++ ++ ret = i2c_transfer(i2c, msgs, ARRAY_SIZE(msgs)); ++ if (ret < 0) ++ return ret; ++ ++ return ret == ARRAY_SIZE(msgs) ? len : 0; ++} ++ ++static int sfp_i2c_read(struct sfp *sfp, bool a2, u8 addr, void *buf, ++ size_t len) ++{ ++ return sfp__i2c_read(sfp->i2c, a2 ? 0x51 : 0x50, addr, buf, len); ++} ++ ++static int sfp_i2c_configure(struct sfp *sfp, struct i2c_adapter *i2c) ++{ ++ struct mii_bus *i2c_mii; ++ int ret; ++ ++ if (!i2c_check_functionality(i2c, I2C_FUNC_I2C)) ++ return -EINVAL; ++ ++ sfp->i2c = i2c; ++ sfp->read = sfp_i2c_read; ++ ++ i2c_mii = mdio_i2c_alloc(sfp->dev, i2c); ++ if (IS_ERR(i2c_mii)) ++ return PTR_ERR(i2c_mii); ++ ++ i2c_mii->name = "SFP I2C Bus"; ++ i2c_mii->phy_mask = ~0; ++ ++ ret = mdiobus_register(i2c_mii); ++ if (ret < 0) { ++ mdiobus_free(i2c_mii); ++ return ret; ++ } ++ ++ sfp->i2c_mii = i2c_mii; ++ ++ return 0; ++} ++ ++ ++/* Interface */ ++static unsigned int sfp_get_state(struct sfp *sfp) ++{ ++ return sfp->get_state(sfp); ++} ++ ++static void sfp_set_state(struct sfp *sfp, unsigned int state) ++{ ++ sfp->set_state(sfp, state); ++} ++ ++static int sfp_read(struct sfp *sfp, bool a2, u8 addr, void *buf, size_t len) ++{ ++ return sfp->read(sfp, a2, addr, buf, len); ++} ++ ++static unsigned int sfp_check(void *buf, size_t len) ++{ ++ u8 *p, check; ++ ++ for (p = buf, check = 0; len; p++, len--) ++ check += *p; ++ ++ return check; ++} ++ ++/* Helpers */ ++static void sfp_module_tx_disable(struct sfp *sfp) ++{ ++ dev_dbg(sfp->dev, "tx disable %u -> %u\n", ++ sfp->state & SFP_F_TX_DISABLE ? 1 : 0, 1); ++ sfp->state |= SFP_F_TX_DISABLE; ++ sfp_set_state(sfp, sfp->state); ++} ++ ++static void sfp_module_tx_enable(struct sfp *sfp) ++{ ++ dev_dbg(sfp->dev, "tx disable %u -> %u\n", ++ sfp->state & SFP_F_TX_DISABLE ? 1 : 0, 0); ++ sfp->state &= ~SFP_F_TX_DISABLE; ++ sfp_set_state(sfp, sfp->state); ++} ++ ++static void sfp_module_tx_fault_reset(struct sfp *sfp) ++{ ++ unsigned int state = sfp->state; ++ ++ if (state & SFP_F_TX_DISABLE) ++ return; ++ ++ sfp_set_state(sfp, state | SFP_F_TX_DISABLE); ++ ++ udelay(T_RESET_US); ++ ++ sfp_set_state(sfp, state); ++} ++ ++/* SFP state machine */ ++static void sfp_sm_set_timer(struct sfp *sfp, unsigned int timeout) ++{ ++ if (timeout) ++ mod_delayed_work(system_power_efficient_wq, &sfp->timeout, ++ timeout); ++ else ++ cancel_delayed_work(&sfp->timeout); ++} ++ ++static void sfp_sm_next(struct sfp *sfp, unsigned int state, ++ unsigned int timeout) ++{ ++ sfp->sm_state = state; ++ sfp_sm_set_timer(sfp, timeout); ++} ++ ++static void sfp_sm_ins_next(struct sfp *sfp, unsigned int state, unsigned int timeout) ++{ ++ sfp->sm_mod_state = state; ++ sfp_sm_set_timer(sfp, timeout); ++} ++ ++static void sfp_sm_phy_detach(struct sfp *sfp) ++{ ++ phy_stop(sfp->mod_phy); ++ if (sfp->phylink) ++ phylink_disconnect_phy(sfp->phylink); ++ phy_device_remove(sfp->mod_phy); ++ phy_device_free(sfp->mod_phy); ++ sfp->mod_phy = NULL; ++} ++ ++static void sfp_sm_probe_phy(struct sfp *sfp) ++{ ++ struct phy_device *phy; ++ int err; ++ ++ msleep(T_PHY_RESET_MS); ++ ++ phy = mdiobus_scan(sfp->i2c_mii, SFP_PHY_ADDR); ++ if (IS_ERR(phy)) { ++ dev_err(sfp->dev, "mdiobus scan returned %ld\n", PTR_ERR(phy)); ++ return; ++ } ++ if (!phy) { ++ dev_info(sfp->dev, "no PHY detected\n"); ++ return; ++ } ++ ++ if (sfp->phylink) { ++ err = phylink_connect_phy(sfp->phylink, phy); ++ if (err) { ++ phy_device_remove(phy); ++ phy_device_free(phy); ++ dev_err(sfp->dev, "phylink_connect_phy failed: %d\n", ++ err); ++ return; ++ } ++ } ++ ++ sfp->mod_phy = phy; ++ phy_start(phy); ++} ++ ++static void sfp_sm_link_up(struct sfp *sfp) ++{ ++ if (sfp->phylink) ++ phylink_enable(sfp->phylink); ++ ++ sfp_sm_next(sfp, SFP_S_LINK_UP, 0); ++} ++ ++static void sfp_sm_link_down(struct sfp *sfp) ++{ ++ if (sfp->phylink) ++ phylink_disable(sfp->phylink); ++} ++ ++static void sfp_sm_link_check_los(struct sfp *sfp) ++{ ++ unsigned int los = sfp->state & SFP_F_LOS; ++ ++ /* FIXME: what if neither SFP_OPTIONS_LOS_INVERTED nor ++ * SFP_OPTIONS_LOS_NORMAL are set? For now, we assume ++ * the same as SFP_OPTIONS_LOS_NORMAL set. ++ */ ++ if (sfp->id.ext.options & SFP_OPTIONS_LOS_INVERTED) ++ los ^= SFP_F_LOS; ++ ++ if (los) ++ sfp_sm_next(sfp, SFP_S_WAIT_LOS, 0); ++ else ++ sfp_sm_link_up(sfp); ++} ++ ++static void sfp_sm_fault(struct sfp *sfp, bool warn) ++{ ++ if (sfp->sm_retries && !--sfp->sm_retries) { ++ dev_err(sfp->dev, "module persistently indicates fault, disabling\n"); ++ sfp_sm_next(sfp, SFP_S_TX_DISABLE, 0); ++ } else { ++ if (warn) ++ dev_err(sfp->dev, "module transmit fault indicated\n"); ++ ++ sfp_sm_next(sfp, SFP_S_TX_FAULT, T_FAULT_RECOVER); ++ } ++} ++ ++static void sfp_sm_mod_init(struct sfp *sfp) ++{ ++ sfp_module_tx_enable(sfp); ++ ++ /* Wait t_init before indicating that the link is up, provided the ++ * current state indicates no TX_FAULT. If TX_FAULT clears before ++ * this time, that's fine too. ++ */ ++ sfp_sm_next(sfp, SFP_S_INIT, T_INIT_JIFFIES); ++ sfp->sm_retries = 5; ++ ++ if (sfp->phylink) { ++ /* Setting the serdes link mode is guesswork: there's no ++ * field in the EEPROM which indicates what mode should ++ * be used. ++ * ++ * If it's a gigabit-only fiber module, it probably does ++ * not have a PHY, so switch to 802.3z negotiation mode. ++ * Otherwise, switch to SGMII mode (which is required to ++ * support non-gigabit speeds) and probe for a PHY. ++ */ ++ if (sfp->id.base.e1000_base_t || ++ sfp->id.base.e100_base_lx || ++ sfp->id.base.e100_base_fx) ++ sfp_sm_probe_phy(sfp); ++ } ++} ++ ++static int sfp_sm_mod_probe(struct sfp *sfp) ++{ ++ /* SFP module inserted - read I2C data */ ++ struct sfp_eeprom_id id; ++ char vendor[17]; ++ char part[17]; ++ char sn[17]; ++ char date[9]; ++ char rev[5]; ++ u8 check; ++ int err; ++ ++ err = sfp_read(sfp, false, 0, &id, sizeof(id)); ++ if (err < 0) { ++ dev_err(sfp->dev, "failed to read EEPROM: %d\n", err); ++ return -EAGAIN; ++ } ++ ++ if (err != sizeof(id)) { ++ dev_err(sfp->dev, "EEPROM short read: %d\n", err); ++ return -EAGAIN; ++ } ++ ++ /* Validate the checksum over the base structure */ ++ check = sfp_check(&id.base, sizeof(id.base) - 1); ++ if (check != id.base.cc_base) { ++ dev_err(sfp->dev, ++ "EEPROM base structure checksum failure: 0x%02x\n", ++ check); ++ print_hex_dump(KERN_ERR, "sfp EE: ", DUMP_PREFIX_OFFSET, ++ 16, 1, &id, sizeof(id.base) - 1, true); ++ return -EINVAL; ++ } ++ ++ check = sfp_check(&id.ext, sizeof(id.ext) - 1); ++ if (check != id.ext.cc_ext) { ++ dev_err(sfp->dev, ++ "EEPROM extended structure checksum failure: 0x%02x\n", ++ check); ++ memset(&id.ext, 0, sizeof(id.ext)); ++ } ++ ++ sfp->id = id; ++ ++ memcpy(vendor, sfp->id.base.vendor_name, 16); ++ vendor[16] = '\0'; ++ memcpy(part, sfp->id.base.vendor_pn, 16); ++ part[16] = '\0'; ++ memcpy(rev, sfp->id.base.vendor_rev, 4); ++ rev[4] = '\0'; ++ memcpy(sn, sfp->id.ext.vendor_sn, 16); ++ sn[16] = '\0'; ++ memcpy(date, sfp->id.ext.datecode, 8); ++ date[8] = '\0'; ++ ++ dev_info(sfp->dev, "module %s %s rev %s sn %s dc %s\n", vendor, part, rev, sn, date); ++ ++ /* We only support SFP modules, not the legacy GBIC modules. */ ++ if (sfp->id.base.phys_id != SFP_PHYS_ID_SFP || ++ sfp->id.base.phys_ext_id != SFP_PHYS_EXT_ID_SFP) { ++ dev_err(sfp->dev, "module is not SFP - phys id 0x%02x 0x%02x\n", ++ sfp->id.base.phys_id, sfp->id.base.phys_ext_id); ++ return -EINVAL; ++ } ++ ++ /* ++ * What isn't clear from the SFP documentation is whether this ++ * specifies the encoding expected on the TD/RD lines, or whether ++ * the TD/RD lines are always 8b10b encoded, but the transceiver ++ * converts. Eg, think of a copper SFP supporting 1G/100M/10M ++ * ethernet: this requires 8b10b encoding for 1G, 4b5b for 100M, ++ * and manchester for 10M. ++ */ ++ /* 1Gbit ethernet requires 8b10b encoding */ ++ if (sfp->id.base.encoding != SFP_ENCODING_8B10B) { ++ dev_err(sfp->dev, "module does not support 8B10B encoding\n"); ++ return -EINVAL; ++ } ++ ++ if (sfp->phylink) { ++ __ETHTOOL_DECLARE_LINK_MODE_MASK(support) = { 0, }; ++ int mode; ++ u8 port; ++ ++ phylink_set(support, Autoneg); ++ phylink_set(support, Pause); ++ phylink_set(support, Asym_Pause); ++ ++ /* Set ethtool support from the compliance fields. */ ++ if (sfp->id.base.e10g_base_sr) ++ phylink_set(support, 10000baseSR_Full); ++ if (sfp->id.base.e10g_base_lr) ++ phylink_set(support, 10000baseLR_Full); ++ if (sfp->id.base.e10g_base_lrm) ++ phylink_set(support, 10000baseLRM_Full); ++ if (sfp->id.base.e10g_base_er) ++ phylink_set(support, 10000baseER_Full); ++ if (sfp->id.base.e1000_base_sx || ++ sfp->id.base.e1000_base_lx || ++ sfp->id.base.e1000_base_cx) ++ phylink_set(support, 1000baseX_Full); ++ if (sfp->id.base.e1000_base_t) { ++ phylink_set(support, 1000baseT_Half); ++ phylink_set(support, 1000baseT_Full); ++ } ++ ++ /* port is the physical connector, set this from the ++ * connector field. ++ */ ++ switch (sfp->id.base.connector) { ++ case SFP_CONNECTOR_SC: ++ case SFP_CONNECTOR_FIBERJACK: ++ case SFP_CONNECTOR_LC: ++ case SFP_CONNECTOR_MT_RJ: ++ case SFP_CONNECTOR_MU: ++ case SFP_CONNECTOR_OPTICAL_PIGTAIL: ++ phylink_set(support, FIBRE); ++ port = PORT_FIBRE; ++ break; ++ ++ case SFP_CONNECTOR_RJ45: ++ phylink_set(support, TP); ++ port = PORT_TP; ++ break; ++ ++ case SFP_CONNECTOR_UNSPEC: ++ if (sfp->id.base.e1000_base_t) { ++ phylink_set(support, TP); ++ port = PORT_TP; ++ break; ++ } ++ /* fallthrough */ ++ case SFP_CONNECTOR_SG: /* guess */ ++ case SFP_CONNECTOR_MPO_1X12: ++ case SFP_CONNECTOR_MPO_2X16: ++ case SFP_CONNECTOR_HSSDC_II: ++ case SFP_CONNECTOR_COPPER_PIGTAIL: ++ case SFP_CONNECTOR_NOSEPARATE: ++ case SFP_CONNECTOR_MXC_2X16: ++ default: ++ /* a guess at the supported link modes */ ++ dev_warn(sfp->dev, "Guessing link modes, please report...\n"); ++ phylink_set(support, 1000baseT_Half); ++ phylink_set(support, 1000baseT_Full); ++ port = PORT_OTHER; ++ break; ++ } ++ ++ /* Setting the serdes link mode is guesswork: there's no ++ * field in the EEPROM which indicates what mode should ++ * be used. ++ * ++ * If it's a gigabit-only fiber module, it probably does ++ * not have a PHY, so switch to 802.3z negotiation mode. ++ * Otherwise, switch to SGMII mode (which is required to ++ * support non-gigabit speeds) and probe for a PHY. ++ */ ++ if (!sfp->id.base.e1000_base_t && ++ !sfp->id.base.e100_base_lx && ++ !sfp->id.base.e100_base_fx) { ++ mode = MLO_AN_8023Z; ++ } else { ++ mode = MLO_AN_SGMII; ++ } ++ ++ phylink_set_link(sfp->phylink, mode, port, support); ++ } ++ ++ return 0; ++} ++ ++static void sfp_sm_mod_remove(struct sfp *sfp) ++{ ++ if (sfp->mod_phy) ++ sfp_sm_phy_detach(sfp); ++ ++ sfp_module_tx_disable(sfp); ++ ++ memset(&sfp->id, 0, sizeof(sfp->id)); ++ ++ dev_info(sfp->dev, "module removed\n"); ++} ++ ++static void sfp_sm_event(struct sfp *sfp, unsigned int event) ++{ ++ mutex_lock(&sfp->sm_mutex); ++ ++ dev_dbg(sfp->dev, "SM: enter %u:%u:%u event %u\n", ++ sfp->sm_mod_state, sfp->sm_dev_state, sfp->sm_state, event); ++ ++ /* This state machine tracks the insert/remove state of ++ * the module, and handles probing the on-board EEPROM. ++ */ ++ switch (sfp->sm_mod_state) { ++ default: ++ if (event == SFP_E_INSERT) { ++ sfp_module_tx_disable(sfp); ++ sfp_sm_ins_next(sfp, SFP_MOD_PROBE, T_PROBE_INIT); ++ } ++ break; ++ ++ case SFP_MOD_PROBE: ++ if (event == SFP_E_REMOVE) { ++ sfp_sm_ins_next(sfp, SFP_MOD_EMPTY, 0); ++ } else if (event == SFP_E_TIMEOUT) { ++ int err = sfp_sm_mod_probe(sfp); ++ ++ if (err == 0) ++ sfp_sm_ins_next(sfp, SFP_MOD_PRESENT, 0); ++ else if (err == -EAGAIN) ++ sfp_sm_set_timer(sfp, T_PROBE_RETRY); ++ else ++ sfp_sm_ins_next(sfp, SFP_MOD_ERROR, 0); ++ } ++ break; ++ ++ case SFP_MOD_PRESENT: ++ case SFP_MOD_ERROR: ++ if (event == SFP_E_REMOVE) { ++ sfp_sm_mod_remove(sfp); ++ sfp_sm_ins_next(sfp, SFP_MOD_EMPTY, 0); ++ } ++ break; ++ } ++ ++ /* This state machine tracks the netdev up/down state */ ++ switch (sfp->sm_dev_state) { ++ default: ++ if (event == SFP_E_DEV_UP) ++ sfp->sm_dev_state = SFP_DEV_UP; ++ break; ++ ++ case SFP_DEV_UP: ++ if (event == SFP_E_DEV_DOWN) { ++ /* If the module has a PHY, avoid raising TX disable ++ * as this resets the PHY. Otherwise, raise it to ++ * turn the laser off. ++ */ ++ if (!sfp->mod_phy) ++ sfp_module_tx_disable(sfp); ++ sfp->sm_dev_state = SFP_DEV_DOWN; ++ } ++ break; ++ } ++ ++ /* Some events are global */ ++ if (sfp->sm_state != SFP_S_DOWN && ++ (sfp->sm_mod_state != SFP_MOD_PRESENT || ++ sfp->sm_dev_state != SFP_DEV_UP)) { ++ if (sfp->sm_state == SFP_S_LINK_UP && ++ sfp->sm_dev_state == SFP_DEV_UP) ++ sfp_sm_link_down(sfp); ++ if (sfp->mod_phy) ++ sfp_sm_phy_detach(sfp); ++ sfp_sm_next(sfp, SFP_S_DOWN, 0); ++ mutex_unlock(&sfp->sm_mutex); ++ return; ++ } ++ ++ /* The main state machine */ ++ switch (sfp->sm_state) { ++ case SFP_S_DOWN: ++ if (sfp->sm_mod_state == SFP_MOD_PRESENT && ++ sfp->sm_dev_state == SFP_DEV_UP) ++ sfp_sm_mod_init(sfp); ++ break; ++ ++ case SFP_S_INIT: ++ if (event == SFP_E_TIMEOUT && sfp->state & SFP_F_TX_FAULT) ++ sfp_sm_fault(sfp, true); ++ else if (event == SFP_E_TIMEOUT || event == SFP_E_TX_CLEAR) ++ sfp_sm_link_check_los(sfp); ++ break; ++ ++ case SFP_S_WAIT_LOS: ++ if (event == SFP_E_TX_FAULT) ++ sfp_sm_fault(sfp, true); ++ else if (event == ++ (sfp->id.ext.options & SFP_OPTIONS_LOS_INVERTED ? ++ SFP_E_LOS_HIGH : SFP_E_LOS_LOW)) ++ sfp_sm_link_up(sfp); ++ break; ++ ++ case SFP_S_LINK_UP: ++ if (event == SFP_E_TX_FAULT) { ++ sfp_sm_link_down(sfp); ++ sfp_sm_fault(sfp, true); ++ } else if (event == ++ (sfp->id.ext.options & SFP_OPTIONS_LOS_INVERTED ? ++ SFP_E_LOS_LOW : SFP_E_LOS_HIGH)) { ++ sfp_sm_link_down(sfp); ++ sfp_sm_next(sfp, SFP_S_WAIT_LOS, 0); ++ } ++ break; ++ ++ case SFP_S_TX_FAULT: ++ if (event == SFP_E_TIMEOUT) { ++ sfp_module_tx_fault_reset(sfp); ++ sfp_sm_next(sfp, SFP_S_REINIT, T_INIT_JIFFIES); ++ } ++ break; ++ ++ case SFP_S_REINIT: ++ if (event == SFP_E_TIMEOUT && sfp->state & SFP_F_TX_FAULT) { ++ sfp_sm_fault(sfp, false); ++ } else if (event == SFP_E_TIMEOUT || event == SFP_E_TX_CLEAR) { ++ dev_info(sfp->dev, "module transmit fault recovered\n"); ++ sfp_sm_link_check_los(sfp); ++ } ++ break; ++ ++ case SFP_S_TX_DISABLE: ++ break; ++ } ++ ++ dev_dbg(sfp->dev, "SM: exit %u:%u:%u\n", ++ sfp->sm_mod_state, sfp->sm_dev_state, sfp->sm_state); ++ ++ mutex_unlock(&sfp->sm_mutex); ++} ++ ++#if 0 ++static int sfp_phy_module_info(struct phy_device *phy, ++ struct ethtool_modinfo *modinfo) ++{ ++ struct sfp *sfp = phy->priv; ++ ++ /* locking... and check module is present */ ++ ++ if (sfp->id.ext.sff8472_compliance) { ++ modinfo->type = ETH_MODULE_SFF_8472; ++ modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN; ++ } else { ++ modinfo->type = ETH_MODULE_SFF_8079; ++ modinfo->eeprom_len = ETH_MODULE_SFF_8079_LEN; ++ } ++ return 0; ++} ++ ++static int sfp_phy_module_eeprom(struct phy_device *phy, ++ struct ethtool_eeprom *ee, u8 *data) ++{ ++ struct sfp *sfp = phy->priv; ++ unsigned int first, last, len; ++ int ret; ++ ++ if (ee->len == 0) ++ return -EINVAL; ++ ++ first = ee->offset; ++ last = ee->offset + ee->len; ++ if (first < ETH_MODULE_SFF_8079_LEN) { ++ len = last; ++ if (len > ETH_MODULE_SFF_8079_LEN) ++ len = ETH_MODULE_SFF_8079_LEN; ++ len -= first; ++ ++ ret = sfp->read(sfp, false, first, data, len); ++ if (ret < 0) ++ return ret; ++ ++ first += len; ++ data += len; ++ } ++ if (first >= ETH_MODULE_SFF_8079_LEN && last > first) { ++ len = last - first; ++ ++ ret = sfp->read(sfp, true, first, data, len); ++ if (ret < 0) ++ return ret; ++ } ++ return 0; ++} ++#endif ++ ++static void sfp_timeout(struct work_struct *work) ++{ ++ struct sfp *sfp = container_of(work, struct sfp, timeout.work); ++ ++ sfp_sm_event(sfp, SFP_E_TIMEOUT); ++} ++ ++static void sfp_check_state(struct sfp *sfp) ++{ ++ unsigned int state, i, changed; ++ ++ state = sfp_get_state(sfp); ++ changed = state ^ sfp->state; ++ changed &= SFP_F_PRESENT | SFP_F_LOS | SFP_F_TX_FAULT; ++ ++ for (i = 0; i < GPIO_MAX; i++) ++ if (changed & BIT(i)) ++ dev_dbg(sfp->dev, "%s %u -> %u\n", gpio_of_names[i], ++ !!(sfp->state & BIT(i)), !!(state & BIT(i))); ++ ++ state |= sfp->state & (SFP_F_TX_DISABLE | SFP_F_RATE_SELECT); ++ sfp->state = state; ++ ++ if (changed & SFP_F_PRESENT) ++ sfp_sm_event(sfp, state & SFP_F_PRESENT ? ++ SFP_E_INSERT : SFP_E_REMOVE); ++ ++ if (changed & SFP_F_TX_FAULT) ++ sfp_sm_event(sfp, state & SFP_F_TX_FAULT ? ++ SFP_E_TX_FAULT : SFP_E_TX_CLEAR); ++ ++ if (changed & SFP_F_LOS) ++ sfp_sm_event(sfp, state & SFP_F_LOS ? ++ SFP_E_LOS_HIGH : SFP_E_LOS_LOW); ++} ++ ++static irqreturn_t sfp_irq(int irq, void *data) ++{ ++ struct sfp *sfp = data; ++ ++ sfp_check_state(sfp); ++ ++ return IRQ_HANDLED; ++} ++ ++static void sfp_poll(struct work_struct *work) ++{ ++ struct sfp *sfp = container_of(work, struct sfp, poll.work); ++ ++ sfp_check_state(sfp); ++ mod_delayed_work(system_wq, &sfp->poll, poll_jiffies); ++} ++ ++static int sfp_netdev_notify(struct notifier_block *nb, unsigned long act, void *data) ++{ ++ struct sfp *sfp = container_of(nb, struct sfp, netdev_nb); ++ struct netdev_notifier_info *info = data; ++ struct net_device *ndev = info->dev; ++ ++ if (!sfp->ndev || ndev != sfp->ndev) ++ return NOTIFY_DONE; ++ ++ switch (act) { ++ case NETDEV_UP: ++ sfp_sm_event(sfp, SFP_E_DEV_UP); ++ break; ++ ++ case NETDEV_GOING_DOWN: ++ sfp_sm_event(sfp, SFP_E_DEV_DOWN); ++ break; ++ ++ case NETDEV_UNREGISTER: ++ if (sfp->mod_phy && sfp->phylink) ++ phylink_disconnect_phy(sfp->phylink); ++ sfp->phylink = NULL; ++ dev_put(sfp->ndev); ++ sfp->ndev = NULL; ++ break; ++ } ++ return NOTIFY_OK; ++} ++ ++static struct sfp *sfp_alloc(struct device *dev) ++{ ++ struct sfp *sfp; ++ ++ sfp = kzalloc(sizeof(*sfp), GFP_KERNEL); ++ if (!sfp) ++ return ERR_PTR(-ENOMEM); ++ ++ sfp->dev = dev; ++ ++ mutex_init(&sfp->sm_mutex); ++ INIT_DELAYED_WORK(&sfp->poll, sfp_poll); ++ INIT_DELAYED_WORK(&sfp->timeout, sfp_timeout); ++ ++ sfp->netdev_nb.notifier_call = sfp_netdev_notify; ++ ++ return sfp; ++} ++ ++static void sfp_destroy(struct sfp *sfp) ++{ ++ cancel_delayed_work_sync(&sfp->poll); ++ cancel_delayed_work_sync(&sfp->timeout); ++ if (sfp->i2c_mii) { ++ mdiobus_unregister(sfp->i2c_mii); ++ mdiobus_free(sfp->i2c_mii); ++ } ++ if (sfp->i2c) ++ i2c_put_adapter(sfp->i2c); ++ of_node_put(sfp->dev->of_node); ++ kfree(sfp); ++} ++ ++static void sfp_cleanup(void *data) ++{ ++ struct sfp *sfp = data; ++ ++ sfp_destroy(sfp); ++} ++ ++static int sfp_probe(struct platform_device *pdev) ++{ ++ struct sfp *sfp; ++ bool poll = false; ++ int irq, err, i; ++ ++ sfp = sfp_alloc(&pdev->dev); ++ if (IS_ERR(sfp)) ++ return PTR_ERR(sfp); ++ ++ platform_set_drvdata(pdev, sfp); ++ ++ err = devm_add_action(sfp->dev, sfp_cleanup, sfp); ++ if (err < 0) ++ return err; ++ ++ if (pdev->dev.of_node) { ++ struct device_node *node = pdev->dev.of_node; ++ struct device_node *np; ++ ++ np = of_parse_phandle(node, "i2c-bus", 0); ++ if (np) { ++ struct i2c_adapter *i2c; ++ ++ i2c = of_find_i2c_adapter_by_node(np); ++ of_node_put(np); ++ if (!i2c) ++ return -EPROBE_DEFER; ++ ++ err = sfp_i2c_configure(sfp, i2c); ++ if (err < 0) { ++ i2c_put_adapter(i2c); ++ return err; ++ } ++ } ++ ++ for (i = 0; i < GPIO_MAX; i++) { ++ sfp->gpio[i] = devm_gpiod_get_optional(sfp->dev, ++ gpio_of_names[i], gpio_flags[i]); ++ if (IS_ERR(sfp->gpio[i])) ++ return PTR_ERR(sfp->gpio[i]); ++ } ++ ++ sfp->get_state = sfp_gpio_get_state; ++ sfp->set_state = sfp_gpio_set_state; ++ ++ np = of_parse_phandle(node, "sfp,ethernet", 0); ++ if (!np) { ++ dev_err(sfp->dev, "missing sfp,ethernet property\n"); ++ return -EINVAL; ++ } ++ ++ sfp->ndev = of_find_net_device_by_node(np); ++ if (!sfp->ndev) { ++ dev_err(sfp->dev, "ethernet device not found\n"); ++ return -EPROBE_DEFER; ++ } ++ ++ dev_hold(sfp->ndev); ++ put_device(&sfp->ndev->dev); ++ ++ sfp->phylink = phylink_lookup_by_netdev(sfp->ndev); ++ if (!sfp->phylink) { ++ dev_err(sfp->dev, "phylink for %s not found\n", ++ netdev_name(sfp->ndev)); ++ return -EPROBE_DEFER; ++ } ++ ++ phylink_disable(sfp->phylink); ++ } ++ ++ sfp->state = sfp_get_state(sfp); ++ if (sfp->gpio[GPIO_TX_DISABLE] && ++ gpiod_get_value_cansleep(sfp->gpio[GPIO_TX_DISABLE])) ++ sfp->state |= SFP_F_TX_DISABLE; ++ if (sfp->gpio[GPIO_RATE_SELECT] && ++ gpiod_get_value_cansleep(sfp->gpio[GPIO_RATE_SELECT])) ++ sfp->state |= SFP_F_RATE_SELECT; ++ sfp_set_state(sfp, sfp->state); ++ sfp_module_tx_disable(sfp); ++ if (sfp->state & SFP_F_PRESENT) ++ sfp_sm_event(sfp, SFP_E_INSERT); ++ ++ for (i = 0; i < GPIO_MAX; i++) { ++ if (gpio_flags[i] != GPIOD_IN || !sfp->gpio[i]) ++ continue; ++ ++ irq = gpiod_to_irq(sfp->gpio[i]); ++ if (!irq) { ++ poll = true; ++ continue; ++ } ++ ++ err = devm_request_threaded_irq(sfp->dev, irq, NULL, sfp_irq, ++ IRQF_ONESHOT | ++ IRQF_TRIGGER_RISING | ++ IRQF_TRIGGER_FALLING, ++ dev_name(sfp->dev), sfp); ++ if (err) ++ poll = true; ++ } ++ ++ if (poll) ++ mod_delayed_work(system_wq, &sfp->poll, poll_jiffies); ++ ++ register_netdevice_notifier(&sfp->netdev_nb); ++ ++ return 0; ++} ++ ++static int sfp_remove(struct platform_device *pdev) ++{ ++ struct sfp *sfp = platform_get_drvdata(pdev); ++ ++ unregister_netdevice_notifier(&sfp->netdev_nb); ++ if (sfp->ndev) ++ dev_put(sfp->ndev); ++ ++ return 0; ++} ++ ++static const struct of_device_id sfp_of_match[] = { ++ { .compatible = "sff,sfp", }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, sfp_of_match); ++ ++static struct platform_driver sfp_driver = { ++ .probe = sfp_probe, ++ .remove = sfp_remove, ++ .driver = { ++ .name = "sfp", ++ .of_match_table = sfp_of_match, ++ }, ++}; ++ ++static int sfp_init(void) ++{ ++ poll_jiffies = msecs_to_jiffies(100); ++ ++ return platform_driver_register(&sfp_driver); ++} ++module_init(sfp_init); ++ ++static void sfp_exit(void) ++{ ++ platform_driver_unregister(&sfp_driver); ++} ++module_exit(sfp_exit); ++ ++MODULE_ALIAS("platform:sfp"); ++MODULE_AUTHOR("Russell King"); ++MODULE_LICENSE("GPL v2"); +--- /dev/null ++++ b/include/linux/sfp.h +@@ -0,0 +1,344 @@ ++#ifndef LINUX_SFP_H ++#define LINUX_SFP_H ++ ++struct __packed sfp_eeprom_base { ++ u8 phys_id; ++ u8 phys_ext_id; ++ u8 connector; ++#if defined __BIG_ENDIAN_BITFIELD ++ u8 e10g_base_er:1; ++ u8 e10g_base_lrm:1; ++ u8 e10g_base_lr:1; ++ u8 e10g_base_sr:1; ++ u8 if_1x_sx:1; ++ u8 if_1x_lx:1; ++ u8 if_1x_copper_active:1; ++ u8 if_1x_copper_passive:1; ++ ++ u8 escon_mmf_1310_led:1; ++ u8 escon_smf_1310_laser:1; ++ u8 sonet_oc192_short_reach:1; ++ u8 sonet_reach_bit1:1; ++ u8 sonet_reach_bit2:1; ++ u8 sonet_oc48_long_reach:1; ++ u8 sonet_oc48_intermediate_reach:1; ++ u8 sonet_oc48_short_reach:1; ++ ++ u8 unallocated_5_7:1; ++ u8 sonet_oc12_smf_long_reach:1; ++ u8 sonet_oc12_smf_intermediate_reach:1; ++ u8 sonet_oc12_short_reach:1; ++ u8 unallocated_5_3:1; ++ u8 sonet_oc3_smf_long_reach:1; ++ u8 sonet_oc3_smf_intermediate_reach:1; ++ u8 sonet_oc3_short_reach:1; ++ ++ u8 e_base_px:1; ++ u8 e_base_bx10:1; ++ u8 e100_base_fx:1; ++ u8 e100_base_lx:1; ++ u8 e1000_base_t:1; ++ u8 e1000_base_cx:1; ++ u8 e1000_base_lx:1; ++ u8 e1000_base_sx:1; ++ ++ u8 fc_ll_v:1; ++ u8 fc_ll_s:1; ++ u8 fc_ll_i:1; ++ u8 fc_ll_l:1; ++ u8 fc_ll_m:1; ++ u8 fc_tech_sa:1; ++ u8 fc_tech_lc:1; ++ u8 fc_tech_electrical_inter_enclosure:1; ++ ++ u8 fc_tech_electrical_intra_enclosure:1; ++ u8 fc_tech_sn:1; ++ u8 fc_tech_sl:1; ++ u8 fc_tech_ll:1; ++ u8 sfp_ct_active:1; ++ u8 sfp_ct_passive:1; ++ u8 unallocated_8_1:1; ++ u8 unallocated_8_0:1; ++ ++ u8 fc_media_tw:1; ++ u8 fc_media_tp:1; ++ u8 fc_media_mi:1; ++ u8 fc_media_tv:1; ++ u8 fc_media_m6:1; ++ u8 fc_media_m5:1; ++ u8 unallocated_9_1:1; ++ u8 fc_media_sm:1; ++ ++ u8 fc_speed_1200:1; ++ u8 fc_speed_800:1; ++ u8 fc_speed_1600:1; ++ u8 fc_speed_400:1; ++ u8 fc_speed_3200:1; ++ u8 fc_speed_200:1; ++ u8 unallocated_10_1:1; ++ u8 fc_speed_100:1; ++#elif defined __LITTLE_ENDIAN_BITFIELD ++ u8 if_1x_copper_passive:1; ++ u8 if_1x_copper_active:1; ++ u8 if_1x_lx:1; ++ u8 if_1x_sx:1; ++ u8 e10g_base_sr:1; ++ u8 e10g_base_lr:1; ++ u8 e10g_base_lrm:1; ++ u8 e10g_base_er:1; ++ ++ u8 sonet_oc3_short_reach:1; ++ u8 sonet_oc3_smf_intermediate_reach:1; ++ u8 sonet_oc3_smf_long_reach:1; ++ u8 unallocated_5_3:1; ++ u8 sonet_oc12_short_reach:1; ++ u8 sonet_oc12_smf_intermediate_reach:1; ++ u8 sonet_oc12_smf_long_reach:1; ++ u8 unallocated_5_7:1; ++ ++ u8 sonet_oc48_short_reach:1; ++ u8 sonet_oc48_intermediate_reach:1; ++ u8 sonet_oc48_long_reach:1; ++ u8 sonet_reach_bit2:1; ++ u8 sonet_reach_bit1:1; ++ u8 sonet_oc192_short_reach:1; ++ u8 escon_smf_1310_laser:1; ++ u8 escon_mmf_1310_led:1; ++ ++ u8 e1000_base_sx:1; ++ u8 e1000_base_lx:1; ++ u8 e1000_base_cx:1; ++ u8 e1000_base_t:1; ++ u8 e100_base_lx:1; ++ u8 e100_base_fx:1; ++ u8 e_base_bx10:1; ++ u8 e_base_px:1; ++ ++ u8 fc_tech_electrical_inter_enclosure:1; ++ u8 fc_tech_lc:1; ++ u8 fc_tech_sa:1; ++ u8 fc_ll_m:1; ++ u8 fc_ll_l:1; ++ u8 fc_ll_i:1; ++ u8 fc_ll_s:1; ++ u8 fc_ll_v:1; ++ ++ u8 unallocated_8_0:1; ++ u8 unallocated_8_1:1; ++ u8 sfp_ct_passive:1; ++ u8 sfp_ct_active:1; ++ u8 fc_tech_ll:1; ++ u8 fc_tech_sl:1; ++ u8 fc_tech_sn:1; ++ u8 fc_tech_electrical_intra_enclosure:1; ++ ++ u8 fc_media_sm:1; ++ u8 unallocated_9_1:1; ++ u8 fc_media_m5:1; ++ u8 fc_media_m6:1; ++ u8 fc_media_tv:1; ++ u8 fc_media_mi:1; ++ u8 fc_media_tp:1; ++ u8 fc_media_tw:1; ++ ++ u8 fc_speed_100:1; ++ u8 unallocated_10_1:1; ++ u8 fc_speed_200:1; ++ u8 fc_speed_3200:1; ++ u8 fc_speed_400:1; ++ u8 fc_speed_1600:1; ++ u8 fc_speed_800:1; ++ u8 fc_speed_1200:1; ++#else ++#error Unknown Endian ++#endif ++ u8 encoding; ++ u8 br_nominal; ++ u8 rate_id; ++ u8 link_len[6]; ++ char vendor_name[16]; ++ u8 reserved36; ++ char vendor_oui[3]; ++ char vendor_pn[16]; ++ char vendor_rev[4]; ++ union { ++ __be16 optical_wavelength; ++ u8 cable_spec; ++ }; ++ u8 reserved62; ++ u8 cc_base; ++}; ++ ++struct __packed sfp_eeprom_ext { ++ __be16 options; ++ u8 br_max; ++ u8 br_min; ++ char vendor_sn[16]; ++ char datecode[8]; ++ u8 diagmon; ++ u8 enhopts; ++ u8 sff8472_compliance; ++ u8 cc_ext; ++}; ++ ++struct __packed sfp_eeprom_id { ++ struct sfp_eeprom_base base; ++ struct sfp_eeprom_ext ext; ++}; ++ ++/* SFP EEPROM registers */ ++enum { ++ SFP_PHYS_ID = 0x00, ++ SFP_PHYS_EXT_ID = 0x01, ++ SFP_CONNECTOR = 0x02, ++ SFP_COMPLIANCE = 0x03, ++ SFP_ENCODING = 0x0b, ++ SFP_BR_NOMINAL = 0x0c, ++ SFP_RATE_ID = 0x0d, ++ SFP_LINK_LEN_SM_KM = 0x0e, ++ SFP_LINK_LEN_SM_100M = 0x0f, ++ SFP_LINK_LEN_50UM_OM2_10M = 0x10, ++ SFP_LINK_LEN_62_5UM_OM1_10M = 0x11, ++ SFP_LINK_LEN_COPPER_1M = 0x12, ++ SFP_LINK_LEN_50UM_OM4_10M = 0x12, ++ SFP_LINK_LEN_50UM_OM3_10M = 0x13, ++ SFP_VENDOR_NAME = 0x14, ++ SFP_VENDOR_OUI = 0x25, ++ SFP_VENDOR_PN = 0x28, ++ SFP_VENDOR_REV = 0x38, ++ SFP_OPTICAL_WAVELENGTH_MSB = 0x3c, ++ SFP_OPTICAL_WAVELENGTH_LSB = 0x3d, ++ SFP_CABLE_SPEC = 0x3c, ++ SFP_CC_BASE = 0x3f, ++ SFP_OPTIONS = 0x40, /* 2 bytes, MSB, LSB */ ++ SFP_BR_MAX = 0x42, ++ SFP_BR_MIN = 0x43, ++ SFP_VENDOR_SN = 0x44, ++ SFP_DATECODE = 0x54, ++ SFP_DIAGMON = 0x5c, ++ SFP_ENHOPTS = 0x5d, ++ SFP_SFF8472_COMPLIANCE = 0x5e, ++ SFP_CC_EXT = 0x5f, ++ ++ SFP_PHYS_ID_SFP = 0x03, ++ SFP_PHYS_EXT_ID_SFP = 0x04, ++ SFP_CONNECTOR_UNSPEC = 0x00, ++ /* codes 01-05 not supportable on SFP, but some modules have single SC */ ++ SFP_CONNECTOR_SC = 0x01, ++ SFP_CONNECTOR_FIBERJACK = 0x06, ++ SFP_CONNECTOR_LC = 0x07, ++ SFP_CONNECTOR_MT_RJ = 0x08, ++ SFP_CONNECTOR_MU = 0x09, ++ SFP_CONNECTOR_SG = 0x0a, ++ SFP_CONNECTOR_OPTICAL_PIGTAIL = 0x0b, ++ SFP_CONNECTOR_MPO_1X12 = 0x0c, ++ SFP_CONNECTOR_MPO_2X16 = 0x0d, ++ SFP_CONNECTOR_HSSDC_II = 0x20, ++ SFP_CONNECTOR_COPPER_PIGTAIL = 0x21, ++ SFP_CONNECTOR_RJ45 = 0x22, ++ SFP_CONNECTOR_NOSEPARATE = 0x23, ++ SFP_CONNECTOR_MXC_2X16 = 0x24, ++ SFP_ENCODING_UNSPEC = 0x00, ++ SFP_ENCODING_8B10B = 0x01, ++ SFP_ENCODING_4B5B = 0x02, ++ SFP_ENCODING_NRZ = 0x03, ++ SFP_ENCODING_MANCHESTER = 0x04, ++ SFP_OPTIONS_HIGH_POWER_LEVEL = BIT(13), ++ SFP_OPTIONS_PAGING_A2 = BIT(12), ++ SFP_OPTIONS_RETIMER = BIT(11), ++ SFP_OPTIONS_COOLED_XCVR = BIT(10), ++ SFP_OPTIONS_POWER_DECL = BIT(9), ++ SFP_OPTIONS_RX_LINEAR_OUT = BIT(8), ++ SFP_OPTIONS_RX_DECISION_THRESH = BIT(7), ++ SFP_OPTIONS_TUNABLE_TX = BIT(6), ++ SFP_OPTIONS_RATE_SELECT = BIT(5), ++ SFP_OPTIONS_TX_DISABLE = BIT(4), ++ SFP_OPTIONS_TX_FAULT = BIT(3), ++ SFP_OPTIONS_LOS_INVERTED = BIT(2), ++ SFP_OPTIONS_LOS_NORMAL = BIT(1), ++ SFP_DIAGMON_DDM = BIT(6), ++ SFP_DIAGMON_INT_CAL = BIT(5), ++ SFP_DIAGMON_EXT_CAL = BIT(4), ++ SFP_DIAGMON_RXPWR_AVG = BIT(3), ++ SFP_DIAGMON_ADDRMODE = BIT(2), ++ SFP_ENHOPTS_ALARMWARN = BIT(7), ++ SFP_ENHOPTS_SOFT_TX_DISABLE = BIT(6), ++ SFP_ENHOPTS_SOFT_TX_FAULT = BIT(5), ++ SFP_ENHOPTS_SOFT_RX_LOS = BIT(4), ++ SFP_ENHOPTS_SOFT_RATE_SELECT = BIT(3), ++ SFP_ENHOPTS_APP_SELECT_SFF8079 = BIT(2), ++ SFP_ENHOPTS_SOFT_RATE_SFF8431 = BIT(1), ++ SFP_SFF8472_COMPLIANCE_NONE = 0x00, ++ SFP_SFF8472_COMPLIANCE_REV9_3 = 0x01, ++ SFP_SFF8472_COMPLIANCE_REV9_5 = 0x02, ++ SFP_SFF8472_COMPLIANCE_REV10_2 = 0x03, ++ SFP_SFF8472_COMPLIANCE_REV10_4 = 0x04, ++ SFP_SFF8472_COMPLIANCE_REV11_0 = 0x05, ++ SFP_SFF8472_COMPLIANCE_REV11_3 = 0x06, ++ SFP_SFF8472_COMPLIANCE_REV11_4 = 0x07, ++ SFP_SFF8472_COMPLIANCE_REV12_0 = 0x08, ++}; ++ ++/* SFP Diagnostics */ ++enum { ++ /* Alarm and warnings stored MSB at lower address then LSB */ ++ SFP_TEMP_HIGH_ALARM = 0x00, ++ SFP_TEMP_LOW_ALARM = 0x02, ++ SFP_TEMP_HIGH_WARN = 0x04, ++ SFP_TEMP_LOW_WARN = 0x06, ++ SFP_VOLT_HIGH_ALARM = 0x08, ++ SFP_VOLT_LOW_ALARM = 0x0a, ++ SFP_VOLT_HIGH_WARN = 0x0c, ++ SFP_VOLT_LOW_WARN = 0x0e, ++ SFP_BIAS_HIGH_ALARM = 0x10, ++ SFP_BIAS_LOW_ALARM = 0x12, ++ SFP_BIAS_HIGH_WARN = 0x14, ++ SFP_BIAS_LOW_WARN = 0x16, ++ SFP_TXPWR_HIGH_ALARM = 0x18, ++ SFP_TXPWR_LOW_ALARM = 0x1a, ++ SFP_TXPWR_HIGH_WARN = 0x1c, ++ SFP_TXPWR_LOW_WARN = 0x1e, ++ SFP_RXPWR_HIGH_ALARM = 0x20, ++ SFP_RXPWR_LOW_ALARM = 0x22, ++ SFP_RXPWR_HIGH_WARN = 0x24, ++ SFP_RXPWR_LOW_WARN = 0x26, ++ SFP_LASER_TEMP_HIGH_ALARM = 0x28, ++ SFP_LASER_TEMP_LOW_ALARM = 0x2a, ++ SFP_LASER_TEMP_HIGH_WARN = 0x2c, ++ SFP_LASER_TEMP_LOW_WARN = 0x2e, ++ SFP_TEC_CUR_HIGH_ALARM = 0x30, ++ SFP_TEC_CUR_LOW_ALARM = 0x32, ++ SFP_TEC_CUR_HIGH_WARN = 0x34, ++ SFP_TEC_CUR_LOW_WARN = 0x36, ++ SFP_CAL_RXPWR4 = 0x38, ++ SFP_CAL_RXPWR3 = 0x3c, ++ SFP_CAL_RXPWR2 = 0x40, ++ SFP_CAL_RXPWR1 = 0x44, ++ SFP_CAL_RXPWR0 = 0x48, ++ SFP_CAL_TXI_SLOPE = 0x4c, ++ SFP_CAL_TXI_OFFSET = 0x4e, ++ SFP_CAL_TXPWR_SLOPE = 0x50, ++ SFP_CAL_TXPWR_OFFSET = 0x52, ++ SFP_CAL_T_SLOPE = 0x54, ++ SFP_CAL_T_OFFSET = 0x56, ++ SFP_CAL_V_SLOPE = 0x58, ++ SFP_CAL_V_OFFSET = 0x5a, ++ SFP_CHKSUM = 0x5f, ++ ++ SFP_TEMP = 0x60, ++ SFP_VCC = 0x62, ++ SFP_TX_BIAS = 0x64, ++ SFP_TX_POWER = 0x66, ++ SFP_RX_POWER = 0x68, ++ SFP_LASER_TEMP = 0x6a, ++ SFP_TEC_CUR = 0x6c, ++ ++ SFP_STATUS = 0x6e, ++ SFP_ALARM = 0x70, ++ ++ SFP_EXT_STATUS = 0x76, ++ SFP_VSL = 0x78, ++ SFP_PAGE = 0x7f, ++}; ++ ++#endif diff --git a/target/linux/mvebu/patches-4.9/418-sfp-display-SFP-module-information.patch b/target/linux/mvebu/patches-4.9/418-sfp-display-SFP-module-information.patch new file mode 100644 index 0000000000..f0f2edae94 --- /dev/null +++ b/target/linux/mvebu/patches-4.9/418-sfp-display-SFP-module-information.patch @@ -0,0 +1,280 @@ +From: Russell King <rmk+kernel@arm.linux.org.uk> +Date: Sun, 13 Sep 2015 01:06:31 +0100 +Subject: [PATCH] sfp: display SFP module information + +Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> +--- + +--- a/drivers/net/phy/sfp.c ++++ b/drivers/net/phy/sfp.c +@@ -251,6 +251,182 @@ static unsigned int sfp_check(void *buf, + return check; + } + ++static const char *sfp_link_len(char *buf, size_t size, unsigned int length, ++ unsigned int multiplier) ++{ ++ if (length == 0) ++ return "unsupported/unspecified"; ++ ++ if (length == 255) { ++ *buf++ = '>'; ++ size -= 1; ++ length -= 1; ++ } ++ ++ length *= multiplier; ++ ++ if (length >= 1000) ++ snprintf(buf, size, "%u.%0*ukm", ++ length / 1000, ++ multiplier > 100 ? 1 : ++ multiplier > 10 ? 2 : 3, ++ length % 1000); ++ else ++ snprintf(buf, size, "%um", length); ++ ++ return buf; ++} ++ ++struct bitfield { ++ unsigned int mask; ++ unsigned int val; ++ const char *str; ++}; ++ ++static const struct bitfield sfp_options[] = { ++ { ++ .mask = SFP_OPTIONS_HIGH_POWER_LEVEL, ++ .val = SFP_OPTIONS_HIGH_POWER_LEVEL, ++ .str = "hpl", ++ }, { ++ .mask = SFP_OPTIONS_PAGING_A2, ++ .val = SFP_OPTIONS_PAGING_A2, ++ .str = "paginga2", ++ }, { ++ .mask = SFP_OPTIONS_RETIMER, ++ .val = SFP_OPTIONS_RETIMER, ++ .str = "retimer", ++ }, { ++ .mask = SFP_OPTIONS_COOLED_XCVR, ++ .val = SFP_OPTIONS_COOLED_XCVR, ++ .str = "cooled", ++ }, { ++ .mask = SFP_OPTIONS_POWER_DECL, ++ .val = SFP_OPTIONS_POWER_DECL, ++ .str = "powerdecl", ++ }, { ++ .mask = SFP_OPTIONS_RX_LINEAR_OUT, ++ .val = SFP_OPTIONS_RX_LINEAR_OUT, ++ .str = "rxlinear", ++ }, { ++ .mask = SFP_OPTIONS_RX_DECISION_THRESH, ++ .val = SFP_OPTIONS_RX_DECISION_THRESH, ++ .str = "rxthresh", ++ }, { ++ .mask = SFP_OPTIONS_TUNABLE_TX, ++ .val = SFP_OPTIONS_TUNABLE_TX, ++ .str = "tunabletx", ++ }, { ++ .mask = SFP_OPTIONS_RATE_SELECT, ++ .val = SFP_OPTIONS_RATE_SELECT, ++ .str = "ratesel", ++ }, { ++ .mask = SFP_OPTIONS_TX_DISABLE, ++ .val = SFP_OPTIONS_TX_DISABLE, ++ .str = "txdisable", ++ }, { ++ .mask = SFP_OPTIONS_TX_FAULT, ++ .val = SFP_OPTIONS_TX_FAULT, ++ .str = "txfault", ++ }, { ++ .mask = SFP_OPTIONS_LOS_INVERTED, ++ .val = SFP_OPTIONS_LOS_INVERTED, ++ .str = "los-", ++ }, { ++ .mask = SFP_OPTIONS_LOS_NORMAL, ++ .val = SFP_OPTIONS_LOS_NORMAL, ++ .str = "los+", ++ }, { } ++}; ++ ++static const struct bitfield diagmon[] = { ++ { ++ .mask = SFP_DIAGMON_DDM, ++ .val = SFP_DIAGMON_DDM, ++ .str = "ddm", ++ }, { ++ .mask = SFP_DIAGMON_INT_CAL, ++ .val = SFP_DIAGMON_INT_CAL, ++ .str = "intcal", ++ }, { ++ .mask = SFP_DIAGMON_EXT_CAL, ++ .val = SFP_DIAGMON_EXT_CAL, ++ .str = "extcal", ++ }, { ++ .mask = SFP_DIAGMON_RXPWR_AVG, ++ .val = SFP_DIAGMON_RXPWR_AVG, ++ .str = "rxpwravg", ++ }, { } ++}; ++ ++static const char *sfp_bitfield(char *out, size_t outsz, const struct bitfield *bits, unsigned int val) ++{ ++ char *p = out; ++ int n; ++ ++ *p = '\0'; ++ while (bits->mask) { ++ if ((val & bits->mask) == bits->val) { ++ n = snprintf(p, outsz, "%s%s", ++ out != p ? ", " : "", ++ bits->str); ++ if (n == outsz) ++ break; ++ p += n; ++ outsz -= n; ++ } ++ bits++; ++ } ++ ++ return out; ++} ++ ++static const char *sfp_connector(unsigned int connector) ++{ ++ switch (connector) { ++ case SFP_CONNECTOR_UNSPEC: ++ return "unknown/unspecified"; ++ case SFP_CONNECTOR_SC: ++ return "SC"; ++ case SFP_CONNECTOR_FIBERJACK: ++ return "Fiberjack"; ++ case SFP_CONNECTOR_LC: ++ return "LC"; ++ case SFP_CONNECTOR_MT_RJ: ++ return "MT-RJ"; ++ case SFP_CONNECTOR_MU: ++ return "MU"; ++ case SFP_CONNECTOR_SG: ++ return "SG"; ++ case SFP_CONNECTOR_OPTICAL_PIGTAIL: ++ return "Optical pigtail"; ++ case SFP_CONNECTOR_HSSDC_II: ++ return "HSSDC II"; ++ case SFP_CONNECTOR_COPPER_PIGTAIL: ++ return "Copper pigtail"; ++ default: ++ return "unknown"; ++ } ++} ++ ++static const char *sfp_encoding(unsigned int encoding) ++{ ++ switch (encoding) { ++ case SFP_ENCODING_UNSPEC: ++ return "unspecified"; ++ case SFP_ENCODING_8B10B: ++ return "8b10b"; ++ case SFP_ENCODING_4B5B: ++ return "4b5b"; ++ case SFP_ENCODING_NRZ: ++ return "NRZ"; ++ case SFP_ENCODING_MANCHESTER: ++ return "MANCHESTER"; ++ default: ++ return "unknown"; ++ } ++} ++ + /* Helpers */ + static void sfp_module_tx_disable(struct sfp *sfp) + { +@@ -428,6 +604,7 @@ static int sfp_sm_mod_probe(struct sfp * + char sn[17]; + char date[9]; + char rev[5]; ++ char options[80]; + u8 check; + int err; + +@@ -471,10 +648,78 @@ static int sfp_sm_mod_probe(struct sfp * + rev[4] = '\0'; + memcpy(sn, sfp->id.ext.vendor_sn, 16); + sn[16] = '\0'; +- memcpy(date, sfp->id.ext.datecode, 8); ++ date[0] = sfp->id.ext.datecode[4]; ++ date[1] = sfp->id.ext.datecode[5]; ++ date[2] = '-'; ++ date[3] = sfp->id.ext.datecode[2]; ++ date[4] = sfp->id.ext.datecode[3]; ++ date[5] = '-'; ++ date[6] = sfp->id.ext.datecode[0]; ++ date[7] = sfp->id.ext.datecode[1]; + date[8] = '\0'; + + dev_info(sfp->dev, "module %s %s rev %s sn %s dc %s\n", vendor, part, rev, sn, date); ++ dev_info(sfp->dev, " %s connector, encoding %s, nominal bitrate %u.%uGbps +%u%% -%u%%\n", ++ sfp_connector(sfp->id.base.connector), ++ sfp_encoding(sfp->id.base.encoding), ++ sfp->id.base.br_nominal / 10, ++ sfp->id.base.br_nominal % 10, ++ sfp->id.ext.br_max, sfp->id.ext.br_min); ++ dev_info(sfp->dev, " 1000BaseSX%c 1000BaseLX%c 1000BaseCX%c 1000BaseT%c 100BaseTLX%c 1000BaseFX%c BaseBX10%c BasePX%c\n", ++ sfp->id.base.e1000_base_sx ? '+' : '-', ++ sfp->id.base.e1000_base_lx ? '+' : '-', ++ sfp->id.base.e1000_base_cx ? '+' : '-', ++ sfp->id.base.e1000_base_t ? '+' : '-', ++ sfp->id.base.e100_base_lx ? '+' : '-', ++ sfp->id.base.e100_base_fx ? '+' : '-', ++ sfp->id.base.e_base_bx10 ? '+' : '-', ++ sfp->id.base.e_base_px ? '+' : '-'); ++ ++ if (!sfp->id.base.sfp_ct_passive && !sfp->id.base.sfp_ct_active && ++ !sfp->id.base.e1000_base_t) { ++ char len_9um[16], len_om[16]; ++ ++ dev_info(sfp->dev, " Wavelength %unm, fiber lengths:\n", ++ be16_to_cpup(&sfp->id.base.optical_wavelength)); ++ ++ if (sfp->id.base.link_len[0] == 255) ++ strcpy(len_9um, ">254km"); ++ else if (sfp->id.base.link_len[1] && sfp->id.base.link_len[1] != 255) ++ sprintf(len_9um, "%um", ++ sfp->id.base.link_len[1] * 100); ++ else if (sfp->id.base.link_len[0]) ++ sprintf(len_9um, "%ukm", sfp->id.base.link_len[0]); ++ else if (sfp->id.base.link_len[1] == 255) ++ strcpy(len_9um, ">25.4km"); ++ else ++ strcpy(len_9um, "unsupported"); ++ ++ dev_info(sfp->dev, " 9µm SM : %s\n", len_9um); ++ dev_info(sfp->dev, " 62.5µm MM OM1: %s\n", ++ sfp_link_len(len_om, sizeof(len_om), ++ sfp->id.base.link_len[3], 10)); ++ dev_info(sfp->dev, " 50µm MM OM2: %s\n", ++ sfp_link_len(len_om, sizeof(len_om), ++ sfp->id.base.link_len[2], 10)); ++ dev_info(sfp->dev, " 50µm MM OM3: %s\n", ++ sfp_link_len(len_om, sizeof(len_om), ++ sfp->id.base.link_len[5], 10)); ++ dev_info(sfp->dev, " 50µm MM OM4: %s\n", ++ sfp_link_len(len_om, sizeof(len_om), ++ sfp->id.base.link_len[4], 10)); ++ } else { ++ char len[16]; ++ dev_info(sfp->dev, " Copper length: %s\n", ++ sfp_link_len(len, sizeof(len), ++ sfp->id.base.link_len[4], 1)); ++ } ++ ++ dev_info(sfp->dev, " Options: %s\n", ++ sfp_bitfield(options, sizeof(options), sfp_options, ++ be16_to_cpu(sfp->id.ext.options))); ++ dev_info(sfp->dev, " Diagnostics: %s\n", ++ sfp_bitfield(options, sizeof(options), diagmon, ++ sfp->id.ext.diagmon)); + + /* We only support SFP modules, not the legacy GBIC modules. */ + if (sfp->id.base.phys_id != SFP_PHYS_ID_SFP || diff --git a/target/linux/mvebu/patches-4.9/419-net-mvneta-convert-to-phylink.patch b/target/linux/mvebu/patches-4.9/419-net-mvneta-convert-to-phylink.patch new file mode 100644 index 0000000000..dca4fa2992 --- /dev/null +++ b/target/linux/mvebu/patches-4.9/419-net-mvneta-convert-to-phylink.patch @@ -0,0 +1,743 @@ +From: Russell King <rmk+kernel@arm.linux.org.uk> +Date: Wed, 16 Sep 2015 21:27:10 +0100 +Subject: [PATCH] net: mvneta: convert to phylink + +Convert mvneta to use phylink, which models the MAC to PHY link in +a generic, reusable form. + +Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> +--- + +--- a/drivers/net/ethernet/marvell/Kconfig ++++ b/drivers/net/ethernet/marvell/Kconfig +@@ -57,7 +57,7 @@ config MVNETA + tristate "Marvell Armada 370/38x/XP network interface support" + depends on PLAT_ORION + select MVMDIO +- select FIXED_PHY ++ select PHYLINK + ---help--- + This driver supports the network interface units in the + Marvell ARMADA XP, ARMADA 370 and ARMADA 38x SoC family. +--- a/drivers/net/ethernet/marvell/mvneta.c ++++ b/drivers/net/ethernet/marvell/mvneta.c +@@ -28,6 +28,7 @@ + #include <linux/of_mdio.h> + #include <linux/of_net.h> + #include <linux/phy.h> ++#include <linux/phylink.h> + #include <linux/platform_device.h> + #include <linux/skbuff.h> + #include <net/hwbm.h> +@@ -188,6 +189,7 @@ + #define MVNETA_GMAC_CTRL_0 0x2c00 + #define MVNETA_GMAC_MAX_RX_SIZE_SHIFT 2 + #define MVNETA_GMAC_MAX_RX_SIZE_MASK 0x7ffc ++#define MVNETA_GMAC0_PORT_1000BASE_X BIT(1) + #define MVNETA_GMAC0_PORT_ENABLE BIT(0) + #define MVNETA_GMAC_CTRL_2 0x2c08 + #define MVNETA_GMAC2_INBAND_AN_ENABLE BIT(0) +@@ -203,13 +205,19 @@ + #define MVNETA_GMAC_TX_FLOW_CTRL_ENABLE BIT(5) + #define MVNETA_GMAC_RX_FLOW_CTRL_ACTIVE BIT(6) + #define MVNETA_GMAC_TX_FLOW_CTRL_ACTIVE BIT(7) ++#define MVNETA_GMAC_AN_COMPLETE BIT(11) ++#define MVNETA_GMAC_SYNC_OK BIT(14) + #define MVNETA_GMAC_AUTONEG_CONFIG 0x2c0c + #define MVNETA_GMAC_FORCE_LINK_DOWN BIT(0) + #define MVNETA_GMAC_FORCE_LINK_PASS BIT(1) + #define MVNETA_GMAC_INBAND_AN_ENABLE BIT(2) ++#define MVNETA_GMAC_AN_BYPASS_ENABLE BIT(3) ++#define MVNETA_GMAC_INBAND_RESTART_AN BIT(4) + #define MVNETA_GMAC_CONFIG_MII_SPEED BIT(5) + #define MVNETA_GMAC_CONFIG_GMII_SPEED BIT(6) + #define MVNETA_GMAC_AN_SPEED_EN BIT(7) ++#define MVNETA_GMAC_CONFIG_FLOW_CTRL BIT(8) ++#define MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL BIT(9) + #define MVNETA_GMAC_AN_FLOW_CTRL_EN BIT(11) + #define MVNETA_GMAC_CONFIG_FULL_DUPLEX BIT(12) + #define MVNETA_GMAC_AN_DUPLEX_EN BIT(13) +@@ -399,14 +407,9 @@ struct mvneta_port { + u16 tx_ring_size; + u16 rx_ring_size; + +- struct mii_bus *mii_bus; +- phy_interface_t phy_interface; +- struct device_node *phy_node; +- unsigned int link; +- unsigned int duplex; +- unsigned int speed; ++ struct device_node *dn; + unsigned int tx_csum_limit; +- unsigned int use_inband_status:1; ++ struct phylink *phylink; + + struct mvneta_bm *bm_priv; + struct mvneta_bm_pool *pool_long; +@@ -1240,44 +1243,6 @@ static void mvneta_set_other_mcast_table + mvreg_write(pp, MVNETA_DA_FILT_OTH_MCAST + offset, val); + } + +-static void mvneta_set_autoneg(struct mvneta_port *pp, int enable) +-{ +- u32 val; +- +- if (enable) { +- val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); +- val &= ~(MVNETA_GMAC_FORCE_LINK_PASS | +- MVNETA_GMAC_FORCE_LINK_DOWN | +- MVNETA_GMAC_AN_FLOW_CTRL_EN); +- val |= MVNETA_GMAC_INBAND_AN_ENABLE | +- MVNETA_GMAC_AN_SPEED_EN | +- MVNETA_GMAC_AN_DUPLEX_EN; +- mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val); +- +- val = mvreg_read(pp, MVNETA_GMAC_CLOCK_DIVIDER); +- val |= MVNETA_GMAC_1MS_CLOCK_ENABLE; +- mvreg_write(pp, MVNETA_GMAC_CLOCK_DIVIDER, val); +- +- val = mvreg_read(pp, MVNETA_GMAC_CTRL_2); +- val |= MVNETA_GMAC2_INBAND_AN_ENABLE; +- mvreg_write(pp, MVNETA_GMAC_CTRL_2, val); +- } else { +- val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); +- val &= ~(MVNETA_GMAC_INBAND_AN_ENABLE | +- MVNETA_GMAC_AN_SPEED_EN | +- MVNETA_GMAC_AN_DUPLEX_EN); +- mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val); +- +- val = mvreg_read(pp, MVNETA_GMAC_CLOCK_DIVIDER); +- val &= ~MVNETA_GMAC_1MS_CLOCK_ENABLE; +- mvreg_write(pp, MVNETA_GMAC_CLOCK_DIVIDER, val); +- +- val = mvreg_read(pp, MVNETA_GMAC_CTRL_2); +- val &= ~MVNETA_GMAC2_INBAND_AN_ENABLE; +- mvreg_write(pp, MVNETA_GMAC_CTRL_2, val); +- } +-} +- + static void mvneta_percpu_unmask_interrupt(void *arg) + { + struct mvneta_port *pp = arg; +@@ -1425,7 +1390,6 @@ static void mvneta_defaults_set(struct m + val &= ~MVNETA_PHY_POLLING_ENABLE; + mvreg_write(pp, MVNETA_UNIT_CONTROL, val); + +- mvneta_set_autoneg(pp, pp->use_inband_status); + mvneta_set_ucast_table(pp, -1); + mvneta_set_special_mcast_table(pp, -1); + mvneta_set_other_mcast_table(pp, -1); +@@ -2630,26 +2594,11 @@ static irqreturn_t mvneta_isr(int irq, v + return IRQ_HANDLED; + } + +-static int mvneta_fixed_link_update(struct mvneta_port *pp, +- struct phy_device *phy) ++static void mvneta_link_change(struct mvneta_port *pp) + { +- struct fixed_phy_status status; +- struct fixed_phy_status changed = {}; + u32 gmac_stat = mvreg_read(pp, MVNETA_GMAC_STATUS); + +- status.link = !!(gmac_stat & MVNETA_GMAC_LINK_UP); +- if (gmac_stat & MVNETA_GMAC_SPEED_1000) +- status.speed = SPEED_1000; +- else if (gmac_stat & MVNETA_GMAC_SPEED_100) +- status.speed = SPEED_100; +- else +- status.speed = SPEED_10; +- status.duplex = !!(gmac_stat & MVNETA_GMAC_FULL_DUPLEX); +- changed.link = 1; +- changed.speed = 1; +- changed.duplex = 1; +- fixed_phy_update_state(phy, &status, &changed); +- return 0; ++ phylink_mac_change(pp->phylink, !!(gmac_stat & MVNETA_GMAC_LINK_UP)); + } + + /* NAPI handler +@@ -2665,7 +2614,6 @@ static int mvneta_poll(struct napi_struc + u32 cause_rx_tx; + int rx_queue; + struct mvneta_port *pp = netdev_priv(napi->dev); +- struct net_device *ndev = pp->dev; + struct mvneta_pcpu_port *port = this_cpu_ptr(pp->ports); + + if (!netif_running(pp->dev)) { +@@ -2679,12 +2627,11 @@ static int mvneta_poll(struct napi_struc + u32 cause_misc = mvreg_read(pp, MVNETA_INTR_MISC_CAUSE); + + mvreg_write(pp, MVNETA_INTR_MISC_CAUSE, 0); +- if (pp->use_inband_status && (cause_misc & +- (MVNETA_CAUSE_PHY_STATUS_CHANGE | +- MVNETA_CAUSE_LINK_CHANGE | +- MVNETA_CAUSE_PSC_SYNC_CHANGE))) { +- mvneta_fixed_link_update(pp, ndev->phydev); +- } ++ ++ if (cause_misc & (MVNETA_CAUSE_PHY_STATUS_CHANGE | ++ MVNETA_CAUSE_LINK_CHANGE | ++ MVNETA_CAUSE_PSC_SYNC_CHANGE)) ++ mvneta_link_change(pp); + } + + /* Release Tx descriptors */ +@@ -2982,7 +2929,6 @@ static int mvneta_setup_txqs(struct mvne + static void mvneta_start_dev(struct mvneta_port *pp) + { + int cpu; +- struct net_device *ndev = pp->dev; + + mvneta_max_rx_size_set(pp, pp->pkt_size); + mvneta_txq_max_tx_size_set(pp, pp->pkt_size); +@@ -3005,16 +2951,15 @@ static void mvneta_start_dev(struct mvne + MVNETA_CAUSE_LINK_CHANGE | + MVNETA_CAUSE_PSC_SYNC_CHANGE); + +- phy_start(ndev->phydev); ++ phylink_start(pp->phylink); + netif_tx_start_all_queues(pp->dev); + } + + static void mvneta_stop_dev(struct mvneta_port *pp) + { + unsigned int cpu; +- struct net_device *ndev = pp->dev; + +- phy_stop(ndev->phydev); ++ phylink_stop(pp->phylink); + + for_each_online_cpu(cpu) { + struct mvneta_pcpu_port *port = per_cpu_ptr(pp->ports, cpu); +@@ -3184,99 +3129,210 @@ static int mvneta_set_mac_addr(struct ne + return 0; + } + +-static void mvneta_adjust_link(struct net_device *ndev) ++static void mvneta_validate_support(struct net_device *ndev, unsigned int mode, ++ unsigned long *support) ++{ ++ __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; ++ ++ /* Allow all the expected bits */ ++ phylink_set(mask, Autoneg); ++ phylink_set(mask, TP); ++ phylink_set(mask, AUI); ++ phylink_set(mask, MII); ++ phylink_set(mask, FIBRE); ++ phylink_set(mask, BNC); ++ phylink_set(mask, Backplane); ++ ++ /* Half-duplex at speeds higher than 100Mbit is unsupported */ ++ phylink_set(mask, 1000baseT_Full); ++ phylink_set(mask, 1000baseX_Full); ++ ++ if (mode != MLO_AN_8023Z) { ++ /* 10M and 100M are only supported in non-802.3z mode */ ++ phylink_set(mask, 10baseT_Half); ++ phylink_set(mask, 10baseT_Full); ++ phylink_set(mask, 100baseT_Half); ++ phylink_set(mask, 100baseT_Full); ++ } else { ++ phylink_set(mask, Pause); ++ } ++ ++ bitmap_and(support, support, mask, __ETHTOOL_LINK_MODE_MASK_NBITS); ++} ++ ++static int mvneta_mac_link_state(struct net_device *ndev, ++ struct phylink_link_state *state) + { + struct mvneta_port *pp = netdev_priv(ndev); +- struct phy_device *phydev = ndev->phydev; +- int status_change = 0; ++ u32 gmac_stat; ++ ++ gmac_stat = mvreg_read(pp, MVNETA_GMAC_STATUS); ++ ++ if (gmac_stat & MVNETA_GMAC_SPEED_1000) ++ state->speed = SPEED_1000; ++ else if (gmac_stat & MVNETA_GMAC_SPEED_100) ++ state->speed = SPEED_100; ++ else ++ state->speed = SPEED_10; + +- if (phydev->link) { +- if ((pp->speed != phydev->speed) || +- (pp->duplex != phydev->duplex)) { +- u32 val; +- +- val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); +- val &= ~(MVNETA_GMAC_CONFIG_MII_SPEED | +- MVNETA_GMAC_CONFIG_GMII_SPEED | +- MVNETA_GMAC_CONFIG_FULL_DUPLEX); +- +- if (phydev->duplex) +- val |= MVNETA_GMAC_CONFIG_FULL_DUPLEX; +- +- if (phydev->speed == SPEED_1000) +- val |= MVNETA_GMAC_CONFIG_GMII_SPEED; +- else if (phydev->speed == SPEED_100) +- val |= MVNETA_GMAC_CONFIG_MII_SPEED; ++ state->an_complete = !!(gmac_stat & MVNETA_GMAC_AN_COMPLETE); ++ state->link = !!(gmac_stat & MVNETA_GMAC_LINK_UP); ++ state->duplex = !!(gmac_stat & MVNETA_GMAC_FULL_DUPLEX); + +- mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val); ++ return 1; ++} + +- pp->duplex = phydev->duplex; +- pp->speed = phydev->speed; +- } ++static void mvneta_mac_an_restart(struct net_device *ndev, unsigned int mode) ++{ ++ struct mvneta_port *pp = netdev_priv(ndev); ++ ++ if (mode == MLO_AN_8023Z) { ++ u32 gmac_an = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); ++ ++ mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, ++ gmac_an | MVNETA_GMAC_INBAND_RESTART_AN); ++ mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, ++ gmac_an & ~MVNETA_GMAC_INBAND_RESTART_AN); + } ++} + +- if (phydev->link != pp->link) { +- if (!phydev->link) { +- pp->duplex = -1; +- pp->speed = 0; +- } ++static void mvneta_mac_config(struct net_device *ndev, unsigned int mode, ++ const struct phylink_link_state *state) ++{ ++ struct mvneta_port *pp = netdev_priv(ndev); ++ u32 new_ctrl0, gmac_ctrl0 = mvreg_read(pp, MVNETA_GMAC_CTRL_0); ++ u32 new_ctrl2, gmac_ctrl2 = mvreg_read(pp, MVNETA_GMAC_CTRL_2); ++ u32 new_clk, gmac_clk = mvreg_read(pp, MVNETA_GMAC_CLOCK_DIVIDER); ++ u32 new_an, gmac_an = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); ++ ++ new_ctrl0 = gmac_ctrl0 & ~MVNETA_GMAC0_PORT_1000BASE_X; ++ new_ctrl2 = gmac_ctrl2 & ~MVNETA_GMAC2_INBAND_AN_ENABLE; ++ new_clk = gmac_clk & ~MVNETA_GMAC_1MS_CLOCK_ENABLE; ++ new_an = gmac_an & ~(MVNETA_GMAC_INBAND_AN_ENABLE | ++ MVNETA_GMAC_INBAND_RESTART_AN | ++ MVNETA_GMAC_CONFIG_MII_SPEED | ++ MVNETA_GMAC_CONFIG_GMII_SPEED | ++ MVNETA_GMAC_AN_SPEED_EN | ++ MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL | ++ MVNETA_GMAC_CONFIG_FLOW_CTRL | ++ MVNETA_GMAC_AN_FLOW_CTRL_EN | ++ MVNETA_GMAC_CONFIG_FULL_DUPLEX | ++ MVNETA_GMAC_AN_DUPLEX_EN); ++ ++ if (phylink_test(state->advertising, Pause)) ++ new_an |= MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL; ++ ++ switch (mode) { ++ case MLO_AN_SGMII: ++ /* SGMII mode receives the state from the PHY */ ++ new_ctrl2 |= MVNETA_GMAC2_INBAND_AN_ENABLE; ++ new_clk |= MVNETA_GMAC_1MS_CLOCK_ENABLE; ++ new_an = (new_an & ~(MVNETA_GMAC_FORCE_LINK_DOWN | ++ MVNETA_GMAC_FORCE_LINK_PASS)) | ++ MVNETA_GMAC_INBAND_AN_ENABLE | ++ MVNETA_GMAC_AN_SPEED_EN | ++ MVNETA_GMAC_AN_DUPLEX_EN; ++ break; + +- pp->link = phydev->link; +- status_change = 1; ++ case MLO_AN_8023Z: ++ /* 802.3z negotiation - only 1000base-X */ ++ new_ctrl0 |= MVNETA_GMAC0_PORT_1000BASE_X; ++ new_clk |= MVNETA_GMAC_1MS_CLOCK_ENABLE; ++ new_an = (new_an & ~(MVNETA_GMAC_FORCE_LINK_DOWN | ++ MVNETA_GMAC_FORCE_LINK_PASS)) | ++ MVNETA_GMAC_INBAND_AN_ENABLE | ++ MVNETA_GMAC_CONFIG_GMII_SPEED | ++ /* The MAC only supports FD mode */ ++ MVNETA_GMAC_CONFIG_FULL_DUPLEX; ++ ++ if (state->an_enabled) ++ new_an |= MVNETA_GMAC_AN_FLOW_CTRL_EN; ++ break; ++ ++ default: ++ /* Phy or fixed speed */ ++ if (state->duplex) ++ new_an |= MVNETA_GMAC_CONFIG_FULL_DUPLEX; ++ ++ if (state->speed == SPEED_1000) ++ new_an |= MVNETA_GMAC_CONFIG_GMII_SPEED; ++ else if (state->speed == SPEED_100) ++ new_an |= MVNETA_GMAC_CONFIG_MII_SPEED; ++ break; + } + +- if (status_change) { +- if (phydev->link) { +- if (!pp->use_inband_status) { +- u32 val = mvreg_read(pp, +- MVNETA_GMAC_AUTONEG_CONFIG); +- val &= ~MVNETA_GMAC_FORCE_LINK_DOWN; +- val |= MVNETA_GMAC_FORCE_LINK_PASS; +- mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, +- val); +- } +- mvneta_port_up(pp); +- } else { +- if (!pp->use_inband_status) { +- u32 val = mvreg_read(pp, +- MVNETA_GMAC_AUTONEG_CONFIG); +- val &= ~MVNETA_GMAC_FORCE_LINK_PASS; +- val |= MVNETA_GMAC_FORCE_LINK_DOWN; +- mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, +- val); +- } +- mvneta_port_down(pp); +- } +- phy_print_status(phydev); ++ /* Armada 370 documentation says we can only change the port mode ++ * and in-band enable when the link is down, so force it down ++ * while making these changes. We also do this for GMAC_CTRL2 */ ++ if ((new_ctrl0 ^ gmac_ctrl0) & MVNETA_GMAC0_PORT_1000BASE_X || ++ (new_ctrl2 ^ gmac_ctrl2) & MVNETA_GMAC2_INBAND_AN_ENABLE || ++ (new_an ^ gmac_an) & MVNETA_GMAC_INBAND_AN_ENABLE) { ++ mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, ++ (gmac_an & ~MVNETA_GMAC_FORCE_LINK_PASS) | ++ MVNETA_GMAC_FORCE_LINK_DOWN); ++ } ++ ++ if (new_ctrl0 != gmac_ctrl0) ++ mvreg_write(pp, MVNETA_GMAC_CTRL_0, new_ctrl0); ++ if (new_ctrl2 != gmac_ctrl2) ++ mvreg_write(pp, MVNETA_GMAC_CTRL_2, new_ctrl2); ++ if (new_clk != gmac_clk) ++ mvreg_write(pp, MVNETA_GMAC_CLOCK_DIVIDER, new_clk); ++ if (new_an != gmac_an) ++ mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, new_an); ++} ++ ++static void mvneta_mac_link_down(struct net_device *ndev, unsigned int mode) ++{ ++ struct mvneta_port *pp = netdev_priv(ndev); ++ u32 val; ++ ++ mvneta_port_down(pp); ++ ++ if (mode == MLO_AN_PHY || mode == MLO_AN_FIXED) { ++ val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); ++ val &= ~MVNETA_GMAC_FORCE_LINK_PASS; ++ val |= MVNETA_GMAC_FORCE_LINK_DOWN; ++ mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val); + } + } + +-static int mvneta_mdio_probe(struct mvneta_port *pp) ++static void mvneta_mac_link_up(struct net_device *ndev, unsigned int mode) + { +- struct phy_device *phy_dev; ++ struct mvneta_port *pp = netdev_priv(ndev); ++ u32 val; + +- phy_dev = of_phy_connect(pp->dev, pp->phy_node, mvneta_adjust_link, 0, +- pp->phy_interface); +- if (!phy_dev) { +- netdev_err(pp->dev, "could not find the PHY\n"); +- return -ENODEV; ++ if (mode == MLO_AN_PHY || mode == MLO_AN_FIXED) { ++ val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); ++ val &= ~MVNETA_GMAC_FORCE_LINK_DOWN; ++ val |= MVNETA_GMAC_FORCE_LINK_PASS; ++ mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val); + } + +- phy_dev->supported &= PHY_GBIT_FEATURES; +- phy_dev->advertising = phy_dev->supported; ++ mvneta_port_up(pp); ++} + +- pp->link = 0; +- pp->duplex = 0; +- pp->speed = 0; ++static const struct phylink_mac_ops mvneta_phylink_ops = { ++ .validate_support = mvneta_validate_support, ++ .mac_link_state = mvneta_mac_link_state, ++ .mac_an_restart = mvneta_mac_an_restart, ++ .mac_config = mvneta_mac_config, ++ .mac_link_down = mvneta_mac_link_down, ++ .mac_link_up = mvneta_mac_link_up, ++}; + +- return 0; ++static int mvneta_mdio_probe(struct mvneta_port *pp) ++{ ++ int err = phylink_of_phy_connect(pp->phylink, pp->dn); ++ if (err) ++ netdev_err(pp->dev, "could not attach PHY\n"); ++ ++ return err; + } + + static void mvneta_mdio_remove(struct mvneta_port *pp) + { +- struct net_device *ndev = pp->dev; +- +- phy_disconnect(ndev->phydev); ++ phylink_disconnect_phy(pp->phylink); + } + + /* Electing a CPU must be done in an atomic way: it should be done +@@ -3534,10 +3590,9 @@ static int mvneta_stop(struct net_device + + static int mvneta_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) + { +- if (!dev->phydev) +- return -ENOTSUPP; ++ struct mvneta_port *pp = netdev_priv(dev); + +- return phy_mii_ioctl(dev->phydev, ifr, cmd); ++ return phylink_mii_ioctl(pp->phylink, ifr, cmd); + } + + /* Ethtool methods */ +@@ -3548,44 +3603,18 @@ mvneta_ethtool_set_link_ksettings(struct + const struct ethtool_link_ksettings *cmd) + { + struct mvneta_port *pp = netdev_priv(ndev); +- struct phy_device *phydev = ndev->phydev; +- +- if (!phydev) +- return -ENODEV; + +- if ((cmd->base.autoneg == AUTONEG_ENABLE) != pp->use_inband_status) { +- u32 val; +- +- mvneta_set_autoneg(pp, cmd->base.autoneg == AUTONEG_ENABLE); +- +- if (cmd->base.autoneg == AUTONEG_DISABLE) { +- val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); +- val &= ~(MVNETA_GMAC_CONFIG_MII_SPEED | +- MVNETA_GMAC_CONFIG_GMII_SPEED | +- MVNETA_GMAC_CONFIG_FULL_DUPLEX); +- +- if (phydev->duplex) +- val |= MVNETA_GMAC_CONFIG_FULL_DUPLEX; +- +- if (phydev->speed == SPEED_1000) +- val |= MVNETA_GMAC_CONFIG_GMII_SPEED; +- else if (phydev->speed == SPEED_100) +- val |= MVNETA_GMAC_CONFIG_MII_SPEED; +- +- mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val); +- } ++ return phylink_ethtool_ksettings_set(pp->phylink, cmd); ++} + +- pp->use_inband_status = (cmd->base.autoneg == AUTONEG_ENABLE); +- netdev_info(pp->dev, "autoneg status set to %i\n", +- pp->use_inband_status); +- +- if (netif_running(ndev)) { +- mvneta_port_down(pp); +- mvneta_port_up(pp); +- } +- } ++/* Get link ksettings for ethtools */ ++static int ++mvneta_ethtool_get_link_ksettings(struct net_device *ndev, ++ struct ethtool_link_ksettings *cmd) ++{ ++ struct mvneta_port *pp = netdev_priv(ndev); + +- return phy_ethtool_ksettings_set(ndev->phydev, cmd); ++ return phylink_ethtool_ksettings_get(pp->phylink, cmd); + } + + /* Set interrupt coalescing for ethtools */ +@@ -3693,26 +3722,28 @@ static void mvneta_ethtool_update_stats( + { + const struct mvneta_statistic *s; + void __iomem *base = pp->base; +- u32 high, low, val; +- u64 val64; ++ u32 high, low; ++ u64 val; + int i; + + for (i = 0, s = mvneta_statistics; + s < mvneta_statistics + ARRAY_SIZE(mvneta_statistics); + s++, i++) { ++ val = 0; ++ + switch (s->type) { + case T_REG_32: + val = readl_relaxed(base + s->offset); +- pp->ethtool_stats[i] += val; + break; + case T_REG_64: + /* Docs say to read low 32-bit then high */ + low = readl_relaxed(base + s->offset); + high = readl_relaxed(base + s->offset + 4); +- val64 = (u64)high << 32 | low; +- pp->ethtool_stats[i] += val64; ++ val = (u64)high << 32 | low; + break; + } ++ ++ pp->ethtool_stats[i] += val; + } + } + +@@ -3872,7 +3903,7 @@ const struct ethtool_ops mvneta_eth_tool + .get_rxnfc = mvneta_ethtool_get_rxnfc, + .get_rxfh = mvneta_ethtool_get_rxfh, + .set_rxfh = mvneta_ethtool_set_rxfh, +- .get_link_ksettings = phy_ethtool_get_link_ksettings, ++ .get_link_ksettings = mvneta_ethtool_get_link_ksettings, + .set_link_ksettings = mvneta_ethtool_set_link_ksettings, + }; + +@@ -3999,14 +4030,13 @@ static int mvneta_probe(struct platform_ + const struct mbus_dram_target_info *dram_target_info; + struct resource *res; + struct device_node *dn = pdev->dev.of_node; +- struct device_node *phy_node; + struct device_node *bm_node; + struct mvneta_port *pp; + struct net_device *dev; ++ struct phylink *phylink; + const char *dt_mac_addr; + char hw_mac_addr[ETH_ALEN]; + const char *mac_from; +- const char *managed; + int tx_csum_limit; + int phy_mode; + int err; +@@ -4022,31 +4052,11 @@ static int mvneta_probe(struct platform_ + goto err_free_netdev; + } + +- phy_node = of_parse_phandle(dn, "phy", 0); +- if (!phy_node) { +- if (!of_phy_is_fixed_link(dn)) { +- dev_err(&pdev->dev, "no PHY specified\n"); +- err = -ENODEV; +- goto err_free_irq; +- } +- +- err = of_phy_register_fixed_link(dn); +- if (err < 0) { +- dev_err(&pdev->dev, "cannot register fixed PHY\n"); +- goto err_free_irq; +- } +- +- /* In the case of a fixed PHY, the DT node associated +- * to the PHY is the Ethernet MAC DT node. +- */ +- phy_node = of_node_get(dn); +- } +- + phy_mode = of_get_phy_mode(dn); + if (phy_mode < 0) { + dev_err(&pdev->dev, "incorrect phy-mode\n"); + err = -EINVAL; +- goto err_put_phy_node; ++ goto err_free_irq; + } + + dev->tx_queue_len = MVNETA_MAX_TXD; +@@ -4057,12 +4067,7 @@ static int mvneta_probe(struct platform_ + + pp = netdev_priv(dev); + spin_lock_init(&pp->lock); +- pp->phy_node = phy_node; +- pp->phy_interface = phy_mode; +- +- err = of_property_read_string(dn, "managed", &managed); +- pp->use_inband_status = (err == 0 && +- strcmp(managed, "in-band-status") == 0); ++ pp->dn = dn; + + pp->rxq_def = rxq_def; + +@@ -4073,7 +4078,7 @@ static int mvneta_probe(struct platform_ + pp->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(pp->clk)) { + err = PTR_ERR(pp->clk); +- goto err_put_phy_node; ++ goto err_free_irq; + } + + clk_prepare_enable(pp->clk); +@@ -4181,6 +4186,14 @@ static int mvneta_probe(struct platform_ + dev->priv_flags |= IFF_LIVE_ADDR_CHANGE; + dev->gso_max_segs = MVNETA_MAX_TSO_SEGS; + ++ phylink = phylink_create(dev, dn, phy_mode, &mvneta_phylink_ops); ++ if (IS_ERR(phylink)) { ++ err = PTR_ERR(phylink); ++ goto err_free_stats; ++ } ++ ++ pp->phylink = phylink; ++ + err = register_netdev(dev); + if (err < 0) { + dev_err(&pdev->dev, "failed to register\n"); +@@ -4192,14 +4205,6 @@ static int mvneta_probe(struct platform_ + + platform_set_drvdata(pdev, pp->dev); + +- if (pp->use_inband_status) { +- struct phy_device *phy = of_phy_find_device(dn); +- +- mvneta_fixed_link_update(pp, phy); +- +- put_device(&phy->mdio.dev); +- } +- + return 0; + + err_netdev: +@@ -4210,16 +4215,14 @@ err_netdev: + 1 << pp->id); + } + err_free_stats: ++ if (pp->phylink) ++ phylink_destroy(pp->phylink); + free_percpu(pp->stats); + err_free_ports: + free_percpu(pp->ports); + err_clk: + clk_disable_unprepare(pp->clk_bus); + clk_disable_unprepare(pp->clk); +-err_put_phy_node: +- of_node_put(phy_node); +- if (of_phy_is_fixed_link(dn)) +- of_phy_deregister_fixed_link(dn); + err_free_irq: + irq_dispose_mapping(dev->irq); + err_free_netdev: +@@ -4231,7 +4234,6 @@ err_free_netdev: + static int mvneta_remove(struct platform_device *pdev) + { + struct net_device *dev = platform_get_drvdata(pdev); +- struct device_node *dn = pdev->dev.of_node; + struct mvneta_port *pp = netdev_priv(dev); + + unregister_netdev(dev); +@@ -4239,10 +4241,8 @@ static int mvneta_remove(struct platform + clk_disable_unprepare(pp->clk); + free_percpu(pp->ports); + free_percpu(pp->stats); +- if (of_phy_is_fixed_link(dn)) +- of_phy_deregister_fixed_link(dn); + irq_dispose_mapping(dev->irq); +- of_node_put(pp->phy_node); ++ phylink_destroy(pp->phylink); + free_netdev(dev); + + if (pp->bm_priv) { diff --git a/target/linux/mvebu/patches-4.9/420-net-mvneta-disable-MVNETA_CAUSE_PSC_SYNC_CHANGE-inte.patch b/target/linux/mvebu/patches-4.9/420-net-mvneta-disable-MVNETA_CAUSE_PSC_SYNC_CHANGE-inte.patch new file mode 100644 index 0000000000..c0abab57da --- /dev/null +++ b/target/linux/mvebu/patches-4.9/420-net-mvneta-disable-MVNETA_CAUSE_PSC_SYNC_CHANGE-inte.patch @@ -0,0 +1,58 @@ +From: Russell King <rmk+kernel@armlinux.org.uk> +Date: Sat, 24 Dec 2016 10:27:08 +0000 +Subject: [PATCH] net: mvneta: disable MVNETA_CAUSE_PSC_SYNC_CHANGE + interrupt + +The PSC sync change interrupt can fire multiple times while the link is +down. As this isn't information we make use of, it's pointless having +the interrupt enabled, so let's disable this interrupt. + +Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> +--- + +--- a/drivers/net/ethernet/marvell/mvneta.c ++++ b/drivers/net/ethernet/marvell/mvneta.c +@@ -2629,9 +2629,11 @@ static int mvneta_poll(struct napi_struc + mvreg_write(pp, MVNETA_INTR_MISC_CAUSE, 0); + + if (cause_misc & (MVNETA_CAUSE_PHY_STATUS_CHANGE | +- MVNETA_CAUSE_LINK_CHANGE | +- MVNETA_CAUSE_PSC_SYNC_CHANGE)) ++ MVNETA_CAUSE_LINK_CHANGE)) { ++ printk(KERN_DEBUG "%s: cause 0x%08x:0x%08x\n", ++ __func__, cause_rx_tx, cause_misc); + mvneta_link_change(pp); ++ } + } + + /* Release Tx descriptors */ +@@ -2948,8 +2950,7 @@ static void mvneta_start_dev(struct mvne + + mvreg_write(pp, MVNETA_INTR_MISC_MASK, + MVNETA_CAUSE_PHY_STATUS_CHANGE | +- MVNETA_CAUSE_LINK_CHANGE | +- MVNETA_CAUSE_PSC_SYNC_CHANGE); ++ MVNETA_CAUSE_LINK_CHANGE); + + phylink_start(pp->phylink); + netif_tx_start_all_queues(pp->dev); +@@ -3440,8 +3441,7 @@ static int mvneta_cpu_online(unsigned in + on_each_cpu(mvneta_percpu_unmask_interrupt, pp, true); + mvreg_write(pp, MVNETA_INTR_MISC_MASK, + MVNETA_CAUSE_PHY_STATUS_CHANGE | +- MVNETA_CAUSE_LINK_CHANGE | +- MVNETA_CAUSE_PSC_SYNC_CHANGE); ++ MVNETA_CAUSE_LINK_CHANGE); + netif_tx_start_all_queues(pp->dev); + spin_unlock(&pp->lock); + return 0; +@@ -3482,8 +3482,7 @@ static int mvneta_cpu_dead(unsigned int + on_each_cpu(mvneta_percpu_unmask_interrupt, pp, true); + mvreg_write(pp, MVNETA_INTR_MISC_MASK, + MVNETA_CAUSE_PHY_STATUS_CHANGE | +- MVNETA_CAUSE_LINK_CHANGE | +- MVNETA_CAUSE_PSC_SYNC_CHANGE); ++ MVNETA_CAUSE_LINK_CHANGE); + netif_tx_start_all_queues(pp->dev); + return 0; + } diff --git a/target/linux/mvebu/patches-4.9/421-phylink-add-ethtool-nway_reset-support.patch b/target/linux/mvebu/patches-4.9/421-phylink-add-ethtool-nway_reset-support.patch new file mode 100644 index 0000000000..d4436a732e --- /dev/null +++ b/target/linux/mvebu/patches-4.9/421-phylink-add-ethtool-nway_reset-support.patch @@ -0,0 +1,44 @@ +From: Russell King <rmk+kernel@arm.linux.org.uk> +Date: Thu, 1 Oct 2015 20:27:19 +0100 +Subject: [PATCH] phylink: add ethtool nway_reset support + +Add ethtool nway_reset support to phylink, to allow userspace to +request a re-negotiation of the link. + +Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> +Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> +--- + +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -783,6 +783,20 @@ int phylink_ethtool_ksettings_set(struct + } + EXPORT_SYMBOL_GPL(phylink_ethtool_ksettings_set); + ++int phylink_ethtool_nway_reset(struct phylink *pl) ++{ ++ int ret = 0; ++ ++ mutex_lock(&pl->config_mutex); ++ if (pl->phydev) ++ ret = genphy_restart_aneg(pl->phydev); ++ phylink_mac_an_restart(pl); ++ mutex_unlock(&pl->config_mutex); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(phylink_ethtool_nway_reset); ++ + /* This emulates MII registers for a fixed-mode phy operating as per the + * passed in state. "aneg" defines if we report negotiation is possible. + * +--- a/include/linux/phylink.h ++++ b/include/linux/phylink.h +@@ -86,6 +86,7 @@ int phylink_ethtool_ksettings_get(struct + struct ethtool_link_ksettings *); + int phylink_ethtool_ksettings_set(struct phylink *, + const struct ethtool_link_ksettings *); ++int phylink_ethtool_nway_reset(struct phylink *); + int phylink_mii_ioctl(struct phylink *, struct ifreq *, int); + + int phylink_set_link(struct phylink *pl, unsigned int mode, u8 port, diff --git a/target/linux/mvebu/patches-4.9/422-net-mvneta-add-nway_reset-support.patch b/target/linux/mvebu/patches-4.9/422-net-mvneta-add-nway_reset-support.patch new file mode 100644 index 0000000000..c7725b3590 --- /dev/null +++ b/target/linux/mvebu/patches-4.9/422-net-mvneta-add-nway_reset-support.patch @@ -0,0 +1,35 @@ +From: Russell King <rmk+kernel@arm.linux.org.uk> +Date: Thu, 1 Oct 2015 19:40:31 +0100 +Subject: [PATCH] net: mvneta: add nway_reset support + +Add ethtool nway_reset support to mvneta via phylink, so that userspace +can request the link in whatever mode to be renegotiated via +ethtool -r ethX. + +Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> +--- + +--- a/drivers/net/ethernet/marvell/mvneta.c ++++ b/drivers/net/ethernet/marvell/mvneta.c +@@ -3616,6 +3616,13 @@ mvneta_ethtool_get_link_ksettings(struct + return phylink_ethtool_ksettings_get(pp->phylink, cmd); + } + ++static int mvneta_ethtool_nway_reset(struct net_device *dev) ++{ ++ struct mvneta_port *pp = netdev_priv(dev); ++ ++ return phylink_ethtool_nway_reset(pp->phylink); ++} ++ + /* Set interrupt coalescing for ethtools */ + static int mvneta_ethtool_set_coalesce(struct net_device *dev, + struct ethtool_coalesce *c) +@@ -3889,6 +3896,7 @@ static const struct net_device_ops mvnet + }; + + const struct ethtool_ops mvneta_eth_tool_ops = { ++ .nway_reset = mvneta_ethtool_nway_reset, + .get_link = ethtool_op_get_link, + .set_coalesce = mvneta_ethtool_set_coalesce, + .get_coalesce = mvneta_ethtool_get_coalesce, diff --git a/target/linux/mvebu/patches-4.9/423-phylink-add-flow-control-support.patch b/target/linux/mvebu/patches-4.9/423-phylink-add-flow-control-support.patch new file mode 100644 index 0000000000..b0359f19a2 --- /dev/null +++ b/target/linux/mvebu/patches-4.9/423-phylink-add-flow-control-support.patch @@ -0,0 +1,232 @@ +From: Russell King <rmk+kernel@arm.linux.org.uk> +Date: Thu, 1 Oct 2015 20:32:07 +0100 +Subject: [PATCH] phylink: add flow control support + +Add flow control support, including ethtool support, to phylink. We +add support to allow ethtool to get and set the current flow control +settings, and the 802.3 specified resolution for the local and remote +link partner abilities. + +Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> +--- + +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -132,6 +132,9 @@ static int phylink_parse_fixedlink(struc + + if (of_property_read_bool(fixed_node, "full-duplex")) + pl->link_config.duplex = DUPLEX_FULL; ++ ++ /* We treat the "pause" and "asym-pause" terminology as ++ * defining the link partner's ability. */ + if (of_property_read_bool(fixed_node, "pause")) + pl->link_config.pause |= MLO_PAUSE_SYM; + if (of_property_read_bool(fixed_node, "asym-pause")) +@@ -277,6 +280,56 @@ static void phylink_get_fixed_state(stru + state->link = !!gpiod_get_value(pl->link_gpio); + } + ++/* Flow control is resolved according to our and the link partners ++ * advertisments 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 RX ++ * 1 1 0 1 TX ++ */ ++static void phylink_resolve_flow(struct phylink *pl, ++ struct phylink_link_state *state) ++{ ++ int new_pause = 0; ++ ++ if (pl->link_config.pause & MLO_PAUSE_AN) { ++ int pause = 0; ++ ++ 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_RX : MLO_PAUSE_TX; ++ } else { ++ new_pause = pl->link_config.pause & MLO_PAUSE_TXRX_MASK; ++ } ++ ++ state->pause &= ~MLO_PAUSE_TXRX_MASK; ++ state->pause |= new_pause; ++} ++ ++static const char *phylink_pause_to_str(int pause) ++{ ++ switch (pause & MLO_PAUSE_TXRX_MASK) { ++ case MLO_PAUSE_TX | MLO_PAUSE_RX: ++ return "rx/tx"; ++ case MLO_PAUSE_TX: ++ return "tx"; ++ case MLO_PAUSE_RX: ++ return "rx"; ++ default: ++ return "off"; ++ } ++} ++ + static void phylink_resolve(struct work_struct *w) + { + struct phylink *pl = container_of(w, struct phylink, resolve); +@@ -290,6 +343,7 @@ static void phylink_resolve(struct work_ + switch (pl->link_an_mode) { + case MLO_AN_PHY: + link_state = pl->phy_state; ++ phylink_resolve_flow(pl, &link_state); + break; + + case MLO_AN_FIXED: +@@ -298,9 +352,12 @@ static void phylink_resolve(struct work_ + + case MLO_AN_SGMII: + phylink_get_mac_state(pl, &link_state); +- if (pl->phydev) ++ if (pl->phydev) { + link_state.link = link_state.link && + pl->phy_state.link; ++ link_state.pause |= pl->phy_state.pause; ++ phylink_resolve_flow(pl, &link_state); ++ } + break; + + case MLO_AN_8023Z: +@@ -330,7 +387,7 @@ static void phylink_resolve(struct work_ + "Link is Up - %s/%s - flow control %s\n", + phy_speed_to_str(link_state.speed), + phy_duplex_to_str(link_state.duplex), +- link_state.pause ? "rx/tx" : "off"); ++ phylink_pause_to_str(link_state.pause)); + } + } + mutex_unlock(&pl->state_mutex); +@@ -358,6 +415,7 @@ struct phylink *phylink_create(struct ne + pl->netdev = ndev; + pl->link_interface = iface; + pl->link_port = PORT_MII; ++ pl->link_config.pause = MLO_PAUSE_AN; + pl->link_config.speed = SPEED_UNKNOWN; + pl->link_config.duplex = DUPLEX_UNKNOWN; + pl->ops = ops; +@@ -580,6 +638,7 @@ void phylink_start(struct phylink *pl) + * a fixed-link to start with the correct parameters, and also + * ensures that we set the appropriate advertisment for Serdes links. + */ ++ phylink_resolve_flow(pl, &pl->link_config); + phylink_mac_config(pl, &pl->link_config); + + clear_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state); +@@ -797,6 +856,79 @@ int phylink_ethtool_nway_reset(struct ph + } + EXPORT_SYMBOL_GPL(phylink_ethtool_nway_reset); + ++void phylink_ethtool_get_pauseparam(struct phylink *pl, ++ struct ethtool_pauseparam *pause) ++{ ++ mutex_lock(&pl->config_mutex); ++ ++ pause->autoneg = !!(pl->link_config.pause & MLO_PAUSE_AN); ++ pause->rx_pause = !!(pl->link_config.pause & MLO_PAUSE_RX); ++ pause->tx_pause = !!(pl->link_config.pause & MLO_PAUSE_TX); ++ ++ mutex_unlock(&pl->config_mutex); ++} ++EXPORT_SYMBOL_GPL(phylink_ethtool_get_pauseparam); ++ ++static int __phylink_ethtool_set_pauseparam(struct phylink *pl, ++ struct ethtool_pauseparam *pause) ++{ ++ struct phylink_link_state *config = &pl->link_config; ++ ++ if (!phylink_test(pl->supported, Pause) && ++ !phylink_test(pl->supported, Asym_Pause)) ++ return -EOPNOTSUPP; ++ ++ if (!phylink_test(pl->supported, Asym_Pause) && ++ !pause->autoneg && pause->rx_pause != pause->tx_pause) ++ return -EINVAL; ++ ++ config->pause &= ~(MLO_PAUSE_AN | MLO_PAUSE_TXRX_MASK); ++ ++ if (pause->autoneg) ++ config->pause |= MLO_PAUSE_AN; ++ if (pause->rx_pause) ++ config->pause |= MLO_PAUSE_RX; ++ if (pause->tx_pause) ++ config->pause |= MLO_PAUSE_TX; ++ ++ if (!test_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state)) { ++ switch (pl->link_an_mode) { ++ case MLO_AN_PHY: ++ /* Silently mark the carrier down, and then trigger a resolve */ ++ netif_carrier_off(pl->netdev); ++ phylink_run_resolve(pl); ++ break; ++ ++ 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_SGMII: ++ case MLO_AN_8023Z: ++ phylink_mac_config(pl, config); ++ phylink_mac_an_restart(pl); ++ break; ++ } ++ } ++ ++ return 0; ++} ++ ++int phylink_ethtool_set_pauseparam(struct phylink *pl, ++ struct ethtool_pauseparam *pause) ++{ ++ int ret; ++ ++ mutex_lock(&pl->config_mutex); ++ ret = __phylink_ethtool_set_pauseparam(pl, pause); ++ mutex_unlock(&pl->config_mutex); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(phylink_ethtool_set_pauseparam); ++ + /* This emulates MII registers for a fixed-mode phy operating as per the + * passed in state. "aneg" defines if we report negotiation is possible. + * +--- a/include/linux/phylink.h ++++ b/include/linux/phylink.h +@@ -13,6 +13,10 @@ 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_TXRX_MASK = MLO_PAUSE_TX | MLO_PAUSE_RX, ++ MLO_PAUSE_AN = BIT(4), + + MLO_AN_PHY = 0, + MLO_AN_FIXED, +@@ -87,6 +91,10 @@ int phylink_ethtool_ksettings_get(struct + int phylink_ethtool_ksettings_set(struct phylink *, + const struct ethtool_link_ksettings *); + int phylink_ethtool_nway_reset(struct phylink *); ++void phylink_ethtool_get_pauseparam(struct phylink *, ++ struct ethtool_pauseparam *); ++int phylink_ethtool_set_pauseparam(struct phylink *, ++ struct ethtool_pauseparam *); + int phylink_mii_ioctl(struct phylink *, struct ifreq *, int); + + int phylink_set_link(struct phylink *pl, unsigned int mode, u8 port, diff --git a/target/linux/mvebu/patches-4.9/424-net-mvneta-add-flow-control-support-via-phylink.patch b/target/linux/mvebu/patches-4.9/424-net-mvneta-add-flow-control-support-via-phylink.patch new file mode 100644 index 0000000000..b46359ccc8 --- /dev/null +++ b/target/linux/mvebu/patches-4.9/424-net-mvneta-add-flow-control-support-via-phylink.patch @@ -0,0 +1,76 @@ +From: Russell King <rmk+kernel@arm.linux.org.uk> +Date: Thu, 1 Oct 2015 17:41:44 +0100 +Subject: [PATCH] net: mvneta: add flow control support via phylink + +Add flow control support to mvneta, including the ethtool hooks. This +uses the phylink code to calculate the result of autonegotiation where +a phy is attached, and to handle the ethtool settings. + +Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> +--- + +--- a/drivers/net/ethernet/marvell/mvneta.c ++++ b/drivers/net/ethernet/marvell/mvneta.c +@@ -3180,6 +3180,12 @@ static int mvneta_mac_link_state(struct + state->link = !!(gmac_stat & MVNETA_GMAC_LINK_UP); + state->duplex = !!(gmac_stat & MVNETA_GMAC_FULL_DUPLEX); + ++ state->pause = 0; ++ if (gmac_stat & MVNETA_GMAC_RX_FLOW_CTRL_ENABLE) ++ state->pause |= MLO_PAUSE_RX; ++ if (gmac_stat & MVNETA_GMAC_TX_FLOW_CTRL_ENABLE) ++ state->pause |= MLO_PAUSE_TX; ++ + return 1; + } + +@@ -3222,6 +3228,8 @@ static void mvneta_mac_config(struct net + + if (phylink_test(state->advertising, Pause)) + new_an |= MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL; ++ if (state->pause & MLO_PAUSE_TXRX_MASK) ++ new_an |= MVNETA_GMAC_CONFIG_FLOW_CTRL; + + switch (mode) { + case MLO_AN_SGMII: +@@ -3246,7 +3254,7 @@ static void mvneta_mac_config(struct net + /* The MAC only supports FD mode */ + MVNETA_GMAC_CONFIG_FULL_DUPLEX; + +- if (state->an_enabled) ++ if (state->pause & MLO_PAUSE_AN && state->an_enabled) + new_an |= MVNETA_GMAC_AN_FLOW_CTRL_EN; + break; + +@@ -3712,6 +3720,22 @@ static int mvneta_ethtool_set_ringparam( + return 0; + } + ++static void mvneta_ethtool_get_pauseparam(struct net_device *dev, ++ struct ethtool_pauseparam *pause) ++{ ++ struct mvneta_port *pp = netdev_priv(dev); ++ ++ phylink_ethtool_get_pauseparam(pp->phylink, pause); ++} ++ ++static int mvneta_ethtool_set_pauseparam(struct net_device *dev, ++ struct ethtool_pauseparam *pause) ++{ ++ struct mvneta_port *pp = netdev_priv(dev); ++ ++ return phylink_ethtool_set_pauseparam(pp->phylink, pause); ++} ++ + static void mvneta_ethtool_get_strings(struct net_device *netdev, u32 sset, + u8 *data) + { +@@ -3903,6 +3927,8 @@ const struct ethtool_ops mvneta_eth_tool + .get_drvinfo = mvneta_ethtool_get_drvinfo, + .get_ringparam = mvneta_ethtool_get_ringparam, + .set_ringparam = mvneta_ethtool_set_ringparam, ++ .get_pauseparam = mvneta_ethtool_get_pauseparam, ++ .set_pauseparam = mvneta_ethtool_set_pauseparam, + .get_strings = mvneta_ethtool_get_strings, + .get_ethtool_stats = mvneta_ethtool_get_stats, + .get_sset_count = mvneta_ethtool_get_sset_count, diff --git a/target/linux/mvebu/patches-4.9/425-net-mvneta-enable-flow-control-for-PHY-connections.patch b/target/linux/mvebu/patches-4.9/425-net-mvneta-enable-flow-control-for-PHY-connections.patch new file mode 100644 index 0000000000..a4ba988198 --- /dev/null +++ b/target/linux/mvebu/patches-4.9/425-net-mvneta-enable-flow-control-for-PHY-connections.patch @@ -0,0 +1,28 @@ +From: Russell King <rmk+kernel@arm.linux.org.uk> +Date: Thu, 1 Oct 2015 00:34:08 +0100 +Subject: [PATCH] net: mvneta: enable flow control for PHY connections + +Enable flow control support for PHY connections by indicating our +support via the ethtool capabilities. phylink takes care of the +appropriate handling. + +Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> +Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> +--- + +--- a/drivers/net/ethernet/marvell/mvneta.c ++++ b/drivers/net/ethernet/marvell/mvneta.c +@@ -3154,10 +3154,11 @@ static void mvneta_validate_support(stru + phylink_set(mask, 10baseT_Full); + phylink_set(mask, 100baseT_Half); + phylink_set(mask, 100baseT_Full); +- } else { +- phylink_set(mask, Pause); + } + ++ if (mode != MLO_AN_FIXED) ++ phylink_set(mask, Pause); ++ + bitmap_and(support, support, mask, __ETHTOOL_LINK_MODE_MASK_NBITS); + } + diff --git a/target/linux/mvebu/patches-4.9/426-net-mvneta-enable-flow-control-for-fixed-connections.patch b/target/linux/mvebu/patches-4.9/426-net-mvneta-enable-flow-control-for-fixed-connections.patch new file mode 100644 index 0000000000..9c1d36328a --- /dev/null +++ b/target/linux/mvebu/patches-4.9/426-net-mvneta-enable-flow-control-for-fixed-connections.patch @@ -0,0 +1,32 @@ +From: Russell King <rmk+kernel@armlinux.org.uk> +Date: Tue, 12 Jul 2016 00:04:13 +0100 +Subject: [PATCH] net: mvneta: enable flow control for fixed connections + +Allow symetric flow control to be enabled for fixed link connections as +well as other types of connections by setting the supported and +advertised capability bits. + +Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> +--- + +--- a/drivers/net/ethernet/marvell/mvneta.c ++++ b/drivers/net/ethernet/marvell/mvneta.c +@@ -3144,6 +3144,8 @@ static void mvneta_validate_support(stru + phylink_set(mask, BNC); + phylink_set(mask, Backplane); + ++ /* Asymmetric pause is unsupported */ ++ phylink_set(mask, Pause); + /* Half-duplex at speeds higher than 100Mbit is unsupported */ + phylink_set(mask, 1000baseT_Full); + phylink_set(mask, 1000baseX_Full); +@@ -3156,9 +3158,6 @@ static void mvneta_validate_support(stru + phylink_set(mask, 100baseT_Full); + } + +- if (mode != MLO_AN_FIXED) +- phylink_set(mask, Pause); +- + bitmap_and(support, support, mask, __ETHTOOL_LINK_MODE_MASK_NBITS); + } + diff --git a/target/linux/mvebu/patches-4.9/427-phylink-add-EEE-support.patch b/target/linux/mvebu/patches-4.9/427-phylink-add-EEE-support.patch new file mode 100644 index 0000000000..27bae047b9 --- /dev/null +++ b/target/linux/mvebu/patches-4.9/427-phylink-add-EEE-support.patch @@ -0,0 +1,116 @@ +From: Russell King <rmk+kernel@arm.linux.org.uk> +Date: Thu, 1 Oct 2015 21:19:53 +0100 +Subject: [PATCH] phylink: add EEE support + +Add EEE hooks to phylink to allow the phylib EEE functions for the +connected phy to be safely accessed. + +Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> +--- + +--- a/drivers/net/ethernet/marvell/mvneta.c ++++ b/drivers/net/ethernet/marvell/mvneta.c +@@ -3306,7 +3306,8 @@ static void mvneta_mac_link_down(struct + } + } + +-static void mvneta_mac_link_up(struct net_device *ndev, unsigned int mode) ++static void mvneta_mac_link_up(struct net_device *ndev, unsigned int mode, ++ struct phy_device *phy) + { + struct mvneta_port *pp = netdev_priv(ndev); + u32 val; +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -379,7 +379,8 @@ static void phylink_resolve(struct work_ + if (pl->phydev) + phylink_mac_config(pl, &link_state); + +- pl->ops->mac_link_up(ndev, pl->link_an_mode); ++ pl->ops->mac_link_up(ndev, pl->link_an_mode, ++ pl->phydev); + + netif_carrier_on(ndev); + +@@ -929,6 +930,58 @@ int phylink_ethtool_set_pauseparam(struc + } + EXPORT_SYMBOL_GPL(phylink_ethtool_set_pauseparam); + ++int phylink_init_eee(struct phylink *pl, bool clk_stop_enable) ++{ ++ int ret = -EPROTONOSUPPORT; ++ ++ mutex_lock(&pl->config_mutex); ++ if (pl->phydev) ++ ret = phy_init_eee(pl->phydev, clk_stop_enable); ++ mutex_unlock(&pl->config_mutex); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(phylink_init_eee); ++ ++int phylink_get_eee_err(struct phylink *pl) ++{ ++ int ret = 0; ++ ++ mutex_lock(&pl->config_mutex); ++ if (pl->phydev) ++ ret = phy_get_eee_err(pl->phydev); ++ mutex_unlock(&pl->config_mutex); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(phylink_get_eee_err); ++ ++int phylink_ethtool_get_eee(struct phylink *pl, struct ethtool_eee *eee) ++{ ++ int ret = -EOPNOTSUPP; ++ ++ mutex_lock(&pl->config_mutex); ++ if (pl->phydev) ++ ret = phy_ethtool_get_eee(pl->phydev, eee); ++ mutex_unlock(&pl->config_mutex); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(phylink_ethtool_get_eee); ++ ++int phylink_ethtool_set_eee(struct phylink *pl, struct ethtool_eee *eee) ++{ ++ int ret = -EOPNOTSUPP; ++ ++ mutex_lock(&pl->config_mutex); ++ if (pl->phydev) ++ ret = phy_ethtool_set_eee(pl->phydev, eee); ++ mutex_unlock(&pl->config_mutex); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(phylink_ethtool_set_eee); ++ + /* This emulates MII registers for a fixed-mode phy operating as per the + * passed in state. "aneg" defines if we report negotiation is possible. + * +--- a/include/linux/phylink.h ++++ b/include/linux/phylink.h +@@ -70,7 +70,8 @@ struct phylink_mac_ops { + void (*mac_an_restart)(struct net_device *, unsigned int mode); + + void (*mac_link_down)(struct net_device *, unsigned int mode); +- void (*mac_link_up)(struct net_device *, unsigned int mode); ++ void (*mac_link_up)(struct net_device *, unsigned int mode, ++ struct phy_device *); + }; + + struct phylink *phylink_create(struct net_device *, struct device_node *, +@@ -95,6 +96,10 @@ void phylink_ethtool_get_pauseparam(stru + struct ethtool_pauseparam *); + int phylink_ethtool_set_pauseparam(struct phylink *, + struct ethtool_pauseparam *); ++int phylink_init_eee(struct phylink *, bool); ++int phylink_get_eee_err(struct phylink *); ++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_set_link(struct phylink *pl, unsigned int mode, u8 port, diff --git a/target/linux/mvebu/patches-4.9/428-net-mvneta-add-EEE-support.patch b/target/linux/mvebu/patches-4.9/428-net-mvneta-add-EEE-support.patch new file mode 100644 index 0000000000..a8bafd9dd0 --- /dev/null +++ b/target/linux/mvebu/patches-4.9/428-net-mvneta-add-EEE-support.patch @@ -0,0 +1,179 @@ +From: Russell King <rmk+kernel@arm.linux.org.uk> +Date: Tue, 29 Sep 2015 15:17:39 +0100 +Subject: [PATCH] net: mvneta: add EEE support + +Add EEE support to mvneta. This allows us to enable the low power idle +support at MAC level if there is a PHY attached through phylink which +supports LPI. The appropriate ethtool support is provided to allow the +feature to be controlled, including ethtool statistics for EEE wakeup +errors. + +Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> +--- + +--- a/drivers/net/ethernet/marvell/mvneta.c ++++ b/drivers/net/ethernet/marvell/mvneta.c +@@ -243,6 +243,12 @@ + #define MVNETA_TXQ_TOKEN_SIZE_REG(q) (0x3e40 + ((q) << 2)) + #define MVNETA_TXQ_TOKEN_SIZE_MAX 0x7fffffff + ++#define MVNETA_LPI_CTRL_0 0x2cc0 ++#define MVNETA_LPI_CTRL_1 0x2cc4 ++#define MVNETA_LPI_REQUEST_ENABLE BIT(0) ++#define MVNETA_LPI_CTRL_2 0x2cc8 ++#define MVNETA_LPI_STATUS 0x2ccc ++ + #define MVNETA_CAUSE_TXQ_SENT_DESC_ALL_MASK 0xff + + /* Descriptor ring Macros */ +@@ -316,6 +322,11 @@ + #define MVNETA_RX_GET_BM_POOL_ID(rxd) \ + (((rxd)->status & MVNETA_RXD_BM_POOL_MASK) >> MVNETA_RXD_BM_POOL_SHIFT) + ++enum { ++ ETHTOOL_STAT_EEE_WAKEUP, ++ ETHTOOL_MAX_STATS, ++}; ++ + struct mvneta_statistic { + unsigned short offset; + unsigned short type; +@@ -324,6 +335,7 @@ struct mvneta_statistic { + + #define T_REG_32 32 + #define T_REG_64 64 ++#define T_SW 1 + + static const struct mvneta_statistic mvneta_statistics[] = { + { 0x3000, T_REG_64, "good_octets_received", }, +@@ -358,6 +370,7 @@ static const struct mvneta_statistic mvn + { 0x304c, T_REG_32, "broadcast_frames_sent", }, + { 0x3054, T_REG_32, "fc_sent", }, + { 0x300c, T_REG_32, "internal_mac_transmit_err", }, ++ { ETHTOOL_STAT_EEE_WAKEUP, T_SW, "eee_wakeup_errors", }, + }; + + struct mvneta_pcpu_stats { +@@ -416,6 +429,10 @@ struct mvneta_port { + struct mvneta_bm_pool *pool_short; + int bm_win_id; + ++ bool eee_enabled; ++ bool eee_active; ++ bool tx_lpi_enabled; ++ + u64 ethtool_stats[ARRAY_SIZE(mvneta_statistics)]; + + u32 indir[MVNETA_RSS_LU_TABLE_SIZE]; +@@ -3291,6 +3308,18 @@ static void mvneta_mac_config(struct net + mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, new_an); + } + ++static void mvneta_set_eee(struct mvneta_port *pp, bool enable) ++{ ++ u32 lpi_ctl1; ++ ++ lpi_ctl1 = mvreg_read(pp, MVNETA_LPI_CTRL_1); ++ if (enable) ++ lpi_ctl1 |= MVNETA_LPI_REQUEST_ENABLE; ++ else ++ lpi_ctl1 &= ~MVNETA_LPI_REQUEST_ENABLE; ++ mvreg_write(pp, MVNETA_LPI_CTRL_1, lpi_ctl1); ++} ++ + static void mvneta_mac_link_down(struct net_device *ndev, unsigned int mode) + { + struct mvneta_port *pp = netdev_priv(ndev); +@@ -3304,6 +3333,9 @@ static void mvneta_mac_link_down(struct + val |= MVNETA_GMAC_FORCE_LINK_DOWN; + mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val); + } ++ ++ pp->eee_active = false; ++ mvneta_set_eee(pp, false); + } + + static void mvneta_mac_link_up(struct net_device *ndev, unsigned int mode, +@@ -3320,6 +3352,11 @@ static void mvneta_mac_link_up(struct ne + } + + mvneta_port_up(pp); ++ ++ if (phy && pp->eee_enabled) { ++ pp->eee_active = phy_init_eee(phy, 0) >= 0; ++ mvneta_set_eee(pp, pp->eee_active && pp->tx_lpi_enabled); ++ } + } + + static const struct phylink_mac_ops mvneta_phylink_ops = { +@@ -3772,6 +3809,13 @@ static void mvneta_ethtool_update_stats( + high = readl_relaxed(base + s->offset + 4); + val = (u64)high << 32 | low; + break; ++ case T_SW: ++ switch (s->offset) { ++ case ETHTOOL_STAT_EEE_WAKEUP: ++ val = phylink_get_eee_err(pp->phylink); ++ break; ++ } ++ break; + } + + pp->ethtool_stats[i] += val; +@@ -3907,6 +3951,47 @@ static u16 mvneta_select_queue(struct ne + } + + ++static int mvneta_ethtool_get_eee(struct net_device *dev, ++ struct ethtool_eee *eee) ++{ ++ struct mvneta_port *pp = netdev_priv(dev); ++ u32 lpi_ctl0; ++ ++ lpi_ctl0 = mvreg_read(pp, MVNETA_LPI_CTRL_0); ++ ++ eee->eee_enabled = pp->eee_enabled; ++ eee->eee_active = pp->eee_active; ++ eee->tx_lpi_enabled = pp->tx_lpi_enabled; ++ eee->tx_lpi_timer = (lpi_ctl0) >> 8; // * scale; ++ ++ return phylink_ethtool_get_eee(pp->phylink, eee); ++} ++ ++static int mvneta_ethtool_set_eee(struct net_device *dev, ++ struct ethtool_eee *eee) ++{ ++ struct mvneta_port *pp = netdev_priv(dev); ++ u32 lpi_ctl0; ++ ++ /* The Armada 37x documents do not give limits for this other than ++ * it being an 8-bit register. */ ++ if (eee->tx_lpi_enabled && ++ (eee->tx_lpi_timer < 0 || eee->tx_lpi_timer > 255)) ++ return -EINVAL; ++ ++ lpi_ctl0 = mvreg_read(pp, MVNETA_LPI_CTRL_0); ++ lpi_ctl0 &= ~(0xff << 8); ++ lpi_ctl0 |= eee->tx_lpi_timer << 8; ++ mvreg_write(pp, MVNETA_LPI_CTRL_0, lpi_ctl0); ++ ++ pp->eee_enabled = eee->eee_enabled; ++ pp->tx_lpi_enabled = eee->tx_lpi_enabled; ++ ++ mvneta_set_eee(pp, eee->tx_lpi_enabled && eee->eee_enabled); ++ ++ return phylink_ethtool_set_eee(pp->phylink, eee); ++} ++ + static const struct net_device_ops mvneta_netdev_ops = { + .ndo_open = mvneta_open, + .ndo_stop = mvneta_stop, +@@ -3939,6 +4024,8 @@ const struct ethtool_ops mvneta_eth_tool + .set_rxfh = mvneta_ethtool_set_rxfh, + .get_link_ksettings = mvneta_ethtool_get_link_ksettings, + .set_link_ksettings = mvneta_ethtool_set_link_ksettings, ++ .get_eee = mvneta_ethtool_get_eee, ++ .set_eee = mvneta_ethtool_set_eee, + }; + + /* Initialize hw */ diff --git a/target/linux/mvebu/patches-4.9/429-phylink-add-module-EEPROM-support.patch b/target/linux/mvebu/patches-4.9/429-phylink-add-module-EEPROM-support.patch new file mode 100644 index 0000000000..88853ca4a4 --- /dev/null +++ b/target/linux/mvebu/patches-4.9/429-phylink-add-module-EEPROM-support.patch @@ -0,0 +1,123 @@ +From: Russell King <rmk+kernel@arm.linux.org.uk> +Date: Thu, 1 Oct 2015 23:10:05 +0100 +Subject: [PATCH] phylink: add module EEPROM support + +Add support for reading module EEPROMs through phylink. + +Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> +Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> +--- + +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -930,6 +930,36 @@ int phylink_ethtool_set_pauseparam(struc + } + EXPORT_SYMBOL_GPL(phylink_ethtool_set_pauseparam); + ++int phylink_ethtool_get_module_info(struct phylink *pl, ++ struct ethtool_modinfo *modinfo) ++{ ++ int ret = -EOPNOTSUPP; ++ ++ mutex_lock(&pl->config_mutex); ++ if (pl->module_ops) ++ ret = pl->module_ops->get_module_info(pl->module_data, ++ modinfo); ++ mutex_unlock(&pl->config_mutex); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(phylink_ethtool_get_module_info); ++ ++int phylink_ethtool_get_module_eeprom(struct phylink *pl, ++ struct ethtool_eeprom *ee, u8 *buf) ++{ ++ int ret = -EOPNOTSUPP; ++ ++ mutex_lock(&pl->config_mutex); ++ if (pl->module_ops) ++ ret = pl->module_ops->get_module_eeprom(pl->module_data, ee, ++ buf); ++ mutex_unlock(&pl->config_mutex); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(phylink_ethtool_get_module_eeprom); ++ + int phylink_init_eee(struct phylink *pl, bool clk_stop_enable) + { + int ret = -EPROTONOSUPPORT; +@@ -1115,6 +1145,39 @@ EXPORT_SYMBOL_GPL(phylink_mii_ioctl); + + + ++int phylink_register_module(struct phylink *pl, void *data, ++ const struct phylink_module_ops *ops) ++{ ++ int ret = -EBUSY; ++ ++ mutex_lock(&pl->config_mutex); ++ if (!pl->module_ops) { ++ pl->module_ops = ops; ++ pl->module_data = data; ++ ret = 0; ++ } ++ mutex_unlock(&pl->config_mutex); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(phylink_register_module); ++ ++int phylink_unregister_module(struct phylink *pl, void *data) ++{ ++ int ret = -EINVAL; ++ ++ mutex_lock(&pl->config_mutex); ++ if (pl->module_data == data) { ++ pl->module_ops = NULL; ++ pl->module_data = NULL; ++ ret = 0; ++ } ++ mutex_unlock(&pl->config_mutex); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(phylink_unregister_module); ++ + void phylink_disable(struct phylink *pl) + { + set_bit(PHYLINK_DISABLE_LINK, &pl->phylink_disable_state); +--- a/include/linux/phylink.h ++++ b/include/linux/phylink.h +@@ -74,6 +74,11 @@ struct phylink_mac_ops { + struct phy_device *); + }; + ++struct phylink_module_ops { ++ int (*get_module_info)(void *, struct ethtool_modinfo *); ++ int (*get_module_eeprom)(void *, struct ethtool_eeprom *, u8 *); ++}; ++ + struct phylink *phylink_create(struct net_device *, struct device_node *, + phy_interface_t iface, const struct phylink_mac_ops *ops); + void phylink_destroy(struct phylink *); +@@ -96,12 +101,19 @@ void phylink_ethtool_get_pauseparam(stru + struct ethtool_pauseparam *); + int phylink_ethtool_set_pauseparam(struct phylink *, + struct ethtool_pauseparam *); ++int phylink_ethtool_get_module_info(struct phylink *, struct ethtool_modinfo *); ++int phylink_ethtool_get_module_eeprom(struct phylink *, ++ struct ethtool_eeprom *, u8 *); + int phylink_init_eee(struct phylink *, bool); + int phylink_get_eee_err(struct phylink *); + 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_register_module(struct phylink *, void *, ++ const struct phylink_module_ops *); ++int phylink_unregister_module(struct phylink *, void *); ++ + int phylink_set_link(struct phylink *pl, unsigned int mode, u8 port, + const unsigned long *support); + void phylink_disable(struct phylink *pl); diff --git a/target/linux/mvebu/patches-4.9/430-net-mvneta-add-module-EEPROM-reading-support.patch b/target/linux/mvebu/patches-4.9/430-net-mvneta-add-module-EEPROM-reading-support.patch new file mode 100644 index 0000000000..a73aa8502c --- /dev/null +++ b/target/linux/mvebu/patches-4.9/430-net-mvneta-add-module-EEPROM-reading-support.patch @@ -0,0 +1,41 @@ +From: Russell King <rmk+kernel@arm.linux.org.uk> +Date: Thu, 1 Oct 2015 23:32:39 +0100 +Subject: [PATCH] net: mvneta: add module EEPROM reading support + +Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> +--- + +--- a/drivers/net/ethernet/marvell/mvneta.c ++++ b/drivers/net/ethernet/marvell/mvneta.c +@@ -3951,6 +3951,22 @@ static u16 mvneta_select_queue(struct ne + } + + ++static int mvneta_ethtool_get_module_info(struct net_device *dev, ++ struct ethtool_modinfo *modinfo) ++{ ++ struct mvneta_port *pp = netdev_priv(dev); ++ ++ return phylink_ethtool_get_module_info(pp->phylink, modinfo); ++} ++ ++static int mvneta_ethtool_get_module_eeprom(struct net_device *dev, ++ struct ethtool_eeprom *ee, u8 *buf) ++{ ++ struct mvneta_port *pp = netdev_priv(dev); ++ ++ return phylink_ethtool_get_module_eeprom(pp->phylink, ee, buf); ++} ++ + static int mvneta_ethtool_get_eee(struct net_device *dev, + struct ethtool_eee *eee) + { +@@ -4024,6 +4040,8 @@ const struct ethtool_ops mvneta_eth_tool + .set_rxfh = mvneta_ethtool_set_rxfh, + .get_link_ksettings = mvneta_ethtool_get_link_ksettings, + .set_link_ksettings = mvneta_ethtool_set_link_ksettings, ++ .get_module_info = mvneta_ethtool_get_module_info, ++ .get_module_eeprom = mvneta_ethtool_get_module_eeprom, + .get_eee = mvneta_ethtool_get_eee, + .set_eee = mvneta_ethtool_set_eee, + }; diff --git a/target/linux/mvebu/patches-4.9/431-sfp-phylink-hook-up-eeprom-functions.patch b/target/linux/mvebu/patches-4.9/431-sfp-phylink-hook-up-eeprom-functions.patch new file mode 100644 index 0000000000..9f049aaa44 --- /dev/null +++ b/target/linux/mvebu/patches-4.9/431-sfp-phylink-hook-up-eeprom-functions.patch @@ -0,0 +1,65 @@ +From: Russell King <rmk+kernel@arm.linux.org.uk> +Date: Thu, 8 Oct 2015 23:49:47 +0100 +Subject: [PATCH] sfp/phylink: hook up eeprom functions + +Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> +--- + +--- a/drivers/net/phy/sfp.c ++++ b/drivers/net/phy/sfp.c +@@ -985,11 +985,9 @@ static void sfp_sm_event(struct sfp *sfp + mutex_unlock(&sfp->sm_mutex); + } + +-#if 0 +-static int sfp_phy_module_info(struct phy_device *phy, +- struct ethtool_modinfo *modinfo) ++static int sfp_module_info(void *priv, struct ethtool_modinfo *modinfo) + { +- struct sfp *sfp = phy->priv; ++ struct sfp *sfp = priv; + + /* locking... and check module is present */ + +@@ -1003,10 +1001,9 @@ static int sfp_phy_module_info(struct ph + return 0; + } + +-static int sfp_phy_module_eeprom(struct phy_device *phy, +- struct ethtool_eeprom *ee, u8 *data) ++static int sfp_module_eeprom(void *priv, struct ethtool_eeprom *ee, u8 *data) + { +- struct sfp *sfp = phy->priv; ++ struct sfp *sfp = priv; + unsigned int first, last, len; + int ret; + +@@ -1037,7 +1034,11 @@ static int sfp_phy_module_eeprom(struct + } + return 0; + } +-#endif ++ ++static const struct phylink_module_ops sfp_module_ops = { ++ .get_module_info = sfp_module_info, ++ .get_module_eeprom = sfp_module_eeprom, ++}; + + static void sfp_timeout(struct work_struct *work) + { +@@ -1113,6 +1114,7 @@ static int sfp_netdev_notify(struct noti + case NETDEV_UNREGISTER: + if (sfp->mod_phy && sfp->phylink) + phylink_disconnect_phy(sfp->phylink); ++ phylink_unregister_module(sfp->phylink, sfp); + sfp->phylink = NULL; + dev_put(sfp->ndev); + sfp->ndev = NULL; +@@ -1230,6 +1232,7 @@ static int sfp_probe(struct platform_dev + } + + phylink_disable(sfp->phylink); ++ phylink_register_module(sfp->phylink, sfp, &sfp_module_ops); + } + + sfp->state = sfp_get_state(sfp); diff --git a/target/linux/mvebu/patches-4.9/432-phy-marvell-88E1512-add-flow-control-support.patch b/target/linux/mvebu/patches-4.9/432-phy-marvell-88E1512-add-flow-control-support.patch new file mode 100644 index 0000000000..749d1680ef --- /dev/null +++ b/target/linux/mvebu/patches-4.9/432-phy-marvell-88E1512-add-flow-control-support.patch @@ -0,0 +1,24 @@ +From: Russell King <rmk+kernel@arm.linux.org.uk> +Date: Thu, 1 Oct 2015 00:34:08 +0100 +Subject: [PATCH] phy: marvell: 88E1512: add flow control support + +The Marvell PHYs support pause frame advertisments, so we should not be +masking their support off. Add the necessary flag to the Marvell PHY +to allow any MAC level pause frame support to be advertised. + +Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> +Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> +--- + +--- a/drivers/net/phy/marvell.c ++++ b/drivers/net/phy/marvell.c +@@ -1671,7 +1671,8 @@ static struct phy_driver marvell_drivers + .phy_id = MARVELL_PHY_ID_88E1510, + .phy_id_mask = MARVELL_PHY_ID_MASK, + .name = "Marvell 88E1510", +- .features = PHY_GBIT_FEATURES | SUPPORTED_FIBRE, ++ .features = PHY_GBIT_FEATURES | SUPPORTED_FIBRE | ++ SUPPORTED_Pause, + .flags = PHY_HAS_INTERRUPT, + .probe = marvell_probe, + .config_init = &m88e1510_config_init, diff --git a/target/linux/mvebu/patches-4.9/433-phy-marvell-88E1111-add-flow-control-support.patch b/target/linux/mvebu/patches-4.9/433-phy-marvell-88E1111-add-flow-control-support.patch new file mode 100644 index 0000000000..1b8bace560 --- /dev/null +++ b/target/linux/mvebu/patches-4.9/433-phy-marvell-88E1111-add-flow-control-support.patch @@ -0,0 +1,22 @@ +From: Russell King <rmk+kernel@armlinux.org.uk> +Date: Tue, 12 Jul 2016 16:45:43 +0100 +Subject: [PATCH] phy: marvell: 88E1111: add flow control support + +The Marvell PHYs support pause frame advertisments, so we should not be +masking their support off. Add the necessary flag to the Marvell PHY +to allow any MAC level pause frame support to be advertised. + +Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> +--- + +--- a/drivers/net/phy/marvell.c ++++ b/drivers/net/phy/marvell.c +@@ -1523,7 +1523,7 @@ static struct phy_driver marvell_drivers + .phy_id = MARVELL_PHY_ID_88E1111, + .phy_id_mask = MARVELL_PHY_ID_MASK, + .name = "Marvell 88E1111", +- .features = PHY_GBIT_FEATURES, ++ .features = PHY_GBIT_FEATURES | SUPPORTED_Pause, + .flags = PHY_HAS_INTERRUPT, + .probe = marvell_probe, + .config_init = &m88e1111_config_init, diff --git a/target/linux/mvebu/patches-4.9/434-phy-marvell-88E1540-add-flow-control-support.patch b/target/linux/mvebu/patches-4.9/434-phy-marvell-88E1540-add-flow-control-support.patch new file mode 100644 index 0000000000..95a4f5773e --- /dev/null +++ b/target/linux/mvebu/patches-4.9/434-phy-marvell-88E1540-add-flow-control-support.patch @@ -0,0 +1,22 @@ +From: Russell King <rmk+kernel@armlinux.org.uk> +Date: Tue, 12 Jul 2016 16:45:43 +0100 +Subject: [PATCH] phy: marvell: 88E1540: add flow control support + +The Marvell PHYs support pause frame advertisments, so we should not be +masking their support off. Add the necessary flag to the Marvell PHY +to allow any MAC level pause frame support to be advertised. + +Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> +--- + +--- a/drivers/net/phy/marvell.c ++++ b/drivers/net/phy/marvell.c +@@ -1691,7 +1691,7 @@ static struct phy_driver marvell_drivers + .phy_id = MARVELL_PHY_ID_88E1540, + .phy_id_mask = MARVELL_PHY_ID_MASK, + .name = "Marvell 88E1540", +- .features = PHY_GBIT_FEATURES, ++ .features = PHY_GBIT_FEATURES | SUPPORTED_Pause, + .flags = PHY_HAS_INTERRUPT, + .probe = marvell_probe, + .config_init = &marvell_config_init, diff --git a/target/linux/mvebu/patches-4.9/435-net-phy-fix-marvell-phy-status-reading.patch b/target/linux/mvebu/patches-4.9/435-net-phy-fix-marvell-phy-status-reading.patch new file mode 100644 index 0000000000..f0fe26e3d0 --- /dev/null +++ b/target/linux/mvebu/patches-4.9/435-net-phy-fix-marvell-phy-status-reading.patch @@ -0,0 +1,26 @@ +From: Russell King <rmk+kernel@armlinux.org.uk> +Date: Fri, 23 Dec 2016 18:54:01 +0000 +Subject: [PATCH] net: phy: fix marvell phy status reading + +The Marvell driver incorrectly provides phydev->lp_advertising as the +logical and of the link partner's advert and our advert. This is +incorrect - this field is supposed to store the link parter's unmodified +advertisment. + +This allows ethtool to report the correct link partner auto-negotiation +status. + +Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> +--- + +--- a/drivers/net/phy/marvell.c ++++ b/drivers/net/phy/marvell.c +@@ -1106,8 +1106,6 @@ static int marvell_read_status_page(stru + if (adv < 0) + return adv; + +- lpa &= adv; +- + if (status & MII_M1011_PHY_STATUS_FULLDUPLEX) + phydev->duplex = DUPLEX_FULL; + else diff --git a/target/linux/mvebu/patches-4.9/436-phylink-propagate-PHY-interface-mode-to-MAC-driver.patch b/target/linux/mvebu/patches-4.9/436-phylink-propagate-PHY-interface-mode-to-MAC-driver.patch new file mode 100644 index 0000000000..9b872b82d7 --- /dev/null +++ b/target/linux/mvebu/patches-4.9/436-phylink-propagate-PHY-interface-mode-to-MAC-driver.patch @@ -0,0 +1,126 @@ +From: Russell King <rmk+kernel@armlinux.org.uk> +Date: Tue, 3 Jan 2017 18:34:17 +0000 +Subject: [PATCH] phylink: propagate PHY interface mode to MAC driver + +Some 10Gigabit PHYs automatically switch the mode of their host +interface depending on their negotiated speed. We need to communicate +this to the MAC driver so the MAC can switch its host interface to +match the PHYs new operating mode. Provide the current PHY interface +mode to the MAC driver. + +Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> +--- + +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -242,8 +242,9 @@ static void phylink_mac_config(struct ph + const struct phylink_link_state *state) + { + netdev_dbg(pl->netdev, +- "%s: mode=%s/%s/%s adv=%*pb pause=%02x link=%u an=%u\n", ++ "%s: mode=%s/%s/%s/%s adv=%*pb pause=%02x link=%u an=%u\n", + __func__, phylink_an_mode_str(pl->link_an_mode), ++ phy_modes(state->interface), + phy_speed_to_str(state->speed), + phy_duplex_to_str(state->duplex), + __ETHTOOL_LINK_MODE_MASK_NBITS, state->advertising, +@@ -264,6 +265,7 @@ static int phylink_get_mac_state(struct + + linkmode_copy(state->advertising, pl->link_config.advertising); + linkmode_zero(state->lp_advertising); ++ state->interface = pl->link_config.interface; + state->an_enabled = pl->link_config.an_enabled; + state->link = 1; + +@@ -344,19 +346,38 @@ static void phylink_resolve(struct work_ + case MLO_AN_PHY: + link_state = pl->phy_state; + phylink_resolve_flow(pl, &link_state); ++ phylink_mac_config(pl, &link_state); + break; + + case MLO_AN_FIXED: + phylink_get_fixed_state(pl, &link_state); ++ phylink_mac_config(pl, &link_state); + break; + + case MLO_AN_SGMII: + phylink_get_mac_state(pl, &link_state); + if (pl->phydev) { ++ bool changed = false; ++ + link_state.link = link_state.link && + pl->phy_state.link; +- link_state.pause |= pl->phy_state.pause; +- phylink_resolve_flow(pl, &link_state); ++ ++ if (pl->phy_state.interface != ++ link_state.interface) { ++ link_state.interface = pl->phy_state.interface; ++ changed = true; ++ } ++ ++ /* Propagate the flow control from the PHY ++ * to the MAC. Also propagate the interface ++ * if changed. ++ */ ++ if (pl->phy_state.link || changed) { ++ link_state.pause |= pl->phy_state.pause; ++ phylink_resolve_flow(pl, &link_state); ++ ++ phylink_mac_config(pl, &link_state); ++ } + } + break; + +@@ -372,13 +393,6 @@ static void phylink_resolve(struct work_ + pl->ops->mac_link_down(ndev, pl->link_an_mode); + netdev_info(ndev, "Link is Down\n"); + } else { +- /* If we have a PHY, we need the MAC updated with +- * the current link parameters (eg, in SGMII mode, +- * with flow control status.) +- */ +- if (pl->phydev) +- phylink_mac_config(pl, &link_state); +- + pl->ops->mac_link_up(ndev, pl->link_an_mode, + pl->phydev); + +@@ -414,8 +428,10 @@ struct phylink *phylink_create(struct ne + mutex_init(&pl->config_mutex); + INIT_WORK(&pl->resolve, phylink_resolve); + pl->netdev = ndev; ++ pl->phy_state.interface = iface; + pl->link_interface = iface; + pl->link_port = PORT_MII; ++ pl->link_config.interface = iface; + pl->link_config.pause = MLO_PAUSE_AN; + pl->link_config.speed = SPEED_UNKNOWN; + pl->link_config.duplex = DUPLEX_UNKNOWN; +@@ -471,12 +487,14 @@ void phylink_phy_change(struct phy_devic + pl->phy_state.pause |= MLO_PAUSE_SYM; + if (phydev->asym_pause) + pl->phy_state.pause |= MLO_PAUSE_ASYM; ++ pl->phy_state.interface = phydev->interface; + pl->phy_state.link = up; + mutex_unlock(&pl->state_mutex); + + phylink_run_resolve(pl); + +- netdev_dbg(pl->netdev, "phy link %s %s/%s\n", up ? "up" : "down", ++ netdev_dbg(pl->netdev, "phy link %s %s/%s/%s\n", up ? "up" : "down", ++ phy_modes(phydev->interface), + phy_speed_to_str(phydev->speed), + phy_duplex_to_str(phydev->duplex)); + } +--- a/include/linux/phylink.h ++++ b/include/linux/phylink.h +@@ -27,6 +27,7 @@ enum { + struct phylink_link_state { + __ETHTOOL_DECLARE_LINK_MODE_MASK(advertising); + __ETHTOOL_DECLARE_LINK_MODE_MASK(lp_advertising); ++ phy_interface_t interface; /* PHY_INTERFACE_xxx */ + int speed; + int duplex; + int pause; diff --git a/target/linux/mvebu/patches-4.9/437-phylink-ensure-link-drops-are-reported.patch b/target/linux/mvebu/patches-4.9/437-phylink-ensure-link-drops-are-reported.patch new file mode 100644 index 0000000000..8544197478 --- /dev/null +++ b/target/linux/mvebu/patches-4.9/437-phylink-ensure-link-drops-are-reported.patch @@ -0,0 +1,52 @@ +From: Russell King <rmk+kernel@armlinux.org.uk> +Date: Mon, 19 Dec 2016 12:17:57 +0000 +Subject: [PATCH] phylink: ensure link drops are reported + +When the MAC reports a link failure, it can be momentary. Ensure +that the event is reported by latching the loss of link, so that the +worker reports link down. + +Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> +--- + +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -60,6 +60,8 @@ struct phylink { + struct phylink_link_state phy_state; + struct work_struct resolve; + ++ bool mac_link_dropped; ++ + const struct phylink_module_ops *module_ops; + void *module_data; + }; +@@ -340,6 +342,9 @@ static void phylink_resolve(struct work_ + + mutex_lock(&pl->state_mutex); + if (pl->phylink_disable_state) { ++ pl->mac_link_dropped = false; ++ link_state.link = false; ++ } else if (pl->mac_link_dropped) { + link_state.link = false; + } else { + switch (pl->link_an_mode) { +@@ -405,6 +410,10 @@ static void phylink_resolve(struct work_ + phylink_pause_to_str(link_state.pause)); + } + } ++ if (!link_state.link && pl->mac_link_dropped) { ++ pl->mac_link_dropped = false; ++ queue_work(system_power_efficient_wq, &pl->resolve); ++ } + mutex_unlock(&pl->state_mutex); + } + +@@ -641,6 +650,8 @@ EXPORT_SYMBOL_GPL(phylink_disconnect_phy + + void phylink_mac_change(struct phylink *pl, bool up) + { ++ if (!up) ++ pl->mac_link_dropped = true; + phylink_run_resolve(pl); + netdev_dbg(pl->netdev, "mac link %s\n", up ? "up" : "down"); + } diff --git a/target/linux/mvebu/patches-4.9/450-reprobe_sfp_phy.patch b/target/linux/mvebu/patches-4.9/450-reprobe_sfp_phy.patch new file mode 100644 index 0000000000..69eaa3773e --- /dev/null +++ b/target/linux/mvebu/patches-4.9/450-reprobe_sfp_phy.patch @@ -0,0 +1,119 @@ +From 28baa5e2635285b178326b301f534ed95c65dd01 Mon Sep 17 00:00:00 2001 +From: Jonas Gorski <jonas.gorski@gmail.com> +Date: Thu, 29 Sep 2016 11:44:39 +0200 +Subject: [PATCH] sfp: retry phy probe if unsuccessful + +Some phys seem to take longer than 50 ms to come out of reset, so retry +until we find a phy. + +Signed-off-by: Jonas Gorski <jonas.gorski@gmail.com> +--- + drivers/net/phy/sfp.c | 38 +++++++++++++++++++++++++------------- + 1 file changed, 25 insertions(+), 13 deletions(-) + +--- a/drivers/net/phy/sfp.c ++++ b/drivers/net/phy/sfp.c +@@ -491,7 +491,7 @@ static void sfp_sm_phy_detach(struct sfp + sfp->mod_phy = NULL; + } + +-static void sfp_sm_probe_phy(struct sfp *sfp) ++static int sfp_sm_probe_phy(struct sfp *sfp) + { + struct phy_device *phy; + int err; +@@ -500,12 +500,12 @@ static void sfp_sm_probe_phy(struct sfp + + phy = mdiobus_scan(sfp->i2c_mii, SFP_PHY_ADDR); + if (IS_ERR(phy)) { ++ if (PTR_ERR(phy) == -ENODEV) { ++ dev_dbg(sfp->dev, "no PHY detected\n"); ++ return -EAGAIN; ++ } + dev_err(sfp->dev, "mdiobus scan returned %ld\n", PTR_ERR(phy)); +- return; +- } +- if (!phy) { +- dev_info(sfp->dev, "no PHY detected\n"); +- return; ++ return PTR_ERR(phy); + } + + if (sfp->phylink) { +@@ -515,12 +515,14 @@ static void sfp_sm_probe_phy(struct sfp + phy_device_free(phy); + dev_err(sfp->dev, "phylink_connect_phy failed: %d\n", + err); +- return; ++ return err; + } + } + + sfp->mod_phy = phy; + phy_start(phy); ++ ++ return 0; + } + + static void sfp_sm_link_up(struct sfp *sfp) +@@ -569,30 +571,41 @@ static void sfp_sm_fault(struct sfp *sfp + + static void sfp_sm_mod_init(struct sfp *sfp) + { ++ int ret = 0; ++ + sfp_module_tx_enable(sfp); + +- /* Wait t_init before indicating that the link is up, provided the +- * current state indicates no TX_FAULT. If TX_FAULT clears before +- * this time, that's fine too. +- */ +- sfp_sm_next(sfp, SFP_S_INIT, T_INIT_JIFFIES); +- sfp->sm_retries = 5; ++ if (!sfp->phylink) ++ return; + +- if (sfp->phylink) { +- /* Setting the serdes link mode is guesswork: there's no +- * field in the EEPROM which indicates what mode should +- * be used. +- * +- * If it's a gigabit-only fiber module, it probably does +- * not have a PHY, so switch to 802.3z negotiation mode. +- * Otherwise, switch to SGMII mode (which is required to +- * support non-gigabit speeds) and probe for a PHY. ++ /* Setting the serdes link mode is guesswork: there's no ++ * field in the EEPROM which indicates what mode should ++ * be used. ++ * ++ * If it's a gigabit-only fiber module, it probably does ++ * not have a PHY, so switch to 802.3z negotiation mode. ++ * Otherwise, switch to SGMII mode (which is required to ++ * support non-gigabit speeds) and probe for a PHY. ++ */ ++ if (sfp->id.base.e1000_base_t || ++ sfp->id.base.e100_base_lx || ++ sfp->id.base.e100_base_fx) ++ ret = sfp_sm_probe_phy(sfp); ++ ++ if (!ret) { ++ /* Wait t_init before indicating that the link is up, provided ++ * the current state indicates no TX_FAULT. If TX_FAULT clears ++ * this time, that's fine too. + */ +- if (sfp->id.base.e1000_base_t || +- sfp->id.base.e100_base_lx || +- sfp->id.base.e100_base_fx) +- sfp_sm_probe_phy(sfp); ++ sfp_sm_next(sfp, SFP_S_INIT, T_INIT_JIFFIES); ++ sfp->sm_retries = 5; ++ return; + } ++ ++ if (ret == -EAGAIN) ++ sfp_sm_set_timer(sfp, T_PROBE_RETRY); ++ else ++ sfp_sm_next(sfp, SFP_S_TX_DISABLE, 0); + } + + static int sfp_sm_mod_probe(struct sfp *sfp) |