Netdev List
 help / color / mirror / Atom feed
* Re: [PATCH net-next 0/5] bridge: Validate and clean up IPv6 neighbour suppression
From: Nikolay Aleksandrov @ 2026-07-20  9:31 UTC (permalink / raw)
  To: Danielle Ratson, netdev
  Cc: dsahern, idosch, davem, edumazet, kuba, pabeni, horms, ja, petrm,
	fw, kuniyu, bridge, linux-kernel
In-Reply-To: <cover.1784463131.git.danieller@nvidia.com>

On 19/07/2026 16:34, Danielle Ratson wrote:
> The bridge implements IPv6 neighbour suppression by snooping Neighbour
> Solicitation and Neighbour Advertisement messages, but it previously only
> checked the ICMPv6 type and code before acting on them. This leaves it
> open to acting on malformed or spoofed packets that any RFC 4861 compliant
> node should reject, and the option parsing in br_nd_send() open-codes a
> loop that has historically been a source of bugs.
> 
> This series hardens and cleans up that path:
> 
> Add ndisc_check_ns_na(), a standalone NS/NA validator modeled after
> ipv6_mc_check_mld(), implementing the RFC 4861 section 7.1.1 / 7.1.2
> mandatory receive checks (hop limit, checksum, code, length, target and
> option validation). Wire the bridge into it so NS/NA messages are
> validated to the same standard MLD already enjoys.
> 
> Replace the manual ND option parsing loop in br_nd_send() with
> ndisc_parse_options() and ndisc_opt_addr_data(), and linearize the skb
> once it has been validated as an NS/NA message so that this and any future
> ND message handling operate on a linear buffer. The first patch is a small
> preparatory cleanup that drops the now-unnecessary skb_header_pointer()
> fallback from br_is_nd_neigh_msg().
> 
> No functional change is intended for well-formed packets.
> 
> Patchset overview:
> Patch #1: drop the skb_header_pointer() fallback.
> Patches #2-#3: add ndisc_check_ns_na() and validate NS/NA with it.
> Patch #4: linearize once the ND message type is validated.
> Patch #5: parse options via ndisc_parse_options().
> 
> Danielle Ratson (5):
>    bridge: Use direct pointer in br_is_nd_neigh_msg()
>    ipv6: ndisc: Add ndisc_check_ns_na() validation helper
>    bridge: Validate NS/NA messages using ndisc_check_ns_na()
>    bridge: Linearize skb once the ND message type is validated
>    bridge: Use ndisc_parse_options() to parse ND options in br_nd_send()
> 
>   include/net/ndisc.h          |   2 +
>   net/bridge/br_arp_nd_proxy.c |  54 +++++-----
>   net/bridge/br_device.c       |   4 +-
>   net/bridge/br_input.c        |   4 +-
>   net/bridge/br_private.h      |   2 +-
>   net/ipv6/Makefile            |   2 +-
>   net/ipv6/ndisc.c             |   1 +
>   net/ipv6/ndisc_snoop.c       | 190 +++++++++++++++++++++++++++++++++++
>   8 files changed, 224 insertions(+), 35 deletions(-)
>   create mode 100644 net/ipv6/ndisc_snoop.c
> 

Nice set, but I'm curious - any reason not to use EXPORT_SYMBOL_GPL() instead?

Cheers,
  Nik


^ permalink raw reply

* Re: [PATCH net-next 5/5] bridge: Use ndisc_parse_options() to parse ND options in br_nd_send()
From: Nikolay Aleksandrov @ 2026-07-20  9:27 UTC (permalink / raw)
  To: Danielle Ratson, netdev
  Cc: dsahern, idosch, davem, edumazet, kuba, pabeni, horms, ja, petrm,
	fw, kuniyu, bridge, linux-kernel
In-Reply-To: <d75c565729850a2b95c5b19fd5997e94cf55029c.1784463131.git.danieller@nvidia.com>

On 19/07/2026 16:34, Danielle Ratson wrote:
> Replace the manual ND option parsing loop in br_nd_send() with
> ndisc_parse_options(), which provides proper validation and avoids the
> class of bugs that were fixed by commit 53fc685243bd ("bridge: Avoid
> infinite loop when suppressing NS messages with invalid options") and
> commit 850837965af1 ("bridge: br_nd_send: validate ND option lengths").
> 
> Use ndisc_opt_addr_data() to extract the source link-layer address
> from the parsed options, which correctly validates the option length
> for the underlying device type.
> 
> Export ndisc_parse_options() so that it can be resolved from the bridge
> when it is built as a module (CONFIG_BRIDGE=m); otherwise modpost fails
> with an undefined symbol.
> 
> Reviewed-by: Petr Machata <petrm@nvidia.com>
> Signed-off-by: Danielle Ratson <danieller@nvidia.com>
> ---
>   net/bridge/br_arp_nd_proxy.c | 32 +++++++++++++++++---------------
>   net/ipv6/ndisc.c             |  1 +
>   2 files changed, 18 insertions(+), 15 deletions(-)
> 

Acked-by: Nikolay Aleksandrov <razor@blackwall.org>


^ permalink raw reply

* Re: [PATCH net-next 4/5] bridge: Linearize skb once the ND message type is validated
From: Nikolay Aleksandrov @ 2026-07-20  9:26 UTC (permalink / raw)
  To: Danielle Ratson, netdev
  Cc: dsahern, idosch, davem, edumazet, kuba, pabeni, horms, ja, petrm,
	fw, kuniyu, bridge, linux-kernel
In-Reply-To: <c431f129eb438c12ef1e48d91039e09489a8f862.1784463131.git.danieller@nvidia.com>

On 19/07/2026 16:34, Danielle Ratson wrote:
> br_nd_send() parses ND options from ns->opt[] and therefore needs the skb
> to be linear. Commit a01aee7cafc5 ("bridge: br_nd_send: linearize skb
> before parsing ND options") ensured that by linearizing inside
> br_nd_send() itself.
> 
> Move the linearization up into br_is_nd_neigh_msg(), right after
> ndisc_check_ns_na() has validated the message as an NS/NA. This makes a
> linear buffer a property of every recognized ND message, so that this and
> any future ND message handling operate on a linear skb and cannot
> reintroduce that class of bug by forgetting to linearize.
> 
> Since the skb is now linear by the time br_nd_send() runs, drop the
> linearization there and derive ns from the transport header set by
> ndisc_check_ns_na(), instead of recomputing it from the network header.
> 
> If linearization fails under memory pressure, br_is_nd_neigh_msg() returns
> NULL and the packet falls back to normal forwarding rather than being
> suppressed.
> 
> Reviewed-by: Petr Machata <petrm@nvidia.com>
> Signed-off-by: Danielle Ratson <danieller@nvidia.com>
> ---
>   net/bridge/br_arp_nd_proxy.c | 8 +++++---
>   1 file changed, 5 insertions(+), 3 deletions(-)
> 
> diff --git a/net/bridge/br_arp_nd_proxy.c b/net/bridge/br_arp_nd_proxy.c
> index 445c930ed59b..46779d9fad61 100644
> --- a/net/bridge/br_arp_nd_proxy.c
> +++ b/net/bridge/br_arp_nd_proxy.c
> @@ -240,6 +240,9 @@ struct nd_msg *br_is_nd_neigh_msg(struct sk_buff *skb)
>   	if (ndisc_check_ns_na(skb))
>   		return NULL;
>   
> +	if (skb_linearize(skb))
> +		return NULL;
> +
>   	return (struct nd_msg *)skb_transport_header(skb);
>   }

This one is a bit weird - I wouldn't expect the check and validation to also
linearize the skb. I'd rename this helper to show that it also linearizes the skb.
Other than that the patch looks good.

>   
> @@ -259,7 +262,7 @@ static void br_nd_send(struct net_bridge *br, struct net_bridge_port *p,
>   	bool dad;
>   	u16 pvid;
>   
> -	if (!dev || skb_linearize(request))
> +	if (!dev)
>   		return;
>   
>   	len = LL_RESERVED_SPACE(dev) + sizeof(struct ipv6hdr) +
> @@ -276,8 +279,7 @@ static void br_nd_send(struct net_bridge *br, struct net_bridge_port *p,
>   	skb_set_mac_header(reply, 0);
>   
>   	daddr = eth_hdr(request)->h_source;
> -	ns = (struct nd_msg *)(skb_network_header(request) +
> -			       sizeof(struct ipv6hdr));
> +	ns = (struct nd_msg *)skb_transport_header(request);
>   
>   	/* Do we need option processing ? */
>   	ns_olen = request->len - (skb_network_offset(request) +


^ permalink raw reply

* [PATCH 8/8] slimbus: qcom-ngd-ctrl: use platform_device_set_fwnode()
From: Bartosz Golaszewski @ 2026-07-20  9:24 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
	Madhavan Srinivasan, Michael Ellerman, Nicholas Piggin,
	Christophe Leroy (CS GROUP), Andi Shyti, Joerg Roedel (AMD),
	Will Deacon, Robin Murphy, Andy Shevchenko, Doug Berger,
	Florian Fainelli, Broadcom internal kernel review list,
	Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Ulf Hansson, Frank Li, Sascha Hauer,
	Pengutronix Kernel Team, Fabio Estevam, Lee Jones,
	Sebastian Hesselbarth, Srinivas Kandagatla
  Cc: brgl, driver-core, linuxppc-dev, linux-kernel, linux-i2c, iommu,
	netdev, linux-pm, imx, linux-arm-kernel, mfd, linux-arm-msm,
	linux-sound, Bartosz Golaszewski
In-Reply-To: <20260720-pdev-set-fwnode-instead-of-of-node-v1-0-2dee93f42c54@oss.qualcomm.com>

Prefer the higher-level platform_device_set_fwnode() over the
OF-specific platform_device_set_of_node() for dynamically allocated
platform devices.

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
---
 drivers/slimbus/qcom-ngd-ctrl.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/slimbus/qcom-ngd-ctrl.c b/drivers/slimbus/qcom-ngd-ctrl.c
index 934c44c5bc1a92fae98f52bccd12ed849379dca8..bf077f47c9ddd484c11f98b47d2d8d4daf78dcb7 100644
--- a/drivers/slimbus/qcom-ngd-ctrl.c
+++ b/drivers/slimbus/qcom-ngd-ctrl.c
@@ -1538,7 +1538,7 @@ static int of_qcom_slim_ngd_register(struct device *parent,
 			kfree(ngd);
 			return ret;
 		}
-		platform_device_set_of_node(ngd->pdev, node);
+		platform_device_set_fwnode(ngd->pdev, of_fwnode_handle(node));
 		ctrl->ngd = ngd;
 
 		ret = platform_device_add(ngd->pdev);

-- 
2.47.3


^ permalink raw reply related

* [PATCH 7/8] net: mv643xx: use platform_device_set_fwnode()
From: Bartosz Golaszewski @ 2026-07-20  9:24 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
	Madhavan Srinivasan, Michael Ellerman, Nicholas Piggin,
	Christophe Leroy (CS GROUP), Andi Shyti, Joerg Roedel (AMD),
	Will Deacon, Robin Murphy, Andy Shevchenko, Doug Berger,
	Florian Fainelli, Broadcom internal kernel review list,
	Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Ulf Hansson, Frank Li, Sascha Hauer,
	Pengutronix Kernel Team, Fabio Estevam, Lee Jones,
	Sebastian Hesselbarth, Srinivas Kandagatla
  Cc: brgl, driver-core, linuxppc-dev, linux-kernel, linux-i2c, iommu,
	netdev, linux-pm, imx, linux-arm-kernel, mfd, linux-arm-msm,
	linux-sound, Bartosz Golaszewski
In-Reply-To: <20260720-pdev-set-fwnode-instead-of-of-node-v1-0-2dee93f42c54@oss.qualcomm.com>

Prefer the higher-level platform_device_set_fwnode() over the
OF-specific platform_device_set_of_node() for dynamically allocated
platform devices.

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
---
 drivers/net/ethernet/marvell/mv643xx_eth.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/net/ethernet/marvell/mv643xx_eth.c b/drivers/net/ethernet/marvell/mv643xx_eth.c
index 9caa1e47c174c9d7a161b7f2e2ee12a829b813d4..2f2d6cce8d852b9ec3ab42678a04a7915d1f00cc 100644
--- a/drivers/net/ethernet/marvell/mv643xx_eth.c
+++ b/drivers/net/ethernet/marvell/mv643xx_eth.c
@@ -2780,7 +2780,7 @@ static int mv643xx_eth_shared_of_add_port(struct platform_device *pdev,
 		goto put_err;
 	}
 	ppdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
-	platform_device_set_of_node(ppdev, pnp);
+	platform_device_set_fwnode(ppdev, of_fwnode_handle(pnp));
 
 	ret = platform_device_add_resources(ppdev, &res, 1);
 	if (ret)

-- 
2.47.3


^ permalink raw reply related

* [PATCH 6/8] mfd: tps6586: use platform_device_set_fwnode()
From: Bartosz Golaszewski @ 2026-07-20  9:24 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
	Madhavan Srinivasan, Michael Ellerman, Nicholas Piggin,
	Christophe Leroy (CS GROUP), Andi Shyti, Joerg Roedel (AMD),
	Will Deacon, Robin Murphy, Andy Shevchenko, Doug Berger,
	Florian Fainelli, Broadcom internal kernel review list,
	Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Ulf Hansson, Frank Li, Sascha Hauer,
	Pengutronix Kernel Team, Fabio Estevam, Lee Jones,
	Sebastian Hesselbarth, Srinivas Kandagatla
  Cc: brgl, driver-core, linuxppc-dev, linux-kernel, linux-i2c, iommu,
	netdev, linux-pm, imx, linux-arm-kernel, mfd, linux-arm-msm,
	linux-sound, Bartosz Golaszewski
In-Reply-To: <20260720-pdev-set-fwnode-instead-of-of-node-v1-0-2dee93f42c54@oss.qualcomm.com>

Prefer the higher-level platform_device_set_fwnode() over the
OF-specific platform_device_set_of_node() for dynamically allocated
platform devices.

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
---
 drivers/mfd/tps6586x.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/mfd/tps6586x.c b/drivers/mfd/tps6586x.c
index 3cfd2f02b62f3cc370e0d970ec2643b638fd0fec..fc7c4b55c385416741925904abae4fd240f55b13 100644
--- a/drivers/mfd/tps6586x.c
+++ b/drivers/mfd/tps6586x.c
@@ -397,7 +397,7 @@ static int tps6586x_add_subdevs(struct tps6586x *tps6586x,
 
 		pdev->dev.parent = tps6586x->dev;
 		pdev->dev.platform_data = subdev->platform_data;
-		platform_device_set_of_node(pdev, subdev->of_node);
+		platform_device_set_fwnode(pdev, of_fwnode_handle(subdev->of_node));
 
 		ret = platform_device_add(pdev);
 		if (ret) {

-- 
2.47.3


^ permalink raw reply related

* [PATCH 5/8] pmdomain: imx: use platform_device_set_fwnode()
From: Bartosz Golaszewski @ 2026-07-20  9:24 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
	Madhavan Srinivasan, Michael Ellerman, Nicholas Piggin,
	Christophe Leroy (CS GROUP), Andi Shyti, Joerg Roedel (AMD),
	Will Deacon, Robin Murphy, Andy Shevchenko, Doug Berger,
	Florian Fainelli, Broadcom internal kernel review list,
	Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Ulf Hansson, Frank Li, Sascha Hauer,
	Pengutronix Kernel Team, Fabio Estevam, Lee Jones,
	Sebastian Hesselbarth, Srinivas Kandagatla
  Cc: brgl, driver-core, linuxppc-dev, linux-kernel, linux-i2c, iommu,
	netdev, linux-pm, imx, linux-arm-kernel, mfd, linux-arm-msm,
	linux-sound, Bartosz Golaszewski
In-Reply-To: <20260720-pdev-set-fwnode-instead-of-of-node-v1-0-2dee93f42c54@oss.qualcomm.com>

Prefer the higher-level platform_device_set_fwnode() over the
OF-specific platform_device_set_of_node() for dynamically allocated
platform devices.

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
---
 drivers/pmdomain/imx/gpc.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/pmdomain/imx/gpc.c b/drivers/pmdomain/imx/gpc.c
index abca5f449a226fbae4213926e1395c413160c950..c147eaf048ba2b79a744ec87029420981581e48e 100644
--- a/drivers/pmdomain/imx/gpc.c
+++ b/drivers/pmdomain/imx/gpc.c
@@ -487,7 +487,7 @@ static int imx_gpc_probe(struct platform_device *pdev)
 			domain->ipg_rate_mhz = ipg_rate_mhz;
 
 			pd_pdev->dev.parent = &pdev->dev;
-			platform_device_set_of_node(pd_pdev, np);
+			platform_device_set_fwnode(pd_pdev, of_fwnode_handle(np));
 
 			ret = platform_device_add(pd_pdev);
 			if (ret) {

-- 
2.47.3


^ permalink raw reply related

* [PATCH 4/8] net: bcmgenet: use platform_device_set_fwnode()
From: Bartosz Golaszewski @ 2026-07-20  9:24 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
	Madhavan Srinivasan, Michael Ellerman, Nicholas Piggin,
	Christophe Leroy (CS GROUP), Andi Shyti, Joerg Roedel (AMD),
	Will Deacon, Robin Murphy, Andy Shevchenko, Doug Berger,
	Florian Fainelli, Broadcom internal kernel review list,
	Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Ulf Hansson, Frank Li, Sascha Hauer,
	Pengutronix Kernel Team, Fabio Estevam, Lee Jones,
	Sebastian Hesselbarth, Srinivas Kandagatla
  Cc: brgl, driver-core, linuxppc-dev, linux-kernel, linux-i2c, iommu,
	netdev, linux-pm, imx, linux-arm-kernel, mfd, linux-arm-msm,
	linux-sound, Bartosz Golaszewski
In-Reply-To: <20260720-pdev-set-fwnode-instead-of-of-node-v1-0-2dee93f42c54@oss.qualcomm.com>

Prefer the higher-level platform_device_set_fwnode() over the
OF-specific platform_device_set_of_node() for dynamically allocated
platform devices.

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
---
 drivers/net/ethernet/broadcom/genet/bcmmii.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/net/ethernet/broadcom/genet/bcmmii.c b/drivers/net/ethernet/broadcom/genet/bcmmii.c
index 0f0dbabfaabbce3469de79af91d7731b8476709f..6f471723bd53f454e4b0de1716977b4382013429 100644
--- a/drivers/net/ethernet/broadcom/genet/bcmmii.c
+++ b/drivers/net/ethernet/broadcom/genet/bcmmii.c
@@ -492,7 +492,8 @@ static int bcmgenet_mii_register(struct bcmgenet_priv *priv)
 	ppdev->dev.parent = &pdev->dev;
 
 	if (dn)
-		platform_device_set_of_node(ppdev, bcmgenet_mii_of_find_mdio(priv));
+		platform_device_set_fwnode(ppdev,
+					   of_fwnode_handle(bcmgenet_mii_of_find_mdio(priv)));
 	else
 		ppd.phy_mask = ~0;
 

-- 
2.47.3


^ permalink raw reply related

* [PATCH 3/8] iommu/fsl: use platform_device_set_fwnode()
From: Bartosz Golaszewski @ 2026-07-20  9:24 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
	Madhavan Srinivasan, Michael Ellerman, Nicholas Piggin,
	Christophe Leroy (CS GROUP), Andi Shyti, Joerg Roedel (AMD),
	Will Deacon, Robin Murphy, Andy Shevchenko, Doug Berger,
	Florian Fainelli, Broadcom internal kernel review list,
	Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Ulf Hansson, Frank Li, Sascha Hauer,
	Pengutronix Kernel Team, Fabio Estevam, Lee Jones,
	Sebastian Hesselbarth, Srinivas Kandagatla
  Cc: brgl, driver-core, linuxppc-dev, linux-kernel, linux-i2c, iommu,
	netdev, linux-pm, imx, linux-arm-kernel, mfd, linux-arm-msm,
	linux-sound, Bartosz Golaszewski
In-Reply-To: <20260720-pdev-set-fwnode-instead-of-of-node-v1-0-2dee93f42c54@oss.qualcomm.com>

Prefer the higher-level platform_device_set_fwnode() over the
OF-specific platform_device_set_of_node() for dynamically allocated
platform devices.

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
---
 drivers/iommu/fsl_pamu.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/iommu/fsl_pamu.c b/drivers/iommu/fsl_pamu.c
index c83bbc3faad56d6ee1c89b0a7f74028af02c81e9..268a1f752fbceab4fd24013aeea5df1b6982fbb1 100644
--- a/drivers/iommu/fsl_pamu.c
+++ b/drivers/iommu/fsl_pamu.c
@@ -975,7 +975,7 @@ static __init int fsl_pamu_init(void)
 		goto error_device_alloc;
 	}
 
-	platform_device_set_of_node(pdev, np);
+	platform_device_set_fwnode(pdev, of_fwnode_handle(np));
 
 	ret = pamu_domain_init();
 	if (ret)

-- 
2.47.3


^ permalink raw reply related

* [PATCH 2/8] i2c: pxa-pci: use platform_device_set_fwnode()
From: Bartosz Golaszewski @ 2026-07-20  9:24 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
	Madhavan Srinivasan, Michael Ellerman, Nicholas Piggin,
	Christophe Leroy (CS GROUP), Andi Shyti, Joerg Roedel (AMD),
	Will Deacon, Robin Murphy, Andy Shevchenko, Doug Berger,
	Florian Fainelli, Broadcom internal kernel review list,
	Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Ulf Hansson, Frank Li, Sascha Hauer,
	Pengutronix Kernel Team, Fabio Estevam, Lee Jones,
	Sebastian Hesselbarth, Srinivas Kandagatla
  Cc: brgl, driver-core, linuxppc-dev, linux-kernel, linux-i2c, iommu,
	netdev, linux-pm, imx, linux-arm-kernel, mfd, linux-arm-msm,
	linux-sound, Bartosz Golaszewski
In-Reply-To: <20260720-pdev-set-fwnode-instead-of-of-node-v1-0-2dee93f42c54@oss.qualcomm.com>

Prefer the higher-level platform_device_set_fwnode() over the
OF-specific platform_device_set_of_node() for dynamically allocated
platform devices.

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
---
 drivers/i2c/busses/i2c-pxa-pci.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/i2c/busses/i2c-pxa-pci.c b/drivers/i2c/busses/i2c-pxa-pci.c
index 92a0647f08c69f841ca99caca757c1728b3f6fce..58532cf9f06d43a0f2173b962eb498c9e4bb81d0 100644
--- a/drivers/i2c/busses/i2c-pxa-pci.c
+++ b/drivers/i2c/busses/i2c-pxa-pci.c
@@ -77,7 +77,7 @@ static struct platform_device *add_i2c_device(struct pci_dev *dev, int bar)
 	}
 	pdev->dev.parent = &dev->dev;
 
-	platform_device_set_of_node(pdev, child);
+	platform_device_set_fwnode(pdev, of_fwnode_handle(child));
 
 	ret = platform_device_add_resources(pdev, res, ARRAY_SIZE(res));
 	if (ret)

-- 
2.47.3


^ permalink raw reply related

* [PATCH 1/8] powerpc/powermac: use platform_device_set_fwnode()
From: Bartosz Golaszewski @ 2026-07-20  9:24 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
	Madhavan Srinivasan, Michael Ellerman, Nicholas Piggin,
	Christophe Leroy (CS GROUP), Andi Shyti, Joerg Roedel (AMD),
	Will Deacon, Robin Murphy, Andy Shevchenko, Doug Berger,
	Florian Fainelli, Broadcom internal kernel review list,
	Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Ulf Hansson, Frank Li, Sascha Hauer,
	Pengutronix Kernel Team, Fabio Estevam, Lee Jones,
	Sebastian Hesselbarth, Srinivas Kandagatla
  Cc: brgl, driver-core, linuxppc-dev, linux-kernel, linux-i2c, iommu,
	netdev, linux-pm, imx, linux-arm-kernel, mfd, linux-arm-msm,
	linux-sound, Bartosz Golaszewski
In-Reply-To: <20260720-pdev-set-fwnode-instead-of-of-node-v1-0-2dee93f42c54@oss.qualcomm.com>

Prefer the higher-level platform_device_set_fwnode() over the
OF-specific platform_device_set_of_node() for dynamically allocated
platform devices.

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
---
 arch/powerpc/platforms/powermac/low_i2c.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/powerpc/platforms/powermac/low_i2c.c b/arch/powerpc/platforms/powermac/low_i2c.c
index a175a32a222bab4cc7400f6ab6071f5630db2cb8..eda35534cfd72e828616f27b5f8319189d68285e 100644
--- a/arch/powerpc/platforms/powermac/low_i2c.c
+++ b/arch/powerpc/platforms/powermac/low_i2c.c
@@ -1471,7 +1471,7 @@ static int __init pmac_i2c_create_platform_devices(void)
 		if (bus->platform_dev == NULL)
 			return -ENOMEM;
 		bus->platform_dev->dev.platform_data = bus;
-		platform_device_set_of_node(bus->platform_dev, bus->busnode);
+		platform_device_set_fwnode(bus->platform_dev, of_fwnode_handle(bus->busnode));
 		platform_device_add(bus->platform_dev);
 	}
 

-- 
2.47.3


^ permalink raw reply related

* [PATCH 0/8] driver core: prefer platform_device_set_fwnode()
From: Bartosz Golaszewski @ 2026-07-20  9:24 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
	Madhavan Srinivasan, Michael Ellerman, Nicholas Piggin,
	Christophe Leroy (CS GROUP), Andi Shyti, Joerg Roedel (AMD),
	Will Deacon, Robin Murphy, Andy Shevchenko, Doug Berger,
	Florian Fainelli, Broadcom internal kernel review list,
	Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Ulf Hansson, Frank Li, Sascha Hauer,
	Pengutronix Kernel Team, Fabio Estevam, Lee Jones,
	Sebastian Hesselbarth, Srinivas Kandagatla
  Cc: brgl, driver-core, linuxppc-dev, linux-kernel, linux-i2c, iommu,
	netdev, linux-pm, imx, linux-arm-kernel, mfd, linux-arm-msm,
	linux-sound, Bartosz Golaszewski

With the final platform device and software node rework changes having
been queued in the driver core tree, Danilo pointed out that
platform_device_set_fwnode() is no longer used and should be removed. I
argued that instead we should prefer it over the OF-specific
platform_device_set_of_node().

This series converts drivers using platform_device_set_of_node() to
platform_device_set_fwnode() and does not intend any functional change
as the semantics of:

  platform_device_set_of_node(pdev, np);

should be equal to those of:

  platform_device_set_fwnode(pdev, of_fwnode_handle(np));

The prerequisite changes are currently in the driver core tree so this
entire series should follow them as well with Acks from maintainers. If
it doesn't make v7.3 for some reason, then I will resend these next
cycle separately targetting individual trees.

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
---
Bartosz Golaszewski (8):
      powerpc/powermac: use platform_device_set_fwnode()
      i2c: pxa-pci: use platform_device_set_fwnode()
      iommu/fsl: use platform_device_set_fwnode()
      net: bcmgenet: use platform_device_set_fwnode()
      pmdomain: imx: use platform_device_set_fwnode()
      mfd: tps6586: use platform_device_set_fwnode()
      net: mv643xx: use platform_device_set_fwnode()
      slimbus: qcom-ngd-ctrl: use platform_device_set_fwnode()

 arch/powerpc/platforms/powermac/low_i2c.c    | 2 +-
 drivers/i2c/busses/i2c-pxa-pci.c             | 2 +-
 drivers/iommu/fsl_pamu.c                     | 2 +-
 drivers/mfd/tps6586x.c                       | 2 +-
 drivers/net/ethernet/broadcom/genet/bcmmii.c | 3 ++-
 drivers/net/ethernet/marvell/mv643xx_eth.c   | 2 +-
 drivers/pmdomain/imx/gpc.c                   | 2 +-
 drivers/slimbus/qcom-ngd-ctrl.c              | 2 +-
 8 files changed, 9 insertions(+), 8 deletions(-)
---
base-commit: 27edebd505791748a77703e311d76f0f55a8d7ec
change-id: 20260720-pdev-set-fwnode-instead-of-of-node-e83ece371509

Best regards,
-- 
Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>


^ permalink raw reply

* Re: [PATCH net-next 3/5] bridge: Validate NS/NA messages using ndisc_check_ns_na()
From: Nikolay Aleksandrov @ 2026-07-20  9:14 UTC (permalink / raw)
  To: Danielle Ratson, netdev
  Cc: dsahern, idosch, davem, edumazet, kuba, pabeni, horms, ja, petrm,
	fw, kuniyu, bridge, linux-kernel
In-Reply-To: <63efa708b4b0bd9edb3a6f6a521cb5022c43aaa5.1784463131.git.danieller@nvidia.com>

On 19/07/2026 16:34, Danielle Ratson wrote:
> The bridge performs neighbor suppression by snooping NS/NA messages, but
> previously only checked the ICMPv6 type and code. This leaves it open to
> acting on malformed or spoofed packets that any RFC-compliant node should
> reject.
> 
> Wire br_is_nd_neigh_msg() into the new ndisc_check_ns_na() helper, which
> enforces the full RFC 4861 section 7.1.1/7.1.2 receive validation:
> hop limit of 255, valid checksum, correct code, and type-specific rules
> (NS target not multicast; NA solicited flag clear for multicast
> destinations).
> 
> MLD messages are already validated by ipv6_mc_check_mld() before the
> bridge acts on them; this brings NS/NA to the same standard.
> 
> As a side effect, the skb parameter of br_is_nd_neigh_msg() changes from
> const to non-const, since ndisc_check_ns_na() may reallocate the skb head
> via pskb_may_pull() and sets the transport header. The returned pointer is
> now derived from skb_transport_header() rather than a direct cast.
> 
> Reviewed-by: Petr Machata <petrm@nvidia.com>
> Signed-off-by: Danielle Ratson <danieller@nvidia.com>
> ---
>   net/bridge/br_arp_nd_proxy.c | 11 ++++-------
>   net/bridge/br_private.h      |  2 +-
>   2 files changed, 5 insertions(+), 8 deletions(-)
> 

Acked-by: Nikolay Aleksandrov <razor@blackwall.org>


^ permalink raw reply

* Re: [PATCH net-next 2/5] ipv6: ndisc: Add ndisc_check_ns_na() validation helper
From: Nikolay Aleksandrov @ 2026-07-20  9:13 UTC (permalink / raw)
  To: Danielle Ratson, netdev
  Cc: dsahern, idosch, davem, edumazet, kuba, pabeni, horms, ja, petrm,
	fw, kuniyu, bridge, linux-kernel
In-Reply-To: <c7f894446494462bd72f717021aa4b6373f31131.1784463131.git.danieller@nvidia.com>

On 19/07/2026 16:34, Danielle Ratson wrote:
> Add ndisc_check_ns_na(), a standalone NS/NA packet validator modeled
> after ipv6_mc_check_mld(). It performs the RFC 4861 section 7.1.1
> (Neighbor Solicitation) and 7.1.2 (Neighbor Advertisement) mandatory
> checks that are relevant for software operating at the bridge level,
> where packets bypass the normal IPv6 stack path:
> 
>   - Hop Limit must be 255 (packet was not forwarded by a router)
>   - ICMPv6 checksum is valid
>   - ICMP Code is 0
>   - ICMP length is at least 24 octets (sizeof(struct nd_msg))
>   - Target Address must not be a multicast address
>   - All included options have a length that is greater than zero
>   - NS/DAD: destination must be a solicited-node multicast address
>   - NS/DAD: no Source Link-Layer Address option when source is unspecified
>   - NA: Solicited flag must be 0 when IP Destination is multicast
> 
> On success the function sets the skb transport header and returns 0,
> matching the convention of ipv6_mc_check_mld().
> 
> Reviewed-by: Petr Machata <petrm@nvidia.com>
> Signed-off-by: Danielle Ratson <danieller@nvidia.com>
> ---
>   include/net/ndisc.h    |   2 +
>   net/ipv6/Makefile      |   2 +-
>   net/ipv6/ndisc_snoop.c | 190 +++++++++++++++++++++++++++++++++++++++++
>   3 files changed, 193 insertions(+), 1 deletion(-)
>   create mode 100644 net/ipv6/ndisc_snoop.c
> 

Acked-by: Nikolay Aleksandrov <razor@blackwall.org>



^ permalink raw reply

* [PATCH net-next v4] net: phy: Add driver for Motorcomm Quad 2.5GbE phy
From: Kyle Switch @ 2026-07-20  9:11 UTC (permalink / raw)
  To: Frank.Sae, andrew, hkallweit1, linux, davem, edumazet, kuba,
	pabeni, netdev, linux-kernel
  Cc: ming.xu, xiaolin.xu, jianmin.wang, jie.han

Add a driver for motorcomm yt8824 quad 2.5G ethernet phy, supports
2.5G/1000M/100M/10M speed.

Signed-off-by: Kyle Switch <kyle.switch@motor-comm.com>
---

changes in v4:
1. Remove motorcomm,yt8xxx.yaml, will update in other patch thread
2. Fix locking issue. Since every interface requires switching of space, 
   the bus is already locked during the space switching process in
   phy_select_page(), other operations within the interface cannot 
   be locked again before unlock in phy_restore_page().
3. Fix warning log identified during the inspection process using
   checkpatch.pl.
4. Fix the way of using common api in phy_package.c


changes in v3:
1. Using common apis defined in phy_package.c to handle shared top
extend register space.
2. Add dts demo in motorcomm,yt8xxx.yaml.
3. Fix unnecessary redundant judgments.
4. Fix BMCR registers operation using magic number.
5. Rename funtion based on its approximate functionality.

changes in v2:
1. Remove duplicate code and replace it with existing api.

 drivers/net/phy/motorcomm.c | 1718 ++++++++++++++++++++++++++++++++++-
 1 file changed, 1698 insertions(+), 20 deletions(-)

diff --git a/drivers/net/phy/motorcomm.c b/drivers/net/phy/motorcomm.c
index 5071605a1a11..ee63c2019946 100644
--- a/drivers/net/phy/motorcomm.c
+++ b/drivers/net/phy/motorcomm.c
@@ -1,23 +1,32 @@
 // SPDX-License-Identifier: GPL-2.0+
 /*
- * Motorcomm 8511/8521/8522/8531/8531S/8821 PHY driver.
+ * Motorcomm 8511/8521/8522/8531/8531S/8821/8824 PHY driver.
  *
  * Author: Peter Geis <pgwipeout@gmail.com>
  * Author: Frank <Frank.Sae@motor-comm.com>
+ * Author: Kyle <kyle.switch@motor-comm.com>
  */
 
 #include <linux/etherdevice.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
+#include <linux/of.h>
 #include <linux/phy.h>
 #include <linux/property.h>
 
+#include "phylib.h"
+
+/* define PHY8824 top extend addr offset internal and external*/
+#define INTERNAL_PHY8824_TOP_EXTEND_OFFSET    (5)
+#define EXTERNAL_PHY8824_TOP_EXTEND_OFFSET    (4)
+
 #define PHY_ID_YT8511		0x0000010a
 #define PHY_ID_YT8521		0x0000011a
 #define PHY_ID_YT8522		0x4f51e928
 #define PHY_ID_YT8531		0x4f51e91b
 #define PHY_ID_YT8531S		0x4f51e91a
 #define PHY_ID_YT8821		0x4f51ea19
+#define PHY_ID_YT8824		0x4f51e8b8
 /* YT8521/YT8531S/YT8821 Register Overview
  *	UTP Register space	|	FIBER Register space
  *  ------------------------------------------------------------
@@ -29,6 +38,18 @@
  *  ------------------------------------------------------------
  */
 
+/* YT8824 Register Overview
+ *	UTP Register space	|	FIBER Register space
+ *  ------------------------------------------------------------
+ * |	UTP MII			|	FIBER MII	        |
+ * |	UTP MMD			|				|
+ * |	UTP Extended		|	FIBER Extended		|
+ * |	UTP Top Extended	|	FIBER Top Extended	|
+ *  ------------------------------------------------------------
+ * |			Common Top Extended			|
+ *  ------------------------------------------------------------
+ */
+
 /* 0x10 ~ 0x15 , 0x1E and 0x1F are common MII registers of yt phy */
 
 /* Specific Function Control Register */
@@ -354,7 +375,7 @@
 
 #define YT8821_UTP_EXT_MU_FINE_FR_CTRL_REG		0x4B5
 #define YT8821_UTP_EXT_MU_FINE_FR_F_FFE			GENMASK(14, 12)
-#define YT8821_UTP_EXT_MU_FINE_FR_F_FBE			GENMASK(10, 8)
+#define YT8821_UTP_EXT_MU_FINE_FR_F_FBE	GENMASK(10, 8)
 
 #define YT8821_UTP_EXT_VGA_LPF1_CAP_CTRL_REG		0x4D2
 #define YT8821_UTP_EXT_VGA_LPF1_CAP_OTHER		GENMASK(7, 4)
@@ -375,6 +396,16 @@
 #define YT8821_CHIP_MODE_AUTO_BX2500_SGMII	0
 #define YT8821_CHIP_MODE_FORCE_BX2500		1
 
+#define YT8824_RSSR_SPACE_MASK			BIT(0)
+#define YT8824_RSSR_FIBER_SPACE			(0x1)
+#define YT8824_RSSR_UTP_SPACE			(0x0)
+#define YT8824_UTP_TEMPLATE_MODE_CTRL		(0x84)
+#define YT8824_UTP_TEMPLATE_MODE_MASK		GENMASK(15, 13)
+#define YT8824_UTP_TEMPLATE_TEST_MODE1		BIT(13)
+#define YT8824_SDS_CFG_MIN_PRE_MASK		GENMASK(3, 0)
+#define YT8824_SDS_EN_FILL_PRE			BIT(13)
+#define YT8824_SDS_TX_PRE_PADDING		(0x7)
+
 struct yt8521_priv {
 	/* combo_advertising is used for case of YT8521 in combo mode,
 	 * this means that yt8521 may work in utp or fiber mode which depends
@@ -393,6 +424,11 @@ struct yt8521_priv {
 	u8 reg_page;
 };
 
+struct yt8824_shared_priv {
+	unsigned int interface_mode;
+	unsigned int offset;
+};
+
 /**
  * ytphy_read_ext() - read a PHY's extended register
  * @phydev: a pointer to a &struct phy_device
@@ -431,6 +467,46 @@ static int ytphy_read_ext_with_lock(struct phy_device *phydev, u16 regnum)
 	return ret;
 }
 
+/**
+ * ytphy_read_top_ext() - read a PHY's top extended register for YT8824
+ * @phydev: a pointer to a &struct phy_device
+ * @regnum: register number to read
+ *
+ * Returns: the value of regnum reg or negative error code
+ */
+static int ytphy_read_top_ext(struct phy_device *phydev, u16 regnum)
+{
+	int ret;
+
+	lockdep_assert_held(&phydev->mdio.bus->mdio_lock);
+	ret = __phy_package_write(phydev, 0, YTPHY_PAGE_SELECT, regnum);
+	if (ret < 0)
+		return ret;
+
+	return __phy_package_read(phydev, 0, YTPHY_PAGE_DATA);
+}
+
+/**
+ * ytphy_write_top_ext() - write a PHY's top extended register for YT8824
+ * @phydev: a pointer to a &struct phy_device
+ * @regnum: register number to write
+ * @val: register val to write
+ *
+ * Returns: the value of regnum reg or negative error code
+ */
+static int ytphy_write_top_ext(struct phy_device *phydev, u16 regnum,
+			       u16 val)
+{
+	int ret;
+
+	lockdep_assert_held(&phydev->mdio.bus->mdio_lock);
+	ret = __phy_package_write(phydev, 0, YTPHY_PAGE_SELECT, regnum);
+	if (ret < 0)
+		return ret;
+
+	return __phy_package_write(phydev, 0, YTPHY_PAGE_DATA, val);
+}
+
 /**
  * ytphy_write_ext() - write a PHY's extended register
  * @phydev: a pointer to a &struct phy_device
@@ -593,35 +669,1016 @@ static int ytphy_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol)
 				goto err_restore_page;
 		}
 
-		/* Enable WOL feature */
-		mask = YTPHY_WCR_PULSE_WIDTH_MASK | YTPHY_WCR_INTR_SEL;
-		val = YTPHY_WCR_ENABLE | YTPHY_WCR_INTR_SEL;
-		val |= YTPHY_WCR_TYPE_PULSE | YTPHY_WCR_PULSE_WIDTH_672MS;
-		ret = ytphy_modify_ext(phydev, YTPHY_WOL_CONFIG_REG, mask, val);
+		/* Enable WOL feature */
+		mask = YTPHY_WCR_PULSE_WIDTH_MASK | YTPHY_WCR_INTR_SEL;
+		val = YTPHY_WCR_ENABLE | YTPHY_WCR_INTR_SEL;
+		val |= YTPHY_WCR_TYPE_PULSE | YTPHY_WCR_PULSE_WIDTH_672MS;
+		ret = ytphy_modify_ext(phydev, YTPHY_WOL_CONFIG_REG, mask, val);
+		if (ret < 0)
+			goto err_restore_page;
+
+		/* Enable WOL interrupt */
+		ret = __phy_modify(phydev, YTPHY_INTERRUPT_ENABLE_REG, 0,
+				   YTPHY_IER_WOL);
+		if (ret < 0)
+			goto err_restore_page;
+
+	} else {
+		old_page = phy_select_page(phydev, YT8521_RSSR_UTP_SPACE);
+		if (old_page < 0)
+			goto err_restore_page;
+
+		/* Disable WOL feature */
+		mask = YTPHY_WCR_ENABLE | YTPHY_WCR_INTR_SEL;
+		ret = ytphy_modify_ext(phydev, YTPHY_WOL_CONFIG_REG, mask, 0);
+
+		/* Disable WOL interrupt */
+		ret = __phy_modify(phydev, YTPHY_INTERRUPT_ENABLE_REG,
+				   YTPHY_IER_WOL, 0);
+		if (ret < 0)
+			goto err_restore_page;
+	}
+
+err_restore_page:
+	return phy_restore_page(phydev, old_page, ret);
+}
+
+/**
+ * yt8824_read_page() - read reg page
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * Returns: current reg space of yt8824 (YT8824_RSSR_FIBER_SPACE/
+ * YT8824_RSSR_UTP_SPACE) or negative errno code
+ */
+static int yt8824_read_page(struct phy_device *phydev)
+{
+	int old_page;
+
+	old_page = ytphy_read_top_ext(phydev, YT8521_REG_SPACE_SELECT_REG);
+	if (old_page < 0)
+		return old_page;
+
+	return old_page & YT8824_RSSR_SPACE_MASK;
+};
+
+/**
+ * yt8824_write_page() - write reg page
+ * @phydev: a pointer to a &struct phy_device
+ * @page: The reg page(YT8824_RSSR_FIBER_SPACE/YT8824_RSSR_UTP_SPACE) to write.
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_write_page(struct phy_device *phydev, int page)
+{
+	int old_page;
+	u16 data;
+
+	old_page = ytphy_read_top_ext(phydev, YT8521_REG_SPACE_SELECT_REG);
+	data = old_page & (~YT8824_RSSR_SPACE_MASK);
+	data |= page;
+
+	return ytphy_write_top_ext(phydev, YT8521_REG_SPACE_SELECT_REG, data);
+};
+
+/**
+ * yt8824_utp_invalid_test_mode_paged() - config YT8824 to invalid test mode.
+ * @phydev: a pointer to a &struct phy_device
+ * @reg_space: The reg page(YT8824_RSSR_FIBER_SPACE/YT8824_RSSR_UTP_SPACE).
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_utp_invalid_test_mode_paged(struct phy_device *phydev,
+					      int reg_space)
+{
+	int old_page;
+	int ret = 0;
+
+	old_page = phy_select_page(phydev, reg_space);
+	if (old_page < 0)
+		goto err_restore_page;
+
+	if (reg_space == YT8824_RSSR_UTP_SPACE) {
+		ret = __phy_read_mmd(phydev, 0x1, YT8824_UTP_TEMPLATE_MODE_CTRL);
+		if (ret < 0)
+			goto err_restore_page;
+		ret &= ~YT8824_UTP_TEMPLATE_MODE_MASK;
+		ret |= YT8824_UTP_TEMPLATE_TEST_MODE1;
+		ret = __phy_write_mmd(phydev, 0x1, YT8824_UTP_TEMPLATE_MODE_CTRL, ret);
+		if (ret < 0)
+			goto err_restore_page;
+	}
+
+err_restore_page:
+	return phy_restore_page(phydev, old_page, ret);
+}
+
+/**
+ * yt8824_sds_isolate_paged() - enable YT8824 serdes isolate.
+ * @phydev: a pointer to a &struct phy_device
+ * @reg_space: The reg page(YT8824_RSSR_FIBER_SPACE/YT8824_RSSR_UTP_SPACE).
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_sds_isolate_paged(struct phy_device *phydev,
+				    int reg_space)
+{
+	int old_page;
+	int ret = 0;
+
+	old_page = phy_select_page(phydev, reg_space);
+	if (old_page < 0)
+		goto err_restore_page;
+
+	if (reg_space == YT8824_RSSR_FIBER_SPACE) {
+		ret = __phy_read(phydev, MII_BMCR);
+		if (ret < 0)
+			goto err_restore_page;
+		/* isolation */
+		ret |= BMCR_ISOLATE;
+		ret = __phy_write(phydev, MII_BMCR, ret);
+		if (ret < 0)
+			goto err_restore_page;
+	}
+
+err_restore_page:
+	return phy_restore_page(phydev, old_page, ret);
+}
+
+/**
+ * yt8824_utp_softreset_paged() - config YT8824 UTP softreset.
+ * @phydev: a pointer to a &struct phy_device
+ * @reg_space: The reg page(YT8824_RSSR_FIBER_SPACE/YT8824_RSSR_UTP_SPACE).
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_utp_softreset_paged(struct phy_device *phydev,
+				      int reg_space)
+{
+	int old_page;
+	int ret = 0;
+	int val;
+
+	old_page = phy_select_page(phydev, reg_space);
+	if (old_page < 0)
+		goto err_restore_page;
+	if (reg_space == YT8824_RSSR_UTP_SPACE) {
+		ret = __phy_read(phydev, MII_BMCR);
+		if (ret < 0)
+			goto err_restore_page;
+		ret |= BMCR_RESET;
+		ret = __phy_write(phydev, MII_BMCR, ret);
+		if (ret < 0)
+			goto err_restore_page;
+		else
+			phy_unlock_mdio_bus(phydev);
+
+		/* wait until softreset done. */
+		return phy_read_poll_timeout(phydev, MII_BMCR, val, !(val & BMCR_RESET),
+					     50000, 600000, true);
+	}
+
+err_restore_page:
+	return phy_restore_page(phydev, old_page, ret);
+}
+
+/**
+ * yt8824_utp_normal_test_mode_paged() - config YT8824 to normal test mode.
+ * @phydev: a pointer to a &struct phy_device
+ * @reg_space: The reg page(YT8824_RSSR_FIBER_SPACE/YT8824_RSSR_UTP_SPACE).
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_utp_normal_test_mode_paged(struct phy_device *phydev,
+					     int reg_space)
+{
+	int old_page;
+	int ret = 0;
+
+	old_page = phy_select_page(phydev, reg_space);
+	if (old_page < 0)
+		goto err_restore_page;
+
+	if (reg_space == YT8824_RSSR_UTP_SPACE) {
+		/* normal mode */
+		ret = __phy_read_mmd(phydev, 0x1, YT8824_UTP_TEMPLATE_MODE_CTRL);
+		if (ret < 0)
+			goto err_restore_page;
+		ret &= ~YT8824_UTP_TEMPLATE_MODE_MASK;
+		ret = __phy_write_mmd(phydev, 0x1, YT8824_UTP_TEMPLATE_MODE_CTRL, ret);
+		if (ret < 0)
+			goto err_restore_page;
+	}
+
+err_restore_page:
+	return phy_restore_page(phydev, old_page, ret);
+}
+
+/**
+ * yt8824_sds_isolate_and_softreset_paged() - disable YT8824 serdes isolate
+ * and sds softreset.
+ * @phydev: a pointer to a &struct phy_device
+ * @reg_space: The reg page(YT8824_RSSR_FIBER_SPACE/YT8824_RSSR_UTP_SPACE).
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_sds_isolate_and_softreset_paged(struct phy_device *phydev,
+						  int reg_space)
+{
+	int old_page;
+	int ret = 0;
+	int val;
+
+	old_page = phy_select_page(phydev, reg_space);
+	if (old_page < 0)
+		goto err_restore_page;
+	if (reg_space == YT8824_RSSR_FIBER_SPACE) {
+		ret = __phy_read(phydev, MII_BMCR);
+		if (ret < 0)
+			goto err_restore_page;
+		/* disable isolation */
+		ret &= ~BMCR_ISOLATE;
+		/* soft reset */
+		ret |= BMCR_RESET;
+		ret = __phy_write(phydev, MII_BMCR, ret);
+		if (ret < 0)
+			goto err_restore_page;
+		else
+			phy_unlock_mdio_bus(phydev);
+		/* wait until softreset done. */
+		return phy_read_poll_timeout(phydev, MII_BMCR, val, !(val & BMCR_RESET),
+					     50000, 600000, true);
+	}
+
+err_restore_page:
+	return phy_restore_page(phydev, old_page, ret);
+}
+
+/**
+ * yt8824_soft_reset() - called to do PHY software reset
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_soft_reset(struct phy_device *phydev)
+{
+	int ret;
+
+	if (phydev->interface == PHY_INTERFACE_MODE_INTERNAL) {
+		/* invalid test mode */
+		ret = yt8824_utp_invalid_test_mode_paged(phydev, YT8824_RSSR_UTP_SPACE);
+		if (ret < 0)
+			return ret;
+		ret = yt8824_utp_softreset_paged(phydev, YT8824_RSSR_UTP_SPACE);
+		if (ret < 0)
+			return ret;
+		/* normal mode */
+		ret = yt8824_utp_normal_test_mode_paged(phydev, YT8824_RSSR_UTP_SPACE);
+		if (ret < 0)
+			return ret;
+	} else {
+		/* invalid test mode */
+		ret = yt8824_utp_invalid_test_mode_paged(phydev, YT8824_RSSR_UTP_SPACE);
+		if (ret < 0)
+			return ret;
+
+		/* sds isolation */
+		ret = yt8824_sds_isolate_paged(phydev, YT8824_RSSR_FIBER_SPACE);
+		if (ret < 0)
+			return ret;
+
+		/* utp soft reset */
+		ret = yt8824_utp_softreset_paged(phydev, YT8824_RSSR_UTP_SPACE);
+		if (ret < 0)
+			return ret;
+
+		/* normal mode */
+		ret = yt8824_utp_normal_test_mode_paged(phydev, YT8824_RSSR_UTP_SPACE);
+		if (ret < 0)
+			return ret;
+
+		/* sds soft reset and disable isolation */
+		ret = yt8824_sds_isolate_and_softreset_paged(phydev, YT8824_RSSR_FIBER_SPACE);
+		if (ret < 0)
+			return ret;
+	}
+	return 0;
+}
+
+/**
+ * yt8824_config_init_paged() - config external phy8824 init
+ * @phydev: target phy_device struct
+ * @reg_space: The reg page(YT8824_RSSR_FIBER_SPACE/YT8824_RSSR_UTP_SPACE).
+ *
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_config_init_paged(struct phy_device *phydev, int reg_space)
+{
+	struct yt8824_shared_priv *shared_priv;
+	int ret = 0, old_page;
+	u16 val_1, val_2, val_3, tmp;
+	u16 data = 0;
+	int port;
+
+	shared_priv = phy_package_get_priv(phydev);
+	port = phydev->mdio.addr - shared_priv->offset;
+	old_page = phy_select_page(phydev, reg_space);
+	if (old_page < 0)
+		goto err_restore_page;
+
+	if (reg_space == YT8824_RSSR_FIBER_SPACE) {
+		/* read efuse */
+		val_1 = ytphy_read_top_ext(phydev, 0xa13e);
+		if (val_1 < 0)
+			goto err_restore_page;
+
+		val_2 = ytphy_read_top_ext(phydev, 0xa13f);
+		if (val_2 < 0)
+			goto err_restore_page;
+
+		val_3 = ytphy_read_top_ext(phydev, 0xa140);
+		if (val_3 < 0)
+			goto err_restore_page;
+
+		if (port == 0) {
+			/* Serdes optimization */
+			ret = ytphy_write_ext(phydev, 0x04be, 0x000d);
+			if (ret < 0)
+				goto err_restore_page;
+
+			ret = ytphy_write_ext(phydev, 0x049f, 0x7ded);
+			if (ret < 0)
+				goto err_restore_page;
+
+			ret = ytphy_write_ext(phydev, 0x04a9, 0x009f);
+			if (ret < 0)
+				goto err_restore_page;
+
+			/* analog CDR */
+			ret = ytphy_write_ext(phydev, 0x0406, 0x0800);
+			if (ret < 0)
+				goto err_restore_page;
+
+			/* optimized VCO */
+			ret = ytphy_write_ext(phydev, 0x0438, 0x9024);
+			if (ret < 0)
+				goto err_restore_page;
+
+			ret = ytphy_write_ext(phydev, 0x0439, 0x00c0);
+			if (ret < 0)
+				goto err_restore_page;
+
+			/* optimized PLL lock */
+			ret = ytphy_read_ext(phydev, 0x0429);
+			if (ret < 0)
+				goto err_restore_page;
+
+			ret &= ~(BIT(13) | BIT(12));
+			tmp = (val_1 & (BIT(7) | BIT(6)) >> 6);
+			ret |= (tmp << 12);
+			ret = ytphy_write_ext(phydev, 0x0429, ret);
+			if (ret < 0)
+				goto err_restore_page;
+
+			ret = ytphy_read_ext(phydev, 0x0441);
+			if (ret < 0)
+				goto err_restore_page;
+
+			ret &= ~(BIT(1) | BIT(0));
+			tmp = (val_1 & (BIT(5) | BIT(4)) >> 4);
+			ret |= tmp;
+			ret = ytphy_write_ext(phydev, 0x0441, ret);
+			if (ret < 0)
+				goto err_restore_page;
+
+			ret = ytphy_read_ext(phydev, 0x042b);
+			if (ret < 0)
+				goto err_restore_page;
+
+			ret &= ~(BIT(13) | BIT(12));
+			tmp = (val_3 & (BIT(1) | BIT(0)));
+			ret |= (tmp << 12);
+			ret = ytphy_write_ext(phydev, 0x042b, ret);
+			if (ret < 0)
+				goto err_restore_page;
+
+			ret = ytphy_write_ext(phydev, 0x043a, 0x1006);
+			if (ret < 0)
+				goto err_restore_page;
+
+			ret = ytphy_write_ext(phydev, 0x042a, 0xf070);
+			if (ret < 0)
+				goto err_restore_page;
+
+			/* cable length threshold */
+			ret = ytphy_write_ext(phydev, 0x0491, 0x007f);
+			if (ret < 0)
+				goto err_restore_page;
+
+			ret = ytphy_write_ext(phydev, 0x0492, 0x7f7f);
+			if (ret < 0)
+				goto err_restore_page;
+
+			/* Serdes training threshold */
+			ret = ytphy_write_ext(phydev, 0x0454, 0x0f14);
+			if (ret < 0)
+				goto err_restore_page;
+
+			ret = ytphy_write_ext(phydev, 0x0497, 0x0a44);
+			if (ret < 0)
+				goto err_restore_page;
+
+			/* digital eye diagram of SerDes */
+			ret = ytphy_write_ext(phydev, 0x04cd, 0x0000);
+			if (ret < 0)
+				goto err_restore_page;
+
+			/* Serdes LDO */
+			ret = ytphy_read_ext(phydev, 0x04b5);
+			if (ret < 0)
+				goto err_restore_page;
+
+			ret &= ~(BIT(6) | BIT(5) | BIT(4));
+			tmp = (val_2 & (BIT(4) | BIT(3) | BIT(2)) >> 2);
+			ret |= (tmp << 4);
+			ret = ytphy_write_ext(phydev, 0x04b5, ret);
+			if (ret < 0)
+				goto err_restore_page;
+
+			ret = ytphy_read_ext(phydev, 0x04b4);
+			if (ret < 0)
+				goto err_restore_page;
+
+			ret &= ~(BIT(10) | BIT(9) | BIT(8));
+			tmp = (val_2 & (BIT(7) | BIT(6) | BIT(5)) >> 5);
+			ret |= (tmp << 8);
+			ret = ytphy_write_ext(phydev, 0x04b4, ret);
+			if (ret < 0)
+				goto err_restore_page;
+
+			/* optimized Serdes RX */
+			ret = ytphy_write_ext(phydev, 0x04af, 0x45e3);
+			if (ret < 0)
+				goto err_restore_page;
+
+			ret = ytphy_write_ext(phydev, 0x048a, 0x0fff);
+			if (ret < 0)
+				goto err_restore_page;
+
+			ret = ytphy_write_ext(phydev, 0x0408, 0x7c00);
+			if (ret < 0)
+				goto err_restore_page;
+
+			ret = ytphy_write_ext(phydev, 0x04d6, 0x007f);
+			if (ret < 0)
+				goto err_restore_page;
+
+			ret = ytphy_write_ext(phydev, 0x044f, 0xff08);
+			if (ret < 0)
+				goto err_restore_page;
+
+			/* optimized Serdes TX */
+			ret = ytphy_write_ext(phydev, 0x048e, 0x7d00);
+			if (ret < 0)
+				goto err_restore_page;
+
+			ret = ytphy_write_ext(phydev, 0x000d, 0x0606);
+			if (ret < 0)
+				goto err_restore_page;
+
+			/* Serdes manual config */
+			ret = ytphy_write_ext(phydev, 0x04b0, 0x0804);
+			if (ret < 0)
+				goto err_restore_page;
+
+			ret = ytphy_write_ext(phydev, 0x04b1, 0x7074);
+			if (ret < 0)
+				goto err_restore_page;
+
+			ret = ytphy_write_ext(phydev, 0x04af, 0x45e7);
+			if (ret < 0)
+				goto err_restore_page;
+
+			/* restart calibration */
+			ret = ytphy_write_ext(phydev, 0x0003, 0x5603);
+			if (ret < 0)
+				goto err_restore_page;
+
+			ret = ytphy_write_ext(phydev, 0x0492, 0x7fff);
+			if (ret < 0)
+				goto err_restore_page;
+
+			ret = ytphy_write_ext(phydev, 0x0492, 0x7f7f);
+			if (ret < 0)
+				goto err_restore_page;
+
+			ret = ytphy_write_ext(phydev, 0x2000, 0x0040);
+			if (ret < 0)
+				goto err_restore_page;
+
+			ret = ytphy_write_ext(phydev, 0x2000, 0x0000);
+			if (ret < 0)
+				goto err_restore_page;
+		}
+
+		/* TX preamble padded to 8; RX IPG always > 8 */
+		data &= ~YT8824_SDS_CFG_MIN_PRE_MASK;
+		data |= YT8824_SDS_TX_PRE_PADDING;
+		data |= YT8824_SDS_EN_FILL_PRE;
+		ret = __phy_write(phydev, MII_RESV1, data);
+		if (ret < 0)
+			goto err_restore_page;
+
+		data = __phy_read(phydev, MII_BMCR);
+		if (data < 0)
+			goto err_restore_page;
+		data |= BMCR_RESET;
+		data |= BMCR_ANENABLE;
+		ret = __phy_write(phydev, MII_BMCR, data);
+		if (ret < 0)
+			goto err_restore_page;
+	} else if (reg_space == YT8824_RSSR_UTP_SPACE) {
+		/* power down */
+		data = __phy_read(phydev, MII_BMCR);
+		if (data < 0)
+			goto err_restore_page;
+		data |= BMCR_PDOWN;
+		ret = __phy_write(phydev, MII_BMCR, data);
+		if (ret < 0)
+			goto err_restore_page;
+
+		/* pll calibration */
+		ret = ytphy_write_ext(phydev, 0x0001, 0x0003);
+		if (ret < 0)
+			goto err_restore_page;
+
+		ret = ytphy_write_ext(phydev, 0xa20e, 0x0cba);
+		if (ret < 0)
+			goto err_restore_page;
+
+		ret = ytphy_write_ext(phydev, 0xa20a, 0xc3f1);
+		if (ret < 0)
+			goto err_restore_page;
+
+		ret = ytphy_write_ext(phydev, 0xa20c, 0x1620);
+		if (ret < 0)
+			goto err_restore_page;
+
+		ret = ytphy_write_ext(phydev, 0xa2b6, 0x0a00);
+		if (ret < 0)
+			goto err_restore_page;
+
+		ret = ytphy_write_ext(phydev, 0xa2b6, 0x0e00);
+		if (ret < 0)
+			goto err_restore_page;
+
+		/* optimization utp */
+		ret = ytphy_write_ext(phydev, 0x0001, 0x0003);
+		if (ret < 0)
+			goto err_restore_page;
+
+		/* enable nibble */
+		ret = ytphy_write_ext(phydev, 0xa003, 0x0003);
+		if (ret < 0)
+			goto err_restore_page;
+
+		/* idle err detect enable */
+		ret = ytphy_write_ext(phydev, 0x03d0, 0x5210);
+		if (ret < 0)
+			goto err_restore_page;
+
+		/* optimized 2.5G long cable performance */
+		ret = ytphy_write_ext(phydev, 0x0372, 0x5038);
+		if (ret < 0)
+			goto err_restore_page;
+
+		ret = ytphy_write_ext(phydev, 0x037c, 0x6068);
+		if (ret < 0)
+			goto err_restore_page;
+
+		ret = ytphy_write_ext(phydev, 0x0388, 0x00a0);
+		if (ret < 0)
+			goto err_restore_page;
+
+		/* optimized fast retrain */
+		ret = ytphy_write_ext(phydev, 0x0359, 0x2140);
+		if (ret < 0)
+			goto err_restore_page;
+
+		ret = ytphy_write_ext(phydev, 0x000c, 0xc1a0);
+		if (ret < 0)
+			goto err_restore_page;
+
+		/* 2.5G template tone */
+		ret = ytphy_write_ext(phydev, 0xa2fa, 0x0083);
+		if (ret < 0)
+			goto err_restore_page;
+
+		ret = ytphy_write_ext(phydev, 0x04e2, 0x0149);
+		if (ret < 0)
+			goto err_restore_page;
+
+		/* optimized 2.5G template */
+		ret = ytphy_write_ext(phydev, 0x047e, 0x3939);
+		if (ret < 0)
+			goto err_restore_page;
+
+		ret = ytphy_write_ext(phydev, 0x047f, 0x3939);
+		if (ret < 0)
+			goto err_restore_page;
+
+		ret = ytphy_write_ext(phydev, 0x0480, 0x3939);
+		if (ret < 0)
+			goto err_restore_page;
+
+		ret = ytphy_write_ext(phydev, 0x0481, 0x3939);
+		if (ret < 0)
+			goto err_restore_page;
+
+		/* optimized 1000M cable length threshold */
+		ret = ytphy_write_ext(phydev, 0x0336, 0xab0a);
+		if (ret < 0)
+			goto err_restore_page;
+
+		ret = ytphy_write_ext(phydev, 0x0340, 0x301d);
+		if (ret < 0)
+			goto err_restore_page;
+
+		/* 100M template amplitude */
+		ret = ytphy_write_ext(phydev, 0x046e, 0x4545);
+		if (ret < 0)
+			goto err_restore_page;
+
+		ret = ytphy_write_ext(phydev, 0x046f, 0x4545);
+		if (ret < 0)
+			goto err_restore_page;
+
+		ret = ytphy_write_ext(phydev, 0x0470, 0x4545);
+		if (ret < 0)
+			goto err_restore_page;
+
+		ret = ytphy_write_ext(phydev, 0x0471, 0x4545);
+		if (ret < 0)
+			goto err_restore_page;
+
+		/* optimized 100M cable length threshold */
+		ret = ytphy_write_ext(phydev, 0x030b, 0xaa1d);
+		if (ret < 0)
+			goto err_restore_page;
+
+		ret = ytphy_write_ext(phydev, 0x071f, 0x0036);
+		if (ret < 0)
+			goto err_restore_page;
+
+		/* 10M template amplitude */
+		ret = ytphy_write_ext(phydev, 0x046b, 0x1818);
+		if (ret < 0)
+			goto err_restore_page;
+
+		ret = ytphy_write_ext(phydev, 0x046c, 0x1818);
+		if (ret < 0)
+			goto err_restore_page;
+
+		/* optimized 10M cable length threshold */
+		ret = ytphy_write_ext(phydev, 0x0466, 0x6c6c);
+		if (ret < 0)
+			goto err_restore_page;
+
+		ret = ytphy_write_ext(phydev, 0x0467, 0x6c6c);
+		if (ret < 0)
+			goto err_restore_page;
+
+		ret = ytphy_write_ext(phydev, 0x0468, 0x6c6c);
+		if (ret < 0)
+			goto err_restore_page;
+
+		ret = ytphy_write_ext(phydev, 0x0469, 0x6c6c);
+		if (ret < 0)
+			goto err_restore_page;
+
+		/* optimize utp 1000M performance */
+		ret = ytphy_write_ext(phydev, 0x034a, 0xff03);
+		if (ret < 0)
+			goto err_restore_page;
+
+		ret = ytphy_write_ext(phydev, 0x00f8, 0xb3ff);
+		if (ret < 0)
+			goto err_restore_page;
+
+		ret = ytphy_write_ext(phydev, 0x0059, 0x4040);
+		if (ret < 0)
+			goto err_restore_page;
+
+		ret = ytphy_write_ext(phydev, 0x032c, 0x5094);
+		if (ret < 0)
+			goto err_restore_page;
+
+		ret = ytphy_write_ext(phydev, 0x032d, 0xd094);
+		if (ret < 0)
+			goto err_restore_page;
+
+		ret = ytphy_write_ext(phydev, 0x032e, 0x5308);
+		if (ret < 0)
+			goto err_restore_page;
+
+		ret = ytphy_write_ext(phydev, 0x0322, 0x6440);
+		if (ret < 0)
+			goto err_restore_page;
+
+		ret = ytphy_write_ext(phydev, 0x04d3, 0x5220);
+		if (ret < 0)
+			goto err_restore_page;
+
+		ret = ytphy_write_ext(phydev, 0x04d2, 0x5220);
+		if (ret < 0)
+			goto err_restore_page;
+
+		/* optimized EMC CS */
+		ret = ytphy_write_ext(phydev, 0x00c8, 0xffff);
+		if (ret < 0)
+			goto err_restore_page;
+
+		ret = ytphy_write_ext(phydev, 0x00be, 0x6406);
+		if (ret < 0)
+			goto err_restore_page;
+
+		ret = ytphy_write_ext(phydev, 0x037a, 0x40ff);
+		if (ret < 0)
+			goto err_restore_page;
+
+		/* optimized EMC RE */
+		ret = ytphy_write_ext(phydev, 0x0482, 0xffff);
+		if (ret < 0)
+			goto err_restore_page;
+
+		ret = ytphy_write_ext(phydev, 0xa2d5, 0x1f1f);
+		if (ret < 0)
+			goto err_restore_page;
+
+		ret = ytphy_write_ext(phydev, 0xa2d6, 0x1f1f);
+		if (ret < 0)
+			goto err_restore_page;
+
+		ret = ytphy_write_ext(phydev, 0xa2d7, 0x1f1f);
+		if (ret < 0)
+			goto err_restore_page;
+
+		ret = ytphy_write_ext(phydev, 0xa2d8, 0x1f1f);
+		if (ret < 0)
+			goto err_restore_page;
+
+		ret = ytphy_write_ext(phydev, 0xa218, 0x006e);
+		if (ret < 0)
+			goto err_restore_page;
+
+		ret = ytphy_write_ext(phydev, 0xa01d, 0xfff0);
+		if (ret < 0)
+			goto err_restore_page;
+
+		ret = ytphy_write_ext(phydev, 0xa01e, 0xfff0);
 		if (ret < 0)
 			goto err_restore_page;
 
-		/* Enable WOL interrupt */
-		ret = __phy_modify(phydev, YTPHY_INTERRUPT_ENABLE_REG, 0,
-				   YTPHY_IER_WOL);
+		ret = ytphy_write_ext(phydev, 0xa01d, 0xffff);
 		if (ret < 0)
 			goto err_restore_page;
 
-	} else {
-		old_page = phy_select_page(phydev, YT8521_RSSR_UTP_SPACE);
-		if (old_page < 0)
+		ret = ytphy_write_ext(phydev, 0xa01e, 0xffff);
+		if (ret < 0)
 			goto err_restore_page;
+	}
 
-		/* Disable WOL feature */
-		mask = YTPHY_WCR_ENABLE | YTPHY_WCR_INTR_SEL;
-		ret = ytphy_modify_ext(phydev, YTPHY_WOL_CONFIG_REG, mask, 0);
+err_restore_page:
+	return phy_restore_page(phydev, old_page, ret);
+}
 
-		/* Disable WOL interrupt */
-		ret = __phy_modify(phydev, YTPHY_INTERRUPT_ENABLE_REG,
-				   YTPHY_IER_WOL, 0);
+/**
+ * yt8824_internal_config_init_paged() - config internal phy8824 init
+ * @phydev: target phy_device struct
+ * @reg_space: The reg page(YT8824_RSSR_FIBER_SPACE/YT8824_RSSR_UTP_SPACE).
+ *
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_internal_config_init_paged(struct phy_device *phydev,
+					     int reg_space)
+{
+	struct yt8824_shared_priv *shared_priv;
+	int old_page;
+	int port = 0;
+	int ret = 0;
+	u16 data;
+
+	old_page = phy_select_page(phydev, reg_space);
+	if (old_page < 0)
+		goto err_restore_page;
+
+	shared_priv = phy_package_get_priv(phydev);
+	port = phydev->mdio.addr - shared_priv->offset;
+	ret = ytphy_write_ext(phydev, 0x1, 0x3);
+	if (ret < 0)
+		goto err_restore_page;
+	data = __phy_read(phydev, MII_BMCR);
+	if (data < 0)
+		goto err_restore_page;
+	data |= BMCR_PDOWN;
+	ret = __phy_write(phydev, MII_BMCR, data);
+	if (ret < 0)
+		goto err_restore_page;
+	if (port == 0) {
+		ret = ytphy_write_ext(phydev, 0xa20e, 0xcba);
+		if (ret < 0)
+			goto err_restore_page;
+		ret = ytphy_write_ext(phydev, 0xa20a, 0xc3f1);
+		if (ret < 0)
+			goto err_restore_page;
+		ret = ytphy_write_ext(phydev, 0xa20c, 0x1620);
+		if (ret < 0)
+			goto err_restore_page;
+		ret = ytphy_write_ext(phydev, 0xa2b6, 0xa00);
+		if (ret < 0)
+			goto err_restore_page;
+		ret = ytphy_write_ext(phydev, 0xa2b6, 0xe00);
+		if (ret < 0)
+			goto err_restore_page;
+		ret = ytphy_write_ext(phydev, 0xa003, 0x3);
+		if (ret < 0)
+			goto err_restore_page;
+	}
+	ret = ytphy_write_ext(phydev, 0x3d0, 0x5210);
+	if (ret < 0)
+		goto err_restore_page;
+	ret = ytphy_write_ext(phydev, 0x372, 0x5038);
+	if (ret < 0)
+		goto err_restore_page;
+	ret = ytphy_write_ext(phydev, 0x37c, 0x6068);
+	if (ret < 0)
+		goto err_restore_page;
+	ret = ytphy_write_ext(phydev, 0x388, 0xa0);
+	if (ret < 0)
+		goto err_restore_page;
+	ret = ytphy_write_ext(phydev, 0x359, 0x2140);
+	if (ret < 0)
+		goto err_restore_page;
+	ret = ytphy_write_top_ext(phydev, 0xa2fa, 0x83);
+	if (ret < 0)
+		goto err_restore_page;
+	ret = ytphy_write_ext(phydev, 0x4e2, 0x149);
+	if (ret < 0)
+		goto err_restore_page;
+	/* 2.5G tempate */
+	ret = ytphy_write_ext(phydev, 0x47e, 0x3939);
+	if (ret < 0)
+		goto err_restore_page;
+	ret = ytphy_write_ext(phydev, 0x47f, 0x3939);
+	if (ret < 0)
+		goto err_restore_page;
+	ret = ytphy_write_ext(phydev, 0x480, 0x3939);
+	if (ret < 0)
+		goto err_restore_page;
+	ret = ytphy_write_ext(phydev, 0x481, 0x3939);
+	if (ret < 0)
+		goto err_restore_page;
+	/* 1000 cable length threshold */
+	ret = ytphy_write_ext(phydev, 0x336, 0xab0a);
+	if (ret < 0)
+		goto err_restore_page;
+	ret = ytphy_write_ext(phydev, 0x340, 0x301d);
+	if (ret < 0)
+		goto err_restore_page;
+	/* 1000 performance */
+	ret = ytphy_write_ext(phydev, 0x34a, 0xff03);
+	if (ret < 0)
+		goto err_restore_page;
+	ret = ytphy_write_ext(phydev, 0xf8, 0xb3ff);
+	if (ret < 0)
+		goto err_restore_page;
+	ret = ytphy_write_ext(phydev, 0x32c, 0x5094);
+	if (ret < 0)
+		goto err_restore_page;
+	ret = ytphy_write_ext(phydev, 0x32d, 0xd094);
+	if (ret < 0)
+		goto err_restore_page;
+	ret = ytphy_write_ext(phydev, 0x32e, 0x5308);
+	if (ret < 0)
+		goto err_restore_page;
+	ret = ytphy_write_ext(phydev, 0x322, 0x6440);
+	if (ret < 0)
+		goto err_restore_page;
+	ret = ytphy_write_ext(phydev, 0x4d3, 0x5220);
+	if (ret < 0)
+		goto err_restore_page;
+	ret = ytphy_write_ext(phydev, 0x4d2, 0x5220);
+	if (ret < 0)
+		goto err_restore_page;
+	/* 100 tempate */
+	ret = ytphy_write_ext(phydev, 0x46e, 0x4545);
+	if (ret < 0)
+		goto err_restore_page;
+	ret = ytphy_write_ext(phydev, 0x46f, 0x4545);
+	if (ret < 0)
+		goto err_restore_page;
+	ret = ytphy_write_ext(phydev, 0x470, 0x4545);
+	if (ret < 0)
+		goto err_restore_page;
+	ret = ytphy_write_ext(phydev, 0x471, 0x4545);
+	if (ret < 0)
+		goto err_restore_page;
+	/* 100 cable length threshold */
+	ret = ytphy_write_ext(phydev, 0x30b, 0xaa1d);
+	if (ret < 0)
+		goto err_restore_page;
+	ret = ytphy_write_ext(phydev, 0x71f, 0x36);
+	if (ret < 0)
+		goto err_restore_page;
+	/* 10 tempate */
+	ret = ytphy_write_ext(phydev, 0x46b, 0x1818);
+	if (ret < 0)
+		goto err_restore_page;
+	ret = ytphy_write_ext(phydev, 0x46c, 0x1818);
+	if (ret < 0)
+		goto err_restore_page;
+	/* 10 tempate MAU*/
+	ret = ytphy_write_ext(phydev, 0x466, 0x6c6c);
+	if (ret < 0)
+		goto err_restore_page;
+	ret = ytphy_write_ext(phydev, 0x467, 0x6c6c);
+	if (ret < 0)
+		goto err_restore_page;
+	ret = ytphy_write_ext(phydev, 0x468, 0x6c6c);
+	if (ret < 0)
+		goto err_restore_page;
+	ret = ytphy_write_ext(phydev, 0x469, 0x6c6c);
+	if (ret < 0)
+		goto err_restore_page;
+	/* EMC CS */
+	ret = ytphy_write_ext(phydev, 0xc8, 0xfff);
+	if (ret < 0)
+		goto err_restore_page;
+	ret = ytphy_write_ext(phydev, 0xbe, 0x6406);
+	if (ret < 0)
+		goto err_restore_page;
+	ret = ytphy_write_ext(phydev, 0x37a, 0x40ff);
+	if (ret < 0)
+		goto err_restore_page;
+	/* EMC RE*/
+	ret = ytphy_write_ext(phydev, 0x482, 0xffff);
+	if (ret < 0)
+		goto err_restore_page;
+	if (port == 0) {
+		ret = ytphy_write_ext(phydev, 0x482, 0xffff);
+		if (ret < 0)
+			goto err_restore_page;
+		ret = ytphy_write_ext(phydev, 0xa2d5, 0x1f1f);
+		if (ret < 0)
+			goto err_restore_page;
+		ret = ytphy_write_ext(phydev, 0xa2d6, 0x1f1f);
+		if (ret < 0)
+			goto err_restore_page;
+		ret = ytphy_write_ext(phydev, 0xa2d7, 0x1f1f);
+		if (ret < 0)
+			goto err_restore_page;
+		ret = ytphy_write_ext(phydev, 0xa2d8, 0x1f1f);
+		if (ret < 0)
+			goto err_restore_page;
+		ret = ytphy_write_ext(phydev, 0xa218, 0x6e);
+		if (ret < 0)
+			goto err_restore_page;
+		ret = ytphy_write_ext(phydev, 0xa01d, 0xfff0);
+		if (ret < 0)
+			goto err_restore_page;
+		ret = ytphy_write_ext(phydev, 0xa01e, 0xfff0);
+		if (ret < 0)
+			goto err_restore_page;
+		ret = ytphy_write_ext(phydev, 0xa01d, 0xffff);
+		if (ret < 0)
+			goto err_restore_page;
+		ret = ytphy_write_ext(phydev, 0xa01e, 0xffff);
 		if (ret < 0)
 			goto err_restore_page;
 	}
+	ret = ytphy_write_ext(phydev, 0xc, 0x41a1);
+	if (ret < 0)
+		goto err_restore_page;
+	data = __phy_read_mmd(phydev, 0x1, YT8824_UTP_TEMPLATE_MODE_CTRL);
+	if (data < 0)
+		goto err_restore_page;
+	data &= ~YT8824_UTP_TEMPLATE_MODE_MASK;
+	data |= YT8824_UTP_TEMPLATE_TEST_MODE1;
+	ret = __phy_write_mmd(phydev, 0x1, YT8824_UTP_TEMPLATE_MODE_CTRL, data);
+	data = __phy_read(phydev, MII_BMCR);
+	if (data < 0)
+		goto err_restore_page;
+	data |= BMCR_RESET;
+	data |= BMCR_ANENABLE;
+	ret = __phy_write(phydev, MII_BMCR, data);
+	if (ret < 0)
+		goto err_restore_page;
+	data = __phy_read_mmd(phydev, 0x1, YT8824_UTP_TEMPLATE_MODE_CTRL);
+	if (data < 0)
+		goto err_restore_page;
+	data &= ~YT8824_UTP_TEMPLATE_MODE_MASK;
+	ret = __phy_write_mmd(phydev, 0x1, YT8824_UTP_TEMPLATE_MODE_CTRL, data);
 
 err_restore_page:
 	return phy_restore_page(phydev, old_page, ret);
@@ -2437,6 +3494,217 @@ static int ytphy_utp_read_abilities(struct phy_device *phydev)
 	return 0;
 }
 
+/**
+ * yt8824_config_init() - phy initializatioin
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_config_init(struct phy_device *phydev)
+{
+	int ret;
+
+	if (phydev->interface == PHY_INTERFACE_MODE_INTERNAL) {
+		ret = yt8824_internal_config_init_paged(phydev, YT8824_RSSR_UTP_SPACE);
+		if (ret < 0)
+			return ret;
+	} else {
+		ret = yt8824_config_init_paged(phydev, YT8824_RSSR_FIBER_SPACE);
+		if (ret < 0)
+			return ret;
+		ret = yt8824_config_init_paged(phydev, YT8824_RSSR_UTP_SPACE);
+		if (ret < 0)
+			return ret;
+	}
+	ret = yt8824_soft_reset(phydev);
+	if (ret < 0)
+		return ret;
+
+	phydev_dbg(phydev, "%s done, phy addr: %d\n",
+		   __func__, phydev->mdio.addr);
+
+	return 0;
+}
+
+/**
+ * yt8824_config_intr() - phy8824 interrupt configuration
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_config_intr(struct phy_device *phydev)
+{
+	struct yt8824_shared_priv *shared_priv;
+	int ret = 0, old_page;
+	int port;
+
+	shared_priv = phy_package_get_priv(phydev);
+	if ((phydev->mdio.addr - shared_priv->offset) < 2)
+		port = 0;
+	else
+		port = 2;
+	old_page = phy_select_page(phydev, YT8824_RSSR_UTP_SPACE);
+	if (old_page < 0)
+		goto err_restore_page;
+
+	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
+		/* top ext reg 0xa000
+		 * bit6 int_polarity 1'b0 low active, 1'b1 high active
+		 */
+		ret = ytphy_read_top_ext(phydev, YT8521_REG_SPACE_SELECT_REG);
+		if (ret < 0)
+			goto err_restore_page;
+
+		ret = ytphy_write_top_ext(phydev, YT8521_REG_SPACE_SELECT_REG, ret & (~BIT(6)));
+		if (ret < 0)
+			goto err_restore_page;
+
+		/* top ext reg 0xa019
+		 * bit5 intr_phy_pulse_en 1'b0 level, 1'b1 pulse
+		 */
+		ret = ytphy_read_top_ext(phydev, 0xa019);
+		if (ret < 0)
+			goto err_restore_page;
+
+		ret = ytphy_write_top_ext(phydev, 0xa019, ret | BIT(5));
+		if (ret < 0)
+			goto err_restore_page;
+
+		/* top ext reg 0xa018 phy_pulse_lth bit14:8
+		 * top ext reg 0xa019 timer_tick_sel bit8:7
+		 * bit14:8 phy_pulse_lth bit8:7 timer_tick_sel   pulse width
+		 * 0x007a		 0x0002		         10ms pulse width
+		 * 0x0064		 0x0001		         1ms pulse width
+		 * 0x004e		 0x0000		         100ms pulse width
+		 * 0x0009		 0x0000		         10ms pulse width(default)
+		 */
+		ret = ytphy_read_top_ext(phydev, 0xa018);
+		if (ret < 0)
+			goto err_restore_page;
+
+		ret &= ~0x7f00;
+		ret |= (0x0009 << 8);
+		ret = ytphy_write_top_ext(phydev, 0xa018, ret);
+		if (ret < 0)
+			goto err_restore_page;
+
+		ret = ytphy_read_top_ext(phydev, 0xa019);
+		if (ret < 0)
+			goto err_restore_page;
+
+		ret &= ~0x180;
+		ret |= (0x0000 << 7);
+		ret = ytphy_write_top_ext(phydev, 0xa019, ret);
+		if (ret < 0)
+			goto err_restore_page;
+
+		/* top ext reg 0xa01c interrupt state(Read Clear)
+		 * bit11 PHY3 1: phy3 link up/down occurs, 0: not occur
+		 * bit10 PHY2 1: phy2 link up/down occurs, 0: not occur
+		 * bit9  PHY1 1: phy1 link up/down occurs, 0: not occur
+		 * bit8  PHY0 1: phy0 link up/down occurs, 0: not occur
+		 *
+		 * bit7  PHY3 1: phy3 link down occurs, 0: not occur
+		 * bit6  PHY2 1: phy2 link down occurs, 0: not occur
+		 * bit5  PHY1 1: phy1 link down occurs, 0: not occur
+		 * bit4  PHY0 1: phy0 link down occurs, 0: not occur
+		 *
+		 * bit3  PHY3 1: phy3 link up occurs, 0: not occur
+		 * bit2  PHY2 1: phy2 link up occurs, 0: not occur
+		 * bit1  PHY1 1: phy1 link up occurs, 0: not occur
+		 * bit0  PHY0 1: phy0 link up occurs, 0: not occur
+		 */
+		ret = ytphy_read_top_ext(phydev, 0xa01c);
+		if (ret < 0)
+			goto err_restore_page;
+
+		/* top ext reg 0xa01a
+		 * bit5 intn_wol_mode 1'b1 enable intn_wol pin output
+		 * bit4 intn_mode 1'b1 enable intn pin output
+		 */
+		ret = ytphy_read_top_ext(phydev, 0xa01a);
+		if (ret < 0)
+			goto err_restore_page;
+
+		ret &= ~BIT(5);
+		ret |= BIT(4);
+		ret = ytphy_write_top_ext(phydev, 0xa01a, ret);
+		if (ret < 0)
+			goto err_restore_page;
+
+		/* top ext reg 0xa01b interrupt mask
+		 * bit11 1'b1 enable interrupt signal(link up/down) output to interrupt pin
+		 * bit7 1'b1 enable interrupt signal(link down) output to interrupt pin
+		 * bit3 1'b1 enable interrupt signal(link up) output to interrupt pin
+		 *
+		 * bit10 1'b1 enable interrupt signal(link up/down) output to interrupt pin
+		 * bit6 1'b1 enable interrupt signal(link down) output to interrupt pin
+		 * bit2 1'b1 enable interrupt signal(link up) output to interrupt pin
+		 *
+		 * bit9 1'b1 enable interrupt signal(link up/down) output to interrupt pin
+		 * bit5 1'b1 enable interrupt signal(link down) output to interrupt pin
+		 * bit1 1'b1 enable interrupt signal(link up) output to interrupt pin
+		 *
+		 * bit8 1'b1 enable interrupt signal(link up/down) output to interrupt pin
+		 * bit4 1'b1 enable interrupt signal(link down) output to interrupt pin
+		 * bit0 1'b1 enable interrupt signal(link up) output to interrupt pin
+		 */
+		ret = ytphy_read_top_ext(phydev, 0xa01b);
+		if (ret < 0)
+			goto err_restore_page;
+
+		ret |= (BIT(port + 8) | BIT(port + 4) | BIT(port));
+		ret = ytphy_write_top_ext(phydev, 0xa01b, ret);
+		if (ret < 0)
+			goto err_restore_page;
+	}
+
+err_restore_page:
+	return phy_restore_page(phydev, old_page, ret);
+}
+
+/**
+ * yt8824_handle_interrupt() - phy8824 interrupt handle
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * Returns: 0 or negative errno code
+ */
+static irqreturn_t yt8824_handle_interrupt(struct phy_device *phydev)
+{
+	int ret = 0, old_page;
+
+	old_page = phy_select_page(phydev, YT8824_RSSR_UTP_SPACE);
+	if (old_page < 0)
+		goto err_restore_page;
+
+	/* top ext reg 0xa01c interrupt state(Read Clear)
+	 * bit11 PHY3 interrupt 1: phy3 link up/down interrupt happened, 0: interrupt not happened
+	 * bit10 PHY2 interrupt 1: phy2 link up/down interrupt happened, 0: interrupt not happened
+	 * bit9  PHY1 interrupt 1: phy1 link up/down interrupt happened, 0: interrupt not happened
+	 * bit8  PHY0 interrupt 1: phy0 link up/down interrupt happened, 0: interrupt not happened
+	 * bit7  PHY3 link down 1: phy3 link down interrupt happened, 0: interrupt not happened
+	 * bit6  PHY2 link down 1: phy2 link down interrupt happened, 0: interrupt not happened
+	 * bit5  PHY1 link down 1: phy1 link down interrupt happened, 0: interrupt not happened
+	 * bit4  PHY0 link down 1: phy0 link down interrupt happened, 0: interrupt not happened
+	 * bit3  PHY3 link up 1: phy3 link up interrupt happened, 0: interrupt not happened
+	 * bit2  PHY2 link up 1: phy2 link up interrupt happened, 0: interrupt not happened
+	 * bit1  PHY1 link up 1: phy1 link up interrupt happened, 0: interrupt not happened
+	 * bit0  PHY0 link up 1: phy0 link up interrupt happened, 0: interrupt not happened
+	 */
+	ret = ytphy_read_top_ext(phydev, 0xa01c);
+	if (ret < 0)
+		goto err_restore_page;
+
+err_restore_page:
+	ret = phy_restore_page(phydev, old_page, ret);
+	if (ret > 0) {
+		phy_trigger_machine(phydev);
+		return IRQ_HANDLED;
+	} else {
+		return IRQ_NONE;
+	}
+}
+
 /**
  * yt8521_get_features_paged() -  read supported link modes for one page
  * @phydev: a pointer to a &struct phy_device
@@ -3059,6 +4327,397 @@ static int yt8821_resume(struct phy_device *phydev)
 	return yt8821_modify_utp_fiber_bmcr(phydev, BMCR_PDOWN, 0);
 }
 
+/**
+ * yt8824_aneg_done()  - check negotiation state.
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_aneg_done(struct phy_device *phydev)
+{
+	int link = 0;
+	int old_page;
+	int ret = 0;
+
+	old_page = phy_select_page(phydev, YT8824_RSSR_UTP_SPACE);
+	if (old_page < 0)
+		goto err_restore_page;
+
+	link = !!(__phy_read(phydev, YTPHY_SPECIFIC_STATUS_REG) &
+			     YTPHY_SSR_LINK);
+
+	phydev_dbg(phydev, "%s, phy addr: %d, link_utp: %d\n",
+		   __func__, phydev->mdio.addr, link);
+
+err_restore_page:
+	return phy_restore_page(phydev, old_page, ret);
+}
+
+/**
+ * yt8824_read_status_paged() -  determines the speed and duplex of one page
+ * @phydev: a pointer to a &struct phy_device
+ * @page: The reg page(YT8824_RSSR_FIBER_SPACE/YT8824_RSSR_UTP_SPACE) to
+ * operate.
+ * @status: The link status, include speed, duplex and link state.
+ * @lpa: link partner advertising.
+ *
+ * Returns: 1 (utp or fiber link),0 (no link) or negative errno code
+ */
+static int yt8824_read_status_paged(struct phy_device *phydev, int page,
+				    int *status, int *lpa)
+{
+	int old_page;
+	int ret = 0;
+
+	page &= YT8824_RSSR_SPACE_MASK;
+	old_page = phy_select_page(phydev, page);
+	if (old_page < 0)
+		goto err_restore_page;
+
+	ret = __phy_read(phydev, MII_LPA);
+	*lpa = ret;
+	if (ret < 0)
+		goto err_restore_page;
+
+	ret = __phy_read(phydev, YTPHY_SPECIFIC_STATUS_REG);
+	*status = ret;
+	if (ret < 0)
+		goto err_restore_page;
+
+	ret = !!(*status & YTPHY_SSR_LINK);
+
+err_restore_page:
+	return phy_restore_page(phydev, old_page, ret);
+}
+
+/**
+ * yt8824_read_status() -  determines the negotiated speed and duplex
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_read_status(struct phy_device *phydev)
+{
+	int link;
+	int lpa;
+	int val;
+
+	phydev->pause = 0;
+	phydev->asym_pause = 0;
+	phydev->link = 0;
+	phydev->speed = SPEED_UNKNOWN;
+	phydev->duplex = DUPLEX_UNKNOWN;
+
+	link = yt8824_read_status_paged(phydev,
+					YT8824_RSSR_UTP_SPACE, &val, &lpa);
+	if (link < 0)
+		return link;
+
+	if (link) {
+		phydev->link = 1;
+		phydev->pause = !!(lpa & BIT(10));
+		phydev->asym_pause = !!(lpa & BIT(11));
+
+		/* update speed & duplex */
+		yt8821_adjust_status(phydev, val);
+	} else {
+		phydev->link = 0;
+		phydev->pause = 0;
+		phydev->asym_pause = 0;
+		phydev->speed = SPEED_UNKNOWN;
+		phydev->duplex = DUPLEX_UNKNOWN;
+	}
+
+	return 0;
+}
+
+/**
+ * yt8824_utp_power_on(): utp power on.
+ * @phydev: a pointer to a &struct phy_device
+ * @reg_space: The reg page(YT8824_RSSR_FIBER_SPACE/YT8824_RSSR_UTP_SPACE)
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_utp_power_on(struct phy_device *phydev,
+			       int reg_space)
+{
+	int old_page;
+	int ret = 0;
+
+	old_page = phy_select_page(phydev, reg_space);
+	if (old_page < 0)
+		goto err_restore_page;
+
+	ret = __phy_read(phydev, MII_BMCR);
+	if (ret < 0)
+		goto err_restore_page;
+
+	ret &= ~BMCR_PDOWN;
+	ret &= ~BMCR_ISOLATE;
+
+	ret = __phy_write(phydev, MII_BMCR, ret);
+
+err_restore_page:
+	return phy_restore_page(phydev, old_page, ret);
+}
+
+/**
+ * yt8824_utp_power_down(): utp power down.
+ * @phydev: a pointer to a &struct phy_device
+ * @reg_space: The reg page(YT8824_RSSR_FIBER_SPACE/YT8824_RSSR_UTP_SPACE)
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_utp_power_down(struct phy_device *phydev,
+				 int reg_space)
+{
+	int ret = 0, old_page;
+
+	old_page = phy_select_page(phydev, YT8824_RSSR_UTP_SPACE);
+	if (old_page < 0)
+		goto err_restore_page;
+
+	ret = __phy_read(phydev, MII_BMCR);
+	if (ret < 0)
+		goto err_restore_page;
+
+	ret = __phy_write(phydev, MII_BMCR, ret | BMCR_PDOWN);
+	if (ret < 0)
+		goto err_restore_page;
+
+err_restore_page:
+	return phy_restore_page(phydev, old_page, ret);
+}
+
+/**
+ * yt8824_power_on()  - set utp power on.
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * NOTE: need WA like softreset
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_power_on(struct phy_device *phydev)
+{
+	int ret;
+
+	if (phydev->interface == PHY_INTERFACE_MODE_INTERNAL) {
+		/* invalid test mode */
+		ret = yt8824_utp_invalid_test_mode_paged(phydev, YT8824_RSSR_UTP_SPACE);
+		if (ret < 0)
+			return ret;
+		/* utp power on */
+		ret = yt8824_utp_power_on(phydev, YT8824_RSSR_UTP_SPACE);
+		if (ret < 0)
+			return ret;
+		/* normal mode */
+		ret = yt8824_utp_normal_test_mode_paged(phydev, YT8824_RSSR_UTP_SPACE);
+		if (ret < 0)
+			return ret;
+	} else {
+		/* invalid test mode */
+		ret = yt8824_utp_invalid_test_mode_paged(phydev, YT8824_RSSR_UTP_SPACE);
+		if (ret < 0)
+			return ret;
+
+		/* sds isolation */
+		ret = yt8824_sds_isolate_paged(phydev, YT8824_RSSR_FIBER_SPACE);
+		if (ret < 0)
+			return ret;
+
+		/* utp power on */
+		ret = yt8824_utp_power_on(phydev, YT8824_RSSR_UTP_SPACE);
+		if (ret < 0)
+			return ret;
+
+		/* normal mode */
+		ret = yt8824_utp_normal_test_mode_paged(phydev, YT8824_RSSR_UTP_SPACE);
+		if (ret < 0)
+			return ret;
+
+		/* sds soft reset and disable isolation */
+		ret = yt8824_sds_isolate_and_softreset_paged(phydev, YT8824_RSSR_FIBER_SPACE);
+		if (ret < 0)
+			return ret;
+	}
+	return 0;
+}
+
+/**
+ * yt8824_resume() - resume the hardware
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_resume(struct phy_device *phydev)
+{
+	return yt8824_power_on(phydev);
+}
+
+/**
+ * yt8824_power_down()  - set utp power down.
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * NOTE: need WA like softreset
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_power_down(struct phy_device *phydev)
+{
+	int ret;
+
+	if (phydev->interface == PHY_INTERFACE_MODE_INTERNAL) {
+		/* invalid test mode */
+		ret = yt8824_utp_invalid_test_mode_paged(phydev, YT8824_RSSR_UTP_SPACE);
+		if (ret < 0)
+			return ret;
+		/* utp power down */
+		ret = yt8824_utp_power_down(phydev, YT8824_RSSR_UTP_SPACE);
+		if (ret < 0)
+			return ret;
+		/* normal mode */
+		ret = yt8824_utp_normal_test_mode_paged(phydev, YT8824_RSSR_UTP_SPACE);
+		if (ret < 0)
+			return ret;
+	} else {
+		/* invalid test mode */
+		ret = yt8824_utp_invalid_test_mode_paged(phydev, YT8824_RSSR_UTP_SPACE);
+		if (ret < 0)
+			return ret;
+
+		/* sds isolation */
+		ret = yt8824_sds_isolate_paged(phydev, YT8824_RSSR_FIBER_SPACE);
+		if (ret < 0)
+			return ret;
+
+		/* utp power down */
+		ret = yt8824_utp_power_down(phydev, YT8824_RSSR_UTP_SPACE);
+		if (ret < 0)
+			return ret;
+
+		/* normal mode */
+		ret = yt8824_utp_normal_test_mode_paged(phydev, YT8824_RSSR_UTP_SPACE);
+		if (ret < 0)
+			return ret;
+
+		/* sds soft reset and disable isolation */
+		ret = yt8824_sds_isolate_and_softreset_paged(phydev,
+							     YT8824_RSSR_FIBER_SPACE);
+		if (ret < 0)
+			return ret;
+	}
+	return 0;
+}
+
+/**
+ * yt8824_suspend() - suspend the hardware
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_suspend(struct phy_device *phydev)
+{
+	return yt8824_power_down(phydev);
+}
+
+/**
+ * yt8824_config_aneg()
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_config_aneg(struct phy_device *phydev)
+{
+	int phy_ctrl = 0;
+	int old_page;
+	int ret = 0;
+
+	old_page = phy_select_page(phydev, YT8824_RSSR_UTP_SPACE);
+	if (old_page < 0)
+		goto err_restore_page;
+
+	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)
+		goto err_restore_page;
+	else
+		phy_unlock_mdio_bus(phydev);
+
+	return genphy_config_aneg(phydev);
+
+err_restore_page:
+	return phy_restore_page(phydev, old_page, ret);
+}
+
+/**
+ * yt8824_phy_package_probe_once()  - init phy packet for phy8824.
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_phy_package_probe_once(struct phy_device *phydev)
+{
+	struct yt8824_shared_priv *priv = phy_package_get_priv(phydev);
+	struct device_node *np = phy_package_get_node(phydev);
+	const char *interface_mode_name;
+
+	priv->interface_mode = PHY_INTERFACE_MODE_NA;
+	priv->offset = INTERNAL_PHY8824_TOP_EXTEND_OFFSET;
+	if (!of_property_read_string(np, "motorcomm,interface-mode",
+				     &interface_mode_name)) {
+		if (!strcasecmp(interface_mode_name,
+				phy_modes(PHY_INTERFACE_MODE_USXGMII))) {
+			priv->interface_mode = PHY_INTERFACE_MODE_USXGMII;
+			priv->offset = EXTERNAL_PHY8824_TOP_EXTEND_OFFSET;
+		} else if (!strcasecmp(interface_mode_name,
+				       phy_modes(PHY_INTERFACE_MODE_INTERNAL))) {
+			priv->interface_mode = PHY_INTERFACE_MODE_INTERNAL;
+			priv->offset = INTERNAL_PHY8824_TOP_EXTEND_OFFSET;
+		} else {
+			return -EINVAL;
+		}
+	}
+	return 0;
+}
+
+/**
+ * yt8824_probe() - phy8824 probe.
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_probe(struct phy_device *phydev)
+{
+	struct device *dev = &phydev->mdio.dev;
+	struct yt8824_shared_priv *shared_priv;
+	struct yt8521_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 = yt8824_phy_package_probe_once(phydev);
+		if (ret)
+			return ret;
+	}
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	phydev->priv = priv;
+
+	return 0;
+}
+
 static struct phy_driver motorcomm_phy_drvs[] = {
 	{
 		PHY_ID_MATCH_EXACT(PHY_ID_YT8511),
@@ -3145,13 +4804,31 @@ static struct phy_driver motorcomm_phy_drvs[] = {
 		.suspend		= yt8821_suspend,
 		.resume			= yt8821_resume,
 	},
+	{
+		PHY_ID_MATCH_EXACT(PHY_ID_YT8824),
+		.name			= "YT8824 Quad Ports 2.5Gbps Ethernet",
+		.get_features		= yt8821_get_features,
+		.read_page		= yt8824_read_page,
+		.write_page		= yt8824_write_page,
+		.config_intr            = yt8824_config_intr,
+		.handle_interrupt	= yt8824_handle_interrupt,
+		.probe		        = yt8824_probe,
+		.config_aneg		= yt8824_config_aneg,
+		.aneg_done		= yt8824_aneg_done,
+		.config_init		= yt8824_config_init,
+		.read_status		= yt8824_read_status,
+		.soft_reset		= yt8824_soft_reset,
+		.suspend		= yt8824_suspend,
+		.resume			= yt8824_resume,
+	},
 };
 
 module_phy_driver(motorcomm_phy_drvs);
 
-MODULE_DESCRIPTION("Motorcomm 8511/8521/8531/8531S/8821 PHY driver");
+MODULE_DESCRIPTION("Motorcomm 8511/8521/8531/8531S/8821/8824 PHY driver");
 MODULE_AUTHOR("Peter Geis");
 MODULE_AUTHOR("Frank");
+MODULE_AUTHOR("Kyle");
 MODULE_LICENSE("GPL");
 
 static const struct mdio_device_id __maybe_unused motorcomm_tbl[] = {
@@ -3161,6 +4838,7 @@ static const struct mdio_device_id __maybe_unused motorcomm_tbl[] = {
 	{ PHY_ID_MATCH_EXACT(PHY_ID_YT8531) },
 	{ PHY_ID_MATCH_EXACT(PHY_ID_YT8531S) },
 	{ PHY_ID_MATCH_EXACT(PHY_ID_YT8821) },
+	{ PHY_ID_MATCH_EXACT(PHY_ID_YT8824) },
 	{ /* sentinel */ }
 };
 
-- 
2.25.1


^ permalink raw reply related

* [PATCH net] hinic: fix leak of ethtool RSS user configuration buffers
From: Chenguang Zhao @ 2026-07-20  9:11 UTC (permalink / raw)
  To: cai.huoqing, andrew+netdev, davem, edumazet, kuba, pabeni
  Cc: netdev, chenguang.zhao, Chenguang Zhao

From: Chenguang Zhao <zhaochenguang@kylinos.cn>

rss_indir_user and rss_hkey_user are allocated in __set_rss_rxfh() when
the user configures RSS via ethtool, but hinic_remove() never frees them.
free_netdev() only releases the nic_dev structure itself, so the separately
allocated buffers are leaked on driver unload.

Free both buffers in hinic_remove() after unregister_netdev().

Fixes: 4fdc51bb4e92 ("hinic: add support for rss parameters with ethtool")
Signed-off-by: Chenguang Zhao <zhaochenguang@kylinos.cn>
---
 drivers/net/ethernet/huawei/hinic/hinic_main.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/net/ethernet/huawei/hinic/hinic_main.c b/drivers/net/ethernet/huawei/hinic/hinic_main.c
index 42f4792d255b..7d9c46004bcd 100644
--- a/drivers/net/ethernet/huawei/hinic/hinic_main.c
+++ b/drivers/net/ethernet/huawei/hinic/hinic_main.c
@@ -1433,6 +1433,9 @@ static void hinic_remove(struct pci_dev *pdev)
 
 	hinic_free_intr_coalesce(nic_dev);
 
+	kfree(nic_dev->rss_indir_user);
+	kfree(nic_dev->rss_hkey_user);
+
 	hinic_port_del_mac(nic_dev, netdev->dev_addr, 0);
 
 	hinic_hwdev_cb_unregister(nic_dev->hwdev,
-- 
2.25.1


^ permalink raw reply related

* Re: [PATCH net] net/mlx5: FW tracer, clamp firmware-reported num_string_db
From: Leon Romanovsky @ 2026-07-20  9:10 UTC (permalink / raw)
  To: Tariq Toukan
  Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	netdev, Paolo Abeni, Akiva Goldberger, Gal Pressman, Kees Cook,
	linux-kernel, linux-rdma, Mark Bloch, Moshe Shemesh,
	Saeed Mahameed, Shay Drori
In-Reply-To: <20260717072543.1241094-1-tariqt@nvidia.com>

On Fri, Jul 17, 2026 at 10:25:43AM +0300, Tariq Toukan wrote:
> From: Akiva Goldberger <agoldberger@nvidia.com>
> 
> mlx5_query_mtrc_caps() reads num_string_db from the MTRC capabilities
> register and uses it directly as a loop bound to populate the fixed-size
> base_address_out[STRINGS_DB_SECTIONS_NUM] and
> size_out[STRINGS_DB_SECTIONS_NUM] arrays in the tracer's str_db
> structure (STRINGS_DB_SECTIONS_NUM == 8).
> 
> The field is 4 bits wide, so firmware can report up to 15. A value
> greater than STRINGS_DB_SECTIONS_NUM makes the loop write past the end
> of those arrays, corrupting adjacent fields of the fw_tracer structure
> on the kernel heap. Clamp the firmware-reported value before it is used.
> 
> Fixes: f53aaa31cce7 ("net/mlx5: FW tracer, implement tracer logic")
> Signed-off-by: Akiva Goldberger <agoldberger@nvidia.com>
> Reviewed-by: Shay Drori <shayd@nvidia.com>
> Signed-off-by: Tariq Toukan <tariqt@nvidia.com>
> ---
>  drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c | 7 +++++++
>  1 file changed, 7 insertions(+)
> 
> diff --git a/drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c b/drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c
> index adcc73e2a5b3..404736c46adf 100644
> --- a/drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c
> +++ b/drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c
> @@ -64,6 +64,13 @@ static int mlx5_query_mtrc_caps(struct mlx5_fw_tracer *tracer)
>  	tracer->str_db.num_string_trace =
>  			MLX5_GET(mtrc_cap, out, num_string_trace);
>  	tracer->str_db.num_string_db = MLX5_GET(mtrc_cap, out, num_string_db);
> +	if (tracer->str_db.num_string_db > STRINGS_DB_SECTIONS_NUM) {
> +		mlx5_core_warn(dev,
> +			       "FWTracer: Firmware reports num_string_db (%u) > (%u), clamping\n",
> +			       tracer->str_db.num_string_db,
> +			       STRINGS_DB_SECTIONS_NUM);
> +		tracer->str_db.num_string_db = STRINGS_DB_SECTIONS_NUM;
> +	}

First, these lines are:

"tracer->str_db.num_string_db = min(tracer->str_db.num_string_db,
				    STRINGS_DB_SECTIONS_NUM);"

Second, this is a very naive approach to "securing" the system.
Everything originates from the firmware: registers, DMA, and data.
You cannot single out one field and claim the system is now "secure".

I am aware of another large vendor that added similar "clamping"
throughout their driver. That does not make the implementation correct
or particularly useful.

Thanks

>  	tracer->owner = !!MLX5_GET(mtrc_cap, out, trace_owner);
>  	tracer->str_db.loaded = false;
>  
> 
> base-commit: 3f1f755366687d051174739fb99f7d560202f60b
> -- 
> 2.44.0
> 
> 

^ permalink raw reply

* [PATCH] tls: don't abort the connection on signal-interrupted sends
From: Maximilian Immanuel Brandtner @ 2026-07-20  9:08 UTC (permalink / raw)
  To: john.fastabend, kuba, sd, davem, edumazet, pabeni, horms,
	bcodding, netdev, linux-kernel, svens, brueckner

When a signal interrupts a blocking send, tls_tx_records() treats the
resulting -ERESTARTSYS as a transmission failure and marks the socket
errored via tls_err_abort() with the raw error code. Later syscalls
return the kernel-internal errno 512 (ERESTARTSYS) to userspace, as the
signal it stems from is no longer pending during syscall exit and thus
never translated.

An interrupted send is not a connection error: the partially sent record
stays queued and is resent later. Interrupt error codes are therefore
excluded from the abort in the same way as -EAGAIN.

Fixes: b341ca51d267 ("tls: Fix tls_sw_sendmsg error handling")
Signed-off-by: Maximilian Immanuel Brandtner <maxbr@linux.ibm.com>
---
Tested on top of net commit 3f1f75536668 ("net: openvswitch: reject
oversized nested action attrs").

Several subsystems contain static helper functions to classify these
interrupt errnos. It might be worthwhile to refactor these static
functions into a generic helper function.
---
 net/tls/tls_sw.c | 20 +++++++++++++++++++-
 1 file changed, 19 insertions(+), 1 deletion(-)

diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c
index d4afc90fd796..3c9e94069e82 100644
--- a/net/tls/tls_sw.c
+++ b/net/tls/tls_sw.c
@@ -405,6 +405,24 @@ static void tls_free_open_rec(struct sock *sk)
 	}
 }
 
+static bool tls_is_non_restartable_err(int err)
+{
+	if (err >= 0)
+		return false;
+
+	switch (err) {
+	case -EAGAIN:
+	case -EINTR:
+	case -ERESTARTSYS:
+	case -ERESTARTNOINTR:
+	case -ERESTARTNOHAND:
+	case -ERESTART_RESTARTBLOCK:
+		return false;
+	default:
+		return true;
+	}
+}
+
 int tls_tx_records(struct sock *sk, int flags)
 {
 	struct tls_context *tls_ctx = tls_get_ctx(sk);
@@ -458,7 +476,7 @@ int tls_tx_records(struct sock *sk, int flags)
 	}
 
 tx_err:
-	if (rc < 0 && rc != -EAGAIN)
+	if (tls_is_non_restartable_err(rc))
 		tls_err_abort(sk, rc);
 
 	return rc;
-- 
2.55.0


^ permalink raw reply related

* Re: [Patch net-next v3] net: phy: Add driver for Motorcomm Quad 2.5GbE phy
From: Kyle Switch @ 2026-07-20  9:05 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: Frank.Sae, hkallweit1, linux, davem, edumazet, kuba, pabeni,
	netdev, linux-kernel, ming.xu, xiaolin.xu, jianmin.wang, jie.han
In-Reply-To: <06a5b8d1-f3a5-40e9-af12-52c5c9fd99e6@lunn.ch>



On 7/17/26 23:11, Andrew Lunn wrote:
>> +/**
>> + * ytphy_read_top_ext() - read a PHY's top extended register for YT8824
>> + * @phydev: a pointer to a &struct phy_device
>> + * @regnum: register number to read
>> + *
>> + * NOTE:The caller must have taken the MDIO bus lock.
> 
> Didn't i request this is done in code, not comments?

Ans: fix done in patch v4 by called lockdep_assert_held to make
sure to hold the lock.

> 
>> +static int ytphy_read_top_ext(struct phy_device *phydev, u16 regnum)
>> +{
>> +	struct yt8824_shared_priv *shared_priv;
>> +	unsigned int offset;
>> +	int ret;
>> +
>> +	shared_priv = phy_package_get_priv(phydev);
>> +	offset = shared_priv->offset;
>> +	ret = __phy_package_write(phydev, offset, YTPHY_PAGE_SELECT, regnum);
> 
> You look to be using the package wrongly.
> 
> https://elixir.bootlin.com/linux/v7.1.3/source/drivers/net/phy/phy_package.c#L192
> 
>  * 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 as base and added to the
>  * passed offset in those functions.
> 
> Although it is called base, it can be above all the others. So when
> you create the package, set the cookie value to your top address. You
> can then use __phy_package_write() directly, offset of 0.

Ans: fix done in patch v4, the base_addr is used to access to the top extend register
for phy8824.

> 
>> -static struct phy_driver motorcomm_phy_drvs[] = {
>> -	{
>> -		PHY_ID_MATCH_EXACT(PHY_ID_YT8511),
>> -		.name		= "YT8511 Gigabit Ethernet",
>> -		.config_init	= yt8511_config_init,
>> -		.suspend	= genphy_suspend,
>> -		.resume		= genphy_resume,
>> -		.read_page	= yt8511_read_page,
>> -		.write_page	= yt8511_write_page,
>> -	},
> 
> I still don't like this. Please stop diff deleting all these and
> putting them back later. It makes me thing something changed, maybe
> white space?

Ans: I tried to refactor this issue by hanging the position of the newly
added code. However, the same issue may still arise in other code segments, 
without modifying the existing code.
> 
>> +static int yt8824_utp_softreset_paged(struct phy_device *phydev,
>> +				      int reg_space)
>> +{
>> +	int old_page;
>> +	int ret = 0;
>> +	int val;
>> +
>> +	old_page = phy_select_page(phydev, reg_space);
>> +	if (old_page < 0)
>> +		goto err_restore_page;
>> +	if (reg_space == YT8824_RSSR_UTP_SPACE) {
>> +		ret = __phy_read(phydev, MII_BMCR);
>> +		if (ret < 0)
>> +			goto err_restore_page;
>> +		ret |= BMCR_RESET;
>> +		ret = __phy_write(phydev, MII_BMCR, ret);
> 
> So here you are using __phy_write() so it does not take the mdio lock.
> 
>> +		if (ret < 0)
>> +			goto err_restore_page;
>> +		/* wait until softreset done. */
>> +		ret = phy_read_poll_timeout(phydev, MII_BMCR, val, !(val & BMCR_RESET),
>> +					    50000, 600000, true);
> 
> This uses phy_read(), so does take the lock.
> 
> How can this be correct?
> 
> Please take some time to think about locking. Please enable all the
> locking debug options in the kernel and see if you get splats. And
> also really do turn your locking comments into code.
> 

Ans: this issue fixed done in patch v4. The correct procedure might be to acquire the mdio lock 
within this phy_select_page() and release it within phy_restore_page(). During this period, 
all interfaces that involve acquiring the mdio lock are unacceptable and incorrect.

>     Andrew

^ permalink raw reply

* Re: [PATCH net-next 1/5] bridge: Use direct pointer in br_is_nd_neigh_msg()
From: Nikolay Aleksandrov @ 2026-07-20  8:58 UTC (permalink / raw)
  To: Danielle Ratson, netdev
  Cc: dsahern, idosch, davem, edumazet, kuba, pabeni, horms, ja, petrm,
	fw, kuniyu, bridge, linux-kernel
In-Reply-To: <c710e4a236e69868a59c969bab31e5f29cfc0e86.1784463131.git.danieller@nvidia.com>

On 19/07/2026 16:34, Danielle Ratson wrote:
> Both callers of br_is_nd_neigh_msg() already call pskb_may_pull() to
> ensure sizeof(struct ipv6hdr) + sizeof(struct nd_msg) bytes are in the
> linear area before invoking this function. The skb_header_pointer()
> call and its fallback buffer are therefore unnecessary.
> 
> Replace skb_header_pointer() with a direct cast to ipv6_hdr(skb) + 1
> and drop the now-unused 'msg' parameter and its corresponding stack
> buffer from all callers.
> 
> Reviewed-by: Petr Machata <petrm@nvidia.com>
> Signed-off-by: Danielle Ratson <danieller@nvidia.com>
> ---
>   net/bridge/br_arp_nd_proxy.c | 9 ++-------
>   net/bridge/br_device.c       | 4 ++--
>   net/bridge/br_input.c        | 4 ++--
>   net/bridge/br_private.h      | 2 +-
>   4 files changed, 7 insertions(+), 12 deletions(-)
> 

Acked-by: Nikolay Aleksandrov <razor@blackwall.org>



^ permalink raw reply

* Re: [Patch net-next v3] net: phy: Add driver for Motorcomm Quad 2.5GbE phy
From: Kyle Switch @ 2026-07-20  8:45 UTC (permalink / raw)
  To: Krzysztof Kozlowski, Frank.Sae, andrew, hkallweit1, linux, davem,
	edumazet, kuba, pabeni
  Cc: netdev, linux-kernel, ming.xu, xiaolin.xu, jianmin.wang, jie.han
In-Reply-To: <6264d655-640e-47ec-9521-5e292bfeaba6@kernel.org>



On 7/18/26 04:19, Krzysztof Kozlowski wrote:
> On 17/07/2026 07:48, Kyle Switch wrote:
>> Add a driver for motorcomm yt8824 quad 2.5G ethernet phy, supports
>> 2.5G/1000M/100M/10M speed.
>>
>> Signed-off-by: Kyle Switch <kyle.switch@motor-comm.com>
> 
> Please run scripts/checkpatch.pl on the patches and fix reported
> warnings. After that, run also 'scripts/checkpatch.pl --strict' on the
> patches and (probably) fix more warnings. Some warnings can be ignored,
> especially from --strict run, but the code here looks like it needs a
> fix. Feel free to get in touch if the warning is not clear.
> 
> Please use scripts/get_maintainers.pl to get a list of necessary people
> and lists to CC (and consider --no-git-fallback argument, so you will
> not CC people just because they made one commit years ago). It might
> happen, that command when run on an older kernel, gives you outdated
> entries. Therefore please be sure you base your patches on recent Linux
> kernel.
> 
> Tools like b4 or scripts/get_maintainer.pl provide you proper list of
> people, so fix your workflow. Tools might also fail if you work on some
> ancient tree (don't, instead use mainline) or work on fork of kernel
> (don't, instead use mainline). Just use b4 and everything should be
> fine, although remember about `b4 prep --auto-to-cc` if you added new
> patches to the patchset.
> 

Ans: Thank you for your support. The warning and error code formats 
will be fixed in the following patches according to this procedure.

>> ---
>>
>> changes in v3:
>> 1. Using common apis defined in phy_package.c to handle shared top
>> extend register space.
>> 2. Add dts demo in motorcomm,yt8xxx.yaml.
>> 3. Fix unnecessary redundant judgments.
>> 4. Fix BMCR registers operation using magic number.
>> 5. Rename funtion based on its approximate functionality.
>>
>> changes in v2:
>> 1. Remove duplicate code and replace it with existing api. 
>>
>>  .../bindings/net/motorcomm,yt8xxx.yaml        |   30 +
>>  drivers/net/phy/motorcomm.c                   | 1808 ++++++++++++++++-
>>  2 files changed, 1750 insertions(+), 88 deletions(-)
>>
>> diff --git a/Documentation/devicetree/bindings/net/motorcomm,yt8xxx.yaml b/Documentation/devicetree/bindings/net/motorcomm,yt8xxx.yaml
>> index 26688e2302ea..e7592468f658 100644
>> --- a/Documentation/devicetree/bindings/net/motorcomm,yt8xxx.yaml
>> +++ b/Documentation/devicetree/bindings/net/motorcomm,yt8xxx.yaml
>> @@ -149,3 +149,33 @@ examples:
>>              motorcomm,auto-sleep-disabled;
>>          };
>>      };
>> +  - |
>> +    mdio {
>> +        #address-cells = <1>;
>> +        #size-cells = <0>;
> 
> I don't understand why you are doing this. Anyway, please read the
> documents I linked and maybe there is some explanation for that change.

Ans: the bind changes will be removed in patch v4, and updated in another
patch thread.

> 
> Best regards,
> Krzysztof

^ permalink raw reply

* Re: [Patch net-next v3] net: phy: Add driver for Motorcomm Quad 2.5GbE phy
From: Kyle Switch @ 2026-07-20  8:47 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: Frank.Sae, hkallweit1, linux, davem, edumazet, kuba, pabeni,
	netdev, linux-kernel, ming.xu, xiaolin.xu, jianmin.wang, jie.han
In-Reply-To: <3a576b50-f2be-476f-b702-964a8158cf1a@lunn.ch>



On 7/17/26 22:53, Andrew Lunn wrote:
> On Fri, Jul 17, 2026 at 01:48:07PM +0800, Kyle Switch wrote:
>> Add a driver for motorcomm yt8824 quad 2.5G ethernet phy, supports
>> 2.5G/1000M/100M/10M speed.
>>
>> Signed-off-by: Kyle Switch <kyle.switch@motor-comm.com>
>> ---
>>
>> changes in v3:
>> 1. Using common apis defined in phy_package.c to handle shared top
>> extend register space.
>> 2. Add dts demo in motorcomm,yt8xxx.yaml.
>> 3. Fix unnecessary redundant judgments.
>> 4. Fix BMCR registers operation using magic number.
>> 5. Rename funtion based on its approximate functionality.
>>
>> changes in v2:
>> 1. Remove duplicate code and replace it with existing api. 
>>
>>  .../bindings/net/motorcomm,yt8xxx.yaml        |   30 +
> 
> Please put binding changes in a patch of its own, in the patch
> series. You also need to Cc: the DT people.
> 
>     Andrew
> 

Ans: as mentioned in other emails. patch v4 will remove binding changes,
and updated in another patch thread.

> ---
> pw-bot: cr

^ permalink raw reply

* Re: [PATCH net] net: pcs: xpcs: fix SGMII state reading
From: Maxime Chevallier @ 2026-07-20  8:47 UTC (permalink / raw)
  To: Coia Prant, Andrew Lunn, Heiner Kallweit, Russell King,
	David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni
  Cc: Jiawen Wu, netdev, linux-kernel, stable
In-Reply-To: <20260717074324.3250043-2-coiaprant@gmail.com>

Hi,

On 7/17/26 09:43, Coia Prant wrote:
> Commit 2a22b7ae2fa3 ("net: pcs: xpcs: adapt Wangxun NICs for SGMII mode")
> added a path in xpcs_get_state_c37_sgmii() that reads speed/duplex from
> BMCR after AN completes. However, BMCR does not reflect the negotiated
> result on the hardware where this has been tested:
> 
> - On RK3568 (MAC side SGMII), BMCR returns a fixed hardware reset value
> - Wangxun engineer Jiawen Wu confirmed that on their side, "BMCR looks
>   like it only wants to be return as 0" [0]
> 
> The correct information is available in CL37_ANSGM_STS, which contains
> the actual link status and negotiated speed/duplex.
> 
> This bug was previously masked by phylink core, which overrides the PCS
> link state with the PHY state when a PHY is present:
> 
>         /* If we have a phy, the "up" state is the union of both the
>          * PHY and the MAC
>          */
>         if (phy)
>                 link_state.link &= pl->phy_state.link;
> 
> Thus, when the link is down, the PHY's link_down state is applied on top
> of whatever the PCS reports, hiding the broken PCS state reading path.
> 
> Modify xpcs_get_state_c37_sgmii() to:
> 1. Read link state from CL37_ANSGM_STS
> 2. If link is up, report speed/duplex from CL37_ANSGM_STS
> 3. Remove the broken BMCR reading path entirely
> 
> Also properly set state->an_complete to reflect the AN completion status,
> and clear CL37_ANCMPLT_INTR when link is down to avoid stale state.
> 
> [0] https://lore.kernel.org/all/000c01dd1593$2ac0b0f0$804212d0$@trustnetic.com/
> 
> Fixes: 2a22b7ae2fa3 ("net: pcs: xpcs: adapt Wangxun NICs for SGMII mode")
> Cc: stable@vger.kernel.org
> Tested-by: Jiawen Wu <jiawenwu@trustnetic.com>
> Signed-off-by: Coia Prant <coiaprant@gmail.com>

Give that a test on ksz9477 that has an older XPCS for SGMII, this makes some
sense to me and no regressions were found.

Tested-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
Reviewed-by: Maxime Chevallier <maxime.chevallier@bootlin.com>

Maxime


^ permalink raw reply

* Re: [PATCH v4 4/5] vhost: synchronize with RCU readers when freeing workers
From: Stefano Garzarella @ 2026-07-20  8:39 UTC (permalink / raw)
  To: Andrey Drobyshev
  Cc: linux-kernel, kvm, virtualization, netdev, mst, stefanha,
	dongli.zhang, maciej.szmigiero, bchaney, mark.kanda, ptikhomirov,
	den
In-Reply-To: <55d7c896-b871-4c50-a324-35f5c4a9d11a@virtuozzo.com>

On Thu, Jul 16, 2026 at 09:01:22PM +0300, Andrey Drobyshev wrote:
>On 7/16/26 7:13 PM, Stefano Garzarella wrote:
>> On Thu, Jul 16, 2026 at 06:39:48PM +0300, Andrey Drobyshev wrote:
>>> On 7/16/26 11:57 AM, Stefano Garzarella wrote:
>>>> On Tue, Jul 14, 2026 at 06:16:37PM +0300, Andrey Drobyshev wrote:
>>>>> vhost_vq_work_queue() only holds the RCU read lock while it dereferences
>>>>> vq->worker and queues work on it.  vhost_workers_free() however clears
>>>>> the vq->worker pointers and immediately frees the workers, without
>>>>> waiting for a grace period.  A caller that fetched the worker right
>>>>> before the pointer was cleared can therefore still be queueing work on
>>>>> it while it is freed.  And even when the queueing itself wins the race,
>>>>> the work is never run, so its VHOST_WORK_QUEUED bit stays set and all
>>>>> future attempts to queue it are silently skipped.
>>>>>
>>>>> None of the current callers can actually hit this: net and scsi stop
>>>>> their virtqueues before the workers are freed, and vsock unhashes the
>>>>> device and does synchronize_rcu() of its own in vhost_vsock_dev_release()
>>>>> before the workers go away.  But the upcoming VHOST_RESET_OWNER support
>>>>> in vhost-vsock keeps the device hashed while its workers are freed, so
>>>>> the lockless send/cancel paths become able to race with the teardown.
>>>>>
>>>>> Close this the way vhost_worker_killed() already does: clear the
>>>>> vq->worker pointers, wait for a grace period, run whatever the last
>>>>> readers may have queued, and only then free the workers.  The
>>>>> synchronize_rcu() is skipped if the device has no workers, so cleanup of
>>>>> devices which never got an owner stays cheap.
>>>>>
>>>>
>>>> Do we need a Fixes tag for this?
>>>>
>>>
>>> I'm guessing it should be:
>>>
>>> Fixes: 228a27cf78af ("vhost: Allow worker switching while work is queueing")
>>>
>>>> Thanks for pointing out that the issue wasn't occurring, but I think we
>>>> should add it because it's a sneaky problem we discovered by chance.
>>>> IMO the code should already have `synchronize_rcu()` after
>>>> `rcu_assign_pointer()` loop.
>>>>
>>>> @Michael, what do you think?
>>>>
>>>>> Suggested-by: Stefano Garzarella <sgarzare@redhat.com>
>>>>> Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
>>>>> ---
>>>>> drivers/vhost/vhost.c | 15 +++++++++++++++
>>>>> 1 file changed, 15 insertions(+)
>>>>>
>>>>> diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c
>>>>> index 4c525b3e16ea..0d1414d40f4e 100644
>>>>> --- a/drivers/vhost/vhost.c
>>>>> +++ b/drivers/vhost/vhost.c
>>>>> @@ -729,6 +729,21 @@ static void vhost_workers_free(struct vhost_dev *dev)
>>>>>
>>>>> 	for (i = 0; i < dev->nvqs; i++)
>>>>> 		rcu_assign_pointer(dev->vqs[i]->worker, NULL);
>>>>> +
>>>>> +	/*
>>>>> +	 * vhost_vq_work_queue() reads vq->worker under rcu_read_lock(), so a
>>>>> +	 * caller that fetched a worker before we cleared the pointers above
>>>>> +	 * may still be about to queue work on it.  Wait for those RCU readers
>>>>> +	 * to finish before freeing the worker, then run whatever they queued
>>>>> +	 * so nothing is left with VHOST_WORK_QUEUED set.  Mirrors
>>>>> +	 * vhost_worker_killed().
>>>>> +	 */
>>>>> +	if (!xa_empty(&dev->worker_xa)) {
>>>>> +		synchronize_rcu();
>>>>> +		xa_for_each(&dev->worker_xa, i, worker)
>>>>> +			vhost_run_work_list(worker);
>>>>> +	}
>>>>> +
>>>>
>>>> Following sashiko review [1], I tried to undersand why we need this, but
>>>> TBH I'm really confused. That said, this seems wrong also because it
>>>> will work only with vhost_tasks, and not with kthreads.
>>>>
>>>> IIUC vhost_worker_killed() will be called anyway when calling
>>>> vhost_worker_destroy(). For vhost_tasks, it will call
>>>> vhost_task_do_stop() that calls vhost_task_stop(). This sets
>>>> VHOST_TASK_FLAGS_STOP and wait the worker on vtsk->exited before freeing
>>>> stuff. The worker breaks the loop and calls vtsk->handle_sigkill() that
>>>> is exactly vhost_worker_killed() you mentioned we are mirroring here.
>>>>
>>>
>>> Hmm, are we sure it's the case for our codepath?  Looking at the
>>> vhost_task loop function:
>>>
>>>> static int vhost_task_fn(void *data)
>>>> {
>>>>     for (;;) {
>>>>         if (signal_pending(current)) {
>>>>             if (get_signal(&ksig))
>>>>                 break;
>>>>         }
>>>>         ...
>>>>         if (test_bit(VHOST_TASK_FLAGS_STOP, &vtsk->flags)) {
>>>>             __set_current_state(TASK_RUNNING);
>>>>             break;
>>>>         }
>>>>         did_work = vtsk->fn(vtsk->data);
>>>>         ...
>>>>     }
>>>>
>>>>     ...
>>>>
>>>>     if (!test_bit(VHOST_TASK_FLAGS_STOP, &vtsk->flags)) {
>>>>         set_bit(VHOST_TASK_FLAGS_KILLED, &vtsk->flags);
>>>>         vtsk->handle_sigkill(vtsk->data);
>>>>     }
>>>>     ...
>>>> }
>>>
>>> AFAICT, we exit the loop in 2 cases: signal delivery or STOP bit
>>> setting.  Like you said, STOP is set by vhost_task_stop.  E.g. for our
>>> RESET_OWNER case:
>>>
>>> vhost_vsock_reset_owner()
>>>  vhost_dev_reset_owner()
>>>    vhost_dev_cleanup()
>>>      vhost_workers_free()
>>>        vhost_worker_destroy()
>>>          vhost_task_stop()  // for vhost_task_ops backend
>>>            set_bit(VHOST_TASK_FLAGS_STOP)
>>>
>>> So, first of all, actual work by .fn() callback is done after the exit
>>> checks, therefore we skip it - no chance to drain there.
>>>
>>> Secondly, the handle_sigkill() callback is deliberately NOT called in
>>> the STOP case and only called on fatal signal delivery.  And for
>>> vhost_task backend the .handle_sigkill() callback is exactly
>>> vhost_worker_killed().
>>>
>>> So my understanding is: if we only call synchronize_rcu() here and leave
>>> this path undrained, then whatever work which was put by send_pkt() for
>>> the worker currently being freed - will be lost.  Please correct me if
>>> I'm wrong.
>>
>> Yep, your right. But what will be the issue of loosing them?
>>
>> IIUC we are not loosing any data, just avoiding some works that will be
>> handled later when/if will set a new owner.
>>
>
>But will it actually be handled?
>
>vhost_transport_send_pkt()  // called on every packet send
>  virtio_vsock_skb_queue_tail(&send_pkt_queue, skb) // add skb to list
>  vhost_vq_work_queue(&send_pkt_work) // try to arm the work
>    vhost_worker_queue()
>      if (!test_and_set_bit(VHOST_WORK_QUEUED, &work->flags)) {
>        llist_add(&worker->work_list)
>      }
>
>So send_pkt_queue is a list of skbs, it lives on the vhost_vsock device
>state, and survives RESET_OWNER.  In that sense you're probably right
>that we aren't loosing any data.
>
>There's also send_pkt_work object, also living on the vhost_vsock device
>state.  So we're accumulating skbs, and then send_pkg_work gets put in
>the worker task list - but only if it's NOT already armed in there, i.e.
>QUEUED bit is unset.  And the bit gets cleared by the workload callback
>- for vhost_task backend it's vhost_run_work_list().
>
>The most important thing is WHERE this piece of work is being put.  That
>is worker->work_list - this list does not survive RESET_OWNER, as we
>free the worker in vhost_workers_free().
>
>Now imagine we have RESET_OWNER racing with send_pkt.  In
>vhost_workers_free() we acquire ptr to a worker but not NULL'ify it yet.
> Then on the send_pkt path we arm the send_pkt_work, set the QUEUED bit,
>and place it on the work_list of a DYING worker.  Then the worker gets
>freed.  Now we have send_pkt_work (a singleton struct) with QUEUED set
>in its flags, and with no worker to walk through this piece of work and
>clear this flag.  As a result - send_pkt_work can't be placed in the
>list of any other worker, because it doesn't pass the "if
>(!test_and_set_bit(QUEUED)" check.  Thus no new packets can be
>processed, and the connection is stalled.
>
>Does this make sense?

Honestly, I don't see the problem. The device is about to be stopped 
because the VMM is resetting the owner, so the connection is going to 
stall anyway, since I don't think the guest will never answer, isn't it?

>>>
>>> That said, I agree that vhost_run_work_list() will only work with
>>> vhost_task backend, not with kthreads backend.  If we do
>>> vhost_worker_flush() instead - I guess it'll keep the drain here, yet
>>> become backend-agnostic. I.e.:
>>>
>>>> +       if (!xa_empty(&dev->worker_xa)) {
>>>> +               synchronize_rcu();
>>>> +               xa_for_each(&dev->worker_xa, i, worker)
>>>> +                       vhost_worker_flush(worker);
>>>> +       }
>>>
>>> With the last 2 lines being equivalent to just calling
>>> vhost_dev_flush(dev).  And once we become backend-agnostic here, I'm
>>> guessing the warning reported by Sashiko should be dealt with as well.
>>
>> I'd avoid `if !xa_empty(&dev->worker_xa)` at all, and call
>> synchronize_rcu() in any case.
>>
>
>Agreed.
>
>> About vhost_dev_flush(), we are calling it in several places, and maybe
>> we should re-check them. E.g. we call in vhost_vsock_flush(), but it's
>> also called by vhost_dev_stop(), maybe we can avoid to call
>> vhost_vsock_flush() if we call vhost_dev_stop().
>>
>> I'm not sure we really need another one here, but if you think some
>> other works can be queued between the vhost_dev_stop() and the
>> synchronize_rcu() we are adding here, then okay, it may have sense.
>>
>
>Note that in our particular case we're gonna do:
>
>vhost_workers_free()
>  vhost_dev_flush()  // the flush we're planning to add
>  xa_for_each(&dev->worker_xa, i, worker)
>    vhost_worker_destroy(dev, worker)
>  xa_destroy(&dev->worker_xa)
>
>So we walk through the XArray, destroy workers in it one by one, then
>destroy the XArray itself.  Then the next time we call
>vhost_dev_flush(), e.g. from vhost_dev_stop() or wherever else, it tries
>iterating over the XArray which no longer exists - which is gonna be a
>no-op.
>
>Now, we can reach vhost_workers_free() via (at least) 2 paths:
>RESET_OWNER and device release path.  On the former the flush is needed
>as I illustrated above.  On the latter it's indeed redundant but is
>cheap as it's a no-op.

Again, I don't see the need for it TBH, but at the same time, I don't 
think it's a problem to include it, so if you think it's necessary, go 
ahead and add it.

Thanks,
Stefano


^ permalink raw reply

* Re: [PATCH net v2] net: erspan: set lltx to avoid sch_direct_xmit deadlock
From: Eric Dumazet @ 2026-07-20  8:26 UTC (permalink / raw)
  To: Ido Schimmel
  Cc: Yun Zhou, dsahern, davem, kuba, pabeni, horms, netdev,
	linux-kernel
In-Reply-To: <20260720075352.GA2233846@shredder>

On Mon, Jul 20, 2026 at 9:54 AM Ido Schimmel <idosch@nvidia.com> wrote:
>
> On Mon, Jul 13, 2026 at 11:14:35PM +0800, Yun Zhou wrote:
> > erspan_xmit() re-enters the network stack via ip_tunnel_xmit(), causing
> > nested acquisition of _xmit_lock on the underlay device while already
> > holding the ERSPAN device's _xmit_lock. Both are ARPHRD_ETHER and share
> > the same lockdep class, creating an ABBA deadlock:
> >
> >   sch_direct_xmit [lock erspan] -> erspan_xmit -> ip_tunnel_xmit ->
> >   ip_output -> __dev_queue_xmit -> sch_direct_xmit [lock underlay]
> >
> > Set dev->lltx = true so HARD_TX_LOCK() skips the spinlock for ERSPAN.
> > This is safe as erspan_xmit() has no shared mutable state: o_seqno is
> > atomic, stats use atomic_long_inc, and dst_cache is per-CPU. GRETAP,
> > the sibling device with identical xmit structure, already sets lltx.
>
> erspan_xmit() (unlike gre_tap_xmit()) is performing non-atomic
> __clear_bit() on shared tunnel flags and KCSAN will probably flag it.
>
> Eric had a patch [1] that changes erspan_xmit() to use a private copy of
> these flags. I think it's better to wait for Eric's patch to be merged
> before setting lltx.
>
> Eric, can you please submit v2 of your patch to net?

Sure, I can work on it today. Thanks!

>
> Also, doesn't ip6erspan suffer from the same problem? Please try to
> reproduce and fix.
>
> [1] https://lore.kernel.org/netdev/20260615140333.3161072-1-edumazet@google.com/
>
> >
> > Closes: https://syzkaller.appspot.com/bug?extid=9bda1b9fbb7fbdf9b62b
> > Reported-by: syzbot+9bda1b9fbb7fbdf9b62b@syzkaller.appspotmail.com
> > Fixes: 84e54fe0a5ea ("gre: introduce native tunnel support for ERSPAN")
> > Signed-off-by: Yun Zhou <yun.zhou@windriver.com>
> > ---
> > v2:
> >   - change subject prefix to [PATCH net]
> >
> >  net/ipv4/ip_gre.c | 2 ++
> >  1 file changed, 2 insertions(+)
> >
> > diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c
> > index 3efdfb4ffa21..9fbff16cda1d 100644
> > --- a/net/ipv4/ip_gre.c
> > +++ b/net/ipv4/ip_gre.c
> > @@ -1363,6 +1363,8 @@ static int erspan_tunnel_init(struct net_device *dev)
> >       dev->features           |= GRE_FEATURES;
> >       dev->hw_features        |= GRE_FEATURES;
> >       dev->priv_flags         |= IFF_LIVE_ADDR_CHANGE;
> > +     /* Skip TX lock: xmit re-enters stack, risking ABBA with underlay */
> > +     dev->lltx = true;
> >       netif_keep_dst(dev);
> >
> >       return ip_tunnel_init(dev);
> > --
> > 2.43.0
> >

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox