Netdev List
 help / color / mirror / Atom feed
* RE: [Intel-wired-lan] [PATCH net v1] ixgbe: ensure IPsec VF<->PF compatibility
From: Jankowski, Konrad0 @ 2022-04-25 11:34 UTC (permalink / raw)
  To: Leon Romanovsky, David S . Miller, Jakub Kicinski
  Cc: Steffen Klassert, Shannon Nelson, netdev@vger.kernel.org,
	linux-kernel@vger.kernel.org, intel-wired-lan@lists.osuosl.org,
	Jeff Kirsher, Raed Salem, Shannon Nelson, Paolo Abeni,
	Leon Romanovsky
In-Reply-To: <737616899df2a482e4ec35aa4056c9ac608d2f50.1648714609.git.leonro@nvidia.com>



> -----Original Message-----
> From: Intel-wired-lan <intel-wired-lan-bounces@osuosl.org> On Behalf Of
> Leon Romanovsky
> Sent: Thursday, March 31, 2022 10:20 AM
> To: David S . Miller <davem@davemloft.net>; Jakub Kicinski
> <kuba@kernel.org>
> Cc: Steffen Klassert <steffen.klassert@secunet.com>; Shannon Nelson
> <snelson@pensando.io>; netdev@vger.kernel.org; linux-
> kernel@vger.kernel.org; intel-wired-lan@lists.osuosl.org; Jeff Kirsher
> <jeffrey.t.kirsher@intel.com>; Raed Salem <raeds@nvidia.com>; Shannon
> Nelson <shannon.nelson@oracle.com>; Paolo Abeni <pabeni@redhat.com>;
> Leon Romanovsky <leonro@nvidia.com>
> Subject: [Intel-wired-lan] [PATCH net v1] ixgbe: ensure IPsec VF<->PF
> compatibility
> 
> From: Leon Romanovsky <leonro@nvidia.com>
> 
> The VF driver can forward any IPsec flags and such makes the function is not
> extendable and prone to backward/forward incompatibility.
> 
> If new software runs on VF, it won't know that PF configured something
> completely different as it "knows" only XFRM_OFFLOAD_INBOUND flag.
> 
> Fixes: eda0333ac293 ("ixgbe: add VF IPsec management")
> Reviewed-by: Raed Salem <raeds@nvidia.com>
> Signed-off-by: Leon Romanovsky <leonro@nvidia.com>a
> ---
> Chaagelog:
> v1:
>  * Replaced bits arithmetic with more simple expression
> v0:
> https://lore.kernel.org/all/3702fad8a016170947da5f3c521a9251cf0f4a22.16
> 48637865.git.leonro@nvidia.com
> ---
>  drivers/net/ethernet/intel/ixgbe/ixgbe_ipsec.c | 3 ++-
>  1 file changed, 2 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ipsec.c
> b/drivers/net/ethernet/intel/ixgbe/ixgbe_ipsec.c
> index e596e1a9fc75..69d11ff7677d 100644
> --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ipsec.c
> +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ipsec.c
> @@ -903,7 +903,8 @@ int ixgbe_ipsec_vf_add_sa(struct ixgbe_adapter
> *adapter, u32 *msgbuf, u32 vf)

Tested-by: Konrad Jankowski <konrad0.jankowski@intel.com>

^ permalink raw reply

* Re: [PATCH net] tcp: make sure treq->af_specific is initialized
From: patchwork-bot+netdevbpf @ 2022-04-25 11:20 UTC (permalink / raw)
  To: Eric Dumazet; +Cc: davem, kuba, pabeni, netdev, edumazet, fruggeri
In-Reply-To: <20220424203509.2801158-1-eric.dumazet@gmail.com>

Hello:

This patch was applied to netdev/net.git (master)
by David S. Miller <davem@davemloft.net>:

On Sun, 24 Apr 2022 13:35:09 -0700 you wrote:
> From: Eric Dumazet <edumazet@google.com>
> 
> syzbot complained about a recent change in TCP stack,
> hitting a NULL pointer [1]
> 
> tcp request sockets have an af_specific pointer, which
> was used before the blamed change only for SYNACK generation
> in non SYNCOOKIE mode.
> 
> [...]

Here is the summary with links:
  - [net] tcp: make sure treq->af_specific is initialized
    https://git.kernel.org/netdev/net/c/ba5a4fdd63ae

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply

* Re: [net-next: PATCH] net: dsa: remove unused headers
From: patchwork-bot+netdevbpf @ 2022-04-25 11:20 UTC (permalink / raw)
  To: Marcin Wojtas
  Cc: linux-kernel, netdev, davem, kuba, andrew, vivien.didelot,
	f.fainelli, olteanv, upstream
In-Reply-To: <20220425101102.2811727-1-mw@semihalf.com>

Hello:

This patch was applied to netdev/net-next.git (master)
by David S. Miller <davem@davemloft.net>:

On Mon, 25 Apr 2022 12:11:02 +0200 you wrote:
> Reduce a number of included headers to a necessary minimum.
> 
> Signed-off-by: Marcin Wojtas <mw@semihalf.com>
> ---
>  net/dsa/dsa.c | 9 ---------
>  1 file changed, 9 deletions(-)

Here is the summary with links:
  - [net-next:] net: dsa: remove unused headers
    https://git.kernel.org/netdev/net-next/c/df1cc21152ff

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply

* Re: [RFC Patch net-next] net: dsa: ksz9477: move get_stats64 to ksz_common.c
From: Oleksij Rempel @ 2022-04-25 11:17 UTC (permalink / raw)
  To: Arun Ramadoss
  Cc: linux-kernel, netdev, Paolo Abeni, Jakub Kicinski,
	David S. Miller, Vladimir Oltean, Florian Fainelli,
	Vivien Didelot, Andrew Lunn, UNGLinuxDriver, Woojung Huh,
	Oleksij Rempel
In-Reply-To: <20220425105500.20899-1-arun.ramadoss@microchip.com>

On Mon, Apr 25, 2022 at 04:25:00PM +0530, Arun Ramadoss wrote:
> The mib counters for the ksz9477 is same for the ksz9477 switch and
> LAN937x switch. Hence moving it to ksz_common.c file in order to have it
> generic function. The DSA hook get_stats64 now can call ksz_get_stats64.
> 
> Signed-off-by: Arun Ramadoss <arun.ramadoss@microchip.com>

Looks ok for me.

Reviewed-by: Oleksij Rempel <o.rempel@pengutronix.de>

Thank you!

> ---
>  drivers/net/dsa/microchip/ksz9477.c    | 98 +-------------------------
>  drivers/net/dsa/microchip/ksz_common.c | 96 +++++++++++++++++++++++++
>  drivers/net/dsa/microchip/ksz_common.h |  3 +
>  3 files changed, 101 insertions(+), 96 deletions(-)
> 
> diff --git a/drivers/net/dsa/microchip/ksz9477.c b/drivers/net/dsa/microchip/ksz9477.c
> index 4f617fee9a4e..48c90e4cda30 100644
> --- a/drivers/net/dsa/microchip/ksz9477.c
> +++ b/drivers/net/dsa/microchip/ksz9477.c
> @@ -65,100 +65,6 @@ static const struct {
>  	{ 0x83, "tx_discards" },
>  };
>  
> -struct ksz9477_stats_raw {
> -	u64 rx_hi;
> -	u64 rx_undersize;
> -	u64 rx_fragments;
> -	u64 rx_oversize;
> -	u64 rx_jabbers;
> -	u64 rx_symbol_err;
> -	u64 rx_crc_err;
> -	u64 rx_align_err;
> -	u64 rx_mac_ctrl;
> -	u64 rx_pause;
> -	u64 rx_bcast;
> -	u64 rx_mcast;
> -	u64 rx_ucast;
> -	u64 rx_64_or_less;
> -	u64 rx_65_127;
> -	u64 rx_128_255;
> -	u64 rx_256_511;
> -	u64 rx_512_1023;
> -	u64 rx_1024_1522;
> -	u64 rx_1523_2000;
> -	u64 rx_2001;
> -	u64 tx_hi;
> -	u64 tx_late_col;
> -	u64 tx_pause;
> -	u64 tx_bcast;
> -	u64 tx_mcast;
> -	u64 tx_ucast;
> -	u64 tx_deferred;
> -	u64 tx_total_col;
> -	u64 tx_exc_col;
> -	u64 tx_single_col;
> -	u64 tx_mult_col;
> -	u64 rx_total;
> -	u64 tx_total;
> -	u64 rx_discards;
> -	u64 tx_discards;
> -};
> -
> -static void ksz9477_r_mib_stats64(struct ksz_device *dev, int port)
> -{
> -	struct rtnl_link_stats64 *stats;
> -	struct ksz9477_stats_raw *raw;
> -	struct ksz_port_mib *mib;
> -
> -	mib = &dev->ports[port].mib;
> -	stats = &mib->stats64;
> -	raw = (struct ksz9477_stats_raw *)mib->counters;
> -
> -	spin_lock(&mib->stats64_lock);
> -
> -	stats->rx_packets = raw->rx_bcast + raw->rx_mcast + raw->rx_ucast;
> -	stats->tx_packets = raw->tx_bcast + raw->tx_mcast + raw->tx_ucast;
> -
> -	/* HW counters are counting bytes + FCS which is not acceptable
> -	 * for rtnl_link_stats64 interface
> -	 */
> -	stats->rx_bytes = raw->rx_total - stats->rx_packets * ETH_FCS_LEN;
> -	stats->tx_bytes = raw->tx_total - stats->tx_packets * ETH_FCS_LEN;
> -
> -	stats->rx_length_errors = raw->rx_undersize + raw->rx_fragments +
> -		raw->rx_oversize;
> -
> -	stats->rx_crc_errors = raw->rx_crc_err;
> -	stats->rx_frame_errors = raw->rx_align_err;
> -	stats->rx_dropped = raw->rx_discards;
> -	stats->rx_errors = stats->rx_length_errors + stats->rx_crc_errors +
> -		stats->rx_frame_errors  + stats->rx_dropped;
> -
> -	stats->tx_window_errors = raw->tx_late_col;
> -	stats->tx_fifo_errors = raw->tx_discards;
> -	stats->tx_aborted_errors = raw->tx_exc_col;
> -	stats->tx_errors = stats->tx_window_errors + stats->tx_fifo_errors +
> -		stats->tx_aborted_errors;
> -
> -	stats->multicast = raw->rx_mcast;
> -	stats->collisions = raw->tx_total_col;
> -
> -	spin_unlock(&mib->stats64_lock);
> -}
> -
> -static void ksz9477_get_stats64(struct dsa_switch *ds, int port,
> -			       struct rtnl_link_stats64 *s)
> -{
> -	struct ksz_device *dev = ds->priv;
> -	struct ksz_port_mib *mib;
> -
> -	mib = &dev->ports[port].mib;
> -
> -	spin_lock(&mib->stats64_lock);
> -	memcpy(s, &mib->stats64, sizeof(*s));
> -	spin_unlock(&mib->stats64_lock);
> -}
> -
>  static void ksz_cfg(struct ksz_device *dev, u32 addr, u8 bits, bool set)
>  {
>  	regmap_update_bits(dev->regmap[0], addr, bits, set ? bits : 0);
> @@ -1462,7 +1368,7 @@ static const struct dsa_switch_ops ksz9477_switch_ops = {
>  	.port_mdb_del           = ksz9477_port_mdb_del,
>  	.port_mirror_add	= ksz9477_port_mirror_add,
>  	.port_mirror_del	= ksz9477_port_mirror_del,
> -	.get_stats64		= ksz9477_get_stats64,
> +	.get_stats64		= ksz_get_stats64,
>  	.port_change_mtu	= ksz9477_change_mtu,
>  	.port_max_mtu		= ksz9477_max_mtu,
>  };
> @@ -1653,7 +1559,7 @@ static const struct ksz_dev_ops ksz9477_dev_ops = {
>  	.port_setup = ksz9477_port_setup,
>  	.r_mib_cnt = ksz9477_r_mib_cnt,
>  	.r_mib_pkt = ksz9477_r_mib_pkt,
> -	.r_mib_stat64 = ksz9477_r_mib_stats64,
> +	.r_mib_stat64 = ksz_r_mib_stats64,
>  	.freeze_mib = ksz9477_freeze_mib,
>  	.port_init_cnt = ksz9477_port_init_cnt,
>  	.shutdown = ksz9477_reset_switch,
> diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c
> index 9b9f570ebb0b..10f127b09e58 100644
> --- a/drivers/net/dsa/microchip/ksz_common.c
> +++ b/drivers/net/dsa/microchip/ksz_common.c
> @@ -20,6 +20,102 @@
>  
>  #include "ksz_common.h"
>  
> +struct ksz_stats_raw {
> +	u64 rx_hi;
> +	u64 rx_undersize;
> +	u64 rx_fragments;
> +	u64 rx_oversize;
> +	u64 rx_jabbers;
> +	u64 rx_symbol_err;
> +	u64 rx_crc_err;
> +	u64 rx_align_err;
> +	u64 rx_mac_ctrl;
> +	u64 rx_pause;
> +	u64 rx_bcast;
> +	u64 rx_mcast;
> +	u64 rx_ucast;
> +	u64 rx_64_or_less;
> +	u64 rx_65_127;
> +	u64 rx_128_255;
> +	u64 rx_256_511;
> +	u64 rx_512_1023;
> +	u64 rx_1024_1522;
> +	u64 rx_1523_2000;
> +	u64 rx_2001;
> +	u64 tx_hi;
> +	u64 tx_late_col;
> +	u64 tx_pause;
> +	u64 tx_bcast;
> +	u64 tx_mcast;
> +	u64 tx_ucast;
> +	u64 tx_deferred;
> +	u64 tx_total_col;
> +	u64 tx_exc_col;
> +	u64 tx_single_col;
> +	u64 tx_mult_col;
> +	u64 rx_total;
> +	u64 tx_total;
> +	u64 rx_discards;
> +	u64 tx_discards;
> +};
> +
> +void ksz_r_mib_stats64(struct ksz_device *dev, int port)
> +{
> +	struct rtnl_link_stats64 *stats;
> +	struct ksz_stats_raw *raw;
> +	struct ksz_port_mib *mib;
> +
> +	mib = &dev->ports[port].mib;
> +	stats = &mib->stats64;
> +	raw = (struct ksz_stats_raw *)mib->counters;
> +
> +	spin_lock(&mib->stats64_lock);
> +
> +	stats->rx_packets = raw->rx_bcast + raw->rx_mcast + raw->rx_ucast;
> +	stats->tx_packets = raw->tx_bcast + raw->tx_mcast + raw->tx_ucast;
> +
> +	/* HW counters are counting bytes + FCS which is not acceptable
> +	 * for rtnl_link_stats64 interface
> +	 */
> +	stats->rx_bytes = raw->rx_total - stats->rx_packets * ETH_FCS_LEN;
> +	stats->tx_bytes = raw->tx_total - stats->tx_packets * ETH_FCS_LEN;
> +
> +	stats->rx_length_errors = raw->rx_undersize + raw->rx_fragments +
> +		raw->rx_oversize;
> +
> +	stats->rx_crc_errors = raw->rx_crc_err;
> +	stats->rx_frame_errors = raw->rx_align_err;
> +	stats->rx_dropped = raw->rx_discards;
> +	stats->rx_errors = stats->rx_length_errors + stats->rx_crc_errors +
> +		stats->rx_frame_errors  + stats->rx_dropped;
> +
> +	stats->tx_window_errors = raw->tx_late_col;
> +	stats->tx_fifo_errors = raw->tx_discards;
> +	stats->tx_aborted_errors = raw->tx_exc_col;
> +	stats->tx_errors = stats->tx_window_errors + stats->tx_fifo_errors +
> +		stats->tx_aborted_errors;
> +
> +	stats->multicast = raw->rx_mcast;
> +	stats->collisions = raw->tx_total_col;
> +
> +	spin_unlock(&mib->stats64_lock);
> +}
> +EXPORT_SYMBOL_GPL(ksz_r_mib_stats64);
> +
> +void ksz_get_stats64(struct dsa_switch *ds, int port,
> +		     struct rtnl_link_stats64 *s)
> +{
> +	struct ksz_device *dev = ds->priv;
> +	struct ksz_port_mib *mib;
> +
> +	mib = &dev->ports[port].mib;
> +
> +	spin_lock(&mib->stats64_lock);
> +	memcpy(s, &mib->stats64, sizeof(*s));
> +	spin_unlock(&mib->stats64_lock);
> +}
> +EXPORT_SYMBOL_GPL(ksz_get_stats64);
> +
>  void ksz_update_port_member(struct ksz_device *dev, int port)
>  {
>  	struct ksz_port *p = &dev->ports[port];
> diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h
> index 4d978832c448..28cda79b090f 100644
> --- a/drivers/net/dsa/microchip/ksz_common.h
> +++ b/drivers/net/dsa/microchip/ksz_common.h
> @@ -151,6 +151,9 @@ int ksz9477_switch_register(struct ksz_device *dev);
>  
>  void ksz_update_port_member(struct ksz_device *dev, int port);
>  void ksz_init_mib_timer(struct ksz_device *dev);
> +void ksz_r_mib_stats64(struct ksz_device *dev, int port);
> +void ksz_get_stats64(struct dsa_switch *ds, int port,
> +		     struct rtnl_link_stats64 *s);
>  
>  /* Common DSA access functions */
>  
> -- 
> 2.33.0
> 
> 

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

^ permalink raw reply

* Re: [PATCH] batman-adv: remove unnecessary type castings
From: yuzhe @ 2022-04-25 11:14 UTC (permalink / raw)
  To: Sven Eckelmann
  Cc: a, b.a.t.m.a.n, davem, kernel-janitors, kuba, linux-kernel,
	liqiong, mareklindner, netdev, pabeni, sw
In-Reply-To: <3537486.13E77TLkhO@ripper>

Hi,

thanks for your reply, we have fixed our mail server. And I'll correct and resubmit my patch.

在 2022/4/22 15:55, Sven Eckelmann 写道:

> Hi,
>
> we neither received your mail via the mailing list nor our private mail
> servers. It seems your mail setup is broken:
>
>      Apr 21 15:48:37 dvalin postfix/smtpd[10256]: NOQUEUE: reject: RCPT from unknown[2400:dd01:100f:2:72e2:84ff:fe10:5f45]: 450 4.7.1 <ha.nfschina.com>: Helo command rejected: Host not found; from=<yuzhe@nfschina.com> to=<sven@narfation.org> proto=ESMTP helo=<ha.nfschina.co>
>
>
> And when I test it myself, it is also not working:
>
>      $ dig @8.8.8.8 ha.nfschina.com
>
>      ; <<>> DiG 9.16.27-Debian <<>> @8.8.8.8 ha.nfschina.com
>      ; (1 server found)
>      ;; global options: +cmd
>      ;; Got answer:
>      ;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 39639
>      ;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1
>      
>      ;; OPT PSEUDOSECTION:
>      ; EDNS: version: 0, flags:; udp: 512
>      ;; QUESTION SECTION:
>      ;ha.nfschina.com.               IN      A
>      
>      ;; AUTHORITY SECTION:
>      nfschina.com.           600     IN      SOA     dns11.hichina.com. hostmaster.hichina.com. 2022011002 3600 1200 86400 600
>
>      ;; Query time: 328 msec
>      ;; SERVER: 8.8.8.8#53(8.8.8.8)
>      ;; WHEN: Fri Apr 22 09:51:56 CEST 2022
>      ;; MSG SIZE  rcvd: 105
>
>
> Please fix this before sending patches out.
>
>
> But the kernel test bot already demonstrated why this patch is not a good
> idea. You can improve it and resent it but I will not accept it in this form.
>
>
> Kind regards,
> 	Sven

^ permalink raw reply

* Re: [PATCH net] tcp: fix potential xmit stalls caused by TCP_NOTSENT_LOWAT
From: patchwork-bot+netdevbpf @ 2022-04-25 11:10 UTC (permalink / raw)
  To: Eric Dumazet
  Cc: davem, kuba, pabeni, netdev, edumazet, dsp, soheil, ncardwell
In-Reply-To: <20220425003407.3002429-1-eric.dumazet@gmail.com>

Hello:

This patch was applied to netdev/net.git (master)
by David S. Miller <davem@davemloft.net>:

On Sun, 24 Apr 2022 17:34:07 -0700 you wrote:
> From: Eric Dumazet <edumazet@google.com>
> 
> I had this bug sitting for too long in my pile, it is time to fix it.
> 
> Thanks to Doug Porter for reminding me of it!
> 
> We had various attempts in the past, including commit
> 0cbe6a8f089e ("tcp: remove SOCK_QUEUE_SHRUNK"),
> but the issue is that TCP stack currently only generates
> EPOLLOUT from input path, when tp->snd_una has advanced
> and skb(s) cleaned from rtx queue.
> 
> [...]

Here is the summary with links:
  - [net] tcp: fix potential xmit stalls caused by TCP_NOTSENT_LOWAT
    https://git.kernel.org/netdev/net/c/4bfe744ff164

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply

* Re: [PATCH net-next] arp: fix unused variable warnning when CONFIG_PROC_FS=n
From: patchwork-bot+netdevbpf @ 2022-04-25 11:00 UTC (permalink / raw)
  To: Yajun Deng
  Cc: davem, yoshfuji, dsahern, kuba, pabeni, netdev, linux-kernel, lkp
In-Reply-To: <20220422061431.1905579-1-yajun.deng@linux.dev>

Hello:

This patch was applied to netdev/net-next.git (master)
by David S. Miller <davem@davemloft.net>:

On Fri, 22 Apr 2022 14:14:31 +0800 you wrote:
> net/ipv4/arp.c:1412:36: warning: unused variable 'arp_seq_ops' [-Wunused-const-variable]
> 
> Add #ifdef CONFIG_PROC_FS for 'arp_seq_ops'.
> 
> Fixes: e968b1b3e9b8 ("arp: Remove #ifdef CONFIG_PROC_FS")
> Reported-by: kernel test robot <lkp@intel.com>
> Signed-off-by: Yajun Deng <yajun.deng@linux.dev>
> 
> [...]

Here is the summary with links:
  - [net-next] arp: fix unused variable warnning when CONFIG_PROC_FS=n
    https://git.kernel.org/netdev/net-next/c/b0e653b2a0d9

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply

* Re: [PATCH net 0/2] Fix Ocelot VLAN regressions introduced by FDB isolation
From: patchwork-bot+netdevbpf @ 2022-04-25 11:00 UTC (permalink / raw)
  To: Vladimir Oltean
  Cc: netdev, kuba, davem, pabeni, f.fainelli, andrew, vivien.didelot,
	olteanv, claudiu.manoil, alexandre.belloni, UNGLinuxDriver
In-Reply-To: <20220421230105.3570690-1-vladimir.oltean@nxp.com>

Hello:

This series was applied to netdev/net.git (master)
by David S. Miller <davem@davemloft.net>:

On Fri, 22 Apr 2022 02:01:03 +0300 you wrote:
> There are 2 regressions in the VLAN handling code of the ocelot/felix
> DSA driver which can be seen when running the bridge_vlan_aware.sh
> selftest. These manifest in the form of valid VLAN configurations being
> rejected by the driver with incorrect extack messages.
> 
> First regression occurs when we attempt to install an egress-untagged
> bridge VLAN to a bridge port that was brought up *while* it was under a
> bridge (not before).
> 
> [...]

Here is the summary with links:
  - [net,1/2] net: mscc: ocelot: ignore VID 0 added by 8021q module
    https://git.kernel.org/netdev/net/c/9323ac367005
  - [net,2/2] net: mscc: ocelot: don't add VID 0 to ocelot->vlans when leaving VLAN-aware bridge
    https://git.kernel.org/netdev/net/c/1fcb8fb3522f

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply

* [PATCH memcg v3] net: set proper memcg for net_init hooks allocations
From: Vasily Averin @ 2022-04-25 10:56 UTC (permalink / raw)
  To: Vlastimil Babka, Shakeel Butt
  Cc: kernel, Florian Westphal, linux-kernel, Roman Gushchin,
	Michal Hocko, cgroups, netdev, David S. Miller, Jakub Kicinski,
	Paolo Abeni
In-Reply-To: <20220424144627.GB13403@xsang-OptiPlex-9020>

__register_pernet_operations() executes init hook of registered
pernet_operation structure in all existing net namespaces.

Typically, these hooks are called by a process associated with
the specified net namespace, and all __GFP_ACCOUNTING marked
allocation are accounted for corresponding container/memcg.

However __register_pernet_operations() calls the hooks in the same
context, and as a result all marked allocations are accounted
to one memcg for all processed net namespaces.

This patch adjusts active memcg for each net namespace and helps
to account memory allocated inside ops_init() into the proper memcg.

Signed-off-by: Vasily Averin <vvs@openvz.org>
---
v3: put_net_memcg() replaced by an alreay existing mem_cgroup_put()
    It checks memcg before accessing it, this is required for
    __register_pernet_operations() called before memcg initialization.
    Additionally fixed leading whitespaces in non-memcg_kmem version
    of mem_cgroup_from_obj().

v2: introduced get/put_net_memcg(),
    new functions are moved under CONFIG_MEMCG_KMEM
    to fix compilation issues reported by Intel's kernel test robot

v1: introduced get_mem_cgroup_from_kmem(), which takes the refcount
    for the found memcg, suggested by Shakeel
---
 include/linux/memcontrol.h | 29 ++++++++++++++++++++++++++++-
 net/core/net_namespace.c   |  7 +++++++
 2 files changed, 35 insertions(+), 1 deletion(-)

diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h
index 0abbd685703b..cfb68a3f7015 100644
--- a/include/linux/memcontrol.h
+++ b/include/linux/memcontrol.h
@@ -1714,6 +1714,29 @@ static inline int memcg_cache_id(struct mem_cgroup *memcg)
 
 struct mem_cgroup *mem_cgroup_from_obj(void *p);
 
+static inline struct mem_cgroup *get_mem_cgroup_from_kmem(void *p)
+{
+	struct mem_cgroup *memcg;
+
+	rcu_read_lock();
+	do {
+		memcg = mem_cgroup_from_obj(p);
+	} while (memcg && !css_tryget(&memcg->css));
+	rcu_read_unlock();
+	return memcg;
+}
+
+static inline struct mem_cgroup *get_net_memcg(void *p)
+{
+	struct mem_cgroup *memcg;
+
+	memcg = get_mem_cgroup_from_kmem(p);
+
+	if (!memcg)
+		memcg = root_mem_cgroup;
+
+	return memcg;
+}
 #else
 static inline bool mem_cgroup_kmem_disabled(void)
 {
@@ -1763,9 +1786,13 @@ static inline void memcg_put_cache_ids(void)
 
 static inline struct mem_cgroup *mem_cgroup_from_obj(void *p)
 {
-       return NULL;
+	return NULL;
 }
 
+static inline struct mem_cgroup *get_net_memcg(void *p)
+{
+	return NULL;
+}
 #endif /* CONFIG_MEMCG_KMEM */
 
 #endif /* _LINUX_MEMCONTROL_H */
diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c
index a5b5bb99c644..3093b4d5b2b9 100644
--- a/net/core/net_namespace.c
+++ b/net/core/net_namespace.c
@@ -26,6 +26,7 @@
 #include <net/net_namespace.h>
 #include <net/netns/generic.h>
 
+#include <linux/sched/mm.h>
 /*
  *	Our network namespace constructor/destructor lists
  */
@@ -1147,7 +1148,13 @@ static int __register_pernet_operations(struct list_head *list,
 		 * setup_net() and cleanup_net() are not possible.
 		 */
 		for_each_net(net) {
+			struct mem_cgroup *old, *memcg;
+
+			memcg = get_net_memcg(net);
+			old = set_active_memcg(memcg);
 			error = ops_init(ops, net);
+			set_active_memcg(old);
+			mem_cgroup_put(memcg);
 			if (error)
 				goto out_undo;
 			list_add_tail(&net->exit_list, &net_exit_list);
-- 
2.31.1


^ permalink raw reply related

* [RFC Patch net-next] net: dsa: ksz9477: move get_stats64 to ksz_common.c
From: Arun Ramadoss @ 2022-04-25 10:55 UTC (permalink / raw)
  To: linux-kernel, netdev
  Cc: Paolo Abeni, Jakub Kicinski, David S. Miller, Vladimir Oltean,
	Florian Fainelli, Vivien Didelot, Andrew Lunn, UNGLinuxDriver,
	Woojung Huh, Oleksij Rempel

The mib counters for the ksz9477 is same for the ksz9477 switch and
LAN937x switch. Hence moving it to ksz_common.c file in order to have it
generic function. The DSA hook get_stats64 now can call ksz_get_stats64.

Signed-off-by: Arun Ramadoss <arun.ramadoss@microchip.com>
---
 drivers/net/dsa/microchip/ksz9477.c    | 98 +-------------------------
 drivers/net/dsa/microchip/ksz_common.c | 96 +++++++++++++++++++++++++
 drivers/net/dsa/microchip/ksz_common.h |  3 +
 3 files changed, 101 insertions(+), 96 deletions(-)

diff --git a/drivers/net/dsa/microchip/ksz9477.c b/drivers/net/dsa/microchip/ksz9477.c
index 4f617fee9a4e..48c90e4cda30 100644
--- a/drivers/net/dsa/microchip/ksz9477.c
+++ b/drivers/net/dsa/microchip/ksz9477.c
@@ -65,100 +65,6 @@ static const struct {
 	{ 0x83, "tx_discards" },
 };
 
-struct ksz9477_stats_raw {
-	u64 rx_hi;
-	u64 rx_undersize;
-	u64 rx_fragments;
-	u64 rx_oversize;
-	u64 rx_jabbers;
-	u64 rx_symbol_err;
-	u64 rx_crc_err;
-	u64 rx_align_err;
-	u64 rx_mac_ctrl;
-	u64 rx_pause;
-	u64 rx_bcast;
-	u64 rx_mcast;
-	u64 rx_ucast;
-	u64 rx_64_or_less;
-	u64 rx_65_127;
-	u64 rx_128_255;
-	u64 rx_256_511;
-	u64 rx_512_1023;
-	u64 rx_1024_1522;
-	u64 rx_1523_2000;
-	u64 rx_2001;
-	u64 tx_hi;
-	u64 tx_late_col;
-	u64 tx_pause;
-	u64 tx_bcast;
-	u64 tx_mcast;
-	u64 tx_ucast;
-	u64 tx_deferred;
-	u64 tx_total_col;
-	u64 tx_exc_col;
-	u64 tx_single_col;
-	u64 tx_mult_col;
-	u64 rx_total;
-	u64 tx_total;
-	u64 rx_discards;
-	u64 tx_discards;
-};
-
-static void ksz9477_r_mib_stats64(struct ksz_device *dev, int port)
-{
-	struct rtnl_link_stats64 *stats;
-	struct ksz9477_stats_raw *raw;
-	struct ksz_port_mib *mib;
-
-	mib = &dev->ports[port].mib;
-	stats = &mib->stats64;
-	raw = (struct ksz9477_stats_raw *)mib->counters;
-
-	spin_lock(&mib->stats64_lock);
-
-	stats->rx_packets = raw->rx_bcast + raw->rx_mcast + raw->rx_ucast;
-	stats->tx_packets = raw->tx_bcast + raw->tx_mcast + raw->tx_ucast;
-
-	/* HW counters are counting bytes + FCS which is not acceptable
-	 * for rtnl_link_stats64 interface
-	 */
-	stats->rx_bytes = raw->rx_total - stats->rx_packets * ETH_FCS_LEN;
-	stats->tx_bytes = raw->tx_total - stats->tx_packets * ETH_FCS_LEN;
-
-	stats->rx_length_errors = raw->rx_undersize + raw->rx_fragments +
-		raw->rx_oversize;
-
-	stats->rx_crc_errors = raw->rx_crc_err;
-	stats->rx_frame_errors = raw->rx_align_err;
-	stats->rx_dropped = raw->rx_discards;
-	stats->rx_errors = stats->rx_length_errors + stats->rx_crc_errors +
-		stats->rx_frame_errors  + stats->rx_dropped;
-
-	stats->tx_window_errors = raw->tx_late_col;
-	stats->tx_fifo_errors = raw->tx_discards;
-	stats->tx_aborted_errors = raw->tx_exc_col;
-	stats->tx_errors = stats->tx_window_errors + stats->tx_fifo_errors +
-		stats->tx_aborted_errors;
-
-	stats->multicast = raw->rx_mcast;
-	stats->collisions = raw->tx_total_col;
-
-	spin_unlock(&mib->stats64_lock);
-}
-
-static void ksz9477_get_stats64(struct dsa_switch *ds, int port,
-			       struct rtnl_link_stats64 *s)
-{
-	struct ksz_device *dev = ds->priv;
-	struct ksz_port_mib *mib;
-
-	mib = &dev->ports[port].mib;
-
-	spin_lock(&mib->stats64_lock);
-	memcpy(s, &mib->stats64, sizeof(*s));
-	spin_unlock(&mib->stats64_lock);
-}
-
 static void ksz_cfg(struct ksz_device *dev, u32 addr, u8 bits, bool set)
 {
 	regmap_update_bits(dev->regmap[0], addr, bits, set ? bits : 0);
@@ -1462,7 +1368,7 @@ static const struct dsa_switch_ops ksz9477_switch_ops = {
 	.port_mdb_del           = ksz9477_port_mdb_del,
 	.port_mirror_add	= ksz9477_port_mirror_add,
 	.port_mirror_del	= ksz9477_port_mirror_del,
-	.get_stats64		= ksz9477_get_stats64,
+	.get_stats64		= ksz_get_stats64,
 	.port_change_mtu	= ksz9477_change_mtu,
 	.port_max_mtu		= ksz9477_max_mtu,
 };
@@ -1653,7 +1559,7 @@ static const struct ksz_dev_ops ksz9477_dev_ops = {
 	.port_setup = ksz9477_port_setup,
 	.r_mib_cnt = ksz9477_r_mib_cnt,
 	.r_mib_pkt = ksz9477_r_mib_pkt,
-	.r_mib_stat64 = ksz9477_r_mib_stats64,
+	.r_mib_stat64 = ksz_r_mib_stats64,
 	.freeze_mib = ksz9477_freeze_mib,
 	.port_init_cnt = ksz9477_port_init_cnt,
 	.shutdown = ksz9477_reset_switch,
diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c
index 9b9f570ebb0b..10f127b09e58 100644
--- a/drivers/net/dsa/microchip/ksz_common.c
+++ b/drivers/net/dsa/microchip/ksz_common.c
@@ -20,6 +20,102 @@
 
 #include "ksz_common.h"
 
+struct ksz_stats_raw {
+	u64 rx_hi;
+	u64 rx_undersize;
+	u64 rx_fragments;
+	u64 rx_oversize;
+	u64 rx_jabbers;
+	u64 rx_symbol_err;
+	u64 rx_crc_err;
+	u64 rx_align_err;
+	u64 rx_mac_ctrl;
+	u64 rx_pause;
+	u64 rx_bcast;
+	u64 rx_mcast;
+	u64 rx_ucast;
+	u64 rx_64_or_less;
+	u64 rx_65_127;
+	u64 rx_128_255;
+	u64 rx_256_511;
+	u64 rx_512_1023;
+	u64 rx_1024_1522;
+	u64 rx_1523_2000;
+	u64 rx_2001;
+	u64 tx_hi;
+	u64 tx_late_col;
+	u64 tx_pause;
+	u64 tx_bcast;
+	u64 tx_mcast;
+	u64 tx_ucast;
+	u64 tx_deferred;
+	u64 tx_total_col;
+	u64 tx_exc_col;
+	u64 tx_single_col;
+	u64 tx_mult_col;
+	u64 rx_total;
+	u64 tx_total;
+	u64 rx_discards;
+	u64 tx_discards;
+};
+
+void ksz_r_mib_stats64(struct ksz_device *dev, int port)
+{
+	struct rtnl_link_stats64 *stats;
+	struct ksz_stats_raw *raw;
+	struct ksz_port_mib *mib;
+
+	mib = &dev->ports[port].mib;
+	stats = &mib->stats64;
+	raw = (struct ksz_stats_raw *)mib->counters;
+
+	spin_lock(&mib->stats64_lock);
+
+	stats->rx_packets = raw->rx_bcast + raw->rx_mcast + raw->rx_ucast;
+	stats->tx_packets = raw->tx_bcast + raw->tx_mcast + raw->tx_ucast;
+
+	/* HW counters are counting bytes + FCS which is not acceptable
+	 * for rtnl_link_stats64 interface
+	 */
+	stats->rx_bytes = raw->rx_total - stats->rx_packets * ETH_FCS_LEN;
+	stats->tx_bytes = raw->tx_total - stats->tx_packets * ETH_FCS_LEN;
+
+	stats->rx_length_errors = raw->rx_undersize + raw->rx_fragments +
+		raw->rx_oversize;
+
+	stats->rx_crc_errors = raw->rx_crc_err;
+	stats->rx_frame_errors = raw->rx_align_err;
+	stats->rx_dropped = raw->rx_discards;
+	stats->rx_errors = stats->rx_length_errors + stats->rx_crc_errors +
+		stats->rx_frame_errors  + stats->rx_dropped;
+
+	stats->tx_window_errors = raw->tx_late_col;
+	stats->tx_fifo_errors = raw->tx_discards;
+	stats->tx_aborted_errors = raw->tx_exc_col;
+	stats->tx_errors = stats->tx_window_errors + stats->tx_fifo_errors +
+		stats->tx_aborted_errors;
+
+	stats->multicast = raw->rx_mcast;
+	stats->collisions = raw->tx_total_col;
+
+	spin_unlock(&mib->stats64_lock);
+}
+EXPORT_SYMBOL_GPL(ksz_r_mib_stats64);
+
+void ksz_get_stats64(struct dsa_switch *ds, int port,
+		     struct rtnl_link_stats64 *s)
+{
+	struct ksz_device *dev = ds->priv;
+	struct ksz_port_mib *mib;
+
+	mib = &dev->ports[port].mib;
+
+	spin_lock(&mib->stats64_lock);
+	memcpy(s, &mib->stats64, sizeof(*s));
+	spin_unlock(&mib->stats64_lock);
+}
+EXPORT_SYMBOL_GPL(ksz_get_stats64);
+
 void ksz_update_port_member(struct ksz_device *dev, int port)
 {
 	struct ksz_port *p = &dev->ports[port];
diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h
index 4d978832c448..28cda79b090f 100644
--- a/drivers/net/dsa/microchip/ksz_common.h
+++ b/drivers/net/dsa/microchip/ksz_common.h
@@ -151,6 +151,9 @@ int ksz9477_switch_register(struct ksz_device *dev);
 
 void ksz_update_port_member(struct ksz_device *dev, int port);
 void ksz_init_mib_timer(struct ksz_device *dev);
+void ksz_r_mib_stats64(struct ksz_device *dev, int port);
+void ksz_get_stats64(struct dsa_switch *ds, int port,
+		     struct rtnl_link_stats64 *s);
 
 /* Common DSA access functions */
 
-- 
2.33.0


^ permalink raw reply related

* Re: [ovs-dev] [PATCH] openvswitch: Ensure nf_ct_put is not called with null pointer
From: Florian Westphal @ 2022-04-25 10:53 UTC (permalink / raw)
  To: Ilya Maximets
  Cc: Florian Westphal, Mark Mielke, dev, netdev, stable,
	Jakub Kicinski, Paolo Abeni, David S. Miller, Pablo Neira Ayuso,
	Antti Antinoja
In-Reply-To: <590d44a1-ca27-c171-de87-fe57fc07dff5@ovn.org>

Ilya Maximets <i.maximets@ovn.org> wrote:
> Hi, Florian.
> 
> There is a problem on 5.15 longterm tree where the offending commit
> got backported, but the previous one was not, so it triggers an issue
> while loading the openvswitch module.
> 
> To be more clear, v5.15.35 contains the following commit:
>   408bdcfce8df ("net: prefer nf_ct_put instead of nf_conntrack_put")
> backported as commit 72dd9e61fa319bc44020c2d365275fc8f6799bff, but
> it doesn't have the previous one:
>   6ae7989c9af0 ("netfilter: conntrack: avoid useless indirection during conntrack destruction")
> that adds the NULL pointer check to the nf_ct_put().
> 
> Either 6ae7989c9af0 should be backported to 5.15 or 72dd9e61fa31
> reverted on that tree.

The commit was never meant to be backported to stable, it doesn't fix any bug.

I suspect it was done to take 'net/sched: act_ct: fix ref leak when
switching zones' without munging it.

I suggest to add stable-only patch that makes nf_ct_put(NULL)
legal, like in linux.git, but I don't know stable team preferences.

^ permalink raw reply

* Re: [PATCH net] net: dsa: flood multicast to CPU when slave has IFF_PROMISC
From: patchwork-bot+netdevbpf @ 2022-04-25 10:50 UTC (permalink / raw)
  To: Vladimir Oltean
  Cc: netdev, kuba, davem, pabeni, f.fainelli, andrew, vivien.didelot,
	olteanv
In-Reply-To: <20220421224222.3563522-1-vladimir.oltean@nxp.com>

Hello:

This patch was applied to netdev/net.git (master)
by David S. Miller <davem@davemloft.net>:

On Fri, 22 Apr 2022 01:42:22 +0300 you wrote:
> Certain DSA switches can eliminate flooding to the CPU when none of the
> ports have the IFF_ALLMULTI or IFF_PROMISC flags set. This is done by
> synthesizing a call to dsa_port_bridge_flags() for the CPU port, a call
> which normally comes from the bridge driver via switchdev.
> 
> The bridge port flags and IFF_PROMISC|IFF_ALLMULTI have slightly
> different semantics, and due to inattention/lack of proper testing, the
> IFF_PROMISC flag allows unknown unicast to be flooded to the CPU, but
> not unknown multicast.
> 
> [...]

Here is the summary with links:
  - [net] net: dsa: flood multicast to CPU when slave has IFF_PROMISC
    https://git.kernel.org/netdev/net/c/7c762e70c50b

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply

* Re: [PATCH net 0/3] ip_gre, ip6_gre: o_seqno fixes
From: patchwork-bot+netdevbpf @ 2022-04-25 10:50 UTC (permalink / raw)
  To: Peilin Ye
  Cc: davem, kuba, yoshfuji, dsahern, pabeni, peilin.ye, xeb, u9012063,
	daniel, cong.wang, eric.dumazet, ast, andrii, kafai,
	songliubraving, yhs, john.fastabend, kpsingh, netdev, bpf,
	linux-kernel
In-Reply-To: <cover.1650575919.git.peilin.ye@bytedance.com>

Hello:

This series was applied to netdev/net.git (master)
by David S. Miller <davem@davemloft.net>:

On Thu, 21 Apr 2022 15:06:39 -0700 you wrote:
> From: Peilin Ye <peilin.ye@bytedance.com>
> 
> Hi all,
> 
> As pointed out [1] by Jakub Kicinski, currently using TUNNEL_SEQ in
> collect_md mode is racy for [IP6]GRE[TAP] devices, since they (typically,
> e.g. if created using "ip") use lockless TX.
> 
> [...]

Here is the summary with links:
  - [net,1/3] ip_gre: Make o_seqno start from 0 in native mode
    https://git.kernel.org/netdev/net/c/ff827beb706e
  - [net,2/3] ip6_gre: Make o_seqno start from 0 in native mode
    https://git.kernel.org/netdev/net/c/fde98ae91f79
  - [net,3/3] ip_gre, ip6_gre: Fix race condition on o_seqno in collect_md mode
    https://git.kernel.org/netdev/net/c/31c417c948d7

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply

* [PATCH net v3] virtio_net: fix wrong buf address calculation when using xdp
From: Nikolay Aleksandrov @ 2022-04-25 10:37 UTC (permalink / raw)
  To: netdev
  Cc: kuba, davem, Nikolay Aleksandrov, stable, Jason Wang, Xuan Zhuo,
	Daniel Borkmann, Michael S. Tsirkin, virtualization

We received a report[1] of kernel crashes when Cilium is used in XDP
mode with virtio_net after updating to newer kernels. After
investigating the reason it turned out that when using mergeable bufs
with an XDP program which adjusts xdp.data or xdp.data_meta page_to_buf()
calculates the build_skb address wrong because the offset can become less
than the headroom so it gets the address of the previous page (-X bytes
depending on how lower offset is):
 page_to_skb: page addr ffff9eb2923e2000 buf ffff9eb2923e1ffc offset 252 headroom 256

This is a pr_err() I added in the beginning of page_to_skb which clearly
shows offset that is less than headroom by adding 4 bytes of metadata
via an xdp prog. The calculations done are:
 receive_mergeable():
 headroom = VIRTIO_XDP_HEADROOM; // VIRTIO_XDP_HEADROOM == 256 bytes
 offset = xdp.data - page_address(xdp_page) -
          vi->hdr_len - metasize;

 page_to_skb():
 p = page_address(page) + offset;
 ...
 buf = p - headroom;

Now buf goes -4 bytes from the page's starting address as can be seen
above which is set as skb->head and skb->data by build_skb later. Depending
on what's done with the skb (when it's freed most often) we get all kinds
of corruptions and BUG_ON() triggers in mm[2]. We have to recalculate
the new headroom after the xdp program has run, similar to how offset
and len are recalculated. Headroom is directly related to
data_hard_start, data and data_meta, so we use them to get the new size.
The result is correct (similar pr_err() in page_to_skb, one case of
xdp_page and one case of virtnet buf):
 a) Case with 4 bytes of metadata
 [  115.949641] page_to_skb: page addr ffff8b4dcfad2000 offset 252 headroom 252
 [  121.084105] page_to_skb: page addr ffff8b4dcf018000 offset 20732 headroom 252
 b) Case of pushing data +32 bytes
 [  153.181401] page_to_skb: page addr ffff8b4dd0c4d000 offset 288 headroom 288
 [  158.480421] page_to_skb: page addr ffff8b4dd00b0000 offset 24864 headroom 288
 c) Case of pushing data -33 bytes
 [  835.906830] page_to_skb: page addr ffff8b4dd3270000 offset 223 headroom 223
 [  840.839910] page_to_skb: page addr ffff8b4dcdd68000 offset 12511 headroom 223

Offset and headroom are equal because offset points to the start of
reserved bytes for the virtio_net header which are at buf start +
headroom, while data points at buf start + vnet hdr size + headroom so
when data or data_meta are adjusted by the xdp prog both the headroom size
and the offset change equally. We can use data_hard_start to compute the
new headroom after the xdp prog (linearized / page start case, the
virtnet buf case is similar just with bigger base offset):
 xdp.data_hard_start = page_address + vnet_hdr
 xdp.data = page_address + vnet_hdr + headroom
 new headroom after xdp prog = xdp.data - xdp.data_hard_start - metasize

An example reproducer xdp prog[3] is below.

[1] https://github.com/cilium/cilium/issues/19453

[2] Two of the many traces:
 [   40.437400] BUG: Bad page state in process swapper/0  pfn:14940
 [   40.916726] BUG: Bad page state in process systemd-resolve  pfn:053b7
 [   41.300891] kernel BUG at include/linux/mm.h:720!
 [   41.301801] invalid opcode: 0000 [#1] PREEMPT SMP NOPTI
 [   41.302784] CPU: 1 PID: 1181 Comm: kubelet Kdump: loaded Tainted: G    B   W         5.18.0-rc1+ #37
 [   41.304458] Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.15.0-1.fc35 04/01/2014
 [   41.306018] RIP: 0010:page_frag_free+0x79/0xe0
 [   41.306836] Code: 00 00 75 ea 48 8b 07 a9 00 00 01 00 74 e0 48 8b 47 48 48 8d 50 ff a8 01 48 0f 45 fa eb d0 48 c7 c6 18 b8 30 a6 e8 d7 f8 fc ff <0f> 0b 48 8d 78 ff eb bc 48 8b 07 a9 00 00 01 00 74 3a 66 90 0f b6
 [   41.310235] RSP: 0018:ffffac05c2a6bc78 EFLAGS: 00010292
 [   41.311201] RAX: 000000000000003e RBX: 0000000000000000 RCX: 0000000000000000
 [   41.312502] RDX: 0000000000000001 RSI: ffffffffa6423004 RDI: 00000000ffffffff
 [   41.313794] RBP: ffff993c98823600 R08: 0000000000000000 R09: 00000000ffffdfff
 [   41.315089] R10: ffffac05c2a6ba68 R11: ffffffffa698ca28 R12: ffff993c98823600
 [   41.316398] R13: ffff993c86311ebc R14: 0000000000000000 R15: 000000000000005c
 [   41.317700] FS:  00007fe13fc56740(0000) GS:ffff993cdd900000(0000) knlGS:0000000000000000
 [   41.319150] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
 [   41.320152] CR2: 000000c00008a000 CR3: 0000000014908000 CR4: 0000000000350ee0
 [   41.321387] Call Trace:
 [   41.321819]  <TASK>
 [   41.322193]  skb_release_data+0x13f/0x1c0
 [   41.322902]  __kfree_skb+0x20/0x30
 [   41.343870]  tcp_recvmsg_locked+0x671/0x880
 [   41.363764]  tcp_recvmsg+0x5e/0x1c0
 [   41.384102]  inet_recvmsg+0x42/0x100
 [   41.406783]  ? sock_recvmsg+0x1d/0x70
 [   41.428201]  sock_read_iter+0x84/0xd0
 [   41.445592]  ? 0xffffffffa3000000
 [   41.462442]  new_sync_read+0x148/0x160
 [   41.479314]  ? 0xffffffffa3000000
 [   41.496937]  vfs_read+0x138/0x190
 [   41.517198]  ksys_read+0x87/0xc0
 [   41.535336]  do_syscall_64+0x3b/0x90
 [   41.551637]  entry_SYSCALL_64_after_hwframe+0x44/0xae
 [   41.568050] RIP: 0033:0x48765b
 [   41.583955] Code: e8 4a 35 fe ff eb 88 cc cc cc cc cc cc cc cc e8 fb 7a fe ff 48 8b 7c 24 10 48 8b 74 24 18 48 8b 54 24 20 48 8b 44 24 08 0f 05 <48> 3d 01 f0 ff ff 76 20 48 c7 44 24 28 ff ff ff ff 48 c7 44 24 30
 [   41.632818] RSP: 002b:000000c000a2f5b8 EFLAGS: 00000212 ORIG_RAX: 0000000000000000
 [   41.664588] RAX: ffffffffffffffda RBX: 000000c000062000 RCX: 000000000048765b
 [   41.681205] RDX: 0000000000005e54 RSI: 000000c000e66000 RDI: 0000000000000016
 [   41.697164] RBP: 000000c000a2f608 R08: 0000000000000001 R09: 00000000000001b4
 [   41.713034] R10: 00000000000000b6 R11: 0000000000000212 R12: 00000000000000e9
 [   41.728755] R13: 0000000000000001 R14: 000000c000a92000 R15: ffffffffffffffff
 [   41.744254]  </TASK>
 [   41.758585] Modules linked in: br_netfilter bridge veth netconsole virtio_net

 and

 [   33.524802] BUG: Bad page state in process systemd-network  pfn:11e60
 [   33.528617] page ffffe05dc0147b00 ffffe05dc04e7a00 ffff8ae9851ec000 (1) len 82 offset 252 metasize 4 hroom 0 hdr_len 12 data ffff8ae9851ec10c data_meta ffff8ae9851ec108 data_end ffff8ae9851ec14e
 [   33.529764] page:000000003792b5ba refcount:0 mapcount:-512 mapping:0000000000000000 index:0x0 pfn:0x11e60
 [   33.532463] flags: 0xfffffc0000000(node=0|zone=1|lastcpupid=0x1fffff)
 [   33.532468] raw: 000fffffc0000000 0000000000000000 dead000000000122 0000000000000000
 [   33.532470] raw: 0000000000000000 0000000000000000 00000000fffffdff 0000000000000000
 [   33.532471] page dumped because: nonzero mapcount
 [   33.532472] Modules linked in: br_netfilter bridge veth netconsole virtio_net
 [   33.532479] CPU: 0 PID: 791 Comm: systemd-network Kdump: loaded Not tainted 5.18.0-rc1+ #37
 [   33.532482] Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.15.0-1.fc35 04/01/2014
 [   33.532484] Call Trace:
 [   33.532496]  <TASK>
 [   33.532500]  dump_stack_lvl+0x45/0x5a
 [   33.532506]  bad_page.cold+0x63/0x94
 [   33.532510]  free_pcp_prepare+0x290/0x420
 [   33.532515]  free_unref_page+0x1b/0x100
 [   33.532518]  skb_release_data+0x13f/0x1c0
 [   33.532524]  kfree_skb_reason+0x3e/0xc0
 [   33.532527]  ip6_mc_input+0x23c/0x2b0
 [   33.532531]  ip6_sublist_rcv_finish+0x83/0x90
 [   33.532534]  ip6_sublist_rcv+0x22b/0x2b0

[3] XDP program to reproduce(xdp_pass.c):
 #include <linux/bpf.h>
 #include <bpf/bpf_helpers.h>

 SEC("xdp_pass")
 int xdp_pkt_pass(struct xdp_md *ctx)
 {
          bpf_xdp_adjust_head(ctx, -(int)32);
          return XDP_PASS;
 }

 char _license[] SEC("license") = "GPL";

 compile: clang -O2 -g -Wall -target bpf -c xdp_pass.c -o xdp_pass.o
 load on virtio_net: ip link set enp1s0 xdpdrv obj xdp_pass.o sec xdp_pass

CC: stable@vger.kernel.org
CC: Jason Wang <jasowang@redhat.com>
CC: Xuan Zhuo <xuanzhuo@linux.alibaba.com>
CC: Daniel Borkmann <daniel@iogearbox.net>
CC: "Michael S. Tsirkin" <mst@redhat.com>
CC: virtualization@lists.linux-foundation.org
Fixes: 8fb7da9e9907 ("virtio_net: get build_skb() buf by data ptr")
Signed-off-by: Nikolay Aleksandrov <razor@blackwall.org>
---
v3: Add a comment explaining why offset and headroom are equal,
    no code changes
v2: Recalculate headroom based on data, data_hard_start and data_meta

 drivers/net/virtio_net.c | 20 +++++++++++++++++++-
 1 file changed, 19 insertions(+), 1 deletion(-)

diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index 87838cbe38cf..cbba9d2e8f32 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -1005,6 +1005,24 @@ static struct sk_buff *receive_mergeable(struct net_device *dev,
 			 * xdp.data_meta were adjusted
 			 */
 			len = xdp.data_end - xdp.data + vi->hdr_len + metasize;
+
+			/* recalculate headroom if xdp.data or xdp_data_meta
+			 * were adjusted, note that offset should always point
+			 * to the start of the reserved bytes for virtio_net
+			 * header which are followed by xdp.data, that means
+			 * that offset is equal to the headroom (when buf is
+			 * starting at the beginning of the page, otherwise
+			 * there is a base offset inside the page) but it's used
+			 * with a different starting point (buf start) than
+			 * xdp.data (buf start + vnet hdr size). If xdp.data or
+			 * data_meta were adjusted by the xdp prog then the
+			 * headroom size has changed and so has the offset, we
+			 * can use data_hard_start, which points at buf start +
+			 * vnet hdr size, to calculate the new headroom and use
+			 * it later to compute buf start in page_to_skb()
+			 */
+			headroom = xdp.data - xdp.data_hard_start - metasize;
+
 			/* We can only create skb based on xdp_page. */
 			if (unlikely(xdp_page != page)) {
 				rcu_read_unlock();
@@ -1012,7 +1030,7 @@ static struct sk_buff *receive_mergeable(struct net_device *dev,
 				head_skb = page_to_skb(vi, rq, xdp_page, offset,
 						       len, PAGE_SIZE, false,
 						       metasize,
-						       VIRTIO_XDP_HEADROOM);
+						       headroom);
 				return head_skb;
 			}
 			break;
-- 
2.35.1


^ permalink raw reply related

* Re: [ovs-dev] [PATCH] openvswitch: Ensure nf_ct_put is not called with null pointer
From: Ilya Maximets @ 2022-04-25 10:36 UTC (permalink / raw)
  To: Florian Westphal, Mark Mielke
  Cc: dev, netdev, stable, Jakub Kicinski, Paolo Abeni, David S. Miller,
	Pablo Neira Ayuso, i.maximets, Antti Antinoja
In-Reply-To: <YlL6uN9WDPtFri0p@strlen.de>

On 4/10/22 17:41, Florian Westphal wrote:
> Mark Mielke <mark.mielke@gmail.com> wrote:
>> A recent commit replaced calls to nf_conntrack_put() with calls
>> to nf_ct_put(). nf_conntrack_put() permitted the caller to pass
>> null without side effects, while nf_ct_put() performs WARN_ON()
>> and proceeds to try and de-reference the pointer. ovs-vswitchd
>> triggers the warning on startup:
>>
>> [   22.178881] WARNING: CPU: 69 PID: 2157 at include/net/netfilter/nf_conntrack.h:176 __ovs_ct_lookup+0x4e2/0x6a0 [openvswitch]
>> ...
>> [   22.213573] Call Trace:
>> [   22.214318]  <TASK>
>> [   22.215064]  ovs_ct_execute+0x49c/0x7f0 [openvswitch]
>> ...
>> Cc: stable@vger.kernel.org
>> Fixes: 408bdcfce8df ("net: prefer nf_ct_put instead of nf_conntrack_put")
> 
> Actually, no.  As Pablo Neira just pointed out to me Upstream kernel is fine.
> The preceeding commit made nf_ct_out() a noop when ct is NULL.

Hi, Florian.

There is a problem on 5.15 longterm tree where the offending commit
got backported, but the previous one was not, so it triggers an issue
while loading the openvswitch module.

To be more clear, v5.15.35 contains the following commit:
  408bdcfce8df ("net: prefer nf_ct_put instead of nf_conntrack_put")
backported as commit 72dd9e61fa319bc44020c2d365275fc8f6799bff, but
it doesn't have the previous one:
  6ae7989c9af0 ("netfilter: conntrack: avoid useless indirection during conntrack destruction")
that adds the NULL pointer check to the nf_ct_put().

Either 6ae7989c9af0 should be backported to 5.15 or 72dd9e61fa31
reverted on that tree.

Best regards, Ilya Maximets.

^ permalink raw reply

* [patch iproute2-next v3] devlink: introduce -[he]x cmdline option to allow dumping numbers in hex format
From: Jiri Pirko @ 2022-04-25 10:36 UTC (permalink / raw)
  To: netdev; +Cc: sthemmin, dsahern, snelson

From: Jiri Pirko <jiri@nvidia.com>

For health reporter dumps it is quite convenient to have the numbers in
hexadecimal format. Introduce a command line option to allow user to
achieve that output.

Signed-off-by: Jiri Pirko <jiri@nvidia.com>
---
v2->v3:
- fixed patch subject
v1->v2:
- changed the hex output option to "-x"
- added "0x" prefix to hex numbers
---
 devlink/devlink.c  | 19 +++++++++++++------
 man/man8/devlink.8 |  4 ++++
 2 files changed, 17 insertions(+), 6 deletions(-)

diff --git a/devlink/devlink.c b/devlink/devlink.c
index da9f97788bcf..7ace968ca081 100644
--- a/devlink/devlink.c
+++ b/devlink/devlink.c
@@ -367,6 +367,7 @@ struct dl {
 	bool pretty_output;
 	bool verbose;
 	bool stats;
+	bool hex;
 	struct {
 		bool present;
 		char *bus_name;
@@ -8044,6 +8045,8 @@ static int cmd_health_dump_clear(struct dl *dl)
 
 static int fmsg_value_show(struct dl *dl, int type, struct nlattr *nl_data)
 {
+	const char *num_fmt = dl->hex ? "%#x" : "%u";
+	const char *num64_fmt = dl->hex ? "%#"PRIx64 : "%"PRIu64;
 	uint8_t *data;
 	uint32_t len;
 
@@ -8053,16 +8056,16 @@ static int fmsg_value_show(struct dl *dl, int type, struct nlattr *nl_data)
 		print_bool(PRINT_ANY, NULL, "%s", mnl_attr_get_u8(nl_data));
 		break;
 	case MNL_TYPE_U8:
-		print_uint(PRINT_ANY, NULL, "%u", mnl_attr_get_u8(nl_data));
+		print_uint(PRINT_ANY, NULL, num_fmt, mnl_attr_get_u8(nl_data));
 		break;
 	case MNL_TYPE_U16:
-		print_uint(PRINT_ANY, NULL, "%u", mnl_attr_get_u16(nl_data));
+		print_uint(PRINT_ANY, NULL, num_fmt, mnl_attr_get_u16(nl_data));
 		break;
 	case MNL_TYPE_U32:
-		print_uint(PRINT_ANY, NULL, "%u", mnl_attr_get_u32(nl_data));
+		print_uint(PRINT_ANY, NULL, num_fmt, mnl_attr_get_u32(nl_data));
 		break;
 	case MNL_TYPE_U64:
-		print_u64(PRINT_ANY, NULL, "%"PRIu64, mnl_attr_get_u64(nl_data));
+		print_u64(PRINT_ANY, NULL, num64_fmt, mnl_attr_get_u64(nl_data));
 		break;
 	case MNL_TYPE_NUL_STRING:
 		print_string(PRINT_ANY, NULL, "%s", mnl_attr_get_str(nl_data));
@@ -8928,7 +8931,7 @@ static void help(void)
 	pr_err("Usage: devlink [ OPTIONS ] OBJECT { COMMAND | help }\n"
 	       "       devlink [ -f[orce] ] -b[atch] filename -N[etns] netnsname\n"
 	       "where  OBJECT := { dev | port | sb | monitor | dpipe | resource | region | health | trap }\n"
-	       "       OPTIONS := { -V[ersion] | -n[o-nice-names] | -j[son] | -p[retty] | -v[erbose] -s[tatistics] }\n");
+	       "       OPTIONS := { -V[ersion] | -n[o-nice-names] | -j[son] | -p[retty] | -v[erbose] -s[tatistics] -[he]x }\n");
 }
 
 static int dl_cmd(struct dl *dl, int argc, char **argv)
@@ -9042,6 +9045,7 @@ int main(int argc, char **argv)
 		{ "statistics",		no_argument,		NULL, 's' },
 		{ "Netns",		required_argument,	NULL, 'N' },
 		{ "iec",		no_argument,		NULL, 'i' },
+		{ "hex",		no_argument,		NULL, 'x' },
 		{ NULL, 0, NULL, 0 }
 	};
 	const char *batch_file = NULL;
@@ -9057,7 +9061,7 @@ int main(int argc, char **argv)
 		return EXIT_FAILURE;
 	}
 
-	while ((opt = getopt_long(argc, argv, "Vfb:njpvsN:i",
+	while ((opt = getopt_long(argc, argv, "Vfb:njpvsN:ix",
 				  long_options, NULL)) >= 0) {
 
 		switch (opt) {
@@ -9095,6 +9099,9 @@ int main(int argc, char **argv)
 		case 'i':
 			use_iec = true;
 			break;
+		case 'x':
+			dl->hex = true;
+			break;
 		default:
 			pr_err("Unknown option.\n");
 			help();
diff --git a/man/man8/devlink.8 b/man/man8/devlink.8
index 840cf44cf97b..de53061bc880 100644
--- a/man/man8/devlink.8
+++ b/man/man8/devlink.8
@@ -63,6 +63,10 @@ Switches to the specified network namespace.
 .BR "\-i", " --iec"
 Print human readable rates in IEC units (e.g. 1Ki = 1024).
 
+.TP
+.BR "\-x", " --hex"
+Print dump numbers in hexadecimal format.
+
 .SS
 .I OBJECT
 
-- 
2.35.1


^ permalink raw reply related

* [PATCH net-next v9 2/2] net: ethernet: Add driver for Sunplus SP7021
From: Wells Lu @ 2022-04-25 10:30 UTC (permalink / raw)
  To: davem, kuba, robh+dt, netdev, devicetree, linux-kernel, p.zabel,
	pabeni, krzk+dt, roopa, andrew, edumazet
  Cc: wells.lu, Wells Lu
In-Reply-To: <1650882640-7106-1-git-send-email-wellslutw@gmail.com>

Add driver for Sunplus SP7021 SoC.

Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: Wells Lu <wellslutw@gmail.com>
---
Changes in v9
  - Addressed comments of Mr. Jakub Kicinski.
    - Fixed wrong coding which tries to unmap error map.
    - Use "skb_padto()" to pad a skb if its length is less than ETH_ZLEN.
    - Added a variable 'mapping' to store result of dma_map_single() so that we don't need
      clear skbinfo->mapping on failure.
      Moved some statements from before dma_map_single() to after success of dma_mapping_error() to simplify codes.
    - Added error-handling for clk_prepare_enable().
    - Use "eth_random_addr()" to generate locally administered (random) address on failure of reading it from nvmem.
    - Pass "comm" as private data in devm_request_irq() function.
    - Moved "register net-devices" to late spl2sw_probe() to avoid from potential crash.
    - Removed "rx_lock" for spl2sw_rx_poll(). It is not necessary.
    - Removed IP-checksum check when receiving a packet. It is not trusted by upper layer.
    - Modified error-handling of allocating skb when receiving a packet.
    - Take 'budget' into account in spl2sw_rx_poll() and spl2sw_tx_poll() functions.
  - Added handling 'EPROBE_DEFER' error returned by nvmem function, spl2sw_nvmem_get_mac_address().
    Adjusted delay time after reset_control_deassert(): udelay(1) --> usleep_range(1000, 2000)
    Hardware needs more time to stabilize in case of re-probing (after EPROBE_DEFER error).
  - Replaced platform driver data 'ndev' with 'comm'.

 MAINTAINERS                                   |   1 +
 drivers/net/ethernet/Kconfig                  |   1 +
 drivers/net/ethernet/Makefile                 |   1 +
 drivers/net/ethernet/sunplus/Kconfig          |  35 ++
 drivers/net/ethernet/sunplus/Makefile         |   6 +
 drivers/net/ethernet/sunplus/spl2sw_define.h  | 270 ++++++++
 drivers/net/ethernet/sunplus/spl2sw_desc.c    | 228 +++++++
 drivers/net/ethernet/sunplus/spl2sw_desc.h    |  19 +
 drivers/net/ethernet/sunplus/spl2sw_driver.c  | 577 ++++++++++++++++++
 drivers/net/ethernet/sunplus/spl2sw_driver.h  |  12 +
 drivers/net/ethernet/sunplus/spl2sw_int.c     | 260 ++++++++
 drivers/net/ethernet/sunplus/spl2sw_int.h     |  13 +
 drivers/net/ethernet/sunplus/spl2sw_mac.c     | 274 +++++++++
 drivers/net/ethernet/sunplus/spl2sw_mac.h     |  18 +
 drivers/net/ethernet/sunplus/spl2sw_mdio.c    | 126 ++++
 drivers/net/ethernet/sunplus/spl2sw_mdio.h    |  12 +
 drivers/net/ethernet/sunplus/spl2sw_phy.c     |  92 +++
 drivers/net/ethernet/sunplus/spl2sw_phy.h     |  12 +
 .../net/ethernet/sunplus/spl2sw_register.h    |  86 +++
 19 files changed, 2043 insertions(+)
 create mode 100644 drivers/net/ethernet/sunplus/Kconfig
 create mode 100644 drivers/net/ethernet/sunplus/Makefile
 create mode 100644 drivers/net/ethernet/sunplus/spl2sw_define.h
 create mode 100644 drivers/net/ethernet/sunplus/spl2sw_desc.c
 create mode 100644 drivers/net/ethernet/sunplus/spl2sw_desc.h
 create mode 100644 drivers/net/ethernet/sunplus/spl2sw_driver.c
 create mode 100644 drivers/net/ethernet/sunplus/spl2sw_driver.h
 create mode 100644 drivers/net/ethernet/sunplus/spl2sw_int.c
 create mode 100644 drivers/net/ethernet/sunplus/spl2sw_int.h
 create mode 100644 drivers/net/ethernet/sunplus/spl2sw_mac.c
 create mode 100644 drivers/net/ethernet/sunplus/spl2sw_mac.h
 create mode 100644 drivers/net/ethernet/sunplus/spl2sw_mdio.c
 create mode 100644 drivers/net/ethernet/sunplus/spl2sw_mdio.h
 create mode 100644 drivers/net/ethernet/sunplus/spl2sw_phy.c
 create mode 100644 drivers/net/ethernet/sunplus/spl2sw_phy.h
 create mode 100644 drivers/net/ethernet/sunplus/spl2sw_register.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 84b17ef41..51c478f36 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -18894,6 +18894,7 @@ L:	netdev@vger.kernel.org
 S:	Maintained
 W:	https://sunplus.atlassian.net/wiki/spaces/doc/overview
 F:	Documentation/devicetree/bindings/net/sunplus,sp7021-emac.yaml
+F:	drivers/net/ethernet/sunplus/
 
 SUNPLUS OCOTP DRIVER
 M:	Vincent Shih <vincent.sunplus@gmail.com>
diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig
index 827993022..955abbc54 100644
--- a/drivers/net/ethernet/Kconfig
+++ b/drivers/net/ethernet/Kconfig
@@ -179,6 +179,7 @@ source "drivers/net/ethernet/smsc/Kconfig"
 source "drivers/net/ethernet/socionext/Kconfig"
 source "drivers/net/ethernet/stmicro/Kconfig"
 source "drivers/net/ethernet/sun/Kconfig"
+source "drivers/net/ethernet/sunplus/Kconfig"
 source "drivers/net/ethernet/synopsys/Kconfig"
 source "drivers/net/ethernet/tehuti/Kconfig"
 source "drivers/net/ethernet/ti/Kconfig"
diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile
index 8ef43e0c3..9eb011699 100644
--- a/drivers/net/ethernet/Makefile
+++ b/drivers/net/ethernet/Makefile
@@ -90,6 +90,7 @@ obj-$(CONFIG_NET_VENDOR_SMSC) += smsc/
 obj-$(CONFIG_NET_VENDOR_SOCIONEXT) += socionext/
 obj-$(CONFIG_NET_VENDOR_STMICRO) += stmicro/
 obj-$(CONFIG_NET_VENDOR_SUN) += sun/
+obj-$(CONFIG_NET_VENDOR_SUNPLUS) += sunplus/
 obj-$(CONFIG_NET_VENDOR_TEHUTI) += tehuti/
 obj-$(CONFIG_NET_VENDOR_TI) += ti/
 obj-$(CONFIG_NET_VENDOR_TOSHIBA) += toshiba/
diff --git a/drivers/net/ethernet/sunplus/Kconfig b/drivers/net/ethernet/sunplus/Kconfig
new file mode 100644
index 000000000..d0144a2ab
--- /dev/null
+++ b/drivers/net/ethernet/sunplus/Kconfig
@@ -0,0 +1,35 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Sunplus network device configuration
+#
+
+config NET_VENDOR_SUNPLUS
+	bool "Sunplus devices"
+	default y
+	depends on ARCH_SUNPLUS || COMPILE_TEST
+	help
+	  If you have a network (Ethernet) card belonging to this
+	  class, say Y here.
+
+	  Note that the answer to this question doesn't directly
+	  affect the kernel: saying N will just cause the configurator
+	  to skip all the questions about Sunplus cards. If you say Y,
+	  you will be asked for your specific card in the following
+	  questions.
+
+if NET_VENDOR_SUNPLUS
+
+config SP7021_EMAC
+	tristate "Sunplus Dual 10M/100M Ethernet devices"
+	depends on SOC_SP7021 || COMPILE_TEST
+	select PHYLIB
+	select COMMON_CLK_SP7021
+	select RESET_SUNPLUS
+	select NVMEM_SUNPLUS_OCOTP
+	help
+	  If you have Sunplus dual 10M/100M Ethernet devices, say Y.
+	  The network device creates two net-device interfaces.
+	  To compile this driver as a module, choose M here. The
+	  module will be called sp7021_emac.
+
+endif # NET_VENDOR_SUNPLUS
diff --git a/drivers/net/ethernet/sunplus/Makefile b/drivers/net/ethernet/sunplus/Makefile
new file mode 100644
index 000000000..ef7d7d0a7
--- /dev/null
+++ b/drivers/net/ethernet/sunplus/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for the Sunplus network device drivers.
+#
+obj-$(CONFIG_SP7021_EMAC) += sp7021_emac.o
+sp7021_emac-objs := spl2sw_driver.o spl2sw_int.o spl2sw_desc.o spl2sw_mac.o spl2sw_mdio.o spl2sw_phy.o
diff --git a/drivers/net/ethernet/sunplus/spl2sw_define.h b/drivers/net/ethernet/sunplus/spl2sw_define.h
new file mode 100644
index 000000000..4beafc07f
--- /dev/null
+++ b/drivers/net/ethernet/sunplus/spl2sw_define.h
@@ -0,0 +1,270 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright Sunplus Technology Co., Ltd.
+ *       All rights reserved.
+ */
+
+#ifndef __SPL2SW_DEFINE_H__
+#define __SPL2SW_DEFINE_H__
+
+#define MAX_NETDEV_NUM			2	/* Maximum # of net-device */
+
+/* Interrupt status */
+#define MAC_INT_DAISY_MODE_CHG		BIT(31) /* Daisy Mode Change             */
+#define MAC_INT_IP_CHKSUM_ERR		BIT(23) /* IP Checksum Append Error      */
+#define MAC_INT_WDOG_TIMER1_EXP		BIT(22) /* Watchdog Timer1 Expired       */
+#define MAC_INT_WDOG_TIMER0_EXP		BIT(21) /* Watchdog Timer0 Expired       */
+#define MAC_INT_INTRUDER_ALERT		BIT(20) /* Atruder Alert                 */
+#define MAC_INT_PORT_ST_CHG		BIT(19) /* Port Status Change            */
+#define MAC_INT_BC_STORM		BIT(18) /* Broad Cast Storm              */
+#define MAC_INT_MUST_DROP_LAN		BIT(17) /* Global Queue Exhausted        */
+#define MAC_INT_GLOBAL_QUE_FULL		BIT(16) /* Global Queue Full             */
+#define MAC_INT_TX_SOC_PAUSE_ON		BIT(15) /* Soc Port TX Pause On          */
+#define MAC_INT_RX_SOC_QUE_FULL		BIT(14) /* Soc Port Out Queue Full       */
+#define MAC_INT_TX_LAN1_QUE_FULL	BIT(9)  /* Port 1 Out Queue Full         */
+#define MAC_INT_TX_LAN0_QUE_FULL	BIT(8)  /* Port 0 Out Queue Full         */
+#define MAC_INT_RX_L_DESCF		BIT(7)  /* Low Priority Descriptor Full  */
+#define MAC_INT_RX_H_DESCF		BIT(6)  /* High Priority Descriptor Full */
+#define MAC_INT_RX_DONE_L		BIT(5)  /* RX Low Priority Done          */
+#define MAC_INT_RX_DONE_H		BIT(4)  /* RX High Priority Done         */
+#define MAC_INT_TX_DONE_L		BIT(3)  /* TX Low Priority Done          */
+#define MAC_INT_TX_DONE_H		BIT(2)  /* TX High Priority Done         */
+#define MAC_INT_TX_DES_ERR		BIT(1)  /* TX Descriptor Error           */
+#define MAC_INT_RX_DES_ERR		BIT(0)  /* Rx Descriptor Error           */
+
+#define MAC_INT_RX			(MAC_INT_RX_DONE_H | MAC_INT_RX_DONE_L | \
+					MAC_INT_RX_DES_ERR)
+#define MAC_INT_TX			(MAC_INT_TX_DONE_L | MAC_INT_TX_DONE_H | \
+					MAC_INT_TX_DES_ERR)
+#define MAC_INT_MASK_DEF		(MAC_INT_DAISY_MODE_CHG | MAC_INT_IP_CHKSUM_ERR | \
+					MAC_INT_WDOG_TIMER1_EXP | MAC_INT_WDOG_TIMER0_EXP | \
+					MAC_INT_INTRUDER_ALERT | MAC_INT_PORT_ST_CHG | \
+					MAC_INT_BC_STORM | MAC_INT_MUST_DROP_LAN | \
+					MAC_INT_GLOBAL_QUE_FULL | MAC_INT_TX_SOC_PAUSE_ON | \
+					MAC_INT_RX_SOC_QUE_FULL | MAC_INT_TX_LAN1_QUE_FULL | \
+					MAC_INT_TX_LAN0_QUE_FULL | MAC_INT_RX_L_DESCF | \
+					MAC_INT_RX_H_DESCF)
+
+/* Address table search */
+#define MAC_ADDR_LOOKUP_IDLE		BIT(2)
+#define MAC_SEARCH_NEXT_ADDR		BIT(1)
+#define MAC_BEGIN_SEARCH_ADDR		BIT(0)
+
+/* Address table status */
+#define MAC_HASH_LOOKUP_ADDR		GENMASK(31, 22)
+#define MAC_R_PORT_MAP			GENMASK(13, 12)
+#define MAC_R_CPU_PORT			GENMASK(11, 10)
+#define MAC_R_VID			GENMASK(9, 7)
+#define MAC_R_AGE			GENMASK(6, 4)
+#define MAC_R_PROXY			BIT(3)
+#define MAC_R_MC_INGRESS		BIT(2)
+#define MAC_AT_TABLE_END		BIT(1)
+#define MAC_AT_DATA_READY		BIT(0)
+
+/* Wt mac ad0 */
+#define MAC_W_PORT_MAP			GENMASK(13, 12)
+#define MAC_W_LAN_PORT_1		BIT(13)
+#define MAC_W_LAN_PORT_0		BIT(12)
+#define MAC_W_CPU_PORT			GENMASK(11, 10)
+#define MAC_W_CPU_PORT_1		BIT(11)
+#define MAC_W_CPU_PORT_0		BIT(10)
+#define MAC_W_VID			GENMASK(9, 7)
+#define MAC_W_AGE			GENMASK(6, 4)
+#define MAC_W_PROXY			BIT(3)
+#define MAC_W_MC_INGRESS		BIT(2)
+#define MAC_W_MAC_DONE			BIT(1)
+#define MAC_W_MAC_CMD			BIT(0)
+
+/* W mac 15_0 bus */
+#define MAC_W_MAC_15_0			GENMASK(15, 0)
+
+/* W mac 47_16 bus */
+#define MAC_W_MAC_47_16			GENMASK(31, 0)
+
+/* PVID config 0 */
+#define MAC_P1_PVID			GENMASK(6, 4)
+#define MAC_P0_PVID			GENMASK(2, 0)
+
+/* VLAN member config 0 */
+#define MAC_VLAN_MEMSET_3		GENMASK(27, 24)
+#define MAC_VLAN_MEMSET_2		GENMASK(19, 16)
+#define MAC_VLAN_MEMSET_1		GENMASK(11, 8)
+#define MAC_VLAN_MEMSET_0		GENMASK(3, 0)
+
+/* VLAN member config 1 */
+#define MAC_VLAN_MEMSET_5		GENMASK(11, 8)
+#define MAC_VLAN_MEMSET_4		GENMASK(3, 0)
+
+/* Port ability */
+#define MAC_PORT_ABILITY_LINK_ST	GENMASK(25, 24)
+
+/* CPU control */
+#define MAC_EN_SOC1_AGING		BIT(15)
+#define MAC_EN_SOC0_AGING		BIT(14)
+#define MAC_DIS_LRN_SOC1		BIT(13)
+#define MAC_DIS_LRN_SOC0		BIT(12)
+#define MAC_EN_CRC_SOC1			BIT(9)
+#define MAC_EN_CRC_SOC0			BIT(8)
+#define MAC_DIS_SOC1_CPU		BIT(7)
+#define MAC_DIS_SOC0_CPU		BIT(6)
+#define MAC_DIS_BC2CPU_P1		BIT(5)
+#define MAC_DIS_BC2CPU_P0		BIT(4)
+#define MAC_DIS_MC2CPU			GENMASK(3, 2)
+#define MAC_DIS_MC2CPU_P1		BIT(3)
+#define MAC_DIS_MC2CPU_P0		BIT(2)
+#define MAC_DIS_UN2CPU			GENMASK(1, 0)
+
+/* Port control 0 */
+#define MAC_DIS_PORT			GENMASK(25, 24)
+#define MAC_DIS_PORT1			BIT(25)
+#define MAC_DIS_PORT0			BIT(24)
+#define MAC_DIS_RMC2CPU_P1		BIT(17)
+#define MAC_DIS_RMC2CPU_P0		BIT(16)
+#define MAC_EN_FLOW_CTL_P1		BIT(9)
+#define MAC_EN_FLOW_CTL_P0		BIT(8)
+#define MAC_EN_BACK_PRESS_P1		BIT(1)
+#define MAC_EN_BACK_PRESS_P0		BIT(0)
+
+/* Port control 1 */
+#define MAC_DIS_SA_LRN_P1		BIT(9)
+#define MAC_DIS_SA_LRN_P0		BIT(8)
+
+/* Port control 2 */
+#define MAC_EN_AGING_P1			BIT(9)
+#define MAC_EN_AGING_P0			BIT(8)
+
+/* Switch Global control */
+#define MAC_RMC_TB_FAULT_RULE		GENMASK(26, 25)
+#define MAC_LED_FLASH_TIME		GENMASK(24, 23)
+#define MAC_BC_STORM_PREV		GENMASK(5, 4)
+
+/* LED port 0 */
+#define MAC_LED_ACT_HI			BIT(28)
+
+/* PHY control register 0  */
+#define MAC_CPU_PHY_WT_DATA		GENMASK(31, 16)
+#define MAC_CPU_PHY_CMD			GENMASK(14, 13)
+#define MAC_CPU_PHY_REG_ADDR		GENMASK(12, 8)
+#define MAC_CPU_PHY_ADDR		GENMASK(4, 0)
+
+/* PHY control register 1 */
+#define MAC_CPU_PHY_RD_DATA		GENMASK(31, 16)
+#define MAC_PHY_RD_RDY			BIT(1)
+#define MAC_PHY_WT_DONE			BIT(0)
+
+/* MAC force mode */
+#define MAC_EXT_PHY1_ADDR		GENMASK(28, 24)
+#define MAC_EXT_PHY0_ADDR		GENMASK(20, 16)
+#define MAC_FORCE_RMII_LINK		GENMASK(9, 8)
+#define MAC_FORCE_RMII_EN_1		BIT(7)
+#define MAC_FORCE_RMII_EN_0		BIT(6)
+#define MAC_FORCE_RMII_FC		GENMASK(5, 4)
+#define MAC_FORCE_RMII_DPX		GENMASK(3, 2)
+#define MAC_FORCE_RMII_SPD		GENMASK(1, 0)
+
+/* CPU transmit trigger */
+#define MAC_TRIG_L_SOC0			BIT(1)
+#define MAC_TRIG_H_SOC0			BIT(0)
+
+/* Config descriptor queue */
+#define TX_DESC_NUM			16	/* # of descriptors in TX queue   */
+#define MAC_GUARD_DESC_NUM		2	/* # of descriptors of gap      0 */
+#define RX_QUEUE0_DESC_NUM		16	/* # of descriptors in RX queue 0 */
+#define RX_QUEUE1_DESC_NUM		16	/* # of descriptors in RX queue 1 */
+#define TX_DESC_QUEUE_NUM		1	/* # of TX queue                  */
+#define RX_DESC_QUEUE_NUM		2	/* # of RX queue                  */
+
+#define MAC_RX_LEN_MAX			2047	/* Size of RX buffer       */
+
+/* Tx descriptor */
+/* cmd1 */
+#define TXD_OWN				BIT(31)
+#define TXD_ERR_CODE			GENMASK(29, 26)
+#define TXD_SOP				BIT(25)		/* start of a packet */
+#define TXD_EOP				BIT(24)		/* end of a packet */
+#define TXD_VLAN			GENMASK(17, 12)
+#define TXD_PKT_LEN			GENMASK(10, 0)	/* packet length */
+/* cmd2 */
+#define TXD_EOR				BIT(31)		/* end of ring */
+#define TXD_BUF_LEN2			GENMASK(22, 12)
+#define TXD_BUF_LEN1			GENMASK(10, 0)
+
+/* Rx descriptor */
+/* cmd1 */
+#define RXD_OWN				BIT(31)
+#define RXD_ERR_CODE			GENMASK(29, 26)
+#define RXD_TCP_UDP_CHKSUM		BIT(23)
+#define RXD_PROXY			BIT(22)
+#define RXD_PROTOCOL			GENMASK(21, 20)
+#define RXD_VLAN_TAG			BIT(19)
+#define RXD_IP_CHKSUM			BIT(18)
+#define RXD_ROUTE_TYPE			GENMASK(17, 16)
+#define RXD_PKT_SP			GENMASK(14, 12)	/* packet source port */
+#define RXD_PKT_LEN			GENMASK(10, 0)	/* packet length */
+/* cmd2 */
+#define RXD_EOR				BIT(31)		/* end of ring */
+#define RXD_BUF_LEN2			GENMASK(22, 12)
+#define RXD_BUF_LEN1			GENMASK(10, 0)
+
+/* structure of descriptor */
+struct spl2sw_mac_desc {
+	u32 cmd1;
+	u32 cmd2;
+	u32 addr1;
+	u32 addr2;
+};
+
+struct spl2sw_skb_info {
+	struct sk_buff *skb;
+	u32 mapping;
+	u32 len;
+};
+
+struct spl2sw_common {
+	void __iomem *l2sw_reg_base;
+
+	struct platform_device *pdev;
+	struct reset_control *rstc;
+	struct clk *clk;
+	int irq;
+
+	void *desc_base;
+	dma_addr_t desc_dma;
+	s32 desc_size;
+	struct spl2sw_mac_desc *rx_desc[RX_DESC_QUEUE_NUM];
+	struct spl2sw_skb_info *rx_skb_info[RX_DESC_QUEUE_NUM];
+	u32 rx_pos[RX_DESC_QUEUE_NUM];
+	u32 rx_desc_num[RX_DESC_QUEUE_NUM];
+	u32 rx_desc_buff_size;
+
+	struct spl2sw_mac_desc *tx_desc;
+	struct spl2sw_skb_info tx_temp_skb_info[TX_DESC_NUM];
+	u32 tx_done_pos;
+	u32 tx_pos;
+	u32 tx_desc_full;
+
+	struct net_device *ndev[MAX_NETDEV_NUM];
+	struct mii_bus *mii_bus;
+
+	struct napi_struct rx_napi;
+	struct napi_struct tx_napi;
+
+	spinlock_t tx_lock;	/* spinlock for accessing tx buffer */
+	spinlock_t mdio_lock;	/* spinlock for mdio commands */
+
+	u8 enable;
+};
+
+struct spl2sw_mac {
+	struct net_device *ndev;
+	struct spl2sw_common *comm;
+
+	u8 mac_addr[ETH_ALEN];
+	phy_interface_t phy_mode;
+	struct device_node *phy_node;
+
+	u8 lan_port;
+	u8 to_vlan;
+	u8 vlan_id;
+};
+
+#endif
diff --git a/drivers/net/ethernet/sunplus/spl2sw_desc.c b/drivers/net/ethernet/sunplus/spl2sw_desc.c
new file mode 100644
index 000000000..3f0d9f78b
--- /dev/null
+++ b/drivers/net/ethernet/sunplus/spl2sw_desc.c
@@ -0,0 +1,228 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright Sunplus Technology Co., Ltd.
+ *       All rights reserved.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/netdevice.h>
+#include <linux/of_mdio.h>
+
+#include "spl2sw_define.h"
+#include "spl2sw_desc.h"
+
+void spl2sw_rx_descs_flush(struct spl2sw_common *comm)
+{
+	struct spl2sw_skb_info *rx_skbinfo;
+	struct spl2sw_mac_desc *rx_desc;
+	u32 i, j;
+
+	for (i = 0; i < RX_DESC_QUEUE_NUM; i++) {
+		rx_desc = comm->rx_desc[i];
+		rx_skbinfo = comm->rx_skb_info[i];
+		for (j = 0; j < comm->rx_desc_num[i]; j++) {
+			rx_desc[j].addr1 = rx_skbinfo[j].mapping;
+			rx_desc[j].cmd2 = (j == comm->rx_desc_num[i] - 1) ?
+					  RXD_EOR | comm->rx_desc_buff_size :
+					  comm->rx_desc_buff_size;
+			wmb();	/* Set RXD_OWN after other fields are ready. */
+			rx_desc[j].cmd1 = RXD_OWN;
+		}
+	}
+}
+
+void spl2sw_tx_descs_clean(struct spl2sw_common *comm)
+{
+	u32 i;
+
+	if (!comm->tx_desc)
+		return;
+
+	for (i = 0; i < TX_DESC_NUM; i++) {
+		comm->tx_desc[i].cmd1 = 0;
+		wmb();	/* Clear TXD_OWN and then set other fields. */
+		comm->tx_desc[i].cmd2 = 0;
+		comm->tx_desc[i].addr1 = 0;
+		comm->tx_desc[i].addr2 = 0;
+
+		if (comm->tx_temp_skb_info[i].mapping) {
+			dma_unmap_single(&comm->pdev->dev, comm->tx_temp_skb_info[i].mapping,
+					 comm->tx_temp_skb_info[i].skb->len, DMA_TO_DEVICE);
+			comm->tx_temp_skb_info[i].mapping = 0;
+		}
+
+		if (comm->tx_temp_skb_info[i].skb) {
+			dev_kfree_skb_any(comm->tx_temp_skb_info[i].skb);
+			comm->tx_temp_skb_info[i].skb = NULL;
+		}
+	}
+}
+
+void spl2sw_rx_descs_clean(struct spl2sw_common *comm)
+{
+	struct spl2sw_skb_info *rx_skbinfo;
+	struct spl2sw_mac_desc *rx_desc;
+	u32 i, j;
+
+	for (i = 0; i < RX_DESC_QUEUE_NUM; i++) {
+		if (!comm->rx_skb_info[i])
+			continue;
+
+		rx_desc = comm->rx_desc[i];
+		rx_skbinfo = comm->rx_skb_info[i];
+		for (j = 0; j < comm->rx_desc_num[i]; j++) {
+			rx_desc[j].cmd1 = 0;
+			wmb();	/* Clear RXD_OWN and then set other fields. */
+			rx_desc[j].cmd2 = 0;
+			rx_desc[j].addr1 = 0;
+
+			if (rx_skbinfo[j].skb) {
+				dma_unmap_single(&comm->pdev->dev, rx_skbinfo[j].mapping,
+						 comm->rx_desc_buff_size, DMA_FROM_DEVICE);
+				dev_kfree_skb_any(rx_skbinfo[j].skb);
+				rx_skbinfo[j].skb = NULL;
+				rx_skbinfo[j].mapping = 0;
+			}
+		}
+
+		kfree(rx_skbinfo);
+		comm->rx_skb_info[i] = NULL;
+	}
+}
+
+void spl2sw_descs_clean(struct spl2sw_common *comm)
+{
+	spl2sw_rx_descs_clean(comm);
+	spl2sw_tx_descs_clean(comm);
+}
+
+void spl2sw_descs_free(struct spl2sw_common *comm)
+{
+	u32 i;
+
+	spl2sw_descs_clean(comm);
+	comm->tx_desc = NULL;
+	for (i = 0; i < RX_DESC_QUEUE_NUM; i++)
+		comm->rx_desc[i] = NULL;
+
+	/*  Free descriptor area  */
+	if (comm->desc_base) {
+		dma_free_coherent(&comm->pdev->dev, comm->desc_size, comm->desc_base,
+				  comm->desc_dma);
+		comm->desc_base = NULL;
+		comm->desc_dma = 0;
+		comm->desc_size = 0;
+	}
+}
+
+void spl2sw_tx_descs_init(struct spl2sw_common *comm)
+{
+	memset(comm->tx_desc, '\0', sizeof(struct spl2sw_mac_desc) *
+	       (TX_DESC_NUM + MAC_GUARD_DESC_NUM));
+}
+
+int spl2sw_rx_descs_init(struct spl2sw_common *comm)
+{
+	struct spl2sw_skb_info *rx_skbinfo;
+	struct spl2sw_mac_desc *rx_desc;
+	struct sk_buff *skb;
+	u32 mapping;
+	u32 i, j;
+
+	for (i = 0; i < RX_DESC_QUEUE_NUM; i++) {
+		comm->rx_skb_info[i] = kcalloc(comm->rx_desc_num[i], sizeof(*rx_skbinfo),
+					       GFP_KERNEL | GFP_DMA);
+		if (!comm->rx_skb_info[i])
+			goto mem_alloc_fail;
+
+		rx_skbinfo = comm->rx_skb_info[i];
+		rx_desc = comm->rx_desc[i];
+		for (j = 0; j < comm->rx_desc_num[i]; j++) {
+			skb = netdev_alloc_skb(NULL, comm->rx_desc_buff_size);
+			if (!skb)
+				goto mem_alloc_fail;
+
+			rx_skbinfo[j].skb = skb;
+			mapping = dma_map_single(&comm->pdev->dev, skb->data,
+						 comm->rx_desc_buff_size,
+						 DMA_FROM_DEVICE);
+			if (dma_mapping_error(&comm->pdev->dev, mapping))
+				goto mem_alloc_fail;
+
+			rx_skbinfo[j].mapping = mapping;
+			rx_desc[j].addr1 = mapping;
+			rx_desc[j].addr2 = 0;
+			rx_desc[j].cmd2 = (j == comm->rx_desc_num[i] - 1) ?
+					  RXD_EOR | comm->rx_desc_buff_size :
+					  comm->rx_desc_buff_size;
+			wmb();	/* Set RXD_OWN after other fields are effective. */
+			rx_desc[j].cmd1 = RXD_OWN;
+		}
+	}
+
+	return 0;
+
+mem_alloc_fail:
+	spl2sw_rx_descs_clean(comm);
+	return -ENOMEM;
+}
+
+int spl2sw_descs_alloc(struct spl2sw_common *comm)
+{
+	s32 desc_size;
+	u32 i;
+
+	/* Alloc descriptor area  */
+	desc_size = (TX_DESC_NUM + MAC_GUARD_DESC_NUM) * sizeof(struct spl2sw_mac_desc);
+	for (i = 0; i < RX_DESC_QUEUE_NUM; i++)
+		desc_size += comm->rx_desc_num[i] * sizeof(struct spl2sw_mac_desc);
+
+	comm->desc_base = dma_alloc_coherent(&comm->pdev->dev, desc_size, &comm->desc_dma,
+					     GFP_KERNEL);
+	if (!comm->desc_base)
+		return -ENOMEM;
+
+	comm->desc_size = desc_size;
+
+	/* Setup Tx descriptor */
+	comm->tx_desc = comm->desc_base;
+
+	/* Setup Rx descriptor */
+	comm->rx_desc[0] = &comm->tx_desc[TX_DESC_NUM + MAC_GUARD_DESC_NUM];
+	for (i = 1; i < RX_DESC_QUEUE_NUM; i++)
+		comm->rx_desc[i] = comm->rx_desc[i - 1] + comm->rx_desc_num[i - 1];
+
+	return 0;
+}
+
+int spl2sw_descs_init(struct spl2sw_common *comm)
+{
+	u32 i, ret;
+
+	/* Initialize rx descriptor's data */
+	comm->rx_desc_num[0] = RX_QUEUE0_DESC_NUM;
+	comm->rx_desc_num[1] = RX_QUEUE1_DESC_NUM;
+
+	for (i = 0; i < RX_DESC_QUEUE_NUM; i++) {
+		comm->rx_desc[i] = NULL;
+		comm->rx_skb_info[i] = NULL;
+		comm->rx_pos[i] = 0;
+	}
+	comm->rx_desc_buff_size = MAC_RX_LEN_MAX;
+
+	/* Initialize tx descriptor's data */
+	comm->tx_done_pos = 0;
+	comm->tx_desc = NULL;
+	comm->tx_pos = 0;
+	comm->tx_desc_full = 0;
+	for (i = 0; i < TX_DESC_NUM; i++)
+		comm->tx_temp_skb_info[i].skb = NULL;
+
+	/* Allocate tx & rx descriptors. */
+	ret = spl2sw_descs_alloc(comm);
+	if (ret)
+		return ret;
+
+	spl2sw_tx_descs_init(comm);
+
+	return spl2sw_rx_descs_init(comm);
+}
diff --git a/drivers/net/ethernet/sunplus/spl2sw_desc.h b/drivers/net/ethernet/sunplus/spl2sw_desc.h
new file mode 100644
index 000000000..f04e2d85c
--- /dev/null
+++ b/drivers/net/ethernet/sunplus/spl2sw_desc.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright Sunplus Technology Co., Ltd.
+ *       All rights reserved.
+ */
+
+#ifndef __SPL2SW_DESC_H__
+#define __SPL2SW_DESC_H__
+
+void spl2sw_rx_descs_flush(struct spl2sw_common *comm);
+void spl2sw_tx_descs_clean(struct spl2sw_common *comm);
+void spl2sw_rx_descs_clean(struct spl2sw_common *comm);
+void spl2sw_descs_clean(struct spl2sw_common *comm);
+void spl2sw_descs_free(struct spl2sw_common *comm);
+void spl2sw_tx_descs_init(struct spl2sw_common *comm);
+int  spl2sw_rx_descs_init(struct spl2sw_common *comm);
+int  spl2sw_descs_alloc(struct spl2sw_common *comm);
+int  spl2sw_descs_init(struct spl2sw_common *comm);
+
+#endif
diff --git a/drivers/net/ethernet/sunplus/spl2sw_driver.c b/drivers/net/ethernet/sunplus/spl2sw_driver.c
new file mode 100644
index 000000000..2058a8d02
--- /dev/null
+++ b/drivers/net/ethernet/sunplus/spl2sw_driver.c
@@ -0,0 +1,577 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright Sunplus Technology Co., Ltd.
+ *       All rights reserved.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/etherdevice.h>
+#include <linux/netdevice.h>
+#include <linux/of_net.h>
+#include <linux/reset.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+
+#include "spl2sw_register.h"
+#include "spl2sw_define.h"
+#include "spl2sw_driver.h"
+#include "spl2sw_desc.h"
+#include "spl2sw_mdio.h"
+#include "spl2sw_phy.h"
+#include "spl2sw_int.h"
+#include "spl2sw_mac.h"
+
+/* net device operations */
+static int spl2sw_ethernet_open(struct net_device *ndev)
+{
+	struct spl2sw_mac *mac = netdev_priv(ndev);
+	struct spl2sw_common *comm = mac->comm;
+	u32 mask;
+
+	netdev_dbg(ndev, "Open port = %x\n", mac->lan_port);
+
+	comm->enable |= mac->lan_port;
+
+	spl2sw_mac_hw_start(comm);
+
+	/* Enable TX and RX interrupts */
+	mask = readl(comm->l2sw_reg_base + L2SW_SW_INT_MASK_0);
+	mask &= ~(MAC_INT_TX | MAC_INT_RX);
+	writel(mask, comm->l2sw_reg_base + L2SW_SW_INT_MASK_0);
+
+	phy_start(ndev->phydev);
+
+	netif_start_queue(ndev);
+
+	return 0;
+}
+
+static int spl2sw_ethernet_stop(struct net_device *ndev)
+{
+	struct spl2sw_mac *mac = netdev_priv(ndev);
+	struct spl2sw_common *comm = mac->comm;
+
+	netif_stop_queue(ndev);
+
+	comm->enable &= ~mac->lan_port;
+
+	phy_stop(ndev->phydev);
+
+	spl2sw_mac_hw_stop(comm);
+
+	return 0;
+}
+
+static int spl2sw_ethernet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+	struct spl2sw_mac *mac = netdev_priv(ndev);
+	struct spl2sw_common *comm = mac->comm;
+	struct spl2sw_skb_info *skbinfo;
+	struct spl2sw_mac_desc *txdesc;
+	unsigned long flags;
+	u32 mapping;
+	u32 tx_pos;
+	u32 cmd1;
+	u32 cmd2;
+
+	if (unlikely(comm->tx_desc_full == 1)) {
+		/* No TX descriptors left. Wait for tx interrupt. */
+		netdev_dbg(ndev, "TX descriptor queue full when xmit!\n");
+		return NETDEV_TX_BUSY;
+	}
+
+	/* If skb size is shorter than ETH_ZLEN (60), pad it with 0. */
+	if (unlikely(skb->len < ETH_ZLEN)) {
+		if (skb_padto(skb, ETH_ZLEN))
+			return NETDEV_TX_OK;
+
+		skb_put(skb, ETH_ZLEN - skb->len);
+	}
+
+	mapping = dma_map_single(&comm->pdev->dev, skb->data,
+				 skb->len, DMA_TO_DEVICE);
+	if (dma_mapping_error(&comm->pdev->dev, mapping)) {
+		ndev->stats.tx_errors++;
+		dev_kfree_skb(skb);
+		return NETDEV_TX_OK;
+	}
+
+	spin_lock_irqsave(&comm->tx_lock, flags);
+
+	tx_pos = comm->tx_pos;
+	txdesc = &comm->tx_desc[tx_pos];
+	skbinfo = &comm->tx_temp_skb_info[tx_pos];
+	skbinfo->mapping = mapping;
+	skbinfo->len = skb->len;
+	skbinfo->skb = skb;
+
+	/* Set up a TX descriptor */
+	cmd1 = TXD_OWN | TXD_SOP | TXD_EOP | (mac->to_vlan << 12) |
+	       (skb->len & TXD_PKT_LEN);
+	cmd2 = skb->len & TXD_BUF_LEN1;
+
+	if (tx_pos == (TX_DESC_NUM - 1))
+		cmd2 |= TXD_EOR;
+
+	txdesc->addr1 = skbinfo->mapping;
+	txdesc->cmd2 = cmd2;
+	wmb();	/* Set TXD_OWN after other fields are effective. */
+	txdesc->cmd1 = cmd1;
+
+	/* Move tx_pos to next position */
+	tx_pos = ((tx_pos + 1) == TX_DESC_NUM) ? 0 : tx_pos + 1;
+
+	if (unlikely(tx_pos == comm->tx_done_pos)) {
+		netif_stop_queue(ndev);
+		comm->tx_desc_full = 1;
+	}
+	comm->tx_pos = tx_pos;
+	wmb();		/* make sure settings are effective. */
+
+	/* Trigger mac to transmit */
+	writel(MAC_TRIG_L_SOC0, comm->l2sw_reg_base + L2SW_CPU_TX_TRIG);
+
+	spin_unlock_irqrestore(&comm->tx_lock, flags);
+	return NETDEV_TX_OK;
+}
+
+static void spl2sw_ethernet_set_rx_mode(struct net_device *ndev)
+{
+	struct spl2sw_mac *mac = netdev_priv(ndev);
+
+	spl2sw_mac_rx_mode_set(mac);
+}
+
+static int spl2sw_ethernet_set_mac_address(struct net_device *ndev, void *addr)
+{
+	struct spl2sw_mac *mac = netdev_priv(ndev);
+	int err;
+
+	err = eth_mac_addr(ndev, addr);
+	if (err)
+		return err;
+
+	/* Delete the old MAC address */
+	netdev_dbg(ndev, "Old Ethernet (MAC) address = %pM\n", mac->mac_addr);
+	if (is_valid_ether_addr(mac->mac_addr)) {
+		err = spl2sw_mac_addr_del(mac);
+		if (err)
+			return err;
+	}
+
+	/* Set the MAC address */
+	ether_addr_copy(mac->mac_addr, ndev->dev_addr);
+	return spl2sw_mac_addr_add(mac);
+}
+
+static void spl2sw_ethernet_tx_timeout(struct net_device *ndev, unsigned int txqueue)
+{
+	struct spl2sw_mac *mac = netdev_priv(ndev);
+	struct spl2sw_common *comm = mac->comm;
+	unsigned long flags;
+	int i;
+
+	netdev_err(ndev, "TX timed out!\n");
+	ndev->stats.tx_errors++;
+
+	spin_lock_irqsave(&comm->tx_lock, flags);
+
+	for (i = 0; i < MAX_NETDEV_NUM; i++)
+		if (comm->ndev[i])
+			netif_stop_queue(comm->ndev[i]);
+
+	spl2sw_mac_soft_reset(comm);
+
+	/* Accept TX packets again. */
+	for (i = 0; i < MAX_NETDEV_NUM; i++)
+		if (comm->ndev[i]) {
+			netif_trans_update(comm->ndev[i]);
+			netif_wake_queue(comm->ndev[i]);
+		}
+
+	spin_unlock_irqrestore(&comm->tx_lock, flags);
+}
+
+static const struct net_device_ops netdev_ops = {
+	.ndo_open = spl2sw_ethernet_open,
+	.ndo_stop = spl2sw_ethernet_stop,
+	.ndo_start_xmit = spl2sw_ethernet_start_xmit,
+	.ndo_set_rx_mode = spl2sw_ethernet_set_rx_mode,
+	.ndo_set_mac_address = spl2sw_ethernet_set_mac_address,
+	.ndo_do_ioctl = phy_do_ioctl,
+	.ndo_tx_timeout = spl2sw_ethernet_tx_timeout,
+};
+
+static void spl2sw_check_mac_vendor_id_and_convert(u8 *mac_addr)
+{
+	u8 tmp;
+
+	/* Byte order of MAC address of some samples are reversed.
+	 * Check vendor id and convert byte order if it is wrong.
+	 * OUI of Sunplus: fc:4b:bc
+	 */
+	if (mac_addr[5] == 0xfc && mac_addr[4] == 0x4b && mac_addr[3] == 0xbc &&
+	    (mac_addr[0] != 0xfc || mac_addr[1] != 0x4b || mac_addr[2] != 0xbc)) {
+		/* Swap mac_addr[0] and mac_addr[5] */
+		tmp = mac_addr[0];
+		mac_addr[0] = mac_addr[5];
+		mac_addr[5] = tmp;
+
+		/* Swap mac_addr[1] and mac_addr[4] */
+		tmp = mac_addr[1];
+		mac_addr[1] = mac_addr[4];
+		mac_addr[4] = tmp;
+
+		/* Swap mac_addr[2] and mac_addr[3] */
+		tmp = mac_addr[2];
+		mac_addr[2] = mac_addr[3];
+		mac_addr[3] = tmp;
+	}
+}
+
+static int spl2sw_nvmem_get_mac_address(struct device *dev, struct device_node *np,
+					void *addrbuf)
+{
+	struct nvmem_cell *cell;
+	ssize_t len;
+	u8 *mac;
+
+	/* Get nvmem cell of mac-address from dts. */
+	cell = of_nvmem_cell_get(np, "mac-address");
+	if (IS_ERR(cell))
+		return PTR_ERR(cell);
+
+	/* Read mac address from nvmem cell. */
+	mac = nvmem_cell_read(cell, &len);
+	nvmem_cell_put(cell);
+	if (IS_ERR(mac))
+		return PTR_ERR(mac);
+
+	if (len != ETH_ALEN) {
+		kfree(mac);
+		dev_info(dev, "Invalid length of mac address in nvmem!\n");
+		return -EINVAL;
+	}
+
+	/* Byte order of some samples are reversed.
+	 * Convert byte order here.
+	 */
+	spl2sw_check_mac_vendor_id_and_convert(mac);
+
+	/* Check if mac address is valid */
+	if (!is_valid_ether_addr(mac)) {
+		kfree(mac);
+		dev_info(dev, "Invalid mac address in nvmem (%pM)!\n", mac);
+		return -EINVAL;
+	}
+
+	ether_addr_copy(addrbuf, mac);
+	kfree(mac);
+	return 0;
+}
+
+static u32 spl2sw_init_netdev(struct platform_device *pdev, u8 *mac_addr,
+			      struct net_device **r_ndev)
+{
+	struct net_device *ndev;
+	struct spl2sw_mac *mac;
+	int ret;
+
+	/* Allocate the devices, and also allocate spl2sw_mac,
+	 * we can get it by netdev_priv().
+	 */
+	ndev = devm_alloc_etherdev(&pdev->dev, sizeof(*mac));
+	if (!ndev) {
+		*r_ndev = NULL;
+		return -ENOMEM;
+	}
+	SET_NETDEV_DEV(ndev, &pdev->dev);
+	ndev->netdev_ops = &netdev_ops;
+	mac = netdev_priv(ndev);
+	mac->ndev = ndev;
+	ether_addr_copy(mac->mac_addr, mac_addr);
+
+	eth_hw_addr_set(ndev, mac_addr);
+	dev_info(&pdev->dev, "Ethernet (MAC) address = %pM\n", mac_addr);
+
+	ret = register_netdev(ndev);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to register net device \"%s\"!\n",
+			ndev->name);
+		free_netdev(ndev);
+		*r_ndev = NULL;
+		return ret;
+	}
+	netdev_dbg(ndev, "Registered net device \"%s\" successfully.\n", ndev->name);
+
+	*r_ndev = ndev;
+	return 0;
+}
+
+static struct device_node *spl2sw_get_eth_child_node(struct device_node *ether_np, int id)
+{
+	struct device_node *port_np;
+	int port_id;
+
+	for_each_child_of_node(ether_np, port_np) {
+		/* It is not a 'port' node, continue. */
+		if (strcmp(port_np->name, "port"))
+			continue;
+
+		if (of_property_read_u32(port_np, "reg", &port_id) < 0)
+			continue;
+
+		if (port_id == id)
+			return port_np;
+	}
+
+	/* Not found! */
+	return NULL;
+}
+
+static int spl2sw_probe(struct platform_device *pdev)
+{
+	struct device_node *eth_ports_np;
+	struct device_node *port_np;
+	struct spl2sw_common *comm;
+	struct device_node *phy_np;
+	phy_interface_t phy_mode;
+	struct net_device *ndev;
+	struct spl2sw_mac *mac;
+	u8 mac_addr[ETH_ALEN];
+	int irq, i, ret;
+
+	if (platform_get_drvdata(pdev))
+		return -ENODEV;
+
+	/* Allocate memory for 'spl2sw_common' area. */
+	comm = devm_kzalloc(&pdev->dev, sizeof(*comm), GFP_KERNEL);
+	if (!comm)
+		return -ENOMEM;
+
+	comm->pdev = pdev;
+	platform_set_drvdata(pdev, comm);
+
+	spin_lock_init(&comm->tx_lock);
+	spin_lock_init(&comm->mdio_lock);
+
+	/* Get memory resource 0 from dts. */
+	comm->l2sw_reg_base = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(comm->l2sw_reg_base))
+		return PTR_ERR(comm->l2sw_reg_base);
+
+	/* Get irq resource from dts. */
+	ret = platform_get_irq(pdev, 0);
+	if (ret < 0)
+		return ret;
+	irq = ret;
+
+	/* Get clock controller. */
+	comm->clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(comm->clk)) {
+		dev_err_probe(&pdev->dev, PTR_ERR(comm->clk),
+			      "Failed to retrieve clock controller!\n");
+		return PTR_ERR(comm->clk);
+	}
+
+	/* Get reset controller. */
+	comm->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL);
+	if (IS_ERR(comm->rstc)) {
+		dev_err_probe(&pdev->dev, PTR_ERR(comm->rstc),
+			      "Failed to retrieve reset controller!\n");
+		return PTR_ERR(comm->rstc);
+	}
+
+	/* Enable clock. */
+	ret = clk_prepare_enable(comm->clk);
+	if (ret)
+		return ret;
+	udelay(1);
+
+	/* Reset MAC */
+	reset_control_assert(comm->rstc);
+	udelay(1);
+	reset_control_deassert(comm->rstc);
+	usleep_range(1000, 2000);
+
+	/* Request irq. */
+	ret = devm_request_irq(&pdev->dev, irq, spl2sw_ethernet_interrupt, 0,
+			       dev_name(&pdev->dev), comm);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to request irq #%d!\n", irq);
+		goto out_clk_disable;
+	}
+
+	/* Initialize TX and RX descriptors. */
+	ret = spl2sw_descs_init(comm);
+	if (ret) {
+		dev_err(&pdev->dev, "Fail to initialize mac descriptors!\n");
+		spl2sw_descs_free(comm);
+		goto out_clk_disable;
+	}
+
+	/* Initialize MAC. */
+	spl2sw_mac_init(comm);
+
+	/* Initialize mdio bus */
+	ret = spl2sw_mdio_init(comm);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to initialize mdio bus!\n");
+		goto out_clk_disable;
+	}
+
+	/* Get child node ethernet-ports. */
+	eth_ports_np = of_get_child_by_name(pdev->dev.of_node, "ethernet-ports");
+	if (!eth_ports_np) {
+		dev_err(&pdev->dev, "No ethernet-ports child node found!\n");
+		ret = -ENODEV;
+		goto out_free_mdio;
+	}
+
+	for (i = 0; i < MAX_NETDEV_NUM; i++) {
+		/* Get port@i of node ethernet-ports. */
+		port_np = spl2sw_get_eth_child_node(eth_ports_np, i);
+		if (!port_np)
+			continue;
+
+		/* Get phy-mode. */
+		if (of_get_phy_mode(port_np, &phy_mode)) {
+			dev_err(&pdev->dev, "Failed to get phy-mode property of port@%d!\n",
+				i);
+			continue;
+		}
+
+		/* Get phy-handle. */
+		phy_np = of_parse_phandle(port_np, "phy-handle", 0);
+		if (!phy_np) {
+			dev_err(&pdev->dev, "Failed to get phy-handle property of port@%d!\n",
+				i);
+			continue;
+		}
+
+		/* Get mac-address from nvmem. */
+		ret = spl2sw_nvmem_get_mac_address(&pdev->dev, port_np, mac_addr);
+		if (ret == -EPROBE_DEFER) {
+			goto out_unregister_dev;
+		} else if (ret) {
+			dev_info(&pdev->dev, "Generate a random mac address!\n");
+			eth_random_addr(mac_addr);
+		}
+
+		/* Initialize the net device. */
+		ret = spl2sw_init_netdev(pdev, mac_addr, &ndev);
+		if (ret)
+			goto out_unregister_dev;
+
+		ndev->irq = irq;
+		comm->ndev[i] = ndev;
+		mac = netdev_priv(ndev);
+		mac->phy_node = phy_np;
+		mac->phy_mode = phy_mode;
+		mac->comm = comm;
+
+		mac->lan_port = 0x1 << i;	/* forward to port i */
+		mac->to_vlan = 0x1 << i;	/* vlan group: i     */
+		mac->vlan_id = i;		/* vlan group: i     */
+
+		/* Set MAC address */
+		ret = spl2sw_mac_addr_add(mac);
+		if (ret)
+			goto out_unregister_dev;
+
+		spl2sw_mac_rx_mode_set(mac);
+	}
+
+	/* Find first valid net device. */
+	for (i = 0; i < MAX_NETDEV_NUM; i++) {
+		if (comm->ndev[i])
+			break;
+	}
+	if (i >= MAX_NETDEV_NUM) {
+		dev_err(&pdev->dev, "No valid ethernet port!\n");
+		ret = -ENODEV;
+		goto out_free_mdio;
+	}
+
+	/* Save first valid net device */
+	ndev = comm->ndev[i];
+
+	ret = spl2sw_phy_connect(comm);
+	if (ret) {
+		netdev_err(ndev, "Failed to connect phy!\n");
+		goto out_unregister_dev;
+	}
+
+	/* Add and enable napi. */
+	netif_napi_add(ndev, &comm->rx_napi, spl2sw_rx_poll, SPL2SW_RX_NAPI_WEIGHT);
+	napi_enable(&comm->rx_napi);
+	netif_napi_add(ndev, &comm->tx_napi, spl2sw_tx_poll, SPL2SW_TX_NAPI_WEIGHT);
+	napi_enable(&comm->tx_napi);
+	return 0;
+
+out_unregister_dev:
+	for (i = 0; i < MAX_NETDEV_NUM; i++)
+		if (comm->ndev[i])
+			unregister_netdev(comm->ndev[i]);
+
+out_free_mdio:
+	spl2sw_mdio_remove(comm);
+
+out_clk_disable:
+	clk_disable_unprepare(comm->clk);
+	return ret;
+}
+
+static int spl2sw_remove(struct platform_device *pdev)
+{
+	struct spl2sw_common *comm;
+	int i;
+
+	comm = platform_get_drvdata(pdev);
+
+	spl2sw_phy_remove(comm);
+
+	/* Unregister and free net device. */
+	for (i = 0; i < MAX_NETDEV_NUM; i++)
+		if (comm->ndev[i])
+			unregister_netdev(comm->ndev[i]);
+
+	comm->enable = 0;
+	spl2sw_mac_hw_stop(comm);
+	spl2sw_descs_free(comm);
+
+	/* Disable and delete napi. */
+	napi_disable(&comm->rx_napi);
+	netif_napi_del(&comm->rx_napi);
+	napi_disable(&comm->tx_napi);
+	netif_napi_del(&comm->tx_napi);
+
+	spl2sw_mdio_remove(comm);
+
+	clk_disable_unprepare(comm->clk);
+
+	return 0;
+}
+
+static const struct of_device_id spl2sw_of_match[] = {
+	{.compatible = "sunplus,sp7021-emac"},
+	{ /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, spl2sw_of_match);
+
+static struct platform_driver spl2sw_driver = {
+	.probe = spl2sw_probe,
+	.remove = spl2sw_remove,
+	.driver = {
+		.name = "sp7021_emac",
+		.owner = THIS_MODULE,
+		.of_match_table = spl2sw_of_match,
+	},
+};
+
+module_platform_driver(spl2sw_driver);
+
+MODULE_AUTHOR("Wells Lu <wellslutw@gmail.com>");
+MODULE_DESCRIPTION("Sunplus Dual 10M/100M Ethernet driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/sunplus/spl2sw_driver.h b/drivers/net/ethernet/sunplus/spl2sw_driver.h
new file mode 100644
index 000000000..5f177b3af
--- /dev/null
+++ b/drivers/net/ethernet/sunplus/spl2sw_driver.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright Sunplus Technology Co., Ltd.
+ *       All rights reserved.
+ */
+
+#ifndef __SPL2SW_DRIVER_H__
+#define __SPL2SW_DRIVER_H__
+
+#define SPL2SW_RX_NAPI_WEIGHT	16
+#define SPL2SW_TX_NAPI_WEIGHT	16
+
+#endif
diff --git a/drivers/net/ethernet/sunplus/spl2sw_int.c b/drivers/net/ethernet/sunplus/spl2sw_int.c
new file mode 100644
index 000000000..69d3c7528
--- /dev/null
+++ b/drivers/net/ethernet/sunplus/spl2sw_int.c
@@ -0,0 +1,260 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright Sunplus Technology Co., Ltd.
+ *       All rights reserved.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/etherdevice.h>
+#include <linux/netdevice.h>
+#include <linux/bitfield.h>
+#include <linux/of_mdio.h>
+
+#include "spl2sw_register.h"
+#include "spl2sw_define.h"
+#include "spl2sw_int.h"
+
+int spl2sw_rx_poll(struct napi_struct *napi, int budget)
+{
+	struct spl2sw_common *comm = container_of(napi, struct spl2sw_common, rx_napi);
+	struct spl2sw_mac_desc *desc, *h_desc;
+	struct net_device_stats *stats;
+	struct sk_buff *skb, *new_skb;
+	struct spl2sw_skb_info *sinfo;
+	int budget_left = budget;
+	u32 rx_pos, pkg_len;
+	u32 num, rx_count;
+	s32 queue;
+	u32 mask;
+	int port;
+	u32 cmd;
+
+	/* Process high-priority queue and then low-priority queue. */
+	for (queue = 0; queue < RX_DESC_QUEUE_NUM; queue++) {
+		rx_pos = comm->rx_pos[queue];
+		rx_count = comm->rx_desc_num[queue];
+
+		for (num = 0; num < rx_count && budget_left; num++) {
+			sinfo = comm->rx_skb_info[queue] + rx_pos;
+			desc = comm->rx_desc[queue] + rx_pos;
+			cmd = desc->cmd1;
+
+			if (cmd & RXD_OWN)
+				break;
+
+			port = FIELD_GET(RXD_PKT_SP, cmd);
+			if (port < MAX_NETDEV_NUM && comm->ndev[port])
+				stats = &comm->ndev[port]->stats;
+			else
+				goto spl2sw_rx_poll_rec_err;
+
+			pkg_len = FIELD_GET(RXD_PKT_LEN, cmd);
+			if (unlikely((cmd & RXD_ERR_CODE) || pkg_len < ETH_ZLEN + 4)) {
+				stats->rx_length_errors++;
+				stats->rx_dropped++;
+				goto spl2sw_rx_poll_rec_err;
+			}
+
+			dma_unmap_single(&comm->pdev->dev, sinfo->mapping,
+					 comm->rx_desc_buff_size, DMA_FROM_DEVICE);
+
+			skb = sinfo->skb;
+			skb_put(skb, pkg_len - 4); /* Minus FCS */
+			skb->ip_summed = CHECKSUM_NONE;
+			skb->protocol = eth_type_trans(skb, comm->ndev[port]);
+			netif_receive_skb(skb);
+
+			stats->rx_packets++;
+			stats->rx_bytes += skb->len;
+
+			/* Allocate a new skb for receiving. */
+			new_skb = netdev_alloc_skb(NULL, comm->rx_desc_buff_size);
+			if (unlikely(!new_skb)) {
+				desc->cmd2 = (rx_pos == comm->rx_desc_num[queue] - 1) ?
+					     RXD_EOR : 0;
+				sinfo->skb = NULL;
+				sinfo->mapping = 0;
+				desc->addr1 = 0;
+				goto spl2sw_rx_poll_alloc_err;
+			}
+
+			sinfo->mapping = dma_map_single(&comm->pdev->dev, new_skb->data,
+							comm->rx_desc_buff_size,
+							DMA_FROM_DEVICE);
+			if (dma_mapping_error(&comm->pdev->dev, sinfo->mapping)) {
+				dev_kfree_skb_irq(new_skb);
+				desc->cmd2 = (rx_pos == comm->rx_desc_num[queue] - 1) ?
+					     RXD_EOR : 0;
+				sinfo->skb = NULL;
+				sinfo->mapping = 0;
+				desc->addr1 = 0;
+				goto spl2sw_rx_poll_alloc_err;
+			}
+
+			sinfo->skb = new_skb;
+			desc->addr1 = sinfo->mapping;
+
+spl2sw_rx_poll_rec_err:
+			desc->cmd2 = (rx_pos == comm->rx_desc_num[queue] - 1) ?
+				     RXD_EOR | comm->rx_desc_buff_size :
+				     comm->rx_desc_buff_size;
+
+			wmb();	/* Set RXD_OWN after other fields are effective. */
+			desc->cmd1 = RXD_OWN;
+
+spl2sw_rx_poll_alloc_err:
+			/* Move rx_pos to next position */
+			rx_pos = ((rx_pos + 1) == comm->rx_desc_num[queue]) ? 0 : rx_pos + 1;
+
+			budget_left--;
+
+			/* If there are packets in high-priority queue,
+			 * stop processing low-priority queue.
+			 */
+			if (queue == 1 && !(h_desc->cmd1 & RXD_OWN))
+				break;
+		}
+
+		comm->rx_pos[queue] = rx_pos;
+
+		/* Save pointer to last rx descriptor of high-priority queue. */
+		if (queue == 0)
+			h_desc = comm->rx_desc[queue] + rx_pos;
+	}
+
+	wmb();	/* make sure settings are effective. */
+	mask = readl(comm->l2sw_reg_base + L2SW_SW_INT_MASK_0);
+	mask &= ~MAC_INT_RX;
+	writel(mask, comm->l2sw_reg_base + L2SW_SW_INT_MASK_0);
+
+	napi_complete(napi);
+	return 0;
+}
+
+int spl2sw_tx_poll(struct napi_struct *napi, int budget)
+{
+	struct spl2sw_common *comm = container_of(napi, struct spl2sw_common, tx_napi);
+	struct spl2sw_skb_info *skbinfo;
+	struct net_device_stats *stats;
+	int budget_left = budget;
+	u32 tx_done_pos;
+	u32 mask;
+	u32 cmd;
+	int i;
+
+	spin_lock(&comm->tx_lock);
+
+	tx_done_pos = comm->tx_done_pos;
+	while (((tx_done_pos != comm->tx_pos) || (comm->tx_desc_full == 1)) && budget_left) {
+		cmd = comm->tx_desc[tx_done_pos].cmd1;
+		if (cmd & TXD_OWN)
+			break;
+
+		skbinfo = &comm->tx_temp_skb_info[tx_done_pos];
+		if (unlikely(!skbinfo->skb))
+			goto spl2sw_tx_poll_next;
+
+		i = ffs(FIELD_GET(TXD_VLAN, cmd)) - 1;
+		if (i < MAX_NETDEV_NUM && comm->ndev[i])
+			stats = &comm->ndev[i]->stats;
+		else
+			goto spl2sw_tx_poll_unmap;
+
+		if (unlikely(cmd & (TXD_ERR_CODE))) {
+			stats->tx_errors++;
+		} else {
+			stats->tx_packets++;
+			stats->tx_bytes += skbinfo->len;
+		}
+
+spl2sw_tx_poll_unmap:
+		dma_unmap_single(&comm->pdev->dev, skbinfo->mapping, skbinfo->len,
+				 DMA_TO_DEVICE);
+		skbinfo->mapping = 0;
+		dev_kfree_skb_irq(skbinfo->skb);
+		skbinfo->skb = NULL;
+
+spl2sw_tx_poll_next:
+		/* Move tx_done_pos to next position */
+		tx_done_pos = ((tx_done_pos + 1) == TX_DESC_NUM) ? 0 : tx_done_pos + 1;
+
+		if (comm->tx_desc_full == 1)
+			comm->tx_desc_full = 0;
+
+		budget_left--;
+	}
+
+	comm->tx_done_pos = tx_done_pos;
+	if (!comm->tx_desc_full)
+		for (i = 0; i < MAX_NETDEV_NUM; i++)
+			if (comm->ndev[i])
+				if (netif_queue_stopped(comm->ndev[i]))
+					netif_wake_queue(comm->ndev[i]);
+
+	spin_unlock(&comm->tx_lock);
+
+	wmb();			/* make sure settings are effective. */
+	mask = readl(comm->l2sw_reg_base + L2SW_SW_INT_MASK_0);
+	mask &= ~MAC_INT_TX;
+	writel(mask, comm->l2sw_reg_base + L2SW_SW_INT_MASK_0);
+
+	napi_complete(napi);
+	return 0;
+}
+
+irqreturn_t spl2sw_ethernet_interrupt(int irq, void *dev_id)
+{
+	struct spl2sw_common *comm = (struct spl2sw_common *)dev_id;
+	u32 status;
+	u32 mask;
+	int i;
+
+	status = readl(comm->l2sw_reg_base + L2SW_SW_INT_STATUS_0);
+	if (unlikely(!status)) {
+		dev_dbg(&comm->pdev->dev, "Interrput status is null!\n");
+		goto spl2sw_ethernet_int_out;
+	}
+	writel(status, comm->l2sw_reg_base + L2SW_SW_INT_STATUS_0);
+
+	if (status & MAC_INT_RX) {
+		/* Disable RX interrupts. */
+		mask = readl(comm->l2sw_reg_base + L2SW_SW_INT_MASK_0);
+		mask |= MAC_INT_RX;
+		writel(mask, comm->l2sw_reg_base + L2SW_SW_INT_MASK_0);
+
+		if (unlikely(status & MAC_INT_RX_DES_ERR)) {
+			for (i = 0; i < MAX_NETDEV_NUM; i++)
+				if (comm->ndev[i]) {
+					comm->ndev[i]->stats.rx_fifo_errors++;
+					break;
+				}
+			dev_dbg(&comm->pdev->dev, "Illegal RX Descriptor!\n");
+		}
+
+		napi_schedule(&comm->rx_napi);
+	}
+
+	if (status & MAC_INT_TX) {
+		/* Disable TX interrupts. */
+		mask = readl(comm->l2sw_reg_base + L2SW_SW_INT_MASK_0);
+		mask |= MAC_INT_TX;
+		writel(mask, comm->l2sw_reg_base + L2SW_SW_INT_MASK_0);
+
+		if (unlikely(status & MAC_INT_TX_DES_ERR)) {
+			for (i = 0; i < MAX_NETDEV_NUM; i++)
+				if (comm->ndev[i]) {
+					comm->ndev[i]->stats.tx_fifo_errors++;
+					break;
+				}
+			dev_dbg(&comm->pdev->dev, "Illegal TX Descriptor Error\n");
+
+			mask = readl(comm->l2sw_reg_base + L2SW_SW_INT_MASK_0);
+			mask &= ~MAC_INT_TX;
+			writel(mask, comm->l2sw_reg_base + L2SW_SW_INT_MASK_0);
+		} else {
+			napi_schedule(&comm->tx_napi);
+		}
+	}
+
+spl2sw_ethernet_int_out:
+	return IRQ_HANDLED;
+}
diff --git a/drivers/net/ethernet/sunplus/spl2sw_int.h b/drivers/net/ethernet/sunplus/spl2sw_int.h
new file mode 100644
index 000000000..64f6f2572
--- /dev/null
+++ b/drivers/net/ethernet/sunplus/spl2sw_int.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright Sunplus Technology Co., Ltd.
+ *       All rights reserved.
+ */
+
+#ifndef __SPL2SW_INT_H__
+#define __SPL2SW_INT_H__
+
+int spl2sw_rx_poll(struct napi_struct *napi, int budget);
+int spl2sw_tx_poll(struct napi_struct *napi, int budget);
+irqreturn_t spl2sw_ethernet_interrupt(int irq, void *dev_id);
+
+#endif
diff --git a/drivers/net/ethernet/sunplus/spl2sw_mac.c b/drivers/net/ethernet/sunplus/spl2sw_mac.c
new file mode 100644
index 000000000..57e431dfc
--- /dev/null
+++ b/drivers/net/ethernet/sunplus/spl2sw_mac.c
@@ -0,0 +1,274 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright Sunplus Technology Co., Ltd.
+ *       All rights reserved.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/netdevice.h>
+#include <linux/bitfield.h>
+#include <linux/of_mdio.h>
+
+#include "spl2sw_register.h"
+#include "spl2sw_define.h"
+#include "spl2sw_desc.h"
+#include "spl2sw_mac.h"
+
+void spl2sw_mac_hw_stop(struct spl2sw_common *comm)
+{
+	u32 reg;
+
+	if (comm->enable == 0) {
+		/* Mask and clear all interrupts. */
+		writel(0xffffffff, comm->l2sw_reg_base + L2SW_SW_INT_MASK_0);
+		writel(0xffffffff, comm->l2sw_reg_base + L2SW_SW_INT_STATUS_0);
+
+		/* Disable cpu 0 and cpu 1. */
+		reg = readl(comm->l2sw_reg_base + L2SW_CPU_CNTL);
+		reg |= MAC_DIS_SOC1_CPU | MAC_DIS_SOC0_CPU;
+		writel(reg, comm->l2sw_reg_base + L2SW_CPU_CNTL);
+	}
+
+	/* Disable LAN ports. */
+	reg = readl(comm->l2sw_reg_base + L2SW_PORT_CNTL0);
+	reg |= FIELD_PREP(MAC_DIS_PORT, ~comm->enable);
+	writel(reg, comm->l2sw_reg_base + L2SW_PORT_CNTL0);
+}
+
+void spl2sw_mac_hw_start(struct spl2sw_common *comm)
+{
+	u32 reg;
+
+	/* Enable cpu port 0 (6) & CRC padding (8) */
+	reg = readl(comm->l2sw_reg_base + L2SW_CPU_CNTL);
+	reg &= ~MAC_DIS_SOC0_CPU;
+	reg |= MAC_EN_CRC_SOC0;
+	writel(reg, comm->l2sw_reg_base + L2SW_CPU_CNTL);
+
+	/* Enable port 0 & port 1 */
+	reg = readl(comm->l2sw_reg_base + L2SW_PORT_CNTL0);
+	reg &= FIELD_PREP(MAC_DIS_PORT, ~comm->enable) | ~MAC_DIS_PORT;
+	writel(reg, comm->l2sw_reg_base + L2SW_PORT_CNTL0);
+}
+
+int spl2sw_mac_addr_add(struct spl2sw_mac *mac)
+{
+	struct spl2sw_common *comm = mac->comm;
+	u32 reg;
+	int ret;
+
+	/* Write 6-octet MAC address. */
+	writel((mac->mac_addr[0] << 0) + (mac->mac_addr[1] << 8),
+	       comm->l2sw_reg_base + L2SW_W_MAC_15_0);
+	writel((mac->mac_addr[2] << 0) + (mac->mac_addr[3] << 8) +
+	       (mac->mac_addr[4] << 16) + (mac->mac_addr[5] << 24),
+	       comm->l2sw_reg_base + L2SW_W_MAC_47_16);
+
+	/* Set learn port = cpu_port, aging = 1 */
+	reg = MAC_W_CPU_PORT_0 | FIELD_PREP(MAC_W_VID, mac->vlan_id) |
+	      FIELD_PREP(MAC_W_AGE, 1) | MAC_W_MAC_CMD;
+	writel(reg, comm->l2sw_reg_base + L2SW_WT_MAC_AD0);
+
+	/* Wait for completing. */
+	ret = read_poll_timeout(readl, reg, reg & MAC_W_MAC_DONE, 1, 200, true,
+				comm->l2sw_reg_base + L2SW_WT_MAC_AD0);
+	if (ret) {
+		netdev_err(mac->ndev, "Failed to add address to table!\n");
+		return ret;
+	}
+
+	netdev_dbg(mac->ndev, "mac_ad0 = %08x, mac_ad = %08x%04x\n",
+		   readl(comm->l2sw_reg_base + L2SW_WT_MAC_AD0),
+		   (u32)FIELD_GET(MAC_W_MAC_47_16,
+		   readl(comm->l2sw_reg_base + L2SW_W_MAC_47_16)),
+		   (u32)FIELD_GET(MAC_W_MAC_15_0,
+		   readl(comm->l2sw_reg_base + L2SW_W_MAC_15_0)));
+	return 0;
+}
+
+int spl2sw_mac_addr_del(struct spl2sw_mac *mac)
+{
+	struct spl2sw_common *comm = mac->comm;
+	u32 reg;
+	int ret;
+
+	/* Write 6-octet MAC address. */
+	writel((mac->mac_addr[0] << 0) + (mac->mac_addr[1] << 8),
+	       comm->l2sw_reg_base + L2SW_W_MAC_15_0);
+	writel((mac->mac_addr[2] << 0) + (mac->mac_addr[3] << 8) +
+	       (mac->mac_addr[4] << 16) + (mac->mac_addr[5] << 24),
+	       comm->l2sw_reg_base + L2SW_W_MAC_47_16);
+
+	/* Set learn port = lan_port0 and aging = 0
+	 * to wipe (age) out the entry.
+	 */
+	reg = MAC_W_LAN_PORT_0 | FIELD_PREP(MAC_W_VID, mac->vlan_id) | MAC_W_MAC_CMD;
+	writel(reg, comm->l2sw_reg_base + L2SW_WT_MAC_AD0);
+
+	/* Wait for completing. */
+	ret = read_poll_timeout(readl, reg, reg & MAC_W_MAC_DONE, 1, 200, true,
+				comm->l2sw_reg_base + L2SW_WT_MAC_AD0);
+	if (ret) {
+		netdev_err(mac->ndev, "Failed to delete address from table!\n");
+		return ret;
+	}
+
+	netdev_dbg(mac->ndev, "mac_ad0 = %08x, mac_ad = %08x%04x\n",
+		   readl(comm->l2sw_reg_base + L2SW_WT_MAC_AD0),
+		   (u32)FIELD_GET(MAC_W_MAC_47_16,
+		   readl(comm->l2sw_reg_base + L2SW_W_MAC_47_16)),
+		   (u32)FIELD_GET(MAC_W_MAC_15_0,
+		   readl(comm->l2sw_reg_base + L2SW_W_MAC_15_0)));
+	return 0;
+}
+
+void spl2sw_mac_hw_init(struct spl2sw_common *comm)
+{
+	u32 reg;
+
+	/* Disable cpu0 and cpu 1 port. */
+	reg = readl(comm->l2sw_reg_base + L2SW_CPU_CNTL);
+	reg |= MAC_DIS_SOC1_CPU | MAC_DIS_SOC0_CPU;
+	writel(reg, comm->l2sw_reg_base + L2SW_CPU_CNTL);
+
+	/* Set base addresses of TX and RX queues. */
+	writel(comm->desc_dma, comm->l2sw_reg_base + L2SW_TX_LBASE_ADDR_0);
+	writel(comm->desc_dma + sizeof(struct spl2sw_mac_desc) * TX_DESC_NUM,
+	       comm->l2sw_reg_base + L2SW_TX_HBASE_ADDR_0);
+	writel(comm->desc_dma + sizeof(struct spl2sw_mac_desc) * (TX_DESC_NUM +
+	       MAC_GUARD_DESC_NUM), comm->l2sw_reg_base + L2SW_RX_HBASE_ADDR_0);
+	writel(comm->desc_dma + sizeof(struct spl2sw_mac_desc) * (TX_DESC_NUM +
+	       MAC_GUARD_DESC_NUM + RX_QUEUE0_DESC_NUM),
+	       comm->l2sw_reg_base + L2SW_RX_LBASE_ADDR_0);
+
+	/* Fc_rls_th=0x4a, Fc_set_th=0x3a, Drop_rls_th=0x2d, Drop_set_th=0x1d */
+	writel(0x4a3a2d1d, comm->l2sw_reg_base + L2SW_FL_CNTL_TH);
+
+	/* Cpu_rls_th=0x4a, Cpu_set_th=0x3a, Cpu_th=0x12, Port_th=0x12 */
+	writel(0x4a3a1212, comm->l2sw_reg_base + L2SW_CPU_FL_CNTL_TH);
+
+	/* mtcc_lmt=0xf, Pri_th_l=6, Pri_th_h=6, weigh_8x_en=1 */
+	writel(0xf6680000, comm->l2sw_reg_base + L2SW_PRI_FL_CNTL);
+
+	/* High-active LED */
+	reg = readl(comm->l2sw_reg_base + L2SW_LED_PORT0);
+	reg |= MAC_LED_ACT_HI;
+	writel(reg, comm->l2sw_reg_base + L2SW_LED_PORT0);
+
+	/* Disable aging of cpu port 0 & 1.
+	 * Disable SA learning of cpu port 0 & 1.
+	 * Enable UC and MC packets
+	 */
+	reg = readl(comm->l2sw_reg_base + L2SW_CPU_CNTL);
+	reg &= ~(MAC_EN_SOC1_AGING | MAC_EN_SOC0_AGING |
+		 MAC_DIS_BC2CPU_P1 | MAC_DIS_BC2CPU_P0 |
+		 MAC_DIS_MC2CPU_P1 | MAC_DIS_MC2CPU_P0);
+	reg |= MAC_DIS_LRN_SOC1 | MAC_DIS_LRN_SOC0;
+	writel(reg, comm->l2sw_reg_base + L2SW_CPU_CNTL);
+
+	/* Enable RMC2CPU for port 0 & 1
+	 * Enable Flow control for port 0 & 1
+	 * Enable Back pressure for port 0 & 1
+	 */
+	reg = readl(comm->l2sw_reg_base + L2SW_PORT_CNTL0);
+	reg &= ~(MAC_DIS_RMC2CPU_P1 | MAC_DIS_RMC2CPU_P0);
+	reg |= MAC_EN_FLOW_CTL_P1 | MAC_EN_FLOW_CTL_P0 |
+	       MAC_EN_BACK_PRESS_P1 | MAC_EN_BACK_PRESS_P0;
+	writel(reg, comm->l2sw_reg_base + L2SW_PORT_CNTL0);
+
+	/* Disable LAN port SA learning. */
+	reg = readl(comm->l2sw_reg_base + L2SW_PORT_CNTL1);
+	reg |= MAC_DIS_SA_LRN_P1 | MAC_DIS_SA_LRN_P0;
+	writel(reg, comm->l2sw_reg_base + L2SW_PORT_CNTL1);
+
+	/* Enable rmii force mode and
+	 * set both external phy-address to 31.
+	 */
+	reg = readl(comm->l2sw_reg_base + L2SW_MAC_FORCE_MODE);
+	reg &= ~(MAC_EXT_PHY1_ADDR | MAC_EXT_PHY0_ADDR);
+	reg |= FIELD_PREP(MAC_EXT_PHY1_ADDR, 31) | FIELD_PREP(MAC_EXT_PHY0_ADDR, 31);
+	reg |= MAC_FORCE_RMII_EN_1 | MAC_FORCE_RMII_EN_0;
+	writel(reg, comm->l2sw_reg_base + L2SW_MAC_FORCE_MODE);
+
+	/* Port 0: VLAN group 0
+	 * Port 1: VLAN group 1
+	 */
+	reg = FIELD_PREP(MAC_P1_PVID, 1) | FIELD_PREP(MAC_P0_PVID, 0);
+	writel(reg, comm->l2sw_reg_base + L2SW_PVID_CONFIG0);
+
+	/* VLAN group 0: cpu0 (bit3) + port0 (bit0) = 1001 = 0x9
+	 * VLAN group 1: cpu0 (bit3) + port1 (bit1) = 1010 = 0xa
+	 */
+	reg = FIELD_PREP(MAC_VLAN_MEMSET_1, 0xa) | FIELD_PREP(MAC_VLAN_MEMSET_0, 9);
+	writel(reg, comm->l2sw_reg_base + L2SW_VLAN_MEMSET_CONFIG0);
+
+	/* RMC forward: to_cpu (1)
+	 * LED: 60mS (1)
+	 * BC storm prev: 31 BC (1)
+	 */
+	reg = readl(comm->l2sw_reg_base + L2SW_SW_GLB_CNTL);
+	reg &= ~(MAC_RMC_TB_FAULT_RULE | MAC_LED_FLASH_TIME | MAC_BC_STORM_PREV);
+	reg |= FIELD_PREP(MAC_RMC_TB_FAULT_RULE, 1) |
+	       FIELD_PREP(MAC_LED_FLASH_TIME, 1) |
+	       FIELD_PREP(MAC_BC_STORM_PREV, 1);
+	writel(reg, comm->l2sw_reg_base + L2SW_SW_GLB_CNTL);
+
+	writel(MAC_INT_MASK_DEF, comm->l2sw_reg_base + L2SW_SW_INT_MASK_0);
+}
+
+void spl2sw_mac_rx_mode_set(struct spl2sw_mac *mac)
+{
+	struct spl2sw_common *comm = mac->comm;
+	struct net_device *ndev = mac->ndev;
+	u32 mask, reg, rx_mode;
+
+	netdev_dbg(ndev, "ndev->flags = %08x\n", ndev->flags);
+	mask = FIELD_PREP(MAC_DIS_MC2CPU, mac->lan_port) |
+	       FIELD_PREP(MAC_DIS_UN2CPU, mac->lan_port);
+	reg = readl(comm->l2sw_reg_base + L2SW_CPU_CNTL);
+
+	if (ndev->flags & IFF_PROMISC) {
+		/* Allow MC and unknown UC packets */
+		rx_mode = FIELD_PREP(MAC_DIS_MC2CPU, mac->lan_port) |
+			  FIELD_PREP(MAC_DIS_UN2CPU, mac->lan_port);
+	} else if ((!netdev_mc_empty(ndev) && (ndev->flags & IFF_MULTICAST)) ||
+		   (ndev->flags & IFF_ALLMULTI)) {
+		/* Allow MC packets */
+		rx_mode = FIELD_PREP(MAC_DIS_MC2CPU, mac->lan_port);
+	} else {
+		/* Disable MC and unknown UC packets */
+		rx_mode = 0;
+	}
+
+	writel((reg & (~mask)) | ((~rx_mode) & mask), comm->l2sw_reg_base + L2SW_CPU_CNTL);
+	netdev_dbg(ndev, "cpu_cntl = %08x\n", readl(comm->l2sw_reg_base + L2SW_CPU_CNTL));
+}
+
+void spl2sw_mac_init(struct spl2sw_common *comm)
+{
+	u32 i;
+
+	for (i = 0; i < RX_DESC_QUEUE_NUM; i++)
+		comm->rx_pos[i] = 0;
+	mb();	/* make sure settings are effective. */
+
+	spl2sw_mac_hw_init(comm);
+}
+
+void spl2sw_mac_soft_reset(struct spl2sw_common *comm)
+{
+	u32 i;
+
+	spl2sw_mac_hw_stop(comm);
+
+	spl2sw_rx_descs_flush(comm);
+	comm->tx_pos = 0;
+	comm->tx_done_pos = 0;
+	comm->tx_desc_full = 0;
+
+	for (i = 0; i < RX_DESC_QUEUE_NUM; i++)
+		comm->rx_pos[i] = 0;
+	mb();	/* make sure settings are effective. */
+
+	spl2sw_mac_hw_init(comm);
+	spl2sw_mac_hw_start(comm);
+}
diff --git a/drivers/net/ethernet/sunplus/spl2sw_mac.h b/drivers/net/ethernet/sunplus/spl2sw_mac.h
new file mode 100644
index 000000000..41a929b3a
--- /dev/null
+++ b/drivers/net/ethernet/sunplus/spl2sw_mac.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright Sunplus Technology Co., Ltd.
+ *       All rights reserved.
+ */
+
+#ifndef __SPL2SW_MAC_H__
+#define __SPL2SW_MAC_H__
+
+void spl2sw_mac_hw_stop(struct spl2sw_common *comm);
+void spl2sw_mac_hw_start(struct spl2sw_common *comm);
+int  spl2sw_mac_addr_add(struct spl2sw_mac *mac);
+int  spl2sw_mac_addr_del(struct spl2sw_mac *mac);
+void spl2sw_mac_hw_init(struct spl2sw_common *comm);
+void spl2sw_mac_rx_mode_set(struct spl2sw_mac *mac);
+void spl2sw_mac_init(struct spl2sw_common *comm);
+void spl2sw_mac_soft_reset(struct spl2sw_common *comm);
+
+#endif
diff --git a/drivers/net/ethernet/sunplus/spl2sw_mdio.c b/drivers/net/ethernet/sunplus/spl2sw_mdio.c
new file mode 100644
index 000000000..139ac8f26
--- /dev/null
+++ b/drivers/net/ethernet/sunplus/spl2sw_mdio.c
@@ -0,0 +1,126 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright Sunplus Technology Co., Ltd.
+ *       All rights reserved.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/netdevice.h>
+#include <linux/bitfield.h>
+#include <linux/of_mdio.h>
+
+#include "spl2sw_register.h"
+#include "spl2sw_define.h"
+#include "spl2sw_mdio.h"
+
+#define SPL2SW_MDIO_READ_CMD           0x02
+#define SPL2SW_MDIO_WRITE_CMD          0x01
+
+static int spl2sw_mdio_access(struct spl2sw_common *comm, u8 cmd, u8 addr, u8 regnum, u16 wdata)
+{
+	u32 reg, reg2;
+	u32 val;
+	int ret;
+
+	/* Note that addr (of phy) should match either ext_phy0_addr
+	 * or ext_phy1_addr, or mdio commands won't be sent out.
+	 */
+	reg = readl(comm->l2sw_reg_base + L2SW_MAC_FORCE_MODE);
+	reg &= ~MAC_EXT_PHY0_ADDR;
+	reg |= FIELD_PREP(MAC_EXT_PHY0_ADDR, addr);
+
+	reg2 = FIELD_PREP(MAC_CPU_PHY_WT_DATA, wdata) | FIELD_PREP(MAC_CPU_PHY_CMD, cmd) |
+	       FIELD_PREP(MAC_CPU_PHY_REG_ADDR, regnum) | FIELD_PREP(MAC_CPU_PHY_ADDR, addr);
+
+	/* Set ext_phy0_addr and then issue mdio command.
+	 * No interrupt is allowed in between.
+	 */
+	spin_lock_irq(&comm->mdio_lock);
+	writel(reg, comm->l2sw_reg_base + L2SW_MAC_FORCE_MODE);
+	writel(reg2, comm->l2sw_reg_base + L2SW_PHY_CNTL_REG0);
+	spin_unlock_irq(&comm->mdio_lock);
+
+	ret = read_poll_timeout(readl, val, val & cmd, 1, 1000, true,
+				comm->l2sw_reg_base + L2SW_PHY_CNTL_REG1);
+
+	/* Set ext_phy0_addr back to 31 to prevent
+	 * from sending mdio command to phy by
+	 * hardware auto-mdio function.
+	 */
+	reg = readl(comm->l2sw_reg_base + L2SW_MAC_FORCE_MODE);
+	reg &= ~MAC_EXT_PHY0_ADDR;
+	reg |= FIELD_PREP(MAC_EXT_PHY0_ADDR, 31);
+	writel(reg, comm->l2sw_reg_base + L2SW_MAC_FORCE_MODE);
+
+	if (ret == 0)
+		return val >> 16;
+	else
+		return ret;
+}
+
+static int spl2sw_mii_read(struct mii_bus *bus, int addr, int regnum)
+{
+	struct spl2sw_common *comm = bus->priv;
+
+	if (regnum & MII_ADDR_C45)
+		return -EOPNOTSUPP;
+
+	return spl2sw_mdio_access(comm, SPL2SW_MDIO_READ_CMD, addr, regnum, 0);
+}
+
+static int spl2sw_mii_write(struct mii_bus *bus, int addr, int regnum, u16 val)
+{
+	struct spl2sw_common *comm = bus->priv;
+	int ret;
+
+	if (regnum & MII_ADDR_C45)
+		return -EOPNOTSUPP;
+
+	ret = spl2sw_mdio_access(comm, SPL2SW_MDIO_WRITE_CMD, addr, regnum, val);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+u32 spl2sw_mdio_init(struct spl2sw_common *comm)
+{
+	struct device_node *mdio_np;
+	struct mii_bus *mii_bus;
+	int ret;
+
+	/* Get mdio child node. */
+	mdio_np = of_get_child_by_name(comm->pdev->dev.of_node, "mdio");
+	if (!mdio_np) {
+		dev_err(&comm->pdev->dev, "No mdio child node found!\n");
+		return -ENODEV;
+	}
+
+	/* Allocate and register mdio bus. */
+	mii_bus = devm_mdiobus_alloc(&comm->pdev->dev);
+	if (!mii_bus)
+		return -ENOMEM;
+
+	mii_bus->name = "sunplus_mii_bus";
+	mii_bus->parent = &comm->pdev->dev;
+	mii_bus->priv = comm;
+	mii_bus->read = spl2sw_mii_read;
+	mii_bus->write = spl2sw_mii_write;
+	snprintf(mii_bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(&comm->pdev->dev));
+
+	ret = of_mdiobus_register(mii_bus, mdio_np);
+	if (ret) {
+		dev_err(&comm->pdev->dev, "Failed to register mdiobus!\n");
+		return ret;
+	}
+
+	comm->mii_bus = mii_bus;
+	return ret;
+}
+
+void spl2sw_mdio_remove(struct spl2sw_common *comm)
+{
+	if (comm->mii_bus) {
+		mdiobus_unregister(comm->mii_bus);
+		comm->mii_bus = NULL;
+	}
+}
diff --git a/drivers/net/ethernet/sunplus/spl2sw_mdio.h b/drivers/net/ethernet/sunplus/spl2sw_mdio.h
new file mode 100644
index 000000000..8a24c9cae
--- /dev/null
+++ b/drivers/net/ethernet/sunplus/spl2sw_mdio.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright Sunplus Technology Co., Ltd.
+ *       All rights reserved.
+ */
+
+#ifndef __SPL2SW_MDIO_H__
+#define __SPL2SW_MDIO_H__
+
+u32  spl2sw_mdio_init(struct spl2sw_common *comm);
+void spl2sw_mdio_remove(struct spl2sw_common *comm);
+
+#endif
diff --git a/drivers/net/ethernet/sunplus/spl2sw_phy.c b/drivers/net/ethernet/sunplus/spl2sw_phy.c
new file mode 100644
index 000000000..404f508a5
--- /dev/null
+++ b/drivers/net/ethernet/sunplus/spl2sw_phy.c
@@ -0,0 +1,92 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright Sunplus Technology Co., Ltd.
+ *       All rights reserved.
+ */
+
+#include <linux/netdevice.h>
+#include <linux/bitfield.h>
+#include <linux/of_mdio.h>
+
+#include "spl2sw_register.h"
+#include "spl2sw_define.h"
+#include "spl2sw_phy.h"
+
+static void spl2sw_mii_link_change(struct net_device *ndev)
+{
+	struct spl2sw_mac *mac = netdev_priv(ndev);
+	struct phy_device *phydev = ndev->phydev;
+	struct spl2sw_common *comm = mac->comm;
+	u32 reg;
+
+	reg = readl(comm->l2sw_reg_base + L2SW_MAC_FORCE_MODE);
+
+	if (phydev->link) {
+		reg |= FIELD_PREP(MAC_FORCE_RMII_LINK, mac->lan_port);
+
+		if (phydev->speed == 100) {
+			reg |= FIELD_PREP(MAC_FORCE_RMII_SPD, mac->lan_port);
+		} else {
+			reg &= FIELD_PREP(MAC_FORCE_RMII_SPD, ~mac->lan_port) |
+			       ~MAC_FORCE_RMII_SPD;
+		}
+
+		if (phydev->duplex) {
+			reg |= FIELD_PREP(MAC_FORCE_RMII_DPX, mac->lan_port);
+		} else {
+			reg &= FIELD_PREP(MAC_FORCE_RMII_DPX, ~mac->lan_port) |
+			       ~MAC_FORCE_RMII_DPX;
+		}
+
+		if (phydev->pause) {
+			reg |= FIELD_PREP(MAC_FORCE_RMII_FC, mac->lan_port);
+		} else {
+			reg &= FIELD_PREP(MAC_FORCE_RMII_FC, ~mac->lan_port) |
+			       ~MAC_FORCE_RMII_FC;
+		}
+	} else {
+		reg &= FIELD_PREP(MAC_FORCE_RMII_LINK, ~mac->lan_port) |
+		       ~MAC_FORCE_RMII_LINK;
+	}
+
+	writel(reg, comm->l2sw_reg_base + L2SW_MAC_FORCE_MODE);
+
+	phy_print_status(phydev);
+}
+
+int spl2sw_phy_connect(struct spl2sw_common *comm)
+{
+	struct phy_device *phydev;
+	struct net_device *ndev;
+	struct spl2sw_mac *mac;
+	int i;
+
+	for (i = 0; i < MAX_NETDEV_NUM; i++)
+		if (comm->ndev[i]) {
+			ndev = comm->ndev[i];
+			mac = netdev_priv(ndev);
+			phydev = of_phy_connect(ndev, mac->phy_node, spl2sw_mii_link_change,
+						0, mac->phy_mode);
+			if (!phydev)
+				return -ENODEV;
+
+			phy_support_asym_pause(phydev);
+			phy_attached_info(phydev);
+		}
+
+	return 0;
+}
+
+void spl2sw_phy_remove(struct spl2sw_common *comm)
+{
+	struct net_device *ndev;
+	int i;
+
+	for (i = 0; i < MAX_NETDEV_NUM; i++)
+		if (comm->ndev[i]) {
+			ndev = comm->ndev[i];
+			if (ndev) {
+				phy_disconnect(ndev->phydev);
+				ndev->phydev = NULL;
+			}
+		}
+}
diff --git a/drivers/net/ethernet/sunplus/spl2sw_phy.h b/drivers/net/ethernet/sunplus/spl2sw_phy.h
new file mode 100644
index 000000000..3d051a254
--- /dev/null
+++ b/drivers/net/ethernet/sunplus/spl2sw_phy.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright Sunplus Technology Co., Ltd.
+ *       All rights reserved.
+ */
+
+#ifndef __SPL2SW_PHY_H__
+#define __SPL2SW_PHY_H__
+
+int  spl2sw_phy_connect(struct spl2sw_common *comm);
+void spl2sw_phy_remove(struct spl2sw_common *comm);
+
+#endif
diff --git a/drivers/net/ethernet/sunplus/spl2sw_register.h b/drivers/net/ethernet/sunplus/spl2sw_register.h
new file mode 100644
index 000000000..9718e2ee2
--- /dev/null
+++ b/drivers/net/ethernet/sunplus/spl2sw_register.h
@@ -0,0 +1,86 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright Sunplus Technology Co., Ltd.
+ *       All rights reserved.
+ */
+
+#ifndef __SPL2SW_REGISTER_H__
+#define __SPL2SW_REGISTER_H__
+
+/* Register L2SW */
+#define L2SW_SW_INT_STATUS_0		0x0
+#define L2SW_SW_INT_MASK_0		0x4
+#define L2SW_FL_CNTL_TH			0x8
+#define L2SW_CPU_FL_CNTL_TH		0xc
+#define L2SW_PRI_FL_CNTL		0x10
+#define L2SW_VLAN_PRI_TH		0x14
+#define L2SW_EN_TOS_BUS			0x18
+#define L2SW_TOS_MAP0			0x1c
+#define L2SW_TOS_MAP1			0x20
+#define L2SW_TOS_MAP2			0x24
+#define L2SW_TOS_MAP3			0x28
+#define L2SW_TOS_MAP4			0x2c
+#define L2SW_TOS_MAP5			0x30
+#define L2SW_TOS_MAP6			0x34
+#define L2SW_TOS_MAP7			0x38
+#define L2SW_GLOBAL_QUE_STATUS		0x3c
+#define L2SW_ADDR_TBL_SRCH		0x40
+#define L2SW_ADDR_TBL_ST		0x44
+#define L2SW_MAC_AD_SER0		0x48
+#define L2SW_MAC_AD_SER1		0x4c
+#define L2SW_WT_MAC_AD0			0x50
+#define L2SW_W_MAC_15_0			0x54
+#define L2SW_W_MAC_47_16		0x58
+#define L2SW_PVID_CONFIG0		0x5c
+#define L2SW_PVID_CONFIG1		0x60
+#define L2SW_VLAN_MEMSET_CONFIG0	0x64
+#define L2SW_VLAN_MEMSET_CONFIG1	0x68
+#define L2SW_PORT_ABILITY		0x6c
+#define L2SW_PORT_ST			0x70
+#define L2SW_CPU_CNTL			0x74
+#define L2SW_PORT_CNTL0			0x78
+#define L2SW_PORT_CNTL1			0x7c
+#define L2SW_PORT_CNTL2			0x80
+#define L2SW_SW_GLB_CNTL		0x84
+#define L2SW_L2SW_SW_RESET		0x88
+#define L2SW_LED_PORT0			0x8c
+#define L2SW_LED_PORT1			0x90
+#define L2SW_LED_PORT2			0x94
+#define L2SW_LED_PORT3			0x98
+#define L2SW_LED_PORT4			0x9c
+#define L2SW_WATCH_DOG_TRIG_RST		0xa0
+#define L2SW_WATCH_DOG_STOP_CPU		0xa4
+#define L2SW_PHY_CNTL_REG0		0xa8
+#define L2SW_PHY_CNTL_REG1		0xac
+#define L2SW_MAC_FORCE_MODE		0xb0
+#define L2SW_VLAN_GROUP_CONFIG0		0xb4
+#define L2SW_VLAN_GROUP_CONFIG1		0xb8
+#define L2SW_FLOW_CTRL_TH3		0xbc
+#define L2SW_QUEUE_STATUS_0		0xc0
+#define L2SW_DEBUG_CNTL			0xc4
+#define L2SW_RESERVED_1			0xc8
+#define L2SW_MEM_TEST_INFO		0xcc
+#define L2SW_SW_INT_STATUS_1		0xd0
+#define L2SW_SW_INT_MASK_1		0xd4
+#define L2SW_SW_GLOBAL_SIGNAL		0xd8
+
+#define L2SW_CPU_TX_TRIG		0x208
+#define L2SW_TX_HBASE_ADDR_0		0x20c
+#define L2SW_TX_LBASE_ADDR_0		0x210
+#define L2SW_RX_HBASE_ADDR_0		0x214
+#define L2SW_RX_LBASE_ADDR_0		0x218
+#define L2SW_TX_HW_ADDR_0		0x21c
+#define L2SW_TX_LW_ADDR_0		0x220
+#define L2SW_RX_HW_ADDR_0		0x224
+#define L2SW_RX_LW_ADDR_0		0x228
+#define L2SW_CPU_PORT_CNTL_REG_0	0x22c
+#define L2SW_TX_HBASE_ADDR_1		0x230
+#define L2SW_TX_LBASE_ADDR_1		0x234
+#define L2SW_RX_HBASE_ADDR_1		0x238
+#define L2SW_RX_LBASE_ADDR_1		0x23c
+#define L2SW_TX_HW_ADDR_1		0x240
+#define L2SW_TX_LW_ADDR_1		0x244
+#define L2SW_RX_HW_ADDR_1		0x248
+#define L2SW_RX_LW_ADDR_1		0x24c
+#define L2SW_CPU_PORT_CNTL_REG_1	0x250
+
+#endif
-- 
2.25.1


^ permalink raw reply related

* [PATCH net-next v9 1/2] devicetree: bindings: net: Add bindings doc for Sunplus SP7021.
From: Wells Lu @ 2022-04-25 10:30 UTC (permalink / raw)
  To: davem, kuba, robh+dt, netdev, devicetree, linux-kernel, p.zabel,
	pabeni, krzk+dt, roopa, andrew, edumazet
  Cc: wells.lu, Wells Lu
In-Reply-To: <1650882640-7106-1-git-send-email-wellslutw@gmail.com>

Add bindings documentation for Sunplus SP7021 SoC.

Reviewed-by: Rob Herring <robh@kernel.org>
Signed-off-by: Wells Lu <wellslutw@gmail.com>
---
Changes in v9
  - None

 .../bindings/net/sunplus,sp7021-emac.yaml     | 141 ++++++++++++++++++
 MAINTAINERS                                   |   7 +
 2 files changed, 148 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/net/sunplus,sp7021-emac.yaml

diff --git a/Documentation/devicetree/bindings/net/sunplus,sp7021-emac.yaml b/Documentation/devicetree/bindings/net/sunplus,sp7021-emac.yaml
new file mode 100644
index 000000000..62dffee27
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/sunplus,sp7021-emac.yaml
@@ -0,0 +1,141 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+# Copyright (C) Sunplus Co., Ltd. 2021
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/net/sunplus,sp7021-emac.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Sunplus SP7021 Dual Ethernet MAC Device Tree Bindings
+
+maintainers:
+  - Wells Lu <wellslutw@gmail.com>
+
+description: |
+  Sunplus SP7021 dual 10M/100M Ethernet MAC controller.
+  Device node of the controller has following properties.
+
+properties:
+  compatible:
+    const: sunplus,sp7021-emac
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  clocks:
+    maxItems: 1
+
+  resets:
+    maxItems: 1
+
+  ethernet-ports:
+    type: object
+    description: Ethernet ports to PHY
+
+    properties:
+      "#address-cells":
+        const: 1
+
+      "#size-cells":
+        const: 0
+
+    patternProperties:
+      "^port@[0-1]$":
+        type: object
+        description: Port to PHY
+
+        properties:
+          reg:
+            minimum: 0
+            maximum: 1
+
+          phy-handle:
+            maxItems: 1
+
+          phy-mode:
+            maxItems: 1
+
+          nvmem-cells:
+            items:
+              - description: nvmem cell address of MAC address
+
+          nvmem-cell-names:
+            description: names corresponding to the nvmem cells
+            items:
+              - const: mac-address
+
+        required:
+          - reg
+          - phy-handle
+          - phy-mode
+          - nvmem-cells
+          - nvmem-cell-names
+
+  mdio:
+    $ref: mdio.yaml#
+    unevaluatedProperties: false
+
+additionalProperties: false
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - clocks
+  - resets
+  - pinctrl-0
+  - pinctrl-names
+  - ethernet-ports
+  - mdio
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/irq.h>
+
+    ethernet@9c108000 {
+        compatible = "sunplus,sp7021-emac";
+        reg = <0x9c108000 0x400>;
+        interrupt-parent = <&intc>;
+        interrupts = <66 IRQ_TYPE_LEVEL_HIGH>;
+        clocks = <&clkc 0xa7>;
+        resets = <&rstc 0x97>;
+        pinctrl-0 = <&emac_demo_board_v3_pins>;
+        pinctrl-names = "default";
+
+        ethernet-ports {
+            #address-cells = <1>;
+            #size-cells = <0>;
+
+            port@0 {
+                reg = <0>;
+                phy-handle = <&eth_phy0>;
+                phy-mode = "rmii";
+                nvmem-cells = <&mac_addr0>;
+                nvmem-cell-names = "mac-address";
+            };
+
+            port@1 {
+                reg = <1>;
+                phy-handle = <&eth_phy1>;
+                phy-mode = "rmii";
+                nvmem-cells = <&mac_addr1>;
+                nvmem-cell-names = "mac-address";
+            };
+        };
+
+        mdio {
+            #address-cells = <1>;
+            #size-cells = <0>;
+
+            eth_phy0: ethernet-phy@0 {
+                reg = <0>;
+            };
+
+            eth_phy1: ethernet-phy@1 {
+                reg = <1>;
+            };
+        };
+    };
+...
diff --git a/MAINTAINERS b/MAINTAINERS
index dd61684c2..84b17ef41 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -18888,6 +18888,13 @@ L:	netdev@vger.kernel.org
 S:	Maintained
 F:	drivers/net/ethernet/dlink/sundance.c
 
+SUNPLUS ETHERNET DRIVER
+M:	Wells Lu <wellslutw@gmail.com>
+L:	netdev@vger.kernel.org
+S:	Maintained
+W:	https://sunplus.atlassian.net/wiki/spaces/doc/overview
+F:	Documentation/devicetree/bindings/net/sunplus,sp7021-emac.yaml
+
 SUNPLUS OCOTP DRIVER
 M:	Vincent Shih <vincent.sunplus@gmail.com>
 S:	Maintained
-- 
2.25.1


^ permalink raw reply related

* [PATCH net-next v9 0/2] This is a patch series for Ethernet driver of Sunplus SP7021 SoC.
From: Wells Lu @ 2022-04-25 10:30 UTC (permalink / raw)
  To: davem, kuba, robh+dt, netdev, devicetree, linux-kernel, p.zabel,
	pabeni, krzk+dt, roopa, andrew, edumazet
  Cc: wells.lu, Wells Lu

Sunplus SP7021 is an ARM Cortex A7 (4 cores) based SoC. It integrates
many peripherals (ex: UART, I2C, SPI, SDIO, eMMC, USB, SD card and
etc.) into a single chip. It is designed for industrial control
applications.

Refer to:
https://sunplus.atlassian.net/wiki/spaces/doc/overview
https://tibbo.com/store/plus1.html

Wells Lu (2):
  devicetree: bindings: net: Add bindings doc for Sunplus SP7021.
  net: ethernet: Add driver for Sunplus SP7021

 .../bindings/net/sunplus,sp7021-emac.yaml     | 141 +++++
 MAINTAINERS                                   |   8 +
 drivers/net/ethernet/Kconfig                  |   1 +
 drivers/net/ethernet/Makefile                 |   1 +
 drivers/net/ethernet/sunplus/Kconfig          |  35 ++
 drivers/net/ethernet/sunplus/Makefile         |   6 +
 drivers/net/ethernet/sunplus/spl2sw_define.h  | 270 ++++++++
 drivers/net/ethernet/sunplus/spl2sw_desc.c    | 228 +++++++
 drivers/net/ethernet/sunplus/spl2sw_desc.h    |  19 +
 drivers/net/ethernet/sunplus/spl2sw_driver.c  | 577 ++++++++++++++++++
 drivers/net/ethernet/sunplus/spl2sw_driver.h  |  12 +
 drivers/net/ethernet/sunplus/spl2sw_int.c     | 260 ++++++++
 drivers/net/ethernet/sunplus/spl2sw_int.h     |  13 +
 drivers/net/ethernet/sunplus/spl2sw_mac.c     | 274 +++++++++
 drivers/net/ethernet/sunplus/spl2sw_mac.h     |  18 +
 drivers/net/ethernet/sunplus/spl2sw_mdio.c    | 126 ++++
 drivers/net/ethernet/sunplus/spl2sw_mdio.h    |  12 +
 drivers/net/ethernet/sunplus/spl2sw_phy.c     |  92 +++
 drivers/net/ethernet/sunplus/spl2sw_phy.h     |  12 +
 .../net/ethernet/sunplus/spl2sw_register.h    |  86 +++
 20 files changed, 2191 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/net/sunplus,sp7021-emac.yaml
 create mode 100644 drivers/net/ethernet/sunplus/Kconfig
 create mode 100644 drivers/net/ethernet/sunplus/Makefile
 create mode 100644 drivers/net/ethernet/sunplus/spl2sw_define.h
 create mode 100644 drivers/net/ethernet/sunplus/spl2sw_desc.c
 create mode 100644 drivers/net/ethernet/sunplus/spl2sw_desc.h
 create mode 100644 drivers/net/ethernet/sunplus/spl2sw_driver.c
 create mode 100644 drivers/net/ethernet/sunplus/spl2sw_driver.h
 create mode 100644 drivers/net/ethernet/sunplus/spl2sw_int.c
 create mode 100644 drivers/net/ethernet/sunplus/spl2sw_int.h
 create mode 100644 drivers/net/ethernet/sunplus/spl2sw_mac.c
 create mode 100644 drivers/net/ethernet/sunplus/spl2sw_mac.h
 create mode 100644 drivers/net/ethernet/sunplus/spl2sw_mdio.c
 create mode 100644 drivers/net/ethernet/sunplus/spl2sw_mdio.h
 create mode 100644 drivers/net/ethernet/sunplus/spl2sw_phy.c
 create mode 100644 drivers/net/ethernet/sunplus/spl2sw_phy.h
 create mode 100644 drivers/net/ethernet/sunplus/spl2sw_register.h

-- 
2.25.1


^ permalink raw reply

* Re: [PATCH V2] net: mscc: ocelot: Remove useless code
From: patchwork-bot+netdevbpf @ 2022-04-25 10:30 UTC (permalink / raw)
  To: Haowen Bai
  Cc: UNGLinuxDriver, alexandre.belloni, claudiu.manoil, davem, kuba,
	linux-kernel, netdev, pabeni, vladimir.oltean
In-Reply-To: <1650537281-15069-1-git-send-email-baihaowen@meizu.com>

Hello:

This patch was applied to netdev/net-next.git (master)
by David S. Miller <davem@davemloft.net>:

On Thu, 21 Apr 2022 18:34:41 +0800 you wrote:
> payload only memset but no use at all, so we drop them.
> 
> Signed-off-by: Haowen Bai <baihaowen@meizu.com>
> ---
> V1->V2: change correct title
> 
>  drivers/net/ethernet/mscc/ocelot_vcap.c | 4 ----
>  1 file changed, 4 deletions(-)

Here is the summary with links:
  - [V2] net: mscc: ocelot: Remove useless code
    https://git.kernel.org/netdev/net-next/c/985e254c738c

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply

* Re: [PATCH net-next] net: ethernet: mtk_eth_soc: add check for allocation failure
From: patchwork-bot+netdevbpf @ 2022-04-25 10:30 UTC (permalink / raw)
  To: Dan Carpenter
  Cc: nbd, john, sean.wang, Mark-MC.Lee, davem, kuba, pabeni,
	matthias.bgg, netdev, linux-mediatek, kernel-janitors
In-Reply-To: <YmF87nnzwiJC71k6@kili>

Hello:

This patch was applied to netdev/net-next.git (master)
by David S. Miller <davem@davemloft.net>:

On Thu, 21 Apr 2022 18:49:02 +0300 you wrote:
> Check if the kzalloc() failed.
> 
> Fixes: 804775dfc288 ("net: ethernet: mtk_eth_soc: add support for Wireless Ethernet Dispatch (WED)")
> Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>
> ---
>  drivers/net/ethernet/mediatek/mtk_wed.c | 2 ++
>  1 file changed, 2 insertions(+)

Here is the summary with links:
  - [net-next] net: ethernet: mtk_eth_soc: add check for allocation failure
    https://git.kernel.org/netdev/net-next/c/a00e41bf2f47

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply

* Re: [PATCH] ethernet: broadcom/sb1250-mac: remove BUG_ON in sbmac_probe()
From: patchwork-bot+netdevbpf @ 2022-04-25 10:30 UTC (permalink / raw)
  To: Yang Yingliang; +Cc: linux-kernel, netdev, davem, kuba
In-Reply-To: <20220421135148.2890823-1-yangyingliang@huawei.com>

Hello:

This patch was applied to netdev/net-next.git (master)
by David S. Miller <davem@davemloft.net>:

On Thu, 21 Apr 2022 21:51:48 +0800 you wrote:
> Replace the BUG_ON() with returning error code to handle
> the fault more gracefully.
> 
> Signed-off-by: Yang Yingliang <yangyingliang@huawei.com>
> ---
>  drivers/net/ethernet/broadcom/sb1250-mac.c | 7 ++++++-
>  1 file changed, 6 insertions(+), 1 deletion(-)

Here is the summary with links:
  - ethernet: broadcom/sb1250-mac: remove BUG_ON in sbmac_probe()
    https://git.kernel.org/netdev/net-next/c/60d78e9fce88

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply

* Re: [PATCH net-next] net: ipa: compute proper aggregation limit
From: patchwork-bot+netdevbpf @ 2022-04-25 10:30 UTC (permalink / raw)
  To: Alex Elder
  Cc: davem, kuba, mka, evgreen, bjorn.andersson, cpratapa, avuyyuru,
	jponduru, subashab, elder, netdev, linux-arm-msm, linux-kernel
In-Reply-To: <20220421185333.1371632-1-elder@linaro.org>

Hello:

This patch was applied to netdev/net-next.git (master)
by David S. Miller <davem@davemloft.net>:

On Thu, 21 Apr 2022 13:53:33 -0500 you wrote:
> The aggregation byte limit for an endpoint is currently computed
> based on the endpoint's receive buffer size.
> 
> However, some bytes at the front of each receive buffer are reserved
> on the assumption that--as with SKBs--it might be useful to insert
> data (such as headers) before what lands in the buffer.
> 
> [...]

Here is the summary with links:
  - [net-next] net: ipa: compute proper aggregation limit
    https://git.kernel.org/netdev/net-next/c/c5794097b269

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply

* Re: [PATCH net] net: lan966x: fix a couple off by one bugs
From: patchwork-bot+netdevbpf @ 2022-04-25 10:30 UTC (permalink / raw)
  To: Dan Carpenter
  Cc: horatiu.vultur, UNGLinuxDriver, davem, kuba, pabeni, netdev,
	kernel-janitors
In-Reply-To: <YmF8RTClhMXPVPgh@kili>

Hello:

This patch was applied to netdev/net.git (master)
by David S. Miller <davem@davemloft.net>:

On Thu, 21 Apr 2022 18:46:13 +0300 you wrote:
> The lan966x->ports[] array has lan966x->num_phys_ports elements.  These
> are assigned in lan966x_probe().  That means the > comparison should be
> changed to >=.
> 
> The first off by one check is harmless but the second one could lead to
> an out of bounds access and a crash.
> 
> [...]

Here is the summary with links:
  - [net] net: lan966x: fix a couple off by one bugs
    https://git.kernel.org/netdev/net/c/9810c58c7051

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply

* Re: [PATCH net] net/smc: sync err code when tcp connection was refused
From: patchwork-bot+netdevbpf @ 2022-04-25 10:20 UTC (permalink / raw)
  To: None; +Cc: kgraul, davem, kuba, pabeni, linux-s390, netdev, linux-kernel,
	tonylu
In-Reply-To: <20220421094027.683992-1-liuyacan@corp.netease.com>

Hello:

This patch was applied to netdev/net.git (master)
by David S. Miller <davem@davemloft.net>:

On Thu, 21 Apr 2022 17:40:27 +0800 you wrote:
> From: liuyacan <liuyacan@corp.netease.com>
> 
> In the current implementation, when TCP initiates a connection
> to an unavailable [ip,port], ECONNREFUSED will be stored in the
> TCP socket, but SMC will not. However, some apps (like curl) use
> getsockopt(,,SO_ERROR,,) to get the error information, which makes
> them miss the error message and behave strangely.
> 
> [...]

Here is the summary with links:
  - [net] net/smc: sync err code when tcp connection was refused
    https://git.kernel.org/netdev/net/c/4e2e65e2e56c

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ 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