RE: [PATCH v2 2/2] net: ethernet: Add driver for Sunplus SP7021

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi,

> 11/11/21 12:04 PM, Wells Lu пишет:
> > Add driver for Sunplus SP7021.
> >
> > Signed-off-by: Wells Lu <wells.lu@xxxxxxxxxxx>
> > ---
> > Changes in V2
> >   - Addressed all comments from Mr. Philipp Zabel.
> >     - Revised probe function.
> >   - Addressed all comments from Mr. Randy Dunlap.
> >     - Revised Kconfig
> >   - Addressed all comments from Mr. Andrew Lunn.
> >     - Removed daisy-chain (hub) function. Only keep dual NIC mode.
> >     - Removed dynamic mode-switching function using sysfs.
> >     - Removed unnecessary wmb().
> >     - Replaced prefix l2sw_ with sp_ for struct, funciton and file names.
> >     - Modified ethernet_do_ioctl() function.
> >     - Removed ethernet_do_change_mtu() function.
> >     - Revised Kconfig. Added '|| COMPILE_TEST'
> >     - Others
> >   - Replaced HWREG_R() and HWREG_W() macro with readl() and writel().
> >   - Created new file sp_phy.c/sp_phy.h and moved phy-related functions in.
> >   - Revised function name in sp_hal.c/.h to add hal_ prefix.
> >
> >   MAINTAINERS                                |   1 +
> >   drivers/net/ethernet/Kconfig               |   1 +
> >   drivers/net/ethernet/Makefile              |   1 +
> >   drivers/net/ethernet/sunplus/Kconfig       |  36 ++
> >   drivers/net/ethernet/sunplus/Makefile      |   6 +
> >   drivers/net/ethernet/sunplus/sp_define.h   | 212 ++++++++++
> >   drivers/net/ethernet/sunplus/sp_desc.c     | 231 +++++++++++
> >   drivers/net/ethernet/sunplus/sp_desc.h     |  21 +
> >   drivers/net/ethernet/sunplus/sp_driver.c   | 606 +++++++++++++++++++++++++++++
> >   drivers/net/ethernet/sunplus/sp_driver.h   |  23 ++
> >   drivers/net/ethernet/sunplus/sp_hal.c      | 331 ++++++++++++++++
> >   drivers/net/ethernet/sunplus/sp_hal.h      |  31 ++
> >   drivers/net/ethernet/sunplus/sp_int.c      | 286 ++++++++++++++
> >   drivers/net/ethernet/sunplus/sp_int.h      |  13 +
> >   drivers/net/ethernet/sunplus/sp_mac.c      |  63 +++
> >   drivers/net/ethernet/sunplus/sp_mac.h      |  23 ++
> >   drivers/net/ethernet/sunplus/sp_mdio.c     |  90 +++++
> >   drivers/net/ethernet/sunplus/sp_mdio.h     |  20 +
> >   drivers/net/ethernet/sunplus/sp_phy.c      |  64 +++
> >   drivers/net/ethernet/sunplus/sp_phy.h      |  16 +
> >   drivers/net/ethernet/sunplus/sp_register.h |  96 +++++
> >   21 files changed, 2171 insertions(+)
> >   create mode 100644 drivers/net/ethernet/sunplus/Kconfig
> >   create mode 100644 drivers/net/ethernet/sunplus/Makefile
> >   create mode 100644 drivers/net/ethernet/sunplus/sp_define.h
> >   create mode 100644 drivers/net/ethernet/sunplus/sp_desc.c
> >   create mode 100644 drivers/net/ethernet/sunplus/sp_desc.h
> >   create mode 100644 drivers/net/ethernet/sunplus/sp_driver.c
> >   create mode 100644 drivers/net/ethernet/sunplus/sp_driver.h
> >   create mode 100644 drivers/net/ethernet/sunplus/sp_hal.c
> >   create mode 100644 drivers/net/ethernet/sunplus/sp_hal.h
> >   create mode 100644 drivers/net/ethernet/sunplus/sp_int.c
> >   create mode 100644 drivers/net/ethernet/sunplus/sp_int.h
> >   create mode 100644 drivers/net/ethernet/sunplus/sp_mac.c
> >   create mode 100644 drivers/net/ethernet/sunplus/sp_mac.h
> >   create mode 100644 drivers/net/ethernet/sunplus/sp_mdio.c
> >   create mode 100644 drivers/net/ethernet/sunplus/sp_mdio.h
> >   create mode 100644 drivers/net/ethernet/sunplus/sp_phy.c
> >   create mode 100644 drivers/net/ethernet/sunplus/sp_phy.h
> >   create mode 100644 drivers/net/ethernet/sunplus/sp_register.h
> >
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index 737b9d0..ec1ddb1 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -18006,6 +18006,7 @@ L:	netdev@xxxxxxxxxxxxxxx
> >   S:	Maintained
> >   W:	https://sunplus-tibbo.atlassian.net/wiki/spaces/doc/overview
> >   F:	Documentation/devicetree/bindings/net/sunplus,sp7021-emac.yaml
> > +F:	drivers/net/ethernet/sunplus/
> >
> >   SUPERH
> >   M:	Yoshinori Sato <ysato@xxxxxxxxxxxxxxxxxxxx>
> > diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig
> > index 412ae3e..0084852 100644
> > --- a/drivers/net/ethernet/Kconfig
> > +++ b/drivers/net/ethernet/Kconfig
> > @@ -176,6 +176,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 aaa5078..e4ce162 100644
> > --- a/drivers/net/ethernet/Makefile
> > +++ b/drivers/net/ethernet/Makefile
> > @@ -87,6 +87,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 0000000..5af2c5b
> > --- /dev/null
> > +++ b/drivers/net/ethernet/sunplus/Kconfig
> > @@ -0,0 +1,36 @@
> > +# 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 PINCTRL_SPPCTL
> > +	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 0000000..963ba1d
> > --- /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 := sp_driver.o sp_int.o sp_hal.o sp_desc.o sp_mac.o sp_mdio.o sp_phy.o
> > diff --git a/drivers/net/ethernet/sunplus/sp_define.h b/drivers/net/ethernet/sunplus/sp_define.h
> > new file mode 100644
> > index 0000000..40e15ba
> > --- /dev/null
> > +++ b/drivers/net/ethernet/sunplus/sp_define.h
> > @@ -0,0 +1,212 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/* Copyright Sunplus Technology Co., Ltd.
> > + *       All rights reserved.
> > + */
> > +
> > +#ifndef __SP_DEFINE_H__
> > +#define __SP_DEFINE_H__
> > +
> > +#include <linux/module.h>
> > +#include <linux/init.h>
> > +#include <linux/sched.h>
> > +#include <linux/kernel.h>
> > +#include <linux/slab.h>
> > +#include <linux/errno.h>
> > +#include <linux/types.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/kdev_t.h>
> > +#include <linux/in.h>
> > +#include <linux/netdevice.h>
> > +#include <linux/etherdevice.h>
> > +#include <linux/ip.h>
> > +#include <linux/tcp.h>
> > +#include <linux/skbuff.h>
> > +#include <linux/ethtool.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/phy.h>
> > +#include <linux/mii.h>
> > +#include <linux/if_vlan.h>
> > +#include <linux/io.h>
> > +#include <linux/dma-mapping.h>
> > +#include <linux/of_address.h>
> > +#include <linux/of_mdio.h>
> > +
> > +//define MAC interrupt status bit
> please embrace all comments with /* */

Do you mean to modify comment, for example,

//define MAC interrupt status bit

to 

/* define MAC interrupt status bit */

for all commets in .h and .c files?


> > +#define MAC_INT_DAISY_MODE_CHG          BIT(31)
> > +#define MAC_INT_IP_CHKSUM_ERR           BIT(23)
> > +#define MAC_INT_WDOG_TIMER1_EXP         BIT(22)
> > +#define MAC_INT_WDOG_TIMER0_EXP         BIT(21)
> > +#define MAC_INT_INTRUDER_ALERT          BIT(20)
> > +#define MAC_INT_PORT_ST_CHG             BIT(19)
> > +#define MAC_INT_BC_STORM                BIT(18)
> > +#define MAC_INT_MUST_DROP_LAN           BIT(17)
> > +#define MAC_INT_GLOBAL_QUE_FULL         BIT(16)
> > +#define MAC_INT_TX_SOC_PAUSE_ON         BIT(15)
> > +#define MAC_INT_RX_SOC_QUE_FULL         BIT(14)
> > +#define MAC_INT_TX_LAN1_QUE_FULL        BIT(9)
> > +#define MAC_INT_TX_LAN0_QUE_FULL        BIT(8)
> > +#define MAC_INT_RX_L_DESCF              BIT(7)
> > +#define MAC_INT_RX_H_DESCF              BIT(6)
> > +#define MAC_INT_RX_DONE_L               BIT(5)
> > +#define MAC_INT_RX_DONE_H               BIT(4)
> > +#define MAC_INT_TX_DONE_L               BIT(3)
> > +#define MAC_INT_TX_DONE_H               BIT(2)
> > +#define MAC_INT_TX_DES_ERR              BIT(1)
> > +#define MAC_INT_RX_DES_ERR              BIT(0)
> > +
> > +#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_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)
> > +
> > +/*define port ability*/
> > +#define PORT_ABILITY_LINK_ST_P1         BIT(25)
> > +#define PORT_ABILITY_LINK_ST_P0         BIT(24)
> > +
> > +/*define PHY command bit*/
> > +#define PHY_WT_DATA_MASK                0xffff0000
> > +#define PHY_RD_CMD                      0x00004000
> > +#define PHY_WT_CMD                      0x00002000
> > +#define PHY_REG_MASK                    0x00001f00
> > +#define PHY_ADR_MASK                    0x0000001f
> > +
> > +/*define PHY status bit*/
> > +#define PHY_RD_DATA_MASK                0xffff0000
> > +#define PHY_RD_RDY                      BIT(1)
> > +#define PHY_WT_DONE                     BIT(0)
> > +
> > +/*define other register bit*/
> > +#define RX_MAX_LEN_MASK                 0x00011000
> > +#define ROUTE_MODE_MASK                 0x00000060
> > +#define POK_INT_THS_MASK                0x000E0000
> > +#define VLAN_TH_MASK                    0x00000007
> > +
> > +/*define tx descriptor bit*/
> > +#define OWN_BIT                         BIT(31)
> > +#define FS_BIT                          BIT(25)
> > +#define LS_BIT                          BIT(24)
> > +#define LEN_MASK                        0x000007FF
> > +#define PKTSP_MASK                      0x00007000
> > +#define PKTSP_PORT1                     0x00001000
> > +#define TO_VLAN_MASK                    0x0003F000
> > +#define TO_VLAN_GROUP1                  0x00002000
> > +
> > +#define EOR_BIT                         BIT(31)
> > +
> > +/*define rx descriptor bit*/
> > +#define ERR_CODE                        (0xf << 26)
> > +#define RX_TCP_UDP_CHKSUM_BIT           BIT(23)
> > +#define RX_IP_CHKSUM_BIT                BIT(18)
> > +
> > +#define OWC_BIT                         BIT(31)
> > +#define TXOK_BIT                        BIT(26)
> > +#define LNKF_BIT                        BIT(25)
> > +#define BUR_BIT                         BIT(22)
> > +#define TWDE_BIT                        BIT(20)
> > +#define CC_MASK                         0x000f0000
> > +#define TBE_MASK                        0x00070000
> > +
> > +// 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 search
> > +#define MAC_HASK_LOOKUP_ADDR_MASK       (0x3ff << 22)
> > +#define MAC_AT_TABLE_END                BIT(1)
> > +#define MAC_AT_DATA_READY               BIT(0)
> > +
> > +/*config descriptor*/
> > +#define TX_DESC_NUM                     16
> > +#define MAC_GUARD_DESC_NUM              2
> > +#define RX_QUEUE0_DESC_NUM              16
> > +#define RX_QUEUE1_DESC_NUM              16
> > +#define TX_DESC_QUEUE_NUM               1
> > +#define RX_DESC_QUEUE_NUM               2
> > +
> > +#define MAC_TX_BUFF_SIZE                1536
> > +#define MAC_RX_LEN_MAX                  2047
> > +
> > +#define DESC_ALIGN_BYTE                 32
> > +#define RX_OFFSET                       0
> > +#define TX_OFFSET                       0
> > +
> > +#define ETHERNET_MAC_ADDR_LEN           6
> > +
> > +struct mac_desc {
> > +	u32 cmd1;
> > +	u32 cmd2;
> > +	u32 addr1;
> > +	u32 addr2;
> > +};
> > +
> > +struct skb_info {
> > +	struct sk_buff *skb;
> > +	u32 mapping;
> > +	u32 len;
> > +};
> > +
> > +struct sp_common {
> > +	void __iomem *sp_reg_base;
> > +	void __iomem *moon5_reg_base;
> > +
> > +	struct net_device *ndev;
> > +	struct platform_device *pdev;
> > +
> > +	void *desc_base;
> > +	dma_addr_t desc_dma;
> > +	s32 desc_size;
> > +	struct clk *clk;
> > +	struct reset_control *rstc;
> > +	int irq;
> > +
> > +	struct mac_desc *rx_desc[RX_DESC_QUEUE_NUM];
> > +	struct 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 mac_desc *tx_desc;
> > +	struct skb_info tx_temp_skb_info[TX_DESC_NUM];
> > +	u32 tx_done_pos;
> > +	u32 tx_pos;
> > +	u32 tx_desc_full;
> > +
> > +	struct mii_bus *mii_bus;
> > +
> > +	struct napi_struct rx_napi;
> > +	struct napi_struct tx_napi;
> > +
> > +	spinlock_t rx_lock;      // spinlock for accessing rx buffer
> > +	spinlock_t tx_lock;      // spinlock for accessing tx buffer
> > +	spinlock_t ioctl_lock;   // spinlock for ioctl operations
> > +
> > +	u8 enable;
> > +};
> > +
> > +struct sp_mac {
> > +	struct net_device *ndev;
> > +	struct net_device *next_ndev;
> > +	struct phy_device *phy_dev;
> > +	struct sp_common *comm;
> > +	struct net_device_stats dev_stats;
> > +	struct device_node *phy_node;
> > +	phy_interface_t phy_mode;
> > +	u32 phy_addr;
> > +
> > +	u8 mac_addr[ETHERNET_MAC_ADDR_LEN];
> > +
> > +	u8 lan_port;
> > +	u8 to_vlan;
> > +	u8 cpu_port;
> > +	u8 vlan_id;
> > +};
> > +
> > +#endif
> > diff --git a/drivers/net/ethernet/sunplus/sp_desc.c b/drivers/net/ethernet/sunplus/sp_desc.c
> > new file mode 100644
> > index 0000000..fed91ec
> > --- /dev/null
> > +++ b/drivers/net/ethernet/sunplus/sp_desc.c
> > @@ -0,0 +1,231 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/* Copyright Sunplus Technology Co., Ltd.
> > + *       All rights reserved.
> > + */
> > +
> > +#include "sp_desc.h"
> > +#include "sp_define.h"
> > +
> > +void rx_descs_flush(struct sp_common *comm)
> > +{
> > +	u32 i, j;
> > +	struct mac_desc *rx_desc;
> > +	struct skb_info *rx_skbinfo;
> > +
> > +	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) ?
> > +					  EOR_BIT | comm->rx_desc_buff_size :
> > +					  comm->rx_desc_buff_size;
> > +			wmb();	// Set OWN_BIT after other fields are ready.
> > +			rx_desc[j].cmd1 = OWN_BIT;
> > +		}
> > +	}
> > +}
> > +
> > +void tx_descs_clean(struct sp_common *comm)
> > +{
> > +	u32 i;
> > +	s32 buflen;
> > +
> > +	if (!comm->tx_desc)
> > +		return;
> > +
> > +	for (i = 0; i < TX_DESC_NUM; i++) {
> > +		comm->tx_desc[i].cmd1 = 0;
> > +		wmb();		// Clear OWN_BIT 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) {
> > +			buflen = (comm->tx_temp_skb_info[i].skb) ?
> > +				 comm->tx_temp_skb_info[i].skb->len :
> > +				 MAC_TX_BUFF_SIZE;
> > +			dma_unmap_single(&comm->pdev->dev, comm->tx_temp_skb_info[i].mapping,
> > +					 buflen, DMA_TO_DEVICE);
> > +			comm->tx_temp_skb_info[i].mapping = 0;
> > +		}
> > +
> > +		if (comm->tx_temp_skb_info[i].skb) {
> > +			dev_kfree_skb(comm->tx_temp_skb_info[i].skb);
> > +			comm->tx_temp_skb_info[i].skb = NULL;
> > +		}
> > +	}
> > +}
> > +
> > +void rx_descs_clean(struct sp_common *comm)
> > +{
> > +	u32 i, j;
> > +	struct mac_desc *rx_desc;
> > +	struct skb_info *rx_skbinfo;
> > +
> > +	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 OWN_BIT 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(rx_skbinfo[j].skb);
> > +				rx_skbinfo[j].skb = NULL;
> > +				rx_skbinfo[j].mapping = 0;
> > +			}
> > +		}
> > +
> > +		kfree(rx_skbinfo);
> > +		comm->rx_skb_info[i] = NULL;
> > +	}
> > +}
> > +
> > +void descs_clean(struct sp_common *comm)
> > +{
> > +	rx_descs_clean(comm);
> > +	tx_descs_clean(comm);
> > +}
> > +
> > +void descs_free(struct sp_common *comm)
> > +{
> > +	u32 i;
> > +
> > +	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 tx_descs_init(struct sp_common *comm)
> > +{
> > +	memset(comm->tx_desc, '\0', sizeof(struct mac_desc) *
> > +	       (TX_DESC_NUM + MAC_GUARD_DESC_NUM));
> > +}
> > +
> > +int rx_descs_init(struct sp_common *comm)
> > +{
> > +	struct sk_buff *skb;
> > +	u32 i, j;
> > +	struct mac_desc *rx_desc;
> > +	struct skb_info *rx_skbinfo;
> > +
> > +	for (i = 0; i < RX_DESC_QUEUE_NUM; i++) {
> > +		comm->rx_skb_info[i] = kmalloc_array(comm->rx_desc_num[i],
> > +						     sizeof(struct skb_info), GFP_KERNEL);
> > +		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 = __dev_alloc_skb(comm->rx_desc_buff_size + RX_OFFSET,
> > +					      GFP_ATOMIC | GFP_DMA);
> > +			if (!skb)
> > +				goto MEM_ALLOC_FAIL;
> > +
> > +			skb->dev = comm->ndev;
> > +			skb_reserve(skb, RX_OFFSET);	/* +data +tail */
> > +
> > +			rx_skbinfo[j].skb = skb;
> > +			rx_skbinfo[j].mapping = dma_map_single(&comm->pdev->dev, skb->data,
> > +							       comm->rx_desc_buff_size,
> > +							       DMA_FROM_DEVICE);
> 			it may fail

Yes, I'll add error check in next patch as shown below:

		rx_skbinfo[j].mapping = dma_map_single(&comm->pdev->dev, skb->data,
						       comm->rx_desc_buff_size,
						       DMA_FROM_DEVICE);
		if (dma_mapping_error(&comm->pdev->dev, rx_skbinfo[j].mapping))
			goto mem_alloc_fail;

[...]
	Return 0;

mem_alloc_fail:
	rx_desc_clean(comm);
	return -ENOMEM;
}


> > +			rx_desc[j].addr1 = rx_skbinfo[j].mapping;
> > +			rx_desc[j].addr2 = 0;
> > +			rx_desc[j].cmd2 = (j == comm->rx_desc_num[i] - 1) ?
> > +					  EOR_BIT | comm->rx_desc_buff_size :
> > +					  comm->rx_desc_buff_size;
> > +			wmb();	// Set OWN_BIT after other fields are effective.
> > +			rx_desc[j].cmd1 = OWN_BIT;
> > +		}
> > +	}
> > +
> > +	return 0;
> > +
> > +MEM_ALLOC_FAIL:
> lowercase

Yes, I'll modify the label and other labels to lowercase in next patch.


> > +	rx_descs_clean(comm);
> > +	return -ENOMEM;
> > +}
> > +
> > +int descs_alloc(struct sp_common *comm)
> > +{
> > +	u32 i;
> > +	s32 desc_size;
> > +
> > +	/* Alloc descriptor area  */
> > +	desc_size = (TX_DESC_NUM + MAC_GUARD_DESC_NUM) * sizeof(struct mac_desc);
> > +	for (i = 0; i < RX_DESC_QUEUE_NUM; i++)
> > +		desc_size += comm->rx_desc_num[i] * sizeof(struct 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 = (struct mac_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 descs_init(struct sp_common *comm)
> > +{
> > +	u32 i, ret;
> > +
> > +	// Initialize rx descriptor's data
> > +	comm->rx_desc_num[0] = RX_QUEUE0_DESC_NUM;
> > +#if RX_DESC_QUEUE_NUM > 1
> > +	comm->rx_desc_num[1] = RX_QUEUE1_DESC_NUM;
> > +#endif
> > +
> > +	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 = descs_alloc(comm);
> > +	if (ret) {
> > +		netdev_err(comm->ndev, "Failed to allocate tx & rx descriptors!\n");
> > +		return ret;
> > +	}
> > +
> > +	tx_descs_init(comm);
> > +
> > +	return rx_descs_init(comm);
> > +}
> > diff --git a/drivers/net/ethernet/sunplus/sp_desc.h b/drivers/net/ethernet/sunplus/sp_desc.h
> > new file mode 100644
> > index 0000000..20f7519
> > --- /dev/null
> > +++ b/drivers/net/ethernet/sunplus/sp_desc.h
> > @@ -0,0 +1,21 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/* Copyright Sunplus Technology Co., Ltd.
> > + *       All rights reserved.
> > + */
> > +
> > +#ifndef __SP_DESC_H__
> > +#define __SP_DESC_H__
> > +
> > +#include "sp_define.h"
> > +
> > +void rx_descs_flush(struct sp_common *comm);
> > +void tx_descs_clean(struct sp_common *comm);
> > +void rx_descs_clean(struct sp_common *comm);
> > +void descs_clean(struct sp_common *comm);
> > +void descs_free(struct sp_common *comm);
> > +void tx_descs_init(struct sp_common *comm);
> > +int  rx_descs_init(struct sp_common *comm);
> > +int  descs_alloc(struct sp_common *comm);
> > +int  descs_init(struct sp_common *comm);
> > +
> > +#endif
> > diff --git a/drivers/net/ethernet/sunplus/sp_driver.c b/drivers/net/ethernet/sunplus/sp_driver.c
> > new file mode 100644
> > index 0000000..a1c76b9
> > --- /dev/null
> > +++ b/drivers/net/ethernet/sunplus/sp_driver.c
> > @@ -0,0 +1,606 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/* Copyright Sunplus Technology Co., Ltd.
> > + *       All rights reserved.
> > + */
> > +
> > +#include <linux/clk.h>
> > +#include <linux/reset.h>
> > +#include <linux/nvmem-consumer.h>
> > +#include <linux/of_net.h>
> > +#include "sp_driver.h"
> > +#include "sp_phy.h"
> > +
> > +static const char def_mac_addr[ETHERNET_MAC_ADDR_LEN] = {
> > +	0xfc, 0x4b, 0xbc, 0x00, 0x00, 0x00
> > +};
> > +
> > +/*********************************************************************
> > + *
> > + * net_device_ops
> > + *
> > + **********************************************************************/
> > +static int ethernet_open(struct net_device *ndev)
> > +{
> > +	struct sp_mac *mac = netdev_priv(ndev);
> > +
> > +	netdev_dbg(ndev, "Open port = %x\n", mac->lan_port);
> > +
> > +	mac->comm->enable |= mac->lan_port;
> > +
> > +	hal_mac_start(mac);
> > +	write_sw_int_mask0(mac, read_sw_int_mask0(mac) & ~(MAC_INT_TX | MAC_INT_RX));
> > +
> > +	netif_carrier_on(ndev);
> > +	if (netif_carrier_ok(ndev))
> > +		netif_start_queue(ndev);
> > +
> > +	return 0;
> > +}
> > +
> > +static int ethernet_stop(struct net_device *ndev)
> > +{
> > +	struct sp_mac *mac = netdev_priv(ndev);
> > +
> > +	netif_stop_queue(ndev);
> > +	netif_carrier_off(ndev);
> > +
> > +	mac->comm->enable &= ~mac->lan_port;
> > +
> > +	hal_mac_stop(mac);
> > +
> > +	return 0;
> > +}
> > +
> > +/* Transmit a packet (called by the kernel) */
> > +static int ethernet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
> > +{
> > +	struct sp_mac *mac = netdev_priv(ndev);
> > +	struct sp_common *comm = mac->comm;
> > +	u32 tx_pos;
> > +	u32 cmd1;
> > +	u32 cmd2;
> > +	struct mac_desc *txdesc;
> > +	struct skb_info *skbinfo;
> > +	unsigned long flags;
> > +
> > +	if (unlikely(comm->tx_desc_full == 1)) {
> > +		// No TX descriptors left. Wait for tx interrupt.
> > +		netdev_info(ndev, "TX descriptor queue full when xmit!\n");
> > +		return NETDEV_TX_BUSY;
> Do you really have to return NETDEV_TX_BUSY?

(tx_desc_full == 1) means there is no TX descriptor left in ring buffer.
So there is no way to do new transmit. Return 'busy' directly.
I am not sure if this is a correct process or not.
Could you please teach is there any other way to take care of this case?
Drop directly?


> > +	}
> > +
> > +	/* if skb size shorter than 60, fill it with '\0' */
> > +	if (unlikely(skb->len < ETH_ZLEN)) {
> > +		if (skb_tailroom(skb) >= (ETH_ZLEN - skb->len)) {
> > +			memset(__skb_put(skb, ETH_ZLEN - skb->len), '\0',
> > +			       ETH_ZLEN - skb->len);
> > +		} else {
> > +			struct sk_buff *old_skb = skb;
> > +
> > +			skb = dev_alloc_skb(ETH_ZLEN + TX_OFFSET);
> > +			if (skb) {
> > +				memset(skb->data + old_skb->len, '\0',
> > +				       ETH_ZLEN - old_skb->len);
> > +				memcpy(skb->data, old_skb->data, old_skb->len);
> > +				skb_put(skb, ETH_ZLEN);	/* add data to an sk_buff */
> > +				dev_kfree_skb_irq(old_skb);
> > +			} else {
> > +				skb = old_skb;
> > +			}
> > +		}
> > +	}
> > +
> > +	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->len = skb->len;
> > +	skbinfo->skb = skb;
> > +	skbinfo->mapping = dma_map_single(&comm->pdev->dev, skb->data,
> > +					  skb->len, DMA_TO_DEVICE);
> it may fail

Yes, I'll add error check in next patch as shown below:

	skbinfo->mapping = dma_map_single(&comm->pdev->dev, skb->data,
					  skb->len, DMA_TO_DEVICE);
	if (dma_mapping_error(&comm->pdev->dev, skbinfo->mapping)) {
		ndev->stats.tx_errors++;
		skbinfo->mapping = 0;
		dev_kfree_skb_irq(skb);
		skbinfo->skb = NULL;
		goto xmit_drop;
	}

[...]

	/* trigger mac to transmit */
	hal_tx_trigger(mac);

xmit_drop:
	spin_unlock_irqrestore(&mac->comm->tx_lock, flags);
	return NETDEV_TX_OK;
}

> > +	cmd1 = (OWN_BIT | FS_BIT | LS_BIT | (mac->to_vlan << 12) | (skb->len & LEN_MASK));
> > +	cmd2 = skb->len & LEN_MASK;
> > +
> > +	if (tx_pos == (TX_DESC_NUM - 1))
> > +		cmd2 |= EOR_BIT;
> > +
> > +	txdesc->addr1 = skbinfo->mapping;
> > +	txdesc->cmd2 = cmd2;
> > +	wmb();	// Set OWN_BIT after other fields of descriptor are effective.
> > +	txdesc->cmd1 = cmd1;
> > +
> > +	NEXT_TX(tx_pos);
> > +
> > +	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 gmac to transmit */
> > +	hal_tx_trigger(mac);
> > +
> > +	spin_unlock_irqrestore(&mac->comm->tx_lock, flags);
> > +	return NETDEV_TX_OK;
> > +}
> > +
> > +static void ethernet_set_rx_mode(struct net_device *ndev)
> > +{
> > +	if (ndev) {
> > +		struct sp_mac *mac = netdev_priv(ndev);
> > +		struct sp_common *comm = mac->comm;
> > +		unsigned long flags;
> > +
> > +		spin_lock_irqsave(&comm->ioctl_lock, flags);
> > +		hal_rx_mode_set(ndev);
> > +		spin_unlock_irqrestore(&comm->ioctl_lock, flags);
> > +	}
> > +}
> > +
> > +static int ethernet_set_mac_address(struct net_device *ndev, void *addr)
> > +{
> > +	struct sockaddr *hwaddr = (struct sockaddr *)addr;
> > +	struct sp_mac *mac = netdev_priv(ndev);
> > +
> > +	if (netif_running(ndev))
> > +		return -EBUSY;
> > +
> > +	memcpy(ndev->dev_addr, hwaddr->sa_data, ndev->addr_len);
> > +
> > +	/* Delete the old Ethernet MAC address */
> > +	netdev_dbg(ndev, "HW Addr = %pM\n", mac->mac_addr);
> > +	if (is_valid_ether_addr(mac->mac_addr))
> > +		hal_mac_addr_del(mac);
> > +
> > +	/* Set the Ethernet MAC address */
> > +	memcpy(mac->mac_addr, hwaddr->sa_data, ndev->addr_len);
> > +	hal_mac_addr_set(mac);
> > +
> > +	return 0;
> > +}
> > +
> > +static int ethernet_do_ioctl(struct net_device *ndev, struct ifreq *ifr, int cmd)
> > +{
> > +	struct sp_mac *mac = netdev_priv(ndev);
> > +
> > +	switch (cmd) {
> > +	case SIOCGMIIPHY:
> > +	case SIOCGMIIREG:
> > +	case SIOCSMIIREG:
> > +		return phy_mii_ioctl(mac->phy_dev, ifr, cmd);
> > +	}
> > +
> > +	return -EOPNOTSUPP;
> > +}
> > +
> > +static void ethernet_tx_timeout(struct net_device *ndev, unsigned int txqueue)
> > +{
> > +}
> the empty function?

I'll add tx_timeout function in next patch as shown below:

static void ethernet_tx_timeout(struct net_device *ndev, unsigned int txqueue)
{
	struct sp_mac *mac = netdev_priv(ndev);
	struct net_device *ndev2;
	unsigned long flags;

	netdev_err(ndev, "TX timed out!\n");
	ndev->stats.tx_errors++;

	spin_lock_irqsave(&mac->comm->tx_lock, flags);
	netif_stop_queue(ndev);
	ndev2 = mac->next_ndev;
	if (ndev2)
		netif_stop_queue(ndev2);

	hal_mac_stop(mac);
	hal_mac_init(mac);
	hal_mac_start(mac);

	// Accept TX packets again.
	netif_trans_update(ndev);
	netif_wake_queue(ndev);
	if (ndev2) {
		netif_trans_update(ndev2);
		netif_wake_queue(ndev2);
	}

	spin_unlock_irqrestore(&mac->comm->tx_lock, flags);
}

Is that ok?


> > +static struct net_device_stats *ethernet_get_stats(struct net_device *ndev)
> > +{
> > +	struct sp_mac *mac;
> > +
> > +	mac = netdev_priv(ndev);
> > +	return &mac->dev_stats;
> > +}
> > +
> > +static const struct net_device_ops netdev_ops = {
> > +	.ndo_open = ethernet_open,
> > +	.ndo_stop = ethernet_stop,
> > +	.ndo_start_xmit = ethernet_start_xmit,
> > +	.ndo_set_rx_mode = ethernet_set_rx_mode,
> > +	.ndo_set_mac_address = ethernet_set_mac_address,
> > +	.ndo_do_ioctl = ethernet_do_ioctl,
> > +	.ndo_tx_timeout = ethernet_tx_timeout,
> > +	.ndo_get_stats = ethernet_get_stats,
> > +};
> > +
> > +char *sp7021_otp_read_mac(struct device *dev, ssize_t *len, char *name)
> > +{
> > +	char *ret = NULL;
> > +	struct nvmem_cell *cell = nvmem_cell_get(dev, name);
> > +
> > +	if (IS_ERR_OR_NULL(cell)) {
> > +		dev_err(dev, "OTP %s read failure: %ld", name, PTR_ERR(cell));
> > +		return NULL;
> > +	}
> > +
> > +	ret = nvmem_cell_read(cell, len);
> > +	nvmem_cell_put(cell);
> > +	dev_dbg(dev, "%zd bytes are read from OTP %s.", *len, name);
> > +
> > +	return ret;
> > +}
> > +
> > +static void check_mac_vendor_id_and_convert(char *mac_addr)
> > +{
> > +	// Byte order of MAC address of some samples are reversed.
> > +	// Check vendor id and convert byte order if it is wrong.
> > +	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))) {
> > +		char tmp;
> > +
> > +		// 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;
> > +	}
> > +}
> > +
> > +/*********************************************************************
> > + *
> > + * platform_driver
> > + *
> > + **********************************************************************/
> > +static u32 init_netdev(struct platform_device *pdev, int eth_no, struct net_device **r_ndev)
> > +{
> > +	struct sp_mac *mac;
> > +	struct net_device *ndev;
> > +	char *m_addr_name = (eth_no == 0) ? "mac_addr0" : "mac_addr1";
> > +	ssize_t otp_l = 0;
> > +	char *otp_v;
> > +	int ret;
> > +
> > +	// Allocate the devices, and also allocate sp_mac, we can get it by netdev_priv().
> > +	ndev = alloc_etherdev(sizeof(*mac));
> > +	if (!ndev) {
> > +		*r_ndev = NULL;
> > +		return -ENOMEM;
> please check the function return value

I am not sure the meaning of this comment.
Do you mean to modify:

	// Initialize the 1st net device.
	ret = init_netdev(pdev, 0, &ndev);
	if (!ndev)
		return ret;

to:

	// Initialize the 1st net device.
	ret = init_netdev(pdev, 0, &ndev);
	if (ret)
		return ret;

?


> > +	}
> > +	SET_NETDEV_DEV(ndev, &pdev->dev);
> > +	ndev->netdev_ops = &netdev_ops;
> > +
> > +	mac = netdev_priv(ndev);
> > +	mac->ndev = ndev;
> > +	mac->next_ndev = NULL;
> > +
> > +	// Get property 'mac-addr0' or 'mac-addr1' from dts.
> > +	otp_v = sp7021_otp_read_mac(&pdev->dev, &otp_l, m_addr_name);
> > +	if ((otp_l < 6) || IS_ERR_OR_NULL(otp_v)) {
> > +		dev_info(&pdev->dev, "OTP mac %s (len = %zd) is invalid, using default!\n",
> > +			 m_addr_name, otp_l);
> > +		otp_l = 0;
> > +	} else {
> > +		// Check if mac-address is valid or not. If not, copy from default.
> > +		memcpy(mac->mac_addr, otp_v, 6);
> > +
> > +		// Byte order of Some samples are reversed. Convert byte order here.
> > +		check_mac_vendor_id_and_convert(mac->mac_addr);
> > +
> > +		if (!is_valid_ether_addr(mac->mac_addr)) {
> > +			dev_info(&pdev->dev, "Invalid mac in OTP[%s] = %pM, use default!\n",
> > +				 m_addr_name, mac->mac_addr);
> > +			otp_l = 0;
> > +		}
> > +	}
> > +	if (otp_l != 6) {
> > +		memcpy(mac->mac_addr, def_mac_addr, ETHERNET_MAC_ADDR_LEN);
> > +		mac->mac_addr[5] += eth_no;
> > +	}
> > +
> > +	dev_info(&pdev->dev, "HW Addr = %pM\n", mac->mac_addr);
> > +
> > +	memcpy(ndev->dev_addr, mac->mac_addr, ETHERNET_MAC_ADDR_LEN);
> > +
> > +	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_info(ndev, "Registered net device \"%s\" successfully.\n", ndev->name);
> > +
> > +	*r_ndev = ndev;
> > +	return 0;
> > +}
> > +
> > +static int soc0_open(struct sp_mac *mac)
> > +{
> > +	struct sp_common *comm = mac->comm;
> > +	u32 ret;
> > +
> > +	hal_mac_stop(mac);
> > +
> > +	ret = descs_init(comm);
> > +	if (ret) {
> > +		netdev_err(mac->ndev, "Fail to initialize mac descriptors!\n");
> > +		descs_free(comm);
> > +		return ret;
> > +	}
> > +
> > +	mac_init(mac);
> > +	return 0;
> > +}
> > +
> > +static int soc0_stop(struct sp_mac *mac)
> > +{
> > +	hal_mac_stop(mac);
> > +
> > +	descs_free(mac->comm);
> > +	return 0;
> > +}
> > +
> > +static int sp_probe(struct platform_device *pdev)
> > +{
> > +	struct sp_common *comm;
> > +	struct resource *rc;
> > +	struct net_device *ndev, *ndev2;
> > +	struct device_node *np;
> > +	struct sp_mac *mac, *mac2;
> > +	int ret;
> > +
> > +	if (platform_get_drvdata(pdev))
> > +		return -ENODEV;
> > +
> > +	// Allocate memory for 'sp_common' area.
> > +	comm = devm_kzalloc(&pdev->dev, sizeof(*comm), GFP_KERNEL);
> > +	if (!comm)
> > +		return -ENOMEM;
> > +	comm->pdev = pdev;
> > +
> > +	spin_lock_init(&comm->rx_lock);
> > +	spin_lock_init(&comm->tx_lock);
> > +	spin_lock_init(&comm->ioctl_lock);
> > +
> > +	// Get memory resoruce "emac" from dts.
> > +	rc = platform_get_resource_byname(pdev, IORESOURCE_MEM, "emac");
> > +	if (!rc) {
> > +		dev_err(&pdev->dev, "No MEM resource \'emac\' found!\n");
> > +		return -ENXIO;
> > +	}
> > +	dev_dbg(&pdev->dev, "name = \"%s\", start = %pa\n", rc->name, &rc->start);
> > +
> > +	comm->sp_reg_base = devm_ioremap_resource(&pdev->dev, rc);
> > +	if (IS_ERR(comm->sp_reg_base)) {
> > +		dev_err(&pdev->dev, "ioremap failed!\n");
> > +		return -ENOMEM;
> > +	}
> > +
> > +	// Get memory resoruce "moon5" from dts.
> > +	rc = platform_get_resource_byname(pdev, IORESOURCE_MEM, "moon5");
> > +	if (!rc) {
> > +		dev_err(&pdev->dev, "No MEM resource \'moon5\' found!\n");
> > +		return -ENXIO;
> > +	}
> > +	dev_dbg(&pdev->dev, "name = \"%s\", start = %pa\n", rc->name, &rc->start);
> > +
> > +	// Note that moon5 is shared resource. Don't use devm_ioremap_resource().
> > +	comm->moon5_reg_base = devm_ioremap(&pdev->dev, rc->start, rc->end - rc->start + 1);
> > +	if (IS_ERR(comm->moon5_reg_base)) {
> > +		dev_err(&pdev->dev, "ioremap failed!\n");
> > +		return -ENOMEM;
> > +	}
> > +
> > +	// Get irq resource from dts.
> > +	ret = platform_get_irq(pdev, 0);
> > +	if (ret < 0)
> > +		return ret;
> > +	comm->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.
> > +	clk_prepare_enable(comm->clk);
> > +	udelay(1);
> > +
> > +	reset_control_assert(comm->rstc);
> > +	udelay(1);
> > +	reset_control_deassert(comm->rstc);
> > +	udelay(1);
> > +
> > +	// Initialize the 1st net device.
> > +	ret = init_netdev(pdev, 0, &ndev);
> > +	if (!ndev)
> > +		return ret;
> > +
> > +	platform_set_drvdata(pdev, ndev);
> > +
> > +	ndev->irq = comm->irq;
> > +	mac = netdev_priv(ndev);
> > +	mac->comm = comm;
> > +	comm->ndev = ndev;
> > +
> > +	// Get node of phy 1.
> > +	mac->phy_node = of_parse_phandle(pdev->dev.of_node, "phy-handle1", 0);
> > +	if (!mac->phy_node) {
> > +		netdev_info(ndev, "Cannot get node of phy 1!\n");
> > +		ret = -ENODEV;
> > +		goto out_unregister_dev;
> > +	}
> > +
> > +	// Get address of phy from dts.
> > +	if (of_property_read_u32(mac->phy_node, "reg", &mac->phy_addr)) {
> > +		mac->phy_addr = 0;
> > +		netdev_info(ndev, "Cannot get address of phy 1! Set to 0.\n");
> > +	}
> > +
> > +	// Get mode of phy from dts.
> > +	if (of_get_phy_mode(mac->phy_node, &mac->phy_mode)) {
> > +		mac->phy_mode = PHY_INTERFACE_MODE_RGMII_ID;
> > +		netdev_info(ndev, "Missing phy-mode of phy 1! Set to \'rgmii-id\'.\n");
> > +	}
> > +
> > +	// Request irq.
> > +	ret = devm_request_irq(&pdev->dev, comm->irq, ethernet_interrupt, 0,
> > +			       ndev->name, ndev);
> > +	if (ret) {
> > +		netdev_err(ndev, "Failed to request irq #%d for \"%s\"!\n",
> > +			   ndev->irq, ndev->name);
> > +		goto out_unregister_dev;
> > +	}
> > +
> > +	mac->cpu_port = 0x1;	// soc0
> > +	mac->lan_port = 0x1;	// forward to port 0
> > +	mac->to_vlan = 0x1;	// vlan group: 0
> > +	mac->vlan_id = 0x0;	// vlan group: 0
> > +
> > +	// Set MAC address
> > +	hal_mac_addr_set(mac);
> > +	hal_rx_mode_set(ndev);
> > +	hal_mac_addr_table_del_all(mac);
> > +
> > +	ndev2 = NULL;
> > +	np = of_parse_phandle(pdev->dev.of_node, "phy-handle2", 0);
> > +	if (np) {
> > +		init_netdev(pdev, 1, &ndev2);
> > +		if (ndev2) {
> > +			mac->next_ndev = ndev2; // Point to the second net device.
> > +
> > +			ndev2->irq = comm->irq;
> > +			mac2 = netdev_priv(ndev2);
> > +			mac2->comm = comm;
> > +			mac2->phy_node = np;
> > +
> > +			if (of_property_read_u32(mac2->phy_node, "reg", &mac2->phy_addr)) {
> > +				mac2->phy_addr = 1;
> > +				netdev_info(ndev2, "Cannot get address of phy 2! Set to 1.\n");
> > +			}
> > +
> > +			if (of_get_phy_mode(mac2->phy_node, &mac2->phy_mode)) {
> > +				mac2->phy_mode = PHY_INTERFACE_MODE_RGMII_ID;
> > +				netdev_info(ndev, "Missing phy-mode phy 2! Set to \'rgmii-id\'.\n");
> > +			}
> > +
> > +			mac2->cpu_port = 0x1;	// soc0
> > +			mac2->lan_port = 0x2;	// forward to port 1
> > +			mac2->to_vlan = 0x2;	// vlan group: 1
> > +			mac2->vlan_id = 0x1;	// vlan group: 1
> > +
> > +			hal_mac_addr_set(mac2);	// Set MAC address for the 2nd net device.
> > +			hal_rx_mode_set(ndev2);
> > +		}
> > +	}
> > +
> > +	soc0_open(mac);
> > +	hal_set_rmii_tx_rx_pol(mac);
> > +	hal_phy_addr(mac);
> > +
> > +	ret = mdio_init(pdev, ndev);
> > +	if (ret) {
> > +		netdev_err(ndev, "Failed to initialize mdio!\n");
> > +		goto out_unregister_dev;
> > +	}
> > +
> > +	ret = sp_phy_probe(ndev);
> > +	if (ret) {
> > +		netdev_err(ndev, "Failed to probe phy!\n");
> > +		goto out_freemdio;
> > +	}
> > +
> > +	if (ndev2) {
> > +		ret = sp_phy_probe(ndev2);
> > +		if (ret) {
> > +			netdev_err(ndev2, "Failed to probe phy!\n");
> > +			unregister_netdev(ndev2);
> > +			mac->next_ndev = 0;
> > +		}
> > +	}
> > +
> > +	netif_napi_add(ndev, &comm->rx_napi, rx_poll, RX_NAPI_WEIGHT);
> > +	napi_enable(&comm->rx_napi);
> > +	netif_napi_add(ndev, &comm->tx_napi, tx_poll, TX_NAPI_WEIGHT);
> > +	napi_enable(&comm->tx_napi);
> > +	return 0;
> > +
> > +out_freemdio:
> > +	if (comm->mii_bus)
> > +		mdio_remove(ndev);
> > +
> > +out_unregister_dev:
> > +	unregister_netdev(ndev);
> > +	if (ndev2)
> > +		unregister_netdev(ndev2);
> > +
> > +	return ret;
> > +}
> > +
> > +static int sp_remove(struct platform_device *pdev)
> > +{
> > +	struct net_device *ndev, *ndev2;
> > +	struct sp_mac *mac;
> > +
> > +	ndev = platform_get_drvdata(pdev);
> > +	if (!ndev)
> > +		return 0;
> > +
> > +	mac = netdev_priv(ndev);
> > +
> > +	// Unregister and free 2nd net device.
> > +	ndev2 = mac->next_ndev;
> > +	if (ndev2) {
> > +		sp_phy_remove(ndev2);
> > +		unregister_netdev(ndev2);
> > +		free_netdev(ndev2);
> > +	}
> > +
> > +	mac->comm->enable = 0;
> > +	soc0_stop(mac);
> > +
> > +	// Disable and delete napi.
> > +	napi_disable(&mac->comm->rx_napi);
> > +	netif_napi_del(&mac->comm->rx_napi);
> > +	napi_disable(&mac->comm->tx_napi);
> > +	netif_napi_del(&mac->comm->tx_napi);
> > +
> > +	sp_phy_remove(ndev);
> > +	mdio_remove(ndev);
> > +
> > +	// Unregister and free 1st net device.
> > +	unregister_netdev(ndev);
> > +	free_netdev(ndev);
> > +
> > +	clk_disable(mac->comm->clk);
> > +
> > +	return 0;
> > +}
> > +
> > +static const struct of_device_id sp_of_match[] = {
> > +	{.compatible = "sunplus,sp7021-emac"},
> > +	{ /* sentinel */ }
> > +};
> > +
> > +MODULE_DEVICE_TABLE(of, sp_of_match);
> > +
> > +static struct platform_driver sp_driver = {
> > +	.probe = sp_probe,
> > +	.remove = sp_remove,
> > +	.driver = {
> > +		.name = "sp7021_emac",
> > +		.owner = THIS_MODULE,
> > +		.of_match_table = sp_of_match,
> > +	},
> > +};
> > +
> > +module_platform_driver(sp_driver);
> > +
> > +MODULE_AUTHOR("Wells Lu <wells.lu@xxxxxxxxxxx>");
> > +MODULE_DESCRIPTION("Sunplus Dual 10M/100M Ethernet driver");
> > +MODULE_LICENSE("GPL v2");
> > diff --git a/drivers/net/ethernet/sunplus/sp_driver.h b/drivers/net/ethernet/sunplus/sp_driver.h
> > new file mode 100644
> > index 0000000..ea80a9e
> > --- /dev/null
> > +++ b/drivers/net/ethernet/sunplus/sp_driver.h
> > @@ -0,0 +1,23 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/* Copyright Sunplus Technology Co., Ltd.
> > + *       All rights reserved.
> > + */
> > +
> > +#ifndef __SP_DRIVER_H__
> > +#define __SP_DRIVER_H__
> > +
> > +#include "sp_define.h"
> > +#include "sp_register.h"
> > +#include "sp_hal.h"
> > +#include "sp_int.h"
> > +#include "sp_mdio.h"
> > +#include "sp_mac.h"
> > +#include "sp_desc.h"
> > +
> > +#define NEXT_TX(N)              ((N) = (((N) + 1) == TX_DESC_NUM) ? 0 : (N) + 1)
> > +#define NEXT_RX(QUEUE, N)       ((N) = (((N) + 1) == mac->comm->rx_desc_num[QUEUE]) ? 0 :
> (N) + 1)
> > +
> > +#define RX_NAPI_WEIGHT          16
> > +#define TX_NAPI_WEIGHT          16
> > +
> > +#endif
> > diff --git a/drivers/net/ethernet/sunplus/sp_hal.c b/drivers/net/ethernet/sunplus/sp_hal.c
> > new file mode 100644
> > index 0000000..a7f06d7
> > --- /dev/null
> > +++ b/drivers/net/ethernet/sunplus/sp_hal.c
> > @@ -0,0 +1,331 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/* Copyright Sunplus Technology Co., Ltd.
> > + *       All rights reserved.
> > + */
> > +
> > +#include <linux/iopoll.h>
> > +#include "sp_hal.h"
> > +
> > +void hal_mac_stop(struct sp_mac *mac)
> > +{
> > +	struct sp_common *comm = mac->comm;
> > +	u32 reg, disable;
> > +
> > +	if (comm->enable == 0) {
> > +		// Mask and clear all interrupts, except PORT_ST_CHG.
> > +		write_sw_int_mask0(mac, 0xffffffff);
> > +		writel(0xffffffff & (~MAC_INT_PORT_ST_CHG),
> > +		       comm->sp_reg_base + SP_SW_INT_STATUS_0);
> > +
> > +		// Disable cpu 0 and cpu 1.
> > +		reg = readl(comm->sp_reg_base + SP_CPU_CNTL);
> > +		writel((0x3 << 6) | reg, comm->sp_reg_base + SP_CPU_CNTL);
> > +	}
> > +
> > +	// Disable lan 0 and lan 1.
> > +	disable = ((~comm->enable) & 0x3) << 24;
> > +	reg = readl(comm->sp_reg_base + SP_PORT_CNTL0);
> > +	writel(disable | reg, comm->sp_reg_base + SP_PORT_CNTL0);
> > +}
> > +
> > +void hal_mac_reset(struct sp_mac *mac)
> > +{
> > +}
> Thre is no need for the empty function

I'll remove the empty function in next patch.


> > +
> > +void hal_mac_start(struct sp_mac *mac)
> > +{
> > +	struct sp_common *comm = mac->comm;
> > +	u32 reg;
> > +
> > +	// Enable cpu port 0 (6) & port 0 crc padding (8)
> > +	reg = readl(comm->sp_reg_base + SP_CPU_CNTL);
> > +	writel((reg & (~(0x1 << 6))) | (0x1 << 8), comm->sp_reg_base + SP_CPU_CNTL);
> > +
> > +	// Enable lan 0 & lan 1
> > +	reg = readl(comm->sp_reg_base + SP_PORT_CNTL0);
> > +	writel(reg & (~(comm->enable << 24)), comm->sp_reg_base + SP_PORT_CNTL0);
> > +}
> > +
> > +void hal_mac_addr_set(struct sp_mac *mac)
> > +{
> > +	struct sp_common *comm = mac->comm;
> > +	u32 reg;
> > +
> > +	// Write MAC address.
> > +	writel(mac->mac_addr[0] + (mac->mac_addr[1] << 8),
> > +	       comm->sp_reg_base + SP_W_MAC_15_0);
> > +	writel(mac->mac_addr[2] + (mac->mac_addr[3] << 8) + (mac->mac_addr[4] << 16) +
> > +	      (mac->mac_addr[5] << 24),	comm->sp_reg_base + SP_W_MAC_47_16);
> > +
> > +	// Set aging=1
> > +	writel((mac->cpu_port << 10) + (mac->vlan_id << 7) + (1 << 4) + 0x1,
> > +	       comm->sp_reg_base + SP_WT_MAC_AD0);
> > +
> > +	// Wait for completing.
> > +	do {
> > +		reg = readl(comm->sp_reg_base + SP_WT_MAC_AD0);
> > +		ndelay(10);
> > +		netdev_dbg(mac->ndev, "wt_mac_ad0 = %08x\n", reg);
> > +	} while ((reg & (0x1 << 1)) == 0x0);
> > +
> > +	netdev_dbg(mac->ndev, "mac_ad0 = %08x, mac_ad = %08x%04x\n",
> > +		   readl(comm->sp_reg_base + SP_WT_MAC_AD0),
> > +		   readl(comm->sp_reg_base + SP_W_MAC_47_16),
> > +		   readl(comm->sp_reg_base + SP_W_MAC_15_0) & 0xffff);
> > +}
> > +
> > +void hal_mac_addr_del(struct sp_mac *mac)
> > +{
> > +	struct sp_common *comm = mac->comm;
> > +	u32 reg;
> > +
> > +	// Write MAC address.
> > +	writel(mac->mac_addr[0] + (mac->mac_addr[1] << 8),
> > +	       comm->sp_reg_base + SP_W_MAC_15_0);
> > +	writel(mac->mac_addr[2] + (mac->mac_addr[3] << 8) + (mac->mac_addr[4] << 16) +
> > +	       (mac->mac_addr[5] << 24), comm->sp_reg_base + SP_W_MAC_47_16);
> > +
> > +	// Wait for completing.
> > +	writel((0x1 << 12) + (mac->vlan_id << 7) + 0x1,
> > +	       comm->sp_reg_base + SP_WT_MAC_AD0);
> > +	do {
> > +		reg = readl(comm->sp_reg_base + SP_WT_MAC_AD0);
> > +		ndelay(10);
> > +		netdev_dbg(mac->ndev, "wt_mac_ad0 = %08x\n", reg);
> > +	} while ((reg & (0x1 << 1)) == 0x0);
> > +
> > +	netdev_dbg(mac->ndev, "mac_ad0 = %08x, mac_ad = %08x%04x\n",
> > +		   readl(comm->sp_reg_base + SP_WT_MAC_AD0),
> > +		   readl(comm->sp_reg_base + SP_W_MAC_47_16),
> > +		   readl(comm->sp_reg_base + SP_W_MAC_15_0) & 0xffff);
> > +}
> > +
> > +void hal_mac_addr_table_del_all(struct sp_mac *mac)
> > +{
> > +	struct sp_common *comm = mac->comm;
> > +	u32 reg;
> > +
> > +	// Wait for address table being idle.
> > +	do {
> > +		reg = readl(comm->sp_reg_base + SP_ADDR_TBL_SRCH);
> > +		ndelay(10);
> > +	} while (!(reg & MAC_ADDR_LOOKUP_IDLE));
> > +
> > +	// Search address table from start.
> > +	writel(readl(comm->sp_reg_base + SP_ADDR_TBL_SRCH) | MAC_BEGIN_SEARCH_ADDR,
> > +	       comm->sp_reg_base + SP_ADDR_TBL_SRCH);
> > +	while (1) {
> > +		do {
> > +			reg = readl(comm->sp_reg_base + SP_ADDR_TBL_ST);
> > +			ndelay(10);
> > +			netdev_dbg(mac->ndev, "addr_tbl_st = %08x\n", reg);
> > +		} while (!(reg & (MAC_AT_TABLE_END | MAC_AT_DATA_READY)));
> > +
> > +		if (reg & MAC_AT_TABLE_END)
> > +			break;
> > +
> > +		netdev_dbg(mac->ndev, "addr_tbl_st = %08x\n", reg);
> > +		netdev_dbg(mac->ndev, "@AT #%u: port=%01x, cpu=%01x, vid=%u, aging=%u, proxy=%u,
> mc_ingress=%u\n",
> > +			   (reg >> 22) & 0x3ff, (reg >> 12) & 0x3, (reg >> 10) & 0x3,
> > +			   (reg >> 7) & 0x7, (reg >> 4) & 0x7, (reg >> 3) & 0x1,
> > +			   (reg >> 2) & 0x1);
> > +
> > +		// Delete all entries which are learnt from lan ports.
> > +		if ((reg >> 12) & 0x3) {
> > +			writel(readl(comm->sp_reg_base + SP_MAC_AD_SER0),
> > +			       comm->sp_reg_base + SP_W_MAC_15_0);
> > +			writel(readl(comm->sp_reg_base + SP_MAC_AD_SER1),
> > +			       comm->sp_reg_base + SP_W_MAC_47_16);
> > +
> > +			writel((0x1 << 12) + (reg & (0x7 << 7)) + 0x1,
> > +			       comm->sp_reg_base + SP_WT_MAC_AD0);
> > +			do {
> > +				reg = readl(comm->sp_reg_base + SP_WT_MAC_AD0);
> > +				ndelay(10);
> > +				netdev_dbg(mac->ndev, "wt_mac_ad0 = %08x\n", reg);
> > +			} while ((reg & (0x1 << 1)) == 0x0);
> > +			netdev_dbg(mac->ndev, "mac_ad0 = %08x, mac_ad = %08x%04x\n",
> > +				   readl(comm->sp_reg_base + SP_WT_MAC_AD0),
> > +				   readl(comm->sp_reg_base + SP_W_MAC_47_16),
> > +				   readl(comm->sp_reg_base + SP_W_MAC_15_0) & 0xffff);
> > +		}
> > +
> > +		// Search next.
> > +		writel(readl(comm->sp_reg_base + SP_ADDR_TBL_SRCH) | MAC_SEARCH_NEXT_ADDR,
> > +		       comm->sp_reg_base + SP_ADDR_TBL_SRCH);
> > +	}
> > +}
> > +
> > +void hal_mac_init(struct sp_mac *mac)
> > +{
> > +	struct sp_common *comm = mac->comm;
> > +	u32 reg;
> > +
> > +	// Disable cpu0 and cpu 1.
> > +	reg = readl(comm->sp_reg_base + SP_CPU_CNTL);
> > +	writel((0x3 << 6) | reg, comm->sp_reg_base + SP_CPU_CNTL);
> Would be nice to see a constant

I'll modify in next patch as shown below:

reg = readl(comm->sp_reg_base + SP_CPU_CNTL);
reg |= MAC_DIS_SOC1_CPU | MAC_DIS_SOC0_CPU;
writel(reg, comm->sp_reg_base + SP_CPU_CNTL);


> > +	// Descriptor base address
> > +	writel(mac->comm->desc_dma, comm->sp_reg_base + SP_TX_LBASE_ADDR_0);
> > +	writel(mac->comm->desc_dma + sizeof(struct mac_desc) * TX_DESC_NUM,
> > +	       comm->sp_reg_base + SP_TX_HBASE_ADDR_0);
> > +	writel(mac->comm->desc_dma + sizeof(struct mac_desc) * (TX_DESC_NUM +
> > +	       MAC_GUARD_DESC_NUM), comm->sp_reg_base + SP_RX_HBASE_ADDR_0);
> > +	writel(mac->comm->desc_dma + sizeof(struct mac_desc) * (TX_DESC_NUM +
> > +	       MAC_GUARD_DESC_NUM + RX_QUEUE0_DESC_NUM),
> > +	       comm->sp_reg_base + SP_RX_LBASE_ADDR_0);
> > +
> > +	// Fc_rls_th=0x4a, Fc_set_th=0x3a, Drop_rls_th=0x2d, Drop_set_th=0x1d
> > +	writel(0x4a3a2d1d, comm->sp_reg_base + SP_FL_CNTL_TH);
> > +
> > +	// Cpu_rls_th=0x4a, Cpu_set_th=0x3a, Cpu_th=0x12, Port_th=0x12
> > +	writel(0x4a3a1212, comm->sp_reg_base + SP_CPU_FL_CNTL_TH);
> > +
> > +	// mtcc_lmt=0xf, Pri_th_l=6, Pri_th_h=6, weigh_8x_en=1
> > +	writel(0xf6680000, comm->sp_reg_base + SP_PRI_FL_CNTL);
> > +
> > +	// High-active LED
> > +	reg = readl(comm->sp_reg_base + SP_LED_PORT0);
> > +	writel(reg | (1 << 28), comm->sp_reg_base + SP_LED_PORT0);
> ditto

I'll modify in next patch as shown below:

reg = readl(comm->sp_reg_base + SP_LED_PORT0);
reg |= MAC_LED_ACT_HI;
writel(reg, comm->sp_reg_base + SP_LED_PORT0);


> > +	// Disable cpu port0 aging (12)
> > +	// Disable cpu port0 learning (14)
> > +	// Enable UC and MC packets
> > +	reg = readl(comm->sp_reg_base + SP_CPU_CNTL);
> > +	writel((reg & (~((0x1 << 14) | (0x3c << 0)))) | (0x1 << 12),
> ditto

I'll modify in next patch as shown below:

reg = readl(comm->sp_reg_base + SP_CPU_CNTL);
reg &= ~(MAC_EN_SOC0_AGING |
MAC_DIS_BC2CPU_P1 | MAC_DIS_BC2CPU_P0 |
MAC_DIS_MC2CPU_P1 | MAC_DIS_MC2CPU_P0);
reg |= MAC_DIS_LRN_SOC0;
writel(reg, comm->sp_reg_base + SP_CPU_CNTL);


> > +	       comm->sp_reg_base + SP_CPU_CNTL);
> > +
> > +	// Disable lan port SA learning.
> > +	reg = readl(comm->sp_reg_base + SP_PORT_CNTL1);
> > +	writel(reg | (0x3 << 8), comm->sp_reg_base + SP_PORT_CNTL1);
> ditto

I'll modify in next patch as shown below:

reg = readl(comm->sp_reg_base + SP_PORT_CNTL1);
reg |= MAC_DIS_SA_LRN_P1 | MAC_DIS_SA_LRN_P0;
writel(reg, comm->sp_reg_base + SP_PORT_CNTL1);


> > +	// Port 0: VLAN group 0
> > +	// Port 1: VLAN group 1
> > +	writel((1 << 4) + 0, comm->sp_reg_base + SP_PVID_CONFIG0);
> > +
> > +	// VLAN group 0: cpu0+port0
> > +	// VLAN group 1: cpu0+port1
> > +	writel((0xa << 8) + 0x9, comm->sp_reg_base + SP_VLAN_MEMSET_CONFIG0);
> > +
> > +	// RMC forward: to cpu
> > +	// LED: 60mS
> > +	// BC storm prev: 31 BC
> > +	reg = readl(comm->sp_reg_base + SP_SW_GLB_CNTL);
> > +	writel((reg & (~((0x3 << 25) | (0x3 << 23) | (0x3 << 4)))) |
> > +	       (0x1 << 25) | (0x1 << 23) | (0x1 << 4),
> > +	       comm->sp_reg_base + SP_SW_GLB_CNTL);
> > +
> > +	write_sw_int_mask0(mac, MAC_INT_MASK_DEF);
> > +}
> > +
> > +void hal_rx_mode_set(struct net_device *ndev)
> > +{
> > +	struct sp_mac *mac = netdev_priv(ndev);
> > +	struct sp_common *comm = mac->comm;
> > +	u32 mask, reg, rx_mode;
> > +
> > +	netdev_dbg(ndev, "ndev->flags = %08x\n", ndev->flags);
> > +
> > +	mask = (mac->lan_port << 2) | (mac->lan_port << 0);
> > +	reg = readl(comm->sp_reg_base + SP_CPU_CNTL);
> > +
> > +	if (ndev->flags & IFF_PROMISC) {	/* Set promiscuous mode */
> > +		// Allow MC and unknown UC packets
> > +		rx_mode = (mac->lan_port << 2) | (mac->lan_port << 0);
> > +	} else if ((!netdev_mc_empty(ndev) && (ndev->flags & IFF_MULTICAST)) ||
> > +		   (ndev->flags & IFF_ALLMULTI)) {
> > +		// Allow MC packets
> > +		rx_mode = (mac->lan_port << 2);
> > +	} else {
> > +		// Disable MC and unknown UC packets
> > +		rx_mode = 0;
> > +	}
> > +
> > +	writel((reg & (~mask)) | ((~rx_mode) & mask), comm->sp_reg_base + SP_CPU_CNTL);
> > +	netdev_dbg(ndev, "cpu_cntl = %08x\n", readl(comm->sp_reg_base + SP_CPU_CNTL));
> > +}
> > +
> > +int hal_mdio_access(struct sp_mac *mac, u8 op_cd, u8 phy_addr, u8 reg_addr, u32 wdata)
> > +{
> > +	struct sp_common *comm = mac->comm;
> > +	u32 val, ret;
> > +
> > +	writel((wdata << 16) | (op_cd << 13) | (reg_addr << 8) | phy_addr,
> > +	       comm->sp_reg_base + SP_PHY_CNTL_REG0);
> > +
> > +	ret = read_poll_timeout(readl, val, val & op_cd, 10, 1000, 1,
> > +				comm->sp_reg_base + SP_PHY_CNTL_REG1);
> > +	if (ret == 0)
> > +		return val >> 16;
> > +	else
> > +		return ret;
> > +}
> > +
> > +void hal_tx_trigger(struct sp_mac *mac)
> > +{
> > +	struct sp_common *comm = mac->comm;
> > +
> > +	writel((0x1 << 1), comm->sp_reg_base + SP_CPU_TX_TRIG);
> > +}
> > +
> > +void hal_set_rmii_tx_rx_pol(struct sp_mac *mac)
> > +{
> > +	struct sp_common *comm = mac->comm;
> > +	u32 reg;
> > +
> > +	// Set polarity of RX and TX of RMII signal.
> > +	reg = readl(comm->moon5_reg_base + MOON5_MO4_L2SW_CLKSW_CTL);
> > +	writel(reg | (0xf << 16) | 0xf, comm->moon5_reg_base + MOON5_MO4_L2SW_CLKSW_CTL);
> > +}
> > +
> > +void hal_phy_addr(struct sp_mac *mac)
> > +{
> > +	struct sp_common *comm = mac->comm;
> > +	u32 reg;
> > +
> > +	// Set address of phy.
> > +	reg = readl(comm->sp_reg_base + SP_MAC_FORCE_MODE);
> > +	reg = (reg & (~(0x1f << 16))) | ((mac->phy_addr & 0x1f) << 16);
> > +	if (mac->next_ndev) {
> > +		struct net_device *ndev2 = mac->next_ndev;
> > +		struct sp_mac *mac2 = netdev_priv(ndev2);
> > +
> > +		reg = (reg & (~(0x1f << 24))) | ((mac2->phy_addr & 0x1f) << 24);
> > +	}
> > +	writel(reg, comm->sp_reg_base + SP_MAC_FORCE_MODE);
> > +}
> > +
> > +u32 read_sw_int_mask0(struct sp_mac *mac)
> > +{
> > +	struct sp_common *comm = mac->comm;
> > +
> > +	return readl(comm->sp_reg_base + SP_SW_INT_MASK_0);
> > +}
> > +
> > +void write_sw_int_mask0(struct sp_mac *mac, u32 value)
> > +{
> > +	struct sp_common *comm = mac->comm;
> > +
> > +	writel(value, comm->sp_reg_base + SP_SW_INT_MASK_0);
> > +}
> > +
> > +void write_sw_int_status0(struct sp_mac *mac, u32 value)
> > +{
> > +	struct sp_common *comm = mac->comm;
> > +
> > +	writel(value, comm->sp_reg_base + SP_SW_INT_STATUS_0);
> > +}
> > +
> > +u32 read_sw_int_status0(struct sp_mac *mac)
> > +{
> > +	struct sp_common *comm = mac->comm;
> > +
> > +	return readl(comm->sp_reg_base + SP_SW_INT_STATUS_0);
> > +}
> > +
> > +u32 read_port_ability(struct sp_mac *mac)
> > +{
> > +	struct sp_common *comm = mac->comm;
> > +
> > +	return readl(comm->sp_reg_base + SP_PORT_ABILITY);
> > +}
> > diff --git a/drivers/net/ethernet/sunplus/sp_hal.h b/drivers/net/ethernet/sunplus/sp_hal.h
> > new file mode 100644
> > index 0000000..f4b1979
> > --- /dev/null
> > +++ b/drivers/net/ethernet/sunplus/sp_hal.h
> > @@ -0,0 +1,31 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/* Copyright Sunplus Technology Co., Ltd.
> > + *       All rights reserved.
> > + */
> > +
> > +#ifndef __SP_HAL_H__
> > +#define __SP_HAL_H__
> > +
> > +#include "sp_register.h"
> > +#include "sp_define.h"
> > +#include "sp_desc.h"
> > +
> > +void hal_mac_stop(struct sp_mac *mac);
> > +void hal_mac_reset(struct sp_mac *mac);
> > +void hal_mac_start(struct sp_mac *mac);
> > +void hal_mac_addr_set(struct sp_mac *mac);
> > +void hal_mac_addr_del(struct sp_mac *mac);
> > +void hal_mac_addr_table_del_all(struct sp_mac *mac);
> > +void hal_mac_init(struct sp_mac *mac);
> > +void hal_rx_mode_set(struct net_device *ndev);
> > +int  hal_mdio_access(struct sp_mac *mac, u8 op_cd, u8 phy_addr, u8 reg_addr, u32 wdata);
> > +void hal_tx_trigger(struct sp_mac *mac);
> > +void hal_set_rmii_tx_rx_pol(struct sp_mac *mac);
> > +void hal_phy_addr(struct sp_mac *mac);
> > +u32  read_sw_int_mask0(struct sp_mac *mac);
> > +void write_sw_int_mask0(struct sp_mac *mac, u32 value);
> > +void write_sw_int_status0(struct sp_mac *mac, u32 value);
> > +u32  read_sw_int_status0(struct sp_mac *mac);
> > +u32  read_port_ability(struct sp_mac *mac);
> > +
> > +#endif
> > diff --git a/drivers/net/ethernet/sunplus/sp_int.c b/drivers/net/ethernet/sunplus/sp_int.c
> > new file mode 100644
> > index 0000000..27d81b6
> > --- /dev/null
> > +++ b/drivers/net/ethernet/sunplus/sp_int.c
> > @@ -0,0 +1,286 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/* Copyright Sunplus Technology Co., Ltd.
> > + *       All rights reserved.
> > + */
> > +
> > +#include "sp_define.h"
> > +#include "sp_int.h"
> > +#include "sp_driver.h"
> > +#include "sp_hal.h"
> > +
> > +static void port_status_change(struct sp_mac *mac)
> > +{
> > +	u32 reg;
> > +	struct net_device *ndev = mac->ndev;
> > +
> > +	reg = read_port_ability(mac);
> > +	if (!netif_carrier_ok(ndev) && (reg & PORT_ABILITY_LINK_ST_P0)) {
> > +		netif_carrier_on(ndev);
> > +		netif_start_queue(ndev);
> > +	} else if (netif_carrier_ok(ndev) && !(reg & PORT_ABILITY_LINK_ST_P0)) {
> > +		netif_carrier_off(ndev);
> > +		netif_stop_queue(ndev);
> > +	}
> > +
> > +	if (mac->next_ndev) {
> > +		struct net_device *ndev2 = mac->next_ndev;
> > +
> > +		if (!netif_carrier_ok(ndev2) && (reg & PORT_ABILITY_LINK_ST_P1)) {
> > +			netif_carrier_on(ndev2);
> > +			netif_start_queue(ndev2);
> > +		} else if (netif_carrier_ok(ndev2) && !(reg & PORT_ABILITY_LINK_ST_P1)) {
> > +			netif_carrier_off(ndev2);
> > +			netif_stop_queue(ndev2);
> > +		}
> > +	}
> > +}
> > +
> > +static void rx_skb(struct sp_mac *mac, struct sk_buff *skb)
> > +{
> > +	mac->dev_stats.rx_packets++;
> > +	mac->dev_stats.rx_bytes += skb->len;
> > +	netif_receive_skb(skb);
> > +}
> > +
> > +int rx_poll(struct napi_struct *napi, int budget)
> > +{
> > +	struct sp_common *comm = container_of(napi, struct sp_common, rx_napi);
> > +	struct sp_mac *mac = netdev_priv(comm->ndev);
> > +	struct sk_buff *skb, *new_skb;
> > +	struct skb_info *sinfo;
> > +	struct mac_desc *desc;
> > +	struct mac_desc *h_desc;
> > +	u32 rx_pos, pkg_len;
> > +	u32 cmd;
> > +	u32 num, rx_count;
> > +	s32 queue;
> > +	int ndev2_pkt;
> > +	struct net_device_stats *dev_stats;
> > +
> > +	spin_lock(&comm->rx_lock);
> > +
> > +	// 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; num++) {
> > +			sinfo = comm->rx_skb_info[queue] + rx_pos;
> > +			desc = comm->rx_desc[queue] + rx_pos;
> > +			cmd = desc->cmd1;
> > +
> > +			if (cmd & OWN_BIT)
> > +				break;
> > +
> > +			if ((cmd & PKTSP_MASK) == PKTSP_PORT1) {
> > +				struct sp_mac *mac2;
> > +
> > +				ndev2_pkt = 1;
> > +				mac2 = (mac->next_ndev) ? netdev_priv(mac->next_ndev) : NULL;
> > +				dev_stats = (mac2) ? &mac2->dev_stats : &mac->dev_stats;
> > +			} else {
> > +				ndev2_pkt = 0;
> > +				dev_stats = &mac->dev_stats;
> > +			}
> > +
> > +			pkg_len = cmd & LEN_MASK;
> > +			if (unlikely((cmd & ERR_CODE) || (pkg_len < 64))) {
> > +				dev_stats->rx_length_errors++;
> > +				dev_stats->rx_dropped++;
> > +				goto NEXT;
> > +			}
> > +
> > +			if (unlikely(cmd & RX_IP_CHKSUM_BIT)) {
> > +				dev_stats->rx_crc_errors++;
> > +				dev_stats->rx_dropped++;
> > +				goto NEXT;
> > +			}
> > +
> > +			// Allocate an skbuff for receiving.
> > +			new_skb = __dev_alloc_skb(comm->rx_desc_buff_size + RX_OFFSET,
> > +						  GFP_ATOMIC | GFP_DMA);
> > +			if (unlikely(!new_skb)) {
> > +				dev_stats->rx_dropped++;
> > +				goto NEXT;
> > +			}
> > +			new_skb->dev = mac->ndev;
> > +
> > +			dma_unmap_single(&comm->pdev->dev, sinfo->mapping,
> > +					 comm->rx_desc_buff_size, DMA_FROM_DEVICE);
> > +
> > +			skb = sinfo->skb;
> > +			skb->ip_summed = CHECKSUM_NONE;
> > +
> > +			/*skb_put will judge if tail exceeds end, but __skb_put won't */
> > +			__skb_put(skb, (pkg_len - 4 > comm->rx_desc_buff_size) ?
> > +				       comm->rx_desc_buff_size : pkg_len - 4);
> > +
> > +			sinfo->mapping = dma_map_single(&comm->pdev->dev, new_skb->data,
> > +							comm->rx_desc_buff_size,
> > +							DMA_FROM_DEVICE);
> may fail

Yes, I'll add error check in next patch as shown below:

		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(new_skb);
			dev_stats->rx_errors++;
			desc->cmd2 = (rx_pos == comm->rx_desc_num[queue] - 1) ?
				     EOR_BIT : 0;
			goto rx_poll_err;
		}
		sinfo->skb = new_skb;
[...]

		desc->addr1 = sinfo->mapping;

rx_poll_next:
		desc->cmd2 = (rx_pos == comm->rx_desc_num[queue] - 1) ?
			     EOR_BIT | MAC_RX_LEN_MAX : MAC_RX_LEN_MAX;
rx_poll_err:
		wmb(); // Set OWN_BIT after other fields of descriptor are effective.
		desc->cmd1 = OWN_BIT;

		NEXT_RX(queue, rx_pos);


> > +			sinfo->skb = new_skb;
> > +
> > +			if (ndev2_pkt) {
> > +				struct net_device *netdev2 = mac->next_ndev;
> > +
> > +				if (netdev2) {
> > +					skb->protocol = eth_type_trans(skb, netdev2);
> > +					rx_skb(netdev_priv(netdev2), skb);
> > +				}
> > +			} else {
> > +				skb->protocol = eth_type_trans(skb, mac->ndev);
> > +				rx_skb(mac, skb);
> > +			}
> > +
> > +			desc->addr1 = sinfo->mapping;
> > +
> > +NEXT:
> > +			desc->cmd2 = (rx_pos == comm->rx_desc_num[queue] - 1) ?
> > +				     EOR_BIT | MAC_RX_LEN_MAX : MAC_RX_LEN_MAX;
> > +			wmb();	// Set OWN_BIT after other fields of descriptor are effective.
> > +			desc->cmd1 = OWN_BIT | (comm->rx_desc_buff_size & LEN_MASK);
> > +
> > +			NEXT_RX(queue, rx_pos);
> > +
> > +			// If there are packets in high-priority queue,
> > +			// stop processing low-priority queue.
> > +			if ((queue == 1) && ((h_desc->cmd1 & OWN_BIT) == 0))
> > +				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;
> > +	}
> > +
> > +	spin_unlock(&comm->rx_lock);
> > +
> > +	wmb();			// make sure settings are effective.
> > +	write_sw_int_mask0(mac, read_sw_int_mask0(mac) & ~MAC_INT_RX);
> > +
> > +	napi_complete(napi);
> > +	return 0;
> > +}
> > +
> > +int tx_poll(struct napi_struct *napi, int budget)
> > +{
> > +	struct sp_common *comm = container_of(napi, struct sp_common, tx_napi);
> > +	struct sp_mac *mac = netdev_priv(comm->ndev);
> > +	u32 tx_done_pos;
> > +	u32 cmd;
> > +	struct skb_info *skbinfo;
> > +	struct sp_mac *smac;
> > +
> > +	spin_lock(&comm->tx_lock);
> > +
> > +	tx_done_pos = comm->tx_done_pos;
> > +	while ((tx_done_pos != comm->tx_pos) || (comm->tx_desc_full == 1)) {
> > +		cmd = comm->tx_desc[tx_done_pos].cmd1;
> > +		if (cmd & OWN_BIT)
> > +			break;
> > +
> > +		skbinfo = &comm->tx_temp_skb_info[tx_done_pos];
> > +		if (unlikely(!skbinfo->skb))
> > +			netdev_err(mac->ndev, "skb is null!\n");
> > +
> > +		smac = mac;
> > +		if (mac->next_ndev && ((cmd & TO_VLAN_MASK) == TO_VLAN_GROUP1))
> > +			smac = netdev_priv(mac->next_ndev);
> > +
> > +		if (unlikely(cmd & (ERR_CODE))) {
> > +			smac->dev_stats.tx_errors++;
> > +		} else {
> > +			smac->dev_stats.tx_packets++;
> > +			smac->dev_stats.tx_bytes += skbinfo->len;
> > +		}
> > +
> > +		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;
> > +
> > +		NEXT_TX(tx_done_pos);
> > +		if (comm->tx_desc_full == 1)
> > +			comm->tx_desc_full = 0;
> > +	}
> > +
> > +	comm->tx_done_pos = tx_done_pos;
> > +	if (!comm->tx_desc_full) {
> > +		if (netif_queue_stopped(mac->ndev))
> > +			netif_wake_queue(mac->ndev);
> > +
> > +		if (mac->next_ndev) {
> > +			if (netif_queue_stopped(mac->next_ndev))
> > +				netif_wake_queue(mac->next_ndev);
> > +		}
> > +	}
> > +
> > +	spin_unlock(&comm->tx_lock);
> > +
> > +	wmb();			// make sure settings are effective.
> > +	write_sw_int_mask0(mac, read_sw_int_mask0(mac) & ~MAC_INT_TX);
> > +
> > +	napi_complete(napi);
> > +	return 0;
> > +}
> > +
> > +irqreturn_t ethernet_interrupt(int irq, void *dev_id)
> > +{
> > +	struct net_device *ndev;
> > +	struct sp_mac *mac;
> > +	struct sp_common *comm;
> > +	u32 status;
> > +
> > +	ndev = (struct net_device *)dev_id;
> > +	if (unlikely(!ndev)) {
> > +		netdev_err(ndev, "ndev is null!\n");
> > +		goto OUT;
> > +	}
> > +
> > +	mac = netdev_priv(ndev);
> > +	comm = mac->comm;
> > +
> > +	status = read_sw_int_status0(mac);
> > +	if (unlikely(status == 0)) {
> > +		netdev_err(ndev, "Interrput status is null!\n");
> > +		goto OUT;
> > +	}
> > +	write_sw_int_status0(mac, status);
> > +
> > +	if (status & MAC_INT_RX) {
> > +		// Disable RX interrupts.
> > +		write_sw_int_mask0(mac, read_sw_int_mask0(mac) | MAC_INT_RX);
> > +
> > +		if (unlikely(status & MAC_INT_RX_DES_ERR)) {
> > +			netdev_err(ndev, "Illegal RX Descriptor!\n");
> > +			mac->dev_stats.rx_fifo_errors++;
> > +		}
> > +		if (napi_schedule_prep(&comm->rx_napi))
> > +			__napi_schedule(&comm->rx_napi);
> > +	}
> > +
> > +	if (status & MAC_INT_TX) {
> > +		// Disable TX interrupts.
> > +		write_sw_int_mask0(mac, read_sw_int_mask0(mac) | MAC_INT_TX);
> > +
> > +		if (unlikely(status & MAC_INT_TX_DES_ERR)) {
> > +			netdev_err(ndev, "Illegal TX Descriptor Error\n");
> > +			mac->dev_stats.tx_fifo_errors++;
> > +			mac_soft_reset(mac);
> > +			wmb();			// make sure settings are effective.
> > +			write_sw_int_mask0(mac, read_sw_int_mask0(mac) & ~MAC_INT_TX);
> > +		} else {
> > +			if (napi_schedule_prep(&comm->tx_napi))
> > +				__napi_schedule(&comm->tx_napi);
> > +		}
> > +	}
> > +
> > +	if (status & MAC_INT_PORT_ST_CHG)	/* link status changed */
> > +		port_status_change(mac);
> > +
> > +OUT:
> > +	return IRQ_HANDLED;
> > +}
> > diff --git a/drivers/net/ethernet/sunplus/sp_int.h b/drivers/net/ethernet/sunplus/sp_int.h
> > new file mode 100644
> > index 0000000..7de8df4
> > --- /dev/null
> > +++ b/drivers/net/ethernet/sunplus/sp_int.h
> > @@ -0,0 +1,13 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/* Copyright Sunplus Technology Co., Ltd.
> > + *       All rights reserved.
> > + */
> > +
> > +#ifndef __SP_INT_H__
> > +#define __SP_INT_H__
> > +
> > +int rx_poll(struct napi_struct *napi, int budget);
> > +int tx_poll(struct napi_struct *napi, int budget);
> > +irqreturn_t ethernet_interrupt(int irq, void *dev_id);
> > +
> > +#endif
> > diff --git a/drivers/net/ethernet/sunplus/sp_mac.c b/drivers/net/ethernet/sunplus/sp_mac.c
> > new file mode 100644
> > index 0000000..6a3dfb1
> > --- /dev/null
> > +++ b/drivers/net/ethernet/sunplus/sp_mac.c
> > @@ -0,0 +1,63 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/* Copyright Sunplus Technology Co., Ltd.
> > + *       All rights reserved.
> > + */
> > +
> > +#include "sp_mac.h"
> > +
> > +void mac_init(struct sp_mac *mac)
> > +{
> > +	u32 i;
> > +
> > +	for (i = 0; i < RX_DESC_QUEUE_NUM; i++)
> > +		mac->comm->rx_pos[i] = 0;
> > +	mb();	// make sure settings are effective.
> > +
> > +	hal_mac_init(mac);
> > +}
> > +
> > +void mac_soft_reset(struct sp_mac *mac)
> > +{
> > +	u32 i;
> > +	struct net_device *ndev2;
> > +
> > +	if (netif_carrier_ok(mac->ndev)) {
> > +		netif_carrier_off(mac->ndev);
> > +		netif_stop_queue(mac->ndev);
> > +	}
> > +
> > +	ndev2 = mac->next_ndev;
> > +	if (ndev2) {
> > +		if (netif_carrier_ok(ndev2)) {
> > +			netif_carrier_off(ndev2);
> > +			netif_stop_queue(ndev2);
> > +		}
> > +	}
> > +
> > +	hal_mac_reset(mac);
> > +	hal_mac_stop(mac);
> > +
> > +	rx_descs_flush(mac->comm);
> > +	mac->comm->tx_pos = 0;
> > +	mac->comm->tx_done_pos = 0;
> > +	mac->comm->tx_desc_full = 0;
> > +
> > +	for (i = 0; i < RX_DESC_QUEUE_NUM; i++)
> > +		mac->comm->rx_pos[i] = 0;
> > +	mb();	// make sure settings are effective.
> > +
> > +	hal_mac_init(mac);
> > +	hal_mac_start(mac);
> > +
> > +	if (!netif_carrier_ok(mac->ndev)) {
> > +		netif_carrier_on(mac->ndev);
> > +		netif_start_queue(mac->ndev);
> > +	}
> > +
> > +	if (ndev2) {
> > +		if (!netif_carrier_ok(ndev2)) {
> > +			netif_carrier_on(ndev2);
> > +			netif_start_queue(ndev2);
> > +		}
> > +	}
> > +}
> > diff --git a/drivers/net/ethernet/sunplus/sp_mac.h b/drivers/net/ethernet/sunplus/sp_mac.h
> > new file mode 100644
> > index 0000000..f35f3b7
> > --- /dev/null
> > +++ b/drivers/net/ethernet/sunplus/sp_mac.h
> > @@ -0,0 +1,23 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/* Copyright Sunplus Technology Co., Ltd.
> > + *       All rights reserved.
> > + */
> > +
> > +#ifndef __SP_MAC_H__
> > +#define __SP_MAC_H__
> > +
> > +#include "sp_define.h"
> > +#include "sp_hal.h"
> > +
> > +void mac_init(struct sp_mac *mac);
> > +void mac_soft_reset(struct sp_mac *mac);
> > +
> > +// Calculate the empty tx descriptor number
> > +#define TX_DESC_AVAIL(mac) \
> > +	(((mac)->tx_pos != (mac)->tx_done_pos) ? \
> > +	(((mac)->tx_done_pos < (mac)->tx_pos) ? \
> > +	(TX_DESC_NUM - ((mac)->tx_pos - (mac)->tx_done_pos)) : \
> > +	((mac)->tx_done_pos - (mac)->tx_pos)) : \
> > +	((mac)->tx_desc_full ? 0 : TX_DESC_NUM))
> > +
> > +#endif
> > diff --git a/drivers/net/ethernet/sunplus/sp_mdio.c b/drivers/net/ethernet/sunplus/sp_mdio.c
> > new file mode 100644
> > index 0000000..f6a7e64
> > --- /dev/null
> > +++ b/drivers/net/ethernet/sunplus/sp_mdio.c
> > @@ -0,0 +1,90 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/* Copyright Sunplus Technology Co., Ltd.
> > + *       All rights reserved.
> > + */
> > +
> > +#include "sp_mdio.h"
> > +
> > +u32 mdio_read(struct sp_mac *mac, u32 phy_id, u16 regnum)
> > +{
> > +	int ret;
> > +
> > +	ret = hal_mdio_access(mac, MDIO_READ_CMD, phy_id, regnum, 0);
> > +	if (ret < 0)
> > +		return -EOPNOTSUPP;
> > +
> > +	return ret;
> > +}
> > +
> > +u32 mdio_write(struct sp_mac *mac, u32 phy_id, u32 regnum, u16 val)
> > +{
> > +	int ret;
> > +
> > +	ret = hal_mdio_access(mac, MDIO_WRITE_CMD, phy_id, regnum, val);
> > +	if (ret < 0)
> > +		return -EOPNOTSUPP;
> > +
> > +	return 0;
> > +}
> > +
> > +static int mii_read(struct mii_bus *bus, int phy_id, int regnum)
> > +{
> > +	struct sp_mac *mac = bus->priv;
> > +
> > +	return mdio_read(mac, phy_id, regnum);
> > +}
> > +
> > +static int mii_write(struct mii_bus *bus, int phy_id, int regnum, u16 val)
> > +{
> > +	struct sp_mac *mac = bus->priv;
> > +
> > +	return mdio_write(mac, phy_id, regnum, val);
> > +}
> > +
> > +u32 mdio_init(struct platform_device *pdev, struct net_device *ndev)
> > +{
> > +	struct sp_mac *mac = netdev_priv(ndev);
> > +	struct mii_bus *mii_bus;
> > +	struct device_node *mdio_node;
> > +	int ret;
> > +
> > +	mii_bus = mdiobus_alloc();
> > +	if (!mii_bus) {
> > +		netdev_err(ndev, "Failed to allocate mdio_bus memory!\n");
> > +		return -ENOMEM;
> > +	}
> > +
> > +	mii_bus->name = "sunplus_mii_bus";
> > +	mii_bus->parent = &pdev->dev;
> > +	mii_bus->priv = mac;
> > +	mii_bus->read = mii_read;
> > +	mii_bus->write = mii_write;
> > +	snprintf(mii_bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(&pdev->dev));
> > +
> > +	mdio_node = of_get_parent(mac->phy_node);
> > +	if (!mdio_node) {
> > +		netdev_err(ndev, "Failed to get mdio_node!\n");
> > +		return -ENODATA;
> > +	}
> > +
> > +	ret = of_mdiobus_register(mii_bus, mdio_node);
> > +	if (ret) {
> > +		netdev_err(ndev, "Failed to register mii bus!\n");
> > +		mdiobus_free(mii_bus);
> > +		return ret;
> > +	}
> > +
> > +	mac->comm->mii_bus = mii_bus;
> > +	return ret;
> > +}
> > +
> > +void mdio_remove(struct net_device *ndev)
> > +{
> > +	struct sp_mac *mac = netdev_priv(ndev);
> > +
> > +	if (mac->comm->mii_bus) {
> > +		mdiobus_unregister(mac->comm->mii_bus);
> > +		mdiobus_free(mac->comm->mii_bus);
> > +		mac->comm->mii_bus = NULL;
> > +	}
> > +}
> > diff --git a/drivers/net/ethernet/sunplus/sp_mdio.h b/drivers/net/ethernet/sunplus/sp_mdio.h
> > new file mode 100644
> > index 0000000..d708624
> > --- /dev/null
> > +++ b/drivers/net/ethernet/sunplus/sp_mdio.h
> > @@ -0,0 +1,20 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/* Copyright Sunplus Technology Co., Ltd.
> > + *       All rights reserved.
> > + */
> > +
> > +#ifndef __SP_MDIO_H__
> > +#define __SP_MDIO_H__
> > +
> > +#include "sp_define.h"
> > +#include "sp_hal.h"
> > +
> > +#define MDIO_READ_CMD           0x02
> > +#define MDIO_WRITE_CMD          0x01
> > +
> > +u32  mdio_read(struct sp_mac *mac, u32 phy_id, u16 regnum);
> > +u32  mdio_write(struct sp_mac *mac, u32 phy_id, u32 regnum, u16 val);
> > +u32  mdio_init(struct platform_device *pdev, struct net_device *ndev);
> > +void mdio_remove(struct net_device *ndev);
> > +
> > +#endif
> > diff --git a/drivers/net/ethernet/sunplus/sp_phy.c b/drivers/net/ethernet/sunplus/sp_phy.c
> > new file mode 100644
> > index 0000000..df6df3a
> > --- /dev/null
> > +++ b/drivers/net/ethernet/sunplus/sp_phy.c
> > @@ -0,0 +1,64 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/* Copyright Sunplus Technology Co., Ltd.
> > + *       All rights reserved.
> > + */
> > +
> > +#include "sp_phy.h"
> > +#include "sp_mdio.h"
> > +
> > +static void mii_linkchange(struct net_device *netdev)
> > +{
> > +}
> > +
> > +int sp_phy_probe(struct net_device *ndev)
> > +{
> > +	struct sp_mac *mac = netdev_priv(ndev);
> > +	struct phy_device *phydev;
> > +	int i;
> > +
> > +	phydev = of_phy_connect(ndev, mac->phy_node, mii_linkchange,
> > +				0, mac->phy_mode);
> > +	if (!phydev) {
> > +		netdev_err(ndev, "\"%s\" failed to connect to phy!\n", ndev->name);
> > +		return -ENODEV;
> > +	}
> > +
> > +	for (i = 0; i < sizeof(phydev->supported) / sizeof(long); i++)
> > +		phydev->advertising[i] = phydev->supported[i];
> > +
> > +	phydev->irq = PHY_MAC_INTERRUPT;
> > +	mac->phy_dev = phydev;
> > +
> > +	// Bug workaround:
> > +	// Flow-control of phy should be enabled. MAC flow-control will refer
> > +	// to the bit to decide to enable or disable flow-control.
> > +	mdio_write(mac, mac->phy_addr, 4, mdio_read(mac, mac->phy_addr, 4) | (1 << 10));
> > +
> > +	return 0;
> > +}
> > +
> > +void sp_phy_start(struct net_device *ndev)
> > +{
> > +	struct sp_mac *mac = netdev_priv(ndev);
> > +
> > +	if (mac->phy_dev)
> > +		phy_start(mac->phy_dev);
> > +}
> > +
> > +void sp_phy_stop(struct net_device *ndev)
> > +{
> > +	struct sp_mac *mac = netdev_priv(ndev);
> > +
> > +	if (mac->phy_dev)
> > +		phy_stop(mac->phy_dev);
> > +}
> > +
> > +void sp_phy_remove(struct net_device *ndev)
> > +{
> > +	struct sp_mac *mac = netdev_priv(ndev);
> > +
> > +	if (mac->phy_dev) {
> > +		phy_disconnect(mac->phy_dev);
> > +		mac->phy_dev = NULL;
> > +	}
> > +}
> > diff --git a/drivers/net/ethernet/sunplus/sp_phy.h b/drivers/net/ethernet/sunplus/sp_phy.h
> > new file mode 100644
> > index 0000000..4ae0351
> > --- /dev/null
> > +++ b/drivers/net/ethernet/sunplus/sp_phy.h
> > @@ -0,0 +1,16 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/* Copyright Sunplus Technology Co., Ltd.
> > + *       All rights reserved.
> > + */
> > +
> > +#ifndef __SP_PHY_H__
> > +#define __SP_PHY_H__
> > +
> > +#include "sp_define.h"
> > +
> > +int  sp_phy_probe(struct net_device *netdev);
> > +void sp_phy_start(struct net_device *netdev);
> > +void sp_phy_stop(struct net_device *netdev);
> > +void sp_phy_remove(struct net_device *netdev);
> > +
> > +#endif
> > diff --git a/drivers/net/ethernet/sunplus/sp_register.h b/drivers/net/ethernet/sunplus/sp_register.h
> > new file mode 100644
> > index 0000000..690c0dd
> > --- /dev/null
> > +++ b/drivers/net/ethernet/sunplus/sp_register.h
> > @@ -0,0 +1,96 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/* Copyright Sunplus Technology Co., Ltd.
> > + *       All rights reserved.
> > + */
> > +
> > +#ifndef __SP_REGISTER_H__
> > +#define __SP_REGISTER_H__
> > +
> > +#include "sp_define.h"
> > +
> > +/* TYPE: RegisterFile_L2SW */
> > +#define SP_SW_INT_STATUS_0		0x0
> > +#define SP_SW_INT_MASK_0		0x4
> > +#define SP_FL_CNTL_TH			0x8
> > +#define SP_CPU_FL_CNTL_TH		0xc
> > +#define SP_PRI_FL_CNTL			0x10
> > +#define SP_VLAN_PRI_TH			0x14
> > +#define SP_EN_TOS_BUS			0x18
> > +#define SP_TOS_MAP0			0x1c
> > +#define SP_TOS_MAP1			0x20
> > +#define SP_TOS_MAP2			0x24
> > +#define SP_TOS_MAP3			0x28
> > +#define SP_TOS_MAP4			0x2c
> > +#define SP_TOS_MAP5			0x30
> > +#define SP_TOS_MAP6			0x34
> > +#define SP_TOS_MAP7			0x38
> > +#define SP_GLOBAL_QUE_STATUS		0x3c
> > +#define SP_ADDR_TBL_SRCH		0x40
> > +#define SP_ADDR_TBL_ST			0x44
> > +#define SP_MAC_AD_SER0			0x48
> > +#define SP_MAC_AD_SER1			0x4c
> > +#define SP_WT_MAC_AD0			0x50
> > +#define SP_W_MAC_15_0			0x54
> > +#define SP_W_MAC_47_16			0x58
> > +#define SP_PVID_CONFIG0			0x5c
> > +#define SP_PVID_CONFIG1			0x60
> > +#define SP_VLAN_MEMSET_CONFIG0		0x64
> > +#define SP_VLAN_MEMSET_CONFIG1		0x68
> > +#define SP_PORT_ABILITY			0x6c
> > +#define SP_PORT_ST			0x70
> > +#define SP_CPU_CNTL			0x74
> > +#define SP_PORT_CNTL0			0x78
> > +#define SP_PORT_CNTL1			0x7c
> > +#define SP_PORT_CNTL2			0x80
> > +#define SP_SW_GLB_CNTL			0x84
> > +#define SP_SP_SW_RESET			0x88
> > +#define SP_LED_PORT0			0x8c
> > +#define SP_LED_PORT1			0x90
> > +#define SP_LED_PORT2			0x94
> > +#define SP_LED_PORT3			0x98
> > +#define SP_LED_PORT4			0x9c
> > +#define SP_WATCH_DOG_TRIG_RST		0xa0
> > +#define SP_WATCH_DOG_STOP_CPU		0xa4
> > +#define SP_PHY_CNTL_REG0		0xa8
> > +#define SP_PHY_CNTL_REG1		0xac
> > +#define SP_MAC_FORCE_MODE		0xb0
> > +#define SP_VLAN_GROUP_CONFIG0		0xb4
> > +#define SP_VLAN_GROUP_CONFIG1		0xb8
> > +#define SP_FLOW_CTRL_TH3		0xbc
> > +#define SP_QUEUE_STATUS_0		0xc0
> > +#define SP_DEBUG_CNTL			0xc4
> > +#define SP_RESERVED_1			0xc8
> > +#define SP_MEM_TEST_INFO		0xcc
> > +#define SP_SW_INT_STATUS_1		0xd0
> > +#define SP_SW_INT_MASK_1		0xd4
> > +#define SP_SW_GLOBAL_SIGNAL		0xd8
> > +
> > +#define SP_CPU_TX_TRIG			0x208
> > +#define SP_TX_HBASE_ADDR_0		0x20c
> > +#define SP_TX_LBASE_ADDR_0		0x210
> > +#define SP_RX_HBASE_ADDR_0		0x214
> > +#define SP_RX_LBASE_ADDR_0		0x218
> > +#define SP_TX_HW_ADDR_0			0x21c
> > +#define SP_TX_LW_ADDR_0			0x220
> > +#define SP_RX_HW_ADDR_0			0x224
> > +#define SP_RX_LW_ADDR_0			0x228
> > +#define SP_CPU_PORT_CNTL_REG_0		0x22c
> > +#define SP_TX_HBASE_ADDR_1		0x230
> > +#define SP_TX_LBASE_ADDR_1		0x234
> > +#define SP_RX_HBASE_ADDR_1		0x238
> > +#define SP_RX_LBASE_ADDR_1		0x23c
> > +#define SP_TX_HW_ADDR_1			0x240
> > +#define SP_TX_LW_ADDR_1			0x244
> > +#define SP_RX_HW_ADDR_1			0x248
> > +#define SP_RX_LW_ADDR_1			0x24c
> > +#define SP_CPU_PORT_CNTL_REG_1		0x250
> > +
> > +/* TYPE: RegisterFile_MOON5 */
> > +#define MOON5_MO5_THERMAL_CTL_0		0x0
> > +#define MOON5_MO5_THERMAL_CTL_1		0x4
> > +#define MOON5_MO4_THERMAL_CTL_2		0xc
> > +#define MOON5_MO4_THERMAL_CTL_3		0x8
> > +#define MOON5_MO4_TMDS_L2SW_CTL		0x10
> > +#define MOON5_MO4_L2SW_CLKSW_CTL	0x14
> > +
> > +#endif
> >

Thank you very much for your review.






[Index of Archives]     [Device Tree Compilter]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux PCI Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Yosemite Backpacking]


  Powered by Linux