generic: 6.1, 6.6: replace Airoha EN8811H PHY driver with upstream

Backport driver from upcoming Linux 6.10 and put a pending fix on top
to make sure the netdev trigger offloading behaves as expected.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
This commit is contained in:
Daniel Golle 2024-05-08 01:48:14 +01:00
parent 71e3e3b892
commit 0d74b2a1e5
12 changed files with 1421 additions and 651 deletions

View File

@ -0,0 +1,161 @@
From 66a5c40f60f5d88ad8d47ba6a4ba05892853fa1f Mon Sep 17 00:00:00 2001
From: Tanzir Hasan <tanzirh@google.com>
Date: Tue, 26 Dec 2023 18:00:00 +0000
Subject: [PATCH] kernel.h: removed REPEAT_BYTE from kernel.h
This patch creates wordpart.h and includes it in asm/word-at-a-time.h
for all architectures. WORD_AT_A_TIME_CONSTANTS depends on kernel.h
because of REPEAT_BYTE. Moving this to another header and including it
where necessary allows us to not include the bloated kernel.h. Making
this implicit dependency on REPEAT_BYTE explicit allows for later
improvements in the lib/string.c inclusion list.
Suggested-by: Al Viro <viro@zeniv.linux.org.uk>
Suggested-by: Andy Shevchenko <andy.shevchenko@gmail.com>
Signed-off-by: Tanzir Hasan <tanzirh@google.com>
Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
Link: https://lore.kernel.org/r/20231226-libstringheader-v6-1-80aa08c7652c@google.com
Signed-off-by: Kees Cook <keescook@chromium.org>
---
arch/arm/include/asm/word-at-a-time.h | 3 ++-
arch/arm64/include/asm/word-at-a-time.h | 3 ++-
arch/powerpc/include/asm/word-at-a-time.h | 4 ++--
arch/riscv/include/asm/word-at-a-time.h | 3 ++-
arch/s390/include/asm/word-at-a-time.h | 3 ++-
arch/sh/include/asm/word-at-a-time.h | 2 ++
arch/x86/include/asm/word-at-a-time.h | 3 ++-
arch/x86/kvm/mmu/mmu.c | 1 +
fs/namei.c | 2 +-
include/asm-generic/word-at-a-time.h | 3 ++-
include/linux/kernel.h | 8 --------
include/linux/wordpart.h | 13 +++++++++++++
12 files changed, 31 insertions(+), 17 deletions(-)
create mode 100644 include/linux/wordpart.h
--- a/arch/arm/include/asm/word-at-a-time.h
+++ b/arch/arm/include/asm/word-at-a-time.h
@@ -8,7 +8,8 @@
* Little-endian word-at-a-time zero byte handling.
* Heavily based on the x86 algorithm.
*/
-#include <linux/kernel.h>
+#include <linux/bitops.h>
+#include <linux/wordpart.h>
struct word_at_a_time {
const unsigned long one_bits, high_bits;
--- a/arch/arm64/include/asm/word-at-a-time.h
+++ b/arch/arm64/include/asm/word-at-a-time.h
@@ -9,7 +9,8 @@
#ifndef __AARCH64EB__
-#include <linux/kernel.h>
+#include <linux/bitops.h>
+#include <linux/wordpart.h>
struct word_at_a_time {
const unsigned long one_bits, high_bits;
--- a/arch/powerpc/include/asm/word-at-a-time.h
+++ b/arch/powerpc/include/asm/word-at-a-time.h
@@ -4,8 +4,8 @@
/*
* Word-at-a-time interfaces for PowerPC.
*/
-
-#include <linux/kernel.h>
+#include <linux/bitops.h>
+#include <linux/wordpart.h>
#include <asm/asm-compat.h>
#include <asm/extable.h>
--- a/arch/sh/include/asm/word-at-a-time.h
+++ b/arch/sh/include/asm/word-at-a-time.h
@@ -5,6 +5,8 @@
#ifdef CONFIG_CPU_BIG_ENDIAN
# include <asm-generic/word-at-a-time.h>
#else
+#include <linux/bitops.h>
+#include <linux/wordpart.h>
/*
* Little-endian version cribbed from x86.
*/
--- a/arch/x86/include/asm/word-at-a-time.h
+++ b/arch/x86/include/asm/word-at-a-time.h
@@ -2,7 +2,8 @@
#ifndef _ASM_WORD_AT_A_TIME_H
#define _ASM_WORD_AT_A_TIME_H
-#include <linux/kernel.h>
+#include <linux/bitops.h>
+#include <linux/wordpart.h>
/*
* This is largely generic for little-endian machines, but the
--- a/arch/x86/kvm/mmu/mmu.c
+++ b/arch/x86/kvm/mmu/mmu.c
@@ -44,6 +44,7 @@
#include <linux/kern_levels.h>
#include <linux/kstrtox.h>
#include <linux/kthread.h>
+#include <linux/wordpart.h>
#include <asm/page.h>
#include <asm/memtype.h>
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -17,8 +17,8 @@
#include <linux/init.h>
#include <linux/export.h>
-#include <linux/kernel.h>
#include <linux/slab.h>
+#include <linux/wordpart.h>
#include <linux/fs.h>
#include <linux/namei.h>
#include <linux/pagemap.h>
--- a/include/asm-generic/word-at-a-time.h
+++ b/include/asm-generic/word-at-a-time.h
@@ -2,7 +2,8 @@
#ifndef _ASM_WORD_AT_A_TIME_H
#define _ASM_WORD_AT_A_TIME_H
-#include <linux/kernel.h>
+#include <linux/bitops.h>
+#include <linux/wordpart.h>
#include <asm/byteorder.h>
#ifdef __BIG_ENDIAN
--- a/include/linux/kernel.h
+++ b/include/linux/kernel.h
@@ -36,14 +36,6 @@
#define STACK_MAGIC 0xdeadbeef
-/**
- * REPEAT_BYTE - repeat the value @x multiple times as an unsigned long value
- * @x: value to repeat
- *
- * NOTE: @x is not checked for > 0xff; larger values produce odd results.
- */
-#define REPEAT_BYTE(x) ((~0ul / 0xff) * (x))
-
/* generic data direction definitions */
#define READ 0
#define WRITE 1
--- /dev/null
+++ b/include/linux/wordpart.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _LINUX_WORDPART_H
+#define _LINUX_WORDPART_H
+/**
+ * REPEAT_BYTE - repeat the value @x multiple times as an unsigned long value
+ * @x: value to repeat
+ *
+ * NOTE: @x is not checked for > 0xff; larger values produce odd results.
+ */
+#define REPEAT_BYTE(x) ((~0ul / 0xff) * (x))
+
+#endif // _LINUX_WORDPART_H

View File

@ -0,0 +1,107 @@
From adeb04362d74188c1e22ccb824b15a0a7b3de2f4 Mon Sep 17 00:00:00 2001
From: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Date: Wed, 14 Feb 2024 19:26:32 +0200
Subject: [PATCH] kernel.h: Move upper_*_bits() and lower_*_bits() to
wordpart.h
The wordpart.h header is collecting APIs related to the handling
parts of the word (usually in byte granularity). The upper_*_bits()
and lower_*_bits() are good candidates to be moved to there.
This helps to clean up header dependency hell with regard to kernel.h
as the latter gathers completely unrelated stuff together and slows
down compilation (especially when it's included into other header).
Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Link: https://lore.kernel.org/r/20240214172752.3605073-1-andriy.shevchenko@linux.intel.com
Reviewed-by: Randy Dunlap <rdunlap@infradead.org>
Signed-off-by: Kees Cook <keescook@chromium.org>
---
include/linux/kernel.h | 30 ++----------------------------
include/linux/wordpart.h | 29 +++++++++++++++++++++++++++++
2 files changed, 31 insertions(+), 28 deletions(-)
--- a/include/linux/kernel.h
+++ b/include/linux/kernel.h
@@ -30,6 +30,8 @@
#include <linux/build_bug.h>
#include <linux/static_call_types.h>
#include <linux/instruction_pointer.h>
+#include <linux/wordpart.h>
+
#include <asm/byteorder.h>
#include <uapi/linux/kernel.h>
@@ -55,34 +57,6 @@
} \
)
-/**
- * upper_32_bits - return bits 32-63 of a number
- * @n: the number we're accessing
- *
- * A basic shift-right of a 64- or 32-bit quantity. Use this to suppress
- * the "right shift count >= width of type" warning when that quantity is
- * 32-bits.
- */
-#define upper_32_bits(n) ((u32)(((n) >> 16) >> 16))
-
-/**
- * lower_32_bits - return bits 0-31 of a number
- * @n: the number we're accessing
- */
-#define lower_32_bits(n) ((u32)((n) & 0xffffffff))
-
-/**
- * upper_16_bits - return bits 16-31 of a number
- * @n: the number we're accessing
- */
-#define upper_16_bits(n) ((u16)((n) >> 16))
-
-/**
- * lower_16_bits - return bits 0-15 of a number
- * @n: the number we're accessing
- */
-#define lower_16_bits(n) ((u16)((n) & 0xffff))
-
struct completion;
struct user;
--- a/include/linux/wordpart.h
+++ b/include/linux/wordpart.h
@@ -2,6 +2,35 @@
#ifndef _LINUX_WORDPART_H
#define _LINUX_WORDPART_H
+
+/**
+ * upper_32_bits - return bits 32-63 of a number
+ * @n: the number we're accessing
+ *
+ * A basic shift-right of a 64- or 32-bit quantity. Use this to suppress
+ * the "right shift count >= width of type" warning when that quantity is
+ * 32-bits.
+ */
+#define upper_32_bits(n) ((u32)(((n) >> 16) >> 16))
+
+/**
+ * lower_32_bits - return bits 0-31 of a number
+ * @n: the number we're accessing
+ */
+#define lower_32_bits(n) ((u32)((n) & 0xffffffff))
+
+/**
+ * upper_16_bits - return bits 16-31 of a number
+ * @n: the number we're accessing
+ */
+#define upper_16_bits(n) ((u16)((n) >> 16))
+
+/**
+ * lower_16_bits - return bits 0-15 of a number
+ * @n: the number we're accessing
+ */
+#define lower_16_bits(n) ((u16)((n) & 0xffff))
+
/**
* REPEAT_BYTE - repeat the value @x multiple times as an unsigned long value
* @x: value to repeat

View File

@ -0,0 +1,47 @@
From 87c33315af380ca12a2e59ac94edad4fe0481b4c Mon Sep 17 00:00:00 2001
From: Dan Carpenter <dan.carpenter@linaro.org>
Date: Fri, 5 Apr 2024 13:08:59 +0300
Subject: [PATCH] net: phy: air_en8811h: fix some error codes
These error paths accidentally return "ret" which is zero/success
instead of the correct error code.
Fixes: 71e79430117d ("net: phy: air_en8811h: Add the Airoha EN8811H PHY driver")
Signed-off-by: Dan Carpenter <dan.carpenter@linaro.org>
Reviewed-by: Simon Horman <horms@kernel.org>
Link: https://lore.kernel.org/r/7ef2e230-dfb7-4a77-8973-9e5be1a99fc2@moroto.mountain
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
drivers/net/phy/air_en8811h.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
--- a/drivers/net/phy/air_en8811h.c
+++ b/drivers/net/phy/air_en8811h.c
@@ -272,11 +272,11 @@ static int __air_buckpbus_reg_read(struc
pbus_data_high = __phy_read(phydev, AIR_BPBUS_RD_DATA_HIGH);
if (pbus_data_high < 0)
- return ret;
+ return pbus_data_high;
pbus_data_low = __phy_read(phydev, AIR_BPBUS_RD_DATA_LOW);
if (pbus_data_low < 0)
- return ret;
+ return pbus_data_low;
*pbus_data = pbus_data_low | (pbus_data_high << 16);
return 0;
@@ -323,11 +323,11 @@ static int __air_buckpbus_reg_modify(str
pbus_data_high = __phy_read(phydev, AIR_BPBUS_RD_DATA_HIGH);
if (pbus_data_high < 0)
- return ret;
+ return pbus_data_high;
pbus_data_low = __phy_read(phydev, AIR_BPBUS_RD_DATA_LOW);
if (pbus_data_low < 0)
- return ret;
+ return pbus_data_low;
pbus_data_old = pbus_data_low | (pbus_data_high << 16);
pbus_data_new = (pbus_data_old & ~mask) | set;

View File

@ -16,8 +16,8 @@ Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
--- a/drivers/net/phy/Kconfig --- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig
@@ -72,9 +72,9 @@ config SFP @@ -77,9 +77,9 @@ config AIR_EN8811H_PHY
comment "MII PHY device drivers" Currently supports the Airoha EN8811H PHY.
config AMD_PHY config AMD_PHY
- tristate "AMD PHYs" - tristate "AMD PHYs"

View File

@ -0,0 +1,161 @@
From 66a5c40f60f5d88ad8d47ba6a4ba05892853fa1f Mon Sep 17 00:00:00 2001
From: Tanzir Hasan <tanzirh@google.com>
Date: Tue, 26 Dec 2023 18:00:00 +0000
Subject: [PATCH] kernel.h: removed REPEAT_BYTE from kernel.h
This patch creates wordpart.h and includes it in asm/word-at-a-time.h
for all architectures. WORD_AT_A_TIME_CONSTANTS depends on kernel.h
because of REPEAT_BYTE. Moving this to another header and including it
where necessary allows us to not include the bloated kernel.h. Making
this implicit dependency on REPEAT_BYTE explicit allows for later
improvements in the lib/string.c inclusion list.
Suggested-by: Al Viro <viro@zeniv.linux.org.uk>
Suggested-by: Andy Shevchenko <andy.shevchenko@gmail.com>
Signed-off-by: Tanzir Hasan <tanzirh@google.com>
Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
Link: https://lore.kernel.org/r/20231226-libstringheader-v6-1-80aa08c7652c@google.com
Signed-off-by: Kees Cook <keescook@chromium.org>
---
arch/arm/include/asm/word-at-a-time.h | 3 ++-
arch/arm64/include/asm/word-at-a-time.h | 3 ++-
arch/powerpc/include/asm/word-at-a-time.h | 4 ++--
arch/riscv/include/asm/word-at-a-time.h | 3 ++-
arch/s390/include/asm/word-at-a-time.h | 3 ++-
arch/sh/include/asm/word-at-a-time.h | 2 ++
arch/x86/include/asm/word-at-a-time.h | 3 ++-
arch/x86/kvm/mmu/mmu.c | 1 +
fs/namei.c | 2 +-
include/asm-generic/word-at-a-time.h | 3 ++-
include/linux/kernel.h | 8 --------
include/linux/wordpart.h | 13 +++++++++++++
12 files changed, 31 insertions(+), 17 deletions(-)
create mode 100644 include/linux/wordpart.h
--- a/arch/arm/include/asm/word-at-a-time.h
+++ b/arch/arm/include/asm/word-at-a-time.h
@@ -8,7 +8,8 @@
* Little-endian word-at-a-time zero byte handling.
* Heavily based on the x86 algorithm.
*/
-#include <linux/kernel.h>
+#include <linux/bitops.h>
+#include <linux/wordpart.h>
struct word_at_a_time {
const unsigned long one_bits, high_bits;
--- a/arch/arm64/include/asm/word-at-a-time.h
+++ b/arch/arm64/include/asm/word-at-a-time.h
@@ -9,7 +9,8 @@
#ifndef __AARCH64EB__
-#include <linux/kernel.h>
+#include <linux/bitops.h>
+#include <linux/wordpart.h>
struct word_at_a_time {
const unsigned long one_bits, high_bits;
--- a/arch/powerpc/include/asm/word-at-a-time.h
+++ b/arch/powerpc/include/asm/word-at-a-time.h
@@ -4,8 +4,8 @@
/*
* Word-at-a-time interfaces for PowerPC.
*/
-
-#include <linux/kernel.h>
+#include <linux/bitops.h>
+#include <linux/wordpart.h>
#include <asm/asm-compat.h>
#include <asm/extable.h>
--- a/arch/sh/include/asm/word-at-a-time.h
+++ b/arch/sh/include/asm/word-at-a-time.h
@@ -5,6 +5,8 @@
#ifdef CONFIG_CPU_BIG_ENDIAN
# include <asm-generic/word-at-a-time.h>
#else
+#include <linux/bitops.h>
+#include <linux/wordpart.h>
/*
* Little-endian version cribbed from x86.
*/
--- a/arch/x86/include/asm/word-at-a-time.h
+++ b/arch/x86/include/asm/word-at-a-time.h
@@ -2,7 +2,8 @@
#ifndef _ASM_WORD_AT_A_TIME_H
#define _ASM_WORD_AT_A_TIME_H
-#include <linux/kernel.h>
+#include <linux/bitops.h>
+#include <linux/wordpart.h>
/*
* This is largely generic for little-endian machines, but the
--- a/arch/x86/kvm/mmu/mmu.c
+++ b/arch/x86/kvm/mmu/mmu.c
@@ -47,6 +47,7 @@
#include <linux/kern_levels.h>
#include <linux/kstrtox.h>
#include <linux/kthread.h>
+#include <linux/wordpart.h>
#include <asm/page.h>
#include <asm/memtype.h>
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -17,8 +17,8 @@
#include <linux/init.h>
#include <linux/export.h>
-#include <linux/kernel.h>
#include <linux/slab.h>
+#include <linux/wordpart.h>
#include <linux/fs.h>
#include <linux/filelock.h>
#include <linux/namei.h>
--- a/include/asm-generic/word-at-a-time.h
+++ b/include/asm-generic/word-at-a-time.h
@@ -2,7 +2,8 @@
#ifndef _ASM_WORD_AT_A_TIME_H
#define _ASM_WORD_AT_A_TIME_H
-#include <linux/kernel.h>
+#include <linux/bitops.h>
+#include <linux/wordpart.h>
#include <asm/byteorder.h>
#ifdef __BIG_ENDIAN
--- a/include/linux/kernel.h
+++ b/include/linux/kernel.h
@@ -38,14 +38,6 @@
#define STACK_MAGIC 0xdeadbeef
-/**
- * REPEAT_BYTE - repeat the value @x multiple times as an unsigned long value
- * @x: value to repeat
- *
- * NOTE: @x is not checked for > 0xff; larger values produce odd results.
- */
-#define REPEAT_BYTE(x) ((~0ul / 0xff) * (x))
-
/* generic data direction definitions */
#define READ 0
#define WRITE 1
--- /dev/null
+++ b/include/linux/wordpart.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _LINUX_WORDPART_H
+#define _LINUX_WORDPART_H
+/**
+ * REPEAT_BYTE - repeat the value @x multiple times as an unsigned long value
+ * @x: value to repeat
+ *
+ * NOTE: @x is not checked for > 0xff; larger values produce odd results.
+ */
+#define REPEAT_BYTE(x) ((~0ul / 0xff) * (x))
+
+#endif // _LINUX_WORDPART_H

View File

@ -0,0 +1,107 @@
From adeb04362d74188c1e22ccb824b15a0a7b3de2f4 Mon Sep 17 00:00:00 2001
From: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Date: Wed, 14 Feb 2024 19:26:32 +0200
Subject: [PATCH] kernel.h: Move upper_*_bits() and lower_*_bits() to
wordpart.h
The wordpart.h header is collecting APIs related to the handling
parts of the word (usually in byte granularity). The upper_*_bits()
and lower_*_bits() are good candidates to be moved to there.
This helps to clean up header dependency hell with regard to kernel.h
as the latter gathers completely unrelated stuff together and slows
down compilation (especially when it's included into other header).
Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Link: https://lore.kernel.org/r/20240214172752.3605073-1-andriy.shevchenko@linux.intel.com
Reviewed-by: Randy Dunlap <rdunlap@infradead.org>
Signed-off-by: Kees Cook <keescook@chromium.org>
---
include/linux/kernel.h | 30 ++----------------------------
include/linux/wordpart.h | 29 +++++++++++++++++++++++++++++
2 files changed, 31 insertions(+), 28 deletions(-)
--- a/include/linux/kernel.h
+++ b/include/linux/kernel.h
@@ -32,6 +32,8 @@
#include <linux/sprintf.h>
#include <linux/static_call_types.h>
#include <linux/instruction_pointer.h>
+#include <linux/wordpart.h>
+
#include <asm/byteorder.h>
#include <uapi/linux/kernel.h>
@@ -57,34 +59,6 @@
} \
)
-/**
- * upper_32_bits - return bits 32-63 of a number
- * @n: the number we're accessing
- *
- * A basic shift-right of a 64- or 32-bit quantity. Use this to suppress
- * the "right shift count >= width of type" warning when that quantity is
- * 32-bits.
- */
-#define upper_32_bits(n) ((u32)(((n) >> 16) >> 16))
-
-/**
- * lower_32_bits - return bits 0-31 of a number
- * @n: the number we're accessing
- */
-#define lower_32_bits(n) ((u32)((n) & 0xffffffff))
-
-/**
- * upper_16_bits - return bits 16-31 of a number
- * @n: the number we're accessing
- */
-#define upper_16_bits(n) ((u16)((n) >> 16))
-
-/**
- * lower_16_bits - return bits 0-15 of a number
- * @n: the number we're accessing
- */
-#define lower_16_bits(n) ((u16)((n) & 0xffff))
-
struct completion;
struct user;
--- a/include/linux/wordpart.h
+++ b/include/linux/wordpart.h
@@ -2,6 +2,35 @@
#ifndef _LINUX_WORDPART_H
#define _LINUX_WORDPART_H
+
+/**
+ * upper_32_bits - return bits 32-63 of a number
+ * @n: the number we're accessing
+ *
+ * A basic shift-right of a 64- or 32-bit quantity. Use this to suppress
+ * the "right shift count >= width of type" warning when that quantity is
+ * 32-bits.
+ */
+#define upper_32_bits(n) ((u32)(((n) >> 16) >> 16))
+
+/**
+ * lower_32_bits - return bits 0-31 of a number
+ * @n: the number we're accessing
+ */
+#define lower_32_bits(n) ((u32)((n) & 0xffffffff))
+
+/**
+ * upper_16_bits - return bits 16-31 of a number
+ * @n: the number we're accessing
+ */
+#define upper_16_bits(n) ((u16)((n) >> 16))
+
+/**
+ * lower_16_bits - return bits 0-15 of a number
+ * @n: the number we're accessing
+ */
+#define lower_16_bits(n) ((u16)((n) & 0xffff))
+
/**
* REPEAT_BYTE - repeat the value @x multiple times as an unsigned long value
* @x: value to repeat

View File

@ -1,83 +1,28 @@
From patchwork Tue Feb 6 19:47:51 2024 From 71e79430117d56c409c5ea485a263bc0d8083390 Mon Sep 17 00:00:00 2001
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
X-Patchwork-Submitter: Eric Woudstra <ericwouds@gmail.com>
X-Patchwork-Id: 13547762
X-Patchwork-Delegate: kuba@kernel.org
From: Eric Woudstra <ericwouds@gmail.com> From: Eric Woudstra <ericwouds@gmail.com>
To: "David S. Miller" <davem@davemloft.net>, Date: Tue, 26 Mar 2024 17:23:05 +0100
Eric Dumazet <edumazet@google.com>, Subject: [PATCH] net: phy: air_en8811h: Add the Airoha EN8811H PHY driver
Jakub Kicinski <kuba@kernel.org>,
Paolo Abeni <pabeni@redhat.com>,
Rob Herring <robh+dt@kernel.org>,
Krzysztof Kozlowski <krzysztof.kozlowski+dt@linaro.org>,
Conor Dooley <conor+dt@kernel.org>,
Andrew Lunn <andrew@lunn.ch>,
Heiner Kallweit <hkallweit1@gmail.com>,
Russell King <linux@armlinux.org.uk>,
Matthias Brugger <matthias.bgg@gmail.com>,
AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>,
"Frank Wunderlich" <frank-w@public-files.de>,
Daniel Golle <daniel@makrotopia.org>,
Lucien Jheng <lucien.jheng@airoha.com>,
Zhi-Jun You <hujy652@protonmail.com>
Cc: netdev@vger.kernel.org,
devicetree@vger.kernel.org,
Eric Woudstra <ericwouds@gmail.com>
Subject: [PATCH net-next 2/2] net: phy: air_en8811h: Add the Airoha EN8811H
PHY driver
Date: Tue, 6 Feb 2024 20:47:51 +0100
Message-ID: <20240206194751.1901802-3-ericwouds@gmail.com>
X-Mailer: git-send-email 2.42.1
In-Reply-To: <20240206194751.1901802-1-ericwouds@gmail.com>
References: <20240206194751.1901802-1-ericwouds@gmail.com>
Precedence: bulk
X-Mailing-List: netdev@vger.kernel.org
List-Id: <netdev.vger.kernel.org>
List-Subscribe: <mailto:netdev+subscribe@vger.kernel.org>
List-Unsubscribe: <mailto:netdev+unsubscribe@vger.kernel.org>
MIME-Version: 1.0
X-Patchwork-Delegate: kuba@kernel.org
* Source originated from airoha's en8811h v1.2.1 driver Add the driver for the Airoha EN8811H 2.5 Gigabit PHY. The phy supports
* Moved air_en8811h.h to air_en8811h.c 100/1000/2500 Mbps with auto negotiation only.
* Removed air_pbus_reg_write() as it writes to another device on mdio-bus
* Load firmware from /lib/firmware/airoha/ instead of /lib/firmware/
* Added .get_rate_matching()
* Use generic phy_read/write() and phy_read/write_mmd()
* Edited .get_features() to use generic C45 functions
* Edited .config_aneg() and .read_status() to use a mix of generic C22/C45
* Use led handling functions from mediatek-ge-soc.c
* Simplified led handling by storing led rules
* Cleanup macro definitions
* Cleanup code to pass checkpatch.pl
* General code cleanup
Changes from original RFC patch: The driver uses two firmware files, for which updated versions are added to
linux-firmware already.
* Use the correct order in Kconfig and Makefile Note: At phy-address + 8 there is another device on the mdio bus, that
* Change some register naming to correspond with datasheet belongs to the EN881H. While the original driver writes to it, Airoha
* Use phy_driver .read_page() and .write_page() has confirmed this is not needed. Therefore, communication with this
* Use module_phy_driver() device is not included in this driver.
* Use get_unaligned_le16() instead of macro
* In .config_aneg() and .read_status() use genphy_xxx() C22
* Use another vendor register to read real speed
* Load firmware only once and store firmware version
* Apply 2.5G LPA work-around (firmware before 24011202)
* Read 2.5G LPA from vendor register (firmware 24011202 and later)
Changes to be committed:
modified: drivers/net/phy/Kconfig
modified: drivers/net/phy/Makefile
new file: drivers/net/phy/air_en8811h.c
Signed-off-by: Eric Woudstra <ericwouds@gmail.com> Signed-off-by: Eric Woudstra <ericwouds@gmail.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Link: https://lore.kernel.org/r/20240326162305.303598-3-ericwouds@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
--- ---
drivers/net/phy/Kconfig | 5 + drivers/net/phy/Kconfig | 5 +
drivers/net/phy/Makefile | 1 + drivers/net/phy/Makefile | 1 +
drivers/net/phy/air_en8811h.c | 1006 +++++++++++++++++++++++++++++++++ drivers/net/phy/air_en8811h.c | 1086 +++++++++++++++++++++++++++++++++
3 files changed, 1012 insertions(+) 3 files changed, 1092 insertions(+)
create mode 100644 drivers/net/phy/air_en8811h.c create mode 100644 drivers/net/phy/air_en8811h.c
--- a/drivers/net/phy/Kconfig --- a/drivers/net/phy/Kconfig
@ -92,7 +37,7 @@ Signed-off-by: Eric Woudstra <ericwouds@gmail.com>
+ Currently supports the Airoha EN8811H PHY. + Currently supports the Airoha EN8811H PHY.
+ +
config AMD_PHY config AMD_PHY
tristate "AMD and Altima PHYs" tristate "AMD PHYs"
help help
--- a/drivers/net/phy/Makefile --- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile
@ -106,12 +51,10 @@ Signed-off-by: Eric Woudstra <ericwouds@gmail.com>
obj-$(CONFIG_AX88796B_PHY) += ax88796b.o obj-$(CONFIG_AX88796B_PHY) += ax88796b.o
--- /dev/null --- /dev/null
+++ b/drivers/net/phy/air_en8811h.c +++ b/drivers/net/phy/air_en8811h.c
@@ -0,0 +1,1006 @@ @@ -0,0 +1,1086 @@
+// SPDX-License-Identifier: GPL-2.0+ +// SPDX-License-Identifier: GPL-2.0+
+/* +/*
+ * Driver for Airoha Ethernet PHYs + * Driver for the Airoha EN8811H 2.5 Gigabit PHY.
+ *
+ * Currently supporting the EN8811H.
+ * + *
+ * Limitations of the EN8811H: + * Limitations of the EN8811H:
+ * - Only full duplex supported + * - Only full duplex supported
@ -125,6 +68,7 @@ Signed-off-by: Eric Woudstra <ericwouds@gmail.com>
+#include <linux/phy.h> +#include <linux/phy.h>
+#include <linux/firmware.h> +#include <linux/firmware.h>
+#include <linux/property.h> +#include <linux/property.h>
+#include <linux/wordpart.h>
+#include <asm/unaligned.h> +#include <asm/unaligned.h>
+ +
+#define EN8811H_PHY_ID 0x03a2a411 +#define EN8811H_PHY_ID 0x03a2a411
@ -135,10 +79,6 @@ Signed-off-by: Eric Woudstra <ericwouds@gmail.com>
+#define AIR_FW_ADDR_DM 0x00000000 +#define AIR_FW_ADDR_DM 0x00000000
+#define AIR_FW_ADDR_DSP 0x00100000 +#define AIR_FW_ADDR_DSP 0x00100000
+ +
+/* u32 (DWORD) component macros */
+#define LOWORD(d) ((u16)(u32)(d))
+#define HIWORD(d) ((u16)(((u32)(d)) >> 16))
+
+/* MII Registers */ +/* MII Registers */
+#define AIR_AUX_CTRL_STATUS 0x1d +#define AIR_AUX_CTRL_STATUS 0x1d
+#define AIR_AUX_CTRL_STATUS_SPEED_MASK GENMASK(4, 2) +#define AIR_AUX_CTRL_STATUS_SPEED_MASK GENMASK(4, 2)
@ -151,32 +91,32 @@ Signed-off-by: Eric Woudstra <ericwouds@gmail.com>
+#define AIR_PHY_PAGE_EXTENDED_4 0x0004 +#define AIR_PHY_PAGE_EXTENDED_4 0x0004
+ +
+/* MII Registers Page 4*/ +/* MII Registers Page 4*/
+#define AIR_PBUS_MODE 0x10 +#define AIR_BPBUS_MODE 0x10
+#define AIR_PBUS_MODE_ADDR_FIXED 0x0000 +#define AIR_BPBUS_MODE_ADDR_FIXED 0x0000
+#define AIR_PBUS_MODE_ADDR_INCR BIT(15) +#define AIR_BPBUS_MODE_ADDR_INCR BIT(15)
+#define AIR_PBUS_WR_ADDR_HIGH 0x11 +#define AIR_BPBUS_WR_ADDR_HIGH 0x11
+#define AIR_PBUS_WR_ADDR_LOW 0x12 +#define AIR_BPBUS_WR_ADDR_LOW 0x12
+#define AIR_PBUS_WR_DATA_HIGH 0x13 +#define AIR_BPBUS_WR_DATA_HIGH 0x13
+#define AIR_PBUS_WR_DATA_LOW 0x14 +#define AIR_BPBUS_WR_DATA_LOW 0x14
+#define AIR_PBUS_RD_ADDR_HIGH 0x15 +#define AIR_BPBUS_RD_ADDR_HIGH 0x15
+#define AIR_PBUS_RD_ADDR_LOW 0x16 +#define AIR_BPBUS_RD_ADDR_LOW 0x16
+#define AIR_PBUS_RD_DATA_HIGH 0x17 +#define AIR_BPBUS_RD_DATA_HIGH 0x17
+#define AIR_PBUS_RD_DATA_LOW 0x18 +#define AIR_BPBUS_RD_DATA_LOW 0x18
+ +
+/* Registers on MDIO_MMD_VEND1 */ +/* Registers on MDIO_MMD_VEND1 */
+#define EN8811H_PHY_FW_STATUS 0x8009 +#define EN8811H_PHY_FW_STATUS 0x8009
+#define EN8811H_PHY_READY 0x02 +#define EN8811H_PHY_READY 0x02
+ +
+#define AIR_PHY_HOST_CMD_1 0x800c +#define AIR_PHY_MCU_CMD_1 0x800c
+#define AIR_PHY_HOST_CMD_1_MODE1 0x0 +#define AIR_PHY_MCU_CMD_1_MODE1 0x0
+#define AIR_PHY_HOST_CMD_2 0x800d +#define AIR_PHY_MCU_CMD_2 0x800d
+#define AIR_PHY_HOST_CMD_2_MODE1 0x0 +#define AIR_PHY_MCU_CMD_2_MODE1 0x0
+#define AIR_PHY_HOST_CMD_3 0x800e +#define AIR_PHY_MCU_CMD_3 0x800e
+#define AIR_PHY_HOST_CMD_3_MODE1 0x1101 +#define AIR_PHY_MCU_CMD_3_MODE1 0x1101
+#define AIR_PHY_HOST_CMD_3_DOCMD 0x1100 +#define AIR_PHY_MCU_CMD_3_DOCMD 0x1100
+#define AIR_PHY_HOST_CMD_4 0x800f +#define AIR_PHY_MCU_CMD_4 0x800f
+#define AIR_PHY_HOST_CMD_4_MODE1 0x0002 +#define AIR_PHY_MCU_CMD_4_MODE1 0x0002
+#define AIR_PHY_HOST_CMD_4_INTCLR 0x00e4 +#define AIR_PHY_MCU_CMD_4_INTCLR 0x00e4
+ +
+/* Registers on MDIO_MMD_VEND2 */ +/* Registers on MDIO_MMD_VEND2 */
+#define AIR_PHY_LED_BCR 0x021 +#define AIR_PHY_LED_BCR 0x021
@ -235,28 +175,21 @@ Signed-off-by: Eric Woudstra <ericwouds@gmail.com>
+#define EN8811H_FW_CTRL_2 0x800000 +#define EN8811H_FW_CTRL_2 0x800000
+#define EN8811H_FW_CTRL_2_LOADING BIT(11) +#define EN8811H_FW_CTRL_2_LOADING BIT(11)
+ +
+/* Led definitions */
+#define EN8811H_LED_COUNT 3 +#define EN8811H_LED_COUNT 3
+ +
+/* GPIO5 <-> BASE_T_LED0 +/* Default LED setup:
+ * GPIO4 <-> BASE_T_LED1 + * GPIO5 <-> LED0 On: Link detected, blink Rx/Tx
+ * GPIO3 <-> BASE_T_LED2 + * GPIO4 <-> LED1 On: Link detected at 2500 or 1000 Mbps
+ * + * GPIO3 <-> LED2 On: Link detected at 2500 or 100 Mbps
+ * Default setup suitable for 2 leds connected:
+ * 100M link up triggers led0, only led0 blinking on traffic
+ * 1000M link up triggers led1, only led1 blinking on traffic
+ * 2500M link up triggers led0 and led1, both blinking on traffic
+ * Also suitable for 1 led connected:
+ * any link up triggers led2
+ */ + */
+#define AIR_DEFAULT_TRIGGER_LED0 (BIT(TRIGGER_NETDEV_LINK_2500) | \ +#define AIR_DEFAULT_TRIGGER_LED0 (BIT(TRIGGER_NETDEV_LINK) | \
+ BIT(TRIGGER_NETDEV_LINK_100) | \
+ BIT(TRIGGER_NETDEV_RX) | \ + BIT(TRIGGER_NETDEV_RX) | \
+ BIT(TRIGGER_NETDEV_TX)) + BIT(TRIGGER_NETDEV_TX))
+#define AIR_DEFAULT_TRIGGER_LED1 (BIT(TRIGGER_NETDEV_LINK_2500) | \ +#define AIR_DEFAULT_TRIGGER_LED1 (BIT(TRIGGER_NETDEV_LINK_2500) | \
+ BIT(TRIGGER_NETDEV_LINK_1000) | \ + BIT(TRIGGER_NETDEV_LINK_1000))
+ BIT(TRIGGER_NETDEV_RX) | \ +#define AIR_DEFAULT_TRIGGER_LED2 (BIT(TRIGGER_NETDEV_LINK_2500) | \
+ BIT(TRIGGER_NETDEV_TX)) + BIT(TRIGGER_NETDEV_LINK_100))
+#define AIR_DEFAULT_TRIGGER_LED2 BIT(TRIGGER_NETDEV_LINK)
+ +
+struct led { +struct led {
+ unsigned long rules; + unsigned long rules;
@ -265,6 +198,7 @@ Signed-off-by: Eric Woudstra <ericwouds@gmail.com>
+ +
+struct en8811h_priv { +struct en8811h_priv {
+ u32 firmware_version; + u32 firmware_version;
+ bool mcu_needs_restart;
+ struct led led[EN8811H_LED_COUNT]; + struct led led[EN8811H_LED_COUNT];
+}; +};
+ +
@ -274,12 +208,12 @@ Signed-off-by: Eric Woudstra <ericwouds@gmail.com>
+}; +};
+ +
+enum { +enum {
+ AIR_PHY_LED_DUR_BLINK_32M, + AIR_PHY_LED_DUR_BLINK_32MS,
+ AIR_PHY_LED_DUR_BLINK_64M, + AIR_PHY_LED_DUR_BLINK_64MS,
+ AIR_PHY_LED_DUR_BLINK_128M, + AIR_PHY_LED_DUR_BLINK_128MS,
+ AIR_PHY_LED_DUR_BLINK_256M, + AIR_PHY_LED_DUR_BLINK_256MS,
+ AIR_PHY_LED_DUR_BLINK_512M, + AIR_PHY_LED_DUR_BLINK_512MS,
+ AIR_PHY_LED_DUR_BLINK_1024M, + AIR_PHY_LED_DUR_BLINK_1024MS,
+}; +};
+ +
+enum { +enum {
@ -298,16 +232,16 @@ Signed-off-by: Eric Woudstra <ericwouds@gmail.com>
+}; +};
+ +
+#define AIR_PHY_LED_DUR_UNIT 1024 +#define AIR_PHY_LED_DUR_UNIT 1024
+#define AIR_PHY_LED_DUR (AIR_PHY_LED_DUR_UNIT << AIR_PHY_LED_DUR_BLINK_64M) +#define AIR_PHY_LED_DUR (AIR_PHY_LED_DUR_UNIT << AIR_PHY_LED_DUR_BLINK_64MS)
+ +
+static const unsigned long en8811h_led_trig = (BIT(TRIGGER_NETDEV_FULL_DUPLEX) | +static const unsigned long en8811h_led_trig = BIT(TRIGGER_NETDEV_FULL_DUPLEX) |
+ BIT(TRIGGER_NETDEV_LINK) | + BIT(TRIGGER_NETDEV_LINK) |
+ BIT(TRIGGER_NETDEV_LINK_10) | + BIT(TRIGGER_NETDEV_LINK_10) |
+ BIT(TRIGGER_NETDEV_LINK_100) | + BIT(TRIGGER_NETDEV_LINK_100) |
+ BIT(TRIGGER_NETDEV_LINK_1000) | + BIT(TRIGGER_NETDEV_LINK_1000) |
+ BIT(TRIGGER_NETDEV_LINK_2500) | + BIT(TRIGGER_NETDEV_LINK_2500) |
+ BIT(TRIGGER_NETDEV_RX) | + BIT(TRIGGER_NETDEV_RX) |
+ BIT(TRIGGER_NETDEV_TX)); + BIT(TRIGGER_NETDEV_TX);
+ +
+static int air_phy_read_page(struct phy_device *phydev) +static int air_phy_read_page(struct phy_device *phydev)
+{ +{
@ -324,23 +258,27 @@ Signed-off-by: Eric Woudstra <ericwouds@gmail.com>
+{ +{
+ int ret; + int ret;
+ +
+ ret = __phy_write(phydev, AIR_PBUS_MODE, AIR_PBUS_MODE_ADDR_FIXED); + ret = __phy_write(phydev, AIR_BPBUS_MODE, AIR_BPBUS_MODE_ADDR_FIXED);
+ if (ret < 0) + if (ret < 0)
+ return ret; + return ret;
+ +
+ ret = __phy_write(phydev, AIR_PBUS_WR_ADDR_HIGH, HIWORD(pbus_address)); + ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_HIGH,
+ upper_16_bits(pbus_address));
+ if (ret < 0) + if (ret < 0)
+ return ret; + return ret;
+ +
+ ret = __phy_write(phydev, AIR_PBUS_WR_ADDR_LOW, LOWORD(pbus_address)); + ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_LOW,
+ lower_16_bits(pbus_address));
+ if (ret < 0) + if (ret < 0)
+ return ret; + return ret;
+ +
+ ret = __phy_write(phydev, AIR_PBUS_WR_DATA_HIGH, HIWORD(pbus_data)); + ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_HIGH,
+ upper_16_bits(pbus_data));
+ if (ret < 0) + if (ret < 0)
+ return ret; + return ret;
+ +
+ ret = __phy_write(phydev, AIR_PBUS_WR_DATA_LOW, LOWORD(pbus_data)); + ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_LOW,
+ lower_16_bits(pbus_data));
+ if (ret < 0) + if (ret < 0)
+ return ret; + return ret;
+ +
@ -350,17 +288,20 @@ Signed-off-by: Eric Woudstra <ericwouds@gmail.com>
+static int air_buckpbus_reg_write(struct phy_device *phydev, +static int air_buckpbus_reg_write(struct phy_device *phydev,
+ u32 pbus_address, u32 pbus_data) + u32 pbus_address, u32 pbus_data)
+{ +{
+ int ret, saved_page; + int saved_page;
+ int ret = 0;
+ +
+ saved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4); + saved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4);
+ +
+ ret = __air_buckpbus_reg_write(phydev, pbus_address, pbus_data); + if (saved_page >= 0) {
+ ret = __air_buckpbus_reg_write(phydev, pbus_address,
+ pbus_data);
+ if (ret < 0) + if (ret < 0)
+ phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__, + phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__,
+ pbus_address, ret); + pbus_address, ret);
+ }
+ +
+ return phy_restore_page(phydev, saved_page, ret); + return phy_restore_page(phydev, saved_page, ret);
+;
+} +}
+ +
+static int __air_buckpbus_reg_read(struct phy_device *phydev, +static int __air_buckpbus_reg_read(struct phy_device *phydev,
@ -369,41 +310,122 @@ Signed-off-by: Eric Woudstra <ericwouds@gmail.com>
+ int pbus_data_low, pbus_data_high; + int pbus_data_low, pbus_data_high;
+ int ret; + int ret;
+ +
+ ret = __phy_write(phydev, AIR_PBUS_MODE, AIR_PBUS_MODE_ADDR_FIXED); + ret = __phy_write(phydev, AIR_BPBUS_MODE, AIR_BPBUS_MODE_ADDR_FIXED);
+ if (ret < 0) + if (ret < 0)
+ return ret; + return ret;
+ +
+ ret = __phy_write(phydev, AIR_PBUS_RD_ADDR_HIGH, HIWORD(pbus_address)); + ret = __phy_write(phydev, AIR_BPBUS_RD_ADDR_HIGH,
+ upper_16_bits(pbus_address));
+ if (ret < 0) + if (ret < 0)
+ return ret; + return ret;
+ +
+ ret = __phy_write(phydev, AIR_PBUS_RD_ADDR_LOW, LOWORD(pbus_address)); + ret = __phy_write(phydev, AIR_BPBUS_RD_ADDR_LOW,
+ lower_16_bits(pbus_address));
+ if (ret < 0) + if (ret < 0)
+ return ret; + return ret;
+ +
+ pbus_data_high = __phy_read(phydev, AIR_PBUS_RD_DATA_HIGH); + pbus_data_high = __phy_read(phydev, AIR_BPBUS_RD_DATA_HIGH);
+ if (pbus_data_high < 0) + if (pbus_data_high < 0)
+ return ret; + return ret;
+ +
+ pbus_data_low = __phy_read(phydev, AIR_PBUS_RD_DATA_LOW); + pbus_data_low = __phy_read(phydev, AIR_BPBUS_RD_DATA_LOW);
+ if (pbus_data_low < 0) + if (pbus_data_low < 0)
+ return ret; + return ret;
+ +
+ *pbus_data = (u16)pbus_data_low | ((u32)(u16)pbus_data_high << 16); + *pbus_data = pbus_data_low | (pbus_data_high << 16);
+ return 0; + return 0;
+} +}
+ +
+static int air_buckpbus_reg_read(struct phy_device *phydev, +static int air_buckpbus_reg_read(struct phy_device *phydev,
+ u32 pbus_address, u32 *pbus_data) + u32 pbus_address, u32 *pbus_data)
+{ +{
+ int ret, saved_page; + int saved_page;
+ int ret = 0;
+ +
+ saved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4); + saved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4);
+ +
+ if (saved_page >= 0) {
+ ret = __air_buckpbus_reg_read(phydev, pbus_address, pbus_data); + ret = __air_buckpbus_reg_read(phydev, pbus_address, pbus_data);
+ if (ret < 0) + if (ret < 0)
+ phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__, + phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__,
+ pbus_address, ret); + pbus_address, ret);
+ }
+
+ return phy_restore_page(phydev, saved_page, ret);
+}
+
+static int __air_buckpbus_reg_modify(struct phy_device *phydev,
+ u32 pbus_address, u32 mask, u32 set)
+{
+ int pbus_data_low, pbus_data_high;
+ u32 pbus_data_old, pbus_data_new;
+ int ret;
+
+ ret = __phy_write(phydev, AIR_BPBUS_MODE, AIR_BPBUS_MODE_ADDR_FIXED);
+ if (ret < 0)
+ return ret;
+
+ ret = __phy_write(phydev, AIR_BPBUS_RD_ADDR_HIGH,
+ upper_16_bits(pbus_address));
+ if (ret < 0)
+ return ret;
+
+ ret = __phy_write(phydev, AIR_BPBUS_RD_ADDR_LOW,
+ lower_16_bits(pbus_address));
+ if (ret < 0)
+ return ret;
+
+ pbus_data_high = __phy_read(phydev, AIR_BPBUS_RD_DATA_HIGH);
+ if (pbus_data_high < 0)
+ return ret;
+
+ pbus_data_low = __phy_read(phydev, AIR_BPBUS_RD_DATA_LOW);
+ if (pbus_data_low < 0)
+ return ret;
+
+ pbus_data_old = pbus_data_low | (pbus_data_high << 16);
+ pbus_data_new = (pbus_data_old & ~mask) | set;
+ if (pbus_data_new == pbus_data_old)
+ return 0;
+
+ ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_HIGH,
+ upper_16_bits(pbus_address));
+ if (ret < 0)
+ return ret;
+
+ ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_LOW,
+ lower_16_bits(pbus_address));
+ if (ret < 0)
+ return ret;
+
+ ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_HIGH,
+ upper_16_bits(pbus_data_new));
+ if (ret < 0)
+ return ret;
+
+ ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_LOW,
+ lower_16_bits(pbus_data_new));
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int air_buckpbus_reg_modify(struct phy_device *phydev,
+ u32 pbus_address, u32 mask, u32 set)
+{
+ int saved_page;
+ int ret = 0;
+
+ saved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4);
+
+ if (saved_page >= 0) {
+ ret = __air_buckpbus_reg_modify(phydev, pbus_address, mask,
+ set);
+ if (ret < 0)
+ phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__,
+ pbus_address, ret);
+ }
+ +
+ return phy_restore_page(phydev, saved_page, ret); + return phy_restore_page(phydev, saved_page, ret);
+} +}
@ -415,26 +437,28 @@ Signed-off-by: Eric Woudstra <ericwouds@gmail.com>
+ int ret; + int ret;
+ u16 val; + u16 val;
+ +
+ ret = __phy_write(phydev, AIR_PBUS_MODE, AIR_PBUS_MODE_ADDR_INCR); + ret = __phy_write(phydev, AIR_BPBUS_MODE, AIR_BPBUS_MODE_ADDR_INCR);
+ if (ret < 0) + if (ret < 0)
+ return ret; + return ret;
+ +
+ ret = __phy_write(phydev, AIR_PBUS_WR_ADDR_HIGH, HIWORD(address)); + ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_HIGH,
+ upper_16_bits(address));
+ if (ret < 0) + if (ret < 0)
+ return ret; + return ret;
+ +
+ ret = __phy_write(phydev, AIR_PBUS_WR_ADDR_LOW, LOWORD(address)); + ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_LOW,
+ lower_16_bits(address));
+ if (ret < 0) + if (ret < 0)
+ return ret; + return ret;
+ +
+ for (offset = 0; offset < fw->size; offset += 4) { + for (offset = 0; offset < fw->size; offset += 4) {
+ val = get_unaligned_le16(&fw->data[offset + 2]); + val = get_unaligned_le16(&fw->data[offset + 2]);
+ ret = __phy_write(phydev, AIR_PBUS_WR_DATA_HIGH, val); + ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_HIGH, val);
+ if (ret < 0) + if (ret < 0)
+ return ret; + return ret;
+ +
+ val = get_unaligned_le16(&fw->data[offset]); + val = get_unaligned_le16(&fw->data[offset]);
+ ret = __phy_write(phydev, AIR_PBUS_WR_DATA_LOW, val); + ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_LOW, val);
+ if (ret < 0) + if (ret < 0)
+ return ret; + return ret;
+ } + }
@ -445,23 +469,43 @@ Signed-off-by: Eric Woudstra <ericwouds@gmail.com>
+static int air_write_buf(struct phy_device *phydev, u32 address, +static int air_write_buf(struct phy_device *phydev, u32 address,
+ const struct firmware *fw) + const struct firmware *fw)
+{ +{
+ int ret, saved_page; + int saved_page;
+ int ret = 0;
+ +
+ saved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4); + saved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4);
+ +
+ if (saved_page >= 0) {
+ ret = __air_write_buf(phydev, address, fw); + ret = __air_write_buf(phydev, address, fw);
+ if (ret < 0) + if (ret < 0)
+ phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__, + phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__,
+ address, ret); + address, ret);
+ }
+ +
+ return phy_restore_page(phydev, saved_page, ret); + return phy_restore_page(phydev, saved_page, ret);
+} +}
+ +
+static int en8811h_wait_mcu_ready(struct phy_device *phydev)
+{
+ int ret, reg_value;
+
+ /* Because of mdio-lock, may have to wait for multiple loads */
+ ret = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1,
+ EN8811H_PHY_FW_STATUS, reg_value,
+ reg_value == EN8811H_PHY_READY,
+ 20000, 7500000, true);
+ if (ret) {
+ phydev_err(phydev, "MCU not ready: 0x%x\n", reg_value);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int en8811h_load_firmware(struct phy_device *phydev) +static int en8811h_load_firmware(struct phy_device *phydev)
+{ +{
+ struct en8811h_priv *priv = phydev->priv;
+ struct device *dev = &phydev->mdio.dev; + struct device *dev = &phydev->mdio.dev;
+ const struct firmware *fw1, *fw2; + const struct firmware *fw1, *fw2;
+ u32 pbus_value;
+ int ret; + int ret;
+ +
+ ret = request_firmware_direct(&fw1, EN8811H_MD32_DM, dev); + ret = request_firmware_direct(&fw1, EN8811H_MD32_DM, dev);
@ -477,11 +521,9 @@ Signed-off-by: Eric Woudstra <ericwouds@gmail.com>
+ if (ret < 0) + if (ret < 0)
+ goto en8811h_load_firmware_out; + goto en8811h_load_firmware_out;
+ +
+ ret = air_buckpbus_reg_read(phydev, EN8811H_FW_CTRL_2, &pbus_value); + ret = air_buckpbus_reg_modify(phydev, EN8811H_FW_CTRL_2,
+ if (ret < 0) + EN8811H_FW_CTRL_2_LOADING,
+ goto en8811h_load_firmware_out; + EN8811H_FW_CTRL_2_LOADING);
+ pbus_value |= EN8811H_FW_CTRL_2_LOADING;
+ ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_2, pbus_value);
+ if (ret < 0) + if (ret < 0)
+ goto en8811h_load_firmware_out; + goto en8811h_load_firmware_out;
+ +
@ -493,11 +535,8 @@ Signed-off-by: Eric Woudstra <ericwouds@gmail.com>
+ if (ret < 0) + if (ret < 0)
+ goto en8811h_load_firmware_out; + goto en8811h_load_firmware_out;
+ +
+ ret = air_buckpbus_reg_read(phydev, EN8811H_FW_CTRL_2, &pbus_value); + ret = air_buckpbus_reg_modify(phydev, EN8811H_FW_CTRL_2,
+ if (ret < 0) + EN8811H_FW_CTRL_2_LOADING, 0);
+ goto en8811h_load_firmware_out;
+ pbus_value &= ~EN8811H_FW_CTRL_2_LOADING;
+ ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_2, pbus_value);
+ if (ret < 0) + if (ret < 0)
+ goto en8811h_load_firmware_out; + goto en8811h_load_firmware_out;
+ +
@ -506,7 +545,12 @@ Signed-off-by: Eric Woudstra <ericwouds@gmail.com>
+ if (ret < 0) + if (ret < 0)
+ goto en8811h_load_firmware_out; + goto en8811h_load_firmware_out;
+ +
+ ret = 0; + ret = en8811h_wait_mcu_ready(phydev);
+
+ air_buckpbus_reg_read(phydev, EN8811H_FW_VERSION,
+ &priv->firmware_version);
+ phydev_info(phydev, "MD32 firmware version: %08x\n",
+ priv->firmware_version);
+ +
+en8811h_load_firmware_out: +en8811h_load_firmware_out:
+ release_firmware(fw2); + release_firmware(fw2);
@ -520,7 +564,7 @@ Signed-off-by: Eric Woudstra <ericwouds@gmail.com>
+ return ret; + return ret;
+} +}
+ +
+static int en8811h_restart_host(struct phy_device *phydev) +static int en8811h_restart_mcu(struct phy_device *phydev)
+{ +{
+ int ret; + int ret;
+ +
@ -529,8 +573,12 @@ Signed-off-by: Eric Woudstra <ericwouds@gmail.com>
+ if (ret < 0) + if (ret < 0)
+ return ret; + return ret;
+ +
+ return air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1, + ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1,
+ EN8811H_FW_CTRL_1_FINISH); + EN8811H_FW_CTRL_1_FINISH);
+ if (ret < 0)
+ return ret;
+
+ return en8811h_wait_mcu_ready(phydev);
+} +}
+ +
+static int air_hw_led_on_set(struct phy_device *phydev, u8 index, bool on) +static int air_hw_led_on_set(struct phy_device *phydev, u8 index, bool on)
@ -664,68 +712,46 @@ Signed-off-by: Eric Woudstra <ericwouds@gmail.com>
+ u16 on = 0, blink = 0; + u16 on = 0, blink = 0;
+ int ret; + int ret;
+ +
+ priv->led[index].rules = rules;
+
+ if (index >= EN8811H_LED_COUNT) + if (index >= EN8811H_LED_COUNT)
+ return -EINVAL; + return -EINVAL;
+ +
+ if (rules & (BIT(TRIGGER_NETDEV_LINK_10) | BIT(TRIGGER_NETDEV_LINK))) { + priv->led[index].rules = rules;
+
+ if (rules & BIT(TRIGGER_NETDEV_FULL_DUPLEX))
+ on |= AIR_PHY_LED_ON_FDX;
+
+ if (rules & (BIT(TRIGGER_NETDEV_LINK_10) | BIT(TRIGGER_NETDEV_LINK)))
+ on |= AIR_PHY_LED_ON_LINK10; + on |= AIR_PHY_LED_ON_LINK10;
+ if (rules & BIT(TRIGGER_NETDEV_RX))
+ blink |= AIR_PHY_LED_BLINK_10RX;
+ if (rules & BIT(TRIGGER_NETDEV_TX))
+ blink |= AIR_PHY_LED_BLINK_10TX;
+ }
+ +
+ if (rules & (BIT(TRIGGER_NETDEV_LINK_100) | BIT(TRIGGER_NETDEV_LINK))) { + if (rules & (BIT(TRIGGER_NETDEV_LINK_100) | BIT(TRIGGER_NETDEV_LINK)))
+ on |= AIR_PHY_LED_ON_LINK100; + on |= AIR_PHY_LED_ON_LINK100;
+ if (rules & BIT(TRIGGER_NETDEV_RX))
+ blink |= AIR_PHY_LED_BLINK_100RX;
+ if (rules & BIT(TRIGGER_NETDEV_TX))
+ blink |= AIR_PHY_LED_BLINK_100TX;
+ }
+ +
+ if (rules & (BIT(TRIGGER_NETDEV_LINK_1000) | BIT(TRIGGER_NETDEV_LINK))) { + if (rules & (BIT(TRIGGER_NETDEV_LINK_1000) | BIT(TRIGGER_NETDEV_LINK)))
+ on |= AIR_PHY_LED_ON_LINK1000; + on |= AIR_PHY_LED_ON_LINK1000;
+ if (rules & BIT(TRIGGER_NETDEV_RX))
+ blink |= AIR_PHY_LED_BLINK_1000RX;
+ if (rules & BIT(TRIGGER_NETDEV_TX))
+ blink |= AIR_PHY_LED_BLINK_1000TX;
+ }
+ +
+ if (rules & (BIT(TRIGGER_NETDEV_LINK_2500) | BIT(TRIGGER_NETDEV_LINK))) { + if (rules & (BIT(TRIGGER_NETDEV_LINK_2500) | BIT(TRIGGER_NETDEV_LINK)))
+ on |= AIR_PHY_LED_ON_LINK2500; + on |= AIR_PHY_LED_ON_LINK2500;
+ if (rules & BIT(TRIGGER_NETDEV_RX))
+ blink |= AIR_PHY_LED_BLINK_2500RX;
+ if (rules & BIT(TRIGGER_NETDEV_TX))
+ blink |= AIR_PHY_LED_BLINK_2500TX;
+ }
+ +
+ if (on == 0) {
+ if (rules & BIT(TRIGGER_NETDEV_RX)) { + if (rules & BIT(TRIGGER_NETDEV_RX)) {
+ blink |= AIR_PHY_LED_BLINK_10RX | + blink |= AIR_PHY_LED_BLINK_10RX |
+ AIR_PHY_LED_BLINK_100RX | + AIR_PHY_LED_BLINK_100RX |
+ AIR_PHY_LED_BLINK_1000RX | + AIR_PHY_LED_BLINK_1000RX |
+ AIR_PHY_LED_BLINK_2500RX; + AIR_PHY_LED_BLINK_2500RX;
+ } + }
+
+ if (rules & BIT(TRIGGER_NETDEV_TX)) { + if (rules & BIT(TRIGGER_NETDEV_TX)) {
+ blink |= AIR_PHY_LED_BLINK_10TX | + blink |= AIR_PHY_LED_BLINK_10TX |
+ AIR_PHY_LED_BLINK_100TX | + AIR_PHY_LED_BLINK_100TX |
+ AIR_PHY_LED_BLINK_1000TX | + AIR_PHY_LED_BLINK_1000TX |
+ AIR_PHY_LED_BLINK_2500TX; + AIR_PHY_LED_BLINK_2500TX;
+ } + }
+ }
+
+ if (rules & BIT(TRIGGER_NETDEV_FULL_DUPLEX))
+ on |= AIR_PHY_LED_ON_FDX;
+
+ if (rules & BIT(TRIGGER_NETDEV_HALF_DUPLEX))
+ on |= AIR_PHY_LED_ON_HDX;
+ +
+ if (blink || on) { + if (blink || on) {
+ /* switch hw-control on, so led-on and led-blink are off */ + /* switch hw-control on, so led-on and led-blink are off */
+ clear_bit(AIR_PHY_LED_STATE_FORCE_ON, &priv->led[index].state); + clear_bit(AIR_PHY_LED_STATE_FORCE_ON,
+ clear_bit(AIR_PHY_LED_STATE_FORCE_BLINK, &priv->led[index].state); + &priv->led[index].state);
+ clear_bit(AIR_PHY_LED_STATE_FORCE_BLINK,
+ &priv->led[index].state);
+ } else { + } else {
+ priv->led[index].rules = 0; + priv->led[index].rules = 0;
+ } + }
@ -742,28 +768,26 @@ Signed-off-by: Eric Woudstra <ericwouds@gmail.com>
+ +
+static int air_led_init(struct phy_device *phydev, u8 index, u8 state, u8 pol) +static int air_led_init(struct phy_device *phydev, u8 index, u8 state, u8 pol)
+{ +{
+ int cl45_data; + int val = 0;
+ int err; + int err;
+ +
+ if (index >= EN8811H_LED_COUNT) + if (index >= EN8811H_LED_COUNT)
+ return -EINVAL; + return -EINVAL;
+ +
+ cl45_data = phy_read_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_ON(index));
+ if (cl45_data < 0)
+ return cl45_data;
+
+ if (state == AIR_LED_ENABLE) + if (state == AIR_LED_ENABLE)
+ cl45_data |= AIR_PHY_LED_ON_ENABLE; + val |= AIR_PHY_LED_ON_ENABLE;
+ else + else
+ cl45_data &= ~AIR_PHY_LED_ON_ENABLE; + val &= ~AIR_PHY_LED_ON_ENABLE;
+ +
+ if (pol == AIR_ACTIVE_HIGH) + if (pol == AIR_ACTIVE_HIGH)
+ cl45_data |= AIR_PHY_LED_ON_POLARITY; + val |= AIR_PHY_LED_ON_POLARITY;
+ else + else
+ cl45_data &= ~AIR_PHY_LED_ON_POLARITY; + val &= ~AIR_PHY_LED_ON_POLARITY;
+
+ err = phy_modify_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_ON(index),
+ AIR_PHY_LED_ON_ENABLE |
+ AIR_PHY_LED_ON_POLARITY, val);
+ +
+ err = phy_write_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_ON(index),
+ cl45_data);
+ if (err < 0) + if (err < 0)
+ return err; + return err;
+ +
@ -773,42 +797,40 @@ Signed-off-by: Eric Woudstra <ericwouds@gmail.com>
+static int air_leds_init(struct phy_device *phydev, int num, int dur, int mode) +static int air_leds_init(struct phy_device *phydev, int num, int dur, int mode)
+{ +{
+ struct en8811h_priv *priv = phydev->priv; + struct en8811h_priv *priv = phydev->priv;
+ int cl45_data = dur;
+ int ret, i; + int ret, i;
+ +
+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_DUR_BLINK, + ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_DUR_BLINK,
+ cl45_data); + dur);
+ if (ret < 0) + if (ret < 0)
+ return ret; + return ret;
+ +
+ cl45_data >>= 1;
+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_DUR_ON, + ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_DUR_ON,
+ cl45_data); + dur >> 1);
+ if (ret < 0) + if (ret < 0)
+ return ret; + return ret;
+ +
+ cl45_data = phy_read_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_BCR);
+ if (cl45_data < 0)
+ return cl45_data;
+
+ switch (mode) { + switch (mode) {
+ case AIR_LED_MODE_DISABLE: + case AIR_LED_MODE_DISABLE:
+ cl45_data &= ~AIR_PHY_LED_BCR_EXT_CTRL; + ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_BCR,
+ cl45_data &= ~AIR_PHY_LED_BCR_MODE_MASK; + AIR_PHY_LED_BCR_EXT_CTRL |
+ AIR_PHY_LED_BCR_MODE_MASK, 0);
+ if (ret < 0)
+ return ret;
+ break; + break;
+ case AIR_LED_MODE_USER_DEFINE: + case AIR_LED_MODE_USER_DEFINE:
+ cl45_data |= AIR_PHY_LED_BCR_EXT_CTRL; + ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_BCR,
+ cl45_data |= AIR_PHY_LED_BCR_CLK_EN; + AIR_PHY_LED_BCR_EXT_CTRL |
+ AIR_PHY_LED_BCR_CLK_EN,
+ AIR_PHY_LED_BCR_EXT_CTRL |
+ AIR_PHY_LED_BCR_CLK_EN);
+ if (ret < 0)
+ return ret;
+ break; + break;
+ default: + default:
+ phydev_err(phydev, "LED mode %d is not supported\n", mode); + phydev_err(phydev, "LED mode %d is not supported\n", mode);
+ return -EINVAL; + return -EINVAL;
+ } + }
+ +
+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_BCR, cl45_data);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < num; ++i) { + for (i = 0; i < num; ++i) {
+ ret = air_led_init(phydev, i, AIR_LED_ENABLE, AIR_ACTIVE_HIGH); + ret = air_led_init(phydev, i, AIR_LED_ENABLE, AIR_ACTIVE_HIGH);
+ if (ret < 0) { + if (ret < 0) {
@ -837,21 +859,42 @@ Signed-off-by: Eric Woudstra <ericwouds@gmail.com>
+static int en8811h_probe(struct phy_device *phydev) +static int en8811h_probe(struct phy_device *phydev)
+{ +{
+ struct en8811h_priv *priv; + struct en8811h_priv *priv;
+ int ret;
+ +
+ priv = devm_kzalloc(&phydev->mdio.dev, sizeof(struct en8811h_priv), + priv = devm_kzalloc(&phydev->mdio.dev, sizeof(struct en8811h_priv),
+ GFP_KERNEL); + GFP_KERNEL);
+ if (!priv) + if (!priv)
+ return -ENOMEM; + return -ENOMEM;
+ phydev->priv = priv;
+
+ ret = en8811h_load_firmware(phydev);
+ if (ret < 0)
+ return ret;
+
+ /* mcu has just restarted after firmware load */
+ priv->mcu_needs_restart = false;
+ +
+ priv->led[0].rules = AIR_DEFAULT_TRIGGER_LED0; + priv->led[0].rules = AIR_DEFAULT_TRIGGER_LED0;
+ priv->led[1].rules = AIR_DEFAULT_TRIGGER_LED1; + priv->led[1].rules = AIR_DEFAULT_TRIGGER_LED1;
+ priv->led[2].rules = AIR_DEFAULT_TRIGGER_LED2; + priv->led[2].rules = AIR_DEFAULT_TRIGGER_LED2;
+ +
+ phydev->priv = priv;
+
+ /* MDIO_DEVS1/2 empty, so set mmds_present bits here */ + /* MDIO_DEVS1/2 empty, so set mmds_present bits here */
+ phydev->c45_ids.mmds_present |= MDIO_DEVS_PMAPMD | MDIO_DEVS_AN; + phydev->c45_ids.mmds_present |= MDIO_DEVS_PMAPMD | MDIO_DEVS_AN;
+ +
+ ret = air_leds_init(phydev, EN8811H_LED_COUNT, AIR_PHY_LED_DUR,
+ AIR_LED_MODE_DISABLE);
+ if (ret < 0) {
+ phydev_err(phydev, "Failed to disable leds: %d\n", ret);
+ return ret;
+ }
+
+ /* Configure led gpio pins as output */
+ ret = air_buckpbus_reg_modify(phydev, EN8811H_GPIO_OUTPUT,
+ EN8811H_GPIO_OUTPUT_345,
+ EN8811H_GPIO_OUTPUT_345);
+ if (ret < 0)
+ return ret;
+
+ return 0; + return 0;
+} +}
+ +
@ -859,58 +902,41 @@ Signed-off-by: Eric Woudstra <ericwouds@gmail.com>
+{ +{
+ struct en8811h_priv *priv = phydev->priv; + struct en8811h_priv *priv = phydev->priv;
+ struct device *dev = &phydev->mdio.dev; + struct device *dev = &phydev->mdio.dev;
+ int ret, pollret, reg_value;
+ u32 pbus_value; + u32 pbus_value;
+ int ret;
+ +
+ if (!priv->firmware_version) + /* If restart happened in .probe(), no need to restart now */
+ ret = en8811h_load_firmware(phydev); + if (priv->mcu_needs_restart) {
+ else + ret = en8811h_restart_mcu(phydev);
+ ret = en8811h_restart_host(phydev);
+ if (ret < 0) + if (ret < 0)
+ return ret; + return ret;
+ + } else {
+ /* Because of mdio-lock, may have to wait for multiple loads */ + /* Next calls to .config_init() mcu needs to restart */
+ pollret = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1, + priv->mcu_needs_restart = true;
+ EN8811H_PHY_FW_STATUS, reg_value,
+ reg_value == EN8811H_PHY_READY,
+ 20000, 7500000, true);
+
+ ret = air_buckpbus_reg_read(phydev, EN8811H_FW_VERSION, &pbus_value);
+ if (ret < 0)
+ return ret;
+
+ if (pollret || !pbus_value) {
+ phydev_err(phydev, "Firmware not ready: 0x%x\n", reg_value);
+ return -ENODEV;
+ } + }
+ +
+ if (!priv->firmware_version) { + /* Select mode 1, the only mode supported.
+ phydev_info(phydev, "MD32 firmware version: %08x\n", pbus_value); + * Configures the SerDes for 2500Base-X with rate adaptation
+ priv->firmware_version = pbus_value; + */
+ } + ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_MCU_CMD_1,
+ + AIR_PHY_MCU_CMD_1_MODE1);
+ /* Select mode 1, the only mode supported */
+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_HOST_CMD_1,
+ AIR_PHY_HOST_CMD_1_MODE1);
+ if (ret < 0) + if (ret < 0)
+ return ret; + return ret;
+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_HOST_CMD_2, + ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_MCU_CMD_2,
+ AIR_PHY_HOST_CMD_2_MODE1); + AIR_PHY_MCU_CMD_2_MODE1);
+ if (ret < 0) + if (ret < 0)
+ return ret; + return ret;
+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_HOST_CMD_3, + ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_MCU_CMD_3,
+ AIR_PHY_HOST_CMD_3_MODE1); + AIR_PHY_MCU_CMD_3_MODE1);
+ if (ret < 0) + if (ret < 0)
+ return ret; + return ret;
+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_HOST_CMD_4, + ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_MCU_CMD_4,
+ AIR_PHY_HOST_CMD_4_MODE1); + AIR_PHY_MCU_CMD_4_MODE1);
+ if (ret < 0) + if (ret < 0)
+ return ret; + return ret;
+ +
+ /* Serdes polarity */ + /* Serdes polarity */
+ ret = air_buckpbus_reg_read(phydev, EN8811H_POLARITY, &pbus_value); + pbus_value = 0;
+ if (ret < 0)
+ return ret;
+ if (device_property_read_bool(dev, "airoha,pnswap-rx")) + if (device_property_read_bool(dev, "airoha,pnswap-rx"))
+ pbus_value |= EN8811H_POLARITY_RX_REVERSE; + pbus_value |= EN8811H_POLARITY_RX_REVERSE;
+ else + else
@ -919,7 +945,9 @@ Signed-off-by: Eric Woudstra <ericwouds@gmail.com>
+ pbus_value &= ~EN8811H_POLARITY_TX_NORMAL; + pbus_value &= ~EN8811H_POLARITY_TX_NORMAL;
+ else + else
+ pbus_value |= EN8811H_POLARITY_TX_NORMAL; + pbus_value |= EN8811H_POLARITY_TX_NORMAL;
+ ret = air_buckpbus_reg_write(phydev, EN8811H_POLARITY, pbus_value); + ret = air_buckpbus_reg_modify(phydev, EN8811H_POLARITY,
+ EN8811H_POLARITY_RX_REVERSE |
+ EN8811H_POLARITY_TX_NORMAL, pbus_value);
+ if (ret < 0) + if (ret < 0)
+ return ret; + return ret;
+ +
@ -930,14 +958,6 @@ Signed-off-by: Eric Woudstra <ericwouds@gmail.com>
+ return ret; + return ret;
+ } + }
+ +
+ ret = air_buckpbus_reg_read(phydev, EN8811H_GPIO_OUTPUT, &pbus_value);
+ if (ret < 0)
+ return ret;
+ pbus_value |= EN8811H_GPIO_OUTPUT_345;
+ ret = air_buckpbus_reg_write(phydev, EN8811H_GPIO_OUTPUT, pbus_value);
+ if (ret < 0)
+ return ret;
+
+ return 0; + return 0;
+} +}
+ +
@ -959,17 +979,21 @@ Signed-off-by: Eric Woudstra <ericwouds@gmail.com>
+static int en8811h_config_aneg(struct phy_device *phydev) +static int en8811h_config_aneg(struct phy_device *phydev)
+{ +{
+ bool changed = false; + bool changed = false;
+ int err, val; + int ret;
+ u32 adv;
+ +
+ val = 0; + if (phydev->autoneg == AUTONEG_DISABLE) {
+ if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, + phydev_warn(phydev, "Disabling autoneg is not supported\n");
+ phydev->advertising)) + return -EINVAL;
+ val |= MDIO_AN_10GBT_CTRL_ADV2_5G; + }
+ err = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL, +
+ MDIO_AN_10GBT_CTRL_ADV2_5G, val); + adv = linkmode_adv_to_mii_10gbt_adv_t(phydev->advertising);
+ if (err < 0) +
+ return err; + ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL,
+ if (err > 0) + MDIO_AN_10GBT_CTRL_ADV2_5G, adv);
+ if (ret < 0)
+ return ret;
+ if (ret > 0)
+ changed = true; + changed = true;
+ +
+ return __genphy_config_aneg(phydev, changed); + return __genphy_config_aneg(phydev, changed);
@ -991,6 +1015,7 @@ Signed-off-by: Eric Woudstra <ericwouds@gmail.com>
+ phydev->duplex = DUPLEX_UNKNOWN; + phydev->duplex = DUPLEX_UNKNOWN;
+ phydev->pause = 0; + phydev->pause = 0;
+ phydev->asym_pause = 0; + phydev->asym_pause = 0;
+ phydev->rate_matching = RATE_MATCH_PAUSE;
+ +
+ ret = genphy_read_master_slave(phydev); + ret = genphy_read_master_slave(phydev);
+ if (ret < 0) + if (ret < 0)
@ -1008,7 +1033,7 @@ Signed-off-by: Eric Woudstra <ericwouds@gmail.com>
+ phydev->lp_advertising, + phydev->lp_advertising,
+ pbus_value & EN8811H_2P5G_LPA_2P5G); + pbus_value & EN8811H_2P5G_LPA_2P5G);
+ +
+ if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete) + if (phydev->autoneg_complete)
+ phy_resolve_aneg_pause(phydev); + phy_resolve_aneg_pause(phydev);
+ +
+ if (!phydev->link) + if (!phydev->link)
@ -1030,8 +1055,7 @@ Signed-off-by: Eric Woudstra <ericwouds@gmail.com>
+ break; + break;
+ } + }
+ +
+ /* BUG in PHY firmware: MDIO_AN_10GBT_STAT_LP2_5G does not get set. + /* Firmware before version 24011202 has no vendor register 2P5G_LPA.
+ * Firmware before version 24011202 has no vendor register 2P5G_LPA.
+ * Assume link partner advertised it if connected at 2500Mbps. + * Assume link partner advertised it if connected at 2500Mbps.
+ */ + */
+ if (priv->firmware_version < 0x24011202) { + if (priv->firmware_version < 0x24011202) {
@ -1050,13 +1074,13 @@ Signed-off-by: Eric Woudstra <ericwouds@gmail.com>
+{ +{
+ int ret; + int ret;
+ +
+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_HOST_CMD_3, + ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_MCU_CMD_3,
+ AIR_PHY_HOST_CMD_3_DOCMD); + AIR_PHY_MCU_CMD_3_DOCMD);
+ if (ret < 0) + if (ret < 0)
+ return ret; + return ret;
+ +
+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_HOST_CMD_4, + ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_MCU_CMD_4,
+ AIR_PHY_HOST_CMD_4_INTCLR); + AIR_PHY_MCU_CMD_4_INTCLR);
+ if (ret < 0) + if (ret < 0)
+ return ret; + return ret;
+ +
@ -1105,6 +1129,7 @@ Signed-off-by: Eric Woudstra <ericwouds@gmail.com>
+ { PHY_ID_MATCH_MODEL(EN8811H_PHY_ID) }, + { PHY_ID_MATCH_MODEL(EN8811H_PHY_ID) },
+ { } + { }
+}; +};
+
+MODULE_DEVICE_TABLE(mdio, en8811h_tbl); +MODULE_DEVICE_TABLE(mdio, en8811h_tbl);
+MODULE_FIRMWARE(EN8811H_MD32_DM); +MODULE_FIRMWARE(EN8811H_MD32_DM);
+MODULE_FIRMWARE(EN8811H_MD32_DSP); +MODULE_FIRMWARE(EN8811H_MD32_DSP);

View File

@ -0,0 +1,47 @@
From 87c33315af380ca12a2e59ac94edad4fe0481b4c Mon Sep 17 00:00:00 2001
From: Dan Carpenter <dan.carpenter@linaro.org>
Date: Fri, 5 Apr 2024 13:08:59 +0300
Subject: [PATCH] net: phy: air_en8811h: fix some error codes
These error paths accidentally return "ret" which is zero/success
instead of the correct error code.
Fixes: 71e79430117d ("net: phy: air_en8811h: Add the Airoha EN8811H PHY driver")
Signed-off-by: Dan Carpenter <dan.carpenter@linaro.org>
Reviewed-by: Simon Horman <horms@kernel.org>
Link: https://lore.kernel.org/r/7ef2e230-dfb7-4a77-8973-9e5be1a99fc2@moroto.mountain
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
drivers/net/phy/air_en8811h.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
--- a/drivers/net/phy/air_en8811h.c
+++ b/drivers/net/phy/air_en8811h.c
@@ -272,11 +272,11 @@ static int __air_buckpbus_reg_read(struc
pbus_data_high = __phy_read(phydev, AIR_BPBUS_RD_DATA_HIGH);
if (pbus_data_high < 0)
- return ret;
+ return pbus_data_high;
pbus_data_low = __phy_read(phydev, AIR_BPBUS_RD_DATA_LOW);
if (pbus_data_low < 0)
- return ret;
+ return pbus_data_low;
*pbus_data = pbus_data_low | (pbus_data_high << 16);
return 0;
@@ -323,11 +323,11 @@ static int __air_buckpbus_reg_modify(str
pbus_data_high = __phy_read(phydev, AIR_BPBUS_RD_DATA_HIGH);
if (pbus_data_high < 0)
- return ret;
+ return pbus_data_high;
pbus_data_low = __phy_read(phydev, AIR_BPBUS_RD_DATA_LOW);
if (pbus_data_low < 0)
- return ret;
+ return pbus_data_low;
pbus_data_old = pbus_data_low | (pbus_data_high << 16);
pbus_data_new = (pbus_data_old & ~mask) | set;

View File

@ -16,8 +16,8 @@ Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
--- a/drivers/net/phy/Kconfig --- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig
@@ -69,9 +69,9 @@ config SFP @@ -74,9 +74,9 @@ config AIR_EN8811H_PHY
comment "MII PHY device drivers" Currently supports the Airoha EN8811H PHY.
config AMD_PHY config AMD_PHY
- tristate "AMD PHYs" - tristate "AMD PHYs"

View File

@ -0,0 +1,45 @@
From 9be9a00adfac8118b6d685e71696f83187308c66 Mon Sep 17 00:00:00 2001
Message-ID: <9be9a00adfac8118b6d685e71696f83187308c66.1715125851.git.daniel@makrotopia.org>
From: Daniel Golle <daniel@makrotopia.org>
Date: Tue, 7 May 2024 22:43:30 +0100
Subject: [PATCH net] net: phy: air_en8811h: reset netdev rules when LED is set
manually
To: Andrew Lunn <andrew@lunn.ch>,
Heiner Kallweit <hkallweit1@gmail.com>,
Russell King <linux@armlinux.org.uk>,
David S. Miller <davem@davemloft.net>,
Eric Dumazet <edumazet@google.com>,
Jakub Kicinski <kuba@kernel.org>,
Paolo Abeni <pabeni@redhat.com>,
SkyLake Huang <skylake.huang@mediatek.com>,
Eric Woudstra <ericwouds@gmail.com>,
netdev@vger.kernel.org,
linux-kernel@vger.kernel.org
Setting LED_OFF via the brightness_set should deactivate hw control,
so make sure netdev trigger rules also get cleared in that case.
Fixes: 71e79430117d ("net: phy: air_en8811h: Add the Airoha EN8811H PHY driver")
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
This is basically a stop-gap measure until unified LED handling has
been implemented accross all MediaTek and Airoha PHYs.
See also
https://patchwork.kernel.org/project/netdevbpf/patch/20240425023325.15586-3-SkyLake.Huang@mediatek.com/
drivers/net/phy/air_en8811h.c | 4 ++++
1 file changed, 4 insertions(+)
--- a/drivers/net/phy/air_en8811h.c
+++ b/drivers/net/phy/air_en8811h.c
@@ -544,6 +544,10 @@ static int air_hw_led_on_set(struct phy_
changed |= (priv->led[index].rules != 0);
+ /* clear netdev trigger rules in case LED_OFF has been set */
+ if (!on)
+ priv->led[index].rules = 0;
+
if (changed)
return phy_modify_mmd(phydev, MDIO_MMD_VEND2,
AIR_PHY_LED_ON(index),

View File

@ -0,0 +1,45 @@
From 9be9a00adfac8118b6d685e71696f83187308c66 Mon Sep 17 00:00:00 2001
Message-ID: <9be9a00adfac8118b6d685e71696f83187308c66.1715125851.git.daniel@makrotopia.org>
From: Daniel Golle <daniel@makrotopia.org>
Date: Tue, 7 May 2024 22:43:30 +0100
Subject: [PATCH net] net: phy: air_en8811h: reset netdev rules when LED is set
manually
To: Andrew Lunn <andrew@lunn.ch>,
Heiner Kallweit <hkallweit1@gmail.com>,
Russell King <linux@armlinux.org.uk>,
David S. Miller <davem@davemloft.net>,
Eric Dumazet <edumazet@google.com>,
Jakub Kicinski <kuba@kernel.org>,
Paolo Abeni <pabeni@redhat.com>,
SkyLake Huang <skylake.huang@mediatek.com>,
Eric Woudstra <ericwouds@gmail.com>,
netdev@vger.kernel.org,
linux-kernel@vger.kernel.org
Setting LED_OFF via the brightness_set should deactivate hw control,
so make sure netdev trigger rules also get cleared in that case.
Fixes: 71e79430117d ("net: phy: air_en8811h: Add the Airoha EN8811H PHY driver")
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
This is basically a stop-gap measure until unified LED handling has
been implemented accross all MediaTek and Airoha PHYs.
See also
https://patchwork.kernel.org/project/netdevbpf/patch/20240425023325.15586-3-SkyLake.Huang@mediatek.com/
drivers/net/phy/air_en8811h.c | 4 ++++
1 file changed, 4 insertions(+)
--- a/drivers/net/phy/air_en8811h.c
+++ b/drivers/net/phy/air_en8811h.c
@@ -544,6 +544,10 @@ static int air_hw_led_on_set(struct phy_
changed |= (priv->led[index].rules != 0);
+ /* clear netdev trigger rules in case LED_OFF has been set */
+ if (!on)
+ priv->led[index].rules = 0;
+
if (changed)
return phy_modify_mmd(phydev, MDIO_MMD_VEND2,
AIR_PHY_LED_ON(index),