mirror of
https://github.com/coolsnowwolf/lede.git
synced 2025-04-16 04:13:31 +00:00
generic: sync kernel 6.1 from upstream
This commit is contained in:
parent
2f7ba54f99
commit
b3e60d1892
@ -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
|
@ -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
|
@ -0,0 +1,36 @@
|
||||
From 8e7daa85641c9559c113f6b217bdc923397de77c Mon Sep 17 00:00:00 2001
|
||||
From: William Zhang <william.zhang@broadcom.com>
|
||||
Date: Thu, 22 Feb 2024 19:47:58 -0800
|
||||
Subject: [PATCH] mtd: rawnand: brcmnand: Support write protection setting from
|
||||
dts
|
||||
|
||||
The write protection feature is controlled by the module parameter wp_on
|
||||
with default set to enabled. But not all the board use this feature
|
||||
especially in BCMBCA broadband board. And module parameter is not
|
||||
sufficient as different board can have different option. Add a device
|
||||
tree property and allow this feature to be configured through the board
|
||||
dts on per board basis.
|
||||
|
||||
Signed-off-by: William Zhang <william.zhang@broadcom.com>
|
||||
Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
|
||||
Reviewed-by: Kamal Dasu <kamal.dasu@broadcom.com>
|
||||
Reviewed-by: David Regan <dregan@broadcom.com>
|
||||
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
|
||||
Link: https://lore.kernel.org/linux-mtd/20240223034758.13753-14-william.zhang@broadcom.com
|
||||
---
|
||||
drivers/mtd/nand/raw/brcmnand/brcmnand.c | 4 ++++
|
||||
1 file changed, 4 insertions(+)
|
||||
|
||||
--- a/drivers/mtd/nand/raw/brcmnand/brcmnand.c
|
||||
+++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c
|
||||
@@ -3188,6 +3188,10 @@ int brcmnand_probe(struct platform_devic
|
||||
/* Disable XOR addressing */
|
||||
brcmnand_rmw_reg(ctrl, BRCMNAND_CS_XOR, 0xff, 0, 0);
|
||||
|
||||
+ /* Check if the board connects the WP pin */
|
||||
+ if (of_property_read_bool(dn, "brcm,wp-not-connected"))
|
||||
+ wp_on = 0;
|
||||
+
|
||||
if (ctrl->features & BRCMNAND_HAS_WP) {
|
||||
/* Permanently disable write protection */
|
||||
if (wp_on == 2)
|
@ -10,7 +10,7 @@ This adds support for "linux,rootfs" binding that is used to mark flash
|
||||
partition containing rootfs. It's useful for devices using device tree
|
||||
that don't have bootloader passing root info in cmdline.
|
||||
|
||||
Signed-off-by: Rafa? Mi?ecki <rafal@milecki.pl>
|
||||
Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
|
||||
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
|
||||
Link: https://lore.kernel.org/linux-mtd/20221022211318.32009-2-zajec5@gmail.com
|
||||
---
|
||||
|
@ -0,0 +1,117 @@
|
||||
From 773bbe10449731c9525457873e0c2342e5cf883b Mon Sep 17 00:00:00 2001
|
||||
From: Michael Walle <michael@walle.cc>
|
||||
Date: Thu, 11 Aug 2022 00:06:53 +0200
|
||||
Subject: [PATCH] mtd: spi-nor: add generic flash driver
|
||||
|
||||
Our SFDP parsing is everything we need to support all basic operations
|
||||
of a flash device. If the flash isn't found in our in-kernel flash
|
||||
database, gracefully fall back to a driver described solely by its SFDP
|
||||
tables.
|
||||
|
||||
Signed-off-by: Michael Walle <michael@walle.cc>
|
||||
Signed-off-by: Tudor Ambarus <tudor.ambarus@microchip.com>
|
||||
Tested-by: Tudor Ambarus <tudor.ambarus@microchip.com>
|
||||
Reviewed-by: Takahiro Kuwano <Takahiro.Kuwano@infineon.com>
|
||||
Link: https://lore.kernel.org/r/20220810220654.1297699-7-michael@walle.cc
|
||||
---
|
||||
drivers/mtd/spi-nor/core.c | 26 ++++++++++++++++++++++++--
|
||||
drivers/mtd/spi-nor/core.h | 1 +
|
||||
drivers/mtd/spi-nor/sfdp.c | 27 +++++++++++++++++++++++++++
|
||||
3 files changed, 52 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/drivers/mtd/spi-nor/core.c
|
||||
+++ b/drivers/mtd/spi-nor/core.c
|
||||
@@ -1636,6 +1636,16 @@ static const struct spi_nor_manufacturer
|
||||
&spi_nor_xmc,
|
||||
};
|
||||
|
||||
+static const struct flash_info spi_nor_generic_flash = {
|
||||
+ .name = "spi-nor-generic",
|
||||
+ /*
|
||||
+ * JESD216 rev A doesn't specify the page size, therefore we need a
|
||||
+ * sane default.
|
||||
+ */
|
||||
+ .page_size = 256,
|
||||
+ .parse_sfdp = true,
|
||||
+};
|
||||
+
|
||||
static const struct flash_info *spi_nor_match_id(struct spi_nor *nor,
|
||||
const u8 *id)
|
||||
{
|
||||
@@ -1669,6 +1679,14 @@ static const struct flash_info *spi_nor_
|
||||
}
|
||||
|
||||
info = spi_nor_match_id(nor, id);
|
||||
+
|
||||
+ /* Fallback to a generic flash described only by its SFDP data. */
|
||||
+ if (!info) {
|
||||
+ ret = spi_nor_check_sfdp_signature(nor);
|
||||
+ if (!ret)
|
||||
+ info = &spi_nor_generic_flash;
|
||||
+ }
|
||||
+
|
||||
if (!info) {
|
||||
dev_err(nor->dev, "unrecognized JEDEC id bytes: %*ph\n",
|
||||
SPI_NOR_MAX_ID_LEN, id);
|
||||
@@ -2105,8 +2123,12 @@ static int spi_nor_select_pp(struct spi_
|
||||
* spi_nor_select_uniform_erase() - select optimum uniform erase type
|
||||
* @map: the erase map of the SPI NOR
|
||||
* @wanted_size: the erase type size to search for. Contains the value of
|
||||
- * info->sector_size or of the "small sector" size in case
|
||||
- * CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is defined.
|
||||
+ * info->sector_size, the "small sector" size in case
|
||||
+ * CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is defined or 0 if
|
||||
+ * there is no information about the sector size. The
|
||||
+ * latter is the case if the flash parameters are parsed
|
||||
+ * solely by SFDP, then the largest supported erase type
|
||||
+ * is selected.
|
||||
*
|
||||
* Once the optimum uniform sector erase command is found, disable all the
|
||||
* other.
|
||||
--- a/drivers/mtd/spi-nor/core.h
|
||||
+++ b/drivers/mtd/spi-nor/core.h
|
||||
@@ -708,6 +708,8 @@ int spi_nor_controller_ops_read_reg(stru
|
||||
int spi_nor_controller_ops_write_reg(struct spi_nor *nor, u8 opcode,
|
||||
const u8 *buf, size_t len);
|
||||
|
||||
+int spi_nor_check_sfdp_signature(struct spi_nor *nor);
|
||||
+
|
||||
static inline struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd)
|
||||
{
|
||||
return container_of(mtd, struct spi_nor, mtd);
|
||||
--- a/drivers/mtd/spi-nor/sfdp.c
|
||||
+++ b/drivers/mtd/spi-nor/sfdp.c
|
||||
@@ -1250,6 +1250,33 @@ static void spi_nor_post_sfdp_fixups(str
|
||||
}
|
||||
|
||||
/**
|
||||
+ * spi_nor_check_sfdp_signature() - check for a valid SFDP signature
|
||||
+ * @nor: pointer to a 'struct spi_nor'
|
||||
+ *
|
||||
+ * Used to detect if the flash supports the RDSFDP command as well as the
|
||||
+ * presence of a valid SFDP table.
|
||||
+ *
|
||||
+ * Return: 0 on success, -errno otherwise.
|
||||
+ */
|
||||
+int spi_nor_check_sfdp_signature(struct spi_nor *nor)
|
||||
+{
|
||||
+ u32 signature;
|
||||
+ int err;
|
||||
+
|
||||
+ /* Get the SFDP header. */
|
||||
+ err = spi_nor_read_sfdp_dma_unsafe(nor, 0, sizeof(signature),
|
||||
+ &signature);
|
||||
+ if (err < 0)
|
||||
+ return err;
|
||||
+
|
||||
+ /* Check the SFDP signature. */
|
||||
+ if (le32_to_cpu(signature) != SFDP_SIGNATURE)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
* spi_nor_parse_sfdp() - parse the Serial Flash Discoverable Parameters.
|
||||
* @nor: pointer to a 'struct spi_nor'
|
||||
*
|
@ -1,13 +1,13 @@
|
||||
From f7982c726e02001afc19052fe48f642dfcbc00b2 Mon Sep 17 00:00:00 2001
|
||||
From c67d90e058550403a3e6f9b05bfcdcfa12b1815c Mon Sep 17 00:00:00 2001
|
||||
From: Vincent Tremblay <vincent@vtremblay.dev>
|
||||
Date: Mon, 26 Dec 2022 21:10:37 -0500
|
||||
Subject: [PATCH 1/2] spidev: Add Silicon Labs EM3581 device compatible
|
||||
Date: Mon, 26 Dec 2022 21:35:48 -0500
|
||||
Subject: [PATCH] spidev: Add Silicon Labs EM3581 device compatible
|
||||
|
||||
Add compatible string for Silicon Labs EM3581 device.
|
||||
|
||||
Note: This patch is adapted from a patch submitted to the for-next branch (v6.3).
|
||||
|
||||
Signed-off-by: Vincent Tremblay <vincent@vtremblay.dev>
|
||||
Link: https://lore.kernel.org/r/20221227023550.569547-2-vincent@vtremblay.dev
|
||||
Signed-off-by: Mark Brown <broonie@kernel.org>
|
||||
---
|
||||
drivers/spi/spidev.c | 2 ++
|
||||
1 file changed, 2 insertions(+)
|
@ -1,13 +1,13 @@
|
||||
From 536581825219e97fa2ae0c4de35605d2f6311416 Mon Sep 17 00:00:00 2001
|
||||
From 6c9d1fd52956c3148e847a214bae9102b1811de5 Mon Sep 17 00:00:00 2001
|
||||
From: Vincent Tremblay <vincent@vtremblay.dev>
|
||||
Date: Tue, 27 Dec 2022 09:00:58 -0500
|
||||
Subject: [PATCH 2/2] spidev: Add Silicon Labs SI3210 device compatible
|
||||
Date: Tue, 27 Dec 2022 09:10:08 -0500
|
||||
Subject: [PATCH] spidev: Add Silicon Labs SI3210 device compatible
|
||||
|
||||
Add compatible string for Silicon Labs SI3210 device.
|
||||
|
||||
Note: This patch is adapted from a patch submitted to the for-next branch (v6.3).
|
||||
|
||||
Signed-off-by: Vincent Tremblay <vincent@vtremblay.dev>
|
||||
Link: https://lore.kernel.org/r/20221227141011.111410-2-vincent@vtremblay.dev
|
||||
Signed-off-by: Mark Brown <broonie@kernel.org>
|
||||
---
|
||||
drivers/spi/spidev.c | 2 ++
|
||||
1 file changed, 2 insertions(+)
|
@ -0,0 +1,33 @@
|
||||
From 1ecf9e390452e73a362ea7fbde8f3f0db83de856 Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Golle <daniel@makrotopia.org>
|
||||
Date: Thu, 22 Dec 2022 19:33:04 +0000
|
||||
Subject: [PATCH] mtd: ubi: wire-up parent MTD device
|
||||
|
||||
Wire up the device parent pointer of UBI devices to their lower MTD
|
||||
device, typically an MTD partition or whole-chip device.
|
||||
|
||||
The most noticeable change is that in sysfs, previously ubi devices
|
||||
would be could in /sys/devices/virtual/ubi while after this change they
|
||||
would be correctly attached to their parent MTD device, e.g.
|
||||
|
||||
/sys/devices/platform/1100d000.spi/spi_master/spi1/spi1.0/mtd/mtd2/ubi0.
|
||||
|
||||
Locating UBI devices using /sys/class/ubi/ of course still works as
|
||||
well.
|
||||
|
||||
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
Signed-off-by: Richard Weinberger <richard@nod.at>
|
||||
---
|
||||
drivers/mtd/ubi/build.c | 1 +
|
||||
1 file changed, 1 insertion(+)
|
||||
|
||||
--- a/drivers/mtd/ubi/build.c
|
||||
+++ b/drivers/mtd/ubi/build.c
|
||||
@@ -929,6 +929,7 @@ int ubi_attach_mtd_dev(struct mtd_info *
|
||||
ubi->dev.release = dev_release;
|
||||
ubi->dev.class = &ubi_class;
|
||||
ubi->dev.groups = ubi_dev_groups;
|
||||
+ ubi->dev.parent = &mtd->dev;
|
||||
|
||||
ubi->mtd = mtd;
|
||||
ubi->ubi_num = ubi_num;
|
@ -0,0 +1,49 @@
|
||||
From 05b8773ca33253ea562be145cf3145b05ef19f86 Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Golle <daniel@makrotopia.org>
|
||||
Date: Thu, 22 Dec 2022 19:33:31 +0000
|
||||
Subject: [PATCH] mtd: ubi: block: wire-up device parent
|
||||
|
||||
ubiblock devices were previously only identifyable by their name, but
|
||||
not connected to their parent UBI volume device e.g. in sysfs.
|
||||
Properly parent ubiblock device as descendant of a UBI volume device
|
||||
to reflect device model hierachy.
|
||||
|
||||
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
Signed-off-by: Richard Weinberger <richard@nod.at>
|
||||
---
|
||||
drivers/mtd/ubi/block.c | 2 +-
|
||||
drivers/mtd/ubi/kapi.c | 1 +
|
||||
include/linux/mtd/ubi.h | 1 +
|
||||
3 files changed, 3 insertions(+), 1 deletion(-)
|
||||
|
||||
--- a/drivers/mtd/ubi/block.c
|
||||
+++ b/drivers/mtd/ubi/block.c
|
||||
@@ -452,7 +452,7 @@ int ubiblock_create(struct ubi_volume_in
|
||||
list_add_tail(&dev->list, &ubiblock_devices);
|
||||
|
||||
/* Must be the last step: anyone can call file ops from now on */
|
||||
- ret = add_disk(dev->gd);
|
||||
+ ret = device_add_disk(vi->dev, dev->gd, NULL);
|
||||
if (ret)
|
||||
goto out_destroy_wq;
|
||||
|
||||
--- a/drivers/mtd/ubi/kapi.c
|
||||
+++ b/drivers/mtd/ubi/kapi.c
|
||||
@@ -79,6 +79,7 @@ void ubi_do_get_volume_info(struct ubi_d
|
||||
vi->name_len = vol->name_len;
|
||||
vi->name = vol->name;
|
||||
vi->cdev = vol->cdev.dev;
|
||||
+ vi->dev = &vol->dev;
|
||||
}
|
||||
|
||||
/**
|
||||
--- a/include/linux/mtd/ubi.h
|
||||
+++ b/include/linux/mtd/ubi.h
|
||||
@@ -110,6 +110,7 @@ struct ubi_volume_info {
|
||||
int name_len;
|
||||
const char *name;
|
||||
dev_t cdev;
|
||||
+ struct device *dev;
|
||||
};
|
||||
|
||||
/**
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,183 @@
|
||||
From e1fbfa4a995d42e02e22b0dff2f8b4fdee1504b3 Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Tue, 14 Nov 2023 15:08:42 +0100
|
||||
Subject: [PATCH 2/3] net: phy: aquantia: move MMD_VEND define to header
|
||||
|
||||
Move MMD_VEND define to header to clean things up and in preparation for
|
||||
firmware loading support that require some define placed in
|
||||
aquantia_main.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/aquantia/aquantia.h | 69 +++++++++++++++++++++++
|
||||
drivers/net/phy/aquantia/aquantia_hwmon.c | 14 -----
|
||||
drivers/net/phy/aquantia/aquantia_main.c | 55 ------------------
|
||||
3 files changed, 69 insertions(+), 69 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/aquantia/aquantia.h
|
||||
+++ b/drivers/net/phy/aquantia/aquantia.h
|
||||
@@ -9,6 +9,75 @@
|
||||
#include <linux/device.h>
|
||||
#include <linux/phy.h>
|
||||
|
||||
+/* Vendor specific 1, MDIO_MMD_VEND1 */
|
||||
+#define VEND1_GLOBAL_FW_ID 0x0020
|
||||
+#define VEND1_GLOBAL_FW_ID_MAJOR GENMASK(15, 8)
|
||||
+#define VEND1_GLOBAL_FW_ID_MINOR GENMASK(7, 0)
|
||||
+
|
||||
+/* The following registers all have similar layouts; first the registers... */
|
||||
+#define VEND1_GLOBAL_CFG_10M 0x0310
|
||||
+#define VEND1_GLOBAL_CFG_100M 0x031b
|
||||
+#define VEND1_GLOBAL_CFG_1G 0x031c
|
||||
+#define VEND1_GLOBAL_CFG_2_5G 0x031d
|
||||
+#define VEND1_GLOBAL_CFG_5G 0x031e
|
||||
+#define VEND1_GLOBAL_CFG_10G 0x031f
|
||||
+/* ...and now the fields */
|
||||
+#define VEND1_GLOBAL_CFG_RATE_ADAPT GENMASK(8, 7)
|
||||
+#define VEND1_GLOBAL_CFG_RATE_ADAPT_NONE 0
|
||||
+#define VEND1_GLOBAL_CFG_RATE_ADAPT_USX 1
|
||||
+#define VEND1_GLOBAL_CFG_RATE_ADAPT_PAUSE 2
|
||||
+
|
||||
+/* Vendor specific 1, MDIO_MMD_VEND2 */
|
||||
+#define VEND1_THERMAL_PROV_HIGH_TEMP_FAIL 0xc421
|
||||
+#define VEND1_THERMAL_PROV_LOW_TEMP_FAIL 0xc422
|
||||
+#define VEND1_THERMAL_PROV_HIGH_TEMP_WARN 0xc423
|
||||
+#define VEND1_THERMAL_PROV_LOW_TEMP_WARN 0xc424
|
||||
+#define VEND1_THERMAL_STAT1 0xc820
|
||||
+#define VEND1_THERMAL_STAT2 0xc821
|
||||
+#define VEND1_THERMAL_STAT2_VALID BIT(0)
|
||||
+#define VEND1_GENERAL_STAT1 0xc830
|
||||
+#define VEND1_GENERAL_STAT1_HIGH_TEMP_FAIL BIT(14)
|
||||
+#define VEND1_GENERAL_STAT1_LOW_TEMP_FAIL BIT(13)
|
||||
+#define VEND1_GENERAL_STAT1_HIGH_TEMP_WARN BIT(12)
|
||||
+#define VEND1_GENERAL_STAT1_LOW_TEMP_WARN BIT(11)
|
||||
+
|
||||
+#define VEND1_GLOBAL_GEN_STAT2 0xc831
|
||||
+#define VEND1_GLOBAL_GEN_STAT2_OP_IN_PROG BIT(15)
|
||||
+
|
||||
+#define VEND1_GLOBAL_RSVD_STAT1 0xc885
|
||||
+#define VEND1_GLOBAL_RSVD_STAT1_FW_BUILD_ID GENMASK(7, 4)
|
||||
+#define VEND1_GLOBAL_RSVD_STAT1_PROV_ID GENMASK(3, 0)
|
||||
+
|
||||
+#define VEND1_GLOBAL_RSVD_STAT9 0xc88d
|
||||
+#define VEND1_GLOBAL_RSVD_STAT9_MODE GENMASK(7, 0)
|
||||
+#define VEND1_GLOBAL_RSVD_STAT9_1000BT2 0x23
|
||||
+
|
||||
+#define VEND1_GLOBAL_INT_STD_STATUS 0xfc00
|
||||
+#define VEND1_GLOBAL_INT_VEND_STATUS 0xfc01
|
||||
+
|
||||
+#define VEND1_GLOBAL_INT_STD_MASK 0xff00
|
||||
+#define VEND1_GLOBAL_INT_STD_MASK_PMA1 BIT(15)
|
||||
+#define VEND1_GLOBAL_INT_STD_MASK_PMA2 BIT(14)
|
||||
+#define VEND1_GLOBAL_INT_STD_MASK_PCS1 BIT(13)
|
||||
+#define VEND1_GLOBAL_INT_STD_MASK_PCS2 BIT(12)
|
||||
+#define VEND1_GLOBAL_INT_STD_MASK_PCS3 BIT(11)
|
||||
+#define VEND1_GLOBAL_INT_STD_MASK_PHY_XS1 BIT(10)
|
||||
+#define VEND1_GLOBAL_INT_STD_MASK_PHY_XS2 BIT(9)
|
||||
+#define VEND1_GLOBAL_INT_STD_MASK_AN1 BIT(8)
|
||||
+#define VEND1_GLOBAL_INT_STD_MASK_AN2 BIT(7)
|
||||
+#define VEND1_GLOBAL_INT_STD_MASK_GBE BIT(6)
|
||||
+#define VEND1_GLOBAL_INT_STD_MASK_ALL BIT(0)
|
||||
+
|
||||
+#define VEND1_GLOBAL_INT_VEND_MASK 0xff01
|
||||
+#define VEND1_GLOBAL_INT_VEND_MASK_PMA BIT(15)
|
||||
+#define VEND1_GLOBAL_INT_VEND_MASK_PCS BIT(14)
|
||||
+#define VEND1_GLOBAL_INT_VEND_MASK_PHY_XS BIT(13)
|
||||
+#define VEND1_GLOBAL_INT_VEND_MASK_AN BIT(12)
|
||||
+#define VEND1_GLOBAL_INT_VEND_MASK_GBE BIT(11)
|
||||
+#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL1 BIT(2)
|
||||
+#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL2 BIT(1)
|
||||
+#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL3 BIT(0)
|
||||
+
|
||||
#if IS_REACHABLE(CONFIG_HWMON)
|
||||
int aqr_hwmon_probe(struct phy_device *phydev);
|
||||
#else
|
||||
--- a/drivers/net/phy/aquantia/aquantia_hwmon.c
|
||||
+++ b/drivers/net/phy/aquantia/aquantia_hwmon.c
|
||||
@@ -13,20 +13,6 @@
|
||||
|
||||
#include "aquantia.h"
|
||||
|
||||
-/* Vendor specific 1, MDIO_MMD_VEND2 */
|
||||
-#define VEND1_THERMAL_PROV_HIGH_TEMP_FAIL 0xc421
|
||||
-#define VEND1_THERMAL_PROV_LOW_TEMP_FAIL 0xc422
|
||||
-#define VEND1_THERMAL_PROV_HIGH_TEMP_WARN 0xc423
|
||||
-#define VEND1_THERMAL_PROV_LOW_TEMP_WARN 0xc424
|
||||
-#define VEND1_THERMAL_STAT1 0xc820
|
||||
-#define VEND1_THERMAL_STAT2 0xc821
|
||||
-#define VEND1_THERMAL_STAT2_VALID BIT(0)
|
||||
-#define VEND1_GENERAL_STAT1 0xc830
|
||||
-#define VEND1_GENERAL_STAT1_HIGH_TEMP_FAIL BIT(14)
|
||||
-#define VEND1_GENERAL_STAT1_LOW_TEMP_FAIL BIT(13)
|
||||
-#define VEND1_GENERAL_STAT1_HIGH_TEMP_WARN BIT(12)
|
||||
-#define VEND1_GENERAL_STAT1_LOW_TEMP_WARN BIT(11)
|
||||
-
|
||||
#if IS_REACHABLE(CONFIG_HWMON)
|
||||
|
||||
static umode_t aqr_hwmon_is_visible(const void *data,
|
||||
--- a/drivers/net/phy/aquantia/aquantia_main.c
|
||||
+++ b/drivers/net/phy/aquantia/aquantia_main.c
|
||||
@@ -89,61 +89,6 @@
|
||||
#define MDIO_C22EXT_STAT_SGMII_TX_FRAME_ALIGN_ERR 0xd31a
|
||||
#define MDIO_C22EXT_STAT_SGMII_TX_RUNT_FRAMES 0xd31b
|
||||
|
||||
-/* Vendor specific 1, MDIO_MMD_VEND1 */
|
||||
-#define VEND1_GLOBAL_FW_ID 0x0020
|
||||
-#define VEND1_GLOBAL_FW_ID_MAJOR GENMASK(15, 8)
|
||||
-#define VEND1_GLOBAL_FW_ID_MINOR GENMASK(7, 0)
|
||||
-
|
||||
-#define VEND1_GLOBAL_GEN_STAT2 0xc831
|
||||
-#define VEND1_GLOBAL_GEN_STAT2_OP_IN_PROG BIT(15)
|
||||
-
|
||||
-/* The following registers all have similar layouts; first the registers... */
|
||||
-#define VEND1_GLOBAL_CFG_10M 0x0310
|
||||
-#define VEND1_GLOBAL_CFG_100M 0x031b
|
||||
-#define VEND1_GLOBAL_CFG_1G 0x031c
|
||||
-#define VEND1_GLOBAL_CFG_2_5G 0x031d
|
||||
-#define VEND1_GLOBAL_CFG_5G 0x031e
|
||||
-#define VEND1_GLOBAL_CFG_10G 0x031f
|
||||
-/* ...and now the fields */
|
||||
-#define VEND1_GLOBAL_CFG_RATE_ADAPT GENMASK(8, 7)
|
||||
-#define VEND1_GLOBAL_CFG_RATE_ADAPT_NONE 0
|
||||
-#define VEND1_GLOBAL_CFG_RATE_ADAPT_USX 1
|
||||
-#define VEND1_GLOBAL_CFG_RATE_ADAPT_PAUSE 2
|
||||
-
|
||||
-#define VEND1_GLOBAL_RSVD_STAT1 0xc885
|
||||
-#define VEND1_GLOBAL_RSVD_STAT1_FW_BUILD_ID GENMASK(7, 4)
|
||||
-#define VEND1_GLOBAL_RSVD_STAT1_PROV_ID GENMASK(3, 0)
|
||||
-
|
||||
-#define VEND1_GLOBAL_RSVD_STAT9 0xc88d
|
||||
-#define VEND1_GLOBAL_RSVD_STAT9_MODE GENMASK(7, 0)
|
||||
-#define VEND1_GLOBAL_RSVD_STAT9_1000BT2 0x23
|
||||
-
|
||||
-#define VEND1_GLOBAL_INT_STD_STATUS 0xfc00
|
||||
-#define VEND1_GLOBAL_INT_VEND_STATUS 0xfc01
|
||||
-
|
||||
-#define VEND1_GLOBAL_INT_STD_MASK 0xff00
|
||||
-#define VEND1_GLOBAL_INT_STD_MASK_PMA1 BIT(15)
|
||||
-#define VEND1_GLOBAL_INT_STD_MASK_PMA2 BIT(14)
|
||||
-#define VEND1_GLOBAL_INT_STD_MASK_PCS1 BIT(13)
|
||||
-#define VEND1_GLOBAL_INT_STD_MASK_PCS2 BIT(12)
|
||||
-#define VEND1_GLOBAL_INT_STD_MASK_PCS3 BIT(11)
|
||||
-#define VEND1_GLOBAL_INT_STD_MASK_PHY_XS1 BIT(10)
|
||||
-#define VEND1_GLOBAL_INT_STD_MASK_PHY_XS2 BIT(9)
|
||||
-#define VEND1_GLOBAL_INT_STD_MASK_AN1 BIT(8)
|
||||
-#define VEND1_GLOBAL_INT_STD_MASK_AN2 BIT(7)
|
||||
-#define VEND1_GLOBAL_INT_STD_MASK_GBE BIT(6)
|
||||
-#define VEND1_GLOBAL_INT_STD_MASK_ALL BIT(0)
|
||||
-
|
||||
-#define VEND1_GLOBAL_INT_VEND_MASK 0xff01
|
||||
-#define VEND1_GLOBAL_INT_VEND_MASK_PMA BIT(15)
|
||||
-#define VEND1_GLOBAL_INT_VEND_MASK_PCS BIT(14)
|
||||
-#define VEND1_GLOBAL_INT_VEND_MASK_PHY_XS BIT(13)
|
||||
-#define VEND1_GLOBAL_INT_VEND_MASK_AN BIT(12)
|
||||
-#define VEND1_GLOBAL_INT_VEND_MASK_GBE BIT(11)
|
||||
-#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL1 BIT(2)
|
||||
-#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL2 BIT(1)
|
||||
-#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL3 BIT(0)
|
||||
-
|
||||
/* Sleep and timeout for checking if the Processor-Intensive
|
||||
* MDIO operation is finished
|
||||
*/
|
@ -0,0 +1,504 @@
|
||||
From e93984ebc1c82bd34f7a1b3391efaceee0a8ae96 Mon Sep 17 00:00:00 2001
|
||||
From: Robert Marko <robimarko@gmail.com>
|
||||
Date: Tue, 14 Nov 2023 15:08:43 +0100
|
||||
Subject: [PATCH 3/3] net: phy: aquantia: add firmware load support
|
||||
|
||||
Aquantia PHY-s require firmware to be loaded before they start operating.
|
||||
It can be automatically loaded in case when there is a SPI-NOR connected
|
||||
to Aquantia PHY-s or can be loaded from the host via MDIO.
|
||||
|
||||
This patch adds support for loading the firmware via MDIO as in most cases
|
||||
there is no SPI-NOR being used to save on cost.
|
||||
Firmware loading code itself is ported from mainline U-boot with cleanups.
|
||||
|
||||
The firmware has mixed values both in big and little endian.
|
||||
PHY core itself is big-endian but it expects values to be in little-endian.
|
||||
The firmware is little-endian but CRC-16 value for it is stored at the end
|
||||
of firmware in big-endian.
|
||||
|
||||
It seems the PHY does the conversion internally from firmware that is
|
||||
little-endian to the PHY that is big-endian on using the mailbox
|
||||
but mailbox returns a big-endian CRC-16 to verify the written data
|
||||
integrity.
|
||||
|
||||
Co-developed-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Signed-off-by: Robert Marko <robimarko@gmail.com>
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/aquantia/Kconfig | 1 +
|
||||
drivers/net/phy/aquantia/Makefile | 2 +-
|
||||
drivers/net/phy/aquantia/aquantia.h | 32 ++
|
||||
drivers/net/phy/aquantia/aquantia_firmware.c | 370 +++++++++++++++++++
|
||||
drivers/net/phy/aquantia/aquantia_main.c | 6 +
|
||||
5 files changed, 410 insertions(+), 1 deletion(-)
|
||||
create mode 100644 drivers/net/phy/aquantia/aquantia_firmware.c
|
||||
|
||||
--- a/drivers/net/phy/aquantia/Kconfig
|
||||
+++ b/drivers/net/phy/aquantia/Kconfig
|
||||
@@ -1,5 +1,6 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
config AQUANTIA_PHY
|
||||
tristate "Aquantia PHYs"
|
||||
+ select CRC_CCITT
|
||||
help
|
||||
Currently supports the Aquantia AQ1202, AQ2104, AQR105, AQR405
|
||||
--- a/drivers/net/phy/aquantia/Makefile
|
||||
+++ b/drivers/net/phy/aquantia/Makefile
|
||||
@@ -1,5 +1,5 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
-aquantia-objs += aquantia_main.o
|
||||
+aquantia-objs += aquantia_main.o aquantia_firmware.o
|
||||
ifdef CONFIG_HWMON
|
||||
aquantia-objs += aquantia_hwmon.o
|
||||
endif
|
||||
--- a/drivers/net/phy/aquantia/aquantia.h
|
||||
+++ b/drivers/net/phy/aquantia/aquantia.h
|
||||
@@ -10,10 +10,35 @@
|
||||
#include <linux/phy.h>
|
||||
|
||||
/* Vendor specific 1, MDIO_MMD_VEND1 */
|
||||
+#define VEND1_GLOBAL_SC 0x0
|
||||
+#define VEND1_GLOBAL_SC_SOFT_RESET BIT(15)
|
||||
+#define VEND1_GLOBAL_SC_LOW_POWER BIT(11)
|
||||
+
|
||||
#define VEND1_GLOBAL_FW_ID 0x0020
|
||||
#define VEND1_GLOBAL_FW_ID_MAJOR GENMASK(15, 8)
|
||||
#define VEND1_GLOBAL_FW_ID_MINOR GENMASK(7, 0)
|
||||
|
||||
+#define VEND1_GLOBAL_MAILBOX_INTERFACE1 0x0200
|
||||
+#define VEND1_GLOBAL_MAILBOX_INTERFACE1_EXECUTE BIT(15)
|
||||
+#define VEND1_GLOBAL_MAILBOX_INTERFACE1_WRITE BIT(14)
|
||||
+#define VEND1_GLOBAL_MAILBOX_INTERFACE1_CRC_RESET BIT(12)
|
||||
+#define VEND1_GLOBAL_MAILBOX_INTERFACE1_BUSY BIT(8)
|
||||
+
|
||||
+#define VEND1_GLOBAL_MAILBOX_INTERFACE2 0x0201
|
||||
+#define VEND1_GLOBAL_MAILBOX_INTERFACE3 0x0202
|
||||
+#define VEND1_GLOBAL_MAILBOX_INTERFACE3_MSW_ADDR_MASK GENMASK(15, 0)
|
||||
+#define VEND1_GLOBAL_MAILBOX_INTERFACE3_MSW_ADDR(x) FIELD_PREP(VEND1_GLOBAL_MAILBOX_INTERFACE3_MSW_ADDR_MASK, (u16)((x) >> 16))
|
||||
+#define VEND1_GLOBAL_MAILBOX_INTERFACE4 0x0203
|
||||
+#define VEND1_GLOBAL_MAILBOX_INTERFACE4_LSW_ADDR_MASK GENMASK(15, 2)
|
||||
+#define VEND1_GLOBAL_MAILBOX_INTERFACE4_LSW_ADDR(x) FIELD_PREP(VEND1_GLOBAL_MAILBOX_INTERFACE4_LSW_ADDR_MASK, (u16)(x))
|
||||
+
|
||||
+#define VEND1_GLOBAL_MAILBOX_INTERFACE5 0x0204
|
||||
+#define VEND1_GLOBAL_MAILBOX_INTERFACE5_MSW_DATA_MASK GENMASK(15, 0)
|
||||
+#define VEND1_GLOBAL_MAILBOX_INTERFACE5_MSW_DATA(x) FIELD_PREP(VEND1_GLOBAL_MAILBOX_INTERFACE5_MSW_DATA_MASK, (u16)((x) >> 16))
|
||||
+#define VEND1_GLOBAL_MAILBOX_INTERFACE6 0x0205
|
||||
+#define VEND1_GLOBAL_MAILBOX_INTERFACE6_LSW_DATA_MASK GENMASK(15, 0)
|
||||
+#define VEND1_GLOBAL_MAILBOX_INTERFACE6_LSW_DATA(x) FIELD_PREP(VEND1_GLOBAL_MAILBOX_INTERFACE6_LSW_DATA_MASK, (u16)(x))
|
||||
+
|
||||
/* The following registers all have similar layouts; first the registers... */
|
||||
#define VEND1_GLOBAL_CFG_10M 0x0310
|
||||
#define VEND1_GLOBAL_CFG_100M 0x031b
|
||||
@@ -28,6 +53,11 @@
|
||||
#define VEND1_GLOBAL_CFG_RATE_ADAPT_PAUSE 2
|
||||
|
||||
/* Vendor specific 1, MDIO_MMD_VEND2 */
|
||||
+#define VEND1_GLOBAL_CONTROL2 0xc001
|
||||
+#define VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_RST BIT(15)
|
||||
+#define VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_OVD BIT(6)
|
||||
+#define VEND1_GLOBAL_CONTROL2_UP_RUN_STALL BIT(0)
|
||||
+
|
||||
#define VEND1_THERMAL_PROV_HIGH_TEMP_FAIL 0xc421
|
||||
#define VEND1_THERMAL_PROV_LOW_TEMP_FAIL 0xc422
|
||||
#define VEND1_THERMAL_PROV_HIGH_TEMP_WARN 0xc423
|
||||
@@ -83,3 +113,5 @@ int aqr_hwmon_probe(struct phy_device *p
|
||||
#else
|
||||
static inline int aqr_hwmon_probe(struct phy_device *phydev) { return 0; }
|
||||
#endif
|
||||
+
|
||||
+int aqr_firmware_load(struct phy_device *phydev);
|
||||
--- /dev/null
|
||||
+++ b/drivers/net/phy/aquantia/aquantia_firmware.c
|
||||
@@ -0,0 +1,370 @@
|
||||
+// SPDX-License-Identifier: GPL-2.0
|
||||
+
|
||||
+#include <linux/bitfield.h>
|
||||
+#include <linux/of.h>
|
||||
+#include <linux/firmware.h>
|
||||
+#include <linux/crc-ccitt.h>
|
||||
+#include <linux/nvmem-consumer.h>
|
||||
+
|
||||
+#include <asm/unaligned.h>
|
||||
+
|
||||
+#include "aquantia.h"
|
||||
+
|
||||
+#define UP_RESET_SLEEP 100
|
||||
+
|
||||
+/* addresses of memory segments in the phy */
|
||||
+#define DRAM_BASE_ADDR 0x3FFE0000
|
||||
+#define IRAM_BASE_ADDR 0x40000000
|
||||
+
|
||||
+/* firmware image format constants */
|
||||
+#define VERSION_STRING_SIZE 0x40
|
||||
+#define VERSION_STRING_OFFSET 0x0200
|
||||
+/* primary offset is written at an offset from the start of the fw blob */
|
||||
+#define PRIMARY_OFFSET_OFFSET 0x8
|
||||
+/* primary offset needs to be then added to a base offset */
|
||||
+#define PRIMARY_OFFSET_SHIFT 12
|
||||
+#define PRIMARY_OFFSET(x) ((x) << PRIMARY_OFFSET_SHIFT)
|
||||
+#define HEADER_OFFSET 0x300
|
||||
+
|
||||
+struct aqr_fw_header {
|
||||
+ u32 padding;
|
||||
+ u8 iram_offset[3];
|
||||
+ u8 iram_size[3];
|
||||
+ u8 dram_offset[3];
|
||||
+ u8 dram_size[3];
|
||||
+} __packed;
|
||||
+
|
||||
+enum aqr_fw_src {
|
||||
+ AQR_FW_SRC_NVMEM = 0,
|
||||
+ AQR_FW_SRC_FS,
|
||||
+};
|
||||
+
|
||||
+static const char * const aqr_fw_src_string[] = {
|
||||
+ [AQR_FW_SRC_NVMEM] = "NVMEM",
|
||||
+ [AQR_FW_SRC_FS] = "FS",
|
||||
+};
|
||||
+
|
||||
+/* AQR firmware doesn't have fixed offsets for iram and dram section
|
||||
+ * but instead provide an header with the offset to use on reading
|
||||
+ * and parsing the firmware.
|
||||
+ *
|
||||
+ * AQR firmware can't be trusted and each offset is validated to be
|
||||
+ * not negative and be in the size of the firmware itself.
|
||||
+ */
|
||||
+static bool aqr_fw_validate_get(size_t size, size_t offset, size_t get_size)
|
||||
+{
|
||||
+ return offset + get_size <= size;
|
||||
+}
|
||||
+
|
||||
+static int aqr_fw_get_be16(const u8 *data, size_t offset, size_t size, u16 *value)
|
||||
+{
|
||||
+ if (!aqr_fw_validate_get(size, offset, sizeof(u16)))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ *value = get_unaligned_be16(data + offset);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int aqr_fw_get_le16(const u8 *data, size_t offset, size_t size, u16 *value)
|
||||
+{
|
||||
+ if (!aqr_fw_validate_get(size, offset, sizeof(u16)))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ *value = get_unaligned_le16(data + offset);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int aqr_fw_get_le24(const u8 *data, size_t offset, size_t size, u32 *value)
|
||||
+{
|
||||
+ if (!aqr_fw_validate_get(size, offset, sizeof(u8) * 3))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ *value = get_unaligned_le24(data + offset);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/* load data into the phy's memory */
|
||||
+static int aqr_fw_load_memory(struct phy_device *phydev, u32 addr,
|
||||
+ const u8 *data, size_t len)
|
||||
+{
|
||||
+ u16 crc = 0, up_crc;
|
||||
+ size_t pos;
|
||||
+
|
||||
+ /* PHY expect addr in LE */
|
||||
+ addr = (__force u32)cpu_to_le32(addr);
|
||||
+
|
||||
+ phy_write_mmd(phydev, MDIO_MMD_VEND1,
|
||||
+ VEND1_GLOBAL_MAILBOX_INTERFACE1,
|
||||
+ VEND1_GLOBAL_MAILBOX_INTERFACE1_CRC_RESET);
|
||||
+ phy_write_mmd(phydev, MDIO_MMD_VEND1,
|
||||
+ VEND1_GLOBAL_MAILBOX_INTERFACE3,
|
||||
+ VEND1_GLOBAL_MAILBOX_INTERFACE3_MSW_ADDR(addr));
|
||||
+ phy_write_mmd(phydev, MDIO_MMD_VEND1,
|
||||
+ VEND1_GLOBAL_MAILBOX_INTERFACE4,
|
||||
+ VEND1_GLOBAL_MAILBOX_INTERFACE4_LSW_ADDR(addr));
|
||||
+
|
||||
+ /* We assume and enforce the size to be word aligned.
|
||||
+ * If a firmware that is not word aligned is found, please report upstream.
|
||||
+ */
|
||||
+ for (pos = 0; pos < len; pos += sizeof(u32)) {
|
||||
+ u32 word;
|
||||
+
|
||||
+ /* FW data is always stored in little-endian */
|
||||
+ word = get_unaligned((const u32 *)(data + pos));
|
||||
+
|
||||
+ phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_MAILBOX_INTERFACE5,
|
||||
+ VEND1_GLOBAL_MAILBOX_INTERFACE5_MSW_DATA(word));
|
||||
+ phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_MAILBOX_INTERFACE6,
|
||||
+ VEND1_GLOBAL_MAILBOX_INTERFACE6_LSW_DATA(word));
|
||||
+
|
||||
+ phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_MAILBOX_INTERFACE1,
|
||||
+ VEND1_GLOBAL_MAILBOX_INTERFACE1_EXECUTE |
|
||||
+ VEND1_GLOBAL_MAILBOX_INTERFACE1_WRITE);
|
||||
+
|
||||
+ /* calculate CRC as we load data to the mailbox.
|
||||
+ * We convert word to big-endian as PHY is BE and mailbox will
|
||||
+ * return a BE CRC.
|
||||
+ */
|
||||
+ word = (__force u32)cpu_to_be32(word);
|
||||
+ crc = crc_ccitt_false(crc, (u8 *)&word, sizeof(word));
|
||||
+ }
|
||||
+
|
||||
+ up_crc = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_MAILBOX_INTERFACE2);
|
||||
+ if (crc != up_crc) {
|
||||
+ phydev_err(phydev, "CRC mismatch: calculated 0x%04x PHY 0x%04x\n",
|
||||
+ crc, up_crc);
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int aqr_fw_boot(struct phy_device *phydev, const u8 *data, size_t size,
|
||||
+ enum aqr_fw_src fw_src)
|
||||
+{
|
||||
+ u16 calculated_crc, read_crc, read_primary_offset;
|
||||
+ u32 iram_offset = 0, iram_size = 0;
|
||||
+ u32 dram_offset = 0, dram_size = 0;
|
||||
+ char version[VERSION_STRING_SIZE];
|
||||
+ u32 primary_offset = 0;
|
||||
+ int ret;
|
||||
+
|
||||
+ /* extract saved CRC at the end of the fw
|
||||
+ * CRC is saved in big-endian as PHY is BE
|
||||
+ */
|
||||
+ ret = aqr_fw_get_be16(data, size - sizeof(u16), size, &read_crc);
|
||||
+ if (ret) {
|
||||
+ phydev_err(phydev, "bad firmware CRC in firmware\n");
|
||||
+ return ret;
|
||||
+ }
|
||||
+ calculated_crc = crc_ccitt_false(0, data, size - sizeof(u16));
|
||||
+ if (read_crc != calculated_crc) {
|
||||
+ phydev_err(phydev, "bad firmware CRC: file 0x%04x calculated 0x%04x\n",
|
||||
+ read_crc, calculated_crc);
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ /* Get the primary offset to extract DRAM and IRAM sections. */
|
||||
+ ret = aqr_fw_get_le16(data, PRIMARY_OFFSET_OFFSET, size, &read_primary_offset);
|
||||
+ if (ret) {
|
||||
+ phydev_err(phydev, "bad primary offset in firmware\n");
|
||||
+ return ret;
|
||||
+ }
|
||||
+ primary_offset = PRIMARY_OFFSET(read_primary_offset);
|
||||
+
|
||||
+ /* Find the DRAM and IRAM sections within the firmware file.
|
||||
+ * Make sure the fw_header is correctly in the firmware.
|
||||
+ */
|
||||
+ if (!aqr_fw_validate_get(size, primary_offset + HEADER_OFFSET,
|
||||
+ sizeof(struct aqr_fw_header))) {
|
||||
+ phydev_err(phydev, "bad fw_header in firmware\n");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ /* offset are in LE and values needs to be converted to cpu endian */
|
||||
+ ret = aqr_fw_get_le24(data, primary_offset + HEADER_OFFSET +
|
||||
+ offsetof(struct aqr_fw_header, iram_offset),
|
||||
+ size, &iram_offset);
|
||||
+ if (ret) {
|
||||
+ phydev_err(phydev, "bad iram offset in firmware\n");
|
||||
+ return ret;
|
||||
+ }
|
||||
+ ret = aqr_fw_get_le24(data, primary_offset + HEADER_OFFSET +
|
||||
+ offsetof(struct aqr_fw_header, iram_size),
|
||||
+ size, &iram_size);
|
||||
+ if (ret) {
|
||||
+ phydev_err(phydev, "invalid iram size in firmware\n");
|
||||
+ return ret;
|
||||
+ }
|
||||
+ ret = aqr_fw_get_le24(data, primary_offset + HEADER_OFFSET +
|
||||
+ offsetof(struct aqr_fw_header, dram_offset),
|
||||
+ size, &dram_offset);
|
||||
+ if (ret) {
|
||||
+ phydev_err(phydev, "bad dram offset in firmware\n");
|
||||
+ return ret;
|
||||
+ }
|
||||
+ ret = aqr_fw_get_le24(data, primary_offset + HEADER_OFFSET +
|
||||
+ offsetof(struct aqr_fw_header, dram_size),
|
||||
+ size, &dram_size);
|
||||
+ if (ret) {
|
||||
+ phydev_err(phydev, "invalid dram size in firmware\n");
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ /* Increment the offset with the primary offset.
|
||||
+ * Validate iram/dram offset and size.
|
||||
+ */
|
||||
+ iram_offset += primary_offset;
|
||||
+ if (iram_size % sizeof(u32)) {
|
||||
+ phydev_err(phydev, "iram size if not aligned to word size. Please report this upstream!\n");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+ if (!aqr_fw_validate_get(size, iram_offset, iram_size)) {
|
||||
+ phydev_err(phydev, "invalid iram offset for iram size\n");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ dram_offset += primary_offset;
|
||||
+ if (dram_size % sizeof(u32)) {
|
||||
+ phydev_err(phydev, "dram size if not aligned to word size. Please report this upstream!\n");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+ if (!aqr_fw_validate_get(size, dram_offset, dram_size)) {
|
||||
+ phydev_err(phydev, "invalid iram offset for iram size\n");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ phydev_dbg(phydev, "primary %d IRAM offset=%d size=%d DRAM offset=%d size=%d\n",
|
||||
+ primary_offset, iram_offset, iram_size, dram_offset, dram_size);
|
||||
+
|
||||
+ if (!aqr_fw_validate_get(size, dram_offset + VERSION_STRING_OFFSET,
|
||||
+ VERSION_STRING_SIZE)) {
|
||||
+ phydev_err(phydev, "invalid version in firmware\n");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+ strscpy(version, (char *)data + dram_offset + VERSION_STRING_OFFSET,
|
||||
+ VERSION_STRING_SIZE);
|
||||
+ if (version[0] == '\0') {
|
||||
+ phydev_err(phydev, "invalid version in firmware\n");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+ phydev_info(phydev, "loading firmware version '%s' from '%s'\n", version,
|
||||
+ aqr_fw_src_string[fw_src]);
|
||||
+
|
||||
+ /* stall the microcprocessor */
|
||||
+ phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_CONTROL2,
|
||||
+ VEND1_GLOBAL_CONTROL2_UP_RUN_STALL | VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_OVD);
|
||||
+
|
||||
+ phydev_dbg(phydev, "loading DRAM 0x%08x from offset=%d size=%d\n",
|
||||
+ DRAM_BASE_ADDR, dram_offset, dram_size);
|
||||
+ ret = aqr_fw_load_memory(phydev, DRAM_BASE_ADDR, data + dram_offset,
|
||||
+ dram_size);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ phydev_dbg(phydev, "loading IRAM 0x%08x from offset=%d size=%d\n",
|
||||
+ IRAM_BASE_ADDR, iram_offset, iram_size);
|
||||
+ ret = aqr_fw_load_memory(phydev, IRAM_BASE_ADDR, data + iram_offset,
|
||||
+ iram_size);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ /* make sure soft reset and low power mode are clear */
|
||||
+ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_SC,
|
||||
+ VEND1_GLOBAL_SC_SOFT_RESET | VEND1_GLOBAL_SC_LOW_POWER);
|
||||
+
|
||||
+ /* Release the microprocessor. UP_RESET must be held for 100 usec. */
|
||||
+ phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_CONTROL2,
|
||||
+ VEND1_GLOBAL_CONTROL2_UP_RUN_STALL |
|
||||
+ VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_OVD |
|
||||
+ VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_RST);
|
||||
+ usleep_range(UP_RESET_SLEEP, UP_RESET_SLEEP * 2);
|
||||
+
|
||||
+ phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_CONTROL2,
|
||||
+ VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_OVD);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int aqr_firmware_load_nvmem(struct phy_device *phydev)
|
||||
+{
|
||||
+ struct nvmem_cell *cell;
|
||||
+ size_t size;
|
||||
+ u8 *buf;
|
||||
+ int ret;
|
||||
+
|
||||
+ cell = nvmem_cell_get(&phydev->mdio.dev, "firmware");
|
||||
+ if (IS_ERR(cell))
|
||||
+ return PTR_ERR(cell);
|
||||
+
|
||||
+ buf = nvmem_cell_read(cell, &size);
|
||||
+ if (IS_ERR(buf)) {
|
||||
+ ret = PTR_ERR(buf);
|
||||
+ goto exit;
|
||||
+ }
|
||||
+
|
||||
+ ret = aqr_fw_boot(phydev, buf, size, AQR_FW_SRC_NVMEM);
|
||||
+ if (ret)
|
||||
+ phydev_err(phydev, "firmware loading failed: %d\n", ret);
|
||||
+
|
||||
+ kfree(buf);
|
||||
+exit:
|
||||
+ nvmem_cell_put(cell);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static int aqr_firmware_load_fs(struct phy_device *phydev)
|
||||
+{
|
||||
+ struct device *dev = &phydev->mdio.dev;
|
||||
+ const struct firmware *fw;
|
||||
+ const char *fw_name;
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = of_property_read_string(dev->of_node, "firmware-name",
|
||||
+ &fw_name);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ ret = request_firmware(&fw, fw_name, dev);
|
||||
+ if (ret) {
|
||||
+ phydev_err(phydev, "failed to find FW file %s (%d)\n",
|
||||
+ fw_name, ret);
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ ret = aqr_fw_boot(phydev, fw->data, fw->size, AQR_FW_SRC_FS);
|
||||
+ if (ret)
|
||||
+ phydev_err(phydev, "firmware loading failed: %d\n", ret);
|
||||
+
|
||||
+ release_firmware(fw);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+int aqr_firmware_load(struct phy_device *phydev)
|
||||
+{
|
||||
+ int ret;
|
||||
+
|
||||
+ /* Check if the firmware is not already loaded by pooling
|
||||
+ * the current version returned by the PHY. If 0 is returned,
|
||||
+ * no firmware is loaded.
|
||||
+ */
|
||||
+ ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_FW_ID);
|
||||
+ if (ret > 0)
|
||||
+ goto exit;
|
||||
+
|
||||
+ ret = aqr_firmware_load_nvmem(phydev);
|
||||
+ if (!ret)
|
||||
+ goto exit;
|
||||
+
|
||||
+ ret = aqr_firmware_load_fs(phydev);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+exit:
|
||||
+ return 0;
|
||||
+}
|
||||
--- a/drivers/net/phy/aquantia/aquantia_main.c
|
||||
+++ b/drivers/net/phy/aquantia/aquantia_main.c
|
||||
@@ -656,11 +656,17 @@ static int aqr107_resume(struct phy_devi
|
||||
|
||||
static int aqr107_probe(struct phy_device *phydev)
|
||||
{
|
||||
+ int ret;
|
||||
+
|
||||
phydev->priv = devm_kzalloc(&phydev->mdio.dev,
|
||||
sizeof(struct aqr107_priv), GFP_KERNEL);
|
||||
if (!phydev->priv)
|
||||
return -ENOMEM;
|
||||
|
||||
+ ret = aqr_firmware_load(phydev);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
return aqr_hwmon_probe(phydev);
|
||||
}
|
||||
|
@ -0,0 +1,326 @@
|
||||
From 4e4aafcddbbfcdd6eed5780e190fcbfac8b4685a Mon Sep 17 00:00:00 2001
|
||||
From: Andrew Lunn <andrew@lunn.ch>
|
||||
Date: Mon, 9 Jan 2023 16:30:41 +0100
|
||||
Subject: [PATCH] net: mdio: Add dedicated C45 API to MDIO bus drivers
|
||||
|
||||
Currently C22 and C45 transactions are mixed over a combined API calls
|
||||
which make use of a special bit in the reg address to indicate if a
|
||||
C45 transaction should be performed. This makes it impossible to know
|
||||
if the bus driver actually supports C45. Additionally, many C22 only
|
||||
drivers don't return -EOPNOTSUPP when asked to perform a C45
|
||||
transaction, they mistaking perform a C22 transaction.
|
||||
|
||||
This is the first step to cleanly separate C22 from C45. To maintain
|
||||
backwards compatibility until all drivers which are capable of
|
||||
performing C45 are converted to this new API, the helper functions
|
||||
will fall back to the older API if the new API is not
|
||||
supported. Eventually this fallback will be removed.
|
||||
|
||||
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Signed-off-by: Michael Walle <michael@walle.cc>
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
---
|
||||
drivers/net/phy/mdio_bus.c | 189 +++++++++++++++++++++++++++++++++++++
|
||||
include/linux/mdio.h | 39 ++++----
|
||||
include/linux/phy.h | 5 +
|
||||
3 files changed, 214 insertions(+), 19 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/mdio_bus.c
|
||||
+++ b/drivers/net/phy/mdio_bus.c
|
||||
@@ -832,6 +832,100 @@ int __mdiobus_modify_changed(struct mii_
|
||||
EXPORT_SYMBOL_GPL(__mdiobus_modify_changed);
|
||||
|
||||
/**
|
||||
+ * __mdiobus_c45_read - Unlocked version of the mdiobus_c45_read function
|
||||
+ * @bus: the mii_bus struct
|
||||
+ * @addr: the phy address
|
||||
+ * @devad: device address to read
|
||||
+ * @regnum: register number to read
|
||||
+ *
|
||||
+ * Read a MDIO bus register. Caller must hold the mdio bus lock.
|
||||
+ *
|
||||
+ * NOTE: MUST NOT be called from interrupt context.
|
||||
+ */
|
||||
+int __mdiobus_c45_read(struct mii_bus *bus, int addr, int devad, u32 regnum)
|
||||
+{
|
||||
+ int retval;
|
||||
+
|
||||
+ lockdep_assert_held_once(&bus->mdio_lock);
|
||||
+
|
||||
+ if (bus->read_c45)
|
||||
+ retval = bus->read_c45(bus, addr, devad, regnum);
|
||||
+ else
|
||||
+ retval = bus->read(bus, addr, mdiobus_c45_addr(devad, regnum));
|
||||
+
|
||||
+ trace_mdio_access(bus, 1, addr, regnum, retval, retval);
|
||||
+ mdiobus_stats_acct(&bus->stats[addr], true, retval);
|
||||
+
|
||||
+ return retval;
|
||||
+}
|
||||
+EXPORT_SYMBOL(__mdiobus_c45_read);
|
||||
+
|
||||
+/**
|
||||
+ * __mdiobus_c45_write - Unlocked version of the mdiobus_write function
|
||||
+ * @bus: the mii_bus struct
|
||||
+ * @addr: the phy address
|
||||
+ * @devad: device address to read
|
||||
+ * @regnum: register number to write
|
||||
+ * @val: value to write to @regnum
|
||||
+ *
|
||||
+ * Write a MDIO bus register. Caller must hold the mdio bus lock.
|
||||
+ *
|
||||
+ * NOTE: MUST NOT be called from interrupt context.
|
||||
+ */
|
||||
+int __mdiobus_c45_write(struct mii_bus *bus, int addr, int devad, u32 regnum,
|
||||
+ u16 val)
|
||||
+{
|
||||
+ int err;
|
||||
+
|
||||
+ lockdep_assert_held_once(&bus->mdio_lock);
|
||||
+
|
||||
+ if (bus->write_c45)
|
||||
+ err = bus->write_c45(bus, addr, devad, regnum, val);
|
||||
+ else
|
||||
+ err = bus->write(bus, addr, mdiobus_c45_addr(devad, regnum),
|
||||
+ val);
|
||||
+
|
||||
+ trace_mdio_access(bus, 0, addr, regnum, val, err);
|
||||
+ mdiobus_stats_acct(&bus->stats[addr], false, err);
|
||||
+
|
||||
+ return err;
|
||||
+}
|
||||
+EXPORT_SYMBOL(__mdiobus_c45_write);
|
||||
+
|
||||
+/**
|
||||
+ * __mdiobus_c45_modify_changed - Unlocked version of the mdiobus_modify function
|
||||
+ * @bus: the mii_bus struct
|
||||
+ * @addr: the phy address
|
||||
+ * @devad: device address to read
|
||||
+ * @regnum: register number to modify
|
||||
+ * @mask: bit mask of bits to clear
|
||||
+ * @set: bit mask of bits to set
|
||||
+ *
|
||||
+ * Read, modify, and if any change, write the register value back to the
|
||||
+ * device. Any error returns a negative number.
|
||||
+ *
|
||||
+ * NOTE: MUST NOT be called from interrupt context.
|
||||
+ */
|
||||
+static int __mdiobus_c45_modify_changed(struct mii_bus *bus, int addr,
|
||||
+ int devad, u32 regnum, u16 mask,
|
||||
+ u16 set)
|
||||
+{
|
||||
+ int new, ret;
|
||||
+
|
||||
+ ret = __mdiobus_c45_read(bus, addr, devad, regnum);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ new = (ret & ~mask) | set;
|
||||
+ if (new == ret)
|
||||
+ return 0;
|
||||
+
|
||||
+ ret = __mdiobus_c45_write(bus, addr, devad, regnum, new);
|
||||
+
|
||||
+ return ret < 0 ? ret : 1;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
* mdiobus_read_nested - Nested version of the mdiobus_read function
|
||||
* @bus: the mii_bus struct
|
||||
* @addr: the phy address
|
||||
@@ -879,6 +973,29 @@ int mdiobus_read(struct mii_bus *bus, in
|
||||
EXPORT_SYMBOL(mdiobus_read);
|
||||
|
||||
/**
|
||||
+ * mdiobus_c45_read - Convenience function for reading a given MII mgmt register
|
||||
+ * @bus: the mii_bus struct
|
||||
+ * @addr: the phy address
|
||||
+ * @devad: device address to read
|
||||
+ * @regnum: register number to read
|
||||
+ *
|
||||
+ * NOTE: MUST NOT be called from interrupt context,
|
||||
+ * because the bus read/write functions may wait for an interrupt
|
||||
+ * to conclude the operation.
|
||||
+ */
|
||||
+int mdiobus_c45_read(struct mii_bus *bus, int addr, int devad, u32 regnum)
|
||||
+{
|
||||
+ int retval;
|
||||
+
|
||||
+ mutex_lock(&bus->mdio_lock);
|
||||
+ retval = __mdiobus_c45_read(bus, addr, devad, regnum);
|
||||
+ mutex_unlock(&bus->mdio_lock);
|
||||
+
|
||||
+ return retval;
|
||||
+}
|
||||
+EXPORT_SYMBOL(mdiobus_c45_read);
|
||||
+
|
||||
+/**
|
||||
* mdiobus_write_nested - Nested version of the mdiobus_write function
|
||||
* @bus: the mii_bus struct
|
||||
* @addr: the phy address
|
||||
@@ -928,6 +1045,31 @@ int mdiobus_write(struct mii_bus *bus, i
|
||||
EXPORT_SYMBOL(mdiobus_write);
|
||||
|
||||
/**
|
||||
+ * mdiobus_c45_write - Convenience function for writing a given MII mgmt register
|
||||
+ * @bus: the mii_bus struct
|
||||
+ * @addr: the phy address
|
||||
+ * @devad: device address to read
|
||||
+ * @regnum: register number to write
|
||||
+ * @val: value to write to @regnum
|
||||
+ *
|
||||
+ * NOTE: MUST NOT be called from interrupt context,
|
||||
+ * because the bus read/write functions may wait for an interrupt
|
||||
+ * to conclude the operation.
|
||||
+ */
|
||||
+int mdiobus_c45_write(struct mii_bus *bus, int addr, int devad, u32 regnum,
|
||||
+ u16 val)
|
||||
+{
|
||||
+ int err;
|
||||
+
|
||||
+ mutex_lock(&bus->mdio_lock);
|
||||
+ err = __mdiobus_c45_write(bus, addr, devad, regnum, val);
|
||||
+ mutex_unlock(&bus->mdio_lock);
|
||||
+
|
||||
+ return err;
|
||||
+}
|
||||
+EXPORT_SYMBOL(mdiobus_c45_write);
|
||||
+
|
||||
+/**
|
||||
* mdiobus_modify - Convenience function for modifying a given mdio device
|
||||
* register
|
||||
* @bus: the mii_bus struct
|
||||
@@ -949,6 +1091,30 @@ int mdiobus_modify(struct mii_bus *bus,
|
||||
EXPORT_SYMBOL_GPL(mdiobus_modify);
|
||||
|
||||
/**
|
||||
+ * mdiobus_c45_modify - Convenience function for modifying a given mdio device
|
||||
+ * register
|
||||
+ * @bus: the mii_bus struct
|
||||
+ * @addr: the phy address
|
||||
+ * @devad: device address to read
|
||||
+ * @regnum: register number to write
|
||||
+ * @mask: bit mask of bits to clear
|
||||
+ * @set: bit mask of bits to set
|
||||
+ */
|
||||
+int mdiobus_c45_modify(struct mii_bus *bus, int addr, int devad, u32 regnum,
|
||||
+ u16 mask, u16 set)
|
||||
+{
|
||||
+ int err;
|
||||
+
|
||||
+ mutex_lock(&bus->mdio_lock);
|
||||
+ err = __mdiobus_c45_modify_changed(bus, addr, devad, regnum,
|
||||
+ mask, set);
|
||||
+ mutex_unlock(&bus->mdio_lock);
|
||||
+
|
||||
+ return err < 0 ? err : 0;
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(mdiobus_c45_modify);
|
||||
+
|
||||
+/**
|
||||
* mdiobus_modify_changed - Convenience function for modifying a given mdio
|
||||
* device register and returning if it changed
|
||||
* @bus: the mii_bus struct
|
||||
@@ -971,6 +1137,29 @@ int mdiobus_modify_changed(struct mii_bu
|
||||
EXPORT_SYMBOL_GPL(mdiobus_modify_changed);
|
||||
|
||||
/**
|
||||
+ * mdiobus_c45_modify_changed - Convenience function for modifying a given mdio
|
||||
+ * device register and returning if it changed
|
||||
+ * @bus: the mii_bus struct
|
||||
+ * @addr: the phy address
|
||||
+ * @devad: device address to read
|
||||
+ * @regnum: register number to write
|
||||
+ * @mask: bit mask of bits to clear
|
||||
+ * @set: bit mask of bits to set
|
||||
+ */
|
||||
+int mdiobus_c45_modify_changed(struct mii_bus *bus, int devad, int addr,
|
||||
+ u32 regnum, u16 mask, u16 set)
|
||||
+{
|
||||
+ int err;
|
||||
+
|
||||
+ mutex_lock(&bus->mdio_lock);
|
||||
+ err = __mdiobus_c45_modify_changed(bus, addr, devad, regnum, mask, set);
|
||||
+ mutex_unlock(&bus->mdio_lock);
|
||||
+
|
||||
+ return err;
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(mdiobus_c45_modify_changed);
|
||||
+
|
||||
+/**
|
||||
* mdio_bus_match - determine if given MDIO driver supports the given
|
||||
* MDIO device
|
||||
* @dev: target MDIO device
|
||||
--- a/include/linux/mdio.h
|
||||
+++ b/include/linux/mdio.h
|
||||
@@ -423,6 +423,17 @@ int mdiobus_modify(struct mii_bus *bus,
|
||||
u16 set);
|
||||
int mdiobus_modify_changed(struct mii_bus *bus, int addr, u32 regnum,
|
||||
u16 mask, u16 set);
|
||||
+int __mdiobus_c45_read(struct mii_bus *bus, int addr, int devad, u32 regnum);
|
||||
+int mdiobus_c45_read(struct mii_bus *bus, int addr, int devad, u32 regnum);
|
||||
+int __mdiobus_c45_write(struct mii_bus *bus, int addr, int devad, u32 regnum,
|
||||
+ u16 val);
|
||||
+int mdiobus_c45_write(struct mii_bus *bus, int addr, int devad, u32 regnum,
|
||||
+ u16 val);
|
||||
+int mdiobus_c45_modify(struct mii_bus *bus, int addr, int devad, u32 regnum,
|
||||
+ u16 mask, u16 set);
|
||||
+
|
||||
+int mdiobus_c45_modify_changed(struct mii_bus *bus, int addr, int devad,
|
||||
+ u32 regnum, u16 mask, u16 set);
|
||||
|
||||
static inline int mdiodev_read(struct mdio_device *mdiodev, u32 regnum)
|
||||
{
|
||||
@@ -463,29 +474,19 @@ static inline u16 mdiobus_c45_devad(u32
|
||||
return FIELD_GET(MII_DEVADDR_C45_MASK, regnum);
|
||||
}
|
||||
|
||||
-static inline int __mdiobus_c45_read(struct mii_bus *bus, int prtad, int devad,
|
||||
- u16 regnum)
|
||||
-{
|
||||
- return __mdiobus_read(bus, prtad, mdiobus_c45_addr(devad, regnum));
|
||||
-}
|
||||
-
|
||||
-static inline int __mdiobus_c45_write(struct mii_bus *bus, int prtad, int devad,
|
||||
- u16 regnum, u16 val)
|
||||
-{
|
||||
- return __mdiobus_write(bus, prtad, mdiobus_c45_addr(devad, regnum),
|
||||
- val);
|
||||
-}
|
||||
-
|
||||
-static inline int mdiobus_c45_read(struct mii_bus *bus, int prtad, int devad,
|
||||
- u16 regnum)
|
||||
+static inline int mdiodev_c45_modify(struct mdio_device *mdiodev, int devad,
|
||||
+ u32 regnum, u16 mask, u16 set)
|
||||
{
|
||||
- return mdiobus_read(bus, prtad, mdiobus_c45_addr(devad, regnum));
|
||||
+ return mdiobus_c45_modify(mdiodev->bus, mdiodev->addr, devad, regnum,
|
||||
+ mask, set);
|
||||
}
|
||||
|
||||
-static inline int mdiobus_c45_write(struct mii_bus *bus, int prtad, int devad,
|
||||
- u16 regnum, u16 val)
|
||||
+static inline int mdiodev_c45_modify_changed(struct mdio_device *mdiodev,
|
||||
+ int devad, u32 regnum, u16 mask,
|
||||
+ u16 set)
|
||||
{
|
||||
- return mdiobus_write(bus, prtad, mdiobus_c45_addr(devad, regnum), val);
|
||||
+ return mdiobus_c45_modify_changed(mdiodev->bus, mdiodev->addr, devad,
|
||||
+ regnum, mask, set);
|
||||
}
|
||||
|
||||
int mdiobus_register_device(struct mdio_device *mdiodev);
|
||||
--- a/include/linux/phy.h
|
||||
+++ b/include/linux/phy.h
|
||||
@@ -364,6 +364,11 @@ struct mii_bus {
|
||||
int (*read)(struct mii_bus *bus, int addr, int regnum);
|
||||
/** @write: Perform a write transfer on the bus */
|
||||
int (*write)(struct mii_bus *bus, int addr, int regnum, u16 val);
|
||||
+ /** @read_c45: Perform a C45 read transfer on the bus */
|
||||
+ int (*read_c45)(struct mii_bus *bus, int addr, int devnum, int regnum);
|
||||
+ /** @write_c45: Perform a C45 write transfer on the bus */
|
||||
+ int (*write_c45)(struct mii_bus *bus, int addr, int devnum,
|
||||
+ int regnum, u16 val);
|
||||
/** @reset: Perform a reset of the bus */
|
||||
int (*reset)(struct mii_bus *bus);
|
||||
|
@ -0,0 +1,105 @@
|
||||
From 9a0e95e34e9c0a713ddfd48c3a88a20d2bdfd514 Mon Sep 17 00:00:00 2001
|
||||
From: Gabor Juhos <j4g8y7@gmail.com>
|
||||
Date: Fri, 11 Aug 2023 13:10:07 +0200
|
||||
Subject: [PATCH] net: phy: Introduce PSGMII PHY interface mode
|
||||
|
||||
The PSGMII interface is similar to QSGMII. The main difference
|
||||
is that the PSGMII interface combines five SGMII lines into a
|
||||
single link while in QSGMII only four lines are combined.
|
||||
|
||||
Similarly to the QSGMII, this interface mode might also needs
|
||||
special handling within the MAC driver.
|
||||
|
||||
It is commonly used by Qualcomm with their QCA807x PHY series and
|
||||
modern WiSoC-s.
|
||||
|
||||
Add definitions for the PHY layer to allow to express this type
|
||||
of connection between the MAC and PHY.
|
||||
|
||||
Signed-off-by: Gabor Juhos <j4g8y7@gmail.com>
|
||||
Signed-off-by: Robert Marko <robert.marko@sartura.hr>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
Documentation/networking/phy.rst | 4 ++++
|
||||
drivers/net/phy/phy-core.c | 2 ++
|
||||
drivers/net/phy/phylink.c | 3 +++
|
||||
include/linux/phy.h | 4 ++++
|
||||
4 files changed, 13 insertions(+)
|
||||
|
||||
--- a/Documentation/networking/phy.rst
|
||||
+++ b/Documentation/networking/phy.rst
|
||||
@@ -323,6 +323,10 @@ Some of the interface modes are describe
|
||||
contrast with the 1000BASE-X phy mode used for Clause 38 and 39 PMDs, this
|
||||
interface mode has different autonegotiation and only supports full duplex.
|
||||
|
||||
+``PHY_INTERFACE_MODE_PSGMII``
|
||||
+ This is the Penta SGMII mode, it is similar to QSGMII but it combines 5
|
||||
+ SGMII lines into a single link compared to 4 on QSGMII.
|
||||
+
|
||||
Pause frames / flow control
|
||||
===========================
|
||||
|
||||
--- a/drivers/net/phy/phy-core.c
|
||||
+++ b/drivers/net/phy/phy-core.c
|
||||
@@ -140,6 +140,8 @@ int phy_interface_num_ports(phy_interfac
|
||||
case PHY_INTERFACE_MODE_QSGMII:
|
||||
case PHY_INTERFACE_MODE_QUSGMII:
|
||||
return 4;
|
||||
+ case PHY_INTERFACE_MODE_PSGMII:
|
||||
+ return 5;
|
||||
case PHY_INTERFACE_MODE_MAX:
|
||||
WARN_ONCE(1, "PHY_INTERFACE_MODE_MAX isn't a valid interface mode");
|
||||
return 0;
|
||||
--- a/drivers/net/phy/phylink.c
|
||||
+++ b/drivers/net/phy/phylink.c
|
||||
@@ -187,6 +187,7 @@ static int phylink_interface_max_speed(p
|
||||
case PHY_INTERFACE_MODE_RGMII_RXID:
|
||||
case PHY_INTERFACE_MODE_RGMII_ID:
|
||||
case PHY_INTERFACE_MODE_RGMII:
|
||||
+ case PHY_INTERFACE_MODE_PSGMII:
|
||||
case PHY_INTERFACE_MODE_QSGMII:
|
||||
case PHY_INTERFACE_MODE_QUSGMII:
|
||||
case PHY_INTERFACE_MODE_SGMII:
|
||||
@@ -448,6 +449,7 @@ unsigned long phylink_get_capabilities(p
|
||||
case PHY_INTERFACE_MODE_RGMII_RXID:
|
||||
case PHY_INTERFACE_MODE_RGMII_ID:
|
||||
case PHY_INTERFACE_MODE_RGMII:
|
||||
+ case PHY_INTERFACE_MODE_PSGMII:
|
||||
case PHY_INTERFACE_MODE_QSGMII:
|
||||
case PHY_INTERFACE_MODE_QUSGMII:
|
||||
case PHY_INTERFACE_MODE_SGMII:
|
||||
@@ -814,6 +816,7 @@ static int phylink_parse_mode(struct phy
|
||||
|
||||
switch (pl->link_config.interface) {
|
||||
case PHY_INTERFACE_MODE_SGMII:
|
||||
+ case PHY_INTERFACE_MODE_PSGMII:
|
||||
case PHY_INTERFACE_MODE_QSGMII:
|
||||
case PHY_INTERFACE_MODE_QUSGMII:
|
||||
case PHY_INTERFACE_MODE_RGMII:
|
||||
--- a/include/linux/phy.h
|
||||
+++ b/include/linux/phy.h
|
||||
@@ -103,6 +103,7 @@ extern const int phy_10gbit_features_arr
|
||||
* @PHY_INTERFACE_MODE_XGMII: 10 gigabit media-independent interface
|
||||
* @PHY_INTERFACE_MODE_XLGMII:40 gigabit media-independent interface
|
||||
* @PHY_INTERFACE_MODE_MOCA: Multimedia over Coax
|
||||
+ * @PHY_INTERFACE_MODE_PSGMII: Penta SGMII
|
||||
* @PHY_INTERFACE_MODE_QSGMII: Quad SGMII
|
||||
* @PHY_INTERFACE_MODE_TRGMII: Turbo RGMII
|
||||
* @PHY_INTERFACE_MODE_100BASEX: 100 BaseX
|
||||
@@ -140,6 +141,7 @@ typedef enum {
|
||||
PHY_INTERFACE_MODE_XGMII,
|
||||
PHY_INTERFACE_MODE_XLGMII,
|
||||
PHY_INTERFACE_MODE_MOCA,
|
||||
+ PHY_INTERFACE_MODE_PSGMII,
|
||||
PHY_INTERFACE_MODE_QSGMII,
|
||||
PHY_INTERFACE_MODE_TRGMII,
|
||||
PHY_INTERFACE_MODE_100BASEX,
|
||||
@@ -247,6 +249,8 @@ static inline const char *phy_modes(phy_
|
||||
return "xlgmii";
|
||||
case PHY_INTERFACE_MODE_MOCA:
|
||||
return "moca";
|
||||
+ case PHY_INTERFACE_MODE_PSGMII:
|
||||
+ return "psgmii";
|
||||
case PHY_INTERFACE_MODE_QSGMII:
|
||||
return "qsgmii";
|
||||
case PHY_INTERFACE_MODE_TRGMII:
|
@ -0,0 +1,32 @@
|
||||
From a593a2fcfdfb92cfd0ffc54bc81b07e6bfaaaf46 Mon Sep 17 00:00:00 2001
|
||||
From: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
|
||||
Date: Thu, 16 Mar 2023 14:08:26 +0200
|
||||
Subject: [PATCH] net: phy: at803x: Replace of_gpio.h with what indeed is used
|
||||
|
||||
of_gpio.h in this driver is solely used as a proxy to other headers.
|
||||
This is incorrect usage of the of_gpio.h. Replace it .h with what
|
||||
indeed is used in the code.
|
||||
|
||||
Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/at803x.c | 3 +--
|
||||
1 file changed, 1 insertion(+), 2 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/at803x.c
|
||||
+++ b/drivers/net/phy/at803x.c
|
||||
@@ -13,12 +13,11 @@
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/ethtool_netlink.h>
|
||||
-#include <linux/of_gpio.h>
|
||||
#include <linux/bitfield.h>
|
||||
-#include <linux/gpio/consumer.h>
|
||||
#include <linux/regulator/of_regulator.h>
|
||||
#include <linux/regulator/driver.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
+#include <linux/of.h>
|
||||
#include <linux/phylink.h>
|
||||
#include <linux/sfp.h>
|
||||
#include <dt-bindings/net/qca-ar803x.h>
|
@ -0,0 +1,70 @@
|
||||
From 8b8bc13d89a7d23d14b0e041c73f037c9db997b1 Mon Sep 17 00:00:00 2001
|
||||
From: Luo Jie <quic_luoj@quicinc.com>
|
||||
Date: Sun, 16 Jul 2023 16:49:19 +0800
|
||||
Subject: [PATCH 1/6] net: phy: at803x: support qca8081
|
||||
genphy_c45_pma_read_abilities
|
||||
|
||||
qca8081 PHY supports to use genphy_c45_pma_read_abilities for
|
||||
getting the PHY features supported except for the autoneg ability
|
||||
|
||||
but autoneg ability exists in MDIO_STAT1 instead of MMD7.1, add it
|
||||
manually after calling genphy_c45_pma_read_abilities.
|
||||
|
||||
Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
|
||||
Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/at803x.c | 28 ++++++++++++++++++----------
|
||||
1 file changed, 18 insertions(+), 10 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/at803x.c
|
||||
+++ b/drivers/net/phy/at803x.c
|
||||
@@ -902,15 +902,6 @@ static int at803x_get_features(struct ph
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
- if (phydev->drv->phy_id == QCA8081_PHY_ID) {
|
||||
- err = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_NG_EXTABLE);
|
||||
- if (err < 0)
|
||||
- return err;
|
||||
-
|
||||
- linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported,
|
||||
- err & MDIO_PMA_NG_EXTABLE_2_5GBT);
|
||||
- }
|
||||
-
|
||||
if (phydev->drv->phy_id != ATH8031_PHY_ID)
|
||||
return 0;
|
||||
|
||||
@@ -1996,6 +1987,23 @@ static int qca808x_cable_test_get_status
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static int qca808x_get_features(struct phy_device *phydev)
|
||||
+{
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = genphy_c45_pma_read_abilities(phydev);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ /* The autoneg ability is not existed in bit3 of MMD7.1,
|
||||
+ * but it is supported by qca808x PHY, so we add it here
|
||||
+ * manually.
|
||||
+ */
|
||||
+ linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
static struct phy_driver at803x_driver[] = {
|
||||
{
|
||||
/* Qualcomm Atheros AR8035 */
|
||||
@@ -2163,7 +2171,7 @@ static struct phy_driver at803x_driver[]
|
||||
.set_tunable = at803x_set_tunable,
|
||||
.set_wol = at803x_set_wol,
|
||||
.get_wol = at803x_get_wol,
|
||||
- .get_features = at803x_get_features,
|
||||
+ .get_features = qca808x_get_features,
|
||||
.config_aneg = at803x_config_aneg,
|
||||
.suspend = genphy_suspend,
|
||||
.resume = genphy_resume,
|
@ -0,0 +1,73 @@
|
||||
From f3db55ae860a82e1224a909072783ef850e5d228 Mon Sep 17 00:00:00 2001
|
||||
From: Luo Jie <quic_luoj@quicinc.com>
|
||||
Date: Sun, 16 Jul 2023 16:49:20 +0800
|
||||
Subject: [PATCH 2/6] net: phy: at803x: merge qca8081 slave seed function
|
||||
|
||||
merge the seed enablement and seed value configuration into
|
||||
one function, since the random seed value is needed to be
|
||||
configured when the seed is enabled.
|
||||
|
||||
Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
|
||||
Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/at803x.c | 29 +++++++++--------------------
|
||||
1 file changed, 9 insertions(+), 20 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/at803x.c
|
||||
+++ b/drivers/net/phy/at803x.c
|
||||
@@ -1730,24 +1730,19 @@ static int qca808x_phy_fast_retrain_conf
|
||||
return 0;
|
||||
}
|
||||
|
||||
-static int qca808x_phy_ms_random_seed_set(struct phy_device *phydev)
|
||||
-{
|
||||
- u16 seed_value = prandom_u32_max(QCA808X_MASTER_SLAVE_SEED_RANGE);
|
||||
-
|
||||
- return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_LOCAL_SEED,
|
||||
- QCA808X_MASTER_SLAVE_SEED_CFG,
|
||||
- FIELD_PREP(QCA808X_MASTER_SLAVE_SEED_CFG, seed_value));
|
||||
-}
|
||||
-
|
||||
static int qca808x_phy_ms_seed_enable(struct phy_device *phydev, bool enable)
|
||||
{
|
||||
- u16 seed_enable = 0;
|
||||
+ u16 seed_value;
|
||||
|
||||
- if (enable)
|
||||
- seed_enable = QCA808X_MASTER_SLAVE_SEED_ENABLE;
|
||||
+ if (!enable)
|
||||
+ return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_LOCAL_SEED,
|
||||
+ QCA808X_MASTER_SLAVE_SEED_ENABLE, 0);
|
||||
|
||||
+ seed_value = prandom_u32_max(QCA808X_MASTER_SLAVE_SEED_RANGE);
|
||||
return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_LOCAL_SEED,
|
||||
- QCA808X_MASTER_SLAVE_SEED_ENABLE, seed_enable);
|
||||
+ QCA808X_MASTER_SLAVE_SEED_CFG | QCA808X_MASTER_SLAVE_SEED_ENABLE,
|
||||
+ FIELD_PREP(QCA808X_MASTER_SLAVE_SEED_CFG, seed_value) |
|
||||
+ QCA808X_MASTER_SLAVE_SEED_ENABLE);
|
||||
}
|
||||
|
||||
static int qca808x_config_init(struct phy_device *phydev)
|
||||
@@ -1771,12 +1766,7 @@ static int qca808x_config_init(struct ph
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
- /* Configure lower ramdom seed to make phy linked as slave mode */
|
||||
- ret = qca808x_phy_ms_random_seed_set(phydev);
|
||||
- if (ret)
|
||||
- return ret;
|
||||
-
|
||||
- /* Enable seed */
|
||||
+ /* Enable seed and configure lower ramdom seed to make phy linked as slave mode */
|
||||
ret = qca808x_phy_ms_seed_enable(phydev, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
@@ -1821,7 +1811,6 @@ static int qca808x_read_status(struct ph
|
||||
if (phydev->master_slave_state == MASTER_SLAVE_STATE_ERR) {
|
||||
qca808x_phy_ms_seed_enable(phydev, false);
|
||||
} else {
|
||||
- qca808x_phy_ms_random_seed_set(phydev);
|
||||
qca808x_phy_ms_seed_enable(phydev, true);
|
||||
}
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
From 7cc3209558002d95c0d45a1276ba4f5f741eec42 Mon Sep 17 00:00:00 2001
|
||||
From: Luo Jie <quic_luoj@quicinc.com>
|
||||
Date: Sun, 16 Jul 2023 16:49:21 +0800
|
||||
Subject: [PATCH 3/6] net: phy: at803x: enable qca8081 slave seed conditionally
|
||||
|
||||
qca8081 is the single port PHY, the slave prefer mode is used
|
||||
by default.
|
||||
|
||||
if the phy master perfer mode is configured, the slave seed
|
||||
configuration should not be enabled, since the slave seed
|
||||
enablement is for making PHY linked as slave mode easily.
|
||||
|
||||
disable slave seed if the master mode is preferred.
|
||||
|
||||
Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/at803x.c | 25 ++++++++++++++++++++-----
|
||||
1 file changed, 20 insertions(+), 5 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/at803x.c
|
||||
+++ b/drivers/net/phy/at803x.c
|
||||
@@ -1745,6 +1745,12 @@ static int qca808x_phy_ms_seed_enable(st
|
||||
QCA808X_MASTER_SLAVE_SEED_ENABLE);
|
||||
}
|
||||
|
||||
+static bool qca808x_is_prefer_master(struct phy_device *phydev)
|
||||
+{
|
||||
+ return (phydev->master_slave_get == MASTER_SLAVE_CFG_MASTER_FORCE) ||
|
||||
+ (phydev->master_slave_get == MASTER_SLAVE_CFG_MASTER_PREFERRED);
|
||||
+}
|
||||
+
|
||||
static int qca808x_config_init(struct phy_device *phydev)
|
||||
{
|
||||
int ret;
|
||||
@@ -1766,11 +1772,17 @@ static int qca808x_config_init(struct ph
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
- /* Enable seed and configure lower ramdom seed to make phy linked as slave mode */
|
||||
- ret = qca808x_phy_ms_seed_enable(phydev, true);
|
||||
- if (ret)
|
||||
+ ret = genphy_read_master_slave(phydev);
|
||||
+ if (ret < 0)
|
||||
return ret;
|
||||
|
||||
+ if (!qca808x_is_prefer_master(phydev)) {
|
||||
+ /* Enable seed and configure lower ramdom seed to make phy linked as slave mode */
|
||||
+ ret = qca808x_phy_ms_seed_enable(phydev, true);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
/* Configure adc threshold as 100mv for the link 10M */
|
||||
return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_ADC_THRESHOLD,
|
||||
QCA808X_ADC_THRESHOLD_MASK, QCA808X_ADC_THRESHOLD_100MV);
|
||||
@@ -1802,13 +1814,16 @@ static int qca808x_read_status(struct ph
|
||||
phydev->interface = PHY_INTERFACE_MODE_SGMII;
|
||||
} else {
|
||||
/* generate seed as a lower random value to make PHY linked as SLAVE easily,
|
||||
- * except for master/slave configuration fault detected.
|
||||
+ * except for master/slave configuration fault detected or the master mode
|
||||
+ * preferred.
|
||||
+ *
|
||||
* the reason for not putting this code into the function link_change_notify is
|
||||
* the corner case where the link partner is also the qca8081 PHY and the seed
|
||||
* value is configured as the same value, the link can't be up and no link change
|
||||
* occurs.
|
||||
*/
|
||||
- if (phydev->master_slave_state == MASTER_SLAVE_STATE_ERR) {
|
||||
+ if (phydev->master_slave_state == MASTER_SLAVE_STATE_ERR ||
|
||||
+ qca808x_is_prefer_master(phydev)) {
|
||||
qca808x_phy_ms_seed_enable(phydev, false);
|
||||
} else {
|
||||
qca808x_phy_ms_seed_enable(phydev, true);
|
@ -0,0 +1,48 @@
|
||||
From fea7cfb83d1a2782e39cd101dd44ed2548539de5 Mon Sep 17 00:00:00 2001
|
||||
From: Luo Jie <quic_luoj@quicinc.com>
|
||||
Date: Sun, 16 Jul 2023 16:49:22 +0800
|
||||
Subject: [PATCH 4/6] net: phy: at803x: support qca8081 1G chip type
|
||||
|
||||
The qca8081 1G chip version does not support 2.5 capability, which
|
||||
is distinguished from qca8081 2.5G chip according to the bit0 of
|
||||
register mmd7.0x901d, the 1G version chip also has the same PHY ID
|
||||
as the normal qca8081 2.5G chip.
|
||||
|
||||
Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
|
||||
Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/at803x.c | 15 +++++++++++++++
|
||||
1 file changed, 15 insertions(+)
|
||||
|
||||
--- a/drivers/net/phy/at803x.c
|
||||
+++ b/drivers/net/phy/at803x.c
|
||||
@@ -272,6 +272,10 @@
|
||||
#define QCA808X_CDT_STATUS_STAT_OPEN 2
|
||||
#define QCA808X_CDT_STATUS_STAT_SHORT 3
|
||||
|
||||
+/* QCA808X 1G chip type */
|
||||
+#define QCA808X_PHY_MMD7_CHIP_TYPE 0x901d
|
||||
+#define QCA808X_PHY_CHIP_TYPE_1G BIT(0)
|
||||
+
|
||||
MODULE_DESCRIPTION("Qualcomm Atheros AR803x and QCA808X PHY driver");
|
||||
MODULE_AUTHOR("Matus Ujhelyi");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -2005,6 +2009,17 @@ static int qca808x_get_features(struct p
|
||||
*/
|
||||
linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported);
|
||||
|
||||
+ /* As for the qca8081 1G version chip, the 2500baseT ability is also
|
||||
+ * existed in the bit0 of MMD1.21, we need to remove it manually if
|
||||
+ * it is the qca8081 1G chip according to the bit0 of MMD7.0x901d.
|
||||
+ */
|
||||
+ ret = phy_read_mmd(phydev, MDIO_MMD_AN, QCA808X_PHY_MMD7_CHIP_TYPE);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ if (QCA808X_PHY_CHIP_TYPE_1G & ret)
|
||||
+ linkmode_clear_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported);
|
||||
+
|
||||
return 0;
|
||||
}
|
||||
|
@ -0,0 +1,98 @@
|
||||
From df9401ff3e6eeaa42bfb06761967f1b71f5afce7 Mon Sep 17 00:00:00 2001
|
||||
From: Luo Jie <quic_luoj@quicinc.com>
|
||||
Date: Sun, 16 Jul 2023 16:49:23 +0800
|
||||
Subject: [PATCH 5/6] net: phy: at803x: remove qca8081 1G fast retrain and
|
||||
slave seed config
|
||||
|
||||
The fast retrain and slave seed configs are only applicable when the 2.5G
|
||||
ability is supported.
|
||||
|
||||
Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/at803x.c | 50 +++++++++++++++++++++++++---------------
|
||||
1 file changed, 32 insertions(+), 18 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/at803x.c
|
||||
+++ b/drivers/net/phy/at803x.c
|
||||
@@ -1755,6 +1755,11 @@ static bool qca808x_is_prefer_master(str
|
||||
(phydev->master_slave_get == MASTER_SLAVE_CFG_MASTER_PREFERRED);
|
||||
}
|
||||
|
||||
+static bool qca808x_has_fast_retrain_or_slave_seed(struct phy_device *phydev)
|
||||
+{
|
||||
+ return linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported);
|
||||
+}
|
||||
+
|
||||
static int qca808x_config_init(struct phy_device *phydev)
|
||||
{
|
||||
int ret;
|
||||
@@ -1771,20 +1776,24 @@ static int qca808x_config_init(struct ph
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
- /* Config the fast retrain for the link 2500M */
|
||||
- ret = qca808x_phy_fast_retrain_config(phydev);
|
||||
- if (ret)
|
||||
- return ret;
|
||||
-
|
||||
- ret = genphy_read_master_slave(phydev);
|
||||
- if (ret < 0)
|
||||
- return ret;
|
||||
-
|
||||
- if (!qca808x_is_prefer_master(phydev)) {
|
||||
- /* Enable seed and configure lower ramdom seed to make phy linked as slave mode */
|
||||
- ret = qca808x_phy_ms_seed_enable(phydev, true);
|
||||
+ if (qca808x_has_fast_retrain_or_slave_seed(phydev)) {
|
||||
+ /* Config the fast retrain for the link 2500M */
|
||||
+ ret = qca808x_phy_fast_retrain_config(phydev);
|
||||
if (ret)
|
||||
return ret;
|
||||
+
|
||||
+ ret = genphy_read_master_slave(phydev);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ if (!qca808x_is_prefer_master(phydev)) {
|
||||
+ /* Enable seed and configure lower ramdom seed to make phy
|
||||
+ * linked as slave mode.
|
||||
+ */
|
||||
+ ret = qca808x_phy_ms_seed_enable(phydev, true);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ }
|
||||
}
|
||||
|
||||
/* Configure adc threshold as 100mv for the link 10M */
|
||||
@@ -1826,11 +1835,13 @@ static int qca808x_read_status(struct ph
|
||||
* value is configured as the same value, the link can't be up and no link change
|
||||
* occurs.
|
||||
*/
|
||||
- if (phydev->master_slave_state == MASTER_SLAVE_STATE_ERR ||
|
||||
- qca808x_is_prefer_master(phydev)) {
|
||||
- qca808x_phy_ms_seed_enable(phydev, false);
|
||||
- } else {
|
||||
- qca808x_phy_ms_seed_enable(phydev, true);
|
||||
+ if (qca808x_has_fast_retrain_or_slave_seed(phydev)) {
|
||||
+ if (phydev->master_slave_state == MASTER_SLAVE_STATE_ERR ||
|
||||
+ qca808x_is_prefer_master(phydev)) {
|
||||
+ qca808x_phy_ms_seed_enable(phydev, false);
|
||||
+ } else {
|
||||
+ qca808x_phy_ms_seed_enable(phydev, true);
|
||||
+ }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1845,7 +1856,10 @@ static int qca808x_soft_reset(struct phy
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
- return qca808x_phy_ms_seed_enable(phydev, true);
|
||||
+ if (qca808x_has_fast_retrain_or_slave_seed(phydev))
|
||||
+ ret = qca808x_phy_ms_seed_enable(phydev, true);
|
||||
+
|
||||
+ return ret;
|
||||
}
|
||||
|
||||
static bool qca808x_cdt_fault_length_valid(int cdt_code)
|
@ -0,0 +1,55 @@
|
||||
From 723970affdd8766fa0d91cd34bf2ffc861538b5f Mon Sep 17 00:00:00 2001
|
||||
From: Luo Jie <quic_luoj@quicinc.com>
|
||||
Date: Sun, 16 Jul 2023 16:49:24 +0800
|
||||
Subject: [PATCH 6/6] net: phy: at803x: add qca8081 fifo reset on the link
|
||||
changed
|
||||
|
||||
The qca8081 sgmii fifo needs to be reset on link down and
|
||||
released on the link up in case of any abnormal issue
|
||||
such as the packet blocked on the PHY.
|
||||
|
||||
Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/at803x.c | 14 ++++++++++++++
|
||||
1 file changed, 14 insertions(+)
|
||||
|
||||
--- a/drivers/net/phy/at803x.c
|
||||
+++ b/drivers/net/phy/at803x.c
|
||||
@@ -276,6 +276,9 @@
|
||||
#define QCA808X_PHY_MMD7_CHIP_TYPE 0x901d
|
||||
#define QCA808X_PHY_CHIP_TYPE_1G BIT(0)
|
||||
|
||||
+#define QCA8081_PHY_SERDES_MMD1_FIFO_CTRL 0x9072
|
||||
+#define QCA8081_PHY_FIFO_RSTN BIT(11)
|
||||
+
|
||||
MODULE_DESCRIPTION("Qualcomm Atheros AR803x and QCA808X PHY driver");
|
||||
MODULE_AUTHOR("Matus Ujhelyi");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -2037,6 +2040,16 @@ static int qca808x_get_features(struct p
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static void qca808x_link_change_notify(struct phy_device *phydev)
|
||||
+{
|
||||
+ /* Assert interface sgmii fifo on link down, deassert it on link up,
|
||||
+ * the interface device address is always phy address added by 1.
|
||||
+ */
|
||||
+ mdiobus_c45_modify_changed(phydev->mdio.bus, phydev->mdio.addr + 1,
|
||||
+ MDIO_MMD_PMAPMD, QCA8081_PHY_SERDES_MMD1_FIFO_CTRL,
|
||||
+ QCA8081_PHY_FIFO_RSTN, phydev->link ? QCA8081_PHY_FIFO_RSTN : 0);
|
||||
+}
|
||||
+
|
||||
static struct phy_driver at803x_driver[] = {
|
||||
{
|
||||
/* Qualcomm Atheros AR8035 */
|
||||
@@ -2213,6 +2226,7 @@ static struct phy_driver at803x_driver[]
|
||||
.soft_reset = qca808x_soft_reset,
|
||||
.cable_test_start = qca808x_cable_test_start,
|
||||
.cable_test_get_status = qca808x_cable_test_get_status,
|
||||
+ .link_change_notify = qca808x_link_change_notify,
|
||||
}, };
|
||||
|
||||
module_phy_driver(at803x_driver);
|
@ -0,0 +1,69 @@
|
||||
From 6a3b8c573b5a152a6aa7a0b54c5e18b84c6ba6f5 Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Fri, 8 Dec 2023 15:51:49 +0100
|
||||
Subject: [PATCH 02/13] net: phy: at803x: move disable WOL to specific at8031
|
||||
probe
|
||||
|
||||
Move the WOL disable call to specific at8031 probe to make at803x_probe
|
||||
more generic and drop extra check for PHY ID.
|
||||
|
||||
Keep the same previous behaviour by first calling at803x_probe and then
|
||||
disabling WOL.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/at803x.c | 27 +++++++++++++++++----------
|
||||
1 file changed, 17 insertions(+), 10 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/at803x.c
|
||||
+++ b/drivers/net/phy/at803x.c
|
||||
@@ -886,15 +886,6 @@ static int at803x_probe(struct phy_devic
|
||||
priv->is_fiber = true;
|
||||
break;
|
||||
}
|
||||
-
|
||||
- /* Disable WoL in 1588 register which is enabled
|
||||
- * by default
|
||||
- */
|
||||
- ret = phy_modify_mmd(phydev, MDIO_MMD_PCS,
|
||||
- AT803X_PHY_MMD3_WOL_CTRL,
|
||||
- AT803X_WOL_EN, 0);
|
||||
- if (ret)
|
||||
- return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -1591,6 +1582,22 @@ static int at803x_cable_test_start(struc
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static int at8031_probe(struct phy_device *phydev)
|
||||
+{
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = at803x_probe(phydev);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ /* Disable WoL in 1588 register which is enabled
|
||||
+ * by default
|
||||
+ */
|
||||
+ return phy_modify_mmd(phydev, MDIO_MMD_PCS,
|
||||
+ AT803X_PHY_MMD3_WOL_CTRL,
|
||||
+ AT803X_WOL_EN, 0);
|
||||
+}
|
||||
+
|
||||
static int qca83xx_config_init(struct phy_device *phydev)
|
||||
{
|
||||
u8 switch_revision;
|
||||
@@ -2092,7 +2099,7 @@ static struct phy_driver at803x_driver[]
|
||||
PHY_ID_MATCH_EXACT(ATH8031_PHY_ID),
|
||||
.name = "Qualcomm Atheros AR8031/AR8033",
|
||||
.flags = PHY_POLL_CABLE_TEST,
|
||||
- .probe = at803x_probe,
|
||||
+ .probe = at8031_probe,
|
||||
.config_init = at803x_config_init,
|
||||
.config_aneg = at803x_config_aneg,
|
||||
.soft_reset = genphy_soft_reset,
|
@ -0,0 +1,129 @@
|
||||
From 07b1ad83b9ed6db1735ba10baf67b7a565ac0cef Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Fri, 8 Dec 2023 15:51:50 +0100
|
||||
Subject: [PATCH 03/13] net: phy: at803x: raname hw_stats functions to qca83xx
|
||||
specific name
|
||||
|
||||
The function and the struct related to hw_stats were specific to qca83xx
|
||||
PHY but were called following the convention in the driver of calling
|
||||
everything with at803x prefix.
|
||||
|
||||
To better organize the code, rename these function a more specific name
|
||||
to better describe that they are specific to 83xx PHY family.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/at803x.c | 44 ++++++++++++++++++++--------------------
|
||||
1 file changed, 22 insertions(+), 22 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/at803x.c
|
||||
+++ b/drivers/net/phy/at803x.c
|
||||
@@ -295,7 +295,7 @@ struct at803x_hw_stat {
|
||||
enum stat_access_type access_type;
|
||||
};
|
||||
|
||||
-static struct at803x_hw_stat at803x_hw_stats[] = {
|
||||
+static struct at803x_hw_stat qca83xx_hw_stats[] = {
|
||||
{ "phy_idle_errors", 0xa, GENMASK(7, 0), PHY},
|
||||
{ "phy_receive_errors", 0x15, GENMASK(15, 0), PHY},
|
||||
{ "eee_wake_errors", 0x16, GENMASK(15, 0), MMD},
|
||||
@@ -311,7 +311,7 @@ struct at803x_priv {
|
||||
bool is_1000basex;
|
||||
struct regulator_dev *vddio_rdev;
|
||||
struct regulator_dev *vddh_rdev;
|
||||
- u64 stats[ARRAY_SIZE(at803x_hw_stats)];
|
||||
+ u64 stats[ARRAY_SIZE(qca83xx_hw_stats)];
|
||||
};
|
||||
|
||||
struct at803x_context {
|
||||
@@ -529,24 +529,24 @@ static void at803x_get_wol(struct phy_de
|
||||
wol->wolopts |= WAKE_MAGIC;
|
||||
}
|
||||
|
||||
-static int at803x_get_sset_count(struct phy_device *phydev)
|
||||
+static int qca83xx_get_sset_count(struct phy_device *phydev)
|
||||
{
|
||||
- return ARRAY_SIZE(at803x_hw_stats);
|
||||
+ return ARRAY_SIZE(qca83xx_hw_stats);
|
||||
}
|
||||
|
||||
-static void at803x_get_strings(struct phy_device *phydev, u8 *data)
|
||||
+static void qca83xx_get_strings(struct phy_device *phydev, u8 *data)
|
||||
{
|
||||
int i;
|
||||
|
||||
- for (i = 0; i < ARRAY_SIZE(at803x_hw_stats); i++) {
|
||||
+ for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++) {
|
||||
strscpy(data + i * ETH_GSTRING_LEN,
|
||||
- at803x_hw_stats[i].string, ETH_GSTRING_LEN);
|
||||
+ qca83xx_hw_stats[i].string, ETH_GSTRING_LEN);
|
||||
}
|
||||
}
|
||||
|
||||
-static u64 at803x_get_stat(struct phy_device *phydev, int i)
|
||||
+static u64 qca83xx_get_stat(struct phy_device *phydev, int i)
|
||||
{
|
||||
- struct at803x_hw_stat stat = at803x_hw_stats[i];
|
||||
+ struct at803x_hw_stat stat = qca83xx_hw_stats[i];
|
||||
struct at803x_priv *priv = phydev->priv;
|
||||
int val;
|
||||
u64 ret;
|
||||
@@ -567,13 +567,13 @@ static u64 at803x_get_stat(struct phy_de
|
||||
return ret;
|
||||
}
|
||||
|
||||
-static void at803x_get_stats(struct phy_device *phydev,
|
||||
- struct ethtool_stats *stats, u64 *data)
|
||||
+static void qca83xx_get_stats(struct phy_device *phydev,
|
||||
+ struct ethtool_stats *stats, u64 *data)
|
||||
{
|
||||
int i;
|
||||
|
||||
- for (i = 0; i < ARRAY_SIZE(at803x_hw_stats); i++)
|
||||
- data[i] = at803x_get_stat(phydev, i);
|
||||
+ for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++)
|
||||
+ data[i] = qca83xx_get_stat(phydev, i);
|
||||
}
|
||||
|
||||
static int at803x_suspend(struct phy_device *phydev)
|
||||
@@ -2175,9 +2175,9 @@ static struct phy_driver at803x_driver[]
|
||||
.flags = PHY_IS_INTERNAL,
|
||||
.config_init = qca83xx_config_init,
|
||||
.soft_reset = genphy_soft_reset,
|
||||
- .get_sset_count = at803x_get_sset_count,
|
||||
- .get_strings = at803x_get_strings,
|
||||
- .get_stats = at803x_get_stats,
|
||||
+ .get_sset_count = qca83xx_get_sset_count,
|
||||
+ .get_strings = qca83xx_get_strings,
|
||||
+ .get_stats = qca83xx_get_stats,
|
||||
.suspend = qca83xx_suspend,
|
||||
.resume = qca83xx_resume,
|
||||
}, {
|
||||
@@ -2191,9 +2191,9 @@ static struct phy_driver at803x_driver[]
|
||||
.flags = PHY_IS_INTERNAL,
|
||||
.config_init = qca83xx_config_init,
|
||||
.soft_reset = genphy_soft_reset,
|
||||
- .get_sset_count = at803x_get_sset_count,
|
||||
- .get_strings = at803x_get_strings,
|
||||
- .get_stats = at803x_get_stats,
|
||||
+ .get_sset_count = qca83xx_get_sset_count,
|
||||
+ .get_strings = qca83xx_get_strings,
|
||||
+ .get_stats = qca83xx_get_stats,
|
||||
.suspend = qca83xx_suspend,
|
||||
.resume = qca83xx_resume,
|
||||
}, {
|
||||
@@ -2207,9 +2207,9 @@ static struct phy_driver at803x_driver[]
|
||||
.flags = PHY_IS_INTERNAL,
|
||||
.config_init = qca83xx_config_init,
|
||||
.soft_reset = genphy_soft_reset,
|
||||
- .get_sset_count = at803x_get_sset_count,
|
||||
- .get_strings = at803x_get_strings,
|
||||
- .get_stats = at803x_get_stats,
|
||||
+ .get_sset_count = qca83xx_get_sset_count,
|
||||
+ .get_strings = qca83xx_get_strings,
|
||||
+ .get_stats = qca83xx_get_stats,
|
||||
.suspend = qca83xx_suspend,
|
||||
.resume = qca83xx_resume,
|
||||
}, {
|
@ -0,0 +1,155 @@
|
||||
From d43cff3f82336c0bd965ea552232d9f4ddac71a6 Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Fri, 8 Dec 2023 15:51:51 +0100
|
||||
Subject: [PATCH 04/13] net: phy: at803x: move qca83xx specific check in
|
||||
dedicated functions
|
||||
|
||||
Rework qca83xx specific check to dedicated function to tidy things up
|
||||
and drop useless phy_id check.
|
||||
|
||||
Also drop an useless link_change_notify for QCA8337 as it did nothing an
|
||||
returned early.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/at803x.c | 68 ++++++++++++++++++++++------------------
|
||||
1 file changed, 37 insertions(+), 31 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/at803x.c
|
||||
+++ b/drivers/net/phy/at803x.c
|
||||
@@ -1623,27 +1623,26 @@ static int qca83xx_config_init(struct ph
|
||||
break;
|
||||
}
|
||||
|
||||
+ /* Following original QCA sourcecode set port to prefer master */
|
||||
+ phy_set_bits(phydev, MII_CTRL1000, CTL1000_PREFER_MASTER);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int qca8327_config_init(struct phy_device *phydev)
|
||||
+{
|
||||
/* QCA8327 require DAC amplitude adjustment for 100m set to +6%.
|
||||
* Disable on init and enable only with 100m speed following
|
||||
* qca original source code.
|
||||
*/
|
||||
- if (phydev->drv->phy_id == QCA8327_A_PHY_ID ||
|
||||
- phydev->drv->phy_id == QCA8327_B_PHY_ID)
|
||||
- at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL,
|
||||
- QCA8327_DEBUG_MANU_CTRL_EN, 0);
|
||||
+ at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL,
|
||||
+ QCA8327_DEBUG_MANU_CTRL_EN, 0);
|
||||
|
||||
- /* Following original QCA sourcecode set port to prefer master */
|
||||
- phy_set_bits(phydev, MII_CTRL1000, CTL1000_PREFER_MASTER);
|
||||
-
|
||||
- return 0;
|
||||
+ return qca83xx_config_init(phydev);
|
||||
}
|
||||
|
||||
static void qca83xx_link_change_notify(struct phy_device *phydev)
|
||||
{
|
||||
- /* QCA8337 doesn't require DAC Amplitude adjustement */
|
||||
- if (phydev->drv->phy_id == QCA8337_PHY_ID)
|
||||
- return;
|
||||
-
|
||||
/* Set DAC Amplitude adjustment to +6% for 100m on link running */
|
||||
if (phydev->state == PHY_RUNNING) {
|
||||
if (phydev->speed == SPEED_100)
|
||||
@@ -1686,19 +1685,6 @@ static int qca83xx_resume(struct phy_dev
|
||||
|
||||
static int qca83xx_suspend(struct phy_device *phydev)
|
||||
{
|
||||
- u16 mask = 0;
|
||||
-
|
||||
- /* Only QCA8337 support actual suspend.
|
||||
- * QCA8327 cause port unreliability when phy suspend
|
||||
- * is set.
|
||||
- */
|
||||
- if (phydev->drv->phy_id == QCA8337_PHY_ID) {
|
||||
- genphy_suspend(phydev);
|
||||
- } else {
|
||||
- mask |= ~(BMCR_SPEED1000 | BMCR_FULLDPLX);
|
||||
- phy_modify(phydev, MII_BMCR, mask, 0);
|
||||
- }
|
||||
-
|
||||
at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_GREEN,
|
||||
AT803X_DEBUG_GATE_CLK_IN1000, 0);
|
||||
|
||||
@@ -1709,6 +1695,27 @@ static int qca83xx_suspend(struct phy_de
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static int qca8337_suspend(struct phy_device *phydev)
|
||||
+{
|
||||
+ /* Only QCA8337 support actual suspend. */
|
||||
+ genphy_suspend(phydev);
|
||||
+
|
||||
+ return qca83xx_suspend(phydev);
|
||||
+}
|
||||
+
|
||||
+static int qca8327_suspend(struct phy_device *phydev)
|
||||
+{
|
||||
+ u16 mask = 0;
|
||||
+
|
||||
+ /* QCA8327 cause port unreliability when phy suspend
|
||||
+ * is set.
|
||||
+ */
|
||||
+ mask |= ~(BMCR_SPEED1000 | BMCR_FULLDPLX);
|
||||
+ phy_modify(phydev, MII_BMCR, mask, 0);
|
||||
+
|
||||
+ return qca83xx_suspend(phydev);
|
||||
+}
|
||||
+
|
||||
static int qca808x_phy_fast_retrain_config(struct phy_device *phydev)
|
||||
{
|
||||
int ret;
|
||||
@@ -2170,7 +2177,6 @@ static struct phy_driver at803x_driver[]
|
||||
.phy_id_mask = QCA8K_PHY_ID_MASK,
|
||||
.name = "Qualcomm Atheros 8337 internal PHY",
|
||||
/* PHY_GBIT_FEATURES */
|
||||
- .link_change_notify = qca83xx_link_change_notify,
|
||||
.probe = at803x_probe,
|
||||
.flags = PHY_IS_INTERNAL,
|
||||
.config_init = qca83xx_config_init,
|
||||
@@ -2178,7 +2184,7 @@ static struct phy_driver at803x_driver[]
|
||||
.get_sset_count = qca83xx_get_sset_count,
|
||||
.get_strings = qca83xx_get_strings,
|
||||
.get_stats = qca83xx_get_stats,
|
||||
- .suspend = qca83xx_suspend,
|
||||
+ .suspend = qca8337_suspend,
|
||||
.resume = qca83xx_resume,
|
||||
}, {
|
||||
/* QCA8327-A from switch QCA8327-AL1A */
|
||||
@@ -2189,12 +2195,12 @@ static struct phy_driver at803x_driver[]
|
||||
.link_change_notify = qca83xx_link_change_notify,
|
||||
.probe = at803x_probe,
|
||||
.flags = PHY_IS_INTERNAL,
|
||||
- .config_init = qca83xx_config_init,
|
||||
+ .config_init = qca8327_config_init,
|
||||
.soft_reset = genphy_soft_reset,
|
||||
.get_sset_count = qca83xx_get_sset_count,
|
||||
.get_strings = qca83xx_get_strings,
|
||||
.get_stats = qca83xx_get_stats,
|
||||
- .suspend = qca83xx_suspend,
|
||||
+ .suspend = qca8327_suspend,
|
||||
.resume = qca83xx_resume,
|
||||
}, {
|
||||
/* QCA8327-B from switch QCA8327-BL1A */
|
||||
@@ -2205,12 +2211,12 @@ static struct phy_driver at803x_driver[]
|
||||
.link_change_notify = qca83xx_link_change_notify,
|
||||
.probe = at803x_probe,
|
||||
.flags = PHY_IS_INTERNAL,
|
||||
- .config_init = qca83xx_config_init,
|
||||
+ .config_init = qca8327_config_init,
|
||||
.soft_reset = genphy_soft_reset,
|
||||
.get_sset_count = qca83xx_get_sset_count,
|
||||
.get_strings = qca83xx_get_strings,
|
||||
.get_stats = qca83xx_get_stats,
|
||||
- .suspend = qca83xx_suspend,
|
||||
+ .suspend = qca8327_suspend,
|
||||
.resume = qca83xx_resume,
|
||||
}, {
|
||||
/* Qualcomm QCA8081 */
|
@ -0,0 +1,94 @@
|
||||
From 900eef75cc5018e149c52fe305c9c3fe424c52a7 Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Fri, 8 Dec 2023 15:51:52 +0100
|
||||
Subject: [PATCH 05/13] net: phy: at803x: move specific DT option for at8031 to
|
||||
specific probe
|
||||
|
||||
Move specific DT options for at8031 to specific probe to tidy things up
|
||||
and make at803x_parse_dt more generic.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/at803x.c | 55 ++++++++++++++++++++++------------------
|
||||
1 file changed, 31 insertions(+), 24 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/at803x.c
|
||||
+++ b/drivers/net/phy/at803x.c
|
||||
@@ -825,30 +825,6 @@ static int at803x_parse_dt(struct phy_de
|
||||
}
|
||||
}
|
||||
|
||||
- /* Only supported on AR8031/AR8033, the AR8030/AR8035 use strapping
|
||||
- * options.
|
||||
- */
|
||||
- if (phydev->drv->phy_id == ATH8031_PHY_ID) {
|
||||
- if (of_property_read_bool(node, "qca,keep-pll-enabled"))
|
||||
- priv->flags |= AT803X_KEEP_PLL_ENABLED;
|
||||
-
|
||||
- ret = at8031_register_regulators(phydev);
|
||||
- if (ret < 0)
|
||||
- return ret;
|
||||
-
|
||||
- ret = devm_regulator_get_enable_optional(&phydev->mdio.dev,
|
||||
- "vddio");
|
||||
- if (ret) {
|
||||
- phydev_err(phydev, "failed to get VDDIO regulator\n");
|
||||
- return ret;
|
||||
- }
|
||||
-
|
||||
- /* Only AR8031/8033 support 1000Base-X for SFP modules */
|
||||
- ret = phy_sfp_probe(phydev, &at803x_sfp_ops);
|
||||
- if (ret < 0)
|
||||
- return ret;
|
||||
- }
|
||||
-
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1582,6 +1558,30 @@ static int at803x_cable_test_start(struc
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static int at8031_parse_dt(struct phy_device *phydev)
|
||||
+{
|
||||
+ struct device_node *node = phydev->mdio.dev.of_node;
|
||||
+ struct at803x_priv *priv = phydev->priv;
|
||||
+ int ret;
|
||||
+
|
||||
+ if (of_property_read_bool(node, "qca,keep-pll-enabled"))
|
||||
+ priv->flags |= AT803X_KEEP_PLL_ENABLED;
|
||||
+
|
||||
+ ret = at8031_register_regulators(phydev);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ ret = devm_regulator_get_enable_optional(&phydev->mdio.dev,
|
||||
+ "vddio");
|
||||
+ if (ret) {
|
||||
+ phydev_err(phydev, "failed to get VDDIO regulator\n");
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ /* Only AR8031/8033 support 1000Base-X for SFP modules */
|
||||
+ return phy_sfp_probe(phydev, &at803x_sfp_ops);
|
||||
+}
|
||||
+
|
||||
static int at8031_probe(struct phy_device *phydev)
|
||||
{
|
||||
int ret;
|
||||
@@ -1590,6 +1590,13 @@ static int at8031_probe(struct phy_devic
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
+ /* Only supported on AR8031/AR8033, the AR8030/AR8035 use strapping
|
||||
+ * options.
|
||||
+ */
|
||||
+ ret = at8031_parse_dt(phydev);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
/* Disable WoL in 1588 register which is enabled
|
||||
* by default
|
||||
*/
|
@ -0,0 +1,78 @@
|
||||
From 25d2ba94005fac18fe68878cddff59a67e115554 Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Fri, 8 Dec 2023 15:51:53 +0100
|
||||
Subject: [PATCH 06/13] net: phy: at803x: move specific at8031 probe mode check
|
||||
to dedicated probe
|
||||
|
||||
Move specific at8031 probe mode check to dedicated probe to make
|
||||
at803x_probe more generic and keep code tidy.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/at803x.c | 39 +++++++++++++++++++--------------------
|
||||
1 file changed, 19 insertions(+), 20 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/at803x.c
|
||||
+++ b/drivers/net/phy/at803x.c
|
||||
@@ -844,26 +844,6 @@ static int at803x_probe(struct phy_devic
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
- if (phydev->drv->phy_id == ATH8031_PHY_ID) {
|
||||
- int ccr = phy_read(phydev, AT803X_REG_CHIP_CONFIG);
|
||||
- int mode_cfg;
|
||||
-
|
||||
- if (ccr < 0)
|
||||
- return ccr;
|
||||
- mode_cfg = ccr & AT803X_MODE_CFG_MASK;
|
||||
-
|
||||
- switch (mode_cfg) {
|
||||
- case AT803X_MODE_CFG_BX1000_RGMII_50OHM:
|
||||
- case AT803X_MODE_CFG_BX1000_RGMII_75OHM:
|
||||
- priv->is_1000basex = true;
|
||||
- fallthrough;
|
||||
- case AT803X_MODE_CFG_FX100_RGMII_50OHM:
|
||||
- case AT803X_MODE_CFG_FX100_RGMII_75OHM:
|
||||
- priv->is_fiber = true;
|
||||
- break;
|
||||
- }
|
||||
- }
|
||||
-
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1584,6 +1564,9 @@ static int at8031_parse_dt(struct phy_de
|
||||
|
||||
static int at8031_probe(struct phy_device *phydev)
|
||||
{
|
||||
+ struct at803x_priv *priv = phydev->priv;
|
||||
+ int mode_cfg;
|
||||
+ int ccr;
|
||||
int ret;
|
||||
|
||||
ret = at803x_probe(phydev);
|
||||
@@ -1597,6 +1580,22 @@ static int at8031_probe(struct phy_devic
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
+ ccr = phy_read(phydev, AT803X_REG_CHIP_CONFIG);
|
||||
+ if (ccr < 0)
|
||||
+ return ccr;
|
||||
+ mode_cfg = ccr & AT803X_MODE_CFG_MASK;
|
||||
+
|
||||
+ switch (mode_cfg) {
|
||||
+ case AT803X_MODE_CFG_BX1000_RGMII_50OHM:
|
||||
+ case AT803X_MODE_CFG_BX1000_RGMII_75OHM:
|
||||
+ priv->is_1000basex = true;
|
||||
+ fallthrough;
|
||||
+ case AT803X_MODE_CFG_FX100_RGMII_50OHM:
|
||||
+ case AT803X_MODE_CFG_FX100_RGMII_75OHM:
|
||||
+ priv->is_fiber = true;
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
/* Disable WoL in 1588 register which is enabled
|
||||
* by default
|
||||
*/
|
@ -0,0 +1,86 @@
|
||||
From 3ae3bc426eaf57ca8f53d75777d9a5ef779bc7b7 Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Fri, 8 Dec 2023 15:51:54 +0100
|
||||
Subject: [PATCH 07/13] net: phy: at803x: move specific at8031 config_init to
|
||||
dedicated function
|
||||
|
||||
Move specific at8031 config_init to dedicated function to make
|
||||
at803x_config_init more generic and tidy things up.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/at803x.c | 45 ++++++++++++++++++++++------------------
|
||||
1 file changed, 25 insertions(+), 20 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/at803x.c
|
||||
+++ b/drivers/net/phy/at803x.c
|
||||
@@ -951,27 +951,8 @@ static int at803x_hibernation_mode_confi
|
||||
|
||||
static int at803x_config_init(struct phy_device *phydev)
|
||||
{
|
||||
- struct at803x_priv *priv = phydev->priv;
|
||||
int ret;
|
||||
|
||||
- if (phydev->drv->phy_id == ATH8031_PHY_ID) {
|
||||
- /* Some bootloaders leave the fiber page selected.
|
||||
- * Switch to the appropriate page (fiber or copper), as otherwise we
|
||||
- * read the PHY capabilities from the wrong page.
|
||||
- */
|
||||
- phy_lock_mdio_bus(phydev);
|
||||
- ret = at803x_write_page(phydev,
|
||||
- priv->is_fiber ? AT803X_PAGE_FIBER :
|
||||
- AT803X_PAGE_COPPER);
|
||||
- phy_unlock_mdio_bus(phydev);
|
||||
- if (ret)
|
||||
- return ret;
|
||||
-
|
||||
- ret = at8031_pll_config(phydev);
|
||||
- if (ret < 0)
|
||||
- return ret;
|
||||
- }
|
||||
-
|
||||
/* The RX and TX delay default is:
|
||||
* after HW reset: RX delay enabled and TX delay disabled
|
||||
* after SW reset: RX delay enabled, while TX delay retains the
|
||||
@@ -1604,6 +1585,30 @@ static int at8031_probe(struct phy_devic
|
||||
AT803X_WOL_EN, 0);
|
||||
}
|
||||
|
||||
+static int at8031_config_init(struct phy_device *phydev)
|
||||
+{
|
||||
+ struct at803x_priv *priv = phydev->priv;
|
||||
+ int ret;
|
||||
+
|
||||
+ /* Some bootloaders leave the fiber page selected.
|
||||
+ * Switch to the appropriate page (fiber or copper), as otherwise we
|
||||
+ * read the PHY capabilities from the wrong page.
|
||||
+ */
|
||||
+ phy_lock_mdio_bus(phydev);
|
||||
+ ret = at803x_write_page(phydev,
|
||||
+ priv->is_fiber ? AT803X_PAGE_FIBER :
|
||||
+ AT803X_PAGE_COPPER);
|
||||
+ phy_unlock_mdio_bus(phydev);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ ret = at8031_pll_config(phydev);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ return at803x_config_init(phydev);
|
||||
+}
|
||||
+
|
||||
static int qca83xx_config_init(struct phy_device *phydev)
|
||||
{
|
||||
u8 switch_revision;
|
||||
@@ -2113,7 +2118,7 @@ static struct phy_driver at803x_driver[]
|
||||
.name = "Qualcomm Atheros AR8031/AR8033",
|
||||
.flags = PHY_POLL_CABLE_TEST,
|
||||
.probe = at8031_probe,
|
||||
- .config_init = at803x_config_init,
|
||||
+ .config_init = at8031_config_init,
|
||||
.config_aneg = at803x_config_aneg,
|
||||
.soft_reset = genphy_soft_reset,
|
||||
.set_wol = at803x_set_wol,
|
@ -0,0 +1,92 @@
|
||||
From 27b89c9dc1b0393090d68d651b82f30ad2696baa Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Fri, 8 Dec 2023 15:51:55 +0100
|
||||
Subject: [PATCH 08/13] net: phy: at803x: move specific at8031 WOL bits to
|
||||
dedicated function
|
||||
|
||||
Move specific at8031 WOL enable/disable to dedicated function to make
|
||||
at803x_set_wol more generic.
|
||||
|
||||
This is needed in preparation for PHY driver split as qca8081 share the
|
||||
same function to toggle WOL settings.
|
||||
|
||||
In this new implementation WOL module in at8031 is enabled after the
|
||||
generic interrupt is setup. This should not cause any problem as the
|
||||
WOL_INT has a separate implementation and only relay on MAC bits.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/at803x.c | 42 ++++++++++++++++++++++++----------------
|
||||
1 file changed, 25 insertions(+), 17 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/at803x.c
|
||||
+++ b/drivers/net/phy/at803x.c
|
||||
@@ -466,27 +466,11 @@ static int at803x_set_wol(struct phy_dev
|
||||
phy_write_mmd(phydev, MDIO_MMD_PCS, offsets[i],
|
||||
mac[(i * 2) + 1] | (mac[(i * 2)] << 8));
|
||||
|
||||
- /* Enable WOL function for 1588 */
|
||||
- if (phydev->drv->phy_id == ATH8031_PHY_ID) {
|
||||
- ret = phy_modify_mmd(phydev, MDIO_MMD_PCS,
|
||||
- AT803X_PHY_MMD3_WOL_CTRL,
|
||||
- 0, AT803X_WOL_EN);
|
||||
- if (ret)
|
||||
- return ret;
|
||||
- }
|
||||
/* Enable WOL interrupt */
|
||||
ret = phy_modify(phydev, AT803X_INTR_ENABLE, 0, AT803X_INTR_ENABLE_WOL);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else {
|
||||
- /* Disable WoL function for 1588 */
|
||||
- if (phydev->drv->phy_id == ATH8031_PHY_ID) {
|
||||
- ret = phy_modify_mmd(phydev, MDIO_MMD_PCS,
|
||||
- AT803X_PHY_MMD3_WOL_CTRL,
|
||||
- AT803X_WOL_EN, 0);
|
||||
- if (ret)
|
||||
- return ret;
|
||||
- }
|
||||
/* Disable WOL interrupt */
|
||||
ret = phy_modify(phydev, AT803X_INTR_ENABLE, AT803X_INTR_ENABLE_WOL, 0);
|
||||
if (ret)
|
||||
@@ -1609,6 +1593,30 @@ static int at8031_config_init(struct phy
|
||||
return at803x_config_init(phydev);
|
||||
}
|
||||
|
||||
+static int at8031_set_wol(struct phy_device *phydev,
|
||||
+ struct ethtool_wolinfo *wol)
|
||||
+{
|
||||
+ int ret;
|
||||
+
|
||||
+ /* First setup MAC address and enable WOL interrupt */
|
||||
+ ret = at803x_set_wol(phydev, wol);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ if (wol->wolopts & WAKE_MAGIC)
|
||||
+ /* Enable WOL function for 1588 */
|
||||
+ ret = phy_modify_mmd(phydev, MDIO_MMD_PCS,
|
||||
+ AT803X_PHY_MMD3_WOL_CTRL,
|
||||
+ 0, AT803X_WOL_EN);
|
||||
+ else
|
||||
+ /* Disable WoL function for 1588 */
|
||||
+ ret = phy_modify_mmd(phydev, MDIO_MMD_PCS,
|
||||
+ AT803X_PHY_MMD3_WOL_CTRL,
|
||||
+ AT803X_WOL_EN, 0);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
static int qca83xx_config_init(struct phy_device *phydev)
|
||||
{
|
||||
u8 switch_revision;
|
||||
@@ -2121,7 +2129,7 @@ static struct phy_driver at803x_driver[]
|
||||
.config_init = at8031_config_init,
|
||||
.config_aneg = at803x_config_aneg,
|
||||
.soft_reset = genphy_soft_reset,
|
||||
- .set_wol = at803x_set_wol,
|
||||
+ .set_wol = at8031_set_wol,
|
||||
.get_wol = at803x_get_wol,
|
||||
.suspend = at803x_suspend,
|
||||
.resume = at803x_resume,
|
@ -0,0 +1,78 @@
|
||||
From 30dd62191d3dd97c08f7f9dc9ce77ffab457e4fb Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Fri, 8 Dec 2023 15:51:56 +0100
|
||||
Subject: [PATCH 09/13] net: phy: at803x: move specific at8031 config_intr to
|
||||
dedicated function
|
||||
|
||||
Move specific at8031 config_intr bits to dedicated function to make
|
||||
at803x_config_initr more generic.
|
||||
|
||||
This is needed in preparation for PHY driver split as qca8081 share the
|
||||
same function to setup interrupts.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/at803x.c | 30 ++++++++++++++++++++++++------
|
||||
1 file changed, 24 insertions(+), 6 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/at803x.c
|
||||
+++ b/drivers/net/phy/at803x.c
|
||||
@@ -990,7 +990,6 @@ static int at803x_ack_interrupt(struct p
|
||||
|
||||
static int at803x_config_intr(struct phy_device *phydev)
|
||||
{
|
||||
- struct at803x_priv *priv = phydev->priv;
|
||||
int err;
|
||||
int value;
|
||||
|
||||
@@ -1007,10 +1006,6 @@ static int at803x_config_intr(struct phy
|
||||
value |= AT803X_INTR_ENABLE_DUPLEX_CHANGED;
|
||||
value |= AT803X_INTR_ENABLE_LINK_FAIL;
|
||||
value |= AT803X_INTR_ENABLE_LINK_SUCCESS;
|
||||
- if (priv->is_fiber) {
|
||||
- value |= AT803X_INTR_ENABLE_LINK_FAIL_BX;
|
||||
- value |= AT803X_INTR_ENABLE_LINK_SUCCESS_BX;
|
||||
- }
|
||||
|
||||
err = phy_write(phydev, AT803X_INTR_ENABLE, value);
|
||||
} else {
|
||||
@@ -1617,6 +1612,29 @@ static int at8031_set_wol(struct phy_dev
|
||||
return ret;
|
||||
}
|
||||
|
||||
+static int at8031_config_intr(struct phy_device *phydev)
|
||||
+{
|
||||
+ struct at803x_priv *priv = phydev->priv;
|
||||
+ int err, value = 0;
|
||||
+
|
||||
+ if (phydev->interrupts == PHY_INTERRUPT_ENABLED &&
|
||||
+ priv->is_fiber) {
|
||||
+ /* Clear any pending interrupts */
|
||||
+ err = at803x_ack_interrupt(phydev);
|
||||
+ if (err)
|
||||
+ return err;
|
||||
+
|
||||
+ value |= AT803X_INTR_ENABLE_LINK_FAIL_BX;
|
||||
+ value |= AT803X_INTR_ENABLE_LINK_SUCCESS_BX;
|
||||
+
|
||||
+ err = phy_set_bits(phydev, AT803X_INTR_ENABLE, value);
|
||||
+ if (err)
|
||||
+ return err;
|
||||
+ }
|
||||
+
|
||||
+ return at803x_config_intr(phydev);
|
||||
+}
|
||||
+
|
||||
static int qca83xx_config_init(struct phy_device *phydev)
|
||||
{
|
||||
u8 switch_revision;
|
||||
@@ -2137,7 +2155,7 @@ static struct phy_driver at803x_driver[]
|
||||
.write_page = at803x_write_page,
|
||||
.get_features = at803x_get_features,
|
||||
.read_status = at803x_read_status,
|
||||
- .config_intr = at803x_config_intr,
|
||||
+ .config_intr = at8031_config_intr,
|
||||
.handle_interrupt = at803x_handle_interrupt,
|
||||
.get_tunable = at803x_get_tunable,
|
||||
.set_tunable = at803x_set_tunable,
|
@ -0,0 +1,78 @@
|
||||
From a5ab9d8e7ae0da8328ac1637a9755311508dc8ab Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Fri, 8 Dec 2023 15:51:57 +0100
|
||||
Subject: [PATCH 10/13] net: phy: at803x: make at8031 related DT functions name
|
||||
more specific
|
||||
|
||||
Rename at8031 related DT function name to a more specific name
|
||||
referencing they are only related to at8031 and not to the generic
|
||||
at803x PHY family.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/at803x.c | 16 ++++++++--------
|
||||
1 file changed, 8 insertions(+), 8 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/at803x.c
|
||||
+++ b/drivers/net/phy/at803x.c
|
||||
@@ -583,7 +583,7 @@ static int at803x_resume(struct phy_devi
|
||||
return phy_modify(phydev, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE, 0);
|
||||
}
|
||||
|
||||
-static int at803x_rgmii_reg_set_voltage_sel(struct regulator_dev *rdev,
|
||||
+static int at8031_rgmii_reg_set_voltage_sel(struct regulator_dev *rdev,
|
||||
unsigned int selector)
|
||||
{
|
||||
struct phy_device *phydev = rdev_get_drvdata(rdev);
|
||||
@@ -596,7 +596,7 @@ static int at803x_rgmii_reg_set_voltage_
|
||||
AT803X_DEBUG_RGMII_1V8, 0);
|
||||
}
|
||||
|
||||
-static int at803x_rgmii_reg_get_voltage_sel(struct regulator_dev *rdev)
|
||||
+static int at8031_rgmii_reg_get_voltage_sel(struct regulator_dev *rdev)
|
||||
{
|
||||
struct phy_device *phydev = rdev_get_drvdata(rdev);
|
||||
int val;
|
||||
@@ -610,8 +610,8 @@ static int at803x_rgmii_reg_get_voltage_
|
||||
|
||||
static const struct regulator_ops vddio_regulator_ops = {
|
||||
.list_voltage = regulator_list_voltage_table,
|
||||
- .set_voltage_sel = at803x_rgmii_reg_set_voltage_sel,
|
||||
- .get_voltage_sel = at803x_rgmii_reg_get_voltage_sel,
|
||||
+ .set_voltage_sel = at8031_rgmii_reg_set_voltage_sel,
|
||||
+ .get_voltage_sel = at8031_rgmii_reg_get_voltage_sel,
|
||||
};
|
||||
|
||||
static const unsigned int vddio_voltage_table[] = {
|
||||
@@ -666,7 +666,7 @@ static int at8031_register_regulators(st
|
||||
return 0;
|
||||
}
|
||||
|
||||
-static int at803x_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
|
||||
+static int at8031_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
|
||||
{
|
||||
struct phy_device *phydev = upstream;
|
||||
__ETHTOOL_DECLARE_LINK_MODE_MASK(phy_support);
|
||||
@@ -710,10 +710,10 @@ static int at803x_sfp_insert(void *upstr
|
||||
return 0;
|
||||
}
|
||||
|
||||
-static const struct sfp_upstream_ops at803x_sfp_ops = {
|
||||
+static const struct sfp_upstream_ops at8031_sfp_ops = {
|
||||
.attach = phy_sfp_attach,
|
||||
.detach = phy_sfp_detach,
|
||||
- .module_insert = at803x_sfp_insert,
|
||||
+ .module_insert = at8031_sfp_insert,
|
||||
};
|
||||
|
||||
static int at803x_parse_dt(struct phy_device *phydev)
|
||||
@@ -1519,7 +1519,7 @@ static int at8031_parse_dt(struct phy_de
|
||||
}
|
||||
|
||||
/* Only AR8031/8033 support 1000Base-X for SFP modules */
|
||||
- return phy_sfp_probe(phydev, &at803x_sfp_ops);
|
||||
+ return phy_sfp_probe(phydev, &at8031_sfp_ops);
|
||||
}
|
||||
|
||||
static int at8031_probe(struct phy_device *phydev)
|
@ -0,0 +1,297 @@
|
||||
From f932a6dc8bae0dae9645b5b1b4c65aed8a8acb2a Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Fri, 8 Dec 2023 15:51:58 +0100
|
||||
Subject: [PATCH 11/13] net: phy: at803x: move at8031 functions in dedicated
|
||||
section
|
||||
|
||||
Move at8031 functions in dedicated section with dedicated at8031
|
||||
parse_dt and probe.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/at803x.c | 266 +++++++++++++++++++--------------------
|
||||
1 file changed, 133 insertions(+), 133 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/at803x.c
|
||||
+++ b/drivers/net/phy/at803x.c
|
||||
@@ -583,139 +583,6 @@ static int at803x_resume(struct phy_devi
|
||||
return phy_modify(phydev, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE, 0);
|
||||
}
|
||||
|
||||
-static int at8031_rgmii_reg_set_voltage_sel(struct regulator_dev *rdev,
|
||||
- unsigned int selector)
|
||||
-{
|
||||
- struct phy_device *phydev = rdev_get_drvdata(rdev);
|
||||
-
|
||||
- if (selector)
|
||||
- return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F,
|
||||
- 0, AT803X_DEBUG_RGMII_1V8);
|
||||
- else
|
||||
- return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F,
|
||||
- AT803X_DEBUG_RGMII_1V8, 0);
|
||||
-}
|
||||
-
|
||||
-static int at8031_rgmii_reg_get_voltage_sel(struct regulator_dev *rdev)
|
||||
-{
|
||||
- struct phy_device *phydev = rdev_get_drvdata(rdev);
|
||||
- int val;
|
||||
-
|
||||
- val = at803x_debug_reg_read(phydev, AT803X_DEBUG_REG_1F);
|
||||
- if (val < 0)
|
||||
- return val;
|
||||
-
|
||||
- return (val & AT803X_DEBUG_RGMII_1V8) ? 1 : 0;
|
||||
-}
|
||||
-
|
||||
-static const struct regulator_ops vddio_regulator_ops = {
|
||||
- .list_voltage = regulator_list_voltage_table,
|
||||
- .set_voltage_sel = at8031_rgmii_reg_set_voltage_sel,
|
||||
- .get_voltage_sel = at8031_rgmii_reg_get_voltage_sel,
|
||||
-};
|
||||
-
|
||||
-static const unsigned int vddio_voltage_table[] = {
|
||||
- 1500000,
|
||||
- 1800000,
|
||||
-};
|
||||
-
|
||||
-static const struct regulator_desc vddio_desc = {
|
||||
- .name = "vddio",
|
||||
- .of_match = of_match_ptr("vddio-regulator"),
|
||||
- .n_voltages = ARRAY_SIZE(vddio_voltage_table),
|
||||
- .volt_table = vddio_voltage_table,
|
||||
- .ops = &vddio_regulator_ops,
|
||||
- .type = REGULATOR_VOLTAGE,
|
||||
- .owner = THIS_MODULE,
|
||||
-};
|
||||
-
|
||||
-static const struct regulator_ops vddh_regulator_ops = {
|
||||
-};
|
||||
-
|
||||
-static const struct regulator_desc vddh_desc = {
|
||||
- .name = "vddh",
|
||||
- .of_match = of_match_ptr("vddh-regulator"),
|
||||
- .n_voltages = 1,
|
||||
- .fixed_uV = 2500000,
|
||||
- .ops = &vddh_regulator_ops,
|
||||
- .type = REGULATOR_VOLTAGE,
|
||||
- .owner = THIS_MODULE,
|
||||
-};
|
||||
-
|
||||
-static int at8031_register_regulators(struct phy_device *phydev)
|
||||
-{
|
||||
- struct at803x_priv *priv = phydev->priv;
|
||||
- struct device *dev = &phydev->mdio.dev;
|
||||
- struct regulator_config config = { };
|
||||
-
|
||||
- config.dev = dev;
|
||||
- config.driver_data = phydev;
|
||||
-
|
||||
- priv->vddio_rdev = devm_regulator_register(dev, &vddio_desc, &config);
|
||||
- if (IS_ERR(priv->vddio_rdev)) {
|
||||
- phydev_err(phydev, "failed to register VDDIO regulator\n");
|
||||
- return PTR_ERR(priv->vddio_rdev);
|
||||
- }
|
||||
-
|
||||
- priv->vddh_rdev = devm_regulator_register(dev, &vddh_desc, &config);
|
||||
- if (IS_ERR(priv->vddh_rdev)) {
|
||||
- phydev_err(phydev, "failed to register VDDH regulator\n");
|
||||
- return PTR_ERR(priv->vddh_rdev);
|
||||
- }
|
||||
-
|
||||
- return 0;
|
||||
-}
|
||||
-
|
||||
-static int at8031_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
|
||||
-{
|
||||
- struct phy_device *phydev = upstream;
|
||||
- __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_support);
|
||||
- __ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_support);
|
||||
- DECLARE_PHY_INTERFACE_MASK(interfaces);
|
||||
- phy_interface_t iface;
|
||||
-
|
||||
- linkmode_zero(phy_support);
|
||||
- phylink_set(phy_support, 1000baseX_Full);
|
||||
- phylink_set(phy_support, 1000baseT_Full);
|
||||
- phylink_set(phy_support, Autoneg);
|
||||
- phylink_set(phy_support, Pause);
|
||||
- phylink_set(phy_support, Asym_Pause);
|
||||
-
|
||||
- linkmode_zero(sfp_support);
|
||||
- sfp_parse_support(phydev->sfp_bus, id, sfp_support, interfaces);
|
||||
- /* Some modules support 10G modes as well as others we support.
|
||||
- * Mask out non-supported modes so the correct interface is picked.
|
||||
- */
|
||||
- linkmode_and(sfp_support, phy_support, sfp_support);
|
||||
-
|
||||
- if (linkmode_empty(sfp_support)) {
|
||||
- dev_err(&phydev->mdio.dev, "incompatible SFP module inserted\n");
|
||||
- return -EINVAL;
|
||||
- }
|
||||
-
|
||||
- iface = sfp_select_interface(phydev->sfp_bus, sfp_support);
|
||||
-
|
||||
- /* Only 1000Base-X is supported by AR8031/8033 as the downstream SerDes
|
||||
- * interface for use with SFP modules.
|
||||
- * However, some copper modules detected as having a preferred SGMII
|
||||
- * interface do default to and function in 1000Base-X mode, so just
|
||||
- * print a warning and allow such modules, as they may have some chance
|
||||
- * of working.
|
||||
- */
|
||||
- if (iface == PHY_INTERFACE_MODE_SGMII)
|
||||
- dev_warn(&phydev->mdio.dev, "module may not function if 1000Base-X not supported\n");
|
||||
- else if (iface != PHY_INTERFACE_MODE_1000BASEX)
|
||||
- return -EINVAL;
|
||||
-
|
||||
- return 0;
|
||||
-}
|
||||
-
|
||||
-static const struct sfp_upstream_ops at8031_sfp_ops = {
|
||||
- .attach = phy_sfp_attach,
|
||||
- .detach = phy_sfp_detach,
|
||||
- .module_insert = at8031_sfp_insert,
|
||||
-};
|
||||
-
|
||||
static int at803x_parse_dt(struct phy_device *phydev)
|
||||
{
|
||||
struct device_node *node = phydev->mdio.dev.of_node;
|
||||
@@ -1498,6 +1365,139 @@ static int at803x_cable_test_start(struc
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static int at8031_rgmii_reg_set_voltage_sel(struct regulator_dev *rdev,
|
||||
+ unsigned int selector)
|
||||
+{
|
||||
+ struct phy_device *phydev = rdev_get_drvdata(rdev);
|
||||
+
|
||||
+ if (selector)
|
||||
+ return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F,
|
||||
+ 0, AT803X_DEBUG_RGMII_1V8);
|
||||
+ else
|
||||
+ return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F,
|
||||
+ AT803X_DEBUG_RGMII_1V8, 0);
|
||||
+}
|
||||
+
|
||||
+static int at8031_rgmii_reg_get_voltage_sel(struct regulator_dev *rdev)
|
||||
+{
|
||||
+ struct phy_device *phydev = rdev_get_drvdata(rdev);
|
||||
+ int val;
|
||||
+
|
||||
+ val = at803x_debug_reg_read(phydev, AT803X_DEBUG_REG_1F);
|
||||
+ if (val < 0)
|
||||
+ return val;
|
||||
+
|
||||
+ return (val & AT803X_DEBUG_RGMII_1V8) ? 1 : 0;
|
||||
+}
|
||||
+
|
||||
+static const struct regulator_ops vddio_regulator_ops = {
|
||||
+ .list_voltage = regulator_list_voltage_table,
|
||||
+ .set_voltage_sel = at8031_rgmii_reg_set_voltage_sel,
|
||||
+ .get_voltage_sel = at8031_rgmii_reg_get_voltage_sel,
|
||||
+};
|
||||
+
|
||||
+static const unsigned int vddio_voltage_table[] = {
|
||||
+ 1500000,
|
||||
+ 1800000,
|
||||
+};
|
||||
+
|
||||
+static const struct regulator_desc vddio_desc = {
|
||||
+ .name = "vddio",
|
||||
+ .of_match = of_match_ptr("vddio-regulator"),
|
||||
+ .n_voltages = ARRAY_SIZE(vddio_voltage_table),
|
||||
+ .volt_table = vddio_voltage_table,
|
||||
+ .ops = &vddio_regulator_ops,
|
||||
+ .type = REGULATOR_VOLTAGE,
|
||||
+ .owner = THIS_MODULE,
|
||||
+};
|
||||
+
|
||||
+static const struct regulator_ops vddh_regulator_ops = {
|
||||
+};
|
||||
+
|
||||
+static const struct regulator_desc vddh_desc = {
|
||||
+ .name = "vddh",
|
||||
+ .of_match = of_match_ptr("vddh-regulator"),
|
||||
+ .n_voltages = 1,
|
||||
+ .fixed_uV = 2500000,
|
||||
+ .ops = &vddh_regulator_ops,
|
||||
+ .type = REGULATOR_VOLTAGE,
|
||||
+ .owner = THIS_MODULE,
|
||||
+};
|
||||
+
|
||||
+static int at8031_register_regulators(struct phy_device *phydev)
|
||||
+{
|
||||
+ struct at803x_priv *priv = phydev->priv;
|
||||
+ struct device *dev = &phydev->mdio.dev;
|
||||
+ struct regulator_config config = { };
|
||||
+
|
||||
+ config.dev = dev;
|
||||
+ config.driver_data = phydev;
|
||||
+
|
||||
+ priv->vddio_rdev = devm_regulator_register(dev, &vddio_desc, &config);
|
||||
+ if (IS_ERR(priv->vddio_rdev)) {
|
||||
+ phydev_err(phydev, "failed to register VDDIO regulator\n");
|
||||
+ return PTR_ERR(priv->vddio_rdev);
|
||||
+ }
|
||||
+
|
||||
+ priv->vddh_rdev = devm_regulator_register(dev, &vddh_desc, &config);
|
||||
+ if (IS_ERR(priv->vddh_rdev)) {
|
||||
+ phydev_err(phydev, "failed to register VDDH regulator\n");
|
||||
+ return PTR_ERR(priv->vddh_rdev);
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int at8031_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
|
||||
+{
|
||||
+ struct phy_device *phydev = upstream;
|
||||
+ __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_support);
|
||||
+ __ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_support);
|
||||
+ DECLARE_PHY_INTERFACE_MASK(interfaces);
|
||||
+ phy_interface_t iface;
|
||||
+
|
||||
+ linkmode_zero(phy_support);
|
||||
+ phylink_set(phy_support, 1000baseX_Full);
|
||||
+ phylink_set(phy_support, 1000baseT_Full);
|
||||
+ phylink_set(phy_support, Autoneg);
|
||||
+ phylink_set(phy_support, Pause);
|
||||
+ phylink_set(phy_support, Asym_Pause);
|
||||
+
|
||||
+ linkmode_zero(sfp_support);
|
||||
+ sfp_parse_support(phydev->sfp_bus, id, sfp_support, interfaces);
|
||||
+ /* Some modules support 10G modes as well as others we support.
|
||||
+ * Mask out non-supported modes so the correct interface is picked.
|
||||
+ */
|
||||
+ linkmode_and(sfp_support, phy_support, sfp_support);
|
||||
+
|
||||
+ if (linkmode_empty(sfp_support)) {
|
||||
+ dev_err(&phydev->mdio.dev, "incompatible SFP module inserted\n");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ iface = sfp_select_interface(phydev->sfp_bus, sfp_support);
|
||||
+
|
||||
+ /* Only 1000Base-X is supported by AR8031/8033 as the downstream SerDes
|
||||
+ * interface for use with SFP modules.
|
||||
+ * However, some copper modules detected as having a preferred SGMII
|
||||
+ * interface do default to and function in 1000Base-X mode, so just
|
||||
+ * print a warning and allow such modules, as they may have some chance
|
||||
+ * of working.
|
||||
+ */
|
||||
+ if (iface == PHY_INTERFACE_MODE_SGMII)
|
||||
+ dev_warn(&phydev->mdio.dev, "module may not function if 1000Base-X not supported\n");
|
||||
+ else if (iface != PHY_INTERFACE_MODE_1000BASEX)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static const struct sfp_upstream_ops at8031_sfp_ops = {
|
||||
+ .attach = phy_sfp_attach,
|
||||
+ .detach = phy_sfp_detach,
|
||||
+ .module_insert = at8031_sfp_insert,
|
||||
+};
|
||||
+
|
||||
static int at8031_parse_dt(struct phy_device *phydev)
|
||||
{
|
||||
struct device_node *node = phydev->mdio.dev.of_node;
|
@ -0,0 +1,114 @@
|
||||
From 21a2802a8365cfa82cc02187c1f95136d85592ad Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Fri, 8 Dec 2023 15:51:59 +0100
|
||||
Subject: [PATCH 12/13] net: phy: at803x: move at8035 specific DT parse to
|
||||
dedicated probe
|
||||
|
||||
Move at8035 specific DT parse for clock out frequency to dedicated probe
|
||||
to make at803x probe function more generic.
|
||||
|
||||
This is to tidy code and no behaviour change are intended.
|
||||
|
||||
Detection logic is changed, we check if the clk 25m mask is set and if
|
||||
it's not zero, we assume the qca,clk-out-frequency property is set.
|
||||
|
||||
The property is checked in the generic at803x_parse_dt called by
|
||||
at803x_probe.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/at803x.c | 60 +++++++++++++++++++++++++++-------------
|
||||
1 file changed, 41 insertions(+), 19 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/at803x.c
|
||||
+++ b/drivers/net/phy/at803x.c
|
||||
@@ -638,23 +638,6 @@ static int at803x_parse_dt(struct phy_de
|
||||
|
||||
priv->clk_25m_reg |= FIELD_PREP(AT803X_CLK_OUT_MASK, sel);
|
||||
priv->clk_25m_mask |= AT803X_CLK_OUT_MASK;
|
||||
-
|
||||
- /* Fixup for the AR8030/AR8035. This chip has another mask and
|
||||
- * doesn't support the DSP reference. Eg. the lowest bit of the
|
||||
- * mask. The upper two bits select the same frequencies. Mask
|
||||
- * the lowest bit here.
|
||||
- *
|
||||
- * Warning:
|
||||
- * There was no datasheet for the AR8030 available so this is
|
||||
- * just a guess. But the AR8035 is listed as pin compatible
|
||||
- * to the AR8030 so there might be a good chance it works on
|
||||
- * the AR8030 too.
|
||||
- */
|
||||
- if (phydev->drv->phy_id == ATH8030_PHY_ID ||
|
||||
- phydev->drv->phy_id == ATH8035_PHY_ID) {
|
||||
- priv->clk_25m_reg &= AT8035_CLK_OUT_MASK;
|
||||
- priv->clk_25m_mask &= AT8035_CLK_OUT_MASK;
|
||||
- }
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(node, "qca,clk-out-strength", &strength);
|
||||
@@ -1635,6 +1618,45 @@ static int at8031_config_intr(struct phy
|
||||
return at803x_config_intr(phydev);
|
||||
}
|
||||
|
||||
+static int at8035_parse_dt(struct phy_device *phydev)
|
||||
+{
|
||||
+ struct at803x_priv *priv = phydev->priv;
|
||||
+
|
||||
+ /* Mask is set by the generic at803x_parse_dt
|
||||
+ * if property is set. Assume property is set
|
||||
+ * with the mask not zero.
|
||||
+ */
|
||||
+ if (priv->clk_25m_mask) {
|
||||
+ /* Fixup for the AR8030/AR8035. This chip has another mask and
|
||||
+ * doesn't support the DSP reference. Eg. the lowest bit of the
|
||||
+ * mask. The upper two bits select the same frequencies. Mask
|
||||
+ * the lowest bit here.
|
||||
+ *
|
||||
+ * Warning:
|
||||
+ * There was no datasheet for the AR8030 available so this is
|
||||
+ * just a guess. But the AR8035 is listed as pin compatible
|
||||
+ * to the AR8030 so there might be a good chance it works on
|
||||
+ * the AR8030 too.
|
||||
+ */
|
||||
+ priv->clk_25m_reg &= AT8035_CLK_OUT_MASK;
|
||||
+ priv->clk_25m_mask &= AT8035_CLK_OUT_MASK;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/* AR8030 and AR8035 shared the same special mask for clk_25m */
|
||||
+static int at8035_probe(struct phy_device *phydev)
|
||||
+{
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = at803x_probe(phydev);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ return at8035_parse_dt(phydev);
|
||||
+}
|
||||
+
|
||||
static int qca83xx_config_init(struct phy_device *phydev)
|
||||
{
|
||||
u8 switch_revision;
|
||||
@@ -2107,7 +2129,7 @@ static struct phy_driver at803x_driver[]
|
||||
PHY_ID_MATCH_EXACT(ATH8035_PHY_ID),
|
||||
.name = "Qualcomm Atheros AR8035",
|
||||
.flags = PHY_POLL_CABLE_TEST,
|
||||
- .probe = at803x_probe,
|
||||
+ .probe = at8035_probe,
|
||||
.config_aneg = at803x_config_aneg,
|
||||
.config_init = at803x_config_init,
|
||||
.soft_reset = genphy_soft_reset,
|
||||
@@ -2128,7 +2150,7 @@ static struct phy_driver at803x_driver[]
|
||||
.phy_id = ATH8030_PHY_ID,
|
||||
.name = "Qualcomm Atheros AR8030",
|
||||
.phy_id_mask = AT8030_PHY_ID_MASK,
|
||||
- .probe = at803x_probe,
|
||||
+ .probe = at8035_probe,
|
||||
.config_init = at803x_config_init,
|
||||
.link_change_notify = at803x_link_change_notify,
|
||||
.set_wol = at803x_set_wol,
|
@ -0,0 +1,219 @@
|
||||
From ef9df47b449e32e06501a11272809be49019bdb6 Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Fri, 8 Dec 2023 15:52:00 +0100
|
||||
Subject: [PATCH 13/13] net: phy: at803x: drop specific PHY ID check from cable
|
||||
test functions
|
||||
|
||||
Drop specific PHY ID check for cable test functions for at803x. This is
|
||||
done to make functions more generic. While at it better describe what
|
||||
the functions does by using more symbolic function names.
|
||||
|
||||
PHYs that requires to set additional reg are moved to specific function
|
||||
calling the more generic one.
|
||||
|
||||
cdt_start and cdt_wait_for_completion are changed to take an additional
|
||||
arg to pass specific values specific to the PHY.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/at803x.c | 95 +++++++++++++++++++++-------------------
|
||||
1 file changed, 50 insertions(+), 45 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/at803x.c
|
||||
+++ b/drivers/net/phy/at803x.c
|
||||
@@ -1222,31 +1222,16 @@ static int at803x_cdt_fault_length(u16 s
|
||||
return (dt * 824) / 10;
|
||||
}
|
||||
|
||||
-static int at803x_cdt_start(struct phy_device *phydev, int pair)
|
||||
+static int at803x_cdt_start(struct phy_device *phydev,
|
||||
+ u32 cdt_start)
|
||||
{
|
||||
- u16 cdt;
|
||||
-
|
||||
- /* qca8081 takes the different bit 15 to enable CDT test */
|
||||
- if (phydev->drv->phy_id == QCA8081_PHY_ID)
|
||||
- cdt = QCA808X_CDT_ENABLE_TEST |
|
||||
- QCA808X_CDT_LENGTH_UNIT |
|
||||
- QCA808X_CDT_INTER_CHECK_DIS;
|
||||
- else
|
||||
- cdt = FIELD_PREP(AT803X_CDT_MDI_PAIR_MASK, pair) |
|
||||
- AT803X_CDT_ENABLE_TEST;
|
||||
-
|
||||
- return phy_write(phydev, AT803X_CDT, cdt);
|
||||
+ return phy_write(phydev, AT803X_CDT, cdt_start);
|
||||
}
|
||||
|
||||
-static int at803x_cdt_wait_for_completion(struct phy_device *phydev)
|
||||
+static int at803x_cdt_wait_for_completion(struct phy_device *phydev,
|
||||
+ u32 cdt_en)
|
||||
{
|
||||
int val, ret;
|
||||
- u16 cdt_en;
|
||||
-
|
||||
- if (phydev->drv->phy_id == QCA8081_PHY_ID)
|
||||
- cdt_en = QCA808X_CDT_ENABLE_TEST;
|
||||
- else
|
||||
- cdt_en = AT803X_CDT_ENABLE_TEST;
|
||||
|
||||
/* One test run takes about 25ms */
|
||||
ret = phy_read_poll_timeout(phydev, AT803X_CDT, val,
|
||||
@@ -1266,11 +1251,13 @@ static int at803x_cable_test_one_pair(st
|
||||
};
|
||||
int ret, val;
|
||||
|
||||
- ret = at803x_cdt_start(phydev, pair);
|
||||
+ val = FIELD_PREP(AT803X_CDT_MDI_PAIR_MASK, pair) |
|
||||
+ AT803X_CDT_ENABLE_TEST;
|
||||
+ ret = at803x_cdt_start(phydev, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
- ret = at803x_cdt_wait_for_completion(phydev);
|
||||
+ ret = at803x_cdt_wait_for_completion(phydev, AT803X_CDT_ENABLE_TEST);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@@ -1292,19 +1279,11 @@ static int at803x_cable_test_one_pair(st
|
||||
}
|
||||
|
||||
static int at803x_cable_test_get_status(struct phy_device *phydev,
|
||||
- bool *finished)
|
||||
+ bool *finished, unsigned long pair_mask)
|
||||
{
|
||||
- unsigned long pair_mask;
|
||||
int retries = 20;
|
||||
int pair, ret;
|
||||
|
||||
- if (phydev->phy_id == ATH9331_PHY_ID ||
|
||||
- phydev->phy_id == ATH8032_PHY_ID ||
|
||||
- phydev->phy_id == QCA9561_PHY_ID)
|
||||
- pair_mask = 0x3;
|
||||
- else
|
||||
- pair_mask = 0xf;
|
||||
-
|
||||
*finished = false;
|
||||
|
||||
/* According to the datasheet the CDT can be performed when
|
||||
@@ -1331,7 +1310,7 @@ static int at803x_cable_test_get_status(
|
||||
return 0;
|
||||
}
|
||||
|
||||
-static int at803x_cable_test_start(struct phy_device *phydev)
|
||||
+static void at803x_cable_test_autoneg(struct phy_device *phydev)
|
||||
{
|
||||
/* Enable auto-negotiation, but advertise no capabilities, no link
|
||||
* will be established. A restart of the auto-negotiation is not
|
||||
@@ -1339,11 +1318,11 @@ static int at803x_cable_test_start(struc
|
||||
*/
|
||||
phy_write(phydev, MII_BMCR, BMCR_ANENABLE);
|
||||
phy_write(phydev, MII_ADVERTISE, ADVERTISE_CSMA);
|
||||
- if (phydev->phy_id != ATH9331_PHY_ID &&
|
||||
- phydev->phy_id != ATH8032_PHY_ID &&
|
||||
- phydev->phy_id != QCA9561_PHY_ID)
|
||||
- phy_write(phydev, MII_CTRL1000, 0);
|
||||
+}
|
||||
|
||||
+static int at803x_cable_test_start(struct phy_device *phydev)
|
||||
+{
|
||||
+ at803x_cable_test_autoneg(phydev);
|
||||
/* we do all the (time consuming) work later */
|
||||
return 0;
|
||||
}
|
||||
@@ -1618,6 +1597,29 @@ static int at8031_config_intr(struct phy
|
||||
return at803x_config_intr(phydev);
|
||||
}
|
||||
|
||||
+/* AR8031 and AR8035 share the same cable test get status reg */
|
||||
+static int at8031_cable_test_get_status(struct phy_device *phydev,
|
||||
+ bool *finished)
|
||||
+{
|
||||
+ return at803x_cable_test_get_status(phydev, finished, 0xf);
|
||||
+}
|
||||
+
|
||||
+/* AR8031 and AR8035 share the same cable test start logic */
|
||||
+static int at8031_cable_test_start(struct phy_device *phydev)
|
||||
+{
|
||||
+ at803x_cable_test_autoneg(phydev);
|
||||
+ phy_write(phydev, MII_CTRL1000, 0);
|
||||
+ /* we do all the (time consuming) work later */
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/* AR8032, AR9331 and QCA9561 share the same cable test get status reg */
|
||||
+static int at8032_cable_test_get_status(struct phy_device *phydev,
|
||||
+ bool *finished)
|
||||
+{
|
||||
+ return at803x_cable_test_get_status(phydev, finished, 0x3);
|
||||
+}
|
||||
+
|
||||
static int at8035_parse_dt(struct phy_device *phydev)
|
||||
{
|
||||
struct at803x_priv *priv = phydev->priv;
|
||||
@@ -2041,11 +2043,14 @@ static int qca808x_cable_test_get_status
|
||||
|
||||
*finished = false;
|
||||
|
||||
- ret = at803x_cdt_start(phydev, 0);
|
||||
+ val = QCA808X_CDT_ENABLE_TEST |
|
||||
+ QCA808X_CDT_LENGTH_UNIT |
|
||||
+ QCA808X_CDT_INTER_CHECK_DIS;
|
||||
+ ret = at803x_cdt_start(phydev, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
- ret = at803x_cdt_wait_for_completion(phydev);
|
||||
+ ret = at803x_cdt_wait_for_completion(phydev, QCA808X_CDT_ENABLE_TEST);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@@ -2143,8 +2148,8 @@ static struct phy_driver at803x_driver[]
|
||||
.handle_interrupt = at803x_handle_interrupt,
|
||||
.get_tunable = at803x_get_tunable,
|
||||
.set_tunable = at803x_set_tunable,
|
||||
- .cable_test_start = at803x_cable_test_start,
|
||||
- .cable_test_get_status = at803x_cable_test_get_status,
|
||||
+ .cable_test_start = at8031_cable_test_start,
|
||||
+ .cable_test_get_status = at8031_cable_test_get_status,
|
||||
}, {
|
||||
/* Qualcomm Atheros AR8030 */
|
||||
.phy_id = ATH8030_PHY_ID,
|
||||
@@ -2181,8 +2186,8 @@ static struct phy_driver at803x_driver[]
|
||||
.handle_interrupt = at803x_handle_interrupt,
|
||||
.get_tunable = at803x_get_tunable,
|
||||
.set_tunable = at803x_set_tunable,
|
||||
- .cable_test_start = at803x_cable_test_start,
|
||||
- .cable_test_get_status = at803x_cable_test_get_status,
|
||||
+ .cable_test_start = at8031_cable_test_start,
|
||||
+ .cable_test_get_status = at8031_cable_test_get_status,
|
||||
}, {
|
||||
/* Qualcomm Atheros AR8032 */
|
||||
PHY_ID_MATCH_EXACT(ATH8032_PHY_ID),
|
||||
@@ -2197,7 +2202,7 @@ static struct phy_driver at803x_driver[]
|
||||
.config_intr = at803x_config_intr,
|
||||
.handle_interrupt = at803x_handle_interrupt,
|
||||
.cable_test_start = at803x_cable_test_start,
|
||||
- .cable_test_get_status = at803x_cable_test_get_status,
|
||||
+ .cable_test_get_status = at8032_cable_test_get_status,
|
||||
}, {
|
||||
/* ATHEROS AR9331 */
|
||||
PHY_ID_MATCH_EXACT(ATH9331_PHY_ID),
|
||||
@@ -2210,7 +2215,7 @@ static struct phy_driver at803x_driver[]
|
||||
.config_intr = at803x_config_intr,
|
||||
.handle_interrupt = at803x_handle_interrupt,
|
||||
.cable_test_start = at803x_cable_test_start,
|
||||
- .cable_test_get_status = at803x_cable_test_get_status,
|
||||
+ .cable_test_get_status = at8032_cable_test_get_status,
|
||||
.read_status = at803x_read_status,
|
||||
.soft_reset = genphy_soft_reset,
|
||||
.config_aneg = at803x_config_aneg,
|
||||
@@ -2226,7 +2231,7 @@ static struct phy_driver at803x_driver[]
|
||||
.config_intr = at803x_config_intr,
|
||||
.handle_interrupt = at803x_handle_interrupt,
|
||||
.cable_test_start = at803x_cable_test_start,
|
||||
- .cable_test_get_status = at803x_cable_test_get_status,
|
||||
+ .cable_test_get_status = at8032_cable_test_get_status,
|
||||
.read_status = at803x_read_status,
|
||||
.soft_reset = genphy_soft_reset,
|
||||
.config_aneg = at803x_config_aneg,
|
@ -0,0 +1,116 @@
|
||||
From 8e732f1c6f2dc5e18f766d0f1b11df9db2dd044a Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Thu, 14 Dec 2023 01:44:31 +0100
|
||||
Subject: [PATCH 1/2] net: phy: at803x: move specific qca808x config_aneg to
|
||||
dedicated function
|
||||
|
||||
Move specific qca808x config_aneg to dedicated function to permit easier
|
||||
split of qca808x portion from at803x driver.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/at803x.c | 66 ++++++++++++++++++++++++----------------
|
||||
1 file changed, 40 insertions(+), 26 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/at803x.c
|
||||
+++ b/drivers/net/phy/at803x.c
|
||||
@@ -1045,9 +1045,8 @@ static int at803x_config_mdix(struct phy
|
||||
FIELD_PREP(AT803X_SFC_MDI_CROSSOVER_MODE_M, val));
|
||||
}
|
||||
|
||||
-static int at803x_config_aneg(struct phy_device *phydev)
|
||||
+static int at803x_prepare_config_aneg(struct phy_device *phydev)
|
||||
{
|
||||
- struct at803x_priv *priv = phydev->priv;
|
||||
int ret;
|
||||
|
||||
ret = at803x_config_mdix(phydev, phydev->mdix_ctrl);
|
||||
@@ -1064,33 +1063,22 @@ static int at803x_config_aneg(struct phy
|
||||
return ret;
|
||||
}
|
||||
|
||||
- if (priv->is_1000basex)
|
||||
- return genphy_c37_config_aneg(phydev);
|
||||
-
|
||||
- /* Do not restart auto-negotiation by setting ret to 0 defautly,
|
||||
- * when calling __genphy_config_aneg later.
|
||||
- */
|
||||
- ret = 0;
|
||||
-
|
||||
- if (phydev->drv->phy_id == QCA8081_PHY_ID) {
|
||||
- int phy_ctrl = 0;
|
||||
+ return 0;
|
||||
+}
|
||||
|
||||
- /* The reg MII_BMCR also needs to be configured for force mode, the
|
||||
- * genphy_config_aneg is also needed.
|
||||
- */
|
||||
- if (phydev->autoneg == AUTONEG_DISABLE)
|
||||
- genphy_c45_pma_setup_forced(phydev);
|
||||
+static int at803x_config_aneg(struct phy_device *phydev)
|
||||
+{
|
||||
+ struct at803x_priv *priv = phydev->priv;
|
||||
+ int ret;
|
||||
|
||||
- if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->advertising))
|
||||
- phy_ctrl = MDIO_AN_10GBT_CTRL_ADV2_5G;
|
||||
+ ret = at803x_prepare_config_aneg(phydev);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
|
||||
- ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL,
|
||||
- MDIO_AN_10GBT_CTRL_ADV2_5G, phy_ctrl);
|
||||
- if (ret < 0)
|
||||
- return ret;
|
||||
- }
|
||||
+ if (priv->is_1000basex)
|
||||
+ return genphy_c37_config_aneg(phydev);
|
||||
|
||||
- return __genphy_config_aneg(phydev, ret);
|
||||
+ return genphy_config_aneg(phydev);
|
||||
}
|
||||
|
||||
static int at803x_get_downshift(struct phy_device *phydev, u8 *d)
|
||||
@@ -2118,6 +2106,32 @@ static int qca808x_get_features(struct p
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static int qca808x_config_aneg(struct phy_device *phydev)
|
||||
+{
|
||||
+ int phy_ctrl = 0;
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = at803x_prepare_config_aneg(phydev);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ /* The reg MII_BMCR also needs to be configured for force mode, the
|
||||
+ * genphy_config_aneg is also needed.
|
||||
+ */
|
||||
+ if (phydev->autoneg == AUTONEG_DISABLE)
|
||||
+ genphy_c45_pma_setup_forced(phydev);
|
||||
+
|
||||
+ if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->advertising))
|
||||
+ phy_ctrl = MDIO_AN_10GBT_CTRL_ADV2_5G;
|
||||
+
|
||||
+ ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL,
|
||||
+ MDIO_AN_10GBT_CTRL_ADV2_5G, phy_ctrl);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ return __genphy_config_aneg(phydev, ret);
|
||||
+}
|
||||
+
|
||||
static void qca808x_link_change_notify(struct phy_device *phydev)
|
||||
{
|
||||
/* Assert interface sgmii fifo on link down, deassert it on link up,
|
||||
@@ -2295,7 +2309,7 @@ static struct phy_driver at803x_driver[]
|
||||
.set_wol = at803x_set_wol,
|
||||
.get_wol = at803x_get_wol,
|
||||
.get_features = qca808x_get_features,
|
||||
- .config_aneg = at803x_config_aneg,
|
||||
+ .config_aneg = qca808x_config_aneg,
|
||||
.suspend = genphy_suspend,
|
||||
.resume = genphy_resume,
|
||||
.read_status = qca808x_read_status,
|
@ -0,0 +1,97 @@
|
||||
From 38eb804e8458ba181a03a0498ce4bf84eebd1931 Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Thu, 14 Dec 2023 01:44:32 +0100
|
||||
Subject: [PATCH 2/2] net: phy: at803x: make read specific status function more
|
||||
generic
|
||||
|
||||
Rework read specific status function to be more generic. The function
|
||||
apply different speed mask based on the PHY ID. Make it more generic by
|
||||
adding an additional arg to pass the specific speed (ss) mask and use
|
||||
the provided mask to parse the speed value.
|
||||
|
||||
This is needed to permit an easier deatch of qca808x code from the
|
||||
at803x driver.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/at803x.c | 26 ++++++++++++++++++--------
|
||||
1 file changed, 18 insertions(+), 8 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/at803x.c
|
||||
+++ b/drivers/net/phy/at803x.c
|
||||
@@ -301,6 +301,11 @@ static struct at803x_hw_stat qca83xx_hw_
|
||||
{ "eee_wake_errors", 0x16, GENMASK(15, 0), MMD},
|
||||
};
|
||||
|
||||
+struct at803x_ss_mask {
|
||||
+ u16 speed_mask;
|
||||
+ u8 speed_shift;
|
||||
+};
|
||||
+
|
||||
struct at803x_priv {
|
||||
int flags;
|
||||
u16 clk_25m_reg;
|
||||
@@ -921,7 +926,8 @@ static void at803x_link_change_notify(st
|
||||
}
|
||||
}
|
||||
|
||||
-static int at803x_read_specific_status(struct phy_device *phydev)
|
||||
+static int at803x_read_specific_status(struct phy_device *phydev,
|
||||
+ struct at803x_ss_mask ss_mask)
|
||||
{
|
||||
int ss;
|
||||
|
||||
@@ -940,11 +946,8 @@ static int at803x_read_specific_status(s
|
||||
if (sfc < 0)
|
||||
return sfc;
|
||||
|
||||
- /* qca8081 takes the different bits for speed value from at803x */
|
||||
- if (phydev->drv->phy_id == QCA8081_PHY_ID)
|
||||
- speed = FIELD_GET(QCA808X_SS_SPEED_MASK, ss);
|
||||
- else
|
||||
- speed = FIELD_GET(AT803X_SS_SPEED_MASK, ss);
|
||||
+ speed = ss & ss_mask.speed_mask;
|
||||
+ speed >>= ss_mask.speed_shift;
|
||||
|
||||
switch (speed) {
|
||||
case AT803X_SS_SPEED_10:
|
||||
@@ -989,6 +992,7 @@ static int at803x_read_specific_status(s
|
||||
static int at803x_read_status(struct phy_device *phydev)
|
||||
{
|
||||
struct at803x_priv *priv = phydev->priv;
|
||||
+ struct at803x_ss_mask ss_mask = { 0 };
|
||||
int err, old_link = phydev->link;
|
||||
|
||||
if (priv->is_1000basex)
|
||||
@@ -1012,7 +1016,9 @@ static int at803x_read_status(struct phy
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
- err = at803x_read_specific_status(phydev);
|
||||
+ ss_mask.speed_mask = AT803X_SS_SPEED_MASK;
|
||||
+ ss_mask.speed_shift = __bf_shf(AT803X_SS_SPEED_MASK);
|
||||
+ err = at803x_read_specific_status(phydev, ss_mask);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
@@ -1869,6 +1875,7 @@ static int qca808x_config_init(struct ph
|
||||
|
||||
static int qca808x_read_status(struct phy_device *phydev)
|
||||
{
|
||||
+ struct at803x_ss_mask ss_mask = { 0 };
|
||||
int ret;
|
||||
|
||||
ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_STAT);
|
||||
@@ -1882,7 +1889,10 @@ static int qca808x_read_status(struct ph
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
- ret = at803x_read_specific_status(phydev);
|
||||
+ /* qca8081 takes the different bits for speed value from at803x */
|
||||
+ ss_mask.speed_mask = QCA808X_SS_SPEED_MASK;
|
||||
+ ss_mask.speed_shift = __bf_shf(QCA808X_SS_SPEED_MASK);
|
||||
+ ret = at803x_read_specific_status(phydev, ss_mask);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
@ -0,0 +1,27 @@
|
||||
From fc9d7264ddc32eaa647d6bfcdc25cdf9f786fde0 Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Mon, 18 Dec 2023 00:27:39 +0100
|
||||
Subject: [PATCH 1/2] net: phy: at803x: remove extra space after cast
|
||||
|
||||
Remove extra space after cast as reported by checkpatch to keep code
|
||||
clean.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Link: https://lore.kernel.org/r/20231217232739.27065-1-ansuelsmth@gmail.com
|
||||
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
|
||||
---
|
||||
drivers/net/phy/at803x.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
--- a/drivers/net/phy/at803x.c
|
||||
+++ b/drivers/net/phy/at803x.c
|
||||
@@ -462,7 +462,7 @@ static int at803x_set_wol(struct phy_dev
|
||||
if (!ndev)
|
||||
return -ENODEV;
|
||||
|
||||
- mac = (const u8 *) ndev->dev_addr;
|
||||
+ mac = (const u8 *)ndev->dev_addr;
|
||||
|
||||
if (!is_valid_ether_addr(mac))
|
||||
return -EINVAL;
|
@ -0,0 +1,38 @@
|
||||
From 3ab5720881a924fb6405d9e6a3b09f1026467c47 Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Mon, 18 Dec 2023 00:25:08 +0100
|
||||
Subject: [PATCH 2/2] net: phy: at803x: replace msleep(1) with usleep_range
|
||||
|
||||
Replace msleep(1) with usleep_range as suggested by timers-howto guide.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Link: https://lore.kernel.org/r/20231217232508.26470-1-ansuelsmth@gmail.com
|
||||
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
|
||||
---
|
||||
drivers/net/phy/at803x.c | 6 +++---
|
||||
1 file changed, 3 insertions(+), 3 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/at803x.c
|
||||
+++ b/drivers/net/phy/at803x.c
|
||||
@@ -916,9 +916,9 @@ static void at803x_link_change_notify(st
|
||||
at803x_context_save(phydev, &context);
|
||||
|
||||
phy_device_reset(phydev, 1);
|
||||
- msleep(1);
|
||||
+ usleep_range(1000, 2000);
|
||||
phy_device_reset(phydev, 0);
|
||||
- msleep(1);
|
||||
+ usleep_range(1000, 2000);
|
||||
|
||||
at803x_context_restore(phydev, &context);
|
||||
|
||||
@@ -1733,7 +1733,7 @@ static int qca83xx_resume(struct phy_dev
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
- msleep(1);
|
||||
+ usleep_range(1000, 2000);
|
||||
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,152 @@
|
||||
From 7961ef1fa10ec35ad6923fb5751877116e4b035b Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Tue, 19 Dec 2023 21:21:24 +0100
|
||||
Subject: [PATCH] net: phy: at803x: better align function varibles to open
|
||||
parenthesis
|
||||
|
||||
Better align function variables to open parenthesis as suggested by
|
||||
checkpatch script for qca808x function to make code cleaner.
|
||||
|
||||
For cable_test_get_status function some additional rework was needed to
|
||||
handle too long functions.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/at803x.c | 67 ++++++++++++++++++++++------------------
|
||||
1 file changed, 37 insertions(+), 30 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/at803x.c
|
||||
+++ b/drivers/net/phy/at803x.c
|
||||
@@ -1781,27 +1781,27 @@ static int qca808x_phy_fast_retrain_conf
|
||||
return ret;
|
||||
|
||||
phy_write_mmd(phydev, MDIO_MMD_AN, QCA808X_PHY_MMD7_TOP_OPTION1,
|
||||
- QCA808X_TOP_OPTION1_DATA);
|
||||
+ QCA808X_TOP_OPTION1_DATA);
|
||||
phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_20DB,
|
||||
- QCA808X_MSE_THRESHOLD_20DB_VALUE);
|
||||
+ QCA808X_MSE_THRESHOLD_20DB_VALUE);
|
||||
phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_17DB,
|
||||
- QCA808X_MSE_THRESHOLD_17DB_VALUE);
|
||||
+ QCA808X_MSE_THRESHOLD_17DB_VALUE);
|
||||
phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_27DB,
|
||||
- QCA808X_MSE_THRESHOLD_27DB_VALUE);
|
||||
+ QCA808X_MSE_THRESHOLD_27DB_VALUE);
|
||||
phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_28DB,
|
||||
- QCA808X_MSE_THRESHOLD_28DB_VALUE);
|
||||
+ QCA808X_MSE_THRESHOLD_28DB_VALUE);
|
||||
phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_1,
|
||||
- QCA808X_MMD3_DEBUG_1_VALUE);
|
||||
+ QCA808X_MMD3_DEBUG_1_VALUE);
|
||||
phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_4,
|
||||
- QCA808X_MMD3_DEBUG_4_VALUE);
|
||||
+ QCA808X_MMD3_DEBUG_4_VALUE);
|
||||
phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_5,
|
||||
- QCA808X_MMD3_DEBUG_5_VALUE);
|
||||
+ QCA808X_MMD3_DEBUG_5_VALUE);
|
||||
phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_3,
|
||||
- QCA808X_MMD3_DEBUG_3_VALUE);
|
||||
+ QCA808X_MMD3_DEBUG_3_VALUE);
|
||||
phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_6,
|
||||
- QCA808X_MMD3_DEBUG_6_VALUE);
|
||||
+ QCA808X_MMD3_DEBUG_6_VALUE);
|
||||
phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_2,
|
||||
- QCA808X_MMD3_DEBUG_2_VALUE);
|
||||
+ QCA808X_MMD3_DEBUG_2_VALUE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1838,13 +1838,14 @@ static int qca808x_config_init(struct ph
|
||||
|
||||
/* Active adc&vga on 802.3az for the link 1000M and 100M */
|
||||
ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_ADDR_CLD_CTRL7,
|
||||
- QCA808X_8023AZ_AFE_CTRL_MASK, QCA808X_8023AZ_AFE_EN);
|
||||
+ QCA808X_8023AZ_AFE_CTRL_MASK, QCA808X_8023AZ_AFE_EN);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Adjust the threshold on 802.3az for the link 1000M */
|
||||
ret = phy_write_mmd(phydev, MDIO_MMD_PCS,
|
||||
- QCA808X_PHY_MMD3_AZ_TRAINING_CTRL, QCA808X_MMD3_AZ_TRAINING_VAL);
|
||||
+ QCA808X_PHY_MMD3_AZ_TRAINING_CTRL,
|
||||
+ QCA808X_MMD3_AZ_TRAINING_VAL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@@ -1870,7 +1871,8 @@ static int qca808x_config_init(struct ph
|
||||
|
||||
/* Configure adc threshold as 100mv for the link 10M */
|
||||
return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_ADC_THRESHOLD,
|
||||
- QCA808X_ADC_THRESHOLD_MASK, QCA808X_ADC_THRESHOLD_100MV);
|
||||
+ QCA808X_ADC_THRESHOLD_MASK,
|
||||
+ QCA808X_ADC_THRESHOLD_100MV);
|
||||
}
|
||||
|
||||
static int qca808x_read_status(struct phy_device *phydev)
|
||||
@@ -1883,7 +1885,7 @@ static int qca808x_read_status(struct ph
|
||||
return ret;
|
||||
|
||||
linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->lp_advertising,
|
||||
- ret & MDIO_AN_10GBT_STAT_LP2_5G);
|
||||
+ ret & MDIO_AN_10GBT_STAT_LP2_5G);
|
||||
|
||||
ret = genphy_read_status(phydev);
|
||||
if (ret)
|
||||
@@ -1913,7 +1915,7 @@ static int qca808x_read_status(struct ph
|
||||
*/
|
||||
if (qca808x_has_fast_retrain_or_slave_seed(phydev)) {
|
||||
if (phydev->master_slave_state == MASTER_SLAVE_STATE_ERR ||
|
||||
- qca808x_is_prefer_master(phydev)) {
|
||||
+ qca808x_is_prefer_master(phydev)) {
|
||||
qca808x_phy_ms_seed_enable(phydev, false);
|
||||
} else {
|
||||
qca808x_phy_ms_seed_enable(phydev, true);
|
||||
@@ -2070,18 +2072,22 @@ static int qca808x_cable_test_get_status
|
||||
ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_D,
|
||||
qca808x_cable_test_result_trans(pair_d));
|
||||
|
||||
- if (qca808x_cdt_fault_length_valid(pair_a))
|
||||
- ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_A,
|
||||
- qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_A));
|
||||
- if (qca808x_cdt_fault_length_valid(pair_b))
|
||||
- ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_B,
|
||||
- qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_B));
|
||||
- if (qca808x_cdt_fault_length_valid(pair_c))
|
||||
- ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_C,
|
||||
- qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_C));
|
||||
- if (qca808x_cdt_fault_length_valid(pair_d))
|
||||
- ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_D,
|
||||
- qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_D));
|
||||
+ if (qca808x_cdt_fault_length_valid(pair_a)) {
|
||||
+ val = qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_A);
|
||||
+ ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_A, val);
|
||||
+ }
|
||||
+ if (qca808x_cdt_fault_length_valid(pair_b)) {
|
||||
+ val = qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_B);
|
||||
+ ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_B, val);
|
||||
+ }
|
||||
+ if (qca808x_cdt_fault_length_valid(pair_c)) {
|
||||
+ val = qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_C);
|
||||
+ ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_C, val);
|
||||
+ }
|
||||
+ if (qca808x_cdt_fault_length_valid(pair_d)) {
|
||||
+ val = qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_D);
|
||||
+ ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_D, val);
|
||||
+ }
|
||||
|
||||
*finished = true;
|
||||
|
||||
@@ -2148,8 +2154,9 @@ static void qca808x_link_change_notify(s
|
||||
* the interface device address is always phy address added by 1.
|
||||
*/
|
||||
mdiobus_c45_modify_changed(phydev->mdio.bus, phydev->mdio.addr + 1,
|
||||
- MDIO_MMD_PMAPMD, QCA8081_PHY_SERDES_MMD1_FIFO_CTRL,
|
||||
- QCA8081_PHY_FIFO_RSTN, phydev->link ? QCA8081_PHY_FIFO_RSTN : 0);
|
||||
+ MDIO_MMD_PMAPMD, QCA8081_PHY_SERDES_MMD1_FIFO_CTRL,
|
||||
+ QCA8081_PHY_FIFO_RSTN,
|
||||
+ phydev->link ? QCA8081_PHY_FIFO_RSTN : 0);
|
||||
}
|
||||
|
||||
static struct phy_driver at803x_driver[] = {
|
@ -0,0 +1,62 @@
|
||||
From 22eb276098da820d9440fad22901f9b74ed4d659 Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Thu, 4 Jan 2024 22:30:38 +0100
|
||||
Subject: [PATCH 1/4] net: phy: at803x: generalize cdt fault length function
|
||||
|
||||
Generalize cable test fault length function since they all base on the
|
||||
same magic values (already reverse engineered to understand the meaning
|
||||
of it) to have consistenct values on every PHY.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Reviewed-by: Simon Horman <horms@kernel.org>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/at803x.c | 13 ++++++-------
|
||||
1 file changed, 6 insertions(+), 7 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/at803x.c
|
||||
+++ b/drivers/net/phy/at803x.c
|
||||
@@ -1192,10 +1192,8 @@ static bool at803x_cdt_fault_length_vali
|
||||
return false;
|
||||
}
|
||||
|
||||
-static int at803x_cdt_fault_length(u16 status)
|
||||
+static int at803x_cdt_fault_length(int dt)
|
||||
{
|
||||
- int dt;
|
||||
-
|
||||
/* According to the datasheet the distance to the fault is
|
||||
* DELTA_TIME * 0.824 meters.
|
||||
*
|
||||
@@ -1211,8 +1209,6 @@ static int at803x_cdt_fault_length(u16 s
|
||||
* With a VF of 0.69 we get the factor 0.824 mentioned in the
|
||||
* datasheet.
|
||||
*/
|
||||
- dt = FIELD_GET(AT803X_CDT_STATUS_DELTA_TIME_MASK, status);
|
||||
-
|
||||
return (dt * 824) / 10;
|
||||
}
|
||||
|
||||
@@ -1265,9 +1261,11 @@ static int at803x_cable_test_one_pair(st
|
||||
ethnl_cable_test_result(phydev, ethtool_pair[pair],
|
||||
at803x_cable_test_result_trans(val));
|
||||
|
||||
- if (at803x_cdt_fault_length_valid(val))
|
||||
+ if (at803x_cdt_fault_length_valid(val)) {
|
||||
+ val = FIELD_GET(AT803X_CDT_STATUS_DELTA_TIME_MASK, val);
|
||||
ethnl_cable_test_fault_length(phydev, ethtool_pair[pair],
|
||||
at803x_cdt_fault_length(val));
|
||||
+ }
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -1992,7 +1990,8 @@ static int qca808x_cdt_fault_length(stru
|
||||
if (val < 0)
|
||||
return val;
|
||||
|
||||
- return (FIELD_GET(QCA808X_CDT_DIAG_LENGTH, val) * 824) / 10;
|
||||
+ val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH, val);
|
||||
+ return at803x_cdt_fault_length(val);
|
||||
}
|
||||
|
||||
static int qca808x_cable_test_start(struct phy_device *phydev)
|
@ -0,0 +1,118 @@
|
||||
From e0e9ada1df6133513249861c1d91c1dbefd9383b Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Thu, 4 Jan 2024 22:30:39 +0100
|
||||
Subject: [PATCH 2/4] net: phy: at803x: refactor qca808x cable test get status
|
||||
function
|
||||
|
||||
Refactor qca808x cable test get status function to remove code
|
||||
duplication and clean things up.
|
||||
|
||||
The same logic is applied to each pair hence it can be generalized and
|
||||
moved to a common function.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Reviewed-by: Simon Horman <horms@kernel.org>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/at803x.c | 80 ++++++++++++++++++++++++----------------
|
||||
1 file changed, 49 insertions(+), 31 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/at803x.c
|
||||
+++ b/drivers/net/phy/at803x.c
|
||||
@@ -2035,10 +2035,43 @@ static int qca808x_cable_test_start(stru
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static int qca808x_cable_test_get_pair_status(struct phy_device *phydev, u8 pair,
|
||||
+ u16 status)
|
||||
+{
|
||||
+ u16 pair_code;
|
||||
+ int length;
|
||||
+
|
||||
+ switch (pair) {
|
||||
+ case ETHTOOL_A_CABLE_PAIR_A:
|
||||
+ pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_A, status);
|
||||
+ break;
|
||||
+ case ETHTOOL_A_CABLE_PAIR_B:
|
||||
+ pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_B, status);
|
||||
+ break;
|
||||
+ case ETHTOOL_A_CABLE_PAIR_C:
|
||||
+ pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_C, status);
|
||||
+ break;
|
||||
+ case ETHTOOL_A_CABLE_PAIR_D:
|
||||
+ pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_D, status);
|
||||
+ break;
|
||||
+ default:
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ ethnl_cable_test_result(phydev, pair,
|
||||
+ qca808x_cable_test_result_trans(pair_code));
|
||||
+
|
||||
+ if (qca808x_cdt_fault_length_valid(pair_code)) {
|
||||
+ length = qca808x_cdt_fault_length(phydev, pair);
|
||||
+ ethnl_cable_test_fault_length(phydev, pair, length);
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
static int qca808x_cable_test_get_status(struct phy_device *phydev, bool *finished)
|
||||
{
|
||||
int ret, val;
|
||||
- int pair_a, pair_b, pair_c, pair_d;
|
||||
|
||||
*finished = false;
|
||||
|
||||
@@ -2057,36 +2090,21 @@ static int qca808x_cable_test_get_status
|
||||
if (val < 0)
|
||||
return val;
|
||||
|
||||
- pair_a = FIELD_GET(QCA808X_CDT_CODE_PAIR_A, val);
|
||||
- pair_b = FIELD_GET(QCA808X_CDT_CODE_PAIR_B, val);
|
||||
- pair_c = FIELD_GET(QCA808X_CDT_CODE_PAIR_C, val);
|
||||
- pair_d = FIELD_GET(QCA808X_CDT_CODE_PAIR_D, val);
|
||||
-
|
||||
- ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_A,
|
||||
- qca808x_cable_test_result_trans(pair_a));
|
||||
- ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_B,
|
||||
- qca808x_cable_test_result_trans(pair_b));
|
||||
- ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_C,
|
||||
- qca808x_cable_test_result_trans(pair_c));
|
||||
- ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_D,
|
||||
- qca808x_cable_test_result_trans(pair_d));
|
||||
-
|
||||
- if (qca808x_cdt_fault_length_valid(pair_a)) {
|
||||
- val = qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_A);
|
||||
- ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_A, val);
|
||||
- }
|
||||
- if (qca808x_cdt_fault_length_valid(pair_b)) {
|
||||
- val = qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_B);
|
||||
- ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_B, val);
|
||||
- }
|
||||
- if (qca808x_cdt_fault_length_valid(pair_c)) {
|
||||
- val = qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_C);
|
||||
- ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_C, val);
|
||||
- }
|
||||
- if (qca808x_cdt_fault_length_valid(pair_d)) {
|
||||
- val = qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_D);
|
||||
- ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_D, val);
|
||||
- }
|
||||
+ ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_A, val);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_B, val);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_C, val);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_D, val);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
|
||||
*finished = true;
|
||||
|
@ -0,0 +1,182 @@
|
||||
From ea73e5ea442ee2aade67b1fb1233ccb3cbea2ceb Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Thu, 4 Jan 2024 22:30:40 +0100
|
||||
Subject: [PATCH 3/4] net: phy: at803x: add support for cdt cross short test
|
||||
for qca808x
|
||||
|
||||
QCA808x PHY Family supports Cable Diagnostic Test also for Cross Pair
|
||||
Short.
|
||||
|
||||
Add all the define to make enable and support these additional tests.
|
||||
|
||||
Cross Short test was previously disabled by default, this is now changed
|
||||
and enabled by default. In this mode, the mask changed a bit and length
|
||||
is shifted based on the fault condition.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Reviewed-by: Simon Horman <horms@kernel.org>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/at803x.c | 86 ++++++++++++++++++++++++++++++++--------
|
||||
1 file changed, 69 insertions(+), 17 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/at803x.c
|
||||
+++ b/drivers/net/phy/at803x.c
|
||||
@@ -254,6 +254,7 @@
|
||||
|
||||
#define QCA808X_CDT_ENABLE_TEST BIT(15)
|
||||
#define QCA808X_CDT_INTER_CHECK_DIS BIT(13)
|
||||
+#define QCA808X_CDT_STATUS BIT(11)
|
||||
#define QCA808X_CDT_LENGTH_UNIT BIT(10)
|
||||
|
||||
#define QCA808X_MMD3_CDT_STATUS 0x8064
|
||||
@@ -261,16 +262,44 @@
|
||||
#define QCA808X_MMD3_CDT_DIAG_PAIR_B 0x8066
|
||||
#define QCA808X_MMD3_CDT_DIAG_PAIR_C 0x8067
|
||||
#define QCA808X_MMD3_CDT_DIAG_PAIR_D 0x8068
|
||||
-#define QCA808X_CDT_DIAG_LENGTH GENMASK(7, 0)
|
||||
+#define QCA808X_CDT_DIAG_LENGTH_SAME_SHORT GENMASK(15, 8)
|
||||
+#define QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT GENMASK(7, 0)
|
||||
|
||||
#define QCA808X_CDT_CODE_PAIR_A GENMASK(15, 12)
|
||||
#define QCA808X_CDT_CODE_PAIR_B GENMASK(11, 8)
|
||||
#define QCA808X_CDT_CODE_PAIR_C GENMASK(7, 4)
|
||||
#define QCA808X_CDT_CODE_PAIR_D GENMASK(3, 0)
|
||||
-#define QCA808X_CDT_STATUS_STAT_FAIL 0
|
||||
-#define QCA808X_CDT_STATUS_STAT_NORMAL 1
|
||||
-#define QCA808X_CDT_STATUS_STAT_OPEN 2
|
||||
-#define QCA808X_CDT_STATUS_STAT_SHORT 3
|
||||
+
|
||||
+#define QCA808X_CDT_STATUS_STAT_TYPE GENMASK(1, 0)
|
||||
+#define QCA808X_CDT_STATUS_STAT_FAIL FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 0)
|
||||
+#define QCA808X_CDT_STATUS_STAT_NORMAL FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 1)
|
||||
+#define QCA808X_CDT_STATUS_STAT_SAME_OPEN FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 2)
|
||||
+#define QCA808X_CDT_STATUS_STAT_SAME_SHORT FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 3)
|
||||
+
|
||||
+#define QCA808X_CDT_STATUS_STAT_MDI GENMASK(3, 2)
|
||||
+#define QCA808X_CDT_STATUS_STAT_MDI1 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 1)
|
||||
+#define QCA808X_CDT_STATUS_STAT_MDI2 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 2)
|
||||
+#define QCA808X_CDT_STATUS_STAT_MDI3 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 3)
|
||||
+
|
||||
+/* NORMAL are MDI with type set to 0 */
|
||||
+#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI1
|
||||
+#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
|
||||
+ QCA808X_CDT_STATUS_STAT_MDI1)
|
||||
+#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
|
||||
+ QCA808X_CDT_STATUS_STAT_MDI1)
|
||||
+#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI2
|
||||
+#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
|
||||
+ QCA808X_CDT_STATUS_STAT_MDI2)
|
||||
+#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
|
||||
+ QCA808X_CDT_STATUS_STAT_MDI2)
|
||||
+#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI3
|
||||
+#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
|
||||
+ QCA808X_CDT_STATUS_STAT_MDI3)
|
||||
+#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
|
||||
+ QCA808X_CDT_STATUS_STAT_MDI3)
|
||||
+
|
||||
+/* Added for reference of existence but should be handled by wait_for_completion already */
|
||||
+#define QCA808X_CDT_STATUS_STAT_BUSY (BIT(1) | BIT(3))
|
||||
|
||||
/* QCA808X 1G chip type */
|
||||
#define QCA808X_PHY_MMD7_CHIP_TYPE 0x901d
|
||||
@@ -1941,8 +1970,17 @@ static int qca808x_soft_reset(struct phy
|
||||
static bool qca808x_cdt_fault_length_valid(int cdt_code)
|
||||
{
|
||||
switch (cdt_code) {
|
||||
- case QCA808X_CDT_STATUS_STAT_SHORT:
|
||||
- case QCA808X_CDT_STATUS_STAT_OPEN:
|
||||
+ case QCA808X_CDT_STATUS_STAT_SAME_SHORT:
|
||||
+ case QCA808X_CDT_STATUS_STAT_SAME_OPEN:
|
||||
+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL:
|
||||
+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN:
|
||||
+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT:
|
||||
+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL:
|
||||
+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN:
|
||||
+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT:
|
||||
+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL:
|
||||
+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN:
|
||||
+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
@@ -1954,17 +1992,28 @@ static int qca808x_cable_test_result_tra
|
||||
switch (cdt_code) {
|
||||
case QCA808X_CDT_STATUS_STAT_NORMAL:
|
||||
return ETHTOOL_A_CABLE_RESULT_CODE_OK;
|
||||
- case QCA808X_CDT_STATUS_STAT_SHORT:
|
||||
+ case QCA808X_CDT_STATUS_STAT_SAME_SHORT:
|
||||
return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT;
|
||||
- case QCA808X_CDT_STATUS_STAT_OPEN:
|
||||
+ case QCA808X_CDT_STATUS_STAT_SAME_OPEN:
|
||||
return ETHTOOL_A_CABLE_RESULT_CODE_OPEN;
|
||||
+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL:
|
||||
+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN:
|
||||
+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT:
|
||||
+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL:
|
||||
+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN:
|
||||
+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT:
|
||||
+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL:
|
||||
+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN:
|
||||
+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT:
|
||||
+ return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT;
|
||||
case QCA808X_CDT_STATUS_STAT_FAIL:
|
||||
default:
|
||||
return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC;
|
||||
}
|
||||
}
|
||||
|
||||
-static int qca808x_cdt_fault_length(struct phy_device *phydev, int pair)
|
||||
+static int qca808x_cdt_fault_length(struct phy_device *phydev, int pair,
|
||||
+ int result)
|
||||
{
|
||||
int val;
|
||||
u32 cdt_length_reg = 0;
|
||||
@@ -1990,7 +2039,11 @@ static int qca808x_cdt_fault_length(stru
|
||||
if (val < 0)
|
||||
return val;
|
||||
|
||||
- val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH, val);
|
||||
+ if (result == ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT)
|
||||
+ val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_SAME_SHORT, val);
|
||||
+ else
|
||||
+ val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT, val);
|
||||
+
|
||||
return at803x_cdt_fault_length(val);
|
||||
}
|
||||
|
||||
@@ -2038,8 +2091,8 @@ static int qca808x_cable_test_start(stru
|
||||
static int qca808x_cable_test_get_pair_status(struct phy_device *phydev, u8 pair,
|
||||
u16 status)
|
||||
{
|
||||
+ int length, result;
|
||||
u16 pair_code;
|
||||
- int length;
|
||||
|
||||
switch (pair) {
|
||||
case ETHTOOL_A_CABLE_PAIR_A:
|
||||
@@ -2058,11 +2111,11 @@ static int qca808x_cable_test_get_pair_s
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
- ethnl_cable_test_result(phydev, pair,
|
||||
- qca808x_cable_test_result_trans(pair_code));
|
||||
+ result = qca808x_cable_test_result_trans(pair_code);
|
||||
+ ethnl_cable_test_result(phydev, pair, result);
|
||||
|
||||
if (qca808x_cdt_fault_length_valid(pair_code)) {
|
||||
- length = qca808x_cdt_fault_length(phydev, pair);
|
||||
+ length = qca808x_cdt_fault_length(phydev, pair, result);
|
||||
ethnl_cable_test_fault_length(phydev, pair, length);
|
||||
}
|
||||
|
||||
@@ -2076,8 +2129,7 @@ static int qca808x_cable_test_get_status
|
||||
*finished = false;
|
||||
|
||||
val = QCA808X_CDT_ENABLE_TEST |
|
||||
- QCA808X_CDT_LENGTH_UNIT |
|
||||
- QCA808X_CDT_INTER_CHECK_DIS;
|
||||
+ QCA808X_CDT_LENGTH_UNIT;
|
||||
ret = at803x_cdt_start(phydev, val);
|
||||
if (ret)
|
||||
return ret;
|
@ -0,0 +1,62 @@
|
||||
From c34d9452d4e5d98a655d7b625e85466320885416 Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Thu, 4 Jan 2024 22:30:41 +0100
|
||||
Subject: [PATCH 4/4] net: phy: at803x: make read_status more generic
|
||||
|
||||
Make read_status more generic in preparation on moving it to shared
|
||||
library as other PHY Family Driver will have the exact same
|
||||
implementation.
|
||||
|
||||
The only specific part was a check for AR8031/33 if 1000basex was used.
|
||||
The check is moved to a dedicated function specific for those PHYs.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Reviewed-by: Simon Horman <horms@kernel.org>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/at803x.c | 17 ++++++++++++-----
|
||||
1 file changed, 12 insertions(+), 5 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/at803x.c
|
||||
+++ b/drivers/net/phy/at803x.c
|
||||
@@ -1020,13 +1020,9 @@ static int at803x_read_specific_status(s
|
||||
|
||||
static int at803x_read_status(struct phy_device *phydev)
|
||||
{
|
||||
- struct at803x_priv *priv = phydev->priv;
|
||||
struct at803x_ss_mask ss_mask = { 0 };
|
||||
int err, old_link = phydev->link;
|
||||
|
||||
- if (priv->is_1000basex)
|
||||
- return genphy_c37_read_status(phydev);
|
||||
-
|
||||
/* Update the link, but return if there was an error */
|
||||
err = genphy_update_link(phydev);
|
||||
if (err)
|
||||
@@ -1618,6 +1614,17 @@ static int at8031_config_intr(struct phy
|
||||
return at803x_config_intr(phydev);
|
||||
}
|
||||
|
||||
+/* AR8031 and AR8033 share the same read status logic */
|
||||
+static int at8031_read_status(struct phy_device *phydev)
|
||||
+{
|
||||
+ struct at803x_priv *priv = phydev->priv;
|
||||
+
|
||||
+ if (priv->is_1000basex)
|
||||
+ return genphy_c37_read_status(phydev);
|
||||
+
|
||||
+ return at803x_read_status(phydev);
|
||||
+}
|
||||
+
|
||||
/* AR8031 and AR8035 share the same cable test get status reg */
|
||||
static int at8031_cable_test_get_status(struct phy_device *phydev,
|
||||
bool *finished)
|
||||
@@ -2281,7 +2288,7 @@ static struct phy_driver at803x_driver[]
|
||||
.read_page = at803x_read_page,
|
||||
.write_page = at803x_write_page,
|
||||
.get_features = at803x_get_features,
|
||||
- .read_status = at803x_read_status,
|
||||
+ .read_status = at8031_read_status,
|
||||
.config_intr = at8031_config_intr,
|
||||
.handle_interrupt = at803x_handle_interrupt,
|
||||
.get_tunable = at803x_get_tunable,
|
@ -0,0 +1,408 @@
|
||||
From 7196062b64ee470b91015f3d2e82d225948258ea Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Thu, 25 Jan 2024 21:37:01 +0100
|
||||
Subject: [PATCH 5/5] net: phy: at803x: add LED support for qca808x
|
||||
|
||||
Add LED support for QCA8081 PHY.
|
||||
|
||||
Documentation for this LEDs PHY is very scarce even with NDA access
|
||||
to Documentation for OEMs. Only the blink pattern are documented and are
|
||||
very confusing most of the time. No documentation is present about
|
||||
forcing the LED on/off or to always blink.
|
||||
|
||||
Those settings were reversed by poking the regs and trying to find the
|
||||
correct bits to trigger these modes. Some bits mode are not clear and
|
||||
maybe the documentation option are not 100% correct. For the sake of LED
|
||||
support the reversed option are enough to add support for current LED
|
||||
APIs.
|
||||
|
||||
Supported HW control modes are:
|
||||
- tx
|
||||
- rx
|
||||
- link_10
|
||||
- link_100
|
||||
- link_1000
|
||||
- link_2500
|
||||
- half_duplex
|
||||
- full_duplex
|
||||
|
||||
Also add support for LED polarity set to set LED polarity to active
|
||||
high or low. QSDK sets this value to high by default but PHY reset value
|
||||
doesn't have this enabled by default.
|
||||
|
||||
QSDK also sets 2 additional bits but their usage is not clear, info about
|
||||
this is added in the header. It was verified that for correct function
|
||||
of the LED if active high is needed, only BIT 6 is needed.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Link: https://lore.kernel.org/r/20240125203702.4552-6-ansuelsmth@gmail.com
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
---
|
||||
drivers/net/phy/at803x.c | 327 +++++++++++++++++++++++++++++++++++++++
|
||||
1 file changed, 327 insertions(+)
|
||||
|
||||
--- a/drivers/net/phy/at803x.c
|
||||
+++ b/drivers/net/phy/at803x.c
|
||||
@@ -301,6 +301,87 @@
|
||||
/* Added for reference of existence but should be handled by wait_for_completion already */
|
||||
#define QCA808X_CDT_STATUS_STAT_BUSY (BIT(1) | BIT(3))
|
||||
|
||||
+#define QCA808X_MMD7_LED_GLOBAL 0x8073
|
||||
+#define QCA808X_LED_BLINK_1 GENMASK(11, 6)
|
||||
+#define QCA808X_LED_BLINK_2 GENMASK(5, 0)
|
||||
+/* Values are the same for both BLINK_1 and BLINK_2 */
|
||||
+#define QCA808X_LED_BLINK_FREQ_MASK GENMASK(5, 3)
|
||||
+#define QCA808X_LED_BLINK_FREQ_2HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x0)
|
||||
+#define QCA808X_LED_BLINK_FREQ_4HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x1)
|
||||
+#define QCA808X_LED_BLINK_FREQ_8HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x2)
|
||||
+#define QCA808X_LED_BLINK_FREQ_16HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x3)
|
||||
+#define QCA808X_LED_BLINK_FREQ_32HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x4)
|
||||
+#define QCA808X_LED_BLINK_FREQ_64HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x5)
|
||||
+#define QCA808X_LED_BLINK_FREQ_128HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x6)
|
||||
+#define QCA808X_LED_BLINK_FREQ_256HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x7)
|
||||
+#define QCA808X_LED_BLINK_DUTY_MASK GENMASK(2, 0)
|
||||
+#define QCA808X_LED_BLINK_DUTY_50_50 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x0)
|
||||
+#define QCA808X_LED_BLINK_DUTY_75_25 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x1)
|
||||
+#define QCA808X_LED_BLINK_DUTY_25_75 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x2)
|
||||
+#define QCA808X_LED_BLINK_DUTY_33_67 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x3)
|
||||
+#define QCA808X_LED_BLINK_DUTY_67_33 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x4)
|
||||
+#define QCA808X_LED_BLINK_DUTY_17_83 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x5)
|
||||
+#define QCA808X_LED_BLINK_DUTY_83_17 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x6)
|
||||
+#define QCA808X_LED_BLINK_DUTY_8_92 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x7)
|
||||
+
|
||||
+#define QCA808X_MMD7_LED2_CTRL 0x8074
|
||||
+#define QCA808X_MMD7_LED2_FORCE_CTRL 0x8075
|
||||
+#define QCA808X_MMD7_LED1_CTRL 0x8076
|
||||
+#define QCA808X_MMD7_LED1_FORCE_CTRL 0x8077
|
||||
+#define QCA808X_MMD7_LED0_CTRL 0x8078
|
||||
+#define QCA808X_MMD7_LED_CTRL(x) (0x8078 - ((x) * 2))
|
||||
+
|
||||
+/* LED hw control pattern is the same for every LED */
|
||||
+#define QCA808X_LED_PATTERN_MASK GENMASK(15, 0)
|
||||
+#define QCA808X_LED_SPEED2500_ON BIT(15)
|
||||
+#define QCA808X_LED_SPEED2500_BLINK BIT(14)
|
||||
+/* Follow blink trigger even if duplex or speed condition doesn't match */
|
||||
+#define QCA808X_LED_BLINK_CHECK_BYPASS BIT(13)
|
||||
+#define QCA808X_LED_FULL_DUPLEX_ON BIT(12)
|
||||
+#define QCA808X_LED_HALF_DUPLEX_ON BIT(11)
|
||||
+#define QCA808X_LED_TX_BLINK BIT(10)
|
||||
+#define QCA808X_LED_RX_BLINK BIT(9)
|
||||
+#define QCA808X_LED_TX_ON_10MS BIT(8)
|
||||
+#define QCA808X_LED_RX_ON_10MS BIT(7)
|
||||
+#define QCA808X_LED_SPEED1000_ON BIT(6)
|
||||
+#define QCA808X_LED_SPEED100_ON BIT(5)
|
||||
+#define QCA808X_LED_SPEED10_ON BIT(4)
|
||||
+#define QCA808X_LED_COLLISION_BLINK BIT(3)
|
||||
+#define QCA808X_LED_SPEED1000_BLINK BIT(2)
|
||||
+#define QCA808X_LED_SPEED100_BLINK BIT(1)
|
||||
+#define QCA808X_LED_SPEED10_BLINK BIT(0)
|
||||
+
|
||||
+#define QCA808X_MMD7_LED0_FORCE_CTRL 0x8079
|
||||
+#define QCA808X_MMD7_LED_FORCE_CTRL(x) (0x8079 - ((x) * 2))
|
||||
+
|
||||
+/* LED force ctrl is the same for every LED
|
||||
+ * No documentation exist for this, not even internal one
|
||||
+ * with NDA as QCOM gives only info about configuring
|
||||
+ * hw control pattern rules and doesn't indicate any way
|
||||
+ * to force the LED to specific mode.
|
||||
+ * These define comes from reverse and testing and maybe
|
||||
+ * lack of some info or some info are not entirely correct.
|
||||
+ * For the basic LED control and hw control these finding
|
||||
+ * are enough to support LED control in all the required APIs.
|
||||
+ *
|
||||
+ * On doing some comparison with implementation with qca807x,
|
||||
+ * it was found that it's 1:1 equal to it and confirms all the
|
||||
+ * reverse done. It was also found further specification with the
|
||||
+ * force mode and the blink modes.
|
||||
+ */
|
||||
+#define QCA808X_LED_FORCE_EN BIT(15)
|
||||
+#define QCA808X_LED_FORCE_MODE_MASK GENMASK(14, 13)
|
||||
+#define QCA808X_LED_FORCE_BLINK_1 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x3)
|
||||
+#define QCA808X_LED_FORCE_BLINK_2 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x2)
|
||||
+#define QCA808X_LED_FORCE_ON FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x1)
|
||||
+#define QCA808X_LED_FORCE_OFF FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x0)
|
||||
+
|
||||
+#define QCA808X_MMD7_LED_POLARITY_CTRL 0x901a
|
||||
+/* QSDK sets by default 0x46 to this reg that sets BIT 6 for
|
||||
+ * LED to active high. It's not clear what BIT 3 and BIT 4 does.
|
||||
+ */
|
||||
+#define QCA808X_LED_ACTIVE_HIGH BIT(6)
|
||||
+
|
||||
/* QCA808X 1G chip type */
|
||||
#define QCA808X_PHY_MMD7_CHIP_TYPE 0x901d
|
||||
#define QCA808X_PHY_CHIP_TYPE_1G BIT(0)
|
||||
@@ -346,6 +427,7 @@ struct at803x_priv {
|
||||
struct regulator_dev *vddio_rdev;
|
||||
struct regulator_dev *vddh_rdev;
|
||||
u64 stats[ARRAY_SIZE(qca83xx_hw_stats)];
|
||||
+ int led_polarity_mode;
|
||||
};
|
||||
|
||||
struct at803x_context {
|
||||
@@ -706,6 +788,9 @@ static int at803x_probe(struct phy_devic
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
+ /* Init LED polarity mode to -1 */
|
||||
+ priv->led_polarity_mode = -1;
|
||||
+
|
||||
phydev->priv = priv;
|
||||
|
||||
ret = at803x_parse_dt(phydev);
|
||||
@@ -2235,6 +2320,242 @@ static void qca808x_link_change_notify(s
|
||||
phydev->link ? QCA8081_PHY_FIFO_RSTN : 0);
|
||||
}
|
||||
|
||||
+static int qca808x_led_parse_netdev(struct phy_device *phydev, unsigned long rules,
|
||||
+ u16 *offload_trigger)
|
||||
+{
|
||||
+ /* Parsing specific to netdev trigger */
|
||||
+ if (test_bit(TRIGGER_NETDEV_TX, &rules))
|
||||
+ *offload_trigger |= QCA808X_LED_TX_BLINK;
|
||||
+ if (test_bit(TRIGGER_NETDEV_RX, &rules))
|
||||
+ *offload_trigger |= QCA808X_LED_RX_BLINK;
|
||||
+ if (test_bit(TRIGGER_NETDEV_LINK_10, &rules))
|
||||
+ *offload_trigger |= QCA808X_LED_SPEED10_ON;
|
||||
+ if (test_bit(TRIGGER_NETDEV_LINK_100, &rules))
|
||||
+ *offload_trigger |= QCA808X_LED_SPEED100_ON;
|
||||
+ if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules))
|
||||
+ *offload_trigger |= QCA808X_LED_SPEED1000_ON;
|
||||
+ if (test_bit(TRIGGER_NETDEV_LINK_2500, &rules))
|
||||
+ *offload_trigger |= QCA808X_LED_SPEED2500_ON;
|
||||
+ if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &rules))
|
||||
+ *offload_trigger |= QCA808X_LED_HALF_DUPLEX_ON;
|
||||
+ if (test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &rules))
|
||||
+ *offload_trigger |= QCA808X_LED_FULL_DUPLEX_ON;
|
||||
+
|
||||
+ if (rules && !*offload_trigger)
|
||||
+ return -EOPNOTSUPP;
|
||||
+
|
||||
+ /* Enable BLINK_CHECK_BYPASS by default to make the LED
|
||||
+ * blink even with duplex or speed mode not enabled.
|
||||
+ */
|
||||
+ *offload_trigger |= QCA808X_LED_BLINK_CHECK_BYPASS;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int qca808x_led_hw_control_enable(struct phy_device *phydev, u8 index)
|
||||
+{
|
||||
+ u16 reg;
|
||||
+
|
||||
+ if (index > 2)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
|
||||
+
|
||||
+ return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg,
|
||||
+ QCA808X_LED_FORCE_EN);
|
||||
+}
|
||||
+
|
||||
+static int qca808x_led_hw_is_supported(struct phy_device *phydev, u8 index,
|
||||
+ unsigned long rules)
|
||||
+{
|
||||
+ u16 offload_trigger = 0;
|
||||
+
|
||||
+ if (index > 2)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ return qca808x_led_parse_netdev(phydev, rules, &offload_trigger);
|
||||
+}
|
||||
+
|
||||
+static int qca808x_led_hw_control_set(struct phy_device *phydev, u8 index,
|
||||
+ unsigned long rules)
|
||||
+{
|
||||
+ u16 reg, offload_trigger = 0;
|
||||
+ int ret;
|
||||
+
|
||||
+ if (index > 2)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ reg = QCA808X_MMD7_LED_CTRL(index);
|
||||
+
|
||||
+ ret = qca808x_led_parse_netdev(phydev, rules, &offload_trigger);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ ret = qca808x_led_hw_control_enable(phydev, index);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ return phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
|
||||
+ QCA808X_LED_PATTERN_MASK,
|
||||
+ offload_trigger);
|
||||
+}
|
||||
+
|
||||
+static bool qca808x_led_hw_control_status(struct phy_device *phydev, u8 index)
|
||||
+{
|
||||
+ u16 reg;
|
||||
+ int val;
|
||||
+
|
||||
+ if (index > 2)
|
||||
+ return false;
|
||||
+
|
||||
+ reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
|
||||
+
|
||||
+ val = phy_read_mmd(phydev, MDIO_MMD_AN, reg);
|
||||
+
|
||||
+ return !(val & QCA808X_LED_FORCE_EN);
|
||||
+}
|
||||
+
|
||||
+static int qca808x_led_hw_control_get(struct phy_device *phydev, u8 index,
|
||||
+ unsigned long *rules)
|
||||
+{
|
||||
+ u16 reg;
|
||||
+ int val;
|
||||
+
|
||||
+ if (index > 2)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ /* Check if we have hw control enabled */
|
||||
+ if (qca808x_led_hw_control_status(phydev, index))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ reg = QCA808X_MMD7_LED_CTRL(index);
|
||||
+
|
||||
+ val = phy_read_mmd(phydev, MDIO_MMD_AN, reg);
|
||||
+ if (val & QCA808X_LED_TX_BLINK)
|
||||
+ set_bit(TRIGGER_NETDEV_TX, rules);
|
||||
+ if (val & QCA808X_LED_RX_BLINK)
|
||||
+ set_bit(TRIGGER_NETDEV_RX, rules);
|
||||
+ if (val & QCA808X_LED_SPEED10_ON)
|
||||
+ set_bit(TRIGGER_NETDEV_LINK_10, rules);
|
||||
+ if (val & QCA808X_LED_SPEED100_ON)
|
||||
+ set_bit(TRIGGER_NETDEV_LINK_100, rules);
|
||||
+ if (val & QCA808X_LED_SPEED1000_ON)
|
||||
+ set_bit(TRIGGER_NETDEV_LINK_1000, rules);
|
||||
+ if (val & QCA808X_LED_SPEED2500_ON)
|
||||
+ set_bit(TRIGGER_NETDEV_LINK_2500, rules);
|
||||
+ if (val & QCA808X_LED_HALF_DUPLEX_ON)
|
||||
+ set_bit(TRIGGER_NETDEV_HALF_DUPLEX, rules);
|
||||
+ if (val & QCA808X_LED_FULL_DUPLEX_ON)
|
||||
+ set_bit(TRIGGER_NETDEV_FULL_DUPLEX, rules);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int qca808x_led_hw_control_reset(struct phy_device *phydev, u8 index)
|
||||
+{
|
||||
+ u16 reg;
|
||||
+
|
||||
+ if (index > 2)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ reg = QCA808X_MMD7_LED_CTRL(index);
|
||||
+
|
||||
+ return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg,
|
||||
+ QCA808X_LED_PATTERN_MASK);
|
||||
+}
|
||||
+
|
||||
+static int qca808x_led_brightness_set(struct phy_device *phydev,
|
||||
+ u8 index, enum led_brightness value)
|
||||
+{
|
||||
+ u16 reg;
|
||||
+ int ret;
|
||||
+
|
||||
+ if (index > 2)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ if (!value) {
|
||||
+ ret = qca808x_led_hw_control_reset(phydev, index);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
|
||||
+
|
||||
+ return phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
|
||||
+ QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK,
|
||||
+ QCA808X_LED_FORCE_EN | value ? QCA808X_LED_FORCE_ON :
|
||||
+ QCA808X_LED_FORCE_OFF);
|
||||
+}
|
||||
+
|
||||
+static int qca808x_led_blink_set(struct phy_device *phydev, u8 index,
|
||||
+ unsigned long *delay_on,
|
||||
+ unsigned long *delay_off)
|
||||
+{
|
||||
+ int ret;
|
||||
+ u16 reg;
|
||||
+
|
||||
+ if (index > 2)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
|
||||
+
|
||||
+ /* Set blink to 50% off, 50% on at 4Hz by default */
|
||||
+ ret = phy_modify_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_LED_GLOBAL,
|
||||
+ QCA808X_LED_BLINK_FREQ_MASK | QCA808X_LED_BLINK_DUTY_MASK,
|
||||
+ QCA808X_LED_BLINK_FREQ_4HZ | QCA808X_LED_BLINK_DUTY_50_50);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ /* We use BLINK_1 for normal blinking */
|
||||
+ ret = phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
|
||||
+ QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK,
|
||||
+ QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_BLINK_1);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ /* We set blink to 4Hz, aka 250ms */
|
||||
+ *delay_on = 250 / 2;
|
||||
+ *delay_off = 250 / 2;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int qca808x_led_polarity_set(struct phy_device *phydev, int index,
|
||||
+ unsigned long modes)
|
||||
+{
|
||||
+ struct at803x_priv *priv = phydev->priv;
|
||||
+ bool active_low = false;
|
||||
+ u32 mode;
|
||||
+
|
||||
+ for_each_set_bit(mode, &modes, __PHY_LED_MODES_NUM) {
|
||||
+ switch (mode) {
|
||||
+ case PHY_LED_ACTIVE_LOW:
|
||||
+ active_low = true;
|
||||
+ break;
|
||||
+ default:
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /* PHY polarity is global and can't be set per LED.
|
||||
+ * To detect this, check if last requested polarity mode
|
||||
+ * match the new one.
|
||||
+ */
|
||||
+ if (priv->led_polarity_mode >= 0 &&
|
||||
+ priv->led_polarity_mode != active_low) {
|
||||
+ phydev_err(phydev, "PHY polarity is global. Mismatched polarity on different LED\n");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ /* Save the last PHY polarity mode */
|
||||
+ priv->led_polarity_mode = active_low;
|
||||
+
|
||||
+ return phy_modify_mmd(phydev, MDIO_MMD_AN,
|
||||
+ QCA808X_MMD7_LED_POLARITY_CTRL,
|
||||
+ QCA808X_LED_ACTIVE_HIGH,
|
||||
+ active_low ? 0 : QCA808X_LED_ACTIVE_HIGH);
|
||||
+}
|
||||
+
|
||||
static struct phy_driver at803x_driver[] = {
|
||||
{
|
||||
/* Qualcomm Atheros AR8035 */
|
||||
@@ -2411,6 +2732,12 @@ static struct phy_driver at803x_driver[]
|
||||
.cable_test_start = qca808x_cable_test_start,
|
||||
.cable_test_get_status = qca808x_cable_test_get_status,
|
||||
.link_change_notify = qca808x_link_change_notify,
|
||||
+ .led_brightness_set = qca808x_led_brightness_set,
|
||||
+ .led_blink_set = qca808x_led_blink_set,
|
||||
+ .led_hw_is_supported = qca808x_led_hw_is_supported,
|
||||
+ .led_hw_control_set = qca808x_led_hw_control_set,
|
||||
+ .led_hw_control_get = qca808x_led_hw_control_get,
|
||||
+ .led_polarity_set = qca808x_led_polarity_set,
|
||||
}, };
|
||||
|
||||
module_phy_driver(at803x_driver);
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,243 @@
|
||||
From 6fb760972c49490b03f3db2ad64cf30bdd28c54a Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Mon, 29 Jan 2024 15:15:20 +0100
|
||||
Subject: [PATCH 2/5] net: phy: qcom: create and move functions to shared
|
||||
library
|
||||
|
||||
Create and move functions to shared library in preparation for qca83xx
|
||||
PHY Family to be detached from at803x driver.
|
||||
|
||||
Only the shared defines are moved to the shared qcom.h header.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Link: https://lore.kernel.org/r/20240129141600.2592-3-ansuelsmth@gmail.com
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
---
|
||||
drivers/net/phy/qcom/Kconfig | 4 ++
|
||||
drivers/net/phy/qcom/Makefile | 1 +
|
||||
drivers/net/phy/qcom/at803x.c | 69 +----------------------------
|
||||
drivers/net/phy/qcom/qcom-phy-lib.c | 53 ++++++++++++++++++++++
|
||||
drivers/net/phy/qcom/qcom.h | 34 ++++++++++++++
|
||||
5 files changed, 94 insertions(+), 67 deletions(-)
|
||||
create mode 100644 drivers/net/phy/qcom/qcom-phy-lib.c
|
||||
create mode 100644 drivers/net/phy/qcom/qcom.h
|
||||
|
||||
--- a/drivers/net/phy/qcom/Kconfig
|
||||
+++ b/drivers/net/phy/qcom/Kconfig
|
||||
@@ -1,6 +1,10 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
+config QCOM_NET_PHYLIB
|
||||
+ tristate
|
||||
+
|
||||
config AT803X_PHY
|
||||
tristate "Qualcomm Atheros AR803X PHYs and QCA833x PHYs"
|
||||
+ select QCOM_NET_PHYLIB
|
||||
depends on REGULATOR
|
||||
help
|
||||
Currently supports the AR8030, AR8031, AR8033, AR8035 and internal
|
||||
--- a/drivers/net/phy/qcom/Makefile
|
||||
+++ b/drivers/net/phy/qcom/Makefile
|
||||
@@ -1,2 +1,3 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
+obj-$(CONFIG_QCOM_NET_PHYLIB) += qcom-phy-lib.o
|
||||
obj-$(CONFIG_AT803X_PHY) += at803x.o
|
||||
--- a/drivers/net/phy/qcom/at803x.c
|
||||
+++ b/drivers/net/phy/qcom/at803x.c
|
||||
@@ -22,6 +22,8 @@
|
||||
#include <linux/sfp.h>
|
||||
#include <dt-bindings/net/qca-ar803x.h>
|
||||
|
||||
+#include "qcom.h"
|
||||
+
|
||||
#define AT803X_SPECIFIC_FUNCTION_CONTROL 0x10
|
||||
#define AT803X_SFC_ASSERT_CRS BIT(11)
|
||||
#define AT803X_SFC_FORCE_LINK BIT(10)
|
||||
@@ -84,9 +86,6 @@
|
||||
#define AT803X_REG_CHIP_CONFIG 0x1f
|
||||
#define AT803X_BT_BX_REG_SEL 0x8000
|
||||
|
||||
-#define AT803X_DEBUG_ADDR 0x1D
|
||||
-#define AT803X_DEBUG_DATA 0x1E
|
||||
-
|
||||
#define AT803X_MODE_CFG_MASK 0x0F
|
||||
#define AT803X_MODE_CFG_BASET_RGMII 0x00
|
||||
#define AT803X_MODE_CFG_BASET_SGMII 0x01
|
||||
@@ -103,19 +102,6 @@
|
||||
#define AT803X_PSSR 0x11 /*PHY-Specific Status Register*/
|
||||
#define AT803X_PSSR_MR_AN_COMPLETE 0x0200
|
||||
|
||||
-#define AT803X_DEBUG_ANALOG_TEST_CTRL 0x00
|
||||
-#define QCA8327_DEBUG_MANU_CTRL_EN BIT(2)
|
||||
-#define QCA8337_DEBUG_MANU_CTRL_EN GENMASK(3, 2)
|
||||
-#define AT803X_DEBUG_RX_CLK_DLY_EN BIT(15)
|
||||
-
|
||||
-#define AT803X_DEBUG_SYSTEM_CTRL_MODE 0x05
|
||||
-#define AT803X_DEBUG_TX_CLK_DLY_EN BIT(8)
|
||||
-
|
||||
-#define AT803X_DEBUG_REG_HIB_CTRL 0x0b
|
||||
-#define AT803X_DEBUG_HIB_CTRL_SEL_RST_80U BIT(10)
|
||||
-#define AT803X_DEBUG_HIB_CTRL_EN_ANY_CHANGE BIT(13)
|
||||
-#define AT803X_DEBUG_HIB_CTRL_PS_HIB_EN BIT(15)
|
||||
-
|
||||
#define AT803X_DEBUG_REG_3C 0x3C
|
||||
|
||||
#define AT803X_DEBUG_REG_GREEN 0x3D
|
||||
@@ -393,18 +379,6 @@ MODULE_DESCRIPTION("Qualcomm Atheros AR8
|
||||
MODULE_AUTHOR("Matus Ujhelyi");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
-enum stat_access_type {
|
||||
- PHY,
|
||||
- MMD
|
||||
-};
|
||||
-
|
||||
-struct at803x_hw_stat {
|
||||
- const char *string;
|
||||
- u8 reg;
|
||||
- u32 mask;
|
||||
- enum stat_access_type access_type;
|
||||
-};
|
||||
-
|
||||
static struct at803x_hw_stat qca83xx_hw_stats[] = {
|
||||
{ "phy_idle_errors", 0xa, GENMASK(7, 0), PHY},
|
||||
{ "phy_receive_errors", 0x15, GENMASK(15, 0), PHY},
|
||||
@@ -439,45 +413,6 @@ struct at803x_context {
|
||||
u16 led_control;
|
||||
};
|
||||
|
||||
-static int at803x_debug_reg_write(struct phy_device *phydev, u16 reg, u16 data)
|
||||
-{
|
||||
- int ret;
|
||||
-
|
||||
- ret = phy_write(phydev, AT803X_DEBUG_ADDR, reg);
|
||||
- if (ret < 0)
|
||||
- return ret;
|
||||
-
|
||||
- return phy_write(phydev, AT803X_DEBUG_DATA, data);
|
||||
-}
|
||||
-
|
||||
-static int at803x_debug_reg_read(struct phy_device *phydev, u16 reg)
|
||||
-{
|
||||
- int ret;
|
||||
-
|
||||
- ret = phy_write(phydev, AT803X_DEBUG_ADDR, reg);
|
||||
- if (ret < 0)
|
||||
- return ret;
|
||||
-
|
||||
- return phy_read(phydev, AT803X_DEBUG_DATA);
|
||||
-}
|
||||
-
|
||||
-static int at803x_debug_reg_mask(struct phy_device *phydev, u16 reg,
|
||||
- u16 clear, u16 set)
|
||||
-{
|
||||
- u16 val;
|
||||
- int ret;
|
||||
-
|
||||
- ret = at803x_debug_reg_read(phydev, reg);
|
||||
- if (ret < 0)
|
||||
- return ret;
|
||||
-
|
||||
- val = ret & 0xffff;
|
||||
- val &= ~clear;
|
||||
- val |= set;
|
||||
-
|
||||
- return phy_write(phydev, AT803X_DEBUG_DATA, val);
|
||||
-}
|
||||
-
|
||||
static int at803x_write_page(struct phy_device *phydev, int page)
|
||||
{
|
||||
int mask;
|
||||
--- /dev/null
|
||||
+++ b/drivers/net/phy/qcom/qcom-phy-lib.c
|
||||
@@ -0,0 +1,53 @@
|
||||
+// SPDX-License-Identifier: GPL-2.0
|
||||
+
|
||||
+#include <linux/phy.h>
|
||||
+#include <linux/module.h>
|
||||
+
|
||||
+#include "qcom.h"
|
||||
+
|
||||
+MODULE_DESCRIPTION("Qualcomm PHY driver Common Functions");
|
||||
+MODULE_AUTHOR("Matus Ujhelyi");
|
||||
+MODULE_AUTHOR("Christian Marangi <ansuelsmth@gmail.com>");
|
||||
+MODULE_LICENSE("GPL");
|
||||
+
|
||||
+int at803x_debug_reg_read(struct phy_device *phydev, u16 reg)
|
||||
+{
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = phy_write(phydev, AT803X_DEBUG_ADDR, reg);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ return phy_read(phydev, AT803X_DEBUG_DATA);
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(at803x_debug_reg_read);
|
||||
+
|
||||
+int at803x_debug_reg_mask(struct phy_device *phydev, u16 reg,
|
||||
+ u16 clear, u16 set)
|
||||
+{
|
||||
+ u16 val;
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = at803x_debug_reg_read(phydev, reg);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ val = ret & 0xffff;
|
||||
+ val &= ~clear;
|
||||
+ val |= set;
|
||||
+
|
||||
+ return phy_write(phydev, AT803X_DEBUG_DATA, val);
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(at803x_debug_reg_mask);
|
||||
+
|
||||
+int at803x_debug_reg_write(struct phy_device *phydev, u16 reg, u16 data)
|
||||
+{
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = phy_write(phydev, AT803X_DEBUG_ADDR, reg);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ return phy_write(phydev, AT803X_DEBUG_DATA, data);
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(at803x_debug_reg_write);
|
||||
--- /dev/null
|
||||
+++ b/drivers/net/phy/qcom/qcom.h
|
||||
@@ -0,0 +1,34 @@
|
||||
+/* SPDX-License-Identifier: GPL-2.0 */
|
||||
+
|
||||
+#define AT803X_DEBUG_ADDR 0x1D
|
||||
+#define AT803X_DEBUG_DATA 0x1E
|
||||
+
|
||||
+#define AT803X_DEBUG_ANALOG_TEST_CTRL 0x00
|
||||
+#define QCA8327_DEBUG_MANU_CTRL_EN BIT(2)
|
||||
+#define QCA8337_DEBUG_MANU_CTRL_EN GENMASK(3, 2)
|
||||
+#define AT803X_DEBUG_RX_CLK_DLY_EN BIT(15)
|
||||
+
|
||||
+#define AT803X_DEBUG_SYSTEM_CTRL_MODE 0x05
|
||||
+#define AT803X_DEBUG_TX_CLK_DLY_EN BIT(8)
|
||||
+
|
||||
+#define AT803X_DEBUG_REG_HIB_CTRL 0x0b
|
||||
+#define AT803X_DEBUG_HIB_CTRL_SEL_RST_80U BIT(10)
|
||||
+#define AT803X_DEBUG_HIB_CTRL_EN_ANY_CHANGE BIT(13)
|
||||
+#define AT803X_DEBUG_HIB_CTRL_PS_HIB_EN BIT(15)
|
||||
+
|
||||
+enum stat_access_type {
|
||||
+ PHY,
|
||||
+ MMD
|
||||
+};
|
||||
+
|
||||
+struct at803x_hw_stat {
|
||||
+ const char *string;
|
||||
+ u8 reg;
|
||||
+ u32 mask;
|
||||
+ enum stat_access_type access_type;
|
||||
+};
|
||||
+
|
||||
+int at803x_debug_reg_read(struct phy_device *phydev, u16 reg);
|
||||
+int at803x_debug_reg_mask(struct phy_device *phydev, u16 reg,
|
||||
+ u16 clear, u16 set);
|
||||
+int at803x_debug_reg_write(struct phy_device *phydev, u16 reg, u16 data);
|
@ -0,0 +1,638 @@
|
||||
From 2e45d404d99d43bb7127b74b5dea8818df64996c Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Mon, 29 Jan 2024 15:15:21 +0100
|
||||
Subject: [PATCH 3/5] net: phy: qcom: deatch qca83xx PHY driver from at803x
|
||||
|
||||
Deatch qca83xx PHY driver from at803x.
|
||||
|
||||
The QCA83xx PHYs implement specific function and doesn't use generic
|
||||
at803x so it can be detached from the driver and moved to a dedicated
|
||||
one.
|
||||
|
||||
Probe function and priv struct is reimplemented to allocate and use
|
||||
only the qca83xx specific data. Unused data from at803x PHY driver
|
||||
are dropped from at803x priv struct.
|
||||
|
||||
This is to make slimmer PHY drivers instead of including lots of bloat
|
||||
that would never be used in specific SoC.
|
||||
|
||||
A new Kconfig flag QCA83XX_PHY is introduced to compile the new
|
||||
introduced PHY driver.
|
||||
|
||||
As the Kconfig name starts with Qualcomm the same order is kept.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Link: https://lore.kernel.org/r/20240129141600.2592-4-ansuelsmth@gmail.com
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
---
|
||||
drivers/net/phy/qcom/Kconfig | 11 +-
|
||||
drivers/net/phy/qcom/Makefile | 1 +
|
||||
drivers/net/phy/qcom/at803x.c | 235 ----------------------------
|
||||
drivers/net/phy/qcom/qca83xx.c | 275 +++++++++++++++++++++++++++++++++
|
||||
4 files changed, 284 insertions(+), 238 deletions(-)
|
||||
create mode 100644 drivers/net/phy/qcom/qca83xx.c
|
||||
|
||||
--- a/drivers/net/phy/qcom/Kconfig
|
||||
+++ b/drivers/net/phy/qcom/Kconfig
|
||||
@@ -3,9 +3,14 @@ config QCOM_NET_PHYLIB
|
||||
tristate
|
||||
|
||||
config AT803X_PHY
|
||||
- tristate "Qualcomm Atheros AR803X PHYs and QCA833x PHYs"
|
||||
+ tristate "Qualcomm Atheros AR803X PHYs"
|
||||
select QCOM_NET_PHYLIB
|
||||
depends on REGULATOR
|
||||
help
|
||||
- Currently supports the AR8030, AR8031, AR8033, AR8035 and internal
|
||||
- QCA8337(Internal qca8k PHY) model
|
||||
+ Currently supports the AR8030, AR8031, AR8033, AR8035 model
|
||||
+
|
||||
+config QCA83XX_PHY
|
||||
+ tristate "Qualcomm Atheros QCA833x PHYs"
|
||||
+ select QCOM_NET_PHYLIB
|
||||
+ help
|
||||
+ Currently supports the internal QCA8337(Internal qca8k PHY) model
|
||||
--- a/drivers/net/phy/qcom/Makefile
|
||||
+++ b/drivers/net/phy/qcom/Makefile
|
||||
@@ -1,3 +1,4 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
obj-$(CONFIG_QCOM_NET_PHYLIB) += qcom-phy-lib.o
|
||||
obj-$(CONFIG_AT803X_PHY) += at803x.o
|
||||
+obj-$(CONFIG_QCA83XX_PHY) += qca83xx.o
|
||||
--- a/drivers/net/phy/qcom/at803x.c
|
||||
+++ b/drivers/net/phy/qcom/at803x.c
|
||||
@@ -102,17 +102,10 @@
|
||||
#define AT803X_PSSR 0x11 /*PHY-Specific Status Register*/
|
||||
#define AT803X_PSSR_MR_AN_COMPLETE 0x0200
|
||||
|
||||
-#define AT803X_DEBUG_REG_3C 0x3C
|
||||
-
|
||||
-#define AT803X_DEBUG_REG_GREEN 0x3D
|
||||
-#define AT803X_DEBUG_GATE_CLK_IN1000 BIT(6)
|
||||
-
|
||||
#define AT803X_DEBUG_REG_1F 0x1F
|
||||
#define AT803X_DEBUG_PLL_ON BIT(2)
|
||||
#define AT803X_DEBUG_RGMII_1V8 BIT(3)
|
||||
|
||||
-#define MDIO_AZ_DEBUG 0x800D
|
||||
-
|
||||
/* AT803x supports either the XTAL input pad, an internal PLL or the
|
||||
* DSP as clock reference for the clock output pad. The XTAL reference
|
||||
* is only used for 25 MHz output, all other frequencies need the PLL.
|
||||
@@ -163,13 +156,7 @@
|
||||
|
||||
#define QCA8081_PHY_ID 0x004dd101
|
||||
|
||||
-#define QCA8327_A_PHY_ID 0x004dd033
|
||||
-#define QCA8327_B_PHY_ID 0x004dd034
|
||||
-#define QCA8337_PHY_ID 0x004dd036
|
||||
#define QCA9561_PHY_ID 0x004dd042
|
||||
-#define QCA8K_PHY_ID_MASK 0xffffffff
|
||||
-
|
||||
-#define QCA8K_DEVFLAGS_REVISION_MASK GENMASK(2, 0)
|
||||
|
||||
#define AT803X_PAGE_FIBER 0
|
||||
#define AT803X_PAGE_COPPER 1
|
||||
@@ -379,12 +366,6 @@ MODULE_DESCRIPTION("Qualcomm Atheros AR8
|
||||
MODULE_AUTHOR("Matus Ujhelyi");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
-static struct at803x_hw_stat qca83xx_hw_stats[] = {
|
||||
- { "phy_idle_errors", 0xa, GENMASK(7, 0), PHY},
|
||||
- { "phy_receive_errors", 0x15, GENMASK(15, 0), PHY},
|
||||
- { "eee_wake_errors", 0x16, GENMASK(15, 0), MMD},
|
||||
-};
|
||||
-
|
||||
struct at803x_ss_mask {
|
||||
u16 speed_mask;
|
||||
u8 speed_shift;
|
||||
@@ -400,7 +381,6 @@ struct at803x_priv {
|
||||
bool is_1000basex;
|
||||
struct regulator_dev *vddio_rdev;
|
||||
struct regulator_dev *vddh_rdev;
|
||||
- u64 stats[ARRAY_SIZE(qca83xx_hw_stats)];
|
||||
int led_polarity_mode;
|
||||
};
|
||||
|
||||
@@ -564,53 +544,6 @@ static void at803x_get_wol(struct phy_de
|
||||
wol->wolopts |= WAKE_MAGIC;
|
||||
}
|
||||
|
||||
-static int qca83xx_get_sset_count(struct phy_device *phydev)
|
||||
-{
|
||||
- return ARRAY_SIZE(qca83xx_hw_stats);
|
||||
-}
|
||||
-
|
||||
-static void qca83xx_get_strings(struct phy_device *phydev, u8 *data)
|
||||
-{
|
||||
- int i;
|
||||
-
|
||||
- for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++) {
|
||||
- strscpy(data + i * ETH_GSTRING_LEN,
|
||||
- qca83xx_hw_stats[i].string, ETH_GSTRING_LEN);
|
||||
- }
|
||||
-}
|
||||
-
|
||||
-static u64 qca83xx_get_stat(struct phy_device *phydev, int i)
|
||||
-{
|
||||
- struct at803x_hw_stat stat = qca83xx_hw_stats[i];
|
||||
- struct at803x_priv *priv = phydev->priv;
|
||||
- int val;
|
||||
- u64 ret;
|
||||
-
|
||||
- if (stat.access_type == MMD)
|
||||
- val = phy_read_mmd(phydev, MDIO_MMD_PCS, stat.reg);
|
||||
- else
|
||||
- val = phy_read(phydev, stat.reg);
|
||||
-
|
||||
- if (val < 0) {
|
||||
- ret = U64_MAX;
|
||||
- } else {
|
||||
- val = val & stat.mask;
|
||||
- priv->stats[i] += val;
|
||||
- ret = priv->stats[i];
|
||||
- }
|
||||
-
|
||||
- return ret;
|
||||
-}
|
||||
-
|
||||
-static void qca83xx_get_stats(struct phy_device *phydev,
|
||||
- struct ethtool_stats *stats, u64 *data)
|
||||
-{
|
||||
- int i;
|
||||
-
|
||||
- for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++)
|
||||
- data[i] = qca83xx_get_stat(phydev, i);
|
||||
-}
|
||||
-
|
||||
static int at803x_suspend(struct phy_device *phydev)
|
||||
{
|
||||
int value;
|
||||
@@ -1707,124 +1640,6 @@ static int at8035_probe(struct phy_devic
|
||||
return at8035_parse_dt(phydev);
|
||||
}
|
||||
|
||||
-static int qca83xx_config_init(struct phy_device *phydev)
|
||||
-{
|
||||
- u8 switch_revision;
|
||||
-
|
||||
- switch_revision = phydev->dev_flags & QCA8K_DEVFLAGS_REVISION_MASK;
|
||||
-
|
||||
- switch (switch_revision) {
|
||||
- case 1:
|
||||
- /* For 100M waveform */
|
||||
- at803x_debug_reg_write(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, 0x02ea);
|
||||
- /* Turn on Gigabit clock */
|
||||
- at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_GREEN, 0x68a0);
|
||||
- break;
|
||||
-
|
||||
- case 2:
|
||||
- phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, 0x0);
|
||||
- fallthrough;
|
||||
- case 4:
|
||||
- phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_AZ_DEBUG, 0x803f);
|
||||
- at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_GREEN, 0x6860);
|
||||
- at803x_debug_reg_write(phydev, AT803X_DEBUG_SYSTEM_CTRL_MODE, 0x2c46);
|
||||
- at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_3C, 0x6000);
|
||||
- break;
|
||||
- }
|
||||
-
|
||||
- /* Following original QCA sourcecode set port to prefer master */
|
||||
- phy_set_bits(phydev, MII_CTRL1000, CTL1000_PREFER_MASTER);
|
||||
-
|
||||
- return 0;
|
||||
-}
|
||||
-
|
||||
-static int qca8327_config_init(struct phy_device *phydev)
|
||||
-{
|
||||
- /* QCA8327 require DAC amplitude adjustment for 100m set to +6%.
|
||||
- * Disable on init and enable only with 100m speed following
|
||||
- * qca original source code.
|
||||
- */
|
||||
- at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL,
|
||||
- QCA8327_DEBUG_MANU_CTRL_EN, 0);
|
||||
-
|
||||
- return qca83xx_config_init(phydev);
|
||||
-}
|
||||
-
|
||||
-static void qca83xx_link_change_notify(struct phy_device *phydev)
|
||||
-{
|
||||
- /* Set DAC Amplitude adjustment to +6% for 100m on link running */
|
||||
- if (phydev->state == PHY_RUNNING) {
|
||||
- if (phydev->speed == SPEED_100)
|
||||
- at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL,
|
||||
- QCA8327_DEBUG_MANU_CTRL_EN,
|
||||
- QCA8327_DEBUG_MANU_CTRL_EN);
|
||||
- } else {
|
||||
- /* Reset DAC Amplitude adjustment */
|
||||
- at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL,
|
||||
- QCA8327_DEBUG_MANU_CTRL_EN, 0);
|
||||
- }
|
||||
-}
|
||||
-
|
||||
-static int qca83xx_resume(struct phy_device *phydev)
|
||||
-{
|
||||
- int ret, val;
|
||||
-
|
||||
- /* Skip reset if not suspended */
|
||||
- if (!phydev->suspended)
|
||||
- return 0;
|
||||
-
|
||||
- /* Reinit the port, reset values set by suspend */
|
||||
- qca83xx_config_init(phydev);
|
||||
-
|
||||
- /* Reset the port on port resume */
|
||||
- phy_set_bits(phydev, MII_BMCR, BMCR_RESET | BMCR_ANENABLE);
|
||||
-
|
||||
- /* On resume from suspend the switch execute a reset and
|
||||
- * restart auto-negotiation. Wait for reset to complete.
|
||||
- */
|
||||
- ret = phy_read_poll_timeout(phydev, MII_BMCR, val, !(val & BMCR_RESET),
|
||||
- 50000, 600000, true);
|
||||
- if (ret)
|
||||
- return ret;
|
||||
-
|
||||
- usleep_range(1000, 2000);
|
||||
-
|
||||
- return 0;
|
||||
-}
|
||||
-
|
||||
-static int qca83xx_suspend(struct phy_device *phydev)
|
||||
-{
|
||||
- at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_GREEN,
|
||||
- AT803X_DEBUG_GATE_CLK_IN1000, 0);
|
||||
-
|
||||
- at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_HIB_CTRL,
|
||||
- AT803X_DEBUG_HIB_CTRL_EN_ANY_CHANGE |
|
||||
- AT803X_DEBUG_HIB_CTRL_SEL_RST_80U, 0);
|
||||
-
|
||||
- return 0;
|
||||
-}
|
||||
-
|
||||
-static int qca8337_suspend(struct phy_device *phydev)
|
||||
-{
|
||||
- /* Only QCA8337 support actual suspend. */
|
||||
- genphy_suspend(phydev);
|
||||
-
|
||||
- return qca83xx_suspend(phydev);
|
||||
-}
|
||||
-
|
||||
-static int qca8327_suspend(struct phy_device *phydev)
|
||||
-{
|
||||
- u16 mask = 0;
|
||||
-
|
||||
- /* QCA8327 cause port unreliability when phy suspend
|
||||
- * is set.
|
||||
- */
|
||||
- mask |= ~(BMCR_SPEED1000 | BMCR_FULLDPLX);
|
||||
- phy_modify(phydev, MII_BMCR, mask, 0);
|
||||
-
|
||||
- return qca83xx_suspend(phydev);
|
||||
-}
|
||||
-
|
||||
static int qca808x_phy_fast_retrain_config(struct phy_device *phydev)
|
||||
{
|
||||
int ret;
|
||||
@@ -2599,53 +2414,6 @@ static struct phy_driver at803x_driver[]
|
||||
.soft_reset = genphy_soft_reset,
|
||||
.config_aneg = at803x_config_aneg,
|
||||
}, {
|
||||
- /* QCA8337 */
|
||||
- .phy_id = QCA8337_PHY_ID,
|
||||
- .phy_id_mask = QCA8K_PHY_ID_MASK,
|
||||
- .name = "Qualcomm Atheros 8337 internal PHY",
|
||||
- /* PHY_GBIT_FEATURES */
|
||||
- .probe = at803x_probe,
|
||||
- .flags = PHY_IS_INTERNAL,
|
||||
- .config_init = qca83xx_config_init,
|
||||
- .soft_reset = genphy_soft_reset,
|
||||
- .get_sset_count = qca83xx_get_sset_count,
|
||||
- .get_strings = qca83xx_get_strings,
|
||||
- .get_stats = qca83xx_get_stats,
|
||||
- .suspend = qca8337_suspend,
|
||||
- .resume = qca83xx_resume,
|
||||
-}, {
|
||||
- /* QCA8327-A from switch QCA8327-AL1A */
|
||||
- .phy_id = QCA8327_A_PHY_ID,
|
||||
- .phy_id_mask = QCA8K_PHY_ID_MASK,
|
||||
- .name = "Qualcomm Atheros 8327-A internal PHY",
|
||||
- /* PHY_GBIT_FEATURES */
|
||||
- .link_change_notify = qca83xx_link_change_notify,
|
||||
- .probe = at803x_probe,
|
||||
- .flags = PHY_IS_INTERNAL,
|
||||
- .config_init = qca8327_config_init,
|
||||
- .soft_reset = genphy_soft_reset,
|
||||
- .get_sset_count = qca83xx_get_sset_count,
|
||||
- .get_strings = qca83xx_get_strings,
|
||||
- .get_stats = qca83xx_get_stats,
|
||||
- .suspend = qca8327_suspend,
|
||||
- .resume = qca83xx_resume,
|
||||
-}, {
|
||||
- /* QCA8327-B from switch QCA8327-BL1A */
|
||||
- .phy_id = QCA8327_B_PHY_ID,
|
||||
- .phy_id_mask = QCA8K_PHY_ID_MASK,
|
||||
- .name = "Qualcomm Atheros 8327-B internal PHY",
|
||||
- /* PHY_GBIT_FEATURES */
|
||||
- .link_change_notify = qca83xx_link_change_notify,
|
||||
- .probe = at803x_probe,
|
||||
- .flags = PHY_IS_INTERNAL,
|
||||
- .config_init = qca8327_config_init,
|
||||
- .soft_reset = genphy_soft_reset,
|
||||
- .get_sset_count = qca83xx_get_sset_count,
|
||||
- .get_strings = qca83xx_get_strings,
|
||||
- .get_stats = qca83xx_get_stats,
|
||||
- .suspend = qca8327_suspend,
|
||||
- .resume = qca83xx_resume,
|
||||
-}, {
|
||||
/* Qualcomm QCA8081 */
|
||||
PHY_ID_MATCH_EXACT(QCA8081_PHY_ID),
|
||||
.name = "Qualcomm QCA8081",
|
||||
@@ -2683,9 +2451,6 @@ static struct mdio_device_id __maybe_unu
|
||||
{ PHY_ID_MATCH_EXACT(ATH8032_PHY_ID) },
|
||||
{ PHY_ID_MATCH_EXACT(ATH8035_PHY_ID) },
|
||||
{ PHY_ID_MATCH_EXACT(ATH9331_PHY_ID) },
|
||||
- { PHY_ID_MATCH_EXACT(QCA8337_PHY_ID) },
|
||||
- { PHY_ID_MATCH_EXACT(QCA8327_A_PHY_ID) },
|
||||
- { PHY_ID_MATCH_EXACT(QCA8327_B_PHY_ID) },
|
||||
{ PHY_ID_MATCH_EXACT(QCA9561_PHY_ID) },
|
||||
{ PHY_ID_MATCH_EXACT(QCA8081_PHY_ID) },
|
||||
{ }
|
||||
--- /dev/null
|
||||
+++ b/drivers/net/phy/qcom/qca83xx.c
|
||||
@@ -0,0 +1,275 @@
|
||||
+// SPDX-License-Identifier: GPL-2.0+
|
||||
+
|
||||
+#include <linux/phy.h>
|
||||
+#include <linux/module.h>
|
||||
+
|
||||
+#include "qcom.h"
|
||||
+
|
||||
+#define AT803X_DEBUG_REG_3C 0x3C
|
||||
+
|
||||
+#define AT803X_DEBUG_REG_GREEN 0x3D
|
||||
+#define AT803X_DEBUG_GATE_CLK_IN1000 BIT(6)
|
||||
+
|
||||
+#define MDIO_AZ_DEBUG 0x800D
|
||||
+
|
||||
+#define QCA8327_A_PHY_ID 0x004dd033
|
||||
+#define QCA8327_B_PHY_ID 0x004dd034
|
||||
+#define QCA8337_PHY_ID 0x004dd036
|
||||
+#define QCA8K_PHY_ID_MASK 0xffffffff
|
||||
+
|
||||
+#define QCA8K_DEVFLAGS_REVISION_MASK GENMASK(2, 0)
|
||||
+
|
||||
+static struct at803x_hw_stat qca83xx_hw_stats[] = {
|
||||
+ { "phy_idle_errors", 0xa, GENMASK(7, 0), PHY},
|
||||
+ { "phy_receive_errors", 0x15, GENMASK(15, 0), PHY},
|
||||
+ { "eee_wake_errors", 0x16, GENMASK(15, 0), MMD},
|
||||
+};
|
||||
+
|
||||
+struct qca83xx_priv {
|
||||
+ u64 stats[ARRAY_SIZE(qca83xx_hw_stats)];
|
||||
+};
|
||||
+
|
||||
+MODULE_DESCRIPTION("Qualcomm Atheros QCA83XX PHY driver");
|
||||
+MODULE_AUTHOR("Matus Ujhelyi");
|
||||
+MODULE_AUTHOR("Christian Marangi <ansuelsmth@gmail.com>");
|
||||
+MODULE_LICENSE("GPL");
|
||||
+
|
||||
+static int qca83xx_get_sset_count(struct phy_device *phydev)
|
||||
+{
|
||||
+ return ARRAY_SIZE(qca83xx_hw_stats);
|
||||
+}
|
||||
+
|
||||
+static void qca83xx_get_strings(struct phy_device *phydev, u8 *data)
|
||||
+{
|
||||
+ int i;
|
||||
+
|
||||
+ for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++) {
|
||||
+ strscpy(data + i * ETH_GSTRING_LEN,
|
||||
+ qca83xx_hw_stats[i].string, ETH_GSTRING_LEN);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static u64 qca83xx_get_stat(struct phy_device *phydev, int i)
|
||||
+{
|
||||
+ struct at803x_hw_stat stat = qca83xx_hw_stats[i];
|
||||
+ struct qca83xx_priv *priv = phydev->priv;
|
||||
+ int val;
|
||||
+ u64 ret;
|
||||
+
|
||||
+ if (stat.access_type == MMD)
|
||||
+ val = phy_read_mmd(phydev, MDIO_MMD_PCS, stat.reg);
|
||||
+ else
|
||||
+ val = phy_read(phydev, stat.reg);
|
||||
+
|
||||
+ if (val < 0) {
|
||||
+ ret = U64_MAX;
|
||||
+ } else {
|
||||
+ val = val & stat.mask;
|
||||
+ priv->stats[i] += val;
|
||||
+ ret = priv->stats[i];
|
||||
+ }
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static void qca83xx_get_stats(struct phy_device *phydev,
|
||||
+ struct ethtool_stats *stats, u64 *data)
|
||||
+{
|
||||
+ int i;
|
||||
+
|
||||
+ for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++)
|
||||
+ data[i] = qca83xx_get_stat(phydev, i);
|
||||
+}
|
||||
+
|
||||
+static int qca83xx_probe(struct phy_device *phydev)
|
||||
+{
|
||||
+ struct device *dev = &phydev->mdio.dev;
|
||||
+ struct qca83xx_priv *priv;
|
||||
+
|
||||
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
+ if (!priv)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ phydev->priv = priv;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int qca83xx_config_init(struct phy_device *phydev)
|
||||
+{
|
||||
+ u8 switch_revision;
|
||||
+
|
||||
+ switch_revision = phydev->dev_flags & QCA8K_DEVFLAGS_REVISION_MASK;
|
||||
+
|
||||
+ switch (switch_revision) {
|
||||
+ case 1:
|
||||
+ /* For 100M waveform */
|
||||
+ at803x_debug_reg_write(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, 0x02ea);
|
||||
+ /* Turn on Gigabit clock */
|
||||
+ at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_GREEN, 0x68a0);
|
||||
+ break;
|
||||
+
|
||||
+ case 2:
|
||||
+ phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, 0x0);
|
||||
+ fallthrough;
|
||||
+ case 4:
|
||||
+ phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_AZ_DEBUG, 0x803f);
|
||||
+ at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_GREEN, 0x6860);
|
||||
+ at803x_debug_reg_write(phydev, AT803X_DEBUG_SYSTEM_CTRL_MODE, 0x2c46);
|
||||
+ at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_3C, 0x6000);
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ /* Following original QCA sourcecode set port to prefer master */
|
||||
+ phy_set_bits(phydev, MII_CTRL1000, CTL1000_PREFER_MASTER);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int qca8327_config_init(struct phy_device *phydev)
|
||||
+{
|
||||
+ /* QCA8327 require DAC amplitude adjustment for 100m set to +6%.
|
||||
+ * Disable on init and enable only with 100m speed following
|
||||
+ * qca original source code.
|
||||
+ */
|
||||
+ at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL,
|
||||
+ QCA8327_DEBUG_MANU_CTRL_EN, 0);
|
||||
+
|
||||
+ return qca83xx_config_init(phydev);
|
||||
+}
|
||||
+
|
||||
+static void qca83xx_link_change_notify(struct phy_device *phydev)
|
||||
+{
|
||||
+ /* Set DAC Amplitude adjustment to +6% for 100m on link running */
|
||||
+ if (phydev->state == PHY_RUNNING) {
|
||||
+ if (phydev->speed == SPEED_100)
|
||||
+ at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL,
|
||||
+ QCA8327_DEBUG_MANU_CTRL_EN,
|
||||
+ QCA8327_DEBUG_MANU_CTRL_EN);
|
||||
+ } else {
|
||||
+ /* Reset DAC Amplitude adjustment */
|
||||
+ at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL,
|
||||
+ QCA8327_DEBUG_MANU_CTRL_EN, 0);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static int qca83xx_resume(struct phy_device *phydev)
|
||||
+{
|
||||
+ int ret, val;
|
||||
+
|
||||
+ /* Skip reset if not suspended */
|
||||
+ if (!phydev->suspended)
|
||||
+ return 0;
|
||||
+
|
||||
+ /* Reinit the port, reset values set by suspend */
|
||||
+ qca83xx_config_init(phydev);
|
||||
+
|
||||
+ /* Reset the port on port resume */
|
||||
+ phy_set_bits(phydev, MII_BMCR, BMCR_RESET | BMCR_ANENABLE);
|
||||
+
|
||||
+ /* On resume from suspend the switch execute a reset and
|
||||
+ * restart auto-negotiation. Wait for reset to complete.
|
||||
+ */
|
||||
+ ret = phy_read_poll_timeout(phydev, MII_BMCR, val, !(val & BMCR_RESET),
|
||||
+ 50000, 600000, true);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ usleep_range(1000, 2000);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int qca83xx_suspend(struct phy_device *phydev)
|
||||
+{
|
||||
+ at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_GREEN,
|
||||
+ AT803X_DEBUG_GATE_CLK_IN1000, 0);
|
||||
+
|
||||
+ at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_HIB_CTRL,
|
||||
+ AT803X_DEBUG_HIB_CTRL_EN_ANY_CHANGE |
|
||||
+ AT803X_DEBUG_HIB_CTRL_SEL_RST_80U, 0);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int qca8337_suspend(struct phy_device *phydev)
|
||||
+{
|
||||
+ /* Only QCA8337 support actual suspend. */
|
||||
+ genphy_suspend(phydev);
|
||||
+
|
||||
+ return qca83xx_suspend(phydev);
|
||||
+}
|
||||
+
|
||||
+static int qca8327_suspend(struct phy_device *phydev)
|
||||
+{
|
||||
+ u16 mask = 0;
|
||||
+
|
||||
+ /* QCA8327 cause port unreliability when phy suspend
|
||||
+ * is set.
|
||||
+ */
|
||||
+ mask |= ~(BMCR_SPEED1000 | BMCR_FULLDPLX);
|
||||
+ phy_modify(phydev, MII_BMCR, mask, 0);
|
||||
+
|
||||
+ return qca83xx_suspend(phydev);
|
||||
+}
|
||||
+
|
||||
+static struct phy_driver qca83xx_driver[] = {
|
||||
+{
|
||||
+ /* QCA8337 */
|
||||
+ .phy_id = QCA8337_PHY_ID,
|
||||
+ .phy_id_mask = QCA8K_PHY_ID_MASK,
|
||||
+ .name = "Qualcomm Atheros 8337 internal PHY",
|
||||
+ /* PHY_GBIT_FEATURES */
|
||||
+ .probe = qca83xx_probe,
|
||||
+ .flags = PHY_IS_INTERNAL,
|
||||
+ .config_init = qca83xx_config_init,
|
||||
+ .soft_reset = genphy_soft_reset,
|
||||
+ .get_sset_count = qca83xx_get_sset_count,
|
||||
+ .get_strings = qca83xx_get_strings,
|
||||
+ .get_stats = qca83xx_get_stats,
|
||||
+ .suspend = qca8337_suspend,
|
||||
+ .resume = qca83xx_resume,
|
||||
+}, {
|
||||
+ /* QCA8327-A from switch QCA8327-AL1A */
|
||||
+ .phy_id = QCA8327_A_PHY_ID,
|
||||
+ .phy_id_mask = QCA8K_PHY_ID_MASK,
|
||||
+ .name = "Qualcomm Atheros 8327-A internal PHY",
|
||||
+ /* PHY_GBIT_FEATURES */
|
||||
+ .link_change_notify = qca83xx_link_change_notify,
|
||||
+ .probe = qca83xx_probe,
|
||||
+ .flags = PHY_IS_INTERNAL,
|
||||
+ .config_init = qca8327_config_init,
|
||||
+ .soft_reset = genphy_soft_reset,
|
||||
+ .get_sset_count = qca83xx_get_sset_count,
|
||||
+ .get_strings = qca83xx_get_strings,
|
||||
+ .get_stats = qca83xx_get_stats,
|
||||
+ .suspend = qca8327_suspend,
|
||||
+ .resume = qca83xx_resume,
|
||||
+}, {
|
||||
+ /* QCA8327-B from switch QCA8327-BL1A */
|
||||
+ .phy_id = QCA8327_B_PHY_ID,
|
||||
+ .phy_id_mask = QCA8K_PHY_ID_MASK,
|
||||
+ .name = "Qualcomm Atheros 8327-B internal PHY",
|
||||
+ /* PHY_GBIT_FEATURES */
|
||||
+ .link_change_notify = qca83xx_link_change_notify,
|
||||
+ .probe = qca83xx_probe,
|
||||
+ .flags = PHY_IS_INTERNAL,
|
||||
+ .config_init = qca8327_config_init,
|
||||
+ .soft_reset = genphy_soft_reset,
|
||||
+ .get_sset_count = qca83xx_get_sset_count,
|
||||
+ .get_strings = qca83xx_get_strings,
|
||||
+ .get_stats = qca83xx_get_stats,
|
||||
+ .suspend = qca8327_suspend,
|
||||
+ .resume = qca83xx_resume,
|
||||
+}, };
|
||||
+
|
||||
+module_phy_driver(qca83xx_driver);
|
||||
+
|
||||
+static struct mdio_device_id __maybe_unused qca83xx_tbl[] = {
|
||||
+ { PHY_ID_MATCH_EXACT(QCA8337_PHY_ID) },
|
||||
+ { PHY_ID_MATCH_EXACT(QCA8327_A_PHY_ID) },
|
||||
+ { PHY_ID_MATCH_EXACT(QCA8327_B_PHY_ID) },
|
||||
+ { }
|
||||
+};
|
||||
+
|
||||
+MODULE_DEVICE_TABLE(mdio, qca83xx_tbl);
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,28 @@
|
||||
From ebb30ccbbdbd6fae5177b676da4f4ac92bb4f635 Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Fri, 15 Dec 2023 14:15:31 +0100
|
||||
Subject: [PATCH 1/4] net: phy: make addr type u8 in phy_package_shared struct
|
||||
|
||||
Switch addr type in phy_package_shared struct to u8.
|
||||
|
||||
The value is already checked to be non negative and to be less than
|
||||
PHY_MAX_ADDR, hence u8 is better suited than using int.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
include/linux/phy.h | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
--- a/include/linux/phy.h
|
||||
+++ b/include/linux/phy.h
|
||||
@@ -330,7 +330,7 @@ struct mdio_bus_stats {
|
||||
* phy_package_leave().
|
||||
*/
|
||||
struct phy_package_shared {
|
||||
- int addr;
|
||||
+ u8 addr;
|
||||
refcount_t refcnt;
|
||||
unsigned long flags;
|
||||
size_t priv_size;
|
@ -0,0 +1,341 @@
|
||||
From 9eea577eb1155fe4a183bc5e7bf269b0b2e7a6ba Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Fri, 15 Dec 2023 14:15:32 +0100
|
||||
Subject: [PATCH 2/4] net: phy: extend PHY package API to support multiple
|
||||
global address
|
||||
|
||||
Current API for PHY package are limited to single address to configure
|
||||
global settings for the PHY package.
|
||||
|
||||
It was found that some PHY package (for example the qca807x, a PHY
|
||||
package that is shipped with a bundle of 5 PHY) requires multiple PHY
|
||||
address to configure global settings. An example scenario is a PHY that
|
||||
have a dedicated PHY for PSGMII/serdes calibrarion and have a specific
|
||||
PHY in the package where the global PHY mode is set and affects every
|
||||
other PHY in the package.
|
||||
|
||||
Change the API in the following way:
|
||||
- Change phy_package_join() to take the base addr of the PHY package
|
||||
instead of the global PHY addr.
|
||||
- Make __/phy_package_write/read() require an additional arg that
|
||||
select what global PHY address to use by passing the offset from the
|
||||
base addr passed on phy_package_join().
|
||||
|
||||
Each user of this API is updated to follow this new implementation
|
||||
following a pattern where an enum is defined to declare the offset of the
|
||||
addr.
|
||||
|
||||
We also drop the check if shared is defined as any user of the
|
||||
phy_package_read/write is expected to use phy_package_join first. Misuse
|
||||
of this will correctly trigger a kernel panic for NULL pointer
|
||||
exception.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/bcm54140.c | 16 ++++++--
|
||||
drivers/net/phy/mscc/mscc.h | 5 +++
|
||||
drivers/net/phy/mscc/mscc_main.c | 4 +-
|
||||
drivers/net/phy/phy_device.c | 35 +++++++++--------
|
||||
include/linux/phy.h | 64 +++++++++++++++++++++-----------
|
||||
5 files changed, 80 insertions(+), 44 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/bcm54140.c
|
||||
+++ b/drivers/net/phy/bcm54140.c
|
||||
@@ -128,6 +128,10 @@
|
||||
#define BCM54140_DEFAULT_DOWNSHIFT 5
|
||||
#define BCM54140_MAX_DOWNSHIFT 9
|
||||
|
||||
+enum bcm54140_global_phy {
|
||||
+ BCM54140_BASE_ADDR = 0,
|
||||
+};
|
||||
+
|
||||
struct bcm54140_priv {
|
||||
int port;
|
||||
int base_addr;
|
||||
@@ -429,11 +433,13 @@ static int bcm54140_base_read_rdb(struct
|
||||
int ret;
|
||||
|
||||
phy_lock_mdio_bus(phydev);
|
||||
- ret = __phy_package_write(phydev, MII_BCM54XX_RDB_ADDR, rdb);
|
||||
+ ret = __phy_package_write(phydev, BCM54140_BASE_ADDR,
|
||||
+ MII_BCM54XX_RDB_ADDR, rdb);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
- ret = __phy_package_read(phydev, MII_BCM54XX_RDB_DATA);
|
||||
+ ret = __phy_package_read(phydev, BCM54140_BASE_ADDR,
|
||||
+ MII_BCM54XX_RDB_DATA);
|
||||
|
||||
out:
|
||||
phy_unlock_mdio_bus(phydev);
|
||||
@@ -446,11 +452,13 @@ static int bcm54140_base_write_rdb(struc
|
||||
int ret;
|
||||
|
||||
phy_lock_mdio_bus(phydev);
|
||||
- ret = __phy_package_write(phydev, MII_BCM54XX_RDB_ADDR, rdb);
|
||||
+ ret = __phy_package_write(phydev, BCM54140_BASE_ADDR,
|
||||
+ MII_BCM54XX_RDB_ADDR, rdb);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
- ret = __phy_package_write(phydev, MII_BCM54XX_RDB_DATA, val);
|
||||
+ ret = __phy_package_write(phydev, BCM54140_BASE_ADDR,
|
||||
+ MII_BCM54XX_RDB_DATA, val);
|
||||
|
||||
out:
|
||||
phy_unlock_mdio_bus(phydev);
|
||||
--- a/drivers/net/phy/mscc/mscc.h
|
||||
+++ b/drivers/net/phy/mscc/mscc.h
|
||||
@@ -414,6 +414,11 @@ struct vsc8531_private {
|
||||
* gpio_lock: used for PHC operations. Common for all PHYs as the load/save GPIO
|
||||
* is shared.
|
||||
*/
|
||||
+
|
||||
+enum vsc85xx_global_phy {
|
||||
+ VSC88XX_BASE_ADDR = 0,
|
||||
+};
|
||||
+
|
||||
struct vsc85xx_shared_private {
|
||||
struct mutex gpio_lock;
|
||||
};
|
||||
--- a/drivers/net/phy/mscc/mscc_main.c
|
||||
+++ b/drivers/net/phy/mscc/mscc_main.c
|
||||
@@ -700,7 +700,7 @@ int phy_base_write(struct phy_device *ph
|
||||
dump_stack();
|
||||
}
|
||||
|
||||
- return __phy_package_write(phydev, regnum, val);
|
||||
+ return __phy_package_write(phydev, VSC88XX_BASE_ADDR, regnum, val);
|
||||
}
|
||||
|
||||
/* phydev->bus->mdio_lock should be locked when using this function */
|
||||
@@ -711,7 +711,7 @@ int phy_base_read(struct phy_device *phy
|
||||
dump_stack();
|
||||
}
|
||||
|
||||
- return __phy_package_read(phydev, regnum);
|
||||
+ return __phy_package_read(phydev, VSC88XX_BASE_ADDR, regnum);
|
||||
}
|
||||
|
||||
u32 vsc85xx_csr_read(struct phy_device *phydev,
|
||||
--- a/drivers/net/phy/phy_device.c
|
||||
+++ b/drivers/net/phy/phy_device.c
|
||||
@@ -1602,20 +1602,22 @@ EXPORT_SYMBOL_GPL(phy_driver_is_genphy_1
|
||||
/**
|
||||
* phy_package_join - join a common PHY group
|
||||
* @phydev: target phy_device struct
|
||||
- * @addr: cookie and PHY address for global register access
|
||||
+ * @base_addr: cookie and base PHY address of PHY package for offset
|
||||
+ * calculation of global register access
|
||||
* @priv_size: if non-zero allocate this amount of bytes for private data
|
||||
*
|
||||
* This joins a PHY group and provides a shared storage for all phydevs in
|
||||
* this group. This is intended to be used for packages which contain
|
||||
* more than one PHY, for example a quad PHY transceiver.
|
||||
*
|
||||
- * The addr parameter serves as a cookie which has to have the same value
|
||||
- * for all members of one group and as a PHY address to access generic
|
||||
- * registers of a PHY package. Usually, one of the PHY addresses of the
|
||||
- * different PHYs in the package provides access to these global registers.
|
||||
+ * The base_addr parameter serves as cookie which has to have the same values
|
||||
+ * for all members of one group and as the base PHY address of the PHY package
|
||||
+ * for offset calculation to access generic registers of a PHY package.
|
||||
+ * Usually, one of the PHY addresses of the different PHYs in the package
|
||||
+ * provides access to these global registers.
|
||||
* The address which is given here, will be used in the phy_package_read()
|
||||
- * and phy_package_write() convenience functions. If your PHY doesn't have
|
||||
- * global registers you can just pick any of the PHY addresses.
|
||||
+ * and phy_package_write() convenience functions as base and added to the
|
||||
+ * passed offset in those functions.
|
||||
*
|
||||
* This will set the shared pointer of the phydev to the shared storage.
|
||||
* If this is the first call for a this cookie the shared storage will be
|
||||
@@ -1625,17 +1627,17 @@ EXPORT_SYMBOL_GPL(phy_driver_is_genphy_1
|
||||
* Returns < 1 on error, 0 on success. Esp. calling phy_package_join()
|
||||
* with the same cookie but a different priv_size is an error.
|
||||
*/
|
||||
-int phy_package_join(struct phy_device *phydev, int addr, size_t priv_size)
|
||||
+int phy_package_join(struct phy_device *phydev, int base_addr, size_t priv_size)
|
||||
{
|
||||
struct mii_bus *bus = phydev->mdio.bus;
|
||||
struct phy_package_shared *shared;
|
||||
int ret;
|
||||
|
||||
- if (addr < 0 || addr >= PHY_MAX_ADDR)
|
||||
+ if (base_addr < 0 || base_addr >= PHY_MAX_ADDR)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&bus->shared_lock);
|
||||
- shared = bus->shared[addr];
|
||||
+ shared = bus->shared[base_addr];
|
||||
if (!shared) {
|
||||
ret = -ENOMEM;
|
||||
shared = kzalloc(sizeof(*shared), GFP_KERNEL);
|
||||
@@ -1647,9 +1649,9 @@ int phy_package_join(struct phy_device *
|
||||
goto err_free;
|
||||
shared->priv_size = priv_size;
|
||||
}
|
||||
- shared->addr = addr;
|
||||
+ shared->base_addr = base_addr;
|
||||
refcount_set(&shared->refcnt, 1);
|
||||
- bus->shared[addr] = shared;
|
||||
+ bus->shared[base_addr] = shared;
|
||||
} else {
|
||||
ret = -EINVAL;
|
||||
if (priv_size && priv_size != shared->priv_size)
|
||||
@@ -1687,7 +1689,7 @@ void phy_package_leave(struct phy_device
|
||||
return;
|
||||
|
||||
if (refcount_dec_and_mutex_lock(&shared->refcnt, &bus->shared_lock)) {
|
||||
- bus->shared[shared->addr] = NULL;
|
||||
+ bus->shared[shared->base_addr] = NULL;
|
||||
mutex_unlock(&bus->shared_lock);
|
||||
kfree(shared->priv);
|
||||
kfree(shared);
|
||||
@@ -1706,7 +1708,8 @@ static void devm_phy_package_leave(struc
|
||||
* devm_phy_package_join - resource managed phy_package_join()
|
||||
* @dev: device that is registering this PHY package
|
||||
* @phydev: target phy_device struct
|
||||
- * @addr: cookie and PHY address for global register access
|
||||
+ * @base_addr: cookie and base PHY address of PHY package for offset
|
||||
+ * calculation of global register access
|
||||
* @priv_size: if non-zero allocate this amount of bytes for private data
|
||||
*
|
||||
* Managed phy_package_join(). Shared storage fetched by this function,
|
||||
@@ -1714,7 +1717,7 @@ static void devm_phy_package_leave(struc
|
||||
* phy_package_join() for more information.
|
||||
*/
|
||||
int devm_phy_package_join(struct device *dev, struct phy_device *phydev,
|
||||
- int addr, size_t priv_size)
|
||||
+ int base_addr, size_t priv_size)
|
||||
{
|
||||
struct phy_device **ptr;
|
||||
int ret;
|
||||
@@ -1724,7 +1727,7 @@ int devm_phy_package_join(struct device
|
||||
if (!ptr)
|
||||
return -ENOMEM;
|
||||
|
||||
- ret = phy_package_join(phydev, addr, priv_size);
|
||||
+ ret = phy_package_join(phydev, base_addr, priv_size);
|
||||
|
||||
if (!ret) {
|
||||
*ptr = phydev;
|
||||
--- a/include/linux/phy.h
|
||||
+++ b/include/linux/phy.h
|
||||
@@ -319,7 +319,8 @@ struct mdio_bus_stats {
|
||||
|
||||
/**
|
||||
* struct phy_package_shared - Shared information in PHY packages
|
||||
- * @addr: Common PHY address used to combine PHYs in one package
|
||||
+ * @base_addr: Base PHY address of PHY package used to combine PHYs
|
||||
+ * in one package and for offset calculation of phy_package_read/write
|
||||
* @refcnt: Number of PHYs connected to this shared data
|
||||
* @flags: Initialization of PHY package
|
||||
* @priv_size: Size of the shared private data @priv
|
||||
@@ -330,7 +331,7 @@ struct mdio_bus_stats {
|
||||
* phy_package_leave().
|
||||
*/
|
||||
struct phy_package_shared {
|
||||
- u8 addr;
|
||||
+ u8 base_addr;
|
||||
refcount_t refcnt;
|
||||
unsigned long flags;
|
||||
size_t priv_size;
|
||||
@@ -1763,10 +1764,10 @@ int phy_ethtool_get_link_ksettings(struc
|
||||
int phy_ethtool_set_link_ksettings(struct net_device *ndev,
|
||||
const struct ethtool_link_ksettings *cmd);
|
||||
int phy_ethtool_nway_reset(struct net_device *ndev);
|
||||
-int phy_package_join(struct phy_device *phydev, int addr, size_t priv_size);
|
||||
+int phy_package_join(struct phy_device *phydev, int base_addr, size_t priv_size);
|
||||
void phy_package_leave(struct phy_device *phydev);
|
||||
int devm_phy_package_join(struct device *dev, struct phy_device *phydev,
|
||||
- int addr, size_t priv_size);
|
||||
+ int base_addr, size_t priv_size);
|
||||
|
||||
#if IS_ENABLED(CONFIG_PHYLIB)
|
||||
int __init mdio_bus_init(void);
|
||||
@@ -1778,46 +1779,65 @@ int phy_ethtool_get_sset_count(struct ph
|
||||
int phy_ethtool_get_stats(struct phy_device *phydev,
|
||||
struct ethtool_stats *stats, u64 *data);
|
||||
|
||||
-static inline int phy_package_read(struct phy_device *phydev, u32 regnum)
|
||||
+static inline int phy_package_address(struct phy_device *phydev,
|
||||
+ unsigned int addr_offset)
|
||||
{
|
||||
struct phy_package_shared *shared = phydev->shared;
|
||||
+ u8 base_addr = shared->base_addr;
|
||||
|
||||
- if (!shared)
|
||||
+ if (addr_offset >= PHY_MAX_ADDR - base_addr)
|
||||
return -EIO;
|
||||
|
||||
- return mdiobus_read(phydev->mdio.bus, shared->addr, regnum);
|
||||
+ /* we know that addr will be in the range 0..31 and thus the
|
||||
+ * implicit cast to a signed int is not a problem.
|
||||
+ */
|
||||
+ return base_addr + addr_offset;
|
||||
}
|
||||
|
||||
-static inline int __phy_package_read(struct phy_device *phydev, u32 regnum)
|
||||
+static inline int phy_package_read(struct phy_device *phydev,
|
||||
+ unsigned int addr_offset, u32 regnum)
|
||||
{
|
||||
- struct phy_package_shared *shared = phydev->shared;
|
||||
+ int addr = phy_package_address(phydev, addr_offset);
|
||||
|
||||
- if (!shared)
|
||||
- return -EIO;
|
||||
+ if (addr < 0)
|
||||
+ return addr;
|
||||
+
|
||||
+ return mdiobus_read(phydev->mdio.bus, addr, regnum);
|
||||
+}
|
||||
+
|
||||
+static inline int __phy_package_read(struct phy_device *phydev,
|
||||
+ unsigned int addr_offset, u32 regnum)
|
||||
+{
|
||||
+ int addr = phy_package_address(phydev, addr_offset);
|
||||
+
|
||||
+ if (addr < 0)
|
||||
+ return addr;
|
||||
|
||||
- return __mdiobus_read(phydev->mdio.bus, shared->addr, regnum);
|
||||
+ return __mdiobus_read(phydev->mdio.bus, addr, regnum);
|
||||
}
|
||||
|
||||
static inline int phy_package_write(struct phy_device *phydev,
|
||||
- u32 regnum, u16 val)
|
||||
+ unsigned int addr_offset, u32 regnum,
|
||||
+ u16 val)
|
||||
{
|
||||
- struct phy_package_shared *shared = phydev->shared;
|
||||
+ int addr = phy_package_address(phydev, addr_offset);
|
||||
|
||||
- if (!shared)
|
||||
- return -EIO;
|
||||
+ if (addr < 0)
|
||||
+ return addr;
|
||||
|
||||
- return mdiobus_write(phydev->mdio.bus, shared->addr, regnum, val);
|
||||
+ return mdiobus_write(phydev->mdio.bus, addr, regnum, val);
|
||||
}
|
||||
|
||||
static inline int __phy_package_write(struct phy_device *phydev,
|
||||
- u32 regnum, u16 val)
|
||||
+ unsigned int addr_offset, u32 regnum,
|
||||
+ u16 val)
|
||||
{
|
||||
- struct phy_package_shared *shared = phydev->shared;
|
||||
+ int addr = phy_package_address(phydev, addr_offset);
|
||||
|
||||
- if (!shared)
|
||||
- return -EIO;
|
||||
+ if (addr < 0)
|
||||
+ return addr;
|
||||
|
||||
- return __mdiobus_write(phydev->mdio.bus, shared->addr, regnum, val);
|
||||
+ return __mdiobus_write(phydev->mdio.bus, addr, regnum, val);
|
||||
}
|
||||
|
||||
static inline bool __phy_package_set_once(struct phy_device *phydev,
|
@ -0,0 +1,116 @@
|
||||
From 028672bd1d73cf65249a420c1de75e8d2acd2f6a Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Fri, 15 Dec 2023 14:15:33 +0100
|
||||
Subject: [PATCH 3/4] net: phy: restructure __phy_write/read_mmd to helper and
|
||||
phydev user
|
||||
|
||||
Restructure phy_write_mmd and phy_read_mmd to implement generic helper
|
||||
for direct mdiobus access for mmd and use these helper for phydev user.
|
||||
|
||||
This is needed in preparation of PHY package API that requires generic
|
||||
access to the mdiobus and are deatched from phydev struct but instead
|
||||
access them based on PHY package base_addr and offsets.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/phy-core.c | 64 ++++++++++++++++++--------------------
|
||||
1 file changed, 30 insertions(+), 34 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/phy-core.c
|
||||
+++ b/drivers/net/phy/phy-core.c
|
||||
@@ -528,6 +528,28 @@ static void mmd_phy_indirect(struct mii_
|
||||
devad | MII_MMD_CTRL_NOINCR);
|
||||
}
|
||||
|
||||
+static int mmd_phy_read(struct mii_bus *bus, int phy_addr, bool is_c45,
|
||||
+ int devad, u32 regnum)
|
||||
+{
|
||||
+ if (is_c45)
|
||||
+ return __mdiobus_c45_read(bus, phy_addr, devad, regnum);
|
||||
+
|
||||
+ mmd_phy_indirect(bus, phy_addr, devad, regnum);
|
||||
+ /* Read the content of the MMD's selected register */
|
||||
+ return __mdiobus_read(bus, phy_addr, MII_MMD_DATA);
|
||||
+}
|
||||
+
|
||||
+static int mmd_phy_write(struct mii_bus *bus, int phy_addr, bool is_c45,
|
||||
+ int devad, u32 regnum, u16 val)
|
||||
+{
|
||||
+ if (is_c45)
|
||||
+ return __mdiobus_c45_write(bus, phy_addr, devad, regnum, val);
|
||||
+
|
||||
+ mmd_phy_indirect(bus, phy_addr, devad, regnum);
|
||||
+ /* Write the data into MMD's selected register */
|
||||
+ return __mdiobus_write(bus, phy_addr, MII_MMD_DATA, val);
|
||||
+}
|
||||
+
|
||||
/**
|
||||
* __phy_read_mmd - Convenience function for reading a register
|
||||
* from an MMD on a given PHY.
|
||||
@@ -539,26 +561,14 @@ static void mmd_phy_indirect(struct mii_
|
||||
*/
|
||||
int __phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum)
|
||||
{
|
||||
- int val;
|
||||
-
|
||||
if (regnum > (u16)~0 || devad > 32)
|
||||
return -EINVAL;
|
||||
|
||||
- if (phydev->drv && phydev->drv->read_mmd) {
|
||||
- val = phydev->drv->read_mmd(phydev, devad, regnum);
|
||||
- } else if (phydev->is_c45) {
|
||||
- val = __mdiobus_c45_read(phydev->mdio.bus, phydev->mdio.addr,
|
||||
- devad, regnum);
|
||||
- } else {
|
||||
- struct mii_bus *bus = phydev->mdio.bus;
|
||||
- int phy_addr = phydev->mdio.addr;
|
||||
-
|
||||
- mmd_phy_indirect(bus, phy_addr, devad, regnum);
|
||||
-
|
||||
- /* Read the content of the MMD's selected register */
|
||||
- val = __mdiobus_read(bus, phy_addr, MII_MMD_DATA);
|
||||
- }
|
||||
- return val;
|
||||
+ if (phydev->drv && phydev->drv->read_mmd)
|
||||
+ return phydev->drv->read_mmd(phydev, devad, regnum);
|
||||
+
|
||||
+ return mmd_phy_read(phydev->mdio.bus, phydev->mdio.addr,
|
||||
+ phydev->is_c45, devad, regnum);
|
||||
}
|
||||
EXPORT_SYMBOL(__phy_read_mmd);
|
||||
|
||||
@@ -595,28 +605,14 @@ EXPORT_SYMBOL(phy_read_mmd);
|
||||
*/
|
||||
int __phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val)
|
||||
{
|
||||
- int ret;
|
||||
-
|
||||
if (regnum > (u16)~0 || devad > 32)
|
||||
return -EINVAL;
|
||||
|
||||
- if (phydev->drv && phydev->drv->write_mmd) {
|
||||
- ret = phydev->drv->write_mmd(phydev, devad, regnum, val);
|
||||
- } else if (phydev->is_c45) {
|
||||
- ret = __mdiobus_c45_write(phydev->mdio.bus, phydev->mdio.addr,
|
||||
- devad, regnum, val);
|
||||
- } else {
|
||||
- struct mii_bus *bus = phydev->mdio.bus;
|
||||
- int phy_addr = phydev->mdio.addr;
|
||||
+ if (phydev->drv && phydev->drv->write_mmd)
|
||||
+ return phydev->drv->write_mmd(phydev, devad, regnum, val);
|
||||
|
||||
- mmd_phy_indirect(bus, phy_addr, devad, regnum);
|
||||
-
|
||||
- /* Write the data into MMD's selected register */
|
||||
- __mdiobus_write(bus, phy_addr, MII_MMD_DATA, val);
|
||||
-
|
||||
- ret = 0;
|
||||
- }
|
||||
- return ret;
|
||||
+ return mmd_phy_write(phydev->mdio.bus, phydev->mdio.addr,
|
||||
+ phydev->is_c45, devad, regnum, val);
|
||||
}
|
||||
EXPORT_SYMBOL(__phy_write_mmd);
|
||||
|
@ -0,0 +1,196 @@
|
||||
From d63710fc0f1a501fd75a7025e3070a96ffa1645f Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Fri, 15 Dec 2023 14:15:34 +0100
|
||||
Subject: [PATCH 4/4] net: phy: add support for PHY package MMD read/write
|
||||
|
||||
Some PHY in PHY package may require to read/write MMD regs to correctly
|
||||
configure the PHY package.
|
||||
|
||||
Add support for these additional required function in both lock and no
|
||||
lock variant.
|
||||
|
||||
It's assumed that the entire PHY package is either C22 or C45. We use
|
||||
C22 or C45 way of writing/reading to mmd regs based on the passed phydev
|
||||
whether it's C22 or C45.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/phy-core.c | 140 +++++++++++++++++++++++++++++++++++++
|
||||
include/linux/phy.h | 16 +++++
|
||||
2 files changed, 156 insertions(+)
|
||||
|
||||
--- a/drivers/net/phy/phy-core.c
|
||||
+++ b/drivers/net/phy/phy-core.c
|
||||
@@ -639,6 +639,146 @@ int phy_write_mmd(struct phy_device *phy
|
||||
EXPORT_SYMBOL(phy_write_mmd);
|
||||
|
||||
/**
|
||||
+ * __phy_package_read_mmd - read MMD reg relative to PHY package base addr
|
||||
+ * @phydev: The phy_device struct
|
||||
+ * @addr_offset: The offset to be added to PHY package base_addr
|
||||
+ * @devad: The MMD to read from
|
||||
+ * @regnum: The register on the MMD to read
|
||||
+ *
|
||||
+ * Convenience helper for reading a register of an MMD on a given PHY
|
||||
+ * using the PHY package base address. The base address is added to
|
||||
+ * the addr_offset value.
|
||||
+ *
|
||||
+ * Same calling rules as for __phy_read();
|
||||
+ *
|
||||
+ * NOTE: It's assumed that the entire PHY package is either C22 or C45.
|
||||
+ */
|
||||
+int __phy_package_read_mmd(struct phy_device *phydev,
|
||||
+ unsigned int addr_offset, int devad,
|
||||
+ u32 regnum)
|
||||
+{
|
||||
+ int addr = phy_package_address(phydev, addr_offset);
|
||||
+
|
||||
+ if (addr < 0)
|
||||
+ return addr;
|
||||
+
|
||||
+ if (regnum > (u16)~0 || devad > 32)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ return mmd_phy_read(phydev->mdio.bus, addr, phydev->is_c45, devad,
|
||||
+ regnum);
|
||||
+}
|
||||
+EXPORT_SYMBOL(__phy_package_read_mmd);
|
||||
+
|
||||
+/**
|
||||
+ * phy_package_read_mmd - read MMD reg relative to PHY package base addr
|
||||
+ * @phydev: The phy_device struct
|
||||
+ * @addr_offset: The offset to be added to PHY package base_addr
|
||||
+ * @devad: The MMD to read from
|
||||
+ * @regnum: The register on the MMD to read
|
||||
+ *
|
||||
+ * Convenience helper for reading a register of an MMD on a given PHY
|
||||
+ * using the PHY package base address. The base address is added to
|
||||
+ * the addr_offset value.
|
||||
+ *
|
||||
+ * Same calling rules as for phy_read();
|
||||
+ *
|
||||
+ * NOTE: It's assumed that the entire PHY package is either C22 or C45.
|
||||
+ */
|
||||
+int phy_package_read_mmd(struct phy_device *phydev,
|
||||
+ unsigned int addr_offset, int devad,
|
||||
+ u32 regnum)
|
||||
+{
|
||||
+ int addr = phy_package_address(phydev, addr_offset);
|
||||
+ int val;
|
||||
+
|
||||
+ if (addr < 0)
|
||||
+ return addr;
|
||||
+
|
||||
+ if (regnum > (u16)~0 || devad > 32)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ phy_lock_mdio_bus(phydev);
|
||||
+ val = mmd_phy_read(phydev->mdio.bus, addr, phydev->is_c45, devad,
|
||||
+ regnum);
|
||||
+ phy_unlock_mdio_bus(phydev);
|
||||
+
|
||||
+ return val;
|
||||
+}
|
||||
+EXPORT_SYMBOL(phy_package_read_mmd);
|
||||
+
|
||||
+/**
|
||||
+ * __phy_package_write_mmd - write MMD reg relative to PHY package base addr
|
||||
+ * @phydev: The phy_device struct
|
||||
+ * @addr_offset: The offset to be added to PHY package base_addr
|
||||
+ * @devad: The MMD to write to
|
||||
+ * @regnum: The register on the MMD to write
|
||||
+ * @val: value to write to @regnum
|
||||
+ *
|
||||
+ * Convenience helper for writing a register of an MMD on a given PHY
|
||||
+ * using the PHY package base address. The base address is added to
|
||||
+ * the addr_offset value.
|
||||
+ *
|
||||
+ * Same calling rules as for __phy_write();
|
||||
+ *
|
||||
+ * NOTE: It's assumed that the entire PHY package is either C22 or C45.
|
||||
+ */
|
||||
+int __phy_package_write_mmd(struct phy_device *phydev,
|
||||
+ unsigned int addr_offset, int devad,
|
||||
+ u32 regnum, u16 val)
|
||||
+{
|
||||
+ int addr = phy_package_address(phydev, addr_offset);
|
||||
+
|
||||
+ if (addr < 0)
|
||||
+ return addr;
|
||||
+
|
||||
+ if (regnum > (u16)~0 || devad > 32)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ return mmd_phy_write(phydev->mdio.bus, addr, phydev->is_c45, devad,
|
||||
+ regnum, val);
|
||||
+}
|
||||
+EXPORT_SYMBOL(__phy_package_write_mmd);
|
||||
+
|
||||
+/**
|
||||
+ * phy_package_write_mmd - write MMD reg relative to PHY package base addr
|
||||
+ * @phydev: The phy_device struct
|
||||
+ * @addr_offset: The offset to be added to PHY package base_addr
|
||||
+ * @devad: The MMD to write to
|
||||
+ * @regnum: The register on the MMD to write
|
||||
+ * @val: value to write to @regnum
|
||||
+ *
|
||||
+ * Convenience helper for writing a register of an MMD on a given PHY
|
||||
+ * using the PHY package base address. The base address is added to
|
||||
+ * the addr_offset value.
|
||||
+ *
|
||||
+ * Same calling rules as for phy_write();
|
||||
+ *
|
||||
+ * NOTE: It's assumed that the entire PHY package is either C22 or C45.
|
||||
+ */
|
||||
+int phy_package_write_mmd(struct phy_device *phydev,
|
||||
+ unsigned int addr_offset, int devad,
|
||||
+ u32 regnum, u16 val)
|
||||
+{
|
||||
+ int addr = phy_package_address(phydev, addr_offset);
|
||||
+ int ret;
|
||||
+
|
||||
+ if (addr < 0)
|
||||
+ return addr;
|
||||
+
|
||||
+ if (regnum > (u16)~0 || devad > 32)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ phy_lock_mdio_bus(phydev);
|
||||
+ ret = mmd_phy_write(phydev->mdio.bus, addr, phydev->is_c45, devad,
|
||||
+ regnum, val);
|
||||
+ phy_unlock_mdio_bus(phydev);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+EXPORT_SYMBOL(phy_package_write_mmd);
|
||||
+
|
||||
+/**
|
||||
* phy_modify_changed - Function for modifying a PHY register
|
||||
* @phydev: the phy_device struct
|
||||
* @regnum: register number to modify
|
||||
--- a/include/linux/phy.h
|
||||
+++ b/include/linux/phy.h
|
||||
@@ -1840,6 +1840,22 @@ static inline int __phy_package_write(st
|
||||
return __mdiobus_write(phydev->mdio.bus, addr, regnum, val);
|
||||
}
|
||||
|
||||
+int __phy_package_read_mmd(struct phy_device *phydev,
|
||||
+ unsigned int addr_offset, int devad,
|
||||
+ u32 regnum);
|
||||
+
|
||||
+int phy_package_read_mmd(struct phy_device *phydev,
|
||||
+ unsigned int addr_offset, int devad,
|
||||
+ u32 regnum);
|
||||
+
|
||||
+int __phy_package_write_mmd(struct phy_device *phydev,
|
||||
+ unsigned int addr_offset, int devad,
|
||||
+ u32 regnum, u16 val);
|
||||
+
|
||||
+int phy_package_write_mmd(struct phy_device *phydev,
|
||||
+ unsigned int addr_offset, int devad,
|
||||
+ u32 regnum, u16 val);
|
||||
+
|
||||
static inline bool __phy_package_set_once(struct phy_device *phydev,
|
||||
unsigned int b)
|
||||
{
|
@ -0,0 +1,103 @@
|
||||
From affa013f494486079c3c5ad2d00cebc41a3d7445 Mon Sep 17 00:00:00 2001
|
||||
From: Sean Anderson <sean.anderson@seco.com>
|
||||
Date: Mon, 17 Oct 2022 16:22:36 -0400
|
||||
Subject: [PATCH 01/21] net: fman: memac: Add serdes support
|
||||
|
||||
This adds support for using a serdes which has to be configured. This is
|
||||
primarly in preparation for phylink conversion, which will then change the
|
||||
serdes mode dynamically.
|
||||
|
||||
Signed-off-by: Sean Anderson <sean.anderson@seco.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
.../net/ethernet/freescale/fman/fman_memac.c | 49 ++++++++++++++++++-
|
||||
1 file changed, 47 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/drivers/net/ethernet/freescale/fman/fman_memac.c
|
||||
+++ b/drivers/net/ethernet/freescale/fman/fman_memac.c
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <linux/io.h>
|
||||
#include <linux/phy.h>
|
||||
#include <linux/phy_fixed.h>
|
||||
+#include <linux/phy/phy.h>
|
||||
#include <linux/of_mdio.h>
|
||||
|
||||
/* PCS registers */
|
||||
@@ -324,6 +325,7 @@ struct fman_mac {
|
||||
void *fm;
|
||||
struct fman_rev_info fm_rev_info;
|
||||
bool basex_if;
|
||||
+ struct phy *serdes;
|
||||
struct phy_device *pcsphy;
|
||||
bool allmulti_enabled;
|
||||
};
|
||||
@@ -1203,17 +1205,56 @@ int memac_initialization(struct mac_devi
|
||||
}
|
||||
}
|
||||
|
||||
+ memac->serdes = devm_of_phy_get(mac_dev->dev, mac_node, "serdes");
|
||||
+ err = PTR_ERR(memac->serdes);
|
||||
+ if (err == -ENODEV || err == -ENOSYS) {
|
||||
+ dev_dbg(mac_dev->dev, "could not get (optional) serdes\n");
|
||||
+ memac->serdes = NULL;
|
||||
+ } else if (IS_ERR(memac->serdes)) {
|
||||
+ dev_err_probe(mac_dev->dev, err, "could not get serdes\n");
|
||||
+ goto _return_fm_mac_free;
|
||||
+ } else {
|
||||
+ err = phy_init(memac->serdes);
|
||||
+ if (err) {
|
||||
+ dev_err_probe(mac_dev->dev, err,
|
||||
+ "could not initialize serdes\n");
|
||||
+ goto _return_fm_mac_free;
|
||||
+ }
|
||||
+
|
||||
+ err = phy_power_on(memac->serdes);
|
||||
+ if (err) {
|
||||
+ dev_err_probe(mac_dev->dev, err,
|
||||
+ "could not power on serdes\n");
|
||||
+ goto _return_phy_exit;
|
||||
+ }
|
||||
+
|
||||
+ if (memac->phy_if == PHY_INTERFACE_MODE_SGMII ||
|
||||
+ memac->phy_if == PHY_INTERFACE_MODE_1000BASEX ||
|
||||
+ memac->phy_if == PHY_INTERFACE_MODE_2500BASEX ||
|
||||
+ memac->phy_if == PHY_INTERFACE_MODE_QSGMII ||
|
||||
+ memac->phy_if == PHY_INTERFACE_MODE_XGMII) {
|
||||
+ err = phy_set_mode_ext(memac->serdes, PHY_MODE_ETHERNET,
|
||||
+ memac->phy_if);
|
||||
+ if (err) {
|
||||
+ dev_err_probe(mac_dev->dev, err,
|
||||
+ "could not set serdes mode to %s\n",
|
||||
+ phy_modes(memac->phy_if));
|
||||
+ goto _return_phy_power_off;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
if (!mac_dev->phy_node && of_phy_is_fixed_link(mac_node)) {
|
||||
struct phy_device *phy;
|
||||
|
||||
err = of_phy_register_fixed_link(mac_node);
|
||||
if (err)
|
||||
- goto _return_fm_mac_free;
|
||||
+ goto _return_phy_power_off;
|
||||
|
||||
fixed_link = kzalloc(sizeof(*fixed_link), GFP_KERNEL);
|
||||
if (!fixed_link) {
|
||||
err = -ENOMEM;
|
||||
- goto _return_fm_mac_free;
|
||||
+ goto _return_phy_power_off;
|
||||
}
|
||||
|
||||
mac_dev->phy_node = of_node_get(mac_node);
|
||||
@@ -1242,6 +1283,10 @@ int memac_initialization(struct mac_devi
|
||||
|
||||
goto _return;
|
||||
|
||||
+_return_phy_power_off:
|
||||
+ phy_power_off(memac->serdes);
|
||||
+_return_phy_exit:
|
||||
+ phy_exit(memac->serdes);
|
||||
_return_fixed_link_free:
|
||||
kfree(fixed_link);
|
||||
_return_fm_mac_free:
|
@ -0,0 +1,384 @@
|
||||
From fe60e7154d3a35af975c5e6570d6ec31aab9a731 Mon Sep 17 00:00:00 2001
|
||||
From: Sean Anderson <sean.anderson@seco.com>
|
||||
Date: Mon, 17 Oct 2022 16:22:37 -0400
|
||||
Subject: [PATCH 02/21] net: fman: memac: Use lynx pcs driver
|
||||
|
||||
Although not stated in the datasheet, as far as I can tell PCS for mEMACs
|
||||
is a "Lynx." By reusing the existing driver, we can remove the PCS
|
||||
management code from the memac driver. This requires calling some PCS
|
||||
functions manually which phylink would usually do for us, but we will let
|
||||
it do that soon.
|
||||
|
||||
One problem is that we don't actually have a PCS for QSGMII. We pretend
|
||||
that each mEMAC's MDIO bus has four QSGMII PCSs, but this is not the case.
|
||||
Only the "base" mEMAC's MDIO bus has the four QSGMII PCSs. This is not an
|
||||
issue yet, because we never get the PCS state. However, it will be once the
|
||||
conversion to phylink is complete, since the links will appear to never
|
||||
come up. To get around this, we allow specifying multiple PCSs in pcsphy.
|
||||
This breaks backwards compatibility with old device trees, but only for
|
||||
QSGMII. IMO this is the only reasonable way to figure out what the actual
|
||||
QSGMII PCS is.
|
||||
|
||||
Additionally, we now also support a separate XFI PCS. This can allow the
|
||||
SerDes driver to set different addresses for the SGMII and XFI PCSs so they
|
||||
can be accessed at the same time.
|
||||
|
||||
Signed-off-by: Sean Anderson <sean.anderson@seco.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/ethernet/freescale/fman/Kconfig | 3 +
|
||||
.../net/ethernet/freescale/fman/fman_memac.c | 258 +++++++-----------
|
||||
2 files changed, 105 insertions(+), 156 deletions(-)
|
||||
|
||||
--- a/drivers/net/ethernet/freescale/fman/Kconfig
|
||||
+++ b/drivers/net/ethernet/freescale/fman/Kconfig
|
||||
@@ -4,6 +4,9 @@ config FSL_FMAN
|
||||
depends on FSL_SOC || ARCH_LAYERSCAPE || COMPILE_TEST
|
||||
select GENERIC_ALLOCATOR
|
||||
select PHYLIB
|
||||
+ select PHYLINK
|
||||
+ select PCS
|
||||
+ select PCS_LYNX
|
||||
select CRC32
|
||||
default n
|
||||
help
|
||||
--- a/drivers/net/ethernet/freescale/fman/fman_memac.c
|
||||
+++ b/drivers/net/ethernet/freescale/fman/fman_memac.c
|
||||
@@ -11,43 +11,12 @@
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/io.h>
|
||||
+#include <linux/pcs-lynx.h>
|
||||
#include <linux/phy.h>
|
||||
#include <linux/phy_fixed.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/of_mdio.h>
|
||||
|
||||
-/* PCS registers */
|
||||
-#define MDIO_SGMII_CR 0x00
|
||||
-#define MDIO_SGMII_DEV_ABIL_SGMII 0x04
|
||||
-#define MDIO_SGMII_LINK_TMR_L 0x12
|
||||
-#define MDIO_SGMII_LINK_TMR_H 0x13
|
||||
-#define MDIO_SGMII_IF_MODE 0x14
|
||||
-
|
||||
-/* SGMII Control defines */
|
||||
-#define SGMII_CR_AN_EN 0x1000
|
||||
-#define SGMII_CR_RESTART_AN 0x0200
|
||||
-#define SGMII_CR_FD 0x0100
|
||||
-#define SGMII_CR_SPEED_SEL1_1G 0x0040
|
||||
-#define SGMII_CR_DEF_VAL (SGMII_CR_AN_EN | SGMII_CR_FD | \
|
||||
- SGMII_CR_SPEED_SEL1_1G)
|
||||
-
|
||||
-/* SGMII Device Ability for SGMII defines */
|
||||
-#define MDIO_SGMII_DEV_ABIL_SGMII_MODE 0x4001
|
||||
-#define MDIO_SGMII_DEV_ABIL_BASEX_MODE 0x01A0
|
||||
-
|
||||
-/* Link timer define */
|
||||
-#define LINK_TMR_L 0xa120
|
||||
-#define LINK_TMR_H 0x0007
|
||||
-#define LINK_TMR_L_BASEX 0xaf08
|
||||
-#define LINK_TMR_H_BASEX 0x002f
|
||||
-
|
||||
-/* SGMII IF Mode defines */
|
||||
-#define IF_MODE_USE_SGMII_AN 0x0002
|
||||
-#define IF_MODE_SGMII_EN 0x0001
|
||||
-#define IF_MODE_SGMII_SPEED_100M 0x0004
|
||||
-#define IF_MODE_SGMII_SPEED_1G 0x0008
|
||||
-#define IF_MODE_SGMII_DUPLEX_HALF 0x0010
|
||||
-
|
||||
/* Num of additional exact match MAC adr regs */
|
||||
#define MEMAC_NUM_OF_PADDRS 7
|
||||
|
||||
@@ -326,7 +295,9 @@ struct fman_mac {
|
||||
struct fman_rev_info fm_rev_info;
|
||||
bool basex_if;
|
||||
struct phy *serdes;
|
||||
- struct phy_device *pcsphy;
|
||||
+ struct phylink_pcs *sgmii_pcs;
|
||||
+ struct phylink_pcs *qsgmii_pcs;
|
||||
+ struct phylink_pcs *xfi_pcs;
|
||||
bool allmulti_enabled;
|
||||
};
|
||||
|
||||
@@ -487,91 +458,22 @@ static u32 get_mac_addr_hash_code(u64 et
|
||||
return xor_val;
|
||||
}
|
||||
|
||||
-static void setup_sgmii_internal_phy(struct fman_mac *memac,
|
||||
- struct fixed_phy_status *fixed_link)
|
||||
-{
|
||||
- u16 tmp_reg16;
|
||||
-
|
||||
- if (WARN_ON(!memac->pcsphy))
|
||||
- return;
|
||||
-
|
||||
- /* SGMII mode */
|
||||
- tmp_reg16 = IF_MODE_SGMII_EN;
|
||||
- if (!fixed_link)
|
||||
- /* AN enable */
|
||||
- tmp_reg16 |= IF_MODE_USE_SGMII_AN;
|
||||
- else {
|
||||
- switch (fixed_link->speed) {
|
||||
- case 10:
|
||||
- /* For 10M: IF_MODE[SPEED_10M] = 0 */
|
||||
- break;
|
||||
- case 100:
|
||||
- tmp_reg16 |= IF_MODE_SGMII_SPEED_100M;
|
||||
- break;
|
||||
- case 1000:
|
||||
- default:
|
||||
- tmp_reg16 |= IF_MODE_SGMII_SPEED_1G;
|
||||
- break;
|
||||
- }
|
||||
- if (!fixed_link->duplex)
|
||||
- tmp_reg16 |= IF_MODE_SGMII_DUPLEX_HALF;
|
||||
- }
|
||||
- phy_write(memac->pcsphy, MDIO_SGMII_IF_MODE, tmp_reg16);
|
||||
-
|
||||
- /* Device ability according to SGMII specification */
|
||||
- tmp_reg16 = MDIO_SGMII_DEV_ABIL_SGMII_MODE;
|
||||
- phy_write(memac->pcsphy, MDIO_SGMII_DEV_ABIL_SGMII, tmp_reg16);
|
||||
-
|
||||
- /* Adjust link timer for SGMII -
|
||||
- * According to Cisco SGMII specification the timer should be 1.6 ms.
|
||||
- * The link_timer register is configured in units of the clock.
|
||||
- * - When running as 1G SGMII, Serdes clock is 125 MHz, so
|
||||
- * unit = 1 / (125*10^6 Hz) = 8 ns.
|
||||
- * 1.6 ms in units of 8 ns = 1.6ms / 8ns = 2*10^5 = 0x30d40
|
||||
- * - When running as 2.5G SGMII, Serdes clock is 312.5 MHz, so
|
||||
- * unit = 1 / (312.5*10^6 Hz) = 3.2 ns.
|
||||
- * 1.6 ms in units of 3.2 ns = 1.6ms / 3.2ns = 5*10^5 = 0x7a120.
|
||||
- * Since link_timer value of 1G SGMII will be too short for 2.5 SGMII,
|
||||
- * we always set up here a value of 2.5 SGMII.
|
||||
- */
|
||||
- phy_write(memac->pcsphy, MDIO_SGMII_LINK_TMR_H, LINK_TMR_H);
|
||||
- phy_write(memac->pcsphy, MDIO_SGMII_LINK_TMR_L, LINK_TMR_L);
|
||||
-
|
||||
- if (!fixed_link)
|
||||
- /* Restart AN */
|
||||
- tmp_reg16 = SGMII_CR_DEF_VAL | SGMII_CR_RESTART_AN;
|
||||
+static void setup_sgmii_internal(struct fman_mac *memac,
|
||||
+ struct phylink_pcs *pcs,
|
||||
+ struct fixed_phy_status *fixed_link)
|
||||
+{
|
||||
+ __ETHTOOL_DECLARE_LINK_MODE_MASK(advertising);
|
||||
+ phy_interface_t iface = memac->basex_if ? PHY_INTERFACE_MODE_1000BASEX :
|
||||
+ PHY_INTERFACE_MODE_SGMII;
|
||||
+ unsigned int mode = fixed_link ? MLO_AN_FIXED : MLO_AN_INBAND;
|
||||
+
|
||||
+ linkmode_set_pause(advertising, true, true);
|
||||
+ pcs->ops->pcs_config(pcs, mode, iface, advertising, true);
|
||||
+ if (fixed_link)
|
||||
+ pcs->ops->pcs_link_up(pcs, mode, iface, fixed_link->speed,
|
||||
+ fixed_link->duplex);
|
||||
else
|
||||
- /* AN disabled */
|
||||
- tmp_reg16 = SGMII_CR_DEF_VAL & ~SGMII_CR_AN_EN;
|
||||
- phy_write(memac->pcsphy, 0x0, tmp_reg16);
|
||||
-}
|
||||
-
|
||||
-static void setup_sgmii_internal_phy_base_x(struct fman_mac *memac)
|
||||
-{
|
||||
- u16 tmp_reg16;
|
||||
-
|
||||
- /* AN Device capability */
|
||||
- tmp_reg16 = MDIO_SGMII_DEV_ABIL_BASEX_MODE;
|
||||
- phy_write(memac->pcsphy, MDIO_SGMII_DEV_ABIL_SGMII, tmp_reg16);
|
||||
-
|
||||
- /* Adjust link timer for SGMII -
|
||||
- * For Serdes 1000BaseX auto-negotiation the timer should be 10 ms.
|
||||
- * The link_timer register is configured in units of the clock.
|
||||
- * - When running as 1G SGMII, Serdes clock is 125 MHz, so
|
||||
- * unit = 1 / (125*10^6 Hz) = 8 ns.
|
||||
- * 10 ms in units of 8 ns = 10ms / 8ns = 1250000 = 0x1312d0
|
||||
- * - When running as 2.5G SGMII, Serdes clock is 312.5 MHz, so
|
||||
- * unit = 1 / (312.5*10^6 Hz) = 3.2 ns.
|
||||
- * 10 ms in units of 3.2 ns = 10ms / 3.2ns = 3125000 = 0x2faf08.
|
||||
- * Since link_timer value of 1G SGMII will be too short for 2.5 SGMII,
|
||||
- * we always set up here a value of 2.5 SGMII.
|
||||
- */
|
||||
- phy_write(memac->pcsphy, MDIO_SGMII_LINK_TMR_H, LINK_TMR_H_BASEX);
|
||||
- phy_write(memac->pcsphy, MDIO_SGMII_LINK_TMR_L, LINK_TMR_L_BASEX);
|
||||
-
|
||||
- /* Restart AN */
|
||||
- tmp_reg16 = SGMII_CR_DEF_VAL | SGMII_CR_RESTART_AN;
|
||||
- phy_write(memac->pcsphy, 0x0, tmp_reg16);
|
||||
+ pcs->ops->pcs_an_restart(pcs);
|
||||
}
|
||||
|
||||
static int check_init_parameters(struct fman_mac *memac)
|
||||
@@ -983,7 +885,6 @@ static int memac_set_exception(struct fm
|
||||
static int memac_init(struct fman_mac *memac)
|
||||
{
|
||||
struct memac_cfg *memac_drv_param;
|
||||
- u8 i;
|
||||
enet_addr_t eth_addr;
|
||||
bool slow_10g_if = false;
|
||||
struct fixed_phy_status *fixed_link = NULL;
|
||||
@@ -1036,32 +937,10 @@ static int memac_init(struct fman_mac *m
|
||||
iowrite32be(reg32, &memac->regs->command_config);
|
||||
}
|
||||
|
||||
- if (memac->phy_if == PHY_INTERFACE_MODE_SGMII) {
|
||||
- /* Configure internal SGMII PHY */
|
||||
- if (memac->basex_if)
|
||||
- setup_sgmii_internal_phy_base_x(memac);
|
||||
- else
|
||||
- setup_sgmii_internal_phy(memac, fixed_link);
|
||||
- } else if (memac->phy_if == PHY_INTERFACE_MODE_QSGMII) {
|
||||
- /* Configure 4 internal SGMII PHYs */
|
||||
- for (i = 0; i < 4; i++) {
|
||||
- u8 qsmgii_phy_addr, phy_addr;
|
||||
- /* QSGMII PHY address occupies 3 upper bits of 5-bit
|
||||
- * phy_address; the lower 2 bits are used to extend
|
||||
- * register address space and access each one of 4
|
||||
- * ports inside QSGMII.
|
||||
- */
|
||||
- phy_addr = memac->pcsphy->mdio.addr;
|
||||
- qsmgii_phy_addr = (u8)((phy_addr << 2) | i);
|
||||
- memac->pcsphy->mdio.addr = qsmgii_phy_addr;
|
||||
- if (memac->basex_if)
|
||||
- setup_sgmii_internal_phy_base_x(memac);
|
||||
- else
|
||||
- setup_sgmii_internal_phy(memac, fixed_link);
|
||||
-
|
||||
- memac->pcsphy->mdio.addr = phy_addr;
|
||||
- }
|
||||
- }
|
||||
+ if (memac->phy_if == PHY_INTERFACE_MODE_SGMII)
|
||||
+ setup_sgmii_internal(memac, memac->sgmii_pcs, fixed_link);
|
||||
+ else if (memac->phy_if == PHY_INTERFACE_MODE_QSGMII)
|
||||
+ setup_sgmii_internal(memac, memac->qsgmii_pcs, fixed_link);
|
||||
|
||||
/* Max Frame Length */
|
||||
err = fman_set_mac_max_frame(memac->fm, memac->mac_id,
|
||||
@@ -1097,12 +976,25 @@ static int memac_init(struct fman_mac *m
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static void pcs_put(struct phylink_pcs *pcs)
|
||||
+{
|
||||
+ struct mdio_device *mdiodev;
|
||||
+
|
||||
+ if (IS_ERR_OR_NULL(pcs))
|
||||
+ return;
|
||||
+
|
||||
+ mdiodev = lynx_get_mdio_device(pcs);
|
||||
+ lynx_pcs_destroy(pcs);
|
||||
+ mdio_device_free(mdiodev);
|
||||
+}
|
||||
+
|
||||
static int memac_free(struct fman_mac *memac)
|
||||
{
|
||||
free_init_resources(memac);
|
||||
|
||||
- if (memac->pcsphy)
|
||||
- put_device(&memac->pcsphy->mdio.dev);
|
||||
+ pcs_put(memac->sgmii_pcs);
|
||||
+ pcs_put(memac->qsgmii_pcs);
|
||||
+ pcs_put(memac->xfi_pcs);
|
||||
|
||||
kfree(memac->memac_drv_param);
|
||||
kfree(memac);
|
||||
@@ -1153,12 +1045,31 @@ static struct fman_mac *memac_config(str
|
||||
return memac;
|
||||
}
|
||||
|
||||
+static struct phylink_pcs *memac_pcs_create(struct device_node *mac_node,
|
||||
+ int index)
|
||||
+{
|
||||
+ struct device_node *node;
|
||||
+ struct mdio_device *mdiodev = NULL;
|
||||
+ struct phylink_pcs *pcs;
|
||||
+
|
||||
+ node = of_parse_phandle(mac_node, "pcsphy-handle", index);
|
||||
+ if (node && of_device_is_available(node))
|
||||
+ mdiodev = of_mdio_find_device(node);
|
||||
+ of_node_put(node);
|
||||
+
|
||||
+ if (!mdiodev)
|
||||
+ return ERR_PTR(-EPROBE_DEFER);
|
||||
+
|
||||
+ pcs = lynx_pcs_create(mdiodev);
|
||||
+ return pcs;
|
||||
+}
|
||||
+
|
||||
int memac_initialization(struct mac_device *mac_dev,
|
||||
struct device_node *mac_node,
|
||||
struct fman_mac_params *params)
|
||||
{
|
||||
int err;
|
||||
- struct device_node *phy_node;
|
||||
+ struct phylink_pcs *pcs;
|
||||
struct fixed_phy_status *fixed_link;
|
||||
struct fman_mac *memac;
|
||||
|
||||
@@ -1188,23 +1099,58 @@ int memac_initialization(struct mac_devi
|
||||
memac = mac_dev->fman_mac;
|
||||
memac->memac_drv_param->max_frame_length = fman_get_max_frm();
|
||||
memac->memac_drv_param->reset_on_init = true;
|
||||
- if (memac->phy_if == PHY_INTERFACE_MODE_SGMII ||
|
||||
- memac->phy_if == PHY_INTERFACE_MODE_QSGMII) {
|
||||
- phy_node = of_parse_phandle(mac_node, "pcsphy-handle", 0);
|
||||
- if (!phy_node) {
|
||||
- pr_err("PCS PHY node is not available\n");
|
||||
- err = -EINVAL;
|
||||
+
|
||||
+ err = of_property_match_string(mac_node, "pcs-handle-names", "xfi");
|
||||
+ if (err >= 0) {
|
||||
+ memac->xfi_pcs = memac_pcs_create(mac_node, err);
|
||||
+ if (IS_ERR(memac->xfi_pcs)) {
|
||||
+ err = PTR_ERR(memac->xfi_pcs);
|
||||
+ dev_err_probe(mac_dev->dev, err, "missing xfi pcs\n");
|
||||
goto _return_fm_mac_free;
|
||||
}
|
||||
+ } else if (err != -EINVAL && err != -ENODATA) {
|
||||
+ goto _return_fm_mac_free;
|
||||
+ }
|
||||
|
||||
- memac->pcsphy = of_phy_find_device(phy_node);
|
||||
- if (!memac->pcsphy) {
|
||||
- pr_err("of_phy_find_device (PCS PHY) failed\n");
|
||||
- err = -EINVAL;
|
||||
+ err = of_property_match_string(mac_node, "pcs-handle-names", "qsgmii");
|
||||
+ if (err >= 0) {
|
||||
+ memac->qsgmii_pcs = memac_pcs_create(mac_node, err);
|
||||
+ if (IS_ERR(memac->qsgmii_pcs)) {
|
||||
+ err = PTR_ERR(memac->qsgmii_pcs);
|
||||
+ dev_err_probe(mac_dev->dev, err,
|
||||
+ "missing qsgmii pcs\n");
|
||||
goto _return_fm_mac_free;
|
||||
}
|
||||
+ } else if (err != -EINVAL && err != -ENODATA) {
|
||||
+ goto _return_fm_mac_free;
|
||||
+ }
|
||||
+
|
||||
+ /* For compatibility, if pcs-handle-names is missing, we assume this
|
||||
+ * phy is the first one in pcsphy-handle
|
||||
+ */
|
||||
+ err = of_property_match_string(mac_node, "pcs-handle-names", "sgmii");
|
||||
+ if (err == -EINVAL || err == -ENODATA)
|
||||
+ pcs = memac_pcs_create(mac_node, 0);
|
||||
+ else if (err < 0)
|
||||
+ goto _return_fm_mac_free;
|
||||
+ else
|
||||
+ pcs = memac_pcs_create(mac_node, err);
|
||||
+
|
||||
+ if (!pcs) {
|
||||
+ dev_err(mac_dev->dev, "missing pcs\n");
|
||||
+ err = -ENOENT;
|
||||
+ goto _return_fm_mac_free;
|
||||
}
|
||||
|
||||
+ /* If err is set here, it means that pcs-handle-names was missing above
|
||||
+ * (and therefore that xfi_pcs cannot be set). If we are defaulting to
|
||||
+ * XGMII, assume this is for XFI. Otherwise, assume it is for SGMII.
|
||||
+ */
|
||||
+ if (err && mac_dev->phy_if == PHY_INTERFACE_MODE_XGMII)
|
||||
+ memac->xfi_pcs = pcs;
|
||||
+ else
|
||||
+ memac->sgmii_pcs = pcs;
|
||||
+
|
||||
memac->serdes = devm_of_phy_get(mac_dev->dev, mac_node, "serdes");
|
||||
err = PTR_ERR(memac->serdes);
|
||||
if (err == -ENODEV || err == -ENOSYS) {
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,93 @@
|
||||
From bf4de031052fe7c5309e8956c342d4e5ce79038e Mon Sep 17 00:00:00 2001
|
||||
From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
|
||||
Date: Mon, 17 Oct 2022 16:22:35 -0400
|
||||
Subject: [PATCH 04/21] net: phylink: provide phylink_validate_mask_caps()
|
||||
helper
|
||||
|
||||
Provide a helper that restricts the link modes according to the
|
||||
phylink capabilities.
|
||||
|
||||
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
|
||||
[rebased on net-next/master and added documentation]
|
||||
Signed-off-by: Sean Anderson <sean.anderson@seco.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/phylink.c | 41 +++++++++++++++++++++++++++------------
|
||||
include/linux/phylink.h | 3 +++
|
||||
2 files changed, 32 insertions(+), 12 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/phylink.c
|
||||
+++ b/drivers/net/phy/phylink.c
|
||||
@@ -564,31 +564,48 @@ unsigned long phylink_get_capabilities(p
|
||||
EXPORT_SYMBOL_GPL(phylink_get_capabilities);
|
||||
|
||||
/**
|
||||
- * phylink_generic_validate() - generic validate() callback implementation
|
||||
- * @config: a pointer to a &struct phylink_config.
|
||||
+ * phylink_validate_mask_caps() - Restrict link modes based on caps
|
||||
* @supported: ethtool bitmask for supported link modes.
|
||||
- * @state: a pointer to a &struct phylink_link_state.
|
||||
+ * @state: an (optional) pointer to a &struct phylink_link_state.
|
||||
+ * @mac_capabilities: bitmask of MAC capabilities
|
||||
*
|
||||
- * Generic implementation of the validate() callback that MAC drivers can
|
||||
- * use when they pass the range of supported interfaces and MAC capabilities.
|
||||
- * This makes use of phylink_get_linkmodes().
|
||||
+ * Calculate the supported link modes based on @mac_capabilities, and restrict
|
||||
+ * @supported and @state based on that. Use this function if your capabiliies
|
||||
+ * aren't constant, such as if they vary depending on the interface.
|
||||
*/
|
||||
-void phylink_generic_validate(struct phylink_config *config,
|
||||
- unsigned long *supported,
|
||||
- struct phylink_link_state *state)
|
||||
+void phylink_validate_mask_caps(unsigned long *supported,
|
||||
+ struct phylink_link_state *state,
|
||||
+ unsigned long mac_capabilities)
|
||||
{
|
||||
__ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
|
||||
unsigned long caps;
|
||||
|
||||
phylink_set_port_modes(mask);
|
||||
phylink_set(mask, Autoneg);
|
||||
- caps = phylink_get_capabilities(state->interface,
|
||||
- config->mac_capabilities,
|
||||
+ caps = phylink_get_capabilities(state->interface, mac_capabilities,
|
||||
state->rate_matching);
|
||||
phylink_caps_to_linkmodes(mask, caps);
|
||||
|
||||
linkmode_and(supported, supported, mask);
|
||||
- linkmode_and(state->advertising, state->advertising, mask);
|
||||
+ if (state)
|
||||
+ linkmode_and(state->advertising, state->advertising, mask);
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(phylink_validate_mask_caps);
|
||||
+
|
||||
+/**
|
||||
+ * phylink_generic_validate() - generic validate() callback implementation
|
||||
+ * @config: a pointer to a &struct phylink_config.
|
||||
+ * @supported: ethtool bitmask for supported link modes.
|
||||
+ * @state: a pointer to a &struct phylink_link_state.
|
||||
+ *
|
||||
+ * Generic implementation of the validate() callback that MAC drivers can
|
||||
+ * use when they pass the range of supported interfaces and MAC capabilities.
|
||||
+ */
|
||||
+void phylink_generic_validate(struct phylink_config *config,
|
||||
+ unsigned long *supported,
|
||||
+ struct phylink_link_state *state)
|
||||
+{
|
||||
+ phylink_validate_mask_caps(supported, state, config->mac_capabilities);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(phylink_generic_validate);
|
||||
|
||||
--- a/include/linux/phylink.h
|
||||
+++ b/include/linux/phylink.h
|
||||
@@ -558,6 +558,9 @@ void phylink_caps_to_linkmodes(unsigned
|
||||
unsigned long phylink_get_capabilities(phy_interface_t interface,
|
||||
unsigned long mac_capabilities,
|
||||
int rate_matching);
|
||||
+void phylink_validate_mask_caps(unsigned long *supported,
|
||||
+ struct phylink_link_state *state,
|
||||
+ unsigned long caps);
|
||||
void phylink_generic_validate(struct phylink_config *config,
|
||||
unsigned long *supported,
|
||||
struct phylink_link_state *state);
|
@ -0,0 +1,39 @@
|
||||
From 2bf7e4a68c42eed909f3c29582e1fb85cb157e35 Mon Sep 17 00:00:00 2001
|
||||
From: Jakub Kicinski <kuba@kernel.org>
|
||||
Date: Tue, 25 Oct 2022 11:51:26 -0700
|
||||
Subject: [PATCH 05/21] phylink: require valid state argument to
|
||||
phylink_validate_mask_caps()
|
||||
|
||||
state is deferenced earlier in the function, the NULL check
|
||||
is pointless. Since we don't have any crash reports presumably
|
||||
it's safe to assume state is not NULL.
|
||||
|
||||
Fixes: f392a1846489 ("net: phylink: provide phylink_validate_mask_caps() helper")
|
||||
Reviewed-by: Sean Anderson <sean.anderson@seco.com>
|
||||
Link: https://lore.kernel.org/r/20221025185126.1720553-1-kuba@kernel.org
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
---
|
||||
drivers/net/phy/phylink.c | 5 ++---
|
||||
1 file changed, 2 insertions(+), 3 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/phylink.c
|
||||
+++ b/drivers/net/phy/phylink.c
|
||||
@@ -566,7 +566,7 @@ EXPORT_SYMBOL_GPL(phylink_get_capabiliti
|
||||
/**
|
||||
* phylink_validate_mask_caps() - Restrict link modes based on caps
|
||||
* @supported: ethtool bitmask for supported link modes.
|
||||
- * @state: an (optional) pointer to a &struct phylink_link_state.
|
||||
+ * @state: pointer to a &struct phylink_link_state.
|
||||
* @mac_capabilities: bitmask of MAC capabilities
|
||||
*
|
||||
* Calculate the supported link modes based on @mac_capabilities, and restrict
|
||||
@@ -587,8 +587,7 @@ void phylink_validate_mask_caps(unsigned
|
||||
phylink_caps_to_linkmodes(mask, caps);
|
||||
|
||||
linkmode_and(supported, supported, mask);
|
||||
- if (state)
|
||||
- linkmode_and(state->advertising, state->advertising, mask);
|
||||
+ linkmode_and(state->advertising, state->advertising, mask);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(phylink_validate_mask_caps);
|
||||
|
@ -1,7 +1,7 @@
|
||||
From 9c5a170677c3c8facc83e931a57f4c99c0511ae0 Mon Sep 17 00:00:00 2001
|
||||
From f8fc363bf0c023e4736a0328174b4a24b44ab23a Mon Sep 17 00:00:00 2001
|
||||
From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
|
||||
Date: Thu, 27 Oct 2022 14:10:37 +0100
|
||||
Subject: [PATCH] net: phylink: add phylink_get_link_timer_ns() helper
|
||||
Subject: [PATCH 06/21] net: phylink: add phylink_get_link_timer_ns() helper
|
||||
|
||||
Add a helper to convert the PHY interface mode to the required link
|
||||
timer setting as stated by the appropriate standard. Inappropriate
|
||||
@ -15,7 +15,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
|
||||
--- a/include/linux/phylink.h
|
||||
+++ b/include/linux/phylink.h
|
||||
@@ -614,6 +614,30 @@ int phylink_speed_up(struct phylink *pl)
|
||||
@@ -617,6 +617,30 @@ int phylink_speed_up(struct phylink *pl)
|
||||
|
||||
void phylink_set_port_modes(unsigned long *bits);
|
||||
|
@ -0,0 +1,250 @@
|
||||
From b45b773a96b0e9e8d51e5d005485f4e376d6ce9a Mon Sep 17 00:00:00 2001
|
||||
From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
|
||||
Date: Fri, 4 Nov 2022 17:13:01 +0000
|
||||
Subject: [PATCH 07/21] net: remove explicit phylink_generic_validate()
|
||||
references
|
||||
|
||||
Virtually all conventional network drivers are now converted to use
|
||||
phylink_generic_validate() - only DSA drivers and fman_memac remain,
|
||||
so lets remove the necessity for network drivers to explicitly set
|
||||
this member, and default to phylink_generic_validate() when unset.
|
||||
This is possible as .validate must currently be set.
|
||||
|
||||
Any remaining instances that have not been addressed by this patch can
|
||||
be fixed up later.
|
||||
|
||||
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
|
||||
Reviewed-by: Vladimir Oltean <vladimir.oltean@nxp.com>
|
||||
Link: https://lore.kernel.org/r/E1or0FZ-001tRa-DI@rmk-PC.armlinux.org.uk
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
---
|
||||
drivers/net/ethernet/altera/altera_tse_main.c | 1 -
|
||||
drivers/net/ethernet/atheros/ag71xx.c | 1 -
|
||||
drivers/net/ethernet/cadence/macb_main.c | 1 -
|
||||
drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c | 1 -
|
||||
drivers/net/ethernet/freescale/enetc/enetc_pf.c | 1 -
|
||||
drivers/net/ethernet/freescale/fman/fman_dtsec.c | 1 -
|
||||
drivers/net/ethernet/freescale/fman/fman_tgec.c | 1 -
|
||||
drivers/net/ethernet/marvell/mvneta.c | 1 -
|
||||
drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c | 1 -
|
||||
drivers/net/ethernet/marvell/prestera/prestera_main.c | 1 -
|
||||
drivers/net/ethernet/mediatek/mtk_eth_soc.c | 1 -
|
||||
drivers/net/ethernet/microchip/lan966x/lan966x_phylink.c | 1 -
|
||||
drivers/net/ethernet/microchip/sparx5/sparx5_phylink.c | 1 -
|
||||
drivers/net/ethernet/mscc/ocelot_net.c | 1 -
|
||||
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 1 -
|
||||
drivers/net/ethernet/ti/am65-cpsw-nuss.c | 1 -
|
||||
drivers/net/ethernet/xilinx/xilinx_axienet_main.c | 1 -
|
||||
drivers/net/phy/phylink.c | 5 ++++-
|
||||
drivers/net/usb/asix_devices.c | 1 -
|
||||
include/linux/phylink.h | 5 +++++
|
||||
20 files changed, 9 insertions(+), 19 deletions(-)
|
||||
|
||||
--- a/drivers/net/ethernet/altera/altera_tse_main.c
|
||||
+++ b/drivers/net/ethernet/altera/altera_tse_main.c
|
||||
@@ -1096,7 +1096,6 @@ static struct phylink_pcs *alt_tse_selec
|
||||
}
|
||||
|
||||
static const struct phylink_mac_ops alt_tse_phylink_ops = {
|
||||
- .validate = phylink_generic_validate,
|
||||
.mac_an_restart = alt_tse_mac_an_restart,
|
||||
.mac_config = alt_tse_mac_config,
|
||||
.mac_link_down = alt_tse_mac_link_down,
|
||||
--- a/drivers/net/ethernet/atheros/ag71xx.c
|
||||
+++ b/drivers/net/ethernet/atheros/ag71xx.c
|
||||
@@ -1086,7 +1086,6 @@ static void ag71xx_mac_link_up(struct ph
|
||||
}
|
||||
|
||||
static const struct phylink_mac_ops ag71xx_phylink_mac_ops = {
|
||||
- .validate = phylink_generic_validate,
|
||||
.mac_config = ag71xx_mac_config,
|
||||
.mac_link_down = ag71xx_mac_link_down,
|
||||
.mac_link_up = ag71xx_mac_link_up,
|
||||
--- a/drivers/net/ethernet/cadence/macb_main.c
|
||||
+++ b/drivers/net/ethernet/cadence/macb_main.c
|
||||
@@ -752,7 +752,6 @@ static struct phylink_pcs *macb_mac_sele
|
||||
}
|
||||
|
||||
static const struct phylink_mac_ops macb_phylink_ops = {
|
||||
- .validate = phylink_generic_validate,
|
||||
.mac_select_pcs = macb_mac_select_pcs,
|
||||
.mac_config = macb_mac_config,
|
||||
.mac_link_down = macb_mac_link_down,
|
||||
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c
|
||||
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c
|
||||
@@ -235,7 +235,6 @@ static void dpaa2_mac_link_down(struct p
|
||||
}
|
||||
|
||||
static const struct phylink_mac_ops dpaa2_mac_phylink_ops = {
|
||||
- .validate = phylink_generic_validate,
|
||||
.mac_select_pcs = dpaa2_mac_select_pcs,
|
||||
.mac_config = dpaa2_mac_config,
|
||||
.mac_link_up = dpaa2_mac_link_up,
|
||||
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf.c
|
||||
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.c
|
||||
@@ -1111,7 +1111,6 @@ static void enetc_pl_mac_link_down(struc
|
||||
}
|
||||
|
||||
static const struct phylink_mac_ops enetc_mac_phylink_ops = {
|
||||
- .validate = phylink_generic_validate,
|
||||
.mac_select_pcs = enetc_pl_mac_select_pcs,
|
||||
.mac_config = enetc_pl_mac_config,
|
||||
.mac_link_up = enetc_pl_mac_link_up,
|
||||
--- a/drivers/net/ethernet/freescale/fman/fman_dtsec.c
|
||||
+++ b/drivers/net/ethernet/freescale/fman/fman_dtsec.c
|
||||
@@ -986,7 +986,6 @@ static void dtsec_link_down(struct phyli
|
||||
}
|
||||
|
||||
static const struct phylink_mac_ops dtsec_mac_ops = {
|
||||
- .validate = phylink_generic_validate,
|
||||
.mac_select_pcs = dtsec_select_pcs,
|
||||
.mac_config = dtsec_mac_config,
|
||||
.mac_link_up = dtsec_link_up,
|
||||
--- a/drivers/net/ethernet/freescale/fman/fman_tgec.c
|
||||
+++ b/drivers/net/ethernet/freescale/fman/fman_tgec.c
|
||||
@@ -469,7 +469,6 @@ static void tgec_link_down(struct phylin
|
||||
}
|
||||
|
||||
static const struct phylink_mac_ops tgec_mac_ops = {
|
||||
- .validate = phylink_generic_validate,
|
||||
.mac_config = tgec_mac_config,
|
||||
.mac_link_up = tgec_link_up,
|
||||
.mac_link_down = tgec_link_down,
|
||||
--- a/drivers/net/ethernet/marvell/mvneta.c
|
||||
+++ b/drivers/net/ethernet/marvell/mvneta.c
|
||||
@@ -4228,7 +4228,6 @@ static void mvneta_mac_link_up(struct ph
|
||||
}
|
||||
|
||||
static const struct phylink_mac_ops mvneta_phylink_ops = {
|
||||
- .validate = phylink_generic_validate,
|
||||
.mac_select_pcs = mvneta_mac_select_pcs,
|
||||
.mac_prepare = mvneta_mac_prepare,
|
||||
.mac_config = mvneta_mac_config,
|
||||
--- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
|
||||
+++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
|
||||
@@ -6633,7 +6633,6 @@ static void mvpp2_mac_link_down(struct p
|
||||
}
|
||||
|
||||
static const struct phylink_mac_ops mvpp2_phylink_ops = {
|
||||
- .validate = phylink_generic_validate,
|
||||
.mac_select_pcs = mvpp2_select_pcs,
|
||||
.mac_prepare = mvpp2_mac_prepare,
|
||||
.mac_config = mvpp2_mac_config,
|
||||
--- a/drivers/net/ethernet/marvell/prestera/prestera_main.c
|
||||
+++ b/drivers/net/ethernet/marvell/prestera/prestera_main.c
|
||||
@@ -360,7 +360,6 @@ static void prestera_pcs_an_restart(stru
|
||||
}
|
||||
|
||||
static const struct phylink_mac_ops prestera_mac_ops = {
|
||||
- .validate = phylink_generic_validate,
|
||||
.mac_select_pcs = prestera_mac_select_pcs,
|
||||
.mac_config = prestera_mac_config,
|
||||
.mac_link_down = prestera_mac_link_down,
|
||||
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
|
||||
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
|
||||
@@ -653,7 +653,6 @@ static void mtk_mac_link_up(struct phyli
|
||||
}
|
||||
|
||||
static const struct phylink_mac_ops mtk_phylink_ops = {
|
||||
- .validate = phylink_generic_validate,
|
||||
.mac_select_pcs = mtk_mac_select_pcs,
|
||||
.mac_pcs_get_state = mtk_mac_pcs_get_state,
|
||||
.mac_config = mtk_mac_config,
|
||||
--- a/drivers/net/ethernet/microchip/lan966x/lan966x_phylink.c
|
||||
+++ b/drivers/net/ethernet/microchip/lan966x/lan966x_phylink.c
|
||||
@@ -125,7 +125,6 @@ static void lan966x_pcs_aneg_restart(str
|
||||
}
|
||||
|
||||
const struct phylink_mac_ops lan966x_phylink_mac_ops = {
|
||||
- .validate = phylink_generic_validate,
|
||||
.mac_select_pcs = lan966x_phylink_mac_select,
|
||||
.mac_config = lan966x_phylink_mac_config,
|
||||
.mac_prepare = lan966x_phylink_mac_prepare,
|
||||
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_phylink.c
|
||||
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_phylink.c
|
||||
@@ -138,7 +138,6 @@ const struct phylink_pcs_ops sparx5_phyl
|
||||
};
|
||||
|
||||
const struct phylink_mac_ops sparx5_phylink_mac_ops = {
|
||||
- .validate = phylink_generic_validate,
|
||||
.mac_select_pcs = sparx5_phylink_mac_select_pcs,
|
||||
.mac_config = sparx5_phylink_mac_config,
|
||||
.mac_link_down = sparx5_phylink_mac_link_down,
|
||||
--- a/drivers/net/ethernet/mscc/ocelot_net.c
|
||||
+++ b/drivers/net/ethernet/mscc/ocelot_net.c
|
||||
@@ -1737,7 +1737,6 @@ static void vsc7514_phylink_mac_link_up(
|
||||
}
|
||||
|
||||
static const struct phylink_mac_ops ocelot_phylink_ops = {
|
||||
- .validate = phylink_generic_validate,
|
||||
.mac_config = vsc7514_phylink_mac_config,
|
||||
.mac_link_down = vsc7514_phylink_mac_link_down,
|
||||
.mac_link_up = vsc7514_phylink_mac_link_up,
|
||||
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
|
||||
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
|
||||
@@ -1090,7 +1090,6 @@ static void stmmac_mac_link_up(struct ph
|
||||
}
|
||||
|
||||
static const struct phylink_mac_ops stmmac_phylink_mac_ops = {
|
||||
- .validate = phylink_generic_validate,
|
||||
.mac_select_pcs = stmmac_mac_select_pcs,
|
||||
.mac_config = stmmac_mac_config,
|
||||
.mac_link_down = stmmac_mac_link_down,
|
||||
--- a/drivers/net/ethernet/ti/am65-cpsw-nuss.c
|
||||
+++ b/drivers/net/ethernet/ti/am65-cpsw-nuss.c
|
||||
@@ -1493,7 +1493,6 @@ static void am65_cpsw_nuss_mac_link_up(s
|
||||
}
|
||||
|
||||
static const struct phylink_mac_ops am65_cpsw_phylink_mac_ops = {
|
||||
- .validate = phylink_generic_validate,
|
||||
.mac_config = am65_cpsw_nuss_mac_config,
|
||||
.mac_link_down = am65_cpsw_nuss_mac_link_down,
|
||||
.mac_link_up = am65_cpsw_nuss_mac_link_up,
|
||||
--- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
|
||||
+++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
|
||||
@@ -1736,7 +1736,6 @@ static void axienet_mac_link_up(struct p
|
||||
}
|
||||
|
||||
static const struct phylink_mac_ops axienet_phylink_ops = {
|
||||
- .validate = phylink_generic_validate,
|
||||
.mac_select_pcs = axienet_mac_select_pcs,
|
||||
.mac_config = axienet_mac_config,
|
||||
.mac_link_down = axienet_mac_link_down,
|
||||
--- a/drivers/net/phy/phylink.c
|
||||
+++ b/drivers/net/phy/phylink.c
|
||||
@@ -651,7 +651,10 @@ static int phylink_validate_mac_and_pcs(
|
||||
}
|
||||
|
||||
/* Then validate the link parameters with the MAC */
|
||||
- pl->mac_ops->validate(pl->config, supported, state);
|
||||
+ if (pl->mac_ops->validate)
|
||||
+ pl->mac_ops->validate(pl->config, supported, state);
|
||||
+ else
|
||||
+ phylink_generic_validate(pl->config, supported, state);
|
||||
|
||||
return phylink_is_empty_linkmode(supported) ? -EINVAL : 0;
|
||||
}
|
||||
--- a/drivers/net/usb/asix_devices.c
|
||||
+++ b/drivers/net/usb/asix_devices.c
|
||||
@@ -787,7 +787,6 @@ static void ax88772_mac_link_up(struct p
|
||||
}
|
||||
|
||||
static const struct phylink_mac_ops ax88772_phylink_mac_ops = {
|
||||
- .validate = phylink_generic_validate,
|
||||
.mac_config = ax88772_mac_config,
|
||||
.mac_link_down = ax88772_mac_link_down,
|
||||
.mac_link_up = ax88772_mac_link_up,
|
||||
--- a/include/linux/phylink.h
|
||||
+++ b/include/linux/phylink.h
|
||||
@@ -207,6 +207,11 @@ struct phylink_mac_ops {
|
||||
*
|
||||
* If the @state->interface mode is not supported, then the @supported
|
||||
* mask must be cleared.
|
||||
+ *
|
||||
+ * This member is optional; if not set, the generic validator will be
|
||||
+ * used making use of @config->mac_capabilities and
|
||||
+ * @config->supported_interfaces to determine which link modes are
|
||||
+ * supported.
|
||||
*/
|
||||
void validate(struct phylink_config *config, unsigned long *supported,
|
||||
struct phylink_link_state *state);
|
@ -0,0 +1,48 @@
|
||||
From a90ac762d345890b40d88a1385a34a2449c2d75e Mon Sep 17 00:00:00 2001
|
||||
From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
|
||||
Date: Fri, 24 Mar 2023 09:23:42 +0000
|
||||
Subject: [PATCH] net: sfp: make sfp_bus_find_fwnode() take a const fwnode
|
||||
|
||||
sfp_bus_find_fwnode() does not write to the fwnode, so let's make it
|
||||
const.
|
||||
|
||||
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
|
||||
Reviewed-by: Simon Horman <simon.horman@corigine.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/sfp-bus.c | 2 +-
|
||||
include/linux/sfp.h | 5 +++--
|
||||
2 files changed, 4 insertions(+), 3 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/sfp-bus.c
|
||||
+++ b/drivers/net/phy/sfp-bus.c
|
||||
@@ -603,7 +603,7 @@ static void sfp_upstream_clear(struct sf
|
||||
* - %-ENOMEM if we failed to allocate the bus.
|
||||
* - an error from the upstream's connect_phy() method.
|
||||
*/
|
||||
-struct sfp_bus *sfp_bus_find_fwnode(struct fwnode_handle *fwnode)
|
||||
+struct sfp_bus *sfp_bus_find_fwnode(const struct fwnode_handle *fwnode)
|
||||
{
|
||||
struct fwnode_reference_args ref;
|
||||
struct sfp_bus *bus;
|
||||
--- a/include/linux/sfp.h
|
||||
+++ b/include/linux/sfp.h
|
||||
@@ -548,7 +548,7 @@ int sfp_get_module_eeprom_by_page(struct
|
||||
void sfp_upstream_start(struct sfp_bus *bus);
|
||||
void sfp_upstream_stop(struct sfp_bus *bus);
|
||||
void sfp_bus_put(struct sfp_bus *bus);
|
||||
-struct sfp_bus *sfp_bus_find_fwnode(struct fwnode_handle *fwnode);
|
||||
+struct sfp_bus *sfp_bus_find_fwnode(const struct fwnode_handle *fwnode);
|
||||
int sfp_bus_add_upstream(struct sfp_bus *bus, void *upstream,
|
||||
const struct sfp_upstream_ops *ops);
|
||||
void sfp_bus_del_upstream(struct sfp_bus *bus);
|
||||
@@ -610,7 +610,8 @@ static inline void sfp_bus_put(struct sf
|
||||
{
|
||||
}
|
||||
|
||||
-static inline struct sfp_bus *sfp_bus_find_fwnode(struct fwnode_handle *fwnode)
|
||||
+static inline struct sfp_bus *
|
||||
+sfp_bus_find_fwnode(const struct fwnode_handle *fwnode)
|
||||
{
|
||||
return NULL;
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
From ecec0ebbc6381a5a375f1cf10c4858f24e91e2ef Mon Sep 17 00:00:00 2001
|
||||
From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
|
||||
Date: Wed, 15 Mar 2023 14:46:49 +0000
|
||||
Subject: [PATCH] net: pcs: lynx: don't print an_enabled in pcs_get_state()
|
||||
|
||||
an_enabled will be going away, and in any case, pcs_get_state() should
|
||||
not be updating this member. Remove the print.
|
||||
|
||||
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
|
||||
Reviewed-by: Steen Hegelund <Steen.Hegelund@microchip.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/pcs/pcs-lynx.c | 4 ++--
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/drivers/net/pcs/pcs-lynx.c
|
||||
+++ b/drivers/net/pcs/pcs-lynx.c
|
||||
@@ -115,11 +115,11 @@ static void lynx_pcs_get_state(struct ph
|
||||
}
|
||||
|
||||
dev_dbg(&lynx->mdio->dev,
|
||||
- "mode=%s/%s/%s link=%u an_enabled=%u an_complete=%u\n",
|
||||
+ "mode=%s/%s/%s link=%u an_complete=%u\n",
|
||||
phy_modes(state->interface),
|
||||
phy_speed_to_str(state->speed),
|
||||
phy_duplex_to_str(state->duplex),
|
||||
- state->link, state->an_enabled, state->an_complete);
|
||||
+ state->link, state->an_complete);
|
||||
}
|
||||
|
||||
static int lynx_pcs_config_giga(struct mdio_device *pcs, unsigned int mode,
|
@ -0,0 +1,32 @@
|
||||
From 99d0f3a1095f4c938b1665025c29411edafe8a01 Mon Sep 17 00:00:00 2001
|
||||
From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
|
||||
Date: Tue, 21 Mar 2023 15:58:44 +0000
|
||||
Subject: [PATCH] net: dpaa2-mac: use Autoneg bit rather than an_enabled
|
||||
|
||||
The Autoneg bit in the advertising bitmap and state->an_enabled are
|
||||
always identical. Thus, we will be removing state->an_enabled.
|
||||
|
||||
Use the Autoneg bit in the advertising bitmap to indicate whether
|
||||
autonegotiation should be used, rather than using the an_enabled
|
||||
member which will be going away. This means we use the same condition
|
||||
as phylink_mii_c22_pcs_config().
|
||||
|
||||
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
|
||||
Reviewed-by: Simon Horman <simon.horman@corigine.com>
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
---
|
||||
drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c | 3 ++-
|
||||
1 file changed, 2 insertions(+), 1 deletion(-)
|
||||
|
||||
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c
|
||||
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c
|
||||
@@ -158,7 +158,8 @@ static void dpaa2_mac_config(struct phyl
|
||||
struct dpmac_link_state *dpmac_state = &mac->state;
|
||||
int err;
|
||||
|
||||
- if (state->an_enabled)
|
||||
+ if (linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
|
||||
+ state->advertising))
|
||||
dpmac_state->options |= DPMAC_LINK_OPT_AUTONEG;
|
||||
else
|
||||
dpmac_state->options &= ~DPMAC_LINK_OPT_AUTONEG;
|
@ -0,0 +1,64 @@
|
||||
From 471c40bde606ec0b1ce8c616f7998739c7a783a6 Mon Sep 17 00:00:00 2001
|
||||
From: Ivan Bornyakov <i.bornyakov@metrotek.ru>
|
||||
Date: Fri, 10 Feb 2023 18:46:27 +0300
|
||||
Subject: [PATCH 10/21] net: phylink: support validated pause and autoneg in
|
||||
fixed-link
|
||||
|
||||
In fixed-link setup phylink_parse_fixedlink() unconditionally sets
|
||||
Pause, Asym_Pause and Autoneg bits to "supported" bitmap, while MAC may
|
||||
not support these.
|
||||
|
||||
This leads to ethtool reporting:
|
||||
|
||||
> Supported pause frame use: Symmetric Receive-only
|
||||
> Supports auto-negotiation: Yes
|
||||
|
||||
regardless of what is actually supported.
|
||||
|
||||
Instead of unconditionally set Pause, Asym_Pause and Autoneg it is
|
||||
sensible to set them according to validated "supported" bitmap, i.e. the
|
||||
result of phylink_validate().
|
||||
|
||||
Signed-off-by: Ivan Bornyakov <i.bornyakov@metrotek.ru>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/phylink.c | 17 ++++++++++++++---
|
||||
1 file changed, 14 insertions(+), 3 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/phylink.c
|
||||
+++ b/drivers/net/phy/phylink.c
|
||||
@@ -709,6 +709,7 @@ static int phylink_parse_fixedlink(struc
|
||||
struct fwnode_handle *fwnode)
|
||||
{
|
||||
struct fwnode_handle *fixed_node;
|
||||
+ bool pause, asym_pause, autoneg;
|
||||
const struct phy_setting *s;
|
||||
struct gpio_desc *desc;
|
||||
u32 speed;
|
||||
@@ -781,13 +782,23 @@ static int phylink_parse_fixedlink(struc
|
||||
linkmode_copy(pl->link_config.advertising, pl->supported);
|
||||
phylink_validate(pl, pl->supported, &pl->link_config);
|
||||
|
||||
+ pause = phylink_test(pl->supported, Pause);
|
||||
+ asym_pause = phylink_test(pl->supported, Asym_Pause);
|
||||
+ autoneg = phylink_test(pl->supported, Autoneg);
|
||||
s = phy_lookup_setting(pl->link_config.speed, pl->link_config.duplex,
|
||||
pl->supported, true);
|
||||
linkmode_zero(pl->supported);
|
||||
phylink_set(pl->supported, MII);
|
||||
- phylink_set(pl->supported, Pause);
|
||||
- phylink_set(pl->supported, Asym_Pause);
|
||||
- phylink_set(pl->supported, Autoneg);
|
||||
+
|
||||
+ if (pause)
|
||||
+ phylink_set(pl->supported, Pause);
|
||||
+
|
||||
+ if (asym_pause)
|
||||
+ phylink_set(pl->supported, Asym_Pause);
|
||||
+
|
||||
+ if (autoneg)
|
||||
+ phylink_set(pl->supported, Autoneg);
|
||||
+
|
||||
if (s) {
|
||||
__set_bit(s->bit, pl->supported);
|
||||
__set_bit(s->bit, pl->link_config.lp_advertising);
|
@ -0,0 +1,177 @@
|
||||
From 7211ffd70941933a7825a56cf480f07ee81c321c Mon Sep 17 00:00:00 2001
|
||||
From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
|
||||
Date: Tue, 21 Mar 2023 15:58:54 +0000
|
||||
Subject: [PATCH 11/21] net: phylink: remove an_enabled
|
||||
|
||||
The Autoneg bit in the advertising bitmap and state->an_enabled are
|
||||
always identical. state->an_enabled is now no longer used by any
|
||||
drivers, so lets kill this duplication.
|
||||
|
||||
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
|
||||
Reviewed-by: Simon Horman <simon.horman@corigine.com>
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
---
|
||||
drivers/net/phy/phylink.c | 37 +++++++++++++++++--------------------
|
||||
include/linux/phylink.h | 2 --
|
||||
2 files changed, 17 insertions(+), 22 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/phylink.c
|
||||
+++ b/drivers/net/phy/phylink.c
|
||||
@@ -841,7 +841,6 @@ static int phylink_parse_mode(struct phy
|
||||
phylink_set(pl->supported, Autoneg);
|
||||
phylink_set(pl->supported, Asym_Pause);
|
||||
phylink_set(pl->supported, Pause);
|
||||
- pl->link_config.an_enabled = true;
|
||||
pl->cfg_link_an_mode = MLO_AN_INBAND;
|
||||
|
||||
switch (pl->link_config.interface) {
|
||||
@@ -944,9 +943,6 @@ static int phylink_parse_mode(struct phy
|
||||
"failed to validate link configuration for in-band status\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
-
|
||||
- /* Check if MAC/PCS also supports Autoneg. */
|
||||
- pl->link_config.an_enabled = phylink_test(pl->supported, Autoneg);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -956,7 +952,8 @@ static void phylink_apply_manual_flow(st
|
||||
struct phylink_link_state *state)
|
||||
{
|
||||
/* If autoneg is disabled, pause AN is also disabled */
|
||||
- if (!state->an_enabled)
|
||||
+ if (!linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
|
||||
+ state->advertising))
|
||||
state->pause &= ~MLO_PAUSE_AN;
|
||||
|
||||
/* Manual configuration of pause modes */
|
||||
@@ -996,21 +993,22 @@ static void phylink_mac_config(struct ph
|
||||
const struct phylink_link_state *state)
|
||||
{
|
||||
phylink_dbg(pl,
|
||||
- "%s: mode=%s/%s/%s/%s/%s adv=%*pb pause=%02x link=%u an=%u\n",
|
||||
+ "%s: mode=%s/%s/%s/%s/%s adv=%*pb pause=%02x link=%u\n",
|
||||
__func__, phylink_an_mode_str(pl->cur_link_an_mode),
|
||||
phy_modes(state->interface),
|
||||
phy_speed_to_str(state->speed),
|
||||
phy_duplex_to_str(state->duplex),
|
||||
phy_rate_matching_to_str(state->rate_matching),
|
||||
__ETHTOOL_LINK_MODE_MASK_NBITS, state->advertising,
|
||||
- state->pause, state->link, state->an_enabled);
|
||||
+ state->pause, state->link);
|
||||
|
||||
pl->mac_ops->mac_config(pl->config, pl->cur_link_an_mode, state);
|
||||
}
|
||||
|
||||
static void phylink_mac_pcs_an_restart(struct phylink *pl)
|
||||
{
|
||||
- if (pl->link_config.an_enabled &&
|
||||
+ if (linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
|
||||
+ pl->link_config.advertising) &&
|
||||
phy_interface_mode_is_8023z(pl->link_config.interface) &&
|
||||
phylink_autoneg_inband(pl->cur_link_an_mode)) {
|
||||
if (pl->pcs)
|
||||
@@ -1137,9 +1135,9 @@ static void phylink_mac_pcs_get_state(st
|
||||
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->rate_matching = pl->link_config.rate_matching;
|
||||
- if (state->an_enabled) {
|
||||
+ if (linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
|
||||
+ state->advertising)) {
|
||||
state->speed = SPEED_UNKNOWN;
|
||||
state->duplex = DUPLEX_UNKNOWN;
|
||||
state->pause = MLO_PAUSE_NONE;
|
||||
@@ -1531,7 +1529,6 @@ struct phylink *phylink_create(struct ph
|
||||
pl->link_config.pause = MLO_PAUSE_AN;
|
||||
pl->link_config.speed = SPEED_UNKNOWN;
|
||||
pl->link_config.duplex = DUPLEX_UNKNOWN;
|
||||
- pl->link_config.an_enabled = true;
|
||||
pl->mac_ops = mac_ops;
|
||||
__set_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state);
|
||||
timer_setup(&pl->link_poll, phylink_fixed_poll, 0);
|
||||
@@ -2155,8 +2152,9 @@ static void phylink_get_ksettings(const
|
||||
kset->base.speed = state->speed;
|
||||
kset->base.duplex = state->duplex;
|
||||
}
|
||||
- kset->base.autoneg = state->an_enabled ? AUTONEG_ENABLE :
|
||||
- AUTONEG_DISABLE;
|
||||
+ kset->base.autoneg = linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
|
||||
+ state->advertising) ?
|
||||
+ AUTONEG_ENABLE : AUTONEG_DISABLE;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2303,9 +2301,8 @@ int phylink_ethtool_ksettings_set(struct
|
||||
/* We have ruled out the case with a PHY attached, and the
|
||||
* fixed-link cases. All that is left are in-band links.
|
||||
*/
|
||||
- config.an_enabled = kset->base.autoneg == AUTONEG_ENABLE;
|
||||
linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, config.advertising,
|
||||
- config.an_enabled);
|
||||
+ kset->base.autoneg == AUTONEG_ENABLE);
|
||||
|
||||
/* If this link is with an SFP, ensure that changes to advertised modes
|
||||
* also cause the associated interface to be selected such that the
|
||||
@@ -2339,13 +2336,14 @@ int phylink_ethtool_ksettings_set(struct
|
||||
}
|
||||
|
||||
/* If autonegotiation is enabled, we must have an advertisement */
|
||||
- if (config.an_enabled && phylink_is_empty_linkmode(config.advertising))
|
||||
+ if (linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
|
||||
+ config.advertising) &&
|
||||
+ phylink_is_empty_linkmode(config.advertising))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&pl->state_mutex);
|
||||
pl->link_config.speed = config.speed;
|
||||
pl->link_config.duplex = config.duplex;
|
||||
- pl->link_config.an_enabled = config.an_enabled;
|
||||
|
||||
if (pl->link_config.interface != config.interface) {
|
||||
/* The interface changed, e.g. 1000base-X <-> 2500base-X */
|
||||
@@ -2951,7 +2949,6 @@ static int phylink_sfp_config_phy(struct
|
||||
config.speed = SPEED_UNKNOWN;
|
||||
config.duplex = DUPLEX_UNKNOWN;
|
||||
config.pause = MLO_PAUSE_AN;
|
||||
- config.an_enabled = pl->link_config.an_enabled;
|
||||
|
||||
/* Ignore errors if we're expecting a PHY to attach later */
|
||||
ret = phylink_validate(pl, support, &config);
|
||||
@@ -3020,7 +3017,6 @@ static int phylink_sfp_config_optical(st
|
||||
config.speed = SPEED_UNKNOWN;
|
||||
config.duplex = DUPLEX_UNKNOWN;
|
||||
config.pause = MLO_PAUSE_AN;
|
||||
- config.an_enabled = true;
|
||||
|
||||
/* For all the interfaces that are supported, reduce the sfp_support
|
||||
* mask to only those link modes that can be supported.
|
||||
@@ -3354,7 +3350,8 @@ void phylink_mii_c22_pcs_decode_state(st
|
||||
/* If there is no link or autonegotiation is disabled, the LP advertisement
|
||||
* data is not meaningful, so don't go any further.
|
||||
*/
|
||||
- if (!state->link || !state->an_enabled)
|
||||
+ if (!state->link || !linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
|
||||
+ state->advertising))
|
||||
return;
|
||||
|
||||
switch (state->interface) {
|
||||
--- a/include/linux/phylink.h
|
||||
+++ b/include/linux/phylink.h
|
||||
@@ -93,7 +93,6 @@ static inline bool phylink_autoneg_inban
|
||||
* the medium link mode (@speed and @duplex) and the speed/duplex of the phy
|
||||
* interface mode (@interface) are different.
|
||||
* @link: true if the link is up.
|
||||
- * @an_enabled: true if autonegotiation is enabled/desired.
|
||||
* @an_complete: true if autonegotiation has completed.
|
||||
*/
|
||||
struct phylink_link_state {
|
||||
@@ -105,7 +104,6 @@ struct phylink_link_state {
|
||||
int pause;
|
||||
int rate_matching;
|
||||
unsigned int link:1;
|
||||
- unsigned int an_enabled:1;
|
||||
unsigned int an_complete:1;
|
||||
};
|
||||
|
@ -0,0 +1,88 @@
|
||||
From a3555d1f5c208f0a63eafee77381f68d304a0512 Mon Sep 17 00:00:00 2001
|
||||
From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
|
||||
Date: Fri, 12 May 2023 17:58:37 +0100
|
||||
Subject: [PATCH 12/21] net: phylink: constify fwnode arguments
|
||||
|
||||
Both phylink_create() and phylink_fwnode_phy_connect() do not modify
|
||||
the fwnode argument that they are passed, so lets constify these.
|
||||
|
||||
Reviewed-by: Simon Horman <simon.horman@corigine.com>
|
||||
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/phylink.c | 11 ++++++-----
|
||||
include/linux/phylink.h | 9 +++++----
|
||||
2 files changed, 11 insertions(+), 9 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/phylink.c
|
||||
+++ b/drivers/net/phy/phylink.c
|
||||
@@ -706,7 +706,7 @@ static int phylink_validate(struct phyli
|
||||
}
|
||||
|
||||
static int phylink_parse_fixedlink(struct phylink *pl,
|
||||
- struct fwnode_handle *fwnode)
|
||||
+ const struct fwnode_handle *fwnode)
|
||||
{
|
||||
struct fwnode_handle *fixed_node;
|
||||
bool pause, asym_pause, autoneg;
|
||||
@@ -817,7 +817,8 @@ static int phylink_parse_fixedlink(struc
|
||||
return 0;
|
||||
}
|
||||
|
||||
-static int phylink_parse_mode(struct phylink *pl, struct fwnode_handle *fwnode)
|
||||
+static int phylink_parse_mode(struct phylink *pl,
|
||||
+ const struct fwnode_handle *fwnode)
|
||||
{
|
||||
struct fwnode_handle *dn;
|
||||
const char *managed;
|
||||
@@ -1440,7 +1441,7 @@ static void phylink_fixed_poll(struct ti
|
||||
static const struct sfp_upstream_ops sfp_phylink_ops;
|
||||
|
||||
static int phylink_register_sfp(struct phylink *pl,
|
||||
- struct fwnode_handle *fwnode)
|
||||
+ const struct fwnode_handle *fwnode)
|
||||
{
|
||||
struct sfp_bus *bus;
|
||||
int ret;
|
||||
@@ -1479,7 +1480,7 @@ static int phylink_register_sfp(struct p
|
||||
* must use IS_ERR() to check for errors from this function.
|
||||
*/
|
||||
struct phylink *phylink_create(struct phylink_config *config,
|
||||
- struct fwnode_handle *fwnode,
|
||||
+ const struct fwnode_handle *fwnode,
|
||||
phy_interface_t iface,
|
||||
const struct phylink_mac_ops *mac_ops)
|
||||
{
|
||||
@@ -1809,7 +1810,7 @@ EXPORT_SYMBOL_GPL(phylink_of_phy_connect
|
||||
* Returns 0 on success or a negative errno.
|
||||
*/
|
||||
int phylink_fwnode_phy_connect(struct phylink *pl,
|
||||
- struct fwnode_handle *fwnode,
|
||||
+ const struct fwnode_handle *fwnode,
|
||||
u32 flags)
|
||||
{
|
||||
struct fwnode_handle *phy_fwnode;
|
||||
--- a/include/linux/phylink.h
|
||||
+++ b/include/linux/phylink.h
|
||||
@@ -568,16 +568,17 @@ void phylink_generic_validate(struct phy
|
||||
unsigned long *supported,
|
||||
struct phylink_link_state *state);
|
||||
|
||||
-struct phylink *phylink_create(struct phylink_config *, struct fwnode_handle *,
|
||||
- phy_interface_t iface,
|
||||
- const struct phylink_mac_ops *mac_ops);
|
||||
+struct phylink *phylink_create(struct phylink_config *,
|
||||
+ const struct fwnode_handle *,
|
||||
+ phy_interface_t,
|
||||
+ const struct phylink_mac_ops *);
|
||||
void phylink_destroy(struct phylink *);
|
||||
bool phylink_expects_phy(struct phylink *pl);
|
||||
|
||||
int phylink_connect_phy(struct phylink *, struct phy_device *);
|
||||
int phylink_of_phy_connect(struct phylink *, struct device_node *, u32 flags);
|
||||
int phylink_fwnode_phy_connect(struct phylink *pl,
|
||||
- struct fwnode_handle *fwnode,
|
||||
+ const struct fwnode_handle *fwnode,
|
||||
u32 flags);
|
||||
void phylink_disconnect_phy(struct phylink *);
|
||||
|
@ -0,0 +1,38 @@
|
||||
From 4a0faa02d419a6728abef0f1d8a32d8c35ef95e6 Mon Sep 17 00:00:00 2001
|
||||
From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
|
||||
Date: Fri, 24 Mar 2023 09:23:53 +0000
|
||||
Subject: [PATCH] net: phy: constify fwnode_get_phy_node() fwnode argument
|
||||
|
||||
fwnode_get_phy_node() does not motify the fwnode structure, so make
|
||||
the argument const,
|
||||
|
||||
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
|
||||
Reviewed-by: Simon Horman <simon.horman@corigine.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/phy_device.c | 2 +-
|
||||
include/linux/phy.h | 2 +-
|
||||
2 files changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/phy_device.c
|
||||
+++ b/drivers/net/phy/phy_device.c
|
||||
@@ -3003,7 +3003,7 @@ EXPORT_SYMBOL_GPL(device_phy_find_device
|
||||
* and "phy-device" are not supported in ACPI. DT supports all the three
|
||||
* named references to the phy node.
|
||||
*/
|
||||
-struct fwnode_handle *fwnode_get_phy_node(struct fwnode_handle *fwnode)
|
||||
+struct fwnode_handle *fwnode_get_phy_node(const struct fwnode_handle *fwnode)
|
||||
{
|
||||
struct fwnode_handle *phy_node;
|
||||
|
||||
--- a/include/linux/phy.h
|
||||
+++ b/include/linux/phy.h
|
||||
@@ -1473,7 +1473,7 @@ int fwnode_get_phy_id(struct fwnode_hand
|
||||
struct mdio_device *fwnode_mdio_find_device(struct fwnode_handle *fwnode);
|
||||
struct phy_device *fwnode_phy_find_device(struct fwnode_handle *phy_fwnode);
|
||||
struct phy_device *device_phy_find_device(struct device *dev);
|
||||
-struct fwnode_handle *fwnode_get_phy_node(struct fwnode_handle *fwnode);
|
||||
+struct fwnode_handle *fwnode_get_phy_node(const struct fwnode_handle *fwnode);
|
||||
struct phy_device *get_phy_device(struct mii_bus *bus, int addr, bool is_c45);
|
||||
int phy_device_register(struct phy_device *phy);
|
||||
void phy_device_free(struct phy_device *phydev);
|
@ -0,0 +1,44 @@
|
||||
From cc73de0411f7d3cdd157564a78f7a39058420ff8 Mon Sep 17 00:00:00 2001
|
||||
From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
|
||||
Date: Sat, 13 May 2023 22:03:45 +0100
|
||||
Subject: [PATCH 13/21] net: phylink: fix ksettings_set() ethtool call
|
||||
|
||||
While testing a Fiberstore SFP-10G-T module (which uses 10GBASE-R with
|
||||
rate adaption) in a Clearfog platform (which can't do that) it was
|
||||
found that the PHYs advertisement was not limited according to the
|
||||
hosts capabilities when using ethtool to change it.
|
||||
|
||||
Fix this by ensuring that we mask the advertisement with the computed
|
||||
support mask as the very first thing we do.
|
||||
|
||||
Fixes: cbc1bb1e4689 ("net: phylink: simplify phy case for ksettings_set method")
|
||||
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/phylink.c | 8 ++++----
|
||||
1 file changed, 4 insertions(+), 4 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/phylink.c
|
||||
+++ b/drivers/net/phy/phylink.c
|
||||
@@ -2226,6 +2226,10 @@ int phylink_ethtool_ksettings_set(struct
|
||||
|
||||
ASSERT_RTNL();
|
||||
|
||||
+ /* Mask out unsupported advertisements */
|
||||
+ linkmode_and(config.advertising, kset->link_modes.advertising,
|
||||
+ pl->supported);
|
||||
+
|
||||
if (pl->phydev) {
|
||||
/* We can rely on phylib for this update; we also do not need
|
||||
* to update the pl->link_config settings:
|
||||
@@ -2250,10 +2254,6 @@ int phylink_ethtool_ksettings_set(struct
|
||||
|
||||
config = pl->link_config;
|
||||
|
||||
- /* Mask out unsupported advertisements */
|
||||
- linkmode_and(config.advertising, kset->link_modes.advertising,
|
||||
- pl->supported);
|
||||
-
|
||||
/* FIXME: should we reject autoneg if phy/mac does not support it? */
|
||||
switch (kset->base.autoneg) {
|
||||
case AUTONEG_DISABLE:
|
@ -0,0 +1,149 @@
|
||||
From 0100d1c5789018ba77bf2f4fab3bd91ecece7b3b Mon Sep 17 00:00:00 2001
|
||||
From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
|
||||
Date: Wed, 17 May 2023 11:38:12 +0100
|
||||
Subject: [PATCH 14/21] net: sfp: add support for setting signalling rate
|
||||
|
||||
Add support to the SFP layer to allow phylink to set the signalling
|
||||
rate for a SFP module. The rate given will be in units of kilo-baud
|
||||
(1000 baud).
|
||||
|
||||
Reviewed-by: Simon Horman <simon.horman@corigine.com>
|
||||
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
---
|
||||
drivers/net/phy/phylink.c | 24 ++++++++++++++++++++++++
|
||||
drivers/net/phy/sfp-bus.c | 20 ++++++++++++++++++++
|
||||
drivers/net/phy/sfp.c | 5 +++++
|
||||
drivers/net/phy/sfp.h | 1 +
|
||||
include/linux/sfp.h | 6 ++++++
|
||||
5 files changed, 56 insertions(+)
|
||||
|
||||
--- a/drivers/net/phy/phylink.c
|
||||
+++ b/drivers/net/phy/phylink.c
|
||||
@@ -156,6 +156,23 @@ static const char *phylink_an_mode_str(u
|
||||
return mode < ARRAY_SIZE(modestr) ? modestr[mode] : "unknown";
|
||||
}
|
||||
|
||||
+static unsigned int phylink_interface_signal_rate(phy_interface_t interface)
|
||||
+{
|
||||
+ switch (interface) {
|
||||
+ case PHY_INTERFACE_MODE_SGMII:
|
||||
+ case PHY_INTERFACE_MODE_1000BASEX: /* 1.25Mbd */
|
||||
+ return 1250;
|
||||
+ case PHY_INTERFACE_MODE_2500BASEX: /* 3.125Mbd */
|
||||
+ return 3125;
|
||||
+ case PHY_INTERFACE_MODE_5GBASER: /* 5.15625Mbd */
|
||||
+ return 5156;
|
||||
+ case PHY_INTERFACE_MODE_10GBASER: /* 10.3125Mbd */
|
||||
+ return 10313;
|
||||
+ default:
|
||||
+ return 0;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
/**
|
||||
* phylink_interface_max_speed() - get the maximum speed of a phy interface
|
||||
* @interface: phy interface mode defined by &typedef phy_interface_t
|
||||
@@ -1024,6 +1041,7 @@ static void phylink_major_config(struct
|
||||
{
|
||||
struct phylink_pcs *pcs = NULL;
|
||||
bool pcs_changed = false;
|
||||
+ unsigned int rate_kbd;
|
||||
int err;
|
||||
|
||||
phylink_dbg(pl, "major config %s\n", phy_modes(state->interface));
|
||||
@@ -1083,6 +1101,12 @@ static void phylink_major_config(struct
|
||||
ERR_PTR(err));
|
||||
}
|
||||
|
||||
+ if (pl->sfp_bus) {
|
||||
+ rate_kbd = phylink_interface_signal_rate(state->interface);
|
||||
+ if (rate_kbd)
|
||||
+ sfp_upstream_set_signal_rate(pl->sfp_bus, rate_kbd);
|
||||
+ }
|
||||
+
|
||||
phylink_pcs_poll_start(pl);
|
||||
}
|
||||
|
||||
--- a/drivers/net/phy/sfp-bus.c
|
||||
+++ b/drivers/net/phy/sfp-bus.c
|
||||
@@ -586,6 +586,26 @@ static void sfp_upstream_clear(struct sf
|
||||
}
|
||||
|
||||
/**
|
||||
+ * sfp_upstream_set_signal_rate() - set data signalling rate
|
||||
+ * @bus: a pointer to the &struct sfp_bus structure for the sfp module
|
||||
+ * @rate_kbd: signalling rate in units of 1000 baud
|
||||
+ *
|
||||
+ * Configure the rate select settings on the SFP module for the signalling
|
||||
+ * rate (not the same as the data rate).
|
||||
+ *
|
||||
+ * Locks that may be held:
|
||||
+ * Phylink's state_mutex
|
||||
+ * rtnl lock
|
||||
+ * SFP's sm_mutex
|
||||
+ */
|
||||
+void sfp_upstream_set_signal_rate(struct sfp_bus *bus, unsigned int rate_kbd)
|
||||
+{
|
||||
+ if (bus->registered)
|
||||
+ bus->socket_ops->set_signal_rate(bus->sfp, rate_kbd);
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(sfp_upstream_set_signal_rate);
|
||||
+
|
||||
+/**
|
||||
* sfp_bus_find_fwnode() - parse and locate the SFP bus from fwnode
|
||||
* @fwnode: firmware node for the parent device (MAC or PHY)
|
||||
*
|
||||
--- a/drivers/net/phy/sfp.c
|
||||
+++ b/drivers/net/phy/sfp.c
|
||||
@@ -2473,6 +2473,10 @@ static void sfp_stop(struct sfp *sfp)
|
||||
sfp_sm_event(sfp, SFP_E_DEV_DOWN);
|
||||
}
|
||||
|
||||
+static void sfp_set_signal_rate(struct sfp *sfp, unsigned int rate_kbd)
|
||||
+{
|
||||
+}
|
||||
+
|
||||
static int sfp_module_info(struct sfp *sfp, struct ethtool_modinfo *modinfo)
|
||||
{
|
||||
/* locking... and check module is present */
|
||||
@@ -2551,6 +2555,7 @@ static const struct sfp_socket_ops sfp_m
|
||||
.detach = sfp_detach,
|
||||
.start = sfp_start,
|
||||
.stop = sfp_stop,
|
||||
+ .set_signal_rate = sfp_set_signal_rate,
|
||||
.module_info = sfp_module_info,
|
||||
.module_eeprom = sfp_module_eeprom,
|
||||
.module_eeprom_by_page = sfp_module_eeprom_by_page,
|
||||
--- a/drivers/net/phy/sfp.h
|
||||
+++ b/drivers/net/phy/sfp.h
|
||||
@@ -19,6 +19,7 @@ struct sfp_socket_ops {
|
||||
void (*detach)(struct sfp *sfp);
|
||||
void (*start)(struct sfp *sfp);
|
||||
void (*stop)(struct sfp *sfp);
|
||||
+ void (*set_signal_rate)(struct sfp *sfp, unsigned int rate_kbd);
|
||||
int (*module_info)(struct sfp *sfp, struct ethtool_modinfo *modinfo);
|
||||
int (*module_eeprom)(struct sfp *sfp, struct ethtool_eeprom *ee,
|
||||
u8 *data);
|
||||
--- a/include/linux/sfp.h
|
||||
+++ b/include/linux/sfp.h
|
||||
@@ -547,6 +547,7 @@ int sfp_get_module_eeprom_by_page(struct
|
||||
struct netlink_ext_ack *extack);
|
||||
void sfp_upstream_start(struct sfp_bus *bus);
|
||||
void sfp_upstream_stop(struct sfp_bus *bus);
|
||||
+void sfp_upstream_set_signal_rate(struct sfp_bus *bus, unsigned int rate_kbd);
|
||||
void sfp_bus_put(struct sfp_bus *bus);
|
||||
struct sfp_bus *sfp_bus_find_fwnode(const struct fwnode_handle *fwnode);
|
||||
int sfp_bus_add_upstream(struct sfp_bus *bus, void *upstream,
|
||||
@@ -606,6 +607,11 @@ static inline void sfp_upstream_stop(str
|
||||
{
|
||||
}
|
||||
|
||||
+static inline void sfp_upstream_set_signal_rate(struct sfp_bus *bus,
|
||||
+ unsigned int rate_kbd)
|
||||
+{
|
||||
+}
|
||||
+
|
||||
static inline void sfp_bus_put(struct sfp_bus *bus)
|
||||
{
|
||||
}
|
@ -0,0 +1,147 @@
|
||||
From b84acdb07222a701bfc6403b374249c86f97d18d Mon Sep 17 00:00:00 2001
|
||||
From: Russell King <rmk+kernel@armlinux.org.uk>
|
||||
Date: Fri, 19 May 2023 14:03:59 +0100
|
||||
Subject: [PATCH 15/21] net: phy: add helpers for comparing phy IDs
|
||||
|
||||
There are several places which open code comparing PHY IDs. Provide a
|
||||
couple of helpers to assist with this, using a slightly simpler test
|
||||
than the original:
|
||||
|
||||
- phy_id_compare() compares two arbitary PHY IDs and a mask of the
|
||||
significant bits in the ID.
|
||||
- phydev_id_compare() compares the bound phydev with the specified
|
||||
PHY ID, using the bound driver's mask.
|
||||
|
||||
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
|
||||
Reviewed-by: Simon Horman <simon.horman@corigine.com>
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/micrel.c | 6 +++---
|
||||
drivers/net/phy/phy_device.c | 16 +++++++---------
|
||||
drivers/net/phy/phylink.c | 4 ++--
|
||||
include/linux/phy.h | 28 ++++++++++++++++++++++++++++
|
||||
4 files changed, 40 insertions(+), 14 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/micrel.c
|
||||
+++ b/drivers/net/phy/micrel.c
|
||||
@@ -620,7 +620,7 @@ static int ksz8051_ksz8795_match_phy_dev
|
||||
{
|
||||
int ret;
|
||||
|
||||
- if ((phydev->phy_id & MICREL_PHY_ID_MASK) != PHY_ID_KSZ8051)
|
||||
+ if (!phy_id_compare(phydev->phy_id, PHY_ID_KSZ8051, MICREL_PHY_ID_MASK))
|
||||
return 0;
|
||||
|
||||
ret = phy_read(phydev, MII_BMSR);
|
||||
@@ -1455,7 +1455,7 @@ static int ksz9x31_cable_test_fault_leng
|
||||
*
|
||||
* distance to fault = (VCT_DATA - 22) * 4 / cable propagation velocity
|
||||
*/
|
||||
- if ((phydev->phy_id & MICREL_PHY_ID_MASK) == PHY_ID_KSZ9131)
|
||||
+ if (phydev_id_compare(phydev, PHY_ID_KSZ9131))
|
||||
dt = clamp(dt - 22, 0, 255);
|
||||
|
||||
return (dt * 400) / 10;
|
||||
@@ -1887,7 +1887,7 @@ static __always_inline int ksz886x_cable
|
||||
*/
|
||||
dt = FIELD_GET(data_mask, status);
|
||||
|
||||
- if ((phydev->phy_id & MICREL_PHY_ID_MASK) == PHY_ID_LAN8814)
|
||||
+ if (phydev_id_compare(phydev, PHY_ID_LAN8814))
|
||||
return ((dt - 22) * 800) / 10;
|
||||
else
|
||||
return (dt * 400) / 10;
|
||||
--- a/drivers/net/phy/phy_device.c
|
||||
+++ b/drivers/net/phy/phy_device.c
|
||||
@@ -422,8 +422,7 @@ int phy_unregister_fixup(const char *bus
|
||||
fixup = list_entry(pos, struct phy_fixup, list);
|
||||
|
||||
if ((!strcmp(fixup->bus_id, bus_id)) &&
|
||||
- ((fixup->phy_uid & phy_uid_mask) ==
|
||||
- (phy_uid & phy_uid_mask))) {
|
||||
+ phy_id_compare(fixup->phy_uid, phy_uid, phy_uid_mask)) {
|
||||
list_del(&fixup->list);
|
||||
kfree(fixup);
|
||||
ret = 0;
|
||||
@@ -459,8 +458,8 @@ static int phy_needs_fixup(struct phy_de
|
||||
if (strcmp(fixup->bus_id, PHY_ANY_ID) != 0)
|
||||
return 0;
|
||||
|
||||
- if ((fixup->phy_uid & fixup->phy_uid_mask) !=
|
||||
- (phydev->phy_id & fixup->phy_uid_mask))
|
||||
+ if (!phy_id_compare(phydev->phy_id, fixup->phy_uid,
|
||||
+ fixup->phy_uid_mask))
|
||||
if (fixup->phy_uid != PHY_ANY_UID)
|
||||
return 0;
|
||||
|
||||
@@ -507,15 +506,14 @@ static int phy_bus_match(struct device *
|
||||
if (phydev->c45_ids.device_ids[i] == 0xffffffff)
|
||||
continue;
|
||||
|
||||
- if ((phydrv->phy_id & phydrv->phy_id_mask) ==
|
||||
- (phydev->c45_ids.device_ids[i] &
|
||||
- phydrv->phy_id_mask))
|
||||
+ if (phy_id_compare(phydev->c45_ids.device_ids[i],
|
||||
+ phydrv->phy_id, phydrv->phy_id_mask))
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
} else {
|
||||
- return (phydrv->phy_id & phydrv->phy_id_mask) ==
|
||||
- (phydev->phy_id & phydrv->phy_id_mask);
|
||||
+ return phy_id_compare(phydev->phy_id, phydrv->phy_id,
|
||||
+ phydrv->phy_id_mask);
|
||||
}
|
||||
}
|
||||
|
||||
--- a/drivers/net/phy/phylink.c
|
||||
+++ b/drivers/net/phy/phylink.c
|
||||
@@ -3151,8 +3151,8 @@ static void phylink_sfp_link_up(void *up
|
||||
*/
|
||||
static bool phylink_phy_no_inband(struct phy_device *phy)
|
||||
{
|
||||
- return phy->is_c45 &&
|
||||
- (phy->c45_ids.device_ids[1] & 0xfffffff0) == 0xae025150;
|
||||
+ return phy->is_c45 && phy_id_compare(phy->c45_ids.device_ids[1],
|
||||
+ 0xae025150, 0xfffffff0);
|
||||
}
|
||||
|
||||
static int phylink_sfp_connect_phy(void *upstream, struct phy_device *phy)
|
||||
--- a/include/linux/phy.h
|
||||
+++ b/include/linux/phy.h
|
||||
@@ -993,6 +993,34 @@ struct phy_driver {
|
||||
#define PHY_ID_MATCH_MODEL(id) .phy_id = (id), .phy_id_mask = GENMASK(31, 4)
|
||||
#define PHY_ID_MATCH_VENDOR(id) .phy_id = (id), .phy_id_mask = GENMASK(31, 10)
|
||||
|
||||
+/**
|
||||
+ * phy_id_compare - compare @id1 with @id2 taking account of @mask
|
||||
+ * @id1: first PHY ID
|
||||
+ * @id2: second PHY ID
|
||||
+ * @mask: the PHY ID mask, set bits are significant in matching
|
||||
+ *
|
||||
+ * Return true if the bits from @id1 and @id2 specified by @mask match.
|
||||
+ * This uses an equivalent test to (@id & @mask) == (@phy_id & @mask).
|
||||
+ */
|
||||
+static inline bool phy_id_compare(u32 id1, u32 id2, u32 mask)
|
||||
+{
|
||||
+ return !((id1 ^ id2) & mask);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * phydev_id_compare - compare @id with the PHY's Clause 22 ID
|
||||
+ * @phydev: the PHY device
|
||||
+ * @id: the PHY ID to be matched
|
||||
+ *
|
||||
+ * Compare the @phydev clause 22 ID with the provided @id and return true or
|
||||
+ * false depending whether it matches, using the bound driver mask. The
|
||||
+ * @phydev must be bound to a driver.
|
||||
+ */
|
||||
+static inline bool phydev_id_compare(struct phy_device *phydev, u32 id)
|
||||
+{
|
||||
+ return phy_id_compare(id, phydev->phy_id, phydev->drv->phy_id_mask);
|
||||
+}
|
||||
+
|
||||
/* A Structure for boards to register fixups with the PHY Lib */
|
||||
struct phy_fixup {
|
||||
struct list_head list;
|
@ -0,0 +1,71 @@
|
||||
From 441e1e44301fc5762a06737f8ec04bf1ce3fb039 Mon Sep 17 00:00:00 2001
|
||||
From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
|
||||
Date: Sat, 20 May 2023 11:41:42 +0100
|
||||
Subject: [PATCH 16/21] net: phylink: require supported_interfaces to be filled
|
||||
|
||||
We have been requiring the supported_interfaces bitmap to be filled in
|
||||
by MAC drivers that have a mac_select_pcs() method. Now that all MAC
|
||||
drivers fill in the supported_interfaces bitmap, it is time to enforce
|
||||
this. We have already required supported_interfaces to be set in order
|
||||
for optical SFPs to be configured in commit f81fa96d8a6c ("net: phylink:
|
||||
use phy_interface_t bitmaps for optical modules").
|
||||
|
||||
Refuse phylink creation if supported_interfaces is empty, and remove
|
||||
code to deal with cases where this mask is empty.
|
||||
|
||||
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Link: https://lore.kernel.org/r/E1q0K1u-006EIP-ET@rmk-PC.armlinux.org.uk
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
---
|
||||
drivers/net/phy/phylink.c | 26 +++++++++++---------------
|
||||
1 file changed, 11 insertions(+), 15 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/phylink.c
|
||||
+++ b/drivers/net/phy/phylink.c
|
||||
@@ -710,14 +710,11 @@ static int phylink_validate(struct phyli
|
||||
{
|
||||
const unsigned long *interfaces = pl->config->supported_interfaces;
|
||||
|
||||
- if (!phy_interface_empty(interfaces)) {
|
||||
- if (state->interface == PHY_INTERFACE_MODE_NA)
|
||||
- return phylink_validate_mask(pl, supported, state,
|
||||
- interfaces);
|
||||
+ if (state->interface == PHY_INTERFACE_MODE_NA)
|
||||
+ return phylink_validate_mask(pl, supported, state, interfaces);
|
||||
|
||||
- if (!test_bit(state->interface, interfaces))
|
||||
- return -EINVAL;
|
||||
- }
|
||||
+ if (!test_bit(state->interface, interfaces))
|
||||
+ return -EINVAL;
|
||||
|
||||
return phylink_validate_mac_and_pcs(pl, supported, state);
|
||||
}
|
||||
@@ -1512,19 +1509,18 @@ struct phylink *phylink_create(struct ph
|
||||
struct phylink *pl;
|
||||
int ret;
|
||||
|
||||
- if (mac_ops->mac_select_pcs &&
|
||||
- mac_ops->mac_select_pcs(config, PHY_INTERFACE_MODE_NA) !=
|
||||
- ERR_PTR(-EOPNOTSUPP))
|
||||
- using_mac_select_pcs = true;
|
||||
-
|
||||
/* Validate the supplied configuration */
|
||||
- if (using_mac_select_pcs &&
|
||||
- phy_interface_empty(config->supported_interfaces)) {
|
||||
+ if (phy_interface_empty(config->supported_interfaces)) {
|
||||
dev_err(config->dev,
|
||||
- "phylink: error: empty supported_interfaces but mac_select_pcs() method present\n");
|
||||
+ "phylink: error: empty supported_interfaces\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
+ if (mac_ops->mac_select_pcs &&
|
||||
+ mac_ops->mac_select_pcs(config, PHY_INTERFACE_MODE_NA) !=
|
||||
+ ERR_PTR(-EOPNOTSUPP))
|
||||
+ using_mac_select_pcs = true;
|
||||
+
|
||||
pl = kzalloc(sizeof(*pl), GFP_KERNEL);
|
||||
if (!pl)
|
||||
return ERR_PTR(-ENOMEM);
|
@ -0,0 +1,64 @@
|
||||
From 4b624a39f2ab523ca6a6ad9448fab1deb7b101e2 Mon Sep 17 00:00:00 2001
|
||||
From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
|
||||
Date: Tue, 23 May 2023 11:15:53 +0100
|
||||
Subject: [PATCH 17/21] net: phylink: remove duplicated linkmode pause
|
||||
resolution
|
||||
|
||||
Phylink had two chunks of code virtually the same for resolving the
|
||||
negotiated pause modes. Factor this down to one function.
|
||||
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
---
|
||||
drivers/net/phy/phylink.c | 15 ++++-----------
|
||||
1 file changed, 4 insertions(+), 11 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/phylink.c
|
||||
+++ b/drivers/net/phy/phylink.c
|
||||
@@ -976,11 +976,10 @@ static void phylink_apply_manual_flow(st
|
||||
state->pause = pl->link_config.pause;
|
||||
}
|
||||
|
||||
-static void phylink_resolve_flow(struct phylink_link_state *state)
|
||||
+static void phylink_resolve_an_pause(struct phylink_link_state *state)
|
||||
{
|
||||
bool tx_pause, rx_pause;
|
||||
|
||||
- state->pause = MLO_PAUSE_NONE;
|
||||
if (state->duplex == DUPLEX_FULL) {
|
||||
linkmode_resolve_pause(state->advertising,
|
||||
state->lp_advertising,
|
||||
@@ -1192,7 +1191,8 @@ static void phylink_get_fixed_state(stru
|
||||
else if (pl->link_gpio)
|
||||
state->link = !!gpiod_get_value_cansleep(pl->link_gpio);
|
||||
|
||||
- phylink_resolve_flow(state);
|
||||
+ state->pause = MLO_PAUSE_NONE;
|
||||
+ phylink_resolve_an_pause(state);
|
||||
}
|
||||
|
||||
static void phylink_mac_initial_config(struct phylink *pl, bool force_restart)
|
||||
@@ -3215,7 +3215,6 @@ static const struct sfp_upstream_ops sfp
|
||||
static void phylink_decode_c37_word(struct phylink_link_state *state,
|
||||
uint16_t config_reg, int speed)
|
||||
{
|
||||
- bool tx_pause, rx_pause;
|
||||
int fd_bit;
|
||||
|
||||
if (speed == SPEED_2500)
|
||||
@@ -3234,13 +3233,7 @@ static void phylink_decode_c37_word(stru
|
||||
state->link = false;
|
||||
}
|
||||
|
||||
- linkmode_resolve_pause(state->advertising, state->lp_advertising,
|
||||
- &tx_pause, &rx_pause);
|
||||
-
|
||||
- if (tx_pause)
|
||||
- state->pause |= MLO_PAUSE_TX;
|
||||
- if (rx_pause)
|
||||
- state->pause |= MLO_PAUSE_RX;
|
||||
+ phylink_resolve_an_pause(state);
|
||||
}
|
||||
|
||||
static void phylink_decode_sgmii_word(struct phylink_link_state *state,
|
@ -0,0 +1,76 @@
|
||||
From aa8b6bd2b1f235b262bd27f317a0516f196c2c6a Mon Sep 17 00:00:00 2001
|
||||
From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
|
||||
Date: Tue, 23 May 2023 11:15:58 +0100
|
||||
Subject: [PATCH 18/21] net: phylink: add function to resolve clause 73
|
||||
negotiation
|
||||
|
||||
Add a function to resolve clause 73 negotiation according to the
|
||||
priority resolution function described in clause 73.3.6.
|
||||
|
||||
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
---
|
||||
drivers/net/phy/phylink.c | 39 +++++++++++++++++++++++++++++++++++++++
|
||||
include/linux/phylink.h | 2 ++
|
||||
2 files changed, 41 insertions(+)
|
||||
|
||||
--- a/drivers/net/phy/phylink.c
|
||||
+++ b/drivers/net/phy/phylink.c
|
||||
@@ -3212,6 +3212,45 @@ static const struct sfp_upstream_ops sfp
|
||||
|
||||
/* Helpers for MAC drivers */
|
||||
|
||||
+static struct {
|
||||
+ int bit;
|
||||
+ int speed;
|
||||
+} phylink_c73_priority_resolution[] = {
|
||||
+ { ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT, SPEED_100000 },
|
||||
+ { ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT, SPEED_100000 },
|
||||
+ /* 100GBASE-KP4 and 100GBASE-CR10 not supported */
|
||||
+ { ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT, SPEED_40000 },
|
||||
+ { ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT, SPEED_40000 },
|
||||
+ { ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, SPEED_10000 },
|
||||
+ { ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT, SPEED_10000 },
|
||||
+ /* 5GBASE-KR not supported */
|
||||
+ { ETHTOOL_LINK_MODE_2500baseX_Full_BIT, SPEED_2500 },
|
||||
+ { ETHTOOL_LINK_MODE_1000baseKX_Full_BIT, SPEED_1000 },
|
||||
+};
|
||||
+
|
||||
+void phylink_resolve_c73(struct phylink_link_state *state)
|
||||
+{
|
||||
+ int i;
|
||||
+
|
||||
+ for (i = 0; i < ARRAY_SIZE(phylink_c73_priority_resolution); i++) {
|
||||
+ int bit = phylink_c73_priority_resolution[i].bit;
|
||||
+ if (linkmode_test_bit(bit, state->advertising) &&
|
||||
+ linkmode_test_bit(bit, state->lp_advertising))
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ if (i < ARRAY_SIZE(phylink_c73_priority_resolution)) {
|
||||
+ state->speed = phylink_c73_priority_resolution[i].speed;
|
||||
+ state->duplex = DUPLEX_FULL;
|
||||
+ } else {
|
||||
+ /* negotiation failure */
|
||||
+ state->link = false;
|
||||
+ }
|
||||
+
|
||||
+ phylink_resolve_an_pause(state);
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(phylink_resolve_c73);
|
||||
+
|
||||
static void phylink_decode_c37_word(struct phylink_link_state *state,
|
||||
uint16_t config_reg, int speed)
|
||||
{
|
||||
--- a/include/linux/phylink.h
|
||||
+++ b/include/linux/phylink.h
|
||||
@@ -656,6 +656,8 @@ int phylink_mii_c22_pcs_config(struct md
|
||||
const unsigned long *advertising);
|
||||
void phylink_mii_c22_pcs_an_restart(struct mdio_device *pcs);
|
||||
|
||||
+void phylink_resolve_c73(struct phylink_link_state *state);
|
||||
+
|
||||
void phylink_mii_c45_pcs_get_state(struct mdio_device *pcs,
|
||||
struct phylink_link_state *state);
|
||||
|
@ -0,0 +1,100 @@
|
||||
From 796d709363135a6bd6a8ccc07b509c939e5b855f Mon Sep 17 00:00:00 2001
|
||||
From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
|
||||
Date: Tue, 23 May 2023 16:31:50 +0100
|
||||
Subject: [PATCH 19/21] net: phylink: provide phylink_pcs_config() and
|
||||
phylink_pcs_link_up()
|
||||
|
||||
Add two helper functions for calling PCS methods. phylink_pcs_config()
|
||||
allows us to handle PCS configuration specifics in one location, rather
|
||||
than the two call sites. phylink_pcs_link_up() gives us consistency.
|
||||
|
||||
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
|
||||
Link: https://lore.kernel.org/r/E1q1TzK-007Exd-Rs@rmk-PC.armlinux.org.uk
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
---
|
||||
drivers/net/phy/phylink.c | 53 ++++++++++++++++++++++++---------------
|
||||
1 file changed, 33 insertions(+), 20 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/phylink.c
|
||||
+++ b/drivers/net/phy/phylink.c
|
||||
@@ -991,6 +991,25 @@ static void phylink_resolve_an_pause(str
|
||||
}
|
||||
}
|
||||
|
||||
+static int phylink_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
|
||||
+ const struct phylink_link_state *state,
|
||||
+ bool permit_pause_to_mac)
|
||||
+{
|
||||
+ if (!pcs)
|
||||
+ return 0;
|
||||
+
|
||||
+ return pcs->ops->pcs_config(pcs, mode, state->interface,
|
||||
+ state->advertising, permit_pause_to_mac);
|
||||
+}
|
||||
+
|
||||
+static void phylink_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
|
||||
+ phy_interface_t interface, int speed,
|
||||
+ int duplex)
|
||||
+{
|
||||
+ if (pcs && pcs->ops->pcs_link_up)
|
||||
+ pcs->ops->pcs_link_up(pcs, mode, interface, speed, duplex);
|
||||
+}
|
||||
+
|
||||
static void phylink_pcs_poll_stop(struct phylink *pl)
|
||||
{
|
||||
if (pl->cfg_link_an_mode == MLO_AN_INBAND)
|
||||
@@ -1074,18 +1093,15 @@ static void phylink_major_config(struct
|
||||
|
||||
phylink_mac_config(pl, state);
|
||||
|
||||
- if (pl->pcs) {
|
||||
- err = pl->pcs->ops->pcs_config(pl->pcs, pl->cur_link_an_mode,
|
||||
- state->interface,
|
||||
- state->advertising,
|
||||
- !!(pl->link_config.pause &
|
||||
- MLO_PAUSE_AN));
|
||||
- if (err < 0)
|
||||
- phylink_err(pl, "pcs_config failed: %pe\n",
|
||||
- ERR_PTR(err));
|
||||
- if (err > 0)
|
||||
- restart = true;
|
||||
- }
|
||||
+ err = phylink_pcs_config(pl->pcs, pl->cur_link_an_mode, state,
|
||||
+ !!(pl->link_config.pause &
|
||||
+ MLO_PAUSE_AN));
|
||||
+ if (err < 0)
|
||||
+ phylink_err(pl, "pcs_config failed: %pe\n",
|
||||
+ ERR_PTR(err));
|
||||
+ else if (err > 0)
|
||||
+ restart = true;
|
||||
+
|
||||
if (restart)
|
||||
phylink_mac_pcs_an_restart(pl);
|
||||
|
||||
@@ -1136,11 +1152,9 @@ static int phylink_change_inband_advert(
|
||||
* restart negotiation if the pcs_config() helper indicates that
|
||||
* the programmed advertisement has changed.
|
||||
*/
|
||||
- ret = pl->pcs->ops->pcs_config(pl->pcs, pl->cur_link_an_mode,
|
||||
- pl->link_config.interface,
|
||||
- pl->link_config.advertising,
|
||||
- !!(pl->link_config.pause &
|
||||
- MLO_PAUSE_AN));
|
||||
+ ret = phylink_pcs_config(pl->pcs, pl->cur_link_an_mode,
|
||||
+ &pl->link_config,
|
||||
+ !!(pl->link_config.pause & MLO_PAUSE_AN));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@@ -1272,9 +1286,8 @@ static void phylink_link_up(struct phyli
|
||||
|
||||
pl->cur_interface = link_state.interface;
|
||||
|
||||
- if (pl->pcs && pl->pcs->ops->pcs_link_up)
|
||||
- pl->pcs->ops->pcs_link_up(pl->pcs, pl->cur_link_an_mode,
|
||||
- pl->cur_interface, speed, duplex);
|
||||
+ phylink_pcs_link_up(pl->pcs, pl->cur_link_an_mode, pl->cur_interface,
|
||||
+ speed, duplex);
|
||||
|
||||
pl->mac_ops->mac_link_up(pl->config, pl->phydev, pl->cur_link_an_mode,
|
||||
pl->cur_interface, speed, duplex,
|
@ -0,0 +1,55 @@
|
||||
From 11933aa76865621d8e82553c8f3bc07796a5aaa2 Mon Sep 17 00:00:00 2001
|
||||
From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
|
||||
Date: Thu, 1 Jun 2023 10:12:06 +0100
|
||||
Subject: [PATCH 20/21] net: phylink: actually fix ksettings_set() ethtool call
|
||||
|
||||
Raju Lakkaraju reported that the below commit caused a regression
|
||||
with Lan743x drivers and a 2.5G SFP. Sadly, this is because the commit
|
||||
was utterly wrong. Let's fix this properly by not moving the
|
||||
linkmode_and(), but instead copying the link ksettings and then
|
||||
modifying the advertising mask before passing the modified link
|
||||
ksettings to phylib.
|
||||
|
||||
Fixes: df0acdc59b09 ("net: phylink: fix ksettings_set() ethtool call")
|
||||
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
|
||||
Link: https://lore.kernel.org/r/E1q4eLm-00Ayxk-GZ@rmk-PC.armlinux.org.uk
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
---
|
||||
drivers/net/phy/phylink.c | 15 ++++++++++-----
|
||||
1 file changed, 10 insertions(+), 5 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/phylink.c
|
||||
+++ b/drivers/net/phy/phylink.c
|
||||
@@ -2259,11 +2259,13 @@ int phylink_ethtool_ksettings_set(struct
|
||||
|
||||
ASSERT_RTNL();
|
||||
|
||||
- /* Mask out unsupported advertisements */
|
||||
- linkmode_and(config.advertising, kset->link_modes.advertising,
|
||||
- pl->supported);
|
||||
-
|
||||
if (pl->phydev) {
|
||||
+ struct ethtool_link_ksettings phy_kset = *kset;
|
||||
+
|
||||
+ linkmode_and(phy_kset.link_modes.advertising,
|
||||
+ phy_kset.link_modes.advertising,
|
||||
+ pl->supported);
|
||||
+
|
||||
/* We can rely on phylib for this update; we also do not need
|
||||
* to update the pl->link_config settings:
|
||||
* - the configuration returned via ksettings_get() will come
|
||||
@@ -2282,10 +2284,13 @@ int phylink_ethtool_ksettings_set(struct
|
||||
* the presence of a PHY, this should not be changed as that
|
||||
* should be determined from the media side advertisement.
|
||||
*/
|
||||
- return phy_ethtool_ksettings_set(pl->phydev, kset);
|
||||
+ return phy_ethtool_ksettings_set(pl->phydev, &phy_kset);
|
||||
}
|
||||
|
||||
config = pl->link_config;
|
||||
+ /* Mask out unsupported advertisements */
|
||||
+ linkmode_and(config.advertising, kset->link_modes.advertising,
|
||||
+ pl->supported);
|
||||
|
||||
/* FIXME: should we reject autoneg if phy/mac does not support it? */
|
||||
switch (kset->base.autoneg) {
|
@ -0,0 +1,324 @@
|
||||
From 79b07c3e9c4a2272927be8848c26b372516e1958 Mon Sep 17 00:00:00 2001
|
||||
From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
|
||||
Date: Fri, 16 Jun 2023 13:06:22 +0100
|
||||
Subject: [PATCH 21/21] net: phylink: add PCS negotiation mode
|
||||
|
||||
PCS have to work out whether they should enable PCS negotiation by
|
||||
looking at the "mode" and "interface" arguments, and the Autoneg bit
|
||||
in the advertising mask.
|
||||
|
||||
This leads to some complex logic, so lets pull that out into phylink
|
||||
and instead pass a "neg_mode" argument to the PCS configuration and
|
||||
link up methods, instead of the "mode" argument.
|
||||
|
||||
In order to transition drivers, add a "neg_mode" flag to the phylink
|
||||
PCS structure to PCS can indicate whether they want to be passed the
|
||||
neg_mode or the old mode argument.
|
||||
|
||||
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
|
||||
Link: https://lore.kernel.org/r/E1qA8De-00EaFA-Ht@rmk-PC.armlinux.org.uk
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
---
|
||||
drivers/net/phy/phylink.c | 45 +++++++++++++----
|
||||
include/linux/phylink.h | 104 +++++++++++++++++++++++++++++++++++---
|
||||
2 files changed, 132 insertions(+), 17 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/phylink.c
|
||||
+++ b/drivers/net/phy/phylink.c
|
||||
@@ -71,6 +71,7 @@ struct phylink {
|
||||
struct mutex state_mutex;
|
||||
struct phylink_link_state phy_state;
|
||||
struct work_struct resolve;
|
||||
+ unsigned int pcs_neg_mode;
|
||||
|
||||
bool mac_link_dropped;
|
||||
bool using_mac_select_pcs;
|
||||
@@ -991,23 +992,23 @@ static void phylink_resolve_an_pause(str
|
||||
}
|
||||
}
|
||||
|
||||
-static int phylink_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
|
||||
+static int phylink_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
|
||||
const struct phylink_link_state *state,
|
||||
bool permit_pause_to_mac)
|
||||
{
|
||||
if (!pcs)
|
||||
return 0;
|
||||
|
||||
- return pcs->ops->pcs_config(pcs, mode, state->interface,
|
||||
+ return pcs->ops->pcs_config(pcs, neg_mode, state->interface,
|
||||
state->advertising, permit_pause_to_mac);
|
||||
}
|
||||
|
||||
-static void phylink_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
|
||||
+static void phylink_pcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode,
|
||||
phy_interface_t interface, int speed,
|
||||
int duplex)
|
||||
{
|
||||
if (pcs && pcs->ops->pcs_link_up)
|
||||
- pcs->ops->pcs_link_up(pcs, mode, interface, speed, duplex);
|
||||
+ pcs->ops->pcs_link_up(pcs, neg_mode, interface, speed, duplex);
|
||||
}
|
||||
|
||||
static void phylink_pcs_poll_stop(struct phylink *pl)
|
||||
@@ -1057,10 +1058,15 @@ static void phylink_major_config(struct
|
||||
struct phylink_pcs *pcs = NULL;
|
||||
bool pcs_changed = false;
|
||||
unsigned int rate_kbd;
|
||||
+ unsigned int neg_mode;
|
||||
int err;
|
||||
|
||||
phylink_dbg(pl, "major config %s\n", phy_modes(state->interface));
|
||||
|
||||
+ pl->pcs_neg_mode = phylink_pcs_neg_mode(pl->cur_link_an_mode,
|
||||
+ state->interface,
|
||||
+ state->advertising);
|
||||
+
|
||||
if (pl->using_mac_select_pcs) {
|
||||
pcs = pl->mac_ops->mac_select_pcs(pl->config, state->interface);
|
||||
if (IS_ERR(pcs)) {
|
||||
@@ -1093,9 +1099,12 @@ static void phylink_major_config(struct
|
||||
|
||||
phylink_mac_config(pl, state);
|
||||
|
||||
- err = phylink_pcs_config(pl->pcs, pl->cur_link_an_mode, state,
|
||||
- !!(pl->link_config.pause &
|
||||
- MLO_PAUSE_AN));
|
||||
+ neg_mode = pl->cur_link_an_mode;
|
||||
+ if (pl->pcs && pl->pcs->neg_mode)
|
||||
+ neg_mode = pl->pcs_neg_mode;
|
||||
+
|
||||
+ err = phylink_pcs_config(pl->pcs, neg_mode, state,
|
||||
+ !!(pl->link_config.pause & MLO_PAUSE_AN));
|
||||
if (err < 0)
|
||||
phylink_err(pl, "pcs_config failed: %pe\n",
|
||||
ERR_PTR(err));
|
||||
@@ -1130,6 +1139,7 @@ static void phylink_major_config(struct
|
||||
*/
|
||||
static int phylink_change_inband_advert(struct phylink *pl)
|
||||
{
|
||||
+ unsigned int neg_mode;
|
||||
int ret;
|
||||
|
||||
if (test_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state))
|
||||
@@ -1148,12 +1158,20 @@ static int phylink_change_inband_advert(
|
||||
__ETHTOOL_LINK_MODE_MASK_NBITS, pl->link_config.advertising,
|
||||
pl->link_config.pause);
|
||||
|
||||
+ /* Recompute the PCS neg mode */
|
||||
+ pl->pcs_neg_mode = phylink_pcs_neg_mode(pl->cur_link_an_mode,
|
||||
+ pl->link_config.interface,
|
||||
+ pl->link_config.advertising);
|
||||
+
|
||||
+ neg_mode = pl->cur_link_an_mode;
|
||||
+ if (pl->pcs->neg_mode)
|
||||
+ neg_mode = pl->pcs_neg_mode;
|
||||
+
|
||||
/* Modern PCS-based method; update the advert at the PCS, and
|
||||
* restart negotiation if the pcs_config() helper indicates that
|
||||
* the programmed advertisement has changed.
|
||||
*/
|
||||
- ret = phylink_pcs_config(pl->pcs, pl->cur_link_an_mode,
|
||||
- &pl->link_config,
|
||||
+ ret = phylink_pcs_config(pl->pcs, neg_mode, &pl->link_config,
|
||||
!!(pl->link_config.pause & MLO_PAUSE_AN));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
@@ -1256,6 +1274,7 @@ static void phylink_link_up(struct phyli
|
||||
struct phylink_link_state link_state)
|
||||
{
|
||||
struct net_device *ndev = pl->netdev;
|
||||
+ unsigned int neg_mode;
|
||||
int speed, duplex;
|
||||
bool rx_pause;
|
||||
|
||||
@@ -1286,8 +1305,12 @@ static void phylink_link_up(struct phyli
|
||||
|
||||
pl->cur_interface = link_state.interface;
|
||||
|
||||
- phylink_pcs_link_up(pl->pcs, pl->cur_link_an_mode, pl->cur_interface,
|
||||
- speed, duplex);
|
||||
+ neg_mode = pl->cur_link_an_mode;
|
||||
+ if (pl->pcs && pl->pcs->neg_mode)
|
||||
+ neg_mode = pl->pcs_neg_mode;
|
||||
+
|
||||
+ phylink_pcs_link_up(pl->pcs, neg_mode, pl->cur_interface, speed,
|
||||
+ duplex);
|
||||
|
||||
pl->mac_ops->mac_link_up(pl->config, pl->phydev, pl->cur_link_an_mode,
|
||||
pl->cur_interface, speed, duplex,
|
||||
--- a/include/linux/phylink.h
|
||||
+++ b/include/linux/phylink.h
|
||||
@@ -21,6 +21,24 @@ enum {
|
||||
MLO_AN_FIXED, /* Fixed-link mode */
|
||||
MLO_AN_INBAND, /* In-band protocol */
|
||||
|
||||
+ /* PCS "negotiation" mode.
|
||||
+ * PHYLINK_PCS_NEG_NONE - protocol has no inband capability
|
||||
+ * PHYLINK_PCS_NEG_OUTBAND - some out of band or fixed link setting
|
||||
+ * PHYLINK_PCS_NEG_INBAND_DISABLED - inband mode disabled, e.g.
|
||||
+ * 1000base-X with autoneg off
|
||||
+ * PHYLINK_PCS_NEG_INBAND_ENABLED - inband mode enabled
|
||||
+ * Additionally, this can be tested using bitmasks:
|
||||
+ * PHYLINK_PCS_NEG_INBAND - inband mode selected
|
||||
+ * PHYLINK_PCS_NEG_ENABLED - negotiation mode enabled
|
||||
+ */
|
||||
+ PHYLINK_PCS_NEG_NONE = 0,
|
||||
+ PHYLINK_PCS_NEG_ENABLED = BIT(4),
|
||||
+ PHYLINK_PCS_NEG_OUTBAND = BIT(5),
|
||||
+ PHYLINK_PCS_NEG_INBAND = BIT(6),
|
||||
+ PHYLINK_PCS_NEG_INBAND_DISABLED = PHYLINK_PCS_NEG_INBAND,
|
||||
+ PHYLINK_PCS_NEG_INBAND_ENABLED = PHYLINK_PCS_NEG_INBAND |
|
||||
+ PHYLINK_PCS_NEG_ENABLED,
|
||||
+
|
||||
/* MAC_SYM_PAUSE and MAC_ASYM_PAUSE are used when configuring our
|
||||
* autonegotiation advertisement. They correspond to the PAUSE and
|
||||
* ASM_DIR bits defined by 802.3, respectively.
|
||||
@@ -80,6 +98,70 @@ static inline bool phylink_autoneg_inban
|
||||
}
|
||||
|
||||
/**
|
||||
+ * phylink_pcs_neg_mode() - helper to determine PCS inband mode
|
||||
+ * @mode: one of %MLO_AN_FIXED, %MLO_AN_PHY, %MLO_AN_INBAND.
|
||||
+ * @interface: interface mode to be used
|
||||
+ * @advertising: adertisement ethtool link mode mask
|
||||
+ *
|
||||
+ * Determines the negotiation mode to be used by the PCS, and returns
|
||||
+ * one of:
|
||||
+ * %PHYLINK_PCS_NEG_NONE: interface mode does not support inband
|
||||
+ * %PHYLINK_PCS_NEG_OUTBAND: an out of band mode (e.g. reading the PHY)
|
||||
+ * will be used.
|
||||
+ * %PHYLINK_PCS_NEG_INBAND_DISABLED: inband mode selected but autoneg disabled
|
||||
+ * %PHYLINK_PCS_NEG_INBAND_ENABLED: inband mode selected and autoneg enabled
|
||||
+ *
|
||||
+ * Note: this is for cases where the PCS itself is involved in negotiation
|
||||
+ * (e.g. Clause 37, SGMII and similar) not Clause 73.
|
||||
+ */
|
||||
+static inline unsigned int phylink_pcs_neg_mode(unsigned int mode,
|
||||
+ phy_interface_t interface,
|
||||
+ const unsigned long *advertising)
|
||||
+{
|
||||
+ unsigned int neg_mode;
|
||||
+
|
||||
+ switch (interface) {
|
||||
+ case PHY_INTERFACE_MODE_SGMII:
|
||||
+ case PHY_INTERFACE_MODE_QSGMII:
|
||||
+ case PHY_INTERFACE_MODE_QUSGMII:
|
||||
+ case PHY_INTERFACE_MODE_USXGMII:
|
||||
+ /* These protocols are designed for use with a PHY which
|
||||
+ * communicates its negotiation result back to the MAC via
|
||||
+ * inband communication. Note: there exist PHYs that run
|
||||
+ * with SGMII but do not send the inband data.
|
||||
+ */
|
||||
+ if (!phylink_autoneg_inband(mode))
|
||||
+ neg_mode = PHYLINK_PCS_NEG_OUTBAND;
|
||||
+ else
|
||||
+ neg_mode = PHYLINK_PCS_NEG_INBAND_ENABLED;
|
||||
+ break;
|
||||
+
|
||||
+ case PHY_INTERFACE_MODE_1000BASEX:
|
||||
+ case PHY_INTERFACE_MODE_2500BASEX:
|
||||
+ /* 1000base-X is designed for use media-side for Fibre
|
||||
+ * connections, and thus the Autoneg bit needs to be
|
||||
+ * taken into account. We also do this for 2500base-X
|
||||
+ * as well, but drivers may not support this, so may
|
||||
+ * need to override this.
|
||||
+ */
|
||||
+ if (!phylink_autoneg_inband(mode))
|
||||
+ neg_mode = PHYLINK_PCS_NEG_OUTBAND;
|
||||
+ else if (linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
|
||||
+ advertising))
|
||||
+ neg_mode = PHYLINK_PCS_NEG_INBAND_ENABLED;
|
||||
+ else
|
||||
+ neg_mode = PHYLINK_PCS_NEG_INBAND_DISABLED;
|
||||
+ break;
|
||||
+
|
||||
+ default:
|
||||
+ neg_mode = PHYLINK_PCS_NEG_NONE;
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ return neg_mode;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
* struct phylink_link_state - link state structure
|
||||
* @advertising: ethtool bitmask containing advertised link modes
|
||||
* @lp_advertising: ethtool bitmask containing link partner advertised link
|
||||
@@ -436,6 +518,7 @@ struct phylink_pcs_ops;
|
||||
/**
|
||||
* struct phylink_pcs - PHYLINK PCS instance
|
||||
* @ops: a pointer to the &struct phylink_pcs_ops structure
|
||||
+ * @neg_mode: provide PCS neg mode via "mode" argument
|
||||
* @poll: poll the PCS for link changes
|
||||
*
|
||||
* This structure is designed to be embedded within the PCS private data,
|
||||
@@ -443,6 +526,7 @@ struct phylink_pcs_ops;
|
||||
*/
|
||||
struct phylink_pcs {
|
||||
const struct phylink_pcs_ops *ops;
|
||||
+ bool neg_mode;
|
||||
bool poll;
|
||||
};
|
||||
|
||||
@@ -460,12 +544,12 @@ struct phylink_pcs_ops {
|
||||
const struct phylink_link_state *state);
|
||||
void (*pcs_get_state)(struct phylink_pcs *pcs,
|
||||
struct phylink_link_state *state);
|
||||
- int (*pcs_config)(struct phylink_pcs *pcs, unsigned int mode,
|
||||
+ int (*pcs_config)(struct phylink_pcs *pcs, unsigned int neg_mode,
|
||||
phy_interface_t interface,
|
||||
const unsigned long *advertising,
|
||||
bool permit_pause_to_mac);
|
||||
void (*pcs_an_restart)(struct phylink_pcs *pcs);
|
||||
- void (*pcs_link_up)(struct phylink_pcs *pcs, unsigned int mode,
|
||||
+ void (*pcs_link_up)(struct phylink_pcs *pcs, unsigned int neg_mode,
|
||||
phy_interface_t interface, int speed, int duplex);
|
||||
};
|
||||
|
||||
@@ -508,7 +592,7 @@ void pcs_get_state(struct phylink_pcs *p
|
||||
/**
|
||||
* pcs_config() - Configure the PCS mode and advertisement
|
||||
* @pcs: a pointer to a &struct phylink_pcs.
|
||||
- * @mode: one of %MLO_AN_FIXED, %MLO_AN_PHY, %MLO_AN_INBAND.
|
||||
+ * @neg_mode: link negotiation mode (see below)
|
||||
* @interface: interface mode to be used
|
||||
* @advertising: adertisement ethtool link mode mask
|
||||
* @permit_pause_to_mac: permit forwarding pause resolution to MAC
|
||||
@@ -526,8 +610,12 @@ void pcs_get_state(struct phylink_pcs *p
|
||||
* For 1000BASE-X, the advertisement should be programmed into the PCS.
|
||||
*
|
||||
* For most 10GBASE-R, there is no advertisement.
|
||||
+ *
|
||||
+ * The %neg_mode argument should be tested via the phylink_mode_*() family of
|
||||
+ * functions, or for PCS that set pcs->neg_mode true, should be tested
|
||||
+ * against the %PHYLINK_PCS_NEG_* definitions.
|
||||
*/
|
||||
-int pcs_config(struct phylink_pcs *pcs, unsigned int mode,
|
||||
+int pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
|
||||
phy_interface_t interface, const unsigned long *advertising,
|
||||
bool permit_pause_to_mac);
|
||||
|
||||
@@ -543,7 +631,7 @@ void pcs_an_restart(struct phylink_pcs *
|
||||
/**
|
||||
* pcs_link_up() - program the PCS for the resolved link configuration
|
||||
* @pcs: a pointer to a &struct phylink_pcs.
|
||||
- * @mode: link autonegotiation mode
|
||||
+ * @neg_mode: link negotiation mode (see below)
|
||||
* @interface: link &typedef phy_interface_t mode
|
||||
* @speed: link speed
|
||||
* @duplex: link duplex
|
||||
@@ -552,8 +640,12 @@ void pcs_an_restart(struct phylink_pcs *
|
||||
* the resolved link parameters. For example, a PCS operating in SGMII
|
||||
* mode without in-band AN needs to be manually configured for the link
|
||||
* and duplex setting. Otherwise, this should be a no-op.
|
||||
+ *
|
||||
+ * The %mode argument should be tested via the phylink_mode_*() family of
|
||||
+ * functions, or for PCS that set pcs->neg_mode true, should be tested
|
||||
+ * against the %PHYLINK_PCS_NEG_* definitions.
|
||||
*/
|
||||
-void pcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
|
||||
+void pcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode,
|
||||
phy_interface_t interface, int speed, int duplex);
|
||||
#endif
|
||||
|
@ -0,0 +1,45 @@
|
||||
From cdb08aa0473730315dbc088d5394e59622314034 Mon Sep 17 00:00:00 2001
|
||||
From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
|
||||
Date: Fri, 16 Jun 2023 13:06:27 +0100
|
||||
Subject: [PATCH 1/2] net: phylink: convert phylink_mii_c22_pcs_config() to
|
||||
neg_mode
|
||||
|
||||
Use phylink_pcs_neg_mode() for phylink_mii_c22_pcs_config(). This
|
||||
results in no functional change.
|
||||
|
||||
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
|
||||
Link: https://lore.kernel.org/r/E1qA8Dj-00EaFG-Mt@rmk-PC.armlinux.org.uk
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
---
|
||||
drivers/net/phy/phylink.c | 9 ++++-----
|
||||
1 file changed, 4 insertions(+), 5 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/phylink.c
|
||||
+++ b/drivers/net/phy/phylink.c
|
||||
@@ -3558,6 +3558,7 @@ int phylink_mii_c22_pcs_config(struct md
|
||||
phy_interface_t interface,
|
||||
const unsigned long *advertising)
|
||||
{
|
||||
+ unsigned int neg_mode;
|
||||
bool changed = 0;
|
||||
u16 bmcr;
|
||||
int ret, adv;
|
||||
@@ -3571,15 +3572,13 @@ int phylink_mii_c22_pcs_config(struct md
|
||||
changed = ret;
|
||||
}
|
||||
|
||||
- /* Ensure ISOLATE bit is disabled */
|
||||
- if (mode == MLO_AN_INBAND &&
|
||||
- (interface == PHY_INTERFACE_MODE_SGMII ||
|
||||
- interface == PHY_INTERFACE_MODE_QSGMII ||
|
||||
- linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, advertising)))
|
||||
+ neg_mode = phylink_pcs_neg_mode(mode, interface, advertising);
|
||||
+ if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED)
|
||||
bmcr = BMCR_ANENABLE;
|
||||
else
|
||||
bmcr = 0;
|
||||
|
||||
+ /* Configure the inband state. Ensure ISOLATE bit is disabled */
|
||||
ret = mdiodev_modify(pcs, MII_BMCR, BMCR_ANENABLE | BMCR_ISOLATE, bmcr);
|
||||
if (ret < 0)
|
||||
return ret;
|
@ -0,0 +1,187 @@
|
||||
From febf2aaf05641f3258cc30e072aff65cffc7c82c Mon Sep 17 00:00:00 2001
|
||||
From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
|
||||
Date: Fri, 16 Jun 2023 13:06:32 +0100
|
||||
Subject: [PATCH 2/2] net: phylink: pass neg_mode into
|
||||
phylink_mii_c22_pcs_config()
|
||||
|
||||
Convert fman_dtsec, xilinx_axienet and pcs-lynx to pass the neg_mode
|
||||
into phylink_mii_c22_pcs_config(). Where appropriate, drivers are
|
||||
updated to have neg_mode passed into their pcs_config() and
|
||||
pcs_link_up() functions. For other drivers, we just hoist the call
|
||||
to phylink_pcs_neg_mode() to their pcs_config() method out of
|
||||
phylink_mii_c22_pcs_config().
|
||||
|
||||
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
|
||||
Link: https://lore.kernel.org/r/E1qA8Do-00EaFM-Ra@rmk-PC.armlinux.org.uk
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
---
|
||||
.../net/ethernet/freescale/fman/fman_dtsec.c | 7 ++++---
|
||||
.../net/ethernet/xilinx/xilinx_axienet_main.c | 6 ++++--
|
||||
drivers/net/pcs/pcs-lynx.c | 18 ++++++++++++------
|
||||
drivers/net/phy/phylink.c | 9 ++++-----
|
||||
include/linux/phylink.h | 5 +++--
|
||||
5 files changed, 27 insertions(+), 18 deletions(-)
|
||||
|
||||
--- a/drivers/net/ethernet/freescale/fman/fman_dtsec.c
|
||||
+++ b/drivers/net/ethernet/freescale/fman/fman_dtsec.c
|
||||
@@ -763,15 +763,15 @@ static void dtsec_pcs_get_state(struct p
|
||||
phylink_mii_c22_pcs_get_state(dtsec->tbidev, state);
|
||||
}
|
||||
|
||||
-static int dtsec_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
|
||||
+static int dtsec_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
|
||||
phy_interface_t interface,
|
||||
const unsigned long *advertising,
|
||||
bool permit_pause_to_mac)
|
||||
{
|
||||
struct fman_mac *dtsec = pcs_to_dtsec(pcs);
|
||||
|
||||
- return phylink_mii_c22_pcs_config(dtsec->tbidev, mode, interface,
|
||||
- advertising);
|
||||
+ return phylink_mii_c22_pcs_config(dtsec->tbidev, interface,
|
||||
+ advertising, neg_mode);
|
||||
}
|
||||
|
||||
static void dtsec_pcs_an_restart(struct phylink_pcs *pcs)
|
||||
@@ -1447,6 +1447,7 @@ int dtsec_initialization(struct mac_devi
|
||||
goto _return_fm_mac_free;
|
||||
}
|
||||
dtsec->pcs.ops = &dtsec_pcs_ops;
|
||||
+ dtsec->pcs.neg_mode = true;
|
||||
dtsec->pcs.poll = true;
|
||||
|
||||
supported = mac_dev->phylink_config.supported_interfaces;
|
||||
--- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
|
||||
+++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
|
||||
@@ -1631,7 +1631,7 @@ static void axienet_pcs_an_restart(struc
|
||||
phylink_mii_c22_pcs_an_restart(pcs_phy);
|
||||
}
|
||||
|
||||
-static int axienet_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
|
||||
+static int axienet_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
|
||||
phy_interface_t interface,
|
||||
const unsigned long *advertising,
|
||||
bool permit_pause_to_mac)
|
||||
@@ -1653,7 +1653,8 @@ static int axienet_pcs_config(struct phy
|
||||
}
|
||||
}
|
||||
|
||||
- ret = phylink_mii_c22_pcs_config(pcs_phy, mode, interface, advertising);
|
||||
+ ret = phylink_mii_c22_pcs_config(pcs_phy, interface, advertising,
|
||||
+ neg_mode);
|
||||
if (ret < 0)
|
||||
netdev_warn(ndev, "Failed to configure PCS: %d\n", ret);
|
||||
|
||||
@@ -2129,6 +2130,7 @@ static int axienet_probe(struct platform
|
||||
}
|
||||
of_node_put(np);
|
||||
lp->pcs.ops = &axienet_pcs_ops;
|
||||
+ lp->pcs.neg_mode = true;
|
||||
lp->pcs.poll = true;
|
||||
}
|
||||
|
||||
--- a/drivers/net/pcs/pcs-lynx.c
|
||||
+++ b/drivers/net/pcs/pcs-lynx.c
|
||||
@@ -122,9 +122,10 @@ static void lynx_pcs_get_state(struct ph
|
||||
state->link, state->an_complete);
|
||||
}
|
||||
|
||||
-static int lynx_pcs_config_giga(struct mdio_device *pcs, unsigned int mode,
|
||||
+static int lynx_pcs_config_giga(struct mdio_device *pcs,
|
||||
phy_interface_t interface,
|
||||
- const unsigned long *advertising)
|
||||
+ const unsigned long *advertising,
|
||||
+ unsigned int neg_mode)
|
||||
{
|
||||
u32 link_timer;
|
||||
u16 if_mode;
|
||||
@@ -137,8 +138,9 @@ static int lynx_pcs_config_giga(struct m
|
||||
|
||||
if_mode = 0;
|
||||
} else {
|
||||
+ /* SGMII and QSGMII */
|
||||
if_mode = IF_MODE_SGMII_EN;
|
||||
- if (mode == MLO_AN_INBAND) {
|
||||
+ if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) {
|
||||
if_mode |= IF_MODE_USE_SGMII_AN;
|
||||
|
||||
/* Adjust link timer for SGMII */
|
||||
@@ -154,7 +156,8 @@ static int lynx_pcs_config_giga(struct m
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
- return phylink_mii_c22_pcs_config(pcs, mode, interface, advertising);
|
||||
+ return phylink_mii_c22_pcs_config(pcs, interface, advertising,
|
||||
+ neg_mode);
|
||||
}
|
||||
|
||||
static int lynx_pcs_config_usxgmii(struct mdio_device *pcs, unsigned int mode,
|
||||
@@ -181,13 +184,16 @@ static int lynx_pcs_config(struct phylin
|
||||
bool permit)
|
||||
{
|
||||
struct lynx_pcs *lynx = phylink_pcs_to_lynx(pcs);
|
||||
+ unsigned int neg_mode;
|
||||
+
|
||||
+ neg_mode = phylink_pcs_neg_mode(mode, ifmode, advertising);
|
||||
|
||||
switch (ifmode) {
|
||||
case PHY_INTERFACE_MODE_1000BASEX:
|
||||
case PHY_INTERFACE_MODE_SGMII:
|
||||
case PHY_INTERFACE_MODE_QSGMII:
|
||||
- return lynx_pcs_config_giga(lynx->mdio, mode, ifmode,
|
||||
- advertising);
|
||||
+ return lynx_pcs_config_giga(lynx->mdio, ifmode, advertising,
|
||||
+ neg_mode);
|
||||
case PHY_INTERFACE_MODE_2500BASEX:
|
||||
if (phylink_autoneg_inband(mode)) {
|
||||
dev_err(&lynx->mdio->dev,
|
||||
--- a/drivers/net/phy/phylink.c
|
||||
+++ b/drivers/net/phy/phylink.c
|
||||
@@ -3545,20 +3545,20 @@ EXPORT_SYMBOL_GPL(phylink_mii_c22_pcs_en
|
||||
/**
|
||||
* phylink_mii_c22_pcs_config() - configure clause 22 PCS
|
||||
* @pcs: a pointer to a &struct mdio_device.
|
||||
- * @mode: link autonegotiation mode
|
||||
* @interface: the PHY interface mode being configured
|
||||
* @advertising: the ethtool advertisement mask
|
||||
+ * @neg_mode: PCS negotiation mode
|
||||
*
|
||||
* Configure a Clause 22 PCS PHY with the appropriate negotiation
|
||||
* parameters for the @mode, @interface and @advertising parameters.
|
||||
* Returns negative error number on failure, zero if the advertisement
|
||||
* has not changed, or positive if there is a change.
|
||||
*/
|
||||
-int phylink_mii_c22_pcs_config(struct mdio_device *pcs, unsigned int mode,
|
||||
+int phylink_mii_c22_pcs_config(struct mdio_device *pcs,
|
||||
phy_interface_t interface,
|
||||
- const unsigned long *advertising)
|
||||
+ const unsigned long *advertising,
|
||||
+ unsigned int neg_mode)
|
||||
{
|
||||
- unsigned int neg_mode;
|
||||
bool changed = 0;
|
||||
u16 bmcr;
|
||||
int ret, adv;
|
||||
@@ -3572,7 +3572,6 @@ int phylink_mii_c22_pcs_config(struct md
|
||||
changed = ret;
|
||||
}
|
||||
|
||||
- neg_mode = phylink_pcs_neg_mode(mode, interface, advertising);
|
||||
if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED)
|
||||
bmcr = BMCR_ANENABLE;
|
||||
else
|
||||
--- a/include/linux/phylink.h
|
||||
+++ b/include/linux/phylink.h
|
||||
@@ -743,9 +743,10 @@ void phylink_mii_c22_pcs_get_state(struc
|
||||
struct phylink_link_state *state);
|
||||
int phylink_mii_c22_pcs_encode_advertisement(phy_interface_t interface,
|
||||
const unsigned long *advertising);
|
||||
-int phylink_mii_c22_pcs_config(struct mdio_device *pcs, unsigned int mode,
|
||||
+int phylink_mii_c22_pcs_config(struct mdio_device *pcs,
|
||||
phy_interface_t interface,
|
||||
- const unsigned long *advertising);
|
||||
+ const unsigned long *advertising,
|
||||
+ unsigned int neg_mode);
|
||||
void phylink_mii_c22_pcs_an_restart(struct mdio_device *pcs);
|
||||
|
||||
void phylink_resolve_c73(struct phylink_link_state *state);
|
@ -0,0 +1,101 @@
|
||||
From 3b2de56a146f34e3f70a84cc3a1897064e445d16 Mon Sep 17 00:00:00 2001
|
||||
From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
|
||||
Date: Fri, 16 Jun 2023 13:06:43 +0100
|
||||
Subject: [PATCH] net: pcs: lynxi: update PCS driver to use neg_mode
|
||||
|
||||
Update the Lynxi PCS driver to use neg_mode rather than the mode
|
||||
argument. This ensures that the link_up() method will always program
|
||||
the speed and duplex when negotiation is disabled.
|
||||
|
||||
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
|
||||
Link: https://lore.kernel.org/r/E1qA8Dz-00EaFY-5A@rmk-PC.armlinux.org.uk
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
---
|
||||
drivers/net/pcs/pcs-mtk-lynxi.c | 39 ++++++++++++++-------------------
|
||||
1 file changed, 16 insertions(+), 23 deletions(-)
|
||||
|
||||
--- a/drivers/net/pcs/pcs-mtk-lynxi.c
|
||||
+++ b/drivers/net/pcs/pcs-mtk-lynxi.c
|
||||
@@ -102,13 +102,13 @@ static void mtk_pcs_lynxi_get_state(stru
|
||||
FIELD_GET(SGMII_LPA, adv));
|
||||
}
|
||||
|
||||
-static int mtk_pcs_lynxi_config(struct phylink_pcs *pcs, unsigned int mode,
|
||||
+static int mtk_pcs_lynxi_config(struct phylink_pcs *pcs, unsigned int neg_mode,
|
||||
phy_interface_t interface,
|
||||
const unsigned long *advertising,
|
||||
bool permit_pause_to_mac)
|
||||
{
|
||||
struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs);
|
||||
- bool mode_changed = false, changed, use_an;
|
||||
+ bool mode_changed = false, changed;
|
||||
unsigned int rgc3, sgm_mode, bmcr;
|
||||
int advertise, link_timer;
|
||||
|
||||
@@ -121,30 +121,22 @@ static int mtk_pcs_lynxi_config(struct p
|
||||
* we assume that fixes it's speed at bitrate = line rate (in
|
||||
* other words, 1000Mbps or 2500Mbps).
|
||||
*/
|
||||
- if (interface == PHY_INTERFACE_MODE_SGMII) {
|
||||
+ if (interface == PHY_INTERFACE_MODE_SGMII)
|
||||
sgm_mode = SGMII_IF_MODE_SGMII;
|
||||
- if (phylink_autoneg_inband(mode)) {
|
||||
- sgm_mode |= SGMII_REMOTE_FAULT_DIS |
|
||||
- SGMII_SPEED_DUPLEX_AN;
|
||||
- use_an = true;
|
||||
- } else {
|
||||
- use_an = false;
|
||||
- }
|
||||
- } else if (phylink_autoneg_inband(mode)) {
|
||||
- /* 1000base-X or 2500base-X autoneg */
|
||||
- sgm_mode = SGMII_REMOTE_FAULT_DIS;
|
||||
- use_an = linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
|
||||
- advertising);
|
||||
- } else {
|
||||
+ else
|
||||
/* 1000base-X or 2500base-X without autoneg */
|
||||
sgm_mode = 0;
|
||||
- use_an = false;
|
||||
- }
|
||||
|
||||
- if (use_an)
|
||||
+ if (neg_mode & PHYLINK_PCS_NEG_INBAND)
|
||||
+ sgm_mode |= SGMII_REMOTE_FAULT_DIS;
|
||||
+
|
||||
+ if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) {
|
||||
+ if (interface == PHY_INTERFACE_MODE_SGMII)
|
||||
+ sgm_mode |= SGMII_SPEED_DUPLEX_AN;
|
||||
bmcr = BMCR_ANENABLE;
|
||||
- else
|
||||
+ } else {
|
||||
bmcr = 0;
|
||||
+ }
|
||||
|
||||
if (mpcs->interface != interface) {
|
||||
link_timer = phylink_get_link_timer_ns(interface);
|
||||
@@ -216,14 +208,15 @@ static void mtk_pcs_lynxi_restart_an(str
|
||||
regmap_set_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1, BMCR_ANRESTART);
|
||||
}
|
||||
|
||||
-static void mtk_pcs_lynxi_link_up(struct phylink_pcs *pcs, unsigned int mode,
|
||||
+static void mtk_pcs_lynxi_link_up(struct phylink_pcs *pcs,
|
||||
+ unsigned int neg_mode,
|
||||
phy_interface_t interface, int speed,
|
||||
int duplex)
|
||||
{
|
||||
struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs);
|
||||
unsigned int sgm_mode;
|
||||
|
||||
- if (!phylink_autoneg_inband(mode)) {
|
||||
+ if (neg_mode != PHYLINK_PCS_NEG_INBAND_ENABLED) {
|
||||
/* Force the speed and duplex setting */
|
||||
if (speed == SPEED_10)
|
||||
sgm_mode = SGMII_SPEED_10;
|
||||
@@ -286,6 +279,7 @@ struct phylink_pcs *mtk_pcs_lynxi_create
|
||||
mpcs->regmap = regmap;
|
||||
mpcs->flags = flags;
|
||||
mpcs->pcs.ops = &mtk_pcs_lynxi_ops;
|
||||
+ mpcs->pcs.neg_mode = true;
|
||||
mpcs->pcs.poll = true;
|
||||
mpcs->interface = PHY_INTERFACE_MODE_NA;
|
||||
|
@ -0,0 +1,55 @@
|
||||
From 459fd2f11204c962e3153020f4f56748e0e10afb Mon Sep 17 00:00:00 2001
|
||||
From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
|
||||
Date: Tue, 21 Mar 2023 15:58:49 +0000
|
||||
Subject: [PATCH] net: pcs: xpcs: use Autoneg bit rather than an_enabled
|
||||
|
||||
The Autoneg bit in the advertising bitmap and state->an_enabled are
|
||||
always identical. Thus, we will be removing state->an_enabled.
|
||||
|
||||
Use the Autoneg bit in the advertising bitmap to indicate whether
|
||||
autonegotiation should be used, rather than using the an_enabled
|
||||
member which will be going away.
|
||||
|
||||
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
|
||||
Reviewed-by: Simon Horman <simon.horman@corigine.com>
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
---
|
||||
drivers/net/pcs/pcs-xpcs.c | 10 +++++++---
|
||||
1 file changed, 7 insertions(+), 3 deletions(-)
|
||||
|
||||
--- a/drivers/net/pcs/pcs-xpcs.c
|
||||
+++ b/drivers/net/pcs/pcs-xpcs.c
|
||||
@@ -931,6 +931,7 @@ static int xpcs_get_state_c73(struct dw_
|
||||
struct phylink_link_state *state,
|
||||
const struct xpcs_compat *compat)
|
||||
{
|
||||
+ bool an_enabled;
|
||||
int ret;
|
||||
|
||||
/* Link needs to be read first ... */
|
||||
@@ -948,11 +949,13 @@ static int xpcs_get_state_c73(struct dw_
|
||||
return xpcs_do_config(xpcs, state->interface, MLO_AN_INBAND, NULL);
|
||||
}
|
||||
|
||||
- if (state->an_enabled && xpcs_aneg_done_c73(xpcs, state, compat)) {
|
||||
+ an_enabled = linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
|
||||
+ state->advertising);
|
||||
+ if (an_enabled && xpcs_aneg_done_c73(xpcs, state, compat)) {
|
||||
state->an_complete = true;
|
||||
xpcs_read_lpa_c73(xpcs, state);
|
||||
xpcs_resolve_lpa_c73(xpcs, state);
|
||||
- } else if (state->an_enabled) {
|
||||
+ } else if (an_enabled) {
|
||||
state->link = 0;
|
||||
} else if (state->link) {
|
||||
xpcs_resolve_pma(xpcs, state);
|
||||
@@ -1007,7 +1010,8 @@ static int xpcs_get_state_c37_1000basex(
|
||||
{
|
||||
int lpa, bmsr;
|
||||
|
||||
- if (state->an_enabled) {
|
||||
+ if (linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
|
||||
+ state->advertising)) {
|
||||
/* Reset link state */
|
||||
state->link = false;
|
||||
|
@ -0,0 +1,30 @@
|
||||
From 43fb622d91a9f408322735d2f736495c1009f575 Mon Sep 17 00:00:00 2001
|
||||
From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
|
||||
Date: Tue, 9 May 2023 12:50:04 +0100
|
||||
Subject: [PATCH] net: pcs: xpcs: fix incorrect number of interfaces
|
||||
|
||||
In synopsys_xpcs_compat[], the DW_XPCS_2500BASEX entry was setting
|
||||
the number of interfaces using the xpcs_2500basex_features array
|
||||
rather than xpcs_2500basex_interfaces. This causes us to overflow
|
||||
the array of interfaces. Fix this.
|
||||
|
||||
Fixes: f27abde3042a ("net: pcs: add 2500BASEX support for Intel mGbE controller")
|
||||
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Reviewed-by: Leon Romanovsky <leonro@nvidia.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/pcs/pcs-xpcs.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
--- a/drivers/net/pcs/pcs-xpcs.c
|
||||
+++ b/drivers/net/pcs/pcs-xpcs.c
|
||||
@@ -1211,7 +1211,7 @@ static const struct xpcs_compat synopsys
|
||||
[DW_XPCS_2500BASEX] = {
|
||||
.supported = xpcs_2500basex_features,
|
||||
.interface = xpcs_2500basex_interfaces,
|
||||
- .num_interfaces = ARRAY_SIZE(xpcs_2500basex_features),
|
||||
+ .num_interfaces = ARRAY_SIZE(xpcs_2500basex_interfaces),
|
||||
.an_mode = DW_2500BASEX,
|
||||
},
|
||||
};
|
@ -0,0 +1,36 @@
|
||||
From f2ec98566775dd4341ec1dcf93aa5859c60de826 Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Thu, 1 Feb 2024 14:46:00 +0100
|
||||
Subject: [PATCH 1/2] net: phy: qcom: qca808x: fix logic error in LED
|
||||
brightness set
|
||||
|
||||
In switching to using phy_modify_mmd and a more short version of the
|
||||
LED ON/OFF condition in later revision, it was made a logic error where
|
||||
|
||||
value ? QCA808X_LED_FORCE_ON : QCA808X_LED_FORCE_OFF is always true as
|
||||
value is always OR with QCA808X_LED_FORCE_EN due to missing ()
|
||||
resulting in the testing condition being QCA808X_LED_FORCE_EN | value.
|
||||
|
||||
Add the () to apply the correct condition and restore correct
|
||||
functionality of the brightness ON/OFF.
|
||||
|
||||
Fixes: 7196062b64ee ("net: phy: at803x: add LED support for qca808x")
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/qcom/qca808x.c | 4 ++--
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/qcom/qca808x.c
|
||||
+++ b/drivers/net/phy/qcom/qca808x.c
|
||||
@@ -820,8 +820,8 @@ static int qca808x_led_brightness_set(st
|
||||
|
||||
return phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
|
||||
QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK,
|
||||
- QCA808X_LED_FORCE_EN | value ? QCA808X_LED_FORCE_ON :
|
||||
- QCA808X_LED_FORCE_OFF);
|
||||
+ QCA808X_LED_FORCE_EN | (value ? QCA808X_LED_FORCE_ON :
|
||||
+ QCA808X_LED_FORCE_OFF));
|
||||
}
|
||||
|
||||
static int qca808x_led_blink_set(struct phy_device *phydev, u8 index,
|
@ -0,0 +1,41 @@
|
||||
From f203c8c77c7616c099647636f4c67d59a45fe8a2 Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Thu, 1 Feb 2024 14:46:01 +0100
|
||||
Subject: [PATCH 2/2] net: phy: qcom: qca808x: default to LED active High if
|
||||
not set
|
||||
|
||||
qca808x PHY provide support for the led_polarity_set OP to configure
|
||||
and apply the active-low property but on PHY reset, the Active High bit
|
||||
is not set resulting in the LED driven as active-low.
|
||||
|
||||
To fix this, check if active-low is not set in DT and enable Active High
|
||||
polarity by default to restore correct funcionality of the LED.
|
||||
|
||||
Fixes: 7196062b64ee ("net: phy: at803x: add LED support for qca808x")
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/qcom/qca808x.c | 10 ++++++++++
|
||||
1 file changed, 10 insertions(+)
|
||||
|
||||
--- a/drivers/net/phy/qcom/qca808x.c
|
||||
+++ b/drivers/net/phy/qcom/qca808x.c
|
||||
@@ -290,8 +290,18 @@ static int qca808x_probe(struct phy_devi
|
||||
|
||||
static int qca808x_config_init(struct phy_device *phydev)
|
||||
{
|
||||
+ struct qca808x_priv *priv = phydev->priv;
|
||||
int ret;
|
||||
|
||||
+ /* Default to LED Active High if active-low not in DT */
|
||||
+ if (priv->led_polarity_mode == -1) {
|
||||
+ ret = phy_set_bits_mmd(phydev, MDIO_MMD_AN,
|
||||
+ QCA808X_MMD7_LED_POLARITY_CTRL,
|
||||
+ QCA808X_LED_ACTIVE_HIGH);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
/* Active adc&vga on 802.3az for the link 1000M and 100M */
|
||||
ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_ADDR_CLD_CTRL7,
|
||||
QCA808X_8023AZ_AFE_CTRL_MASK, QCA808X_8023AZ_AFE_EN);
|
@ -0,0 +1,211 @@
|
||||
From 385ef48f468696d6d172eb367656a3466fa0408d Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Tue, 6 Feb 2024 18:31:05 +0100
|
||||
Subject: [PATCH 02/10] net: phy: add support for scanning PHY in PHY packages
|
||||
nodes
|
||||
|
||||
Add support for scanning PHY in PHY package nodes. PHY packages nodes
|
||||
are just container for actual PHY on the MDIO bus.
|
||||
|
||||
Their PHY address defined in the PHY package node are absolute and
|
||||
reflect the address on the MDIO bus.
|
||||
|
||||
mdio_bus.c and of_mdio.c is updated to now support and parse also
|
||||
PHY package subnode by checking if the node name match
|
||||
"ethernet-phy-package".
|
||||
|
||||
As PHY package reg is mandatory and each PHY in the PHY package must
|
||||
have a reg, every invalid PHY Package node is ignored and will be
|
||||
skipped by the autoscan fallback.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/mdio/of_mdio.c | 79 +++++++++++++++++++++++++++-----------
|
||||
drivers/net/phy/mdio_bus.c | 44 +++++++++++++++++----
|
||||
2 files changed, 92 insertions(+), 31 deletions(-)
|
||||
|
||||
--- a/drivers/net/mdio/of_mdio.c
|
||||
+++ b/drivers/net/mdio/of_mdio.c
|
||||
@@ -138,6 +138,53 @@ bool of_mdiobus_child_is_phy(struct devi
|
||||
}
|
||||
EXPORT_SYMBOL(of_mdiobus_child_is_phy);
|
||||
|
||||
+static int __of_mdiobus_parse_phys(struct mii_bus *mdio, struct device_node *np,
|
||||
+ bool *scanphys)
|
||||
+{
|
||||
+ struct device_node *child;
|
||||
+ int addr, rc = 0;
|
||||
+
|
||||
+ /* Loop over the child nodes and register a phy_device for each phy */
|
||||
+ for_each_available_child_of_node(np, child) {
|
||||
+ if (of_node_name_eq(child, "ethernet-phy-package")) {
|
||||
+ /* Ignore invalid ethernet-phy-package node */
|
||||
+ if (!of_find_property(child, "reg", NULL))
|
||||
+ continue;
|
||||
+
|
||||
+ rc = __of_mdiobus_parse_phys(mdio, child, NULL);
|
||||
+ if (rc && rc != -ENODEV)
|
||||
+ goto exit;
|
||||
+
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ addr = of_mdio_parse_addr(&mdio->dev, child);
|
||||
+ if (addr < 0) {
|
||||
+ /* Skip scanning for invalid ethernet-phy-package node */
|
||||
+ if (scanphys)
|
||||
+ *scanphys = true;
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ if (of_mdiobus_child_is_phy(child))
|
||||
+ rc = of_mdiobus_register_phy(mdio, child, addr);
|
||||
+ else
|
||||
+ rc = of_mdiobus_register_device(mdio, child, addr);
|
||||
+
|
||||
+ if (rc == -ENODEV)
|
||||
+ dev_err(&mdio->dev,
|
||||
+ "MDIO device at address %d is missing.\n",
|
||||
+ addr);
|
||||
+ else if (rc)
|
||||
+ goto exit;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+exit:
|
||||
+ of_node_put(child);
|
||||
+ return rc;
|
||||
+}
|
||||
+
|
||||
/**
|
||||
* __of_mdiobus_register - Register mii_bus and create PHYs from the device tree
|
||||
* @mdio: pointer to mii_bus structure
|
||||
@@ -179,33 +226,18 @@ int __of_mdiobus_register(struct mii_bus
|
||||
return rc;
|
||||
|
||||
/* Loop over the child nodes and register a phy_device for each phy */
|
||||
- for_each_available_child_of_node(np, child) {
|
||||
- addr = of_mdio_parse_addr(&mdio->dev, child);
|
||||
- if (addr < 0) {
|
||||
- scanphys = true;
|
||||
- continue;
|
||||
- }
|
||||
-
|
||||
- if (of_mdiobus_child_is_phy(child))
|
||||
- rc = of_mdiobus_register_phy(mdio, child, addr);
|
||||
- else
|
||||
- rc = of_mdiobus_register_device(mdio, child, addr);
|
||||
-
|
||||
- if (rc == -ENODEV)
|
||||
- dev_err(&mdio->dev,
|
||||
- "MDIO device at address %d is missing.\n",
|
||||
- addr);
|
||||
- else if (rc)
|
||||
- goto unregister;
|
||||
- }
|
||||
+ rc = __of_mdiobus_parse_phys(mdio, np, &scanphys);
|
||||
+ if (rc)
|
||||
+ goto unregister;
|
||||
|
||||
if (!scanphys)
|
||||
return 0;
|
||||
|
||||
/* auto scan for PHYs with empty reg property */
|
||||
for_each_available_child_of_node(np, child) {
|
||||
- /* Skip PHYs with reg property set */
|
||||
- if (of_find_property(child, "reg", NULL))
|
||||
+ /* Skip PHYs with reg property set or ethernet-phy-package node */
|
||||
+ if (of_find_property(child, "reg", NULL) ||
|
||||
+ of_node_name_eq(child, "ethernet-phy-package"))
|
||||
continue;
|
||||
|
||||
for (addr = 0; addr < PHY_MAX_ADDR; addr++) {
|
||||
@@ -226,15 +258,16 @@ int __of_mdiobus_register(struct mii_bus
|
||||
if (!rc)
|
||||
break;
|
||||
if (rc != -ENODEV)
|
||||
- goto unregister;
|
||||
+ goto put_unregister;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
-unregister:
|
||||
+put_unregister:
|
||||
of_node_put(child);
|
||||
+unregister:
|
||||
mdiobus_unregister(mdio);
|
||||
return rc;
|
||||
}
|
||||
--- a/drivers/net/phy/mdio_bus.c
|
||||
+++ b/drivers/net/phy/mdio_bus.c
|
||||
@@ -448,19 +448,34 @@ EXPORT_SYMBOL(of_mdio_find_bus);
|
||||
* found, set the of_node pointer for the mdio device. This allows
|
||||
* auto-probed phy devices to be supplied with information passed in
|
||||
* via DT.
|
||||
+ * If a PHY package is found, PHY is searched also there.
|
||||
*/
|
||||
-static void of_mdiobus_link_mdiodev(struct mii_bus *bus,
|
||||
- struct mdio_device *mdiodev)
|
||||
+static int of_mdiobus_find_phy(struct device *dev, struct mdio_device *mdiodev,
|
||||
+ struct device_node *np)
|
||||
{
|
||||
- struct device *dev = &mdiodev->dev;
|
||||
struct device_node *child;
|
||||
|
||||
- if (dev->of_node || !bus->dev.of_node)
|
||||
- return;
|
||||
-
|
||||
- for_each_available_child_of_node(bus->dev.of_node, child) {
|
||||
+ for_each_available_child_of_node(np, child) {
|
||||
int addr;
|
||||
|
||||
+ if (of_node_name_eq(child, "ethernet-phy-package")) {
|
||||
+ /* Validate PHY package reg presence */
|
||||
+ if (!of_find_property(child, "reg", NULL)) {
|
||||
+ of_node_put(child);
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ if (!of_mdiobus_find_phy(dev, mdiodev, child)) {
|
||||
+ /* The refcount for the PHY package will be
|
||||
+ * incremented later when PHY join the Package.
|
||||
+ */
|
||||
+ of_node_put(child);
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
addr = of_mdio_parse_addr(dev, child);
|
||||
if (addr < 0)
|
||||
continue;
|
||||
@@ -470,9 +485,22 @@ static void of_mdiobus_link_mdiodev(stru
|
||||
/* The refcount on "child" is passed to the mdio
|
||||
* device. Do _not_ use of_node_put(child) here.
|
||||
*/
|
||||
- return;
|
||||
+ return 0;
|
||||
}
|
||||
}
|
||||
+
|
||||
+ return -ENODEV;
|
||||
+}
|
||||
+
|
||||
+static void of_mdiobus_link_mdiodev(struct mii_bus *bus,
|
||||
+ struct mdio_device *mdiodev)
|
||||
+{
|
||||
+ struct device *dev = &mdiodev->dev;
|
||||
+
|
||||
+ if (dev->of_node || !bus->dev.of_node)
|
||||
+ return;
|
||||
+
|
||||
+ of_mdiobus_find_phy(dev, mdiodev, bus->dev.of_node);
|
||||
}
|
||||
#else /* !IS_ENABLED(CONFIG_OF_MDIO) */
|
||||
static inline void of_mdiobus_link_mdiodev(struct mii_bus *mdio,
|
@ -0,0 +1,185 @@
|
||||
From 471e8fd3afcef5a9f9089f0bd21965ad9ba35c91 Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Tue, 6 Feb 2024 18:31:06 +0100
|
||||
Subject: [PATCH 03/10] net: phy: add devm/of_phy_package_join helper
|
||||
|
||||
Add devm/of_phy_package_join helper to join PHYs in a PHY package. These
|
||||
are variant of the manual phy_package_join with the difference that
|
||||
these will use DT nodes to derive the base_addr instead of manually
|
||||
passing an hardcoded value.
|
||||
|
||||
An additional value is added in phy_package_shared, "np" to reference
|
||||
the PHY package node pointer in specific PHY driver probe_once and
|
||||
config_init_once functions to make use of additional specific properties
|
||||
defined in the PHY package node in DT.
|
||||
|
||||
The np value is filled only with of_phy_package_join if a valid PHY
|
||||
package node is found. A valid PHY package node must have the node name
|
||||
set to "ethernet-phy-package".
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/phy_device.c | 96 ++++++++++++++++++++++++++++++++++++
|
||||
include/linux/phy.h | 6 +++
|
||||
2 files changed, 102 insertions(+)
|
||||
|
||||
--- a/drivers/net/phy/phy_device.c
|
||||
+++ b/drivers/net/phy/phy_device.c
|
||||
@@ -1648,6 +1648,7 @@ int phy_package_join(struct phy_device *
|
||||
shared->priv_size = priv_size;
|
||||
}
|
||||
shared->base_addr = base_addr;
|
||||
+ shared->np = NULL;
|
||||
refcount_set(&shared->refcnt, 1);
|
||||
bus->shared[base_addr] = shared;
|
||||
} else {
|
||||
@@ -1671,6 +1672,63 @@ err_unlock:
|
||||
EXPORT_SYMBOL_GPL(phy_package_join);
|
||||
|
||||
/**
|
||||
+ * of_phy_package_join - join a common PHY group in PHY package
|
||||
+ * @phydev: target phy_device struct
|
||||
+ * @priv_size: if non-zero allocate this amount of bytes for private data
|
||||
+ *
|
||||
+ * This is a variant of phy_package_join for PHY package defined in DT.
|
||||
+ *
|
||||
+ * The parent node of the @phydev is checked as a valid PHY package node
|
||||
+ * structure (by matching the node name "ethernet-phy-package") and the
|
||||
+ * base_addr for the PHY package is passed to phy_package_join.
|
||||
+ *
|
||||
+ * With this configuration the shared struct will also have the np value
|
||||
+ * filled to use additional DT defined properties in PHY specific
|
||||
+ * probe_once and config_init_once PHY package OPs.
|
||||
+ *
|
||||
+ * Returns < 0 on error, 0 on success. Esp. calling phy_package_join()
|
||||
+ * with the same cookie but a different priv_size is an error. Or a parent
|
||||
+ * node is not detected or is not valid or doesn't match the expected node
|
||||
+ * name for PHY package.
|
||||
+ */
|
||||
+int of_phy_package_join(struct phy_device *phydev, size_t priv_size)
|
||||
+{
|
||||
+ struct device_node *node = phydev->mdio.dev.of_node;
|
||||
+ struct device_node *package_node;
|
||||
+ u32 base_addr;
|
||||
+ int ret;
|
||||
+
|
||||
+ if (!node)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ package_node = of_get_parent(node);
|
||||
+ if (!package_node)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ if (!of_node_name_eq(package_node, "ethernet-phy-package")) {
|
||||
+ ret = -EINVAL;
|
||||
+ goto exit;
|
||||
+ }
|
||||
+
|
||||
+ if (of_property_read_u32(package_node, "reg", &base_addr)) {
|
||||
+ ret = -EINVAL;
|
||||
+ goto exit;
|
||||
+ }
|
||||
+
|
||||
+ ret = phy_package_join(phydev, base_addr, priv_size);
|
||||
+ if (ret)
|
||||
+ goto exit;
|
||||
+
|
||||
+ phydev->shared->np = package_node;
|
||||
+
|
||||
+ return 0;
|
||||
+exit:
|
||||
+ of_node_put(package_node);
|
||||
+ return ret;
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(of_phy_package_join);
|
||||
+
|
||||
+/**
|
||||
* phy_package_leave - leave a common PHY group
|
||||
* @phydev: target phy_device struct
|
||||
*
|
||||
@@ -1686,6 +1744,10 @@ void phy_package_leave(struct phy_device
|
||||
if (!shared)
|
||||
return;
|
||||
|
||||
+ /* Decrease the node refcount on leave if present */
|
||||
+ if (shared->np)
|
||||
+ of_node_put(shared->np);
|
||||
+
|
||||
if (refcount_dec_and_mutex_lock(&shared->refcnt, &bus->shared_lock)) {
|
||||
bus->shared[shared->base_addr] = NULL;
|
||||
mutex_unlock(&bus->shared_lock);
|
||||
@@ -1739,6 +1801,40 @@ int devm_phy_package_join(struct device
|
||||
EXPORT_SYMBOL_GPL(devm_phy_package_join);
|
||||
|
||||
/**
|
||||
+ * devm_of_phy_package_join - resource managed of_phy_package_join()
|
||||
+ * @dev: device that is registering this PHY package
|
||||
+ * @phydev: target phy_device struct
|
||||
+ * @priv_size: if non-zero allocate this amount of bytes for private data
|
||||
+ *
|
||||
+ * Managed of_phy_package_join(). Shared storage fetched by this function,
|
||||
+ * phy_package_leave() is automatically called on driver detach. See
|
||||
+ * of_phy_package_join() for more information.
|
||||
+ */
|
||||
+int devm_of_phy_package_join(struct device *dev, struct phy_device *phydev,
|
||||
+ size_t priv_size)
|
||||
+{
|
||||
+ struct phy_device **ptr;
|
||||
+ int ret;
|
||||
+
|
||||
+ ptr = devres_alloc(devm_phy_package_leave, sizeof(*ptr),
|
||||
+ GFP_KERNEL);
|
||||
+ if (!ptr)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ ret = of_phy_package_join(phydev, priv_size);
|
||||
+
|
||||
+ if (!ret) {
|
||||
+ *ptr = phydev;
|
||||
+ devres_add(dev, ptr);
|
||||
+ } else {
|
||||
+ devres_free(ptr);
|
||||
+ }
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(devm_of_phy_package_join);
|
||||
+
|
||||
+/**
|
||||
* phy_detach - detach a PHY device from its network device
|
||||
* @phydev: target phy_device struct
|
||||
*
|
||||
--- a/include/linux/phy.h
|
||||
+++ b/include/linux/phy.h
|
||||
@@ -321,6 +321,7 @@ struct mdio_bus_stats {
|
||||
* struct phy_package_shared - Shared information in PHY packages
|
||||
* @base_addr: Base PHY address of PHY package used to combine PHYs
|
||||
* in one package and for offset calculation of phy_package_read/write
|
||||
+ * @np: Pointer to the Device Node if PHY package defined in DT
|
||||
* @refcnt: Number of PHYs connected to this shared data
|
||||
* @flags: Initialization of PHY package
|
||||
* @priv_size: Size of the shared private data @priv
|
||||
@@ -332,6 +333,8 @@ struct mdio_bus_stats {
|
||||
*/
|
||||
struct phy_package_shared {
|
||||
u8 base_addr;
|
||||
+ /* With PHY package defined in DT this points to the PHY package node */
|
||||
+ struct device_node *np;
|
||||
refcount_t refcnt;
|
||||
unsigned long flags;
|
||||
size_t priv_size;
|
||||
@@ -1793,9 +1796,12 @@ int phy_ethtool_set_link_ksettings(struc
|
||||
const struct ethtool_link_ksettings *cmd);
|
||||
int phy_ethtool_nway_reset(struct net_device *ndev);
|
||||
int phy_package_join(struct phy_device *phydev, int base_addr, size_t priv_size);
|
||||
+int of_phy_package_join(struct phy_device *phydev, size_t priv_size);
|
||||
void phy_package_leave(struct phy_device *phydev);
|
||||
int devm_phy_package_join(struct device *dev, struct phy_device *phydev,
|
||||
int base_addr, size_t priv_size);
|
||||
+int devm_of_phy_package_join(struct device *dev, struct phy_device *phydev,
|
||||
+ size_t priv_size);
|
||||
|
||||
#if IS_ENABLED(CONFIG_PHYLIB)
|
||||
int __init mdio_bus_init(void);
|
@ -0,0 +1,583 @@
|
||||
From 737eb75a815f9c08dcbb6631db57f4f4b0540a5b Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Tue, 6 Feb 2024 18:31:07 +0100
|
||||
Subject: [PATCH 04/10] net: phy: qcom: move more function to shared library
|
||||
|
||||
Move more function to shared library in preparation for introduction of
|
||||
new PHY Family qca807x that will make use of both functions from at803x
|
||||
and qca808x as it's a transition PHY with some implementation of at803x
|
||||
and some from the new qca808x.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/qcom/at803x.c | 35 -----
|
||||
drivers/net/phy/qcom/qca808x.c | 205 ----------------------------
|
||||
drivers/net/phy/qcom/qcom-phy-lib.c | 193 ++++++++++++++++++++++++++
|
||||
drivers/net/phy/qcom/qcom.h | 51 +++++++
|
||||
4 files changed, 244 insertions(+), 240 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/qcom/at803x.c
|
||||
+++ b/drivers/net/phy/qcom/at803x.c
|
||||
@@ -504,41 +504,6 @@ static void at803x_link_change_notify(st
|
||||
}
|
||||
}
|
||||
|
||||
-static int at803x_read_status(struct phy_device *phydev)
|
||||
-{
|
||||
- struct at803x_ss_mask ss_mask = { 0 };
|
||||
- int err, old_link = phydev->link;
|
||||
-
|
||||
- /* Update the link, but return if there was an error */
|
||||
- err = genphy_update_link(phydev);
|
||||
- if (err)
|
||||
- return err;
|
||||
-
|
||||
- /* why bother the PHY if nothing can have changed */
|
||||
- if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link)
|
||||
- return 0;
|
||||
-
|
||||
- phydev->speed = SPEED_UNKNOWN;
|
||||
- phydev->duplex = DUPLEX_UNKNOWN;
|
||||
- phydev->pause = 0;
|
||||
- phydev->asym_pause = 0;
|
||||
-
|
||||
- err = genphy_read_lpa(phydev);
|
||||
- if (err < 0)
|
||||
- return err;
|
||||
-
|
||||
- ss_mask.speed_mask = AT803X_SS_SPEED_MASK;
|
||||
- ss_mask.speed_shift = __bf_shf(AT803X_SS_SPEED_MASK);
|
||||
- err = at803x_read_specific_status(phydev, ss_mask);
|
||||
- if (err < 0)
|
||||
- return err;
|
||||
-
|
||||
- if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete)
|
||||
- phy_resolve_aneg_pause(phydev);
|
||||
-
|
||||
- return 0;
|
||||
-}
|
||||
-
|
||||
static int at803x_config_aneg(struct phy_device *phydev)
|
||||
{
|
||||
struct at803x_priv *priv = phydev->priv;
|
||||
--- a/drivers/net/phy/qcom/qca808x.c
|
||||
+++ b/drivers/net/phy/qcom/qca808x.c
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#include <linux/phy.h>
|
||||
#include <linux/module.h>
|
||||
-#include <linux/ethtool_netlink.h>
|
||||
|
||||
#include "qcom.h"
|
||||
|
||||
@@ -63,55 +62,6 @@
|
||||
#define QCA808X_DBG_AN_TEST 0xb
|
||||
#define QCA808X_HIBERNATION_EN BIT(15)
|
||||
|
||||
-#define QCA808X_CDT_ENABLE_TEST BIT(15)
|
||||
-#define QCA808X_CDT_INTER_CHECK_DIS BIT(13)
|
||||
-#define QCA808X_CDT_STATUS BIT(11)
|
||||
-#define QCA808X_CDT_LENGTH_UNIT BIT(10)
|
||||
-
|
||||
-#define QCA808X_MMD3_CDT_STATUS 0x8064
|
||||
-#define QCA808X_MMD3_CDT_DIAG_PAIR_A 0x8065
|
||||
-#define QCA808X_MMD3_CDT_DIAG_PAIR_B 0x8066
|
||||
-#define QCA808X_MMD3_CDT_DIAG_PAIR_C 0x8067
|
||||
-#define QCA808X_MMD3_CDT_DIAG_PAIR_D 0x8068
|
||||
-#define QCA808X_CDT_DIAG_LENGTH_SAME_SHORT GENMASK(15, 8)
|
||||
-#define QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT GENMASK(7, 0)
|
||||
-
|
||||
-#define QCA808X_CDT_CODE_PAIR_A GENMASK(15, 12)
|
||||
-#define QCA808X_CDT_CODE_PAIR_B GENMASK(11, 8)
|
||||
-#define QCA808X_CDT_CODE_PAIR_C GENMASK(7, 4)
|
||||
-#define QCA808X_CDT_CODE_PAIR_D GENMASK(3, 0)
|
||||
-
|
||||
-#define QCA808X_CDT_STATUS_STAT_TYPE GENMASK(1, 0)
|
||||
-#define QCA808X_CDT_STATUS_STAT_FAIL FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 0)
|
||||
-#define QCA808X_CDT_STATUS_STAT_NORMAL FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 1)
|
||||
-#define QCA808X_CDT_STATUS_STAT_SAME_OPEN FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 2)
|
||||
-#define QCA808X_CDT_STATUS_STAT_SAME_SHORT FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 3)
|
||||
-
|
||||
-#define QCA808X_CDT_STATUS_STAT_MDI GENMASK(3, 2)
|
||||
-#define QCA808X_CDT_STATUS_STAT_MDI1 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 1)
|
||||
-#define QCA808X_CDT_STATUS_STAT_MDI2 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 2)
|
||||
-#define QCA808X_CDT_STATUS_STAT_MDI3 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 3)
|
||||
-
|
||||
-/* NORMAL are MDI with type set to 0 */
|
||||
-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI1
|
||||
-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
|
||||
- QCA808X_CDT_STATUS_STAT_MDI1)
|
||||
-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
|
||||
- QCA808X_CDT_STATUS_STAT_MDI1)
|
||||
-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI2
|
||||
-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
|
||||
- QCA808X_CDT_STATUS_STAT_MDI2)
|
||||
-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
|
||||
- QCA808X_CDT_STATUS_STAT_MDI2)
|
||||
-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI3
|
||||
-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
|
||||
- QCA808X_CDT_STATUS_STAT_MDI3)
|
||||
-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
|
||||
- QCA808X_CDT_STATUS_STAT_MDI3)
|
||||
-
|
||||
-/* Added for reference of existence but should be handled by wait_for_completion already */
|
||||
-#define QCA808X_CDT_STATUS_STAT_BUSY (BIT(1) | BIT(3))
|
||||
-
|
||||
#define QCA808X_MMD7_LED_GLOBAL 0x8073
|
||||
#define QCA808X_LED_BLINK_1 GENMASK(11, 6)
|
||||
#define QCA808X_LED_BLINK_2 GENMASK(5, 0)
|
||||
@@ -406,86 +356,6 @@ static int qca808x_soft_reset(struct phy
|
||||
return ret;
|
||||
}
|
||||
|
||||
-static bool qca808x_cdt_fault_length_valid(int cdt_code)
|
||||
-{
|
||||
- switch (cdt_code) {
|
||||
- case QCA808X_CDT_STATUS_STAT_SAME_SHORT:
|
||||
- case QCA808X_CDT_STATUS_STAT_SAME_OPEN:
|
||||
- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL:
|
||||
- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN:
|
||||
- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT:
|
||||
- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL:
|
||||
- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN:
|
||||
- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT:
|
||||
- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL:
|
||||
- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN:
|
||||
- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT:
|
||||
- return true;
|
||||
- default:
|
||||
- return false;
|
||||
- }
|
||||
-}
|
||||
-
|
||||
-static int qca808x_cable_test_result_trans(int cdt_code)
|
||||
-{
|
||||
- switch (cdt_code) {
|
||||
- case QCA808X_CDT_STATUS_STAT_NORMAL:
|
||||
- return ETHTOOL_A_CABLE_RESULT_CODE_OK;
|
||||
- case QCA808X_CDT_STATUS_STAT_SAME_SHORT:
|
||||
- return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT;
|
||||
- case QCA808X_CDT_STATUS_STAT_SAME_OPEN:
|
||||
- return ETHTOOL_A_CABLE_RESULT_CODE_OPEN;
|
||||
- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL:
|
||||
- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN:
|
||||
- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT:
|
||||
- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL:
|
||||
- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN:
|
||||
- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT:
|
||||
- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL:
|
||||
- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN:
|
||||
- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT:
|
||||
- return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT;
|
||||
- case QCA808X_CDT_STATUS_STAT_FAIL:
|
||||
- default:
|
||||
- return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC;
|
||||
- }
|
||||
-}
|
||||
-
|
||||
-static int qca808x_cdt_fault_length(struct phy_device *phydev, int pair,
|
||||
- int result)
|
||||
-{
|
||||
- int val;
|
||||
- u32 cdt_length_reg = 0;
|
||||
-
|
||||
- switch (pair) {
|
||||
- case ETHTOOL_A_CABLE_PAIR_A:
|
||||
- cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_A;
|
||||
- break;
|
||||
- case ETHTOOL_A_CABLE_PAIR_B:
|
||||
- cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_B;
|
||||
- break;
|
||||
- case ETHTOOL_A_CABLE_PAIR_C:
|
||||
- cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_C;
|
||||
- break;
|
||||
- case ETHTOOL_A_CABLE_PAIR_D:
|
||||
- cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_D;
|
||||
- break;
|
||||
- default:
|
||||
- return -EINVAL;
|
||||
- }
|
||||
-
|
||||
- val = phy_read_mmd(phydev, MDIO_MMD_PCS, cdt_length_reg);
|
||||
- if (val < 0)
|
||||
- return val;
|
||||
-
|
||||
- if (result == ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT)
|
||||
- val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_SAME_SHORT, val);
|
||||
- else
|
||||
- val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT, val);
|
||||
-
|
||||
- return at803x_cdt_fault_length(val);
|
||||
-}
|
||||
-
|
||||
static int qca808x_cable_test_start(struct phy_device *phydev)
|
||||
{
|
||||
int ret;
|
||||
@@ -526,81 +396,6 @@ static int qca808x_cable_test_start(stru
|
||||
|
||||
return 0;
|
||||
}
|
||||
-
|
||||
-static int qca808x_cable_test_get_pair_status(struct phy_device *phydev, u8 pair,
|
||||
- u16 status)
|
||||
-{
|
||||
- int length, result;
|
||||
- u16 pair_code;
|
||||
-
|
||||
- switch (pair) {
|
||||
- case ETHTOOL_A_CABLE_PAIR_A:
|
||||
- pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_A, status);
|
||||
- break;
|
||||
- case ETHTOOL_A_CABLE_PAIR_B:
|
||||
- pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_B, status);
|
||||
- break;
|
||||
- case ETHTOOL_A_CABLE_PAIR_C:
|
||||
- pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_C, status);
|
||||
- break;
|
||||
- case ETHTOOL_A_CABLE_PAIR_D:
|
||||
- pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_D, status);
|
||||
- break;
|
||||
- default:
|
||||
- return -EINVAL;
|
||||
- }
|
||||
-
|
||||
- result = qca808x_cable_test_result_trans(pair_code);
|
||||
- ethnl_cable_test_result(phydev, pair, result);
|
||||
-
|
||||
- if (qca808x_cdt_fault_length_valid(pair_code)) {
|
||||
- length = qca808x_cdt_fault_length(phydev, pair, result);
|
||||
- ethnl_cable_test_fault_length(phydev, pair, length);
|
||||
- }
|
||||
-
|
||||
- return 0;
|
||||
-}
|
||||
-
|
||||
-static int qca808x_cable_test_get_status(struct phy_device *phydev, bool *finished)
|
||||
-{
|
||||
- int ret, val;
|
||||
-
|
||||
- *finished = false;
|
||||
-
|
||||
- val = QCA808X_CDT_ENABLE_TEST |
|
||||
- QCA808X_CDT_LENGTH_UNIT;
|
||||
- ret = at803x_cdt_start(phydev, val);
|
||||
- if (ret)
|
||||
- return ret;
|
||||
-
|
||||
- ret = at803x_cdt_wait_for_completion(phydev, QCA808X_CDT_ENABLE_TEST);
|
||||
- if (ret)
|
||||
- return ret;
|
||||
-
|
||||
- val = phy_read_mmd(phydev, MDIO_MMD_PCS, QCA808X_MMD3_CDT_STATUS);
|
||||
- if (val < 0)
|
||||
- return val;
|
||||
-
|
||||
- ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_A, val);
|
||||
- if (ret)
|
||||
- return ret;
|
||||
-
|
||||
- ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_B, val);
|
||||
- if (ret)
|
||||
- return ret;
|
||||
-
|
||||
- ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_C, val);
|
||||
- if (ret)
|
||||
- return ret;
|
||||
-
|
||||
- ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_D, val);
|
||||
- if (ret)
|
||||
- return ret;
|
||||
-
|
||||
- *finished = true;
|
||||
-
|
||||
- return 0;
|
||||
-}
|
||||
|
||||
static int qca808x_get_features(struct phy_device *phydev)
|
||||
{
|
||||
--- a/drivers/net/phy/qcom/qcom-phy-lib.c
|
||||
+++ b/drivers/net/phy/qcom/qcom-phy-lib.c
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
+#include <linux/ethtool_netlink.h>
|
||||
|
||||
#include "qcom.h"
|
||||
|
||||
@@ -311,6 +312,42 @@ int at803x_prepare_config_aneg(struct ph
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(at803x_prepare_config_aneg);
|
||||
|
||||
+int at803x_read_status(struct phy_device *phydev)
|
||||
+{
|
||||
+ struct at803x_ss_mask ss_mask = { 0 };
|
||||
+ int err, old_link = phydev->link;
|
||||
+
|
||||
+ /* Update the link, but return if there was an error */
|
||||
+ err = genphy_update_link(phydev);
|
||||
+ if (err)
|
||||
+ return err;
|
||||
+
|
||||
+ /* why bother the PHY if nothing can have changed */
|
||||
+ if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link)
|
||||
+ return 0;
|
||||
+
|
||||
+ phydev->speed = SPEED_UNKNOWN;
|
||||
+ phydev->duplex = DUPLEX_UNKNOWN;
|
||||
+ phydev->pause = 0;
|
||||
+ phydev->asym_pause = 0;
|
||||
+
|
||||
+ err = genphy_read_lpa(phydev);
|
||||
+ if (err < 0)
|
||||
+ return err;
|
||||
+
|
||||
+ ss_mask.speed_mask = AT803X_SS_SPEED_MASK;
|
||||
+ ss_mask.speed_shift = __bf_shf(AT803X_SS_SPEED_MASK);
|
||||
+ err = at803x_read_specific_status(phydev, ss_mask);
|
||||
+ if (err < 0)
|
||||
+ return err;
|
||||
+
|
||||
+ if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete)
|
||||
+ phy_resolve_aneg_pause(phydev);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(at803x_read_status);
|
||||
+
|
||||
static int at803x_get_downshift(struct phy_device *phydev, u8 *d)
|
||||
{
|
||||
int val;
|
||||
@@ -427,3 +464,159 @@ int at803x_cdt_wait_for_completion(struc
|
||||
return ret < 0 ? ret : 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(at803x_cdt_wait_for_completion);
|
||||
+
|
||||
+static bool qca808x_cdt_fault_length_valid(int cdt_code)
|
||||
+{
|
||||
+ switch (cdt_code) {
|
||||
+ case QCA808X_CDT_STATUS_STAT_SAME_SHORT:
|
||||
+ case QCA808X_CDT_STATUS_STAT_SAME_OPEN:
|
||||
+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL:
|
||||
+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN:
|
||||
+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT:
|
||||
+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL:
|
||||
+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN:
|
||||
+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT:
|
||||
+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL:
|
||||
+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN:
|
||||
+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT:
|
||||
+ return true;
|
||||
+ default:
|
||||
+ return false;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static int qca808x_cable_test_result_trans(int cdt_code)
|
||||
+{
|
||||
+ switch (cdt_code) {
|
||||
+ case QCA808X_CDT_STATUS_STAT_NORMAL:
|
||||
+ return ETHTOOL_A_CABLE_RESULT_CODE_OK;
|
||||
+ case QCA808X_CDT_STATUS_STAT_SAME_SHORT:
|
||||
+ return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT;
|
||||
+ case QCA808X_CDT_STATUS_STAT_SAME_OPEN:
|
||||
+ return ETHTOOL_A_CABLE_RESULT_CODE_OPEN;
|
||||
+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL:
|
||||
+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN:
|
||||
+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT:
|
||||
+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL:
|
||||
+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN:
|
||||
+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT:
|
||||
+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL:
|
||||
+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN:
|
||||
+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT:
|
||||
+ return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT;
|
||||
+ case QCA808X_CDT_STATUS_STAT_FAIL:
|
||||
+ default:
|
||||
+ return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static int qca808x_cdt_fault_length(struct phy_device *phydev, int pair,
|
||||
+ int result)
|
||||
+{
|
||||
+ int val;
|
||||
+ u32 cdt_length_reg = 0;
|
||||
+
|
||||
+ switch (pair) {
|
||||
+ case ETHTOOL_A_CABLE_PAIR_A:
|
||||
+ cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_A;
|
||||
+ break;
|
||||
+ case ETHTOOL_A_CABLE_PAIR_B:
|
||||
+ cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_B;
|
||||
+ break;
|
||||
+ case ETHTOOL_A_CABLE_PAIR_C:
|
||||
+ cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_C;
|
||||
+ break;
|
||||
+ case ETHTOOL_A_CABLE_PAIR_D:
|
||||
+ cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_D;
|
||||
+ break;
|
||||
+ default:
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ val = phy_read_mmd(phydev, MDIO_MMD_PCS, cdt_length_reg);
|
||||
+ if (val < 0)
|
||||
+ return val;
|
||||
+
|
||||
+ if (result == ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT)
|
||||
+ val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_SAME_SHORT, val);
|
||||
+ else
|
||||
+ val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT, val);
|
||||
+
|
||||
+ return at803x_cdt_fault_length(val);
|
||||
+}
|
||||
+
|
||||
+static int qca808x_cable_test_get_pair_status(struct phy_device *phydev, u8 pair,
|
||||
+ u16 status)
|
||||
+{
|
||||
+ int length, result;
|
||||
+ u16 pair_code;
|
||||
+
|
||||
+ switch (pair) {
|
||||
+ case ETHTOOL_A_CABLE_PAIR_A:
|
||||
+ pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_A, status);
|
||||
+ break;
|
||||
+ case ETHTOOL_A_CABLE_PAIR_B:
|
||||
+ pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_B, status);
|
||||
+ break;
|
||||
+ case ETHTOOL_A_CABLE_PAIR_C:
|
||||
+ pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_C, status);
|
||||
+ break;
|
||||
+ case ETHTOOL_A_CABLE_PAIR_D:
|
||||
+ pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_D, status);
|
||||
+ break;
|
||||
+ default:
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ result = qca808x_cable_test_result_trans(pair_code);
|
||||
+ ethnl_cable_test_result(phydev, pair, result);
|
||||
+
|
||||
+ if (qca808x_cdt_fault_length_valid(pair_code)) {
|
||||
+ length = qca808x_cdt_fault_length(phydev, pair, result);
|
||||
+ ethnl_cable_test_fault_length(phydev, pair, length);
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+int qca808x_cable_test_get_status(struct phy_device *phydev, bool *finished)
|
||||
+{
|
||||
+ int ret, val;
|
||||
+
|
||||
+ *finished = false;
|
||||
+
|
||||
+ val = QCA808X_CDT_ENABLE_TEST |
|
||||
+ QCA808X_CDT_LENGTH_UNIT;
|
||||
+ ret = at803x_cdt_start(phydev, val);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ ret = at803x_cdt_wait_for_completion(phydev, QCA808X_CDT_ENABLE_TEST);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ val = phy_read_mmd(phydev, MDIO_MMD_PCS, QCA808X_MMD3_CDT_STATUS);
|
||||
+ if (val < 0)
|
||||
+ return val;
|
||||
+
|
||||
+ ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_A, val);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_B, val);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_C, val);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_D, val);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ *finished = true;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(qca808x_cable_test_get_status);
|
||||
--- a/drivers/net/phy/qcom/qcom.h
|
||||
+++ b/drivers/net/phy/qcom/qcom.h
|
||||
@@ -54,6 +54,55 @@
|
||||
#define AT803X_CDT_STATUS_STAT_MASK GENMASK(9, 8)
|
||||
#define AT803X_CDT_STATUS_DELTA_TIME_MASK GENMASK(7, 0)
|
||||
|
||||
+#define QCA808X_CDT_ENABLE_TEST BIT(15)
|
||||
+#define QCA808X_CDT_INTER_CHECK_DIS BIT(13)
|
||||
+#define QCA808X_CDT_STATUS BIT(11)
|
||||
+#define QCA808X_CDT_LENGTH_UNIT BIT(10)
|
||||
+
|
||||
+#define QCA808X_MMD3_CDT_STATUS 0x8064
|
||||
+#define QCA808X_MMD3_CDT_DIAG_PAIR_A 0x8065
|
||||
+#define QCA808X_MMD3_CDT_DIAG_PAIR_B 0x8066
|
||||
+#define QCA808X_MMD3_CDT_DIAG_PAIR_C 0x8067
|
||||
+#define QCA808X_MMD3_CDT_DIAG_PAIR_D 0x8068
|
||||
+#define QCA808X_CDT_DIAG_LENGTH_SAME_SHORT GENMASK(15, 8)
|
||||
+#define QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT GENMASK(7, 0)
|
||||
+
|
||||
+#define QCA808X_CDT_CODE_PAIR_A GENMASK(15, 12)
|
||||
+#define QCA808X_CDT_CODE_PAIR_B GENMASK(11, 8)
|
||||
+#define QCA808X_CDT_CODE_PAIR_C GENMASK(7, 4)
|
||||
+#define QCA808X_CDT_CODE_PAIR_D GENMASK(3, 0)
|
||||
+
|
||||
+#define QCA808X_CDT_STATUS_STAT_TYPE GENMASK(1, 0)
|
||||
+#define QCA808X_CDT_STATUS_STAT_FAIL FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 0)
|
||||
+#define QCA808X_CDT_STATUS_STAT_NORMAL FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 1)
|
||||
+#define QCA808X_CDT_STATUS_STAT_SAME_OPEN FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 2)
|
||||
+#define QCA808X_CDT_STATUS_STAT_SAME_SHORT FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 3)
|
||||
+
|
||||
+#define QCA808X_CDT_STATUS_STAT_MDI GENMASK(3, 2)
|
||||
+#define QCA808X_CDT_STATUS_STAT_MDI1 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 1)
|
||||
+#define QCA808X_CDT_STATUS_STAT_MDI2 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 2)
|
||||
+#define QCA808X_CDT_STATUS_STAT_MDI3 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 3)
|
||||
+
|
||||
+/* NORMAL are MDI with type set to 0 */
|
||||
+#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI1
|
||||
+#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
|
||||
+ QCA808X_CDT_STATUS_STAT_MDI1)
|
||||
+#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
|
||||
+ QCA808X_CDT_STATUS_STAT_MDI1)
|
||||
+#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI2
|
||||
+#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
|
||||
+ QCA808X_CDT_STATUS_STAT_MDI2)
|
||||
+#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
|
||||
+ QCA808X_CDT_STATUS_STAT_MDI2)
|
||||
+#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI3
|
||||
+#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
|
||||
+ QCA808X_CDT_STATUS_STAT_MDI3)
|
||||
+#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
|
||||
+ QCA808X_CDT_STATUS_STAT_MDI3)
|
||||
+
|
||||
+/* Added for reference of existence but should be handled by wait_for_completion already */
|
||||
+#define QCA808X_CDT_STATUS_STAT_BUSY (BIT(1) | BIT(3))
|
||||
+
|
||||
#define AT803X_LOC_MAC_ADDR_0_15_OFFSET 0x804C
|
||||
#define AT803X_LOC_MAC_ADDR_16_31_OFFSET 0x804B
|
||||
#define AT803X_LOC_MAC_ADDR_32_47_OFFSET 0x804A
|
||||
@@ -110,6 +159,7 @@ int at803x_read_specific_status(struct p
|
||||
struct at803x_ss_mask ss_mask);
|
||||
int at803x_config_mdix(struct phy_device *phydev, u8 ctrl);
|
||||
int at803x_prepare_config_aneg(struct phy_device *phydev);
|
||||
+int at803x_read_status(struct phy_device *phydev);
|
||||
int at803x_get_tunable(struct phy_device *phydev,
|
||||
struct ethtool_tunable *tuna, void *data);
|
||||
int at803x_set_tunable(struct phy_device *phydev,
|
||||
@@ -118,3 +168,4 @@ int at803x_cdt_fault_length(int dt);
|
||||
int at803x_cdt_start(struct phy_device *phydev, u32 cdt_start);
|
||||
int at803x_cdt_wait_for_completion(struct phy_device *phydev,
|
||||
u32 cdt_en);
|
||||
+int qca808x_cable_test_get_status(struct phy_device *phydev, bool *finished);
|
@ -0,0 +1,100 @@
|
||||
From 9b1d5e055508393561e26bd1720f4c2639b03b1a Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Tue, 6 Feb 2024 18:31:09 +0100
|
||||
Subject: [PATCH 06/10] net: phy: provide whether link has changed in
|
||||
c37_read_status
|
||||
|
||||
Some PHY driver might require additional regs call after
|
||||
genphy_c37_read_status() is called.
|
||||
|
||||
Expand genphy_c37_read_status to provide a bool wheather the link has
|
||||
changed or not to permit PHY driver to skip additional regs call if
|
||||
nothing has changed.
|
||||
|
||||
Every user of genphy_c37_read_status() is updated with the new
|
||||
additional bool.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/broadcom.c | 3 ++-
|
||||
drivers/net/phy/phy_device.c | 11 +++++++++--
|
||||
drivers/net/phy/qcom/at803x.c | 3 ++-
|
||||
include/linux/phy.h | 2 +-
|
||||
4 files changed, 14 insertions(+), 5 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/broadcom.c
|
||||
+++ b/drivers/net/phy/broadcom.c
|
||||
@@ -609,10 +609,11 @@ static int bcm54616s_config_aneg(struct
|
||||
static int bcm54616s_read_status(struct phy_device *phydev)
|
||||
{
|
||||
struct bcm54616s_phy_priv *priv = phydev->priv;
|
||||
+ bool changed;
|
||||
int err;
|
||||
|
||||
if (priv->mode_1000bx_en)
|
||||
- err = genphy_c37_read_status(phydev);
|
||||
+ err = genphy_c37_read_status(phydev, &changed);
|
||||
else
|
||||
err = genphy_read_status(phydev);
|
||||
|
||||
--- a/drivers/net/phy/phy_device.c
|
||||
+++ b/drivers/net/phy/phy_device.c
|
||||
@@ -2549,12 +2549,15 @@ EXPORT_SYMBOL(genphy_read_status);
|
||||
/**
|
||||
* genphy_c37_read_status - check the link status and update current link state
|
||||
* @phydev: target phy_device struct
|
||||
+ * @changed: pointer where to store if link changed
|
||||
*
|
||||
* Description: Check the link, then figure out the current state
|
||||
* by comparing what we advertise with what the link partner
|
||||
* advertises. This function is for Clause 37 1000Base-X mode.
|
||||
+ *
|
||||
+ * If link has changed, @changed is set to true, false otherwise.
|
||||
*/
|
||||
-int genphy_c37_read_status(struct phy_device *phydev)
|
||||
+int genphy_c37_read_status(struct phy_device *phydev, bool *changed)
|
||||
{
|
||||
int lpa, err, old_link = phydev->link;
|
||||
|
||||
@@ -2564,9 +2567,13 @@ int genphy_c37_read_status(struct phy_de
|
||||
return err;
|
||||
|
||||
/* why bother the PHY if nothing can have changed */
|
||||
- if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link)
|
||||
+ if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link) {
|
||||
+ *changed = false;
|
||||
return 0;
|
||||
+ }
|
||||
|
||||
+ /* Signal link has changed */
|
||||
+ *changed = true;
|
||||
phydev->duplex = DUPLEX_UNKNOWN;
|
||||
phydev->pause = 0;
|
||||
phydev->asym_pause = 0;
|
||||
--- a/drivers/net/phy/qcom/at803x.c
|
||||
+++ b/drivers/net/phy/qcom/at803x.c
|
||||
@@ -912,9 +912,10 @@ static int at8031_config_intr(struct phy
|
||||
static int at8031_read_status(struct phy_device *phydev)
|
||||
{
|
||||
struct at803x_priv *priv = phydev->priv;
|
||||
+ bool changed;
|
||||
|
||||
if (priv->is_1000basex)
|
||||
- return genphy_c37_read_status(phydev);
|
||||
+ return genphy_c37_read_status(phydev, &changed);
|
||||
|
||||
return at803x_read_status(phydev);
|
||||
}
|
||||
--- a/include/linux/phy.h
|
||||
+++ b/include/linux/phy.h
|
||||
@@ -1688,7 +1688,7 @@ int genphy_write_mmd_unsupported(struct
|
||||
|
||||
/* Clause 37 */
|
||||
int genphy_c37_config_aneg(struct phy_device *phydev);
|
||||
-int genphy_c37_read_status(struct phy_device *phydev);
|
||||
+int genphy_c37_read_status(struct phy_device *phydev, bool *changed);
|
||||
|
||||
/* Clause 45 PHY */
|
||||
int genphy_c45_restart_aneg(struct phy_device *phydev);
|
@ -0,0 +1,668 @@
|
||||
From d1cb613efbd3cd7d0c000167816beb3f248f5eb8 Mon Sep 17 00:00:00 2001
|
||||
From: Robert Marko <robert.marko@sartura.hr>
|
||||
Date: Tue, 6 Feb 2024 18:31:10 +0100
|
||||
Subject: [PATCH 07/10] net: phy: qcom: add support for QCA807x PHY Family
|
||||
|
||||
This adds driver for the Qualcomm QCA8072 and QCA8075 PHY-s.
|
||||
|
||||
They are 2 or 5 port IEEE 802.3 clause 22 compliant 10BASE-Te,
|
||||
100BASE-TX and 1000BASE-T PHY-s.
|
||||
|
||||
They feature 2 SerDes, one for PSGMII or QSGMII connection with
|
||||
MAC, while second one is SGMII for connection to MAC or fiber.
|
||||
|
||||
Both models have a combo port that supports 1000BASE-X and
|
||||
100BASE-FX fiber.
|
||||
|
||||
PHY package can be configured in 3 mode following this table:
|
||||
|
||||
First Serdes mode Second Serdes mode
|
||||
Option 1 PSGMII for copper Disabled
|
||||
ports 0-4
|
||||
Option 2 PSGMII for copper 1000BASE-X / 100BASE-FX
|
||||
ports 0-4
|
||||
Option 3 QSGMII for copper SGMII for
|
||||
ports 0-3 copper port 4
|
||||
|
||||
Each PHY inside of QCA807x series has 4 digitally controlled
|
||||
output only pins that natively drive LED-s.
|
||||
But some vendors used these to driver generic LED-s controlled
|
||||
by userspace, so lets enable registering each PHY as GPIO
|
||||
controller and add driver for it.
|
||||
|
||||
These are commonly used in Qualcomm IPQ40xx, IPQ60xx and IPQ807x
|
||||
boards.
|
||||
|
||||
Co-developed-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Signed-off-by: Robert Marko <robert.marko@sartura.hr>
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/qcom/Kconfig | 8 +
|
||||
drivers/net/phy/qcom/Makefile | 1 +
|
||||
drivers/net/phy/qcom/qca807x.c | 597 +++++++++++++++++++++++++++++++++
|
||||
3 files changed, 606 insertions(+)
|
||||
create mode 100644 drivers/net/phy/qcom/qca807x.c
|
||||
|
||||
--- a/drivers/net/phy/qcom/Kconfig
|
||||
+++ b/drivers/net/phy/qcom/Kconfig
|
||||
@@ -20,3 +20,11 @@ config QCA808X_PHY
|
||||
select QCOM_NET_PHYLIB
|
||||
help
|
||||
Currently supports the QCA8081 model
|
||||
+
|
||||
+config QCA807X_PHY
|
||||
+ tristate "Qualcomm QCA807x PHYs"
|
||||
+ select QCOM_NET_PHYLIB
|
||||
+ depends on OF_MDIO
|
||||
+ help
|
||||
+ Currently supports the Qualcomm QCA8072, QCA8075 and the PSGMII
|
||||
+ control PHY.
|
||||
--- a/drivers/net/phy/qcom/Makefile
|
||||
+++ b/drivers/net/phy/qcom/Makefile
|
||||
@@ -3,3 +3,4 @@ obj-$(CONFIG_QCOM_NET_PHYLIB) += qcom-ph
|
||||
obj-$(CONFIG_AT803X_PHY) += at803x.o
|
||||
obj-$(CONFIG_QCA83XX_PHY) += qca83xx.o
|
||||
obj-$(CONFIG_QCA808X_PHY) += qca808x.o
|
||||
+obj-$(CONFIG_QCA807X_PHY) += qca807x.o
|
||||
--- /dev/null
|
||||
+++ b/drivers/net/phy/qcom/qca807x.c
|
||||
@@ -0,0 +1,597 @@
|
||||
+// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
+/*
|
||||
+ * Copyright (c) 2023 Sartura Ltd.
|
||||
+ *
|
||||
+ * Author: Robert Marko <robert.marko@sartura.hr>
|
||||
+ * Christian Marangi <ansuelsmth@gmail.com>
|
||||
+ *
|
||||
+ * Qualcomm QCA8072 and QCA8075 PHY driver
|
||||
+ */
|
||||
+
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/of.h>
|
||||
+#include <linux/phy.h>
|
||||
+#include <linux/bitfield.h>
|
||||
+#include <linux/gpio/driver.h>
|
||||
+#include <linux/sfp.h>
|
||||
+
|
||||
+#include "qcom.h"
|
||||
+
|
||||
+#define QCA807X_CHIP_CONFIGURATION 0x1f
|
||||
+#define QCA807X_BT_BX_REG_SEL BIT(15)
|
||||
+#define QCA807X_BT_BX_REG_SEL_FIBER 0
|
||||
+#define QCA807X_BT_BX_REG_SEL_COPPER 1
|
||||
+#define QCA807X_CHIP_CONFIGURATION_MODE_CFG_MASK GENMASK(3, 0)
|
||||
+#define QCA807X_CHIP_CONFIGURATION_MODE_QSGMII_SGMII 4
|
||||
+#define QCA807X_CHIP_CONFIGURATION_MODE_PSGMII_FIBER 3
|
||||
+#define QCA807X_CHIP_CONFIGURATION_MODE_PSGMII_ALL_COPPER 0
|
||||
+
|
||||
+#define QCA807X_MEDIA_SELECT_STATUS 0x1a
|
||||
+#define QCA807X_MEDIA_DETECTED_COPPER BIT(5)
|
||||
+#define QCA807X_MEDIA_DETECTED_1000_BASE_X BIT(4)
|
||||
+#define QCA807X_MEDIA_DETECTED_100_BASE_FX BIT(3)
|
||||
+
|
||||
+#define QCA807X_MMD7_FIBER_MODE_AUTO_DETECTION 0x807e
|
||||
+#define QCA807X_MMD7_FIBER_MODE_AUTO_DETECTION_EN BIT(0)
|
||||
+
|
||||
+#define QCA807X_MMD7_1000BASE_T_POWER_SAVE_PER_CABLE_LENGTH 0x801a
|
||||
+#define QCA807X_CONTROL_DAC_MASK GENMASK(2, 0)
|
||||
+/* List of tweaks enabled by this bit:
|
||||
+ * - With both FULL amplitude and FULL bias current: bias current
|
||||
+ * is set to half.
|
||||
+ * - With only DSP amplitude: bias current is set to half and
|
||||
+ * is set to 1/4 with cable < 10m.
|
||||
+ * - With DSP bias current (included both DSP amplitude and
|
||||
+ * DSP bias current): bias current is half the detected current
|
||||
+ * with cable < 10m.
|
||||
+ */
|
||||
+#define QCA807X_CONTROL_DAC_BIAS_CURRENT_TWEAK BIT(2)
|
||||
+#define QCA807X_CONTROL_DAC_DSP_BIAS_CURRENT BIT(1)
|
||||
+#define QCA807X_CONTROL_DAC_DSP_AMPLITUDE BIT(0)
|
||||
+
|
||||
+#define QCA807X_MMD7_LED_100N_1 0x8074
|
||||
+#define QCA807X_MMD7_LED_100N_2 0x8075
|
||||
+#define QCA807X_MMD7_LED_1000N_1 0x8076
|
||||
+#define QCA807X_MMD7_LED_1000N_2 0x8077
|
||||
+
|
||||
+#define QCA807X_MMD7_LED_CTRL(x) (0x8074 + ((x) * 2))
|
||||
+#define QCA807X_MMD7_LED_FORCE_CTRL(x) (0x8075 + ((x) * 2))
|
||||
+
|
||||
+#define QCA807X_GPIO_FORCE_EN BIT(15)
|
||||
+#define QCA807X_GPIO_FORCE_MODE_MASK GENMASK(14, 13)
|
||||
+
|
||||
+#define QCA807X_FUNCTION_CONTROL 0x10
|
||||
+#define QCA807X_FC_MDI_CROSSOVER_MODE_MASK GENMASK(6, 5)
|
||||
+#define QCA807X_FC_MDI_CROSSOVER_AUTO 3
|
||||
+#define QCA807X_FC_MDI_CROSSOVER_MANUAL_MDIX 1
|
||||
+#define QCA807X_FC_MDI_CROSSOVER_MANUAL_MDI 0
|
||||
+
|
||||
+/* PQSGMII Analog PHY specific */
|
||||
+#define PQSGMII_CTRL_REG 0x0
|
||||
+#define PQSGMII_ANALOG_SW_RESET BIT(6)
|
||||
+#define PQSGMII_DRIVE_CONTROL_1 0xb
|
||||
+#define PQSGMII_TX_DRIVER_MASK GENMASK(7, 4)
|
||||
+#define PQSGMII_TX_DRIVER_140MV 0x0
|
||||
+#define PQSGMII_TX_DRIVER_160MV 0x1
|
||||
+#define PQSGMII_TX_DRIVER_180MV 0x2
|
||||
+#define PQSGMII_TX_DRIVER_200MV 0x3
|
||||
+#define PQSGMII_TX_DRIVER_220MV 0x4
|
||||
+#define PQSGMII_TX_DRIVER_240MV 0x5
|
||||
+#define PQSGMII_TX_DRIVER_260MV 0x6
|
||||
+#define PQSGMII_TX_DRIVER_280MV 0x7
|
||||
+#define PQSGMII_TX_DRIVER_300MV 0x8
|
||||
+#define PQSGMII_TX_DRIVER_320MV 0x9
|
||||
+#define PQSGMII_TX_DRIVER_400MV 0xa
|
||||
+#define PQSGMII_TX_DRIVER_500MV 0xb
|
||||
+#define PQSGMII_TX_DRIVER_600MV 0xc
|
||||
+#define PQSGMII_MODE_CTRL 0x6d
|
||||
+#define PQSGMII_MODE_CTRL_AZ_WORKAROUND_MASK BIT(0)
|
||||
+#define PQSGMII_MMD3_SERDES_CONTROL 0x805a
|
||||
+
|
||||
+#define PHY_ID_QCA8072 0x004dd0b2
|
||||
+#define PHY_ID_QCA8075 0x004dd0b1
|
||||
+
|
||||
+#define QCA807X_COMBO_ADDR_OFFSET 4
|
||||
+#define QCA807X_PQSGMII_ADDR_OFFSET 5
|
||||
+#define SERDES_RESET_SLEEP 100
|
||||
+
|
||||
+enum qca807x_global_phy {
|
||||
+ QCA807X_COMBO_ADDR = 4,
|
||||
+ QCA807X_PQSGMII_ADDR = 5,
|
||||
+};
|
||||
+
|
||||
+struct qca807x_shared_priv {
|
||||
+ unsigned int package_mode;
|
||||
+ u32 tx_drive_strength;
|
||||
+};
|
||||
+
|
||||
+struct qca807x_gpio_priv {
|
||||
+ struct phy_device *phy;
|
||||
+};
|
||||
+
|
||||
+struct qca807x_priv {
|
||||
+ bool dac_full_amplitude;
|
||||
+ bool dac_full_bias_current;
|
||||
+ bool dac_disable_bias_current_tweak;
|
||||
+};
|
||||
+
|
||||
+static int qca807x_cable_test_start(struct phy_device *phydev)
|
||||
+{
|
||||
+ /* we do all the (time consuming) work later */
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+#ifdef CONFIG_GPIOLIB
|
||||
+static int qca807x_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
|
||||
+{
|
||||
+ return GPIO_LINE_DIRECTION_OUT;
|
||||
+}
|
||||
+
|
||||
+static int qca807x_gpio_get(struct gpio_chip *gc, unsigned int offset)
|
||||
+{
|
||||
+ struct qca807x_gpio_priv *priv = gpiochip_get_data(gc);
|
||||
+ u16 reg;
|
||||
+ int val;
|
||||
+
|
||||
+ reg = QCA807X_MMD7_LED_FORCE_CTRL(offset);
|
||||
+ val = phy_read_mmd(priv->phy, MDIO_MMD_AN, reg);
|
||||
+
|
||||
+ return FIELD_GET(QCA807X_GPIO_FORCE_MODE_MASK, val);
|
||||
+}
|
||||
+
|
||||
+static void qca807x_gpio_set(struct gpio_chip *gc, unsigned int offset, int value)
|
||||
+{
|
||||
+ struct qca807x_gpio_priv *priv = gpiochip_get_data(gc);
|
||||
+ u16 reg;
|
||||
+ int val;
|
||||
+
|
||||
+ reg = QCA807X_MMD7_LED_FORCE_CTRL(offset);
|
||||
+
|
||||
+ val = phy_read_mmd(priv->phy, MDIO_MMD_AN, reg);
|
||||
+ val &= ~QCA807X_GPIO_FORCE_MODE_MASK;
|
||||
+ val |= QCA807X_GPIO_FORCE_EN;
|
||||
+ val |= FIELD_PREP(QCA807X_GPIO_FORCE_MODE_MASK, value);
|
||||
+
|
||||
+ phy_write_mmd(priv->phy, MDIO_MMD_AN, reg, val);
|
||||
+}
|
||||
+
|
||||
+static int qca807x_gpio_dir_out(struct gpio_chip *gc, unsigned int offset, int value)
|
||||
+{
|
||||
+ qca807x_gpio_set(gc, offset, value);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int qca807x_gpio(struct phy_device *phydev)
|
||||
+{
|
||||
+ struct device *dev = &phydev->mdio.dev;
|
||||
+ struct qca807x_gpio_priv *priv;
|
||||
+ struct gpio_chip *gc;
|
||||
+
|
||||
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
+ if (!priv)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ priv->phy = phydev;
|
||||
+
|
||||
+ gc = devm_kzalloc(dev, sizeof(*gc), GFP_KERNEL);
|
||||
+ if (!gc)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ gc->label = dev_name(dev);
|
||||
+ gc->base = -1;
|
||||
+ gc->ngpio = 2;
|
||||
+ gc->parent = dev;
|
||||
+ gc->owner = THIS_MODULE;
|
||||
+ gc->can_sleep = true;
|
||||
+ gc->get_direction = qca807x_gpio_get_direction;
|
||||
+ gc->direction_output = qca807x_gpio_dir_out;
|
||||
+ gc->get = qca807x_gpio_get;
|
||||
+ gc->set = qca807x_gpio_set;
|
||||
+
|
||||
+ return devm_gpiochip_add_data(dev, gc, priv);
|
||||
+}
|
||||
+#endif
|
||||
+
|
||||
+static int qca807x_read_fiber_status(struct phy_device *phydev)
|
||||
+{
|
||||
+ bool changed;
|
||||
+ int ss, err;
|
||||
+
|
||||
+ err = genphy_c37_read_status(phydev, &changed);
|
||||
+ if (err || !changed)
|
||||
+ return err;
|
||||
+
|
||||
+ /* Read the QCA807x PHY-Specific Status register fiber page,
|
||||
+ * which indicates the speed and duplex that the PHY is actually
|
||||
+ * using, irrespective of whether we are in autoneg mode or not.
|
||||
+ */
|
||||
+ ss = phy_read(phydev, AT803X_SPECIFIC_STATUS);
|
||||
+ if (ss < 0)
|
||||
+ return ss;
|
||||
+
|
||||
+ phydev->speed = SPEED_UNKNOWN;
|
||||
+ phydev->duplex = DUPLEX_UNKNOWN;
|
||||
+ if (ss & AT803X_SS_SPEED_DUPLEX_RESOLVED) {
|
||||
+ switch (FIELD_GET(AT803X_SS_SPEED_MASK, ss)) {
|
||||
+ case AT803X_SS_SPEED_100:
|
||||
+ phydev->speed = SPEED_100;
|
||||
+ break;
|
||||
+ case AT803X_SS_SPEED_1000:
|
||||
+ phydev->speed = SPEED_1000;
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ if (ss & AT803X_SS_DUPLEX)
|
||||
+ phydev->duplex = DUPLEX_FULL;
|
||||
+ else
|
||||
+ phydev->duplex = DUPLEX_HALF;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int qca807x_read_status(struct phy_device *phydev)
|
||||
+{
|
||||
+ if (linkmode_test_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, phydev->supported)) {
|
||||
+ switch (phydev->port) {
|
||||
+ case PORT_FIBRE:
|
||||
+ return qca807x_read_fiber_status(phydev);
|
||||
+ case PORT_TP:
|
||||
+ return at803x_read_status(phydev);
|
||||
+ default:
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return at803x_read_status(phydev);
|
||||
+}
|
||||
+
|
||||
+static int qca807x_phy_package_probe_once(struct phy_device *phydev)
|
||||
+{
|
||||
+ struct phy_package_shared *shared = phydev->shared;
|
||||
+ struct qca807x_shared_priv *priv = shared->priv;
|
||||
+ unsigned int tx_drive_strength;
|
||||
+ const char *package_mode_name;
|
||||
+
|
||||
+ /* Default to 600mw if not defined */
|
||||
+ if (of_property_read_u32(shared->np, "qcom,tx-drive-strength-milliwatt",
|
||||
+ &tx_drive_strength))
|
||||
+ tx_drive_strength = 600;
|
||||
+
|
||||
+ switch (tx_drive_strength) {
|
||||
+ case 140:
|
||||
+ priv->tx_drive_strength = PQSGMII_TX_DRIVER_140MV;
|
||||
+ break;
|
||||
+ case 160:
|
||||
+ priv->tx_drive_strength = PQSGMII_TX_DRIVER_160MV;
|
||||
+ break;
|
||||
+ case 180:
|
||||
+ priv->tx_drive_strength = PQSGMII_TX_DRIVER_180MV;
|
||||
+ break;
|
||||
+ case 200:
|
||||
+ priv->tx_drive_strength = PQSGMII_TX_DRIVER_200MV;
|
||||
+ break;
|
||||
+ case 220:
|
||||
+ priv->tx_drive_strength = PQSGMII_TX_DRIVER_220MV;
|
||||
+ break;
|
||||
+ case 240:
|
||||
+ priv->tx_drive_strength = PQSGMII_TX_DRIVER_240MV;
|
||||
+ break;
|
||||
+ case 260:
|
||||
+ priv->tx_drive_strength = PQSGMII_TX_DRIVER_260MV;
|
||||
+ break;
|
||||
+ case 280:
|
||||
+ priv->tx_drive_strength = PQSGMII_TX_DRIVER_280MV;
|
||||
+ break;
|
||||
+ case 300:
|
||||
+ priv->tx_drive_strength = PQSGMII_TX_DRIVER_300MV;
|
||||
+ break;
|
||||
+ case 320:
|
||||
+ priv->tx_drive_strength = PQSGMII_TX_DRIVER_320MV;
|
||||
+ break;
|
||||
+ case 400:
|
||||
+ priv->tx_drive_strength = PQSGMII_TX_DRIVER_400MV;
|
||||
+ break;
|
||||
+ case 500:
|
||||
+ priv->tx_drive_strength = PQSGMII_TX_DRIVER_500MV;
|
||||
+ break;
|
||||
+ case 600:
|
||||
+ priv->tx_drive_strength = PQSGMII_TX_DRIVER_600MV;
|
||||
+ break;
|
||||
+ default:
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ priv->package_mode = PHY_INTERFACE_MODE_NA;
|
||||
+ if (!of_property_read_string(shared->np, "qcom,package-mode",
|
||||
+ &package_mode_name)) {
|
||||
+ if (!strcasecmp(package_mode_name,
|
||||
+ phy_modes(PHY_INTERFACE_MODE_PSGMII)))
|
||||
+ priv->package_mode = PHY_INTERFACE_MODE_PSGMII;
|
||||
+ else if (!strcasecmp(package_mode_name,
|
||||
+ phy_modes(PHY_INTERFACE_MODE_QSGMII)))
|
||||
+ priv->package_mode = PHY_INTERFACE_MODE_QSGMII;
|
||||
+ else
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int qca807x_phy_package_config_init_once(struct phy_device *phydev)
|
||||
+{
|
||||
+ struct phy_package_shared *shared = phydev->shared;
|
||||
+ struct qca807x_shared_priv *priv = shared->priv;
|
||||
+ int val, ret;
|
||||
+
|
||||
+ phy_lock_mdio_bus(phydev);
|
||||
+
|
||||
+ /* Set correct PHY package mode */
|
||||
+ val = __phy_package_read(phydev, QCA807X_COMBO_ADDR,
|
||||
+ QCA807X_CHIP_CONFIGURATION);
|
||||
+ val &= ~QCA807X_CHIP_CONFIGURATION_MODE_CFG_MASK;
|
||||
+ /* package_mode can be QSGMII or PSGMII and we validate
|
||||
+ * this in probe_once.
|
||||
+ * With package_mode to NA, we default to PSGMII.
|
||||
+ */
|
||||
+ switch (priv->package_mode) {
|
||||
+ case PHY_INTERFACE_MODE_QSGMII:
|
||||
+ val |= QCA807X_CHIP_CONFIGURATION_MODE_QSGMII_SGMII;
|
||||
+ break;
|
||||
+ case PHY_INTERFACE_MODE_PSGMII:
|
||||
+ default:
|
||||
+ val |= QCA807X_CHIP_CONFIGURATION_MODE_PSGMII_ALL_COPPER;
|
||||
+ }
|
||||
+ ret = __phy_package_write(phydev, QCA807X_COMBO_ADDR,
|
||||
+ QCA807X_CHIP_CONFIGURATION, val);
|
||||
+ if (ret)
|
||||
+ goto exit;
|
||||
+
|
||||
+ /* After mode change Serdes reset is required */
|
||||
+ val = __phy_package_read(phydev, QCA807X_PQSGMII_ADDR,
|
||||
+ PQSGMII_CTRL_REG);
|
||||
+ val &= ~PQSGMII_ANALOG_SW_RESET;
|
||||
+ ret = __phy_package_write(phydev, QCA807X_PQSGMII_ADDR,
|
||||
+ PQSGMII_CTRL_REG, val);
|
||||
+ if (ret)
|
||||
+ goto exit;
|
||||
+
|
||||
+ msleep(SERDES_RESET_SLEEP);
|
||||
+
|
||||
+ val = __phy_package_read(phydev, QCA807X_PQSGMII_ADDR,
|
||||
+ PQSGMII_CTRL_REG);
|
||||
+ val |= PQSGMII_ANALOG_SW_RESET;
|
||||
+ ret = __phy_package_write(phydev, QCA807X_PQSGMII_ADDR,
|
||||
+ PQSGMII_CTRL_REG, val);
|
||||
+ if (ret)
|
||||
+ goto exit;
|
||||
+
|
||||
+ /* Workaround to enable AZ transmitting ability */
|
||||
+ val = __phy_package_read_mmd(phydev, QCA807X_PQSGMII_ADDR,
|
||||
+ MDIO_MMD_PMAPMD, PQSGMII_MODE_CTRL);
|
||||
+ val &= ~PQSGMII_MODE_CTRL_AZ_WORKAROUND_MASK;
|
||||
+ ret = __phy_package_write_mmd(phydev, QCA807X_PQSGMII_ADDR,
|
||||
+ MDIO_MMD_PMAPMD, PQSGMII_MODE_CTRL, val);
|
||||
+ if (ret)
|
||||
+ goto exit;
|
||||
+
|
||||
+ /* Set PQSGMII TX AMP strength */
|
||||
+ val = __phy_package_read(phydev, QCA807X_PQSGMII_ADDR,
|
||||
+ PQSGMII_DRIVE_CONTROL_1);
|
||||
+ val &= ~PQSGMII_TX_DRIVER_MASK;
|
||||
+ val |= FIELD_PREP(PQSGMII_TX_DRIVER_MASK, priv->tx_drive_strength);
|
||||
+ ret = __phy_package_write(phydev, QCA807X_PQSGMII_ADDR,
|
||||
+ PQSGMII_DRIVE_CONTROL_1, val);
|
||||
+ if (ret)
|
||||
+ goto exit;
|
||||
+
|
||||
+ /* Prevent PSGMII going into hibernation via PSGMII self test */
|
||||
+ val = __phy_package_read_mmd(phydev, QCA807X_COMBO_ADDR,
|
||||
+ MDIO_MMD_PCS, PQSGMII_MMD3_SERDES_CONTROL);
|
||||
+ val &= ~BIT(1);
|
||||
+ ret = __phy_package_write_mmd(phydev, QCA807X_COMBO_ADDR,
|
||||
+ MDIO_MMD_PCS, PQSGMII_MMD3_SERDES_CONTROL, val);
|
||||
+
|
||||
+exit:
|
||||
+ phy_unlock_mdio_bus(phydev);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static int qca807x_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
|
||||
+{
|
||||
+ struct phy_device *phydev = upstream;
|
||||
+ __ETHTOOL_DECLARE_LINK_MODE_MASK(support) = { 0, };
|
||||
+ phy_interface_t iface;
|
||||
+ int ret;
|
||||
+ DECLARE_PHY_INTERFACE_MASK(interfaces);
|
||||
+
|
||||
+ sfp_parse_support(phydev->sfp_bus, id, support, interfaces);
|
||||
+ iface = sfp_select_interface(phydev->sfp_bus, support);
|
||||
+
|
||||
+ dev_info(&phydev->mdio.dev, "%s SFP module inserted\n", phy_modes(iface));
|
||||
+
|
||||
+ switch (iface) {
|
||||
+ case PHY_INTERFACE_MODE_1000BASEX:
|
||||
+ case PHY_INTERFACE_MODE_100BASEX:
|
||||
+ /* Set PHY mode to PSGMII combo (1/4 copper + combo ports) mode */
|
||||
+ ret = phy_modify(phydev,
|
||||
+ QCA807X_CHIP_CONFIGURATION,
|
||||
+ QCA807X_CHIP_CONFIGURATION_MODE_CFG_MASK,
|
||||
+ QCA807X_CHIP_CONFIGURATION_MODE_PSGMII_FIBER);
|
||||
+ /* Enable fiber mode autodection (1000Base-X or 100Base-FX) */
|
||||
+ ret = phy_set_bits_mmd(phydev,
|
||||
+ MDIO_MMD_AN,
|
||||
+ QCA807X_MMD7_FIBER_MODE_AUTO_DETECTION,
|
||||
+ QCA807X_MMD7_FIBER_MODE_AUTO_DETECTION_EN);
|
||||
+ /* Select fiber page */
|
||||
+ ret = phy_clear_bits(phydev,
|
||||
+ QCA807X_CHIP_CONFIGURATION,
|
||||
+ QCA807X_BT_BX_REG_SEL);
|
||||
+
|
||||
+ phydev->port = PORT_FIBRE;
|
||||
+ break;
|
||||
+ default:
|
||||
+ dev_err(&phydev->mdio.dev, "Incompatible SFP module inserted\n");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static void qca807x_sfp_remove(void *upstream)
|
||||
+{
|
||||
+ struct phy_device *phydev = upstream;
|
||||
+
|
||||
+ /* Select copper page */
|
||||
+ phy_set_bits(phydev,
|
||||
+ QCA807X_CHIP_CONFIGURATION,
|
||||
+ QCA807X_BT_BX_REG_SEL);
|
||||
+
|
||||
+ phydev->port = PORT_TP;
|
||||
+}
|
||||
+
|
||||
+static const struct sfp_upstream_ops qca807x_sfp_ops = {
|
||||
+ .attach = phy_sfp_attach,
|
||||
+ .detach = phy_sfp_detach,
|
||||
+ .module_insert = qca807x_sfp_insert,
|
||||
+ .module_remove = qca807x_sfp_remove,
|
||||
+};
|
||||
+
|
||||
+static int qca807x_probe(struct phy_device *phydev)
|
||||
+{
|
||||
+ struct device_node *node = phydev->mdio.dev.of_node;
|
||||
+ struct qca807x_shared_priv *shared_priv;
|
||||
+ struct device *dev = &phydev->mdio.dev;
|
||||
+ struct phy_package_shared *shared;
|
||||
+ struct qca807x_priv *priv;
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = devm_of_phy_package_join(dev, phydev, sizeof(*shared_priv));
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ if (phy_package_probe_once(phydev)) {
|
||||
+ ret = qca807x_phy_package_probe_once(phydev);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ shared = phydev->shared;
|
||||
+ shared_priv = shared->priv;
|
||||
+
|
||||
+ /* Make sure PHY follow PHY package mode if enforced */
|
||||
+ if (shared_priv->package_mode != PHY_INTERFACE_MODE_NA &&
|
||||
+ phydev->interface != shared_priv->package_mode)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
+ if (!priv)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ priv->dac_full_amplitude = of_property_read_bool(node, "qcom,dac-full-amplitude");
|
||||
+ priv->dac_full_bias_current = of_property_read_bool(node, "qcom,dac-full-bias-current");
|
||||
+ priv->dac_disable_bias_current_tweak = of_property_read_bool(node,
|
||||
+ "qcom,dac-disable-bias-current-tweak");
|
||||
+
|
||||
+ if (IS_ENABLED(CONFIG_GPIOLIB)) {
|
||||
+ /* Do not register a GPIO controller unless flagged for it */
|
||||
+ if (of_property_read_bool(node, "gpio-controller")) {
|
||||
+ ret = qca807x_gpio(phydev);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /* Attach SFP bus on combo port*/
|
||||
+ if (phy_read(phydev, QCA807X_CHIP_CONFIGURATION)) {
|
||||
+ ret = phy_sfp_probe(phydev, &qca807x_sfp_ops);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, phydev->supported);
|
||||
+ linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, phydev->advertising);
|
||||
+ }
|
||||
+
|
||||
+ phydev->priv = priv;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int qca807x_config_init(struct phy_device *phydev)
|
||||
+{
|
||||
+ struct qca807x_priv *priv = phydev->priv;
|
||||
+ u16 control_dac;
|
||||
+ int ret;
|
||||
+
|
||||
+ if (phy_package_init_once(phydev)) {
|
||||
+ ret = qca807x_phy_package_config_init_once(phydev);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ control_dac = phy_read_mmd(phydev, MDIO_MMD_AN,
|
||||
+ QCA807X_MMD7_1000BASE_T_POWER_SAVE_PER_CABLE_LENGTH);
|
||||
+ control_dac &= ~QCA807X_CONTROL_DAC_MASK;
|
||||
+ if (!priv->dac_full_amplitude)
|
||||
+ control_dac |= QCA807X_CONTROL_DAC_DSP_AMPLITUDE;
|
||||
+ if (!priv->dac_full_amplitude)
|
||||
+ control_dac |= QCA807X_CONTROL_DAC_DSP_BIAS_CURRENT;
|
||||
+ if (!priv->dac_disable_bias_current_tweak)
|
||||
+ control_dac |= QCA807X_CONTROL_DAC_BIAS_CURRENT_TWEAK;
|
||||
+ return phy_write_mmd(phydev, MDIO_MMD_AN,
|
||||
+ QCA807X_MMD7_1000BASE_T_POWER_SAVE_PER_CABLE_LENGTH,
|
||||
+ control_dac);
|
||||
+}
|
||||
+
|
||||
+static struct phy_driver qca807x_drivers[] = {
|
||||
+ {
|
||||
+ PHY_ID_MATCH_EXACT(PHY_ID_QCA8072),
|
||||
+ .name = "Qualcomm QCA8072",
|
||||
+ .flags = PHY_POLL_CABLE_TEST,
|
||||
+ /* PHY_GBIT_FEATURES */
|
||||
+ .probe = qca807x_probe,
|
||||
+ .config_init = qca807x_config_init,
|
||||
+ .read_status = qca807x_read_status,
|
||||
+ .config_intr = at803x_config_intr,
|
||||
+ .handle_interrupt = at803x_handle_interrupt,
|
||||
+ .soft_reset = genphy_soft_reset,
|
||||
+ .get_tunable = at803x_get_tunable,
|
||||
+ .set_tunable = at803x_set_tunable,
|
||||
+ .resume = genphy_resume,
|
||||
+ .suspend = genphy_suspend,
|
||||
+ .cable_test_start = qca807x_cable_test_start,
|
||||
+ .cable_test_get_status = qca808x_cable_test_get_status,
|
||||
+ },
|
||||
+ {
|
||||
+ PHY_ID_MATCH_EXACT(PHY_ID_QCA8075),
|
||||
+ .name = "Qualcomm QCA8075",
|
||||
+ .flags = PHY_POLL_CABLE_TEST,
|
||||
+ /* PHY_GBIT_FEATURES */
|
||||
+ .probe = qca807x_probe,
|
||||
+ .config_init = qca807x_config_init,
|
||||
+ .read_status = qca807x_read_status,
|
||||
+ .config_intr = at803x_config_intr,
|
||||
+ .handle_interrupt = at803x_handle_interrupt,
|
||||
+ .soft_reset = genphy_soft_reset,
|
||||
+ .get_tunable = at803x_get_tunable,
|
||||
+ .set_tunable = at803x_set_tunable,
|
||||
+ .resume = genphy_resume,
|
||||
+ .suspend = genphy_suspend,
|
||||
+ .cable_test_start = qca807x_cable_test_start,
|
||||
+ .cable_test_get_status = qca808x_cable_test_get_status,
|
||||
+ },
|
||||
+};
|
||||
+module_phy_driver(qca807x_drivers);
|
||||
+
|
||||
+static struct mdio_device_id __maybe_unused qca807x_tbl[] = {
|
||||
+ { PHY_ID_MATCH_EXACT(PHY_ID_QCA8072) },
|
||||
+ { PHY_ID_MATCH_EXACT(PHY_ID_QCA8075) },
|
||||
+ { }
|
||||
+};
|
||||
+
|
||||
+MODULE_AUTHOR("Robert Marko <robert.marko@sartura.hr>");
|
||||
+MODULE_AUTHOR("Christian Marangi <ansuelsmth@gmail.com>");
|
||||
+MODULE_DESCRIPTION("Qualcomm QCA807x PHY driver");
|
||||
+MODULE_DEVICE_TABLE(mdio, qca807x_tbl);
|
||||
+MODULE_LICENSE("GPL");
|
@ -0,0 +1,179 @@
|
||||
From ee9d9807bee0e6af8ca2a4db6f0d1dc0e5b41f44 Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Tue, 6 Feb 2024 18:31:11 +0100
|
||||
Subject: [PATCH 08/10] net: phy: qcom: move common qca808x LED define to
|
||||
shared header
|
||||
|
||||
The LED implementation of qca808x and qca807x is the same but qca807x
|
||||
supports also Fiber port and have different hw control bits for Fiber
|
||||
port.
|
||||
|
||||
In preparation for qca807x introduction, move all the common define to
|
||||
shared header.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/qcom/qca808x.c | 65 ----------------------------------
|
||||
drivers/net/phy/qcom/qcom.h | 65 ++++++++++++++++++++++++++++++++++
|
||||
2 files changed, 65 insertions(+), 65 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/qcom/qca808x.c
|
||||
+++ b/drivers/net/phy/qcom/qca808x.c
|
||||
@@ -62,29 +62,6 @@
|
||||
#define QCA808X_DBG_AN_TEST 0xb
|
||||
#define QCA808X_HIBERNATION_EN BIT(15)
|
||||
|
||||
-#define QCA808X_MMD7_LED_GLOBAL 0x8073
|
||||
-#define QCA808X_LED_BLINK_1 GENMASK(11, 6)
|
||||
-#define QCA808X_LED_BLINK_2 GENMASK(5, 0)
|
||||
-/* Values are the same for both BLINK_1 and BLINK_2 */
|
||||
-#define QCA808X_LED_BLINK_FREQ_MASK GENMASK(5, 3)
|
||||
-#define QCA808X_LED_BLINK_FREQ_2HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x0)
|
||||
-#define QCA808X_LED_BLINK_FREQ_4HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x1)
|
||||
-#define QCA808X_LED_BLINK_FREQ_8HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x2)
|
||||
-#define QCA808X_LED_BLINK_FREQ_16HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x3)
|
||||
-#define QCA808X_LED_BLINK_FREQ_32HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x4)
|
||||
-#define QCA808X_LED_BLINK_FREQ_64HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x5)
|
||||
-#define QCA808X_LED_BLINK_FREQ_128HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x6)
|
||||
-#define QCA808X_LED_BLINK_FREQ_256HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x7)
|
||||
-#define QCA808X_LED_BLINK_DUTY_MASK GENMASK(2, 0)
|
||||
-#define QCA808X_LED_BLINK_DUTY_50_50 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x0)
|
||||
-#define QCA808X_LED_BLINK_DUTY_75_25 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x1)
|
||||
-#define QCA808X_LED_BLINK_DUTY_25_75 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x2)
|
||||
-#define QCA808X_LED_BLINK_DUTY_33_67 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x3)
|
||||
-#define QCA808X_LED_BLINK_DUTY_67_33 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x4)
|
||||
-#define QCA808X_LED_BLINK_DUTY_17_83 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x5)
|
||||
-#define QCA808X_LED_BLINK_DUTY_83_17 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x6)
|
||||
-#define QCA808X_LED_BLINK_DUTY_8_92 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x7)
|
||||
-
|
||||
#define QCA808X_MMD7_LED2_CTRL 0x8074
|
||||
#define QCA808X_MMD7_LED2_FORCE_CTRL 0x8075
|
||||
#define QCA808X_MMD7_LED1_CTRL 0x8076
|
||||
@@ -92,51 +69,9 @@
|
||||
#define QCA808X_MMD7_LED0_CTRL 0x8078
|
||||
#define QCA808X_MMD7_LED_CTRL(x) (0x8078 - ((x) * 2))
|
||||
|
||||
-/* LED hw control pattern is the same for every LED */
|
||||
-#define QCA808X_LED_PATTERN_MASK GENMASK(15, 0)
|
||||
-#define QCA808X_LED_SPEED2500_ON BIT(15)
|
||||
-#define QCA808X_LED_SPEED2500_BLINK BIT(14)
|
||||
-/* Follow blink trigger even if duplex or speed condition doesn't match */
|
||||
-#define QCA808X_LED_BLINK_CHECK_BYPASS BIT(13)
|
||||
-#define QCA808X_LED_FULL_DUPLEX_ON BIT(12)
|
||||
-#define QCA808X_LED_HALF_DUPLEX_ON BIT(11)
|
||||
-#define QCA808X_LED_TX_BLINK BIT(10)
|
||||
-#define QCA808X_LED_RX_BLINK BIT(9)
|
||||
-#define QCA808X_LED_TX_ON_10MS BIT(8)
|
||||
-#define QCA808X_LED_RX_ON_10MS BIT(7)
|
||||
-#define QCA808X_LED_SPEED1000_ON BIT(6)
|
||||
-#define QCA808X_LED_SPEED100_ON BIT(5)
|
||||
-#define QCA808X_LED_SPEED10_ON BIT(4)
|
||||
-#define QCA808X_LED_COLLISION_BLINK BIT(3)
|
||||
-#define QCA808X_LED_SPEED1000_BLINK BIT(2)
|
||||
-#define QCA808X_LED_SPEED100_BLINK BIT(1)
|
||||
-#define QCA808X_LED_SPEED10_BLINK BIT(0)
|
||||
-
|
||||
#define QCA808X_MMD7_LED0_FORCE_CTRL 0x8079
|
||||
#define QCA808X_MMD7_LED_FORCE_CTRL(x) (0x8079 - ((x) * 2))
|
||||
|
||||
-/* LED force ctrl is the same for every LED
|
||||
- * No documentation exist for this, not even internal one
|
||||
- * with NDA as QCOM gives only info about configuring
|
||||
- * hw control pattern rules and doesn't indicate any way
|
||||
- * to force the LED to specific mode.
|
||||
- * These define comes from reverse and testing and maybe
|
||||
- * lack of some info or some info are not entirely correct.
|
||||
- * For the basic LED control and hw control these finding
|
||||
- * are enough to support LED control in all the required APIs.
|
||||
- *
|
||||
- * On doing some comparison with implementation with qca807x,
|
||||
- * it was found that it's 1:1 equal to it and confirms all the
|
||||
- * reverse done. It was also found further specification with the
|
||||
- * force mode and the blink modes.
|
||||
- */
|
||||
-#define QCA808X_LED_FORCE_EN BIT(15)
|
||||
-#define QCA808X_LED_FORCE_MODE_MASK GENMASK(14, 13)
|
||||
-#define QCA808X_LED_FORCE_BLINK_1 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x3)
|
||||
-#define QCA808X_LED_FORCE_BLINK_2 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x2)
|
||||
-#define QCA808X_LED_FORCE_ON FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x1)
|
||||
-#define QCA808X_LED_FORCE_OFF FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x0)
|
||||
-
|
||||
#define QCA808X_MMD7_LED_POLARITY_CTRL 0x901a
|
||||
/* QSDK sets by default 0x46 to this reg that sets BIT 6 for
|
||||
* LED to active high. It's not clear what BIT 3 and BIT 4 does.
|
||||
--- a/drivers/net/phy/qcom/qcom.h
|
||||
+++ b/drivers/net/phy/qcom/qcom.h
|
||||
@@ -103,6 +103,71 @@
|
||||
/* Added for reference of existence but should be handled by wait_for_completion already */
|
||||
#define QCA808X_CDT_STATUS_STAT_BUSY (BIT(1) | BIT(3))
|
||||
|
||||
+#define QCA808X_MMD7_LED_GLOBAL 0x8073
|
||||
+#define QCA808X_LED_BLINK_1 GENMASK(11, 6)
|
||||
+#define QCA808X_LED_BLINK_2 GENMASK(5, 0)
|
||||
+/* Values are the same for both BLINK_1 and BLINK_2 */
|
||||
+#define QCA808X_LED_BLINK_FREQ_MASK GENMASK(5, 3)
|
||||
+#define QCA808X_LED_BLINK_FREQ_2HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x0)
|
||||
+#define QCA808X_LED_BLINK_FREQ_4HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x1)
|
||||
+#define QCA808X_LED_BLINK_FREQ_8HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x2)
|
||||
+#define QCA808X_LED_BLINK_FREQ_16HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x3)
|
||||
+#define QCA808X_LED_BLINK_FREQ_32HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x4)
|
||||
+#define QCA808X_LED_BLINK_FREQ_64HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x5)
|
||||
+#define QCA808X_LED_BLINK_FREQ_128HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x6)
|
||||
+#define QCA808X_LED_BLINK_FREQ_256HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x7)
|
||||
+#define QCA808X_LED_BLINK_DUTY_MASK GENMASK(2, 0)
|
||||
+#define QCA808X_LED_BLINK_DUTY_50_50 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x0)
|
||||
+#define QCA808X_LED_BLINK_DUTY_75_25 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x1)
|
||||
+#define QCA808X_LED_BLINK_DUTY_25_75 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x2)
|
||||
+#define QCA808X_LED_BLINK_DUTY_33_67 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x3)
|
||||
+#define QCA808X_LED_BLINK_DUTY_67_33 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x4)
|
||||
+#define QCA808X_LED_BLINK_DUTY_17_83 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x5)
|
||||
+#define QCA808X_LED_BLINK_DUTY_83_17 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x6)
|
||||
+#define QCA808X_LED_BLINK_DUTY_8_92 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x7)
|
||||
+
|
||||
+/* LED hw control pattern is the same for every LED */
|
||||
+#define QCA808X_LED_PATTERN_MASK GENMASK(15, 0)
|
||||
+#define QCA808X_LED_SPEED2500_ON BIT(15)
|
||||
+#define QCA808X_LED_SPEED2500_BLINK BIT(14)
|
||||
+/* Follow blink trigger even if duplex or speed condition doesn't match */
|
||||
+#define QCA808X_LED_BLINK_CHECK_BYPASS BIT(13)
|
||||
+#define QCA808X_LED_FULL_DUPLEX_ON BIT(12)
|
||||
+#define QCA808X_LED_HALF_DUPLEX_ON BIT(11)
|
||||
+#define QCA808X_LED_TX_BLINK BIT(10)
|
||||
+#define QCA808X_LED_RX_BLINK BIT(9)
|
||||
+#define QCA808X_LED_TX_ON_10MS BIT(8)
|
||||
+#define QCA808X_LED_RX_ON_10MS BIT(7)
|
||||
+#define QCA808X_LED_SPEED1000_ON BIT(6)
|
||||
+#define QCA808X_LED_SPEED100_ON BIT(5)
|
||||
+#define QCA808X_LED_SPEED10_ON BIT(4)
|
||||
+#define QCA808X_LED_COLLISION_BLINK BIT(3)
|
||||
+#define QCA808X_LED_SPEED1000_BLINK BIT(2)
|
||||
+#define QCA808X_LED_SPEED100_BLINK BIT(1)
|
||||
+#define QCA808X_LED_SPEED10_BLINK BIT(0)
|
||||
+
|
||||
+/* LED force ctrl is the same for every LED
|
||||
+ * No documentation exist for this, not even internal one
|
||||
+ * with NDA as QCOM gives only info about configuring
|
||||
+ * hw control pattern rules and doesn't indicate any way
|
||||
+ * to force the LED to specific mode.
|
||||
+ * These define comes from reverse and testing and maybe
|
||||
+ * lack of some info or some info are not entirely correct.
|
||||
+ * For the basic LED control and hw control these finding
|
||||
+ * are enough to support LED control in all the required APIs.
|
||||
+ *
|
||||
+ * On doing some comparison with implementation with qca807x,
|
||||
+ * it was found that it's 1:1 equal to it and confirms all the
|
||||
+ * reverse done. It was also found further specification with the
|
||||
+ * force mode and the blink modes.
|
||||
+ */
|
||||
+#define QCA808X_LED_FORCE_EN BIT(15)
|
||||
+#define QCA808X_LED_FORCE_MODE_MASK GENMASK(14, 13)
|
||||
+#define QCA808X_LED_FORCE_BLINK_1 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x3)
|
||||
+#define QCA808X_LED_FORCE_BLINK_2 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x2)
|
||||
+#define QCA808X_LED_FORCE_ON FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x1)
|
||||
+#define QCA808X_LED_FORCE_OFF FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x0)
|
||||
+
|
||||
#define AT803X_LOC_MAC_ADDR_0_15_OFFSET 0x804C
|
||||
#define AT803X_LOC_MAC_ADDR_16_31_OFFSET 0x804B
|
||||
#define AT803X_LOC_MAC_ADDR_32_47_OFFSET 0x804A
|
@ -0,0 +1,172 @@
|
||||
From 47b930d0dd437af927145dba50a2e2ea1ba97c67 Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Tue, 6 Feb 2024 18:31:12 +0100
|
||||
Subject: [PATCH 09/10] net: phy: qcom: generalize some qca808x LED functions
|
||||
|
||||
Generalize some qca808x LED functions in preparation for qca807x LED
|
||||
support.
|
||||
|
||||
The LED implementation of qca808x and qca807x is the same but qca807x
|
||||
supports also Fiber port and have different hw control bits for Fiber
|
||||
port. To limit code duplication introduce micro functions that takes reg
|
||||
instead of LED index to tweak all the supported LED modes.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/qcom/qca808x.c | 38 +++-----------------
|
||||
drivers/net/phy/qcom/qcom-phy-lib.c | 54 +++++++++++++++++++++++++++++
|
||||
drivers/net/phy/qcom/qcom.h | 7 ++++
|
||||
3 files changed, 65 insertions(+), 34 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/qcom/qca808x.c
|
||||
+++ b/drivers/net/phy/qcom/qca808x.c
|
||||
@@ -437,9 +437,7 @@ static int qca808x_led_hw_control_enable
|
||||
return -EINVAL;
|
||||
|
||||
reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
|
||||
-
|
||||
- return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg,
|
||||
- QCA808X_LED_FORCE_EN);
|
||||
+ return qca808x_led_reg_hw_control_enable(phydev, reg);
|
||||
}
|
||||
|
||||
static int qca808x_led_hw_is_supported(struct phy_device *phydev, u8 index,
|
||||
@@ -480,16 +478,12 @@ static int qca808x_led_hw_control_set(st
|
||||
static bool qca808x_led_hw_control_status(struct phy_device *phydev, u8 index)
|
||||
{
|
||||
u16 reg;
|
||||
- int val;
|
||||
|
||||
if (index > 2)
|
||||
return false;
|
||||
|
||||
reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
|
||||
-
|
||||
- val = phy_read_mmd(phydev, MDIO_MMD_AN, reg);
|
||||
-
|
||||
- return !(val & QCA808X_LED_FORCE_EN);
|
||||
+ return qca808x_led_reg_hw_control_status(phydev, reg);
|
||||
}
|
||||
|
||||
static int qca808x_led_hw_control_get(struct phy_device *phydev, u8 index,
|
||||
@@ -557,44 +551,20 @@ static int qca808x_led_brightness_set(st
|
||||
}
|
||||
|
||||
reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
|
||||
-
|
||||
- return phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
|
||||
- QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK,
|
||||
- QCA808X_LED_FORCE_EN | (value ? QCA808X_LED_FORCE_ON :
|
||||
- QCA808X_LED_FORCE_OFF));
|
||||
+ return qca808x_led_reg_brightness_set(phydev, reg, value);
|
||||
}
|
||||
|
||||
static int qca808x_led_blink_set(struct phy_device *phydev, u8 index,
|
||||
unsigned long *delay_on,
|
||||
unsigned long *delay_off)
|
||||
{
|
||||
- int ret;
|
||||
u16 reg;
|
||||
|
||||
if (index > 2)
|
||||
return -EINVAL;
|
||||
|
||||
reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
|
||||
-
|
||||
- /* Set blink to 50% off, 50% on at 4Hz by default */
|
||||
- ret = phy_modify_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_LED_GLOBAL,
|
||||
- QCA808X_LED_BLINK_FREQ_MASK | QCA808X_LED_BLINK_DUTY_MASK,
|
||||
- QCA808X_LED_BLINK_FREQ_4HZ | QCA808X_LED_BLINK_DUTY_50_50);
|
||||
- if (ret)
|
||||
- return ret;
|
||||
-
|
||||
- /* We use BLINK_1 for normal blinking */
|
||||
- ret = phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
|
||||
- QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK,
|
||||
- QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_BLINK_1);
|
||||
- if (ret)
|
||||
- return ret;
|
||||
-
|
||||
- /* We set blink to 4Hz, aka 250ms */
|
||||
- *delay_on = 250 / 2;
|
||||
- *delay_off = 250 / 2;
|
||||
-
|
||||
- return 0;
|
||||
+ return qca808x_led_reg_blink_set(phydev, reg, delay_on, delay_off);
|
||||
}
|
||||
|
||||
static int qca808x_led_polarity_set(struct phy_device *phydev, int index,
|
||||
--- a/drivers/net/phy/qcom/qcom-phy-lib.c
|
||||
+++ b/drivers/net/phy/qcom/qcom-phy-lib.c
|
||||
@@ -620,3 +620,57 @@ int qca808x_cable_test_get_status(struct
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(qca808x_cable_test_get_status);
|
||||
+
|
||||
+int qca808x_led_reg_hw_control_enable(struct phy_device *phydev, u16 reg)
|
||||
+{
|
||||
+ return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg,
|
||||
+ QCA808X_LED_FORCE_EN);
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(qca808x_led_reg_hw_control_enable);
|
||||
+
|
||||
+bool qca808x_led_reg_hw_control_status(struct phy_device *phydev, u16 reg)
|
||||
+{
|
||||
+ int val;
|
||||
+
|
||||
+ val = phy_read_mmd(phydev, MDIO_MMD_AN, reg);
|
||||
+ return !(val & QCA808X_LED_FORCE_EN);
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(qca808x_led_reg_hw_control_status);
|
||||
+
|
||||
+int qca808x_led_reg_brightness_set(struct phy_device *phydev,
|
||||
+ u16 reg, enum led_brightness value)
|
||||
+{
|
||||
+ return phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
|
||||
+ QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK,
|
||||
+ QCA808X_LED_FORCE_EN | (value ? QCA808X_LED_FORCE_ON :
|
||||
+ QCA808X_LED_FORCE_OFF));
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(qca808x_led_reg_brightness_set);
|
||||
+
|
||||
+int qca808x_led_reg_blink_set(struct phy_device *phydev, u16 reg,
|
||||
+ unsigned long *delay_on,
|
||||
+ unsigned long *delay_off)
|
||||
+{
|
||||
+ int ret;
|
||||
+
|
||||
+ /* Set blink to 50% off, 50% on at 4Hz by default */
|
||||
+ ret = phy_modify_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_LED_GLOBAL,
|
||||
+ QCA808X_LED_BLINK_FREQ_MASK | QCA808X_LED_BLINK_DUTY_MASK,
|
||||
+ QCA808X_LED_BLINK_FREQ_4HZ | QCA808X_LED_BLINK_DUTY_50_50);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ /* We use BLINK_1 for normal blinking */
|
||||
+ ret = phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
|
||||
+ QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK,
|
||||
+ QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_BLINK_1);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ /* We set blink to 4Hz, aka 250ms */
|
||||
+ *delay_on = 250 / 2;
|
||||
+ *delay_off = 250 / 2;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(qca808x_led_reg_blink_set);
|
||||
--- a/drivers/net/phy/qcom/qcom.h
|
||||
+++ b/drivers/net/phy/qcom/qcom.h
|
||||
@@ -234,3 +234,10 @@ int at803x_cdt_start(struct phy_device *
|
||||
int at803x_cdt_wait_for_completion(struct phy_device *phydev,
|
||||
u32 cdt_en);
|
||||
int qca808x_cable_test_get_status(struct phy_device *phydev, bool *finished);
|
||||
+int qca808x_led_reg_hw_control_enable(struct phy_device *phydev, u16 reg);
|
||||
+bool qca808x_led_reg_hw_control_status(struct phy_device *phydev, u16 reg);
|
||||
+int qca808x_led_reg_brightness_set(struct phy_device *phydev,
|
||||
+ u16 reg, enum led_brightness value);
|
||||
+int qca808x_led_reg_blink_set(struct phy_device *phydev, u16 reg,
|
||||
+ unsigned long *delay_on,
|
||||
+ unsigned long *delay_off);
|
@ -0,0 +1,326 @@
|
||||
From f508a226b517a6a8afd78a317de46bc83e3e3d51 Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Tue, 6 Feb 2024 18:31:13 +0100
|
||||
Subject: [PATCH 10/10] net: phy: qca807x: add support for configurable LED
|
||||
|
||||
QCA8072/5 have up to 2 LEDs attached for PHY.
|
||||
|
||||
LEDs can be configured to be ON/hw blink or be set to HW control.
|
||||
|
||||
Hw blink mode is set to blink at 4Hz or 250ms.
|
||||
|
||||
PHY can support both copper (TP) or fiber (FIBRE) kind and supports
|
||||
different HW control modes based on the port type.
|
||||
|
||||
HW control modes supported for netdev trigger for copper ports are:
|
||||
- LINK_10
|
||||
- LINK_100
|
||||
- LINK_1000
|
||||
- TX
|
||||
- RX
|
||||
- FULL_DUPLEX
|
||||
- HALF_DUPLEX
|
||||
|
||||
HW control modes supported for netdev trigger for fiber ports are:
|
||||
- LINK_100
|
||||
- LINK_1000
|
||||
- TX
|
||||
- RX
|
||||
- FULL_DUPLEX
|
||||
- HALF_DUPLEX
|
||||
|
||||
LED support conflicts with GPIO controller feature and must be disabled
|
||||
if gpio-controller is used for the PHY.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/qcom/qca807x.c | 256 ++++++++++++++++++++++++++++++++-
|
||||
1 file changed, 254 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/qcom/qca807x.c
|
||||
+++ b/drivers/net/phy/qcom/qca807x.c
|
||||
@@ -57,8 +57,18 @@
|
||||
#define QCA807X_MMD7_LED_CTRL(x) (0x8074 + ((x) * 2))
|
||||
#define QCA807X_MMD7_LED_FORCE_CTRL(x) (0x8075 + ((x) * 2))
|
||||
|
||||
-#define QCA807X_GPIO_FORCE_EN BIT(15)
|
||||
-#define QCA807X_GPIO_FORCE_MODE_MASK GENMASK(14, 13)
|
||||
+/* LED hw control pattern for fiber port */
|
||||
+#define QCA807X_LED_FIBER_PATTERN_MASK GENMASK(11, 1)
|
||||
+#define QCA807X_LED_FIBER_TXACT_BLK_EN BIT(10)
|
||||
+#define QCA807X_LED_FIBER_RXACT_BLK_EN BIT(9)
|
||||
+#define QCA807X_LED_FIBER_FDX_ON_EN BIT(6)
|
||||
+#define QCA807X_LED_FIBER_HDX_ON_EN BIT(5)
|
||||
+#define QCA807X_LED_FIBER_1000BX_ON_EN BIT(2)
|
||||
+#define QCA807X_LED_FIBER_100FX_ON_EN BIT(1)
|
||||
+
|
||||
+/* Some device repurpose the LED as GPIO out */
|
||||
+#define QCA807X_GPIO_FORCE_EN QCA808X_LED_FORCE_EN
|
||||
+#define QCA807X_GPIO_FORCE_MODE_MASK QCA808X_LED_FORCE_MODE_MASK
|
||||
|
||||
#define QCA807X_FUNCTION_CONTROL 0x10
|
||||
#define QCA807X_FC_MDI_CROSSOVER_MODE_MASK GENMASK(6, 5)
|
||||
@@ -121,6 +131,233 @@ static int qca807x_cable_test_start(stru
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static int qca807x_led_parse_netdev(struct phy_device *phydev, unsigned long rules,
|
||||
+ u16 *offload_trigger)
|
||||
+{
|
||||
+ /* Parsing specific to netdev trigger */
|
||||
+ switch (phydev->port) {
|
||||
+ case PORT_TP:
|
||||
+ if (test_bit(TRIGGER_NETDEV_TX, &rules))
|
||||
+ *offload_trigger |= QCA808X_LED_TX_BLINK;
|
||||
+ if (test_bit(TRIGGER_NETDEV_RX, &rules))
|
||||
+ *offload_trigger |= QCA808X_LED_RX_BLINK;
|
||||
+ if (test_bit(TRIGGER_NETDEV_LINK_10, &rules))
|
||||
+ *offload_trigger |= QCA808X_LED_SPEED10_ON;
|
||||
+ if (test_bit(TRIGGER_NETDEV_LINK_100, &rules))
|
||||
+ *offload_trigger |= QCA808X_LED_SPEED100_ON;
|
||||
+ if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules))
|
||||
+ *offload_trigger |= QCA808X_LED_SPEED1000_ON;
|
||||
+ if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &rules))
|
||||
+ *offload_trigger |= QCA808X_LED_HALF_DUPLEX_ON;
|
||||
+ if (test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &rules))
|
||||
+ *offload_trigger |= QCA808X_LED_FULL_DUPLEX_ON;
|
||||
+ break;
|
||||
+ case PORT_FIBRE:
|
||||
+ if (test_bit(TRIGGER_NETDEV_TX, &rules))
|
||||
+ *offload_trigger |= QCA807X_LED_FIBER_TXACT_BLK_EN;
|
||||
+ if (test_bit(TRIGGER_NETDEV_RX, &rules))
|
||||
+ *offload_trigger |= QCA807X_LED_FIBER_RXACT_BLK_EN;
|
||||
+ if (test_bit(TRIGGER_NETDEV_LINK_100, &rules))
|
||||
+ *offload_trigger |= QCA807X_LED_FIBER_100FX_ON_EN;
|
||||
+ if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules))
|
||||
+ *offload_trigger |= QCA807X_LED_FIBER_1000BX_ON_EN;
|
||||
+ if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &rules))
|
||||
+ *offload_trigger |= QCA807X_LED_FIBER_HDX_ON_EN;
|
||||
+ if (test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &rules))
|
||||
+ *offload_trigger |= QCA807X_LED_FIBER_FDX_ON_EN;
|
||||
+ break;
|
||||
+ default:
|
||||
+ return -EOPNOTSUPP;
|
||||
+ }
|
||||
+
|
||||
+ if (rules && !*offload_trigger)
|
||||
+ return -EOPNOTSUPP;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int qca807x_led_hw_control_enable(struct phy_device *phydev, u8 index)
|
||||
+{
|
||||
+ u16 reg;
|
||||
+
|
||||
+ if (index > 1)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ reg = QCA807X_MMD7_LED_FORCE_CTRL(index);
|
||||
+ return qca808x_led_reg_hw_control_enable(phydev, reg);
|
||||
+}
|
||||
+
|
||||
+static int qca807x_led_hw_is_supported(struct phy_device *phydev, u8 index,
|
||||
+ unsigned long rules)
|
||||
+{
|
||||
+ u16 offload_trigger = 0;
|
||||
+
|
||||
+ if (index > 1)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ return qca807x_led_parse_netdev(phydev, rules, &offload_trigger);
|
||||
+}
|
||||
+
|
||||
+static int qca807x_led_hw_control_set(struct phy_device *phydev, u8 index,
|
||||
+ unsigned long rules)
|
||||
+{
|
||||
+ u16 reg, mask, offload_trigger = 0;
|
||||
+ int ret;
|
||||
+
|
||||
+ if (index > 1)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ ret = qca807x_led_parse_netdev(phydev, rules, &offload_trigger);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ ret = qca807x_led_hw_control_enable(phydev, index);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ switch (phydev->port) {
|
||||
+ case PORT_TP:
|
||||
+ reg = QCA807X_MMD7_LED_CTRL(index);
|
||||
+ mask = QCA808X_LED_PATTERN_MASK;
|
||||
+ break;
|
||||
+ case PORT_FIBRE:
|
||||
+ /* HW control pattern bits are in LED FORCE reg */
|
||||
+ reg = QCA807X_MMD7_LED_FORCE_CTRL(index);
|
||||
+ mask = QCA807X_LED_FIBER_PATTERN_MASK;
|
||||
+ break;
|
||||
+ default:
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ return phy_modify_mmd(phydev, MDIO_MMD_AN, reg, mask,
|
||||
+ offload_trigger);
|
||||
+}
|
||||
+
|
||||
+static bool qca807x_led_hw_control_status(struct phy_device *phydev, u8 index)
|
||||
+{
|
||||
+ u16 reg;
|
||||
+
|
||||
+ if (index > 1)
|
||||
+ return false;
|
||||
+
|
||||
+ reg = QCA807X_MMD7_LED_FORCE_CTRL(index);
|
||||
+ return qca808x_led_reg_hw_control_status(phydev, reg);
|
||||
+}
|
||||
+
|
||||
+static int qca807x_led_hw_control_get(struct phy_device *phydev, u8 index,
|
||||
+ unsigned long *rules)
|
||||
+{
|
||||
+ u16 reg;
|
||||
+ int val;
|
||||
+
|
||||
+ if (index > 1)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ /* Check if we have hw control enabled */
|
||||
+ if (qca807x_led_hw_control_status(phydev, index))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ /* Parsing specific to netdev trigger */
|
||||
+ switch (phydev->port) {
|
||||
+ case PORT_TP:
|
||||
+ reg = QCA807X_MMD7_LED_CTRL(index);
|
||||
+ val = phy_read_mmd(phydev, MDIO_MMD_AN, reg);
|
||||
+ if (val & QCA808X_LED_TX_BLINK)
|
||||
+ set_bit(TRIGGER_NETDEV_TX, rules);
|
||||
+ if (val & QCA808X_LED_RX_BLINK)
|
||||
+ set_bit(TRIGGER_NETDEV_RX, rules);
|
||||
+ if (val & QCA808X_LED_SPEED10_ON)
|
||||
+ set_bit(TRIGGER_NETDEV_LINK_10, rules);
|
||||
+ if (val & QCA808X_LED_SPEED100_ON)
|
||||
+ set_bit(TRIGGER_NETDEV_LINK_100, rules);
|
||||
+ if (val & QCA808X_LED_SPEED1000_ON)
|
||||
+ set_bit(TRIGGER_NETDEV_LINK_1000, rules);
|
||||
+ if (val & QCA808X_LED_HALF_DUPLEX_ON)
|
||||
+ set_bit(TRIGGER_NETDEV_HALF_DUPLEX, rules);
|
||||
+ if (val & QCA808X_LED_FULL_DUPLEX_ON)
|
||||
+ set_bit(TRIGGER_NETDEV_FULL_DUPLEX, rules);
|
||||
+ break;
|
||||
+ case PORT_FIBRE:
|
||||
+ /* HW control pattern bits are in LED FORCE reg */
|
||||
+ reg = QCA807X_MMD7_LED_FORCE_CTRL(index);
|
||||
+ val = phy_read_mmd(phydev, MDIO_MMD_AN, reg);
|
||||
+ if (val & QCA807X_LED_FIBER_TXACT_BLK_EN)
|
||||
+ set_bit(TRIGGER_NETDEV_TX, rules);
|
||||
+ if (val & QCA807X_LED_FIBER_RXACT_BLK_EN)
|
||||
+ set_bit(TRIGGER_NETDEV_RX, rules);
|
||||
+ if (val & QCA807X_LED_FIBER_100FX_ON_EN)
|
||||
+ set_bit(TRIGGER_NETDEV_LINK_100, rules);
|
||||
+ if (val & QCA807X_LED_FIBER_1000BX_ON_EN)
|
||||
+ set_bit(TRIGGER_NETDEV_LINK_1000, rules);
|
||||
+ if (val & QCA807X_LED_FIBER_HDX_ON_EN)
|
||||
+ set_bit(TRIGGER_NETDEV_HALF_DUPLEX, rules);
|
||||
+ if (val & QCA807X_LED_FIBER_FDX_ON_EN)
|
||||
+ set_bit(TRIGGER_NETDEV_FULL_DUPLEX, rules);
|
||||
+ break;
|
||||
+ default:
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int qca807x_led_hw_control_reset(struct phy_device *phydev, u8 index)
|
||||
+{
|
||||
+ u16 reg, mask;
|
||||
+
|
||||
+ if (index > 1)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ switch (phydev->port) {
|
||||
+ case PORT_TP:
|
||||
+ reg = QCA807X_MMD7_LED_CTRL(index);
|
||||
+ mask = QCA808X_LED_PATTERN_MASK;
|
||||
+ break;
|
||||
+ case PORT_FIBRE:
|
||||
+ /* HW control pattern bits are in LED FORCE reg */
|
||||
+ reg = QCA807X_MMD7_LED_FORCE_CTRL(index);
|
||||
+ mask = QCA807X_LED_FIBER_PATTERN_MASK;
|
||||
+ break;
|
||||
+ default:
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg, mask);
|
||||
+}
|
||||
+
|
||||
+static int qca807x_led_brightness_set(struct phy_device *phydev,
|
||||
+ u8 index, enum led_brightness value)
|
||||
+{
|
||||
+ u16 reg;
|
||||
+ int ret;
|
||||
+
|
||||
+ if (index > 1)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ /* If we are setting off the LED reset any hw control rule */
|
||||
+ if (!value) {
|
||||
+ ret = qca807x_led_hw_control_reset(phydev, index);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ reg = QCA807X_MMD7_LED_FORCE_CTRL(index);
|
||||
+ return qca808x_led_reg_brightness_set(phydev, reg, value);
|
||||
+}
|
||||
+
|
||||
+static int qca807x_led_blink_set(struct phy_device *phydev, u8 index,
|
||||
+ unsigned long *delay_on,
|
||||
+ unsigned long *delay_off)
|
||||
+{
|
||||
+ u16 reg;
|
||||
+
|
||||
+ if (index > 1)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ reg = QCA807X_MMD7_LED_FORCE_CTRL(index);
|
||||
+ return qca808x_led_reg_blink_set(phydev, reg, delay_on, delay_off);
|
||||
+}
|
||||
+
|
||||
#ifdef CONFIG_GPIOLIB
|
||||
static int qca807x_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
|
||||
{
|
||||
@@ -496,6 +733,16 @@ static int qca807x_probe(struct phy_devi
|
||||
"qcom,dac-disable-bias-current-tweak");
|
||||
|
||||
if (IS_ENABLED(CONFIG_GPIOLIB)) {
|
||||
+ /* Make sure we don't have mixed leds node and gpio-controller
|
||||
+ * to prevent registering leds and having gpio-controller usage
|
||||
+ * conflicting with them.
|
||||
+ */
|
||||
+ if (of_find_property(node, "leds", NULL) &&
|
||||
+ of_find_property(node, "gpio-controller", NULL)) {
|
||||
+ phydev_err(phydev, "Invalid property detected. LEDs and gpio-controller are mutually exclusive.");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
/* Do not register a GPIO controller unless flagged for it */
|
||||
if (of_property_read_bool(node, "gpio-controller")) {
|
||||
ret = qca807x_gpio(phydev);
|
||||
@@ -580,6 +827,11 @@ static struct phy_driver qca807x_drivers
|
||||
.suspend = genphy_suspend,
|
||||
.cable_test_start = qca807x_cable_test_start,
|
||||
.cable_test_get_status = qca808x_cable_test_get_status,
|
||||
+ .led_brightness_set = qca807x_led_brightness_set,
|
||||
+ .led_blink_set = qca807x_led_blink_set,
|
||||
+ .led_hw_is_supported = qca807x_led_hw_is_supported,
|
||||
+ .led_hw_control_set = qca807x_led_hw_control_set,
|
||||
+ .led_hw_control_get = qca807x_led_hw_control_get,
|
||||
},
|
||||
};
|
||||
module_phy_driver(qca807x_drivers);
|
@ -0,0 +1,51 @@
|
||||
From 3be0d950b62852a693182cb678948f481de02825 Mon Sep 17 00:00:00 2001
|
||||
From: Robert Marko <robimarko@gmail.com>
|
||||
Date: Mon, 12 Feb 2024 12:49:34 +0100
|
||||
Subject: [PATCH] net: phy: qca807x: move interface mode check to
|
||||
.config_init_once
|
||||
|
||||
Currently, we are checking whether the PHY package mode matches the
|
||||
individual PHY interface modes at PHY package probe time, but at that time
|
||||
we only know the PHY package mode and not the individual PHY interface
|
||||
modes as of_get_phy_mode() that populates it will only get called once the
|
||||
netdev to which PHY-s are attached to is being probed and thus this check
|
||||
will always fail and return -EINVAL.
|
||||
|
||||
So, lets move this check to .config_init_once as at that point individual
|
||||
PHY interface modes should be populated.
|
||||
|
||||
Fixes: d1cb613efbd3 ("net: phy: qcom: add support for QCA807x PHY Family")
|
||||
Signed-off-by: Robert Marko <robimarko@gmail.com>
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Link: https://lore.kernel.org/r/20240212115043.1725918-1-robimarko@gmail.com
|
||||
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
|
||||
---
|
||||
drivers/net/phy/qcom/qca807x.c | 10 +++++-----
|
||||
1 file changed, 5 insertions(+), 5 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/qcom/qca807x.c
|
||||
+++ b/drivers/net/phy/qcom/qca807x.c
|
||||
@@ -562,6 +562,11 @@ static int qca807x_phy_package_config_in
|
||||
struct qca807x_shared_priv *priv = shared->priv;
|
||||
int val, ret;
|
||||
|
||||
+ /* Make sure PHY follow PHY package mode if enforced */
|
||||
+ if (priv->package_mode != PHY_INTERFACE_MODE_NA &&
|
||||
+ phydev->interface != priv->package_mode)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
phy_lock_mdio_bus(phydev);
|
||||
|
||||
/* Set correct PHY package mode */
|
||||
@@ -718,11 +723,6 @@ static int qca807x_probe(struct phy_devi
|
||||
shared = phydev->shared;
|
||||
shared_priv = shared->priv;
|
||||
|
||||
- /* Make sure PHY follow PHY package mode if enforced */
|
||||
- if (shared_priv->package_mode != PHY_INTERFACE_MODE_NA &&
|
||||
- phydev->interface != shared_priv->package_mode)
|
||||
- return -EINVAL;
|
||||
-
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
@ -0,0 +1,45 @@
|
||||
From 6a4aee277740d04ac0fd54cfa17cc28261932ddc Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Mon, 25 Mar 2024 20:06:19 +0100
|
||||
Subject: [PATCH] net: phy: qcom: at803x: fix kernel panic with at8031_probe
|
||||
|
||||
On reworking and splitting the at803x driver, in splitting function of
|
||||
at803x PHYs it was added a NULL dereference bug where priv is referenced
|
||||
before it's actually allocated and then is tried to write to for the
|
||||
is_1000basex and is_fiber variables in the case of at8031, writing on
|
||||
the wrong address.
|
||||
|
||||
Fix this by correctly setting priv local variable only after
|
||||
at803x_probe is called and actually allocates priv in the phydev struct.
|
||||
|
||||
Reported-by: William Wortel <wwortel@dorpstraat.com>
|
||||
Cc: <stable@vger.kernel.org>
|
||||
Fixes: 25d2ba94005f ("net: phy: at803x: move specific at8031 probe mode check to dedicated probe")
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Link: https://lore.kernel.org/r/20240325190621.2665-1-ansuelsmth@gmail.com
|
||||
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
|
||||
---
|
||||
drivers/net/phy/qcom/at803x.c | 4 +++-
|
||||
1 file changed, 3 insertions(+), 1 deletion(-)
|
||||
|
||||
--- a/drivers/net/phy/qcom/at803x.c
|
||||
+++ b/drivers/net/phy/qcom/at803x.c
|
||||
@@ -797,7 +797,7 @@ static int at8031_parse_dt(struct phy_de
|
||||
|
||||
static int at8031_probe(struct phy_device *phydev)
|
||||
{
|
||||
- struct at803x_priv *priv = phydev->priv;
|
||||
+ struct at803x_priv *priv;
|
||||
int mode_cfg;
|
||||
int ccr;
|
||||
int ret;
|
||||
@@ -806,6 +806,8 @@ static int at8031_probe(struct phy_devic
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
+ priv = phydev->priv;
|
||||
+
|
||||
/* Only supported on AR8031/AR8033, the AR8030/AR8035 use strapping
|
||||
* options.
|
||||
*/
|
@ -0,0 +1,205 @@
|
||||
From bdce82e960d1205d118662f575cec39379984e34 Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Wed, 31 Jan 2024 03:26:04 +0100
|
||||
Subject: [PATCH] net: mdio: ipq4019: add support for clock-frequency property
|
||||
|
||||
The IPQ4019 MDIO internally divide the clock feed by AHB based on the
|
||||
MDIO_MODE reg. On reset or power up, the default value for the
|
||||
divider is 0xff that reflect the divider set to /256.
|
||||
|
||||
This makes the MDC run at a very low rate, that is, considering AHB is
|
||||
always fixed to 100Mhz, a value of 390KHz.
|
||||
|
||||
This hasn't have been a problem as MDIO wasn't used for time sensitive
|
||||
operation, it is now that on IPQ807x is usually mounted with PHY that
|
||||
requires MDIO to load their firmware (example Aquantia PHY).
|
||||
|
||||
To handle this problem and permit to set the correct designed MDC
|
||||
frequency for the SoC add support for the standard "clock-frequency"
|
||||
property for the MDIO node.
|
||||
|
||||
The divider supports value from /1 to /256 and the common value are to
|
||||
set it to /16 to reflect 6.25Mhz or to /8 on newer platform to reflect
|
||||
12.5Mhz.
|
||||
|
||||
To scan if the requested rate is supported by the divider, loop with
|
||||
each supported divider and stop when the requested rate match the final
|
||||
rate with the current divider. An error is returned if the rate doesn't
|
||||
match any value.
|
||||
|
||||
On MDIO reset, the divider is restored to the requested value to prevent
|
||||
any kind of downclocking caused by the divider reverting to a default
|
||||
value.
|
||||
|
||||
To follow 802.3 spec of 2.5MHz of default value, if divider is set at
|
||||
/256 and "clock-frequency" is not set in DT, assume nobody set the
|
||||
divider and try to find the closest MDC rate to 2.5MHz. (in the case of
|
||||
AHB set to 100MHz, it's 1.5625MHz)
|
||||
|
||||
While at is also document other bits of the MDIO_MODE reg to have a
|
||||
clear idea of what is actually applied there.
|
||||
|
||||
Documentation of some BITs is skipped as they are marked as reserved and
|
||||
their usage is not clear (RES 11:9 GENPHY 16:13 RES1 19:17)
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/mdio/mdio-ipq4019.c | 109 ++++++++++++++++++++++++++++++--
|
||||
1 file changed, 103 insertions(+), 6 deletions(-)
|
||||
|
||||
--- a/drivers/net/mdio/mdio-ipq4019.c
|
||||
+++ b/drivers/net/mdio/mdio-ipq4019.c
|
||||
@@ -14,6 +14,20 @@
|
||||
#include <linux/clk.h>
|
||||
|
||||
#define MDIO_MODE_REG 0x40
|
||||
+#define MDIO_MODE_MDC_MODE BIT(12)
|
||||
+/* 0 = Clause 22, 1 = Clause 45 */
|
||||
+#define MDIO_MODE_C45 BIT(8)
|
||||
+#define MDIO_MODE_DIV_MASK GENMASK(7, 0)
|
||||
+#define MDIO_MODE_DIV(x) FIELD_PREP(MDIO_MODE_DIV_MASK, (x) - 1)
|
||||
+#define MDIO_MODE_DIV_1 0x0
|
||||
+#define MDIO_MODE_DIV_2 0x1
|
||||
+#define MDIO_MODE_DIV_4 0x3
|
||||
+#define MDIO_MODE_DIV_8 0x7
|
||||
+#define MDIO_MODE_DIV_16 0xf
|
||||
+#define MDIO_MODE_DIV_32 0x1f
|
||||
+#define MDIO_MODE_DIV_64 0x3f
|
||||
+#define MDIO_MODE_DIV_128 0x7f
|
||||
+#define MDIO_MODE_DIV_256 0xff
|
||||
#define MDIO_ADDR_REG 0x44
|
||||
#define MDIO_DATA_WRITE_REG 0x48
|
||||
#define MDIO_DATA_READ_REG 0x4c
|
||||
@@ -26,9 +40,6 @@
|
||||
#define MDIO_CMD_ACCESS_CODE_C45_WRITE 1
|
||||
#define MDIO_CMD_ACCESS_CODE_C45_READ 2
|
||||
|
||||
-/* 0 = Clause 22, 1 = Clause 45 */
|
||||
-#define MDIO_MODE_C45 BIT(8)
|
||||
-
|
||||
#define IPQ4019_MDIO_TIMEOUT 10000
|
||||
#define IPQ4019_MDIO_SLEEP 10
|
||||
|
||||
@@ -41,6 +52,7 @@ struct ipq4019_mdio_data {
|
||||
void __iomem *membase;
|
||||
void __iomem *eth_ldo_rdy;
|
||||
struct clk *mdio_clk;
|
||||
+ unsigned int mdc_rate;
|
||||
};
|
||||
|
||||
static int ipq4019_mdio_wait_busy(struct mii_bus *bus)
|
||||
@@ -179,6 +191,38 @@ static int ipq4019_mdio_write(struct mii
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static int ipq4019_mdio_set_div(struct ipq4019_mdio_data *priv)
|
||||
+{
|
||||
+ unsigned long ahb_rate;
|
||||
+ int div;
|
||||
+ u32 val;
|
||||
+
|
||||
+ /* If we don't have a clock for AHB use the fixed value */
|
||||
+ ahb_rate = IPQ_MDIO_CLK_RATE;
|
||||
+ if (priv->mdio_clk)
|
||||
+ ahb_rate = clk_get_rate(priv->mdio_clk);
|
||||
+
|
||||
+ /* MDC rate is ahb_rate/(MDIO_MODE_DIV + 1)
|
||||
+ * While supported, internal documentation doesn't
|
||||
+ * assure correct functionality of the MDIO bus
|
||||
+ * with divider of 1, 2 or 4.
|
||||
+ */
|
||||
+ for (div = 8; div <= 256; div *= 2) {
|
||||
+ /* The requested rate is supported by the div */
|
||||
+ if (priv->mdc_rate == DIV_ROUND_UP(ahb_rate, div)) {
|
||||
+ val = readl(priv->membase + MDIO_MODE_REG);
|
||||
+ val &= ~MDIO_MODE_DIV_MASK;
|
||||
+ val |= MDIO_MODE_DIV(div);
|
||||
+ writel(val, priv->membase + MDIO_MODE_REG);
|
||||
+
|
||||
+ return 0;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /* The requested rate is not supported */
|
||||
+ return -EINVAL;
|
||||
+}
|
||||
+
|
||||
static int ipq_mdio_reset(struct mii_bus *bus)
|
||||
{
|
||||
struct ipq4019_mdio_data *priv = bus->priv;
|
||||
@@ -201,10 +245,58 @@ static int ipq_mdio_reset(struct mii_bus
|
||||
return ret;
|
||||
|
||||
ret = clk_prepare_enable(priv->mdio_clk);
|
||||
- if (ret == 0)
|
||||
- mdelay(10);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ mdelay(10);
|
||||
|
||||
- return ret;
|
||||
+ /* Restore MDC rate */
|
||||
+ return ipq4019_mdio_set_div(priv);
|
||||
+}
|
||||
+
|
||||
+static void ipq4019_mdio_select_mdc_rate(struct platform_device *pdev,
|
||||
+ struct ipq4019_mdio_data *priv)
|
||||
+{
|
||||
+ unsigned long ahb_rate;
|
||||
+ int div;
|
||||
+ u32 val;
|
||||
+
|
||||
+ /* MDC rate defined in DT, we don't have to decide a default value */
|
||||
+ if (!of_property_read_u32(pdev->dev.of_node, "clock-frequency",
|
||||
+ &priv->mdc_rate))
|
||||
+ return;
|
||||
+
|
||||
+ /* If we don't have a clock for AHB use the fixed value */
|
||||
+ ahb_rate = IPQ_MDIO_CLK_RATE;
|
||||
+ if (priv->mdio_clk)
|
||||
+ ahb_rate = clk_get_rate(priv->mdio_clk);
|
||||
+
|
||||
+ /* Check what is the current div set */
|
||||
+ val = readl(priv->membase + MDIO_MODE_REG);
|
||||
+ div = FIELD_GET(MDIO_MODE_DIV_MASK, val);
|
||||
+
|
||||
+ /* div is not set to the default value of /256
|
||||
+ * Probably someone changed that (bootloader, other drivers)
|
||||
+ * Keep this and don't overwrite it.
|
||||
+ */
|
||||
+ if (div != MDIO_MODE_DIV_256) {
|
||||
+ priv->mdc_rate = DIV_ROUND_UP(ahb_rate, div + 1);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ /* If div is /256 assume nobody have set this value and
|
||||
+ * try to find one MDC rate that is close the 802.3 spec of
|
||||
+ * 2.5MHz
|
||||
+ */
|
||||
+ for (div = 256; div >= 8; div /= 2) {
|
||||
+ /* Stop as soon as we found a divider that
|
||||
+ * reached the closest value to 2.5MHz
|
||||
+ */
|
||||
+ if (DIV_ROUND_UP(ahb_rate, div) > 2500000)
|
||||
+ break;
|
||||
+
|
||||
+ priv->mdc_rate = DIV_ROUND_UP(ahb_rate, div);
|
||||
+ }
|
||||
}
|
||||
|
||||
static int ipq4019_mdio_probe(struct platform_device *pdev)
|
||||
@@ -228,6 +320,11 @@ static int ipq4019_mdio_probe(struct pla
|
||||
if (IS_ERR(priv->mdio_clk))
|
||||
return PTR_ERR(priv->mdio_clk);
|
||||
|
||||
+ ipq4019_mdio_select_mdc_rate(pdev, priv);
|
||||
+ ret = ipq4019_mdio_set_div(priv);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
/* The platform resource is provided on the chipset IPQ5018 */
|
||||
/* This resource is optional */
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
@ -0,0 +1,92 @@
|
||||
From 7edce370d87a23e8ed46af5b76a9fef1e341b67b Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Tue, 28 Nov 2023 14:59:28 +0100
|
||||
Subject: [PATCH] net: phy: aquantia: drop wrong endianness conversion for addr
|
||||
and CRC
|
||||
|
||||
On further testing on BE target with kernel test robot, it was notice
|
||||
that the endianness conversion for addr and CRC in fw_load_memory was
|
||||
wrong.
|
||||
|
||||
Drop the cpu_to_le32 conversion for addr load as it's not needed.
|
||||
|
||||
Use get_unaligned_le32 instead of get_unaligned for FW data word load to
|
||||
correctly convert data in the correct order to follow system endian.
|
||||
|
||||
Also drop the cpu_to_be32 for CRC calculation as it's wrong and would
|
||||
cause different CRC on BE system.
|
||||
The loaded word is swapped internally and MAILBOX calculates the CRC on
|
||||
the swapped word. To correctly calculate the CRC to be later matched
|
||||
with the one from MAILBOX, use an u8 struct and swap the word there to
|
||||
keep the same order on both LE and BE for crc_ccitt_false function.
|
||||
Also add additional comments on how the CRC verification for the loaded
|
||||
section works.
|
||||
|
||||
CRC is calculated as we load the section and verified with the MAILBOX
|
||||
only after the entire section is loaded to skip additional slowdown by
|
||||
loop the section data again.
|
||||
|
||||
Reported-by: kernel test robot <lkp@intel.com>
|
||||
Closes: https://lore.kernel.org/oe-kbuild-all/202311210414.sEJZjlcD-lkp@intel.com/
|
||||
Fixes: e93984ebc1c8 ("net: phy: aquantia: add firmware load support")
|
||||
Tested-by: Robert Marko <robimarko@gmail.com> # ipq8072 LE device
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Link: https://lore.kernel.org/r/20231128135928.9841-1-ansuelsmth@gmail.com
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
---
|
||||
drivers/net/phy/aquantia/aquantia_firmware.c | 24 ++++++++++++--------
|
||||
1 file changed, 14 insertions(+), 10 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/aquantia/aquantia_firmware.c
|
||||
+++ b/drivers/net/phy/aquantia/aquantia_firmware.c
|
||||
@@ -93,9 +93,6 @@ static int aqr_fw_load_memory(struct phy
|
||||
u16 crc = 0, up_crc;
|
||||
size_t pos;
|
||||
|
||||
- /* PHY expect addr in LE */
|
||||
- addr = (__force u32)cpu_to_le32(addr);
|
||||
-
|
||||
phy_write_mmd(phydev, MDIO_MMD_VEND1,
|
||||
VEND1_GLOBAL_MAILBOX_INTERFACE1,
|
||||
VEND1_GLOBAL_MAILBOX_INTERFACE1_CRC_RESET);
|
||||
@@ -110,10 +107,11 @@ static int aqr_fw_load_memory(struct phy
|
||||
* If a firmware that is not word aligned is found, please report upstream.
|
||||
*/
|
||||
for (pos = 0; pos < len; pos += sizeof(u32)) {
|
||||
+ u8 crc_data[4];
|
||||
u32 word;
|
||||
|
||||
/* FW data is always stored in little-endian */
|
||||
- word = get_unaligned((const u32 *)(data + pos));
|
||||
+ word = get_unaligned_le32((const u32 *)(data + pos));
|
||||
|
||||
phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_MAILBOX_INTERFACE5,
|
||||
VEND1_GLOBAL_MAILBOX_INTERFACE5_MSW_DATA(word));
|
||||
@@ -124,15 +122,21 @@ static int aqr_fw_load_memory(struct phy
|
||||
VEND1_GLOBAL_MAILBOX_INTERFACE1_EXECUTE |
|
||||
VEND1_GLOBAL_MAILBOX_INTERFACE1_WRITE);
|
||||
|
||||
- /* calculate CRC as we load data to the mailbox.
|
||||
- * We convert word to big-endian as PHY is BE and mailbox will
|
||||
- * return a BE CRC.
|
||||
+ /* Word is swapped internally and MAILBOX CRC is calculated
|
||||
+ * using big-endian order. Mimic what the PHY does to have a
|
||||
+ * matching CRC...
|
||||
*/
|
||||
- word = (__force u32)cpu_to_be32(word);
|
||||
- crc = crc_ccitt_false(crc, (u8 *)&word, sizeof(word));
|
||||
- }
|
||||
+ crc_data[0] = word >> 24;
|
||||
+ crc_data[1] = word >> 16;
|
||||
+ crc_data[2] = word >> 8;
|
||||
+ crc_data[3] = word;
|
||||
|
||||
+ /* ...calculate CRC as we load data... */
|
||||
+ crc = crc_ccitt_false(crc, crc_data, sizeof(crc_data));
|
||||
+ }
|
||||
+ /* ...gets CRC from MAILBOX after we have loaded the entire section... */
|
||||
up_crc = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_MAILBOX_INTERFACE2);
|
||||
+ /* ...and make sure it does match our calculated CRC */
|
||||
if (crc != up_crc) {
|
||||
phydev_err(phydev, "CRC mismatch: calculated 0x%04x PHY 0x%04x\n",
|
||||
crc, up_crc);
|
@ -16,7 +16,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
|
||||
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
|
||||
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
|
||||
@@ -4333,6 +4333,7 @@ static const struct mtk_soc_data mt7986_
|
||||
@@ -4332,6 +4332,7 @@ static const struct mtk_soc_data mt7986_
|
||||
.hw_features = MTK_HW_FEATURES,
|
||||
.required_clks = MT7986_CLKS_BITMAP,
|
||||
.required_pctl = false,
|
||||
|
@ -12,7 +12,7 @@ Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
|
||||
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
|
||||
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
|
||||
@@ -3480,11 +3480,8 @@ static void mtk_pending_work(struct work
|
||||
@@ -3479,11 +3479,8 @@ static void mtk_pending_work(struct work
|
||||
rtnl_lock();
|
||||
|
||||
dev_dbg(eth->dev, "[%s][%d] reset\n", __func__, __LINE__);
|
||||
@ -25,7 +25,7 @@ Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
/* stop all devices to make sure that dma is properly shut down */
|
||||
for (i = 0; i < MTK_MAC_COUNT; i++) {
|
||||
if (!eth->netdev[i])
|
||||
@@ -3518,7 +3515,7 @@ static void mtk_pending_work(struct work
|
||||
@@ -3517,7 +3514,7 @@ static void mtk_pending_work(struct work
|
||||
|
||||
dev_dbg(eth->dev, "[%s][%d] reset done\n", __func__, __LINE__);
|
||||
|
||||
|
@ -16,7 +16,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
|
||||
|
||||
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
|
||||
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
|
||||
@@ -3256,6 +3256,27 @@ static void mtk_set_mcr_max_rx(struct mt
|
||||
@@ -3255,6 +3255,27 @@ static void mtk_set_mcr_max_rx(struct mt
|
||||
mtk_w32(mac->hw, mcr_new, MTK_MAC_MCR(mac->id));
|
||||
}
|
||||
|
||||
@ -44,7 +44,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
|
||||
static int mtk_hw_init(struct mtk_eth *eth)
|
||||
{
|
||||
u32 dma_mask = ETHSYS_DMA_AG_MAP_PDMA | ETHSYS_DMA_AG_MAP_QDMA |
|
||||
@@ -3295,22 +3316,9 @@ static int mtk_hw_init(struct mtk_eth *e
|
||||
@@ -3294,22 +3315,9 @@ static int mtk_hw_init(struct mtk_eth *e
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
|
||||
|
||||
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
|
||||
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
|
||||
@@ -3277,7 +3277,54 @@ static void mtk_hw_reset(struct mtk_eth
|
||||
@@ -3276,7 +3276,54 @@ static void mtk_hw_reset(struct mtk_eth
|
||||
0x3ffffff);
|
||||
}
|
||||
|
||||
@ -73,7 +73,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
|
||||
{
|
||||
u32 dma_mask = ETHSYS_DMA_AG_MAP_PDMA | ETHSYS_DMA_AG_MAP_QDMA |
|
||||
ETHSYS_DMA_AG_MAP_PPE;
|
||||
@@ -3316,7 +3363,12 @@ static int mtk_hw_init(struct mtk_eth *e
|
||||
@@ -3315,7 +3362,12 @@ static int mtk_hw_init(struct mtk_eth *e
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -87,7 +87,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
|
||||
|
||||
if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
|
||||
/* Set FE to PDMAv2 if necessary */
|
||||
@@ -3507,7 +3559,7 @@ static void mtk_pending_work(struct work
|
||||
@@ -3506,7 +3558,7 @@ static void mtk_pending_work(struct work
|
||||
if (eth->dev->pins)
|
||||
pinctrl_select_state(eth->dev->pins->p,
|
||||
eth->dev->pins->default_state);
|
||||
@ -96,7 +96,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
|
||||
|
||||
/* restart DMA and enable IRQs */
|
||||
for (i = 0; i < MTK_MAC_COUNT; i++) {
|
||||
@@ -4109,7 +4161,7 @@ static int mtk_probe(struct platform_dev
|
||||
@@ -4108,7 +4160,7 @@ static int mtk_probe(struct platform_dev
|
||||
eth->msg_enable = netif_msg_init(mtk_msg_level, MTK_DEFAULT_MSG_ENABLE);
|
||||
INIT_WORK(ð->pending_work, mtk_pending_work);
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user